14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.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[] =
308462SApril.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
getnode(State_t * state,Node_t * np)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);
2438462SApril.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
expr_cond(State_t * state,Node_t * np)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
expr_mult(State_t * state,Node_t * np)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
expr_add(State_t * state,Node_t * np)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
expr_cmp(State_t * state,Node_t * np)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
expr_and(State_t * state,Node_t * np)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
expr_or(State_t * state,Node_t * np)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
b_expr(int argc,char * argv[],void * context)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