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