1 /* expr -- evaluate expressions.
2    Copyright (C) 1986-2023 Free Software Foundation, Inc.
3 
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 3 of the License, or
7    (at your option) any later version.
8 
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.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Author: Mike Parker.
18    Modified for arbitrary-precision calculation by James Youngman.
19 
20    This program evaluates expressions.  Each token (operator, operand,
21    parenthesis) of the expression must be a separate 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.
24 
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).
28 
29    Define EVAL_TRACE to print an evaluation trace.  */
30 
31 #include <config.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include "system.h"
35 
36 #include <gmp.h>
37 #include <regex.h>
38 #include "long-options.h"
39 #include "mcel.h"
40 #include "strnumcmp.h"
41 #include "xstrtol.h"
42 
43 /* Various parts of this code assume size_t fits into unsigned long
44    int, the widest unsigned type that GMP supports.  */
45 static_assert (SIZE_MAX <= ULONG_MAX);
46 
47 /* The official name of this program (e.g., no 'g' prefix).  */
48 #define PROGRAM_NAME "expr"
49 
50 #define AUTHORS \
51   proper_name ("Mike Parker"), \
52   proper_name ("James Youngman"), \
53   proper_name ("Paul Eggert")
54 
55 /* Exit statuses.  */
56 enum
57   {
58     /* Invalid expression: e.g., its form does not conform to the
59        grammar for expressions.  Our grammar is an extension of the
60        POSIX grammar.  */
61     EXPR_INVALID = 2,
62 
63     /* An internal error occurred, e.g., arithmetic overflow, storage
64        exhaustion.  */
65     EXPR_FAILURE
66   };
67 
68 /* The kinds of value we can have.  */
69 enum valtype
70 {
71   integer,
72   string
73 };
74 typedef enum valtype TYPE;
75 
76 /* A value is.... */
77 struct valinfo
78 {
79   TYPE type;			/* Which kind. */
80   union
81   {				/* The value itself. */
82     mpz_t i;
83     char *s;
84   } u;
85 };
86 typedef struct valinfo VALUE;
87 
88 /* The arguments given to the program, minus the program name.  */
89 static char **args;
90 
91 static VALUE *eval (bool);
92 static bool nomoreargs (void);
93 static bool null (VALUE *v);
94 static void printv (VALUE *v);
95 
96 
97 /*
98    Find the first occurrence in the character string STRING of any character
99    in the character string ACCEPT.
100 
101    Copied from gnulib's mbscspn, with two differences:
102    1. Returns 1-based position of first found character, or zero if not found.
103    2. Returned value is the logical character index, NOT byte offset.
104 
105    Examples:
106      mbs_logical_cspn ('hello','a')  => 0
107      mbs_logical_cspn ('hello','h')  => 1
108      mbs_logical_cspn ('hello','oe') => 1
109      mbs_logical_cspn ('hello','lo') => 3
110 
111    In UTF-8 \xCE\xB1 is a single character (greek alpha):
112      mbs_logical_cspn ('\xCE\xB1bc','\xCE\xB1') => 1
113      mbs_logical_cspn ('\xCE\xB1bc','c') => 3 */
114 static size_t
mbs_logical_cspn(char const * s,char const * accept)115 mbs_logical_cspn (char const *s, char const *accept)
116 {
117   size_t idx = 0;
118 
119   if (accept[0] == '\0')
120     return 0;
121 
122   /* General case.  */
123   if (MB_CUR_MAX > 1)
124     {
125       for (char const *p = s; *p; )
126         {
127           ++idx;
128           mcel_t g = mcel_scanz (p);
129           if (g.len == 1)
130             {
131               if (mbschr (accept, *p))
132                 return idx;
133             }
134           else
135             for (char const *a = accept; *a; )
136               {
137                 mcel_t h = mcel_scanz (a);
138                 if (mcel_cmp (g, h) == 0)
139                   return idx;
140                 a += h.len;
141               }
142           p += g.len;
143         }
144     }
145   else
146     {
147       /* single-byte locale,
148          convert returned byte offset to 1-based index or zero if not found. */
149       size_t i = strcspn (s, accept);
150       if (s[i])
151         return i + 1;
152     }
153 
154   /* not found */
155   return 0;
156 }
157 
158 /* Extract the substring of S, from logical character
159    position POS and LEN characters.
160    first character position is 1.
161    POS and LEN refer to logical characters, not octets.
162 
163    Upon exit, sets v->s to the new string.
164    The new string might be empty if POS/LEN are invalid. */
165 static char *
mbs_logical_substr(char const * s,size_t pos,size_t len)166 mbs_logical_substr (char const *s, size_t pos, size_t len)
167 {
168   size_t mb_cur_max = MB_CUR_MAX;
169   idx_t llen = mb_cur_max <= 1 ? strlen (s) : mbslen (s); /* logical length */
170 
171   /* characters to copy */
172   size_t vlen = MIN (len, pos <= llen ? llen - pos + 1 : 0);
173 
174   char const *substart = s;
175   idx_t sublen = 0;
176   if (pos == 0 || len == SIZE_MAX)
177     {
178       /* The request is invalid.  Silently yield an empty string.  */
179     }
180   else if (mb_cur_max <= 1)
181     {
182       substart += pos - 1;
183       sublen = vlen;
184     }
185   else
186     for (idx_t idx = 1; *s && vlen; idx++)
187       {
188         idx_t char_bytes = mcel_scanz (s).len;
189 
190         /* Skip until we reach the starting position.  */
191         if (pos <= idx)
192           {
193             if (pos == idx)
194               substart = s;
195 
196             /* Add one character's length in bytes.  */
197             vlen--;
198             sublen += char_bytes;
199           }
200 
201         s += char_bytes;
202       }
203 
204   return ximemdup0 (substart, sublen);
205 }
206 
207 /* Return the number of logical characters (possibly multibyte)
208    that are in string S in the first OFS octets.
209 
210    Example in UTF-8:
211    "\xE2\x9D\xA7" is "U+2767 ROTATED FLORAL HEART BULLET".
212    In the string below, there are only two characters
213    up to the first 4 bytes (The U+2767 which occupies 3 bytes and 'x'):
214       mbs_count_to_offset ("\xE2\x9D\xA7xyz", 4) => 2  */
215 static size_t
mbs_offset_to_chars(char const * s,size_t ofs)216 mbs_offset_to_chars (char const *s, size_t ofs)
217 {
218   size_t c = 0;
219   for (size_t d = 0; d < ofs && s[d]; d += mcel_scanz (s + d).len)
220     c++;
221   return c;
222 }
223 
224 
225 
226 void
usage(int status)227 usage (int status)
228 {
229   if (status != EXIT_SUCCESS)
230     emit_try_help ();
231   else
232     {
233       printf (_("\
234 Usage: %s EXPRESSION\n\
235   or:  %s OPTION\n\
236 "),
237               program_name, program_name);
238       putchar ('\n');
239       fputs (HELP_OPTION_DESCRIPTION, stdout);
240       fputs (VERSION_OPTION_DESCRIPTION, stdout);
241       fputs (_("\
242 \n\
243 Print the value of EXPRESSION to standard output.  A blank line below\n\
244 separates increasing precedence groups.  EXPRESSION may be:\n\
245 \n\
246   ARG1 | ARG2       ARG1 if it is neither null nor 0, otherwise ARG2\n\
247 \n\
248   ARG1 & ARG2       ARG1 if neither argument is null or 0, otherwise 0\n\
249 "), stdout);
250       fputs (_("\
251 \n\
252   ARG1 < ARG2       ARG1 is less than ARG2\n\
253   ARG1 <= ARG2      ARG1 is less than or equal to ARG2\n\
254   ARG1 = ARG2       ARG1 is equal to ARG2\n\
255   ARG1 != ARG2      ARG1 is unequal to ARG2\n\
256   ARG1 >= ARG2      ARG1 is greater than or equal to ARG2\n\
257   ARG1 > ARG2       ARG1 is greater than ARG2\n\
258 "), stdout);
259       fputs (_("\
260 \n\
261   ARG1 + ARG2       arithmetic sum of ARG1 and ARG2\n\
262   ARG1 - ARG2       arithmetic difference of ARG1 and ARG2\n\
263 "), stdout);
264       /* Tell xgettext that the "% A" below is not a printf-style
265          format string:  xgettext:no-c-format */
266       fputs (_("\
267 \n\
268   ARG1 * ARG2       arithmetic product of ARG1 and ARG2\n\
269   ARG1 / ARG2       arithmetic quotient of ARG1 divided by ARG2\n\
270   ARG1 % ARG2       arithmetic remainder of ARG1 divided by ARG2\n\
271 "), stdout);
272       fputs (_("\
273 \n\
274   STRING : REGEXP   anchored pattern match of REGEXP in STRING\n\
275 \n\
276   match STRING REGEXP        same as STRING : REGEXP\n\
277   substr STRING POS LENGTH   substring of STRING, POS counted from 1\n\
278   index STRING CHARS         index in STRING where any CHARS is found, or 0\n\
279   length STRING              length of STRING\n\
280 "), stdout);
281       fputs (_("\
282   + TOKEN                    interpret TOKEN as a string, even if it is a\n\
283                                keyword like 'match' or an operator like '/'\n\
284 \n\
285   ( EXPRESSION )             value of EXPRESSION\n\
286 "), stdout);
287       fputs (_("\
288 \n\
289 Beware that many operators need to be escaped or quoted for shells.\n\
290 Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
291 Pattern matches return the string matched between \\( and \\) or null; if\n\
292 \\( and \\) are not used, they return the number of characters matched or 0.\n\
293 "), stdout);
294       fputs (_("\
295 \n\
296 Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
297 or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
298 "), stdout);
299       emit_ancillary_info (PROGRAM_NAME);
300     }
301   exit (status);
302 }
303 
304 
305 int
main(int argc,char ** argv)306 main (int argc, char **argv)
307 {
308   VALUE *v;
309 
310   initialize_main (&argc, &argv);
311   set_program_name (argv[0]);
312   setlocale (LC_ALL, "");
313   bindtextdomain (PACKAGE, LOCALEDIR);
314   textdomain (PACKAGE);
315 
316   initialize_exit_failure (EXPR_FAILURE);
317   atexit (close_stdout);
318 
319   parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
320                       usage, AUTHORS, (char const *) nullptr);
321 
322   /* The above handles --help and --version.
323      Since there is no other invocation of getopt, handle '--' here.  */
324   if (1 < argc && STREQ (argv[1], "--"))
325     {
326       --argc;
327       ++argv;
328     }
329 
330   if (argc <= 1)
331     {
332       error (0, 0, _("missing operand"));
333       usage (EXPR_INVALID);
334     }
335 
336   args = argv + 1;
337 
338   v = eval (true);
339   if (!nomoreargs ())
340     error (EXPR_INVALID, 0, _("syntax error: unexpected argument %s"),
341            quotearg_n_style (0, locale_quoting_style, *args));
342 
343   printv (v);
344 
345   main_exit (null (v));
346 }
347 
348 /* Return a VALUE for I.  */
349 
350 static VALUE *
int_value(unsigned long int i)351 int_value (unsigned long int i)
352 {
353   VALUE *v = xmalloc (sizeof *v);
354   v->type = integer;
355   mpz_init_set_ui (v->u.i, i);
356   return v;
357 }
358 
359 /* Return a VALUE for S.  */
360 
361 static VALUE *
str_value(char const * s)362 str_value (char const *s)
363 {
364   VALUE *v = xmalloc (sizeof *v);
365   v->type = string;
366   v->u.s = xstrdup (s);
367   return v;
368 }
369 
370 /* Free VALUE V, including structure components.  */
371 
372 static void
freev(VALUE * v)373 freev (VALUE *v)
374 {
375   if (v->type == string)
376     free (v->u.s);
377   else
378     mpz_clear (v->u.i);
379   free (v);
380 }
381 
382 /* Print VALUE V.  */
383 
384 static void
printv(VALUE * v)385 printv (VALUE *v)
386 {
387   switch (v->type)
388     {
389     case integer:
390       mpz_out_str (stdout, 10, v->u.i);
391       putchar ('\n');
392       break;
393     case string:
394       puts (v->u.s);
395       break;
396     default:
397       unreachable ();
398     }
399 }
400 
401 /* Return true if V is a null-string or zero-number.  */
402 
403 ATTRIBUTE_PURE
404 static bool
null(VALUE * v)405 null (VALUE *v)
406 {
407   switch (v->type)
408     {
409     case integer:
410       return mpz_sgn (v->u.i) == 0;
411     case string:
412       {
413         char const *cp = v->u.s;
414         if (*cp == '\0')
415           return true;
416 
417         cp += (*cp == '-');
418 
419         do
420           {
421             if (*cp != '0')
422               return false;
423           }
424         while (*++cp);
425 
426         return true;
427       }
428     default:
429       unreachable ();
430     }
431 }
432 
433 /* Return true if CP takes the form of an integer.  */
434 
435 ATTRIBUTE_PURE
436 static bool
looks_like_integer(char const * cp)437 looks_like_integer (char const *cp)
438 {
439   cp += (*cp == '-');
440 
441   do
442     if (! ISDIGIT (*cp))
443       return false;
444   while (*++cp);
445 
446   return true;
447 }
448 
449 /* Coerce V to a string value (can't fail).  */
450 
451 static void
tostring(VALUE * v)452 tostring (VALUE *v)
453 {
454   switch (v->type)
455     {
456     case integer:
457       {
458         char *s = mpz_get_str (nullptr, 10, v->u.i);
459         mpz_clear (v->u.i);
460         v->u.s = s;
461         v->type = string;
462       }
463       break;
464     case string:
465       break;
466     default:
467       unreachable ();
468     }
469 }
470 
471 /* Coerce V to an integer value.  Return true on success, false on failure.  */
472 
473 static bool
toarith(VALUE * v)474 toarith (VALUE *v)
475 {
476   switch (v->type)
477     {
478     case integer:
479       return true;
480     case string:
481       {
482         char *s = v->u.s;
483 
484         if (! looks_like_integer (s))
485           return false;
486         if (mpz_init_set_str (v->u.i, s, 10) != 0)
487           error (EXPR_FAILURE, ERANGE, "%s", (s));
488         free (s);
489         v->type = integer;
490         return true;
491       }
492     default:
493       unreachable ();
494     }
495 }
496 
497 /* Extract a size_t value from an integer value I.
498    If the value is negative, return SIZE_MAX.
499    If the value is too large, return SIZE_MAX - 1.  */
500 static size_t
getsize(mpz_t i)501 getsize (mpz_t i)
502 {
503   if (mpz_sgn (i) < 0)
504     return SIZE_MAX;
505   if (mpz_fits_ulong_p (i))
506     {
507       unsigned long int ul = mpz_get_ui (i);
508       if (ul < SIZE_MAX)
509         return ul;
510     }
511   return SIZE_MAX - 1;
512 }
513 
514 /* Return true and advance if the next token matches STR exactly.
515    STR must not be null.  */
516 
517 static bool
nextarg(char const * str)518 nextarg (char const *str)
519 {
520   if (*args == nullptr)
521     return false;
522   else
523     {
524       bool r = STREQ (*args, str);
525       args += r;
526       return r;
527     }
528 }
529 
530 /* Return true if there no more tokens.  */
531 
532 static bool
nomoreargs(void)533 nomoreargs (void)
534 {
535   return *args == 0;
536 }
537 
538 /* Report missing operand.
539    There is an implicit assumption that there was a previous argument,
540    and (args-1) is valid. */
541 static void
require_more_args(void)542 require_more_args (void)
543 {
544   if (nomoreargs ())
545     error (EXPR_INVALID, 0, _("syntax error: missing argument after %s"),
546            quotearg_n_style (0, locale_quoting_style, *(args - 1)));
547 }
548 
549 
550 #ifdef EVAL_TRACE
551 /* Print evaluation trace and args remaining.  */
552 
553 static void
trace(fxn)554 trace (fxn)
555      char *fxn;
556 {
557   char **a;
558 
559   printf ("%s:", fxn);
560   for (a = args; *a; a++)
561     printf (" %s", *a);
562   putchar ('\n');
563 }
564 #endif
565 
566 /* Do the : operator.
567    SV is the VALUE for the lhs (the string),
568    PV is the VALUE for the rhs (the pattern).  */
569 
570 static VALUE *
docolon(VALUE * sv,VALUE * pv)571 docolon (VALUE *sv, VALUE *pv)
572 {
573   VALUE *v;
574   char const *errmsg;
575   struct re_pattern_buffer re_buffer;
576   char fastmap[UCHAR_MAX + 1];
577   struct re_registers re_regs;
578   regoff_t matchlen;
579 
580   tostring (sv);
581   tostring (pv);
582 
583   re_regs.num_regs = 0;
584   re_regs.start = nullptr;
585   re_regs.end = nullptr;
586 
587   re_buffer.buffer = nullptr;
588   re_buffer.allocated = 0;
589   re_buffer.fastmap = fastmap;
590   re_buffer.translate = nullptr;
591   re_syntax_options =
592     RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
593   errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer);
594   if (errmsg)
595     error (EXPR_INVALID, 0, "%s", (errmsg));
596   re_buffer.newline_anchor = 0;
597 
598   matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
599   if (0 <= matchlen)
600     {
601       /* Were \(...\) used? */
602       if (re_buffer.re_nsub > 0)
603         {
604           if (re_regs.end[1] < 0)
605             v = str_value ("");
606           else
607             {
608               sv->u.s[re_regs.end[1]] = '\0';
609               v = str_value (sv->u.s + re_regs.start[1]);
610             }
611         }
612       else
613         {
614           /* In multibyte locales, convert the matched offset (=number of bytes)
615              to the number of matched characters. */
616           size_t i = (MB_CUR_MAX == 1
617                       ? matchlen
618                       : mbs_offset_to_chars (sv->u.s, matchlen));
619           v = int_value (i);
620         }
621     }
622   else if (matchlen == -1)
623     {
624       /* Match failed -- return the right kind of null.  */
625       if (re_buffer.re_nsub > 0)
626         v = str_value ("");
627       else
628         v = int_value (0);
629     }
630   else
631     error (EXPR_FAILURE,
632            matchlen == -2 ? errno : EOVERFLOW,
633            _("error in regular expression matcher"));
634 
635   if (0 < re_regs.num_regs)
636     {
637       free (re_regs.start);
638       free (re_regs.end);
639     }
640   re_buffer.fastmap = nullptr;
641   regfree (&re_buffer);
642   return v;
643 }
644 
645 /* Handle bare operands and ( expr ) syntax.  */
646 
647 static VALUE *
eval7(bool evaluate)648 eval7 (bool evaluate)
649 {
650   VALUE *v;
651 
652 #ifdef EVAL_TRACE
653   trace ("eval7");
654 #endif
655   require_more_args ();
656 
657   if (nextarg ("("))
658     {
659       v = eval (evaluate);
660       if (nomoreargs ())
661         error (EXPR_INVALID, 0, _("syntax error: expecting ')' after %s"),
662                quotearg_n_style (0, locale_quoting_style, *(args - 1)));
663       if (!nextarg (")"))
664         error (EXPR_INVALID, 0, _("syntax error: expecting ')' instead of %s"),
665                quotearg_n_style (0, locale_quoting_style, *args));
666       return v;
667     }
668 
669   if (nextarg (")"))
670     error (EXPR_INVALID, 0, _("syntax error: unexpected ')'"));
671 
672   return str_value (*args++);
673 }
674 
675 /* Handle match, substr, index, and length keywords, and quoting "+".  */
676 
677 static VALUE *
eval6(bool evaluate)678 eval6 (bool evaluate)
679 {
680   VALUE *l;
681   VALUE *r;
682   VALUE *v;
683   VALUE *i1;
684   VALUE *i2;
685 
686 #ifdef EVAL_TRACE
687   trace ("eval6");
688 #endif
689   if (nextarg ("+"))
690     {
691       require_more_args ();
692       return str_value (*args++);
693     }
694   else if (nextarg ("length"))
695     {
696       r = eval6 (evaluate);
697       tostring (r);
698       v = int_value (mbslen (r->u.s));
699       freev (r);
700       return v;
701     }
702   else if (nextarg ("match"))
703     {
704       l = eval6 (evaluate);
705       r = eval6 (evaluate);
706       if (evaluate)
707         {
708           v = docolon (l, r);
709           freev (l);
710         }
711       else
712         v = l;
713       freev (r);
714       return v;
715     }
716   else if (nextarg ("index"))
717     {
718       size_t pos;
719 
720       l = eval6 (evaluate);
721       r = eval6 (evaluate);
722       tostring (l);
723       tostring (r);
724       pos = mbs_logical_cspn (l->u.s, r->u.s);
725       v = int_value (pos);
726       freev (l);
727       freev (r);
728       return v;
729     }
730   else if (nextarg ("substr"))
731     {
732       l = eval6 (evaluate);
733       i1 = eval6 (evaluate);
734       i2 = eval6 (evaluate);
735       tostring (l);
736 
737       if (!toarith (i1) || !toarith (i2))
738         v = str_value ("");
739       else
740         {
741           size_t pos = getsize (i1->u.i);
742           size_t len = getsize (i2->u.i);
743 
744           char *s = mbs_logical_substr (l->u.s, pos, len);
745           v = str_value (s);
746           free (s);
747         }
748       freev (l);
749       freev (i1);
750       freev (i2);
751       return v;
752     }
753   else
754     return eval7 (evaluate);
755 }
756 
757 /* Handle : operator (pattern matching).
758    Calls docolon to do the real work.  */
759 
760 static VALUE *
eval5(bool evaluate)761 eval5 (bool evaluate)
762 {
763   VALUE *l;
764   VALUE *r;
765   VALUE *v;
766 
767 #ifdef EVAL_TRACE
768   trace ("eval5");
769 #endif
770   l = eval6 (evaluate);
771   while (true)
772     {
773       if (nextarg (":"))
774         {
775           r = eval6 (evaluate);
776           if (evaluate)
777             {
778               v = docolon (l, r);
779               freev (l);
780               l = v;
781             }
782           freev (r);
783         }
784       else
785         return l;
786     }
787 }
788 
789 /* Handle *, /, % operators.  */
790 
791 static VALUE *
eval4(bool evaluate)792 eval4 (bool evaluate)
793 {
794   VALUE *l;
795   VALUE *r;
796   enum { multiply, divide, mod } fxn;
797 
798 #ifdef EVAL_TRACE
799   trace ("eval4");
800 #endif
801   l = eval5 (evaluate);
802   while (true)
803     {
804       if (nextarg ("*"))
805         fxn = multiply;
806       else if (nextarg ("/"))
807         fxn = divide;
808       else if (nextarg ("%"))
809         fxn = mod;
810       else
811         return l;
812       r = eval5 (evaluate);
813       if (evaluate)
814         {
815           if (!toarith (l) || !toarith (r))
816             error (EXPR_INVALID, 0, _("non-integer argument"));
817           if (fxn != multiply && mpz_sgn (r->u.i) == 0)
818             error (EXPR_INVALID, 0, _("division by zero"));
819           ((fxn == multiply ? mpz_mul
820             : fxn == divide ? mpz_tdiv_q
821             : mpz_tdiv_r)
822            (l->u.i, l->u.i, r->u.i));
823         }
824       freev (r);
825     }
826 }
827 
828 /* Handle +, - operators.  */
829 
830 static VALUE *
eval3(bool evaluate)831 eval3 (bool evaluate)
832 {
833   VALUE *l;
834   VALUE *r;
835   enum { plus, minus } fxn;
836 
837 #ifdef EVAL_TRACE
838   trace ("eval3");
839 #endif
840   l = eval4 (evaluate);
841   while (true)
842     {
843       if (nextarg ("+"))
844         fxn = plus;
845       else if (nextarg ("-"))
846         fxn = minus;
847       else
848         return l;
849       r = eval4 (evaluate);
850       if (evaluate)
851         {
852           if (!toarith (l) || !toarith (r))
853             error (EXPR_INVALID, 0, _("non-integer argument"));
854           (fxn == plus ? mpz_add : mpz_sub) (l->u.i, l->u.i, r->u.i);
855         }
856       freev (r);
857     }
858 }
859 
860 /* Handle comparisons.  */
861 
862 static VALUE *
eval2(bool evaluate)863 eval2 (bool evaluate)
864 {
865   VALUE *l;
866 
867 #ifdef EVAL_TRACE
868   trace ("eval2");
869 #endif
870   l = eval3 (evaluate);
871   while (true)
872     {
873       VALUE *r;
874       enum
875         {
876           less_than, less_equal, equal, not_equal, greater_equal, greater_than
877         } fxn;
878       bool val = false;
879 
880       if (nextarg ("<"))
881         fxn = less_than;
882       else if (nextarg ("<="))
883         fxn = less_equal;
884       else if (nextarg ("=") || nextarg ("=="))
885         fxn = equal;
886       else if (nextarg ("!="))
887         fxn = not_equal;
888       else if (nextarg (">="))
889         fxn = greater_equal;
890       else if (nextarg (">"))
891         fxn = greater_than;
892       else
893         return l;
894       r = eval3 (evaluate);
895 
896       if (evaluate)
897         {
898           int cmp;
899           tostring (l);
900           tostring (r);
901 
902           if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
903             cmp = strintcmp (l->u.s, r->u.s);
904           else
905             {
906               errno = 0;
907               cmp = strcoll (l->u.s, r->u.s);
908 
909               if (errno)
910                 {
911                   error (0, errno, _("string comparison failed"));
912                   error (0, 0, _("set LC_ALL='C' to work around the problem"));
913                   error (EXPR_INVALID, 0,
914                          _("the strings compared were %s and %s"),
915                          quotearg_n_style (0, locale_quoting_style, l->u.s),
916                          quotearg_n_style (1, locale_quoting_style, r->u.s));
917                 }
918             }
919 
920           switch (fxn)
921             {
922             case less_than:     val = (cmp <  0); break;
923             case less_equal:    val = (cmp <= 0); break;
924             case equal:         val = (cmp == 0); break;
925             case not_equal:     val = (cmp != 0); break;
926             case greater_equal: val = (cmp >= 0); break;
927             case greater_than:  val = (cmp >  0); break;
928             default: unreachable ();
929             }
930         }
931 
932       freev (l);
933       freev (r);
934       l = int_value (val);
935     }
936 }
937 
938 /* Handle &.  */
939 
940 static VALUE *
eval1(bool evaluate)941 eval1 (bool evaluate)
942 {
943   VALUE *l;
944   VALUE *r;
945 
946 #ifdef EVAL_TRACE
947   trace ("eval1");
948 #endif
949   l = eval2 (evaluate);
950   while (true)
951     {
952       if (nextarg ("&"))
953         {
954           r = eval2 (evaluate && !null (l));
955           if (null (l) || null (r))
956             {
957               freev (l);
958               freev (r);
959               l = int_value (0);
960             }
961           else
962             freev (r);
963         }
964       else
965         return l;
966     }
967 }
968 
969 /* Handle |.  */
970 
971 static VALUE *
eval(bool evaluate)972 eval (bool evaluate)
973 {
974   VALUE *l;
975   VALUE *r;
976 
977 #ifdef EVAL_TRACE
978   trace ("eval");
979 #endif
980   l = eval1 (evaluate);
981   while (true)
982     {
983       if (nextarg ("|"))
984         {
985           r = eval1 (evaluate && null (l));
986           if (null (l))
987             {
988               freev (l);
989               l = r;
990               if (null (l))
991                 {
992                   freev (l);
993                   l = int_value (0);
994                 }
995             }
996           else
997             freev (r);
998         }
999       else
1000         return l;
1001     }
1002 }
1003