]> git.ipfire.org Git - thirdparty/bash.git/blame - test.c
Bash-5.2 patch 26: fix typo when specifying readline's custom color prefix
[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
74091dd4 5/* Copyright (C) 1987-2021 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
8868edaf 106extern int sh_stat PARAMS((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
8868edaf
CR
113static void test_syntax_error PARAMS((char *, char *)) __attribute__((__noreturn__));
114static void beyond PARAMS((void)) __attribute__((__noreturn__));
115static void integer_expected_error PARAMS((char *)) __attribute__((__noreturn__));
f73dda09 116
8868edaf
CR
117static int unary_operator PARAMS((void));
118static int binary_operator PARAMS((void));
119static int two_arguments PARAMS((void));
120static int three_arguments PARAMS((void));
121static int posixtest PARAMS((void));
726f6388 122
8868edaf
CR
123static int expr PARAMS((void));
124static int term PARAMS((void));
125static int and PARAMS((void));
126static int or PARAMS((void));
726f6388 127
8868edaf
CR
128static int filecomp PARAMS((char *, char *, int));
129static int arithcomp PARAMS((char *, char *, int, int));
130static int patcomp PARAMS((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
8868edaf
CR
276 /* Might be a switch type argument -- make sure we have enough arguments for
277 the unary operator and argument */
278 else if ((pos + 2) <= argc && test_unop (argv[pos]))
279 value = unary_operator ();
280
726f6388
JA
281 else
282 {
ccc6cda3 283 value = argv[pos][0] != '\0';
726f6388
JA
284 advance (0);
285 }
286
287 return (value);
288}
289
ac50fbac
CR
290static int
291stat_mtime (fn, st, ts)
292 char *fn;
293 struct stat *st;
294 struct timespec *ts;
295{
296 int r;
297
298 r = sh_stat (fn, st);
299 if (r < 0)
300 return r;
301 *ts = get_stat_mtime (st);
302 return 0;
303}
304
726f6388 305static int
ccc6cda3
JA
306filecomp (s, t, op)
307 char *s, *t;
308 int op;
726f6388 309{
ccc6cda3 310 struct stat st1, st2;
ac50fbac 311 struct timespec ts1, ts2;
7117c2d2 312 int r1, r2;
726f6388 313
ac50fbac 314 if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
28ef6c31 315 {
28ef6c31
JA
316 if (op == EF)
317 return (FALSE);
318 }
ac50fbac 319 if ((r2 = stat_mtime (t, &st2, &ts2)) < 0)
28ef6c31 320 {
28ef6c31
JA
321 if (op == EF)
322 return (FALSE);
323 }
324
ccc6cda3 325 switch (op)
726f6388 326 {
ac50fbac
CR
327 case OT: return (r1 < r2 || (r2 == 0 && timespec_cmp (ts1, ts2) < 0));
328 case NT: return (r1 > r2 || (r1 == 0 && timespec_cmp (ts1, ts2) > 0));
3185942a 329 case EF: return (same_file (s, t, &st1, &st2));
ccc6cda3
JA
330 }
331 return (FALSE);
332}
726f6388 333
ccc6cda3 334static int
cce855bc 335arithcomp (s, t, op, flags)
ccc6cda3 336 char *s, *t;
cce855bc 337 int op, flags;
ccc6cda3 338{
7117c2d2 339 intmax_t l, r;
cce855bc
JA
340 int expok;
341
74091dd4 342 if (flags & TEST_ARITHEXP) /* conditional command */
cce855bc 343 {
74091dd4
CR
344 int eflag;
345
346 eflag = (shell_compatibility_level > 51) ? 0 : EXP_EXPANDED;
347 l = evalexp (s, eflag, &expok);
cce855bc
JA
348 if (expok == 0)
349 return (FALSE); /* should probably longjmp here */
74091dd4 350 r = evalexp (t, eflag, &expok);
cce855bc
JA
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);
74091dd4 498 return (unary_test (op, argv[pos - 1], 0));
28ef6c31
JA
499 }
500 else
501 return (FALSE);
cce855bc
JA
502 }
503 else
74091dd4 504 return (unary_test (op, "1", 0));
cce855bc 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 ();
74091dd4 512 return (unary_test (op, argv[pos - 1], 0));
cce855bc 513}
726f6388 514
cce855bc 515int
74091dd4 516unary_test (op, arg, flags)
cce855bc 517 char *op, *arg;
74091dd4 518 int flags;
cce855bc 519{
7117c2d2 520 intmax_t r;
cce855bc 521 struct stat stat_buf;
8868edaf 522 struct timespec mtime, atime;
495aee44 523 SHELL_VAR *v;
74091dd4 524 int aflags;
cce855bc
JA
525
526 switch (op[1])
527 {
726f6388
JA
528 case 'a': /* file exists in the file system? */
529 case 'e':
0628567a 530 return (sh_stat (arg, &stat_buf) == 0);
726f6388
JA
531
532 case 'r': /* file is readable? */
0628567a 533 return (sh_eaccess (arg, R_OK) == 0);
726f6388
JA
534
535 case 'w': /* File is writeable? */
0628567a 536 return (sh_eaccess (arg, W_OK) == 0);
726f6388
JA
537
538 case 'x': /* File is executable? */
0628567a 539 return (sh_eaccess (arg, X_OK) == 0);
726f6388
JA
540
541 case 'O': /* File is owned by you? */
0628567a 542 return (sh_stat (arg, &stat_buf) == 0 &&
d166f048 543 (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
726f6388
JA
544
545 case 'G': /* File is owned by your group? */
0628567a 546 return (sh_stat (arg, &stat_buf) == 0 &&
d166f048 547 (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
726f6388 548
cce855bc 549 case 'N':
8868edaf
CR
550 if (sh_stat (arg, &stat_buf) < 0)
551 return (FALSE);
552 atime = get_stat_atime (&stat_buf);
553 mtime = get_stat_mtime (&stat_buf);
554 return (timespec_cmp (mtime, atime) > 0);
cce855bc 555
726f6388 556 case 'f': /* File is a file? */
0628567a 557 if (sh_stat (arg, &stat_buf) < 0)
726f6388
JA
558 return (FALSE);
559
ccc6cda3 560 /* -f is true if the given file exists and is a regular file. */
726f6388 561#if defined (S_IFMT)
ccc6cda3 562 return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
726f6388 563#else
ccc6cda3 564 return (S_ISREG (stat_buf.st_mode));
726f6388
JA
565#endif /* !S_IFMT */
566
567 case 'd': /* File is a directory? */
0628567a 568 return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
726f6388
JA
569
570 case 's': /* File has something in it? */
0628567a 571 return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
726f6388
JA
572
573 case 'S': /* File is a socket? */
574#if !defined (S_ISSOCK)
575 return (FALSE);
576#else
0628567a 577 return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
ccc6cda3 578#endif /* S_ISSOCK */
726f6388
JA
579
580 case 'c': /* File is character special? */
0628567a 581 return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
726f6388
JA
582
583 case 'b': /* File is block special? */
0628567a 584 return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
726f6388
JA
585
586 case 'p': /* File is a named pipe? */
726f6388
JA
587#ifndef S_ISFIFO
588 return (FALSE);
589#else
0628567a 590 return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
ccc6cda3 591#endif /* S_ISFIFO */
726f6388
JA
592
593 case 'L': /* Same as -h */
726f6388 594 case 'h': /* File is a symbolic link? */
ccc6cda3 595#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
726f6388
JA
596 return (FALSE);
597#else
cce855bc
JA
598 return ((arg[0] != '\0') &&
599 (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
ccc6cda3 600#endif /* S_IFLNK && HAVE_LSTAT */
726f6388
JA
601
602 case 'u': /* File is setuid? */
0628567a 603 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
726f6388
JA
604
605 case 'g': /* File is setgid? */
0628567a 606 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
726f6388
JA
607
608 case 'k': /* File has sticky bit set? */
726f6388
JA
609#if !defined (S_ISVTX)
610 /* This is not Posix, and is not defined on some Posix systems. */
611 return (FALSE);
612#else
0628567a 613 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
726f6388
JA
614#endif
615
cce855bc
JA
616 case 't': /* File fd is a terminal? */
617 if (legal_number (arg, &r) == 0)
618 return (FALSE);
f73dda09 619 return ((r == (int)r) && isatty ((int)r));
726f6388
JA
620
621 case 'n': /* True if arg has some length. */
cce855bc 622 return (arg[0] != '\0');
726f6388
JA
623
624 case 'z': /* True if arg has no length. */
cce855bc 625 return (arg[0] == '\0');
726f6388 626
cce855bc
JA
627 case 'o': /* True if option `arg' is set. */
628 return (minus_o_option_value (arg) == 1);
495aee44
CR
629
630 case 'v':
ac50fbac 631#if defined (ARRAY_VARS)
74091dd4
CR
632 aflags = assoc_expand_once ? AV_NOEXPAND : 0;
633 if (valid_array_reference (arg, aflags))
ac50fbac
CR
634 {
635 char *t;
74091dd4
CR
636 int ret;
637 array_eltstate_t es;
8868edaf
CR
638
639 /* Let's assume that this has already been expanded once. */
74091dd4
CR
640 /* XXX - TAG:bash-5.2 fix with corresponding fix to execute_cmd.c:
641 execute_cond_node() that passes TEST_ARRAYEXP in FLAGS */
642
643 if (shell_compatibility_level > 51)
644 /* Allow associative arrays to use `test -v array[@]' to look for
645 a key named `@'. */
646 aflags |= AV_ATSTARKEYS; /* XXX */
647 init_eltstate (&es);
648 t = get_array_value (arg, aflags|AV_ALLOWALL, &es);
d233b485 649 ret = t ? TRUE : FALSE;
74091dd4 650 if (es.subtype > 0) /* subscript is * or @ */
d233b485 651 free (t);
74091dd4 652 flush_eltstate (&es);
d233b485 653 return ret;
ac50fbac 654 }
8868edaf
CR
655 else if (legal_number (arg, &r)) /* -v n == is $n set? */
656 return ((r >= 0 && r <= number_of_args()) ? TRUE : FALSE);
d233b485
CR
657 v = find_variable (arg);
658 if (v && invisible_p (v) == 0 && array_p (v))
ac50fbac
CR
659 {
660 char *t;
661 /* [[ -v foo ]] == [[ -v foo[0] ]] */
662 t = array_reference (array_cell (v), 0);
663 return (t ? TRUE : FALSE);
664 }
665 else if (v && invisible_p (v) == 0 && assoc_p (v))
666 {
667 char *t;
668 t = assoc_reference (assoc_cell (v), "0");
669 return (t ? TRUE : FALSE);
670 }
d233b485
CR
671#else
672 v = find_variable (arg);
ac50fbac
CR
673#endif
674 return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
675
676 case 'R':
f5c8237d
CR
677 v = find_variable_noref (arg);
678 return ((v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v)) ? TRUE : FALSE);
726f6388 679 }
f73dda09
JA
680
681 /* We can't actually get here, but this shuts up gcc. */
682 return (FALSE);
726f6388
JA
683}
684
cce855bc
JA
685/* Return TRUE if OP is one of the test command's binary operators. */
686int
687test_binop (op)
688 char *op;
726f6388 689{
cce855bc 690 if (op[0] == '=' && op[1] == '\0')
ccc6cda3 691 return (1); /* '=' */
cce855bc 692 else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
ccc6cda3 693 return (1);
cce855bc 694 else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
ccc6cda3
JA
695 return (1); /* `==' and `!=' */
696#if defined (PATTERN_MATCHING)
cce855bc 697 else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
ccc6cda3
JA
698 return (1);
699#endif
d233b485 700 else if (op[0] != '-' || op[1] == '\0' || op[2] == '\0' || op[3] != '\0')
ccc6cda3
JA
701 return (0);
702 else
703 {
cce855bc
JA
704 if (op[2] == 't')
705 switch (op[1])
ccc6cda3 706 {
cce855bc
JA
707 case 'n': /* -nt */
708 case 'o': /* -ot */
709 case 'l': /* -lt */
710 case 'g': /* -gt */
711 return (1);
712 default:
713 return (0);
ccc6cda3 714 }
cce855bc
JA
715 else if (op[1] == 'e')
716 switch (op[2])
ccc6cda3 717 {
cce855bc
JA
718 case 'q': /* -eq */
719 case 'f': /* -ef */
720 return (1);
721 default:
722 return (0);
ccc6cda3 723 }
cce855bc
JA
724 else if (op[2] == 'e')
725 switch (op[1])
ccc6cda3 726 {
cce855bc
JA
727 case 'n': /* -ne */
728 case 'g': /* -ge */
729 case 'l': /* -le */
730 return (1);
731 default:
732 return (0);
ccc6cda3
JA
733 }
734 else
28ef6c31 735 return (0);
ccc6cda3 736 }
726f6388
JA
737}
738
739/* Return non-zero if OP is one of the test command's unary operators. */
cce855bc
JA
740int
741test_unop (op)
742 char *op;
726f6388 743{
d233b485 744 if (op[0] != '-' || (op[1] && op[2] != 0))
cce855bc
JA
745 return (0);
746
747 switch (op[1])
ccc6cda3
JA
748 {
749 case 'a': case 'b': case 'c': case 'd': case 'e':
750 case 'f': case 'g': case 'h': case 'k': case 'n':
cce855bc 751 case 'o': case 'p': case 'r': case 's': case 't':
495aee44 752 case 'u': case 'v': case 'w': case 'x': case 'z':
cce855bc 753 case 'G': case 'L': case 'O': case 'S': case 'N':
f5c8237d 754 case 'R':
ccc6cda3
JA
755 return (1);
756 }
cce855bc 757
ccc6cda3 758 return (0);
726f6388
JA
759}
760
761static int
762two_arguments ()
763{
ccc6cda3
JA
764 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
765 return (argv[pos + 1][0] == '\0');
d233b485 766 else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
726f6388 767 {
cce855bc 768 if (test_unop (argv[pos]))
ccc6cda3 769 return (unary_operator ());
726f6388 770 else
b80f6443 771 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388
JA
772 }
773 else
b80f6443 774 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388 775
ccc6cda3 776 return (0);
726f6388
JA
777}
778
d233b485 779#define ANDOR(s) (s[0] == '-' && (s[1] == 'a' || s[1] == 'o') && s[2] == 0)
ccc6cda3 780
f73dda09
JA
781/* This could be augmented to handle `-t' as equivalent to `-t 1', but
782 POSIX requires that `-t' be given an argument. */
ccc6cda3
JA
783#define ONE_ARG_TEST(s) ((s)[0] != '\0')
784
726f6388
JA
785static int
786three_arguments ()
787{
788 int value;
789
cce855bc 790 if (test_binop (argv[pos+1]))
ccc6cda3
JA
791 {
792 value = binary_operator ();
793 pos = argc;
794 }
795 else if (ANDOR (argv[pos+1]))
796 {
797 if (argv[pos+1][1] == 'a')
28ef6c31 798 value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
ccc6cda3 799 else
28ef6c31 800 value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
ccc6cda3
JA
801 pos = argc;
802 }
cce855bc 803 else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
804 {
805 advance (1);
806 value = !two_arguments ();
74091dd4 807 pos = argc;
726f6388 808 }
ccc6cda3 809 else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
726f6388 810 {
ccc6cda3 811 value = ONE_ARG_TEST(argv[pos+1]);
726f6388
JA
812 pos = argc;
813 }
726f6388 814 else
b80f6443 815 test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
ccc6cda3 816
726f6388
JA
817 return (value);
818}
819
820/* This is an implementation of a Posix.2 proposal by David Korn. */
821static int
822posixtest ()
823{
824 int value;
825
826 switch (argc - 1) /* one extra passed in */
827 {
828 case 0:
829 value = FALSE;
830 pos = argc;
831 break;
832
833 case 1:
ccc6cda3 834 value = ONE_ARG_TEST(argv[1]);
726f6388
JA
835 pos = argc;
836 break;
837
838 case 2:
839 value = two_arguments ();
840 pos = argc;
841 break;
842
843 case 3:
844 value = three_arguments ();
845 break;
846
847 case 4:
ccc6cda3 848 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
849 {
850 advance (1);
851 value = !three_arguments ();
852 break;
853 }
a0c0a00f
CR
854 else if (argv[pos][0] == '(' && argv[pos][1] == '\0' && argv[argc-1][0] == ')' && argv[argc-1][1] == '\0')
855 {
856 advance (1);
857 value = two_arguments ();
858 pos = argc;
859 break;
860 }
726f6388 861 /* FALLTHROUGH */
726f6388
JA
862 default:
863 value = expr ();
864 }
865
866 return (value);
867}
868
869/*
870 * [:
871 * '[' expr ']'
872 * test:
873 * test expr
874 */
875int
d166f048 876test_command (margc, margv)
726f6388
JA
877 int margc;
878 char **margv;
879{
880 int value;
726f6388
JA
881 int code;
882
f73dda09
JA
883 USE_VAR(margc);
884
ac50fbac 885 code = setjmp_nosigs (test_exit_buf);
726f6388
JA
886
887 if (code)
888 return (test_error_return);
726f6388
JA
889
890 argv = margv;
891
ccc6cda3 892 if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
726f6388
JA
893 {
894 --margc;
895
726f6388 896 if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
b80f6443 897 test_syntax_error (_("missing `]'"), (char *)NULL);
28ef6c31
JA
898
899 if (margc < 2)
900 test_exit (SHELL_BOOLEAN (FALSE));
726f6388
JA
901 }
902
903 argc = margc;
904 pos = 1;
905
906 if (pos >= argc)
907 test_exit (SHELL_BOOLEAN (FALSE));
908
909 noeval = 0;
910 value = posixtest ();
911
912 if (pos != argc)
8868edaf
CR
913 {
914 if (pos < argc && argv[pos][0] == '-')
915 test_syntax_error (_("syntax error: `%s' unexpected"), argv[pos]);
916 else
917 test_syntax_error (_("too many arguments"), (char *)NULL);
918 }
726f6388
JA
919
920 test_exit (SHELL_BOOLEAN (value));
921}