1 /* expr -- evaluate expressions.
2 Copyright (C) 86, 1991-1997, 1999-2008 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Author: Mike Parker.
19 This program evaluates expressions. Each token (operator, operand,
20 parenthesis) of the expression must be a seperate argument. The
21 parser used is a reasonably general one, though any incarnation of
22 it is language-specific. It is especially nice for expressions.
24 No parse tree is needed; a new node is evaluated immediately.
25 One function can handle multiple operators all of equal precedence,
26 provided they all associate ((x op x) op x).
28 Define EVAL_TRACE to print an evaluation trace. */
32 #include <sys/types.h>
36 #include "long-options.h"
39 #include "strnumcmp.h"
42 /* The official name of this program (e.g., no `g' prefix). */
43 #define PROGRAM_NAME "expr"
45 #define AUTHORS proper_name ("Mike Parker")
50 /* Invalid expression: e.g., its form does not conform to the
51 grammar for expressions. Our grammar is an extension of the
55 /* An internal error occurred, e.g., arithmetic overflow, storage
60 /* The kinds of value we can have. */
66 typedef enum valtype TYPE;
71 TYPE type; /* Which kind. */
73 { /* The value itself. */
78 typedef struct valinfo VALUE;
80 /* The arguments given to the program, minus the program name. */
83 static VALUE *eval (bool);
84 static bool nomoreargs (void);
85 static bool null (VALUE *v);
86 static void printv (VALUE *v);
91 if (status != EXIT_SUCCESS)
92 fprintf (stderr, _("Try `%s --help' for more information.\n"),
97 Usage: %s EXPRESSION\n\
100 program_name, program_name);
102 fputs (HELP_OPTION_DESCRIPTION, stdout);
103 fputs (VERSION_OPTION_DESCRIPTION, stdout);
106 Print the value of EXPRESSION to standard output. A blank line below\n\
107 separates increasing precedence groups. EXPRESSION may be:\n\
109 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
111 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
115 ARG1 < ARG2 ARG1 is less than ARG2\n\
116 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
117 ARG1 = ARG2 ARG1 is equal to ARG2\n\
118 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
119 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
120 ARG1 > ARG2 ARG1 is greater than ARG2\n\
124 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
125 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
127 /* Tell xgettext that the "% A" below is not a printf-style
128 format string: xgettext:no-c-format */
131 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
132 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
133 ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
137 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
139 match STRING REGEXP same as STRING : REGEXP\n\
140 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
141 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
142 length STRING length of STRING\n\
145 + TOKEN interpret TOKEN as a string, even if it is a\n\
146 keyword like `match' or an operator like `/'\n\
148 ( EXPRESSION ) value of EXPRESSION\n\
152 Beware that many operators need to be escaped or quoted for shells.\n\
153 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
154 Pattern matches return the string matched between \\( and \\) or null; if\n\
155 \\( and \\) are not used, they return the number of characters matched or 0.\n\
159 Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
160 or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
162 emit_bug_reporting_address ();
167 /* Report a syntax error and exit. */
171 error (EXPR_INVALID, 0, _("syntax error"));
174 /* Report an integer overflow for operation OP and exit. */
176 integer_overflow (char op)
178 error (EXPR_FAILURE, ERANGE, "%c", op);
182 main (int argc, char **argv)
186 initialize_main (&argc, &argv);
187 set_program_name (argv[0]);
188 setlocale (LC_ALL, "");
189 bindtextdomain (PACKAGE, LOCALEDIR);
190 textdomain (PACKAGE);
192 initialize_exit_failure (EXPR_FAILURE);
193 atexit (close_stdout);
195 parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
196 usage, AUTHORS, (char const *) NULL);
197 /* The above handles --help and --version.
198 Since there is no other invocation of getopt, handle `--' here. */
199 if (argc > 1 && STREQ (argv[1], "--"))
207 error (0, 0, _("missing operand"));
208 usage (EXPR_INVALID);
221 /* Return a VALUE for I. */
224 int_value (intmax_t i)
226 VALUE *v = xmalloc (sizeof *v);
232 /* Return a VALUE for S. */
235 str_value (char const *s)
237 VALUE *v = xmalloc (sizeof *v);
239 v->u.s = xstrdup (s);
243 /* Free VALUE V, including structure components. */
248 if (v->type == string)
259 char buf[INT_BUFSIZE_BOUND (intmax_t)];
264 p = imaxtostr (v->u.i, buf);
276 /* Return true if V is a null-string or zero-number. */
287 char const *cp = v->u.s;
307 /* Return true if CP takes the form of an integer. */
310 looks_like_integer (char const *cp)
322 /* Coerce V to a string value (can't fail). */
327 char buf[INT_BUFSIZE_BOUND (intmax_t)];
332 v->u.s = xstrdup (imaxtostr (v->u.i, buf));
342 /* Coerce V to an integer value. Return true on success, false on failure. */
355 if (! looks_like_integer (v->u.s))
357 if (xstrtoimax (v->u.s, NULL, 10, &value, NULL) != LONGINT_OK)
358 error (EXPR_FAILURE, ERANGE, "%s", v->u.s);
369 /* Return true and advance if the next token matches STR exactly.
370 STR must not be NULL. */
373 nextarg (char const *str)
379 bool r = STREQ (*args, str);
385 /* Return true if there no more tokens. */
394 /* Print evaluation trace and args remaining. */
403 for (a = args; *a; a++)
409 /* Do the : operator.
410 SV is the VALUE for the lhs (the string),
411 PV is the VALUE for the rhs (the pattern). */
414 docolon (VALUE *sv, VALUE *pv)
416 VALUE *v IF_LINT (= NULL);
418 struct re_pattern_buffer re_buffer;
419 char fastmap[UCHAR_MAX + 1];
420 struct re_registers re_regs;
426 re_regs.num_regs = 0;
427 re_regs.start = NULL;
430 re_buffer.buffer = NULL;
431 re_buffer.allocated = 0;
432 re_buffer.fastmap = fastmap;
433 re_buffer.translate = NULL;
435 RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
436 errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer);
438 error (EXPR_INVALID, 0, "%s", errmsg);
439 re_buffer.newline_anchor = 0;
441 matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
444 /* Were \(...\) used? */
445 if (re_buffer.re_nsub > 0)
447 sv->u.s[re_regs.end[1]] = '\0';
448 v = str_value (sv->u.s + re_regs.start[1]);
451 v = int_value (matchlen);
453 else if (matchlen == -1)
455 /* Match failed -- return the right kind of null. */
456 if (re_buffer.re_nsub > 0)
463 (matchlen == -2 ? errno : EOVERFLOW),
464 _("error in regular expression matcher"));
466 if (0 < re_regs.num_regs)
468 free (re_regs.start);
471 re_buffer.fastmap = NULL;
472 regfree (&re_buffer);
476 /* Handle bare operands and ( expr ) syntax. */
479 eval7 (bool evaluate)
500 return str_value (*args++);
503 /* Handle match, substr, index, and length keywords, and quoting "+". */
506 eval6 (bool evaluate)
521 return str_value (*args++);
523 else if (nextarg ("length"))
525 r = eval6 (evaluate);
527 v = int_value (strlen (r->u.s));
531 else if (nextarg ("match"))
533 l = eval6 (evaluate);
534 r = eval6 (evaluate);
545 else if (nextarg ("index"))
547 l = eval6 (evaluate);
548 r = eval6 (evaluate);
551 v = int_value (strcspn (l->u.s, r->u.s) + 1);
552 if (v->u.i == strlen (l->u.s) + 1)
558 else if (nextarg ("substr"))
561 l = eval6 (evaluate);
562 i1 = eval6 (evaluate);
563 i2 = eval6 (evaluate);
565 llen = strlen (l->u.s);
566 if (!toarith (i1) || !toarith (i2)
568 || i1->u.i <= 0 || i2->u.i <= 0)
572 size_t vlen = MIN (i2->u.i, llen - i1->u.i + 1);
574 v = xmalloc (sizeof *v);
576 v->u.s = xmalloc (vlen + 1);
577 vlim = mempcpy (v->u.s, l->u.s + i1->u.i - 1, vlen);
586 return eval7 (evaluate);
589 /* Handle : operator (pattern matching).
590 Calls docolon to do the real work. */
593 eval5 (bool evaluate)
602 l = eval6 (evaluate);
607 r = eval6 (evaluate);
621 /* Handle *, /, % operators. */
624 eval4 (bool evaluate)
628 enum { multiply, divide, mod } fxn;
634 l = eval5 (evaluate);
639 else if (nextarg ("/"))
641 else if (nextarg ("%"))
645 r = eval5 (evaluate);
648 if (!toarith (l) || !toarith (r))
649 error (EXPR_INVALID, 0, _("non-numeric argument"));
652 val = l->u.i * r->u.i;
653 if (! (l->u.i == 0 || r->u.i == 0
654 || ((val < 0) == ((l->u.i < 0) ^ (r->u.i < 0))
655 && val / l->u.i == r->u.i)))
656 integer_overflow ('*');
661 error (EXPR_INVALID, 0, _("division by zero"));
662 if (l->u.i < - INTMAX_MAX && r->u.i == -1)
664 /* Some x86-style hosts raise an exception for
665 INT_MIN / -1 and INT_MIN % -1, so handle these
666 problematic cases specially. */
668 integer_overflow ('/');
672 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
681 /* Handle +, - operators. */
684 eval3 (bool evaluate)
688 enum { plus, minus } fxn;
694 l = eval4 (evaluate);
699 else if (nextarg ("-"))
703 r = eval4 (evaluate);
706 if (!toarith (l) || !toarith (r))
707 error (EXPR_INVALID, 0, _("non-numeric argument"));
710 val = l->u.i + r->u.i;
711 if ((val < l->u.i) != (r->u.i < 0))
712 integer_overflow ('+');
716 val = l->u.i - r->u.i;
717 if ((l->u.i < val) != (r->u.i < 0))
718 integer_overflow ('-');
727 /* Handle comparisons. */
730 eval2 (bool evaluate)
737 l = eval3 (evaluate);
743 less_than, less_equal, equal, not_equal, greater_equal, greater_than
749 else if (nextarg ("<="))
751 else if (nextarg ("=") || nextarg ("=="))
753 else if (nextarg ("!="))
755 else if (nextarg (">="))
757 else if (nextarg (">"))
761 r = eval3 (evaluate);
769 if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
770 cmp = strintcmp (l->u.s, r->u.s);
774 cmp = strcoll (l->u.s, r->u.s);
778 error (0, errno, _("string comparison failed"));
779 error (0, 0, _("set LC_ALL='C' to work around the problem"));
780 error (EXPR_INVALID, 0,
781 _("the strings compared were %s and %s"),
782 quotearg_n_style (0, locale_quoting_style, l->u.s),
783 quotearg_n_style (1, locale_quoting_style, r->u.s));
789 case less_than: val = (cmp < 0); break;
790 case less_equal: val = (cmp <= 0); break;
791 case equal: val = (cmp == 0); break;
792 case not_equal: val = (cmp != 0); break;
793 case greater_equal: val = (cmp >= 0); break;
794 case greater_than: val = (cmp > 0); break;
808 eval1 (bool evaluate)
816 l = eval2 (evaluate);
821 r = eval2 (evaluate & ~ null (l));
822 if (null (l) || null (r))
847 l = eval1 (evaluate);
852 r = eval1 (evaluate & null (l));