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