mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
935 lines
25 KiB
C
935 lines
25 KiB
C
/* $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, "%s", *(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, "%s", 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, "%s", 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);
|
|
}
|