*** empty log message ***
[coreutils.git] / src / test.c
blob273a35d8d2217d850120e50a39c9e2a33f8f317a
1 /* GNU test program (ksb and mjb) */
3 /* Modified to run with the GNU shell by bfox. */
5 /* Copyright (C) 1987-1999 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
12 version.
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
17 for more details.
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 */
27 #include <config.h>
28 #include <stdio.h>
29 #include <sys/types.h>
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "test"
34 #define TEST_STANDALONE 1
36 #if !defined (TEST_STANDALONE)
37 # include "shell.h"
38 # include "posixstat.h"
39 # include "filecntl.h"
40 #else /* TEST_STANDALONE */
41 # include "system.h"
42 # include "group-member.h"
43 # include "error.h"
44 # if !defined (S_IXUGO)
45 # define S_IXUGO 0111
46 # endif /* S_IXUGO */
47 # if defined (_POSIX_VERSION)
48 # include <limits.h>
49 # else /* !_POSIX_VERSION */
50 # include <sys/param.h>
51 # endif /* _POSIX_VERSION */
52 # define whitespace(c) (((c) == ' ') || ((c) == '\t'))
53 # define digit(c) ((c) >= '0' && (c) <= '9')
54 # define digit_value(c) ((c) - '0')
55 char *program_name;
56 #endif /* TEST_STANDALONE */
58 #if !defined (_POSIX_VERSION)
59 # include <sys/file.h>
60 #endif /* !_POSIX_VERSION */
62 #include <errno.h>
63 #ifndef errno
64 extern int errno;
65 #endif
67 #undef STREQ
68 #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
70 #if !defined (member)
71 # define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
72 #endif /* !member */
74 extern gid_t getegid ();
75 extern uid_t geteuid ();
77 #if !defined (R_OK)
78 # define R_OK 4
79 # define W_OK 2
80 # define X_OK 1
81 # define F_OK 0
82 #endif /* R_OK */
84 /* This name is used solely when printing --version information. */
85 #define PROGRAM_NAME "test"
87 /* The following few defines control the truth and false output of each stage.
88 TRUE and FALSE are what we use to compute the final output value.
89 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
90 TRUTH_OR is how to do logical or with TRUE and FALSE.
91 TRUTH_AND is how to do logical and with TRUE and FALSE..
92 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
93 SHELL_BOOLEAN = (!value). */
94 #define TRUE 1
95 #define FALSE 0
96 #define SHELL_BOOLEAN(value) (!(value))
97 #define TRUTH_OR(a, b) ((a) | (b))
98 #define TRUTH_AND(a, b) ((a) & (b))
100 #if defined (TEST_STANDALONE)
101 # define test_exit(val) exit (val)
102 #else
103 static jmp_buf test_exit_buf;
104 static int test_error_return = 0;
105 # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
106 #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 PARAMS ((int op));
113 static int binop PARAMS ((char *s));
114 static int unary_operator PARAMS ((void));
115 static int binary_operator PARAMS ((void));
116 static int two_arguments PARAMS ((void));
117 static int three_arguments PARAMS ((void));
118 static int posixtest PARAMS ((void));
120 static int expr PARAMS ((void));
121 static int term PARAMS ((void));
122 static int and PARAMS ((void));
123 static int or PARAMS ((void));
125 #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
126 && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
127 # define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
128 #else
129 # define NO_RETURN_ATTRIBUTE /* empty */
130 #endif
132 static void test_syntax_error PARAMS ((char *format, char *arg)) NO_RETURN_ATTRIBUTE;
133 static void beyond PARAMS ((void)) NO_RETURN_ATTRIBUTE;
135 static void
136 test_syntax_error (char *format, char *arg)
138 fprintf (stderr, "%s: ", argv[0]);
139 fprintf (stderr, format, arg);
140 fflush (stderr);
141 test_exit (SHELL_BOOLEAN (FALSE));
144 /* A wrapper for stat () which disallows pathnames that are empty strings. */
145 static int
146 test_stat (char *path, struct stat *finfo)
148 if (*path == '\0')
150 errno = ENOENT;
151 return (-1);
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. */
159 static int
160 eaccess (char *path, int mode)
162 struct stat st;
163 static int euid = -1;
165 if (test_stat (path, &st) < 0)
166 return (-1);
168 if (euid == -1)
169 euid = geteuid ();
171 if (euid == 0)
173 /* Root can read or write any file. */
174 if (mode != X_OK)
175 return (0);
177 /* Root can execute any file that has any one of the execute
178 bits set. */
179 if (st.st_mode & S_IXUGO)
180 return (0);
183 if (st.st_uid == euid) /* owner */
184 mode <<= 6;
185 else if (group_member (st.st_gid))
186 mode <<= 3;
188 if (st.st_mode & mode)
189 return (0);
191 return (-1);
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. */
197 #define advance(f) \
198 do \
200 ++pos; \
201 if ((f) && pos >= argc) \
202 beyond (); \
204 while (0)
206 #if !defined (advance)
207 static int
208 advance (int f)
210 ++pos;
212 if (f && pos >= argc)
213 beyond ();
215 #endif /* advance */
217 #define unary_advance() \
218 do \
220 advance (1); \
221 ++pos; \
223 while (0)
226 * beyond - call when we're beyond the end of the argument list (an
227 * error condition)
229 static void
230 beyond (void)
232 test_syntax_error (_("argument expected\n"), NULL);
235 /* Syntax error for when an integer argument was expected, but
236 something else was found. */
237 static void
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. */
246 static int
247 isint (register char *string, long int *result)
249 int sign;
250 long value;
252 sign = 1;
253 value = 0;
255 if (result)
256 *result = 0;
258 /* Skip leading whitespace characters. */
259 while (whitespace (*string))
260 string++;
262 if (!*string)
263 return (0);
265 /* We allow leading `-' or `+'. */
266 if (*string == '-' || *string == '+')
268 if (!digit (string[1]))
269 return (0);
271 if (*string == '-')
272 sign = -1;
274 string++;
277 while (digit (*string))
279 if (result)
280 value = (value * 10) + digit_value (*string);
281 string++;
284 /* Skip trailing whitespace, if any. */
285 while (whitespace (*string))
286 string++;
288 /* Error if not at end of string. */
289 if (*string)
290 return (0);
292 if (result)
294 value *= sign;
295 *result = value;
298 return (1);
301 /* Find the modification time of FILE, and stuff it into AGE, a pointer
302 to a long. Return nonzero if successful, else zero. */
303 static int
304 age_of (char *filename, long int *age)
306 struct stat finfo;
308 if (test_stat (filename, &finfo) < 0)
309 return (0);
311 if (age)
312 *age = finfo.st_mtime;
314 return (1);
318 * term - parse a term and return 1 or 0 depending on whether the term
319 * evaluates to true or false, respectively.
321 * term ::=
322 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
323 * '-'('L'|'x') filename
324 * '-t' [ int ]
325 * '-'('z'|'n') string
326 * string
327 * string ('!='|'=') string
328 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
329 * file '-'(nt|ot|ef) file
330 * '(' <expr> ')'
331 * int ::=
332 * '-l' string
333 * positive and negative integers
335 static int
336 term (void)
338 int value;
340 if (pos >= argc)
341 beyond ();
343 /* Deal with leading "not"'s. */
344 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
346 value = FALSE;
347 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
349 advance (1);
350 value ^= (TRUE);
353 return (value ^ (term ()));
356 /* A paren-bracketed argument. */
357 if (argv[pos][0] == '(' && !argv[pos][1])
359 advance (1);
360 value = expr ();
361 if (!argv[pos])
362 test_syntax_error (_("')' expected\n"), NULL);
363 else
364 if (argv[pos][0] != ')' || argv[pos][1])
365 test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
366 advance (0);
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] && argv[pos][1] && 0 == argv[pos][2])
378 if (unop (argv[pos][1]))
379 value = unary_operator ();
380 else
381 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
383 else
385 value = (argv[pos][0] != '\0');
386 advance (0);
389 return (value);
392 static int
393 binary_operator (void)
395 register int op;
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'? */
399 int l_is_l, r_is_l;
401 if (strcmp (argv[pos], "-l") == 0)
403 l_is_l = 1;
404 op = pos + 2;
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]);
410 advance (0);
412 else
414 l_is_l = 0;
415 op = pos + 1;
418 if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
420 r_is_l = 1;
421 advance (0);
423 else
424 r_is_l = 0;
426 if (argv[op][0] == '-')
428 /* check for eq, nt, and stuff */
429 switch (argv[op][1])
431 default:
432 break;
434 case 'l':
435 if (argv[op][2] == 't' && !argv[op][3])
437 /* lt */
438 if (l_is_l)
439 l = strlen (argv[op - 1]);
440 else
442 if (!isint (argv[op - 1], &l))
443 integer_expected_error (_("before -lt"));
446 if (r_is_l)
447 r = strlen (argv[op + 2]);
448 else
450 if (!isint (argv[op + 1], &r))
451 integer_expected_error (_("after -lt"));
453 pos += 3;
454 return (TRUE == (l < r));
457 if (argv[op][2] == 'e' && !argv[op][3])
459 /* le */
460 if (l_is_l)
461 l = strlen (argv[op - 1]);
462 else
464 if (!isint (argv[op - 1], &l))
465 integer_expected_error (_("before -le"));
467 if (r_is_l)
468 r = strlen (argv[op + 2]);
469 else
471 if (!isint (argv[op + 1], &r))
472 integer_expected_error (_("after -le"));
474 pos += 3;
475 return (TRUE == (l <= r));
477 break;
479 case 'g':
480 if (argv[op][2] == 't' && !argv[op][3])
482 /* gt integer greater than */
483 if (l_is_l)
484 l = strlen (argv[op - 1]);
485 else
487 if (!isint (argv[op - 1], &l))
488 integer_expected_error (_("before -gt"));
490 if (r_is_l)
491 r = strlen (argv[op + 2]);
492 else
494 if (!isint (argv[op + 1], &r))
495 integer_expected_error (_("after -gt"));
497 pos += 3;
498 return (TRUE == (l > r));
501 if (argv[op][2] == 'e' && !argv[op][3])
503 /* ge - integer greater than or equal to */
504 if (l_is_l)
505 l = strlen (argv[op - 1]);
506 else
508 if (!isint (argv[op - 1], &l))
509 integer_expected_error (_("before -ge"));
511 if (r_is_l)
512 r = strlen (argv[op + 2]);
513 else
515 if (!isint (argv[op + 1], &r))
516 integer_expected_error (_("after -ge"));
518 pos += 3;
519 return (TRUE == (l >= r));
521 break;
523 case 'n':
524 if (argv[op][2] == 't' && !argv[op][3])
526 /* nt - newer than */
527 pos += 3;
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));
532 else
533 return (FALSE);
536 if (argv[op][2] == 'e' && !argv[op][3])
538 /* ne - integer not equal */
539 if (l_is_l)
540 l = strlen (argv[op - 1]);
541 else
543 if (!isint (argv[op - 1], &l))
544 integer_expected_error (_("before -ne"));
546 if (r_is_l)
547 r = strlen (argv[op + 2]);
548 else
550 if (!isint (argv[op + 1], &r))
551 integer_expected_error (_("after -ne"));
553 pos += 3;
554 return (TRUE == (l != r));
556 break;
558 case 'e':
559 if (argv[op][2] == 'q' && !argv[op][3])
561 /* eq - integer equal */
562 if (l_is_l)
563 l = strlen (argv[op - 1]);
564 else
566 if (!isint (argv[op - 1], &l))
567 integer_expected_error (_("before -eq"));
569 if (r_is_l)
570 r = strlen (argv[op + 2]);
571 else
573 if (!isint (argv[op + 1], &r))
574 integer_expected_error (_("after -eq"));
576 pos += 3;
577 return (TRUE == (l == r));
580 if (argv[op][2] == 'f' && !argv[op][3])
582 /* ef - hard link? */
583 pos += 3;
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)
587 return (FALSE);
588 if (stat (argv[op + 1], &stat_spare) < 0)
589 return (FALSE);
590 return (TRUE ==
591 (stat_buf.st_dev == stat_spare.st_dev &&
592 stat_buf.st_ino == stat_spare.st_ino));
594 break;
596 case 'o':
597 if ('t' == argv[op][2] && '\000' == argv[op][3])
599 /* ot - older than */
600 pos += 3;
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));
605 return (FALSE);
607 break;
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);
615 pos += 3;
616 return (TRUE == value);
619 if (strcmp (argv[op], "!=") == 0)
621 value = (strcmp (argv[pos], argv[pos + 2]) != 0);
622 pos += 3;
623 return (TRUE == value);
626 /* Not reached. */
627 abort ();
630 static int
631 unary_operator (void)
633 long fd, value;
634 struct stat stat_buf;
636 switch (argv[pos][1])
638 default:
639 return (FALSE);
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
644 argument. */
646 case 'a': /* file exists in the file system? */
647 case 'e':
648 unary_advance ();
649 value = -1 != test_stat (argv[pos - 1], &stat_buf);
650 return (TRUE == value);
652 case 'r': /* file is readable? */
653 unary_advance ();
654 value = -1 != eaccess (argv[pos - 1], R_OK);
655 return (TRUE == value);
657 case 'w': /* File is writable? */
658 unary_advance ();
659 value = -1 != eaccess (argv[pos - 1], W_OK);
660 return (TRUE == value);
662 case 'x': /* File is executable? */
663 unary_advance ();
664 value = -1 != eaccess (argv[pos - 1], X_OK);
665 return (TRUE == value);
667 case 'O': /* File is owned by you? */
668 unary_advance ();
669 if (test_stat (argv[pos - 1], &stat_buf) < 0)
670 return (FALSE);
672 return (TRUE == (geteuid () == stat_buf.st_uid));
674 case 'G': /* File is owned by your group? */
675 unary_advance ();
676 if (test_stat (argv[pos - 1], &stat_buf) < 0)
677 return (FALSE);
679 return (TRUE == (getegid () == stat_buf.st_gid));
681 case 'f': /* File is a file? */
682 unary_advance ();
683 if (test_stat (argv[pos - 1], &stat_buf) < 0)
684 return (FALSE);
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? */
692 unary_advance ();
693 if (test_stat (argv[pos - 1], &stat_buf) < 0)
694 return (FALSE);
696 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
698 case 's': /* File has something in it? */
699 unary_advance ();
700 if (test_stat (argv[pos - 1], &stat_buf) < 0)
701 return (FALSE);
703 return (TRUE == (stat_buf.st_size > (off_t) 0));
705 case 'S': /* File is a socket? */
706 #if !defined (S_ISSOCK)
707 return (FALSE);
708 #else
709 unary_advance ();
711 if (test_stat (argv[pos - 1], &stat_buf) < 0)
712 return (FALSE);
714 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
715 #endif /* S_ISSOCK */
717 case 'c': /* File is character special? */
718 unary_advance ();
719 if (test_stat (argv[pos - 1], &stat_buf) < 0)
720 return (FALSE);
722 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
724 case 'b': /* File is block special? */
725 unary_advance ();
726 if (test_stat (argv[pos - 1], &stat_buf) < 0)
727 return (FALSE);
729 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
731 case 'p': /* File is a named pipe? */
732 unary_advance ();
733 #ifndef S_ISFIFO
734 return (FALSE);
735 #else
736 if (test_stat (argv[pos - 1], &stat_buf) < 0)
737 return (FALSE);
738 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
739 #endif /* S_ISFIFO */
741 case 'L': /* Same as -h */
742 /*FALLTHROUGH*/
744 case 'h': /* File is a symbolic link? */
745 unary_advance ();
746 #ifndef S_ISLNK
747 return (FALSE);
748 #else
749 /* An empty filename is not a valid pathname. */
750 if ((argv[pos - 1][0] == '\0') ||
751 (lstat (argv[pos - 1], &stat_buf) < 0))
752 return (FALSE);
754 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
755 #endif /* S_IFLNK */
757 case 'u': /* File is setuid? */
758 unary_advance ();
759 #ifndef S_ISUID
760 return (FALSE);
761 #else
762 if (test_stat (argv[pos - 1], &stat_buf) < 0)
763 return (FALSE);
765 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
766 #endif
768 case 'g': /* File is setgid? */
769 unary_advance ();
770 #ifndef S_ISGID
771 return (FALSE);
772 #else
773 if (test_stat (argv[pos - 1], &stat_buf) < 0)
774 return (FALSE);
776 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
777 #endif
779 case 'k': /* File has sticky bit set? */
780 unary_advance ();
781 if (test_stat (argv[pos - 1], &stat_buf) < 0)
782 return (FALSE);
783 #ifndef S_ISVTX
784 /* This is not Posix, and is not defined on some Posix systems. */
785 return (FALSE);
786 #else
787 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
788 #endif
790 case 't': /* File (fd) is a terminal? */
791 advance (0);
792 if (pos < argc)
794 if (!isint (argv[pos], &fd))
795 integer_expected_error (_("after -t"));
796 advance (0);
798 else
800 fd = 1;
802 return (TRUE == (isatty ((int) fd)));
804 case 'n': /* True if arg has some length. */
805 unary_advance ();
806 return (TRUE == (argv[pos - 1][0] != 0));
808 case 'z': /* True if arg has no length. */
809 unary_advance ();
810 return (TRUE == (argv[pos - 1][0] == '\0'));
815 * and:
816 * term
817 * term '-a' and
819 static int
820 and (void)
822 int value;
824 value = term ();
825 while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
827 advance (0);
828 value = TRUTH_AND (value, and ());
830 return (TRUE == value);
834 * or:
835 * and
836 * and '-o' or
838 static int
839 or (void)
841 int value;
843 value = and ();
845 while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
847 advance (0);
848 value = TRUTH_OR (value, or ());
851 return (TRUE == value);
855 * expr:
856 * or
858 static int
859 expr (void)
861 if (pos >= argc)
862 beyond ();
864 return (FALSE ^ (or ())); /* Same with this. */
867 /* Return TRUE if S is one of the test command's binary operators. */
868 static int
869 binop (char *s)
871 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
872 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
873 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
874 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
877 /* Return nonzero if OP is one of the test command's unary operators. */
878 static int
879 unop (int op)
881 return (member (op, "abcdefgkLhprsStuwxOGnz"));
884 static int
885 one_argument (const char *s)
887 if (STREQ (s, "-t"))
888 return (TRUE == (isatty (1)));
890 return strlen (s) != 0;
893 static int
894 two_arguments (void)
896 int value;
898 if (STREQ (argv[pos], "!"))
899 value = ! one_argument (argv[pos+1]);
900 else if (argv[pos][0] == '-'
901 && argv[pos][1] != '\0'
902 && argv[pos][2] == '\0')
904 if (unop (argv[pos][1]))
905 value = unary_operator ();
906 else
907 test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
909 else
910 beyond ();
911 return (value);
914 static int
915 three_arguments (void)
917 int value;
919 if (STREQ (argv[pos], "!"))
921 advance (1);
922 value = !two_arguments ();
924 else if (binop (argv[pos+1]))
926 value = binary_operator ();
927 pos = argc;
929 else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
930 (argv[pos][0] == '('))
931 value = expr ();
932 else
933 test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
934 return (value);
937 /* This is an implementation of a Posix.2 proposal by David Korn. */
938 static int
939 posixtest (void)
941 int value;
943 switch (argc - 1) /* one extra passed in */
945 case 0:
946 value = FALSE;
947 pos = argc;
948 break;
950 case 1:
951 value = one_argument (argv[1]);
952 pos = argc;
953 break;
955 case 2:
956 value = two_arguments ();
957 pos = argc;
958 break;
960 case 3:
961 value = three_arguments ();
962 break;
964 case 4:
965 if (STREQ (argv[pos], "!"))
967 advance (1);
968 value = !three_arguments ();
969 break;
971 /* FALLTHROUGH */
972 case 5:
973 default:
974 value = expr ();
977 return (value);
980 #if defined (TEST_STANDALONE)
981 # include "long-options.h"
983 void
984 usage (int status)
986 if (status != 0)
987 fprintf (stderr, _("Try `%s --help' for more information.\n"),
988 program_name);
989 else
991 printf (_("\
992 Usage: %s EXPRESSION\n\
993 or: [ EXPRESSION ]\n\
994 or: %s OPTION\n\
996 program_name, program_name);
997 printf (_("\
998 Exit with the status determined by EXPRESSION.\n\
1000 --help display this help and exit\n\
1001 --version output version information and exit\n\
1003 EXPRESSION is true or false and sets exit status. It is one of:\n\
1004 "));
1005 printf (_("\
1007 ( EXPRESSION ) EXPRESSION is true\n\
1008 ! EXPRESSION EXPRESSION is false\n\
1009 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true\n\
1010 EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true\n\
1012 [-n] STRING the length of STRING is nonzero\n\
1013 -z STRING the length of STRING is zero\n\
1014 STRING1 = STRING2 the strings are equal\n\
1015 STRING1 != STRING2 the strings are not equal\n\
1017 INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
1018 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
1019 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
1020 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
1021 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
1022 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
1023 "));
1024 printf (_("\
1026 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
1027 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
1028 FILE1 -ot FILE2 FILE1 is older than FILE2\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 -G FILE FILE exists and is owned by the effective group ID\n\
1037 -k FILE FILE exists and has its sticky bit set\n\
1038 -L FILE FILE exists and is a symbolic link\n\
1039 -O FILE FILE exists and is owned by the effective user ID\n\
1040 -p FILE FILE exists and is a named pipe\n\
1041 -r FILE FILE exists and is readable\n\
1042 -s FILE FILE exists and has a size greater than zero\n\
1043 -S FILE FILE exists and is a socket\n\
1044 -t [FD] file descriptor FD (stdout by default) is opened on a terminal\n\
1045 -u FILE FILE exists and its set-user-ID bit is set\n\
1046 -w FILE FILE exists and is writable\n\
1047 -x FILE FILE exists and is executable\n\
1048 "));
1049 printf (_("\
1051 Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
1052 INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
1053 "));
1054 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
1056 exit (status);
1058 #endif /* TEST_STANDALONE */
1060 #if !defined (TEST_STANDALONE)
1061 # define main test_command
1062 #endif
1064 #define AUTHORS "FIXME: ksb and mjb"
1067 * [:
1068 * '[' expr ']'
1069 * test:
1070 * test expr
1073 main (int margc, char **margv)
1075 int value;
1077 #if !defined (TEST_STANDALONE)
1078 int code;
1080 code = setjmp (test_exit_buf);
1082 if (code)
1083 return (test_error_return);
1084 #else /* TEST_STANDALONE */
1085 program_name = margv[0];
1086 setlocale (LC_ALL, "");
1087 bindtextdomain (PACKAGE, LOCALEDIR);
1088 textdomain (PACKAGE);
1089 #endif /* TEST_STANDALONE */
1091 argv = margv;
1093 if (margv[0] && strcmp (margv[0], "[") == 0)
1095 /* Don't recognize --help or --version if POSIXLY_CORRECT is set. */
1096 if (getenv ("POSIXLY_CORRECT") == NULL)
1097 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1098 AUTHORS, usage);
1100 --margc;
1102 if (margc < 2)
1103 test_exit (SHELL_BOOLEAN (FALSE));
1105 if (margv[margc] && strcmp (margv[margc], "]") != 0)
1106 test_syntax_error (_("missing `]'\n"), NULL);
1109 argc = margc;
1110 pos = 1;
1112 if (pos >= argc)
1113 test_exit (SHELL_BOOLEAN (FALSE));
1115 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
1116 AUTHORS, usage);
1117 value = posixtest ();
1119 if (pos != argc)
1120 test_syntax_error (_("too many arguments\n"), NULL);
1122 test_exit (SHELL_BOOLEAN (value));