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 "group-member.h"
40 # if !defined (S_IXUGO)
43 # if defined (_POSIX_VERSION)
45 # else /* !_POSIX_VERSION */
46 # include <sys/param.h>
47 # endif /* _POSIX_VERSION */
48 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
49 #define digit(c) ((c) >= '0' && (c) <= '9')
50 #define digit_value(c) ((c) - '0')
52 #endif /* TEST_STANDALONE */
54 #if !defined (_POSIX_VERSION)
55 # include <sys/file.h>
56 #endif /* !_POSIX_VERSION */
64 # 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 getgid (), 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 */
107 static int pos; /* The offset of the current argument in ARGV. */
108 static int argc; /* The number of arguments present in ARGV. */
109 static char **argv; /* The argument list. */
113 static int unary_operator ();
114 static int binary_operator ();
115 static int two_arguments ();
116 static int three_arguments ();
117 static int posixtest ();
124 #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
125 && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
126 #define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
128 #define NO_RETURN_ATTRIBUTE /* empty */
131 static void test_syntax_error () NO_RETURN_ATTRIBUTE;
132 static void beyond () NO_RETURN_ATTRIBUTE;
135 test_syntax_error (format, 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 (path, finfo)
155 return (stat (path, finfo));
158 /* Do the same thing access(2) does, but use the effective uid and gid,
159 and don't make the mistake of telling root that any file is executable.
160 But this loses when the containing filesystem is mounted e.g. read-only. */
167 static int euid = -1;
169 if (test_stat (path, &st) < 0)
177 /* Root can read or write any file. */
181 /* Root can execute any file that has any one of the execute
183 if (st.st_mode & S_IXUGO)
187 if (st.st_uid == euid) /* owner */
189 else if (group_member (st.st_gid))
192 if (st.st_mode & mode)
198 /* Increment our position in the argument list. Check that we're not
199 past the end of the argument list. This check is supressed if the
200 argument is FALSE. Made a macro for efficiency. */
205 if ((f) && pos >= argc) \
210 #if !defined (advance)
217 if (f && pos >= argc)
222 #define unary_advance() \
231 * beyond - call when we're beyond the end of the argument list (an
237 test_syntax_error ("argument expected\n", NULL);
240 /* Syntax error for when an integer argument was expected, but
241 something else was found. */
243 integer_expected_error (pch)
246 test_syntax_error ("integer expression expected %s\n", pch);
249 /* Return non-zero if the characters pointed to by STRING constitute a
250 valid number. Stuff the converted number into RESULT if RESULT is
251 a non-null pointer to a long. */
253 isint (string, result)
254 register char *string;
266 /* Skip leading whitespace characters. */
267 while (whitespace (*string))
273 /* We allow leading `-' or `+'. */
274 if (*string == '-' || *string == '+')
276 if (!digit (string[1]))
285 while (digit (*string))
288 value = (value * 10) + digit_value (*string);
292 /* Skip trailing whitespace, if any. */
293 while (whitespace (*string))
296 /* Error if not at end of string. */
309 /* Find the modification time of FILE, and stuff it into AGE, a pointer
310 to a long. Return non-zero if successful, else zero. */
312 age_of (filename, age)
318 if (test_stat (filename, &finfo) < 0)
322 *age = finfo.st_mtime;
328 * term - parse a term and return 1 or 0 depending on whether the term
329 * evaluates to true or false, respectively.
332 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
333 * '-'('L'|'x') filename
335 * '-'('z'|'n') string
337 * string ('!='|'=') string
338 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
339 * file '-'(nt|ot|ef) file
343 * positive and negative integers
353 /* Deal with leading "not"'s. */
354 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
357 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
363 return (value ^ (term ()));
366 /* A paren-bracketed argument. */
367 if (argv[pos][0] == '(' && !argv[pos][1])
372 test_syntax_error ("')' expected\n", NULL);
374 if (argv[pos][0] != ')' || argv[pos][1])
375 test_syntax_error ("')' expected, found %s\n", argv[pos]);
377 return (TRUE == (value));
380 /* are there enough arguments left that this could be dyadic? */
381 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
382 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
383 value = binary_operator ();
385 /* Might be a switch type argument */
386 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
388 if (unop (argv[pos][1]))
389 value = unary_operator ();
391 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
395 value = (argv[pos][0] != '\0');
406 struct stat stat_buf, stat_spare;
407 long int l, r, value;
408 /* Are the left and right integer expressions of the form '-l string'? */
411 if (strcmp (argv[pos], "-l") == 0)
416 /* Make sure that OP is still a valid binary operator. */
417 if ((op >= argc - 1) || (binop (argv[op]) == 0))
418 test_syntax_error ("%s: binary operator expected\n", argv[op]);
428 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
436 if (argv[op][0] == '-')
438 /* check for eq, nt, and stuff */
445 if (argv[op][2] == 't' && !argv[op][3])
449 l = strlen (argv[op - 1]);
452 if (!isint (argv[op - 1], &l))
453 integer_expected_error ("before -lt");
457 r = strlen (argv[op + 2]);
460 if (!isint (argv[op + 1], &r))
461 integer_expected_error ("after -lt");
464 return (TRUE == (l < r));
467 if (argv[op][2] == 'e' && !argv[op][3])
471 l = strlen (argv[op - 1]);
474 if (!isint (argv[op - 1], &l))
475 integer_expected_error ("before -le");
478 r = strlen (argv[op + 2]);
481 if (!isint (argv[op + 1], &r))
482 integer_expected_error ("after -le");
485 return (TRUE == (l <= r));
490 if (argv[op][2] == 't' && !argv[op][3])
492 /* gt integer greater than */
494 l = strlen (argv[op - 1]);
497 if (!isint (argv[op - 1], &l))
498 integer_expected_error ("before -gt");
501 r = strlen (argv[op + 2]);
504 if (!isint (argv[op + 1], &r))
505 integer_expected_error ("after -gt");
508 return (TRUE == (l > r));
511 if (argv[op][2] == 'e' && !argv[op][3])
513 /* ge - integer greater than or equal to */
515 l = strlen (argv[op - 1]);
518 if (!isint (argv[op - 1], &l))
519 integer_expected_error ("before -ge");
522 r = strlen (argv[op + 2]);
525 if (!isint (argv[op + 1], &r))
526 integer_expected_error ("after -ge");
529 return (TRUE == (l >= r));
534 if (argv[op][2] == 't' && !argv[op][3])
536 /* nt - newer than */
538 if (l_is_l || r_is_l)
539 test_syntax_error ("-nt does not accept -l\n", NULL);
540 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
541 return (TRUE == (l > r));
546 if (argv[op][2] == 'e' && !argv[op][3])
548 /* ne - integer not equal */
550 l = strlen (argv[op - 1]);
553 if (!isint (argv[op - 1], &l))
554 integer_expected_error ("before -ne");
557 r = strlen (argv[op + 2]);
560 if (!isint (argv[op + 1], &r))
561 integer_expected_error ("after -ne");
564 return (TRUE == (l != r));
569 if (argv[op][2] == 'q' && !argv[op][3])
571 /* eq - integer equal */
573 l = strlen (argv[op - 1]);
576 if (!isint (argv[op - 1], &l))
577 integer_expected_error ("before -eq");
580 r = strlen (argv[op + 2]);
583 if (!isint (argv[op + 1], &r))
584 integer_expected_error ("after -eq");
587 return (TRUE == (l == r));
590 if (argv[op][2] == 'f' && !argv[op][3])
592 /* ef - hard link? */
594 if (l_is_l || r_is_l)
595 test_syntax_error ("-ef does not accept -l\n", NULL);
596 if (stat (argv[op - 1], &stat_buf) < 0)
598 if (stat (argv[op + 1], &stat_spare) < 0)
601 (stat_buf.st_dev == stat_spare.st_dev &&
602 stat_buf.st_ino == stat_spare.st_ino));
607 if ('t' == argv[op][2] && '\000' == argv[op][3])
609 /* ot - older than */
611 if (l_is_l || r_is_l)
612 test_syntax_error ("-nt does not accept -l\n", NULL);
613 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
614 return (TRUE == (l < r));
619 test_syntax_error ("unknown binary operator", argv[op]);
622 if (argv[op][0] == '=' && !argv[op][1])
624 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
626 return (TRUE == value);
629 if (strcmp (argv[op], "!=") == 0)
631 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
633 return (TRUE == value);
644 struct stat stat_buf;
646 switch (argv[pos][1])
651 /* All of the following unary operators use unary_advance (), which
652 checks to make sure that there is an argument, and then advances
653 pos right past it. This means that pos - 1 is the location of the
656 case 'a': /* file exists in the file system? */
659 value = -1 != test_stat (argv[pos - 1], &stat_buf);
660 return (TRUE == value);
662 case 'r': /* file is readable? */
664 value = -1 != eaccess (argv[pos - 1], R_OK);
665 return (TRUE == value);
667 case 'w': /* File is writeable? */
669 value = -1 != eaccess (argv[pos - 1], W_OK);
670 return (TRUE == value);
672 case 'x': /* File is executable? */
674 value = -1 != eaccess (argv[pos - 1], X_OK);
675 return (TRUE == value);
677 case 'O': /* File is owned by you? */
679 if (test_stat (argv[pos - 1], &stat_buf) < 0)
682 return (TRUE == (geteuid () == stat_buf.st_uid));
684 case 'G': /* File is owned by your group? */
686 if (test_stat (argv[pos - 1], &stat_buf) < 0)
689 return (TRUE == (getegid () == stat_buf.st_gid));
691 case 'f': /* File is a file? */
693 if (test_stat (argv[pos - 1], &stat_buf) < 0)
696 /* Under POSIX, -f is true if the given file exists
697 and is a regular file. */
698 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
699 (0 == (stat_buf.st_mode & S_IFMT))));
701 case 'd': /* File is a directory? */
703 if (test_stat (argv[pos - 1], &stat_buf) < 0)
706 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
708 case 's': /* File has something in it? */
710 if (test_stat (argv[pos - 1], &stat_buf) < 0)
713 return (TRUE == (stat_buf.st_size > (off_t) 0));
715 case 'S': /* File is a socket? */
716 #if !defined (S_ISSOCK)
721 if (test_stat (argv[pos - 1], &stat_buf) < 0)
724 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
725 #endif /* S_ISSOCK */
727 case 'c': /* File is character special? */
729 if (test_stat (argv[pos - 1], &stat_buf) < 0)
732 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
734 case 'b': /* File is block special? */
736 if (test_stat (argv[pos - 1], &stat_buf) < 0)
739 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
741 case 'p': /* File is a named pipe? */
746 if (test_stat (argv[pos - 1], &stat_buf) < 0)
748 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
749 #endif /* S_ISFIFO */
751 case 'L': /* Same as -h */
754 case 'h': /* File is a symbolic link? */
759 /* An empty filename is not a valid pathname. */
760 if ((argv[pos - 1][0] == '\0') ||
761 (lstat (argv[pos - 1], &stat_buf) < 0))
764 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
767 case 'u': /* File is setuid? */
772 if (test_stat (argv[pos - 1], &stat_buf) < 0)
775 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
778 case 'g': /* File is setgid? */
783 if (test_stat (argv[pos - 1], &stat_buf) < 0)
786 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
789 case 'k': /* File has sticky bit set? */
791 if (test_stat (argv[pos - 1], &stat_buf) < 0)
794 /* This is not Posix, and is not defined on some Posix systems. */
797 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
800 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
802 if (pos < argc && isint (argv[pos], &r))
805 return (TRUE == (isatty ((int) r)));
807 return (TRUE == (isatty (1)));
809 case 'n': /* True if arg has some length. */
811 return (TRUE == (argv[pos - 1][0] != 0));
813 case 'z': /* True if arg has no length. */
815 return (TRUE == (argv[pos - 1][0] == '\0'));
830 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
833 value = TRUTH_AND (value, and ());
835 return (TRUE == value);
850 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
853 value = TRUTH_OR (value, or ());
856 return (TRUE == value);
869 return (FALSE ^ (or ())); /* Same with this. */
872 /* Return TRUE if S is one of the test command's binary operators. */
877 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
878 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
879 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
880 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
883 /* Return non-zero if OP is one of the test command's unary operators. */
888 return (member (op, "abcdefgkLhprsStuwxOGnz"));
896 if (STREQ (argv[pos], "!"))
897 value = strlen (argv[pos+1]) == 0;
898 else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
900 if (unop (argv[pos][1]))
901 value = unary_operator ();
903 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
915 if (STREQ (argv[pos], "!"))
918 value = !two_arguments ();
920 else if (binop (argv[pos+1]))
922 value = binary_operator ();
925 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
926 (argv[pos][0] == '('))
929 test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
933 /* This is an implementation of a Posix.2 proposal by David Korn. */
939 switch (argc - 1) /* one extra passed in */
947 value = strlen (argv[1]) != 0;
952 value = two_arguments ();
957 value = three_arguments ();
961 if (STREQ (argv[pos], "!"))
964 value = !three_arguments ();
976 #if defined (TEST_STANDALONE)
977 #include "long-options.h"
984 fprintf (stderr, "Try `%s --help' for more information.\n",
989 Usage: %s EXPRESSION\n\
990 or: [ EXPRESSION ]\n\
993 program_name, program_name);
996 --help display this help and exit\n\
997 --version output version information and exit\n\
999 EXPRESSION is true or false and sets exit status. It is one of:\n\
1003 ( EXPRESSION ) EXPRESSION is true\n\
1004 ! EXPRESSION EXPRESSION is false\n\
1005 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
1006 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
1008 [-n] STRING the length of STRING is non-zero\n\
1009 -z STRING the length of STRING is zero\n\
1010 STRING1 = STRING2 the strings are equal\n\
1011 STRING1 != STRING2 the strings are not equal\n\
1013 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1014 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1015 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1016 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1017 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1018 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1022 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1023 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1024 FILE1 -ot FILE2 FILE1 is older than FILE2\n\
1026 -G FILE FILE exists and is owned by the effective group ID\n\
1027 -L FILE FILE exists and is a symbolic link\n\
1028 -O FILE FILE exists and is owned by the effective user ID\n\
1029 -S FILE FILE exists and is a socket\n\
1030 -b FILE FILE exists and is block special\n\
1031 -c FILE FILE exists and is character special\n\
1032 -d FILE FILE exists and is a directory\n\
1033 -e FILE FILE exists\n\
1034 -f FILE FILE exists and is a regular file\n\
1035 -g FILE FILE exists and is set-group-ID\n\
1036 -k FILE FILE exists and has its sticky bit set\n\
1037 -p FILE FILE exists and is a named pipe\n\
1038 -r FILE FILE exists and is readable\n\
1039 -s FILE FILE exists and has a size greater than zero\n\
1040 -t [FD] file descriptor FD (stdout by default) is opened on a terminal\n\
1041 -u FILE FILE exists and its set-user-ID bit is set\n\
1042 -w FILE FILE exists and is writable\n\
1043 -x FILE FILE exists and is executable\n\
1047 Beware that parentheses need to be escaped by backslashes for shells.\n\
1048 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1053 #endif /* TEST_STANDALONE */
1062 #if defined (TEST_STANDALONE)
1065 test_command (margc, margv)
1066 #endif /* !TEST_STANDALONE */
1072 #if !defined (TEST_STANDALONE)
1075 code = setjmp (test_exit_buf);
1078 return (test_error_return);
1079 #else /* TEST_STANDALONE */
1080 program_name = margv[0];
1081 #endif /* TEST_STANDALONE */
1085 if (margv[0] && strcmp (margv[0], "[") == 0)
1087 parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
1092 test_exit (SHELL_BOOLEAN (FALSE));
1094 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1095 test_syntax_error ("missing `]'\n", NULL);
1102 test_exit (SHELL_BOOLEAN (FALSE));
1104 parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
1105 value = posixtest ();
1108 test_syntax_error ("too many arguments\n", NULL);
1110 test_exit (SHELL_BOOLEAN (value));