Don't include version.h.
[coreutils.git] / src / test.c
blob076bef71125697db9541d344bc05d8cf55cad371
1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987-1993, 1994, 1995 Free Software Foundation, Inc.
7 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
19 You should have received a copy of the GNU General Public License along
20 with Bash; see the file COPYING. If not, write to the Free Software
21 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
23 /* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
24 the shell builtin version. */
25 /* #define TEST_STANDALONE */
27 #include <config.h>
28 #include <stdio.h>
29 #include <sys/types.h>
31 #define TEST_STANDALONE 1
33 #if !defined (TEST_STANDALONE)
34 # include "shell.h"
35 # include "posixstat.h"
36 # include "filecntl.h"
37 #else /* TEST_STANDALONE */
38 # include "system.h"
39 # include "group-member.h"
40 # include "error.h"
41 # if !defined (S_IXUGO)
42 # define S_IXUGO 0111
43 # endif /* S_IXUGO */
44 # if defined (_POSIX_VERSION)
45 # include <limits.h>
46 # else /* !_POSIX_VERSION */
47 # include <sys/param.h>
48 # endif /* _POSIX_VERSION */
49 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
50 #define digit(c) ((c) >= '0' && (c) <= '9')
51 #define digit_value(c) ((c) - '0')
52 char *program_name;
53 #endif /* TEST_STANDALONE */
55 #if !defined (_POSIX_VERSION)
56 # include <sys/file.h>
57 #endif /* !_POSIX_VERSION */
59 #include <errno.h>
60 #ifndef errno
61 extern int errno;
62 #endif
64 #if !defined (STREQ)
65 # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
66 #endif /* !STREQ */
68 #if !defined (member)
69 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
70 #endif /* !member */
72 extern gid_t getegid ();
73 extern uid_t geteuid ();
75 #if !defined (R_OK)
76 #define R_OK 4
77 #define W_OK 2
78 #define X_OK 1
79 #define F_OK 0
80 #endif /* R_OK */
82 /* This name is used solely when printing --version information. */
83 #define COMMAND_NAME "test"
85 /* The following few defines control the truth and false output of each stage.
86 TRUE and FALSE are what we use to compute the final output value.
87 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
88 TRUTH_OR is how to do logical or with TRUE and FALSE.
89 TRUTH_AND is how to do logical and with TRUE and FALSE..
90 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
91 SHELL_BOOLEAN = (!value). */
92 #define TRUE 1
93 #define FALSE 0
94 #define SHELL_BOOLEAN(value) (!(value))
95 #define TRUTH_OR(a, b) ((a) | (b))
96 #define TRUTH_AND(a, b) ((a) & (b))
98 #if defined (TEST_STANDALONE)
99 # define test_exit(val) exit (val)
100 #else
101 static jmp_buf test_exit_buf;
102 static int test_error_return = 0;
103 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
104 #endif /* !TEST_STANDALONE */
106 char *xrealloc ();
108 static int pos; /* The offset of the current argument in ARGV. */
109 static int argc; /* The number of arguments present in ARGV. */
110 static char **argv; /* The argument list. */
112 static int unop __P ((int op));
113 static int binop __P ((char *s));
114 static int unary_operator __P ((void));
115 static int binary_operator __P ((void));
116 static int two_arguments __P ((void));
117 static int three_arguments __P ((void));
118 static int posixtest __P ((void));
120 static int expr __P ((void));
121 static int term __P ((void));
122 static int and __P ((void));
123 static int or __P ((void));
125 #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
126 && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
127 #define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
128 #else
129 #define NO_RETURN_ATTRIBUTE /* empty */
130 #endif
132 static void test_syntax_error __P ((char *format, char *arg)) NO_RETURN_ATTRIBUTE;
133 static void beyond __P ((void)) NO_RETURN_ATTRIBUTE;
135 static void
136 test_syntax_error (char *format, char *arg)
138 fprintf (stderr, "%s: ", argv[0]);
139 fprintf (stderr, format, arg);
140 fflush (stderr);
141 test_exit (SHELL_BOOLEAN (FALSE));
144 /* A wrapper for stat () which disallows pathnames that are empty strings. */
145 static int
146 test_stat (char *path, struct stat *finfo)
148 if (*path == '\0')
150 errno = ENOENT;
151 return (-1);
153 return (stat (path, finfo));
156 /* Do the same thing access(2) does, but use the effective uid and gid,
157 and don't make the mistake of telling root that any file is executable.
158 But this loses when the containing filesystem is mounted e.g. read-only. */
159 static int
160 eaccess (char *path, int mode)
162 struct stat st;
163 static int euid = -1;
165 if (test_stat (path, &st) < 0)
166 return (-1);
168 if (euid == -1)
169 euid = geteuid ();
171 if (euid == 0)
173 /* Root can read or write any file. */
174 if (mode != X_OK)
175 return (0);
177 /* Root can execute any file that has any one of the execute
178 bits set. */
179 if (st.st_mode & S_IXUGO)
180 return (0);
183 if (st.st_uid == euid) /* owner */
184 mode <<= 6;
185 else if (group_member (st.st_gid))
186 mode <<= 3;
188 if (st.st_mode & mode)
189 return (0);
191 return (-1);
194 /* Increment our position in the argument list. Check that we're not
195 past the end of the argument list. This check is supressed if the
196 argument is FALSE. Made a macro for efficiency. */
197 #define advance(f) \
198 do \
200 ++pos; \
201 if ((f) && pos >= argc) \
202 beyond (); \
204 while (0)
206 #if !defined (advance)
207 static int
208 advance (int f)
210 ++pos;
212 if (f && pos >= argc)
213 beyond ();
215 #endif /* advance */
217 #define unary_advance() \
218 do \
220 advance (1); \
221 ++pos; \
223 while (0)
226 * beyond - call when we're beyond the end of the argument list (an
227 * error condition)
229 static void
230 beyond (void)
232 test_syntax_error (_("argument expected\n"), NULL);
235 /* Syntax error for when an integer argument was expected, but
236 something else was found. */
237 static void
238 integer_expected_error (char *pch)
240 test_syntax_error (_("integer expression expected %s\n"), pch);
243 /* Return nonzero if the characters pointed to by STRING constitute a
244 valid number. Stuff the converted number into RESULT if RESULT is
245 a non-null pointer to a long. */
246 static int
247 isint (register char *string, long int *result)
249 int sign;
250 long value;
252 sign = 1;
253 value = 0;
255 if (result)
256 *result = 0;
258 /* Skip leading whitespace characters. */
259 while (whitespace (*string))
260 string++;
262 if (!*string)
263 return (0);
265 /* We allow leading `-' or `+'. */
266 if (*string == '-' || *string == '+')
268 if (!digit (string[1]))
269 return (0);
271 if (*string == '-')
272 sign = -1;
274 string++;
277 while (digit (*string))
279 if (result)
280 value = (value * 10) + digit_value (*string);
281 string++;
284 /* Skip trailing whitespace, if any. */
285 while (whitespace (*string))
286 string++;
288 /* Error if not at end of string. */
289 if (*string)
290 return (0);
292 if (result)
294 value *= sign;
295 *result = value;
298 return (1);
301 /* Find the modification time of FILE, and stuff it into AGE, a pointer
302 to a long. Return nonzero if successful, else zero. */
303 static int
304 age_of (char *filename, long int *age)
306 struct stat finfo;
308 if (test_stat (filename, &finfo) < 0)
309 return (0);
311 if (age)
312 *age = finfo.st_mtime;
314 return (1);
318 * term - parse a term and return 1 or 0 depending on whether the term
319 * evaluates to true or false, respectively.
321 * term ::=
322 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
323 * '-'('L'|'x') filename
324 * '-t' [ int ]
325 * '-'('z'|'n') string
326 * string
327 * string ('!='|'=') string
328 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
329 * file '-'(nt|ot|ef) file
330 * '(' <expr> ')'
331 * int ::=
332 * '-l' string
333 * positive and negative integers
335 static int
336 term (void)
338 int value;
340 if (pos >= argc)
341 beyond ();
343 /* Deal with leading "not"'s. */
344 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
346 value = FALSE;
347 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
349 advance (1);
350 value ^= (TRUE);
353 return (value ^ (term ()));
356 /* A paren-bracketed argument. */
357 if (argv[pos][0] == '(' && !argv[pos][1])
359 advance (1);
360 value = expr ();
361 if (!argv[pos])
362 test_syntax_error (_("')' expected\n"), NULL);
363 else
364 if (argv[pos][0] != ')' || argv[pos][1])
365 test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
366 advance (0);
367 return (TRUE == (value));
370 /* are there enough arguments left that this could be dyadic? */
371 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
372 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
373 value = binary_operator ();
375 /* Might be a switch type argument */
376 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
378 if (unop (argv[pos][1]))
379 value = unary_operator ();
380 else
381 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
383 else
385 value = (argv[pos][0] != '\0');
386 advance (0);
389 return (value);
392 static int
393 binary_operator (void)
395 register int op;
396 struct stat stat_buf, stat_spare;
397 long int l, r, value;
398 /* Are the left and right integer expressions of the form '-l string'? */
399 int l_is_l, r_is_l;
401 if (strcmp (argv[pos], "-l") == 0)
403 l_is_l = 1;
404 op = pos + 2;
406 /* Make sure that OP is still a valid binary operator. */
407 if ((op >= argc - 1) || (binop (argv[op]) == 0))
408 test_syntax_error (_("%s: binary operator expected\n"), argv[op]);
410 advance (0);
412 else
414 l_is_l = 0;
415 op = pos + 1;
418 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
420 r_is_l = 1;
421 advance (0);
423 else
424 r_is_l = 0;
426 if (argv[op][0] == '-')
428 /* check for eq, nt, and stuff */
429 switch (argv[op][1])
431 default:
432 break;
434 case 'l':
435 if (argv[op][2] == 't' && !argv[op][3])
437 /* lt */
438 if (l_is_l)
439 l = strlen (argv[op - 1]);
440 else
442 if (!isint (argv[op - 1], &l))
443 integer_expected_error (_("before -lt"));
446 if (r_is_l)
447 r = strlen (argv[op + 2]);
448 else
450 if (!isint (argv[op + 1], &r))
451 integer_expected_error (_("after -lt"));
453 pos += 3;
454 return (TRUE == (l < r));
457 if (argv[op][2] == 'e' && !argv[op][3])
459 /* le */
460 if (l_is_l)
461 l = strlen (argv[op - 1]);
462 else
464 if (!isint (argv[op - 1], &l))
465 integer_expected_error (_("before -le"));
467 if (r_is_l)
468 r = strlen (argv[op + 2]);
469 else
471 if (!isint (argv[op + 1], &r))
472 integer_expected_error (_("after -le"));
474 pos += 3;
475 return (TRUE == (l <= r));
477 break;
479 case 'g':
480 if (argv[op][2] == 't' && !argv[op][3])
482 /* gt integer greater than */
483 if (l_is_l)
484 l = strlen (argv[op - 1]);
485 else
487 if (!isint (argv[op - 1], &l))
488 integer_expected_error (_("before -gt"));
490 if (r_is_l)
491 r = strlen (argv[op + 2]);
492 else
494 if (!isint (argv[op + 1], &r))
495 integer_expected_error (_("after -gt"));
497 pos += 3;
498 return (TRUE == (l > r));
501 if (argv[op][2] == 'e' && !argv[op][3])
503 /* ge - integer greater than or equal to */
504 if (l_is_l)
505 l = strlen (argv[op - 1]);
506 else
508 if (!isint (argv[op - 1], &l))
509 integer_expected_error (_("before -ge"));
511 if (r_is_l)
512 r = strlen (argv[op + 2]);
513 else
515 if (!isint (argv[op + 1], &r))
516 integer_expected_error (_("after -ge"));
518 pos += 3;
519 return (TRUE == (l >= r));
521 break;
523 case 'n':
524 if (argv[op][2] == 't' && !argv[op][3])
526 /* nt - newer than */
527 pos += 3;
528 if (l_is_l || r_is_l)
529 test_syntax_error (_("-nt does not accept -l\n"), NULL);
530 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
531 return (TRUE == (l > r));
532 else
533 return (FALSE);
536 if (argv[op][2] == 'e' && !argv[op][3])
538 /* ne - integer not equal */
539 if (l_is_l)
540 l = strlen (argv[op - 1]);
541 else
543 if (!isint (argv[op - 1], &l))
544 integer_expected_error (_("before -ne"));
546 if (r_is_l)
547 r = strlen (argv[op + 2]);
548 else
550 if (!isint (argv[op + 1], &r))
551 integer_expected_error (_("after -ne"));
553 pos += 3;
554 return (TRUE == (l != r));
556 break;
558 case 'e':
559 if (argv[op][2] == 'q' && !argv[op][3])
561 /* eq - integer equal */
562 if (l_is_l)
563 l = strlen (argv[op - 1]);
564 else
566 if (!isint (argv[op - 1], &l))
567 integer_expected_error (_("before -eq"));
569 if (r_is_l)
570 r = strlen (argv[op + 2]);
571 else
573 if (!isint (argv[op + 1], &r))
574 integer_expected_error (_("after -eq"));
576 pos += 3;
577 return (TRUE == (l == r));
580 if (argv[op][2] == 'f' && !argv[op][3])
582 /* ef - hard link? */
583 pos += 3;
584 if (l_is_l || r_is_l)
585 test_syntax_error (_("-ef does not accept -l\n"), NULL);
586 if (stat (argv[op - 1], &stat_buf) < 0)
587 return (FALSE);
588 if (stat (argv[op + 1], &stat_spare) < 0)
589 return (FALSE);
590 return (TRUE ==
591 (stat_buf.st_dev == stat_spare.st_dev &&
592 stat_buf.st_ino == stat_spare.st_ino));
594 break;
596 case 'o':
597 if ('t' == argv[op][2] && '\000' == argv[op][3])
599 /* ot - older than */
600 pos += 3;
601 if (l_is_l || r_is_l)
602 test_syntax_error (_("-nt does not accept -l\n"), NULL);
603 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
604 return (TRUE == (l < r));
605 return (FALSE);
607 break;
609 test_syntax_error (_("unknown binary operator"), argv[op]);
612 if (argv[op][0] == '=' && !argv[op][1])
614 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
615 pos += 3;
616 return (TRUE == value);
619 if (strcmp (argv[op], "!=") == 0)
621 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
622 pos += 3;
623 return (TRUE == value);
626 /* Not reached. */
627 abort ();
630 static int
631 unary_operator (void)
633 long r, value;
634 struct stat stat_buf;
636 switch (argv[pos][1])
638 default:
639 return (FALSE);
641 /* All of the following unary operators use unary_advance (), which
642 checks to make sure that there is an argument, and then advances
643 pos right past it. This means that pos - 1 is the location of the
644 argument. */
646 case 'a': /* file exists in the file system? */
647 case 'e':
648 unary_advance ();
649 value = -1 != test_stat (argv[pos - 1], &stat_buf);
650 return (TRUE == value);
652 case 'r': /* file is readable? */
653 unary_advance ();
654 value = -1 != eaccess (argv[pos - 1], R_OK);
655 return (TRUE == value);
657 case 'w': /* File is writeable? */
658 unary_advance ();
659 value = -1 != eaccess (argv[pos - 1], W_OK);
660 return (TRUE == value);
662 case 'x': /* File is executable? */
663 unary_advance ();
664 value = -1 != eaccess (argv[pos - 1], X_OK);
665 return (TRUE == value);
667 case 'O': /* File is owned by you? */
668 unary_advance ();
669 if (test_stat (argv[pos - 1], &stat_buf) < 0)
670 return (FALSE);
672 return (TRUE == (geteuid () == stat_buf.st_uid));
674 case 'G': /* File is owned by your group? */
675 unary_advance ();
676 if (test_stat (argv[pos - 1], &stat_buf) < 0)
677 return (FALSE);
679 return (TRUE == (getegid () == stat_buf.st_gid));
681 case 'f': /* File is a file? */
682 unary_advance ();
683 if (test_stat (argv[pos - 1], &stat_buf) < 0)
684 return (FALSE);
686 /* Under POSIX, -f is true if the given file exists
687 and is a regular file. */
688 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
689 (0 == (stat_buf.st_mode & S_IFMT))));
691 case 'd': /* File is a directory? */
692 unary_advance ();
693 if (test_stat (argv[pos - 1], &stat_buf) < 0)
694 return (FALSE);
696 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
698 case 's': /* File has something in it? */
699 unary_advance ();
700 if (test_stat (argv[pos - 1], &stat_buf) < 0)
701 return (FALSE);
703 return (TRUE == (stat_buf.st_size > (off_t) 0));
705 case 'S': /* File is a socket? */
706 #if !defined (S_ISSOCK)
707 return (FALSE);
708 #else
709 unary_advance ();
711 if (test_stat (argv[pos - 1], &stat_buf) < 0)
712 return (FALSE);
714 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
715 #endif /* S_ISSOCK */
717 case 'c': /* File is character special? */
718 unary_advance ();
719 if (test_stat (argv[pos - 1], &stat_buf) < 0)
720 return (FALSE);
722 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
724 case 'b': /* File is block special? */
725 unary_advance ();
726 if (test_stat (argv[pos - 1], &stat_buf) < 0)
727 return (FALSE);
729 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
731 case 'p': /* File is a named pipe? */
732 unary_advance ();
733 #ifndef S_ISFIFO
734 return (FALSE);
735 #else
736 if (test_stat (argv[pos - 1], &stat_buf) < 0)
737 return (FALSE);
738 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
739 #endif /* S_ISFIFO */
741 case 'L': /* Same as -h */
742 /*FALLTHROUGH*/
744 case 'h': /* File is a symbolic link? */
745 unary_advance ();
746 #ifndef S_ISLNK
747 return (FALSE);
748 #else
749 /* An empty filename is not a valid pathname. */
750 if ((argv[pos - 1][0] == '\0') ||
751 (lstat (argv[pos - 1], &stat_buf) < 0))
752 return (FALSE);
754 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
755 #endif /* S_IFLNK */
757 case 'u': /* File is setuid? */
758 unary_advance ();
759 #ifndef S_ISUID
760 return (FALSE);
761 #else
762 if (test_stat (argv[pos - 1], &stat_buf) < 0)
763 return (FALSE);
765 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
766 #endif
768 case 'g': /* File is setgid? */
769 unary_advance ();
770 #ifndef S_ISGID
771 return (FALSE);
772 #else
773 if (test_stat (argv[pos - 1], &stat_buf) < 0)
774 return (FALSE);
776 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
777 #endif
779 case 'k': /* File has sticky bit set? */
780 unary_advance ();
781 if (test_stat (argv[pos - 1], &stat_buf) < 0)
782 return (FALSE);
783 #ifndef S_ISVTX
784 /* This is not Posix, and is not defined on some Posix systems. */
785 return (FALSE);
786 #else
787 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
788 #endif
790 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
791 advance (0);
792 if (pos < argc && isint (argv[pos], &r))
794 advance (0);
795 return (TRUE == (isatty ((int) r)));
797 return (TRUE == (isatty (1)));
799 case 'n': /* True if arg has some length. */
800 unary_advance ();
801 return (TRUE == (argv[pos - 1][0] != 0));
803 case 'z': /* True if arg has no length. */
804 unary_advance ();
805 return (TRUE == (argv[pos - 1][0] == '\0'));
810 * and:
811 * term
812 * term '-a' and
814 static int
815 and (void)
817 int value;
819 value = term ();
820 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
822 advance (0);
823 value = TRUTH_AND (value, and ());
825 return (TRUE == value);
829 * or:
830 * and
831 * and '-o' or
833 static int
834 or (void)
836 int value;
838 value = and ();
840 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
842 advance (0);
843 value = TRUTH_OR (value, or ());
846 return (TRUE == value);
850 * expr:
851 * or
853 static int
854 expr (void)
856 if (pos >= argc)
857 beyond ();
859 return (FALSE ^ (or ())); /* Same with this. */
862 /* Return TRUE if S is one of the test command's binary operators. */
863 static int
864 binop (char *s)
866 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
867 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
868 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
869 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
872 /* Return nonzero if OP is one of the test command's unary operators. */
873 static int
874 unop (int op)
876 return (member (op, "abcdefgkLhprsStuwxOGnz"));
879 static int
880 two_arguments (void)
882 int value;
884 if (STREQ (argv[pos], "!"))
885 value = strlen (argv[pos+1]) == 0;
886 else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
888 if (unop (argv[pos][1]))
889 value = unary_operator ();
890 else
891 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
893 else
894 beyond ();
895 return (value);
898 static int
899 three_arguments (void)
901 int value;
903 if (STREQ (argv[pos], "!"))
905 advance (1);
906 value = !two_arguments ();
908 else if (binop (argv[pos+1]))
910 value = binary_operator ();
911 pos = argc;
913 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
914 (argv[pos][0] == '('))
915 value = expr ();
916 else
917 test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
918 return (value);
921 /* This is an implementation of a Posix.2 proposal by David Korn. */
922 static int
923 posixtest (void)
925 int value;
927 switch (argc - 1) /* one extra passed in */
929 case 0:
930 value = FALSE;
931 pos = argc;
932 break;
934 case 1:
935 value = strlen (argv[1]) != 0;
936 pos = argc;
937 break;
939 case 2:
940 value = two_arguments ();
941 pos = argc;
942 break;
944 case 3:
945 value = three_arguments ();
946 break;
948 case 4:
949 if (STREQ (argv[pos], "!"))
951 advance (1);
952 value = !three_arguments ();
953 break;
955 /* FALLTHROUGH */
956 case 5:
957 default:
958 value = expr ();
961 return (value);
964 #if defined (TEST_STANDALONE)
965 #include "long-options.h"
967 static void
968 usage (int status)
970 if (status != 0)
971 fprintf (stderr, _("Try `%s --help' for more information.\n"),
972 program_name);
973 else
975 printf (_("\
976 Usage: %s EXPRESSION\n\
977 or: [ EXPRESSION ]\n\
978 or: %s OPTION\n\
980 program_name, program_name);
981 printf (_("\
982 Exit with the status determined by EXPRESSION.\n\
984 --help display this help and exit\n\
985 --version output version information and exit\n\
987 EXPRESSION is true or false and sets exit status. It is one of:\n\
988 "));
989 printf (_("\
991 ( EXPRESSION ) EXPRESSION is true\n\
992 ! EXPRESSION EXPRESSION is false\n\
993 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
994 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
996 [-n] STRING the length of STRING is nonzero\n\
997 -z STRING the length of STRING is zero\n\
998 STRING1 = STRING2 the strings are equal\n\
999 STRING1 != STRING2 the strings are not equal\n\
1001 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1002 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1003 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1004 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1005 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1006 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1007 "));
1008 printf (_("\
1010 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1011 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1012 FILE1 -ot FILE2 FILE1 is older than FILE2\n\
1014 -G FILE FILE exists and is owned by the effective group ID\n\
1015 -L FILE FILE exists and is a symbolic link\n\
1016 -O FILE FILE exists and is owned by the effective user ID\n\
1017 -S FILE FILE exists and is a socket\n\
1018 -b FILE FILE exists and is block special\n\
1019 -c FILE FILE exists and is character special\n\
1020 -d FILE FILE exists and is a directory\n\
1021 -e FILE FILE exists\n\
1022 -f FILE FILE exists and is a regular file\n\
1023 -g FILE FILE exists and is set-group-ID\n\
1024 -k FILE FILE exists and has its sticky bit set\n\
1025 -p FILE FILE exists and is a named pipe\n\
1026 -r FILE FILE exists and is readable\n\
1027 -s FILE FILE exists and has a size greater than zero\n\
1028 -t [FD] file descriptor FD (stdout by default) is opened on a terminal\n\
1029 -u FILE FILE exists and its set-user-ID bit is set\n\
1030 -w FILE FILE exists and is writable\n\
1031 -x FILE FILE exists and is executable\n\
1032 "));
1033 printf (_("\
1035 Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
1036 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1037 "));
1039 exit (status);
1041 #endif /* TEST_STANDALONE */
1043 #if !defined (TEST_STANDALONE)
1044 # define main test_command
1045 #endif
1048 * [:
1049 * '[' expr ']'
1050 * test:
1051 * test expr
1054 main (int margc, char **margv)
1056 int value;
1058 #if !defined (TEST_STANDALONE)
1059 int code;
1061 code = setjmp (test_exit_buf);
1063 if (code)
1064 return (test_error_return);
1065 #else /* TEST_STANDALONE */
1066 program_name = margv[0];
1067 setlocale (LC_ALL, "");
1068 bindtextdomain (PACKAGE, LOCALEDIR);
1069 textdomain (PACKAGE);
1070 #endif /* TEST_STANDALONE */
1072 argv = margv;
1074 if (margv[0] && strcmp (margv[0], "[") == 0)
1076 parse_long_options (argc, argv, COMMAND_NAME, PACKAGE_VERSION, usage);
1078 --margc;
1080 if (margc < 2)
1081 test_exit (SHELL_BOOLEAN (FALSE));
1083 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1084 test_syntax_error (_("missing `]'\n"), NULL);
1087 argc = margc;
1088 pos = 1;
1090 if (pos >= argc)
1091 test_exit (SHELL_BOOLEAN (FALSE));
1093 parse_long_options (argc, argv, COMMAND_NAME, PACKAGE_VERSION, usage);
1094 value = posixtest ();
1096 if (pos != argc)
1097 test_syntax_error (_("too many arguments\n"), NULL);
1099 test_exit (SHELL_BOOLEAN (value));