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