summaryrefslogtreecommitdiff
path: root/usr/src/lib/libcmd/common/expr.c
diff options
context:
space:
mode:
authorchin <none@none>2007-08-17 12:01:52 -0700
committerchin <none@none>2007-08-17 12:01:52 -0700
commitda2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch)
tree5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libcmd/common/expr.c
parent073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff)
downloadillumos-gate-da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968.tar.gz
6437624 RFE: Add ksh93 (as /usr/bin/ksh93) and libshell.so to OS/Net
6505835 AST tools and library (libpp) required for creating l10n messages for ksh93 PSARC/2006/550 Korn Shell 93 Integration PSARC/2006/587 /etc/ksh.kshrc for ksh93 PSARC/2007/035 ksh93 Amendments Contributed by Roland Mainz <roland.mainz@nrubsig.org> --HG-- rename : usr/src/lib/libcmd/common/mapfile-vers => deleted_files/usr/src/lib/libcmd/common/mapfile-vers rename : usr/src/lib/libcmd/common/placeholder.c => deleted_files/usr/src/lib/libcmd/common/placeholder.c
Diffstat (limited to 'usr/src/lib/libcmd/common/expr.c')
-rw-r--r--usr/src/lib/libcmd/common/expr.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/usr/src/lib/libcmd/common/expr.c b/usr/src/lib/libcmd/common/expr.c
new file mode 100644
index 0000000000..1baee2435e
--- /dev/null
+++ b/usr/src/lib/libcmd/common/expr.c
@@ -0,0 +1,535 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * expr.c
+ * Written by David Korn
+ * Tue Oct 31 08:48:11 EST 1995
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: expr (AT&T Research) 2004-05-27 $\n]"
+USAGE_LICENSE
+"[+NAME?expr - evaluate arguments as an expression]"
+"[+DESCRIPTION?\bexpr\b evaluates an expression given as arguments and writes "
+ "the result to standard output. The character \b0\b will be written "
+ "to indicate a zero value and nothing will be written to indicate an "
+ "empty string.]"
+"[+?Most of the functionality of \bexpr\b is provided in a more natural "
+ "way by the shell, \bsh\b(1), and \bexpr\b is provided primarily "
+ "for backward compatibility.]"
+"[+?Terms of the expression must be separate arguments. A string argument is "
+ "one that can not be identified as an integer. Integer-valued "
+ "arguments may be preceded by a unary plus or minus sign. Because "
+ "many of the operators use characters that have special meaning to "
+ "the shell, they must be quoted when entered from the shell.]"
+
+"[+?Expressions are formed from the operators listed below in order "
+ "of increasing precedence within groups. All of the operators are "
+ "left associative. The symbols \aexpr1\a and \aexpr2\a represent "
+ "expressions formed from strings and integers and the following "
+ "operators:]{"
+ "[+\aexpr1\a \b|\b \aexpr2\a?Returns the evaluation of \aexpr1\a if "
+ "it is neither null nor 0, otherwise returns the evaluation of expr2.]"
+
+ "[+\aexpr1\a \b&\b \aexpr2\a?Returns the evaluation of \aexpr1\a if "
+ "neither expression evaluates to null or 0, otherwise returns 0.]"
+
+ "[+\aexpr1\a \aop\a \aexpr2\a?Returns the result of a decimal integer "
+ "comparison if both arguments are integers; otherwise, returns the "
+ "result of a string comparison using the locale-specific collation "
+ "sequence. The result of each comparison will be 1 if the specified "
+ "relationship is true, or 0 if the relationship is false. \aop\a "
+ "can be one of the following:]{"
+ "[+=?Equal.]"
+ "[+==?Equal.]"
+ "[+>?Greater than.]"
+ "[+>=?Greater than or equal to.]"
+ "[+<?Less than.]"
+ "[+<=?Less than or equal to.]"
+ "[+!=?Not equal to.]"
+ "}"
+
+ "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b+\b or \b-\b; "
+ "addition or subtraction of decimal integer-valued arguments.]"
+ "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b*\b, \b/\b or \b%\b; "
+ "multiplication, division, or remainder of the decimal "
+ "integer-valued arguments.]"
+ "[+\aexpr1\a \b::\b \aexpr2\a?The matching operator : compares "
+ "\aexpr1\a with \aexpr2\a, which must be a BRE. Normally, "
+ "the matching operator returns the number of bytes matched "
+ "and 0 on failure. However, if the pattern contains at "
+ "least one sub-expression [\\( . . .\\)]], the string "
+ "corresponding to \\1 will be returned.]"
+ "[+( \aexpr1\a )?Grouping symbols. An expression can "
+ "be placed within parenthesis to change precedence.]"
+ "[+match\b \astring\a \aexpr\a?Equivalent to \astring\a \b:\b "
+ "\aexpr\a.]"
+ "[+substr\b \astring\a \apos\a \alength\a?\alength\a character "
+ "substring of \astring\a starting at \apos\a "
+ "(counting from 1).]"
+ "[+index\b \astring\a \achars\a?The position in \astring\a "
+ "(counting from 1) of the leftmost occurrence of any "
+ "character in \achars\a.]"
+ "[+length\b \astring\a?The number of characters in \astring\a.]"
+ "[+quote\b \atoken\a?Treat \atoken\a as a string operand.]"
+ "}"
+"[+?For backwards compatibility, unrecognized options beginning with "
+ "a \b-\b will be treated as operands. Portable applications "
+ "should use \b--\b to indicate end of options.]"
+
+"\n"
+"\n operand ...\n"
+"\n"
+
+"[+EXIT STATUS?]{"
+ "[+0?The expression is neither null nor 0.]"
+ "[+1?The expression is null or 0.]"
+ "[+2?Invalid expressions.]"
+ "[+>2?An error occurred.]"
+ "}"
+"[+SEE ALSO?\bregcomp\b(5), \bgrep\b(1), \bsh\b(1)]"
+;
+
+#include <cmd.h>
+#include <regex.h>
+
+#define T_ADD 0x100
+#define T_MULT 0x200
+#define T_CMP 0x400
+#define T_FUN 0x800
+#define T_OP 7
+#define T_NUM 1
+#define T_STR 2
+
+#define OP_EQ (T_CMP|0)
+#define OP_GT (T_CMP|1)
+#define OP_LT (T_CMP|2)
+#define OP_GE (T_CMP|3)
+#define OP_LE (T_CMP|4)
+#define OP_NE (T_CMP|5)
+#define OP_PLUS (T_ADD|0)
+#define OP_MINUS (T_ADD|1)
+#define OP_MULT (T_MULT|0)
+#define OP_DIV (T_MULT|1)
+#define OP_MOD (T_MULT|2)
+#define OP_INDEX (T_FUN|0)
+#define OP_LENGTH (T_FUN|1)
+#define OP_MATCH (T_FUN|2)
+#define OP_QUOTE (T_FUN|3)
+#define OP_SUBSTR (T_FUN|4)
+
+#define numeric(np) ((np)->type&T_NUM)
+
+static const struct Optable_s
+{
+ const char opname[3];
+ int op;
+}
+optable[] =
+{
+ "|", '|',
+ "&", '&',
+ "=", OP_EQ,
+ "==", OP_EQ,
+ ">", OP_GT,
+ "<", OP_LT,
+ ">=", OP_GE,
+ "<=", OP_LE,
+ "!=", OP_NE,
+ "+", OP_PLUS,
+ "-", OP_MINUS,
+ "*", OP_MULT,
+ "/", OP_DIV,
+ "%", OP_MOD,
+ ":", ':',
+ "(", '(',
+ ")", ')'
+};
+
+typedef struct Node_s
+{
+ int type;
+ long num;
+ char *str;
+} Node_t;
+
+typedef struct State_s
+{
+ int standard;
+ char** arglist;
+ char buf[36];
+} State_t;
+
+static int expr_or(State_t*, Node_t*);
+
+static int getnode(State_t* state, Node_t *np)
+{
+ register char* sp;
+ register char* cp;
+ register int i;
+ register int j;
+ register int k;
+ register int tok;
+ char* ep;
+
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "argument expected");
+ if (!state->standard)
+ switch (cp[0])
+ {
+ case 'i':
+ if (cp[1] == 'n' && !strcmp(cp, "index"))
+ {
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ if (!(ep = *state->arglist++))
+ error(ERROR_exit(2), "chars argument expected");
+ np->num = (ep = strpbrk(cp, ep)) ? (ep - cp + 1) : 0;
+ np->type = T_NUM;
+ goto next;
+ }
+ break;
+ case 'l':
+ if (cp[1] == 'e' && !strcmp(cp, "length"))
+ {
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ np->num = strlen(cp);
+ np->type = T_NUM;
+ goto next;
+ }
+ break;
+ case 'm':
+ if (cp[1] == 'a' && !strcmp(cp, "match"))
+ {
+ if (!(np->str = *state->arglist++))
+ error(ERROR_exit(2), "pattern argument expected");
+ np->type = T_STR;
+ return ':';
+ }
+ break;
+ case 'q':
+ if (cp[1] == 'u' && !strcmp(cp, "quote") && !(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ break;
+ case 's':
+ if (cp[1] == 'u' && !strcmp(cp, "substr"))
+ {
+ if (!(sp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "position argument expected");
+ i = strtol(cp, &ep, 10);
+ if (*ep || --i <= 0)
+ i = -1;
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "length argument expected");
+ j = strtol(cp, &ep, 10);
+ if (*ep)
+ j = -1;
+ k = strlen(sp);
+ if (i < 0 || i >= k || j < 0)
+ sp = "";
+ else
+ {
+ sp += i;
+ k -= i;
+ if (j < k)
+ sp[j] = 0;
+ }
+ np->type = T_STR;
+ np->str = sp;
+ goto next;
+ }
+ break;
+ }
+ if (*cp=='(' && cp[1]==0)
+ {
+ tok = expr_or(state, np);
+ if (tok != ')')
+ error(ERROR_exit(2),"closing parenthesis missing");
+ }
+ else
+ {
+ np->type = T_STR;
+ np->str = cp;
+ if (*cp)
+ {
+ np->num = strtol(np->str,&ep,10);
+ if (!*ep)
+ np->type |= T_NUM;
+ }
+ }
+ next:
+ if (!(cp = *state->arglist))
+ return 0;
+ state->arglist++;
+ for (i=0; i < sizeof(optable)/sizeof(*optable); i++)
+ if (*cp==optable[i].opname[0] && cp[1]==optable[i].opname[1])
+ return optable[i].op;
+ error(ERROR_exit(2),"%s: unknown operator argument",cp);
+ return 0;
+}
+
+static int expr_cond(State_t* state, Node_t *np)
+{
+ register int tok = getnode(state, np);
+
+ while (tok==':')
+ {
+ regex_t re;
+ regmatch_t match[2];
+ int n;
+ Node_t rp;
+ char *cp;
+ tok = getnode(state, &rp);
+ if (np->type&T_STR)
+ cp = np->str;
+ else
+ sfsprintf(cp=state->buf,sizeof(state->buf),"%d",np->num);
+ np->num = 0;
+ np->type = T_NUM;
+ if (n = regcomp(&re, rp.str, REG_LEFT|REG_LENIENT))
+ regfatal(&re, ERROR_exit(2), n);
+ if (!(n = regexec(&re, cp, elementsof(match), match, 0)))
+ {
+ if (re.re_nsub > 0)
+ {
+ np->type = T_STR;
+ if (match[1].rm_so >= 0)
+ {
+ np->str = cp + match[1].rm_so;
+ np->str[match[1].rm_eo - match[1].rm_so] = 0;
+ np->num = strtol(np->str,&cp,10);
+ if (cp!=np->str && *cp==0)
+ np->type |= T_NUM;
+ }
+ else
+ np->str = "";
+ }
+ else
+ np->num = match[0].rm_eo - match[0].rm_so;
+ }
+ else if (n != REG_NOMATCH)
+ regfatal(&re, ERROR_exit(2), n);
+ else if (re.re_nsub)
+ {
+ np->str = "";
+ np->type = T_STR;
+ }
+ regfree(&re);
+ }
+ return tok;
+}
+
+static int expr_mult(State_t* state, Node_t *np)
+{
+ register int tok = expr_cond(state, np);
+
+ while ((tok&~T_OP)==T_MULT)
+ {
+ Node_t rp;
+ int op = (tok&T_OP);
+ tok = expr_cond(state, &rp);
+ if (!numeric(np) || !numeric(&rp))
+ error(ERROR_exit(2),"non-numeric argument");
+ if (op && rp.num==0)
+ error(ERROR_exit(2),"division by zero");
+ switch(op)
+ {
+ case 0:
+ np->num *= rp.num;
+ break;
+ case 1:
+ np->num /= rp.num;
+ break;
+ case 2:
+ np->num %= rp.num;
+ }
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_add(State_t* state, Node_t *np)
+{
+ register int tok = expr_mult(state, np);
+
+ while ((tok&~T_OP)==T_ADD)
+ {
+ Node_t rp;
+ int op = (tok&T_OP);
+ tok = expr_mult(state, &rp);
+ if (!numeric(np) || !numeric(&rp))
+ error(ERROR_exit(2),"non-numeric argument");
+ if (op)
+ np->num -= rp.num;
+ else
+ np->num += rp.num;
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_cmp(State_t* state, Node_t *np)
+{
+ register int tok = expr_add(state, np);
+
+ while ((tok&~T_OP)==T_CMP)
+ {
+ Node_t rp;
+ register char *left,*right;
+ char buff1[36],buff2[36];
+ int op = (tok&T_OP);
+ tok = expr_add(state, &rp);
+ if (numeric(&rp) && numeric(np))
+ op |= 010;
+ else
+ {
+ if (np->type&T_STR)
+ left = np->str;
+ else
+ sfsprintf(left=buff1,sizeof(buff1),"%d",np->num);
+ if (rp.type&T_STR)
+ right = rp.str;
+ else
+ sfsprintf(right=buff2,sizeof(buff2),"%d",rp.num);
+ }
+ switch(op)
+ {
+ case 0:
+ np->num = streq(left,right);
+ break;
+ case 1:
+ np->num = (strcoll(left,right)>0);
+ break;
+ case 2:
+ np->num = (strcoll(left,right)<0);
+ break;
+ case 3:
+ np->num = (strcoll(left,right)>=0);
+ break;
+ case 4:
+ np->num = (strcoll(left,right)<=0);
+ break;
+ case 5:
+ np->num = !streq(left,right);
+ break;
+ case 010:
+ np->num = (np->num==rp.num);
+ break;
+ case 011:
+ np->num = (np->num>rp.num);
+ break;
+ case 012:
+ np->num = (np->num<rp.num);
+ break;
+ case 013:
+ np->num = (np->num>=rp.num);
+ break;
+ case 014:
+ np->num = (np->num<=rp.num);
+ break;
+ case 015:
+ np->num = (np->num!=rp.num);
+ break;
+ }
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_and(State_t* state, Node_t *np)
+{
+ register int tok = expr_cmp(state, np);
+ while (tok=='&')
+ {
+ Node_t rp;
+ tok = expr_cmp(state, &rp);
+ if ((numeric(&rp) && rp.num==0) || *rp.str==0)
+ {
+ np->num = 0;
+ np->type=T_NUM;
+ }
+ }
+ return tok;
+}
+
+static int expr_or(State_t* state, Node_t *np)
+{
+ register int tok = expr_and(state, np);
+ while (tok=='|')
+ {
+ Node_t rp;
+ tok = expr_and(state, &rp);
+ if ((numeric(np) && np->num==0) || *np->str==0)
+ *np = rp;
+ }
+ return tok;
+}
+
+int
+b_expr(int argc, char *argv[], void *context)
+{
+ State_t state;
+ Node_t node;
+ int n;
+
+ cmdinit(argc, argv,context, ERROR_CATALOG, 0);
+ state.standard = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
+#if 0
+ if (state.standard)
+ state.arglist = argv+1;
+ else
+#endif
+ {
+ while (n=optget(argv, usage))
+ {
+ /*
+ * NOTE: this loop ignores all but literal -- and -?
+ * out of kindness for obsolescent usage
+ * (and is ok with the standard) but strict
+ * getopt conformance would give usage for all
+ * unknown - options
+ */
+ if(n=='?')
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ if (opt_info.option[1] != '?')
+ break;
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ }
+ if (error_info.errors)
+ error(ERROR_usage(2),"%s",optusage((char*)0));
+ state.arglist = argv+opt_info.index;
+ }
+ if (expr_or(&state, &node))
+ error(ERROR_exit(2),"syntax error");
+ if (node.type&T_STR)
+ {
+ if (*node.str)
+ sfprintf(sfstdout,"%s\n",node.str);
+ }
+ else
+ sfprintf(sfstdout,"%d\n",node.num);
+ return numeric(&node)?node.num==0:*node.str==0;
+}