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