1 /* expr -- evaluate expressions.
2 Copyright (C) 86, 1991-1997, 1999-2002 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"
41 /* The official name of this program (e.g., no `g' prefix). */
42 #define PROGRAM_NAME "expr"
44 #define AUTHORS "Mike Parker"
47 #define NEW(Type) XMALLOC (Type, 1)
48 #define OLD(x) free ((char *) x)
50 /* The kinds of value we can have. */
56 typedef enum valtype TYPE
;
61 TYPE type
; /* Which kind. */
63 { /* The value itself. */
68 typedef struct valinfo VALUE
;
70 /* The arguments given to the program, minus the program name. */
73 /* The name this program was run with. */
76 static VALUE
*eval
PARAMS ((void));
77 static int nomoreargs
PARAMS ((void));
78 static int null
PARAMS ((VALUE
*v
));
79 static void printv
PARAMS ((VALUE
*v
));
85 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
90 Usage: %s EXPRESSION\n\
93 program_name
, program_name
);
95 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
96 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
99 Print the value of EXPRESSION to standard output. A blank line below\n\
100 separates increasing precedence groups. EXPRESSION may be:\n\
102 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
104 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
108 ARG1 < ARG2 ARG1 is less than ARG2\n\
109 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
110 ARG1 = ARG2 ARG1 is equal to ARG2\n\
111 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
112 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
113 ARG1 > ARG2 ARG1 is greater than ARG2\n\
117 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
118 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
122 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
123 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
124 ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
128 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
130 match STRING REGEXP same as STRING : REGEXP\n\
131 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
132 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
133 length STRING length of STRING\n\
136 + TOKEN interpret TOKEN as a string, even if it is a\n\
137 keyword like `match' or an operator like `/'\n\
139 ( EXPRESSION ) value of EXPRESSION\n\
143 Beware that many operators need to be escaped or quoted for shells.\n\
144 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
145 Pattern matches return the string matched between \\( and \\) or null; if\n\
146 \\( and \\) are not used, they return the number of characters matched or 0.\n\
148 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT
);
154 main (int argc
, char **argv
)
158 program_name
= argv
[0];
159 setlocale (LC_ALL
, "");
160 bindtextdomain (PACKAGE
, LOCALEDIR
);
161 textdomain (PACKAGE
);
163 atexit (close_stdout
);
165 parse_long_options (argc
, argv
, PROGRAM_NAME
, GNU_PACKAGE
, VERSION
,
167 /* The above handles --help and --version.
168 Since there is no other invocation of getopt, handle `--' here. */
169 if (argc
> 1 && STREQ (argv
[1], "--"))
177 error (0, 0, _("too few arguments"));
185 error (2, 0, _("syntax error"));
191 /* Return a VALUE for I. */
194 int_value (intmax_t i
)
204 /* Return a VALUE for S. */
213 v
->u
.s
= xstrdup (s
);
217 /* Free VALUE V, including structure components. */
222 if (v
->type
== string
)
227 /* Store a printable representation of I somewhere into BUF, and
228 return a pointer to the stored representation. */
231 inttostr (intmax_t i
, char buf
[INT_STRLEN_BOUND (intmax_t) + 1])
234 char *p
= buf
+ INT_STRLEN_BOUND (intmax_t);
239 *--p
= '0' + ui
% 10;
240 while ((ui
/= 10) != 0);
252 char buf
[INT_STRLEN_BOUND (intmax_t) + 1];
257 p
= inttostr (v
->u
.i
, buf
);
269 /* Return nonzero if V is a null-string or zero-number. */
279 return v
->u
.s
[0] == '\0' || strcmp (v
->u
.s
, "0") == 0;
285 /* Coerce V to a string value (can't fail). */
290 char buf
[INT_STRLEN_BOUND (intmax_t) + 1];
295 v
->u
.s
= xstrdup (inttostr (v
->u
.i
, buf
));
305 /* Coerce V to an integer value. Return 1 on success, 0 on failure. */
328 i
= i
* 10 + *cp
- '0';
335 v
->u
.i
= i
* (neg
? -1 : 1);
343 /* Return nonzero and advance if the next token matches STR exactly.
344 STR must not be NULL. */
353 int r
= strcoll (*args
, str
) == 0;
359 /* Return nonzero if there no more tokens. */
368 /* Print evaluation trace and args remaining. */
377 for (a
= args
; *a
; a
++)
383 /* Do the : operator.
384 SV is the VALUE for the lhs (the string),
385 PV is the VALUE for the rhs (the pattern). */
388 docolon (VALUE
*sv
, VALUE
*pv
)
392 struct re_pattern_buffer re_buffer
;
393 struct re_registers re_regs
;
400 if (pv
->u
.s
[0] == '^')
403 warning: unportable BRE: `%s': using `^' as the first character\n\
404 of the basic regular expression is not portable; it is being ignored"),
408 len
= strlen (pv
->u
.s
);
409 memset (&re_buffer
, 0, sizeof (re_buffer
));
410 memset (&re_regs
, 0, sizeof (re_regs
));
411 re_buffer
.allocated
= 2 * len
;
412 if (re_buffer
.allocated
< len
)
414 re_buffer
.buffer
= (unsigned char *) xmalloc (re_buffer
.allocated
);
415 re_buffer
.translate
= 0;
416 re_syntax_options
= RE_SYNTAX_POSIX_BASIC
;
417 errmsg
= re_compile_pattern (pv
->u
.s
, len
, &re_buffer
);
419 error (2, 0, "%s", errmsg
);
421 matchlen
= re_match (&re_buffer
, sv
->u
.s
, strlen (sv
->u
.s
), 0, &re_regs
);
424 /* Were \(...\) used? */
425 if (re_buffer
.re_nsub
> 0)/* was (re_regs.start[1] >= 0) */
427 sv
->u
.s
[re_regs
.end
[1]] = '\0';
428 v
= str_value (sv
->u
.s
+ re_regs
.start
[1]);
431 v
= int_value (matchlen
);
435 /* Match failed -- return the right kind of null. */
436 if (re_buffer
.re_nsub
> 0)
441 free (re_buffer
.buffer
);
445 /* Handle bare operands and ( expr ) syntax. */
456 error (2, 0, _("syntax error"));
462 error (2, 0, _("syntax error"));
467 error (2, 0, _("syntax error"));
469 return str_value (*args
++);
472 /* Handle match, substr, index, and length keywords, and quoting "+". */
489 error (2, 0, _("syntax error"));
490 return str_value (*args
++);
492 else if (nextarg ("length"))
496 v
= int_value (strlen (r
->u
.s
));
500 else if (nextarg ("match"))
509 else if (nextarg ("index"))
515 v
= int_value (strcspn (l
->u
.s
, r
->u
.s
) + 1);
516 if (v
->u
.i
== strlen (l
->u
.s
) + 1)
522 else if (nextarg ("substr"))
528 if (!toarith (i1
) || !toarith (i2
)
529 || strlen (l
->u
.s
) < i1
->u
.i
530 || i1
->u
.i
<= 0 || i2
->u
.i
<= 0)
536 v
->u
.s
= strncpy ((char *) xmalloc (i2
->u
.i
+ 1),
537 l
->u
.s
+ i1
->u
.i
- 1, i2
->u
.i
);
549 /* Handle : operator (pattern matching).
550 Calls docolon to do the real work. */
578 /* Handle *, /, % operators. */
585 enum { multiply
, divide
, mod
} fxn
;
596 else if (nextarg ("/"))
598 else if (nextarg ("%"))
603 if (!toarith (l
) || !toarith (r
))
604 error (2, 0, _("non-numeric argument"));
606 val
= l
->u
.i
* r
->u
.i
;
610 error (2, 0, _("division by zero"));
611 val
= fxn
== divide
? l
->u
.i
/ r
->u
.i
: l
->u
.i
% r
->u
.i
;
619 /* Handle +, - operators. */
626 enum { plus
, minus
} fxn
;
637 else if (nextarg ("-"))
642 if (!toarith (l
) || !toarith (r
))
643 error (2, 0, _("non-numeric argument"));
644 val
= fxn
== plus
? l
->u
.i
+ r
->u
.i
: l
->u
.i
- r
->u
.i
;
651 /* Handle comparisons. */
660 less_than
, less_equal
, equal
, not_equal
, greater_equal
, greater_than
674 else if (nextarg ("<="))
676 else if (nextarg ("=") || nextarg ("=="))
678 else if (nextarg ("!="))
680 else if (nextarg (">="))
682 else if (nextarg (">"))
689 lval
= strcoll (l
->u
.s
, r
->u
.s
);
691 if (toarith (l
) && toarith (r
))
698 case less_than
: val
= (lval
< rval
); break;
699 case less_equal
: val
= (lval
<= rval
); break;
700 case equal
: val
= (lval
== rval
); break;
701 case not_equal
: val
= (lval
!= rval
); break;
702 case greater_equal
: val
= (lval
>= rval
); break;
703 case greater_than
: val
= (lval
> rval
); break;
729 if (null (l
) || null (r
))