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