15b81b6b3SRodney W. Grimes %{
29ddb49cbSWarner Losh /*-
39ddb49cbSWarner Losh * Written by Pace Willisson (pace@blitz.com)
455c497bfSJ.T. Conklin * and placed in the public domain.
55b81b6b3SRodney W. Grimes *
655c497bfSJ.T. Conklin * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
75b81b6b3SRodney W. Grimes */
84cf61abaSJ.T. Conklin
964867286SStefan Eßer #include <sys/types.h>
10f07e4247SGarrett Wollman
115b81b6b3SRodney W. Grimes #include <ctype.h>
1255c497bfSJ.T. Conklin #include <err.h>
13915198b4SStefan Eßer #include <errno.h>
14f07e4247SGarrett Wollman #include <inttypes.h>
15915198b4SStefan Eßer #include <limits.h>
16f07e4247SGarrett Wollman #include <locale.h>
17f07e4247SGarrett Wollman #include <stdio.h>
18f07e4247SGarrett Wollman #include <stdlib.h>
19f07e4247SGarrett Wollman #include <string.h>
20f07e4247SGarrett Wollman #include <regex.h>
21f07e4247SGarrett Wollman #include <unistd.h>
22f07e4247SGarrett Wollman
23f07e4247SGarrett Wollman /*
24f07e4247SGarrett Wollman * POSIX specifies a specific error code for syntax errors. We exit
25f07e4247SGarrett Wollman * with this code for all errors.
26f07e4247SGarrett Wollman */
27f07e4247SGarrett Wollman #define ERR_EXIT 2
285b81b6b3SRodney W. Grimes
295b81b6b3SRodney W. Grimes enum valtype {
30717252eaSJoerg Wunsch integer, numeric_string, string
315b81b6b3SRodney W. Grimes } ;
325b81b6b3SRodney W. Grimes
335b81b6b3SRodney W. Grimes struct val {
345b81b6b3SRodney W. Grimes enum valtype type;
355b81b6b3SRodney W. Grimes union {
365b81b6b3SRodney W. Grimes char *s;
37f07e4247SGarrett Wollman intmax_t i;
385b81b6b3SRodney W. Grimes } u;
395b81b6b3SRodney W. Grimes } ;
405b81b6b3SRodney W. Grimes
416fbe7bf4SStefan Eßer char **av;
426fbe7bf4SStefan Eßer int nonposix;
435b81b6b3SRodney W. Grimes struct val *result;
443d06e95dSKris Kennaway
45fa717604SStefan Eßer void assert_to_integer(struct val *);
466fbe7bf4SStefan Eßer void assert_div(intmax_t, intmax_t);
476fbe7bf4SStefan Eßer void assert_minus(intmax_t, intmax_t, intmax_t);
486fbe7bf4SStefan Eßer void assert_plus(intmax_t, intmax_t, intmax_t);
496fbe7bf4SStefan Eßer void assert_times(intmax_t, intmax_t, intmax_t);
506fbe7bf4SStefan Eßer int compare_vals(struct val *, struct val *);
517669d0fcSWarner Losh void free_value(struct val *);
52fa717604SStefan Eßer int is_integer(const char *);
5369759f08SStefan Eßer int is_string(struct val *);
54fa717604SStefan Eßer int is_zero_or_null(struct val *);
55f07e4247SGarrett Wollman struct val *make_integer(intmax_t);
567669d0fcSWarner Losh struct val *make_str(const char *);
577669d0fcSWarner Losh struct val *op_and(struct val *, struct val *);
587669d0fcSWarner Losh struct val *op_colon(struct val *, struct val *);
597669d0fcSWarner Losh struct val *op_div(struct val *, struct val *);
607669d0fcSWarner Losh struct val *op_eq(struct val *, struct val *);
617669d0fcSWarner Losh struct val *op_ge(struct val *, struct val *);
627669d0fcSWarner Losh struct val *op_gt(struct val *, struct val *);
637669d0fcSWarner Losh struct val *op_le(struct val *, struct val *);
647669d0fcSWarner Losh struct val *op_lt(struct val *, struct val *);
657669d0fcSWarner Losh struct val *op_minus(struct val *, struct val *);
667669d0fcSWarner Losh struct val *op_ne(struct val *, struct val *);
677669d0fcSWarner Losh struct val *op_or(struct val *, struct val *);
687669d0fcSWarner Losh struct val *op_plus(struct val *, struct val *);
697669d0fcSWarner Losh struct val *op_rem(struct val *, struct val *);
707669d0fcSWarner Losh struct val *op_times(struct val *, struct val *);
71fa717604SStefan Eßer int to_integer(struct val *);
727669d0fcSWarner Losh void to_string(struct val *);
737669d0fcSWarner Losh int yyerror(const char *);
747669d0fcSWarner Losh int yylex(void);
755b81b6b3SRodney W. Grimes
765b81b6b3SRodney W. Grimes %}
775b81b6b3SRodney W. Grimes
785b81b6b3SRodney W. Grimes %union
795b81b6b3SRodney W. Grimes {
805b81b6b3SRodney W. Grimes struct val *val;
815b81b6b3SRodney W. Grimes }
825b81b6b3SRodney W. Grimes
835b81b6b3SRodney W. Grimes %left <val> '|'
845b81b6b3SRodney W. Grimes %left <val> '&'
855b81b6b3SRodney W. Grimes %left <val> '=' '>' '<' GE LE NE
865b81b6b3SRodney W. Grimes %left <val> '+' '-'
875b81b6b3SRodney W. Grimes %left <val> '*' '/' '%'
885b81b6b3SRodney W. Grimes %left <val> ':'
895b81b6b3SRodney W. Grimes
905b81b6b3SRodney W. Grimes %token <val> TOKEN
915b81b6b3SRodney W. Grimes %type <val> start expr
925b81b6b3SRodney W. Grimes
935b81b6b3SRodney W. Grimes %%
945b81b6b3SRodney W. Grimes
955b81b6b3SRodney W. Grimes start: expr { result = $$; }
965b81b6b3SRodney W. Grimes
975b81b6b3SRodney W. Grimes expr: TOKEN
985b81b6b3SRodney W. Grimes | '(' expr ')' { $$ = $2; }
995b81b6b3SRodney W. Grimes | expr '|' expr { $$ = op_or($1, $3); }
1005b81b6b3SRodney W. Grimes | expr '&' expr { $$ = op_and($1, $3); }
1015b81b6b3SRodney W. Grimes | expr '=' expr { $$ = op_eq($1, $3); }
1025b81b6b3SRodney W. Grimes | expr '>' expr { $$ = op_gt($1, $3); }
1035b81b6b3SRodney W. Grimes | expr '<' expr { $$ = op_lt($1, $3); }
1045b81b6b3SRodney W. Grimes | expr GE expr { $$ = op_ge($1, $3); }
1055b81b6b3SRodney W. Grimes | expr LE expr { $$ = op_le($1, $3); }
1065b81b6b3SRodney W. Grimes | expr NE expr { $$ = op_ne($1, $3); }
1075b81b6b3SRodney W. Grimes | expr '+' expr { $$ = op_plus($1, $3); }
1085b81b6b3SRodney W. Grimes | expr '-' expr { $$ = op_minus($1, $3); }
1095b81b6b3SRodney W. Grimes | expr '*' expr { $$ = op_times($1, $3); }
1105b81b6b3SRodney W. Grimes | expr '/' expr { $$ = op_div($1, $3); }
1115b81b6b3SRodney W. Grimes | expr '%' expr { $$ = op_rem($1, $3); }
1125b81b6b3SRodney W. Grimes | expr ':' expr { $$ = op_colon($1, $3); }
1135b81b6b3SRodney W. Grimes ;
1145b81b6b3SRodney W. Grimes
1155b81b6b3SRodney W. Grimes %%
1165b81b6b3SRodney W. Grimes
1175b81b6b3SRodney W. Grimes struct val *
118f07e4247SGarrett Wollman make_integer(intmax_t i)
1195b81b6b3SRodney W. Grimes {
1205b81b6b3SRodney W. Grimes struct val *vp;
1215b81b6b3SRodney W. Grimes
1225b81b6b3SRodney W. Grimes vp = (struct val *)malloc(sizeof(*vp));
12369759f08SStefan Eßer if (vp == NULL)
124f07e4247SGarrett Wollman errx(ERR_EXIT, "malloc() failed");
1255b81b6b3SRodney W. Grimes
1265b81b6b3SRodney W. Grimes vp->type = integer;
1275b81b6b3SRodney W. Grimes vp->u.i = i;
12869759f08SStefan Eßer return (vp);
1295b81b6b3SRodney W. Grimes }
1305b81b6b3SRodney W. Grimes
1315b81b6b3SRodney W. Grimes struct val *
make_str(const char * s)1327669d0fcSWarner Losh make_str(const char *s)
1335b81b6b3SRodney W. Grimes {
1345b81b6b3SRodney W. Grimes struct val *vp;
1355b81b6b3SRodney W. Grimes
1365b81b6b3SRodney W. Grimes vp = (struct val *)malloc(sizeof(*vp));
13769759f08SStefan Eßer if (vp == NULL || ((vp->u.s = strdup(s)) == NULL))
138f07e4247SGarrett Wollman errx(ERR_EXIT, "malloc() failed");
1395b81b6b3SRodney W. Grimes
140fa717604SStefan Eßer if (is_integer(s))
141f07e4247SGarrett Wollman vp->type = numeric_string;
142fa717604SStefan Eßer else
143fa717604SStefan Eßer vp->type = string;
1442a353a9fSJoerg Wunsch
14569759f08SStefan Eßer return (vp);
1465b81b6b3SRodney W. Grimes }
1475b81b6b3SRodney W. Grimes
1485b81b6b3SRodney W. Grimes void
free_value(struct val * vp)1497669d0fcSWarner Losh free_value(struct val *vp)
1505b81b6b3SRodney W. Grimes {
151717252eaSJoerg Wunsch if (vp->type == string || vp->type == numeric_string)
1525b81b6b3SRodney W. Grimes free(vp->u.s);
1535b81b6b3SRodney W. Grimes }
1545b81b6b3SRodney W. Grimes
155fa717604SStefan Eßer int
to_integer(struct val * vp)1567669d0fcSWarner Losh to_integer(struct val *vp)
1575b81b6b3SRodney W. Grimes {
158f07e4247SGarrett Wollman intmax_t i;
1595b81b6b3SRodney W. Grimes
160fa717604SStefan Eßer /* we can only convert numeric_string to integer, here */
161fa717604SStefan Eßer if (vp->type == numeric_string) {
162915198b4SStefan Eßer errno = 0;
163f07e4247SGarrett Wollman i = strtoimax(vp->u.s, (char **)NULL, 10);
164fa717604SStefan Eßer /* just keep as numeric_string, if the conversion fails */
165fa717604SStefan Eßer if (errno != ERANGE) {
1665b81b6b3SRodney W. Grimes free(vp->u.s);
1675b81b6b3SRodney W. Grimes vp->u.i = i;
168717252eaSJoerg Wunsch vp->type = integer;
169fa717604SStefan Eßer }
170fa717604SStefan Eßer }
171fa717604SStefan Eßer return (vp->type == integer);
172fa717604SStefan Eßer }
173fa717604SStefan Eßer
174fa717604SStefan Eßer void
assert_to_integer(struct val * vp)175fa717604SStefan Eßer assert_to_integer(struct val *vp)
176fa717604SStefan Eßer {
177fa717604SStefan Eßer if (vp->type == string)
178fa717604SStefan Eßer errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s);
179fa717604SStefan Eßer if (!to_integer(vp))
180fa717604SStefan Eßer errx(ERR_EXIT, "operand too large: '%s'", vp->u.s);
1815b81b6b3SRodney W. Grimes }
1825b81b6b3SRodney W. Grimes
1835b81b6b3SRodney W. Grimes void
to_string(struct val * vp)1847669d0fcSWarner Losh to_string(struct val *vp)
1855b81b6b3SRodney W. Grimes {
1865b81b6b3SRodney W. Grimes char *tmp;
1875b81b6b3SRodney W. Grimes
188717252eaSJoerg Wunsch if (vp->type == string || vp->type == numeric_string)
1895b81b6b3SRodney W. Grimes return;
1905b81b6b3SRodney W. Grimes
191f07e4247SGarrett Wollman /*
192f07e4247SGarrett Wollman * log_10(x) ~= 0.3 * log_2(x). Rounding up gives the number
193f07e4247SGarrett Wollman * of digits; add one each for the sign and terminating null
194f07e4247SGarrett Wollman * character, respectively.
195f07e4247SGarrett Wollman */
196f07e4247SGarrett Wollman #define NDIGITS(x) (3 * (sizeof(x) * CHAR_BIT) / 10 + 1 + 1 + 1)
197f07e4247SGarrett Wollman tmp = malloc(NDIGITS(vp->u.i));
198f07e4247SGarrett Wollman if (tmp == NULL)
199f07e4247SGarrett Wollman errx(ERR_EXIT, "malloc() failed");
2005b81b6b3SRodney W. Grimes
201f07e4247SGarrett Wollman sprintf(tmp, "%jd", vp->u.i);
2025b81b6b3SRodney W. Grimes vp->type = string;
2035b81b6b3SRodney W. Grimes vp->u.s = tmp;
2045b81b6b3SRodney W. Grimes }
2055b81b6b3SRodney W. Grimes
2065b81b6b3SRodney W. Grimes int
is_integer(const char * s)207fa717604SStefan Eßer is_integer(const char *s)
208fa717604SStefan Eßer {
209fa717604SStefan Eßer if (nonposix) {
210fa717604SStefan Eßer if (*s == '\0')
211fa717604SStefan Eßer return (1);
212fa717604SStefan Eßer while (isspace((unsigned char)*s))
213fa717604SStefan Eßer s++;
214fa717604SStefan Eßer }
215fa717604SStefan Eßer if (*s == '-' || (nonposix && *s == '+'))
216fa717604SStefan Eßer s++;
217fa717604SStefan Eßer if (*s == '\0')
218fa717604SStefan Eßer return (0);
219fa717604SStefan Eßer while (isdigit((unsigned char)*s))
220fa717604SStefan Eßer s++;
221fa717604SStefan Eßer return (*s == '\0');
222fa717604SStefan Eßer }
223fa717604SStefan Eßer
224fa717604SStefan Eßer int
is_string(struct val * vp)22569759f08SStefan Eßer is_string(struct val *vp)
2265b81b6b3SRodney W. Grimes {
227717252eaSJoerg Wunsch /* only TRUE if this string is not a valid integer */
2285b81b6b3SRodney W. Grimes return (vp->type == string);
2295b81b6b3SRodney W. Grimes }
2305b81b6b3SRodney W. Grimes
2315b81b6b3SRodney W. Grimes int
yylex(void)2327669d0fcSWarner Losh yylex(void)
2335b81b6b3SRodney W. Grimes {
2345b81b6b3SRodney W. Grimes char *p;
2355b81b6b3SRodney W. Grimes
2365b81b6b3SRodney W. Grimes if (*av == NULL)
2375b81b6b3SRodney W. Grimes return (0);
2385b81b6b3SRodney W. Grimes
2395b81b6b3SRodney W. Grimes p = *av++;
2405b81b6b3SRodney W. Grimes
2415b81b6b3SRodney W. Grimes if (strlen(p) == 1) {
2425b81b6b3SRodney W. Grimes if (strchr("|&=<>+-*/%:()", *p))
2435b81b6b3SRodney W. Grimes return (*p);
2445b81b6b3SRodney W. Grimes } else if (strlen(p) == 2 && p[1] == '=') {
2455b81b6b3SRodney W. Grimes switch (*p) {
2465b81b6b3SRodney W. Grimes case '>': return (GE);
2475b81b6b3SRodney W. Grimes case '<': return (LE);
2485b81b6b3SRodney W. Grimes case '!': return (NE);
2495b81b6b3SRodney W. Grimes }
2505b81b6b3SRodney W. Grimes }
2515b81b6b3SRodney W. Grimes
2525b81b6b3SRodney W. Grimes yylval.val = make_str(p);
2535b81b6b3SRodney W. Grimes return (TOKEN);
2545b81b6b3SRodney W. Grimes }
2555b81b6b3SRodney W. Grimes
2565b81b6b3SRodney W. Grimes int
is_zero_or_null(struct val * vp)2577669d0fcSWarner Losh is_zero_or_null(struct val *vp)
2585b81b6b3SRodney W. Grimes {
25969759f08SStefan Eßer if (vp->type == integer)
2605b81b6b3SRodney W. Grimes return (vp->u.i == 0);
26169759f08SStefan Eßer
262c9fe00dcSJ.T. Conklin return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0));
2635b81b6b3SRodney W. Grimes }
2645b81b6b3SRodney W. Grimes
265717252eaSJoerg Wunsch int
main(int argc,char * argv[])266f07e4247SGarrett Wollman main(int argc, char *argv[])
2675b81b6b3SRodney W. Grimes {
268f07e4247SGarrett Wollman int c;
2694cf61abaSJ.T. Conklin
270f07e4247SGarrett Wollman setlocale(LC_ALL, "");
271c9885518SGarrett Wollman if (getenv("EXPR_COMPAT") != NULL
272c9885518SGarrett Wollman || check_utility_compat("expr")) {
27396ab7da3SGarrett Wollman av = argv + 1;
274fa717604SStefan Eßer nonposix = 1;
27596ab7da3SGarrett Wollman } else {
27669759f08SStefan Eßer while ((c = getopt(argc, argv, "e")) != -1) {
277f07e4247SGarrett Wollman switch (c) {
2781393277eSGarrett Wollman case 'e':
279fa717604SStefan Eßer nonposix = 1;
2801393277eSGarrett Wollman break;
281f07e4247SGarrett Wollman default:
28269759f08SStefan Eßer errx(ERR_EXIT,
2831393277eSGarrett Wollman "usage: expr [-e] expression\n");
28469759f08SStefan Eßer }
285f07e4247SGarrett Wollman }
286f07e4247SGarrett Wollman av = argv + optind;
28796ab7da3SGarrett Wollman }
2885b81b6b3SRodney W. Grimes
2895b81b6b3SRodney W. Grimes yyparse();
2905b81b6b3SRodney W. Grimes
2915b81b6b3SRodney W. Grimes if (result->type == integer)
292f07e4247SGarrett Wollman printf("%jd\n", result->u.i);
2935b81b6b3SRodney W. Grimes else
2945b81b6b3SRodney W. Grimes printf("%s\n", result->u.s);
2955b81b6b3SRodney W. Grimes
296717252eaSJoerg Wunsch return (is_zero_or_null(result));
2975b81b6b3SRodney W. Grimes }
2985b81b6b3SRodney W. Grimes
2995b81b6b3SRodney W. Grimes int
yyerror(const char * s __unused)3007669d0fcSWarner Losh yyerror(const char *s __unused)
3015b81b6b3SRodney W. Grimes {
302f07e4247SGarrett Wollman errx(ERR_EXIT, "syntax error");
3035b81b6b3SRodney W. Grimes }
3045b81b6b3SRodney W. Grimes
3055b81b6b3SRodney W. Grimes struct val *
op_or(struct val * a,struct val * b)3067669d0fcSWarner Losh op_or(struct val *a, struct val *b)
3075b81b6b3SRodney W. Grimes {
308fa717604SStefan Eßer if (!is_zero_or_null(a)) {
3095b81b6b3SRodney W. Grimes free_value(b);
3105b81b6b3SRodney W. Grimes return (a);
3115b81b6b3SRodney W. Grimes }
312fa717604SStefan Eßer free_value(a);
313fa717604SStefan Eßer if (!is_zero_or_null(b))
314fa717604SStefan Eßer return (b);
315fa717604SStefan Eßer free_value(b);
316fa717604SStefan Eßer return (make_integer((intmax_t)0));
3175b81b6b3SRodney W. Grimes }
3185b81b6b3SRodney W. Grimes
3195b81b6b3SRodney W. Grimes struct val *
op_and(struct val * a,struct val * b)3207669d0fcSWarner Losh op_and(struct val *a, struct val *b)
3215b81b6b3SRodney W. Grimes {
3225b81b6b3SRodney W. Grimes if (is_zero_or_null(a) || is_zero_or_null(b)) {
3235b81b6b3SRodney W. Grimes free_value(a);
3245b81b6b3SRodney W. Grimes free_value(b);
325f07e4247SGarrett Wollman return (make_integer((intmax_t)0));
3265b81b6b3SRodney W. Grimes } else {
3275b81b6b3SRodney W. Grimes free_value(b);
3285b81b6b3SRodney W. Grimes return (a);
3295b81b6b3SRodney W. Grimes }
3305b81b6b3SRodney W. Grimes }
3315b81b6b3SRodney W. Grimes
3326fbe7bf4SStefan Eßer int
compare_vals(struct val * a,struct val * b)3336fbe7bf4SStefan Eßer compare_vals(struct val *a, struct val *b)
3345b81b6b3SRodney W. Grimes {
3356fbe7bf4SStefan Eßer int r;
3365b81b6b3SRodney W. Grimes
33769759f08SStefan Eßer if (is_string(a) || is_string(b)) {
3385b81b6b3SRodney W. Grimes to_string(a);
3395b81b6b3SRodney W. Grimes to_string(b);
3406fbe7bf4SStefan Eßer r = strcoll(a->u.s, b->u.s);
3415b81b6b3SRodney W. Grimes } else {
342fa717604SStefan Eßer assert_to_integer(a);
343fa717604SStefan Eßer assert_to_integer(b);
3446fbe7bf4SStefan Eßer if (a->u.i > b->u.i)
3456fbe7bf4SStefan Eßer r = 1;
3466fbe7bf4SStefan Eßer else if (a->u.i < b->u.i)
3476fbe7bf4SStefan Eßer r = -1;
3486fbe7bf4SStefan Eßer else
3496fbe7bf4SStefan Eßer r = 0;
3505b81b6b3SRodney W. Grimes }
3515b81b6b3SRodney W. Grimes
3525b81b6b3SRodney W. Grimes free_value(a);
3535b81b6b3SRodney W. Grimes free_value(b);
3546fbe7bf4SStefan Eßer return (r);
3556fbe7bf4SStefan Eßer }
3566fbe7bf4SStefan Eßer
3576fbe7bf4SStefan Eßer struct val *
op_eq(struct val * a,struct val * b)3586fbe7bf4SStefan Eßer op_eq(struct val *a, struct val *b)
3596fbe7bf4SStefan Eßer {
3606fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) == 0)));
3615b81b6b3SRodney W. Grimes }
3625b81b6b3SRodney W. Grimes
3635b81b6b3SRodney W. Grimes struct val *
op_gt(struct val * a,struct val * b)3647669d0fcSWarner Losh op_gt(struct val *a, struct val *b)
3655b81b6b3SRodney W. Grimes {
3666fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) > 0)));
3675b81b6b3SRodney W. Grimes }
3685b81b6b3SRodney W. Grimes
3695b81b6b3SRodney W. Grimes struct val *
op_lt(struct val * a,struct val * b)3707669d0fcSWarner Losh op_lt(struct val *a, struct val *b)
3715b81b6b3SRodney W. Grimes {
3726fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) < 0)));
3735b81b6b3SRodney W. Grimes }
3745b81b6b3SRodney W. Grimes
3755b81b6b3SRodney W. Grimes struct val *
op_ge(struct val * a,struct val * b)3767669d0fcSWarner Losh op_ge(struct val *a, struct val *b)
3775b81b6b3SRodney W. Grimes {
3786fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) >= 0)));
3795b81b6b3SRodney W. Grimes }
3805b81b6b3SRodney W. Grimes
3815b81b6b3SRodney W. Grimes struct val *
op_le(struct val * a,struct val * b)3827669d0fcSWarner Losh op_le(struct val *a, struct val *b)
3835b81b6b3SRodney W. Grimes {
3846fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) <= 0)));
3855b81b6b3SRodney W. Grimes }
3865b81b6b3SRodney W. Grimes
3875b81b6b3SRodney W. Grimes struct val *
op_ne(struct val * a,struct val * b)3887669d0fcSWarner Losh op_ne(struct val *a, struct val *b)
3895b81b6b3SRodney W. Grimes {
3906fbe7bf4SStefan Eßer return (make_integer((intmax_t)(compare_vals(a, b) != 0)));
3915b81b6b3SRodney W. Grimes }
3925b81b6b3SRodney W. Grimes
3936fbe7bf4SStefan Eßer void
assert_plus(intmax_t a,intmax_t b,intmax_t r)3946fbe7bf4SStefan Eßer assert_plus(intmax_t a, intmax_t b, intmax_t r)
395915198b4SStefan Eßer {
3966fbe7bf4SStefan Eßer /*
3976fbe7bf4SStefan Eßer * sum of two positive numbers must be positive,
3986fbe7bf4SStefan Eßer * sum of two negative numbers must be negative
3996fbe7bf4SStefan Eßer */
4006fbe7bf4SStefan Eßer if ((a > 0 && b > 0 && r <= 0) ||
4016fbe7bf4SStefan Eßer (a < 0 && b < 0 && r >= 0))
4026fbe7bf4SStefan Eßer errx(ERR_EXIT, "overflow");
403915198b4SStefan Eßer }
404915198b4SStefan Eßer
4055b81b6b3SRodney W. Grimes struct val *
op_plus(struct val * a,struct val * b)4067669d0fcSWarner Losh op_plus(struct val *a, struct val *b)
4075b81b6b3SRodney W. Grimes {
4085b81b6b3SRodney W. Grimes struct val *r;
4095b81b6b3SRodney W. Grimes
410fa717604SStefan Eßer assert_to_integer(a);
411fa717604SStefan Eßer assert_to_integer(b);
4121393277eSGarrett Wollman r = make_integer(a->u.i + b->u.i);
4136fbe7bf4SStefan Eßer assert_plus(a->u.i, b->u.i, r->u.i);
4141393277eSGarrett Wollman
4155b81b6b3SRodney W. Grimes free_value(a);
4165b81b6b3SRodney W. Grimes free_value(b);
41769759f08SStefan Eßer return (r);
4185b81b6b3SRodney W. Grimes }
4195b81b6b3SRodney W. Grimes
4206fbe7bf4SStefan Eßer void
assert_minus(intmax_t a,intmax_t b,intmax_t r)4216fbe7bf4SStefan Eßer assert_minus(intmax_t a, intmax_t b, intmax_t r)
422915198b4SStefan Eßer {
423*41bd31e6SConrad Meyer if ((a >= 0 && b < 0 && r <= 0) ||
424*41bd31e6SConrad Meyer (a < 0 && b > 0 && r >= 0))
4256fbe7bf4SStefan Eßer errx(ERR_EXIT, "overflow");
426915198b4SStefan Eßer }
427915198b4SStefan Eßer
4285b81b6b3SRodney W. Grimes struct val *
op_minus(struct val * a,struct val * b)4297669d0fcSWarner Losh op_minus(struct val *a, struct val *b)
4305b81b6b3SRodney W. Grimes {
4315b81b6b3SRodney W. Grimes struct val *r;
4325b81b6b3SRodney W. Grimes
433fa717604SStefan Eßer assert_to_integer(a);
434fa717604SStefan Eßer assert_to_integer(b);
4351393277eSGarrett Wollman r = make_integer(a->u.i - b->u.i);
4366fbe7bf4SStefan Eßer assert_minus(a->u.i, b->u.i, r->u.i);
4371393277eSGarrett Wollman
4385b81b6b3SRodney W. Grimes free_value(a);
4395b81b6b3SRodney W. Grimes free_value(b);
44069759f08SStefan Eßer return (r);
4415b81b6b3SRodney W. Grimes }
4425b81b6b3SRodney W. Grimes
4438da97f00SStefan Eßer /*
4448da97f00SStefan Eßer * We depend on undefined behaviour giving a result (in r).
4458da97f00SStefan Eßer * To test this result, pass it as volatile. This prevents
4468da97f00SStefan Eßer * optimizing away of the test based on the undefined behaviour.
4478da97f00SStefan Eßer */
4486fbe7bf4SStefan Eßer void
assert_times(intmax_t a,intmax_t b,volatile intmax_t r)4498da97f00SStefan Eßer assert_times(intmax_t a, intmax_t b, volatile intmax_t r)
450915198b4SStefan Eßer {
4516fbe7bf4SStefan Eßer /*
4528da97f00SStefan Eßer * If the first operand is 0, no overflow is possible,
4538da97f00SStefan Eßer * else the result of the division test must match the
4548da97f00SStefan Eßer * second operand.
4558da97f00SStefan Eßer *
4568da97f00SStefan Eßer * Be careful to avoid overflow in the overflow test, as
4578da97f00SStefan Eßer * in assert_div(). Overflow in division would kill us
4588da97f00SStefan Eßer * with a SIGFPE before getting the test wrong. In old
4598da97f00SStefan Eßer * buggy versions, optimization used to give a null test
4608da97f00SStefan Eßer * instead of a SIGFPE.
4616fbe7bf4SStefan Eßer */
4628da97f00SStefan Eßer if ((a == -1 && b == INTMAX_MIN) || (a != 0 && r / a != b))
4636fbe7bf4SStefan Eßer errx(ERR_EXIT, "overflow");
464915198b4SStefan Eßer }
465915198b4SStefan Eßer
4665b81b6b3SRodney W. Grimes struct val *
op_times(struct val * a,struct val * b)4677669d0fcSWarner Losh op_times(struct val *a, struct val *b)
4685b81b6b3SRodney W. Grimes {
4695b81b6b3SRodney W. Grimes struct val *r;
4705b81b6b3SRodney W. Grimes
471fa717604SStefan Eßer assert_to_integer(a);
472fa717604SStefan Eßer assert_to_integer(b);
4731393277eSGarrett Wollman r = make_integer(a->u.i * b->u.i);
4746fbe7bf4SStefan Eßer assert_times(a->u.i, b->u.i, r->u.i);
4751393277eSGarrett Wollman
4765b81b6b3SRodney W. Grimes free_value(a);
4775b81b6b3SRodney W. Grimes free_value(b);
4785b81b6b3SRodney W. Grimes return (r);
4795b81b6b3SRodney W. Grimes }
4805b81b6b3SRodney W. Grimes
4816fbe7bf4SStefan Eßer void
assert_div(intmax_t a,intmax_t b)4826fbe7bf4SStefan Eßer assert_div(intmax_t a, intmax_t b)
483915198b4SStefan Eßer {
4846fbe7bf4SStefan Eßer if (b == 0)
4856fbe7bf4SStefan Eßer errx(ERR_EXIT, "division by zero");
486f07e4247SGarrett Wollman /* only INTMAX_MIN / -1 causes overflow */
487f07e4247SGarrett Wollman if (a == INTMAX_MIN && b == -1)
4886fbe7bf4SStefan Eßer errx(ERR_EXIT, "overflow");
489915198b4SStefan Eßer }
490915198b4SStefan Eßer
4915b81b6b3SRodney W. Grimes struct val *
op_div(struct val * a,struct val * b)4927669d0fcSWarner Losh op_div(struct val *a, struct val *b)
4935b81b6b3SRodney W. Grimes {
4945b81b6b3SRodney W. Grimes struct val *r;
4955b81b6b3SRodney W. Grimes
496fa717604SStefan Eßer assert_to_integer(a);
497fa717604SStefan Eßer assert_to_integer(b);
4986fbe7bf4SStefan Eßer /* assert based on operands only, not on result */
4996fbe7bf4SStefan Eßer assert_div(a->u.i, b->u.i);
500fa717604SStefan Eßer r = make_integer(a->u.i / b->u.i);
5011393277eSGarrett Wollman
5025b81b6b3SRodney W. Grimes free_value(a);
5035b81b6b3SRodney W. Grimes free_value(b);
50469759f08SStefan Eßer return (r);
5055b81b6b3SRodney W. Grimes }
5065b81b6b3SRodney W. Grimes
5075b81b6b3SRodney W. Grimes struct val *
op_rem(struct val * a,struct val * b)5087669d0fcSWarner Losh op_rem(struct val *a, struct val *b)
5095b81b6b3SRodney W. Grimes {
5105b81b6b3SRodney W. Grimes struct val *r;
5115b81b6b3SRodney W. Grimes
512fa717604SStefan Eßer assert_to_integer(a);
513fa717604SStefan Eßer assert_to_integer(b);
5146fbe7bf4SStefan Eßer /* pass a=1 to only check for div by zero */
5156fbe7bf4SStefan Eßer assert_div(1, b->u.i);
5161393277eSGarrett Wollman r = make_integer(a->u.i % b->u.i);
5171393277eSGarrett Wollman
5185b81b6b3SRodney W. Grimes free_value(a);
5195b81b6b3SRodney W. Grimes free_value(b);
52069759f08SStefan Eßer return (r);
5215b81b6b3SRodney W. Grimes }
5225b81b6b3SRodney W. Grimes
5235b81b6b3SRodney W. Grimes struct val *
op_colon(struct val * a,struct val * b)5247669d0fcSWarner Losh op_colon(struct val *a, struct val *b)
5255b81b6b3SRodney W. Grimes {
5264ba5f298SAndrew Moore regex_t rp;
5274cf61abaSJ.T. Conklin regmatch_t rm[2];
5284ba5f298SAndrew Moore char errbuf[256];
5294ba5f298SAndrew Moore int eval;
5304ba5f298SAndrew Moore struct val *v;
531c9fe00dcSJ.T. Conklin
532e1854a84SCeri Davies /* coerce both arguments to strings */
533c9fe00dcSJ.T. Conklin to_string(a);
534c9fe00dcSJ.T. Conklin to_string(b);
5355b81b6b3SRodney W. Grimes
5364ba5f298SAndrew Moore /* compile regular expression */
5374a13ab7cSJ.T. Conklin if ((eval = regcomp(&rp, b->u.s, 0)) != 0) {
5384ba5f298SAndrew Moore regerror(eval, &rp, errbuf, sizeof(errbuf));
539f07e4247SGarrett Wollman errx(ERR_EXIT, "%s", errbuf);
5405b81b6b3SRodney W. Grimes }
5414ba5f298SAndrew Moore
5424ba5f298SAndrew Moore /* compare string against pattern */
5434a13ab7cSJ.T. Conklin /* remember that patterns are anchored to the beginning of the line */
54469759f08SStefan Eßer if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0)
5454ba5f298SAndrew Moore if (rm[1].rm_so >= 0) {
54655c497bfSJ.T. Conklin *(a->u.s + rm[1].rm_eo) = '\0';
5474ba5f298SAndrew Moore v = make_str(a->u.s + rm[1].rm_so);
5484ba5f298SAndrew Moore
54969759f08SStefan Eßer } else
5508c3bbba8SEitan Adler v = make_integer((intmax_t)(rm[0].rm_eo));
55169759f08SStefan Eßer else
55269759f08SStefan Eßer if (rp.re_nsub == 0)
553f07e4247SGarrett Wollman v = make_integer((intmax_t)0);
55469759f08SStefan Eßer else
55555c497bfSJ.T. Conklin v = make_str("");
5564ba5f298SAndrew Moore
5574ba5f298SAndrew Moore /* free arguments and pattern buffer */
5584ba5f298SAndrew Moore free_value(a);
5594ba5f298SAndrew Moore free_value(b);
5604ba5f298SAndrew Moore regfree(&rp);
5614ba5f298SAndrew Moore
56269759f08SStefan Eßer return (v);
5634ba5f298SAndrew Moore }
564