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