1 /* $NetBSD: expr.y,v 1.39 2016/09/05 01:00:07 sevan 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.39 2016/09/05 01:00:07 sevan Exp $");
38 #include <sys/types.h>
50 static const char * const *av
;
52 static void yyerror(const char *, ...
) __dead
;
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 #define YYSTYPE const char *
65 %left ADD_SUB_OPERATOR
66 %left MUL_DIV_MOD_OPERATOR
69 %left LEFT_PARENT RIGHT_PARENT
74 (void) printf
("%s\n", $1);
75 return
(is_zero_or_null
($1));
79 expr: item
{ $$
= $1; }
80 | expr SPEC_OR expr
= {
82 * Return evaluation of first expression if it is neither
83 * an empty string nor zero; otherwise, returns the evaluation
84 * of second expression.
86 if
(!is_zero_or_null
($1))
91 | expr SPEC_AND expr
= {
93 * Returns the evaluation of first expr if neither expression
94 * evaluates to an empty string or zero; otherwise, returns
97 if
(!is_zero_or_null
($1) && !is_zero_or_null
($3))
102 | expr SPEC_REG expr
= {
104 * The ``:'' operator matches first expr against the second,
105 * which must be a regular expression.
111 /* compile regular expression */
112 if
((eval
= regcomp
(&rp
, $3, REG_BASIC
)) != 0) {
114 (void)regerror
(eval
, &rp
, errbuf
, sizeof
(errbuf
));
115 yyerror("%s", errbuf
);
119 /* compare string against pattern -- remember that patterns
120 are anchored to the beginning of the line */
121 if
(regexec
(&rp
, $1, 2, rm
, 0) == 0 && rm
[0].rm_so
== 0) {
123 if
(rm
[1].rm_so
>= 0) {
124 (void) asprintf
(&val
, "%.*s",
125 (int) (rm
[1].rm_eo
- rm
[1].rm_so
),
128 (void) asprintf
(&val
, "%d",
129 (int)(rm
[0].rm_eo
- rm
[0].rm_so
));
135 if
(rp.re_nsub
== 0) {
143 | expr ADD_SUB_OPERATOR expr
= {
144 /* Returns the results of addition, subtraction */
148 res
= perform_arith_op
($1, $2, $3);
149 (void) asprintf
(&val
, "%lld", (long long int) res
);
155 | expr MUL_DIV_MOD_OPERATOR expr
= {
157 * Returns the results of multiply, divide or remainder of
158 * numeric-valued arguments.
163 res
= perform_arith_op
($1, $2, $3);
164 (void) asprintf
(&val
, "%lld", (long long int) res
);
170 | expr COMPARE expr
= {
172 * Returns the results of integer comparison if both arguments
173 * are integers; otherwise, returns the results of string
174 * comparison using the locale-specific collation sequence.
175 * The result of each comparison is 1 if the specified relation
176 * is true, or 0 if the relation is false.
185 * Slight hack to avoid differences in the compare code
186 * between string and numeric compare.
188 if
(is_integer
($1) && is_integer
($3)) {
189 /* numeric comparison */
190 l
= strtoll
($1, NULL
, 10);
191 r
= strtoll
($3, NULL
, 10);
193 /* string comparison */
199 case
'=': /* equal */
202 case
'>': /* greater or greater-equal */
208 case
'<': /* lower or lower-equal */
214 case
'!': /* not equal */
215 /* the check if this is != was done in yylex() */
219 $$
= (res
) ?
"1" : "0";
222 | LEFT_PARENT expr RIGHT_PARENT
{ $$
= $2; }
225 * Return length of 'expr' in bytes.
229 asprintf
(&ln
, "%ld", (long) strlen
($2));
238 | MUL_DIV_MOD_OPERATOR
248 * Returns 1 if the string is empty or contains only numeric zero.
251 is_zero_or_null
(const char *str
)
255 return str
[0] == '\0'
256 ||
( strtoll
(str
, &endptr
, 10) == 0LL
257 && endptr
[0] == '\0');
261 * Returns 1 if the string is an integer.
264 is_integer
(const char *str
)
268 (void) strtoll
(str
, &endptr
, 10);
269 /* note we treat empty string as valid number */
270 return
(endptr
[0] == '\0');
274 perform_arith_op
(const char *left
, const char *op
, const char *right
)
276 int64_t res
, sign
, l
, r
;
281 if
(!is_integer
(left
)) {
282 yyerror("non-integer argument '%s'", left
);
285 if
(!is_integer
(right
)) {
286 yyerror("non-integer argument '%s'", right
);
291 l
= strtoll
(left
, NULL
, 10);
292 if
(errno
== ERANGE
) {
293 yyerror("value '%s' is %s is %lld", left
,
294 (l
> 0) ?
"too big, maximum" : "too small, minimum",
295 (l
> 0) ? LLONG_MAX
: LLONG_MIN
);
300 r
= strtoll
(right
, NULL
, 10);
301 if
(errno
== ERANGE
) {
302 yyerror("value '%s' is %s is %lld", right
,
303 (l
> 0) ?
"too big, maximum" : "too small, minimum",
304 (l
> 0) ? LLONG_MAX
: LLONG_MIN
);
311 * Do the op into an unsigned to avoid overflow and then cast
312 * back to check the resulting signage.
315 res
= (int64_t) temp
;
316 /* very simplistic check for over-& underflow */
317 if
((res
< 0 && l
> 0 && r
> 0)
318 ||
(res
> 0 && l
< 0 && r
< 0))
319 yyerror("integer overflow or underflow occurred for "
320 "operation '%s %s %s'", left
, op
, right
);
324 * Do the op into an unsigned to avoid overflow and then cast
325 * back to check the resulting signage.
328 res
= (int64_t) temp
;
329 /* very simplistic check for over-& underflow */
330 if
((res
< 0 && l
> 0 && l
> r
)
331 ||
(res
> 0 && l
< 0 && l
< r
) )
332 yyerror("integer overflow or underflow occurred for "
333 "operation '%s %s %s'", left
, op
, right
);
337 yyerror("second argument to '%s' must not be zero", op
);
343 yyerror("second argument to '%s' must not be zero", op
);
348 if
((l
== 0) ||
(r
== 0)) {
361 * XXX: not the most portable but works on anything with 2's
362 * complement arithmetic. If the signs don't match or the
363 * result was 0 on 2's complement this overflowed.
365 if
((res
< 0 && sign
> 0) ||
(res
> 0 && sign
< 0) ||
367 yyerror("integer overflow or underflow occurred for "
368 "operation '%s %s %s'", left
, op
, right
);
375 static const char *x
= "|&=<>+-*/%:()";
376 static const int x_token
[] = {
377 SPEC_OR
, SPEC_AND
, COMPARE
, COMPARE
, COMPARE
, ADD_SUB_OPERATOR
,
378 ADD_SUB_OPERATOR
, MUL_DIV_MOD_OPERATOR
, MUL_DIV_MOD_OPERATOR
,
379 MUL_DIV_MOD_OPERATOR
, SPEC_REG
, LEFT_PARENT
, RIGHT_PARENT
382 static int handle_ddash
= 1;
387 const char *p
= *av
++;
392 else if
(p
[1] == '\0') {
393 const char *w
= strchr
(x
, p
[0]);
395 retval
= x_token
[w
-x
];
399 } else if
(p
[1] == '=' && p
[2] == '\0'
400 && (p
[0] == '>' || p
[0] == '<' || p
[0] == '!'))
402 else if
(handle_ddash
&& p
[0] == '-' && p
[1] == '-' && p
[2] == '\0') {
403 /* ignore "--" if passed as first argument and isn't followed
404 * by another STRING */
406 if
(retval
!= STRING
&& retval
!= LEFT_PARENT
407 && retval
!= RIGHT_PARENT
) {
408 /* is not followed by string or parenthesis, use as
411 av
--; /* was increased in call to yylex() above */
414 /* "--" is to be ignored */
417 } else if
(strcmp
(p
, "length") == 0)
429 * Print error message and exit with error 2 (syntax error).
431 static __printflike
(1, 2) void
432 yyerror(const char *fmt
, ...
)
442 main
(int argc
, const char * const *argv
)
444 setprogname
(argv
[0]);
445 (void)setlocale
(LC_ALL
, "");
448 (void)fprintf
(stderr
, "usage: %s expression\n",