📄 test.c
字号:
/* test.c - GNU test program (ksb and mjb) *//* Modified to run with the GNU shell Apr 25, 1988 by bfox. *//* Copyright (C) 1987-2010 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.*//* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching binary operators. *//* #define PATTERN_MATCHING */#if defined (HAVE_CONFIG_H)# include <config.h>#endif#include <stdio.h>#include "bashtypes.h"#if !defined (HAVE_LIMITS_H)# include <sys/param.h>#endif#if defined (HAVE_UNISTD_H)# include <unistd.h>#endif#include <errno.h>#if !defined (errno)extern int errno;#endif /* !errno */#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)# include <sys/file.h>#endif /* !_POSIX_VERSION */#include "posixstat.h"#include "filecntl.h"#include "bashintl.h"#include "shell.h"#include "pathexp.h"#include "test.h"#include "builtins/common.h"#include <glob/strmatch.h>#if !defined (STRLEN)# define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)#endif#if !defined (STREQ)# define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)#endif /* !STREQ */#define STRCOLLEQ(a, b) ((a)[0] == (b)[0] && strcoll ((a), (b)) == 0)#if !defined (R_OK)#define R_OK 4#define W_OK 2#define X_OK 1#define F_OK 0#endif /* R_OK */#define EQ 0#define NE 1#define LT 2#define GT 3#define LE 4#define GE 5#define NT 0#define OT 1#define EF 2/* 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. Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */#define TRUE 1#define FALSE 0#define SHELL_BOOLEAN(value) (!(value))#define TEST_ERREXIT_STATUS 2static procenv_t test_exit_buf;static int test_error_return;#define test_exit(val) \ do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)extern int sh_stat __P((const char *, struct stat *));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 noeval;static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));static void beyond __P((void)) __attribute__((__noreturn__));static void integer_expected_error __P((char *)) __attribute__((__noreturn__));static int unary_operator __P((void));static int binary_operator __P((void));static int two_arguments __P((void));static int three_arguments __P((void));static int posixtest __P((void));static int expr __P((void));static int term __P((void));static int and __P((void));static int or __P((void));static int filecomp __P((char *, char *, int));static int arithcomp __P((char *, char *, int, int));static int patcomp __P((char *, char *, int));static voidtest_syntax_error (format, arg) char *format, *arg;{ builtin_error (format, arg); test_exit (TEST_ERREXIT_STATUS);}/* * beyond - call when we're beyond the end of the argument list (an * error condition) */static voidbeyond (){ test_syntax_error (_("argument expected"), (char *)NULL);}/* Syntax error for when an integer argument was expected, but something else was found. */static voidinteger_expected_error (pch) char *pch;{ test_syntax_error (_("%s: integer expression expected"), pch);}/* 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)#define unary_advance() do { advance (1); ++pos; } while (0)/* * expr: * or */static intexpr (){ if (pos >= argc) beyond (); return (FALSE ^ or ()); /* Same with this. */}/* * or: * and * and '-o' or */static intor (){ int value, v2; value = and (); if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2]) { advance (0); v2 = or (); return (value || v2); } return (value);}/* * and: * term * term '-a' and */static intand (){ int value, v2; value = term (); if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2]) { advance (0); v2 = and (); return (value && v2); } return (value);}/* * term - parse a term and return 1 or 0 depending on whether the term * evaluates to true or false, respectively. * * term ::= * '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename * '-'('G'|'L'|'O'|'S'|'N') filename * '-t' [int] * '-'('z'|'n') string * '-o' option * string * string ('!='|'='|'==') string * <int> '-'(eq|ne|le|lt|ge|gt) <int> * file '-'(nt|ot|ef) file * '(' <expr> ')' * int ::= * positive and negative integers */static intterm (){ int value; if (pos >= argc) beyond (); /* Deal with leading `not's. */ if (argv[pos][0] == '!' && argv[pos][1] == '\0') { value = 0; while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0') { advance (1); value = 1 - value; } return (value ? !term() : term()); } /* A paren-bracketed argument. */ if (argv[pos][0] == '(' && argv[pos][1] == '\0') /* ) */ { advance (1); value = expr (); if (argv[pos] == 0) /* ( */ test_syntax_error (_("`)' expected"), (char *)NULL); else if (argv[pos][0] != ')' || argv[pos][1]) /* ( */ test_syntax_error (_("`)' expected, found %s"), argv[pos]); advance (0); return (value); } /* are there enough arguments left that this could be dyadic? */ if ((pos + 3 <= argc) && test_binop (argv[pos + 1])) value = binary_operator (); /* Might be a switch type argument */ else if (argv[pos][0] == '-' && argv[pos][2] == '\0') { if (test_unop (argv[pos])) value = unary_operator (); else test_syntax_error (_("%s: unary operator expected"), argv[pos]); } else { value = argv[pos][0] != '\0'; advance (0); } return (value);}static intfilecomp (s, t, op) char *s, *t; int op;{ struct stat st1, st2; int r1, r2; if ((r1 = sh_stat (s, &st1)) < 0) { if (op == EF) return (FALSE); } if ((r2 = sh_stat (t, &st2)) < 0) { if (op == EF) return (FALSE); } switch (op) { case OT: return (r1 < r2 || (r2 == 0 && st1.st_mtime < st2.st_mtime)); case NT: return (r1 > r2 || (r1 == 0 && st1.st_mtime > st2.st_mtime)); case EF: return (same_file (s, t, &st1, &st2)); } return (FALSE);}static intarithcomp (s, t, op, flags) char *s, *t; int op, flags;{ intmax_t l, r; int expok; if (flags & TEST_ARITHEXP) { l = evalexp (s, &expok); if (expok == 0) return (FALSE); /* should probably longjmp here */ r = evalexp (t, &expok); if (expok == 0) return (FALSE); /* ditto */ } else { if (legal_number (s, &l) == 0) integer_expected_error (s); if (legal_number (t, &r) == 0) integer_expected_error (t); } switch (op) { case EQ: return (l == r); case NE: return (l != r); case LT: return (l < r); case GT: return (l > r); case LE: return (l <= r); case GE: return (l >= r); } return (FALSE);}static intpatcomp (string, pat, op) char *string, *pat; int op;{ int m; m = strmatch (pat, string, FNMATCH_EXTFLAG|FNMATCH_IGNCASE); return ((op == EQ) ? (m == 0) : (m != 0));}intbinary_test (op, arg1, arg2, flags) char *op, *arg1, *arg2; int flags;{ int patmatch; patmatch = (flags & TEST_PATMATCH); if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0'))) return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2)); else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0') { if (shell_compatibility_level > 40 && flags & TEST_LOCALE) return ((op[0] == '>') ? (strcoll (arg1, arg2) > 0) : (strcoll (arg1, arg2) < 0)); else return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0)); } else if (op[0] == '!' && op[1] == '=' && op[2] == '\0') return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0)); else if (op[2] == 't') { switch (op[1]) { case 'n': return (filecomp (arg1, arg2, NT)); /* -nt */ case 'o': return (filecomp (arg1, arg2, OT)); /* -ot */ case 'l': return (arithcomp (arg1, arg2, LT, flags)); /* -lt */ case 'g': return (arithcomp (arg1, arg2, GT, flags)); /* -gt */ } } else if (op[1] == 'e') { switch (op[2]) { case 'f': return (filecomp (arg1, arg2, EF)); /* -ef */ case 'q': return (arithcomp (arg1, arg2, EQ, flags)); /* -eq */ } } else if (op[2] == 'e') { switch (op[1]) { case 'n': return (arithcomp (arg1, arg2, NE, flags)); /* -ne */ case 'g': return (arithcomp (arg1, arg2, GE, flags)); /* -ge */ case 'l': return (arithcomp (arg1, arg2, LE, flags)); /* -le */ } } return (FALSE); /* should never get here */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -