Imported from ../bash-2.04.tar.gz.
[platform/upstream/bash.git] / test.c
1 /* GNU test program (ksb and mjb) */
2
3 /* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
4
5 /* Copyright (C) 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
6
7    This file is part of GNU Bash, the Bourne Again SHell.
8
9    Bash is free software; you can redistribute it and/or modify it under
10    the terms of the GNU General Public License as published by the Free
11    Software Foundation; either version 2, or (at your option) any later
12    version.
13
14    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
15    WARRANTY; without even the implied warranty of MERCHANTABILITY or
16    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17    for more details.
18
19    You should have received a copy of the GNU General Public License along
20    with Bash; see the file COPYING.  If not, write to the Free Software
21    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
23 /* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
24    binary operators. */
25 /* #define PATTERN_MATCHING */
26
27 #if defined (HAVE_CONFIG_H)
28 #  include <config.h>
29 #endif
30
31 #include <stdio.h>
32
33 #include "bashtypes.h"
34
35 #if defined (HAVE_LIMITS_H)
36 #  include <limits.h>
37 #else
38 #  include <sys/param.h>
39 #endif
40
41 #if defined (HAVE_UNISTD_H)
42 #  include <unistd.h>
43 #endif
44
45 #include <errno.h>
46 #if !defined (errno)
47 extern int errno;
48 #endif /* !errno */
49
50 #if !defined (_POSIX_VERSION)
51 #  include <sys/file.h>
52 #endif /* !_POSIX_VERSION */
53 #include "posixstat.h"
54 #include "filecntl.h"
55
56 #include "shell.h"
57 #include "pathexp.h"
58 #include "test.h"
59 #include "builtins/common.h"
60
61 #include <glob/fnmatch.h>
62
63 #if !defined (STRLEN)
64 #  define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
65 #endif
66
67 #if !defined (STREQ)
68 #  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
69 #endif /* !STREQ */
70
71 #if !defined (member)
72 #  define member(c, s) (int)((c) ? (char *)strchr ((s), (c)) : 0)
73 #endif /* !member */
74
75 #if !defined (R_OK)
76 #define R_OK 4
77 #define W_OK 2
78 #define X_OK 1
79 #define F_OK 0
80 #endif /* R_OK */
81
82 #define EQ      0
83 #define NE      1
84 #define LT      2
85 #define GT      3
86 #define LE      4
87 #define GE      5
88
89 #define NT      0
90 #define OT      1
91 #define EF      2
92
93 /* The following few defines control the truth and false output of each stage.
94    TRUE and FALSE are what we use to compute the final output value.
95    SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
96    Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
97 #define TRUE 1
98 #define FALSE 0
99 #define SHELL_BOOLEAN(value) (!(value))
100
101 #define TEST_ERREXIT_STATUS     2
102
103 static procenv_t test_exit_buf;
104 static int test_error_return;
105 #define test_exit(val) \
106         do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
107
108 /* We have to use access(2) for machines running AFS, because it's
109    not a Unix file system.  This may produce incorrect answers for
110    non-AFS files.  I hate AFS. */
111 #if defined (AFS)
112 #  define EACCESS(path, mode)   access(path, mode)
113 #else
114 #  define EACCESS(path, mode)   test_eaccess(path, mode)
115 #endif /* AFS */
116
117 static int pos;         /* The offset of the current argument in ARGV. */
118 static int argc;        /* The number of arguments present in ARGV. */
119 static char **argv;     /* The argument list. */
120 static int noeval;
121
122 static int unary_operator ();
123 static int binary_operator ();
124 static int two_arguments ();
125 static int three_arguments ();
126 static int posixtest ();
127
128 static int expr ();
129 static int term ();
130 static int and ();
131 static int or ();
132
133 static void beyond ();
134
135 static void
136 test_syntax_error (format, arg)
137      char *format, *arg;
138 {
139   extern int interactive_shell;
140   extern char *get_name_for_error ();
141   if (interactive_shell == 0)
142     fprintf (stderr, "%s: ", get_name_for_error ());
143   fprintf (stderr, "%s: ", argv[0]);
144   fprintf (stderr, format, arg);
145   fprintf (stderr, "\n");
146   fflush (stderr);
147   test_exit (TEST_ERREXIT_STATUS);
148 }
149
150 /*
151  * beyond - call when we're beyond the end of the argument list (an
152  *      error condition)
153  */
154 static void
155 beyond ()
156 {
157   test_syntax_error ("argument expected", (char *)NULL);
158 }
159
160 /* Syntax error for when an integer argument was expected, but
161    something else was found. */
162 static void
163 integer_expected_error (pch)
164      char *pch;
165 {
166   test_syntax_error ("%s: integer expression expected", pch);
167 }
168
169 /* A wrapper for stat () which disallows pathnames that are empty strings
170    and handles /dev/fd emulation on systems that don't have it. */
171 static int
172 test_stat (path, finfo)
173      char *path;
174      struct stat *finfo;
175 {
176   if (*path == '\0')
177     {
178       errno = ENOENT;
179       return (-1);
180     }
181   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
182     {
183 #if !defined (HAVE_DEV_FD)
184       long fd;
185       if (legal_number (path + 8, &fd))
186         return (fstat ((int)fd, finfo));
187       else
188         {
189           errno = EBADF;
190           return (-1);
191         }
192 #else
193   /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
194      trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
195      On most systems, with the notable exception of linux, this is
196      effectively a no-op. */
197       char pbuf[32];
198       strcpy (pbuf, DEV_FD_PREFIX);
199       strcat (pbuf, path + 8);
200       return (stat (pbuf, finfo));
201 #endif /* !HAVE_DEV_FD */
202     }
203 #if !defined (HAVE_DEV_STDIN)
204   else if (STREQN (path, "/dev/std", 8))
205     {
206       if (STREQ (path+8, "in"))
207         return (fstat (0, finfo));
208       else if (STREQ (path+8, "out"))
209         return (fstat (1, finfo));
210       else if (STREQ (path+8, "err"))
211         return (fstat (2, finfo));
212       else
213         return (stat (path, finfo));
214     }
215 #endif /* !HAVE_DEV_STDIN */
216   return (stat (path, finfo));
217 }
218
219 /* Do the same thing access(2) does, but use the effective uid and gid,
220    and don't make the mistake of telling root that any file is
221    executable. */
222 int
223 test_eaccess (path, mode)
224      char *path;
225      int mode;
226 {
227   struct stat st;
228
229   if (test_stat (path, &st) < 0)
230     return (-1);
231
232   if (current_user.euid == 0)
233     {
234       /* Root can read or write any file. */
235       if (mode != X_OK)
236         return (0);
237
238       /* Root can execute any file that has any one of the execute
239          bits set. */
240       if (st.st_mode & S_IXUGO)
241         return (0);
242     }
243
244   if (st.st_uid == current_user.euid)        /* owner */
245     mode <<= 6;
246   else if (group_member (st.st_gid))
247     mode <<= 3;
248
249   if (st.st_mode & mode)
250     return (0);
251
252   errno = EACCES;
253   return (-1);
254 }
255
256 /* Increment our position in the argument list.  Check that we're not
257    past the end of the argument list.  This check is supressed if the
258    argument is FALSE.  Made a macro for efficiency. */
259 #define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
260 #define unary_advance() do { advance (1); ++pos; } while (0)
261
262 /*
263  * expr:
264  *      or
265  */
266 static int
267 expr ()
268 {
269   if (pos >= argc)
270     beyond ();
271
272   return (FALSE ^ or ());               /* Same with this. */
273 }
274
275 /*
276  * or:
277  *      and
278  *      and '-o' or
279  */
280 static int
281 or ()
282 {
283   int value, v2;
284
285   value = and ();
286   while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
287     {
288       advance (0);
289       v2 = or ();
290       return (value || v2);
291     }
292
293   return (value);
294 }
295
296 /*
297  * and:
298  *      term
299  *      term '-a' and
300  */
301 static int
302 and ()
303 {
304   int value, v2;
305
306   value = term ();
307   while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
308     {
309       advance (0);
310       v2 = and ();
311       return (value && v2);
312     }
313   return (value);
314 }
315
316 /*
317  * term - parse a term and return 1 or 0 depending on whether the term
318  *      evaluates to true or false, respectively.
319  *
320  * term ::=
321  *      '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
322  *      '-'('G'|'L'|'O'|'S'|'N') filename
323  *      '-t' [int]
324  *      '-'('z'|'n') string
325  *      '-o' option
326  *      string
327  *      string ('!='|'='|'==') string
328  *      <int> '-'(eq|ne|le|lt|ge|gt) <int>
329  *      file '-'(nt|ot|ef) file
330  *      '(' <expr> ')'
331  * int ::=
332  *      positive and negative integers
333  */
334 static int
335 term ()
336 {
337   int value;
338
339   if (pos >= argc)
340     beyond ();
341
342   /* Deal with leading `not's. */
343   if (argv[pos][0] == '!' && argv[pos][1] == '\0')
344     {
345       value = 0;
346       while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
347         {
348           advance (1);
349           value = 1 - value;
350         }
351
352       return (value ? !term() : term());
353     }
354
355   /* A paren-bracketed argument. */
356   if (argv[pos][0] == '(' && argv[pos][1] == '\0')
357     {
358       advance (1);
359       value = expr ();
360       if (argv[pos] == 0)
361         test_syntax_error ("`)' expected", (char *)NULL);
362       else if (argv[pos][0] != ')' || argv[pos][1])
363         test_syntax_error ("`)' expected, found %s", argv[pos]);
364       advance (0);
365       return (value);
366     }
367
368   /* are there enough arguments left that this could be dyadic? */
369   if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
370     value = binary_operator ();
371
372   /* Might be a switch type argument */
373   else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
374     {
375       if (test_unop (argv[pos]))
376         value = unary_operator ();
377       else
378         test_syntax_error ("%s: unary operator expected", argv[pos]);
379     }
380   else
381     {
382       value = argv[pos][0] != '\0';
383       advance (0);
384     }
385
386   return (value);
387 }
388
389 static int
390 filecomp (s, t, op)
391      char *s, *t;
392      int op;
393 {
394   struct stat st1, st2;
395
396   if (test_stat (s, &st1) < 0 || test_stat (t, &st2) < 0)
397     return (FALSE);
398   switch (op)
399     {
400     case OT: return (st1.st_mtime < st2.st_mtime);
401     case NT: return (st1.st_mtime > st2.st_mtime);
402     case EF: return ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino));
403     }
404   return (FALSE);
405 }
406
407 static int
408 arithcomp (s, t, op, flags)
409      char *s, *t;
410      int op, flags;
411 {
412   long l, r;
413   int expok;
414
415   if (flags & TEST_ARITHEXP)
416     {
417       l = evalexp (s, &expok);
418       if (expok == 0)
419         return (FALSE);         /* should probably longjmp here */
420       r = evalexp (t, &expok);
421       if (expok == 0)
422         return (FALSE);         /* ditto */
423     }
424   else
425     {
426       if (legal_number (s, &l) == 0)
427         integer_expected_error (s);
428       if (legal_number (t, &r) == 0)
429         integer_expected_error (t);
430     }
431
432   switch (op)
433     {
434     case EQ: return (l == r);
435     case NE: return (l != r);
436     case LT: return (l < r);
437     case GT: return (l > r);
438     case LE: return (l <= r);
439     case GE: return (l >= r);
440     }
441
442   return (FALSE);
443 }
444
445 static int
446 patcomp (string, pat, op)
447      char *string, *pat;
448      int op;
449 {
450   int m;
451
452   m = fnmatch (pat, string, FNMATCH_EXTFLAG);
453   return ((op == EQ) ? (m == 0) : (m != 0));
454 }
455
456 int
457 binary_test (op, arg1, arg2, flags)
458      char *op, *arg1, *arg2;
459      int flags;
460 {
461   int patmatch;
462
463   patmatch = (flags & TEST_PATMATCH);
464
465   if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
466     return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
467
468   else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
469     return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
470
471   else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
472     return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
473
474   else if (op[2] == 't')
475     {
476       switch (op[1])
477         {
478         case 'n': return (filecomp (arg1, arg2, NT));           /* -nt */
479         case 'o': return (filecomp (arg1, arg2, OT));           /* -ot */
480         case 'l': return (arithcomp (arg1, arg2, LT, flags));   /* -lt */
481         case 'g': return (arithcomp (arg1, arg2, GT, flags));   /* -gt */
482         }
483     }
484   else if (op[1] == 'e')
485     {
486       switch (op[2])
487         {
488         case 'f': return (filecomp (arg1, arg2, EF));           /* -ef */
489         case 'q': return (arithcomp (arg1, arg2, EQ, flags));   /* -eq */
490         }
491     }
492   else if (op[2] == 'e')
493     {
494       switch (op[1])
495         {
496         case 'n': return (arithcomp (arg1, arg2, NE, flags));   /* -ne */
497         case 'g': return (arithcomp (arg1, arg2, GE, flags));   /* -ge */
498         case 'l': return (arithcomp (arg1, arg2, LE, flags));   /* -le */
499         }
500     }
501
502   return (FALSE);       /* should never get here */
503 }
504
505
506 static int
507 binary_operator ()
508 {
509   int value;
510   char *w;
511
512   w = argv[pos + 1];
513   if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
514       ((w[0] == '>' || w[0] == '<') && w[1] == '\0') ||         /* <, > */
515       (w[0] == '!' && w[1] == '=' && w[2] == '\0'))             /* != */
516     {
517       value = binary_test (w, argv[pos], argv[pos + 2], 0);
518       pos += 3;
519       return (value);
520     }
521
522 #if defined (PATTERN_MATCHING)
523   if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
524     {
525       value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
526       pos += 3;
527       return (value);
528     }
529 #endif
530
531   if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
532     {
533       test_syntax_error ("%s: binary operator expected", w);
534       /* NOTREACHED */
535       return (FALSE);
536     }
537
538   value = binary_test (w, argv[pos], argv[pos + 2], 0);
539   pos += 3;
540   return value;
541 }
542
543 static int
544 unary_operator ()
545 {
546   char *op, *arg;
547   long r;
548
549   op = argv[pos];
550   if (test_unop (op) == 0)
551     return (FALSE);
552
553   /* the only tricky case is `-t', which may or may not take an argument. */
554   if (op[1] == 't')
555     {
556       advance (0);
557       if (pos < argc && legal_number (argv[pos], &r))
558         {
559           advance (0);
560           return (unary_test (op, argv[pos - 1]));
561         }
562       else
563         return (unary_test (op, "1"));
564     }
565
566   /* All of the unary operators take an argument, so we first call
567      unary_advance (), which checks to make sure that there is an
568      argument, and then advances pos right past it.  This means that
569      pos - 1 is the location of the argument. */
570   unary_advance ();
571   return (unary_test (op, argv[pos - 1]));
572 }
573
574 int
575 unary_test (op, arg)
576      char *op, *arg;
577 {
578   long r;
579   struct stat stat_buf;
580      
581   switch (op[1])
582     {
583     case 'a':                   /* file exists in the file system? */
584     case 'e':
585       return (test_stat (arg, &stat_buf) == 0);
586
587     case 'r':                   /* file is readable? */
588       return (EACCESS (arg, R_OK) == 0);
589
590     case 'w':                   /* File is writeable? */
591       return (EACCESS (arg, W_OK) == 0);
592
593     case 'x':                   /* File is executable? */
594       return (EACCESS (arg, X_OK) == 0);
595
596     case 'O':                   /* File is owned by you? */
597       return (test_stat (arg, &stat_buf) == 0 &&
598               (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
599
600     case 'G':                   /* File is owned by your group? */
601       return (test_stat (arg, &stat_buf) == 0 &&
602               (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
603
604     case 'N':
605       return (test_stat (arg, &stat_buf) == 0 &&
606               stat_buf.st_atime <= stat_buf.st_mtime);
607
608     case 'f':                   /* File is a file? */
609       if (test_stat (arg, &stat_buf) < 0)
610         return (FALSE);
611
612       /* -f is true if the given file exists and is a regular file. */
613 #if defined (S_IFMT)
614       return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
615 #else
616       return (S_ISREG (stat_buf.st_mode));
617 #endif /* !S_IFMT */
618
619     case 'd':                   /* File is a directory? */
620       return (test_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
621
622     case 's':                   /* File has something in it? */
623       return (test_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
624
625     case 'S':                   /* File is a socket? */
626 #if !defined (S_ISSOCK)
627       return (FALSE);
628 #else
629       return (test_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
630 #endif /* S_ISSOCK */
631
632     case 'c':                   /* File is character special? */
633       return (test_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
634
635     case 'b':                   /* File is block special? */
636       return (test_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
637
638     case 'p':                   /* File is a named pipe? */
639 #ifndef S_ISFIFO
640       return (FALSE);
641 #else
642       return (test_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
643 #endif /* S_ISFIFO */
644
645     case 'L':                   /* Same as -h  */
646     case 'h':                   /* File is a symbolic link? */
647 #if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
648       return (FALSE);
649 #else
650       return ((arg[0] != '\0') &&
651               (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
652 #endif /* S_IFLNK && HAVE_LSTAT */
653
654     case 'u':                   /* File is setuid? */
655       return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
656
657     case 'g':                   /* File is setgid? */
658       return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
659
660     case 'k':                   /* File has sticky bit set? */
661 #if !defined (S_ISVTX)
662       /* This is not Posix, and is not defined on some Posix systems. */
663       return (FALSE);
664 #else
665       return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
666 #endif
667
668     case 't':   /* File fd is a terminal? */
669       if (legal_number (arg, &r) == 0)
670         return (FALSE);
671       return (isatty ((int)r));
672
673     case 'n':                   /* True if arg has some length. */
674       return (arg[0] != '\0');
675
676     case 'z':                   /* True if arg has no length. */
677       return (arg[0] == '\0');
678
679     case 'o':                   /* True if option `arg' is set. */
680       return (minus_o_option_value (arg) == 1);
681     }
682 }
683
684 /* Return TRUE if OP is one of the test command's binary operators. */
685 int
686 test_binop (op)
687      char *op;
688 {
689   if (op[0] == '=' && op[1] == '\0')
690     return (1);         /* '=' */
691   else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0')  /* string <, > */
692     return (1);
693   else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
694     return (1);         /* `==' and `!=' */
695 #if defined (PATTERN_MATCHING)
696   else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
697     return (1);
698 #endif
699   else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
700     return (0);
701   else
702     {
703       if (op[2] == 't')
704         switch (op[1])
705           {
706           case 'n':             /* -nt */
707           case 'o':             /* -ot */
708           case 'l':             /* -lt */
709           case 'g':             /* -gt */
710             return (1);
711           default:
712             return (0);
713           }
714       else if (op[1] == 'e')
715         switch (op[2])
716           {
717           case 'q':             /* -eq */
718           case 'f':             /* -ef */
719             return (1);
720           default:
721             return (0);
722           }
723       else if (op[2] == 'e')
724         switch (op[1])
725           {
726           case 'n':             /* -ne */
727           case 'g':             /* -ge */
728           case 'l':             /* -le */
729             return (1);
730           default:
731             return (0);
732           }
733       else
734         return (0);
735     }
736 }
737
738 /* Return non-zero if OP is one of the test command's unary operators. */
739 int
740 test_unop (op)
741      char *op;
742 {
743   if (op[0] != '-')
744     return (0);
745
746   switch (op[1])
747     {
748     case 'a': case 'b': case 'c': case 'd': case 'e':
749     case 'f': case 'g': case 'h': case 'k': case 'n':
750     case 'o': case 'p': case 'r': case 's': case 't':
751     case 'u': case 'w': case 'x': case 'z':
752     case 'G': case 'L': case 'O': case 'S': case 'N':
753       return (1);
754     }
755
756   return (0);
757 }
758
759 static int
760 two_arguments ()
761 {
762   if (argv[pos][0] == '!' && argv[pos][1] == '\0')
763     return (argv[pos + 1][0] == '\0');
764   else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
765     {
766       if (test_unop (argv[pos]))
767         return (unary_operator ());
768       else
769         test_syntax_error ("%s: unary operator expected", argv[pos]);
770     }
771   else
772     test_syntax_error ("%s: unary operator expected", argv[pos]);
773
774   return (0);
775 }
776
777 #define ANDOR(s)  (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
778
779 #define ONE_ARG_TEST(s)         ((s)[0] != '\0')
780
781 static int
782 three_arguments ()
783 {
784   int value;
785
786   if (test_binop (argv[pos+1]))
787     {
788       value = binary_operator ();
789       pos = argc;
790     }
791   else if (ANDOR (argv[pos+1]))
792     {
793       if (argv[pos+1][1] == 'a')
794         value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
795       else
796         value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
797       pos = argc;
798     }
799   else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
800     {
801       advance (1);
802       value = !two_arguments ();
803     }
804   else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
805     {
806       value = ONE_ARG_TEST(argv[pos+1]);
807       pos = argc;
808     }
809   else
810     test_syntax_error ("%s: binary operator expected", argv[pos+1]);
811
812   return (value);
813 }
814
815 /* This is an implementation of a Posix.2 proposal by David Korn. */
816 static int
817 posixtest ()
818 {
819   int value;
820
821   switch (argc - 1)     /* one extra passed in */
822     {
823       case 0:
824         value = FALSE;
825         pos = argc;
826         break;
827
828       case 1:
829         value = ONE_ARG_TEST(argv[1]);
830         pos = argc;
831         break;
832
833       case 2:
834         value = two_arguments ();
835         pos = argc;
836         break;
837
838       case 3:
839         value = three_arguments ();
840         break;
841
842       case 4:
843         if (argv[pos][0] == '!' && argv[pos][1] == '\0')
844           {
845             advance (1);
846             value = !three_arguments ();
847             break;
848           }
849         /* FALLTHROUGH */
850       default:
851         value = expr ();
852     }
853
854   return (value);
855 }
856
857 /*
858  * [:
859  *      '[' expr ']'
860  * test:
861  *      test expr
862  */
863 int
864 test_command (margc, margv)
865      int margc;
866      char **margv;
867 {
868   int value;
869
870   int code;
871
872   code = setjmp (test_exit_buf);
873
874   if (code)
875     return (test_error_return);
876
877   argv = margv;
878
879   if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
880     {
881       --margc;
882
883       if (margc < 2)
884         test_exit (SHELL_BOOLEAN (FALSE));
885
886       if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
887         test_syntax_error ("missing `]'", (char *)NULL);
888     }
889
890   argc = margc;
891   pos = 1;
892
893   if (pos >= argc)
894     test_exit (SHELL_BOOLEAN (FALSE));
895
896   noeval = 0;
897   value = posixtest ();
898
899   if (pos != argc)
900     test_syntax_error ("too many arguments", (char *)NULL);
901
902   test_exit (SHELL_BOOLEAN (value));
903 }