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