(R_OK, W_OK, X_OK, FOK): Remove; system.h defines them.
[platform/upstream/coreutils.git] / src / test.c
1 /* GNU test program (ksb and mjb) */
2
3 /* Modified to run with the GNU shell by bfox. */
4
5 /* Copyright (C) 1987-2004 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
20    along with this program; if not, write to the Free Software Foundation,
21    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22
23 /* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get
24    the shell builtin version. */
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29
30 #define TEST_STANDALONE 1
31
32 #ifndef LBRACKET
33 # define LBRACKET 0
34 #endif
35
36 /* The official name of this program (e.g., no `g' prefix).  */
37 #if LBRACKET
38 # define PROGRAM_NAME "["
39 #else
40 # define PROGRAM_NAME "test"
41 #endif
42
43 #include "system.h"
44 #include "error.h"
45 #include "euidaccess.h"
46 #include "quote.h"
47
48 #ifndef _POSIX_VERSION
49 # include <sys/param.h>
50 #endif /* _POSIX_VERSION */
51 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
52 #define digit(c)  ((c) >= '0' && (c) <= '9')
53 #define digit_value(c) ((c) - '0')
54
55 char *program_name;
56
57 #if !defined (_POSIX_VERSION)
58 # include <sys/file.h>
59 #endif /* !_POSIX_VERSION */
60
61 extern gid_t getegid ();
62 extern uid_t geteuid ();
63
64 /* The following few defines control the truth and false output of each stage.
65    TRUE and FALSE are what we use to compute the final output value.
66    SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
67    TRUTH_OR is how to do logical or with TRUE and FALSE.
68    TRUTH_AND is how to do logical and with TRUE and FALSE..
69    Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
70     SHELL_BOOLEAN = (!value). */
71 #define TRUE 1
72 #define FALSE 0
73 #define SHELL_BOOLEAN(value) (!(value))
74 #define TRUTH_OR(a, b) ((a) | (b))
75 #define TRUTH_AND(a, b) ((a) & (b))
76
77 /* Exit status for syntax errors, etc.  */
78 enum { TEST_FAILURE = 2 };
79
80 #if defined (TEST_STANDALONE)
81 # define test_exit(val) exit (val)
82 #else
83    static jmp_buf test_exit_buf;
84    static int test_error_return = 0;
85 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
86 #endif /* !TEST_STANDALONE */
87
88 static int pos;         /* The offset of the current argument in ARGV. */
89 static int argc;        /* The number of arguments present in ARGV. */
90 static char **argv;     /* The argument list. */
91
92 static int test_unop (char const *s);
93 static int binop (char *s);
94 static int unary_operator (void);
95 static int binary_operator (bool);
96 static int two_arguments (void);
97 static int three_arguments (void);
98 static int posixtest (int);
99
100 static int expr (void);
101 static int term (void);
102 static int and (void);
103 static int or (void);
104
105 static void test_syntax_error (char const *format, char const *arg)
106      ATTRIBUTE_NORETURN;
107 static void beyond (void) ATTRIBUTE_NORETURN;
108
109 static void
110 test_syntax_error (char const *format, char const *arg)
111 {
112   fprintf (stderr, "%s: ", argv[0]);
113   fprintf (stderr, format, arg);
114   fflush (stderr);
115   test_exit (TEST_FAILURE);
116 }
117
118 /* Increment our position in the argument list.  Check that we're not
119    past the end of the argument list.  This check is supressed if the
120    argument is FALSE.  Made a macro for efficiency. */
121 #define advance(f)                                                      \
122   do                                                                    \
123     {                                                                   \
124       ++pos;                                                            \
125       if ((f) && pos >= argc)                                           \
126         beyond ();                                                      \
127     }                                                                   \
128   while (0)
129
130 #if !defined (advance)
131 static int
132 advance (int f)
133 {
134   ++pos;
135
136   if (f && pos >= argc)
137     beyond ();
138 }
139 #endif /* advance */
140
141 #define unary_advance()                                                 \
142   do                                                                    \
143     {                                                                   \
144       advance (1);                                                      \
145       ++pos;                                                            \
146     }                                                                   \
147   while (0)
148
149 /*
150  * beyond - call when we're beyond the end of the argument list (an
151  *      error condition)
152  */
153 static void
154 beyond (void)
155 {
156   test_syntax_error (_("missing argument after %s"), quote (argv[argc - 1]));
157 }
158
159 /* Syntax error for when an integer argument was expected, but
160    something else was found. */
161 static void
162 integer_expected_error (char const *pch)
163 {
164   test_syntax_error (_("%s: integer expression expected\n"), pch);
165 }
166
167 /* Return nonzero if the characters pointed to by STRING constitute a
168    valid number.  Stuff the converted number into RESULT if RESULT is
169    not null.  */
170 static int
171 isint (register char *string, intmax_t *result)
172 {
173   int sign;
174   intmax_t value;
175
176   sign = 1;
177   value = 0;
178
179   if (result)
180     *result = 0;
181
182   /* Skip leading whitespace characters. */
183   while (whitespace (*string))
184     string++;
185
186   if (!*string)
187     return (0);
188
189   /* We allow leading `-' or `+'. */
190   if (*string == '-' || *string == '+')
191     {
192       if (!digit (string[1]))
193         return (0);
194
195       if (*string == '-')
196         sign = -1;
197
198       string++;
199     }
200
201   while (digit (*string))
202     {
203       if (result)
204         value = (value * 10) + digit_value (*string);
205       string++;
206     }
207
208   /* Skip trailing whitespace, if any. */
209   while (whitespace (*string))
210     string++;
211
212   /* Error if not at end of string. */
213   if (*string)
214     return (0);
215
216   if (result)
217     {
218       value *= sign;
219       *result = value;
220     }
221
222   return (1);
223 }
224
225 /* Find the modification time of FILE, and stuff it into *AGE.
226    Return 0 if successful, -1 if not.  */
227 static int
228 age_of (char *filename, time_t *age)
229 {
230   struct stat finfo;
231   int r = stat (filename, &finfo);
232   if (r == 0)
233     *age = finfo.st_mtime;
234   return r;
235 }
236
237 /*
238  * term - parse a term and return 1 or 0 depending on whether the term
239  *      evaluates to true or false, respectively.
240  *
241  * term ::=
242  *      '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
243  *      '-'('L'|'x') filename
244  *      '-t' int
245  *      '-'('z'|'n') string
246  *      string
247  *      string ('!='|'=') string
248  *      <int> '-'(eq|ne|le|lt|ge|gt) <int>
249  *      file '-'(nt|ot|ef) file
250  *      '(' <expr> ')'
251  * int ::=
252  *      '-l' string
253  *      positive and negative integers
254  */
255 static int
256 term (void)
257 {
258   int value;
259
260   if (pos >= argc)
261     beyond ();
262
263   /* Deal with leading `not's. */
264   if (argv[pos][0] == '!' && argv[pos][1] == '\0')
265     {
266       value = 0;
267       while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
268         {
269           advance (1);
270           value = 1 - value;
271         }
272
273       return (value ? !term() : term());
274     }
275
276   /* A paren-bracketed argument. */
277   if (argv[pos][0] == '(' && argv[pos][1] == '\0')
278     {
279       int nargs;
280
281       advance (1);
282
283       for (nargs = 1;
284            pos + nargs < argc && ! STREQ (argv[pos + nargs], ")");
285            nargs++)
286         if (nargs == 4)
287           {
288             nargs = argc - pos;
289             break;
290           }
291
292       value = posixtest (nargs);
293       if (argv[pos] == 0)
294         test_syntax_error (_("')' expected\n"), NULL);
295       else
296         if (argv[pos][0] != ')' || argv[pos][1])
297           test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
298       advance (0);
299       return (value);
300     }
301
302   /* are there enough arguments left that this could be dyadic? */
303   if (pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))
304     value = binary_operator (true);
305   else if (pos + 3 <= argc && binop (argv[pos + 1]))
306     value = binary_operator (false);
307
308   /* Might be a switch type argument */
309   else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
310     {
311       if (test_unop (argv[pos]))
312         value = unary_operator ();
313       else
314         test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
315     }
316   else
317     {
318       value = (argv[pos][0] != '\0');
319       advance (0);
320     }
321
322   return (value);
323 }
324
325 static int
326 binary_operator (bool l_is_l)
327 {
328   register int op;
329   struct stat stat_buf, stat_spare;
330   intmax_t l, r;
331   int value;
332   /* Is the right integer expression of the form '-l string'? */
333   int r_is_l;
334
335   if (l_is_l)
336     advance (0);
337   op = pos + 1;
338
339   if ((op < argc - 2) && STREQ (argv[op + 1], "-l"))
340     {
341       r_is_l = 1;
342       advance (0);
343     }
344   else
345     r_is_l = 0;
346
347   if (argv[op][0] == '-')
348     {
349       /* check for eq, nt, and stuff */
350       switch (argv[op][1])
351         {
352         default:
353           break;
354
355         case 'l':
356           if (argv[op][2] == 't' && !argv[op][3])
357             {
358               /* lt */
359               if (l_is_l)
360                 l = strlen (argv[op - 1]);
361               else
362                 {
363                   if (!isint (argv[op - 1], &l))
364                     integer_expected_error (_("before -lt"));
365                 }
366
367               if (r_is_l)
368                 r = strlen (argv[op + 2]);
369               else
370                 {
371                   if (!isint (argv[op + 1], &r))
372                     integer_expected_error (_("after -lt"));
373                 }
374               pos += 3;
375               return (TRUE == (l < r));
376             }
377
378           if (argv[op][2] == 'e' && !argv[op][3])
379             {
380               /* le */
381               if (l_is_l)
382                 l = strlen (argv[op - 1]);
383               else
384                 {
385                   if (!isint (argv[op - 1], &l))
386                     integer_expected_error (_("before -le"));
387                 }
388               if (r_is_l)
389                 r = strlen (argv[op + 2]);
390               else
391                 {
392                   if (!isint (argv[op + 1], &r))
393                     integer_expected_error (_("after -le"));
394                 }
395               pos += 3;
396               return (TRUE == (l <= r));
397             }
398           break;
399
400         case 'g':
401           if (argv[op][2] == 't' && !argv[op][3])
402             {
403               /* gt integer greater than */
404               if (l_is_l)
405                 l = strlen (argv[op - 1]);
406               else
407                 {
408                   if (!isint (argv[op - 1], &l))
409                     integer_expected_error (_("before -gt"));
410                 }
411               if (r_is_l)
412                 r = strlen (argv[op + 2]);
413               else
414                 {
415                   if (!isint (argv[op + 1], &r))
416                     integer_expected_error (_("after -gt"));
417                 }
418               pos += 3;
419               return (TRUE == (l > r));
420             }
421
422           if (argv[op][2] == 'e' && !argv[op][3])
423             {
424               /* ge - integer greater than or equal to */
425               if (l_is_l)
426                 l = strlen (argv[op - 1]);
427               else
428                 {
429                   if (!isint (argv[op - 1], &l))
430                     integer_expected_error (_("before -ge"));
431                 }
432               if (r_is_l)
433                 r = strlen (argv[op + 2]);
434               else
435                 {
436                   if (!isint (argv[op + 1], &r))
437                     integer_expected_error (_("after -ge"));
438                 }
439               pos += 3;
440               return (TRUE == (l >= r));
441             }
442           break;
443
444         case 'n':
445           if (argv[op][2] == 't' && !argv[op][3])
446             {
447               /* nt - newer than */
448               time_t lt, rt;
449               int le, re;
450               pos += 3;
451               if (l_is_l || r_is_l)
452                 test_syntax_error (_("-nt does not accept -l\n"), NULL);
453               le = age_of (argv[op - 1], &lt);
454               re = age_of (argv[op + 1], &rt);
455               return le > re || (le == 0 && lt > rt);
456             }
457
458           if (argv[op][2] == 'e' && !argv[op][3])
459             {
460               /* ne - integer not equal */
461               if (l_is_l)
462                 l = strlen (argv[op - 1]);
463               else
464                 {
465                   if (!isint (argv[op - 1], &l))
466                     integer_expected_error (_("before -ne"));
467                 }
468               if (r_is_l)
469                 r = strlen (argv[op + 2]);
470               else
471                 {
472                   if (!isint (argv[op + 1], &r))
473                     integer_expected_error (_("after -ne"));
474                 }
475               pos += 3;
476               return (TRUE == (l != r));
477             }
478           break;
479
480         case 'e':
481           if (argv[op][2] == 'q' && !argv[op][3])
482             {
483               /* eq - integer equal */
484               if (l_is_l)
485                 l = strlen (argv[op - 1]);
486               else
487                 {
488                   if (!isint (argv[op - 1], &l))
489                     integer_expected_error (_("before -eq"));
490                 }
491               if (r_is_l)
492                 r = strlen (argv[op + 2]);
493               else
494                 {
495                   if (!isint (argv[op + 1], &r))
496                     integer_expected_error (_("after -eq"));
497                 }
498               pos += 3;
499               return (TRUE == (l == r));
500             }
501
502           if (argv[op][2] == 'f' && !argv[op][3])
503             {
504               /* ef - hard link? */
505               pos += 3;
506               if (l_is_l || r_is_l)
507                 test_syntax_error (_("-ef does not accept -l\n"), NULL);
508               if (stat (argv[op - 1], &stat_buf) < 0)
509                 return (FALSE);
510               if (stat (argv[op + 1], &stat_spare) < 0)
511                 return (FALSE);
512               return (TRUE ==
513                       (stat_buf.st_dev == stat_spare.st_dev &&
514                        stat_buf.st_ino == stat_spare.st_ino));
515             }
516           break;
517
518         case 'o':
519           if ('t' == argv[op][2] && '\000' == argv[op][3])
520             {
521               /* ot - older than */
522               time_t lt, rt;
523               int le, re;
524               pos += 3;
525               if (l_is_l || r_is_l)
526                 test_syntax_error (_("-ot does not accept -l\n"), NULL);
527               le = age_of (argv[op - 1], &lt);
528               re = age_of (argv[op + 1], &rt);
529               return le < re || (re == 0 && lt < rt);
530             }
531           break;
532         }
533
534       /* FIXME: is this dead code? */
535       test_syntax_error (_("unknown binary operator\n"), argv[op]);
536     }
537
538   if (argv[op][0] == '=' && !argv[op][1])
539     {
540       value = STREQ (argv[pos], argv[pos + 2]);
541       pos += 3;
542       return (TRUE == value);
543     }
544
545   if (STREQ (argv[op], "!="))
546     {
547       value = !STREQ (argv[pos], argv[pos + 2]);
548       pos += 3;
549       return (TRUE == value);
550     }
551
552   /* Not reached.  */
553   abort ();
554 }
555
556 static int
557 unary_operator (void)
558 {
559   int value;
560   struct stat stat_buf;
561
562   switch (argv[pos][1])
563     {
564     default:
565       return (FALSE);
566
567       /* All of the following unary operators use unary_advance (), which
568          checks to make sure that there is an argument, and then advances
569          pos right past it.  This means that pos - 1 is the location of the
570          argument. */
571
572     case 'a':                   /* file exists in the file system? */
573     case 'e':
574       unary_advance ();
575       value = -1 != stat (argv[pos - 1], &stat_buf);
576       return (TRUE == value);
577
578     case 'r':                   /* file is readable? */
579       unary_advance ();
580       value = -1 != euidaccess (argv[pos - 1], R_OK);
581       return (TRUE == value);
582
583     case 'w':                   /* File is writable? */
584       unary_advance ();
585       value = -1 != euidaccess (argv[pos - 1], W_OK);
586       return (TRUE == value);
587
588     case 'x':                   /* File is executable? */
589       unary_advance ();
590       value = -1 != euidaccess (argv[pos - 1], X_OK);
591       return (TRUE == value);
592
593     case 'O':                   /* File is owned by you? */
594       unary_advance ();
595       if (stat (argv[pos - 1], &stat_buf) < 0)
596         return (FALSE);
597
598       return (TRUE == (geteuid () == stat_buf.st_uid));
599
600     case 'G':                   /* File is owned by your group? */
601       unary_advance ();
602       if (stat (argv[pos - 1], &stat_buf) < 0)
603         return (FALSE);
604
605       return (TRUE == (getegid () == stat_buf.st_gid));
606
607     case 'f':                   /* File is a file? */
608       unary_advance ();
609       if (stat (argv[pos - 1], &stat_buf) < 0)
610         return (FALSE);
611
612       /* Under POSIX, -f is true if the given file exists
613          and is a regular file. */
614       return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
615                        (0 == (stat_buf.st_mode & S_IFMT))));
616
617     case 'd':                   /* File is a directory? */
618       unary_advance ();
619       if (stat (argv[pos - 1], &stat_buf) < 0)
620         return (FALSE);
621
622       return (TRUE == (S_ISDIR (stat_buf.st_mode)));
623
624     case 's':                   /* File has something in it? */
625       unary_advance ();
626       if (stat (argv[pos - 1], &stat_buf) < 0)
627         return (FALSE);
628
629       return (TRUE == (stat_buf.st_size > (off_t) 0));
630
631     case 'S':                   /* File is a socket? */
632 #if !defined (S_ISSOCK)
633       return (FALSE);
634 #else
635       unary_advance ();
636
637       if (stat (argv[pos - 1], &stat_buf) < 0)
638         return (FALSE);
639
640       return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
641 #endif                          /* S_ISSOCK */
642
643     case 'c':                   /* File is character special? */
644       unary_advance ();
645       if (stat (argv[pos - 1], &stat_buf) < 0)
646         return (FALSE);
647
648       return (TRUE == (S_ISCHR (stat_buf.st_mode)));
649
650     case 'b':                   /* File is block special? */
651       unary_advance ();
652       if (stat (argv[pos - 1], &stat_buf) < 0)
653         return (FALSE);
654
655       return (TRUE == (S_ISBLK (stat_buf.st_mode)));
656
657     case 'p':                   /* File is a named pipe? */
658       unary_advance ();
659 #ifndef S_ISFIFO
660       return (FALSE);
661 #else
662       if (stat (argv[pos - 1], &stat_buf) < 0)
663         return (FALSE);
664       return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
665 #endif                          /* S_ISFIFO */
666
667     case 'L':                   /* Same as -h  */
668       /*FALLTHROUGH*/
669
670     case 'h':                   /* File is a symbolic link? */
671       unary_advance ();
672 #ifndef S_ISLNK
673       return (FALSE);
674 #else
675       /* An empty filename is not a valid pathname. */
676       if ((argv[pos - 1][0] == '\0') ||
677           (lstat (argv[pos - 1], &stat_buf) < 0))
678         return (FALSE);
679
680       return (TRUE == (S_ISLNK (stat_buf.st_mode)));
681 #endif                          /* S_IFLNK */
682
683     case 'u':                   /* File is setuid? */
684       unary_advance ();
685 #ifndef S_ISUID
686       return (FALSE);
687 #else
688       if (stat (argv[pos - 1], &stat_buf) < 0)
689         return (FALSE);
690
691       return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
692 #endif
693
694     case 'g':                   /* File is setgid? */
695       unary_advance ();
696 #ifndef S_ISGID
697       return (FALSE);
698 #else
699       if (stat (argv[pos - 1], &stat_buf) < 0)
700         return (FALSE);
701
702       return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
703 #endif
704
705     case 'k':                   /* File has sticky bit set? */
706       unary_advance ();
707       if (stat (argv[pos - 1], &stat_buf) < 0)
708         return (FALSE);
709 #ifndef S_ISVTX
710       /* This is not Posix, and is not defined on some Posix systems. */
711       return (FALSE);
712 #else
713       return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
714 #endif
715
716     case 't':                   /* File (fd) is a terminal? */
717       {
718         intmax_t fd;
719         unary_advance ();
720         if (!isint (argv[pos - 1], &fd))
721           integer_expected_error (_("after -t"));
722         return (TRUE == (fd == (int) fd && isatty (fd)));
723       }
724
725     case 'n':                   /* True if arg has some length. */
726       unary_advance ();
727       return (TRUE == (argv[pos - 1][0] != 0));
728
729     case 'z':                   /* True if arg has no length. */
730       unary_advance ();
731       return (TRUE == (argv[pos - 1][0] == '\0'));
732     }
733 }
734
735 /*
736  * and:
737  *      term
738  *      term '-a' and
739  */
740 static int
741 and (void)
742 {
743   int value;
744
745   value = term ();
746   while ((pos < argc) && STREQ (argv[pos], "-a"))
747     {
748       advance (0);
749       value = TRUTH_AND (value, and ());
750     }
751   return (TRUE == value);
752 }
753
754 /*
755  * or:
756  *      and
757  *      and '-o' or
758  */
759 static int
760 or (void)
761 {
762   int value;
763
764   value = and ();
765
766   while ((pos < argc) && STREQ (argv[pos], "-o"))
767     {
768       advance (0);
769       value = TRUTH_OR (value, or ());
770     }
771
772   return (TRUE == value);
773 }
774
775 /*
776  * expr:
777  *      or
778  */
779 static int
780 expr (void)
781 {
782   if (pos >= argc)
783     beyond ();
784
785   return (FALSE ^ (or ()));             /* Same with this. */
786 }
787
788 /* Return TRUE if S is one of the test command's binary operators. */
789 static int
790 binop (char *s)
791 {
792   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
793           (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
794           (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
795           (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
796 }
797
798 /* Return nonzero if OP is one of the test command's unary operators. */
799 static int
800 test_unop (char const *op)
801 {
802   if (op[0] != '-')
803     return (0);
804
805   switch (op[1])
806     {
807     case 'a': case 'b': case 'c': case 'd': case 'e':
808     case 'f': case 'g': case 'h': case 'k': case 'n':
809     case 'o': case 'p': case 'r': case 's': case 't':
810     case 'u': case 'w': case 'x': case 'z':
811     case 'G': case 'L': case 'O': case 'S': case 'N':
812       return (1);
813     }
814
815   return (0);
816 }
817
818 static int
819 one_argument (void)
820 {
821   return argv[pos++][0] != '\0';
822 }
823
824 static int
825 two_arguments (void)
826 {
827   int value;
828
829   if (STREQ (argv[pos], "!"))
830     {
831       advance (0);
832       value = ! one_argument ();
833     }
834   else if (argv[pos][0] == '-'
835            && argv[pos][1] != '\0'
836            && argv[pos][2] == '\0')
837     {
838       if (test_unop (argv[pos]))
839         value = unary_operator ();
840       else
841         test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
842     }
843   else
844     beyond ();
845   return (value);
846 }
847
848 static int
849 three_arguments (void)
850 {
851   int value;
852
853   if (binop (argv[pos + 1]))
854     value = binary_operator (false);
855   else if (STREQ (argv[pos], "!"))
856     {
857       advance (1);
858       value = !two_arguments ();
859     }
860   else if (STREQ (argv[pos], "(") && STREQ (argv[pos + 2], ")"))
861     {
862       advance (0);
863       value = one_argument ();
864       advance (0);
865     }
866   else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o"))
867     value = expr ();
868   else
869     test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
870   return (value);
871 }
872
873 /* This is an implementation of a Posix.2 proposal by David Korn. */
874 static int
875 posixtest (int nargs)
876 {
877   int value;
878
879   switch (nargs)
880     {
881       case 1:
882         value = one_argument ();
883         break;
884
885       case 2:
886         value = two_arguments ();
887         break;
888
889       case 3:
890         value = three_arguments ();
891         break;
892
893       case 4:
894         if (STREQ (argv[pos], "!"))
895           {
896             advance (1);
897             value = !three_arguments ();
898             break;
899           }
900         if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")"))
901           {
902             advance (0);
903             value = two_arguments ();
904             advance (0);
905             break;
906           }
907         /* FALLTHROUGH */
908       case 5:
909       default:
910         if (nargs <= 0)
911           abort ();
912         value = expr ();
913     }
914
915   return (value);
916 }
917
918 #if defined (TEST_STANDALONE)
919 # include "long-options.h"
920
921 void
922 usage (int status)
923 {
924   if (status != EXIT_SUCCESS)
925     fprintf (stderr, _("Try `%s --help' for more information.\n"),
926              program_name);
927   else
928     {
929       fputs (_("\
930 Usage: test EXPRESSION\n\
931   or:  [ EXPRESSION ]\n\
932   or:  [ OPTION\n\
933 Exit with the status determined by EXPRESSION.\n\
934 \n\
935 "), stdout);
936       fputs (HELP_OPTION_DESCRIPTION, stdout);
937       fputs (VERSION_OPTION_DESCRIPTION, stdout);
938       fputs (_("\
939 \n\
940 EXPRESSION is true or false and sets exit status.  It is one of:\n\
941 "), stdout);
942       fputs (_("\
943 \n\
944   ( EXPRESSION )               EXPRESSION is true\n\
945   ! EXPRESSION                 EXPRESSION is false\n\
946   EXPRESSION1 -a EXPRESSION2   both EXPRESSION1 and EXPRESSION2 are true\n\
947   EXPRESSION1 -o EXPRESSION2   either EXPRESSION1 or EXPRESSION2 is true\n\
948 "), stdout);
949       fputs (_("\
950 \n\
951   [-n] STRING          the length of STRING is nonzero\n\
952   -z STRING            the length of STRING is zero\n\
953   STRING1 = STRING2    the strings are equal\n\
954   STRING1 != STRING2   the strings are not equal\n\
955 "), stdout);
956       fputs (_("\
957 \n\
958   INTEGER1 -eq INTEGER2   INTEGER1 is equal to INTEGER2\n\
959   INTEGER1 -ge INTEGER2   INTEGER1 is greater than or equal to INTEGER2\n\
960   INTEGER1 -gt INTEGER2   INTEGER1 is greater than INTEGER2\n\
961   INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2\n\
962   INTEGER1 -lt INTEGER2   INTEGER1 is less than INTEGER2\n\
963   INTEGER1 -ne INTEGER2   INTEGER1 is not equal to INTEGER2\n\
964 "), stdout);
965       fputs (_("\
966 \n\
967   FILE1 -ef FILE2   FILE1 and FILE2 have the same device and inode numbers\n\
968   FILE1 -nt FILE2   FILE1 is newer (modification date) than FILE2\n\
969   FILE1 -ot FILE2   FILE1 is older than FILE2\n\
970 "), stdout);
971       fputs (_("\
972 \n\
973   -b FILE     FILE exists and is block special\n\
974   -c FILE     FILE exists and is character special\n\
975   -d FILE     FILE exists and is a directory\n\
976   -e FILE     FILE exists\n\
977 "), stdout);
978       fputs (_("\
979   -f FILE     FILE exists and is a regular file\n\
980   -g FILE     FILE exists and is set-group-ID\n\
981   -h FILE     FILE exists and is a symbolic link (same as -L)\n\
982   -G FILE     FILE exists and is owned by the effective group ID\n\
983   -k FILE     FILE exists and has its sticky bit set\n\
984 "), stdout);
985       fputs (_("\
986   -L FILE     FILE exists and is a symbolic link (same as -h)\n\
987   -O FILE     FILE exists and is owned by the effective user ID\n\
988   -p FILE     FILE exists and is a named pipe\n\
989   -r FILE     FILE exists and is readable\n\
990   -s FILE     FILE exists and has a size greater than zero\n\
991 "), stdout);
992       fputs (_("\
993   -S FILE     FILE exists and is a socket\n\
994   -t FD       file descriptor FD is opened on a terminal\n\
995   -u FILE     FILE exists and its set-user-ID bit is set\n\
996   -w FILE     FILE exists and is writable\n\
997   -x FILE     FILE exists and is executable\n\
998 "), stdout);
999       fputs (_("\
1000 \n\
1001 Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
1002 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1003 "), stdout);
1004       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1005     }
1006   exit (status);
1007 }
1008 #endif /* TEST_STANDALONE */
1009
1010 #if !defined (TEST_STANDALONE)
1011 # define main test_command
1012 #endif
1013
1014 #define AUTHORS "Kevin Braunsdorf", "Matthew Bradburn"
1015
1016 /*
1017  * [:
1018  *      '[' expr ']'
1019  * test:
1020  *      test expr
1021  */
1022 int
1023 main (int margc, char **margv)
1024 {
1025   int value;
1026
1027 #if !defined (TEST_STANDALONE)
1028   int code;
1029
1030   code = setjmp (test_exit_buf);
1031
1032   if (code)
1033     return (test_error_return);
1034 #else /* TEST_STANDALONE */
1035   initialize_main (&margc, &margv);
1036   program_name = margv[0];
1037   setlocale (LC_ALL, "");
1038   bindtextdomain (PACKAGE, LOCALEDIR);
1039   textdomain (PACKAGE);
1040
1041   initialize_exit_failure (TEST_FAILURE);
1042   atexit (close_stdout);
1043 #endif /* TEST_STANDALONE */
1044
1045   argv = margv;
1046
1047   if (LBRACKET)
1048     {
1049       /* Recognize --help or --version, but only when invoked in the
1050          "[" form, and when the last argument is not "]".  POSIX
1051          allows "[ --help" and "[ --version" to have the usual GNU
1052          behavior, but it requires "test --help" and "test --version"
1053          to exit silently with status 1.  */
1054       if (margc < 2 || !STREQ (margv[margc - 1], "]"))
1055         {
1056           parse_long_options (margc, margv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1057                               usage, AUTHORS, (char const *) NULL);
1058           test_syntax_error (_("missing `]'\n"), NULL);
1059         }
1060
1061       --margc;
1062     }
1063
1064   argc = margc;
1065   pos = 1;
1066
1067   if (pos >= argc)
1068     test_exit (SHELL_BOOLEAN (FALSE));
1069
1070   value = posixtest (argc - 1);
1071
1072   if (pos != argc)
1073     test_syntax_error (_("extra argument %s"), quote (argv[pos]));
1074
1075   test_exit (SHELL_BOOLEAN (value));
1076 }