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