1 /* $Id: tmesh-cmds.c,v 1.6 2006/11/15 23:12:30 fredette Exp $ */
2 
3 /* tmesh/tmesh-cmds.c - functions implementing the tmesh commands: */
4 
5 /*
6  * Copyright (c) 2003 Matt Fredette
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Matt Fredette.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <tme/common.h>
37 _TME_RCSID("$Id: tmesh-cmds.c,v 1.6 2006/11/15 23:12:30 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/threads.h>
41 #include <stdlib.h>
42 #include "tmesh-impl.h"
43 
44 /* macros: */
45 
46 /* flags for ls: */
47 #define TMESH_LS_NORMAL		(0)
48 #define TMESH_LS_ALL		TME_BIT(0)
49 #define TMESH_LS_RECURSE	TME_BIT(1)
50 #define TMESH_LS_ABSOLUTE	TME_BIT(2)
51 
52 /* the "source" command: */
53 static int
_tmesh_command_source(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)54 _tmesh_command_source(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
55 {
56   struct tmesh_io_stack *stack_new;
57   struct tmesh_io *io_new;
58   struct tmesh_io *io_old;
59   int rc;
60 
61   /* allocate the new io stack member: */
62   stack_new = tme_new(struct tmesh_io_stack, 1);
63   io_new = &stack_new->tmesh_io_stack_io;
64 
65   /* initialize the new io: */
66   io_new->tmesh_io_name = value->tmesh_parser_value_arg;
67   io_new->tmesh_io_input_line = 0;
68 
69   /* call the current input source's open function: */
70   io_old = &tmesh->tmesh_io_stack->tmesh_io_stack_io;
71   rc = (*io_old->tmesh_io_open)(io_new, io_old, _output);
72 
73   /* if the open succeeded, push this new input source onto the stack,
74      otherwise free it: */
75   if (rc == TME_OK) {
76     _tmesh_gc_release(tmesh, io_new->tmesh_io_name);
77     stack_new->tmesh_io_stack_next = tmesh->tmesh_io_stack;
78     tmesh->tmesh_io_stack = stack_new;
79   }
80   else {
81     tme_free(stack_new);
82   }
83 
84   /* done: */
85   return (rc);
86 }
87 
88 /* the "mkdir" command: */
89 static int
_tmesh_command_mkdir(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)90 _tmesh_command_mkdir(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
91 {
92   struct tmesh_fs_dirent *parent, *entry;
93   char *dirname;
94   int rc;
95 
96   /* look up the new directory name: */
97   dirname = value->tmesh_parser_value_pathname0;
98   rc = _tmesh_fs_lookup(tmesh,
99 			&dirname,
100 			&parent, &entry,
101 			_output,
102 			TMESH_SEARCH_LAST_PART_OK);
103 
104   /* if the lookup succeeded: */
105   if (rc == TME_OK) {
106 
107     /* if the new directory name already exists, we can't
108        make it: */
109     if (entry != NULL) {
110       rc = EEXIST;
111     }
112 
113     /* otherwise, make the new directory: */
114     else {
115       _tmesh_fs_mkdir(parent, tme_strdup(dirname));
116     }
117   }
118 
119   return (rc);
120 }
121 
122 /* the "rmdir" command: */
123 static int
_tmesh_command_rmdir(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)124 _tmesh_command_rmdir(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
125 {
126   struct tmesh_fs_dirent *parent, *entry, *dir;
127   char *dirname;
128   int rc;
129 
130   /* look up the directory: */
131   dirname = value->tmesh_parser_value_pathname0;
132   rc = _tmesh_fs_lookup(tmesh,
133 			&dirname,
134 			&parent, &entry,
135 			_output,
136 			TMESH_SEARCH_NORMAL);
137 
138   /* if the lookup succeeded: */
139   if (rc == TME_OK) {
140 
141     /* if this pathname doesn't refer to a directory, we can't
142        remove it: */
143     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_DIR) {
144       rc = ENOTDIR;
145     }
146 
147     /* otherwise, this is a directory: */
148     else {
149       dir = entry->tmesh_fs_dirent_value;
150 
151       /* if the directory isn't empty, we can't remove it: */
152       if (dir->tmesh_fs_dirent_prev
153 	  != &dir->tmesh_fs_dirent_next->tmesh_fs_dirent_next) {
154 	rc = ENOTEMPTY;
155       }
156 
157       /* you can't remove the current working directory,
158 	 or the "." or ".." directories: */
159       else if (dir == tmesh->tmesh_cwd
160 	       || !strcmp(entry->tmesh_fs_dirent_name, ".")
161 	       || !strcmp(entry->tmesh_fs_dirent_name, "..")) {
162 	rc = EACCES;
163       }
164 
165       /* otherwise, remove the directory: */
166       else {
167 
168 	/* unlink the directory in the parent: */
169 	_tmesh_fs_unlink(entry);
170 
171 	/* free the "." and ".." directory entries: */
172 	tme_free(entry->tmesh_fs_dirent_next->tmesh_fs_dirent_name);
173 	tme_free(entry->tmesh_fs_dirent_next);
174 	tme_free(entry->tmesh_fs_dirent_name);
175 	tme_free(entry);
176       }
177     }
178   }
179 
180   return (rc);
181 }
182 
183 /* the "cd" command: */
184 static int
_tmesh_command_cd(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)185 _tmesh_command_cd(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
186 {
187   struct tmesh_fs_dirent *parent, *entry;
188   char *dirname;
189   int rc;
190 
191   /* look up the directory: */
192   dirname = value->tmesh_parser_value_pathname0;
193   rc = _tmesh_fs_lookup(tmesh,
194 			&dirname,
195 			&parent, &entry,
196 			_output,
197 			TMESH_SEARCH_NORMAL);
198 
199   /* if the lookup succeeded: */
200   if (rc == TME_OK) {
201 
202     /* if this pathname doesn't refer to a directory, we can't
203        cd to it: */
204     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_DIR) {
205       rc = ENOTDIR;
206     }
207 
208     /* otherwise, this is a directory: */
209     else {
210 
211       /* this is the new current working directory: */
212       tmesh->tmesh_cwd = entry->tmesh_fs_dirent_value;
213     }
214   }
215 
216   return (rc);
217 }
218 
219 /* the "pwd" command: */
220 static int
_tmesh_command_pwd(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)221 _tmesh_command_pwd(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
222 {
223   _tmesh_fs_pathname_dir(tmesh->tmesh_cwd, _output, NULL);
224   tme_output_append(_output, "\n");
225   return (TME_OK);
226 }
227 
228 /* this outputs an argv: */
229 static void
_tmesh_ls_output_argv(char ** _output,struct tmesh_parser_argv * argv,unsigned int skip)230 _tmesh_ls_output_argv(char **_output, struct tmesh_parser_argv *argv, unsigned int skip)
231 {
232   unsigned int argc;
233   char **args;
234   argc = argv->tmesh_parser_argv_argc;
235   args = argv->tmesh_parser_argv_argv;
236   assert(argc > 0 && argc >= skip);
237   argc -= skip;
238   args += skip;
239   for (; argc-- > 0; ) {
240     tme_output_append(_output, " ");
241     tme_output_append(_output, *(args++));
242   }
243 }
244 
245 /* this lists an element: */
246 static void
_tmesh_ls_element(struct tmesh_fs_element * element,char ** _output,int flags)247 _tmesh_ls_element(struct tmesh_fs_element *element,
248 		  char **_output,
249 		  int flags)
250 {
251   int output_element_argv;
252   struct tmesh_fs_element *element_other;
253   struct tmesh_fs_element_conn *conn, *conn_other;
254 
255   /* we haven't yet output the element's argv: */
256   output_element_argv = FALSE;
257 
258   /* loop over the element's connections: */
259   for (conn = element->tmesh_fs_element_conns;
260        conn != NULL;
261        conn = conn->tmesh_fs_element_conn_next) {
262 
263     /* if we're not showing all connections, and this connection was
264        made after this element was created, skip it: */
265     if (!(flags & TMESH_LS_ALL)
266 	&& (conn->tmesh_fs_element_conn_gen
267 	    > element->tmesh_fs_element_gen)) {
268       continue;
269     }
270 
271     /* output this element's name and connection argv: */
272     _tmesh_fs_pathname_element(element, _output,
273 			       ((flags & TMESH_LS_ABSOLUTE)
274 				? NULL
275 				: element->tmesh_fs_element_parent));
276     _tmesh_ls_output_argv(_output, &conn->tmesh_fs_element_conn_argv, 1);
277 
278     /* get the other side of this connection: */
279     conn_other = conn->tmesh_fs_element_conn_other;
280     element_other = conn_other->tmesh_fs_element_conn_element;
281     tme_output_append(_output, " at ");
282 
283     /* output the other element's name and connection argv: */
284     _tmesh_fs_pathname_element(element_other, _output,
285 			       ((flags & TMESH_LS_ABSOLUTE)
286 				? NULL
287 				: element->tmesh_fs_element_parent));
288     _tmesh_ls_output_argv(_output, &conn_other->tmesh_fs_element_conn_argv, 1);
289 
290     /* if we haven't output the element's creation argv yet, do so: */
291     if (!output_element_argv) {
292       tme_output_append(_output, ":");
293       _tmesh_ls_output_argv(_output, &element->tmesh_fs_element_argv, 0);
294       output_element_argv = TRUE;
295     }
296 
297     /* output a newline: */
298     tme_output_append(_output, "\n");
299   }
300 
301   /* if we haven't output the element's creation argv yet, do so: */
302   if (!output_element_argv) {
303     _tmesh_fs_pathname_element(element, _output,
304 			       ((flags & TMESH_LS_ABSOLUTE)
305 				? NULL
306 				: element->tmesh_fs_element_parent));
307     tme_output_append(_output, ":");
308     _tmesh_ls_output_argv(_output, &element->tmesh_fs_element_argv, 0);
309     tme_output_append(_output, "\n");
310   }
311 }
312 
313 /* this lists a directory: */
314 static void
_tmesh_ls_dir(struct tmesh_fs_dirent * parent,char ** _output,struct tmesh_fs_dirent * parent_top,int flags)315 _tmesh_ls_dir(struct tmesh_fs_dirent *parent,
316 	      char **_output,
317 	      struct tmesh_fs_dirent *parent_top,
318 	      int flags)
319 {
320   struct tmesh_fs_dirent *entry, *dir;
321   struct tmesh_fs_element *element;
322   int pass;
323 
324   /* list this directory: */
325   for (pass = 0; ++pass < 2;) {
326 
327     /* if this is pass two, but we're not recursing, stop: */
328     if (pass == 2
329 	&& !(flags & TMESH_LS_RECURSE)) {
330       return;
331     }
332 
333     /* loop over the entries in the directory: */
334     entry = parent;
335     do {
336 
337       /* dispatch on the directory entry's type: */
338       switch (entry->tmesh_fs_dirent_type) {
339 
340 	/* this is a subdirectory: */
341       case TMESH_FS_DIRENT_DIR:
342 	dir = entry->tmesh_fs_dirent_value;
343 
344 	/* handle a "." or ".." subdirectory: */
345 	if (!strcmp(entry->tmesh_fs_dirent_name, ".")
346 	    || !strcmp(entry->tmesh_fs_dirent_name, "..")) {
347 
348 	  /* if we're listing all, and this is pass one: */
349 	  if ((flags & TMESH_LS_ALL)
350 	      && pass == 1) {
351 
352 	    /* output the directory's name: */
353 	    _tmesh_fs_pathname_dir(dir, _output,
354 				   ((flags & TMESH_LS_ABSOLUTE)
355 				    ? NULL
356 				    : parent));
357 	    tme_output_append(_output, "\n");
358 	  }
359 	}
360 
361 	/* otherwise this is a regular subdirectory: */
362 	else {
363 
364 	  /* if this is pass one: */
365 	  if (pass == 1) {
366 
367 	    /* output the directory's name: */
368 	    _tmesh_fs_pathname_dir(dir, _output,
369 				   ((flags & TMESH_LS_ABSOLUTE)
370 				    ? NULL
371 				    : parent));
372 	    tme_output_append(_output, "/\n");
373 	  }
374 
375 	  /* otherwise, this is pass two: */
376 	  else {
377 
378 	    /* output the directory's name and recurse: */
379 	    tme_output_append(_output, "\n./");
380 	    _tmesh_fs_pathname_dir(dir, _output,
381 				   ((flags & TMESH_LS_ABSOLUTE)
382 				    ? NULL
383 				    : parent_top));
384 	    tme_output_append(_output, ":\n");
385 	    _tmesh_ls_dir(dir, _output, parent_top, flags);
386 	  }
387 	}
388 	break;
389 
390 	/* this is an element: */
391       case TMESH_FS_DIRENT_ELEMENT:
392 	element = entry->tmesh_fs_dirent_value;
393 	_tmesh_ls_element(element, _output, flags);
394 	break;
395 
396       default: assert(FALSE);
397       }
398 
399       /* if this was the last entry in the directory, we're done: */
400       entry = entry->tmesh_fs_dirent_next;
401     } while (entry != parent);
402   }
403 }
404 
405 /* the "ls" command: */
406 static int
_tmesh_command_ls(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)407 _tmesh_command_ls(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
408 {
409   char *opts, *pathname, opt;
410   struct tmesh_fs_dirent *parent, *entry;
411   int type;
412   void *what;
413   int rc;
414   int flags;
415 
416   /* take apart any options: */
417   opts = value->tmesh_parser_value_strings[0];
418   flags = TMESH_LS_NORMAL;
419   if (opts != NULL) {
420     assert(*opts == '-');
421     for (; (opt = *(++opts)) != '\0'; ) {
422       switch (opt) {
423 
424 	/* 'a' lists all directory entries, and all connections on elements: */
425       case 'a': flags |= TMESH_LS_ALL; break;
426 
427 	/* 'l' lists absolute pathnames for elements: */
428       case 'l': flags |= TMESH_LS_ABSOLUTE; break;
429 
430 	/* 'R' recurses: */
431       case 'R': flags |= TMESH_LS_RECURSE; break;
432 
433 	/* an unknown option: */
434       default:
435 	tme_output_append(_output,
436 			  "ls: %s '-%c'\n",
437 			  _("invalid option"),
438 			  opt);
439 	return (EINVAL);
440       }
441     }
442   }
443 
444   /* if a path name is given, look it up: */
445   pathname = value->tmesh_parser_value_strings[1];
446   if (pathname != NULL) {
447     rc = _tmesh_fs_lookup(tmesh,
448 			  &pathname,
449 			  &parent, &entry,
450 			  _output,
451 			  TMESH_SEARCH_NORMAL);
452 
453     /* if the lookup failed: */
454     if (rc != TME_OK) {
455       return (rc);
456     }
457 
458     /* get what we're listing: */
459     type = entry->tmesh_fs_dirent_type;
460     what = entry->tmesh_fs_dirent_value;
461   }
462 
463   /* otherwise, list the cwd: */
464   else {
465     type = TMESH_FS_DIRENT_DIR;
466     what = tmesh->tmesh_cwd;
467   }
468 
469   /* dispatch on the type: */
470   switch (type) {
471   case TMESH_FS_DIRENT_DIR:
472     _tmesh_ls_dir(what, _output, what, flags);
473     break;
474   case TMESH_FS_DIRENT_ELEMENT:
475     _tmesh_ls_element(what, _output, flags);
476     break;
477   default: assert(FALSE);
478   }
479 
480   return (TME_OK);
481 }
482 
483 /* the "connect" command: */
484 static int
_tmesh_command_connect(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)485 _tmesh_command_connect(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
486 {
487   struct tmesh_fs_dirent *parent, *entry;
488   char *pathname;
489   struct tmesh_fs_element *element0, *element1;
490   char **element0_args;
491   char **element1_args;
492   char **creation_args;
493   struct tmesh_fs_element_conn *conn0, *conn1, **prev;
494   int which;
495   int rc;
496 
497   /* get and NULL-terminate all argument lists: */
498   element0_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv;
499   assert(element0_args != NULL);
500   element0_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL;
501   element1_args = value->tmesh_parser_value_argvs[1].tmesh_parser_argv_argv;
502   if (element1_args != NULL) {
503     element1_args[value->tmesh_parser_value_argvs[1].tmesh_parser_argv_argc] = NULL;
504   }
505   creation_args = value->tmesh_parser_value_argvs[2].tmesh_parser_argv_argv;
506   if (creation_args != NULL) {
507     creation_args[value->tmesh_parser_value_argvs[2].tmesh_parser_argv_argc] = NULL;
508   }
509 
510   /* check any other element to connect to: */
511   element1 = NULL;
512   if (element1_args != NULL) {
513 
514     /* look up the element: */
515     pathname = element1_args[0];
516     rc = _tmesh_fs_lookup(tmesh,
517 			  &pathname,
518 			  &parent, &entry,
519 			  _output,
520 			  TMESH_SEARCH_NORMAL);
521     if (rc != TME_OK) {
522       return (rc);
523     }
524 
525     /* this must be an element: */
526     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) {
527       tme_output_append(_output, element1_args[0]);
528       return (ENOTSOCK);
529     }
530     element1 = entry->tmesh_fs_dirent_value;
531   }
532 
533   /* check the element: */
534   pathname = element0_args[0];
535   rc = _tmesh_fs_lookup(tmesh,
536 			&pathname,
537 			&parent, &entry,
538 			_output,
539 			TMESH_SEARCH_LAST_PART_OK);
540   if (rc != TME_OK) {
541     return (rc);
542   }
543 
544   /* if the name exists: */
545   if (entry != NULL) {
546 
547     /* it must be an element: */
548     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) {
549       tme_output_append(_output, value->tmesh_parser_value_pathname0);
550       return (ENOTSOCK);
551     }
552     element0 = entry->tmesh_fs_dirent_value;
553 
554     /* we must have been given no creation arguments: */
555     if (creation_args != NULL) {
556       return (EEXIST);
557     }
558   }
559 
560   /* otherwise, the name doesn't exist: */
561   else {
562 
563     /* we must have been given some creation arguments: */
564     if (creation_args == NULL) {
565       return (ENOENT);
566     }
567 
568     /* allocate the new element: */
569     element0 = tme_new0(struct tmesh_fs_element, 1);
570 
571     /* set this element's parent and link it into the directory: */
572     element0->tmesh_fs_element_parent = parent;
573     entry = _tmesh_fs_link(parent, tme_strdup(pathname),
574 			   TMESH_FS_DIRENT_ELEMENT, element0);
575 
576     /* open the log for this element: */
577     pathname = NULL;
578     _tmesh_fs_pathname_element(element0, &pathname, NULL);
579     (*tmesh->tmesh_support.tmesh_support_log_open)
580       (&tmesh->tmesh_support,
581        &element0->tmesh_fs_element_element.tme_element_log_handle,
582        pathname,
583        creation_args[0]);
584     tme_free(pathname);
585 
586     /* create the element: */
587     rc = tme_element_new(&element0->tmesh_fs_element_element,
588 			 (const char **) creation_args,
589 			 NULL,
590 			 _output);
591 
592     /* if the creation failed: */
593     if (rc != TME_OK) {
594 
595       /* close the log for this element: */
596       (*tmesh->tmesh_support.tmesh_support_log_close)
597 	(&tmesh->tmesh_support,
598 	 &element0->tmesh_fs_element_element.tme_element_log_handle);
599 
600       /* unlink and free this entry: */
601       _tmesh_fs_unlink(entry);
602       tme_free(entry->tmesh_fs_dirent_name);
603       tme_free(entry);
604       return (rc);
605     }
606 
607     /* the creation succeeded.  set the generation number and preserve
608        the creation arguments: */
609     element0->tmesh_fs_element_gen = ++tmesh->tmesh_gen_last;
610     element0->tmesh_fs_element_argv = value->tmesh_parser_value_argvs[2];
611     _tmesh_gc_release_argv(tmesh, &element0->tmesh_fs_element_argv);
612   }
613 
614   /* if we have another element, make a connection: */
615   if (element1 != NULL) {
616     rc = tme_element_connect(&element0->tmesh_fs_element_element,
617 			     (const char **) element0_args,
618 			     &element1->tmesh_fs_element_element,
619 			     (const char **) element1_args,
620 			     _output, &which);
621 
622     /* if the connection failed: */
623     if (rc != TME_OK) {
624       return (rc);
625     }
626 
627     /* allocate the new connections: */
628     for (prev = &element0->tmesh_fs_element_conns;
629 	 (conn0 = *prev) != NULL;
630 	 prev = &conn0->tmesh_fs_element_conn_next);
631     *prev = conn0 = tme_new0(struct tmesh_fs_element_conn, 1);
632     for (prev = &element1->tmesh_fs_element_conns;
633 	 (conn1 = *prev) != NULL;
634 	 prev = &conn1->tmesh_fs_element_conn_next);
635     *prev = conn1 = tme_new0(struct tmesh_fs_element_conn, 1);
636 
637     /* remember the connections: */
638     conn0->tmesh_fs_element_conn_element = element0;
639     conn0->tmesh_fs_element_conn_gen = tmesh->tmesh_gen_last;
640     conn0->tmesh_fs_element_conn_other = conn1;
641     conn0->tmesh_fs_element_conn_argv = value->tmesh_parser_value_argvs[0];
642     _tmesh_gc_release_argv(tmesh, &conn0->tmesh_fs_element_conn_argv);
643     conn1->tmesh_fs_element_conn_element = element1;
644     conn1->tmesh_fs_element_conn_gen = tmesh->tmesh_gen_last;
645     conn1->tmesh_fs_element_conn_other = conn0;
646     conn1->tmesh_fs_element_conn_argv = value->tmesh_parser_value_argvs[1];
647     _tmesh_gc_release_argv(tmesh, &conn1->tmesh_fs_element_conn_argv);
648   }
649 
650   /* done: */
651   return (rc);
652 }
653 
654 /* the "mv" command: */
655 static int
_tmesh_command_mv(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)656 _tmesh_command_mv(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
657 {
658   /* TBD: */
659   abort();
660 }
661 
662 /* the "rm" command: */
663 static int
_tmesh_command_rm(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)664 _tmesh_command_rm(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
665 {
666   /* TBD: */
667   abort();
668 }
669 
670 /* the "command" command: */
671 static int
_tmesh_command_command(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)672 _tmesh_command_command(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
673 {
674   struct tmesh_fs_dirent *parent, *entry;
675   char *pathname;
676   struct tmesh_fs_element *element;
677   char **element_args;
678   int rc;
679 
680   /* look up the element: */
681   element_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv;
682   assert(element_args != NULL);
683   element_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL;
684   pathname = element_args[0];
685   rc = _tmesh_fs_lookup(tmesh,
686 			&pathname,
687 			&parent, &entry,
688 			_output,
689 			TMESH_SEARCH_NORMAL);
690 
691   /* if the lookup succeeded: */
692   if (rc == TME_OK) {
693 
694     /* if this pathname doesn't refer to an element, we can't
695        power it: */
696     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) {
697       rc = ENOTSOCK;
698     }
699 
700     /* otherwise, this is an element: */
701     else {
702       element = entry->tmesh_fs_dirent_value;
703 
704       /* if it doesn't support the "command" command: */
705       if (element->tmesh_fs_element_element.tme_element_command == NULL) {
706 	rc = EOPNOTSUPP;
707       }
708 
709       /* otherwise, run the command: */
710       else {
711 	rc = ((*element->tmesh_fs_element_element.tme_element_command)
712 	      (&element->tmesh_fs_element_element,
713 	       (const char **) element_args,
714 	       _output));
715       }
716     }
717   }
718 
719   return (rc);
720 }
721 
722 /* the "log" command: */
723 static int
_tmesh_command_log(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)724 _tmesh_command_log(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
725 {
726   struct tmesh_fs_dirent *parent, *entry;
727   char *pathname;
728   struct tmesh_fs_element *element;
729   char **element_args;
730 #ifndef TME_NO_LOG
731   unsigned long log_level;
732   char *p1;
733 #endif /* !TME_NO_LOG */
734   int rc;
735 
736   /* look up the element: */
737   element_args = value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argv;
738   assert(element_args != NULL);
739   element_args[value->tmesh_parser_value_argvs[0].tmesh_parser_argv_argc] = NULL;
740   pathname = element_args[0];
741   rc = _tmesh_fs_lookup(tmesh,
742 			&pathname,
743 			&parent, &entry,
744 			_output,
745 			TMESH_SEARCH_NORMAL);
746 
747   /* if the lookup succeeded: */
748   if (rc == TME_OK) {
749 
750     /* if this pathname doesn't refer to an element, we can't
751        set its logging level: */
752     if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) {
753       rc = ENOTSOCK;
754     }
755 
756     /* otherwise, this is an element: */
757     else {
758       element = entry->tmesh_fs_dirent_value;
759 
760       /* if logging is not supported: */
761 #ifdef TME_NO_LOG
762       rc = EOPNOTSUPP;
763 #else  /* !TME_NO_LOG */
764 
765       /* assume our arguments are invalid: */
766       rc = EINVAL;
767 
768       /* check our arguments: */
769       if (element_args[1] != NULL
770 	  && element_args[2] == NULL) {
771 
772 	/* assume a log level of zero: */
773 	log_level = 0;
774 
775 	/* "off" means a log level of zero: */
776 	if (!strcmp(element_args[1], "off")) {
777 	  rc = TME_OK;
778 	}
779 
780 	/* any other argument must be a log level: */
781 	else {
782 	  log_level = strtoul(element_args[1], &p1, 0);
783 	  if (p1 != element_args[1]
784 	      && *p1 == '\0') {
785 	    rc = TME_OK;
786 	  }
787 	}
788 
789 	/* set the log level: */
790 	if (rc == TME_OK) {
791 	  element->tmesh_fs_element_element.tme_element_log_handle.tme_log_handle_level_max
792 	    = log_level;
793 	}
794       }
795 #endif /* !TME_NO_LOG */
796     }
797   }
798 
799   return (rc);
800 }
801 
802 /* the "alias" command: */
803 static int
_tmesh_command_alias(struct tmesh * tmesh,struct tmesh_parser_value * value,char ** _output)804 _tmesh_command_alias(struct tmesh *tmesh, struct tmesh_parser_value *value, char **_output)
805 {
806   struct tmesh_fs_dirent *parent, *entry;
807   struct tmesh_fs_element *element;
808   char *oldname;
809   char *newname;
810   int rc;
811 
812   /* look up the existing element: */
813   oldname = value->tmesh_parser_value_pathname1;
814   rc = _tmesh_fs_lookup(tmesh,
815 			&oldname,
816 			&parent, &entry,
817 			_output,
818 			TMESH_SEARCH_NORMAL);
819 
820   /* if the lookup failed, return now: */
821   if (rc != TME_OK) {
822     return (rc);
823   }
824 
825   /* if this pathname doesn't refer to an element, we can't alias it: */
826   if (entry->tmesh_fs_dirent_type != TMESH_FS_DIRENT_ELEMENT) {
827     return (ENOTSOCK);
828   }
829 
830   /* get the element to alias: */
831   element = entry->tmesh_fs_dirent_value;
832 
833   /* look up the new name: */
834   newname = value->tmesh_parser_value_pathname0;
835   rc = _tmesh_fs_lookup(tmesh,
836 			&newname,
837 			&parent, &entry,
838 			_output,
839 			TMESH_SEARCH_LAST_PART_OK);
840   if (rc != TME_OK) {
841     return (rc);
842   }
843 
844   /* if the name exists: */
845   if (entry != NULL) {
846     return (EEXIST);
847   }
848 
849   /* create the alias: */
850   entry = _tmesh_fs_link(parent, tme_strdup(newname),
851 			 TMESH_FS_DIRENT_ELEMENT, element);
852   return (TME_OK);
853 }
854 
855 /* this evaluates one or more commands: */
856 int
tmesh_eval(void * _tmesh,char ** _output,int * _yield)857 tmesh_eval(void *_tmesh, char **_output, int *_yield)
858 {
859   struct tmesh *tmesh;
860   struct tmesh_parser_value value;
861   int rc;
862   int (*command_func) _TME_P((struct tmesh *, struct tmesh_parser_value *, char **));
863 
864   /* recover our structure: */
865   tmesh = (struct tmesh *) _tmesh;
866 
867   /* start the output: */
868   *_output = NULL;
869 
870   /* we don't have any memory to gc: */
871   tmesh->tmesh_gc_record = NULL;
872 
873   /* parse a command: */
874   rc = _tmesh_yyparse(tmesh, &value, _output, _yield);
875 
876   /* if the parse succeeded: */
877   if (rc == TME_OK && !*_yield) {
878 
879     /* dispatch on the command: */
880     switch (value.tmesh_parser_value_token) {
881     default: assert(FALSE);
882     case TMESH_COMMAND_NOP: command_func = NULL; break;
883     case TMESH_COMMAND_SOURCE: command_func = _tmesh_command_source; break;
884     case TMESH_COMMAND_MKDIR: command_func = _tmesh_command_mkdir; break;
885     case TMESH_COMMAND_RMDIR: command_func = _tmesh_command_rmdir; break;
886     case TMESH_COMMAND_CD: command_func = _tmesh_command_cd; break;
887     case TMESH_COMMAND_PWD: command_func = _tmesh_command_pwd; break;
888     case TMESH_COMMAND_LS: command_func = _tmesh_command_ls; break;
889     case TMESH_COMMAND_CONNECT: command_func = _tmesh_command_connect; break;
890     case TMESH_COMMAND_RM: command_func = _tmesh_command_rm; break;
891     case TMESH_COMMAND_MV: command_func = _tmesh_command_mv; break;
892     case TMESH_COMMAND_COMMAND: command_func = _tmesh_command_command; break;
893     case TMESH_COMMAND_LOG: command_func = _tmesh_command_log; break;
894     case TMESH_COMMAND_ALIAS: command_func = _tmesh_command_alias; break;
895     }
896 
897     /* call the function: */
898     if (command_func != NULL) {
899       rc = (*command_func)(tmesh, &value, _output);
900     }
901   }
902 
903   /* garbage collect: */
904   _tmesh_gc_gc(tmesh);
905 
906   /* done: */
907   return (rc);
908 }
909 
910 /* this creates a new shell: */
911 void *
tmesh_new(const struct tmesh_support * support,const struct tmesh_io * first_io)912 tmesh_new(const struct tmesh_support *support, const struct tmesh_io *first_io)
913 {
914   struct tmesh *tmesh;
915 
916   /* allocate the new shell: */
917   tmesh = tme_new(struct tmesh, 1);
918 
919   /* start the io stack: */
920   tmesh->tmesh_io_stack = tme_new0(struct tmesh_io_stack, 1);
921   tmesh->tmesh_io_stack->tmesh_io_stack_io = *first_io;
922 
923   /* create the root directory: */
924   tmesh->tmesh_root = _tmesh_fs_mkdir(NULL, NULL);
925 
926   /* set the cwd: */
927   tmesh->tmesh_cwd = tmesh->tmesh_root;
928 
929   /* save the support: */
930   tmesh->tmesh_support = *support;
931 
932   /* done: */
933   return (tmesh);
934 }
935