1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987, 1988, 1989, 1990, 1991 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 */
28 #include <sys/types.h>
30 #if !defined (TEST_STANDALONE)
32 # include "posixstat.h"
33 # include "filecntl.h"
34 #else /* TEST_STANDALONE */
36 # if !defined (S_IXUGO)
39 # if defined (_POSIX_VERSION)
41 # else /* !_POSIX_VERSION */
42 # include <sys/param.h>
43 # endif /* _POSIX_VERSION */
44 # if defined (NGROUPS_MAX) || defined (_SC_NGROUPS_MAX) || defined (NGROUPS)
45 # define HAVE_GETGROUPS
46 # endif /* NGROUPS_MAX || _SC_NGROUPS_MAX || NGROUPS */
47 #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
48 #define digit(c) ((c) >= '0' && (c) <= '9')
49 #define digit_value(c) ((c) - '0')
51 #endif /* TEST_STANDALONE */
53 #if !defined (_POSIX_VERSION)
54 # include <sys/file.h>
55 #endif /* !_POSIX_VERSION */
63 # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
67 # define member(c, s) (int)((c) ? index ((s), (c)) : 0)
70 #if defined (_POSIX_VERSION)
72 /* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
73 their getgroups system call (except in the `System V' environment, which
74 is troublesome in other ways) fills in an array of int, not gid_t
75 (which is `short' on those systems). Kludge, kludge. */
77 #if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
78 #define GETGROUPS_T gid_t
80 #define GETGROUPS_T int
82 #else /* !_POSIX_VERSION */
83 #define GETGROUPS_T int
84 #endif /* !_POSIX_VERSION */
86 extern gid_t getgid (), getegid ();
87 extern uid_t geteuid ();
96 /* The following few defines control the truth and false output of each stage.
97 TRUE and FALSE are what we use to compute the final output value.
98 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
99 TRUTH_OR is how to do logical or with TRUE and FALSE.
100 TRUTH_AND is how to do logical and with TRUE and FALSE..
101 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
102 SHELL_BOOLEAN = (!value). */
105 #define SHELL_BOOLEAN(value) (!(value))
106 #define TRUTH_OR(a, b) ((a) | (b))
107 #define TRUTH_AND(a, b) ((a) & (b))
109 #if defined (TEST_STANDALONE)
110 # define test_exit(val) exit (val)
112 static jmp_buf test_exit_buf;
113 static int test_error_return = 0;
114 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
115 #endif /* !TEST_STANDALONE */
119 static int pos; /* The offset of the current argument in ARGV. */
120 static int argc; /* The number of arguments present in ARGV. */
121 static char **argv; /* The argument list. */
125 static int unary_operator ();
126 static int binary_operator ();
127 static int two_arguments ();
128 static int three_arguments ();
129 static int posixtest ();
136 static int group_member ();
139 test_syntax_error (format, arg)
142 fprintf (stderr, "%s: ", argv[0]);
143 fprintf (stderr, format, arg);
145 test_exit (SHELL_BOOLEAN (FALSE));
148 /* A wrapper for stat () which disallows pathnames that are empty strings. */
150 test_stat (path, finfo)
159 return (stat (path, finfo));
162 /* Do the same thing access(2) does, but use the effective uid and gid,
163 and don't make the mistake of telling root that any file is
171 static int euid = -1;
173 if (test_stat (path, &st) < 0)
181 /* Root can read or write any file. */
185 /* Root can execute any file that has any one of the execute
187 if (st.st_mode & S_IXUGO)
191 if (st.st_uid == euid) /* owner */
193 else if (group_member (st.st_gid))
196 if (st.st_mode & mode)
202 #if defined (HAVE_GETGROUPS)
203 /* The number of groups that this user is a member of. */
204 static int ngroups = 0;
205 static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
206 static int default_group_array_size = 0;
207 #endif /* HAVE_GETGROUPS */
209 /* Return non-zero if GID is one that we have in our groups list. */
214 #if !defined (HAVE_GETGROUPS)
215 return ((gid == getgid ()) || (gid == getegid ()));
219 /* getgroups () returns the number of elements that it was able to
220 place into the array. We simply continue to call getgroups ()
221 until the number of elements placed into the array is smaller than
222 the physical size of the array. */
224 while (ngroups == default_group_array_size)
226 default_group_array_size += 64;
228 group_array = (GETGROUPS_T *)
229 xrealloc (group_array,
230 default_group_array_size * sizeof (GETGROUPS_T));
232 ngroups = getgroups (default_group_array_size, group_array);
235 /* In case of error, the user loses. */
239 /* Search through the list looking for GID. */
240 for (i = 0; i < ngroups; i++)
241 if (gid == group_array[i])
245 #endif /* HAVE_GETGROUPS */
248 /* Increment our position in the argument list. Check that we're not
249 past the end of the argument list. This check is supressed if the
250 argument is FALSE. Made a macro for efficiency. */
252 #define advance(f) (++pos, f && (pos < argc ? 0 : beyond()))
255 #if !defined (advance)
262 if (f && pos >= argc)
267 #define unary_advance() (advance (1),++pos)
270 * beyond - call when we're beyond the end of the argument list (an
276 test_syntax_error ("argument expected\n", (char *)NULL);
279 /* Syntax error for when an integer argument was expected, but
280 something else was found. */
282 integer_expected_error (pch)
285 test_syntax_error ("integer expression expected %s\n", pch);
288 /* Return non-zero if the characters pointed to by STRING constitute a
289 valid number. Stuff the converted number into RESULT if RESULT is
290 a non-null pointer to a long. */
292 isint (string, result)
293 register char *string;
305 /* Skip leading whitespace characters. */
306 while (whitespace (*string))
312 /* We allow leading `-' or `+'. */
313 if (*string == '-' || *string == '+')
315 if (!digit (string[1]))
324 while (digit (*string))
327 value = (value * 10) + digit_value (*string);
331 /* Skip trailing whitespace, if any. */
332 while (whitespace (*string))
335 /* Error if not at end of string. */
348 /* Find the modification time of FILE, and stuff it into AGE, a pointer
349 to a long. Return non-zero if successful, else zero. */
351 age_of (filename, age)
357 if (test_stat (filename, &finfo) < 0)
361 *age = finfo.st_mtime;
367 * term - parse a term and return 1 or 0 depending on whether the term
368 * evaluates to true or false, respectively.
371 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
372 * '-'('L'|'x') filename
374 * '-'('z'|'n') string
376 * string ('!='|'=') string
377 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
378 * file '-'(nt|ot|ef) file
382 * positive and negative integers
392 /* Deal with leading "not"'s. */
393 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
396 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
402 return (value ^ (term ()));
405 /* A paren-bracketed argument. */
406 if (argv[pos][0] == '(' && !argv[pos][1])
410 if (argv[pos][0] != ')' || argv[pos][1])
411 test_syntax_error ("')' expected, found %s\n", argv[pos]);
413 return (TRUE == (value));
416 /* are there enough arguments left that this could be dyadic? */
417 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
418 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
419 value = binary_operator ();
421 /* Might be a switch type argument */
422 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
424 if (unop (argv[pos][1]))
425 value = unary_operator ();
427 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
431 value = (argv[pos][0] != '\0');
442 struct stat stat_buf, stat_spare;
443 long int l, r, value;
444 /* Are the left and right integer expressions of the form '-l string'? */
447 if (strcmp (argv[pos], "-l") == 0)
452 /* Make sure that OP is still a valid binary operator. */
453 if ((op >= argc - 1) || (binop (argv[op]) == 0))
454 test_syntax_error ("%s: binary operator expected\n", argv[op]);
464 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
472 if (argv[op][0] == '-')
474 /* check for eq, nt, and stuff */
481 if (argv[op][2] == 't' && !argv[op][3])
485 l = strlen (argv[op - 1]);
488 if (!isint (argv[op - 1], &l))
489 integer_expected_error ("before -lt");
493 r = strlen (argv[op + 2]);
496 if (!isint (argv[op + 1], &r))
497 integer_expected_error ("after -lt");
500 return (TRUE == (l < r));
503 if (argv[op][2] == 'e' && !argv[op][3])
507 l = strlen (argv[op - 1]);
510 if (!isint (argv[op - 1], &l))
511 integer_expected_error ("before -le");
514 r = strlen (argv[op + 2]);
517 if (!isint (argv[op + 1], &r))
518 integer_expected_error ("after -le");
521 return (TRUE == (l <= r));
526 if (argv[op][2] == 't' && !argv[op][3])
528 /* gt integer greater than */
530 l = strlen (argv[op - 1]);
533 if (!isint (argv[op - 1], &l))
534 integer_expected_error ("before -gt");
537 r = strlen (argv[op + 2]);
540 if (!isint (argv[op + 1], &r))
541 integer_expected_error ("after -gt");
544 return (TRUE == (l > r));
547 if (argv[op][2] == 'e' && !argv[op][3])
549 /* ge - integer greater than or equal to */
551 l = strlen (argv[op - 1]);
554 if (!isint (argv[op - 1], &l))
555 integer_expected_error ("before -ge");
558 r = strlen (argv[op + 2]);
561 if (!isint (argv[op + 1], &r))
562 integer_expected_error ("after -ge");
565 return (TRUE == (l >= r));
570 if (argv[op][2] == 't' && !argv[op][3])
572 /* nt - newer than */
574 if (l_is_l || r_is_l)
575 test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
576 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
577 return (TRUE == (l > r));
582 if (argv[op][2] == 'e' && !argv[op][3])
584 /* ne - integer not equal */
586 l = strlen (argv[op - 1]);
589 if (!isint (argv[op - 1], &l))
590 integer_expected_error ("before -ne");
593 r = strlen (argv[op + 2]);
596 if (!isint (argv[op + 1], &r))
597 integer_expected_error ("after -ne");
600 return (TRUE == (l != r));
605 if (argv[op][2] == 'q' && !argv[op][3])
607 /* eq - integer equal */
609 l = strlen (argv[op - 1]);
612 if (!isint (argv[op - 1], &l))
613 integer_expected_error ("before -eq");
616 r = strlen (argv[op + 2]);
619 if (!isint (argv[op + 1], &r))
620 integer_expected_error ("after -eq");
623 return (TRUE == (l == r));
626 if (argv[op][2] == 'f' && !argv[op][3])
628 /* ef - hard link? */
630 if (l_is_l || r_is_l)
631 test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
632 if (stat (argv[op - 1], &stat_buf) < 0)
634 if (stat (argv[op + 1], &stat_spare) < 0)
637 (stat_buf.st_dev == stat_spare.st_dev &&
638 stat_buf.st_ino == stat_spare.st_ino));
643 if ('t' == argv[op][2] && '\000' == argv[op][3])
645 /* ot - older than */
647 if (l_is_l || r_is_l)
648 test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
649 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
650 return (TRUE == (l < r));
655 test_syntax_error ("unknown binary operator", argv[op]);
658 if (argv[op][0] == '=' && !argv[op][1])
660 value = (strcmp (argv[pos], argv[pos + 2]) == 0);
662 return (TRUE == value);
665 if (strcmp (argv[op], "!=") == 0)
667 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
669 return (TRUE == value);
677 struct stat stat_buf;
679 switch (argv[pos][1])
684 /* All of the following unary operators use unary_advance (), which
685 checks to make sure that there is an argument, and then advances
686 pos right past it. This means that pos - 1 is the location of the
689 case 'a': /* file exists in the file system? */
692 value = -1 != test_stat (argv[pos - 1], &stat_buf);
693 return (TRUE == value);
695 case 'r': /* file is readable? */
697 value = -1 != eaccess (argv[pos - 1], R_OK);
698 return (TRUE == value);
700 case 'w': /* File is writeable? */
702 value = -1 != eaccess (argv[pos - 1], W_OK);
703 return (TRUE == value);
705 case 'x': /* File is executable? */
707 value = -1 != eaccess (argv[pos - 1], X_OK);
708 return (TRUE == value);
710 case 'O': /* File is owned by you? */
712 if (test_stat (argv[pos - 1], &stat_buf) < 0)
715 return (TRUE == (geteuid () == stat_buf.st_uid));
717 case 'G': /* File is owned by your group? */
719 if (test_stat (argv[pos - 1], &stat_buf) < 0)
722 return (TRUE == (getegid () == stat_buf.st_gid));
724 case 'f': /* File is a file? */
726 if (test_stat (argv[pos - 1], &stat_buf) < 0)
729 /* Under POSIX, -f is true if the given file exists
730 and is a regular file. */
731 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
732 (0 == (stat_buf.st_mode & S_IFMT))));
734 case 'd': /* File is a directory? */
736 if (test_stat (argv[pos - 1], &stat_buf) < 0)
739 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
741 case 's': /* File has something in it? */
743 if (test_stat (argv[pos - 1], &stat_buf) < 0)
746 return (TRUE == (stat_buf.st_size > (off_t) 0));
748 case 'S': /* File is a socket? */
749 #if !defined (S_ISSOCK)
754 if (test_stat (argv[pos - 1], &stat_buf) < 0)
757 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
758 #endif /* S_ISSOCK */
760 case 'c': /* File is character special? */
762 if (test_stat (argv[pos - 1], &stat_buf) < 0)
765 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
767 case 'b': /* File is block special? */
769 if (test_stat (argv[pos - 1], &stat_buf) < 0)
772 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
774 case 'p': /* File is a named pipe? */
779 if (test_stat (argv[pos - 1], &stat_buf) < 0)
781 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
782 #endif /* S_ISFIFO */
784 case 'L': /* Same as -h */
787 case 'h': /* File is a symbolic link? */
792 /* An empty filename is not a valid pathname. */
793 if ((argv[pos - 1][0] == '\0') ||
794 (lstat (argv[pos - 1], &stat_buf) < 0))
797 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
800 case 'u': /* File is setuid? */
802 if (test_stat (argv[pos - 1], &stat_buf) < 0)
805 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
807 case 'g': /* File is setgid? */
809 if (test_stat (argv[pos - 1], &stat_buf) < 0)
812 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
814 case 'k': /* File has sticky bit set? */
816 if (test_stat (argv[pos - 1], &stat_buf) < 0)
818 #if !defined (S_ISVTX)
819 /* This is not Posix, and is not defined on some Posix systems. */
822 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
825 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
827 if (pos < argc && isint (argv[pos], &r))
830 return (TRUE == (isatty ((int) r)));
832 return (TRUE == (isatty (1)));
834 case 'n': /* True if arg has some length. */
836 return (TRUE == (argv[pos - 1][0] != 0));
838 case 'z': /* True if arg has no length. */
840 return (TRUE == (argv[pos - 1][0] == '\0'));
855 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
858 value = TRUTH_AND (value, and ());
860 return (TRUE == value);
875 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
878 value = TRUTH_OR (value, or ());
881 return (TRUE == value);
894 return (FALSE ^ (or ())); /* Same with this. */
897 /* Return TRUE if S is one of the test command's binary operators. */
902 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
903 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
904 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
905 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
908 /* Return non-zero if OP is one of the test command's unary operators. */
913 return (member (op, "abcdefgkLhprsStuwxOGnz"));
921 if (STREQ (argv[pos], "!"))
922 value = strlen (argv[pos+1]) == 0;
923 else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
925 if (unop (argv[pos][1]))
926 value = unary_operator ();
928 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
940 if (STREQ (argv[pos], "!"))
943 value = !two_arguments ();
945 else if (binop (argv[pos+1]))
947 value = binary_operator ();
950 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
951 (argv[pos][0] == '('))
954 test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
958 /* This is an implementation of a Posix.2 proposal by David Korn. */
964 switch (argc - 1) /* one extra passed in */
972 value = strlen (argv[1]) != 0;
977 value = two_arguments ();
982 value = three_arguments ();
986 if (STREQ (argv[pos], "!"))
989 value = !three_arguments ();
1008 #if defined (TEST_STANDALONE)
1011 test_command (margc, margv)
1012 #endif /* !TEST_STANDALONE */
1019 #if !defined (TEST_STANDALONE)
1022 code = setjmp (test_exit_buf);
1025 return (test_error_return);
1026 #else /* TEST_STANDALONE */
1027 program_name = margv[0];
1028 #endif /* TEST_STANDALONE */
1032 if (margv[0] && strcmp (margv[0], "[") == 0)
1037 test_exit (SHELL_BOOLEAN (FALSE));
1039 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1040 test_syntax_error ("missing `]'\n", (char *)NULL);
1047 test_exit (SHELL_BOOLEAN (FALSE));
1049 value = posixtest ();
1052 test_syntax_error ("too many arguments\n", (char *)NULL);
1054 test_exit (SHELL_BOOLEAN (value));