Improve the parser. Now we understand negative exponents; fixes PR 50768.

Also handle negative numbers better in general (don't randomly drop
the sign in a number of cases) and don't choke on exponents > 9.

This commit alters the meaning of a few previously valid but marginal
inputs (e.g. "3 foot-5 pound" is now treated as "3*-5 foot-pound"
rather than "3*5 foot-pound"; if you want the latter insert another
space) but corrects obviously wrong handling of many more.
This commit is contained in:
dholland 2016-02-05 03:30:08 +00:00
parent 3bf8089a9a
commit 0561f14b04
1 changed files with 64 additions and 12 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: units.c,v 1.25 2014/01/07 02:07:09 joerg Exp $ */
/* $NetBSD: units.c,v 1.26 2016/02/05 03:30:08 dholland Exp $ */
/*
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
@ -17,6 +17,7 @@
* improvements you might make to this program.
*/
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <float.h>
@ -344,14 +345,41 @@ addunit(struct unittype * theunit, const char *toadd, int flip)
char *scratch, *savescr;
char *item;
char *divider, *slash;
char *minus;
size_t pos, len;
int doingtop;
savescr = scratch = dupstr(toadd);
for (slash = scratch + 1; *slash; slash++)
if (*slash == '-' &&
(tolower((unsigned char)*(slash - 1)) != 'e' ||
!strchr(".0123456789", *(slash + 1))))
*slash = ' ';
/*
* "foot-pound" is the same as "foot pound". But don't
* trash minus signs on numbers.
*
* 20160204 dholland: this used to let through only minus
* signs at the beginning of the string or in the middle of a
* floating constant (e.g. 3.6e-5), and a minus sign at the
* beginning of the string failed further on. I have changed
* it so any minus sign before a digit (or decimal point) is
* treated as going with that digit.
*
* Note that this changed the interpretation of certain
* marginally valid inputs like "3 N-5 s"; that used to be
* interpreted as "3 N 5 s" or 15 N s, but now it reads as
* "3 N -5 s" or -15 N s. However, it also makes negative
* exponents on units work, which used to be silently trashed.
*/
for (minus = scratch + 1; *minus; minus++) {
if (*minus != '-') {
continue;
}
if (strchr(".0123456789", *(minus + 1))) {
continue;
}
*minus = ' ';
}
/* Process up to the next / in one go. */
slash = strchr(scratch, '/');
if (slash)
*slash = 0;
@ -359,7 +387,9 @@ addunit(struct unittype * theunit, const char *toadd, int flip)
do {
item = strtok(scratch, " *\t\n/");
while (item) {
if (strchr("0123456789.", *item)) {
if ((*item == '-' && strchr("0123456789.", *(item+1)))
|| strchr("0123456789.", *item)) {
/* item starts with a number */
char *endptr;
double num;
@ -415,14 +445,36 @@ addunit(struct unittype * theunit, const char *toadd, int flip)
}
else { /* item is not a number */
int repeat = 1;
int flipthis = 0;
if (strchr("23456789",
item[strlen(item) - 1])) {
repeat = item[strlen(item) - 1] - '0';
item[strlen(item) - 1] = 0;
pos = len = strlen(item);
assert(pos > 0);
while (strchr("0123456789", item[pos - 1])) {
pos--;
/* string began with non-digit */
assert(pos > 0);
}
if (pos < len) {
if (pos > 0 && item[pos - 1] == '-') {
/* allow negative exponents */
pos--;
}
/* have an exponent */
repeat = strtol(item + pos, NULL, 10);
item[pos] = 0;
if (repeat == 0) {
/* not really the right msg */
zeroerror();
return 1;
}
if (repeat < 0) {
flipthis = 1;
repeat = -repeat;
}
}
flipthis ^= doingtop ^ flip;
for (; repeat; repeat--)
if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
if (addsubunit(flipthis ? theunit->numerator : theunit->denominator, item))
return 1;
}
item = strtok(NULL, " *\t/\n");