]> git.ipfire.org Git - thirdparty/bash.git/blob - test.c
Imported from ../bash-1.14.7.tar.gz.
[thirdparty/bash.git] / test.c
1 /* GNU test program (ksb and mjb) */
2
3 /* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
4
5 /* Copyright (C) 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
6
7 This file is part of GNU Bash, the Bourne Again SHell.
8
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.
13
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.
18
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. */
22
23 /* Define STANDALONE to get the /bin/test version. Otherwise, you get
24 the shell builtin version. */
25 /* #define STANDALONE */
26
27 #include <stdio.h>
28 #include "bashtypes.h"
29
30 #if !defined (STANDALONE)
31 # if !defined (_POSIX_VERSION)
32 # include <sys/file.h>
33 # endif /* !_POSIX_VERSION */
34 # include "posixstat.h"
35 # include "filecntl.h"
36 # include "shell.h"
37 #else /* STANDALONE */
38 # include "system.h"
39 # if !defined (S_IXUGO)
40 # define S_IXUGO 0111
41 # endif
42 # if defined (HAVE_UNISTD_H)
43 # include <unistd.h>
44 # endif /* HAVE_UNISTD_H */
45 # define whitespace(c) (((c) == ' ') || ((c) == '\t'))
46 # define digit(c) ((c) >= '0' && (c) <= '9')
47 # define digit_value(c) ((c) - '0')
48 #endif /* STANDALONE */
49
50 #if !defined (STRLEN)
51 # define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
52 #endif
53
54 #include <errno.h>
55 #if !defined (errno)
56 extern int errno;
57 #endif /* !errno */
58
59 #if !defined (STREQ)
60 # define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
61 #endif /* !STREQ */
62
63 #if !defined (member)
64 # define member(c, s) (int)((c) ? (char *)strchr ((s), (c)) : 0)
65 #endif /* !member */
66
67 /* Make gid_t and uid_t mean something for non-posix systems. */
68 #if !defined (_POSIX_VERSION) && !defined (HAVE_UID_T)
69 # if !defined (gid_t)
70 # define gid_t int
71 # endif
72 # if !defined (uid_t)
73 # define uid_t int
74 # endif
75 #endif /* !_POSIX_VERSION */
76
77 /* What type are the user and group ids? GID_T is actually the type of
78 the members of the array that getgroups(3) fills in from its second
79 argument. */
80 #if defined (INT_GROUPS_ARRAY)
81 # define GID_T int
82 # define UID_T int
83 #else /* !INT_GROUPS_ARRAY */
84 # define GID_T gid_t
85 # define UID_T uid_t
86 #endif /* !INT_GROUPS_ARRAY */
87
88 #if !defined (Linux) && !defined (USGr4_2) && !defined (SunOS5)
89 extern gid_t getegid ();
90 extern uid_t geteuid ();
91 # if !defined (sony)
92 extern gid_t getgid ();
93 # endif /* !sony */
94 #endif /* !Linux && !USGr4_2 && !SunOS5 */
95
96 #if !defined (R_OK)
97 #define R_OK 4
98 #define W_OK 2
99 #define X_OK 1
100 #define F_OK 0
101 #endif /* R_OK */
102
103 /* The following few defines control the truth and false output of each stage.
104 TRUE and FALSE are what we use to compute the final output value.
105 SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
106 TRUTH_OR is how to do logical or with TRUE and FALSE.
107 TRUTH_AND is how to do logical and with TRUE and FALSE..
108 Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
109 SHELL_BOOLEAN = (!value). */
110 #define TRUE 1
111 #define FALSE 0
112 #define SHELL_BOOLEAN(value) (!(value))
113 #define TRUTH_OR(a, b) ((a) | (b))
114 #define TRUTH_AND(a, b) ((a) & (b))
115
116 #if defined (STANDALONE)
117 # define test_exit(val) exit (val)
118 #else
119 static jmp_buf test_exit_buf;
120 static int test_error_return = 0;
121 # define test_exit(val) \
122 do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
123 #endif /* STANDALONE */
124
125 #if defined (AFS)
126 /* We have to use access(2) for machines running AFS, because it's
127 not a Unix file system. This may produce incorrect answers for
128 non-AFS files. I hate AFS. */
129 # define EACCESS(path, mode) access(path, mode)
130 #else
131 # define EACCESS(path, mode) eaccess(path, mode)
132 #endif /* AFS */
133
134 static int pos; /* The offset of the current argument in ARGV. */
135 static int argc; /* The number of arguments present in ARGV. */
136 static char **argv; /* The argument list. */
137 static int noeval;
138
139 static int isint ();
140 static int unop ();
141 static int binop ();
142 static int unary_operator ();
143 static int binary_operator ();
144 static int two_arguments ();
145 static int three_arguments ();
146 static int posixtest ();
147
148 static int expr ();
149 static int term ();
150 static int and ();
151 static int or ();
152
153 static void
154 test_syntax_error (format, arg)
155 char *format, *arg;
156 {
157 #if !defined (STANDALONE)
158 extern int interactive_shell;
159 extern char *get_name_for_error ();
160 if (!interactive_shell)
161 fprintf (stderr, "%s: ", get_name_for_error ());
162 #endif
163 fprintf (stderr, "%s: ", argv[0]);
164 fprintf (stderr, format, arg);
165 fflush (stderr);
166 test_exit (SHELL_BOOLEAN (FALSE));
167 }
168
169 /* A wrapper for stat () which disallows pathnames that are empty strings
170 and handles /dev/fd emulation on systems that don't have it. */
171 static int
172 test_stat (path, finfo)
173 char *path;
174 struct stat *finfo;
175 {
176 if (*path == '\0')
177 {
178 errno = ENOENT;
179 return (-1);
180 }
181 #if !defined (HAVE_DEV_FD)
182 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
183 {
184 int fd;
185 if (isint (path + 8, &fd))
186 return (fstat (fd, finfo));
187 else
188 {
189 errno = EBADF;
190 return (-1);
191 }
192 }
193 #endif /* !HAVE_DEV_FD */
194 return (stat (path, finfo));
195 }
196
197 /* Do the same thing access(2) does, but use the effective uid and gid,
198 and don't make the mistake of telling root that any file is
199 executable. */
200 static int
201 eaccess (path, mode)
202 char *path;
203 int mode;
204 {
205 struct stat st;
206 static int euid = -1;
207
208 if (test_stat (path, &st) < 0)
209 return (-1);
210
211 if (euid == -1)
212 #if defined (SHELL)
213 euid = current_user.euid;
214 #else
215 euid = geteuid ();
216 #endif
217
218 if (euid == 0)
219 {
220 /* Root can read or write any file. */
221 if (mode != X_OK)
222 return (0);
223
224 /* Root can execute any file that has any one of the execute
225 bits set. */
226 if (st.st_mode & S_IXUGO)
227 return (0);
228 }
229
230 if (st.st_uid == euid) /* owner */
231 mode <<= 6;
232 else if (group_member (st.st_gid))
233 mode <<= 3;
234
235 if (st.st_mode & mode)
236 return (0);
237
238 return (-1);
239 }
240
241 #if defined (HAVE_GETGROUPS)
242 /* The number of groups that this user is a member of. */
243 static int ngroups = 0;
244 static GID_T *group_array = (GID_T *)NULL;
245 static int default_group_array_size = 0;
246 #endif /* HAVE_GETGROUPS */
247
248 #if !defined (NOGROUP)
249 # define NOGROUP (GID_T) -1
250 #endif
251
252 /* Return non-zero if GID is one that we have in our groups list. */
253 int
254 group_member (gid)
255 GID_T gid;
256 {
257 static GID_T pgid = (GID_T)NOGROUP;
258 static GID_T egid = (GID_T)NOGROUP;
259
260 if (pgid == (GID_T)NOGROUP)
261 #if defined (SHELL)
262 pgid = (GID_T) current_user.gid;
263 #else /* !SHELL */
264 pgid = (GID_T) getgid ();
265 #endif /* !SHELL */
266
267 if (egid == (GID_T)NOGROUP)
268 #if defined (SHELL)
269 egid = (GID_T) current_user.egid;
270 #else /* !SHELL */
271 egid = (GID_T) getegid ();
272 #endif /* !SHELL */
273
274 if (gid == pgid || gid == egid)
275 return (1);
276
277 #if defined (HAVE_GETGROUPS)
278 /* getgroups () returns the number of elements that it was able to
279 place into the array. We simply continue to call getgroups ()
280 until the number of elements placed into the array is smaller than
281 the physical size of the array. */
282
283 while (ngroups == default_group_array_size)
284 {
285 default_group_array_size += 64;
286
287 group_array = (GID_T *)
288 xrealloc (group_array, default_group_array_size * sizeof (GID_T));
289
290 ngroups = getgroups (default_group_array_size, group_array);
291 }
292
293 /* In case of error, the user loses. */
294 if (ngroups < 0)
295 return (0);
296
297 /* Search through the list looking for GID. */
298 {
299 register int i;
300
301 for (i = 0; i < ngroups; i++)
302 if (gid == group_array[i])
303 return (1);
304 }
305 #endif /* HAVE_GETGROUPS */
306
307 return (0);
308 }
309
310 /* Increment our position in the argument list. Check that we're not
311 past the end of the argument list. This check is supressed if the
312 argument is FALSE. Made a macro for efficiency. */
313 #if !defined (lint)
314 #define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
315 #endif
316
317 #if !defined (advance)
318 static int
319 advance (f)
320 int f;
321 {
322 ++pos;
323
324 if (f && pos >= argc)
325 beyond ();
326 }
327 #endif /* advance */
328
329 #define unary_advance() do { advance (1); ++pos; } while (0)
330
331 /*
332 * beyond - call when we're beyond the end of the argument list (an
333 * error condition)
334 */
335 static int
336 beyond ()
337 {
338 test_syntax_error ("argument expected\n", (char *)NULL);
339 }
340
341 /* Syntax error for when an integer argument was expected, but
342 something else was found. */
343 static void
344 integer_expected_error (pch)
345 char *pch;
346 {
347 test_syntax_error ("integer expression expected %s\n", pch);
348 }
349
350 /* Return non-zero if the characters pointed to by STRING constitute a
351 valid number. Stuff the converted number into RESULT if RESULT is
352 a non-null pointer to a long. */
353 static int
354 isint (string, result)
355 register char *string;
356 long *result;
357 {
358 int sign;
359 long value;
360
361 sign = 1;
362 value = 0;
363
364 if (result)
365 *result = 0;
366
367 /* Skip leading whitespace characters. */
368 while (whitespace (*string))
369 string++;
370
371 if (!*string)
372 return (0);
373
374 /* We allow leading `-' or `+'. */
375 if (*string == '-' || *string == '+')
376 {
377 if (!digit (string[1]))
378 return (0);
379
380 if (*string == '-')
381 sign = -1;
382
383 string++;
384 }
385
386 while (digit (*string))
387 {
388 if (result)
389 value = (value * 10) + digit_value (*string);
390 string++;
391 }
392
393 /* Skip trailing whitespace, if any. */
394 while (whitespace (*string))
395 string++;
396
397 /* Error if not at end of string. */
398 if (*string)
399 return (0);
400
401 if (result)
402 {
403 value *= sign;
404 *result = value;
405 }
406
407 return (1);
408 }
409
410 /* Find the modification time of FILE, and stuff it into AGE, a pointer
411 to a long. Return non-zero if successful, else zero. */
412 static int
413 age_of (filename, age)
414 char *filename;
415 long *age;
416 {
417 struct stat finfo;
418
419 if (test_stat (filename, &finfo) < 0)
420 return (0);
421
422 if (age)
423 *age = finfo.st_mtime;
424
425 return (1);
426 }
427
428 /*
429 * term - parse a term and return 1 or 0 depending on whether the term
430 * evaluates to true or false, respectively.
431 *
432 * term ::=
433 * '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
434 * '-'('L'|'x') filename
435 * '-t' [ int ]
436 * '-'('z'|'n') string
437 * string
438 * string ('!='|'=') string
439 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
440 * file '-'(nt|ot|ef) file
441 * '(' <expr> ')'
442 * int ::=
443 * '-l' string
444 * positive and negative integers
445 */
446 static int
447 term ()
448 {
449 int value;
450
451 if (pos >= argc)
452 beyond ();
453
454 /* Deal with leading "not"'s. */
455 if ('!' == argv[pos][0] && '\000' == argv[pos][1])
456 {
457 value = FALSE;
458 while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
459 {
460 advance (1);
461 value ^= (TRUE);
462 }
463
464 return (value ^ (term ()));
465 }
466
467 /* A paren-bracketed argument. */
468 if (argv[pos][0] == '(' && !argv[pos][1])
469 {
470 advance (1);
471 value = expr ();
472 if (argv[pos] == 0)
473 test_syntax_error ("`)' expected\n");
474 else if (argv[pos][0] != ')' || argv[pos][1])
475 test_syntax_error ("`)' expected, found %s\n", argv[pos]);
476 advance (0);
477 return (TRUE == (value));
478 }
479
480 /* are there enough arguments left that this could be dyadic? */
481 if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
482 ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
483 value = binary_operator ();
484
485 /* Might be a switch type argument */
486 else if ('-' == argv[pos][0] && 0 == argv[pos][2])
487 {
488 if (unop (argv[pos][1]))
489 value = unary_operator ();
490 else
491 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
492 }
493 else
494 {
495 value = (argv[pos][0] != '\0');
496 advance (0);
497 }
498
499 return (value);
500 }
501
502 static int
503 binary_operator ()
504 {
505 register int op;
506 struct stat stat_buf, stat_spare;
507 long int l, r, value;
508 /* Are the left and right integer expressions of the form '-l string'? */
509 int l_is_l, r_is_l;
510
511 if (argv[pos][0] == '-' && argv[pos][1] == 'l' && !argv[pos][2])
512 {
513 l_is_l = 1;
514 op = pos + 2;
515
516 /* Make sure that OP is still a valid binary operator. */
517 if ((op >= argc - 1) || (binop (argv[op]) == 0))
518 test_syntax_error ("%s: binary operator expected\n", argv[op]);
519
520 advance (0);
521 }
522 else
523 {
524 l_is_l = 0;
525 op = pos + 1;
526 }
527
528 if ((op < argc - 2) &&
529 (argv[op + 1][0] == '-' && argv[op + 1][1] == 'l' && !argv[op + 1][2]))
530 {
531 r_is_l = 1;
532 advance (0);
533 }
534 else
535 r_is_l = 0;
536
537 if (argv[op][0] == '-')
538 {
539 /* check for eq, nt, and stuff */
540 switch (argv[op][1])
541 {
542 default:
543 break;
544
545 case 'l':
546 if (argv[op][2] == 't' && !argv[op][3])
547 {
548 /* lt */
549 if (l_is_l)
550 l = strlen (argv[op - 1]);
551 else
552 {
553 if (!isint (argv[op - 1], &l))
554 integer_expected_error ("before -lt");
555 }
556
557 if (r_is_l)
558 r = strlen (argv[op + 2]);
559 else
560 {
561 if (!isint (argv[op + 1], &r))
562 integer_expected_error ("after -lt");
563 }
564 pos += 3;
565 return (TRUE == (l < r));
566 }
567
568 if (argv[op][2] == 'e' && !argv[op][3])
569 {
570 /* le */
571 if (l_is_l)
572 l = strlen (argv[op - 1]);
573 else
574 {
575 if (!isint (argv[op - 1], &l))
576 integer_expected_error ("before -le");
577 }
578 if (r_is_l)
579 r = strlen (argv[op + 2]);
580 else
581 {
582 if (!isint (argv[op + 1], &r))
583 integer_expected_error ("after -le");
584 }
585 pos += 3;
586 return (TRUE == (l <= r));
587 }
588 break;
589
590 case 'g':
591 if (argv[op][2] == 't' && !argv[op][3])
592 {
593 /* gt integer greater than */
594 if (l_is_l)
595 l = strlen (argv[op - 1]);
596 else
597 {
598 if (!isint (argv[op - 1], &l))
599 integer_expected_error ("before -gt");
600 }
601 if (r_is_l)
602 r = strlen (argv[op + 2]);
603 else
604 {
605 if (!isint (argv[op + 1], &r))
606 integer_expected_error ("after -gt");
607 }
608 pos += 3;
609 return (TRUE == (l > r));
610 }
611
612 if (argv[op][2] == 'e' && !argv[op][3])
613 {
614 /* ge - integer greater than or equal to */
615 if (l_is_l)
616 l = strlen (argv[op - 1]);
617 else
618 {
619 if (!isint (argv[op - 1], &l))
620 integer_expected_error ("before -ge");
621 }
622 if (r_is_l)
623 r = strlen (argv[op + 2]);
624 else
625 {
626 if (!isint (argv[op + 1], &r))
627 integer_expected_error ("after -ge");
628 }
629 pos += 3;
630 return (TRUE == (l >= r));
631 }
632 break;
633
634 case 'n':
635 if (argv[op][2] == 't' && !argv[op][3])
636 {
637 /* nt - newer than */
638 pos += 3;
639 if (l_is_l || r_is_l)
640 test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
641 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
642 return (TRUE == (l > r));
643 else
644 return (FALSE);
645 }
646
647 if (argv[op][2] == 'e' && !argv[op][3])
648 {
649 /* ne - integer not equal */
650 if (l_is_l)
651 l = strlen (argv[op - 1]);
652 else
653 {
654 if (!isint (argv[op - 1], &l))
655 integer_expected_error ("before -ne");
656 }
657 if (r_is_l)
658 r = strlen (argv[op + 2]);
659 else
660 {
661 if (!isint (argv[op + 1], &r))
662 integer_expected_error ("after -ne");
663 }
664 pos += 3;
665 return (TRUE == (l != r));
666 }
667 break;
668
669 case 'e':
670 if (argv[op][2] == 'q' && !argv[op][3])
671 {
672 /* eq - integer equal */
673 if (l_is_l)
674 l = strlen (argv[op - 1]);
675 else
676 {
677 if (!isint (argv[op - 1], &l))
678 integer_expected_error ("before -eq");
679 }
680 if (r_is_l)
681 r = strlen (argv[op + 2]);
682 else
683 {
684 if (!isint (argv[op + 1], &r))
685 integer_expected_error ("after -eq");
686 }
687 pos += 3;
688 return (TRUE == (l == r));
689 }
690
691 if (argv[op][2] == 'f' && !argv[op][3])
692 {
693 /* ef - hard link? */
694 pos += 3;
695 if (l_is_l || r_is_l)
696 test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
697 if (test_stat (argv[op - 1], &stat_buf) < 0)
698 return (FALSE);
699 if (test_stat (argv[op + 1], &stat_spare) < 0)
700 return (FALSE);
701 return (TRUE ==
702 (stat_buf.st_dev == stat_spare.st_dev &&
703 stat_buf.st_ino == stat_spare.st_ino));
704 }
705 break;
706
707 case 'o':
708 if ('t' == argv[op][2] && '\000' == argv[op][3])
709 {
710 /* ot - older than */
711 pos += 3;
712 if (l_is_l || r_is_l)
713 test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
714 if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
715 return (TRUE == (l < r));
716 return (FALSE);
717 }
718 break;
719 }
720 test_syntax_error ("%s: unknown binary operator", argv[op]);
721 }
722
723 if (argv[op][0] == '=' && !argv[op][1])
724 {
725 value = (argv[pos][0] == argv[pos+2][0]) &&
726 (strcmp (argv[pos], argv[pos + 2]) == 0);
727 pos += 3;
728 return (TRUE == value);
729 }
730
731 if (argv[op][0] == '!' && argv[op][1] == '=' && !argv[op][2])
732 {
733 value = (argv[pos][0] != argv[pos + 2][0]) ||
734 (strcmp (argv[pos], argv[pos + 2]) != 0);
735 pos += 3;
736 return (TRUE == value);
737 }
738 return (FALSE);
739 }
740
741 static int
742 unary_operator ()
743 {
744 long r, value;
745 struct stat stat_buf;
746
747 switch (argv[pos][1])
748 {
749 default:
750 return (FALSE);
751
752 /* All of the following unary operators use unary_advance (), which
753 checks to make sure that there is an argument, and then advances
754 pos right past it. This means that pos - 1 is the location of the
755 argument. */
756
757 case 'a': /* file exists in the file system? */
758 case 'e':
759 unary_advance ();
760 value = -1 != test_stat (argv[pos - 1], &stat_buf);
761 return (TRUE == value);
762
763 case 'r': /* file is readable? */
764 unary_advance ();
765 value = -1 != EACCESS (argv[pos - 1], R_OK);
766 return (TRUE == value);
767
768 case 'w': /* File is writeable? */
769 unary_advance ();
770 value = -1 != EACCESS (argv[pos - 1], W_OK);
771 return (TRUE == value);
772
773 case 'x': /* File is executable? */
774 unary_advance ();
775 value = -1 != EACCESS (argv[pos - 1], X_OK);
776 return (TRUE == value);
777
778 case 'O': /* File is owned by you? */
779 unary_advance ();
780 if (test_stat (argv[pos - 1], &stat_buf) < 0)
781 return (FALSE);
782
783 #if defined (SHELL)
784 return (TRUE == ((UID_T) current_user.euid == (UID_T) stat_buf.st_uid));
785 #else
786 return (TRUE == ((UID_T) geteuid () == (UID_T) stat_buf.st_uid));
787 #endif /* !SHEL */
788
789 case 'G': /* File is owned by your group? */
790 unary_advance ();
791 if (test_stat (argv[pos - 1], &stat_buf) < 0)
792 return (FALSE);
793
794 return (TRUE == ((GID_T) getegid () == (GID_T) stat_buf.st_gid));
795
796 case 'f': /* File is a file? */
797 unary_advance ();
798 if (test_stat (argv[pos - 1], &stat_buf) < 0)
799 return (FALSE);
800
801 /* Under POSIX, -f is true if the given file exists
802 and is a regular file. */
803 #if defined (S_IFMT)
804 return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
805 (0 == (stat_buf.st_mode & S_IFMT))));
806 #else
807 return (TRUE == (S_ISREG (stat_buf.st_mode)));
808 #endif /* !S_IFMT */
809
810 case 'd': /* File is a directory? */
811 unary_advance ();
812 if (test_stat (argv[pos - 1], &stat_buf) < 0)
813 return (FALSE);
814
815 return (TRUE == (S_ISDIR (stat_buf.st_mode)));
816
817 case 's': /* File has something in it? */
818 unary_advance ();
819 if (test_stat (argv[pos - 1], &stat_buf) < 0)
820 return (FALSE);
821
822 return (TRUE == (stat_buf.st_size > (off_t) 0));
823
824 case 'S': /* File is a socket? */
825 #if !defined (S_ISSOCK)
826 return (FALSE);
827 #else
828 unary_advance ();
829
830 if (test_stat (argv[pos - 1], &stat_buf) < 0)
831 return (FALSE);
832
833 return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
834 #endif /* S_ISSOCK */
835
836 case 'c': /* File is character special? */
837 unary_advance ();
838 if (test_stat (argv[pos - 1], &stat_buf) < 0)
839 return (FALSE);
840
841 return (TRUE == (S_ISCHR (stat_buf.st_mode)));
842
843 case 'b': /* File is block special? */
844 unary_advance ();
845 if (test_stat (argv[pos - 1], &stat_buf) < 0)
846 return (FALSE);
847
848 return (TRUE == (S_ISBLK (stat_buf.st_mode)));
849
850 case 'p': /* File is a named pipe? */
851 unary_advance ();
852 #ifndef S_ISFIFO
853 return (FALSE);
854 #else
855 if (test_stat (argv[pos - 1], &stat_buf) < 0)
856 return (FALSE);
857 return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
858 #endif /* S_ISFIFO */
859
860 case 'L': /* Same as -h */
861 /*FALLTHROUGH*/
862
863 case 'h': /* File is a symbolic link? */
864 unary_advance ();
865 #ifndef S_ISLNK
866 return (FALSE);
867 #else
868 /* An empty filename is not a valid pathname. */
869 if ((argv[pos - 1][0] == '\0') ||
870 (lstat (argv[pos - 1], &stat_buf) < 0))
871 return (FALSE);
872
873 return (TRUE == (S_ISLNK (stat_buf.st_mode)));
874 #endif /* S_IFLNK */
875
876 case 'u': /* File is setuid? */
877 unary_advance ();
878 if (test_stat (argv[pos - 1], &stat_buf) < 0)
879 return (FALSE);
880
881 return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
882
883 case 'g': /* File is setgid? */
884 unary_advance ();
885 if (test_stat (argv[pos - 1], &stat_buf) < 0)
886 return (FALSE);
887
888 return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
889
890 case 'k': /* File has sticky bit set? */
891 unary_advance ();
892 if (test_stat (argv[pos - 1], &stat_buf) < 0)
893 return (FALSE);
894 #if !defined (S_ISVTX)
895 /* This is not Posix, and is not defined on some Posix systems. */
896 return (FALSE);
897 #else
898 return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
899 #endif
900
901 case 't': /* File (fd) is a terminal? (fd) defaults to stdout. */
902 advance (0);
903 if (pos < argc && isint (argv[pos], &r))
904 {
905 advance (0);
906 return (TRUE == (isatty ((int) r)));
907 }
908 return (TRUE == (isatty (1)));
909
910 case 'n': /* True if arg has some length. */
911 unary_advance ();
912 return (TRUE == (argv[pos - 1][0] != 0));
913
914 case 'z': /* True if arg has no length. */
915 unary_advance ();
916 return (TRUE == (argv[pos - 1][0] == '\0'));
917 }
918 }
919
920 /*
921 * and:
922 * term
923 * term '-a' and
924 */
925 static int
926 and ()
927 {
928 int value;
929
930 value = term ();
931 while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
932 {
933 advance (0);
934 value = TRUTH_AND (value, and ());
935 }
936 return (TRUE == value);
937 }
938
939 /*
940 * or:
941 * and
942 * and '-o' or
943 */
944 static int
945 or ()
946 {
947 int value;
948
949 value = and ();
950
951 while (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
952 {
953 advance (0);
954 value = TRUTH_OR (value, or ());
955 }
956
957 return (TRUE == value);
958 }
959
960 /*
961 * expr:
962 * or
963 */
964 static int
965 expr ()
966 {
967 if (pos >= argc)
968 beyond ();
969
970 return (FALSE ^ (or ())); /* Same with this. */
971 }
972
973 /* Return TRUE if S is one of the test command's binary operators. */
974 static int
975 binop (s)
976 char *s;
977 {
978 return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
979 (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
980 (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
981 (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
982 }
983
984 /* Return non-zero if OP is one of the test command's unary operators. */
985 static int
986 unop (op)
987 int op;
988 {
989 return (member (op, "abcdefgkLhprsStuwxOGnz"));
990 }
991
992 static int
993 two_arguments ()
994 {
995 int value;
996
997 if (argv[pos][0] == '!' && !argv[pos][1])
998 value = argv[pos + 1][0] == '\0';
999 else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
1000 {
1001 if (unop (argv[pos][1]))
1002 value = unary_operator ();
1003 else
1004 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
1005 }
1006 else
1007 test_syntax_error ("%s: unary operator expected\n", argv[pos]);
1008
1009 return (value);
1010 }
1011
1012 static int
1013 three_arguments ()
1014 {
1015 int value;
1016
1017 if (argv[pos][0] == '!' && !argv[pos][1])
1018 {
1019 advance (1);
1020 value = !two_arguments ();
1021 }
1022 else if (binop (argv[pos+1]))
1023 {
1024 value = binary_operator ();
1025 pos = argc;
1026 }
1027 /* Check for -a or -o or a parenthesized subexpression. */
1028 else if ((argv[pos+1][0] == '-' && !argv[pos+1][2] &&
1029 (argv[pos+1][1] == 'a' || argv[pos+1][1] == 'o')) ||
1030 (argv[pos][0] == '('))
1031 value = expr ();
1032 else
1033 test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
1034 return (value);
1035 }
1036
1037 /* This is an implementation of a Posix.2 proposal by David Korn. */
1038 static int
1039 posixtest ()
1040 {
1041 int value;
1042
1043 switch (argc - 1) /* one extra passed in */
1044 {
1045 case 0:
1046 value = FALSE;
1047 pos = argc;
1048 break;
1049
1050 case 1:
1051 value = argv[1][0] != '\0';
1052 pos = argc;
1053 break;
1054
1055 case 2:
1056 value = two_arguments ();
1057 pos = argc;
1058 break;
1059
1060 case 3:
1061 value = three_arguments ();
1062 break;
1063
1064 case 4:
1065 if (STREQ (argv[pos], "!"))
1066 {
1067 advance (1);
1068 value = !three_arguments ();
1069 break;
1070 }
1071 /* FALLTHROUGH */
1072 case 5:
1073 default:
1074 value = expr ();
1075 }
1076
1077 return (value);
1078 }
1079
1080 /*
1081 * [:
1082 * '[' expr ']'
1083 * test:
1084 * test expr
1085 */
1086 int
1087 #if defined (STANDALONE)
1088 main (margc, margv)
1089 #else
1090 test_command (margc, margv)
1091 #endif /* STANDALONE */
1092 int margc;
1093 char **margv;
1094 {
1095 int value;
1096
1097 #if !defined (STANDALONE)
1098 int code;
1099
1100 code = setjmp (test_exit_buf);
1101
1102 if (code)
1103 return (test_error_return);
1104 #endif /* STANDALONE */
1105
1106 argv = margv;
1107
1108 if (margv[0] && margv[0][0] == '[' && !margv[0][1])
1109 {
1110 --margc;
1111
1112 if (margc < 2)
1113 test_exit (SHELL_BOOLEAN (FALSE));
1114
1115 if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
1116 test_syntax_error ("missing `]'\n", (char *)NULL);
1117 }
1118
1119 argc = margc;
1120 pos = 1;
1121
1122 if (pos >= argc)
1123 test_exit (SHELL_BOOLEAN (FALSE));
1124
1125 noeval = 0;
1126 value = posixtest ();
1127
1128 if (pos != argc)
1129 test_syntax_error ("too many arguments\n", (char *)NULL);
1130
1131 test_exit (SHELL_BOOLEAN (value));
1132 }