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