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