| /* $Id: tmesh-cmds.c,v 1.6 2006/11/15 23:12:30 fredette Exp $ */ |
| |
| /* tmesh/tmesh-cmds.c - functions implementing the tmesh commands: */ |
| |
| /* |
| * Copyright (c) 2003 Matt Fredette |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Matt Fredette. |
| * 4. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <tme/common.h> |
| _TME_RCSID("$Id: tmesh-cmds.c,v 1.6 2006/11/15 23:12:30 fredette Exp $"); |
| |
| /* includes: */ |
| #include <tme/threads.h> |
| #include <stdlib.h> |
| #include "tmesh-impl.h" |
| |
| /* macros: */ |
| |
| /* flags for ls: */ |
| #define TMESH_LS_NORMAL (0) |
| #define TMESH_LS_ALL TME_BIT(0) |
| #define TMESH_LS_RECURSE TME_BIT(1) |
| #define TMESH_LS_ABSOLUTE TME_BIT(2) |
| |
| /* the "source" command: */ |
| static int |
| _tmesh_command_source(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_io_stack *stack_new; |
| struct tmesh_io *io_new; |
| struct tmesh_io *io_old; |
| int rc; |
| |
| /* allocate the new io stack member: */ |
| stack_new = tme_new(struct tmesh_io_stack, 1); |
| io_new = &stack_new->tmesh_io_stack_io; |
| |
| /* initialize the new io: */ |
| io_new->tmesh_io_name = value->tmesh_parser_value_arg; |
| io_new->tmesh_io_input_line = 0; |
| |
| /* call the current input source's open function: */ |
| io_old = &tmesh->tmesh_io_stack->tmesh_io_stack_io; |
| rc = (*io_old->tmesh_io_open)(io_new, io_old, _output); |
| |
| /* if the open succeeded, push this new input source onto the stack, |
| otherwise free it: */ |
| if (rc == TME_OK) { |
| _tmesh_gc_release(tmesh, io_new->tmesh_io_name); |
| stack_new->tmesh_io_stack_next = tmesh->tmesh_io_stack; |
| tmesh->tmesh_io_stack = stack_new; |
| } |
| else { |
| tme_free(stack_new); |
| } |
| |
| /* done: */ |
| return (rc); |
| } |
| |
| /* the "mkdir" command: */ |
| static int |
| _tmesh_command_mkdir(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| char *dirname; |
| int rc; |
| |
| /* look up the new directory name: */ |
| dirname = value->tmesh_parser_value_pathname0; |
| rc = _tmesh_fs_lookup(tmesh, |
| &dirname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_LAST_PART_OK); |
| |
| /* if the lookup succeeded: */ |
| if (rc == TME_OK) { |
| |
| /* if the new directory name already exists, we can't |
| make it: */ |
| if (entry != NULL) { |
| rc = EEXIST; |
| } |
| |
| /* otherwise, make the new directory: */ |
| else { |
| _tmesh_fs_mkdir(parent, tme_strdup(dirname)); |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /* the "rmdir" command: */ |
| static int |
| _tmesh_command_rmdir(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry, *dir; |
| char *dirname; |
| int rc; |
| |
| /* look up the directory: */ |
| dirname = value->tmesh_parser_value_pathname0; |
| rc = _tmesh_fs_lookup(tmesh, |
| &dirname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup succeeded: */ |
| if (rc == TME_OK) { |
| |
| /* if this pathname doesn't refer to a directory, we can't |
| remove it: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_DIR) { |
| rc = ENOTDIR; |
| } |
| |
| /* otherwise, this is a directory: */ |
| else { |
| dir = entry->tmesh_fs_dirent_value; |
| |
| /* if the directory isn't empty, we can't remove it: */ |
| if (dir->tmesh_fs_dirent_prev |
| != &dir->tmesh_fs_dirent_next->tmesh_fs_dirent_next) { |
| rc = ENOTEMPTY; |
| } |
| |
| /* you can't remove the current working directory, |
| or the "." or ".." directories: */ |
| else if (dir == tmesh->tmesh_cwd |
| || !strcmp(entry->tmesh_fs_dirent_name, ".") |
| || !strcmp(entry->tmesh_fs_dirent_name, "..")) { |
| rc = EACCES; |
| } |
| |
| /* otherwise, remove the directory: */ |
| else { |
| |
| /* unlink the directory in the parent: */ |
| _tmesh_fs_unlink(entry); |
| |
| /* free the "." and ".." directory entries: */ |
| tme_free(entry->tmesh_fs_dirent_next->tmesh_fs_dirent_name); |
| tme_free(entry->tmesh_fs_dirent_next); |
| tme_free(entry->tmesh_fs_dirent_name); |
| tme_free(entry); |
| } |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /* the "cd" command: */ |
| static int |
| _tmesh_command_cd(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| char *dirname; |
| int rc; |
| |
| /* look up the directory: */ |
| dirname = value->tmesh_parser_value_pathname0; |
| rc = _tmesh_fs_lookup(tmesh, |
| &dirname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup succeeded: */ |
| if (rc == TME_OK) { |
| |
| /* if this pathname doesn't refer to a directory, we can't |
| cd to it: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_DIR) { |
| rc = ENOTDIR; |
| } |
| |
| /* otherwise, this is a directory: */ |
| else { |
| |
| /* this is the new current working directory: */ |
| tmesh->tmesh_cwd = entry->tmesh_fs_dirent_value; |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /* the "pwd" command: */ |
| static int |
| _tmesh_command_pwd(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| _tmesh_fs_pathname_dir(tmesh->tmesh_cwd, _output, NULL); |
| tme_output_append(_output, "\n"); |
| return (TME_OK); |
| } |
| |
| /* this outputs an argv: */ |
| static void |
| _tmesh_ls_output_argv(char **_output, struct tmesh_parser_argv *argv, unsigned int skip) |
| { |
| unsigned int argc; |
| char **args; |
| argc = argv->tmesh_parser_argv_argc; |
| args = argv->tmesh_parser_argv_argv; |
| assert(argc > 0 && argc >= skip); |
| argc -= skip; |
| args += skip; |
| for (; argc-- > 0; ) { |
| tme_output_append(_output, " "); |
| tme_output_append(_output, *(args++)); |
| } |
| } |
| |
| /* this lists an element: */ |
| static void |
| _tmesh_ls_element(struct tmesh_fs_element *element, |
| char **_output, |
| int flags) |
| { |
| int output_element_argv; |
| struct tmesh_fs_element *element_other; |
| struct tmesh_fs_element_conn *conn, *conn_other; |
| |
| /* we haven't yet output the element's argv: */ |
| output_element_argv = FALSE; |
| |
| /* loop over the element's connections: */ |
| for (conn = element->tmesh_fs_element_conns; |
| conn != NULL; |
| conn = conn->tmesh_fs_element_conn_next) { |
| |
| /* if we're not showing all connections, and this connection was |
| made after this element was created, skip it: */ |
| if (!(flags & TMESH_LS_ALL) |
| && (conn->tmesh_fs_element_conn_gen |
| > element->tmesh_fs_element_gen)) { |
| continue; |
| } |
| |
| /* output this element's name and connection argv: */ |
| _tmesh_fs_pathname_element(element, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : element->tmesh_fs_element_parent)); |
| _tmesh_ls_output_argv(_output, &conn->tmesh_fs_element_conn_argv, 1); |
| |
| /* get the other side of this connection: */ |
| conn_other = conn->tmesh_fs_element_conn_other; |
| element_other = conn_other->tmesh_fs_element_conn_element; |
| tme_output_append(_output, " at "); |
| |
| /* output the other element's name and connection argv: */ |
| _tmesh_fs_pathname_element(element_other, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : element->tmesh_fs_element_parent)); |
| _tmesh_ls_output_argv(_output, &conn_other->tmesh_fs_element_conn_argv, 1); |
| |
| /* if we haven't output the element's creation argv yet, do so: */ |
| if (!output_element_argv) { |
| tme_output_append(_output, ":"); |
| _tmesh_ls_output_argv(_output, &element->tmesh_fs_element_argv, 0); |
| output_element_argv = TRUE; |
| } |
| |
| /* output a newline: */ |
| tme_output_append(_output, "\n"); |
| } |
| |
| /* if we haven't output the element's creation argv yet, do so: */ |
| if (!output_element_argv) { |
| _tmesh_fs_pathname_element(element, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : element->tmesh_fs_element_parent)); |
| tme_output_append(_output, ":"); |
| _tmesh_ls_output_argv(_output, &element->tmesh_fs_element_argv, 0); |
| tme_output_append(_output, "\n"); |
| } |
| } |
| |
| /* this lists a directory: */ |
| static void |
| _tmesh_ls_dir(struct tmesh_fs_dirent *parent, |
| char **_output, |
| struct tmesh_fs_dirent *parent_top, |
| int flags) |
| { |
| struct tmesh_fs_dirent *entry, *dir; |
| struct tmesh_fs_element *element; |
| int pass; |
| |
| /* list this directory: */ |
| for (pass = 0; ++pass < 2;) { |
| |
| /* if this is pass two, but we're not recursing, stop: */ |
| if (pass == 2 |
| && !(flags & TMESH_LS_RECURSE)) { |
| return; |
| } |
| |
| /* loop over the entries in the directory: */ |
| entry = parent; |
| do { |
| |
| /* dispatch on the directory entry's type: */ |
| switch (entry->tmesh_fs_dirent_type) { |
| |
| /* this is a subdirectory: */ |
| case TMESH_FS_DIRENT_DIR: |
| dir = entry->tmesh_fs_dirent_value; |
| |
| /* handle a "." or ".." subdirectory: */ |
| if (!strcmp(entry->tmesh_fs_dirent_name, ".") |
| || !strcmp(entry->tmesh_fs_dirent_name, "..")) { |
| |
| /* if we're listing all, and this is pass one: */ |
| if ((flags & TMESH_LS_ALL) |
| && pass == 1) { |
| |
| /* output the directory's name: */ |
| _tmesh_fs_pathname_dir(dir, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : parent)); |
| tme_output_append(_output, "\n"); |
| } |
| } |
| |
| /* otherwise this is a regular subdirectory: */ |
| else { |
| |
| /* if this is pass one: */ |
| if (pass == 1) { |
| |
| /* output the directory's name: */ |
| _tmesh_fs_pathname_dir(dir, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : parent)); |
| tme_output_append(_output, "/\n"); |
| } |
| |
| /* otherwise, this is pass two: */ |
| else { |
| |
| /* output the directory's name and recurse: */ |
| tme_output_append(_output, "\n./"); |
| _tmesh_fs_pathname_dir(dir, _output, |
| ((flags & TMESH_LS_ABSOLUTE) |
| ? NULL |
| : parent_top)); |
| tme_output_append(_output, ":\n"); |
| _tmesh_ls_dir(dir, _output, parent_top, flags); |
| } |
| } |
| break; |
| |
| /* this is an element: */ |
| case TMESH_FS_DIRENT_ELEMENT: |
| element = entry->tmesh_fs_dirent_value; |
| _tmesh_ls_element(element, _output, flags); |
| break; |
| |
| default: assert(FALSE); |
| } |
| |
| /* if this was the last entry in the directory, we're done: */ |
| entry = entry->tmesh_fs_dirent_next; |
| } while (entry != parent); |
| } |
| } |
| |
| /* the "ls" command: */ |
| static int |
| _tmesh_command_ls(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| char *opts, *pathname, opt; |
| struct tmesh_fs_dirent *parent, *entry; |
| int type; |
| void *what; |
| int rc; |
| int flags; |
| |
| /* take apart any options: */ |
| opts = value->tmesh_parser_value_strings[0]; |
| flags = TMESH_LS_NORMAL; |
| if (opts != NULL) { |
| assert(*opts == '-'); |
| for (; (opt = *(++opts)) != '\0'; ) { |
| switch (opt) { |
| |
| /* 'a' lists all directory entries, and all connections on elements: */ |
| case 'a': flags |= TMESH_LS_ALL; break; |
| |
| /* 'l' lists absolute pathnames for elements: */ |
| case 'l': flags |= TMESH_LS_ABSOLUTE; break; |
| |
| /* 'R' recurses: */ |
| case 'R': flags |= TMESH_LS_RECURSE; break; |
| |
| /* an unknown option: */ |
| default: |
| tme_output_append(_output, |
| "ls: %s '-%c'\n", |
| _("invalid option"), |
| opt); |
| return (EINVAL); |
| } |
| } |
| } |
| |
| /* if a path name is given, look it up: */ |
| pathname = value->tmesh_parser_value_strings[1]; |
| if (pathname != NULL) { |
| rc = _tmesh_fs_lookup(tmesh, |
| &pathname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup failed: */ |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* get what we're listing: */ |
| type = entry->tmesh_fs_dirent_type; |
| what = entry->tmesh_fs_dirent_value; |
| } |
| |
| /* otherwise, list the cwd: */ |
| else { |
| type = TMESH_FS_DIRENT_DIR; |
| what = tmesh->tmesh_cwd; |
| } |
| |
| /* dispatch on the type: */ |
| switch (type) { |
| case TMESH_FS_DIRENT_DIR: |
| _tmesh_ls_dir(what, _output, what, flags); |
| break; |
| case TMESH_FS_DIRENT_ELEMENT: |
| _tmesh_ls_element(what, _output, flags); |
| break; |
| default: assert(FALSE); |
| } |
| |
| return (TME_OK); |
| } |
| |
| /* the "connect" command: */ |
| static int |
| _tmesh_command_connect(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| char *pathname; |
| struct tmesh_fs_element *element0, *element1; |
| char **element0_args; |
| char **element1_args; |
| char **creation_args; |
| struct tmesh_fs_element_conn *conn0, *conn1, **prev; |
| int which; |
| int rc; |
| |
| /* get and NULL-terminate all argument lists: */ |
| element0_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv; |
| assert(element0_args != NULL); |
| element0_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL; |
| element1_args = value->tmesh_parser_value_argvs[1].tmesh_parser_argv_argv; |
| if (element1_args != NULL) { |
| element1_args[value->tmesh_parser_value_argvs[1].tmesh_parser_argv_argc] = NULL; |
| } |
| creation_args = value->tmesh_parser_value_argvs[2].tmesh_parser_argv_argv; |
| if (creation_args != NULL) { |
| creation_args[value->tmesh_parser_value_argvs[2].tmesh_parser_argv_argc] = NULL; |
| } |
| |
| /* check any other element to connect to: */ |
| element1 = NULL; |
| if (element1_args != NULL) { |
| |
| /* look up the element: */ |
| pathname = element1_args[0]; |
| rc = _tmesh_fs_lookup(tmesh, |
| &pathname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* this must be an element: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) { |
| tme_output_append(_output, element1_args[0]); |
| return (ENOTSOCK); |
| } |
| element1 = entry->tmesh_fs_dirent_value; |
| } |
| |
| /* check the element: */ |
| pathname = element0_args[0]; |
| rc = _tmesh_fs_lookup(tmesh, |
| &pathname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_LAST_PART_OK); |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* if the name exists: */ |
| if (entry != NULL) { |
| |
| /* it must be an element: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) { |
| tme_output_append(_output, value->tmesh_parser_value_pathname0); |
| return (ENOTSOCK); |
| } |
| element0 = entry->tmesh_fs_dirent_value; |
| |
| /* we must have been given no creation arguments: */ |
| if (creation_args != NULL) { |
| return (EEXIST); |
| } |
| } |
| |
| /* otherwise, the name doesn't exist: */ |
| else { |
| |
| /* we must have been given some creation arguments: */ |
| if (creation_args == NULL) { |
| return (ENOENT); |
| } |
| |
| /* allocate the new element: */ |
| element0 = tme_new0(struct tmesh_fs_element, 1); |
| |
| /* set this element's parent and link it into the directory: */ |
| element0->tmesh_fs_element_parent = parent; |
| entry = _tmesh_fs_link(parent, tme_strdup(pathname), |
| TMESH_FS_DIRENT_ELEMENT, element0); |
| |
| /* open the log for this element: */ |
| pathname = NULL; |
| _tmesh_fs_pathname_element(element0, &pathname, NULL); |
| (*tmesh->tmesh_support.tmesh_support_log_open) |
| (&tmesh->tmesh_support, |
| &element0->tmesh_fs_element_element.tme_element_log_handle, |
| pathname, |
| creation_args[0]); |
| tme_free(pathname); |
| |
| /* create the element: */ |
| rc = tme_element_new(&element0->tmesh_fs_element_element, |
| (const char **) creation_args, |
| NULL, |
| _output); |
| |
| /* if the creation failed: */ |
| if (rc != TME_OK) { |
| |
| /* close the log for this element: */ |
| (*tmesh->tmesh_support.tmesh_support_log_close) |
| (&tmesh->tmesh_support, |
| &element0->tmesh_fs_element_element.tme_element_log_handle); |
| |
| /* unlink and free this entry: */ |
| _tmesh_fs_unlink(entry); |
| tme_free(entry->tmesh_fs_dirent_name); |
| tme_free(entry); |
| return (rc); |
| } |
| |
| /* the creation succeeded. set the generation number and preserve |
| the creation arguments: */ |
| element0->tmesh_fs_element_gen = ++tmesh->tmesh_gen_last; |
| element0->tmesh_fs_element_argv = value->tmesh_parser_value_argvs[2]; |
| _tmesh_gc_release_argv(tmesh, &element0->tmesh_fs_element_argv); |
| } |
| |
| /* if we have another element, make a connection: */ |
| if (element1 != NULL) { |
| rc = tme_element_connect(&element0->tmesh_fs_element_element, |
| (const char **) element0_args, |
| &element1->tmesh_fs_element_element, |
| (const char **) element1_args, |
| _output, &which); |
| |
| /* if the connection failed: */ |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* allocate the new connections: */ |
| for (prev = &element0->tmesh_fs_element_conns; |
| (conn0 = *prev) != NULL; |
| prev = &conn0->tmesh_fs_element_conn_next); |
| *prev = conn0 = tme_new0(struct tmesh_fs_element_conn, 1); |
| for (prev = &element1->tmesh_fs_element_conns; |
| (conn1 = *prev) != NULL; |
| prev = &conn1->tmesh_fs_element_conn_next); |
| *prev = conn1 = tme_new0(struct tmesh_fs_element_conn, 1); |
| |
| /* remember the connections: */ |
| conn0->tmesh_fs_element_conn_element = element0; |
| conn0->tmesh_fs_element_conn_gen = tmesh->tmesh_gen_last; |
| conn0->tmesh_fs_element_conn_other = conn1; |
| conn0->tmesh_fs_element_conn_argv = value->tmesh_parser_value_argvs[0]; |
| _tmesh_gc_release_argv(tmesh, &conn0->tmesh_fs_element_conn_argv); |
| conn1->tmesh_fs_element_conn_element = element1; |
| conn1->tmesh_fs_element_conn_gen = tmesh->tmesh_gen_last; |
| conn1->tmesh_fs_element_conn_other = conn0; |
| conn1->tmesh_fs_element_conn_argv = value->tmesh_parser_value_argvs[1]; |
| _tmesh_gc_release_argv(tmesh, &conn1->tmesh_fs_element_conn_argv); |
| } |
| |
| /* done: */ |
| return (rc); |
| } |
| |
| /* the "mv" command: */ |
| static int |
| _tmesh_command_mv(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| /* TBD: */ |
| abort(); |
| } |
| |
| /* the "rm" command: */ |
| static int |
| _tmesh_command_rm(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| /* TBD: */ |
| abort(); |
| } |
| |
| /* the "command" command: */ |
| static int |
| _tmesh_command_command(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| char *pathname; |
| struct tmesh_fs_element *element; |
| char **element_args; |
| int rc; |
| |
| /* look up the element: */ |
| element_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv; |
| assert(element_args != NULL); |
| element_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL; |
| pathname = element_args[0]; |
| rc = _tmesh_fs_lookup(tmesh, |
| &pathname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup succeeded: */ |
| if (rc == TME_OK) { |
| |
| /* if this pathname doesn't refer to an element, we can't |
| power it: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) { |
| rc = ENOTSOCK; |
| } |
| |
| /* otherwise, this is an element: */ |
| else { |
| element = entry->tmesh_fs_dirent_value; |
| |
| /* if it doesn't support the "command" command: */ |
| if (element->tmesh_fs_element_element.tme_element_command == NULL) { |
| rc = EOPNOTSUPP; |
| } |
| |
| /* otherwise, run the command: */ |
| else { |
| rc = ((*element->tmesh_fs_element_element.tme_element_command) |
| (&element->tmesh_fs_element_element, |
| (const char **) element_args, |
| _output)); |
| } |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /* the "log" command: */ |
| static int |
| _tmesh_command_log(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| char *pathname; |
| struct tmesh_fs_element *element; |
| char **element_args; |
| #ifndef TME_NO_LOG |
| unsigned long log_level; |
| char *p1; |
| #endif /* !TME_NO_LOG */ |
| int rc; |
| |
| /* look up the element: */ |
| element_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv; |
| assert(element_args != NULL); |
| element_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL; |
| pathname = element_args[0]; |
| rc = _tmesh_fs_lookup(tmesh, |
| &pathname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup succeeded: */ |
| if (rc == TME_OK) { |
| |
| /* if this pathname doesn't refer to an element, we can't |
| set its logging level: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) { |
| rc = ENOTSOCK; |
| } |
| |
| /* otherwise, this is an element: */ |
| else { |
| element = entry->tmesh_fs_dirent_value; |
| |
| /* if logging is not supported: */ |
| #ifdef TME_NO_LOG |
| rc = EOPNOTSUPP; |
| #else /* !TME_NO_LOG */ |
| |
| /* assume our arguments are invalid: */ |
| rc = EINVAL; |
| |
| /* check our arguments: */ |
| if (element_args[1] != NULL |
| && element_args[2] == NULL) { |
| |
| /* assume a log level of zero: */ |
| log_level = 0; |
| |
| /* "off" means a log level of zero: */ |
| if (!strcmp(element_args[1], "off")) { |
| rc = TME_OK; |
| } |
| |
| /* any other argument must be a log level: */ |
| else { |
| log_level = strtoul(element_args[1], &p1, 0); |
| if (p1 != element_args[1] |
| && *p1 == '\0') { |
| rc = TME_OK; |
| } |
| } |
| |
| /* set the log level: */ |
| if (rc == TME_OK) { |
| element->tmesh_fs_element_element.tme_element_log_handle.tme_log_handle_level_max |
| = log_level; |
| } |
| } |
| #endif /* !TME_NO_LOG */ |
| } |
| } |
| |
| return (rc); |
| } |
| |
| /* the "alias" command: */ |
| static int |
| _tmesh_command_alias(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output) |
| { |
| struct tmesh_fs_dirent *parent, *entry; |
| struct tmesh_fs_element *element; |
| char *oldname; |
| char *newname; |
| int rc; |
| |
| /* look up the existing element: */ |
| oldname = value->tmesh_parser_value_pathname1; |
| rc = _tmesh_fs_lookup(tmesh, |
| &oldname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_NORMAL); |
| |
| /* if the lookup failed, return now: */ |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* if this pathname doesn't refer to an element, we can't alias it: */ |
| if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) { |
| return (ENOTSOCK); |
| } |
| |
| /* get the element to alias: */ |
| element = entry->tmesh_fs_dirent_value; |
| |
| /* look up the new name: */ |
| newname = value->tmesh_parser_value_pathname0; |
| rc = _tmesh_fs_lookup(tmesh, |
| &newname, |
| &parent, &entry, |
| _output, |
| TMESH_SEARCH_LAST_PART_OK); |
| if (rc != TME_OK) { |
| return (rc); |
| } |
| |
| /* if the name exists: */ |
| if (entry != NULL) { |
| return (EEXIST); |
| } |
| |
| /* create the alias: */ |
| entry = _tmesh_fs_link(parent, tme_strdup(newname), |
| TMESH_FS_DIRENT_ELEMENT, element); |
| return (TME_OK); |
| } |
| |
| /* this evaluates one or more commands: */ |
| int |
| tmesh_eval(void *_tmesh, char **_output, int *_yield) |
| { |
| struct tmesh *tmesh; |
| struct tmesh_parser_value value; |
| int rc; |
| int (*command_func) _TME_P((struct tmesh *, struct tmesh_parser_value *, char **)); |
| |
| /* recover our structure: */ |
| tmesh = (struct tmesh *) _tmesh; |
| |
| /* start the output: */ |
| *_output = NULL; |
| |
| /* we don't have any memory to gc: */ |
| tmesh->tmesh_gc_record = NULL; |
| |
| /* parse a command: */ |
| rc = _tmesh_yyparse(tmesh, &value, _output, _yield); |
| |
| /* if the parse succeeded: */ |
| if (rc == TME_OK && !*_yield) { |
| |
| /* dispatch on the command: */ |
| switch (value.tmesh_parser_value_token) { |
| default: assert(FALSE); |
| case TMESH_COMMAND_NOP: command_func = NULL; break; |
| case TMESH_COMMAND_SOURCE: command_func = _tmesh_command_source; break; |
| case TMESH_COMMAND_MKDIR: command_func = _tmesh_command_mkdir; break; |
| case TMESH_COMMAND_RMDIR: command_func = _tmesh_command_rmdir; break; |
| case TMESH_COMMAND_CD: command_func = _tmesh_command_cd; break; |
| case TMESH_COMMAND_PWD: command_func = _tmesh_command_pwd; break; |
| case TMESH_COMMAND_LS: command_func = _tmesh_command_ls; break; |
| case TMESH_COMMAND_CONNECT: command_func = _tmesh_command_connect; break; |
| case TMESH_COMMAND_RM: command_func = _tmesh_command_rm; break; |
| case TMESH_COMMAND_MV: command_func = _tmesh_command_mv; break; |
| case TMESH_COMMAND_COMMAND: command_func = _tmesh_command_command; break; |
| case TMESH_COMMAND_LOG: command_func = _tmesh_command_log; break; |
| case TMESH_COMMAND_ALIAS: command_func = _tmesh_command_alias; break; |
| } |
| |
| /* call the function: */ |
| if (command_func != NULL) { |
| rc = (*command_func)(tmesh, &value, _output); |
| } |
| } |
| |
| /* garbage collect: */ |
| _tmesh_gc_gc(tmesh); |
| |
| /* done: */ |
| return (rc); |
| } |
| |
| /* this creates a new shell: */ |
| void * |
| tmesh_new(const struct tmesh_support *support, const struct tmesh_io *first_io) |
| { |
| struct tmesh *tmesh; |
| |
| /* allocate the new shell: */ |
| tmesh = tme_new(struct tmesh, 1); |
| |
| /* start the io stack: */ |
| tmesh->tmesh_io_stack = tme_new0(struct tmesh_io_stack, 1); |
| tmesh->tmesh_io_stack->tmesh_io_stack_io = *first_io; |
| |
| /* create the root directory: */ |
| tmesh->tmesh_root = _tmesh_fs_mkdir(NULL, NULL); |
| |
| /* set the cwd: */ |
| tmesh->tmesh_cwd = tmesh->tmesh_root; |
| |
| /* save the support: */ |
| tmesh->tmesh_support = *support; |
| |
| /* done: */ |
| return (tmesh); |
| } |