home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD2.bin
/
bbs
/
gnu
/
sh-utils-1.12-src.lha
/
sh-utils-1.12
/
src
/
test.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-10-01
|
26KB
|
1,114 lines
/* GNU test program (ksb and mjb) */
/* Modified to run with the GNU shell by bfox. */
/* Copyright (C) 1987-1993, 1994 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
Bash is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with Bash; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
the shell builtin version. */
/* #define TEST_STANDALONE */
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#if !defined (TEST_STANDALONE)
# include "shell.h"
# include "posixstat.h"
# include "filecntl.h"
#else /* TEST_STANDALONE */
# include "system.h"
# include "version.h"
# include "safe-stat.h"
# include "safe-lstat.h"
# include "group-member.h"
# if !defined (S_IXUGO)
# define S_IXUGO 0111
# endif /* S_IXUGO */
# if defined (_POSIX_VERSION)
# include <limits.h>
# else /* !_POSIX_VERSION */
# include <sys/param.h>
# endif /* _POSIX_VERSION */
#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
#define digit(c) ((c) >= '0' && (c) <= '9')
#define digit_value(c) ((c) - '0')
char *program_name;
#endif /* TEST_STANDALONE */
#if !defined (_POSIX_VERSION)
# include <sys/file.h>
#endif /* !_POSIX_VERSION */
#include <errno.h>
#ifndef errno
extern int errno;
#endif
#if !defined (STREQ)
# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
#endif /* !STREQ */
#if !defined (member)
# define member(c, s) ((c) ? (index ((s), (c)) ? 1 : 0) : 0)
#endif /* !member */
extern gid_t getgid (), getegid ();
extern uid_t geteuid ();
#if !defined (R_OK)
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
#endif /* R_OK */
/* This name is used solely when printing --version information. */
#define COMMAND_NAME "test"
/* The following few defines control the truth and false output of each stage.
TRUE and FALSE are what we use to compute the final output value.
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
TRUTH_OR is how to do logical or with TRUE and FALSE.
TRUTH_AND is how to do logical and with TRUE and FALSE..
Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
SHELL_BOOLEAN = (!value). */
#define TRUE 1
#define FALSE 0
#define SHELL_BOOLEAN(value) (!(value))
#define TRUTH_OR(a, b) ((a) | (b))
#define TRUTH_AND(a, b) ((a) & (b))
#if defined (TEST_STANDALONE)
# define test_exit(val) exit (val)
#else
static jmp_buf test_exit_buf;
static int test_error_return = 0;
# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
#endif /* !TEST_STANDALONE */
char *xrealloc ();
static int pos; /* The offset of the current argument in ARGV. */
static int argc; /* The number of arguments present in ARGV. */
static char **argv; /* The argument list. */
static int unop ();
static int binop ();
static int unary_operator ();
static int binary_operator ();
static int two_arguments ();
static int three_arguments ();
static int posixtest ();
static int expr ();
static int term ();
static int and ();
static int or ();
#if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
&& __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
#define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
#else
#define NO_RETURN_ATTRIBUTE /* empty */
#endif
static void test_syntax_error () NO_RETURN_ATTRIBUTE;
static void beyond () NO_RETURN_ATTRIBUTE;
static void
test_syntax_error (format, arg)
char *format, *arg;
{
fprintf (stderr, "%s: ", argv[0]);
fprintf (stderr, format, arg);
fflush (stderr);
test_exit (SHELL_BOOLEAN (FALSE));
}
/* A wrapper for stat () which disallows pathnames that are empty strings. */
static int
test_stat (path, finfo)
char *path;
struct stat *finfo;
{
if (*path == '\0')
{
errno = ENOENT;
return (-1);
}
return (SAFE_STAT (path, finfo));
}
/* Do the same thing access(2) does, but use the effective uid and gid,
and don't make the mistake of telling root that any file is
executable. */
static int
eaccess (path, mode)
char *path;
int mode;
{
struct stat st;
static int euid = -1;
if (test_stat (path, &st) < 0)
return (-1);
if (euid == -1)
euid = geteuid ();
if (euid == 0)
{
/* Root can read or write any file. */
if (mode != X_OK)
return (0);
/* Root can execute any file that has any one of the execute
bits set. */
if (st.st_mode & S_IXUGO)
return (0);
}
if (st.st_uid == euid) /* owner */
mode <<= 6;
else if (group_member (st.st_gid))
mode <<= 3;
if (st.st_mode & mode)
return (0);
return (-1);
}
/* Increment our position in the argument list. Check that we're not
past the end of the argument list. This check is supressed if the
argument is FALSE. Made a macro for efficiency. */
#define advance(f) \
do \
{ \
++pos; \
if ((f) && pos >= argc) \
beyond (); \
} \
while (0)
#if !defined (advance)
static int
advance (f)
int f;
{
++pos;
if (f && pos >= argc)
beyond ();
}
#endif /* advance */
#define unary_advance() \
do \
{ \
advance (1); \
++pos; \
} \
while (0)
/*
* beyond - call when we're beyond the end of the argument list (an
* error condition)
*/
static void
beyond ()
{
test_syntax_error ("argument expected\n", (char *)NULL);
}
/* Syntax error for when an integer argument was expected, but
something else was found. */
static void
integer_expected_error (pch)
char *pch;
{
test_syntax_error ("integer expression expected %s\n", pch);
}
/* Return non-zero if the characters pointed to by STRING constitute a
valid number. Stuff the converted number into RESULT if RESULT is
a non-null pointer to a long. */
static int
isint (string, result)
register char *string;
long *result;
{
int sign;
long value;
sign = 1;
value = 0;
if (result)
*result = 0;
/* Skip leading whitespace characters. */
while (whitespace (*string))
string++;
if (!*string)
return (0);
/* We allow leading `-' or `+'. */
if (*string == '-' || *string == '+')
{
if (!digit (string[1]))
return (0);
if (*string == '-')
sign = -1;
string++;
}
while (digit (*string))
{
if (result)
value = (value * 10) + digit_value (*string);
string++;
}
/* Skip trailing whitespace, if any. */
while (whitespace (*string))
string++;
/* Error if not at end of string. */
if (*string)
return (0);
if (result)
{
value *= sign;
*result = value;
}
return (1);
}
/* Find the modification time of FILE, and stuff it into AGE, a pointer
to a long. Return non-zero if successful, else zero. */
static int
age_of (filename, age)
char *filename;
long *age;
{
struct stat finfo;
if (test_stat (filename, &finfo) < 0)
return (0);
if (age)
*age = finfo.st_mtime;
return (1);
}
/*
* term - parse a term and return 1 or 0 depending on whether the term
* evaluates to true or false, respectively.
*
* term ::=
* '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
* '-'('L'|'x') filename
* '-t' [ int ]
* '-'('z'|'n') string
* string
* string ('!='|'=') string
* <int> '-'(eq|ne|le|lt|ge|gt) <int>
* file '-'(nt|ot|ef) file
* '(' <expr> ')'
* int ::=
* '-l' string
* positive and negative integers
*/
static int
term ()
{
int value;
if (pos >= argc)
beyond ();
/* Deal with leading "not"'s. */
if ('!' == argv[pos][0]