/* expr -- evaluate expressions. This is the expr utility
Copyright (C) 1986-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Author: Mike Parker.
Modified for arbitrary-precision calculation by James Youngman.
This program evaluates expressions. Each token (operator, operand,
parenthesis) of the expression must be a separate argument. The
parser used is a reasonably general one, though any incarnation of
it is language-specific. It is especially nice for expressions.
No parse tree is needed; a new node is evaluated immediately.
One function can handle multiple operators all of equal precedence,
provided they all associate ((x op x) op x).
Define EVAL_TRACE to print an evaluation trace. */
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <sys/types.h> Provides system data types
#include "system.h" ...!includes auto-comment...
#include <regex.h> ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "long-options.h" ...!includes auto-comment...
#include "mbuiter.h" ...!includes auto-comment...
#include "strnumcmp.h" ...!includes auto-comment...
#include "xstrtol.h" ...!includes auto-comment...
/* Various parts of this code assume size_t fits into unsigned long
int, the widest unsigned type that GMP supports. */
verify (SIZE_MAX <= ULONG_MAX); Line 46
#ifndef HAVE_GMP Line 48
# define HAVE_GMP 0 Line 49
#endif Line 50
#if HAVE_GMP Line 52
# include <gmp.h> Line 53
#else Line 54
static void integer_overflow (char) ATTRIBUTE_NORETURN; Line 55
/* Approximate gmp.h well enough for expr.c's purposes. */
typedef intmax_t mpz_t[1]; Line 57
static void mpz_clear (mpz_t z) { (void) z; } Line 58Block 1
static void mpz_init_set_ui (mpz_t z, unsigned long int i) { z[0] = i; } Line 59Block 2
static int Line 60
mpz_init_set_str (mpz_t z, char *s, int base) Line 61
{
return xstrtoimax (s, NULL, base, z, NULL) == LONGINT_OK ? 0 : -1; Line 63
} Block 3
static void Line 65
mpz_add (mpz_t r, mpz_t a0, mpz_t b0) Line 66
{
intmax_t a = a0[0]; Line 68
intmax_t b = b0[0]; Line 69
intmax_t val = a + b; Line 70
if ((val < a) != (b < 0)) Line 71
integer_overflow ('+'); Line 72
r[0] = val; Line 73
} Block 4
static void Line 75
mpz_sub (mpz_t r, mpz_t a0, mpz_t b0) Line 76
{
intmax_t a = a0[0]; Line 78
intmax_t b = b0[0]; Line 79
intmax_t val = a - b; Line 80
if ((a < val) != (b < 0)) Line 81
integer_overflow ('-'); Line 82
r[0] = val; Line 83
} Block 5
static void Line 85
mpz_mul (mpz_t r, mpz_t a0, mpz_t b0) Line 86
{
intmax_t a = a0[0]; Line 88
intmax_t b = b0[0]; Line 89
intmax_t val = a * b; Line 90
if (! (a == 0 || b == 0 Line 91
|| ((val < 0) == ((a < 0) ^ (b < 0)) && val / a == b))) Line 92
integer_overflow ('*'); Line 93
r[0] = val; Line 94
} Block 6
static void Line 96
mpz_tdiv_q (mpz_t r, mpz_t a0, mpz_t b0) Line 97
{
intmax_t a = a0[0]; Line 99
intmax_t b = b0[0]; Line 100
/* Some x86-style hosts raise an exception for INT_MIN / -1. */
if (a < - INTMAX_MAX && b == -1) Line 103
integer_overflow ('/'); Line 104
r[0] = a / b; Line 105
} Block 7
static void Line 107
mpz_tdiv_r (mpz_t r, mpz_t a0, mpz_t b0) Line 108
{
intmax_t a = a0[0]; Line 110
intmax_t b = b0[0]; Line 111
/* Some x86-style hosts raise an exception for INT_MIN % -1. */
r[0] = a < - INTMAX_MAX && b == -1 ? 0 : a % b; Line 114
} Block 8
static char * _GL_ATTRIBUTE_MALLOC Line 116
mpz_get_str (char const *str, int base, mpz_t z) Line 117
{
(void) str; (void) base; Line 119
char buf[INT_BUFSIZE_BOUND (intmax_t)]; Line 120
return xstrdup (imaxtostr (z[0], buf)); Line 121
} Block 9
static int Line 123
mpz_sgn (mpz_t z) Line 124
{
return z[0] < 0 ? -1 : 0 < z[0]; Line 126
} Block 10
static int Line 128
mpz_fits_ulong_p (mpz_t z) Line 129
{
return 0 <= z[0] && z[0] <= ULONG_MAX; Line 131
} Block 11
static unsigned long int Line 133
mpz_get_ui (mpz_t z) Line 134
{
return z[0]; Line 136
} Block 12
static int Line 138
mpz_out_str (FILE *stream, int base, mpz_t z) Line 139
{
(void) base; Line 141
char buf[INT_BUFSIZE_BOUND (intmax_t)]; Line 142
return fputs (imaxtostr (z[0], buf), stream) != EOF; Line 143
} Block 13
#endif Line 145
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "expr" Line 148
#define AUTHORS \ Line 150
proper_name ("Mike Parker"), \ Line 151
proper_name ("James Youngman"), \ Line 152
proper_name ("Paul Eggert") Line 153
/* Exit statuses. */
enum Line 156
{
/* Invalid expression: e.g., its form does not conform to the
grammar for expressions. Our grammar is an extension of the
POSIX grammar. */
EXPR_INVALID = 2, Line 161
/* An internal error occurred, e.g., arithmetic overflow, storage
exhaustion. */
EXPR_FAILURE Line 165
};
/* The kinds of value we can have. */
enum valtype Line 169
{
integer, Line 171
string Line 172
}; Block 15
typedef enum valtype TYPE; Line 174
/* A value is.... */
struct valinfo Line 177
{
TYPE type; /* Which kind. */ Line 179
union Line 180
{ /* The value itself. */ Line 181
mpz_t i; Line 182
char *s; Line 183
} u; Line 184
}; Block 16
typedef struct valinfo VALUE; Line 186
/* The arguments given to the program, minus the program name. */
static char **args; Line 189
static VALUE *eval (bool); Line 191
static bool nomoreargs (void); Line 192
static bool null (VALUE *v); Line 193
static void printv (VALUE *v); Line 194
/*
Find the first occurrence in the character string STRING of any character
in the character string ACCEPT.
Copied from gnulib's mbscspn, with two differences:
1. Returns 1-based position of first found character, or zero if not found.
2. Returned value is the logical character index, NOT byte offset.
Examples:
mbs_logical_cspn ('hello','a') => 0
mbs_logical_cspn ('hello','h') => 1
mbs_logical_cspn ('hello','oe') => 1
mbs_logical_cspn ('hello','lo') => 3
In UTF-8 \xCE\xB1 is a single character (greek alpha):
mbs_logical_cspn ('\xCE\xB1bc','\xCE\xB1') => 1
mbs_logical_cspn ('\xCE\xB1bc','c') => 3 */
static size_t Line 214
mbs_logical_cspn (const char *s, const char *accept) Line 215
{
size_t idx = 0; Line 217
if (accept[0] == '\0') Line 219
return 0; Line 220
/* General case. */
if (MB_CUR_MAX > 1) Line 223
{
mbui_iterator_t iter; Line 225
for (mbui_init (iter, s); mbui_avail (iter); mbui_advance (iter)) Line 227
{
++idx; Line 229
if (mb_len (mbui_cur (iter)) == 1) Line 230
{
if (mbschr (accept, *mbui_cur_ptr (iter))) Line 232
return idx; Line 233
}
else Line 235
{
mbui_iterator_t aiter; Line 237
for (mbui_init (aiter, accept); Line 239
mbui_avail (aiter); Line 240
mbui_advance (aiter)) Line 241
if (mb_equal (mbui_cur (aiter), mbui_cur (iter))) Line 242
return idx; Line 243
}
}
/* not found */
return 0; Line 248
}
else Line 250
{
/* single-byte locale,
convert returned byte offset to 1-based index or zero if not found. */
size_t i = strcspn (s, accept); Line 254
return (s[i] ? i + 1 : 0); Line 255
}
} Block 17
/* Extract the substring of S, from logical character
position POS and LEN characters.
first character position is 1.
POS and LEN refer to logical characters, not octets.
Upon exit, sets v->s to the new string.
The new string might be empty if POS/LEN are invalid. */
static char * Line 266
mbs_logical_substr (const char *s, size_t pos, size_t len) Line 267
{
char *v, *vlim; Line 269
size_t blen = strlen (s); /* byte length */ Line 271
size_t llen = (MB_CUR_MAX > 1) ? mbslen (s) : blen; /* logical length */ Line 272
if (llen < pos || pos == 0 || len == 0 || len == SIZE_MAX) Line 274
return xstrdup (""); Line 275
/* characters to copy */
size_t vlen = MIN (len, llen - pos + 1); Line 278
if (MB_CUR_MAX == 1) Line 280
{
/* Single-byte case */
v = xmalloc (vlen + 1); Line 283
vlim = mempcpy (v, s + pos - 1, vlen); Line 284
}
else Line 286
{
/* Multibyte case */
/* FIXME: this is wasteful. Some memory can be saved by counting
how many bytes the matching characters occupy. */
vlim = v = xmalloc (blen + 1); Line 292
mbui_iterator_t iter; Line 294
size_t idx=1; Line 295
for (mbui_init (iter, s); Line 296
mbui_avail (iter) && vlen > 0; Line 297
mbui_advance (iter), ++idx) Line 298
{
/* Skip until we reach the starting position */
if (idx < pos) Line 301
continue; Line 302
/* Copy one character */
--vlen; Line 305
vlim = mempcpy (vlim, mbui_cur_ptr (iter), mb_len (mbui_cur (iter))); Line 306
}
}
*vlim = '\0'; Line 309
return v; Line 310
} Block 18
/* Return the number of logical characteres (possibly multibyte)
that are in string S in the first OFS octets.
Example in UTF-8:
"\xE2\x9D\xA7" is "U+2767 ROTATED FLORAL HEART BULLET".
In the string below, there are only two characters
up to the first 4 bytes (The U+2767 which occupies 3 bytes and 'x'):
mbs_count_to_offset ("\xE2\x9D\xA7xyz", 4) => 2 */
static size_t Line 321
mbs_offset_to_chars (const char *s, size_t ofs) Line 322
{
mbui_iterator_t iter; Line 324
size_t c = 0; Line 325
for (mbui_init (iter, s); mbui_avail (iter); mbui_advance (iter)) Line 326
{
ptrdiff_t d = mbui_cur_ptr (iter) - s; Line 328
if (d >= ofs) Line 329
break; Line 330
++c; Line 331
}
return c; Line 333
} Block 19
void Line 338
usage (int status) Line 339
{
if (status != EXIT_SUCCESS) Line 341
emit_try_help (); ...!common auto-comment...
else Line 343
{
printf (_("\ Line 345
Usage: %s EXPRESSION\n\ Line 346
or: %s OPTION\n\ Line 347
"), Line 348
program_name, program_name); Line 349
putchar ('\n'); Line 350
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 351
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 352
fputs (_("\ Line 353
\n\
Print the value of EXPRESSION to standard output. A blank line below\n\ Line 355
separates increasing precedence groups. EXPRESSION may be:\n\ Line 356
\n\
ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n\ Line 358
\n\
ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n\ Line 360
"), stdout); Line 361
fputs (_("\ Line 362
\n\
ARG1 < ARG2 ARG1 is less than ARG2\n\ Line 364
ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n\ Line 365
ARG1 = ARG2 ARG1 is equal to ARG2\n\ Line 366
ARG1 != ARG2 ARG1 is unequal to ARG2\n\ Line 367
ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n\ Line 368
ARG1 > ARG2 ARG1 is greater than ARG2\n\ Line 369
"), stdout); Line 370
fputs (_("\ Line 371
\n\
ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\ Line 373
ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\ Line 374
"), stdout); Line 375
/* Tell xgettext that the "% A" below is not a printf-style
format string: xgettext:no-c-format */
fputs (_("\ Line 378
\n\
ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\ Line 380
ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n\ Line 381
ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n\ Line 382
"), stdout); Line 383
fputs (_("\ Line 384
\n\
STRING : REGEXP anchored pattern match of REGEXP in STRING\n\ Line 386
\n\
match STRING REGEXP same as STRING : REGEXP\n\ Line 388
substr STRING POS LENGTH substring of STRING, POS counted from 1\n\ Line 389
index STRING CHARS index in STRING where any CHARS is found, or 0\n\ Line 390
length STRING length of STRING\n\ Line 391
"), stdout); Line 392
fputs (_("\ Line 393
+ TOKEN interpret TOKEN as a string, even if it is a\n\ Line 394
keyword like 'match' or an operator like '/'\n\ Line 395
\n\
( EXPRESSION ) value of EXPRESSION\n\ Line 397
"), stdout); Line 398
fputs (_("\ Line 399
\n\
Beware that many operators need to be escaped or quoted for shells.\n\ Line 401
Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\ Line 402
Pattern matches return the string matched between \\( and \\) or null; if\n\ Line 403
\\( and \\) are not used, they return the number of characters matched or 0.\n\ Line 404
"), stdout); Line 405
fputs (_("\ Line 406
\n\
Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\Line 408
or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\ Line 409
"), stdout); Line 410
emit_ancillary_info (PROGRAM_NAME); Line 411
}
exit (status); Line 413
} Block 20
#if ! HAVE_GMP Line 417
/* Report an integer overflow for operation OP and exit. */
static void Line 419
integer_overflow (char op) Line 420
{
die (EXPR_FAILURE, ERANGE, "%c", op); Line 422
} Block 21
#endif Line 424
int
main (int argc, char **argv) Line 427
{
VALUE *v; Line 429
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
initialize_exit_failure (EXPR_FAILURE); Line 437
atexit (close_stdout); Close stdout on exit (see gnulib)
parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION, ...!common auto-comment...
usage, AUTHORS, (char const *) NULL); Line 441
/* The above handles --help and --version.
Since there is no other invocation of getopt, handle '--' here. */
unsigned int u_argc = argc; Line 445
if (1 < u_argc && STREQ (argv[1], "--")) Line 446
{
--u_argc; Line 448
++argv; Line 449
}
if (u_argc <= 1) Line 452
{
error (0, 0, _("missing operand")); Line 454
usage (EXPR_INVALID); Line 455
}
args = argv + 1; Line 458
v = eval (true); Line 460
if (!nomoreargs ()) Line 461
die (EXPR_INVALID, 0, _("syntax error: unexpected argument %s"), Line 462
quotearg_n_style (0, locale_quoting_style, *args)); Line 463
printv (v); Line 465
return null (v); Line 467
} Block 22
/* Return a VALUE for I. */
static VALUE * Line 472
int_value (unsigned long int i) Line 473
{
VALUE *v = xmalloc (sizeof *v); Line 475
v->type = integer; Line 476
mpz_init_set_ui (v->u.i, i); Line 477
return v; Line 478
} Block 23
/* Return a VALUE for S. */
static VALUE * Line 483
str_value (char const *s) Line 484
{
VALUE *v = xmalloc (sizeof *v); Line 486
v->type = string; Line 487
v->u.s = xstrdup (s); Line 488
return v; Line 489
} Block 24
/* Free VALUE V, including structure components. */
static void Line 494
freev (VALUE *v) Line 495
{
if (v->type == string) Line 497
free (v->u.s); Line 498
else Line 499
mpz_clear (v->u.i); Line 500
free (v); Line 501
} Block 25
/* Print VALUE V. */
static void Line 506
printv (VALUE *v) Line 507
{
switch (v->type) Line 509
{
case integer: Line 511
mpz_out_str (stdout, 10, v->u.i); Line 512
putchar ('\n'); Line 513
break; Line 514
case string: Line 515
puts (v->u.s); Line 516
break; Line 517
default: Line 518
abort (); ...!common auto-comment...
}
} Block 26
/* Return true if V is a null-string or zero-number. */
static bool _GL_ATTRIBUTE_PURE Line 525
null (VALUE *v) Line 526
{
switch (v->type) Line 528
{
case integer: Line 530
return mpz_sgn (v->u.i) == 0; Line 531
case string: Line 532
{
char const *cp = v->u.s; Line 534
if (*cp == '\0') Line 535
return true; Line 536
cp += (*cp == '-'); Line 538
do
{
if (*cp != '0') Line 542
return false; Line 543
}
while (*++cp); Line 545
return true; Line 547
}
default: Line 549
abort (); ...!common auto-comment...
}
} Block 27
/* Return true if CP takes the form of an integer. */
static bool _GL_ATTRIBUTE_PURE Line 556
looks_like_integer (char const *cp) Line 557
{
cp += (*cp == '-'); Line 559
do
if (! ISDIGIT (*cp)) Line 562
return false; Line 563
while (*++cp); Line 564
return true; Line 566
} Block 28
/* Coerce V to a string value (can't fail). */
static void Line 571
tostring (VALUE *v) Line 572
{
switch (v->type) Line 574
{
case integer: Line 576
{
char *s = mpz_get_str (NULL, 10, v->u.i); Line 578
mpz_clear (v->u.i); Line 579
v->u.s = s; Line 580
v->type = string; Line 581
}
break; Line 583
case string: Line 584
break; Line 585
default: Line 586
abort (); ...!common auto-comment...
}
} Block 29
/* Coerce V to an integer value. Return true on success, false on failure. */
static bool Line 593
toarith (VALUE *v) Line 594
{
switch (v->type) Line 596
{
case integer: Line 598
return true; Line 599
case string: Line 600
{
char *s = v->u.s; Line 602
if (! looks_like_integer (s)) Line 604
return false; Line 605
if (mpz_init_set_str (v->u.i, s, 10) != 0 && !HAVE_GMP) Line 606
die (EXPR_FAILURE, ERANGE, "%s", (s)); Line 607
free (s); Line 608
v->type = integer; Line 609
return true; Line 610
}
default: Line 612
abort (); ...!common auto-comment...
}
} Block 30
/* Extract a size_t value from an integer value I.
If the value is negative, return SIZE_MAX.
If the value is too large, return SIZE_MAX - 1. */
static size_t Line 620
getsize (mpz_t i) Line 621
{
if (mpz_sgn (i) < 0) Line 623
return SIZE_MAX; Line 624
if (mpz_fits_ulong_p (i)) Line 625
{
unsigned long int ul = mpz_get_ui (i); Line 627
if (ul < SIZE_MAX) Line 628
return ul; Line 629
}
return SIZE_MAX - 1; Line 631
} Block 31
/* Return true and advance if the next token matches STR exactly.
STR must not be NULL. */
static bool Line 637
nextarg (char const *str) Line 638
{
if (*args == NULL) Line 640
return false; Line 641
else Line 642
{
bool r = STREQ (*args, str); Line 644
args += r; Line 645
return r; Line 646
}
} Block 32
/* Return true if there no more tokens. */
static bool Line 652
nomoreargs (void) Line 653
{
return *args == 0; Line 655
} Block 33
/* Report missing operand.
There is an implicit assumption that there was a previous argument,
and (args-1) is valid. */
static void Line 661
require_more_args (void) Line 662
{
if (nomoreargs ()) Line 664
die (EXPR_INVALID, 0, _("syntax error: missing argument after %s"), Line 665
quotearg_n_style (0, locale_quoting_style, *(args-1))); Line 666
} Block 34
#ifdef EVAL_TRACE Line 670
/* Print evaluation trace and args remaining. */
static void Line 673
trace (fxn) Line 674
char *fxn; Line 675
{
char **a; Line 677
printf ("%s:", fxn); Line 679
for (a = args; *a; a++) Line 680
printf (" %s", *a); Line 681
putchar ('\n'); Line 682
} Block 35
#endif Line 684
/* Do the : operator.
SV is the VALUE for the lhs (the string),
PV is the VALUE for the rhs (the pattern). */
static VALUE * Line 690
docolon (VALUE *sv, VALUE *pv) Line 691
{
VALUE *v IF_LINT ( = NULL); Line 693
const char *errmsg; Line 694
struct re_pattern_buffer re_buffer; Line 695
char fastmap[UCHAR_MAX + 1]; Line 696
struct re_registers re_regs; Line 697
regoff_t matchlen; Line 698
tostring (sv); Line 700
tostring (pv); Line 701
re_regs.num_regs = 0; Line 703
re_regs.start = NULL; Line 704
re_regs.end = NULL; Line 705
re_buffer.buffer = NULL; Line 707
re_buffer.allocated = 0; Line 708
re_buffer.fastmap = fastmap; Line 709
re_buffer.translate = NULL; Line 710
re_syntax_options = Line 711
RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES; Line 712
errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer); Line 713
if (errmsg) Line 714
die (EXPR_INVALID, 0, "%s", (errmsg)); Line 715
re_buffer.newline_anchor = 0; Line 716
matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); Line 718
if (0 <= matchlen) Line 719
{
/* Were \(...\) used? */
if (re_buffer.re_nsub > 0) Line 722
{
sv->u.s[re_regs.end[1]] = '\0'; Line 724
v = str_value (sv->u.s + re_regs.start[1]); Line 725
}
else Line 727
{
/* In multibyte locales, convert the matched offset (=number of bytes)
to the number of matched characters. */
size_t i = (MB_CUR_MAX == 1 Line 731
? matchlen Line 732
: mbs_offset_to_chars (sv->u.s, matchlen)); Line 733
v = int_value (i); Line 734
}
}
else if (matchlen == -1) Line 737
{
/* Match failed -- return the right kind of null. */
if (re_buffer.re_nsub > 0) Line 740
v = str_value (""); Line 741
else Line 742
v = int_value (0); Line 743
}
else Line 745
die (EXPR_FAILURE, Line 746
(matchlen == -2 ? errno : EOVERFLOW), Line 747
_("error in regular expression matcher")); Line 748
if (0 < re_regs.num_regs) Line 750
{
free (re_regs.start); Line 752
free (re_regs.end); Line 753
}
re_buffer.fastmap = NULL; Line 755
regfree (&re_buffer); Line 756
return v; Line 757
} Block 36
/* Handle bare operands and ( expr ) syntax. */
static VALUE * Line 762
eval7 (bool evaluate) Line 763
{
VALUE *v; Line 765
#ifdef EVAL_TRACE Line 767
trace ("eval7"); Line 768
#endif Line 769
require_more_args (); Line 770
if (nextarg ("(")) Line 772
{
v = eval (evaluate); Line 774
if (nomoreargs ()) Line 775
die (EXPR_INVALID, 0, _("syntax error: expecting ')' after %s"), Line 776
quotearg_n_style (0, locale_quoting_style, *(args-1))); Line 777
if (!nextarg (")")) Line 778
die (EXPR_INVALID, 0, _("syntax error: expecting ')' instead of %s"), Line 779
quotearg_n_style (0, locale_quoting_style, *args)); Line 780
return v; Line 781
}
if (nextarg (")")) Line 784
die (EXPR_INVALID, 0, _("syntax error: unexpected ')'")); Line 785
return str_value (*args++); Line 787
} Block 37
/* Handle match, substr, index, and length keywords, and quoting "+". */
static VALUE * Line 792
eval6 (bool evaluate) Line 793
{
VALUE *l; Line 795
VALUE *r; Line 796
VALUE *v; Line 797
VALUE *i1; Line 798
VALUE *i2; Line 799
#ifdef EVAL_TRACE Line 801
trace ("eval6"); Line 802
#endif Line 803
if (nextarg ("+")) Line 804
{
require_more_args (); Line 806
return str_value (*args++); Line 807
}
else if (nextarg ("length")) Line 809
{
r = eval6 (evaluate); Line 811
tostring (r); Line 812
v = int_value (mbslen (r->u.s)); Line 813
freev (r); Line 814
return v; Line 815
}
else if (nextarg ("match")) Line 817
{
l = eval6 (evaluate); Line 819
r = eval6 (evaluate); Line 820
if (evaluate) Line 821
{
v = docolon (l, r); Line 823
freev (l); Line 824
}
else Line 826
v = l; Line 827
freev (r); Line 828
return v; Line 829
}
else if (nextarg ("index")) Line 831
{
size_t pos; Line 833
l = eval6 (evaluate); Line 835
r = eval6 (evaluate); Line 836
tostring (l); Line 837
tostring (r); Line 838
pos = mbs_logical_cspn (l->u.s, r->u.s); Line 839
v = int_value (pos); Line 840
freev (l); Line 841
freev (r); Line 842
return v; Line 843
}
else if (nextarg ("substr")) Line 845
{
l = eval6 (evaluate); Line 847
i1 = eval6 (evaluate); Line 848
i2 = eval6 (evaluate); Line 849
tostring (l); Line 850
if (!toarith (i1) || !toarith (i2)) Line 852
v = str_value (""); Line 853
else Line 854
{
size_t pos = getsize (i1->u.i); Line 856
size_t len = getsize (i2->u.i); Line 857
char *s = mbs_logical_substr (l->u.s, pos, len); Line 859
v = str_value (s); Line 860
free (s); Line 861
}
freev (l); Line 863
freev (i1); Line 864
freev (i2); Line 865
return v; Line 866
}
else Line 868
return eval7 (evaluate); Line 869
} Block 38
/* Handle : operator (pattern matching).
Calls docolon to do the real work. */
static VALUE * Line 875
eval5 (bool evaluate) Line 876
{
VALUE *l; Line 878
VALUE *r; Line 879
VALUE *v; Line 880
#ifdef EVAL_TRACE Line 882
trace ("eval5"); Line 883
#endif Line 884
l = eval6 (evaluate); Line 885
while (1) Line 886
{
if (nextarg (":")) Line 888
{
r = eval6 (evaluate); Line 890
if (evaluate) Line 891
{
v = docolon (l, r); Line 893
freev (l); Line 894
l = v; Line 895
}
freev (r); Line 897
}
else Line 899
return l; Line 900
}
} Block 39
/* Handle *, /, % operators. */
static VALUE * Line 906
eval4 (bool evaluate) Line 907
{
VALUE *l; Line 909
VALUE *r; Line 910
enum { multiply, divide, mod } fxn; Line 911
#ifdef EVAL_TRACE Line 913
trace ("eval4"); Line 914
#endif Line 915
l = eval5 (evaluate); Line 916
while (1) Line 917
{
if (nextarg ("*")) Line 919
fxn = multiply; Line 920
else if (nextarg ("/")) Line 921
fxn = divide; Line 922
else if (nextarg ("%")) Line 923
fxn = mod; Line 924
else Line 925
return l; Line 926
r = eval5 (evaluate); Line 927
if (evaluate) Line 928
{
if (!toarith (l) || !toarith (r)) Line 930
die (EXPR_INVALID, 0, _("non-integer argument")); Line 931
if (fxn != multiply && mpz_sgn (r->u.i) == 0) Line 932
die (EXPR_INVALID, 0, _("division by zero")); Line 933
((fxn == multiply ? mpz_mul Line 934
: fxn == divide ? mpz_tdiv_q Line 935
: mpz_tdiv_r) Line 936
(l->u.i, l->u.i, r->u.i)); Line 937
}
freev (r); Line 939
}
} Block 40
/* Handle +, - operators. */
static VALUE * Line 945
eval3 (bool evaluate) Line 946
{
VALUE *l; Line 948
VALUE *r; Line 949
enum { plus, minus } fxn; Line 950
#ifdef EVAL_TRACE Line 952
trace ("eval3"); Line 953
#endif Line 954
l = eval4 (evaluate); Line 955
while (1) Line 956
{
if (nextarg ("+")) Line 958
fxn = plus; Line 959
else if (nextarg ("-")) Line 960
fxn = minus; Line 961
else Line 962
return l; Line 963
r = eval4 (evaluate); Line 964
if (evaluate) Line 965
{
if (!toarith (l) || !toarith (r)) Line 967
die (EXPR_INVALID, 0, _("non-integer argument")); Line 968
(fxn == plus ? mpz_add : mpz_sub) (l->u.i, l->u.i, r->u.i); Line 969
}
freev (r); Line 971
}
} Block 41
/* Handle comparisons. */
static VALUE * Line 977
eval2 (bool evaluate) Line 978
{
VALUE *l; Line 980
#ifdef EVAL_TRACE Line 982
trace ("eval2"); Line 983
#endif Line 984
l = eval3 (evaluate); Line 985
while (1) Line 986
{
VALUE *r; Line 988
enum Line 989
{
less_than, less_equal, equal, not_equal, greater_equal, greater_than Line 991
} fxn; Line 992
bool val = false; Line 993
if (nextarg ("<")) Line 995
fxn = less_than; Line 996
else if (nextarg ("<=")) Line 997
fxn = less_equal; Line 998
else if (nextarg ("=") || nextarg ("==")) Line 999
fxn = equal; Line 1000
else if (nextarg ("!=")) Line 1001
fxn = not_equal; Line 1002
else if (nextarg (">=")) Line 1003
fxn = greater_equal; Line 1004
else if (nextarg (">")) Line 1005
fxn = greater_than; Line 1006
else Line 1007
return l; Line 1008
r = eval3 (evaluate); Line 1009
if (evaluate) Line 1011
{
int cmp; Line 1013
tostring (l); Line 1014
tostring (r); Line 1015
if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s)) Line 1017
cmp = strintcmp (l->u.s, r->u.s); Line 1018
else Line 1019
{
errno = 0; Line 1021
cmp = strcoll (l->u.s, r->u.s); Line 1022
if (errno) Line 1024
{
error (0, errno, _("string comparison failed")); Line 1026
error (0, 0, _("set LC_ALL='C' to work around the problem")); Line 1027
die (EXPR_INVALID, 0, Line 1028
_("the strings compared were %s and %s"), Line 1029
quotearg_n_style (0, locale_quoting_style, l->u.s), Line 1030
quotearg_n_style (1, locale_quoting_style, r->u.s)); Line 1031
}
}
switch (fxn) Line 1035
{
case less_than: val = (cmp < 0); break; Line 1037
case less_equal: val = (cmp <= 0); break; Line 1038
case equal: val = (cmp == 0); break; Line 1039
case not_equal: val = (cmp != 0); break; Line 1040
case greater_equal: val = (cmp >= 0); break; Line 1041
case greater_than: val = (cmp > 0); break; Line 1042
default: abort (); ...!common auto-comment...
}
}
freev (l); Line 1047
freev (r); Line 1048
l = int_value (val); Line 1049
}
} Block 42
/* Handle &. */
static VALUE * Line 1055
eval1 (bool evaluate) Line 1056
{
VALUE *l; Line 1058
VALUE *r; Line 1059
#ifdef EVAL_TRACE Line 1061
trace ("eval1"); Line 1062
#endif Line 1063
l = eval2 (evaluate); Line 1064
while (1) Line 1065
{
if (nextarg ("&")) Line 1067
{
r = eval2 (evaluate && !null (l)); Line 1069
if (null (l) || null (r)) Line 1070
{
freev (l); Line 1072
freev (r); Line 1073
l = int_value (0); Line 1074
}
else Line 1076
freev (r); Line 1077
}
else Line 1079
return l; Line 1080
}
} Block 43
/* Handle |. */
static VALUE * Line 1086
eval (bool evaluate) Line 1087
{
VALUE *l; Line 1089
VALUE *r; Line 1090
#ifdef EVAL_TRACE Line 1092
trace ("eval"); Line 1093
#endif Line 1094
l = eval1 (evaluate); Line 1095
while (1) Line 1096
{
if (nextarg ("|")) Line 1098
{
r = eval1 (evaluate && null (l)); Line 1100
if (null (l)) Line 1101
{
freev (l); Line 1103
l = r; Line 1104
if (null (l)) Line 1105
{
freev (l); Line 1107
l = int_value (0); Line 1108
}
}
else Line 1111
freev (r); Line 1112
}
else Line 1114
return l; Line 1115
}
} Block 44