Dynamic string (dstring) rework
This commit is contained in:
parent
9452121954
commit
89efe5aeae
|
|
@ -9,28 +9,28 @@ Patched: 2010/2012 by Bill Swartz (hash table for vectors)
|
|||
* Do a 'diff' of two plots.
|
||||
*/
|
||||
|
||||
#include "ngspice/ngspice.h"
|
||||
#include "ngspice/ftedefs.h"
|
||||
#include "ngspice/dstring.h"
|
||||
#include "ngspice/dvec.h"
|
||||
#include "ngspice/ftedefs.h"
|
||||
#include "ngspice/hash.h"
|
||||
#include "ngspice/ngspice.h"
|
||||
#include "ngspice/sim.h"
|
||||
|
||||
#include "diff.h"
|
||||
#include <ngspice/hash.h>
|
||||
#include <ngspice/dstring.h>
|
||||
#include "variable.h"
|
||||
|
||||
static bool nameeq(const char *n1,const char *n2);
|
||||
static char *canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p,
|
||||
static bool nameeq(const char *n1, const char *n2);
|
||||
static char *canonical_name(const char *name, DSTRINGPTR dbuf_p,
|
||||
bool make_i_name_lower);
|
||||
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p,
|
||||
canonical_name(const char *name, DSTRINGPTR dbuf_p,
|
||||
bool make_i_name_lower)
|
||||
{
|
||||
spice_dstring_reinit(dbuf_p); /* Reset dynamic buffer */
|
||||
ds_clear(dbuf_p); /* Reset dynamic buffer */
|
||||
|
||||
/* "i(some_name)" -> "some_name#branch" */
|
||||
/* "I(some_name)" -> "some_name#branch" */
|
||||
|
|
@ -38,22 +38,28 @@ canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p,
|
|||
static const char sz_branch[] = "#branch";
|
||||
const char *p_start = name + 2;
|
||||
size_t n = strlen(p_start) - 1; /* copy all but final ')' */
|
||||
if (make_i_name_lower) {
|
||||
(void) spice_dstring_append_lower(dbuf_p, p_start, (int) n);
|
||||
ds_case_t case_type = make_i_name_lower ?
|
||||
ds_case_lower : ds_case_as_is;
|
||||
bool f_ok = ds_cat_mem_case(dbuf_p, p_start, (int) n,
|
||||
case_type) == DS_E_OK;
|
||||
f_ok &= ds_cat_mem(dbuf_p, sz_branch,
|
||||
sizeof sz_branch / sizeof *sz_branch - 1) == DS_E_OK;
|
||||
if (!f_ok) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
else {
|
||||
(void) spice_dstring_append(dbuf_p, p_start, (int) n);
|
||||
}
|
||||
return spice_dstring_append(dbuf_p, sz_branch,
|
||||
sizeof sz_branch / sizeof *sz_branch - 1);
|
||||
return ds_get_buf(dbuf_p);
|
||||
}
|
||||
|
||||
/* Convert a name starting with a digit, such as a numbered node to
|
||||
* something like v(33) */
|
||||
if (isdigit_c(*name)) {
|
||||
(void) spice_dstring_append(dbuf_p, "v(", 2);
|
||||
(void) spice_dstring_append(dbuf_p, name, -1);
|
||||
return spice_dstring_append_char(dbuf_p, ')');
|
||||
bool f_ok = ds_cat_mem(dbuf_p, "v(", 2) == DS_E_OK;
|
||||
f_ok &= ds_cat_str(dbuf_p, name) == DS_E_OK;
|
||||
f_ok &= ds_cat_char(dbuf_p, ')') == DS_E_OK;
|
||||
if (!f_ok) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
return ds_get_buf(dbuf_p);
|
||||
}
|
||||
|
||||
/* Finally if neither of the special cases above occur, there is
|
||||
|
|
@ -62,7 +68,10 @@ canonical_name(const char *name, SPICE_DSTRINGPTR dbuf_p,
|
|||
* argument. Making a copy ensures that it can be modified without
|
||||
* changing the original, but in the current use cases that is
|
||||
* not an issue. */
|
||||
return spice_dstring_append(dbuf_p, name, -1);
|
||||
if (ds_cat_str(dbuf_p, name) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
return ds_get_buf(dbuf_p);
|
||||
} /* end of function canonical_name */
|
||||
|
||||
|
||||
|
|
@ -79,19 +88,17 @@ nameeq(const char *n1, const char *n2)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
SPICE_DSTRING ds1, ds2; /* Buffers to build canonical names */
|
||||
|
||||
/* Init the dynamic string buffers */
|
||||
spice_dstring_init(&ds1);
|
||||
spice_dstring_init(&ds2);
|
||||
/* Init the dynamic string buffers to build canonical names */
|
||||
DS_CREATE(ds1, 100);
|
||||
DS_CREATE(ds2, 100);
|
||||
|
||||
/* Compare canonical names */
|
||||
const BOOL rc = (BOOL) cieq(canonical_name(n1, &ds1, FALSE),
|
||||
canonical_name(n2, &ds2, FALSE));
|
||||
|
||||
/* Free the dynamic string buffers */
|
||||
spice_dstring_free(&ds1);
|
||||
spice_dstring_free(&ds2);
|
||||
ds_free(&ds1);
|
||||
ds_free(&ds2);
|
||||
|
||||
return rc;
|
||||
} /* end of function nameeq */
|
||||
|
|
@ -110,7 +117,6 @@ com_diff(wordlist *wl)
|
|||
char *v1_name; /* canonical v1 name */
|
||||
char *v2_name; /* canonical v2 name */
|
||||
NGHASHPTR crossref_p; /* cross reference hash table */
|
||||
SPICE_DSTRING ibuf; /* used to build canonical name */
|
||||
wordlist *tw;
|
||||
char numbuf[BSIZE_SP], numbuf2[BSIZE_SP], numbuf3[BSIZE_SP], numbuf4[BSIZE_SP]; /* For printnum */
|
||||
|
||||
|
|
@ -180,7 +186,7 @@ com_diff(wordlist *wl)
|
|||
for (v1 = p1->pl_dvecs; v1; v1 = v1->v_next)
|
||||
v1->v_link2 = NULL;
|
||||
|
||||
spice_dstring_init(&ibuf);
|
||||
DS_CREATE(ibuf, 100); /* used to build canonical name */
|
||||
crossref_p = nghash_init(NGHASH_MIN_SIZE);
|
||||
nghash_unique(crossref_p, FALSE);
|
||||
|
||||
|
|
@ -208,7 +214,7 @@ com_diff(wordlist *wl)
|
|||
}
|
||||
}
|
||||
|
||||
spice_dstring_free(&ibuf);
|
||||
ds_free(&ibuf);
|
||||
nghash_free(crossref_p, NULL, NULL);
|
||||
|
||||
for (v1 = p1->pl_dvecs; v1; v1 = v1->v_next)
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@
|
|||
#include "ngspice/bool.h"
|
||||
|
||||
|
||||
char *pscopy(SPICE_DSTRINGPTR s, const char *str, const char *stop);
|
||||
void scopyd(SPICE_DSTRINGPTR a, SPICE_DSTRINGPTR b);
|
||||
void scopys(SPICE_DSTRINGPTR a, const char *b);
|
||||
void sadd(SPICE_DSTRINGPTR s, const char *t);
|
||||
void cadd(SPICE_DSTRINGPTR s, char c);
|
||||
void pscopy(DSTRINGPTR s, const char *str, const char *stop);
|
||||
void scopyd(DSTRINGPTR dst, const DSTRINGPTR src);
|
||||
void scopys(DSTRINGPTR a, const char *b);
|
||||
void sadd(DSTRINGPTR s, const char *t);
|
||||
void cadd(DSTRINGPTR s, char c);
|
||||
|
||||
bool alfa(char c);
|
||||
bool alfanum(char c);
|
||||
|
|
|
|||
|
|
@ -71,9 +71,11 @@ yes_or_no(void)
|
|||
* Function: add string t to dynamic string dstr_p.
|
||||
* ----------------------------------------------------------------- */
|
||||
void
|
||||
sadd(SPICE_DSTRINGPTR dstr_p, const char *t)
|
||||
sadd(DSTRINGPTR dstr_p, const char *t)
|
||||
{
|
||||
spice_dstring_append(dstr_p, t, -1);
|
||||
if (ds_cat_str(dstr_p, t) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -81,12 +83,11 @@ sadd(SPICE_DSTRINGPTR dstr_p, const char *t)
|
|||
* Function: add character c to dynamic string dstr_p.
|
||||
* ----------------------------------------------------------------- */
|
||||
void
|
||||
cadd(SPICE_DSTRINGPTR dstr_p, char c)
|
||||
cadd(DSTRINGPTR dstr_p, char c)
|
||||
{
|
||||
char tmp_str[2];
|
||||
tmp_str[0] = c;
|
||||
tmp_str[1] = '\0';
|
||||
spice_dstring_append(dstr_p, tmp_str, -1);
|
||||
if (ds_cat_char(dstr_p, c) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -95,43 +96,43 @@ cadd(SPICE_DSTRINGPTR dstr_p, char c)
|
|||
* terminated.
|
||||
* ----------------------------------------------------------------- */
|
||||
void
|
||||
scopyd(SPICE_DSTRINGPTR s, SPICE_DSTRINGPTR t) /* returns success flag */
|
||||
scopyd(DSTRINGPTR dst, const DSTRINGPTR src) /* returns success flag */
|
||||
{
|
||||
spice_dstring_reinit(s);
|
||||
spice_dstring_append(s, spice_dstring_value(t), -1);
|
||||
(void) ds_clear(dst);
|
||||
if (ds_cat_ds(dst, src) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Create copy of the string in the dynamic string. Dynamic strings
|
||||
* are always NULLterminated.
|
||||
* are always NULL terminated.
|
||||
* ----------------------------------------------------------------- */
|
||||
void
|
||||
scopys(SPICE_DSTRINGPTR s, const char *t) /* returns success flag */
|
||||
scopys(DSTRINGPTR s, const char *t) /* returns success flag */
|
||||
{
|
||||
spice_dstring_reinit(s);
|
||||
spice_dstring_append(s, t, -1);
|
||||
ds_clear(s);
|
||||
if (ds_cat_str(s, t) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
pscopy(SPICE_DSTRINGPTR dstr_p, const char *t, const char *stop)
|
||||
/* Copy until stop char (exclusive) or end of string if none given */
|
||||
void
|
||||
pscopy(DSTRINGPTR dstr_p, const char *t, const char *stop)
|
||||
{
|
||||
int i;
|
||||
char *s_p;
|
||||
|
||||
if (!stop)
|
||||
if (!stop) { /* locate end of string if no stop char given */
|
||||
stop = strchr(t, '\0');
|
||||
}
|
||||
|
||||
s_p = _spice_dstring_setlength(dstr_p, (int)(stop - t));
|
||||
if (ds_cat_mem(dstr_p, t, stop - t) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
|
||||
for (i = 0; t < stop;)
|
||||
s_p[i++] = *t++;
|
||||
|
||||
s_p[i] = '\0';
|
||||
|
||||
return s_p;
|
||||
}
|
||||
return;
|
||||
} /* end of function pscopy */
|
||||
|
||||
|
||||
bool
|
||||
|
|
@ -157,3 +158,6 @@ alfanumps(char c)
|
|||
{
|
||||
return alfa(c) || ((c >= '0') && (c <= '9')) || c == '-';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ static long placeholder = 0;
|
|||
|
||||
|
||||
static void
|
||||
stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol)
|
||||
stripsomespace(DSTRINGPTR dstr_p, bool incontrol)
|
||||
{
|
||||
/* if s starts with one of some markers, strip leading space */
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol)
|
|||
? "*.&+#$"
|
||||
: "*.&+#$" "xX";
|
||||
|
||||
char *s = spice_dstring_value(dstr_p);
|
||||
char *s = ds_get_buf(dstr_p);
|
||||
|
||||
int i = 0;
|
||||
while (s[i] && (s[i] <= ' '))
|
||||
|
|
@ -90,15 +90,13 @@ stripsomespace(SPICE_DSTRINGPTR dstr_p, bool incontrol)
|
|||
|
||||
|
||||
static int
|
||||
stripbraces(SPICE_DSTRINGPTR dstr_p)
|
||||
stripbraces(DSTRINGPTR dstr_p)
|
||||
/* puts the funny placeholders. returns the number of {...} substitutions */
|
||||
{
|
||||
int n = 0;
|
||||
char *s = spice_dstring_value(dstr_p);
|
||||
char *s = ds_get_buf(dstr_p);
|
||||
char *p, *brace;
|
||||
SPICE_DSTRING tstr; /* temporary dynamic string */
|
||||
|
||||
spice_dstring_init(&tstr);
|
||||
DS_CREATE(tstr, 200);
|
||||
p = s;
|
||||
|
||||
while ((brace = strchr(p, '{')) != NULL) {
|
||||
|
|
@ -132,32 +130,30 @@ stripbraces(SPICE_DSTRINGPTR dstr_p)
|
|||
if (*j_ptr >= ' ')
|
||||
cadd(&tstr, ' ');
|
||||
|
||||
int ilen = spice_dstring_length(&tstr);
|
||||
int ilen = (int) ds_get_length(&tstr);
|
||||
sadd(&tstr, j_ptr);
|
||||
scopyd(dstr_p, &tstr);
|
||||
s = spice_dstring_value(dstr_p);
|
||||
s = ds_get_buf(dstr_p);
|
||||
p = s + ilen;
|
||||
}
|
||||
|
||||
dynsubst = placeholder;
|
||||
spice_dstring_free(&tstr);
|
||||
ds_free(&tstr);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
findsubname(dico_t *dico, SPICE_DSTRINGPTR dstr_p)
|
||||
findsubname(dico_t *dico, DSTRINGPTR dstr_p)
|
||||
/* truncate the parameterized subckt call to regular old Spice */
|
||||
/* scan a string from the end, skipping non-idents and {expressions} */
|
||||
/* then truncate s after the last subckt(?) identifier */
|
||||
{
|
||||
char * const s = spice_dstring_value(dstr_p);
|
||||
char *p = s + spice_dstring_length(dstr_p);
|
||||
char * const s = ds_get_buf(dstr_p);
|
||||
char *p = s + ds_get_length(dstr_p);
|
||||
|
||||
SPICE_DSTRING name; /* extract a name */
|
||||
|
||||
spice_dstring_init(&name);
|
||||
DS_CREATE(name, 200); /* extract a name */
|
||||
|
||||
while (p > s) {
|
||||
|
||||
|
|
@ -193,23 +189,23 @@ findsubname(dico_t *dico, SPICE_DSTRINGPTR dstr_p)
|
|||
else
|
||||
for (t = p; alfanum(*t); t++)
|
||||
;
|
||||
spice_dstring_reinit(&name);
|
||||
ds_clear(&name);
|
||||
pscopy(&name, p, t);
|
||||
entry = entrynb(dico, spice_dstring_value(&name));
|
||||
entry = entrynb(dico, ds_get_buf(&name));
|
||||
if (entry && (entry->tp == NUPA_SUBCKT)) {
|
||||
spice_dstring_setlength(dstr_p, (int) (p_end - s));
|
||||
spice_dstring_free(&name);
|
||||
(void) ds_set_length(dstr_p, p_end - s);
|
||||
ds_free(&name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spice_dstring_free(&name);
|
||||
ds_free(&name);
|
||||
}
|
||||
|
||||
|
||||
static char
|
||||
transform(dico_t *dico, SPICE_DSTRINGPTR dstr_p, bool incontrol)
|
||||
transform(dico_t *dico, DSTRINGPTR dstr_p, bool incontrol)
|
||||
/* line s is categorized and crippled down to basic Spice
|
||||
* returns in u control word following dot, if any
|
||||
*
|
||||
|
|
@ -238,7 +234,7 @@ transform(dico_t *dico, SPICE_DSTRINGPTR dstr_p, bool incontrol)
|
|||
char category;
|
||||
stripsomespace(dstr_p, incontrol);
|
||||
|
||||
s = spice_dstring_value(dstr_p);
|
||||
s = ds_get_buf(dstr_p);
|
||||
|
||||
if (s[0] == '.') {
|
||||
/* check PS parameter format */
|
||||
|
|
@ -571,9 +567,8 @@ nupa_copy(struct card *deck)
|
|||
|
||||
char *t;
|
||||
char c, d;
|
||||
SPICE_DSTRING u;
|
||||
|
||||
spice_dstring_init(&u);
|
||||
DS_CREATE(u, 200);
|
||||
|
||||
pscopy(&u, s, s_end); /* strip trailing space, CrLf and so on */
|
||||
dicoS->srcline = linenum;
|
||||
|
|
@ -599,14 +594,14 @@ nupa_copy(struct card *deck)
|
|||
dicoS->dyncategory[linenum] = c;
|
||||
} /* keep a local copy and mangle the string */
|
||||
|
||||
t = copy(spice_dstring_value(&u));
|
||||
t = copy(ds_get_buf(&u));
|
||||
|
||||
if (!t) {
|
||||
fputs("Fatal: String malloc crash in nupa_copy()\n", stderr);
|
||||
controlled_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
spice_dstring_free(&u);
|
||||
ds_free(&u);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -300,7 +300,6 @@ dicostack_pop(dico_t *dico)
|
|||
/* pop operation for nested subcircuit locals */
|
||||
{
|
||||
char *inst_name; /* name of subcircuit instance */
|
||||
char *param_p; /* qualified inst parameter name */
|
||||
entry_t *entry; /* current entry */
|
||||
NGHASHPTR htable_p; /* current hash table */
|
||||
NGHASHITER iter; /* hash iterator - thread safe */
|
||||
|
|
@ -317,22 +316,23 @@ dicostack_pop(dico_t *dico)
|
|||
inst_name = dico->inst_name[dico->stack_depth];
|
||||
htable_p = dico->symbols[dico->stack_depth];
|
||||
if (htable_p) {
|
||||
SPICE_DSTRING param_name; /* build a qualified name */
|
||||
spice_dstring_init(¶m_name);
|
||||
/* build a qualified name */
|
||||
DS_CREATE(param_name, 100);
|
||||
|
||||
NGHASH_FIRST(&iter);
|
||||
for (entry = (entry_t *) nghash_enumerateRE(htable_p, &iter);
|
||||
entry;
|
||||
entry = (entry_t *) nghash_enumerateRE(htable_p, &iter))
|
||||
{
|
||||
spice_dstring_reinit(¶m_name);
|
||||
param_p = spice_dstring_print(¶m_name, "%s.%s",
|
||||
inst_name, entry->symbol);
|
||||
nupa_add_inst_param(param_p, entry->vl);
|
||||
entry;
|
||||
entry = (entry_t *) nghash_enumerateRE(htable_p, &iter)) {
|
||||
ds_clear(¶m_name);
|
||||
if (ds_cat_printf(¶m_name, "%s.%s",
|
||||
inst_name, entry->symbol) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
nupa_add_inst_param(ds_get_buf(¶m_name), entry->vl);
|
||||
dico_free_entry(entry);
|
||||
}
|
||||
nghash_free(htable_p, NULL, NULL);
|
||||
spice_dstring_free(¶m_name);
|
||||
ds_free(¶m_name);
|
||||
}
|
||||
tfree(inst_name);
|
||||
|
||||
|
|
@ -521,11 +521,11 @@ defsubckt(dico_t *dico, struct card *card)
|
|||
s_end++;
|
||||
|
||||
if (s_end > s) {
|
||||
SPICE_DSTRING ustr; /* temp user string */
|
||||
spice_dstring_init(&ustr);
|
||||
DS_CREATE(ustr, 200); /* temp user string */
|
||||
pscopy(&ustr, s, s_end);
|
||||
err = nupa_define(dico, spice_dstring_value(&ustr), ' ', NUPA_SUBCKT, 0.0, w, NULL);
|
||||
spice_dstring_free(&ustr);
|
||||
err = nupa_define(dico, ds_get_buf(&ustr), ' ',
|
||||
NUPA_SUBCKT, 0.0, w, NULL);
|
||||
ds_free(&ustr);
|
||||
} else {
|
||||
err = message(dico, "Subcircuit or Model without name.\n");
|
||||
}
|
||||
|
|
@ -542,14 +542,13 @@ findsubckt(dico_t *dico, const char *s)
|
|||
const char *name_e = skip_back_ws(s + strlen(s), s);
|
||||
const char *name_b = skip_back_non_ws(name_e, s);
|
||||
|
||||
SPICE_DSTRING ustr; /* u= subckt name is last token in string s */
|
||||
entry_t *entry; /* symbol table entry */
|
||||
|
||||
spice_dstring_init(&ustr);
|
||||
DS_CREATE(ustr, 200); /* u= subckt name is last token in string s */
|
||||
|
||||
pscopy(&ustr, name_b, name_e);
|
||||
entry = entrynb(dico, spice_dstring_value(&ustr));
|
||||
spice_dstring_free(&ustr);
|
||||
entry = entrynb(dico, ds_get_buf(&ustr));
|
||||
ds_free(&ustr);
|
||||
|
||||
if (entry && (entry->tp == NUPA_SUBCKT)) {
|
||||
return entry->ivl;
|
||||
|
|
@ -850,10 +849,9 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
char uop[nprece + 1];
|
||||
int i, natom;
|
||||
bool ok;
|
||||
SPICE_DSTRING tstr;
|
||||
const char *s_orig = s;
|
||||
|
||||
spice_dstring_init(&tstr);
|
||||
DS_CREATE(tstr, 200);
|
||||
|
||||
for (i = 0; i <= nprece; i++) {
|
||||
accu[i] = 0.0;
|
||||
|
|
@ -922,17 +920,17 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
u = formula(dico, s, kptr, &error);
|
||||
state = S_atom;
|
||||
if (fu > 0) {
|
||||
if (fu == XFU_TERNARY_FCN)
|
||||
if ((fu == XFU_TERNARY_FCN))
|
||||
u = ternary_fcn(v, w, u);
|
||||
else if (fu == XFU_AGAUSS)
|
||||
else if ((fu == XFU_AGAUSS))
|
||||
u = agauss(v, w, u);
|
||||
else if (fu == XFU_GAUSS)
|
||||
else if ((fu == XFU_GAUSS))
|
||||
u = gauss(v, w, u);
|
||||
else if (fu == XFU_UNIF)
|
||||
else if ((fu == XFU_UNIF))
|
||||
u = unif(v, u);
|
||||
else if (fu == XFU_AUNIF)
|
||||
else if ((fu == XFU_AUNIF))
|
||||
u = aunif(v, u);
|
||||
else if (fu == XFU_LIMIT)
|
||||
else if ((fu == XFU_LIMIT))
|
||||
u = limit(v, u);
|
||||
else
|
||||
u = mathfunction(fu, v, u);
|
||||
|
|
@ -946,9 +944,9 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
if (fu > 0) {
|
||||
state = S_init; /* S_init means: ignore for the moment */
|
||||
} else {
|
||||
spice_dstring_reinit(&tstr);
|
||||
ds_clear(&tstr);
|
||||
pscopy(&tstr, s, s_next);
|
||||
u = fetchnumentry(dico, spice_dstring_value(&tstr), &error);
|
||||
u = fetchnumentry(dico, ds_get_buf(&tstr), &error);
|
||||
state = S_atom;
|
||||
}
|
||||
s = s_next;
|
||||
|
|
@ -1043,7 +1041,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
|
||||
*perror = error;
|
||||
|
||||
spice_dstring_free(&tstr);
|
||||
ds_free(&tstr);
|
||||
|
||||
if (error)
|
||||
return 1.0;
|
||||
|
|
@ -1061,7 +1059,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
*/
|
||||
|
||||
static void
|
||||
double_to_string(SPICE_DSTRINGPTR qstr_p, double value)
|
||||
double_to_string(DSTRINGPTR qstr_p, double value)
|
||||
{
|
||||
char buf[ACT_CHARACTS + 1];
|
||||
if (snprintf(buf, sizeof(buf), "% 25.17e", value) != ACT_CHARACTS) {
|
||||
|
|
@ -1074,12 +1072,12 @@ double_to_string(SPICE_DSTRINGPTR qstr_p, double value)
|
|||
|
||||
/* transform exression in string `t' to result q */
|
||||
static bool
|
||||
evaluate_expr(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char *t, const char * const t_end)
|
||||
evaluate_expr(dico_t *dico, DSTRINGPTR qstr_p, const char *t, const char * const t_end)
|
||||
{
|
||||
bool err = 0;
|
||||
double u;
|
||||
|
||||
spice_dstring_reinit(qstr_p);
|
||||
ds_clear(qstr_p);
|
||||
|
||||
u = formula(dico, t, t_end, &err);
|
||||
if (err)
|
||||
|
|
@ -1094,10 +1092,10 @@ evaluate_expr(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char *t, const char *
|
|||
/********* interface functions for spice3f5 extension ***********/
|
||||
|
||||
static char *
|
||||
insertnumber(dico_t *dico, char * const s, SPICE_DSTRINGPTR ustr_p)
|
||||
insertnumber(dico_t *dico, char * const s, DSTRINGPTR ustr_p)
|
||||
/* insert u in string s in place of the next placeholder number */
|
||||
{
|
||||
const char *u = spice_dstring_value(ustr_p);
|
||||
const char *u = ds_get_buf(ustr_p);
|
||||
|
||||
char buf[ACT_CHARACTS+1];
|
||||
|
||||
|
|
@ -1138,9 +1136,8 @@ nupa_substitute(dico_t *dico, const char *s, char *r)
|
|||
const char * const s_end = s + strlen(s);
|
||||
bool err = 0;
|
||||
|
||||
SPICE_DSTRING qstr; /* temp result dynamic string */
|
||||
DS_CREATE(qstr, 200); /* temp result dynamic string */
|
||||
|
||||
spice_dstring_init(&qstr);
|
||||
|
||||
while (s < s_end) {
|
||||
|
||||
|
|
@ -1171,7 +1168,7 @@ nupa_substitute(dico_t *dico, const char *s, char *r)
|
|||
|
||||
/* exeption made for .meas */
|
||||
if (s + 4 == kptr && strncasecmp(s, "LAST", 4) == 0) {
|
||||
spice_dstring_reinit(&qstr);
|
||||
ds_clear(&qstr);
|
||||
sadd(&qstr, "last");
|
||||
} else {
|
||||
err = evaluate_expr(dico, &qstr, s, kptr);
|
||||
|
|
@ -1188,14 +1185,14 @@ nupa_substitute(dico_t *dico, const char *s, char *r)
|
|||
}
|
||||
|
||||
Lend:
|
||||
spice_dstring_free(&qstr);
|
||||
ds_free(&qstr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
getword(const char *s, SPICE_DSTRINGPTR tstr_p)
|
||||
getword(const char *s, DSTRINGPTR tstr_p)
|
||||
{
|
||||
const char *s_end = s + strlen(s);
|
||||
const char *word;
|
||||
|
|
@ -1207,7 +1204,7 @@ getword(const char *s, SPICE_DSTRINGPTR tstr_p)
|
|||
while (alfa(*s) || isdigit_c(*s))
|
||||
s++;
|
||||
|
||||
spice_dstring_reinit(tstr_p);
|
||||
ds_clear(tstr_p);
|
||||
pscopy(tstr_p, word, s);
|
||||
|
||||
return s;
|
||||
|
|
@ -1215,7 +1212,7 @@ getword(const char *s, SPICE_DSTRINGPTR tstr_p)
|
|||
|
||||
|
||||
static char *
|
||||
getexpress(nupa_type *type, SPICE_DSTRINGPTR tstr_p, const char *s)
|
||||
getexpress(nupa_type *type, DSTRINGPTR tstr_p, const char *s)
|
||||
/* returns expression-like string until next separator
|
||||
Input i=position before expr, output i=just after expr, on separator.
|
||||
returns tpe=='R' if (numeric, 'S' if (string only
|
||||
|
|
@ -1309,11 +1306,8 @@ nupa_assignment(dico_t *dico, const char *s, char mode)
|
|||
double rval = 0.0;
|
||||
char *t_p; /* dstring contents value */
|
||||
|
||||
SPICE_DSTRING tstr; /* temporary dstring */
|
||||
SPICE_DSTRING ustr; /* temporary dstring */
|
||||
|
||||
spice_dstring_init(&tstr);
|
||||
spice_dstring_init(&ustr);
|
||||
DS_CREATE(tstr, 200); /* temporary dstrings */
|
||||
DS_CREATE(ustr, 200);
|
||||
|
||||
while ((p < s_end) && (*p <= ' '))
|
||||
p++;
|
||||
|
|
@ -1325,7 +1319,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode)
|
|||
while (p < s_end) {
|
||||
|
||||
p = getword(p, &tstr);
|
||||
t_p = spice_dstring_value(&tstr);
|
||||
t_p = ds_get_buf(&tstr);
|
||||
if (t_p[0] == '\0') {
|
||||
error = message(dico, " Identifier expected\n");
|
||||
break;
|
||||
|
|
@ -1343,7 +1337,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode)
|
|||
p = getexpress(&dtype, &ustr, p + 1) + 1;
|
||||
|
||||
if (dtype == NUPA_REAL) {
|
||||
const char *tmp = spice_dstring_value(&ustr);
|
||||
const char *tmp = ds_get_buf(&ustr);
|
||||
rval = formula(dico, tmp, tmp + strlen(tmp), &error);
|
||||
if (error) {
|
||||
message(dico,
|
||||
|
|
@ -1355,7 +1349,7 @@ nupa_assignment(dico_t *dico, const char *s, char mode)
|
|||
wval = (int) (p - s);
|
||||
}
|
||||
|
||||
error = nupa_define(dico, spice_dstring_value(&tstr), mode /* was ' ' */ ,
|
||||
error = nupa_define(dico, ds_get_buf(&tstr), mode /* was ' ' */ ,
|
||||
dtype, rval, wval, NULL);
|
||||
if (error)
|
||||
break;
|
||||
|
|
@ -1366,8 +1360,8 @@ nupa_assignment(dico_t *dico, const char *s, char mode)
|
|||
}
|
||||
}
|
||||
|
||||
spice_dstring_free(&tstr);
|
||||
spice_dstring_free(&ustr);
|
||||
ds_free(&tstr);
|
||||
ds_free(&ustr);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
@ -1406,25 +1400,20 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
*/
|
||||
{
|
||||
int n, narg = 0;
|
||||
SPICE_DSTRING subname;
|
||||
SPICE_DSTRING tstr;
|
||||
SPICE_DSTRING ustr;
|
||||
SPICE_DSTRING vstr;
|
||||
SPICE_DSTRING idlist;
|
||||
bool err = 0;
|
||||
|
||||
spice_dstring_init(&subname);
|
||||
spice_dstring_init(&tstr);
|
||||
spice_dstring_init(&ustr);
|
||||
spice_dstring_init(&vstr);
|
||||
spice_dstring_init(&idlist);
|
||||
DS_CREATE(subname, 200);
|
||||
DS_CREATE(tstr, 200);
|
||||
DS_CREATE(ustr, 200);
|
||||
DS_CREATE(vstr, 200);
|
||||
DS_CREATE(idlist, 200);
|
||||
|
||||
/***** first, analyze the subckt definition line */
|
||||
n = 0; /* number of parameters if any */
|
||||
n = 0; /* number of parameters if any */
|
||||
|
||||
scopys(&tstr, s);
|
||||
|
||||
const char *j2 = strstr(spice_dstring_value(&tstr), "subckt");
|
||||
const char *j2 = strstr(ds_get_buf(&tstr), "subckt");
|
||||
if (j2) {
|
||||
j2 = skip_ws(j2 + 6); /* skip subckt and whitespace */
|
||||
pscopy(&subname, j2, skip_non_ws(j2));
|
||||
|
|
@ -1432,7 +1421,7 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
err = message(dico, " ! a subckt line!\n");
|
||||
}
|
||||
|
||||
const char *i2 = strstr(spice_dstring_value(&tstr), "params:");
|
||||
const char *i2 = strstr(ds_get_buf(&tstr), "params:");
|
||||
|
||||
if (i2) {
|
||||
const char *optr, *jptr;
|
||||
|
|
@ -1441,9 +1430,9 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
|
||||
/* search identifier to the left of '=' assignments */
|
||||
|
||||
for (optr = spice_dstring_value(&tstr);
|
||||
(jptr = strchr(optr, '=')) != NULL;
|
||||
optr = jptr + 1)
|
||||
for (optr = ds_get_buf(&tstr);
|
||||
(jptr = strchr(optr, '=')) != NULL;
|
||||
optr = jptr + 1)
|
||||
{
|
||||
const char *kptr, *hptr;
|
||||
|
||||
|
|
@ -1486,22 +1475,20 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
*/
|
||||
scopys(&tstr, skip_non_ws(x));
|
||||
|
||||
char * const t_p = spice_dstring_value(&tstr);
|
||||
char * const t_p = ds_get_buf(&tstr);
|
||||
char *jp = NULL;
|
||||
|
||||
/* search for the last occurence of `subname' in the given line */
|
||||
for (;;) {
|
||||
char *next_p =
|
||||
search_isolated_identifier(jp ? jp + 1 : t_p,
|
||||
spice_dstring_value(&subname));
|
||||
char *next_p = search_isolated_identifier(jp ? jp + 1 : t_p,
|
||||
ds_get_buf(&subname));
|
||||
if (!next_p)
|
||||
break;
|
||||
jp = next_p;
|
||||
}
|
||||
|
||||
if (jp) {
|
||||
|
||||
jp += spice_dstring_length(&subname);
|
||||
jp += ds_get_length(&subname);
|
||||
|
||||
while (isspace_c(*jp) || (*jp == ','))
|
||||
jp++;
|
||||
|
|
@ -1512,7 +1499,7 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
|
||||
/* try to fetch valid arguments */
|
||||
char *kp = jp;
|
||||
spice_dstring_reinit(&ustr);
|
||||
ds_clear(&ustr);
|
||||
|
||||
if (alfanum(*kp) || *kp == '.') {
|
||||
/* number, identifier */
|
||||
|
|
@ -1526,9 +1513,9 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
message(dico, "Subckt call, symbol %c not understood\n", *kp);
|
||||
}
|
||||
|
||||
char * const u_p = spice_dstring_value(&ustr);
|
||||
char * const u_p = ds_get_buf(&ustr);
|
||||
if (*u_p) {
|
||||
char * const idlist_p = spice_dstring_value(&idlist);
|
||||
char * const idlist_p = ds_get_buf(&idlist);
|
||||
char *dollar = strchr(idlist_p, '$');
|
||||
if (dollar) {
|
||||
/* replace dollar with expression string u */
|
||||
|
|
@ -1552,17 +1539,17 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x,
|
|||
err = message(dico,
|
||||
" Mismatch: %d formal but %d actual params.\n"
|
||||
"%s\n",
|
||||
n, narg, spice_dstring_value(&idlist));
|
||||
n, narg, ds_get_buf(&idlist));
|
||||
/* ;} else { debugwarn(dico, idlist) */
|
||||
}
|
||||
|
||||
err = nupa_assignment(dico, spice_dstring_value(&idlist), 'N');
|
||||
err = nupa_assignment(dico, ds_get_buf(&idlist), 'N');
|
||||
|
||||
spice_dstring_free(&subname);
|
||||
spice_dstring_free(&tstr);
|
||||
spice_dstring_free(&ustr);
|
||||
spice_dstring_free(&vstr);
|
||||
spice_dstring_free(&idlist);
|
||||
ds_free(&subname);
|
||||
ds_free(&tstr);
|
||||
ds_free(&ustr);
|
||||
ds_free(&vstr);
|
||||
ds_free(&idlist);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,41 +33,45 @@ struct dvec *EVTfindvec(char *node);
|
|||
static void
|
||||
vec_rebuild_lookup_table(struct plot *pl)
|
||||
{
|
||||
int cnt; /* count entries */
|
||||
struct dvec *d; /* dynamic vector */
|
||||
NGHASHPTR lookup_p; /* lookup table for speed */
|
||||
SPICE_DSTRING dbuf; /* dynamic buffer */
|
||||
char *lower_name; /* lower case name */
|
||||
|
||||
if (pl->pl_lookup_table) {
|
||||
if (pl->pl_lookup_table) { /* existing table */
|
||||
nghash_empty(pl->pl_lookup_table, NULL, NULL);
|
||||
} else {
|
||||
cnt = 0;
|
||||
for (d = pl->pl_dvecs; d; d = d->v_next)
|
||||
}
|
||||
else { /* new table */
|
||||
int cnt = 0; /* count entries */
|
||||
struct dvec *d; /* dynamic vector */
|
||||
for (d = pl->pl_dvecs; d; d = d->v_next) { /* get # vec */
|
||||
cnt++;
|
||||
}
|
||||
pl->pl_lookup_table = nghash_init(cnt);
|
||||
/* allow multiple entries */
|
||||
nghash_unique(pl->pl_lookup_table, FALSE);
|
||||
}
|
||||
lookup_p = pl->pl_lookup_table;
|
||||
spice_dstring_init(&dbuf);
|
||||
for (d = pl->pl_dvecs; d; d = d->v_next) {
|
||||
spice_dstring_reinit(&dbuf);
|
||||
lower_name = spice_dstring_append_lower(&dbuf, d->v_name, -1);
|
||||
nghash_insert(lookup_p, lower_name, d);
|
||||
|
||||
{
|
||||
/* Access lookup table directly for speed */
|
||||
NGHASHPTR lookup_p = pl->pl_lookup_table;
|
||||
DS_CREATE(dbuf, 200); /* make dynamic buffer */
|
||||
struct dvec *d; /* dynamic vector */
|
||||
for (d = pl->pl_dvecs; d; d = d->v_next) {
|
||||
ds_clear(&dbuf);
|
||||
if (ds_cat_str_case(&dbuf, d->v_name, ds_case_lower) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
char * const lower_name = ds_get_buf(&dbuf);
|
||||
nghash_insert(lookup_p, lower_name, d); /* add lower-cased name */
|
||||
} /* end of loop over vectors */
|
||||
ds_free(&dbuf);
|
||||
}
|
||||
spice_dstring_free(&dbuf);
|
||||
|
||||
pl->pl_lookup_valid = TRUE;
|
||||
}
|
||||
} /* end of function vec_rebuild_lookup_table */
|
||||
|
||||
|
||||
|
||||
/* Find a named vector in a plot. We are careful to copy the vector if
|
||||
* v_link2 is set, because otherwise we will get screwed up. */
|
||||
static struct dvec *
|
||||
findvec(char *word, struct plot *pl) {
|
||||
SPICE_DSTRING dbuf; /* dynamic buffer */
|
||||
char *lower_name; /* lower case name */
|
||||
char *node_name;
|
||||
struct dvec *d, *newv = NULL, *end = NULL, *v;
|
||||
|
||||
if (pl == NULL)
|
||||
|
|
@ -152,8 +156,11 @@ findvec(char *word, struct plot *pl) {
|
|||
if (!pl->pl_lookup_valid)
|
||||
vec_rebuild_lookup_table(pl);
|
||||
|
||||
spice_dstring_init(&dbuf);
|
||||
lower_name = spice_dstring_append_lower(&dbuf, word, -1);
|
||||
DS_CREATE(dbuf, 200); /* make dynamic buffer */
|
||||
if (ds_cat_str_case(&dbuf, word, ds_case_lower) != DS_E_OK) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
char * const lower_name = ds_get_buf(&dbuf);
|
||||
|
||||
for (d = nghash_find(pl->pl_lookup_table, lower_name);
|
||||
d;
|
||||
|
|
@ -163,11 +170,15 @@ findvec(char *word, struct plot *pl) {
|
|||
}
|
||||
|
||||
if (!d) {
|
||||
spice_dstring_reinit(&dbuf);
|
||||
spice_dstring_append(&dbuf, "v(", -1);
|
||||
spice_dstring_append_lower(&dbuf, word, -1);
|
||||
node_name = spice_dstring_append_char(&dbuf, ')');
|
||||
|
||||
ds_clear(&dbuf);
|
||||
bool f_ok = ds_cat_str(&dbuf, "v(") == DS_E_OK;
|
||||
f_ok &= ds_cat_str_case(&dbuf, word,
|
||||
ds_case_lower) == DS_E_OK;
|
||||
f_ok &= ds_cat_char(&dbuf, ')') == DS_E_OK;
|
||||
if (!f_ok) {
|
||||
controlled_exit(-1);
|
||||
}
|
||||
char * const node_name = ds_get_buf(&dbuf);
|
||||
for (d = nghash_find(pl->pl_lookup_table, node_name);
|
||||
d;
|
||||
d = nghash_find_again(pl->pl_lookup_table, node_name)) {
|
||||
|
|
@ -176,7 +187,7 @@ findvec(char *word, struct plot *pl) {
|
|||
}
|
||||
}
|
||||
|
||||
spice_dstring_free(&dbuf);
|
||||
ds_free(&dbuf);
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - begin - Add processing for getting event-driven vector */
|
||||
|
|
@ -191,8 +202,9 @@ findvec(char *word, struct plot *pl) {
|
|||
vec_new(d);
|
||||
}
|
||||
|
||||
return (d);
|
||||
}
|
||||
return d;
|
||||
} /* end of function findvec */
|
||||
|
||||
|
||||
|
||||
/* If there are imbedded numeric strings, compare them numerically, not
|
||||
|
|
|
|||
|
|
@ -1,40 +1,250 @@
|
|||
/* dstring.h */
|
||||
|
||||
#ifndef ngspice_DSTRING_H
|
||||
#define ngspice_DSTRING_H
|
||||
#ifndef DSTRING_H
|
||||
#define DSTRING_H
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* This structure is modified from Tcl. We do this to avoid a
|
||||
* conflict and later add a conditional compile to just use the Tcl
|
||||
* code if desired.
|
||||
----------------------------------------------------------------- */
|
||||
#define SPICE_DSTRING_STATIC_SIZE 200
|
||||
typedef struct spice_dstring {
|
||||
char *string; /* Points to beginning of string: either
|
||||
* staticSpace below or a malloced array. */
|
||||
int length ; /* Number of characters in the string excluding the
|
||||
* terminating NULL. */
|
||||
int spaceAvl ; /* Total number of bytes available for the
|
||||
* string and its terminating NULL char. */
|
||||
char staticSpace[SPICE_DSTRING_STATIC_SIZE] ;
|
||||
/* Space to use in common case where string
|
||||
* is small. */
|
||||
} SPICE_DSTRING, *SPICE_DSTRINGPTR ;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* spice_dstring_xxxx routines. Used to manipulate dynamic strings.
|
||||
----------------------------------------------------------------- */
|
||||
extern void spice_dstring_init(SPICE_DSTRINGPTR dsPtr) ;
|
||||
extern char *spice_dstring_append(SPICE_DSTRINGPTR dsPtr,const char *string,int length) ;
|
||||
extern char *spice_dstring_append_lower(SPICE_DSTRINGPTR dsPtr,const char *string,int length) ;
|
||||
extern char *spice_dstring_append_char(SPICE_DSTRINGPTR dsPtr,char c) ;
|
||||
extern char *spice_dstring_print(SPICE_DSTRINGPTR dsPtr,const char *format, ... ) ;
|
||||
extern char *spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) ;
|
||||
extern char *_spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length) ;
|
||||
extern void spice_dstring_free(SPICE_DSTRINGPTR dsPtr) ;
|
||||
#define spice_dstring_reinit(x_xz) spice_dstring_setlength(x_xz,0) ;
|
||||
#define spice_dstring_value(x_xz) ((x_xz)->string)
|
||||
#define spice_dstring_space(x_xz) ((x_xz)->spaceAvl)
|
||||
#define spice_dstring_length(x_xz) ((x_xz)->length)
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#endif
|
||||
/* Error codes */
|
||||
#define DS_E_OK 0
|
||||
#define DS_E_INVALID (-1)
|
||||
#define DS_E_NO_MEMORY (-2)
|
||||
|
||||
/* Macros to create and initialize the most common type of dstring, which is
|
||||
* one that uses the stack for the initial buffer and begins empty.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* DS_CREATE(ds1, 50); // Creates dstring ds1 backed by 50 bytes of stack
|
||||
* // memory and initialized to "".
|
||||
* Note that each DS_CREATE macro must be on a separate line due to the use
|
||||
* of the __LINE__ macro. Using __COUNTER__ in its place would resolve this
|
||||
* issue, but __COUNTER__ is not part of the ANSI standard.
|
||||
*/
|
||||
#undef DS_CONCAT
|
||||
#undef DS_CONCAT2
|
||||
#define DS_CONCAT2(a, b) a##b
|
||||
#define DS_CONCAT(a, b) DS_CONCAT2(a, b)
|
||||
#define DS_CREATE(ds_name, n) \
|
||||
char DS_CONCAT(ds_buf___, __LINE__)[n]; \
|
||||
DSTRING ds_name; \
|
||||
ds_init(&ds_name, DS_CONCAT(ds_buf___, __LINE__), 0,\
|
||||
sizeof DS_CONCAT(ds_buf___, __LINE__), ds_buf_type_stack)
|
||||
|
||||
|
||||
/* Structure for maintaining a dynamic string */
|
||||
typedef struct Dstring {
|
||||
char *p_buf; /* Active data buffer */
|
||||
size_t length; /* Number of characters in the string excluding the
|
||||
* terminating NULL. */
|
||||
size_t n_byte_alloc; /* Allocated size of current buffer */
|
||||
char *p_stack_buf; /* address of stack-based buffer backing dstring
|
||||
* or NULL if none */
|
||||
size_t n_byte_stack_buf; /* size of stack_buffer or 0 if none */
|
||||
} DSTRING, *DSTRINGPTR;
|
||||
|
||||
|
||||
/* Enumeration defining buffer types used during initialization */
|
||||
typedef enum ds_buf_type {
|
||||
ds_buf_type_stack, /* Buffer allocated from stack */
|
||||
ds_buf_type_heap /* Buffer allocated from heap */
|
||||
} ds_buf_type_t;
|
||||
|
||||
/* Enumeration defining case conversion */
|
||||
typedef enum ds_case {
|
||||
ds_case_as_is, /* Leave characters as they are */
|
||||
ds_case_lower, /* Losercase chars */
|
||||
ds_case_upper /* Uppercase chars */
|
||||
} ds_case_t;
|
||||
|
||||
|
||||
|
||||
/* General initialization */
|
||||
int ds_init(DSTRING *p_ds, char *p_buf, size_t length_string,
|
||||
size_t n_byte_buf, ds_buf_type_t type_buffer);
|
||||
|
||||
/* Free all memory used */
|
||||
void ds_free(DSTRING *p_ds);
|
||||
|
||||
|
||||
/* Concatenate string */
|
||||
int ds_cat_str_case(DSTRING *p_ds, const char *sz, ds_case_t case_type);
|
||||
inline int ds_cat_str(DSTRING *p_ds, const char *sz)
|
||||
{
|
||||
return ds_cat_str_case(p_ds, sz, ds_case_as_is);
|
||||
} /* end of function ds_cat_str */
|
||||
|
||||
|
||||
|
||||
/* Concatenate character */
|
||||
int ds_cat_char_case(DSTRING *p_ds, char c, ds_case_t case_type);
|
||||
inline int ds_cat_char(DSTRING *p_ds, char c)
|
||||
{
|
||||
return ds_cat_char_case(p_ds, c, ds_case_as_is);
|
||||
} /* end of function ds_cat_char */
|
||||
|
||||
|
||||
|
||||
/* Concatenate another dstring */
|
||||
int ds_cat_ds_case(DSTRING *p_ds_dst, const DSTRING *p_ds_src,
|
||||
ds_case_t case_type);
|
||||
inline int ds_cat_ds(DSTRING *p_ds_dst, const DSTRING *p_ds_src)
|
||||
{
|
||||
return ds_cat_ds_case(p_ds_dst, p_ds_src, ds_case_as_is);
|
||||
} /* end of function ds_cat_ds */
|
||||
|
||||
|
||||
|
||||
/* General concatenation of a memory buffer */
|
||||
int ds_cat_mem_case(DSTRING *p_ds, const char *p_src, size_t n_char,
|
||||
ds_case_t type_case);
|
||||
inline int ds_cat_mem(DSTRING *p_ds, const char *p_src, size_t n_char)
|
||||
{
|
||||
return ds_cat_mem_case(p_ds, p_src, n_char, ds_case_as_is);
|
||||
} /* end of function ds_cat_mem */
|
||||
|
||||
|
||||
|
||||
/* Ensure minimum internal buffer size */
|
||||
int ds_reserve(DSTRING *p_ds, size_t n_byte_alloc_min);
|
||||
|
||||
/* Concatenate the result of a printf-style format */
|
||||
int ds_cat_printf(DSTRING *p_ds, const char *sz_fmt, ...);
|
||||
|
||||
/* Concatenate the result of a printf-style format using va_list */
|
||||
int ds_cat_vprintf(DSTRING *p_ds, const char *sz_fmt, va_list p_arg);
|
||||
|
||||
/* Reallocate/free to eliminate unused buffer space */
|
||||
int ds_compact(DSTRING *p_ds);
|
||||
|
||||
|
||||
|
||||
/* This function sets the length of the buffer to some size less than
|
||||
* the current allocated size
|
||||
*
|
||||
* Return codes
|
||||
* DS_E_OK: length set OK
|
||||
* DS_E_INVALID: length to large for current allocation
|
||||
*/
|
||||
inline int ds_set_length(DSTRING *p_ds, size_t length)
|
||||
{
|
||||
if (length >= p_ds->n_byte_alloc) {
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
p_ds->length = length;
|
||||
p_ds->p_buf[p_ds->length] = '\0';
|
||||
return DS_E_OK;
|
||||
} /* end of function ds_set_length */
|
||||
|
||||
|
||||
|
||||
/* Sets the length of the data in the buffer to 0. It is equivalent to
|
||||
* ds_set_length(p_ds, 0), except that the check for a valid length can
|
||||
* be skipped since 0 is always valid. */
|
||||
inline void ds_clear(DSTRING *p_ds)
|
||||
{
|
||||
p_ds->length = 0;
|
||||
p_ds->p_buf[0] = '\0';
|
||||
} /* end of function ds_clear */
|
||||
|
||||
|
||||
|
||||
/* This function, if successful, returns an allocated buffer with the
|
||||
* string to the caller and frees any other resources used by the DSTRING.
|
||||
* If the buffer is not allocated and the DS_FREE_MOVE_OPT_FORCE_ALLOC
|
||||
* option is not selected, NULL is returned.
|
||||
*
|
||||
* Parameters
|
||||
* p_ds: Address of DSTRING to free
|
||||
* opt: Bitwise options
|
||||
* DS_FREE_MOVE_OPT_FORCE_ALLOC -- Force allocation in all cases.
|
||||
* If fails, the DSTRING is unchanged.
|
||||
* DS_FREE_MOVE_OPT_COMPACT -- Resize allocation to minimum size
|
||||
* if one already exists.
|
||||
*
|
||||
* Return values
|
||||
* The data string is returned as an allocation to be freed by the caller
|
||||
* NULL is returned if either the allocation was stack-based and
|
||||
* DS_FREE_MOVE_OPT_FORCE_ALLOC was not selected or if
|
||||
* DS_FREE_MOVE_OPT_COMPACT or DS_FREE_MOVE_OPT_FORCE_ALLOC
|
||||
* options were given and there was an allocation failure.
|
||||
* In any case when NULL is returned, the DSTRING is unchanged
|
||||
* on return.
|
||||
*
|
||||
* Remarks
|
||||
* To force freeing of resources if this function fails, either it can
|
||||
* be called again with no options or equivalently ds_free() can be used.
|
||||
*/
|
||||
#define DS_FREE_MOVE_OPT_FORCE_ALLOC 1
|
||||
#define DS_FREE_MOVE_OPT_COMPACT 2
|
||||
inline char *ds_free_move(DSTRING *p_ds, unsigned int opt)
|
||||
{
|
||||
char * const p_buf_active = p_ds->p_buf;
|
||||
|
||||
/* If the buffer is from the stack, allocate if requested. Note that the
|
||||
* compaction option is meaningless in this case since it is allocated
|
||||
* to the minimum size required */
|
||||
if (p_buf_active == p_ds->p_stack_buf) { /* not allocated */
|
||||
if (opt & DS_FREE_MOVE_OPT_FORCE_ALLOC) {
|
||||
/* Allocate to minimum size */
|
||||
size_t n_byte_alloc = p_ds->length + 1;
|
||||
char * const p_ret = (char *) malloc(n_byte_alloc);
|
||||
if (p_ret == (char *) NULL) {
|
||||
return (char *) NULL;
|
||||
}
|
||||
return memcpy(p_ret, p_buf_active, n_byte_alloc);
|
||||
}
|
||||
return (char *) NULL;
|
||||
}
|
||||
/* Else allocated */
|
||||
if (opt & DS_FREE_MOVE_OPT_COMPACT) {
|
||||
/* Allocate to minimum size */
|
||||
size_t n_byte_alloc = p_ds->length + 1;
|
||||
char * const p_ret = (char *) realloc(p_buf_active, n_byte_alloc);
|
||||
if (p_ret == (char *) NULL) {
|
||||
/* Realloc to smaller size somehow failed! */
|
||||
return (char *) NULL;
|
||||
}
|
||||
return p_ret; /* Return resized allocation */
|
||||
}
|
||||
return p_buf_active; /* Return unchanged */
|
||||
} /* end of function ds_free_move */
|
||||
|
||||
|
||||
|
||||
/* Returns the address of the buffer. The caller should never attempt
|
||||
* to free the buffer. With care (not changing the length), it can
|
||||
* be modified. */
|
||||
inline char *ds_get_buf(DSTRING *p_ds)
|
||||
{
|
||||
return p_ds->p_buf;
|
||||
} /* end of function ds_get_buffer */
|
||||
|
||||
|
||||
|
||||
/* Returns the current dstring length */
|
||||
inline size_t ds_get_length(const DSTRING *p_ds)
|
||||
{
|
||||
return p_ds->length;
|
||||
} /* end of function ds_get_length */
|
||||
|
||||
|
||||
|
||||
/* Returns the allocated dstring buffer size */
|
||||
inline size_t ds_get_buf_size(const DSTRING *p_ds)
|
||||
{
|
||||
return p_ds->n_byte_alloc;
|
||||
} /* end of function ds_get_buf_size */
|
||||
|
||||
|
||||
|
||||
#ifdef DSTRING_UNIT_TEST
|
||||
#include <stdio.h>
|
||||
int ds_test(FILE *fp);
|
||||
#endif /* UNIT_TEST_DSTRING */
|
||||
|
||||
#endif /* include guard */
|
||||
|
|
|
|||
|
|
@ -1,409 +1,539 @@
|
|||
/* -----------------------------------------------------------------
|
||||
FILE: dstring.c
|
||||
FILE: dstring.c
|
||||
DESCRIPTION:This file contains the routines for manipulating dynamic strings.
|
||||
----------------------------------------------------------------- */
|
||||
#include "ngspice/ngspice.h"
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ngspice/dstring.h"
|
||||
|
||||
/* definitions local to this file only */
|
||||
|
||||
/* ********************** TYPE DEFINITIONS ************************* */
|
||||
static int ds_reserve_internal(DSTRING *p_ds,
|
||||
size_t n_byte_alloc_opt, size_t n_byte_alloc_min);
|
||||
|
||||
/* ********************** STATIC DEFINITIONS ************************* */
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
/* Instantiations of dstring functions in case inlining is not performed */
|
||||
int ds_cat_str(DSTRING *p_ds, const char *sz);
|
||||
int ds_cat_char(DSTRING *p_ds, char c);
|
||||
int ds_cat_ds(DSTRING *p_ds_dst, const DSTRING *p_ds_src);
|
||||
int ds_cat_mem(DSTRING *p_ds, const char *p_src, size_t n_char);
|
||||
int ds_set_length(DSTRING *p_ds, size_t length);
|
||||
void ds_clear(DSTRING *p_ds);
|
||||
char *ds_free_move(DSTRING *p_ds, unsigned int opt);
|
||||
char *ds_get_buf(DSTRING *p_ds);
|
||||
size_t ds_get_length(const DSTRING *p_ds);
|
||||
size_t ds_get_buf_size(const DSTRING *p_ds);
|
||||
|
||||
|
||||
/* This function initalizes a dstring using *p_buf as the initial backing
|
||||
*
|
||||
* spice_dstring_init --
|
||||
* Parameters
|
||||
* p_buf: Inital buffer backing the dstring
|
||||
* length_string: Length of string in the initial buffer
|
||||
* n_byte_data: Length of initial buffer. Must be at least 1
|
||||
* type_buffer: Type of buffer providing initial backing
|
||||
*
|
||||
* Initializes a dynamic string, discarding any previous contents
|
||||
* of the string (spice_dstring_free should have been called already
|
||||
* if the dynamic string was previously in use).
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* The dynamic string is initialized to be empty.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* Return codes
|
||||
* DS_E_OK: Init OK
|
||||
* DS_E_INVALID: n_byte_data = 0 length_string too long,
|
||||
* or unknown buffer type
|
||||
*/
|
||||
|
||||
void spice_dstring_init(SPICE_DSTRINGPTR dsPtr)
|
||||
int ds_init(DSTRING *p_ds, char *p_buf, size_t length_string,
|
||||
size_t n_byte_buf, ds_buf_type_t type_buffer)
|
||||
{
|
||||
dsPtr->string = dsPtr->staticSpace ;
|
||||
dsPtr->length = 0 ;
|
||||
dsPtr->spaceAvl = SPICE_DSTRING_STATIC_SIZE ;
|
||||
dsPtr->staticSpace[0] = '\0';
|
||||
} /* end spice_dstring_init() */
|
||||
/* Validate buffer size */
|
||||
if (n_byte_buf == 0) {
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* spice_dstring_append --
|
||||
*
|
||||
* Append more characters to the current value of a dynamic string.
|
||||
*
|
||||
* Results:
|
||||
* The return value is a pointer to the dynamic string's new value.
|
||||
*
|
||||
* Side effects:
|
||||
* Length bytes from string (or all of string if length is less
|
||||
* than zero) are added to the current value of the string. Memory
|
||||
* gets reallocated if needed to accomodate the string's new size.
|
||||
*
|
||||
* Notes: char *string; String to append. If length is -1 then
|
||||
* this must be null-terminated.
|
||||
* INT length; Number of characters from string to append.
|
||||
* If < 0, then append all of string, up to null at end.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
char *spice_dstring_append(SPICE_DSTRINGPTR dsPtr, const char *string, int length)
|
||||
/* Set current buffer */
|
||||
p_ds->p_buf = p_buf;
|
||||
|
||||
/* Set size of current string >= rather than > because this function
|
||||
* adds a terminating null */
|
||||
if (length_string >= n_byte_buf) {
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
|
||||
p_ds->n_byte_alloc = n_byte_buf;
|
||||
p_ds->length = length_string;
|
||||
p_ds->p_buf[length_string] = '\0';
|
||||
|
||||
/* Set stack buffer */
|
||||
if (type_buffer == ds_buf_type_stack) {
|
||||
p_ds->p_stack_buf = p_buf;
|
||||
p_ds->n_byte_stack_buf = n_byte_buf;
|
||||
}
|
||||
else if (type_buffer == ds_buf_type_heap) {
|
||||
p_ds->p_stack_buf = (char *) NULL;
|
||||
p_ds->n_byte_stack_buf = 0;
|
||||
}
|
||||
else { /* unknown buffer type */
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
|
||||
return DS_E_OK;
|
||||
} /* end of function ds_init */
|
||||
|
||||
|
||||
|
||||
/* This function frees all memory used by the dstring. After calling this
|
||||
* function, the dstring should not be used again. */
|
||||
void ds_free(DSTRING *p_ds)
|
||||
{
|
||||
int newSize ; /* needed size */
|
||||
char *newString ; /* newly allocated string buffer */
|
||||
char *dst ; /* destination */
|
||||
const char *end ; /* end of string */
|
||||
|
||||
if( length < 0){
|
||||
length = (int) strlen(string) ;
|
||||
if (p_ds->p_buf != p_ds->p_stack_buf) {
|
||||
free((void *) p_ds->p_buf);
|
||||
}
|
||||
newSize = length + dsPtr->length ;
|
||||
} /* end of function ds_free */
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Allocate a larger buffer for the string if the current one isn't
|
||||
* large enough. Allocate extra space in the new buffer so that there
|
||||
* will be room to grow before we have to allocate again.
|
||||
----------------------------------------------------------------- */
|
||||
if (newSize >= dsPtr->spaceAvl) {
|
||||
dsPtr->spaceAvl = 2 * newSize ;
|
||||
newString = TMALLOC(char, dsPtr->spaceAvl) ;
|
||||
memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ;
|
||||
if (dsPtr->string != dsPtr->staticSpace) {
|
||||
txfree(dsPtr->string) ;
|
||||
}
|
||||
dsPtr->string = newString;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Copy the new string into the buffer at the end of the old
|
||||
* one.
|
||||
----------------------------------------------------------------- */
|
||||
for( dst = dsPtr->string + dsPtr->length, end = string+length;
|
||||
string < end; string++, dst++) {
|
||||
*dst = *string ;
|
||||
}
|
||||
*dst = '\0' ;
|
||||
dsPtr->length += length ;
|
||||
|
||||
return(dsPtr->string) ;
|
||||
|
||||
} /* end spice_dstring_append() */
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* spice_dstring_append_lower --
|
||||
*
|
||||
* Append more characters converted to lower case to the current
|
||||
* value of a dynamic string.
|
||||
*
|
||||
* Results:
|
||||
* The return value is a pointer to the dynamic string's new value.
|
||||
*
|
||||
* Side effects:
|
||||
* Length bytes from string (or all of string if length is less
|
||||
* than zero) are added to the current value of the string. Memory
|
||||
* gets reallocated if needed to accomodate the string's new size.
|
||||
*
|
||||
* Notes: char *string; String to append. If length is -1 then
|
||||
* this must be null-terminated.
|
||||
* INT length; Number of characters from string to append.
|
||||
* If < 0, then append all of string, up to null at end.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
char *spice_dstring_append_lower(SPICE_DSTRINGPTR dsPtr, const char *string, int length)
|
||||
/* Concatenate string */
|
||||
int ds_cat_str_case(DSTRING *p_ds, const char *sz, ds_case_t case_type)
|
||||
{
|
||||
int newSize ; /* needed size */
|
||||
char *newString ; /* newly allocated string buffer */
|
||||
char *dst ; /* destination */
|
||||
const char *end ; /* end of string */
|
||||
return ds_cat_mem_case(p_ds, sz, strlen(sz), case_type);
|
||||
} /* end of function ds_cat_str_case */
|
||||
|
||||
if( length < 0){
|
||||
length = (int) strlen(string) ;
|
||||
}
|
||||
newSize = length + dsPtr->length ;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Allocate a larger buffer for the string if the current one isn't
|
||||
* large enough. Allocate extra space in the new buffer so that there
|
||||
* will be room to grow before we have to allocate again.
|
||||
----------------------------------------------------------------- */
|
||||
if (newSize >= dsPtr->spaceAvl) {
|
||||
dsPtr->spaceAvl = 2 * newSize ;
|
||||
newString = TMALLOC(char, dsPtr->spaceAvl) ;
|
||||
memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ;
|
||||
if (dsPtr->string != dsPtr->staticSpace) {
|
||||
txfree(dsPtr->string) ;
|
||||
}
|
||||
dsPtr->string = newString;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Copy the new string into the buffer at the end of the old
|
||||
* one.
|
||||
----------------------------------------------------------------- */
|
||||
for( dst = dsPtr->string + dsPtr->length, end = string+length;
|
||||
string < end; string++, dst++) {
|
||||
if( isupper_c(*string) ) {
|
||||
*dst = tolower_c(*string) ;
|
||||
} else {
|
||||
*dst = *string ;
|
||||
/* Concatenate character */
|
||||
int ds_cat_char_case(DSTRING *p_ds, char c, ds_case_t case_type)
|
||||
{
|
||||
return ds_cat_mem_case(p_ds, &c, 1, case_type);
|
||||
} /* end of function ds_cat_char_case */
|
||||
|
||||
|
||||
|
||||
/* Concatenate another dstring */
|
||||
int ds_cat_ds_case(DSTRING *p_ds_dst, const DSTRING *p_ds_src,
|
||||
ds_case_t case_type)
|
||||
{
|
||||
return ds_cat_mem_case(p_ds_dst, p_ds_src->p_buf, p_ds_src->length,
|
||||
case_type);
|
||||
} /* end of function ds_cat_ds_case */
|
||||
|
||||
|
||||
|
||||
/* General concatenation of a memory buffer. A terminating null is added. */
|
||||
int ds_cat_mem_case(DSTRING *p_ds, const char *p_src, size_t n_char,
|
||||
ds_case_t type_case)
|
||||
{
|
||||
/* Resize buffer if necessary. Double required size, if available,
|
||||
* to reduce the number of allocations */
|
||||
const size_t length_new = p_ds->length + n_char;
|
||||
const size_t n_byte_needed = length_new + 1;
|
||||
if (n_byte_needed > p_ds->n_byte_alloc) {
|
||||
if (ds_reserve_internal(p_ds,
|
||||
2 * n_byte_needed, n_byte_needed) == DS_E_NO_MEMORY) {
|
||||
return DS_E_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
*dst = '\0' ;
|
||||
dsPtr->length += length ;
|
||||
|
||||
return(dsPtr->string) ;
|
||||
|
||||
} /* end spice_dstring_append_lower() */
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Function: add character c to dynamic string dstr_p.
|
||||
* ----------------------------------------------------------------- */
|
||||
char *spice_dstring_append_char( SPICE_DSTRINGPTR dstr_p, char c)
|
||||
{
|
||||
return spice_dstring_append( dstr_p, &c, 1 ) ;
|
||||
} /* end spice_dstring_append_char() */
|
||||
|
||||
static int spice_format_length( const char *fmt, va_list args )
|
||||
{
|
||||
int i ; /* integer */
|
||||
int len ; /* length of format */
|
||||
int size_format ; /* width of field */
|
||||
int found_special ; /* look for special characters */
|
||||
char *s ; /* string */
|
||||
double d ;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* First find length of buffer.
|
||||
----------------------------------------------------------------- */
|
||||
len = 0 ;
|
||||
while(fmt && *fmt){
|
||||
if( *fmt == '%' ){
|
||||
fmt++ ;
|
||||
if( *fmt == '%' ){
|
||||
len++ ;
|
||||
} else {
|
||||
/* -----------------------------------------------------------------
|
||||
* We have a real formatting character, loop until we get a special
|
||||
* character.
|
||||
----------------------------------------------------------------- */
|
||||
if( *fmt == '.' || *fmt == '-' ){
|
||||
fmt++ ; /* skip over these characters */
|
||||
}
|
||||
size_format = atoi(fmt) ;
|
||||
if( size_format > 0 ){
|
||||
len += size_format ;
|
||||
}
|
||||
found_special = FALSE ;
|
||||
for( ; fmt && *fmt ; fmt++ ){
|
||||
switch( *fmt ){
|
||||
case 's':
|
||||
s = va_arg(args, char *) ;
|
||||
if( s ){
|
||||
len += (int) strlen(s) ;
|
||||
}
|
||||
found_special = TRUE ;
|
||||
break ;
|
||||
case 'i':
|
||||
case 'd':
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'u':
|
||||
i = va_arg(args, int) ;
|
||||
len += 10 ;
|
||||
found_special = TRUE ;
|
||||
break ;
|
||||
case 'c':
|
||||
i = va_arg(args, int) ;
|
||||
len++ ;
|
||||
found_special = TRUE ;
|
||||
break ;
|
||||
case 'f':
|
||||
case 'e':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
d = va_arg(args, double) ;
|
||||
len += 35 ;
|
||||
found_special = TRUE ;
|
||||
break ;
|
||||
default:
|
||||
;
|
||||
} /* end switch() */
|
||||
|
||||
if( found_special ){
|
||||
break ;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len++ ;
|
||||
}
|
||||
fmt++ ;
|
||||
} /* end while() */
|
||||
|
||||
return(len) ;
|
||||
|
||||
} /* end Ymessage_format_length() */
|
||||
|
||||
|
||||
char *spice_dstring_print( SPICE_DSTRINGPTR dsPtr, const char *format, ... )
|
||||
{
|
||||
va_list args ;
|
||||
int format_len ; /* length of format */
|
||||
int length ; /* new length */
|
||||
int orig_length ; /* original length of buffer */
|
||||
char *buffer ; /* proper length of buffer */
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* First get the length of the buffer needed.
|
||||
----------------------------------------------------------------- */
|
||||
va_start( args, format ) ;
|
||||
format_len = spice_format_length(format, args) ;
|
||||
va_end(args) ;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Next allocate the proper buffer size.
|
||||
----------------------------------------------------------------- */
|
||||
orig_length = dsPtr->length ;
|
||||
length = orig_length + format_len + 1 ;
|
||||
buffer = spice_dstring_setlength( dsPtr, length) ;
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
* Convert the format.
|
||||
----------------------------------------------------------------- */
|
||||
va_start( args, format ) ;
|
||||
if( format ){
|
||||
vsprintf( buffer + orig_length, format, args ) ;
|
||||
dsPtr->length = (int) strlen(buffer) ;
|
||||
} else {
|
||||
buffer = NULL ;
|
||||
/* For "as-is" can simply memcpy */
|
||||
if (type_case == ds_case_as_is) {
|
||||
char *p_dst = p_ds->p_buf + p_ds->length;
|
||||
(void) memcpy(p_dst, p_src, n_char);
|
||||
p_dst += n_char;
|
||||
*p_dst = '\0';
|
||||
p_ds->length = length_new;
|
||||
return DS_E_OK;
|
||||
}
|
||||
va_end(args) ;
|
||||
return( buffer ) ;
|
||||
|
||||
} /* end spice_dstring_print() */
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
/* For lowercasing, work char by char */
|
||||
if (type_case == ds_case_lower) {
|
||||
char *p_dst = p_ds->p_buf + p_ds->length;
|
||||
char *p_dst_end = p_dst + n_char;
|
||||
for ( ; p_dst < p_dst_end; p_dst++, p_src++) {
|
||||
*p_dst = (char) tolower(*p_src);
|
||||
}
|
||||
*p_dst_end = '\0';
|
||||
p_ds->length = length_new;
|
||||
return DS_E_OK;
|
||||
}
|
||||
|
||||
/* Uppercasing done like lowercasing. Note that it would be possible to
|
||||
* use a function pointer and select either tolower() or toupper() based
|
||||
* on type_case, but doing so may degrade performance by inhibiting
|
||||
* inlining. */
|
||||
if (type_case == ds_case_upper) {
|
||||
char *p_dst = p_ds->p_buf + p_ds->length;
|
||||
char *p_dst_end = p_dst + n_char;
|
||||
for ( ; p_dst < p_dst_end; p_dst++, p_src++) {
|
||||
*p_dst = (char) toupper(*p_src);
|
||||
}
|
||||
*p_dst_end = '\0';
|
||||
p_ds->length = length_new;
|
||||
return DS_E_OK;
|
||||
}
|
||||
|
||||
return DS_E_INVALID; /* unknown case type */
|
||||
} /* end of function ds_cat_mem_case */
|
||||
|
||||
|
||||
|
||||
/* Ensure minimum internal buffer size */
|
||||
int ds_reserve(DSTRING *p_ds, size_t n_byte_alloc)
|
||||
{
|
||||
/* Return if buffer already large enough */
|
||||
if (p_ds->n_byte_alloc >= n_byte_alloc) {
|
||||
return DS_E_OK;
|
||||
}
|
||||
|
||||
return ds_reserve_internal(p_ds, n_byte_alloc, 0);
|
||||
} /* end of function ds_reserve */
|
||||
|
||||
|
||||
|
||||
/* This function resizes the buffer for the string and handles freeing
|
||||
* the original alloction, if necessary. It is assumed that the requested
|
||||
* size or sizes are larger than the current size.
|
||||
*
|
||||
* _spice_dstring_setlength --
|
||||
* Parameters
|
||||
* p_ds: Dstring pointer
|
||||
* n_byte_alloc_opt: Optimal alloction amount
|
||||
* n_byte_alloc_min: Absolute minimum allocation amount or 0 if no
|
||||
* smaller amount can be allocated
|
||||
*
|
||||
* Change the length of a dynamic string. This can cause the
|
||||
* string to either grow or shrink, depending on the value of
|
||||
* length.
|
||||
* Return codes
|
||||
* DS_E_OK: At least the minimum allocation was performed
|
||||
* DS_E_NO_MEMORY: Unable to resize the buffer */
|
||||
static int ds_reserve_internal(DSTRING *p_ds,
|
||||
size_t n_byte_alloc_opt, size_t n_byte_alloc_min)
|
||||
{
|
||||
size_t n_byte_alloc = n_byte_alloc_opt;
|
||||
/* Allocate. First try (larger) optimal size, and gradually fall back
|
||||
* to min size if that fails and one was provided. */
|
||||
char * p_buf_new;
|
||||
if (n_byte_alloc_min == 0) {
|
||||
n_byte_alloc_min = n_byte_alloc_opt;
|
||||
}
|
||||
for ( ; ; ) {
|
||||
if ((p_buf_new = (char *) malloc(n_byte_alloc)) != (char *) NULL) {
|
||||
break; /* Allocated OK */
|
||||
}
|
||||
|
||||
if (n_byte_alloc == n_byte_alloc_min) { /* min alloc failed */
|
||||
return DS_E_NO_MEMORY;
|
||||
}
|
||||
|
||||
if ((n_byte_alloc /= 2) < n_byte_alloc_min) { /* last try */
|
||||
n_byte_alloc = n_byte_alloc_min;
|
||||
}
|
||||
} /* end of loop trying smaller allocations */
|
||||
|
||||
/* Copy to the new buffer */
|
||||
(void) memcpy(p_buf_new, p_ds->p_buf, p_ds->length + 1);
|
||||
|
||||
/* If there already was a dynamic allocation, free it */
|
||||
if (p_ds->p_buf != p_ds->p_stack_buf) {
|
||||
free((void *) p_ds->p_buf);
|
||||
}
|
||||
|
||||
/* Assign new active buffer and its size */
|
||||
p_ds->p_buf = p_buf_new;
|
||||
p_ds->n_byte_alloc = n_byte_alloc;
|
||||
|
||||
return DS_E_OK;
|
||||
} /* end of function ds_reserve_nocheck */
|
||||
|
||||
|
||||
|
||||
/* Concatenate the result of a printf-style format
|
||||
*
|
||||
* Results:
|
||||
* Returns the current string buffer.
|
||||
* Return codes as for ds_cat_vprintf */
|
||||
int ds_cat_printf(DSTRING *p_ds, const char *sz_fmt, ...)
|
||||
{
|
||||
va_list p_arg;
|
||||
va_start(p_arg, sz_fmt);
|
||||
const int xrc = ds_cat_vprintf(p_ds, sz_fmt, p_arg);
|
||||
va_end(p_arg);
|
||||
return xrc;
|
||||
} /* end of function ds_cat_printf */
|
||||
|
||||
|
||||
|
||||
/* Concatenate the result of a printf-style format using va_list
|
||||
*
|
||||
* Side effects:
|
||||
* The length of dsPtr is changed to length but a null byte is not
|
||||
* stored at that position in the string. Use spice_dstring_setlength
|
||||
* for that function. If length is larger
|
||||
* than the space allocated for dsPtr, then a panic occurs.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* Return codes
|
||||
* DS_E_OK: Formatted OK
|
||||
* DS_E_NO_MEMORY: Unable to allocate memory to resize buffer
|
||||
* DS_E_INVALID: Invalid formatter / data
|
||||
*/
|
||||
|
||||
char *_spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length)
|
||||
int ds_cat_vprintf(DSTRING *p_ds, const char *sz_fmt, va_list p_arg)
|
||||
{
|
||||
char *newString ;
|
||||
|
||||
if (length < 0) {
|
||||
length = 0 ;
|
||||
/* Make a copy of the argument list in case need to format more than
|
||||
* once */
|
||||
va_list p_arg2;
|
||||
va_copy(p_arg2, p_arg);
|
||||
const size_t n_byte_free = p_ds->n_byte_alloc - p_ds->length;
|
||||
char * const p_dst = p_ds->p_buf + p_ds->length;
|
||||
const int rc = vsnprintf(p_dst, n_byte_free, sz_fmt, p_arg);
|
||||
if (rc < 0) { /* Check for formatting error */
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
if (length >= dsPtr->spaceAvl) {
|
||||
|
||||
dsPtr->spaceAvl = length+1;
|
||||
newString = TMALLOC(char, dsPtr->spaceAvl) ;
|
||||
/* -----------------------------------------------------------------
|
||||
* SPECIAL NOTE: must use memcpy, not strcpy, to copy the string
|
||||
* to a larger buffer, since there may be embedded NULLs in the
|
||||
* string in some cases.
|
||||
----------------------------------------------------------------- */
|
||||
memcpy(newString, dsPtr->string, (size_t) dsPtr->length) ;
|
||||
if( dsPtr->string != dsPtr->staticSpace ) {
|
||||
txfree(dsPtr->string) ;
|
||||
}
|
||||
dsPtr->string = newString ;
|
||||
/* Else check for buffer large enough and set length if it is */
|
||||
if (rc < n_byte_free) {
|
||||
p_ds->length += rc;
|
||||
return DS_E_OK;
|
||||
}
|
||||
dsPtr->length = length ;
|
||||
return(dsPtr->string) ;
|
||||
|
||||
} /* end _spice_dstring_setlength() */
|
||||
/* Else buffer too small, so resize and format again */
|
||||
{
|
||||
/* Double required size to avoid excessive allocations +1 for
|
||||
* null, which is not included in the count returned by snprintf */
|
||||
const size_t n_byte_alloc_min = p_ds->length + rc + 1;
|
||||
if (ds_reserve_internal(p_ds,
|
||||
2 * n_byte_alloc_min, n_byte_alloc_min) == DS_E_NO_MEMORY) {
|
||||
/* vsnprintf may have written bytes to the buffer.
|
||||
* Ensure that dstring in a consistent state by writing
|
||||
* a null at the length of the string */
|
||||
p_ds->p_buf[p_ds->length] = '\0';
|
||||
return DS_E_NO_MEMORY;
|
||||
}
|
||||
const size_t n_byte_free2 = p_ds->n_byte_alloc - p_ds->length;
|
||||
char * const p_dst2 = p_ds->p_buf + p_ds->length;
|
||||
const int rc2 = vsnprintf(p_dst2, n_byte_free2, sz_fmt, p_arg2);
|
||||
if (rc2 < 0) { /* Check for formatting error */
|
||||
/* vsnprintf may have written bytes to the buffer.
|
||||
* Ensure that dstring in a consistent state by writing
|
||||
* a null at the length of the string */
|
||||
p_ds->p_buf[p_ds->length] = '\0';
|
||||
return DS_E_INVALID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* spice_dstring_setlength --
|
||||
*
|
||||
* Change the length of a dynamic string. This can cause the
|
||||
* string to either grow or shrink, depending on the value of
|
||||
* length.
|
||||
*
|
||||
* Results:
|
||||
* Returns the current string buffer.
|
||||
*
|
||||
* Side effects:
|
||||
* The length of dsPtr is changed to length and a null byte is
|
||||
* stored at that position in the string. If length is larger
|
||||
* than the space allocated for dsPtr, then a panic occurs.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
/* Else update length. No need to check buffer size since it was
|
||||
* sized to fit the string. */
|
||||
p_ds->length += rc2;
|
||||
return DS_E_OK;
|
||||
}
|
||||
} /* end of function ds_cat_vprintf */
|
||||
|
||||
char *spice_dstring_setlength(SPICE_DSTRINGPTR dsPtr,int length)
|
||||
|
||||
|
||||
|
||||
/* Reallocate/free to eliminate unused buffer space.
|
||||
*
|
||||
* Return codes
|
||||
* DS_E_OK: Compacted OK
|
||||
* DS_E_NO_MEMORY: Compaction failed, but dstring still valid */
|
||||
int ds_compact(DSTRING *p_ds)
|
||||
{
|
||||
char *str_p ; /* newly create string */
|
||||
const size_t n_byte_alloc_min = p_ds->length + 1;
|
||||
|
||||
str_p = _spice_dstring_setlength( dsPtr,length) ;
|
||||
str_p[length] = '\0' ;
|
||||
return( str_p ) ;
|
||||
|
||||
} /* end spice_dstring_setlength() */
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* spice_dstring_free --
|
||||
*
|
||||
* Frees up any memory allocated for the dynamic string and
|
||||
* reinitializes the string to an empty state.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* The previous contents of the dynamic string are lost, and
|
||||
* the new value is an empty string.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void spice_dstring_free(SPICE_DSTRINGPTR dsPtr)
|
||||
{
|
||||
if (dsPtr->string != dsPtr->staticSpace) {
|
||||
txfree(dsPtr->string) ;
|
||||
/* If the string is in the stack buffer, there is nothing to do */
|
||||
if (p_ds->p_stack_buf == p_ds->p_buf) {
|
||||
return DS_E_OK;
|
||||
}
|
||||
dsPtr->string = dsPtr->staticSpace ;
|
||||
dsPtr->length = 0 ;
|
||||
dsPtr->spaceAvl = SPICE_DSTRING_STATIC_SIZE;
|
||||
dsPtr->staticSpace[0] = '\0' ;
|
||||
|
||||
} /* end spice_dstring_free() */
|
||||
/* Else if the string will fit in the stack buffer, copy it there and
|
||||
* free the allocation. */
|
||||
if (p_ds->n_byte_stack_buf >= n_byte_alloc_min) {
|
||||
(void) memcpy(p_ds->p_stack_buf, p_ds->p_buf, n_byte_alloc_min);
|
||||
free((void *) p_ds->p_buf);
|
||||
p_ds->p_buf = p_ds->p_stack_buf;
|
||||
p_ds->n_byte_alloc = p_ds->n_byte_stack_buf;
|
||||
return DS_E_OK;
|
||||
}
|
||||
|
||||
/* Else if the heap buffer is the minimum size, there is nothng to do */
|
||||
if (n_byte_alloc_min == p_ds->n_byte_alloc) {
|
||||
return DS_E_OK;
|
||||
}
|
||||
|
||||
/* Else realloc the heap buffer */
|
||||
{
|
||||
void *p = realloc(p_ds->p_buf, n_byte_alloc_min);
|
||||
if (p == NULL) {
|
||||
return DS_E_NO_MEMORY;
|
||||
}
|
||||
p_ds->p_buf = (char *) p;
|
||||
p_ds->n_byte_alloc = n_byte_alloc_min;
|
||||
return DS_E_OK;
|
||||
}
|
||||
} /* end of function ds_compact */
|
||||
|
||||
|
||||
|
||||
#ifdef DSTRING_UNIT_TEST
|
||||
#if defined (_WIN32) && !defined(CONSOLE)
|
||||
#include "ngspice/wstdio.h"
|
||||
#endif
|
||||
static void ds_print_info(DSTRING *p_ds, FILE *fp, const char *sz_id);
|
||||
static int ds_test_from_macro(FILE *fp);
|
||||
static int ds_test_from_stack(FILE *fp);
|
||||
static int ds_test_from_heap(FILE *fp);
|
||||
static int ds_test1(DSTRING *p_ds, FILE *fp);
|
||||
|
||||
|
||||
int ds_test(FILE *fp)
|
||||
{
|
||||
if (ds_test_from_macro(fp) != 0) { /* create from macro and run test */
|
||||
return -1;
|
||||
}
|
||||
if (ds_test_from_stack(fp) != 0) { /* create from stack */
|
||||
return -1;
|
||||
}
|
||||
if (ds_test_from_heap(fp) != 0) { /* create from heap */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} /* end of function ds_test */
|
||||
|
||||
|
||||
|
||||
/* Run tests from a macro-created dstring */
|
||||
static int ds_test_from_macro(FILE *fp)
|
||||
{
|
||||
DS_CREATE(ds, 10);
|
||||
(void) fprintf(fp, "Macro initialization\n");
|
||||
return ds_test1(&ds, fp);
|
||||
} /* end of function ds_test_from_macro */
|
||||
|
||||
|
||||
|
||||
/* Run tests from a manually created stack-backed dstring */
|
||||
static int ds_test_from_stack(FILE *fp)
|
||||
{
|
||||
static char p_buf[30] = "Hello World";
|
||||
DSTRING ds;
|
||||
(void) fprintf(fp, "Stack initialization\n");
|
||||
(void) ds_init(&ds, p_buf, 11, sizeof p_buf, ds_buf_type_stack);
|
||||
return ds_test1(&ds, fp);
|
||||
} /* end of function ds_test_from_stack */
|
||||
|
||||
|
||||
|
||||
/* Run tests from a heap-backed dstring */
|
||||
static int ds_test_from_heap(FILE *fp)
|
||||
{
|
||||
char *p_buf = (char *) malloc(25);
|
||||
if (p_buf == (char *) NULL) {
|
||||
return -1;
|
||||
}
|
||||
(void) memcpy(p_buf, "Heap", 4);
|
||||
DSTRING ds;
|
||||
(void) ds_init(&ds, p_buf, 4, 25, ds_buf_type_heap);
|
||||
(void) fprintf(fp, "Heap initialization\n");
|
||||
return ds_test1(&ds, fp);
|
||||
} /* end of function ds_test_from_heap */
|
||||
|
||||
|
||||
|
||||
static int ds_test1(DSTRING *p_ds, FILE *fp)
|
||||
{
|
||||
/* Print info on entry */
|
||||
ds_print_info(p_ds, fp, "On entry to ds_test1\n");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ds_cat_str(p_ds, "Abc") != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_str_case(p_ds, "Abc", ds_case_as_is) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string as-is %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_str_case(p_ds, "Abc", ds_case_upper) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string upper %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_str_case(p_ds, "Abc", ds_case_lower) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string lower %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_char(p_ds, 'z') != 0) {
|
||||
(void) fprintf(fp, "Unable to cat char %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_char_case(p_ds, 'z', ds_case_as_is) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat char as-is %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_char_case(p_ds, 'z', ds_case_upper) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat char upper %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_char_case(p_ds, 'Z', ds_case_lower) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat char lower %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ds_cat_mem(p_ds, "Zyxw", 4) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_as_is) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string as-is %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_upper) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string upper %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (ds_cat_mem_case(p_ds, "Zyxw", 4, ds_case_lower) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat string lower %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ds_cat_printf(p_ds, "--- And finally a formatted %s (%d)",
|
||||
"string", i) != 0) {
|
||||
(void) fprintf(fp, "Unable to cat formatted string %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Print info after cats */
|
||||
ds_print_info(p_ds, fp, "After appending strings");
|
||||
|
||||
/* Truncate the string */
|
||||
if (ds_set_length(p_ds, i * (size_t) 10) != 0) {
|
||||
(void) fprintf(fp, "Unable to set size %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Print info after truncation */
|
||||
ds_print_info(p_ds, fp, "After setting length");
|
||||
|
||||
/* Compact the string */
|
||||
if (ds_compact(p_ds) != 0) {
|
||||
(void) fprintf(fp, "Unable to compact %d.\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Print info after compaction */
|
||||
ds_print_info(p_ds, fp, "After compacting the string");
|
||||
} /* end of loop over tests */
|
||||
|
||||
ds_free(p_ds); /* free buffer if allocated */
|
||||
|
||||
return 0;
|
||||
} /* end of funtion ds_test */
|
||||
|
||||
|
||||
|
||||
/* Print some info about the DSTRING */
|
||||
static void ds_print_info(DSTRING *p_ds, FILE *fp, const char *sz_id)
|
||||
{
|
||||
(void) fprintf(fp, "%s: length = %zu; "
|
||||
"allocated buffer size = %zu; value = \"%s\"; "
|
||||
"address of active buffer = %p; "
|
||||
"address of stack buffer = %p; "
|
||||
"size of stack buffer = %zu\n",
|
||||
sz_id,
|
||||
ds_get_length(p_ds), ds_get_buf_size(p_ds),
|
||||
ds_get_buf(p_ds), ds_get_buf(p_ds),
|
||||
p_ds->p_stack_buf, p_ds->n_byte_stack_buf);
|
||||
} /* end of function ds_print_info */
|
||||
|
||||
|
||||
|
||||
#endif /* DSTRING_UNIT_TEST */
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue