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