LCOV - code coverage report
Current view: top level - src - test.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 179 338 53.0 %
Date: 2018-01-30 Functions: 12 20 60.0 %

          Line data    Source code
       1             : /* GNU test program (ksb and mjb) */
       2             : 
       3             : /* Modified to run with the GNU shell by bfox. */
       4             : 
       5             : /* Copyright (C) 1987-2005, 2007-2008 Free Software Foundation, Inc.
       6             : 
       7             :    This program is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      19             : 
      20             : /* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get
      21             :    the shell builtin version. */
      22             : 
      23             : #include <config.h>
      24             : #include <stdio.h>
      25             : #include <sys/types.h>
      26             : 
      27             : #define TEST_STANDALONE 1
      28             : 
      29             : #ifndef LBRACKET
      30             : # define LBRACKET 0
      31             : #endif
      32             : 
      33             : /* The official name of this program (e.g., no `g' prefix).  */
      34             : #if LBRACKET
      35             : # define PROGRAM_NAME "["
      36             : #else
      37             : # define PROGRAM_NAME "test"
      38             : #endif
      39             : 
      40             : #include "system.h"
      41             : #include "euidaccess.h"
      42             : #include "inttostr.h"
      43             : #include "quote.h"
      44             : #include "stat-time.h"
      45             : #include "strnumcmp.h"
      46             : 
      47             : #if HAVE_SYS_PARAM_H
      48             : # include <sys/param.h>
      49             : #endif
      50             : 
      51             : char *program_name;
      52             : 
      53             : /* Exit status for syntax errors, etc.  */
      54             : enum { TEST_TRUE, TEST_FALSE, TEST_FAILURE };
      55             : 
      56             : #if defined TEST_STANDALONE
      57             : # define test_exit(val) exit (val)
      58             : #else
      59             :    static jmp_buf test_exit_buf;
      60             :    static int test_error_return = 0;
      61             : # define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
      62             : #endif /* !TEST_STANDALONE */
      63             : 
      64             : static int pos;         /* The offset of the current argument in ARGV. */
      65             : static int argc;        /* The number of arguments present in ARGV. */
      66             : static char **argv;     /* The argument list. */
      67             : 
      68             : static bool test_unop (char const *s);
      69             : static bool unary_operator (void);
      70             : static bool binary_operator (bool);
      71             : static bool two_arguments (void);
      72             : static bool three_arguments (void);
      73             : static bool posixtest (int);
      74             : 
      75             : static bool expr (void);
      76             : static bool term (void);
      77             : static bool and (void);
      78             : static bool or (void);
      79             : 
      80             : static void test_syntax_error (char const *format, char const *arg)
      81             :      ATTRIBUTE_NORETURN;
      82             : static void beyond (void) ATTRIBUTE_NORETURN;
      83             : 
      84             : static void
      85          35 : test_syntax_error (char const *format, char const *arg)
      86             : {
      87          35 :   fprintf (stderr, "%s: ", argv[0]);
      88          35 :   fprintf (stderr, format, arg);
      89          35 :   fputc ('\n', stderr);
      90          35 :   fflush (stderr);
      91          35 :   test_exit (TEST_FAILURE);
      92             : }
      93             : 
      94             : /* Increment our position in the argument list.  Check that we're not
      95             :    past the end of the argument list.  This check is supressed if the
      96             :    argument is false.  */
      97             : 
      98             : static inline void
      99          56 : advance (bool f)
     100             : {
     101          56 :   ++pos;
     102             : 
     103          56 :   if (f && pos >= argc)
     104           0 :     beyond ();
     105          56 : }
     106             : 
     107             : static inline void
     108          55 : unary_advance (void)
     109             : {
     110          55 :   advance (true);
     111          55 :   ++pos;
     112          55 : }
     113             : 
     114             : /*
     115             :  * beyond - call when we're beyond the end of the argument list (an
     116             :  *      error condition)
     117             :  */
     118             : static void
     119          18 : beyond (void)
     120             : {
     121          18 :   test_syntax_error (_("missing argument after %s"), quote (argv[argc - 1]));
     122             : }
     123             : 
     124             : /* If the characters pointed to by STRING constitute a valid number,
     125             :    return a pointer to the start of the number, skipping any blanks or
     126             :    leading '+'.  Otherwise, report an error and exit.  */
     127             : static char const *
     128          10 : find_int (char const *string)
     129             : {
     130             :   char const *p;
     131             :   char const *number_start;
     132             : 
     133          11 :   for (p = string; isblank (to_uchar (*p)); p++)
     134           1 :     continue;
     135             : 
     136          10 :   if (*p == '+')
     137             :     {
     138           3 :       p++;
     139           3 :       number_start = p;
     140             :     }
     141             :   else
     142             :     {
     143           7 :       number_start = p;
     144           7 :       p += (*p == '-');
     145             :     }
     146             : 
     147          10 :   if (ISDIGIT (*p++))
     148             :     {
     149          18 :       while (ISDIGIT (*p))
     150           2 :         p++;
     151          17 :       while (isblank (to_uchar (*p)))
     152           1 :         p++;
     153           8 :       if (!*p)
     154          14 :         return number_start;
     155             :     }
     156             : 
     157           3 :   test_syntax_error (_("invalid integer %s"), quote (string));
     158             : }
     159             : 
     160             : /* Find the modification time of FILE, and stuff it into *MTIME.
     161             :    Return true if successful.  */
     162             : static bool
     163           0 : get_mtime (char const *filename, struct timespec *mtime)
     164             : {
     165             :   struct stat finfo;
     166           0 :   bool ok = (stat (filename, &finfo) == 0);
     167             : #ifdef lint
     168             :   static struct timespec const zero;
     169             :   *mtime = zero;
     170             : #endif
     171           0 :   if (ok)
     172           0 :     *mtime = get_stat_mtime (&finfo);
     173           0 :   return ok;
     174             : }
     175             : 
     176             : /* Return true if S is one of the test command's binary operators.  */
     177             : static bool
     178           0 : binop (char const *s)
     179             : {
     180           0 :   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
     181           0 :           (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
     182           0 :           (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
     183           0 :           (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
     184             : }
     185             : 
     186             : /*
     187             :  * term - parse a term and return 1 or 0 depending on whether the term
     188             :  *      evaluates to true or false, respectively.
     189             :  *
     190             :  * term ::=
     191             :  *      '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
     192             :  *      '-'('L'|'x') filename
     193             :  *      '-t' int
     194             :  *      '-'('z'|'n') string
     195             :  *      string
     196             :  *      string ('!='|'=') string
     197             :  *      <int> '-'(eq|ne|le|lt|ge|gt) <int>
     198             :  *      file '-'(nt|ot|ef) file
     199             :  *      '(' <expr> ')'
     200             :  * int ::=
     201             :  *      '-l' string
     202             :  *      positive and negative integers
     203             :  */
     204             : static bool
     205           0 : term (void)
     206             : {
     207             :   bool value;
     208           0 :   bool negated = false;
     209             : 
     210             :   /* Deal with leading `not's.  */
     211           0 :   while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
     212             :     {
     213           0 :       advance (true);
     214           0 :       negated = !negated;
     215             :     }
     216             : 
     217           0 :   if (pos >= argc)
     218           0 :     beyond ();
     219             : 
     220             :   /* A paren-bracketed argument. */
     221           0 :   if (argv[pos][0] == '(' && argv[pos][1] == '\0')
     222           0 :     {
     223             :       int nargs;
     224             : 
     225           0 :       advance (true);
     226             : 
     227           0 :       for (nargs = 1;
     228           0 :            pos + nargs < argc && ! STREQ (argv[pos + nargs], ")");
     229           0 :            nargs++)
     230           0 :         if (nargs == 4)
     231             :           {
     232           0 :             nargs = argc - pos;
     233           0 :             break;
     234             :           }
     235             : 
     236           0 :       value = posixtest (nargs);
     237           0 :       if (argv[pos] == 0)
     238           0 :         test_syntax_error (_("')' expected"), NULL);
     239             :       else
     240           0 :         if (argv[pos][0] != ')' || argv[pos][1])
     241           0 :           test_syntax_error (_("')' expected, found %s"), argv[pos]);
     242           0 :       advance (false);
     243             :     }
     244             : 
     245             :   /* Are there enough arguments left that this could be dyadic?  */
     246           0 :   else if (4 <= argc - pos && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))
     247           0 :     value = binary_operator (true);
     248           0 :   else if (3 <= argc - pos && binop (argv[pos + 1]))
     249           0 :     value = binary_operator (false);
     250             : 
     251             :   /* It might be a switch type argument.  */
     252           0 :   else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
     253             :     {
     254           0 :       if (test_unop (argv[pos]))
     255           0 :         value = unary_operator ();
     256             :       else
     257           0 :         test_syntax_error (_("%s: unary operator expected"), argv[pos]);
     258             :     }
     259             :   else
     260             :     {
     261           0 :       value = (argv[pos][0] != '\0');
     262           0 :       advance (false);
     263             :     }
     264             : 
     265           0 :   return negated ^ value;
     266             : }
     267             : 
     268             : static bool
     269           0 : binary_operator (bool l_is_l)
     270             : {
     271             :   int op;
     272             :   struct stat stat_buf, stat_spare;
     273             :   /* Is the right integer expression of the form '-l string'? */
     274             :   bool r_is_l;
     275             : 
     276           0 :   if (l_is_l)
     277           0 :     advance (false);
     278           0 :   op = pos + 1;
     279             : 
     280           0 :   if ((op < argc - 2) && STREQ (argv[op + 1], "-l"))
     281             :     {
     282           0 :       r_is_l = true;
     283           0 :       advance (false);
     284             :     }
     285             :   else
     286           0 :     r_is_l = false;
     287             : 
     288           0 :   if (argv[op][0] == '-')
     289             :     {
     290             :       /* check for eq, nt, and stuff */
     291           0 :       if ((((argv[op][1] == 'l' || argv[op][1] == 'g')
     292           0 :             && (argv[op][2] == 'e' || argv[op][2] == 't'))
     293           0 :            || (argv[op][1] == 'e' && argv[op][2] == 'q')
     294           0 :            || (argv[op][1] == 'n' && argv[op][2] == 'e'))
     295           0 :           && !argv[op][3])
     296             :         {
     297             :           char lbuf[INT_BUFSIZE_BOUND (uintmax_t)];
     298             :           char rbuf[INT_BUFSIZE_BOUND (uintmax_t)];
     299           0 :           char const *l = (l_is_l
     300           0 :                            ? umaxtostr (strlen (argv[op - 1]), lbuf)
     301           0 :                            : find_int (argv[op - 1]));
     302           0 :           char const *r = (r_is_l
     303           0 :                            ? umaxtostr (strlen (argv[op + 2]), rbuf)
     304           0 :                            : find_int (argv[op + 1]));
     305           0 :           int cmp = strintcmp (l, r);
     306           0 :           bool xe_operator = (argv[op][2] == 'e');
     307           0 :           pos += 3;
     308           0 :           return (argv[op][1] == 'l' ? cmp < xe_operator
     309           0 :                   : argv[op][1] == 'g' ? cmp > - xe_operator
     310           0 :                   : (cmp != 0) == xe_operator);
     311             :         }
     312             : 
     313           0 :       switch (argv[op][1])
     314             :         {
     315           0 :         default:
     316           0 :           break;
     317             : 
     318           0 :         case 'n':
     319           0 :           if (argv[op][2] == 't' && !argv[op][3])
     320             :             {
     321             :               /* nt - newer than */
     322             :               struct timespec lt, rt;
     323             :               bool le, re;
     324           0 :               pos += 3;
     325           0 :               if (l_is_l | r_is_l)
     326           0 :                 test_syntax_error (_("-nt does not accept -l"), NULL);
     327           0 :               le = get_mtime (argv[op - 1], &lt);
     328           0 :               re = get_mtime (argv[op + 1], &rt);
     329           0 :               return le && (!re || timespec_cmp (lt, rt) > 0);
     330             :             }
     331           0 :           break;
     332             : 
     333           0 :         case 'e':
     334           0 :           if (argv[op][2] == 'f' && !argv[op][3])
     335             :             {
     336             :               /* ef - hard link? */
     337           0 :               pos += 3;
     338           0 :               if (l_is_l | r_is_l)
     339           0 :                 test_syntax_error (_("-ef does not accept -l"), NULL);
     340           0 :               return (stat (argv[op - 1], &stat_buf) == 0
     341           0 :                       && stat (argv[op + 1], &stat_spare) == 0
     342           0 :                       && stat_buf.st_dev == stat_spare.st_dev
     343           0 :                       && stat_buf.st_ino == stat_spare.st_ino);
     344             :             }
     345           0 :           break;
     346             : 
     347           0 :         case 'o':
     348           0 :           if ('t' == argv[op][2] && '\000' == argv[op][3])
     349             :             {
     350             :               /* ot - older than */
     351             :               struct timespec lt, rt;
     352             :               bool le, re;
     353           0 :               pos += 3;
     354           0 :               if (l_is_l | r_is_l)
     355           0 :                 test_syntax_error (_("-ot does not accept -l"), NULL);
     356           0 :               le = get_mtime (argv[op - 1], &lt);
     357           0 :               re = get_mtime (argv[op + 1], &rt);
     358           0 :               return re && (!le || timespec_cmp (lt, rt) < 0);
     359             :             }
     360           0 :           break;
     361             :         }
     362             : 
     363             :       /* FIXME: is this dead code? */
     364           0 :       test_syntax_error (_("unknown binary operator"), argv[op]);
     365             :     }
     366             : 
     367           0 :   if (argv[op][0] == '=' && !argv[op][1])
     368             :     {
     369           0 :       bool value = STREQ (argv[pos], argv[pos + 2]);
     370           0 :       pos += 3;
     371           0 :       return value;
     372             :     }
     373             : 
     374           0 :   if (STREQ (argv[op], "!="))
     375             :     {
     376           0 :       bool value = !STREQ (argv[pos], argv[pos + 2]);
     377           0 :       pos += 3;
     378           0 :       return value;
     379             :     }
     380             : 
     381             :   /* Not reached.  */
     382           0 :   abort ();
     383             : }
     384             : 
     385             : static bool
     386          56 : unary_operator (void)
     387             : {
     388             :   struct stat stat_buf;
     389             : 
     390          56 :   switch (argv[pos][1])
     391             :     {
     392           1 :     default:
     393           1 :       return false;
     394             : 
     395             :       /* All of the following unary operators use unary_advance (), which
     396             :          checks to make sure that there is an argument, and then advances
     397             :          pos right past it.  This means that pos - 1 is the location of the
     398             :          argument. */
     399             : 
     400           1 :     case 'a':                   /* file exists in the file system? */
     401             :     case 'e':
     402           1 :       unary_advance ();
     403           1 :       return stat (argv[pos - 1], &stat_buf) == 0;
     404             : 
     405           6 :     case 'r':                   /* file is readable? */
     406           6 :       unary_advance ();
     407           6 :       return euidaccess (argv[pos - 1], R_OK) == 0;
     408             : 
     409           1 :     case 'w':                   /* File is writable? */
     410           1 :       unary_advance ();
     411           1 :       return euidaccess (argv[pos - 1], W_OK) == 0;
     412             : 
     413           1 :     case 'x':                   /* File is executable? */
     414           1 :       unary_advance ();
     415           1 :       return euidaccess (argv[pos - 1], X_OK) == 0;
     416             : 
     417           2 :     case 'O':                   /* File is owned by you? */
     418           2 :       unary_advance ();
     419           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     420           2 :               && (geteuid () == stat_buf.st_uid));
     421             : 
     422           2 :     case 'G':                   /* File is owned by your group? */
     423           2 :       unary_advance ();
     424           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     425           2 :               && (getegid () == stat_buf.st_gid));
     426             : 
     427           2 :     case 'f':                   /* File is a file? */
     428           2 :       unary_advance ();
     429             :       /* Under POSIX, -f is true if the given file exists
     430             :          and is a regular file. */
     431           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     432           2 :               && S_ISREG (stat_buf.st_mode));
     433             : 
     434           2 :     case 'd':                   /* File is a directory? */
     435           2 :       unary_advance ();
     436           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     437           2 :               && S_ISDIR (stat_buf.st_mode));
     438             : 
     439           2 :     case 's':                   /* File has something in it? */
     440           2 :       unary_advance ();
     441           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     442           2 :               && 0 < stat_buf.st_size);
     443             : 
     444           2 :     case 'S':                   /* File is a socket? */
     445           2 :       unary_advance ();
     446           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     447           2 :               && S_ISSOCK (stat_buf.st_mode));
     448             : 
     449           2 :     case 'c':                   /* File is character special? */
     450           2 :       unary_advance ();
     451           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     452           2 :               && S_ISCHR (stat_buf.st_mode));
     453             : 
     454           4 :     case 'b':                   /* File is block special? */
     455           4 :       unary_advance ();
     456           4 :       return (stat (argv[pos - 1], &stat_buf) == 0
     457           4 :               && S_ISBLK (stat_buf.st_mode));
     458             : 
     459           2 :     case 'p':                   /* File is a named pipe? */
     460           2 :       unary_advance ();
     461           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     462           2 :               && S_ISFIFO (stat_buf.st_mode));
     463             : 
     464           6 :     case 'L':                   /* Same as -h  */
     465             :       /*FALLTHROUGH*/
     466             : 
     467             :     case 'h':                   /* File is a symbolic link? */
     468           6 :       unary_advance ();
     469           6 :       return (lstat (argv[pos - 1], &stat_buf) == 0
     470           6 :               && S_ISLNK (stat_buf.st_mode));
     471             : 
     472           2 :     case 'u':                   /* File is setuid? */
     473           2 :       unary_advance ();
     474           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     475           2 :               && (stat_buf.st_mode & S_ISUID));
     476             : 
     477           4 :     case 'g':                   /* File is setgid? */
     478           4 :       unary_advance ();
     479           4 :       return (stat (argv[pos - 1], &stat_buf) == 0
     480           4 :               && (stat_buf.st_mode & S_ISGID));
     481             : 
     482           2 :     case 'k':                   /* File has sticky bit set? */
     483           2 :       unary_advance ();
     484           2 :       return (stat (argv[pos - 1], &stat_buf) == 0
     485           2 :               && (stat_buf.st_mode & S_ISVTX));
     486             : 
     487          10 :     case 't':                   /* File (fd) is a terminal? */
     488             :       {
     489             :         long int fd;
     490             :         char const *arg;
     491          10 :         unary_advance ();
     492          10 :         arg = find_int (argv[pos - 1]);
     493           7 :         errno = 0;
     494           7 :         fd = strtol (arg, NULL, 10);
     495           7 :         return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd));
     496             :       }
     497             : 
     498           1 :     case 'n':                   /* True if arg has some length. */
     499           1 :       unary_advance ();
     500           1 :       return argv[pos - 1][0] != 0;
     501             : 
     502           1 :     case 'z':                   /* True if arg has no length. */
     503           1 :       unary_advance ();
     504           1 :       return argv[pos - 1][0] == '\0';
     505             :     }
     506             : }
     507             : 
     508             : /*
     509             :  * and:
     510             :  *      term
     511             :  *      term '-a' and
     512             :  */
     513             : static bool
     514           0 : and (void)
     515             : {
     516           0 :   bool value = true;
     517             : 
     518             :   for (;;)
     519             :     {
     520           0 :       value &= term ();
     521           0 :       if (! (pos < argc && STREQ (argv[pos], "-a")))
     522           0 :         return value;
     523           0 :       advance (false);
     524             :     }
     525             : }
     526             : 
     527             : /*
     528             :  * or:
     529             :  *      and
     530             :  *      and '-o' or
     531             :  */
     532             : static bool
     533           0 : or (void)
     534             : {
     535           0 :   bool value = false;
     536             : 
     537             :   for (;;)
     538             :     {
     539           0 :       value |= and ();
     540           0 :       if (! (pos < argc && STREQ (argv[pos], "-o")))
     541           0 :         return value;
     542           0 :       advance (false);
     543             :     }
     544             : }
     545             : 
     546             : /*
     547             :  * expr:
     548             :  *      or
     549             :  */
     550             : static bool
     551           0 : expr (void)
     552             : {
     553           0 :   if (pos >= argc)
     554           0 :     beyond ();
     555             : 
     556           0 :   return or ();         /* Same with this. */
     557             : }
     558             : 
     559             : /* Return true if OP is one of the test command's unary operators. */
     560             : static bool
     561          58 : test_unop (char const *op)
     562             : {
     563          58 :   if (op[0] != '-')
     564           0 :     return false;
     565             : 
     566          58 :   switch (op[1])
     567             :     {
     568          56 :     case 'a': case 'b': case 'c': case 'd': case 'e':
     569             :     case 'f': case 'g': case 'h': case 'k': case 'n':
     570             :     case 'o': case 'p': case 'r': case 's': case 't':
     571             :     case 'u': case 'w': case 'x': case 'z':
     572             :     case 'G': case 'L': case 'O': case 'S': case 'N':
     573          56 :       return true;
     574             :     }
     575             : 
     576           2 :   return false;
     577             : }
     578             : 
     579             : static bool
     580           3 : one_argument (void)
     581             : {
     582           3 :   return argv[pos++][0] != '\0';
     583             : }
     584             : 
     585             : static bool
     586          77 : two_arguments (void)
     587             : {
     588             :   bool value;
     589             : 
     590          77 :   if (STREQ (argv[pos], "!"))
     591             :     {
     592           1 :       advance (false);
     593           1 :       value = ! one_argument ();
     594             :     }
     595          76 :   else if (argv[pos][0] == '-'
     596          67 :            && argv[pos][1] != '\0'
     597          59 :            && argv[pos][2] == '\0')
     598             :     {
     599         111 :       if (test_unop (argv[pos]))
     600          56 :         value = unary_operator ();
     601             :       else
     602           2 :         test_syntax_error (_("%s: unary operator expected"), argv[pos]);
     603             :     }
     604             :   else
     605          18 :     beyond ();
     606          54 :   return (value);
     607             : }
     608             : 
     609             : static bool
     610           0 : three_arguments (void)
     611             : {
     612             :   bool value;
     613             : 
     614           0 :   if (binop (argv[pos + 1]))
     615           0 :     value = binary_operator (false);
     616           0 :   else if (STREQ (argv[pos], "!"))
     617             :     {
     618           0 :       advance (true);
     619           0 :       value = !two_arguments ();
     620             :     }
     621           0 :   else if (STREQ (argv[pos], "(") && STREQ (argv[pos + 2], ")"))
     622             :     {
     623           0 :       advance (false);
     624           0 :       value = one_argument ();
     625           0 :       advance (false);
     626             :     }
     627           0 :   else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o"))
     628           0 :     value = expr ();
     629             :   else
     630           0 :     test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
     631           0 :   return (value);
     632             : }
     633             : 
     634             : /* This is an implementation of a Posix.2 proposal by David Korn. */
     635             : static bool
     636          79 : posixtest (int nargs)
     637             : {
     638             :   bool value;
     639             : 
     640          79 :   switch (nargs)
     641             :     {
     642           2 :       case 1:
     643           2 :         value = one_argument ();
     644           2 :         break;
     645             : 
     646          77 :       case 2:
     647          77 :         value = two_arguments ();
     648          54 :         break;
     649             : 
     650           0 :       case 3:
     651           0 :         value = three_arguments ();
     652           0 :         break;
     653             : 
     654           0 :       case 4:
     655           0 :         if (STREQ (argv[pos], "!"))
     656             :           {
     657           0 :             advance (true);
     658           0 :             value = !three_arguments ();
     659           0 :             break;
     660             :           }
     661           0 :         if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")"))
     662             :           {
     663           0 :             advance (false);
     664           0 :             value = two_arguments ();
     665           0 :             advance (false);
     666           0 :             break;
     667             :           }
     668             :         /* FALLTHROUGH */
     669             :       case 5:
     670             :       default:
     671           0 :         if (nargs <= 0)
     672           0 :           abort ();
     673           0 :         value = expr ();
     674             :     }
     675             : 
     676          56 :   return (value);
     677             : }
     678             : 
     679             : #if defined TEST_STANDALONE
     680             : # include "long-options.h"
     681             : 
     682             : void
     683           2 : usage (int status)
     684             : {
     685           2 :   if (status != EXIT_SUCCESS)
     686           0 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     687             :              program_name);
     688             :   else
     689             :     {
     690           2 :       fputs (_("\
     691             : Usage: test EXPRESSION\n\
     692             :   or:  test\n\
     693             :   or:  [ EXPRESSION ]\n\
     694             :   or:  [ ]\n\
     695             :   or:  [ OPTION\n\
     696             : "), stdout);
     697           2 :       fputs (_("\
     698             : Exit with the status determined by EXPRESSION.\n\
     699             : \n\
     700             : "), stdout);
     701           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     702           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     703           2 :       fputs (_("\
     704             : \n\
     705             : An omitted EXPRESSION defaults to false.  Otherwise,\n\
     706             : EXPRESSION is true or false and sets exit status.  It is one of:\n\
     707             : "), stdout);
     708           2 :       fputs (_("\
     709             : \n\
     710             :   ( EXPRESSION )               EXPRESSION is true\n\
     711             :   ! EXPRESSION                 EXPRESSION is false\n\
     712             :   EXPRESSION1 -a EXPRESSION2   both EXPRESSION1 and EXPRESSION2 are true\n\
     713             :   EXPRESSION1 -o EXPRESSION2   either EXPRESSION1 or EXPRESSION2 is true\n\
     714             : "), stdout);
     715           2 :       fputs (_("\
     716             : \n\
     717             :   -n STRING            the length of STRING is nonzero\n\
     718             :   STRING               equivalent to -n STRING\n\
     719             :   -z STRING            the length of STRING is zero\n\
     720             :   STRING1 = STRING2    the strings are equal\n\
     721             :   STRING1 != STRING2   the strings are not equal\n\
     722             : "), stdout);
     723           2 :       fputs (_("\
     724             : \n\
     725             :   INTEGER1 -eq INTEGER2   INTEGER1 is equal to INTEGER2\n\
     726             :   INTEGER1 -ge INTEGER2   INTEGER1 is greater than or equal to INTEGER2\n\
     727             :   INTEGER1 -gt INTEGER2   INTEGER1 is greater than INTEGER2\n\
     728             :   INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2\n\
     729             :   INTEGER1 -lt INTEGER2   INTEGER1 is less than INTEGER2\n\
     730             :   INTEGER1 -ne INTEGER2   INTEGER1 is not equal to INTEGER2\n\
     731             : "), stdout);
     732           2 :       fputs (_("\
     733             : \n\
     734             :   FILE1 -ef FILE2   FILE1 and FILE2 have the same device and inode numbers\n\
     735             :   FILE1 -nt FILE2   FILE1 is newer (modification date) than FILE2\n\
     736             :   FILE1 -ot FILE2   FILE1 is older than FILE2\n\
     737             : "), stdout);
     738           2 :       fputs (_("\
     739             : \n\
     740             :   -b FILE     FILE exists and is block special\n\
     741             :   -c FILE     FILE exists and is character special\n\
     742             :   -d FILE     FILE exists and is a directory\n\
     743             :   -e FILE     FILE exists\n\
     744             : "), stdout);
     745           2 :       fputs (_("\
     746             :   -f FILE     FILE exists and is a regular file\n\
     747             :   -g FILE     FILE exists and is set-group-ID\n\
     748             :   -G FILE     FILE exists and is owned by the effective group ID\n\
     749             :   -h FILE     FILE exists and is a symbolic link (same as -L)\n\
     750             :   -k FILE     FILE exists and has its sticky bit set\n\
     751             : "), stdout);
     752           2 :       fputs (_("\
     753             :   -L FILE     FILE exists and is a symbolic link (same as -h)\n\
     754             :   -O FILE     FILE exists and is owned by the effective user ID\n\
     755             :   -p FILE     FILE exists and is a named pipe\n\
     756             :   -r FILE     FILE exists and read permission is granted\n\
     757             :   -s FILE     FILE exists and has a size greater than zero\n\
     758             : "), stdout);
     759           2 :       fputs (_("\
     760             :   -S FILE     FILE exists and is a socket\n\
     761             :   -t FD       file descriptor FD is opened on a terminal\n\
     762             :   -u FILE     FILE exists and its set-user-ID bit is set\n\
     763             :   -w FILE     FILE exists and write permission is granted\n\
     764             :   -x FILE     FILE exists and execute (or search) permission is granted\n\
     765             : "), stdout);
     766           2 :       fputs (_("\
     767             : \n\
     768             : Except for -h and -L, all FILE-related tests dereference symbolic links.\n\
     769             : Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
     770             : INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
     771             : "), stdout);
     772           2 :       fputs (_("\
     773             : \n\
     774             : NOTE: [ honors the --help and --version options, but test does not.\n\
     775             : test treats each of those as it treats any other nonempty STRING.\n\
     776             : "), stdout);
     777           2 :       printf (USAGE_BUILTIN_WARNING, _("test and/or ["));
     778           2 :       emit_bug_reporting_address ();
     779             :     }
     780           2 :   exit (status);
     781             : }
     782             : #endif /* TEST_STANDALONE */
     783             : 
     784             : #if !defined TEST_STANDALONE
     785             : # define main test_command
     786             : #endif
     787             : 
     788             : #define AUTHORS "Kevin Braunsdorf", "Matthew Bradburn"
     789             : 
     790             : /*
     791             :  * [:
     792             :  *      '[' expr ']'
     793             :  * test:
     794             :  *      test expr
     795             :  */
     796             : int
     797          94 : main (int margc, char **margv)
     798             : {
     799             :   bool value;
     800             : 
     801             : #if !defined TEST_STANDALONE
     802             :   int code;
     803             : 
     804             :   code = setjmp (test_exit_buf);
     805             : 
     806             :   if (code)
     807             :     return (test_error_return);
     808             : #else /* TEST_STANDALONE */
     809             :   initialize_main (&margc, &margv);
     810          94 :   program_name = margv[0];
     811          94 :   setlocale (LC_ALL, "");
     812             :   bindtextdomain (PACKAGE, LOCALEDIR);
     813             :   textdomain (PACKAGE);
     814             : 
     815          94 :   initialize_exit_failure (TEST_FAILURE);
     816          94 :   atexit (close_stdout);
     817             : #endif /* TEST_STANDALONE */
     818             : 
     819          94 :   argv = margv;
     820             : 
     821             :   if (LBRACKET)
     822             :     {
     823             :       /* Recognize --help or --version, but only when invoked in the
     824             :          "[" form, and when the last argument is not "]".  POSIX
     825             :          allows "[ --help" and "[ --version" to have the usual GNU
     826             :          behavior, but it requires "test --help" and "test --version"
     827             :          to exit silently with status 0.  */
     828          94 :       if (margc < 2 || !STREQ (margv[margc - 1], "]"))
     829             :         {
     830          14 :           parse_long_options (margc, margv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
     831             :                               usage, AUTHORS, (char const *) NULL);
     832          11 :           test_syntax_error (_("missing `]'"), NULL);
     833             :         }
     834             : 
     835          80 :       --margc;
     836             :     }
     837             : 
     838          80 :   argc = margc;
     839          80 :   pos = 1;
     840             : 
     841          80 :   if (pos >= argc)
     842           1 :     test_exit (TEST_FALSE);
     843             : 
     844          79 :   value = posixtest (argc - 1);
     845             : 
     846          56 :   if (pos != argc)
     847           1 :     test_syntax_error (_("extra argument %s"), quote (argv[pos]));
     848             : 
     849          55 :   test_exit (value ? TEST_TRUE : TEST_FALSE);
     850             : }

Generated by: LCOV version 1.10