Dynamic string (dstring) rework

This commit is contained in:
Jim Monte 2019-11-26 20:59:56 -05:00 committed by Holger Vogt
parent 9452121954
commit 89efe5aeae
8 changed files with 949 additions and 605 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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 == '-';
}

View File

@ -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;
}

View File

@ -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(&param_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(&param_name);
param_p = spice_dstring_print(&param_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(&param_name);
if (ds_cat_printf(&param_name, "%s.%s",
inst_name, entry->symbol) != DS_E_OK) {
controlled_exit(-1);
}
nupa_add_inst_param(ds_get_buf(&param_name), entry->vl);
dico_free_entry(entry);
}
nghash_free(htable_p, NULL, NULL);
spice_dstring_free(&param_name);
ds_free(&param_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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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 */