1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987-1999 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
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
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 /* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
24 the shell builtin version. */
25 /* #define TEST_STANDALONE */
29 #include <sys/types.h>
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "test"
34 #define TEST_STANDALONE 1
36 #if !defined (TEST_STANDALONE)
38 # include "posixstat.h"
39 # include "filecntl.h"
40 #else /* TEST_STANDALONE */
42 # include "group-member.h"
44 # if !defined (S_IXUGO)
47 # if defined (_POSIX_VERSION)
49 # else /* !_POSIX_VERSION */
50 # include <sys/param.h>
51 # endif /* _POSIX_VERSION */
52 # define whitespace(c) (((c) == ' ') || ((c) == '\t'))
53 # define digit(c) ((c) >= '0' && (c) <= '9')
54 # define digit_value(c) ((c) - '0')
56 #endif /* TEST_STANDALONE */
58 #if !defined (_POSIX_VERSION)
59 # include <sys/file.h>
60 #endif /* !_POSIX_VERSION */
68 #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
71 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
74 extern gid_t getegid ();
75 extern uid_t geteuid ();
84 /* This name is used solely when printing --version information. */
85 #define PROGRAM_NAME "test"
87 /* The following few defines control the truth and false output of each stage.
88 TRUE and FALSE are what we use to compute the final output value.
89 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
90 TRUTH_OR is how to do logical or with TRUE and FALSE.
91 TRUTH_AND is how to do logical and with TRUE and FALSE..
92 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
93 SHELL_BOOLEAN = (!value). */
96 #define SHELL_BOOLEAN(value) (!(value))
97 #define TRUTH_OR(a, b) ((a) | (b))
98 #define TRUTH_AND(a, b) ((a) & (b))
100 #if defined (TEST_STANDALONE)
101 # define test_exit(val) exit (val)
103 static jmp_buf test_exit_buf;
104 static int test_error_return = 0;
105 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
106 #endif /* !TEST_STANDALONE */
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 PARAMS ((int op));
113 static int binop PARAMS ((char *s));
114 static int unary_operator PARAMS ((void));
115 static int binary_operator PARAMS ((void));
116 static int two_arguments PARAMS ((void));
117 static int three_arguments PARAMS ((void));
118 static int posixtest PARAMS ((void));
120 static int expr PARAMS ((void));
121 static int term PARAMS ((void));
122 static int and PARAMS ((void));
123 static int or PARAMS ((void));
125 static void test_syntax_error PARAMS ((char *format, char *arg))
127 static void beyond PARAMS ((void)) ATTRIBUTE_NORETURN;
130 test_syntax_error (char *format, char *arg)
132 fprintf (stderr, "%s: ", argv[0]);
133 fprintf (stderr, format, arg);
135 test_exit (SHELL_BOOLEAN (FALSE));
138 /* A wrapper for stat () which disallows pathnames that are empty strings. */
140 test_stat (char *path, struct stat *finfo)
147 return (stat (path, finfo));
150 /* Do the same thing access(2) does, but use the effective uid and gid,
151 and don't make the mistake of telling root that any file is executable.
152 But this loses when the containing filesystem is mounted e.g. read-only. */
154 eaccess (char *path, int mode)
157 static int euid = -1;
159 if (test_stat (path, &st) < 0)
167 /* Root can read or write any file. */
171 /* Root can execute any file that has any one of the execute
173 if (st.st_mode & S_IXUGO)
177 if (st.st_uid == euid) /* owner */
179 else if (group_member (st.st_gid))
182 if (st.st_mode & mode)
188 /* Increment our position in the argument list. Check that we're not
189 past the end of the argument list. This check is supressed if the
190 argument is FALSE. Made a macro for efficiency. */
195 if ((f) && pos >= argc) \
200 #if !defined (advance)
206 if (f && pos >= argc)
211 #define unary_advance() \
220 * beyond - call when we're beyond the end of the argument list (an
226 test_syntax_error (_("argument expected\n"), NULL);
229 /* Syntax error for when an integer argument was expected, but
230 something else was found. */
232 integer_expected_error (char *pch)
234 test_syntax_error (_("integer expression expected %s\n"), pch);
237 /* Return nonzero if the characters pointed to by STRING constitute a
238 valid number. Stuff the converted number into RESULT if RESULT is
239 a non-null pointer to a long. */
241 isint (register char *string, long int *result)
252 /* Skip leading whitespace characters. */
253 while (whitespace (*string))
259 /* We allow leading `-' or `+'. */
260 if (*string == '-' || *string == '+')
262 if (!digit (string[1]))
271 while (digit (*string))
274 value = (value * 10) + digit_value (*string);
278 /* Skip trailing whitespace, if any. */
279 while (whitespace (*string))
282 /* Error if not at end of string. */
295 /* Find the modification time of FILE, and stuff it into AGE, a pointer
296 to a long. Return nonzero if successful, else zero. */
298 age_of (char *filename, long int *age)
302 if (test_stat (filename, &finfo) < 0)
306 *age = finfo.st_mtime;
312 * term - parse a term and return 1 or 0 depending on whether the term
313 * evaluates to true or false, respectively.
316 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
317 * '-'('L'|'x') filename
319 * '-'('z'|'n') string
321 * string ('!='|'=') string
322 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
323 * file '-'(nt|ot|ef) file
327 * positive and negative integers
337 /* Deal with leading "not"'s. */
338 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
341 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
347 return (value ^ (term ()));
350 /* A paren-bracketed argument. */
351 if (argv[pos][0] == '(' && !argv[pos][1])
356 test_syntax_error (_("')' expected\n"), NULL);
358 if (argv[pos][0] != ')' || argv[pos][1])
359 test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
361 return (TRUE == (value));
364 /* are there enough arguments left that this could be dyadic? */
365 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
366 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
367 value = binary_operator ();
369 /* Might be a switch type argument */
370 else if ('-' == argv[pos][0] && argv[pos][1] && 0 == argv[pos][2])
372 if (unop (argv[pos][1]))
373 value = unary_operator ();
375 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
379 value = (argv[pos][0] != '\0');
387 binary_operator (void)
390 struct stat stat_buf, stat_spare;
391 long int l, r, value;
392 /* Are the left and right integer expressions of the form '-l string'? */
395 if (strcmp (argv[pos], "-l") == 0)
400 /* Make sure that OP is still a valid binary operator. */
401 if ((op >= argc - 1) || (binop (argv[op]) == 0))
402 test_syntax_error (_("%s: binary operator expected\n"), argv[op]);
412 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
420 if (argv[op][0] == '-')
422 /* check for eq, nt, and stuff */
429 if (argv[op][2] == 't' && !argv[op][3])
433 l = strlen (argv[op - 1]);
436 if (!isint (argv[op - 1], &l))
437 integer_expected_error (_("before -lt"));
441 r = strlen (argv[op + 2]);
444 if (!isint (argv[op + 1], &r))
445 integer_expected_error (_("after -lt"));
448 return (TRUE == (l < r));
451 if (argv[op][2] == 'e' && !argv[op][3])
455 l = strlen (argv[op - 1]);
458 if (!isint (argv[op - 1], &l))
459 integer_expected_error (_("before -le"));
462 r = strlen (argv[op + 2]);
465 if (!isint (argv[op + 1], &r))
466 integer_expected_error (_("after -le"));
469 return (TRUE == (l <= r));
474 if (argv[op][2] == 't' && !argv[op][3])
476 /* gt integer greater than */
478 l = strlen (argv[op - 1]);
481 if (!isint (argv[op - 1], &l))
482 integer_expected_error (_("before -gt"));
485 r = strlen (argv[op + 2]);
488 if (!isint (argv[op + 1], &r))
489 integer_expected_error (_("after -gt"));
492 return (TRUE == (l > r));
495 if (argv[op][2] == 'e' && !argv[op][3])
497 /* ge - integer greater than or equal to */
499 l = strlen (argv[op - 1]);
502 if (!isint (argv[op - 1], &l))
503 integer_expected_error (_("before -ge"));
506 r = strlen (argv[op + 2]);
509 if (!isint (argv[op + 1], &r))
510 integer_expected_error (_("after -ge"));
513 return (TRUE == (l >= r));
518 if (argv[op][2] == 't' && !argv[op][3])
520 /* nt - newer than */
522 if (l_is_l || r_is_l)
523 test_syntax_error (_("-nt does not accept -l\n"), NULL);
524 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
525 return (TRUE == (l > r));
530 if (argv[op][2] == 'e' && !argv[op][3])
532 /* ne - integer not equal */
534 l = strlen (argv[op - 1]);
537 if (!isint (argv[op - 1], &l))
538 integer_expected_error (_("before -ne"));
541 r = strlen (argv[op + 2]);
544 if (!isint (argv[op + 1], &r))
545 integer_expected_error (_("after -ne"));
548 return (TRUE == (l != r));
553 if (argv[op][2] == 'q' && !argv[op][3])
555 /* eq - integer equal */
557 l = strlen (argv[op - 1]);
560 if (!isint (argv[op - 1], &l))
561 integer_expected_error (_("before -eq"));
564 r = strlen (argv[op + 2]);
567 if (!isint (argv[op + 1], &r))
568 integer_expected_error (_("after -eq"));
571 return (TRUE == (l == r));
574 if (argv[op][2] == 'f' && !argv[op][3])
576 /* ef - hard link? */
578 if (l_is_l || r_is_l)
579 test_syntax_error (_("-ef does not accept -l\n"), NULL);
580 if (stat (argv[op - 1], &stat_buf) < 0)
582 if (stat (argv[op + 1], &stat_spare) < 0)
585 (stat_buf.st_dev == stat_spare.st_dev &&
586 stat_buf.st_ino == stat_spare.st_ino));
591 if ('t' == argv[op][2] && '\000' == argv[op][3])
593 /* ot - older than */
595 if (l_is_l || r_is_l)
596 test_syntax_error (_("-nt does not accept -l\n"), NULL);
597 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
598 return (TRUE == (l < r));
603 test_syntax_error (_("unknown binary operator"), argv[op]);
606 if (argv[op][0] == '=' && !argv[op][1])
608 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
610 return (TRUE == value);
613 if (strcmp (argv[op], "!=") == 0)
615 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
617 return (TRUE == value);
625 unary_operator (void)
628 struct stat stat_buf;
630 switch (argv[pos][1])
635 /* All of the following unary operators use unary_advance (), which
636 checks to make sure that there is an argument, and then advances
637 pos right past it. This means that pos - 1 is the location of the
640 case 'a': /* file exists in the file system? */
643 value = -1 != test_stat (argv[pos - 1], &stat_buf);
644 return (TRUE == value);
646 case 'r': /* file is readable? */
648 value = -1 != eaccess (argv[pos - 1], R_OK);
649 return (TRUE == value);
651 case 'w': /* File is writable? */
653 value = -1 != eaccess (argv[pos - 1], W_OK);
654 return (TRUE == value);
656 case 'x': /* File is executable? */
658 value = -1 != eaccess (argv[pos - 1], X_OK);
659 return (TRUE == value);
661 case 'O': /* File is owned by you? */
663 if (test_stat (argv[pos - 1], &stat_buf) < 0)
666 return (TRUE == (geteuid () == stat_buf.st_uid));
668 case 'G': /* File is owned by your group? */
670 if (test_stat (argv[pos - 1], &stat_buf) < 0)
673 return (TRUE == (getegid () == stat_buf.st_gid));
675 case 'f': /* File is a file? */
677 if (test_stat (argv[pos - 1], &stat_buf) < 0)
680 /* Under POSIX, -f is true if the given file exists
681 and is a regular file. */
682 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
683 (0 == (stat_buf.st_mode & S_IFMT))));
685 case 'd': /* File is a directory? */
687 if (test_stat (argv[pos - 1], &stat_buf) < 0)
690 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
692 case 's': /* File has something in it? */
694 if (test_stat (argv[pos - 1], &stat_buf) < 0)
697 return (TRUE == (stat_buf.st_size > (off_t) 0));
699 case 'S': /* File is a socket? */
700 #if !defined (S_ISSOCK)
705 if (test_stat (argv[pos - 1], &stat_buf) < 0)
708 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
709 #endif /* S_ISSOCK */
711 case 'c': /* File is character special? */
713 if (test_stat (argv[pos - 1], &stat_buf) < 0)
716 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
718 case 'b': /* File is block special? */
720 if (test_stat (argv[pos - 1], &stat_buf) < 0)
723 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
725 case 'p': /* File is a named pipe? */
730 if (test_stat (argv[pos - 1], &stat_buf) < 0)
732 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
733 #endif /* S_ISFIFO */
735 case 'L': /* Same as -h */
738 case 'h': /* File is a symbolic link? */
743 /* An empty filename is not a valid pathname. */
744 if ((argv[pos - 1][0] == '\0') ||
745 (lstat (argv[pos - 1], &stat_buf) < 0))
748 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
751 case 'u': /* File is setuid? */
756 if (test_stat (argv[pos - 1], &stat_buf) < 0)
759 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
762 case 'g': /* File is setgid? */
767 if (test_stat (argv[pos - 1], &stat_buf) < 0)
770 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
773 case 'k': /* File has sticky bit set? */
775 if (test_stat (argv[pos - 1], &stat_buf) < 0)
778 /* This is not Posix, and is not defined on some Posix systems. */
781 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
784 case 't': /* File (fd) is a terminal? */
788 if (!isint (argv[pos], &fd))
789 integer_expected_error (_("after -t"));
796 return (TRUE == (isatty ((int) fd)));
798 case 'n': /* True if arg has some length. */
800 return (TRUE == (argv[pos - 1][0] != 0));
802 case 'z': /* True if arg has no length. */
804 return (TRUE == (argv[pos - 1][0] == '\0'));
819 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
822 value = TRUTH_AND (value, and ());
824 return (TRUE == value);
839 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
842 value = TRUTH_OR (value, or ());
845 return (TRUE == value);
858 return (FALSE ^ (or ())); /* Same with this. */
861 /* Return TRUE if S is one of the test command's binary operators. */
865 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
866 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
867 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
868 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
871 /* Return nonzero if OP is one of the test command's unary operators. */
875 return (member (op, "abcdefgkLhprsStuwxOGnz"));
879 one_argument (const char *s)
882 return (TRUE == (isatty (1)));
884 return strlen (s) != 0;
892 if (STREQ (argv[pos], "!"))
893 value = ! one_argument (argv[pos+1]);
894 else if (argv[pos][0] == '-'
895 && argv[pos][1] != '\0'
896 && argv[pos][2] == '\0')
898 if (unop (argv[pos][1]))
899 value = unary_operator ();
901 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
909 three_arguments (void)
913 if (STREQ (argv[pos], "!"))
916 value = !two_arguments ();
918 else if (binop (argv[pos+1]))
920 value = binary_operator ();
923 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
924 (argv[pos][0] == '('))
927 test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
931 /* This is an implementation of a Posix.2 proposal by David Korn. */
937 switch (argc - 1) /* one extra passed in */
945 value = one_argument (argv[1]);
950 value = two_arguments ();
955 value = three_arguments ();
959 if (STREQ (argv[pos], "!"))
962 value = !three_arguments ();
974 #if defined (TEST_STANDALONE)
975 # include "long-options.h"
981 fprintf (stderr, _("Try `%s --help' for more information.\n"),
986 Usage: %s EXPRESSION\n\
987 or: [ EXPRESSION ]\n\
990 program_name, program_name);
992 Exit with the status determined by EXPRESSION.\n\
994 --help display this help and exit\n\
995 --version output version information and exit\n\
997 EXPRESSION is true or false and sets exit status. It is one of:\n\
1001 ( EXPRESSION ) EXPRESSION is true\n\
1002 ! EXPRESSION EXPRESSION is false\n\
1003 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
1004 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
1006 [-n] STRING the length of STRING is nonzero\n\
1007 -z STRING the length of STRING is zero\n\
1008 STRING1 = STRING2 the strings are equal\n\
1009 STRING1 != STRING2 the strings are not equal\n\
1011 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1012 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1013 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1014 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1015 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1016 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1020 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1021 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1022 FILE1 -ot FILE2 FILE1 is older than FILE2\n\
1024 -b FILE FILE exists and is block special\n\
1025 -c FILE FILE exists and is character special\n\
1026 -d FILE FILE exists and is a directory\n\
1027 -e FILE FILE exists\n\
1028 -f FILE FILE exists and is a regular file\n\
1029 -g FILE FILE exists and is set-group-ID\n\
1030 -G FILE FILE exists and is owned by the effective group ID\n\
1031 -k FILE FILE exists and has its sticky bit set\n\
1032 -L FILE FILE exists and is a symbolic link\n\
1033 -O FILE FILE exists and is owned by the effective user ID\n\
1034 -p FILE FILE exists and is a named pipe\n\
1035 -r FILE FILE exists and is readable\n\
1036 -s FILE FILE exists and has a size greater than zero\n\
1037 -S FILE FILE exists and is a socket\n\
1038 -t [FD] file descriptor FD (stdout by default) is opened on a terminal\n\
1039 -u FILE FILE exists and its set-user-ID bit is set\n\
1040 -w FILE FILE exists and is writable\n\
1041 -x FILE FILE exists and is executable\n\
1045 Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
1046 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1048 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
1052 #endif /* TEST_STANDALONE */
1054 #if !defined (TEST_STANDALONE)
1055 # define main test_command
1058 #define AUTHORS "FIXME: ksb and mjb"
1067 main (int margc, char **margv)
1071 #if !defined (TEST_STANDALONE)
1074 code = setjmp (test_exit_buf);
1077 return (test_error_return);
1078 #else /* TEST_STANDALONE */
1079 program_name = margv[0];
1080 setlocale (LC_ALL, "");
1081 bindtextdomain (PACKAGE, LOCALEDIR);
1082 textdomain (PACKAGE);
1083 #endif /* TEST_STANDALONE */
1087 if (margv[0] && strcmp (margv[0], "[") == 0)
1089 /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
1090 if (getenv ("POSIXLY_CORRECT") == NULL)
1091 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1097 test_exit (SHELL_BOOLEAN (FALSE));
1099 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1100 test_syntax_error (_("missing `]'\n"), NULL);
1107 test_exit (SHELL_BOOLEAN (FALSE));
1109 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1111 value = posixtest ();
1114 test_syntax_error (_("too many arguments\n"), NULL);
1116 test_exit (SHELL_BOOLEAN (value));