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