]> git.ipfire.org Git - thirdparty/bash.git/blame - test.c
Bash-4.3 patch 1
[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) \
726f6388 104 do { test_error_return = val; 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
cce855bc 228 * '-o' option
726f6388 229 * string
ccc6cda3 230 * string ('!='|'='|'==') string
726f6388
JA
231 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
232 * file '-'(nt|ot|ef) file
233 * '(' <expr> ')'
234 * int ::=
726f6388
JA
235 * positive and negative integers
236 */
237static int
238term ()
239{
240 int value;
241
242 if (pos >= argc)
243 beyond ();
244
ccc6cda3
JA
245 /* Deal with leading `not's. */
246 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388 247 {
ccc6cda3
JA
248 value = 0;
249 while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
250 {
251 advance (1);
ccc6cda3 252 value = 1 - value;
726f6388
JA
253 }
254
ccc6cda3 255 return (value ? !term() : term());
726f6388
JA
256 }
257
ccc6cda3 258 /* A paren-bracketed argument. */
b80f6443 259 if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
726f6388
JA
260 {
261 advance (1);
262 value = expr ();
b80f6443
JA
263 if (argv[pos] == 0) /* ( */
264 test_syntax_error (_("`)' expected"), (char *)NULL);
265 else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
266 test_syntax_error (_("`)' expected, found %s"), argv[pos]);
726f6388 267 advance (0);
ccc6cda3 268 return (value);
726f6388
JA
269 }
270
271 /* are there enough arguments left that this could be dyadic? */
cce855bc 272 if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
726f6388
JA
273 value = binary_operator ();
274
275 /* Might be a switch type argument */
ccc6cda3 276 else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
726f6388 277 {
cce855bc 278 if (test_unop (argv[pos]))
726f6388
JA
279 value = unary_operator ();
280 else
b80f6443 281 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388
JA
282 }
283 else
284 {
ccc6cda3 285 value = argv[pos][0] != '\0';
726f6388
JA
286 advance (0);
287 }
288
289 return (value);
290}
291
ac50fbac
CR
292static int
293stat_mtime (fn, st, ts)
294 char *fn;
295 struct stat *st;
296 struct timespec *ts;
297{
298 int r;
299
300 r = sh_stat (fn, st);
301 if (r < 0)
302 return r;
303 *ts = get_stat_mtime (st);
304 return 0;
305}
306
726f6388 307static int
ccc6cda3
JA
308filecomp (s, t, op)
309 char *s, *t;
310 int op;
726f6388 311{
ccc6cda3 312 struct stat st1, st2;
ac50fbac 313 struct timespec ts1, ts2;
7117c2d2 314 int r1, r2;
726f6388 315
ac50fbac 316 if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
28ef6c31 317 {
28ef6c31
JA
318 if (op == EF)
319 return (FALSE);
320 }
ac50fbac 321 if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
28ef6c31 322 {
28ef6c31
JA
323 if (op == EF)
324 return (FALSE);
325 }
326
ccc6cda3 327 switch (op)
726f6388 328 {
ac50fbac
CR
329 case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
330 case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
3185942a 331 case EF: return (same_file (s, t, &st1, &st2));
ccc6cda3
JA
332 }
333 return (FALSE);
334}
726f6388 335
ccc6cda3 336static int
cce855bc 337arithcomp (s, t, op, flags)
ccc6cda3 338 char *s, *t;
cce855bc 339 int op, flags;
ccc6cda3 340{
7117c2d2 341 intmax_t l, r;
cce855bc
JA
342 int expok;
343
344 if (flags & TEST_ARITHEXP)
345 {
346 l = evalexp (s, &expok);
347 if (expok == 0)
348 return (FALSE); /* should probably longjmp here */
349 r = evalexp (t, &expok);
350 if (expok == 0)
351 return (FALSE); /* ditto */
352 }
353 else
354 {
355 if (legal_number (s, &l) == 0)
356 integer_expected_error (s);
357 if (legal_number (t, &r) == 0)
358 integer_expected_error (t);
359 }
726f6388 360
ccc6cda3
JA
361 switch (op)
362 {
363 case EQ: return (l == r);
364 case NE: return (l != r);
365 case LT: return (l < r);
366 case GT: return (l > r);
367 case LE: return (l <= r);
368 case GE: return (l >= r);
726f6388 369 }
cce855bc 370
ccc6cda3
JA
371 return (FALSE);
372}
373
ccc6cda3
JA
374static int
375patcomp (string, pat, op)
376 char *string, *pat;
377 int op;
378{
379 int m;
380
95732b49 381 m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
cce855bc
JA
382 return ((op == EQ) ? (m == 0) : (m != 0));
383}
384
385int
386binary_test (op, arg1, arg2, flags)
387 char *op, *arg1, *arg2;
388 int flags;
389{
390 int patmatch;
391
392 patmatch = (flags & TEST_PATMATCH);
393
394 if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
395 return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
cce855bc 396 else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
0001803f 397 {
ac50fbac 398#if defined (HAVE_STRCOLL)
0001803f
CR
399 if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
400 return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
401 else
ac50fbac 402#endif
0001803f
CR
403 return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
404 }
cce855bc
JA
405 else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
406 return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
0001803f 407
cce855bc
JA
408
409 else if (op[2] == 't')
410 {
411 switch (op[1])
412 {
28ef6c31
JA
413 case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
414 case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
cce855bc
JA
415 case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
416 case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
417 }
418 }
419 else if (op[1] == 'e')
726f6388 420 {
cce855bc
JA
421 switch (op[2])
422 {
423 case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
424 case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
425 }
426 }
427 else if (op[2] == 'e')
428 {
429 switch (op[1])
430 {
431 case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
432 case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
433 case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
434 }
726f6388 435 }
cce855bc
JA
436
437 return (FALSE); /* should never get here */
ccc6cda3 438}
cce855bc 439
726f6388 440
ccc6cda3
JA
441static int
442binary_operator ()
443{
444 int value;
445 char *w;
446
447 w = argv[pos + 1];
cce855bc
JA
448 if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
449 ((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
450 (w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
726f6388 451 {
cce855bc 452 value = binary_test (w, argv[pos], argv[pos + 2], 0);
ccc6cda3
JA
453 pos += 3;
454 return (value);
455 }
cce855bc 456
ccc6cda3
JA
457#if defined (PATTERN_MATCHING)
458 if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
459 {
460 value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
461 pos += 3;
462 return (value);
463 }
464#endif
ccc6cda3 465
cce855bc 466 if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
ccc6cda3 467 {
b80f6443 468 test_syntax_error (_("%s: binary operator expected"), w);
ccc6cda3
JA
469 /* NOTREACHED */
470 return (FALSE);
726f6388 471 }
726f6388 472
cce855bc 473 value = binary_test (w, argv[pos], argv[pos + 2], 0);
ccc6cda3
JA
474 pos += 3;
475 return value;
726f6388
JA
476}
477
478static int
479unary_operator ()
480{
f73dda09 481 char *op;
7117c2d2 482 intmax_t r;
726f6388 483
cce855bc
JA
484 op = argv[pos];
485 if (test_unop (op) == 0)
486 return (FALSE);
487
488 /* the only tricky case is `-t', which may or may not take an argument. */
489 if (op[1] == 't')
726f6388 490 {
cce855bc 491 advance (0);
28ef6c31 492 if (pos < argc)
cce855bc 493 {
28ef6c31
JA
494 if (legal_number (argv[pos], &r))
495 {
496 advance (0);
497 return (unary_test (op, argv[pos - 1]));
498 }
499 else
500 return (FALSE);
cce855bc
JA
501 }
502 else
503 return (unary_test (op, "1"));
504 }
726f6388 505
cce855bc
JA
506 /* All of the unary operators take an argument, so we first call
507 unary_advance (), which checks to make sure that there is an
508 argument, and then advances pos right past it. This means that
509 pos - 1 is the location of the argument. */
510 unary_advance ();
511 return (unary_test (op, argv[pos - 1]));
512}
726f6388 513
cce855bc
JA
514int
515unary_test (op, arg)
516 char *op, *arg;
517{
7117c2d2 518 intmax_t r;
cce855bc 519 struct stat stat_buf;
495aee44 520 SHELL_VAR *v;
cce855bc
JA
521
522 switch (op[1])
523 {
726f6388
JA
524 case 'a': /* file exists in the file system? */
525 case 'e':
0628567a 526 return (sh_stat (arg, &stat_buf) == 0);
726f6388
JA
527
528 case 'r': /* file is readable? */
0628567a 529 return (sh_eaccess (arg, R_OK) == 0);
726f6388
JA
530
531 case 'w': /* File is writeable? */
0628567a 532 return (sh_eaccess (arg, W_OK) == 0);
726f6388
JA
533
534 case 'x': /* File is executable? */
0628567a 535 return (sh_eaccess (arg, X_OK) == 0);
726f6388
JA
536
537 case 'O': /* File is owned by you? */
0628567a 538 return (sh_stat (arg, &stat_buf) == 0 &&
d166f048 539 (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
726f6388
JA
540
541 case 'G': /* File is owned by your group? */
0628567a 542 return (sh_stat (arg, &stat_buf) == 0 &&
d166f048 543 (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
726f6388 544
cce855bc 545 case 'N':
0628567a 546 return (sh_stat (arg, &stat_buf) == 0 &&
cce855bc
JA
547 stat_buf.st_atime <= stat_buf.st_mtime);
548
726f6388 549 case 'f': /* File is a file? */
0628567a 550 if (sh_stat (arg, &stat_buf) < 0)
726f6388
JA
551 return (FALSE);
552
ccc6cda3 553 /* -f is true if the given file exists and is a regular file. */
726f6388 554#if defined (S_IFMT)
ccc6cda3 555 return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
726f6388 556#else
ccc6cda3 557 return (S_ISREG (stat_buf.st_mode));
726f6388
JA
558#endif /* !S_IFMT */
559
560 case 'd': /* File is a directory? */
0628567a 561 return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
726f6388
JA
562
563 case 's': /* File has something in it? */
0628567a 564 return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
726f6388
JA
565
566 case 'S': /* File is a socket? */
567#if !defined (S_ISSOCK)
568 return (FALSE);
569#else
0628567a 570 return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
ccc6cda3 571#endif /* S_ISSOCK */
726f6388
JA
572
573 case 'c': /* File is character special? */
0628567a 574 return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
726f6388
JA
575
576 case 'b': /* File is block special? */
0628567a 577 return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
726f6388
JA
578
579 case 'p': /* File is a named pipe? */
726f6388
JA
580#ifndef S_ISFIFO
581 return (FALSE);
582#else
0628567a 583 return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
ccc6cda3 584#endif /* S_ISFIFO */
726f6388
JA
585
586 case 'L': /* Same as -h */
726f6388 587 case 'h': /* File is a symbolic link? */
ccc6cda3 588#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
726f6388
JA
589 return (FALSE);
590#else
cce855bc
JA
591 return ((arg[0] != '\0') &&
592 (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
ccc6cda3 593#endif /* S_IFLNK && HAVE_LSTAT */
726f6388
JA
594
595 case 'u': /* File is setuid? */
0628567a 596 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
726f6388
JA
597
598 case 'g': /* File is setgid? */
0628567a 599 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
726f6388
JA
600
601 case 'k': /* File has sticky bit set? */
726f6388
JA
602#if !defined (S_ISVTX)
603 /* This is not Posix, and is not defined on some Posix systems. */
604 return (FALSE);
605#else
0628567a 606 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
726f6388
JA
607#endif
608
cce855bc
JA
609 case 't': /* File fd is a terminal? */
610 if (legal_number (arg, &r) == 0)
611 return (FALSE);
f73dda09 612 return ((r == (int)r) && isatty ((int)r));
726f6388
JA
613
614 case 'n': /* True if arg has some length. */
cce855bc 615 return (arg[0] != '\0');
726f6388
JA
616
617 case 'z': /* True if arg has no length. */
cce855bc 618 return (arg[0] == '\0');
726f6388 619
cce855bc
JA
620 case 'o': /* True if option `arg' is set. */
621 return (minus_o_option_value (arg) == 1);
495aee44
CR
622
623 case 'v':
624 v = find_variable (arg);
ac50fbac
CR
625#if defined (ARRAY_VARS)
626 if (v == 0 && valid_array_reference (arg))
627 {
628 char *t;
629 t = array_value (arg, 0, 0, (int *)0, (arrayind_t *)0);
630 return (t ? TRUE : FALSE);
631 }
632 else if (v && invisible_p (v) == 0 && array_p (v))
633 {
634 char *t;
635 /* [[ -v foo ]] == [[ -v foo[0] ]] */
636 t = array_reference (array_cell (v), 0);
637 return (t ? TRUE : FALSE);
638 }
639 else if (v && invisible_p (v) == 0 && assoc_p (v))
640 {
641 char *t;
642 t = assoc_reference (assoc_cell (v), "0");
643 return (t ? TRUE : FALSE);
644 }
645#endif
646 return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
647
648 case 'R':
f5c8237d
CR
649 v = find_variable_noref (arg);
650 return ((v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v)) ? TRUE : FALSE);
726f6388 651 }
f73dda09
JA
652
653 /* We can't actually get here, but this shuts up gcc. */
654 return (FALSE);
726f6388
JA
655}
656
cce855bc
JA
657/* Return TRUE if OP is one of the test command's binary operators. */
658int
659test_binop (op)
660 char *op;
726f6388 661{
cce855bc 662 if (op[0] == '=' && op[1] == '\0')
ccc6cda3 663 return (1); /* '=' */
cce855bc 664 else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
ccc6cda3 665 return (1);
cce855bc 666 else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
ccc6cda3
JA
667 return (1); /* `==' and `!=' */
668#if defined (PATTERN_MATCHING)
cce855bc 669 else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
ccc6cda3
JA
670 return (1);
671#endif
cce855bc 672 else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
ccc6cda3
JA
673 return (0);
674 else
675 {
cce855bc
JA
676 if (op[2] == 't')
677 switch (op[1])
ccc6cda3 678 {
cce855bc
JA
679 case 'n': /* -nt */
680 case 'o': /* -ot */
681 case 'l': /* -lt */
682 case 'g': /* -gt */
683 return (1);
684 default:
685 return (0);
ccc6cda3 686 }
cce855bc
JA
687 else if (op[1] == 'e')
688 switch (op[2])
ccc6cda3 689 {
cce855bc
JA
690 case 'q': /* -eq */
691 case 'f': /* -ef */
692 return (1);
693 default:
694 return (0);
ccc6cda3 695 }
cce855bc
JA
696 else if (op[2] == 'e')
697 switch (op[1])
ccc6cda3 698 {
cce855bc
JA
699 case 'n': /* -ne */
700 case 'g': /* -ge */
701 case 'l': /* -le */
702 return (1);
703 default:
704 return (0);
ccc6cda3
JA
705 }
706 else
28ef6c31 707 return (0);
ccc6cda3 708 }
726f6388
JA
709}
710
711/* Return non-zero if OP is one of the test command's unary operators. */
cce855bc
JA
712int
713test_unop (op)
714 char *op;
726f6388 715{
3185942a 716 if (op[0] != '-' || op[2] != 0)
cce855bc
JA
717 return (0);
718
719 switch (op[1])
ccc6cda3
JA
720 {
721 case 'a': case 'b': case 'c': case 'd': case 'e':
722 case 'f': case 'g': case 'h': case 'k': case 'n':
cce855bc 723 case 'o': case 'p': case 'r': case 's': case 't':
495aee44 724 case 'u': case 'v': case 'w': case 'x': case 'z':
cce855bc 725 case 'G': case 'L': case 'O': case 'S': case 'N':
f5c8237d 726 case 'R':
ccc6cda3
JA
727 return (1);
728 }
cce855bc 729
ccc6cda3 730 return (0);
726f6388
JA
731}
732
733static int
734two_arguments ()
735{
ccc6cda3
JA
736 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
737 return (argv[pos + 1][0] == '\0');
738 else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
726f6388 739 {
cce855bc 740 if (test_unop (argv[pos]))
ccc6cda3 741 return (unary_operator ());
726f6388 742 else
b80f6443 743 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388
JA
744 }
745 else
b80f6443 746 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388 747
ccc6cda3 748 return (0);
726f6388
JA
749}
750
ccc6cda3
JA
751#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
752
f73dda09
JA
753/* This could be augmented to handle `-t' as equivalent to `-t 1', but
754 POSIX requires that `-t' be given an argument. */
ccc6cda3
JA
755#define ONE_ARG_TEST(s) ((s)[0] != '\0')
756
726f6388
JA
757static int
758three_arguments ()
759{
760 int value;
761
cce855bc 762 if (test_binop (argv[pos+1]))
ccc6cda3
JA
763 {
764 value = binary_operator ();
765 pos = argc;
766 }
767 else if (ANDOR (argv[pos+1]))
768 {
769 if (argv[pos+1][1] == 'a')
28ef6c31 770 value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
ccc6cda3 771 else
28ef6c31 772 value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
ccc6cda3
JA
773 pos = argc;
774 }
cce855bc 775 else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
776 {
777 advance (1);
778 value = !two_arguments ();
779 }
ccc6cda3 780 else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
726f6388 781 {
ccc6cda3 782 value = ONE_ARG_TEST(argv[pos+1]);
726f6388
JA
783 pos = argc;
784 }
726f6388 785 else
b80f6443 786 test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
ccc6cda3 787
726f6388
JA
788 return (value);
789}
790
791/* This is an implementation of a Posix.2 proposal by David Korn. */
792static int
793posixtest ()
794{
795 int value;
796
797 switch (argc - 1) /* one extra passed in */
798 {
799 case 0:
800 value = FALSE;
801 pos = argc;
802 break;
803
804 case 1:
ccc6cda3 805 value = ONE_ARG_TEST(argv[1]);
726f6388
JA
806 pos = argc;
807 break;
808
809 case 2:
810 value = two_arguments ();
811 pos = argc;
812 break;
813
814 case 3:
815 value = three_arguments ();
816 break;
817
818 case 4:
ccc6cda3 819 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
820 {
821 advance (1);
822 value = !three_arguments ();
823 break;
824 }
825 /* FALLTHROUGH */
726f6388
JA
826 default:
827 value = expr ();
828 }
829
830 return (value);
831}
832
833/*
834 * [:
835 * '[' expr ']'
836 * test:
837 * test expr
838 */
839int
d166f048 840test_command (margc, margv)
726f6388
JA
841 int margc;
842 char **margv;
843{
844 int value;
726f6388
JA
845 int code;
846
f73dda09
JA
847 USE_VAR(margc);
848
ac50fbac 849 code = setjmp_nosigs (test_exit_buf);
726f6388
JA
850
851 if (code)
852 return (test_error_return);
726f6388
JA
853
854 argv = margv;
855
ccc6cda3 856 if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
726f6388
JA
857 {
858 --margc;
859
726f6388 860 if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
b80f6443 861 test_syntax_error (_("missing `]'"), (char *)NULL);
28ef6c31
JA
862
863 if (margc < 2)
864 test_exit (SHELL_BOOLEAN (FALSE));
726f6388
JA
865 }
866
867 argc = margc;
868 pos = 1;
869
870 if (pos >= argc)
871 test_exit (SHELL_BOOLEAN (FALSE));
872
873 noeval = 0;
874 value = posixtest ();
875
876 if (pos != argc)
b80f6443 877 test_syntax_error (_("too many arguments"), (char *)NULL);
726f6388
JA
878
879 test_exit (SHELL_BOOLEAN (value));
880}