2000-04-27 22:03:57 +02:00
|
|
|
/**********
|
|
|
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
|
|
|
**********/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* String functions
|
|
|
|
|
*/
|
2019-12-07 01:39:08 +01:00
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <stdarg.h>
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2011-12-11 19:05:00 +01:00
|
|
|
#include "ngspice/ngspice.h"
|
2011-12-17 18:16:29 +01:00
|
|
|
#include "ngspice/stringutil.h"
|
2016-03-21 17:42:25 +01:00
|
|
|
#include "ngspice/stringskip.h"
|
2011-12-11 19:05:00 +01:00
|
|
|
#include "ngspice/dstring.h"
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2014-03-29 13:11:56 +01:00
|
|
|
|
2020-03-05 13:15:52 +01:00
|
|
|
/* Instantiations of string functions */
|
|
|
|
|
extern inline char *copy(const char *str);
|
|
|
|
|
extern inline char *copy_substring(const char *str, const char *end);
|
|
|
|
|
extern inline int scannum(const char *str);
|
|
|
|
|
extern inline int substring(const char *sub, const char *str);
|
2014-03-29 13:11:56 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
static size_t get_kr_msb_factor(size_t n);
|
|
|
|
|
static size_t kr_hash(size_t n, const char *p);
|
|
|
|
|
static inline const char *next_substr(
|
|
|
|
|
size_t n_char_pattern, const char *p_pattern,
|
|
|
|
|
const char **pp_string, const char * const p_last,
|
|
|
|
|
const size_t msb_factor, const size_t h_pattern, size_t *p_h_string);
|
|
|
|
|
static bool can_overlap(size_t n_char_pattern, const char * const p_pattern);
|
|
|
|
|
|
2022-07-06 18:43:48 +02:00
|
|
|
static void findtok_np(char** p_str, char** p_token, char** p_token_end);
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
|
|
|
|
|
/* This function returns true if the string s begins with the
|
|
|
|
|
* string p and false otherwise. */
|
|
|
|
|
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';
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function prefix */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* This function returns 1 if string begins with prefix and 0 otherwise.
|
|
|
|
|
* Neither the prefix nor string needs a null termination. */
|
|
|
|
|
int prefix_n(size_t n_char_prefix, const char *prefix,
|
|
|
|
|
size_t n_char_string, const char *string)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
/*Test that string is long enough */
|
|
|
|
|
if (n_char_prefix > n_char_string) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return memcmp(prefix, string, n_char_prefix) == 0;
|
|
|
|
|
} /* end of function prefix_n */
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2012-07-28 19:53:55 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2016-04-09 12:25:37 +02:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* This function allocates a buffer and copies the specified number of
|
|
|
|
|
* characters from the input string into the buffer followed by a
|
|
|
|
|
* terminating null.
|
|
|
|
|
*
|
|
|
|
|
* Paramters
|
|
|
|
|
* str: String to copy
|
|
|
|
|
* n_char: Number of characters to copy
|
|
|
|
|
*
|
|
|
|
|
* Return values
|
|
|
|
|
* NULL: Allocation failure
|
|
|
|
|
* otherwise: The initialized string.
|
2016-04-09 12:25:37 +02:00
|
|
|
*/
|
2019-12-07 01:39:08 +01:00
|
|
|
char *dup_string(const char *str, size_t n_char)
|
2010-04-11 10:49:05 +02:00
|
|
|
{
|
2021-01-05 17:21:23 +01:00
|
|
|
char *p = TMALLOC(char, n_char + 1);
|
2010-04-11 10:49:05 +02:00
|
|
|
|
2021-01-05 17:21:23 +01:00
|
|
|
if (p != NULL) {
|
|
|
|
|
(void) memcpy(p, str, n_char + 1);
|
2019-12-07 01:39:08 +01:00
|
|
|
p[n_char] = '\0';
|
2010-04-11 10:49:05 +02:00
|
|
|
}
|
2017-03-25 16:48:36 +01:00
|
|
|
return p;
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function dup_string */
|
2010-04-11 10:49:05 +02:00
|
|
|
|
2014-03-29 13:11:56 +01:00
|
|
|
|
2019-12-07 01:39:08 +01: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);
|
2019-12-07 01:39:08 +01:00
|
|
|
int nchars;
|
2014-03-29 13:11:56 +01:00
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
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
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* This case was previously handled by doubling the size of
|
|
|
|
|
* the buffer for "compatibility to old implementations."
|
|
|
|
|
* However, vsnprintf is defined in both C99 and SUSv2 from 1997.
|
|
|
|
|
* There is a slight difference which does not affect this
|
|
|
|
|
* usage, but both return negative values (possibly -1) on an
|
|
|
|
|
* encoding error, which would lead to an infinte loop (until
|
|
|
|
|
* memory was exhausted) with the old behavior */
|
2020-03-04 22:44:20 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
if (nchars < 0) {
|
|
|
|
|
controlled_exit(-1);
|
2019-05-01 16:35:59 +02:00
|
|
|
}
|
2019-12-07 01:39:08 +01:00
|
|
|
|
|
|
|
|
if (nchars < size) { /* String formatted OK */
|
2014-03-29 13:11:56 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* 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 */
|
2020-03-06 20:49:00 +01:00
|
|
|
|
2019-05-01 16:35:59 +02:00
|
|
|
/* Allocate a larger buffer */
|
2019-12-07 01:39:08 +01:00
|
|
|
if (p == buf) {
|
2014-03-29 13:11:56 +01:00
|
|
|
p = TMALLOC(char, size);
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2014-03-29 13:11:56 +01:00
|
|
|
p = TREALLOC(char, p, size);
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2014-03-29 13:11:56 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 16:35:59 +02:00
|
|
|
/* Return the formatted string, making a copy on the heap if the
|
|
|
|
|
* stack's buffer (buf) contains the string */
|
2019-12-07 01:39:08 +01:00
|
|
|
return (p == buf) ? dup_string(p, (size_t) nchars) : p;
|
2019-05-01 16:35:59 +02:00
|
|
|
} /* end of function tvprintf */
|
2014-03-29 13:11:56 +01:00
|
|
|
|
|
|
|
|
|
2019-05-01 16:35:59 +02: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. */
|
2019-12-07 01:39:08 +01:00
|
|
|
char *tprintf(const char *fmt, ...)
|
2014-04-12 21:00:56 +02:00
|
|
|
{
|
|
|
|
|
char *rv;
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
rv = tvprintf(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return rv;
|
2019-05-01 16:35:59 +02:00
|
|
|
} /* end of function tprintf */
|
2014-04-12 21:00:56 +02:00
|
|
|
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Append one character to a string. Don't check for overflow. */
|
|
|
|
|
/* Almost like strcat( ) XXX */
|
2019-12-07 01:39:08 +01:00
|
|
|
void appendc(char *s, char c)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
while (*s) {
|
2000-04-27 22:03:57 +02:00
|
|
|
s++;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
*s++ = c;
|
|
|
|
|
*s = '\0';
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function appendc */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* Returns the unsigned number at *p_str or 0 if there is none. *p_str
|
|
|
|
|
* points to the first character after the number that was read, so
|
|
|
|
|
* it is possible to distingish between the value 0 and a missing number
|
|
|
|
|
* by testing if the string has been advanced. */
|
|
|
|
|
int scannum_adv(char **p_str)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
const char *str = *p_str;
|
2000-04-27 22:03:57 +02:00
|
|
|
int i = 0;
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
while (isdigit_c(*str)) {
|
2000-04-27 22:03:57 +02:00
|
|
|
i = i * 10 + *(str++) - '0';
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
*p_str = (char *) str; /* locate end of number */
|
2017-03-25 16:48:36 +01:00
|
|
|
return i;
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function scannum_adv */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function returns the integer at the current string location.
|
|
|
|
|
* The string does not need to be null-terminated.
|
|
|
|
|
*
|
|
|
|
|
* Parameters
|
|
|
|
|
* str: String containing the integer to return at the beginning
|
|
|
|
|
* n: Number of characters in the string
|
|
|
|
|
* p_value: Address where the integer is returned
|
|
|
|
|
*
|
|
|
|
|
* Return values
|
|
|
|
|
* -1: No integer present
|
|
|
|
|
* -2: Overflow
|
|
|
|
|
* >0: Number of characters in the integer
|
|
|
|
|
*/
|
|
|
|
|
int get_int_n(const char *str, size_t n, int *p_value)
|
|
|
|
|
{
|
|
|
|
|
if (n == 0) { /* no string */
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int value = 0;
|
|
|
|
|
const char *p_cur = str;
|
|
|
|
|
const char * const p_end = str + n;
|
|
|
|
|
bool f_neg;
|
|
|
|
|
if (*p_cur == '-') { /* Check for leading negative sign */
|
|
|
|
|
f_neg = 1;
|
|
|
|
|
++p_cur;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
f_neg = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Iterate over chars until end or char that is not numeric */
|
|
|
|
|
for ( ; p_cur != p_end; ++p_cur) {
|
2022-05-02 20:32:21 +02:00
|
|
|
char ch_cur = *p_cur;
|
2019-12-07 01:39:08 +01:00
|
|
|
if (!isdigit(ch_cur)) { /* Test for exit due to non-numeric char */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-04-25 19:46:26 +02:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* Compute new value and check for overflow. */
|
2020-04-25 19:46:26 +02:00
|
|
|
const unsigned int value_new =
|
|
|
|
|
10 * value + (unsigned int) (ch_cur - '0');
|
2019-12-07 01:39:08 +01:00
|
|
|
if (value_new < value) {
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
value = value_new;
|
|
|
|
|
} /* end of loop over digits */
|
|
|
|
|
|
|
|
|
|
/* Test for at least one digit */
|
|
|
|
|
if (p_cur == str + f_neg) {
|
|
|
|
|
return -1; /* no digit */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test for overflow.
|
|
|
|
|
* If negative, can be 1 greater (-2**n vs 2**n -1) */
|
2020-04-25 19:46:26 +02:00
|
|
|
if (value - (unsigned int) f_neg > (unsigned int) INT_MAX) {
|
2019-12-07 01:39:08 +01:00
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Take negative if negative sign present. (This operation works
|
|
|
|
|
* correctly if value == INT_MIN since -INT_MIN == INT_MIN */
|
|
|
|
|
*p_value = f_neg ? -(int) value : (int) value;
|
|
|
|
|
|
|
|
|
|
return (int) (p_cur - str); /* number of chars in the number */
|
|
|
|
|
} /* end of function get_int_n */
|
|
|
|
|
|
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 */
|
2019-12-07 01:39:08 +01:00
|
|
|
int cieq(const char *p, const char *s)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
for (; *p; p++, s++) {
|
|
|
|
|
if (tolower_c(*p) != tolower_c(*s)) {
|
2017-03-25 16:48:36 +01:00
|
|
|
return FALSE;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2017-03-25 16:48:36 +01:00
|
|
|
return *s == '\0';
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function cieq */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* Case-insensitive string compare fore equialty with explicit length
|
|
|
|
|
* given. Neither character array needs to be null terminated. By not
|
|
|
|
|
* including the trailing null in the count, it can be used to check
|
|
|
|
|
* for a prefix. This function is useful for avoiding string copies
|
|
|
|
|
* to temporary buffers and the potential for buffer overruns that
|
|
|
|
|
* can occur when using temporary buffers without checking lengths. */
|
|
|
|
|
int cieqn(const char *p, const char *s, size_t n)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
|
|
|
if (tolower_c(p[i]) != tolower_c(s[i])) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TRUE; /* all chars matched */
|
|
|
|
|
} /* end of function cineq */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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++)
|
2019-12-07 01:39:08 +01:00
|
|
|
if (tolower_c(*p) != tolower_c(*s)) {
|
2017-03-25 16:48:36 +01:00
|
|
|
return FALSE;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2017-03-25 16:48:36 +01:00
|
|
|
return TRUE;
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function ciprefix */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
|
|
|
|
|
void strtolower(char *str)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
if (!str) {
|
2017-03-24 23:23:42 +01:00
|
|
|
return;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2017-03-24 23:23:42 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
for (; *str; str++) {
|
2017-03-24 23:23:42 +01:00
|
|
|
*str = tolower_c(*str);
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
|
|
|
|
} /* end of function strtolower */
|
|
|
|
|
|
2010-11-04 21:01:46 +01:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
void strtoupper(char *str)
|
2010-11-04 21:01:46 +01:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
if (!str) {
|
2017-03-24 23:23:42 +01:00
|
|
|
return;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2017-03-24 23:23:42 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
for (; *str; str++) {
|
2017-03-24 23:23:42 +01:00
|
|
|
*str = toupper_c(*str);
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
|
|
|
|
} /* end of function strtoupper */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-08-11 22:53:33 +02:00
|
|
|
#ifdef CIDER
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-08-11 22:53:33 +02: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
|
|
|
|
2003-08-11 22:53:33 +02: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
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
int cinprefix(char *p, char *s, int n)
|
2003-08-11 22:53:33 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
if (!p || !s) {
|
2017-03-25 16:48:36 +01:00
|
|
|
return 0;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
for (; *p; p++, s++, n--) {
|
|
|
|
|
if (tolower_c(*p) != tolower_c(*s)) {
|
2017-03-25 16:48:36 +01:00
|
|
|
return 0;
|
2019-12-07 01:39:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2017-03-25 16:48:36 +01:00
|
|
|
return n <= 0;
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function cinprefix */
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
|
|
|
|
|
2003-08-11 22:53:33 +02: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
|
|
|
*
|
2003-08-11 22:53:33 +02:00
|
|
|
*/
|
2017-03-25 16:48:22 +01:00
|
|
|
|
|
|
|
|
int
|
2017-03-25 16:56:22 +01:00
|
|
|
cimatch(char *p, char *s)
|
2003-08-11 22:53:33 +02:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
|
2003-08-11 22:53:33 +02:00
|
|
|
#endif /* CIDER */
|
|
|
|
|
|
|
|
|
|
|
2004-01-10 22:39:36 +01:00
|
|
|
/*-------------------------------------------------------------------------*
|
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
|
2004-01-10 22:39:36 +01:00
|
|
|
* 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
|
|
|
|
2022-09-01 16:57:54 +02:00
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
paren = 0;
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2016-03-21 18:04:39 +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;
|
2016-03-08 21:20:11 +01:00
|
|
|
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
|
|
|
|
2016-03-08 21:20:11 +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
|
|
|
}
|
|
|
|
|
|
2016-06-15 21:09:53 +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.
|
|
|
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
|
|
2017-03-19 20:11:01 +01:00
|
|
|
char *
|
2016-11-01 11:30:08 +01:00
|
|
|
nexttok(const char *s)
|
2016-06-15 21:09:53 +02:00
|
|
|
{
|
2019-06-14 22:22:17 +02:00
|
|
|
if (!s)
|
|
|
|
|
return NULL;
|
2017-03-19 20:11:01 +01:00
|
|
|
int paren = 0;
|
2016-06-15 21:09:53 +02:00
|
|
|
|
2017-03-19 20:11:01 +01:00
|
|
|
s = skip_ws(s);
|
|
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
for (; *s && !isspace_c(*s); s++)
|
2017-03-19 20:11:01 +01:00
|
|
|
if (*s == '(')
|
2016-06-15 21:09:53 +02:00
|
|
|
paren += 1;
|
2017-03-19 20:11:01 +01:00
|
|
|
else if (*s == ')')
|
2016-06-15 21:09:53 +02:00
|
|
|
paren -= 1;
|
2017-03-19 20:11:01 +01:00
|
|
|
else if (*s == ',' && paren < 1)
|
2016-06-15 21:09:53 +02:00
|
|
|
break;
|
2017-03-19 20:11:01 +01:00
|
|
|
|
|
|
|
|
while (isspace_c(*s) || *s == ',')
|
|
|
|
|
s++;
|
|
|
|
|
|
2016-11-01 11:30:08 +01:00
|
|
|
return (char *) s;
|
2016-06-15 21:09:53 +02:00
|
|
|
}
|
|
|
|
|
|
2021-01-28 17:06:02 +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
|
2022-07-06 18:43:48 +02:00
|
|
|
* actstring = nexttok(actstring). This is derived from the gettok_np version.
|
2021-01-28 17:06:02 +01:00
|
|
|
* It acts like gettok, except that it treats parens and commas like
|
|
|
|
|
* whitespace.
|
|
|
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
nexttok_noparens(const char* s)
|
|
|
|
|
{
|
|
|
|
|
if (!s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
s = skip_ws(s);
|
|
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
for (; *s && !isspace_c(*s); s++)
|
|
|
|
|
if (*s == '(')
|
|
|
|
|
break;
|
|
|
|
|
else if (*s == ')')
|
|
|
|
|
break;
|
|
|
|
|
else if (*s == ',')
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
while (isspace_c(*s) || *s == ',' || *s == '(' || *s == ')')
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
return (char*)s;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 21:09:53 +02:00
|
|
|
|
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)
|
|
|
|
|
{
|
2019-06-01 19:41:42 +02:00
|
|
|
char *p_src = *s; /* location in source string */
|
|
|
|
|
char c; /* current char */
|
2010-07-10 13:27:57 +02:00
|
|
|
|
2019-06-01 19:41:42 +02:00
|
|
|
/* Step past whitespace and '=' */
|
|
|
|
|
while (isspace_c(c = *p_src) || (c == '=')) {
|
|
|
|
|
p_src++;
|
|
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-06-01 19:41:42 +02: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
|
|
|
|
2019-06-01 19:41:42 +02: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
|
|
|
|
2019-06-01 19:41:42 +02: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
|
|
|
|
2019-06-01 19:41:42 +02:00
|
|
|
/* Step past whitespace and ',' */
|
|
|
|
|
while (isspace_c(c = *p_src) || (c == ',')) {
|
|
|
|
|
p_src++;
|
|
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-06-01 19:41:42 +02:00
|
|
|
*s = p_src; /* update position in string */
|
2017-03-25 16:48:36 +01:00
|
|
|
return token;
|
2019-06-01 19:41:42 +02:00
|
|
|
} /* end of function gettok_iv */
|
|
|
|
|
|
2010-07-10 13:27:57 +02:00
|
|
|
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
/* findtok_noparen() does the string scanning for gettok_noparens() but
|
|
|
|
|
* does not allocate a token. Hence it is useful when a copy of the token
|
|
|
|
|
* is not required */
|
|
|
|
|
void findtok_noparen(char **p_str, char **p_token, char **p_token_end)
|
|
|
|
|
{
|
|
|
|
|
char *str = *p_str;
|
|
|
|
|
|
|
|
|
|
str = skip_ws(str);
|
|
|
|
|
|
|
|
|
|
if (!*str) {
|
|
|
|
|
*p_str = str;
|
|
|
|
|
*p_token = (char *) NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p_token = str; /* Token starts after whitespace */
|
|
|
|
|
{
|
|
|
|
|
char c;
|
|
|
|
|
while ((c = *str) != '\0' &&
|
|
|
|
|
!isspace_c(c) &&
|
|
|
|
|
(c != '(') &&
|
|
|
|
|
(c != ')') &&
|
|
|
|
|
(c != ',')
|
|
|
|
|
) {
|
|
|
|
|
str++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*p_token_end = str;
|
|
|
|
|
|
|
|
|
|
str = skip_ws(str);
|
|
|
|
|
*p_str = str;
|
|
|
|
|
} /* end of function findtok_noparen */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
/*-------------------------------------------------------------------------*
|
|
|
|
|
* gettok_noparens was added by SDB on 4.21.2003.
|
2004-01-10 22:39:36 +01:00
|
|
|
* 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
|
2004-01-10 22:39:36 +01:00
|
|
|
* 'translate' (subckt.c).
|
2003-07-23 21:36:39 +02:00
|
|
|
*-------------------------------------------------------------------------*/
|
2019-12-07 01:39:08 +01:00
|
|
|
char *gettok_noparens(char **s)
|
2003-07-23 21:36:39 +02:00
|
|
|
{
|
2019-12-07 01:39:08 +01:00
|
|
|
char *token, *token_e;
|
2022-09-01 16:57:54 +02:00
|
|
|
|
|
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
findtok_noparen(s, &token, &token_e);
|
|
|
|
|
if (token == (char *) NULL) {
|
|
|
|
|
return (char *) NULL; /* return NULL if we come to end of line */
|
2003-07-23 21:36:39 +02:00
|
|
|
}
|
2004-01-10 22:39:36 +01:00
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
return copy_substring(token, token_e);
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of function gettok_noparens */
|
|
|
|
|
|
2004-01-10 22:39:36 +01:00
|
|
|
|
2022-07-06 18:43:48 +02:00
|
|
|
/* findtok_np() does the string scanning for gettok_np() but
|
|
|
|
|
* does not allocate a token. It skips over all white spaces, ',', '('and ')' */
|
|
|
|
|
static
|
|
|
|
|
void findtok_np(char** p_str, char** p_token, char** p_token_end)
|
|
|
|
|
{
|
|
|
|
|
char* str = *p_str;
|
|
|
|
|
|
|
|
|
|
while (isspace_c(*str) || *str == ',' || *str == '(' || *str == ')')
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
if (!*str) {
|
|
|
|
|
*p_str = str;
|
|
|
|
|
*p_token = (char*)NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p_token = str; /* Token starts after whitespace */
|
|
|
|
|
{
|
|
|
|
|
char c;
|
|
|
|
|
while ((c = *str) != '\0' &&
|
|
|
|
|
!isspace_c(c) &&
|
|
|
|
|
(c != '(') &&
|
|
|
|
|
(c != ')') &&
|
|
|
|
|
(c != ',')
|
|
|
|
|
) {
|
|
|
|
|
str++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*p_token_end = str;
|
|
|
|
|
|
|
|
|
|
while (isspace_c(*str) || *str == ',' || *str == '(' || *str == ')')
|
|
|
|
|
str++;
|
|
|
|
|
|
|
|
|
|
*p_str = str;
|
|
|
|
|
} /* end of function findtok_noparen */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
|
|
|
* gettok_np acts like gettok, except that it treats parens and commas like
|
|
|
|
|
* whitespace. That is, it stops parsing and returns when it finds one of
|
|
|
|
|
* those chars. It then moves s beyond all white spaces, ',', '('and ')'.
|
|
|
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
|
char* gettok_np(char** s)
|
|
|
|
|
{
|
|
|
|
|
char* token, * token_e;
|
2022-09-01 16:57:54 +02:00
|
|
|
|
|
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2022-07-06 18:43:48 +02:00
|
|
|
findtok_np(s, &token, &token_e);
|
|
|
|
|
if (token == (char*)NULL) {
|
|
|
|
|
return (char*)NULL; /* return NULL if we come to end of line */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return copy_substring(token, token_e);
|
|
|
|
|
} /* end of function gettok_noparens */
|
|
|
|
|
|
2019-04-13 12:42:55 +02:00
|
|
|
/*-------------------------------------------------------------------------*
|
|
|
|
|
* 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;
|
|
|
|
|
|
2022-09-01 16:57:54 +02:00
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2019-04-13 12:42:55 +02:00
|
|
|
*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
|
|
|
|
2022-09-01 16:57:54 +02:00
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2016-03-21 18:04:39 +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;
|
2013-12-21 20:15:51 +01:00
|
|
|
|
2007-12-29 22:13:23 +01:00
|
|
|
/* Now iterate up to next non-whitespace char */
|
2016-03-21 18:04:39 +01:00
|
|
|
*s = skip_ws(*s);
|
2011-12-21 22:33:47 +01:00
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
return copy_substring(token, token_e);
|
2011-12-21 22:33:47 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2020-12-04 17:11:40 +01:00
|
|
|
/* get the next token starting at next non white space, 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.
|
2011-12-21 22:33:47 +01:00
|
|
|
*/
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2011-12-21 22:33:47 +01:00
|
|
|
char *
|
2012-07-21 22:23:49 +02:00
|
|
|
gettok_char(char **s, char p, bool inc_p, bool nested)
|
2011-12-21 22:33:47 +01:00
|
|
|
{
|
|
|
|
|
char c;
|
2017-03-25 16:50:19 +01:00
|
|
|
const char *token, *token_e;
|
2011-12-21 22:33:47 +01:00
|
|
|
|
2022-09-01 16:57:54 +02:00
|
|
|
if (!*s)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2016-03-21 18:04:39 +01:00
|
|
|
*s = skip_ws(*s);
|
2011-12-21 22:33:47 +01:00
|
|
|
|
|
|
|
|
if (!**s)
|
2017-03-25 16:48:36 +01:00
|
|
|
return NULL; /* return NULL if we come to end of line */
|
2011-12-21 22:33:47 +01:00
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
token = *s;
|
2017-03-25 16:48:22 +01:00
|
|
|
if (nested && ((p == '}') || (p == ')') || (p == ']'))) {
|
2012-07-21 22:23:49 +02:00
|
|
|
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 = '(';
|
2012-07-21 22:23:49 +02:00
|
|
|
/* 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)++;
|
2012-07-21 22:23:49 +02:00
|
|
|
/* 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)
|
2012-07-21 22:23:49 +02:00
|
|
|
break;
|
2017-03-25 16:48:22 +01:00
|
|
|
(*s)++;
|
2012-07-21 22:23:49 +02:00
|
|
|
}
|
2011-12-21 22:33:47 +01:00
|
|
|
}
|
2012-07-21 22:23:49 +02:00
|
|
|
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)++;
|
2012-07-21 22:23:49 +02:00
|
|
|
|
2012-07-19 23:16:15 +02:00
|
|
|
if (c == '\0')
|
|
|
|
|
/* p not found */
|
2017-03-25 16:48:36 +01:00
|
|
|
return NULL;
|
2012-07-21 22:23:49 +02:00
|
|
|
|
2011-12-21 22:33:47 +01:00
|
|
|
if (inc_p)
|
2012-07-21 22:23:49 +02:00
|
|
|
/* add p */
|
2017-03-25 16:48:22 +01:00
|
|
|
(*s)++;
|
2013-12-21 20:15:51 +01:00
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
token_e = *s;
|
2011-12-21 22:33:47 +01:00
|
|
|
|
|
|
|
|
/* Now iterate up to next non-whitespace char */
|
2016-03-21 18:04:39 +01:00
|
|
|
*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
|
|
|
|
2004-01-10 22:39:36 +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
|
|
|
|
2004-01-10 22:39:36 +01:00
|
|
|
char *
|
|
|
|
|
gettok_node(char **s)
|
|
|
|
|
{
|
|
|
|
|
char c;
|
2017-03-25 16:50:19 +01:00
|
|
|
const char *token, *token_e;
|
2004-01-10 22:39:36 +01:00
|
|
|
|
2013-10-13 22:41:49 +02:00
|
|
|
if (*s == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2016-03-08 21:20:11 +01:00
|
|
|
while (isspace_c(**s) ||
|
2017-03-25 16:48:22 +01:00
|
|
|
(**s == '(') ||
|
|
|
|
|
(**s == ')') ||
|
|
|
|
|
(**s == ',')
|
|
|
|
|
)
|
2004-01-10 22:39:36 +01:00
|
|
|
(*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 */
|
2004-01-10 22:39:36 +01:00
|
|
|
|
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)++;
|
2004-01-10 22:39:36 +01:00
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
token_e = *s;
|
2013-10-18 21:29:38 +02:00
|
|
|
|
2004-01-10 22:39:36 +01:00
|
|
|
/* Now iterate up to next non-whitespace char */
|
2016-03-08 21:20:11 +01:00
|
|
|
while (isspace_c(**s) ||
|
2017-03-25 16:48:22 +01:00
|
|
|
(**s == '(') ||
|
|
|
|
|
(**s == ')') ||
|
|
|
|
|
(**s == ',')
|
|
|
|
|
)
|
2004-01-10 22:39:36 +01:00
|
|
|
(*s)++; /* iterate over whitespace and ( , ) */
|
|
|
|
|
|
2017-03-25 16:50:19 +01:00
|
|
|
return copy_substring(token, token_e);
|
2003-07-23 21:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02: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
|
2020-03-07 19:46:54 +01:00
|
|
|
* paren, 1 if no left paren is found, -1 if left paren is the last character.
|
|
|
|
|
* It is called from 'translate' (subckt.c).
|
2003-07-23 21:36:39 +02:00
|
|
|
*-------------------------------------------------------------------------*/
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
int
|
|
|
|
|
get_l_paren(char **s)
|
|
|
|
|
{
|
2017-03-25 16:48:22 +01:00
|
|
|
while (**s && (**s != '('))
|
2003-07-23 21:36:39 +02:00
|
|
|
(*s)++;
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
if (!**s)
|
2017-03-25 16:48:36 +01:00
|
|
|
return 1;
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
(*s)++;
|
|
|
|
|
|
2020-03-07 19:46:54 +01:00
|
|
|
if (**s == '\0')
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
2003-07-23 21:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
|
|
|
* 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
|
2020-03-07 19:46:54 +01:00
|
|
|
* paren, 1 if no right paren is found, and -1 if right paren is te last
|
|
|
|
|
* character. It is called from 'translate' (subckt.c).
|
2003-07-23 21:36:39 +02:00
|
|
|
*-------------------------------------------------------------------------*/
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
int
|
|
|
|
|
get_r_paren(char **s)
|
|
|
|
|
{
|
2017-03-25 16:48:22 +01:00
|
|
|
while (**s && (**s != ')'))
|
2003-07-23 21:36:39 +02:00
|
|
|
(*s)++;
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2003-07-23 21:36:39 +02:00
|
|
|
if (!**s)
|
2017-03-25 16:48:36 +01:00
|
|
|
return 1;
|
2003-07-23 21:36:39 +02:00
|
|
|
|
|
|
|
|
(*s)++;
|
|
|
|
|
|
2020-03-07 19:46:54 +01:00
|
|
|
if (**s == '\0')
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
2003-07-23 21:36:39 +02:00
|
|
|
}
|
|
|
|
|
|
2007-12-02 23:00:25 +01:00
|
|
|
/*-------------------------------------------------------------------------*
|
|
|
|
|
* this function strips all white space inside parens
|
2019-06-01 19:41:42 +02:00
|
|
|
* is needed in gettoks (dotcards.c) for correct processing of expressions
|
|
|
|
|
* like " .plot v( 5 , 4 ) v( 6 )" -> .plot v(5,4) v(6)"
|
2007-12-02 23:00:25 +01:00
|
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
|
char *
|
2019-06-01 19:41:42 +02:00
|
|
|
stripWhiteSpacesInsideParens(const char *str)
|
2007-12-02 23:00:25 +01:00
|
|
|
{
|
2019-06-01 19:41:42 +02:00
|
|
|
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;
|
2007-12-02 23:00:25 +01:00
|
|
|
}
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-06-01 19:41:42 +02: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
|
|
|
|
|
|
|
|
|
2008-04-06 21:36:06 +02:00
|
|
|
bool
|
2017-03-25 16:48:22 +01:00
|
|
|
isquote(char ch)
|
2008-04-06 21:36:06 +02:00
|
|
|
{
|
2017-03-25 16:48:36 +01:00
|
|
|
return ch == '\'' || ch == '"';
|
2008-04-06 21:36:06 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2008-04-06 21:36:06 +02:00
|
|
|
bool
|
2017-03-25 16:48:22 +01:00
|
|
|
is_arith_char(char c)
|
2008-04-06 21:36:06 +02:00
|
|
|
{
|
2017-03-25 16:48:36 +01:00
|
|
|
return c != '\0' && strchr("+-*/()<>?:|&^!%\\", c);
|
2008-04-06 21:36:06 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2008-04-06 21:36:06 +02:00
|
|
|
bool
|
2017-03-25 16:48:22 +01:00
|
|
|
str_has_arith_char(char *s)
|
2008-04-06 21:36:06 +02:00
|
|
|
{
|
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;
|
2008-04-06 21:36:06 +02:00
|
|
|
}
|
|
|
|
|
|
2017-03-25 16:48:22 +01:00
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
int get_comma_separated_values(char *values[], char *str)
|
|
|
|
|
{
|
2017-03-25 16:48:22 +01:00
|
|
|
int count = 0;
|
2017-03-25 16:47:07 +01:00
|
|
|
char *comma_ptr;
|
2017-03-25 16:48:22 +01:00
|
|
|
|
|
|
|
|
while ((comma_ptr = strchr(str, ',')) != NULL) {
|
2017-03-25 16:47:07 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2017-03-25 16:47:07 +01:00
|
|
|
values[count++] = copy(str);
|
2017-03-25 16:48:22 +01:00
|
|
|
return count;
|
2008-04-06 21:36:06 +02:00
|
|
|
}
|
2014-08-09 15:35:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
check if the given token matches a model name
|
2017-03-25 16:48:22 +01:00
|
|
|
either exact
|
|
|
|
|
then return 1
|
2014-08-09 15:35:31 +02:00
|
|
|
or
|
2017-03-25 16:48:22 +01:00
|
|
|
modulo a trailing model binning extension '\.[0-9]+'
|
|
|
|
|
then return 2
|
2014-08-09 15:35:31 +02:00
|
|
|
*/
|
2019-12-07 01:39:08 +01:00
|
|
|
int model_name_match(const char *token, const char *model_name)
|
2014-08-09 15:35:31 +02:00
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
size_t token_len = strlen(token);
|
|
|
|
|
|
|
|
|
|
if (strncmp(token, model_name, token_len) != 0)
|
2014-08-09 15:35:31 +02:00
|
|
|
return 0;
|
2014-08-09 15:35:31 +02:00
|
|
|
|
|
|
|
|
p = model_name + token_len;
|
|
|
|
|
|
|
|
|
|
// exact match
|
|
|
|
|
if (*p == '\0')
|
2014-08-09 15:35:31 +02:00
|
|
|
return 1;
|
2014-08-09 15:35:31 +02:00
|
|
|
|
|
|
|
|
// check for .
|
|
|
|
|
if (*p++ != '.')
|
2014-08-09 15:35:31 +02:00
|
|
|
return 0;
|
2014-08-09 15:35:31 +02:00
|
|
|
|
|
|
|
|
// minimum one trailing char
|
|
|
|
|
if (*p == '\0')
|
2014-08-09 15:35:31 +02:00
|
|
|
return 0;
|
2014-08-09 15:35:31 +02:00
|
|
|
|
|
|
|
|
// all of them digits
|
|
|
|
|
for (; *p; p++)
|
2016-03-08 21:20:11 +01:00
|
|
|
if (!isdigit_c(*p))
|
2014-08-09 15:35:31 +02:00
|
|
|
return 0;
|
2014-08-09 15:35:31 +02:00
|
|
|
|
2014-08-09 15:35:31 +02:00
|
|
|
return 2;
|
2019-12-07 01:39:08 +01:00
|
|
|
} /* end of funtion model_name_match */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function returns 1 if pattern is a substring anywhere in str and
|
|
|
|
|
* 0 otherwise. A null pattern is considered a mismatch.
|
|
|
|
|
*
|
|
|
|
|
* Uses Karp-Rabin substring matching with base=256 and modulus=1009
|
|
|
|
|
*/
|
|
|
|
|
int substring_n(size_t n_char_pattern, const char *p_pattern,
|
|
|
|
|
size_t n_char_string, const char *p_string)
|
|
|
|
|
{
|
|
|
|
|
/* Test for a pattern to match */
|
|
|
|
|
if (n_char_pattern == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test for a string of sufficient length */
|
|
|
|
|
if (n_char_pattern > n_char_string) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Factor for rolling hash computation */
|
|
|
|
|
const size_t msb_factor = get_kr_msb_factor(n_char_pattern);
|
|
|
|
|
|
|
|
|
|
const size_t h_pattern = kr_hash(n_char_pattern, p_pattern);
|
|
|
|
|
size_t h_string = kr_hash(n_char_pattern, p_string);
|
|
|
|
|
|
|
|
|
|
/* Compare at beginning. If hashes match, do full compare */
|
|
|
|
|
if (h_pattern == h_string &&
|
|
|
|
|
memcmp(p_pattern, p_string, n_char_pattern) == 0) {
|
|
|
|
|
return 1; /* match at start */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compare at each possible starting point in the string */
|
|
|
|
|
const char *p_last = p_string + (n_char_string - n_char_pattern - 1);
|
|
|
|
|
|
|
|
|
|
return next_substr(n_char_pattern, p_pattern, &p_string, p_last,
|
|
|
|
|
msb_factor, h_pattern, &h_string) == (char *) NULL ?
|
|
|
|
|
0 : 1;
|
|
|
|
|
} /* end of function substring_n */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function initializes a scan for substring matches */
|
|
|
|
|
void substring_match_init(size_t n_char_pattern, const char *p_pattern,
|
|
|
|
|
size_t n_char_string, const char *p_string, bool f_overlap,
|
|
|
|
|
struct substring_match_info *p_scan_state)
|
|
|
|
|
{
|
|
|
|
|
/* Save input info into structure. Note that the strings are not
|
|
|
|
|
* copied, so they must remain allocated and unaltered while the
|
|
|
|
|
* search is in progress. */
|
|
|
|
|
p_scan_state->n_char_pattern = n_char_pattern;
|
|
|
|
|
p_scan_state->p_pattern = p_pattern;
|
|
|
|
|
p_scan_state->n_char_string = n_char_string;
|
|
|
|
|
p_scan_state->p_string = p_string;
|
|
|
|
|
|
|
|
|
|
/*** Calculate intermediate data ***/
|
|
|
|
|
|
|
|
|
|
/* Test for a pattern to match */
|
|
|
|
|
if (n_char_pattern == 0) {
|
|
|
|
|
p_scan_state->f_done = TRUE;
|
|
|
|
|
}
|
|
|
|
|
/* Test for a string of sufficient length */
|
|
|
|
|
else if (n_char_pattern > n_char_string) {
|
|
|
|
|
p_scan_state->f_done = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p_scan_state->f_done = FALSE;
|
|
|
|
|
|
|
|
|
|
/* Look for overlaps only if possible */
|
|
|
|
|
p_scan_state->f_overlap= f_overlap ?
|
|
|
|
|
!can_overlap(n_char_pattern, p_pattern) : FALSE;
|
|
|
|
|
p_scan_state->n_char_pattern_1 = n_char_pattern - 1;
|
|
|
|
|
p_scan_state->msb_factor = get_kr_msb_factor(n_char_pattern);
|
|
|
|
|
p_scan_state->h_pattern = kr_hash(n_char_pattern, p_pattern);
|
|
|
|
|
p_scan_state->h_string = kr_hash(n_char_pattern, p_string);
|
|
|
|
|
p_scan_state->p_last =
|
|
|
|
|
p_string + (n_char_string - n_char_pattern - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
} /* end of function substring_match_init */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function finds the next substring match
|
|
|
|
|
*
|
|
|
|
|
* Parameter
|
|
|
|
|
* p_scan_state: Address of struct substring_match_info initialized by
|
|
|
|
|
* substring_match_init()
|
|
|
|
|
*
|
|
|
|
|
* Return value
|
|
|
|
|
* NULL if there is no match or the address of the next match otherwise
|
|
|
|
|
*/
|
|
|
|
|
char *substring_match_next(struct substring_match_info *p_scan_state)
|
|
|
|
|
{
|
|
|
|
|
/* First test if there are no more possible matches */
|
|
|
|
|
if (p_scan_state->f_done) {
|
|
|
|
|
return (char *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find next match, if any */
|
|
|
|
|
const char * const p_match = next_substr(
|
|
|
|
|
p_scan_state->n_char_pattern, p_scan_state->p_pattern,
|
|
|
|
|
&p_scan_state->p_string, p_scan_state->p_last,
|
|
|
|
|
p_scan_state->msb_factor,p_scan_state->h_pattern,
|
|
|
|
|
&p_scan_state->h_string);
|
|
|
|
|
|
|
|
|
|
/* Update done status if changed */
|
|
|
|
|
if (p_match == (char *) NULL) {
|
|
|
|
|
p_scan_state->f_done = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (!p_scan_state->f_overlap) {
|
|
|
|
|
p_scan_state->p_string +=
|
|
|
|
|
p_scan_state->n_char_pattern_1; /* end of match */
|
|
|
|
|
p_scan_state->h_string = p_scan_state->h_pattern;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (char *) p_match; /* Return result */
|
|
|
|
|
} /* end of function substring_match_next */
|
|
|
|
|
|
2019-06-01 19:41:42 +02:00
|
|
|
|
|
|
|
|
|
2019-12-07 01:39:08 +01:00
|
|
|
#ifdef COMPILE_UNUSED_FUNCTIONS
|
|
|
|
|
/* This funtion returns the locations of optionally non-overlapping substring
|
|
|
|
|
* matches. For example, in the string aaaaa, aa is found in non-overlapping
|
|
|
|
|
* locations at 0-based offsets 0 and 2 ahd with overlapping allowed atr
|
|
|
|
|
* offsets 0, 1, 2, and 3 */
|
|
|
|
|
size_t get_substring_matches(size_t n_char_pattern, const char *p_pattern,
|
|
|
|
|
size_t n_char_string, const char *p_string,
|
|
|
|
|
size_t n_elem_buf, char *p_match_buf, bool f_overlap)
|
|
|
|
|
{
|
|
|
|
|
/* Test for a pattern to match */
|
|
|
|
|
if (n_char_pattern == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test for a string of sufficient length */
|
|
|
|
|
if (n_char_pattern > n_char_string) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle 0-sized buffer */
|
|
|
|
|
if (n_elem_buf == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Factor for rolling hash computation */
|
|
|
|
|
const size_t msb_factor = get_kr_msb_factor(n_char_pattern);
|
|
|
|
|
|
|
|
|
|
const size_t h_pattern = kr_hash(n_char_pattern, p_pattern);
|
|
|
|
|
size_t h_string = kr_hash(n_char_pattern, p_string);
|
|
|
|
|
|
|
|
|
|
/* Compare at beginning. If hashes match, do full compare */
|
|
|
|
|
if (h_pattern == h_string &&
|
|
|
|
|
memcmp(p_pattern, p_string, n_char_pattern) == 0) {
|
|
|
|
|
return 1; /* match at start */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compare at each possible starting point in the string */
|
|
|
|
|
const char *p_last = p_string + (n_char_string - n_char_pattern - 1);
|
|
|
|
|
const size_t n_char_pattern_1 = n_char_pattern - 1;
|
|
|
|
|
char **pp_match_buf_cur = &p_match_buf;
|
|
|
|
|
char * const * const pp_match_buf_end = pp_match_buf_cur + n_elem_buf;
|
|
|
|
|
|
|
|
|
|
/* Look for overlaps only if possible */
|
|
|
|
|
f_overlap = f_overlap ? !can_overlap(n_char_pattern, p_pattern) : FALSE;
|
|
|
|
|
|
|
|
|
|
for ( ; pp_match_buf_cur < pp_match_buf_end; pp_match_buf_cur++) {
|
|
|
|
|
const char *p_match = next_substr(n_char_pattern, p_pattern,
|
|
|
|
|
&p_string, p_last, msb_factor, h_pattern, &h_string);
|
|
|
|
|
if (p_match == (char *) NULL) { /* if no match, done */
|
|
|
|
|
return (int) (pp_match_buf_cur - &p_match_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save result */
|
|
|
|
|
*pp_match_buf_cur = (char *) p_match;
|
|
|
|
|
|
|
|
|
|
/* If overlapping is not allowed, contniue search after the match.
|
|
|
|
|
* Note that in this case, the string hash is the pattern hash. */
|
|
|
|
|
if (!f_overlap) {
|
|
|
|
|
p_string += n_char_pattern_1; /* end of match */
|
|
|
|
|
h_string = h_pattern;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over string */
|
|
|
|
|
|
|
|
|
|
return n_elem_buf; /* full buffer */
|
|
|
|
|
} /* end of funtion get_substring_matches */
|
|
|
|
|
#endif /* COMPILE_UNUSED_FUNCTIONS */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function determines if a pattern can allow overlapping matches.
|
|
|
|
|
* For example, the pattern "starts" would have overlapped matches in the
|
|
|
|
|
* string "startstarts".
|
|
|
|
|
*
|
|
|
|
|
* Remarks
|
|
|
|
|
* While not directly related to this function, there is only a binary yes/no
|
|
|
|
|
* interest regarding overlap rather than an offset into the the string where
|
|
|
|
|
* such overlap may occur. That is because the hash value is being computed
|
|
|
|
|
* incremetally, so the only time when there is substantial computational
|
|
|
|
|
* savings in this approach is when the hash value is known, as it would be
|
|
|
|
|
* at the end of a match (since the hash of the pattern is knonw.)
|
|
|
|
|
*/
|
|
|
|
|
static bool can_overlap(size_t n_char_pattern, const char * const p_pattern)
|
|
|
|
|
{
|
|
|
|
|
if (n_char_pattern < 2) { /* does not matter */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find the last occurrance of the first character */
|
|
|
|
|
const char * const p_end = p_pattern + n_char_pattern;
|
|
|
|
|
const char *p_cur = p_end - 1;
|
|
|
|
|
const char ch_first = *p_pattern;
|
|
|
|
|
for ( ; p_cur > p_pattern; --p_cur) {
|
|
|
|
|
if (*p_cur == ch_first) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop finding the first char */
|
|
|
|
|
|
|
|
|
|
/* Test for no duplicate */
|
|
|
|
|
if (p_cur == p_pattern) { /* not found */
|
|
|
|
|
return FALSE; /* no duplicate so cannot overlap */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now must match from this char onward to overlap */
|
|
|
|
|
const char *p_src = p_pattern;
|
|
|
|
|
for ( ; p_cur != p_end; ++p_cur, ++p_src) {
|
|
|
|
|
if (*p_cur != *p_src) { /* comparing 'b' to 'd' in "abcad"
|
|
|
|
|
* for example */
|
|
|
|
|
return FALSE; /* Mismatch, so not an overlap */
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop finding the first char */
|
|
|
|
|
|
|
|
|
|
return TRUE; /* Matched to end of word */
|
|
|
|
|
} /* end of function can_overlap */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Prime number of Karp-Rabin hashing. Tradeoff between number of hash
|
|
|
|
|
* collisions and number of times modulus must be taken. */
|
|
|
|
|
#define KR_MODULUS 1009
|
|
|
|
|
/* Compute (256^(n-1))%KR_MODULUS */
|
|
|
|
|
static size_t get_kr_msb_factor(size_t n)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
size_t factor = 1;
|
|
|
|
|
const size_t n_itr = n - 1;
|
|
|
|
|
for (i = 0; i < n_itr; ++i) {
|
|
|
|
|
size_t factor_new = (factor << 8);
|
|
|
|
|
if (factor_new < factor) { /* overflow */
|
|
|
|
|
factor %= KR_MODULUS; /* take modulus */
|
|
|
|
|
factor <<= 8; /* and recompute */
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop building factor */
|
|
|
|
|
|
|
|
|
|
/* Return the factor after final modulus if necessary */
|
|
|
|
|
if (factor >= KR_MODULUS) {
|
|
|
|
|
factor %= KR_MODULUS;
|
|
|
|
|
}
|
|
|
|
|
return factor;
|
|
|
|
|
} /* end of function get_kr_msb_factor */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Compute KR hash assuming n >= 1 */
|
|
|
|
|
static size_t kr_hash(size_t n, const char *p)
|
|
|
|
|
{
|
|
|
|
|
const char * const p_end = p + n;
|
|
|
|
|
size_t hash = *(unsigned char *) p;
|
|
|
|
|
for (p++; p < p_end; p++) {
|
|
|
|
|
unsigned char ch = *(unsigned char *) p;
|
|
|
|
|
size_t hash_new = (hash << 8) + ch;
|
|
|
|
|
if (hash_new < hash) { /* overflow */
|
|
|
|
|
hash %= KR_MODULUS; /* take modulus */
|
|
|
|
|
hash = (hash << 8) + ch; /* and recompute */
|
|
|
|
|
}
|
|
|
|
|
else { /* no overflow, so no need for modulus yet */
|
|
|
|
|
hash = hash_new;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop hasing chars */
|
|
|
|
|
|
|
|
|
|
/* Do final modulus if necessary */
|
|
|
|
|
if (hash >= KR_MODULUS) {
|
|
|
|
|
hash %= KR_MODULUS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
|
} /* end of function kr_hash */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function locates the next substring match. It is intended to be called
|
|
|
|
|
* as part of the scanning of a string for a substring
|
|
|
|
|
*
|
|
|
|
|
* Parameters
|
|
|
|
|
* n_char_pattern: Length of pattern to find
|
|
|
|
|
* p_pattern: Pattern to find. Need not be null-terminated
|
|
|
|
|
* pp_string: Address containing the current location in the string. Updated
|
|
|
|
|
* if a match is found.
|
|
|
|
|
* p_last: Address of last possible location of a match
|
|
|
|
|
* msb_factor: Constant related to hash update
|
|
|
|
|
* h_pattern: Computed hash of pattern
|
|
|
|
|
* p_h_string: Address containing the current hash value of the location
|
|
|
|
|
* in the string being considered. It is updated in the function.
|
|
|
|
|
*
|
|
|
|
|
* Return value
|
|
|
|
|
* NULL if no substring, or the address of the substring if one exists.
|
|
|
|
|
*/
|
|
|
|
|
static inline const char *next_substr(
|
|
|
|
|
size_t n_char_pattern, const char *p_pattern,
|
|
|
|
|
const char **pp_string, const char * const p_last,
|
|
|
|
|
const size_t msb_factor, const size_t h_pattern, size_t *p_h_string)
|
|
|
|
|
{
|
|
|
|
|
const char *p_string = *pp_string;
|
|
|
|
|
size_t h_string = *p_h_string;
|
|
|
|
|
|
|
|
|
|
for ( ; ; ) {
|
|
|
|
|
/* Update hash for next starting point at p_string + 1 */
|
|
|
|
|
if ((h_string = (((h_string - (unsigned char) p_string[0] *
|
2020-04-25 19:46:26 +02:00
|
|
|
msb_factor) << 8) + (size_t) p_string[n_char_pattern]) %
|
2019-12-07 01:39:08 +01:00
|
|
|
KR_MODULUS) > KR_MODULUS) { /* negative value when signed */
|
|
|
|
|
h_string += KR_MODULUS;
|
|
|
|
|
}
|
|
|
|
|
++p_string; /* step to next starting point */
|
|
|
|
|
|
|
|
|
|
/* Compare at current starting point. If hashes match,
|
|
|
|
|
* do full compare */
|
|
|
|
|
if (h_pattern == h_string &&
|
|
|
|
|
memcmp(p_pattern, p_string, n_char_pattern) == 0) {
|
|
|
|
|
*pp_string = p_string; /* Update string location */
|
|
|
|
|
*p_h_string = h_string; /* and hash for another call */
|
|
|
|
|
return p_string; /* match here */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Exit with no match if at last starting point */
|
|
|
|
|
if (p_string == p_last) {
|
|
|
|
|
return (char *) NULL; /* no match found */
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over starting points in string */
|
|
|
|
|
} /* end of function next_substr */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function returns TRUE if '\0' is among the n characters at p and
|
|
|
|
|
* FALSE otherwise. */
|
|
|
|
|
static inline bool have_null(size_t n, const char *p)
|
|
|
|
|
{
|
|
|
|
|
/* Scan backwards to make the common case of using a null termination
|
|
|
|
|
* of a string for the null char be faster */
|
|
|
|
|
const char *p_cur = p + n - 1;
|
|
|
|
|
for ( ; p_cur >= p; --p_cur) { /* Locate '\0' among the chars */
|
|
|
|
|
if (*p_cur == '\0') { /* found */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
} /* end of function have_null */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function "finds a needle in a haystack" aka the first occurrence of
|
|
|
|
|
* any character of needle in haystack. NULL is returned if none is found.
|
|
|
|
|
* haystack must be terminated with '\0'.
|
|
|
|
|
*
|
|
|
|
|
* Remarks
|
|
|
|
|
* p_needle does not need to be null terminated. In fact, a null can be
|
|
|
|
|
* included among the characters to be located so that this funtion will
|
|
|
|
|
* locate the end of haystack if none of the other characters is found and
|
|
|
|
|
* would guarantee that the returned value is not NULL.
|
|
|
|
|
*
|
|
|
|
|
* The case of a '\0' included among the chars to locate is treated as a
|
|
|
|
|
* special case for improved efficiency.
|
|
|
|
|
*
|
|
|
|
|
* For a sufficiently large haystack, further gains in performance can be
|
|
|
|
|
* achieved by analyzing the characteristics of the needle values and
|
|
|
|
|
* developing comparisons based on bit values or range values. As a
|
|
|
|
|
* trivial example, for the needle string "01234567", instead of 8
|
|
|
|
|
* comparisons for the 8 values, 2 comparisons can be used by comparing
|
|
|
|
|
* against >= 0 and against <= 7. Without a large enough haystack, the
|
|
|
|
|
* computational time required for the analysis would not be recovered.
|
|
|
|
|
*/
|
|
|
|
|
char *find_first_of(const char *haystack,
|
|
|
|
|
unsigned int n_needle, const char *p_needle)
|
|
|
|
|
{
|
|
|
|
|
/* Hanldle case of nothing to find */
|
|
|
|
|
if (n_needle == 0) {
|
|
|
|
|
return (char *) NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char * const p_needle_end = p_needle + n_needle;
|
|
|
|
|
if (have_null(n_needle, p_needle)) { /* searching for '\0' */
|
|
|
|
|
for ( ; ; ++haystack) { /* iterate over straws in haystack */
|
|
|
|
|
const char straw = *haystack;
|
|
|
|
|
const char *p_needle_cur = p_needle;
|
|
|
|
|
for ( ; p_needle_cur != p_needle_end; ++p_needle_cur) {
|
|
|
|
|
const char needle = *p_needle_cur;
|
|
|
|
|
if (straw == needle) { /* found needle */
|
|
|
|
|
return (char *) haystack;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over needles */
|
|
|
|
|
} /* end of loop over straws in haystack */
|
|
|
|
|
} /* end of case that '\0' among items being located */
|
|
|
|
|
|
|
|
|
|
/* Else '\0' is not among the items being located */
|
|
|
|
|
for ( ; ; ++haystack) { /* iterate over straws in haystack */
|
|
|
|
|
const char straw = *haystack;
|
|
|
|
|
const char *p_needle_cur = p_needle;
|
|
|
|
|
for ( ; p_needle_cur != p_needle_end; ++p_needle_cur) {
|
|
|
|
|
const char needle = *p_needle_cur;
|
|
|
|
|
if (straw == needle) { /* found needle */
|
|
|
|
|
return (char *) haystack;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over needles */
|
|
|
|
|
if (straw == '\0') { /* entire haystack searched */
|
|
|
|
|
return (char *) NULL;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over straws in haystack */
|
|
|
|
|
} /* end of function find_first_of */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function returns TRUE if the string has any of the characters
|
|
|
|
|
* '"', '\'' or '\\' */
|
|
|
|
|
bool has_escape_or_quote(size_t n, const char *str)
|
|
|
|
|
{
|
|
|
|
|
const char *str_end = str + n;
|
|
|
|
|
for ( ; str != str_end; ++str) {
|
|
|
|
|
const char ch_cur = *str;
|
|
|
|
|
if (ch_cur == '"' || ch_cur == '\'' || ch_cur == '\\') {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
} /* end of loop over chars in string */
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
} /* end of function may_have_eq */
|
2019-06-01 19:41:42 +02:00
|
|
|
|
2022-04-25 13:35:23 +02:00
|
|
|
/* Converts integer to string.
|
|
|
|
|
Return the result string.
|
|
|
|
|
Only 10 radix is supported */
|
|
|
|
|
char *itoa10(int n, char s[])
|
|
|
|
|
{
|
|
|
|
|
int i, j, sign;
|
|
|
|
|
char c;
|
|
|
|
|
|
|
|
|
|
if ((sign = n) < 0) /* record sign */
|
|
|
|
|
n = -n; /* make n positive */
|
|
|
|
|
i = 0;
|
|
|
|
|
do { /* generate digits in reverse order */
|
|
|
|
|
s[i++] = n % 10 + '0'; /* get next digit */
|
|
|
|
|
} while ((n /= 10) > 0); /* delete it */
|
|
|
|
|
if (sign < 0)
|
|
|
|
|
s[i++] = '-';
|
|
|
|
|
s[i] = '\0';
|
|
|
|
|
/* revert string */
|
|
|
|
|
for (i = 0, j = (int)strlen(s) - 1; i < j; i++, j--) {
|
|
|
|
|
c = s[i];
|
|
|
|
|
s[i] = s[j];
|
|
|
|
|
s[j] = c;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|