Don't crash when board size is changed.
[AROS-Contrib.git] / gnu / abc-shell / syn.c
blob526663201508052e31f2b5f1b398e6707fbddd94
1 /*
2 * shell parser (C version)
3 */
5 #include "sh.h"
6 #include "c_test.h"
8 struct nesting_state {
9 int start_token; /* token than began nesting (eg, FOR) */
10 int start_line; /* line nesting began on */
13 static void yyparse(void);
14 static struct op *pipeline(int);
15 static struct op *andor(void);
16 static struct op *c_list(int);
17 static struct ioword *synio(int);
18 static void musthave(int, int);
19 static struct op *nested(int, int, int);
20 static struct op *get_command(int);
21 static struct op *dogroup(void);
22 static struct op *thenpart(void);
23 static struct op *elsepart(void);
24 static struct op *caselist(void);
25 static struct op *casepart(int);
26 static struct op *function_body(char *, int);
27 static char ** wordlist(void);
28 static struct op *block(int, struct op *, struct op *, char **);
29 static struct op *newtp(int);
30 static void syntaxerr(const char *) __attribute__((__noreturn__));
31 static void nesting_push(struct nesting_state *, int);
32 static void nesting_pop(struct nesting_state *);
33 static int assign_command(char *);
34 static int inalias(struct source *);
35 static int dbtestp_isa(Test_env *, Test_meta);
36 static const char *dbtestp_getopnd(Test_env *, Test_op, int);
37 static int dbtestp_eval(Test_env *, Test_op, const char *, const char *, int);
38 static void dbtestp_error(Test_env *, int, const char *);
40 static struct op *outtree; /* yyparse output */
41 static struct nesting_state nesting; /* \n changed to ; */
43 static int reject; /* token(cf) gets symbol again */
44 static int symbol; /* yylex value */
46 #define REJECT (reject = 1)
47 #define ACCEPT (reject = 0)
48 #define token(cf) \
49 ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
50 #define tpeek(cf) \
51 ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
53 static void
54 yyparse(void)
56 int c;
58 ACCEPT;
60 outtree = c_list(source->type == SSTRING);
61 c = tpeek(0);
62 if (c == 0 && !outtree)
63 outtree = newtp(TEOF);
64 else if (c != '\n' && c != 0)
65 syntaxerr((char *) 0);
68 static struct op *
69 pipeline(int cf)
71 struct op *t, *p, *tl = NULL;
73 t = get_command(cf);
74 if (t != NULL) {
75 while (token(0) == '|') {
76 if ((p = get_command(CONTIN)) == NULL)
77 syntaxerr((char *) 0);
78 if (tl == NULL)
79 t = tl = block(TPIPE, t, p, NOWORDS);
80 else
81 tl = tl->right = block(TPIPE, tl->right, p, NOWORDS);
83 REJECT;
85 return (t);
88 static struct op *
89 andor(void)
91 struct op *t, *p;
92 int c;
94 t = pipeline(0);
95 if (t != NULL) {
96 while ((c = token(0)) == LOGAND || c == LOGOR) {
97 if ((p = pipeline(CONTIN)) == NULL)
98 syntaxerr((char *) 0);
99 t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS);
101 REJECT;
103 return (t);
106 static struct op *
107 c_list(int multi)
109 struct op *t = NULL, *p, *tl = NULL;
110 int c;
111 int have_sep;
113 while (1) {
114 p = andor();
115 /* Token has always been read/rejected at this point, so
116 * we don't worry about what flags to pass token()
118 c = token(0);
119 have_sep = 1;
120 if (c == '\n' && (multi || inalias(source))) {
121 if (!p) /* ignore blank lines */
122 continue;
123 } else if (!p)
124 break;
125 else if (c == '&' || c == COPROC)
126 p = block(c == '&' ? TASYNC : TCOPROC,
127 p, NOBLOCK, NOWORDS);
128 else if (c != ';')
129 have_sep = 0;
130 if (!t)
131 t = p;
132 else if (!tl)
133 t = tl = block(TLIST, t, p, NOWORDS);
134 else
135 tl = tl->right = block(TLIST, tl->right, p, NOWORDS);
136 if (!have_sep)
137 break;
139 REJECT;
140 return t;
143 static struct ioword *
144 synio(int cf)
146 struct ioword *iop;
147 int ishere;
149 if (tpeek(cf) != REDIR)
150 return NULL;
151 ACCEPT;
152 iop = yylval.iop;
153 ishere = (iop->flag&IOTYPE) == IOHERE;
154 musthave(LWORD, ishere ? HEREDELIM : 0);
155 if (ishere) {
156 iop->delim = yylval.cp;
157 if (*ident != 0) /* unquoted */
158 iop->flag |= IOEVAL;
159 if (herep >= &heres[HERES])
160 yyerror("too many <<'s\n");
161 *herep++ = iop;
162 } else
163 iop->name = yylval.cp;
164 return iop;
167 static void
168 musthave(int c, int cf)
170 if ((token(cf)) != c)
171 syntaxerr((char *) 0);
174 static struct op *
175 nested(int type, int smark, int emark)
177 struct op *t;
178 struct nesting_state old_nesting;
180 nesting_push(&old_nesting, smark);
181 t = c_list(true);
182 musthave(emark, KEYWORD|ALIAS);
183 nesting_pop(&old_nesting);
184 return (block(type, t, NOBLOCK, NOWORDS));
187 static struct op *
188 get_command(int cf)
190 struct op *t;
191 int c, iopn = 0, syniocf;
192 struct ioword *iop, **iops;
193 XPtrV args, vars;
194 struct nesting_state old_nesting;
196 iops = (struct ioword **) alloc(sizeofN(struct ioword *, NUFILE+1),
197 ATEMP);
198 XPinit(args, 16);
199 XPinit(vars, 16);
201 syniocf = KEYWORD|ALIAS;
202 switch (c = token(cf|KEYWORD|ALIAS|VARASN)) {
203 default:
204 REJECT;
205 afree((void*) iops, ATEMP);
206 XPfree(args);
207 XPfree(vars);
208 return NULL; /* empty line */
210 case LWORD:
211 case REDIR:
212 REJECT;
213 syniocf &= ~(KEYWORD|ALIAS);
214 t = newtp(TCOM);
215 t->lineno = source->line;
216 while (1) {
217 cf = (t->u.evalflags ? ARRAYVAR : 0)
218 | (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
219 switch (tpeek(cf)) {
220 case REDIR:
221 if (iopn >= NUFILE)
222 yyerror("too many redirections\n");
223 iops[iopn++] = synio(cf);
224 break;
226 case LWORD:
227 ACCEPT;
228 /* the iopn == 0 and XPsize(vars) == 0 are
229 * dubious but at&t ksh acts this way
231 if (iopn == 0 && XPsize(vars) == 0
232 && XPsize(args) == 0
233 && assign_command(ident))
234 t->u.evalflags = DOVACHECK;
235 if ((XPsize(args) == 0 || Flag(FKEYWORD))
236 && is_wdvarassign(yylval.cp))
237 XPput(vars, yylval.cp);
238 else
239 XPput(args, yylval.cp);
240 break;
242 case '(':
243 /* Check for "> foo (echo hi)", which at&t ksh
244 * allows (not POSIX, but not disallowed)
246 afree(t, ATEMP);
247 if (XPsize(args) == 0 && XPsize(vars) == 0) {
248 ACCEPT;
249 goto Subshell;
251 /* Must be a function */
252 if (iopn != 0 || XPsize(args) != 1
253 || XPsize(vars) != 0)
254 syntaxerr((char *) 0);
255 ACCEPT;
256 /*(*/
257 musthave(')', 0);
258 t = function_body(XPptrv(args)[0], false);
259 goto Leave;
261 default:
262 goto Leave;
265 Leave:
266 break;
268 Subshell:
269 case '(':
270 t = nested(TPAREN, '(', ')');
271 break;
273 case '{': /*}*/
274 t = nested(TBRACE, '{', '}');
275 break;
277 case MDPAREN:
279 static const char let_cmd[] = { CHAR, 'l', CHAR, 'e',
280 CHAR, 't', EOS };
281 /* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
282 t = newtp(TCOM);
283 t->lineno = source->line;
284 ACCEPT;
285 XPput(args, wdcopy(let_cmd, ATEMP));
286 musthave(LWORD,LETEXPR);
287 XPput(args, yylval.cp);
288 break;
291 case DBRACKET: /* [[ .. ]] */
292 /* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
293 t = newtp(TDBRACKET);
294 ACCEPT;
296 Test_env te;
298 te.flags = TEF_DBRACKET;
299 te.pos.av = &args;
300 te.isa = dbtestp_isa;
301 te.getopnd = dbtestp_getopnd;
302 te.eval = dbtestp_eval;
303 te.error = dbtestp_error;
305 test_parse(&te);
307 break;
309 case FOR:
310 case SELECT:
311 t = newtp((c == FOR) ? TFOR : TSELECT);
312 musthave(LWORD, ARRAYVAR);
313 if (!is_wdvarname(yylval.cp, true))
314 yyerror("%s: bad identifier\n",
315 c == FOR ? "for" : "select");
316 t->str = str_save(ident, ATEMP);
317 nesting_push(&old_nesting, c);
318 t->vars = wordlist();
319 t->left = dogroup();
320 nesting_pop(&old_nesting);
321 break;
323 case WHILE:
324 case UNTIL:
325 nesting_push(&old_nesting, c);
326 t = newtp((c == WHILE) ? TWHILE : TUNTIL);
327 t->left = c_list(true);
328 t->right = dogroup();
329 nesting_pop(&old_nesting);
330 break;
332 case CASE:
333 t = newtp(TCASE);
334 musthave(LWORD, 0);
335 t->str = yylval.cp;
336 nesting_push(&old_nesting, c);
337 t->left = caselist();
338 nesting_pop(&old_nesting);
339 break;
341 case IF:
342 nesting_push(&old_nesting, c);
343 t = newtp(TIF);
344 t->left = c_list(true);
345 t->right = thenpart();
346 musthave(FI, KEYWORD|ALIAS);
347 nesting_pop(&old_nesting);
348 break;
350 case BANG:
351 syniocf &= ~(KEYWORD|ALIAS);
352 t = pipeline(0);
353 if (t == (struct op *) 0)
354 syntaxerr((char *) 0);
355 t = block(TBANG, NOBLOCK, t, NOWORDS);
356 break;
358 case TIME:
359 syniocf &= ~(KEYWORD|ALIAS);
360 t = pipeline(0);
361 t = block(TTIME, t, NOBLOCK, NOWORDS);
362 break;
364 case FUNCTION:
365 musthave(LWORD, 0);
366 t = function_body(yylval.cp, true);
367 break;
370 while ((iop = synio(syniocf)) != NULL) {
371 if (iopn >= NUFILE)
372 yyerror("too many redirections\n");
373 iops[iopn++] = iop;
376 if (iopn == 0) {
377 afree((void*) iops, ATEMP);
378 t->ioact = NULL;
379 } else {
380 iops[iopn++] = NULL;
381 iops = (struct ioword **) aresize((void*) iops,
382 sizeofN(struct ioword *, iopn), ATEMP);
383 t->ioact = iops;
386 if (t->type == TCOM || t->type == TDBRACKET) {
387 XPput(args, NULL);
388 t->args = (char **) XPclose(args);
389 XPput(vars, NULL);
390 t->vars = (char **) XPclose(vars);
391 } else {
392 XPfree(args);
393 XPfree(vars);
396 return t;
399 static struct op *
400 dogroup(void)
402 int c;
403 struct op *list;
405 c = token(CONTIN|KEYWORD|ALIAS);
406 /* A {...} can be used instead of do...done for for/select loops
407 * but not for while/until loops - we don't need to check if it
408 * is a while loop because it would have been parsed as part of
409 * the conditional command list...
411 if (c == DO)
412 c = DONE;
413 else if (c == '{')
414 c = '}';
415 else
416 syntaxerr((char *) 0);
417 list = c_list(true);
418 musthave(c, KEYWORD|ALIAS);
419 return list;
422 static struct op *
423 thenpart(void)
425 struct op *t;
427 musthave(THEN, KEYWORD|ALIAS);
428 t = newtp(0);
429 t->left = c_list(true);
430 if (t->left == NULL)
431 syntaxerr((char *) 0);
432 t->right = elsepart();
433 return (t);
436 static struct op *
437 elsepart(void)
439 struct op *t;
441 switch (token(KEYWORD|ALIAS|VARASN)) {
442 case ELSE:
443 if ((t = c_list(true)) == NULL)
444 syntaxerr((char *) 0);
445 return (t);
447 case ELIF:
448 t = newtp(TELIF);
449 t->left = c_list(true);
450 t->right = thenpart();
451 return (t);
453 default:
454 REJECT;
456 return NULL;
459 static struct op *
460 caselist(void)
462 struct op *t, *tl;
463 int c;
465 c = token(CONTIN|KEYWORD|ALIAS);
466 /* A {...} can be used instead of in...esac for case statements */
467 if (c == IN)
468 c = ESAC;
469 else if (c == '{')
470 c = '}';
471 else
472 syntaxerr((char *) 0);
473 t = tl = NULL;
474 while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */
475 struct op *tc = casepart(c);
476 if (tl == NULL)
477 t = tl = tc, tl->right = NULL;
478 else
479 tl->right = tc, tl = tc;
481 musthave(c, KEYWORD|ALIAS);
482 return (t);
485 static struct op *
486 casepart(int endtok)
488 struct op *t;
489 int c;
490 XPtrV ptns;
492 XPinit(ptns, 16);
493 t = newtp(TPAT);
494 c = token(CONTIN|KEYWORD); /* no ALIAS here */
495 if (c != '(')
496 REJECT;
497 do {
498 musthave(LWORD, 0);
499 XPput(ptns, yylval.cp);
500 } while ((c = token(0)) == '|');
501 REJECT;
502 XPput(ptns, NULL);
503 t->vars = (char **) XPclose(ptns);
504 musthave(')', 0);
506 t->left = c_list(true);
507 /* Note: Posix requires the ;; */
508 if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)
509 musthave(BREAK, CONTIN|KEYWORD|ALIAS);
510 return (t);
513 static struct op *
514 function_body(char *name,
515 int ksh_func) /* function foo { ... } vs foo() { .. } */
517 char *sname, *p;
518 struct op *t;
519 int old_func_parse;
521 sname = wdstrip(name);
522 /* Check for valid characters in name. posix and ksh93 say only
523 * allow [a-zA-Z_0-9] but this allows more as old pdksh's have
524 * allowed more (the following were never allowed:
525 * nul space nl tab $ ' " \ ` ( ) & | ; = < >
526 * C_QUOTE covers all but = and adds # [ ? *)
528 for (p = sname; *p; p++)
529 if (ctype(*p, C_QUOTE) || *p == '=')
530 yyerror("%s: invalid function name\n", sname);
532 t = newtp(TFUNCT);
533 t->str = sname;
534 t->u.ksh_func = ksh_func;
535 t->lineno = source->line;
537 /* Note that POSIX allows only compound statements after foo(), sh and
538 * at&t ksh allow any command, go with the later since it shouldn't
539 * break anything. However, for function foo, at&t ksh only accepts
540 * an open-brace.
542 if (ksh_func) {
543 musthave('{', CONTIN|KEYWORD|ALIAS); /* } */
544 REJECT;
547 old_func_parse = e->flags & EF_FUNC_PARSE;
548 e->flags |= EF_FUNC_PARSE;
549 if ((t->left = get_command(CONTIN)) == (struct op *) 0) {
551 * Probably something like foo() followed by eof or ;.
552 * This is accepted by sh and ksh88.
553 * To make "typset -f foo" work reliably (so its output can
554 * be used as input), we pretend there is a colon here.
556 t->left = newtp(TCOM);
557 t->left->args = (char **) alloc(sizeof(char *) * 2, ATEMP);
558 t->left->args[0] = alloc(sizeof(char) * 3, ATEMP);
559 t->left->args[0][0] = CHAR;
560 t->left->args[0][1] = ':';
561 t->left->args[0][2] = EOS;
562 t->left->args[1] = (char *) 0;
563 t->left->vars = (char **) alloc(sizeof(char *), ATEMP);
564 t->left->vars[0] = (char *) 0;
565 t->left->lineno = 1;
567 if (!old_func_parse)
568 e->flags &= ~EF_FUNC_PARSE;
570 return t;
573 static char **
574 wordlist(void)
576 int c;
577 XPtrV args;
579 XPinit(args, 16);
580 /* Posix does not do alias expansion here... */
581 if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) {
582 if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */
583 REJECT;
584 return NULL;
586 while ((c = token(0)) == LWORD)
587 XPput(args, yylval.cp);
588 if (c != '\n' && c != ';')
589 syntaxerr((char *) 0);
590 if (XPsize(args) == 0) {
591 XPfree(args);
592 return NULL;
593 } else {
594 XPput(args, NULL);
595 return (char **) XPclose(args);
600 * supporting functions
603 static struct op *
604 block(int type, struct op *t1, struct op *t2, char **wp)
606 struct op *t;
608 t = newtp(type);
609 t->left = t1;
610 t->right = t2;
611 t->vars = wp;
612 return (t);
615 const struct tokeninfo {
616 const char *name;
617 short val;
618 short reserved;
619 } tokentab[] = {
620 /* Reserved words */
621 { "if", IF, true },
622 { "then", THEN, true },
623 { "else", ELSE, true },
624 { "elif", ELIF, true },
625 { "fi", FI, true },
626 { "case", CASE, true },
627 { "esac", ESAC, true },
628 { "for", FOR, true },
629 { "select", SELECT, true },
630 { "while", WHILE, true },
631 { "until", UNTIL, true },
632 { "do", DO, true },
633 { "done", DONE, true },
634 { "in", IN, true },
635 { "function", FUNCTION, true },
636 { "time", TIME, true },
637 { "{", '{', true },
638 { "}", '}', true },
639 { "!", BANG, true },
640 { "[[", DBRACKET, true },
641 /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
642 { "&&", LOGAND, false },
643 { "||", LOGOR, false },
644 { ";;", BREAK, false },
645 { "((", MDPAREN, false },
646 { "|&", COPROC, false },
647 /* and some special cases... */
648 { "newline", '\n', false },
649 { 0 }
652 void
653 initkeywords(void)
655 struct tokeninfo const *tt;
656 struct tbl *p;
658 ktinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */
659 for (tt = tokentab; tt->name; tt++) {
660 if (tt->reserved) {
661 p = ktenter(&keywords, tt->name, hash(tt->name));
662 p->flag |= DEFINED|ISSET;
663 p->type = CKEYWD;
664 p->val.i = tt->val;
669 static void
670 syntaxerr(const char *what)
672 char redir[6]; /* 2<<- is the longest redirection, I think */
673 const char *s;
674 struct tokeninfo const *tt;
675 int c;
677 if (!what)
678 what = "unexpected";
679 REJECT;
680 c = token(0);
681 Again:
682 switch (c) {
683 case 0:
684 if (nesting.start_token) {
685 c = nesting.start_token;
686 source->errline = nesting.start_line;
687 what = "unmatched";
688 goto Again;
690 /* don't quote the EOF */
691 yyerror("syntax error: unexpected EOF\n");
692 /*NOTREACHED*/
694 case LWORD:
695 s = snptreef((char *) 0, 32, "%S", yylval.cp);
696 break;
698 case REDIR:
699 s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
700 break;
702 default:
703 for (tt = tokentab; tt->name; tt++)
704 if (tt->val == c)
705 break;
706 if (tt->name)
707 s = tt->name;
708 else {
709 if (c > 0 && c < 256) {
710 redir[0] = c;
711 redir[1] = '\0';
712 } else
713 shf_snprintf(redir, sizeof(redir),
714 "?%d", c);
715 s = redir;
718 yyerror("syntax error: `%s' %s\n", s, what);
721 static void
722 nesting_push(struct nesting_state *save, int tok)
724 *save = nesting;
725 nesting.start_token = tok;
726 nesting.start_line = source->line;
729 static void
730 nesting_pop(struct nesting_state *saved)
732 nesting = *saved;
735 static struct op *
736 newtp(int type)
738 struct op *t;
740 t = (struct op *) alloc(sizeof(*t), ATEMP);
741 t->type = type;
742 t->u.evalflags = 0;
743 t->args = t->vars = NULL;
744 t->ioact = NULL;
745 t->left = t->right = NULL;
746 t->str = NULL;
747 return (t);
750 struct op *
751 compile(Source *s)
753 nesting.start_token = 0;
754 nesting.start_line = 0;
755 herep = heres;
756 source = s;
757 yyparse();
758 return outtree;
761 /* This kludge exists to take care of sh/at&t ksh oddity in which
762 * the arguments of alias/export/readonly/typeset have no field
763 * splitting, file globbing, or (normal) tilde expansion done.
764 * at&t ksh seems to do something similar to this since
765 * $ touch a=a; typeset a=[ab]; echo "$a"
766 * a=[ab]
767 * $ x=typeset; $x a=[ab]; echo "$a"
768 * a=a
769 * $
771 static int
772 assign_command(char *s)
774 if (Flag(FPOSIX) || !*s)
775 return 0;
776 return (strcmp(s, "alias") == 0) ||
777 (strcmp(s, "export") == 0) ||
778 (strcmp(s, "readonly") == 0) ||
779 (strcmp(s, "typeset") == 0);
782 /* Check if we are in the middle of reading an alias */
783 static int
784 inalias(struct source *s)
786 for (; s && s->type == SALIAS; s = s->next)
787 if (!(s->flags & SF_ALIASEND))
788 return 1;
789 return 0;
792 /* Order important - indexed by Test_meta values
793 * Note that ||, &&, ( and ) can't appear in as unquoted strings
794 * in normal shell input, so these can be interpreted unambiguously
795 * in the evaluation pass.
797 static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
798 static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
799 static const char dbtest_not[] = { CHAR, '!', EOS };
800 static const char dbtest_oparen[] = { CHAR, '(', EOS };
801 static const char dbtest_cparen[] = { CHAR, ')', EOS };
802 const char *const dbtest_tokens[] = {
803 dbtest_or, dbtest_and, dbtest_not,
804 dbtest_oparen, dbtest_cparen
806 const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
807 const char db_lthan[] = { CHAR, '<', EOS };
808 const char db_gthan[] = { CHAR, '>', EOS };
810 /* Test if the current token is a whatever. Accepts the current token if
811 * it is. Returns 0 if it is not, non-zero if it is (in the case of
812 * TM_UNOP and TM_BINOP, the returned value is a Test_op).
814 static int
815 dbtestp_isa(Test_env *te, Test_meta meta)
817 int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
818 int uqword = 0;
819 char *save = (char *) 0;
820 int ret = 0;
822 /* unquoted word? */
823 uqword = c == LWORD && *ident;
825 if (meta == TM_OR)
826 ret = c == LOGOR;
827 else if (meta == TM_AND)
828 ret = c == LOGAND;
829 else if (meta == TM_NOT)
830 ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;
831 else if (meta == TM_OPAREN)
832 ret = c == '(' /*)*/;
833 else if (meta == TM_CPAREN)
834 ret = c == /*(*/ ')';
835 else if (meta == TM_UNOP || meta == TM_BINOP) {
836 if (meta == TM_BINOP && c == REDIR
837 && (yylval.iop->flag == IOREAD
838 || yylval.iop->flag == IOWRITE))
840 ret = 1;
841 save = wdcopy(yylval.iop->flag == IOREAD ?
842 db_lthan : db_gthan, ATEMP);
843 } else if (uqword && (ret = (int) test_isop(te, meta, ident)))
844 save = yylval.cp;
845 } else /* meta == TM_END */
846 ret = uqword && strcmp(yylval.cp, db_close) == 0;
847 if (ret) {
848 ACCEPT;
849 if (meta != TM_END) {
850 if (!save)
851 save = wdcopy(dbtest_tokens[(int) meta], ATEMP);
852 XPput(*te->pos.av, save);
855 return ret;
858 static const char *
859 dbtestp_getopnd(Test_env *te, Test_op op, int do_eval)
861 int c = tpeek(ARRAYVAR);
863 if (c != LWORD)
864 return (const char *) 0;
866 ACCEPT;
867 XPput(*te->pos.av, yylval.cp);
869 return null;
872 static int
873 dbtestp_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
874 int do_eval)
876 return 1;
879 static void
880 dbtestp_error(Test_env *te, int offset, const char *msg)
882 te->flags |= TEF_ERROR;
884 if (offset < 0) {
885 REJECT;
886 /* Kludgy to say the least... */
887 symbol = LWORD;
888 yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av)
889 + offset);
891 syntaxerr(msg);