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