On Tue, Mar 27, 2018 at 10:36:17PM +0200, Ingo Schwarze wrote: > > + int sig; > > Drop this variable, it does no good but only harm.
Yeah, too bad that I didn't notice this left-over from a previous development step. Strange enough that regression tests didn't choke on it... > > + /* skip leading zeros */ > > + while (*p == '0' && isdigit((unsigned char)p[1])) > > + p++; > > + > > + /* turn 0 always positive */ > > + if (*p == '0' && !isdigit((unsigned char)p[1])) > > + sig = 1; As tb@ pointed out, the !isdigit check can be removed as well. > > + c = strncmp(p1, p2, len1); > > That's another bug: > > schwarze@isnote $ /bin/test -1 -gt -2 ; echo $? > 1 > schwarze@isnote $ /bin/test 1 -gt 2 ; echo $? > 1 It's really great to have people who do extensive reviews. Thanks a lot for spotting this. I have added it to the regression tests. > With these changes, OK schwarze@. See below for the patch that > i tested. Modified with tb@'s remark. Also I changed the error message "bad number" to "invalid", so we stay fully in sync with strtonum's error messages. > Given that breaking test(1) might cause hard-to-find breakage > deep down in build systems and scripts, you might with a to > have a second OK for commit, though. Definitely, I'm not out to rush this one in. Index: bin/test/test.c =================================================================== RCS file: /cvs/src/bin/test/test.c,v retrieving revision 1.18 diff -u -p -u -p -r1.18 test.c --- bin/test/test.c 24 Jul 2017 22:15:52 -0000 1.18 +++ bin/test/test.c 27 Mar 2018 20:43:38 -0000 @@ -16,6 +16,7 @@ #include <unistd.h> #include <ctype.h> #include <errno.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -145,6 +146,8 @@ static int aexpr(enum token n); static int nexpr(enum token n); static int binop(void); static int primary(enum token n); +static const char *getnstr(const char *, int *, size_t *); +static int intcmp(const char *, const char *); static int filstat(char *nm, enum token mode); static int getn(const char *s); static int newerf(const char *, const char *); @@ -290,6 +293,70 @@ primary(enum token n) return strlen(*t_wp) > 0; } +static const char * +getnstr(const char *s, int *signum, size_t *len) +{ + const char *p, *start; + + /* skip leading whitespaces */ + p = s; + while (isspace((unsigned char)*p)) + p++; + + /* accept optional sign */ + if (*p == '-') { + *signum = -1; + p++; + } else { + *signum = 1; + if (*p == '+') + p++; + } + + /* skip leading zeros */ + while (*p == '0' && isdigit((unsigned char)p[1])) + p++; + + /* turn 0 always positive */ + if (*p == '0') + *signum = 1; + + start = p; + while (isdigit((unsigned char)*p)) + p++; + *len = p - start; + + /* allow trailing whitespaces */ + while (isspace((unsigned char)*p)) + p++; + + /* validate number */ + if (*p != '\0' || *start == '\0') + errx(2, "%s: invalid", s); + + return start; +} + +static int +intcmp(const char *opnd1, const char *opnd2) +{ + const char *p1, *p2; + size_t len1, len2; + int c, sig1, sig2; + + p1 = getnstr(opnd1, &sig1, &len1); + p2 = getnstr(opnd2, &sig2, &len2); + + if (sig1 != sig2) + c = sig1; + else if (len1 != len2) + c = (len1 < len2) ? -sig1 : sig1; + else + c = strncmp(p1, p2, len1) * sig1; + + return c; +} + static int binop(void) { @@ -313,17 +380,17 @@ binop(void) case STRGT: return strcmp(opnd1, opnd2) > 0; case INTEQ: - return getn(opnd1) == getn(opnd2); + return intcmp(opnd1, opnd2) == 0; case INTNE: - return getn(opnd1) != getn(opnd2); + return intcmp(opnd1, opnd2) != 0; case INTGE: - return getn(opnd1) >= getn(opnd2); + return intcmp(opnd1, opnd2) >= 0; case INTGT: - return getn(opnd1) > getn(opnd2); + return intcmp(opnd1, opnd2) > 0; case INTLE: - return getn(opnd1) <= getn(opnd2); + return intcmp(opnd1, opnd2) <= 0; case INTLT: - return getn(opnd1) < getn(opnd2); + return intcmp(opnd1, opnd2) < 0; case FILNT: return newerf(opnd1, opnd2); case FILOT: @@ -455,22 +522,26 @@ t_lex(char *s) static int getn(const char *s) { - char *p; - long r; - - errno = 0; - r = strtol(s, &p, 10); - - if (errno != 0) - errx(2, "%s: out of range", s); - - while (isspace((unsigned char)*p)) - p++; + char buf[32]; + const char *errstr, *p; + size_t len; + int r, sig; + + p = getnstr(s, &sig, &len); + if (sig != 1) + errstr = "too small"; + else if (len >= sizeof(buf)) + errstr = "too large"; + else { + strlcpy(buf, p, sizeof(buf)); + buf[len] = '\0'; + r = strtonum(buf, 0, INT_MAX, &errstr); + } - if (*p) - errx(2, "%s: bad number", s); + if (errstr != NULL) + errx(2, "%s: %s", s, errstr); - return (int) r; + return r; } static int Index: regress/bin/test/TEST.sh =================================================================== RCS file: /cvs/src/regress/bin/test/TEST.sh,v retrieving revision 1.2 diff -u -p -u -p -r1.2 TEST.sh --- regress/bin/test/TEST.sh 25 Nov 2014 23:09:22 -0000 1.2 +++ regress/bin/test/TEST.sh 27 Mar 2018 20:43:38 -0000 @@ -124,6 +124,17 @@ t 0 '0 -eq 0' t 1 '-5 -eq 5' t 0 '\( 0 -eq 0 \)' t 1 '1 -eq 0 -o a = a -a 1 -eq 0 -o a = aa' +t 0 '" +123 " -eq 123' +t 1 '"-123 " -gt " -1"' +t 0 '123 -gt -123' +t 0 '-0 -eq +0' +t 1 '+0 -gt 0' +t 0 '0 -eq 0' +t 0 '0000 -eq -0' +t 0 '-1 -gt -2' +t 1 '1 -gt 2' +t 1 '4294967296 -eq 0' +t 0 '12345678901234567890 -eq +0012345678901234567890' t 1 '"" -o ""' t 1 '"" -a ""'