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