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