1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * * 20 ***********************************************************************/ 21 #pragma prototyped 22 23 /* 24 * expr.c 25 * Written by David Korn 26 * Tue Oct 31 08:48:11 EST 1995 27 */ 28 29 static const char usage[] = 30 "[-?\n@(#)$Id: expr (AT&T Research) 2004-05-27 $\n]" 31 USAGE_LICENSE 32 "[+NAME?expr - evaluate arguments as an expression]" 33 "[+DESCRIPTION?\bexpr\b evaluates an expression given as arguments and writes " 34 "the result to standard output. The character \b0\b will be written " 35 "to indicate a zero value and nothing will be written to indicate an " 36 "empty string.]" 37 "[+?Most of the functionality of \bexpr\b is provided in a more natural " 38 "way by the shell, \bsh\b(1), and \bexpr\b is provided primarily " 39 "for backward compatibility.]" 40 "[+?Terms of the expression must be separate arguments. A string argument is " 41 "one that can not be identified as an integer. Integer-valued " 42 "arguments may be preceded by a unary plus or minus sign. Because " 43 "many of the operators use characters that have special meaning to " 44 "the shell, they must be quoted when entered from the shell.]" 45 46 "[+?Expressions are formed from the operators listed below in order " 47 "of increasing precedence within groups. All of the operators are " 48 "left associative. The symbols \aexpr1\a and \aexpr2\a represent " 49 "expressions formed from strings and integers and the following " 50 "operators:]{" 51 "[+\aexpr1\a \b|\b \aexpr2\a?Returns the evaluation of \aexpr1\a if " 52 "it is neither null nor 0, otherwise returns the evaluation of expr2.]" 53 54 "[+\aexpr1\a \b&\b \aexpr2\a?Returns the evaluation of \aexpr1\a if " 55 "neither expression evaluates to null or 0, otherwise returns 0.]" 56 57 "[+\aexpr1\a \aop\a \aexpr2\a?Returns the result of a decimal integer " 58 "comparison if both arguments are integers; otherwise, returns the " 59 "result of a string comparison using the locale-specific collation " 60 "sequence. The result of each comparison will be 1 if the specified " 61 "relationship is true, or 0 if the relationship is false. \aop\a " 62 "can be one of the following:]{" 63 "[+=?Equal.]" 64 "[+==?Equal.]" 65 "[+>?Greater than.]" 66 "[+>=?Greater than or equal to.]" 67 "[+<?Less than.]" 68 "[+<=?Less than or equal to.]" 69 "[+!=?Not equal to.]" 70 "}" 71 72 "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b+\b or \b-\b; " 73 "addition or subtraction of decimal integer-valued arguments.]" 74 "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b*\b, \b/\b or \b%\b; " 75 "multiplication, division, or remainder of the decimal " 76 "integer-valued arguments.]" 77 "[+\aexpr1\a \b::\b \aexpr2\a?The matching operator : compares " 78 "\aexpr1\a with \aexpr2\a, which must be a BRE. Normally, " 79 "the matching operator returns the number of bytes matched " 80 "and 0 on failure. However, if the pattern contains at " 81 "least one sub-expression [\\( . . .\\)]], the string " 82 "corresponding to \\1 will be returned.]" 83 "[+( \aexpr1\a )?Grouping symbols. An expression can " 84 "be placed within parenthesis to change precedence.]" 85 "[+match\b \astring\a \aexpr\a?Equivalent to \astring\a \b:\b " 86 "\aexpr\a.]" 87 "[+substr\b \astring\a \apos\a \alength\a?\alength\a character " 88 "substring of \astring\a starting at \apos\a " 89 "(counting from 1).]" 90 "[+index\b \astring\a \achars\a?The position in \astring\a " 91 "(counting from 1) of the leftmost occurrence of any " 92 "character in \achars\a.]" 93 "[+length\b \astring\a?The number of characters in \astring\a.]" 94 "[+quote\b \atoken\a?Treat \atoken\a as a string operand.]" 95 "}" 96 "[+?For backwards compatibility, unrecognized options beginning with " 97 "a \b-\b will be treated as operands. Portable applications " 98 "should use \b--\b to indicate end of options.]" 99 100 "\n" 101 "\n operand ...\n" 102 "\n" 103 104 "[+EXIT STATUS?]{" 105 "[+0?The expression is neither null nor 0.]" 106 "[+1?The expression is null or 0.]" 107 "[+2?Invalid expressions.]" 108 "[+>2?An error occurred.]" 109 "}" 110 "[+SEE ALSO?\bregcomp\b(5), \bgrep\b(1), \bsh\b(1)]" 111 ; 112 113 #include <cmd.h> 114 #include <regex.h> 115 116 #define T_ADD 0x100 117 #define T_MULT 0x200 118 #define T_CMP 0x400 119 #define T_FUN 0x800 120 #define T_OP 7 121 #define T_NUM 1 122 #define T_STR 2 123 124 #define OP_EQ (T_CMP|0) 125 #define OP_GT (T_CMP|1) 126 #define OP_LT (T_CMP|2) 127 #define OP_GE (T_CMP|3) 128 #define OP_LE (T_CMP|4) 129 #define OP_NE (T_CMP|5) 130 #define OP_PLUS (T_ADD|0) 131 #define OP_MINUS (T_ADD|1) 132 #define OP_MULT (T_MULT|0) 133 #define OP_DIV (T_MULT|1) 134 #define OP_MOD (T_MULT|2) 135 #define OP_INDEX (T_FUN|0) 136 #define OP_LENGTH (T_FUN|1) 137 #define OP_MATCH (T_FUN|2) 138 #define OP_QUOTE (T_FUN|3) 139 #define OP_SUBSTR (T_FUN|4) 140 141 #define numeric(np) ((np)->type&T_NUM) 142 143 static const struct Optable_s 144 { 145 const char opname[3]; 146 int op; 147 } 148 optable[] = 149 { 150 "|", '|', 151 "&", '&', 152 "=", OP_EQ, 153 "==", OP_EQ, 154 ">", OP_GT, 155 "<", OP_LT, 156 ">=", OP_GE, 157 "<=", OP_LE, 158 "!=", OP_NE, 159 "+", OP_PLUS, 160 "-", OP_MINUS, 161 "*", OP_MULT, 162 "/", OP_DIV, 163 "%", OP_MOD, 164 ":", ':', 165 "(", '(', 166 ")", ')' 167 }; 168 169 typedef struct Node_s 170 { 171 int type; 172 long num; 173 char *str; 174 } Node_t; 175 176 typedef struct State_s 177 { 178 int standard; 179 char** arglist; 180 char buf[36]; 181 } State_t; 182 183 static int expr_or(State_t*, Node_t*); 184 185 static int getnode(State_t* state, Node_t *np) 186 { 187 register char* sp; 188 register char* cp; 189 register int i; 190 register int j; 191 register int k; 192 register int tok; 193 char* ep; 194 195 if (!(cp = *state->arglist++)) 196 error(ERROR_exit(2), "argument expected"); 197 if (!state->standard) 198 switch (cp[0]) 199 { 200 case 'i': 201 if (cp[1] == 'n' && !strcmp(cp, "index")) 202 { 203 if (!(cp = *state->arglist++)) 204 error(ERROR_exit(2), "string argument expected"); 205 if (!(ep = *state->arglist++)) 206 error(ERROR_exit(2), "chars argument expected"); 207 np->num = (ep = strpbrk(cp, ep)) ? (ep - cp + 1) : 0; 208 np->type = T_NUM; 209 goto next; 210 } 211 break; 212 case 'l': 213 if (cp[1] == 'e' && !strcmp(cp, "length")) 214 { 215 if (!(cp = *state->arglist++)) 216 error(ERROR_exit(2), "string argument expected"); 217 np->num = strlen(cp); 218 np->type = T_NUM; 219 goto next; 220 } 221 break; 222 case 'm': 223 if (cp[1] == 'a' && !strcmp(cp, "match")) 224 { 225 if (!(np->str = *state->arglist++)) 226 error(ERROR_exit(2), "pattern argument expected"); 227 np->type = T_STR; 228 return ':'; 229 } 230 break; 231 case 'q': 232 if (cp[1] == 'u' && !strcmp(cp, "quote") && !(cp = *state->arglist++)) 233 error(ERROR_exit(2), "string argument expected"); 234 break; 235 case 's': 236 if (cp[1] == 'u' && !strcmp(cp, "substr")) 237 { 238 if (!(sp = *state->arglist++)) 239 error(ERROR_exit(2), "string argument expected"); 240 if (!(cp = *state->arglist++)) 241 error(ERROR_exit(2), "position argument expected"); 242 i = strtol(cp, &ep, 10); 243 if (*ep || --i <= 0) 244 i = -1; 245 if (!(cp = *state->arglist++)) 246 error(ERROR_exit(2), "length argument expected"); 247 j = strtol(cp, &ep, 10); 248 if (*ep) 249 j = -1; 250 k = strlen(sp); 251 if (i < 0 || i >= k || j < 0) 252 sp = ""; 253 else 254 { 255 sp += i; 256 k -= i; 257 if (j < k) 258 sp[j] = 0; 259 } 260 np->type = T_STR; 261 np->str = sp; 262 goto next; 263 } 264 break; 265 } 266 if (*cp=='(' && cp[1]==0) 267 { 268 tok = expr_or(state, np); 269 if (tok != ')') 270 error(ERROR_exit(2),"closing parenthesis missing"); 271 } 272 else 273 { 274 np->type = T_STR; 275 np->str = cp; 276 if (*cp) 277 { 278 np->num = strtol(np->str,&ep,10); 279 if (!*ep) 280 np->type |= T_NUM; 281 } 282 } 283 next: 284 if (!(cp = *state->arglist)) 285 return 0; 286 state->arglist++; 287 for (i=0; i < sizeof(optable)/sizeof(*optable); i++) 288 if (*cp==optable[i].opname[0] && cp[1]==optable[i].opname[1]) 289 return optable[i].op; 290 error(ERROR_exit(2),"%s: unknown operator argument",cp); 291 return 0; 292 } 293 294 static int expr_cond(State_t* state, Node_t *np) 295 { 296 register int tok = getnode(state, np); 297 298 while (tok==':') 299 { 300 regex_t re; 301 regmatch_t match[2]; 302 int n; 303 Node_t rp; 304 char *cp; 305 tok = getnode(state, &rp); 306 if (np->type&T_STR) 307 cp = np->str; 308 else 309 sfsprintf(cp=state->buf,sizeof(state->buf),"%d",np->num); 310 np->num = 0; 311 np->type = T_NUM; 312 if (n = regcomp(&re, rp.str, REG_LEFT|REG_LENIENT)) 313 regfatal(&re, ERROR_exit(2), n); 314 if (!(n = regexec(&re, cp, elementsof(match), match, 0))) 315 { 316 if (re.re_nsub > 0) 317 { 318 np->type = T_STR; 319 if (match[1].rm_so >= 0) 320 { 321 np->str = cp + match[1].rm_so; 322 np->str[match[1].rm_eo - match[1].rm_so] = 0; 323 np->num = strtol(np->str,&cp,10); 324 if (cp!=np->str && *cp==0) 325 np->type |= T_NUM; 326 } 327 else 328 np->str = ""; 329 } 330 else 331 np->num = match[0].rm_eo - match[0].rm_so; 332 } 333 else if (n != REG_NOMATCH) 334 regfatal(&re, ERROR_exit(2), n); 335 else if (re.re_nsub) 336 { 337 np->str = ""; 338 np->type = T_STR; 339 } 340 regfree(&re); 341 } 342 return tok; 343 } 344 345 static int expr_mult(State_t* state, Node_t *np) 346 { 347 register int tok = expr_cond(state, np); 348 349 while ((tok&~T_OP)==T_MULT) 350 { 351 Node_t rp; 352 int op = (tok&T_OP); 353 tok = expr_cond(state, &rp); 354 if (!numeric(np) || !numeric(&rp)) 355 error(ERROR_exit(2),"non-numeric argument"); 356 if (op && rp.num==0) 357 error(ERROR_exit(2),"division by zero"); 358 switch(op) 359 { 360 case 0: 361 np->num *= rp.num; 362 break; 363 case 1: 364 np->num /= rp.num; 365 break; 366 case 2: 367 np->num %= rp.num; 368 } 369 np->type = T_NUM; 370 } 371 return tok; 372 } 373 374 static int expr_add(State_t* state, Node_t *np) 375 { 376 register int tok = expr_mult(state, np); 377 378 while ((tok&~T_OP)==T_ADD) 379 { 380 Node_t rp; 381 int op = (tok&T_OP); 382 tok = expr_mult(state, &rp); 383 if (!numeric(np) || !numeric(&rp)) 384 error(ERROR_exit(2),"non-numeric argument"); 385 if (op) 386 np->num -= rp.num; 387 else 388 np->num += rp.num; 389 np->type = T_NUM; 390 } 391 return tok; 392 } 393 394 static int expr_cmp(State_t* state, Node_t *np) 395 { 396 register int tok = expr_add(state, np); 397 398 while ((tok&~T_OP)==T_CMP) 399 { 400 Node_t rp; 401 register char *left,*right; 402 char buff1[36],buff2[36]; 403 int op = (tok&T_OP); 404 tok = expr_add(state, &rp); 405 if (numeric(&rp) && numeric(np)) 406 op |= 010; 407 else 408 { 409 if (np->type&T_STR) 410 left = np->str; 411 else 412 sfsprintf(left=buff1,sizeof(buff1),"%d",np->num); 413 if (rp.type&T_STR) 414 right = rp.str; 415 else 416 sfsprintf(right=buff2,sizeof(buff2),"%d",rp.num); 417 } 418 switch(op) 419 { 420 case 0: 421 np->num = streq(left,right); 422 break; 423 case 1: 424 np->num = (strcoll(left,right)>0); 425 break; 426 case 2: 427 np->num = (strcoll(left,right)<0); 428 break; 429 case 3: 430 np->num = (strcoll(left,right)>=0); 431 break; 432 case 4: 433 np->num = (strcoll(left,right)<=0); 434 break; 435 case 5: 436 np->num = !streq(left,right); 437 break; 438 case 010: 439 np->num = (np->num==rp.num); 440 break; 441 case 011: 442 np->num = (np->num>rp.num); 443 break; 444 case 012: 445 np->num = (np->num<rp.num); 446 break; 447 case 013: 448 np->num = (np->num>=rp.num); 449 break; 450 case 014: 451 np->num = (np->num<=rp.num); 452 break; 453 case 015: 454 np->num = (np->num!=rp.num); 455 break; 456 } 457 np->type = T_NUM; 458 } 459 return tok; 460 } 461 462 static int expr_and(State_t* state, Node_t *np) 463 { 464 register int tok = expr_cmp(state, np); 465 while (tok=='&') 466 { 467 Node_t rp; 468 tok = expr_cmp(state, &rp); 469 if ((numeric(&rp) && rp.num==0) || *rp.str==0) 470 { 471 np->num = 0; 472 np->type=T_NUM; 473 } 474 } 475 return tok; 476 } 477 478 static int expr_or(State_t* state, Node_t *np) 479 { 480 register int tok = expr_and(state, np); 481 while (tok=='|') 482 { 483 Node_t rp; 484 tok = expr_and(state, &rp); 485 if ((numeric(np) && np->num==0) || *np->str==0) 486 *np = rp; 487 } 488 return tok; 489 } 490 491 int 492 b_expr(int argc, char *argv[], void *context) 493 { 494 State_t state; 495 Node_t node; 496 int n; 497 498 cmdinit(argc, argv,context, ERROR_CATALOG, 0); 499 state.standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"); 500 #if 0 501 if (state.standard) 502 state.arglist = argv+1; 503 else 504 #endif 505 { 506 while (n=optget(argv, usage)) 507 { 508 /* 509 * NOTE: this loop ignores all but literal -- and -? 510 * out of kindness for obsolescent usage 511 * (and is ok with the standard) but strict 512 * getopt conformance would give usage for all 513 * unknown - options 514 */ 515 if(n=='?') 516 error(ERROR_usage(2), "%s", opt_info.arg); 517 if (opt_info.option[1] != '?') 518 break; 519 error(ERROR_usage(2), "%s", opt_info.arg); 520 } 521 if (error_info.errors) 522 error(ERROR_usage(2),"%s",optusage((char*)0)); 523 state.arglist = argv+opt_info.index; 524 } 525 if (expr_or(&state, &node)) 526 error(ERROR_exit(2),"syntax error"); 527 if (node.type&T_STR) 528 { 529 if (*node.str) 530 sfprintf(sfstdout,"%s\n",node.str); 531 } 532 else 533 sfprintf(sfstdout,"%d\n",node.num); 534 return numeric(&node)?node.num==0:*node.str==0; 535 } 536