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