1 /* Steven Andrews, 1/10/04.
2 Library for runtime command interpreter used for various simulations.
3 Copyright 2004-2007 by Steven Andrews. This work is distributed under the terms
4 of the Gnu Lesser General Public License (LGPL). */
5
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <limits.h>
11 #include "SimCommand.h"
12 #include "Zn.h"
13
14
15 /* Define SMOLDYN to 1 if this library is used in Smoldyn, or undefine this definition
16 if it is not used in Smoldyn. */
17
18 #define SMOLDYN 1
19
20 #if defined(SMOLDYN)
21 #include "smoldynfuncs.h"
22 #define SCMDPRINTF(S,A,...) simLog((simptr) S,A,__VA_ARGS__)
23 #else
24 #define SCMDPRINTF(S,A,...) printf(__VA_ARGS__)
25 #endif
26
27
28 void scmdcatfname(cmdssptr cmds,int fid,char *str);
29 void scmdcopycommand(cmdptr cmdfrom,cmdptr cmdto);
30 char *scmdcode2string(enum CMDcode code,char *string);
31
32 cmdptr scmdalloc(void);
33 void scmdfree(cmdptr cmd);
34 int scmdcmdlistalloc(cmdssptr cmds,int newspaces);
35 int scmdqalloc(cmdssptr cmds,int n);
36 int scmdqalloci(cmdssptr cmds,int n);
37
38 void scmddocommandtiming(cmdptr cmd,double tmin,double tmax,double dt,int iter);
39
40
41 /* ***** Utility functions ***** */
42
43 /* scmdcatfname */
scmdcatfname(cmdssptr cmds,int fid,char * str)44 void scmdcatfname(cmdssptr cmds,int fid,char *str) {
45 char *dot;
46 int min;
47
48 strncpy(str,cmds->root,STRCHAR);
49 strncat(str,cmds->froot,STRCHAR-strlen(str));
50 dot=strrchr(cmds->fname[fid],'.');
51 if(dot) {
52 min=STRCHAR-strlen(str)<(unsigned int)(dot-cmds->fname[fid])?STRCHAR-strlen(str):(unsigned int)(dot-cmds->fname[fid]);
53 strncat(str,cmds->fname[fid],min); }
54 else strncat(str,cmds->fname[fid],STRCHAR);
55 if(cmds->fsuffix[fid] && STRCHAR-strlen(str)>4) snprintf(str+strlen(str),sizeof(str)-strlen(str),"_%03i",cmds->fsuffix[fid]);
56 if(dot) strncat(str,dot,STRCHAR-strlen(str));
57 return; }
58
59
60 /* scmdcopycommand */
scmdcopycommand(cmdptr cmdfrom,cmdptr cmdto)61 void scmdcopycommand(cmdptr cmdfrom,cmdptr cmdto) {
62 if(!cmdfrom || !cmdto || cmdfrom==cmdto) return;
63 cmdto->cmds=cmdfrom->cmds;
64 cmdto->twin=cmdfrom;
65 cmdto->timing=cmdfrom->timing;
66 cmdto->on=cmdfrom->on;
67 cmdto->off=cmdfrom->off;
68 cmdto->dt=cmdfrom->dt;
69 cmdto->xt=cmdfrom->xt;
70 cmdto->oni=cmdfrom->oni;
71 cmdto->offi=cmdfrom->offi;
72 cmdto->dti=cmdfrom->dti;
73 cmdto->invoke=0;
74 strncpy(cmdto->str,cmdfrom->str,STRCHAR);
75 strncpy(cmdto->erstr,"",STRCHAR);
76 cmdto->i1=cmdto->i2=cmdto->i3=0;
77 cmdto->f1=cmdto->f2=cmdto->f3=0;
78 cmdto->v1=cmdto->v2=cmdto->v3=NULL;
79 cmdto->freefn=NULL;
80 return; }
81
82
83 /* scmdcode2string */
scmdcode2string(enum CMDcode code,char * string)84 char *scmdcode2string(enum CMDcode code,char *string) {
85 if(code==CMDok) strcpy(string,"ok");
86 else if(code==CMDpause) strcpy(string,"pause");
87 else if(code==CMDstop) strcpy(string,"stop");
88 else if(code==CMDwarn) strcpy(string,"warn");
89 else if(code==CMDabort) strcpy(string,"abort");
90 else if(code==CMDcontrol) strcpy(string,"control");
91 else if(code==CMDobserve) strcpy(string,"observe");
92 else if(code==CMDmanipulate) strcpy(string,"manipulate");
93 else strcpy(string,"none");
94 return string; }
95
96
97 /* Memory management */
98
99 /* scmdalloc */
scmdalloc(void)100 cmdptr scmdalloc(void) {
101 cmdptr cmd;
102
103 cmd=(cmdptr) malloc(sizeof(struct cmdstruct));
104 if(!cmd) return NULL;
105 cmd->cmds=NULL;
106 cmd->twin=NULL;
107 cmd->timing='?';
108 cmd->on=cmd->off=cmd->dt=cmd->xt=0;
109 cmd->oni=cmd->offi=cmd->dti=0;
110 cmd->invoke=0;
111 cmd->str=cmd->erstr=NULL;
112 cmd->str=EmptyString();
113 if(!cmd->str) {scmdfree(cmd);return NULL;}
114 cmd->erstr=EmptyString();
115 if(!cmd->erstr) {scmdfree(cmd);return NULL;}
116 cmd->i1=cmd->i2=cmd->i3=0;
117 cmd->f1=cmd->f2=cmd->f3=0;
118 cmd->v1=cmd->v2=cmd->v3=NULL;
119 cmd->freefn=NULL;
120 return cmd; }
121
122
123 /* scmdfree */
scmdfree(cmdptr cmd)124 void scmdfree(cmdptr cmd) {
125 if(!cmd) return;
126 if(cmd->freefn) (*cmd->freefn)(cmd);
127 if(cmd->str) free(cmd->str);
128 if(cmd->erstr) free(cmd->erstr);
129 free(cmd);
130 return; }
131
132
133 /* scmdssalloc */
scmdssalloc(enum CMDcode (* cmdfn)(void *,cmdptr,char *),void * simvd,const char * root)134 cmdssptr scmdssalloc(enum CMDcode (*cmdfn)(void*,cmdptr,char*),void *simvd,const char *root) {
135 cmdssptr cmds;
136
137 cmds=(cmdssptr) malloc(sizeof(struct cmdsuperstruct));
138 if(!cmds) return NULL;
139
140 cmds->condition=0;
141 cmds->cmdlist=NULL;
142 cmds->maxcmdlist=0;
143 cmds->ncmdlist=0;
144 cmds->cmd=NULL;
145 cmds->cmdi=NULL;
146 cmds->cmdfn=cmdfn;
147 cmds->simvd=simvd;
148 cmds->iter=0;
149
150 cmds->flag=0;
151
152 cmds->maxfile=0;
153 cmds->nfile=0;
154 if(root) {
155 strncpy(cmds->root,root,STRCHAR-1);
156 cmds->root[STRCHAR-1]='\0'; }
157 else cmds->root[0]='\0';
158 cmds->froot[0]='\0';
159 cmds->fname=NULL;
160 cmds->fsuffix=NULL;
161 cmds->fappend=NULL;
162 cmds->fptr=NULL;
163 cmds->precision=-1;
164 cmds->outformat='s';
165
166 cmds->maxdata=0;
167 cmds->ndata=0;
168 cmds->dname=NULL;
169 cmds->data=NULL;
170 return cmds; }
171
172
173 /* scmdssfree */
scmdssfree(cmdssptr cmds)174 void scmdssfree(cmdssptr cmds) {
175 int fid,did,i;
176 void *voidptr;
177 cmdptr cmd;
178
179 if(!cmds) return;
180
181 if(cmds->cmd) {
182 while(q_pop(cmds->cmd,NULL,NULL,NULL,NULL,&voidptr)>=0) {
183 cmd=(cmdptr)voidptr;
184 scmdfree(cmd); }
185 q_free(cmds->cmd,0,0); }
186
187 if(cmds->cmdi) {
188 while(q_pop(cmds->cmdi,NULL,NULL,NULL,NULL,&voidptr)>=0) {
189 cmd=(cmdptr)voidptr;
190 scmdfree(cmd); }
191 q_free(cmds->cmdi,0,0); }
192
193 if(cmds->cmdlist) {
194 for(i=0;i<cmds->ncmdlist;i++)
195 scmdfree(cmds->cmdlist[i]);
196 free(cmds->cmdlist); }
197
198 for(fid=0;fid<cmds->nfile;fid++)
199 if(cmds->fptr && cmds->fptr[fid]) fclose(cmds->fptr[fid]);
200 free(cmds->fptr);
201 for(fid=0;fid<cmds->maxfile;fid++)
202 if(cmds->fname) free(cmds->fname[fid]);
203 free(cmds->fname);
204 free(cmds->fsuffix);
205 free(cmds->fappend);
206
207 for(did=0;did<cmds->maxdata;did++) {
208 if(cmds->dname) free(cmds->dname[did]);
209 if(cmds->data) ListFreeDD(cmds->data[did]); }
210 free(cmds->dname);
211 free(cmds->data);
212
213 free(cmds);
214 return; }
215
216
217 /* scmdcmdlistalloc */
scmdcmdlistalloc(cmdssptr cmds,int newspaces)218 int scmdcmdlistalloc(cmdssptr cmds,int newspaces) {
219 int i,newmax;
220 cmdptr *newcmdlist;
221
222 if(!cmds) return 0;
223 if(newspaces<=0) return 0;
224 newmax=cmds->maxcmdlist+newspaces;
225 newcmdlist=(cmdptr*) calloc(newmax,sizeof(cmdptr));
226 if(!newcmdlist) return 1;
227
228 for(i=0;i<cmds->ncmdlist;i++)
229 newcmdlist[i]=cmds->cmdlist[i];
230 for(;i<newmax;i++)
231 newcmdlist[i]=NULL;
232
233 if(cmds->cmdlist)
234 free(cmds->cmdlist);
235 cmds->cmdlist=newcmdlist;
236 cmds->maxcmdlist=newmax;
237 return 0; }
238
239
240 /* scmdqalloc */
scmdqalloc(cmdssptr cmds,int n)241 int scmdqalloc(cmdssptr cmds,int n) {
242 if(!cmds) return 2;
243 if(n<=0) return 0;
244 if(cmds->cmd) return 3;
245 cmds->cmd=q_alloc(n+1,Qdouble,NULL);
246 if(!cmds->cmd) return 1;
247 return 0; }
248
249
250 /* scmdqalloci */
scmdqalloci(cmdssptr cmds,int n)251 int scmdqalloci(cmdssptr cmds,int n) {
252 if(!cmds) return 2;
253 if(n<=0) return 0;
254 if(cmds->cmdi) return 3;
255 cmds->cmdi=q_alloc(n+1,Qlong,NULL);
256 if(!cmds->cmdi) return 1;
257 return 0; }
258
259
260 /* scmdsetcondition */
scmdsetcondition(cmdssptr cmds,int cond,int upgrade)261 void scmdsetcondition(cmdssptr cmds,int cond,int upgrade) {
262 if(!cmds) return;
263 if(upgrade==0 && cmds->condition>cond) cmds->condition=cond;
264 else if(upgrade==1 && cmds->condition<cond) cmds->condition=cond;
265 else if(upgrade==2) cmds->condition=cond;
266 return; }
267
268
269 /* scmdaddcommand */
scmdaddcommand(cmdssptr cmds,char timing,double on,double off,double step,double multiplier,const char * commandstring)270 int scmdaddcommand(cmdssptr cmds,char timing,double on,double off,double step,double multiplier,const char *commandstring) {
271 cmdptr cmd;
272 int er;
273
274 if(!cmds) return 2;
275 if(!commandstring || strlen(commandstring)==0) return 3;
276 if(!(cmd=scmdalloc())) return 1;
277 cmd->cmds=cmds;
278 cmd->timing=timing;
279 if(strchr("baBAEe",timing));
280 else if(strchr("@",timing))
281 cmd->on=cmd->off=on;
282 else if(strchr("i",timing)) {
283 cmd->on=on;
284 cmd->off=off;
285 cmd->dt=step; }
286 else if(strchr("x",timing)) {
287 cmd->on=on;
288 cmd->off=off;
289 cmd->dt=step;
290 cmd->xt=multiplier; }
291 else if(strchr("&",timing)) {
292 cmd->oni=cmd->offi=(Q_LONGLONG)on;
293 cmd->dti=1; }
294 else if(strchr("Ij",timing)) {
295 cmd->oni=(Q_LONGLONG)on;
296 cmd->offi=(Q_LONGLONG)off;
297 cmd->dti=(Q_LONGLONG)step; }
298 else if(strchr("Nn",timing))
299 cmd->dti=(Q_LONGLONG)step;
300 else {
301 scmdfree(cmd);
302 return 6; }
303
304 strncpy(cmd->str,commandstring,STRCHAR);
305 if(cmd->str[strlen(cmd->str)-1]=='\n')
306 cmd->str[strlen(cmd->str)-1]='\0';
307
308 if(cmds->ncmdlist==cmds->maxcmdlist) {
309 er=scmdcmdlistalloc(cmds,1+cmds->maxcmdlist);
310 if(er) {scmdfree(cmd);return 1;} }
311 cmds->cmdlist[cmds->ncmdlist]=cmd;
312 cmds->ncmdlist++;
313 scmdsetcondition(cmds,2,0);
314 return 0; }
315
316
317 /* scmdstr2cmd */
scmdstr2cmd(cmdssptr cmds,char * line2,char ** varnames,double * varvalues,int nvar)318 int scmdstr2cmd(cmdssptr cmds,char *line2,char **varnames,double *varvalues,int nvar) {
319 int itct,er;
320 char timing;
321 double on,off,step,multiplier;
322
323 if(!cmds) return 2;
324 if(!line2) return 3;
325
326 on=off=step=multiplier=0;
327 itct=sscanf(line2,"%c",&timing);
328 if(itct!=1) return 3;
329 if(!(line2=strnword(line2,2))) return 3;
330 if(strchr("baBAEe",timing));
331 else if(strchr("@&",timing)) {
332 itct=strmathsscanf(line2,"%mlg",varnames,varvalues,nvar,&on);
333 if(itct!=1) return 3;
334 if(!(line2=strnword(line2,2))) return 3; }
335 else if(strchr("Nn",timing)) {
336 itct=strmathsscanf(line2,"%mlg",varnames,varvalues,nvar,&step);
337 if(itct!=1) return 3;
338 if(!(line2=strnword(line2,2))) return 3; }
339 else if(strchr("iIj",timing)) {
340 itct=strmathsscanf(line2,"%mlg %mlg %mlg",varnames,varvalues,nvar,&on,&off,&step);
341 if(itct!=3) return 3;
342 if(!(line2=strnword(line2,4))) return 3; }
343 else if(strchr("x",timing)) {
344 itct=strmathsscanf(line2,"%mlg %mlg %mlg %mlg",varnames,varvalues,nvar,&on,&off,&step,&multiplier);
345 if(itct!=4) return 3;
346 if(!(line2=strnword(line2,5))) return 3; }
347 else return 6;
348
349 er=scmdaddcommand(cmds,timing,on,off,step,multiplier,line2);
350 return er; }
351
352
353 /* scmddocommandtiming */
scmddocommandtiming(cmdptr cmd,double tmin,double tmax,double dt,int iter)354 void scmddocommandtiming(cmdptr cmd,double tmin,double tmax,double dt,int iter) {
355 char timing;
356
357 timing=cmd->timing;
358 if(timing=='b') {
359 cmd->on=cmd->off=tmin-dt;
360 cmd->dt=dt; }
361 else if(timing=='a') {
362 cmd->on=cmd->off=tmax+dt;
363 cmd->dt=dt; }
364 else if(timing=='@') {
365 cmd->dt=dt; }
366 else if(timing=='i') {
367 if(cmd->on<tmin) cmd->on=tmin;
368 if(cmd->off>tmax) cmd->off=tmax; }
369 else if(timing=='x') {
370 if(cmd->on<tmin) cmd->on=tmin;
371 if(cmd->off>tmax) cmd->off=tmax; }
372 else if(timing=='B') {
373 cmd->oni=cmd->offi=iter-1;
374 cmd->dti=1; }
375 else if(timing=='A') {
376 cmd->oni=cmd->offi=iter+(Q_LONGLONG)((tmax-tmin)/dt+0.5)+1;
377 cmd->dti=1; }
378 else if(timing=='&');
379 else if(strchr("Ij",timing)) {
380 if(cmd->oni<0) cmd->oni=iter+1; }
381 else if(strchr("Ee",timing)) {
382 cmd->oni=iter;
383 cmd->offi=iter+(Q_LONGLONG)((tmax-tmin)/dt+0.5);
384 cmd->dti=1; }
385 else if(strchr("Nn",timing)) {
386 cmd->oni=iter;
387 cmd->offi=iter+(Q_LONGLONG)((tmax-tmin)/dt+0.5); }
388
389 return; }
390
391
392 /* scmdupdatecommands */
scmdupdatecommands(cmdssptr cmds,double tmin,double tmax,double dt)393 int scmdupdatecommands(cmdssptr cmds,double tmin,double tmax,double dt) {
394 int i;
395 char timing;
396 cmdptr cmd,cmdtemplate;
397 void *voidptr;
398
399 if(!cmds || !cmds->cmdlist) return 0;
400 if(cmds->condition==3) return 0; // already ready to simulate
401 if(dt<=0 || tmax<tmin) return 0; // can't assign times to commands
402
403 if(cmds->condition==0) {
404 if(cmds->cmd) {
405 while(q_pop(cmds->cmd,NULL,NULL,NULL,NULL,&voidptr)>=0) {
406 cmd=(cmdptr)voidptr;
407 scmdfree(cmd); }}
408 if(cmds->cmdi) {
409 while(q_pop(cmds->cmdi,NULL,NULL,NULL,NULL,&voidptr)>=0) {
410 cmd=(cmdptr)voidptr;
411 scmdfree(cmd); }}
412 for(i=0;i<cmds->ncmdlist;i++) {
413 cmdtemplate=cmds->cmdlist[i];
414 cmdtemplate->twin=NULL;
415 cmdtemplate->invoke=0; }}
416
417 for(i=0;i<cmds->ncmdlist;i++) {
418 cmdtemplate=cmds->cmdlist[i];
419 if(!cmdtemplate->twin && cmdtemplate->invoke==0) {
420 cmd=scmdalloc();
421 if(!cmd) return 1;
422 scmdcopycommand(cmdtemplate,cmd);
423 scmddocommandtiming(cmd,tmin,tmax,dt,cmds->iter);
424 timing=cmd->timing;
425 if(strchr("ba@ix",timing)) {
426 if(!cmds->cmd)
427 if(scmdqalloc(cmds,10)==1) {scmdfree(cmd);return 1;}
428 if(q_insert(NULL,0,cmd->on,0,(void*)cmd,cmds->cmd)==1)
429 if(q_expand(cmds->cmd,q_length(cmds->cmd))) {scmdfree(cmd);return 1; }}
430 else if(strchr("BA&jIENen",timing)) {
431 if(!cmds->cmdi)
432 if(scmdqalloci(cmds,10)==1) {scmdfree(cmd);return 1;}
433 if(q_insert(NULL,0,0,cmd->oni,(void*)cmd,cmds->cmdi)==1)
434 if(q_expand(cmds->cmdi,q_length(cmds->cmdi))) {scmdfree(cmd);return 1; }}
435 else {
436 scmdfree(cmd);
437 return 6; }
438 cmdtemplate->twin=cmd;
439 cmdtemplate->invoke=1; }
440 else if(cmds->condition==1) {
441 cmd=cmdtemplate->twin;
442 if(cmd)
443 scmddocommandtiming(cmd,tmin,tmax,dt,cmds->iter); }}
444
445 scmdsetcondition(cmds,3,1);
446 return 0; }
447
448
449 /* scmdpop */
scmdpop(cmdssptr cmds,double t)450 void scmdpop(cmdssptr cmds,double t) {
451 cmdptr cmd;
452 void *voidptr;
453
454 if(!cmds || !cmds->cmd) return;
455 while(q_length(cmds->cmd)>0 && q_frontkeyD(cmds->cmd)<=t) {
456 q_pop(cmds->cmd,NULL,NULL,NULL,NULL,&voidptr);
457 cmd=(cmdptr)voidptr;
458 cmd->twin->twin=NULL;
459 scmdfree(cmd); }
460 return; }
461
462
463 /* scmdexecute */
scmdexecute(cmdssptr cmds,double time,double simdt,Q_LONGLONG iter,int donow)464 enum CMDcode scmdexecute(cmdssptr cmds,double time,double simdt,Q_LONGLONG iter,int donow) {
465 enum CMDcode code1,code2;
466 cmdptr cmd;
467 double dt;
468 void *voidptr,*simvd;
469
470 if(!cmds) return CMDok;
471 code2=CMDok;
472 if(iter<0) iter=cmds->iter++;
473 else cmds->iter=iter;
474 simvd=cmds->simvd;
475
476 if(cmds->cmdi) // integer execution times
477 while((q_length(cmds->cmdi)>0) && (q_frontkeyL(cmds->cmdi)<=iter || donow)) {
478 q_pop(cmds->cmdi,NULL,NULL,NULL,NULL,&voidptr);
479 cmd=(cmdptr)voidptr;
480 cmd->invoke++;
481 code1=(*cmds->cmdfn)(cmds->simvd,cmd,cmd->str);
482 if(code1==CMDwarn) {
483 if(strlen(cmd->erstr)) SCMDPRINTF(simvd,7,"command '%s' error: %s\n",cmd->str,cmd->erstr);
484 else SCMDPRINTF(simvd,7,"error with command: '%s'\n",cmd->str); }
485 if(cmd->oni+cmd->dti<=cmd->offi && !donow && (code1==CMDok || code1==CMDpause)) {
486 cmd->oni+=cmd->dti;
487 q_insert(NULL,0,0,cmd->oni,(void*)cmd,cmds->cmdi); }
488 else {
489 cmd->twin->twin=NULL;
490 scmdfree(cmd); }
491 if(code1==CMDabort) return code1;
492 if(code1>code2) code2=code1; }
493
494 if(cmds->cmd) // float execution times
495 while((q_length(cmds->cmd)>0) && (q_frontkeyD(cmds->cmd)<=time || donow)) {
496 q_pop(cmds->cmd,NULL,NULL,NULL,NULL,&voidptr);
497 cmd=(cmdptr)voidptr;
498 cmd->invoke++;
499 code1=(*cmds->cmdfn)(cmds->simvd,cmd,cmd->str);
500 if(code1==CMDwarn) {
501 if(strlen(cmd->erstr)) SCMDPRINTF(simvd,7,"command '%s' error: %s\n",cmd->str,cmd->erstr);
502 else SCMDPRINTF(simvd,7,"error with command: '%s'\n",cmd->str); }
503 dt=(cmd->dt>=simdt)?cmd->dt:simdt;
504 if(cmd->on+dt<=cmd->off && !donow && (code1==CMDok || code1==CMDpause)) {
505 cmd->on+=dt;
506 if(cmd->xt>1) cmd->dt*=cmd->xt;
507 q_insert(NULL,0,cmd->on,0,(void*)cmd,cmds->cmd); }
508 else {
509 cmd->twin->twin=NULL;
510 scmdfree(cmd); }
511 if(code1==CMDabort) return code1;
512 if(code1>code2) code2=code1; }
513
514 return code2; }
515
516
517 /* scmdcmdtype */
scmdcmdtype(cmdssptr cmds,cmdptr cmd)518 enum CMDcode scmdcmdtype(cmdssptr cmds,cmdptr cmd) {
519 char string[STRCHAR];
520
521 sscanf(cmd->str,"%s",string);
522 strncat(string," cmdtype",STRCHAR-strlen(string));
523 return (*cmds->cmdfn)(cmds->simvd,cmd,string); }
524
525
526 /* scmdoutput */
scmdoutput(cmdssptr cmds)527 void scmdoutput(cmdssptr cmds) {
528 int fid,i,did;
529 queue cmdq;
530 cmdptr cmd;
531 void *voidptr,*simvd;
532 char timing,string[STRCHAR],string2[STRCHAR];
533
534 simvd=cmds?cmds->simvd:NULL;
535 SCMDPRINTF(simvd,2,"RUNTIME COMMAND INTERPRETER\n");
536 if(!cmds) {
537 SCMDPRINTF(simvd,2," No command superstructure defined\n\n");
538 return; }
539 if(!cmds->cmdfn) SCMDPRINTF(simvd,10," ERROR: Command executer undefined");
540 if(!cmds->simvd) SCMDPRINTF(simvd,10," WARNING: No argument for command executer");
541 if(cmds->iter) SCMDPRINTF(simvd,2," Commands iteration counter: %i\n",cmds->iter);
542 if(cmds->nfile) {
543 SCMDPRINTF(simvd,2," Output file root: '%s%s'\n",cmds->root,cmds->froot);
544 SCMDPRINTF(simvd,2," Output file paths and names:\n"); }
545 else
546 SCMDPRINTF(simvd,2," No output files\n");
547 for(fid=0;fid<cmds->nfile;fid++) {
548 if(!strcmp(cmds->fname[fid],"stdout") || !strcmp(cmds->fname[fid],"stderr"))
549 SCMDPRINTF(simvd,2," %s (file open): %s\n",cmds->fname[fid],cmds->fname[fid]);
550 else {
551 scmdcatfname(cmds,fid,string);
552 SCMDPRINTF(simvd,2," %s (file %s): %s\n",cmds->fname[fid],cmds->fptr[fid]?"open":"closed",string); }}
553 if(cmds->ndata) {
554 SCMDPRINTF(simvd,2," Output data table names:\n"); }
555 else
556 SCMDPRINTF(simvd,2," No output data tables\n");
557 for(did=0;did<cmds->ndata;did++)
558 SCMDPRINTF(simvd,2," %s\n",cmds->dname[did]);
559
560 if(!cmds->cmdlist || cmds->ncmdlist==0)
561 SCMDPRINTF(simvd,2," No commands\n");
562 else {
563 SCMDPRINTF(simvd,2," Commands:\n");
564 for(i=0;i<cmds->ncmdlist;i++) {
565 cmd=cmds->cmdlist[i];
566 timing=cmd->timing;
567 SCMDPRINTF(simvd,2," %c",timing);
568 if(strchr("baBAEe",timing));
569 else if(strchr("@",timing))
570 SCMDPRINTF(simvd,2," time: %g",cmd->on);
571 else if(strchr("&",timing))
572 SCMDPRINTF(simvd,2," iteration: %i",cmd->oni);
573 else if(strchr("Nn",timing))
574 SCMDPRINTF(simvd,2," every: %i",cmd->dti);
575 else if(strchr("i",timing))
576 SCMDPRINTF(simvd,2," from: %g to: %g step: %g",cmd->on,cmd->off,cmd->dt);
577 else if(strchr("Ij",timing))
578 SCMDPRINTF(simvd,2," from: %i to: %i step: %i",cmd->oni,cmd->offi,cmd->dti);
579 else if(strchr("x",timing))
580 SCMDPRINTF(simvd,2," from: %g to: %g step: %g mult: %g",cmd->on,cmd->off,cmd->dt,cmd->xt);
581 SCMDPRINTF(simvd,2," '%s' (%s)\n",cmd->str,scmdcode2string(scmdcmdtype(cmds,cmd),string)); }}
582
583 cmdq=cmds->cmd;
584 if(cmdq) {
585 SCMDPRINTF(simvd,1," Time queue:\n");
586 SCMDPRINTF(simvd,1," %i queue spaces used of %i total\n",q_length(cmdq),q_maxlength(cmdq)-1);
587 SCMDPRINTF(simvd,1," Times to start, stop, and step, strings, and command type:\n");
588 i=-1;
589 while((i=q_next(i,NULL,NULL,NULL,NULL,&voidptr,cmdq))>=0) {
590 cmd=(cmdptr)voidptr;
591 SCMDPRINTF(simvd,1," %g %g%s%g '%s' (%s)\n",cmd->on,cmd->off,cmd->xt>1?" *":" ",cmd->xt>1?cmd->xt:cmd->dt,cmd->str,scmdcode2string(scmdcmdtype(cmds,cmd),string)); }}
592 cmdq=cmds->cmdi;
593 if(cmdq) {
594 SCMDPRINTF(simvd,1," Integer queue:\n");
595 SCMDPRINTF(simvd,1," %i queue spaces used of %i total\n",q_length(cmdq),q_maxlength(cmdq)-1);
596 SCMDPRINTF(simvd,1," Iterations to start, stop, and step, strings, and command type:\n");
597 i=-1;
598 while((i=q_next(i,NULL,NULL,NULL,NULL,&voidptr,cmdq))>=0) {
599 cmd=(cmdptr)voidptr;
600 if(cmd->offi!=Q_LLONG_MAX) {
601 snprintf(string2,STRCHAR," %s %s %s '%%s' (%%s)\n",Q_LLI,Q_LLI,Q_LLI);
602 SCMDPRINTF(simvd,1,string2,cmd->oni,cmd->offi,cmd->dti,cmd->str,scmdcode2string(scmdcmdtype(cmds,cmd),string)); }
603 else {
604 snprintf(string2,STRCHAR," %s end %s '%%s' (%%s)\n",Q_LLI,Q_LLI);
605 SCMDPRINTF(simvd,1,string2,cmd->oni,cmd->dti,cmd->str,scmdcode2string(scmdcmdtype(cmds,cmd),string)); }}}
606
607 SCMDPRINTF(simvd,2,"\n");
608 return; }
609
610
611 /* scmdwritecommands */
scmdwritecommands(cmdssptr cmds,FILE * fptr,char * filename)612 void scmdwritecommands(cmdssptr cmds,FILE *fptr,char *filename) {
613 int i,fid,did;
614 cmdptr cmd;
615 char timing;
616
617 if(!fptr) return;
618 fprintf(fptr,"# Command parameters\n");
619 if(strlen(cmds->froot)) fprintf(fptr,"output_root %s\n",cmds->froot);
620
621 if(!(cmds->nfile==1 && !strcmp(cmds->fname[0],filename))) {
622 if(cmds->nfile) {
623 fprintf(fptr,"output_files");
624 for(fid=0;fid<cmds->nfile;fid++)
625 if(!filename || strcmp(cmds->fname[fid],filename))
626 fprintf(fptr," %s",cmds->fname[fid]);
627 fprintf(fptr,"\n"); }
628 for(fid=0;fid<cmds->nfile;fid++)
629 if(cmds->fsuffix[fid])
630 fprintf(fptr,"output_file_number %s %i\n",cmds->fname[fid],cmds->fsuffix[fid]); }
631
632 if(cmds->ndata) {
633 fprintf(fptr,"output_data");
634 for(did=0;did<cmds->ndata;did++)
635 fprintf(fptr," %s",cmds->dname[did]);
636 fprintf(fptr,"\n"); }
637
638 for(i=0;i<cmds->ncmdlist;i++) {
639 cmd=cmds->cmdlist[i];
640 timing=cmd->timing;
641 fprintf(fptr,"cmd %c",timing);
642 if(strchr("baBAEe",timing));
643 else if(strchr("@&",timing))
644 fprintf(fptr," %g",cmd->on);
645 else if(strchr("Nn",timing))
646 fprintf(fptr," %g",cmd->dt);
647 else if(strchr("iIj",timing))
648 fprintf(fptr," %g %g %g",cmd->on,cmd->off,cmd->dt);
649 else if(strchr("x",timing))
650 fprintf(fptr," %g %g %g %g",cmd->on,cmd->off,cmd->dt,cmd->xt);
651 fprintf(fptr," %s\n",cmd->str); }
652
653 fprintf(fptr,"\n");
654 return; }
655
656
657 /* scmdsetflag */
scmdsetflag(cmdssptr cmds,double flag)658 void scmdsetflag(cmdssptr cmds,double flag) {
659 cmds->flag=flag;
660 return; }
661
662
663 /* scmdreadflag */
scmdreadflag(cmdssptr cmds)664 double scmdreadflag(cmdssptr cmds) {
665 return cmds->flag; }
666
667
668 /* scmdsetprecision */
scmdsetprecision(cmdssptr cmds,int precision)669 void scmdsetprecision(cmdssptr cmds,int precision) {
670 cmds->precision=precision;
671 return; }
672
673
674 /* scmdsetoutputformat */
scmdsetoutputformat(cmdssptr cmds,char * format)675 int scmdsetoutputformat(cmdssptr cmds,char *format) {
676 if(!strcmp(format,"ssv") || !strcmp(format,"SSV")) cmds->outformat='s';
677 else if(!strcmp(format,"csv") || !strcmp(format,"CSV")) cmds->outformat='c';
678 else return 1;
679 return 0; }
680
681
682 /*********** Data functions *************/
683
684 /* scmdsetdnames */
scmdsetdnames(cmdssptr cmds,char * str)685 int scmdsetdnames(cmdssptr cmds,char *str) {
686 int did,itct,n,newmaxdata;
687 char **newdname;
688 listptrdd *newdata;
689
690 if(!cmds) return 4;
691 n=wordcount(str);
692
693 if(cmds->ndata+n > cmds->maxdata) {
694 newmaxdata=cmds->maxdata+n;
695
696 newdname=(char**) calloc(newmaxdata,sizeof(char*));
697 if(!newdname) return 1;
698 for(did=0;did<cmds->maxdata;did++)
699 newdname[did]=cmds->dname[did];
700 for(;did<newmaxdata;did++)
701 newdname[did]=NULL;
702 for(did=cmds->maxdata;did<newmaxdata;did++) {
703 newdname[did]=EmptyString();
704 if(!newdname[did]) return 1; }
705
706 newdata=(listptrdd*) calloc(newmaxdata,sizeof(listptrdd));
707 if(!newdata) return 1;
708 for(did=0;did<cmds->maxdata;did++)
709 newdata[did]=cmds->data[did];
710 for(;did<newmaxdata;did++)
711 newdata[did]=NULL;
712
713 cmds->maxdata=newmaxdata;
714 free(cmds->dname);
715 cmds->dname=newdname;
716 free(cmds->data);
717 cmds->data=newdata; }
718
719 while(str) {
720 did=cmds->ndata;
721 itct=sscanf(str,"%s",cmds->dname[did]);
722 if(itct!=1) return 2;
723 if(cmds->data[did])
724 ListClearDD(cmds->data[did]);
725 cmds->ndata++;
726 str=strnword(str,2); }
727
728 return 0; }
729
730
731 /* scmdappenddata */
scmdappenddata(cmdssptr cmds,int dataid,int newrow,int narg,...)732 void scmdappenddata(cmdssptr cmds,int dataid,int newrow, int narg, ...) {
733 va_list arguments;
734 listptrdd data;
735
736 if(dataid<0) return;
737 va_start(arguments,narg);
738 data=ListAppendItemsDDv(cmds->data[dataid],newrow,narg,arguments);
739 va_end(arguments);
740 if(data) cmds->data[dataid]=data;
741 return; }
742
743
744 /************** file functions **************/
745
746 /* scmdsetfroot */
scmdsetfroot(cmdssptr cmds,const char * root)747 int scmdsetfroot(cmdssptr cmds,const char *root) {
748 if(!cmds || !root) return 1;
749 strncpy(cmds->froot,root,STRCHAR-1);
750 cmds->froot[STRCHAR-1]='\0';
751 return 0; }
752
753
754 /* scmdsetfnames */
scmdsetfnames(cmdssptr cmds,char * str,int append)755 int scmdsetfnames(cmdssptr cmds,char *str,int append) {
756 int fid,itct,n,newmaxfile,*newfsuffix,*newfappend;
757 char **newfname;
758 FILE **newfptr;
759
760 if(!cmds) return 4;
761 n=wordcount(str);
762
763 if(cmds->nfile+n>cmds->maxfile) {
764 newmaxfile=cmds->maxfile+n;
765
766 newfname=(char**)calloc(newmaxfile,sizeof(char*));
767 if(!newfname) return 1;
768 for(fid=0;fid<cmds->maxfile;fid++)
769 newfname[fid]=cmds->fname[fid];
770 for(;fid<newmaxfile;fid++)
771 newfname[fid]=NULL;
772 for(fid=cmds->maxfile;fid<newmaxfile;fid++) {
773 newfname[fid]=EmptyString();
774 if(!newfname[fid]) return 1; }
775
776 newfsuffix=(int*)calloc(newmaxfile,sizeof(int));
777 if(!newfsuffix) return 1;
778 for(fid=0;fid<cmds->maxfile;fid++)
779 newfsuffix[fid]=cmds->fsuffix[fid];
780 for(;fid<newmaxfile;fid++)
781 newfsuffix[fid]=0;
782
783 newfappend=(int*)calloc(newmaxfile,sizeof(int));
784 if(!newfappend) return 1;
785 for(fid=0;fid<cmds->maxfile;fid++)
786 newfappend[fid]=cmds->fappend[fid];
787 for(;fid<newmaxfile;fid++)
788 newfappend[fid]=0;
789
790 newfptr=(FILE **)calloc(newmaxfile,sizeof(FILE*));
791 if(!newfptr) return 1;
792 for(fid=0;fid<cmds->maxfile;fid++)
793 newfptr[fid]=cmds->fptr[fid];
794 for(;fid<newmaxfile;fid++)
795 newfptr[fid]=NULL;
796
797 cmds->maxfile=newmaxfile;
798 free(cmds->fname);
799 cmds->fname=newfname;
800 free(cmds->fsuffix);
801 cmds->fsuffix=newfsuffix;
802 free(cmds->fappend);
803 cmds->fappend=newfappend;
804 free(cmds->fptr);
805 cmds->fptr=newfptr; }
806
807 while(str) {
808 fid=cmds->nfile;
809 itct=sscanf(str,"%s",cmds->fname[fid]);
810 if(itct!=1) return 2;
811 cmds->fappend[fid]=append;
812 cmds->nfile++;
813 str=strnword(str,2); }
814
815 return 0; }
816
817
818 /* scmdsetfsuffix */
scmdsetfsuffix(cmdssptr cmds,const char * fname,int i)819 int scmdsetfsuffix(cmdssptr cmds,const char *fname,int i) {
820 int fid;
821
822 if(!cmds || !cmds->nfile) return 1;
823 fid=stringfind(cmds->fname,cmds->nfile,fname);
824 if(fid<0) return 1;
825 cmds->fsuffix[fid]=i;
826 return 0; }
827
828
829 /* scmdopenfiles */
scmdopenfiles(cmdssptr cmds,int overwrite)830 int scmdopenfiles(cmdssptr cmds,int overwrite) {
831 int fid;
832 char str1[STRCHAR];
833 FILE *fptr;
834
835 if(!cmds) return 0;
836 for(fid=0;fid<cmds->nfile;fid++) {
837 if(cmds->fptr[fid] && strcmp(cmds->fname[fid],"stdout") && strcmp(cmds->fname[fid],"stderr"))
838 fclose(cmds->fptr[fid]);
839 cmds->fptr[fid]=NULL; }
840
841 for(fid=0;fid<cmds->nfile;fid++) {
842 if(!strcmp(cmds->fname[fid],"stdout")) cmds->fptr[fid]=stdout;
843 else if(!strcmp(cmds->fname[fid],"stderr")) cmds->fptr[fid]=stderr;
844 else {
845 scmdcatfname(cmds,fid,str1);
846 if(!cmds->fappend[fid] && !overwrite) {
847 fptr=fopen(str1,"r");
848 if(fptr) {
849 fclose(fptr);
850 #ifdef COMPILE_AS_PY_MODULE
851 // When built as a python module. There is no way to use scanf (probably)?
852 fprintf(stderr, "File '%s' already exists. Refusing to overwrite.\n", cmds->fname[fid]);
853 fprintf(stderr, "Tip: pass `overwrite=True` to `run()` method.\n");
854 return 1;
855 #else
856 // When compiled for c++ binary.
857 char str2[STRCHAR];
858 fprintf(stderr,"Overwrite existing output file '%s' (y/n)? ",cmds->fname[fid]);
859 scanf("%s",str2);
860 if(!(str2[0]=='y' || str2[0]=='Y')) return 1;
861 #endif
862 }}
863 if(cmds->fappend[fid]) cmds->fptr[fid]=fopen(str1,"a");
864 else cmds->fptr[fid]=fopen(str1,"w");
865 if(!cmds->fptr[fid]) {
866 SCMDPRINTF(cmds->simvd,7,"Failed to open file '%s' for writing\n",cmds->fname[fid]);
867 return 1; }}}
868
869 return 0; }
870
871
872 /* scmdoverwrite */
scmdoverwrite(cmdssptr cmds,char * line2)873 int scmdoverwrite(cmdssptr cmds,char *line2) {
874 int itct,fid;
875 static char fname[STRCHAR],str1[STRCHAR];
876
877 if(!line2) return 0;
878 itct=sscanf(line2,"%s",fname);
879 if(itct!=1) return 0;
880 if(!strcmp(fname,"stdout") || !strcmp(fname,"stderr")) return 0;
881
882 fid=stringfind(cmds->fname,cmds->nfile,fname);
883 if(fid<0) return 1;
884 fclose(cmds->fptr[fid]);
885 scmdcatfname(cmds,fid,str1);
886 cmds->fptr[fid]=fopen(str1,"w");
887 if(!cmds->fptr[fid]) return 2;
888 return 0; }
889
890
891 /* scmdincfile */
scmdincfile(cmdssptr cmds,char * line2)892 int scmdincfile(cmdssptr cmds,char *line2) {
893 int itct,fid;
894 static char fname[STRCHAR],str1[STRCHAR];
895
896 if(!line2) return 0;
897 itct=sscanf(line2,"%s",fname);
898 if(itct!=1) return 0;
899 if(!strcmp(fname,"stdout") || !strcmp(fname,"stderr")) return 0;
900
901 fid=stringfind(cmds->fname,cmds->nfile,fname);
902 if(fid<0) return 1;
903 fclose(cmds->fptr[fid]);
904 cmds->fsuffix[fid]++;
905 scmdcatfname(cmds,fid,str1);
906 if(cmds->fappend[fid])
907 cmds->fptr[fid]=fopen(str1,"a");
908 else
909 cmds->fptr[fid]=fopen(str1,"w");
910 if(!cmds->fptr[fid]) return 2;
911 return 0; }
912
913
914 /* scmdgetfptr */
scmdgetfptr(cmdssptr cmds,char * line2,int outstyle,FILE ** fptrptr,int * dataidptr)915 int scmdgetfptr(cmdssptr cmds,char *line2,int outstyle,FILE **fptrptr,int *dataidptr) {
916 int itct,fid,did;
917 char name[STRCHAR];
918
919 if(fptrptr) *fptrptr=NULL;
920 if(dataidptr) *dataidptr=-1;
921 if(outstyle==0) return 0;
922
923 fid=did=-1;
924 itct=0;
925 if(line2)
926 itct=sscanf(line2,"%s",name);
927 if(itct!=1) *fptrptr=stdout;
928 else if(fptrptr && !strcmp(name,"stdout")) *fptrptr=stdout;
929 else if(fptrptr && !strcmp(name,"stderr")) *fptrptr=stderr;
930 else {
931 if(fptrptr) fid=stringfind(cmds->fname,cmds->nfile,name);
932 if(dataidptr) did=stringfind(cmds->dname,cmds->ndata,name); }
933 if(fptrptr && *fptrptr && outstyle&1);
934 else if(fid>=0 && outstyle&1) *fptrptr=cmds->fptr[fid];
935 else if(did>=0 && outstyle&2) *dataidptr=did;
936 else return -1;
937 if(outstyle<3) return 1;
938
939 line2=strnword(line2,2);
940 if(!line2) return 1;
941
942 fid=did=-1;
943 itct=sscanf(line2,"%s",name);
944 if(itct!=1) return 1;
945 else if(fptrptr && !(*fptrptr) && !strcmp(name,"stdout")) *fptrptr=stdout;
946 else if(fptrptr && !(*fptrptr) && !strcmp(name,"stderr")) *fptrptr=stderr;
947 else if(fptrptr && !(*fptrptr)) fid=stringfind(cmds->fname,cmds->nfile,name);
948 else if(dataidptr && *dataidptr<0) did=stringfind(cmds->dname,cmds->ndata,name);
949 if(fid>=0) *fptrptr=cmds->fptr[fid];
950 else if(did>=0) *dataidptr=did;
951 else return 1; // got 1 good answer, word 2 doesn't make sense
952
953 return 2; }
954
955
956 /* scmdfprintf */
scmdfprintf(cmdssptr cmds,FILE * fptr,const char * format,...)957 int scmdfprintf(cmdssptr cmds,FILE *fptr,const char *format,...) {
958 char message[STRCHARLONG],newformat[STRCHAR],replacestr[STRCHAR];
959 va_list arguments;
960 int code;
961
962 if(!fptr) return 0;
963 strncpy(newformat,format,STRCHAR-1);
964 newformat[STRCHAR-1]='\0';
965 if(!cmds) {
966 strstrreplace(newformat,"%,"," ",STRCHAR); }
967 else {
968 if(cmds->precision>=0) {
969 snprintf(replacestr,STRCHAR,"%%.%ig",cmds->precision);
970 strstrreplace(newformat,"%g",replacestr,STRCHAR); }
971 if(cmds->outformat=='c')
972 strstrreplace(newformat,"%,",",",STRCHAR);
973 else
974 strstrreplace(newformat,"%,"," ",STRCHAR); }
975 va_start(arguments,format);
976 vsnprintf(message,STRCHARLONG,newformat,arguments);
977 va_end(arguments);
978 code=fprintf(fptr,"%s",message);
979 return code; }
980
981
982 /* scmdflush */
scmdflush(FILE * fptr)983 void scmdflush(FILE *fptr) {
984 if(fptr) fflush(fptr);
985 return; }
986
987