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