From 704797f10a6aec5a8f73fdb5cdaec63be685a4fd Mon Sep 17 00:00:00 2001 From: h_vogt Date: Sat, 23 Mar 2013 17:34:15 +0100 Subject: [PATCH] main file and header for ngspice as shared module --- src/include/ngspice/sharedspice.h | 262 +++++ src/sharedspice.c | 1507 +++++++++++++++++++++++++++++ 2 files changed, 1769 insertions(+) create mode 100644 src/include/ngspice/sharedspice.h create mode 100644 src/sharedspice.c diff --git a/src/include/ngspice/sharedspice.h b/src/include/ngspice/sharedspice.h new file mode 100644 index 000000000..554e488dd --- /dev/null +++ b/src/include/ngspice/sharedspice.h @@ -0,0 +1,262 @@ +/* header file for shared ngspice */ +/* Copyright 2013 Holger Vogt */ +/* Modified BSD license */ + +/* +Interface between a calling program (caller) and ngspice.dll (ngspice.so) + +** +ngSpice_Init(SendChar*, SendStat*, ControlledExit*, void*) +After caller has loaded ngspice.dll, the simulator has to be initialized +by calling ngSpice_Init(). Address pointers of several callback functions +defined in the caller are sent to ngspice.dll. + +Callback funtion typedefs +SendChar typedef of callback function for reading printf, fprintf, fputs +SendStat typedef of callback function for reading status string and precent value +ControlledExit typedef of callback function for tranferring a signal upon + ngspice controlled_exit to caller. May be used by caller + to detach ngspice.dll. +SendData typedef of callback function for sending an array of structs containing + data values of all vectors in the current plot (simulation output) + +The void pointer may contain the object address of the calling +function ('self' or 'this' pointer), so that the answer may be directed +to a calling object. Callback functions are defined in the global section. + +** +ngSpice_Command(char*) +Send a valid command (see the control or interactive commands) from caller +to ngspice.dll. Will be executed immediately (as if in interactive mode). +Some commands are rejected (e.g. 'plot', because there is no graphics interface). +Command 'quit' will remove internal data, and then send a notice to caller via +ngexit(). + +** +ngGet_Vec_Info(char*) +receives the name of a vector (may be in the form 'vectorname' or +.vectorname) and returns a pointer to a vector_info struct. +The caller may then directly assess the vector data (but probably should +not modify them). + +** +ngSpice_Circ(char**) +sends an array of null-terminated char* to ngspice.dll. Each char* contains a +single line of a circuit (each line like in an input file **.sp). The last +entry to char** has to be NULL. Upon receiving the arry, ngspice.dll will +immediately parse the input and set up the circuit structure (as if received +the circuit from a file by the 'source' command. + +** +char* ngSpice_CurPlot(); +returns to the caller a pointer to the name of the current plot + +** +char** ngSpice_AllPlots() +returns to the caller a pointer to an array of all plots (by their typename) + +** +char** ngSpice_AllVecs(char*); +returns to the caller a pointer to an array of vector names in the plot +named by the string in the argument. + +** +Additional basics: +No memory mallocing and freeing across the interface: +Memory allocated in ngspice.dll has to be freed in ngspice.dll. +Memory allocated in the calling program has to be freed only there. + +ngspice.dll should never call exit() directly, but handle either the 'quit' +request to the caller or an request for exiting upon error, +done by callback function ngexit(). +*/ + +#ifndef NGSPICE_DLL_H +#define NGSPICE_DLL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__MINGW32__) || defined(_MSC_VER) || defined(__CYGWIN__) + #ifdef SHARED_MODULE + #define IMPEXP __declspec(dllexport) + #else + #define IMPEXP __declspec(dllimport) + #endif +#else + /* use with gcc flag -fvisibility=hidden */ + #if __GNUC__ >= 4 + #define IMPEXP __attribute__ ((visibility ("default"))) + #define IMPEXPLOCAL __attribute__ ((visibility ("hidden"))) + #else + #define IMPEXP + #define IMPEXP_LOCAL + #endif +#endif + +/* required only if header is used by the caller, + is already defined in ngspice.dll */ +#ifndef ngspice_NGSPICE_H +/* Complex numbers. */ +struct ngcomplex { + double cx_real; + double cx_imag; +} ; + +typedef struct ngcomplex ngcomplex_t; +#endif + +/* vector info obtained from any vector in ngspice.dll */ +typedef struct vector_info { + char *v_name; /* Same as so_vname. */ + int 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. */ + int v_length; /* Length of the vector. */ +} vector_info, *pvector_info; + +typedef struct vecvalues { + char* name; + double creal; + double cimag; + bool is_scale; + bool is_complex; +} vecvalues, *pvecvalues; + +typedef struct vecvaluesall { + int veccount; + pvecvalues *vecsa; +} vecvaluesall, *pvecvaluesall; + +/* info for a specific vector */ +typedef struct vecinfo +{ + int number; /* number of vector, as postion in the linked list of vectors, starts with 0 */ + char *vecname; /* name of the actual vector */ + bool is_real; /* TRUE if the actual vector has real data */ + void *pdvec; /* a void pointer to struct dvec *d, the actual vector */ + void *pdvecscale; /* a void pointer to struct dvec *ds, the scale vector */ +} vecinfo, *pvecinfo; + +/* info for the current plot */ +typedef struct vecinfoall +{ + /* the plot */ + char *name; + char *title; + char *date; + char *type; + int veccount; + + /* the data as an array of vecinfo with length equal to the number of vectors in the plot */ + pvecinfo *vecs; + +} vecinfoall, *pvecinfoall; + + +/* callback functions +addresses received from caller with ngSpice_Init() function +*/ +/* sending output from stdout, stderr to caller */ +typedef int (SendChar)(char*, void*); +/* + char* string to be sent to caller output + void* return pointer received from caller, e.g. pointer to object having sent the request +*/ +/* sending simulation status to caller */ +typedef int (SendStat)(char*, void*); +/* + char* simulation status and value (in percent) to be sent to caller + void* return pointer received from caller +*/ +/* asking for controlled exit */ +typedef int (ControlledExit)(int, bool, bool, void*); +/* + int exit status + bool if true: immediate unloading dll, if false: just set flag, unload is done when function has returned + bool if true: exit upon 'quit', if false: exit due to ngspice.dll error + void* return pointer received from caller +*/ +/* send back actual vector data */ +typedef int (SendData)(pvecvaluesall, int, void*); +/* + vecvaluesall* pointer to array of structs containing actual values from all vectors + int number of structs (one per vector) + void* return pointer received from caller +*/ + +/* send back initailization vector data */ +typedef int (SendInitData)(pvecinfoall, void*); +/* + vecinfoall* pointer to array of structs containing data from all vectors right afetr initialization + void* return pointer received from caller +*/ +/* indicate if worker thread is running */ +typedef int (BGThreadRunning)(bool, void*); +/* + vecinfoall* pointer to array of structs containing data from all vectors right afetr initialization + void* return pointer received from caller +*/ + + +/* ngspice initialization, +printfcn: pointer to callback function for reading printf, fprintf +statfcn: pointer to callback function for the status string and percent value +ControlledExit: pointer to callback function for setting a 'quit' signal in caller +SendData: pointer to callback function for returning data values of all current output vectors +SendInitData: pointer to callback function for returning information of all output vectors just initialized +BGThreadRunning: pointer to callback function indicating if workrt thread is running +userData: pointer to user-defined data, will not be modified, but + handed over back to caller during Callback, e.g. address of calling object */ +IMPEXP +int ngSpice_Init(SendChar* printfcn, SendStat* statfcn, ControlledExit* ngexit, + SendData* sdata, SendInitData* sinitdata, BGThreadRunning* bgtrun, void* userData); + + +/* Caller may send ngspice commands to ngspice.dll. +Commands are executed immediately */ +IMPEXP +int ngSpice_Command(char* command); + + +/* get info about a vector */ +IMPEXP +pvector_info ngGet_Vec_Info(char* vecname); + + +/* send a circuit to ngspice.dll + The circuit description is a dynamic array + of char*. Each char* corresponds to a single circuit + line. The last entry of the array has to be a NULL */ +IMPEXP +int ngSpice_Circ(char** circarray); + + +/* return to the caller a pointer to the name of the current plot */ +IMPEXP +char* ngSpice_CurPlot(void); + + +/* return to the caller a pointer to an array of all plots created +so far by ngspice.dll */ +IMPEXP +char** ngSpice_AllPlots(void); + + +/* return to the caller a pointer to an array of vector names in the plot +named by plotname */ +IMPEXP +char** ngSpice_AllVecs(char* plotname); + +/* returns TRUE if ngspice is running in a second thread */ +IMPEXP +bool ngSpice_running(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sharedspice.c b/src/sharedspice.c new file mode 100644 index 000000000..11c417a1c --- /dev/null +++ b/src/sharedspice.c @@ -0,0 +1,1507 @@ +/* Copyright 2013 Holger Vogt + * + * Modified BSD license + */ + +/* For comments and explanations see sharedspice.h */ + +/*******************/ +/* Defines */ +/*******************/ +#define CS + +#ifdef _MSC_VER +#define SHAREDSPICE_version "25.1" +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +/**********************************************************************/ +/* Header files for C functions */ +/**********************************************************************/ + +#include +#include + +/* workaround since fputs, putc are replaced by sh_fputs, +sh_putc, through redefinition in ngspice.h */ + +int myputs(const char* inp, FILE* f); +int myputc(const char inp, FILE* f); +int myfputc(const char inp, FILE* f); + +int +myputs(const char* inp, FILE* f) +{ + return fputs(inp, f); +} + +int +myputc(const char inp, FILE* f) +{ + return putc(inp, f); +} + +int +myfputc(const char inp, FILE* f) +{ + return fputc(inp, f); +} + +#if defined(__MINGW32__) || defined(_MSC_VER) +#include +#endif + +#include "ngspice/ngspice.h" +#include "misc/misc_time.h" + +/*Use Windows threads if on W32 without pthreads*/ +#ifndef HAVE_LIBPTHREAD + +#if defined(__MINGW32__) || defined(_MSC_VER) +//#if defined(_MSC_VER) +#ifdef CS +#define mutex_lock(a) EnterCriticalSection(a) +#define mutex_unlock(a) LeaveCriticalSection(a) +typedef CRITICAL_SECTION mutexType; +#else +#define mutex_lock(a) AcquireSRWLockExclusive(a) +#define mutex_unlock(a) ReleaseSRWLockExclusive(a) +typedef SRWLOCK mutexType; +#endif +#define thread_self() GetCurrentThread() +#define threadid_self() GetThreadId(GetCurrentThread()) +typedef HANDLE threadId_t; +#define WIN_THREADS +#define THREADS + +#endif + +#else + +#include +#define mutex_lock(a) pthread_mutex_lock(a) +#define mutex_unlock(a) pthread_mutex_unlock(a) +#define thread_self() pthread_self() +#define threadid_self() 0 //FIXME t.b.d. +typedef pthread_mutex_t mutexType; +typedef pthread_t threadId_t; +#define THREADS + +#endif + + +/* Copied from main.c in ngspice*/ +#if defined(__MINGW32__) +#include +/* remove type incompatibility with winnt.h*/ +#undef BOOLEAN +#include +#include /* Sleep */ +#elif defined(_MSC_VER) +#include +/* remove type incompatibility with winnt.h*/ +#undef BOOLEAN +#include /* Sleep */ +#include /* _getpid */ +#define dup _dup +#define dup2 _dup2 +#define open _open +#define close _close +#else +#include /* usleep */ +#endif /* __MINGW32__ */ + +#include "ngspice/iferrmsg.h" +#include "ngspice/ftedefs.h" +#include "ngspice/devdefs.h" +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#define snprintf _snprintf +#endif +#include +#include "ngspice/memory.h" +#include +#include + +#ifndef HAVE_GETRUSAGE +#ifdef HAVE_FTIME +#include +#endif +#endif + +/* To interupt a spice run */ +#include +typedef void (*sighandler)(int); + +#include +#include "frontend/signal_handler.h" + +/*Included for the module to access data*/ +#include "ngspice/dvec.h" +#include "ngspice/plot.h" + +#ifdef __CYGWIN__ +#undef WIN32 +#endif +#include "ngspice/sim.h" + +/*For get_output*/ +#include +#include + +#ifdef _MSC_VER +#define S_IRWXU _S_IWRITE +#endif + +#ifdef HAVE_ASPRINTF +#ifdef HAVE_LIBIBERTY_H /* asprintf */ +#include +#elif defined(__MINGW32__) || defined(__SUNPRO_C) /* we have asprintf, but not libiberty.h */ +#include +extern int asprintf(char **out, const char *fmt, ...); +extern int vasprintf(char **out, const char *fmt, va_list ap); +#endif +#endif + +extern IFfrontEnd nutmeginfo; + +extern struct comm spcp_coms[ ]; +extern void DevInit(void); +extern int SIMinit(IFfrontEnd *frontEnd, IFsimulator **simulator); +extern wordlist *cp_varwl(struct variable *var); +extern void create_circbyline(char *line); +extern void initw(void); + + +/*The current run (to get variable names, etc)*/ +static runDesc *cur_run; + +//static int ownVectors; + +void sh_stdflush(FILE *f); +int sh_vfprintf(FILE *f, const char *fmt, va_list args); + +//int sh_fputs(const char *input, FILE* outf); +//int sh_fputc(const char input, FILE* outf); +//int sh_putc(const char input, FILE* outf); +int sh_fputsll(const char *input, FILE* outf); + +int sh_ExecutePerLoop(void); +int sh_vecinit(runDesc *run); + +void shared_exit(int status); + +void sighandler_sharedspice(int num); + +#if !defined(low_latency) +static char* outstorage(char*, bool); +static void* printsend(void); +#endif + +#include "ngspice/sharedspice.h" + +static SendChar* pfcn; +static void* userptr; +static SendStat* statfcn; +static ControlledExit* ngexit; +static SendData* datfcn; +static SendInitData* datinitfcn; +static BGThreadRunning* bgtr; +static pvector_info myvec = NULL; +char **allvecs = NULL; +char **allplots = NULL; +static bool noprintfwanted = FALSE; +static bool nostatuswanted = FALSE; +static bool nodatawanted = FALSE; +static bool nodatainitwanted = FALSE; +static bool nobgtrwanted = FALSE; +static bool immediate = FALSE; +static bool coquit = FALSE; +static jmp_buf errbufm, errbufc; +static int intermj = 1; + + +// thread IDs +unsigned int main_id, ng_id, command_id; + +#ifdef THREADS +mutexType triggerMutex; +mutexType allocMutex; +mutexType fputsMutex; +#endif + +/* initialization status */ +static bool is_initialized = FALSE; +static char* no_init = "Error: ngspice is not initialized!\n Run ngSpice_Init first"; + +/*helper function*//* +static struct plot * +get_plot(int plot) +{ + struct plot *pl; + pl = plot_list; + for (; 0 < plot; plot--) { + pl = pl->pl_next; + if (!pl) + return NULL; + } + return pl; +} +*/ +static struct plot * +get_plot_byname(char* plotname) +{ + struct plot *pl; + pl = plot_list; + while (pl) { + if(cieq(pl->pl_typename, plotname)) + break; + pl = pl->pl_next; + } + return pl; +} + + +/*this holds the number of time points done (altered by spice)*/ +//int steps_completed; + + + +/******************************************************************/ +/* Main spice command executions and thread control */ +/*****************************************************************/ + +#ifdef THREADS +#ifdef __MINGW32__ +static threadId_t tid, printtid, bgtid; +#else +static threadId_t tid, printtid, bgtid = (threadId_t) 0; +#endif + +static bool fl_running = FALSE; +static bool fl_exited = TRUE; + +#if defined(__MINGW32__) || defined(_MSC_VER) +#define EXPORT_FLAVOR WINAPI +#else +#define EXPORT_FLAVOR +#endif + +static void * EXPORT_FLAVOR +_thread_run(void *string) +{ + ng_id = threadid_self(); + fl_exited = FALSE; + /* notify caller that thread is running */ + if (!nobgtrwanted) + bgtr(fl_exited, userptr); + bgtid = thread_self(); + cp_evloop((char *)string); + FREE(string); +#ifdef __MINGW32__nn + bgtid.p = NULL; + bgtid.x = 0; +#else + bgtid = (threadId_t)0; +#endif + fl_exited = TRUE; + /* notify caller that thread has exited */ + if (!nobgtrwanted) + bgtr(fl_exited, userptr); + return NULL; +} + + +/*Stops a running thread, hopefully */ +static int EXPORT_FLAVOR +_thread_stop(void) +{ + int timeout = 0; + + if (fl_running) { + while (!fl_exited && timeout < 100) { + /* ft_intrpt is the flag to stop simulation, if set TRUE ! + E.g. SPfrontEnd->IFpauseTest() in dctran.c points to + OUTstopnow(void), which returns 1, which leads dctran + to return with -1 and thus terminates the simulation*/ + ft_intrpt = TRUE; + timeout++; +#if defined(__MINGW32__) || defined(_MSC_VER) + Sleep(100); // va: windows native +#else + usleep(10000); +#endif + } + if (!fl_exited) { + fprintf(stderr, "Error: Couldn't stop ngspice\n"); + return EXIT_BAD; + } + else + fprintf(stdout, "Background thread stopped with timeout = %d\n", timeout); +#ifdef HAVE_LIBPTHREAD + pthread_join(tid, NULL); +#endif + fl_running = FALSE; + ft_intrpt = FALSE; + return EXIT_NORMAL; + } else { + fprintf(stderr, "Spice not running\n"); + } + return EXIT_NORMAL; +} + + +void +sighandler_sharedspice(int num) +{ + NG_IGNORE(num); + if (fl_running) + _thread_stop(); + return; +} + +#endif /*THREADS*/ + + +static int +runc(char* command) +{ + char buf[1024] = ""; + sighandler oldHandler; +#ifdef THREADS + char *string; + bool fl_bg = FALSE; + command_id = threadid_self(); + /* run task in background if command is preceeded by "bg_" */ + if (!cieq("bg_halt", command) && (ciprefix("bg_", command))) { + strncpy(buf, command+3, 1024); + fl_bg = TRUE; + } + else + strncpy(buf, command, 1024); +#else + strncpy(buf, command, 1024); +#endif + + /* Catch Ctrl-C to break simulations */ +#if !defined(_MSC_VER) /*&& !defined(__MINGW32__) */ + oldHandler = signal(SIGINT, (SIGNAL_FUNCTION) ft_sigintr); + if (SETJMP(jbuf, 1) != 0) { + signal(SIGINT, oldHandler); + return 0; + } +#else + oldHandler = SIG_IGN; +#endif + + + +#ifdef THREADS + /* run in the background */ + if (fl_bg && fl_exited) { + if (fl_running) + _thread_stop(); + fl_running = TRUE; + string = copy(buf); /*as buf gets freed fairly quickly*/ +#ifdef HAVE_LIBPTHREAD + pthread_create(&tid, NULL, (void * (*)(void *))_thread_run, (void *)string); +#elif defined _MSC_VER || defined __MINGW32__ + tid = (HANDLE)_beginthreadex(NULL, 0, (unsigned int (__stdcall *)(void *))_thread_run, + (void*)string, 0, NULL); +#else + tid = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)_thread_run, (void*)string, + 0, NULL); +#endif + } else + /* bg_halt (pause) a bg run */ + if (!strcmp(buf, "bg_halt")) { + signal(SIGINT, oldHandler); + return _thread_stop(); + } else + /* cannot do anything if ngspice is running in the bg*/ + if (fl_running) { + if (fl_exited) { + _thread_stop(); + cp_evloop(buf); + } else { + fprintf(stderr, "Warning: cannot execute \"%s\", type \"bg_halt\" first\n", buf); + } + } else { + /*do the command*/ + cp_evloop(buf); + } +#else + cp_evloop(buf); +#endif /*THREADS*/ + signal(SIGINT, oldHandler); + return 0; +} + + + +/**********************************************************/ +/* The functions exported explicitely from shared ngspice */ +/**********************************************************/ + +#ifdef THREADS + +/* Checks if spice is running in the background */ +IMPEXP +bool +ngSpice_running (void) +{ + return (fl_running && !fl_exited); +} +#endif + + +/* Initialise spice and setup native methods */ +IMPEXP +int +ngSpice_Init(SendChar* printfcn, SendStat* statusfcn, ControlledExit* ngspiceexit, + SendData* sdata, SendInitData* sinitdata, BGThreadRunning* bgtrun, void* userData) +{ + sighandler old_sigint; + + pfcn = printfcn; + /* if caller sends NULL, don't send printf strings */ + if (!pfcn) + noprintfwanted = TRUE; + userptr = userData; + statfcn = statusfcn; + /* if caller sends NULL, don't send status data */ + if (!statfcn) + nostatuswanted = TRUE; + ngexit = ngspiceexit; + datfcn = sdata; + /* if caller sends NULL, don't send data */ + if (!datfcn) + nodatawanted = TRUE; + /* if caller sends NULL, don't initialize and send data */ + datinitfcn = sinitdata; + if (!datinitfcn) + nodatawanted = nodatainitwanted = TRUE; + bgtr = bgtrun; + if (!bgtr) + nobgtrwanted = TRUE; + immediate = FALSE; + +#ifdef THREADS + /* init the mutex */ +#ifdef HAVE_LIBPTHREAD + pthread_mutex_init(&triggerMutex, NULL); + pthread_mutex_init(&allocMutex, NULL); + pthread_mutex_init(&fputsMutex, NULL); +#else +#ifdef CS + InitializeCriticalSection(&triggerMutex); + InitializeCriticalSection(&allocMutex); + InitializeCriticalSection(&fputsMutex); +#else + InitializeSRWLock(&triggerMutex); + InitializeSRWLock(&allocMutex); + InitializeSRWLock(&fputsMutex); +#endif +#endif + // Id of primary thread + main_id = threadid_self(); + signal(SIGINT, sighandler_sharedspice); +#endif + + ft_rawfile = NULL; + ivars(NULL); + + cp_in = stdin; + cp_out = stdout; + cp_err = stderr; + + /*timer*/ + init_time(); + + /*IFsimulator struct initilised*/ + SIMinit(&nutmeginfo, &ft_sim); + + /* program name*/ + cp_program = ft_sim->simulator; + + srand((unsigned int) getpid()); + TausSeed(); + + /*parameter fetcher, used in show, alter, altermod */ +// if_getparam = spif_getparam; + if_getparam = spif_getparam_special; + + /* Get startup system limits */ + init_rlimits(); + + /*Command prompt stuff */ + ft_cpinit(); + + /* Read the user config files */ + /* To catch interrupts during .spiceinit... */ + old_sigint = signal(SIGINT, (SIGNAL_FUNCTION) ft_sigintr); + if (SETJMP(jbuf, 1) == 1) { + fprintf(cp_err, "Warning: error executing .spiceinit.\n"); + goto bot; + } + +#ifdef HAVE_PWD_H + /* Try to source either .spiceinit or ~/.spiceinit. */ + if (access(".spiceinit", 0) == 0) { + inp_source(".spiceinit"); + } else { + char *s; + struct passwd *pw; + pw = getpwuid(getuid()); + +#ifdef HAVE_ASPRINTF + asprintf(&s, "%s%s", pw->pw_dir, INITSTR); +#else + s = TMALLOC(char, 1 + strlen(pw->pw_dir) + strlen(INITSTR)); + sprintf(s, "%s%s", pw->pw_dir, INITSTR); +#endif + if (access(s, 0) == 0) + inp_source(s); + } +#else /* ~ HAVE_PWD_H */ + { + FILE *fp; + /* Try to source the file ".spiceinit" in the current directory. */ + if ((fp = fopen(".spiceinit", "r")) != NULL) { + (void) fclose(fp); + inp_source(".spiceinit"); + } + } +#endif /* ~ HAVE_PWD_H */ +bot: + signal(SIGINT, old_sigint); + + /* initilise display to 'no display at all'*/ + DevInit(); + +#ifdef FastRand +// initialization and seed for FastNorm Gaussian random generator + { + unsigned int rseed = 66; + initnorm (0, 0); + if (!cp_getvar("rndseed", CP_NUM, &rseed)) { + time_t acttime = time(NULL); + rseed = (unsigned int) acttime; + } + initnorm (rseed, 2); + fprintf (cp_out, "SoS %f, seed value: %ld\n", renormalize(), rseed); + } +#elif defined (WaGauss) + { + unsigned int rseed = 66; + if (!cp_getvar("rndseed", CP_NUM, &rseed)) { + time_t acttime = time(NULL); + rseed = (unsigned int) acttime; + } + srand(rseed); + initw(); + } +#endif + + com_version(NULL); + + is_initialized = TRUE; + + if(!myvec) + myvec = TMALLOC(vector_info, sizeof(vector_info)); + +#if !defined(low_latency) + /* If caller has sent valid address for pfcn */ + if (!noprintfwanted) + printtid = (HANDLE)_beginthreadex(NULL, 0, (unsigned int (__stdcall *)(void *))printsend, + (void*) NULL, 0, NULL); +#endif + + return 0; +} + +/* retrieve a ngspice command from caller and run it +immediately */ +IMPEXP +int ngSpice_Command(char* comexec) +{ + if ( ! setjmp(errbufc) ) { +// HANDLE tid2; + + immediate = FALSE; + intermj = 1; + + if (!is_initialized) { + fprintf(stderr, no_init); + return 1; + } +// tid2 = (HANDLE)_beginthreadex(NULL, 0, (PTHREAD_START_ROUTINE)runc, (void*)comexec, +// 0, NULL); + runc(comexec); + /* main thread prepares immediate detaching of dll, + in case of controlled_exit from tid2 thread, caller is asked + to detach dll via fcn ngexit() */ + immediate = TRUE; + return 0; + } + return 1; +}; + +/* Return information about a vector to the caller */ +IMPEXP +pvector_info ngGet_Vec_Info(char* vecname) +{ + struct dvec* newvec; + + if (!is_initialized) { + fprintf(stderr, no_init); + return NULL; + } + + newvec = vec_get(vecname); + + if (newvec == NULL) { + fprintf(stderr, "Error: vector %s not found!\n", vecname); + return NULL; + } + if (newvec->v_numdims > 1) { + fprintf(stderr, "Error: vector %s is multidimensional!\n This is not yet handled\n!", vecname); + return NULL; + } + + myvec->v_name = newvec->v_name; + myvec->v_type = newvec->v_type; + myvec->v_flags = newvec->v_flags; + myvec->v_realdata = newvec->v_realdata; + myvec->v_compdata = newvec->v_compdata; + myvec->v_length = newvec->v_length; + return myvec; +}; + +/* Receive a circuit from the caller as a + pointer to an array of char* . + Last entry in array has to be NULL +*/ +IMPEXP +int ngSpice_Circ(char** circa){ + int entries = 0, i; + char* newline; + + if ( ! setjmp(errbufm) ) { + intermj = 0; + /* count the entries */ + while (circa[entries]) { + entries++; + } + /* create a local copy (to be freed in inpcom.c) */ + for (i = 0; i < entries; i++) { + newline = copy(circa[i]); + create_circbyline(newline); + } + return 0; + } + /* upon error */ + return 1; +} + + +/* return to the caller a pointer to the name of the current plot */ +IMPEXP +char* ngSpice_CurPlot(void) +{ + struct plot *pl = plot_cur; + return pl->pl_typename; +} + +/* return to the caller a pointer to an array of all plots created +by ngspice.dll */ +IMPEXP +char** ngSpice_AllPlots(void) +{ + int len = 0, i = 0; + struct plot *pl = plot_list; + if (allplots) + tfree(allplots); + + while (pl) { + len++; + pl = pl->pl_next; + } + allplots = TMALLOC(char*, len+1); + pl = plot_list; + for (i = 0; i < len; i++) { + allplots[i] = pl->pl_typename; + pl = pl->pl_next; + } + allplots[len] = '\0'; + return allplots; +} + +/* return to the caller a pointer to an array of vector names in the plot +named by plotname */ +IMPEXP +char** ngSpice_AllVecs(char* plotname) +{ + struct dvec *d; + int len = 0, i = 0; + struct plot *pl; + + if (allvecs) + tfree(allvecs); + + /* get the plot plotname */ + pl = get_plot_byname(plotname); + + if (pl) + for (d = pl->pl_dvecs; d; d = d->v_next) + len++; + + if (len == 0) { + fprintf(cp_err, "Error: There are no vectors currently active.\n"); + return NULL; + } + + allvecs = TMALLOC(char*, len + 1); + + for (d = pl->pl_dvecs, i = 0; d; d = d->v_next, i++) + allvecs[i] = d->v_name; + + allvecs[len] = NULL; + + return allvecs; +} + + +/*------------------------------------------------------*/ +/* Redefine the vfprintf() functions for callback */ +/*------------------------------------------------------*/ + +/* handling of escape characters (extra \ added) is removed, may be added by + un-commenting some lines */ + +int +sh_vfprintf(FILE *f, const char *fmt, va_list args) +{ + char buf[1024]; + char *p/*, *s*/; + int nchars, /*escapes,*/ result; + size_t size; + + + if ((fileno(f) != STDOUT_FILENO && fileno(f) != STDERR_FILENO && + f != stderr && f != stdout) +#ifdef THREADS +// || (fl_running && bgtid == thread_self()) +#endif + ) + return vfprintf(f, fmt, args); + + p = buf; + + // size: how much ist left for chars and terminating '\0' + size = sizeof(buf); + // assert(size > 0); + + for (;;) { + nchars = vsnprintf(p, size, fmt, args); + + if(nchars == -1) { // compatibility to old implementations + size *= 2; + nchars = vsnprintf(p, size, fmt, args); + } else if (size < (size_t)nchars + 1) { + size = (size_t)nchars + 1; + } else { + break; + } + + if(p == buf) + p = TMALLOC(char, size); + else + p = TREALLOC(char, p, size); + } + + /* add / to escape characters, if 'set addescape' is called in .spiceinit */ + if (cp_getvar("addescape", CP_BOOL, NULL)) { + size_t escapes; + const char * const escape_chars = "$[]\"\\"; + char *s = p; + for (escapes = 0; ; escapes++) { + s = strpbrk(s, escape_chars); + if (!s) + break; + s++; + } + + if (escapes) { + + size_t new_size = (size_t)nchars + escapes + 1; + char *src, *dst; + + if (p != buf) { + p = TREALLOC(char, p, new_size); + } else if (new_size > sizeof(buf)) { + p = TMALLOC(char, new_size); + strcpy(p, buf); + } + + src = p + nchars; + dst = src + escapes; + + while (dst > src) { + char c = *--src; + *--dst = c; + if (strchr(escape_chars, c)) + *--dst = '\\'; + } + } + } + + /* use sharedspice implementation of fputs (sh_fputs) + to assess callback function derived from address printfcn received via + Spice_Init() from caller of ngspice.dll */ + + + result = sh_fputs(p, f); + + if (p != buf) + tfree(p); + + return nchars; +} + + +/*----------------------------------------------------------------------*/ +/* Reimplement fprintf() as a call to callback function pfcn */ +/*----------------------------------------------------------------------*/ + +int +sh_fprintf(FILE *f, const char *format, ...) +{ + va_list args; + int rtn; + + va_start (args, format); + rtn = sh_vfprintf(f, format, args); + va_end(args); + + return rtn; +} + + +/*----------------------------------------------------------------------*/ +/* Reimplement printf() as a call to callback function pfcn */ +/*----------------------------------------------------------------------*/ + +int +sh_printf(const char *format, ...) +{ + va_list args; + int rtn; + + va_start (args, format); + rtn = sh_vfprintf(stdout, format, args); + va_end(args); + + return rtn; +} + +int +sh_putc(const char inp, FILE* f) +{ + char inpconv[2]; + if ((fileno(f) != STDOUT_FILENO && fileno(f) != STDERR_FILENO && + f != stderr && f != stdout)) + return myfputc(inp, f); + + sprintf(inpconv, "%c", inp); + fputs(inpconv, f); + return inp; +} + +int +sh_fputc(const char inp, FILE* f) +{ + char inpconv[2]; + if ((fileno(f) != STDOUT_FILENO && fileno(f) != STDERR_FILENO && + f != stderr && f != stdout)) + return myfputc(inp, f); + + sprintf(inpconv, "%c", inp); + fputs(inpconv, f); + return inp; +} + +/*----------------------------------------------------------------------*/ +/* Reimplement fputs() as a call to callback function pfcn */ +/*----------------------------------------------------------------------*/ + + +/* Collect and cat strings. + If \n is detected, send string + to caller via pfcn() */ +static char* outstringerr = NULL; +static char* outstringout = NULL; + +#ifdef low_latency + +int +sh_fputsll(const char *input, FILE* outf) +{ + int result = 0; + size_t len; + char *delstring, *newstring, *prstring; + size_t inputlen = strlen(input); + + /* If caller has sent NULL address for pfcn */ + if (noprintfwanted) + return -1; + + if (outf == stderr) { + if (!outstringerr) + delstring = outstringerr = copy(input); + else { + len = strlen(outstringerr); + delstring = outstringerr = TREALLOC(char, outstringerr, len + inputlen + 2); + strcat(outstringerr, input); + } + if (strchr(input, '\n')) { + while (outstringerr) { + newstring = gettok_char(&outstringerr, '\n', FALSE, FALSE); + if(!newstring) + break; + prstring = TMALLOC(char, 7 + strlen(newstring) + 1); + strcat(prstring, "stderr "); + strcat(prstring, newstring); + + printtid = (HANDLE)_beginthreadex(NULL, 0, (unsigned int (__stdcall *)(void *))printing, + (void*)prstring, 0, NULL); + +// result = pfcn(prstring, userptr); + tfree(newstring); + tfree(prstring); + } + /* copy the rest following \n, but without trailing \n to new address */ + if (outstringerr && *outstringerr != '\0') + outstringerr = copy(outstringerr); + else + outstringerr = NULL; + tfree(delstring); + return result; + } + else if (strchr(input, '\r')) { + result = pfcn(outstringerr, userptr); + tfree(outstringerr); + return result; + } + } + if (outf == stdout) { + if (!outstringout) + delstring = outstringout = copy(input); + else { + len = strlen(outstringout); + delstring = outstringout = TREALLOC(char, outstringout, len + inputlen + 1); + strcat(outstringout, input); + } + if (strchr(input, '\n')) { + while (outstringout) { + newstring = gettok_char(&outstringout, '\n', FALSE, FALSE); + if(!newstring) + break; + prstring = TMALLOC(char, 7 + strlen(newstring) + 1); + strcat(prstring, "stdout "); + strcat(prstring, newstring); + result = pfcn(prstring, userptr); + tfree(newstring); + tfree(prstring); + } + /* copy the rest following \n, but without trailing \n to new address */ + if (outstringout && *outstringout != '\0') + outstringout = copy(outstringout); + else + outstringout = NULL; + tfree(delstring); + return result; + } + else if (strchr(input, '\r')) { + result = pfcn(outstringout, userptr); + tfree(outstringout); + return result; + } + } + else + myputs(input, outf); + + return 0; +} + +/* allow a lock around printing function */ +int +sh_fputs(const char *input, FILE* outf) +{ + mutex_lock(&fputsMutex); + sh_fputsll(input, outf); + mutex_unlock(&fputsMutex); + return 0; +} + +#else + +/* FIFO storage for strings created by fputs and all other printing commands. + A string will be appended to the FIFO by fcn + outstorage() by the main thread or the background (bg_) thread. + Each string is read from top of the FIFO by independent thread using + again fcn outstoraghe(), top entry is deleted and string is + sent to caller in an endless loop by fcn printsend() */ +static wordlist *wlstart = NULL, *wlend = NULL; + + + +int +sh_fputs(const char *input, FILE* outf) +{ + int result = 0; + size_t len; + char *delstring, *newstring, *prstring; + size_t inputlen = strlen(input); + + /* If caller has sent NULL address for pfcn */ + if (noprintfwanted) + return -1; + + if (outf == stderr) { + if (!outstringerr) + delstring = outstringerr = copy(input); + else { + len = strlen(outstringerr); + delstring = outstringerr = TREALLOC(char, outstringerr, len + inputlen + 2); + strcat(outstringerr, input); + } + if (strchr(input, '\n')) { + while (outstringerr) { + newstring = gettok_char(&outstringerr, '\n', FALSE, FALSE); + if(!newstring) + break; + prstring = TMALLOC(char, 7 + strlen(newstring) + 1); + strcat(prstring, "stderr "); + strcat(prstring, newstring); + mutex_lock(&fputsMutex); + outstorage(prstring, TRUE); + mutex_unlock(&fputsMutex); + tfree(newstring); + prstring = NULL; /* keep prstring here, address is in use */ + } + /* copy the rest following \n, but without trailing \n to new address */ + if (outstringerr && *outstringerr != '\0') + outstringerr = copy(outstringerr); + else + outstringerr = NULL; + tfree(delstring); + return result; + } + else if (strchr(input, '\r')) { + mutex_lock(&fputsMutex); + outstorage(outstringerr, TRUE); + mutex_unlock(&fputsMutex); + outstringerr = NULL; + return 0; + } + } + if (outf == stdout) { + if (!outstringout) + delstring = outstringout = copy(input); + else { + len = strlen(outstringout); + delstring = outstringout = TREALLOC(char, outstringout, len + inputlen + 1); + strcat(outstringout, input); + } + if (strchr(input, '\n')) { + while (outstringout) { + newstring = gettok_char(&outstringout, '\n', FALSE, FALSE); + if(!newstring) + break; + prstring = TMALLOC(char, 7 + strlen(newstring) + 1); + strcat(prstring, "stdout "); + strcat(prstring, newstring); + mutex_lock(&fputsMutex); + outstorage(prstring, TRUE); + mutex_unlock(&fputsMutex); + tfree(newstring); + prstring = NULL; + } + /* copy the rest following \n, but without trailing \n to new address */ + if (outstringout && *outstringout != '\0') + outstringout = copy(outstringout); + else + outstringout = NULL; + tfree(delstring); + return result; + } + else if (strchr(input, '\r')) { + mutex_lock(&fputsMutex); + outstorage(outstringout, TRUE); + mutex_unlock(&fputsMutex); + outstringout = NULL; + return result; + } + } + else + myputs(input, outf); + + return 0; +} + +/* Endless lop in its own thread for reading data from FIFO and sending to caller */ +static void * +printsend(void) +{ + char *outsend = NULL; + + for (;;) { +#if defined(__MINGW32__) || defined(_MSC_VER) + Sleep(100); +#else + usleep(100000); +#endif + mutex_lock(&fputsMutex); + outsend = outstorage(NULL, FALSE); + mutex_unlock(&fputsMutex); + if (outsend) { + /* requires outsend to be copied by the caller, + because it is freed immediately */ + pfcn(outsend, userptr); + tfree(outsend); + } + } +} + +/* remove the first entry of a wordlist, but keep wl->wl_word */ +void wl_delete_first(wordlist **wlstart, wordlist **wlend) +{ + wordlist *wl_temp; + + if (!(*wlstart)) + return; + if ((*wlstart) && !((*wlstart)->wl_next)) { + tfree(*wlstart); /* keep wlstart->wl_word */ + (*wlstart) = NULL; + (*wlend) = NULL; + return; + } + wl_temp = (*wlstart)->wl_next; + wl_temp->wl_prev = NULL; + tfree(*wlstart); /* keep wlstart->wl_word */ + (*wlstart) = wl_temp; +} + + +/* create a wordlist FIFO using global static variables wlstart and wlend. + wordin has to be malloced on the heap */ +char* outstorage(char* wordin, bool write) +{ + char *wordout = NULL; + + if(write) + wl_append_word(&wlstart, &wlend, wordin); + else if (wlstart) { + wordout = wlstart->wl_word; + wl_delete_first(&wlstart, &wlend); + } + return wordout; +} + +#endif + + +/* New progress report to statfcn(). + Update only every DELTATIME milliseconds */ +#define DELTATIME 150 +void SetAnalyse( + char * Analyse, /*in: analysis type */ + int DecaPercent /*in: 10 times the progress [%]*/ + /*HWND hwAnalyse, in: global handle to analysis window */ +) { + static int OldPercent = -2; /* Previous progress value */ + static char OldAn[128]; /* Previous analysis type */ + char* s; /* outputs to callback function */ + static struct timeb timebefore; /* previous time stamp */ + struct timeb timenow; /* actual time stamp */ + int diffsec, diffmillisec; /* differences actual minus prev. time stamp */ + int result; /* return value from callback function */ + + /* If caller has sent NULL address for statfcn */ + if (nostatuswanted) + return; + + if ((DecaPercent == OldPercent) && !strcmp(OldAn, Analyse)) + return; + + /* get actual time */ + ftime(&timenow); + timediff(&timenow, &timebefore, &diffsec, &diffmillisec); + s = TMALLOC(char, 128); + + if (DecaPercent >= 1000){ + sprintf( s, "--ready--"); + result = statfcn(s, userptr); + tfree(s); + return; + } + /* info every one percent of progress: + actual time, progress, + to catch linearity of progress of simulation */ + if (ft_ngdebug && !strcmp(Analyse, "tran")) + if ((int)((double)DecaPercent/10.) > (int)((double)OldPercent/10.)) { + printf("%3.1f%% percent progress after %4.2f seconds.\n", (double)DecaPercent/10., seconds()); + } + OldPercent = DecaPercent; + /* output only into hwAnalyse window and if time elapsed is larger than + DELTATIME given value, or if analysis has changed, else return */ + if ((diffsec > 0) || (diffmillisec > DELTATIME) || strcmp(OldAn, Analyse)) { + if (DecaPercent < 0) { + sprintf( s, "--ready--"); + } + else if (DecaPercent == 0) { + sprintf( s, "%s", Analyse); + } + else if (!strcmp(Analyse, "shooting")) { + sprintf( s, "%s: %d", Analyse, DecaPercent); + } + else { + sprintf( s, "%s: %3.1f%%", Analyse, (double)DecaPercent/10.); + } + timebefore.dstflag = timenow.dstflag; + timebefore.millitm = timenow.millitm; + timebefore.time = timenow.time; + timebefore.timezone = timenow.timezone; + /* info when previous analysis period has finished */ + if (strcmp(OldAn, Analyse)) { + if (ft_ngdebug && (strcmp(OldAn, ""))) + printf("%s finished after %4.2f seconds.\n", OldAn, seconds()); + strncpy(OldAn, Analyse, 127); + } + + result = statfcn(s, userptr); + } + tfree(s); +} + +/* a dll or shared library should never exit, but ask for graceful shutdown + (e.g. being detached) via a callback function*/ +void shared_exit(int status) +{ + /* alert caller to detach dll (if we are in the main thread), + or detach after a short sleep, if immediate is true, and we are + in a worker thread */ + if (immediate) +#if defined(__MINGW32__) || defined(_MSC_VER) + Sleep(100); // va: windows native +#else + usleep(10000); +#endif + /* status >= 1000 tells us that we react on command 'quit' + hand this information over to caller */ + if (status >= 1000) { + coquit = TRUE; + fprintf(stdout, "\nNote: 'quit' asks for detaching ngspice.dll.\n"); + status -= 1000; + } + else { + coquit = FALSE; + fprintf(stderr, "Error: ngspice.dll cannot recover and awaits to be detached\n"); + } + ngexit(status, immediate, coquit, userptr); + if (!intermj) + longjmp(errbufm,1); /* jump back to ngSpice_Circ() */ + else + longjmp(errbufc,1); /* jump back to ngSpice_Command() */ +} + +static int len = 0; +static pvecvaluesall curvecvalsall; + +#ifdef olld +static pvecvalues* curvecvals; +static char type_name[128]; +int sh_ExecutePerLoop_old(void) +{ + struct dvec *d; + int i, veclen; + struct plot *pl = plot_cur; + /* return immediately if callback not wanted */ + if (nodatawanted) + return 2; + + /* reset data structure if there is a change in plot type */ + if (strcmp(type_name, pl->pl_typename)) { + + if (curvecvals) { + for (i = 0; i < len; i++) + tfree(curvecvals[i]); + tfree(curvecvals); + } + len = 0; + bzero(type_name, 128); + } + + /* initialize new for every new plot, e.g. if changed from op1 to ac1 + or from tran1 to tran2 */ + if ((pl) && (len == 0)) { + strcpy(type_name, pl->pl_typename); + for (d = pl->pl_dvecs; d; d = d->v_next) + len++; + + if (len == 0) { + fprintf(cp_err, "Error: There are no vectors currently active.\n"); + return 1; + } + /* allocate memory for the number of vectors */ + curvecvals = TMALLOC(pvecvalues, len); + + /* allocate memory for each entry and add vector names once */ + for (d = pl->pl_dvecs, i = 0; d; d = d->v_next, i++) { + curvecvals[i] = TMALLOC(vecvalues, 1); + curvecvals[i]->name = copy(d->v_name); + } + } + /* get the data of the last entry to the plot vector */ + veclen = pl->pl_dvecs->v_length - 1; + for (d = pl->pl_dvecs, i = 0; d; d = d->v_next, i++) { + /* test if real */ + if (d->v_flags & VF_REAL) { + curvecvals[i]->is_complex = FALSE; + curvecvals[i]->creal = d->v_realdata[veclen]; + curvecvals[i]->cimag = 0.; + } + else { + curvecvals[i]->is_complex = TRUE; + curvecvals[i]->creal = d->v_compdata[veclen].cx_real; + curvecvals[i]->cimag = d->v_compdata[veclen].cx_imag; + } + } + /* now call the callback function to return the data to the caller */ + if (!nodatawanted) + // datfcn(curvecvals, len, userptr); + + return 0; +} +#endif + +/* called each time a new data set is written to the output vectors */ +int sh_ExecutePerLoop(void) +{ + struct dvec *d; + int i, veclen; + struct plot *pl = plot_cur; + /* return immediately if callback not wanted */ + if (nodatawanted) + return 2; + + /* get the data of the last entry to the plot vector */ + veclen = pl->pl_dvecs->v_length - 1; + for (d = pl->pl_dvecs, i = 0; d; d = d->v_next, i++) { + /* test if real */ + if (d->v_flags & VF_REAL) { + curvecvalsall->vecsa[i]->is_complex = FALSE; + curvecvalsall->vecsa[i]->creal = d->v_realdata[veclen]; + curvecvalsall->vecsa[i]->cimag = 0.; + } + else { + curvecvalsall->vecsa[i]->is_complex = TRUE; + curvecvalsall->vecsa[i]->creal = d->v_compdata[veclen].cx_real; + curvecvalsall->vecsa[i]->cimag = d->v_compdata[veclen].cx_imag; + } + } + /* now call the callback function to return the data to the caller */ + datfcn(curvecvalsall, len, userptr); + + return 0; +} + + +/* called once for a new plot from beginPlot() in outitf.c, + after the vectors in ngspice have been set. + Transfers vector information to the caller via callback datinitfcn() + and sets transfer structure for use in sh_ExecutePerLoop() */ +int sh_vecinit(runDesc *run) +{ + struct dvec *d, *ds; + int veccount, i; + static pvecinfoall pvca = NULL; + pvecinfo *pvc; + + /* return immediately if callback not wanted */ + if (nodatainitwanted) + return 2; + + cur_run = run; + + len = veccount = cur_run->numData; + + if (veccount == 0) { + fprintf(cp_err, "Error: There are no vectors currently active.\n"); + return 1; + } + + /* delete the structs from the previous plot */ + if (pvca) { + for (i = 0; i < pvca->veccount; i++) + tfree(pvca->vecs[i]); + tfree(pvca->vecs); + tfree(pvca); + pvca = NULL; + } + + pvc = TMALLOC(pvecinfo, veccount); + ds = cur_run->runPlot->pl_scale; + for (i = 0, d = cur_run->runPlot->pl_dvecs; i < veccount; i++, d = d->v_next) { + pvc[i] = TMALLOC(vecinfo, 1); + pvc[i]->number = i; + pvc[i]->pdvec = (void*)d; + pvc[i]->pdvecscale = (void*)ds; + pvc[i]->vecname = d->v_name; + pvc[i]->is_real = (d->v_flags & VF_REAL); + } + pvca = TMALLOC(vecinfoall, 1); + // the plot + pvca->title = cur_run->runPlot->pl_title; + pvca->date = cur_run->runPlot->pl_date; + pvca->name = cur_run->runPlot->pl_name; + pvca->type = cur_run->runPlot->pl_typename; + pvca->veccount = veccount; + // the data + pvca->vecs = pvc; + /* now call the callback function to return the data to the caller */ + datinitfcn(pvca, userptr); + + /* generate the data tranfer structure, + data will be sent from sh_ExecutePerLoop() via datfcn() */ + if (!curvecvalsall) { + curvecvalsall = TMALLOC(vecvaluesall, 1); + curvecvalsall->veccount = veccount; + } + else { + for (i = 0; i < curvecvalsall->veccount; i++) + tfree(curvecvalsall->vecsa[i]); + tfree(curvecvalsall->vecsa); + } + + curvecvalsall->vecsa = TMALLOC(pvecvalues, veccount); + + for (i = 0, d = cur_run->runPlot->pl_dvecs; i < veccount; i++, d = d->v_next) { + curvecvalsall->vecsa[i] = TMALLOC(vecvalues,1); + curvecvalsall->vecsa[i]->name = d->v_name; + if (cieq(d->v_plot->pl_scale->v_name, d->v_name)) + curvecvalsall->vecsa[i]->is_scale = TRUE; + else + curvecvalsall->vecsa[i]->is_scale = FALSE; + } + return 0; +} +