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 /* Non-zero if the POSIXLY_CORRECT environment variable is set.
72 The unary operator `quote' is disabled when this variable is zero. */
73 static int posixly_correct;
75 /* The arguments given to the program, minus the program name. */
78 /* The name this program was run with. */
81 static VALUE *eval PARAMS ((void));
82 static int nomoreargs PARAMS ((void));
83 static int null PARAMS ((VALUE *v));
84 static void printv PARAMS ((VALUE *v));
90 fprintf (stderr, _("Try `%s --help' for more information.\n"),
95 Usage: %s EXPRESSION\n\
98 program_name, program_name);
101 --help display this help and exit\n\
102 --version output version information and exit\n\
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\
113 ARG1 < ARG2 ARG1 is less than ARG2\n\
114 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
115 ARG1 = ARG2 ARG1 is equal to ARG2\n\
116 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
117 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
118 ARG1 > ARG2 ARG1 is greater than ARG2\n\
120 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
121 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
123 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
124 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
125 ARG1 %% ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
127 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
129 match STRING REGEXP same as STRING : REGEXP\n\
130 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
131 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
132 length STRING length of STRING\n\
133 quote TOKEN interpret TOKEN as a string, even if it is a\n\
134 keyword like `match' or an operator like `/'\n\
136 ( EXPRESSION ) value of EXPRESSION\n\
140 Beware that many operators need to be escaped or quoted for shells.\n\
141 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
142 Pattern matches return the string matched between \\( and \\) or null; if\n\
143 \\( and \\) are not used, they return the number of characters matched or 0.\n\
145 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
151 main (int argc, char **argv)
155 program_name = argv[0];
156 setlocale (LC_ALL, "");
157 bindtextdomain (PACKAGE, LOCALEDIR);
158 textdomain (PACKAGE);
160 atexit (close_stdout);
162 posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
164 /* Recognize --help or --version only if POSIXLY_CORRECT is not set. */
165 if (!posixly_correct)
166 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
168 /* The above handles --help and --version.
169 Since there is no other invocation of getopt, handle `--' here. */
170 if (argc > 1 && STREQ (argv[1], "--"))
178 error (0, 0, _("too few arguments"));
186 error (2, 0, _("syntax error"));
192 /* Return a VALUE for I. */
195 int_value (intmax_t i)
205 /* Return a VALUE for S. */
214 v->u.s = xstrdup (s);
218 /* Free VALUE V, including structure components. */
223 if (v->type == string)
228 /* Store a printable representation of I somewhere into BUF, and
229 return a pointer to the stored representation. */
232 inttostr (intmax_t i, char buf[INT_STRLEN_BOUND (intmax_t) + 1])
235 char *p = buf + INT_STRLEN_BOUND (intmax_t);
240 *--p = '0' + ui % 10;
241 while ((ui /= 10) != 0);
253 char buf[INT_STRLEN_BOUND (intmax_t) + 1];
258 p = inttostr (v->u.i, buf);
270 /* Return nonzero if V is a null-string or zero-number. */
280 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
286 /* Coerce V to a string value (can't fail). */
291 char buf[INT_STRLEN_BOUND (intmax_t) + 1];
296 v->u.s = xstrdup (inttostr (v->u.i, buf));
306 /* Coerce V to an integer value. Return 1 on success, 0 on failure. */
329 i = i * 10 + *cp - '0';
336 v->u.i = i * (neg ? -1 : 1);
344 /* Return nonzero and advance if the next token matches STR exactly.
345 STR must not be NULL. */
354 int r = strcmp (*args, str) == 0;
360 /* Return nonzero if there no more tokens. */
369 /* Print evaluation trace and args remaining. */
378 for (a = args; *a; a++)
384 /* Do the : operator.
385 SV is the VALUE for the lhs (the string),
386 PV is the VALUE for the rhs (the pattern). */
389 docolon (VALUE *sv, VALUE *pv)
393 struct re_pattern_buffer re_buffer;
394 struct re_registers re_regs;
401 if (pv->u.s[0] == '^')
404 warning: unportable BRE: `%s': using `^' as the first character\n\
405 of the basic regular expression is not portable; it is being ignored"),
409 len = strlen (pv->u.s);
410 memset (&re_buffer, 0, sizeof (re_buffer));
411 memset (&re_regs, 0, sizeof (re_regs));
412 re_buffer.allocated = 2 * len;
413 if (re_buffer.allocated < len)
415 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
416 re_buffer.translate = 0;
417 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
418 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
420 error (2, 0, "%s", errmsg);
422 matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
425 /* Were \(...\) used? */
426 if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
428 sv->u.s[re_regs.end[1]] = '\0';
429 v = str_value (sv->u.s + re_regs.start[1]);
432 v = int_value (matchlen);
436 /* Match failed -- return the right kind of null. */
437 if (re_buffer.re_nsub > 0)
442 free (re_buffer.buffer);
446 /* Handle bare operands and ( expr ) syntax. */
457 error (2, 0, _("syntax error"));
463 error (2, 0, _("syntax error"));
468 error (2, 0, _("syntax error"));
470 return str_value (*args++);
473 /* Handle match, substr, index, length, and quote keywords. */
487 if (!posixly_correct && nextarg ("quote"))
490 error (2, 0, _("syntax error"));
491 return str_value (*args++);
493 else if (nextarg ("length"))
497 v = int_value (strlen (r->u.s));
501 else if (nextarg ("match"))
510 else if (nextarg ("index"))
516 v = int_value (strcspn (l->u.s, r->u.s) + 1);
517 if (v->u.i == strlen (l->u.s) + 1)
523 else if (nextarg ("substr"))
529 if (!toarith (i1) || !toarith (i2)
530 || strlen (l->u.s) < i1->u.i
531 || i1->u.i <= 0 || i2->u.i <= 0)
537 v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
538 l->u.s + i1->u.i - 1, i2->u.i);
550 /* Handle : operator (pattern matching).
551 Calls docolon to do the real work. */
579 /* Handle *, /, % operators. */
586 enum { multiply, divide, mod } fxn;
597 else if (nextarg ("/"))
599 else if (nextarg ("%"))
604 if (!toarith (l) || !toarith (r))
605 error (2, 0, _("non-numeric argument"));
607 val = l->u.i * r->u.i;
611 error (2, 0, _("division by zero"));
612 val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
620 /* Handle +, - operators. */
627 enum { plus, minus } fxn;
638 else if (nextarg ("-"))
643 if (!toarith (l) || !toarith (r))
644 error (2, 0, _("non-numeric argument"));
645 val = fxn == plus ? l->u.i + r->u.i : l->u.i - r->u.i;
652 /* Handle comparisons. */
661 less_than, less_equal, equal, not_equal, greater_equal, greater_than
675 else if (nextarg ("<="))
677 else if (nextarg ("=") || nextarg ("=="))
679 else if (nextarg ("!="))
681 else if (nextarg (">="))
683 else if (nextarg (">"))
690 lval = strcoll (l->u.s, r->u.s);
692 if (toarith (l) && toarith (r))
699 case less_than: val = (lval < rval); break;
700 case less_equal: val = (lval <= rval); break;
701 case equal: val = (lval == rval); break;
702 case not_equal: val = (lval != rval); break;
703 case greater_equal: val = (lval >= rval); break;
704 case greater_than: val = (lval > rval); break;
730 if (null (l) || null (r))