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