1 /*************************************************************
2 * $Id: getcom.c,v 1.10 2003/09/25 15:54:27 marcolz Exp $
3 *
4 * getcom.c
5 *
6 * Get a command, and do a bunch of things to it.
7 *
8 * - convert aliases
9 * - replace variable strings
10 * - Check if we are in if-endif sequence
11 * - control where the command is coming from
12 *
13 * And a zillion other little details.
14 */
15 /*******************************************************
16 * Copyright (C) Doug Hay, 1991.
17 * Permission to use and abuse this code, as long
18 * as this copyright notice stays intact and with the
19 * code. No warranty implied. This code supplied as is.
20 *******************************************************/
21
22 #include "config.h"
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif /* HAVE_STDLIB_H */
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif /* HAVE_STRING_H */
33 #ifdef HAVE_STRINGS_H
34 #include <strings.h>
35 #endif /* HAVE_STRINGS_H */
36
37 #ifdef HAVE_READLINE_READLINE_H
38 #include <readline/history.h>
39 #include <readline/readline.h>
40 #endif /* HAVE_READLINE_READLINE_H */
41 #ifdef HAVE_READLINE_TILDE_H
42 #include <readline/tilde.h>
43 #endif /* HAVE_READLINE_TILDE_H */
44
45 #include "alias.h"
46 #include "getcom.h"
47 #include "print.h"
48 #include "vars.h"
49
50 extern int Interrupt; /* True if we got a SIGINT */
51 extern int Pipe_Interrupt; /* True if we got a SIGPIPE */
52
53 static FILE *current_input;
54 static int current_input_isapipe;
55
56 #define FSTACKMAX 40
57 #define FSTK_USER 0
58 #define FSTK_EXEC 1
59 #define FSTK_REDIR 2
60 #define FSTK_CMDPART 3
61
62 typedef struct {
63 int type;
64 int depth; /* Depth of if's */
65 int disabled; /* True if commands being ignored due
66 * to if and the depth at which
67 * disabled. */
68 union {
69 struct {
70 FILE *oldin;
71 int ispipe;
72 } exec;
73 struct {
74 FILE *oldout;
75 int ispipe;
76 } redir;
77 struct {
78 char *cmd;
79 } cmdpart;
80 } un;
81 } filesav_t;
82
83 static int starwalk = 1;
84 static int fsk_depth = -1;
85 static filesav_t fstack[FSTACKMAX];
86
87 /* Local to this file */
88 static int change_vars(char *cmd, char *buf);
89 static void stuff_semis(char *cmd);
90 static int replace_alias(char *cmd, char *alias, char *ret);
91 static int find_aliasarg(int arg, char *cmd, char **retp,
92 const char *condp, char **maxcp, char *alias);
93 static int find_redirs(char *cmd, char *buf);
94 static filesav_t *fst_add(int typ);
95 static int get_nextbit(char *cmd, int cmdlen, const char *prompt,
96 int sub, int dontstrip);
97
98 static filesav_t *
fst_add(typ)99 fst_add(typ)
100 int typ;
101 {
102 filesav_t *fs;
103
104 if (fsk_depth >= FSTACKMAX - 1) {
105 eprt("Error: Too many cmd stack layers, max %d\n", FSTACKMAX);
106 return ((filesav_t *) 0);
107 }
108 fs = &fstack[++fsk_depth];
109 fs->type = typ;
110 return (fs);
111 }
112
113 static int
get_nextbit(cmd,cmdlen,prompt,sub,dontstrip)114 get_nextbit(cmd, cmdlen, prompt, sub, dontstrip)
115 char *cmd;
116 const char *prompt;
117 int cmdlen, dontstrip, sub;
118 {
119 filesav_t *fst;
120 static char lastcom[1024];
121 char *cp, *bp;
122 int cmdok;
123 int echoed = 0;
124
125 #ifndef READLINE_HANDLES_CONST
126 union {
127 const char *cchp;
128 char *chp;
129 } const_temp;
130
131 const_temp.cchp = prompt;
132 #endif
133
134 *cmd = '\0';
135 do {
136 cmdok = 1;
137 fst = &fstack[fsk_depth];
138 if (FSTK_CMDPART == fst->type) {
139 cp = fst->un.cmdpart.cmd;
140 if (!dontstrip)
141 while (isspace(*cp))
142 cp++;
143 strcpy(cmd, cp);
144 free(fst->un.cmdpart.cmd);
145 fsk_depth--;
146 } else if (FSTK_EXEC == fst->type) {
147 if (!fgets(cmd, cmdlen, current_input)) {
148 if (current_input_isapipe) {
149 (void) pclose(current_input);
150 } else
151 (void) fclose(current_input);
152 current_input = fst->un.exec.oldin;
153 current_input_isapipe = fst->un.exec.ispipe;
154 fsk_depth--;
155 prt("End of exec\n");
156 cmdok = 0;
157 continue;
158 }
159 if (!dontstrip && isspace(*cmd)) {
160 cp = cmd;
161 while (isspace(*cp))
162 cp++;
163 strcpy(cmd, cp);
164 }
165 prt("%s", cmd);
166 echoed = 1;
167 } else if (FSTK_USER == fst->type) {
168 if (sub) {
169 if (prompt)
170 eprt("%s", prompt);
171 (void) fflush(stdout);
172 (void) fflush(stderr);
173 if (!fgets(cmd, cmdlen, stdin)) {
174 strcpy(cmd, "ctld");
175 clearerr(stdin);
176 }
177 } else {
178 do {
179 int eofd = 0;
180
181 /*
182 * Keep reading until we get a
183 * non-null input line.
184 */
185 do {
186 #ifdef READLINE_HANDLES_CONST
187 if ((cp = readline(prompt))) {
188 #else /* ! READLINE_HANDLES_CONST */
189 /*
190 * Dirty trick to make it
191 * compile, don't try this at
192 * home. This is ok only
193 * because readline does not
194 * modify prompt...
195 */
196 if ((cp = readline(const_temp.chp))) {
197 #endif /* ! READLINE_HANDLES_CONST */
198 if (!dontstrip)
199 while (isspace(*cp))
200 cp++;
201 if (!*cp) {
202 /*
203 * Return
204 * blank
205 * lines.
206 */
207 *cmd = '\0';
208 return (0);
209 }
210 } else {
211 if (++eofd > 20) {
212 eprt("Error: Input eof'd more than 20 times, dying.\n");
213 exit(1);
214 }
215 }
216 } while (!cp || !*cp);
217
218 /*
219 * Now, if she input "!" or "^", we
220 * parse it here.
221 */
222 if (cp && ('!' == *cp || '^' == *cp)) {
223 if (-1 == history_expand(cp, &bp)) {
224 eprt("Syntax: Error in history: %s\n", bp);
225 cp = (char *)NULL;
226 } else {
227 cp = bp;
228 /*
229 * Echo it to the
230 * user
231 */
232 prt("%s\n", cp);
233 echoed = 1;
234 }
235 }
236 } while (!cp || !*cp);
237
238 /*
239 * Only non-null, non-duplicate user input
240 * lines onto history
241 */
242 if (cp && *cp) {
243 if (*cp != lastcom[0] || (strcmp(cp, lastcom))) {
244 add_history(cp);
245 strcpy(lastcom, cp);
246 }
247 }
248 strcpy(cmd, cp);
249 }
250 }
251 } while (!cmdok);
252
253 /* Remove any newline chars at end. */
254 cp = cmd;
255 while (*cp && '\n' != *cp)
256 cp++;
257 *cp = '\0';
258 return (echoed);
259 }
260
261 /* unescapes only those characters that needed escaping. */
262 int
unescape_backslashes(dst,src)263 unescape_backslashes(dst, src)
264 char *dst;
265 const char *src;
266 {
267 const char *sp = src;
268 char *dp = dst;
269
270 while (*sp)
271 {
272 if (*sp == '\\') {
273 switch (sp[1])
274 {
275 case '\0':
276 eprt("Syntax: Error in unescape: trailing \\\n");
277 return 1;
278 case '"':
279 case ';':
280 case '$':
281 case '&':
282 case '|':
283 case '\\':
284 /* skip */
285 sp++;
286 break;
287 default:
288 /* NOTHING */
289 ;
290 }
291 }
292 *dp++ = *sp++;
293 }
294 *dp = 0;
295 return 0;
296 }
297
298 /************************************
299 * get_main_input
300 *
301 * Return the next line of user input.
302 *
303 * We must:
304 * Expand aliases.
305 * Parse seperate command lines, seperated by ";".
306 * Expand variables.
307 */
308 int
get_main_input(retbuf,len,prompt)309 get_main_input(retbuf, len, prompt)
310 char *retbuf;
311 int len __attribute__((unused));
312 const char *prompt;
313 {
314 filesav_t *fst;
315 int i, cmdok, echoed = 0;
316 char *cp, *bp, *ap;
317 char buf[1024], cmd[1024];
318
319 /* Initialization */
320 if (-1 == fsk_depth) {
321 current_input = stdin;
322 fsk_depth = 0;
323 fstack[0].type = FSTK_USER;
324 }
325 if (Interrupt || Pipe_Interrupt) {
326 int stop = 0;
327 while (fsk_depth > 0 && !stop) {
328 fst = &fstack[fsk_depth];
329 if (FSTK_REDIR == fst->type) {
330 output_file(fst->un.redir.oldout, fst->un.redir.ispipe);
331 if (fst->un.redir.ispipe && Pipe_Interrupt && !Interrupt)
332 stop = 1;
333 fsk_depth--;
334 } else if (FSTK_CMDPART == fst->type) {
335 free(fst->un.cmdpart.cmd);
336 fsk_depth--;
337 } else if (FSTK_EXEC == fst->type) {
338 (void) fclose(current_input);
339 current_input = fst->un.exec.oldin;
340 fsk_depth--;
341 } else {
342 fsk_depth--;
343 }
344 }
345 Interrupt = 0;
346 Pipe_Interrupt = 0;
347 }
348 do {
349 cmdok = 1;
350 fst = &fstack[fsk_depth];
351 /* pop redir */
352 if (FSTK_REDIR == fst->type) {
353 output_file(fst->un.redir.oldout, fst->un.redir.ispipe);
354 fsk_depth--;
355 cmdok = 0;
356 continue;
357 }
358 echoed = get_nextbit(cmd, sizeof(cmd), prompt, 0, 0);
359 fst = &fstack[fsk_depth];
360
361 /* Is it a comment? */
362 if ('#' == cmd[0]) {
363 cmdok = 0;
364 continue;
365 }
366 /* Is it a line marked for secondary input. If so, ignore */
367 if ('&' == cmd[0]) {
368 stuff_semis(cmd);
369 cmdok = 0;
370 continue;
371 }
372 /* Expand any aliases */
373 clear_alias_marks();
374 do {
375 cp = cmd;
376 bp = buf;
377 i = 0;
378 while (cp && *cp && !isspace(*cp) && (++i < (signed) sizeof(buf)))
379 *bp++ = *cp++;
380 *bp = '\0';
381
382 if ((ap = find_alias(buf))) {
383 if (!replace_alias(cmd, ap, cmd))
384 cmdok = 0;
385 echoed = 0;
386 }
387 } while (ap && cmdok);
388 if (!cmdok)
389 continue;
390
391 /* Any ";" commands to stuff into the stack? */
392 stuff_semis(cmd);
393
394 /* Any variables to replace? */
395 if (!change_vars(cmd, buf)) {
396 cmdok = 0;
397 continue;
398 }
399 /*
400 * If we are disabled, we only let 'if', 'else', 'elseif',
401 * and 'endif' through.
402 */
403 if (fst->disabled) {
404 cp = cmd;
405 while (isspace(*cp))
406 cp++;
407 if (!strncmp("if", cp, 2)) {
408 cp += 2;
409 if (*cp && !isspace(*cp))
410 cmdok = 0;
411 } else if (!strncmp("elseif", cp, 6)) {
412 cp += 6;
413 if (*cp && !isspace(*cp))
414 cmdok = 0;
415 } else if (!strncmp("else", cp, 4)) {
416 cp += 4;
417 if (*cp && !isspace(*cp))
418 cmdok = 0;
419 } else if (!strncmp("endif", cp, 5)) {
420 cp += 5;
421 if (*cp && !isspace(*cp))
422 cmdok = 0;
423 } else
424 cmdok = 0;
425 if (!cmdok)
426 continue;
427 }
428 /* Any file redirections or pipes? */
429 if (!(i = find_redirs(cmd, buf))) {
430 cmdok = 0;
431 continue;
432 } else if (i < 0) {
433 /*
434 * Redirected, so command was not echoed to redir
435 * file
436 */
437 echoed = 0;
438 }
439 } while (!cmdok);
440
441 unescape_backslashes(retbuf, cmd);
442 return (echoed);
443 }
444
445 /************************************
446 * get_input
447 *
448 * No history, vars, aliases.
449 */
450 int
get_input(retbuf,len,prompt)451 get_input(retbuf, len, prompt)
452 char *retbuf;
453 int len;
454 const char *prompt;
455 {
456 filesav_t *fst;
457 const char *wp;
458 char *cp;
459 FILE *fp;
460 int psav = 0, dontstrip = 0;
461
462 /* Tel's and ann's prompts look like " 512 left: " */
463 if (prompt) {
464 wp = prompt;
465 while (isspace(*wp))
466 wp++;
467 if (isdigit(*wp)) {
468 while (isdigit(*wp))
469 wp++;
470 while (isspace(*wp))
471 wp++;
472 if (!strncmp("left:", wp, 5))
473 dontstrip = 1;
474 }
475 }
476 /* Must save redirections so output makes it to a file. */
477 fp = (FILE *) 0;
478 fst = &fstack[fsk_depth];
479 if (fst->type == FSTK_REDIR) {
480 fp = fst->un.redir.oldout;
481 psav = fst->un.redir.ispipe;
482 fsk_depth--;
483 }
484 /* SPECIAL for empire telegrams and announcements. */
485
486 get_nextbit(retbuf, len, prompt, 1, dontstrip);
487
488 if (fp) {
489 fst = fst_add(FSTK_REDIR);
490 fst->un.redir.oldout = fp;
491 fst->un.redir.ispipe = psav;
492 }
493 /*
494 * To allow semicolons in ann & tele's, or not? This allows them.
495 */
496 if (!dontstrip)
497 stuff_semis(retbuf);
498
499 cp = retbuf;
500
501 if (!dontstrip) {
502 while (isspace(*cp))
503 cp++;
504 /* If marked as an secondary input, remove marker */
505 if ('&' == *cp) {
506 cp++;
507 while (isspace(*cp))
508 cp++;
509 }
510 }
511 if (cp != retbuf)
512 strcpy(retbuf, cp);
513
514 /* Remove trailing space */
515 cp = retbuf;
516 while (*cp)
517 cp++;
518 if (cp != retbuf) {
519 while (isspace(*--cp) && (cp != retbuf));
520 cp++;
521 *cp = '\0';
522
523 }
524 if (fsk_depth)
525 eprt("%s\n", retbuf);
526
527 if (Interrupt) {
528 return (0);
529 } else
530 return (1);
531 }
532
533 /************************************
534 * stuff_semis
535 *
536 * Scan cmd for ";" not inside of quotes, and
537 * stick them on the cmd stack.
538 */
539 static void
stuff_semis(cmd)540 stuff_semis(cmd)
541 char *cmd;
542 {
543 filesav_t *fst;
544 char *cp;
545
546 cp = cmd;
547 while (*cp) {
548 if (('\\' == *cp) &&
549 ((';' == cp[1]) || ('"' == cp[1]))) {
550 /* Skip next character */
551 cp++;
552 } else if ('\"' == *cp) {
553 /* Ignore things inside of quotes. */
554 cp ++;
555 while (*cp && '\"' != *cp)
556 cp++;
557 } else if (';' == *cp) {
558 *cp++ = '\0';
559 if (*cp) {
560 if ((fst = fst_add(FSTK_CMDPART))) {
561 fst->un.cmdpart.cmd = malloc(strlen(cp) + 1);
562 strcpy(fst->un.cmdpart.cmd, cp);
563 }
564 }
565 return;
566 }
567 cp++;
568 }
569 }
570
571 /************************************
572 * change_vars
573 *
574 * Find and substitute in the $VARs in
575 * the cmd line.
576 *
577 * Looking for:
578 * $varname
579 * ${varname}
580 *
581 * Return 0 if there is a problem.
582 * Return 1 if ok.
583 */
584 static int
change_vars(cmd,buf)585 change_vars(cmd, buf)
586 char *cmd, *buf;
587 {
588 int err;
589 char *bp, *cp, *dp;
590 const char *ap;
591
592 /* Look for variables to expand */
593 /* This should probably be in a procedure.... */
594 for (cp = cmd; *cp; cp++) {
595 if ('\\' == *cp) {
596 cp ++;
597 } else if ('"' == *cp) {
598 cp++;
599 while (*cp)
600 {
601 if ('\\' == *cp)
602 {
603 if (!*++cp)
604 break;
605 } else if ('"' == *cp)
606 break;
607
608 cp++;
609 }
610 if ('"' != *cp) {
611 eprt("Syntax: Missing trailing \" in '%s'\n", cmd);
612 return (0);
613 }
614 } else if ('$' == *cp) {
615 dp = cp + 1;
616 if (*dp && !isspace(*dp)) {
617 bp = buf;
618 if ('{' /* } */ == *dp) {
619 dp++;
620 while (*dp && /* { */ '}' != *dp)
621 *bp++ = *dp++;
622 *bp = '\0';
623 if (!*dp) {
624 eprt("Syntax: Bad variable syntax, '%s'\n", cp);
625 return (0);
626 }
627 dp++;
628 } else {
629 while (*dp && !isspace(*dp) && ':' != *dp)
630 *bp++ = *dp++;
631 *bp = '\0';
632 }
633 if (!buf[0]) {
634 *cp = '\0';
635 strcpy(buf, cmd);
636 strcat(buf, "$");
637 cp = cmd + strlen(buf) - 1;
638 strcat(buf, dp);
639 strcpy(cmd, buf);
640 } else if ((ap = find_var(buf, &err))) {
641 *cp = '\0';
642 strcpy(buf, cmd);
643 strcat(buf, ap);
644 cp = cmd + strlen(buf) - 1;
645 strcat(buf, dp);
646 strcpy(cmd, buf);
647 } else {
648 if (err)
649 eprt("Error: Unable to find variable '%s'\n", buf);
650 return (0);
651 }
652 }
653 }
654 }
655 return (1);
656 }
657
658 /************************************
659 * find_redirs
660 *
661 * Look for "|", ">", etc, in the command line.
662 *
663 * Let us be sleazy.
664 * If we find a "|", pipe it into a "sh -c" for the
665 * rest of it.
666 *
667 * Return 0 if there was a problem.
668 * Return 1 if ok.
669 * Return -1 if redirected.
670 */
671 static int
find_redirs(cmd,buf)672 find_redirs(cmd, buf)
673 char *cmd, *buf __attribute__((unused));
674 {
675 filesav_t *fst;
676 char *cp, *bp, *np;
677 FILE *fp;
678 int xs = 0;
679
680 cp = cmd;
681 while (*cp && ('|' != *cp) && ('>' != *cp)) {
682 /* Ignore quoted strings. */
683 if ('\"' == *cp) {
684 cp++;
685 while (*cp && '\"' != *cp)
686 cp++;
687 if (!*cp)
688 break;
689 }
690 /* Empire has commands which contain '>'. */
691 /* However, they all follow '?' */
692 /* Thus, we skip over strings following '?'s */
693 if ('?' == *cp) {
694 while (*cp && !isspace(*cp))
695 cp++;
696 if (!*cp)
697 break;
698 }
699 cp++;
700 }
701 if ('|' == *cp) {
702 *cp++ = '\0';
703 while (isspace(*cp))
704 cp++;
705 if (!*cp) {
706 eprt("Syntax: Bad pipe. Need a destination program.\n");
707 return (0);
708 }
709 if (!(fst = fst_add(FSTK_REDIR)))
710 return (0);
711
712 /* Expand any ~'s in destination name */
713 np = tilde_expand(cp);
714
715 /* Set up the output pipe. */
716 fp = output_topipe(np, &fst->un.redir.ispipe);
717
718 /* tilde_expand returns an alloc'd string */
719 free(np);
720
721 fst->un.redir.oldout = fp;
722 if (!fp) {
723 output_file(fp, fst->un.redir.ispipe);
724 fsk_depth--;
725 eprt("Error: Unable to popen pipe. %d:%s\n", errno, cp);
726 return (0);
727 }
728 return (-1);
729 } else if ('>' == *cp) {
730 *cp++ = '\0';
731 if ('>' == *cp) {
732 xs = 1;
733 cp++;
734 } else if ('!' == *cp) {
735 xs = -1;
736 cp++;
737 }
738 while (isspace(*cp))
739 cp++;
740 if (!*cp) {
741 eprt("Syntax: Bad file redirection. Need a destination file.\n");
742 return (0);
743 }
744 bp = cp;
745 while (*bp && !isspace(*bp) && ('>' != *bp) && ('!' != *bp))
746 bp++;
747 if (('>' == *bp) || ('!' == *bp)) {
748 eprt("Syntax: Bad file redirection. Only one redirection allowed.\n");
749 return (0);
750 }
751 if (*bp)
752 *bp++ = '\0';
753 while (isspace(*bp))
754 bp++;
755 if (*bp) {
756 eprt("Syntax: Bad file redirection. Redirection must be last.\n");
757 return (0);
758 }
759 if (!(fst = fst_add(FSTK_REDIR)))
760 return (0);
761
762 /* Expand any ~'s in destination name */
763 np = tilde_expand(cp);
764
765 /* Set up the output file. */
766 fp = output_to(np, xs, &fst->un.redir.ispipe);
767
768 /* tilde_expand returns an alloc'd string */
769 free(np);
770
771 fst->un.redir.oldout = fp;
772 if (!fp) {
773 fsk_depth--;
774 eprt("Error: Unable to redirect to file '%s', %d.\n", cp, errno);
775 return (0);
776 } else if ((FILE *) - 1 == fp) {
777 fsk_depth--;
778 eprt("Error: File %s exists, redirection failed.\n", cp);
779 return (0);
780 }
781 return (-1);
782 }
783 return (1);
784 }
785
786 /******************************************
787 * cmd_exec
788 *
789 * Read commands from a file.
790 *
791 * sub = 0, read the file in.
792 * sub = 1, execute file, and read in output from it.
793 * sub = 2, read the file in, but don't echo the output.
794 */
795 void
cmd_exec(buf,sub)796 cmd_exec(buf, sub)
797 const char *buf;
798 int sub;
799 {
800 int ispipe = 0;
801 filesav_t *fst;
802 char *cp, *nam = NULL;
803 FILE *fp;
804
805 #ifndef READLINE_HANDLES_CONST
806 union {
807 const char *cchp;
808 char *chp;
809 } const_temp;
810 #endif
811
812 /* Initialization */
813 if (-1 == fsk_depth) {
814 current_input = stdin;
815 fsk_depth = 0;
816 fstack[0].type = FSTK_USER;
817 }
818 while (isspace(*buf))
819 buf++;
820
821 if (!*buf) {
822 eprt("Syntax: Need a filename to execute.\n");
823 return;
824 }
825 /* Expand any ~'s in filename */
826 /* This is in the GNU readline stuff. */
827 #ifdef READLINE_HANDLES_CONST
828 nam = tilde_expand(buf);
829 #else /* READLINE_HANDLES_CONST */
830 const_temp.cchp = buf;
831
832 nam = tilde_expand(const_temp.chp);
833 #endif /* READLINE_HANDLES_CONST */
834
835 if ((0 == sub) || (2 == sub)) {
836 cp = nam;
837 while (*cp && !isspace(*cp))
838 cp++;
839 if (*cp)
840 *cp++ = '\0';
841 while (isspace(*cp))
842 cp++;
843 if (*cp) {
844 eprt("Syntax: Extraneous characters after filename, `%s'\n", cp);
845 goto error_ret;
846 }
847 if (!(fp = fopen(nam, "r"))) {
848 eprt("Error: Unable to exec file %s, %d\n", nam, errno);
849 goto error_ret;
850 }
851 /* Disable output by stacking another filetype. */
852 if (2 == sub) {
853 if (!(fst = fst_add(FSTK_REDIR)))
854 goto error_ret;
855 fst->un.redir.oldout = output_turnoff(&fst->un.redir.ispipe);
856 }
857 } else if (1 == sub) {
858 if (!(fp = popen(nam, "r"))) {
859 eprt("Unable to execute command '%s', %d\n", nam, errno);
860 goto error_ret;
861 }
862 ispipe = 1;
863 } else {
864 eprt("Internal Error, bad sub to cmd_exec\n");
865 goto error_ret;
866 }
867
868 if (!(fst = fst_add(FSTK_EXEC))) {
869 (void) fclose(fp);
870 goto error_ret;
871 }
872 fst->un.exec.oldin = current_input;
873 fst->un.exec.ispipe = current_input_isapipe;
874 current_input = fp;
875 current_input_isapipe = ispipe;
876
877 error_ret:
878 if (nam)
879 free(nam);
880 }
881
882 /******************************************
883 * replace_alias
884 *
885 * Take a command and an alias, and make up the
886 * new command.
887 */
888 static int
replace_alias(cmd,alias,ret)889 replace_alias(cmd, alias, ret)
890 char *cmd, *alias, *ret;
891 {
892 int arg;
893 char *ap, *bp, *cp, *maxcp;
894 const char *sp;
895 char buf[1024];
896
897 /* Find the end of the alias parameter. */
898 maxcp = cmd;
899 while (isspace(*maxcp))
900 maxcp++;
901 while (*maxcp && !isspace(*maxcp))
902 maxcp++;
903
904 starwalk = 1;
905
906 for (ap = alias, bp = buf; *ap; ap++) {
907 if ('\\' == *ap)
908 {
909 *bp++ = *ap++;
910 if (!*ap)
911 break;
912
913 *bp++ = *ap;
914 } else if ('"' == *ap) {
915 /* Ignore quoted strings. */
916 *bp++ = *ap++;
917 while (*ap)
918 {
919 if ('\\' == *ap)
920 {
921 *bp++ = *ap++;
922 if (!*ap)
923 break;
924 } else if ('"' == *ap)
925 break;
926 *bp++ = *ap++;
927 }
928 while (*ap && '"' != *ap)
929 *bp++ = *ap++;
930 if ('"' != *ap) {
931 eprt("Syntax: Alias missing trailing \" in '%s'\n", alias);
932 return (0);
933 }
934 *bp++ = *ap;
935 } else if ('$' == *ap) {
936 cp = ap;
937 cp++;
938 if ('{' /* '} */ == *cp) {
939 if (!isdigit(*++cp)) {
940 *bp++ = *ap;
941 continue;
942 }
943 /* It is a ${N type variable. } */
944 arg = atoi(cp);
945 while (isdigit(*cp))
946 cp++;
947 sp = NULL;
948 if (':' == *cp) {
949 sp = ++cp;
950 while (*cp && /* { */ '}' != *cp)
951 cp++;
952 }
953 if (/* { */ '}' != *cp) {
954 eprt( /* { */ "Syntax: Alias missing trailing '}' in '%s'\n",
955 alias);
956 return (0);
957 }
958 ap = cp;
959 if (find_aliasarg(arg, cmd, &bp, sp, &maxcp, alias))
960 return (0);
961 } else if (isdigit(*cp)) {
962 /* It is a numeric variable name */
963 arg = atoi(cp);
964 if (find_aliasarg(arg, cmd, &bp, (char *)0, &maxcp, alias))
965 return (0);
966 while (isdigit(*cp))
967 ap = cp++;
968 } else if ('*' == *cp) {
969 /* All args except 0 */
970 arg = starwalk;
971 sp = "*";
972 if (!find_aliasarg(arg, cmd, &bp, sp, &maxcp, alias)) {
973 starwalk++;
974 arg++;
975 ap -= 2;
976 } else {
977 if (starwalk > 1)
978 *(--bp) = 0;
979 starwalk = 1;
980 ap++;
981 }
982 } else {
983 *bp++ = '$';
984 /* Special case for $$ */
985 if ('$' == *cp)
986 *bp++ = *ap++;
987 }
988 } else {
989 *bp++ = *ap;
990 }
991 }
992 *bp = '\0';
993 strcpy(bp, maxcp);
994 strcpy(ret, buf);
995 return (1);
996 }
997
998 /******************************************
999 * find_aliasarg
1000 *
1001 * Find the alias argument, and add it to the
1002 * new command being built.
1003 *
1004 * Arguments are quoted strings or space delimited words,
1005 * which can contain any characters.
1006 */
1007 static int
find_aliasarg(arg,cmd,retp,condp,maxcp,alias)1008 find_aliasarg(arg, cmd, retp, condp, maxcp, alias)
1009 int arg;
1010 char *cmd;
1011 char **retp;
1012 const char *condp;
1013 char **maxcp;
1014 char *alias;
1015 {
1016 char *cp;
1017 int i;
1018
1019 cp = cmd;
1020 i = arg;
1021 /* While characters, and not running into the next command. */
1022 while (*cp && (';' != *cp) && (i >= 0)) {
1023 while (isspace(*cp))
1024 cp++;
1025 /* Quoted string */
1026 if ('"' == *cp) {
1027 cp++;
1028 while (*cp && '"' != *cp) {
1029 if (i) {
1030 cp++;
1031 } else
1032 *(*retp)++ = *cp++;
1033 }
1034 if ('"' != *cp++) {
1035 eprt("Syntax: Arg %d missing trailing \" in '%s'\n", i, cmd);
1036 return (-1);
1037 }
1038 i--;
1039 } else if (*cp && ';' != *cp) {
1040 while (*cp && ';' != *cp && !isspace(*cp)) {
1041 if (i) {
1042 cp++;
1043 } else
1044 *(*retp)++ = *cp++;
1045 }
1046 i--;
1047 }
1048 }
1049 if (cp > *maxcp)
1050 *maxcp = cp;
1051
1052 /* We didn't find the argument. */
1053 if (i >= 0) {
1054 if (condp) {
1055 if ('-' == *condp) {
1056 /* Use the following string */
1057 condp++;
1058 while (*condp && /* { */ '}' != *condp)
1059 *(*retp)++ = *condp++;
1060 if ( /* { */ '}' != *condp) {
1061 eprt( /* { */
1062 "Syntax: Conditional variable missing '}' in '%s'\n",
1063 alias);
1064 return (-1);
1065 }
1066 } else if ('?' == *condp) {
1067 /* Print an error, and abort command. */
1068 char *xp;
1069 char buf[1024];
1070
1071 xp = buf;
1072 condp++;
1073 while (*condp && /* { */ '}' != *condp)
1074 *xp++ = *condp++;
1075 if ( /* { */ '}' != *condp)
1076 eprt( /* { */
1077 "Syntax: Conditional variable missing '}' in '%s'\n",
1078 alias);
1079 *xp = '\0';
1080 eprt("%s\n", buf);
1081 return (-1);
1082 } else if ('+' == *condp) {
1083 /* Substitute nothing. */
1084 while (*condp && /* { */ '}' != *condp)
1085 condp++;
1086 if ( /* { */ '}' != *condp) {
1087 eprt( /* { */
1088 "Syntax: Conditional variable missing '}' in '%s'\n",
1089 alias);
1090 return (-1);
1091 }
1092 } else if (*condp == '*') {
1093 return (-1);
1094 } else {
1095 eprt("Syntax: Bad conditional variable syntax in alias '%s`\n",
1096 alias);
1097 return (-1);
1098 }
1099 } else {
1100 eprt("Missing alias parameter %d\n", arg);
1101 return (-1);
1102 }
1103 }
1104 return (0);
1105 }
1106
1107 /******************************************
1108 * cmd_if
1109 *
1110 * sub:
1111 * 0 - if
1112 * 1 - elseif
1113 * 2 - else
1114 * 3 - endif
1115 */
1116 void
cmd_if(buf,sub)1117 cmd_if(buf, sub)
1118 const char *buf;
1119 int sub;
1120 {
1121 filesav_t *fst;
1122 int ret;
1123 char cmd[200];
1124
1125 fst = &fstack[fsk_depth];
1126 if ((sub == 0) || (sub == 1)) {
1127 /* IF or ELSEIF */
1128 if (sub == 1) {
1129 /* ELSEIF */
1130 if (fst->depth <= 0) {
1131 eprt("elseif not inside an if list.\n");
1132 return;
1133 }
1134 if (fst->disabled) {
1135 /*
1136 * Lower level, or previous elseif disabled
1137 * us
1138 */
1139 if (fst->depth > fst->disabled)
1140 return;
1141 /* This level disabled us, re-enable */
1142 fst->disabled = 0;
1143 } else {
1144 /* Disabled until endif */
1145 fst->disabled = fst->depth - 1;
1146 prt("Commands disabled til matching endif.\n");
1147 }
1148 } else
1149 fst->depth += 2;
1150
1151 if (!fst->disabled) {
1152 while (isspace(*buf))
1153 buf++;
1154
1155 sprintf(cmd, "/bin/test %s", buf);
1156 ret = system(cmd);
1157 if ((ret % 256) != 0) {
1158 eprt("Problem in system call of /bin/test, %d", (ret % 256));
1159 eprt("Command was: %s", cmd);
1160 ret = -1;
1161 } else {
1162 ret = (ret / 256) % 256;
1163 if (ret == 255) {
1164 eprt("Bad test conditons.\n");
1165 ret = -1;
1166 }
1167 }
1168 if (ret) {
1169 if (ret == -1) {
1170 if ((fsk_depth == 0) && (fst->depth == 2)) {
1171 /*
1172 * Interactive command,
1173 * ignore error.
1174 */
1175 fst->depth = 0;
1176 fst->disabled = 0;
1177 } else {
1178 /*
1179 * Abort until this if is
1180 * done.
1181 */
1182 fst->disabled = fst->depth - 1;
1183 prt("Commands disabled til matching endif.\n");
1184 }
1185 } else {
1186 fst->disabled = fst->depth;
1187 prt("Commands disabled.\n");
1188 }
1189 } else if (sub == 1)
1190 prt("Commands enabled.\n");
1191 }
1192 } else if (sub == 2) {
1193 if (fst->depth <= 0) {
1194 eprt("else not inside an if list.\n");
1195 return;
1196 }
1197 /* ELSE */
1198 if (fst->disabled) {
1199 if (fst->depth <= fst->disabled) {
1200 fst->disabled = 0;
1201 prt("Commands enabled.\n");
1202 }
1203 } else if (fst->depth > 0) {
1204 prt("Commands disabled til matching endif.\n");
1205 fst->disabled = fst->depth - 1;
1206 }
1207 } else if (sub == 3) {
1208 /* ENDIF */
1209 if (fst->depth <= 0) {
1210 fst->depth = 0;
1211 if (!fst->disabled) /* So I'm paranoid */
1212 eprt("endif not inside an if list.\n");
1213 } else
1214 fst->depth -= 2;
1215 if (fst->disabled && (fst->depth <= fst->disabled)) {
1216 fst->disabled = 0;
1217 prt("Commands enabled.\n");
1218 }
1219 if (fst->depth <= 0) {
1220 fst->depth = 0;
1221 fst->disabled = 0;
1222 }
1223 }
1224 }
1225
1226 /* vim:ts=8:ai:sw=8:syntax=c
1227 */
1228