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