1 1.11 christos /* $NetBSD: c_test.c,v 1.11 2025/06/14 18:14:58 christos Exp $ */ 2 1.2 tls 3 1.1 jtc /* 4 1.1 jtc * test(1); version 7-like -- author Erik Baalbergen 5 1.1 jtc * modified by Eric Gisin to be used as built-in. 6 1.1 jtc * modified by Arnold Robbins to add SVR3 compatibility 7 1.1 jtc * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8 1.1 jtc * modified by Michael Rendell to add Korn's [[ .. ]] expressions. 9 1.1 jtc * modified by J.T. Conklin to add POSIX compatibility. 10 1.1 jtc */ 11 1.4 agc #include <sys/cdefs.h> 12 1.4 agc 13 1.4 agc #ifndef lint 14 1.11 christos __RCSID("$NetBSD: c_test.c,v 1.11 2025/06/14 18:14:58 christos Exp $"); 15 1.4 agc #endif 16 1.4 agc 17 1.7 kamil #include <sys/stat.h> 18 1.1 jtc 19 1.1 jtc #include "sh.h" 20 1.1 jtc #include "c_test.h" 21 1.1 jtc 22 1.1 jtc /* test(1) accepts the following grammar: 23 1.1 jtc oexpr ::= aexpr | aexpr "-o" oexpr ; 24 1.1 jtc aexpr ::= nexpr | nexpr "-a" aexpr ; 25 1.1 jtc nexpr ::= primary | "!" nexpr ; 26 1.1 jtc primary ::= unary-operator operand 27 1.1 jtc | operand binary-operator operand 28 1.1 jtc | operand 29 1.1 jtc | "(" oexpr ")" 30 1.1 jtc ; 31 1.1 jtc 32 1.1 jtc unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"| 33 1.1 jtc "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"| 34 1.1 jtc "-L"|"-h"|"-S"|"-H"; 35 1.1 jtc 36 1.1 jtc binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 37 1.1 jtc "-nt"|"-ot"|"-ef"| 38 1.1 jtc "<"|">" # rules used for [[ .. ]] expressions 39 1.1 jtc ; 40 1.1 jtc operand ::= <any thing> 41 1.1 jtc */ 42 1.1 jtc 43 1.1 jtc #define T_ERR_EXIT 2 /* POSIX says > 1 for errors */ 44 1.1 jtc 45 1.1 jtc struct t_op { 46 1.1 jtc char op_text[4]; 47 1.1 jtc Test_op op_num; 48 1.1 jtc }; 49 1.1 jtc static const struct t_op u_ops [] = { 50 1.1 jtc {"-a", TO_FILAXST }, 51 1.1 jtc {"-b", TO_FILBDEV }, 52 1.1 jtc {"-c", TO_FILCDEV }, 53 1.1 jtc {"-d", TO_FILID }, 54 1.1 jtc {"-e", TO_FILEXST }, 55 1.1 jtc {"-f", TO_FILREG }, 56 1.1 jtc {"-G", TO_FILGID }, 57 1.1 jtc {"-g", TO_FILSETG }, 58 1.1 jtc {"-h", TO_FILSYM }, 59 1.1 jtc {"-H", TO_FILCDF }, 60 1.1 jtc {"-k", TO_FILSTCK }, 61 1.1 jtc {"-L", TO_FILSYM }, 62 1.1 jtc {"-n", TO_STNZE }, 63 1.1 jtc {"-O", TO_FILUID }, 64 1.1 jtc {"-o", TO_OPTION }, 65 1.1 jtc {"-p", TO_FILFIFO }, 66 1.1 jtc {"-r", TO_FILRD }, 67 1.1 jtc {"-s", TO_FILGZ }, 68 1.1 jtc {"-S", TO_FILSOCK }, 69 1.1 jtc {"-t", TO_FILTT }, 70 1.1 jtc {"-u", TO_FILSETU }, 71 1.1 jtc {"-w", TO_FILWR }, 72 1.1 jtc {"-x", TO_FILEX }, 73 1.1 jtc {"-z", TO_STZER }, 74 1.1 jtc {"", TO_NONOP } 75 1.1 jtc }; 76 1.1 jtc static const struct t_op b_ops [] = { 77 1.1 jtc {"=", TO_STEQL }, 78 1.1 jtc #ifdef KSH 79 1.1 jtc {"==", TO_STEQL }, 80 1.1 jtc #endif /* KSH */ 81 1.1 jtc {"!=", TO_STNEQ }, 82 1.1 jtc {"<", TO_STLT }, 83 1.1 jtc {">", TO_STGT }, 84 1.1 jtc {"-eq", TO_INTEQ }, 85 1.1 jtc {"-ne", TO_INTNE }, 86 1.1 jtc {"-gt", TO_INTGT }, 87 1.1 jtc {"-ge", TO_INTGE }, 88 1.1 jtc {"-lt", TO_INTLT }, 89 1.1 jtc {"-le", TO_INTLE }, 90 1.1 jtc {"-ef", TO_FILEQ }, 91 1.1 jtc {"-nt", TO_FILNT }, 92 1.1 jtc {"-ot", TO_FILOT }, 93 1.1 jtc {"", TO_NONOP } 94 1.1 jtc }; 95 1.1 jtc 96 1.6 christos static int test_stat ARGS((const char *, struct stat *)); 97 1.6 christos static int test_eaccess ARGS((const char *, int)); 98 1.6 christos static int test_oexpr ARGS((Test_env *, int)); 99 1.6 christos static int test_aexpr ARGS((Test_env *, int)); 100 1.6 christos static int test_nexpr ARGS((Test_env *, int)); 101 1.6 christos static int test_primary ARGS((Test_env *, int)); 102 1.6 christos static int ptest_isa ARGS((Test_env *, Test_meta)); 103 1.6 christos static const char *ptest_getopnd ARGS((Test_env *, Test_op, int)); 104 1.6 christos static int ptest_eval ARGS((Test_env *, Test_op, const char *, 105 1.6 christos const char *, int)); 106 1.6 christos static void ptest_error ARGS((Test_env *, int, const char *)); 107 1.1 jtc 108 1.1 jtc int 109 1.1 jtc c_test(wp) 110 1.1 jtc char **wp; 111 1.1 jtc { 112 1.1 jtc int argc; 113 1.1 jtc int res; 114 1.1 jtc Test_env te; 115 1.1 jtc 116 1.1 jtc te.flags = 0; 117 1.1 jtc te.isa = ptest_isa; 118 1.1 jtc te.getopnd = ptest_getopnd; 119 1.1 jtc te.eval = ptest_eval; 120 1.1 jtc te.error = ptest_error; 121 1.1 jtc 122 1.1 jtc for (argc = 0; wp[argc]; argc++) 123 1.1 jtc ; 124 1.1 jtc 125 1.1 jtc if (strcmp(wp[0], "[") == 0) { 126 1.1 jtc if (strcmp(wp[--argc], "]") != 0) { 127 1.1 jtc bi_errorf("missing ]"); 128 1.1 jtc return T_ERR_EXIT; 129 1.1 jtc } 130 1.1 jtc } 131 1.1 jtc 132 1.1 jtc te.pos.wp = wp + 1; 133 1.1 jtc te.wp_end = wp + argc; 134 1.1 jtc 135 1.5 mycroft /* 136 1.1 jtc * Handle the special cases from POSIX.2, section 4.62.4. 137 1.5 mycroft * Implementation of all the rules isn't necessary since 138 1.5 mycroft * our parser does the right thing for the omitted steps. 139 1.1 jtc */ 140 1.1 jtc if (argc <= 5) { 141 1.1 jtc char **owp = wp; 142 1.1 jtc int invert = 0; 143 1.1 jtc Test_op op; 144 1.1 jtc const char *opnd1, *opnd2; 145 1.1 jtc 146 1.1 jtc while (--argc >= 0) { 147 1.1 jtc if ((*te.isa)(&te, TM_END)) 148 1.1 jtc return !0; 149 1.1 jtc if (argc == 3) { 150 1.1 jtc opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); 151 1.1 jtc if ((op = (Test_op) (*te.isa)(&te, TM_BINOP))) { 152 1.1 jtc opnd2 = (*te.getopnd)(&te, op, 1); 153 1.1 jtc res = (*te.eval)(&te, op, opnd1, opnd2, 154 1.1 jtc 1); 155 1.1 jtc if (te.flags & TEF_ERROR) 156 1.1 jtc return T_ERR_EXIT; 157 1.1 jtc if (invert & 1) 158 1.1 jtc res = !res; 159 1.1 jtc return !res; 160 1.1 jtc } 161 1.1 jtc /* back up to opnd1 */ 162 1.1 jtc te.pos.wp--; 163 1.1 jtc } 164 1.1 jtc if (argc == 1) { 165 1.1 jtc opnd1 = (*te.getopnd)(&te, TO_NONOP, 1); 166 1.3 hubertf /* Historically, -t by itself test if fd 1 167 1.3 hubertf * is a file descriptor, but POSIX says its 168 1.3 hubertf * a string test... 169 1.3 hubertf */ 170 1.3 hubertf if (!Flag(FPOSIX) && strcmp(opnd1, "-t") == 0) 171 1.3 hubertf break; 172 1.1 jtc res = (*te.eval)(&te, TO_STNZE, opnd1, 173 1.1 jtc (char *) 0, 1); 174 1.1 jtc if (invert & 1) 175 1.1 jtc res = !res; 176 1.1 jtc return !res; 177 1.1 jtc } 178 1.1 jtc if ((*te.isa)(&te, TM_NOT)) { 179 1.1 jtc invert++; 180 1.1 jtc } else 181 1.1 jtc break; 182 1.1 jtc } 183 1.1 jtc te.pos.wp = owp + 1; 184 1.1 jtc } 185 1.1 jtc 186 1.1 jtc return test_parse(&te); 187 1.1 jtc } 188 1.1 jtc 189 1.1 jtc /* 190 1.1 jtc * Generic test routines. 191 1.1 jtc */ 192 1.1 jtc 193 1.1 jtc Test_op 194 1.1 jtc test_isop(te, meta, s) 195 1.1 jtc Test_env *te; 196 1.1 jtc Test_meta meta; 197 1.1 jtc const char *s; 198 1.1 jtc { 199 1.1 jtc char sc1; 200 1.1 jtc const struct t_op *otab; 201 1.1 jtc 202 1.1 jtc otab = meta == TM_UNOP ? u_ops : b_ops; 203 1.1 jtc if (*s) { 204 1.1 jtc sc1 = s[1]; 205 1.1 jtc for (; otab->op_text[0]; otab++) 206 1.1 jtc if (sc1 == otab->op_text[1] 207 1.1 jtc && strcmp(s, otab->op_text) == 0 208 1.1 jtc && ((te->flags & TEF_DBRACKET) 209 1.1 jtc || (otab->op_num != TO_STLT 210 1.1 jtc && otab->op_num != TO_STGT))) 211 1.1 jtc return otab->op_num; 212 1.1 jtc } 213 1.1 jtc return TO_NONOP; 214 1.1 jtc } 215 1.1 jtc 216 1.1 jtc int 217 1.1 jtc test_eval(te, op, opnd1, opnd2, do_eval) 218 1.1 jtc Test_env *te; 219 1.1 jtc Test_op op; 220 1.1 jtc const char *opnd1; 221 1.1 jtc const char *opnd2; 222 1.1 jtc int do_eval; 223 1.1 jtc { 224 1.1 jtc int res; 225 1.1 jtc int not; 226 1.1 jtc struct stat b1, b2; 227 1.1 jtc 228 1.1 jtc if (!do_eval) 229 1.1 jtc return 0; 230 1.1 jtc 231 1.1 jtc switch ((int) op) { 232 1.1 jtc /* 233 1.1 jtc * Unary Operators 234 1.1 jtc */ 235 1.1 jtc case TO_STNZE: /* -n */ 236 1.1 jtc return *opnd1 != '\0'; 237 1.1 jtc case TO_STZER: /* -z */ 238 1.1 jtc return *opnd1 == '\0'; 239 1.1 jtc case TO_OPTION: /* -o */ 240 1.1 jtc if ((not = *opnd1 == '!')) 241 1.1 jtc opnd1++; 242 1.1 jtc if ((res = option(opnd1)) < 0) 243 1.1 jtc res = 0; 244 1.1 jtc else { 245 1.1 jtc res = Flag(res); 246 1.1 jtc if (not) 247 1.1 jtc res = !res; 248 1.1 jtc } 249 1.5 mycroft return res; 250 1.1 jtc case TO_FILRD: /* -r */ 251 1.1 jtc return test_eaccess(opnd1, R_OK) == 0; 252 1.1 jtc case TO_FILWR: /* -w */ 253 1.1 jtc return test_eaccess(opnd1, W_OK) == 0; 254 1.1 jtc case TO_FILEX: /* -x */ 255 1.1 jtc return test_eaccess(opnd1, X_OK) == 0; 256 1.1 jtc case TO_FILAXST: /* -a */ 257 1.1 jtc return test_stat(opnd1, &b1) == 0; 258 1.1 jtc case TO_FILEXST: /* -e */ 259 1.1 jtc /* at&t ksh does not appear to do the /dev/fd/ thing for 260 1.1 jtc * this (unless the os itself handles it) 261 1.1 jtc */ 262 1.1 jtc return stat(opnd1, &b1) == 0; 263 1.1 jtc case TO_FILREG: /* -r */ 264 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode); 265 1.1 jtc case TO_FILID: /* -d */ 266 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode); 267 1.1 jtc case TO_FILCDEV: /* -c */ 268 1.1 jtc #ifdef S_ISCHR 269 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode); 270 1.1 jtc #else 271 1.1 jtc return 0; 272 1.1 jtc #endif 273 1.1 jtc case TO_FILBDEV: /* -b */ 274 1.1 jtc #ifdef S_ISBLK 275 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode); 276 1.1 jtc #else 277 1.1 jtc return 0; 278 1.1 jtc #endif 279 1.1 jtc case TO_FILFIFO: /* -p */ 280 1.1 jtc #ifdef S_ISFIFO 281 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode); 282 1.1 jtc #else 283 1.1 jtc return 0; 284 1.1 jtc #endif 285 1.1 jtc case TO_FILSYM: /* -h -L */ 286 1.1 jtc #ifdef S_ISLNK 287 1.1 jtc return lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode); 288 1.1 jtc #else 289 1.1 jtc return 0; 290 1.1 jtc #endif 291 1.1 jtc case TO_FILSOCK: /* -S */ 292 1.1 jtc #ifdef S_ISSOCK 293 1.1 jtc return test_stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode); 294 1.1 jtc #else 295 1.1 jtc return 0; 296 1.1 jtc #endif 297 1.1 jtc case TO_FILCDF:/* -H HP context dependent files (directories) */ 298 1.1 jtc #ifdef S_ISCDF 299 1.1 jtc { 300 1.1 jtc /* Append a + to filename and check to see if result is a 301 1.1 jtc * setuid directory. CDF stuff in general is hookey, since 302 1.1 jtc * it breaks for the following sequence: echo hi > foo+; 303 1.1 jtc * mkdir foo; echo bye > foo/default; chmod u+s foo 304 1.1 jtc * (foo+ refers to the file with hi in it, there is no way 305 1.1 jtc * to get at the file with bye in it - please correct me if 306 1.1 jtc * I'm wrong about this). 307 1.1 jtc */ 308 1.1 jtc int len = strlen(opnd1); 309 1.1 jtc char *p = str_nsave(opnd1, len + 1, ATEMP); 310 1.1 jtc 311 1.1 jtc p[len++] = '+'; 312 1.1 jtc p[len] = '\0'; 313 1.1 jtc return stat(p, &b1) == 0 && S_ISCDF(b1.st_mode); 314 1.1 jtc } 315 1.1 jtc #else 316 1.1 jtc return 0; 317 1.1 jtc #endif 318 1.1 jtc case TO_FILSETU: /* -u */ 319 1.1 jtc #ifdef S_ISUID 320 1.1 jtc return test_stat(opnd1, &b1) == 0 321 1.1 jtc && (b1.st_mode & S_ISUID) == S_ISUID; 322 1.1 jtc #else 323 1.1 jtc return 0; 324 1.1 jtc #endif 325 1.1 jtc case TO_FILSETG: /* -g */ 326 1.1 jtc #ifdef S_ISGID 327 1.1 jtc return test_stat(opnd1, &b1) == 0 328 1.1 jtc && (b1.st_mode & S_ISGID) == S_ISGID; 329 1.1 jtc #else 330 1.1 jtc return 0; 331 1.1 jtc #endif 332 1.1 jtc case TO_FILSTCK: /* -k */ 333 1.1 jtc return test_stat(opnd1, &b1) == 0 334 1.1 jtc && (b1.st_mode & S_ISVTX) == S_ISVTX; 335 1.1 jtc case TO_FILGZ: /* -s */ 336 1.1 jtc return test_stat(opnd1, &b1) == 0 && b1.st_size > 0L; 337 1.1 jtc case TO_FILTT: /* -t */ 338 1.1 jtc if (opnd1 && !bi_getn(opnd1, &res)) { 339 1.1 jtc te->flags |= TEF_ERROR; 340 1.1 jtc res = 0; 341 1.3 hubertf } else { 342 1.3 hubertf /* generate error if in FPOSIX mode? */ 343 1.1 jtc res = isatty(opnd1 ? res : 0); 344 1.3 hubertf } 345 1.1 jtc return res; 346 1.1 jtc case TO_FILUID: /* -O */ 347 1.3 hubertf return test_stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid; 348 1.1 jtc case TO_FILGID: /* -G */ 349 1.1 jtc return test_stat(opnd1, &b1) == 0 && b1.st_gid == getegid(); 350 1.1 jtc /* 351 1.1 jtc * Binary Operators 352 1.1 jtc */ 353 1.1 jtc case TO_STEQL: /* = */ 354 1.1 jtc if (te->flags & TEF_DBRACKET) 355 1.9 kamil return gmatch(opnd1, opnd2, false); 356 1.1 jtc return strcmp(opnd1, opnd2) == 0; 357 1.1 jtc case TO_STNEQ: /* != */ 358 1.1 jtc if (te->flags & TEF_DBRACKET) 359 1.9 kamil return !gmatch(opnd1, opnd2, false); 360 1.1 jtc return strcmp(opnd1, opnd2) != 0; 361 1.1 jtc case TO_STLT: /* < */ 362 1.1 jtc return strcmp(opnd1, opnd2) < 0; 363 1.1 jtc case TO_STGT: /* > */ 364 1.1 jtc return strcmp(opnd1, opnd2) > 0; 365 1.1 jtc case TO_INTEQ: /* -eq */ 366 1.1 jtc case TO_INTNE: /* -ne */ 367 1.1 jtc case TO_INTGE: /* -ge */ 368 1.1 jtc case TO_INTGT: /* -gt */ 369 1.1 jtc case TO_INTLE: /* -le */ 370 1.1 jtc case TO_INTLT: /* -lt */ 371 1.1 jtc { 372 1.1 jtc long v1, v2; 373 1.1 jtc 374 1.3 hubertf if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR) 375 1.3 hubertf || !evaluate(opnd2, &v2, KSH_RETURN_ERROR)) 376 1.1 jtc { 377 1.1 jtc /* error already printed.. */ 378 1.1 jtc te->flags |= TEF_ERROR; 379 1.1 jtc return 1; 380 1.1 jtc } 381 1.1 jtc switch ((int) op) { 382 1.1 jtc case TO_INTEQ: 383 1.1 jtc return v1 == v2; 384 1.1 jtc case TO_INTNE: 385 1.1 jtc return v1 != v2; 386 1.1 jtc case TO_INTGE: 387 1.1 jtc return v1 >= v2; 388 1.1 jtc case TO_INTGT: 389 1.1 jtc return v1 > v2; 390 1.1 jtc case TO_INTLE: 391 1.1 jtc return v1 <= v2; 392 1.1 jtc case TO_INTLT: 393 1.1 jtc return v1 < v2; 394 1.1 jtc } 395 1.1 jtc } 396 1.10 christos abort(); 397 1.1 jtc case TO_FILNT: /* -nt */ 398 1.3 hubertf { 399 1.3 hubertf int s2; 400 1.3 hubertf /* ksh88/ksh93 succeed if file2 can't be stated 401 1.3 hubertf * (subtly different from `does not exist'). 402 1.3 hubertf */ 403 1.3 hubertf return stat(opnd1, &b1) == 0 404 1.11 christos && (((s2 = stat(opnd2, &b2)) == 0 405 1.11 christos && timespeccmp(&b1.st_mtim, &b2.st_mtim, >)) 406 1.11 christos || s2 == -1); 407 1.3 hubertf } 408 1.1 jtc case TO_FILOT: /* -ot */ 409 1.3 hubertf { 410 1.3 hubertf int s1; 411 1.3 hubertf /* ksh88/ksh93 succeed if file1 can't be stated 412 1.3 hubertf * (subtly different from `does not exist'). 413 1.3 hubertf */ 414 1.3 hubertf return stat(opnd2, &b2) == 0 415 1.11 christos && (((s1 = stat(opnd1, &b1)) == 0 416 1.11 christos && timespeccmp(&b1.st_mtim, &b2.st_mtim, <)) 417 1.11 christos || s1 == -1); 418 1.3 hubertf } 419 1.1 jtc case TO_FILEQ: /* -ef */ 420 1.11 christos return stat(opnd1, &b1) == 0 && stat(opnd2, &b2) == 0 421 1.1 jtc && b1.st_dev == b2.st_dev 422 1.1 jtc && b1.st_ino == b2.st_ino; 423 1.1 jtc } 424 1.1 jtc (*te->error)(te, 0, "internal error: unknown op"); 425 1.1 jtc return 1; 426 1.1 jtc } 427 1.1 jtc 428 1.1 jtc static int 429 1.6 christos test_stat(pathx, statb) 430 1.6 christos const char *pathx; 431 1.1 jtc struct stat *statb; 432 1.1 jtc { 433 1.6 christos return stat(pathx, statb); 434 1.1 jtc } 435 1.1 jtc 436 1.3 hubertf /* Routine to handle Korn's /dev/fd hack, and to deal with X_OK on 437 1.3 hubertf * non-directories when running as root. 438 1.3 hubertf */ 439 1.1 jtc static int 440 1.6 christos test_eaccess(pathx, mode) 441 1.6 christos const char *pathx; 442 1.1 jtc int mode; 443 1.1 jtc { 444 1.3 hubertf int res; 445 1.3 hubertf 446 1.6 christos res = eaccess(pathx, mode); 447 1.5 mycroft /* 448 1.5 mycroft * On most (all?) unixes, access() says everything is executable for 449 1.3 hubertf * root - avoid this on files by using stat(). 450 1.3 hubertf */ 451 1.5 mycroft if (res == 0 && ksheuid == 0 && (mode & X_OK)) { 452 1.3 hubertf struct stat statb; 453 1.3 hubertf 454 1.6 christos if (stat(pathx, &statb) < 0) 455 1.3 hubertf res = -1; 456 1.3 hubertf else if (S_ISDIR(statb.st_mode)) 457 1.3 hubertf res = 0; 458 1.3 hubertf else 459 1.3 hubertf res = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 460 1.3 hubertf ? 0 : -1; 461 1.5 mycroft } 462 1.3 hubertf 463 1.3 hubertf return res; 464 1.1 jtc } 465 1.1 jtc 466 1.1 jtc int 467 1.1 jtc test_parse(te) 468 1.1 jtc Test_env *te; 469 1.1 jtc { 470 1.1 jtc int res; 471 1.1 jtc 472 1.1 jtc res = test_oexpr(te, 1); 473 1.1 jtc 474 1.1 jtc if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END)) 475 1.1 jtc (*te->error)(te, 0, "unexpected operator/operand"); 476 1.1 jtc 477 1.1 jtc return (te->flags & TEF_ERROR) ? T_ERR_EXIT : !res; 478 1.1 jtc } 479 1.1 jtc 480 1.1 jtc static int 481 1.1 jtc test_oexpr(te, do_eval) 482 1.1 jtc Test_env *te; 483 1.1 jtc int do_eval; 484 1.1 jtc { 485 1.1 jtc int res; 486 1.1 jtc 487 1.1 jtc res = test_aexpr(te, do_eval); 488 1.1 jtc if (res) 489 1.1 jtc do_eval = 0; 490 1.1 jtc if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR)) 491 1.1 jtc return test_oexpr(te, do_eval) || res; 492 1.1 jtc return res; 493 1.1 jtc } 494 1.1 jtc 495 1.1 jtc static int 496 1.1 jtc test_aexpr(te, do_eval) 497 1.1 jtc Test_env *te; 498 1.1 jtc int do_eval; 499 1.1 jtc { 500 1.1 jtc int res; 501 1.1 jtc 502 1.1 jtc res = test_nexpr(te, do_eval); 503 1.1 jtc if (!res) 504 1.1 jtc do_eval = 0; 505 1.1 jtc if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND)) 506 1.1 jtc return test_aexpr(te, do_eval) && res; 507 1.1 jtc return res; 508 1.1 jtc } 509 1.1 jtc 510 1.1 jtc static int 511 1.1 jtc test_nexpr(te, do_eval) 512 1.1 jtc Test_env *te; 513 1.1 jtc int do_eval; 514 1.1 jtc { 515 1.1 jtc if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT)) 516 1.1 jtc return !test_nexpr(te, do_eval); 517 1.1 jtc return test_primary(te, do_eval); 518 1.1 jtc } 519 1.1 jtc 520 1.1 jtc static int 521 1.1 jtc test_primary(te, do_eval) 522 1.1 jtc Test_env *te; 523 1.1 jtc int do_eval; 524 1.1 jtc { 525 1.1 jtc const char *opnd1, *opnd2; 526 1.1 jtc int res; 527 1.1 jtc Test_op op; 528 1.1 jtc 529 1.1 jtc if (te->flags & TEF_ERROR) 530 1.1 jtc return 0; 531 1.1 jtc if ((*te->isa)(te, TM_OPAREN)) { 532 1.1 jtc res = test_oexpr(te, do_eval); 533 1.1 jtc if (te->flags & TEF_ERROR) 534 1.1 jtc return 0; 535 1.1 jtc if (!(*te->isa)(te, TM_CPAREN)) { 536 1.1 jtc (*te->error)(te, 0, "missing closing paren"); 537 1.1 jtc return 0; 538 1.1 jtc } 539 1.1 jtc return res; 540 1.1 jtc } 541 1.1 jtc if ((op = (Test_op) (*te->isa)(te, TM_UNOP))) { 542 1.1 jtc /* unary expression */ 543 1.1 jtc opnd1 = (*te->getopnd)(te, op, do_eval); 544 1.1 jtc if (!opnd1) { 545 1.1 jtc (*te->error)(te, -1, "missing argument"); 546 1.1 jtc return 0; 547 1.1 jtc } 548 1.1 jtc 549 1.1 jtc return (*te->eval)(te, op, opnd1, (const char *) 0, do_eval); 550 1.1 jtc } 551 1.1 jtc opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval); 552 1.1 jtc if (!opnd1) { 553 1.1 jtc (*te->error)(te, 0, "expression expected"); 554 1.1 jtc return 0; 555 1.1 jtc } 556 1.1 jtc if ((op = (Test_op) (*te->isa)(te, TM_BINOP))) { 557 1.1 jtc /* binary expression */ 558 1.1 jtc opnd2 = (*te->getopnd)(te, op, do_eval); 559 1.1 jtc if (!opnd2) { 560 1.1 jtc (*te->error)(te, -1, "missing second argument"); 561 1.1 jtc return 0; 562 1.1 jtc } 563 1.1 jtc 564 1.1 jtc return (*te->eval)(te, op, opnd1, opnd2, do_eval); 565 1.1 jtc } 566 1.1 jtc if (te->flags & TEF_DBRACKET) { 567 1.1 jtc (*te->error)(te, -1, "missing expression operator"); 568 1.1 jtc return 0; 569 1.1 jtc } 570 1.1 jtc return (*te->eval)(te, TO_STNZE, opnd1, (const char *) 0, do_eval); 571 1.1 jtc } 572 1.1 jtc 573 1.1 jtc /* 574 1.1 jtc * Plain test (test and [ .. ]) specific routines. 575 1.1 jtc */ 576 1.1 jtc 577 1.1 jtc /* Test if the current token is a whatever. Accepts the current token if 578 1.1 jtc * it is. Returns 0 if it is not, non-zero if it is (in the case of 579 1.1 jtc * TM_UNOP and TM_BINOP, the returned value is a Test_op). 580 1.1 jtc */ 581 1.1 jtc static int 582 1.1 jtc ptest_isa(te, meta) 583 1.1 jtc Test_env *te; 584 1.1 jtc Test_meta meta; 585 1.1 jtc { 586 1.1 jtc /* Order important - indexed by Test_meta values */ 587 1.1 jtc static const char *const tokens[] = { 588 1.1 jtc "-o", "-a", "!", "(", ")" 589 1.1 jtc }; 590 1.1 jtc int ret; 591 1.1 jtc 592 1.1 jtc if (te->pos.wp >= te->wp_end) 593 1.1 jtc return meta == TM_END; 594 1.1 jtc 595 1.1 jtc if (meta == TM_UNOP || meta == TM_BINOP) 596 1.1 jtc ret = (int) test_isop(te, meta, *te->pos.wp); 597 1.1 jtc else if (meta == TM_END) 598 1.1 jtc ret = 0; 599 1.1 jtc else 600 1.1 jtc ret = strcmp(*te->pos.wp, tokens[(int) meta]) == 0; 601 1.1 jtc 602 1.1 jtc /* Accept the token? */ 603 1.1 jtc if (ret) 604 1.1 jtc te->pos.wp++; 605 1.1 jtc 606 1.1 jtc return ret; 607 1.1 jtc } 608 1.1 jtc 609 1.1 jtc static const char * 610 1.1 jtc ptest_getopnd(te, op, do_eval) 611 1.1 jtc Test_env *te; 612 1.1 jtc Test_op op; 613 1.1 jtc int do_eval; 614 1.1 jtc { 615 1.1 jtc if (te->pos.wp >= te->wp_end) 616 1.1 jtc return op == TO_FILTT ? "1" : (const char *) 0; 617 1.1 jtc return *te->pos.wp++; 618 1.1 jtc } 619 1.1 jtc 620 1.1 jtc static int 621 1.1 jtc ptest_eval(te, op, opnd1, opnd2, do_eval) 622 1.1 jtc Test_env *te; 623 1.1 jtc Test_op op; 624 1.1 jtc const char *opnd1; 625 1.1 jtc const char *opnd2; 626 1.1 jtc int do_eval; 627 1.1 jtc { 628 1.1 jtc return test_eval(te, op, opnd1, opnd2, do_eval); 629 1.1 jtc } 630 1.1 jtc 631 1.1 jtc static void 632 1.1 jtc ptest_error(te, offset, msg) 633 1.1 jtc Test_env *te; 634 1.1 jtc int offset; 635 1.1 jtc const char *msg; 636 1.1 jtc { 637 1.1 jtc const char *op = te->pos.wp + offset >= te->wp_end ? 638 1.1 jtc (const char *) 0 : te->pos.wp[offset]; 639 1.1 jtc 640 1.1 jtc te->flags |= TEF_ERROR; 641 1.1 jtc if (op) 642 1.1 jtc bi_errorf("%s: %s", op, msg); 643 1.1 jtc else 644 1.1 jtc bi_errorf("%s", msg); 645 1.1 jtc } 646