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