1 /* expr -- evaluate expressions.
2 Copyright (C) 86, 1991-1997, 1999-2005 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 2, or (at your option)
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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Author: Mike Parker.
20 This program evaluates expressions. Each token (operator, operand,
21 parenthesis) of the expression must be a seperate argument. The
22 parser used is a reasonably general one, though any incarnation of
23 it is language-specific. It is especially nice for expressions.
25 No parse tree is needed; a new node is evaluated immediately.
26 One function can handle multiple operators all of equal precedence,
27 provided they all associate ((x op x) op x).
29 Define EVAL_TRACE to print an evaluation trace. */
33 #include <sys/types.h>
37 #include "long-options.h"
42 #include "strnumcmp.h"
45 /* The official name of this program (e.g., no `g' prefix). */
46 #define PROGRAM_NAME "expr"
48 #define AUTHORS "Mike Parker"
53 /* Invalid expression: i.e., its form does not conform to the
54 grammar for expressions. Our grammar is an extension of the
58 /* Some other error occurred. */
62 /* The kinds of value we can have. */
68 typedef enum valtype TYPE;
73 TYPE type; /* Which kind. */
75 { /* The value itself. */
80 typedef struct valinfo VALUE;
82 /* The arguments given to the program, minus the program name. */
85 /* The name this program was run with. */
88 static VALUE *eval (bool);
89 static bool nomoreargs (void);
90 static bool null (VALUE *v);
91 static void printv (VALUE *v);
96 if (status != EXIT_SUCCESS)
97 fprintf (stderr, _("Try `%s --help' for more information.\n"),
102 Usage: %s EXPRESSION\n\
105 program_name, program_name);
107 fputs (HELP_OPTION_DESCRIPTION, stdout);
108 fputs (VERSION_OPTION_DESCRIPTION, stdout);
111 Print the value of EXPRESSION to standard output. A blank line below\n\
112 separates increasing precedence groups. EXPRESSION may be:\n\
114 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
116 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
120 ARG1 < ARG2 ARG1 is less than ARG2\n\
121 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
122 ARG1 = ARG2 ARG1 is equal to ARG2\n\
123 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
124 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
125 ARG1 > ARG2 ARG1 is greater than ARG2\n\
129 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
130 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
134 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
135 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
136 ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
140 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
142 match STRING REGEXP same as STRING : REGEXP\n\
143 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
144 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
145 length STRING length of STRING\n\
148 + TOKEN interpret TOKEN as a string, even if it is a\n\
149 keyword like `match' or an operator like `/'\n\
151 ( EXPRESSION ) value of EXPRESSION\n\
155 Beware that many operators need to be escaped or quoted for shells.\n\
156 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
157 Pattern matches return the string matched between \\( and \\) or null; if\n\
158 \\( and \\) are not used, they return the number of characters matched or 0.\n\
162 Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
163 or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
165 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
170 /* Report a syntax error and exit. */
174 error (EXPR_INVALID, 0, _("syntax error"));
178 main (int argc, char **argv)
182 initialize_main (&argc, &argv);
183 program_name = argv[0];
184 setlocale (LC_ALL, "");
185 bindtextdomain (PACKAGE, LOCALEDIR);
186 textdomain (PACKAGE);
188 initialize_exit_failure (EXPR_FAILURE);
189 atexit (close_stdout);
191 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
192 usage, AUTHORS, (char const *) NULL);
193 /* The above handles --help and --version.
194 Since there is no other invocation of getopt, handle `--' here. */
195 if (argc > 1 && STREQ (argv[1], "--"))
203 error (0, 0, _("missing operand"));
204 usage (EXPR_INVALID);
217 /* Return a VALUE for I. */
220 int_value (intmax_t i)
222 VALUE *v = xmalloc (sizeof *v);
228 /* Return a VALUE for S. */
233 VALUE *v = xmalloc (sizeof *v);
235 v->u.s = xstrdup (s);
239 /* Free VALUE V, including structure components. */
244 if (v->type == string)
255 char buf[INT_BUFSIZE_BOUND (intmax_t)];
260 p = imaxtostr (v->u.i, buf);
272 /* Return true if V is a null-string or zero-number. */
283 char const *cp = v->u.s;
303 /* Return true if CP takes the form of an integer. */
306 looks_like_integer (char const *cp)
318 /* Coerce V to a string value (can't fail). */
323 char buf[INT_BUFSIZE_BOUND (intmax_t)];
328 v->u.s = xstrdup (imaxtostr (v->u.i, buf));
338 /* Coerce V to an integer value. Return true on success, false on failure. */
351 if (! looks_like_integer (v->u.s))
353 if (xstrtoimax (v->u.s, NULL, 10, &value, NULL) != LONGINT_OK)
354 error (EXPR_FAILURE, ERANGE, "%s", v->u.s);
365 /* Return true and advance if the next token matches STR exactly.
366 STR must not be NULL. */
369 nextarg (char const *str)
375 bool r = STREQ (*args, str);
381 /* Return true if there no more tokens. */
390 /* Print evaluation trace and args remaining. */
399 for (a = args; *a; a++)
405 /* Do the : operator.
406 SV is the VALUE for the lhs (the string),
407 PV is the VALUE for the rhs (the pattern). */
410 docolon (VALUE *sv, VALUE *pv)
414 struct re_pattern_buffer re_buffer;
415 struct re_registers re_regs;
422 if (pv->u.s[0] == '^')
425 warning: unportable BRE: %s: using `^' as the first character\n\
426 of the basic regular expression is not portable; it is being ignored"),
430 len = strlen (pv->u.s);
431 memset (&re_buffer, 0, sizeof (re_buffer));
432 memset (&re_regs, 0, sizeof (re_regs));
433 re_buffer.allocated = 2 * len;
434 if (re_buffer.allocated < len)
436 re_buffer.buffer = xmalloc (re_buffer.allocated);
437 re_buffer.translate = NULL;
438 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
439 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
441 error (EXPR_FAILURE, 0, "%s", errmsg);
443 matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
446 /* Were \(...\) used? */
447 if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
449 sv->u.s[re_regs.end[1]] = '\0';
450 v = str_value (sv->u.s + re_regs.start[1]);
453 v = int_value (matchlen);
457 /* Match failed -- return the right kind of null. */
458 if (re_buffer.re_nsub > 0)
463 free (re_buffer.buffer);
467 /* Handle bare operands and ( expr ) syntax. */
470 eval7 (bool evaluate)
491 return str_value (*args++);
494 /* Handle match, substr, index, and length keywords, and quoting "+". */
497 eval6 (bool evaluate)
512 return str_value (*args++);
514 else if (nextarg ("length"))
516 r = eval6 (evaluate);
518 v = int_value (strlen (r->u.s));
522 else if (nextarg ("match"))
524 l = eval6 (evaluate);
525 r = eval6 (evaluate);
536 else if (nextarg ("index"))
538 l = eval6 (evaluate);
539 r = eval6 (evaluate);
542 v = int_value (strcspn (l->u.s, r->u.s) + 1);
543 if (v->u.i == strlen (l->u.s) + 1)
549 else if (nextarg ("substr"))
551 l = eval6 (evaluate);
552 i1 = eval6 (evaluate);
553 i2 = eval6 (evaluate);
555 if (!toarith (i1) || !toarith (i2)
556 || strlen (l->u.s) < i1->u.i
557 || i1->u.i <= 0 || i2->u.i <= 0)
561 v = xmalloc (sizeof *v);
563 v->u.s = strncpy (xmalloc (i2->u.i + 1),
564 l->u.s + i1->u.i - 1, i2->u.i);
573 return eval7 (evaluate);
576 /* Handle : operator (pattern matching).
577 Calls docolon to do the real work. */
580 eval5 (bool evaluate)
589 l = eval6 (evaluate);
594 r = eval6 (evaluate);
608 /* Handle *, /, % operators. */
611 eval4 (bool evaluate)
615 enum { multiply, divide, mod } fxn;
621 l = eval5 (evaluate);
626 else if (nextarg ("/"))
628 else if (nextarg ("%"))
632 r = eval5 (evaluate);
635 if (!toarith (l) || !toarith (r))
636 error (EXPR_FAILURE, 0, _("non-numeric argument"));
638 val = l->u.i * r->u.i;
642 error (EXPR_FAILURE, 0, _("division by zero"));
643 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
652 /* Handle +, - operators. */
655 eval3 (bool evaluate)
659 enum { plus, minus } fxn;
665 l = eval4 (evaluate);
670 else if (nextarg ("-"))
674 r = eval4 (evaluate);
677 if (!toarith (l) || !toarith (r))
678 error (EXPR_FAILURE, 0, _("non-numeric argument"));
679 val = fxn == plus ? l->u.i + r->u.i : l->u.i - r->u.i;
687 /* Handle comparisons. */
690 eval2 (bool evaluate)
697 l = eval3 (evaluate);
703 less_than, less_equal, equal, not_equal, greater_equal, greater_than
709 else if (nextarg ("<="))
711 else if (nextarg ("=") || nextarg ("=="))
713 else if (nextarg ("!="))
715 else if (nextarg (">="))
717 else if (nextarg (">"))
721 r = eval3 (evaluate);
729 if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
730 cmp = strintcmp (l->u.s, r->u.s);
734 cmp = strcoll (l->u.s, r->u.s);
738 error (0, errno, _("string comparison failed"));
739 error (0, 0, _("Set LC_ALL='C' to work around the problem."));
740 error (EXPR_FAILURE, 0,
741 _("The strings compared were %s and %s."),
742 quotearg_n_style (0, locale_quoting_style, l->u.s),
743 quotearg_n_style (1, locale_quoting_style, r->u.s));
749 case less_than: val = (cmp < 0); break;
750 case less_equal: val = (cmp <= 0); break;
751 case equal: val = (cmp == 0); break;
752 case not_equal: val = (cmp != 0); break;
753 case greater_equal: val = (cmp >= 0); break;
754 case greater_than: val = (cmp > 0); break;
768 eval1 (bool evaluate)
776 l = eval2 (evaluate);
781 r = eval2 (evaluate & ~ null (l));
782 if (null (l) || null (r))
807 l = eval1 (evaluate);
812 r = eval1 (evaluate & null (l));