1 /* expr -- evaluate expressions.
2 Copyright (C) 86, 1991-1997, 1999, 2000, 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 /* The official name of this program (e.g., no `g' prefix). */
43 #define PROGRAM_NAME "expr"
45 #define AUTHORS "Mike Parker"
48 #define NEW(Type) XMALLOC (Type, 1)
49 #define OLD(x) free ((char *) x)
51 /* The kinds of value we can have. */
57 typedef enum valtype TYPE;
62 TYPE type; /* Which kind. */
64 { /* The value itself. */
69 typedef struct valinfo VALUE;
71 /* The arguments given to the program, minus the program name. */
74 /* The name this program was run with. */
77 static VALUE *eval PARAMS ((void));
78 static int nomoreargs PARAMS ((void));
79 static int null PARAMS ((VALUE *v));
80 static void printv PARAMS ((VALUE *v));
86 fprintf (stderr, _("Try `%s --help' for more information.\n"),
91 Usage: %s EXPRESSION\n\
94 program_name, program_name);
97 --help display this help and exit\n\
98 --version output version information and exit\n\
102 Print the value of EXPRESSION to standard output. A blank line below\n\
103 separates increasing precedence groups. EXPRESSION may be:\n\
105 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
107 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
109 ARG1 < ARG2 ARG1 is less than ARG2\n\
110 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
111 ARG1 = ARG2 ARG1 is equal to ARG2\n\
112 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
113 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
114 ARG1 > ARG2 ARG1 is greater than ARG2\n\
116 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
117 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
119 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
120 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
121 ARG1 %% ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
123 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
125 match STRING REGEXP same as STRING : REGEXP\n\
126 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
127 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
128 length STRING length of STRING\n\
129 + TOKEN interpret TOKEN as a string, even if it is a\n\
130 keyword like `match' or an operator like `/'\n\
132 ( EXPRESSION ) value of EXPRESSION\n\
136 Beware that many operators need to be escaped or quoted for shells.\n\
137 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
138 Pattern matches return the string matched between \\( and \\) or null; if\n\
139 \\( and \\) are not used, they return the number of characters matched or 0.\n\
141 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
147 main (int argc, char **argv)
151 program_name = argv[0];
152 setlocale (LC_ALL, "");
153 bindtextdomain (PACKAGE, LOCALEDIR);
154 textdomain (PACKAGE);
156 atexit (close_stdout);
158 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
160 /* The above handles --help and --version.
161 Since there is no other invocation of getopt, handle `--' here. */
162 if (argc > 1 && STREQ (argv[1], "--"))
170 error (0, 0, _("too few arguments"));
178 error (2, 0, _("syntax error"));
184 /* Return a VALUE for I. */
187 int_value (intmax_t i)
197 /* Return a VALUE for S. */
206 v->u.s = xstrdup (s);
210 /* Free VALUE V, including structure components. */
215 if (v->type == string)
220 /* Store a printable representation of I somewhere into BUF, and
221 return a pointer to the stored representation. */
224 inttostr (intmax_t i, char buf[INT_STRLEN_BOUND (intmax_t) + 1])
227 char *p = buf + INT_STRLEN_BOUND (intmax_t);
232 *--p = '0' + ui % 10;
233 while ((ui /= 10) != 0);
245 char buf[INT_STRLEN_BOUND (intmax_t) + 1];
250 p = inttostr (v->u.i, buf);
262 /* Return nonzero if V is a null-string or zero-number. */
272 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
278 /* Coerce V to a string value (can't fail). */
283 char buf[INT_STRLEN_BOUND (intmax_t) + 1];
288 v->u.s = xstrdup (inttostr (v->u.i, buf));
298 /* Coerce V to an integer value. Return 1 on success, 0 on failure. */
321 i = i * 10 + *cp - '0';
328 v->u.i = i * (neg ? -1 : 1);
336 /* Return nonzero and advance if the next token matches STR exactly.
337 STR must not be NULL. */
346 int r = strcmp (*args, str) == 0;
352 /* Return nonzero if there no more tokens. */
361 /* Print evaluation trace and args remaining. */
370 for (a = args; *a; a++)
376 /* Do the : operator.
377 SV is the VALUE for the lhs (the string),
378 PV is the VALUE for the rhs (the pattern). */
381 docolon (VALUE *sv, VALUE *pv)
385 struct re_pattern_buffer re_buffer;
386 struct re_registers re_regs;
393 if (pv->u.s[0] == '^')
396 warning: unportable BRE: `%s': using `^' as the first character\n\
397 of the basic regular expression is not portable; it is being ignored"),
401 len = strlen (pv->u.s);
402 memset (&re_buffer, 0, sizeof (re_buffer));
403 memset (&re_regs, 0, sizeof (re_regs));
404 re_buffer.allocated = 2 * len;
405 if (re_buffer.allocated < len)
407 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
408 re_buffer.translate = 0;
409 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
410 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
412 error (2, 0, "%s", errmsg);
414 matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
417 /* Were \(...\) used? */
418 if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
420 sv->u.s[re_regs.end[1]] = '\0';
421 v = str_value (sv->u.s + re_regs.start[1]);
424 v = int_value (matchlen);
428 /* Match failed -- return the right kind of null. */
429 if (re_buffer.re_nsub > 0)
434 free (re_buffer.buffer);
438 /* Handle bare operands and ( expr ) syntax. */
449 error (2, 0, _("syntax error"));
455 error (2, 0, _("syntax error"));
460 error (2, 0, _("syntax error"));
462 return str_value (*args++);
465 /* Handle match, substr, index, and length keywords, and quoting "+". */
482 error (2, 0, _("syntax error"));
483 return str_value (*args++);
485 else if (nextarg ("length"))
489 v = int_value (strlen (r->u.s));
493 else if (nextarg ("match"))
502 else if (nextarg ("index"))
508 v = int_value (strcspn (l->u.s, r->u.s) + 1);
509 if (v->u.i == strlen (l->u.s) + 1)
515 else if (nextarg ("substr"))
521 if (!toarith (i1) || !toarith (i2)
522 || strlen (l->u.s) < i1->u.i
523 || i1->u.i <= 0 || i2->u.i <= 0)
529 v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
530 l->u.s + i1->u.i - 1, i2->u.i);
542 /* Handle : operator (pattern matching).
543 Calls docolon to do the real work. */
571 /* Handle *, /, % operators. */
578 enum { multiply, divide, mod } fxn;
589 else if (nextarg ("/"))
591 else if (nextarg ("%"))
596 if (!toarith (l) || !toarith (r))
597 error (2, 0, _("non-numeric argument"));
599 val = l->u.i * r->u.i;
603 error (2, 0, _("division by zero"));
604 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
612 /* Handle +, - operators. */
619 enum { plus, minus } fxn;
630 else if (nextarg ("-"))
635 if (!toarith (l) || !toarith (r))
636 error (2, 0, _("non-numeric argument"));
637 val = fxn == plus ? l->u.i + r->u.i : l->u.i - r->u.i;
644 /* Handle comparisons. */
653 less_than, less_equal, equal, not_equal, greater_equal, greater_than
667 else if (nextarg ("<="))
669 else if (nextarg ("=") || nextarg ("=="))
671 else if (nextarg ("!="))
673 else if (nextarg (">="))
675 else if (nextarg (">"))
682 lval = strcoll (l->u.s, r->u.s);
684 if (toarith (l) && toarith (r))
691 case less_than: val = (lval < rval); break;
692 case less_equal: val = (lval <= rval); break;
693 case equal: val = (lval == rval); break;
694 case not_equal: val = (lval != rval); break;
695 case greater_equal: val = (lval >= rval); break;
696 case greater_than: val = (lval > rval); break;
722 if (null (l) || null (r))