1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
25 * Written by David Korn
26 * Tue Oct 31 08:48:11 EST 1995
29 static const char usage
[] =
30 "[-?\n@(#)$Id: expr (AT&T Research) 2008-01-30 $\n]"
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 "
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.]"
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 "
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.]"
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.]"
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:]{"
66 "[+>=?Greater than or equal to.]"
68 "[+<=?Less than or equal to.]"
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 "
87 "[+substr\b \astring\a \apos\a \alength\a?\alength\a character "
88 "substring of \astring\a starting at \apos\a "
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.]"
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.]"
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.]"
110 "[+SEE ALSO?\bregcomp\b(5), \bgrep\b(1), \bsh\b(1)]"
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)
141 #define numeric(np) ((np)->type&T_NUM)
143 static const struct Optable_s
145 const char opname
[3];
169 typedef struct Node_s
176 typedef struct State_s
183 static int expr_or(State_t
*, Node_t
*);
185 static int getnode(State_t
* state
, Node_t
*np
)
195 if (!(cp
= *state
->arglist
++))
196 error(ERROR_exit(2), "argument expected");
197 if (!state
->standard
)
201 if (cp
[1] == 'n' && !strcmp(cp
, "index"))
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;
213 if (cp
[1] == 'e' && !strcmp(cp
, "length"))
215 if (!(cp
= *state
->arglist
++))
216 error(ERROR_exit(2), "string argument expected");
217 np
->num
= strlen(cp
);
223 if (cp
[1] == 'a' && !strcmp(cp
, "match"))
225 if (!(np
->str
= *state
->arglist
++))
226 error(ERROR_exit(2), "pattern argument expected");
232 if (cp
[1] == 'u' && !strcmp(cp
, "quote") && !(cp
= *state
->arglist
++))
233 error(ERROR_exit(2), "string argument expected");
236 if (cp
[1] == 'u' && !strcmp(cp
, "substr"))
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);
245 if (!(cp
= *state
->arglist
++))
246 error(ERROR_exit(2), "length argument expected");
247 j
= strtol(cp
, &ep
, 10);
251 if (i
< 0 || i
>= k
|| j
< 0)
266 if (*cp
=='(' && cp
[1]==0)
268 tok
= expr_or(state
, np
);
270 error(ERROR_exit(2),"closing parenthesis missing");
278 np
->num
= strtol(np
->str
,&ep
,10);
284 if (!(cp
= *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
);
294 static int expr_cond(State_t
* state
, Node_t
*np
)
296 register int tok
= getnode(state
, np
);
305 tok
= getnode(state
, &rp
);
309 sfsprintf(cp
=state
->buf
,sizeof(state
->buf
),"%d",np
->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)))
319 if (match
[1].rm_so
>= 0)
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)
331 np
->num
= match
[0].rm_eo
- match
[0].rm_so
;
333 else if (n
!= REG_NOMATCH
)
334 regfatal(&re
, ERROR_exit(2), n
);
345 static int expr_mult(State_t
* state
, Node_t
*np
)
347 register int tok
= expr_cond(state
, np
);
349 while ((tok
&~T_OP
)==T_MULT
)
353 tok
= expr_cond(state
, &rp
);
354 if (!numeric(np
) || !numeric(&rp
))
355 error(ERROR_exit(2),"non-numeric argument");
357 error(ERROR_exit(2),"division by zero");
374 static int expr_add(State_t
* state
, Node_t
*np
)
376 register int tok
= expr_mult(state
, np
);
378 while ((tok
&~T_OP
)==T_ADD
)
382 tok
= expr_mult(state
, &rp
);
383 if (!numeric(np
) || !numeric(&rp
))
384 error(ERROR_exit(2),"non-numeric argument");
394 static int expr_cmp(State_t
* state
, Node_t
*np
)
396 register int tok
= expr_add(state
, np
);
398 while ((tok
&~T_OP
)==T_CMP
)
401 register char *left
,*right
;
402 char buff1
[36],buff2
[36];
404 tok
= expr_add(state
, &rp
);
405 if (numeric(&rp
) && numeric(np
))
412 sfsprintf(left
=buff1
,sizeof(buff1
),"%d",np
->num
);
416 sfsprintf(right
=buff2
,sizeof(buff2
),"%d",rp
.num
);
421 np
->num
= streq(left
,right
);
424 np
->num
= (strcoll(left
,right
)>0);
427 np
->num
= (strcoll(left
,right
)<0);
430 np
->num
= (strcoll(left
,right
)>=0);
433 np
->num
= (strcoll(left
,right
)<=0);
436 np
->num
= !streq(left
,right
);
439 np
->num
= (np
->num
==rp
.num
);
442 np
->num
= (np
->num
>rp
.num
);
445 np
->num
= (np
->num
<rp
.num
);
448 np
->num
= (np
->num
>=rp
.num
);
451 np
->num
= (np
->num
<=rp
.num
);
454 np
->num
= (np
->num
!=rp
.num
);
462 static int expr_and(State_t
* state
, Node_t
*np
)
464 register int tok
= expr_cmp(state
, np
);
468 tok
= expr_cmp(state
, &rp
);
469 if ((numeric(&rp
) && rp
.num
==0) || *rp
.str
==0)
478 static int expr_or(State_t
* state
, Node_t
*np
)
480 register int tok
= expr_and(state
, np
);
484 tok
= expr_and(state
, &rp
);
485 if ((numeric(np
) && np
->num
==0) || *np
->str
==0)
492 b_expr(int argc
, char *argv
[], void *context
)
498 cmdinit(argc
, argv
,context
, ERROR_CATALOG
, 0);
499 state
.standard
= !strcmp(astconf("CONFORMANCE", NiL
, NiL
), "standard");
502 state
.arglist
= argv
+1;
506 while (n
=optget(argv
, usage
))
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
516 error(ERROR_usage(2), "%s", opt_info
.arg
);
517 if (opt_info
.option
[1] != '?')
519 error(ERROR_usage(2), "%s", opt_info
.arg
);
521 if (error_info
.errors
)
522 error(ERROR_usage(2),"%s",optusage((char*)0));
523 state
.arglist
= argv
+opt_info
.index
;
525 if (expr_or(&state
, &node
))
526 error(ERROR_exit(2),"syntax error");
530 sfprintf(sfstdout
,"%s\n",node
.str
);
533 sfprintf(sfstdout
,"%d\n",node
.num
);
534 return numeric(&node
)?node
.num
==0:*node
.str
==0;