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