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"
40 #include "strnumcmp.h"
43 /* The official name of this program (e.g., no `g' prefix). */
44 #define PROGRAM_NAME "expr"
46 #define AUTHORS proper_name ("Mike Parker")
51 /* Invalid expression: e.g., its form does not conform to the
52 grammar for expressions. Our grammar is an extension of the
56 /* An internal error occurred, e.g., arithmetic overflow, storage
61 /* The kinds of value we can have. */
67 typedef enum valtype TYPE;
72 TYPE type; /* Which kind. */
74 { /* The value itself. */
79 typedef struct valinfo VALUE;
81 /* The arguments given to the program, minus the program name. */
84 static VALUE *eval (bool);
85 static bool nomoreargs (void);
86 static bool null (VALUE *v);
87 static void printv (VALUE *v);
92 if (status != EXIT_SUCCESS)
93 fprintf (stderr, _("Try `%s --help' for more information.\n"),
98 Usage: %s EXPRESSION\n\
101 program_name, program_name);
103 fputs (HELP_OPTION_DESCRIPTION, stdout);
104 fputs (VERSION_OPTION_DESCRIPTION, stdout);
107 Print the value of EXPRESSION to standard output. A blank line below\n\
108 separates increasing precedence groups. EXPRESSION may be:\n\
110 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
112 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
116 ARG1 < ARG2 ARG1 is less than ARG2\n\
117 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
118 ARG1 = ARG2 ARG1 is equal to ARG2\n\
119 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
120 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
121 ARG1 > ARG2 ARG1 is greater than ARG2\n\
125 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
126 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
128 /* Tell xgettext that the "% A" below is not a printf-style
129 format string: xgettext:no-c-format */
132 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
133 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
134 ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
138 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
140 match STRING REGEXP same as STRING : REGEXP\n\
141 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
142 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
143 length STRING length of STRING\n\
146 + TOKEN interpret TOKEN as a string, even if it is a\n\
147 keyword like `match' or an operator like `/'\n\
149 ( EXPRESSION ) value of EXPRESSION\n\
153 Beware that many operators need to be escaped or quoted for shells.\n\
154 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
155 Pattern matches return the string matched between \\( and \\) or null; if\n\
156 \\( and \\) are not used, they return the number of characters matched or 0.\n\
160 Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
161 or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
163 emit_bug_reporting_address ();
168 /* Report a syntax error and exit. */
172 error (EXPR_INVALID, 0, _("syntax error"));
175 /* Report an integer overflow for operation OP and exit. */
177 integer_overflow (char op)
179 error (EXPR_FAILURE, ERANGE, "%c", op);
183 main (int argc, char **argv)
187 initialize_main (&argc, &argv);
188 set_program_name (argv[0]);
189 setlocale (LC_ALL, "");
190 bindtextdomain (PACKAGE, LOCALEDIR);
191 textdomain (PACKAGE);
193 initialize_exit_failure (EXPR_FAILURE);
194 atexit (close_stdout);
196 parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
197 usage, AUTHORS, (char const *) NULL);
198 /* The above handles --help and --version.
199 Since there is no other invocation of getopt, handle `--' here. */
200 if (argc > 1 && STREQ (argv[1], "--"))
208 error (0, 0, _("missing operand"));
209 usage (EXPR_INVALID);
222 /* Return a VALUE for I. */
225 int_value (intmax_t i)
227 VALUE *v = xmalloc (sizeof *v);
233 /* Return a VALUE for S. */
236 str_value (char const *s)
238 VALUE *v = xmalloc (sizeof *v);
240 v->u.s = xstrdup (s);
244 /* Free VALUE V, including structure components. */
249 if (v->type == string)
260 char buf[INT_BUFSIZE_BOUND (intmax_t)];
265 p = imaxtostr (v->u.i, buf);
277 /* Return true if V is a null-string or zero-number. */
288 char const *cp = v->u.s;
308 /* Return true if CP takes the form of an integer. */
311 looks_like_integer (char const *cp)
323 /* Coerce V to a string value (can't fail). */
328 char buf[INT_BUFSIZE_BOUND (intmax_t)];
333 v->u.s = xstrdup (imaxtostr (v->u.i, buf));
343 /* Coerce V to an integer value. Return true on success, false on failure. */
356 if (! looks_like_integer (v->u.s))
358 if (xstrtoimax (v->u.s, NULL, 10, &value, NULL) != LONGINT_OK)
359 error (EXPR_FAILURE, ERANGE, "%s", v->u.s);
370 /* Return true and advance if the next token matches STR exactly.
371 STR must not be NULL. */
374 nextarg (char const *str)
380 bool r = STREQ (*args, str);
386 /* Return true if there no more tokens. */
395 /* Print evaluation trace and args remaining. */
404 for (a = args; *a; a++)
410 /* Do the : operator.
411 SV is the VALUE for the lhs (the string),
412 PV is the VALUE for the rhs (the pattern). */
415 docolon (VALUE *sv, VALUE *pv)
417 VALUE *v IF_LINT (= NULL);
419 struct re_pattern_buffer re_buffer;
420 char fastmap[UCHAR_MAX + 1];
421 struct re_registers re_regs;
427 re_regs.num_regs = 0;
428 re_regs.start = NULL;
431 re_buffer.buffer = NULL;
432 re_buffer.allocated = 0;
433 re_buffer.fastmap = fastmap;
434 re_buffer.translate = NULL;
436 RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
437 errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer);
439 error (EXPR_INVALID, 0, "%s", errmsg);
440 re_buffer.newline_anchor = 0;
442 matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
445 /* Were \(...\) used? */
446 if (re_buffer.re_nsub > 0)
448 sv->u.s[re_regs.end[1]] = '\0';
449 v = str_value (sv->u.s + re_regs.start[1]);
452 v = int_value (matchlen);
454 else if (matchlen == -1)
456 /* Match failed -- return the right kind of null. */
457 if (re_buffer.re_nsub > 0)
464 (matchlen == -2 ? errno : EOVERFLOW),
465 _("error in regular expression matcher"));
467 if (0 < re_regs.num_regs)
469 free (re_regs.start);
472 re_buffer.fastmap = NULL;
473 regfree (&re_buffer);
477 /* Handle bare operands and ( expr ) syntax. */
480 eval7 (bool evaluate)
501 return str_value (*args++);
504 /* Handle match, substr, index, and length keywords, and quoting "+". */
507 eval6 (bool evaluate)
522 return str_value (*args++);
524 else if (nextarg ("length"))
526 r = eval6 (evaluate);
528 v = int_value (strlen (r->u.s));
532 else if (nextarg ("match"))
534 l = eval6 (evaluate);
535 r = eval6 (evaluate);
546 else if (nextarg ("index"))
548 l = eval6 (evaluate);
549 r = eval6 (evaluate);
552 v = int_value (strcspn (l->u.s, r->u.s) + 1);
553 if (v->u.i == strlen (l->u.s) + 1)
559 else if (nextarg ("substr"))
562 l = eval6 (evaluate);
563 i1 = eval6 (evaluate);
564 i2 = eval6 (evaluate);
566 llen = strlen (l->u.s);
567 if (!toarith (i1) || !toarith (i2)
569 || i1->u.i <= 0 || i2->u.i <= 0)
573 size_t vlen = MIN (i2->u.i, llen - i1->u.i + 1);
575 v = xmalloc (sizeof *v);
577 v->u.s = xmalloc (vlen + 1);
578 vlim = mempcpy (v->u.s, l->u.s + i1->u.i - 1, vlen);
587 return eval7 (evaluate);
590 /* Handle : operator (pattern matching).
591 Calls docolon to do the real work. */
594 eval5 (bool evaluate)
603 l = eval6 (evaluate);
608 r = eval6 (evaluate);
622 /* Handle *, /, % operators. */
625 eval4 (bool evaluate)
629 enum { multiply, divide, mod } fxn;
635 l = eval5 (evaluate);
640 else if (nextarg ("/"))
642 else if (nextarg ("%"))
646 r = eval5 (evaluate);
649 if (!toarith (l) || !toarith (r))
650 error (EXPR_INVALID, 0, _("non-numeric argument"));
653 val = l->u.i * r->u.i;
654 if (! (l->u.i == 0 || r->u.i == 0
655 || ((val < 0) == ((l->u.i < 0) ^ (r->u.i < 0))
656 && val / l->u.i == r->u.i)))
657 integer_overflow ('*');
662 error (EXPR_INVALID, 0, _("division by zero"));
663 if (l->u.i < - INTMAX_MAX && r->u.i == -1)
665 /* Some x86-style hosts raise an exception for
666 INT_MIN / -1 and INT_MIN % -1, so handle these
667 problematic cases specially. */
669 integer_overflow ('/');
673 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
682 /* Handle +, - operators. */
685 eval3 (bool evaluate)
689 enum { plus, minus } fxn;
695 l = eval4 (evaluate);
700 else if (nextarg ("-"))
704 r = eval4 (evaluate);
707 if (!toarith (l) || !toarith (r))
708 error (EXPR_INVALID, 0, _("non-numeric argument"));
711 val = l->u.i + r->u.i;
712 if ((val < l->u.i) != (r->u.i < 0))
713 integer_overflow ('+');
717 val = l->u.i - r->u.i;
718 if ((l->u.i < val) != (r->u.i < 0))
719 integer_overflow ('-');
728 /* Handle comparisons. */
731 eval2 (bool evaluate)
738 l = eval3 (evaluate);
744 less_than, less_equal, equal, not_equal, greater_equal, greater_than
750 else if (nextarg ("<="))
752 else if (nextarg ("=") || nextarg ("=="))
754 else if (nextarg ("!="))
756 else if (nextarg (">="))
758 else if (nextarg (">"))
762 r = eval3 (evaluate);
770 if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
771 cmp = strintcmp (l->u.s, r->u.s);
775 cmp = strcoll (l->u.s, r->u.s);
779 error (0, errno, _("string comparison failed"));
780 error (0, 0, _("Set LC_ALL='C' to work around the problem."));
781 error (EXPR_INVALID, 0,
782 _("The strings compared were %s and %s."),
783 quotearg_n_style (0, locale_quoting_style, l->u.s),
784 quotearg_n_style (1, locale_quoting_style, r->u.s));
790 case less_than: val = (cmp < 0); break;
791 case less_equal: val = (cmp <= 0); break;
792 case equal: val = (cmp == 0); break;
793 case not_equal: val = (cmp != 0); break;
794 case greater_equal: val = (cmp >= 0); break;
795 case greater_than: val = (cmp > 0); break;
809 eval1 (bool evaluate)
817 l = eval2 (evaluate);
822 r = eval2 (evaluate & ~ null (l));
823 if (null (l) || null (r))
848 l = eval1 (evaluate);
853 r = eval1 (evaluate & null (l));