[PATCH #48] Fixed infinite loop and variation from documented behavior of

atodims()
This commit is contained in:
Jim Monte 2019-05-27 21:06:34 +02:00 committed by Holger Vogt
parent 65dcb0b4eb
commit 9a7459b8e0
2 changed files with 222 additions and 69 deletions

View File

@ -13,6 +13,14 @@ Author: 1992 David A. Gates, U. C. Berkeley CAD Group
#include "ngspice/stringskip.h"
static int atodims_bracketed(const char *p, int *data, int *p_n_dim);
static int atodims_unbracketed(const char *p, int *data, int *p_n_dim);
static int get_bracketed_dim(const char *p, int *p_val);
static int atodims_csv(const char *p, int *data, int *p_n_dim);
static int get_dim(const char *p, int *p_val);
/*
* Create a string of the form "12,1,10".
*
@ -120,94 +128,239 @@ incindex(int *counts, int numcounts, const int *dims, int numdims)
*
* Return 0 on success, 1 on failure.
*/
int
atodims(char *p, int *data, int *outlength)
int atodims(const char *p, int *data, int *p_n_dim)
{
int length = 0;
int state = 0;
int err = 0;
int needbracket = 0;
char sep = '\0';
if (!data || !outlength)
/* Validate arguments partially */
if (!data || !p_n_dim) {
return 1;
}
/* NULL string = no dimensions */
if (!p) {
*outlength = 0;
*p_n_dim = 0;
return 0;
}
/* Move to first "real" character */
p = skip_ws(p);
if (*p == '[') {
p = skip_ws(p + 1);
needbracket = 1;
}
/* Allowed first char is [ to start bracked string or a number */
return *p == '[' ? atodims_bracketed(p, data, p_n_dim) :
atodims_unbracketed(p, data, p_n_dim);
} /* end of function atodims */
while (*p && state != 3) {
switch (state) {
case 0: /* p just at or before a number */
if (length >= MAXDIMS) {
if (length == MAXDIMS)
printf("Error: maximum of %d dimensions allowed.\n",
MAXDIMS);
length += 1;
} else if (!isdigit_c(*p)) {
data[length++] = 0; /* This position was empty. */
} else {
data[length++] = atoi(p);
while (isdigit_c(*p))
p++;
/* This function processes a dimension string of the form
* [1,2,3,4] or [1][2][3][4]. Whitespace is allowed anywhere except
* at the beginning of the string or between the digits of a dimension.
*
* Return codes
* 0: OK
* +1: Error
*/
static int atodims_bracketed(const char *p, int *data, int *p_n_dim)
{
/* Process the first element, which is special because it determines
* if the string is of form [] or [1,2...] or [1][2]... */
p = skip_ws(++p); /* Step to number */
{
int rc;
/* Get the dimension value exiting with an error on failure */
if ((rc = get_dim(p, data)) <= 0) { /* no number or overflow */
if (rc < 0) { /* overflow */
return +1;
}
state = 1;
break;
case 1: /* p after a number, looking for ',' or ']' */
if (sep == '\0')
sep = *p;
if (*p == ']' && *p == sep) {
p++;
state = 2;
} else if (*p == ',' && *p == sep) {
p++;
state = 0;
} else { /* Funny character following a # */
break;
/* Handle special case of [] */
if (*p == ']') {
*p_n_dim = 0;
return 0;
}
break;
case 2: /* p after a ']', either at the end or looking for '[' */
if (*p == '[') {
p++;
state = 0;
} else {
state = 3;
}
break;
return +1; /* else an error */
}
p = skip_ws(p + rc); /* at comma or ] (or error) */
switch (*p) {
case ',': /* form [1,2,... */
*p_n_dim = 1;
rc = atodims_csv(++p, data, p_n_dim);
if (rc <= 1) { /* error or invalid termination */
return +1; /* Return error */
}
/* Else scan ended with ']', but did it end the string,
* whitespace excluded? */
p = skip_ws(p + rc);
return *p != '\0';
case ']': /* form [1][2]... */
++p; /* step past ']' */
break;
default: /* invalid char */
return +1;
}
}
/* Continue parsing form [1][2]... */
unsigned int n_dim = 1; /* already 1 dim from above */
for ( ; ; ) {
if (n_dim == MAXDIMS) { /* too many dimensions */
return +1;
}
int rc = get_bracketed_dim(p, data + n_dim);
if (rc <= 0) { /* error or normal exit */
*p_n_dim = n_dim;
return !!rc;
}
p += rc; /* step after the dimension that was processed */
++n_dim; /* one more dimension */
} /* end of loop getting dimensions */
} /* end of function atodims_bracketed */
/* This function processes a dimension string of the form
* 1,2,3,4. Whiltespace is allowed anywhere except
* at the beginning of the string or between the digits of a dimension.
*
* Return codes
* 0: OK
* +1: Error
*/
static int atodims_unbracketed(const char *p, int *data, int *p_n_dim)
{
*p_n_dim = 0; /* either "" so 0 or init for atodims_csv */
if (*p == '\0') { /* special case of "" */
return 0;
}
/* Scan comma-separated dimensions. Must end with '\0' (rc=0) */
return !!atodims_csv(p, data, p_n_dim);
} /* end of function atodims_unbracked */
/* This function processes dimension strings of the form
* 1,2,3,4 and 1,2,3,4]. Whiltespace is allowed anywhere except
* at the beginning of the string or between the digits of a dimension.
* On entry, *p_n_dim is the number of dimensions already added to data
* and p points to the first number to be processed.
*
* Return codes
* -1: Error
* 0: OK, scan ended by '\0'
* >0: OK, scan ended by ']', returned value = # chars processed
*/
static int atodims_csv(const char *p, int *data, int *p_n_dim)
{
const char *p0 = p;
unsigned int n_dim = (unsigned int) *p_n_dim;
for ( ; ; ) {
int val;
p = skip_ws(p);
int rc = get_dim(p, &val);
if (rc <= 0) { /* No number or overflow */
return -1;
}
/* Dimension was read */
if (n_dim >= MAXDIMS) { /* too many dimensions */
return -1;
}
data[n_dim++] = val; /* Add data for this dimension */
p = skip_ws(p + rc); /* step after the dimension that was processed */
/* Should normally be at comma, but there are special cases for
* end of regular list or bracketed list */
switch (*p) {
case ',': /* inter-dimension comma */
++p;
break;
case ']': /* ] ended scan */
*p_n_dim = n_dim;
return (int) (p - p0) + 1;
case '\0': /* end of string ended scan */
*p_n_dim = n_dim;
return 0;
default: /* invalid char */
return -1;
} /* end of switch over character ending scan */
} /* end of loop getting dimensions */
} /* end of function atodims_csv */
/* This function gets the dimension value in a string of the form
* [1] where spaces may appear anywhere except between the digits of the
* number.
*
* Return codes
* -1: Error
* 0: String ended before '['
* >0: Number of characters processed
*/
static int get_bracketed_dim(const char *p, int *p_val)
{
const char *p0 = p; /* save start */
p = skip_ws(p); /* move to opening bracket */
const char char_cur = *p;
if (char_cur == '\0') { /* end of string */
return 0;
}
if (char_cur != '[') { /* no bracket */
return -1;
}
p = skip_ws(++p); /* move to dimension */
int rc = get_dim(p, p_val); /* read the dimension */
if (rc <= 0) { /* error */
return -1;
}
*outlength = length;
if (length > MAXDIMS)
return (1);
if (state == 3) { /* We finished with a closing bracket */
err = !needbracket;
} else if (*p) { /* We finished by hitting a bad character after a # */
err = 1;
} else { /* We finished by exhausting the string */
err = needbracket;
p = skip_ws(p + rc); /* move to closing backet */
if (*p != ']') { /* no bracket */
return -1;
}
if (err)
*outlength = 0;
return (int) (p - p0) + 1;
} /* end of function get_bracketed_dim */
return (err);
}
/* This function reads the unsigned number at p as a dimension
*
* Return codes
* -1: overflow
* 0: *p is not a digit
* >0: Number of characters processed
*/
static int get_dim(const char *p, int *p_val)
{
unsigned int val = 0;
const char *p0 = p;
for ( ; ; ++p) {
const char c_cur = *p;
unsigned int digit_cur = c_cur - '0';
unsigned int val_new;
if (digit_cur > 9) { /* not a digit */
if ((*p_val = (int) val) < 0) { /* overflow */
return -1;
}
return (int) (p - p0);
} /* end of case of not a digit */
if ((val_new = 10 * val + digit_cur) < val) { /* overflow */
return -1;
}
val = val_new; /* update number */
} /* end of loop over digits */
} /* end of function get_dim */

View File

@ -9,7 +9,7 @@
void dimstring(const int *dim_data, int n_dim, char *retstring);
void indexstring(const int *dim_data, int n_dim, char *retstring);
int incindex(int *counts, int numcounts, const int *dims, int numdims);
int atodims(char *p, int *data, int *outlength);
int atodims(const char *p, int *data, int *outlength);
#ifdef COMPILE_UNUSED_FUNCTIONS
/* #ifdef COMPILE_UNUSED_FUNCTIONS added 2019-03-31 */