Commit | Line | Data |
---|---|---|
9146ff61 WJ |
1 | /* |
2 | * GNU test program (ksb and mjb) | |
3 | * | |
4 | * Modified to run with the GNU shell Apr 25, 1988 by bfox. | |
5 | * | |
6 | *??? -G file is owned by same gid; the effective gid is checked | |
7 | * Chet Ramey, CWRU 3/23/89 | |
8 | * | |
9 | * Fixed a BSD dependency (_doprnt()) in the port to AIX 2.2 | |
10 | * Chet Ramey, CWRU 5/3/89 | |
11 | */ | |
12 | ||
13 | /* Copyright (C) 1987,1989 Free Software Foundation, Inc. | |
14 | ||
15 | This file is part of GNU Bash, the Bourne Again SHell. | |
16 | ||
17 | Bash is free software; you can redistribute it and/or modify it under | |
18 | the terms of the GNU General Public License as published by the Free | |
19 | Software Foundation; either version 1, or (at your option) any later | |
20 | version. | |
21 | ||
22 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
23 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
24 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
25 | for more details. | |
26 | ||
27 | You should have received a copy of the GNU General Public License along | |
28 | with Bash; see the file COPYING. If not, write to the Free Software | |
29 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
30 | ||
31 | #include <stdio.h> | |
32 | #include <varargs.h> | |
33 | ||
34 | #ifndef SONY | |
35 | #include <fcntl.h> | |
36 | #endif | |
37 | ||
38 | #include <sys/types.h> | |
39 | #include <sys/stat.h> | |
40 | #include <sys/file.h> | |
41 | ||
42 | #ifndef R_OK | |
43 | #define R_OK 4 | |
44 | #define W_OK 2 | |
45 | #define X_OK 1 | |
46 | #define F_OK 0 | |
47 | #endif | |
48 | ||
49 | #ifndef lint | |
50 | static char *rcsid = "$Id: gtest.c,v 1.10 88/07/02 13:34:45 afb Exp Locker: afb $"; | |
51 | #endif | |
52 | ||
53 | /* The following few defines control the truth and false output of each stage. | |
54 | TRUE and FALSE are what we use to compute the final output value. | |
55 | SHELL_BOOLEAN is the form which returns truth or falseness in shell terms. | |
56 | TRUTH_OR is how to do logical or with TRUE and FALSE. | |
57 | TRUTH_AND is how to do logical and with TRUE and FALSE.. | |
58 | Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b, | |
59 | SHELL_BOOLEAN = (!value). */ | |
60 | #define TRUE 1 | |
61 | #define FALSE 0 | |
62 | #define SHELL_BOOLEAN(value) (!(value)) | |
63 | #define TRUTH_OR(a, b) ((a) | (b)) | |
64 | #define TRUTH_AND(a, b) ((a) & (b)) | |
65 | ||
66 | ||
67 | /* Define STANDALONE to get the /bin/test version. Otherwise, we are | |
68 | making this for the shell. */ | |
69 | /* #define STANDALONE */ | |
70 | ||
71 | #ifdef STANDALONE | |
72 | #define test_exit(val) exit (val) | |
73 | #else | |
74 | #include <setjmp.h> | |
75 | jmp_buf test_exit_buf; | |
76 | int test_error_return = 0; | |
77 | #define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1) | |
78 | #endif /* STANDALONE */ | |
79 | ||
80 | #ifdef SYSV | |
81 | int sys_v = 1; | |
82 | #else | |
83 | int sys_v = 0; | |
84 | #endif | |
85 | ||
86 | static int pos; /* position in list */ | |
87 | static int argc; /* number of args from command line */ | |
88 | static char **argv; /* the argument list */ | |
89 | ||
90 | static void | |
91 | test_syntax_error (format, arg) | |
92 | char *format, *arg; | |
93 | { | |
94 | (void) fprintf (stderr, "%s: ", argv[0]); | |
95 | (void) fprintf (stderr, format, arg); | |
96 | test_exit (SHELL_BOOLEAN (FALSE)); | |
97 | } | |
98 | ||
99 | test_io_error (name) | |
100 | char *name; | |
101 | { | |
102 | extern int errno; | |
103 | int old_errno = errno; | |
104 | fprintf (stderr, "%s: ", argv[0]); | |
105 | errno = old_errno; | |
106 | perror (name); | |
107 | test_exit (SHELL_BOOLEAN (FALSE)); | |
108 | } | |
109 | ||
110 | /* | |
111 | * advance - increment our position in the argument list. Check that | |
112 | * we're not past the end of the argument list. This check is | |
113 | * supressed if the argument is FALSE. made a macro for efficiency. | |
114 | */ | |
115 | #ifndef lint | |
116 | #define advance(f) (++pos, f && (pos < argc ? 0 : beyond())) | |
117 | #endif | |
118 | ||
119 | #if !defined(advance) | |
120 | static int | |
121 | advance (f) | |
122 | int f; | |
123 | { | |
124 | ++pos; | |
125 | if (f && pos >= argc) | |
126 | beyond (); | |
127 | } | |
128 | ||
129 | #endif | |
130 | ||
131 | #define unary_advance() (advance (1),++pos) | |
132 | ||
133 | /* | |
134 | * beyond - call when we're beyond the end of the argument list (an | |
135 | * error condition) | |
136 | */ | |
137 | static int | |
138 | beyond () | |
139 | { | |
140 | test_syntax_error ("argument expected\n", (char *)NULL); | |
141 | } | |
142 | ||
143 | /* | |
144 | * int_expt_err - when an integer argument was expected, but something else | |
145 | * was found. | |
146 | */ | |
147 | static void | |
148 | int_expt_err (pch) | |
149 | char *pch; | |
150 | { | |
151 | test_syntax_error ("integer expression expected %s\n", pch); | |
152 | } | |
153 | ||
154 | /* | |
155 | * isint - is the argument whose position in the argument vector is 'm' an | |
156 | * integer? Convert it for me too, returning it's value in 'pl'. | |
157 | */ | |
158 | static int | |
159 | isint (m, pl) | |
160 | int m; | |
161 | long *pl; | |
162 | { | |
163 | extern long atol (); | |
164 | register char *pch; | |
165 | ||
166 | pch = argv[m]; | |
167 | ||
168 | /* Skip leading whitespace characters. */ | |
169 | while (*pch == '\t' || *pch == ' ') | |
170 | pch++; | |
171 | ||
172 | /* accept negative numbers but not '-' alone */ | |
173 | if ('-' == *pch) | |
174 | if ('\000' == *++pch) | |
175 | return 0; | |
176 | ||
177 | while ('\000' != *pch) | |
178 | { | |
179 | switch (*pch) | |
180 | { | |
181 | case '0': | |
182 | case '1': | |
183 | case '2': | |
184 | case '3': | |
185 | case '4': | |
186 | case '5': | |
187 | case '6': | |
188 | case '7': | |
189 | case '8': | |
190 | case '9': | |
191 | break; | |
192 | default: | |
193 | return (FALSE); | |
194 | } | |
195 | ++pch; | |
196 | } | |
197 | *pl = atol (argv[m]); | |
198 | return (TRUE); | |
199 | } | |
200 | ||
201 | /* | |
202 | * age_of - find the age of the given file. Return YES or NO depending | |
203 | * on whether the file exists, and if it does, fill in the age with | |
204 | * the modify time. | |
205 | */ | |
206 | static int | |
207 | age_of (posit, age) | |
208 | int posit; | |
209 | long *age; | |
210 | { | |
211 | struct stat stat_buf; | |
212 | ||
213 | if (stat (argv[posit], &stat_buf) < 0) | |
214 | { | |
215 | return (FALSE); | |
216 | } | |
217 | *age = stat_buf.st_mtime; | |
218 | return (TRUE); | |
219 | } | |
220 | ||
221 | /* | |
222 | * term - parse a term and return 1 or 0 depending on whether the term | |
223 | * evaluates to true or false, respectively. | |
224 | * | |
225 | * term ::= | |
226 | * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename | |
227 | * '-'('L'|'x') filename | |
228 | * '-t' [ int ] | |
229 | * '-'('z'|'n') string | |
230 | * string | |
231 | * string ('!='|'=') string | |
232 | * <int> '-'(eq|ne|le|lt|ge|gt) <int> | |
233 | * file '-'(nt|ot|ef) file | |
234 | * '(' <expr> ')' | |
235 | * int ::= | |
236 | * '-l' string | |
237 | * positive and negative integers | |
238 | */ | |
239 | static int | |
240 | term () | |
241 | { | |
242 | int expr (); | |
243 | auto struct stat stat_buf, stat_spare; | |
244 | auto long int l, r; | |
245 | auto int l_is_l, r_is_l; /* are the left and right integer | |
246 | * expressions of the form '-l string'? | |
247 | */ | |
248 | auto int value, fd; | |
249 | ||
250 | if (pos >= argc) | |
251 | beyond (); | |
252 | ||
253 | /* Deal with leading "not". */ | |
254 | if (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1]) | |
255 | { | |
256 | advance (1); | |
257 | ||
258 | /* This has to be rewritten to handle the TRUTH and FALSE stuff. */ | |
259 | return (!term ()); | |
260 | } | |
261 | ||
262 | if ('(' == argv[pos][0] && '\000' == argv[pos][1]) | |
263 | { | |
264 | advance (1); | |
265 | value = expr (); | |
266 | if (')' != argv[pos][0] || '\000' != argv[pos][1]) | |
267 | test_syntax_error ("argument expected, found %s\n", argv[pos]); | |
268 | advance (0); | |
269 | return (TRUE == (value)); | |
270 | } | |
271 | /* are there enough arguments left that this could be dyadic? */ | |
272 | if (pos + 3 <= argc) | |
273 | { | |
274 | register int op; | |
275 | if ('-' == argv[pos][0] && 'l' == argv[pos][1] && | |
276 | '\000' == argv[pos][2]) | |
277 | { | |
278 | l_is_l = 1; | |
279 | op = pos + 2; | |
280 | advance (0); | |
281 | } | |
282 | else | |
283 | { | |
284 | l_is_l = 0; | |
285 | op = pos + 1; | |
286 | } | |
287 | if ('-' == argv[op + 1][0] && 'l' == argv[op + 1][1] | |
288 | && '\000' == argv[op + 1][2]) | |
289 | { | |
290 | r_is_l = 1; | |
291 | advance (0); | |
292 | } | |
293 | else | |
294 | r_is_l = 0; | |
295 | ||
296 | if ('-' == argv[op][0]) | |
297 | { | |
298 | /* check for eq, nt, and stuff */ | |
299 | switch (argv[op][1]) | |
300 | { | |
301 | default: | |
302 | break; | |
303 | case 'l': | |
304 | if ('t' == argv[op][2] && '\000' == argv[op][3]) | |
305 | { | |
306 | /* lt */ | |
307 | if (l_is_l) | |
308 | l = strlen (argv[op - 1]); | |
309 | else | |
310 | { | |
311 | if (!isint (op - 1, &l)) | |
312 | int_expt_err ("before -lt"); | |
313 | } | |
314 | ||
315 | if (r_is_l) | |
316 | r = strlen (argv[op + 2]); | |
317 | else | |
318 | { | |
319 | if (!isint (op + 1, &r)) | |
320 | int_expt_err ("after -lt"); | |
321 | } | |
322 | pos += 3; | |
323 | return (TRUE == (l < r)); | |
324 | } | |
325 | ||
326 | if ('e' == argv[op][2] && '\000' == argv[op][3]) | |
327 | { | |
328 | /* le */ | |
329 | if (l_is_l) | |
330 | l = strlen (argv[op - 1]); | |
331 | else | |
332 | { | |
333 | if (!isint (op - 1, &l)) | |
334 | int_expt_err ("before -le"); | |
335 | } | |
336 | if (r_is_l) | |
337 | r = strlen (argv[op + 2]); | |
338 | else | |
339 | { | |
340 | if (!isint (op + 1, &r)) | |
341 | int_expt_err ("after -le"); | |
342 | } | |
343 | pos += 3; | |
344 | return (TRUE == (l <= r)); | |
345 | } | |
346 | break; | |
347 | ||
348 | case 'g': | |
349 | ||
350 | if ('t' == argv[op][2] && '\000' == argv[op][3]) | |
351 | { | |
352 | /* gt integer greater than */ | |
353 | if (l_is_l) | |
354 | l = strlen (argv[op - 1]); | |
355 | else | |
356 | { | |
357 | if (!isint (op - 1, &l)) | |
358 | int_expt_err ("before -gt"); | |
359 | } | |
360 | if (r_is_l) | |
361 | r = strlen (argv[op + 2]); | |
362 | else | |
363 | { | |
364 | if (!isint (op + 1, &r)) | |
365 | int_expt_err ("after -gt"); | |
366 | } | |
367 | pos += 3; | |
368 | return (TRUE == (l > r)); | |
369 | } | |
370 | ||
371 | if ('e' == argv[op][2] && '\000' == argv[op][3]) | |
372 | { | |
373 | /* ge - integer greater than or equal to */ | |
374 | if (l_is_l) | |
375 | l = strlen (argv[op - 1]); | |
376 | else | |
377 | { | |
378 | if (!isint (op - 1, &l)) | |
379 | int_expt_err ("before -ge"); | |
380 | } | |
381 | if (r_is_l) | |
382 | r = strlen (argv[op + 2]); | |
383 | else | |
384 | { | |
385 | if (!isint (op + 1, &r)) | |
386 | int_expt_err ("after -ge"); | |
387 | } | |
388 | pos += 3; | |
389 | return (TRUE == (l >= r)); | |
390 | } | |
391 | break; | |
392 | ||
393 | case 'n': | |
394 | if ('t' == argv[op][2] && '\000' == argv[op][3]) | |
395 | { | |
396 | /* nt - newer than */ | |
397 | pos += 3; | |
398 | if (l_is_l || r_is_l) | |
399 | test_syntax_error ("-nt does not accept -l\n", | |
400 | (char *)NULL); | |
401 | if (age_of (op - 1, &l) && age_of (op + 1, &r)) | |
402 | return (TRUE == (l > r)); | |
403 | else | |
404 | return (FALSE); | |
405 | } | |
406 | ||
407 | if ('e' == argv[op][2] && '\000' == argv[op][3]) | |
408 | { | |
409 | /* ne - integer not equal */ | |
410 | if (l_is_l) | |
411 | l = strlen (argv[op - 1]); | |
412 | else | |
413 | { | |
414 | if (!isint (op - 1, &l)) | |
415 | int_expt_err ("before -ne"); | |
416 | } | |
417 | if (r_is_l) | |
418 | r = strlen (argv[op + 2]); | |
419 | else | |
420 | { | |
421 | if (!isint (op + 1, &r)) | |
422 | int_expt_err ("after -ne"); | |
423 | } | |
424 | pos += 3; | |
425 | return (TRUE == (l != r)); | |
426 | } | |
427 | break; | |
428 | ||
429 | case 'e': | |
430 | if ('q' == argv[op][2] && '\000' == argv[op][3]) | |
431 | { | |
432 | /* eq - integer equal */ | |
433 | if (l_is_l) | |
434 | l = strlen (argv[op - 1]); | |
435 | else | |
436 | { | |
437 | if (!isint (op - 1, &l)) | |
438 | int_expt_err ("before -eq"); | |
439 | } | |
440 | if (r_is_l) | |
441 | r = strlen (argv[op + 2]); | |
442 | else | |
443 | { | |
444 | if (!isint (op + 1, &r)) | |
445 | int_expt_err ("after -eq"); | |
446 | } | |
447 | pos += 3; | |
448 | return (TRUE == (l == r)); | |
449 | } | |
450 | ||
451 | if ('f' == argv[op][2] && '\000' == argv[op][3]) | |
452 | { | |
453 | /* ef - hard link? */ | |
454 | pos += 3; | |
455 | if (l_is_l || r_is_l) | |
456 | test_syntax_error ("-ef does not accept -l\n", | |
457 | (char *)NULL); | |
458 | if (stat (argv[op - 1], &stat_buf) < 0) | |
459 | return (FALSE); | |
460 | if (stat (argv[op + 1], &stat_spare) < 0) | |
461 | return (FALSE); | |
462 | return (TRUE == | |
463 | (stat_buf.st_dev == stat_spare.st_dev && | |
464 | stat_buf.st_ino == stat_spare.st_ino)); | |
465 | } | |
466 | break; | |
467 | ||
468 | case 'o': | |
469 | if ('t' == argv[op][2] && '\000' == argv[op][3]) | |
470 | { | |
471 | /* ot - older than */ | |
472 | pos += 3; | |
473 | if (l_is_l || r_is_l) | |
474 | test_syntax_error ("-nt does not accept -l\n", | |
475 | (char *)NULL); | |
476 | if (age_of (op - 1, &l) && age_of (op + 1, &r)) | |
477 | return (TRUE == (l < r)); | |
478 | return (FALSE); | |
479 | } | |
480 | break; | |
481 | } | |
482 | } | |
483 | ||
484 | if ('=' == argv[op][0] && '\000' == argv[op][1]) | |
485 | { | |
486 | value = (0 == strcmp (argv[pos], argv[pos + 2])); | |
487 | pos += 3; | |
488 | return (TRUE == value); | |
489 | } | |
490 | if ('!' == argv[op][0] && '=' == argv[op][1] && '\000' == argv[op][2]) | |
491 | { | |
492 | value = 0 != strcmp (argv[pos], argv[pos + 2]); | |
493 | pos += 3; | |
494 | return (TRUE == value); | |
495 | } | |
496 | } | |
497 | ||
498 | /* Might be a switch type argument */ | |
499 | if ('-' == argv[pos][0] && '\000' == argv[pos][2] /* && pos < argc-1 */ ) | |
500 | { | |
501 | switch (argv[pos][1]) | |
502 | { | |
503 | default: | |
504 | break; | |
505 | ||
506 | /* All of the following unary operators use unary_advance (), which | |
507 | checks to make sure that there is an argument, and then advances | |
508 | pos right past it. This means that pos - 1 is the location of the | |
509 | argument. */ | |
510 | ||
511 | case 'r': /* file is readable? */ | |
512 | unary_advance (); | |
513 | value = -1 != access (argv[pos - 1], R_OK); | |
514 | return (TRUE == value); | |
515 | ||
516 | case 'w': /* File is writeable? */ | |
517 | unary_advance (); | |
518 | value = -1 != access (argv[pos - 1], W_OK); | |
519 | return (TRUE == value); | |
520 | ||
521 | case 'x': /* File is executable? */ | |
522 | unary_advance (); | |
523 | value = -1 != access (argv[pos - 1], X_OK); | |
524 | return (TRUE == value); | |
525 | ||
526 | case 'O': /* File is owned by you? */ | |
527 | unary_advance (); | |
528 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
529 | return (FALSE); | |
530 | ||
531 | return (TRUE == (geteuid () == stat_buf.st_uid)); | |
532 | ||
533 | case 'f': /* File is a file? */ | |
534 | unary_advance (); | |
535 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
536 | return (FALSE); | |
537 | ||
538 | /* | |
539 | * Under SYSV, -f is true if the given file exists | |
540 | * and is a regular file. Other places, this checks | |
541 | * to see if the given file is not a directory. | |
542 | */ | |
543 | if (sys_v) | |
544 | return (TRUE == ((S_IFREG == (stat_buf.st_mode & S_IFMT)) || | |
545 | (0 == (stat_buf.st_mode & S_IFMT)))); | |
546 | else | |
547 | return (TRUE == (S_IFDIR != (stat_buf.st_mode & S_IFMT))); | |
548 | ||
549 | case 'd': /* File is a directory? */ | |
550 | unary_advance (); | |
551 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
552 | return (FALSE); | |
553 | ||
554 | return (TRUE == (S_IFDIR == (stat_buf.st_mode & S_IFMT))); | |
555 | ||
556 | case 's': /* File has something in it? */ | |
557 | unary_advance (); | |
558 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
559 | return (FALSE); | |
560 | ||
561 | return (TRUE == (stat_buf.st_size > (off_t) 0)); | |
562 | ||
563 | #ifdef S_IFSOCK | |
564 | case 'S': /* File is a socket? */ | |
565 | unary_advance (); | |
566 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
567 | return (FALSE); | |
568 | ||
569 | return (TRUE == (S_IFSOCK == (stat_buf.st_mode & S_IFMT))); | |
570 | #endif | |
571 | ||
572 | case 'c': /* File is character special? */ | |
573 | unary_advance (); | |
574 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
575 | return (FALSE); | |
576 | ||
577 | return (TRUE == (S_IFCHR == (stat_buf.st_mode & S_IFMT))); | |
578 | ||
579 | case 'b': /* File is block special? */ | |
580 | unary_advance (); | |
581 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
582 | return (FALSE); | |
583 | ||
584 | return (TRUE == (S_IFBLK == (stat_buf.st_mode & S_IFMT))); | |
585 | ||
586 | case 'p': /* File is a named pipe? */ | |
587 | unary_advance (); | |
588 | #ifndef S_IFIFO | |
589 | return (FALSE); | |
590 | #else | |
591 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
592 | return (FALSE); | |
593 | return (TRUE == (S_IFIFO == (stat_buf.st_mode & S_IFMT))); | |
594 | #endif /* S_IFIFO */ | |
595 | ||
596 | case 'L': /* Same as -h */ | |
597 | /*FALLTHROUGH*/ | |
598 | ||
599 | case 'h': /* File is a symbolic link? */ | |
600 | unary_advance (); | |
601 | #ifndef S_IFLNK | |
602 | return (FALSE); | |
603 | #else | |
604 | if (lstat (argv[pos - 1], &stat_buf) < 0) | |
605 | return (FALSE); | |
606 | ||
607 | return (TRUE == (S_IFLNK == (stat_buf.st_mode & S_IFMT))); | |
608 | #endif /* S_IFLNK */ | |
609 | ||
610 | case 'u': /* File is setuid? */ | |
611 | unary_advance (); | |
612 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
613 | return (FALSE); | |
614 | ||
615 | return (TRUE == (0 != (stat_buf.st_mode & S_ISUID))); | |
616 | ||
617 | case 'g': /* File is setgid? */ | |
618 | unary_advance (); | |
619 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
620 | return (FALSE); | |
621 | ||
622 | return (TRUE == (0 != (stat_buf.st_mode & S_ISGID))); | |
623 | ||
624 | case 'k': /* File has sticky bit set? */ | |
625 | unary_advance (); | |
626 | if (stat (argv[pos - 1], &stat_buf) < 0) | |
627 | return (FALSE); | |
628 | return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX))); | |
629 | ||
630 | case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */ | |
631 | advance (0); | |
632 | if (pos < argc && isint (pos, &r)) | |
633 | { | |
634 | advance (0); | |
635 | return (TRUE == (isatty ((int) r))); | |
636 | } | |
637 | return (TRUE == (isatty (1))); | |
638 | ||
639 | case 'n': /* True if arg has some length. */ | |
640 | unary_advance (); | |
641 | return (TRUE == (0 != strlen (argv[pos - 1]))); | |
642 | break; | |
643 | ||
644 | case 'z': /* True if arg has no length. */ | |
645 | unary_advance (); | |
646 | return (TRUE == (0 == strlen (argv[pos - 1]))); | |
647 | } | |
648 | } | |
649 | value = 0 != strlen (argv[pos]); | |
650 | advance (0); | |
651 | return value; | |
652 | } | |
653 | ||
654 | /* | |
655 | * and: | |
656 | * and '-a' term | |
657 | * term | |
658 | */ | |
659 | static int | |
660 | and () | |
661 | { | |
662 | auto int value; | |
663 | ||
664 | value = term (); | |
665 | while (pos < argc && '-' == argv[pos][0] && 'a' == argv[pos][1] && '\000' == argv[pos][2]) | |
666 | { | |
667 | advance (0); | |
668 | value = TRUTH_AND (value, term ()); | |
669 | } | |
670 | return (TRUE == value); | |
671 | } | |
672 | ||
673 | /* | |
674 | * or: | |
675 | * or '-o' and | |
676 | * and | |
677 | */ | |
678 | static int | |
679 | or () | |
680 | { | |
681 | auto int value; | |
682 | ||
683 | value = and (); | |
684 | while (pos < argc && '-' == argv[pos][0] && 'o' == argv[pos][1] && '\000' == argv[pos][2]) | |
685 | { | |
686 | advance (0); | |
687 | value = TRUTH_OR (value, and ()); | |
688 | } | |
689 | return (TRUE == value); | |
690 | } | |
691 | ||
692 | /* | |
693 | * expr: | |
694 | * or | |
695 | */ | |
696 | int | |
697 | expr () | |
698 | { | |
699 | auto int value; | |
700 | ||
701 | if (pos >= argc) | |
702 | beyond (); | |
703 | ||
704 | value = FALSE; | |
705 | ||
706 | return value ^ or (); /* Same with this. */ | |
707 | } | |
708 | ||
709 | /* | |
710 | * [: | |
711 | * '[' expr ']' | |
712 | * test: | |
713 | * test expr | |
714 | */ | |
715 | int | |
716 | #ifdef STANDALONE | |
717 | main (margc, margv) | |
718 | #else | |
719 | test_command (margc, margv) | |
720 | #endif /* STANDALONE */ | |
721 | int margc; | |
722 | char **margv; | |
723 | { | |
724 | auto int value; | |
725 | int expr (); | |
726 | #ifndef STANDALONE | |
727 | int code = setjmp (test_exit_buf); | |
728 | ||
729 | if (code) | |
730 | return (test_error_return); | |
731 | #endif /* STANDALONE */ | |
732 | ||
733 | argv = margv; | |
734 | ||
735 | if (margv[0] && strcmp (margv[0], "[") == 0) | |
736 | { | |
737 | --margc; | |
738 | ||
739 | if (margc < 2) | |
740 | test_exit (SHELL_BOOLEAN (FALSE)); | |
741 | ||
742 | if (margv[margc] && strcmp (margv[margc], "]") != 0) | |
743 | test_syntax_error ("missing `]'\n", (char *)NULL); | |
744 | } | |
745 | ||
746 | argc = margc; | |
747 | pos = 1; | |
748 | ||
749 | if (pos >= argc) | |
750 | test_exit (SHELL_BOOLEAN (FALSE)); | |
751 | ||
752 | value = expr (); | |
753 | if (pos != argc) | |
754 | test_syntax_error ("too many arguments\n", (char *)NULL); | |
755 | ||
756 | test_exit (SHELL_BOOLEAN (value)); | |
757 | } |