1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987-1996, 1997, 1998 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)
68 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
71 extern gid_t getegid ();
72 extern uid_t geteuid ();
81 /* This name is used solely when printing --version information. */
82 #define COMMAND_NAME "test"
84 /* The following few defines control the truth and false output of each stage.
85 TRUE and FALSE are what we use to compute the final output value.
86 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
87 TRUTH_OR is how to do logical or with TRUE and FALSE.
88 TRUTH_AND is how to do logical and with TRUE and FALSE..
89 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
90 SHELL_BOOLEAN = (!value). */
93 #define SHELL_BOOLEAN(value) (!(value))
94 #define TRUTH_OR(a, b) ((a) | (b))
95 #define TRUTH_AND(a, b) ((a) & (b))
97 #if defined (TEST_STANDALONE)
98 # define test_exit(val) exit (val)
100 static jmp_buf test_exit_buf;
101 static int test_error_return = 0;
102 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
103 #endif /* !TEST_STANDALONE */
105 static int pos; /* The offset of the current argument in ARGV. */
106 static int argc; /* The number of arguments present in ARGV. */
107 static char **argv; /* The argument list. */
109 static int unop PARAMS ((int op));
110 static int binop PARAMS ((char *s));
111 static int unary_operator PARAMS ((void));
112 static int binary_operator PARAMS ((void));
113 static int two_arguments PARAMS ((void));
114 static int three_arguments PARAMS ((void));
115 static int posixtest PARAMS ((void));
117 static int expr PARAMS ((void));
118 static int term PARAMS ((void));
119 static int and PARAMS ((void));
120 static int or PARAMS ((void));
122 #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
123 && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
124 # define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
126 # define NO_RETURN_ATTRIBUTE /* empty */
129 static void test_syntax_error PARAMS ((char *format, char *arg)) NO_RETURN_ATTRIBUTE;
130 static void beyond PARAMS ((void)) NO_RETURN_ATTRIBUTE;
133 test_syntax_error (char *format, char *arg)
135 fprintf (stderr, "%s: ", argv[0]);
136 fprintf (stderr, format, arg);
138 test_exit (SHELL_BOOLEAN (FALSE));
141 /* A wrapper for stat () which disallows pathnames that are empty strings. */
143 test_stat (char *path, struct stat *finfo)
150 return (stat (path, finfo));
153 /* Do the same thing access(2) does, but use the effective uid and gid,
154 and don't make the mistake of telling root that any file is executable.
155 But this loses when the containing filesystem is mounted e.g. read-only. */
157 eaccess (char *path, int mode)
160 static int euid = -1;
162 if (test_stat (path, &st) < 0)
170 /* Root can read or write any file. */
174 /* Root can execute any file that has any one of the execute
176 if (st.st_mode & S_IXUGO)
180 if (st.st_uid == euid) /* owner */
182 else if (group_member (st.st_gid))
185 if (st.st_mode & mode)
191 /* Increment our position in the argument list. Check that we're not
192 past the end of the argument list. This check is supressed if the
193 argument is FALSE. Made a macro for efficiency. */
198 if ((f) && pos >= argc) \
203 #if !defined (advance)
209 if (f && pos >= argc)
214 #define unary_advance() \
223 * beyond - call when we're beyond the end of the argument list (an
229 test_syntax_error (_("argument expected\n"), NULL);
232 /* Syntax error for when an integer argument was expected, but
233 something else was found. */
235 integer_expected_error (char *pch)
237 test_syntax_error (_("integer expression expected %s\n"), pch);
240 /* Return nonzero if the characters pointed to by STRING constitute a
241 valid number. Stuff the converted number into RESULT if RESULT is
242 a non-null pointer to a long. */
244 isint (register char *string, long int *result)
255 /* Skip leading whitespace characters. */
256 while (whitespace (*string))
262 /* We allow leading `-' or `+'. */
263 if (*string == '-' || *string == '+')
265 if (!digit (string[1]))
274 while (digit (*string))
277 value = (value * 10) + digit_value (*string);
281 /* Skip trailing whitespace, if any. */
282 while (whitespace (*string))
285 /* Error if not at end of string. */
298 /* Find the modification time of FILE, and stuff it into AGE, a pointer
299 to a long. Return nonzero if successful, else zero. */
301 age_of (char *filename, long int *age)
305 if (test_stat (filename, &finfo) < 0)
309 *age = finfo.st_mtime;
315 * term - parse a term and return 1 or 0 depending on whether the term
316 * evaluates to true or false, respectively.
319 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
320 * '-'('L'|'x') filename
322 * '-'('z'|'n') string
324 * string ('!='|'=') string
325 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
326 * file '-'(nt|ot|ef) file
330 * positive and negative integers
340 /* Deal with leading "not"'s. */
341 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
344 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
350 return (value ^ (term ()));
353 /* A paren-bracketed argument. */
354 if (argv[pos][0] == '(' && !argv[pos][1])
359 test_syntax_error (_("')' expected\n"), NULL);
361 if (argv[pos][0] != ')' || argv[pos][1])
362 test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
364 return (TRUE == (value));
367 /* are there enough arguments left that this could be dyadic? */
368 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
369 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
370 value = binary_operator ();
372 /* Might be a switch type argument */
373 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
375 if (unop (argv[pos][1]))
376 value = unary_operator ();
378 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
382 value = (argv[pos][0] != '\0');
390 binary_operator (void)
393 struct stat stat_buf, stat_spare;
394 long int l, r, value;
395 /* Are the left and right integer expressions of the form '-l string'? */
398 if (strcmp (argv[pos], "-l") == 0)
403 /* Make sure that OP is still a valid binary operator. */
404 if ((op >= argc - 1) || (binop (argv[op]) == 0))
405 test_syntax_error (_("%s: binary operator expected\n"), argv[op]);
415 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
423 if (argv[op][0] == '-')
425 /* check for eq, nt, and stuff */
432 if (argv[op][2] == 't' && !argv[op][3])
436 l = strlen (argv[op - 1]);
439 if (!isint (argv[op - 1], &l))
440 integer_expected_error (_("before -lt"));
444 r = strlen (argv[op + 2]);
447 if (!isint (argv[op + 1], &r))
448 integer_expected_error (_("after -lt"));
451 return (TRUE == (l < r));
454 if (argv[op][2] == 'e' && !argv[op][3])
458 l = strlen (argv[op - 1]);
461 if (!isint (argv[op - 1], &l))
462 integer_expected_error (_("before -le"));
465 r = strlen (argv[op + 2]);
468 if (!isint (argv[op + 1], &r))
469 integer_expected_error (_("after -le"));
472 return (TRUE == (l <= r));
477 if (argv[op][2] == 't' && !argv[op][3])
479 /* gt integer greater than */
481 l = strlen (argv[op - 1]);
484 if (!isint (argv[op - 1], &l))
485 integer_expected_error (_("before -gt"));
488 r = strlen (argv[op + 2]);
491 if (!isint (argv[op + 1], &r))
492 integer_expected_error (_("after -gt"));
495 return (TRUE == (l > r));
498 if (argv[op][2] == 'e' && !argv[op][3])
500 /* ge - integer greater than or equal to */
502 l = strlen (argv[op - 1]);
505 if (!isint (argv[op - 1], &l))
506 integer_expected_error (_("before -ge"));
509 r = strlen (argv[op + 2]);
512 if (!isint (argv[op + 1], &r))
513 integer_expected_error (_("after -ge"));
516 return (TRUE == (l >= r));
521 if (argv[op][2] == 't' && !argv[op][3])
523 /* nt - newer than */
525 if (l_is_l || r_is_l)
526 test_syntax_error (_("-nt does not accept -l\n"), NULL);
527 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
528 return (TRUE == (l > r));
533 if (argv[op][2] == 'e' && !argv[op][3])
535 /* ne - integer not equal */
537 l = strlen (argv[op - 1]);
540 if (!isint (argv[op - 1], &l))
541 integer_expected_error (_("before -ne"));
544 r = strlen (argv[op + 2]);
547 if (!isint (argv[op + 1], &r))
548 integer_expected_error (_("after -ne"));
551 return (TRUE == (l != r));
556 if (argv[op][2] == 'q' && !argv[op][3])
558 /* eq - integer equal */
560 l = strlen (argv[op - 1]);
563 if (!isint (argv[op - 1], &l))
564 integer_expected_error (_("before -eq"));
567 r = strlen (argv[op + 2]);
570 if (!isint (argv[op + 1], &r))
571 integer_expected_error (_("after -eq"));
574 return (TRUE == (l == r));
577 if (argv[op][2] == 'f' && !argv[op][3])
579 /* ef - hard link? */
581 if (l_is_l || r_is_l)
582 test_syntax_error (_("-ef does not accept -l\n"), NULL);
583 if (stat (argv[op - 1], &stat_buf) < 0)
585 if (stat (argv[op + 1], &stat_spare) < 0)
588 (stat_buf.st_dev == stat_spare.st_dev &&
589 stat_buf.st_ino == stat_spare.st_ino));
594 if ('t' == argv[op][2] && '\000' == argv[op][3])
596 /* ot - older than */
598 if (l_is_l || r_is_l)
599 test_syntax_error (_("-nt does not accept -l\n"), NULL);
600 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
601 return (TRUE == (l < r));
606 test_syntax_error (_("unknown binary operator"), argv[op]);
609 if (argv[op][0] == '=' && !argv[op][1])
611 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
613 return (TRUE == value);
616 if (strcmp (argv[op], "!=") == 0)
618 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
620 return (TRUE == value);
628 unary_operator (void)
631 struct stat stat_buf;
633 switch (argv[pos][1])
638 /* All of the following unary operators use unary_advance (), which
639 checks to make sure that there is an argument, and then advances
640 pos right past it. This means that pos - 1 is the location of the
643 case 'a': /* file exists in the file system? */
646 value = -1 != test_stat (argv[pos - 1], &stat_buf);
647 return (TRUE == value);
649 case 'r': /* file is readable? */
651 value = -1 != eaccess (argv[pos - 1], R_OK);
652 return (TRUE == value);
654 case 'w': /* File is writable? */
656 value = -1 != eaccess (argv[pos - 1], W_OK);
657 return (TRUE == value);
659 case 'x': /* File is executable? */
661 value = -1 != eaccess (argv[pos - 1], X_OK);
662 return (TRUE == value);
664 case 'O': /* File is owned by you? */
666 if (test_stat (argv[pos - 1], &stat_buf) < 0)
669 return (TRUE == (geteuid () == stat_buf.st_uid));
671 case 'G': /* File is owned by your group? */
673 if (test_stat (argv[pos - 1], &stat_buf) < 0)
676 return (TRUE == (getegid () == stat_buf.st_gid));
678 case 'f': /* File is a file? */
680 if (test_stat (argv[pos - 1], &stat_buf) < 0)
683 /* Under POSIX, -f is true if the given file exists
684 and is a regular file. */
685 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
686 (0 == (stat_buf.st_mode & S_IFMT))));
688 case 'd': /* File is a directory? */
690 if (test_stat (argv[pos - 1], &stat_buf) < 0)
693 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
695 case 's': /* File has something in it? */
697 if (test_stat (argv[pos - 1], &stat_buf) < 0)
700 return (TRUE == (stat_buf.st_size > (off_t) 0));
702 case 'S': /* File is a socket? */
703 #if !defined (S_ISSOCK)
708 if (test_stat (argv[pos - 1], &stat_buf) < 0)
711 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
712 #endif /* S_ISSOCK */
714 case 'c': /* File is character special? */
716 if (test_stat (argv[pos - 1], &stat_buf) < 0)
719 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
721 case 'b': /* File is block special? */
723 if (test_stat (argv[pos - 1], &stat_buf) < 0)
726 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
728 case 'p': /* File is a named pipe? */
733 if (test_stat (argv[pos - 1], &stat_buf) < 0)
735 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
736 #endif /* S_ISFIFO */
738 case 'L': /* Same as -h */
741 case 'h': /* File is a symbolic link? */
746 /* An empty filename is not a valid pathname. */
747 if ((argv[pos - 1][0] == '\0') ||
748 (lstat (argv[pos - 1], &stat_buf) < 0))
751 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
754 case 'u': /* File is setuid? */
759 if (test_stat (argv[pos - 1], &stat_buf) < 0)
762 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
765 case 'g': /* File is setgid? */
770 if (test_stat (argv[pos - 1], &stat_buf) < 0)
773 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
776 case 'k': /* File has sticky bit set? */
778 if (test_stat (argv[pos - 1], &stat_buf) < 0)
781 /* This is not Posix, and is not defined on some Posix systems. */
784 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
787 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
789 if (pos < argc && isint (argv[pos], &r))
792 return (TRUE == (isatty ((int) r)));
794 return (TRUE == (isatty (1)));
796 case 'n': /* True if arg has some length. */
798 return (TRUE == (argv[pos - 1][0] != 0));
800 case 'z': /* True if arg has no length. */
802 return (TRUE == (argv[pos - 1][0] == '\0'));
817 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
820 value = TRUTH_AND (value, and ());
822 return (TRUE == value);
837 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
840 value = TRUTH_OR (value, or ());
843 return (TRUE == value);
856 return (FALSE ^ (or ())); /* Same with this. */
859 /* Return TRUE if S is one of the test command's binary operators. */
863 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
864 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
865 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
866 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
869 /* Return nonzero if OP is one of the test command's unary operators. */
873 return (member (op, "abcdefgkLhprsStuwxOGnz"));
881 if (STREQ (argv[pos], "!"))
882 value = strlen (argv[pos+1]) == 0;
883 else if (argv[pos][0] == '-'
884 && argv[pos][1] != '\0'
885 && argv[pos][2] == '\0')
887 if (unop (argv[pos][1]))
888 value = unary_operator ();
890 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
898 three_arguments (void)
902 if (STREQ (argv[pos], "!"))
905 value = !two_arguments ();
907 else if (binop (argv[pos+1]))
909 value = binary_operator ();
912 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
913 (argv[pos][0] == '('))
916 test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
920 /* This is an implementation of a Posix.2 proposal by David Korn. */
926 switch (argc - 1) /* one extra passed in */
934 value = strlen (argv[1]) != 0;
939 value = two_arguments ();
944 value = three_arguments ();
948 if (STREQ (argv[pos], "!"))
951 value = !three_arguments ();
963 #if defined (TEST_STANDALONE)
964 # include "long-options.h"
970 fprintf (stderr, _("Try `%s --help' for more information.\n"),
975 Usage: %s EXPRESSION\n\
976 or: [ EXPRESSION ]\n\
979 program_name, program_name);
981 Exit with the status determined by EXPRESSION.\n\
983 --help display this help and exit\n\
984 --version output version information and exit\n\
986 EXPRESSION is true or false and sets exit status. It is one of:\n\
990 ( EXPRESSION ) EXPRESSION is true\n\
991 ! EXPRESSION EXPRESSION is false\n\
992 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
993 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
995 [-n] STRING the length of STRING is nonzero\n\
996 -z STRING the length of STRING is zero\n\
997 STRING1 = STRING2 the strings are equal\n\
998 STRING1 != STRING2 the strings are not equal\n\
1000 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1001 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1002 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1003 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1004 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1005 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1009 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1010 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1011 FILE1 -ot FILE2 FILE1 is older than FILE2\n\
1013 -b FILE FILE exists and is block special\n\
1014 -c FILE FILE exists and is character special\n\
1015 -d FILE FILE exists and is a directory\n\
1016 -e FILE FILE exists\n\
1017 -f FILE FILE exists and is a regular file\n\
1018 -g FILE FILE exists and is set-group-ID\n\
1019 -G FILE FILE exists and is owned by the effective group ID\n\
1020 -k FILE FILE exists and has its sticky bit set\n\
1021 -L FILE FILE exists and is a symbolic link\n\
1022 -O FILE FILE exists and is owned by the effective user ID\n\
1023 -p FILE FILE exists and is a named pipe\n\
1024 -r FILE FILE exists and is readable\n\
1025 -s FILE FILE exists and has a size greater than zero\n\
1026 -S FILE FILE exists and is a socket\n\
1027 -t [FD] file descriptor FD (stdout by default) is opened on a terminal\n\
1028 -u FILE FILE exists and its set-user-ID bit is set\n\
1029 -w FILE FILE exists and is writable\n\
1030 -x FILE FILE exists and is executable\n\
1034 Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
1035 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1037 puts (_("\nReport bugs to <sh-utils-bugs@gnu.org>."));
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, GNU_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, GNU_PACKAGE, VERSION, usage);
1096 value = posixtest ();
1099 test_syntax_error (_("too many arguments\n"), NULL);
1101 test_exit (SHELL_BOOLEAN (value));