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