]> git.ipfire.org Git - thirdparty/bash.git/blame - test.c
bash-4.3-rc1 overlay
[thirdparty/bash.git] / test.c
CommitLineData
2e4498b3 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
6faad625 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
2e4498b3
CR
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
2e4498b3
CR
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
2e4498b3
CR
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
fd58d46e 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
d3a24ed2 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"
278286c9 53#include "stat-time.h"
726f6388 54
5e13499c
CR
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)
a8fd3f3e 69# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
726f6388 70#endif /* !STREQ */
a8fd3f3e 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
ac18b312 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{
5e13499c 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{
5e13499c 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
8581f42d 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. */
5e13499c 259 if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
726f6388
JA
260 {
261 advance (1);
262 value = expr ();
5e13499c
CR
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
5e13499c 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
278286c9
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;
278286c9 313 struct timespec ts1, ts2;
7117c2d2 314 int r1, r2;
726f6388 315
278286c9 316 if ((r1 = stat_mtime (s, &st1, &ts1)) < 0)
28ef6c31 317 {
28ef6c31
JA
318 if (op == EF)
319 return (FALSE);
320 }
278286c9 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 {
278286c9
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));
e77a3058 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
2206f89a 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')
a8fd3f3e 397 {
10e78433 398#if defined (HAVE_STRCOLL)
6bf8a8a7 399 if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
a8fd3f3e
CR
400 return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0));
401 else
10e78433 402#endif
a8fd3f3e
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));
a8fd3f3e 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 {
5e13499c 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;
6faad625 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':
ac18b312 526 return (sh_stat (arg, &stat_buf) == 0);
726f6388
JA
527
528 case 'r': /* file is readable? */
ac18b312 529 return (sh_eaccess (arg, R_OK) == 0);
726f6388
JA
530
531 case 'w': /* File is writeable? */
ac18b312 532 return (sh_eaccess (arg, W_OK) == 0);
726f6388
JA
533
534 case 'x': /* File is executable? */
ac18b312 535 return (sh_eaccess (arg, X_OK) == 0);
726f6388
JA
536
537 case 'O': /* File is owned by you? */
ac18b312 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? */
ac18b312 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':
ac18b312 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? */
ac18b312 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? */
ac18b312 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? */
ac18b312 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
ac18b312 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? */
ac18b312 574 return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
726f6388
JA
575
576 case 'b': /* File is block special? */
ac18b312 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
ac18b312 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? */
ac18b312 596 return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
726f6388
JA
597
598 case 'g': /* File is setgid? */
ac18b312 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
ac18b312 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);
6faad625
CR
622
623 case 'v':
624 v = find_variable (arg);
3087e51c 625 return (v && invisible_p (v) == 0 && var_isset (v) ? TRUE : FALSE);
15623760
CR
626
627 case 'R':
628 v = find_variable (arg);
3087e51c 629 return (v && invisible_p (v) == 0 && var_isset (v) && nameref_p (v) ? TRUE : FALSE);
726f6388 630 }
f73dda09
JA
631
632 /* We can't actually get here, but this shuts up gcc. */
633 return (FALSE);
726f6388
JA
634}
635
cce855bc
JA
636/* Return TRUE if OP is one of the test command's binary operators. */
637int
638test_binop (op)
639 char *op;
726f6388 640{
cce855bc 641 if (op[0] == '=' && op[1] == '\0')
ccc6cda3 642 return (1); /* '=' */
cce855bc 643 else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
ccc6cda3 644 return (1);
cce855bc 645 else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
ccc6cda3
JA
646 return (1); /* `==' and `!=' */
647#if defined (PATTERN_MATCHING)
cce855bc 648 else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
ccc6cda3
JA
649 return (1);
650#endif
cce855bc 651 else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
ccc6cda3
JA
652 return (0);
653 else
654 {
cce855bc
JA
655 if (op[2] == 't')
656 switch (op[1])
ccc6cda3 657 {
cce855bc
JA
658 case 'n': /* -nt */
659 case 'o': /* -ot */
660 case 'l': /* -lt */
661 case 'g': /* -gt */
662 return (1);
663 default:
664 return (0);
ccc6cda3 665 }
cce855bc
JA
666 else if (op[1] == 'e')
667 switch (op[2])
ccc6cda3 668 {
cce855bc
JA
669 case 'q': /* -eq */
670 case 'f': /* -ef */
671 return (1);
672 default:
673 return (0);
ccc6cda3 674 }
cce855bc
JA
675 else if (op[2] == 'e')
676 switch (op[1])
ccc6cda3 677 {
cce855bc
JA
678 case 'n': /* -ne */
679 case 'g': /* -ge */
680 case 'l': /* -le */
681 return (1);
682 default:
683 return (0);
ccc6cda3
JA
684 }
685 else
28ef6c31 686 return (0);
ccc6cda3 687 }
726f6388
JA
688}
689
690/* Return non-zero if OP is one of the test command's unary operators. */
cce855bc
JA
691int
692test_unop (op)
693 char *op;
726f6388 694{
7a131ef2 695 if (op[0] != '-' || op[2] != 0)
cce855bc
JA
696 return (0);
697
698 switch (op[1])
ccc6cda3
JA
699 {
700 case 'a': case 'b': case 'c': case 'd': case 'e':
701 case 'f': case 'g': case 'h': case 'k': case 'n':
cce855bc 702 case 'o': case 'p': case 'r': case 's': case 't':
6faad625 703 case 'u': case 'v': case 'w': case 'x': case 'z':
cce855bc 704 case 'G': case 'L': case 'O': case 'S': case 'N':
ccc6cda3
JA
705 return (1);
706 }
cce855bc 707
ccc6cda3 708 return (0);
726f6388
JA
709}
710
711static int
712two_arguments ()
713{
ccc6cda3
JA
714 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
715 return (argv[pos + 1][0] == '\0');
716 else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
726f6388 717 {
cce855bc 718 if (test_unop (argv[pos]))
ccc6cda3 719 return (unary_operator ());
726f6388 720 else
5e13499c 721 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388
JA
722 }
723 else
5e13499c 724 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
726f6388 725
ccc6cda3 726 return (0);
726f6388
JA
727}
728
ccc6cda3
JA
729#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
730
f73dda09
JA
731/* This could be augmented to handle `-t' as equivalent to `-t 1', but
732 POSIX requires that `-t' be given an argument. */
ccc6cda3
JA
733#define ONE_ARG_TEST(s) ((s)[0] != '\0')
734
726f6388
JA
735static int
736three_arguments ()
737{
738 int value;
739
cce855bc 740 if (test_binop (argv[pos+1]))
ccc6cda3
JA
741 {
742 value = binary_operator ();
743 pos = argc;
744 }
745 else if (ANDOR (argv[pos+1]))
746 {
747 if (argv[pos+1][1] == 'a')
28ef6c31 748 value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
ccc6cda3 749 else
28ef6c31 750 value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
ccc6cda3
JA
751 pos = argc;
752 }
cce855bc 753 else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
754 {
755 advance (1);
756 value = !two_arguments ();
757 }
ccc6cda3 758 else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
726f6388 759 {
ccc6cda3 760 value = ONE_ARG_TEST(argv[pos+1]);
726f6388
JA
761 pos = argc;
762 }
726f6388 763 else
5e13499c 764 test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
ccc6cda3 765
726f6388
JA
766 return (value);
767}
768
769/* This is an implementation of a Posix.2 proposal by David Korn. */
770static int
771posixtest ()
772{
773 int value;
774
775 switch (argc - 1) /* one extra passed in */
776 {
777 case 0:
778 value = FALSE;
779 pos = argc;
780 break;
781
782 case 1:
ccc6cda3 783 value = ONE_ARG_TEST(argv[1]);
726f6388
JA
784 pos = argc;
785 break;
786
787 case 2:
788 value = two_arguments ();
789 pos = argc;
790 break;
791
792 case 3:
793 value = three_arguments ();
794 break;
795
796 case 4:
ccc6cda3 797 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
726f6388
JA
798 {
799 advance (1);
800 value = !three_arguments ();
801 break;
802 }
803 /* FALLTHROUGH */
726f6388
JA
804 default:
805 value = expr ();
806 }
807
808 return (value);
809}
810
811/*
812 * [:
813 * '[' expr ']'
814 * test:
815 * test expr
816 */
817int
d166f048 818test_command (margc, margv)
726f6388
JA
819 int margc;
820 char **margv;
821{
822 int value;
726f6388
JA
823 int code;
824
f73dda09
JA
825 USE_VAR(margc);
826
36eb585c 827 code = setjmp_nosigs (test_exit_buf);
726f6388
JA
828
829 if (code)
830 return (test_error_return);
726f6388
JA
831
832 argv = margv;
833
ccc6cda3 834 if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
726f6388
JA
835 {
836 --margc;
837
726f6388 838 if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
5e13499c 839 test_syntax_error (_("missing `]'"), (char *)NULL);
28ef6c31
JA
840
841 if (margc < 2)
842 test_exit (SHELL_BOOLEAN (FALSE));
726f6388
JA
843 }
844
845 argc = margc;
846 pos = 1;
847
848 if (pos >= argc)
849 test_exit (SHELL_BOOLEAN (FALSE));
850
851 noeval = 0;
852 value = posixtest ();
853
854 if (pos != argc)
5e13499c 855 test_syntax_error (_("too many arguments"), (char *)NULL);
726f6388
JA
856
857 test_exit (SHELL_BOOLEAN (value));
858}