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
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 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 */
29 #include <sys/types.h>
31 #if !defined (TEST_STANDALONE)
33 # include "posixstat.h"
34 # include "filecntl.h"
35 #else /* TEST_STANDALONE */
38 # include "safe-stat.h"
39 # include "safe-lstat.h"
40 # include "group-member.h"
42 # if !defined (S_IXUGO)
45 # if defined (_POSIX_VERSION)
47 # else /* !_POSIX_VERSION */
48 # include <sys/param.h>
49 # endif /* _POSIX_VERSION */
50 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
51 #define digit(c) ((c) >= '0' && (c) <= '9')
52 #define digit_value(c) ((c) - '0')
54 #endif /* TEST_STANDALONE */
56 #if !defined (_POSIX_VERSION)
57 # include <sys/file.h>
58 #endif /* !_POSIX_VERSION */
66 # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
70 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
73 extern gid_t getgid (), getegid ();
74 extern uid_t geteuid ();
83 /* This name is used solely when printing --version information. */
84 #define COMMAND_NAME "test"
86 /* The following few defines control the truth and false output of each stage.
87 TRUE and FALSE are what we use to compute the final output value.
88 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
89 TRUTH_OR is how to do logical or with TRUE and FALSE.
90 TRUTH_AND is how to do logical and with TRUE and FALSE..
91 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
92 SHELL_BOOLEAN = (!value). */
95 #define SHELL_BOOLEAN(value) (!(value))
96 #define TRUTH_OR(a, b) ((a) | (b))
97 #define TRUTH_AND(a, b) ((a) & (b))
99 #if defined (TEST_STANDALONE)
100 # define test_exit(val) exit (val)
102 static jmp_buf test_exit_buf;
103 static int test_error_return = 0;
104 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
105 #endif /* !TEST_STANDALONE */
109 static int pos; /* The offset of the current argument in ARGV. */
110 static int argc; /* The number of arguments present in ARGV. */
111 static char **argv; /* The argument list. */
115 static int unary_operator ();
116 static int binary_operator ();
117 static int two_arguments ();
118 static int three_arguments ();
119 static int posixtest ();
126 #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
127 && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
128 #define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
130 #define NO_RETURN_ATTRIBUTE /* empty */
133 static void test_syntax_error () NO_RETURN_ATTRIBUTE;
134 static void beyond () NO_RETURN_ATTRIBUTE;
137 test_syntax_error (format, arg)
140 fprintf (stderr, "%s: ", argv[0]);
141 fprintf (stderr, format, arg);
143 test_exit (SHELL_BOOLEAN (FALSE));
146 /* A wrapper for stat () which disallows pathnames that are empty strings. */
148 test_stat (path, finfo)
157 return (safe_stat (path, finfo));
160 /* Do the same thing access(2) does, but use the effective uid and gid,
161 and don't make the mistake of telling root that any file is executable.
162 But this loses when the containing filesystem is mounted e.g. read-only. */
169 static int euid = -1;
171 if (test_stat (path, &st) < 0)
179 /* Root can read or write any file. */
183 /* Root can execute any file that has any one of the execute
185 if (st.st_mode & S_IXUGO)
189 if (st.st_uid == euid) /* owner */
191 else if (group_member (st.st_gid))
194 if (st.st_mode & mode)
200 /* Increment our position in the argument list. Check that we're not
201 past the end of the argument list. This check is supressed if the
202 argument is FALSE. Made a macro for efficiency. */
207 if ((f) && pos >= argc) \
212 #if !defined (advance)
219 if (f && pos >= argc)
224 #define unary_advance() \
233 * beyond - call when we're beyond the end of the argument list (an
239 test_syntax_error ("argument expected\n", NULL);
242 /* Syntax error for when an integer argument was expected, but
243 something else was found. */
245 integer_expected_error (pch)
248 test_syntax_error ("integer expression expected %s\n", pch);
251 /* Return non-zero if the characters pointed to by STRING constitute a
252 valid number. Stuff the converted number into RESULT if RESULT is
253 a non-null pointer to a long. */
255 isint (string, result)
256 register char *string;
268 /* Skip leading whitespace characters. */
269 while (whitespace (*string))
275 /* We allow leading `-' or `+'. */
276 if (*string == '-' || *string == '+')
278 if (!digit (string[1]))
287 while (digit (*string))
290 value = (value * 10) + digit_value (*string);
294 /* Skip trailing whitespace, if any. */
295 while (whitespace (*string))
298 /* Error if not at end of string. */
311 /* Find the modification time of FILE, and stuff it into AGE, a pointer
312 to a long. Return non-zero if successful, else zero. */
314 age_of (filename, age)
320 if (test_stat (filename, &finfo) < 0)
324 *age = finfo.st_mtime;
330 * term - parse a term and return 1 or 0 depending on whether the term
331 * evaluates to true or false, respectively.
334 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
335 * '-'('L'|'x') filename
337 * '-'('z'|'n') string
339 * string ('!='|'=') string
340 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
341 * file '-'(nt|ot|ef) file
345 * positive and negative integers
355 /* Deal with leading "not"'s. */
356 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
359 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
365 return (value ^ (term ()));
368 /* A paren-bracketed argument. */
369 if (argv[pos][0] == '(' && !argv[pos][1])
374 test_syntax_error ("')' expected\n", NULL);
376 if (argv[pos][0] != ')' || argv[pos][1])
377 test_syntax_error ("')' expected, found %s\n", argv[pos]);
379 return (TRUE == (value));
382 /* are there enough arguments left that this could be dyadic? */
383 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
384 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
385 value = binary_operator ();
387 /* Might be a switch type argument */
388 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
390 if (unop (argv[pos][1]))
391 value = unary_operator ();
393 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
397 value = (argv[pos][0] != '\0');
408 struct stat stat_buf, stat_spare;
409 long int l, r, value;
410 /* Are the left and right integer expressions of the form '-l string'? */
413 if (strcmp (argv[pos], "-l") == 0)
418 /* Make sure that OP is still a valid binary operator. */
419 if ((op >= argc - 1) || (binop (argv[op]) == 0))
420 test_syntax_error ("%s: binary operator expected\n", argv[op]);
430 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
438 if (argv[op][0] == '-')
440 /* check for eq, nt, and stuff */
447 if (argv[op][2] == 't' && !argv[op][3])
451 l = strlen (argv[op - 1]);
454 if (!isint (argv[op - 1], &l))
455 integer_expected_error ("before -lt");
459 r = strlen (argv[op + 2]);
462 if (!isint (argv[op + 1], &r))
463 integer_expected_error ("after -lt");
466 return (TRUE == (l < r));
469 if (argv[op][2] == 'e' && !argv[op][3])
473 l = strlen (argv[op - 1]);
476 if (!isint (argv[op - 1], &l))
477 integer_expected_error ("before -le");
480 r = strlen (argv[op + 2]);
483 if (!isint (argv[op + 1], &r))
484 integer_expected_error ("after -le");
487 return (TRUE == (l <= r));
492 if (argv[op][2] == 't' && !argv[op][3])
494 /* gt integer greater than */
496 l = strlen (argv[op - 1]);
499 if (!isint (argv[op - 1], &l))
500 integer_expected_error ("before -gt");
503 r = strlen (argv[op + 2]);
506 if (!isint (argv[op + 1], &r))
507 integer_expected_error ("after -gt");
510 return (TRUE == (l > r));
513 if (argv[op][2] == 'e' && !argv[op][3])
515 /* ge - integer greater than or equal to */
517 l = strlen (argv[op - 1]);
520 if (!isint (argv[op - 1], &l))
521 integer_expected_error ("before -ge");
524 r = strlen (argv[op + 2]);
527 if (!isint (argv[op + 1], &r))
528 integer_expected_error ("after -ge");
531 return (TRUE == (l >= r));
536 if (argv[op][2] == 't' && !argv[op][3])
538 /* nt - newer than */
540 if (l_is_l || r_is_l)
541 test_syntax_error ("-nt does not accept -l\n", NULL);
542 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
543 return (TRUE == (l > r));
548 if (argv[op][2] == 'e' && !argv[op][3])
550 /* ne - integer not equal */
552 l = strlen (argv[op - 1]);
555 if (!isint (argv[op - 1], &l))
556 integer_expected_error ("before -ne");
559 r = strlen (argv[op + 2]);
562 if (!isint (argv[op + 1], &r))
563 integer_expected_error ("after -ne");
566 return (TRUE == (l != r));
571 if (argv[op][2] == 'q' && !argv[op][3])
573 /* eq - integer equal */
575 l = strlen (argv[op - 1]);
578 if (!isint (argv[op - 1], &l))
579 integer_expected_error ("before -eq");
582 r = strlen (argv[op + 2]);
585 if (!isint (argv[op + 1], &r))
586 integer_expected_error ("after -eq");
589 return (TRUE == (l == r));
592 if (argv[op][2] == 'f' && !argv[op][3])
594 /* ef - hard link? */
596 if (l_is_l || r_is_l)
597 test_syntax_error ("-ef does not accept -l\n", NULL);
598 if (safe_stat (argv[op - 1], &stat_buf) < 0)
600 if (safe_stat (argv[op + 1], &stat_spare) < 0)
603 (stat_buf.st_dev == stat_spare.st_dev &&
604 stat_buf.st_ino == stat_spare.st_ino));
609 if ('t' == argv[op][2] && '\000' == argv[op][3])
611 /* ot - older than */
613 if (l_is_l || r_is_l)
614 test_syntax_error ("-nt does not accept -l\n", NULL);
615 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
616 return (TRUE == (l < r));
621 test_syntax_error ("unknown binary operator", argv[op]);
624 if (argv[op][0] == '=' && !argv[op][1])
626 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
628 return (TRUE == value);
631 if (strcmp (argv[op], "!=") == 0)
633 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
635 return (TRUE == value);
646 struct stat stat_buf;
648 switch (argv[pos][1])
653 /* All of the following unary operators use unary_advance (), which
654 checks to make sure that there is an argument, and then advances
655 pos right past it. This means that pos - 1 is the location of the
658 case 'a': /* file exists in the file system? */
661 value = -1 != test_stat (argv[pos - 1], &stat_buf);
662 return (TRUE == value);
664 case 'r': /* file is readable? */
666 value = -1 != eaccess (argv[pos - 1], R_OK);
667 return (TRUE == value);
669 case 'w': /* File is writeable? */
671 value = -1 != eaccess (argv[pos - 1], W_OK);
672 return (TRUE == value);
674 case 'x': /* File is executable? */
676 value = -1 != eaccess (argv[pos - 1], X_OK);
677 return (TRUE == value);
679 case 'O': /* File is owned by you? */
681 if (test_stat (argv[pos - 1], &stat_buf) < 0)
684 return (TRUE == (geteuid () == stat_buf.st_uid));
686 case 'G': /* File is owned by your group? */
688 if (test_stat (argv[pos - 1], &stat_buf) < 0)
691 return (TRUE == (getegid () == stat_buf.st_gid));
693 case 'f': /* File is a file? */
695 if (test_stat (argv[pos - 1], &stat_buf) < 0)
698 /* Under POSIX, -f is true if the given file exists
699 and is a regular file. */
700 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
701 (0 == (stat_buf.st_mode & S_IFMT))));
703 case 'd': /* File is a directory? */
705 if (test_stat (argv[pos - 1], &stat_buf) < 0)
708 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
710 case 's': /* File has something in it? */
712 if (test_stat (argv[pos - 1], &stat_buf) < 0)
715 return (TRUE == (stat_buf.st_size > (off_t) 0));
717 case 'S': /* File is a socket? */
718 #if !defined (S_ISSOCK)
723 if (test_stat (argv[pos - 1], &stat_buf) < 0)
726 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
727 #endif /* S_ISSOCK */
729 case 'c': /* File is character special? */
731 if (test_stat (argv[pos - 1], &stat_buf) < 0)
734 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
736 case 'b': /* File is block special? */
738 if (test_stat (argv[pos - 1], &stat_buf) < 0)
741 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
743 case 'p': /* File is a named pipe? */
748 if (test_stat (argv[pos - 1], &stat_buf) < 0)
750 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
751 #endif /* S_ISFIFO */
753 case 'L': /* Same as -h */
756 case 'h': /* File is a symbolic link? */
761 /* An empty filename is not a valid pathname. */
762 if ((argv[pos - 1][0] == '\0') ||
763 (safe_lstat (argv[pos - 1], &stat_buf) < 0))
766 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
769 case 'u': /* File is setuid? */
774 if (test_stat (argv[pos - 1], &stat_buf) < 0)
777 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
780 case 'g': /* File is setgid? */
785 if (test_stat (argv[pos - 1], &stat_buf) < 0)
788 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
791 case 'k': /* File has sticky bit set? */
793 if (test_stat (argv[pos - 1], &stat_buf) < 0)
796 /* This is not Posix, and is not defined on some Posix systems. */
799 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
802 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
804 if (pos < argc && isint (argv[pos], &r))
807 return (TRUE == (isatty ((int) r)));
809 return (TRUE == (isatty (1)));
811 case 'n': /* True if arg has some length. */
813 return (TRUE == (argv[pos - 1][0] != 0));
815 case 'z': /* True if arg has no length. */
817 return (TRUE == (argv[pos - 1][0] == '\0'));
832 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
835 value = TRUTH_AND (value, and ());
837 return (TRUE == value);
852 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
855 value = TRUTH_OR (value, or ());
858 return (TRUE == value);
871 return (FALSE ^ (or ())); /* Same with this. */
874 /* Return TRUE if S is one of the test command's binary operators. */
879 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
880 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
881 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
882 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
885 /* Return non-zero if OP is one of the test command's unary operators. */
890 return (member (op, "abcdefgkLhprsStuwxOGnz"));
898 if (STREQ (argv[pos], "!"))
899 value = strlen (argv[pos+1]) == 0;
900 else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
902 if (unop (argv[pos][1]))
903 value = unary_operator ();
905 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
917 if (STREQ (argv[pos], "!"))
920 value = !two_arguments ();
922 else if (binop (argv[pos+1]))
924 value = binary_operator ();
927 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
928 (argv[pos][0] == '('))
931 test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
935 /* This is an implementation of a Posix.2 proposal by David Korn. */
941 switch (argc - 1) /* one extra passed in */
949 value = strlen (argv[1]) != 0;
954 value = two_arguments ();
959 value = three_arguments ();
963 if (STREQ (argv[pos], "!"))
966 value = !three_arguments ();
978 #if defined (TEST_STANDALONE)
979 #include "long-options.h"
986 fprintf (stderr, "Try `%s --help' for more information.\n",
991 Usage: %s EXPRESSION\n\
992 or: [ EXPRESSION ]\n\
995 program_name, program_name);
998 --help display this help and exit\n\
999 --version output version information and exit\n\
1001 EXPRESSION is true or false and sets exit status. It is one of:\n\
1005 ( EXPRESSION ) EXPRESSION is true\n\
1006 ! EXPRESSION EXPRESSION is false\n\
1007 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
1008 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
1010 [-n] STRING the length of STRING is non-zero\n\
1011 -z STRING the length of STRING is zero\n\
1012 STRING1 = STRING2 the strings are equal\n\
1013 STRING1 != STRING2 the strings are not equal\n\
1015 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1016 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1017 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1018 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1019 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1020 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1024 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1025 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1026 FILE1 -ot FILE2 FILE1 is older than FILE2\n\
1028 -G FILE FILE exists and is owned by the effective group ID\n\
1029 -L FILE FILE exists and is a symbolic link\n\
1030 -O FILE FILE exists and is owned by the effective user ID\n\
1031 -S FILE FILE exists and is a socket\n\
1032 -b FILE FILE exists and is block special\n\
1033 -c FILE FILE exists and is character special\n\
1034 -d FILE FILE exists and is a directory\n\
1035 -e FILE FILE exists\n\
1036 -f FILE FILE exists and is a regular file\n\
1037 -g FILE FILE exists and is set-group-ID\n\
1038 -k FILE FILE exists and has its sticky bit set\n\
1039 -p FILE FILE exists and is a named pipe\n\
1040 -r FILE FILE exists and is readable\n\
1041 -s FILE FILE exists and has a size greater than zero\n\
1042 -t standard output is opened on a terminal\n\
1043 -t FD file descriptor FD is opened on a terminal\n\
1044 -u FILE FILE exists and its set-user-ID bit is set\n\
1045 -w FILE FILE exists and is writable\n\
1046 -x FILE FILE exists and is executable\n\
1050 --help display this help and exit\n\
1051 --version output version information and exit\n\
1053 Beware that parentheses need to be escaped by backslashes for shells.\n\
1054 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1059 #endif /* TEST_STANDALONE */
1068 #if defined (TEST_STANDALONE)
1071 test_command (margc, margv)
1072 #endif /* !TEST_STANDALONE */
1078 #if !defined (TEST_STANDALONE)
1081 code = setjmp (test_exit_buf);
1084 return (test_error_return);
1085 #else /* TEST_STANDALONE */
1086 program_name = margv[0];
1087 #endif /* TEST_STANDALONE */
1091 if (margv[0] && strcmp (margv[0], "[") == 0)
1093 parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
1098 test_exit (SHELL_BOOLEAN (FALSE));
1100 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1101 test_syntax_error ("missing `]'\n", NULL);
1108 test_exit (SHELL_BOOLEAN (FALSE));
1110 parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
1111 value = posixtest ();
1114 test_syntax_error ("too many arguments\n", NULL);
1116 test_exit (SHELL_BOOLEAN (value));