14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1992-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin 234887Schin /* 244887Schin * expr.c 254887Schin * Written by David Korn 264887Schin * Tue Oct 31 08:48:11 EST 1995 274887Schin */ 284887Schin 294887Schin static const char usage[] = 30*8462SApril.Chin@Sun.COM "[-?\n@(#)$Id: expr (AT&T Research) 2008-01-30 $\n]" 314887Schin USAGE_LICENSE 324887Schin "[+NAME?expr - evaluate arguments as an expression]" 334887Schin "[+DESCRIPTION?\bexpr\b evaluates an expression given as arguments and writes " 344887Schin "the result to standard output. The character \b0\b will be written " 354887Schin "to indicate a zero value and nothing will be written to indicate an " 364887Schin "empty string.]" 374887Schin "[+?Most of the functionality of \bexpr\b is provided in a more natural " 384887Schin "way by the shell, \bsh\b(1), and \bexpr\b is provided primarily " 394887Schin "for backward compatibility.]" 404887Schin "[+?Terms of the expression must be separate arguments. A string argument is " 414887Schin "one that can not be identified as an integer. Integer-valued " 424887Schin "arguments may be preceded by a unary plus or minus sign. Because " 434887Schin "many of the operators use characters that have special meaning to " 444887Schin "the shell, they must be quoted when entered from the shell.]" 454887Schin 464887Schin "[+?Expressions are formed from the operators listed below in order " 474887Schin "of increasing precedence within groups. All of the operators are " 484887Schin "left associative. The symbols \aexpr1\a and \aexpr2\a represent " 494887Schin "expressions formed from strings and integers and the following " 504887Schin "operators:]{" 514887Schin "[+\aexpr1\a \b|\b \aexpr2\a?Returns the evaluation of \aexpr1\a if " 524887Schin "it is neither null nor 0, otherwise returns the evaluation of expr2.]" 534887Schin 544887Schin "[+\aexpr1\a \b&\b \aexpr2\a?Returns the evaluation of \aexpr1\a if " 554887Schin "neither expression evaluates to null or 0, otherwise returns 0.]" 564887Schin 574887Schin "[+\aexpr1\a \aop\a \aexpr2\a?Returns the result of a decimal integer " 584887Schin "comparison if both arguments are integers; otherwise, returns the " 594887Schin "result of a string comparison using the locale-specific collation " 604887Schin "sequence. The result of each comparison will be 1 if the specified " 614887Schin "relationship is true, or 0 if the relationship is false. \aop\a " 624887Schin "can be one of the following:]{" 634887Schin "[+=?Equal.]" 644887Schin "[+==?Equal.]" 654887Schin "[+>?Greater than.]" 664887Schin "[+>=?Greater than or equal to.]" 674887Schin "[+<?Less than.]" 684887Schin "[+<=?Less than or equal to.]" 694887Schin "[+!=?Not equal to.]" 704887Schin "}" 714887Schin 724887Schin "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b+\b or \b-\b; " 734887Schin "addition or subtraction of decimal integer-valued arguments.]" 744887Schin "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b*\b, \b/\b or \b%\b; " 754887Schin "multiplication, division, or remainder of the decimal " 764887Schin "integer-valued arguments.]" 774887Schin "[+\aexpr1\a \b::\b \aexpr2\a?The matching operator : compares " 784887Schin "\aexpr1\a with \aexpr2\a, which must be a BRE. Normally, " 794887Schin "the matching operator returns the number of bytes matched " 804887Schin "and 0 on failure. However, if the pattern contains at " 814887Schin "least one sub-expression [\\( . . .\\)]], the string " 824887Schin "corresponding to \\1 will be returned.]" 834887Schin "[+( \aexpr1\a )?Grouping symbols. An expression can " 844887Schin "be placed within parenthesis to change precedence.]" 854887Schin "[+match\b \astring\a \aexpr\a?Equivalent to \astring\a \b:\b " 864887Schin "\aexpr\a.]" 874887Schin "[+substr\b \astring\a \apos\a \alength\a?\alength\a character " 884887Schin "substring of \astring\a starting at \apos\a " 894887Schin "(counting from 1).]" 904887Schin "[+index\b \astring\a \achars\a?The position in \astring\a " 914887Schin "(counting from 1) of the leftmost occurrence of any " 924887Schin "character in \achars\a.]" 934887Schin "[+length\b \astring\a?The number of characters in \astring\a.]" 944887Schin "[+quote\b \atoken\a?Treat \atoken\a as a string operand.]" 954887Schin "}" 964887Schin "[+?For backwards compatibility, unrecognized options beginning with " 974887Schin "a \b-\b will be treated as operands. Portable applications " 984887Schin "should use \b--\b to indicate end of options.]" 994887Schin 1004887Schin "\n" 1014887Schin "\n operand ...\n" 1024887Schin "\n" 1034887Schin 1044887Schin "[+EXIT STATUS?]{" 1054887Schin "[+0?The expression is neither null nor 0.]" 1064887Schin "[+1?The expression is null or 0.]" 1074887Schin "[+2?Invalid expressions.]" 1084887Schin "[+>2?An error occurred.]" 1094887Schin "}" 1104887Schin "[+SEE ALSO?\bregcomp\b(5), \bgrep\b(1), \bsh\b(1)]" 1114887Schin ; 1124887Schin 1134887Schin #include <cmd.h> 1144887Schin #include <regex.h> 1154887Schin 1164887Schin #define T_ADD 0x100 1174887Schin #define T_MULT 0x200 1184887Schin #define T_CMP 0x400 1194887Schin #define T_FUN 0x800 1204887Schin #define T_OP 7 1214887Schin #define T_NUM 1 1224887Schin #define T_STR 2 1234887Schin 1244887Schin #define OP_EQ (T_CMP|0) 1254887Schin #define OP_GT (T_CMP|1) 1264887Schin #define OP_LT (T_CMP|2) 1274887Schin #define OP_GE (T_CMP|3) 1284887Schin #define OP_LE (T_CMP|4) 1294887Schin #define OP_NE (T_CMP|5) 1304887Schin #define OP_PLUS (T_ADD|0) 1314887Schin #define OP_MINUS (T_ADD|1) 1324887Schin #define OP_MULT (T_MULT|0) 1334887Schin #define OP_DIV (T_MULT|1) 1344887Schin #define OP_MOD (T_MULT|2) 1354887Schin #define OP_INDEX (T_FUN|0) 1364887Schin #define OP_LENGTH (T_FUN|1) 1374887Schin #define OP_MATCH (T_FUN|2) 1384887Schin #define OP_QUOTE (T_FUN|3) 1394887Schin #define OP_SUBSTR (T_FUN|4) 1404887Schin 1414887Schin #define numeric(np) ((np)->type&T_NUM) 1424887Schin 1434887Schin static const struct Optable_s 1444887Schin { 1454887Schin const char opname[3]; 1464887Schin int op; 1474887Schin } 1484887Schin optable[] = 1494887Schin { 1504887Schin "|", '|', 1514887Schin "&", '&', 1524887Schin "=", OP_EQ, 1534887Schin "==", OP_EQ, 1544887Schin ">", OP_GT, 1554887Schin "<", OP_LT, 1564887Schin ">=", OP_GE, 1574887Schin "<=", OP_LE, 1584887Schin "!=", OP_NE, 1594887Schin "+", OP_PLUS, 1604887Schin "-", OP_MINUS, 1614887Schin "*", OP_MULT, 1624887Schin "/", OP_DIV, 1634887Schin "%", OP_MOD, 1644887Schin ":", ':', 1654887Schin "(", '(', 1664887Schin ")", ')' 1674887Schin }; 1684887Schin 1694887Schin typedef struct Node_s 1704887Schin { 1714887Schin int type; 1724887Schin long num; 1734887Schin char *str; 1744887Schin } Node_t; 1754887Schin 1764887Schin typedef struct State_s 1774887Schin { 1784887Schin int standard; 1794887Schin char** arglist; 1804887Schin char buf[36]; 1814887Schin } State_t; 1824887Schin 1834887Schin static int expr_or(State_t*, Node_t*); 1844887Schin 1854887Schin static int getnode(State_t* state, Node_t *np) 1864887Schin { 1874887Schin register char* sp; 1884887Schin register char* cp; 1894887Schin register int i; 1904887Schin register int j; 1914887Schin register int k; 1924887Schin register int tok; 1934887Schin char* ep; 1944887Schin 1954887Schin if (!(cp = *state->arglist++)) 1964887Schin error(ERROR_exit(2), "argument expected"); 1974887Schin if (!state->standard) 1984887Schin switch (cp[0]) 1994887Schin { 2004887Schin case 'i': 2014887Schin if (cp[1] == 'n' && !strcmp(cp, "index")) 2024887Schin { 2034887Schin if (!(cp = *state->arglist++)) 2044887Schin error(ERROR_exit(2), "string argument expected"); 2054887Schin if (!(ep = *state->arglist++)) 2064887Schin error(ERROR_exit(2), "chars argument expected"); 2074887Schin np->num = (ep = strpbrk(cp, ep)) ? (ep - cp + 1) : 0; 2084887Schin np->type = T_NUM; 2094887Schin goto next; 2104887Schin } 2114887Schin break; 2124887Schin case 'l': 2134887Schin if (cp[1] == 'e' && !strcmp(cp, "length")) 2144887Schin { 2154887Schin if (!(cp = *state->arglist++)) 2164887Schin error(ERROR_exit(2), "string argument expected"); 2174887Schin np->num = strlen(cp); 2184887Schin np->type = T_NUM; 2194887Schin goto next; 2204887Schin } 2214887Schin break; 2224887Schin case 'm': 2234887Schin if (cp[1] == 'a' && !strcmp(cp, "match")) 2244887Schin { 2254887Schin if (!(np->str = *state->arglist++)) 2264887Schin error(ERROR_exit(2), "pattern argument expected"); 2274887Schin np->type = T_STR; 2284887Schin return ':'; 2294887Schin } 2304887Schin break; 2314887Schin case 'q': 2324887Schin if (cp[1] == 'u' && !strcmp(cp, "quote") && !(cp = *state->arglist++)) 2334887Schin error(ERROR_exit(2), "string argument expected"); 2344887Schin break; 2354887Schin case 's': 2364887Schin if (cp[1] == 'u' && !strcmp(cp, "substr")) 2374887Schin { 2384887Schin if (!(sp = *state->arglist++)) 2394887Schin error(ERROR_exit(2), "string argument expected"); 2404887Schin if (!(cp = *state->arglist++)) 2414887Schin error(ERROR_exit(2), "position argument expected"); 2424887Schin i = strtol(cp, &ep, 10); 243*8462SApril.Chin@Sun.COM if (*ep || --i < 0) 2444887Schin i = -1; 2454887Schin if (!(cp = *state->arglist++)) 2464887Schin error(ERROR_exit(2), "length argument expected"); 2474887Schin j = strtol(cp, &ep, 10); 2484887Schin if (*ep) 2494887Schin j = -1; 2504887Schin k = strlen(sp); 2514887Schin if (i < 0 || i >= k || j < 0) 2524887Schin sp = ""; 2534887Schin else 2544887Schin { 2554887Schin sp += i; 2564887Schin k -= i; 2574887Schin if (j < k) 2584887Schin sp[j] = 0; 2594887Schin } 2604887Schin np->type = T_STR; 2614887Schin np->str = sp; 2624887Schin goto next; 2634887Schin } 2644887Schin break; 2654887Schin } 2664887Schin if (*cp=='(' && cp[1]==0) 2674887Schin { 2684887Schin tok = expr_or(state, np); 2694887Schin if (tok != ')') 2704887Schin error(ERROR_exit(2),"closing parenthesis missing"); 2714887Schin } 2724887Schin else 2734887Schin { 2744887Schin np->type = T_STR; 2754887Schin np->str = cp; 2764887Schin if (*cp) 2774887Schin { 2784887Schin np->num = strtol(np->str,&ep,10); 2794887Schin if (!*ep) 2804887Schin np->type |= T_NUM; 2814887Schin } 2824887Schin } 2834887Schin next: 2844887Schin if (!(cp = *state->arglist)) 2854887Schin return 0; 2864887Schin state->arglist++; 2874887Schin for (i=0; i < sizeof(optable)/sizeof(*optable); i++) 2884887Schin if (*cp==optable[i].opname[0] && cp[1]==optable[i].opname[1]) 2894887Schin return optable[i].op; 2904887Schin error(ERROR_exit(2),"%s: unknown operator argument",cp); 2914887Schin return 0; 2924887Schin } 2934887Schin 2944887Schin static int expr_cond(State_t* state, Node_t *np) 2954887Schin { 2964887Schin register int tok = getnode(state, np); 2974887Schin 2984887Schin while (tok==':') 2994887Schin { 3004887Schin regex_t re; 3014887Schin regmatch_t match[2]; 3024887Schin int n; 3034887Schin Node_t rp; 3044887Schin char *cp; 3054887Schin tok = getnode(state, &rp); 3064887Schin if (np->type&T_STR) 3074887Schin cp = np->str; 3084887Schin else 3094887Schin sfsprintf(cp=state->buf,sizeof(state->buf),"%d",np->num); 3104887Schin np->num = 0; 3114887Schin np->type = T_NUM; 3124887Schin if (n = regcomp(&re, rp.str, REG_LEFT|REG_LENIENT)) 3134887Schin regfatal(&re, ERROR_exit(2), n); 3144887Schin if (!(n = regexec(&re, cp, elementsof(match), match, 0))) 3154887Schin { 3164887Schin if (re.re_nsub > 0) 3174887Schin { 3184887Schin np->type = T_STR; 3194887Schin if (match[1].rm_so >= 0) 3204887Schin { 3214887Schin np->str = cp + match[1].rm_so; 3224887Schin np->str[match[1].rm_eo - match[1].rm_so] = 0; 3234887Schin np->num = strtol(np->str,&cp,10); 3244887Schin if (cp!=np->str && *cp==0) 3254887Schin np->type |= T_NUM; 3264887Schin } 3274887Schin else 3284887Schin np->str = ""; 3294887Schin } 3304887Schin else 3314887Schin np->num = match[0].rm_eo - match[0].rm_so; 3324887Schin } 3334887Schin else if (n != REG_NOMATCH) 3344887Schin regfatal(&re, ERROR_exit(2), n); 3354887Schin else if (re.re_nsub) 3364887Schin { 3374887Schin np->str = ""; 3384887Schin np->type = T_STR; 3394887Schin } 3404887Schin regfree(&re); 3414887Schin } 3424887Schin return tok; 3434887Schin } 3444887Schin 3454887Schin static int expr_mult(State_t* state, Node_t *np) 3464887Schin { 3474887Schin register int tok = expr_cond(state, np); 3484887Schin 3494887Schin while ((tok&~T_OP)==T_MULT) 3504887Schin { 3514887Schin Node_t rp; 3524887Schin int op = (tok&T_OP); 3534887Schin tok = expr_cond(state, &rp); 3544887Schin if (!numeric(np) || !numeric(&rp)) 3554887Schin error(ERROR_exit(2),"non-numeric argument"); 3564887Schin if (op && rp.num==0) 3574887Schin error(ERROR_exit(2),"division by zero"); 3584887Schin switch(op) 3594887Schin { 3604887Schin case 0: 3614887Schin np->num *= rp.num; 3624887Schin break; 3634887Schin case 1: 3644887Schin np->num /= rp.num; 3654887Schin break; 3664887Schin case 2: 3674887Schin np->num %= rp.num; 3684887Schin } 3694887Schin np->type = T_NUM; 3704887Schin } 3714887Schin return tok; 3724887Schin } 3734887Schin 3744887Schin static int expr_add(State_t* state, Node_t *np) 3754887Schin { 3764887Schin register int tok = expr_mult(state, np); 3774887Schin 3784887Schin while ((tok&~T_OP)==T_ADD) 3794887Schin { 3804887Schin Node_t rp; 3814887Schin int op = (tok&T_OP); 3824887Schin tok = expr_mult(state, &rp); 3834887Schin if (!numeric(np) || !numeric(&rp)) 3844887Schin error(ERROR_exit(2),"non-numeric argument"); 3854887Schin if (op) 3864887Schin np->num -= rp.num; 3874887Schin else 3884887Schin np->num += rp.num; 3894887Schin np->type = T_NUM; 3904887Schin } 3914887Schin return tok; 3924887Schin } 3934887Schin 3944887Schin static int expr_cmp(State_t* state, Node_t *np) 3954887Schin { 3964887Schin register int tok = expr_add(state, np); 3974887Schin 3984887Schin while ((tok&~T_OP)==T_CMP) 3994887Schin { 4004887Schin Node_t rp; 4014887Schin register char *left,*right; 4024887Schin char buff1[36],buff2[36]; 4034887Schin int op = (tok&T_OP); 4044887Schin tok = expr_add(state, &rp); 4054887Schin if (numeric(&rp) && numeric(np)) 4064887Schin op |= 010; 4074887Schin else 4084887Schin { 4094887Schin if (np->type&T_STR) 4104887Schin left = np->str; 4114887Schin else 4124887Schin sfsprintf(left=buff1,sizeof(buff1),"%d",np->num); 4134887Schin if (rp.type&T_STR) 4144887Schin right = rp.str; 4154887Schin else 4164887Schin sfsprintf(right=buff2,sizeof(buff2),"%d",rp.num); 4174887Schin } 4184887Schin switch(op) 4194887Schin { 4204887Schin case 0: 4214887Schin np->num = streq(left,right); 4224887Schin break; 4234887Schin case 1: 4244887Schin np->num = (strcoll(left,right)>0); 4254887Schin break; 4264887Schin case 2: 4274887Schin np->num = (strcoll(left,right)<0); 4284887Schin break; 4294887Schin case 3: 4304887Schin np->num = (strcoll(left,right)>=0); 4314887Schin break; 4324887Schin case 4: 4334887Schin np->num = (strcoll(left,right)<=0); 4344887Schin break; 4354887Schin case 5: 4364887Schin np->num = !streq(left,right); 4374887Schin break; 4384887Schin case 010: 4394887Schin np->num = (np->num==rp.num); 4404887Schin break; 4414887Schin case 011: 4424887Schin np->num = (np->num>rp.num); 4434887Schin break; 4444887Schin case 012: 4454887Schin np->num = (np->num<rp.num); 4464887Schin break; 4474887Schin case 013: 4484887Schin np->num = (np->num>=rp.num); 4494887Schin break; 4504887Schin case 014: 4514887Schin np->num = (np->num<=rp.num); 4524887Schin break; 4534887Schin case 015: 4544887Schin np->num = (np->num!=rp.num); 4554887Schin break; 4564887Schin } 4574887Schin np->type = T_NUM; 4584887Schin } 4594887Schin return tok; 4604887Schin } 4614887Schin 4624887Schin static int expr_and(State_t* state, Node_t *np) 4634887Schin { 4644887Schin register int tok = expr_cmp(state, np); 4654887Schin while (tok=='&') 4664887Schin { 4674887Schin Node_t rp; 4684887Schin tok = expr_cmp(state, &rp); 4694887Schin if ((numeric(&rp) && rp.num==0) || *rp.str==0) 4704887Schin { 4714887Schin np->num = 0; 4724887Schin np->type=T_NUM; 4734887Schin } 4744887Schin } 4754887Schin return tok; 4764887Schin } 4774887Schin 4784887Schin static int expr_or(State_t* state, Node_t *np) 4794887Schin { 4804887Schin register int tok = expr_and(state, np); 4814887Schin while (tok=='|') 4824887Schin { 4834887Schin Node_t rp; 4844887Schin tok = expr_and(state, &rp); 4854887Schin if ((numeric(np) && np->num==0) || *np->str==0) 4864887Schin *np = rp; 4874887Schin } 4884887Schin return tok; 4894887Schin } 4904887Schin 4914887Schin int 4924887Schin b_expr(int argc, char *argv[], void *context) 4934887Schin { 4944887Schin State_t state; 4954887Schin Node_t node; 4964887Schin int n; 4974887Schin 4984887Schin cmdinit(argc, argv,context, ERROR_CATALOG, 0); 4994887Schin state.standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard"); 5004887Schin #if 0 5014887Schin if (state.standard) 5024887Schin state.arglist = argv+1; 5034887Schin else 5044887Schin #endif 5054887Schin { 5064887Schin while (n=optget(argv, usage)) 5074887Schin { 5084887Schin /* 5094887Schin * NOTE: this loop ignores all but literal -- and -? 5104887Schin * out of kindness for obsolescent usage 5114887Schin * (and is ok with the standard) but strict 5124887Schin * getopt conformance would give usage for all 5134887Schin * unknown - options 5144887Schin */ 5154887Schin if(n=='?') 5164887Schin error(ERROR_usage(2), "%s", opt_info.arg); 5174887Schin if (opt_info.option[1] != '?') 5184887Schin break; 5194887Schin error(ERROR_usage(2), "%s", opt_info.arg); 5204887Schin } 5214887Schin if (error_info.errors) 5224887Schin error(ERROR_usage(2),"%s",optusage((char*)0)); 5234887Schin state.arglist = argv+opt_info.index; 5244887Schin } 5254887Schin if (expr_or(&state, &node)) 5264887Schin error(ERROR_exit(2),"syntax error"); 5274887Schin if (node.type&T_STR) 5284887Schin { 5294887Schin if (*node.str) 5304887Schin sfprintf(sfstdout,"%s\n",node.str); 5314887Schin } 5324887Schin else 5334887Schin sfprintf(sfstdout,"%d\n",node.num); 5344887Schin return numeric(&node)?node.num==0:*node.str==0; 5354887Schin } 536