syn.c revision 1.1 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 case MDPAREN:
286 {
287 static const char let_cmd[] = { CHAR, 'l', CHAR, 'e',
288 CHAR, 't', EOS };
289 syniocf &= ~(KEYWORD|ALIAS);
290 t = newtp(TCOM);
291 ACCEPT;
292 XPput(args, wdcopy(let_cmd, ATEMP));
293 musthave(LWORD,LETEXPR);
294 XPput(args, yylval.cp);
295 break;
296 }
297
298 #ifdef KSH
299 case DBRACKET: /* [[ .. ]] */
300 syniocf &= ~(KEYWORD|ALIAS);
301 t = newtp(TDBRACKET);
302 ACCEPT;
303 {
304 Test_env te;
305
306 te.flags = TEF_DBRACKET;
307 te.pos.av = &args;
308 te.isa = dbtestp_isa;
309 te.getopnd = dbtestp_getopnd;
310 te.eval = dbtestp_eval;
311 te.error = dbtestp_error;
312
313 test_parse(&te);
314 }
315 break;
316 #endif /* KSH */
317
318 case FOR:
319 case SELECT:
320 t = newtp((c == FOR) ? TFOR : TSELECT);
321 musthave(LWORD, ARRAYVAR);
322 if (!is_wdvarname(yylval.cp, TRUE))
323 yyerror("%s: bad identifier\n",
324 c == FOR ? "for" : "select");
325 t->str = str_save(ident, ATEMP);
326 multiline_push(&old_multiline, c);
327 t->vars = wordlist();
328 t->left = dogroup();
329 multiline_pop(&old_multiline);
330 break;
331
332 case WHILE:
333 case UNTIL:
334 multiline_push(&old_multiline, c);
335 t = newtp((c == WHILE) ? TWHILE : TUNTIL);
336 t->left = c_list();
337 t->right = dogroup();
338 multiline_pop(&old_multiline);
339 break;
340
341 case CASE:
342 t = newtp(TCASE);
343 musthave(LWORD, 0);
344 t->str = yylval.cp;
345 multiline_push(&old_multiline, c);
346 t->left = caselist();
347 multiline_pop(&old_multiline);
348 break;
349
350 case IF:
351 multiline_push(&old_multiline, c);
352 t = newtp(TIF);
353 t->left = c_list();
354 t->right = thenpart();
355 musthave(FI, KEYWORD|ALIAS);
356 multiline_pop(&old_multiline);
357 break;
358
359 case BANG:
360 syniocf &= ~(KEYWORD|ALIAS);
361 t = pipeline(0);
362 if (t == (struct op *) 0)
363 syntaxerr((char *) 0);
364 t = block(TBANG, NOBLOCK, t, NOWORDS);
365 break;
366
367 case TIME:
368 syniocf &= ~(KEYWORD|ALIAS);
369 t = pipeline(0);
370 t = block(TTIME, t, NOBLOCK, NOWORDS);
371 break;
372
373 case FUNCTION:
374 musthave(LWORD, 0);
375 t = function_body(yylval.cp, TRUE);
376 break;
377 }
378
379 while ((iop = synio(syniocf)) != NULL) {
380 if (iopn >= NUFILE)
381 yyerror("too many redirections\n");
382 iops[iopn++] = iop;
383 }
384
385 if (iopn == 0) {
386 afree((void*) iops, ATEMP);
387 t->ioact = NULL;
388 } else {
389 iops[iopn++] = NULL;
390 iops = (struct ioword **) aresize((void*) iops,
391 sizeofN(struct ioword *, iopn), ATEMP);
392 t->ioact = iops;
393 }
394
395 if (t->type == TCOM || t->type == TDBRACKET) {
396 XPput(args, NULL);
397 t->args = (char **) XPclose(args);
398 XPput(vars, NULL);
399 t->vars = (char **) XPclose(vars);
400 } else {
401 XPfree(args);
402 XPfree(vars);
403 }
404
405 return t;
406 }
407
408 static struct op *
409 dogroup()
410 {
411 register int c;
412 register struct op *list;
413
414 c = token(CONTIN|KEYWORD|ALIAS);
415 /* A {...} can be used instead of do...done for for/select loops
416 * but not for while/until loops - we don't need to check if it
417 * is a while loop because it would have been parsed as part of
418 * the conditional command list...
419 */
420 if (c == DO)
421 c = DONE;
422 else if (c == '{')
423 c = '}';
424 else
425 syntaxerr((char *) 0);
426 list = c_list();
427 musthave(c, KEYWORD|ALIAS);
428 return list;
429 }
430
431 static struct op *
432 thenpart()
433 {
434 register struct op *t;
435
436 musthave(THEN, KEYWORD|ALIAS);
437 t = newtp(0);
438 t->left = c_list();
439 if (t->left == NULL)
440 syntaxerr((char *) 0);
441 t->right = elsepart();
442 return (t);
443 }
444
445 static struct op *
446 elsepart()
447 {
448 register struct op *t;
449
450 switch (token(KEYWORD|ALIAS|VARASN)) {
451 case ELSE:
452 if ((t = c_list()) == NULL)
453 syntaxerr((char *) 0);
454 return (t);
455
456 case ELIF:
457 t = newtp(TELIF);
458 t->left = c_list();
459 t->right = thenpart();
460 return (t);
461
462 default:
463 REJECT;
464 }
465 return NULL;
466 }
467
468 static struct op *
469 caselist()
470 {
471 register struct op *t, *tl;
472 int c;
473
474 c = token(CONTIN|KEYWORD|ALIAS);
475 /* A {...} can be used instead of in...esac for case statements */
476 if (c == IN)
477 c = ESAC;
478 else if (c == '{')
479 c = '}';
480 else
481 syntaxerr((char *) 0);
482 t = tl = NULL;
483 while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
484 struct op *tc = casepart(c);
485 if (tl == NULL)
486 t = tl = tc, tl->right = NULL;
487 else
488 tl->right = tc, tl = tc;
489 }
490 musthave(c, KEYWORD|ALIAS);
491 return (t);
492 }
493
494 static struct op *
495 casepart(endtok)
496 int endtok;
497 {
498 register struct op *t;
499 register int c;
500 XPtrV ptns;
501
502 XPinit(ptns, 16);
503 t = newtp(TPAT);
504 c = token(CONTIN|KEYWORD); /* no ALIAS here */
505 if (c != '(')
506 REJECT;
507 do {
508 musthave(LWORD, 0);
509 XPput(ptns, yylval.cp);
510 } while ((c = token(0)) == '|');
511 REJECT;
512 XPput(ptns, NULL);
513 t->vars = (char **) XPclose(ptns);
514 musthave(')', 0);
515
516 t->left = c_list();
517 if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
518 musthave(BREAK, CONTIN|KEYWORD|ALIAS);
519 return (t);
520 }
521
522 static struct op *
523 function_body(name, ksh_func)
524 char *name;
525 int ksh_func; /* function foo { ... } vs foo() { .. } */
526 {
527 XString xs;
528 char *xp, *p;
529 struct op *t;
530 int old_func_parse;
531
532 Xinit(xs, xp, 16, ATEMP);
533 for (p = name; ; ) {
534 if ((*p == EOS && Xlength(xs, xp) == 0)
535 || (*p != EOS && *p != CHAR && *p != QCHAR
536 && *p != OQUOTE && *p != CQUOTE))
537 {
538 p = snptreef((char *) 0, 32, "%S", name);
539 yyerror("%s: invalid function name\n", p);
540 }
541 Xcheck(xs, xp);
542 if (*p == EOS) {
543 Xput(xs, xp, '\0');
544 break;
545 } else if (*p == CHAR || *p == QCHAR) {
546 Xput(xs, xp, p[1]);
547 p += 2;
548 } else
549 p++; /* OQUOTE/CQUOTE */
550 }
551 t = newtp(TFUNCT);
552 t->str = Xclose(xs, xp);
553 t->u.ksh_func = ksh_func;
554
555 /* Note that POSIX allows only compound statements after foo(), sh and
556 * at&t ksh allow any command, go with the later since it shouldn't
557 * break anything. However, for function foo, at&t ksh only accepts
558 * an open-brace.
559 */
560 if (ksh_func) {
561 musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
562 REJECT;
563 }
564
565 old_func_parse = e->flags & EF_FUNC_PARSE;
566 e->flags |= EF_FUNC_PARSE;
567 if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
568 /* create empty command so foo(): will work */
569 t->left = newtp(TCOM);
570 t->args = (char **) alloc(sizeof(char *), ATEMP);
571 t->args[0] = (char *) 0;
572 t->vars = (char **) alloc(sizeof(char *), ATEMP);
573 t->vars[0] = (char *) 0;
574 }
575 if (!old_func_parse)
576 e->flags &= ~EF_FUNC_PARSE;
577
578 return t;
579 }
580
581 static char **
582 wordlist()
583 {
584 register int c;
585 XPtrV args;
586
587 XPinit(args, 16);
588 if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
589 if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */
590 REJECT;
591 return NULL;
592 }
593 while ((c = token(0)) == LWORD)
594 XPput(args, yylval.cp);
595 if (c != '\n' && c != ';')
596 syntaxerr((char *) 0);
597 if (XPsize(args) == 0) {
598 XPfree(args);
599 return NULL;
600 } else {
601 XPput(args, NULL);
602 return (char **) XPclose(args);
603 }
604 }
605
606 /*
607 * supporting functions
608 */
609
610 static struct op *
611 block(type, t1, t2, wp)
612 int type;
613 struct op *t1, *t2;
614 char **wp;
615 {
616 register struct op *t;
617
618 t = newtp(type);
619 t->left = t1;
620 t->right = t2;
621 t->vars = wp;
622 return (t);
623 }
624
625 const struct tokeninfo {
626 const char *name;
627 short val;
628 short reserved;
629 } tokentab[] = {
630 /* Reserved words */
631 { "if", IF, TRUE },
632 { "then", THEN, TRUE },
633 { "else", ELSE, TRUE },
634 { "elif", ELIF, TRUE },
635 { "fi", FI, TRUE },
636 { "case", CASE, TRUE },
637 { "esac", ESAC, TRUE },
638 { "for", FOR, TRUE },
639 #ifdef KSH
640 { "select", SELECT, TRUE },
641 #endif /* KSH */
642 { "while", WHILE, TRUE },
643 { "until", UNTIL, TRUE },
644 { "do", DO, TRUE },
645 { "done", DONE, TRUE },
646 { "in", IN, TRUE },
647 { "function", FUNCTION, TRUE },
648 { "time", TIME, TRUE },
649 { "{", '{', TRUE },
650 { "}", '}', TRUE },
651 { "!", BANG, TRUE },
652 #ifdef KSH
653 { "[[", DBRACKET, TRUE },
654 #endif /* KSH */
655 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
656 { "&&", LOGAND, FALSE },
657 { "||", LOGOR, FALSE },
658 { ";;", BREAK, FALSE },
659 { "((", MDPAREN, FALSE },
660 #ifdef KSH
661 { "|&", COPROC, FALSE },
662 #endif /* KSH */
663 /* and some special cases... */
664 { "newline", '\n', FALSE },
665 { 0 }
666 };
667
668 void
669 initkeywords()
670 {
671 register struct tokeninfo const *tt;
672 register struct tbl *p;
673
674 tinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
675 for (tt = tokentab; tt->name; tt++) {
676 if (tt->reserved) {
677 p = tenter(&keywords, tt->name, hash(tt->name));
678 p->flag |= DEFINED|ISSET;
679 p->type = CKEYWD;
680 p->val.i = tt->val;
681 }
682 }
683 }
684
685 static void
686 syntaxerr(what)
687 const char *what;
688 {
689 char redir[6]; /* 2<<- is the longest redirection, I think */
690 const char *s;
691 struct tokeninfo const *tt;
692 int c;
693
694 if (!what)
695 what = "unexpected";
696 REJECT;
697 c = token(0);
698 Again:
699 switch (c) {
700 case 0:
701 if (multiline.on && multiline.start_token) {
702 multiline.on = FALSE; /* avoid infinate loops */
703 c = multiline.start_token;
704 source->errline = multiline.start_line;
705 what = "unmatched";
706 goto Again;
707 }
708 /* don't quote the EOF */
709 yyerror("syntax error: unexpected EOF\n");
710 /*NOTREACHED*/
711
712 case LWORD:
713 s = snptreef((char *) 0, 32, "%S", yylval.cp);
714 break;
715
716 case REDIR:
717 s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
718 break;
719
720 default:
721 for (tt = tokentab; tt->name; tt++)
722 if (tt->val == c)
723 break;
724 if (tt->name)
725 s = tt->name;
726 else {
727 if (c > 0 && c < 256) {
728 redir[0] = c;
729 redir[1] = '\0';
730 } else
731 shf_snprintf(redir, sizeof(redir),
732 "?%d", c);
733 s = redir;
734 }
735 }
736 yyerror("syntax error: `%s' %s\n", s, what);
737 }
738
739 static void
740 multiline_push(save, tok)
741 struct multiline_state *save;
742 int tok;
743 {
744 *save = multiline;
745 multiline.on = TRUE;
746 multiline.start_token = tok;
747 multiline.start_line = source->line;
748 }
749
750 static void
751 multiline_pop(saved)
752 struct multiline_state *saved;
753 {
754 multiline = *saved;
755 }
756
757 static struct op *
758 newtp(type)
759 int type;
760 {
761 register struct op *t;
762
763 t = (struct op *) alloc(sizeof(*t), ATEMP);
764 t->type = type;
765 t->u.evalflags = 0;
766 t->args = t->vars = NULL;
767 t->ioact = NULL;
768 t->left = t->right = NULL;
769 t->str = NULL;
770 return (t);
771 }
772
773 struct op *
774 compile(s)
775 Source *s;
776 {
777 yynerrs = 0;
778 multiline.on = s->type == SSTRING;
779 multiline.start_token = 0;
780 multiline.start_line = 0;
781 herep = heres;
782 source = s;
783 yyparse();
784 return outtree;
785 }
786
787 /* This kludge exists to take care of sh/at&t ksh oddity in which
788 * the arguments of alias/export/readonly/typeset have no field
789 * splitting, file globbing, or (normal) tilde expansion done.
790 * at&t ksh seems to do something similar to this since
791 * $ touch a=a; typeset a=[ab]; echo "$a"
792 * a=[ab]
793 * $ x=typeset; $x a=[ab]; echo "$a"
794 * a=a
795 * $
796 */
797 static int
798 assign_command(s)
799 char *s;
800 {
801 char c = *s;
802
803 if (Flag(FPOSIX) || !*s)
804 return 0;
805 return (c == 'a' && strcmp(s, "alias") == 0)
806 || (c == 'e' && strcmp(s, "export") == 0)
807 || (c == 'r' && strcmp(s, "readonly") == 0)
808 || (c == 't' && strcmp(s, "typeset") == 0);
809 }
810
811
812 #ifdef KSH
813 /* Order important - indexed by Test_meta values
814 * Note that ||, &&, ( and ) can't appear in as unquoted strings
815 * in normal shell input, so these can be interpreted unambiguously
816 * in the evaluation pass.
817 */
818 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
819 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
820 static const char dbtest_not[] = { CHAR, '!', EOS };
821 static const char dbtest_oparen[] = { CHAR, '(', EOS };
822 static const char dbtest_cparen[] = { CHAR, ')', EOS };
823 const char *const dbtest_tokens[] = {
824 dbtest_or, dbtest_and, dbtest_not,
825 dbtest_oparen, dbtest_cparen
826 };
827 const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
828 const char db_lthan[] = { CHAR, '<', EOS };
829 const char db_gthan[] = { CHAR, '>', EOS };
830
831 /* Test if the current token is a whatever. Accepts the current token if
832 * it is. Returns 0 if it is not, non-zero if it is (in the case of
833 * TM_UNOP and TM_BINOP, the returned value is a Test_op).
834 */
835 static int
836 dbtestp_isa(te, meta)
837 Test_env *te;
838 Test_meta meta;
839 {
840 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
841 int uqword = 0;
842 char *save = (char *) 0;
843 int ret = 0;
844
845 /* unquoted word? */
846 uqword = c == LWORD && *ident;
847
848 if (meta == TM_OR)
849 ret = c == LOGOR;
850 else if (meta == TM_AND)
851 ret = c == LOGAND;
852 else if (meta == TM_NOT)
853 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;
854 else if (meta == TM_OPAREN)
855 ret = c == '(' /*)*/;
856 else if (meta == TM_CPAREN)
857 ret = c == /*(*/ ')';
858 else if (meta == TM_UNOP || meta == TM_BINOP) {
859 if (meta == TM_BINOP && c == REDIR
860 && (yylval.iop->flag == IOREAD
861 || yylval.iop->flag == IOWRITE))
862 {
863 ret = 1;
864 save = wdcopy(yylval.iop->flag == IOREAD ?
865 db_lthan : db_gthan, ATEMP);
866 } else if (uqword && (ret = (int) test_isop(te, meta, ident)))
867 save = yylval.cp;
868 } else /* meta == TM_END */
869 ret = uqword && strcmp(yylval.cp, db_close) == 0;
870 if (ret) {
871 ACCEPT;
872 if (meta != TM_END) {
873 if (!save)
874 save = wdcopy(dbtest_tokens[(int) meta], ATEMP);
875 XPput(*te->pos.av, save);
876 }
877 }
878 return ret;
879 }
880
881 static const char *
882 dbtestp_getopnd(te, op, do_eval)
883 Test_env *te;
884 Test_op op;
885 int do_eval;
886 {
887 int c = tpeek(ARRAYVAR);
888
889 if (c != LWORD)
890 return (const char *) 0;
891
892 ACCEPT;
893 XPput(*te->pos.av, yylval.cp);
894
895 return null;
896 }
897
898 static int
899 dbtestp_eval(te, op, opnd1, opnd2, do_eval)
900 Test_env *te;
901 Test_op op;
902 const char *opnd1;
903 const char *opnd2;
904 int do_eval;
905 {
906 return 1;
907 }
908
909 static void
910 dbtestp_error(te, offset, msg)
911 Test_env *te;
912 int offset;
913 const char *msg;
914 {
915 te->flags |= TEF_ERROR;
916
917 if (offset < 0) {
918 REJECT;
919 /* Kludgy to say the least... */
920 symbol = LWORD;
921 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av)
922 + offset);
923 }
924 syntaxerr(msg);
925 }
926 #endif /* KSH */
927