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