1 /* Steven Andrews, 1/99
2 See documentation called string2_doc.doc
3 Copyright 2003-2007 by Steven Andrews.	This work is distributed under the terms
4 of the Gnu Lesser General Public License (LGPL). */
5 
6 #include <math.h>
7 #include <string.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include "random2.h"
13 #include "string2.h"
14 
15 #ifndef NAN			// NAN is defined in math.h, so should already be defined
16 	#include <limits>
17 	#define NAN std::numeric_limits<double>::quiet_NaN( );
18 #endif
19 
20 
21 /******************************************************************/
22 /************ Declarations for internal functions ****************/
23 /******************************************************************/
24 
25 #define CHECKS(A,...)		if(!(A)) {snprintf(StrErrorString,sizeof(StrErrorString),__VA_ARGS__); goto failure;} else (void)0
26 #define CHECK(A)		if(!(A)) {goto failure;} else (void)0
27 
28 char StrErrorString[STRCHAR];
29 int MathParseError=0;
30 
31 int permutelex(int *seq,int n);
32 int allocresults(char ***resultsptr,int *maxrptr,int nchar);
33 
34 
35 /******************************************************************/
36 /******************* String classification ************************/
37 /******************************************************************/
38 
39 
40 /* strisnumber */
strisnumber(const char * str)41 int strisnumber(const char *str) {
42   char *ptr;
43   int len;
44 
45   if(!(len=strlen(str))) return 0;
46   strtod(str,&ptr);
47   return(ptr==str+len); }
48 
49 
50 /* strokname */
strokname(const char * name)51 int strokname(const char *name) {
52 	int ok;
53 
54 	ok=1;
55 	ok=ok && isalpha(*name);
56 	for(name++;*name && ok;name++)
57 		ok=ok && (isalnum(*name) || *name=='_');
58 	return ok; }
59 
60 
61 /* strhasname */
strhasname(const char * string,const char * name)62 int strhasname(const char *string,const char *name) {
63 	int i,len;
64 
65 	len=strlen(name);
66 	while(*string) {
67 		for(i=0;i<len && *string==name[i];i++) string++;
68 		if(i==len && (*string=='\0' || !(isalnum(*string) || *string=='_'))) return 1;
69 		while(*string && (*string!=name[0] || isalnum(*(string-1)) || *(string-1)=='_')) string++; }
70 	return 0; }
71 
72 
73 /* strbegin */
strbegin(const char * strshort,const char * strlong,int casesensitive)74 int strbegin(const char *strshort,const char *strlong,int casesensitive) {
75 	int i;
76 
77 	i=0;
78 	if(casesensitive)
79 		while(strshort[i]!='\0' && strshort[i]==strlong[i]) i++;
80 	else
81 		while(strshort[i]!='\0' && tolower(strshort[i])==tolower(strlong[i])) i++;
82 	if(strshort[i]=='\0' && i>0) return 1;
83 	return 0; }
84 
85 
86 /* strsymbolcount */
strsymbolcount(const char * s,char c)87 int strsymbolcount(const char *s,char c) {
88 	int n;
89 
90 	n=0;
91 	for(;*s;s++)
92 		if(*s==c) n++;
93 	return n; }
94 
95 
96 /* strisfunctionform */
strisfunctionform(char * str,char ** parenptr)97 int strisfunctionform(char *str,char **parenptr) {
98 	int i,len,ok;
99 	char *ptr;
100 
101 	len=strlen(str);
102 	if(len<3 || str[len-1]!=')') return 0;
103 	if(!(ptr=strchr(str+1,'('))) return 0;
104 
105 	len=ptr-str;
106 	ok=isalpha(*str);
107 	for(i=1;i<len && ok;i++)
108 		ok=isalnum(str[i]) || str[i]=='_';
109 	if(parenptr) *parenptr=ptr;
110 	return ok; }
111 
112 
113 /******************************************************************/
114 /*********************** Character locating ***********************/
115 /******************************************************************/
116 
117 
118 /* strchrindex */
strchrindex(const char * cs,char c,int i)119 int strchrindex(const char *cs,char c,int i) {
120   while(cs[i]!='\0') {
121     if(cs[i]==c) return i;
122     i++; }
123   return -1; }
124 
125 
126 /* strrpbrk */
strrpbrk(char * cs,const char * ct)127 char *strrpbrk(char *cs,const char *ct) {
128 	char *s2;
129 	int i,n;
130 
131 	if(!cs || !ct) return NULL;
132 	n=strlen(ct);
133 	for(s2=cs+strlen(cs)-1;s2>=cs;s2--)
134 		for(i=0;i<n;i++)
135 			if(*s2==ct[i]) return s2;
136 	return NULL; }
137 
138 
139 /* StrChrQuote */
StrChrQuote(char * cs,char c)140 char *StrChrQuote(char *cs,char c) {
141 	int qt;
142 
143 	qt=0;
144 	for(;*cs;cs++)	{
145 		if(*cs=='"')	qt=!qt;
146 		else if(*cs==c && !qt) return cs;	}
147 	return NULL; }
148 
149 
150 /* StrChrPQuote */
StrChrPQuote(const char * cs,char c)151 int StrChrPQuote(const char *cs,char c) {
152 	unsigned int i,pn,qt;
153 
154 	pn=qt=0;
155 	for(i=0;i<strlen(cs);i++)
156 		if(cs[i]=='"') qt=!qt;
157 		else if(cs[i]==c && !qt && !pn) return i;
158 		else if(cs[i]=='(' && !qt) pn++;
159 		else if(cs[i]==')' && !qt && pn) pn--;
160 		else if(cs[i]==')' && !qt) return -2;
161 	if(pn) return -2;
162 	if(qt) return -3;
163 	return -1; }
164 
165 
166 /* StrrChrPQuote */
StrrChrPQuote(const char * cs,char c)167 int StrrChrPQuote(const char *cs,char c) {
168 	int i,pn,qt;
169 
170 	pn=qt=0;
171 	for(i=strlen(cs)-1;i>=0;i--)
172 		if(cs[i]=='"') qt=!qt;
173 		else if(cs[i]==c && !qt && !pn) return i;
174 		else if(cs[i]==')' && !qt) pn++;
175 		else if(cs[i]=='(' && !qt && pn) pn--;
176 		else if(cs[i]=='(' && !qt) return -2;
177 	if(pn) return -2;
178 	if(qt) return -3;
179 	return -1; }
180 
181 
182 /* strChrBrackets */
strChrBrackets(const char * string,int n,char c,const char * delimit)183 int strChrBrackets(const char *string,int n,char c,const char *delimit) {
184 	int i,pn,bk,bc,qt,sq,ckpn,ckbk,ckbc,ckqt,cksq;
185 	char ch;
186 
187 	pn=bk=bc=qt=sq=0;
188 	ckpn=strchr(delimit,'(')?1:0;
189 	ckbk=strchr(delimit,'[')?1:0;
190 	ckbc=strchr(delimit,'{')?1:0;
191 	ckqt=strchr(delimit,'"')?1:0;
192 	cksq=strchr(delimit,'\'')?1:0;
193 	if(n<0)
194 		n=strlen(string);
195 
196 	for(i=0;i<n;i++) {
197 		ch=string[i];
198 		if(ch==c) {
199 			if(pn!=0 || bk!=0 || bc!=0 || qt!=0 || sq!=0);
200 			else return i; }
201 		else if(ckpn && ch=='(') pn++;
202 		else if(ckbk && ch=='[') bk++;
203 		else if(ckbc && ch=='{') bc++;
204 		else if(ckqt && ch=='"') qt=!qt;
205 		else if(cksq && ch=='\'') sq=!sq;
206 		else if(ckpn && ch==')') {if(--pn<0) return -2;}
207 		else if(ckbk && ch==']') {if(--bk<0) return -3;}
208 		else if(ckbc && ch=='}') {if(--bc<0) return -4;} }
209 
210 	return -1; }
211 
212 
213 /* strPbrkBrackets */
strPbrkBrackets(const char * string,int n,const char * c,const char * delimit,int imin)214 int strPbrkBrackets(const char *string,int n,const char *c,const char *delimit,int imin) {
215 	int i,pn,bk,bc,qt,sq,ckpn,ckbk,ckbc,ckqt,cksq;
216 	char ch;
217 
218 	pn=bk=bc=qt=sq=0;
219 	ckpn=strchr(delimit,'(')?1:0;
220 	ckbk=strchr(delimit,'[')?1:0;
221 	ckbc=strchr(delimit,'{')?1:0;
222 	ckqt=strchr(delimit,'"')?1:0;
223 	cksq=strchr(delimit,'\'')?1:0;
224 	if(n<0)
225 		n=strlen(string);
226 
227 	for(i=0;i<n;i++) {
228 		ch=string[i];
229 		if(strchr(c,ch)) {
230 			if(pn!=0 || bk!=0 || bc!=0 || qt!=0 || sq!=0 || i<imin);
231 			else return i; }
232 		else if(ckpn && ch=='(') pn++;
233 		else if(ckbk && ch=='[') bk++;
234 		else if(ckbc && ch=='{') bc++;
235 		else if(ckqt && ch=='"') qt=!qt;
236 		else if(cksq && ch=='\'') sq=!sq;
237 		else if(ckpn && ch==')') {if(--pn<0) return -2;}
238 		else if(ckbk && ch==']') {if(--bk<0) return -3;}
239 		else if(ckbc && ch=='}') {if(--bc<0) return -4;} }
240 
241 	return -1; }
242 
243 
244 /* strparenmatch */
strparenmatch(const char * string,int index)245 int strparenmatch(const char *string,int index) {
246 	char ch1,ch2;
247 	int dir,i,count;
248 
249 	ch1=string[index];
250 	if(ch1=='(') {dir=1;ch2=')';}
251 	else if(ch1=='[') {dir=1;ch2=']';}
252 	else if(ch1=='{') {dir=1;ch2='}';}
253 	else if(ch1==')') {dir=-1;ch2='(';}
254 	else if(ch1==']') {dir=-1;ch2='[';}
255 	else if(ch1=='}') {dir=-1;ch2='{';}
256 	else return -1;
257 
258 	i=index+dir;
259 	count=0;
260 	for(;;) {
261 		while(i>=0 && string[i]!='\0' && string[i]!=ch1 && string[i]!=ch2) i+=dir;
262 		if(i<0 || string[i]=='\0') return -2;
263 		if(string[i]==ch1) count++;
264 		else if(count==0) return i;
265 		else count--;
266 		i+=dir; }
267 	return 0; }
268 
269 
270 /* strsymbolmatch */
strsymbolmatch(char * str1,char * str2,const char * symbols)271 int strsymbolmatch(char *str1,char *str2,const char *symbols) {
272 	char *s1,*s2;
273 
274 	s1=str1;
275 	s2=str2;
276 	while(1) {
277 		s1=strpbrk(s1,symbols);
278 		s2=strpbrk(s2,symbols);
279 		if(!s1 && !s2) return 1;
280 		if(!s1 || !s2) return 0;
281 		if(*s1!=*s2) return 0;
282 		s1++;
283 		s2++; }
284 	return 0; }
285 
286 
287 
288 /******************************************************************/
289 /************************* Word operations ************************/
290 /******************************************************************/
291 
292 
293 /* wordcount */
wordcount(const char * s)294 int wordcount(const char *s) {
295 	int n,sp;
296 	const char *s2;
297 
298 	n=0;
299 	sp=1;
300 	for(s2=s;*s2;s2++) {
301 		if(sp && !isspace(*s2)) n++;
302 		sp=isspace(*s2); }
303 	return n; }
304 
305 
306 /* wordcountpbrk */
wordcountpbrk(const char * s,const char * symbols)307 int wordcountpbrk(const char *s,const char *symbols) {
308 	int n,sp,ischar;
309 	const char *s2;
310 
311 	n=0;
312 	sp=1;
313 	ischar=0;
314 	for(s2=s;*s2;s2++) {
315 		if(sp && (ischar=!strchr(symbols,*s2))) n++;
316 		sp=!ischar; }
317 	return n; }
318 
319 
320 /* strwhichword */
strwhichword(const char * s,const char * end)321 int strwhichword(const char *s,const char *end) {
322 	int n,sp;
323 	const char *s2;
324 
325 	n=0;
326 	sp=1;
327 	for(s2=s;*s2 && s2!=end+1;s2++) {
328 		if(sp && !isspace(*s2)) n++;
329 		sp=isspace(*s2); }
330 	return n; }
331 
332 
333 /* strnword */
strnword(char * s,int n)334 char *strnword(char *s,int n) {
335 	char *s2;
336 
337 	if(!s) return NULL;
338 	s2=s;
339 	if(!isspace(*s)) n--;
340 	for(;n>0 && *s2;n--) {
341 		while(!isspace(*s2) && *s2) s2++;
342 		while(isspace(*s2)) s2++; }
343 	return *s2?s2:NULL; }
344 
345 
346 /* strnwordc */
strnwordc(const char * s,int n)347 const char *strnwordc(const char *s,int n) {
348 	const char *s2;
349 
350 	if(!s) return NULL;
351 	s2=s;
352 	if(!isspace(*s)) n--;
353 	for(;n>0 && *s2;n--) {
354 		while(!isspace(*s2) && *s2) s2++;
355 		while(isspace(*s2)) s2++; }
356 	return *s2?s2:NULL; }
357 
358 
359 /* strnword1 */
strnword1(char * s,int n)360 char *strnword1(char *s,int n) {
361 	char *s2;
362 
363 	if(!s) return NULL;
364 	s2=s;
365 	for(n--;n>0 && *s2;n--) {
366 		while(*s2 && *s2!=' ' && *s2!='\t') s2++;
367 		if(*s2) s2++; }
368 	if(*s2==' ' || *s2=='\t' || !*s2) return NULL;
369 	else return s2;}
370 
371 
372 /* strnwordend */
strnwordend(char * s,int n)373 char *strnwordend(char *s,int n) {
374 	char *s2;
375 
376 	if(!s) return NULL;
377 	s2=s;
378 	for(;n>0 && *s2;n--) {
379 		while(isspace(*s2)) s2++;
380 		while(!isspace(*s2) && *s2) s2++; }
381 	return n==0?s2:NULL; }
382 
383 
384 /* strwordcpy */
strwordcpy(char * destination,const char * source,int n)385 char *strwordcpy(char *destination,const char *source,int n) {
386 	int i;
387 
388 	i=0;
389 	while(n) {
390 		while(isspace(source[i])) {
391 			destination[i]=source[i];
392 			i++; }
393 		while(!isspace(source[i])) {
394 			destination[i]=source[i];
395 			i++; }
396 		n--; }
397 
398 	if(i>0 && destination[i-1]!='\0')
399 		destination[i]='\0';
400 
401 	return destination; }
402 
403 
404 /******************************************************************/
405 /************************** String arrays *************************/
406 /******************************************************************/
407 
408 
409 /* stringfind */
410 // C++ supports const in nested lists but C does not
411 #ifdef __GNUG__
stringfind(const char * const * slist,int n,const char * s)412 int stringfind(const char * const *slist,int n,const char *s) {
413 #else
414 int stringfind(char **slist,int n,const char *s) {
415 #endif
416 	int i;
417 
418 	for(i=0;i<n && strcmp(slist[i],s);i++);
419 	return i<n?i:-1; }
420 
421 
422 /* stringnfind */
423 // C++ supports const in nested lists but C does not
424 #ifdef __GNUG__
425 int stringnfind(const char * const *slist,int n,const char *s,int nchar) {
426 #else
427 int stringnfind(char **slist,int n,const char *s,int nchar) {
428 #endif
429 	int i;
430 
431 	for(i=0;i<n && strncmp(slist[i],s,nchar);i++);
432 	return i<n?i:-1; }
433 
434 
435 /******************************************************************/
436 /**************** Reading sequential items from strings ***********/
437 /******************************************************************/
438 
439 
440 /* strreadni */
441 int strreadni(char *s,int n,int *a,char **endp) {
442 	int i,ok;
443 	char *s2;
444 
445 	s2=s;
446 	ok=1;
447 	for(i=0;i<n && ok;i++) {
448 		a[i]=(int) strtol(s,&s2,10);
449 		if(s2==s) ok=0;
450 		s=s2; }
451 	if(endp) *endp=s2;
452 	return ok?i:i-1; }
453 
454 
455 /* strreadnli */
456 int strreadnli(char *s,int n,long int *a,char **endp) {
457 	int i,ok;
458 	char *s2;
459 
460 	s2=s;
461 	ok=1;
462 	for(i=0;i<n && ok;i++) {
463 		a[i]=(long int) strtol(s,&s2,10);
464 		if(s2==s) ok=0;
465 		s=s2; }
466 	if(endp) *endp=s2;
467 	return ok?i:i-1; }
468 
469 
470 /* strreadnf */
471 int strreadnf(char *s,int n,float *a,char **endp) {
472 	int i,ok;
473 	char *s2;
474 
475 	s2=s;
476 	ok=1;
477 	for(i=0;i<n && ok;i++) {
478 		a[i]=(float) strtod(s,&s2);
479 		if(s2==s) ok=0;
480 		s=s2; }
481 	if(endp) *endp=s2;
482 	return ok?i:i-1; }
483 
484 
485 /* strreadnd */
486 int strreadnd(char *s,int n,double *a,char **endp) {
487 	int i,ok;
488 	char *s2;
489 
490 	s2=s;
491 	ok=1;
492 	for(i=0;i<n && ok;i++) {
493 		a[i]=strtod(s,&s2);
494 		if(s2==s) ok=0;
495 		s=s2; }
496 	if(endp) *endp=s2;
497 	return ok?i:i-1; }
498 
499 
500 /* strreadns */
501 int strreadns(char *s,int n,char **a,char **endp) {
502 	int i,j;
503 	char *s2;
504 
505 	s2=s;
506 	j=1;
507 	for(i=0;i<n && *s2;i++) {
508 		while(isspace(*s2)) s2++;
509 		for(j=0;!isspace(*s2) && *s2;j++) a[i][j]=*(s2++);
510 		if(j) a[i][j]='\0'; }
511 	if(endp) *endp=s2;
512 	return j?i:i-1; }
513 
514 
515 /******************************************************************/
516 /************ String copying with memory allocation ***************/
517 /******************************************************************/
518 
519 
520 /* EmptyString */
521 char *EmptyString() {
522 	char *s;
523 	int i;
524 
525 	s=(char*) calloc(STRCHAR,sizeof(char));
526 	if(s)	for(i=0;i<STRCHAR;i++)	s[i]='\0';
527 	return s; }
528 
529 
530 /* StringCopy */
531 char *StringCopy(const char *s) {
532 	char *s2;
533 	int i;
534 
535 	s2=(char *) calloc(strlen(s)+1,sizeof(char));
536 	if(!s2) return NULL;
537 	for(i=0;s[i];i++)	s2[i]=s[i];
538 	s2[i]='\0';
539 	return s2; }
540 
541 
542 /* PascalString */
543 unsigned char *PascalString(const char *s) {
544 	unsigned char *s2;
545 	int i;
546 
547 	s2=(unsigned char *) calloc(strlen(s)+1,sizeof(unsigned char));
548 	if(!s2) return 0;
549 	for(i=0;s[i];i++)	s2[i+1]=s[i];
550 	s2[0]=(unsigned char) i;
551 	return s2; }
552 
553 
554 /******************************************************************/
555 /************ String modifying without memory allocation **********/
556 /******************************************************************/
557 
558 
559 /* strPreCat */
560 char *strPreCat(char *str,const char *cat,int start,int stop) {
561 	int i,n,len;
562 
563 	n=stop-start;
564 	len=strlen(str);
565 	for(i=len+n;i>=n;i--) str[i]=str[i-n];
566 	for(;i>=0;i--) str[i]=cat[start+i];
567 	return str; }
568 
569 
570 /* strPostCat */
571 char *strPostCat(char *str,const char *cat,int start,int stop) {
572 	int i,n,len;
573 
574 	n=stop-start;
575 	len=strlen(str);
576 	for(i=0;i<n;i++) str[len+i]=cat[start+i];
577 	str[len+i]='\0';
578 	return str; }
579 
580 
581 /* strMidCat */
582 char *strMidCat(char *str,int s1,int s2,const char *cat,int start,int stop) {
583 	int i,n,len,shift;
584 
585 	if(stop<0) stop=strlen(cat);
586 	n=stop-start;
587 	shift=n-(s2-s1);
588 	len=strlen(str);
589 	if(shift>0)
590 		for(i=len+shift;i>=s2+shift;i--) str[i]=str[i-shift];
591 	else if(shift<0)
592 		for(i=s2+shift;i<=len+shift;i++) str[i]=str[i-shift];
593 	for(i=0;i<n;i++) str[i+s1]=cat[start+i];
594 	return str; }
595 
596 
597 /* strchrreplace */
598 int strchrreplace(char *str,char charfrom,char charto) {
599 	int n;
600 	char *s1;
601 
602 	n=0;
603 	while((s1=strchr(str,charfrom))) {
604 		*s1=charto;
605 		n++; }
606 	return n; }
607 
608 
609 /* strstrreplace */
610 int strstrreplace(char *str,const char *strfrom,const char *strto,int max) {
611 	int n,diff,lento,lenfrom,over;
612 	char *s1,*s2;
613 	int i,j;
614 
615 	if(strto) lento=strlen(strto);
616 	else lento=0;
617 	lenfrom=strlen(strfrom);
618 	diff=lento-lenfrom;
619 	over=0;
620 	n=0;
621 	s2=str;
622 	while((s1=strstr(s2,strfrom))) {
623 		i=s1-str;
624 		if(diff<0) {
625 			for(j=i+lenfrom;str[j-1] && j<max;j++)
626 				str[j+diff]=str[j]; }
627 		else if(diff>0) {
628 			for(j=strlen(str);j>=i+lenfrom;j--)
629 				if(j+diff<max) str[j+diff]=str[j];
630 			else over=1; }
631 		for(j=0;j<lento;j++)
632 			if(i+j<max) str[i+j]=strto[j];
633 			else over=1;
634 		if(i+lento<max) s2=s1+lento;
635 		else s2=str+strlen(str);
636 		n++; }
637 	if(over) return -n;
638 	return n; }
639 
640 
641 /* strcutwhite */
642 void strcutwhite(char *str,int end) {
643 	int i,j;
644 
645 	if(end&2) {	// end of string
646 		for(i=strlen(str)-1;i>=0 && isspace(str[i]);i--);
647 		str[i+1]='\0'; }
648 	if(end&1) {	// start of string
649 		for(i=0;str[i]!='\0' && isspace(str[i]);i++);
650 		j=0;
651 		for(;str[i]!='\0';i++) str[j++]=str[i];
652 		str[j]='\0'; }
653 	return; }
654 
655 
656 /* strbslash2escseq */
657 int strbslash2escseq(char *str) {
658 	char *s1,*s2;
659 
660 	s1=s2=str;
661 	while(*s2) {
662 		if(*s2=='\\') {
663 			s2++;
664 			if(*s2=='a') *s1='\a';
665 			else if(*s2=='b') *s1='\b';
666 			else if(*s2=='t') *s1='\t';
667 			else if(*s2=='n') *s1='\n';
668 			else if(*s2=='v') *s1='\v';
669 			else if(*s2=='f') *s1='\f';
670 			else if(*s2=='r') *s1='\r';
671 			else if(*s2=='\\') *s1='\\';
672 			else if(*s2=='"') *s1='\"';
673 			else *s1='\\'; }
674 		else
675 			*s1=*s2;
676 		s1++;
677 		s2++; }
678 	*s1=*s2;
679 	return s2-s1; }
680 
681 
682 /******************************************************************/
683 /**************************** Wildcards ***************************/
684 /******************************************************************/
685 
686 
687 /* strcharlistmatch */
688 int strcharlistmatch(const char *pat,const char ch,int n) {
689 	int p;
690 
691 	if(n<0) n=strlen(pat);
692 	for(p=0;p<n;p++) {
693 		if(pat[p]=='-') {															// initial -
694 			if(p+1==n) return 1;												// only -
695 			if(ch<=pat[p+1]) return 1;									// match initial -
696 			p++; }
697 		else if(p+1<n && pat[p+1]=='-') {							// internal or terminal -
698 			if(p+2==n) {																// terminal -
699 				if(ch>=pat[p]) return 1;									// terminal - match
700 				return 0; }																// terminal - no match
701 			if(ch>=pat[p] && ch<=pat[p+2]) return 1;		// internal - match
702 			p+=2; }
703 		else if(pat[p]==ch) return 1; }								// character match
704 	return 0; }
705 
706 
707 /* strwildcardmatch */
708 int strwildcardmatch(const char *pat,const char *str) {
709 	int s,p,s1,p1,match,blen,brend;
710 
711 	p1=s1=-1;
712 	s=p=0;
713 	while(str[s]) {
714 		if(pat[p]=='*') {															// star found in pat
715 			if(!pat[p+1]) return 1;											// terminal star, so this is a match
716 			p++;
717 			p1=p;																				// p and p1 are at '*'+1, where post-star text will resume
718 			s1=s; }																			// s1 is at s, where any star text will start
719 		else if(pat[p]=='?') {s++;p++;}								// '?' wildcard character
720 		else if(pat[p]=='[') {												// character list or range
721 			brend=strchrindex(pat,']',p);
722       if(brend==-1) return -1;
723       blen=brend-p-1;
724 			match=strcharlistmatch(pat+p+1,str[s],blen);
725 			if(match) {
726 				s++;
727 				p+=blen+2; }
728 			else if(p1>=0) goto starlabel;							// act as if this option weren't ever selected
729 			else return 0; }
730 		else if(pat[p]==str[s]) {s++;p++;}						// same character
731 		else if(p1>=0) {															// in star text
732 		starlabel:
733 			if(p==p1) s++;
734 			else {
735 				p=p1;
736 				s1++;
737 				s=s1; }}
738 		else return 0; }															// no match
739 	while(pat[p]=='*') p++;
740 	if(!pat[p]) return 1;														// match
741 	return 0; }																			// more pattern, so no match
742 
743 
744 /* strwildcardmatchandsub */
745 int strwildcardmatchandsub(const char *pat,const char *str,char *dest,int starextra) {
746 	int s,p,s1,p1,starpt,isub,nsub,isubstar,match,blen,d,brend,starxc;
747 	int maxsub=16;
748 	char subst[16][STRCHAR],patst[16][STRCHAR];
749 
750 	p1=s1=-1;																				// p1 and s1 are pat and str indicies following star text
751 	p=s=0;																					// p and s are current pat and str indicies
752 	starpt=0;																				// starpt is star start location in str
753 	isub=0;																					// isub is current substitution string index
754 	isubstar=0;																			// isubstar is substitution string index for most recent open star
755 	starxc=0;																				// starxc is extra characters that were included in first star text
756 	while(str[s]) {																	// walk through pat and str, determining if match and recording pattern and substitution strings
757 		if(pat[p]=='*') {
758 			if(p1>=0) {																	// resolve any open star
759 				if(starxc<starextra) {
760 					starxc++;
761 					goto starlabel; }
762 				if(isub==maxsub) return -6;
763 				strncpy(subst[isubstar],str+starpt,s1-starpt);
764 				subst[isubstar][s1-starpt]='\0';
765 				strcpy(patst[isubstar],"**");
766 				p1=-1; }
767 			starpt=s;																		// point in str where star was reached
768 			if(!pat[p+1]) {															// terminal star
769 				s=strlen(str);
770 				if(isub==maxsub) return -6;
771 				strncpy(subst[isub],str+starpt,s-starpt);
772 				subst[isub][s-starpt]='\0';
773 				strcpy(patst[isub],"**");
774 				isub++;
775 				p++; }
776 			else {																			// new star, which is not terminal
777 				isubstar=isub++;
778 				p++;
779 				p1=p;
780 				s1=s; }}
781 
782 		else if(pat[p]=='?') {												// '?' wildcard character
783 			if(isub==maxsub) return -6;
784 			subst[isub][0]=str[s];
785 			subst[isub][1]='\0';
786 			strcpy(patst[isub],"??");
787 			isub++;
788 			s++;
789 			p++; }
790 
791 		else if(pat[p]=='[') {												// character list or range
792       brend=strchrindex(pat,']',p);
793       if(brend==-1) return -7;
794       blen=brend-p-1;                             // length of text within brackets not including brackets
795 			match=strcharlistmatch(pat+p+1,str[s],blen);
796 			if(match) {
797 				if(isub==maxsub) return -6;
798 				subst[isub][0]=str[s];
799 				subst[isub][1]='\0';
800 				patst[isub][0]='[';
801 				strncpy(patst[isub]+1,pat+p,blen+2);
802 				patst[isub][blen+3]='\0';
803 				isub++;
804 				s++;
805 				p+=blen+2; }
806 			else if(p1>=0) goto starlabel;							// act as if this option weren't ever selected
807 			else return 0; }														// bracket pattern doesn't match and no open star, so no match
808 
809 		else if(pat[p]==str[s]) {											// same character
810 			s++;
811 			p++; }
812 
813 		else if(p1>=0) {															// in star text
814 		starlabel:
815 			if(p==p1) {																	// str and pat still don't match so keep going
816 				s++;
817 				s1++; }
818 			else {																			// str and pat did match but now don't, so try one larger star text
819 				isub=isubstar+1;
820 				p=p1;
821 				s1++;
822 				s=s1; }}
823 
824 		else return 0; }															// no match
825 
826 	if(starxc<starextra) return 0;									// doesn't qualify as match because extra characters weren't used
827 
828 	if(p1>=0) {																			// resolve any open star
829 		if(isub==maxsub) return -6;
830 		strncpy(subst[isubstar],str+starpt,s1-starpt);
831 		subst[isubstar][s1-starpt]='\0';
832 		strcpy(patst[isubstar],"**");
833 		p1=-1; }
834 	while(pat[p]=='*') {														// any additional stars in pat represent no characters
835 		if(isub==maxsub) return -6;
836 		subst[isub][0]='\0';
837 		strcpy(patst[isub],"**");
838 		isub++;
839 		p++; }
840 	if(pat[p]) return 0;														// more text remains in pat, so no match
841 	nsub=isub;
842 
843 // At this point, have returned 0 if no match; continuing if match.  Have nsub substitutions, recorded in substr and subpat
844 //	for(isub=0;isub<nsub;isub++) {									// debug code
845 //		printf("%i: pat='%s' subst='%s'\n",isub,patst[isub],subst[isub]); }
846 
847 	if(dest) {																			// perform substitutions
848 		for(d=0;dest[d]!='\0';d++) {
849 			if(dest[d]=='*') {
850 				for(isub=0;isub<nsub && (patst[isub][0]=='x' || strcmp(patst[isub],"**"));isub++);
851 				if(isub==nsub) return -9;
852 				patst[isub][0]='x';
853 				strMidCat(dest,d,d+1,subst[isub],0,-1); }
854 			else if(dest[d]=='?') {
855 				for(isub=0;isub<nsub && (patst[isub][0]=='x' || strcmp(patst[isub],"??"));isub++);
856 				if(isub==nsub) return -9;
857 				patst[isub][0]='x';
858 				strMidCat(dest,d,d+1,subst[isub],0,-1); }
859 			else if(dest[d]=='[') {
860         brend=strchrindex(dest,']',d);
861         if(brend==-1) return -8;
862         blen=brend-d-1;
863 				for(isub=0;isub<nsub && (patst[isub][0]=='x' || strncmp(patst[isub]+1,dest+d,blen+2));isub++);
864 				if(isub==nsub) return -9;
865 				patst[isub][0]='x';
866 				strMidCat(dest,d,d+blen+2,subst[isub],0,-1); }
867 			else if(dest[d]=='$') {
868 				if(!dest[d+1]) return -10;
869 				isub=((int)dest[d+1])-((int)'1');
870 				if(isub<0 || isub>nsub-1) return -10;
871 				strMidCat(dest,d,d+2,subst[isub],0,-1); }}}
872 
873 	return 1; }
874 
875 
876 /* permutelex - internal function, duplicate of a Zn.c function */
877 int permutelex(int *seq,int n) {
878 	int i,j;
879 	int temp;
880 
881 	i=n-1;
882 	while(i>0 && seq[i-1]>=seq[i]) i--;
883 	if(i==0) {						// input was final sequence
884 		i=0;
885 		j=n-1;
886 		while(i<j) {
887 			temp=seq[i];			// swap values at positions i and j
888 			seq[i]=seq[j];
889 			seq[j]=temp;
890 			i++;
891 			j--; }
892 		return 2; }
893 
894 	j=n;
895 	while(seq[j-1]<=seq[i-1]) j--;
896 
897 	temp=seq[i-1];			// swap values at positions (i-1) and (j-1)
898 	seq[i-1]=seq[j-1];
899 	seq[j-1]=temp;
900 
901 	i++;
902 	j=n;
903 	while(i<j) {
904 		temp=seq[i-1];			// swap values at positions (i-1) and (j-1)
905 		seq[i-1]=seq[j-1];
906 		seq[j-1]=temp;
907 		i++;
908 		j--; }
909 
910 	i=n-1;
911 	while(i>0 && seq[i-1]>=seq[i]) i--;
912 	if(i==0) return 1;	// at final sequence
913 
914 	return 0; }
915 
916 
917 /* allocresults - internal function used by strexpandlogic */
918 int allocresults(char ***resultsptr,int *maxrptr,int nchar) {
919 	char **results,**newresults;
920 	int maxr,newmaxr,i;
921 
922 	results=*resultsptr;
923 	maxr=*maxrptr;
924 	if(!results) maxr=0;
925 
926 	if(nchar<0) {													// free memory
927 		if(results) {
928 			for(i=0;i<maxr;i++) free(results[i]);
929 			free(results);
930 			results=NULL; }
931 		maxr=0; }
932 
933 	else {																// expand current list or make new one
934 		newmaxr=maxr*2+2;
935 		newresults=(char **) calloc(newmaxr,sizeof(char*));
936 		if(!newresults) return 1;
937 		for(i=0;i<newmaxr;i++) newresults[i]=NULL;
938 		for(i=0;i<newmaxr;i++) {
939 			newresults[i]=(char*) calloc(nchar,sizeof(char));
940 			if(!newresults[i]) return 1;
941 			newresults[i][0]='\0'; }
942 		for(i=0;i<maxr;i++) {
943 			strncpy(newresults[i],results[i],nchar-1);
944 			newresults[i][nchar-1]='\0'; }
945 		allocresults(resultsptr,maxrptr,-1);
946 		results=newresults;
947 		maxr=newmaxr; }
948 
949 	*resultsptr=results;
950 	*maxrptr=maxr;
951 	return 0; }
952 
953 
954 /* strexpandlogic */
955 int strexpandlogic(const char *pat,int start,int stop,char ***resultsptr) {
956 	char **results;
957 	int isym,il,ir,lnum,rnum,i2,ires;
958 	char **llist,**rlist;
959 
960 	results=*resultsptr=NULL;
961 	if(stop<0) stop=strlen(pat);
962 
963 	if(stop==start) return 0;																				// empty pattern
964 
965 	isym=start+strChrBrackets(pat+start,stop-start,' ',"{");				// word separator
966 	if(isym>=start) {																								// space operator found at i2
967 		if(isym==start || isym==stop-1) return -2;										// missing space operand
968 		lnum=strexpandlogic(pat,start,isym,&llist);
969 		if(lnum<0) return lnum;
970 		rnum=strexpandlogic(pat,isym+1,stop,&rlist);
971 		if(rnum<0) return rnum;
972 		if(lnum*rnum>0) {
973 			results=(char**) calloc(lnum*rnum,sizeof(char*));
974 			if(!results) return -1; }
975 		for(il=0;il<lnum;il++)
976 			for(ir=0;ir<rnum;ir++) {
977 				ires=il*rnum+ir;
978 				results[ires]=(char*)calloc(strlen(llist[il])+1+strlen(rlist[ir])+1,sizeof(char));
979 				if(!results[ires]) return -1;
980 				strcpy(results[ires],llist[il]);
981 				strcat(results[ires]," ");
982 				strcat(results[ires],rlist[ir]); }
983 		for(il=0;il<lnum;il++) free(llist[il]);
984 		for(ir=0;ir<rnum;ir++) free(rlist[ir]);
985 		free(llist);
986 		free(rlist);
987 		*resultsptr=results;
988 		return lnum*rnum; }
989 
990 	isym=start+strChrBrackets(pat+start,stop-start,'|',"{");				// OR operator
991 	if(isym>=start) {																								// OR operator found at i2
992 		if(isym>start) {						// left operand
993 			lnum=strexpandlogic(pat,start,isym,&llist);
994 			if(lnum<0) return lnum; }
995 		else {											// empty left operand
996 			lnum=1;
997 			llist=(char**)malloc(sizeof(char*));
998 			if(!llist) return -1;
999 			llist[0]=(char*)malloc(sizeof(char));
1000 			if(!llist[0]) return -1;
1001 			llist[0][0]='\0'; }
1002 		if(isym<stop-1) {						// right operand
1003 			rnum=strexpandlogic(pat,isym+1,stop,&rlist);
1004 			if(rnum<0) return rnum; }
1005 		else {											// empty right operand
1006 			rnum=1;
1007 			rlist=(char**)malloc(sizeof(char*));
1008 			if(!rlist) return -1;
1009 			rlist[0]=(char*)malloc(sizeof(char));
1010 			if(!rlist[0]) return -1;
1011 			rlist[0][0]='\0'; }
1012 		if(lnum==0 && rnum==0) return 0;
1013 		results=(char**) calloc(lnum+rnum,sizeof(char*));
1014 		if(!results) return -1;
1015 		for(il=0;il<lnum;il++) results[il]=llist[il];
1016 		for(ir=0;ir<rnum;ir++) results[lnum+ir]=rlist[ir];
1017 		free(llist);
1018 		free(rlist);
1019 		*resultsptr=results;
1020 		return lnum+rnum; }
1021 
1022 	isym=start+strChrBrackets(pat+start,stop-start,'&',"{");				// AND operator
1023 	if(isym>=start) {																								// AND operator found at i2
1024 		if(isym==start || isym==stop-1) return -3;											// missing AND operand
1025 		lnum=strexpandlogic(pat,start,isym,&llist);
1026 		if(lnum<0) return lnum;
1027 		rnum=strexpandlogic(pat,isym+1,stop,&rlist);
1028 		if(rnum<0) return rnum;
1029 		if(lnum*rnum>0) {
1030 			results=(char**) calloc(2*lnum*rnum,sizeof(char*));
1031 			if(!results) return -1; }
1032 		for(il=0;il<lnum;il++)
1033 			for(ir=0;ir<rnum;ir++) {
1034 				ires=il*rnum+ir;
1035 				results[ires]=(char*)calloc(strlen(llist[il])+strlen(rlist[ir])+1,sizeof(char));
1036 				if(!results[ires]) return -1;
1037 				strcpy(results[ires],llist[il]);
1038 				strcat(results[ires],rlist[ir]); }
1039 		for(il=0;il<lnum;il++)
1040 			for(ir=0;ir<rnum;ir++) {
1041 				ires=lnum*rnum+il*rnum+ir;
1042 				results[ires]=(char*)calloc(strlen(llist[il])+strlen(rlist[ir])+1,sizeof(char));
1043 				if(!results[ires]) return -1;
1044 				strcpy(results[ires],rlist[ir]);
1045 				strcat(results[ires],llist[il]); }
1046 		for(il=0;il<lnum;il++) free(llist[il]);
1047 		for(ir=0;ir<rnum;ir++) free(rlist[ir]);
1048 		free(llist);
1049 		free(rlist);
1050 		*resultsptr=results;
1051 		return 2*lnum*rnum; }
1052 
1053 	isym=start+strChrBrackets(pat+start,stop-start,'{',"");		 // braces
1054 	if(isym>=start) {
1055 		i2=strparenmatch(pat,isym);
1056 		if(i2<0) return -5;																				// no matching brace
1057 		lnum=strexpandlogic(pat,isym+1,i2,&llist);
1058 		if(lnum<0) return lnum;
1059 		if(i2+1==stop) {																					// right brace at stop
1060 			*resultsptr=llist;
1061 			for(il=0;il<lnum;il++)
1062 				strPreCat(llist[il],pat,start,isym);
1063 			return lnum; }
1064 		rnum=strexpandlogic(pat,i2+1,stop,&rlist);
1065 		if(rnum<0) return rnum;
1066 		if(lnum*rnum>0) {
1067 			results=(char**) calloc(lnum*rnum,sizeof(char*));
1068 			if(!results) return -1; }
1069 		for(il=0;il<lnum;il++)
1070 			for(ir=0;ir<rnum;ir++) {
1071 				ires=il*rnum+ir;
1072 				results[ires]=(char*)calloc(isym+strlen(llist[il])+strlen(rlist[ir])+1,sizeof(char));
1073 				if(!results[ires]) return -1;
1074 				strncpy(results[ires],pat+start,isym-start);
1075 				strcat(results[ires],llist[il]);
1076 				strcat(results[ires],rlist[ir]); }
1077 		for(il=0;il<lnum;il++) free(llist[il]);
1078 		for(ir=0;ir<rnum;ir++) free(rlist[ir]);
1079 		free(llist);
1080 		free(rlist);
1081 		*resultsptr=results;
1082 		return lnum*rnum; }
1083 
1084 	results=(char**) malloc(sizeof(char*));												// no special symbols
1085 	if(!results) return -1;
1086 	results[0]=(char*)calloc(stop-start+1,sizeof(char));
1087 	if(!results[0]) return -1;
1088 	strncpy(results[0],pat+start,stop-start);
1089 	results[0][stop-start]='\0';
1090 	*resultsptr=results;
1091 	return 1; }
1092 
1093 
1094 /* strEnhWildcardMatch */
1095 int strEnhWildcardMatch(const char *pat,const char *str) {
1096 	static char *localpat=NULL;
1097 	static char **results=NULL;
1098 	static int nr=0;
1099 	int i;
1100 
1101 	if(!pat || !localpat || strcmp(pat,localpat)) {								// create list of patterns
1102 		if(nr>0) {
1103 			for(i=0;i<nr;i++) free(results[i]);
1104 			free(results);
1105 			results=NULL;
1106 			nr=0; }
1107 		if(localpat) {
1108 			free(localpat);
1109 			localpat=NULL; }
1110 		if(pat) {
1111 			localpat=(char*)calloc(strlen(pat)+1,sizeof(char));
1112 			if(!localpat) return -1;
1113 			strcpy(localpat,pat);
1114 			nr=strexpandlogic(localpat,0,-1,&results);
1115 			if(nr<0) return nr; }}
1116 
1117 //	for(i=0;i<nr;i++) printf("%s\n",results[i]);	//?? debug
1118 
1119 	if(str)
1120 		for(i=0;i<nr;i++)
1121 			if(strwildcardmatch(results[i],str)) return 1;
1122 
1123 	return 0; }
1124 
1125 
1126 /* strEnhWildcardMatchAndSub */
1127 int strEnhWildcardMatchAndSub(const char *pat,const char *str,const char *destpat,char *dest) {
1128 	static char *localpat=NULL,*localdestpat=NULL;
1129 	static char **patlist=NULL,**destlist=NULL;
1130 	static int npr=0,ndr=0,iresults=0,starextra=0;
1131 	int i,ismatch,destwords;
1132 
1133 	if(!pat || !localpat || strcmp(pat,localpat)) {
1134 		if(npr>0) {																		// npr is number of pattern results, which is items in patlist
1135 			for(i=0;i<npr;i++) free(patlist[i]);
1136 			free(patlist);
1137 			patlist=NULL;
1138 			npr=0; }
1139 		if(localpat) {
1140 			free(localpat);
1141 			localpat=NULL; }
1142 		if(pat) {
1143 			localpat=(char*)calloc(strlen(pat)+1,sizeof(char));
1144 			if(!localpat) return -1;
1145 			strcpy(localpat,pat);													// localpat is local copy of pat
1146 			npr=strexpandlogic(localpat,0,-1,&patlist);		// patlist lists the same pattern as pat but with the logic expanded
1147 			if(npr<0) return npr; }												// error occured
1148 		iresults=0;																			// which result is being returned
1149 		starextra=0; }
1150 
1151 //	for(i=0;i<npr;i++) printf("%s\n",patlist[i]);	//?? debug
1152 
1153 	if(!destpat || !localdestpat || strcmp(destpat,localdestpat)) {
1154 		if(ndr>0) {																			// ndr is number of destination results, which is items in destlist
1155 			for(i=0;i<ndr;i++) free(destlist[i]);
1156 			free(destlist);
1157 			destlist=NULL;
1158 			ndr=0; }
1159 		if(localdestpat) {
1160 			free(localdestpat);
1161 			localdestpat=NULL; }
1162 		if(destpat) {
1163 			localdestpat=(char*)calloc(strlen(destpat)+1,sizeof(char));
1164 			if(!localdestpat) return -1;
1165 			strcpy(localdestpat,destpat);
1166 			ndr=strexpandlogic(localdestpat,0,-1,&destlist);	// destlist lists the same pattern as destpat but with logic expanded
1167 			if(ndr<0) return ndr; }
1168 		iresults=0;
1169 		starextra=0; }
1170 
1171 	if(ndr>1 && npr>1 && ndr!=npr) return -10;
1172 
1173 	if(!str) {
1174 		iresults=0;
1175 		starextra=0;
1176 		return 0; }
1177 
1178 	destwords=wordcount(destpat);
1179 
1180 	if((ndr==0 || ndr==1) && npr>0) {
1181 		while(iresults<npr) {																// iresults scans of list of patterns
1182 			if(strwildcardmatch(patlist[iresults],str)) {
1183 				strncpy(dest,destlist?destlist[0]:"",STRCHAR-1);
1184 				dest[STRCHAR-1]='\0';
1185 				ismatch=strwildcardmatchandsub(patlist[iresults],str,dest,starextra);
1186 				if(ismatch) starextra++;												// starextra looks for multiple matches when there are 2 stars
1187 				else {
1188 					starextra=0;
1189 					iresults++; }
1190 				if(ismatch && wordcount(dest)==destwords) return 1; }
1191 			else {
1192 				starextra=0;
1193 				iresults++; }}}
1194 
1195 	else if((npr==0 || npr==1) && ndr>0) {
1196 		if(patlist?strwildcardmatch(patlist[0],str):str[0]=='\0') {
1197 			while(iresults<ndr) {															// iresults scans of list of destinations
1198 				strncpy(dest,destlist[iresults],STRCHAR-1);
1199 				dest[STRCHAR-1]='\0';
1200 				ismatch=strwildcardmatchandsub(patlist?patlist[0]:"",str,dest,starextra);
1201 				if(ismatch) starextra++;												// starextra looks for multiple matches when there are 2 stars
1202 				else {
1203 					starextra=0;
1204 					iresults++; }
1205 				if(ismatch && wordcount(dest)==destwords) return 1; }}}
1206 
1207 	else if(npr>0 && ndr>0) {															// ndr==npr and ndr,ndr>0
1208 		while(iresults<npr) {																// iresults scans list of patterns
1209 			if(strwildcardmatch(patlist[iresults],str)) {
1210 				strncpy(dest,destlist[iresults],STRCHAR-1);
1211 				dest[STRCHAR-1]='\0';
1212 				ismatch=strwildcardmatchandsub(patlist[iresults],str,dest,starextra);
1213 				if(ismatch) starextra++;												// starextra looks for multiple matches when there are 2 stars
1214 				else {
1215 					starextra=0;
1216 					iresults++; }
1217 				if(ismatch && wordcount(dest)==destwords) return 1; }
1218 			else {
1219 				starextra=0;
1220 				iresults++; }}}
1221 
1222 	iresults=0;
1223 
1224 	return 0; }
1225 
1226 
1227 
1228 /******************************************************************/
1229 /***************************** Math parsing ***********************/
1230 /******************************************************************/
1231 
1232 
1233 
1234 /* dblnan */
1235 double dblnan() {
1236   return (double)NAN; }
1237 
1238 
1239 /* strloadmathfunctions */
1240 int strloadmathfunctions(void) {
1241 	double er;
1242 	char str1[STRCHAR],str2[STRCHAR];
1243 	double (*fnptrdd)(double);
1244 	double (*fnptrddd)(double,double);
1245 
1246 	er=0;
1247 	// functions from standard library math.h
1248 	er+=strevalfunction(strcpy(str1,"acos"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&acos),NULL,NULL,0);
1249 	er+=strevalfunction(strcpy(str1,"asin"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&asin),NULL,NULL,0);
1250 	er+=strevalfunction(strcpy(str1,"atan"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&atan),NULL,NULL,0);
1251 	er+=strevalfunction(strcpy(str1,"cos"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&cos),NULL,NULL,0);
1252 	er+=strevalfunction(strcpy(str1,"cosh"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&cosh),NULL,NULL,0);
1253 	er+=strevalfunction(strcpy(str1,"sin"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&sin),NULL,NULL,0);
1254 	er+=strevalfunction(strcpy(str1,"sinh"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&sinh),NULL,NULL,0);
1255 	er+=strevalfunction(strcpy(str1,"tan"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&tan),NULL,NULL,0);
1256 	er+=strevalfunction(strcpy(str1,"tanh"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&tanh),NULL,NULL,0);
1257 	er+=strevalfunction(strcpy(str1,"exp"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&exp),NULL,NULL,0);
1258 	er+=strevalfunction(strcpy(str1,"log"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&log),NULL,NULL,0);
1259 	er+=strevalfunction(strcpy(str1,"log10"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&log10),NULL,NULL,0);
1260 	er+=strevalfunction(strcpy(str1,"sqrt"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&sqrt),NULL,NULL,0);
1261 	er+=strevalfunction(strcpy(str1,"ceil"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&ceil),NULL,NULL,0);
1262 	er+=strevalfunction(strcpy(str1,"fabs"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&fabs),NULL,NULL,0);
1263 	er+=strevalfunction(strcpy(str1,"floor"),strcpy(str2,"dd"),NULL,(void*) (fnptrdd=&floor),NULL,NULL,0);
1264 
1265 	er+=strevalfunction(strcpy(str1,"atan2"),strcpy(str2,"ddd"),NULL,(void*) (fnptrddd=&atan2),NULL,NULL,0);
1266 	er+=strevalfunction(strcpy(str1,"pow"),strcpy(str2,"ddd"),NULL,(void*) (fnptrddd=&pow),NULL,NULL,0);
1267 	er+=strevalfunction(strcpy(str1,"rand"),strcpy(str2,"ddd"),NULL,(void*) (fnptrddd=&unirandCCD),NULL,NULL,0);
1268 
1269 	return (int) er; }
1270 
1271 
1272 /* strevalfunction */
1273 double strevalfunction(char *expression,char *parameters,void *voidptr,void *funcptr,char **varnames,const double *varvalues,int nvar) {
1274 	static int maxfunc=0,nfunc=0;
1275 	static char **funclist=NULL,**paramlist=NULL;
1276 	static void **funcptrs=NULL,**voidptrs=NULL;
1277 	int newmaxfunc;
1278 	char **newfunclist,**newparamlist;
1279 	void **newfuncptrs,**newvoidptrs;
1280 	char erstr[STRCHAR];
1281 
1282 	int i,comma;
1283 	double (*fnptrdd)(double);
1284 	double (*fnptrddd)(double,double);
1285 	double (*fnptrdves)(void*,char*,char*);
1286 
1287 	double answer,f1,f2;
1288 	char *s1;
1289 
1290 	if(!expression) {
1291 		for(i=0;i<nfunc;i++) {
1292 			free(funclist[i]);
1293 			free(paramlist[i]); }
1294 		free(funclist);
1295 		free(paramlist);
1296 		free(funcptrs);
1297 		free(voidptrs);
1298 		maxfunc=0;
1299 		nfunc=0;
1300 		return 0; }
1301 
1302 	if(funcptr) {
1303 		if(nfunc==maxfunc) {
1304 			newmaxfunc=2*maxfunc+1;
1305 			newfunclist=(char **) calloc(newmaxfunc,sizeof(char *));
1306 			if(!newfunclist) return 1;
1307 			for(i=0;i<nfunc;i++)
1308 				newfunclist[i]=funclist[i];
1309 			for(;i<newmaxfunc;i++)
1310 				newfunclist[i]=NULL;
1311 
1312 			newparamlist=(char **) calloc(newmaxfunc,sizeof(char *));
1313 			if(!newparamlist) return 1;
1314 			for(i=0;i<nfunc;i++)
1315 				newparamlist[i]=paramlist[i];
1316 			for(;i<newmaxfunc;i++)
1317 				newparamlist[i]=NULL;
1318 
1319 			newfuncptrs=(void **) calloc(newmaxfunc,sizeof(void *));
1320 			if(!newfuncptrs) return 1;
1321 			for(i=0;i<nfunc;i++)
1322 				newfuncptrs[i]=funcptrs[i];
1323 			for(;i<newmaxfunc;i++)
1324 				newfuncptrs[i]=NULL;
1325 
1326 			newvoidptrs=(void **) calloc(newmaxfunc,sizeof(void *));
1327 			if(!newvoidptrs) return 1;
1328 			for(i=0;i<nfunc;i++)
1329 				newvoidptrs[i]=voidptrs[i];
1330 			for(;i<newmaxfunc;i++)
1331 				newvoidptrs[i]=NULL;
1332 
1333 			free(funclist);
1334 			free(paramlist);
1335 			free(funcptrs);
1336 			free(voidptrs);
1337 			funclist=newfunclist;
1338 			paramlist=newparamlist;
1339 			funcptrs=newfuncptrs;
1340 			voidptrs=newvoidptrs;
1341 			maxfunc=newmaxfunc; }
1342 
1343 		funclist[nfunc]=StringCopy(expression);
1344 		if(!funclist[nfunc]) return 1;
1345 		paramlist[nfunc]=StringCopy(parameters);
1346 		if(!paramlist[nfunc]) return 1;
1347 		funcptrs[nfunc]=funcptr;
1348 		voidptrs[nfunc]=voidptr;
1349 		nfunc++;
1350 		return 0; }
1351 
1352 	for(i=0;i<nfunc && strcmp(expression,funclist[i]);i++);		// i is function number
1353 	CHECKS(i<nfunc,"unknown function name");
1354 
1355 	if(!strcmp(paramlist[i],"dd")) {
1356 		fnptrdd=(double(*)(double)) funcptrs[i];
1357 		f1=strmatheval(parameters,varnames,varvalues,nvar);
1358 		answer=(*fnptrdd)(f1); }
1359 	else if(!strcmp(paramlist[i],"ddd")) {
1360 		fnptrddd=(double(*)(double,double)) funcptrs[i];
1361 		CHECKS((comma=strChrBrackets(parameters,-1,',',"([{,\"'"))>0,"missing parameter");
1362 		parameters[comma]='\0';
1363 		f1=strmatheval(parameters,varnames,varvalues,nvar);
1364 		parameters+=comma+1;
1365 		f2=strmatheval(parameters,varnames,varvalues,nvar);
1366 		answer=(*fnptrddd)(f1,f2); }
1367 	else if(!strcmp(paramlist[i],"dves")) {
1368 		fnptrdves=(double(*)(void*,char*,char*)) funcptrs[i];
1369 		s1=parameters;
1370 		answer=(*fnptrdves)(voidptrs[i],erstr,s1);
1371 		CHECKS(answer>=0 || answer<0,"%s",erstr); }
1372 	else
1373 		CHECKS(0,"BUG: unknown function format");
1374 
1375 	return answer;
1376 
1377  failure:
1378 	MathParseError=1;
1379 	return dblnan(); }
1380 
1381 
1382 /* strmatheval */
1383 double strmatheval(char *expression,char **varnames,const double *varvalues,int nvar) {
1384   static int unarysymbol=0;
1385   int length,i1,i2;
1386   double answer,term;
1387   char *ptr,*ptr2,ptrchar,ptr2char;
1388 
1389 //	printf("strmatheval expression: '%s'\n",expression);	// DEBUG
1390 
1391   MathParseError=0;
1392   length=strlen(expression);
1393   CHECKS(length>0,"missing expression");
1394 
1395   if(strisnumber(expression)) {																		// number
1396     unarysymbol=0;
1397     answer=strtod(expression,NULL); }
1398 
1399   else if((i1=stringfind(varnames,nvar,expression))>=0) {					// variable
1400     unarysymbol=0;
1401     answer=varvalues[i1]; }
1402 
1403   else if(strchr("([{",expression[0]) && strparenmatch(expression,0)==length-1) {   // {[()]}
1404     unarysymbol=0;
1405     ptr=expression+length-1;
1406     ptrchar=*ptr;
1407     *ptr='\0';
1408     answer=strmatheval(expression+1,varnames,varvalues,nvar);
1409     *ptr=ptrchar; }
1410 
1411 	else if(strisfunctionform(expression,&ptr)) {										// function
1412 		unarysymbol=0;
1413 		expression[length-1]='\0';
1414 		*ptr='\0';
1415 		ptr++;
1416 		answer=strevalfunction(expression,ptr,NULL,NULL,varnames,varvalues,nvar);
1417 		CHECK(answer>=0 || answer<0); }
1418 
1419   else if((i1=strPbrkBrackets(expression,length-1,"+-","([{",1))>0 && !strchr("^*/",expression[i1-1]) && !(strchr("Ee",expression[i1-1]) && i1>1 && strchr("0123456789",expression[i1-2]))) {  // binary + -
1420     unarysymbol=0;
1421     ptr=expression+i1;
1422     ptrchar=*ptr;
1423     *ptr='\0';
1424     answer=strmatheval(expression,varnames,varvalues,nvar);        // first term
1425     *ptr=ptrchar;
1426     while((i2=strPbrkBrackets(ptr+1,strlen(ptr+1)-1,"+-","([{",1))>0 && !strchr("^*/",ptr[i2]) && !(strchr("Ee",ptr[i2]) && strchr("0123456789",ptr[i2-1]))) {
1427       ptr2=ptr+1+i2;
1428       ptr2char=*ptr2;
1429       *ptr2='\0';
1430       term=strmatheval(ptr+1,varnames,varvalues,nvar);          // middle terms
1431       *ptr2=ptr2char;
1432       if(ptrchar=='+') answer=answer+term;
1433       else answer=answer-term;
1434       ptr=ptr2;
1435       ptrchar=ptr2char; }
1436     term=strmatheval(ptr+1,varnames,varvalues,nvar);            // last term
1437     if(ptrchar=='+') answer=answer+term;
1438     else answer=answer-term; }
1439 
1440   else if((i1=strPbrkBrackets(expression,length-1,"*/%","([{",1))>0) {  // binary * / %
1441     unarysymbol=0;
1442     ptr=expression+i1;
1443     ptrchar=*ptr;
1444     *ptr='\0';
1445     answer=strmatheval(expression,varnames,varvalues,nvar);     // first term
1446     *ptr=ptrchar;
1447     while((i2=strPbrkBrackets(ptr+1,strlen(ptr+1)-1,"*/%","([{",1))>0) {
1448       ptr2=ptr+1+i2;
1449       ptr2char=*ptr2;
1450       *ptr2='\0';
1451       term=strmatheval(ptr+1,varnames,varvalues,nvar);          // middle terms
1452       *ptr2=ptr2char;
1453       if(ptrchar=='*') answer=answer*term;
1454       else if(ptrchar=='/') {
1455         CHECKS(term!=0,"divide by zero");
1456         answer=answer/term; }
1457       else {
1458         CHECKS(term>0.5,"illegal modulo value");
1459         answer=(double)((long int)(answer+0.5)%(long int)(term+0.5)); }
1460       ptr=ptr2;
1461       ptrchar=ptr2char; }
1462     term=strmatheval(ptr+1,varnames,varvalues,nvar);            // last term
1463     if(ptrchar=='*') answer=answer*term;
1464     else if(ptrchar=='/') {
1465       CHECKS(term!=0,"divide by zero");
1466       answer=answer/term; }
1467     else {
1468       CHECKS(term>0.5,"illegal modulo value");
1469       answer=(double)((long int)(answer+0.5)%(long int)(term+0.5)); }}
1470 
1471   else if(expression[0]=='+' || expression[0]=='-') {           // unary + -
1472     CHECKS(!unarysymbol,"cannot have multiple preceding signs");
1473     unarysymbol=1;
1474     term=strmatheval(expression+1,varnames,varvalues,nvar);
1475     answer=expression[0]=='+'?term:-term; }
1476 
1477 	else if((i1=strPbrkBrackets(expression,length-1,"^","([{",1))>0) {	// binary ^
1478 		unarysymbol=0;
1479 		ptr=expression+i1;
1480 		ptrchar=*ptr;
1481 		*ptr='\0';
1482 		answer=strmatheval(expression,varnames,varvalues,nvar);
1483 		*ptr=ptrchar;
1484 		term=strmatheval(ptr+1,varnames,varvalues,nvar);
1485 		CHECKS(answer>0 || (answer==0 && term>0) || (answer<0 && term==round(term)),"exponent error");
1486 		answer=pow(answer,term); }
1487 
1488   else {
1489     answer=0;
1490     CHECKS(0,"syntax error"); }
1491 
1492 //	printf("\t=%g\n",answer);	// DEBUG
1493 
1494   return answer;
1495 failure:
1496 	MathParseError=1;
1497   return dblnan(); }
1498 
1499 
1500 /* strmathevalint */
1501 int strmathevalint(char *expression,char **varnames,const double *varvalues,int nvar) {
1502 	double value;
1503 
1504 	value=strmatheval(expression,varnames,varvalues,nvar);
1505 	return (int)round(value); }
1506 
1507 
1508 /* strmatherror */
1509 int strmatherror(char *string,int clear) {
1510 	int er;
1511 
1512   if(string)
1513     strcpy(string,MathParseError?StrErrorString:"");
1514 	er=MathParseError;
1515 	if(clear) {
1516 		MathParseError=0;
1517 		StrErrorString[0]='\0'; }
1518   return er; }
1519 
1520 
1521 /* strmathsscanf */
1522 int strmathsscanf(const char *str,const char *format,char **varnames,const double *varvalues,int nvar,...) {
1523 	va_list arguments;
1524 	char newformat[STRCHAR],newstr[STRCHAR],expression[STRCHAR];
1525 	const char *fmtpos1,*fmtpos2,*strpos1,*strpos2;
1526 	int word,count,readint,valueint;
1527 	double value;
1528 
1529 	if(!str || !format) return 0;
1530 
1531 	newformat[0]='\0';
1532 	newstr[0]='\0';
1533 	fmtpos1=format;														// position in original format string
1534 	strpos1=str;															// position in original str string
1535 
1536 	fmtpos2=strstr(fmtpos1,"%m");							// location of math operation to deal with
1537 
1538 	while(fmtpos2) {
1539 		if(*(fmtpos2+2)=='i') readint=1;
1540 		else if(*(fmtpos2+2)=='l' && *(fmtpos2+3)=='g') readint=0;
1541 		else CHECKS(0,"BUG: illegal string formatting argument");
1542 
1543 		word=strwhichword(fmtpos1,fmtpos2);
1544 		strpos2=strnwordc(strpos1,word);				// position in string with math for parsing
1545 		if(!strpos2) break;
1546 
1547 		strncat(newformat,fmtpos1,fmtpos2-fmtpos1);
1548 		strncat(newstr,strpos1,strpos2-strpos1);
1549 		sscanf(strpos2,"%s",expression);
1550 		if(readint) {
1551 			valueint=strmathevalint(expression,varnames,varvalues,nvar);
1552 			if(strmatherror(NULL,0)) break;
1553 			strcat(newformat,"%i ");
1554 			snprintf(newstr+strlen(newstr),STRCHAR-strlen(newstr),"%i ",valueint); }
1555 		else {
1556 			value=strmatheval(expression,varnames,varvalues,nvar);
1557 			if(strmatherror(NULL,0)) break;
1558 			strcat(newformat,"%lg ");
1559 			snprintf(newstr+strlen(newstr),STRCHAR-strlen(newstr),"%.17g ",value); }
1560 
1561 		fmtpos1=strnwordc(fmtpos2,2);
1562 		strpos1=strnwordc(strpos2,2);
1563 		fmtpos2=fmtpos1?strstr(fmtpos1,"%m"):NULL; }
1564 
1565 	if(!strmatherror(NULL,0)) {
1566 		if(fmtpos1) strcat(newformat,fmtpos1);
1567 		if(strpos1) strcat(newstr,strpos1); }
1568 
1569 	va_start(arguments,nvar);
1570 	count=vsscanf(newstr,newformat,arguments);
1571 	va_end(arguments);
1572 
1573 	return count;
1574 
1575  failure:
1576 	return 0; }
1577 
1578 
1579 
1580 
1581 
1582