From 15c034803da8b5ba55bab20401124e8307ad0e89 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Wed, 1 Jan 2020 14:39:03 +0100 Subject: [PATCH 01/58] 64 bit compilation is the standard --- compile_linux.sh | 30 +++++++++++++++--------------- compile_min.sh | 30 +++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/compile_linux.sh b/compile_linux.sh index 84fc33f88..04e8533ae 100644 --- a/compile_linux.sh +++ b/compile_linux.sh @@ -6,7 +6,7 @@ # Install gcc, bison, flex, libtool, autoconf, automake, # libx11 and libx11-dev (headers), libXaw and libXaw-dev, libreadline and dev # Declare 'compile_linux.sh' executable and start compiling with -# './compile_linux.sh' or './compile_min.sh 64' from the ngspice directory. +# './compile_linux.sh' or './compile_min.sh 32' from the ngspice directory. # Options: # --adms and --enable-adms will install extra HICUM, EKV and MEXTRAM models via the # adms interface. You need to download and install the *.va files via ng-adms-va.tgz @@ -19,10 +19,10 @@ # Add (optionally) --enable-relpath to avoid absolute paths when searching for code models. # It might be necessary to uncomment and run ./autogen.sh . -if test "$1" = "64"; then - if [ ! -d "release64" ]; then - mkdir release64 - if [ $? -ne 0 ]; then echo "mkdir release64 failed"; exit 1 ; fi +if test "$1" = "32"; then + if [ ! -d "release32" ]; then + mkdir release32 + if [ $? -ne 0 ]; then echo "mkdir release32 failed"; exit 1 ; fi fi else if [ ! -d "release" ]; then @@ -42,20 +42,20 @@ if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi #if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi echo -if test "$1" = "64"; then - cd release64 - if [ $? -ne 0 ]; then echo "cd release64 failed"; exit 1 ; fi - echo "configuring for 64 bit" - echo -# You may add --enable-adms to the following command for adding adms generated devices - ../configure --with-x --enable-xspice --enable-cider --with-readline=yes --enable-openmp --disable-debug CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" -else - cd release - if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi +if test "$1" = "32"; then + cd release32 + if [ $? -ne 0 ]; then echo "cd release32 failed"; exit 1 ; fi echo "configuring for 32 bit" echo # You may add --enable-adms to the following command for adding adms generated devices ../configure --with-x --enable-xspice --enable-cider --with-readline=yes --enable-openmp --disable-debug CFLAGS="-m32 -O2" LDFLAGS="-m32 -s" +else + cd release + if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi + echo "configuring for 64 bit" + echo +# You may add --enable-adms to the following command for adding adms generated devices + ../configure --with-x --enable-xspice --enable-cider --with-readline=yes --enable-openmp --disable-debug CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" fi if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi diff --git a/compile_min.sh b/compile_min.sh index f215de448..fdf26a6e2 100755 --- a/compile_min.sh +++ b/compile_min.sh @@ -9,7 +9,7 @@ # (allows to generate either 32 or 64 bit executables by setting flag -m32 or -m64) # set path to compiler in msys/xx/etc/fstab (e.g. c:/MinGW64 /mingw) # start compiling with -# './compile_min.sh' or './compile_min.sh 64' +# './compile_min.sh 32' or './compile_min.sh' # As an (more recent) alternative install MSYS2 and the tools cited above. # Options: @@ -25,10 +25,10 @@ # Add (optionally) --enable-relpath to avoid absolute paths when searching for code models. # It might be necessary to uncomment and run ./autogen.sh . -if test "$1" = "64"; then - if [ ! -d "release64" ]; then - mkdir release64 - if [ $? -ne 0 ]; then echo "mkdir release64 failed"; exit 1 ; fi +if test "$1" = "32"; then + if [ ! -d "release32" ]; then + mkdir release32 + if [ $? -ne 0 ]; then echo "mkdir release32 failed"; exit 1 ; fi fi else if [ ! -d "release" ]; then @@ -48,20 +48,20 @@ fi #if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi echo -if test "$1" = "64"; then - cd release64 - if [ $? -ne 0 ]; then echo "cd release64 failed"; exit 1 ; fi - echo "configuring for 64 bit" - echo -# You may add --enable-adms to the following command for adding adms generated devices - ../configure --with-wingui --enable-xspice --enable-cider --enable-openmp --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" -else - cd release - if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi +if test "$1" = "32"; then + cd release32 + if [ $? -ne 0 ]; then echo "cd release32 failed"; exit 1 ; fi echo "configuring for 32 bit" echo # You may add --enable-adms to the following command for adding adms generated devices ../configure --with-wingui --enable-xspice --enable-cider --enable-openmp --disable-debug prefix="C:/Spice" CFLAGS="-m32 -O2" LDFLAGS="-m32 -s" +else + cd release + if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi + echo "configuring for 64 bit" + echo +# You may add --enable-adms to the following command for adding adms generated devices + ../configure --with-wingui --enable-xspice --enable-cider --enable-openmp --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" fi if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi From 146f94392d66a2dfd02099d97ddc8821a2581c2e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Jan 2020 10:10:08 +0100 Subject: [PATCH 02/58] Remove memory leaks in cmpp. Two small ones (a few bytes) are still there, difficult to assess. --- src/xspice/cmpp/cmpp.h | 1 + src/xspice/cmpp/pp_ifs.c | 33 ++++++++++++++++++++++++++++++++- src/xspice/cmpp/pp_lst.c | 35 ++++++++++++++++++++++++++++++++--- src/xspice/cmpp/pp_mod.c | 5 ++++- src/xspice/cmpp/read_ifs.c | 4 ++++ src/xspice/cmpp/writ_ifs.c | 3 ++- 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/xspice/cmpp/cmpp.h b/src/xspice/cmpp/cmpp.h index 00bbaa207..5d3865a86 100644 --- a/src/xspice/cmpp/cmpp.h +++ b/src/xspice/cmpp/cmpp.h @@ -301,6 +301,7 @@ Status_t write_ifs_c_file(const char *filename, Ifs_Table_t *ifs_table); FILE *fopen_cmpp(const char **path_p, const char *mode); +void rem_ifs_table(Ifs_Table_t *ifs_table); /* * type safe variants of the functions for char arguments diff --git a/src/xspice/cmpp/pp_ifs.c b/src/xspice/cmpp/pp_ifs.c index 00ba1dc9a..f0830b51a 100644 --- a/src/xspice/cmpp/pp_ifs.c +++ b/src/xspice/cmpp/pp_ifs.c @@ -44,6 +44,11 @@ NON-STANDARD FEATURES /* *********************************************************************** */ +static void txfree(void *ptr) +{ + if(ptr) + free(ptr); +}; /* preprocess_ifs_file @@ -65,7 +70,6 @@ void preprocess_ifs_file(void) Status_t status; /* Return status */ - /* Read the entire ifspec.ifs file and load the data into ifs_table */ status = read_ifs_file(IFSPEC_FILENAME,GET_IFS_TABLE,&ifs_table); @@ -82,7 +86,34 @@ void preprocess_ifs_file(void) if(status != OK) { exit(1); } + rem_ifs_table(&ifs_table); +} +void rem_ifs_table(Ifs_Table_t *ifs_table) +{ + int i; + /* Remove the ifs_table */ + txfree(ifs_table->name.c_fcn_name); + txfree(ifs_table->name.description); + txfree(ifs_table->name.model_name); + + for(i = 0; i < ifs_table->num_conn; i++) { + txfree(ifs_table->conn[i].name); + txfree(ifs_table->conn[i].description); + txfree(ifs_table->conn[i].default_type); + } + + for(i = 0; i < ifs_table->num_param; i++) { + txfree(ifs_table->param[i].name); + txfree(ifs_table->param[i].description); + } + for(i = 0; i < ifs_table->num_inst_var; i++) { + txfree(ifs_table->inst_var[i].name); + txfree(ifs_table->inst_var[i].description); + } + txfree(ifs_table->conn); + txfree(ifs_table->param); + txfree(ifs_table->inst_var); } diff --git a/src/xspice/cmpp/pp_lst.c b/src/xspice/cmpp/pp_lst.c index 257bb3581..5c3b3b02f 100644 --- a/src/xspice/cmpp/pp_lst.c +++ b/src/xspice/cmpp/pp_lst.c @@ -193,7 +193,16 @@ void preprocess_lst_files(void) if(status != OK) { exit(1); } - + /* remove model_info and node_info */ + if (model_info) { + if(model_info->cfunc_name) free(model_info->cfunc_name); + if(model_info->path_name) free(model_info->path_name); + if(model_info->spice_name) free(model_info->spice_name); + } + if (node_info) { + if(node_info->node_name) free(node_info->node_name); + if(node_info->path_name) free(node_info->path_name); + } } @@ -309,6 +318,9 @@ static Status_t read_modpath( *num_models = n; *model_info = model; + if(filename) + free((char*)filename); + return(OK); } @@ -424,6 +436,9 @@ static Status_t read_udnpath( *num_nodes = n; *node_info = node; + if(filename) + free((char*)filename); + return(OK); } @@ -467,14 +482,17 @@ static Status_t read_model_names( /* Transfer the names into the model_info structure */ if(status == OK) { - model_info[i].spice_name = ifs_table.name.model_name; - model_info[i].cfunc_name = ifs_table.name.c_fcn_name; + model_info[i].spice_name = strdup(ifs_table.name.model_name); + model_info[i].cfunc_name = strdup(ifs_table.name.c_fcn_name); } else { all_found = FALSE; print_error("ERROR - Problems reading %s in directory %s", IFSPEC_FILENAME, model_info[i].path_name); } + + /* Remove the ifs_table */ + rem_ifs_table(&ifs_table); } if(all_found) @@ -767,6 +785,8 @@ static Status_t write_CMextrn( /* Close the file and return */ fclose(fp); + if(filename) + free((char*)filename); return(OK); } @@ -808,6 +828,8 @@ static Status_t write_CMinfo( /* Close the file and return */ fclose(fp); + if(filename) + free((char*)filename); return(OK); } @@ -853,6 +875,8 @@ static Status_t write_UDNextrn( /* Close the file and return */ fclose(fp); + if(filename) + free((char*)filename); return(OK); } @@ -895,6 +919,8 @@ static Status_t write_UDNinfo( } /* Close the file and return */ + if(filename) + free((char*)filename); fclose(fp); return(OK); } @@ -947,6 +973,8 @@ static Status_t write_objects_inc( /* Close the file and return */ fclose(fp); + if(filename) + free((char*)filename); return(OK); } @@ -976,6 +1004,7 @@ static Status_t read_udn_type_name( /* Open the file from which the node type name will be read */ fp = fopen_cmpp(&path, "r"); + free((char*)path); if(fp == NULL) return(ERROR); diff --git a/src/xspice/cmpp/pp_mod.c b/src/xspice/cmpp/pp_mod.c index 4b2436b91..240371ed6 100644 --- a/src/xspice/cmpp/pp_mod.c +++ b/src/xspice/cmpp/pp_mod.c @@ -159,7 +159,10 @@ void preprocess_mod_file ( exit (1); } fclose (mod_yyout); - mod_yyrestart(NULL); + if(output_filename) + free((char*)output_filename); + rem_ifs_table(&ifs_table); + mod_yyrestart(NULL); } /*---------------------------------------------------------------------------*/ diff --git a/src/xspice/cmpp/read_ifs.c b/src/xspice/cmpp/read_ifs.c index 2dd038b81..9a2bb0197 100644 --- a/src/xspice/cmpp/read_ifs.c +++ b/src/xspice/cmpp/read_ifs.c @@ -150,6 +150,10 @@ static Status_t read_ifs_table( assert (ifs_table); assert (fp); + ifs_table->name.description = + ifs_table->name.c_fcn_name = + ifs_table->name.model_name = NULL; + ifs_yylineno = 1; ifs_yyin = fp; parser_just_names = (mode == GET_IFS_NAME) ? TRUE : FALSE; diff --git a/src/xspice/cmpp/writ_ifs.c b/src/xspice/cmpp/writ_ifs.c index 00ff98575..a09f61c99 100644 --- a/src/xspice/cmpp/writ_ifs.c +++ b/src/xspice/cmpp/writ_ifs.c @@ -155,7 +155,8 @@ Status_t write_ifs_c_file( /* Close the ifspec.c file and return */ int_status = fclose(fp); - + if(filename) + free((char*)filename); if(int_status == 0) return(OK); else From 9644f2ae4c1d16e11488ff35fce8c1664c295a72 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 26 Nov 2019 20:59:56 -0500 Subject: [PATCH 03/58] Dynamic string (dstring) rework --- src/frontend/diff.c | 64 +-- src/frontend/numparam/general.h | 10 +- src/frontend/numparam/mystring.c | 60 ++- src/frontend/numparam/spicenum.c | 49 +- src/frontend/numparam/xpressn.c | 155 +++--- src/frontend/vectors.c | 74 +-- src/include/ngspice/dstring.h | 280 ++++++++-- src/misc/dstring.c | 862 ++++++++++++++++++------------- 8 files changed, 949 insertions(+), 605 deletions(-) diff --git a/src/frontend/diff.c b/src/frontend/diff.c index 4e6502989..ed5f74d02 100644 --- a/src/frontend/diff.c +++ b/src/frontend/diff.c @@ -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 -#include #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) diff --git a/src/frontend/numparam/general.h b/src/frontend/numparam/general.h index aa7eeb6c7..a17c26d9a 100644 --- a/src/frontend/numparam/general.h +++ b/src/frontend/numparam/general.h @@ -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); diff --git a/src/frontend/numparam/mystring.c b/src/frontend/numparam/mystring.c index fa9e67092..297b7a970 100644 --- a/src/frontend/numparam/mystring.c +++ b/src/frontend/numparam/mystring.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 == '-'; } + + + diff --git a/src/frontend/numparam/spicenum.c b/src/frontend/numparam/spicenum.c index 9d9a69f11..d4405964b 100644 --- a/src/frontend/numparam/spicenum.c +++ b/src/frontend/numparam/spicenum.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; } diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index 64cc72b16..cdd5299d0 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -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; } diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 3751e4bef..3daba86cf 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -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 diff --git a/src/include/ngspice/dstring.h b/src/include/ngspice/dstring.h index b95f76234..25ad51773 100644 --- a/src/include/ngspice/dstring.h +++ b/src/include/ngspice/dstring.h @@ -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 +#include +#include +#include -#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 +int ds_test(FILE *fp); +#endif /* UNIT_TEST_DSTRING */ + +#endif /* include guard */ diff --git a/src/misc/dstring.c b/src/misc/dstring.c index 65c0c37d3..3706134b4 100644 --- a/src/misc/dstring.c +++ b/src/misc/dstring.c @@ -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 #include +#include +#include +#include +#include +#include + #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 */ + + + From e969a054113c256146066d591d8fbec9b2e43cf5 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Wed, 27 Nov 2019 10:44:15 -0500 Subject: [PATCH 04/58] Added a macro R_MIN to define the minimum resistance value aS 0.01 and replaced the numeric value with the macro in the sourc code. --- src/spicelib/devices/vbic/vbicmpar.c | 29 +++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/spicelib/devices/vbic/vbicmpar.c b/src/spicelib/devices/vbic/vbicmpar.c index 118a5a143..16bd00386 100644 --- a/src/spicelib/devices/vbic/vbicmpar.c +++ b/src/spicelib/devices/vbic/vbicmpar.c @@ -17,6 +17,7 @@ Spice3 Implementation: 2003 Dietmar Warning DAnalyse GmbH #include "ngspice/sperror.h" #include "ngspice/suffix.h" +#define R_MIN 0.01 int VBICmParam(int param, IFvalue *value, GENmodel *inModel) @@ -41,12 +42,16 @@ VBICmParam(int param, IFvalue *value, GENmodel *inModel) break; case VBIC_MOD_RCX: mods->VBICextCollResist = value->rValue; - if (mods->VBICextCollResist < 0.01) mods->VBICextCollResist = 0.01; + if (mods->VBICextCollResist < R_MIN) { + mods->VBICextCollResist = R_MIN; + } mods->VBICextCollResistGiven = TRUE; break; case VBIC_MOD_RCI: mods->VBICintCollResist = value->rValue; - if (mods->VBICintCollResist < 0.01) mods->VBICintCollResist = 0.01; + if (mods->VBICintCollResist < R_MIN) { + mods->VBICintCollResist = R_MIN; + } mods->VBICintCollResistGiven = TRUE; break; case VBIC_MOD_VO: @@ -63,27 +68,37 @@ VBICmParam(int param, IFvalue *value, GENmodel *inModel) break; case VBIC_MOD_RBX: mods->VBICextBaseResist = value->rValue; - if (mods->VBICextBaseResist < 0.01) mods->VBICextBaseResist = 0.01; + if (mods->VBICextBaseResist < R_MIN) { + mods->VBICextBaseResist = R_MIN; + } mods->VBICextBaseResistGiven = TRUE; break; case VBIC_MOD_RBI: mods->VBICintBaseResist = value->rValue; - if (mods->VBICintBaseResist < 0.01) mods->VBICintBaseResist = 0.01; + if (mods->VBICintBaseResist < R_MIN) { + mods->VBICintBaseResist = R_MIN; + } mods->VBICintBaseResistGiven = TRUE; break; case VBIC_MOD_RE: mods->VBICemitterResist = value->rValue; - if (mods->VBICemitterResist < 0.01) mods->VBICemitterResist = 0.01; + if (mods->VBICemitterResist < R_MIN) { + mods->VBICemitterResist = R_MIN; + } mods->VBICemitterResistGiven = TRUE; break; case VBIC_MOD_RS: mods->VBICsubstrateResist = value->rValue; - if (mods->VBICsubstrateResist < 0.01) mods->VBICsubstrateResist = 0.01; + if (mods->VBICsubstrateResist < R_MIN) { + mods->VBICsubstrateResist = R_MIN; + } mods->VBICsubstrateResistGiven = TRUE; break; case VBIC_MOD_RBP: mods->VBICparBaseResist = value->rValue; - if (mods->VBICparBaseResist < 0.01) mods->VBICparBaseResist = 0.01; + if (mods->VBICparBaseResist < R_MIN) { + mods->VBICparBaseResist = R_MIN; + } mods->VBICparBaseResistGiven = TRUE; break; case VBIC_MOD_IS: From 8285bd107a914edc4b504ee27952457ca03921ca Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 29 Nov 2019 20:12:30 -0500 Subject: [PATCH 05/58] Fixed compiler warnings regarding type of argument passed --- src/frontend/breakp.c | 2 +- src/frontend/device.c | 2 +- src/frontend/postcoms.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/breakp.c b/src/frontend/breakp.c index 0766361a3..3f74e6102 100644 --- a/src/frontend/breakp.c +++ b/src/frontend/breakp.c @@ -95,7 +95,7 @@ com_stop(wordlist *wl) charr[1] = copy("eq"); charr[2] = tokafter; charr[3] = NULL; - wln = wl_build(charr); + wln = wl_build((const char * const *) charr); wl_splice(wl->wl_next, wln); } /* continue with parsing the enhanced wordlist */ diff --git a/src/frontend/device.c b/src/frontend/device.c index 9c7e7a453..37c806b6f 100644 --- a/src/frontend/device.c +++ b/src/frontend/device.c @@ -1469,7 +1469,7 @@ com_alter_mod(wordlist *wl) } arglist[2] = inptoken; /* create a new wordlist from array arglist */ - newcommand = wl_build(arglist); + newcommand = wl_build((const char * const *) arglist); com_alter_common(newcommand->wl_next, 1); wl_free(newcommand); tfree(inptoken); diff --git a/src/frontend/postcoms.c b/src/frontend/postcoms.c index 728a305ee..e5da60b2e 100644 --- a/src/frontend/postcoms.c +++ b/src/frontend/postcoms.c @@ -579,7 +579,7 @@ com_write_sparam(wordlist *wl) sbuf[3] = "S12"; sbuf[4] = "S22"; sbuf[5] = NULL; - wl_sparam = wl_build(sbuf); + wl_sparam = wl_build((const char * const *) sbuf); names = ft_getpnames(wl_sparam, TRUE); if (names == NULL) From ba29c514f9e5495787172d7ca17a14b39c0617db Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 29 Nov 2019 22:22:04 -0500 Subject: [PATCH 06/58] Cleaned up code to locate a vector in a plot by name. The code was made more modular, and special names like allv are handled more efficiently --- src/frontend/vectors.c | 526 +++++++++++++++++++++-------------- src/include/ngspice/fteext.h | 3 +- 2 files changed, 318 insertions(+), 211 deletions(-) diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 3daba86cf..3d26bc95b 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -23,6 +23,17 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "ngspice/dstring.h" #include "plotting/plotting.h" + +static struct dvec *findvec_all(struct plot *pl); +static struct dvec *findvec_allv(struct plot *pl); +static struct dvec *findvec_alli(struct plot *pl); +static struct dvec *findvec_ally(struct plot *pl); +static struct dvec *find_permanent_vector_by_name( + NGHASHPTR pl_lookup_table, char *name); +static enum ALL_TYPE_ENUM get_all_type(const char *word); +static bool plot_prefix(const char *pre, const char *str); + + #ifdef XSPICE /* gtri - begin - add function prototype for EVTfindvec */ struct dvec *EVTfindvec(char *node); @@ -30,8 +41,7 @@ struct dvec *EVTfindvec(char *node); #endif -static void -vec_rebuild_lookup_table(struct plot *pl) +static void vec_rebuild_lookup_table(struct plot *pl) { if (pl->pl_lookup_table) { /* existing table */ nghash_empty(pl->pl_lookup_table, NULL, NULL); @@ -63,112 +73,107 @@ vec_rebuild_lookup_table(struct plot *pl) ds_free(&dbuf); } - pl->pl_lookup_valid = TRUE; + pl->pl_lookup_valid = TRUE; /* now lookup table valid */ } /* end of function vec_rebuild_lookup_table */ +enum ALL_TYPE_ENUM { + ALL_TYPE_NONE, + ALL_TYPE_ALL, + ALL_TYPE_ALLV, + ALL_TYPE_ALLI, + ALL_TYPE_ALLY +}; + + +/* Efficient identification of "all", "allv", "alli", "ally", and anything + * else */ +static enum ALL_TYPE_ENUM get_all_type(const char *word) +{ + /* Check for start of "all" */ + if (tolower(word[0] != 'a')) { + return ALL_TYPE_NONE; + } + if (tolower(word[1] != 'l')) { + return ALL_TYPE_NONE; + } + if (tolower(word[2] != 'l')) { + return ALL_TYPE_NONE; + } + + /* It may be some type of all */ + switch (tolower(word[3])) { + case '\0': + return ALL_TYPE_ALL; + case 'v': + if (word[4] == '\0') { + return ALL_TYPE_ALLV; + } + else { + return ALL_TYPE_NONE; + } + case 'i': + if (word[4] == '\0') { + return ALL_TYPE_ALLI; + } + else { + return ALL_TYPE_NONE; + } + case 'y': + if (word[4] == '\0') { + return ALL_TYPE_ALLY; + } + else { + return ALL_TYPE_NONE; + } + default: + return ALL_TYPE_NONE; + } /* end of swith over char after "all" */ +} /* end of function get_all_type */ + + + /* 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) { - struct dvec *d, *newv = NULL, *end = NULL, *v; - - if (pl == NULL) - return (NULL); - - if (cieq(word, "all")) { - for (d = pl->pl_dvecs; d; d = d->v_next) { - if (d->v_flags & VF_PERMANENT) { - if (d->v_link2) { - v = vec_copy(d); - vec_new(v); - } else { - v = d; - } - if (end) - end->v_link2 = v; - else - newv = v; - end = v; - } - } - return (newv); +static struct dvec *findvec(char *word, struct plot *pl) +{ + /* If no plot, cannot find */ + if (pl == NULL) { + return NULL; } - if (cieq(word, "allv")) { - for (d = pl->pl_dvecs; d; d = d->v_next) { - if ((d->v_flags & VF_PERMANENT) && (d->v_type == SV_VOLTAGE)) { - if (d->v_link2) { - v = vec_copy(d); - vec_new(v); - } else { - v = d; - } - if (end) - end->v_link2 = v; - else - newv = v; - end = v; - } - } - return (newv); + /* Identify and handle special cases all, allv, alli, ally */ + switch (get_all_type(word)) { + case ALL_TYPE_ALL: + return findvec_all(pl); + case ALL_TYPE_ALLV: + return findvec_allv(pl); + case ALL_TYPE_ALLI: + return findvec_alli(pl); + case ALL_TYPE_ALLY: + return findvec_ally(pl); + default: /* case ALL_TYPE_NOT_ALL -- not some type of ALL */ + break; } - if (cieq(word, "alli")) { - for (d = pl->pl_dvecs; d; d = d->v_next) { - if ((d->v_flags & VF_PERMANENT) && (d->v_type == SV_CURRENT)) { - if (d->v_link2) { - v = vec_copy(d); - vec_new(v); - } else { - v = d; - } - if (end) - end->v_link2 = v; - else - newv = v; - end = v; - } - } - return (newv); - } - - if (cieq(word, "ally")) { - for (d = pl->pl_dvecs; d; d = d->v_next) { - if ((d->v_flags & VF_PERMANENT) && (!cieq(d->v_name, pl->pl_scale->v_name))) { - if (d->v_link2) { - v = vec_copy(d); - vec_new(v); - } else { - v = d; - } - if (end) - end->v_link2 = v; - else - newv = v; - end = v; - } - } - return (newv); - } - - if (!pl->pl_lookup_valid) + /* The find is not for one of the "all" cases */ + if (!pl->pl_lookup_valid) { + /* Table lookup not valid, so rebuild to make valid */ vec_rebuild_lookup_table(pl); + } 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); + NGHASHPTR pl_lookup_table = pl->pl_lookup_table; + struct dvec *d = find_permanent_vector_by_name(pl_lookup_table, + lower_name); - for (d = nghash_find(pl->pl_lookup_table, lower_name); - d; - d = nghash_find_again(pl->pl_lookup_table, lower_name)) { - if (d->v_flags & VF_PERMANENT) - break; - } - + /* If the vector was not using the lowercased name, try finding it as + * v(lowercased name) */ if (!d) { ds_clear(&dbuf); bool f_ok = ds_cat_str(&dbuf, "v(") == DS_E_OK; @@ -179,12 +184,7 @@ findvec(char *word, struct plot *pl) { 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)) { - if (d->v_flags & VF_PERMANENT) - break; - } + d = find_permanent_vector_by_name(pl_lookup_table, node_name); } ds_free(&dbuf); @@ -192,8 +192,9 @@ findvec(char *word, struct plot *pl) { #ifdef XSPICE /* gtri - begin - Add processing for getting event-driven vector */ - if (!d) + if (!d) { d = EVTfindvec(word); + } /* gtri - end - Add processing for getting event-driven vector */ #endif @@ -207,11 +208,72 @@ findvec(char *word, struct plot *pl) { +/* Macro taking a function name and vector filter as arguments that + * generates the function that applies the filter */ +#define FINDVEC_ALL_GEN(fun_name, filter)\ +static struct dvec *fun_name(struct plot *pl)\ +{\ + struct dvec *d, *newv = NULL, *end = NULL, *v;\ + for (d = pl->pl_dvecs; d; d = d->v_next) {\ + if (filter) {\ + if (d->v_link2) {\ + v = vec_copy(d);\ + vec_new(v);\ + }\ + else {\ + v = d;\ + }\ + if (end) {\ + end->v_link2 = v;\ + }\ + else {\ + newv = v;\ + }\ + end = v;\ + }\ + } /* end of loop over vectors in plot */\ +\ + return newv;\ +} /* end of function */ + +/* Generate the functions for each filter */ +FINDVEC_ALL_GEN(findvec_all, d->v_flags & VF_PERMANENT) +FINDVEC_ALL_GEN(findvec_allv, + (d->v_flags & VF_PERMANENT) && (d->v_type == SV_VOLTAGE)) +FINDVEC_ALL_GEN(findvec_alli, + (d->v_flags & VF_PERMANENT) && (d->v_type == SV_CURRENT)) +FINDVEC_ALL_GEN(findvec_ally, + (d->v_flags & VF_PERMANENT) && + (!cieq(d->v_name, pl->pl_scale->v_name))) + + + +/* Find a permanent vector with the given name */ +static struct dvec *find_permanent_vector_by_name( + NGHASHPTR pl_lookup_table, char *name) +{ + struct dvec *d; + + /* Find the first vector with the given name and then find others + * until one having the VF_PERMANENT flag set is found. */ + for (d = nghash_find(pl_lookup_table, name); + d; + d = nghash_find_again(pl_lookup_table, name)) { + if (d->v_flags & VF_PERMANENT) { + /* A "permanent" vector was found with the name, so done */ + return d; + } + } /* end of loop over vectors in the plot having this name */ + + return (struct dvec *) NULL; /* not found */ +} /* end of function find_permanent_vector_by_name */ + + + /* If there are imbedded numeric strings, compare them numerically, not * alphabetically. */ -static int -namecmp(const void *a, const void *b) +static int namecmp(const void *a, const void *b) { int i, j; @@ -373,38 +435,44 @@ vec_remove(const char *name) * it checks for pre-defined vectors. */ -struct dvec * -vec_fromplot(char *word, struct plot *plot) { - struct dvec *d; - char buf[BSIZE_SP], buf2[BSIZE_SP], cc, *s; - - d = findvec(word, plot); - if (!d) { - (void) strcpy(buf, word); - strtolower(buf); - d = findvec(buf, plot); - } - if (!d) { - (void) strcpy(buf, word); - strtoupper(buf); - d = findvec(buf, plot); +struct dvec *vec_fromplot(char *word, struct plot *plot) { + struct dvec *d = findvec(word, plot); + if (d != (struct dvec *) NULL) { + return d; } - /* scanf("%c(%s)" doesn't do what it should do. ) */ - if (!d && (sscanf(word, "%c(%s", &cc, buf) == 2) && - ((s = strrchr(buf, ')')) != NULL) && - (s[1] == '\0')) { - *s = '\0'; - if (prefix("i(", word) || prefix("I(", word)) { - /* Spice dependency... */ - (void) sprintf(buf2, "%s#branch", buf); - (void) strcpy(buf, buf2); + /* Consider forms I(node) and i(node) -> node#branch */ + if (tolower(word[0]) != (int) 'i') { /* Not i of I.* */ + return (struct dvec *) NULL; + } + + if (word[1] != '(') { /* Not i or I(.* */ + return (struct dvec *) NULL; + } + + { + const char * const p_node = word + 2; + const char *p_end = p_node + strlen(p_node); + if (*--p_end != ')') { /* Not i or I then '(' with a closing ')' */ + return (struct dvec *) NULL; } - d = findvec(buf, plot); - } - return (d); -} + /* Form is correct, so see if node#branch exists */ + DS_CREATE(ds, 100); + if (ds_cat_mem(&ds, p_node, p_end - p_node) != DS_E_OK) { + controlled_exit(-1); + } + if (ds_cat_str(&ds, "#branch") != DS_E_OK) { + controlled_exit(-1); + } + + d = findvec(ds_get_buf(&ds), plot); + ds_free(&ds); + + return d; + } +} /* end of function vec_fromplot */ + /* This is the main lookup routine for names. The possible types of names are: @@ -484,7 +552,7 @@ vec_get(const char *vec_name) { } } - if (!d && (*word == SPECCHAR)) { + if (!d && (*word == SPECCHAR)) { /* "@" */ /* This is a special quantity... */ if (ft_nutmeg) { fprintf(cp_err, @@ -579,30 +647,37 @@ vec_get(const char *vec_name) { */ struct variable *nv; + /* Count the number of nodes in the list */ i = 0; - for (nv = vv->va_vlist; nv; nv = nv->va_next) + for (nv = vv->va_vlist; nv; nv = nv->va_next) { i++; + } - dvec_realloc(d, i, NULL); + dvec_realloc(d, i, NULL); /* Resize to # nodes */ + /* Step through the list again, setting values this time */ i = 0; - for (nv = vv->va_vlist; nv; nv = nv->va_next) + for (nv = vv->va_vlist; nv; nv = nv->va_next) { d->v_realdata[i++] = nv->va_real; + } /* To be able to identify the vector to represent * belongs to a special "conunto" and should be printed in a * special way. */ d->v_dims[1] = 1; - } else if (vv->va_type == CP_NUM) { /* Variable is an integer */ + } + else if (vv->va_type == CP_NUM) { /* Variable is an integer */ *d->v_realdata = (double) vv->va_num; - } else if (vv->va_type == CP_REAL) { /* Variable is a real */ + } + else if (vv->va_type == CP_REAL) { /* Variable is a real */ if (!(vv->va_next)) { /* Only a real data * usually normal */ *d->v_realdata = vv->va_real; - } else { + } + else { /* Real data set * When you print a model @ [all] * Just print numerical values, not the string @@ -632,8 +707,9 @@ vec_get(const char *vec_name) { } nv = nv->va_next; - if (!nv) + if (!nv) { break; + } } /* To distinguish those does not take anything for print screen to @@ -648,7 +724,7 @@ vec_get(const char *vec_name) { tfree(wd); vec_new(d); tfree(whole); - return (d); + return d; } tfree(wd); @@ -676,27 +752,29 @@ plot_docoms(wordlist *wl) } -/* Create a copy of a vector. */ - -struct dvec * -vec_copy(struct dvec *v) { +/* Create a copy of a vector. The vector is not "permananent" */ +struct dvec *vec_copy(struct dvec *v) { struct dvec *nv; - int i; - if (!v) - return (NULL); + if (!v) { + return (struct dvec *) NULL; + } + /* Make a copy with the VF_PERMANENT bit cleared in v_flags */ nv = dvec_alloc(copy(v->v_name), v->v_type, v->v_flags & ~VF_PERMANENT, v->v_length, NULL); - if (isreal(v)) - memcpy(nv->v_realdata, v->v_realdata, + /* Copy the data to the new vector */ + if (isreal(v)) { + (void) memcpy(nv->v_realdata, v->v_realdata, sizeof(double) * (size_t) v->v_length); - else - memcpy(nv->v_compdata, v->v_compdata, + } + else { + (void) memcpy(nv->v_compdata, v->v_compdata, sizeof(ngcomplex_t) * (size_t) v->v_length); + } nv->v_minsignal = v->v_minsignal; nv->v_maxsignal = v->v_maxsignal; @@ -719,23 +797,26 @@ vec_copy(struct dvec *v) { nv->v_color = 0; /*XXX???*/ nv->v_defcolor = v->v_defcolor; nv->v_numdims = v->v_numdims; - for (i = 0; i < v->v_numdims; i++) - nv->v_dims[i] = v->v_dims[i]; + + /* Copy defined dimensions */ + (void) memcpy(nv->v_dims, v->v_dims, v->v_numdims * sizeof *v->v_dims); + nv->v_plot = v->v_plot; nv->v_next = NULL; nv->v_link2 = NULL; nv->v_scale = v->v_scale; - return (nv); -} + return nv; +} /* end of function vec_copy */ + /* Create a new plot structure. This just fills in the typename and sets up * the ccom struct. */ -struct plot * -plot_alloc(char *name) { +struct plot * plot_alloc(char *name) +{ struct plot *pl = TMALLOC(struct plot, 1), *tp; char *s; struct ccom *ccom; @@ -841,71 +922,93 @@ vec_gc(void) * in a plot are gone it stays around... */ -void -vec_free_x(struct dvec *v) +void vec_free_x(struct dvec *v) { - struct plot *pl; - - if ((v == NULL) || (v->v_name == NULL)) + /* Do not free if NULL or name is NULL. The second possibility is a + * special case */ + if ((v == NULL) || (v->v_name == NULL)) { return; - pl = v->v_plot; + } + struct plot * const pl = v->v_plot; /* Now we have to take this dvec out of the plot list. */ if (pl != NULL) { pl->pl_lookup_valid = FALSE; + + /* If at head of list of vectors in the plot, make the next one + * the new head of the list */ if (pl->pl_dvecs == v) { pl->pl_dvecs = v->v_next; - } else { + } + else { + /* Not at head of list so must locate and fix links */ struct dvec *lv = pl->pl_dvecs; - if (lv) - for (; lv->v_next; lv = lv->v_next) - if (lv->v_next == v) + if (lv) { /* the plot has at least one vector */ + for ( ; lv->v_next; lv = lv->v_next) { + if (lv->v_next == v) { /* found prev vector */ break; - if (lv && lv->v_next) + } + } + } + + /* If found in the list, link prev vector to next one */ + if (lv && lv->v_next) { lv->v_next = v->v_next; - else - fprintf(cp_err, + } + else { + (void) fprintf(cp_err, "vec_free: Internal Error: %s not in plot\n", v->v_name); - } + } + } /* end of case that vector being freed is not at head of list */ + if (pl->pl_scale == v) { - if (pl->pl_dvecs) + if (pl->pl_dvecs) { pl->pl_scale = pl->pl_dvecs; /* Random one... */ - else + } + else { pl->pl_scale = NULL; + } + } + } /* end of case that have a plot */ + + dvec_free(v); +} /* end of function vec_free_x */ + + + +/* This function returns TRUE if every element of v and every element of + * every vector linked to v through v_link2 is zero and FALSE otherwise. */ +bool vec_iszero(const struct dvec *v) +{ + for (; v; v = v->v_link2) { /* step through linked vectors */ + if (isreal(v)) { /* current vector is real */ + const int n = v->v_length; + int i; + for (i = 0; i < n; i++) { + if (v->v_realdata[i] != 0.0) { + return FALSE; + } + } + } + else { /* current vector is complex */ + const int n = v->v_length; + int i; + for (i = 0; i < n; i++) { + if (realpart(v->v_compdata[i]) != 0.0) { + return FALSE; + } + if (imagpart(v->v_compdata[i]) != 0.0) { + return FALSE; + } + } } } - dvec_free(v); -} + return TRUE; /* every value tested was 0.0 */ +} /* end of function vec_iszero */ -/* - * return TRUE if every vector element is zero - */ - -bool -vec_iszero(struct dvec *v) -{ - int i; - - for (; v; v = v->v_link2) - if (isreal(v)) - for (i = 0; i < v->v_length; i++) { - if (v->v_realdata[i] != 0.0) - return FALSE; - } - else - for (i = 0; i < v->v_length; i++) { - if (realpart(v->v_compdata[i]) != 0.0) - return FALSE; - if (imagpart(v->v_compdata[i]) != 0.0) - return FALSE; - } - - return TRUE; -} - /* This is something we do in a few places... Since vectors get copied a lot, * we can't just compare pointers to tell if two vectors are 'really' the same. @@ -962,15 +1065,20 @@ vec_basename(struct dvec *v) } /* get address of plot named 'name' */ -struct plot * -get_plot(char* name) { +struct plot *get_plot(char* name) + + +{ struct plot *pl; - for (pl = plot_list; pl; pl = pl->pl_next) - if (plot_prefix(name, pl->pl_typename)) + for (pl = plot_list; pl; pl = pl->pl_next) { + if (plot_prefix(name, pl->pl_typename)) { return pl; + } + } fprintf(cp_err, "Error: no such plot named %s\n", name); - return NULL; -} + return (struct plot *) NULL; +} /* end of function get_plot */ + /* Make a plot the current one. This gets called by cp_usrset() when one @@ -1177,16 +1285,16 @@ vec_mkfamily(struct dvec *v) { /* This function will match "op" with "op1", but not "op1" with "op12". */ - -bool -plot_prefix(char *pre, char *str) +static bool plot_prefix(const char *pre, const char *str) { - if (!*pre) - return (TRUE); + if (!*pre) { /* prefix is empty string */ + return TRUE; /* Define "" to be prefix */ + } while (*pre && *str) { - if (*pre != *str) + if (*pre != *str) { /* stop at first mismatch */ break; + } pre++; str++; } diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 6c35ae118..9a4c9bae0 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -336,7 +336,7 @@ extern int ft_typnum(char *); /* vectors.c */ -extern bool vec_iszero(struct dvec *v); +extern bool vec_iszero(const struct dvec *v); extern bool vec_eq(struct dvec *v1, struct dvec *v2); extern int plot_num; extern struct dvec *vec_fromplot(char *word, struct plot *plot); @@ -363,7 +363,6 @@ extern void plot_setcur(char *name); extern struct plot *get_plot(char* name); extern void plot_new(struct plot *pl); extern char *vec_basename(struct dvec *v); -extern bool plot_prefix(char *pre, char *str); extern void vec_transpose(struct dvec *v); /* main.c */ From ada262488b34878e44f69be6c0eef6b29a214081 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 29 Nov 2019 22:45:56 -0500 Subject: [PATCH 07/58] The check for the scale vector was not comparing the name in a case-insensitive manner, so unlet Yes would delete the default yes scale vector, for example. That issue was corrected. The code was also made more modular and descriptive regarding the check for the scale vector. Also, the warning now prints the scale vector as it is stored to make clear that vector names are not case sensitive. --- src/frontend/postcoms.c | 57 ++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/src/frontend/postcoms.c b/src/frontend/postcoms.c index e5da60b2e..978bdecf2 100644 --- a/src/frontend/postcoms.c +++ b/src/frontend/postcoms.c @@ -31,31 +31,49 @@ static void DelPlotWindows(struct plot *pl); /* check if the user want's to delete the scale vector of the current plot. This should not happen, because then redrawing the graph crashes ngspice */ static bool -check_cp(char* vecname) +is_scale_vec_of_current_plot(const char *v_name) { - if (plot_cur && plot_cur->pl_scale && plot_cur->pl_scale->v_name && eq(plot_cur->pl_scale->v_name, vecname)) { - fprintf(cp_err, "\nWarning: Scale vector '%s' of current plot cannot be deleted!\n", vecname); - fprintf(cp_err, " Command 'unlet %s' is ignored.\n\n", vecname); - return TRUE; - } - else + if (!plot_cur) { /* no current plot */ return FALSE; -} + } + const struct dvec * const pl_scale = plot_cur->pl_scale; + if (!pl_scale) { /* no scale vector */ + return FALSE; + } + + /* Test if this vector's name matches the scale vector's name */ + return cieq(v_name, pl_scale->v_name); +} /* end of function is_scale_vec_of_current_plot */ + + + +/* Remove vectors in the wordlist from the current plot */ void com_unlet(wordlist *wl) { - while (wl) { - /* don't delete the scale vector of the current plot */ - if (!check_cp(wl->wl_word)) - vec_remove(wl->wl_word); - wl = wl->wl_next; - } -} + for ( ; wl != (wordlist *) NULL; wl = wl->wl_next) { + /* Don't delete the scale vector of the current plot */ + const char * const vector_name = wl->wl_word; + if (is_scale_vec_of_current_plot(vector_name)) { + /* If it is the scale vector of the current plot, print a + * warning. Note that if it is true, the scale vector name must + * exist, so no part of plot_cur->pl_scale->v_name can be null. */ + fprintf(cp_err, + "\nWarning: Scale vector '%s' of the current plot " + "cannot be deleted!\n" + "Command 'unlet %s' is ignored.\n\n", + plot_cur->pl_scale->v_name, vector_name); + } + else { + vec_remove(vector_name); + } + } /* end of loop over vectors to delete */ +} /* end of function com_unlet */ + /* Load in a file. */ - void com_load(wordlist *wl) { @@ -461,7 +479,7 @@ com_write(wordlist *wl) /* Note that since we are building a new plot * we don't want to vec_new this one... */ - tfree(vv->v_name); + txfree(vv->v_name); vv->v_name = basename; if (end) @@ -512,10 +530,7 @@ com_write(wordlist *wl) /* Otherwise loop through again... */ } - if (ascii) - raw_write(file, &newplot, appendwrite, FALSE); - else - raw_write(file, &newplot, appendwrite, TRUE); + raw_write(file, &newplot, appendwrite, !ascii); for (vv = newplot.pl_dvecs; vv;) { struct dvec *next_vv = vv->v_next; From 70e6318915ff4d3078139e7d169e4e6b37f00560 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 29 Nov 2019 23:32:13 -0500 Subject: [PATCH 08/58] If the prompt variable was set to a value that is not a string, memory that has been freed would be used when displaying the command prompt. This could lead to strange prompts and possible access violations, although it may appear to work OK if the freed memory has not been modified. This issue is fixed. Some useless duplications of strings when defining variables are also removed. --- src/frontend/variable.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 40f7e913a..d68ef8766 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -163,8 +163,14 @@ cp_vset(char *varname, enum cp_types type, void *value) cp_noclobber = TRUE; else if (eq(varname, "echo")) /*CDHW*/ cp_echo = TRUE; /*CDHW*/ - else if (eq(copyvarname, "prompt") && (type == CP_STRING)) - cp_promptstring = v->va_string; + else if (eq(copyvarname, "prompt")) { + if (type == CP_STRING) { + cp_promptstring = v->va_string; + } + else { /* use a default string since prompt is not a string */ + cp_promptstring = "-> "; + } + } else if (eq(copyvarname, "ignoreeof")) cp_ignoreeof = TRUE; else if (eq(copyvarname, "cpdebug")) { @@ -272,7 +278,11 @@ cp_setparse(wordlist *wl) struct variable *listv = NULL, *vv, *lv = NULL; struct variable *vars = NULL; int balance; - + /* Step through the list of words. Words may be various combinations of + * the information needed to set a variable. For example, to set x to + * the value 3, the data could be supplied as one word x=3, two words + * x= 3 or x =3 or three words x = 3. Additionally words may be quoted + * or unquoted. Each iteration through the loop handles one variable */ while (wl) { if (name) { @@ -283,8 +293,8 @@ cp_setparse(wordlist *wl) wl = wl->wl_next; if ((!wl || (*wl->wl_word != '=')) && !strchr(name, '=')) { - vars = var_alloc_bool(copy(name), TRUE, vars); - tfree(name); /*DG: cp_unquote Memory leak*/ + vars = var_alloc_bool(name, TRUE, vars); + name = (char *) NULL; /* Given to variable vars */ continue; } @@ -392,18 +402,19 @@ cp_setparse(wordlist *wl) td = ft_numparse(&ss, FALSE); if (td) { /*** We should try to get CP_NUM's... */ - vars = var_alloc_real(copy(name), *td, vars); + vars = var_alloc_real(name, *td, vars); } else { - vars = var_alloc_string(copy(name), copy(val), vars); + vars = var_alloc_string(name, copy(val), vars); } } + name = (char *) NULL; /* name given to variable via var_alloc_* */ tfree(copyval); /*DG: must free ss any way to avoid cp_unquote memory leak */ - tfree(name); /* va: cp_unquote memory leak: free name for every loop */ } - if (name) + if (name) { tfree(name); + } return (vars); } From bdac21da035b5514e94d2e187ce8dbba263332be Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Jan 2020 10:59:50 +0100 Subject: [PATCH 09/58] Update to Visual Studio 2019 --- visualc/vngspice.vcxproj | 5342 ++++++++++++++--------------- visualc/xspice/analog.vcxproj | 10 +- visualc/xspice/cmpp/cmpp.vcxproj | 10 +- visualc/xspice/digital.vcxproj | 14 +- visualc/xspice/spice2poly.vcxproj | 12 +- visualc/xspice/table.vcxproj | 10 +- visualc/xspice/xtradev.vcxproj | 10 +- visualc/xspice/xtraevt.vcxproj | 10 +- 8 files changed, 2709 insertions(+), 2709 deletions(-) diff --git a/visualc/vngspice.vcxproj b/visualc/vngspice.vcxproj index 123d0cffb..efd8222af 100644 --- a/visualc/vngspice.vcxproj +++ b/visualc/vngspice.vcxproj @@ -1,2686 +1,2686 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - console_debug - Win32 - - - console_release - Win32 - - - console_debug - x64 - - - console_release - x64 - - - ReleaseOMP - Win32 - - - ReleaseOMP - x64 - - - console_release_omp - Win32 - - - console_release_omp - x64 - - - - {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A} - vngspice - Win32Proj - - - - Application - v140 - - - Application - true - v140 - - - Application - v140 - - - Application - true - v140 - - - Application - v140 - - - Application - true - v140 - - - Application - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - Application - true - v140 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - ngspice - $(ProjectName)\$(Configuration).$(Platform)\ - $(ProjectName)\$(Configuration).$(Platform)\obj\ - - - false - false - - - false - - - false - false - true - - - false - true - - - false - false - - - false - - - false - false - true - - - false - true - - - false - - - false - true - - - false - - - false - true - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - Disabled - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;%(PreprocessorDefinitions) - false - - - Default - MultiThreadedDebug - false - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 0 - 0 - false - - - MachineX86 - true - - - make-install-vngspiced.bat $(OutDir) - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 0 - 0 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX86 - true - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + console_debug + Win32 + + + console_release + Win32 + + + console_debug + x64 + + + console_release + x64 + + + ReleaseOMP + Win32 + + + ReleaseOMP + x64 + + + console_release_omp + Win32 + + + console_release_omp + x64 + + + + {83E315C7-EDD3-4F6B-AF28-87A92A4FA49A} + vngspice + Win32Proj + + + + Application + v142 + + + Application + true + v142 + + + Application + v142 + + + Application + true + v142 + + + Application + v142 + + + Application + true + v142 + + + Application + v142 + + + Application + true + v142 + + + Application + true + v142 + + + Application + true + v142 + + + Application + true + v142 + + + Application + true + v142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ngspice + $(ProjectName)\$(Configuration).$(Platform)\ + $(ProjectName)\$(Configuration).$(Platform)\obj\ + + + false + false + + + false + + + false + false + true + + + false + true + + + false + false + + + false + + + false + false + true + + + false + true + + + false + + + false + true + + + false + + + false + true + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + Disabled + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;%(PreprocessorDefinitions) + false + + + Default + MultiThreadedDebug + false + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 0 + 0 + false + + + MachineX86 + true + + + make-install-vngspiced.bat $(OutDir) + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 0 + 0 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX86 + true + + make-install-vngspice.bat $(OutDir) - - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - Disabled - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONFIG64;%(PreprocessorDefinitions) - false - - - Default - MultiThreadedDebug - false - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 4194304 - 1048576 - false - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + Disabled + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONFIG64;%(PreprocessorDefinitions) + false + + + Default + MultiThreadedDebug + false + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 4194304 + 1048576 + false + + + MachineX64 + true + + make-install-vngspiced.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONFIG64;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - false - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 4194304 - 1048576 - - - - - UseLinkTimeCodeGeneration - true - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONFIG64;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + false + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 4194304 + 1048576 + + + + + UseLinkTimeCodeGeneration + true + + + MachineX64 + true + + make-install-vngspice.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - Disabled - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONSOLE;%(PreprocessorDefinitions) - false - - - Default - MultiThreadedDebug - false - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 0 - 0 - false - - - MachineX86 - true - - - make-install-vngspiced.bat $(OutDir) - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 0 - 0 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX86 - true - - - make-install-vngspice.bat $(OutDir) - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - Disabled - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONSOLE;CONFIG64;%(PreprocessorDefinitions) - false - - - Default - MultiThreadedDebug - false - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 4194304 - 1048576 - false - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + Disabled + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONSOLE;%(PreprocessorDefinitions) + false + + + Default + MultiThreadedDebug + false + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 0 + 0 + false + + + MachineX86 + true + + + make-install-vngspiced.bat $(OutDir) + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 0 + 0 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX86 + true + + + make-install-vngspice.bat $(OutDir) + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + Disabled + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);NGDEBUG;CONSOLE;CONFIG64;%(PreprocessorDefinitions) + false + + + Default + MultiThreadedDebug + false + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 4194304 + 1048576 + false + + + MachineX64 + true + + make-install-vngspiced.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;CONFIG64;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 4194304 - 1048576 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;CONFIG64;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 4194304 + 1048576 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX64 + true + + make-install-vngspice.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);USE_OMP;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 0 - 0 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX86 - true - - - make-install-vngspice.bat $(OutDir) - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);USE_OMP;CONFIG64;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Windows - 0 - 0 - 4194304 - 1048576 - - - - - UseLinkTimeCodeGeneration - true - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);USE_OMP;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 0 + 0 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX86 + true + + + make-install-vngspice.bat $(OutDir) + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);USE_OMP;CONFIG64;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Windows + 0 + 0 + 4194304 + 1048576 + + + + + UseLinkTimeCodeGeneration + true + + + MachineX64 + true + + make-install-vngspice.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;USE_OMP;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 0 - 0 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX86 - true - - - make-install-vngspice.bat $(OutDir) - - - $(ProjectDir)ngspice-x86.exe.manifest - - - - - force recompilation of conf.c with actual date - if exist $(IntDir)conf.obj del $(IntDir)conf.obj - - - X64 - - - MaxSpeed - true - Speed - true - ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;CONFIG64;USE_OMP;%(PreprocessorDefinitions) - false - - - Default - MultiThreaded - true - true - - - Level4 - ProgramDatabase - CompileAsC - true - - - psapi.lib;%(AdditionalDependencies) - true - Console - 0 - 0 - 4194304 - 1048576 - true - true - UseLinkTimeCodeGeneration - true - - - MachineX64 - true - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;USE_OMP;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 0 + 0 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX86 + true + + + make-install-vngspice.bat $(OutDir) + + + $(ProjectDir)ngspice-x86.exe.manifest + + + + + force recompilation of conf.c with actual date + if exist $(IntDir)conf.obj del $(IntDir)conf.obj + + + X64 + + + MaxSpeed + true + Speed + true + ..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\include;..\src\spicelib\parser;.;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;_MSC_PLATFORM_TOOLSET=$(PlatformToolsetVersion);CONSOLE;CONFIG64;USE_OMP;%(PreprocessorDefinitions) + false + + + Default + MultiThreaded + true + true + + + Level4 + ProgramDatabase + CompileAsC + true + + + psapi.lib;%(AdditionalDependencies) + true + Console + 0 + 0 + 4194304 + 1048576 + true + true + UseLinkTimeCodeGeneration + true + + + MachineX64 + true + + make-install-vngspice.bat $(OutDir) 64 - - - - $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - invoke win_bison.exe for %(Identity) - ..\..\flex-bison\win_bison.exe --output=.\tmp-bison\%(Filename).c --defines=.\tmp-bison\%(Filename).h %(Identity) || exit 1 - .\tmp-bison\%(Filename).c;.\tmp-bison\%(Filename).h - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + $(ProjectDir)ngspice.exe.manifest %(AdditionalManifestFiles) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + invoke win_bison.exe for %(Identity) + ..\..\flex-bison\win_bison.exe --output=.\tmp-bison\%(Filename).c --defines=.\tmp-bison\%(Filename).h %(Identity) || exit 1 + .\tmp-bison\%(Filename).c;.\tmp-bison\%(Filename).h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/visualc/xspice/analog.vcxproj b/visualc/xspice/analog.vcxproj index b720cba92..14576b480 100644 --- a/visualc/xspice/analog.vcxproj +++ b/visualc/xspice/analog.vcxproj @@ -22,30 +22,30 @@ analog {8271FEA2-8AC0-4B6D-BAEA-A503D37B5DB2} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 diff --git a/visualc/xspice/cmpp/cmpp.vcxproj b/visualc/xspice/cmpp/cmpp.vcxproj index 6865a50a8..45cecc0ec 100644 --- a/visualc/xspice/cmpp/cmpp.vcxproj +++ b/visualc/xspice/cmpp/cmpp.vcxproj @@ -21,32 +21,32 @@ {7C865696-FA10-43AE-A20B-22AE72A165E2} cmpp - 8.1 + 10.0 Application true - v140 + v142 MultiByte Application false - v140 + v142 true MultiByte Application true - v140 + v142 MultiByte Application false - v140 + v142 true MultiByte diff --git a/visualc/xspice/digital.vcxproj b/visualc/xspice/digital.vcxproj index 23957daf2..f48a5e999 100644 --- a/visualc/xspice/digital.vcxproj +++ b/visualc/xspice/digital.vcxproj @@ -22,30 +22,30 @@ digital {9ABEC5F2-F6C6-41DE-88AB-02460A07F46E} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 @@ -242,7 +242,7 @@ - + ..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories) @@ -329,7 +329,7 @@ - + diff --git a/visualc/xspice/spice2poly.vcxproj b/visualc/xspice/spice2poly.vcxproj index 96864c5dd..d72ac9cda 100644 --- a/visualc/xspice/spice2poly.vcxproj +++ b/visualc/xspice/spice2poly.vcxproj @@ -22,30 +22,30 @@ spice2poly {D701EA0E-B8B0-41D6-A90E-A0D8233F15FB} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 @@ -209,7 +209,7 @@ - + diff --git a/visualc/xspice/table.vcxproj b/visualc/xspice/table.vcxproj index 6d3d134d6..ec972ee53 100644 --- a/visualc/xspice/table.vcxproj +++ b/visualc/xspice/table.vcxproj @@ -22,30 +22,30 @@ table {7A6473F5-AFED-4910-88D2-6204DA829832} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 diff --git a/visualc/xspice/xtradev.vcxproj b/visualc/xspice/xtradev.vcxproj index 04590d0ba..31bf6689f 100644 --- a/visualc/xspice/xtradev.vcxproj +++ b/visualc/xspice/xtradev.vcxproj @@ -22,30 +22,30 @@ xtradev {4BB60215-9A09-4192-9DB6-1A0CA823AFCA} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 diff --git a/visualc/xspice/xtraevt.vcxproj b/visualc/xspice/xtraevt.vcxproj index 98a1bc6e4..e83a4c5c6 100644 --- a/visualc/xspice/xtraevt.vcxproj +++ b/visualc/xspice/xtraevt.vcxproj @@ -22,30 +22,30 @@ xtraevt {13500662-AF0B-4AB6-9AF9-BC3E07B5C1C6} icmanalog - 8.1 + 10.0 DynamicLibrary NotSet - v140 + v142 DynamicLibrary NotSet true - v140 + v142 DynamicLibrary NotSet - v140 + v142 DynamicLibrary MultiByte true - v140 + v142 From 3b5773fb8420a0c707ea5e94b96eca30e19b8096 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Thu, 5 Dec 2019 00:37:32 -0500 Subject: [PATCH 10/58] Fixed usage of new dstring functions. Also added const to some parameters that did not change. --- src/frontend/numparam/mystring.c | 3 ++- src/frontend/numparam/numpaif.h | 2 +- src/frontend/numparam/numparam.h | 2 +- src/frontend/numparam/spicenum.c | 7 ++++--- src/frontend/numparam/xpressn.c | 2 +- src/frontend/subckt.c | 11 +++++++---- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/frontend/numparam/mystring.c b/src/frontend/numparam/mystring.c index 297b7a970..d699522f6 100644 --- a/src/frontend/numparam/mystring.c +++ b/src/frontend/numparam/mystring.c @@ -98,7 +98,7 @@ cadd(DSTRINGPTR dstr_p, char c) void scopyd(DSTRINGPTR dst, const DSTRINGPTR src) /* returns success flag */ { - (void) ds_clear(dst); + ds_clear(dst); if (ds_cat_ds(dst, src) != DS_E_OK) { controlled_exit(-1); } @@ -127,6 +127,7 @@ pscopy(DSTRINGPTR dstr_p, const char *t, const char *stop) stop = strchr(t, '\0'); } + ds_clear(dstr_p); if (ds_cat_mem(dstr_p, t, stop - t) != DS_E_OK) { controlled_exit(-1); } diff --git a/src/frontend/numparam/numpaif.h b/src/frontend/numparam/numpaif.h index 6a1d84f26..800f4c701 100644 --- a/src/frontend/numparam/numpaif.h +++ b/src/frontend/numparam/numpaif.h @@ -16,7 +16,7 @@ struct card; extern char *nupa_copy(struct card *c); extern int nupa_eval(struct card *card); extern void nupa_signal(int sig); -extern void nupa_scan(struct card *card); +extern void nupa_scan(const struct card *card); extern void nupa_list_params(FILE *cp_out); extern double nupa_get_param(char *param_name, int *found); extern void nupa_add_param(char *param_name, double value); diff --git a/src/frontend/numparam/numparam.h b/src/frontend/numparam/numparam.h index d9258787d..e5076a4c2 100644 --- a/src/frontend/numparam/numparam.h +++ b/src/frontend/numparam/numparam.h @@ -58,7 +58,7 @@ typedef struct { /* the input scanner data structure */ void initdico(dico_t *); int donedico(dico_t *); void dico_free_entry(entry_t *); -bool defsubckt(dico_t *, struct card *); +bool defsubckt(dico_t *, const struct card *); int findsubckt(dico_t *, const char *s); bool nupa_substitute(dico_t *, const char *s, char *r); bool nupa_assignment(dico_t *, const char *s, char mode); diff --git a/src/frontend/numparam/spicenum.c b/src/frontend/numparam/spicenum.c index d4405964b..52255a050 100644 --- a/src/frontend/numparam/spicenum.c +++ b/src/frontend/numparam/spicenum.c @@ -246,8 +246,9 @@ transform(dico_t *dico, DSTRINGPTR dstr_p, bool incontrol) char *params; /* split off any "params" tail */ params = strstr(s, "params:"); - if (params) - pscopy(dstr_p, s, params); + if (params) { + ds_set_length(dstr_p, params - s); + } category = 'S'; } else if (prefix(".control", s)) { category = 'C'; @@ -387,7 +388,7 @@ nupa_done(void) /* SJB - Scan the line for subcircuits */ void -nupa_scan(struct card *card) +nupa_scan(const struct card *card) { defsubckt(dicoS, card); } diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index cdd5299d0..78fb231c9 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -494,7 +494,7 @@ nupa_define(dico_t *dico, bool -defsubckt(dico_t *dico, struct card *card) +defsubckt(dico_t *dico, const struct card *card) /* called on 1st pass of spice source code, to enter subcircuit names */ diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index fcaa359b0..fcdcce9ee 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -239,13 +239,17 @@ inp_subcktexpand(struct card *deck) { nupa_signal(NUPADECKCOPY); /* get the subckt names from the deck */ - for (c = deck; c; c = c->nextcard) /* first Numparam pass */ - if (ciprefix(".subckt", c->line)) + for (c = deck; c; c = c->nextcard) { /* first Numparam pass */ + if (ciprefix(".subckt", c->line)) { nupa_scan(c); + } + } + /* now copy instances */ for (c = deck; c; c = c->nextcard) { /* first Numparam pass */ - if (*(c->line) == '*') + if (*(c->line) == '*') { continue; + } c->line = nupa_copy(c); } @@ -456,7 +460,6 @@ doit(struct card *deck, wordlist *modnames) { struct card *prev_of_c = NULL; while (c) { - if (ciprefix(sbend, c->line)) { /* if line == .ends */ fprintf(cp_err, "Error: misplaced %s line: %s\n", sbend, c->line); From 4081013b13bd79b0de4c8faa9614104cee693e70 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Thu, 5 Dec 2019 14:38:57 -0500 Subject: [PATCH 11/58] Added no_histsubst option and related fixes --- src/frontend/com_set.c | 51 ++++--- src/frontend/options.c | 5 +- src/frontend/parser/cshpar.c | 54 +++++-- src/frontend/variable.c | 267 +++++++++++++++++++++++---------- src/include/ngspice/cpextern.h | 3 +- 5 files changed, 264 insertions(+), 116 deletions(-) diff --git a/src/frontend/com_set.c b/src/frontend/com_set.c index 688c22feb..69014177b 100644 --- a/src/frontend/com_set.c +++ b/src/frontend/com_set.c @@ -9,25 +9,36 @@ wordlist* readifile(wordlist*); -/* The set command. Syntax is set [opt ...] [opt = val ...]. Val may - * be a string, an int, a float, or a list of the form (elt1 elt2 - * ...). */ -void -com_set(wordlist *wl) +/* The set command. + * + * Syntax is set [var [= val] ...] + * + * var is the name of the variable to be defined. Quoting allows special + * characters such as = to be included as part of the name. + * val may be a string, an int, a float, a bool, or a list of the form + * ( elt1 ... ). + * + * With no var value, all variables that are currently defined are printed + * Without the "= val" portion, the variable becomes a Boolean set to true. + * Lists must have spaces both after the leading '(' and before the + * trailing ')'. Individual elements may be of any type. + * Quoted expressions are taken to be strings in all cases and quoting a + * grouping character ("(" or ")") suppresses its special properties. + * Further, words "(" and ")" within a list are ordinary words. + * + * This function may alter the input wordlist, but on return its resources + * can be freed in the normal manner. + */ +void com_set(wordlist *wl) { - struct variable *vars, *oldvar; - - if (wl == NULL) { + /* Handle case of printing defined variables */ + if (wl == (wordlist *) NULL) { cp_vprint(); return; } - /* special case input redirection*/ - wordlist *ww = wl->wl_next; - if (ww && eq(ww->wl_word, "<")) - wl = readifile(wl); - - vars = cp_setparse(wl); + struct variable *oldvar; + struct variable *vars = cp_setparse(wl); /* This is sort of a hassle... */ while (vars) { @@ -54,12 +65,14 @@ com_set(wordlist *wl) cp_vset(vars->va_name, vars->va_type, s); oldvar = vars; vars = vars->va_next; - /* va: avoid memory leak: free oldvar carefully */ - tfree(oldvar->va_name); - if (oldvar->va_type == CP_STRING) - tfree(oldvar->va_string); /* copied in cp_vset */ + + /* Free allocations associated with the current variable */ + txfree(oldvar->va_name); + if (oldvar->va_type == CP_STRING){ + txfree(oldvar->va_string); /* copied in cp_vset */ + } /* don't free oldvar->va_list! This structure is used furthermore! */ - tfree(oldvar); + txfree(oldvar); } } diff --git a/src/frontend/options.c b/src/frontend/options.c index d80d2003e..a4f423b27 100644 --- a/src/frontend/options.c +++ b/src/frontend/options.c @@ -140,7 +140,8 @@ cp_usrvars(void) } return v; -} +} /* end of function cp_usrvars */ + /* Extract the .option lines from the deck */ @@ -226,8 +227,6 @@ cp_usrset(struct variable *var, bool isset) fprintf(cp_err, "Warning: %s compiled without debug messages\n", cp_program); #endif - } else if (eq(var->va_name, "program")) { - cp_program = var->va_string; } else if (eq(var->va_name, "rawfile")) { ft_rawfile = copy(var->va_string); } else if (eq(var->va_name, "acct")) { diff --git a/src/frontend/parser/cshpar.c b/src/frontend/parser/cshpar.c index 8d0639ea3..35a31c9e9 100644 --- a/src/frontend/parser/cshpar.c +++ b/src/frontend/parser/cshpar.c @@ -38,6 +38,8 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #endif +bool cp_no_histsubst = FALSE; /* perform history substitution by default */ + /* Things go as follows: * (1) Read the line and do some initial quoting (by setting the 8th bit), * and command ignoring. Also deal with command completion. @@ -54,29 +56,48 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group static void pwlist(wordlist *wlist, char *name); -wordlist * -cp_parse(char *string) +wordlist *cp_parse(char *string) { wordlist *wlist; wlist = cp_lexer(string); - if (!string) - cp_event++; + /* Test for valid wordlist */ + if (!wlist) { + return (wordlist *) NULL; + } + if (!wlist->wl_word) { + wl_free(wlist); + return (wordlist *) NULL; + } - if (!wlist || !wlist->wl_word) - return (wlist); + if (!string) { /* cp_lexer read user data */ + cp_event++; + } pwlist(wlist, "Initial parse"); - wlist = cp_histsubst(wlist); - if (!wlist || !wlist->wl_word) - return (wlist); - pwlist(wlist, "After history substitution"); - if (cp_didhsubst) { - wl_print(wlist, stdout); - putc('\n', stdout); - } + /* Do history substitution (!1, etc.) if enabled */ + if (!cp_no_histsubst) { + wlist = cp_histsubst(wlist); + + /* Test for valid wordlist */ + if (!wlist) { + return (wordlist *) NULL; + } + if (!wlist->wl_word) { + wl_free(wlist); + return (wordlist *) NULL; + } + + pwlist(wlist, "After history substitution"); + if (cp_didhsubst) { + wl_print(wlist, stdout); + putc('\n', stdout); + } + } /* end of case that history substitutions are allowed */ + + /* Add the word list to the history. */ /* MW. If string==NULL we do not have to do this, and then play @@ -87,8 +108,9 @@ cp_parse(char *string) wlist = cp_doalias(wlist); pwlist(wlist, "After alias substitution"); pwlist(wlist, "Returning "); - return (wlist); -} + return wlist; +} /* end of function cp_parse */ + static void diff --git a/src/frontend/variable.c b/src/frontend/variable.c index d68ef8766..9c4151c11 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -29,8 +29,13 @@ bool cp_echo = FALSE; /* CDHW */ struct variable *variables = NULL; -wordlist * -cp_varwl(struct variable *var) + +static void update_option_variables(const char *sz_var_name, + struct variable *p_v); + + + +wordlist *cp_varwl(struct variable *var) { wordlist *wl = NULL, *w, *wx = NULL; char *buf; @@ -49,25 +54,26 @@ cp_varwl(struct variable *var) buf = tprintf("%G", var->va_real); break; case CP_STRING: - buf = cp_unquote(var->va_string); + buf = copy(var->va_string); break; case CP_LIST: /* The tricky case. */ for (vt = var->va_vlist; vt; vt = vt->va_next) { - w = cp_varwl(vt); + w = cp_varwl(vt); /* recursive call */ if (wl == NULL) { wl = wx = w; - } else { + } + else { wx->wl_next = w; w->wl_prev = wx; wx = w; } } - return (wl); + return wl; default: fprintf(cp_err, "cp_varwl: Internal Error: bad variable type %d\n", var->va_type); - return (NULL); + return NULL; } return wl_cons(buf, NULL); @@ -75,8 +81,7 @@ cp_varwl(struct variable *var) /* Set a variable. */ -void -cp_vset(char *varname, enum cp_types type, void *value) +void cp_vset(const char *varname, enum cp_types type, const void *value) { struct variable *v, *u, *w; int i; @@ -118,7 +123,9 @@ cp_vset(char *varname, enum cp_types type, void *value) } tfree(copyvarname); return; - } else { + } + else { + /* The variable only exists in TRUE state */ var_set_bool(v, TRUE); } break; @@ -147,39 +154,8 @@ cp_vset(char *varname, enum cp_types type, void *value) return; } - /* Now, see if there is anything interesting going on. We - * recognise these special variables: noglob, nonomatch, history, - * echo, noclobber, prompt, and verbose. cp_remvar looks for these - * variables too. The host program will get any others. */ - if (eq(copyvarname, "noglob")) - cp_noglob = TRUE; - else if (eq(copyvarname, "nonomatch")) - cp_nonomatch = TRUE; - else if (eq(copyvarname, "history") && (type == CP_NUM)) - cp_maxhistlength = v->va_num; - else if (eq(copyvarname, "history") && (type == CP_REAL)) - cp_maxhistlength = (int)floor(v->va_real + 0.5); - else if (eq(copyvarname, "noclobber")) - cp_noclobber = TRUE; - else if (eq(varname, "echo")) /*CDHW*/ - cp_echo = TRUE; /*CDHW*/ - else if (eq(copyvarname, "prompt")) { - if (type == CP_STRING) { - cp_promptstring = v->va_string; - } - else { /* use a default string since prompt is not a string */ - cp_promptstring = "-> "; - } - } - else if (eq(copyvarname, "ignoreeof")) - cp_ignoreeof = TRUE; - else if (eq(copyvarname, "cpdebug")) { - cp_debug = TRUE; -#ifndef CPDEBUG - fprintf(cp_err, - "Warning: program not compiled with cshpar debug messages\n"); -#endif - } + /* Update variables controlling options */ + update_option_variables(copyvarname, v); switch (i = cp_usrset(v, TRUE)) { @@ -260,7 +236,148 @@ cp_vset(char *varname, enum cp_types type, void *value) } tfree(copyvarname); -} +} /* end of function cp_vset */ + + + +/* Process special variables: noglob, nonomatch, history, + * noclobber, echo, prompt, ignoreeof, cpdebug, and no_histsubst + * by setting the values of associated option variables. + * + * Parmeters + * sz_var_name: Name of variable + * p_v: Variable if it is being added or NULL if being removed. + */ +static void update_option_variables(const char *sz_var_name, + struct variable *p_v) +{ + static const unsigned char p_ch0['p' - 'a' + 1] = { + ['n' - 'a'] = 1, /* noglob, nonomatch, noclobber, no_histsubst */ + ['h' - 'a'] = 2, /* history */ + ['e' - 'a'] = 3, /* echo */ + ['p' - 'a'] = 4, /* prompt, program */ + ['i' - 'a'] = 5, /* ignoreeof */ + ['c' - 'a'] = 6 /* cpdebug */ + }; + + unsigned int index0 = (unsigned int) sz_var_name[0] - 'a'; + + /* Check if first char of is in range of interest. + * Note that if < 0, as unsigned, it will be very large so this + * single compare checks both < 'a' and > 'p' */ + if (index0 >= sizeof p_ch0) { + return; + } + + unsigned int id0 = (unsigned int) p_ch0[index0]; + if (id0 == 0) { /* not of interest */ + return; + } + + /* Flag that bool values should be set is based on if the + * variable is being added (via a set) or removed */ + const bool f_set = p_v != (struct variable *) NULL; + + switch (id0) { + case 1: + /* noglob, nonomatch, noclobber, no_histsubst */ + if (sz_var_name[1] != 'o') { + return; + } + { + bool *p_var; + const char *sz_rest = sz_var_name + 2; + if (eq(sz_rest, "glob")) { + p_var = &cp_noglob; + } + else if (eq(sz_rest, "nomatch")) { + p_var = &cp_nonomatch; + } + else if (eq(sz_rest, "clobber")) { + p_var = &cp_noclobber; + } + else if (eq(sz_rest, "_histsubst")) { + p_var = &cp_no_histsubst; + } + else { /* not a variable of interest */ + return; + } + *p_var = f_set; + } + return; + case 2: /* history */ + if (eq(sz_var_name + 1, "istory")) { + if (f_set) { + int n = -1; + enum cp_types type = p_v->va_type; + if (type == CP_NUM) { + n = p_v->va_num; + } + else if (type == CP_REAL) { + n = (int) round(p_v->va_real); + } + if (n >= 0) { + cp_maxhistlength = n; + } + } + /* Note that 'unset history' doesn't do anything here... Causes + * trouble... */ + } + return; + case 3: /* echo */ + if (eq(sz_var_name + 1, "cho")) { + cp_echo = f_set; + } + return; + case 4: /* prompt, program */ + if (sz_var_name[1] != 'r') { + return; + } + if (sz_var_name[2] != 'o') { + return; + } + const char *sz_rest = sz_var_name + 3; + if (eq(sz_rest, "mpt")) { /* prompt */ + if (f_set && p_v->va_type == CP_STRING) { + cp_promptstring = p_v->va_string; + } + else { + /* Use a default string since prompt is not a string or the + * previous prompt string was freed */ + cp_promptstring = "-> "; + } + return; + } + if (eq(sz_rest, "gram")) { /* program */ + if (f_set && p_v->va_type == CP_STRING) { + cp_program = p_v->va_string; + } + else { + /* Use a default string since program is not a string or the + * previous program string was freed */ + cp_program = ""; + } + return; + } + return; /* not of interest */ + case 5: + if (eq(sz_var_name + 1, "gnoreeof")) { /* ignoreeof */ + cp_ignoreeof = f_set; + } + return; + case 6: + if (eq(sz_var_name + 1, "pdebug")) { /* cpdebug */ + cp_debug = f_set; +#ifndef CPDEBUG + if (cp_debug) { + fprintf(cp_err, "Warning: program not compiled " + "with cshpar debug messages\n"); + } +#endif + } + } /* end of switch over index for first char */ +} /* end of function update_option_variables */ + /* Read a wordlist, e.g. from the options or set commands @@ -437,8 +554,7 @@ free_struct_variable(struct variable *v) } -void -cp_remvar(char *varname) +void cp_remvar(char *varname) { struct variable *v, **p; struct variable *uv1; @@ -446,49 +562,45 @@ cp_remvar(char *varname) uv1 = cp_usrvars(); - for (p = &variables; *p; p = &(*p)->va_next) - if (eq((*p)->va_name, varname)) + for (p = &variables; *p; p = &(*p)->va_next) { + if (eq((*p)->va_name, varname)) { break; + } + } - if (*p == NULL) - for (p = &uv1; *p; p = &(*p)->va_next) - if (eq((*p)->va_name, varname)) + if (*p == NULL) { + for (p = &uv1; *p; p = &(*p)->va_next) { + if (eq((*p)->va_name, varname)) { break; + } + } + } - if (*p == NULL && plot_cur) - for (p = &plot_cur->pl_env; *p; p = &(*p)->va_next) - if (eq((*p)->va_name, varname)) + if (*p == NULL && plot_cur) { + for (p = &plot_cur->pl_env; *p; p = &(*p)->va_next) { + if (eq((*p)->va_name, varname)) { break; + } + } + } - if (*p == NULL && ft_curckt) - for (p = &ft_curckt->ci_vars; *p; p = &(*p)->va_next) - if (eq((*p)->va_name, varname)) + if (*p == NULL && ft_curckt) { + for (p = &ft_curckt->ci_vars; *p; p = &(*p)->va_next) { + if (eq((*p)->va_name, varname)) { break; + } + } + } v = *p; /* make up an auxiliary struct variable for cp_usrset() */ - if (!v) + if (!v) { v = var_alloc_num(copy(varname), 0, NULL); + } - /* Note that 'unset history' doesn't do anything here... Causes - * trouble... */ - if (eq(varname, "noglob")) - cp_noglob = FALSE; - else if (eq(varname, "nonomatch")) - cp_nonomatch = FALSE; - else if (eq(varname, "noclobber")) - cp_noclobber = FALSE; - else if (eq(varname, "echo")) /*CDHW*/ - cp_echo = FALSE; /*CDHW*/ - else if (eq(varname, "prompt")) - cp_promptstring = NULL; - else if (eq(varname, "cpdebug")) - cp_debug = FALSE; - else if (eq(varname, "ignoreeof")) - cp_ignoreeof = FALSE; - else if (eq(varname, "program")) - cp_program = ""; + /* Update options that depend on variables */ + update_option_variables(varname, (struct variable *) NULL); switch (i = cp_usrset(v, FALSE)) { @@ -540,7 +652,8 @@ cp_remvar(char *varname) free_struct_variable(v); free_struct_variable(uv1); -} +} /* end of function cp_remvar */ + /* Determine the value of a variable. Fail if the variable is unset, diff --git a/src/include/ngspice/cpextern.h b/src/include/ngspice/cpextern.h index 31c341c79..2268da9fc 100644 --- a/src/include/ngspice/cpextern.h +++ b/src/include/ngspice/cpextern.h @@ -57,6 +57,7 @@ extern FILE *cp_curin; extern FILE *cp_curout; extern FILE *cp_curerr; extern bool cp_debug; +extern bool cp_no_histsubst; /* controlled by "no_histsubst" true/false */ extern char cp_amp; extern char cp_gt; extern char cp_lt; @@ -162,7 +163,7 @@ extern bool cp_noglob; extern bool cp_nonomatch; extern char cp_dol; extern void cp_remvar(char *varname); -extern void cp_vset(char *varname, enum cp_types type, void *value); +void cp_vset(const char *varname, enum cp_types type, const void *value); extern struct variable *cp_setparse(wordlist *wl); extern wordlist *vareval(char *string); extern char *span_var_expr(char *t); From 9bbde919e3169aff401a2af5267736bc2599faea Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Jan 2020 11:17:45 +0100 Subject: [PATCH 12/58] Fixed merge conflict. Also made readifile() static and removed an unnecessary string duplication there. --- src/frontend/com_set.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/frontend/com_set.c b/src/frontend/com_set.c index 69014177b..d0d233b48 100644 --- a/src/frontend/com_set.c +++ b/src/frontend/com_set.c @@ -6,7 +6,7 @@ #include "variable.h" #include "com_set.h" -wordlist* readifile(wordlist*); +static wordlist* readifile(wordlist*); /* The set command. @@ -37,7 +37,15 @@ void com_set(wordlist *wl) return; } - struct variable *oldvar; + /* Handle special case input redirection. The file contents is + * converted to a list that can be handled by cp_setparse(). */ + { + const wordlist * const ww = wl->wl_next; + if (ww && eq(ww->wl_word, "<")) { + wl = readifile(wl); + } + } + struct variable *vars = cp_setparse(wl); /* This is sort of a hassle... */ @@ -63,7 +71,7 @@ void com_set(wordlist *wl) s = NULL; } cp_vset(vars->va_name, vars->va_type, s); - oldvar = vars; + struct variable * const oldvar = vars; vars = vars->va_next; /* Free allocations associated with the current variable */ @@ -74,12 +82,14 @@ void com_set(wordlist *wl) /* don't free oldvar->va_list! This structure is used furthermore! */ txfree(oldvar); } -} +} /* end of function com_set */ + + /* read a file from cp_in, add the tokens to a wordlist and create an input for a string list like set ccc = ( 3 5 7 ). Comment lines in input file (starting with '*') are ignored. */ -wordlist* +static wordlist* readifile(wordlist* win) { /* max file size */ @@ -93,17 +103,16 @@ readifile(wordlist* win) wl_append_word(&win, &win, copy("=")); wl_append_word(&win, &win, copy("(")); /* read a line. If it starts with '*', ignore it */ - while (fgets(intoken, 4096, cp_in) != NULL) { - if (intoken[0] == '*') + while (fgets(intoken, sizeof intoken, cp_in) != NULL) { + if (intoken[0] == '*') { /* skip comment lines */ continue; - char* delstr; - char* instr = delstr = copy(intoken); + } + char *instr = intoken; /* get all tokens, ignoring '\n' and add to string list */ while ((tmpstr = gettok(&instr)) != NULL) { wl_append_word(&win, &win, tmpstr); } - tfree(delstr); } wl_append_word(&win, &win, copy(")")); /* close and reset cp_in From b4bbcb1497d006c092f07925b226223819591a4c Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 6 Dec 2019 16:04:45 -0500 Subject: [PATCH 13/58] Made ft_numparse() thread-safe (no internal static variables) and prepared to support ngspice variable type CP_NUM. --- src/frontend/breakp.c | 48 +++--- src/frontend/com_compose.c | 128 ++++++++------- src/frontend/com_fft.c | 17 +- src/frontend/com_measure2.c | 30 ++-- src/frontend/control.c | 78 +++++----- src/frontend/dotcards.c | 110 +++++++------ src/frontend/fourier.c | 7 +- src/frontend/parse.c | 274 ++++++++++++++++++--------------- src/frontend/parser/numparse.c | 269 ++++++++++++++++++++------------ src/frontend/plotting/plotit.c | 21 +-- src/frontend/postcoms.c | 44 ++++-- src/frontend/spec.c | 77 ++++----- src/frontend/variable.c | 142 +++++++++-------- src/include/ngspice/cpstd.h | 10 +- src/include/ngspice/fteext.h | 2 +- src/misc/printnum.c | 40 ++++- src/misc/printnum.h | 5 +- 17 files changed, 758 insertions(+), 544 deletions(-) diff --git a/src/frontend/breakp.c b/src/frontend/breakp.c index 3f74e6102..86acbfd4b 100644 --- a/src/frontend/breakp.c +++ b/src/frontend/breakp.c @@ -46,7 +46,6 @@ com_stop(wordlist *wl) struct dbcomm *d = NULL; char *s, buf[64]; int i; - double *val; while (wl) { if (thisone == NULL) { @@ -80,10 +79,9 @@ com_stop(wordlist *wl) /* cp_lexer(string) will not discriminate '=', so we have to do it here */ if (strchr(wl->wl_next->wl_word, '=') && - (!(wl->wl_next->wl_next) || - strstr(wl->wl_next->wl_next->wl_word, "when") || - strstr(wl->wl_next->wl_next->wl_word, "after"))) - { + (!(wl->wl_next->wl_next) || + strstr(wl->wl_next->wl_next->wl_word, "when") || + strstr(wl->wl_next->wl_next->wl_word, "after"))) { /* we have vec=val in a single word */ wordlist *wln; char **charr = TMALLOC(char*, 4); @@ -98,17 +96,23 @@ com_stop(wordlist *wl) wln = wl_build((const char * const *) charr); wl_splice(wl->wl_next, wln); } + /* continue with parsing the enhanced wordlist */ if (wl->wl_next->wl_next && wl->wl_next->wl_next->wl_next) { wl = wl->wl_next; d->db_number = debugnumber; d->db_type = DB_STOPWHEN; s = wl->wl_word; - val = ft_numparse(&s, FALSE); - if (val) - d->db_value1 = *val; - else - d->db_nodename1 = copy(wl->wl_word); + + { + double val; + if (ft_numparse(&s, FALSE, &val) >= 0) { + d->db_value1 = val; + } + else { + d->db_nodename1 = copy(wl->wl_word); + } + } wl = wl->wl_next; /* Now get the condition */ @@ -130,17 +134,23 @@ com_stop(wordlist *wl) /* Now see about the second one. */ s = wl->wl_word; - val = ft_numparse(&s, FALSE); - if (val) - d->db_value2 = *val; - else + + { + double val; + if (ft_numparse(&s, FALSE, &val) >= 0) { + d->db_value2 = val; + } + else { d->db_nodename2 = copy(wl->wl_word); + } + } wl = wl->wl_next; - } else { + } + else { goto bad; } - } - } + } /* end of case of word "when" */ + } /* end of loop over wordlist */ if (thisone) { if (dbs) { @@ -159,11 +169,11 @@ com_stop(wordlist *wl) bad: fprintf(cp_err, "Syntax error parsing breakpoint specification.\n"); -} +} /* end of funtion com_stop */ + /* Trace a node (have wrd_point print it). Usage is "trace node ..."*/ - void com_trce(wordlist *wl) { diff --git a/src/frontend/com_compose.c b/src/frontend/com_compose.c index f91f8f9d9..4c80a8170 100644 --- a/src/frontend/com_compose.c +++ b/src/frontend/com_compose.c @@ -1,5 +1,7 @@ /* The 'compose' command. This is a more powerful and convenient form * of the 'let' command. */ +#include /* log10 */ + #include "ngspice/ngspice.h" #include "ngspice/complex.h" #include "ngspice/dvec.h" @@ -14,7 +16,6 @@ #include "com_compose.h" #include "completion.h" -#include /* log10 */ /* Copy the data from a vector into a buffer with larger dimensions. */ static void @@ -103,8 +104,7 @@ com_compose(wordlist *wl) int log = 0, dec = 0, oct = 0, gauss = 0, unif = 0; int i; - char *s, *var, *val; - double *td, tt; + double tt; double *data = NULL; ngcomplex_t *cdata = NULL; int length = 0; @@ -227,9 +227,11 @@ com_compose(wordlist *wl) } length *= blocksize; - } else { + } + else { /* Parse the line... */ while (wl) { + char *s, *var, *val; if ((s = strchr(wl->wl_word, '=')) != NULL && s[1]) { /* This is var=val. */ *s = '\0'; @@ -278,109 +280,124 @@ com_compose(wordlist *wl) } if (cieq(var, "start")) { startgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &start) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - start = *td; - } else if (cieq(var, "stop")) { + } + else if (cieq(var, "stop")) { stopgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &stop) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - stop = *td; - } else if (cieq(var, "step")) { + } + else if (cieq(var, "step")) { stepgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &step) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - step = *td; - } else if (cieq(var, "center")) { + } + else if (cieq(var, "center")) { centergiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, ¢er) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - center = *td; - } else if (cieq(var, "span")) { + } + else if (cieq(var, "span")) { spangiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &span) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - span = *td; - } else if (cieq(var, "mean")) { + } + else if (cieq(var, "mean")) { meangiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &mean) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - mean = *td; - } else if (cieq(var, "sd")) { + } + else if (cieq(var, "sd")) { sdgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &sd) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - sd = *td; - } else if (cieq(var, "lin")) { + } + else if (cieq(var, "lin")) { lingiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &lin) < 0) { fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - lin = *td; - } else if (cieq(var, "log")) { + } + else if (cieq(var, "log")) { + double dbl_val; loggiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &dbl_val) <= 0) { + /* Cannot convert value to int */ fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - log = (int)(*td); - } else if (cieq(var, "dec")) { + log = (int) dbl_val; + } + else if (cieq(var, "dec")) { + double dbl_val; decgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &dbl_val) <= 0) { + /* Cannot convert value to int */ fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - dec = (int)(*td); - } else if (cieq(var, "oct")) { + dec = (int) dbl_val; + } + else if (cieq(var, "oct")) { + double dbl_val; octgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &dbl_val) <= 0) { + /* Cannot convert value to integer */ fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - oct = (int)(*td); - } else if (cieq(var, "gauss")) { + oct = (int) dbl_val; + } + else if (cieq(var, "gauss")) { + double dbl_val; gaussgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &dbl_val) <= 0) { + /* Cannot convert value to int */ fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - gauss = (int)(*td); - } else if (cieq(var, "unif")) { + gauss = (int) dbl_val; + } + else if (cieq(var, "unif")) { + double dbl_val; unifgiven = TRUE; - if ((td = ft_numparse(&val, FALSE)) == NULL) { + if (ft_numparse(&val, FALSE, &dbl_val)<= 0) { + /* cannot convert to int */ fprintf(cp_err, "Error: compose -> bad parm %s = %s\n", var, val); goto done; } - unif = (int)(*td); - } else { + unif = (int) dbl_val; + } + else { fprintf(cp_err, "Error: compose -> bad parm %s\n", var); goto done; } @@ -405,7 +422,8 @@ com_compose(wordlist *wl) fprintf(cp_err, "Error: compose -> can have at most one of (lin, log, dec, oct, unif, gauss)\n"); goto done; - } else if (lingiven + loggiven + decgiven + octgiven + unifgiven + gaussgiven == 0) { + } + else if (lingiven + loggiven + decgiven + octgiven + unifgiven + gaussgiven == 0) { /* Hmm, if we have a start, stop, and step we're ok. */ if (startgiven && stopgiven && stepgiven) { lingiven = TRUE; @@ -415,7 +433,8 @@ com_compose(wordlist *wl) } lin = (stop - start) / step + 1.; stepgiven = FALSE; /* Problems below... */ - } else { + } + else { fprintf(cp_err, "Error: compose -> either one of (lin, log, dec, oct, unif, gauss) must be given, or all\n"); fprintf(cp_err, @@ -463,10 +482,12 @@ com_compose(wordlist *wl) step = (stop - start) / (lin - 1.0); } - for (i = 0, tt = start; i < length; i++, tt += step) + for (i = 0, tt = start; i < length; i++, tt += step) { data[i] = tt; + } - } else if (loggiven || decgiven || octgiven) { + } + else if (loggiven || decgiven || octgiven) { /* Create a log sweep... */ if (centergiven && spangiven) { if (center <= span/2.0) { @@ -505,7 +526,8 @@ com_compose(wordlist *wl) for (i = 0; i < length; i++) data[i] = start * pow(stop/start, (double)i/(log-1.0)); - } else if (unifgiven) { + } + else if (unifgiven) { /* Create a set of uniform distributed values... */ if (startgiven || stopgiven) { if (!startgiven || !stopgiven) { @@ -541,7 +563,8 @@ com_compose(wordlist *wl) for (i = 0; i < length; i++) data[i] = mean + span * 0.5 * drand(); - } else if (gaussgiven) { + } + else if (gaussgiven) { /* Create a gaussian distribution... */ if (gauss <= 0) { fprintf(cp_err, @@ -558,8 +581,9 @@ com_compose(wordlist *wl) } length = gauss; data = TMALLOC(double, length); - for (i = 0; i < length; i++) + for (i = 0; i < length; i++) { data[i] = mean + sd * gauss1(); + } } } @@ -589,5 +613,5 @@ com_compose(wordlist *wl) done: free_pnode(names); - tfree(resname); -} + txfree(resname); +} /* end of function com_compose */ diff --git a/src/frontend/com_fft.c b/src/frontend/com_fft.c index e9bd460a1..31baa1a51 100644 --- a/src/frontend/com_fft.c +++ b/src/frontend/com_fft.c @@ -241,7 +241,7 @@ com_psd(wordlist *wl) { ngcomplex_t **fdvec = NULL; double **tdvec = NULL; - double *freq, *win = NULL, *time, *ave; + double *freq, *win = NULL, *time; double span, noipower; int ngood, fpts, i, j, jj, length, smooth, hsmooth; char *s; @@ -278,12 +278,15 @@ com_psd(wordlist *wl) // get filter length from parameter input s = wl->wl_word; - ave = ft_numparse(&s, FALSE); - if (!ave || (*ave < 1.0)) { - fprintf(cp_out, "Number of averaged data points: %d\n", 1); - smooth = 1; - } else { - smooth = (int)(*ave); + { + double val; + if (ft_numparse(&s, FALSE, &val) <= 0 || val < 1.0) { + fprintf(cp_out, "Number of averaged data points: 1\n"); + smooth = 1; + } + else { + smooth = (int) val; + } } wl = wl->wl_next; diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index 0eb609679..5346cf7c9 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -21,8 +21,8 @@ typedef enum { MEASUREMENT_FAILURE = 1 } MEASURE_VAL_T; -#define MEASURE_DEFAULT -1 -#define MEASURE_LAST_TRANSITION -2 +#define MEASURE_DEFAULT (-1) +#define MEASURE_LAST_TRANSITION (-2) typedef struct measure { @@ -1152,7 +1152,7 @@ measure_parse_stdParams( { int pCnt; char *p, *pName, *pValue; - double *engVal, engVal1; + double engVal1; pCnt = 0; while (wl != wlBreak) { @@ -1176,12 +1176,12 @@ measure_parse_stdParams( if (strcasecmp(pValue, "LAST") == 0) { engVal1 = MEASURE_LAST_TRANSITION; - } else { - if ((engVal = ft_numparse(&pValue, FALSE)) == NULL) { + } + else { + if (ft_numparse(&pValue, FALSE, &engVal1) < 0) { sprintf(errbuf, "bad syntax of ??\n"); return 0; } - engVal1 = *engVal; // What is this ?? } if (strcasecmp(pName, "RISE") == 0) { @@ -1257,8 +1257,6 @@ measure_parse_find( ) { int pCnt; - char *p, *pName, *pVal; - double *engVal, engVal1; meas->m_vec = NULL; meas->m_vec2 = NULL; @@ -1280,7 +1278,7 @@ measure_parse_find( pCnt = 0; while (wl != wlBreak) { - p = wl->wl_word; + char *p = wl->wl_word; if (pCnt == 0) { meas->m_vec = cp_unquote(wl->wl_word); @@ -1288,8 +1286,8 @@ measure_parse_find( if (cieq("ac", meas->m_analysis) || cieq("sp", meas->m_analysis)) correct_vec(meas); } else if (pCnt == 1) { - pName = strtok(p, "="); - pVal = strtok(NULL, "="); + char * const pName = strtok(p, "="); + char * const pVal = strtok(NULL, "="); if (pVal == NULL) { sprintf(errbuf, "bad syntax of WHEN\n"); @@ -1297,16 +1295,12 @@ measure_parse_find( } if (strcasecmp(pName, "AT") == 0) { - if ((engVal = ft_numparse(&pVal, FALSE)) == NULL) { + if (ft_numparse((char **) &pVal, FALSE, &meas->m_at) < 0) { sprintf(errbuf, "bad syntax of WHEN\n"); return 0; } - - engVal1 = *engVal; - - meas->m_at = engVal1; - - } else { + } + else { sprintf(errbuf, "bad syntax of WHEN\n"); return 0; } diff --git a/src/frontend/control.c b/src/frontend/control.c index d2f5f13ae..27192b768 100644 --- a/src/frontend/control.c +++ b/src/frontend/control.c @@ -29,13 +29,13 @@ static void cp_free_control(void); /* needed by resetcontrol */ * more clever. */ bool cp_cwait = FALSE; -char *cp_csep = ";"; +char *cp_csep = ";"; /* character that separates commands */ bool cp_dounixcom = FALSE; /* We have to keep the control structures in a stack, so that when we * do a 'source', we can push a fresh set onto the top... Actually - * there have to be two stacks -- one for the pointer to the list of + * there has to be two stacks -- one for the pointer to the list of * control structs, and one for the 'current command' pointer... */ struct control *control[CONTROLSTACKSIZE]; struct control *cend[CONTROLSTACKSIZE]; @@ -62,12 +62,13 @@ int stackp = 0; static char *noredirect[] = { "stop", "define", NULL }; -static struct control * -findlabel(char *s, struct control *ct) +/* This function returns the (first) structure wit the label s */ +static struct control *findlabel(const char *s, struct control *ct) { while (ct) { - if ((ct->co_type == CO_LABEL) && eq(s, ct->co_text->wl_word)) + if ((ct->co_type == CO_LABEL) && eq(s, ct->co_text->wl_word)) { break; + } ct = ct->co_next; } return (ct); @@ -116,7 +117,7 @@ ctl_free(struct control *ctrl) wl_free(ctrl->co_cond); ctrl->co_cond = NULL; - tfree(ctrl->co_foreachvar); + txfree(ctrl->co_foreachvar); ctrl->co_foreachvar = NULL; wl_free(ctrl->co_text); ctrl->co_text = NULL; @@ -288,8 +289,9 @@ doblock(struct control *bl, int *num) switch (bl->co_type) { case CO_WHILE: if (!bl->co_children) { - fprintf(cp_err, "Warning: Executing empty 'while' block.\n"); - fprintf(cp_err, " (Use a label statement as a no-op to suppress this warning.)\n"); + fprintf(cp_err, "Warning: Executing empty 'while' block.\n" + " (Use a label statement as a no-op " + "to suppress this warning.)\n"); } while (bl->co_cond && cp_istrue(bl->co_cond)) { if (!bl->co_children) cp_periodic(); /*CDHW*/ @@ -558,11 +560,11 @@ doblock(struct control *bl, int *num) char * get_alt_prompt(void) { - int i = 0, j; + int i = 0; static char buf[MAX_CHEVRONS + 2]; /* includes terminating space & null */ struct control *c; - /* if nothing on the command stack return NULL */ + /* If nothing on the command stack return NULL */ if (cend[stackp] == NULL) return NULL; @@ -570,8 +572,9 @@ get_alt_prompt(void) for (c = cend[stackp]->co_parent; c; c = c->co_parent) i++; - if (i <= 0) + if (i == 0) { return NULL; + } /* Avoid overflow of buffer and indicate when we've limited the chevrons by starting with a '+' */ @@ -583,15 +586,19 @@ get_alt_prompt(void) } /* return one chevron per command stack depth */ - for (j = 1; j < i; j++) - buf[j] = '>'; + { + int j; + for (j = 1; j < i; j++) + buf[j] = '>'; - /* Add space and terminate */ - buf[j] = ' '; - buf[j + 1] = '\0'; + /* Add space and terminate */ + buf[j] = ' '; + buf[j + 1] = '\0'; + } return buf; -} +} /* end of function get_alt_prompt */ + /* Get a command. This does all the bookkeeping things like turning @@ -601,8 +608,9 @@ getcommand(char *string) { wordlist *wlist; - if (cp_debug) + if (cp_debug) { fprintf(cp_err, "calling getcommand %s\n", string ? string : ""); + } #if !defined(HAVE_GNUREADLINE) && !defined(HAVE_BSDEDITLINE) /* set cp_altprompt for use by the lexer - see parser/lexical.c */ @@ -617,7 +625,7 @@ getcommand(char *string) wl_print(wlist, stdout); putc('\n', stdout); } - return (wlist); + return wlist; } @@ -628,7 +636,6 @@ cp_evloop(char *string) wordlist *wlist, *ww, *freewl; struct control *x; char *i; - int nn; #define newblock \ do { \ @@ -653,8 +660,9 @@ cp_evloop(char *string) /* User just typed return. */ wl_free(wlist); /* va, avoid memory leak */ if (string) { - return (1); - } else { + return 1; + } + else { cp_event--; continue; } @@ -708,22 +716,23 @@ cp_evloop(char *string) cend[stackp]->co_numtimes = -1; } else { char *s; - double *dd; + double val; struct wordlist *t; /*CDHW*/ /*CDHW wlist = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(wlist)))); Wrong order? Leak? CDHW*/ t = cp_doglob(cp_bquote(cp_variablesubst(wl_copy(wlist)))); /*CDHW leak from cp_doglob? */ s = t->wl_next->wl_word; - dd = ft_numparse(&s, FALSE); - if (dd) { - if (*dd < 0) { + if (ft_numparse(&s, FALSE, &val) > 0) { + /* Can be converted to int */ + if (val < 0) { fprintf(cp_err, "Error: can't repeat a negative number of times\n"); - *dd = 0.0; + val = 0.0; } - cend[stackp]->co_numtimes = (int) *dd; - } else { + cend[stackp]->co_numtimes = (int) val; + } + else { fprintf(cp_err, "Error: bad repeat argument %s\n", t->wl_next->wl_word); /* CDHW */ @@ -805,7 +814,7 @@ cp_evloop(char *string) cend[stackp]->co_numtimes = 1; } } else if (eq(wlist->wl_word, "end")) { - /* Throw away this thing. */ + /* Throw away this thing if not in a block. */ if (!cend[stackp]->co_parent) { fprintf(stderr, "Error: no block to end.\n"); cend[stackp]->co_type = CO_UNFILLED; @@ -824,8 +833,7 @@ cp_evloop(char *string) } } else if (eq(wlist->wl_word, "else")) { if (!cend[stackp]->co_parent || - (cend[stackp]->co_parent->co_type != - CO_IF)) { + (cend[stackp]->co_parent->co_type != CO_IF)) { fprintf(stderr, "Error: misplaced else.\n"); cend[stackp]->co_type = CO_UNFILLED; } else { @@ -847,7 +855,7 @@ cp_evloop(char *string) * that gotos at the top level will work. */ do { - nn = 0; /* CDHW */ + int nn = 0; /* CDHW */ i = doblock(x, &nn); switch (*i) { case NORMAL: @@ -873,8 +881,8 @@ cp_evloop(char *string) wl_free(freewl); if (string) return (1); /* The return value is irrelevant. */ - } -} + } /* end of unconditional loop */ +} /* end of function cp_evloop */ /* This blows away the control structures... */ diff --git a/src/frontend/dotcards.c b/src/frontend/dotcards.c index 1171ab211..48cf5a26a 100644 --- a/src/frontend/dotcards.c +++ b/src/frontend/dotcards.c @@ -12,6 +12,7 @@ Modified: 2000 AlansFixes #include #include "ngspice/cpdefs.h" #include "ngspice/ftedefs.h" +#include "ngspice/dstring.h" #include "ngspice/dvec.h" #include "ngspice/fteinp.h" #include "ngspice/sim.h" @@ -200,8 +201,9 @@ ft_cktcoms(bool terse) all.wl_next = NULL; all.wl_word = "all"; - if (!ft_curckt) + if (!ft_curckt) { return 1; + } plot_cur = setcplot("op"); if (!ft_curckt->ci_commands && !plot_cur) @@ -249,8 +251,9 @@ ft_cktcoms(bool terse) } fprintf(cp_out, "\n"); - if (!ft_nomod) + if (!ft_nomod) { com_showmod(&all); + } com_show(&all); } } @@ -271,8 +274,10 @@ ft_cktcoms(bool terse) /* Now all the '.' lines */ while (coms) { command = cp_lexer(coms->wl_word); - if (!command) + if (!command || command->wl_word == (char *) NULL) { + /* Line not converted to a wordlist */ goto bad; + } if (eq(command->wl_word, ".width")) { do command = command->wl_next; @@ -360,12 +365,11 @@ ft_cktcoms(bool terse) !eq(command->wl_word, ".op") && // !eq(command->wl_word, ".measure") && !ciprefix(".meas", command->wl_word) && - !eq(command->wl_word, ".tf")) - { + !eq(command->wl_word, ".tf")) { goto bad; } - coms = coms->wl_next; - } + coms = coms->wl_next; /* go to next line */ + } /* end of loop over '.' lines */ nocmds: /* Now the node table @@ -399,7 +403,7 @@ bad: /* These routines make sure that the arguments to .plot and .print in - * spice2 decks are acceptable to spice3. The things we look for are: + * spice2 decks are acceptable to spice3. The things we look for are * trailing (a,b) in .plot -> xlimit a b * vm(x) -> mag(v(x)) * vp(x) -> ph(v(x)) @@ -410,60 +414,70 @@ bad: static void fixdotplot(wordlist *wl) { - char *s; - char numbuf[128]; /* Printnum Fix */ - double *d, d1, d2; + /* Create a buffer for printing numbers */ + DS_CREATE(numbuf, 100); while (wl) { wl->wl_word = fixem(wl->wl_word); - /* Is this a trailing (a,b) ? Note that we require it to be - * one word. - */ + /* Is this a trailing "(a,b)"? Note that we require it to be + * one word. */ if (!wl->wl_next && (*wl->wl_word == '(')) { - s = wl->wl_word + 1; - d = ft_numparse(&s, FALSE); - if (*s != ',') { + double d1, d2; + char *s = wl->wl_word + 1; + if (ft_numparse(&s, FALSE, &d1) < 0 || + *s != ',') { fprintf(cp_err, "Error: bad limits \"%s\"\n", wl->wl_word); - return; + goto EXITPOINT; } - d1 = *d; - s++; - d = ft_numparse(&s, FALSE); - if ((*s != ')') || s[1]) { + s++; /* step past comma */ + if (ft_numparse(&s, FALSE, &d2) < 0 || + *s != ')' || s[1] != '\0') { /* must end with ")" */ fprintf(cp_err, "Error: bad limits \"%s\"\n", wl->wl_word); - return; + goto EXITPOINT; } - d2 = *d; + tfree(wl->wl_word); wl->wl_word = copy("xlimit"); - printnum(numbuf, d1); - wl_append_word(NULL, &wl, copy(numbuf)); - printnum(numbuf, d2); - wl_append_word(NULL, &wl, copy(numbuf)); - } + ds_clear(&numbuf); + if (printnum_ds(&numbuf, d1) != 0) { + fprintf(cp_err, "Unable to print limit 1: %g\n", d1); + goto EXITPOINT; + } + wl_append_word(NULL, &wl, copy(ds_get_buf(&numbuf))); + ds_clear(&numbuf); + if (printnum_ds(&numbuf, d2) != 0) { + fprintf(cp_err, "Unable to print limit 2: %g\n", d2); + goto EXITPOINT; + } + wl_append_word(NULL, &wl, copy(ds_get_buf(&numbuf))); + } /* end of case of start of potential (a,b) */ wl = wl->wl_next; - } -} + } /* end of loop over words */ + +EXITPOINT: + ds_free(&numbuf); /* Free DSTRING resources */ +} /* end of function fixdotplot */ -static void -fixdotprint(wordlist *wl) + +static void fixdotprint(wordlist *wl) { + /* Process each word in the wordlist */ while (wl) { wl->wl_word = fixem(wl->wl_word); wl = wl->wl_next; } -} +} /* end of function fixdotprint */ -static char * -fixem(char *string) + +static char *fixem(char *string) { char buf[BSIZE_SP], *s, *t; - char *ss = string; /* Get rid of ss ? */ + char *ss = string; /* save addr of string in case it is freed */ if (ciprefix("v(", string) &&strchr(string, ',')) { for (s = string; *s && (*s != ','); s++) @@ -550,14 +564,15 @@ fixem(char *string) string += 2; (void) sprintf(buf, "%s#branch", string); } else { - return (string); + return string; } - tfree(ss); + txfree(ss); string = copy(buf); - return (string); -} + return string; +} /* end of function fixem */ + wordlist * @@ -611,7 +626,8 @@ gettoks(char *s) sprintf(buf, "%s#branch", l + 1); wl->wl_word = copy(buf); c = r = NULL; - } else { + } + else { wl->wl_word = copy(l + 1); } @@ -622,7 +638,11 @@ gettoks(char *s) prevp = &wl->wl_next; } tfree(t); - } - tfree(s0); + } /* end of loop parsing string */ + + txfree(s0); return list; -} +} /* end of function gettoks */ + + + diff --git a/src/frontend/fourier.c b/src/frontend/fourier.c index 5e86b7642..2aad24cb2 100644 --- a/src/frontend/fourier.c +++ b/src/frontend/fourier.c @@ -41,7 +41,7 @@ fourier(wordlist *wl, struct plot *current_plot) { struct dvec *time, *vec; struct pnode *pn, *names; - double *ff, fundfreq, *data = NULL; + double fundfreq, *data = NULL; int nfreqs, fourgridsize, polydegree; double *freq, *mag, *phase, *nmag, *nphase; /* Outputs from CKTfour */ double thd, *timescale = NULL; @@ -78,11 +78,10 @@ fourier(wordlist *wl, struct plot *current_plot) return 1; } s = wl->wl_word; - if ((ff = ft_numparse(&s, FALSE)) == NULL || (*ff <= 0.0)) { - fprintf(cp_err, "Error: bad fund freq %s\n", wl->wl_word); + if (ft_numparse(&s, FALSE, &fundfreq) < 0 || fundfreq <= 0.0) { + fprintf(cp_err, "Error: bad fundamental freq %s\n", wl->wl_word); return 1; } - fundfreq = *ff; freq = TMALLOC(double, nfreqs); mag = TMALLOC(double, nfreqs); diff --git a/src/frontend/parse.c b/src/frontend/parse.c index c4e2f775a..fe3a1d852 100644 --- a/src/frontend/parse.c +++ b/src/frontend/parse.c @@ -29,8 +29,7 @@ extern int PPparse(char **, struct pnode **); void db_print_pnode_tree(struct pnode *p, char *print); -struct pnode * -ft_getpnames_from_string(const char *sz, bool check) +struct pnode *ft_getpnames_from_string(const char *sz, bool check) { struct pnode *pn; @@ -62,7 +61,7 @@ ft_getpnames(const wordlist *wl, bool check) return (struct pnode *) NULL; } - /* Convert the list to a string then parse the string */ + /* Conver the list to a string then parse the string */ const char * const sz = wl_flatten(wl); struct pnode * const pn = ft_getpnames_from_string(sz, check); txfree((void *) sz); @@ -213,63 +212,70 @@ struct func func_not = { "not", cx_not }; /* Binary operator node. */ - -struct pnode * -PP_mkbnode(int opnum, struct pnode *arg1, struct pnode *arg2) +struct pnode *PP_mkbnode(int opnum, struct pnode *arg1, struct pnode *arg2) { struct op *o; struct pnode *p; - for (o = &ops[0]; o->op_name; o++) - if (o->op_num == opnum) + for (o = &ops[0]; o->op_name; o++) { + if (o->op_num == opnum) { break; + } + } - if (!o->op_name) + if (!o->op_name) { fprintf(cp_err, "PP_mkbnode: Internal Error: no such op num %d\n", opnum); + } p = alloc_pnode(); p->pn_op = o; p->pn_left = arg1; - if (p->pn_left) + if (p->pn_left) { p->pn_left->pn_use++; + } p->pn_right = arg2; - if (p->pn_right) + if (p->pn_right) { p->pn_right->pn_use++; + } + + return p; +} /* end of function PP_mkbnode */ - return (p); -} /* Unary operator node. */ - -struct pnode * -PP_mkunode(int op, struct pnode *arg) +struct pnode *PP_mkunode(int op, struct pnode *arg) { struct pnode *p; struct op *o; p = alloc_pnode(); - for (o = uops; o->op_name; o++) - if (o->op_num == op) + for (o = uops; o->op_name; o++) { + if (o->op_num == op) { break; + } + } - if (!o->op_name) + if (!o->op_name) { fprintf(cp_err, "PP_mkunode: Internal Error: no such op num %d\n", op); + } p->pn_op = o; p->pn_left = arg; - if (p->pn_left) + if (p->pn_left) { p->pn_left->pn_use++; + } + + return p; +} /* end of function PP_mkunode */ - return (p); -} /* Function node. We have to worry about a lot of things here. Something @@ -280,9 +286,7 @@ PP_mkunode(int op, struct pnode *arg) * the arguments, and then return a copy of the expression that it was * defined to be. */ - -struct pnode * -PP_mkfnode(const char *func, struct pnode *arg) +struct pnode *PP_mkfnode(const char *func, struct pnode *arg) { struct func *f; struct pnode *p, *q; @@ -292,15 +296,18 @@ PP_mkfnode(const char *func, struct pnode *arg) (void) strcpy(buf, func); strtolower(buf); /* Make sure the case is ok. */ - for (f = &ft_funcs[0]; f->fu_name; f++) - if (eq(f->fu_name, buf)) + for (f = &ft_funcs[0]; f->fu_name; f++) { + if (eq(f->fu_name, buf)) { break; + } + } - if (f->fu_name == NULL) { + if (f->fu_name == NULL) { /* not found yet */ /* Give the user-defined functions a try. */ q = ft_substdef(func, arg); - if (q) - return (q); + if (q) { /* found */ + return q; + } } if ((f->fu_name == NULL) && arg->pn_value) { @@ -312,15 +319,16 @@ PP_mkfnode(const char *func, struct pnode *arg) /* Well, too bad. */ fprintf(cp_err, "Error: no such function as %s.\n", func); - return (NULL); + return (struct pnode *) NULL; } /* (void) strcpy(buf, d->v_name); XXX */ - return (PP_mksnode(buf)); - } else if (f->fu_name == NULL) { + return PP_mksnode(buf); + } + else if (f->fu_name == NULL) { fprintf(cp_err, "Error: no function as %s with that arity.\n", func); free_pnode(arg); - return (NULL); + return (struct pnode *) NULL; } if (!f->fu_func && arg->pn_op && arg->pn_op->op_num == PT_OP_COMMA) { @@ -335,17 +343,17 @@ PP_mkfnode(const char *func, struct pnode *arg) p->pn_func = f; p->pn_left = arg; - if (p->pn_left) + if (p->pn_left) { p->pn_left->pn_use++; + } + + return p; +} /* end of function PP_mkfnode */ - return (p); -} /* Number node. */ - -struct pnode * -PP_mknnode(double number) +struct pnode *PP_mknnode(double number) { struct pnode *p; struct dvec *v; @@ -354,7 +362,7 @@ PP_mknnode(double number) * to be careful to deal properly with node numbers that are quite * large... */ - v = dvec_alloc(number < MAXPOSINT + v = dvec_alloc(number <= INT_MAX ? tprintf("%d", (int) number) : tprintf("%G", number), SV_NOTYPE, @@ -368,13 +376,12 @@ PP_mknnode(double number) p = alloc_pnode(); p->pn_value = v; return (p); -} +} /* end of function PP_mknnode */ + /* String node. */ - -struct pnode * -PP_mksnode(const char *string) +struct pnode *PP_mksnode(const char *string) { struct dvec *v, *nv, *vs, *newv = NULL, *end = NULL; struct pnode *p; @@ -387,17 +394,19 @@ PP_mksnode(const char *string) 0, 0, NULL); p->pn_value = nv; - return (p); + return p; } /* It's not obvious that we should be doing this, but... */ for (vs = v; vs; vs = vs->v_link2) { nv = vec_copy(vs); vec_new(nv); - if (end) + if (end) { end->v_link2 = nv; - else + } + else { newv = end = nv; + } end = nv; } p->pn_value = newv; @@ -410,12 +419,12 @@ PP_mksnode(const char *string) /* The two lines above have been commented out to prevent deletion of @xxx[par] after execution of only a single command like plot @xxx[par] or write. We need to monitor if this will lead to excessive memory usage. h_vogt 090221 */ - return (p); -} + return p; +} /* end of function PP_mksnode */ -struct pnode * -alloc_pnode(void) + +struct pnode *alloc_pnode(void) { struct pnode *pn = TMALLOC(struct pnode, 1); @@ -432,42 +441,45 @@ alloc_pnode(void) pn->pn_next = NULL; return pn; -} +} /* end of function alloc_pnode */ + /* Don't call this directly, always use the free_pnode() macro. The linked pnodes do not necessarily form a perfect tree as some nodes get - reused. Hence, in this recursive walk trough the 'tree' we only free node - that have their pn_use value at zero. Nodes that have pn_use values above - zero have the link severed and their pn_use value decremented. + reused. Hence, in this recursive walk through the 'tree', we only free + nodes that have their pn_use value at zero. Nodes that have pn_use values + above zero have the link severed and their pn_use value decremented. In addition, we don't walk past nodes with pn_use values avoid zero, just - in case we have a circular reference (this probable does not happen in - practice, but it does no harm playing safe) */ -void -free_pnode_x(struct pnode *t) + in case we have a circular reference (This probably does not happen in + practice, but it does no harm playing safe.) */ +void free_pnode_x(struct pnode *t) { - if (!t) + if (!t) { return; + } - /* don't walk past nodes used elsewhere. We decrement the pn_use value here, + /* Don't walk past nodes used elsewhere. We decrement the pn_use value here, but the link gets severed by the action of the free_pnode() macro */ if (t->pn_use > 1) { t->pn_use--; - } else { + } + else { /* pn_use is now 1, so its safe to free the pnode */ free_pnode(t->pn_left); free_pnode(t->pn_right); free_pnode(t->pn_next); tfree(t->pn_name); /* va: it is a copy() of original string, can be free'd */ - if (t->pn_value && !(t->pn_value->v_flags & VF_PERMANENT)) + if (t->pn_value && !(t->pn_value->v_flags & VF_PERMANENT)) { vec_free(t->pn_value); /* patch by Stefan Jones */ - tfree(t); + } + txfree(t); } -} +} /* end of function free_pnode_x */ -static void -db_print_func(FILE *fdst, struct func *f) + +static void db_print_func(FILE *fdst, struct func *f) { if (!f) { fprintf(fdst, "nil"); @@ -475,11 +487,11 @@ db_print_func(FILE *fdst, struct func *f) } fprintf(fdst, "(func :fu_name %s :fu_func %p)", f->fu_name, f->fu_func); -} +} /* end of function db_print_func */ -static void -db_print_op(FILE *fdst, struct op *op) + +static void db_print_op(FILE *fdst, struct op *op) { if (!op) { fprintf(fdst, "nil"); @@ -488,11 +500,11 @@ db_print_op(FILE *fdst, struct op *op) fprintf(fdst, "(op :op_num %d :op_name %s :op_arity %d :op_func %p)", op->op_num, op->op_name, op->op_arity, op->op_func.anonymous); -} +} /* end of function db_print_op */ -static void -db_print_dvec(FILE *fdst, struct dvec *d) + +static void db_print_dvec(FILE *fdst, struct dvec *d) { if (!d) { fprintf(fdst, "nil"); @@ -501,11 +513,11 @@ db_print_dvec(FILE *fdst, struct dvec *d) fprintf(fdst, "(dvec :v_name %s :v_type %d :v_flags %d :v_length %d ...)", d->v_name, d->v_type, d->v_flags, d->v_length); -} +} /* end of function db_print_dvec */ -static void -db_print_pnode(FILE *fdst, struct pnode *p) + +static void db_print_pnode(FILE *fdst, struct pnode *p) { if (!p) { fprintf(fdst, "nil\n"); @@ -513,8 +525,7 @@ db_print_pnode(FILE *fdst, struct pnode *p) } if (!p->pn_name && p->pn_value && !p->pn_func && !p->pn_op && - !p->pn_left && !p->pn_right && !p->pn_next) - { + !p->pn_left && !p->pn_right && !p->pn_next) { fprintf(fdst, "(pnode-value :pn_use %d", p->pn_use); fprintf(fdst, " :pn_value "); db_print_dvec(fdst, p->pn_value); fprintf(fdst, ")\n"); @@ -522,8 +533,7 @@ db_print_pnode(FILE *fdst, struct pnode *p) } if (!p->pn_name && !p->pn_value && p->pn_func && !p->pn_op && - !p->pn_right && !p->pn_next) - { + !p->pn_right && !p->pn_next) { fprintf(fdst, "(pnode-func :pn_use %d", p->pn_use); fprintf(fdst, "\n :pn_func "); db_print_func(fdst, p->pn_func); fprintf(fdst, "\n :pn_left "); db_print_pnode(fdst, p->pn_left); @@ -532,8 +542,7 @@ db_print_pnode(FILE *fdst, struct pnode *p) } if (!p->pn_name && !p->pn_value && !p->pn_func && p->pn_op && - !p->pn_next) - { + !p->pn_next) { fprintf(fdst, "(pnode-op :pn_use %d", p->pn_use); fprintf(fdst, "\n :pn_op "); db_print_op(fdst, p->pn_op); fprintf(fdst, "\n :pn_left "); db_print_pnode(fdst, p->pn_left); @@ -550,11 +559,11 @@ db_print_pnode(FILE *fdst, struct pnode *p) fprintf(fdst, "\n :pn_right "); db_print_pnode(fdst, p->pn_right); fprintf(fdst, "\n :pn_next "); db_print_pnode(fdst, p->pn_next); fprintf(fdst, "\n)\n"); -} +} /* end of function db_print_pnode */ -void -db_print_pnode_tree(struct pnode *p, char *print) + +void db_print_pnode_tree(struct pnode *p, char *print) { #if 1 NG_IGNORE(print); @@ -569,18 +578,19 @@ db_print_pnode_tree(struct pnode *p, char *print) printf("%s:%d: %s {%s}\n%s\n", __FILE__, __LINE__, __func__, print, buf); tfree(buf); #endif -} +} /* end of function db_print_pnode_tree */ -int -PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) + +int PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) { static char *specials = " \t%()-^+*,/|&<>~="; char *sbuf = *line; int token; - while ((*sbuf == ' ') || (*sbuf == '\t')) + while ((*sbuf == ' ') || (*sbuf == '\t')) { sbuf++; + } llocp->start = sbuf; @@ -590,28 +600,36 @@ PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) if ((sbuf[0] == 'g') && (sbuf[1] == 't') && strchr(specials, sbuf[2])) { lexer_return('>', 2); - } else if ((sbuf[0] == 'l') && (sbuf[1] == 't') && + } + else if ((sbuf[0] == 'l') && (sbuf[1] == 't') && strchr(specials, sbuf[2])) { lexer_return('<', 2); - } else if ((sbuf[0] == 'g') && (sbuf[1] == 'e') && + } + else if ((sbuf[0] == 'g') && (sbuf[1] == 'e') && strchr(specials, sbuf[2])) { lexer_return(TOK_GE, 2); - } else if ((sbuf[0] == 'l') && (sbuf[1] == 'e') && + } + else if ((sbuf[0] == 'l') && (sbuf[1] == 'e') && strchr(specials, sbuf[2])) { lexer_return(TOK_LE, 2); - } else if ((sbuf[0] == 'n') && (sbuf[1] == 'e') && + } + else if ((sbuf[0] == 'n') && (sbuf[1] == 'e') && strchr(specials, sbuf[2])) { lexer_return(TOK_NE, 2); - } else if ((sbuf[0] == 'e') && (sbuf[1] == 'q') && + } + else if ((sbuf[0] == 'e') && (sbuf[1] == 'q') && strchr(specials, sbuf[2])) { lexer_return('=', 2); - } else if ((sbuf[0] == 'o') && (sbuf[1] == 'r') && + } + else if ((sbuf[0] == 'o') && (sbuf[1] == 'r') && strchr(specials, sbuf[2])) { lexer_return('|', 2); - } else if ((sbuf[0] == 'a') && (sbuf[1] == 'n') && + } + else if ((sbuf[0] == 'a') && (sbuf[1] == 'n') && (sbuf[2] == 'd') && strchr(specials, sbuf[3])) { lexer_return('&', 3); - } else if ((sbuf[0] == 'n') && (sbuf[1] == 'o') && + } + else if ((sbuf[0] == 'n') && (sbuf[1] == 'o') && (sbuf[2] == 't') && strchr(specials, sbuf[3])) { lexer_return('~', 3); } @@ -623,18 +641,19 @@ PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) lexer_return(*sbuf, 1); case '>': - case '<': - { + case '<': { /* Workaround, The Frontend makes "<>" into "< >" */ - int j = 1; + size_t j = 1; while (isspace_c(sbuf[j])) j++; if (((sbuf[j] == '<') || (sbuf[j] == '>')) && (sbuf[0] != sbuf[j])) { /* Allow both <> and >< for NE. */ - lexer_return(TOK_NE, j+1); - } else if (sbuf[1] == '=') { + lexer_return(TOK_NE, j + 1); + } + else if (sbuf[1] == '=') { lexer_return((sbuf[0] == '>') ? TOK_GE : TOK_LE, 2); - } else { + } + else { lexer_return(*sbuf, 1); } } @@ -659,26 +678,28 @@ PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) case '\0': lexer_return(*sbuf, 0); - case '"': - { + case '"': { char *start = ++sbuf; while (*sbuf && (*sbuf != '"')) sbuf++; lvalp->str = copy_substring(start, sbuf); - if (*sbuf) + if (*sbuf) { sbuf++; + } lexer_return(TOK_STR, 0); } - default: - { + default: { char *s = sbuf; - double *td = ft_numparse(&s, FALSE); - if ((!s || *s != ':') && td) { + double val; + + if (ft_numparse(&s, FALSE, &val) >= 0 && + (!s || *s != ':')) { sbuf = s; - lvalp->num = *td; + lvalp->num = val; lexer_return(TOK_NUM, 0); - } else { + } + else { int atsign = 0; char *start = sbuf; /* It is bad how we have to recognise '[' -- sometimes @@ -693,13 +714,16 @@ PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) * i(v5) --> v5#branch */ for (; *sbuf && !strchr(specials, *sbuf); sbuf++) - if (*sbuf == '@') + if (*sbuf == '@') { atsign = 1; - else if (!atsign && *sbuf == '[') + } + else if (!atsign && *sbuf == '[') { break; + } else if (*sbuf == ']') { - if (atsign) + if (atsign) { sbuf++; + } break; } @@ -707,19 +731,25 @@ PPlex(YYSTYPE *lvalp, struct PPltype *llocp, char **line) lexer_return(TOK_STR, 0); } } - } + } /* end of switch over characters */ done: if (ft_parsedb) { - if (token == TOK_STR) + if (token == TOK_STR) { fprintf(stderr, "lexer: TOK_STR, \"%s\"\n", lvalp->str); - else if (token == TOK_NUM) + } + else if (token == TOK_NUM) { fprintf(stderr, "lexer: TOK_NUM, %G\n", lvalp->num); - else + } + else { fprintf(stderr, "lexer: token %d\n", token); + } } *line = sbuf; llocp->stop = sbuf; - return (token); -} + return token; +} /* end of function PPlex */ + + + diff --git a/src/frontend/parser/numparse.c b/src/frontend/parser/numparse.c index 991ff6ab7..43e69fb24 100644 --- a/src/frontend/parser/numparse.c +++ b/src/frontend/parser/numparse.c @@ -4,6 +4,9 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group **********/ /* This routine parses a number. */ +#include +#include +#include #include "ngspice/ngspice.h" #include "ngspice/bool.h" @@ -11,160 +14,226 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "numparse.h" -static double -power10(double num) /* Chris Inbody */ -{ - double d = 1.0; - - while (num-- > 0) - d *= 10.0; - return (d); -} - - bool ft_strictnumparse = FALSE; +static int get_decimal_number(const char **p_str, double *p_val); + + /* Parse a number. This will handle things like 10M, etc... If the number * must not end before the end of the string, then whole is TRUE. * If whole is FALSE and there is more left to the number, the argument - * is advanced to the end of the word. Returns NULL + * is advanced to the end of the word. Returns -1. * if no number can be found or if there are trailing characters when * whole is TRUE. * * If ft_strictnumparse is TRUE, and whole is FALSE, the first of the - * trailing characters must be a '_'. */ - -double * -ft_numparse(char **s, bool whole) + * trailing characters must be a '_'. + * + * Return codes + * +1: String represented an integer number that was converted to a double + * but which can be stored as an int without loss of data + * 0: String represented a non-integer number that was converted to a double + * that may not be expressed as an integer. + * -1: Conversion failure + */ +int ft_numparse(char **p_str, bool whole, double *p_val) { - double mant = 0.0; - int sign = 1, exsign = 1, p; - double expo = 0.0; - static double num; - char *string = *s; + double mant; + double expo; + const char *p_cur = *p_str; /* position in string */ - /* See if the number begins with + or -. */ - if (*string == '+') { - string++; - } else if (*string == '-') { - string++; - sign = -1; - } - - /* We don't want to recognise "P" as 0P, or .P as 0.0P... */ - if ((!isdigit_c(*string) && *string != '.') || - ((*string == '.') && !isdigit_c(string[1]))) - return (NULL); - - /* Now accumulate a number. Note ascii dependencies here... */ - while (isdigit_c(*string)) - mant = mant * 10.0 + (*string++ - '0'); - - /* Now maybe a decimal point. */ - if (*string == '.') { - string++; - p = 1; - while (isdigit_c(*string)) - mant += (*string++ - '0') / power10(p++); + /* Parse the mantissa (or decimal number if no exponent) */ + if (get_decimal_number(&p_cur, &mant) < 0) { + return -1; } /* Now look for the scale factor or the exponent (can't have both). */ - switch (*string) { + switch (*p_cur) { case 'e': case 'E': - /* Parse another number. */ - string++; - if (*string == '+') { - exsign = 1; - string++; - } else if (*string == '-') { - exsign = -1; - string++; + /* Parse another number. Note that a decimal number such as 1.23 + * is allowed as the exponent */ + ++p_cur; + if (get_decimal_number(&p_cur, &expo) < 0) { + expo = 0.0; + --p_cur; /* The "E" was not part of the number */ } - while (isdigit_c(*string)) - expo = expo * 10.0 + (*string++ - '0'); - if (*string == '.') { - string++; - p = 1; - while (isdigit_c(*string)) - expo += (*string++ - '0') / power10(p++); - } - expo *= exsign; break; case 't': case 'T': expo = 12.0; - string++; + ++p_cur; break; case 'g': case 'G': expo = 9.0; - string++; + ++p_cur; break; case 'k': case 'K': expo = 3.0; - string++; + ++p_cur; break; case 'u': case 'U': expo = -6.0; - string++; + ++p_cur; break; case 'n': case 'N': expo = -9.0; - string++; + ++p_cur; break; case 'p': case 'P': expo = -12.0; - string++; + ++p_cur; break; case 'f': case 'F': expo = -15.0; - string++; + ++p_cur; break; case 'm': - case 'M': + case 'M': { + char ch_cur; + /* Can be either m, mil, or meg. */ - if (string[1] && string[2] && - ((string[1] == 'e') || (string[1] == 'E')) && - ((string[2] == 'g') || (string[2] == 'G'))) - { + if (((ch_cur = p_cur[1]) == 'e' || ch_cur == 'E') && + (((ch_cur = p_cur[2]) == 'g') || ch_cur == 'G')) { expo = 6.0; - string += 3; - } else if (string[1] && string[2] && - ((string[1] == 'i') || (string[1] == 'I')) && - ((string[2] == 'l') || (string[2] == 'L'))) - { + p_cur += 3; + } + else if (((ch_cur = p_cur[1]) == 'i' || ch_cur == 'I') && + (((ch_cur = p_cur[2]) == 'l') || ch_cur == 'L')) { expo = -6.0; mant *= 25.4; - string += 3; - } else { + p_cur += 3; + } + else { /* plain m for milli */ expo = -3.0; - string++; + ++p_cur; } break; } - - if (whole && *string != '\0') { - return (NULL); - } else if (ft_strictnumparse && *string && isdigit_c(string[-1])) { - if (*string == '_') - while (isalpha_c(*string) || (*string == '_')) - string++; - else - return (NULL); - } else { - while (isalpha_c(*string) || (*string == '_')) - string++; + default: + expo = 0.0; } - *s = string; - num = sign * mant * pow(10.0, expo); - if (ft_parsedb) - fprintf(cp_err, "numparse: got %e, left = %s\n", num, *s); - return (&num); -} + + /* p_cur is now pointing to the fist char after the number */ + { + /* If whole is true, it must be the end of the string */ + const char ch_cur = *p_cur; + if (whole && ch_cur != '\0') { + return -1; + } + + /* If ft_strictnumparse is true, the first character after the + * string representing the number, if any, must be '_' */ + if (ft_strictnumparse && ch_cur != '\0' && ch_cur != '_') { + return -1; + } + } + + /* Remove the alpha and '_' characters after the number */ + for ( ; ; ++p_cur) { + const char ch_cur = *p_cur; + if (!isalpha(ch_cur) && ch_cur != '_') { + break; + } + } + + /* Return results */ + { + /* Value of number. Ternary operator used to prevent avoidable + * calls to pow(). */ + const double val = *p_val = mant * + (expo == 0.0 ? 1.0 : pow(10.0, expo)); + *p_str = (char *) p_cur; /* updated location in string */ + + if (ft_parsedb) { /* diagnostics for parsing the number */ + fprintf(cp_err, "numparse: got %e, left = \"%s\"\n", + val, p_cur); + } + + /* Test if the number can be represented as an intger */ + return (double) (int) val == val; + } +} /* end of function ft_numparse */ + + + +/* This function converts the string form of a decimal number at *p_str to + * its value and returns it in *p_val. The location in *p_str is advanced + * to the first character after the number if the conversion is OK and + * is unchanged otherwise. + * + * Return codes + * -1: Conversion failure. *p_val is unchanged + * 0: Conversion OK. The string was not the representation of an integer + * +1: Conversion OK. The string was an integer */ +static int get_decimal_number(const char **p_str, double *p_val) +{ + double sign = 1.0; /* default sign multiplier if missing is 1.0 */ + const char *p_cur = *p_str; + char ch_cur = *p_cur; /* 1st char */ + bool f_is_integer = TRUE; /* assume integer */ + + /* Test for a sign */ + if (ch_cur == '+') { /* Advance position in string. Sign unchanged */ + ch_cur = *++p_cur; + } + else if (ch_cur == '-') { /* Advance position in string. Sign = -1 */ + ch_cur = *++p_cur; + sign = -1.0; + } + + /* Ensure string either starts with a digit or a decimal point followed + * by a digit */ + if ((!isdigit(ch_cur) && ch_cur != '.') || + ((ch_cur == '.') && !isdigit(p_cur[1]))) { + return -1; + } + + /* Parse and compute the number. Assuming 0-9 digits are contiguous and + * increasing in char representation (true for ASCII and EBCDIC) */ + double val = 0.0; + for ( ; ; p_cur++) { + const unsigned int digit = + (unsigned int) *p_cur - (unsigned int) '0'; + if (digit > 9) { /* not digit */ + break; + } + val = val * 10.0 + (double) digit; + } + + /* Handle fraction, if any */ + if (*p_cur == '.') { + const char *p0 = ++p_cur; /* start of fraction */ + double numerator = 0.0; + + /* Not an integer expression (even if no fraction after the '.') */ + f_is_integer = FALSE; + + /* Add the fractional part of the number */ + for ( ; ; p_cur++) { + const unsigned int digit = + (unsigned int) *p_cur - (unsigned int) '0'; + if (digit > 9) { /* not digit */ + /* Add fractional part to intergral part from earlier */ + val += numerator * pow(10, (double) (p0 - p_cur)); + break; + } + numerator = numerator * 10.0 + (double) digit; + } + } /* end of case of fraction */ + + /* Return the value and update the position in the string */ + *p_val = sign * val; + *p_str = p_cur; + return (int) f_is_integer; +} /* end of function get_decimal_number */ + + + diff --git a/src/frontend/plotting/plotit.c b/src/frontend/plotting/plotit.c index d53f7b436..6daa82ba1 100644 --- a/src/frontend/plotting/plotit.c +++ b/src/frontend/plotting/plotit.c @@ -29,7 +29,6 @@ static bool sameflag; static double * getlims(wordlist *wl, char *name, int number) { - double *d; wordlist *beg, *wk; int n; @@ -43,39 +42,35 @@ getlims(wordlist *wl, char *name, int number) wk = beg->wl_next; - d = TMALLOC(double, number); + double * const d = TMALLOC(double, number); /* alloc for returned vals */ for (n = 0; n < number; n++) { char *ss; - double *td; if (!wk) { fprintf(cp_err, "Syntax error: not enough parameters for \"%s\".\n", name); txfree(d); - return NULL; + return (double *) NULL; } ss = wk->wl_word; - td = ft_numparse(&ss, FALSE); - - if (!td) { + if (ft_numparse(&ss, FALSE, d + n) < 0) { /* put val in d[n] */ fprintf(cp_err, "Syntax error: bad parameters for \"%s\".\n", name); txfree(d); - return NULL; + return (double *) NULL; } - d[n] = *td; - wk = wk->wl_next; - } + } /* end of loop over numbers */ - wl_delete_slice(beg, wk); + wl_delete_slice(beg, wk); /* remove param name and its value nodes */ return d; -} +} /* end of function getlims */ + /* Extend a data vector to length by replicating the last element, or diff --git a/src/frontend/postcoms.c b/src/frontend/postcoms.c index 978bdecf2..846ae1115 100644 --- a/src/frontend/postcoms.c +++ b/src/frontend/postcoms.c @@ -444,8 +444,9 @@ com_write(wordlist *wl) else names = ft_getpnames(&all, TRUE); - if (names == NULL) + if (names == NULL) { return; + } for (pn = names; pn; pn = pn->pn_next) { d = ft_evaluate(pn); @@ -731,22 +732,27 @@ com_transpose(wordlist *wl) struct dvec *d; char *s; - while (wl) { + /* For each vector named in the wordlist, perform the transform to + * it and the vectors associated with it through v_link2 */ + for ( ; wl != (wordlist *) NULL; wl = wl->wl_next) { s = cp_unquote(wl->wl_word); d = vec_get(s); tfree(s); /*DG: Avoid Memory Leak */ - if (d == NULL) + if (d == NULL) { + /* Print error message, but continue with other vectors */ fprintf(cp_err, "Error: no such vector as %s.\n", wl->wl_word); - else + } + else { + /* Transpose the named vector and vectors tied to it + * through v_link2 */ while (d) { vec_transpose(d); d = d->v_link2; } - if (wl->wl_next == NULL) - return; - wl = wl->wl_next; - } -} + } + } /* end of loop over words in wordlist */ +} /* end of function com_transpose */ + /* Take a set of vectors and form a new vector of the nth elements of each. */ @@ -758,19 +764,23 @@ com_cross(wordlist *wl) struct pnode *pn, *names; int i, ind; bool comp = FALSE; - double *d; newvec = wl->wl_word; wl = wl->wl_next; s = wl->wl_word; - if ((d = ft_numparse(&s, FALSE)) == NULL) { - fprintf(cp_err, "Error: bad number %s\n", wl->wl_word); - return; - } - if ((ind = (int)*d) < 0) { - fprintf(cp_err, "Error: badstrchr %d\n", ind); - return; + + { + double val; + if (ft_numparse(&s, FALSE, &val) <= 0) { + fprintf(cp_err, "Error: bad index value %s\n", wl->wl_word); + return; + } + if ((ind = (int) val) < 0) { + fprintf(cp_err, "Error: badstrchr %d\n", ind); + return; + } } + wl = wl->wl_next; names = ft_getpnames(wl, TRUE); for (pn = names; pn; pn = pn->pn_next) { diff --git a/src/frontend/spec.c b/src/frontend/spec.c index 11db680d9..73cd11e84 100644 --- a/src/frontend/spec.c +++ b/src/frontend/spec.c @@ -24,7 +24,7 @@ com_spec(wordlist *wl) { ngcomplex_t **fdvec = NULL; double **tdvec = NULL; - double *freq, *win = NULL, *time, *dc = NULL; + double *win = NULL, *time, *dc = NULL; double startf, stopf, stepf, span; int fpts, i, j, k, tlen, ngood; bool trace; @@ -45,27 +45,24 @@ com_spec(wordlist *wl) s = wl->wl_word; tlen = (plot_cur->pl_scale)->v_length; - if ((freq = ft_numparse(&s, FALSE)) == NULL || (*freq < 0.0)) { + if (ft_numparse(&s, FALSE, &startf) < 0 || startf < 0.0) { fprintf(cp_err, "Error: bad start freq %s\n", wl->wl_word); goto done; } - startf = *freq; wl = wl->wl_next; s = wl->wl_word; - if ((freq = ft_numparse(&s, FALSE)) == NULL || (*freq <= startf)) { + if (ft_numparse(&s, FALSE, &stopf) < 0 || stopf <= startf) { fprintf(cp_err, "Error: bad stop freq %s\n", wl->wl_word); goto done; } - stopf = *freq; wl = wl->wl_next; s = wl->wl_word; - if ((freq = ft_numparse(&s, FALSE)) == NULL || !(*freq <= (stopf-startf))) { + if (ft_numparse(&s, FALSE, &stepf) < 0 || stepf > stopf - startf) { fprintf(cp_err, "Error: bad step freq %s\n", wl->wl_word); goto done; } - stepf = *freq; wl = wl->wl_next; time = (plot_cur->pl_scale)->v_realdata; @@ -212,9 +209,8 @@ com_spec(wordlist *wl) VF_REAL | VF_PERMANENT | VF_PRINT, fpts, NULL); vec_new(f); - freq = f->v_realdata; - tdvec = TMALLOC(double *, ngood); + tdvec = TMALLOC(double *, ngood); fdvec = TMALLOC(ngcomplex_t *, ngood); for (i = 0, vec = vlist; i < ngood; i++) { tdvec[i] = vec->v_realdata; @@ -238,36 +234,41 @@ com_spec(wordlist *wl) } } trace = cp_getvar("spectrace", CP_BOOL, NULL, 0); - for (j = (startf == 0 ? 1 : 0); j < fpts; j++) { - freq[j] = startf + j*stepf; - if (trace) - fprintf(cp_err, "spec: %e Hz: \r", freq[j]); - for (i = 0; i < ngood; i++) { - fdvec[i][j].cx_real = 0; - fdvec[i][j].cx_imag = 0; - } - for (k = 1; k < tlen; k++) { - double - amp = 2*win[k]/(tlen-1), - rad = 2*M_PI*time[k]*freq[j], - cosa = amp*cos(rad), - sina = amp*sin(rad); - for (i = 0; i < ngood; i++) { - double value = tdvec[i][k]-dc[i]; - fdvec[i][j].cx_real += value*cosa; - fdvec[i][j].cx_imag += value*sina; - } - } -#ifdef HAS_PROGREP - SetAnalyse("spec", (int)(j * 1000./ fpts)); -#endif - } - if (startf == 0) { - freq[0] = 0; - for (i = 0; i < ngood; i++) { - fdvec[i][0].cx_real = dc[i]; - fdvec[i][0].cx_imag = 0; + { + double * const freq = f->v_realdata; + for (j = (startf == 0 ? 1 : 0); j < fpts; j++) { + freq[j] = startf + j*stepf; + if (trace) { + fprintf(cp_err, "spec: %e Hz: \r", freq[j]); + } + for (i = 0; i < ngood; i++) { + fdvec[i][j].cx_real = 0; + fdvec[i][j].cx_imag = 0; + } + for (k = 1; k < tlen; k++) { + double + amp = 2*win[k]/(tlen-1), + rad = 2*M_PI*time[k]*freq[j], + cosa = amp*cos(rad), + sina = amp*sin(rad); + for (i = 0; i < ngood; i++) { + double value = tdvec[i][k]-dc[i]; + fdvec[i][j].cx_real += value*cosa; + fdvec[i][j].cx_imag += value*sina; + } + } +#ifdef HAS_PROGREP + SetAnalyse("spec", (int)(j * 1000./ fpts)); +#endif + } + + if (startf == 0) { + freq[0] = 0; + for (i = 0; i < ngood; i++) { + fdvec[i][0].cx_real = dc[i]; + fdvec[i][0].cx_imag = 0; + } } } diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 9c4151c11..270303605 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -77,11 +77,13 @@ wordlist *cp_varwl(struct variable *var) } return wl_cons(buf, NULL); -} +} /* end of function cp_varwl */ + /* Set a variable. */ -void cp_vset(const char *varname, enum cp_types type, const void *value) +void cp_vset(const char *varname, enum cp_types type, + const void *value) { struct variable *v, *u, *w; int i; @@ -387,14 +389,12 @@ static void update_option_variables(const char *sz_var_name, Generate variables (real, string or list) Value in double quotes will always become string variable. Without quotes tokens like 2N5401_C will be evaluated as real number 2n, i.e. 2e-9 */ -struct variable * -cp_setparse(wordlist *wl) +struct variable *cp_setparse(wordlist *wl) { char *name = NULL, *val, *copyval, *s, *ss; - double *td; struct variable *listv = NULL, *vv, *lv = NULL; struct variable *vars = NULL; - int balance; + /* Step through the list of words. Words may be various combinations of * the information needed to set a variable. For example, to set x to * the value 3, the data could be supplied as one word x=3, two words @@ -415,21 +415,22 @@ cp_setparse(wordlist *wl) continue; } - if (wl && eq(wl->wl_word, "=")) { + if (wl && eq(wl->wl_word, "=")) { /* name= */ wl = wl->wl_next; if (wl == NULL) { fprintf(cp_err, "Error: bad set form.\n"); tfree(name); /*DG: cp_unquote Memory leak*/ if (ft_stricterror) controlled_exit(EXIT_BAD); - return (NULL); + return NULL; } val = wl->wl_word; wl = wl->wl_next; - } else if (wl && (*wl->wl_word == '=')) { + } else if (wl && (*wl->wl_word == '=')) { /* name=val */ val = wl->wl_word + 1; wl = wl->wl_next; } else if ((s = strchr(name, '=')) != NULL) { + /* name=value or name=value */ val = s + 1; *s = '\0'; if (*val == '\0') { @@ -438,18 +439,19 @@ cp_setparse(wordlist *wl) tfree(name); /*DG: cp_unquote Memory leak: free name before exiting*/ if (ft_stricterror) controlled_exit(EXIT_BAD); - return (NULL); + return NULL; } else { val = wl->wl_word; wl = wl->wl_next; } } - } else { + } + else { fprintf(cp_err, "Error: bad set form.\n"); tfree(name); /*DG: cp_unquote Memory leak: free name befor exiting */ if (ft_stricterror) controlled_exit(EXIT_BAD); - return (NULL); + return NULL; } /* if val is in double quotes, treat as string */ @@ -463,11 +465,12 @@ cp_setparse(wordlist *wl) strcpy(val, copyval); tfree(copyval); + /* Test for a list variable */ if (eq(val, "(")) { /* The beginning of a list... We have to walk down the * list until we find a close paren... If there are nested * ()'s, treat them as tokens... */ - balance = 1; + int balance = 1; while (wl && wl->wl_word) { if (eq(wl->wl_word, "(")) { balance++; @@ -481,11 +484,13 @@ cp_setparse(wordlist *wl) vv = var_alloc_string(NULL, copy(ss), NULL); } else { - td = ft_numparse(&ss, FALSE); - if (td) - vv = var_alloc_real(NULL, *td, NULL); - else + double dbl_val; + if (ft_numparse(&ss, FALSE, &dbl_val) >= 0) { + vv = var_alloc_real(NULL, dbl_val, NULL); + } + else { vv = var_alloc_string(NULL, copy(ss), NULL); + } } tfree(copyval); if (listv) { @@ -501,9 +506,10 @@ cp_setparse(wordlist *wl) tfree(name); /* va: cp_unquote memory leak: free name before exiting */ if (ft_stricterror) controlled_exit(EXIT_BAD); - return (NULL); + return NULL; } + /* Add list variable to linked list of variables. */ vars = var_alloc_vlist(copy(name), listv, vars); wl = wl->wl_next; @@ -516,10 +522,10 @@ cp_setparse(wordlist *wl) vars = var_alloc_string(copy(name), copy(copyval), vars); } else { - td = ft_numparse(&ss, FALSE); - if (td) { + double dbl_val; + if (ft_numparse(&ss, FALSE, &dbl_val) >= 0) { /*** We should try to get CP_NUM's... */ - vars = var_alloc_real(name, *td, vars); + vars = var_alloc_real(name, dbl_val, vars); } else { vars = var_alloc_string(name, copy(val), vars); @@ -532,8 +538,9 @@ cp_setparse(wordlist *wl) if (name) { tfree(name); } - return (vars); -} + return vars; +} /* end of function cp_setparse */ + /* free the struct variable. The type of the union is given by va_type */ @@ -543,15 +550,15 @@ free_struct_variable(struct variable *v) while (v) { struct variable *next_v = v->va_next; if (v->va_name) - tfree(v->va_name); + txfree(v->va_name); if (v->va_type == CP_LIST) free_struct_variable(v->va_vlist); if (v->va_type == CP_STRING) - tfree(v->va_string); - tfree(v); + txfree(v->va_string); + txfree(v); v = next_v; } -} +} /* end of function free_struct_variable */ void cp_remvar(char *varname) @@ -770,11 +777,19 @@ char cp_dol = '$'; /* Non-alphanumeric characters that may appear in variable names. < is very * special... */ - #define VALIDCHARS "$-_<#?@.()[]&" -char * -span_var_expr(char *t) +/* This function determines the first character after a variable name and + * returns its address. + * + * Parameter + * t: Address of the variable name whose end is to be found. This is the + * address of the first character following the leading $ + * + * Return value + * Address of the first character after the variable name. + */ +char *span_var_expr(char *t) { int parenthesis = 0; int brackets = 0; @@ -805,12 +820,11 @@ span_var_expr(char *t) } return t; -} +} /* end of function span_var_expr */ /* Substitute variable name by its value and restore to wordlist */ -wordlist * -cp_variablesubst(wordlist *wlist) +wordlist *cp_variablesubst(wordlist *wlist) { wordlist *wl; @@ -849,24 +863,26 @@ cp_variablesubst(wordlist *wlist) tfree(x); } else { wordlist *next = wl->wl_next; - if (wlist == wl) + if (wlist == wl) { wlist = next; + } wl_delete_slice(wl, next); - if (!next) + if (!next) { /* wordlist ends after wl */ return wlist; + } wl = next; i = 0; } - } - } + } /* end of loop over parts of wordlist node */ + } /* end of loop over words in wordlist */ + + return wlist; +} /* end of function cp_variablesubst */ - return (wlist); -} /* Evaluate a variable. */ -wordlist * -vareval(char *string) +wordlist *vareval(/* NOT const */ char *string) { struct variable *v, *vfree = NULL; wordlist *wl; @@ -887,8 +903,8 @@ vareval(char *string) case '$': wl = wl_cons(tprintf("%d", getpid()), NULL); - tfree(oldstring); - return (wl); + txfree(oldstring); + return wl; case '<': (void) fflush(cp_out); @@ -903,8 +919,8 @@ vareval(char *string) /* This is a hack. */ if (!wl->wl_word) wl->wl_word = copy(""); - tfree(oldstring); - return (wl); + txfree(oldstring); + return wl; case '?': string++; @@ -918,8 +934,8 @@ vareval(char *string) } wl = wl_cons(copy(v ? "1" : "0"), NULL); free_struct_variable(vfree); - tfree(oldstring); - return (wl); + txfree(oldstring); + return wl; case '#': string++; @@ -933,23 +949,26 @@ vareval(char *string) } if (!v) { fprintf(cp_err, "Error: %s: no such variable.\n", string); - tfree(oldstring); - return (NULL); + txfree(oldstring); + return NULL; } - if (v->va_type == CP_LIST) - for (v = v->va_vlist, i = 0; v; v = v->va_next) + if (v->va_type == CP_LIST) { + for (v = v->va_vlist, i = 0; v; v = v->va_next) { i++; - else + } + } + else { i = (v->va_type != CP_BOOL); + } wl = wl_cons(tprintf("%d", i), NULL); - tfree(oldstring); + txfree(oldstring); free_struct_variable(vfree); - return (wl); + return wl; case '\0': wl = wl_cons(copy("$"), NULL); - tfree(oldstring); - return (wl); + txfree(oldstring); + return wl; } vfree = NULL; //just in case ... @@ -980,8 +999,8 @@ vareval(char *string) } if (!v) { fprintf(cp_err, "Error: %s: no such variable.\n", string); - tfree(oldstring); - return (NULL); + txfree(oldstring); + return NULL; } wl = cp_varwl(v); free_struct_variable(vfree); @@ -1000,7 +1019,7 @@ vareval(char *string) r = vareval(range); if (!r || r->wl_next) { fprintf(cp_err, "Error: %s: illegal index.\n", string); - tfree(oldstring); + txfree(oldstring); wl_free(r); return NULL; } @@ -1018,8 +1037,9 @@ vareval(char *string) up--, low--; wl = wl_range(wl, low, up); wl_free(r); - } - tfree(oldstring); + } /* end of case of range given for variable */ + + txfree(oldstring); return (wl); } diff --git a/src/include/ngspice/cpstd.h b/src/include/ngspice/cpstd.h index 121654381..b6498de39 100644 --- a/src/include/ngspice/cpstd.h +++ b/src/include/ngspice/cpstd.h @@ -12,10 +12,7 @@ Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group #define ngspice_CPSTD_H -#ifndef FILE -/* XXX Bogus */ -# include -#endif +#include /* FIXME: Split this file and adjust all callers to use new header files */ #if 0 @@ -23,6 +20,7 @@ Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group #endif #include "ngspice/bool.h" +#include "ngspice/dstring.h" #include "ngspice/wordlist.h" #include "ngspice/complex.h" @@ -30,8 +28,12 @@ Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group extern char *tildexpand(char *string); extern void printnum(char *buf, double num); +int printnum_ds(DSTRING *p_ds, double num); extern int cp_numdgt; extern void cp_printword(char *string, FILE *fp); + + + #endif diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 9a4c9bae0..e7f405f49 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -245,7 +245,7 @@ extern void ft_polyderiv(double *coeffs, int degree); /* numparse.c */ extern bool ft_strictnumparse; -double *ft_numparse(char **s, bool whole); +int ft_numparse(char **s, bool whole, double *p_val); /* options.c */ diff --git a/src/misc/printnum.c b/src/misc/printnum.c index 99a8ae922..297af4cd4 100644 --- a/src/misc/printnum.c +++ b/src/misc/printnum.c @@ -8,23 +8,49 @@ Modified: 2001 Paolo Nenzi * It is up to the caller to allocate space for strings. */ +#include + #include "ngspice/ngspice.h" #include "printnum.h" -#include int cp_numdgt = -1; -void printnum(char *buf, double num) + +static inline int get_num_width(double num) { int n; - if (cp_numdgt > 1) + if (cp_numdgt > 1) { n = cp_numdgt; - else + } + else { n = 6; - if (num < 0.0) + } + if (num < 0.0 && n > 1) { n--; + } + return n; +} /* end of function get_num_width */ + + + +/* This funtion writes num to buf. It can cause buffer overruns. The size of + * buf is unknown, so cp_numdgt can be large enough to cause sprintf() + * to write past the end of the array. */ +void printnum(char *buf, double num) +{ + int n = get_num_width(num); (void) sprintf(buf, "%.*e", n, num); - -} +} /* end of function printnum */ + + + +int printnum_ds(DSTRING *p_ds, double num) +{ + const int n = get_num_width(num); + return ds_cat_printf(p_ds, "%.*e", n, num); +} /* end of function printnum_ds */ + + + diff --git a/src/misc/printnum.h b/src/misc/printnum.h index b569c77fc..8426b4fe2 100644 --- a/src/misc/printnum.h +++ b/src/misc/printnum.h @@ -6,6 +6,9 @@ #ifndef ngspice_PRINTNUM_H #define ngspice_PRINTNUM_H -void printnum(char * buf, double num); +#include "ngspice/dstring.h" + +void printnum(char *buf, double num); +int printnum_ds(DSTRING *p_dstring, double num); #endif From 713fad16647f2ac46c0d9e14602239ed7352e763 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Fri, 6 Dec 2019 19:39:08 -0500 Subject: [PATCH 14/58] Fixed potential infinite loop (until memory is exhausted) and added several utility functions for processing strings. --- src/include/ngspice/ngspice.h | 1 + src/include/ngspice/stringutil.h | 130 ++++- src/misc/string.c | 820 ++++++++++++++++++++++++++----- 3 files changed, 794 insertions(+), 157 deletions(-) diff --git a/src/include/ngspice/ngspice.h b/src/include/ngspice/ngspice.h index 211f91f70..3e1c1c1f4 100644 --- a/src/include/ngspice/ngspice.h +++ b/src/include/ngspice/ngspice.h @@ -241,6 +241,7 @@ extern double x_atanh(double); #define HUGE HUGE_VAL #endif +void findtok_noparen(char **p_str, char **p_token, char **p_token_end); extern char *gettok_noparens(char **s); extern char *gettok_node(char **s); extern char *gettok_iv(char **s); diff --git a/src/include/ngspice/stringutil.h b/src/include/ngspice/stringutil.h index 17b25052d..e990e4efa 100644 --- a/src/include/ngspice/stringutil.h +++ b/src/include/ngspice/stringutil.h @@ -6,37 +6,117 @@ #ifndef ngspice_STRINGUTIL_H #define ngspice_STRINGUTIL_H +#include +#include + #include "ngspice/config.h" #include "ngspice/bool.h" -#include - - -int prefix(const char *p, const char *s); -char * copy(const char *str); -char * copy_substring(const char *str, const char *end); -int substring(const char *sub, const char *str); -void appendc(char *s, char c); -int scannum(char *str); -int cieq(const char *p, const char *s); -int ciprefix(const char *p, const char *s); -void strtolower(char *str); -void strtoupper(char *str); -char * stripWhiteSpacesInsideParens(const char *str); -char * gettok(char **s); -char * gettok_instance(char **); -char * gettok_char(char **s, char p, bool inc_p, bool nested); -int model_name_match(const char *token, const char *model_name); - -extern char *tvprintf(const char *fmt, va_list args); #ifdef __GNUC__ -extern char *tprintf(const char *fmt, ...) __attribute__ ((format (__printf__, 1, 2))); +#define ATTR_TPRINTF __attribute__ ((format (__printf__, 1, 2))) #else -extern char *tprintf(const char *fmt, ...); +#define ATTR_TPRINTF #endif +/* Structure for storing state to find substring matches in a string */ +struct substring_match_info { + /* Input data */ + size_t n_char_pattern; /* length of pattern being located */ + const char *p_pattern; /* pattern to find */ + size_t n_char_string; /* length of string to search */ + const char *p_string; /* String to search. Final null not required */ + bool f_overlap; /* flag that substring matches can overlap */ + + /* Intermediate results */ + size_t n_char_pattern_1; /* length of pattern being located - 1 */ + size_t msb_factor; /* constant related to updating hash */ + size_t h_pattern; /* hash value of pattern */ + size_t h_string; /* current hash value of string */ + const char *p_last; /* last possible substring match location */ + bool f_done; /* flag that last match was found */ +}; + +void appendc(char *s, char c); +int cieq(const char *p, const char *s); +int cieqn(const char *p, const char *s, size_t n); +int ciprefix(const char *p, const char *s); +char *dup_string(const char *str, size_t n_char); +char *find_first_of(const char *haystack, + unsigned int n_needle, const char *p_needle); +int get_comma_separated_values(char *values[], char *str); +int get_int_n(const char *str, size_t n, int *p_value); +#ifdef COMPILE_UNUSED_FUNCTIONS +size_t get_substring_matches(size_t n_char_pattern, const char *p_pattern, + size_t n_char_string, const char *p_string, + size_t n_elem_buf, char *p_match_buf, bool f_overlap); +#endif +char *gettok(char **s); +char *gettok_char(char **s, char p, bool inc_p, bool nested); +char *gettok_instance(char **); +bool has_escape_or_quote(size_t n, const char *str); +bool is_arith_char(char c); +bool isquote(char ch); +int model_name_match(const char *token, const char *model_name); +int prefix(const char *p, const char *s); +int prefix_n(size_t n_char_prefix, const char *prefix, + size_t n_char_string, const char *string); +int scannum_adv(char **p_str); +bool str_has_arith_char(char *s); +char *stripWhiteSpacesInsideParens(const char *str); +void strtolower(char *str); +void strtoupper(char *str); +void substring_match_init(size_t n_char_pattern, const char *p_pattern, + size_t n_char_string, const char *p_string, bool f_overlap, + struct substring_match_info *p_scan_state); +char *substring_match_next(struct substring_match_info *p_scan_state); +int substring_n(size_t n_char_pattern, const char *p_pattern, + size_t n_char_str, const char *p_str); +char *tprintf(const char *fmt, ...) ATTR_TPRINTF; +char *tvprintf(const char *fmt, va_list args); + + + +/* Allocate and create a copy of a string if the argument is not null or + * returns null if it is. */ +inline char *copy(const char *str) +{ + return str == (char *) NULL ? + (char *) NULL : dup_string(str, strlen(str)); +} /* end of function copy */ + + + +/* Allocate a buffer and copy a substring, from 'str' to 'end' + * including *str, excluding *end + */ +inline char *copy_substring(const char *str, const char *end) +{ + return dup_string(str, (size_t) (end - str)); +} /* end of function copy_substring */ + + + +/* Like scannum but *p_str is advanced past the number */ + +/* Try to identify an unsigned integer that begins a string. Stop when a + * non- numeric character is reached. There is no way to distinguish + * between a value of 0 and a string that does not contain a numeric + * value. */ +inline int scannum(const char *str) +{ + return scannum_adv((char **) &str); +} /* end of function scannum */ + + + +/* Determine whether sub is a substring of str. */ +inline int substring(const char *sub, const char *str) +{ + return strstr(sub, str) != (char *) NULL; +} /* end of function substring */ + #ifdef CIDER /* cider integration */ @@ -44,9 +124,5 @@ int cinprefix(register char *p, register char *s, register int n); int cimatch(register char *p, register char *s); #endif -bool isquote(char ch); -bool is_arith_char(char c); -bool str_has_arith_char(char *s); -int get_comma_separated_values( char *values[], char *str ); -#endif +#endif /* include guard */ diff --git a/src/misc/string.c b/src/misc/string.c index 19cecec10..8dfdfd734 100644 --- a/src/misc/string.c +++ b/src/misc/string.c @@ -5,97 +5,132 @@ Copyright 1990 Regents of the University of California. All rights reserved. /* * String functions */ +#include +#include #include "ngspice/ngspice.h" #include "ngspice/stringutil.h" #include "ngspice/stringskip.h" #include "ngspice/dstring.h" -#include + +/* Instantiations of string functions in case inlining is not performed */ +char *copy(const char *str); +char *copy_substring(const char *str, const char *end); +int scannum(const char *str); +int substring(const char *sub, const char *str); -int -prefix(const char *p, const char *s) + +static size_t get_kr_msb_factor(size_t n); +static size_t kr_hash(size_t n, const char *p); +static inline const char *next_substr( + size_t n_char_pattern, const char *p_pattern, + const char **pp_string, const char * const p_last, + const size_t msb_factor, const size_t h_pattern, size_t *p_h_string); +static bool can_overlap(size_t n_char_pattern, const char * const p_pattern); + + +/* This function returns true if the string s begins with the + * string p and false otherwise. */ +int prefix(const char *p, const char *s) { while (*p && (*p == *s)) p++, s++; return *p == '\0'; -} +} /* end of function prefix */ -/* Create a copy of a string. */ -char * -copy(const char *str) +/* This function returns 1 if string begins with prefix and 0 otherwise. + * Neither the prefix nor string needs a null termination. */ +int prefix_n(size_t n_char_prefix, const char *prefix, + size_t n_char_string, const char *string) { - char *p; + /*Test that string is long enough */ + if (n_char_prefix > n_char_string) { + return 0; + } - if (!str) - return NULL; - - if ((p = TMALLOC(char, strlen(str) + 1)) != NULL) - (void) strcpy(p, str); - return p; -} + return memcmp(prefix, string, n_char_prefix) == 0; +} /* end of function prefix_n */ -/* copy a substring, from 'str' to 'end' - * including *str, excluding *end + + +/* This function allocates a buffer and copies the specified number of + * characters from the input string into the buffer followed by a + * terminating null. + * + * Paramters + * str: String to copy + * n_char: Number of characters to copy + * + * Return values + * NULL: Allocation failure + * otherwise: The initialized string. */ -char * -copy_substring(const char *str, const char *end) +char *dup_string(const char *str, size_t n_char) { - size_t n = (size_t) (end - str); char *p; - if ((p = TMALLOC(char, n + 1)) != NULL) { - (void) strncpy(p, str, n); - p[n] = '\0'; + if ((p = TMALLOC(char, n_char + 1)) != NULL) { + (void) strncpy(p, str, n_char); + p[n_char] = '\0'; } return p; -} +} /* end of function dup_string */ -char * -tvprintf(const char *fmt, va_list args) + +char *tvprintf(const char *fmt, va_list args) { char buf[1024]; char *p = buf; int size = sizeof(buf); + int nchars; for (;;) { - int nchars; va_list ap; va_copy(ap, args); nchars = vsnprintf(p, (size_t) size, fmt, ap); va_end(ap); - if (nchars == -1) { // compatibility to old implementations - size *= 2; + /* This case was previously handled by doubling the size of + * the buffer for "compatibility to old implementations." + * However, vsnprintf is defined in both C99 and SUSv2 from 1997. + * There is a slight difference which does not affect this + * usage, but both return negative values (possibly -1) on an + * encoding error, which would lead to an infinte loop (until + * memory was exhausted) with the old behavior */ + if (nchars < 0) { + controlled_exit(-1); } - else if (nchars >= size) { - /* Output was truncated. Returned value is the number of chars - * that would have been written if the buffer were large enough - * excluding the terminiating null. */ - size = nchars + 1; /* min required allocation size */ - } - else { /* String formatted OK */ + + if (nchars < size) { /* String formatted OK */ break; } + /* Output was truncated. Returned value is the number of chars + * that would have been written if the buffer were large enough + * excluding the terminiating null. */ + size = nchars + 1; /* min required allocation size */ + /* Allocate a larger buffer */ - if (p == buf) + if (p == buf) { p = TMALLOC(char, size); - else + } + else { p = TREALLOC(char, p, size); + } } /* Return the formatted string, making a copy on the heap if the * stack's buffer (buf) contains the string */ - return (p == buf) ? copy(p) : p; + return (p == buf) ? dup_string(p, (size_t) nchars) : p; } /* end of function tvprintf */ @@ -103,8 +138,7 @@ tvprintf(const char *fmt, va_list args) /* This function returns an allocation containing the string formatted * according to fmt and the variadic argument list provided. It is a wrapper * around tvprintf() which processes the argumens as a va_list. */ -char * -tprintf(const char *fmt, ...) +char *tprintf(const char *fmt, ...) { char *rv; va_list ap; @@ -117,103 +151,173 @@ tprintf(const char *fmt, ...) } /* end of function tprintf */ -/* Determine whether sub is a substring of str. */ -/* Like strstr( ) XXX */ - -int -substring(const char *sub, const char *str) -{ - for (; *str; str++) - if (*str == *sub) { - const char *s = sub, *t = str; - for (; *s; s++, t++) - if (!*t || (*s != *t)) - break; - if (*s == '\0') - return TRUE; - } - - return FALSE; -} - - /* Append one character to a string. Don't check for overflow. */ /* Almost like strcat( ) XXX */ - -void -appendc(char *s, char c) +void appendc(char *s, char c) { - while (*s) + while (*s) { s++; + } *s++ = c; *s = '\0'; -} +} /* end of function appendc */ -/* Try to identify an integer that begins a string. Stop when a non- - * numeric character is reached. - */ -/* Like atoi( ) XXX */ -int -scannum(char *str) +/* Returns the unsigned number at *p_str or 0 if there is none. *p_str + * points to the first character after the number that was read, so + * it is possible to distingish between the value 0 and a missing number + * by testing if the string has been advanced. */ +int scannum_adv(char **p_str) { + const char *str = *p_str; int i = 0; - while (isdigit_c(*str)) + while (isdigit_c(*str)) { i = i * 10 + *(str++) - '0'; + } + *p_str = (char *) str; /* locate end of number */ return i; -} +} /* end of function scannum_adv */ + + + +/* This function returns the integer at the current string location. + * The string does not need to be null-terminated. + * + * Parameters + * str: String containing the integer to return at the beginning + * n: Number of characters in the string + * p_value: Address where the integer is returned + * + * Return values + * -1: No integer present + * -2: Overflow + * >0: Number of characters in the integer + */ +int get_int_n(const char *str, size_t n, int *p_value) +{ + if (n == 0) { /* no string */ + return -1; + } + + unsigned int value = 0; + const char *p_cur = str; + const char * const p_end = str + n; + bool f_neg; + if (*p_cur == '-') { /* Check for leading negative sign */ + f_neg = 1; + ++p_cur; + } + else { + f_neg = 0; + } + + /* Iterate over chars until end or char that is not numeric */ + for ( ; p_cur != p_end; ++p_cur) { + char ch_cur = *str; + if (!isdigit(ch_cur)) { /* Test for exit due to non-numeric char */ + break; + } + + /* Compute new value and check for overflow. */ + const unsigned int value_new = 10 * value + (ch_cur - '0'); + if (value_new < value) { + return -2; + } + value = value_new; + } /* end of loop over digits */ + + /* Test for at least one digit */ + if (p_cur == str + f_neg) { + return -1; /* no digit */ + } + + /* Test for overflow. + * If negative, can be 1 greater (-2**n vs 2**n -1) */ + if (value - f_neg > INT_MAX) { + return -2; + } + + /* Take negative if negative sign present. (This operation works + * correctly if value == INT_MIN since -INT_MIN == INT_MIN */ + *p_value = f_neg ? -(int) value : (int) value; + + return (int) (p_cur - str); /* number of chars in the number */ +} /* end of function get_int_n */ + /* Case insensitive str eq. */ /* Like strcasecmp( ) XXX */ - -int -cieq(const char *p, const char *s) +int cieq(const char *p, const char *s) { - for (; *p; p++, s++) - if (tolower_c(*p) != tolower_c(*s)) + for (; *p; p++, s++) { + if (tolower_c(*p) != tolower_c(*s)) { return FALSE; + } + } return *s == '\0'; -} +} /* end of function cieq */ + + + +/* Case-insensitive string compare fore equialty with explicit length + * given. Neither character array needs to be null terminated. By not + * including the trailing null in the count, it can be used to check + * for a prefix. This function is useful for avoiding string copies + * to temporary buffers and the potential for buffer overruns that + * can occur when using temporary buffers without checking lengths. */ +int cieqn(const char *p, const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n; ++i) { + if (tolower_c(p[i]) != tolower_c(s[i])) { + return FALSE; + } + } + return TRUE; /* all chars matched */ +} /* end of function cineq */ /* Case insensitive prefix. */ - -int -ciprefix(const char *p, const char *s) +int ciprefix(const char *p, const char *s) { for (; *p; p++, s++) - if (tolower_c(*p) != tolower_c(*s)) + if (tolower_c(*p) != tolower_c(*s)) { return FALSE; + } return TRUE; -} +} /* end of function ciprefix */ -void -strtolower(char *str) + +void strtolower(char *str) { - if (!str) + if (!str) { return; + } - for (; *str; str++) + for (; *str; str++) { *str = tolower_c(*str); -} + } +} /* end of function strtolower */ -void -strtoupper(char *str) + +void strtoupper(char *str) { - if (!str) + if (!str) { return; + } - for (; *str; str++) + for (; *str; str++) { *str = toupper_c(*str); -} + } +} /* end of function strtoupper */ #ifdef CIDER @@ -230,18 +334,21 @@ strtoupper(char *str) * first n characters are the same */ -int -cinprefix(char *p, char *s, int n) +int cinprefix(char *p, char *s, int n) { - if (!p || !s) + if (!p || !s) { return 0; + } - for (; *p; p++, s++, n--) - if (tolower_c(*p) != tolower_c(*s)) + for (; *p; p++, s++, n--) { + if (tolower_c(*p) != tolower_c(*s)) { return 0; + } + } return n <= 0; -} +} /* end of function cinprefix */ + /* @@ -410,6 +517,41 @@ gettok_iv(char **s) +/* findtok_noparen() does the string scanning for gettok_noparens() but + * does not allocate a token. Hence it is useful when a copy of the token + * is not required */ +void findtok_noparen(char **p_str, char **p_token, char **p_token_end) +{ + char *str = *p_str; + + str = skip_ws(str); + + if (!*str) { + *p_str = str; + *p_token = (char *) NULL; + return; + } + + *p_token = str; /* Token starts after whitespace */ + { + char c; + while ((c = *str) != '\0' && + !isspace_c(c) && + (c != '(') && + (c != ')') && + (c != ',') + ) { + str++; + } + } + *p_token_end = str; + + str = skip_ws(str); + *p_str = str; +} /* end of function findtok_noparen */ + + + /*-------------------------------------------------------------------------* * gettok_noparens was added by SDB on 4.21.2003. * It acts like gettok, except that it treats parens and commas like @@ -417,33 +559,17 @@ gettok_iv(char **s) * parsing and returns when it finds one of those chars. It is called from * 'translate' (subckt.c). *-------------------------------------------------------------------------*/ - -char * -gettok_noparens(char **s) +char *gettok_noparens(char **s) { - char c; - const char *token, *token_e; - - *s = skip_ws(*s); - - if (!**s) - return NULL; /* return NULL if we come to end of line */ - - token = *s; - while ((c = **s) != '\0' && - !isspace_c(c) && - (**s != '(') && - (**s != ')') && - (**s != ',') - ) { - (*s)++; + char *token, *token_e; + findtok_noparen(s, &token, &token_e); + if (token == (char *) NULL) { + return (char *) NULL; /* return NULL if we come to end of line */ } - token_e = *s; - - *s = skip_ws(*s); return copy_substring(token, token_e); -} +} /* end of function gettok_noparens */ + /*-------------------------------------------------------------------------* * gettok_model acts like gettok_noparens, however when it encounters a '{', @@ -746,8 +872,8 @@ str_has_arith_char(char *s) } -int -get_comma_separated_values(char *values[], char *str) { +int get_comma_separated_values(char *values[], char *str) +{ int count = 0; char *comma_ptr; @@ -769,9 +895,7 @@ get_comma_separated_values(char *values[], char *str) { modulo a trailing model binning extension '\.[0-9]+' then return 2 */ - -int -model_name_match(const char *token, const char *model_name) +int model_name_match(const char *token, const char *model_name) { const char *p; size_t token_len = strlen(token); @@ -799,7 +923,443 @@ model_name_match(const char *token, const char *model_name) return 0; return 2; -} +} /* end of funtion model_name_match */ +/* This function returns 1 if pattern is a substring anywhere in str and + * 0 otherwise. A null pattern is considered a mismatch. + * + * Uses Karp-Rabin substring matching with base=256 and modulus=1009 + */ +int substring_n(size_t n_char_pattern, const char *p_pattern, + size_t n_char_string, const char *p_string) +{ + /* Test for a pattern to match */ + if (n_char_pattern == 0) { + return 0; + } + + /* Test for a string of sufficient length */ + if (n_char_pattern > n_char_string) { + return 0; + } + + /* Factor for rolling hash computation */ + const size_t msb_factor = get_kr_msb_factor(n_char_pattern); + + const size_t h_pattern = kr_hash(n_char_pattern, p_pattern); + size_t h_string = kr_hash(n_char_pattern, p_string); + + /* Compare at beginning. If hashes match, do full compare */ + if (h_pattern == h_string && + memcmp(p_pattern, p_string, n_char_pattern) == 0) { + return 1; /* match at start */ + } + + /* Compare at each possible starting point in the string */ + const char *p_last = p_string + (n_char_string - n_char_pattern - 1); + + return next_substr(n_char_pattern, p_pattern, &p_string, p_last, + msb_factor, h_pattern, &h_string) == (char *) NULL ? + 0 : 1; +} /* end of function substring_n */ + + + +/* This function initializes a scan for substring matches */ +void substring_match_init(size_t n_char_pattern, const char *p_pattern, + size_t n_char_string, const char *p_string, bool f_overlap, + struct substring_match_info *p_scan_state) +{ + /* Save input info into structure. Note that the strings are not + * copied, so they must remain allocated and unaltered while the + * search is in progress. */ + p_scan_state->n_char_pattern = n_char_pattern; + p_scan_state->p_pattern = p_pattern; + p_scan_state->n_char_string = n_char_string; + p_scan_state->p_string = p_string; + + /*** Calculate intermediate data ***/ + + /* Test for a pattern to match */ + if (n_char_pattern == 0) { + p_scan_state->f_done = TRUE; + } + /* Test for a string of sufficient length */ + else if (n_char_pattern > n_char_string) { + p_scan_state->f_done = TRUE; + } + else { + p_scan_state->f_done = FALSE; + + /* Look for overlaps only if possible */ + p_scan_state->f_overlap= f_overlap ? + !can_overlap(n_char_pattern, p_pattern) : FALSE; + p_scan_state->n_char_pattern_1 = n_char_pattern - 1; + p_scan_state->msb_factor = get_kr_msb_factor(n_char_pattern); + p_scan_state->h_pattern = kr_hash(n_char_pattern, p_pattern); + p_scan_state->h_string = kr_hash(n_char_pattern, p_string); + p_scan_state->p_last = + p_string + (n_char_string - n_char_pattern - 1); + } + + return; +} /* end of function substring_match_init */ + + + +/* This function finds the next substring match + * + * Parameter + * p_scan_state: Address of struct substring_match_info initialized by + * substring_match_init() + * + * Return value + * NULL if there is no match or the address of the next match otherwise + */ +char *substring_match_next(struct substring_match_info *p_scan_state) +{ + /* First test if there are no more possible matches */ + if (p_scan_state->f_done) { + return (char *) NULL; + } + + /* Find next match, if any */ + const char * const p_match = next_substr( + p_scan_state->n_char_pattern, p_scan_state->p_pattern, + &p_scan_state->p_string, p_scan_state->p_last, + p_scan_state->msb_factor,p_scan_state->h_pattern, + &p_scan_state->h_string); + + /* Update done status if changed */ + if (p_match == (char *) NULL) { + p_scan_state->f_done = TRUE; + } + else { + if (!p_scan_state->f_overlap) { + p_scan_state->p_string += + p_scan_state->n_char_pattern_1; /* end of match */ + p_scan_state->h_string = p_scan_state->h_pattern; + } + } + + return (char *) p_match; /* Return result */ +} /* end of function substring_match_next */ + + + +#ifdef COMPILE_UNUSED_FUNCTIONS +/* This funtion returns the locations of optionally non-overlapping substring + * matches. For example, in the string aaaaa, aa is found in non-overlapping + * locations at 0-based offsets 0 and 2 ahd with overlapping allowed atr + * offsets 0, 1, 2, and 3 */ +size_t get_substring_matches(size_t n_char_pattern, const char *p_pattern, + size_t n_char_string, const char *p_string, + size_t n_elem_buf, char *p_match_buf, bool f_overlap) +{ + /* Test for a pattern to match */ + if (n_char_pattern == 0) { + return 0; + } + + /* Test for a string of sufficient length */ + if (n_char_pattern > n_char_string) { + return 0; + } + + /* Handle 0-sized buffer */ + if (n_elem_buf == 0) { + return 0; + } + + /* Factor for rolling hash computation */ + const size_t msb_factor = get_kr_msb_factor(n_char_pattern); + + const size_t h_pattern = kr_hash(n_char_pattern, p_pattern); + size_t h_string = kr_hash(n_char_pattern, p_string); + + /* Compare at beginning. If hashes match, do full compare */ + if (h_pattern == h_string && + memcmp(p_pattern, p_string, n_char_pattern) == 0) { + return 1; /* match at start */ + } + + /* Compare at each possible starting point in the string */ + const char *p_last = p_string + (n_char_string - n_char_pattern - 1); + const size_t n_char_pattern_1 = n_char_pattern - 1; + char **pp_match_buf_cur = &p_match_buf; + char * const * const pp_match_buf_end = pp_match_buf_cur + n_elem_buf; + + /* Look for overlaps only if possible */ + f_overlap = f_overlap ? !can_overlap(n_char_pattern, p_pattern) : FALSE; + + for ( ; pp_match_buf_cur < pp_match_buf_end; pp_match_buf_cur++) { + const char *p_match = next_substr(n_char_pattern, p_pattern, + &p_string, p_last, msb_factor, h_pattern, &h_string); + if (p_match == (char *) NULL) { /* if no match, done */ + return (int) (pp_match_buf_cur - &p_match_buf); + } + + /* Save result */ + *pp_match_buf_cur = (char *) p_match; + + /* If overlapping is not allowed, contniue search after the match. + * Note that in this case, the string hash is the pattern hash. */ + if (!f_overlap) { + p_string += n_char_pattern_1; /* end of match */ + h_string = h_pattern; + } + } /* end of loop over string */ + + return n_elem_buf; /* full buffer */ +} /* end of funtion get_substring_matches */ +#endif /* COMPILE_UNUSED_FUNCTIONS */ + + + +/* This function determines if a pattern can allow overlapping matches. + * For example, the pattern "starts" would have overlapped matches in the + * string "startstarts". + * + * Remarks + * While not directly related to this function, there is only a binary yes/no + * interest regarding overlap rather than an offset into the the string where + * such overlap may occur. That is because the hash value is being computed + * incremetally, so the only time when there is substantial computational + * savings in this approach is when the hash value is known, as it would be + * at the end of a match (since the hash of the pattern is knonw.) + */ +static bool can_overlap(size_t n_char_pattern, const char * const p_pattern) +{ + if (n_char_pattern < 2) { /* does not matter */ + return TRUE; + } + + /* Find the last occurrance of the first character */ + const char * const p_end = p_pattern + n_char_pattern; + const char *p_cur = p_end - 1; + const char ch_first = *p_pattern; + for ( ; p_cur > p_pattern; --p_cur) { + if (*p_cur == ch_first) { + break; + } + } /* end of loop finding the first char */ + + /* Test for no duplicate */ + if (p_cur == p_pattern) { /* not found */ + return FALSE; /* no duplicate so cannot overlap */ + } + + /* Now must match from this char onward to overlap */ + const char *p_src = p_pattern; + for ( ; p_cur != p_end; ++p_cur, ++p_src) { + if (*p_cur != *p_src) { /* comparing 'b' to 'd' in "abcad" + * for example */ + return FALSE; /* Mismatch, so not an overlap */ + } + } /* end of loop finding the first char */ + + return TRUE; /* Matched to end of word */ +} /* end of function can_overlap */ + + + +/* Prime number of Karp-Rabin hashing. Tradeoff between number of hash + * collisions and number of times modulus must be taken. */ +#define KR_MODULUS 1009 +/* Compute (256^(n-1))%KR_MODULUS */ +static size_t get_kr_msb_factor(size_t n) +{ + size_t i; + size_t factor = 1; + const size_t n_itr = n - 1; + for (i = 0; i < n_itr; ++i) { + size_t factor_new = (factor << 8); + if (factor_new < factor) { /* overflow */ + factor %= KR_MODULUS; /* take modulus */ + factor <<= 8; /* and recompute */ + } + } /* end of loop building factor */ + + /* Return the factor after final modulus if necessary */ + if (factor >= KR_MODULUS) { + factor %= KR_MODULUS; + } + return factor; +} /* end of function get_kr_msb_factor */ + + + +/* Compute KR hash assuming n >= 1 */ +static size_t kr_hash(size_t n, const char *p) +{ + const char * const p_end = p + n; + size_t hash = *(unsigned char *) p; + for (p++; p < p_end; p++) { + unsigned char ch = *(unsigned char *) p; + size_t hash_new = (hash << 8) + ch; + if (hash_new < hash) { /* overflow */ + hash %= KR_MODULUS; /* take modulus */ + hash = (hash << 8) + ch; /* and recompute */ + } + else { /* no overflow, so no need for modulus yet */ + hash = hash_new; + } + } /* end of loop hasing chars */ + + /* Do final modulus if necessary */ + if (hash >= KR_MODULUS) { + hash %= KR_MODULUS; + } + + return hash; +} /* end of function kr_hash */ + + + +/* This function locates the next substring match. It is intended to be called + * as part of the scanning of a string for a substring + * + * Parameters + * n_char_pattern: Length of pattern to find + * p_pattern: Pattern to find. Need not be null-terminated + * pp_string: Address containing the current location in the string. Updated + * if a match is found. + * p_last: Address of last possible location of a match + * msb_factor: Constant related to hash update + * h_pattern: Computed hash of pattern + * p_h_string: Address containing the current hash value of the location + * in the string being considered. It is updated in the function. + * + * Return value + * NULL if no substring, or the address of the substring if one exists. + */ +static inline const char *next_substr( + size_t n_char_pattern, const char *p_pattern, + const char **pp_string, const char * const p_last, + const size_t msb_factor, const size_t h_pattern, size_t *p_h_string) +{ + const char *p_string = *pp_string; + size_t h_string = *p_h_string; + + for ( ; ; ) { + /* Update hash for next starting point at p_string + 1 */ + if ((h_string = (((h_string - (unsigned char) p_string[0] * + msb_factor) << 8) + p_string[n_char_pattern]) % + KR_MODULUS) > KR_MODULUS) { /* negative value when signed */ + h_string += KR_MODULUS; + } + ++p_string; /* step to next starting point */ + + /* Compare at current starting point. If hashes match, + * do full compare */ + if (h_pattern == h_string && + memcmp(p_pattern, p_string, n_char_pattern) == 0) { + *pp_string = p_string; /* Update string location */ + *p_h_string = h_string; /* and hash for another call */ + return p_string; /* match here */ + } + + /* Exit with no match if at last starting point */ + if (p_string == p_last) { + return (char *) NULL; /* no match found */ + } + } /* end of loop over starting points in string */ +} /* end of function next_substr */ + + + +/* This function returns TRUE if '\0' is among the n characters at p and + * FALSE otherwise. */ +static inline bool have_null(size_t n, const char *p) +{ + /* Scan backwards to make the common case of using a null termination + * of a string for the null char be faster */ + const char *p_cur = p + n - 1; + for ( ; p_cur >= p; --p_cur) { /* Locate '\0' among the chars */ + if (*p_cur == '\0') { /* found */ + return TRUE; + } + } + return FALSE; +} /* end of function have_null */ + + + +/* This function "finds a needle in a haystack" aka the first occurrence of + * any character of needle in haystack. NULL is returned if none is found. + * haystack must be terminated with '\0'. + * + * Remarks + * p_needle does not need to be null terminated. In fact, a null can be + * included among the characters to be located so that this funtion will + * locate the end of haystack if none of the other characters is found and + * would guarantee that the returned value is not NULL. + * + * The case of a '\0' included among the chars to locate is treated as a + * special case for improved efficiency. + * + * For a sufficiently large haystack, further gains in performance can be + * achieved by analyzing the characteristics of the needle values and + * developing comparisons based on bit values or range values. As a + * trivial example, for the needle string "01234567", instead of 8 + * comparisons for the 8 values, 2 comparisons can be used by comparing + * against >= 0 and against <= 7. Without a large enough haystack, the + * computational time required for the analysis would not be recovered. + */ +char *find_first_of(const char *haystack, + unsigned int n_needle, const char *p_needle) +{ + /* Hanldle case of nothing to find */ + if (n_needle == 0) { + return (char *) NULL; + } + + const char * const p_needle_end = p_needle + n_needle; + if (have_null(n_needle, p_needle)) { /* searching for '\0' */ + for ( ; ; ++haystack) { /* iterate over straws in haystack */ + const char straw = *haystack; + const char *p_needle_cur = p_needle; + for ( ; p_needle_cur != p_needle_end; ++p_needle_cur) { + const char needle = *p_needle_cur; + if (straw == needle) { /* found needle */ + return (char *) haystack; + } + } /* end of loop over needles */ + } /* end of loop over straws in haystack */ + } /* end of case that '\0' among items being located */ + + /* Else '\0' is not among the items being located */ + for ( ; ; ++haystack) { /* iterate over straws in haystack */ + const char straw = *haystack; + const char *p_needle_cur = p_needle; + for ( ; p_needle_cur != p_needle_end; ++p_needle_cur) { + const char needle = *p_needle_cur; + if (straw == needle) { /* found needle */ + return (char *) haystack; + } + } /* end of loop over needles */ + if (straw == '\0') { /* entire haystack searched */ + return (char *) NULL; + } + } /* end of loop over straws in haystack */ +} /* end of function find_first_of */ + + + +/* This function returns TRUE if the string has any of the characters + * '"', '\'' or '\\' */ +bool has_escape_or_quote(size_t n, const char *str) +{ + const char *str_end = str + n; + for ( ; str != str_end; ++str) { + const char ch_cur = *str; + if (ch_cur == '"' || ch_cur == '\'' || ch_cur == '\\') { + return TRUE; + } + } /* end of loop over chars in string */ + + return FALSE; +} /* end of function may_have_eq */ + From 8f7f213ee922164093e1bd0576376d053a3097a6 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sun, 8 Dec 2019 23:32:02 -0500 Subject: [PATCH 15/58] Modified regression tests for library processing to make indication of failures more robust --- tests/regression/lib-processing/ex1a.cir | 9 +++++---- tests/regression/lib-processing/ex1b.cir | 9 +++++---- tests/regression/lib-processing/ex2a.cir | 25 ++++++++++++++++++------ tests/regression/lib-processing/ex3a.cir | 25 ++++++++++++++++++------ 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/tests/regression/lib-processing/ex1a.cir b/tests/regression/lib-processing/ex1a.cir index 97ee70480..14375c877 100644 --- a/tests/regression/lib-processing/ex1a.cir +++ b/tests/regression/lib-processing/ex1a.cir @@ -17,12 +17,13 @@ op echo "Note: v(check0) = $&v(check0)" -if abs(v(check0)) > 1e-9 - quit 1 +if abs(v(check0)) <= 1e-9 + echo "INFO: ok" + quit 0 end -echo "INFO: ok" -quit 0 +echo ERROR: Test failure +quit 1 .endc diff --git a/tests/regression/lib-processing/ex1b.cir b/tests/regression/lib-processing/ex1b.cir index f92aa6b25..d37547d8d 100644 --- a/tests/regression/lib-processing/ex1b.cir +++ b/tests/regression/lib-processing/ex1b.cir @@ -21,12 +21,13 @@ op echo "Note: v(check0) = $&v(check0)" -if abs(v(check0)) > 1e-9 - quit 1 +if abs(v(check0)) <= 1e-9 + echo "INFO: ok" + quit 0 end -echo "INFO: ok" -quit 0 +echo ERROR: Test failure +quit 1 .endc diff --git a/tests/regression/lib-processing/ex2a.cir b/tests/regression/lib-processing/ex2a.cir index 32aadb77a..497b50b8f 100644 --- a/tests/regression/lib-processing/ex2a.cir +++ b/tests/regression/lib-processing/ex2a.cir @@ -15,19 +15,32 @@ Vcheck2 9 check2 2.0V .control op +let n_pass = 0 + echo "Note: v(check1) = $&v(check1)" echo "Note: v(check2) = $&v(check2)" -if abs(v(check1)) > 1e-9 - quit 1 +if abs(v(check1)) <= 1e-9 + let n_pass = n_pass + 1 +else + echo "ERROR: Test 1 failed" end -if abs(v(check2)) > 1e-9 - quit 1 + +if abs(v(check2)) <= 1e-9 + let n_pass = n_pass + 1 +else + echo "ERROR: Test 2 failed" end -echo "INFO: ok" -quit 0 + +if n_pass = 2 + echo "INFO: ok" + quit 0 +end + +echo ERROR: $&n_pass of 2 tests passed +quit 1 .endc diff --git a/tests/regression/lib-processing/ex3a.cir b/tests/regression/lib-processing/ex3a.cir index 844ea2ea8..16578da72 100644 --- a/tests/regression/lib-processing/ex3a.cir +++ b/tests/regression/lib-processing/ex3a.cir @@ -15,19 +15,32 @@ Vcheck2 9 check2 2.0V .control op +let n_pass = 0 + echo "Note: v(check1) = $&v(check1)" echo "Note: v(check2) = $&v(check2)" -if abs(v(check1)) > 1e-9 - quit 1 +if abs(v(check1)) <= 1e-9 + let n_pass = n_pass + 1 +else + echo "ERROR: Test 1 failed" end -if abs(v(check2)) > 1e-9 - quit 1 + +if abs(v(check2)) <= 1e-9 + let n_pass = n_pass + 1 +else + echo "ERROR: Test 2 failed" end -echo "INFO: ok" -quit 0 + +if n_pass = 2 + echo "INFO: ok" + quit 0 +end + +echo ERROR: $&n_pass of 2 tests passed +quit 1 .endc From 814d1f07d680517f3f33ac6df631ead5de998f54 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sun, 8 Dec 2019 23:42:49 -0500 Subject: [PATCH 16/58] Fixed locating vectors like V(node) --- src/frontend/vectors.c | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 3d26bc95b..60168fea2 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -441,36 +441,36 @@ struct dvec *vec_fromplot(char *word, struct plot *plot) { return d; } - /* Consider forms I(node) and i(node) -> node#branch */ - if (tolower(word[0]) != (int) 'i') { /* Not i of I.* */ - return (struct dvec *) NULL; - } + /* Forms I(node) and i(node) are converted to node#branch; + * forms x(node), x != i, x != I, and x != '(' are converted to node */ + if (word[0] != '\0' && word[0] != '(') { /* 1 or more char, not '(' */ + if (word[1] == '(') { /* x(, x != '(' */ + const char * const p_last_close_paren = strrchr(word + 2, ')'); + if (p_last_close_paren != (char *) NULL && + p_last_close_paren - word > (ptrdiff_t) 3 && + p_last_close_paren[1] == '\0') { + /* Of form x(node). Create node string. */ + DS_CREATE(ds, 100); + const char * const node_start = word + 2; + bool ds_ok = ds_cat_mem(&ds, node_start, + p_last_close_paren - node_start) == DS_E_OK; + /* If i(node) or I(node), append #branch */ + if (tolower(word[0]) == (int) 'i') { + /* i(node) or I(node) */ + ds_ok &= ds_cat_mem(&ds, "#branch", 7) == DS_E_OK; + } + if (!ds_ok) { /* Dstring error (allocation failure) */ + (void) fprintf(cp_err, "Unable to build vector name.\n"); + } + else { /* name built OK */ + d = findvec(ds_get_buf(&ds), plot); + } /* end of case of vector name built OK */ + ds_free(&ds); + } /* end of case of x(node) */ + } /* end of case of x( */ + } /* end of case of non-empty string and not leading '(' */ - if (word[1] != '(') { /* Not i or I(.* */ - return (struct dvec *) NULL; - } - - { - const char * const p_node = word + 2; - const char *p_end = p_node + strlen(p_node); - if (*--p_end != ')') { /* Not i or I then '(' with a closing ')' */ - return (struct dvec *) NULL; - } - - /* Form is correct, so see if node#branch exists */ - DS_CREATE(ds, 100); - if (ds_cat_mem(&ds, p_node, p_end - p_node) != DS_E_OK) { - controlled_exit(-1); - } - if (ds_cat_str(&ds, "#branch") != DS_E_OK) { - controlled_exit(-1); - } - - d = findvec(ds_get_buf(&ds), plot); - ds_free(&ds); - - return d; - } + return d; } /* end of function vec_fromplot */ From bb10b84045c4191fefa5a0bfcaddbb41ce70e5bf Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 00:27:03 -0500 Subject: [PATCH 17/58] Fixed buffer resizing, made string utilities more modular, and added several new utilities, some which do not require a null termination, potentially avoiding the need to copy a string. Also some substring utilities using the Rabin-Karp algorithm were added. --- src/include/ngspice/stringutil.h | 4 +--- src/misc/string.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/include/ngspice/stringutil.h b/src/include/ngspice/stringutil.h index e990e4efa..f02c2a6b1 100644 --- a/src/include/ngspice/stringutil.h +++ b/src/include/ngspice/stringutil.h @@ -98,8 +98,6 @@ inline char *copy_substring(const char *str, const char *end) -/* Like scannum but *p_str is advanced past the number */ - /* Try to identify an unsigned integer that begins a string. Stop when a * non- numeric character is reached. There is no way to distinguish * between a value of 0 and a string that does not contain a numeric @@ -114,7 +112,7 @@ inline int scannum(const char *str) /* Determine whether sub is a substring of str. */ inline int substring(const char *sub, const char *str) { - return strstr(sub, str) != (char *) NULL; + return strstr(str, sub) != (char *) NULL; } /* end of function substring */ #ifdef CIDER diff --git a/src/misc/string.c b/src/misc/string.c index 8dfdfd734..7aaf87c03 100644 --- a/src/misc/string.c +++ b/src/misc/string.c @@ -236,7 +236,7 @@ int get_int_n(const char *str, size_t n, int *p_value) /* Test for overflow. * If negative, can be 1 greater (-2**n vs 2**n -1) */ - if (value - f_neg > INT_MAX) { + if (value - f_neg > (unsigned int) INT_MAX) { return -2; } From 5a7257520d032acd2460850a4dd845b7cc8dfdcd Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 16:18:31 -0500 Subject: [PATCH 18/58] Made cp_enqvar() mode modular by adding a separate function to handle the case of a vector. Lists are more efficiently handled by moving common comparisons out of the loop over elements. The first argument of cp_enqvar() is changed from char * to const char * since the name of the variable being found is not altered by the function. --- src/frontend/options.c | 195 ++++++++++++++++++++++----------- src/include/ngspice/cpextern.h | 2 +- 2 files changed, 133 insertions(+), 64 deletions(-) diff --git a/src/frontend/options.c b/src/frontend/options.c index a4f423b27..c417a853a 100644 --- a/src/frontend/options.c +++ b/src/frontend/options.c @@ -21,14 +21,14 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "control.h" #include "spiceif.h" - -static void setdb(char *str); - bool ft_acctprint = FALSE, ft_noacctprint = FALSE, ft_listprint = FALSE; bool ft_nodesprint = FALSE, ft_optsprint = FALSE, ft_noinitprint = FALSE; bool ft_norefprint = FALSE; bool ft_ngdebug = FALSE, ft_stricterror = FALSE; +static void setdb(char *str); +static struct variable *cp_enqvec_as_var(const char *vec_name, + int *p_f_found); /* The user-supplied routine to query the address of a variable, if its * name is given. This recognises the $&varname notation, and also @@ -36,87 +36,156 @@ bool ft_ngdebug = FALSE, ft_stricterror = FALSE; * tbfreed is set to 1, if the variable is malloced here and may safely * be freed, and is set to 0 if plot and circuit environment variables * are returned. + * + * Note that if tbfreed is set to 1 that any changes will have no effect + * on the original variable. The variables that are copied are as follows: + * "curplotname", "curplottitle", "curplotdate", "curplot", and + * "plots". + * + * The $&v notation returns the values of a real vector v or the real part + * of a complex vector v. If there is only a single element, it is returned + * as a CP_REAL variable; otherwise a list is returned. In either case, + * tbfreed is set to 1 for this case. */ - -struct variable * -cp_enqvar(char *word, int *tbfreed) +struct variable *cp_enqvar(const char *word, int *tbfreed) { - struct dvec *d; - struct variable *vv; - - if (*word == '&') { - - word++; - *tbfreed = 1; - - d = vec_get(word); - if (!d) - return (NULL); - - if (d->v_link2) - fprintf(cp_err, - "Warning: only one vector may be accessed with the $& notation.\n"); - - if (d->v_length == 1) { - double value = isreal(d) - ? d->v_realdata[0] - : realpart(d->v_compdata[0]); - return var_alloc_real(copy(word), value, NULL); - } else { - struct variable *list = NULL; - int i; - for (i = d->v_length; --i >= 0;) { - double value = isreal(d) - ? d->v_realdata[i] - : realpart(d->v_compdata[i]); - list = var_alloc_real(NULL, value, list); - } - return var_alloc_vlist(copy(word), list, NULL); - } + if (*word == '&') { /* The variable is a vector */ + return cp_enqvec_as_var(word + 1, tbfreed); } - if (plot_cur) { - *tbfreed = 0; - for (vv = plot_cur->pl_env; vv; vv = vv->va_next) - if (eq(vv->va_name, word)) - return (vv); + if (plot_cur) { /* a current plot is defined */ + struct variable *vv; + for (vv = plot_cur->pl_env; vv; vv = vv->va_next) { + if (eq(vv->va_name, word)) { + *tbfreed = 0; + return vv; + } + } /* end of loop over variables of the current plot */ + *tbfreed = 1; - if (eq(word, "curplotname")) - return var_alloc_string(copy(word), copy(plot_cur->pl_name), NULL); - if (eq(word, "curplottitle")) - return var_alloc_string(copy(word), copy(plot_cur->pl_title), NULL); - if (eq(word, "curplotdate")) - return var_alloc_string(copy(word), copy(plot_cur->pl_date), NULL); - if (eq(word, "curplot")) - return var_alloc_string(copy(word), copy(plot_cur->pl_typename), NULL); - if (eq(word, "plots")) { + /* Look for the variables beginning with curplot: + * curplot, curplotname, curplottitle, and curplotdate */ + if (strncmp(word, "curplot", 7) == 0) { /* begins with curplot */ + const char * const rest = word + 7; + if (*rest == '\0') { /* curplot */ + return var_alloc_string(copy(word), + copy(plot_cur->pl_typename), NULL); + } + else if (eq(rest, "name")) { /* curplotname */ + return var_alloc_string(copy(word), + copy(plot_cur->pl_name), NULL); + } + else if (eq(rest, "title")) { /* curplottitle */ + return var_alloc_string(copy(word), + copy(plot_cur->pl_title), NULL); + } + else if (eq(rest, "date")) { /* curplotname */ + return var_alloc_string(copy(word), + copy(plot_cur->pl_date), NULL); + } + } + + if (eq(word, "plots")) { /* list of defined plots */ struct variable *list = NULL; struct plot *pl; for (pl = plot_list; pl; pl = pl->pl_next) - list = var_alloc_string(NULL, copy(pl->pl_typename), list); + list = var_alloc_string(NULL, + copy(pl->pl_typename), list); return var_alloc_vlist(copy(word), list, NULL); } + } /* end of case that a current plot is defined */ + + *tbfreed = 0; + if (ft_curckt) { /* a current circuit is defined */ + struct variable *vv; + for (vv = ft_curckt->ci_vars; vv; vv = vv->va_next) { + if (eq(vv->va_name, word)) { + return vv; + } + } } - *tbfreed = 0; - if (ft_curckt) - for (vv = ft_curckt->ci_vars; vv; vv = vv->va_next) - if (eq(vv->va_name, word)) - return (vv); - - return (NULL); -} + return (struct variable *) NULL; +} /* end of function cp_enqvar */ -/* Return $plots, $curplot, $curplottitle, $curplotname, $curplotdate */ +/* This functon returns the contents of a vector as a variable. + * If the vector has more than one element, it is returned as a list. + * The "shape" of the vector (number of dimensions and number of + * elements per dimension) has no effect on the returned list. + * + * Paramters + * vec_name: Name of vector + * p_f_found: Address to receive 1 if the vector was found and + * the corresponding variable must be freed and 0 if the vector + * was not found. + * + * Return values + * The address of the created list variable or NULL if none + * was found. + * + * Remarks + * The name of the created variable is the same as that of the vector. + */ +static struct variable *cp_enqvec_as_var(const char *vec_name, + int *p_f_found) +{ + const struct dvec * const d = vec_get(vec_name); /* locate vector */ + if (!d) { /* not found */ + *p_f_found = 0; + return (struct variable *) NULL; + } + + /* Variables from vectors are always copies since variable + * structures must be created. */ + *p_f_found = 1; + + if (d->v_link2) { + /* The vector has other vectors linked to it via the v_link2 + * pointer. That is OK, but a warning is printed that other + * vectors will not be returned */ + fprintf(cp_err, + "Warning: only one vector may be accessed with the $& notation.\n"); + } + + if (d->v_length == 1) { /* 1 element, so return as a CP_REAL */ + double value = isreal(d) + ? d->v_realdata[0] + : realpart(d->v_compdata[0]); + return var_alloc_real(copy(vec_name), value, NULL); + } + else { /* >1 element, so return as a list of all CP_REALs */ + struct variable *list = NULL; + if (isreal(d)) { + int i; + double *realdata = d->v_realdata; + for (i = d->v_length; --i >= 0;) { + list = var_alloc_real(NULL, realdata[i], list); + } + } + else { + int i; + ngcomplex_t *compdata = d->v_compdata; + for (i = d->v_length; --i >= 0;) { + list = var_alloc_real(NULL, realpart(compdata[i]), list); + } + } + return var_alloc_vlist(copy(vec_name), list, NULL); + } +} /* end of function cp_enqvec_as_var */ + + + +/* Return $plots, $curplot, $curplottitle, $curplotname, and + * $curplotdate as a linked list of variables in that order */ struct variable * cp_usrvars(void) { struct variable *v, *tv; int tbfreed; - v = NULL; + v = (struct variable *) NULL; if ((tv = cp_enqvar("plots", &tbfreed)) != NULL) { tv->va_next = v; diff --git a/src/include/ngspice/cpextern.h b/src/include/ngspice/cpextern.h index 2268da9fc..52b88744a 100644 --- a/src/include/ngspice/cpextern.h +++ b/src/include/ngspice/cpextern.h @@ -181,7 +181,7 @@ extern void cp_periodic(void); extern void ft_cpinit(void); extern struct comm *cp_coms; extern char *cp_program; -extern struct variable *cp_enqvar(char *word, int *tbfreed); +extern struct variable *cp_enqvar(const char *word, int *tbfreed); extern struct variable *cp_usrvars(void); int cp_usrset(struct variable *var, bool isset); extern void fatal(void); From 550d26b57a7ba38f3194255ee1b81dbc6d7a2d04 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 18:26:52 -0500 Subject: [PATCH 19/58] Used the new capabilities of ft_numparse() to allow the creation of variables and list items of type CP_NUM --- src/frontend/variable.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 270303605..7c1772cd9 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -484,11 +484,15 @@ struct variable *cp_setparse(wordlist *wl) vv = var_alloc_string(NULL, copy(ss), NULL); } else { - double dbl_val; - if (ft_numparse(&ss, FALSE, &dbl_val) >= 0) { + double dbl_val; + switch (ft_numparse(&ss, FALSE, &dbl_val)) { + case 0: /* CP_REAL */ vv = var_alloc_real(NULL, dbl_val, NULL); - } - else { + break; + case +1: /* CP_NUM */ + vv = var_alloc_num(NULL, (int) dbl_val, NULL); + break; + default: /* CP_STRING */ vv = var_alloc_string(NULL, copy(ss), NULL); } } @@ -523,13 +527,18 @@ struct variable *cp_setparse(wordlist *wl) } else { double dbl_val; - if (ft_numparse(&ss, FALSE, &dbl_val) >= 0) { - /*** We should try to get CP_NUM's... */ + switch (ft_numparse(&ss, FALSE, &dbl_val)) { + case 0: /* CP_REAL */ vars = var_alloc_real(name, dbl_val, vars); - } - else { + break; + case +1: /* CP_NUM */ + vars = var_alloc_num(name, (int) dbl_val, vars); + break; + default: /* CP_STRING */ vars = var_alloc_string(name, copy(val), vars); } + + } name = (char *) NULL; /* name given to variable via var_alloc_* */ tfree(copyval); /*DG: must free ss any way to avoid cp_unquote memory leak */ From 2ab6f03de16d8951ec5f8739cfc7cc807f8125fb Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Jan 2020 11:29:45 +0100 Subject: [PATCH 20/58] Fixed some comments that appear to have been missed when compile_min.sh was used to create compile_linux.sh --- compile_linux.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compile_linux.sh b/compile_linux.sh index 04e8533ae..b9ded540e 100644 --- a/compile_linux.sh +++ b/compile_linux.sh @@ -69,8 +69,7 @@ echo "compiling (see make.log)" make 2>&1 -j8 | tee make.log exitcode=${PIPESTATUS[0]} if [ $exitcode -ne 0 ]; then echo "make failed"; exit 1 ; fi -# 32 bit: Install to C:\Spice -# 64 bit: Install to C:\Spice64 +# Install to /usr/local echo "installing (see make_install.log)" make install 2>&1 | tee make_install.log exitcode=${PIPESTATUS[0]} From eddd2827ec41085e4b778df538a5852f200e3c6c Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 19:09:39 -0500 Subject: [PATCH 21/58] Made checking for single-character words faster. The testing now also uses less memory and works for 8-bit characters, which would have previously caused buffer overruns. It is also more modular and has additional documentation. --- src/frontend/init.c | 11 ------ src/frontend/parser/lexical.c | 73 ++++++++++++++++++++++++++++------- src/include/ngspice/cpdefs.h | 5 --- 3 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/frontend/init.c b/src/frontend/init.c index fddb48679..5e963e585 100644 --- a/src/frontend/init.c +++ b/src/frontend/init.c @@ -12,10 +12,6 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "variable.h" -char cp_chars[128]; /* used in fcn cp_lexer() from lexical.c */ - -static char *singlec = "<>;&"; - void cp_init(void) /* called from ft_cpinit() in cpitf.c. @@ -25,13 +21,6 @@ cp_init(void) cp_curin, cp_curout, cp_curerr (defined in streams.c) */ { - char *s; - - memset(cp_chars, 0, 128); - for (s = singlec; *s; s++) - /* break word to right or left of characters <>;&*/ - cp_chars[(int) *s] = (CPC_BRR | CPC_BRL); - cp_vset("history", CP_NUM, &cp_maxhistlength); cp_curin = stdin; diff --git a/src/frontend/parser/lexical.c b/src/frontend/parser/lexical.c index 32fae3767..e5ba348c6 100644 --- a/src/frontend/parser/lexical.c +++ b/src/frontend/parser/lexical.c @@ -7,6 +7,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group * Initial lexer. */ +#include "ngspice/defines.h" #include "ngspice/ngspice.h" #include "ngspice/cpdefs.h" @@ -44,6 +45,23 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include "ngspice/fteinput.h" #include "lexical.h" +/** Constants related to characters that form their own words. + ** These expressions will be resolved at compile time */ +#define ID_SOLO_CHAR 1 /* Identifier for special chars */ + +/* Largest of the special chars */ +#define MAX_SOLO_CHAR1 ('<' > '>' ? '<' : '>') +#define MAX_SOLO_CHAR2 (MAX_SOLO_CHAR1 > ';' ? MAX_SOLO_CHAR1 : ';') +#define MAX_SOLO_CHAR (MAX_SOLO_CHAR2 > '&' ? MAX_SOLO_CHAR2 : '&') + +/* Smallest of the special chars */ +#define MIN_SOLO_CHAR1 ('<' < '>' ? '<' : '>') +#define MIN_SOLO_CHAR2 (MIN_SOLO_CHAR1 < ';' ? MIN_SOLO_CHAR1 : ';') +#define MIN_SOLO_CHAR (MIN_SOLO_CHAR2 < '&' ? MIN_SOLO_CHAR2 : '&') + +/* Largest index of solo char array */ +#define MAX_INDEX_SOLO_CHAR (MAX_SOLO_CHAR - MIN_SOLO_CHAR) + static void prompt(void); extern bool cp_echo; /* For CDHW patches: defined in variable.c */ @@ -370,19 +388,48 @@ nloop: goto ldefault; default: - /* We have to remember the special case $< - * here - */ - ldefault: - if ((cp_chars[c] & CPC_BRL) && (buf.i > 0)) - if ((c != '<') || (buf.s[buf.i - 1] != '$')) - newword; - push(&buf, c); - if (cp_chars[c] & CPC_BRR) - if ((c != '<') || (buf.i < 2) || (buf.s[buf.i - 2] != '$')) - newword; - } - } + /* $< is a special case where the '<' is not treated + * as a character forming its own word */ + ldefault: { + /* Lookup table for "solo" chars forming their own word */ + static const char id_solo_chars[MAX_INDEX_SOLO_CHAR + 1] = { + ['<' - MIN_SOLO_CHAR] = ID_SOLO_CHAR, + ['>' - MIN_SOLO_CHAR] = ID_SOLO_CHAR, + [';' - MIN_SOLO_CHAR] = ID_SOLO_CHAR, + ['&' - MIN_SOLO_CHAR] = ID_SOLO_CHAR + }; + + /* Find index into solo chars table */ + const unsigned int index_char = + (unsigned int) c - (unsigned int) MIN_SOLO_CHAR; + + /* Flag that the current character c is a solo character */ + const bool f_solo_char = index_char <= MAX_INDEX_SOLO_CHAR && + id_solo_chars[index_char]; + bool f_is_dollar_lt = FALSE; + + if (f_solo_char && buf.i > 0) { + /* The current char is a character forming its own word, + * unless it is "$<" */ + if (c == '<' && buf.s[buf.i - 1] == '$') { /* is "$<" */ + f_is_dollar_lt = TRUE; /* set flag that "$<" found */ + } + else { + /* not "$<", so terminate current word and start + * another one */ + newword; + } + } + + push(&buf, c); /* Add the current char to the current word */ + + if (f_solo_char && !f_is_dollar_lt) { + /* Split into a new word if this char forms its own word */ + newword; + } + } /* end of ldefault block */ + } /* end of switch over character value */ + } /* end of loop over characters */ done: if (wlist->wl_word) diff --git a/src/include/ngspice/cpdefs.h b/src/include/ngspice/cpdefs.h index 00ba6908e..8fc78dfd8 100644 --- a/src/include/ngspice/cpdefs.h +++ b/src/include/ngspice/cpdefs.h @@ -84,11 +84,6 @@ struct alias { struct alias *al_prev; } ; -/* The current record of what characters are special. */ - -#define CPC_BRR 004 /* Break word to right of character. */ -#define CPC_BRL 010 /* Break word to left of character. */ - #define CT_ALIASES 1 #define CT_LABEL 15 From be8bd7b40eb3d6a3412b2b9af733767fabe05a4c Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 23:01:28 -0500 Subject: [PATCH 22/58] Used Win32 function PathIsRelativeA() to identify an absolute path instead of checking the string since the system function may handle more cases (network shares, etc.). Also reviewed formatting that was done on inpcom.c by a code beautifier in an earlier commit. Nearly all of it was an improvement, but a few cases were reverted back to closer to the original form. One particular instance was comments where a list of steps was given one per line. --- src/frontend/inpcom.c | 428 +++++++++++++++++++++++------------------- 1 file changed, 231 insertions(+), 197 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 88d19fffd..74ab6bceb 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -9,6 +9,13 @@ Author: 1985 Wayne A. Christopher Central function is inp_readall() */ +/* Note: Must include shlwapi.h before ngspice header defining BOOL due + * to conflict */ +#ifdef _WIN32 +#include /* for definition of PathIsRelativeA() */ +#pragma comment(lib, "Shlwapi.lib") +#endif + #include "ngspice/ngspice.h" #include "ngspice/compatmode.h" @@ -47,8 +54,8 @@ Author: 1985 Wayne A. Christopher /*#define TRACE*/ /* globals -- wanted to avoid complicating inp_readall interface */ -#define N_LIBRARIES 1000 -#define N_PARAMS 1000 +#define N_LIBRARIES 1000 +#define N_PARAMS 1000 #define N_SUBCKT_W_PARAMS 4000 #define NPARAMS 10000 @@ -69,7 +76,8 @@ struct names { int num_names; }; -struct function_env { +struct function_env +{ struct function_env *up; struct function { @@ -79,10 +87,11 @@ struct function_env { char *params[N_PARAMS]; int num_parameters; const char *accept; - } * functions; + } *functions; }; -struct func_temper { +struct func_temper +{ char *funcname; int subckt_depth; int subckt_count; @@ -307,7 +316,7 @@ static struct library *read_a_lib(char *y, char *dir_name) return NULL; } -#if defined(__MINGW32__) || defined(_MSC_VER) +#if defined(_WIN32) yy = _fullpath(NULL, y_resolved, 0); #else yy = realpath(y_resolved, NULL); @@ -342,8 +351,8 @@ static struct library *read_a_lib(char *y, char *dir_name) fclose(newfp); } - tfree(yy); - tfree(y_resolved); + txfree(yy); + txfree(y_resolved); return lib; } @@ -556,13 +565,21 @@ static COMPATMODE_T ngspice_compat_mode(void) store contents in string new_title process .lib lines read file and library name, open file using fcn inp_pathopen() - read file contents and put into struct libraries[].deck, one entry per .lib - line process .inc lines read file and library name, open file using fcn - inp_pathopen() read file contents and add lines to cc make line entry lower - case allow for shell end of line continuation (\\) add '+' to beginning of - next line add line entry to list cc add '.global gnd' add libraries find - library section add lines add .end card strip end-of-line comments make - continuation lines a single line + read file contents and put into struct libraries[].deck, one entry per .lib line + process .inc lines + read file and library name, open file using fcn inp_pathopen() + read file contents and add lines to cc + make line entry lower case + allow for shell end of line continuation (\\) + add '+' to beginning of next line + add line entry to list cc + add '.global gnd' + add libraries + find library section + add lines + add .end card + strip end-of-line comments + make continuation lines a single line *** end of processing for command files *** start preparation of input deck for numparam ... @@ -716,16 +733,16 @@ struct card *inp_readall(FILE *fp, char *dir_name, bool comfile, bool intfile, for (t = cc->nextcard; t; t = t->nextcard) { if (*(t->line) == '*') continue; - fprintf(fd, "%6d %6d %s\n", t->linenum_orig, t->linenum, - t->line); + fprintf(fd, "%6d %6d %s\n", + t->linenum_orig, t->linenum, t->line); } fprintf(fd, "\n****************** complete deck " "***************\n\n"); /* now completely */ for (t = cc; t; t = t->nextcard) - fprintf(fd, "%6d %6d %s\n", t->linenum_orig, t->linenum, - t->line); + fprintf(fd, "%6d %6d %s\n", + t->linenum_orig, t->linenum, t->line); fclose(fd); fprintf(stdout, @@ -763,8 +780,8 @@ struct inp_read_t inp_read( int line_count = 0; #endif char *new_title = NULL; - int line_number = - 1; /* sjb - renamed to avoid confusion with struct card */ + int line_number = 1; + /* sjb - renamed to avoid confusion with struct card */ int line_number_orig = 1; int cirlinecount = 0; /* length of circarray */ static int is_control = 0; /* We are reading from a .control section */ @@ -828,8 +845,9 @@ struct inp_read_t inp_read( #else buffer = readline(fp); - if (!buffer) + if (!buffer) { break; + } #endif } @@ -839,8 +857,9 @@ struct inp_read_t inp_read( printf("in inp_read, just read %s", buffer); #endif - if (!buffer) + if (!buffer) { continue; + } /* OK -- now we have loaded the next line into 'buffer'. Process it. */ @@ -983,10 +1002,9 @@ struct inp_read_t inp_read( end->linenum = line_number++; end->linenum_orig = line_number_inc++; } - end->linenum = - line_number++; /* SJB - renumber the last line */ - end->linenum_orig = - line_number_inc++; /* SJB - renumber the last line */ + end->linenum = line_number++; /* SJB - renumber last line */ + end->linenum_orig = line_number_inc++; + /* SJB - renumber the last line */ } /* Fix the buffer up a bit. */ @@ -1122,10 +1140,10 @@ struct inp_read_t inp_read( } *s = '\0'; /* Zap the newline. */ - if ((s - 1) >= buffer && - *(s - 1) == - '\r') /* Zop the carriage return under windows */ - *(s - 1) = '\0'; + if ((s - 1) >= buffer && s[- 1] == '\r') { + /* Zap the carriage return under windows */ + s[- 1] = '\0'; + } } /* find the true .end command out of .endc, .ends, .endl, .end @@ -1214,17 +1232,18 @@ struct inp_read_t inp_read( } -static bool is_absolute_pathname(const char *p) + +/* Returns true if path is an absolute path and false if it is a + * relative path. No check is done for the existance of the path. */ +inline static bool is_absolute_pathname(const char *path) { -#if defined(__MINGW32__) || defined(_MSC_VER) - /* /... or \... or D:\... or D:/... */ - return p[0] == DIR_TERM || p[0] == DIR_TERM_LINUX || - (isalpha_c(p[0]) && p[1] == ':' && - (p[2] == DIR_TERM_LINUX || p[2] == DIR_TERM)); +#ifdef _WIN32 + return !PathIsRelativeA(path); #else - return p[0] == DIR_TERM; + return path[0] == DIR_TERM; #endif -} +} /* end of funciton is_absolute_pathname */ + #if 0 @@ -1232,7 +1251,7 @@ static bool is_absolute_pathname(const char *p) static bool is_plain_filename(const char *p) { -#if defined(__MINGW32__) || defined(_MSC_VER) +#if defined(_WIN32) return !strchr(p, DIR_TERM) && !strchr(p, DIR_TERM_LINUX); @@ -1270,7 +1289,7 @@ static char *inp_pathresolve(const char *name) struct variable *v; struct stat st; -#if defined(__MINGW32__) || defined(_MSC_VER) +#if defined(_WIN32) /* If variable 'mingwpath' is set: convert mingw /d/... to d:/... */ if (cp_getvar("mingwpath", CP_BOOL, NULL, 0) && @@ -1299,8 +1318,8 @@ static char *inp_pathresolve(const char *name) switch (v->va_type) { case CP_STRING: cp_wstrip(v->va_string); - (void) sprintf( - buf, "%s%s%s", v->va_string, DIR_PATHSEP, name); + (void) sprintf(buf, "%s%s%s", + v->va_string, DIR_PATHSEP, name); break; case CP_NUM: (void) sprintf(buf, "%d%s%s", v->va_num, DIR_PATHSEP, name); @@ -1729,9 +1748,11 @@ static bool chk_for_line_continuation(char *line) // // change .macro --> .subckt // .eom --> .ends -// .subckt name 1 2 3 params: w=9u l=180n --> .subckt name 1 2 3 w=9u -// l=180n .subckt name (1 2 3) --> .subckt name 1 2 3 x1 (1 2 3) --> x1 -// 1 2 3 .param func1(x,y) = {x*y} --> .func func1(x,y) {x*y} +// .subckt name 1 2 3 params: w=9u l=180n --> +// .subckt name 1 2 3 w=9u l=180n +// .subckt name (1 2 3) --> .subckt name 1 2 3 +// x1 (1 2 3) --> x1 1 2 3 +// .param func1(x,y) = {x*y} --> .func func1(x,y) {x*y} static void inp_fix_macro_param_func_paren_io(struct card *card) { @@ -1758,8 +1779,8 @@ static void inp_fix_macro_param_func_paren_io(struct card *card) if (ciprefix(".subckt", card->line) || ciprefix("x", card->line)) { /* remove () */ - str_ptr = skip_non_ws( - card->line); // skip over .subckt, instance name + str_ptr = skip_non_ws(card->line); + // skip over .subckt, instance name str_ptr = skip_ws(str_ptr); if (ciprefix(".subckt", card->line)) { str_ptr = skip_non_ws(str_ptr); // skip over subckt name @@ -1774,9 +1795,9 @@ static void inp_fix_macro_param_func_paren_io(struct card *card) } str_ptr++; } - card->line = inp_remove_ws( - card->line); /* remove the extra white spaces just - introduced */ + + /* Remove the extra white spaces just introduced */ + card->line = inp_remove_ws(card->line); } } @@ -1915,66 +1936,69 @@ static int is_a_modelname(const char *s) /* not beeing a valid number */ testval = strtod(s, &st); /* conversion failed, so no number */ - if (eq(s, st)) + if (eq(s, st)) { return TRUE; + } + /* test if we have a true number */ - else if (*st == '\0' || isspace(*st)) + if (*st == '\0' || isspace(*st)) { return FALSE; - else { - /* look for the scale factor (alphabetic) and skip it. - * INPevaluate will not do it because is does not swallow - * the scale factor from the string. - */ - switch (*st) { - case 't': - case 'T': - case 'g': - case 'G': - case 'k': - case 'K': - case 'u': - case 'U': - case 'n': - case 'N': - case 'p': - case 'P': - case 'f': - case 'F': - st = st + 1; - break; - case 'm': - case 'M': - if (((st[1] == 'E') || (st[1] == 'e')) && - ((st[2] == 'G') || (st[2] == 'g'))) { - st = st + 3; /* Meg */ - } - else if (((st[1] == 'I') || (st[1] == 'i')) && - ((st[2] == 'L') || (st[2] == 'l'))) { - st = st + 3; /* Mil */ - } - else { - st = st + 1; /* m, milli */ - } - break; - default: - break; - } - /* test if we have a true scale factor */ - if (*st == '\0' || isspace(*st)) - return FALSE; + } - /* test if people use Ohms, F, H for RLC, like pF or uOhms */ - if (ciprefix("ohms", st)) - st = st + 4; - else if (ciprefix("farad", st)) - st = st + 5; - else if (ciprefix("henry", st)) - st = st + 5; - else if ((*st == 'f') || (*st == 'h')) + /* look for the scale factor (alphabetic) and skip it. + * INPevaluate will not do it because is does not swallow + * the scale factor from the string. + */ + switch (*st) { + case 't': + case 'T': + case 'g': + case 'G': + case 'k': + case 'K': + case 'u': + case 'U': + case 'n': + case 'N': + case 'p': + case 'P': + case 'f': + case 'F': st = st + 1; + break; + case 'm': + case 'M': + if (((st[1] == 'E') || (st[1] == 'e')) && + ((st[2] == 'G') || (st[2] == 'g'))) { + st = st + 3; /* Meg */ + } + else if (((st[1] == 'I') || (st[1] == 'i')) && + ((st[2] == 'L') || (st[2] == 'l'))) { + st = st + 3; /* Mil */ + } + else { + st = st + 1; /* m, milli */ + } + break; + default: + break; + } + /* test if we have a true scale factor */ + if (*st == '\0' || isspace(*st)) + return FALSE; - if (*st == '\0' || isspace(*st)) - return FALSE; + /* test if people use Ohms, F, H for RLC, like pF or uOhms */ + if (ciprefix("ohms", st)) + st = st + 4; + else if (ciprefix("farad", st)) + st = st + 5; + else if (ciprefix("henry", st)) + st = st + 5; + else if ((*st == 'f') || (*st == 'h')) + st = st + 1; + + if (*st == '\0' || isspace(*st)) { + return FALSE; } /* token starts with non alphanum character */ @@ -2213,7 +2237,7 @@ static void comment_out_unused_subckt_models(struct card *start_card) } if (remove_subckt) - *line = '*'; + *line = '*'; /* make line a comment */ #if 0 else if (has_models && (ciprefix(".model", line) || ciprefix(".cmodel", line))) { @@ -2306,7 +2330,7 @@ static char *inp_spawn_brace(char *s) /*-------------------------------------------------------------------------* removes " " quotes, returns lower case letters, - replaces non-printable characterss with '_', however if + replaces non-printable characters with '_', however if non-printable character is the only character in a line, replace it by '*' *-------------------------------------------------------------------------*/ @@ -2378,8 +2402,7 @@ static void inp_stripcomments_deck(struct card *c, bool cf) * Comments on a continuation line (i.e. line begining with '+') are allowed * and are removed before lines are stitched. * Lines that contain only an end-of-line comment with or without leading - white - * space are also allowed. + * white space are also allowed. If there is only white space before the end-of-line comment the the whole line is converted to a normal comment line (i.e. one that @@ -4759,11 +4782,11 @@ static void inp_compat(struct card *card) } // Exxx n1 n2 int1 0 1 - ckt_array[0] = tprintf("%s %s %s %s_int1 0 1", title_tok, - node1, node2, title_tok); + ckt_array[0] = tprintf("%s %s %s %s_int1 0 1", + title_tok, node1, node2, title_tok); // BExxx int1 0 V = {equation} - ckt_array[1] = tprintf("b%s %s_int1 0 v = %s", title_tok, - title_tok, str_ptr); + ckt_array[1] = tprintf("b%s %s_int1 0 v = %s", + title_tok, title_tok, str_ptr); // comment out current variable e line *(card->line) = '*'; @@ -4820,8 +4843,8 @@ static void inp_compat(struct card *card) } else m_token = copy("1"); - ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s", title_tok, - node1, node2, title_tok, m_token); + ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s", + title_tok, node1, node2, title_tok, m_token); // skip "table" cut_line = skip_ws(cut_line); if (!ciprefix("table", cut_line)) { @@ -4834,9 +4857,8 @@ static void inp_compat(struct card *card) if (*cut_line == '=') *cut_line++ = ' '; // get the expression - str_ptr = gettok_char(&cut_line, '{', FALSE, FALSE); - expression = gettok_char( - &cut_line, '}', TRUE, TRUE); /* expression */ + str_ptr = gettok_char(&cut_line, '{', FALSE, FALSE); + expression = gettok_char(&cut_line, '}', TRUE, TRUE); if (!expression || !str_ptr) { fprintf(stderr, "Error: bad syntax in line %d\n %s\n", card->linenum_orig, card->line); @@ -4936,11 +4958,11 @@ static void inp_compat(struct card *card) // Gxxx n1 n2 int1 0 1 // or // Gxxx n1 n2 int1 0 m='expr' - ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s", title_tok, - node1, node2, title_tok, m_token); + ckt_array[0] = tprintf("%s %s %s %s_int1 0 %s", + title_tok, node1, node2, title_tok, m_token); // BGxxx int1 0 V = {equation} - ckt_array[1] = tprintf("b%s %s_int1 0 v = %s", title_tok, - title_tok, str_ptr); + ckt_array[1] = tprintf("b%s %s_int1 0 v = %s", + title_tok, title_tok, str_ptr); // comment out current variable g line *(card->line) = '*'; @@ -4980,14 +5002,14 @@ static void inp_compat(struct card *card) vbFxxx int1 0 0 */ // Fxxx n1 n2 VBFxxx -1 - ckt_array[0] = tprintf("%s %s %s vb%s -1", title_tok, node1, - node2, title_tok); + ckt_array[0] = tprintf("%s %s %s vb%s -1", + title_tok, node1, node2, title_tok); // BFxxx BFxxx_int1 0 I = I(vnam)*{equation} ckt_array[1] = tprintf("b%s %s_int1 0 i = i(%s) * (%s)", title_tok, title_tok, vnamstr, equastr); // VBFxxx int1 0 0 - ckt_array[2] = - tprintf("vb%s %s_int1 0 dc 0", title_tok, title_tok); + ckt_array[2] = tprintf("vb%s %s_int1 0 dc 0", + title_tok, title_tok); // comment out current variable f line *(card->line) = '*'; // insert new three lines immediately after current line @@ -5026,8 +5048,8 @@ static void inp_compat(struct card *card) vbHxxx int1 0 0 */ // Hxxx n1 n2 VBHxxx -1 - ckt_array[0] = tprintf("%s %s %s vb%s -1", title_tok, node1, - node2, title_tok); + ckt_array[0] = tprintf("%s %s %s vb%s -1", + title_tok, node1, node2, title_tok); // BHxxx BHxxx_int1 0 I = I(vnam)*{equation} ckt_array[1] = tprintf("b%s %s_int1 0 i = i(%s) * (%s)", title_tok, title_tok, vnamstr, equastr); @@ -5323,8 +5345,8 @@ static void inp_compat(struct card *card) } } // Fxxx n-aux 0 Bxxx 1 - ckt_array[0] = tprintf( - "f%s %s_int2 0 b%s -1", title_tok, title_tok, title_tok); + ckt_array[0] = tprintf("f%s %s_int2 0 b%s -1", + title_tok, title_tok, title_tok); // Lxxx n-aux 0 1 ckt_array[1] = tprintf("l%s %s_int2 0 1", title_tok, title_tok); // Bxxx n1 n2 V = v(n-aux) * equation @@ -5378,13 +5400,13 @@ static void inp_compat(struct card *card) * + * + * - * .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN - out_variable2=val + * .MEASURE {DC|AC|TRAN} result FIND out_variable + * + WHEN out_variable2=val * + * + * - * .MEASURE {DC|AC|TRAN} result FIND out_variable WHEN - out_variable2=out_variable3 + * .MEASURE {DC|AC|TRAN} result FIND out_variable + * + WHEN out_variable2=out_variable3 * + * + * @@ -5392,7 +5414,7 @@ static void inp_compat(struct card *card) * + * * .MEASURE {DC|AC|TRAN} result {AVG|MIN|MAX|MIN_AT|MAX_AT|PP|RMS} - out_variable + * + out_variable * + * * .MEASURE {DC|AC|TRAN} result INTEG out_variable @@ -5400,22 +5422,20 @@ static void inp_compat(struct card *card) * * .MEASURE {DC|AC|TRAN} result DERIV out_variable AT=val * - * .MEASURE {DC|AC|TRAN} result DERIV out_variable WHEN - out_variable2=val + * .MEASURE {DC|AC|TRAN} result DERIV out_variable + * + WHEN out_variable2=val * + * + * - * .MEASURE {DC|AC|TRAN} result DERIV out_variable WHEN - out_variable2=out_variable3 + * .MEASURE {DC|AC|TRAN} result DERIV out_variable + * + WHEN out_variable2=out_variable3 * + * + The user may set any out_variable to par(' expr '). We have to replace this by v(pa_xx) and generate a B source line. - * - ----------------------------------------------------------------- - */ + * ------------------------------------------------------------ */ if (ciprefix(".meas", curr_line)) { if (strstr(curr_line, "par(") == NULL) continue; @@ -5436,15 +5456,16 @@ static void inp_compat(struct card *card) // find expression beg_ptr = end_ptr = str_ptr + 5; while ((*end_ptr != ' ') && (*end_ptr != '=') && - (*end_ptr != '\0')) + (*end_ptr != '\0')) { end_ptr++; + } exp_ptr = copy_substring(beg_ptr, end_ptr - 2); cut_line = str_ptr; // generate node out_ptr = tprintf("pa_%02d", (int) pai); // Bout_ptr out_ptr 0 V = v(expr_ptr) - ckt_array[pai] = tprintf( - "b%s %s 0 v = %s", out_ptr, out_ptr, exp_ptr); + ckt_array[pai] = tprintf("b%s %s 0 v = %s", + out_ptr, out_ptr, exp_ptr); ckt_array[++pai] = NULL; // length of the replacement V(out_ptr) del_ptr = copy_ptr = tprintf("v(%s)", out_ptr); @@ -5472,8 +5493,8 @@ static void inp_compat(struct card *card) // generate node out_ptr = tprintf("pa_%02d", (int) pai); // Bout_ptr out_ptr 0 V = v(expr_ptr) - ckt_array[pai] = tprintf( - "b%s %s 0 v = %s", out_ptr, out_ptr, exp_ptr); + ckt_array[pai] = tprintf("b%s %s 0 v = %s", + out_ptr, out_ptr, exp_ptr); ckt_array[++pai] = NULL; // length of the replacement V(out_ptr) del_ptr = copy_ptr = tprintf("v(%s)", out_ptr); @@ -5539,8 +5560,8 @@ static void inp_compat(struct card *card) // generate node out_ptr = tprintf("pa_%02d", (int) pai); // Bout_ptr out_ptr 0 V = v(expr_ptr) - ckt_array[pai] = tprintf( - "b%s %s 0 v = %s", out_ptr, out_ptr, exp_ptr); + ckt_array[pai] = tprintf("b%s %s 0 v = %s", + out_ptr, out_ptr, exp_ptr); ckt_array[++pai] = NULL; // length of the replacement V(out_ptr) del_ptr = copy_ptr = tprintf("%s", out_ptr); @@ -5558,7 +5579,7 @@ static void inp_compat(struct card *card) tfree(out_ptr); } // or we have '={par({ ... })}' - else if (ciprefix("={par({", (str_ptr - 2))) { + else if (ciprefix("={par({", str_ptr - 2)) { // find myoutput beg_ptr = end_ptr = str_ptr - 2; @@ -5572,8 +5593,8 @@ static void inp_compat(struct card *card) end_ptr++; exp_ptr = copy_substring(beg_ptr, end_ptr - 3); // Bout_ptr out_ptr 0 V = v(expr_ptr) - ckt_array[pai] = tprintf( - "b%s %s 0 v = %s", out_ptr, out_ptr, exp_ptr); + ckt_array[pai] = tprintf("b%s %s 0 v = %s", + out_ptr, out_ptr, exp_ptr); ckt_array[++pai] = NULL; // length of the replacement V(out_ptr) del_ptr = copy_ptr = tprintf("%s", out_ptr); @@ -5807,15 +5828,17 @@ static bool inp_temper_compat(struct card *card) * and other keywords like TEMPER. --> Only parameter replacement in numparam */ -static char *inp_modify_exp(char *expr) +static char *inp_modify_exp(/* NOT CONST */ char *expr) { char *s; wordlist *wl = NULL, *wlist = NULL; - /* scan the expression and remove all '{' and '}' */ - for (s = expr; *s; s++) - if ((*s == '{') || (*s == '}')) + /* Scan the expression and replace all '{' and '}' with ' ' */ + for (s = expr; *s; s++) { + if ((*s == '{') || (*s == '}')) { *s = ' '; + } + } /* scan the expression */ s = expr; @@ -5860,8 +5883,9 @@ static char *inp_modify_exp(char *expr) else if ((c == '>') || (c == '<') || (c == '!') || (c == '=')) { /* >=, <=, !=, ==, <>, ... */ char *beg = s++; - if ((*s == '=') || (*s == '<') || (*s == '>')) + if ((*s == '=') || (*s == '<') || (*s == '>')) { s++; + } wl->wl_word = copy_substring(beg, s); } else if ((c == '|') || (c == '&')) { @@ -5876,8 +5900,9 @@ static char *inp_modify_exp(char *expr) int i = 0; if (((c == 'v') || (c == 'i')) && (s[1] == '(')) { - while (*s != ')') + while (*s != ')') { buf[i++] = *s++; + } buf[i++] = *s++; buf[i] = '\0'; wl->wl_word = copy(buf); @@ -5927,8 +5952,9 @@ static char *inp_modify_exp(char *expr) double dvalue = INPevaluate(&s, &error1, 0); wl->wl_word = tprintf("%18.10e", dvalue); /* skip the `unit', FIXME INPevaluate() should do this */ - while (isalpha_c(*s)) + while (isalpha_c(*s)) { s++; + } } else { /* strange char */ printf("Preparing expression for numparam\nWhat is this?\n%s\n", @@ -6257,9 +6283,11 @@ static void inp_fix_temper_in_param(struct card *deck) sub_count[j] = 0; /* first pass: determine all .param with temper inside and replace by - .func .param xxx1 = 'temper + 25' will become .func xxx1() 'temper + - 25' - */ + * .func + * .param xxx1 = 'temper + 25' + * will become + * .func xxx1() 'temper + 25' + */ card = deck; for (; card; card = card->nextcard) { @@ -6858,7 +6886,8 @@ static void inp_vdmos_model(struct card *deck) /* storage for devices which get voltage source added */ -struct replace_currm { +struct replace_currm +{ struct card *s_start; struct card *cline; char *rtoken; @@ -6989,8 +7018,9 @@ static void inp_meas_current(struct card *deck) } /* return if we did not find any i( */ - if (rep == NULL) + if (rep == NULL) { return; + } /* scan through all the devices, search for xyz, modify node 1 by adding _vmeas, add a line with zero voltage v_xyz, having original node 1 and @@ -7069,8 +7099,8 @@ static void inp_meas_current(struct card *deck) /* Add _vmeas only once to first device node. Continue if we already have modified device "tok" */ if (!strstr(node1, "_vmeas")) { - new_line = tprintf( - "%s %s_vmeas_%d %s", tok, node1, sn, curr_line); + new_line = tprintf("%s %s_vmeas_%d %s", + tok, node1, sn, curr_line); tfree(card->line); card->line = new_line; } @@ -7079,8 +7109,8 @@ static void inp_meas_current(struct card *deck) /* We have already added a line v_xyz to the deck */ if (!ciprefix(new_tok, card->nextcard->line)) { /* add new line */ - new_line = tprintf( - "%s %s %s_vmeas_%d 0", new_tok, node1, node1, sn); + new_line = tprintf("%s %s %s_vmeas_%d 0", + new_tok, node1, node1, sn); /* insert new_line after card->line */ insert_new_line(card, new_line, card->linenum + 1, 0); } @@ -7102,12 +7132,13 @@ static void inp_meas_current(struct card *deck) } /* replace the E source TABLE function by a B source pwl - (used by ST OpAmps and comparators). - E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 - )*I(VreadIo)} will become BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 - , 35 , 3.3 , 15 , 5 , 10 ) E_RO_3 VB_3 VB_4 VALUE={ - V(TABLE_NEW_1)*I(VreadIo)} -*/ + * (used by ST OpAmps and comparators). + * E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 + * )*I(VreadIo)} + * will become + * BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 + * ) E_RO_3 VB_3 VB_4 VALUE={ V(TABLE_NEW_1)*I(VreadIo)} + */ static void replace_table(struct card *startcard) { struct card *card; @@ -7124,8 +7155,8 @@ static void replace_table(struct card *startcard) /* get the table function */ char *tabfun = gettok_char(&ftablebeg, ')', TRUE, TRUE); /* the new e line */ - char *neweline = tprintf( - "%s v(table_new_%d)%s", begline, numb, ftablebeg); + char *neweline = tprintf("%s v(table_new_%d)%s", + begline, numb, ftablebeg); char *newbline = tprintf("btable_new_%d table_new_%d 0 v=pwl%s", numb, numb, tabfun + 5); @@ -7182,8 +7213,8 @@ static struct card *find_model(struct card *startcard, controlled_exit(1); } /* we have got it */ - char *newmodcard = tprintf(".model %s %s %s%s", newmname, - newmtype, origmodline, endstr); + char *newmodcard = tprintf(".model %s %s %s%s", + newmname, newmtype, origmodline, endstr); char *tmpstr = strstr(newmodcard, ")("); if (tmpstr) { tmpstr[0] = ' '; @@ -7206,7 +7237,7 @@ static struct card *find_model(struct card *startcard, } /* do the .model replacement required by ako (a kind of) - * PSPICE does not support ested .subckt definitions, so + * PSPICE does not support nested .subckt definitions, so * a simple structure is needed: search for ako:modelname, * then for modelname in the subcircuit or in the top level. * .model qorig npn (BF=48 IS=2e-7) @@ -7360,8 +7391,8 @@ static bool del_models(struct vsmodels *vsmodel) * add .functions limit, pwr, pwrs, stp, if, int * replace S1 D S DG GND SWN - .MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } RON = { 1 / (2 * M*(W / -LE)*(KPN / 2) * 10) } ROFF = { 1G }) + .MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) * by as1 %vd(DG GND) % gd(D S) aswn .model aswn aswitch(cntl_off={0.49} cntl_on={0.55} r_off={1G} @@ -7468,8 +7499,8 @@ static struct card *pspice_compat(struct card *oldcard) tctok[-1] = '\0'; char *newstring; if (tc1 && tc2) - newstring = - tprintf("%s tc1=%s tc2=%s", cut_line, tc1, tc2); + newstring = tprintf("%s tc1=%s tc2=%s", + cut_line, tc1, tc2); else if (tc1) newstring = tprintf("%s tc1=%s", cut_line, tc1); else { @@ -7616,8 +7647,9 @@ static struct card *pspice_compat(struct card *oldcard) while (*cut_line && !isspace(*cut_line)) if (!isdigit(*cut_line++)) is_node4 = FALSE; // already model name - if (is_node4) + if (is_node4) { cut_line = nexttok(cut_line); // model name + } } if (cut_line && *cut_line && atof(cut_line) > 0.0) { // size of area is a real number @@ -7667,12 +7699,12 @@ static struct card *pspice_compat(struct card *oldcard) /* replace * S1 D S DG GND SWN * .MODEL SWN VSWITCH ( VON = {0.55} VOFF = {0.49} - RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) + * RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) * by * a1 %v(DG) %gd(D S) swa * .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G - r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) - + * r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) + * * simple hierachy, as nested subcircuits are not allowed in PSPICE */ /* first scan: find the vswitch models, transform them and put them into a @@ -7827,9 +7859,11 @@ static void pspice_compat_a(struct card *oldcard) * Replace * D1 A K SDMOD * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 - * Epsilon=0.2 Ilimit=7 Revilimit=7) by ad1 a k asdmod .model asdmod - * sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 - * Epsilon=0.2 Ilimit=7 Revilimit=7) + * Epsilon=0.2 Ilimit=7 Revilimit=7) + * by + * ad1 a k asdmod + * .model asdmod sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) */ struct card *ltspice_compat(struct card *oldcard) { @@ -7856,13 +7890,13 @@ struct card *ltspice_compat(struct card *oldcard) /* replace * D1 A K SDMOD * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 - Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) * by * a1 a k SDMOD * .model SDMOD sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 - Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) * Do this if one of the parameters, which are uncommon to standard diode - model, has been found. + * model, has been found. * simple hierachy, as nested subcircuits are not allowed in PSPICE */ @@ -7961,14 +7995,14 @@ struct card *ltspice_compat(struct card *oldcard) if ((nesting > 0) && find_a_model(modelsfound, stoks[3], subcktline->line)) { tfree(card->line); - card->line = tprintf("a%s %s %s a%s", stoks[0], stoks[1], - stoks[2], stoks[3]); + card->line = tprintf("a%s %s %s a%s", + stoks[0], stoks[1], stoks[2], stoks[3]); } /* if model is not within same subcircuit, search at top level */ else if (find_a_model(modelsfound, stoks[3], "top")) { tfree(card->line); - card->line = tprintf("a%s %s %s a%s", stoks[0], stoks[1], - stoks[2], stoks[3]); + card->line = tprintf("a%s %s %s a%s", + stoks[0], stoks[1], stoks[2], stoks[3]); } for (i = 0; i < 4; i++) tfree(stoks[i]); From 4f79916a3eb9821e9554df79bf0ae208ce23ad17 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 9 Dec 2019 23:52:41 -0500 Subject: [PATCH 23/58] Cleaned up defintions of macro constants --- src/include/ngspice/defines.h | 74 ++++++++++++++++------------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/src/include/ngspice/defines.h b/src/include/ngspice/defines.h index e95f650ce..57b98afcd 100644 --- a/src/include/ngspice/defines.h +++ b/src/include/ngspice/defines.h @@ -13,6 +13,10 @@ #ifndef ngspice_DEFINES_H #define ngspice_DEFINES_H +/* Floating point and integral limits */ +#include +#include + /* * Physical constants (const.h) */ @@ -20,6 +24,13 @@ * CONSTepsSi02, CONSTmuZero, REFTEMP */ #include "ngspice/const.h" +/* These constants are defined by GCC in math.h, but they are not part of + * the ANSI C standard. The #ifndefs will prevent warnings regarding macro + * redefinitions if this file is included AFTER math.h. However, if the + * order is reversed, the warnings will occur. Thus, they introduce a header + * order dependency. A better solution would be to rename the macros to + * something like NGM_* (ngspice math) throughout the source code. Then there + * would be no header ordering dependency. */ #ifndef M_PI #define M_PI CONSTpi #endif @@ -37,60 +48,43 @@ * IEEE Floating point */ -#define MAX_EXP_ARG 709.0 - -#ifndef DBL_EPSILON -# define DBL_EPSILON 2.2204460492503131e-16 -#endif -#ifndef DBL_MAX -# define DBL_MAX 1.79769313486231e+308 -#endif -#ifndef DBL_MIN -# define DBL_MIN 2.22507385850721e-308 -#endif -#ifndef SHRT_MAX -# define SHRT_MAX 32766 -#endif -#ifndef INT_MAX -# define INT_MAX 2147483646 -#endif -#ifndef LONG_MAX -# define LONG_MAX 2147483646 -#endif - -#define MAXPOSINT INT_MAX +/* Largest exponent such that exp(MAX_EXP_ARG) <= DBL_MAX + * Actual max is ln(DBL_MAX) = 709.78. Unsure if there was any reason + * for setting the value lower */ +#define MAX_EXP_ARG 709.0 /* Standard initialisation file name */ -#define INITSTR ".spiceinit" +#define INITSTR ".spiceinit" /* Alternate initialisation file name */ -#define ALT_INITSTR "spice.rc" +#define ALT_INITSTR "spice.rc" #if defined(__MINGW32__) || defined(_MSC_VER) || defined (HAS_WINGUI) -#define DIR_PATHSEP "\\" -#define DIR_TERM '\\' -#define DIR_PATHSEP_LINUX "/" -#define DIR_TERM_LINUX '/' -#define DIR_CWD "." +#define DIR_PATHSEP "\\" +#define DIR_TERM '\\' +#define DIR_PATHSEP_LINUX "/" +#define DIR_TERM_LINUX '/' +#define DIR_CWD "." + +#define TEMPFORMAT "%s%d.tmp" -#define TEMPFORMAT "%s%d.tmp" /* -#define SYSTEM_PLOT5LPR "lpr -P%s -g %s" -#define SYSTEM_PSLPR "lpr -P%s %s" -#define SYSTEM_MAIL "Mail -s \"%s (%s) Bug Report\" %s" +#define SYSTEM_PLOT5LPR "lpr -P%s -g %s" +#define SYSTEM_PSLPR "lpr -P%s %s" +#define SYSTEM_MAIL "Mail -s \"%s (%s) Bug Report\" %s" */ #else -#define DIR_PATHSEP "/" -#define DIR_TERM '/' -#define DIR_CWD "." +#define DIR_PATHSEP "/" +#define DIR_TERM '/' +#define DIR_CWD "." -#define TEMPFORMAT "/tmp/%s%d" -#define SYSTEM_PLOT5LPR "lpr -P%s -g %s" -#define SYSTEM_PSLPR "lpr -P%s %s" -#define SYSTEM_MAIL "Mail -s \"%s (%s) Bug Report\" %s" +#define TEMPFORMAT "/tmp/%s%d" +#define SYSTEM_PLOT5LPR "lpr -P%s -g %s" +#define SYSTEM_PSLPR "lpr -P%s %s" +#define SYSTEM_MAIL "Mail -s \"%s (%s) Bug Report\" %s" #endif From a968c5c2fd1db6258c22a600fecda179776840b7 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 00:14:22 -0500 Subject: [PATCH 24/58] Made UI more consistent when comments are entered. --- src/frontend/parser/lexical.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/frontend/parser/lexical.c b/src/frontend/parser/lexical.c index e5ba348c6..cd970ea3a 100644 --- a/src/frontend/parser/lexical.c +++ b/src/frontend/parser/lexical.c @@ -225,8 +225,11 @@ nloop: tfree(linebuf.s); return NULL; } - while (((c = cp_readchar(&string, cp_inp_cur)) != '\n') && (c != EOF)) + while (((c = cp_readchar(&string, cp_inp_cur)) != '\n') && + (c != EOF)) { ; + } + prompt(); goto nloop; } From 3982458005c84ae5935e0a45f9bea1c668aa9fac Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 01:27:14 -0500 Subject: [PATCH 25/58] Fixed reporting of system information in Windows. --- src/frontend/com_sysinfo.c | 984 ++++++++++++++++++++++++++++--------- 1 file changed, 739 insertions(+), 245 deletions(-) diff --git a/src/frontend/com_sysinfo.c b/src/frontend/com_sysinfo.c index 2cd1c2509..8d1f817c0 100644 --- a/src/frontend/com_sysinfo.c +++ b/src/frontend/com_sysinfo.c @@ -12,40 +12,32 @@ #include "ngspice/fteext.h" #include "com_commands.h" -/* We might compile for Windows, but only as a console application (e.g. tcl) */ -#if defined(HAS_WINGUI) || defined(__MINGW32__) || defined(_MSC_VER) -#define HAVE_WIN32 -#endif - -#ifdef HAVE_WIN32 - -#define WIN32_LEAN_AND_MEAN +#ifdef _WIN32 +//#define WIN32_LEAN_AND_MEAN #undef BOOLEAN -#include "windows.h" +#include #include -#include -#include #endif -#include "stdio.h" -#include "stdlib.h" -#include "string.h" +#include +#include +#include +#include -#define tInt int -#define TesError int -#define TES_FAIL 1 -#define TES_SUCCESS 0 -#define TES_INVALID_PARAMS 1 +#include "ngspice/dstring.h" /* system info */ typedef struct TSI { char *cpuModelName; - unsigned numPhysicalProcessors; - unsigned numLogicalProcessors; char *osName; + unsigned int numPhysicalProcessors; + unsigned int numLogicalProcessors; } TesSystemInfo; +/* Struture with info about system */ +static TesSystemInfo system_info; + /* memory info */ struct sys_memory { unsigned long long size_m; /* Total memory size */ @@ -54,82 +46,132 @@ struct sys_memory { unsigned long long swap_f; /* Swap free */ }; -static struct sys_memory mem_t_act; - -TesError tesCreateSystemInfo(TesSystemInfo *info); +static void fprintmem(FILE *stream, unsigned long long memory); +static void free_static_system_info(void); static int get_sysmem(struct sys_memory *memall); +static void set_static_system_info(void); + +#ifdef _WIN32 +static inline void get_logical_processor_count(void); +static void get_os_info(void); +static void get_physical_processor_count(void); +static void get_processor_name(void); +#endif + + + +/* Print the available system info */ +void com_sysinfo(wordlist *wl) +{ + NG_IGNORE(wl); + + /* Invariant system data such as OS name */ + { + /* Flag that have at least some system info */ + bool f_have_system_info = FALSE; + + static bool f_first_call = TRUE; + if (f_first_call) { + /* Obtain the system info when this function is called the + * first time */ + set_static_system_info(); + + /* Free the allocations on exit. Not really necessary since they + * will be cleaned up then, but it may be useful when checking for + * memory leaks */ + if (atexit(&free_static_system_info) != 0) { + fprintf(cp_err, + "Unable to set handler to clean up system info.\n"); + } + + /* Mark that first-call init is done. Note that since the calls to + * set_static_system_info() and atexit define sequence points, the + * flag will not be set until after they complete, so the code is + * safe for reentrant calls. */ + f_first_call = FALSE; + } + + if (system_info.osName != (char *) NULL) { + fprintf(cp_out, "\nOS: %s\n", system_info.osName); + f_have_system_info = TRUE; + } + + if (system_info.cpuModelName != (char *) NULL) { + fprintf(cp_out, "CPU: %s\n", system_info.cpuModelName); + f_have_system_info = TRUE; + } + + if (system_info.numPhysicalProcessors > 0) { + fprintf(cp_out, "Physical processors: %u, ", + system_info.numPhysicalProcessors); + f_have_system_info = TRUE; + } + + if (system_info.numLogicalProcessors > 0) { + fprintf(cp_out, "Logical processors: %u\n", + system_info.numLogicalProcessors); + f_have_system_info = TRUE; + } + + /* Print something if no system info available */ + if (!f_have_system_info) { + fprintf(cp_err, "No system info available!\n"); + } + } /* end of block getting invariant system info */ + + /* Get memory information */ + { + struct sys_memory mem_t_act; + if (get_sysmem(&mem_t_act) == 0) { + /* get_sysmem returns bytes */ + fprintf(cp_out, "Total DRAM available = "); + fprintmem(cp_out, mem_t_act.size_m); + fprintf(cp_out, ".\n"); + + fprintf(cp_out, "DRAM currently available = "); + fprintmem(cp_out, mem_t_act.free_m); + fprintf(cp_out, ".\n\n"); + } + else { + fprintf(cp_err, "Memory info is unavailable! \n"); + } + } + + return; +} /* end of function com_sysinfo */ + + + +/* This function frees the buffers used to store system allocation strings */ +static void free_static_system_info(void) +{ + tfree(system_info.cpuModelName); + tfree(system_info.osName); +} /* end of fuction free_system_info */ + /* Print to stream the given memory size in a human friendly format */ -static void -fprintmem(FILE *stream, unsigned long long memory) +static void fprintmem(FILE *stream, unsigned long long memory) { - if (memory > 1048576) - fprintf(stream, "%8.6f MB", (double)memory /1048576.); - else if (memory > 1024) - fprintf(stream, "%5.3f kB", (double)memory / 1024.); - else - fprintf(stream, "%u bytes", (unsigned)memory); -} - - -static void -tesFreeSystemInfo(TesSystemInfo *info) -{ - if (info != NULL) { - tfree(info->cpuModelName); - tfree(info->osName); + if (memory > 1048576) { + fprintf(stream, "%8.6f MB", (double) memory /1048576.); } -} - - -/* print system info */ -void -com_sysinfo(wordlist *wl) -{ - int errorcode; - TesSystemInfo *info; - - NG_IGNORE(wl); - - info = TMALLOC(TesSystemInfo, 1); - - errorcode = tesCreateSystemInfo(info); - if (errorcode) { - fprintf(cp_err, "No system info available! \n"); - } else { - fprintf(cp_out, "\nOS: %s\n", info->osName); - fprintf(cp_out, "CPU: %s\n", info->cpuModelName); - if (info->numPhysicalProcessors > 0) - fprintf(cp_out, "Physical processors: %u, ", info->numPhysicalProcessors); - fprintf(cp_out, "Logical processors: %u\n", - info->numLogicalProcessors); + else if (memory > 1024) { + fprintf(stream, "%5.3f kB", (double) memory / 1024.); } -#if defined(HAVE_WIN32) || defined(HAVE__PROC_MEMINFO) - - get_sysmem(&mem_t_act); - - /* get_sysmem returns bytes */ - fprintf(cp_out, "Total DRAM available = "); - fprintmem(cp_out, mem_t_act.size_m); - fprintf(cp_out, ".\n"); - - fprintf(cp_out, "DRAM currently available = "); - fprintmem(cp_out, mem_t_act.free_m); - fprintf(cp_out, ".\n\n"); - -#endif - - tesFreeSystemInfo(info); - tfree(info); -} + else { + fprintf(stream, "%u bytes", (unsigned) memory); + } +} /* end of funtion fprintmem */ + +/*** Get processor and memory information as appropriate for the system ***/ #ifdef HAVE__PROC_MEMINFO /* Get memory information */ -static int -get_sysmem(struct sys_memory *memall) +static int get_sysmem(struct sys_memory *memall) { FILE *fp; char buffer[2048]; @@ -139,51 +181,51 @@ get_sysmem(struct sys_memory *memall) if ((fp = fopen("/proc/meminfo", "r")) == NULL) { perror("fopen(\"/proc/meminfo\")"); - return 0; + return -1; } bytes_read = fread(buffer, 1, sizeof(buffer), fp); fclose(fp); if (bytes_read == 0 || bytes_read == sizeof(buffer)) - return 0; + return -1; buffer[bytes_read] = '\0'; /* Search for string "MemTotal" */ match = strstr(buffer, "MemTotal"); if (match == NULL) /* not found */ - return 0; + return -1; sscanf(match, "MemTotal: %ld", &mem_got); memall->size_m = mem_got*1024; /* 1MB = 1024KB */ /* Search for string "MemFree" */ match = strstr(buffer, "MemFree"); if (match == NULL) /* not found */ - return 0; + return -1; sscanf(match, "MemFree: %ld", &mem_got); memall->free_m = mem_got*1024; /* 1MB = 1024KB */ /* Search for string "SwapTotal" */ match = strstr(buffer, "SwapTotal"); if (match == NULL) /* not found */ - return 0; + return -1; sscanf(match, "SwapTotal: %ld", &mem_got); memall->swap_t = mem_got*1024; /* 1MB = 1024KB */ /* Search for string "SwapFree" */ match = strstr(buffer, "SwapFree"); if (match == NULL) /* not found */ - return 0; + return -1; sscanf(match, "SwapFree: %ld", &mem_got); memall->swap_f = mem_got*1024; /* 1MB = 1024KB */ - return 1; + return 0; } /* Return length of first line in a string */ -static size_t -getLineLength(const char *str) +static inline size_t getLineLength(const char *str) { const char *p = str; - while (*p && (*p != '\n')) + while (*p && (*p != '\n')) { p++; + } return (size_t) (p - str); } @@ -191,8 +233,7 @@ getLineLength(const char *str) /* Checks if number 'match' is found in a vector 'set' of size 'size' Returns 1 if yes, otherwise, 0 */ -static tInt -searchInSet(const tInt *set, unsigned size, tInt match) +static int searchInSet(const int *set, unsigned size, int match) { unsigned index; for (index = 0; index < size; index++) @@ -203,17 +244,14 @@ searchInSet(const tInt *set, unsigned size, tInt match) /* Get system information */ -TesError -tesCreateSystemInfo(TesSystemInfo *info) +static void set_static_system_info(void) { FILE *file; - TesError error = TES_SUCCESS; - if (info == NULL) - return TES_INVALID_PARAMS; - info->cpuModelName = NULL; - info->osName = NULL; - info->numLogicalProcessors = info->numPhysicalProcessors = 0; + /* Init to all information unailable */ + system_info.cpuModelName = (char *) NULL; + system_info.osName = (char *) NULL; + system_info.numLogicalProcessors = system_info.numPhysicalProcessors = 0; /* get kernel version string */ file = fopen("/proc/version", "rb"); @@ -221,18 +259,18 @@ tesCreateSystemInfo(TesSystemInfo *info) size_t size; /* read bytes and find end of file */ - for (size = 0; ; size++) - if (EOF == fgetc(file)) + for (size = 0; ; size++) { + if (EOF == fgetc(file)) { break; + } + } - info->osName = TMALLOC(char, size); + system_info.osName = TMALLOC(char, size + 1); rewind(file); - fread(info->osName, sizeof(char), size, file); + fread(system_info.osName, sizeof(char), size, file); fclose(file); - info->osName[size-1] = '\0'; - } else { - error = TES_FAIL; + system_info.osName[size] = '\0'; } /* get cpu information */ @@ -242,9 +280,11 @@ tesCreateSystemInfo(TesSystemInfo *info) char *inStr; /* read bytes and find end of file */ - for (size = 0; ; size++) - if (EOF == fgetc(file)) + for (size = 0; ; size++) { + if (EOF == fgetc(file)) { break; + } + } /* get complete string */ inStr = TMALLOC(char, size+1); @@ -265,23 +305,20 @@ tesCreateSystemInfo(TesSystemInfo *info) if (numToEOL > 2) { /* skip ": "*/ numToEOL -= 2; - info->cpuModelName = TMALLOC(char, numToEOL+1); - memcpy(info->cpuModelName, modelPtr+2, numToEOL); - info->cpuModelName[numToEOL] = '\0'; + system_info.cpuModelName = TMALLOC(char, numToEOL+1); + memcpy(system_info.cpuModelName, modelPtr+2, numToEOL); + system_info.cpuModelName[numToEOL] = '\0'; } - } else { - error = TES_FAIL; } - } else { - error = TES_FAIL; } } + { const char *matchStrProc = "processor"; const char *matchStrPhys = "physical id"; char *strPtr = inStr; unsigned numProcs = 0; - tInt *physIDs; + int *physIDs; /* get number of logical processors */ while ((strPtr = strstr(strPtr, matchStrProc)) != NULL) { @@ -289,8 +326,8 @@ tesCreateSystemInfo(TesSystemInfo *info) strPtr += strlen(matchStrProc); if (isblank_c(*strPtr)) numProcs++; } - info->numLogicalProcessors = numProcs; - physIDs = TMALLOC(tInt, numProcs); + system_info.numLogicalProcessors = numProcs; + physIDs = TMALLOC(int, numProcs); /* get number of physical CPUs */ numProcs = 0; @@ -303,7 +340,7 @@ tesCreateSystemInfo(TesSystemInfo *info) /* go to ';' */ strPtr = strchr(strPtr, ':'); if (strPtr != NULL) { - tInt buffer = 0; + int buffer = 0; /* skip ": " */ strPtr += 2; /* get number */ @@ -314,21 +351,21 @@ tesCreateSystemInfo(TesSystemInfo *info) physIDs[numProcs] = buffer; numProcs++; } - } else { - // error = TES_FAIL; + } + else { break; } - } else { - // error = TES_FAIL; + } + else { break; } } - info->numPhysicalProcessors = numProcs; + system_info.numPhysicalProcessors = numProcs; tfree(physIDs); } /* another test to get number of logical processors - * if (info->numLogicalProcessors == 0) { + * if (system_info.numLogicalProcessors == 0) { * char *token; * char *cpustr = copy(inStr); * while (cpustr && !*cpustr) @@ -337,30 +374,31 @@ tesCreateSystemInfo(TesSystemInfo *info) * token = gettok(&cpustr); * } * - * info->numLogicalProcessors = atoi(token) + 1; + * system_info.numLogicalProcessors = atoi(token) + 1; * tfree(cpustr); * } */ tfree(inStr); fclose(file); - } else { - error = TES_FAIL; - } + } /* end of case that file was opened OK */ - return error; -} + return; +} /* end of function set_static_system_info */ -#elif defined(HAVE_WIN32) -/* get memory information */ -static int -get_sysmem(struct sys_memory *memall) + +#elif defined(_WIN32) + +/* Get memory information */ +static int get_sysmem(struct sys_memory *memall) { -#if (_WIN32_WINNT >= 0x0500) +#if (_WIN32_WINNT >= 0x0500) /* Windows 2000+ */ MEMORYSTATUSEX ms; ms.dwLength = sizeof(MEMORYSTATUSEX); - GlobalMemoryStatusEx(&ms); + if (GlobalMemoryStatusEx(&ms) == FALSE) { + return -1; + } memall->size_m = ms.ullTotalPhys; memall->free_m = ms.ullAvailPhys; memall->swap_t = ms.ullTotalPageFile; @@ -368,119 +406,575 @@ get_sysmem(struct sys_memory *memall) #else MEMORYSTATUS ms; ms.dwLength = sizeof(MEMORYSTATUS); - GlobalMemoryStatus(&ms); + if (GlobalMemoryStatus(&ms) == FALSE) { + return -1; + } memall->size_m = ms.dwTotalPhys; memall->free_m = ms.dwAvailPhys; memall->swap_t = ms.dwTotalPageFile; memall->swap_f = ms.dwAvailPageFile; #endif - return 1; -} + return 0; +} /* end of function get_sysmem */ -/* get system information */ -TesError -tesCreateSystemInfo(TesSystemInfo *info) + +/* This function gets system information about the version of Windows and + * the number processors available, and save this information in the static + * TesSystemInfo structure. If an item cannot be obtained, it is set to + * 0/NULL. This allows callers to check for valid data since neither of these + * values are valid */ +static void set_static_system_info(void) { - char *procStr, *freeStr; - char mversion[32]; - DWORD dwLen = 0; - HKEY hkBaseCPU; - LONG lResult; + get_processor_name(); /* name of processor */ + get_os_info(); /* name of OS with build and service pack, if any */ + get_logical_processor_count(); /* Get number of logical cores */ + get_physical_processor_count(); /* # hardware components */ + return; +} /* end of function set_static_system_info */ - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - info->numPhysicalProcessors = 0; - info->numLogicalProcessors = sysinfo.dwNumberOfProcessors; - info->osName = NULL; - mversion[0] = '\0'; - if (IsWindowsXPOrGreater()) { - strcpy(mversion, "Windows XP"); - } - - if (IsWindowsXPSP1OrGreater()) { - strcpy(mversion, "Windows XP Service Pack 1"); - } - - if (IsWindowsXPSP2OrGreater()) { - strcpy(mversion, "Windows XP Service Pack 2"); - } - - if (IsWindowsXPSP3OrGreater()) { - strcpy(mversion, "Windows XP Service Pack 3"); - } - - if (IsWindowsVistaOrGreater()) { - strcpy(mversion, "Windows Vista"); - } - - if (IsWindowsVistaSP1OrGreater()) { - strcpy(mversion, "Windows Vista Service Pack 1"); - } - - if (IsWindowsVistaSP2OrGreater()) { - strcpy(mversion, "Windows Vista Service Pack 2"); - } - - if (IsWindows7OrGreater()) { - strcpy(mversion, "Windows 7"); - } - - if (IsWindows7SP1OrGreater()) { - strcpy(mversion, "Windows 7 Service Pack 1"); - } - - if (IsWindows8OrGreater()) { - strcpy(mversion, "Windows 8"); - } - if (IsWindows8Point1OrGreater()) { - strcpy(mversion, "Windows 8.1"); - } - /* SDK has version 10 and thus IsWindows10OrGreater() */ -#if (VER_PRODUCTBUILD > 10000) - /* This will deliver the right OS version only if we have a manifest */ - if (IsWindows10OrGreater()) { - strcpy(mversion, "Windows 10"); - } -#endif - if (IsWindowsServer()) { - printf("Server\n"); - } - - if (mversion[0] != '\0') - info->osName = tprintf("%s", mversion); - - lResult = RegOpenKeyExA(HKEY_LOCAL_MACHINE, - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", - 0, KEY_READ, &hkBaseCPU); - if (lResult != ERROR_SUCCESS) { - info->cpuModelName = NULL; - return TES_FAIL; - } - - RegQueryValueExA(hkBaseCPU, "ProcessorNameString", 0, 0, NULL, &dwLen); - freeStr = procStr = TMALLOC(char, dwLen + 1); - RegQueryValueExA(hkBaseCPU, "ProcessorNameString", 0, 0, (LPBYTE)procStr, &dwLen); - procStr[dwLen] = '\0'; - while (*procStr == ' ') - procStr++; - info->cpuModelName = copy(procStr); - tfree(freeStr); - - RegCloseKey(hkBaseCPU); - - return TES_SUCCESS; -} - -#else - -/* no Windows OS, no proc info file system */ -TesError -tesCreateSystemInfo(TesSystemInfo *info) +/* Copy data at HKLM/sz_subkey/sz_val_name to an allocated buffer that is + * 1 byte longer and always null-termianted, possibly with 2 nulls + * + * Parameters + * sz_subkey: Subkey string + * sz_val_name: Name of value to get + * p_ds: Address of dstring to receive data + * + * Return codes + * 0: Data obtained OK + * -1: Data not obtained. + */ +static int registry_value_to_ds(const char *sz_subkey, + const char *sz_val_name, DSTRING *p_ds) { - return 1; -} + int xrc = 0; + DWORD n_byte_data = 0; + HKEY hk; + bool f_key_open = FALSE; + + /* Opwn the key with the processor details */ + { + DWORD rc; + if ((rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + sz_subkey, 0, KEY_READ, &hk)) != ERROR_SUCCESS) { + fprintf(cp_err, + "Unable to open key for registry data \"%s\". " + "System code = %lu\n", + sz_subkey, rc); + xrc = -1; + goto EXITPOINT; + } + } + f_key_open = TRUE; + + /* Get size of the name string. Strings in the registry need not be + * null-terminated, but if they are, the null is included in the + * size. */ + { + DWORD rc; + if ((rc = RegQueryValueExA(hk, sz_val_name, + 0, 0, NULL, &n_byte_data)) != ERROR_SUCCESS) { + fprintf(cp_err, + "Unable to get the size of value for \"%s\". " + "System code = %lu\n", + sz_val_name, rc); + xrc = -1; + goto EXITPOINT; + } + } + + /* Ensure dstring buffer is large enough for the data + 1 byte to add + * a null to the end */ + { + size_t n_byte_reserve = (size_t) n_byte_data + 1; + if (ds_reserve(p_ds, n_byte_reserve) != 0) { + (void) fprintf(cp_err, + "Unable to reserve a buffer of %u bytes for data.\n", + n_byte_reserve); + xrc = -1; + goto EXITPOINT; + } + } + + /* Retrieve the value using the dstring buffer to receive it */ + { + DWORD rc; + char *p_buf = ds_get_buf(p_ds); + if ((rc = RegQueryValueExA(hk, sz_val_name, 0, 0, + (LPBYTE) p_buf, &n_byte_data)) != ERROR_SUCCESS) { + (void) fprintf(cp_err, + "Unable to get the value for \"%s\". " + "System code = %lu\n", + sz_val_name, rc); + xrc = -1; + goto EXITPOINT; + } + } + + /* Set the dstring length */ + (void) ds_set_length(p_ds, n_byte_data); + +EXITPOINT: + /* Indicate error if failure */ + if (xrc != 0) { + ds_clear(p_ds); + } + + if (f_key_open) { /* close key if opened */ + RegCloseKey(hk); + } + + return xrc; +} /* end of function registry_value_to_ds */ + + + +/* Gets the name of the processor from the registry and sets field + * cpuModelName in system_info. On failure, the field is set to NULL */ +static void get_processor_name(void) +{ + DS_CREATE(ds, 200); + + system_info.cpuModelName = NULL; /* init in case of failure */ + if (registry_value_to_ds( + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + "ProcessorNameString", + &ds) != 0) { + (void) fprintf(cp_err, + "Unable to get processor name data from the registry.\n"); + return; + } + + /* Step past any leading blanks and copy name to cpuModelName */ + { + const char *proc_name = ds_get_buf(&ds); + + while (*proc_name == ' ') { + ++proc_name; + } /* end of loop finding first non-blank of processor name */ + + /* Make a copy of the string at cpuModelName field of system_info */ + system_info.cpuModelName = copy(proc_name); + } + + ds_free(&ds); /* Free resources */ + + return; +} /* end of function get_processor_name */ + + + +/* This function gets the release details to distinguish between + * 2016 and 2019 servers. If necessary, it can be extended to return + * codes for other servers in the future. + * + * See + * https://techcommunity.microsoft.com/t5/Windows-Server-Insiders/Windows-Server-2019-version-info/m-p/234472 + * + * Return codes + * -1: Failure + * +1: 2016 server + * +2: 2019 server (probably) + * + * Remarks + * Calling this function alone is not sufficient to identify a server. + * Rather it should be called given that a server OS is present to identify + * the serer version. + */ +static int get_server_id(void) +{ + DS_CREATE(ds, 25); + + if (registry_value_to_ds( + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + "ReleaseId", + &ds) != 0) { + (void) fprintf(cp_err, + "Unable to get release ID data from the registry.\n"); + return -1; + } + + int id_code = -1; /* Set to failure until found */ + /* Convert the release ID to a number */ + { + char *p_end; + errno = 0; + const char *p_buf = ds_get_buf(&ds); + unsigned long id_val = strtoul(p_buf, &p_end, 10); + if (errno || *p_end != '\0') { + fprintf(cp_err, + "Unable to convert \"%s\" to a release ID number.\n", + p_buf); + goto EXITPOINT; + } + + if (id_val == 1607ul) { /* code for 2016 server */ + id_code = 1; + } + else if (id_code > 1607ul) { /* Probably 2019 server */ + id_code = 2; + } + /* Else unknown ID */ + } + +EXITPOINT: + ds_free(&ds); /* Free resources */ + + return id_code; +} /* end of function get_server_id */ + + + +/* This function creates a name of the form ' ' , + * allocates a buffer for it, and stores it in system_info.osNname. On + * failure an error is reported and the string is set to NULL. + * + * Remarks + * Getting the version has been complicated greatly in later versions of + * Windows. A good discussion of the issue can be found at + * https://stackoverflow.com/questions/47581146/getting-os-build-version-from-win32-api-c + * + * First, the function GetVersionEx() has been deprecated, so the + * straightforward call to retrieve the version is not the recommended + * approach any longer and will output a message to this effect during + * compilation. Also, it may be removed at some later time. Even if it is + * called, since Windows 8.0, the value returned depends not on the version + * of the OS, but the manifested version of the calling program. + * + * As an alternative function RtlGetVersion() always returns version info + * the same version as GetVersionEx() prior to Windows 8.1, and it + * is not deprecated. Unfortunately, the simple solution is made less + * simple because the header providing a prototype for RtlGetVersion() + * is part of the Windows DDK and the function is not directly exposed + * by ntdll.lib. Also, the DDK only works with UTF-16, so the name string + * must be converted. + * + * The following link has a table showing how to determine the all operating + * systems from Windows 2000 through Windows 10/Windows Server 2016. + * https://web.archive.org/web/20190501082653/https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa + * + * OS ver Other OSV=OSVERSIONINFOEX + * Windows Server 2016 10.0 OSV.wProductType != VER_NT_WORKSTATION + * Windows 10 10.0 OSV.wProductType == VER_NT_WORKSTATION + * Windows Server 2008 6.0 OSV.wProductType != VER_NT_WORKSTATION + * Windows Vista 6.0 OSV.wProductType == VER_NT_WORKSTATION + * Windows Server 2008 R2 6.1 OSV.wProductType != VER_NT_WORKSTATION + * Windows 7 6.1 OSV.wProductType == VER_NT_WORKSTATION + * Windows Server 2012 6.2 OSV.wProductType != VER_NT_WORKSTATION + * Windows 8 6.2 OSV.wProductType == VER_NT_WORKSTATION + * Windows Server 2012 R2 6.3 OSV.wProductType != VER_NT_WORKSTATION + * Windows 8.1 6.3 OSV.wProductType == VER_NT_WORKSTATION + * Windows 2000 5.0 Not applicable + * Windows XP 5.1 Not applicable + * Windows Home Server 5.2 OSV.wSuiteMask & VER_SUITE_WH_SERVER + * Windows XP Professional + * x64 Edition 5.2 (OSV.wProductType == VER_NT_WORKSTATION) && + * (SYSTEM_INFO.wProcessorArchitecture == + * PROESSOR_ARCHITECTURE_AMD64) + * Windows Server 2003 5.2 GetSystemMetrics(SM_SERVERR2) == 0 + * Windows Server 2003 R2 5.2 GetSystemMetrics(SM_SERVERR2) != 0 + + * Information on distinguishing between Windows Server 2016 and 2019 does + * not appear to have been provided as of early 2019: + * https://stackoverflow.com/questions/53393150/c-how-to-detect-windows-server-2019 + * Hopefully this issue will be resolved in the future. + */ +static void get_os_info(void) +{ + OSVERSIONINFOEXW ver_info; + + /* the name of the OS. Init to prevent compiler warning */ + const char *sz_os_name = NULL; + + /* Load library containing RtlGetVersion() */ + HMODULE lib = LoadLibraryExW(L"ntdll.dll", NULL, 0); + if (lib == (HMODULE) NULL) { /* Not loaded OK */ + (void) fprintf(cp_err, + "Unable to load ntdll.dll. " + "System code = %lu\n", + (unsigned long) GetLastError()); + system_info.osName = (char *) NULL; + return; + } + + /* Locate RtlGetVersion() */ + FARPROC p_get_ver = GetProcAddress(lib, "RtlGetVersion"); + if (p_get_ver == (FARPROC) NULL) { /* Did not get function addr OK */ + (void) fprintf(cp_err, + "Unable to locate function RtlGetVersion. " + "System code = %lu\n", + (unsigned long) GetLastError()); + system_info.osName = (char *) NULL; + return; + } + + /* Get version info. RtlGetVersion cannot fail. */ + ver_info.dwOSVersionInfoSize = sizeof(ver_info); + (void) ((DWORD (WINAPI *)(OSVERSIONINFOEXW *)) p_get_ver)( + &ver_info); + + switch (ver_info.dwMajorVersion) { + case 10: { + static const char OS_srvr[] = "Windows Server 2016/2019/other"; + static const char OS_10[] = "Windows 10"; + static const char OS_2016[] = "Windows Server 2016"; + static const char OS_2019[] = "Windows Server 2019"; + static const char * const p_str[] = { + OS_srvr, OS_10, OS_2016, OS_2019 + }; + + if (ver_info.dwMinorVersion != 0) { /* only know 10.0 */ + system_info.osName = (char *) NULL; + return; + } + sz_os_name = p_str[ver_info.wProductType == VER_NT_WORKSTATION ? + 1 : get_server_id() + 1]; + break; + } + case 6: { + static const char OS_2008[] = "Windows Server 2008"; + static const char OS_vista[] = "Windows Vista"; + static const char OS_2008R2[] = "Windows Server 2008 R2"; + static const char OS_7[] = "Windows 7"; + static const char OS_2012[] = "Windows Server 2012"; + static const char OS_8[] = "Windows 8"; + static const char OS_2012R2[] = "Windows Server 2012 R2"; + static const char OS_8_1[] = "Windows 8.1"; + static const char * const p_str[] = { + OS_2008, OS_vista, + OS_2008R2, OS_7, + OS_2012, OS_8, + OS_2012R2, OS_8_1 + }; + if (ver_info.dwMinorVersion > 3) { /* know 6.0 through 6.3 */ + (void) fprintf(cp_err, "Unknown Windows version 6.%lu. ", + (unsigned long) ver_info.dwMinorVersion); + system_info.osName = (char *) NULL; + return; + } + sz_os_name = p_str[2 * ver_info.dwMinorVersion + + ver_info.wProductType == VER_NT_WORKSTATION]; + break; + } + case 5: { /* an assortment of other conditions must be checked */ + + switch (ver_info.dwMinorVersion) { /* filter by minor verson */ + case 0: { + static const char OS_2k[] = "Windows 2000"; + sz_os_name = OS_2k; + break; + } + case 1: { + static const char OS_xp[] = "Windows XP"; + sz_os_name = OS_xp; + break; + } + case 2: + if (ver_info.wSuiteMask & VER_SUITE_WH_SERVER) { + static const char OS_home_server[] = "Windows Home Server"; + sz_os_name = OS_home_server; + } + else if (ver_info.wProductType == VER_NT_WORKSTATION) { + SYSTEM_INFO si; + GetSystemInfo(&si); + if (si.wProcessorArchitecture == + PROCESSOR_ARCHITECTURE_AMD64) { + static const char OS_xp64[] = + "Windows XP Professional x64 Edition"; + sz_os_name = OS_xp64; + } + } + else { /* Server 2003 or 2003 R2 */ + static const char OS_2003R2[] = "Windows Server 2003 R2"; + static const char OS_2003[] = "Windows Server 2003"; + static const char * const p_str[] = {OS_2003R2, OS_2003}; + sz_os_name = p_str[!GetSystemMetrics(SM_SERVERR2)]; + } + break; + default: + (void) fprintf(cp_err, "Unknown Windows version 5.%lu. ", + (unsigned long) ver_info.dwMinorVersion); + system_info.osName = (char *) NULL; + return; + } /* end of switch over minor version for major version 5 */ + break; + } + case 4: + switch (ver_info.dwMinorVersion) { + case 0: { + static const char OS_95[] = "Windows 95"; + static const char OS_nt4[] = "Windows NT 4.0"; + static const char * const p_str[] = {OS_95, OS_nt4}; + sz_os_name = p_str[ver_info.wProductType == VER_NT_WORKSTATION]; + } + case 10: { + static const char OS_98[] = "Windows 98"; + sz_os_name = OS_98; + break; + } + case 90: { + static const char OS_me[] = "Windows ME"; + sz_os_name = OS_me; + break; + } + default: + (void) fprintf(cp_err, "Unknown Windows version 4.%lu. ", + (unsigned long) ver_info.dwMinorVersion); + system_info.osName = (char *) NULL; + return; + } /* end of switch over minor version for major version 4 */ + default: + (void) fprintf(cp_err, "Unknown Windows version %lu.%lu. ", + (unsigned long) ver_info.dwMajorVersion, + (unsigned long) ver_info.dwMinorVersion); + system_info.osName = (char *) NULL; + return; + }/* end of switch over major version */ + + /* Have the base version name. Now must add service pack, if any */ + if (ver_info.wServicePackMajor == 0) { /* no service pack */ + system_info.osName = tprintf("%s, Build %lu", + sz_os_name, (unsigned long) ver_info.dwBuildNumber); + } + else if (ver_info.wServicePackMinor == 0) { /* major # only */ + system_info.osName = tprintf("%s, Build %lu, Service Pack %u", + sz_os_name, (unsigned long) ver_info.dwBuildNumber, + (unsigned) ver_info.wServicePackMajor); + } + else { /* service pack has major and minor versions */ + system_info.osName = tprintf("%s, Build %lu, Service Pack %u.%u", + sz_os_name, (unsigned long) ver_info.dwBuildNumber, + (unsigned) ver_info.wServicePackMajor, + (unsigned) ver_info.wServicePackMinor); + } + + return; +} /* end of function get_os_info */ + + + +/* This function sets the number of processors field in system_info */ + static inline void get_logical_processor_count(void) + { + SYSTEM_INFO si; + GetSystemInfo(&si); + system_info.numLogicalProcessors = si.dwNumberOfProcessors; +} /* end of function get_logical_processor_count */ + + + + /* This funtion sets the field storing the number of physical processors + * in system_info */ +typedef BOOL (WINAPI *glp_t)(LOGICAL_PROCESSOR_RELATIONSHIP, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); +static void get_physical_processor_count(void) +{ + DWORD n_byte_buf = 0; + system_info.numPhysicalProcessors = 0; /* Init to 0 until found */ + + /* Get a handle to the DLL with the required function. Since the + * functdion GetModuleHandleW() is in kernel32.dll, it is safe to + * assume that kernel32.dll is already loaded. Not using + * LoadLibraryExW() simplifies error handling. */ + HMODULE lib = GetModuleHandleW(L"kernel32.dll"); + if (lib == (HMODULE) NULL) { /* Handle not obtained */ + (void) fprintf(cp_err, + "Unable to obtain a handle to kernel32.dll. " + "System code = %lu\n", + (unsigned long) GetLastError()); + return; + } + + /* Locate GetLogicalProcessorInformationEx(). It must be + * dynamically loaded since it is only present in + * Windows 7/Server 2008 R2 and later OS versions */ + FARPROC p_glp = GetProcAddress(lib, + "GetLogicalProcessorInformationEx"); + if (p_glp == (FARPROC) NULL) { /* Did not get function addr OK */ + (void) fprintf(cp_err, + "Unable to locate function " + "GetLogicalProcessorInformationEx. " + "System code = %lu\n", + (unsigned long) GetLastError()); + return; + } + + /* Find requried size. Should return FALSE/ERROR_INSUFFICIENT_BUFFER if + * working properly */ + if (((glp_t) (*p_glp))(RelationProcessorPackage, + NULL, &n_byte_buf) != 0) { + fprintf(cp_err, + "Unexpected error getting logical processor buffer size.\n"); + return; + } + + { + DWORD rc; + if ((rc = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) { + fprintf(cp_err, + "Unable to get the logical processor bufer size. " + "System code = %lu.\n", + (unsigned long) rc); + return; + } + } + + /* Allocate buffer to get the info */ + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * const buf = + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) malloc(n_byte_buf); + if (buf == (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) NULL) { + fprintf(cp_err, + "Unable to allocate a buffer of %lu bytes " + "for logical processor information.\n", + n_byte_buf); + return; + } + + /* Try again with a buffer and the size obtained before */ + { + DWORD rc; + if ((rc = ((glp_t) (*p_glp))(RelationProcessorPackage, + buf, &n_byte_buf)) == 0) { + fprintf(cp_err, + "Unable to get the logical processor info. " + "System code = %lu.\n", + (unsigned long) rc); + return; + } + } + + /* Count the number of processor packages */ + { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * p_buf_cur = buf; + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * const p_buf_end = + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) + ((char *) buf + n_byte_buf); + unsigned int n_processor_package = 0; + for ( ; p_buf_cur < p_buf_end; + p_buf_cur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) + ((char *) p_buf_cur + p_buf_cur->Size)) { + ++n_processor_package; + } + system_info.numPhysicalProcessors = n_processor_package; + } + + return; +} /* end of function get_physical_processor_count */ + + + +#else /* no Windows OS, no proc info file system */ +void set_static_system_info(void) +{ + /* Set to no data available */ + system_info.osName = (char *) NULL; + system_info.cpuModelName = (char *) NULL; + system_info.numPhysicalProcessors = 0; + system_info.numLogicalProcessors = 0; + return; +} /* end of function set_static_system_info */ #endif From 0f5fccaa12cf5898ce2a913cb0397cde34c96b1b Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 15:04:24 -0500 Subject: [PATCH 26/58] Reduced the number of configuration changes that must be made when a new version of ngspice is released. --- visualc/src/include/ngspice/config.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/visualc/src/include/ngspice/config.h b/visualc/src/include/ngspice/config.h index 506605728..31034f571 100644 --- a/visualc/src/include/ngspice/config.h +++ b/visualc/src/include/ngspice/config.h @@ -448,20 +448,23 @@ /* Name of package */ #define PACKAGE "ngspice" +/* Version number of package */ +#define VERSION "31+" //" Built on " __DATE__ " at " __TIME__ + /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "http://ngspice.sourceforge.net/bugrep.html" /* Define to the full name of this package. */ -#define PACKAGE_NAME "ngspice" +#define PACKAGE_NAME PACKAGE /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ngspice 31+" +#define PACKAGE_STRING PACKAGE " " VERSION /* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "ngspice" +#define PACKAGE_TARNAME PACKAGE /* Define to the version of this package. */ -#define PACKAGE_VERSION "31+" +#define PACKAGE_VERSION VERSION /* Define if we want predictor algorithm */ /* #undef PREDICTOR */ @@ -515,9 +518,6 @@ /* Define to 1 if your declares `struct tm'. */ /* #undef TM_IN_SYS_TIME */ -/* Version number of package */ -#define VERSION "31+" - /* Define if we want spice2 sensitivity analysis */ /* #undef WANT_SENSE2 */ From 6f8635550a7148a38462177890db6be7394e9db1 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 15:22:19 -0500 Subject: [PATCH 27/58] Fixed memory leak when empty word found. --- src/frontend/control.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/control.c b/src/frontend/control.c index 27192b768..40f0f040c 100644 --- a/src/frontend/control.c +++ b/src/frontend/control.c @@ -648,11 +648,12 @@ cp_evloop(char *string) for (;;) { freewl = wlist = getcommand(string); - if (wlist == NULL) { /* End of file or end of user input. */ + if (wlist == NULL) { /* End of file or end of user input. */ if (cend[stackp] && cend[stackp]->co_parent && !string) { cp_resetcontrol(); continue; - } else { + } + else { return (0); } } @@ -669,12 +670,14 @@ cp_evloop(char *string) } /* Just a check... */ - for (ww = wlist; ww; ww = ww->wl_next) + for (ww = wlist; ww; ww = ww->wl_next) { if (!ww->wl_word) { fprintf(cp_err, "cp_evloop: Internal Error: NULL word pointer\n"); + wl_free(wlist); continue; } + } /* Add this to the control structure list. If cend->co_type is From 9f89ab705a3e0763c6cfc5558ccbe2313bcbfc37 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 15:44:57 -0500 Subject: [PATCH 28/58] Added error recovery when invalid foreach statement found --- src/frontend/control.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/frontend/control.c b/src/frontend/control.c index 40f0f040c..e322df45f 100644 --- a/src/frontend/control.c +++ b/src/frontend/control.c @@ -761,9 +761,12 @@ cp_evloop(char *string) cend[stackp]->co_foreachvar = copy(wlist->wl_word); wlist = wlist->wl_next; - } else { + } + else { fprintf(stderr, "Error: missing foreach variable.\n"); + wl_free(wlist); + continue; } wlist = cp_doglob(wlist); cend[stackp]->co_text = wl_copy(wlist); From 8021566265f6b2ab66a4ac5cc316ac6844b2512c Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 18:18:48 -0500 Subject: [PATCH 29/58] Fixed bug with 255 card deck entered using circbyline --- src/frontend/inp.c | 143 ++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 61 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index d3729a7dc..a4e0c7d68 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -49,20 +49,24 @@ Author: 1985 Wayne A. Christopher line = NULL; \ } while(0) -static char *upper(register char *string); -static bool doedit(char *filename); + static struct card *com_options = NULL; static struct card *mc_deck = NULL; static struct card *recent_deck = NULL; + static void cktislinear(CKTcircuit *ckt, struct card *deck); -static void dotifeval(struct card *deck); -static void recifeval(struct card *pdeck); - -static wordlist *inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist *controls); - -static void eval_agauss(struct card *deck, char *fcn); -void line_free_x(struct card *deck, bool recurse); void create_circbyline(char *line); +static bool doedit(char *filename); +static void dotifeval(struct card *deck); +static void eval_agauss(struct card *deck, char *fcn); +static wordlist *inp_savecurrents(struct card *deck, struct card *options, + wordlist *wl, wordlist *controls); +void line_free_x(struct card *deck, bool recurse); +static void recifeval(struct card *pdeck); +static char *upper(register char *string); + + + //void inp_source_recent(void); //void inp_mc_free(void); //void inp_remove_recent(void); @@ -641,13 +645,14 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) else fprintf(cp_err, "Warning: misplaced .endc card\n"); } else if (commands || prefix("*#", dd->line)) { - /* assemble all commands starting with pre_ after stripping pre_, - to be executed before circuit parsing */ + /* assemble all commands starting with pre_ after stripping + * pre_, to be executed before circuit parsing */ if (ciprefix("pre_", dd->line)) { s = copy(dd->line + 4); pre_controls = wl_cons(s, pre_controls); } - /* assemble all other commands to be executed after circuit parsing */ + /* assemble all other commands to be executed after circuit + * parsing */ else { /* special control lines outside of .control section*/ if (prefix("*#", dd->line)) { @@ -673,14 +678,13 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) if (!eq(s, ".plot") && !eq(s, ".print")) inp_casefix(dd->line); if (eq(s, ".width") || - ciprefix(".four", s) || - eq(s, ".plot") || - eq(s, ".print") || - eq(s, ".save") || - eq(s, ".op") || - ciprefix(".meas", s) || - eq(s, ".tf")) - { + ciprefix(".four", s) || + eq(s, ".plot") || + eq(s, ".print") || + eq(s, ".save") || + eq(s, ".op") || + ciprefix(".meas", s) || + eq(s, ".tf")) { wl_append_word(&wl_first, &end, copy(dd->line)); if (!eq(s, ".op") && !eq(s, ".tf") && !ciprefix(".meas", s)) { @@ -782,8 +786,9 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) cstoken[0] = gettok_char(&s, '=', FALSE, FALSE); cstoken[1] = gettok_char(&s, '=', TRUE, FALSE); cstoken[2] = gettok(&s); - for (i = 3; --i >= 0;) + for (i = 3; --i >= 0; ) { wlist = wl_cons(cstoken[i], wlist); + } com_let(wlist); wl_free(wlist); } @@ -1599,9 +1604,11 @@ com_source(wordlist *wl) } -void -inp_source(char *file) +void inp_source(char *file) { + /* This wordlist is special in that nothing in it should be freed -- + * the file name word is "borrowed" from the argument to file and + * the wordlist is allocated on the stack. */ static struct wordlist wl = { NULL, NULL, NULL }; wl.wl_word = file; com_source(&wl); @@ -1612,8 +1619,7 @@ inp_source(char *file) for linear elements. If only linear elements are found, ckt->CKTisLinear is set to 1. Return immediately if a first non-linear element is found. */ -static void -cktislinear(CKTcircuit *ckt, struct card *deck) +static void cktislinear(CKTcircuit *ckt, struct card *deck) { struct card *dd; char firstchar; @@ -1646,40 +1652,51 @@ cktislinear(CKTcircuit *ckt, struct card *deck) /* global array for assembling circuit lines entered by fcn circbyline - or receiving array from external caller. Array is created once per ngspice call. - Last line of the array has to get the value NULL */ + * or receiving array from external caller. Array is created whenever + * a new deck is started. Last line of the array has to get the value NULL */ char **circarray; -void -create_circbyline(char *line) +void create_circbyline(char *line) { - static int linec = 0; - static int memlen = 256; - FILE *fp = NULL; - if (!circarray) - circarray = TMALLOC(char*, memlen); - char *p = skip_ws(line); - if (line < p) - memmove(line, p, strlen(p) + 1); - circarray[linec++] = line; - if (linec < memlen) { - if (ciprefix(".end", line) && (line[4] == '\0' || isspace_c(line[4]))) { - circarray[linec] = NULL; - inp_spsource(fp, FALSE, NULL, TRUE); - linec = 0; + static unsigned int linec = 0; + static unsigned int n_elem_alloc = 0; + + /* Ensure up to 2 cards can be added */ + if (n_elem_alloc < linec + 2) { + n_elem_alloc = n_elem_alloc == 0 ? 256 : 2 * n_elem_alloc; + circarray = TREALLOC(char *, circarray, n_elem_alloc); + } + + /* Remove any leading whitespace by shifting */ + char *p_src = skip_ws(line); + if (p_src != line) { + char *p_dst = line; + char ch_cur; + do { + ch_cur = *p_dst++ = *p_src++; } + while (ch_cur != '\0'); } - else { - memlen += memlen; - circarray = TREALLOC(char*, circarray, memlen); + + circarray[linec++] = line; /* add card to deck */ + + /* If the card added ended the deck, send it for processing and + * free the deck. The card allocations themselves will be freed + * elsewhere */ + if (ciprefix(".end", line) && (line[4] == '\0' || isspace_c(line[4]))) { + circarray[linec] = NULL; /* termiante the deck */ + inp_spsource((FILE *) NULL, FALSE, NULL, TRUE); /* process */ + tfree(circarray); /* set to empty */ + linec = 0; + n_elem_alloc = 0; } -} +} /* end of function create_circbyline */ + /* fcn called by command 'circbyline' */ -void -com_circbyline(wordlist *wl) +void com_circbyline(wordlist *wl) { /* undo the automatic wordline creation. wl_flatten allocates memory on the heap for each newline. @@ -1693,8 +1710,7 @@ com_circbyline(wordlist *wl) numparam has evaluated .if('boolean expression') to .if ( 1.000000000e+000 ) or .elseif ( 0.000000000e+000 ). Evaluation is done recursively, starting with .IF, ending with .ENDIF*/ -static void -recifeval(struct card *pdeck) +static void recifeval(struct card *pdeck) { struct card *nd; int iftrue = 0, elseiftrue = 0, elsetrue = 0, iffound = 0, elseiffound = 0, elsefound = 0; @@ -1753,8 +1769,7 @@ recifeval(struct card *pdeck) } /* Scan through all lines of the deck */ -static void -dotifeval(struct card *deck) +static void dotifeval(struct card *deck) { struct card *dd; char *dottoken; @@ -1806,8 +1821,8 @@ dotifeval(struct card *deck) to the model parameters or device instance parameters. */ -static int -inp_parse_temper(struct card *card, struct pt_temper **modtlist_p, struct pt_temper **devtlist_p) +static int inp_parse_temper(struct card *card, struct pt_temper **modtlist_p, + struct pt_temper **devtlist_p) { int error = 0; @@ -1930,8 +1945,8 @@ rem_tlist(struct pt_temper *p) } -void -inp_evaluate_temper(struct circ *circ) + +void inp_evaluate_temper(struct circ *circ) { struct pt_temper *d; double result; @@ -1944,20 +1959,24 @@ inp_evaluate_temper(struct circ *circ) com_alter(d->wl); } + /* Step through the nodes of the linked list at circ->modtlist */ for(d = circ->modtlist; d; d = d->next) { char *name = d->wl->wl_word; INPretrieve(&name, circ->ci_symtab); /* only evaluate models which have been entered into the hash table ckt->MODnameHash */ - if (ft_sim->findModel (circ->ci_ckt, name) == NULL) + if (ft_sim->findModel (circ->ci_ckt, name) == NULL) { continue; + } + IFeval((IFparseTree *) d->pt, 1e-12, &result, NULL, NULL); if (d->wlend->wl_word) tfree(d->wlend->wl_word); d->wlend->wl_word = tprintf("%g", result); com_altermod(d->wl); } -} +} /* end of funtion inp_evaluate_temper */ + /* @@ -1998,10 +2017,12 @@ inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist break; /* if not found, then add '.save all' */ - if (!p) + if (!p) { p = wl_cons(copy(".save all"), NULL); - else + } + else { p = NULL; + } /* Scan the deck for devices with their terminals. * We currently serve bipolars, resistors, MOS1, capacitors, inductors, From 10415875f46c46d91eb0b35e436cfa16aab0cd8f Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 10 Dec 2019 19:47:00 -0500 Subject: [PATCH 30/58] Fixed "phantom vector" issue with plots and crash when plots are resized. See bugs #419 and #423 for details. Also several parameters were changed from char * to const char * in the plotting functions. --- src/frontend/plotting/gnuplot.c | 41 +- src/frontend/plotting/gnuplot.h | 16 +- src/frontend/plotting/graf.c | 199 +++--- src/frontend/plotting/graf.h | 24 +- src/frontend/plotting/plotit.c | 1030 ++++++++++++++++++------------- src/frontend/plotting/plotit.h | 2 +- src/include/ngspice/graph.h | 2 +- 7 files changed, 785 insertions(+), 529 deletions(-) diff --git a/src/frontend/plotting/gnuplot.c b/src/frontend/plotting/gnuplot.c index 57811e243..5a0ff5015 100644 --- a/src/frontend/plotting/gnuplot.c +++ b/src/frontend/plotting/gnuplot.c @@ -47,8 +47,11 @@ quote_gnuplot_string(FILE *stream, char *s) } -void -ft_gnuplot(double *xlims, double *ylims, char *filename, char *title, char *xlabel, char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, struct dvec *vecs) +void ft_gnuplot(double *xlims, double *ylims, + const char *filename, const char *title, + const char *xlabel, const char *ylabel, + GRIDTYPE gridtype, PLOTTYPE plottype, + struct dvec *vecs) { FILE *file, *file_data; struct dvec *v, *scale = NULL; @@ -82,20 +85,27 @@ ft_gnuplot(double *xlims, double *ylims, char *filename, char *title, char *xlab extrange = 0.05 * (ylims[1] - ylims[0]); - if (!cp_getvar("gnuplot_terminal", CP_STRING, terminal, sizeof(terminal))) { + if (!cp_getvar("gnuplot_terminal", CP_STRING, + terminal, sizeof(terminal))) { terminal_type = 1; - } else { + } + else { terminal_type = 1; - if (cieq(terminal,"png")) + if (cieq(terminal,"png")) { terminal_type = 2; - if (cieq(terminal,"png/quit")) + } + else if (cieq(terminal,"png/quit")) { terminal_type = 3; - if (cieq(terminal, "eps")) + } + else if (cieq(terminal, "eps")) { terminal_type = 4; - if (cieq(terminal, "eps/quit")) + } + else if (cieq(terminal, "eps/quit")) { terminal_type = 5; - if (cieq(terminal, "xterm")) + } + else if (cieq(terminal, "xterm")) { terminal_type = 6; + } } if (!cp_getvar("xbrushwidth", CP_NUM, &linewidth, 0)) @@ -344,10 +354,12 @@ ft_gnuplot(double *xlims, double *ylims, char *filename, char *title, char *xlab fprintf(cp_out, "writing plot to file %s.eps\n", filename); (void) sprintf(buf, "gnuplot %s", filename_plt); } - else if (terminal_type == 6) + else if (terminal_type == 6) { (void) sprintf(buf, "xterm -e gnuplot %s - &", filename_plt); - else + } + else { (void) sprintf(buf, "gnuplot -p %s - &", filename_plt); + } #endif err = system(buf); @@ -379,8 +391,11 @@ ft_gnuplot(double *xlims, double *ylims, char *filename, char *title, char *xlab if scale vectors are of same length (there is little risk here!). Width of numbers printed is set by option 'numdgt'. */ -void -ft_writesimple(double *xlims, double *ylims, char *filename, char *title, char *xlabel, char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, struct dvec *vecs) +void ft_writesimple(double *xlims, double *ylims, + const char *filename, const char *title, + const char *xlabel, const char *ylabel, + GRIDTYPE gridtype, PLOTTYPE plottype, + struct dvec *vecs) { FILE *file_data; struct dvec *v; diff --git a/src/frontend/plotting/gnuplot.h b/src/frontend/plotting/gnuplot.h index 533826a35..f4ebbabea 100644 --- a/src/frontend/plotting/gnuplot.h +++ b/src/frontend/plotting/gnuplot.h @@ -6,13 +6,17 @@ #ifndef ngspice_GNUPLOT_H #define ngspice_GNUPLOT_H -void ft_gnuplot(double *xlims, double *ylims, char *filename, char *title, - char *xlabel, char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, - struct dvec *vecs); +void ft_gnuplot(double *xlims, double *ylims, + const char *filename, const char *title, + const char *xlabel, const char *ylabel, + GRIDTYPE gridtype, PLOTTYPE plottype, + struct dvec *vecs); -void ft_writesimple(double *xlims, double *ylims, char *filename, char *title, - char *xlabel, char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, - struct dvec *vecs); +void ft_writesimple(double *xlims, double *ylims, + const char *filename, const char *title, + const char *xlabel, const char *ylabel, + GRIDTYPE gridtype, PLOTTYPE plottype, + struct dvec *vecs); #endif diff --git a/src/frontend/plotting/graf.c b/src/frontend/plotting/graf.c index 1f907b05f..d8bf48ef2 100644 --- a/src/frontend/plotting/graf.c +++ b/src/frontend/plotting/graf.c @@ -68,27 +68,28 @@ static char *ticlist = ticbuf; * */ -int -gr_init(double *xlims, double *ylims, /* The size of the screen. */ - char *xname, char *plotname, /* What to label things. */ - char *hcopy, /* The raster file. */ - int nplots, /* How many plots there will be. */ +int gr_init(double *xlims, double *ylims, /* The size of the screen. */ + const char *xname, + const char *plotname, /* What to label things. */ + const char *hcopy, /* The raster file. */ + int nplots, /* How many plots there will be. */ double xdelta, double ydelta, /* Line increments for the scale. */ - GRIDTYPE gridtype, /* The grid type */ - PLOTTYPE plottype, /* and the plot type. */ - char *xlabel, char *ylabel, /* Labels for axes. */ - int xtype, int ytype, /* The types of the data graphed. */ - char *pname, - char *commandline) /* For xi_zoomdata() */ + GRIDTYPE gridtype, /* The grid type */ + PLOTTYPE plottype, /* and the plot type. */ + const char *xlabel, + const char *ylabel, /* Labels for axes. */ + int xtype, int ytype, /* The types of the data graphed. */ + const char *pname, + const char *commandline) /* For xi_zoomdata() */ { GRAPH *graph; wordlist *wl; - char *comb_title; NG_IGNORE(nplots); - if ((graph = NewGraph()) == NULL) - return (FALSE); + if ((graph = NewGraph()) == (GRAPH *) NULL) { + return FALSE; + } /* The global currentgraph will always be the current graph. @@ -98,8 +99,10 @@ gr_init(double *xlims, double *ylims, /* The size of the screen. */ graph->onevalue = (xname ? FALSE : TRUE); /* communicate filename to plot 5 driver */ - if (hcopy) - graph->devdep = hcopy; + if (hcopy) { + graph->devdep = copy(hcopy); + graph->n_byte_devdep = strlen(hcopy) + 1; + } cur.plotno = 0; @@ -108,23 +111,26 @@ gr_init(double *xlims, double *ylims, /* The size of the screen. */ (void) strcpy(pointchars, DEFPOINTCHARS); if (!cp_getvar("ticmarks", CP_NUM, &graph->ticmarks, 0)) { - if (cp_getvar("ticmarks", CP_BOOL, NULL, 0)) + if (cp_getvar("ticmarks", CP_BOOL, NULL, 0)) { graph->ticmarks = 10; - else + } + else { graph->ticmarks = 0; + } } if (cp_getvar("ticlist", CP_LIST, ticlist, 0)) { wl = vareval("ticlist"); ticlist = wl_flatten(wl); graph->ticdata = readtics(ticlist); - } else { + } + else { graph->ticdata = NULL; } if (!xlims || !ylims) { internalerror("gr_init: no range specified"); - return (FALSE); + return FALSE; } /* save upper and lower limits */ @@ -134,13 +140,14 @@ gr_init(double *xlims, double *ylims, /* The size of the screen. */ graph->data.ymax = ylims[1]; /* get title into plot window */ - if (!pname) + if (!pname) { pname = "(unknown)"; - if (!plotname) + } + if (!plotname) { plotname = "(unknown)"; + } - comb_title = tprintf("%s: %s", pname, plotname); - graph->plotname = comb_title; + graph->plotname = tprintf("%s: %s", pname, plotname); /* note: have enum here or some better convention */ if (NewViewport(graph) == 1) { @@ -167,47 +174,60 @@ gr_init(double *xlims, double *ylims, /* The size of the screen. */ graph->grid.xsized = 0; if (!graph->onevalue) { - if (xlabel) - graph->grid.xlabel = xlabel; - else - graph->grid.xlabel = xname; + if (xlabel) { + graph->grid.xlabel = copy(xlabel); + } + else { + graph->grid.xlabel = copy(xname); + } - if (ylabel) - graph->grid.ylabel = ylabel; - } else { - if (xlabel) - graph->grid.xlabel = xlabel; - else - graph->grid.xlabel = "real"; + if (ylabel) { + graph->grid.ylabel = copy(ylabel); + } + else { + graph->grid.ylabel = (char *) NULL; + } + } + else { + if (xlabel) { + graph->grid.xlabel = copy(xlabel); + } + else { + graph->grid.xlabel = copy("real"); + } - if (ylabel) - graph->grid.ylabel = ylabel; - else - graph->grid.ylabel = "imag"; + if (ylabel) { + graph->grid.ylabel = copy(ylabel); + } + else { + graph->grid.ylabel = copy("imag"); + } } gr_resize_internal(graph); gr_redrawgrid(graph); /* Set up colors and line styles. */ - if (dispdev->numlinestyles == 1) + if (dispdev->numlinestyles == 1) { cur.linestyle = 0; /* Use the same one all the time. */ - else + } + else { cur.linestyle = 1; + } /* XXX Special exception for SMITH */ if (dispdev->numcolors > 2 && - (graph->grid.gridtype == GRID_SMITH || - graph->grid.gridtype == GRID_SMITHGRID)) - { + (graph->grid.gridtype == GRID_SMITH || + graph->grid.gridtype == GRID_SMITHGRID)) { cur.color = 3; - } else { + } + else { cur.color = 1; } graph->commandline = copy(commandline); - return (TRUE); + return TRUE; } @@ -315,8 +335,7 @@ gr_point(struct dvec *dv, } -static void -gr_start_internal(struct dvec *dv, bool copyvec) +static void gr_start_internal(struct dvec *dv, bool copyvec) { struct dveclist *link; @@ -325,34 +344,41 @@ gr_start_internal(struct dvec *dv, bool copyvec) if (dv->v_type == SV_POLE) { dv->v_linestyle = 'x'; return; - } else if (dv->v_type == SV_ZERO) { + } + else if (dv->v_type == SV_ZERO) { dv->v_linestyle = 'o'; return; } /* Find a (hopefully) new line style and color. */ if (currentgraph->plottype == PLOT_POINT) { - if (pointchars[cur.linestyle - 1]) + if (pointchars[cur.linestyle - 1]) { cur.linestyle++; - else + } + else { cur.linestyle = 2; - } else if ((cur.linestyle > 0) && (++cur.linestyle == dispdev->numlinestyles)) { + } + } + else if ((cur.linestyle > 0) && + (++cur.linestyle == dispdev->numlinestyles)) { cur.linestyle = 2; } if ((cur.color > 0) && (++cur.color == dispdev->numcolors)) cur.color = (((currentgraph->grid.gridtype == GRID_SMITH || - currentgraph->grid.gridtype == GRID_SMITHGRID) && - (dispdev->numcolors > 3)) ? 4 : 2); + currentgraph->grid.gridtype == GRID_SMITHGRID) && + (dispdev->numcolors > 3)) ? 4 : 2); - if (currentgraph->plottype == PLOT_POINT) + if (currentgraph->plottype == PLOT_POINT) { dv->v_linestyle = pointchars[cur.linestyle - 2]; - else + } + else { dv->v_linestyle = cur.linestyle; + } dv->v_color = cur.color; - /* save the data so we can refresh */ + /* Save the data so we can refresh */ link = TMALLOC(struct dveclist, 1); link->next = currentgraph->plotdata; @@ -368,17 +394,36 @@ gr_start_internal(struct dvec *dv, bool copyvec) currentgraph->plotdata = link; + /* Add the scale vector to the list of vectors associated with the plot + * and use the copy instead of the original scale vector */ + link = TMALLOC(struct dveclist, 1); + link->next = currentgraph->plotdata; + + if (copyvec) { + link->vector = vec_copy(dv->v_scale); + link->vector->v_flags |= VF_PERMANENT; + link->next->vector->v_scale = link->vector; + } + else { + link->vector = dv->v_scale; + } + + /* Make the new vector the start of the list of vectors */ + currentgraph->plotdata = link; + + /* Put the legend entry on the screen. */ drawlegend(currentgraph, cur.plotno++, dv); -} +} /* end of function gr_start_internal */ -/* start one plot of a graph */ -void -gr_start(struct dvec *dv) + +/* Start one plot of a graph */ +void gr_start(struct dvec *dv) { gr_start_internal(dv, TRUE); -} +} /* end of function gr_start */ + /* make sure the linestyles in this graph don't exceed the number of @@ -400,30 +445,28 @@ gr_relinestyle(GRAPH *graph) /* PN static */ -void -drawlegend(GRAPH *graph, int plotno, struct dvec *dv) +void drawlegend(GRAPH *graph, int plotno, struct dvec *dv) { - int x, y, i; - char buf[16]; - - x = ((plotno % 2) ? graph->viewportxoff : - ((graph->viewport.width) / 2)); - y = graph->absolute.height - graph->fontheight + const int x = (plotno % 2) ? + graph->viewportxoff : (graph->viewport.width / 2); + const int x_base = x + graph->viewport.width / 20; + const int y = graph->absolute.height - graph->fontheight - ((plotno + 2) / 2) * (graph->fontheight); - i = y + graph->fontheight / 2 + 1; + const int i = y + graph->fontheight / 2 + 1; SetColor(dv->v_color); if (graph->plottype == PLOT_POINT) { + char buf[16]; (void) sprintf(buf, "%c : ", dv->v_linestyle); - DevDrawText(buf, x + graph->viewport.width / 20 - - 3 * graph->fontwidth, y, 0); - } else { + DevDrawText(buf, x_base - 3 * graph->fontwidth, y, 0); + } + else { SetLinestyle(dv->v_linestyle); - DevDrawLine(x, i, x + graph->viewport.width / 20, i); + DevDrawLine(x, i, x_base, i); } SetColor(1); - DevDrawText(dv->v_name, x + graph->viewport.width / 20 - + graph->fontwidth, y, 0); -} + DevDrawText(dv->v_name, x_base + graph->fontwidth, y, 0); +} /* end of function drawlegend */ + /* end one plot of a graph */ diff --git a/src/frontend/plotting/graf.h b/src/frontend/plotting/graf.h index 247787bc5..9d73c5cab 100644 --- a/src/frontend/plotting/graf.h +++ b/src/frontend/plotting/graf.h @@ -9,18 +9,20 @@ #include "ngspice/graph.h" int gr_init(double *xlims, double *ylims, - char *xname, char *plotname, - char *hcopy, - int nplots, - double xdelta, double ydelta, - GRIDTYPE gridtype, - PLOTTYPE plottype, - char *xlabel, char *ylabel, - int xtype, int ytype, - char *pname, char *commandline); + const char *xname, + const char *plotname, + const char *hcopy, + int nplots, + double xdelta, double ydelta, + GRIDTYPE gridtype, + PLOTTYPE plottype, + const char *xlabel, + const char *ylabel, /* Labels for axes. */ + int xtype, int ytype, + const char *pname, const char *commandline); void gr_point(struct dvec *dv, - double newx, double newy, - double oldx, double oldy, int np); + double newx, double newy, + double oldx, double oldy, int np); void gr_start(struct dvec *dv); void gr_relinestyle(GRAPH *graph); void drawlegend(GRAPH *graph, int plotno, struct dvec *dv); diff --git a/src/frontend/plotting/plotit.c b/src/frontend/plotting/plotit.c index 6daa82ba1..ba3c1039d 100644 --- a/src/frontend/plotting/plotit.c +++ b/src/frontend/plotting/plotit.c @@ -1,5 +1,6 @@ #include "ngspice/ngspice.h" #include "ngspice/bool.h" +#include "ngspice/dstring.h" #include "ngspice/wordlist.h" #include "ngspice/graph.h" #include "ngspice/cpdefs.h" @@ -10,6 +11,7 @@ #include #include "plotit.h" +#include "points.h" #include "agraf.h" #include "xgraph.h" #include "gnuplot.h" @@ -22,30 +24,52 @@ static bool sameflag; #endif +static struct dvec *vec_self(struct dvec *v); +static struct dvec *vec_scale(struct dvec *v); +static void find_axis_limits(double *lim, bool oneval, bool f_real, + struct dvec *vecs, + struct dvec *(*p_get_axis_dvec)(struct dvec *dvec), + double *lims); + + /* This routine gets parameters from the command line, which are of * the form "name number ..." It returns a pointer to the parameter - * values. */ - -static double * -getlims(wordlist *wl, char *name, int number) + * values. + * + * Parameters + * wl: Wordlist prefixed with dummy node from which the parameter value or + * values is to be extracted. On return, the nodes corresponding to the + * name of the parameter and the following value nodes are removed. + * name: Name of parameter + * number: number of values for the parameter + * + * Return values + * Allocated list of values extracted from the wordlist + * + * Remarks + * The dummy node at the front of wl guarantees that removing the nodes + * for this parameter will not change the first node of the wordlist. + */ +static double *getlims(wordlist *wl, const char *name, int number) { - wordlist *beg, *wk; + wordlist *wk; int n; - if (number < 1) - return NULL; + if (number < 1) { /* Parameter takes no argument */ + return (double *) NULL; + } - beg = wl_find(name, wl->wl_next); + /* Locate parameter name in the wordlist */ + wordlist * const beg = wl_find(name, wl->wl_next); + if (!beg) { /* not foumd */ + return (double *) NULL; + } - if (!beg) - return NULL; - - wk = beg->wl_next; + wk = beg->wl_next; /* Start of values for parameter */ double * const d = TMALLOC(double, number); /* alloc for returned vals */ - for (n = 0; n < number; n++) { - + for (n = 0; n < number; n++) { /* loop over values */ char *ss; if (!wk) { @@ -74,53 +98,57 @@ getlims(wordlist *wl, char *name, int number) /* Extend a data vector to length by replicating the last element, or - * truncate it if it is too long. */ - -static void -xtend(struct dvec *v, int length) + * truncate it if it is too long. If the vector is empty, it is + * extended with NAN */ +static void xtend(struct dvec *v, int length) { int i; - if (v->v_length == length) + if (v->v_length == length) { /* no change required */ return; + } - if (v->v_length > length) { + if (v->v_length > length) { /* too long */ dvec_trunc(v, length); return; } + /* Else must be extended */ i = v->v_length; dvec_realloc(v, length, NULL); if (isreal(v)) { double d = NAN; - if (i > 0) + if (i > 0) { /* At least one value */ d = v->v_realdata[i - 1]; - while (i < length) + } + while (i < length) { /* Fill new elements at end */ v->v_realdata[i++] = d; - } else { - ngcomplex_t c = {NAN, NAN}; - if (i > 0) - c = v->v_compdata[i - 1]; - while (i < length) - v->v_compdata[i++] = c; + } } -} + else { + ngcomplex_t c = {NAN, NAN}; + if (i > 0) { /* At least one value */ + c = v->v_compdata[i - 1]; + } + while (i < length) { /* Fill new elements at end */ + v->v_compdata[i++] = c; + } + } +} /* end of function xtend */ + /* Collapse every *xcomp elements into one, and use only the elements - * between xind[0] and xind[1]. - */ - -static void -compress(struct dvec *d, double *xcomp, double *xind) + * between xind[0] and xind[1]. Decimate would be a better description + * than compress */ +static void compress(struct dvec *d, double *xcomp, double *xind) { - int cfac, ihi, ilo, newlen, i; - if (xind) { - ilo = (int) xind[0]; - ihi = (int) xind[1]; + int newlen; + const int ilo = (int) xind[0]; + const int ihi = (int) xind[1]; if ((ihi >= ilo) && (ilo > 0) && (ilo < d->v_length) && (ihi > 1) && (ihi <= d->v_length)) { newlen = ihi - ilo; @@ -137,71 +165,102 @@ compress(struct dvec *d, double *xcomp, double *xind) } if (xcomp) { - cfac = (int) *xcomp; + const int cfac = (int) *xcomp; if ((cfac > 1) && (cfac < d->v_length)) { - for (i = 0; i * cfac < d->v_length; i++) - if (isreal(d)) - d->v_realdata[i] = d->v_realdata[i * cfac]; - else - d->v_compdata[i] = d->v_compdata[i * cfac]; + int i, j; + const int n = d->v_length; + for (i = 0, j = 0; j < n; i++, j += cfac) { + if (isreal(d)) { + d->v_realdata[i] = d->v_realdata[j]; + } + else { + d->v_compdata[i] = d->v_compdata[j]; + } + } dvec_trunc(d, i); } } -} +} /* end of function compress */ -/* Check for and remove a one-word keyword. */ -static bool -getflag(wordlist *wl, char *name) +/* Check for and remove a one-word keyword (without an argument). */ +static bool getflag(wordlist *wl, const char *name) { wl = wl_find(name, wl->wl_next); - if (!wl) + if (!wl) { return FALSE; + } wl_delete_slice(wl, wl->wl_next); return TRUE; -} +} /* end of function getflag */ -/* Return a parameter of the form "xlabel foo" */ -static char * -getword(wordlist *wl, char *name) +/* Return a copy of the value of parameter and deletes the keyword and + * value nodes in the wordlist. The search for the keyword begins after + * the node wl. (This behavior is due to a dummy node being added + * to the front of the wordlist.) + * + * Parameters + * wl: wordlist to process + * sz_keyword: keyword to locate + * + * Return values + * NULL: The keyword was not found or its value was missing + * allocation consisting of the value node as a string. + * + * Example + * wl= "a" <-> "xlabel" <-> "voltage" <-> "b" + * sz_keyword = "xlabel" + * On return, + * wl= "a" <-> "b" + * return value = "voltage" +*/ +static char *getword(wordlist *wl, const char *sz_keyword) { - wordlist *beg; - char *s; + wordlist *kw = wl_find(sz_keyword, wl->wl_next); - beg = wl_find(name, wl->wl_next); - - if (!beg) - return NULL; - - if (!beg->wl_next) { - fprintf(cp_err, - "Syntax error: looking for plot keyword at \"%s\".\n", name); - return NULL; + if (kw == (wordlist *) NULL) { /* not found */ + return (char *) NULL; } - s = copy(beg->wl_next->wl_word); + wordlist *value = kw->wl_next; /* value follows keyword */ + if (value == (wordlist *) NULL) { /* no value for keyword */ + fprintf(cp_err, + "Syntax error: missing value for plot keyword \"%s\".\n", + sz_keyword); + return (char *) NULL; + } - wl_delete_slice(beg, beg->wl_next->wl_next); + char *sz_ret = copy(value->wl_word); /* save value */ + wl_delete_slice(kw, value->wl_next); /* remove kw and val nodes */ + + return sz_ret; +} /* end of funtion getword */ - return s; -} /* The common routine for all plotting commands. This does hardcopy - * and graphics plotting. */ - -bool -plotit(wordlist *wl, char *hcopy, char *devname) + * and graphics plotting. + * + * Parameters + * wl: plotting command + * hcopy: File used for plotting + * devname: "Device" for plotting, e.g. Gnuplot + */ +bool plotit(wordlist *wl, const char *hcopy, const char *devname) { + if (!wl) { /* no wordlist -> cannot plot */ + return FALSE; + } + /* All these things are static so that "samep" will work. */ static double *xcompress = NULL, *xindices = NULL; - static double *xlim = NULL, *ylim = NULL, *xynull; + static double *xlim = NULL, *ylim = NULL; static double *xdelta = NULL, *ydelta = NULL; static char *xlabel = NULL, *ylabel = NULL, *title = NULL; static bool nointerp = FALSE; @@ -209,112 +268,129 @@ plotit(wordlist *wl, char *hcopy, char *devname) static PLOTTYPE ptype = PLOT_LIN; bool gfound = FALSE, pfound = FALSE, oneval = FALSE; - double *dd, ylims[2], xlims[2]; + double ylims[2], xlims[2]; struct pnode *pn, *names; struct dvec *d = NULL, *vecs = NULL, *lv, *lastvs = NULL; char *xn; int i, y_type, xt; - wordlist *wwl, *tail; - char *cline = NULL, buf[BSIZE_SP], *pname; + wordlist *wwl; char *nxlabel = NULL, *nylabel = NULL, *ntitle = NULL; double tstep, tstart, tstop, ttime; + /* Save start of vectors on entry for cleaning up junk left behind + * by ft_getpnames() */ + struct dvec *dv_head_orig = + plot_cur ? plot_cur->pl_dvecs : (struct dvec *) NULL; + + /* Dstring for building plot command */ + DS_CREATE(ds_cline, 200); + int rc_ds = 0; /* return code from dstring operations */ + /* return value, error by default */ bool rtn = FALSE; - if (!wl) - return rtn; - - /* - * we need a preceding element here, - * and we will destructively modify the wordlist in stupid ways, - * thus lets make our own copy which fits our purpose - */ + /* Create a copy of the input wordlist with a dummy node at the + * beginning of the list. The dummy node is used to ensure that + * the keyword and value nodes and the labels and title nodes + * that are removed are not at the beginning of the list. + * As a result, the head of the list remains unchanged while + * the undesired nodes are being removed. */ wl = wl_cons(NULL, wl_copy(wl)); /* First get the command line, without the limits. - Wii be used for zoomed windows */ + Wii be used for zoomed windows. Besides returning the values, + which are not wanted here, the getlims calls remove the + nodes for the keyword and its value from wwl. */ wwl = wl_copy(wl); - xynull = getlims(wwl, "xl", 2); - tfree(xynull); - xynull = getlims(wwl, "xlimit", 2); - tfree(xynull); - xynull = getlims(wwl, "yl", 2); - tfree(xynull); - xynull = getlims(wwl, "ylimit", 2); - tfree(xynull); - /* remove tile, xlabel, ylabel */ + txfree(getlims(wwl, "xl", 2)); + txfree(getlims(wwl, "xlimit", 2)); + txfree(getlims(wwl, "yl", 2)); + txfree(getlims(wwl, "ylimit", 2)); + + /* Save title, xlabel and ylabel for use later and remove the + * corresponding nodes from the wordlist */ nxlabel = getword(wwl, "xlabel"); nylabel = getword(wwl, "ylabel"); ntitle = getword(wwl, "title"); - pname = wl_flatten(wwl->wl_next); - tail = wl_cons(tprintf("plot %s", pname), NULL); - tfree(pname); + + /* Build the plot command. This construction had been done with wordlists + * and reversing, and flattening, but it is clearer as well as much more + * efficient to use a dstring. */ + rc_ds |= ds_cat_printf(&ds_cline, "plot %s", wl_flatten(wwl->wl_next)); wl_free(wwl); - /* add title, xlabel or ylabel, if available, with quotes '' */ + /* Add title, xlabel or ylabel, if available, with quotes ''. */ if (nxlabel) { - tail = wl_cons(tprintf("xlabel '%s'", nxlabel), tail); + rc_ds |= ds_cat_printf(&ds_cline, " xlabel '%s'", nxlabel); tfree(nxlabel); } if (nylabel) { - tail = wl_cons(tprintf("ylabel '%s'", nylabel), tail); - tfree(nylabel); + rc_ds |= ds_cat_printf(&ds_cline, " ylabel '%s'", nylabel); + tfree(nxlabel); } if (ntitle) { - tail = wl_cons(tprintf("title '%s'", ntitle), tail); + rc_ds |= ds_cat_printf(&ds_cline, " title '%s'", ntitle); tfree(ntitle); } + if (rc_ds != 0) { + fprintf(cp_err, "Unable to build plot command line.\n"); + goto quit1; + } - tail = wl_reverse(tail); - cline = wl_flatten(tail); - wl_free(tail); /* Now extract all the parameters. */ - sameflag = getflag(wl, "samep"); if (!sameflag || !xlim) { xlim = getlims(wl, "xl", 2); - if (!xlim) + if (!xlim) { xlim = getlims(wl, "xlimit", 2); - } else { + } + } + else { txfree(getlims(wl, "xl", 2)); txfree(getlims(wl, "xlimit", 2)); } if (!sameflag || !ylim) { ylim = getlims(wl, "yl", 2); - if (!ylim) + if (!ylim) { ylim = getlims(wl, "ylimit", 2); - } else { + } + } + else { txfree(getlims(wl, "yl", 2)); txfree(getlims(wl, "ylimit", 2)); } if (!sameflag || !xcompress) { xcompress = getlims(wl, "xcompress", 1); - if (!xcompress) + if (!xcompress) { xcompress = getlims(wl, "xcomp", 1); - } else { + } + } + else { txfree(getlims(wl, "xcompress", 1)); txfree(getlims(wl, "xcomp", 1)); } if (!sameflag || !xindices) { xindices = getlims(wl, "xindices", 2); - if (!xindices) + if (!xindices) { xindices = getlims(wl, "xind", 2); - } else { + } + } + else { txfree(getlims(wl, "xindices", 2)); txfree(getlims(wl, "xind", 2)); } if (!sameflag || !xdelta) { xdelta = getlims(wl, "xdelta", 1); - if (!xdelta) + if (!xdelta) { xdelta = getlims(wl, "xdel", 1); + } } else { txfree(getlims(wl, "xdelta", 1)); txfree(getlims(wl, "xdel", 1)); @@ -322,9 +398,11 @@ plotit(wordlist *wl, char *hcopy, char *devname) if (!sameflag || !ydelta) { ydelta = getlims(wl, "ydelta", 1); - if (!ydelta) + if (!ydelta) { ydelta = getlims(wl, "ydel", 1); - } else { + } + } + else { txfree(getlims(wl, "ydelta", 1)); txfree(getlims(wl, "ydel", 1)); } @@ -334,7 +412,9 @@ plotit(wordlist *wl, char *hcopy, char *devname) */ if (getflag(wl, "lingrid")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); + fprintf(cp_err, + "Warning: too many grid types given. " + "\"lingrid\" is ignored.\n"); } else { gtype = GRID_LIN; gfound = TRUE; @@ -342,24 +422,33 @@ plotit(wordlist *wl, char *hcopy, char *devname) } if (getflag(wl, "loglog")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"loglog\" is ignored.\n"); + } + else { gtype = GRID_LOGLOG; gfound = TRUE; } } if (getflag(wl, "nogrid")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"nogrid\" is ignored.\n"); + } + else { gtype = GRID_NONE; gfound = TRUE; } } if (getflag(wl, "linear")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"linear\" is ignored.\n"); + } + else { gtype = GRID_LIN; gfound = TRUE; } @@ -368,151 +457,208 @@ plotit(wordlist *wl, char *hcopy, char *devname) if (gfound) { if (gtype == GRID_YLOG) gtype = GRID_LOGLOG; - else - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"xlog\" is ignored.\n"); + } + } + else { gtype = GRID_XLOG; gfound = TRUE; } } if (getflag(wl, "ylog")) { if (gfound) { - if (gtype == GRID_XLOG) + if (gtype == GRID_XLOG) { gtype = GRID_LOGLOG; - else - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + } + else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"xlog\" is ignored.\n"); + } + } + else { gtype = GRID_YLOG; gfound = TRUE; } } if (getflag(wl, "polar")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"polar\" is ignored.\n"); + } + else { gtype = GRID_POLAR; gfound = TRUE; } } if (getflag(wl, "smith")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"smith\" is ignored.\n"); + } + else { gtype = GRID_SMITH; gfound = TRUE; } } if (getflag(wl, "smithgrid")) { if (gfound) { - fprintf(cp_err, "Warning: too many grid types given\n"); - } else { + fprintf(cp_err, + "Warning: too many grid types given. " + "\"smithgrid\" is ignored.\n"); + } + else { gtype = GRID_SMITHGRID; gfound = TRUE; } } if (!sameflag && !gfound) { + char buf[BSIZE_SP]; if (cp_getvar("gridstyle", CP_STRING, buf, sizeof(buf))) { - if (eq(buf, "lingrid")) + if (eq(buf, "lingrid")) { gtype = GRID_LIN; - else if (eq(buf, "loglog")) + } + else if (eq(buf, "loglog")) { gtype = GRID_LOGLOG; - else if (eq(buf, "xlog")) + } + else if (eq(buf, "xlog")) { gtype = GRID_XLOG; - else if (eq(buf, "ylog")) + } + else if (eq(buf, "ylog")) { gtype = GRID_YLOG; - else if (eq(buf, "smith")) + } + else if (eq(buf, "smith")) { gtype = GRID_SMITH; - else if (eq(buf, "smithgrid")) + } + else if (eq(buf, "smithgrid")) { gtype = GRID_SMITHGRID; - else if (eq(buf, "polar")) + } + else if (eq(buf, "polar")) { gtype = GRID_POLAR; - else if (eq(buf, "nogrid")) + } + else if (eq(buf, "nogrid")) { gtype = GRID_NONE; + } else { - fprintf(cp_err, "Warning: strange grid type %s\n", buf); + fprintf(cp_err, + "Warning: unknown grid type \"%s\" is ignored. " + "The grid type will default to linear.\n", buf); gtype = GRID_LIN; } gfound = TRUE; - } else { + } + else { gtype = GRID_LIN; } } /* Now get the point type. */ - if (getflag(wl, "linplot")) { if (pfound) { - fprintf(cp_err, "Warning: too many plot types given\n"); - } else { + fprintf(cp_err, + "Warning: too many plot types given. " + "\"linplot\" is ignored.\n"); + } + else { ptype = PLOT_LIN; pfound = TRUE; } } if (getflag(wl, "noretraceplot")) { if (pfound) { - fprintf(cp_err, "Warning: too many plot types given\n"); - } else { + fprintf(cp_err, + "Warning: too many plot types given. " + "\"noretraceplot\" is ignored.\n"); + } + else { ptype = PLOT_MONOLIN; pfound = TRUE; } } if (getflag(wl, "combplot")) { if (pfound) { - fprintf(cp_err, "Warning: too many plot types given\n"); - } else { + fprintf(cp_err, + "Warning: too many plot types given. " + "\"combplot\" is ignored.\n"); + } + else { ptype = PLOT_COMB; pfound = TRUE; } } if (getflag(wl, "pointplot")) { if (pfound) { - fprintf(cp_err, "Warning: too many plot types given\n"); - } else { + fprintf(cp_err, + "Warning: too many plot types given. " + "\"pointplot\" is ignored.\n"); + } + else { ptype = PLOT_POINT; pfound = TRUE; } } if (!sameflag && !pfound) { + char buf[BSIZE_SP]; if (cp_getvar("plotstyle", CP_STRING, buf, sizeof(buf))) { - if (eq(buf, "linplot")) + if (eq(buf, "linplot")) { ptype = PLOT_LIN; - else if (eq(buf, "noretraceplot")) + } + else if (eq(buf, "noretraceplot")) { ptype = PLOT_MONOLIN; - else if (eq(buf, "combplot")) + } + else if (eq(buf, "combplot")) { ptype = PLOT_COMB; - else if (eq(buf, "pointplot")) + } + else if (eq(buf, "pointplot")) { ptype = PLOT_POINT; + } else { - fprintf(cp_err, "Warning: strange plot type %s\n", buf); + fprintf(cp_err, + "Warning: strange plot type \"%s\" is ignored. " + "The plot type will default to linear.\n", buf); ptype = PLOT_LIN; } pfound = TRUE; - } else { + } + else { ptype = PLOT_LIN; } } - if (!sameflag || !xlabel) + if (!sameflag || !xlabel) { xlabel = getword(wl, "xlabel"); - else + } + else { txfree(getword(wl, "xlabel")); + } - if (!sameflag || !ylabel) + if (!sameflag || !ylabel) { ylabel = getword(wl, "ylabel"); - else + } + else { txfree(getword(wl, "ylabel")); + } - if (!sameflag || !title) + if (!sameflag || !title) { title = getword(wl, "title"); - else + } + else { txfree(getword(wl, "title")); + } - if (!sameflag) + if (!sameflag) { nointerp = getflag(wl, "nointerp"); - else if (getflag(wl, "nointerp")) + } + else if (getflag(wl, "nointerp")) { nointerp = TRUE; + } if (!wl->wl_next) { fprintf(cp_err, "Error: no vectors given\n"); @@ -526,128 +672,166 @@ plotit(wordlist *wl, char *hcopy, char *devname) * vectors with the name "vs"... This is a sort of a gross hack, * since we have to check for 0-length vectors ourselves after * evaulating the pnodes... + * + * Note: Evaluating the wordlist starting at wl->wl_next since the first + * node is a dummy node. */ names = ft_getpnames(wl->wl_next, FALSE); - if (names == NULL) + if (names == (struct pnode *) NULL) { goto quit1; + } /* Now evaluate the names. */ - for (pn = names, lv = NULL; pn; pn = pn->pn_next) - if (pn->pn_value && (pn->pn_value->v_length == 0) && - eq(pn->pn_value->v_name, "vs")) - { + for (pn = names, lv = NULL; pn; pn = pn->pn_next) { + struct dvec *pn_value = pn->pn_value; + + /* Test for a vs b construct */ + if (pn_value && (pn_value->v_length == 0) && + eq(pn_value->v_name, "vs")) { struct dvec *dv; - if (!lv) { + if (!lv) { /* e.g. "plot vs b" */ fprintf(cp_err, "Error: misplaced vs arg\n"); goto quit; } - if ((pn = pn->pn_next) == NULL) { + if ((pn = pn->pn_next) == NULL) { /* "plot a vs" */ fprintf(cp_err, "Error: missing vs arg\n"); goto quit; } dv = ft_evaluate(pn); - if (!dv) + if (!dv) { goto quit; + } - if (lastvs) + if (lastvs) { lv = lastvs->v_link2; - else + } + else { lv = vecs; + } while (lv) { lv->v_scale = dv; lastvs = lv; lv = lv->v_link2; } - - } else { - - struct dvec *dv; - - dv = ft_evaluate(pn); - if (!dv) + } + else { /* An explicit scale vector is not given ("plot a") */ + struct dvec * const dv = ft_evaluate(pn); + if (!dv) { goto quit; + } - if (!d) + if (!d) { vecs = dv; - else + } + else { d->v_link2 = dv; + } - for (d = dv; d->v_link2; d = d->v_link2) + for (d = dv; d->v_link2; d = d->v_link2) { ; + } lv = dv; } + } /* end of loop evaluating the names */ + d->v_link2 = NULL; /* terminate list */ - /* free_pnode(names); pn:really should be commented out ? */ - d->v_link2 = NULL; /* Now check for 0-length vectors. */ - for (d = vecs; d; d = d->v_link2) + for (d = vecs; d; d = d->v_link2) { if (!d->v_length) { fprintf(cp_err, "Error(plotit.c--plotit): %s: no such vector\n", d->v_name); goto quit; } + } /* If there are higher dimensional vectors, transform them into a * family of vectors. */ - for (d = vecs, lv = NULL; d; d = d->v_link2) - if (d->v_numdims > 1) { - if (lv) + for (d = vecs, lv = NULL; d; d = d->v_link2) { + /* Link the family of vectors that is created through the v_link2 link. + * Note that vec_mkfamily links all of the vector that are created + * through v_link2 also, so the family of vectors can be added to + * the plot list by stepping to the end and linking to the next + * vector */ + if (d->v_numdims > 1) { /* multi-dim vector */ + if (lv) { lv->v_link2 = vec_mkfamily(d); - else + } + else { vecs = lv = vec_mkfamily(d); - while (lv->v_link2) + } + + /* Step to end of the family of vectors */ + while (lv->v_link2) { lv = lv->v_link2; + } + + /* And link last vector in family to next vector to plot */ lv->v_link2 = d->v_link2; d = lv; - } else { + } + else { + /* Ordinary 1-dim vector, so set prev vector to this one in + * preparation for next increment in loop */ lv = d; } + } /* end of loop over vectors being plotted */ /* Now fill in the scales for vectors who aren't already fixed up. */ - for (d = vecs; d; d = d->v_link2) + for (d = vecs; d; d = d->v_link2) { if (!d->v_scale) { - if (d->v_plot->pl_scale) + if (d->v_plot->pl_scale) { d->v_scale = d->v_plot->pl_scale; - else + } + else { d->v_scale = d; + } } + } /* The following line displays the unit at the time of temp-sweep, res-sweep, and i-sweep. This may not be a so good solution. by H.T */ - if (strcmp(vecs->v_scale->v_name, "temp-sweep") == 0) + if (strcmp(vecs->v_scale->v_name, "temp-sweep") == 0) { vecs->v_scale->v_type = SV_TEMP; - if (strcmp(vecs->v_scale->v_name, "res-sweep") == 0) + } + if (strcmp(vecs->v_scale->v_name, "res-sweep") == 0) { vecs->v_scale->v_type = SV_RES; - if (strcmp(vecs->v_scale->v_name, "i-sweep") == 0) + } + if (strcmp(vecs->v_scale->v_name, "i-sweep") == 0) { vecs->v_scale->v_type = SV_CURRENT; + } /* See if the log flag is set anywhere... */ if (!gfound) { - for (d = vecs; d; d = d->v_link2) - if (d->v_scale && (d->v_scale->v_gridtype == GRID_XLOG)) + for (d = vecs; d; d = d->v_link2) { + if (d->v_scale && (d->v_scale->v_gridtype == GRID_XLOG)) { gtype = GRID_XLOG; - for (d = vecs; d; d = d->v_link2) - if (d->v_gridtype == GRID_YLOG) { - if ((gtype == GRID_XLOG) || (gtype == GRID_LOGLOG)) - gtype = GRID_LOGLOG; - else - gtype = GRID_YLOG; } - for (d = vecs; d; d = d->v_link2) + } + for (d = vecs; d; d = d->v_link2) { + if (d->v_gridtype == GRID_YLOG) { + if ((gtype == GRID_XLOG) || (gtype == GRID_LOGLOG)) { + gtype = GRID_LOGLOG; + } + else { + gtype = GRID_YLOG; + } + } + } + for (d = vecs; d; d = d->v_link2) { if (d->v_gridtype == GRID_SMITH || - d->v_gridtype == GRID_SMITHGRID || - d->v_gridtype == GRID_POLAR) - { + d->v_gridtype == GRID_SMITHGRID || + d->v_gridtype == GRID_POLAR) { gtype = d->v_gridtype; break; } + } } /* See if there are any default plot types... Here, like above, we @@ -656,57 +840,60 @@ plotit(wordlist *wl, char *hcopy, char *devname) */ if (!sameflag && !pfound) { ptype = PLOT_LIN; - for (d = vecs; d; d = d->v_link2) + for (d = vecs; d; d = d->v_link2) { if (d->v_plottype != PLOT_LIN) { ptype = d->v_plottype; break; } + } } /* Check and see if this is pole zero stuff. */ - if ((vecs->v_type == SV_POLE) || (vecs->v_type == SV_ZERO)) + if ((vecs->v_type == SV_POLE) || (vecs->v_type == SV_ZERO)) { oneval = TRUE; + } - for (d = vecs; d; d = d->v_link2) + for (d = vecs; d; d = d->v_link2) { if (((d->v_type == SV_POLE) || (d->v_type == SV_ZERO)) != - oneval ? 1 : 0) { + oneval ? 1 : 0) { fprintf(cp_err, - "Error: plot must be either all pole-zero or contain no poles or zeros\n"); + "Error: plot must be either all pole-zero " + "or contain no poles or zeros\n"); goto quit; } + } - if ((gtype == GRID_POLAR) || (gtype == GRID_SMITH || gtype == GRID_SMITHGRID)) + if (gtype == GRID_POLAR || gtype == GRID_SMITH || + gtype == GRID_SMITHGRID) { oneval = TRUE; + } - /* If we are plotting scalars, make sure there is enough - * data to fit on the screen. - */ - for (d = vecs; d; d = d->v_link2) - if (d->v_length == 1) + /* If a vector contains a single point, copy the point so that there are + * as many copies as the scale vector has elements. */ + for (d = vecs; d; d = d->v_link2) { + if (d->v_length == 1) { /* single value */ xtend(d, d->v_scale->v_length); + } + } - /* Now patch up each vector with the compression and thestrchr - * selection. - */ - if (xcompress || xindices) + /* Now patch up each vector with the compression (decimation) and + * the strchr selection. */ + if (xcompress || xindices) { for (d = vecs; d; d = d->v_link2) { compress(d, xcompress, xindices); d->v_scale = vec_copy(d->v_scale); compress(d->v_scale, xcompress, xindices); } + } /* Transform for smith plots */ if (gtype == GRID_SMITH) { - double re, im, rex, imx; - double r; - struct dvec **prevvp, *n; - int j; - - prevvp = &vecs; + struct dvec **prevvp = &vecs; + /* Loop over vectors being plotted */ for (d = vecs; d; d = d->v_link2) { if (d->v_flags & VF_PERMANENT) { - n = vec_copy(d); + struct dvec * const n = vec_copy(d); n->v_flags &= ~VF_PERMANENT; n->v_link2 = d->v_link2; d = n; @@ -716,149 +903,40 @@ plotit(wordlist *wl, char *hcopy, char *devname) if (isreal(d)) { fprintf(cp_err, - "Warning: plotting real data \"%s\" on a smith grid\n", + "Warning: plotting real data \"%s\" on a Smith grid\n", d->v_name); - for (j = 0; j < d->v_length; j++) { - r = d->v_realdata[j]; + const int n_elem = d->v_length; + int j; + for (j = 0; j < n_elem; j++) { + const double r = d->v_realdata[j]; d->v_realdata[j] = (r - 1) / (r + 1); } - } else { - for (j = 0; j < d->v_length; j++) { - /* (re - 1, im) / (re + 1, im) */ - - re = realpart(d->v_compdata[j]); - im = imagpart(d->v_compdata[j]); - - rex = re + 1; - imx = im; - re = re - 1; - - /* (re, im) / (rex, imx) */ - /* x = 1 - (imx / rex) * (imx / rex); - * r = re / rex + im / rex * imx / rex; - * i = im / rex - re / rex * imx / rex; - * - * - * realpart(d->v_compdata[j]) = r / x; - * imagpart(d->v_compdata[j]) = i / x; - */ - realpart(d->v_compdata[j]) = (rex*re+imx*imx) / (rex*rex+imx*imx); - imagpart(d->v_compdata[j]) = (2*imx) / (rex*rex+imx*imx); - } } - } - } + else { + ngcomplex_t * const v0 = d->v_compdata; + const int n_elem = d->v_length; + int j; + for (j = 0; j < n_elem; j++) { + ngcomplex_t * const p_cur = v0 + j; + (void) SMITH_tfm(realpart(*p_cur), imagpart(*p_cur), + &realpart(*p_cur), &imagpart(*p_cur)); + } /* end of loop over elements in vector */ + } /* complex data */ + } /* end of loop over vectors being plotted */ + } /* end of case of Smith grid */ - /* Figure out the proper x- and y-axis limits. */ - if (ylim) { - ylims[0] = ylim[0]; - ylims[1] = ylim[1]; - } else if (oneval) { - ylims[0] = HUGE; - ylims[1] = - ylims[0]; - for (d = vecs; d; d = d->v_link2) { - /* dd = ft_minmax(d, TRUE); */ - /* With this we seek the maximum and minimum of imaginary part - * that will go to Y axis - */ - dd = ft_minmax(d, FALSE); - if (ylims[0] > dd[0]) - ylims[0] = dd[0]; - if (ylims[1] < dd[1]) - ylims[1] = dd[1]; - } - } else { - ylims[0] = HUGE; - ylims[1] = - ylims[0]; - for (d = vecs; d; d = d->v_link2) { - dd = ft_minmax(d, TRUE); - if (ylims[0] > dd[0]) - ylims[0] = dd[0]; - if (ylims[1] < dd[1]) - ylims[1] = dd[1]; - } + /* Figure out the proper x-axis and y-axis limits. */ + find_axis_limits(ylim, oneval, FALSE, vecs, &vec_self, ylims); + find_axis_limits(xlim, oneval, TRUE, vecs, &vec_scale, xlims); - /* XXX */ - for (d = vecs; d; d = d->v_link2) { - if (d->v_flags & VF_MINGIVEN) - if (ylims[0] < d->v_minsignal) - ylims[0] = d->v_minsignal; - if (d->v_flags & VF_MAXGIVEN) - if (ylims[1] > d->v_maxsignal) - ylims[1] = d->v_maxsignal; - } - } - - if (xlim) { - xlims[0] = xlim[0]; - xlims[1] = xlim[1]; - } else if (oneval) { - xlims[0] = HUGE; - xlims[1] = - xlims[0]; - for (d = vecs; d; d = d->v_link2) { - /* dd = ft_minmax(d, FALSE); */ - /* With this we seek the maximum and minimum of imaginary part - * that will go to Y axis - */ - dd = ft_minmax(d, TRUE); - - if (xlims[0] > dd[0]) - xlims[0] = dd[0]; - if (xlims[1] < dd[1]) - xlims[1] = dd[1]; - } - } else { - xlims[0] = HUGE; - xlims[1] = - xlims[0]; - for (d = vecs; d; d = d->v_link2) { - dd = ft_minmax(d->v_scale, TRUE); - if (xlims[0] > dd[0]) - xlims[0] = dd[0]; - if (xlims[1] < dd[1]) - xlims[1] = dd[1]; - } - for (d = vecs; d; d = d->v_link2) { - if (d->v_scale->v_flags & VF_MINGIVEN) - if (xlims[0] < d->v_scale->v_minsignal) - xlims[0] = d->v_scale->v_minsignal; - if (d->v_scale->v_flags & VF_MAXGIVEN) - if (xlims[1] > d->v_scale->v_maxsignal) - xlims[1] = d->v_scale->v_maxsignal; - } - } - - /* Do some coercion of the limits to make them reasonable. */ - if ((xlims[0] == 0) && (xlims[1] == 0)) { - xlims[0] = -1.0; - xlims[1] = 1.0; - } - if ((ylims[0] == 0) && (ylims[1] == 0)) { - ylims[0] = -1.0; - ylims[1] = 1.0; - } - if (xlims[0] > xlims[1]) { - SWAP(double, xlims[0], xlims[1]); - } - if (ylims[0] > ylims[1]) { - SWAP(double, ylims[0], ylims[1]); - } - if (AlmostEqualUlps(xlims[0], xlims[1], 10)) { - xlims[0] *= (xlims[0] > 0) ? 0.9 : 1.1; - xlims[1] *= (xlims[1] > 0) ? 1.1 : 0.9; - } - if (AlmostEqualUlps(ylims[0], ylims[1], 10)) { - ylims[0] *= (ylims[0] > 0) ? 0.9 : 1.1; - ylims[1] *= (ylims[1] > 0) ? 1.1 : 0.9; - } - - if ((xlims[0] <= 0.0) && ((gtype == GRID_XLOG) || - (gtype == GRID_LOGLOG))) { + if ((xlims[0] <= 0.0) && + ((gtype == GRID_XLOG) || (gtype == GRID_LOGLOG))) { fprintf(cp_err, "Error: X values must be > 0 for log scale\n"); goto quit; } - if ((ylims[0] <= 0.0) && ((gtype == GRID_YLOG) || - (gtype == GRID_LOGLOG))) { + if ((ylims[0] <= 0.0) && + ((gtype == GRID_YLOG) || (gtype == GRID_LOGLOG))) { fprintf(cp_err, "Error: Y values must be > 0 for log scale\n"); goto quit; } @@ -876,52 +954,38 @@ plotit(wordlist *wl, char *hcopy, char *devname) * the complex value */ rad = hypot(mx, my); - xlims[0] = - rad; + xlims[0] = -rad; xlims[1] = rad; - ylims[0] = - rad; + ylims[0] = -rad; ylims[1] = rad; - } else if ((!xlim || !ylim) && (gtype == GRID_SMITH || gtype == GRID_SMITHGRID)) - { + } + else if ((!xlim || !ylim) && + (gtype == GRID_SMITH || gtype == GRID_SMITHGRID)) { xlims[0] = -1.0; xlims[1] = 1.0; ylims[0] = -1.0; ylims[1] = 1.0; } - if (xlim) + if (xlim) { tfree(xlim); - if (ylim) + } + if (ylim) { tfree(ylim); - - /* Sanity check: scale and vector may differ, if user assembles a single graph from different plots */ - if(!oneval) { - int firstscalelength = vecs->v_scale->v_length; - for (d = vecs; d; d = d->v_link2) - if (firstscalelength != d->v_length) { - fprintf(cp_err, "\nWarning: length of vector %s and its scale differ.\n", d->v_name); - /* Do not plot when strict_errorhandling is set */ - if (ft_stricterror) { - fprintf(cp_err, " Cannot plot!\n"); - goto quit; - } - else - fprintf(cp_err, " Check the plot data!\n"); - } } - /* We don't want to try to deal with smith plots for asciiplot. */ + /* We don't want to try to deal with Smith plots for asciiplot. */ if (devname && eq(devname, "lpr")) { /* check if we should (can) linearize */ if (ft_curckt && ft_curckt->ci_ckt && - (strcmp(ft_curckt->ci_name, plot_cur->pl_title) == 0) && - if_tranparams(ft_curckt, &tstart, &tstop, &tstep) && - ((tstop - tstart) * tstep > 0.0) && - ((tstop - tstart) >= tstep) && - plot_cur && plot_cur->pl_dvecs && - plot_cur->pl_scale && - isreal(plot_cur->pl_scale) && - ciprefix("tran", plot_cur->pl_typename)) - { + (strcmp(ft_curckt->ci_name, plot_cur->pl_title) == 0) && + if_tranparams(ft_curckt, &tstart, &tstop, &tstep) && + ((tstop - tstart) * tstep > 0.0) && + ((tstop - tstart) >= tstep) && + plot_cur && plot_cur->pl_dvecs && + plot_cur->pl_scale && + isreal(plot_cur->pl_scale) && + ciprefix("tran", plot_cur->pl_typename)) { int newlen = (int)((tstop - tstart) / tstep + 1.5); double *newscale; @@ -935,15 +999,16 @@ plotit(wordlist *wl, char *hcopy, char *devname) newv_scale->v_gridtype = vecs->v_scale->v_gridtype; newscale = newv_scale->v_realdata; - for (i = 0, ttime = tstart; i < newlen; i++, ttime += tstep) + for (i = 0, ttime = tstart; i < newlen; i++, ttime += tstep) { newscale[i] = ttime; + } for (v = vecs; v; v = v->v_link2) { double *newdata = TMALLOC(double, newlen); if (!ft_interpolate(v->v_realdata, newdata, - v->v_scale->v_realdata, v->v_scale->v_length, - newscale, newlen, 1)) { + v->v_scale->v_realdata, v->v_scale->v_length, + newscale, newlen, 1)) { fprintf(cp_err, "Error: can't interpolate %s\n", v->v_name); goto quit; } @@ -955,7 +1020,6 @@ plotit(wordlist *wl, char *hcopy, char *devname) } vecs->v_scale = newv_scale; - } ft_agraf(xlims, ylims, @@ -970,9 +1034,11 @@ plotit(wordlist *wl, char *hcopy, char *devname) } /* See if there is one common v_type we can give for the y scale... */ - for (d = vecs->v_link2; d; d = d->v_link2) - if (d->v_type != vecs->v_type) + for (d = vecs->v_link2; d; d = d->v_link2) { + if (d->v_type != vecs->v_type) { break; + } + } y_type = d ? SV_NOTYPE : vecs->v_type; @@ -1021,35 +1087,161 @@ plotit(wordlist *wl, char *hcopy, char *devname) } #endif - for (d = vecs, i = 0; d; d = d->v_link2) + /* Find the number of vectors being plotted */ + for (d = vecs, i = 0; d; d = d->v_link2) { i++; + } /* Figure out the X name and the X type. This is sort of bad... */ xn = vecs->v_scale->v_name; xt = vecs->v_scale->v_type; - pname = plot_cur->pl_typename; if (!gr_init(xlims, ylims, (oneval ? NULL : xn), - title ? title : vecs->v_plot->pl_title, - hcopy, i, - xdelta ? *xdelta : 0.0, - ydelta ? *ydelta : 0.0, - gtype, ptype, xlabel, ylabel, xt, y_type, pname, cline)) + title ? title : vecs->v_plot->pl_title, + hcopy, i, + xdelta ? *xdelta : 0.0, + ydelta ? *ydelta : 0.0, + gtype, ptype, xlabel, ylabel, xt, y_type, + plot_cur->pl_typename, ds_get_buf(&ds_cline))) { goto quit; + } /* Now plot all the graphs. */ - for (d = vecs; d; d = d->v_link2) + for (d = vecs; d; d = d->v_link2) { ft_graf(d, oneval ? NULL : d->v_scale, FALSE); + } gr_clean(); - rtn = TRUE; + rtn = TRUE; /* Indicate success */ quit: - tfree(cline); + ds_free(&ds_cline); /* free dstring resources, if any */ free_pnode(names); FREE(title); + quit1: + /* Free any vectors left behing while parsing the plot arguments. These + * are vectors created by ft_evaluate() */ + if (plot_cur != (struct plot *) NULL) { + struct dvec *dv = plot_cur->pl_dvecs; + while(dv != dv_head_orig) { + struct dvec *dv_next = dv->v_next; + vec_free(dv); + dv = dv_next; + } + } + wl_free(wl); return rtn; +} /* end of function plotit */ + + + +/* Return itself */ +static struct dvec *vec_self(struct dvec *v) +{ + return v; } + + + +/* Return scale vector */ +static struct dvec *vec_scale(struct dvec *v) +{ + return v->v_scale; +} + + + +/* This function finds the range limits for an x-axis or y-axis. + * + * Parameters + * lim: Existing limits + * oneval: Flag that there is no scale vector + * f_real: Flag that the real component of a complex value should be used + * when finding the range if true and the imaginary part if false + * vecs: Vectors being used to determine the range. It is related to athe + * oneval flag in that it determines + * p_get_axis_dvec: Address of function used to get range information + * from a vector. It should be either the address of vec_self to use + * the vector itself (for y range with scale value) or the address of + * vec_scale for its scale vector (for x range of scale). + * lims: Address of an array of 2 double values to receive the limits. + **/ +static void find_axis_limits(double lim[2], bool oneval, bool f_real, + struct dvec *vecs, + struct dvec *(*p_get_axis_dvec)(struct dvec *dvec), + double lims[2]) +{ + if (lim != (double *) NULL) { + lims[0] = lim[0]; + lims[1] = lim[1]; + } + else if (oneval) { + struct dvec *d; + lims[0] = HUGE; + lims[1] = -lims[0]; + for (d = vecs; d; d = d->v_link2) { + /* dd = ft_minmax(d, FALSE); */ + /* With this we seek the maximum and minimum of imaginary part + * that will go to Y axis + */ + const double * const dd = ft_minmax(d, f_real); + + if (lims[0] > dd[0]) { + lims[0] = dd[0]; + } + if (lims[1] < dd[1]) { + lims[1] = dd[1]; + } + } + } + else { /* have scale vector */ + struct dvec *d; + lims[0] = HUGE; + lims[1] = -lims[0]; + for (d = vecs; d; d = d->v_link2) { + const double * const dd = ft_minmax((*p_get_axis_dvec)(d), TRUE); + if (lims[0] > dd[0]) { + lims[0] = dd[0]; + } + if (lims[1] < dd[1]) { + lims[1] = dd[1]; + } + } + for (d = vecs; d; d = d->v_link2) { + struct dvec *d2 = (*p_get_axis_dvec)(d); + short v_flags = d2->v_flags; + if (v_flags & VF_MINGIVEN) { + double v_minsignal = d2->v_minsignal; + if (lims[0] < v_minsignal) { + lims[0] = v_minsignal; + } + } + if (v_flags & VF_MAXGIVEN) { + double v_maxsignal = d2->v_maxsignal; + if (lims[1] > v_maxsignal) { + lims[1] = v_maxsignal; + } + } + } /* end of loop over vectors being plotted */ + } /* end of case of vector with scale vector */ + + /* Do some coercion of the limits to make them reasonable. */ + if ((lims[0] == 0.0) && (lims[1] == 0.0)) { + lims[0] = -1.0; + lims[1] = 1.0; + } + if (lims[0] > lims[1]) { + SWAP(double, lims[0], lims[1]); + } + if (AlmostEqualUlps(lims[0], lims[1], 10)) { + lims[0] *= (lims[0] > 0) ? 0.9 : 1.1; + lims[1] *= (lims[1] > 0) ? 1.1 : 0.9; + } + +} /* end of function find_axis_limits */ + + + diff --git a/src/frontend/plotting/plotit.h b/src/frontend/plotting/plotit.h index 2ca853afc..c57578eed 100644 --- a/src/frontend/plotting/plotit.h +++ b/src/frontend/plotting/plotit.h @@ -1,6 +1,6 @@ #ifndef ngspice_PLOTIT_H #define ngspice_PLOTIT_H -bool plotit(wordlist *wl, char *hcopy, char *devname); +bool plotit(wordlist *wl, const char *hcopy, const char *devname); #endif diff --git a/src/include/ngspice/graph.h b/src/include/ngspice/graph.h index 02cdb0e0d..b682f377f 100644 --- a/src/include/ngspice/graph.h +++ b/src/include/ngspice/graph.h @@ -108,7 +108,7 @@ struct graph { and de-allocated by DestroyGraph. */ void *devdep; - + size_t n_byte_devdep; /* Size of devdep. Needed to allow copying */ }; From 503af6ac002951090139c4334b8e1ea56b7628eb Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Thu, 12 Dec 2019 19:10:29 -0500 Subject: [PATCH 31/58] Fixed several issues related to plotting and vector lifetimes and made ownership of vectors clearer. The issues in bugs 419, 423, 425, and 426 were related and were addressed here. --- src/frontend/com_hardcopy.c | 61 ++++-- src/frontend/dvec.c | 79 +++++--- src/frontend/outitf.c | 60 +++--- src/frontend/plotting/graf.c | 270 ++++++++++++++----------- src/frontend/plotting/graphdb.c | 342 ++++++++++++++++++++------------ src/frontend/postcoms.c | 89 +++++---- src/frontend/runcoms.c | 3 +- src/frontend/variable.c | 142 +++++++------ src/frontend/vectors.c | 60 +++--- src/include/ngspice/bool.h | 2 +- src/include/ngspice/dvec.h | 72 ++++--- src/include/ngspice/fteext.h | 4 +- 12 files changed, 710 insertions(+), 474 deletions(-) diff --git a/src/frontend/com_hardcopy.c b/src/frontend/com_hardcopy.c index 595dfc583..39aa0baed 100644 --- a/src/frontend/com_hardcopy.c +++ b/src/frontend/com_hardcopy.c @@ -21,10 +21,16 @@ /* hardcopy file plotargs, or 'hardcopy file' -- with no other args * this prompts the user for a window to dump to a plot file. XXX no * it doesn't. */ -void -com_hardcopy(wordlist *wl) +void com_hardcopy(wordlist *wl) { + /* Check if there is a graph available */ + if (currentgraph == (GRAPH *) NULL) { + (void) fprintf(cp_err, "There is no graph to hardcopy.\n"); + return; + } + char *fname; + size_t n_byte_fname; /* size of fname in bytes, including null */ char buf[BSIZE_SP], device[BSIZE_SP]; bool tempf = FALSE; char *devtype; @@ -40,18 +46,22 @@ com_hardcopy(wordlist *wl) if (wl) { hc_button = 0; - fname = wl->wl_word; + fname = copy(wl->wl_word); wl = wl->wl_next; - } else { + } + else { hc_button = 1; fname = smktemp("hc"); tempf = TRUE; } + n_byte_fname = (strlen(fname) + 1) * sizeof *fname; - if (!cp_getvar("hcopydevtype", CP_STRING, buf, sizeof(buf))) + if (!cp_getvar("hcopydevtype", CP_STRING, buf, sizeof(buf))) { devtype = "postscript"; - else + } + else { devtype = buf; + } /* enable screen plot selection for these display types */ foundit = 0; @@ -63,8 +73,9 @@ com_hardcopy(wordlist *wl) if (!wl && hc_button) { char *psfname; GRAPH *tempgraph; - if (DevSwitch(devtype)) + if (DevSwitch(devtype)) { return; + } tempgraph = CopyGraph(currentgraph); /* change .tmp to .ps */ psfname = strchr(fname, '.'); @@ -72,11 +83,15 @@ com_hardcopy(wordlist *wl) psfname[1] = 'p'; psfname[2] = 's'; psfname[3] = '\0'; - } else { - fname = trealloc(fname, strlen(fname)+4); - strcat(fname, ".ps"); + } + else { + fname = trealloc(fname, n_byte_fname + 3); + (void) memcpy(fname + n_byte_fname - 1, ".ps", 4); + n_byte_fname += 3; } tempgraph->devdep = fname; + tempgraph->n_byte_devdep = n_byte_fname; + if (NewViewport(tempgraph)) { DevSwitch(NULL); return; @@ -92,21 +107,23 @@ com_hardcopy(wordlist *wl) #ifndef X_DISPLAY_MISSING if (!wl && hc_button) { - REQUEST request; RESPONSE response; GRAPH *tempgraph; request.option = click_option; Input(&request, &response); - if (response.option == error_option) + if (response.option == error_option) { return; + } if (response.reply.graph) { - if (DevSwitch(devtype)) + if (DevSwitch(devtype)) { return; + } tempgraph = CopyGraph(response.reply.graph); tempgraph->devdep = fname; + tempgraph->n_byte_devdep = n_byte_fname; if (NewViewport(tempgraph)) { DevSwitch(NULL); return; @@ -133,19 +150,20 @@ com_hardcopy(wordlist *wl) PushGraphContext(currentgraph); if (!foundit) { - if (!wl) { char *buf2; outmenuprompt("which variable ? "); buf2 = prompt(cp_in); - if (!buf2) + if (!buf2) { return; + } wl = wl_cons(buf2, NULL); wl = process(wl); } - if (DevSwitch(devtype)) + if (DevSwitch(devtype)) { return; + } if (!wl || !plotit(wl, fname, NULL)) { printf("com_hardcopy: graph not defined\n"); @@ -189,11 +207,13 @@ com_hardcopy(wordlist *wl) fname); fprintf(cp_out, "\tor by using the '-g' flag to the Unix lpr command.\n"); - } else if (!strcmp(devtype, "postscript")) { + } + else if (!strcmp(devtype, "postscript")) { fprintf(cp_out, "\nThe file \"%s\" may be printed on a postscript printer.\n", fname); - } else if (!strcmp(devtype, "MFB")) { + } + else if (!strcmp(devtype, "MFB")) { fprintf(cp_out, "The file \"%s\" may be printed on a MFB device.\n", fname); @@ -205,4 +225,7 @@ com_hardcopy(wordlist *wl) /* restore previous graphics context by retrieving the previous currentgraph */ PopGraphContext(); -} +} /* end of function com_hardcopy */ + + + diff --git a/src/frontend/dvec.c b/src/frontend/dvec.c index f837f17e3..303f2455f 100644 --- a/src/frontend/dvec.c +++ b/src/frontend/dvec.c @@ -2,10 +2,10 @@ #include "ngspice/dvec.h" -struct dvec * -dvec_alloc(char *name, int type, short flags, int length, void *storage) +struct dvec *dvec_alloc(const char *name, + int type, short flags, int length, void *storage) { - struct dvec *rv = TMALLOC(struct dvec, 1); + struct dvec * const rv = TMALLOC(struct dvec, 1); /* If the allocation failed, return NULL as a failure flag. * As of 2019-03, TMALLOC will not return on failure, so this check is @@ -54,60 +54,81 @@ dvec_alloc(char *name, int type, short flags, int length, void *storage) rv->v_numdims = 0; /* Really "unknown" */ return rv; -} +} /* end of function dvec_alloc */ -void -dvec_realloc(struct dvec *v, int length, void *storage) +/* Resize dvec to length if storage is NULL orr replace + * its existing allocation with storage if not + */ +void dvec_realloc(struct dvec *v, int length, void *storage) { if (isreal(v)) { if (storage) { tfree(v->v_realdata); v->v_realdata = (double *) storage; - } else { + } + else { v->v_realdata = TREALLOC(double, v->v_realdata, length); } - } else { + } + else { if (storage) { tfree(v->v_compdata); v->v_compdata = (ngcomplex_t *) storage; - } else { + } + else { v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, length); } } v->v_length = length; v->v_alloc_length = length; -} +} /* end of function dvec_realloc */ -void -dvec_extend(struct dvec *v, int length) +void dvec_extend(struct dvec *v, int length) { - if (isreal(v)) + if (isreal(v)) { v->v_realdata = TREALLOC(double, v->v_realdata, length); - else + } + else { v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, length); + } v->v_alloc_length = length; -} +} /* end of function dvec_extend */ -void -dvec_trunc(struct dvec *v, int length) + +void dvec_trunc(struct dvec *v, int length) { - v->v_length = length; -} + /* Ensure valid */ + if (v->v_alloc_length <= length) { + v->v_length = length; + } +} /* end of function dvec_trunc */ -void -dvec_free(struct dvec *v) + +void dvec_free(struct dvec *v) { - if (v->v_name) - tfree(v->v_name); - if (v->v_realdata) - tfree(v->v_realdata); - if (v->v_compdata) - tfree(v->v_compdata); - tfree(v); -} + /* Check for freed vector */ + if (v == (struct dvec *) NULL) { + return; + } + + /* Free the various allocations */ + if (v->v_name) { + txfree(v->v_name); + } + if (v->v_realdata) { + txfree(v->v_realdata); + } + else if (v->v_compdata) { /* if data real, not complex */ + txfree(v->v_compdata); + } + txfree(v); +} /* end of function dvec_free */ + + + diff --git a/src/frontend/outitf.c b/src/frontend/outitf.c index 6738b9fa0..0e617df92 100644 --- a/src/frontend/outitf.c +++ b/src/frontend/outitf.c @@ -565,21 +565,22 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) #ifdef TCL_MODULE steps_completed = run->pointCount; #endif - /* interpolated batch mode output to file in transient analysis */ - if (interpolated && run->circuit->CKTcurJob->JOBtype == 4 && run->writeOut) { - InterpFileAdd(run, refValue, valuePtr); - return (OK); - } - /* interpolated interactive or control mode output to plot in transient analysis */ - else if (interpolated && run->circuit->CKTcurJob->JOBtype == 4 && !(run->writeOut)) { - InterpPlotAdd(run, refValue, valuePtr); - return (OK); + /* interpolated batch mode output to file/plot in transient analysis */ + if (interpolated && run->circuit->CKTcurJob->JOBtype == 4) { + if (run->writeOut) { /* To file */ + InterpFileAdd(run, refValue, valuePtr); + } + else { /* To plot */ + InterpPlotAdd(run, refValue, valuePtr); + } + return OK; } + /* standard batch mode output to file */ else if (run->writeOut) { - - if (run->pointCount == 1) + if (run->pointCount == 1) { fileInit_pass2(run); + } fileStartPoint(run->fp, run->binary, run->pointCount); @@ -600,10 +601,8 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) } } #endif - } else { - - /* And the same for a non-complex value */ - + } + else { /* And the same for a non-complex (real) value */ fileAddRealValue(run->fp, run->binary, refValue->rValue); #ifndef HAS_WINGUI if (!orflag && !ft_norefprint) { @@ -620,8 +619,9 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) for (i = 0; i < run->numData; i++) { /* we've already printed reference vec first */ - if (run->data[i].outIndex == -1) + if (run->data[i].outIndex == -1) { continue; + } #ifdef TCL_MODULE blt_add(i, refValue ? refValue->rValue : NAN); @@ -630,13 +630,14 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) if (run->data[i].regular) { if (run->data[i].type == IF_REAL) fileAddRealValue(run->fp, run->binary, - valuePtr->v.vec.rVec [run->data[i].outIndex]); + valuePtr->v.vec.rVec [run->data[i].outIndex]); else if (run->data[i].type == IF_COMPLEX) fileAddComplexValue(run->fp, run->binary, - valuePtr->v.vec.cVec [run->data[i].outIndex]); + valuePtr->v.vec.cVec [run->data[i].outIndex]); else fprintf(stderr, "OUTpData: unsupported data type\n"); - } else { + } + else { IFvalue val; /* should pre-check instance */ if (!getSpecial(&run->data[i], run, &val)) { @@ -652,7 +653,8 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) val.cValue.real = 0; val.cValue.imag = 0; fileAddComplexValue(run->fp, run->binary, val.cValue); - } else { + } + else { val.rValue = 0; fileAddRealValue(run->fp, run->binary, val.rValue); } @@ -683,8 +685,8 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) shouldstop = TRUE; } - } else { - + } + else { OUTpD_memory(run, refValue, valuePtr); /* This is interactive mode. Update the screen with the reference @@ -718,18 +720,18 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr) sh_ExecutePerLoop(); #endif - return (OK); -} + return OK; +} /* end of function OUTpData */ -int -OUTwReference(void *plotPtr, IFvalue *valuePtr, void **refPtr) + +int OUTwReference(void *plotPtr, IFvalue *valuePtr, void **refPtr) { NG_IGNORE(refPtr); NG_IGNORE(valuePtr); NG_IGNORE(plotPtr); - return (OK); + return OK; } @@ -741,7 +743,7 @@ OUTwData(runDesc *plotPtr, int dataIndex, IFvalue *valuePtr, void *refPtr) NG_IGNORE(dataIndex); NG_IGNORE(plotPtr); - return (OK); + return OK; } @@ -750,7 +752,7 @@ OUTwEnd(runDesc *plotPtr) { NG_IGNORE(plotPtr); - return (OK); + return OK; } diff --git a/src/frontend/plotting/graf.c b/src/frontend/plotting/graf.c index d8bf48ef2..15ad1bdb8 100644 --- a/src/frontend/plotting/graf.c +++ b/src/frontend/plotting/graf.c @@ -17,19 +17,18 @@ Author: 1988 Jeffrey M. Hsu #include "ngspice/dvec.h" /* for struct dvec */ #include "ngspice/ftedefs.h" /* for FTEextern.h and IPOINT{MIN,MAX} */ #include "ngspice/fteinput.h" -#include "ngspice/graph.h" #include "ngspice/ftedbgra.h" #include "ngspice/ftedev.h" +#include "ngspice/graph.h" +#include "ngspice/grid.h" #include "ngspice/sim.h" -#include +#include "ngspice/stringskip.h" +#include "breakp2.h" +#include "display.h" #include "graf.h" #include "graphdb.h" -#include "ngspice/grid.h" -#include "../terminal.h" -#include "../breakp2.h" -#include "../display.h" -#include "../runcoms.h" -#include "ngspice/stringskip.h" +#include "runcoms.h" +#include "terminal.h" static void gr_start_internal(struct dvec *dv, bool copyvec); @@ -107,8 +106,10 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ cur.plotno = 0; /* note: should do only once, maybe in gr_init_once */ - if (!cp_getvar("pointchars", CP_STRING, pointchars, sizeof(pointchars))) + if (!cp_getvar("pointchars", CP_STRING, + pointchars, sizeof(pointchars))) { (void) strcpy(pointchars, DEFPOINTCHARS); + } if (!cp_getvar("ticmarks", CP_NUM, &graph->ticmarks, 0)) { if (cp_getvar("ticmarks", CP_BOOL, NULL, 0)) { @@ -242,8 +243,7 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */ * We pass two points in so we can multiplex plots. * */ -void -gr_point(struct dvec *dv, +void gr_point(struct dvec *dv, double newx, double newy, double oldx, double oldy, int np) @@ -262,11 +262,13 @@ gr_point(struct dvec *dv, oldtox = tox; oldtoy = toy; if (!currentgraph->grid.circular) { if (clip_line(&fromx, &fromy, &tox, &toy, - currentgraph->viewportxoff, currentgraph->viewportyoff, - currentgraph->viewport.width + currentgraph->viewportxoff, - currentgraph->viewport.height + currentgraph->viewportyoff)) + currentgraph->viewportxoff, currentgraph->viewportyoff, + currentgraph->viewport.width + currentgraph->viewportxoff, + currentgraph->viewport.height + currentgraph->viewportyoff)) { return; - } else { + } + } + else { if (clip_to_circle(&fromx, &fromy, &tox, &toy, currentgraph->grid.xaxis.circular.center, currentgraph->grid.yaxis.circular.center, @@ -276,11 +278,13 @@ gr_point(struct dvec *dv, if (currentgraph->plottype != PLOT_POINT) { SetLinestyle(dv->v_linestyle); - } else { + } + else { /* if PLOT_POINT, don't want to plot an endpoint which have been clipped */ - if (tox != oldtox || toy != oldtoy) + if (tox != oldtox || toy != oldtoy) { return; + } } SetColor(dv->v_color); @@ -290,8 +294,9 @@ gr_point(struct dvec *dv, case PLOT_MONOLIN: /* If it's a linear plot, ignore first point since we don't want to connect with oldx and oldy. */ - if (np) + if (np) { DevDrawLine(fromx, fromy, tox, toy); + } if ((tics = currentgraph->ticdata) != NULL) { for (; *tics < HUGE; tics++) if (*tics == (double) np) { @@ -304,9 +309,9 @@ gr_point(struct dvec *dv, (int) (toy - currentgraph->fontheight / 2)); */ break; } - } else if ((currentgraph->ticmarks >0) && (np > 0) && - (np % currentgraph->ticmarks == 0)) - { + } + else if ((currentgraph->ticmarks >0) && (np > 0) && + (np % currentgraph->ticmarks == 0)) { /* Draw an 'x' */ DevDrawText("x", (int) (tox - currentgraph->fontwidth / 2), (int) (toy - currentgraph->fontheight / 2), 0); @@ -382,34 +387,45 @@ static void gr_start_internal(struct dvec *dv, bool copyvec) link = TMALLOC(struct dveclist, 1); link->next = currentgraph->plotdata; + /* Either reuse input vector or copy depnding on copyvec */ if (copyvec) { link->vector = vec_copy(dv); /* vec_copy doesn't set v_color or v_linestyle */ link->vector->v_color = dv->v_color; link->vector->v_linestyle = dv->v_linestyle; link->vector->v_flags |= VF_PERMANENT; - } else { + link->f_own_vector = TRUE; + } + else { link->vector = dv; + link->f_own_vector = FALSE; } currentgraph->plotdata = link; /* Add the scale vector to the list of vectors associated with the plot - * and use the copy instead of the original scale vector */ - link = TMALLOC(struct dveclist, 1); - link->next = currentgraph->plotdata; + * and use the copy instead of the original scale vector if requested */ + { + struct dvec * const custom_scale = dv->v_scale; + if (custom_scale != (struct dvec *) NULL) { + link = TMALLOC(struct dveclist, 1); + link->next = currentgraph->plotdata; - if (copyvec) { - link->vector = vec_copy(dv->v_scale); - link->vector->v_flags |= VF_PERMANENT; - link->next->vector->v_scale = link->vector; - } - else { - link->vector = dv->v_scale; - } + if (copyvec) { + link->vector = vec_copy(dv->v_scale); + link->vector->v_flags |= VF_PERMANENT; + link->next->vector->v_scale = link->vector; + link->f_own_vector = TRUE; + } + else { + link->vector = dv->v_scale; + link->f_own_vector = FALSE; + } - /* Make the new vector the start of the list of vectors */ - currentgraph->plotdata = link; + /* Make the new vector the start of the list of vectors */ + currentgraph->plotdata = link; + } + } /* Put the legend entry on the screen. */ @@ -470,8 +486,7 @@ void drawlegend(GRAPH *graph, int plotno, struct dvec *dv) /* end one plot of a graph */ -void -gr_end(struct dvec *dv) +void gr_end(struct dvec *dv) { NG_IGNORE(dv); DevUpdate(); @@ -480,8 +495,7 @@ gr_end(struct dvec *dv) /* Print text in the bottom line. */ -void -gr_pmsg(char *text) +void gr_pmsg(char *text) { char buf[BSIZE_SP]; buf[0] = '\0'; @@ -503,16 +517,14 @@ gr_pmsg(char *text) } -void -gr_clean(void) +void gr_clean(void) { DevUpdate(); } /* call this routine after viewport size changes */ -void -gr_resize(GRAPH *graph) +void gr_resize(GRAPH *graph) { double oldxratio, oldyratio; double scalex, scaley; @@ -552,8 +564,7 @@ gr_resize(GRAPH *graph) /* PN static */ -void -gr_resize_internal(GRAPH *graph) +void gr_resize_internal(GRAPH *graph) { if (!graph->grid.xsized) graph->viewport.width = (int)(graph->absolute.width - @@ -579,8 +590,7 @@ gr_resize_internal(GRAPH *graph) /* redraw everything in struct graph */ -void -gr_redraw(GRAPH *graph) +void gr_redraw(GRAPH *graph) { struct dveclist *link; @@ -616,8 +626,7 @@ gr_redraw(GRAPH *graph) } -void -gr_restoretext(GRAPH *graph) +void gr_restoretext(GRAPH *graph) { struct _keyed *k; @@ -633,7 +642,8 @@ gr_restoretext(GRAPH *graph) * * First, if length < IPOINTMIN, don't do anything. * - * Second, if length = IPOINTMIN, plot what we have so far. + * Second, if length = IPOINTMIN, plot what we have so far. This step + * is essentially the initializaiton for the graph. * * Third, if length > IPOINTMIN, plot the last points and resize if * needed. @@ -643,16 +653,25 @@ gr_restoretext(GRAPH *graph) * FIXME: there is a problem with multiple iplots that use the same * vector, namely, that vector has the same color throughout. This is * another reason why we need to pull color and linestyle out of dvec - * XXX Or maybe even something more drastic ?? */ - -static int -iplot(struct plot *pl, int id) + * XXX Or maybe even something more drastic ?? + * It would be better to associate a color with an instance using a + * vector than the vector itself, for which color is something artificial. */ +static int iplot(struct plot *pl, int id) { int len = pl->pl_scale->v_length; + + if (ft_grdb) { + fprintf(cp_err, "Entering iplot, len = %d\n", len); + } + + /* Do simple check for exit first */ + if (len < IPOINTMIN) { /* Nothing yet */ + return 0; + } + struct dvec *v, *xs = pl->pl_scale; double *lims, dy; double start, stop, step; - register int j; bool changed = FALSE; int yt; char *yl = NULL; @@ -660,51 +679,61 @@ iplot(struct plot *pl, int id) static REQUEST reqst = { checkup_option, NULL }; int inited = 0; char commandline[513]; + int n_vec_plot = 0; - for (j = 0, v = pl->pl_dvecs; v; v = v->v_next) - if (v->v_flags & VF_PLOT) - j++; - if (!j) - return (0); - if (ft_grdb) - fprintf(cp_err, "Entering iplot, len = %d\n", len); + /* Exit if nothing is being plotted */ + for (v = pl->pl_dvecs; v; v = v->v_next) { + if (v->v_flags & VF_PLOT) { + ++n_vec_plot; + } + } - if (len < IPOINTMIN) { - /* Nothing yet */ - return (0); - } else if (len == IPOINTMIN || !id) { + if (n_vec_plot == 0) { + return 0; + } + + if (len == IPOINTMIN || !id) { /* Do initialization */ resumption = FALSE; /* Draw the grid for the first time, and plot everything. */ lims = ft_minmax(xs, TRUE); xlims[0] = lims[0]; xlims[1] = lims[1]; ylims[0] = HUGE; - ylims[1] = - ylims[0]; - for (v = pl->pl_dvecs; v; v = v->v_next) + ylims[1] = -ylims[0]; + for (v = pl->pl_dvecs; v; v = v->v_next) { if (v->v_flags & VF_PLOT) { lims = ft_minmax(v, TRUE); - if (ylims[0] > lims[0]) + if (ylims[0] > lims[0]) { ylims[0] = lims[0]; - if (ylims[1] < lims[1]) + } + if (ylims[1] < lims[1]) { ylims[1] = lims[1]; - if (!yl) + } + if (!yl) { yl = v->v_name; + } } - /* generate a small difference between ymin and ymax - to catch the y=const case */ - if (ylims[0] == ylims[1]) - ylims[1] += 1e-9; + } - if (ft_grdb) + /* Generate a small difference between ymin and ymax + to catch the y=const case */ + if (ylims[0] == ylims[1]) { + ylims[1] += 1e-9; + } + + if (ft_grdb) { fprintf(cp_err, "iplot: after 5, xlims = %G, %G, ylims = %G, %G\n", xlims[0], xlims[1], ylims[0], ylims[1]); + } - for (yt = pl->pl_dvecs->v_type, v = pl->pl_dvecs->v_next; v; v = v->v_next) + for (yt = pl->pl_dvecs->v_type, v = pl->pl_dvecs->v_next; v; + v = v->v_next) { if ((v->v_flags & VF_PLOT) && (v->v_type != yt)) { yt = SV_NOTYPE; break; } + } /* note: have command options for iplot to specify xdelta, etc. So don't need static variables hack. Assume default @@ -712,25 +741,28 @@ iplot(struct plot *pl, int id) sprintf(commandline, "plot %s", yl); (void) gr_init(xlims, ylims, xs->v_name, - pl->pl_title, NULL, j, 0.0, 0.0, - GRID_LIN, PLOT_LIN, xs->v_name, yl, xs->v_type, yt, - plot_cur->pl_typename, commandline); + pl->pl_title, NULL, n_vec_plot, 0.0, 0.0, + GRID_LIN, PLOT_LIN, xs->v_name, yl, xs->v_type, yt, + plot_cur->pl_typename, commandline); - for (v = pl->pl_dvecs; v; v = v->v_next) + for (v = pl->pl_dvecs; v; v = v->v_next) { if (v->v_flags & VF_PLOT) { gr_start_internal(v, FALSE); ft_graf(v, xs, TRUE); } + } inited = 1; - } else { + } + else { /* plot the last points and resize if needed */ Input(&reqst, NULL); /* First see if we have to make the screen bigger */ dy = (isreal(xs) ? xs->v_realdata[len - 1] : realpart(xs->v_compdata[len - 1])); - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "x = %G\n", dy); + } if (!if_tranparams(ft_curckt, &start, &stop, &step) || !ciprefix("tran", pl->pl_typename)) { stop = HUGE; @@ -739,32 +771,35 @@ iplot(struct plot *pl, int id) /* checking for x lo */ while (dy < currentgraph->data.xmin) { changed = TRUE; - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "resize: xlo %G -> %G\n", currentgraph->data.xmin, currentgraph->data.xmin - (currentgraph->data.xmax - currentgraph->data.xmin) * XFACTOR); + } /* set the new x lo value */ currentgraph->data.xmin -= - (currentgraph->data.xmax - currentgraph->data.xmin) - * XFACTOR; + (currentgraph->data.xmax - currentgraph->data.xmin) + * XFACTOR; if (currentgraph->data.xmin < start) { currentgraph->data.xmin = start; break; } } - if (currentgraph->data.xmax < currentgraph->data.xmin) + if (currentgraph->data.xmax < currentgraph->data.xmin) { currentgraph->data.xmax = currentgraph->data.xmin; + } /* checking for x hi */ while (dy > currentgraph->data.xmax) { changed = TRUE; - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "resize: xhi %G -> %G\n", currentgraph->data.xmax, currentgraph->data.xmax + (currentgraph->data.xmax - currentgraph->data.xmin) * XFACTOR); + } /* set the new x hi value */ currentgraph->data.xmax += (currentgraph->data.xmax - currentgraph->data.xmin) * @@ -776,21 +811,24 @@ iplot(struct plot *pl, int id) } /* checking for all y values */ for (v = pl->pl_dvecs; v; v = v->v_next) { - if (!(v->v_flags & VF_PLOT)) + if (!(v->v_flags & VF_PLOT)) { continue; + } dy = (isreal(v) ? v->v_realdata[len - 1] : realpart(v->v_compdata[len - 1])); - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "y = %G\n", dy); + } /* checking for y lo */ while (dy < currentgraph->data.ymin) { changed = TRUE; - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "resize: ylo %G -> %G\n", currentgraph->data.ymin, currentgraph->data.ymin - (currentgraph->data.ymax - currentgraph->data.ymin) * YFACTOR); + } /* set the new y lo value */ currentgraph->data.ymin -= (currentgraph->data.ymax - currentgraph->data.ymin) @@ -800,17 +838,19 @@ iplot(struct plot *pl, int id) /* currentgraph->data.ymin = dy; currentgraph->data.ymin *= (1 + YFACTOR); */ } - if (currentgraph->data.ymax < currentgraph->data.ymin) + if (currentgraph->data.ymax < currentgraph->data.ymin) { currentgraph->data.ymax = currentgraph->data.ymin; + } /* checking for y hi */ while (dy > currentgraph->data.ymax) { changed = TRUE; - if (ft_grdb) + if (ft_grdb) { fprintf(cp_err, "resize: yhi %G -> %G\n", currentgraph->data.ymax, currentgraph->data.ymax + (currentgraph->data.ymax - currentgraph->data.ymin) * YFACTOR); + } /* set the new y hi value */ currentgraph->data.ymax += (currentgraph->data.ymax - currentgraph->data.ymin) @@ -829,10 +869,11 @@ iplot(struct plot *pl, int id) #ifndef X_DISPLAY_MISSING gr_redraw(currentgraph); #endif - } else { + } + else { /* Just connect the last two points. This won't be done * with curve interpolation, so it might look funny. */ - for (v = pl->pl_dvecs; v; v = v->v_next) + for (v = pl->pl_dvecs; v; v = v->v_next) { if (v->v_flags & VF_PLOT) { gr_point(v, (isreal(xs) ? xs->v_realdata[len - 1] : @@ -845,15 +886,15 @@ iplot(struct plot *pl, int id) realpart(v->v_compdata[len - 2])), len - 1); } + } } } DevUpdate(); - return (inited); + return inited; } -static void -set(struct plot *plot, struct dbcomm *db, bool unset, short mode) +static void set(struct plot *plot, struct dbcomm *db, bool unset, short mode) { struct dvec *v; struct dbcomm *dc; @@ -885,8 +926,7 @@ set(struct plot *plot, struct dbcomm *db, bool unset, short mode) } -static char * -getitright(char *buf, double num) +static char *getitright(char *buf, double num) { char *p; int k; @@ -909,16 +949,14 @@ getitright(char *buf, double num) static int hit, hit2; -void -reset_trace(void) +void reset_trace(void) { hit = -1; hit2 = -1; } -void -gr_iplot(struct plot *plot) +void gr_iplot(struct plot *plot) { struct dbcomm *db; int dontpop; /* So we don't pop w/o push. */ @@ -959,10 +997,12 @@ gr_iplot(struct plot *plot) if (v->v_flags & VF_PRINT) { u = plot->pl_scale; if (len <= 1 || hit <= 0 || hit2 < 0) { - if (len <= 1 || hit2 < 0) + if (len <= 1 || hit2 < 0) { term_clear(); - else + } + else { term_home(); + } hit = 1; hit2 = 1; printf( @@ -975,7 +1015,8 @@ gr_iplot(struct plot *plot) if (isreal(u)) { printf("%s", getitright(buf, u->v_realdata[len - 1])); - } else { + } + else { /* MW. Complex data here, realdata is NULL (why someone use realdata here again) */ printf("%s", @@ -987,12 +1028,14 @@ gr_iplot(struct plot *plot) printf("\n"); } } - if (v == u) + if (v == u) { continue; + } printf("%12s:", v->v_name); if (isreal(v)) { printf("%s", getitright(buf, v->v_realdata[len - 1])); - } else { + } + else { /* MW. Complex data again */ printf("%s", getitright(buf, v->v_compdata[len - 1].cx_real)); printf(", %s", getitright(buf, v->v_compdata[len - 1].cx_imag)); @@ -1014,8 +1057,7 @@ gr_iplot(struct plot *plot) * Note: This is a clear case for separating the linestyle and color * fields from dvec. */ -void -gr_end_iplot(void) +void gr_end_iplot(void) { struct dbcomm *db, *prev, *next; GRAPH *graph; @@ -1034,7 +1076,8 @@ gr_end_iplot(void) ft_curckt->ci_dbs = dbs = next; dbfree1(db); } - } else if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) { + } + else if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) { if (db->db_graphid) { /* get private copy of dvecs */ @@ -1063,8 +1106,7 @@ gr_end_iplot(void) } -double * -readtics(char *string) +double *readtics(char *string) { int k; char *words, *worde; diff --git a/src/frontend/plotting/graphdb.c b/src/frontend/plotting/graphdb.c index 09ba1dc90..a11a6640f 100644 --- a/src/frontend/plotting/graphdb.c +++ b/src/frontend/plotting/graphdb.c @@ -44,232 +44,320 @@ static GBUCKET GBucket[NUMGBUCKETS]; /* note: Zero is not a valid id. This is used in plot() in graf.c. */ static int RunningId = 1; -/* initialize graph structure */ -#define SETGRAPH(pgraph, id) \ - do { \ - (pgraph)->graphid = (id); \ - (pgraph)->degree = 1; \ - (pgraph)->linestyle = -1; \ - } while(0) - - -/* returns NULL on error */ - -GRAPH * -NewGraph(void) +/* Initialize graph structure */ +static inline void setgraph(GRAPH *pgraph, int id) +{ + pgraph->graphid = id; + pgraph->degree = 1; + pgraph->linestyle = -1; +} /* end of function setgraph */ + + + +/* Creates a new graph. Returns NULL on error */ +GRAPH *NewGraph(void) { - GRAPH *pgraph; LISTGRAPH *list; - int BucketId = RunningId % NUMGBUCKETS; + const int BucketId = RunningId % NUMGBUCKETS; if ((list = TMALLOC(LISTGRAPH, 1)) == NULL) { internalerror("can't allocate a listgraph"); - return (NULL); + return (GRAPH *) NULL; } - pgraph = &list->graph; - SETGRAPH(pgraph, RunningId); + GRAPH * const pgraph = &list->graph; + setgraph(pgraph, RunningId); + GBUCKET *p_bucket = GBucket + BucketId; - if (!GBucket[BucketId].list) { - GBucket[BucketId].list = list; - } else { + /* Add to the appropriate bucket at the front of the linked list */ + if (!p_bucket->list) { /* no list yet */ + p_bucket->list = list; + } + else { /* insert at front of current list */ - list->next = GBucket[BucketId].list; - GBucket[BucketId].list = list; + list->next = p_bucket->list; + p_bucket->list = list; } RunningId++; - return (pgraph); -} + return pgraph; +} /* end of function NewGraph */ + /* Given graph id, return graph */ -GRAPH * -FindGraph(int id) +GRAPH *FindGraph(int id) { LISTGRAPH *list; + /* Step through list of graphs until found or list ends */ for (list = GBucket[id % NUMGBUCKETS].list; - list && list->graph.graphid != id; - list = list->next) + list && list->graph.graphid != id; + list = list->next) { ; + } - if (list) - return (&list->graph); - else - return (NULL); -} + if (list) { /* found */ + return &list->graph; + } + else { + return (GRAPH *) NULL; + } +} /* end of function FindGraph */ -GRAPH * -CopyGraph(GRAPH *graph) + +GRAPH *CopyGraph(GRAPH *graph) { GRAPH *ret; - struct _keyed *k; struct dveclist *link, *newlink; - if (!graph) + if (!graph) { return NULL; + } ret = NewGraph(); - memcpy(ret, graph, sizeof(GRAPH)); /* va: compatible pointer types */ - ret->graphid = RunningId - 1; /* restore id */ + { + const int id = ret->graphid; /* save ID of the new graph */ + memcpy(ret, graph, sizeof(GRAPH)); /* copy graph info (inc. ID) */ + ret->graphid = id; /* restore ID */ + } /* copy keyed */ - for (ret->keyed = NULL, k = graph->keyed; k; k = k->next) - SaveText(ret, k->text, k->x, k->y); - - /* copy dvecs */ - ret->plotdata = NULL; - for (link = graph->plotdata; link; link = link->next) { - newlink = TMALLOC(struct dveclist, 1); - newlink->next = ret->plotdata; - newlink->vector = vec_copy(link->vector); - /* vec_copy doesn't set v_color or v_linestyle */ - newlink->vector->v_color = link->vector->v_color; - newlink->vector->v_linestyle = link->vector->v_linestyle; - newlink->vector->v_flags |= VF_PERMANENT; - ret->plotdata = newlink; + { + struct _keyed *k; + for (ret->keyed = NULL, k = graph->keyed; k; k = k->next) { + SaveText(ret, k->text, k->x, k->y); + } } + /* copy dvecs or reuse if "borrowed" already */ + { + struct dveclist *new_plotdata = (struct dveclist *) NULL; + for (link = graph->plotdata; link; link = link->next) { + if (link->f_own_vector) { + struct dvec * const old_vector = link->vector; + struct dvec * const new_vector = vec_copy(old_vector); + /* vec_copy doesn't set v_color or v_linestyle */ + new_vector->v_color = old_vector->v_color; + new_vector->v_linestyle = old_vector->v_linestyle; + new_vector->v_flags |= VF_PERMANENT; + newlink = TMALLOC(struct dveclist, 1); + newlink->next = new_plotdata; + newlink->f_own_vector = TRUE; + newlink->vector = new_vector; + + /* If the link owns the vector, it also owns its scale + * vector, if present */ + struct dvec *old_scale = old_vector->v_scale; + if (old_scale != (struct dvec *) NULL) { + new_plotdata = newlink; /* put in front */ + struct dvec * const new_scale = vec_copy(old_scale); + new_scale->v_flags |= VF_PERMANENT; + newlink = TMALLOC(struct dveclist, 1); + newlink->next = new_plotdata; + newlink->f_own_vector = TRUE; + newlink->vector = new_scale; + newlink->next = new_plotdata; + } + } + else { + newlink->vector = link->vector; + newlink->f_own_vector = FALSE; + } + new_plotdata = newlink; /* put in front */ + } + + ret->plotdata = new_plotdata; /* give vector list to plot */ + } /* end of block copying or reusing dvecs */ + ret->commandline = copy(graph->commandline); ret->plotname = copy(graph->plotname); - return (ret); -} + { + const char * const lbl = graph->grid.xlabel; + if (lbl) { + ret->grid.xlabel = copy(lbl); + } + } + + { + const char * const lbl = graph->grid.ylabel; + if (lbl) { + ret->grid.ylabel = copy(lbl); + } + } + + /* Copy devdep information and size if present */ + { + const void * const p = graph->devdep; + if (p != NULL) { + const size_t n = ret->n_byte_devdep = graph->n_byte_devdep; + void * const dst = ret->devdep = tmalloc(n); + (void) memcpy(dst, graph->devdep, n); + } + } + + return ret; +} /* end of function CopyGraph */ -int -DestroyGraph(int id) + +int DestroyGraph(int id) { - LISTGRAPH *list, *lastlist; - struct _keyed *k, *nextk; - struct dveclist *d, *nextd; - struct dbcomm *db; + /* Locate hash bucket for this graph */ + const int index = id % NUMGBUCKETS; + LISTGRAPH *list = GBucket[index].list; - list = GBucket[id % NUMGBUCKETS].list; - lastlist = NULL; + /* Pointer before current one. Allows fixing list when the current + * node is deleted. Init to NULL to indicate that at head of list */ + LISTGRAPH *lastlist = (LISTGRAPH *) NULL; + + /* Step through graphs in the bucket until the one with id is found */ while (list) { if (list->graph.graphid == id) { /* found it */ + struct _keyed *k, *nextk; + struct dbcomm *db; /* Fix the iplot/trace dbs list */ - for (db = dbs; db && db->db_graphid != id; db = db->db_next) + for (db = dbs; db && db->db_graphid != id; db = db->db_next) { ; - - if (db && (db->db_type == DB_IPLOT || - db->db_type == DB_IPLOTALL)) - { - db->db_type = DB_DEADIPLOT; - /* Delete this later */ - return (0); } - /* adjust bucket pointers */ - if (lastlist) - lastlist->next = list->next; - else - GBucket[id % NUMGBUCKETS].list = list->next; + if (db && (db->db_type == DB_IPLOT || + db->db_type == DB_IPLOTALL)) { + db->db_type = DB_DEADIPLOT; + /* Delete this later */ + return 0; + } - /* run through and de-allocate dynamically allocated keyed list */ + /* Adjust bucket pointers to remove the current node */ + if (lastlist) { /* not at front */ + lastlist->next = list->next; + } + else { /* at front */ + GBucket[index].list = list->next; + } + + /* Run through and de-allocate dynamically allocated keyed list */ k = list->graph.keyed; while (k) { nextk = k->next; - tfree(k->text); - tfree(k); + txfree(k->text); + txfree(k); k = nextk; } - /* de-allocate dveclist */ - d = list->graph.plotdata; - while (d) { - nextd = d->next; - dvec_free(d->vector); - tfree(d); - d = nextd; + /* Free vectors owned by this graph and free the list */ + { + struct dveclist *d = list->graph.plotdata; + struct dveclist *nextd; + while (d != (struct dveclist *) NULL) { + nextd = d->next; + if (d->f_own_vector) { + /* list responsible for freeing this vector */ + dvec_free(d->vector); + } + txfree(d); + d = nextd; + } } - tfree(list->graph.commandline); - tfree(list->graph.plotname); + txfree(list->graph.commandline); + txfree(list->graph.plotname); + txfree(list->graph.grid.xlabel); + txfree(list->graph.grid.ylabel); - /* If device dependent space allocated, free it. */ - if (list->graph.devdep) - tfree(list->graph.devdep); - tfree(list); + /* If device-dependent space was allocated, free it. */ + { + void * const p = list->graph.devdep; + if (p) { + txfree(p); + } + } - return (1); - } - lastlist = list; - list = list->next; - } + txfree(list); + return 1; + } /* end of case that graph ID was found */ + + lastlist = list; /* update previous node */ + list = list->next; /* step to next node */ + } /* end of loop over graphs in the current bucket */ + + /* The graph with ID id was not found */ internalerror("tried to destroy non-existent graph"); - return (0); -} + return 0; +} /* end of function DestroyGraph */ -/* free up all dynamically allocated data structures */ -void -FreeGraphs(void) + +/* Free up all dynamically allocated data structures */ +void FreeGraphs(void) { + /* Iterate over all hash buckets */ GBUCKET *gbucket; - LISTGRAPH *list, *deadl; - for (gbucket = GBucket; gbucket < &GBucket[NUMGBUCKETS]; gbucket++) { - list = gbucket->list; - while (list) { - deadl = list; + LISTGRAPH * list = gbucket->list; /* linked list of graphs here */ + while (list) { /* Free each until end of list */ + LISTGRAPH *deadl = list; list = list->next; - tfree(deadl); + txfree(deadl); } } -} +} /* end of functdion FreeGraphs */ -void -SetGraphContext(int graphid) + +/* This function sets global varial currentgraph based on graphid */ +void SetGraphContext(int graphid) { currentgraph = FindGraph(graphid); -} +} /* end of function SetGraphContext */ + +/* Stack of graph objects implemented as a linked list */ typedef struct gcstack { GRAPH *pgraph; struct gcstack *next; } GCSTACK; -GCSTACK *gcstacktop; +static GCSTACK *gcstacktop; /* top of the stack of graphs */ /* note: This Push and Pop has tricky semantics. Push(graph) will push the currentgraph onto the stack and set currentgraph to graph. - Pop() simply sets currentgraph to the top of the stack and pops stack. + Pop() simply sets currentgraph to previous value at the top of the stack + and pops stack. */ -void -PushGraphContext(GRAPH *graph) +void PushGraphContext(GRAPH *graph) { GCSTACK *gcstack = TMALLOC(GCSTACK, 1); if (!gcstacktop) { gcstacktop = gcstack; - } else { + } + else { gcstack->next = gcstacktop; gcstacktop = gcstack; } gcstacktop->pgraph = currentgraph; currentgraph = graph; -} +} /* end of function PushGraphContext */ -void -PopGraphContext(void) + +void PopGraphContext(void) { - GCSTACK *dead; - - currentgraph = gcstacktop->pgraph; - dead = gcstacktop; + currentgraph = gcstacktop->pgraph; /* pop from stack, making current */ + GCSTACK *dead = gcstacktop; /* remove from stack */ gcstacktop = gcstacktop->next; - tfree(dead); -} + txfree(dead); /* free allocation */ +} /* end of function PopGraphContext */ + + + diff --git a/src/frontend/postcoms.c b/src/frontend/postcoms.c index 846ae1115..0484fcd4a 100644 --- a/src/frontend/postcoms.c +++ b/src/frontend/postcoms.c @@ -835,90 +835,111 @@ done: free_pnode(names); } - -void -com_destroy(wordlist *wl) +/* Free resources associated with "plot" datasets. The wordlist contains + * the names of the plots to delete or the word "all" to delete all but the + * default "const" plot, which cannot be deleted, even by name. If there are + * no names given, the current plot is deleted */ +void com_destroy(wordlist *wl) { - struct plot *pl, *npl = NULL; - + /* If no name given, delete the current output data */ if (!wl) { DelPlotWindows(plot_cur); killplot(plot_cur); - } else if (eq(wl->wl_word, "all")) { + } + else if (eq(wl->wl_word, "all")) { /* "all" -> all plots deleted */ + struct plot *pl, *npl = NULL; for (pl = plot_list; pl; pl = npl) { npl = pl->pl_next; if (!eq(pl->pl_typename, "const")) { DelPlotWindows(pl); killplot(pl); - } else { + } + else { plot_num = 1; } } - } else { + } + else { /* list of plots by name */ while (wl) { - for (pl = plot_list; pl; pl = pl->pl_next) - if (eq(pl->pl_typename, wl->wl_word)) + struct plot *pl; + for (pl = plot_list; pl; pl = pl->pl_next) { + if (eq(pl->pl_typename, wl->wl_word)) { break; + } + } if (pl) { DelPlotWindows(pl); killplot(pl); - } else { + } + else { fprintf(cp_err, "Error: no such plot %s\n", wl->wl_word); } wl = wl->wl_next; } } -} +} /* end of function com_destroy */ -static void -killplot(struct plot *pl) + +static void killplot(struct plot *pl) { - struct dvec *v, *nv = NULL; - struct plot *op; - if (eq(pl->pl_typename, "const")) { fprintf(cp_err, "Error: can't destroy the constant plot\n"); return; } /* pl_dvecs, pl_scale */ - for (v = pl->pl_dvecs; v; v = nv) { - nv = v->v_next; - vec_free(v); + { + struct dvec *v; + struct dvec *nv; + for (v = pl->pl_dvecs; v; v = nv) { + nv = v->v_next; + vec_free(v); + } } + /* unlink from plot_list (linked via pl_next) */ - if (pl == plot_list) { + if (pl == plot_list) { /* First in list */ plot_list = pl->pl_next; - if (pl == plot_cur) + if (pl == plot_cur) { plot_cur = plot_list; - } else { - for (op = plot_list; op; op = op->pl_next) - if (op->pl_next == pl) + } + } + else { /* inside list */ + struct plot *op; + for (op = plot_list; op; op = op->pl_next) { + if (op->pl_next == pl) { break; - if (!op) + } + } + if (!op) { fprintf(cp_err, "Internal Error: kill plot -- not in list\n"); + return; + } op->pl_next = pl->pl_next; - if (pl == plot_cur) + if (pl == plot_cur) { plot_cur = op; + } } /* delete the hash table entry for this plot */ - if (pl->pl_lookup_table) + if (pl->pl_lookup_table) { nghash_free(pl->pl_lookup_table, NULL, NULL); - tfree(pl->pl_title); - tfree(pl->pl_name); - tfree(pl->pl_typename); + } + txfree(pl->pl_title); + txfree(pl->pl_name); + txfree(pl->pl_typename); wl_free(pl->pl_commands); - tfree(pl->pl_date); /* va: also tfree (memory leak) */ - if (pl->pl_ccom) /* va: also tfree (memory leak) */ + txfree(pl->pl_date); /* va: also tfree (memory leak) */ + if (pl->pl_ccom) { /* va: also tfree (memory leak) */ throwaway(pl->pl_ccom); + } if (pl->pl_env) { /* The 'environment' for this plot. */ /* va: HOW to do? */ printf("va: killplot should tfree pl->pl_env=(%p)\n", pl->pl_env); fflush(stdout); } - tfree(pl); /* va: also tfree pl itself (memory leak) */ + txfree(pl); /* va: also tfree pl itself (memory leak) */ } diff --git a/src/frontend/runcoms.c b/src/frontend/runcoms.c index 17c28e032..1c5b629ec 100644 --- a/src/frontend/runcoms.c +++ b/src/frontend/runcoms.c @@ -369,8 +369,7 @@ dosim( /* Usage is run [filename] */ -void -com_run(wordlist *wl) +void com_run(wordlist *wl) { /* ft_getsaves(); */ dosim("run", wl); diff --git a/src/frontend/variable.c b/src/frontend/variable.c index 7c1772cd9..5883b4c0a 100644 --- a/src/frontend/variable.c +++ b/src/frontend/variable.c @@ -938,8 +938,9 @@ wordlist *vareval(/* NOT const */ char *string) break; if (!v) { v = cp_enqvar(string, &tbfreed); - if (tbfreed) + if (tbfreed) { vfree = v; + } } wl = wl_cons(copy(v ? "1" : "0"), NULL); free_struct_variable(vfree); @@ -948,13 +949,16 @@ wordlist *vareval(/* NOT const */ char *string) case '#': string++; - for (v = variables; v; v = v->va_next) - if (eq(v->va_name, string)) + for (v = variables; v; v = v->va_next) { + if (eq(v->va_name, string)) { break; + } + } if (!v) { v = cp_enqvar(string, &tbfreed); - if (tbfreed) + if (tbfreed) { vfree = v; + } } if (!v) { fprintf(cp_err, "Error: %s: no such variable.\n", string); @@ -989,17 +993,20 @@ wordlist *vareval(/* NOT const */ char *string) if (eq(v->va_name, string)) break; if (!v && isdigit_c(*string)) { - for (v = variables; v; v = v->va_next) - if (eq(v->va_name, "argv")) + for (v = variables; v; v = v->va_next) { + if (eq(v->va_name, "argv")) { break; + } + } range = string; } if (!v) { range = NULL; string = oldstring; v = cp_enqvar(string, &tbfreed); - if (tbfreed) + if (tbfreed) { vfree = v; + } } if (!v && (s = getenv(string)) != NULL) { wl = wl_cons(copy(s), NULL); @@ -1020,10 +1027,12 @@ wordlist *vareval(/* NOT const */ char *string) wordlist *r = NULL; if (*range == '$') { char *t = ++range; - if (*t == '&') + if (*t == '&') { t++; - while (isalnum_c(*t)) + } + while (isalnum_c(*t)) { t++; + } *t = '\0'; r = vareval(range); if (!r || r->wl_next) { @@ -1034,15 +1043,20 @@ wordlist *vareval(/* NOT const */ char *string) } range = r->wl_word; } - for (low = 0; isdigit_c(*range); range++) + for (low = 0; isdigit_c(*range); range++) { low = low * 10 + *range - '0'; - if ((*range == '-') && isdigit_c(range[1])) - for (up = 0, range++; isdigit_c(*range); range++) + } + if ((*range == '-') && isdigit_c(range[1])) { + for (up = 0, range++; isdigit_c(*range); range++) { up = up * 10 + *range - '0'; - else if (*range == '-') + } + } + else if (*range == '-') { up = wl_length(wl); - else + } + else { up = low; + } up--, low--; wl = wl_range(wl, low, up); wl_free(r); @@ -1059,23 +1073,23 @@ struct xxx { }; -static int -vcmp(const void *a, const void *b) +static int vcmp(const void *a, const void *b) { int i; struct xxx *v1 = (struct xxx *) a; struct xxx *v2 = (struct xxx *) b; - if ((i = strcmp(v1->x_v->va_name, v2->x_v->va_name)) != 0) - return (i); - else - return (v1->x_char - v2->x_char); + if ((i = strcmp(v1->x_v->va_name, v2->x_v->va_name)) != 0) { + return i; + } + else { + return v1->x_char - v2->x_char; + } } /* Print the values of currently defined variables. */ -void -cp_vprint(void) +void cp_vprint(void) { struct variable *v; struct variable *uv1; @@ -1086,16 +1100,22 @@ cp_vprint(void) uv1 = cp_usrvars(); - for (v = variables, i = 0; v; v = v->va_next) + for (v = variables, i = 0; v; v = v->va_next) { i++; - for (v = uv1; v; v = v->va_next) + } + for (v = uv1; v; v = v->va_next) { i++; - if (plot_cur) - for (v = plot_cur->pl_env; v; v = v->va_next) + } + if (plot_cur) { + for (v = plot_cur->pl_env; v; v = v->va_next) { i++; - if (ft_curckt) - for (v = ft_curckt->ci_vars; v; v = v->va_next) + } + } + if (ft_curckt) { + for (v = ft_curckt->ci_vars; v; v = v->va_next) { i++; + } + } vars = TMALLOC(struct xxx, i); @@ -1108,26 +1128,30 @@ cp_vprint(void) vars[i].x_v = v; vars[i].x_char = '*'; } - if (plot_cur) + if (plot_cur) { for (v = plot_cur->pl_env; v; v = v->va_next, i++) { vars[i].x_v = v; vars[i].x_char = '*'; } - if (ft_curckt) + } + if (ft_curckt) { for (v = ft_curckt->ci_vars; v; v = v->va_next, i++) { vars[i].x_v = v; vars[i].x_char = '+'; } + } qsort(vars, (size_t) i, sizeof(*vars), vcmp); for (j = 0; j < i; j++) { - if (j && eq(vars[j].x_v->va_name, vars[j-1].x_v->va_name)) + if (j && eq(vars[j].x_v->va_name, vars[j-1].x_v->va_name)) { continue; + } v = vars[j].x_v; if (v->va_type == CP_BOOL) { out_printf("%c %s\n", vars[j].x_char, v->va_name); - } else { + } + else { out_printf("%c %s\t", vars[j].x_char, v->va_name); wl = vareval(v->va_name); s = wl_flatten(wl); @@ -1140,88 +1164,86 @@ cp_vprint(void) free_struct_variable(uv1); tfree(vars); -} +} /* end of function cp_vprint */ -struct variable * -var_alloc(char *name, struct variable *next) + + +struct variable *var_alloc(char *name, struct variable *next) { - struct variable *v = TMALLOC(struct variable, 1); - ZERO(v, struct variable); - v -> va_name = name; - v -> va_next = next; - return v; + struct variable * const v = TMALLOC(struct variable, 1); + ZERO(v, struct variable); + v -> va_name = name; + v -> va_next = next; + return v; } -struct variable * -var_alloc_bool(char *name, bool value, struct variable *next) + + +struct variable *var_alloc_bool(char *name, bool value, + struct variable *next) { struct variable *v = var_alloc(name, next); var_set_bool(v, value); return v; } -struct variable * -var_alloc_num(char *name, int value, struct variable *next) +struct variable *var_alloc_num(char *name, int value, + struct variable *next) { struct variable *v = var_alloc(name, next); var_set_num(v, value); return v; } -struct variable * -var_alloc_real(char *name, double value, struct variable *next) +struct variable *var_alloc_real(char *name, double value, + struct variable *next) { struct variable *v = var_alloc(name, next); var_set_real(v, value); return v; } -struct variable * -var_alloc_string(char *name, char * value, struct variable *next) +struct variable *var_alloc_string(char *name, char * value, + struct variable *next) { struct variable *v = var_alloc(name, next); var_set_string(v, value); return v; } -struct variable * -var_alloc_vlist(char *name, struct variable * value, struct variable *next) +struct variable * var_alloc_vlist(char *name, struct variable * value, + struct variable *next) { struct variable *v = var_alloc(name, next); var_set_vlist(v, value); return v; } -void -var_set_bool(struct variable *v, bool value) +void var_set_bool(struct variable *v, bool value) { v->va_type = CP_BOOL; v->va_bool = value; } -void -var_set_num(struct variable *v, int value) +void var_set_num(struct variable *v, int value) { v->va_type = CP_NUM; v->va_num = value; } -void -var_set_real(struct variable *v, double value) +void var_set_real(struct variable *v, double value) { v->va_type = CP_REAL; v->va_real = value; } -void -var_set_string(struct variable *v, char *value) +void var_set_string(struct variable *v, char *value) { v->va_type = CP_STRING; v->va_string = value; } -void -var_set_vlist(struct variable *v, struct variable *value) +void var_set_vlist(struct variable *v, struct variable *value) { v->va_type = CP_LIST; v->va_vlist = value; diff --git a/src/frontend/vectors.c b/src/frontend/vectors.c index 60168fea2..15f994176 100644 --- a/src/frontend/vectors.c +++ b/src/frontend/vectors.c @@ -1065,7 +1065,7 @@ vec_basename(struct dvec *v) } /* get address of plot named 'name' */ -struct plot *get_plot(char* name) +struct plot *get_plot(const char *name) { @@ -1086,9 +1086,7 @@ struct plot *get_plot(char* name) * va: ATTENTION: has unlinked old keyword-class-tree from keywords[CT_VECTOR] * (potentially memory leak) */ - -void -plot_setcur(char *name) +void plot_setcur(const char *name) { struct plot *pl; @@ -1103,28 +1101,43 @@ plot_setcur(char *name) } /* plots are listed in pl in reverse order */ else if (cieq(name, "previous")) { - if (plot_cur->pl_next) + if (plot_cur->pl_next) { plot_cur = plot_cur->pl_next; - else - fprintf(cp_err, "Warning: Switching to previous plot not possible, stay with current plot (%s)\n", plot_cur->pl_typename); - return; - } else if (cieq(name, "next")) { - struct plot *prev_pl = NULL; - for (pl = plot_list; pl; pl = pl->pl_next) { - if (pl == plot_cur) - break; - prev_pl = pl; } - if (!prev_pl) { - fprintf(cp_err, "Warning: Switching to next plot not possible, stay with current plot (%s)\n", plot_cur->pl_typename); - return; + else { + fprintf(cp_err, + "Warning: No previous plot is available. " + "Plot remains unchanged (%s).\n", + plot_cur->pl_typename); } - plot_cur = prev_pl; return; } - pl = get_plot(name); - if (!pl) + else if (cieq(name, "next")) { + /* Step through the list, which has plots in reverse order */ + struct plot *prev_pl = NULL; + for (pl = plot_list; pl; pl = pl->pl_next) { + if (pl == plot_cur) { + break; + } + prev_pl = pl; + } + if (prev_pl) { /* found */ + plot_cur = prev_pl; + } + else { /* no next plot */ + fprintf(cp_err, + "Warning: No next plot is available. " + "Plot remains unchanged (%s).\n", + plot_cur->pl_typename); + } return; + } + + pl = get_plot(name); + if (!pl) { + return; + } + /* va: we skip cp_kwswitch, because it confuses the keyword-tree management for * repeated op-commands. When however cp_kwswitch is necessary for other * reasons, we should hold the original keyword table pointer in an @@ -1135,15 +1148,14 @@ plot_setcur(char *name) } */ plot_cur = pl; -} +} /* end of function plot_setcur */ + /* Add a plot to the plot list. This is different from plot_add() in that * all this does is update the list and the variable $plots. */ - -void -plot_new(struct plot *pl) +void plot_new(struct plot *pl) { pl->pl_next = plot_list; plot_list = pl; diff --git a/src/include/ngspice/bool.h b/src/include/ngspice/bool.h index 6bdc0df8e..873b1a15e 100644 --- a/src/include/ngspice/bool.h +++ b/src/include/ngspice/bool.h @@ -4,7 +4,7 @@ //typedef unsigned char bool; typedef int bool; -typedef int BOOL ; +typedef int BOOL; #define BOOLEAN int #define TRUE 1 diff --git a/src/include/ngspice/dvec.h b/src/include/ngspice/dvec.h index e24aa066f..888e31242 100644 --- a/src/include/ngspice/dvec.h +++ b/src/include/ngspice/dvec.h @@ -1,6 +1,7 @@ #ifndef ngspice_DVEC_H #define ngspice_DVEC_H +#include "ngspice/bool.h" #include "ngspice/complex.h" #include "ngspice/grid.h" #include "ngspice/sim.h" @@ -8,14 +9,14 @@ /* Dvec flags. */ enum dvec_flags { - VF_REAL = (1 << 0), /* The data is real. */ - VF_COMPLEX = (1 << 1), /* The data is complex. */ - VF_ACCUM = (1 << 2), /* writedata should save this vector. */ - VF_PLOT = (1 << 3), /* writedata should incrementally plot it. */ - VF_PRINT = (1 << 4), /* writedata should print this vector. */ - VF_MINGIVEN = (1 << 5), /* The v_minsignal value is valid. */ - VF_MAXGIVEN = (1 << 6), /* The v_maxsignal value is valid. */ - VF_PERMANENT = (1 << 7) /* Don't garbage collect this vector. */ + VF_REAL = (1 << 0), /* The data is real. */ + VF_COMPLEX = (1 << 1), /* The data is complex. */ + VF_ACCUM = (1 << 2), /* writedata should save this vector. */ + VF_PLOT = (1 << 3), /* writedata should incrementally plot it. */ + VF_PRINT = (1 << 4), /* writedata should print this vector. */ + VF_MINGIVEN = (1 << 5), /* The v_minsignal value is valid. */ + VF_MAXGIVEN = (1 << 6), /* The v_maxsignal value is valid. */ + VF_PERMANENT = (1 << 7) /* Don't garbage collect this vector. */ }; @@ -36,41 +37,46 @@ typedef enum { #define MAXDIMS 8 struct dvec { - char *v_name; /* Same as so_vname. */ + char *v_name; /* Same as so_vname. */ enum simulation_types v_type; /* Same as so_vtype. */ - short v_flags; /* Flags (a combination of VF_*). */ - double *v_realdata; /* Real data. */ - ngcomplex_t *v_compdata; /* Complex data. */ - double v_minsignal; /* Minimum value to plot. */ - double v_maxsignal; /* Maximum value to plot. */ - GRIDTYPE v_gridtype; /* One of GRID_*. */ - PLOTTYPE v_plottype; /* One of PLOT_*. */ - int v_length; /* Length of the vector. */ - int v_alloc_length; /* How much has been actually allocated. */ - int v_rlength; /* How much space we really have. Used as binary flag */ - int v_outindex; /* Index if writedata is building the - vector. */ - int v_linestyle; /* What line style we are using. */ - int v_color; /* What color we are using. */ - char *v_defcolor; /* The name of a color to use. */ - int v_numdims; /* How many dims -- 0 = scalar (len = 1). */ - int v_dims[MAXDIMS]; /* The actual size in each dimension. */ - struct plot *v_plot; /* The plot structure (if it has one). */ - struct dvec *v_next; /* Link for list of plot vectors. */ - struct dvec *v_link2; /* Extra link for things like print. */ - struct dvec *v_scale; /* If this has a non-standard scale... */ + short v_flags; /* Flags (a combination of VF_*). */ + double *v_realdata; /* Real data. */ + ngcomplex_t *v_compdata; /* Complex data. */ + double v_minsignal; /* Minimum value to plot. */ + double v_maxsignal; /* Maximum value to plot. */ + GRIDTYPE v_gridtype; /* One of GRID_*. */ + PLOTTYPE v_plottype; /* One of PLOT_*. */ + int v_length; /* Length of the vector. */ + int v_alloc_length; /* How much has been actually allocated. */ + int v_rlength; /* How much space we really have. Used as binary flag */ + int v_outindex; /* Index if writedata is building the vector. */ + int v_linestyle; /* What line style we are using. */ + int v_color; /* What color we are using. */ + char *v_defcolor; /* The name of a color to use. */ + int v_numdims; /* How many dims -- 0 = scalar (len = 1). */ + int v_dims[MAXDIMS]; /* The actual size in each dimension. */ + struct plot *v_plot; /* The plot structure (if it has one). */ + struct dvec *v_next; /* Link for list of plot vectors. */ + struct dvec *v_link2; /* Extra link for things like print. */ + struct dvec *v_scale; /* If this has a non-standard scale... */ } ; -#define isreal(v) ((v)->v_flags & VF_REAL) +#define isreal(v) ((v)->v_flags & VF_REAL) #define iscomplex(v) ((v)->v_flags & VF_COMPLEX) /* list of data vectors being displayed */ struct dveclist { - struct dvec *vector; struct dveclist *next; + struct dvec *vector; + + /* Flag that this list owns the vector in the sense that it is + * responsible for freeing it. Depending on how the entry was created, + * it either made its own copy or "borrowed" one from anothe use. */ + bool f_own_vector; }; -struct dvec *dvec_alloc(char *name, int type, short flags, int length, void *storage); +struct dvec *dvec_alloc(const char *name, + int type, short flags, int length, void *storage); void dvec_realloc(struct dvec *v, int length, void *storage); void dvec_extend(struct dvec *v, int length); void dvec_trunc(struct dvec *v, int length); diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index e7f405f49..08e5e5449 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -359,8 +359,8 @@ extern void ft_loadfile(char *file); extern void vec_new(struct dvec *d); extern void plot_docoms(wordlist *wl); extern void vec_remove(const char *name); -extern void plot_setcur(char *name); -extern struct plot *get_plot(char* name); +extern void plot_setcur(const char *name); +extern struct plot *get_plot(const char *name); extern void plot_new(struct plot *pl); extern char *vec_basename(struct dvec *v); extern void vec_transpose(struct dvec *v); From 1b43d16594e6a293afffffb622a7701553b76404 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Thu, 12 Dec 2019 19:44:09 -0500 Subject: [PATCH 32/58] Fixed access of freed memory when reporting an error. --- src/frontend/streams.c | 104 +++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/src/frontend/streams.c b/src/frontend/streams.c index 8558220b1..67bc420b6 100644 --- a/src/frontend/streams.c +++ b/src/frontend/streams.c @@ -29,21 +29,21 @@ FILE *cp_curout = NULL; FILE *cp_curerr = NULL; -static bool -fileexists(char *name) +static bool fileexists(const char *name) { #ifdef HAVE_ACCESS - if (access(name, 0) == 0) - return (TRUE); + if (access(name, 0) == 0) { + return TRUE; + } #endif - return (FALSE); + return FALSE; } + /* This routine sets the cp_{in,out,err} pointers and takes the io * directions out of the command line. */ -wordlist * -cp_redirect(wordlist *wl) +wordlist *cp_redirect(wordlist *wl) { int gotinput = 0, gotoutput = 0, goterror = 0, append = 0; wordlist *w; @@ -77,8 +77,9 @@ cp_redirect(wordlist *wl) w = w->wl_next; #ifdef CPDEBUG - if (cp_debug) + if (cp_debug) { fprintf(cp_err, "Input file is %s...\n", fname); + } #endif fp = fopen(fname, "r"); @@ -87,18 +88,19 @@ cp_redirect(wordlist *wl) tfree(fname); goto error; } - - tfree(fname); + + tfree(fname); cp_in = fp; /* special case for set command: keep i/o information (handled in com_set.c) */ wordlist* bw = beg->wl_prev->wl_prev; - if (!(bw && cieq(bw->wl_word, "set"))) + if (!(bw && cieq(bw->wl_word, "set"))) { wl_delete_slice(beg, w); + } - } else if (*w->wl_word == cp_gt) { - + } + else if (*w->wl_word == cp_gt) { wordlist *beg = w; if (gotoutput++) { @@ -115,14 +117,14 @@ cp_redirect(wordlist *wl) if (w && *w->wl_word == cp_amp) { if (goterror++) { fprintf(cp_err, "Error: ambiguous error redirect.\n"); - return (NULL); + return (wordlist *) NULL; } w = w->wl_next; } if (!w) { fprintf(cp_err, "Error: missing name for output.\n"); - return (NULL); + return (wordlist *) NULL; } fname = cp_unquote(w->wl_word); @@ -140,31 +142,35 @@ cp_redirect(wordlist *wl) } fp = fopen(fname, append ? "a" : "w+"); - tfree(fname); if (!fp) { + tfree(fname); perror(fname); goto error; } + tfree(fname); cp_out = fp; - if (goterror) + if (goterror) { cp_err = fp; + } out_isatty = FALSE; wl_delete_slice(beg, w); - } else { + } + else { w = w->wl_next; } } - return (wl); + return wl; error: wl_free(wl); /* FIXME, Ouch !! */ - return (NULL); -} + return (wordlist *) NULL; +} /* end of function cp_redirect */ + /* Reset the cp_* FILE pointers to the standard ones. This is tricky, @@ -174,19 +180,23 @@ error: * bar" where foo is a script, and it has redirections of its own * inside of it, none of the output from foo will get sent to * stdout... */ - -void -cp_ioreset(void) +void cp_ioreset(void) { - if (cp_in != cp_curin) - if (cp_in) + if (cp_in != cp_curin) { + if (cp_in) { fclose(cp_in); - if (cp_out != cp_curout) - if (cp_out) + } + } + if (cp_out != cp_curout) { + if (cp_out) { fclose(cp_out); - if (cp_err != cp_curerr) - if (cp_err && cp_err != cp_out) + } + } + if (cp_err != cp_curerr) { + if (cp_err && cp_err != cp_out) { fclose(cp_err); + } + } cp_in = cp_curin; cp_out = cp_curout; @@ -194,18 +204,30 @@ cp_ioreset(void) /*** Minor bug here... */ out_isatty = TRUE; -} +} /* end of function cp_ioreset */ + /* Do this only right before an exec, since we lose the old std*'s. */ - -void -fixdescriptors(void) +void fixdescriptors(void) { - if (cp_in != stdin) - dup2(fileno(cp_in), fileno(stdin)); - if (cp_out != stdout) - dup2(fileno(cp_out), fileno(stdout)); - if (cp_err != stderr) - dup2(fileno(cp_err), fileno(stderr)); -} + bool dup2_fail = FALSE; + if (cp_in != stdin) { + dup2_fail |= dup2(fileno(cp_in), fileno(stdin)) == -1; + } + if (cp_out != stdout) { + dup2_fail |= dup2(fileno(cp_out), fileno(stdout)) == -1; + } + if (cp_err != stderr) { + dup2_fail |= dup2(fileno(cp_err), fileno(stderr)) == -1; + } + + /* Warn if there was some failure */ + if (dup2_fail) { + (void) fprintf(cp_err, + "I/O descriptior failure: %s.\n", strerror(errno)); + } +} /* end of function fixdescriptors */ + + + From 0bff1592c77bf9947ed4c518c8e817f28eab5aee Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Thu, 12 Dec 2019 20:07:45 -0500 Subject: [PATCH 33/58] Fixed checking for redirection tokens --- src/frontend/streams.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/frontend/streams.c b/src/frontend/streams.c index 67bc420b6..730d5658b 100644 --- a/src/frontend/streams.c +++ b/src/frontend/streams.c @@ -47,13 +47,13 @@ wordlist *cp_redirect(wordlist *wl) { int gotinput = 0, gotoutput = 0, goterror = 0, append = 0; wordlist *w; - char *fname; FILE *fp; w = wl->wl_next; /* Don't consider empty commands. */ while (w) { - if (*w->wl_word == cp_lt) { + char *fname; + if (*w->wl_word == cp_lt && w->wl_word[1] == '\0') { wordlist *beg = w; @@ -63,14 +63,14 @@ wordlist *cp_redirect(wordlist *wl) } w = w->wl_next; - if (w && *w->wl_word == cp_lt) { + if (w && *w->wl_word == cp_lt && w->wl_word[1] == '\0') { fprintf(cp_err, "Error: `<<' redirection is not implemented.\n"); goto error; } if (!w) { fprintf(cp_err, "Error: missing name for input.\n"); - return (NULL); + return (wordlist *) NULL; } fname = cp_unquote(w->wl_word); @@ -100,7 +100,7 @@ wordlist *cp_redirect(wordlist *wl) } } - else if (*w->wl_word == cp_gt) { + else if (*w->wl_word == cp_gt && w->wl_word[1] == '\0') { wordlist *beg = w; if (gotoutput++) { @@ -109,7 +109,7 @@ wordlist *cp_redirect(wordlist *wl) } w = w->wl_next; - if (w && *w->wl_word == cp_gt) { + if (w && *w->wl_word == cp_gt && w->wl_word[1] == '\0') { append++; w = w->wl_next; } @@ -163,7 +163,7 @@ wordlist *cp_redirect(wordlist *wl) else { w = w->wl_next; } - } + } /* end of loop over arguments */ return wl; error: From f9e6664929164e11f5fa2fdce778d652e234c6ec Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sat, 14 Dec 2019 01:13:13 -0500 Subject: [PATCH 34/58] Fixed resolution of ~ to home directory. (Bug #405) Also fixed potential buffer overruns in glob expansion --- src/frontend/parser/glob.c | 842 +++++++++++++++++++++++++++++------- src/frontend/streams.c | 2 + src/include/ngspice/cpstd.h | 2 +- src/misc/tilde.c | 267 ++++++++---- src/misc/tilde.h | 8 +- 5 files changed, 888 insertions(+), 233 deletions(-) diff --git a/src/frontend/parser/glob.c b/src/frontend/parser/glob.c index ac32b642a..4cdd8c8f0 100644 --- a/src/frontend/parser/glob.c +++ b/src/frontend/parser/glob.c @@ -6,9 +6,12 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group /* * Expand global characters. */ +#include #include "ngspice/ngspice.h" #include "ngspice/cpdefs.h" +#include "ngspice/wordlist.h" +#include "../misc/tilde.h" #include "glob.h" #ifdef HAVE_SYS_DIR_H @@ -30,198 +33,725 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group #include #endif +#define OPT_WLL_COPY_ALL 1 +/* This structure is a "long-form" of the wordlist structure. The inital + * wordlist structure fields have the same meanings as in a standalone + * wordlist structure, except that the allocations for p_escape and + * p_after are separate from the one for wl_word. This structure is useful + * when a wordlist must undergo many modifications to its words, as when + * globbing is being expanded */ +typedef struct wordlist_l { + struct wordlist wl; + size_t n_char_word; /* length of word excluding null */ + size_t n_elem_word_alloc; /* Allocated size of word array */ +} wordlist_l; + +static void wl_modify_word(wordlist *wl_node, unsigned int n_input, + const size_t *p_n_char_word, char **pp_worde); +static wordlist *wll_to_wl(const wordlist_l *wll); +static wordlist_l *wll_append(wordlist_l *wl_dst, wordlist_l *wl_to_append); +static void wll_append_to_node(wordlist_l *dst, const wordlist_l *to_append); +static wordlist_l *wll_cons( + size_t n_elem_word_alloc, size_t n_char_word, const char *p_word, + unsigned int opt, wordlist_l *tail); +static void wll_free(wordlist_l *wll); +static wordlist *wll_node_to_wl_node(const wordlist_l *wll); char cp_comma = ','; char cp_ocurl = '{'; char cp_ccurl = '}'; char cp_til = '~'; -static wordlist *bracexpand(char *string); -static wordlist *brac1(char *string); -static wordlist *brac2(char *string); +static wordlist_l *brac1(size_t offset_ocurl1, const char *p_str_cur); +static wordlist_l *brac2(const char *string, + size_t *p_n_char_processed); +static wordlist *bracexpand(const wordlist *w_exp); +static void merge_home_with_rest(wordlist *wl_node, + size_t n_char_home, const char *sz_home, size_t n_char_skip); +static inline void strip_1st_char(wordlist *wl_node); +static void tilde_expand_word(wordlist *wl_node); /* For each word, go through two steps: expand the {}'s, and then do ?*[] * globbing in them. Sort after the second phase but not the first... - */ - -/* MW. Now only tilde is supported, {}*? don't work */ - -wordlist * -cp_doglob(wordlist *wlist) + * + * Globbing of arbitrary levels of brace nesting and tilde expansion to the + * name of a "HOME" directory are supported. ?*[] are not */ +wordlist *cp_doglob(wordlist *wlist) { - wordlist *wl; - char *s; - /* Expand {a,b,c} */ - - for (wl = wlist; wl; wl = wl->wl_next) { - wordlist *nwl, *w = bracexpand(wl->wl_word); - if (!w) { - wlist->wl_word = NULL; /* XXX */ - return (wlist); - } - nwl = wl_splice(wl, w); - if (wlist == wl) - wlist = w; - wl = nwl; - } - - /* Do tilde expansion. */ - - for (wl = wlist; wl; wl = wl->wl_next) - if (*wl->wl_word == cp_til) { - s = cp_tildexpand(wl->wl_word); - txfree(wl->wl_word); /* sjb - fix memory leak */ - if (!s) - wl->wl_word = copy(""); /* MW. We Con't touch tmalloc addres */ - else - wl->wl_word = s; - } - - return (wlist); -} - - -static wordlist * -bracexpand(char *string) -{ - wordlist *wl, *w; - char *s; - - if (!string) - return (NULL); - wl = brac1(string); - if (!wl) - return (NULL); - for (w = wl; w; w = w->wl_next) { - s = w->wl_word; - w->wl_word = copy(s); - tfree(s); - } - return (wl); -} - -/* Given a string, returns a wordlist of all the {} expansions. This is - * called recursively by cp_brac2(). All the words here will be of size - * BSIZE_SP, so it is a good idea to copy() and free() the old words. - */ - -static wordlist * -brac1(char *string) -{ - wordlist *words, *wl, *w, *nw, *nwl, *newwl; - char *s; - int nb; - - words = wl_cons(TMALLOC(char, BSIZE_SP), NULL); - words->wl_word[0] = '\0'; - for (s = string; *s; s++) { - if (*s == cp_ocurl) { - nwl = brac2(s); - nb = 0; - for (;;) { - if (*s == cp_ocurl) - nb++; - if (*s == cp_ccurl) - nb--; - if (*s == '\0') { - fprintf(cp_err, "Error: missing }.\n"); - return (NULL); - } - if (nb == 0) - break; - s++; + { + wordlist *wl = wlist; + while (wl != (wordlist *) NULL) { + wordlist *w = bracexpand(wl); + if (!w) { + wl_free(wlist); + return (wordlist *) NULL; } - /* Add nwl to the rest of the strings in words. */ - newwl = NULL; - for (wl = words; wl; wl = wl->wl_next) - for (w = nwl; w; w = w->wl_next) { - nw = wl_cons(TMALLOC(char, BSIZE_SP), NULL); - (void) strcpy(nw->wl_word, wl->wl_word); - (void) strcat(nw->wl_word, w->wl_word); - newwl = wl_append(newwl, nw); + + /* Replace the node that was just expanded, wl, with the + * expansion w (if different) and continue after that */ + if (wl != w) { + wordlist *wl_next = wl->wl_next; + (void) wl_splice(wl, w); + + /* Update head of list if the replacement + * changed it */ + if (wlist == wl) { + wlist = w; } - wl_free(words); - wl_free(nwl); - words = newwl; - } else { - for (wl = words; wl; wl = wl->wl_next) - appendc(wl->wl_word, *s); + + /* Continue after the spliced nodes since + * they are already fully expanded */ + wl = wl_next; + } + else { /* same node, so just step to the next node */ + wl = wl->wl_next; + } + } /* end of loop over words in wordlist */ + } /* end of block expanding braces */ + + /* Do tilde expansion on each word. */ + { + wordlist *wl; + for (wl = wlist; wl; wl = wl->wl_next) { + if (*wl->wl_word == cp_til) { + tilde_expand_word(wl); + } + } /* end of loop over words in wordlist */ + } /* end of block expanding braces */ + + return wlist; +} /* end of function cp_doglob */ + + + +static wordlist *bracexpand(const wordlist *w_exp) +{ + const char * const wl_word = w_exp->wl_word; + + /* If no string, nothing to expand */ + if (wl_word == (char *) NULL) { + return (wordlist *) NULL; + } + + + /* Find first opening brace. If none, the string expands to itself as + * a wordlist */ + size_t offset_ocurl = ~(size_t) 0; /* flag for not found */ + + /* Loop until find opening brace or end of string */ + { + const char *p_cur = wl_word; + char ch_cur; + for ( ; (ch_cur = *p_cur) != '\0'; p_cur++) { + if (ch_cur == cp_ocurl) { + offset_ocurl = p_cur - wl_word; + break; + } } } - return (words); -} + + + /* Test for '{' and glob if there is one */ + if (offset_ocurl != ~(size_t) 0) { + /* Found a brace, so glob */ + wordlist_l *wll_glob = brac1(offset_ocurl, wl_word); + + wordlist *wl_glob = wll_to_wl(wll_glob); + wll_free(wll_glob); + return wl_glob; + } + + /* Unescaped '{' not found, so return the input node */ + return (wordlist *) w_exp; +} /* end of function bracexpand */ + + + +/* Given a string, returns a wordlist of all the {} expansions. This function + * calls cp_brac2() with braced expressions and is called recursively by + * cp_brac2(). + * + * Parameters + * offset_ocurl1: Offset from p_str where the first opening brace occurs + * or the offset to the terminating null of p_str (length of the + * string) if it contains no opening brace. + */ +static wordlist_l *brac1(size_t offset_ocurl1, const char *p_str) +{ + wordlist_l *words; + const char *s; + + /* Create the inital entry in the list using all of the characters + * before the first '{' */ + { + const size_t n_byte_alloc = BSIZE_SP + 1; + + words = wll_cons(n_byte_alloc, offset_ocurl1, p_str, + OPT_WLL_COPY_ALL, (wordlist_l *) NULL); + } + + /* Step through string. In each iteration one {} group and the ungrouped + * characters following that group, if any are processed */ + for (s = p_str + offset_ocurl1; *s != '\0'; ) { + { /* Process braced expression */ + size_t n_char_processed; + + /* Process braced list using brac2() */ + wordlist_l *nwl = brac2(s, + &n_char_processed); + if (nwl == (wordlist_l *) NULL) { + /* brac2() already printed an error message */ + wll_free(words); + return (wordlist_l *) NULL; + } + + /* New wordlist to replace existing words. Note + * the number of nodes is + * #(existing list) X #(brac2() list). Each of + * the brac2() words is appended to each of the + * existing words to form the new wordlist */ + wordlist_l *newwl = (wordlist_l *) NULL; + + /* For each word in the existing word list (words) */ + wordlist_l *wl; /* loop iterator */ + for (wl = words; wl; wl = (wordlist_l *) wl->wl.wl_next) { + + /* For each word in the word list from brac2() */ + wordlist_l *w; /* loop iterator */ + for (w = nwl; w; w = (wordlist_l *) w->wl.wl_next) { + wordlist_l *nw = wll_cons( + BSIZE_SP + 1, 0, (char *) NULL, + OPT_WLL_COPY_ALL, (wordlist_l *) NULL); + wll_append_to_node(nw, wl); + wll_append_to_node(nw, w); + newwl = wll_append(newwl, nw); + } /* end of loop over words from brac2() */ + } /* end of loop over words */ + wll_free(words); + wll_free(nwl); + words = newwl; + s += n_char_processed; /* skip braced list */ + } /* end of processing of braced expression */ + + { + /* Apend all chars after {} expression until the next + * '{' or the end of the word to each word in the wordlist */ + const char * const p_start = s; + char ch_cur; + for ( ; (ch_cur = *s) != cp_ocurl; s++) { + if (ch_cur == '\0') { + break; + } + } + + const size_t n_char_append = s - p_start; + + if (n_char_append > 0) { + wordlist_l *wl; + for (wl = words; wl; wl = (wordlist_l *) wl->wl.wl_next) { + const size_t n_char_total = wl->n_char_word + + n_char_append; + const size_t n_elem_needed = n_char_total + 1; + if (wl->n_elem_word_alloc < n_elem_needed) { + const size_t n_elem_alloc = 2 * n_elem_needed; + wl->wl.wl_word = TREALLOC(char, wl->wl.wl_word, + n_elem_alloc); + wl->n_elem_word_alloc = n_elem_alloc; + } + char *p_dst = wl->wl.wl_word + wl->n_char_word; + (void) memcpy(p_dst, p_start, n_char_append); + p_dst += n_char_append; + *p_dst = '\0'; + wl->n_char_word = n_char_total; + } + } + } /* end of characters after braced expression */ + } /* end of loop over braced expressions + following chars in string */ + + return words; +} /* end of function brac1 */ + + /* Given a string starting with a {, return a wordlist of the expansions - * for the text until the matching }. + * for the text until the matching }. A vaild input string must have both + * an opening brace and a closing brace. If an error occurs, NULL is + * returned. On a successful return, *p_n_char_processed will contain the + * number of characters processed by brac2 up to and including the closing + * outermost brace */ - -static wordlist * -brac2(char *string) +static wordlist_l *brac2(const char *string, + size_t *p_n_char_processed) { - wordlist *wlist = NULL, *nwl; - char buf[BSIZE_SP], *s; - int nb; - bool eflag = FALSE; + wordlist_l *wlist = (wordlist_l *) NULL; + char buf_fixed[BSIZE_SP]; /* default work buffer */ + char *buf = buf_fixed; /* actual work buffer */ + bool eflag = FALSE; /* end-of-processing flag */ + + /* Required buffer size. Note that 1st char of string is not copied, + * so strlen(string) includes the length of the null at the end */ + const size_t n_elem_needed = strlen(string); + + /* Allocate and use a larger buffer if required */ + if (n_elem_needed > BSIZE_SP) { /* will not fit in stack buffer */ + buf = TMALLOC(char, n_elem_needed); + } string++; /* Get past the first open brace... */ - for (;;) { - (void) strcpy(buf, string); - nb = 0; - s = buf; - for (;;) { - if ((*s == cp_ccurl) && (nb == 0)) { - eflag = TRUE; - break; - } - if ((*s == cp_comma) && (nb == 0)) - break; - if (*s == cp_ocurl) - nb++; - if (*s == cp_ccurl) + (void) strcpy(buf, string); /* make a copy of string */ + char *buf_cur = buf; /* current position in buffer */ + + /* Each iteration of the outer loop processes one comma-separated + * expression of the top-level brace-enclosed list */ + for ( ; ; ) { + int nb = 0; /* number of braces **inside 1st brace** */ + size_t offset_ocurl1 = SIZE_MAX; /* Offset to 1st '{' */ + + /* Start processing at start of next top-level term */ + char *s = buf_cur; + + /* Scan the string until the next comma at the top level is found, + * the closing brace at the top level is found or the string ends + * with a missing brace. If another term is found, it is processed + * as a null-terminated string by calling brac1() */ + for ( ; ; ) { + const char ch_cur = *s; + if (ch_cur == cp_ccurl) { /* closing brace found */ + if (nb == 0) { + /* A closing brace found without any internal opening + * braces, so this one is the outermost brace */ + eflag = TRUE; /* done -- set end flag */ + break; + } + /* Else closing brace of internal level */ nb--; - if (*s == '\0') { - fprintf(cp_err, "Error: missing }.\n"); - return (NULL); } - s++; - } + else if (ch_cur == cp_ocurl) { /* another brace level started */ + if (nb++ == 0) { /* Inc count. If 1st '{', save offset */ + offset_ocurl1 = s - buf_cur; + } + } + else if ((ch_cur == cp_comma) && (nb == 0)) { + /* Comma found outside of any internal braced + * expression */ + break; + } + + /* Check if reached end of string. If so, the closing + * brace was not present. */ + if (ch_cur == '\0') { + fprintf(cp_err, "Error: missing }.\n"); + if (buf != buf_fixed) { /* free allocation if made */ + txfree(buf); + } + if (wlist != (wordlist_l *) NULL) { + wll_free(wlist); + } + + return (wordlist_l *) NULL; + } + s++; /* process next char */ + } /* end of loop finding end of braces */ + + /* The above loop exits without returning from the function if either + * a comma at the top level or the closing top level brace is reached. + * Variable s points to this location. By setting it to null, a string + * is created for one of the comma-separated expressions at the top + * level */ *s = '\0'; - nwl = brac1(buf); - wlist = wl_append(wlist, nwl); - string += s - buf + 1; - if (eflag) - return (wlist); - } -} + + /* Process the top-level expression found and append to the + * wordlist being built */ + { + wordlist_l *nwl = brac1( + offset_ocurl1 == SIZE_MAX ? s - buf_cur : offset_ocurl1, + buf_cur); + wlist = wll_append(wlist, nwl); + } + + /* Check for competion of processing */ + if (eflag) { /* done -- normal function exit */ + if (buf != buf_fixed) { /* free allocation if made */ + txfree(buf); + } + + /* When the loop is exited, s is at a brace or comma, which + * is also considered to be processed. Hence +2 not +1. */ + *p_n_char_processed = s - buf + 2; + return wlist; + } + + buf_cur = s + 1; /* go to next term after comma */ + } /* end of loop over top-level comma-separated expressions */ +} /* end of function brac2 */ + + /* Expand tildes. */ - -char * -cp_tildexpand(char *string) +char *cp_tildexpand(char *string) { - char *result; + /* Attempt to do the tilde expansion */ + char * const result = tildexpand(string); - result = tildexpand(string); - - if (!result) { - if (cp_nonomatch) + if (!result) { /* expansion failed */ + if (cp_nonomatch) { /* If set, should return the original string */ return copy(string); - else - return NULL; + } + /* Else should return NULL to indiciate failure */ + return (char *) NULL; } - return result; -} + return result; /* successful expansion returned */ +} /* end of function cp_tildexpand */ + /* Say whether the pattern p can match the string s. */ /* MW. Now simply compare strings */ -bool -cp_globmatch(char *p, char *s) +bool cp_globmatch(char *p, char *s) { - return (!(strcmp(p, s))); -} + return !(strcmp(p, s)); +} /* end of function cp_globmatch */ + + + +/* This function expands the leading ~ of wl_node. */ +static void tilde_expand_word(wordlist *wl_node) +{ + char *word = wl_node->wl_word; + char *p_char_cur = ++word; + char ch = *p_char_cur; + if (ch == '\0' || ch == DIR_TERM) { + char *sz_home; + const int n_char_home = get_local_home(0, &sz_home); + if (n_char_home < 0) { /* expansion failed */ + /* Strip the ~ and return the rest */ + strip_1st_char(wl_node); + return; + } + merge_home_with_rest(wl_node, (size_t) n_char_home, sz_home, 1); + return; + } + +#ifdef HAVE_PWD_H + /* ~bob -- Get name of user and find home for that user */ + { + char * const usr_start = wl_node->wl_word + 1; + char *usr_end = usr_start; + char c; + while ((c = *usr_end) != '\0' && c != DIR_TERM) { + ++usr_end; + } + const size_t n_char_usr = usr_end - usr_start; + const size_t n_byte_usr = n_char_usr + 1; + const char c_orig = c; /* save char to be overwritten by '\0' */ + *usr_end = '\0'; + + char *sz_home; + const int n_char_home = get_usr_home(usr_start, 0, &sz_home); + *usr_end = c_orig; /* restore char overwritten by '\0' */ + if (n_char_home < 0) { + strip_1st_char(wl_node); + return; /* Strip the ~ and return the rest */ + } + merge_home_with_rest(wl_node, (size_t) n_char_home, sz_home, + n_char_usr + 1); + return; + } + +#else + /* ~bob is meaningless. Strip the ~ and return the rest */ + strip_1st_char(wl_node); + return; +#endif +} /* end of function tilde_expand_word */ + + + +/* Strip the 1st char. Equivalent to merging an empty HOME string with the + * chars after the 1st char. Assumes string is at least 1 char long + * excluding trailing NULL */ +static inline void strip_1st_char(wordlist *wl_node) +{ + merge_home_with_rest(wl_node, 0, (char *) NULL, 1); + return; +} /* end of function strip_1st_char */ + + + +/* This function modifies the wordlist node to consist of + + * rest of string after n_char_skip. It is assumed that the string + * is at least n_char_skip characters long excluding the trailing null */ +static inline void merge_home_with_rest(wordlist *wl_node, + size_t n_char_home, const char *sz_home, size_t n_char_skip) +{ + size_t p_n_char_word[2]; + p_n_char_word[0] = n_char_home; + p_n_char_word[1] = strlen(wl_node->wl_word) - n_char_skip; + + char *pp_word[2]; + pp_word[0] = (char *) sz_home; + pp_word[1] = wl_node->wl_word + n_char_skip; + + wl_modify_word(wl_node, 2u, p_n_char_word, pp_word); + return; +} /* end of function merge_home_with_rest */ + + + + +/*** Long-form wordlist functions ***/ + +/* This function converts long-form wordlist wll to a standard wordlist. + * The input long-form list is not modified. */ +static wordlist *wll_to_wl(const wordlist_l *wll) +{ + /* Handle degnerate case of NULL input */ + if (wll == (wordlist_l *) NULL) { + return (wordlist *) NULL; + } + + /* There is at least one node in the long-form wordlist. */ + /* Convert it to a standard wordlist node, which is the node to + * return */ + wordlist * const wl_start = wll_node_to_wl_node(wll); + wordlist * wl_dst_prev = wl_start; + wl_start->wl_prev = (wordlist *) NULL; + + /* Continue adding nodes */ + for (wll = (wordlist_l *) wll->wl.wl_next ; wll != (wordlist_l *) NULL; + wll = (wordlist_l *) wll->wl.wl_next) { + /* Convert a single long-form node to a standard for node */ + wordlist *wl_dst_cur = wll_node_to_wl_node(wll); + wl_dst_prev->wl_next = wl_dst_cur; + wl_dst_cur->wl_prev = wl_dst_prev; + wl_dst_prev = wl_dst_cur; + } /* end of loop over nodes in input long-form wordlist */ + + /* Terminate the list of words */ + wl_dst_prev->wl_next = (wordlist *) NULL; + return wl_start; +} /* end of function wll_to_wl */ + + + +/* This function creates word data for a standard list from word data of + * a long-form list. Both structures must be allocated on input, and the + * long-form data is not change */ +static wordlist *wll_node_to_wl_node(const wordlist_l *wll) +{ + /* Allocate node being returned */ + wordlist * const wl_dst = TMALLOC(wordlist, 1); + + /* Find required size of allocation and save lengths of arrays */ + const size_t n_char_word = wll->n_char_word; + const size_t n_byte_alloc = n_char_word + 1; + + /* Allocate buffer */ + char * p_dst_cur = wl_dst->wl_word = TMALLOC(char, n_byte_alloc); + + /* Word data */ + wl_dst->wl_word = p_dst_cur; + (void) memcpy(p_dst_cur, wll->wl.wl_word, n_char_word); + p_dst_cur += n_char_word; + *p_dst_cur++ = '\0'; + + return wl_dst; +} /* end of function wll_node_to_wl_node */ + + + +/* Free a long-form wordlist */ +void wll_free(wordlist_l *wll) +{ + while (wll != (wordlist_l *) NULL) { + wordlist_l * const next = (wordlist_l *) wll->wl.wl_next; + void *p; + if ((p = (void *) wll->wl.wl_word) != NULL) { + txfree(p); + } + txfree(wll); + wll = next; + } /* end of loop over wordlist nodes */ +} /* end of function wll_free */ + + + +/* This function prepends a wordlist_l node to the existing list. + * + * Parameters + * n_char_word: Length of word, excluding trailing NULL + * p_word: Address of word, or NULL if none. A null terminiation is + * not required if the word will be duplicated. + * opt: OPT_WLL_COPY_ALL -- create copy instead of resuing word allocation + * tail: Address of wordlist having this word prepended. May be null. + * + * Return value + * New wordlist node which is the start of the list + */ +wordlist_l *wll_cons( + size_t n_elem_word_alloc, size_t n_char_word, const char *p_word, + unsigned int opt, wordlist_l *tail) +{ + /* Create a new node and link with the existing wordlist */ + wordlist_l *w = TMALLOC(wordlist_l, 1); + w->wl.wl_next = (wordlist *) tail; + w->wl.wl_prev = (wordlist *) NULL; + + w->n_char_word = n_char_word; + w->n_elem_word_alloc = n_elem_word_alloc; + + if (opt & OPT_WLL_COPY_ALL) { + char *p_dst = w->wl.wl_word = TMALLOC(char, n_elem_word_alloc); + (void) memcpy(p_dst, p_word, n_char_word); + p_dst += n_char_word; + *p_dst = '\0'; + } + else { + w->wl.wl_word = (char *) p_word; + } + + /* Link to front of rest of nodes, if present */ + if (tail) { + /* The new word goes in the front */ + tail->wl.wl_prev = (wordlist *) w; + } + + return w; +} /* end of function wl_cons */ + + + +/* This function appends wl_to_append to the end of wl_dst and returns the + * start of the combined wordlist */ +static wordlist_l *wll_append(wordlist_l *wl_dst, wordlist_l *wl_to_append) +{ + /* Handle degenerate cases where both of the input wordlists + * are not non-NULL */ + if (wl_dst == (wordlist_l *) NULL) { + return wl_to_append; + } + if (wl_to_append == (wordlist_l *) NULL) { + return wl_dst; + } + + /* Locate last node of wl_dst */ + { + wordlist_l *wl; + for (wl = wl_dst; wl->wl.wl_next; + wl = (wordlist_l *) wl->wl.wl_next) { + ; + } + + /* Link nwl to end */ + wl->wl.wl_next = (wordlist *) wl_to_append; + wl_to_append->wl.wl_prev = (wordlist *) wl; + } + + return wl_dst; /* Return combined wordlist */ +} /* end of function wll_append */ + + + +/* This function appends word data, of the wordlist_l node "to_append" to + * the existing values at wordlist_l node "dst". + */ +void wll_append_to_node(wordlist_l *dst, const wordlist_l *to_append) +{ + /* Get sizes */ + const size_t n_old = dst->n_char_word; + const size_t n_new = to_append->n_char_word; + const size_t n_total = n_old + n_new; + const size_t n_elem_needed = n_total + 1; + + /* Resize if needed */ + if (dst->n_elem_word_alloc < n_elem_needed) { + const size_t n_elem_alloc = 2 * n_elem_needed; + dst->wl.wl_word = TREALLOC( + char, dst->wl.wl_word, n_elem_alloc); + dst->n_elem_word_alloc = n_elem_alloc; + } + + /* Do append */ + { + char *p_dst = dst->wl.wl_word + n_old; + char * const p_src = to_append->wl.wl_word; + (void) memcpy(p_dst, p_src, n_new); + p_dst += n_new; + *p_dst = '\0'; + } + dst->n_char_word = n_total; +} /* end of function wll_append_to_node */ + + + +/* This function modifies word data in a word list node by building a new word + * from the arrays supplied. These arrays may contain pieces of the word + * being modified, with overlap and duplication of intervals allowed. Null + * terminiations are not required on the input data. + * + * Parameters + * wl_node: wordlist node being modified. Cannot be NULL. + * n_input: Number of inputs + * p_n_char_word: Array of length n_input of lengths of input strings, + excluding trailing nulls + * pp_word: Array of pointers to input character data. An entry may be NULL + * iff the corrseponding value of p_n_char_word is 0. + */ +static void wl_modify_word(wordlist *wl_node, unsigned int n_input, + const size_t *p_n_char_word, char **pp_word) +{ + /* Find the number of chars of word data */ + size_t n_char_word_new = 0; + { /* have array */ + /* Accumulate count of chars */ + const size_t *p_n_char_word_cur = p_n_char_word; + const size_t * const p_n_char_word_end = p_n_char_word + n_input; + for ( ; p_n_char_word_cur != p_n_char_word_end; + ++p_n_char_word_cur) { + n_char_word_new += *p_n_char_word_cur; + } + } + + /* New allocation */ + char *p_word_new; + + /* Process the segments. */ + { /* no escapes */ + /* + 1 for null after word */ + const size_t n_byte_alloc = n_char_word_new + 1; + + /* New allocation */ + p_word_new = TMALLOC(char, n_byte_alloc); + + /* New word. Build from input pieces */ + { + const size_t *p_n_char_word_cur = p_n_char_word; + const size_t * const p_n_char_word_end = p_n_char_word + n_input; + char **pp_word_cur = pp_word; + char *p_dst = p_word_new; + for ( ; p_n_char_word_cur < p_n_char_word_end; + ++p_n_char_word_cur, ++pp_word_cur) { + const size_t n_char_word_cur = *p_n_char_word_cur; + (void) memcpy(p_dst, *pp_word_cur, n_char_word_cur); + p_dst += n_char_word_cur; + } + *p_dst = '\0'; + } + } + + + /* Free old and assign new */ + txfree(wl_node->wl_word); + wl_node->wl_word = p_word_new; +} /* end of function wl_modify_word */ + + + + + + diff --git a/src/frontend/streams.c b/src/frontend/streams.c index 730d5658b..e49a91a9b 100644 --- a/src/frontend/streams.c +++ b/src/frontend/streams.c @@ -1,6 +1,8 @@ /************* * streams.c ************/ +#include +#include #include "ngspice/ngspice.h" #include "ngspice/wordlist.h" diff --git a/src/include/ngspice/cpstd.h b/src/include/ngspice/cpstd.h index b6498de39..aab9409f9 100644 --- a/src/include/ngspice/cpstd.h +++ b/src/include/ngspice/cpstd.h @@ -26,7 +26,7 @@ Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group /* Externs defined in std.c */ -extern char *tildexpand(char *string); +extern char *tildexpand(const char *string); extern void printnum(char *buf, double num); int printnum_ds(DSTRING *p_ds, double num); extern int cp_numdgt; diff --git a/src/misc/tilde.c b/src/misc/tilde.c index db0ef8bd4..017330d84 100644 --- a/src/misc/tilde.c +++ b/src/misc/tilde.c @@ -3,20 +3,24 @@ Copyright 1991 Regents of the University of California. All rights reserved. Modified: 2002 R. Oktas, **********/ +#include "ngspice/defines.h" #include "ngspice/ngspice.h" -#include "tilde.h" #include "ngspice/stringskip.h" +#include "tilde.h" #ifdef HAVE_PWD_H #include #endif -#if defined(__MINGW32__) || defined(_MSC_VER) +#ifdef _WIN32 #undef BOOLEAN #include /* win32 functions */ #include "shlobj.h" /* SHGetFolderPath */ #endif +static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst, + const char *src); + /* XXX To prevent a name collision with `readline's `tilde_expand', the original name: `tilde_expand' has changed to `tildexpand'. This situation naturally brings to mind that `tilde_expand' could be used @@ -26,83 +30,196 @@ Modified: 2002 R. Oktas, reason why it should be replaced: eg. it returns NULL which should not behave this way, IMHO. Anyway... Don't care for the moment, may be in the future. -- ro */ - - -char * -tildexpand(char *string) +char *tildexpand(const char *string) { -#ifdef HAVE_PWD_H - char buf[BSIZE_SP]; - char *k, c; -#endif -#if defined(__MINGW32__) || defined(_MSC_VER) - char buf2[BSIZE_SP]; -#endif - char *result = NULL; - - if (!string) - return NULL; - - string = skip_ws(string); - - if (*string != '~') - return copy(string); - - string += 1; - - if (!*string || *string == '/') { - /* First try the environment setting. May also make life easier - for non-unix platforms, eg. MS-DOS. -- ro */ - result = getenv("HOME"); -#ifdef HAVE_PWD_H - /* Can't find a result from the environment, let's try - the other stuff. -- ro */ - if (!result) { - struct passwd *pw; - pw = getpwuid(getuid()); - if (pw) - result = pw->pw_dir; - *buf = '\0'; - } - - } else { - struct passwd *pw; - k = buf; - while ((c = *string) && c != '/') - *k++ = c, string++; - *k = '\0'; - pw = getpwnam(buf); - if (pw) - result = pw->pw_dir; -#endif + /* If no string passed, return NULL */ + if (!string) { + return NULL; } - if (result) { -#ifdef HAVE_PWD_H - strcpy(buf, result); - if (*string) - strcat(buf, string); - return copy(buf); - } else - return NULL; + string = skip_ws(string); /* step past leading whitespace */ + + /* If the string does not begin with a tilde, there is no ~ to expand */ + if (*string != '~') { + return copy(string); + } + + ++string; /* step past tilde */ + + /* Test for home of current user */ + if (*string == '\0' || *string == DIR_TERM) { + char *sz_home; + const int n_char_home = get_local_home(0, &sz_home); + if (n_char_home < 0) { + return copy(string); /* Strip the ~ and return the rest */ + } + const size_t n_char_rest = strlen(string); + TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1); + strcpy(sz_home + n_char_home, string); + return sz_home; + } + +#ifdef HAVE_PWD_H + /* ~bob -- Get name of user and find home for that user */ + { + char buf_fixed[100]; + char *buf = buf_fixed; + const char * const usr_start = string; + char c; + while ((c = *string) && c != '/') { + string++; + } + const char * const usr_end = string; + const size_t n_char_usr = usr_end - usr_start; + const size_t n_byte_usr = n_char_usr + 1; + if (n_byte_usr > sizeof buf_fixed) { + buf = TMALLOC(char, n_byte_usr); + } + (void) memcpy(buf, usr_start, n_char_usr); + buf[n_char_usr] = '\0'; + + + char *sz_home; + const int n_char_home = get_usr_home(buf, 0, &sz_home); + if (buf != buf_fixed) { /* free allocated buffer for user name */ + txfree(buf); + } + if (n_char_home < 0) { + return copy(usr_start); /* Strip the ~ and return the rest */ + } + const size_t n_char_rest = strlen(string); + TREALLOC(char, sz_home, (size_t) n_char_home + n_char_rest + 1); + strcpy(sz_home + n_char_home, string); + return sz_home; + } #else - /* Emulate the old behavior to prevent side effects. -- ro */ - return copy(string); - } -#if defined(__MINGW32__) || defined(_MSC_VER) - else if(SUCCEEDED(SHGetFolderPath(NULL, - CSIDL_PERSONAL, - NULL, - 0, - buf2))) - { - if (*string) - strcat(buf2, string); - return copy(buf2); - } + /* ~abc is meaningless */ + return copy(string); /* Again strip ~ and return rest */ #endif - return NULL; -#endif -} +} /* end of function tildexpand */ + + + + +/* Get value of "HOME" for the current user and copy to *p_buf if + * the value is less than n_byte_buf characters long. Otherwise + * allocate a buffer of the minimum required size. + * + * Return values + * >0: Number of characters copied to *p_buf, excluding the trailing null + * -1: A value for HOME could not be obtained. + * + * Remarks: + * This function does not free any allocation at *p_buf, allowing a + * fixed buffer to be passed to it. + */ +int get_local_home(size_t n_byte_buf, char **p_buf) +{ + char *sz_home = (char *) NULL; +#ifdef _WIN32 + char buf_sh_path[MAX_PATH]; +#endif + + do { + /* First thing to try is an environment variable HOME */ + if ((sz_home = getenv("HOME")) != (char *) NULL) { + break; + } + +#if defined(_WIN32) + /* If Windows, try an env var USERPROFILE next */ + if ((sz_home = getenv("USERPROFILE")) != (char *) NULL) { + break; + } + + /* For Windows, the folder path CSIDL_PERSONAL is tried next */ + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, + buf_sh_path))) { + sz_home = buf_sh_path; + break; + } +#elif defined(HAVE_PWD_H) /* _WIN32 and HAVE_PWD_H are mutually exclusive */ + /* Get home information for the current user */ + { + struct passwd *pw; + pw = getpwuid(getuid()); + if (pw) { + sz_home = pw->pw_dir; + } + } +#endif + } while (0); + + if (sz_home == (char *) NULL) { /* did not find a HOME value */ + return -1; + } + + /* Copy home value to buffer */ + return copy_home_to_buf(n_byte_buf, p_buf, sz_home); +} /* end of function get_local_home */ + + + +#ifdef HAVE_PWD_H +/* Get value of "HOME" for usr and copy to *p_buf if + * the value is less than n_byte_buf characters long. Otherwise + * allocate a buffer of the minimum required size. + * + * Return values + * >0: Number of characters copied to *pp_buf, excluding the trailing null + * -1: A value for HOME could not be obtained. + * + * Remarks: + * This function does not free any allocation at *p_buf, allowing a + * fixed buffer to be passed to it. + */ +int get_usr_home(const char *usr, size_t n_byte_buf, char **p_buf) +{ + struct passwd * const pw = getpwnam(usr); + if (pw) { + /* Copy home value to buffer */ + return copy_home_to_buf(n_byte_buf, p_buf, pw->pw_dir); + } + + return -1; +} /* end of function get_usr_home */ +#endif /* HAVE_PWD_H */ + + + +/* This function copies home value src to the buffer at *p_dst, allocating + * a larger buffer if required. + * + * Parameters + * n_byte_dst: Size of supplied destination buffer + * p_dst: Address containing address of supplied buffer on input. May be + * given the address of a larger buffer allocation if the input buffer + * is too small. + * src: Address of HOME value + * + * Return values + * number of characters copied excluding terminating null + * + * Remarks: + * This function does not free any allocation at *p_dst, allowing a + * fixed buffer to be passed to it. + */ +static inline int copy_home_to_buf(size_t n_byte_dst, char **p_dst, + const char *src) +{ + const size_t n_char_src = strlen(src); /* Size of HOME value */ + const size_t n_byte_src = n_char_src + 1; + + /* Allocate dst if input buffer too small */ + if (n_byte_src > n_byte_dst) { /* too big */ + *p_dst = TMALLOC(char, n_byte_src); + } + + (void) memcpy(*p_dst, src, n_byte_src); + + return (int) n_char_src; +} /* end of function copy_home_to_buf */ + + diff --git a/src/misc/tilde.h b/src/misc/tilde.h index 19dd519af..7aea0ef29 100644 --- a/src/misc/tilde.h +++ b/src/misc/tilde.h @@ -6,7 +6,13 @@ #ifndef ngspice_TILDE_H #define ngspice_TILDE_H -char * tildexpand(char *string); +char * tildexpand(const char *string); + +int get_local_home(size_t n_byte_buf, char **p_buf); +#ifdef HAVE_PWD_H +int get_usr_home(const char *usr, size_t n_byte_buf, char **p_buf); #endif + +#endif /* include guard */ From fd730148a724293c1191d67c21d57f4638f2cefe Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sat, 14 Dec 2019 13:05:51 -0500 Subject: [PATCH 35/58] Minor formatting and punctuation change. --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index ab16b64b6..84d30b4a3 100644 --- a/INSTALL +++ b/INSTALL @@ -628,7 +628,7 @@ Most of the options now following are not well maintained, are not tested or eve strings.h has been necessary during setting up the project. Install Microsoft Visual Studio 2019 Community with C/C++. For - example the German edition is available at no cost from + example, the German edition is available at no cost from https://www.visualstudio.com/de/vs/community/. Goto /ngspice/visualc. From c24b1e5f8c1f203ed9748cb018cc6bf83833947c Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sun, 15 Dec 2019 00:13:50 -0500 Subject: [PATCH 36/58] Removed redundant macro --- src/maths/misc/equality.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/maths/misc/equality.c b/src/maths/misc/equality.c index dbf7a920d..0a453a782 100644 --- a/src/maths/misc/equality.c +++ b/src/maths/misc/equality.c @@ -10,7 +10,6 @@ Copyright 1991 Regents of the University of California. All rights reserved. #define llabs(x) ((x) < 0 ? -(x) : (x)) #endif -#define int64_min INT64_MIN /* From Bruce Dawson, Comparing floating point numbers, http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm @@ -43,13 +42,13 @@ bool AlmostEqualUlps(double A, double B, int maxUlps) aInt = uA.i; /* Make aInt lexicographically ordered as a twos-complement int */ if (aInt < 0) - aInt = int64_min - aInt; + aInt = INT64_MIN - aInt; uB.d = B; bInt = uB.i; /* Make bInt lexicographically ordered as a twos-complement int */ if (bInt < 0) - bInt = int64_min - bInt; + bInt = INT64_MIN - bInt; intDiff = llabs(aInt - bInt); From 7681f3466422ed2186d372c038dcb57743057215 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sun, 15 Dec 2019 01:17:04 -0500 Subject: [PATCH 37/58] Additional freeing of device-dependent information for bug #419 and related bugs. --- src/frontend/hpgl.c | 67 ++-- src/frontend/plotting/plot5.c | 44 ++- src/frontend/plotting/x11.c | 12 +- src/frontend/postsc.c | 63 ++-- src/frontend/wdisp/windisp.c | 150 ++++---- src/frontend/wdisp/winprint.c | 621 ++++++++++++++++++---------------- 6 files changed, 495 insertions(+), 462 deletions(-) diff --git a/src/frontend/hpgl.c b/src/frontend/hpgl.c index 68d0a3485..865bbb62c 100644 --- a/src/frontend/hpgl.c +++ b/src/frontend/hpgl.c @@ -89,7 +89,8 @@ int GL_Init(void) { if (!cp_getvar("hcopyscale", CP_STRING, psscale, sizeof(psscale))) { scale = 1.0; - } else { + } + else { sscanf(psscale, "%lf", &scale); if ((scale <= 0) || (scale > 10)) scale = 1.0; @@ -106,20 +107,21 @@ int GL_Init(void) dispdev->minx = (int)(XOFF * 1.0); dispdev->miny = (int)(YOFF * 1.0); - return (0); + return 0; } /* devdep initially contains name of output file */ -int -GL_NewViewport(GRAPH *graph) +int GL_NewViewport(GRAPH *graph) { hcopygraphid = graph->graphid; if ((plotfile = fopen((char*) graph->devdep, "w")) == NULL) { - perror((char*) graph->devdep); + perror((char *) graph->devdep); + free(graph->devdep); graph->devdep = NULL; - return (1); + graph->n_byte_devdep = 0; + return 1; } if (graph->absolute.width) { @@ -146,13 +148,18 @@ GL_NewViewport(GRAPH *graph) /* start file off with a % */ fprintf(plotfile, "IN;DF;PA;"); - fprintf(plotfile, "SI %f,%f;", tocm*jgmult*fontwidth*scale, tocm*jgmult*fontheight*scale); + fprintf(plotfile, "SI %f,%f;", + tocm * jgmult * fontwidth * scale, + tocm * jgmult * fontheight * scale); #ifdef notdef if (!screenflag) #endif - + { graph->devdep = TMALLOC(GLdevdep, 1); + graph->n_byte_devdep = sizeof(GLdevdep); + } + DEVDEP(graph).lastlinestyle = -1; DEVDEP(graph).lastx = -1; DEVDEP(graph).lasty = -1; @@ -163,8 +170,7 @@ GL_NewViewport(GRAPH *graph) } -int -GL_Close(void) +int GL_Close(void) { /* in case GL_Close is called as part of an abort, w/o having reached GL_NewViewport */ @@ -187,8 +193,7 @@ GL_Close(void) } -int -GL_Clear(void) +int GL_Clear(void) { /* do nothing */ @@ -196,21 +201,21 @@ GL_Clear(void) } -int -GL_DrawLine(int x1, int y1, int x2, int y2) +int GL_DrawLine(int x1, int y1, int x2, int y2) { /* note: this is not extendible to more than one graph => will have to give NewViewport a writeable graph XXX */ if (DEVDEP(currentgraph).linecount == 0 - || x1 != DEVDEP(currentgraph).lastx - || y1 != DEVDEP(currentgraph).lasty) - { - fprintf(plotfile, "PU;PA %d , %d ;", jgmult*(x1 + xoff), jgmult*(y1 + yoff)); + || x1 != DEVDEP(currentgraph).lastx + || y1 != DEVDEP(currentgraph).lasty) { + fprintf(plotfile, "PU;PA %d , %d ;", + jgmult * (x1 + xoff), jgmult * (y1 + yoff)); } if (x1 != x2 || y1 != y2) { - fprintf(plotfile, "PD;PA %d , %d ;", jgmult*(x2 + xoff), jgmult*(y2 + yoff)); + fprintf(plotfile, "PD;PA %d , %d ;", + jgmult * (x2 + xoff), jgmult * (y2 + yoff)); DEVDEP(currentgraph).linecount += 1; } @@ -223,8 +228,7 @@ GL_DrawLine(int x1, int y1, int x2, int y2) /* ARGSUSED */ -int -GL_Arc(int x0, int y0, int r, double theta, double delta_theta) +int GL_Arc(int x0, int y0, int r, double theta, double delta_theta) { int x1, y1, angle; @@ -233,8 +237,10 @@ GL_Arc(int x0, int y0, int r, double theta, double delta_theta) angle = (int)(RAD_TO_DEG * delta_theta); - fprintf(plotfile, "PU;PA %d , %d;", jgmult*(x1+xoff+XTADJ), jgmult*(y1+yoff+YTADJ)); - fprintf(plotfile, "PD;AA %d , %d, %d;", jgmult*(x0+xoff+XTADJ), jgmult*(y0+yoff+YTADJ), angle); + fprintf(plotfile, "PU;PA %d , %d;", + jgmult * (x1 + xoff + XTADJ), jgmult * (y1 + yoff + YTADJ)); + fprintf(plotfile, "PD;AA %d , %d, %d;", + jgmult * (x0 + xoff + XTADJ), jgmult*(y0 + yoff + YTADJ), angle); DEVDEP(currentgraph).linecount = 0; @@ -242,13 +248,13 @@ GL_Arc(int x0, int y0, int r, double theta, double delta_theta) } -int -GL_Text(char *text, int x, int y, int angle) +int GL_Text(char *text, int x, int y, int angle) { /* move to (x, y) */ NG_IGNORE(angle); - fprintf(plotfile, "PU;PA %d , %d;", jgmult*(x+xoff+XTADJ), jgmult*(y+yoff+YTADJ)); + fprintf(plotfile, "PU;PA %d , %d;", + jgmult * (x + xoff + XTADJ), jgmult * (y + yoff + YTADJ)); fprintf(plotfile, "LB %s \x03", text); DEVDEP(currentgraph).lastx = -1; @@ -258,8 +264,7 @@ GL_Text(char *text, int x, int y, int angle) } -int -GL_SetLinestyle(int linestyleid) +int GL_SetLinestyle(int linestyleid) { /* special case get it when GL_Text restores a -1 linestyle */ @@ -283,8 +288,7 @@ GL_SetLinestyle(int linestyleid) /* ARGSUSED */ -int -GL_SetColor(int colorid) +int GL_SetColor(int colorid) { fprintf(plotfile, "SP %d;", colorid); @@ -292,8 +296,7 @@ GL_SetColor(int colorid) } -int -GL_Update(void) +int GL_Update(void) { fflush(plotfile); diff --git a/src/frontend/plotting/plot5.c b/src/frontend/plotting/plot5.c index f07b26426..5c4db679c 100644 --- a/src/frontend/plotting/plot5.c +++ b/src/frontend/plotting/plot5.c @@ -28,8 +28,7 @@ static char *linestyle[] = { static int currentlinestyle = SOLID; -int -Plt5_Init(void) +int Plt5_Init(void) { dispdev->numlinestyles = 4; dispdev->numcolors = 2; @@ -38,17 +37,18 @@ Plt5_Init(void) dispdev->width = 1000; dispdev->height = 1000; - return (0); + return 0; } -int -Plt5_NewViewport(GRAPH *graph) +int Plt5_NewViewport(GRAPH *graph) { if ((plotfile = fopen((char*) graph->devdep, "w")) == NULL) { + perror((char *) graph->devdep); + free(graph->devdep); graph->devdep = NULL; - perror((char*) graph->devdep); - return (1); + graph->n_byte_devdep = 0; + return 1; } if (graph->absolute.width) { @@ -64,7 +64,8 @@ Plt5_NewViewport(GRAPH *graph) /* re-scale linestyles */ gr_relinestyle(graph); - } else { + } + else { /* scale space */ putc('s', plotfile); putsi(0); @@ -83,13 +84,13 @@ Plt5_NewViewport(GRAPH *graph) /* set to NULL so graphdb doesn't incorrectly de-allocate it */ graph->devdep = NULL; + graph->n_byte_devdep = 0; - return (0); + return 0; } -int -Plt5_Close(void) +int Plt5_Close(void) { /* in case Plt5_Close is called as part of an abort, w/o having reached Plt5_NewViewport */ @@ -100,16 +101,14 @@ Plt5_Close(void) } -int -Plt5_Clear(void) +int Plt5_Clear(void) { /* do nothing */ return 0; } -int -Plt5_DrawLine(int x1, int y1, int x2, int y2) +int Plt5_DrawLine(int x1, int y1, int x2, int y2) { putc('l', plotfile); putsi(x1); @@ -121,8 +120,7 @@ Plt5_DrawLine(int x1, int y1, int x2, int y2) } -int -Plt5_Arc(int xc, int yc, int radius, double theta, double delta_theta) +int Plt5_Arc(int xc, int yc, int radius, double theta, double delta_theta) { int x0, y0, x1, y1; @@ -169,8 +167,7 @@ Plt5_Arc(int xc, int yc, int radius, double theta, double delta_theta) } -int -Plt5_Text(char *text, int x, int y, int angle) +int Plt5_Text(char *text, int x, int y, int angle) { int savedlstyle; NG_IGNORE(angle); @@ -195,8 +192,7 @@ Plt5_Text(char *text, int x, int y, int angle) } -int -Plt5_SetLinestyle(int linestyleid) +int Plt5_SetLinestyle(int linestyleid) { if (linestyleid < 0 || linestyleid > dispdev->numlinestyles) { internalerror("bad linestyleid"); @@ -211,8 +207,7 @@ Plt5_SetLinestyle(int linestyleid) /* ARGSUSED */ -int -Plt5_SetColor(int colorid) +int Plt5_SetColor(int colorid) { NG_IGNORE(colorid); @@ -221,8 +216,7 @@ Plt5_SetColor(int colorid) } -int -Plt5_Update(void) +int Plt5_Update(void) { fflush(plotfile); return 0; diff --git a/src/frontend/plotting/x11.c b/src/frontend/plotting/x11.c index 3b726d7f7..f581028f5 100644 --- a/src/frontend/plotting/x11.c +++ b/src/frontend/plotting/x11.c @@ -383,6 +383,7 @@ X11_NewViewport(GRAPH *graph) int trys; graph->devdep = TMALLOC(X11devdep, 1); + graph->n_byte_devdep = sizeof(X11devdep); /* set up new shell */ DEVDEP(graph).shell = XtCreateApplicationShell @@ -855,7 +856,7 @@ hardcopy(Widget w, XtPointer client_data, XtPointer call_data) NG_IGNORE(call_data); NG_IGNORE(w); - /* com_hardcopy() -> gr_resize() -> setcolor() dirung postscript + /* com_hardcopy() -> gr_resize() -> setcolor() during postscript printing will act on currentgraph with a DEVDEP inherited from PSdevdep. But currentgraph had not changed its devdep, which was derived from incompatible X11devdep, thus overwriting some variables. Here you find a @@ -893,16 +894,16 @@ killwin(Widget w, XtPointer client_data, XtPointer call_data) } + /* called from postcoms.c In the command 'destroy ac2' Will remove window associated with the plot (e.g. ac2) just before data of the plot are deleted.*/ -void -RemoveWindow(GRAPH *graph) +void RemoveWindow(GRAPH *graph) { if (graph->devdep) { /* Iplots are done asynchronously */ DEVDEP(graph).isopen = 0; - /* MW. Not sure but DestroyGraph might free() to much - try Xt...() first */ + /* MW. Not sure but DestroyGraph might free() too much - try Xt...() first */ XtUnmapWidget(DEVDEP(graph).shell); XtDestroyWidget(DEVDEP(graph).shell); XFreeFont(display, DEVDEP(graph).font); @@ -913,7 +914,8 @@ RemoveWindow(GRAPH *graph) currentgraph = NULL; DestroyGraph(graph->graphid); -} +} /* end of function RemoveWindow */ + /* call higher gr_redraw routine */ diff --git a/src/frontend/postsc.c b/src/frontend/postsc.c index 4c422f459..a3145b027 100644 --- a/src/frontend/postsc.c +++ b/src/frontend/postsc.c @@ -79,8 +79,7 @@ void PS_Stroke(void); /* Set scale, color and size of the plot */ -int -PS_Init(void) +int PS_Init(void) { char pswidth[30], psheight[30]; @@ -108,7 +107,8 @@ PS_Init(void) /* plot size */ if (!cp_getvar("hcopywidth", CP_STRING, pswidth, sizeof( pswidth))) { dispdev->width = (int)(7.75 * 72.0 * scale); /* (8 1/2 - 3/4) * 72 */ - } else { + } + else { sscanf(pswidth, "%d", &(dispdev->width)); if (dispdev->width <= 100) dispdev->width = 100; @@ -117,7 +117,8 @@ PS_Init(void) } if (!cp_getvar("hcopyheight", CP_STRING, psheight, sizeof(psheight))) { dispdev->height = dispdev->width; - } else { + } + else { sscanf(psheight, "%d", &(dispdev->height)); if (dispdev->height <= 100) dispdev->height = 100; @@ -135,15 +136,17 @@ PS_Init(void) * viewport.height = absolute.height - 2 * viewportyoff */ - if (!cp_getvar("hcopyfont", CP_STRING, psfont, sizeof(psfont))) + if (!cp_getvar("hcopyfont", CP_STRING, psfont, sizeof(psfont))) { strcpy(psfont, "Helvetica"); + } if (!cp_getvar("hcopyfontsize", CP_STRING, psfontsize, sizeof(psfontsize))) { fontsize = 10; fontwidth = 6; fontheight = 14; xtadj = (int)(XTADJ * scale); ytadj = (int)(YTADJ * scale); - } else { + } + else { sscanf(psfontsize, "%d", &fontsize); if ((fontsize < 10) || (fontsize > 14)) fontsize = 10; @@ -157,21 +160,23 @@ PS_Init(void) dispdev->minx = (int)(XOFF / scale); dispdev->miny = (int)(YOFF / scale); - return (0); -} + return 0; +} /* end of function PS_Init */ + /* Plot and fill bounding box */ -int -PS_NewViewport(GRAPH *graph) +int PS_NewViewport(GRAPH *graph) { int x1, x2, y1, y2; hcopygraphid = graph->graphid; /* devdep initially contains name of output file */ if ((plotfile = fopen((char*)graph->devdep, "w")) == NULL) { - perror((char*)graph->devdep); + perror((char *) graph->devdep); + free(graph->devdep); graph->devdep = NULL; - return (1); + graph->n_byte_devdep = 0; + return 1; } if (graph->absolute.width) { @@ -233,6 +238,7 @@ PS_NewViewport(GRAPH *graph) psfont, (int) (fontsize * scale)); graph->devdep = TMALLOC(PSdevdep, 1); + graph->n_byte_devdep = sizeof(PSdevdep); DEVDEP(graph).lastlinestyle = -1; DEVDEP(graph).lastcolor = -1; DEVDEP(graph).lastx = -1; @@ -245,8 +251,7 @@ PS_NewViewport(GRAPH *graph) } -int -PS_Close(void) +int PS_Close(void) { /* in case PS_Close is called as part of an abort, w/o having reached PS_NewViewport */ @@ -267,16 +272,14 @@ PS_Close(void) } -int -PS_Clear(void) +int PS_Clear(void) { /* do nothing */ return 0; } -int -PS_DrawLine(int x1, int y1, int x2, int y2) +int PS_DrawLine(int x1, int y1, int x2, int y2) { /* note: this is not extendible to more than one graph => will have to give NewViewport a writeable graph XXX */ @@ -303,8 +306,7 @@ PS_DrawLine(int x1, int y1, int x2, int y2) } -int -PS_Arc(int x0, int y0, int r, double theta, double delta_theta) +int PS_Arc(int x0, int y0, int r, double theta, double delta_theta) { double x1, y1; double angle1, angle2; @@ -325,8 +327,7 @@ PS_Arc(int x0, int y0, int r, double theta, double delta_theta) } -int -PS_Text(char *text, int x, int y, int angle) +int PS_Text(char *text, int x, int y, int angle) { int savedlstyle, savedcolor; @@ -368,8 +369,7 @@ PS_Text(char *text, int x, int y, int angle) /* PS_DefineColor */ /* PS_DefineLinestyle */ -int -PS_SetLinestyle(int linestyleid) +int PS_SetLinestyle(int linestyleid) { /* special case get it when PS_Text restores a -1 linestyle */ @@ -387,16 +387,14 @@ PS_SetLinestyle(int linestyleid) } -int -PS_SetColor(int colorid) +int PS_SetColor(int colorid) { PS_LinestyleColor(currentgraph->linestyle, colorid); return 0; } -int -PS_Update(void) +int PS_Update(void) { fflush(plotfile); return 0; @@ -405,8 +403,7 @@ PS_Update(void) /**************** PRIVAT FUNCTIONS OF PS FRONTEND *****************************/ -void -PS_SelectColor(int colorid) /* should be replaced by PS_DefineColor */ +void PS_SelectColor(int colorid) /* should be replaced by PS_DefineColor */ { char colorN[30] = "", colorstring[30] = ""; char rgb[30], s_red[30] = "0x", s_green[30] = "0x", s_blue[30] = "0x"; @@ -468,8 +465,7 @@ PS_SelectColor(int colorid) /* should be replaced by PS_DefineColor */ } -void -PS_LinestyleColor(int linestyleid, int colorid) +void PS_LinestyleColor(int linestyleid, int colorid) { /* we have some different linestyles and colors: - color and linestyle we got via function call @@ -519,8 +515,7 @@ PS_LinestyleColor(int linestyleid, int colorid) } -void -PS_Stroke(void) +void PS_Stroke(void) { /* strokes an open path */ if (DEVDEP(currentgraph).linecount > 0) { diff --git a/src/frontend/wdisp/windisp.c b/src/frontend/wdisp/windisp.c index 557169cb4..598f613e0 100644 --- a/src/frontend/wdisp/windisp.c +++ b/src/frontend/wdisp/windisp.c @@ -107,9 +107,7 @@ WIN_Init() returns 0, if no error ocurred. WIN_Init() does not yet open a window, this happens only in WIN_NewViewport() ******************************************************************************/ - -int -WIN_Init(void) +int WIN_Init(void) { char colorstring[BSIZE_SP]; @@ -126,10 +124,12 @@ WIN_Init(void) isblack = !cieq(colorstring, "white"); /* get linewidth information from spinit */ - if (!cp_getvar("xbrushwidth", CP_NUM, &linewidth, 0)) + if (!cp_getvar("xbrushwidth", CP_NUM, &linewidth, 0)) { linewidth = 0; - if (linewidth < 0) + } + if (linewidth < 0) { linewidth = 0; + } /* only for the first time: */ if (!IsRegistered) { @@ -165,10 +165,12 @@ WIN_Init(void) ColorTable[11] = RGB(255, 128, 128); /* pink */ /* 2. color bank (with different line style */ - if (isblack) + if (isblack) { ColorTable[12] = RGB(255, 255, 255); /* white */ - else + } + else { ColorTable[12] = RGB( 0, 0, 0); /* black */ + } ColorTable[13] = RGB( 0, 255, 0); /* green */ ColorTable[14] = RGB(255, 0, 0); /* red */ @@ -192,17 +194,20 @@ WIN_Init(void) TheWndClass.lpszMenuName = NULL; TheWndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - if (isblack) + if (isblack) { TheWndClass.hbrBackground = GetStockObject(BLACK_BRUSH); - else + } + else { TheWndClass.hbrBackground = GetStockObject(WHITE_BRUSH); + } TheWndClass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(2)); TheWndClass.cbClsExtra = 0; TheWndClass.cbWndExtra = sizeof(GRAPH *); - if (!RegisterClass(&TheWndClass)) + if (!RegisterClass(&TheWndClass)) { return 1; + } } /* not first time */ else if (isblackold != isblack) { @@ -214,10 +219,12 @@ WIN_Init(void) ColorTable[0] = RGB(255, 255, 255); /* white = background */ ColorTable[1] = RGB( 0, 0, 0); /* black = text and grid */ } - if (isblack) + if (isblack) { ColorTable[12] = RGB(255, 255, 255); /* white */ - else + } + else { ColorTable[12] = RGB( 0, 0, 0); /* black */ + } isblackold = isblack; } @@ -230,28 +237,27 @@ WIN_Init(void) /* get pointer to graph */ /* (attach to window) */ -static GRAPH * -pGraph(HWND hwnd) +static GRAPH *pGraph(HWND hwnd) { return (GRAPH *) GetWindowLongPtr(hwnd, 0); } /* return line style for plotting */ -static int -LType(int ColorIndex) +static int LType(int ColorIndex) { - if (ColorIndex >= 12) + if (ColorIndex >= 12) { return PS_DOT; - else + } + else { return PS_SOLID; + } } /* postscript hardcopy from a plot window */ /* called by SystemMenue / Postscript hardcopy */ -static LRESULT -HcpyPlot(HWND hwnd) +static LRESULT HcpyPlot(HWND hwnd) { int colorval = isblack? 0 : 1; NG_IGNORE(hwnd); @@ -261,13 +267,13 @@ HcpyPlot(HWND hwnd) } -static LRESULT -HcpyPlotBW(HWND hwnd) +static LRESULT HcpyPlotBW(HWND hwnd) { int bgcolor; NG_IGNORE(hwnd); - if (cp_getvar("hcopypscolor", CP_NUM, &bgcolor, 0)) + if (cp_getvar("hcopypscolor", CP_NUM, &bgcolor, 0)) { cp_remvar("hcopypscolor"); + } com_hardcopy(NULL); return 0; } @@ -275,8 +281,7 @@ HcpyPlotBW(HWND hwnd) /* print a plot window */ /* called by SystemMenue / Print */ -static LRESULT -PrintPlot(HWND hwnd) +static LRESULT PrintPlot(HWND hwnd) { GRAPH *graph; GRAPH *temp; @@ -288,20 +293,23 @@ PrintPlot(HWND hwnd) /* switch to printer */ /* (results in WPRINT_Init()) */ - if (DevSwitch("WinPrint")) + if (DevSwitch("WinPrint")) { return 0; + } /* Cursor = wait */ SetCursor(LoadCursor(NULL, IDC_WAIT)); /* copy graph */ temp = CopyGraph(graph); - if (!temp) + if (!temp) { goto PrintEND; + } /* add to the copy the new printer data */ - if (NewViewport(temp)) + if (NewViewport(temp)) { goto PrintEND2; + } /* make correction to placement of grid (copy from gr_init) */ temp->viewportxoff = temp->fontwidth * 8; @@ -326,8 +334,7 @@ PrintPlot(HWND hwnd) /* initialze printer */ -static LRESULT -PrintInit(HWND hwnd) +static LRESULT PrintInit(HWND hwnd) { /* hand over to printer module */ WPRINT_PrintInit(hwnd); @@ -336,8 +343,8 @@ PrintInit(HWND hwnd) /* window procedure */ -LRESULT CALLBACK -PlotWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT CALLBACK PlotWindowProc(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) { static int x0, y0, xep, yep; int xe, ye, prevmix; @@ -380,10 +387,12 @@ PlotWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /* left mouse button: connect coordinate pair by dashed pair of x, y lines */ if (wParam & MK_LBUTTON) { hdc = GetDC(hwnd); - if (isblack) + if (isblack) { prevmix = SetROP2(hdc, R2_XORPEN); - else + } + else { prevmix = SetROP2(hdc, R2_NOTXORPEN); + } /* Create white dashed pen */ NewPen = CreatePen(LType(12), 0, ColorTable[1]); OldPen = SelectObject(hdc, NewPen); @@ -466,7 +475,8 @@ PlotWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) hypot(fx0, fy0), (angle > 0) ? angle : 360.0 + angle); } - } else { + } + else { /* need to print info about two points */ fprintf(stdout, "\nx0 = %g, y0 = %g x1 = %g, y1 = %g\n", fx0, fy0, fxe, fye); @@ -601,8 +611,7 @@ PlotWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ******************************************************************************/ -int -WIN_NewViewport(GRAPH *graph) +int WIN_NewViewport(GRAPH *graph) { int i; HWND window; @@ -612,8 +621,9 @@ WIN_NewViewport(GRAPH *graph) HMENU sysmenu; /* test the parameters */ - if (!graph) + if (!graph) { return 1; + } /* initialize if not yet done */ if (WIN_Init() != 0) { @@ -623,24 +633,30 @@ WIN_NewViewport(GRAPH *graph) /* allocate device dependency info */ wd = calloc(1, sizeof(tWindowData)); - if (!wd) + if (!wd) { return 1; + } graph->devdep = wd; + graph->n_byte_devdep = sizeof(tWindowData); /* Create the window */ i = GetSystemMetrics(SM_CYSCREEN) / 3; window = CreateWindow(WindowName, graph->plotname, WS_OVERLAPPEDWINDOW, 0, 0, WinLineWidth, i * 2 - 22, NULL, NULL, hInst, NULL); - if (!window) + if (!window) { return 1; + } + /* change the background color of all windows (both new and already plotted) by assessing the registered window class */ - if (isblack) + if (isblack) { SetClassLongPtr(window, GCLP_HBRBACKGROUND, (LONG_PTR)GetStockObject(BLACK_BRUSH)); - else + } + else { SetClassLongPtr(window, GCLP_HBRBACKGROUND, (LONG_PTR)GetStockObject(WHITE_BRUSH)); + } wd->wnd = window; @@ -709,8 +725,7 @@ to the printer. Therefore WIN_Close is not allowed to do anything, cancelling of the structures occurs at program termination. ******************************************************************************/ -int -WIN_Close(void) +int WIN_Close(void) { return 0; } @@ -733,8 +748,7 @@ RealClose(void) #endif -int -WIN_Clear(void) +int WIN_Clear(void) { tpWindowData wd; @@ -753,8 +767,7 @@ WIN_Clear(void) } -int -WIN_DrawLine(int x1, int y1, int x2, int y2) +int WIN_DrawLine(int x1, int y1, int x2, int y2) { tpWindowData wd; HPEN OldPen; @@ -778,8 +791,7 @@ WIN_DrawLine(int x1, int y1, int x2, int y2) } -int -WIN_Arc(int x0, int y0, int radius, double theta, double delta_theta) +int WIN_Arc(int x0, int y0, int radius, double theta, double delta_theta) /* * Notes: * Draws an arc of and center at (x0,y0) beginning at @@ -859,8 +871,7 @@ WIN_Text_old(char *text, int x, int y, int degrees) #endif -int -WIN_Text(char *text, int x, int y, int angle) +int WIN_Text(char *text, int x, int y, int angle) { tpWindowData wd; HFONT hfont; @@ -906,8 +917,7 @@ WIN_Text(char *text, int x, int y, int angle) } -int -WIN_DefineColor(int colorid, double red, double green, double blue) +int WIN_DefineColor(int colorid, double red, double green, double blue) { NG_IGNORE(colorid); NG_IGNORE(red); @@ -917,8 +927,7 @@ WIN_DefineColor(int colorid, double red, double green, double blue) } -int -WIN_DefineLinestyle(int num, int mask) +int WIN_DefineLinestyle(int num, int mask) { NG_IGNORE(num); NG_IGNORE(mask); @@ -926,16 +935,14 @@ WIN_DefineLinestyle(int num, int mask) } -int -WIN_SetLinestyle(int style) +int WIN_SetLinestyle(int style) { NG_IGNORE(style); return 0; } -int -WIN_SetColor(int color) +int WIN_SetColor(int color) { tpWindowData wd; @@ -952,8 +959,7 @@ WIN_SetColor(int color) } -int -WIN_Update(void) +int WIN_Update(void) { tpWindowData wd; @@ -973,16 +979,14 @@ WIN_Update(void) #if 0 -int -WIN_DiagramReady(void) +int WIN_DiagramReady(void) { return 0; } #endif -void -RemoveWindow(GRAPH *dgraph) +void RemoveWindow(GRAPH *dgraph) { tpWindowData wd; @@ -998,15 +1002,15 @@ static void WIN_ScreentoData(GRAPH *graph, int x, int y, double *fx, double *fy) double lmin, lmax; if (graph->grid.gridtype == GRID_XLOG || - graph->grid.gridtype == GRID_LOGLOG) - { + graph->grid.gridtype == GRID_LOGLOG) { lmin = log10(graph->datawindow.xmin); lmax = log10(graph->datawindow.xmax); *fx = exp(((x - graph->viewportxoff) * - (lmax - lmin) / graph->viewport.width + lmin) * M_LN10); - } else { + (lmax - lmin) / graph->viewport.width + lmin) * M_LN10); + } + else { *fx = (x - graph->viewportxoff) * graph->aspectratiox + - graph->datawindow.xmin; + graph->datawindow.xmin; } if (graph->grid.gridtype == GRID_YLOG || @@ -1015,10 +1019,10 @@ static void WIN_ScreentoData(GRAPH *graph, int x, int y, double *fx, double *fy) lmin = log10(graph->datawindow.ymin); lmax = log10(graph->datawindow.ymax); *fy = exp(((graph->absolute.height - y - graph->viewportyoff) * - (lmax - lmin) / graph->viewport.height + lmin) * M_LN10); + (lmax - lmin) / graph->viewport.height + lmin) * M_LN10); } else { *fy = ((graph->absolute.height - y) - graph->viewportyoff) * - graph->aspectratioy + graph->datawindow.ymin; + graph->aspectratioy + graph->datawindow.ymin; } } diff --git a/src/frontend/wdisp/winprint.c b/src/frontend/wdisp/winprint.c index 9a9d5ea9d..e81fff873 100644 --- a/src/frontend/wdisp/winprint.c +++ b/src/frontend/wdisp/winprint.c @@ -34,26 +34,26 @@ #include "winprint.h" /* function prototypes */ /* Typen */ -typedef struct { /* Extra Printdaten */ - int ColorIndex; /* Index auf die akt. Farbe */ - int LineIndex; /* Index auf den akt. Linientyp */ +typedef struct { /* Extra Printdaten */ + int ColorIndex; /* Index auf die akt. Farbe */ + int LineIndex; /* Index auf den akt. Linientyp */ } tPrintData; -typedef tPrintData * tpPrintData; /* Zeiger darauf */ +typedef tPrintData *tpPrintData; /* Zeiger darauf */ #define pPrintData(g) ((tpPrintData)(g->devdep)) /* externals */ -void WaitForIdle(void); /* Warte, bis keine Events da */ +void WaitForIdle(void); /* Warte, bis keine Events da */ /* lokale Variablen */ -static HFONT PlotFont = NULL; /* Font-Merker */ -static HFONT OldFont = NULL; -#define NumLines 7 /* Anzahl der LineStyles */ -static int LineTable[NumLines]; /* Speicher fuer die LineStyles */ -static HDC PrinterDC = NULL; /* Device Context */ -#define NumPrintColors 2 /* vordef. Farben */ -static COLORREF ColorTable[NumPrintColors];/* Speicher fuer die Farben */ -static int PrinterWidth = 1000; /* Breite des Papiers */ -static int PrinterHeight = 1000; /* Hoehe des Papiers */ +static HFONT PlotFont = NULL; /* Font-Merker */ +static HFONT OldFont = NULL; +#define NumLines 7 /* Anzahl der LineStyles */ +static int LineTable[NumLines]; /* Speicher fuer die LineStyles */ +static HDC PrinterDC = NULL; /* Device Context */ +#define NumPrintColors 2 /* vordef. Farben */ +static COLORREF ColorTable[NumPrintColors];/* Speicher fuer die Farben */ +static int PrinterWidth = 1000; /* Breite des Papiers */ +static int PrinterHeight = 1000; /* Hoehe des Papiers */ /****************************************************************************** Drucker-Initialisierung @@ -61,59 +61,63 @@ static int PrinterHeight = 1000; /* Hoehe des Papiers */ void WPRINT_PrintInit(HWND hwnd) { - /* Parameter-Block */ - PRINTDLG pd; + /* Parameter-Block */ + PRINTDLG pd; - /* Initialisieren */ - pd.lStructSize = sizeof(PRINTDLG); - pd.hwndOwner = hwnd; - pd.hDevMode = NULL; - pd.hDevNames = NULL; - pd.hDC = NULL; - pd.Flags = PD_PRINTSETUP; - pd.nFromPage = 1; - pd.nToPage = 1; - pd.nMinPage = 0; - pd.nMaxPage = 0; - pd.nCopies = 1; - pd.hInstance = NULL; - pd.lCustData = 0; - pd.lpfnPrintHook = NULL; - pd.lpfnSetupHook = NULL; - pd.lpPrintTemplateName = NULL; - pd.lpSetupTemplateName = NULL; - pd.hPrintTemplate = NULL; - pd.hSetupTemplate = NULL; + /* Initialisieren */ + pd.lStructSize = sizeof(PRINTDLG); + pd.hwndOwner = hwnd; + pd.hDevMode = NULL; + pd.hDevNames = NULL; + pd.hDC = NULL; + pd.Flags = PD_PRINTSETUP; + pd.nFromPage = 1; + pd.nToPage = 1; + pd.nMinPage = 0; + pd.nMaxPage = 0; + pd.nCopies = 1; + pd.hInstance = NULL; + pd.lCustData = 0; + pd.lpfnPrintHook = NULL; + pd.lpfnSetupHook = NULL; + pd.lpPrintTemplateName = NULL; + pd.lpSetupTemplateName = NULL; + pd.hPrintTemplate = NULL; + pd.hSetupTemplate = NULL; - /* Default-Drucker initialisieren */ - (void) PrintDlg( &pd); + /* Default-Drucker initialisieren */ + (void) PrintDlg(&pd); - /* Speicher freigeben */ - if( pd.hDevMode) GlobalFree( pd.hDevMode); - if( pd.hDevNames) GlobalFree( pd.hDevNames); + /* Speicher freigeben */ + if (pd.hDevMode) { + GlobalFree(pd.hDevMode); + } + if (pd.hDevNames) { + GlobalFree(pd.hDevNames); + } } /* Abort-Procedur zum Drucken */ -BOOL CALLBACK WPRINT_Abort( HDC hdc, int iError) +BOOL CALLBACK WPRINT_Abort(HDC hdc, int iError) { NG_IGNORE(hdc); NG_IGNORE(iError); /* Multitasking */ - WaitForIdle(); + WaitForIdle(); - /* Warten */ - return TRUE; + /* Warten */ + return TRUE; } /****************************************************************************** WPRINT_Init() stellt die Verbindung zur Grafik her. Dazu gehoert die Feststellung von - dispdev->numlinestyles - dispdev->numcolors - dispdev->width - dispdev->height + dispdev->numlinestyles + dispdev->numcolors + dispdev->width + dispdev->height WPRINT_Init() gibt 0 zurueck, falls kein Fehler auftrat. @@ -121,102 +125,108 @@ WPRINT_Init() gibt 0 zurueck, falls kein Fehler auftrat. int WPRINT_Init(void) { - int pWidth; - int pHeight; + int pWidth; + int pHeight; - /* Printer-DC holen */ - if (!PrinterDC) { + /* Printer-DC holen */ + if (!PrinterDC) { - /* Parameter-Block */ - PRINTDLG pd; + /* Parameter-Block */ + PRINTDLG pd; - /* Initialisieren */ - pd.lStructSize = sizeof(PRINTDLG); - pd.hwndOwner = NULL; - pd.hDevMode = NULL; - pd.hDevNames = NULL; - pd.hDC = NULL; - pd.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC; - pd.nFromPage = 1; - pd.nToPage = 1; - pd.nMinPage = 0; - pd.nMaxPage = 0; - pd.nCopies = 1; - pd.hInstance = NULL; - pd.lCustData = 0; - pd.lpfnPrintHook = NULL; - pd.lpfnSetupHook = NULL; - pd.lpPrintTemplateName = NULL; - pd.lpSetupTemplateName = NULL; - pd.hPrintTemplate = NULL; - pd.hSetupTemplate = NULL; + /* Initialisieren */ + pd.lStructSize = sizeof(PRINTDLG); + pd.hwndOwner = NULL; + pd.hDevMode = NULL; + pd.hDevNames = NULL; + pd.hDC = NULL; + pd.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC; + pd.nFromPage = 1; + pd.nToPage = 1; + pd.nMinPage = 0; + pd.nMaxPage = 0; + pd.nCopies = 1; + pd.hInstance = NULL; + pd.lCustData = 0; + pd.lpfnPrintHook = NULL; + pd.lpfnSetupHook = NULL; + pd.lpPrintTemplateName = NULL; + pd.lpSetupTemplateName = NULL; + pd.hPrintTemplate = NULL; + pd.hSetupTemplate = NULL; - /* Default-Drucker initialisieren */ - (void) PrintDlg( &pd); + /* Default-Drucker initialisieren */ + (void) PrintDlg(&pd); - /* Speicher freigeben */ - if( pd.hDevMode) GlobalFree( pd.hDevMode); - if( pd.hDevNames) GlobalFree( pd.hDevNames); + /* Speicher freigeben */ + if (pd.hDevMode) { + GlobalFree(pd.hDevMode); + } + if (pd.hDevNames) { + GlobalFree(pd.hDevNames); + } - /* DC holen */ - PrinterDC = pd.hDC; - if (!PrinterDC) return 1; + /* DC holen */ + PrinterDC = pd.hDC; + if (!PrinterDC) return 1; - /* Abmasze bestimmen */ - PrinterWidth = GetDeviceCaps( PrinterDC, HORZRES); - PrinterHeight = GetDeviceCaps( PrinterDC, VERTRES); - pWidth = GetDeviceCaps( PrinterDC, HORZSIZE); - pHeight = GetDeviceCaps( PrinterDC, VERTSIZE); + /* Abmasze bestimmen */ + PrinterWidth = GetDeviceCaps(PrinterDC, HORZRES); + PrinterHeight = GetDeviceCaps(PrinterDC, VERTRES); + pWidth = GetDeviceCaps(PrinterDC, HORZSIZE); + pHeight = GetDeviceCaps(PrinterDC, VERTSIZE); - /* Mapping Mode setzen (fuer Kreise) */ - if ( pWidth > pHeight) - /* Querformat */ - PrinterWidth = (PrinterHeight * pWidth) / pHeight; - else - /* Hochformat */ - PrinterHeight = (PrinterWidth * pHeight) / pWidth; + /* Mapping Mode setzen (fuer Kreise) */ + if (pWidth > pHeight) { + /* Querformat */ + PrinterWidth = (PrinterHeight * pWidth) / pHeight; + } + else { + /* Hochformat */ + PrinterHeight = (PrinterWidth * pHeight) / pWidth; + } - SetMapMode( PrinterDC, MM_ISOTROPIC); - SetWindowExtEx( PrinterDC, PrinterWidth, PrinterHeight, NULL); - SetViewportExtEx( PrinterDC, PrinterWidth, PrinterHeight, NULL); + SetMapMode(PrinterDC, MM_ISOTROPIC); + SetWindowExtEx(PrinterDC, PrinterWidth, PrinterHeight, NULL); + SetViewportExtEx(PrinterDC, PrinterWidth, PrinterHeight, NULL); - /* nicht hoeher als breit zeichnen */ - if (pWidth < pHeight) { - /* Papier im Hochformat */ - PrinterHeight = PrinterWidth; - } + /* nicht hoeher als breit zeichnen */ + if (pWidth < pHeight) { + /* Papier im Hochformat */ + PrinterHeight = PrinterWidth; + } - /* Initialisierungen des Display-Descriptors */ - dispdev->width = PrinterWidth; - dispdev->height = PrinterHeight; - dispdev->numlinestyles = NumLines; - dispdev->numcolors = NumPrintColors; + /* Initialisierungen des Display-Descriptors */ + dispdev->width = PrinterWidth; + dispdev->height = PrinterHeight; + dispdev->numlinestyles = NumLines; + dispdev->numcolors = NumPrintColors; - /* Farben initialisieren */ - ColorTable[0] = RGB(255,255,255); /* Weisz */ - ColorTable[1] = RGB( 0, 0, 0); /* Schwarz */ + /* Farben initialisieren */ + ColorTable[0] = RGB(255,255,255); /* Weisz */ + ColorTable[1] = RGB(0, 0, 0); /* Schwarz */ - /* LineStyles initialisieren */ - LineTable[0] = PS_SOLID; - LineTable[1] = PS_DOT; /* Gitter */ - LineTable[2] = PS_SOLID; /* Erste Linie */ - LineTable[3] = PS_DOT; /* Zweite Linie */ - LineTable[4] = PS_DASH; /* usw */ - LineTable[5] = PS_DASHDOT; - LineTable[6] = PS_DASHDOTDOT; + /* LineStyles initialisieren */ + LineTable[0] = PS_SOLID; + LineTable[1] = PS_DOT; /* Gitter */ + LineTable[2] = PS_SOLID; /* Erste Linie */ + LineTable[3] = PS_DOT; /* Zweite Linie */ + LineTable[4] = PS_DASH; /* usw */ + LineTable[5] = PS_DASHDOT; + LineTable[6] = PS_DASHDOTDOT; - /* Font */ - if (!PlotFont) { - PlotFont = CreateFont( 0,0,0,0, FW_DONTCARE, FALSE, FALSE, FALSE, - ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - PROOF_QUALITY, FIXED_PITCH, NULL); - } + /* Font */ + if (!PlotFont) { + PlotFont = CreateFont(0,0,0,0, FW_DONTCARE, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + PROOF_QUALITY, FIXED_PITCH, NULL); + } - /* Abort-Prozedur setzen */ - SetAbortProc( PrinterDC, WPRINT_Abort); - } - /* fertig */ - return (0); + /* Abort-Prozedur setzen */ + SetAbortProc(PrinterDC, WPRINT_Abort); + } + /* fertig */ + return 0; } @@ -227,250 +237,275 @@ int WPRINT_Init(void) ******************************************************************************/ -int WPRINT_NewViewport( GRAPH * graph) +int WPRINT_NewViewport(GRAPH * graph) { - TEXTMETRIC tm; - tpPrintData pd; - DOCINFO di; + TEXTMETRIC tm; + tpPrintData pd; + DOCINFO di; - /* Parameter testen */ - if (!graph) return 1; + /* Parameter testen */ + if (!graph) { + return 1; + } - /* Initialisiere, falls noch nicht geschehen */ - if (WPRINT_Init() != 0) { - externalerror("Can't initialize Printer."); - return(1); - } + /* Initialisiere, falls noch nicht geschehen */ + if (WPRINT_Init() != 0) { + externalerror("Can't initialize Printer."); + return(1); + } - /* Device dep. Info allocieren */ - pd = calloc(1, sizeof(tPrintData)); - if (!pd) return 1; - graph->devdep = pd; + /* Device dep. Info allocieren */ + pd = calloc(1, sizeof(tPrintData)); + if (!pd) return 1; + graph->devdep = pd; + graph->n_byte_devdep = sizeof(tPrintData); - /* Setze den Color-Index */ - pd->ColorIndex = 0; + /* Setze den Color-Index */ + pd->ColorIndex = 0; - /* Font setzen */ - OldFont = SelectObject( PrinterDC, PlotFont); + /* Font setzen */ + OldFont = SelectObject(PrinterDC, PlotFont); - /* Font-Parameter abfragen */ - if (GetTextMetrics( PrinterDC, &tm)) { - graph->fontheight = tm.tmHeight; - graph->fontwidth = tm.tmAveCharWidth; - } + /* Font-Parameter abfragen */ + if (GetTextMetrics(PrinterDC, &tm)) { + graph->fontheight = tm.tmHeight; + graph->fontwidth = tm.tmAveCharWidth; + } - /* Setze den Linien-Index */ - pd->LineIndex = 0; + /* Setze den Linien-Index */ + pd->LineIndex = 0; - /* Viewport-Parameter setzen */ - graph->viewport.height = PrinterHeight; - graph->viewport.width = PrinterWidth; + /* Viewport-Parameter setzen */ + graph->viewport.height = PrinterHeight; + graph->viewport.width = PrinterWidth; - /* Absolut-Parameter setzen */ - graph->absolute.xpos = 0; - graph->absolute.ypos = 0; - graph->absolute.width = PrinterWidth; - graph->absolute.height = PrinterHeight; + /* Absolut-Parameter setzen */ + graph->absolute.xpos = 0; + graph->absolute.ypos = 0; + graph->absolute.width = PrinterWidth; + graph->absolute.height = PrinterHeight; - /* Druckauftrag anmelden */ - di.cbSize = sizeof( DOCINFO); - di.lpszDocName = graph->plotname; - di.lpszOutput = NULL; - if (StartDoc( PrinterDC, &di) <= 0) return 1; - if (StartPage( PrinterDC) <= 0) return 1; + /* Druckauftrag anmelden */ + di.cbSize = sizeof(DOCINFO); + di.lpszDocName = graph->plotname; + di.lpszOutput = NULL; + if (StartDoc(PrinterDC, &di) <= 0) { + return 1; + } + if (StartPage(PrinterDC) <= 0) { + return 1; + } - /* titel drucken */ - if (graph->plotname) { - UINT align; - align = GetTextAlign( PrinterDC); - SetTextAlign( PrinterDC, TA_RIGHT | TA_TOP | TA_NOUPDATECP); - TextOut( PrinterDC, PrinterWidth-graph->fontwidth, 1, graph->plotname, - (int)strlen(graph->plotname)); - SetTextAlign( PrinterDC, align); - } + /* titel drucken */ + if (graph->plotname) { + UINT align; + align = GetTextAlign(PrinterDC); + SetTextAlign(PrinterDC, TA_RIGHT | TA_TOP | TA_NOUPDATECP); + TextOut(PrinterDC, PrinterWidth-graph->fontwidth, 1, graph->plotname, + (int) strlen(graph->plotname)); + SetTextAlign(PrinterDC, align); + } - /* fertig */ - return(0); + /* fertig */ + return 0; } int WPRINT_Close(void) { - if (PrinterDC) { - EndPage( PrinterDC); - EndDoc( PrinterDC); - if (OldFont) { - SelectObject( PrinterDC, OldFont); - OldFont = NULL; - } - DeleteObject( PlotFont); - DeleteDC( PrinterDC); - PrinterDC = NULL; - } - return (0); + if (PrinterDC) { + EndPage(PrinterDC); + EndDoc(PrinterDC); + if (OldFont) { + SelectObject(PrinterDC, OldFont); + OldFont = NULL; + } + DeleteObject(PlotFont); + DeleteDC(PrinterDC); + PrinterDC = NULL; + } + return 0; } int WPRINT_Clear(void) { - return 0; + return 0; } int WPRINT_DrawLine(int x1, int y1, int x2, int y2) { - tpPrintData pd; - HPEN OldPen; - HPEN NewPen; - int ColIndex; + tpPrintData pd; + HPEN OldPen; + HPEN NewPen; + int ColIndex; - if (!currentgraph) return 0; - pd = pPrintData(currentgraph); - if (!pd) return 0; + if (!currentgraph) return 0; + pd = pPrintData(currentgraph); + if (!pd) return 0; - /* Farben/Dicke */ - ColIndex = pd->ColorIndex; - if (ColIndex > 1) - ColIndex = 1; + /* Farben/Dicke */ + ColIndex = pd->ColorIndex; + if (ColIndex > 1) + ColIndex = 1; - MoveToEx(PrinterDC, x1, PrinterHeight - y1, NULL); - NewPen = CreatePen( LineTable[pd->LineIndex], 0, ColorTable[ColIndex] ); - OldPen = SelectObject(PrinterDC, NewPen); - LineTo(PrinterDC, x2, PrinterHeight - y2); - OldPen = SelectObject(PrinterDC, OldPen); - DeleteObject( NewPen); - return (0); + MoveToEx(PrinterDC, x1, PrinterHeight - y1, NULL); + NewPen = CreatePen(LineTable[pd->LineIndex], 0, ColorTable[ColIndex] ); + OldPen = SelectObject(PrinterDC, NewPen); + LineTo(PrinterDC, x2, PrinterHeight - y2); + OldPen = SelectObject(PrinterDC, OldPen); + DeleteObject(NewPen); + return 0; } int WPRINT_Arc(int x0, int y0, int radius, double theta, double delta_theta) - /* - * Notes: - * Draws an arc of and center at (x0,y0) beginning at - * angle theta (in rad) and ending at theta + delta_theta - */ + /* + * Notes: + * Draws an arc of and center at (x0,y0) beginning at + * angle theta (in rad) and ending at theta + delta_theta + */ { - tpPrintData pd; - HPEN OldPen; - HPEN NewPen; - int left, right, top, bottom; - int xs, ys, xe, ye; - int yb; - int direction; - int ColIndex; - double r; - double dx0; - double dy0; + tpPrintData pd; + HPEN OldPen; + HPEN NewPen; + int left, right, top, bottom; + int xs, ys, xe, ye; + int yb; + int direction; + int ColIndex; + double r; + double dx0; + double dy0; - if (!currentgraph) return 0; - pd = pPrintData(currentgraph); - if (!pd) return 0; + if (!currentgraph) { + return 0; + } + pd = pPrintData(currentgraph); + if (!pd) { + return 0; + } - ColIndex = pd->ColorIndex; - if (ColIndex > 1) - ColIndex = 1; + ColIndex = pd->ColorIndex; + if (ColIndex > 1) { + ColIndex = 1; + } - direction = AD_COUNTERCLOCKWISE; - if (delta_theta < 0) { - theta = theta + delta_theta; - delta_theta = - delta_theta; - direction = AD_CLOCKWISE; - } - SetArcDirection( PrinterDC, direction); + direction = AD_COUNTERCLOCKWISE; + if (delta_theta < 0) { + theta = theta + delta_theta; + delta_theta = - delta_theta; + direction = AD_CLOCKWISE; + } + SetArcDirection(PrinterDC, direction); - /* Geometrische Vorueberlegungen */ - yb = PrinterHeight; - left = x0 - radius; - right = x0 + radius; - top = y0 + radius; - bottom = y0 - radius; + /* Geometrische Vorueberlegungen */ + yb = PrinterHeight; + left = x0 - radius; + right = x0 + radius; + top = y0 + radius; + bottom = y0 - radius; - r = radius; - dx0 = x0; - dy0 = y0; - xs = (int)(dx0 + (r * cos(theta))); - ys = (int)(dy0 + (r * sin(theta))); - xe = (int)(dx0 + (r * cos(theta + delta_theta))); - ye = (int)(dy0 + (r * sin(theta + delta_theta))); + r = radius; + dx0 = x0; + dy0 = y0; + xs = (int) (dx0 + (r * cos(theta))); + ys = (int) (dy0 + (r * sin(theta))); + xe = (int) (dx0 + (r * cos(theta + delta_theta))); + ye = (int) (dy0 + (r * sin(theta + delta_theta))); - /* Zeichnen */ - NewPen = CreatePen( LineTable[pd->LineIndex], 0, ColorTable[ColIndex] ); - OldPen = SelectObject(PrinterDC, NewPen); - Arc( PrinterDC, left, yb-top, right, yb-bottom, xs, yb-ys, xe, yb-ye); - OldPen = SelectObject(PrinterDC, OldPen); - DeleteObject( NewPen); + /* Zeichnen */ + NewPen = CreatePen(LineTable[pd->LineIndex], 0, ColorTable[ColIndex] ); + OldPen = SelectObject(PrinterDC, NewPen); + Arc(PrinterDC, left, yb-top, right, yb-bottom, xs, yb-ys, xe, yb-ye); + OldPen = SelectObject(PrinterDC, OldPen); + DeleteObject(NewPen); - return 0; + return 0; } -int WPRINT_Text( char * text, int x, int y, int degrees) +int WPRINT_Text(char * text, int x, int y, int degrees) { - tpPrintData pd; - int ColIndex; + tpPrintData pd; + int ColIndex; NG_IGNORE(degrees); - if (!currentgraph) return 0; - pd = pPrintData(currentgraph); - if (!pd) return 0; + if (!currentgraph) { + return 0; + } + pd = pPrintData(currentgraph); + if (!pd) { + return 0; + } - ColIndex = pd->ColorIndex; - if (ColIndex > 1) { - ColIndex = 1; - } + ColIndex = pd->ColorIndex; + if (ColIndex > 1) { + ColIndex = 1; + } - SetTextColor( PrinterDC, ColorTable[ColIndex]); - TextOut( PrinterDC, x, PrinterHeight - y - currentgraph->fontheight, text, (int) strlen(text)); - return (0); + SetTextColor(PrinterDC, ColorTable[ColIndex]); + TextOut(PrinterDC, x, PrinterHeight - y - currentgraph->fontheight, + text, (int) strlen(text)); + return 0; } int WPRINT_DefineColor(int colorid, double red, double green, double blue) { - /* nix */ + /* nix */ NG_IGNORE(colorid); NG_IGNORE(red); NG_IGNORE(green); NG_IGNORE(blue); - return (0); + return 0; } int WPRINT_DefineLinestyle(int num, int mask) { - /* nix */ + /* nix */ NG_IGNORE(num); NG_IGNORE(mask); - return (0); + return 0; } int WPRINT_SetLinestyle(int style) { - tpPrintData pd; - if (!currentgraph) return 0; - pd = pPrintData(currentgraph); - if (!pd) return 0; + tpPrintData pd; + if (!currentgraph) { + return 0; + } + pd = pPrintData(currentgraph); + if (!pd) { + return 0; + } - pd->LineIndex = style % NumLines; - return (0); + pd->LineIndex = style % NumLines; + return 0; } -int WPRINT_SetColor( int color) +int WPRINT_SetColor(int color) { - tpPrintData pd; - if (!currentgraph) return 0; - pd = pPrintData(currentgraph); - if (!pd) return 0; + tpPrintData pd; + if (!currentgraph) { + return 0; + } + pd = pPrintData(currentgraph); + if (!pd) { + return 0; + } - pd->ColorIndex = color; - return (0); + pd->ColorIndex = color; + return 0; } int WPRINT_Update(void) { - return (0); + return 0; } int WPRINT_DiagramReady(void) { - return 0; + return 0; } #endif /* HAS_WINGUI */ From 76cf00853c81d15108a73dc6793dcff8ec2f1a23 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sun, 15 Dec 2019 14:51:37 -0500 Subject: [PATCH 38/58] Added conjugate function conj(). --- src/frontend/parse.c | 1 + src/include/ngspice/fteext.h | 1 + src/maths/cmaths/cmath1.c | 103 ++++++++++++++++++++++++----------- src/maths/cmaths/cmath1.h | 1 + 4 files changed, 75 insertions(+), 31 deletions(-) diff --git a/src/frontend/parse.c b/src/frontend/parse.c index fe3a1d852..a1ac39fa4 100644 --- a/src/frontend/parse.c +++ b/src/frontend/parse.c @@ -161,6 +161,7 @@ struct func ft_funcs[] = { { "re", cx_real }, { "imag", cx_imag }, { "im", cx_imag }, + { "conj", cx_conj }, { "db", cx_db }, { "log", cx_log }, { "log10", cx_log10 }, diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 08e5e5449..0b7a43757 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -60,6 +60,7 @@ extern void *cx_unwrap(void *, short int , int , int *, short int *); extern void *cx_j(void *, short int , int , int *, short int *); extern void *cx_real(void *, short int , int , int *, short int *); extern void *cx_imag(void *, short int , int , int *, short int *); +extern void *cx_conj(void *, short int , int , int *, short int *); extern void *cx_pos(void *, short int , int , int *, short int *); extern void *cx_db(void *, short int , int , int *, short int *); extern void *cx_log10(void *, short int , int , int *, short int *); diff --git a/src/maths/cmaths/cmath1.c b/src/maths/cmaths/cmath1.c index 8b5480a6f..34a5409c6 100644 --- a/src/maths/cmaths/cmath1.c +++ b/src/maths/cmaths/cmath1.c @@ -117,68 +117,109 @@ cx_unwrap(void *data, short int type, int length, int *newlength, short int *new } /* If this is pure imaginary we might get real, but never mind... */ - -void * -cx_j(void *data, short int type, int length, int *newlength, short int *newtype) +void *cx_j(void *data, short int type, int length, int *newlength, + short int *newtype) { ngcomplex_t *c = alloc_c(length); - ngcomplex_t *cc = (ngcomplex_t *) data; - double *dd = (double *) data; - int i; - *newlength = length; *newtype = VF_COMPLEX; - if (type == VF_COMPLEX) + + if (type == VF_COMPLEX) { + ngcomplex_t *cc = (ngcomplex_t *) data; + int i; for (i = 0; i < length; i++) { - realpart(c[i]) = - imagpart(cc[i]); + realpart(c[i]) = -imagpart(cc[i]); imagpart(c[i]) = realpart(cc[i]); } - else + } + else { + double *dd = (double *) data; + int i; for (i = 0; i < length; i++) { imagpart(c[i]) = dd[i]; /* Real part is already 0. */ } - return ((void *) c); + } + return (void *) c; } -void * -cx_real(void *data, short int type, int length, int *newlength, short int *newtype) +void *cx_real(void *data, short int type, int length, int *newlength, + short int *newtype) { double *d = alloc_d(length); - double *dd = (double *) data; - ngcomplex_t *cc = (ngcomplex_t *) data; - int i; *newlength = length; *newtype = VF_REAL; - if (type == VF_COMPLEX) - for (i = 0; i < length; i++) + if (type == VF_COMPLEX) { + ngcomplex_t *cc = (ngcomplex_t *) data; + int i; + for (i = 0; i < length; i++) { d[i] = realpart(cc[i]); - else - for (i = 0; i < length; i++) + } + } + else { + double *dd = (double *) data; + int i; + for (i = 0; i < length; i++) { d[i] = dd[i]; - return ((void *) d); + } + } + return (void *) d; } -void * -cx_imag(void *data, short int type, int length, int *newlength, short int *newtype) +void *cx_imag(void *data, short int type, int length, int *newlength, + short int *newtype) { double *d = alloc_d(length); - double *dd = (double *) data; - ngcomplex_t *cc = (ngcomplex_t *) data; - int i; *newlength = length; *newtype = VF_REAL; - if (type == VF_COMPLEX) - for (i = 0; i < length; i++) + if (type == VF_COMPLEX) { + ngcomplex_t *cc = (ngcomplex_t *) data; + int i; + for (i = 0; i < length; i++) { d[i] = imagpart(cc[i]); - else - for (i = 0; i < length; i++) + } + } + else { + double *dd = (double *) data; + int i; + for (i = 0; i < length; i++) { d[i] = dd[i]; - return ((void *) d); + } + } + return (void *) d; } + + +/* Create complex conjugate of data */ +void *cx_conj(void *data, short int type, int length, + int *p_newlength, short int *p_newtype) +{ + /* Length and type do not change */ + *p_newlength = length; + *p_newtype = type; + + /* For complex, copy with conjugation */ + if (type == VF_COMPLEX) { + ngcomplex_t * const c_dst = alloc_c(length); + ngcomplex_t *c_dst_cur = c_dst; + ngcomplex_t *c_src_cur = (ngcomplex_t *) data; + ngcomplex_t * const c_src_end = c_src_cur + length; + for ( ; c_src_cur < c_src_end; c_src_cur++, c_dst_cur++) { + c_dst_cur->cx_real = c_src_cur->cx_real; + c_dst_cur->cx_imag = -c_src_cur->cx_imag; + } + return (void *) c_dst; + } + + /* Else real, so just copy */ + return memcpy(alloc_d(length), data, length * sizeof(double)); +} /* end of function cx_conj */ + + + /* This is obsolete... */ void * diff --git a/src/maths/cmaths/cmath1.h b/src/maths/cmaths/cmath1.h index 3ffc3ff99..f13587d67 100644 --- a/src/maths/cmaths/cmath1.h +++ b/src/maths/cmaths/cmath1.h @@ -14,6 +14,7 @@ void * cx_unwrap(void *data, short int type, int length, int *newlength, short i void * cx_j(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_real(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_imag(void *data, short int type, int length, int *newlength, short int *newtype); +void * cx_conj(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_pos(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_db(void *data, short int type, int length, int *newlength, short int *newtype); void * cx_log10(void *data, short int type, int length, int *newlength, short int *newtype); From de7d29250104fccf62745b4bcb5dc4b83c58da70 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 16 Dec 2019 01:40:25 -0500 Subject: [PATCH 39/58] Added basic help messages in addtion to reference to web pages --- src/frontend/com_ghelp.c | 23 ++++++------ src/frontend/com_help.c | 81 +++++++++++++++++++++++++++------------- 2 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/frontend/com_ghelp.c b/src/frontend/com_ghelp.c index fdb247ad6..ab0241694 100644 --- a/src/frontend/com_ghelp.c +++ b/src/frontend/com_ghelp.c @@ -17,19 +17,20 @@ * Internet Archive using the link below. * https://web.archive.org/web/20180221111839/http://newton.ex.ac.uk/teaching/CDHW/Electronics2/userguide/ */ -void -com_ghelp(wordlist *wl) +#define BASE_HELP_URL "http://ngspice.sourceforge.net/docs" +void com_ghelp(wordlist *wl) { -#if defined(HAS_WINGUI) || defined(_MSC_VER) || defined(__MINGW32__) || defined(X_DISPLAY_MISSING) || defined(NOINTHELP) +#if defined(HAS_WINGUI) || defined(_MSC_VER) || defined(__MINGW32__) ||\ + defined(X_DISPLAY_MISSING) || defined(NOINTHELP) + com_help(wl); - NG_IGNORE(wl); - - (void) printf("Internal help is no longer available!\n" - "For the latest official ngspice manual in PDF format, " - "please see\n" - " http://ngspice.sourceforge.net/docs/ngspice-manual.pdf\n" - "Or for HTML see\n" - " http://ngspice.sourceforge.net/docs/ngspice-html-manual/manual.html\n"); + /* After brief help from com_help, add info on the web links to the + * the PDF and HTML versions of the manual */ + (void) out_printf("For further details please see the latest official " + "ngspice manual in PDF format at\n" + " " BASE_HELP_URL "/ngspice-manual.pdf\n" + "or in HTML format at\n" + " " BASE_HELP_URL "/ngspice-html-manual/manual.html\n\n"); return; #else diff --git a/src/frontend/com_help.c b/src/frontend/com_help.c index 1bff50266..1039ec386 100644 --- a/src/frontend/com_help.c +++ b/src/frontend/com_help.c @@ -9,51 +9,78 @@ #include "com_help.h" #include "ngspice/fteext.h" - -void -com_help(wordlist *wl) +#define N_CMD_DFLT 512 +void com_help(wordlist *wl) { - struct comm *c; - struct comm *ccc[512]; /* Should be enough. */ - int numcoms, i; bool allflag = FALSE; + /* Make empty list and "all" behave the same except for the part + * related to "help all" */ if (wl && eq(wl->wl_word, "all")) { allflag = TRUE; - wl = NULL; /* XXX Probably right */ + wl = (wordlist *) NULL; } - /* We want to use more mode whether "moremode" is set or not. */ + /* We want to use more mode whether "moremode" is set or not. + * In that case the code below should be changed... */ out_moremode = TRUE; out_init(); out_moremode = FALSE; + + if (wl == NULL) { - out_printf("For a complete description " - "read the Spice3 User's Manual.\n"); + struct comm *ccc_dflt[N_CMD_DFLT]; /* Should be enough. */ + struct comm **ccc; /* dynamic alloc in case it is not */ + int numcoms; if (!allflag) { out_printf("For a list of all commands " "type \"help all\", for a short\n" "description of \"command\", " "type \"help command\".\n"); + return; + } + + /* Count the number of commands */ + for (numcoms = 0; cp_coms[numcoms].co_func != NULL; numcoms++) { + ; + } + if (numcoms > N_CMD_DFLT) { + ccc = TMALLOC(struct comm *, numcoms); + } + else { + ccc = ccc_dflt; } /* Sort the commands */ - for (numcoms = 0; cp_coms[numcoms].co_func != NULL; numcoms++) + for (numcoms = 0; cp_coms[numcoms].co_func != NULL; numcoms++) { ccc[numcoms] = &cp_coms[numcoms]; + } qsort(ccc, (size_t) numcoms, sizeof(struct comm *), hcomp); - for (i = 0; i < numcoms; i++) { - if ((ccc[i]->co_spiceonly && ft_nutmeg) || - (ccc[i]->co_help == NULL) || - (!allflag && !ccc[i]->co_major)) - continue; - out_printf("%s ", ccc[i]->co_comname); - out_printf(ccc[i]->co_help, cp_program); - out_send("\n"); + /* Print help for each of the "major" commands */ + { + int i; + for (i = 0; i < numcoms; i++) { + if ((ccc[i]->co_spiceonly && ft_nutmeg) || + (ccc[i]->co_help == NULL) || + (!allflag && !ccc[i]->co_major)) { + continue; + } + out_printf("%s ", ccc[i]->co_comname); + out_printf(ccc[i]->co_help, cp_program); + out_send("\n"); + } } - } else { + + /* Free allocation if it was required */ + if (ccc != ccc_dflt) { + txfree(ccc); + } + } + else { while (wl != NULL) { + struct comm *c; for (c = &cp_coms[0]; c->co_func != NULL; c++) if (eq(wl->wl_word, c->co_comname)) { out_printf("%s ", c->co_comname); @@ -73,16 +100,20 @@ com_help(wordlist *wl) if (al == NULL) { fprintf(cp_out, "Sorry, no help for %s.\n", wl->wl_word); - } else { + } + else { out_printf("%s is aliased to ", wl->wl_word); /* Minor badness here... */ wl_print(al->al_text, cp_out); out_send("\n"); } - } - wl = wl->wl_next; - } + } /* end of case that a function with the given name was found */ + wl = wl->wl_next; /* step to next word in list of help items */ + } /* end of loop over list of help items */ } out_send("\n"); -} +} /* end of function com_help */ + + + From d4b71500e254e25b40d9c6a2b34e07b96d35ef49 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Mon, 16 Dec 2019 14:53:40 -0500 Subject: [PATCH 40/58] Opened binary files for writing using "wb" in all cases --- src/frontend/rawfile.c | 103 ++++++++++++++++++++++++----------------- src/frontend/runcoms.c | 96 +++++++++++++++++++++----------------- 2 files changed, 114 insertions(+), 85 deletions(-) diff --git a/src/frontend/rawfile.c b/src/frontend/rawfile.c index a4b50ab42..38dc60684 100644 --- a/src/frontend/rawfile.c +++ b/src/frontend/rawfile.c @@ -35,8 +35,7 @@ int raw_prec = -1; /* How many sigfigs to use, default 15 (max). */ /* Write a raw file. We write everything in the plot pointed to. */ -void -raw_write(char *name, struct plot *pl, bool app, bool binary) +void raw_write(char *name, struct plot *pl, bool app, bool binary) { FILE *fp; bool realflag = TRUE, writedims; @@ -58,12 +57,12 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) return; } - if (raw_prec != -1) + if (raw_prec != -1) { prec = raw_prec; - else + } + else { prec = DEFPREC; - -#if defined(__MINGW32__) || defined(_MSC_VER) + } /* - Binary file binary write - hvogt 15.03.2000 ---------------------*/ if (binary) { @@ -72,7 +71,8 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) return; } fprintf(cp_out, "binary raw file \"%s\"\n", name); - } else { + } + else { if ((fp = fopen(name, app ? "a" : "w")) == NULL) { perror(name); return; @@ -81,19 +81,11 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) } /* --------------------------------------------------------------------*/ -#else - - if (!(fp = fopen(name, app ? "a" : "w"))) { - perror(name); - return; - } - -#endif - numdims = nvars = length = 0; for (v = pl->pl_dvecs; v; v = v->v_next) { - if (iscomplex(v)) + if (iscomplex(v)) { realflag = FALSE; + } nvars++; /* Find the length and dimensions of the longest vector * in the plot. @@ -125,20 +117,24 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) fprintf(fp, "Dimensions: %s\n", buf); } - for (wl = pl->pl_commands; wl; wl = wl->wl_next) + for (wl = pl->pl_commands; wl; wl = wl->wl_next) { fprintf(fp, "Command: %s\n", wl->wl_word); + } for (vv = pl->pl_env; vv; vv = vv->va_next) { wl = cp_varwl(vv); if (vv->va_type == CP_BOOL) { fprintf(fp, "Option: %s\n", vv->va_name); - } else { + } + else { fprintf(fp, "Option: %s = ", vv->va_name); - if (vv->va_type == CP_LIST) + if (vv->va_type == CP_LIST) { fprintf(fp, "( "); + } wl_print(wl, fp); - if (vv->va_type == CP_LIST) + if (vv->va_type == CP_LIST) { fprintf(fp, " )"); + } (void) putc('\n', fp); } } @@ -146,8 +142,9 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) /* Before we write the stuff out, make sure that the scale is the first * in the list. */ - for (lv = NULL, v = pl->pl_dvecs; v != pl->pl_scale; v = v->v_next) + for (lv = NULL, v = pl->pl_dvecs; v != pl->pl_scale; v = v->v_next) { lv = v; + } if (lv) { lv->v_next = v->v_next; v->v_next = pl->pl_dvecs; @@ -163,26 +160,34 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) } fprintf(fp, "\t%d\ti(%s)\t%s", i++, v->v_name, ft_typenames(v->v_type)); if (branch != NULL) *branch = '#'; - } else if (v->v_type == SV_VOLTAGE) { - fprintf(fp, "\t%d\t%s\t%s", i++, v->v_name, ft_typenames(v->v_type)); - } else { + } + else if (v->v_type == SV_VOLTAGE) { fprintf(fp, "\t%d\t%s\t%s", i++, v->v_name, ft_typenames(v->v_type)); } - if (v->v_flags & VF_MINGIVEN) + else { + fprintf(fp, "\t%d\t%s\t%s", i++, v->v_name, ft_typenames(v->v_type)); + } + if (v->v_flags & VF_MINGIVEN) { fprintf(fp, " min=%e", v->v_minsignal); - if (v->v_flags & VF_MAXGIVEN) + } + if (v->v_flags & VF_MAXGIVEN) { fprintf(fp, " max=%e", v->v_maxsignal); - if (v->v_defcolor) + } + if (v->v_defcolor) { fprintf(fp, " color=%s", v->v_defcolor); - if (v->v_gridtype) + } + if (v->v_gridtype) { fprintf(fp, " grid=%d", v->v_gridtype); - if (v->v_plottype) + } + if (v->v_plottype) { fprintf(fp, " plot=%d", v->v_plottype); + } /* Only write dims if they are different from default. */ writedims = FALSE; if (v->v_numdims != numdims) { writedims = TRUE; - } else { + } + else { for (j = 0; j < numdims; j++) if (dims[j] != v->v_dims[j]) writedims = TRUE; @@ -204,58 +209,70 @@ raw_write(char *name, struct plot *pl, bool app, bool binary) dd = (isreal(v) ? v->v_realdata[i] : realpart(v->v_compdata[i])); (void) fwrite(&dd, sizeof(double), 1, fp); - } else if (isreal(v)) { + } + else if (isreal(v)) { dd = v->v_realdata[i]; (void) fwrite(&dd, sizeof(double), 1, fp); dd = 0.0; (void) fwrite(&dd, sizeof(double), 1, fp); - } else { + } + else { dd = realpart(v->v_compdata[i]); (void) fwrite(&dd, sizeof(double), 1, fp); dd = imagpart(v->v_compdata[i]); (void) fwrite(&dd, sizeof(double), 1, fp); } - } else if (raw_padding) { + } + else if (raw_padding) { dd = 0.0; if (realflag) { (void) fwrite(&dd, sizeof(double), 1, fp); - } else { + } + else { (void) fwrite(&dd, sizeof(double), 1, fp); (void) fwrite(&dd, sizeof(double), 1, fp); } } } } - } else { + } + else { fprintf(fp, "Values:\n"); for (i = 0; i < length; i++) { fprintf(fp, " %d", i); for (v = pl->pl_dvecs; v; v = v->v_next) { if (i < v->v_length) { - if (realflag) + if (realflag) { fprintf(fp, "\t%.*e\n", prec, isreal(v) ? v->v_realdata[i] : realpart(v->v_compdata[i])); - else if (isreal(v)) + } + else if (isreal(v)) { fprintf(fp, "\t%.*e,0.0\n", prec, v->v_realdata[i]); - else + } + else { fprintf(fp, "\t%.*e,%.*e\n", prec, realpart(v->v_compdata[i]), prec, imagpart(v->v_compdata[i])); - } else if (raw_padding) { - if (realflag) + } + } + else if (raw_padding) { + if (realflag) { fprintf(fp, "\t%.*e\n", prec, 0.0); - else + } + else { fprintf(fp, "\t%.*e,%.*e\n", prec, 0.0, prec, 0.0); + } } } (void) putc('\n', fp); } } (void) fclose(fp); -} +} /* end of function raw_write */ + /* Read a raw file. Returns a list of plot structures. This routine should be diff --git a/src/frontend/runcoms.c b/src/frontend/runcoms.c index 1c5b629ec..ecd74c570 100644 --- a/src/frontend/runcoms.c +++ b/src/frontend/runcoms.c @@ -187,14 +187,14 @@ com_pss(wordlist *wl) #endif -static int -dosim( - char *what, /* in: command (pz,op,dc,ac,tf,tran,sens,disto,noise,run) */ - wordlist *wl /* in: command option */ - /* global variables in: ft_curckt, ft_circuits, - out: ft_setflag, ft_intrpt, rawfileFp, rawfileBinary, - last_used_rawfile - */ +static int dosim( + char *what, /* in: command + * (pz,op,dc,ac,tf,tran,sens,disto,noise,run) */ + wordlist *wl /* in: command option */ + /* global variables in: ft_curckt, ft_circuits, + * out: ft_setflag, ft_intrpt, rawfileFp, rawfileBinary, + * last_used_rawfile + */ ) { wordlist *ww = NULL; @@ -205,8 +205,9 @@ dosim( /* set file type to binary or to what is given by environmental variable SPICE_ASCIIRAWFILE in ivars.c */ bool ascii = AsciiRawFile; - if (eq(what, "run") && wl) + if (eq(what, "run") && wl) { dofile = TRUE; + } /* add "what" to beginning of wordlist wl, except "what" equals "run" and a rawfile name is given (in wl) */ if (!dofile) { @@ -214,10 +215,12 @@ dosim( } /* reset output file type according to variable given in spinit */ if (cp_getvar("filetype", CP_STRING, buf, sizeof(buf))) { - if (eq(buf, "binary")) + if (eq(buf, "binary")) { ascii = FALSE; - else if (eq(buf, "ascii")) + } + else if (eq(buf, "ascii")) { ascii = TRUE; + } else { fprintf(cp_err, "Warning: strange file type \"%s\" (using \"ascii\")\n", buf); @@ -228,17 +231,19 @@ dosim( if (!ft_curckt) { fprintf(cp_err, "Error: there aren't any circuits loaded.\n"); return 1; - } else if (ft_curckt->ci_ckt == NULL) { /* Set noparse? */ + } + else if (ft_curckt->ci_ckt == NULL) { /* Set noparse? */ fprintf(cp_err, "Error: circuit not parsed.\n"); return 1; } - for (ct = ft_circuits; ct; ct = ct->ci_next) + for (ct = ft_circuits; ct; ct = ct->ci_next) { if (ct->ci_inprogress && (ct != ft_curckt)) { fprintf(cp_err, "Warning: losing old state for circuit '%s'\n", ct->ci_name); ct->ci_inprogress = FALSE; } + } /* "resume" will never occur in ngspice */ if (ft_curckt->ci_inprogress && eq(what, "resume")) { ft_setflag = TRUE; /* don't allow abort upon interrupt during run */ @@ -257,9 +262,10 @@ dosim( ft_intrpt = FALSE; /* command "run" is given with rawfile name in wl */ if (dofile) { - if (!*wl->wl_word) + if (!*wl->wl_word) { rawfileFp = stdout; -#if defined(__MINGW32__) || defined(_MSC_VER) + } + /* ask if binary or ASCII, open file with wb or w */ else if (ascii) { if ((rawfileFp = fopen(wl->wl_word, "w")) == NULL) { @@ -269,7 +275,7 @@ dosim( } fprintf(cp_out, "ASCII raw file \"%s\"\n", wl->wl_word); } - else if (!ascii) { + else { /* binary */ if ((rawfileFp = fopen(wl->wl_word, "wb")) == NULL) { perror(wl->wl_word); ft_setflag = FALSE; @@ -277,26 +283,22 @@ dosim( } fprintf(cp_out, "binary raw file \"%s\"\n", wl->wl_word); } -/*---------------------------------------------------------------------------*/ -#else - else if (!(rawfileFp = fopen(wl->wl_word, "w"))) { - perror(wl->wl_word); - ft_setflag = FALSE; - return 1; - } -#endif /* __MINGW32__ */ rawfileBinary = !ascii; - } else { + } + else { rawfileFp = NULL; } /*save rawfile name */ - if (last_used_rawfile) + if (last_used_rawfile) { tfree(last_used_rawfile); - if (rawfileFp) + } + if (rawfileFp) { last_used_rawfile = copy(wl->wl_word); - else + } + else { last_used_rawfile = NULL; + } ft_curckt->ci_inprogress = TRUE; cp_vset("sim_status", CP_NUM, &err); @@ -308,15 +310,18 @@ dosim( #ifdef XSPICE /* gtri - add - 12/12/90 - wbk - record error and return errchk */ g_ipc.run_error = IPC_TRUE; - if (g_ipc.enabled) + if (g_ipc.enabled) { ipc_send_errchk(); + } /* gtri - end - 12/12/90 */ #endif - } else { + } + else { ft_curckt->ci_inprogress = FALSE; } /* Do a run of the circuit */ - } else { + } + else { err = if_run(ft_curckt->ci_ckt, what, ww, ft_curckt->ci_symtab); if (err == 1) { /* The circuit was interrupted somewhere. */ @@ -324,17 +329,20 @@ dosim( #ifdef XSPICE /* record error and return errchk */ g_ipc.run_error = IPC_TRUE; - if (g_ipc.enabled) + if (g_ipc.enabled) { ipc_send_errchk(); + } /* gtri - end - 12/12/90 */ #endif err = 0; - } else if (err == 2) { + } + else if (err == 2) { fprintf(cp_err, "%s simulation(s) aborted\n", what); ft_curckt->ci_inprogress = FALSE; err = 1; cp_vset("sim_status", CP_NUM, &err); - } else { + } + else { ft_curckt->ci_inprogress = FALSE; } } @@ -342,9 +350,11 @@ dosim( if (rawfileFp) { if (ftell(rawfileFp) == 0) { (void) fclose(rawfileFp); - if (wl) + if (wl) { (void) unlink(wl->wl_word); - } else { + } + } + else { (void) fclose(rawfileFp); } } @@ -353,22 +363,24 @@ dosim( /* va: garbage collection: unlink first word (inserted here) and tfree it */ if (!dofile) { - tfree(ww->wl_word); - if (wl) + txfree(ww->wl_word); + if (wl) { wl->wl_prev = NULL; - tfree(ww); + } + txfree(ww); } /* execute the .measure statements */ - if (!err && ft_curckt->ci_last_an && ft_curckt->ci_meas) + if (!err && ft_curckt->ci_last_an && ft_curckt->ci_meas) { do_measure(ft_curckt->ci_last_an, FALSE); + } return err; -} +} /* end of function dosim */ + /* Usage is run [filename] */ - void com_run(wordlist *wl) { /* ft_getsaves(); */ From c6319dd19e13c276b34d7dc7d8a9dc3b2bb1e2f0 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 17 Dec 2019 01:38:33 -0500 Subject: [PATCH 41/58] Added -D/--define option to define a variable from the command line. --- src/frontend/inp.c | 4 +- src/include/ngspice/fteext.h | 2 +- src/main.c | 237 +++++++++++++++++++++-------------- 3 files changed, 147 insertions(+), 96 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index a4e0c7d68..bb5009bd9 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -1604,13 +1604,13 @@ com_source(wordlist *wl) } -void inp_source(char *file) +void inp_source(const char *file) { /* This wordlist is special in that nothing in it should be freed -- * the file name word is "borrowed" from the argument to file and * the wordlist is allocated on the stack. */ static struct wordlist wl = { NULL, NULL, NULL }; - wl.wl_word = file; + wl.wl_word = (char *) file; com_source(&wl); } diff --git a/src/include/ngspice/fteext.h b/src/include/ngspice/fteext.h index 0b7a43757..2c12fbd31 100644 --- a/src/include/ngspice/fteext.h +++ b/src/include/ngspice/fteext.h @@ -210,7 +210,7 @@ extern bool gr_circular; void inp_dodeck(struct card *deck, char *tt, wordlist *end, bool reuse, struct card *options, char *filename); -extern void inp_source(char *file); +extern void inp_source(const char *file); void inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile); extern void inp_casefix(char *string); extern void inp_list(FILE *file, struct card *deck, struct card *extras, int type); diff --git a/src/main.c b/src/main.c index e2f7324f3..f452ecb3a 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include "ngspice/ngspice.h" #include "ngspice/const.h" +#include "ngspice/dstring.h" #include #include @@ -177,7 +178,7 @@ static int app_event_func(void); static void show_help(void); static void show_version(void); -static bool read_initialisation_file(char *dir, char *name); +static bool read_initialisation_file(const char *dir, const char *name); #ifdef SIMULATOR static void append_to_stream(FILE *dest, FILE *source); @@ -621,8 +622,9 @@ app_rl_readlines(void) for (;;) { history_set_pos(history_length); - if (SETJMP(jbuf, 1)) /* Set location to jump to after handling SIGINT (ctrl-C) */ + if (SETJMP(jbuf, 1)) { /* Set location to jump to after handling SIGINT (ctrl-C) */ ft_sigintr_cleanup(); + } line = readline(prompt()); @@ -636,9 +638,11 @@ app_rl_readlines(void) if (s == 2) { fprintf(stderr, "-> %s\n", expanded_line); - } else if (s == -1) { + } + else if (s == -1) { fprintf(stderr, "readline: %s\n", expanded_line); - } else { + } + else { cp_evloop(expanded_line); add_history(expanded_line); } @@ -650,10 +654,12 @@ app_rl_readlines(void) /* History gets written in ../fte/misccoms.c com_quit */ #else - while (cp_evloop(NULL) == 1) + while (cp_evloop(NULL) == 1) { ; + } #endif /* defined(HAVE_GNUREADLINE) || defined(HAVE_BSDEDITLINE) */ -} +} /* end of function app_rl_readlines */ + /* -------------------------------------------------------------------------- */ @@ -666,6 +672,7 @@ show_help(void) " -a --autorun run the loaded netlist\n" " -b, --batch process FILE in batch mode\n" " -c, --circuitfile=FILE set the circuitfile\n" + " -D, --define=variable[=value] define variable to true/[value]\n" " -i, --interactive run in interactive mode\n" " -n, --no-spiceinit don't load the local or user's config file\n" " -o, --output=FILE set the outputfile\n" @@ -713,29 +720,32 @@ append_to_stream(FILE *dest, FILE *source) name is the initialisation file's name Return true on success SJB 25th April 2005 */ -static bool -read_initialisation_file(char *dir, char *name) +static bool read_initialisation_file(const char *dir, const char *name) { - char *path; + const char *path; bool result = FALSE; /* check name */ - if (!name || *name == '\0') + if (!name || *name == '\0') { return FALSE; /* Fail; name needed */ + } /* contruct the full path */ if (!dir || *dir == '\0') { path = name; - } else { + } + else { path = tprintf("%s" DIR_PATHSEP "%s", dir, name); - if (!path) + if (!path) { return FALSE; /* memory allocation error */ + } } /* now access the file */ #ifdef HAVE_UNISTD_H - if (access(path, R_OK) == 0) + if (access(path, R_OK) == 0) { result = TRUE; + } #else { FILE *fp = fopen(path, "r"); @@ -753,44 +763,45 @@ read_initialisation_file(char *dir, char *name) #endif } - if (path != name) - tfree(path); + if (path != name) { /* Allocated by tprintf() */ + txfree(path); + } return result; -} +} /* end of function read_initialisation_file */ + + /* -------------------------------------------------------------------------- */ - -static void -print_news(void) +static void print_news(void) { if (News_File && *News_File) { - char *fname = cp_tildexpand(News_File); /*DG Memory leak */ - FILE *fp = fopen(fname, "r"); - tfree(fname); + const char * const fname = cp_tildexpand(News_File); /*DG Memory leak */ + FILE * const fp = fopen(fname, "r"); + txfree(fname); if (fp) { char buf[BSIZE_SP]; - while (fgets(buf, BSIZE_SP, fp)) + while (fgets(buf, BSIZE_SP, fp)) { fputs(buf, stdout); + } fclose(fp); } } -} +} /* end of function print_news */ #ifdef HAS_WINGUI #define main xmain #endif -int -main(int argc, char **argv) +int main(int argc, char **argv) { char log_file[BSIZE_SP]; char soa_log_file[BSIZE_SP]; - volatile bool readinit = TRUE; - volatile bool istty = TRUE; - bool iflag = FALSE; - bool qflag = FALSE; + bool readinit = TRUE; /* read initialization file */ + bool istty = TRUE; + bool iflag = FALSE; /* flag for interactive mode */ + bool qflag = FALSE; /* flag for command completion */ FILE * volatile circuit_file; bool oflag = FALSE; @@ -819,18 +830,20 @@ main(int argc, char **argv) #if defined(HAVE_GNUREADLINE) || defined(HAVE_BSDEDITLINE) application_name = strrchr(argv[0], '/'); - if (application_name) - application_name ++; - else + if (application_name) { + ++application_name; + } + else { application_name = argv[0]; + } #endif - ivars(argv[0]); + ivars(argv[0]); /* Create internal variables */ + /* Set default data sources */ cp_in = stdin; cp_out = stdout; cp_err = stderr; - circuit_file = stdin; #if defined(HAVE_ISATTY) && !defined(HAS_WINGUI) @@ -859,6 +872,7 @@ main(int argc, char **argv) enum { soa_log = 1001, }; static struct option long_options[] = { + {"define", required_argument, NULL, 'D'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"batch", no_argument, NULL, 'b'}, @@ -878,13 +892,31 @@ main(int argc, char **argv) int option_index = 0; - int c = getopt_long(argc, argv, "hvbac:ino:pqr:st:", + int c = getopt_long(argc, argv, "D:bac:ino:pqr:st:", long_options, &option_index); - if (c == -1) + if (c == -1) { break; + } switch (c) { + case 'D': /* Definition of variable */ + if (optarg) { + const char *eq = strchr(optarg, '='); + if (eq == (char *) NULL) { /* no assignment */ + bool true_val = TRUE; + cp_vset(optarg, CP_BOOL, &true_val); + } + else { + DS_CREATE(ds, 100); + if (ds_cat_mem(&ds, optarg, eq - optarg) == 0) { + cp_vset(ds_get_buf(&ds), CP_STRING, eq + 1); + } + ds_free(&ds); + } + } + break; + case 'h': /* Help */ show_help(); sp_shutdown(EXIT_INFO); @@ -952,8 +984,9 @@ main(int argc, char **argv) break; case 'r': /* The raw file */ - if (optarg) + if (optarg) { cp_vset("rawfile", CP_STRING, optarg); + } rflag = TRUE; break; @@ -962,8 +995,9 @@ main(int argc, char **argv) break; case 't': - if (optarg) + if (optarg) { cp_vset("term", CP_STRING, optarg); + } break; case soa_log: @@ -986,15 +1020,19 @@ main(int argc, char **argv) com_version(NULL); - if (ft_servermode) + if (ft_servermode) { fprintf(stdout, "\nServer mode\n\n"); - else if (ft_batchmode) + } + else if (ft_batchmode) { fprintf(stdout, "\nBatch mode\n\n"); - else + } + else { fprintf(stdout, "\nInteractive mode, better used without -o option\n\n"); + } - if (rflag) + if (rflag) { fprintf(stdout, "Simulation output goes to rawfile: %s\n", ft_rawfile); + } fprintf(stdout, "Comments and warnings go to log-file: %s\n\n", log_file); @@ -1036,19 +1074,26 @@ main(int argc, char **argv) if_getparam = nutif_getparam; #endif - if ((!iflag && !istty) || ft_servermode) /* (batch and file) or server operation */ + if ((!iflag && !istty) || ft_servermode) { /* (batch and file) or + * server operation */ ft_batchmode = TRUE; + } - if ((iflag && !istty) || qflag) /* (interactive and file) or command completion */ + if ((iflag && !istty) || qflag) { /* (interactive and file) or + * command completion */ cp_nocc = TRUE; /* set command completion */ - else + } + else { cp_nocc = FALSE; + } - if (ft_servermode) /* in server no init file */ + if (ft_servermode) { /* in server no init file */ readinit = FALSE; + } - if (!istty || ft_batchmode) /* file or batch - no more output */ + if (!istty || ft_batchmode) { /* file or batch - no more output */ out_moremode = FALSE; + } /* Get information on memory status upon startup. Would like to do this later, but cpinit evals commands. @@ -1056,7 +1101,7 @@ main(int argc, char **argv) init_rlimits(); /* Have to initialize cp now. - fcn is in cpitf.c*/ + fcn is in cpitf.c */ ft_cpinit(); @@ -1090,12 +1135,10 @@ main(int argc, char **argv) /* To catch interrupts during .spiceinit... */ if (SETJMP(jbuf, 1)) { - ft_sigintr_cleanup(); fprintf(cp_err, "Warning: error executing .spiceinit.\n"); - - } else { - + } + else { if (readinit) { /* load user's initialisation file try accessing the initialisation file in the current directory @@ -1105,31 +1148,26 @@ main(int argc, char **argv) /* if that failed try in the user's home directory if their HOME environment variable is set */ char *homedir = getenv("HOME"); - if (homedir) { - if (FALSE == read_initialisation_file(homedir, INITSTR) && - FALSE == read_initialisation_file(homedir, ALT_INITSTR)) { - ; + if (homedir == (char *) NULL) { /* no HOME env variable */ + /* If there is no HOME environment (e.g. MS Windows), + * try user's profile directory */ + homedir = getenv("USERPROFILE"); + } + + if (homedir) { /* Found a home location */ + if (FALSE == read_initialisation_file(homedir, INITSTR)) { + (void) read_initialisation_file(homedir, ALT_INITSTR); } } - else { - /* If there is no HOME environment (e.g. MS Windows), try user's profile directory */ - homedir = getenv("USERPROFILE"); - if (homedir) - if (FALSE == read_initialisation_file(homedir, INITSTR) && - FALSE == read_initialisation_file(homedir, ALT_INITSTR)) { - ; - } - } - } - } + } /* end of case init file not found in current directory */ + } /* end of case that init file is to be read */ if (!ft_batchmode) { com_version(NULL); DevInit(); print_news(); } - - } + } /* end of normal execution for setjmp() */ #ifdef SIMULATOR @@ -1139,12 +1177,10 @@ main(int argc, char **argv) * process any of these args. */ if (SETJMP(jbuf, 1)) { - ft_sigintr_cleanup(); fprintf(cp_err, "Warning: error executing during ngspice startup.\n"); - - } else { - + } + else { bool gotone = FALSE; cp_interactive = FALSE; @@ -1179,7 +1215,7 @@ main(int argc, char **argv) startup time. */ FILE *tempfile = tmpfile(); - char *dname = NULL; /* input file*/ + char *dname = NULL; /* input file */ #if defined(HAS_WINGUI) || defined(_MSC_VER) || defined(__MINGW32__) char *tpf = NULL; /* temporary file */ @@ -1188,12 +1224,12 @@ main(int argc, char **argv) in directory C:\something (no write permission to root C:). Then we add a tempfile in the local directory. File will be removed automatically due to TD option in fopen */ - if (tempfile == NULL) { tpf = smktemp("sp"); tempfile = fopen(tpf, "w+bTD"); if (tempfile == NULL) { - fprintf(stderr, "Could not open a temporary file to save and use optional arguments."); + fprintf(stderr, "Could not open a temporary file " + "to save and use optional arguments."); sp_shutdown(EXIT_BAD); } } @@ -1204,15 +1240,17 @@ main(int argc, char **argv) sp_shutdown(EXIT_BAD); } - if (optind == argc && !istty) + if (optind == argc && !istty) { append_to_stream(tempfile, stdin); + } while (optind < argc) { char *arg = argv[optind++]; FILE *tp; /* Copy the the path of the first filename only */ - if (!Infile_Path) + if (!Infile_Path) { Infile_Path = ngdirname(arg); + } /* unquote the input string, needed if it results from double clicking the filename */ #if defined(HAS_WINGUI) @@ -1223,7 +1261,8 @@ main(int argc, char **argv) if (!tp) { char *lbuffer = getenv("NGSPICE_INPUT_DIR"); if (lbuffer && *lbuffer) { - char *p = tprintf("%s%s%s", lbuffer, DIR_PATHSEP, arg); + char *p = tprintf("%s" DIR_PATHSEP "%s", + lbuffer, arg); tp = fopen(p, "r"); tfree(p); } @@ -1257,13 +1296,15 @@ main(int argc, char **argv) gotone = TRUE; } - if (ft_batchmode && err) + if (ft_batchmode && err) { sp_shutdown(EXIT_BAD); + } } /* --- if (!ft_servermode) --- */ - if (!gotone && ft_batchmode) + if (!gotone && ft_batchmode) { inp_spsource(circuit_file, FALSE, NULL, FALSE); + } } @@ -1298,15 +1339,18 @@ main(int argc, char **argv) */ int error2 = ft_dorun(ft_rawfile); /* Execute the .whatever lines found in the deck, after we are done running. */ - if (ft_cktcoms(TRUE) || error2) + if (ft_cktcoms(TRUE) || error2) { sp_shutdown(EXIT_BAD); - } else if (ft_savedotargs()) { + } + } + else if (ft_savedotargs()) { /* all dot card data to be put into dbs */ int error2 = ft_dorun(NULL); /* Execute the .whatever lines found in the deck, after we are done running. */ if (ft_cktcoms(FALSE) || error2) sp_shutdown(EXIT_BAD); - } else { + } + else { fprintf(stderr, "Note: No \".plot\", \".print\", or \".fourier\" lines; " "no simulations run\n"); @@ -1324,30 +1368,37 @@ main(int argc, char **argv) ft_sigintr_cleanup(); fprintf(cp_err, "Warning: error executing during ft_loadfile().\n"); - } else { - + } + else { cp_interactive = FALSE; - while (optind < argc) + while (optind < argc) { ft_loadfile(argv[optind++]); + } } #endif /* ~ SIMULATOR */ - for (;;) + for (;;) { if (!SETJMP(jbuf, 1)) { /* enter the command processing loop */ cp_interactive = TRUE; #ifdef HAS_WINGUI int i; if (argv) { - for (i = 0; i < argc; i++) - tfree(argv[i]); + for (i = 0; i < argc; i++) { + txfree(argv[i]); + } tfree(argv); } #endif app_rl_readlines(); - } else { + } + else { ft_sigintr_cleanup(); } -} + } +} /* end of function main */ + + + From 22764310168b1d75611054ca2222f54355d6e271 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 17 Dec 2019 01:44:36 -0500 Subject: [PATCH 42/58] Modified search for init file so that it searches both and if both are defined. --- src/main.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/main.c b/src/main.c index f452ecb3a..92e627f81 100644 --- a/src/main.c +++ b/src/main.c @@ -1143,24 +1143,39 @@ int main(int argc, char **argv) /* load user's initialisation file try accessing the initialisation file in the current directory if that fails try the alternate name */ - if (FALSE == read_initialisation_file("", INITSTR) && - FALSE == read_initialisation_file("", ALT_INITSTR)) { - /* if that failed try in the user's home directory - if their HOME environment variable is set */ - char *homedir = getenv("HOME"); - if (homedir == (char *) NULL) { /* no HOME env variable */ - /* If there is no HOME environment (e.g. MS Windows), - * try user's profile directory */ - homedir = getenv("USERPROFILE"); + do { + if (read_initialisation_file("", INITSTR) != FALSE) { + break; + } + if (read_initialisation_file("", ALT_INITSTR) != FALSE) { + break; } - if (homedir) { /* Found a home location */ - if (FALSE == read_initialisation_file(homedir, INITSTR)) { - (void) read_initialisation_file(homedir, ALT_INITSTR); + { + const char * const home = getenv("HOME"); + if (home) { + if (read_initialisation_file(home, INITSTR) != FALSE) { + break; + } + if (read_initialisation_file(home, ALT_INITSTR) != FALSE) { + break; + } } } - } /* end of case init file not found in current directory */ - } /* end of case that init file is to be read */ + + { + const char * const usr = getenv("USERPROFILE"); + if (usr) { + if (read_initialisation_file(usr, INITSTR) != FALSE) { + break; + } + if (read_initialisation_file(usr, ALT_INITSTR) != FALSE) { + break; + } + } + } + } while (0); + } /* end of case that init file is read */ if (!ft_batchmode) { com_version(NULL); From 36057d080ed16e5a3616697667ae9460c83ebc09 Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Tue, 17 Dec 2019 15:41:00 -0500 Subject: [PATCH 43/58] Added #include to define FILE. --- src/include/ngspice/wordlist.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/include/ngspice/wordlist.h b/src/include/ngspice/wordlist.h index a591b58c5..713ef26a2 100644 --- a/src/include/ngspice/wordlist.h +++ b/src/include/ngspice/wordlist.h @@ -1,6 +1,7 @@ #ifndef ngspice_WORDLIST_H #define ngspice_WORDLIST_H +#include /* Doubly linked lists of words. */ struct wordlist { From 24cc13feaf16a94c0234423d840c591162c1b37b Mon Sep 17 00:00:00 2001 From: Jim Monte Date: Sat, 21 Dec 2019 15:39:24 -0500 Subject: [PATCH 44/58] Restored command arguments accidentally deleted. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 92e627f81..47326fc99 100644 --- a/src/main.c +++ b/src/main.c @@ -892,7 +892,7 @@ int main(int argc, char **argv) int option_index = 0; - int c = getopt_long(argc, argv, "D:bac:ino:pqr:st:", + int c = getopt_long(argc, argv, "D:hvbac:ino:pqr:st:", long_options, &option_index); if (c == -1) { From 13c5fd15b9e3c823d0877f84674b38699656d478 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 00:05:20 +0100 Subject: [PATCH 45/58] Improve error messages for the 'meas' command. --- src/frontend/com_measure2.c | 6 +++--- src/frontend/measure.c | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index 5346cf7c9..936f5420a 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -1169,7 +1169,7 @@ measure_parse_stdParams( wl = wl->wl_next; continue; } else { - sprintf(errbuf, "bad syntax of ??\n"); + sprintf(errbuf, "bad syntax. equal sign missing ?\n"); return 0; } } @@ -1179,7 +1179,7 @@ measure_parse_stdParams( } else { if (ft_numparse(&pValue, FALSE, &engVal1) < 0) { - sprintf(errbuf, "bad syntax of ??\n"); + sprintf(errbuf, "bad syntax, cannot evaluate right hand side of %s=%s\n", pName, pValue); return 0; } } @@ -1216,7 +1216,7 @@ measure_parse_stdParams( } if (pCnt == 0) { - sprintf(errbuf, "bad syntax of ??\n"); + sprintf(errbuf, "bad syntax of %s\n", pName); return 0; } diff --git a/src/frontend/measure.c b/src/frontend/measure.c index 603b2d0af..068f8b7b6 100644 --- a/src/frontend/measure.c +++ b/src/frontend/measure.c @@ -67,6 +67,12 @@ com_meas(wordlist *wl) May be in the next wl_word */ if (token[strlen(token) - 1] == '=') { wl_index = wl_index->wl_next; + if (wl_index == NULL) { + line_in = wl_flatten(wl); + fprintf(stderr, "\nError: meas %s failed!\n\n", line_in); + tfree(line_in); + return; + } vec_found = wl_index->wl_word; /* token may be already a value, maybe 'LAST', which we have to keep, or maybe a vector */ if (!cieq(vec_found, "LAST")) { From 3f5f3afabf03b0833bd93cb1b5edfaa4fb986a82 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 09:56:00 +0100 Subject: [PATCH 46/58] Further improvement of error message im 'meas' --- src/frontend/measure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/measure.c b/src/frontend/measure.c index 068f8b7b6..05727a6cb 100644 --- a/src/frontend/measure.c +++ b/src/frontend/measure.c @@ -69,7 +69,7 @@ com_meas(wordlist *wl) wl_index = wl_index->wl_next; if (wl_index == NULL) { line_in = wl_flatten(wl); - fprintf(stderr, "\nError: meas %s failed!\n\n", line_in); + fprintf(stderr, "\nError: meas failed due to missing token in \n meas %s \n\n", line_in); tfree(line_in); return; } From 505eb59b767905759eafe69d306712250e13cd91 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 10:30:23 +0100 Subject: [PATCH 47/58] O1 -> O0 for debug Patch #71 provided by Taras Rosa --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d73fe1b39..c969c1603 100644 --- a/configure.ac +++ b/configure.ac @@ -220,7 +220,7 @@ if test "x$ext_CFLAGS" != xyes; then else AC_DEFINE([NGDEBUG], [1], [Compile with debug info]) if test "x$GCC" = xyes; then - CFLAGS="-g -O1" + CFLAGS="-g -O0" else CFLAGS="-g" fi From bb26062b14befe3f36f4f00cb8d7c9c061665742 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 10:50:56 +0100 Subject: [PATCH 48/58] patch provided by Vito (see https://sourceforge.net/p/ngspice/discussion/133842/thread/31aa24261e/ ) --- src/xspice/evt/evtload.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c index 903e5f2b4..1dc0368a7 100644 --- a/src/xspice/evt/evtload.c +++ b/src/xspice/evt/evtload.c @@ -340,6 +340,7 @@ static void EVTcreate_state( { new_state = state_data->free[inst_index]; state_data->free[inst_index] = new_state->next; + new_state->next = NULL; // reusing dirty memory: next must be reset } else { From e1be1ace77c6fbaed04c9cdca70a8d09cf5f95d9 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 14:32:34 +0100 Subject: [PATCH 49/58] error message even if pNAME is not defined --- src/frontend/com_measure2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/frontend/com_measure2.c b/src/frontend/com_measure2.c index 936f5420a..e4c3d4418 100644 --- a/src/frontend/com_measure2.c +++ b/src/frontend/com_measure2.c @@ -1151,7 +1151,7 @@ measure_parse_stdParams( ) { int pCnt; - char *p, *pName, *pValue; + char *p, *pName = NULL, *pValue; double engVal1; pCnt = 0; @@ -1216,7 +1216,10 @@ measure_parse_stdParams( } if (pCnt == 0) { - sprintf(errbuf, "bad syntax of %s\n", pName); + if (pName) + sprintf(errbuf, "bad syntax of %s\n", pName); + else + sprintf(errbuf, "bad syntax of\n"); return 0; } From 00606fecc2c25e284609bd753f81c059dc46ff46 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 14:34:35 +0100 Subject: [PATCH 50/58] deprecated gets() -> fgets() --- src/xspice/ipc/ipcstdio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/xspice/ipc/ipcstdio.c b/src/xspice/ipc/ipcstdio.c index 15ef467b1..229fff3ba 100644 --- a/src/xspice/ipc/ipcstdio.c +++ b/src/xspice/ipc/ipcstdio.c @@ -45,7 +45,10 @@ Ipc_Status_t ipc_transport_get_line ( NG_IGNORE(wait); printf ("GET_LINE\n"); - gets (str); + fgets (str, 512, stdin); + char *tmp = strchr(str, '\n'); + if (tmp) + *tmp = '\0'; *len = (int) strlen (str); return IPC_STATUS_OK; } From 5dfdf88ccc93b91346a6e041513b894409eb5f4a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 14:34:57 +0100 Subject: [PATCH 51/58] update to Visual Studio 2019 --- visualc/sharedspice.vcxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/visualc/sharedspice.vcxproj b/visualc/sharedspice.vcxproj index cf35a0786..a4dc57acb 100644 --- a/visualc/sharedspice.vcxproj +++ b/visualc/sharedspice.vcxproj @@ -33,27 +33,27 @@ DynamicLibrary - v140 + v142 DynamicLibrary - v140 + v142 DynamicLibrary - v140 + v142 DynamicLibrary - v140 + v142 DynamicLibrary - v140 + v142 DynamicLibrary - v140 + v142 From 047dbc476501f1edf0862aec8011e5a493d15e1f Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 14:50:43 +0100 Subject: [PATCH 52/58] use only TMALLOC and TREALLOC for memory allocation --- src/frontend/com_sysinfo.c | 2 +- src/include/ngspice/dstring.h | 6 ++++-- src/misc/dstring.c | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/frontend/com_sysinfo.c b/src/frontend/com_sysinfo.c index 8d1f817c0..03f7e11d2 100644 --- a/src/frontend/com_sysinfo.c +++ b/src/frontend/com_sysinfo.c @@ -924,7 +924,7 @@ static void get_physical_processor_count(void) /* Allocate buffer to get the info */ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * const buf = - (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) malloc(n_byte_buf); + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)TMALLOC(char, n_byte_buf); if (buf == (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) NULL) { fprintf(cp_err, "Unable to allocate a buffer of %lu bytes " diff --git a/src/include/ngspice/dstring.h b/src/include/ngspice/dstring.h index 25ad51773..65b88a9cb 100644 --- a/src/include/ngspice/dstring.h +++ b/src/include/ngspice/dstring.h @@ -8,6 +8,8 @@ #include #include #include +#include "ngspice/memory.h" + /* Error codes */ #define DS_E_OK 0 @@ -192,7 +194,7 @@ inline char *ds_free_move(DSTRING *p_ds, unsigned int opt) 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); + char * const p_ret = TMALLOC(char, n_byte_alloc); if (p_ret == (char *) NULL) { return (char *) NULL; } @@ -204,7 +206,7 @@ inline char *ds_free_move(DSTRING *p_ds, unsigned int opt) 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); + char * const p_ret = TREALLOC(char, p_buf_active, n_byte_alloc); if (p_ret == (char *) NULL) { /* Realloc to smaller size somehow failed! */ return (char *) NULL; diff --git a/src/misc/dstring.c b/src/misc/dstring.c index 3706134b4..d5cb4cac5 100644 --- a/src/misc/dstring.c +++ b/src/misc/dstring.c @@ -4,7 +4,6 @@ DESCRIPTION:This file contains the routines for manipulating dynamic strings. ----------------------------------------------------------------- */ #include #include -#include #include #include #include @@ -347,7 +346,7 @@ int ds_compact(DSTRING *p_ds) /* Else realloc the heap buffer */ { - void *p = realloc(p_ds->p_buf, n_byte_alloc_min); + void *p = TREALLOC(char, p_ds->p_buf, n_byte_alloc_min); if (p == NULL) { return DS_E_NO_MEMORY; } From 4fcf5364d75a452a2c68a13909ae1ce34fd04cef Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 15:28:25 +0100 Subject: [PATCH 53/58] User expects an empty input line, not a text that has to be deleted befopre writing --- src/winmain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/winmain.c b/src/winmain.c index 1de964375..d22bde1a3 100644 --- a/src/winmain.c +++ b/src/winmain.c @@ -1080,8 +1080,8 @@ static struct History_info *init_history(void) } { - /* Initialize history buffer with a greeting */ - static const char cmd_welcome[] = "# Welcome to ngspice!"; + /* Initialize history buffer with empty input line */ + static const char cmd_welcome[] = ""; (void) history_add(&p_hi, sizeof cmd_welcome - 1, cmd_welcome); } From 42a6226fdf0e9226e252f9f0a5a6508f704156f3 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 15:28:58 +0100 Subject: [PATCH 54/58] Give a better, modern name for this type of display --- src/frontend/display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/display.c b/src/frontend/display.c index faf14f1ec..0faa71b27 100644 --- a/src/frontend/display.c +++ b/src/frontend/display.c @@ -119,7 +119,7 @@ DISPDEVICE device[] = { (disp_fn_Track_t *) nodev, (disp_fn_MakeMenu_t *) nodev, (disp_fn_MakeDialog_t *) nodev, (disp_fn_Input_t *) nodev, gen_DatatoScreen, }, - { "printf", 0, 0, 24, 80, 0, 0, + { "PrinterOnly", 0, 0, 24, 80, 0, 0, (disp_fn_Init_t *) nodev, (disp_fn_NewViewport_t *) nodev, (disp_fn_Close_t *) nop, (disp_fn_Clear_t *) nodev, (disp_fn_DrawLine_t *) nodev, (disp_fn_Arc_t *) nodev, (disp_fn_Text_t *) nodev, From 82aad9d5b7d8d5849ab6d72bae43ff003418c37f Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sun, 5 Jan 2020 15:29:36 +0100 Subject: [PATCH 55/58] make simulation faster, allow batch mode --- examples/xspice/pll/pll-xspice-fstep.cir | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/xspice/pll/pll-xspice-fstep.cir b/examples/xspice/pll/pll-xspice-fstep.cir index 4f86fe9d9..d66a96061 100644 --- a/examples/xspice/pll/pll-xspice-fstep.cir +++ b/examples/xspice/pll/pll-xspice-fstep.cir @@ -17,7 +17,7 @@ vdd dd 0 dc 'vcc' * 10 MHz reference frequency * PULSE(V1 V2 TD TR TF PW PER) -vref ref 0 dc 0 pulse(0 'vcc' 10n 1n 1n '1/fref/2' '1/fref') +vref ref 0 dc 0 pulse(0 'vcc' 10n 2n 2n '1/fref/2' '1/fref') abridgeref [ref] [d_ref] adc_vbuf .model adc_vbuf adc_bridge(in_low = 0.5 in_high = 0.5) @@ -66,7 +66,10 @@ abridge-w1 [d_divout d_ref d_Un d_D] [s1 s2 u1 d1] dac1 ; change to d_u or d_Un .control save cont s1 s2 u1 d1 -iplot cont +let isbmode = $?batchmode +if isbmode = 0 + iplot cont +endif * calculate breakpoint for switching frequency let t1_3 = simtime/3 set ti1_3 ="$&t1_3" @@ -80,7 +83,7 @@ let pw2 = per2/2 let per3=1/f3 let pw3 = per3/2 *simulate -tran 0.1n $&simtime 0 0.5n uic +tran 10n $&simtime 0 1n uic *change frequency after stopping * first pair of [] without spaces, second pair with spaces alter @vref[pulse] = [ 0 3.3 10n 1n 1n $&pw2 $&per2 ] @@ -89,8 +92,11 @@ resume alter @vref[pulse] = [ 0 3.3 10n 1n 1n $&pw3 $&per3 ] resume rusage -plot cont s1 s2+1.2 u1+2.4 d1+3.6 xlimit 15u 16u -*plot cont +if isbmode = 0 + plot cont s1 s2+1.2 u1+2.4 d1+3.6 xlimit 15u 16u +else + write pll.raw all +endif .endc *model = bsim3v3 From b343bc88035e7bc3dcbc0deb92b518c624e4bf0d Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 11 Jan 2020 11:25:14 +0100 Subject: [PATCH 56/58] plug memory leaks for the 'plot' command --- src/frontend/misccoms.c | 5 +++++ src/frontend/plotting/plotit.c | 34 +++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/frontend/misccoms.c b/src/frontend/misccoms.c index 6d306f964..40f35ede9 100644 --- a/src/frontend/misccoms.c +++ b/src/frontend/misccoms.c @@ -38,6 +38,7 @@ extern void rem_controls(void); extern IFsimulator SIMinfo; extern void spice_destroy_devices(void); /* FIXME need a better place */ +extern void pl_rempar(void); /* plotit.c */ static void byemesg(void); static int confirm_quit(void); @@ -56,6 +57,7 @@ com_quit(wordlist *wl) gr_clean(); cp_ccon(FALSE); + /* Make sure the guy really wants to quit. */ if (!ft_nutmeg) if (!noask && !confirm_quit()) @@ -90,6 +92,9 @@ com_quit(wordlist *wl) cp_destroy_keywords(); destroy_ivars(); #else + /* remove plotting parameters */ + pl_rempar(); + while (ft_curckt) com_remcirc(NULL); #endif diff --git a/src/frontend/plotting/plotit.c b/src/frontend/plotting/plotit.c index ba3c1039d..95fb45207 100644 --- a/src/frontend/plotting/plotit.c +++ b/src/frontend/plotting/plotit.c @@ -18,11 +18,28 @@ #include "graf.h" static bool sameflag; - +/* All these things are static so that "samep" will work. + They are outside of plotit() to allow deleting */ +static double *xcompress = NULL, *xindices = NULL; +static double *xlim = NULL, *ylim = NULL; +static double *xdelta = NULL, *ydelta = NULL; +static char *xlabel = NULL, *ylabel = NULL, *title = NULL; #ifdef TCL_MODULE #include "ngspice/tclspice.h" #endif +/* remove the malloced parameters upon ngspice quit */ +void pl_rempar(void) +{ + txfree(xcompress); + txfree(xindices); + txfree(xlim); + txfree(ylim); + txfree(xdelta); + txfree(ydelta); + txfree(xlabel); + txfree(ylabel); +} static struct dvec *vec_self(struct dvec *v); static struct dvec *vec_scale(struct dvec *v); @@ -258,11 +275,6 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) return FALSE; } - /* All these things are static so that "samep" will work. */ - static double *xcompress = NULL, *xindices = NULL; - static double *xlim = NULL, *ylim = NULL; - static double *xdelta = NULL, *ydelta = NULL; - static char *xlabel = NULL, *ylabel = NULL, *title = NULL; static bool nointerp = FALSE; static GRIDTYPE gtype = GRID_LIN; static PLOTTYPE ptype = PLOT_LIN; @@ -317,8 +329,10 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) /* Build the plot command. This construction had been done with wordlists * and reversing, and flattening, but it is clearer as well as much more * efficient to use a dstring. */ - rc_ds |= ds_cat_printf(&ds_cline, "plot %s", wl_flatten(wwl->wl_next)); + char *flatstr = wl_flatten(wwl->wl_next); + rc_ds |= ds_cat_printf(&ds_cline, "plot %s", flatstr); wl_free(wwl); + tfree(flatstr); /* Add title, xlabel or ylabel, if available, with quotes ''. */ if (nxlabel) { @@ -343,6 +357,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) sameflag = getflag(wl, "samep"); if (!sameflag || !xlim) { + txfree(xlim); xlim = getlims(wl, "xl", 2); if (!xlim) { xlim = getlims(wl, "xlimit", 2); @@ -354,6 +369,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) } if (!sameflag || !ylim) { + txfree(ylim); ylim = getlims(wl, "yl", 2); if (!ylim) { ylim = getlims(wl, "ylimit", 2); @@ -365,6 +381,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) } if (!sameflag || !xcompress) { + txfree(xcompress); xcompress = getlims(wl, "xcompress", 1); if (!xcompress) { xcompress = getlims(wl, "xcomp", 1); @@ -376,6 +393,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) } if (!sameflag || !xindices) { + txfree(xindices); xindices = getlims(wl, "xindices", 2); if (!xindices) { xindices = getlims(wl, "xind", 2); @@ -387,6 +405,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) } if (!sameflag || !xdelta) { + txfree(xdelta); xdelta = getlims(wl, "xdelta", 1); if (!xdelta) { xdelta = getlims(wl, "xdel", 1); @@ -397,6 +416,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) } if (!sameflag || !ydelta) { + txfree(ydelta); ydelta = getlims(wl, "ydelta", 1); if (!ydelta) { ydelta = getlims(wl, "ydel", 1); From 434538896988111350af77e3ddef61fd22421ec0 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 11 Jan 2020 13:07:53 +0100 Subject: [PATCH 57/58] enable PSPICE compatible ~(~v(9)&v(8)) --- src/frontend/inpcom.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 74ab6bceb..faa2373cd 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -7518,7 +7518,7 @@ static struct card *pspice_compat(struct card *oldcard) } } - /* replace & with && and | with || and *# with * # */ + /* replace & with && , | with || , *# with * # , and ~ with ! */ for (card = newcard; card; card = card->nextcard) { char *t; char *cut_line = card->line; @@ -7581,6 +7581,18 @@ static struct card *pspice_compat(struct card *oldcard) t = strstr(tt, "|"); } } + /* We may have '~' in path names or A devices */ + char *firsttok = nexttok(card->line); /* skip over whitespaces */ + if (ciprefix(".inc", firsttok) || ciprefix(".lib", firsttok) || + ciprefix("A", firsttok)) + continue; + + if ((t = strstr(card->line, "~")) != NULL) { + while (t) { + *t = '!'; + t = strstr(t, "~"); + } + } } /* replace T_ABS by temp, T_REL_GLOBAL by dtemp, and T_MEASURED by TNOM From 12bc3a55b6dd61255f09a55aa55ed7ae5cba3a1c Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 11 Jan 2020 13:08:39 +0100 Subject: [PATCH 58/58] example for ~(~v(9)&v(8)) --- examples/p-to-n-examples/logic.cir | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 examples/p-to-n-examples/logic.cir diff --git a/examples/p-to-n-examples/logic.cir b/examples/p-to-n-examples/logic.cir new file mode 100644 index 000000000..7a68b2bdf --- /dev/null +++ b/examples/p-to-n-examples/logic.cir @@ -0,0 +1,15 @@ +logic test, PSPICE +* requires 'set ngbehavior=psa' + +v8 8 0 1 Pulse (0 1 0.45 1m 1m 5 10) +v9 9 0 0 + +B1 1 0 V=~(~v(9)&v(8)) + 0.5 + +.control +tran 1m 1 +plot v(1) v(8) v(9) +listing +.endc + +.end