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