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