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