1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987-1993, 1994, 95, 1996 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 #define TEST_STANDALONE 1
33 #if !defined (TEST_STANDALONE)
35 # include "posixstat.h"
36 # include "filecntl.h"
37 #else /* TEST_STANDALONE */
39 # include "group-member.h"
41 # if !defined (S_IXUGO)
44 # if defined (_POSIX_VERSION)
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')
53 #endif /* TEST_STANDALONE */
55 #if !defined (_POSIX_VERSION)
56 # include <sys/file.h>
57 #endif /* !_POSIX_VERSION */
65 # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
69 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
72 extern gid_t getegid ();
73 extern uid_t geteuid ();
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). */
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)
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 */
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))
129 #define NO_RETURN_ATTRIBUTE /* empty */
132 static void test_syntax_error __P ((char *format, char *arg)) NO_RETURN_ATTRIBUTE;
133 static void beyond __P ((void)) NO_RETURN_ATTRIBUTE;
136 test_syntax_error (char *format, char *arg)
138 fprintf (stderr, "%s: ", argv[0]);
139 fprintf (stderr, format, arg);
141 test_exit (SHELL_BOOLEAN (FALSE));
144 /* A wrapper for stat () which disallows pathnames that are empty strings. */
146 test_stat (char *path, struct stat *finfo)
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. */
160 eaccess (char *path, int mode)
163 static int euid = -1;
165 if (test_stat (path, &st) < 0)
173 /* Root can read or write any file. */
177 /* Root can execute any file that has any one of the execute
179 if (st.st_mode & S_IXUGO)
183 if (st.st_uid == euid) /* owner */
185 else if (group_member (st.st_gid))
188 if (st.st_mode & mode)
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. */
201 if ((f) && pos >= argc) \
206 #if !defined (advance)
212 if (f && pos >= argc)
217 #define unary_advance() \
226 * beyond - call when we're beyond the end of the argument list (an
232 test_syntax_error (_("argument expected\n"), NULL);
235 /* Syntax error for when an integer argument was expected, but
236 something else was found. */
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. */
247 isint (register char *string, long int *result)
258 /* Skip leading whitespace characters. */
259 while (whitespace (*string))
265 /* We allow leading `-' or `+'. */
266 if (*string == '-' || *string == '+')
268 if (!digit (string[1]))
277 while (digit (*string))
280 value = (value * 10) + digit_value (*string);
284 /* Skip trailing whitespace, if any. */
285 while (whitespace (*string))
288 /* Error if not at end of string. */
301 /* Find the modification time of FILE, and stuff it into AGE, a pointer
302 to a long. Return nonzero if successful, else zero. */
304 age_of (char *filename, long int *age)
308 if (test_stat (filename, &finfo) < 0)
312 *age = finfo.st_mtime;
318 * term - parse a term and return 1 or 0 depending on whether the term
319 * evaluates to true or false, respectively.
322 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
323 * '-'('L'|'x') filename
325 * '-'('z'|'n') string
327 * string ('!='|'=') string
328 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
329 * file '-'(nt|ot|ef) file
333 * positive and negative integers
343 /* Deal with leading "not"'s. */
344 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
347 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
353 return (value ^ (term ()));
356 /* A paren-bracketed argument. */
357 if (argv[pos][0] == '(' && !argv[pos][1])
362 test_syntax_error (_("')' expected\n"), NULL);
364 if (argv[pos][0] != ')' || argv[pos][1])
365 test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
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 ();
381 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
385 value = (argv[pos][0] != '\0');
393 binary_operator (void)
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'? */
401 if (strcmp (argv[pos], "-l") == 0)
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]);
418 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
426 if (argv[op][0] == '-')
428 /* check for eq, nt, and stuff */
435 if (argv[op][2] == 't' && !argv[op][3])
439 l = strlen (argv[op - 1]);
442 if (!isint (argv[op - 1], &l))
443 integer_expected_error (_("before -lt"));
447 r = strlen (argv[op + 2]);
450 if (!isint (argv[op + 1], &r))
451 integer_expected_error (_("after -lt"));
454 return (TRUE == (l < r));
457 if (argv[op][2] == 'e' && !argv[op][3])
461 l = strlen (argv[op - 1]);
464 if (!isint (argv[op - 1], &l))
465 integer_expected_error (_("before -le"));
468 r = strlen (argv[op + 2]);
471 if (!isint (argv[op + 1], &r))
472 integer_expected_error (_("after -le"));
475 return (TRUE == (l <= r));
480 if (argv[op][2] == 't' && !argv[op][3])
482 /* gt integer greater than */
484 l = strlen (argv[op - 1]);
487 if (!isint (argv[op - 1], &l))
488 integer_expected_error (_("before -gt"));
491 r = strlen (argv[op + 2]);
494 if (!isint (argv[op + 1], &r))
495 integer_expected_error (_("after -gt"));
498 return (TRUE == (l > r));
501 if (argv[op][2] == 'e' && !argv[op][3])
503 /* ge - integer greater than or equal to */
505 l = strlen (argv[op - 1]);
508 if (!isint (argv[op - 1], &l))
509 integer_expected_error (_("before -ge"));
512 r = strlen (argv[op + 2]);
515 if (!isint (argv[op + 1], &r))
516 integer_expected_error (_("after -ge"));
519 return (TRUE == (l >= r));
524 if (argv[op][2] == 't' && !argv[op][3])
526 /* nt - newer than */
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));
536 if (argv[op][2] == 'e' && !argv[op][3])
538 /* ne - integer not equal */
540 l = strlen (argv[op - 1]);
543 if (!isint (argv[op - 1], &l))
544 integer_expected_error (_("before -ne"));
547 r = strlen (argv[op + 2]);
550 if (!isint (argv[op + 1], &r))
551 integer_expected_error (_("after -ne"));
554 return (TRUE == (l != r));
559 if (argv[op][2] == 'q' && !argv[op][3])
561 /* eq - integer equal */
563 l = strlen (argv[op - 1]);
566 if (!isint (argv[op - 1], &l))
567 integer_expected_error (_("before -eq"));
570 r = strlen (argv[op + 2]);
573 if (!isint (argv[op + 1], &r))
574 integer_expected_error (_("after -eq"));
577 return (TRUE == (l == r));
580 if (argv[op][2] == 'f' && !argv[op][3])
582 /* ef - hard link? */
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)
588 if (stat (argv[op + 1], &stat_spare) < 0)
591 (stat_buf.st_dev == stat_spare.st_dev &&
592 stat_buf.st_ino == stat_spare.st_ino));
597 if ('t' == argv[op][2] && '\000' == argv[op][3])
599 /* ot - older than */
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));
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);
616 return (TRUE == value);
619 if (strcmp (argv[op], "!=") == 0)
621 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
623 return (TRUE == value);
631 unary_operator (void)
634 struct stat stat_buf;
636 switch (argv[pos][1])
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
646 case 'a': /* file exists in the file system? */
649 value = -1 != test_stat (argv[pos - 1], &stat_buf);
650 return (TRUE == value);
652 case 'r': /* file is readable? */
654 value = -1 != eaccess (argv[pos - 1], R_OK);
655 return (TRUE == value);
657 case 'w': /* File is writeable? */
659 value = -1 != eaccess (argv[pos - 1], W_OK);
660 return (TRUE == value);
662 case 'x': /* File is executable? */
664 value = -1 != eaccess (argv[pos - 1], X_OK);
665 return (TRUE == value);
667 case 'O': /* File is owned by you? */
669 if (test_stat (argv[pos - 1], &stat_buf) < 0)
672 return (TRUE == (geteuid () == stat_buf.st_uid));
674 case 'G': /* File is owned by your group? */
676 if (test_stat (argv[pos - 1], &stat_buf) < 0)
679 return (TRUE == (getegid () == stat_buf.st_gid));
681 case 'f': /* File is a file? */
683 if (test_stat (argv[pos - 1], &stat_buf) < 0)
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? */
693 if (test_stat (argv[pos - 1], &stat_buf) < 0)
696 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
698 case 's': /* File has something in it? */
700 if (test_stat (argv[pos - 1], &stat_buf) < 0)
703 return (TRUE == (stat_buf.st_size > (off_t) 0));
705 case 'S': /* File is a socket? */
706 #if !defined (S_ISSOCK)
711 if (test_stat (argv[pos - 1], &stat_buf) < 0)
714 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
715 #endif /* S_ISSOCK */
717 case 'c': /* File is character special? */
719 if (test_stat (argv[pos - 1], &stat_buf) < 0)
722 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
724 case 'b': /* File is block special? */
726 if (test_stat (argv[pos - 1], &stat_buf) < 0)
729 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
731 case 'p': /* File is a named pipe? */
736 if (test_stat (argv[pos - 1], &stat_buf) < 0)
738 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
739 #endif /* S_ISFIFO */
741 case 'L': /* Same as -h */
744 case 'h': /* File is a symbolic link? */
749 /* An empty filename is not a valid pathname. */
750 if ((argv[pos - 1][0] == '\0') ||
751 (lstat (argv[pos - 1], &stat_buf) < 0))
754 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
757 case 'u': /* File is setuid? */
762 if (test_stat (argv[pos - 1], &stat_buf) < 0)
765 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
768 case 'g': /* File is setgid? */
773 if (test_stat (argv[pos - 1], &stat_buf) < 0)
776 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
779 case 'k': /* File has sticky bit set? */
781 if (test_stat (argv[pos - 1], &stat_buf) < 0)
784 /* This is not Posix, and is not defined on some Posix systems. */
787 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
790 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
792 if (pos < argc && isint (argv[pos], &r))
795 return (TRUE == (isatty ((int) r)));
797 return (TRUE == (isatty (1)));
799 case 'n': /* True if arg has some length. */
801 return (TRUE == (argv[pos - 1][0] != 0));
803 case 'z': /* True if arg has no length. */
805 return (TRUE == (argv[pos - 1][0] == '\0'));
820 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
823 value = TRUTH_AND (value, and ());
825 return (TRUE == value);
840 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
843 value = TRUTH_OR (value, or ());
846 return (TRUE == value);
859 return (FALSE ^ (or ())); /* Same with this. */
862 /* Return TRUE if S is one of the test command's binary operators. */
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. */
876 return (member (op, "abcdefgkLhprsStuwxOGnz"));
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 ();
891 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
899 three_arguments (void)
903 if (STREQ (argv[pos], "!"))
906 value = !two_arguments ();
908 else if (binop (argv[pos+1]))
910 value = binary_operator ();
913 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
914 (argv[pos][0] == '('))
917 test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
921 /* This is an implementation of a Posix.2 proposal by David Korn. */
927 switch (argc - 1) /* one extra passed in */
935 value = strlen (argv[1]) != 0;
940 value = two_arguments ();
945 value = three_arguments ();
949 if (STREQ (argv[pos], "!"))
952 value = !three_arguments ();
964 #if defined (TEST_STANDALONE)
965 #include "long-options.h"
971 fprintf (stderr, _("Try `%s --help' for more information.\n"),
976 Usage: %s EXPRESSION\n\
977 or: [ EXPRESSION ]\n\
980 program_name, program_name);
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\
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\
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\
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\
1041 #endif /* TEST_STANDALONE */
1043 #if !defined (TEST_STANDALONE)
1044 # define main test_command
1054 main (int margc, char **margv)
1058 #if !defined (TEST_STANDALONE)
1061 code = setjmp (test_exit_buf);
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 */
1074 if (margv[0] && strcmp (margv[0], "[") == 0)
1076 /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
1077 if (getenv ("POSIXLY_CORRECT") == NULL)
1078 parse_long_options (argc, argv, COMMAND_NAME, PACKAGE_VERSION, usage);
1083 test_exit (SHELL_BOOLEAN (FALSE));
1085 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1086 test_syntax_error (_("missing `]'\n"), NULL);
1093 test_exit (SHELL_BOOLEAN (FALSE));
1095 parse_long_options (argc, argv, COMMAND_NAME, PACKAGE_VERSION, usage);
1096 value = posixtest ();
1099 test_syntax_error (_("too many arguments\n"), NULL);
1101 test_exit (SHELL_BOOLEAN (value));