xref: /onnv-gate/usr/src/lib/libcmd/common/expr.c (revision 12068:08a39a083754)
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