1 1.45 dholland /* $NetBSD: test.c,v 1.45 2022/08/27 21:18:39 dholland Exp $ */ 2 1.15 cgd 3 1.13 jtc /* 4 1.13 jtc * test(1); version 7-like -- author Erik Baalbergen 5 1.13 jtc * modified by Eric Gisin to be used as built-in. 6 1.13 jtc * modified by Arnold Robbins to add SVR3 compatibility 7 1.13 jtc * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8 1.13 jtc * modified by J.T. Conklin for NetBSD. 9 1.1 glass * 10 1.13 jtc * This program is in the Public Domain. 11 1.1 glass */ 12 1.1 glass 13 1.17 christos #include <sys/cdefs.h> 14 1.1 glass #ifndef lint 15 1.45 dholland __RCSID("$NetBSD: test.c,v 1.45 2022/08/27 21:18:39 dholland Exp $"); 16 1.13 jtc #endif 17 1.1 glass 18 1.23 wiz #include <sys/stat.h> 19 1.1 glass #include <sys/types.h> 20 1.23 wiz 21 1.13 jtc #include <ctype.h> 22 1.23 wiz #include <err.h> 23 1.1 glass #include <errno.h> 24 1.33 christos #include <limits.h> 25 1.35 christos #include <locale.h> 26 1.13 jtc #include <stdio.h> 27 1.1 glass #include <stdlib.h> 28 1.1 glass #include <string.h> 29 1.23 wiz #include <unistd.h> 30 1.22 christos #include <stdarg.h> 31 1.1 glass 32 1.13 jtc /* test(1) accepts the following grammar: 33 1.13 jtc oexpr ::= aexpr | aexpr "-o" oexpr ; 34 1.13 jtc aexpr ::= nexpr | nexpr "-a" aexpr ; 35 1.14 cgd nexpr ::= primary | "!" primary 36 1.13 jtc primary ::= unary-operator operand 37 1.13 jtc | operand binary-operator operand 38 1.13 jtc | operand 39 1.13 jtc | "(" oexpr ")" 40 1.13 jtc ; 41 1.13 jtc unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 42 1.13 jtc "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 43 1.13 jtc 44 1.30 hubertf binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 45 1.30 hubertf "-nt"|"-ot"|"-ef"; 46 1.13 jtc operand ::= <any legal UNIX file name> 47 1.13 jtc */ 48 1.13 jtc 49 1.13 jtc enum token { 50 1.13 jtc EOI, 51 1.13 jtc FILRD, 52 1.13 jtc FILWR, 53 1.13 jtc FILEX, 54 1.13 jtc FILEXIST, 55 1.13 jtc FILREG, 56 1.13 jtc FILDIR, 57 1.13 jtc FILCDEV, 58 1.13 jtc FILBDEV, 59 1.13 jtc FILFIFO, 60 1.13 jtc FILSOCK, 61 1.13 jtc FILSYM, 62 1.13 jtc FILGZ, 63 1.13 jtc FILTT, 64 1.13 jtc FILSUID, 65 1.13 jtc FILSGID, 66 1.13 jtc FILSTCK, 67 1.13 jtc FILNT, 68 1.13 jtc FILOT, 69 1.13 jtc FILEQ, 70 1.13 jtc FILUID, 71 1.13 jtc FILGID, 72 1.13 jtc STREZ, 73 1.13 jtc STRNZ, 74 1.13 jtc STREQ, 75 1.13 jtc STRNE, 76 1.13 jtc STRLT, 77 1.13 jtc STRGT, 78 1.13 jtc INTEQ, 79 1.13 jtc INTNE, 80 1.13 jtc INTGE, 81 1.13 jtc INTGT, 82 1.13 jtc INTLE, 83 1.13 jtc INTLT, 84 1.13 jtc UNOT, 85 1.13 jtc BAND, 86 1.13 jtc BOR, 87 1.13 jtc LPAREN, 88 1.13 jtc RPAREN, 89 1.13 jtc OPERAND 90 1.13 jtc }; 91 1.1 glass 92 1.13 jtc enum token_types { 93 1.13 jtc UNOP, 94 1.43 kre BINOP 95 1.43 kre #ifndef SMALL 96 1.43 kre , 97 1.13 jtc BUNOP, 98 1.13 jtc BBINOP, 99 1.13 jtc PAREN 100 1.43 kre #endif 101 1.1 glass }; 102 1.1 glass 103 1.31 christos struct t_op { 104 1.13 jtc const char *op_text; 105 1.13 jtc short op_num, op_type; 106 1.31 christos }; 107 1.31 christos 108 1.31 christos static const struct t_op cop[] = { 109 1.43 kre #ifndef SMALL 110 1.13 jtc {"!", UNOT, BUNOP}, 111 1.13 jtc {"(", LPAREN, PAREN}, 112 1.13 jtc {")", RPAREN, PAREN}, 113 1.43 kre #endif 114 1.31 christos {"<", STRLT, BINOP}, 115 1.31 christos {"=", STREQ, BINOP}, 116 1.31 christos {">", STRGT, BINOP}, 117 1.31 christos }; 118 1.31 christos 119 1.31 christos static const struct t_op cop2[] = { 120 1.31 christos {"!=", STRNE, BINOP}, 121 1.31 christos }; 122 1.31 christos 123 1.31 christos static const struct t_op mop3[] = { 124 1.31 christos {"ef", FILEQ, BINOP}, 125 1.31 christos {"eq", INTEQ, BINOP}, 126 1.31 christos {"ge", INTGE, BINOP}, 127 1.31 christos {"gt", INTGT, BINOP}, 128 1.31 christos {"le", INTLE, BINOP}, 129 1.31 christos {"lt", INTLT, BINOP}, 130 1.31 christos {"ne", INTNE, BINOP}, 131 1.31 christos {"nt", FILNT, BINOP}, 132 1.31 christos {"ot", FILOT, BINOP}, 133 1.31 christos }; 134 1.31 christos 135 1.31 christos static const struct t_op mop2[] = { 136 1.31 christos {"G", FILGID, UNOP}, 137 1.31 christos {"L", FILSYM, UNOP}, 138 1.31 christos {"O", FILUID, UNOP}, 139 1.31 christos {"S", FILSOCK,UNOP}, 140 1.43 kre #ifndef SMALL 141 1.31 christos {"a", BAND, BBINOP}, 142 1.43 kre #endif 143 1.31 christos {"b", FILBDEV,UNOP}, 144 1.31 christos {"c", FILCDEV,UNOP}, 145 1.31 christos {"d", FILDIR, UNOP}, 146 1.31 christos {"e", FILEXIST,UNOP}, 147 1.31 christos {"f", FILREG, UNOP}, 148 1.31 christos {"g", FILSGID,UNOP}, 149 1.31 christos {"h", FILSYM, UNOP}, /* for backwards compat */ 150 1.31 christos {"k", FILSTCK,UNOP}, 151 1.31 christos {"n", STRNZ, UNOP}, 152 1.43 kre #ifndef SMALL 153 1.31 christos {"o", BOR, BBINOP}, 154 1.43 kre #endif 155 1.31 christos {"p", FILFIFO,UNOP}, 156 1.31 christos {"r", FILRD, UNOP}, 157 1.31 christos {"s", FILGZ, UNOP}, 158 1.31 christos {"t", FILTT, UNOP}, 159 1.31 christos {"u", FILSUID,UNOP}, 160 1.31 christos {"w", FILWR, UNOP}, 161 1.31 christos {"x", FILEX, UNOP}, 162 1.31 christos {"z", STREZ, UNOP}, 163 1.1 glass }; 164 1.1 glass 165 1.43 kre #ifndef SMALL 166 1.22 christos static char **t_wp; 167 1.22 christos static struct t_op const *t_wp_op; 168 1.43 kre #endif 169 1.1 glass 170 1.43 kre #ifndef SMALL 171 1.38 joerg __dead static void syntax(const char *, const char *); 172 1.23 wiz static int oexpr(enum token); 173 1.23 wiz static int aexpr(enum token); 174 1.23 wiz static int nexpr(enum token); 175 1.23 wiz static int primary(enum token); 176 1.23 wiz static int binop(void); 177 1.43 kre static enum token t_lex(char *); 178 1.43 kre static int isoperand(void); 179 1.43 kre #endif 180 1.43 kre static struct t_op const *findop(const char *); 181 1.42 kre static int perform_unop(enum token, const char *); 182 1.42 kre static int perform_binop(enum token, const char *, const char *); 183 1.33 christos static int test_access(struct stat *, mode_t); 184 1.42 kre static int filstat(const char *, enum token); 185 1.36 christos static long long getn(const char *); 186 1.23 wiz static int newerf(const char *, const char *); 187 1.23 wiz static int olderf(const char *, const char *); 188 1.23 wiz static int equalf(const char *, const char *); 189 1.17 christos 190 1.42 kre static int one_arg(const char *); 191 1.42 kre static int two_arg(const char *, const char *); 192 1.42 kre static int three_arg(const char *, const char *, const char *); 193 1.42 kre static int four_arg(const char *, const char *, const char *, const char *); 194 1.42 kre 195 1.22 christos #if defined(SHELL) 196 1.39 joerg extern void error(const char *, ...) __dead __printflike(1, 2); 197 1.33 christos extern void *ckmalloc(size_t); 198 1.22 christos #else 199 1.39 joerg static void error(const char *, ...) __dead __printflike(1, 2); 200 1.22 christos 201 1.22 christos static void 202 1.22 christos error(const char *msg, ...) 203 1.22 christos { 204 1.22 christos va_list ap; 205 1.22 christos 206 1.22 christos va_start(ap, msg); 207 1.22 christos verrx(2, msg, ap); 208 1.22 christos /*NOTREACHED*/ 209 1.22 christos va_end(ap); 210 1.22 christos } 211 1.33 christos 212 1.33 christos static void *ckmalloc(size_t); 213 1.33 christos static void * 214 1.33 christos ckmalloc(size_t nbytes) 215 1.33 christos { 216 1.33 christos void *p = malloc(nbytes); 217 1.33 christos 218 1.33 christos if (!p) 219 1.33 christos error("Not enough memory!"); 220 1.33 christos return p; 221 1.33 christos } 222 1.22 christos #endif 223 1.22 christos 224 1.22 christos #ifdef SHELL 225 1.23 wiz int testcmd(int, char **); 226 1.22 christos 227 1.22 christos int 228 1.23 wiz testcmd(int argc, char **argv) 229 1.22 christos #else 230 1.1 glass int 231 1.24 wiz main(int argc, char *argv[]) 232 1.22 christos #endif 233 1.1 glass { 234 1.24 wiz int res; 235 1.28 christos const char *argv0; 236 1.22 christos 237 1.28 christos #ifdef SHELL 238 1.28 christos argv0 = argv[0]; 239 1.28 christos #else 240 1.24 wiz setprogname(argv[0]); 241 1.35 christos (void)setlocale(LC_ALL, ""); 242 1.28 christos argv0 = getprogname(); 243 1.28 christos #endif 244 1.28 christos if (strcmp(argv0, "[") == 0) { 245 1.1 glass if (strcmp(argv[--argc], "]")) 246 1.22 christos error("missing ]"); 247 1.1 glass argv[argc] = NULL; 248 1.1 glass } 249 1.1 glass 250 1.42 kre /* 251 1.42 kre * POSIX defines operations of test for up to 4 args 252 1.42 kre * (depending upon what the args are in some cases) 253 1.42 kre * 254 1.42 kre * arg count does not include the command name, (but argc does) 255 1.42 kre * nor the closing ']' when the command was '[' (removed above) 256 1.42 kre * 257 1.42 kre * None of the following allow -a or -o as an operator (those 258 1.42 kre * only apply in the evaluation of unspeicified expressions) 259 1.42 kre * 260 1.42 kre * Note that the xxx_arg() functions return "shell" true/false 261 1.42 kre * (0 == true, 1 == false) or -1 for "unspecified case" 262 1.42 kre * 263 1.42 kre * Other functions return C true/false (1 == true, 0 == false) 264 1.42 kre * 265 1.42 kre * Hence we simply return the result from xxx_arg(), but 266 1.42 kre * invert the result of oexpr() below before returning it. 267 1.42 kre */ 268 1.42 kre switch (argc - 1) { 269 1.42 kre case -1: /* impossible, but never mind */ 270 1.42 kre case 0: /* test $a where a='' false */ 271 1.22 christos return 1; 272 1.22 christos 273 1.42 kre case 1: /* test "$a" */ 274 1.42 kre return one_arg(argv[1]); /* always works */ 275 1.42 kre 276 1.42 kre case 2: /* test op "$a" */ 277 1.42 kre res = two_arg(argv[1], argv[2]); 278 1.42 kre if (res >= 0) 279 1.42 kre return res; 280 1.42 kre break; 281 1.42 kre 282 1.42 kre case 3: /* test "$a" op "$b" or test ! op "$a" */ 283 1.42 kre res = three_arg(argv[1], argv[2], argv[3]); 284 1.42 kre if (res >= 0) 285 1.42 kre return res; 286 1.42 kre break; 287 1.42 kre 288 1.42 kre case 4: /* test ! "$a" op "$b" or test ( op "$a" ) */ 289 1.42 kre res = four_arg(argv[1], argv[2], argv[3], argv[4]); 290 1.42 kre if (res >= 0) 291 1.42 kre return res; 292 1.42 kre break; 293 1.42 kre 294 1.42 kre default: 295 1.42 kre break; 296 1.42 kre } 297 1.42 kre 298 1.42 kre /* 299 1.42 kre * All other cases produce unspecified results 300 1.42 kre * (including cases above with small arg counts where the 301 1.42 kre * args are not what was expected to be seen) 302 1.42 kre * 303 1.42 kre * We fall back to the old method, of attempting to parse 304 1.42 kre * the expr (highly ambiguous as there is no distinction between 305 1.42 kre * operators and operands that happen to look like operators) 306 1.42 kre */ 307 1.42 kre 308 1.43 kre #ifdef SMALL 309 1.45 dholland error("unsupported expression when built with -DSMALL"); 310 1.43 kre #else 311 1.43 kre 312 1.14 cgd t_wp = &argv[1]; 313 1.13 jtc res = !oexpr(t_lex(*t_wp)); 314 1.13 jtc 315 1.13 jtc if (*t_wp != NULL && *++t_wp != NULL) 316 1.21 kleink syntax(*t_wp, "unexpected operator"); 317 1.13 jtc 318 1.13 jtc return res; 319 1.43 kre #endif 320 1.13 jtc } 321 1.13 jtc 322 1.43 kre #ifndef SMALL 323 1.13 jtc static void 324 1.23 wiz syntax(const char *op, const char *msg) 325 1.13 jtc { 326 1.26 simonb 327 1.13 jtc if (op && *op) 328 1.22 christos error("%s: %s", op, msg); 329 1.13 jtc else 330 1.22 christos error("%s", msg); 331 1.42 kre } 332 1.43 kre #endif 333 1.42 kre 334 1.42 kre static int 335 1.42 kre one_arg(const char *arg) 336 1.42 kre { 337 1.42 kre /* 338 1.42 kre * True (exit 0, so false...) if arg is not a null string 339 1.42 kre * False (so exit 1, so true) if it is. 340 1.42 kre */ 341 1.42 kre return *arg == '\0'; 342 1.42 kre } 343 1.42 kre 344 1.42 kre static int 345 1.42 kre two_arg(const char *a1, const char *a2) 346 1.42 kre { 347 1.42 kre static struct t_op const *op; 348 1.42 kre 349 1.42 kre if (a1[0] == '!' && a1[1] == 0) 350 1.42 kre return !one_arg(a2); 351 1.42 kre 352 1.42 kre op = findop(a1); 353 1.42 kre if (op != NULL && op->op_type == UNOP) 354 1.42 kre return !perform_unop(op->op_num, a2); 355 1.42 kre 356 1.43 kre #ifndef TINY 357 1.42 kre /* 358 1.42 kre * an extension, but as we've entered the realm of the unspecified 359 1.42 kre * we're allowed... test ( $a ) where a='' 360 1.42 kre */ 361 1.42 kre if (a1[0] == '(' && a2[0] == ')' && (a1[1] | a2[1]) == 0) 362 1.42 kre return 1; 363 1.43 kre #endif 364 1.42 kre 365 1.42 kre return -1; 366 1.42 kre } 367 1.42 kre 368 1.42 kre static int 369 1.42 kre three_arg(const char *a1, const char *a2, const char *a3) 370 1.42 kre { 371 1.42 kre static struct t_op const *op; 372 1.42 kre int res; 373 1.42 kre 374 1.42 kre op = findop(a2); 375 1.42 kre if (op != NULL && op->op_type == BINOP) 376 1.42 kre return !perform_binop(op->op_num, a1, a3); 377 1.42 kre 378 1.42 kre if (a1[1] != '\0') 379 1.42 kre return -1; 380 1.42 kre 381 1.42 kre if (a1[0] == '!') { 382 1.42 kre res = two_arg(a2, a3); 383 1.42 kre if (res >= 0) 384 1.42 kre res = !res; 385 1.42 kre return res; 386 1.42 kre } 387 1.42 kre 388 1.43 kre #ifndef TINY 389 1.42 kre if (a1[0] == '(' && a3[0] == ')' && a3[1] == '\0') 390 1.42 kre return one_arg(a2); 391 1.43 kre #endif 392 1.42 kre 393 1.42 kre return -1; 394 1.42 kre } 395 1.42 kre 396 1.42 kre static int 397 1.42 kre four_arg(const char *a1, const char *a2, const char *a3, const char *a4) 398 1.42 kre { 399 1.42 kre int res; 400 1.42 kre 401 1.42 kre if (a1[1] != '\0') 402 1.42 kre return -1; 403 1.42 kre 404 1.42 kre if (a1[0] == '!') { 405 1.42 kre res = three_arg(a2, a3, a4); 406 1.42 kre if (res >= 0) 407 1.42 kre res = !res; 408 1.42 kre return res; 409 1.42 kre } 410 1.42 kre 411 1.43 kre #ifndef TINY 412 1.42 kre if (a1[0] == '(' && a4[0] == ')' && a4[1] == '\0') 413 1.42 kre return two_arg(a2, a3); 414 1.43 kre #endif 415 1.42 kre 416 1.42 kre return -1; 417 1.13 jtc } 418 1.13 jtc 419 1.43 kre #ifndef SMALL 420 1.13 jtc static int 421 1.23 wiz oexpr(enum token n) 422 1.13 jtc { 423 1.13 jtc int res; 424 1.13 jtc 425 1.13 jtc res = aexpr(n); 426 1.32 christos if (*t_wp == NULL) 427 1.32 christos return res; 428 1.13 jtc if (t_lex(*++t_wp) == BOR) 429 1.13 jtc return oexpr(t_lex(*++t_wp)) || res; 430 1.13 jtc t_wp--; 431 1.13 jtc return res; 432 1.13 jtc } 433 1.13 jtc 434 1.13 jtc static int 435 1.23 wiz aexpr(enum token n) 436 1.13 jtc { 437 1.13 jtc int res; 438 1.13 jtc 439 1.13 jtc res = nexpr(n); 440 1.32 christos if (*t_wp == NULL) 441 1.32 christos return res; 442 1.13 jtc if (t_lex(*++t_wp) == BAND) 443 1.13 jtc return aexpr(t_lex(*++t_wp)) && res; 444 1.13 jtc t_wp--; 445 1.13 jtc return res; 446 1.13 jtc } 447 1.13 jtc 448 1.13 jtc static int 449 1.23 wiz nexpr(enum token n) 450 1.13 jtc { 451 1.26 simonb 452 1.13 jtc if (n == UNOT) 453 1.13 jtc return !nexpr(t_lex(*++t_wp)); 454 1.13 jtc return primary(n); 455 1.13 jtc } 456 1.13 jtc 457 1.13 jtc static int 458 1.23 wiz primary(enum token n) 459 1.13 jtc { 460 1.21 kleink enum token nn; 461 1.13 jtc int res; 462 1.13 jtc 463 1.13 jtc if (n == EOI) 464 1.21 kleink return 0; /* missing expression */ 465 1.13 jtc if (n == LPAREN) { 466 1.21 kleink if ((nn = t_lex(*++t_wp)) == RPAREN) 467 1.21 kleink return 0; /* missing expression */ 468 1.21 kleink res = oexpr(nn); 469 1.13 jtc if (t_lex(*++t_wp) != RPAREN) 470 1.13 jtc syntax(NULL, "closing paren expected"); 471 1.13 jtc return res; 472 1.1 glass } 473 1.13 jtc if (t_wp_op && t_wp_op->op_type == UNOP) { 474 1.13 jtc /* unary expression */ 475 1.13 jtc if (*++t_wp == NULL) 476 1.13 jtc syntax(t_wp_op->op_text, "argument expected"); 477 1.42 kre return perform_unop(n, *t_wp); 478 1.13 jtc } 479 1.14 cgd 480 1.14 cgd if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 481 1.14 cgd return binop(); 482 1.14 cgd } 483 1.14 cgd 484 1.14 cgd return strlen(*t_wp) > 0; 485 1.14 cgd } 486 1.43 kre #endif /* !SMALL */ 487 1.14 cgd 488 1.14 cgd static int 489 1.42 kre perform_unop(enum token n, const char *opnd) 490 1.42 kre { 491 1.42 kre switch (n) { 492 1.42 kre case STREZ: 493 1.42 kre return strlen(opnd) == 0; 494 1.42 kre case STRNZ: 495 1.42 kre return strlen(opnd) != 0; 496 1.42 kre case FILTT: 497 1.42 kre return isatty((int)getn(opnd)); 498 1.42 kre default: 499 1.42 kre return filstat(opnd, n); 500 1.42 kre } 501 1.42 kre } 502 1.42 kre 503 1.43 kre #ifndef SMALL 504 1.42 kre static int 505 1.23 wiz binop(void) 506 1.14 cgd { 507 1.16 tls const char *opnd1, *opnd2; 508 1.14 cgd struct t_op const *op; 509 1.14 cgd 510 1.13 jtc opnd1 = *t_wp; 511 1.13 jtc (void) t_lex(*++t_wp); 512 1.14 cgd op = t_wp_op; 513 1.13 jtc 514 1.26 simonb if ((opnd2 = *++t_wp) == NULL) 515 1.14 cgd syntax(op->op_text, "argument expected"); 516 1.13 jtc 517 1.42 kre return perform_binop(op->op_num, opnd1, opnd2); 518 1.42 kre } 519 1.43 kre #endif 520 1.42 kre 521 1.42 kre static int 522 1.42 kre perform_binop(enum token op_num, const char *opnd1, const char *opnd2) 523 1.42 kre { 524 1.42 kre switch (op_num) { 525 1.14 cgd case STREQ: 526 1.14 cgd return strcmp(opnd1, opnd2) == 0; 527 1.14 cgd case STRNE: 528 1.14 cgd return strcmp(opnd1, opnd2) != 0; 529 1.14 cgd case STRLT: 530 1.14 cgd return strcmp(opnd1, opnd2) < 0; 531 1.14 cgd case STRGT: 532 1.14 cgd return strcmp(opnd1, opnd2) > 0; 533 1.14 cgd case INTEQ: 534 1.14 cgd return getn(opnd1) == getn(opnd2); 535 1.14 cgd case INTNE: 536 1.14 cgd return getn(opnd1) != getn(opnd2); 537 1.14 cgd case INTGE: 538 1.14 cgd return getn(opnd1) >= getn(opnd2); 539 1.14 cgd case INTGT: 540 1.14 cgd return getn(opnd1) > getn(opnd2); 541 1.14 cgd case INTLE: 542 1.14 cgd return getn(opnd1) <= getn(opnd2); 543 1.14 cgd case INTLT: 544 1.14 cgd return getn(opnd1) < getn(opnd2); 545 1.14 cgd case FILNT: 546 1.26 simonb return newerf(opnd1, opnd2); 547 1.14 cgd case FILOT: 548 1.26 simonb return olderf(opnd1, opnd2); 549 1.14 cgd case FILEQ: 550 1.26 simonb return equalf(opnd1, opnd2); 551 1.17 christos default: 552 1.19 mycroft abort(); 553 1.17 christos /* NOTREACHED */ 554 1.1 glass } 555 1.1 glass } 556 1.1 glass 557 1.33 christos /* 558 1.33 christos * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits, 559 1.33 christos * not use access(): 560 1.33 christos * 561 1.33 christos * True shall indicate only that the write flag is on. The file is not 562 1.33 christos * writable on a read-only file system even if this test indicates true. 563 1.33 christos * 564 1.33 christos * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only: 565 1.33 christos * 566 1.33 christos * True shall indicate that permission to read from file will be granted, 567 1.33 christos * as defined in "File Read, Write, and Creation". 568 1.33 christos * 569 1.33 christos * and that section says: 570 1.33 christos * 571 1.33 christos * When a file is to be read or written, the file shall be opened with an 572 1.33 christos * access mode corresponding to the operation to be performed. If file 573 1.33 christos * access permissions deny access, the requested operation shall fail. 574 1.33 christos * 575 1.33 christos * and of course access permissions are described as one might expect: 576 1.33 christos * 577 1.33 christos * * If a process has the appropriate privilege: 578 1.33 christos * 579 1.33 christos * * If read, write, or directory search permission is requested, 580 1.33 christos * access shall be granted. 581 1.33 christos * 582 1.33 christos * * If execute permission is requested, access shall be granted if 583 1.33 christos * execute permission is granted to at least one user by the file 584 1.33 christos * permission bits or by an alternate access control mechanism; 585 1.33 christos * otherwise, access shall be denied. 586 1.33 christos * 587 1.33 christos * * Otherwise: 588 1.33 christos * 589 1.33 christos * * The file permission bits of a file contain read, write, and 590 1.33 christos * execute/search permissions for the file owner class, file group 591 1.33 christos * class, and file other class. 592 1.33 christos * 593 1.33 christos * * Access shall be granted if an alternate access control mechanism 594 1.33 christos * is not enabled and the requested access permission bit is set for 595 1.33 christos * the class (file owner class, file group class, or file other class) 596 1.33 christos * to which the process belongs, or if an alternate access control 597 1.33 christos * mechanism is enabled and it allows the requested access; otherwise, 598 1.33 christos * access shall be denied. 599 1.33 christos * 600 1.33 christos * and when I first read this I thought: surely we can't go about using 601 1.33 christos * open(O_WRONLY) to try this test! However the POSIX 1003.1-2001 Rationale 602 1.33 christos * section for test does in fact say: 603 1.33 christos * 604 1.33 christos * On historical BSD systems, test -w directory always returned false 605 1.33 christos * because test tried to open the directory for writing, which always 606 1.33 christos * fails. 607 1.33 christos * 608 1.33 christos * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX 609 1.33 christos * System III, and thus presumably also for BSD up to and including 4.3. 610 1.33 christos * 611 1.33 christos * Secondly I remembered why using open() and/or access() are bogus. They 612 1.33 christos * don't work right for detecting read and write permissions bits when called 613 1.33 christos * by root. 614 1.33 christos * 615 1.33 christos * Interestingly the 'test' in 4.4BSD was closer to correct (as per 616 1.33 christos * 1003.2-1992) and it was implemented efficiently with stat() instead of 617 1.33 christos * open(). 618 1.33 christos * 619 1.33 christos * This was apparently broken in NetBSD around about 1994/06/30 when the old 620 1.33 christos * 4.4BSD implementation was replaced with a (arguably much better coded) 621 1.33 christos * implementation derived from pdksh. 622 1.33 christos * 623 1.33 christos * Note that modern pdksh is yet different again, but still not correct, at 624 1.33 christos * least not w.r.t. 1003.2-1992. 625 1.33 christos * 626 1.33 christos * As I think more about it and read more of the related IEEE docs I don't like 627 1.33 christos * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all. I very 628 1.33 christos * much prefer the original wording in 1003.2-1992. It is much more useful, 629 1.33 christos * and so that's what I've implemented. 630 1.33 christos * 631 1.33 christos * (Note that a strictly conforming implementation of 1003.1-2001 is in fact 632 1.33 christos * totally useless for the case in question since its 'test -w' and 'test -r' 633 1.33 christos * can never fail for root for any existing files, i.e. files for which 'test 634 1.33 christos * -e' succeeds.) 635 1.33 christos * 636 1.33 christos * The rationale for 1003.1-2001 suggests that the wording was "clarified" in 637 1.33 christos * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999), 638 1.33 christos * which is the latest copy I have, does carry the same suggested wording as is 639 1.33 christos * in 1003.1-2001, with its rationale saying: 640 1.33 christos * 641 1.33 christos * This change is a clarification and is the result of interpretation 642 1.33 christos * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992. 643 1.33 christos * 644 1.33 christos * That interpretation can be found here: 645 1.33 christos * 646 1.33 christos * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html 647 1.33 christos * 648 1.33 christos * Not terribly helpful, unfortunately. I wonder who that fence sitter was. 649 1.33 christos * 650 1.33 christos * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the 651 1.33 christos * PASC interpretation and appear to be gone against at least one widely used 652 1.33 christos * implementation (namely 4.4BSD). The problem is that for file access by root 653 1.33 christos * this means that if test '-r' and '-w' are to behave as if open() were called 654 1.33 christos * then there's no way for a shell script running as root to check if a file 655 1.33 christos * has certain access bits set other than by the grotty means of interpreting 656 1.33 christos * the output of 'ls -l'. This was widely considered to be a bug in V7's 657 1.33 christos * "test" and is, I believe, one of the reasons why direct use of access() was 658 1.33 christos * avoided in some more recent implementations! 659 1.33 christos * 660 1.33 christos * I have always interpreted '-r' to match '-w' and '-x' as per the original 661 1.33 christos * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much 662 1.33 christos * too far the wrong way without any valid rationale and that it's best if we 663 1.33 christos * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of 664 1.44 msaitoh * open() since we already know very well how it will work -- existence of the 665 1.33 christos * file is all that matters to open() for root. 666 1.33 christos * 667 1.33 christos * Unfortunately the SVID is no help at all (which is, I guess, partly why 668 1.33 christos * we're in this mess in the first place :-). 669 1.33 christos * 670 1.33 christos * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use 671 1.33 christos * access(name, 2) even though it also goes to much greater lengths for '-x' 672 1.33 christos * matching the 1003.2-1992 definition (which is no doubt where that definition 673 1.33 christos * came from). 674 1.33 christos * 675 1.33 christos * The ksh93 implementation uses access() for '-r' and '-w' if 676 1.33 christos * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root. 677 1.33 christos * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b). 678 1.33 christos */ 679 1.33 christos static int 680 1.33 christos test_access(struct stat *sp, mode_t stmode) 681 1.33 christos { 682 1.33 christos gid_t *groups; 683 1.33 christos register int n; 684 1.33 christos uid_t euid; 685 1.33 christos int maxgroups; 686 1.33 christos 687 1.33 christos /* 688 1.33 christos * I suppose we could use access() if not running as root and if we are 689 1.33 christos * running with ((euid == uid) && (egid == gid)), but we've already 690 1.33 christos * done the stat() so we might as well just test the permissions 691 1.33 christos * directly instead of asking the kernel to do it.... 692 1.33 christos */ 693 1.33 christos euid = geteuid(); 694 1.33 christos if (euid == 0) /* any bit is good enough */ 695 1.33 christos stmode = (stmode << 6) | (stmode << 3) | stmode; 696 1.33 christos else if (sp->st_uid == euid) 697 1.33 christos stmode <<= 6; 698 1.33 christos else if (sp->st_gid == getegid()) 699 1.33 christos stmode <<= 3; 700 1.33 christos else { 701 1.33 christos /* XXX stolen almost verbatim from ksh93.... */ 702 1.33 christos /* on some systems you can be in several groups */ 703 1.33 christos if ((maxgroups = getgroups(0, NULL)) <= 0) 704 1.33 christos maxgroups = NGROUPS_MAX; /* pre-POSIX system? */ 705 1.33 christos groups = ckmalloc((maxgroups + 1) * sizeof(gid_t)); 706 1.33 christos n = getgroups(maxgroups, groups); 707 1.33 christos while (--n >= 0) { 708 1.33 christos if (groups[n] == sp->st_gid) { 709 1.33 christos stmode <<= 3; 710 1.33 christos break; 711 1.33 christos } 712 1.33 christos } 713 1.33 christos free(groups); 714 1.33 christos } 715 1.33 christos 716 1.33 christos return sp->st_mode & stmode; 717 1.33 christos } 718 1.33 christos 719 1.1 glass static int 720 1.42 kre filstat(const char *nm, enum token mode) 721 1.1 glass { 722 1.13 jtc struct stat s; 723 1.13 jtc 724 1.18 mycroft if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 725 1.13 jtc return 0; 726 1.1 glass 727 1.13 jtc switch (mode) { 728 1.13 jtc case FILRD: 729 1.33 christos return test_access(&s, S_IROTH); 730 1.13 jtc case FILWR: 731 1.33 christos return test_access(&s, S_IWOTH); 732 1.13 jtc case FILEX: 733 1.33 christos return test_access(&s, S_IXOTH); 734 1.13 jtc case FILEXIST: 735 1.33 christos return 1; /* the successful lstat()/stat() is good enough */ 736 1.13 jtc case FILREG: 737 1.18 mycroft return S_ISREG(s.st_mode); 738 1.13 jtc case FILDIR: 739 1.18 mycroft return S_ISDIR(s.st_mode); 740 1.13 jtc case FILCDEV: 741 1.18 mycroft return S_ISCHR(s.st_mode); 742 1.13 jtc case FILBDEV: 743 1.18 mycroft return S_ISBLK(s.st_mode); 744 1.13 jtc case FILFIFO: 745 1.18 mycroft return S_ISFIFO(s.st_mode); 746 1.13 jtc case FILSOCK: 747 1.18 mycroft return S_ISSOCK(s.st_mode); 748 1.18 mycroft case FILSYM: 749 1.18 mycroft return S_ISLNK(s.st_mode); 750 1.13 jtc case FILSUID: 751 1.18 mycroft return (s.st_mode & S_ISUID) != 0; 752 1.13 jtc case FILSGID: 753 1.18 mycroft return (s.st_mode & S_ISGID) != 0; 754 1.13 jtc case FILSTCK: 755 1.18 mycroft return (s.st_mode & S_ISVTX) != 0; 756 1.13 jtc case FILGZ: 757 1.18 mycroft return s.st_size > (off_t)0; 758 1.13 jtc case FILUID: 759 1.13 jtc return s.st_uid == geteuid(); 760 1.13 jtc case FILGID: 761 1.13 jtc return s.st_gid == getegid(); 762 1.13 jtc default: 763 1.13 jtc return 1; 764 1.13 jtc } 765 1.1 glass } 766 1.1 glass 767 1.31 christos #define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text 768 1.31 christos 769 1.31 christos static int 770 1.31 christos compare1(const void *va, const void *vb) 771 1.31 christos { 772 1.31 christos const unsigned char *a = va; 773 1.31 christos const unsigned char *b = VTOC(vb); 774 1.31 christos 775 1.31 christos return a[0] - b[0]; 776 1.31 christos } 777 1.31 christos 778 1.31 christos static int 779 1.31 christos compare2(const void *va, const void *vb) 780 1.31 christos { 781 1.31 christos const unsigned char *a = va; 782 1.31 christos const unsigned char *b = VTOC(vb); 783 1.31 christos int z = a[0] - b[0]; 784 1.31 christos 785 1.31 christos return z ? z : (a[1] - b[1]); 786 1.31 christos } 787 1.31 christos 788 1.31 christos static struct t_op const * 789 1.31 christos findop(const char *s) 790 1.31 christos { 791 1.31 christos if (s[0] == '-') { 792 1.31 christos if (s[1] == '\0') 793 1.31 christos return NULL; 794 1.31 christos if (s[2] == '\0') 795 1.31 christos return bsearch(s + 1, mop2, __arraycount(mop2), 796 1.31 christos sizeof(*mop2), compare1); 797 1.31 christos else if (s[3] != '\0') 798 1.31 christos return NULL; 799 1.31 christos else 800 1.31 christos return bsearch(s + 1, mop3, __arraycount(mop3), 801 1.31 christos sizeof(*mop3), compare2); 802 1.31 christos } else { 803 1.31 christos if (s[1] == '\0') 804 1.31 christos return bsearch(s, cop, __arraycount(cop), sizeof(*cop), 805 1.31 christos compare1); 806 1.31 christos else if (strcmp(s, cop2[0].op_text) == 0) 807 1.31 christos return cop2; 808 1.31 christos else 809 1.31 christos return NULL; 810 1.31 christos } 811 1.31 christos } 812 1.31 christos 813 1.43 kre #ifndef SMALL 814 1.13 jtc static enum token 815 1.23 wiz t_lex(char *s) 816 1.1 glass { 817 1.24 wiz struct t_op const *op; 818 1.24 wiz 819 1.31 christos if (s == NULL) { 820 1.26 simonb t_wp_op = NULL; 821 1.13 jtc return EOI; 822 1.13 jtc } 823 1.31 christos 824 1.31 christos if ((op = findop(s)) != NULL) { 825 1.31 christos if (!((op->op_type == UNOP && isoperand()) || 826 1.31 christos (op->op_num == LPAREN && *(t_wp+1) == 0))) { 827 1.13 jtc t_wp_op = op; 828 1.13 jtc return op->op_num; 829 1.13 jtc } 830 1.13 jtc } 831 1.26 simonb t_wp_op = NULL; 832 1.13 jtc return OPERAND; 833 1.21 kleink } 834 1.21 kleink 835 1.21 kleink static int 836 1.23 wiz isoperand(void) 837 1.21 kleink { 838 1.24 wiz struct t_op const *op; 839 1.24 wiz char *s, *t; 840 1.21 kleink 841 1.21 kleink if ((s = *(t_wp+1)) == 0) 842 1.21 kleink return 1; 843 1.21 kleink if ((t = *(t_wp+2)) == 0) 844 1.21 kleink return 0; 845 1.31 christos if ((op = findop(s)) != NULL) 846 1.31 christos return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0'); 847 1.21 kleink return 0; 848 1.1 glass } 849 1.43 kre #endif 850 1.1 glass 851 1.13 jtc /* atoi with error detection */ 852 1.36 christos static long long 853 1.23 wiz getn(const char *s) 854 1.1 glass { 855 1.6 alm char *p; 856 1.36 christos long long r; 857 1.1 glass 858 1.6 alm errno = 0; 859 1.36 christos r = strtoll(s, &p, 10); 860 1.13 jtc 861 1.6 alm if (errno != 0) 862 1.36 christos if (errno == ERANGE && (r == LLONG_MAX || r == LLONG_MIN)) 863 1.22 christos error("%s: out of range", s); 864 1.13 jtc 865 1.42 kre if (p != s) 866 1.42 kre while (isspace((unsigned char)*p)) 867 1.42 kre p++; 868 1.13 jtc 869 1.37 christos if (*p || p == s) 870 1.42 kre error("'%s': bad number", s); 871 1.13 jtc 872 1.36 christos return r; 873 1.1 glass } 874 1.1 glass 875 1.13 jtc static int 876 1.26 simonb newerf(const char *f1, const char *f2) 877 1.1 glass { 878 1.13 jtc struct stat b1, b2; 879 1.13 jtc 880 1.26 simonb return (stat(f1, &b1) == 0 && 881 1.26 simonb stat(f2, &b2) == 0 && 882 1.40 uebayasi timespeccmp(&b1.st_mtim, &b2.st_mtim, >)); 883 1.1 glass } 884 1.1 glass 885 1.13 jtc static int 886 1.26 simonb olderf(const char *f1, const char *f2) 887 1.1 glass { 888 1.13 jtc struct stat b1, b2; 889 1.13 jtc 890 1.26 simonb return (stat(f1, &b1) == 0 && 891 1.26 simonb stat(f2, &b2) == 0 && 892 1.40 uebayasi timespeccmp(&b1.st_mtim, &b2.st_mtim, <)); 893 1.1 glass } 894 1.1 glass 895 1.13 jtc static int 896 1.26 simonb equalf(const char *f1, const char *f2) 897 1.13 jtc { 898 1.13 jtc struct stat b1, b2; 899 1.1 glass 900 1.26 simonb return (stat(f1, &b1) == 0 && 901 1.26 simonb stat(f2, &b2) == 0 && 902 1.13 jtc b1.st_dev == b2.st_dev && 903 1.13 jtc b1.st_ino == b2.st_ino); 904 1.1 glass } 905