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