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