1 /* $OpenBSD: expr.c,v 1.17 2006/06/21 18:28:24 deraadt Exp $ */ 2 /* $NetBSD: expr.c,v 1.3.6.1 1996/06/04 20:41:47 cgd Exp $ */ 3 4 /* 5 * Written by J.T. Conklin <jtc@netbsd.org>. 6 * Public domain. 7 */ 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <locale.h> 13 #include <ctype.h> 14 #include <regex.h> 15 #include <err.h> 16 17 struct val *make_int(int); 18 struct val *make_str(char *); 19 void free_value(struct val *); 20 int is_integer(struct val *, int *); 21 int to_integer(struct val *); 22 void to_string(struct val *); 23 int is_zero_or_null(struct val *); 24 void nexttoken(int); 25 __dead void error(void); 26 struct val *eval6(void); 27 struct val *eval5(void); 28 struct val *eval4(void); 29 struct val *eval3(void); 30 struct val *eval2(void); 31 struct val *eval1(void); 32 struct val *eval0(void); 33 34 enum token { 35 OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP, 36 NE, LE, GE, OPERAND, EOI 37 }; 38 39 struct val { 40 enum { 41 integer, 42 string 43 } type; 44 45 union { 46 char *s; 47 int i; 48 } u; 49 }; 50 51 enum token token; 52 struct val *tokval; 53 char **av; 54 55 struct val * 56 make_int(int i) 57 { 58 struct val *vp; 59 60 vp = (struct val *) malloc(sizeof(*vp)); 61 if (vp == NULL) { 62 err(3, NULL); 63 } 64 vp->type = integer; 65 vp->u.i = i; 66 return vp; 67 } 68 69 70 struct val * 71 make_str(char *s) 72 { 73 struct val *vp; 74 75 vp = (struct val *) malloc(sizeof(*vp)); 76 if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) { 77 err(3, NULL); 78 } 79 vp->type = string; 80 return vp; 81 } 82 83 84 void 85 free_value(struct val *vp) 86 { 87 if (vp->type == string) 88 free(vp->u.s); 89 free(vp); 90 } 91 92 93 /* determine if vp is an integer; if so, return it's value in *r */ 94 int 95 is_integer(struct val *vp, int *r) 96 { 97 char *s; 98 int neg; 99 int i; 100 101 if (vp->type == integer) { 102 *r = vp->u.i; 103 return 1; 104 } 105 106 /* 107 * POSIX.2 defines an "integer" as an optional unary minus 108 * followed by digits. 109 */ 110 s = vp->u.s; 111 i = 0; 112 113 neg = (*s == '-'); 114 if (neg) 115 s++; 116 117 while (*s) { 118 if (!isdigit(*s)) 119 return 0; 120 121 i *= 10; 122 i += *s - '0'; 123 124 s++; 125 } 126 127 if (neg) 128 i *= -1; 129 130 *r = i; 131 return 1; 132 } 133 134 135 /* coerce to vp to an integer */ 136 int 137 to_integer(struct val *vp) 138 { 139 int r; 140 141 if (vp->type == integer) 142 return 1; 143 144 if (is_integer(vp, &r)) { 145 free(vp->u.s); 146 vp->u.i = r; 147 vp->type = integer; 148 return 1; 149 } 150 151 return 0; 152 } 153 154 155 /* coerce to vp to an string */ 156 void 157 to_string(struct val *vp) 158 { 159 char *tmp; 160 161 if (vp->type == string) 162 return; 163 164 if (asprintf(&tmp, "%d", vp->u.i) == -1) 165 err(3, NULL); 166 167 vp->type = string; 168 vp->u.s = tmp; 169 } 170 171 int 172 is_zero_or_null(struct val *vp) 173 { 174 if (vp->type == integer) { 175 return (vp->u.i == 0); 176 } else { 177 return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0)); 178 } 179 /* NOTREACHED */ 180 } 181 182 void 183 nexttoken(int pat) 184 { 185 char *p; 186 187 if ((p = *av) == NULL) { 188 token = EOI; 189 return; 190 } 191 av++; 192 193 194 if (pat == 0 && p[0] != '\0') { 195 if (p[1] == '\0') { 196 const char *x = "|&=<>+-*/%:()"; 197 char *i; /* index */ 198 199 if ((i = strchr(x, *p)) != NULL) { 200 token = i - x; 201 return; 202 } 203 } else if (p[1] == '=' && p[2] == '\0') { 204 switch (*p) { 205 case '<': 206 token = LE; 207 return; 208 case '>': 209 token = GE; 210 return; 211 case '!': 212 token = NE; 213 return; 214 } 215 } 216 } 217 tokval = make_str(p); 218 token = OPERAND; 219 return; 220 } 221 222 __dead void 223 error(void) 224 { 225 errx(2, "syntax error"); 226 /* NOTREACHED */ 227 } 228 229 struct val * 230 eval6(void) 231 { 232 struct val *v; 233 234 if (token == OPERAND) { 235 nexttoken(0); 236 return tokval; 237 238 } else if (token == RP) { 239 nexttoken(0); 240 v = eval0(); 241 242 if (token != LP) { 243 error(); 244 /* NOTREACHED */ 245 } 246 nexttoken(0); 247 return v; 248 } else { 249 error(); 250 } 251 /* NOTREACHED */ 252 } 253 254 /* Parse and evaluate match (regex) expressions */ 255 struct val * 256 eval5(void) 257 { 258 regex_t rp; 259 regmatch_t rm[2]; 260 char errbuf[256]; 261 int eval; 262 struct val *l, *r; 263 struct val *v; 264 265 l = eval6(); 266 while (token == MATCH) { 267 nexttoken(1); 268 r = eval6(); 269 270 /* coerce to both arguments to strings */ 271 to_string(l); 272 to_string(r); 273 274 /* compile regular expression */ 275 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) { 276 regerror(eval, &rp, errbuf, sizeof(errbuf)); 277 errx(2, "%s", errbuf); 278 } 279 280 /* compare string against pattern -- remember that patterns 281 are anchored to the beginning of the line */ 282 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) { 283 if (rm[1].rm_so >= 0) { 284 *(l->u.s + rm[1].rm_eo) = '\0'; 285 v = make_str(l->u.s + rm[1].rm_so); 286 287 } else { 288 v = make_int((int)(rm[0].rm_eo - rm[0].rm_so)); 289 } 290 } else { 291 if (rp.re_nsub == 0) { 292 v = make_int(0); 293 } else { 294 v = make_str(""); 295 } 296 } 297 298 /* free arguments and pattern buffer */ 299 free_value(l); 300 free_value(r); 301 regfree(&rp); 302 303 l = v; 304 } 305 306 return l; 307 } 308 309 /* Parse and evaluate multiplication and division expressions */ 310 struct val * 311 eval4(void) 312 { 313 struct val *l, *r; 314 enum token op; 315 316 l = eval5(); 317 while ((op = token) == MUL || op == DIV || op == MOD) { 318 nexttoken(0); 319 r = eval5(); 320 321 if (!to_integer(l) || !to_integer(r)) { 322 errx(2, "non-numeric argument"); 323 } 324 325 if (op == MUL) { 326 l->u.i *= r->u.i; 327 } else { 328 if (r->u.i == 0) { 329 errx(2, "division by zero"); 330 } 331 if (op == DIV) { 332 l->u.i /= r->u.i; 333 } else { 334 l->u.i %= r->u.i; 335 } 336 } 337 338 free_value(r); 339 } 340 341 return l; 342 } 343 344 /* Parse and evaluate addition and subtraction expressions */ 345 struct val * 346 eval3(void) 347 { 348 struct val *l, *r; 349 enum token op; 350 351 l = eval4(); 352 while ((op = token) == ADD || op == SUB) { 353 nexttoken(0); 354 r = eval4(); 355 356 if (!to_integer(l) || !to_integer(r)) { 357 errx(2, "non-numeric argument"); 358 } 359 360 if (op == ADD) { 361 l->u.i += r->u.i; 362 } else { 363 l->u.i -= r->u.i; 364 } 365 366 free_value(r); 367 } 368 369 return l; 370 } 371 372 /* Parse and evaluate comparison expressions */ 373 struct val * 374 eval2(void) 375 { 376 struct val *l, *r; 377 enum token op; 378 int v = 0, li, ri; 379 380 l = eval3(); 381 while ((op = token) == EQ || op == NE || op == LT || op == GT || 382 op == LE || op == GE) { 383 nexttoken(0); 384 r = eval3(); 385 386 if (is_integer(l, &li) && is_integer(r, &ri)) { 387 switch (op) { 388 case GT: 389 v = (li > ri); 390 break; 391 case GE: 392 v = (li >= ri); 393 break; 394 case LT: 395 v = (li < ri); 396 break; 397 case LE: 398 v = (li <= ri); 399 break; 400 case EQ: 401 v = (li == ri); 402 break; 403 case NE: 404 v = (li != ri); 405 break; 406 default: 407 break; 408 } 409 } else { 410 to_string(l); 411 to_string(r); 412 413 switch (op) { 414 case GT: 415 v = (strcoll(l->u.s, r->u.s) > 0); 416 break; 417 case GE: 418 v = (strcoll(l->u.s, r->u.s) >= 0); 419 break; 420 case LT: 421 v = (strcoll(l->u.s, r->u.s) < 0); 422 break; 423 case LE: 424 v = (strcoll(l->u.s, r->u.s) <= 0); 425 break; 426 case EQ: 427 v = (strcoll(l->u.s, r->u.s) == 0); 428 break; 429 case NE: 430 v = (strcoll(l->u.s, r->u.s) != 0); 431 break; 432 default: 433 break; 434 } 435 } 436 437 free_value(l); 438 free_value(r); 439 l = make_int(v); 440 } 441 442 return l; 443 } 444 445 /* Parse and evaluate & expressions */ 446 struct val * 447 eval1(void) 448 { 449 struct val *l, *r; 450 451 l = eval2(); 452 while (token == AND) { 453 nexttoken(0); 454 r = eval2(); 455 456 if (is_zero_or_null(l) || is_zero_or_null(r)) { 457 free_value(l); 458 free_value(r); 459 l = make_int(0); 460 } else { 461 free_value(r); 462 } 463 } 464 465 return l; 466 } 467 468 /* Parse and evaluate | expressions */ 469 struct val * 470 eval0(void) 471 { 472 struct val *l, *r; 473 474 l = eval1(); 475 while (token == OR) { 476 nexttoken(0); 477 r = eval1(); 478 479 if (is_zero_or_null(l)) { 480 free_value(l); 481 l = r; 482 } else { 483 free_value(r); 484 } 485 } 486 487 return l; 488 } 489 490 491 int 492 main(int argc, char *argv[]) 493 { 494 struct val *vp; 495 496 (void) setlocale(LC_ALL, ""); 497 498 if (argc > 1 && !strcmp(argv[1], "--")) 499 argv++; 500 501 av = argv + 1; 502 503 nexttoken(0); 504 vp = eval0(); 505 506 if (token != EOI) { 507 error(); 508 /* NOTREACHED */ 509 } 510 511 if (vp->type == integer) 512 printf("%d\n", vp->u.i); 513 else 514 printf("%s\n", vp->u.s); 515 516 exit(is_zero_or_null(vp)); 517 } 518