diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/math.c | 120 | ||||
-rw-r--r-- | Src/prompt.c | 4 | ||||
-rw-r--r-- | Src/utils.c | 51 |
3 files changed, 120 insertions, 55 deletions
diff --git a/Src/math.c b/Src/math.c index eb3a768ec..ce316414e 100644 --- a/Src/math.c +++ b/Src/math.c @@ -186,6 +186,68 @@ static int type[TOKCOUNT] = /* 50 */ LR|OP_OPF, RL|OP_E2, LR|OP_OPF }; +static int +lexconstant(void) +{ +#ifdef USE_LOCALE + char *prev_locale; +#endif + char *nptr; + + nptr = ptr; + if (*nptr == '-') + nptr++; + + if (*nptr == '0') + { + nptr++; + if (*nptr == 'x' || *nptr == 'X') { + /* Let zstrtol parse number with base */ + yyval.u.l = zstrtol(ptr, &ptr, 0); + /* Should we set lastbase here? */ + lastbase = 16; + return NUM; + } + else if (isset(OCTALZEROES) && + (memchr(nptr, '.', strlen(nptr)) == NULL) && + idigit(*nptr)) { + yyval.u.l = zstrtol(ptr, &ptr, 0); + lastbase = 8; + return NUM; + } + } + + while (idigit(*nptr)) + nptr++; + + if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') { + /* it's a float */ + yyval.type = MN_FLOAT; +#ifdef USE_LOCALE + prev_locale = dupstring(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "POSIX"); +#endif + yyval.u.d = strtod(ptr, &nptr); +#ifdef USE_LOCALE + if (prev_locale) setlocale(LC_NUMERIC, prev_locale); +#endif + if (ptr == nptr || *nptr == '.') { + zerr("bad floating point constant", NULL, 0); + return EOI; + } + ptr = nptr; + } else { + /* it's an integer */ + yyval.u.l = zstrtol(ptr, &ptr, 10); + + if (*ptr == '#') { + ptr++; + yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l); + } + } + return NUM; +} + /**/ int outputradix; @@ -193,9 +255,6 @@ int outputradix; static int zzlex(void) { -#ifdef USE_LOCALE - char *prev_locale; -#endif int cct = 0; yyval.type = MN_INTEGER; @@ -220,7 +279,14 @@ zzlex(void) ptr++; return MINUSEQ; } - return (unary) ? UMINUS : MINUS; + if (unary) { + if (idigit(*ptr) || *ptr == '.') { + ptr--; + return lexconstant(); + } else + return UMINUS; + } else + return MINUS; case '(': return M_INPAR; case ')': @@ -376,52 +442,10 @@ zzlex(void) case '\t': case '\n': break; - case '0': - if (*ptr == 'x' || *ptr == 'X') { - ptr++; - /* Should we set lastbase here? */ - yyval.u.l = zstrtol(ptr, &ptr, lastbase = 16); - return NUM; - } - else if (isset(OCTALZEROES) && - (memchr(ptr, '.', strlen(ptr)) == NULL) && - idigit(*ptr)) { - yyval.u.l = zstrtol(ptr, &ptr, lastbase = 8); - return NUM; - } /* Fall through! */ default: - if (idigit(*--ptr) || *ptr == '.') { - char *nptr; - for (nptr = ptr; idigit(*nptr); nptr++); - - if (*nptr == '.' || *nptr == 'e' || *nptr == 'E') { - /* it's a float */ - yyval.type = MN_FLOAT; -#ifdef USE_LOCALE - prev_locale = dupstring(setlocale(LC_NUMERIC, NULL)); - setlocale(LC_NUMERIC, "POSIX"); -#endif - yyval.u.d = strtod(ptr, &nptr); -#ifdef USE_LOCALE - if (prev_locale) setlocale(LC_NUMERIC, prev_locale); -#endif - if (ptr == nptr || *nptr == '.') { - zerr("bad floating point constant", NULL, 0); - return EOI; - } - ptr = nptr; - } else { - /* it's an integer */ - yyval.u.l = zstrtol(ptr, &ptr, 10); - - if (*ptr == '#') { - ptr++; - yyval.u.l = zstrtol(ptr, &ptr, lastbase = yyval.u.l); - } - } - return NUM; - } + if (idigit(*--ptr) || *ptr == '.') + return lexconstant(); if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { int v; diff --git a/Src/prompt.c b/Src/prompt.c index b05bbf110..c0e73fb5f 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -163,13 +163,15 @@ promptexpand(char *s, int ns, char *rs, char *Rs) if (isset(PROMPTSUBST)) { int olderr = errflag; + int oldval = lastval; s = dupstring(s); if (!parsestr(s)) singsub(&s); - /* Ignore errors in prompt substitution */ + /* Ignore errors and status change in prompt substitution */ errflag = olderr; + lastval = oldval; } rstring = rs; diff --git a/Src/utils.c b/Src/utils.c index 678376eae..143855160 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1261,7 +1261,8 @@ skipparens(char inpar, char outpar, char **s) mod_export zlong zstrtol(const char *s, char **t, int base) { - zlong ret = 0; + const char *inp, *trunc = NULL; + zulong calc = 0, newcalc = 0; int neg; while (inblank(*s)) @@ -1280,16 +1281,54 @@ zstrtol(const char *s, char **t, int base) else base = 8; } + inp = s; if (base <= 10) - for (; *s >= '0' && *s < ('0' + base); s++) - ret = ret * base + *s - '0'; + for (; *s >= '0' && *s < ('0' + base); s++) { + if (trunc) + continue; + newcalc = calc * base + *s - '0'; + if (newcalc < calc) + { + trunc = s; + continue; + } + calc = newcalc; + } else for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) - || (*s >= 'A' && *s < ('A' + base - 10)); s++) - ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + || (*s >= 'A' && *s < ('A' + base - 10)); s++) { + if (trunc) + continue; + newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (newcalc < calc) + { + trunc = s; + continue; + } + calc = newcalc; + } + + /* + * Special case: check for a number that was just too long for + * signed notation. + * Extra special case: the lowest negative number would trigger + * the first test, but is actually representable correctly. + * This is a 1 in the top bit, all others zero, so test for + * that explicitly. + */ + if (!trunc && (zlong)calc < 0 && + (!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1)))) + { + trunc = s - 1; + calc /= base; + } + + if (trunc) + zwarn("number truncated after %d digits: %s", inp, trunc - inp); + if (t) *t = (char *)s; - return neg ? -ret : ret; + return neg ? -(zlong)calc : (zlong)calc; } /**/ |