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