1183234Ssimon/* test.c - GNU test program (ksb and mjb) */
2296341Sdelphij
3296341Sdelphij/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */
4183234Ssimon
5183234Ssimon/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
6183234Ssimon
7183234Ssimon   This file is part of GNU Bash, the Bourne Again SHell.
8183234Ssimon
9183234Ssimon   Bash is free software: you can redistribute it and/or modify
10183234Ssimon   it under the terms of the GNU General Public License as published by
11183234Ssimon   the Free Software Foundation, either version 3 of the License, or
12183234Ssimon   (at your option) any later version.
13183234Ssimon
14296341Sdelphij   Bash is distributed in the hope that it will be useful,
15183234Ssimon   but WITHOUT ANY WARRANTY; without even the implied warranty of
16183234Ssimon   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17183234Ssimon   GNU General Public License for more details.
18183234Ssimon
19183234Ssimon   You should have received a copy of the GNU General Public License
20183234Ssimon   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
21183234Ssimon*/
22183234Ssimon
23183234Ssimon/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
24183234Ssimon   binary operators. */
25183234Ssimon/* #define PATTERN_MATCHING */
26183234Ssimon
27183234Ssimon#if defined (HAVE_CONFIG_H)
28183234Ssimon#  include <config.h>
29183234Ssimon#endif
30183234Ssimon
31183234Ssimon#include <stdio.h>
32183234Ssimon
33183234Ssimon#include "bashtypes.h"
34183234Ssimon
35183234Ssimon#if !defined (HAVE_LIMITS_H)
36183234Ssimon#  include <sys/param.h>
37183234Ssimon#endif
38183234Ssimon
39183234Ssimon#if defined (HAVE_UNISTD_H)
40183234Ssimon#  include <unistd.h>
41183234Ssimon#endif
42183234Ssimon
43183234Ssimon#include <errno.h>
44183234Ssimon#if !defined (errno)
45183234Ssimonextern int errno;
46183234Ssimon#endif /* !errno */
47183234Ssimon
48183234Ssimon#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
49183234Ssimon#  include <sys/file.h>
50183234Ssimon#endif /* !_POSIX_VERSION */
51183234Ssimon#include "posixstat.h"
52183234Ssimon#include "filecntl.h"
53183234Ssimon
54183234Ssimon#include "bashintl.h"
55183234Ssimon
56183234Ssimon#include "shell.h"
57183234Ssimon#include "pathexp.h"
58183234Ssimon#include "test.h"
59183234Ssimon#include "builtins/common.h"
60183234Ssimon
61183234Ssimon#include <glob/strmatch.h>
62183234Ssimon
63296341Sdelphij#if !defined (STRLEN)
64296341Sdelphij#  define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
65296341Sdelphij#endif
66296341Sdelphij
67296341Sdelphij#if !defined (STREQ)
68296341Sdelphij#  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
69183234Ssimon#endif /* !STREQ */
70296341Sdelphij
71296341Sdelphij#if !defined (R_OK)
72183234Ssimon#define R_OK 4
73183234Ssimon#define W_OK 2
74183234Ssimon#define X_OK 1
75296341Sdelphij#define F_OK 0
76296341Sdelphij#endif /* R_OK */
77296341Sdelphij
78183234Ssimon#define EQ	0
79296341Sdelphij#define NE	1
80296341Sdelphij#define LT	2
81296341Sdelphij#define GT	3
82296341Sdelphij#define LE	4
83296341Sdelphij#define GE	5
84296341Sdelphij
85296341Sdelphij#define NT	0
86296341Sdelphij#define OT	1
87296341Sdelphij#define EF	2
88296341Sdelphij
89296341Sdelphij/* The following few defines control the truth and false output of each stage.
90296341Sdelphij   TRUE and FALSE are what we use to compute the final output value.
91296341Sdelphij   SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
92296341Sdelphij   Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
93296341Sdelphij#define TRUE 1
94296341Sdelphij#define FALSE 0
95296341Sdelphij#define SHELL_BOOLEAN(value) (!(value))
96296341Sdelphij
97296341Sdelphij#define TEST_ERREXIT_STATUS	2
98183234Ssimon
99238405Sjkimstatic procenv_t test_exit_buf;
100238405Sjkimstatic int test_error_return;
101183234Ssimon#define test_exit(val) \
102183234Ssimon	do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)
103183234Ssimon
104296341Sdelphijextern int sh_stat __P((const char *, struct stat *));
105296341Sdelphij
106296341Sdelphijstatic int pos;		/* The offset of the current argument in ARGV. */
107296341Sdelphijstatic int argc;	/* The number of arguments present in ARGV. */
108296341Sdelphijstatic char **argv;	/* The argument list. */
109296341Sdelphijstatic int noeval;
110296341Sdelphij
111296341Sdelphijstatic void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
112296341Sdelphijstatic void beyond __P((void)) __attribute__((__noreturn__));
113296341Sdelphijstatic void integer_expected_error __P((char *)) __attribute__((__noreturn__));
114296341Sdelphij
115296341Sdelphijstatic int unary_operator __P((void));
116296341Sdelphijstatic int binary_operator __P((void));
117296341Sdelphijstatic int two_arguments __P((void));
118296341Sdelphijstatic int three_arguments __P((void));
119296341Sdelphijstatic int posixtest __P((void));
120296341Sdelphij
121296341Sdelphijstatic int expr __P((void));
122296341Sdelphijstatic int term __P((void));
123296341Sdelphijstatic int and __P((void));
124296341Sdelphijstatic int or __P((void));
125296341Sdelphij
126296341Sdelphijstatic int filecomp __P((char *, char *, int));
127296341Sdelphijstatic int arithcomp __P((char *, char *, int, int));
128296341Sdelphijstatic int patcomp __P((char *, char *, int));
129296341Sdelphij
130296341Sdelphijstatic void
131296341Sdelphijtest_syntax_error (format, arg)
132296341Sdelphij     char *format, *arg;
133296341Sdelphij{
134296341Sdelphij  builtin_error (format, arg);
135296341Sdelphij  test_exit (TEST_ERREXIT_STATUS);
136296341Sdelphij}
137296341Sdelphij
138296341Sdelphij/*
139296341Sdelphij * beyond - call when we're beyond the end of the argument list (an
140296341Sdelphij *	error condition)
141296341Sdelphij */
142183234Ssimonstatic void
143296341Sdelphijbeyond ()
144183234Ssimon{
145296341Sdelphij  test_syntax_error (_("argument expected"), (char *)NULL);
146183234Ssimon}
147296341Sdelphij
148296341Sdelphij/* Syntax error for when an integer argument was expected, but
149183234Ssimon   something else was found. */
150296341Sdelphijstatic void
151183234Ssimoninteger_expected_error (pch)
152296341Sdelphij     char *pch;
153296341Sdelphij{
154296341Sdelphij  test_syntax_error (_("%s: integer expression expected"), pch);
155296341Sdelphij}
156183234Ssimon
157296341Sdelphij/* Increment our position in the argument list.  Check that we're not
158296341Sdelphij   past the end of the argument list.  This check is supressed if the
159183234Ssimon   argument is FALSE.  Made a macro for efficiency. */
160296341Sdelphij#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
161296341Sdelphij#define unary_advance() do { advance (1); ++pos; } while (0)
162296341Sdelphij
163296341Sdelphij/*
164296341Sdelphij * expr:
165296341Sdelphij *	or
166296341Sdelphij */
167296341Sdelphijstatic int
168296341Sdelphijexpr ()
169296341Sdelphij{
170296341Sdelphij  if (pos >= argc)
171296341Sdelphij    beyond ();
172296341Sdelphij
173296341Sdelphij  return (FALSE ^ or ());		/* Same with this. */
174296341Sdelphij}
175296341Sdelphij
176296341Sdelphij/*
177296341Sdelphij * or:
178296341Sdelphij *	and
179296341Sdelphij *	and '-o' or
180296341Sdelphij */
181296341Sdelphijstatic int
182296341Sdelphijor ()
183296341Sdelphij{
184296341Sdelphij  int value, v2;
185296341Sdelphij
186296341Sdelphij  value = and ();
187296341Sdelphij  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
188296341Sdelphij    {
189296341Sdelphij      advance (0);
190296341Sdelphij      v2 = or ();
191296341Sdelphij      return (value || v2);
192296341Sdelphij    }
193296341Sdelphij
194296341Sdelphij  return (value);
195296341Sdelphij}
196296341Sdelphij
197296341Sdelphij/*
198296341Sdelphij * and:
199296341Sdelphij *	term
200296341Sdelphij *	term '-a' and
201296341Sdelphij */
202296341Sdelphijstatic int
203296341Sdelphijand ()
204296341Sdelphij{
205296341Sdelphij  int value, v2;
206296341Sdelphij
207296341Sdelphij  value = term ();
208296341Sdelphij  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
209296341Sdelphij    {
210296341Sdelphij      advance (0);
211296341Sdelphij      v2 = and ();
212296341Sdelphij      return (value && v2);
213296341Sdelphij    }
214296341Sdelphij  return (value);
215296341Sdelphij}
216296341Sdelphij
217296341Sdelphij/*
218296341Sdelphij * term - parse a term and return 1 or 0 depending on whether the term
219296341Sdelphij *	evaluates to true or false, respectively.
220296341Sdelphij *
221296341Sdelphij * term ::=
222296341Sdelphij *	'-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
223296341Sdelphij *	'-'('G'|'L'|'O'|'S'|'N') filename
224296341Sdelphij * 	'-t' [int]
225296341Sdelphij *	'-'('z'|'n') string
226296341Sdelphij *	'-o' option
227296341Sdelphij *	string
228296341Sdelphij *	string ('!='|'='|'==') string
229296341Sdelphij *	<int> '-'(eq|ne|le|lt|ge|gt) <int>
230296341Sdelphij *	file '-'(nt|ot|ef) file
231296341Sdelphij *	'(' <expr> ')'
232296341Sdelphij * int ::=
233296341Sdelphij *	positive and negative integers
234296341Sdelphij */
235296341Sdelphijstatic int
236296341Sdelphijterm ()
237296341Sdelphij{
238296341Sdelphij  int value;
239296341Sdelphij
240296341Sdelphij  if (pos >= argc)
241296341Sdelphij    beyond ();
242296341Sdelphij
243296341Sdelphij  /* Deal with leading `not's. */
244296341Sdelphij  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
245296341Sdelphij    {
246296341Sdelphij      value = 0;
247296341Sdelphij      while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
248296341Sdelphij	{
249296341Sdelphij	  advance (1);
250296341Sdelphij	  value = 1 - value;
251296341Sdelphij	}
252296341Sdelphij
253296341Sdelphij      return (value ? !term() : term());
254296341Sdelphij    }
255296341Sdelphij
256296341Sdelphij  /* A paren-bracketed argument. */
257296341Sdelphij  if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */
258296341Sdelphij    {
259296341Sdelphij      advance (1);
260296341Sdelphij      value = expr ();
261296341Sdelphij      if (argv[pos] == 0) /* ( */
262296341Sdelphij	test_syntax_error (_("`)' expected"), (char *)NULL);
263296341Sdelphij      else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */
264296341Sdelphij	test_syntax_error (_("`)' expected, found %s"), argv[pos]);
265296341Sdelphij      advance (0);
266296341Sdelphij      return (value);
267296341Sdelphij    }
268296341Sdelphij
269296341Sdelphij  /* are there enough arguments left that this could be dyadic? */
270296341Sdelphij  if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
271296341Sdelphij    value = binary_operator ();
272296341Sdelphij
273296341Sdelphij  /* Might be a switch type argument */
274296341Sdelphij  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
275296341Sdelphij    {
276296341Sdelphij      if (test_unop (argv[pos]))
277296341Sdelphij	value = unary_operator ();
278296341Sdelphij      else
279296341Sdelphij	test_syntax_error (_("%s: unary operator expected"), argv[pos]);
280296341Sdelphij    }
281296341Sdelphij  else
282296341Sdelphij    {
283296341Sdelphij      value = argv[pos][0] != '\0';
284296341Sdelphij      advance (0);
285296341Sdelphij    }
286296341Sdelphij
287296341Sdelphij  return (value);
288296341Sdelphij}
289296341Sdelphij
290296341Sdelphijstatic int
291296341Sdelphijfilecomp (s, t, op)
292296341Sdelphij     char *s, *t;
293296341Sdelphij     int op;
294296341Sdelphij{
295296341Sdelphij  struct stat st1, st2;
296296341Sdelphij  int r1, r2;
297296341Sdelphij
298296341Sdelphij  if ((r1 = sh_stat (s, &st1)) < 0)
299296341Sdelphij    {
300296341Sdelphij      if (op == EF)
301296341Sdelphij	return (FALSE);
302296341Sdelphij    }
303296341Sdelphij  if ((r2 = sh_stat (t, &st2)) < 0)
304296341Sdelphij    {
305296341Sdelphij      if (op == EF)
306296341Sdelphij	return (FALSE);
307296341Sdelphij    }
308296341Sdelphij
309296341Sdelphij  switch (op)
310296341Sdelphij    {
311296341Sdelphij    case OT: return (r1 < r2 || (r2 == 0 && st1.st_mtime < st2.st_mtime));
312296341Sdelphij    case NT: return (r1 > r2 || (r1 == 0 && st1.st_mtime > st2.st_mtime));
313296341Sdelphij    case EF: return (same_file (s, t, &st1, &st2));
314296341Sdelphij    }
315296341Sdelphij  return (FALSE);
316296341Sdelphij}
317296341Sdelphij
318296341Sdelphijstatic int
319296341Sdelphijarithcomp (s, t, op, flags)
320296341Sdelphij     char *s, *t;
321296341Sdelphij     int op, flags;
322296341Sdelphij{
323296341Sdelphij  intmax_t l, r;
324296341Sdelphij  int expok;
325296341Sdelphij
326296341Sdelphij  if (flags & TEST_ARITHEXP)
327296341Sdelphij    {
328296341Sdelphij      l = evalexp (s, &expok);
329296341Sdelphij      if (expok == 0)
330296341Sdelphij	return (FALSE);		/* should probably longjmp here */
331296341Sdelphij      r = evalexp (t, &expok);
332296341Sdelphij      if (expok == 0)
333296341Sdelphij	return (FALSE);		/* ditto */
334296341Sdelphij    }
335296341Sdelphij  else
336296341Sdelphij    {
337296341Sdelphij      if (legal_number (s, &l) == 0)
338296341Sdelphij	integer_expected_error (s);
339296341Sdelphij      if (legal_number (t, &r) == 0)
340296341Sdelphij	integer_expected_error (t);
341296341Sdelphij    }
342296341Sdelphij
343296341Sdelphij  switch (op)
344296341Sdelphij    {
345296341Sdelphij    case EQ: return (l == r);
346296341Sdelphij    case NE: return (l != r);
347296341Sdelphij    case LT: return (l < r);
348296341Sdelphij    case GT: return (l > r);
349296341Sdelphij    case LE: return (l <= r);
350296341Sdelphij    case GE: return (l >= r);
351296341Sdelphij    }
352296341Sdelphij
353296341Sdelphij  return (FALSE);
354296341Sdelphij}
355296341Sdelphij
356296341Sdelphijstatic int
357296341Sdelphijpatcomp (string, pat, op)
358296341Sdelphij     char *string, *pat;
359296341Sdelphij     int op;
360296341Sdelphij{
361296341Sdelphij  int m;
362296341Sdelphij
363296341Sdelphij  m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE);
364296341Sdelphij  return ((op == EQ) ? (m == 0) : (m != 0));
365183234Ssimon}
366296341Sdelphij
367296341Sdelphijint
368296341Sdelphijbinary_test (op, arg1, arg2, flags)
369296341Sdelphij     char *op, *arg1, *arg2;
370296341Sdelphij     int flags;
371296341Sdelphij{
372296341Sdelphij  int patmatch;
373296341Sdelphij
374296341Sdelphij  patmatch = (flags & TEST_PATMATCH);
375296341Sdelphij
376296341Sdelphij  if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
377296341Sdelphij    return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));
378296341Sdelphij
379296341Sdelphij  else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
380296341Sdelphij    return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));
381296341Sdelphij
382296341Sdelphij  else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
383296341Sdelphij    return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));
384296341Sdelphij
385296341Sdelphij  else if (op[2] == 't')
386296341Sdelphij    {
387296341Sdelphij      switch (op[1])
388296341Sdelphij	{
389296341Sdelphij	case 'n': return (filecomp (arg1, arg2, NT));		/* -nt */
390296341Sdelphij	case 'o': return (filecomp (arg1, arg2, OT));		/* -ot */
391296341Sdelphij	case 'l': return (arithcomp (arg1, arg2, LT, flags));	/* -lt */
392296341Sdelphij	case 'g': return (arithcomp (arg1, arg2, GT, flags));	/* -gt */
393296341Sdelphij	}
394296341Sdelphij    }
395296341Sdelphij  else if (op[1] == 'e')
396296341Sdelphij    {
397296341Sdelphij      switch (op[2])
398296341Sdelphij	{
399296341Sdelphij	case 'f': return (filecomp (arg1, arg2, EF));		/* -ef */
400296341Sdelphij	case 'q': return (arithcomp (arg1, arg2, EQ, flags));	/* -eq */
401296341Sdelphij	}
402296341Sdelphij    }
403296341Sdelphij  else if (op[2] == 'e')
404296341Sdelphij    {
405296341Sdelphij      switch (op[1])
406296341Sdelphij	{
407296341Sdelphij	case 'n': return (arithcomp (arg1, arg2, NE, flags));	/* -ne */
408296341Sdelphij	case 'g': return (arithcomp (arg1, arg2, GE, flags));	/* -ge */
409296341Sdelphij	case 'l': return (arithcomp (arg1, arg2, LE, flags));	/* -le */
410296341Sdelphij	}
411296341Sdelphij    }
412296341Sdelphij
413296341Sdelphij  return (FALSE);	/* should never get here */
414296341Sdelphij}
415296341Sdelphij
416296341Sdelphij
417296341Sdelphijstatic int
418296341Sdelphijbinary_operator ()
419296341Sdelphij{
420296341Sdelphij  int value;
421296341Sdelphij  char *w;
422296341Sdelphij
423296341Sdelphij  w = argv[pos + 1];
424296341Sdelphij  if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
425296341Sdelphij      ((w[0] == '>' || w[0] == '<') && w[1] == '\0') ||		/* <, > */
426296341Sdelphij      (w[0] == '!' && w[1] == '=' && w[2] == '\0'))		/* != */
427296341Sdelphij    {
428296341Sdelphij      value = binary_test (w, argv[pos], argv[pos + 2], 0);
429296341Sdelphij      pos += 3;
430296341Sdelphij      return (value);
431296341Sdelphij    }
432296341Sdelphij
433296341Sdelphij#if defined (PATTERN_MATCHING)
434296341Sdelphij  if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
435296341Sdelphij    {
436296341Sdelphij      value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
437296341Sdelphij      pos += 3;
438296341Sdelphij      return (value);
439296341Sdelphij    }
440296341Sdelphij#endif
441296341Sdelphij
442296341Sdelphij  if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
443296341Sdelphij    {
444296341Sdelphij      test_syntax_error (_("%s: binary operator expected"), w);
445296341Sdelphij      /* NOTREACHED */
446296341Sdelphij      return (FALSE);
447296341Sdelphij    }
448296341Sdelphij
449296341Sdelphij  value = binary_test (w, argv[pos], argv[pos + 2], 0);
450296341Sdelphij  pos += 3;
451296341Sdelphij  return value;
452296341Sdelphij}
453296341Sdelphij
454296341Sdelphijstatic int
455296341Sdelphijunary_operator ()
456296341Sdelphij{
457296341Sdelphij  char *op;
458183234Ssimon  intmax_t r;
459296341Sdelphij
460296341Sdelphij  op = argv[pos];
461296341Sdelphij  if (test_unop (op) == 0)
462296341Sdelphij    return (FALSE);
463183234Ssimon
464296341Sdelphij  /* the only tricky case is `-t', which may or may not take an argument. */
465296341Sdelphij  if (op[1] == 't')
466296341Sdelphij    {
467296341Sdelphij      advance (0);
468296341Sdelphij      if (pos < argc)
469296341Sdelphij	{
470296341Sdelphij	  if (legal_number (argv[pos], &r))
471296341Sdelphij	    {
472183234Ssimon	      advance (0);
473296341Sdelphij	      return (unary_test (op, argv[pos - 1]));
474296341Sdelphij	    }
475296341Sdelphij	  else
476296341Sdelphij	    return (FALSE);
477296341Sdelphij	}
478296341Sdelphij      else
479296341Sdelphij	return (unary_test (op, "1"));
480296341Sdelphij    }
481296341Sdelphij
482296341Sdelphij  /* All of the unary operators take an argument, so we first call
483296341Sdelphij     unary_advance (), which checks to make sure that there is an
484296341Sdelphij     argument, and then advances pos right past it.  This means that
485296341Sdelphij     pos - 1 is the location of the argument. */
486296341Sdelphij  unary_advance ();
487296341Sdelphij  return (unary_test (op, argv[pos - 1]));
488296341Sdelphij}
489296341Sdelphij
490296341Sdelphijint
491296341Sdelphijunary_test (op, arg)
492296341Sdelphij     char *op, *arg;
493296341Sdelphij{
494296341Sdelphij  intmax_t r;
495296341Sdelphij  struct stat stat_buf;
496296341Sdelphij
497183234Ssimon  switch (op[1])
498296341Sdelphij    {
499296341Sdelphij    case 'a':			/* file exists in the file system? */
500296341Sdelphij    case 'e':
501296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0);
502296341Sdelphij
503296341Sdelphij    case 'r':			/* file is readable? */
504296341Sdelphij      return (sh_eaccess (arg, R_OK) == 0);
505296341Sdelphij
506296341Sdelphij    case 'w':			/* File is writeable? */
507296341Sdelphij      return (sh_eaccess (arg, W_OK) == 0);
508296341Sdelphij
509296341Sdelphij    case 'x':			/* File is executable? */
510296341Sdelphij      return (sh_eaccess (arg, X_OK) == 0);
511296341Sdelphij
512183234Ssimon    case 'O':			/* File is owned by you? */
513296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 &&
514296341Sdelphij	      (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);
515296341Sdelphij
516296341Sdelphij    case 'G':			/* File is owned by your group? */
517296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 &&
518296341Sdelphij	      (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);
519296341Sdelphij
520296341Sdelphij    case 'N':
521296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 &&
522296341Sdelphij	      stat_buf.st_atime <= stat_buf.st_mtime);
523296341Sdelphij
524296341Sdelphij    case 'f':			/* File is a file? */
525296341Sdelphij      if (sh_stat (arg, &stat_buf) < 0)
526296341Sdelphij	return (FALSE);
527296341Sdelphij
528296341Sdelphij      /* -f is true if the given file exists and is a regular file. */
529296341Sdelphij#if defined (S_IFMT)
530296341Sdelphij      return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
531296341Sdelphij#else
532296341Sdelphij      return (S_ISREG (stat_buf.st_mode));
533296341Sdelphij#endif /* !S_IFMT */
534296341Sdelphij
535296341Sdelphij    case 'd':			/* File is a directory? */
536296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));
537296341Sdelphij
538296341Sdelphij    case 's':			/* File has something in it? */
539296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);
540296341Sdelphij
541296341Sdelphij    case 'S':			/* File is a socket? */
542296341Sdelphij#if !defined (S_ISSOCK)
543296341Sdelphij      return (FALSE);
544296341Sdelphij#else
545296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
546296341Sdelphij#endif /* S_ISSOCK */
547296341Sdelphij
548296341Sdelphij    case 'c':			/* File is character special? */
549296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));
550296341Sdelphij
551296341Sdelphij    case 'b':			/* File is block special? */
552296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));
553296341Sdelphij
554296341Sdelphij    case 'p':			/* File is a named pipe? */
555296341Sdelphij#ifndef S_ISFIFO
556296341Sdelphij      return (FALSE);
557296341Sdelphij#else
558296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
559296341Sdelphij#endif /* S_ISFIFO */
560296341Sdelphij
561296341Sdelphij    case 'L':			/* Same as -h  */
562296341Sdelphij    case 'h':			/* File is a symbolic link? */
563296341Sdelphij#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
564296341Sdelphij      return (FALSE);
565296341Sdelphij#else
566296341Sdelphij      return ((arg[0] != '\0') &&
567296341Sdelphij	      (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
568296341Sdelphij#endif /* S_IFLNK && HAVE_LSTAT */
569296341Sdelphij
570296341Sdelphij    case 'u':			/* File is setuid? */
571296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);
572296341Sdelphij
573296341Sdelphij    case 'g':			/* File is setgid? */
574296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);
575296341Sdelphij
576296341Sdelphij    case 'k':			/* File has sticky bit set? */
577296341Sdelphij#if !defined (S_ISVTX)
578296341Sdelphij      /* This is not Posix, and is not defined on some Posix systems. */
579296341Sdelphij      return (FALSE);
580296341Sdelphij#else
581296341Sdelphij      return (sh_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
582296341Sdelphij#endif
583296341Sdelphij
584296341Sdelphij    case 't':	/* File fd is a terminal? */
585296341Sdelphij      if (legal_number (arg, &r) == 0)
586296341Sdelphij	return (FALSE);
587296341Sdelphij      return ((r == (int)r) && isatty ((int)r));
588296341Sdelphij
589296341Sdelphij    case 'n':			/* True if arg has some length. */
590296341Sdelphij      return (arg[0] != '\0');
591296341Sdelphij
592296341Sdelphij    case 'z':			/* True if arg has no length. */
593296341Sdelphij      return (arg[0] == '\0');
594296341Sdelphij
595296341Sdelphij    case 'o':			/* True if option `arg' is set. */
596296341Sdelphij      return (minus_o_option_value (arg) == 1);
597296341Sdelphij    }
598296341Sdelphij
599296341Sdelphij  /* We can't actually get here, but this shuts up gcc. */
600296341Sdelphij  return (FALSE);
601296341Sdelphij}
602296341Sdelphij
603296341Sdelphij/* Return TRUE if OP is one of the test command's binary operators. */
604296341Sdelphijint
605296341Sdelphijtest_binop (op)
606183234Ssimon     char *op;
607296341Sdelphij{
608296341Sdelphij  if (op[0] == '=' && op[1] == '\0')
609296341Sdelphij    return (1);		/* '=' */
610296341Sdelphij  else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0')  /* string <, > */
611183234Ssimon    return (1);
612296341Sdelphij  else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
613296341Sdelphij    return (1);		/* `==' and `!=' */
614296341Sdelphij#if defined (PATTERN_MATCHING)
615296341Sdelphij  else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
616296341Sdelphij    return (1);
617296341Sdelphij#endif
618183234Ssimon  else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
619296341Sdelphij    return (0);
620183234Ssimon  else
621296341Sdelphij    {
622296341Sdelphij      if (op[2] == 't')
623183234Ssimon	switch (op[1])
624296341Sdelphij	  {
625296341Sdelphij	  case 'n':		/* -nt */
626296341Sdelphij	  case 'o':		/* -ot */
627296341Sdelphij	  case 'l':		/* -lt */
628296341Sdelphij	  case 'g':		/* -gt */
629296341Sdelphij	    return (1);
630296341Sdelphij	  default:
631183234Ssimon	    return (0);
632296341Sdelphij	  }
633296341Sdelphij      else if (op[1] == 'e')
634296341Sdelphij	switch (op[2])
635296341Sdelphij	  {
636296341Sdelphij	  case 'q':		/* -eq */
637296341Sdelphij	  case 'f':		/* -ef */
638296341Sdelphij	    return (1);
639183234Ssimon	  default:
640296341Sdelphij	    return (0);
641296341Sdelphij	  }
642296341Sdelphij      else if (op[2] == 'e')
643296341Sdelphij	switch (op[1])
644296341Sdelphij	  {
645296341Sdelphij	  case 'n':		/* -ne */
646296341Sdelphij	  case 'g':		/* -ge */
647296341Sdelphij	  case 'l':		/* -le */
648296341Sdelphij	    return (1);
649183234Ssimon	  default:
650296341Sdelphij	    return (0);
651296341Sdelphij	  }
652296341Sdelphij      else
653296341Sdelphij	return (0);
654183234Ssimon    }
655296341Sdelphij}
656296341Sdelphij
657296341Sdelphij/* Return non-zero if OP is one of the test command's unary operators. */
658296341Sdelphijint
659296341Sdelphijtest_unop (op)
660296341Sdelphij     char *op;
661296341Sdelphij{
662296341Sdelphij  if (op[0] != '-' || op[2] != 0)
663296341Sdelphij    return (0);
664296341Sdelphij
665296341Sdelphij  switch (op[1])
666183234Ssimon    {
667296341Sdelphij    case 'a': case 'b': case 'c': case 'd': case 'e':
668296341Sdelphij    case 'f': case 'g': case 'h': case 'k': case 'n':
669296341Sdelphij    case 'o': case 'p': case 'r': case 's': case 't':
670296341Sdelphij    case 'u': case 'w': case 'x': case 'z':
671296341Sdelphij    case 'G': case 'L': case 'O': case 'S': case 'N':
672296341Sdelphij      return (1);
673296341Sdelphij    }
674183234Ssimon
675296341Sdelphij  return (0);
676296341Sdelphij}
677296341Sdelphij
678296341Sdelphijstatic int
679296341Sdelphijtwo_arguments ()
680296341Sdelphij{
681296341Sdelphij  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
682183234Ssimon    return (argv[pos + 1][0] == '\0');
683296341Sdelphij  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
684296341Sdelphij    {
685296341Sdelphij      if (test_unop (argv[pos]))
686296341Sdelphij	return (unary_operator ());
687296341Sdelphij      else
688296341Sdelphij	test_syntax_error (_("%s: unary operator expected"), argv[pos]);
689296341Sdelphij    }
690183234Ssimon  else
691296341Sdelphij    test_syntax_error (_("%s: unary operator expected"), argv[pos]);
692296341Sdelphij
693296341Sdelphij  return (0);
694296341Sdelphij}
695296341Sdelphij
696296341Sdelphij#define ANDOR(s)  (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))
697296341Sdelphij
698296341Sdelphij/* This could be augmented to handle `-t' as equivalent to `-t 1', but
699183234Ssimon   POSIX requires that `-t' be given an argument. */
700296341Sdelphij#define ONE_ARG_TEST(s)		((s)[0] != '\0')
701296341Sdelphij
702296341Sdelphijstatic int
703296341Sdelphijthree_arguments ()
704296341Sdelphij{
705296341Sdelphij  int value;
706183234Ssimon
707296341Sdelphij  if (test_binop (argv[pos+1]))
708296341Sdelphij    {
709296341Sdelphij      value = binary_operator ();
710296341Sdelphij      pos = argc;
711296341Sdelphij    }
712296341Sdelphij  else if (ANDOR (argv[pos+1]))
713296341Sdelphij    {
714183234Ssimon      if (argv[pos+1][1] == 'a')
715296341Sdelphij	value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
716296341Sdelphij      else
717296341Sdelphij	value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
718296341Sdelphij      pos = argc;
719296341Sdelphij    }
720296341Sdelphij  else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
721296341Sdelphij    {
722296341Sdelphij      advance (1);
723296341Sdelphij      value = !two_arguments ();
724296341Sdelphij    }
725296341Sdelphij  else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
726183234Ssimon    {
727296341Sdelphij      value = ONE_ARG_TEST(argv[pos+1]);
728296341Sdelphij      pos = argc;
729296341Sdelphij    }
730296341Sdelphij  else
731296341Sdelphij    test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
732296341Sdelphij
733296341Sdelphij  return (value);
734296341Sdelphij}
735296341Sdelphij
736296341Sdelphij/* This is an implementation of a Posix.2 proposal by David Korn. */
737296341Sdelphijstatic int
738296341Sdelphijposixtest ()
739296341Sdelphij{
740296341Sdelphij  int value;
741296341Sdelphij
742296341Sdelphij  switch (argc - 1)	/* one extra passed in */
743296341Sdelphij    {
744296341Sdelphij      case 0:
745296341Sdelphij	value = FALSE;
746296341Sdelphij	pos = argc;
747296341Sdelphij	break;
748296341Sdelphij
749296341Sdelphij      case 1:
750183234Ssimon	value = ONE_ARG_TEST(argv[1]);
751296341Sdelphij	pos = argc;
752296341Sdelphij	break;
753296341Sdelphij
754296341Sdelphij      case 2:
755296341Sdelphij	value = two_arguments ();
756296341Sdelphij	pos = argc;
757183234Ssimon	break;
758296341Sdelphij
759296341Sdelphij      case 3:
760296341Sdelphij	value = three_arguments ();
761296341Sdelphij	break;
762296341Sdelphij
763296341Sdelphij      case 4:
764296341Sdelphij	if (argv[pos][0] == '!' && argv[pos][1] == '\0')
765296341Sdelphij	  {
766296341Sdelphij	    advance (1);
767296341Sdelphij	    value = !three_arguments ();
768183234Ssimon	    break;
769296341Sdelphij	  }
770296341Sdelphij	/* FALLTHROUGH */
771296341Sdelphij      default:
772296341Sdelphij	value = expr ();
773296341Sdelphij    }
774183234Ssimon
775296341Sdelphij  return (value);
776296341Sdelphij}
777296341Sdelphij
778296341Sdelphij/*
779296341Sdelphij * [:
780296341Sdelphij *	'[' expr ']'
781296341Sdelphij * test:
782296341Sdelphij *	test expr
783296341Sdelphij */
784296341Sdelphijint
785296341Sdelphijtest_command (margc, margv)
786296341Sdelphij     int margc;
787296341Sdelphij     char **margv;
788296341Sdelphij{
789183234Ssimon  int value;
790296341Sdelphij  int code;
791296341Sdelphij
792296341Sdelphij  USE_VAR(margc);
793296341Sdelphij
794296341Sdelphij  code = setjmp (test_exit_buf);
795296341Sdelphij
796296341Sdelphij  if (code)
797183234Ssimon    return (test_error_return);
798296341Sdelphij
799183234Ssimon  argv = margv;
800296341Sdelphij
801296341Sdelphij  if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
802296341Sdelphij    {
803296341Sdelphij      --margc;
804296341Sdelphij
805296341Sdelphij      if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
806296341Sdelphij	test_syntax_error (_("missing `]'"), (char *)NULL);
807296341Sdelphij
808296341Sdelphij      if (margc < 2)
809296341Sdelphij	test_exit (SHELL_BOOLEAN (FALSE));
810296341Sdelphij    }
811296341Sdelphij
812296341Sdelphij  argc = margc;
813296341Sdelphij  pos = 1;
814296341Sdelphij
815296341Sdelphij  if (pos >= argc)
816296341Sdelphij    test_exit (SHELL_BOOLEAN (FALSE));
817296341Sdelphij
818296341Sdelphij  noeval = 0;
819296341Sdelphij  value = posixtest ();
820296341Sdelphij
821296341Sdelphij  if (pos != argc)
822296341Sdelphij    test_syntax_error (_("too many arguments"), (char *)NULL);
823296341Sdelphij
824296341Sdelphij  test_exit (SHELL_BOOLEAN (value));
825296341Sdelphij}
826296341Sdelphij