1 /* expr -- evaluate expressions.
2 Copyright (C) 86, 1991-1997, 1999, 2000 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
*docolon
PARAMS ((VALUE
*sv
, VALUE
*pv
));
82 static VALUE
*eval
PARAMS ((void));
83 static VALUE
*int_value
PARAMS ((int i
));
84 static VALUE
*str_value
PARAMS ((char *s
));
85 static int isstring
PARAMS ((VALUE
*v
));
86 static int nextarg
PARAMS ((char *str
));
87 static int nomoreargs
PARAMS ((void));
88 static int null
PARAMS ((VALUE
*v
));
89 static int toarith
PARAMS ((VALUE
*v
));
90 static void freev
PARAMS ((VALUE
*v
));
91 static void printv
PARAMS ((VALUE
*v
));
92 static void tostring
PARAMS ((VALUE
*v
));
102 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
107 Usage: %s EXPRESSION\n\
110 program_name
, program_name
);
113 --help display this help and exit\n\
114 --version output version information and exit\n\
118 Print the value of EXPRESSION to standard output. A blank line below\n\
119 separates increasing precedence groups. EXPRESSION may be:\n\
121 ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\
123 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\
125 ARG1 < ARG2 ARG1 is less than ARG2\n\
126 ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\
127 ARG1 = ARG2 ARG1 is equal to ARG2\n\
128 ARG1 != ARG2 ARG1 is unequal to ARG2\n\
129 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\
130 ARG1 > ARG2 ARG1 is greater than ARG2\n\
132 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
133 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
135 ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
136 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\
137 ARG1 %% ARG2 arithmetic remainder of ARG1 divided by ARG2\n\
139 STRING : REGEXP anchored pattern match of REGEXP in STRING\n\
141 match STRING REGEXP same as STRING : REGEXP\n\
142 substr STRING POS LENGTH substring of STRING, POS counted from 1\n\
143 index STRING CHARS index in STRING where any CHARS is found, or 0\n\
144 length STRING length of STRING\n\
145 quote TOKEN interpret TOKEN as a string, even if it is a\n\
146 keyword like `match' or an operator like `/'\n\
148 ( EXPRESSION ) value of EXPRESSION\n\
152 Beware that many operators need to be escaped or quoted for shells.\n\
153 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
154 Pattern matches return the string matched between \\( and \\) or null; if\n\
155 \\( and \\) are not used, they return the number of characters matched or 0.\n\
157 puts (_("\nReport bugs to <bug-sh-utils@gnu.org>."));
163 main (int argc
, char **argv
)
167 program_name
= argv
[0];
168 setlocale (LC_ALL
, "");
169 bindtextdomain (PACKAGE
, LOCALEDIR
);
170 textdomain (PACKAGE
);
172 atexit (close_stdout
);
174 posixly_correct
= (getenv ("POSIXLY_CORRECT") != NULL
);
176 /* Recognize --help or --version only if POSIXLY_CORRECT is not set. */
177 if (!posixly_correct
)
178 parse_long_options (argc
, argv
, PROGRAM_NAME
, GNU_PACKAGE
, VERSION
,
183 error (0, 0, _("too few arguments"));
191 error (2, 0, _("syntax error"));
197 /* Return a VALUE for I. */
210 /* Return a VALUE for S. */
219 v
->u
.s
= xstrdup (s
);
223 /* Free VALUE V, including structure components. */
228 if (v
->type
== string
)
241 printf ("%d\n", v
->u
.i
);
244 printf ("%s\n", v
->u
.s
);
251 /* Return nonzero if V is a null-string or zero-number. */
261 return v
->u
.s
[0] == '\0' || strcmp (v
->u
.s
, "0") == 0;
267 /* Return nonzero if V is a string value. */
272 return v
->type
== string
;
275 /* Coerce V to a string value (can't fail). */
285 temp
= xmalloc (4 * (sizeof (int) / sizeof (char)));
286 sprintf (temp
, "%d", v
->u
.i
);
297 /* Coerce V to an integer value. Return 1 on success, 0 on failure. */
313 /* Don't interpret the empty string as an integer. */
322 i
= i
* 10 + *cp
- '0';
327 v
->u
.i
= i
* (neg
? -1 : 1);
335 /* Return nonzero if the next token matches STR exactly.
336 STR must not be NULL. */
343 return strcmp (*args
, str
) == 0;
346 /* Return nonzero if there no more tokens. */
354 /* The comparison operator handling functions. */
356 #define cmpf(name, rel) \
358 int name (l, r) VALUE *l; VALUE *r; \
360 if (isstring (l) || isstring (r)) \
364 return strcmp (l->u.s, r->u.s) rel 0; \
367 return l->u.i rel r->u.i; \
370 cmpf (less_equal
, <=)
373 cmpf (greater_equal
, >=)
374 cmpf (greater_than
, >)
378 /* The arithmetic operator handling functions. */
380 #define arithf(name, op) \
382 int name (l, r) VALUE *l; VALUE *r; \
384 if (!toarith (l) || !toarith (r)) \
385 error (2, 0, _("non-numeric argument")); \
386 return l->u.i op r->u.i; \
389 #define arithdivf(name, op) \
390 int name (l, r) VALUE *l; VALUE *r; \
392 if (!toarith (l) || !toarith (r)) \
393 error (2, 0, _("non-numeric argument")); \
395 error (2, 0, _("division by zero")); \
396 return l->u.i op r->u.i; \
402 arithdivf (divide
, /)
409 /* Print evaluation trace and args remaining. */
418 for (a
= args
; *a
; a
++)
424 /* Do the : operator.
425 SV is the VALUE for the lhs (the string),
426 PV is the VALUE for the rhs (the pattern). */
429 docolon (VALUE
*sv
, VALUE
*pv
)
433 struct re_pattern_buffer re_buffer
;
434 struct re_registers re_regs
;
440 if (pv
->u
.s
[0] == '^')
443 warning: unportable BRE: `%s': using `^' as the first character\n\
444 of the basic regular expression is not portable; it is being ignored"),
448 len
= strlen (pv
->u
.s
);
449 memset (&re_buffer
, 0, sizeof (re_buffer
));
450 memset (&re_regs
, 0, sizeof (re_regs
));
451 re_buffer
.allocated
= 2 * len
;
452 re_buffer
.buffer
= (unsigned char *) xmalloc (re_buffer
.allocated
);
453 re_buffer
.translate
= 0;
454 re_syntax_options
= RE_SYNTAX_POSIX_BASIC
;
455 errmsg
= re_compile_pattern (pv
->u
.s
, len
, &re_buffer
);
457 error (2, 0, "%s", errmsg
);
459 len
= re_match (&re_buffer
, sv
->u
.s
, strlen (sv
->u
.s
), 0, &re_regs
);
462 /* Were \(...\) used? */
463 if (re_buffer
.re_nsub
> 0)/* was (re_regs.start[1] >= 0) */
465 sv
->u
.s
[re_regs
.end
[1]] = '\0';
466 v
= str_value (sv
->u
.s
+ re_regs
.start
[1]);
473 /* Match failed -- return the right kind of null. */
474 if (re_buffer
.re_nsub
> 0)
479 free (re_buffer
.buffer
);
483 /* Handle bare operands and ( expr ) syntax. */
494 error (2, 0, _("syntax error"));
501 error (2, 0, _("syntax error"));
507 error (2, 0, _("syntax error"));
509 return str_value (*args
++);
512 /* Handle match, substr, index, length, and quote keywords. */
526 if (!posixly_correct
&& nextarg ("quote"))
530 error (2, 0, _("syntax error"));
531 return str_value (*args
++);
533 else if (nextarg ("length"))
538 v
= int_value (strlen (r
->u
.s
));
542 else if (nextarg ("match"))
552 else if (nextarg ("index"))
559 v
= int_value (strcspn (l
->u
.s
, r
->u
.s
) + 1);
560 if (v
->u
.i
== (int) strlen (l
->u
.s
) + 1)
566 else if (nextarg ("substr"))
573 if (!toarith (i1
) || !toarith (i2
)
574 || i1
->u
.i
> (int) strlen (l
->u
.s
)
575 || i1
->u
.i
<= 0 || i2
->u
.i
<= 0)
581 v
->u
.s
= strncpy ((char *) xmalloc (i2
->u
.i
+ 1),
582 l
->u
.s
+ i1
->u
.i
- 1, i2
->u
.i
);
594 /* Handle : operator (pattern matching).
595 Calls docolon to do the real work. */
624 /* Handle *, /, % operators. */
642 else if (nextarg ("/"))
644 else if (nextarg ("%"))
657 /* Handle +, - operators. */
675 else if (nextarg ("-"))
688 /* Handle comparisons. */
706 else if (nextarg ("<="))
708 else if (nextarg ("=") || nextarg ("=="))
710 else if (nextarg ("!="))
712 else if (nextarg (">="))
714 else if (nextarg (">"))
747 if (null (l
) || null (r
))