From 1eaf0e9870d0fa7e0891d5a9557fe3e6973e4b2a Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 18 Jul 2003 07:22:38 +0000 Subject: [PATCH] Include "exitfail.h", "quotearg.h". (EXPR_INVALID, EXPR_ERROR): New constants. (nomoreargs, null, toarith, nextarg): Return bool, not int. (syntax_error): New function, exiting with status 2. Use it insteading of printing "syntax error" ourselves. (main): Initialize exit_failure to EXPR_ERROR. Exit with EXPR_INVALID on syntax error (too few arguments). (nextarg): Use strcmp, not strcoll; strcoll might return an undesirable 0, or might fail. (docolon, eval4, eval3): Exit with status 3 on invalid argument type or other such error. (eval2): Report an error if strcoll fails in a string comparison. --- src/expr.c | 99 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/src/expr.c b/src/expr.c index bfc8dc545e..c7f81db3fa 100644 --- a/src/expr.c +++ b/src/expr.c @@ -37,7 +37,9 @@ #include "long-options.h" #include "error.h" #include "closeout.h" +#include "exitfail.h" #include "inttostr.h" +#include "quotearg.h" /* The official name of this program (e.g., no `g' prefix). */ #define PROGRAM_NAME "expr" @@ -48,6 +50,18 @@ #define NEW(Type) XMALLOC (Type, 1) #define OLD(x) free (x) +/* Exit statuses. */ +enum + { + /* Invalid expression: i.e., its form does not conform to the + grammar for expressions. Our grammar is an extension of the + POSIX grammar. */ + EXPR_INVALID = 2, + + /* Some other error occurred. */ + EXPR_ERROR + }; + /* The kinds of value we can have. */ enum valtype { @@ -75,8 +89,8 @@ static char **args; char *program_name; static VALUE *eval (void); -static int nomoreargs (void); -static int null (VALUE *v); +static bool nomoreargs (void); +static bool null (VALUE *v); static void printv (VALUE *v); void @@ -151,6 +165,13 @@ Pattern matches return the string matched between \\( and \\) or null; if\n\ exit (status); } +/* Report a syntax error and exit. */ +static void +syntax_error (void) +{ + error (EXPR_INVALID, 0, _("syntax error")); +} + int main (int argc, char **argv) { @@ -162,6 +183,9 @@ main (int argc, char **argv) bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); + /* Change the way library functions fail. */ + exit_failure = EXPR_ERROR; + atexit (close_stdout); parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION, @@ -177,14 +201,14 @@ main (int argc, char **argv) if (argc <= 1) { error (0, 0, _("too few arguments")); - usage (EXIT_FAILURE); + usage (EXPR_INVALID); } args = argv + 1; v = eval (); if (!nomoreargs ()) - error (2, 0, _("syntax error")); + syntax_error (); printv (v); exit (null (v)); @@ -249,9 +273,9 @@ printv (VALUE *v) puts (p); } -/* Return nonzero if V is a null-string or zero-number. */ +/* Return true if V is a null-string or zero-number. */ -static int +static bool null (VALUE *v) { switch (v->type) @@ -285,19 +309,19 @@ tostring (VALUE *v) } } -/* Coerce V to an integer value. Return 1 on success, 0 on failure. */ +/* Coerce V to an integer value. Return true on success, false on failure. */ -static int +static bool toarith (VALUE *v) { intmax_t i; - int neg; + bool neg; char *cp; switch (v->type) { case integer: - return 1; + return true; case string: i = 0; cp = v->u.s; @@ -310,14 +334,14 @@ toarith (VALUE *v) if (ISDIGIT (*cp)) i = i * 10 + *cp - '0'; else - return 0; + return false; } while (*++cp); free (v->u.s); v->u.i = i * (neg ? -1 : 1); v->type = integer; - return 1; + return true; default: abort (); } @@ -326,22 +350,22 @@ toarith (VALUE *v) /* Return nonzero and advance if the next token matches STR exactly. STR must not be NULL. */ -static int -nextarg (char *str) +static bool +nextarg (char const *str) { if (*args == NULL) return 0; else { - int r = strcoll (*args, str) == 0; + bool r = strcmp (*args, str) == 0; args += r; return r; } } -/* Return nonzero if there no more tokens. */ +/* Return true if there no more tokens. */ -static int +static bool nomoreargs (void) { return *args == 0; @@ -399,7 +423,7 @@ of the basic regular expression is not portable; it is being ignored"), re_syntax_options = RE_SYNTAX_POSIX_BASIC; errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); if (errmsg) - error (2, 0, "%s", errmsg); + error (EXPR_ERROR, 0, "%s", errmsg); matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); if (0 <= matchlen) @@ -436,18 +460,18 @@ eval7 (void) trace ("eval7"); #endif if (nomoreargs ()) - error (2, 0, _("syntax error")); + syntax_error (); if (nextarg ("(")) { v = eval (); if (!nextarg (")")) - error (2, 0, _("syntax error")); + syntax_error (); return v; } if (nextarg (")")) - error (2, 0, _("syntax error")); + syntax_error (); return str_value (*args++); } @@ -469,7 +493,7 @@ eval6 (void) if (nextarg ("+")) { if (nomoreargs ()) - error (2, 0, _("syntax error")); + syntax_error (); return str_value (*args++); } else if (nextarg ("length")) @@ -584,13 +608,13 @@ eval4 (void) return l; r = eval5 (); if (!toarith (l) || !toarith (r)) - error (2, 0, _("non-numeric argument")); + error (EXPR_ERROR, 0, _("non-numeric argument")); if (fxn == multiply) val = l->u.i * r->u.i; else { if (r->u.i == 0) - error (2, 0, _("division by zero")); + error (EXPR_ERROR, 0, _("division by zero")); val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i; } freev (l); @@ -623,7 +647,7 @@ eval3 (void) return l; r = eval4 (); if (!toarith (l) || !toarith (r)) - error (2, 0, _("non-numeric argument")); + error (EXPR_ERROR, 0, _("non-numeric argument")); val = fxn == plus ? l->u.i + r->u.i : l->u.i - r->u.i; freev (l); freev (r); @@ -642,9 +666,11 @@ eval2 (void) { less_than, less_equal, equal, not_equal, greater_equal, greater_than } fxn; - int val; + bool val; intmax_t lval; intmax_t rval; + int collation_errno; + char *collation_arg1; #ifdef EVAL_TRACE trace ("eval2"); @@ -669,13 +695,31 @@ eval2 (void) r = eval3 (); tostring (l); tostring (r); - lval = strcoll (l->u.s, r->u.s); + + /* Save the first arg to strcoll, in case we need its value for + a diagnostic later. This is needed because 'toarith' might + free the first arg. */ + collation_arg1 = xstrdup (l->u.s); + + errno = 0; + lval = strcoll (collation_arg1, r->u.s); + collation_errno = errno; rval = 0; if (toarith (l) && toarith (r)) { lval = l->u.i; rval = r->u.i; } + else if (collation_errno) + { + error (0, collation_errno, _("string comparison failed")); + error (0, 0, _("Set LC_ALL='C' to work around the problem.")); + error (EXPR_ERROR, 0, + _("The strings compared were %s and %s."), + quotearg_n_style (0, locale_quoting_style, collation_arg1), + quotearg_n_style (1, locale_quoting_style, r->u.s)); + } + switch (fxn) { case less_than: val = (lval < rval); break; @@ -688,6 +732,7 @@ eval2 (void) } freev (l); freev (r); + free (collation_arg1); l = int_value (val); } } -- 2.47.2