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