ngspice/src/misc/string.c

806 lines
18 KiB
C
Raw Normal View History

2000-04-27 22:03:57 +02:00
/**********
Copyright 1990 Regents of the University of California. All rights reserved.
**********/
/*
* String functions
*/
#include "ngspice/ngspice.h"
#include "ngspice/stringutil.h"
#include "ngspice/stringskip.h"
#include "ngspice/dstring.h"
2000-04-27 22:03:57 +02:00
2014-03-29 13:11:56 +01:00
#include <stdarg.h>
2000-04-27 22:03:57 +02:00
int
prefix(const char *p, const char *s)
2000-04-27 22:03:57 +02:00
{
while (*p && (*p == *s))
p++, s++;
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return *p == '\0';
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
/* Create a copy of a string. */
char *
2010-04-11 10:49:05 +02:00
copy(const char *str)
2000-04-27 22:03:57 +02:00
{
char *p;
2017-03-25 16:48:22 +01:00
2012-07-28 19:53:55 +02:00
if (!str)
return NULL;
if ((p = TMALLOC(char, strlen(str) + 1)) != NULL)
2017-03-25 16:48:22 +01:00
(void) strcpy(p, str);
2017-03-25 16:48:36 +01:00
return p;
2000-04-27 22:03:57 +02:00
}
2016-04-09 12:25:37 +02:00
/* copy a substring, from 'str' to 'end'
* including *str, excluding *end
*/
2010-04-11 10:49:05 +02:00
char *
copy_substring(const char *str, const char *end)
{
2010-11-06 21:14:21 +01:00
size_t n = (size_t) (end - str);
2010-04-11 10:49:05 +02:00
char *p;
if ((p = TMALLOC(char, n + 1)) != NULL) {
2010-04-11 10:49:05 +02:00
(void) strncpy(p, str, n);
p[n] = '\0';
}
2017-03-25 16:48:36 +01:00
return p;
2010-04-11 10:49:05 +02:00
}
2014-03-29 13:11:56 +01:00
2014-04-12 21:00:56 +02:00
char *
tvprintf(const char *fmt, va_list args)
2014-03-29 13:11:56 +01:00
{
char buf[1024];
char *p = buf;
int size = sizeof(buf);
for (;;) {
int nchars;
2014-04-12 21:00:56 +02:00
va_list ap;
2014-03-29 13:11:56 +01:00
2014-04-12 21:00:56 +02:00
va_copy(ap, args);
nchars = vsnprintf(p, (size_t) size, fmt, ap);
va_end(ap);
2014-03-29 13:11:56 +01:00
if (nchars == -1) { // compatibility to old implementations
size *= 2;
}
else if (nchars >= size) {
/* Output was truncated. Returned value is the number of chars
* that would have been written if the buffer were large enough
* excluding the terminiating null. */
size = nchars + 1; /* min required allocation size */
}
else { /* String formatted OK */
2014-03-29 13:11:56 +01:00
break;
}
/* Allocate a larger buffer */
2014-03-29 13:11:56 +01:00
if (p == buf)
p = TMALLOC(char, size);
else
p = TREALLOC(char, p, size);
}
/* Return the formatted string, making a copy on the heap if the
* stack's buffer (buf) contains the string */
2014-03-29 13:11:56 +01:00
return (p == buf) ? copy(p) : p;
} /* end of function tvprintf */
2014-03-29 13:11:56 +01:00
/* This function returns an allocation containing the string formatted
* according to fmt and the variadic argument list provided. It is a wrapper
* around tvprintf() which processes the argumens as a va_list. */
2014-04-12 21:00:56 +02:00
char *
tprintf(const char *fmt, ...)
{
char *rv;
va_list ap;
va_start(ap, fmt);
rv = tvprintf(fmt, ap);
va_end(ap);
return rv;
} /* end of function tprintf */
2014-04-12 21:00:56 +02:00
2000-04-27 22:03:57 +02:00
/* Determine whether sub is a substring of str. */
/* Like strstr( ) XXX */
int
substring(const char *sub, const char *str)
2000-04-27 22:03:57 +02:00
{
2017-03-24 23:23:42 +01:00
for (; *str; str++)
2000-04-27 22:03:57 +02:00
if (*str == *sub) {
2017-03-24 23:23:42 +01:00
const char *s = sub, *t = str;
for (; *s; s++, t++)
if (!*t || (*s != *t))
2000-04-27 22:03:57 +02:00
break;
if (*s == '\0')
2017-03-25 16:48:36 +01:00
return TRUE;
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return FALSE;
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
/* Append one character to a string. Don't check for overflow. */
/* Almost like strcat( ) XXX */
void
appendc(char *s, char c)
{
while (*s)
s++;
*s++ = c;
*s = '\0';
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
/* Try to identify an integer that begins a string. Stop when a non-
* numeric character is reached.
*/
/* Like atoi( ) XXX */
int
scannum(char *str)
{
int i = 0;
2017-03-25 16:48:22 +01:00
while (isdigit_c(*str))
2000-04-27 22:03:57 +02:00
i = i * 10 + *(str++) - '0';
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return i;
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
/* Case insensitive str eq. */
/* Like strcasecmp( ) XXX */
int
cieq(const char *p, const char *s)
2000-04-27 22:03:57 +02:00
{
2017-03-24 23:23:42 +01:00
for (; *p; p++, s++)
if (tolower_c(*p) != tolower_c(*s))
2017-03-25 16:48:36 +01:00
return FALSE;
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return *s == '\0';
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
/* Case insensitive prefix. */
int
ciprefix(const char *p, const char *s)
2000-04-27 22:03:57 +02:00
{
2017-03-24 23:23:42 +01:00
for (; *p; p++, s++)
if (tolower_c(*p) != tolower_c(*s))
2017-03-25 16:48:36 +01:00
return FALSE;
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return TRUE;
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
2000-04-27 22:03:57 +02:00
void
strtolower(char *str)
{
2017-03-24 23:23:42 +01:00
if (!str)
return;
for (; *str; str++)
*str = tolower_c(*str);
}
2017-03-25 16:48:22 +01:00
void
strtoupper(char *str)
{
2017-03-24 23:23:42 +01:00
if (!str)
return;
for (; *str; str++)
*str = toupper_c(*str);
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:48:22 +01:00
#ifdef CIDER
2017-03-25 16:48:22 +01:00
/*
* Imported from cider file support/strmatch.c
* Original copyright notice:
* Author: 1991 David A. Gates, U. C. Berkeley CAD Group
*
*/
2017-03-25 16:48:22 +01:00
/*
* Case-insensitive test of whether p is a prefix of s and at least the
* first n characters are the same
*/
2017-03-25 16:48:22 +01:00
int
2017-03-25 16:56:22 +01:00
cinprefix(char *p, char *s, int n)
{
2017-03-25 16:48:22 +01:00
if (!p || !s)
2017-03-25 16:48:36 +01:00
return 0;
2017-03-25 16:48:22 +01:00
2017-03-24 23:23:42 +01:00
for (; *p; p++, s++, n--)
if (tolower_c(*p) != tolower_c(*s))
2017-03-25 16:48:36 +01:00
return 0;
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return n <= 0;
2017-03-25 16:48:22 +01:00
}
/*
* Case-insensitive match of prefix string p against string s
* returns the number of matching characters
2017-03-25 16:48:22 +01:00
*
*/
2017-03-25 16:48:22 +01:00
int
2017-03-25 16:56:22 +01:00
cimatch(char *p, char *s)
{
2017-03-25 16:56:22 +01:00
int n = 0;
2017-03-25 16:48:22 +01:00
if (!p || !s)
2017-03-25 16:48:36 +01:00
return 0;
2017-03-25 16:48:22 +01:00
2017-03-24 23:23:42 +01:00
for (; *p; p++, s++, n++)
if (tolower_c(*p) != tolower_c(*s))
2017-03-25 16:48:36 +01:00
return n;
2017-03-25 16:48:22 +01:00
2017-03-25 16:48:36 +01:00
return n;
2017-03-25 16:48:22 +01:00
}
#endif /* CIDER */
/*-------------------------------------------------------------------------*
2017-03-25 16:48:22 +01:00
* gettok skips over whitespace and returns the next token found. This is
* the original version. It does not "do the right thing" when you have
* parens or commas anywhere in the nodelist. Note that I left this unmodified
* since I didn't want to break any fcns which called it from elsewhere than
* subckt.c. -- SDB 12.3.2003.
*-------------------------------------------------------------------------*/
2000-04-27 22:03:57 +02:00
char *
gettok(char **s)
{
char c;
int paren;
2017-03-25 16:50:19 +01:00
const char *token, *token_e;
2000-04-27 22:03:57 +02:00
paren = 0;
2017-03-25 16:48:22 +01:00
*s = skip_ws(*s);
2000-04-27 22:03:57 +02:00
if (!**s)
2017-03-25 16:48:36 +01:00
return NULL;
2017-03-25 16:48:22 +01:00
2017-03-25 16:50:19 +01:00
token = *s;
while ((c = **s) != '\0' && !isspace_c(c)) {
2017-03-25 16:48:22 +01:00
if (c == '(')
paren += 1;
else if (c == ')')
paren -= 1;
else if (c == ',' && paren < 1)
break;
(*s)++;
2000-04-27 22:03:57 +02:00
}
2017-03-25 16:50:19 +01:00
token_e = *s;
2017-03-25 16:48:22 +01:00
while (isspace_c(**s) || **s == ',')
2000-04-27 22:03:57 +02:00
(*s)++;
2017-03-25 16:48:22 +01:00
2017-03-25 16:50:19 +01:00
return copy_substring(token, token_e);
2000-04-27 22:03:57 +02:00
}
/*-------------------------------------------------------------------------*
2017-03-25 16:48:22 +01:00
* nexttok skips over whitespaces and the next token in s
* returns NULL if there is nothing left to skip.
* It replaces constructs like txfree(gettok(&actstring)) by
* actstring = nexttok(actstring). This is derived from the original gettok version.
* It does not "do the right thing" when
* you have parens or commas anywhere in the nodelist.
*-------------------------------------------------------------------------*/
char *
2016-11-01 11:30:08 +01:00
nexttok(const char *s)
{
if (!s)
return NULL;
int paren = 0;
s = skip_ws(s);
if (!*s)
return NULL;
2017-03-25 16:48:22 +01:00
for (; *s && !isspace_c(*s); s++)
if (*s == '(')
paren += 1;
else if (*s == ')')
paren -= 1;
else if (*s == ',' && paren < 1)
break;
while (isspace_c(*s) || *s == ',')
s++;
2016-11-01 11:30:08 +01:00
return (char *) s;
}
2010-07-10 13:27:57 +02:00
/*-------------------------------------------------------------------------*
2017-03-25 16:48:22 +01:00
* gettok skips over whitespaces or '=' and returns the next token found,
2010-07-10 13:27:57 +02:00
* if the token is something like i(xxx), v(yyy), or v(xxx,yyy)
* -- h_vogt 10.07.2010.
*-------------------------------------------------------------------------*/
2017-03-25 16:48:22 +01:00
2010-07-10 13:27:57 +02:00
char *
gettok_iv(char **s)
{
char *p_src = *s; /* location in source string */
char c; /* current char */
2010-07-10 13:27:57 +02:00
/* Step past whitespace and '=' */
while (isspace_c(c = *p_src) || (c == '=')) {
p_src++;
}
2017-03-25 16:48:22 +01:00
/* Test for valid leading character */
if (((c =*p_src) == '\0') ||
((c != 'v') && (c != 'i') && (c != 'V') && (c != 'I'))) {
*s = p_src; /* update position in string */
return (char *) NULL;
}
2017-03-25 16:48:22 +01:00
/* Allocate buffer for token being returned */
char * const token = TMALLOC(char, strlen(p_src) + 1);
char *p_dst = token; /* location in token */
2017-03-25 16:48:22 +01:00
// add v or i to buf
*p_dst++ = *p_src++;
{
int n_paren = 0;
/* Skip any space between v/V/i/I and '(' */
p_src = skip_ws(p_src);
while ((c = *p_src) != '\0') {
/* Keep track of nesting level */
if (c == '(') {
n_paren++;
}
else if (c == ')') {
n_paren--;
}
if (isspace_c(c)) { /* Do not copy whitespace to output */
p_src++;
}
else {
*p_dst++ = *p_src++;
if (n_paren == 0) {
break;
}
}
2010-07-10 13:27:57 +02:00
}
}
2017-03-25 16:48:22 +01:00
/* Step past whitespace and ',' */
while (isspace_c(c = *p_src) || (c == ',')) {
p_src++;
}
2017-03-25 16:48:22 +01:00
*s = p_src; /* update position in string */
2017-03-25 16:48:36 +01:00
return token;
} /* end of function gettok_iv */
2010-07-10 13:27:57 +02:00
/*-------------------------------------------------------------------------*
* gettok_noparens was added by SDB on 4.21.2003.
* It acts like gettok, except that it treats parens and commas like
2017-03-25 16:48:22 +01:00
* whitespace while looking for the POLY token. That is, it stops
* parsing and returns when it finds one of those chars. It is called from
* 'translate' (subckt.c).
*-------------------------------------------------------------------------*/
2017-03-25 16:48:22 +01:00
char *
gettok_noparens(char **s)
{
char c;
2017-03-25 16:50:19 +01:00
const char *token, *token_e;
*s = skip_ws(*s);
if (!**s)
2017-03-25 16:48:36 +01:00
return NULL; /* return NULL if we come to end of line */
2017-03-25 16:50:19 +01:00
token = *s;
2017-03-25 16:48:22 +01:00
while ((c = **s) != '\0' &&
!isspace_c(c) &&
(**s != '(') &&
(**s != ')') &&
(**s != ',')
) {
(*s)++;
}
2017-03-25 16:50:19 +01:00
token_e = *s;
*s = skip_ws(*s);
2017-03-25 16:50:19 +01:00
return copy_substring(token, token_e);
}
/*-------------------------------------------------------------------------*
* gettok_model acts like gettok_noparens, however when it encounters a '{',
* it searches for the corresponding '}' and adds the string to the output
* token.
*-------------------------------------------------------------------------*/
char *
gettok_model(char **s)
{
char c;
const char *token, *token_e;
*s = skip_ws(*s);
if (!**s)
return NULL; /* return NULL if we come to end of line */
token = *s;
while ((c = **s) != '\0' &&
!isspace_c(c) &&
(**s != '(') &&
(**s != ')') &&
(**s != ',')
) {
(*s)++;
if (**s == '{') {
char *tmpstr = gettok_char(s, '}', FALSE, TRUE);
tfree(tmpstr);
}
}
token_e = *s;
*s = skip_ws(*s);
return copy_substring(token, token_e);
}
2017-03-25 16:48:22 +01:00
2007-12-29 22:13:23 +01:00
char *
gettok_instance(char **s)
{
char c;
2017-03-25 16:50:19 +01:00
const char *token, *token_e;
2007-12-29 22:13:23 +01:00
*s = skip_ws(*s);
2007-12-29 22:13:23 +01:00
if (!**s)
2017-03-25 16:48:36 +01:00
return NULL; /* return NULL if we come to end of line */
2007-12-29 22:13:23 +01:00
2017-03-25 16:50:19 +01:00
token = *s;
2017-03-25 16:48:22 +01:00
while ((c = **s) != '\0' &&
!isspace_c(c) &&
(**s != '(') &&
(**s != ')')
) {
(*s)++;
2007-12-29 22:13:23 +01:00
}
2017-03-25 16:50:19 +01:00
token_e = *s;
2007-12-29 22:13:23 +01:00
/* Now iterate up to next non-whitespace char */
*s = skip_ws(*s);
2017-03-25 16:50:19 +01:00
return copy_substring(token, token_e);
}
2017-03-25 16:48:22 +01:00
/* get the next token starting at next non white spice, stopping
at p, if inc_p is true, then including p, else excluding p,
return NULL if p is not found.
If '}', ']' or ')' and nested is true, find corresponding p
*/
2017-03-25 16:48:22 +01:00
char *
gettok_char(char **s, char p, bool inc_p, bool nested)
{
char c;
2017-03-25 16:50:19 +01:00
const char *token, *token_e;
*s = skip_ws(*s);
if (!**s)
2017-03-25 16:48:36 +01:00
return NULL; /* return NULL if we come to end of line */
2017-03-25 16:50:19 +01:00
token = *s;
2017-03-25 16:48:22 +01:00
if (nested && ((p == '}') || (p == ')') || (p == ']'))) {
char q;
int count = 0;
/* find opening bracket */
2017-03-25 16:48:22 +01:00
if (p == '}')
2012-08-06 19:50:23 +02:00
q = '{';
2017-03-25 16:48:22 +01:00
else if (p == ']')
2012-08-06 19:50:23 +02:00
q = '[';
else
q = '(';
/* add string in front of q, excluding q */
2017-03-24 23:23:42 +01:00
while ((c = **s) != '\0' && (**s != q))
2017-03-25 16:48:22 +01:00
(*s)++;
/* return if nested bracket found, excluding its character */
2017-03-25 16:48:22 +01:00
while ((c = **s) != '\0') {
if (c == q)
count++;
else if (c == p)
count--;
2017-03-24 23:23:42 +01:00
if (count == 0)
break;
2017-03-25 16:48:22 +01:00
(*s)++;
}
}
else
/* just look for p and return string, excluding p */
2017-03-24 23:23:42 +01:00
while ((c = **s) != '\0' && (**s != p))
2017-03-25 16:48:22 +01:00
(*s)++;
if (c == '\0')
/* p not found */
2017-03-25 16:48:36 +01:00
return NULL;
if (inc_p)
/* add p */
2017-03-25 16:48:22 +01:00
(*s)++;
2017-03-25 16:50:19 +01:00
token_e = *s;
/* Now iterate up to next non-whitespace char */
*s = skip_ws(*s);
2007-12-29 22:13:23 +01:00
2017-03-25 16:50:19 +01:00
return copy_substring(token, token_e);
2007-12-29 22:13:23 +01:00
}
2017-03-25 16:48:22 +01:00
/*-------------------------------------------------------------------------*
* gettok_node was added by SDB on 12.3.2003
* It acts like gettok, except that it treats parens and commas like
* whitespace (i.e. it ignores them). Use it when parsing through netnames
* (node names) since they may be grouped using ( , ).
*-------------------------------------------------------------------------*/
2017-03-25 16:48:22 +01:00
char *
gettok_node(char **s)
{
char c;
2017-03-25 16:50:19 +01:00
const char *token, *token_e;
if (*s == NULL)
return NULL;
while (isspace_c(**s) ||
2017-03-25 16:48:22 +01:00
(**s == '(') ||
(**s == ')') ||
(**s == ',')
)
(*s)++; /* iterate over whitespace and ( , ) */
if (!**s)
2017-03-25 16:48:36 +01:00
return NULL; /* return NULL if we come to end of line */
2013-10-18 21:29:38 +02:00
token = *s;
2017-03-25 16:48:22 +01:00
while ((c = **s) != '\0' &&
!isspace_c(c) &&
(**s != '(') &&
(**s != ')') &&
(**s != ',')
2017-03-24 23:23:42 +01:00
) /* collect chars until whitespace or ( , ) */
2013-10-18 21:29:38 +02:00
(*s)++;
2017-03-25 16:50:19 +01:00
token_e = *s;
2013-10-18 21:29:38 +02:00
/* Now iterate up to next non-whitespace char */
while (isspace_c(**s) ||
2017-03-25 16:48:22 +01:00
(**s == '(') ||
(**s == ')') ||
(**s == ',')
)
(*s)++; /* iterate over whitespace and ( , ) */
2017-03-25 16:50:19 +01:00
return copy_substring(token, token_e);
}
2017-03-25 16:48:22 +01:00
/*-------------------------------------------------------------------------*
* get_l_paren iterates the pointer forward in a string until it hits
2017-03-25 16:48:22 +01:00
* the position after the next left paren "(". It returns 0 if it found a left
* paren, and 1 if no left paren is found. It is called from 'translate'
* (subckt.c).
*-------------------------------------------------------------------------*/
2017-03-25 16:48:22 +01:00
int
get_l_paren(char **s)
{
2017-03-25 16:48:22 +01:00
while (**s && (**s != '('))
(*s)++;
2017-03-25 16:48:22 +01:00
if (!**s)
2017-03-25 16:48:36 +01:00
return 1;
2017-03-25 16:48:22 +01:00
(*s)++;
2017-03-25 16:48:36 +01:00
return **s == '\0';
}
/*-------------------------------------------------------------------------*
* get_r_paren iterates the pointer forward in a string until it hits
2017-03-25 16:48:22 +01:00
* the position after the next right paren ")". It returns 0 if it found a right
* paren, and 1 if no right paren is found. It is called from 'translate'
* (subckt.c).
*-------------------------------------------------------------------------*/
2017-03-25 16:48:22 +01:00
int
get_r_paren(char **s)
{
2017-03-25 16:48:22 +01:00
while (**s && (**s != ')'))
(*s)++;
2017-03-25 16:48:22 +01:00
if (!**s)
2017-03-25 16:48:36 +01:00
return 1;
(*s)++;
2017-03-25 16:48:36 +01:00
return **s == '\0';
}
/*-------------------------------------------------------------------------*
* this function strips all white space inside parens
* is needed in gettoks (dotcards.c) for correct processing of expressions
* like " .plot v( 5 , 4 ) v( 6 )" -> .plot v(5,4) v(6)"
*-------------------------------------------------------------------------*/
char *
stripWhiteSpacesInsideParens(const char *str)
{
str = skip_ws(str); /* Skip leading whitespace */
const size_t n_char_str = strlen(str);
/* Allocate buffer for string being built */
char * const str_out = TMALLOC(char, n_char_str + 1);
char *p_dst = str_out; /* location in str_out */
char ch; /* current char */
/* Process input string until its end */
for ( ; ; ) {
/* Add char. If at end of input string, return the string
* that was built */
if ((*p_dst++ = (ch = *str++)) == '\0') {
return str_out;
}
2017-03-25 16:48:22 +01:00
/* If the char is a ')' add all non-whitespace until ')' or,
* if the string is malformed, until '\0' */
if (ch == '(') {
for ( ; ; ) {
/* If at end of input string, the closing ') was missing.
* The caller will need to resolve this issue. */
if ((ch = *str++) == '\0') {
*p_dst = '\0';
return str_out;
}
if (isspace((int) ch)) { /* skip whitespace */
continue;
}
/* Not whitespace, so add next character */
*p_dst++ = ch;
/* If the char that was added was ')', done */
if (ch == ')') {
break;
}
} /* end of loop processing () */
} /* end of case of '(' found */
} /* end of loop over chars in input string */
} /* end of function stripWhiteSpacesInsideParens */
2000-04-27 22:03:57 +02:00
bool
2017-03-25 16:48:22 +01:00
isquote(char ch)
{
2017-03-25 16:48:36 +01:00
return ch == '\'' || ch == '"';
}
2017-03-25 16:48:22 +01:00
bool
2017-03-25 16:48:22 +01:00
is_arith_char(char c)
{
2017-03-25 16:48:36 +01:00
return c != '\0' && strchr("+-*/()<>?:|&^!%\\", c);
}
2017-03-25 16:48:22 +01:00
bool
2017-03-25 16:48:22 +01:00
str_has_arith_char(char *s)
{
2017-03-24 23:23:42 +01:00
for (; *s; s++)
2017-03-25 16:48:22 +01:00
if (is_arith_char(*s))
return TRUE;
return FALSE;
}
2017-03-25 16:48:22 +01:00
int
2017-03-25 16:48:22 +01:00
get_comma_separated_values(char *values[], char *str) {
int count = 0;
char *comma_ptr;
2017-03-25 16:48:22 +01:00
while ((comma_ptr = strchr(str, ',')) != NULL) {
char *ptr = skip_back_ws(comma_ptr, str);
values[count++] = copy_substring(str, ptr);
2017-03-25 16:48:22 +01:00
str = skip_ws(comma_ptr + 1);
}
values[count++] = copy(str);
2017-03-25 16:48:22 +01:00
return count;
}
/*
check if the given token matches a model name
2017-03-25 16:48:22 +01:00
either exact
then return 1
or
2017-03-25 16:48:22 +01:00
modulo a trailing model binning extension '\.[0-9]+'
then return 2
*/
int
model_name_match(const char *token, const char *model_name)
{
const char *p;
size_t token_len = strlen(token);
if (strncmp(token, model_name, token_len) != 0)
return 0;
p = model_name + token_len;
// exact match
if (*p == '\0')
return 1;
// check for .
if (*p++ != '.')
return 0;
// minimum one trailing char
if (*p == '\0')
return 0;
// all of them digits
for (; *p; p++)
if (!isdigit_c(*p))
return 0;
return 2;
}