1 /* @(#)cond.c	1.29 18/05/03 Copyright 1985-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)cond.c	1.29 18/05/03 Copyright 1985-2018 J. Schilling";
6 #endif
7 /*
8  *	Bsh conditional code handling
9  *
10  *		if .. then .. else .. fi
11  *		for .. in .. end
12  *		loop .. end
13  *		switch .. case .. end
14  *		read
15  *
16  *	Copyright (c) 1985-2018 J. Schilling
17  */
18 /*
19  * The contents of this file are subject to the terms of the
20  * Common Development and Distribution License, Version 1.0 only
21  * (the "License").  You may not use this file except in compliance
22  * with the License.
23  *
24  * See the file CDDL.Schily.txt in this distribution for details.
25  * A copy of the CDDL is also available via the Internet at
26  * http://www.opensource.org/licenses/cddl1.txt
27  *
28  * When distributing Covered Code, include this CDDL HEADER in each
29  * file and include the License file CDDL.Schily.txt from this distribution.
30  */
31 
32 #include <schily/stdio.h>
33 #include <schily/varargs.h>
34 #include "bsh.h"
35 #include "node.h"
36 #include "str.h"
37 #include "strsubs.h"
38 #include <schily/string.h>
39 #include <schily/stdlib.h>
40 #include <schily/unistd.h>
41 #include <schily/utypes.h>
42 #define	REDEFINE_CTYPE		/* Allow to use our local ctype.h */
43 #include "ctype.h"
44 #include <schily/patmatch.h>
45 
46 
47 #define	EXEC		1
48 #define	SKIP		2
49 #define	ABORTED		-2
50 
51 #define	STOPFI		1
52 #define	STOPEND		2
53 #define	QUANT		10
54 
55 typedef struct m_stack {
56 	char		*s_lp;
57 	int		s_level;
58 	struct m_stack	*s_next;
59 } _MSTK, *MSTK;
60 
61 #ifdef	DEBUG
62 extern	int	ttyflg;
63 #endif
64 extern	int	delim;
65 
66 EXPORT	void	push		__PR((int id));
67 EXPORT	void	freestack	__PR((void));
68 LOCAL	int	pop		__PR((void));
69 LOCAL	void	dec_level	__PR((void));
70 EXPORT	char	*cons_args	__PR((Argvec * vp, int n));
71 LOCAL	char	*growline	__PR((char *s1, char *s2));
72 LOCAL	char	*gnextline	__PR((void));
73 LOCAL	int	readloop	__PR((Argvec * vp, int stopc));
74 LOCAL	void	freeup		__PR((void));
75 LOCAL	int	parseuntil	__PR((int task, int ilev, FILE ** std, char **vpp));
76 LOCAL	BOOL	makevec		__PR((char **vec, va_list args));
77 LOCAL	int	skipuntil	__PR((int ilev, ...));
78 LOCAL	int	execuntil	__PR((FILE ** std, int ilev, ...));
79 LOCAL	void	not_found	__PR((FILE * fp, char *name));
80 EXPORT	void	bif		__PR((Argvec * vp, FILE ** std, int flag));
81 EXPORT	void	bfor		__PR((Argvec * vp, FILE ** std, int flag));
82 EXPORT	void	bloop		__PR((Argvec * vp, FILE ** std, int flag));
83 EXPORT	void	bread		__PR((Argvec * vp, FILE ** std, int flag));
84 EXPORT	void	bswitch		__PR((Argvec * vp, FILE ** std, int flag));
85 
86 int		level = 0,			/* Achtung !!!!		   */
87 		idsp = 0,			/* alle statischen Variablen */
88 		idlen = 0,			/* aus cond.c mueszen in   */
89 		read_delim = 0,			/* call_cmd (in call.c)	   */
90 		*idstack = (int *) NULL;	/* gerettet werden.	   */
91 MSTK		firstp = (MSTK) NULL,		/* Wichtig, falls neue	   */
92 		stackp = (MSTK) NULL,		/* Variablen dazu kommen.  */
93 		foundline = (MSTK) NULL;
94 
95 #ifdef	OOO
96 EXPORT void
push(id)97 push(id)
98 	int	id;
99 {
100 	int	*np;
101 	size_t	newidlen;
102 
103 	if (idlen == idsp) {	/* realloc !!! */
104 		newidlen = idlen + QUANT;
105 		np = (int *)malloc(newidlen * sizeof (int));
106 		if (idstack) {
107 			movebytes((char *) idstack, (char *) np, idlen * sizeof (int));
108 			free((char *) idstack);
109 		}
110 		idlen = newidlen;
111 		idstack = np;
112 	}
113 	idstack[idsp++] = id;
114 }
115 #else
116 EXPORT void
push(id)117 push(id)
118 	int	id;
119 {
120 	if (idlen == idsp) {
121 		idlen += QUANT;
122 		if (idstack)
123 			idstack = (int *)realloc(idstack, idlen * sizeof (int));
124 		else
125 			idstack = (int *)malloc(idlen * sizeof (int));
126 	}
127 	idstack[idsp++] = id;
128 }
129 #endif
130 
131 EXPORT void
freestack()132 freestack()
133 {
134 	free((char *) idstack);
135 	idstack = (int *) 0;
136 	idlen = 0;
137 	idsp = 0;
138 }
139 
140 LOCAL int
pop()141 pop()
142 {
143 	int	id;
144 
145 	id = idstack[--idsp];
146 	if (idsp == 0)
147 		freestack();
148 	return (id);
149 }
150 
151 LOCAL void
dec_level()152 dec_level()
153 {
154 	level--;
155 	if (!level)
156 		freeup();
157 }
158 
159 EXPORT char *
cons_args(vp,n)160 cons_args(vp, n)
161 	Argvec	*vp;
162 	int	n;
163 {
164 		char	*cmdln;
165 	register char	*end;
166 	register char	**av = vp->av_av;
167 	register int	ac = vp->av_ac;
168 	register int	i;
169 	register size_t	len = 0;
170 
171 	if (n >= ac)
172 		return (makestr(nullstr));
173 	for (i = n; i < ac; i++)
174 		len += strlen(av[i]);
175 	len += (ac - n);
176 	cmdln = end = malloc(len);
177 	if (!cmdln)
178 		return (NULL);
179 #ifdef DEBUG
180 	printf("        cons_args: len = %d\n", len);
181 #endif
182 	for (i = n; i < ac - 1; i++)
183 		end = strcatl(end, av[i], " ", (char *)NULL);
184 	strcatl(end, av[i], (char *)NULL);
185 #ifdef DEBUG
186 	printf("        cons_args = '%s'\n", cmdln);
187 #endif
188 	return (cmdln);
189 }
190 
191 LOCAL char *
growline(s1,s2)192 growline(s1, s2)
193 	char	*s1;
194 	char	*s2;
195 {
196 	char	*s;
197 
198 	s = concat(s1, nl, s2, (char *)NULL);
199 	free(s1);
200 	free(s2);
201 	return (s);
202 }
203 
204 LOCAL char *
gnextline()205 gnextline()
206 {
207 	register	char	*lp = NULL;
208 
209 	do {
210 		if (lp)
211 			lp = growline(lp, nextline());
212 		else
213 			lp = nextline();
214 	} while (lp && *lp && lp[strlen(lp)-1] == '\\');
215 	return (lp);
216 }
217 
218 LOCAL int
readloop(vp,stopc)219 readloop(vp, stopc)
220 	Argvec	*vp;
221 	int	stopc;
222 {
223 	register int	ilev = 0;
224 	register MSTK	sp;
225 	register MSTK	lp = (MSTK) NULL;
226 		char	*linep = NULL;
227 	register Uchar	*p;
228 	register char	*cp;
229 
230 #ifdef DEBUG
231 	printf("        readloop: ttyflg = %s\n", ttyflg?"TRUE":"FALSE");
232 #endif
233 	if (firstp)
234 		return (TRUE);
235 	push(stopc);
236 	for (;;) {
237 		if (stackp) {
238 			quote();
239 			linep = gnextline();
240 			unquote();
241 #ifdef	DEBUG
242 			printf("        readloop: linep = '%s'\n", linep);
243 #endif
244 			if (delim == EOF) {
245 				berror("EOF unexpected.");
246 				ex_status = 1;
247 				return (FALSE);
248 			}
249 		} else {
250 			linep = cons_args(vp, 0);
251 		}
252 		for (p = (Uchar *)linep; iswhite(*p); p++);
253 		if (wordeql((char *)p, "if")) {
254 			push(STOPFI);
255 			ilev++;
256 		} else if (wordeql((char *)p, "for") || wordeql((char *)p, "loop") ||
257 			    wordeql((char *)p, "switch")) {
258 			push(STOPEND);
259 			ilev++;
260 		}
261 		sp = (MSTK)malloc(sizeof (_MSTK));
262 		if (lp)
263 			lp->s_next = sp;
264 		else
265 			firstp = stackp = sp;
266 		cp = makestr((char *)p);
267 		sp->s_lp = cp;
268 		sp->s_level = ilev;
269 		sp->s_next = (MSTK) NULL;
270 #ifdef DEBUG
271 		printf("        stacked line: '%s', internal level %d, at %x\n", cp, ilev, cp);
272 #endif
273 		if (linep)
274 			free(linep);
275 		if (wordeql(cp, "fi") || wordeql(cp, "end")) {
276 			stopc = pop();
277 			if (wordeql(cp, "fi")) {
278 				if (stopc == STOPEND) {
279 					freestack();
280 					freeup();
281 					berror("'end' expected.");
282 					ex_status = 1;
283 					return (FALSE);
284 				}
285 			} else {
286 				if (stopc == STOPFI) {
287 					freestack();
288 					freeup();
289 					berror("'fi' expected.");
290 					ex_status = 1;
291 					return (FALSE);
292 				}
293 			}
294 			ilev--;
295 		}
296 		if (ilev == 0)
297 			return (TRUE);
298 		lp = sp;
299 	}
300 }
301 
302 LOCAL void
freeup()303 freeup()
304 {
305 	register MSTK	np;
306 	register MSTK	cp = firstp;
307 
308 #ifdef DEBUG
309 	printf("        freeup:\n");
310 #endif
311 	while (cp) {
312 		np = cp->s_next;
313 #ifdef DEBUG
314 		printf("        freeup: '%s' at %x, ",	cp->s_lp, cp->s_lp);
315 #endif
316 		free(cp->s_lp);
317 #ifdef DEBUG
318 		printf("struct at %x\n", cp);
319 #endif
320 		free((char *) cp);
321 		cp = np;
322 	}
323 	stackp = firstp = (MSTK) NULL;
324 }
325 
326 LOCAL int
parseuntil(task,ilev,std,vpp)327 parseuntil(task, ilev, std, vpp)
328 	int		task;
329 	register int	ilev;
330 	FILE		*std[];
331 	register char	**vpp;
332 {
333 	register int	i;
334 	register MSTK	sp = stackp;
335 
336 #ifdef DEBUG
337 	for (i = 0; vpp[i]; i++)
338 		printf("'%s' ", vpp[i]);
339 	printf("at level %d\n", ilev);
340 #endif
341 	for (;;) {
342 		if (!sp)
343 			return (-1);
344 #ifdef DEBUG
345 		if (ilev != sp->s_level)
346 			printf("        %s line '%s' (level=%d).\n",
347 				(task == SKIP) ? "skipping" : "executing",
348 				sp->s_lp, sp->s_level);
349 #endif
350 		if (ilev == sp->s_level) {
351 			for (i = 0; vpp[i]; i++) {
352 #ifdef DEBUG
353 				printf("        comparing line '%s' at level %d with '%s'\n", sp->s_lp, ilev, vpp[i]);
354 #endif
355 				if (streqln(vpp[i], sp->s_lp, strlen(vpp[i])))
356 					break;
357 			}
358 			if (vpp[i]) {
359 #ifdef DEBUG
360 				printf("        found at #%d.\n", i);
361 #endif
362 				foundline = sp;
363 				stackp = sp->s_next;
364 				return (i);
365 			}
366 		}
367 		if (task == EXEC) {
368 			stackp = sp;
369 			if (wordeql(sp->s_lp, "break") || ctlc || read_delim == EOF)
370 				return (ABORTED);
371 			pushline(sp->s_lp);
372 			freetree(cmdline(0, std, FALSE));
373 			if (stackp == sp)
374 				sp = sp->s_next;
375 			else
376 				sp = stackp;
377 		} else if (task == SKIP) {
378 			sp = sp->s_next;
379 		}
380 	}
381 }
382 
383 #define	VEC_SIZE	4
384 LOCAL BOOL
makevec(vec,args)385 makevec(vec, args)
386 	char	**vec;
387 	va_list	args;
388 {
389 	char	*p;
390 	int	n = 0;
391 
392 	do {
393 		if (n == VEC_SIZE) {
394 			berror("Implementation error (VEC_SIZE too small).");
395 			return (FALSE);
396 		}
397 		p = va_arg(args, char *);
398 		vec[n++] = p;
399 
400 	} while (p != NULL);
401 	return (TRUE);
402 }
403 
404 /* VARARGS1 */
405 
406 #ifdef	PROTOTYPES
407 LOCAL int
skipuntil(int ilev,...)408 skipuntil(int ilev, ...)
409 #else
410 LOCAL int
411 skipuntil(ilev, va_alist)
412 	int	ilev;
413 	va_dcl
414 #endif
415 {
416 	va_list	args;
417 	char	*vec[VEC_SIZE];
418 	int	ret;
419 
420 #ifdef DEBUG
421 	printf("        skipuntil: ");
422 #endif
423 #ifdef	PROTOTYPES
424 	va_start(args, ilev);
425 #else
426 	va_start(args);
427 #endif
428 	if (!makevec(vec, args)) {
429 		va_end(args);
430 		return (ABORTED);
431 	}
432 	va_end(args);
433 	ret = parseuntil(SKIP, ilev, gstd, vec);
434 	return (ret);
435 }
436 
437 /* VARARGS2 */
438 
439 #ifdef	PROTOTYPES
440 LOCAL int
execuntil(FILE * std[],int ilev,...)441 execuntil(FILE *std[], int ilev, ...)
442 #else
443 LOCAL int
444 execuntil(std, ilev, va_alist)
445 	FILE	*std[];
446 	int	ilev;
447 	va_dcl
448 #endif
449 {
450 	va_list	args;
451 	char	*vec[VEC_SIZE];
452 	int	ret;
453 
454 #ifdef DEBUG
455 	printf("        execuntil: ");
456 #endif
457 #ifdef	PROTOTYPES
458 	va_start(args, ilev);
459 #else
460 	va_start(args);
461 #endif
462 	if (!makevec(vec, args)) {
463 		va_end(args);
464 		return (ABORTED);
465 	}
466 	va_end(args);
467 	ret = parseuntil(EXEC, ilev, std, vec);
468 	return (ret);
469 }
470 
471 LOCAL void
not_found(fp,name)472 not_found(fp, name)
473 	FILE	*fp;
474 	char	*name;
475 {
476 	fprintf(fp, "'%s' not found.\n", name);
477 	ex_status = 1;
478 }
479 
480 /* if - command */
481 /* ARGSUSED */
482 EXPORT void
bif(vp,std,flag)483 bif(vp, std, flag)
484 	Argvec	*vp;
485 	FILE	*std[];
486 	int	flag;
487 {
488 		char	*cmdl;
489 	register int	ilev;
490 
491 #ifdef	DEBUG
492 	int	i;
493 #endif
494 
495 	level++;
496 #ifdef DEBUG
497 	printf("        bif: ");
498 	for (i = 0; i < vp->av_ac; i++)
499 		printf("%s ", vp->av_av[i]);
500 	printf(", files %d %d %d,\n", std[0], std[1], std[2]);
501 #endif
502 	if (vp->av_ac < 2) {
503 		wrong_args(vp, std);
504 		dec_level();
505 		return;
506 	}
507 	if (!readloop(vp, STOPFI))
508 		return;
509 	ilev = stackp->s_level;
510 	if (streql(vp->av_av[1], "(")) {
511 		if (!test(vp, std)) {
512 			dec_level();
513 			return;
514 		}
515 	} else {
516 		cmdl = cons_args(vp, 1);
517 #ifdef DEBUG
518 		printf("        bif: cmdl '%s'.\n", cmdl);
519 #endif
520 		pushline(cmdl);
521 		freetree(cmdline(0, std, FALSE));
522 		free(cmdl);
523 	}
524 #ifdef DEBUG
525 	printf("        bif: retval = %d\n", ex_status);
526 #endif
527 	if (ex_status) {
528 		if (skipuntil(ilev, "else", "fi", (char *)NULL) == 0) {
529 			if (execuntil(std, ilev, "fi", (char *)NULL))
530 				not_found(std[2], "fi");
531 		}
532 	} else {
533 		if (skipuntil(ilev, "then", "fi", (char *)NULL) != 0) {
534 			not_found(std[2], "then");
535 		} else {
536 			if (execuntil(std, ilev, "else", "fi", (char *)NULL) == 0) {
537 				if (skipuntil(ilev, "fi", (char *)NULL))
538 					not_found(std[2], "fi");
539 			}
540 		}
541 	}
542 	dec_level();
543 }
544 
545 /* ARGSUSED */
546 EXPORT void
bfor(vp,std,flag)547 bfor(vp, std, flag)
548 	Argvec	*vp;
549 	FILE	*std[];
550 	int	flag;
551 {
552 	register int	i;
553 	register int	brktype;
554 		int	ilev;
555 		char	*name;
556 		MSTK	sp;
557 
558 	level++;
559 	if (vp->av_ac < 3) {
560 		wrong_args(vp, std);
561 		dec_level();
562 		return;
563 	}
564 	name = vp->av_av[1];
565 	if (!streql(vp->av_av[2], "in")) {
566 		not_found(std[2], "in");
567 		ex_status = 1;
568 		dec_level();
569 		return;
570 	}
571 #ifdef DEBUG
572 	printf("        bfor: for %s in ", name);
573 	for (i = 3; i < vp->av_ac; i++)
574 		printf("%s ", vp->av_av[i]);
575 	printf(nl);
576 #endif
577 	if (!readloop(vp, STOPEND))
578 		return;
579 	ilev = stackp->s_level;
580 	sp = stackp = stackp->s_next;
581 	for (i = 3, brktype = 0; (i < vp->av_ac) && (brktype != ABORTED); i++) {
582 		ev_insert(concat(name, eql, vp->av_av[i], (char *)NULL));
583 #ifdef DEBUG
584 		printf("        executing loop with '%s'\n", vp->av_av[i]);
585 #endif
586 		brktype = execuntil(std, ilev, "end", (char *)NULL);
587 		stackp = sp;
588 	}
589 	if (skipuntil(ilev, "end", (char *)NULL))
590 		not_found(std[2], "end");
591 	dec_level();
592 }
593 
594 /* ARGSUSED */
595 EXPORT void
bloop(vp,std,flag)596 bloop(vp, std, flag)
597 		Argvec	*vp;
598 	register FILE	*std[];
599 		int	flag;
600 {
601 	register int	ilev;
602 	register int	brktype;
603 	register MSTK	sp;
604 
605 	level++;
606 	if (!readloop(vp, STOPEND))
607 		return;
608 	ilev = stackp->s_level;
609 	sp = stackp = stackp->s_next;
610 	do {
611 		brktype = execuntil(std, ilev, "end", (char *)NULL);
612 		stackp = sp;
613 	} while (brktype != ABORTED);
614 	if (skipuntil(ilev, "end", (char *)NULL))
615 		not_found(std[2], "end");
616 	dec_level();
617 }
618 
619 /* ARGSUSED */
620 EXPORT void
bread(vp,std,flag)621 bread(vp, std, flag)
622 	Argvec	*vp;
623 	FILE	*std[];
624 	int	flag;
625 {
626 	extern int	prflg;
627 	extern int	ttyflg;
628 	FILE	*old;
629 	int	save = delim;
630 	char	*linep = NULL;
631 	int	prsave = prflg;
632 	int	ttysave = ttyflg;
633 
634 	ttyflg = isatty(fdown(std[0]));
635 	prflg = ttyflg || iflg;
636 
637 	old = setinput(std[0]);
638 	linep = nextline();
639 #ifdef	DEBUG
640 	printf("linep: %s\n", linep);
641 #endif
642 	if (old == std[0])
643 		read_delim = delim;
644 	prflg = prsave;
645 	ttyflg = ttysave;
646 	setinput(old);
647 	delim = save;
648 	ev_insert(concat(vp->av_av[1], eql, linep, (char *)NULL));
649 	if (linep)
650 		free(linep);
651 }
652 
653 /* ARGSUSED */
654 EXPORT void
bswitch(vp,std,flag)655 bswitch(vp, std, flag)
656 	Argvec	*vp;
657 	FILE	*std[];
658 	int	flag;
659 {
660 		int	ilev;
661 	register Uchar	*pattern;
662 	register char	*p;
663 		char	*name, found;
664 		int	plen, *aux, *state, alt;
665 
666 	level++;
667 	if (!streql(vp->av_av[2], "of")) {
668 		not_found(std[2], "of");
669 		dec_level();
670 		return;
671 	}
672 	if (!readloop(vp, STOPEND))
673 		return;
674 	ilev = stackp->s_level;
675 	stackp = stackp->s_next;
676 	name = vp->av_av[1];
677 	found = FALSE;
678 	for (;;) {
679 		if (skipuntil(ilev, "case", "end", (char *)NULL) == 1)
680 			break;
681 		pattern = (Uchar *)foundline->s_lp + strlen("case");
682 		while (iswhite(*pattern))
683 			pattern++;
684 		plen = strlen((char *)pattern);
685 #ifdef DEBUG
686 		printf("matching with '%s'\n", pattern);
687 #endif
688 		aux = (int *)malloc((size_t) plen * sizeof (int));
689 		if ((alt = patcompile((unsigned char *)pattern, plen, aux)) == 0) {
690 			fprintf(std[2], "'%s': %s\n", pattern, ebadpattern);
691 			ex_status = 1;
692 			free((char *) aux);
693 			break;
694 		}
695 		state = (int *)malloc((size_t) (plen+1) * sizeof (int));
696 		p = (char *)patmatch((unsigned char *)pattern, aux,
697 				(unsigned char *)name, 0, strlen(name), alt, state);
698 		if (p && *p == '\0') {
699 #ifdef DEBUG
700 			printf("found.\n");
701 #endif
702 			found = TRUE;
703 			free((char *) aux);
704 			free((char *) state);
705 			break;
706 		}
707 		free((char *) aux);
708 		free((char *) state);
709 	}
710 	if (found) {
711 		if (execuntil(std, ilev, "end", (char *)NULL) == ABORTED)
712 			skipuntil(ilev, "end", (char *)NULL);
713 	} else if (ex_status == 1) {
714 		skipuntil(ilev, "end", (char *)NULL);
715 	}
716 	dec_level();
717 }
718