syn.c revision 1.1.1.2 1 /*
2 * shell parser (C version)
3 */
4
5 #include "sh.h"
6 #include "c_test.h"
7
8 struct multiline_state {
9 int on; /* set in multiline commands (\n becomes ;) */
10 int start_token; /* token multiline is for (eg, FOR, {, etc.) */
11 int start_line; /* line multiline command started on */
12 };
13
14 static void yyparse ARGS((void));
15 static struct op *pipeline ARGS((int cf));
16 static struct op *andor ARGS((void));
17 static struct op *c_list ARGS((void));
18 static struct ioword *synio ARGS((int cf));
19 static void musthave ARGS((int c, int cf));
20 static struct op *nested ARGS((int type, int smark, int emark));
21 static struct op *get_command ARGS((int cf));
22 static struct op *dogroup ARGS((void));
23 static struct op *thenpart ARGS((void));
24 static struct op *elsepart ARGS((void));
25 static struct op *caselist ARGS((void));
26 static struct op *casepart ARGS((int endtok));
27 static struct op *function_body ARGS((char *name, int ksh_func));
28 static char ** wordlist ARGS((void));
29 static struct op *block ARGS((int type, struct op *t1, struct op *t2,
30 char **wp));
31 static struct op *newtp ARGS((int type));
32 static void syntaxerr ARGS((const char *what))
33 GCC_FUNC_ATTR(noreturn);
34 static void multiline_push ARGS((struct multiline_state *save, int tok));
35 static void multiline_pop ARGS((struct multiline_state *saved));
36 static int assign_command ARGS((char *s));
37 #ifdef KSH
38 static int dbtestp_isa ARGS((Test_env *te, Test_meta meta));
39 static const char *dbtestp_getopnd ARGS((Test_env *te, Test_op op,
40 int do_eval));
41 static int dbtestp_eval ARGS((Test_env *te, Test_op op, const char *opnd1,
42 const char *opnd2, int do_eval));
43 static void dbtestp_error ARGS((Test_env *te, int offset, const char *msg));
44 #endif /* KSH */
45
46 static struct op *outtree; /* yyparse output */
47
48 static struct multiline_state multiline; /* \n changed to ; */
49
50 static int reject; /* token(cf) gets symbol again */
51 static int symbol; /* yylex value */
52
53 #define REJECT (reject = 1)
54 #define ACCEPT (reject = 0)
55 #define token(cf) \
56 ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
57 #define tpeek(cf) \
58 ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
59
60 static void
61 yyparse()
62 {
63 int c;
64
65 ACCEPT;
66 yynerrs = 0;
67
68 outtree = c_list();
69 c = tpeek(0);
70 if (c == 0 && !outtree)
71 outtree = newtp(TEOF);
72 else if (c != '\n' && c != 0)
73 syntaxerr((char *) 0);
74 }
75
76 static struct op *
77 pipeline(cf)
78 int cf;
79 {
80 register struct op *t, *p, *tl = NULL;
81
82 t = get_command(cf);
83 if (t != NULL) {
84 while (token(0) == '|') {
85 if ((p = get_command(CONTIN)) == NULL)
86 syntaxerr((char *) 0);
87 if (tl == NULL)
88 t = tl = block(TPIPE, t, p, NOWORDS);
89 else
90 tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
91 }
92 REJECT;
93 }
94 return (t);
95 }
96
97 static struct op *
98 andor()
99 {
100 register struct op *t, *p;
101 register int c;
102
103 t = pipeline(0);
104 if (t != NULL) {
105 while ((c = token(0)) == LOGAND || c == LOGOR) {
106 if ((p = pipeline(CONTIN)) == NULL)
107 syntaxerr((char *) 0);
108 t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
109 }
110 REJECT;
111 }
112 return (t);
113 }
114
115 static struct op *
116 c_list()
117 {
118 register struct op *t, *p, *tl = NULL;
119 register int c;
120
121 t = andor();
122 if (t != NULL) {
123 while ((c = token(0)) == ';' || c == '&' || c == COPROC ||
124 (c == '\n' && (multiline.on || source->type == SALIAS)))
125 {
126 if (c == '&' || c == COPROC) {
127 int type = c == '&' ? TASYNC : TCOPROC;
128 if (tl)
129 tl->right = block(type, tl->right,
130 NOBLOCK, NOWORDS);
131 else
132 t = block(type, t, NOBLOCK, NOWORDS);
133 }
134 if ((p = andor()) == NULL)
135 return (t);
136 if (tl == NULL)
137 t = tl = block(TLIST, t, p, NOWORDS);
138 else
139 tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
140 }
141 REJECT;
142 }
143 return (t);
144 }
145
146 static struct ioword *
147 synio(cf)
148 int cf;
149 {
150 register struct ioword *iop;
151 int ishere;
152
153 if (tpeek(cf) != REDIR)
154 return NULL;
155 ACCEPT;
156 iop = yylval.iop;
157 ishere = (iop->flag&IOTYPE) == IOHERE;
158 musthave(LWORD, ishere ? HEREDELIM : 0);
159 if (ishere) {
160 iop->delim = yylval.cp;
161 if (*ident != 0) /* unquoted */
162 iop->flag |= IOEVAL;
163 if (herep >= &heres[HERES])
164 yyerror("too many <<'s\n");
165 *herep++ = iop;
166 } else
167 iop->name = yylval.cp;
168 return iop;
169 }
170
171 static void
172 musthave(c, cf)
173 int c, cf;
174 {
175 if ((token(cf)) != c)
176 syntaxerr((char *) 0);
177 }
178
179 static struct op *
180 nested(type, smark, emark)
181 int type, smark, emark;
182 {
183 register struct op *t;
184 struct multiline_state old_multiline;
185
186 multiline_push(&old_multiline, smark);
187 t = c_list();
188 musthave(emark, KEYWORD|ALIAS);
189 multiline_pop(&old_multiline);
190 return (block(type, t, NOBLOCK, NOWORDS));
191 }
192
193 static struct op *
194 get_command(cf)
195 int cf;
196 {
197 register struct op *t;
198 register int c, iopn = 0, syniocf;
199 struct ioword *iop, **iops;
200 XPtrV args, vars;
201 struct multiline_state old_multiline;
202
203 iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1),
204 ATEMP);
205 XPinit(args, 16);
206 XPinit(vars, 16);
207
208 if (multiline.on)
209 cf = CONTIN;
210 syniocf = KEYWORD|ALIAS;
211 switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
212 default:
213 REJECT;
214 afree((void*) iops, ATEMP);
215 XPfree(args);
216 XPfree(vars);
217 return NULL; /* empty line */
218
219 case LWORD:
220 case REDIR:
221 REJECT;
222 syniocf &= ~(KEYWORD|ALIAS);
223 t = newtp(TCOM);
224 while (1) {
225 cf = (t->u.evalflags ? ARRAYVAR : 0)
226 | (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
227 switch (tpeek(cf)) {
228 case REDIR:
229 if (iopn >= NUFILE)
230 yyerror("too many redirections\n");
231 iops[iopn++] = synio(cf);
232 break;
233
234 case LWORD:
235 ACCEPT;
236 /* the iopn == 0 and XPsize(vars) == 0 are
237 * dubious but at&t ksh acts this way
238 */
239 if (iopn == 0 && XPsize(vars) == 0
240 && XPsize(args) == 0
241 && assign_command(ident))
242 t->u.evalflags = DOVACHECK;
243 if ((XPsize(args) == 0 || Flag(FKEYWORD))
244 && is_wdvarassign(yylval.cp))
245 XPput(vars, yylval.cp);
246 else
247 XPput(args, yylval.cp);
248 break;
249
250 case '(':
251 /* Check for "> foo (echo hi)", which at&t ksh
252 * allows (not POSIX, but not disallowed)
253 */
254 afree(t, ATEMP);
255 if (XPsize(args) == 0 && XPsize(vars) == 0) {
256 ACCEPT;
257 goto Subshell;
258 }
259 /* Must be a function */
260 if (iopn != 0 || XPsize(args) != 1
261 || XPsize(vars) != 0)
262 syntaxerr((char *) 0);
263 ACCEPT;
264 /*(*/
265 musthave(')', 0);
266 t = function_body(XPptrv(args)[0], FALSE);
267 goto Leave;
268
269 default:
270 goto Leave;
271 }
272 }
273 Leave:
274 break;
275
276 Subshell:
277 case '(':
278 t = nested(TPAREN, '(', ')');
279 break;
280
281 case '{': /*}*/
282 t = nested(TBRACE, '{', '}');
283 break;
284
285 #ifdef KSH
286 case MDPAREN:
287 {
288 static const char let_cmd[] = { CHAR, 'l', CHAR, 'e',
289 CHAR, 't', EOS };
290 syniocf &= ~(KEYWORD|ALIAS);
291 t = newtp(TCOM);
292 ACCEPT;
293 XPput(args, wdcopy(let_cmd, ATEMP));
294 musthave(LWORD,LETEXPR);
295 XPput(args, yylval.cp);
296 break;
297 }
298 #endif /* KSH */
299
300 #ifdef KSH
301 case DBRACKET: /* [[ .. ]] */
302 syniocf &= ~(KEYWORD|ALIAS);
303 t = newtp(TDBRACKET);
304 ACCEPT;
305 {
306 Test_env te;
307
308 te.flags = TEF_DBRACKET;
309 te.pos.av = &args;
310 te.isa = dbtestp_isa;
311 te.getopnd = dbtestp_getopnd;
312 te.eval = dbtestp_eval;
313 te.error = dbtestp_error;
314
315 test_parse(&te);
316 }
317 break;
318 #endif /* KSH */
319
320 case FOR:
321 case SELECT:
322 t = newtp((c == FOR) ? TFOR : TSELECT);
323 musthave(LWORD, ARRAYVAR);
324 if (!is_wdvarname(yylval.cp, TRUE))
325 yyerror("%s: bad identifier\n",
326 c == FOR ? "for" : "select");
327 t->str = str_save(ident, ATEMP);
328 multiline_push(&old_multiline, c);
329 t->vars = wordlist();
330 t->left = dogroup();
331 multiline_pop(&old_multiline);
332 break;
333
334 case WHILE:
335 case UNTIL:
336 multiline_push(&old_multiline, c);
337 t = newtp((c == WHILE) ? TWHILE : TUNTIL);
338 t->left = c_list();
339 t->right = dogroup();
340 multiline_pop(&old_multiline);
341 break;
342
343 case CASE:
344 t = newtp(TCASE);
345 musthave(LWORD, 0);
346 t->str = yylval.cp;
347 multiline_push(&old_multiline, c);
348 t->left = caselist();
349 multiline_pop(&old_multiline);
350 break;
351
352 case IF:
353 multiline_push(&old_multiline, c);
354 t = newtp(TIF);
355 t->left = c_list();
356 t->right = thenpart();
357 musthave(FI, KEYWORD|ALIAS);
358 multiline_pop(&old_multiline);
359 break;
360
361 case BANG:
362 syniocf &= ~(KEYWORD|ALIAS);
363 t = pipeline(0);
364 if (t == (struct op *) 0)
365 syntaxerr((char *) 0);
366 t = block(TBANG, NOBLOCK, t, NOWORDS);
367 break;
368
369 case TIME:
370 syniocf &= ~(KEYWORD|ALIAS);
371 t = pipeline(0);
372 t = block(TTIME, t, NOBLOCK, NOWORDS);
373 break;
374
375 case FUNCTION:
376 musthave(LWORD, 0);
377 t = function_body(yylval.cp, TRUE);
378 break;
379 }
380
381 while ((iop = synio(syniocf)) != NULL) {
382 if (iopn >= NUFILE)
383 yyerror("too many redirections\n");
384 iops[iopn++] = iop;
385 }
386
387 if (iopn == 0) {
388 afree((void*) iops, ATEMP);
389 t->ioact = NULL;
390 } else {
391 iops[iopn++] = NULL;
392 iops = (struct ioword **) aresize((void*) iops,
393 sizeofN(struct ioword *, iopn), ATEMP);
394 t->ioact = iops;
395 }
396
397 if (t->type == TCOM || t->type == TDBRACKET) {
398 XPput(args, NULL);
399 t->args = (char **) XPclose(args);
400 XPput(vars, NULL);
401 t->vars = (char **) XPclose(vars);
402 } else {
403 XPfree(args);
404 XPfree(vars);
405 }
406
407 return t;
408 }
409
410 static struct op *
411 dogroup()
412 {
413 register int c;
414 register struct op *list;
415
416 c = token(CONTIN|KEYWORD|ALIAS);
417 /* A {...} can be used instead of do...done for for/select loops
418 * but not for while/until loops - we don't need to check if it
419 * is a while loop because it would have been parsed as part of
420 * the conditional command list...
421 */
422 if (c == DO)
423 c = DONE;
424 else if (c == '{')
425 c = '}';
426 else
427 syntaxerr((char *) 0);
428 list = c_list();
429 musthave(c, KEYWORD|ALIAS);
430 return list;
431 }
432
433 static struct op *
434 thenpart()
435 {
436 register struct op *t;
437
438 musthave(THEN, KEYWORD|ALIAS);
439 t = newtp(0);
440 t->left = c_list();
441 if (t->left == NULL)
442 syntaxerr((char *) 0);
443 t->right = elsepart();
444 return (t);
445 }
446
447 static struct op *
448 elsepart()
449 {
450 register struct op *t;
451
452 switch (token(KEYWORD|ALIAS|VARASN)) {
453 case ELSE:
454 if ((t = c_list()) == NULL)
455 syntaxerr((char *) 0);
456 return (t);
457
458 case ELIF:
459 t = newtp(TELIF);
460 t->left = c_list();
461 t->right = thenpart();
462 return (t);
463
464 default:
465 REJECT;
466 }
467 return NULL;
468 }
469
470 static struct op *
471 caselist()
472 {
473 register struct op *t, *tl;
474 int c;
475
476 c = token(CONTIN|KEYWORD|ALIAS);
477 /* A {...} can be used instead of in...esac for case statements */
478 if (c == IN)
479 c = ESAC;
480 else if (c == '{')
481 c = '}';
482 else
483 syntaxerr((char *) 0);
484 t = tl = NULL;
485 while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
486 struct op *tc = casepart(c);
487 if (tl == NULL)
488 t = tl = tc, tl->right = NULL;
489 else
490 tl->right = tc, tl = tc;
491 }
492 musthave(c, KEYWORD|ALIAS);
493 return (t);
494 }
495
496 static struct op *
497 casepart(endtok)
498 int endtok;
499 {
500 register struct op *t;
501 register int c;
502 XPtrV ptns;
503
504 XPinit(ptns, 16);
505 t = newtp(TPAT);
506 c = token(CONTIN|KEYWORD); /* no ALIAS here */
507 if (c != '(')
508 REJECT;
509 do {
510 musthave(LWORD, 0);
511 XPput(ptns, yylval.cp);
512 } while ((c = token(0)) == '|');
513 REJECT;
514 XPput(ptns, NULL);
515 t->vars = (char **) XPclose(ptns);
516 musthave(')', 0);
517
518 t->left = c_list();
519 if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
520 musthave(BREAK, CONTIN|KEYWORD|ALIAS);
521 return (t);
522 }
523
524 static struct op *
525 function_body(name, ksh_func)
526 char *name;
527 int ksh_func; /* function foo { ... } vs foo() { .. } */
528 {
529 XString xs;
530 char *xp, *p;
531 struct op *t;
532 int old_func_parse;
533
534 Xinit(xs, xp, 16, ATEMP);
535 for (p = name; ; ) {
536 if ((*p == EOS && Xlength(xs, xp) == 0)
537 || (*p != EOS && *p != CHAR && *p != QCHAR
538 && *p != OQUOTE && *p != CQUOTE))
539 {
540 p = snptreef((char *) 0, 32, "%S", name);
541 yyerror("%s: invalid function name\n", p);
542 }
543 Xcheck(xs, xp);
544 if (*p == EOS) {
545 Xput(xs, xp, '\0');
546 break;
547 } else if (*p == CHAR || *p == QCHAR) {
548 Xput(xs, xp, p[1]);
549 p += 2;
550 } else
551 p++; /* OQUOTE/CQUOTE */
552 }
553 t = newtp(TFUNCT);
554 t->str = Xclose(xs, xp);
555 t->u.ksh_func = ksh_func;
556
557 /* Note that POSIX allows only compound statements after foo(), sh and
558 * at&t ksh allow any command, go with the later since it shouldn't
559 * break anything. However, for function foo, at&t ksh only accepts
560 * an open-brace.
561 */
562 if (ksh_func) {
563 musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
564 REJECT;
565 }
566
567 old_func_parse = e->flags & EF_FUNC_PARSE;
568 e->flags |= EF_FUNC_PARSE;
569 if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
570 /* create empty command so foo(): will work */
571 t->left = newtp(TCOM);
572 t->args = (char **) alloc(sizeof(char *), ATEMP);
573 t->args[0] = (char *) 0;
574 t->vars = (char **) alloc(sizeof(char *), ATEMP);
575 t->vars[0] = (char *) 0;
576 }
577 if (!old_func_parse)
578 e->flags &= ~EF_FUNC_PARSE;
579
580 return t;
581 }
582
583 static char **
584 wordlist()
585 {
586 register int c;
587 XPtrV args;
588
589 XPinit(args, 16);
590 if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
591 if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */
592 REJECT;
593 return NULL;
594 }
595 while ((c = token(0)) == LWORD)
596 XPput(args, yylval.cp);
597 if (c != '\n' && c != ';')
598 syntaxerr((char *) 0);
599 if (XPsize(args) == 0) {
600 XPfree(args);
601 return NULL;
602 } else {
603 XPput(args, NULL);
604 return (char **) XPclose(args);
605 }
606 }
607
608 /*
609 * supporting functions
610 */
611
612 static struct op *
613 block(type, t1, t2, wp)
614 int type;
615 struct op *t1, *t2;
616 char **wp;
617 {
618 register struct op *t;
619
620 t = newtp(type);
621 t->left = t1;
622 t->right = t2;
623 t->vars = wp;
624 return (t);
625 }
626
627 const struct tokeninfo {
628 const char *name;
629 short val;
630 short reserved;
631 } tokentab[] = {
632 /* Reserved words */
633 { "if", IF, TRUE },
634 { "then", THEN, TRUE },
635 { "else", ELSE, TRUE },
636 { "elif", ELIF, TRUE },
637 { "fi", FI, TRUE },
638 { "case", CASE, TRUE },
639 { "esac", ESAC, TRUE },
640 { "for", FOR, TRUE },
641 #ifdef KSH
642 { "select", SELECT, TRUE },
643 #endif /* KSH */
644 { "while", WHILE, TRUE },
645 { "until", UNTIL, TRUE },
646 { "do", DO, TRUE },
647 { "done", DONE, TRUE },
648 { "in", IN, TRUE },
649 { "function", FUNCTION, TRUE },
650 { "time", TIME, TRUE },
651 { "{", '{', TRUE },
652 { "}", '}', TRUE },
653 { "!", BANG, TRUE },
654 #ifdef KSH
655 { "[[", DBRACKET, TRUE },
656 #endif /* KSH */
657 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
658 { "&&", LOGAND, FALSE },
659 { "||", LOGOR, FALSE },
660 { ";;", BREAK, FALSE },
661 #ifdef KSH
662 { "((", MDPAREN, FALSE },
663 { "|&", COPROC, FALSE },
664 #endif /* KSH */
665 /* and some special cases... */
666 { "newline", '\n', FALSE },
667 { 0 }
668 };
669
670 void
671 initkeywords()
672 {
673 register struct tokeninfo const *tt;
674 register struct tbl *p;
675
676 tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
677 for (tt = tokentab; tt->name; tt++) {
678 if (tt->reserved) {
679 p = tenter(&keywords, tt->name, hash(tt->name));
680 p->flag |= DEFINED|ISSET;
681 p->type = CKEYWD;
682 p->val.i = tt->val;
683 }
684 }
685 }
686
687 static void
688 syntaxerr(what)
689 const char *what;
690 {
691 char redir[6]; /* 2<<- is the longest redirection, I think */
692 const char *s;
693 struct tokeninfo const *tt;
694 int c;
695
696 if (!what)
697 what = "unexpected";
698 REJECT;
699 c = token(0);
700 Again:
701 switch (c) {
702 case 0:
703 if (multiline.on && multiline.start_token) {
704 multiline.on = FALSE; /* avoid infinate loops */
705 c = multiline.start_token;
706 source->errline = multiline.start_line;
707 what = "unmatched";
708 goto Again;
709 }
710 /* don't quote the EOF */
711 yyerror("syntax error: unexpected EOF\n");
712 /*NOTREACHED*/
713
714 case LWORD:
715 s = snptreef((char *) 0, 32, "%S", yylval.cp);
716 break;
717
718 case REDIR:
719 s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
720 break;
721
722 default:
723 for (tt = tokentab; tt->name; tt++)
724 if (tt->val == c)
725 break;
726 if (tt->name)
727 s = tt->name;
728 else {
729 if (c > 0 && c < 256) {
730 redir[0] = c;
731 redir[1] = '\0';
732 } else
733 shf_snprintf(redir, sizeof(redir),
734 "?%d", c);
735 s = redir;
736 }
737 }
738 yyerror("syntax error: `%s' %s\n", s, what);
739 }
740
741 static void
742 multiline_push(save, tok)
743 struct multiline_state *save;
744 int tok;
745 {
746 *save = multiline;
747 multiline.on = TRUE;
748 multiline.start_token = tok;
749 multiline.start_line = source->line;
750 }
751
752 static void
753 multiline_pop(saved)
754 struct multiline_state *saved;
755 {
756 multiline = *saved;
757 }
758
759 static struct op *
760 newtp(type)
761 int type;
762 {
763 register struct op *t;
764
765 t = (struct op *) alloc(sizeof(*t), ATEMP);
766 t->type = type;
767 t->u.evalflags = 0;
768 t->args = t->vars = NULL;
769 t->ioact = NULL;
770 t->left = t->right = NULL;
771 t->str = NULL;
772 return (t);
773 }
774
775 struct op *
776 compile(s)
777 Source *s;
778 {
779 yynerrs = 0;
780 multiline.on = s->type == SSTRING;
781 multiline.start_token = 0;
782 multiline.start_line = 0;
783 herep = heres;
784 source = s;
785 yyparse();
786 return outtree;
787 }
788
789 /* This kludge exists to take care of sh/at&t ksh oddity in which
790 * the arguments of alias/export/readonly/typeset have no field
791 * splitting, file globbing, or (normal) tilde expansion done.
792 * at&t ksh seems to do something similar to this since
793 * $ touch a=a; typeset a=[ab]; echo "$a"
794 * a=[ab]
795 * $ x=typeset; $x a=[ab]; echo "$a"
796 * a=a
797 * $
798 */
799 static int
800 assign_command(s)
801 char *s;
802 {
803 char c = *s;
804
805 if (Flag(FPOSIX) || !*s)
806 return 0;
807 return (c == 'a' && strcmp(s, "alias") == 0)
808 || (c == 'e' && strcmp(s, "export") == 0)
809 || (c == 'r' && strcmp(s, "readonly") == 0)
810 || (c == 't' && strcmp(s, "typeset") == 0);
811 }
812
813
814 #ifdef KSH
815 /* Order important - indexed by Test_meta values
816 * Note that ||, &&, ( and ) can't appear in as unquoted strings
817 * in normal shell input, so these can be interpreted unambiguously
818 * in the evaluation pass.
819 */
820 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
821 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
822 static const char dbtest_not[] = { CHAR, '!', EOS };
823 static const char dbtest_oparen[] = { CHAR, '(', EOS };
824 static const char dbtest_cparen[] = { CHAR, ')', EOS };
825 const char *const dbtest_tokens[] = {
826 dbtest_or, dbtest_and, dbtest_not,
827 dbtest_oparen, dbtest_cparen
828 };
829 const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
830 const char db_lthan[] = { CHAR, '<', EOS };
831 const char db_gthan[] = { CHAR, '>', EOS };
832
833 /* Test if the current token is a whatever. Accepts the current token if
834 * it is. Returns 0 if it is not, non-zero if it is (in the case of
835 * TM_UNOP and TM_BINOP, the returned value is a Test_op).
836 */
837 static int
838 dbtestp_isa(te, meta)
839 Test_env *te;
840 Test_meta meta;
841 {
842 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
843 int uqword = 0;
844 char *save = (char *) 0;
845 int ret = 0;
846
847 /* unquoted word? */
848 uqword = c == LWORD && *ident;
849
850 if (meta == TM_OR)
851 ret = c == LOGOR;
852 else if (meta == TM_AND)
853 ret = c == LOGAND;
854 else if (meta == TM_NOT)
855 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;
856 else if (meta == TM_OPAREN)
857 ret = c == '(' /*)*/;
858 else if (meta == TM_CPAREN)
859 ret = c == /*(*/ ')';
860 else if (meta == TM_UNOP || meta == TM_BINOP) {
861 if (meta == TM_BINOP && c == REDIR
862 && (yylval.iop->flag == IOREAD
863 || yylval.iop->flag == IOWRITE))
864 {
865 ret = 1;
866 save = wdcopy(yylval.iop->flag == IOREAD ?
867 db_lthan : db_gthan, ATEMP);
868 } else if (uqword && (ret = (int) test_isop(te, meta, ident)))
869 save = yylval.cp;
870 } else /* meta == TM_END */
871 ret = uqword && strcmp(yylval.cp, db_close) == 0;
872 if (ret) {
873 ACCEPT;
874 if (meta != TM_END) {
875 if (!save)
876 save = wdcopy(dbtest_tokens[(int) meta], ATEMP);
877 XPput(*te->pos.av, save);
878 }
879 }
880 return ret;
881 }
882
883 static const char *
884 dbtestp_getopnd(te, op, do_eval)
885 Test_env *te;
886 Test_op op;
887 int do_eval;
888 {
889 int c = tpeek(ARRAYVAR);
890
891 if (c != LWORD)
892 return (const char *) 0;
893
894 ACCEPT;
895 XPput(*te->pos.av, yylval.cp);
896
897 return null;
898 }
899
900 static int
901 dbtestp_eval(te, op, opnd1, opnd2, do_eval)
902 Test_env *te;
903 Test_op op;
904 const char *opnd1;
905 const char *opnd2;
906 int do_eval;
907 {
908 return 1;
909 }
910
911 static void
912 dbtestp_error(te, offset, msg)
913 Test_env *te;
914 int offset;
915 const char *msg;
916 {
917 te->flags |= TEF_ERROR;
918
919 if (offset < 0) {
920 REJECT;
921 /* Kludgy to say the least... */
922 symbol = LWORD;
923 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av)
924 + offset);
925 }
926 syntaxerr(msg);
927 }
928 #endif /* KSH */
929