1 /* $OpenBSD: expr.c,v 1.5 1996/12/14 12:17:59 mickey 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(2, 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(2, 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(2, 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() 179 { 180 char *p; 181 182 if ((p = *av) == NULL) { 183 token = EOI; 184 return; 185 } 186 av++; 187 188 if (p[0] != '\0') { 189 if (p[1] == '\0') { 190 const char *x = "|&=<>+-*/%:()"; 191 char *i; /* index */ 192 193 if ((i = strchr(x, *p)) != NULL) { 194 token = i - x; 195 return; 196 } 197 } else if (p[1] == '=' && p[2] == '\0') { 198 switch (*p) { 199 case '<': 200 token = LE; 201 return; 202 case '>': 203 token = GE; 204 return; 205 case '!': 206 token = NE; 207 return; 208 } 209 } 210 } 211 tokval = make_str(p); 212 token = OPERAND; 213 return; 214 } 215 216 __dead void 217 error() 218 { 219 errx(2, "syntax error"); 220 /* NOTREACHED */ 221 } 222 223 struct val * 224 eval6() 225 { 226 struct val *eval0 __P((void)); 227 struct val *v; 228 229 if (token == OPERAND) { 230 nexttoken(); 231 return tokval; 232 233 } else if (token == RP) { 234 nexttoken(); 235 v = eval0(); 236 237 if (token != LP) { 238 error(); 239 /* NOTREACHED */ 240 } 241 nexttoken(); 242 return v; 243 } else { 244 error(); 245 } 246 /* NOTREACHED */ 247 } 248 249 /* Parse and evaluate match (regex) expressions */ 250 struct val * 251 eval5() 252 { 253 regex_t rp; 254 regmatch_t rm[2]; 255 char errbuf[256]; 256 int eval; 257 struct val *l, *r; 258 struct val *v; 259 260 l = eval6(); 261 while (token == MATCH) { 262 nexttoken(); 263 r = eval6(); 264 265 /* coerce to both arguments to strings */ 266 to_string(l); 267 to_string(r); 268 269 /* compile regular expression */ 270 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) { 271 regerror(eval, &rp, errbuf, sizeof(errbuf)); 272 errx(2, "%s", errbuf); 273 } 274 275 /* compare string against pattern -- remember that patterns 276 are anchored to the beginning of the line */ 277 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) { 278 if (rm[1].rm_so >= 0) { 279 *(l->u.s + rm[1].rm_eo) = '\0'; 280 v = make_str(l->u.s + rm[1].rm_so); 281 282 } else { 283 v = make_int((int)(rm[0].rm_eo - rm[0].rm_so)); 284 } 285 } else { 286 if (rp.re_nsub == 0) { 287 v = make_int(0); 288 } else { 289 v = make_str(""); 290 } 291 } 292 293 /* free arguments and pattern buffer */ 294 free_value(l); 295 free_value(r); 296 regfree(&rp); 297 298 l = v; 299 } 300 301 return l; 302 } 303 304 /* Parse and evaluate multiplication and division expressions */ 305 struct val * 306 eval4() 307 { 308 struct val *l, *r; 309 enum token op; 310 311 l = eval5(); 312 while ((op = token) == MUL || op == DIV || op == MOD) { 313 nexttoken(); 314 r = eval5(); 315 316 if (!to_integer(l) || !to_integer(r)) { 317 errx(2, "non-numeric argument"); 318 } 319 320 if (op == MUL) { 321 l->u.i *= r->u.i; 322 } else { 323 if (r->u.i == 0) { 324 errx(2, "division by zero"); 325 } 326 if (op == DIV) { 327 l->u.i /= r->u.i; 328 } else { 329 l->u.i %= r->u.i; 330 } 331 } 332 333 free_value(r); 334 } 335 336 return l; 337 } 338 339 /* Parse and evaluate addition and subtraction expressions */ 340 struct val * 341 eval3() 342 { 343 struct val *l, *r; 344 enum token op; 345 346 l = eval4(); 347 while ((op = token) == ADD || op == SUB) { 348 nexttoken(); 349 r = eval4(); 350 351 if (!to_integer(l) || !to_integer(r)) { 352 errx(2, "non-numeric argument"); 353 } 354 355 if (op == ADD) { 356 l->u.i += r->u.i; 357 } else { 358 l->u.i -= r->u.i; 359 } 360 361 free_value(r); 362 } 363 364 return l; 365 } 366 367 /* Parse and evaluate comparison expressions */ 368 struct val * 369 eval2() 370 { 371 struct val *l, *r; 372 enum token op; 373 int v = 0, li, ri; 374 375 l = eval3(); 376 while ((op = token) == EQ || op == NE || op == LT || op == GT || op == LE || op == GE) { 377 nexttoken(); 378 r = eval3(); 379 380 if (is_integer(l, &li) && is_integer(r, &ri)) { 381 switch (op) { 382 case GT: 383 v = (li > ri); 384 break; 385 case GE: 386 v = (li >= ri); 387 break; 388 case LT: 389 v = (li < ri); 390 break; 391 case LE: 392 v = (li <= ri); 393 break; 394 case EQ: 395 v = (li == ri); 396 break; 397 case NE: 398 v = (li != ri); 399 break; 400 default: 401 break; 402 } 403 } else { 404 to_string(l); 405 to_string(r); 406 407 switch (op) { 408 case GT: 409 v = (strcoll(l->u.s, r->u.s) > 0); 410 break; 411 case GE: 412 v = (strcoll(l->u.s, r->u.s) >= 0); 413 break; 414 case LT: 415 v = (strcoll(l->u.s, r->u.s) < 0); 416 break; 417 case LE: 418 v = (strcoll(l->u.s, r->u.s) <= 0); 419 break; 420 case EQ: 421 v = (strcoll(l->u.s, r->u.s) == 0); 422 break; 423 case NE: 424 v = (strcoll(l->u.s, r->u.s) != 0); 425 break; 426 default: 427 break; 428 } 429 } 430 431 free_value(l); 432 free_value(r); 433 l = make_int(v); 434 } 435 436 return l; 437 } 438 439 /* Parse and evaluate & expressions */ 440 struct val * 441 eval1() 442 { 443 struct val *l, *r; 444 445 l = eval2(); 446 while (token == AND) { 447 nexttoken(); 448 r = eval2(); 449 450 if (is_zero_or_null(l) || is_zero_or_null(r)) { 451 free_value(l); 452 free_value(r); 453 l = make_int(0); 454 } else { 455 free_value(r); 456 } 457 } 458 459 return l; 460 } 461 462 /* Parse and evaluate | expressions */ 463 struct val * 464 eval0() 465 { 466 struct val *l, *r; 467 468 l = eval1(); 469 while (token == OR) { 470 nexttoken(); 471 r = eval1(); 472 473 if (is_zero_or_null(l)) { 474 free_value(l); 475 l = r; 476 } else { 477 free_value(r); 478 } 479 } 480 481 return l; 482 } 483 484 485 int 486 main(argc, argv) 487 int argc; 488 char **argv; 489 { 490 struct val *vp; 491 492 (void) setlocale(LC_ALL, ""); 493 av = argv + 1; 494 495 nexttoken(); 496 vp = eval0(); 497 498 if (token != EOI) { 499 error(); 500 /* NOTREACHED */ 501 } 502 503 if (vp->type == integer) 504 printf("%d\n", vp->u.i); 505 else 506 printf("%s\n", vp->u.s); 507 508 exit(is_zero_or_null(vp)); 509 } 510