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