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