1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdlib.h> 34 #include <regexpr.h> 35 #include <locale.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <regex.h> 39 #include <limits.h> 40 #include <stdio.h> 41 #include <ctype.h> 42 #include <errno.h> 43 44 #define A_STRING 258 45 #define NOARG 259 46 #define OR 260 47 #define AND 261 48 #define EQ 262 49 #define LT 263 50 #define GT 264 51 #define GEQ 265 52 #define LEQ 266 53 #define NEQ 267 54 #define ADD 268 55 #define SUBT 269 56 #define MULT 270 57 #define DIV 271 58 #define REM 272 59 #define MCH 273 60 #define MATCH 274 61 #ifdef _iBCS2 62 #define SUBSTR 276 63 #define LENGTH 277 64 #define INDEX 278 65 #endif /* _iBCS2 */ 66 67 /* size of subexpression array */ 68 #define MSIZE LINE_MAX 69 #define error(c) errxx() 70 #define EQL(x, y) (strcmp(x, y) == 0) 71 72 #define ERROR(c) errxx() 73 #define MAX_MATCH 20 74 static int ematch(char *, char *); 75 static void yyerror(char *); 76 static void errxx(); 77 static void *exprmalloc(size_t size); 78 79 long atol(); 80 char *strcpy(), *strncpy(); 81 void exit(); 82 83 static char *ltoa(); 84 static char *lltoa(); 85 static char **Av; 86 static char *buf; 87 static int Ac; 88 static int Argi; 89 static int noarg; 90 static int paren; 91 #ifdef _iBCS2 92 char *sysv3_set; 93 #endif /* _iBCS2 */ 94 /* 95 * Array used to store subexpressions in regular expressions 96 * Only one subexpression allowed per regular expression currently 97 */ 98 static char Mstring[1][MSIZE]; 99 100 101 static char *operator[] = { 102 "|", "&", "+", "-", "*", "/", "%", ":", 103 "=", "==", "<", "<=", ">", ">=", "!=", 104 "match", 105 #ifdef _iBCS2 106 "substr", "length", "index", 107 #endif /* _iBCS2 */ 108 "\0" }; 109 static int op[] = { 110 OR, AND, ADD, SUBT, MULT, DIV, REM, MCH, 111 EQ, EQ, LT, LEQ, GT, GEQ, NEQ, 112 MATCH 113 #ifdef _iBCS2 114 , SUBSTR, LENGTH, INDEX 115 #endif /* _iBCS2 */ 116 }; 117 static int pri[] = { 118 1, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 7 119 #ifdef _iBCS2 120 , 7, 7, 7 121 #endif /* _iBCS2 */ 122 }; 123 124 125 /* 126 * clean_buf - XCU4 mod to remove leading zeros from negative signed 127 * numeric output, e.g., -00001 becomes -1 128 */ 129 static void 130 clean_buf(buf) 131 char *buf; 132 { 133 int i = 0; 134 int is_a_num = 1; 135 int len; 136 long long num; 137 138 if (buf[0] == '\0') 139 return; 140 len = strlen(buf); 141 if (len <= 0) 142 return; 143 144 if (buf[0] == '-') { 145 i++; /* Skip the leading '-' see while loop */ 146 if (len <= 1) /* Is it a '-' all by itself? */ 147 return; /* Yes, so return */ 148 149 while (i < len) { 150 if (! isdigit(buf[i])) { 151 is_a_num = 0; 152 break; 153 } 154 i++; 155 } 156 if (is_a_num) { 157 (void) sscanf(buf, "%lld", &num); 158 (void) sprintf(buf, "%lld", num); 159 } 160 } 161 } 162 163 /* 164 * End XCU4 mods. 165 */ 166 167 static int 168 yylex() 169 { 170 char *p; 171 int i; 172 173 if (Argi >= Ac) 174 return (NOARG); 175 176 p = Av[Argi]; 177 178 if ((*p == '(' || *p == ')') && p[1] == '\0') 179 return ((int)*p); 180 for (i = 0; *operator[i]; ++i) 181 if (EQL(operator[i], p)) 182 return (op[i]); 183 184 185 return (A_STRING); 186 } 187 188 static char 189 *rel(oper, r1, r2) register char *r1, *r2; 190 { 191 long long i, l1, l2; 192 193 if (ematch(r1, "-\\{0,1\\}[0-9]*$") && 194 ematch(r2, "-\\{0,1\\}[0-9]*$")) { 195 errno = 0; 196 l1 = strtoll(r1, (char **)NULL, 10); 197 l2 = strtoll(r2, (char **)NULL, 10); 198 if (errno) { 199 #ifdef XPG6 200 /* XPG6: stdout will always contain newline even on error */ 201 (void) write(1, "\n", 1); 202 #endif 203 if (errno == ERANGE) { 204 (void) fprintf(stderr, gettext( 205 "expr: Integer argument too large\n")); 206 exit(3); 207 } else { 208 perror("expr"); 209 exit(3); 210 } 211 } 212 switch (oper) { 213 case EQ: 214 i = (l1 == l2); 215 break; 216 case GT: 217 i = (l1 > l2); 218 break; 219 case GEQ: 220 i = (l1 >= l2); 221 break; 222 case LT: 223 i = (l1 < l2); 224 break; 225 case LEQ: 226 i = (l1 <= l2); 227 break; 228 case NEQ: 229 i = (l1 != l2); 230 break; 231 } 232 } 233 else 234 { 235 i = strcoll(r1, r2); 236 switch (oper) { 237 case EQ: 238 i = i == 0; 239 break; 240 case GT: 241 i = i > 0; 242 break; 243 case GEQ: 244 i = i >= 0; 245 break; 246 case LT: 247 i = i < 0; 248 break; 249 case LEQ: 250 i = i <= 0; 251 break; 252 case NEQ: 253 i = i != 0; 254 break; 255 } 256 } 257 return (i ? "1": "0"); 258 } 259 260 static char 261 *arith(oper, r1, r2) char *r1, *r2; 262 { 263 long long i1, i2; 264 register char *rv; 265 266 if (!(ematch(r1, "-\\{0,1\\}[0-9]*$") && 267 ematch(r2, "-\\{0,1\\}[0-9]*$"))) 268 yyerror("non-numeric argument"); 269 errno = 0; 270 i1 = strtoll(r1, (char **)NULL, 10); 271 i2 = strtoll(r2, (char **)NULL, 10); 272 if (errno) { 273 #ifdef XPG6 274 /* XPG6: stdout will always contain newline even on error */ 275 (void) write(1, "\n", 1); 276 #endif 277 if (errno == ERANGE) { 278 (void) fprintf(stderr, gettext( 279 "expr: Integer argument too large\n")); 280 exit(3); 281 } else { 282 perror("expr"); 283 exit(3); 284 } 285 } 286 287 switch (oper) { 288 case ADD: 289 i1 = i1 + i2; 290 break; 291 case SUBT: 292 i1 = i1 - i2; 293 break; 294 case MULT: 295 i1 = i1 * i2; 296 break; 297 case DIV: 298 if (i2 == 0) 299 yyerror("division by zero"); 300 i1 = i1 / i2; 301 break; 302 case REM: 303 if (i2 == 0) 304 yyerror("division by zero"); 305 i1 = i1 % i2; 306 break; 307 } 308 rv = exprmalloc(25); 309 (void) strcpy(rv, lltoa(i1)); 310 return (rv); 311 } 312 313 static char 314 *conj(oper, r1, r2) 315 char *r1, *r2; 316 { 317 register char *rv; 318 319 switch (oper) { 320 321 case OR: 322 if (EQL(r1, "0") || EQL(r1, "")) { 323 if (EQL(r2, "0") || EQL(r2, "")) 324 rv = "0"; 325 else 326 rv = r2; 327 } else 328 rv = r1; 329 break; 330 case AND: 331 if (EQL(r1, "0") || EQL(r1, "")) 332 rv = "0"; 333 else if (EQL(r2, "0") || EQL(r2, "")) 334 rv = "0"; 335 else 336 rv = r1; 337 break; 338 } 339 return (rv); 340 } 341 342 #ifdef _iBCS2 343 char * 344 substr(char *v, char *s, char *w) 345 { 346 int si, wi; 347 char *res; 348 349 si = atol(s); 350 wi = atol(w); 351 while (--si) 352 if (*v) ++v; 353 354 res = v; 355 356 while (wi--) 357 if (*v) ++v; 358 359 *v = '\0'; 360 return (res); 361 } 362 363 char * 364 index(char *s, char *t) 365 { 366 long i, j; 367 char *rv; 368 369 for (i = 0; s[i]; ++i) 370 for (j = 0; t[j]; ++j) 371 if (s[i] == t[j]) { 372 (void) strcpy(rv = exprmalloc(8), ltoa(++i)); 373 return (rv); 374 } 375 return ("0"); 376 } 377 378 char * 379 length(char *s) 380 { 381 long i = 0; 382 char *rv; 383 384 while (*s++) ++i; 385 386 rv = exprmalloc(8); 387 (void) strcpy(rv, ltoa(i)); 388 return (rv); 389 } 390 #endif /* _iBCS2 */ 391 392 static char * 393 match(char *s, char *p) 394 { 395 char *rv; 396 long val; /* XCU4 */ 397 398 (void) strcpy(rv = exprmalloc(8), ltoa(val = (long)ematch(s, p))); 399 if (nbra /* && val != 0 */) { 400 rv = exprmalloc((unsigned)strlen(Mstring[0]) + 1); 401 (void) strcpy(rv, Mstring[0]); 402 } 403 return (rv); 404 } 405 406 407 /* 408 * ematch - XCU4 mods involve calling compile/advance which simulate 409 * the obsolete compile/advance functions using regcomp/regexec 410 */ 411 static int 412 ematch(char *s, char *p) 413 { 414 static char *expbuf; 415 char *nexpbuf; 416 int num; 417 #ifdef XPG4 418 int nmatch; /* number of matched bytes */ 419 char tempbuf[256]; 420 char *tmptr1 = 0; /* If tempbuf is not large enough */ 421 char *tmptr; 422 int nmbchars; /* number characters in multibyte string */ 423 #endif 424 425 nexpbuf = compile(p, (char *)0, (char *)0); /* XCU4 regex mod */ 426 if (0 /* XXX nbra > 1*/) 427 yyerror("Too many '\\('s"); 428 if (regerrno) { 429 if (regerrno != 41 || expbuf == NULL) 430 errxx(); 431 } else { 432 if (expbuf) 433 free(expbuf); 434 expbuf = nexpbuf; 435 } 436 if (advance(s, expbuf)) { 437 if (nbra > 0) { 438 p = braslist[0]; 439 num = braelist[0] - p; 440 if ((num > MSIZE - 1) || (num < 0)) 441 yyerror("string too long"); 442 (void) strncpy(Mstring[0], p, num); 443 Mstring[0][num] = '\0'; 444 } 445 #ifdef XPG4 446 /* 447 * Use mbstowcs to find the number of multibyte characters 448 * in the multibyte string beginning at s, and 449 * ending at loc2. Create a separate string 450 * of the substring, so it can be passed to mbstowcs. 451 */ 452 nmatch = loc2 - s; 453 if (nmatch > ((sizeof (tempbuf) / sizeof (char)) - 1)) { 454 tmptr1 = exprmalloc(nmatch + 1); 455 tmptr = tmptr1; 456 } else { 457 tmptr = tempbuf; 458 } 459 memcpy(tmptr, s, nmatch); 460 *(tmptr + nmatch) = '\0'; 461 if ((nmbchars = mbstowcs(NULL, tmptr, NULL)) == -1) { 462 yyerror("invalid multibyte character encountered"); 463 if (tmptr1 != NULL) 464 free(tmptr1); 465 return (0); 466 } 467 if (tmptr1 != NULL) 468 free(tmptr1); 469 return (nmbchars); 470 #else 471 return (loc2-s); 472 #endif 473 } 474 return (0); 475 } 476 477 static void 478 errxx() 479 { 480 yyerror("RE error"); 481 } 482 483 static void 484 yyerror(char *s) 485 { 486 #ifdef XPG6 487 /* XPG6: stdout will always contain newline even on error */ 488 (void) write(1, "\n", 1); 489 #endif 490 (void) write(2, "expr: ", 6); 491 (void) write(2, gettext(s), (unsigned)strlen(gettext(s))); 492 (void) write(2, "\n", 1); 493 exit(2); 494 /* NOTREACHED */ 495 } 496 497 static char * 498 ltoa(long l) 499 { 500 static char str[20]; 501 char *sp = &str[18]; /* u370 */ 502 int i; 503 int neg = 0; 504 505 if ((unsigned long)l == 0x80000000UL) 506 return ("-2147483648"); 507 if (l < 0) 508 ++neg, l = -l; 509 str[19] = '\0'; 510 do { 511 i = l % 10; 512 *sp-- = '0' + i; 513 l /= 10; 514 } while (l); 515 if (neg) 516 *sp-- = '-'; 517 return (++sp); 518 } 519 520 static char * 521 lltoa(long long l) 522 { 523 static char str[25]; 524 char *sp = &str[23]; 525 int i; 526 int neg = 0; 527 528 if (l == 0x8000000000000000ULL) 529 return ("-9223372036854775808"); 530 if (l < 0) 531 ++neg, l = -l; 532 str[24] = '\0'; 533 do { 534 i = l % 10; 535 *sp-- = '0' + i; 536 l /= 10; 537 } while (l); 538 if (neg) 539 *sp-- = '-'; 540 return (++sp); 541 } 542 543 static char * 544 expres(int prior, int par) 545 { 546 int ylex, temp, op1; 547 char *r1, *ra, *rb, *rc; 548 ylex = yylex(); 549 if (ylex >= NOARG && ylex < MATCH) { 550 yyerror("syntax error"); 551 } 552 if (ylex == A_STRING) { 553 r1 = Av[Argi++]; 554 temp = Argi; 555 } else { 556 if (ylex == '(') { 557 paren++; 558 Argi++; 559 r1 = expres(0, Argi); 560 Argi--; 561 } 562 } 563 lop: 564 ylex = yylex(); 565 if (ylex > NOARG && ylex < MATCH) { 566 op1 = ylex; 567 Argi++; 568 if (pri[op1-OR] <= prior) 569 return (r1); 570 else { 571 switch (op1) { 572 case OR: 573 case AND: 574 r1 = conj(op1, r1, expres(pri[op1-OR], 0)); 575 break; 576 case EQ: 577 case LT: 578 case GT: 579 case LEQ: 580 case GEQ: 581 case NEQ: 582 r1 = rel(op1, r1, expres(pri[op1-OR], 0)); 583 break; 584 case ADD: 585 case SUBT: 586 case MULT: 587 case DIV: 588 case REM: 589 r1 = arith(op1, r1, expres(pri[op1-OR], 0)); 590 break; 591 case MCH: 592 r1 = match(r1, expres(pri[op1-OR], 0)); 593 break; 594 } 595 if (noarg == 1) { 596 return (r1); 597 } 598 Argi--; 599 goto lop; 600 } 601 } 602 ylex = yylex(); 603 if (ylex == ')') { 604 if (par == Argi) { 605 yyerror("syntax error"); 606 } 607 if (par != 0) { 608 paren--; 609 Argi++; 610 } 611 Argi++; 612 return (r1); 613 } 614 ylex = yylex(); 615 #ifdef _iBCS2 616 if (ylex > MCH && ((sysv3_set && ylex <= INDEX) || ylex <= MATCH)) { 617 #else 618 if (ylex > MCH && ylex <= MATCH) { 619 #endif /* _iBCS2 */ 620 if (Argi == temp) { 621 return (r1); 622 } 623 op1 = ylex; 624 Argi++; 625 switch (op1) { 626 case MATCH: 627 rb = expres(pri[op1-OR], 0); 628 ra = expres(pri[op1-OR], 0); 629 break; 630 #ifdef _iBCS2 631 case SUBSTR: 632 rc = expres(pri[op1-OR], 0); 633 rb = expres(pri[op1-OR], 0); 634 ra = expres(pri[op1-OR], 0); 635 break; 636 case LENGTH: 637 ra = expres(pri[op1-OR], 0); 638 break; 639 case INDEX: 640 rb = expres(pri[op1-OR], 0); 641 ra = expres(pri[op1-OR], 0); 642 break; 643 #endif /* _iBCS2 */ 644 } 645 switch (op1) { 646 case MATCH: 647 r1 = match(rb, ra); 648 break; 649 #ifdef _iBCS2 650 case SUBSTR: 651 r1 = substr(rc, rb, ra); 652 break; 653 case LENGTH: 654 r1 = length(ra); 655 break; 656 case INDEX: 657 r1 = index(rb, ra); 658 break; 659 #endif /* _iBCS2 */ 660 } 661 if (noarg == 1) { 662 return (r1); 663 } 664 Argi--; 665 goto lop; 666 } 667 ylex = yylex(); 668 if (ylex == NOARG) { 669 noarg = 1; 670 } 671 return (r1); 672 } 673 674 void * 675 exprmalloc(size_t size) 676 { 677 void *rv; 678 679 if ((rv = malloc(size)) == NULL) { 680 char *s = gettext("malloc error"); 681 682 (void) write(2, "expr: ", 6); 683 (void) write(2, s, (unsigned)strlen(s)); 684 (void) write(2, "\n", 1); 685 exit(3); 686 } 687 return (rv); 688 } 689 690 int 691 main(int argc, char **argv) 692 { 693 /* 694 * XCU4 allow "--" as argument 695 */ 696 if (argc > 1 && strcmp(argv[1], "--") == 0) 697 argv++, argc--; 698 /* 699 * XCU4 - print usage message when invoked without args 700 */ 701 if (argc < 2) { 702 #ifdef XPG6 703 /* XPG6: stdout will always contain newline even on error */ 704 (void) write(1, "\n", 1); 705 #endif 706 (void) fprintf(stderr, gettext("Usage: expr expression\n")); 707 exit(3); 708 } 709 Ac = argc; 710 Argi = 1; 711 noarg = 0; 712 paren = 0; 713 Av = argv; 714 #ifdef _iBCS2 715 sysv3_set = getenv("SYSV3"); 716 #endif /* _iBCS2 */ 717 718 (void) setlocale(LC_ALL, ""); 719 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 720 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 721 #endif 722 (void) textdomain(TEXT_DOMAIN); 723 buf = expres(0, 1); 724 if (Ac != Argi || paren != 0) { 725 yyerror("syntax error"); 726 } 727 /* 728 * XCU4 - strip leading zeros from numeric output 729 */ 730 clean_buf(buf); 731 (void) write(1, buf, (unsigned)strlen(buf)); 732 (void) write(1, "\n", 1); 733 return ((strcmp(buf, "0") == 0 || buf[0] == 0) ? 1 : 0); 734 } 735