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