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