1 /* $NetBSD: expr.y,v 1.35 2009/01/20 13:04:55 joerg Exp $ */
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
35 __RCSID
("$NetBSD: expr.y,v 1.34 2008/04/30 13:39:13 martin Exp $");
38 #include <sys/types.h>
50 static const char * const *av
;
52 static void yyerror(const char *, ...
);
53 static int yylex(void);
54 static int is_zero_or_null
(const char *);
55 static int is_integer
(const char *);
56 static int64_t perform_arith_op
(const char *, const char *, const char *);
58 int main
(int, const char * const *);
60 #define YYSTYPE const char *
67 %left ADD_SUB_OPERATOR
68 %left MUL_DIV_MOD_OPERATOR
71 %left LEFT_PARENT RIGHT_PARENT
76 (void) printf
("%s\n", $1);
77 return
(is_zero_or_null
($1));
81 expr: item
{ $$
= $1; }
82 | expr SPEC_OR expr
= {
84 * Return evaluation of first expression if it is neither
85 * an empty string nor zero; otherwise, returns the evaluation
86 * of second expression.
88 if
(!is_zero_or_null
($1))
93 | expr SPEC_AND expr
= {
95 * Returns the evaluation of first expr if neither expression
96 * evaluates to an empty string or zero; otherwise, returns
99 if
(!is_zero_or_null
($1) && !is_zero_or_null
($3))
104 | expr SPEC_REG expr
= {
106 * The ``:'' operator matches first expr against the second,
107 * which must be a regular expression.
113 /* compile regular expression */
114 if
((eval
= regcomp
(&rp
, $3, REG_BASIC
)) != 0) {
116 (void)regerror
(eval
, &rp
, errbuf
, sizeof
(errbuf
));
117 yyerror("%s", errbuf
);
121 /* compare string against pattern -- remember that patterns
122 are anchored to the beginning of the line */
123 if
(regexec
(&rp
, $1, 2, rm
, 0) == 0 && rm
[0].rm_so
== 0) {
125 if
(rm
[1].rm_so
>= 0) {
126 (void) asprintf
(&val
, "%.*s",
127 (int) (rm
[1].rm_eo
- rm
[1].rm_so
),
130 (void) asprintf
(&val
, "%d",
131 (int)(rm
[0].rm_eo
- rm
[0].rm_so
));
137 if
(rp.re_nsub
== 0) {
145 | expr ADD_SUB_OPERATOR expr
= {
146 /* Returns the results of addition, subtraction */
150 res
= perform_arith_op
($1, $2, $3);
151 (void) asprintf
(&val
, "%lld", (long long int) res
);
157 | expr MUL_DIV_MOD_OPERATOR expr
= {
159 * Returns the results of multiply, divide or remainder of
160 * numeric-valued arguments.
165 res
= perform_arith_op
($1, $2, $3);
166 (void) asprintf
(&val
, "%lld", (long long int) res
);
172 | expr COMPARE expr
= {
174 * Returns the results of integer comparison if both arguments
175 * are integers; otherwise, returns the results of string
176 * comparison using the locale-specific collation sequence.
177 * The result of each comparison is 1 if the specified relation
178 * is true, or 0 if the relation is false.
187 * Slight hack to avoid differences in the compare code
188 * between string and numeric compare.
190 if
(is_integer
($1) && is_integer
($3)) {
191 /* numeric comparison */
192 l
= strtoll
($1, NULL
, 10);
193 r
= strtoll
($3, NULL
, 10);
195 /* string comparison */
201 case
'=': /* equal */
204 case
'>': /* greater or greater-equal */
210 case
'<': /* lower or lower-equal */
216 case
'!': /* not equal */
217 /* the check if this is != was done in yylex() */
221 $$
= (res
) ?
"1" : "0";
224 | LEFT_PARENT expr RIGHT_PARENT
{ $$
= $2; }
227 * Return length of 'expr' in bytes.
231 asprintf
(&ln
, "%ld", (long) strlen
($2));
240 | MUL_DIV_MOD_OPERATOR
250 * Returns 1 if the string is empty or contains only numeric zero.
253 is_zero_or_null
(const char *str
)
257 return str
[0] == '\0'
258 ||
( strtoll
(str
, &endptr
, 10) == 0LL
259 && endptr
[0] == '\0');
263 * Returns 1 if the string is an integer.
266 is_integer
(const char *str
)
270 (void) strtoll
(str
, &endptr
, 10);
271 /* note we treat empty string as valid number */
272 return
(endptr
[0] == '\0');
276 perform_arith_op
(const char *left
, const char *op
, const char *right
)
278 int64_t res
, sign
, l
, r
;
283 if
(!is_integer
(left
)) {
284 yyerror("non-integer argument '%s'", left
);
287 if
(!is_integer
(right
)) {
288 yyerror("non-integer argument '%s'", right
);
293 l
= strtoll
(left
, NULL
, 10);
294 if
(errno
== ERANGE
) {
295 yyerror("value '%s' is %s is %lld", left
,
296 (l
> 0) ?
"too big, maximum" : "too small, minimum",
297 (l
> 0) ? LLONG_MAX
: LLONG_MIN
);
302 r
= strtoll
(right
, NULL
, 10);
303 if
(errno
== ERANGE
) {
304 yyerror("value '%s' is %s is %lld", right
,
305 (l
> 0) ?
"too big, maximum" : "too small, minimum",
306 (l
> 0) ? LLONG_MAX
: LLONG_MIN
);
313 * Do the op into an unsigned to avoid overflow and then cast
314 * back to check the resulting signage.
317 res
= (int64_t) temp
;
318 /* very simplistic check for over-& underflow */
319 if
((res
< 0 && l
> 0 && r
> 0)
320 ||
(res
> 0 && l
< 0 && r
< 0))
321 yyerror("integer overflow or underflow occurred for "
322 "operation '%s %s %s'", left
, op
, right
);
326 * Do the op into an unsigned to avoid overflow and then cast
327 * back to check the resulting signage.
330 res
= (int64_t) temp
;
331 /* very simplistic check for over-& underflow */
332 if
((res
< 0 && l
> 0 && l
> r
)
333 ||
(res
> 0 && l
< 0 && l
< r
) )
334 yyerror("integer overflow or underflow occurred for "
335 "operation '%s %s %s'", left
, op
, right
);
339 yyerror("second argument to '%s' must not be zero", op
);
345 yyerror("second argument to '%s' must not be zero", op
);
350 if
((l
== 0) ||
(r
== 0)) {
363 * XXX: not the most portable but works on anything with 2's
364 * complement arithmetic. If the signs don't match or the
365 * result was 0 on 2's complement this overflowed.
367 if
((res
< 0 && sign
> 0) ||
(res
> 0 && sign
< 0) ||
369 yyerror("integer overflow or underflow occurred for "
370 "operation '%s %s %s'", left
, op
, right
);
377 static const char *x
= "|&=<>+-*/%:()";
378 static const int x_token
[] = {
379 SPEC_OR
, SPEC_AND
, COMPARE
, COMPARE
, COMPARE
, ADD_SUB_OPERATOR
,
380 ADD_SUB_OPERATOR
, MUL_DIV_MOD_OPERATOR
, MUL_DIV_MOD_OPERATOR
,
381 MUL_DIV_MOD_OPERATOR
, SPEC_REG
, LEFT_PARENT
, RIGHT_PARENT
384 static int handle_ddash
= 1;
389 const char *p
= *av
++;
394 else if
(p
[1] == '\0') {
395 const char *w
= strchr
(x
, p
[0]);
397 retval
= x_token
[w
-x
];
401 } else if
(p
[1] == '=' && p
[2] == '\0'
402 && (p
[0] == '>' || p
[0] == '<' || p
[0] == '!'))
404 else if
(handle_ddash
&& p
[0] == '-' && p
[1] == '-' && p
[2] == '\0') {
405 /* ignore "--" if passed as first argument and isn't followed
406 * by another STRING */
408 if
(retval
!= STRING
&& retval
!= LEFT_PARENT
409 && retval
!= RIGHT_PARENT
) {
410 /* is not followed by string or parenthesis, use as
413 av
--; /* was increased in call to yylex() above */
416 /* "--" is to be ignored */
419 } else if
(strcmp
(p
, "length") == 0)
431 * Print error message and exit with error 2 (syntax error).
434 yyerror(const char *fmt
, ...
)
444 main
(int argc
, const char * const *argv
)
446 setprogname
(argv
[0]);
447 (void)setlocale
(LC_ALL
, "");
450 (void)fprintf
(stderr
, "usage: %s expression\n",