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