test.c revision 1.5 1 1.1 glass /*-
2 1.1 glass * Copyright (c) 1992 The Regents of the University of California.
3 1.1 glass * All rights reserved.
4 1.1 glass *
5 1.1 glass * This code is derived from software contributed to Berkeley by
6 1.1 glass * Kenneth Almquist.
7 1.1 glass *
8 1.1 glass * Redistribution and use in source and binary forms, with or without
9 1.1 glass * modification, are permitted provided that the following conditions
10 1.1 glass * are met:
11 1.1 glass * 1. Redistributions of source code must retain the above copyright
12 1.1 glass * notice, this list of conditions and the following disclaimer.
13 1.1 glass * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 glass * notice, this list of conditions and the following disclaimer in the
15 1.1 glass * documentation and/or other materials provided with the distribution.
16 1.1 glass * 3. All advertising materials mentioning features or use of this software
17 1.1 glass * must display the following acknowledgement:
18 1.1 glass * This product includes software developed by the University of
19 1.1 glass * California, Berkeley and its contributors.
20 1.1 glass * 4. Neither the name of the University nor the names of its contributors
21 1.1 glass * may be used to endorse or promote products derived from this software
22 1.1 glass * without specific prior written permission.
23 1.1 glass *
24 1.1 glass * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 1.1 glass * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 glass * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 glass * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 1.1 glass * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 glass * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 glass * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 glass * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 glass * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 glass * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 glass * SUCH DAMAGE.
35 1.1 glass */
36 1.1 glass
37 1.1 glass #ifndef lint
38 1.1 glass char copyright[] =
39 1.1 glass "@(#) Copyright (c) 1992 The Regents of the University of California.\n\
40 1.1 glass All rights reserved.\n";
41 1.1 glass #endif /* not lint */
42 1.1 glass
43 1.1 glass #ifndef lint
44 1.3 mycroft /*static char sccsid[] = "from: @(#)test.c 5.4 (Berkeley) 2/12/93";*/
45 1.5 jtc static char rcsid[] = "$Id: test.c,v 1.5 1993/09/08 00:46:55 jtc Exp $";
46 1.1 glass #endif /* not lint */
47 1.1 glass
48 1.1 glass #include <sys/types.h>
49 1.1 glass #include <sys/stat.h>
50 1.1 glass #include <errno.h>
51 1.1 glass #include <stdlib.h>
52 1.1 glass #include <string.h>
53 1.1 glass #include <stdio.h>
54 1.1 glass
55 1.1 glass #include "operators.h"
56 1.1 glass
57 1.1 glass #define STACKSIZE 12
58 1.1 glass #define NESTINCR 16
59 1.1 glass
60 1.1 glass /* data types */
61 1.1 glass #define STRING 0
62 1.1 glass #define INTEGER 1
63 1.1 glass #define BOOLEAN 2
64 1.1 glass
65 1.1 glass #define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
66 1.1 glass
67 1.1 glass /*
68 1.1 glass * This structure hold a value. The type keyword specifies the type of
69 1.1 glass * the value, and the union u holds the value. The value of a boolean
70 1.1 glass * is stored in u.num (1 = TRUE, 0 = FALSE).
71 1.1 glass */
72 1.1 glass struct value {
73 1.1 glass int type;
74 1.1 glass union {
75 1.1 glass char *string;
76 1.1 glass long num;
77 1.1 glass } u;
78 1.1 glass };
79 1.1 glass
80 1.1 glass struct operator {
81 1.1 glass short op; /* Which operator. */
82 1.1 glass short pri; /* Priority of operator. */
83 1.1 glass };
84 1.1 glass
85 1.1 glass struct filestat {
86 1.1 glass char *name; /* Name of file. */
87 1.1 glass int rcode; /* Return code from stat. */
88 1.1 glass struct stat stat; /* Status info on file. */
89 1.1 glass };
90 1.1 glass
91 1.1 glass static void err __P((const char *, ...));
92 1.1 glass static int expr_is_false __P((struct value *));
93 1.1 glass static void expr_operator __P((int, struct value *, struct filestat *));
94 1.1 glass static int int_tcheck __P((char *));
95 1.1 glass static int lookup_op __P((char *, char *const *));
96 1.1 glass static void overflow __P((void));
97 1.1 glass static int posix_binary_op __P((char **));
98 1.1 glass static int posix_unary_op __P((char **));
99 1.1 glass static void syntax __P((void));
100 1.1 glass
101 1.1 glass int
102 1.1 glass main(argc, argv)
103 1.1 glass int argc;
104 1.1 glass char *argv[];
105 1.1 glass {
106 1.1 glass struct operator opstack[STACKSIZE];
107 1.1 glass struct operator *opsp;
108 1.1 glass struct value valstack[STACKSIZE + 1];
109 1.1 glass struct value *valsp;
110 1.1 glass struct filestat fs;
111 1.1 glass char c, **ap, *opname, *p;
112 1.1 glass int binary, nest, op, pri, ret_val, skipping;
113 1.1 glass
114 1.1 glass if ((p = argv[0]) == NULL) {
115 1.1 glass err("test: argc is zero.\n");
116 1.1 glass exit(2);
117 1.1 glass }
118 1.1 glass
119 1.1 glass if (*p != '\0' && p[strlen(p) - 1] == '[') {
120 1.1 glass if (strcmp(argv[--argc], "]"))
121 1.1 glass err("missing ]");
122 1.1 glass argv[argc] = NULL;
123 1.1 glass }
124 1.1 glass ap = argv + 1;
125 1.1 glass fs.name = NULL;
126 1.1 glass
127 1.1 glass /*
128 1.1 glass * Test(1) implements an inherently ambiguous grammer. In order to
129 1.1 glass * assure some degree of consistency, we special case the POSIX 1003.2
130 1.1 glass * requirements to assure correct evaluation for POSIX scripts. The
131 1.1 glass * following special cases comply with POSIX P1003.2/D11.2 Section
132 1.1 glass * 4.62.4.
133 1.1 glass */
134 1.1 glass switch(argc - 1) {
135 1.1 glass case 0: /* % test */
136 1.1 glass return (1);
137 1.1 glass break;
138 1.1 glass case 1: /* % test arg */
139 1.1 glass /* MIPS machine returns NULL of '[ ]' is called. */
140 1.1 glass return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0;
141 1.1 glass break;
142 1.1 glass case 2: /* % test op arg */
143 1.1 glass opname = argv[1];
144 1.1 glass if (IS_BANG(opname))
145 1.2 cgd return (*argv[2] == '\0') ? 0 : 1;
146 1.1 glass else {
147 1.1 glass ret_val = posix_unary_op(&argv[1]);
148 1.1 glass if (ret_val >= 0)
149 1.1 glass return (ret_val);
150 1.1 glass }
151 1.1 glass break;
152 1.1 glass case 3: /* % test arg1 op arg2 */
153 1.1 glass if (IS_BANG(argv[1])) {
154 1.1 glass ret_val = posix_unary_op(&argv[1]);
155 1.1 glass if (ret_val >= 0)
156 1.1 glass return (!ret_val);
157 1.1 glass } else {
158 1.1 glass ret_val = posix_binary_op(&argv[1]);
159 1.1 glass if (ret_val >= 0)
160 1.1 glass return (ret_val);
161 1.1 glass }
162 1.1 glass break;
163 1.1 glass case 4: /* % test ! arg1 op arg2 */
164 1.1 glass if (IS_BANG(argv[1])) {
165 1.1 glass ret_val = posix_binary_op(&argv[2]);
166 1.1 glass if (ret_val >= 0)
167 1.1 glass return (!ret_val);
168 1.1 glass }
169 1.1 glass break;
170 1.1 glass default:
171 1.1 glass break;
172 1.1 glass }
173 1.1 glass
174 1.1 glass /*
175 1.1 glass * We use operator precedence parsing, evaluating the expression as
176 1.1 glass * we parse it. Parentheses are handled by bumping up the priority
177 1.1 glass * of operators using the variable "nest." We use the variable
178 1.1 glass * "skipping" to turn off evaluation temporarily for the short
179 1.1 glass * circuit boolean operators. (It is important do the short circuit
180 1.1 glass * evaluation because under NFS a stat operation can take infinitely
181 1.1 glass * long.)
182 1.1 glass */
183 1.1 glass opsp = opstack + STACKSIZE;
184 1.1 glass valsp = valstack;
185 1.1 glass nest = skipping = 0;
186 1.1 glass if (*ap == NULL) {
187 1.1 glass valstack[0].type = BOOLEAN;
188 1.1 glass valstack[0].u.num = 0;
189 1.1 glass goto done;
190 1.1 glass }
191 1.1 glass for (;;) {
192 1.1 glass opname = *ap++;
193 1.1 glass if (opname == NULL)
194 1.1 glass syntax();
195 1.1 glass if (opname[0] == '(' && opname[1] == '\0') {
196 1.1 glass nest += NESTINCR;
197 1.1 glass continue;
198 1.1 glass } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
199 1.1 glass if (opsp == &opstack[0])
200 1.1 glass overflow();
201 1.1 glass --opsp;
202 1.1 glass opsp->op = op;
203 1.1 glass opsp->pri = op_priority[op] + nest;
204 1.1 glass continue;
205 1.1 glass } else {
206 1.1 glass valsp->type = STRING;
207 1.1 glass valsp->u.string = opname;
208 1.1 glass valsp++;
209 1.1 glass }
210 1.1 glass for (;;) {
211 1.1 glass opname = *ap++;
212 1.1 glass if (opname == NULL) {
213 1.1 glass if (nest != 0)
214 1.1 glass syntax();
215 1.1 glass pri = 0;
216 1.1 glass break;
217 1.1 glass }
218 1.1 glass if (opname[0] != ')' || opname[1] != '\0') {
219 1.1 glass if ((op = lookup_op(opname, binary_op)) < 0)
220 1.1 glass syntax();
221 1.1 glass op += FIRST_BINARY_OP;
222 1.1 glass pri = op_priority[op] + nest;
223 1.1 glass break;
224 1.1 glass }
225 1.1 glass if ((nest -= NESTINCR) < 0)
226 1.1 glass syntax();
227 1.1 glass }
228 1.1 glass while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
229 1.1 glass binary = opsp->op;
230 1.1 glass for (;;) {
231 1.1 glass valsp--;
232 1.1 glass c = op_argflag[opsp->op];
233 1.1 glass if (c == OP_INT) {
234 1.1 glass if (valsp->type == STRING &&
235 1.1 glass int_tcheck(valsp->u.string))
236 1.1 glass valsp->u.num =
237 1.1 glass atol(valsp->u.string);
238 1.1 glass valsp->type = INTEGER;
239 1.1 glass } else if (c >= OP_STRING) {
240 1.1 glass /* OP_STRING or OP_FILE */
241 1.1 glass if (valsp->type == INTEGER) {
242 1.1 glass if ((p = malloc(32)) == NULL)
243 1.1 glass err("%s",
244 1.1 glass strerror(errno));
245 1.1 glass #ifdef SHELL
246 1.1 glass fmtstr(p, 32, "%d",
247 1.1 glass valsp->u.num);
248 1.1 glass #else
249 1.1 glass (void)sprintf(p,
250 1.1 glass "%d", valsp->u.num);
251 1.1 glass #endif
252 1.1 glass valsp->u.string = p;
253 1.1 glass } else if (valsp->type == BOOLEAN) {
254 1.1 glass if (valsp->u.num)
255 1.1 glass valsp->u.string =
256 1.1 glass "true";
257 1.1 glass else
258 1.1 glass valsp->u.string = "";
259 1.1 glass }
260 1.1 glass valsp->type = STRING;
261 1.1 glass if (c == OP_FILE && (fs.name == NULL ||
262 1.1 glass strcmp(fs.name, valsp->u.string))) {
263 1.1 glass fs.name = valsp->u.string;
264 1.1 glass fs.rcode =
265 1.1 glass stat(valsp->u.string,
266 1.1 glass &fs.stat);
267 1.1 glass }
268 1.1 glass }
269 1.1 glass if (binary < FIRST_BINARY_OP)
270 1.1 glass break;
271 1.1 glass binary = 0;
272 1.1 glass }
273 1.1 glass if (!skipping)
274 1.1 glass expr_operator(opsp->op, valsp, &fs);
275 1.1 glass else if (opsp->op == AND1 || opsp->op == OR1)
276 1.1 glass skipping--;
277 1.1 glass valsp++; /* push value */
278 1.1 glass opsp++; /* pop operator */
279 1.1 glass }
280 1.1 glass if (opname == NULL)
281 1.1 glass break;
282 1.1 glass if (opsp == &opstack[0])
283 1.1 glass overflow();
284 1.1 glass if (op == AND1 || op == AND2) {
285 1.1 glass op = AND1;
286 1.1 glass if (skipping || expr_is_false(valsp - 1))
287 1.1 glass skipping++;
288 1.1 glass }
289 1.1 glass if (op == OR1 || op == OR2) {
290 1.1 glass op = OR1;
291 1.1 glass if (skipping || !expr_is_false(valsp - 1))
292 1.1 glass skipping++;
293 1.1 glass }
294 1.1 glass opsp--;
295 1.1 glass opsp->op = op;
296 1.1 glass opsp->pri = pri;
297 1.1 glass }
298 1.1 glass done: return (expr_is_false(&valstack[0]));
299 1.1 glass }
300 1.1 glass
301 1.1 glass static int
302 1.1 glass expr_is_false(val)
303 1.1 glass struct value *val;
304 1.1 glass {
305 1.1 glass if (val->type == STRING) {
306 1.1 glass if (val->u.string[0] == '\0')
307 1.1 glass return (1);
308 1.1 glass } else { /* INTEGER or BOOLEAN */
309 1.1 glass if (val->u.num == 0)
310 1.1 glass return (1);
311 1.1 glass }
312 1.1 glass return (0);
313 1.1 glass }
314 1.1 glass
315 1.1 glass
316 1.1 glass /*
317 1.1 glass * Execute an operator. Op is the operator. Sp is the stack pointer;
318 1.1 glass * sp[0] refers to the first operand, sp[1] refers to the second operand
319 1.1 glass * (if any), and the result is placed in sp[0]. The operands are converted
320 1.1 glass * to the type expected by the operator before expr_operator is called.
321 1.1 glass * Fs is a pointer to a structure which holds the value of the last call
322 1.1 glass * to stat, to avoid repeated stat calls on the same file.
323 1.1 glass */
324 1.1 glass static void
325 1.1 glass expr_operator(op, sp, fs)
326 1.1 glass int op;
327 1.1 glass struct value *sp;
328 1.1 glass struct filestat *fs;
329 1.1 glass {
330 1.1 glass int i;
331 1.1 glass
332 1.1 glass switch (op) {
333 1.1 glass case NOT:
334 1.1 glass sp->u.num = expr_is_false(sp);
335 1.1 glass sp->type = BOOLEAN;
336 1.1 glass break;
337 1.1 glass case ISEXIST:
338 1.1 glass if (fs == NULL || fs->rcode == -1)
339 1.1 glass goto false;
340 1.1 glass else
341 1.1 glass goto true;
342 1.1 glass case ISREAD:
343 1.1 glass i = S_IROTH;
344 1.1 glass goto permission;
345 1.1 glass case ISWRITE:
346 1.1 glass i = S_IWOTH;
347 1.1 glass goto permission;
348 1.1 glass case ISEXEC:
349 1.1 glass i = S_IXOTH;
350 1.1 glass permission: if (fs->stat.st_uid == geteuid())
351 1.1 glass i <<= 6;
352 1.1 glass else if (fs->stat.st_gid == getegid())
353 1.1 glass i <<= 3;
354 1.1 glass goto filebit; /* true if (stat.st_mode & i) != 0 */
355 1.1 glass case ISFILE:
356 1.1 glass i = S_IFREG;
357 1.1 glass goto filetype;
358 1.1 glass case ISDIR:
359 1.1 glass i = S_IFDIR;
360 1.1 glass goto filetype;
361 1.1 glass case ISCHAR:
362 1.1 glass i = S_IFCHR;
363 1.1 glass goto filetype;
364 1.1 glass case ISBLOCK:
365 1.1 glass i = S_IFBLK;
366 1.1 glass goto filetype;
367 1.1 glass case ISFIFO:
368 1.1 glass i = S_IFIFO;
369 1.1 glass goto filetype;
370 1.1 glass filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
371 1.1 glass true: sp->u.num = 1;
372 1.1 glass else
373 1.1 glass false: sp->u.num = 0;
374 1.1 glass sp->type = BOOLEAN;
375 1.1 glass break;
376 1.1 glass case ISSETUID:
377 1.1 glass i = S_ISUID;
378 1.1 glass goto filebit;
379 1.1 glass case ISSETGID:
380 1.1 glass i = S_ISGID;
381 1.1 glass goto filebit;
382 1.1 glass case ISSTICKY:
383 1.1 glass i = S_ISVTX;
384 1.1 glass filebit: if (fs->stat.st_mode & i && fs->rcode >= 0)
385 1.1 glass goto true;
386 1.1 glass goto false;
387 1.1 glass case ISSIZE:
388 1.1 glass sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
389 1.1 glass sp->type = INTEGER;
390 1.1 glass break;
391 1.1 glass case ISTTY:
392 1.1 glass sp->u.num = isatty(sp->u.num);
393 1.1 glass sp->type = BOOLEAN;
394 1.1 glass break;
395 1.1 glass case NULSTR:
396 1.1 glass if (sp->u.string[0] == '\0')
397 1.1 glass goto true;
398 1.1 glass goto false;
399 1.1 glass case STRLEN:
400 1.1 glass sp->u.num = strlen(sp->u.string);
401 1.1 glass sp->type = INTEGER;
402 1.1 glass break;
403 1.1 glass case OR1:
404 1.1 glass case AND1:
405 1.1 glass /*
406 1.1 glass * These operators are mostly handled by the parser. If we
407 1.1 glass * get here it means that both operands were evaluated, so
408 1.1 glass * the value is the value of the second operand.
409 1.1 glass */
410 1.1 glass *sp = *(sp + 1);
411 1.1 glass break;
412 1.1 glass case STREQ:
413 1.1 glass case STRNE:
414 1.1 glass i = 0;
415 1.1 glass if (!strcmp(sp->u.string, (sp + 1)->u.string))
416 1.1 glass i++;
417 1.1 glass if (op == STRNE)
418 1.1 glass i = 1 - i;
419 1.1 glass sp->u.num = i;
420 1.1 glass sp->type = BOOLEAN;
421 1.1 glass break;
422 1.1 glass case EQ:
423 1.1 glass if (sp->u.num == (sp + 1)->u.num)
424 1.1 glass goto true;
425 1.1 glass goto false;
426 1.1 glass case NE:
427 1.1 glass if (sp->u.num != (sp + 1)->u.num)
428 1.1 glass goto true;
429 1.1 glass goto false;
430 1.1 glass case GT:
431 1.1 glass if (sp->u.num > (sp + 1)->u.num)
432 1.1 glass goto true;
433 1.1 glass goto false;
434 1.1 glass case LT:
435 1.1 glass if (sp->u.num < (sp + 1)->u.num)
436 1.1 glass goto true;
437 1.1 glass goto false;
438 1.1 glass case LE:
439 1.1 glass if (sp->u.num <= (sp + 1)->u.num)
440 1.1 glass goto true;
441 1.1 glass goto false;
442 1.1 glass case GE:
443 1.1 glass if (sp->u.num >= (sp + 1)->u.num)
444 1.1 glass goto true;
445 1.1 glass goto false;
446 1.1 glass
447 1.1 glass }
448 1.1 glass }
449 1.1 glass
450 1.1 glass static int
451 1.1 glass lookup_op(name, table)
452 1.1 glass char *name;
453 1.1 glass char *const * table;
454 1.1 glass {
455 1.1 glass register char *const * tp;
456 1.1 glass register char const *p;
457 1.1 glass char c;
458 1.1 glass
459 1.1 glass c = name[1];
460 1.1 glass for (tp = table; (p = *tp) != NULL; tp++)
461 1.1 glass if (p[1] == c && !strcmp(p, name))
462 1.1 glass return (tp - table);
463 1.1 glass return (-1);
464 1.1 glass }
465 1.1 glass
466 1.1 glass static int
467 1.1 glass posix_unary_op(argv)
468 1.1 glass char **argv;
469 1.1 glass {
470 1.1 glass struct filestat fs;
471 1.1 glass struct value valp;
472 1.1 glass int op, c;
473 1.1 glass char *opname;
474 1.1 glass
475 1.1 glass opname = *argv;
476 1.1 glass if ((op = lookup_op(opname, unary_op)) < 0)
477 1.1 glass return (-1);
478 1.1 glass c = op_argflag[op];
479 1.1 glass opname = argv[1];
480 1.1 glass valp.u.string = opname;
481 1.1 glass if (c == OP_FILE) {
482 1.1 glass fs.name = opname;
483 1.1 glass fs.rcode = stat(opname, &fs.stat);
484 1.1 glass } else if (c != OP_STRING)
485 1.1 glass return (-1);
486 1.1 glass
487 1.1 glass expr_operator(op, &valp, &fs);
488 1.1 glass return (valp.u.num == 0);
489 1.1 glass }
490 1.1 glass
491 1.1 glass static int
492 1.1 glass posix_binary_op(argv)
493 1.1 glass char **argv;
494 1.1 glass {
495 1.1 glass struct value v[2];
496 1.1 glass int op, c;
497 1.1 glass char *opname;
498 1.1 glass
499 1.1 glass opname = argv[1];
500 1.1 glass if ((op = lookup_op(opname, binary_op)) < 0)
501 1.1 glass return (-1);
502 1.1 glass op += FIRST_BINARY_OP;
503 1.1 glass c = op_argflag[op];
504 1.1 glass
505 1.1 glass if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) {
506 1.1 glass v[0].u.num = atol(argv[0]);
507 1.1 glass v[1].u.num = atol(argv[2]);
508 1.1 glass } else {
509 1.1 glass v[0].u.string = argv[0];
510 1.1 glass v[1].u.string = argv[2];
511 1.1 glass }
512 1.1 glass expr_operator(op, v, NULL);
513 1.1 glass return (v[0].u.num == 0);
514 1.1 glass }
515 1.1 glass
516 1.1 glass /*
517 1.1 glass * Integer type checking.
518 1.1 glass */
519 1.1 glass static int
520 1.1 glass int_tcheck(v)
521 1.1 glass char *v;
522 1.1 glass {
523 1.5 jtc char *p = v;;
524 1.1 glass
525 1.5 jtc while (isspace(*p))
526 1.5 jtc p++;
527 1.5 jtc
528 1.5 jtc if (*p == '-' || *p == '+')
529 1.5 jtc p++;
530 1.5 jtc
531 1.5 jtc while (*p) {
532 1.5 jtc if (!isdigit(*p))
533 1.5 jtc errx(2, "illegal operand \"%s\" -- expected integer.", v);
534 1.5 jtc p++;
535 1.5 jtc }
536 1.5 jtc
537 1.1 glass return (1);
538 1.1 glass }
539 1.1 glass
540 1.1 glass static void
541 1.1 glass syntax()
542 1.1 glass {
543 1.1 glass err("syntax error");
544 1.1 glass }
545 1.1 glass
546 1.1 glass static void
547 1.1 glass overflow()
548 1.1 glass {
549 1.1 glass err("expression is too complex");
550 1.1 glass }
551 1.1 glass
552 1.1 glass #if __STDC__
553 1.1 glass #include <stdarg.h>
554 1.1 glass #else
555 1.1 glass #include <varargs.h>
556 1.1 glass #endif
557 1.1 glass
558 1.1 glass void
559 1.1 glass #if __STDC__
560 1.1 glass err(const char *fmt, ...)
561 1.1 glass #else
562 1.1 glass err(fmt, va_alist)
563 1.1 glass char *fmt;
564 1.1 glass va_dcl
565 1.1 glass #endif
566 1.1 glass {
567 1.1 glass va_list ap;
568 1.1 glass #if __STDC__
569 1.1 glass va_start(ap, fmt);
570 1.1 glass #else
571 1.1 glass va_start(ap);
572 1.1 glass #endif
573 1.1 glass (void)fprintf(stderr, "test: ");
574 1.1 glass (void)vfprintf(stderr, fmt, ap);
575 1.1 glass va_end(ap);
576 1.1 glass (void)fprintf(stderr, "\n");
577 1.1 glass exit(2);
578 1.1 glass /* NOTREACHED */
579 1.1 glass }
580