24#include <limits.h> 25#include <stdarg.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30 31#ifdef SHELL 32#define main testcmd 33#include "bltin/bltin.h" 34#else 35#include <locale.h> 36 37static void error(const char *, ...) __attribute__((__noreturn__)) 38 __printf0like(1, 2); 39 40static void 41error(const char *msg, ...) 42{ 43 va_list ap; 44 va_start(ap, msg); 45 verrx(2, msg, ap); 46 /*NOTREACHED*/ 47 va_end(ap); 48} 49#endif 50 51/* test(1) accepts the following grammar: 52 oexpr ::= aexpr | aexpr "-o" oexpr ; 53 aexpr ::= nexpr | nexpr "-a" aexpr ; 54 nexpr ::= primary | "!" primary 55 primary ::= unary-operator operand 56 | operand binary-operator operand 57 | operand 58 | "(" oexpr ")" 59 ; 60 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 61 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 62 63 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 64 "-nt"|"-ot"|"-ef"; 65 operand ::= <any legal UNIX file name> 66*/ 67 68enum token { 69 EOI, 70 FILRD, 71 FILWR, 72 FILEX, 73 FILEXIST, 74 FILREG, 75 FILDIR, 76 FILCDEV, 77 FILBDEV, 78 FILFIFO, 79 FILSOCK, 80 FILSYM, 81 FILGZ, 82 FILTT, 83 FILSUID, 84 FILSGID, 85 FILSTCK, 86 FILNT, 87 FILOT, 88 FILEQ, 89 FILUID, 90 FILGID, 91 STREZ, 92 STRNZ, 93 STREQ, 94 STRNE, 95 STRLT, 96 STRGT, 97 INTEQ, 98 INTNE, 99 INTGE, 100 INTGT, 101 INTLE, 102 INTLT, 103 UNOT, 104 BAND, 105 BOR, 106 LPAREN, 107 RPAREN, 108 OPERAND 109}; 110 111enum token_types { 112 UNOP, 113 BINOP, 114 BUNOP, 115 BBINOP, 116 PAREN 117}; 118 119struct t_op { 120 const char *op_text; 121 short op_num, op_type; 122} const ops [] = { 123 {"-r", FILRD, UNOP}, 124 {"-w", FILWR, UNOP}, 125 {"-x", FILEX, UNOP}, 126 {"-e", FILEXIST,UNOP}, 127 {"-f", FILREG, UNOP}, 128 {"-d", FILDIR, UNOP}, 129 {"-c", FILCDEV,UNOP}, 130 {"-b", FILBDEV,UNOP}, 131 {"-p", FILFIFO,UNOP}, 132 {"-u", FILSUID,UNOP}, 133 {"-g", FILSGID,UNOP}, 134 {"-k", FILSTCK,UNOP}, 135 {"-s", FILGZ, UNOP}, 136 {"-t", FILTT, UNOP}, 137 {"-z", STREZ, UNOP}, 138 {"-n", STRNZ, UNOP}, 139 {"-h", FILSYM, UNOP}, /* for backwards compat */ 140 {"-O", FILUID, UNOP}, 141 {"-G", FILGID, UNOP}, 142 {"-L", FILSYM, UNOP}, 143 {"-S", FILSOCK,UNOP}, 144 {"=", STREQ, BINOP}, 145 {"!=", STRNE, BINOP}, 146 {"<", STRLT, BINOP}, 147 {">", STRGT, BINOP}, 148 {"-eq", INTEQ, BINOP}, 149 {"-ne", INTNE, BINOP}, 150 {"-ge", INTGE, BINOP}, 151 {"-gt", INTGT, BINOP}, 152 {"-le", INTLE, BINOP}, 153 {"-lt", INTLT, BINOP}, 154 {"-nt", FILNT, BINOP}, 155 {"-ot", FILOT, BINOP}, 156 {"-ef", FILEQ, BINOP}, 157 {"!", UNOT, BUNOP}, 158 {"-a", BAND, BBINOP}, 159 {"-o", BOR, BBINOP}, 160 {"(", LPAREN, PAREN}, 161 {")", RPAREN, PAREN}, 162 {0, 0, 0} 163}; 164 165struct t_op const *t_wp_op; 166char **t_wp; 167 168static int aexpr(enum token); 169static int binop(void); 170static int equalf(const char *, const char *); 171static int filstat(char *, enum token); 172static int getn(const char *);
| 25#include <limits.h> 26#include <stdarg.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31 32#ifdef SHELL 33#define main testcmd 34#include "bltin/bltin.h" 35#else 36#include <locale.h> 37 38static void error(const char *, ...) __attribute__((__noreturn__)) 39 __printf0like(1, 2); 40 41static void 42error(const char *msg, ...) 43{ 44 va_list ap; 45 va_start(ap, msg); 46 verrx(2, msg, ap); 47 /*NOTREACHED*/ 48 va_end(ap); 49} 50#endif 51 52/* test(1) accepts the following grammar: 53 oexpr ::= aexpr | aexpr "-o" oexpr ; 54 aexpr ::= nexpr | nexpr "-a" aexpr ; 55 nexpr ::= primary | "!" primary 56 primary ::= unary-operator operand 57 | operand binary-operator operand 58 | operand 59 | "(" oexpr ")" 60 ; 61 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 62 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 63 64 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 65 "-nt"|"-ot"|"-ef"; 66 operand ::= <any legal UNIX file name> 67*/ 68 69enum token { 70 EOI, 71 FILRD, 72 FILWR, 73 FILEX, 74 FILEXIST, 75 FILREG, 76 FILDIR, 77 FILCDEV, 78 FILBDEV, 79 FILFIFO, 80 FILSOCK, 81 FILSYM, 82 FILGZ, 83 FILTT, 84 FILSUID, 85 FILSGID, 86 FILSTCK, 87 FILNT, 88 FILOT, 89 FILEQ, 90 FILUID, 91 FILGID, 92 STREZ, 93 STRNZ, 94 STREQ, 95 STRNE, 96 STRLT, 97 STRGT, 98 INTEQ, 99 INTNE, 100 INTGE, 101 INTGT, 102 INTLE, 103 INTLT, 104 UNOT, 105 BAND, 106 BOR, 107 LPAREN, 108 RPAREN, 109 OPERAND 110}; 111 112enum token_types { 113 UNOP, 114 BINOP, 115 BUNOP, 116 BBINOP, 117 PAREN 118}; 119 120struct t_op { 121 const char *op_text; 122 short op_num, op_type; 123} const ops [] = { 124 {"-r", FILRD, UNOP}, 125 {"-w", FILWR, UNOP}, 126 {"-x", FILEX, UNOP}, 127 {"-e", FILEXIST,UNOP}, 128 {"-f", FILREG, UNOP}, 129 {"-d", FILDIR, UNOP}, 130 {"-c", FILCDEV,UNOP}, 131 {"-b", FILBDEV,UNOP}, 132 {"-p", FILFIFO,UNOP}, 133 {"-u", FILSUID,UNOP}, 134 {"-g", FILSGID,UNOP}, 135 {"-k", FILSTCK,UNOP}, 136 {"-s", FILGZ, UNOP}, 137 {"-t", FILTT, UNOP}, 138 {"-z", STREZ, UNOP}, 139 {"-n", STRNZ, UNOP}, 140 {"-h", FILSYM, UNOP}, /* for backwards compat */ 141 {"-O", FILUID, UNOP}, 142 {"-G", FILGID, UNOP}, 143 {"-L", FILSYM, UNOP}, 144 {"-S", FILSOCK,UNOP}, 145 {"=", STREQ, BINOP}, 146 {"!=", STRNE, BINOP}, 147 {"<", STRLT, BINOP}, 148 {">", STRGT, BINOP}, 149 {"-eq", INTEQ, BINOP}, 150 {"-ne", INTNE, BINOP}, 151 {"-ge", INTGE, BINOP}, 152 {"-gt", INTGT, BINOP}, 153 {"-le", INTLE, BINOP}, 154 {"-lt", INTLT, BINOP}, 155 {"-nt", FILNT, BINOP}, 156 {"-ot", FILOT, BINOP}, 157 {"-ef", FILEQ, BINOP}, 158 {"!", UNOT, BUNOP}, 159 {"-a", BAND, BBINOP}, 160 {"-o", BOR, BBINOP}, 161 {"(", LPAREN, PAREN}, 162 {")", RPAREN, PAREN}, 163 {0, 0, 0} 164}; 165 166struct t_op const *t_wp_op; 167char **t_wp; 168 169static int aexpr(enum token); 170static int binop(void); 171static int equalf(const char *, const char *); 172static int filstat(char *, enum token); 173static int getn(const char *);
|
174static int intcmp(const char *, const char *); 175static int isoperand(void); 176static int newerf(const char *, const char *); 177static int nexpr(enum token); 178static int oexpr(enum token); 179static int olderf(const char *, const char *); 180static int primary(enum token); 181static void syntax(const char *, const char *); 182static enum token t_lex(char *); 183 184int 185main(int argc, char **argv) 186{ 187 int i, res; 188 char *p; 189 char **nargv; 190 191 /* 192 * XXX copy the whole contents of argv to a newly allocated 193 * space with two extra cells filled with NULL's - this source 194 * code totally depends on their presence. 195 */ 196 if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL) 197 error("Out of space"); 198 199 for (i = 0; i < argc; i++) 200 nargv[i] = argv[i]; 201 202 nargv[i] = nargv[i + 1] = NULL; 203 argv = nargv; 204 205 if ((p = rindex(argv[0], '/')) == NULL) 206 p = argv[0]; 207 else 208 p++; 209 if (strcmp(p, "[") == 0) { 210 if (strcmp(argv[--argc], "]") != 0) 211 error("missing ]"); 212 argv[argc] = NULL; 213 } 214 215#ifndef SHELL 216 (void)setlocale(LC_CTYPE, ""); 217#endif 218 t_wp = &argv[1]; 219 res = !oexpr(t_lex(*t_wp)); 220 221 if (*t_wp != NULL && *++t_wp != NULL) 222 syntax(*t_wp, "unexpected operator"); 223 224 return res; 225} 226 227static void 228syntax(const char *op, const char *msg) 229{ 230 231 if (op && *op) 232 error("%s: %s", op, msg); 233 else 234 error("%s", msg); 235} 236 237static int 238oexpr(enum token n) 239{ 240 int res; 241 242 res = aexpr(n); 243 if (t_lex(*++t_wp) == BOR) 244 return oexpr(t_lex(*++t_wp)) || res; 245 t_wp--; 246 return res; 247} 248 249static int 250aexpr(enum token n) 251{ 252 int res; 253 254 res = nexpr(n); 255 if (t_lex(*++t_wp) == BAND) 256 return aexpr(t_lex(*++t_wp)) && res; 257 t_wp--; 258 return res; 259} 260 261static int 262nexpr(enum token n) 263{ 264 if (n == UNOT) 265 return !nexpr(t_lex(*++t_wp)); 266 return primary(n); 267} 268 269static int 270primary(enum token n) 271{ 272 enum token nn; 273 int res; 274 275 if (n == EOI) 276 return 0; /* missing expression */ 277 if (n == LPAREN) { 278 if ((nn = t_lex(*++t_wp)) == RPAREN) 279 return 0; /* missing expression */ 280 res = oexpr(nn); 281 if (t_lex(*++t_wp) != RPAREN) 282 syntax(NULL, "closing paren expected"); 283 return res; 284 } 285 if (t_wp_op && t_wp_op->op_type == UNOP) { 286 /* unary expression */ 287 if (*++t_wp == NULL) 288 syntax(t_wp_op->op_text, "argument expected"); 289 switch (n) { 290 case STREZ: 291 return strlen(*t_wp) == 0; 292 case STRNZ: 293 return strlen(*t_wp) != 0; 294 case FILTT: 295 return isatty(getn(*t_wp)); 296 default: 297 return filstat(*t_wp, n); 298 } 299 } 300 301 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 302 return binop(); 303 } 304 305 return strlen(*t_wp) > 0; 306} 307 308static int 309binop(void) 310{ 311 const char *opnd1, *opnd2; 312 struct t_op const *op; 313 314 opnd1 = *t_wp; 315 (void) t_lex(*++t_wp); 316 op = t_wp_op; 317 318 if ((opnd2 = *++t_wp) == NULL) 319 syntax(op->op_text, "argument expected"); 320 321 switch (op->op_num) { 322 case STREQ: 323 return strcmp(opnd1, opnd2) == 0; 324 case STRNE: 325 return strcmp(opnd1, opnd2) != 0; 326 case STRLT: 327 return strcmp(opnd1, opnd2) < 0; 328 case STRGT: 329 return strcmp(opnd1, opnd2) > 0; 330 case INTEQ: 331 return intcmp(opnd1, opnd2) == 0; 332 case INTNE: 333 return intcmp(opnd1, opnd2) != 0; 334 case INTGE: 335 return intcmp(opnd1, opnd2) >= 0; 336 case INTGT: 337 return intcmp(opnd1, opnd2) > 0; 338 case INTLE: 339 return intcmp(opnd1, opnd2) <= 0; 340 case INTLT: 341 return intcmp(opnd1, opnd2) < 0; 342 case FILNT: 343 return newerf (opnd1, opnd2); 344 case FILOT: 345 return olderf (opnd1, opnd2); 346 case FILEQ: 347 return equalf (opnd1, opnd2); 348 default: 349 abort(); 350 /* NOTREACHED */ 351 } 352} 353 354static int 355filstat(char *nm, enum token mode) 356{ 357 struct stat s; 358 359 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 360 return 0; 361 362 switch (mode) { 363 case FILRD: 364 return (eaccess(nm, R_OK) == 0); 365 case FILWR: 366 return (eaccess(nm, W_OK) == 0); 367 case FILEX: 368 /* XXX work around eaccess(2) false positives for superuser */ 369 if (eaccess(nm, X_OK) != 0) 370 return 0; 371 if (S_ISDIR(s.st_mode) || geteuid() != 0) 372 return 1; 373 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 374 case FILEXIST: 375 return (eaccess(nm, F_OK) == 0); 376 case FILREG: 377 return S_ISREG(s.st_mode); 378 case FILDIR: 379 return S_ISDIR(s.st_mode); 380 case FILCDEV: 381 return S_ISCHR(s.st_mode); 382 case FILBDEV: 383 return S_ISBLK(s.st_mode); 384 case FILFIFO: 385 return S_ISFIFO(s.st_mode); 386 case FILSOCK: 387 return S_ISSOCK(s.st_mode); 388 case FILSYM: 389 return S_ISLNK(s.st_mode); 390 case FILSUID: 391 return (s.st_mode & S_ISUID) != 0; 392 case FILSGID: 393 return (s.st_mode & S_ISGID) != 0; 394 case FILSTCK: 395 return (s.st_mode & S_ISVTX) != 0; 396 case FILGZ: 397 return s.st_size > (off_t)0; 398 case FILUID: 399 return s.st_uid == geteuid(); 400 case FILGID: 401 return s.st_gid == getegid(); 402 default: 403 return 1; 404 } 405} 406 407static enum token 408t_lex(char *s) 409{ 410 struct t_op const *op = ops; 411 412 if (s == 0) { 413 t_wp_op = NULL; 414 return EOI; 415 } 416 while (op->op_text) { 417 if (strcmp(s, op->op_text) == 0) { 418 if ((op->op_type == UNOP && isoperand()) || 419 (op->op_num == LPAREN && *(t_wp+1) == 0)) 420 break; 421 t_wp_op = op; 422 return op->op_num; 423 } 424 op++; 425 } 426 t_wp_op = NULL; 427 return OPERAND; 428} 429 430static int 431isoperand(void) 432{ 433 struct t_op const *op = ops; 434 char *s; 435 char *t; 436 437 if ((s = *(t_wp+1)) == 0) 438 return 1; 439 if ((t = *(t_wp+2)) == 0) 440 return 0; 441 while (op->op_text) { 442 if (strcmp(s, op->op_text) == 0) 443 return op->op_type == BINOP && 444 (t[0] != ')' || t[1] != '\0'); 445 op++; 446 } 447 return 0; 448} 449 450/* atoi with error detection */ 451static int 452getn(const char *s) 453{ 454 char *p; 455 long r; 456 457 errno = 0; 458 r = strtol(s, &p, 10); 459 460 if (s == p) 461 error("%s: bad number", s); 462 463 if (errno != 0) 464 error((errno == EINVAL) ? "%s: bad number" : 465 "%s: out of range", s); 466 467 while (isspace((unsigned char)*p)) 468 p++; 469 470 if (*p) 471 error("%s: bad number", s); 472 473 return (int) r; 474} 475 476/* atoi with error detection and 64 bit range */
| 175static int intcmp(const char *, const char *); 176static int isoperand(void); 177static int newerf(const char *, const char *); 178static int nexpr(enum token); 179static int oexpr(enum token); 180static int olderf(const char *, const char *); 181static int primary(enum token); 182static void syntax(const char *, const char *); 183static enum token t_lex(char *); 184 185int 186main(int argc, char **argv) 187{ 188 int i, res; 189 char *p; 190 char **nargv; 191 192 /* 193 * XXX copy the whole contents of argv to a newly allocated 194 * space with two extra cells filled with NULL's - this source 195 * code totally depends on their presence. 196 */ 197 if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL) 198 error("Out of space"); 199 200 for (i = 0; i < argc; i++) 201 nargv[i] = argv[i]; 202 203 nargv[i] = nargv[i + 1] = NULL; 204 argv = nargv; 205 206 if ((p = rindex(argv[0], '/')) == NULL) 207 p = argv[0]; 208 else 209 p++; 210 if (strcmp(p, "[") == 0) { 211 if (strcmp(argv[--argc], "]") != 0) 212 error("missing ]"); 213 argv[argc] = NULL; 214 } 215 216#ifndef SHELL 217 (void)setlocale(LC_CTYPE, ""); 218#endif 219 t_wp = &argv[1]; 220 res = !oexpr(t_lex(*t_wp)); 221 222 if (*t_wp != NULL && *++t_wp != NULL) 223 syntax(*t_wp, "unexpected operator"); 224 225 return res; 226} 227 228static void 229syntax(const char *op, const char *msg) 230{ 231 232 if (op && *op) 233 error("%s: %s", op, msg); 234 else 235 error("%s", msg); 236} 237 238static int 239oexpr(enum token n) 240{ 241 int res; 242 243 res = aexpr(n); 244 if (t_lex(*++t_wp) == BOR) 245 return oexpr(t_lex(*++t_wp)) || res; 246 t_wp--; 247 return res; 248} 249 250static int 251aexpr(enum token n) 252{ 253 int res; 254 255 res = nexpr(n); 256 if (t_lex(*++t_wp) == BAND) 257 return aexpr(t_lex(*++t_wp)) && res; 258 t_wp--; 259 return res; 260} 261 262static int 263nexpr(enum token n) 264{ 265 if (n == UNOT) 266 return !nexpr(t_lex(*++t_wp)); 267 return primary(n); 268} 269 270static int 271primary(enum token n) 272{ 273 enum token nn; 274 int res; 275 276 if (n == EOI) 277 return 0; /* missing expression */ 278 if (n == LPAREN) { 279 if ((nn = t_lex(*++t_wp)) == RPAREN) 280 return 0; /* missing expression */ 281 res = oexpr(nn); 282 if (t_lex(*++t_wp) != RPAREN) 283 syntax(NULL, "closing paren expected"); 284 return res; 285 } 286 if (t_wp_op && t_wp_op->op_type == UNOP) { 287 /* unary expression */ 288 if (*++t_wp == NULL) 289 syntax(t_wp_op->op_text, "argument expected"); 290 switch (n) { 291 case STREZ: 292 return strlen(*t_wp) == 0; 293 case STRNZ: 294 return strlen(*t_wp) != 0; 295 case FILTT: 296 return isatty(getn(*t_wp)); 297 default: 298 return filstat(*t_wp, n); 299 } 300 } 301 302 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 303 return binop(); 304 } 305 306 return strlen(*t_wp) > 0; 307} 308 309static int 310binop(void) 311{ 312 const char *opnd1, *opnd2; 313 struct t_op const *op; 314 315 opnd1 = *t_wp; 316 (void) t_lex(*++t_wp); 317 op = t_wp_op; 318 319 if ((opnd2 = *++t_wp) == NULL) 320 syntax(op->op_text, "argument expected"); 321 322 switch (op->op_num) { 323 case STREQ: 324 return strcmp(opnd1, opnd2) == 0; 325 case STRNE: 326 return strcmp(opnd1, opnd2) != 0; 327 case STRLT: 328 return strcmp(opnd1, opnd2) < 0; 329 case STRGT: 330 return strcmp(opnd1, opnd2) > 0; 331 case INTEQ: 332 return intcmp(opnd1, opnd2) == 0; 333 case INTNE: 334 return intcmp(opnd1, opnd2) != 0; 335 case INTGE: 336 return intcmp(opnd1, opnd2) >= 0; 337 case INTGT: 338 return intcmp(opnd1, opnd2) > 0; 339 case INTLE: 340 return intcmp(opnd1, opnd2) <= 0; 341 case INTLT: 342 return intcmp(opnd1, opnd2) < 0; 343 case FILNT: 344 return newerf (opnd1, opnd2); 345 case FILOT: 346 return olderf (opnd1, opnd2); 347 case FILEQ: 348 return equalf (opnd1, opnd2); 349 default: 350 abort(); 351 /* NOTREACHED */ 352 } 353} 354 355static int 356filstat(char *nm, enum token mode) 357{ 358 struct stat s; 359 360 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 361 return 0; 362 363 switch (mode) { 364 case FILRD: 365 return (eaccess(nm, R_OK) == 0); 366 case FILWR: 367 return (eaccess(nm, W_OK) == 0); 368 case FILEX: 369 /* XXX work around eaccess(2) false positives for superuser */ 370 if (eaccess(nm, X_OK) != 0) 371 return 0; 372 if (S_ISDIR(s.st_mode) || geteuid() != 0) 373 return 1; 374 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 375 case FILEXIST: 376 return (eaccess(nm, F_OK) == 0); 377 case FILREG: 378 return S_ISREG(s.st_mode); 379 case FILDIR: 380 return S_ISDIR(s.st_mode); 381 case FILCDEV: 382 return S_ISCHR(s.st_mode); 383 case FILBDEV: 384 return S_ISBLK(s.st_mode); 385 case FILFIFO: 386 return S_ISFIFO(s.st_mode); 387 case FILSOCK: 388 return S_ISSOCK(s.st_mode); 389 case FILSYM: 390 return S_ISLNK(s.st_mode); 391 case FILSUID: 392 return (s.st_mode & S_ISUID) != 0; 393 case FILSGID: 394 return (s.st_mode & S_ISGID) != 0; 395 case FILSTCK: 396 return (s.st_mode & S_ISVTX) != 0; 397 case FILGZ: 398 return s.st_size > (off_t)0; 399 case FILUID: 400 return s.st_uid == geteuid(); 401 case FILGID: 402 return s.st_gid == getegid(); 403 default: 404 return 1; 405 } 406} 407 408static enum token 409t_lex(char *s) 410{ 411 struct t_op const *op = ops; 412 413 if (s == 0) { 414 t_wp_op = NULL; 415 return EOI; 416 } 417 while (op->op_text) { 418 if (strcmp(s, op->op_text) == 0) { 419 if ((op->op_type == UNOP && isoperand()) || 420 (op->op_num == LPAREN && *(t_wp+1) == 0)) 421 break; 422 t_wp_op = op; 423 return op->op_num; 424 } 425 op++; 426 } 427 t_wp_op = NULL; 428 return OPERAND; 429} 430 431static int 432isoperand(void) 433{ 434 struct t_op const *op = ops; 435 char *s; 436 char *t; 437 438 if ((s = *(t_wp+1)) == 0) 439 return 1; 440 if ((t = *(t_wp+2)) == 0) 441 return 0; 442 while (op->op_text) { 443 if (strcmp(s, op->op_text) == 0) 444 return op->op_type == BINOP && 445 (t[0] != ')' || t[1] != '\0'); 446 op++; 447 } 448 return 0; 449} 450 451/* atoi with error detection */ 452static int 453getn(const char *s) 454{ 455 char *p; 456 long r; 457 458 errno = 0; 459 r = strtol(s, &p, 10); 460 461 if (s == p) 462 error("%s: bad number", s); 463 464 if (errno != 0) 465 error((errno == EINVAL) ? "%s: bad number" : 466 "%s: out of range", s); 467 468 while (isspace((unsigned char)*p)) 469 p++; 470 471 if (*p) 472 error("%s: bad number", s); 473 474 return (int) r; 475} 476 477/* atoi with error detection and 64 bit range */
|