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