1 /*
2 Assortment of functions to maniplate strings, particularly those
3 from help output.
4
5 Many of those functions originated in SUMA/ but they are now part
6 of libmri.a.
7
8 While the functions herein use SUMA_[c,m,re]alloc, you're OK using either
9 free or SUMA_free on returned pointers because the SUMA's allocation
10 functions are the same as AFNI's mcw_[c,m,re]alloc
11
12 */
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <assert.h>
17 #include <string.h>
18 #include <sys/time.h>
19 #include <math.h>
20 #include "mrilib.h"
21 #include "niml.h"
22 #include "../niml/niml_private.h"
23 #include "suma_objs.h" /* 21 Apr 2020 */
24 /*------------------------------------------------------------*/
25
26 #if defined SUMA_COMPILED
27 extern SUMA_CommonFields *SUMAg_CF;
28 extern int SUMAg_N_DOv;
29 extern SUMA_DO *SUMAg_DOv;
30 #endif
31
32 /*------------------------------------------------------------*/
33
34 /*
35 SUMA_EscapeChars ("Hallo_Baby%Hallo", "_%", "//")
36 returns
37 Hallo\\_Baby\\%Hallo
38 \sa SUMA_ReplaceChars
39 */
SUMA_EscapeChars(char * s1,char * ca,char * es)40 char *SUMA_EscapeChars(char *s1, char *ca, char *es)
41 {
42 static char FuncName[]={"SUMA_EscapeChars"};
43 char *ses =NULL;
44 int nca=0, nes=0, ns1 = 0, nses = 0;
45 int i=0, j=0, k=0, l=0, nfound = 0;
46 SUMA_ENTRY;
47
48 if (!s1 || !ca || !es) SUMA_RETURN(ses);
49
50 nca = strlen(ca);
51 nes = strlen(es);
52 ns1 = strlen(s1);
53 nfound = 0;
54 for (i=0;i<ns1;++i) {
55 for (j=0; j<nca; ++j) if (s1[i] == ca[j]) ++nfound;
56 }
57 nses = ns1+nfound*nes+1;
58 ses = (char *)SUMA_calloc(nses, sizeof(char));
59
60 i=0;l=0;
61 while (s1[i]) {
62 for (j=0; j<nca; ++j) {
63 if (s1[i] == ca[j]) {
64 for (k=0; k<nes; ++k) { ses[l] = es[k]; ++l;}
65 continue;
66 }
67 }
68 ses[l] = s1[i]; ++l;
69 ++i;
70 }
71 ses[l] = '\0';
72
73 SUMA_RETURN(ses);
74 }
75
SUMA_ReplaceChars(char * s1,char * ca,char * es)76 char *SUMA_ReplaceChars(char *s1, char *ca, char *es)
77 {
78 static char FuncName[]={"SUMA_ReplaceChars"};
79 char *ses =NULL;
80 int nca=0, nes=0, ns1 = 0, nses = 0;
81 int i=0, j=0, k=0, l=0, nfound = 0, rpl = 0;
82 SUMA_ENTRY;
83
84 if (!s1 || !ca || !es) SUMA_RETURN(ses);
85
86 nca = strlen(ca);
87 nes = strlen(es);
88 ns1 = strlen(s1);
89 nfound = 0;
90 for (i=0;i<ns1;++i) {
91 for (j=0; j<nca; ++j) if (s1[i] == ca[j]) ++nfound;
92 }
93 nses = ns1-nfound+nfound*nes+1;
94 ses = (char *)SUMA_calloc(nses, sizeof(char));
95
96 i=0;l=0;
97 while (s1[i]) {
98 for (j=0; j<nca; ++j) {
99 rpl = 0 ;
100 if (s1[i] == ca[j]) {
101 for (k=0; k<nes; ++k) { ses[l] = es[k]; ++l;}
102 rpl = 1;
103 continue;
104 }
105 }
106 if (!rpl) { ses[l] = s1[i]; ++l; }
107 ++i;
108 }
109 ses[l] = '\0';
110
111 SUMA_RETURN(ses);
112 }
113
114 /*
115 Insert string 'ins' at pointer 'pos' inside of string '*s' which
116 has at most *nalloc characters.
117 Returns the string with the insertion, and reallocates and updates
118 *nalloc if needed.
119 Call with something like:
120 s = insert_in_string(&s, pos, ins, nalloc);
121 */
insert_in_string(char ** s,char * pos,char * ins,int * nalloc)122 char *insert_in_string(char **s, char *pos, char *ins, int *nalloc)
123 {
124 char *sp=NULL;
125 int ns = -1, n_ins=-1, i_ins, i;
126
127 if (!s || !*s || !pos || !nalloc) return(sp);
128
129 sp = *s;
130 if (!ins || ins[0] == '\0') return(sp); /* nothing to do */
131 ns = strlen(sp);
132 n_ins = strlen(ins);
133
134 if ((i_ins = pos - sp) < 0 || (i_ins > ns)) {
135 ERROR_message("Inserting outside of boundaries of string");
136 return(*s);
137 }
138
139 /* fprintf(stderr,"i_ins=%d, ins=%s, ns=%d",i_ins, ins, ns); */
140 /* Check for enough allocation */
141 if (ns+n_ins >= *nalloc) {
142 *nalloc += 500;
143 *s = (char *)realloc(sp, (*nalloc+1)*sizeof(char));
144 sp = *s;
145 }
146
147 /* Now move the second half ins steps */
148 for (i=ns; i>=i_ins; --i) {
149 sp[i+n_ins] = sp[i];
150 }
151
152 /* And now put in the insertion string */
153 for (i=0; i<n_ins; ++i) {
154 sp[i_ins+i] = ins[i];
155 }
156
157 return(*s);
158 }
159
160
write_string(char * s,char * prelude,char * postscript,int nmax,int multiline,FILE * fout)161 void write_string(char *s, char *prelude, char *postscript,
162 int nmax, int multiline, FILE *fout)
163 {
164 int k, ns;
165
166 if (!fout) fout = stdout;
167 if (prelude) fprintf(fout, "%s", prelude);
168 if (s) {
169 ns = strlen(s);
170 if (nmax>ns) nmax = ns;
171 else if (nmax<0) nmax = ns;
172 k = 0;
173 if (multiline) {
174 while (k<nmax ) {
175 fprintf(stderr,"%c",*(s+k));
176 ++k;
177 }
178 } else {
179 while (k<nmax && s[k] !='\n') {
180 fprintf(stderr,"%c",*(s+k));
181 ++k;
182 }
183 }
184 }
185 if (postscript) fprintf(fout, "%s", postscript);
186 return;
187 }
188
189 /*!
190 Append s2 to s1 but without exceeding nmax characters for
191 s1
192
193 s1 must be able to hold nmax characters
194 */
SUMA_strncat(char * s1,char * s2,int nmax)195 char *SUMA_strncat(char *s1, char *s2, int nmax)
196 {
197 int ns1=0;
198 if (!s1 || !s2) return(s1);
199 if (s1) {
200 ns1 = strlen(s1);
201 if (ns1 >= nmax) return(s1);
202 }
203 if (s2) {
204 nmax = nmax - ns1;
205 s1 = strncat(s1,s2, nmax);
206 }
207 return(s1);
208 }
209
summarize_string(char * us,int lmax)210 char *summarize_string(char *us, int lmax)
211 {
212 static char FuncName[]={"summarize_string"};
213 static char os[10][250], elli[]={" ... "};
214 static int n = 0;
215 char *s = NULL;
216 int nelli, nchunk, nleft;
217
218 SUMA_ENTRY;
219
220 ++n;
221 if (n>9) n = 0;
222 if (lmax > 249) lmax = 249;
223 nelli = strlen(elli);
224 if (lmax - nelli < 3) lmax = nelli+3;
225
226
227 s = (char *)os[n]; s[0] = '\0';
228
229 if (strlen(us)<=lmax) {
230 strcpy(s,us);
231 SUMA_RETURN(s);
232 }
233
234
235
236 /* long one */
237 nchunk = (lmax - nelli)/2;
238 strncpy(s, us, nchunk); s[nchunk]='\0';
239 strcat(s,elli);
240 nleft = lmax - nchunk -nelli;
241 SUMA_strncat(s, us+strlen(us)-nleft, nleft);
242 s[lmax] = '\0';
243
244 SUMA_RETURN(s);
245 }
246
247 /*
248 Find 1st location in cur that begins with string opt.
249 Blanks are ignored.
250
251 If term is not null, then string opt must be followed
252 by one of the characters in term.
253
254 If bracketers is not null, accept an opening bracket as
255 a valid starting character. bracketers must have an even
256 number of characters with each pair containing the opening/closing
257 characters.
258
259 Function returns pointer to beginning of opt in the line,
260 and sets number of blanks preceding it */
line_begins_with(char * cur,char * opt,int * nb,char * term,char * bracketers,int mintoend)261 char *line_begins_with(char *cur, char *opt, int *nb,
262 char *term, char *bracketers, int mintoend)
263 {
264 static char FuncName[]={"line_begins_with"};
265 char *loc=NULL, *nl=NULL, *eee=NULL, obrac='\0',
266 cbrac='\0', *bop=NULL, *eopt=NULL;
267 int bad = 1, lopt, nbra;
268 SUMA_Boolean LocalHead = NOPE;
269
270 SUMA_ENTRY;
271
272 if (!cur || !opt) {
273 ERROR_message("NULL option or null string");
274 SUMA_RETURN(loc);
275 }
276 if (bracketers && (nbra=strlen(bracketers)) % 2) {
277 ERROR_message("Must have even number of chars in bracketers. Have %d",
278 nbra);
279 SUMA_RETURN(loc);
280 }
281
282 lopt = strlen(opt);
283 if (nb) *nb = -1;
284 do {
285 loc = strstr(cur, opt);
286 if (loc) {
287 SUMA_LH("Found '%s'\non\n'%s'\nat\n'%s'\n",
288 opt, summarize_string(cur, 50),
289 summarize_string(loc,50));
290 bad = 0; /* Assume it is good */
291 obrac='\0'; cbrac = '\0'; eopt= '\0';
292 /* Do we have a bracket?*/
293 if (bracketers) {
294 if (loc > cur && (bop = strchr(bracketers,*(loc-1)))) {
295 SUMA_LH("Found opening bracket '%c' at >>%s<<",
296 *bop, summarize_string(loc-1, 50));
297 if ((bop - bracketers) % 2) {
298 SUMA_S_Warn("Closing bracket before option '%s'! >>%s<<",
299 opt, summarize_string(cur,50));
300 /* let it fail below...*/
301 } else {
302 obrac=*bop;
303 cbrac=*(bop+1); /* closing bracket character */
304 }
305 }
306 }
307 if (!bad && cbrac != '\0') { /* make sure closing bracket is there */
308 eee = loc+lopt;
309 while (*eee != '\0' && *eee != cbrac) {
310 ++eee;
311 }
312 if (*eee != cbrac) {
313 SUMA_S_Warn("No closing bracket '%c' found "
314 "for >>%s<< on option '%s'",
315 cbrac, summarize_string(cur,50), opt);
316 bad = 1;
317 } else {
318 eopt = eee; /* Mark location for end of option */
319 }
320 }
321 if (!bad && term) { /* check for proper termination */
322 eee = term;
323 if (*(loc+lopt) != '\0') {
324 bad = 1;
325 while (bad && *eee != '\0') {
326 if (*(loc+lopt) == *eee) {
327 bad = 0;
328 }
329 ++eee;
330 }
331 }
332 }
333 if (!bad && mintoend > 0) { /* number of chars until new line or end */
334 if (eopt) eee = eopt;
335 else eee = loc+lopt;
336 SUMA_SKIP_TO_EOL(eee,NULL);
337 if ((eee-(loc+lopt))<mintoend) {
338 SUMA_LH("Failed minend test %d < %d\n",
339 (int)(eee-(loc+lopt)),mintoend);
340 bad = 1;
341 }
342 }
343 if (!bad) {
344 if (loc == cur) {
345 if (nb) *nb = 0;
346 SUMA_RETURN(loc);
347 }
348 /* search back to new line */
349 nl = loc-1;
350 while (nl != cur && *nl != '\n' && !bad) {
351 if (*nl != ' ' && *nl != '\t' &&
352 obrac != '\0' && *nl != obrac) { /* No need to continue */
353 SUMA_LH("Failed at search back to new line");
354 bad = 1;
355 }
356 --nl;
357 }
358 }
359
360 if (!bad) { /* Good */
361 if (*nl == '\n') ++nl;
362 if (nb) *nb = loc -nl;
363 SUMA_RETURN(loc);
364 } else {
365 /* continue search past this find. */
366 cur = loc+1;
367 }
368 } else {
369 /* nothing found, get out */
370 SUMA_RETURN(NULL);
371 }
372
373 } while (*cur != '\n');
374
375 SUMA_RETURN(NULL);
376 }
377
378
379
380 /*--------------------------------------------------------------------*/
381 /*! My version of Bob's Decode a single string into a bunch of strings, separated
382 by characters from the list in sep.
383 - Passing sep in as NULL means to use "," as the separator.
384 - In each sub-string, leading and trailing blanks will be excised.
385 - This can result in 0 length strings (e.g., "1,,2," will result
386 in the second and fourth output strings having 0 length).
387 \sa SUMA_NI_get_ith_string
388 ----------------------------------------------------------------------*/
SUMA_NI_decode_string_list(char * ss,char * sep)389 NI_str_array * SUMA_NI_decode_string_list( char *ss , char *sep )
390 {
391 static char FuncName[]={"SUMA_NI_decode_string_list"};
392 NI_str_array *sar ;
393 int num , nn,id,jd , lss ;
394
395 if( ss == NULL || ss[0] == '\0' ) return NULL ; /* bad input */
396
397 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
398
399 sar = NI_malloc(NI_str_array, sizeof(NI_str_array)) ; /* create output */
400 sar->num = 0 ; sar->str = NULL ;
401
402 /* scan for sub-strings */
403
404 lss = NI_strlen(ss) ;
405 num = id = 0 ;
406 while( id < lss ){
407
408 /* skip current position ahead over whitespace */
409
410 while( id < lss && isspace(ss[id]) ) id++ ;
411 if( id == lss ) break ; /* ran out of string */
412
413 jd = id ; /* save current position (start of new string) */
414
415 /* skip ahead until ss[id] is a separator
416 [or a space - 10 Dec 2002 ZSS I don't like that one,
417 gives me funny looking results with legitimate spaces ,
418 line below was:
419 while( id < lss && strchr(sep,ss[id]) == NULL &&
420 !isspace(ss[id])) id++; ] */
421
422 while( id < lss && strchr(sep,ss[id]) == NULL ) id++;
423 if( id == jd ){ /* a blank string */
424 /* Prior to Dec. 17 2013, I would:
425 id++; continue;
426 But that is a bad idea in cases when parsing
427 strings that have something like "...;;..." where
428 ';;' indicates an empty string. That can come up for
429 column range of data elements when a range cannot be
430 computed. */
431 }
432
433 /* new sub-string runs from ss[jd] to ss[id-1] */
434
435 sar->str = NI_realloc( sar->str , char*, sizeof(char *)*(num+1) ) ;
436
437 nn = id-jd ; /* length of sub-string */
438 #if 0
439 while( nn > 0 && isspace(ss[jd+nn-1]) ) nn-- ; /* clip trailing blanks */
440 #endif
441 sar->str[num] = NI_malloc(char, (nn+1)*sizeof(char)); /* output string */
442 if( nn > 0 ) memcpy(sar->str[num],ss+jd,nn) ; /* copy sub-string */
443 sar->str[num++][nn] = '\0' ; /* terminate output */
444
445 id++ ; /* skip separator */
446 }
447
448 sar->num = num ; return sar ;
449 }
450
SUMA_NI_string_vec_to_str_array(char ** ss,int nss)451 NI_str_array * SUMA_NI_string_vec_to_str_array( char **ss , int nss )
452 {
453 static char FuncName[]={"SUMA_NI_string_vec_to_str_array"};
454 NI_str_array *sar ;
455 int num ,id, nn=0 ;
456
457 if( ss == NULL || nss == 0 ) return NULL ; /* bad input */
458
459 sar = NI_malloc(NI_str_array, sizeof(NI_str_array)) ; /* create output */
460 sar->num = nss ; sar->str=NULL;
461 sar->str = NI_realloc( sar->str , char*, sizeof(char *)*nss ) ;
462
463 /* scan for sub-strings */
464
465 num = 0 ;
466 while( num < nss ){
467 if (ss[num]) nn = strlen(ss[num]);
468 else nn=0;
469 sar->str[num] = NI_malloc(char, (nn+1)*sizeof(char)) ;
470 memcpy(sar->str[num],ss[num], nn) ;
471 sar->str[num++][nn] = '\0' ;
472 }
473
474 return sar ;
475 }
476
477
478 /*--------------------------------------------------------------------*/
479 /*! \brief Returns a copy of the ith string in a string list.
480 \sa SUMA_NI_decode_string_list ( on which this function is based)
481 ----------------------------------------------------------------------*/
SUMA_NI_get_ith_string(char * ss,char * sep,int i)482 char * SUMA_NI_get_ith_string( char *ss , char *sep, int i )
483 {
484 static char FuncName[]={"SUMA_NI_get_ith_string"};
485 char *str =NULL;
486 int num , nn,id,jd , lss ;
487
488 SUMA_ENTRY;
489
490 if( ss == NULL || ss[0] == '\0' || i<0) SUMA_RETURN( NULL ) ; /* bad input */
491
492 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
493
494
495 /* scan for sub-strings */
496
497 lss = NI_strlen(ss) ;
498 num = id = 0 ;
499 while( id < lss ){
500
501 /* skip current position ahead over whitespace */
502
503 while( id < lss && isspace(ss[id]) ) id++ ;
504 if( id == lss ) break ; /* ran out of string */
505
506 jd = id ; /* save current position (start of new string) */
507
508 /* skip ahead until ss[id] is a separator
509 [or a space - 10 Dec 2002 ZSS I don't like that one,
510 gives me funny looking results with legitimate spaces ,
511 line below was:
512 while( id < lss && strchr(sep,ss[id]) == NULL && !isspace(ss[id])) id++;]
513 */
514
515 while( id < lss && strchr(sep,ss[id]) == NULL ) id++;
516 if( id == jd ){ id++; continue; } /* is only a separator? */
517
518
519
520 nn = id-jd ; /* length of sub-string */
521
522 if (i==num) { /* that is the one I want */
523 /* new sub-string runs from ss[jd] to ss[id-1] */
524 str = (char *) SUMA_malloc( sizeof(char )*(nn+1) ) ;
525 if( nn > 0 ) memcpy(str,ss+jd,nn) ; /* copy sub-string */
526 str[nn] = '\0' ; /* terminate output */
527 SUMA_RETURN(str);
528 }
529 ++num;
530 id++ ; /* skip separator */
531 }
532
533 /* not found, return with NULL */
534 SUMA_RETURN( str );
535 }
536
537 /*--------------------------------------------------------------------*/
538 /*! \brief Returns the index of a string in a string list.
539 \sa SUMA_NI_decode_string_list ( on which this function is based)
540 ----------------------------------------------------------------------*/
SUMA_NI_find_in_cs_string(char * ss,char * sep,char * str)541 int SUMA_NI_find_in_cs_string( char *ss , char *sep, char *str )
542 {
543 static char FuncName[]={"SUMA_NI_find_in_cs_string"};
544 int i = -1;
545 int num , nn,id,jd , lss ;
546
547 SUMA_ENTRY;
548
549 if( ss == NULL || ss[0] == '\0' || str == NULL) SUMA_RETURN(i);/* bad input */
550
551 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
552
553
554 /* scan for sub-strings */
555
556 lss = NI_strlen(ss) ;
557 num = id = 0 ;
558 while( id < lss ){
559
560 /* skip current position ahead over whitespace */
561
562 while( id < lss && isspace(ss[id]) ) id++ ;
563 if( id == lss ) break ; /* ran out of string */
564
565 jd = id ; /* save current position (start of new string) */
566
567 /* skip ahead until ss[id] is a separator
568 [or a space - 10 Dec 2002 ZSS I don't like that one,
569 gives me funny looking results with legitimate spaces ,
570 line below was:
571 while( id < lss && strchr(sep,ss[id]) == NULL && !isspace(ss[id])) id++;]
572 */
573
574 while( id < lss && strchr(sep,ss[id]) == NULL ) id++;
575 if( id == jd ){ id++; continue; } /* is only a separator? */
576
577
578
579 nn = id-jd ; /* length of sub-string */
580
581 /* new sub-string runs from ss[jd] to ss[id-1] */
582 if (nn == strlen(str)) { /* a strict search, might want to allow for
583 blanks at some point ... */
584 if (!strncmp(str,ss+jd, strlen(str))) SUMA_RETURN(num);
585 }
586 ++num;
587 id++ ; /* skip separator */
588 }
589
590 /* not found */
591 SUMA_RETURN( -1 );
592 }
593
594 /*--------------------------------------------------------------------*/
595 /*! \brief Returns a the number of composite strings in a string list.
596 \sa SUMA_NI_decode_string_list ( on which this function is based)
597 ----------------------------------------------------------------------*/
598
SUMA_NI_get_num_strings(char * ss,char * sep)599 int SUMA_NI_get_num_strings( char *ss , char *sep)
600 {
601 static char FuncName[]={"SUMA_NI_get_num_strings"};
602 char *str =NULL;
603 int num , nn,id,jd , lss ;
604
605 SUMA_ENTRY;
606
607 if( ss == NULL || ss[0] == '\0') SUMA_RETURN( -1 ) ; /* bad input */
608
609 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
610
611
612 /* scan for sub-strings */
613
614 lss = NI_strlen(ss) ;
615 num = id = 0 ;
616 while( id < lss ){
617
618 /* skip current position ahead over whitespace */
619
620 while( id < lss && isspace(ss[id]) ) id++ ;
621 if( id == lss ) break ; /* ran out of string */
622
623 jd = id ; /* save current position (start of new string) */
624
625 /* skip ahead until ss[id] is a separator
626 [or a space - 10 Dec 2002 ZSS I don't like that one,
627 gives me funny looking results with legitimate spaces ,
628 line below was:
629 while( id < lss && strchr(sep,ss[id]) == NULL &&
630 !isspace(ss[id])) id++; ] */
631
632 while( id < lss && strchr(sep,ss[id]) == NULL ) id++;
633 if( id == jd ){ id++; continue; } /* is only a separator? */
634
635
636
637 nn = id-jd ; /* length of sub-string */
638
639 ++num;
640 id++ ; /* skip separator */
641 }
642
643 SUMA_RETURN( num );
644 }
645
SUMA_Show_NI_str_ar(NI_str_array * nisa,FILE * out)646 void SUMA_Show_NI_str_ar(NI_str_array *nisa, FILE *out)
647 {
648 static char FuncName[]={"SUMA_Show_NI_str_ar"};
649 int i;
650 char *s=NULL;
651 SUMA_STRING *SS = NULL;
652
653 SUMA_ENTRY;
654
655 if (!out) out = SUMA_STDOUT;
656
657 SS = SUMA_StringAppend(NULL, NULL);
658
659 if (!nisa) SS = SUMA_StringAppend_va(SS, "NULL struct");
660 else {
661 SS = SUMA_StringAppend_va(SS, "%d strings:\n", nisa->num);
662 for (i=0; i<nisa->num; ++i) {
663 SS = SUMA_StringAppend_va(SS, "\t%d->>>%s<<<\n",
664 i, nisa->str[i]?nisa->str[i]:"NULL nisa str");
665 }
666 }
667
668 SUMA_SS2S(SS,s);
669
670 fprintf(out, "%s", s); SUMA_free(s); s= NULL;
671 fflush(out);
672 SUMA_RETURNe;
673 }
674
675 /*!
676 \brief take a bunch of strings stored in NI_str_array
677 and turn them into a composite string
678 Free result with SUMA_free
679 \sa SUMA_NI_str_ar_2_comp_str
680 */
SUMA_NI_str_ar_2_comp_str(NI_str_array * nisa,char * sep)681 char *SUMA_NI_str_ar_2_comp_str (NI_str_array *nisa, char *sep)
682 {
683 static char FuncName[]={"SUMA_NI_str_ar_2_comp_str"};
684 char *ar = NULL, *s=NULL;
685 int i, nsep, k, ns, cnt, Nchars = 0;
686 SUMA_Boolean LocalHead = NOPE;
687
688 SUMA_ENTRY;
689
690 if (LocalHead) SUMA_Show_NI_str_ar(nisa, NULL);
691
692 if (!nisa) SUMA_RETURN(NULL);
693
694 if (sep) nsep = strlen(sep);
695 else nsep = 0;
696
697 /* what's the total number of chars ? */
698 for (i=0; i<nisa->num; ++i) {
699 if (nisa->str[i]) {
700 Nchars += (strlen(nisa->str[i])+nsep+1) ;
701 } /* be safe allocate a bit more ...*/
702 else Nchars += (nsep+1); /* for separator */
703 }
704
705 ar = (char *)SUMA_malloc(sizeof(char)*Nchars);
706
707 cnt = 0;
708 for (i=0; i<nisa->num; ++i) {
709 s = nisa->str[i];
710 if (s) {
711 ns = strlen(s);
712 } else {
713 ns = 0;
714 }
715 k = 0;
716 while (k < ns) { ar[cnt] = s[k]; ++k; ++cnt; }
717 k = 0;
718 while (k < nsep) { ar[cnt] = sep[k]; ++k; ++cnt; }
719 }
720 ar[cnt] = '\0'; /* le bouchon */
721
722 SUMA_RETURN(ar);
723 }
724
725 /*!
726 \brief Inverse of SUMA_NI_str_ar_2_comp_str
727 free output with SUMA_free_NI_str_array
728 */
SUMA_comp_str_2_NI_str_ar(char * s,char * sep)729 NI_str_array *SUMA_comp_str_2_NI_str_ar(char *s, char *sep)
730 {
731 static char FuncName[]={"SUMA_comp_str_2_NI_str_ar"};
732 NI_str_array *nisa = NULL;
733
734 SUMA_ENTRY;
735
736 if (!s) SUMA_RETURN(nisa);
737
738 nisa = SUMA_NI_decode_string_list(s, sep);
739
740 SUMA_RETURN(nisa);
741 }
742
SUMA_NI_str_array(NI_str_array * clss,char * what,char * action)743 NI_str_array *SUMA_NI_str_array(NI_str_array *clss, char *what, char *action)
744 {
745 static char FuncName[]={"SUMA_NI_str_array"};
746 int i=0;
747
748 SUMA_ENTRY;
749
750 if (!what || !action) SUMA_RETURN(clss);
751 if (!clss) {
752 clss = (NI_str_array *)NI_calloc(1,sizeof(NI_str_array));
753 clss->num = 0;
754 clss->str = NULL;
755 }
756 if (action[0] == 'a' ||
757 (action[0] == 'A' && NI_str_array_find(what, clss) < 0)) { /* add */
758 clss->num = clss->num+1;
759 clss->str =
760 NI_realloc(clss->str, char *, sizeof(char *)*(clss->num));
761 clss->str[clss->num-1] = NI_malloc(char, strlen(what)+1);
762 strcpy(clss->str[clss->num-1], what);
763 clss->str[clss->num-1][strlen(what)]='\0';
764 } else if ( action[0] == 'r' ) {/* remove */
765 i=NI_str_array_find(what,clss);
766 if (i>=0 && i!=clss->num-1) {
767 NI_free(clss->str[i]); clss->str[i] = clss->str[clss->num-1];
768 }
769 clss->num = clss->num-1;
770 clss->str =
771 NI_realloc(clss->str, char *, sizeof(char *)*(clss->num));
772 } else if (action[0] == 'c') {
773 /* change mode, get the index */
774 if ((i=(int)strtol(action+1, NULL, 10))>10000) {
775 SUMA_S_Errv("I have a feeling %d is in error...\n", i);
776 SUMA_RETURN(clss);
777 }
778 if (i >= clss->num || !clss->str) {
779 clss->num = i+1;
780 clss->str = NI_realloc(clss->str, char *, sizeof(char *)*(clss->num));
781 clss->str[i] = NI_malloc(char, strlen(what)+1);
782 } else {
783 clss->str[i] = NI_realloc(clss->str[i], char, (strlen(what)+1));
784 }
785 strcpy(clss->str[i], what);
786 clss->str[i][strlen(what)]='\0';
787 } else if (action[0] != 'A'){
788 SUMA_S_Warnv("action %s unknown, nothing done\n", action);
789 }
790
791 SUMA_RETURN(clss);
792
793 }
794
795 /* WARNING: For partial match, only the first hit is returned */
SUMA_NI_str_array_find(char * targ,NI_str_array * sar,int partial,int ci)796 int SUMA_NI_str_array_find( char *targ , NI_str_array *sar , int partial, int ci)
797 {
798 static char FuncName[]={"SUMA_NI_str_array_find"};
799 int ii ;
800
801 SUMA_ENTRY;
802
803 if( targ == NULL || *targ == '\0' || sar == NULL || sar->num < 1 )
804 SUMA_RETURN(-1);
805
806 if (!partial) {
807 if (!ci) {
808 for( ii=0 ; ii < sar->num ; ii++ )
809 if( strcmp(targ,sar->str[ii]) == 0 ) SUMA_RETURN(ii) ;
810 } else {
811 for( ii=0 ; ii < sar->num ; ii++ )
812 if( strcasecmp(targ,sar->str[ii]) == 0 ) SUMA_RETURN(ii) ;
813 }
814 } else {
815 if (!ci) {
816 for( ii=0 ; ii < sar->num ; ii++ )
817 if( strstr(sar->str[ii], targ) == NULL ) SUMA_RETURN(ii) ;
818 } else {
819 for( ii=0 ; ii < sar->num ; ii++ )
820 if( !AFNI_strcasestr(sar->str[ii], targ) ) SUMA_RETURN(ii) ;
821 }
822 }
823 SUMA_RETURN(-1) ;
824 }
825
826
SUMA_free_NI_str_array(NI_str_array * nisa)827 NI_str_array *SUMA_free_NI_str_array(NI_str_array *nisa)
828 {
829 static char FuncName[]={"SUMA_free_NI_str_array"};
830 int i;
831
832 SUMA_ENTRY;
833
834 if (nisa) {
835 if (nisa->str) {
836 for (i=0; i<nisa->num; ++i) {
837 if (nisa->str[i]) NI_free(nisa->str[i]); nisa->str[i] = NULL;
838 }
839 NI_free(nisa->str);
840 }
841 NI_free(nisa); nisa = NULL;
842 }
843
844 SUMA_RETURN(nisa);
845 }
846
847 /*!
848 \brief returns the iith string in a sep separated composite string cs
849 free result with SUMA_free
850 */
SUMA_Get_Sub_String(char * cs,char * sep,int ii)851 char *SUMA_Get_Sub_String(char *cs, char *sep, int ii)
852 {
853 static char FuncName[]={"SUMA_Get_Sub_String"};
854 NI_str_array *nisa=NULL;
855 char *s = NULL;
856 SUMA_Boolean LocalHead = NOPE;
857
858 SUMA_ENTRY;
859
860 if (ii < 0) { SUMA_SL_Err("Bad index"); SUMA_RETURN(s); }
861 if (!cs) { SUMA_SL_Err("NULL input"); SUMA_RETURN(s); }
862 #if 0 /* old slow way */
863 nisa = SUMA_comp_str_2_NI_str_ar(cs, sep);
864 if (LocalHead) SUMA_Show_NI_str_ar(nisa, NULL);
865 if (!nisa) {
866 SUMA_SL_Err("Failed in SUMA_comp_str_2_NI_str_ar"); SUMA_RETURN(s); }
867 if (ii >= nisa->num) {
868 /* SUMA_SL_Warn("not enough strings"); */ SUMA_RETURN(s); }
869 s = SUMA_copy_string(nisa->str[ii]);
870 SUMA_free_NI_str_array(nisa); nisa = NULL;
871 #else
872 s = SUMA_NI_get_ith_string( cs , sep, ii );
873
874 #endif
875 SUMA_RETURN(s);
876 }
877
SUMA_Find_Sub_String(char * cs,char * sep,char * ss)878 int SUMA_Find_Sub_String(char *cs, char *sep, char *ss)
879 {
880 static char FuncName[]={"SUMA_Find_Sub_String"};
881 NI_str_array *nisa=NULL;
882 int ii = -1;
883 SUMA_Boolean LocalHead = NOPE;
884
885 SUMA_ENTRY;
886
887 if (!ss) { SUMA_SL_Err("Bad string"); SUMA_RETURN(ii); }
888 if (!cs) { SUMA_SL_Err("NULL input"); SUMA_RETURN(ii); }
889
890 SUMA_RETURN(SUMA_NI_find_in_cs_string ( cs, sep, ss));
891
892 SUMA_RETURN(ii);
893 }
894
SUMA_Set_Sub_String(char ** cs,char * sep,int ii,char * str)895 SUMA_Boolean SUMA_Set_Sub_String(char **cs, char *sep, int ii, char *str)
896 {
897 static char FuncName[]={"SUMA_Set_Sub_String"};
898 NI_str_array *nisa=NULL;
899 char *s = NULL, act[64];
900 SUMA_Boolean LocalHead = NOPE;
901
902 SUMA_ENTRY;
903
904 if (ii < 0) { SUMA_SL_Err("Bad index"); SUMA_RETURN(NOPE); }
905 if (!cs || !str) { SUMA_SL_Err("NULL input %p %p", cs, str);
906 if (LocalHead) SUMA_DUMP_TRACE("Why");
907 SUMA_RETURN(NOPE); }
908 if (!*cs && ii != 0) {
909 SUMA_S_Errv("Bad spot %d with NULL string", ii); SUMA_RETURN(NOPE); }
910 if (!*cs && ii == 0) {
911 *cs = SUMA_copy_string(str);
912 SUMA_RETURN(YUP);
913 }
914 sprintf(act,"c%d",ii);
915 nisa = SUMA_NI_decode_string_list( *cs , sep );
916 /* SUMA_LHv("act: >>%s<< >>%s<< >>%s<< >>%s<<\n", act, *cs, sep, str); */
917 nisa = SUMA_NI_str_array(nisa,str,act);
918 SUMA_free(*cs);
919 *cs = SUMA_NI_str_ar_2_comp_str(nisa, sep);
920 if (nisa) SUMA_free_NI_str_array(nisa); nisa = NULL;
921 SUMA_RETURN(YUP);
922 }
923
924 /*!
925 \brief removes a string in a sep separated
926 composite string cs
927 The function does not reallocate for cs
928 returns 0 fail
929 1 strn found and removed
930 -1 strn not found
931 */
SUMA_Remove_Sub_String(char * cs,char * sep,char * strn)932 int SUMA_Remove_Sub_String(char *cs, char *sep, char *strn)
933 {
934 static char FuncName[]={"SUMA_Remove_Sub_String"};
935 NI_str_array *nisa=NULL;
936 char *s = NULL, *s0=NULL, *s1=NULL;
937 SUMA_Boolean LocalHead = NOPE;
938
939 SUMA_ENTRY;
940
941 if (!cs || !strn || !sep) SUMA_RETURN(0);
942
943 if (LocalHead) fprintf(SUMA_STDERR, "Strng was:\n"
944 ">>>%s<<<\n"
945 "id>%s<<<\n",
946 cs, strn);
947
948 if (!(s0 = strstr(cs, strn))) {
949 SUMA_LH("id not in strn");
950 SUMA_RETURN(-1); /* nothing to do */
951 }
952 /* advance past strn */
953 s = s0+strlen(strn);
954 /* advance past sep */
955 s1 = strstr(s, sep);
956 if (s1) s1 = s1+strlen(sep);
957 else s1 = s;
958
959 /* now copy all that is left into s */
960 while (*s1 != '\0') {
961 *s0 = *s1; ++s0; ++s1;
962 }
963 *s0 = '\0';
964
965 /* Do not bother reallocating */
966
967 if (LocalHead) fprintf(SUMA_STDERR, "Strng now:\n"
968 ">>>%s<<<\n", cs);
969 SUMA_RETURN(1);
970 }
971
972 /*!
973 \brief Reads in a sequence of numbers of an undetermined length
974 Not for reading in large numbers of numbers!
975 \param op (char *) pointing to the beginning of a
976 blank delimited series of numbers
977 \param opend (char **) if not NULL, *opend will contain the value
978 of op at the end of successful reads
979 \param tp (SUMA_VARTYPE) SUMA_int, SUMA_float, SUMA_double supported
980 at the moment
981 \return ans (void*) if tp == SUMA_int then ans is (SUMA_IVEC *)
982 tp == SUMA_float then ans is (SUMA_FVEC *)
983 tp == SUMA_double then ans is (SUMA_DVEC *)
984 \sa SUMA_strtol_vec
985 \sa SUMA_SringToNum
986 */
SUMA_AdvancePastNumbers(char * op,char ** opend,SUMA_VARTYPE tp)987 void *SUMA_AdvancePastNumbers(char *op, char **opend, SUMA_VARTYPE tp)
988 {
989 static char FuncName[]={"SUMA_AdvancePastNumbers"};
990 double *d=NULL, db;
991 int nrealloc = 0, Chunk = 100, nalloc = 0;
992 int Found = 0, i, nread;
993 void *ans;
994 SUMA_Boolean LocalHead = NOPE;
995
996 SUMA_ENTRY;
997
998 nread = 0;
999 Found = 1;
1000 while (Found) {
1001 SUMA_ADVANCE_PAST_NUM(op, db, Found);
1002 if (Found) {
1003 if (nread == nalloc) {
1004 nalloc += Chunk; ++nrealloc;
1005 d = (double*)SUMA_realloc(d, nalloc*sizeof(double));
1006 if (!d) { SUMA_SL_Crit("Failed to allocate"); SUMA_RETURN(NULL); }
1007 if (!(nrealloc % 10)) {
1008 SUMA_SL_Warn("Too much reallocation, improper use of function?");
1009 }
1010 }
1011 d[nread] = db;
1012 ++(nread);
1013 }
1014 }
1015
1016 if (LocalHead) {
1017 fprintf(SUMA_STDERR,"%s: Found %d numbers:\n", FuncName, nread);
1018 for (i=0; i<nread; ++i) fprintf(SUMA_STDERR,"%f\t", d[i]);
1019 fprintf(SUMA_STDERR,"\n");
1020 }
1021
1022 if (opend) *opend = op;
1023
1024 ans = NULL;
1025 switch (tp) {
1026 case SUMA_int:
1027 {
1028 SUMA_IVEC *ivec= (SUMA_IVEC *)SUMA_calloc(1,sizeof(SUMA_IVEC));
1029 ivec->v = (int *)SUMA_calloc(nread,sizeof(int));
1030 ivec->n = nread;
1031 for (i=0; i<nread; ++i) ivec->v[i] = (int)d[i];
1032 ans = (void *)ivec;
1033 }
1034 break;
1035 case SUMA_float:
1036 {
1037 SUMA_FVEC *fvec= (SUMA_FVEC *)SUMA_calloc(1,sizeof(SUMA_FVEC));
1038 fvec->v = (float *)SUMA_calloc(nread,sizeof(float));
1039 fvec->n = nread;
1040 for (i=0; i<nread; ++i) fvec->v[i] = (float)d[i];
1041 ans = (void *)fvec;
1042 }
1043 break;
1044 case SUMA_double:
1045 {
1046 SUMA_DVEC *dvec= (SUMA_DVEC *)SUMA_calloc(1,sizeof(SUMA_DVEC));
1047 dvec->v = (double *)SUMA_calloc(nread,sizeof(double));
1048 dvec->n = nread;
1049 for (i=0; i<nread; ++i) dvec->v[i] = (double)d[i];
1050 ans = (void *)dvec;
1051 }
1052 break;
1053 case SUMA_notypeset:
1054 SUMA_SL_Err("Type not set");
1055 ans = NULL;
1056 break;
1057 default:
1058 SUMA_SL_Err("Type not supported by this function");
1059 ans = NULL;
1060 break;
1061
1062 }
1063 if (d) SUMA_free(d); d = NULL;
1064
1065 SUMA_RETURN(ans);
1066
1067 }
1068
1069 /*!
1070 \brief change a character string of numbers to a vector of values.
1071 op must be NULL terminated!
1072
1073 \sa SUMA_AdvancePastNumbers
1074 \sa SUMA_StringToNum
1075 */
SUMA_strtol_vec(char * op,int nvals,int * nread,SUMA_VARTYPE vtp,char ** opend)1076 void *SUMA_strtol_vec(char *op, int nvals, int *nread,
1077 SUMA_VARTYPE vtp, char **opend)
1078 {
1079 static char FuncName[]={"SUMA_strtol_vec"};
1080 void *ans = NULL;
1081 long lv;
1082 double dv;
1083 char *endptr=NULL;
1084 SUMA_Boolean LocalHead = NOPE;
1085
1086 SUMA_ENTRY;
1087 *nread = 0;
1088 if (opend) *opend = op;
1089
1090 if (!SUMA_OK_OPENDX_DATA_TYPE(vtp)) {
1091 SUMA_SL_Err("Bad type");
1092 SUMA_RETURN(ans);
1093 }
1094
1095 ans = NULL;
1096 switch (vtp) {
1097 case SUMA_byte:
1098 {
1099 byte *bvec=NULL;
1100 bvec = (byte *)SUMA_calloc(nvals,sizeof(byte));
1101 lv = strtol(op, &endptr, 10);
1102 while (endptr && endptr != op && *nread < nvals) {
1103 bvec[*nread] = (byte)lv;
1104 /* if (LocalHead)
1105 fprintf(SUMA_STDERR,">>>%d<<<\t", bvec[*nread]); */
1106 ++(*nread);
1107 op = endptr;
1108 lv = strtol(op, &endptr, 10);
1109 }
1110 ans = (void *)bvec;
1111 }
1112 break;
1113 case SUMA_int:
1114 {
1115 int *ivec=NULL;
1116 ivec = (int *)SUMA_calloc(nvals,sizeof(int));
1117 lv = strtol(op, &endptr, 10);
1118 while (endptr && endptr != op && *nread < nvals) {
1119 ivec[*nread] = (int)lv;
1120 /* if (LocalHead && *nread < 10)
1121 fprintf(SUMA_STDERR,">>>%d<<<\t", ivec[*nread]); */
1122 ++(*nread);
1123 op = endptr;
1124 lv = strtol(op, &endptr, 10);
1125 }
1126 ans = (void *)ivec;
1127 }
1128 break;
1129 case SUMA_float:
1130 {
1131 float *fvec=NULL;
1132 fvec = (float *)SUMA_calloc(nvals,sizeof(float));
1133 dv = strtod(op, &endptr);
1134 while (endptr && endptr != op && *nread < nvals) {
1135 fvec[*nread] = (float)dv;
1136 /* if (LocalHead)
1137 fprintf(SUMA_STDERR,">>>%f<<<\t", fvec[*nread]); */
1138 ++(*nread);
1139 op = endptr;
1140 dv = strtod(op, &endptr);
1141 }
1142 ans = (void *)fvec;
1143 }
1144 break;
1145 case SUMA_double:
1146 {
1147 double *dvec=NULL;
1148 dvec = (double *)SUMA_calloc(nvals,sizeof(double));
1149 dv = strtod(op, &endptr);
1150 while (endptr && endptr != op && *nread < nvals) {
1151 dvec[*nread] = (double)dv;
1152 /* if (LocalHead)
1153 fprintf(SUMA_STDERR,">>>%f<<<\t", dvec[*nread]); */
1154 ++(*nread);
1155 op = endptr;
1156 dv = strtod(op, &endptr);
1157 }
1158 ans = (void *)dvec;
1159 }
1160 break;
1161 case SUMA_notypeset:
1162 SUMA_SL_Err("Type not set");
1163 ans = NULL;
1164 break;
1165 default:
1166 SUMA_SL_Err("Type not supported by this function");
1167 ans = NULL;
1168 break;
1169
1170 }
1171
1172 if (opend) *opend = op;
1173 SUMA_RETURN(ans);
1174 }
1175
1176 /*
1177 Break long lines into ones that are no longer than mxln
1178 You need to handle the freeing of the string returned by this function
1179 */
SUMA_Break_String(char * si,int mxln)1180 char *SUMA_Break_String(char *si, int mxln)
1181 {
1182 static char FuncName[]={"SUMA_Break_String"};
1183 char *so = NULL;
1184 int nsi, nso, nso_max, bsi, bso, ex, slen, ln;
1185 SUMA_Boolean LocalHead = NOPE;
1186
1187 SUMA_ENTRY;
1188
1189 if (!si) SUMA_RETURN(so);
1190
1191 SUMA_LH("Have string:>s=>%s<\n", si);
1192 slen = strlen(si);
1193 nso_max = slen+100;
1194 so = (char *)SUMA_calloc(nso_max, sizeof(char));
1195
1196 bsi = bso = -1; /* index of last encountered blank */
1197 ln = 0; /* Last line length in output string */
1198 ex = 0; /* Number of extra chars */
1199 nso = 0; nsi = 0; /* write/read position in so and si */
1200 while (si[nsi]) {
1201 while (si[nsi] && ln < mxln) {
1202 if (SUMA_IS_BLANK(si[nsi])) {
1203 bsi = nsi; bso = nso;
1204 }
1205 so[nso++] = si[nsi++];
1206 if (si[nsi] == '\n') {
1207 ln = 0; bsi = bso = -1;
1208 } else {
1209 ++ln;
1210 }
1211 }
1212 if (ln == mxln) { /* need to make a cut */
1213 if (bso > 0 && ((nso-bso)) < mxln-15) {
1214 /* had a good blank preceding, but not too far*/
1215 nso = bso; /* rewind on so */
1216 nsi = bsi; /* rewind on si */
1217 so[++nso] = '\n'; /* add new line after blank */
1218 ex += 1; /* added one new char */
1219 ln = 0; bsi = bso = -1;
1220 ++nsi; ++nso;
1221 } else {
1222 /* add a '-' */
1223 so[nso++] = '-'; so[nso++] = '\n';
1224 ex += 2;
1225 ln = 0; bsi = bso = -1;
1226 }
1227 }
1228
1229 /* realloc ? */
1230 if (ex >= (nso_max - slen - 5)) {
1231 nso_max += 100;
1232 so = (char *)SUMA_realloc(so, nso_max*sizeof(char));
1233 }
1234
1235 }
1236
1237 so[nso] = '\0';
1238 SUMA_LH("Returning:>so=>%s>", so);
1239 SUMA_RETURN(so);
1240 }
1241
1242 /*
1243
1244 A convenience version of SUMA_Offset_SLines which
1245 absolves you of having to free the offset strings at all.
1246
1247 Now, if you feel moved to clean, DO NOT FREE the strings
1248 outside of this function. To clean the allocated space, just call:
1249 SUMA_Offset_SLines(NULL, 0);
1250 and you're good as new.
1251
1252 */
SUMA_Offset_SLines(char * si,int off)1253 char *SUMA_Offset_SLines(char *si, int off)
1254 {
1255 static char FuncName[]={"SUMA_Offset_SLines"};
1256 static char **sov = NULL;
1257 static int Nmax=10, cnt = 0;
1258 int i;
1259
1260 SUMA_ENTRY;
1261
1262 if (!si) {
1263 if (sov) {
1264 for (i=0; i<Nmax; ++i) SUMA_ifree(sov[i]);
1265 SUMA_ifree(sov);
1266 }
1267 cnt = 0;
1268 SUMA_RETURN(NULL);
1269 }
1270
1271 ++cnt; if (cnt >= Nmax) cnt = 0;
1272 if (!sov) sov = (char**)SUMA_calloc(Nmax, sizeof(char *));
1273
1274 if (sov[cnt]) SUMA_ifree(sov[cnt]);
1275 sov[cnt] = SUMA_Offset_Lines(si, off);
1276
1277 SUMA_RETURN(sov[cnt]);
1278 }
1279
1280 /*
1281 Offset each line by off blanks
1282
1283 You must handle the freeing of the returned string.
1284
1285 \sa SUMA_Offset_SLines()
1286 */
SUMA_Offset_Lines(char * si,int off)1287 char *SUMA_Offset_Lines(char *si, int off)
1288 {
1289 static char FuncName[]={"SUMA_Offset_Lines"};
1290 char *so = NULL, *s=NULL;
1291 int nnl=0, nso_max, nso=0, i, slen;
1292 SUMA_Boolean LocalHead = NOPE;
1293
1294 SUMA_ENTRY;
1295
1296 if (!si) SUMA_RETURN(so);
1297
1298 SUMA_LH("Have string:>s=>%s<\n", si);
1299 slen = strlen(si);
1300 s = si; nnl = 1;
1301 while (*s != '\0') {
1302 if (*s == '\n') ++ nnl;
1303 ++s;
1304 }
1305 nso_max = slen+(nnl+1)*off;
1306 so = (char *)SUMA_calloc(nso_max, sizeof(char));
1307 nso = 0;
1308 for (i=0; i<off; ++i) so[nso++] = ' ';
1309 s = si;
1310 while (*s != '\0') {
1311 so[nso++] = *s;
1312 if (*s == '\n' && (strncmp(s+1,":NOF:",5))) {
1313 /* You could conceivably get rid of :NOF: here...
1314 but I get rid of it later so that's OK for now */
1315 for (i=0; i<off; ++i) so[nso++] = ' ';
1316 }
1317 ++s;
1318 }
1319
1320 so[nso] = '\0';
1321 SUMA_LH("Returning:>so=>%s>", so);
1322 SUMA_RETURN(so);
1323 }
1324
1325 /*
1326 Cut string sc from string s
1327 */
SUMA_Cut_String(char * s,char * sc)1328 char *SUMA_Cut_String(char *s, char *sc)
1329 {
1330 static char FuncName[]={"SUMA_Cut_String"};
1331 char *so, *ss=NULL;
1332 int nso=0;
1333
1334 SUMA_ENTRY;
1335
1336 if (!s || !sc || !(ss=strstr(s, sc))) {
1337 SUMA_RETURN(s);
1338 }
1339
1340 so = s;
1341 nso = 0;
1342 while (ss) {
1343 while (s < ss) {
1344 so[nso++]=*(s++);
1345 }
1346 s += strlen(sc);
1347 ss=strstr(s, sc);
1348 }
1349 /* copy till end */
1350 while (*s != '\0') {
1351 so[nso++]=*(s++);
1352 }
1353 so[nso] = '\0';
1354
1355 SUMA_RETURN(so);
1356 }
1357
1358 /*
1359 Given a string that contains sphinx markup like
1360
1361 s = "That's one :ref:`nice<tag>` pen."
1362
1363 the function SUMA_Sphinx_DeRef(s, ":ref:") returns :
1364
1365 "That's one nice pen."
1366
1367 Note that 's' cannot be a constant string because
1368 the function will need to write into it.
1369
1370 */
SUMA_Sphinx_DeRef(char * s,char * r)1371 char *SUMA_Sphinx_DeRef(char *s, char *r)
1372 {
1373 static char FuncName[]={"SUMA_Sphinx_DeRef"};
1374 char *so, *ss=NULL, *se=NULL, *sef=NULL;
1375 int nso=0;
1376
1377 SUMA_ENTRY;
1378
1379 if (!s || !r || !(ss=strstr(s, r))) {
1380 SUMA_RETURN(s);
1381 }
1382
1383 if (!strcmp(r,":LIT:")) { /* special case for non Sphinx directive */
1384 so = s;
1385 nso = 0;
1386 while (ss) {
1387 while (s < ss) {
1388 so[nso++]=*(s++);
1389 }
1390 if (nso && !SUMA_IS_PURE_BLANK(so[nso-1])) so[nso++] = ':';
1391 s += strlen(r);
1392 ss=strstr(s, r);
1393 }
1394 /* copy till end */
1395 while (*s != '\0') {
1396 so[nso++]=*(s++);
1397 }
1398 so[nso] = '\0';
1399
1400 SUMA_RETURN(so);
1401 }
1402
1403 /* Things of the form :DIREC:`something <SOMETHING>` */
1404 so = s;
1405 nso = 0;
1406 while (ss) {
1407 while (s < ss) {
1408 so[nso++]=*(s++);
1409 }
1410 s += strlen(r); /* s->`blah blah <REF>` */
1411 if (*s == '`') {
1412 s++; se = s;
1413 while (*se != '`' && *se != '\0') ++se;
1414 if (*se == '`') { /* found closing quote */
1415 sef = se;
1416 /* backup till you find > */
1417 while (se > s && *se != '>') { --se; }
1418 if (*se == '>') {
1419 /* backup till you find < */
1420 while (se > s && *se != '<') { --se; }
1421 if (*se == '<') { /* All good, copy blah blah */
1422 while (s < se) {
1423 so[nso++]=*(s++);
1424 }
1425 }
1426 } else {
1427 /*copy all between quotes */
1428 while (s < sef) {
1429 so[nso++]=*(s++);
1430 }
1431 }
1432 /* move s till after closing quote */
1433 s = sef+1;
1434 } else {
1435 SUMA_S_Warn("No closing forward quote after ref! in %s", so);
1436 }
1437 } else {
1438 SUMA_S_Warn("No forward quote after ref! in %s", so);
1439 }
1440 ss=strstr(s, r);
1441 }
1442 /* copy till end */
1443 while (*s != '\0') {
1444 so[nso++]=*(s++);
1445 }
1446 so[nso] = '\0';
1447
1448 SUMA_RETURN(so);
1449 }
1450
1451 /*
1452 Switch occurence of 'sc' in '*sp' with 'sw'
1453
1454 Function handles reallocation if
1455 sw is longer than sc. Just make sure *sp
1456 can be reallocated.
1457
1458 */
SUMA_Swap_String(char ** sp,char * sc,char * sw)1459 char *SUMA_Swap_String(char **sp, char *sc, char *sw)
1460 {
1461 static char FuncName[]={"SUMA_Swap_String"};
1462 char *so, *ss=NULL, *s=NULL;
1463 int nso=0, ww, nfound=0, nsc=0;
1464
1465 SUMA_ENTRY;
1466
1467 if (!sp) SUMA_RETURN(NULL);
1468
1469 if (!*sp || !sc || !sw || !(ss=strstr(*sp, sc))) {
1470 SUMA_RETURN(*sp);
1471 }
1472 nsc = strlen(sc);
1473 if (strlen(sw) > nsc) {
1474 /* Count the number of times sc is found */
1475 s = *sp; nfound=0;
1476 while (strstr(s, sc)) { ++nfound; s += nsc; }
1477 SUMA_S_Note("%d words found", nfound);
1478 nso = strlen(*sp)+nfound*(strlen(sw)-nsc+1);
1479 SUMA_S_Note("Reallocating from %ld to %d\n",
1480 strlen(*sp), nso);
1481 so = (char *)SUMA_realloc(*sp, nso*sizeof(char));
1482
1483 if (!so) {
1484 SUMA_S_Err("Failed to allocate %d chars",
1485 (int)(strlen(*sp)+strlen(sw)-nsc+1));
1486 SUMA_RETURN(s);
1487 }
1488 s = so; *sp = so;
1489 } else {
1490 s = *sp;
1491 }
1492
1493 so = s;
1494 nso = 0;
1495 while (ss) {
1496 while (s < ss) {
1497 so[nso++]=*(s++);
1498 }
1499 for (ww=0; ww<strlen(sw); ++ww) so[nso++]=sw[ww];
1500 s += nsc;
1501 ss=strstr(s, sc);
1502 }
1503 /* copy till end */
1504 while (*s != '\0') {
1505 so[nso++]=*(s++);
1506 }
1507 so[nso] = '\0';
1508
1509 SUMA_RETURN(so);
1510 }
1511
1512 /*
1513 Split a string into multiple ones using string sc
1514 as a deliminter.
1515
1516 Use SUMA_free_NI_str_array() to free NI_str_array*
1517
1518 */
SUMA_Split_String(char * s,char * sc)1519 NI_str_array *SUMA_Split_String(char *s, char *sc)
1520 {
1521 static char FuncName[]={"SUMA_Split_String"};
1522 char *so, *ss=NULL;
1523 int nso=0;
1524 NI_str_array *nisa = NULL;
1525
1526 SUMA_ENTRY;
1527
1528 if (!s || !sc) {
1529 SUMA_RETURN(NULL);
1530 }
1531
1532 nisa = NI_malloc(NI_str_array, sizeof(NI_str_array)) ; /* create output */
1533 nisa->num = 0 ; nisa->str = NULL ;
1534
1535 if (!(ss=strstr(s, sc))) { /* Just one found */
1536 nisa->str = NI_realloc( nisa->str , char*, sizeof(char *)*(nisa->num+1) ) ;
1537 nisa->str[nisa->num] = NI_malloc(char, ((strlen(s)+1)*sizeof(char)));
1538 strcat(nisa->str[nisa->num], s);
1539 nisa->num++;
1540 SUMA_RETURN(nisa);
1541 }
1542
1543 so = s;
1544 nso = 0;
1545 while (ss) {
1546 nisa->str = NI_realloc( nisa->str , char*, sizeof(char *)*(nisa->num+1) ) ;
1547 nisa->str[nisa->num] = NI_malloc(char, ((ss-s+1)*sizeof(char)));
1548 nso = 0;
1549 while (s < ss) {
1550 nisa->str[nisa->num][nso++]=*(s++);
1551 }
1552 nisa->str[nisa->num][nso]='\0'; ++nisa->num;
1553 s += strlen(sc);
1554 ss=strstr(s, sc);
1555 }
1556
1557 if (*s != '\0') {/* copy till end */
1558 nisa->str = NI_realloc( nisa->str , char*, sizeof(char *)*(nisa->num+1) ) ;
1559 nisa->str[nisa->num] = NI_malloc(char, ((strlen(s)+1)*sizeof(char)));
1560 nso = 0;
1561 while (*s != '\0') {
1562 nisa->str[nisa->num][nso++]=*(s++);
1563 }
1564 nisa->str[nisa->num][nso]='\0'; ++nisa->num;
1565 }
1566
1567 SUMA_RETURN(nisa);
1568 }
1569
1570 /*
1571 Cut out chunk of string 's' bracketed by 'sc0' and 'sc1', but do
1572 leave any portion between 'save' and 'sc1', if any exist.
1573
1574 This function is used to handle the markup:
1575
1576 :SPX:
1577 ........
1578 :DEF:
1579 ........
1580 :SPX:
1581
1582 */
SUMA_Cut_Between_String(char * s,char * sc0,char * sc1,char * save)1583 char *SUMA_Cut_Between_String(char *s, char *sc0, char *sc1, char *save)
1584 {
1585 static char FuncName[]={"SUMA_Cut_Between_String"};
1586 char *so, *ss0=NULL, *ss1=NULL, *ssa=NULL;
1587 int nso=0;
1588
1589 SUMA_ENTRY;
1590
1591 if (!sc1) sc1 = sc0;
1592
1593 if (!s || !sc1 || !sc0
1594 || !(ss0=strstr(s, sc0))
1595 || !(ss1=strstr(ss0+strlen(sc0), sc1)) || (ss1==ss0) ) {
1596 SUMA_RETURN(s);
1597 }
1598
1599 so = s;
1600 nso = 0;
1601 while (ss0 && ss1 && ss0 != ss1) {
1602 while (s < ss0) {
1603 so[nso++]=*(s++);
1604 }
1605
1606 if ( save && (ssa = af_strnstr(ss0+strlen(sc0), save, ss1-ss0) ) ) {
1607 s = ssa+strlen(save);
1608 while (s < ss1) {
1609 so[nso++]=*(s++);
1610 }
1611 s += strlen(sc1);
1612 } else {
1613 s += strlen(sc1)+ss1-ss0;
1614 }
1615
1616 ss0=strstr(s, sc0);
1617 if (ss0) ss1=strstr(ss0+strlen(sc0), sc1);
1618 }
1619 /* copy till end */
1620 while (*s != '\0') {
1621 so[nso++]=*(s++);
1622 }
1623 so[nso] = '\0';
1624
1625 SUMA_RETURN(so);
1626 }
1627
1628 /*
1629 A function to explain clipping plane behaviour.
1630 */
SUMA_Clipping_Planes_Help(FILE * fout,int forweb)1631 void SUMA_Clipping_Planes_Help(FILE *fout, int forweb)
1632 {
1633 static char FuncName[]={"SUMA_Sphinx_String_Edit_Help"};
1634 char *s0=NULL;
1635 char intro[]={
1636 "Clipping planes are used to select only the volume of the active \n"
1637 "surface objects that one is interested in. Several surface objects\n"
1638 "may be included in this volume but parts of the objects, outside the\n"
1639 "volume are invisible so as not to obscure the parts of interest\n"
1640 "\n"
1641 };
1642 char s[] = {
1643 "Enter clipping plane mode with Shift-Ctrl-C. There can be up to \n"
1644 "6 clipping planes numbered [1-6]. Each has a separate color \n"
1645 "being in order R G B C M Y. So plane 1 can be viewed in\n"
1646 "Red, plane 4 in Cyan, etc. The clipping planes may be viewed, \n"
1647 "as squares, with shift-C which toggles the squares on and off.\n\n"
1648 "Below, \"a number\" can be in range [1-6]. \"active\" means\n"
1649 "a clipping plane is clipping, not just visible. The \"selected\" \n"
1650 "plane is controlled with the action keys.\n\n"
1651 "Clipping planes can be scrolled along their normals and/or \n"
1652 "rotated around their tangent or cotangent axis. Currently, the \n"
1653 "increment of one is also the increment of the others. The \n"
1654 "default increment is 1.0 where the tilt increment is in degrees. \n"
1655 "The minus (-) key halves the increment from its current value. \n"
1656 "The + key doubles the increment from its current \n"
1657 "value. The '=' key resets the increment to 1.0.\n\n"
1658 "Key+mouse behavior\n"
1659 "------------------\n\n"
1660 "\tShift-Ctrl-C: Toggles in and out of clipping plane mode.\n"
1661 "\t\tA \"C\" will be visible in the viewer title\n"
1662 "\t\tbar when this mode is active.\n\n"
1663 "\tn: Successively add new clipping planes which are\n"
1664 "\t\tactive as they appear. (Initially, you might not see\n"
1665 "\t\tthem; just their clipping effect).\n\n"
1666 "\t[a number]: Toggle the clipping behavior of numbered plane on/off\n\n"
1667 "\talt/cmd/opt-[a number]: Select the numbered clipping plane (so\n"
1668 "\t\tyou can rotate/translate it with other key\n"
1669 "\t\tpresses). If the specified number > # planes,\n"
1670 "\t\tplanes are added up to the specified number.\n\n"
1671 "\t7: Toggle all active clipping planes on or off.\n\n"
1672 "\t0: Reset all clipping planes.\n\n"
1673 "\tctrl-f: Flip clipping direction of selected plane.\n\n"
1674 "\tShift+C: Toggle view of all active clipping planes on/off.\n\n"
1675 "\tAlt/Cmd/Opt+[scroll-wheel]: Translate a clipping plane in space.\n\n"
1676 "\ts: Scroll clipping plane inwards.\n\n"
1677 "\tShift-S: Scroll clipping plane outwards.\n\n"
1678 "\tAlt/Cmd/Opt+[up-down arrow key]: Rotate clipping plane around x-axis.\n\n"
1679 "\tAlt/Cmd/Opt+[left-right arrow key]: Rotate clipping plane around y-axis.\n\n"
1680 "\t+: Double the increment for rotation and scrolling.\n\n"
1681 "\t-: Halve the increment for rotation and scrolling.\n\n"
1682 "\t=: Reset the increment for rotation and scrolling.\n\n"
1683 "\n"
1684 };
1685
1686 if (!fout) fout = SUMA_STDERR;
1687
1688 fprintf(fout,
1689 "=============\nClipping Plane Mode\n=============\n\n");
1690 s0 = SUMA_copy_string(intro);
1691
1692 fprintf(fout, "%s\n%s", intro, s);
1693
1694 /*
1695 fprintf(fout,"\n%s\n", s0); SUMA_ifree(s0);
1696 if (forweb) {
1697 fprintf(fout,
1698 "Strings as defined in the source code::\n\n");
1699 s0 = SUMA_Offset_Lines(s,3);
1700 } else {
1701 s0 = SUMA_copy_string(s);
1702 }
1703 fprintf(fout,
1704 "%s\n -------\n", s0); SUMA_ifree(s0);
1705
1706 s0 = SUMA_copy_string(s);
1707 fprintf(fout,
1708 "\nEdited for display in AFNI or SUMA::\n\n%s\n -------\n",
1709 SUMA_Sphinx_String_Edit(&s0,TXT, forweb?3:0)); SUMA_ifree(s0);
1710
1711 s0 = SUMA_copy_string(s);
1712 fprintf(fout,"\nEdited for SPHINX::\n\n%s\n -------\n",
1713 SUMA_Sphinx_String_Edit(&s0,SPX, forweb?3:0));
1714
1715 if (forweb) {
1716 fprintf(fout,"\nAs would be displayed by SPHINX once compiled:\n\n%s"
1717 "\n -------\n",
1718 s0);
1719 }
1720 SUMA_ifree(s0);
1721 */
1722 return;
1723 }
1724
1725 /*
1726 A function to illustrate the use of markup gimmicks.
1727 if fout == NULL, use stderr for output.
1728 */
SUMA_Sphinx_String_Edit_Help(FILE * fout,int forweb)1729 void SUMA_Sphinx_String_Edit_Help(FILE *fout, int forweb)
1730 {
1731 static char FuncName[]={"SUMA_Sphinx_String_Edit_Help"};
1732 char *s0=NULL;
1733 char intro[]={
1734 "Function SUMA_Sphinx_String_Edit is used to take strings with \n"
1735 "the following special markers and return them formatted in either\n"
1736 "Sphinx or regular text. What follows is a list of special directives\n"
1737 "that change the output string depending on the desired format and a bunch\n"
1738 "examples to illustrate their use.\n"
1739 "\n"
1740 " :SPX: Hiding a SPHINX directive with minimal fanfare:\n"
1741 " Text between :SPX: markers does not appear in default output\n"
1742 " format.\n"
1743 " :SPX: Sphinx chunk :DEF: regular chunk :SPX:\n"
1744 " Use this to insert into a text string a section that is\n"
1745 " only displayed when Sphinx output is requested.\n"
1746 " It is also possible to provide an alternate section\n"
1747 " after the :DEF: marker between the opening and closing\n"
1748 " :SPX: markers. The alternate section is used when the\n"
1749 " requested output format is simple text.\n"
1750 "\n"
1751 " The example coming up next will show how we can have\n"
1752 " alternate output where a key press would be mentioned\n"
1753 " simply in the SUMA output but with a reference directive\n"
1754 " when SPHINX output is used:\n\n"
1755 " :LR: Replace this marker with a new line character for \n"
1756 " Sphinx output. Cut it out for regular output.\n"
1757 " :LIT: Replace this marker with '::\n' to mark an upoming literal\n"
1758 " paragraph for sphinx. If the character before :LIT:\n"
1759 " is a non blank, a ':' will terminate the sentence preceding\n"
1760 " the literal paragraph.\n"
1761 " For regular output, :LIT: is cut out if it is preceded by\n"
1762 " a blank. Otherwise it is replaced by a ':'\n"
1763 " Note that the literal paragraph must be indented relative to\n"
1764 " the preceding one.\n"
1765 "\n"
1766 " :ref:`Some Label <reference_key>` Leave such a block untouched for\n"
1767 " sphinx format. Replace whole thing\n"
1768 " with just 'Some Label' for default format.\n"
1769 "\n"
1770 " :[blanks]: Cut this marker out of string for Sphinx output,\n"
1771 " but keep all blanks and pads with two more in regular\n"
1772 " output to compensate for the ':' characters.\n"
1773 " Also, for the Sphinx format, a newline directly preceding\n"
1774 " the opening ':' gets cut out.\n"
1775 "\n"
1776 " '\\|' Escaped vertical bar are kept as such for Sphinx, but shown\n"
1777 " without the escape character in default output. This is\n"
1778 " needed to keep sphinx from considering words between vertical\n"
1779 " bars to be substitution references.\n"
1780 "\n"
1781 " :NOF: When found right after a new line, don't let function \n"
1782 " SUMA_Offset_Lines() insert any spaces. :NOF: is otherwise cut\n"
1783 " from all output\n"
1784 "\n"
1785 " :=ABIN: Replace with afni bin directory\n"
1786 " :=AFACE: Replace with afni face directory\n"
1787 "\n"
1788 "See function SUMA_Sphinx_String_Edit_Help() for a code sample.\n"
1789 "\n"
1790 };
1791 char s[] = {
1792 "Example 1:\n"
1793 "Below you will see a figure directive, but only for Sphinx format.\n"
1794 ":SPX:\n\n"
1795 ".. figure:: media/face_houstonbull.jpg\n"
1796 " :align: center\n"
1797 "\n:SPX:"
1798 "And now the rest of text continues...\n"
1799 "\n"
1800 "Example 2:\n"
1801 "Press buton :SPX::ref:`a <LC_a>`:DEF:'a':SPX: to attenuate...\n"
1802 "\n"
1803 "Example 2.1 (simpler version):\n"
1804 "Press buton :ref:`a <LC_a>` to attenuate...\n"
1805 "\n"
1806 "Example 3:\n"
1807 "For 'Trn' choose one of::LR:\n"
1808 " 0: No transparency.\n"
1809 ": :Surface is opaque.:LR:\n"
1810 " 8: 50% transparency.\n"
1811 ": :Surface is in cheese cloth transparency.:LR:\n"
1812 "\n"
1813 "Example 4:\n"
1814 "... or if '\\|T\\|' is used then ...\n"
1815 "\n"
1816 "Example 5:\n"
1817 "A sample file would be: test.1D.col with content:LIT:\n" \
1818 " 0 0.1 0.2 1 \n"
1819 " 1 0 1 0.8 \n"
1820 " 4 1 1 1 \n"
1821 " 7 1 0 1 \n"
1822 " 14 0.7 0.3 0 "
1823 "\n"
1824 };
1825
1826 if (!fout) fout = SUMA_STDERR;
1827
1828 if (forweb) {
1829 fprintf(fout,
1830 "Creating strings with special markup for classic and "
1831 "sphinx display::\n\n");
1832 s0 = SUMA_Offset_Lines(intro,3);
1833 } else {
1834 s0 = SUMA_copy_string(intro);
1835 }
1836
1837 fprintf(fout,"\n%s\n", s0); SUMA_ifree(s0);
1838
1839 if (forweb) {
1840 fprintf(fout,
1841 "Strings as defined in the source code::\n\n");
1842 s0 = SUMA_Offset_Lines(s,3);
1843 } else {
1844 s0 = SUMA_copy_string(s);
1845 }
1846 fprintf(fout,
1847 "%s\n -------\n", s0); SUMA_ifree(s0);
1848
1849 s0 = SUMA_copy_string(s);
1850 fprintf(fout,
1851 "\nEdited for display in AFNI or SUMA::\n\n%s\n -------\n",
1852 SUMA_Sphinx_String_Edit(&s0,TXT, forweb?3:0)); SUMA_ifree(s0);
1853
1854 s0 = SUMA_copy_string(s);
1855 fprintf(fout,"\nEdited for SPHINX::\n\n%s\n -------\n",
1856 SUMA_Sphinx_String_Edit(&s0,SPX, forweb?3:0));
1857
1858 if (forweb) {
1859 fprintf(fout,"\nAs would be displayed by SPHINX once compiled:\n\n%s"
1860 "\n -------\n",
1861 s0);
1862 }
1863 SUMA_ifree(s0);
1864
1865 return;
1866 }
1867
1868 /*
1869 Format the content of file fname for regular or sphinx output.
1870
1871 \sa SUMA_Sphinx_String_Edit()
1872
1873 \param fname: (char *)the filename
1874 \param targ: (TFORM) the format, 0 for regular, 1 for sphinx
1875 \param off: (int) Number of blank characters to insert at
1876 the beginning of each line that does not
1877 begin with :NOF:
1878 \return s (char *) The edited/formatted string.
1879 */
SUMA_Sphinx_File_Edit(char * fname,TFORM targ,int off)1880 char *SUMA_Sphinx_File_Edit(char *fname, TFORM targ, int off)
1881 {
1882 static char FuncName[]={"SUMA_Sphinx_File_Edit"};
1883 char *s=NULL;
1884 SUMA_Boolean LocalHead = NOPE;
1885
1886 SUMA_ENTRY;
1887
1888 if (!fname) SUMA_RETURN(s);
1889
1890 if (!SUMA_suck_file(fname, &s)) {
1891 SUMA_S_Err("Empty file or file not found");
1892 SUMA_RETURN(NULL);
1893 }
1894
1895 SUMA_RETURN(SUMA_Sphinx_String_Edit(&s, targ, off));
1896 }
1897
1898
1899 /*
1900 A function that allows me to format help strings for
1901 display in SUMA as was done in the past, and for
1902 fancier SPHINX formatted output.
1903
1904 \param suser (char **): Pointer to user's string
1905 \param targ (TFORM) the format, 0 for regular, 1 for sphinx
1906 \param off: (int) Number of blank characters to insert at
1907 the beginning of each line that does not
1908 begin with :NOF:
1909 \return: s (char *): Edited string. Note that usually
1910 s = *suser, unless reallocation
1911 was necessary. In that case the
1912 function takes care of freeing
1913 *suser and resetting it to
1914 new pointer.
1915
1916 \sa SUMA_Sphinx_String_Edit_Help() for documentation.
1917 */
1918
SUMA_Sphinx_String_Edit(char ** suser,TFORM targ,int off)1919 char *SUMA_Sphinx_String_Edit(char **suser, TFORM targ, int off)
1920 {
1921 static char FuncName[]={"SUMA_Sphinx_String_Edit"};
1922 char stmp[6]={""}, *s=NULL;
1923 SUMA_Boolean LocalHead = NOPE;
1924
1925 SUMA_ENTRY;
1926
1927 if (!suser || !(*suser)) SUMA_RETURN(s);
1928
1929 s = *suser;
1930
1931 switch (targ) {
1932 case WEB:
1933 case NO_FORMAT:
1934 SUMA_RETURN(s);
1935 break;
1936 case TXT: /* Default C output */
1937 SUMA_LH(">s=>\n%s\n<", s);
1938 SUMA_Cut_Between_String(s, ":SPX:", ":SPX:", ":DEF:");
1939 SUMA_Cut_String(s,":LR:"); SUMA_Cut_String(s,":NOF:");
1940 SUMA_Sphinx_LineSpacer(s, targ);
1941 sprintf(stmp,"\\|"); /* to avoid compile warning for
1942 direct use of "\|" in SUMA_Swap_String below */
1943 s = SUMA_Swap_String(&s, stmp,"|");
1944 SUMA_Sphinx_DeRef(s,":ref:");
1945 SUMA_Sphinx_DeRef(s,":term:");
1946 SUMA_Sphinx_DeRef(s, ":LIT:");
1947 SUMA_LH(">so=>\n%s\n<", s);
1948 SUMA_RETURN(s);
1949 break;
1950 case ASPX:
1951 case SPX: /* Sphinx */
1952 SUMA_Cut_String(
1953 SUMA_Cut_Between_String(s, ":DEF:", ":SPX:", NULL), ":SPX:");
1954 SUMA_Swap_String(&s, ":LR:","\n");
1955 SUMA_Sphinx_LineSpacer(s, targ);
1956 SUMA_Swap_String(&s, ":LIT:","::\n");
1957 SUMA_Cut_String(s,"(more with BHelp)");
1958 if (off) {
1959 *suser = SUMA_Offset_Lines(s,off);
1960 SUMA_ifree(s); s = *suser;
1961 }
1962 SUMA_Cut_String(s,":NOF:");
1963 SUMA_Cut_String(s,"(BHelp for more)");
1964 SUMA_Cut_String(s,"(much more with BHelp)");
1965 break;
1966 case TFORM_NOT_SET:
1967 SUMA_S_Warn("Targ not set, doing nothing.");
1968 SUMA_RETURN(s);
1969 break;
1970 default:
1971 SUMA_S_Err("What is TFORM of %d?", targ);
1972 SUMA_RETURN(s);
1973 break;
1974 }
1975
1976 s = SUMA_Sphinx_SetVars(&s, targ);
1977
1978 SUMA_RETURN(s);
1979 }
1980
1981
1982 /*
1983 Take the help output of program prog and
1984 return it as a string in sphinx format.
1985 */
sphinxize_prog_help(char * prog,int verb)1986 char *sphinxize_prog_help (char *prog, int verb)
1987 {
1988 static char FuncName[]={"sphinxize_prog_help"};
1989 char *oh=NULL;
1990 SUMA_Boolean LocalHead=NOPE;
1991
1992 SUMA_ENTRY;
1993
1994 if (!prog) {
1995 SUMA_RETURN(NULL);
1996 }
1997 /* Get the original help string */
1998 if (!(oh = phelp(prog, SPX, verb))) {
1999 SUMA_S_Err("Weird, dude");
2000 SUMA_RETURN(NULL);
2001 }
2002 SUMA_RETURN(sphinxize_prog_shelp(prog, oh, verb));
2003 }
2004
2005
SUMA_is_underline(char * sh,char * ul,int * nread)2006 int SUMA_is_underline(char *sh, char *ul, int *nread)
2007 {
2008 char lnc, *ish=NULL;
2009 int nunl;
2010
2011 if (!sh || *sh == '\0') return(0);
2012
2013 ish = sh;
2014 SUMA_SKIP_PURE_BLANK(sh,NULL);
2015 lnc = '\0'; nunl = 0;
2016 while (*sh != '\n' && *sh != 0) {
2017 if (SUMA_IS_UNDERLINE_CHAR(*sh)) {
2018 if (!lnc ) {
2019 /*
2020 fprintf(stderr,"1st underline");
2021 write_string(sh,NULL, "\n", 10, 0, stderr);
2022 */
2023 lnc = *sh;
2024 nunl = 1;
2025 } else {
2026 if (*sh == lnc) {
2027 ++nunl;
2028 } else {
2029 SUMA_SKIP_PURE_BLANK(sh,NULL);
2030 if (*sh == '\n') { /* Not a problem */
2031 --sh;
2032 } else { /* not an underline */
2033 lnc = '\0'; nunl = 0;
2034 break;
2035 }
2036 }
2037 }
2038 } else {
2039 SUMA_SKIP_PURE_BLANK(sh,NULL);
2040 if (*sh == '\n') { /* Not a problem */
2041 --sh;
2042 } else { /* not an underline */
2043 if (lnc) { lnc = '\0'; nunl = 0; }
2044 break;
2045 }
2046 }
2047 ++sh;
2048 }
2049
2050 SUMA_SKIP_TO_EOL(sh, NULL);
2051
2052 if (ul) *ul = lnc;
2053 if (nread) *nread=(sh-ish);
2054 /* write_string(ish ,"\nResult for:>>", "<<\n", 40, 0, stderr);
2055 fprintf(stderr,"out, nunl=%d\n", nunl); */
2056 return(nunl);
2057 }
2058
SUMA_Demote_Underlining(char * sh)2059 int SUMA_Demote_Underlining(char *sh)
2060 {
2061 static char FuncName[]={"SUMA_Demote_Underlining"};
2062 int ii = 0, jj = 0, nskip=0;
2063
2064 SUMA_ENTRY;
2065
2066 if (!sh || *sh == '\0') SUMA_RETURN(0);
2067
2068 ii = 0;
2069 while (sh[ii] != '\0') {
2070 if (SUMA_is_underline(sh+ii, NULL, &nskip)) {
2071 for (jj=0; jj<nskip; ++jj) {
2072 if (!SUMA_IS_PURE_BLANK(sh[ii])) sh[ii] = '^';
2073 ++ii;
2074 }
2075 } else {
2076 while (sh[ii] != '\n' && sh[ii] != '\0') ++ii;
2077 }
2078 if (sh[ii] != '\0') ++ii;
2079 }
2080
2081 SUMA_RETURN(1);
2082 }
2083
sphinxize_prog_shelp(char * prog,char * oh,int verb)2084 char *sphinxize_prog_shelp (char *prog, char *oh, int verb)
2085 {
2086 static char FuncName[]={"sphinxize_prog_shelp"};
2087 char **ws=NULL, *sout=NULL, *ofile=NULL, *bb=NULL;
2088 char *sh=NULL, *l=NULL, sins[1024]={""}, *ohc=NULL, *uoh=NULL;
2089 int N_ws=0, ishtp=0, nb = 0, i, k, nalloc, offs;
2090 SUMA_Boolean LocalHead=NOPE;
2091
2092 SUMA_ENTRY;
2093
2094 if (LocalHead) verb = 1;
2095
2096 if (!prog) {
2097 SUMA_RETURN(NULL);
2098 }
2099 if (verb) {
2100 if (oh) {
2101 SUMA_S_Note("Using passed help string");
2102 } else {
2103 SUMA_S_Note("Generating help string");
2104 }
2105 }
2106 /* Get the original help string */
2107 uoh = oh;
2108 if (!oh && !(oh = phelp(prog, SPX, verb))) {
2109 SUMA_S_Err("Weird, dude");
2110 SUMA_RETURN(NULL);
2111 }
2112 ohc = SUMA_copy_string(oh); /* make copy to avoid corrupting oh
2113 in approx_str_sort_all_popts */
2114
2115 /* Replace all underlining with something below level ----- */
2116 SUMA_Demote_Underlining(oh);
2117
2118 if (!(ws = approx_str_sort_all_popts(ohc, 1, &N_ws,
2119 1, NULL,
2120 NULL, NULL, 1, 0, '\\'))) {
2121
2122 SUMA_S_Err("Failed to sort all options");
2123 SUMA_ifree(oh); SUMA_ifree(ohc); SUMA_RETURN(NULL);
2124 }
2125 SUMA_ifree(ohc);
2126
2127 SUMA_LH("Have %d opts total.", N_ws);
2128 nalloc = 2*strlen(oh);
2129 sh = (char*)calloc(2*strlen(oh), sizeof(char));
2130 strcpy(sh, oh);
2131 sh[strlen(oh)]='\0';
2132
2133 snprintf(sins, 1020, ":tocdepth: 2\n\n"
2134 ".. _%s:\n\n%s\n", prog, prog); bb = sins+strlen(sins);
2135 for (i=0; i<strlen(prog); ++i) {*bb='-'; ++bb;}
2136 *bb='\0';
2137 SUMA_strncat(sins,"\n\n", 1020);
2138 SUMA_strncat(sins, "`Link to classic view <", 1020);
2139 SUMA_strncat(sins, web_prog_help_link(prog,0), 1020);
2140 SUMA_strncat(sins, ">`_\n\n", 1020);
2141
2142 sh = insert_in_string(&sh, sh, sins, &nalloc);
2143 for (i=0; i<N_ws; ++i) {
2144 if (ws[i]) {
2145 l = find_popt(sh,ws[i], &nb);
2146 if (l) {
2147 offs = l - sh -nb;
2148 if (verb) {
2149 fprintf(stderr,"Found option %s (nalloc=%d, len=%d) at::",
2150 ws[i], nalloc, (int)strlen(sh));
2151 write_string(l-nb, "\n", "\n",50, 0, stderr);
2152 }
2153 snprintf(sins, 1020, "\n.. _%s-%s:\n\n",
2154 prog, ws[i]);
2155 sh = insert_in_string(&sh, l-nb, sins, &nalloc);
2156 sh = insert_in_string(&sh, l+strlen(sins), "**", &nalloc);
2157 sh = insert_in_string(&sh, l+strlen(sins)+2+strlen(ws[i]),
2158 "**\\ ", &nalloc);
2159 if (verb) {
2160 write_string(sh+offs, " Now have\n", "\n\n",50, 1, stderr);
2161 }
2162 } else {
2163 fprintf(stderr,"Option %s not found\n\n", ws[i]);
2164 }
2165 SUMA_free(ws[i]); ws[i]=NULL;
2166 }
2167 }
2168 SUMA_free(ws); ws = NULL;
2169 if (!uoh) {
2170 SUMA_free(oh); oh = NULL;
2171 }
2172
2173 SUMA_RETURN(SUMA_Sphinx_String_Edit(&sh, SPX, 0));
2174 }
2175
2176
2177 /*
2178 Check if string begins with sphinx directives
2179 used in SUMA's code
2180 */
SUMA_Known_Sphinx_Dir(char * s)2181 SUMA_Boolean SUMA_Known_Sphinx_Dir(char *s)
2182 {
2183 static char FuncName[]={"SUMA_Known_Sphinx_Dir"};
2184 if (!s) return(NOPE);
2185 if (!strncmp(s,":ref:",5)) return(YUP);
2186 if (!strncmp(s,":term:",6)) return(YUP);
2187 return(NOPE);
2188 }
2189
2190 /*
2191 Check if string begins with AFNI sphinx directives
2192 used in SUMA's code
2193 */
SUMA_Known_Sphinx_ADir(char * s)2194 SUMA_Boolean SUMA_Known_Sphinx_ADir(char *s)
2195 {
2196 static char FuncName[]={"SUMA_Known_Sphinx_ADir"};
2197 if (!s) return(NOPE);
2198 if (!strncmp(s,":LR:",4)) return(YUP);
2199 if (!strncmp(s,":NOF:",5)) return(YUP);
2200 if (!strncmp(s,":LIT:",5)) return(YUP);
2201 if (!strncmp(s,":SPX:",5)) return(YUP);
2202 if (!strncmp(s,":DEF:",5)) return(YUP);
2203 if (!strncmp(s,":=ABIN:",7)) return(YUP);
2204 if (!strncmp(s,":=AFACE:",8)) return(YUP);
2205 return(NOPE);
2206 }
2207
2208
2209 /*
2210
2211 Handle white space markup
2212
2213 { char *sdo, so[]={
2214 "Choose the rendering mode for this surface.\n"
2215 " Viewer: Surface's rendering mode is set "
2216 ": :by the viewer's setting which can "
2217 ": :be changed with the 'p' option.:LR:\n"
2218 " Fill: Shaded rendering mode.:LR:\n"
2219 " Line: Mesh rendering mode.:LR:\n"
2220 " Points: Points rendering mode.:LR:\n"};
2221
2222 sdo = SUMA_Sphinx_LineSpacer(so , 1);
2223 fprintf(SUMA_STDERR,"%s\n", sdo);
2224 }
2225
2226 */
SUMA_Sphinx_LineSpacer(char * s,TFORM targ)2227 char *SUMA_Sphinx_LineSpacer(char *s, TFORM targ)
2228 {
2229 static char FuncName[]={"SUMA_Sphinx_LineSpacer"};
2230 int bln, ns, nso, slen;
2231 char *so=NULL;
2232
2233 SUMA_ENTRY;
2234
2235 /* search for :.*: */
2236
2237 if (!s) SUMA_RETURN(s);
2238
2239 slen = strlen(s);
2240
2241 ns = 0; nso = 0;
2242 so = s;
2243 while (s[ns]) {
2244 if (s[ns] == ':' && ns < slen-1) {
2245 bln=0;
2246 while (s[ns+bln+1] && SUMA_IS_PURE_BLANK(s[ns+1+bln])) { ++bln; }
2247 if (bln > 0 && s[ns+1+bln] == ':' &&
2248 !SUMA_Known_Sphinx_Dir(s+ns+1+bln) &&
2249 !SUMA_Known_Sphinx_ADir(s+ns+1+bln)) {
2250 /* Have blank gap */
2251 switch(targ) {
2252 case TXT: /* just replace : with space */
2253 if (nso>1 && SUMA_IS_PURE_BLANK(so[nso-1])) {
2254 so[nso-1] = '\n';/* Need newline to make it come out nice */
2255 }
2256 so[nso++] = ' '; ++ns;
2257 while(s[ns] != ':') { so[nso++] = s[ns++]; }
2258 so[nso++] = ' '; ++ns;
2259 break;
2260 case ASPX:
2261 case SPX: /* remove all spaces */
2262 /* remove preceding new line just to keep superfluous
2263 new line characters that were there for the purpose of keeping
2264 the output width short. Do not remove the newline if there
2265 is two of them in a row, or there is certain punctuation
2266 before the newline.*/
2267 if (nso>1 && so[nso-1] == '\n' &&
2268 (so[nso-2] != '\n' && so[nso-2] != ':')) {
2269 so[nso-1]=' ';
2270 }
2271 ns += bln+2;
2272 break;
2273 case WEB:
2274 case NO_FORMAT: /* You asked for it! */
2275 break;
2276 default:
2277 SUMA_S_Warn("Not equipped for this %d!", targ);
2278 break;
2279 }
2280 } else {
2281 /* nothing, copy character and move on */
2282 so[nso++] = s[ns++];
2283 }
2284 } else {
2285 so[nso++] = s[ns++];
2286 }
2287 }
2288 so[nso] = '\0';
2289 SUMA_RETURN(so);
2290 }
2291
2292 typedef struct {
2293 int where;
2294 int norig;
2295 char *what;
2296 } insertion;
2297
2298 /*!
2299 Replace strings like ":=ABIN:"
2300 and ":=AFACE:" with their values.
2301
2302 This function will reallocate for whatever
2303 is in us and set *us accordingly.
2304
2305 The function returns *us, whether or not *us
2306 was changed.
2307 */
SUMA_Sphinx_SetVars(char ** us,TFORM targ)2308 char *SUMA_Sphinx_SetVars(char **us, TFORM targ)
2309 {
2310 static char FuncName[]={"SUMA_Sphinx_SetVars"};
2311 insertion *ins=NULL;
2312 int N_ins=0, ntok=0, ns=0, nso=0, ii=0, jj=0, oo=0, N_ins_alloc, nextra=0;
2313 char *s=NULL, *ss=NULL, *so=NULL, *tok=NULL, *rep=NULL;
2314
2315 ENTRY("SUMA_Sphinx_SetVars");
2316
2317 if (!us || !*us) RETURN(NULL);
2318
2319 /* Maximum number of insertions */
2320 N_ins_alloc = 0; N_ins = 0;
2321 s = *us;
2322 while((ss = strstr(s, ":="))) {
2323 tok = ":=ABIN:"; ntok = strlen(tok);
2324 if (strstr(ss,tok)) {
2325 if (N_ins+1 > N_ins_alloc) {
2326 N_ins_alloc +=100;
2327 ins = (insertion*)realloc(ins, sizeof(insertion)*N_ins_alloc);
2328 }
2329 if (targ == SPX || targ == ASPX) {
2330 /* Looks like sphinx likes // for absolute path references
2331 like .. image:: //Users/home/abin/...
2332 otherwise, the first slash gets dropped */
2333 ins[N_ins].what =
2334 SUMA_append_replace_string("/",THD_abindir(0),"",2);
2335 } else {
2336 ins[N_ins].what = THD_abindir(0);
2337 }
2338 ins[N_ins].where = ss-*us;
2339 ins[N_ins].norig = ntok;
2340 nextra += (strlen(ins[N_ins].what)-ntok);
2341 ++N_ins;
2342 s = ss + ntok; continue;
2343 }
2344 tok = ":=AFACE:"; ntok = strlen(tok);
2345 if (strstr(ss,tok)) {
2346 if (N_ins+1 > N_ins_alloc) {
2347 N_ins_alloc +=100;
2348 ins = (insertion*)realloc(ins, sizeof(insertion)*N_ins_alloc);
2349 }
2350 if (targ == SPX || targ == ASPX) {
2351 ins[N_ins].what =
2352 SUMA_append_replace_string("/",THD_facedir(0),"",2);
2353 } else {
2354 ins[N_ins].what = THD_facedir(0);
2355 }
2356 ins[N_ins].where = ss-*us;
2357 ins[N_ins].norig = ntok;
2358 nextra += (strlen(ins[N_ins].what)-ntok);
2359 ++N_ins;
2360 s = ss + ntok; continue;
2361 }
2362 s += 2;
2363 }
2364
2365 if (!N_ins) {
2366 RETURN(*us); /* nothing to be done */
2367 }
2368
2369 /* Allocate for output */
2370 ns = strlen(*us);
2371 nso = nextra+1+ns;
2372 if (!(so = (char *)calloc(nso, sizeof(char)))) {
2373 ERROR_message("Failed to allocate for %d chars, RETURNing original sin",
2374 nso);
2375 for (ii=0; ii<N_ins; ++ii) {
2376 if (ins[ii].what) free(ins[ii].what); ins[ii].what=NULL;
2377 }
2378 if (ins) free(ins); ins = NULL;
2379 RETURN(*us);
2380 }
2381
2382 /* Copy and replace */
2383 s = *us; oo = 0;
2384 ii = 0; jj = 0;
2385 while (ii<ns && jj < N_ins) {
2386 if (ii<ins[jj].where) {
2387 so[oo++] = s[ii++];
2388 } else { /* insert jj */
2389 rep = ins[jj].what;
2390 while (*rep) {
2391 so[oo++] = *rep; ++rep;
2392 }
2393 ii += ins[jj].norig;
2394 free(ins[jj].what); ins[jj].what=NULL;
2395 ++jj;
2396 }
2397 }
2398 free(ins); ins = NULL;
2399
2400 /* finish last bit */
2401 while (ii<ns) so[oo++] = s[ii++];
2402 so[oo++] = '\0';
2403
2404 free(*us); *us = so;
2405
2406 RETURN(so);
2407 }
2408
2409 /*
2410 A function that formats the content of a printf statement
2411 to handle Sphinx markups as well as our own, before writing
2412 the results.
2413
2414 \param targ: (int) 0 -- Regular output
2415 1 -- Sphinx output
2416 \param off: (int) Number of blank characters to insert at
2417 the beginning of each line that does not
2418 begin with :NOF:
2419 \param fout: (FILE *) Where do you want the output?
2420 NULL == stdout
2421 \param ...: Whatever you'd put in an printf() call.
2422
2423 \return (int): Whatever the final fprintf returns.
2424
2425 \sa macros: sphinx_printf(int targ, char *str, ...)
2426 and sphinx_fprintf(int targ, FILE *fout, char *str, ...)
2427
2428 Examples:
2429 {
2430 sphinx_printf(1,NULL);
2431 sphinx_printf(1,"Hello\n");
2432 sphinx_printf(1,"Hello %s, %d donuts\n","Jimminy Cricket", 12344554);
2433 }
2434
2435
2436 */
sphinx_offprintf(TFORM targ,int off,FILE * fout,char * str,...)2437 int sphinx_offprintf(TFORM targ, int off, FILE *fout, char *str, ... )
2438 {
2439 static char FuncName[]={"sphinx_offprintf"};
2440 char *s=NULL;
2441 va_list vararg_ptr , saved_copy;
2442 int rr = 1, nalloc, nchunk=30000, nout, toolittle, ns;
2443 SUMA_Boolean LocalHead = NOPE;
2444
2445 SUMA_ENTRY;
2446
2447 if (!fout) fout = stdout;
2448
2449 if (!str) SUMA_RETURN(rr);
2450
2451 va_copy( saved_copy, vararg_ptr );
2452 va_start( vararg_ptr , str) ;
2453 nalloc = strlen(str)+nchunk;
2454 if (!(s = (char *)SUMA_calloc(nalloc, sizeof(char)))) {
2455 SUMA_S_Err("Failed to allocate for %d chars", nalloc);
2456 SUMA_RETURN(rr);
2457 }
2458 s[0] = s[nalloc-1] = '\0';
2459 do {
2460 nout = vsnprintf (s, nalloc*sizeof(char), str, vararg_ptr);
2461 SUMA_LH("nout=%d", nout);
2462 if (nout < 0) {
2463 SUMA_SL_Err("Error reported by vsnprintf");
2464 SUMA_strncat(s,"Error SUMA_StringAppend_va:"
2465 " ***Error reported by vsnprintf", nalloc-1);
2466 SUMA_free(s);
2467 SUMA_RETURN(1);
2468 }
2469 toolittle = 0;
2470 if (nout >= nalloc) {
2471 SUMA_LH("Reallocating %d more", nchunk);
2472 toolittle = 1;
2473 nalloc += nchunk;
2474 if (!(s = (char *)SUMA_realloc(s, sizeof(char)*(nalloc)))){
2475 SUMA_S_Err("Failed to allocate for %d chars", nalloc);
2476 SUMA_RETURN(rr);
2477 }
2478 s[0] = s[nalloc-1] = '\0';
2479 va_end(vararg_ptr); /* clean this copy then recreate */
2480 va_copy( vararg_ptr, saved_copy);
2481 va_start(vararg_ptr, str);
2482 }
2483 } while(toolittle);
2484
2485 va_end(vararg_ptr); /* cleanup */
2486 va_end(saved_copy);
2487
2488 ns = strlen(s);
2489 s = (char *)SUMA_realloc(s, sizeof(char)*(ns+1));s[ns]='\0';
2490
2491 if ((s = SUMA_Sphinx_String_Edit(&s, targ, off))) {
2492 rr = fprintf(fout,"%s",s);
2493 SUMA_ifree(s);
2494 } else {
2495 ERROR_message("Failed miserably");
2496 rr = 1;
2497 }
2498
2499 SUMA_RETURN(rr);
2500 }
2501
2502