source: vendor/bash/3.1-p17/test.c

Last change on this file was 3231, checked in by bird, 18 years ago

eol style.

  • Property svn:eol-style set to native
File size: 21.2 KB
Line 
1/* GNU test program (ksb and mjb) */
2
3/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
4
5/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
6
7 This file is part of GNU Bash, the Bourne Again SHell.
8
9 Bash is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
13
14 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with Bash; see the file COPYING. If not, write to the Free Software
21 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
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
31#include <stdio.h>
32
33#include "bashtypes.h"
34
35#if !defined (HAVE_LIMITS_H)
36# include <sys/param.h>
37#endif
38
39#if defined (HAVE_UNISTD_H)
40# include <unistd.h>
41#endif
42
43#include <errno.h>
44#if !defined (errno)
45extern int errno;
46#endif /* !errno */
47
48#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
49# include <sys/file.h>
50#endif /* !_POSIX_VERSION */
51#include "posixstat.h"
52#include "filecntl.h"
53
54#include "bashintl.h"
55
56#include "shell.h"
57#include "pathexp.h"
58#include "test.h"
59#include "builtins/common.h"
60
61#include <glob/strmatch.h>
62
63#if !defined (STRLEN)
64# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
65#endif
66
67#if !defined (STREQ)
68# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
69#endif /* !STREQ */
70
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
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
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.
92 Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
93#define TRUE 1
94#define FALSE 0
95#define SHELL_BOOLEAN(value) (!(value))
96
97#define TEST_ERREXIT_STATUS 2
98
99static procenv_t test_exit_buf;
100static int test_error_return;
101#define test_exit(val) \
102 do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
103
104/* We have to use access(2) for machines running AFS, because it's
105 not a Unix file system. This may produce incorrect answers for
106 non-AFS files. I hate AFS. */
107#if defined (AFS)
108# define EACCESS(path, mode) access(path, mode)
109#else
110# define EACCESS(path, mode) test_eaccess(path, mode)
111#endif /* AFS */
112
113static int pos; /* The offset of the current argument in ARGV. */
114static int argc; /* The number of arguments present in ARGV. */
115static char **argv; /* The argument list. */
116static int noeval;
117
118static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
119static void beyond __P((void)) __attribute__((__noreturn__));
120static void integer_expected_error __P((char *)) __attribute__((__noreturn__));
121
122static int test_stat __P((char *, struct stat *));
123
124static int unary_operator __P((void));
125static int binary_operator __P((void));
126static int two_arguments __P((void));
127static int three_arguments __P((void));
128static int posixtest __P((void));
129
130static int expr __P((void));
131static int term __P((void));
132static int and __P((void));
133static int or __P((void));
134
135static int filecomp __P((char *, char *, int));
136static int arithcomp __P((char *, char *, int, int));
137static int patcomp __P((char *, char *, int));
138
139static void
140test_syntax_error (format, arg)
141 char *format, *arg;
142{
143 builtin_error (format, arg);
144 test_exit (TEST_ERREXIT_STATUS);
145}
146
147/*
148 * beyond - call when we're beyond the end of the argument list (an
149 * error condition)
150 */
151static void
152beyond ()
153{
154 test_syntax_error (_("argument expected"), (char *)NULL);
155}
156
157/* Syntax error for when an integer argument was expected, but
158 something else was found. */
159static void
160integer_expected_error (pch)
161 char *pch;
162{
163 test_syntax_error (_("%s: integer expression expected"), pch);
164}
165
166/* A wrapper for stat () which disallows pathnames that are empty strings
167 and handles /dev/fd emulation on systems that don't have it. */
168static int
169test_stat (path, finfo)
170 char *path;
171 struct stat *finfo;
172{
173 if (*path == '\0')
174 {
175 errno = ENOENT;
176 return (-1);
177 }
178 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
179 {
180#if !defined (HAVE_DEV_FD)
181 intmax_t fd;
182 int r;
183
184 if (legal_number (path + 8, &fd) && fd == (int)fd)
185 {
186 r = fstat ((int)fd, finfo);
187 if (r == 0 || errno != EBADF)
188 return (r);
189 }
190 errno = ENOENT;
191 return (-1);
192#else
193 /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
194 trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
195 On most systems, with the notable exception of linux, this is
196 effectively a no-op. */
197 char pbuf[32];
198 strcpy (pbuf, DEV_FD_PREFIX);
199 strcat (pbuf, path + 8);
200 return (stat (pbuf, finfo));
201#endif /* !HAVE_DEV_FD */
202 }
203#if !defined (HAVE_DEV_STDIN)
204 else if (STREQN (path, "/dev/std", 8))
205 {
206 if (STREQ (path+8, "in"))
207 return (fstat (0, finfo));
208 else if (STREQ (path+8, "out"))
209 return (fstat (1, finfo));
210 else if (STREQ (path+8, "err"))
211 return (fstat (2, finfo));
212 else
213 return (stat (path, finfo));
214 }
215#endif /* !HAVE_DEV_STDIN */
216 return (stat (path, finfo));
217}
218
219/* Do the same thing access(2) does, but use the effective uid and gid,
220 and don't make the mistake of telling root that any file is
221 executable. */
222int
223test_eaccess (path, mode)
224 char *path;
225 int mode;
226{
227 struct stat st;
228
229 if (test_stat (path, &st) < 0)
230 return (-1);
231
232 if (current_user.euid == 0)
233 {
234 /* Root can read or write any file. */
235 if (mode != X_OK)
236 return (0);
237
238 /* Root can execute any file that has any one of the execute
239 bits set. */
240 if (st.st_mode & S_IXUGO)
241 return (0);
242 }
243
244 if (st.st_uid == current_user.euid) /* owner */
245 mode <<= 6;
246 else if (group_member (st.st_gid))
247 mode <<= 3;
248
249 if (st.st_mode & mode)
250 return (0);
251
252 errno = EACCES;
253 return (-1);
254}
255
256/* Increment our position in the argument list. Check that we're not
257 past the end of the argument list. This check is supressed if the
258 argument is FALSE. Made a macro for efficiency. */
259#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
260#define unary_advance() do { advance (1); ++pos; } while (0)
261
262/*
263 * expr:
264 * or
265 */
266static int
267expr ()
268{
269 if (pos >= argc)
270 beyond ();
271
272 return (FALSE ^ or ()); /* Same with this. */
273}
274
275/*
276 * or:
277 * and
278 * and '-o' or
279 */
280static int
281or ()
282{
283 int value, v2;
284
285 value = and ();
286 if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
287 {
288 advance (0);
289 v2 = or ();
290 return (value || v2);
291 }
292
293 return (value);
294}
295
296/*
297 * and:
298 * term
299 * term '-a' and
300 */
301static int
302and ()
303{
304 int value, v2;
305
306 value = term ();
307 if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
308 {
309 advance (0);
310 v2 = and ();
311 return (value && v2);
312 }
313 return (value);
314}
315
316/*
317 * term - parse a term and return 1 or 0 depending on whether the term
318 * evaluates to true or false, respectively.
319 *
320 * term ::=
321 * '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
322 * '-'('G'|'L'|'O'|'S'|'N') filename
323 * '-t' [int]
324 * '-'('z'|'n') string
325 * '-o' option
326 * string
327 * string ('!='|'='|'==') string
328 * <int> '-'(eq|ne|le|lt|ge|gt) <int>
329 * file '-'(nt|ot|ef) file
330 * '(' <expr> ')'
331 * int ::=
332 * positive and negative integers
333 */
334static int
335term ()
336{
337 int value;
338
339 if (pos >= argc)
340 beyond ();
341
342 /* Deal with leading `not's. */
343 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
344 {
345 value = 0;
346 while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
347 {
348 advance (1);
349 value = 1 - value;
350 }
351
352 return (value ? !term() : term());
353 }
354
355 /* A paren-bracketed argument. */
356 if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
357 {
358 advance (1);
359 value = expr ();
360 if (argv[pos] == 0) /* ( */
361 test_syntax_error (_("`)' expected"), (char *)NULL);
362 else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
363 test_syntax_error (_("`)' expected, found %s"), argv[pos]);
364 advance (0);
365 return (value);
366 }
367
368 /* are there enough arguments left that this could be dyadic? */
369 if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
370 value = binary_operator ();
371
372 /* Might be a switch type argument */
373 else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
374 {
375 if (test_unop (argv[pos]))
376 value = unary_operator ();
377 else
378 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
379 }
380 else
381 {
382 value = argv[pos][0] != '\0';
383 advance (0);
384 }
385
386 return (value);
387}
388
389static int
390filecomp (s, t, op)
391 char *s, *t;
392 int op;
393{
394 struct stat st1, st2;
395 int r1, r2;
396
397 if ((r1 = test_stat (s, &st1)) < 0)
398 {
399 if (op == EF)
400 return (FALSE);
401 }
402 if ((r2 = test_stat (t, &st2)) < 0)
403 {
404 if (op == EF)
405 return (FALSE);
406 }
407
408 switch (op)
409 {
410 case OT: return (r1 < r2 || (r2 == 0 && st1.st_mtime < st2.st_mtime));
411 case NT: return (r1 > r2 || (r1 == 0 && st1.st_mtime > st2.st_mtime));
412 case EF: return ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino));
413 }
414 return (FALSE);
415}
416
417static int
418arithcomp (s, t, op, flags)
419 char *s, *t;
420 int op, flags;
421{
422 intmax_t l, r;
423 int expok;
424
425 if (flags & TEST_ARITHEXP)
426 {
427 l = evalexp (s, &expok);
428 if (expok == 0)
429 return (FALSE); /* should probably longjmp here */
430 r = evalexp (t, &expok);
431 if (expok == 0)
432 return (FALSE); /* ditto */
433 }
434 else
435 {
436 if (legal_number (s, &l) == 0)
437 integer_expected_error (s);
438 if (legal_number (t, &r) == 0)
439 integer_expected_error (t);
440 }
441
442 switch (op)
443 {
444 case EQ: return (l == r);
445 case NE: return (l != r);
446 case LT: return (l < r);
447 case GT: return (l > r);
448 case LE: return (l <= r);
449 case GE: return (l >= r);
450 }
451
452 return (FALSE);
453}
454
455static int
456patcomp (string, pat, op)
457 char *string, *pat;
458 int op;
459{
460 int m;
461
462 m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
463 return ((op == EQ) ? (m == 0) : (m != 0));
464}
465
466int
467binary_test (op, arg1, arg2, flags)
468 char *op, *arg1, *arg2;
469 int flags;
470{
471 int patmatch;
472
473 patmatch = (flags & TEST_PATMATCH);
474
475 if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
476 return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
477
478 else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
479 return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
480
481 else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
482 return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
483
484 else if (op[2] == 't')
485 {
486 switch (op[1])
487 {
488 case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */
489 case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */
490 case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */
491 case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */
492 }
493 }
494 else if (op[1] == 'e')
495 {
496 switch (op[2])
497 {
498 case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */
499 case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */
500 }
501 }
502 else if (op[2] == 'e')
503 {
504 switch (op[1])
505 {
506 case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */
507 case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */
508 case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */
509 }
510 }
511
512 return (FALSE); /* should never get here */
513}
514
515
516static int
517binary_operator ()
518{
519 int value;
520 char *w;
521
522 w = argv[pos + 1];
523 if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
524 ((w[0] == '>' || w[0] == '<') && w[1] == '\0') || /* <, > */
525 (w[0] == '!' && w[1] == '=' && w[2] == '\0')) /* != */
526 {
527 value = binary_test (w, argv[pos], argv[pos + 2], 0);
528 pos += 3;
529 return (value);
530 }
531
532#if defined (PATTERN_MATCHING)
533 if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
534 {
535 value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
536 pos += 3;
537 return (value);
538 }
539#endif
540
541 if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
542 {
543 test_syntax_error (_("%s: binary operator expected"), w);
544 /* NOTREACHED */
545 return (FALSE);
546 }
547
548 value = binary_test (w, argv[pos], argv[pos + 2], 0);
549 pos += 3;
550 return value;
551}
552
553static int
554unary_operator ()
555{
556 char *op;
557 intmax_t r;
558
559 op = argv[pos];
560 if (test_unop (op) == 0)
561 return (FALSE);
562
563 /* the only tricky case is `-t', which may or may not take an argument. */
564 if (op[1] == 't')
565 {
566 advance (0);
567 if (pos < argc)
568 {
569 if (legal_number (argv[pos], &r))
570 {
571 advance (0);
572 return (unary_test (op, argv[pos - 1]));
573 }
574 else
575 return (FALSE);
576 }
577 else
578 return (unary_test (op, "1"));
579 }
580
581 /* All of the unary operators take an argument, so we first call
582 unary_advance (), which checks to make sure that there is an
583 argument, and then advances pos right past it. This means that
584 pos - 1 is the location of the argument. */
585 unary_advance ();
586 return (unary_test (op, argv[pos - 1]));
587}
588
589int
590unary_test (op, arg)
591 char *op, *arg;
592{
593 intmax_t r;
594 struct stat stat_buf;
595
596 switch (op[1])
597 {
598 case 'a': /* file exists in the file system? */
599 case 'e':
600 return (test_stat (arg, &stat_buf) == 0);
601
602 case 'r': /* file is readable? */
603 return (EACCESS (arg, R_OK) == 0);
604
605 case 'w': /* File is writeable? */
606 return (EACCESS (arg, W_OK) == 0);
607
608 case 'x': /* File is executable? */
609 return (EACCESS (arg, X_OK) == 0);
610
611 case 'O': /* File is owned by you? */
612 return (test_stat (arg, &stat_buf) == 0 &&
613 (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
614
615 case 'G': /* File is owned by your group? */
616 return (test_stat (arg, &stat_buf) == 0 &&
617 (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
618
619 case 'N':
620 return (test_stat (arg, &stat_buf) == 0 &&
621 stat_buf.st_atime <= stat_buf.st_mtime);
622
623 case 'f': /* File is a file? */
624 if (test_stat (arg, &stat_buf) < 0)
625 return (FALSE);
626
627 /* -f is true if the given file exists and is a regular file. */
628#if defined (S_IFMT)
629 return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
630#else
631 return (S_ISREG (stat_buf.st_mode));
632#endif /* !S_IFMT */
633
634 case 'd': /* File is a directory? */
635 return (test_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
636
637 case 's': /* File has something in it? */
638 return (test_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
639
640 case 'S': /* File is a socket? */
641#if !defined (S_ISSOCK)
642 return (FALSE);
643#else
644 return (test_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
645#endif /* S_ISSOCK */
646
647 case 'c': /* File is character special? */
648 return (test_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
649
650 case 'b': /* File is block special? */
651 return (test_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
652
653 case 'p': /* File is a named pipe? */
654#ifndef S_ISFIFO
655 return (FALSE);
656#else
657 return (test_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
658#endif /* S_ISFIFO */
659
660 case 'L': /* Same as -h */
661 case 'h': /* File is a symbolic link? */
662#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
663 return (FALSE);
664#else
665 return ((arg[0] != '\0') &&
666 (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
667#endif /* S_IFLNK && HAVE_LSTAT */
668
669 case 'u': /* File is setuid? */
670 return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
671
672 case 'g': /* File is setgid? */
673 return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
674
675 case 'k': /* File has sticky bit set? */
676#if !defined (S_ISVTX)
677 /* This is not Posix, and is not defined on some Posix systems. */
678 return (FALSE);
679#else
680 return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
681#endif
682
683 case 't': /* File fd is a terminal? */
684 if (legal_number (arg, &r) == 0)
685 return (FALSE);
686 return ((r == (int)r) && isatty ((int)r));
687
688 case 'n': /* True if arg has some length. */
689 return (arg[0] != '\0');
690
691 case 'z': /* True if arg has no length. */
692 return (arg[0] == '\0');
693
694 case 'o': /* True if option `arg' is set. */
695 return (minus_o_option_value (arg) == 1);
696 }
697
698 /* We can't actually get here, but this shuts up gcc. */
699 return (FALSE);
700}
701
702/* Return TRUE if OP is one of the test command's binary operators. */
703int
704test_binop (op)
705 char *op;
706{
707 if (op[0] == '=' && op[1] == '\0')
708 return (1); /* '=' */
709 else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0') /* string <, > */
710 return (1);
711 else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
712 return (1); /* `==' and `!=' */
713#if defined (PATTERN_MATCHING)
714 else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
715 return (1);
716#endif
717 else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
718 return (0);
719 else
720 {
721 if (op[2] == 't')
722 switch (op[1])
723 {
724 case 'n': /* -nt */
725 case 'o': /* -ot */
726 case 'l': /* -lt */
727 case 'g': /* -gt */
728 return (1);
729 default:
730 return (0);
731 }
732 else if (op[1] == 'e')
733 switch (op[2])
734 {
735 case 'q': /* -eq */
736 case 'f': /* -ef */
737 return (1);
738 default:
739 return (0);
740 }
741 else if (op[2] == 'e')
742 switch (op[1])
743 {
744 case 'n': /* -ne */
745 case 'g': /* -ge */
746 case 'l': /* -le */
747 return (1);
748 default:
749 return (0);
750 }
751 else
752 return (0);
753 }
754}
755
756/* Return non-zero if OP is one of the test command's unary operators. */
757int
758test_unop (op)
759 char *op;
760{
761 if (op[0] != '-')
762 return (0);
763
764 switch (op[1])
765 {
766 case 'a': case 'b': case 'c': case 'd': case 'e':
767 case 'f': case 'g': case 'h': case 'k': case 'n':
768 case 'o': case 'p': case 'r': case 's': case 't':
769 case 'u': case 'w': case 'x': case 'z':
770 case 'G': case 'L': case 'O': case 'S': case 'N':
771 return (1);
772 }
773
774 return (0);
775}
776
777static int
778two_arguments ()
779{
780 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
781 return (argv[pos + 1][0] == '\0');
782 else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
783 {
784 if (test_unop (argv[pos]))
785 return (unary_operator ());
786 else
787 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
788 }
789 else
790 test_syntax_error (_("%s: unary operator expected"), argv[pos]);
791
792 return (0);
793}
794
795#define ANDOR(s) (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
796
797/* This could be augmented to handle `-t' as equivalent to `-t 1', but
798 POSIX requires that `-t' be given an argument. */
799#define ONE_ARG_TEST(s) ((s)[0] != '\0')
800
801static int
802three_arguments ()
803{
804 int value;
805
806 if (test_binop (argv[pos+1]))
807 {
808 value = binary_operator ();
809 pos = argc;
810 }
811 else if (ANDOR (argv[pos+1]))
812 {
813 if (argv[pos+1][1] == 'a')
814 value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
815 else
816 value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
817 pos = argc;
818 }
819 else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
820 {
821 advance (1);
822 value = !two_arguments ();
823 }
824 else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
825 {
826 value = ONE_ARG_TEST(argv[pos+1]);
827 pos = argc;
828 }
829 else
830 test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
831
832 return (value);
833}
834
835/* This is an implementation of a Posix.2 proposal by David Korn. */
836static int
837posixtest ()
838{
839 int value;
840
841 switch (argc - 1) /* one extra passed in */
842 {
843 case 0:
844 value = FALSE;
845 pos = argc;
846 break;
847
848 case 1:
849 value = ONE_ARG_TEST(argv[1]);
850 pos = argc;
851 break;
852
853 case 2:
854 value = two_arguments ();
855 pos = argc;
856 break;
857
858 case 3:
859 value = three_arguments ();
860 break;
861
862 case 4:
863 if (argv[pos][0] == '!' && argv[pos][1] == '\0')
864 {
865 advance (1);
866 value = !three_arguments ();
867 break;
868 }
869 /* FALLTHROUGH */
870 default:
871 value = expr ();
872 }
873
874 return (value);
875}
876
877/*
878 * [:
879 * '[' expr ']'
880 * test:
881 * test expr
882 */
883int
884test_command (margc, margv)
885 int margc;
886 char **margv;
887{
888 int value;
889 int code;
890
891 USE_VAR(margc);
892
893 code = setjmp (test_exit_buf);
894
895 if (code)
896 return (test_error_return);
897
898 argv = margv;
899
900 if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
901 {
902 --margc;
903
904 if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
905 test_syntax_error (_("missing `]'"), (char *)NULL);
906
907 if (margc < 2)
908 test_exit (SHELL_BOOLEAN (FALSE));
909 }
910
911 argc = margc;
912 pos = 1;
913
914 if (pos >= argc)
915 test_exit (SHELL_BOOLEAN (FALSE));
916
917 noeval = 0;
918 value = posixtest ();
919
920 if (pos != argc)
921 test_syntax_error (_("too many arguments"), (char *)NULL);
922
923 test_exit (SHELL_BOOLEAN (value));
924}
Note: See TracBrowser for help on using the repository browser.