1 /*
2    Assortment of generic functions needed to support I/O functions
3    in suma_datasets.c
4 */
5 
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <assert.h>
9 #include <string.h>
10 #include <sys/time.h>
11 #include <math.h>
12 #include "mrilib.h"
13 #include "niml.h"
14 #include "../niml/niml_private.h"
15 #include "suma_objs.h" /* 21 Apr 2020 */
16 /*------------------------------------------------------------*/
17 
18 #if defined SUMA_COMPILED
19    extern SUMA_CommonFields *SUMAg_CF;
20    extern int SUMAg_N_DOv;
21    extern SUMA_DO *SUMAg_DOv;
22 #endif
23 
24 /*------------------------------------------------------------*/
25 
26 /*! ********** Begin Multiplexed Vectors Functions ************* */
27 /*!
28    \brief A function to create a multiplexed vector structure
29    \param tp (SUMA_VARTYPE) type of data in array. Only SUMA_double for now.
30    \param N_dims (int) Number of array dimensions
31    \param *dims (int) Size of each dimension
32    \param first_dim_first (byte) 1 = first dimension first
33                                  (column major, for a matrix)
34                                  only 1 is accepted at the moment
35    \return mxv (SUMA_MX_VEC *) the structure with enough memory allocated to
36                                the pointer of interest.
37                                (mxv->dv for SUMA_double).
38    - Use SUMA_FreeMxVec to free mxv
39    - see macros mxvd* to handle data in mxv
40 */
SUMA_NewMxNullVec(SUMA_VARTYPE tp,int N_dims,int * dims,byte first_dim_first)41 SUMA_MX_VEC *SUMA_NewMxNullVec(SUMA_VARTYPE tp, int N_dims, int *dims, byte first_dim_first)
42 {
43    static char FuncName[]={"SUMA_NewMxNullVec"};
44    SUMA_MX_VEC *mxv=NULL;
45    int n_vals=0, i = 0;
46 
47    SUMA_ENTRY;
48 
49    if (first_dim_first != 1) {
50       SUMA_SL_Err("first_dim_first must be 1 for now");
51       SUMA_RETURN(NULL);
52    }
53 
54    if (N_dims < 1) {
55       SUMA_SL_Err("N_dims < 1");
56       SUMA_RETURN(NULL);
57    } else if (N_dims > SUMA_MX_VEC_MAX_DIMS-1) {
58       SUMA_SL_Err("N_dims > 49");
59       SUMA_RETURN(NULL);
60    }
61    if (!dims) {
62       SUMA_SL_Err("NULL dims");
63       SUMA_RETURN(NULL);
64    }
65    mxv = (SUMA_MX_VEC *)SUMA_calloc(1,sizeof(SUMA_MX_VEC));
66    mxv->fdf = 1;
67    mxv->bv = NULL;
68    mxv->sv = NULL;
69    mxv->iv = NULL;
70    mxv->fv = NULL;
71    mxv->dv = NULL;
72    mxv->cv = NULL;
73    mxv->v = NULL;
74    mxv->m = NULL;
75    mxv->N_dims = N_dims;
76    mxv->N_vals = dims[0];
77    mxv->dims[0] = dims[0];
78    for (i=1; i<N_dims; ++i) {
79       mxv->N_vals = mxv->N_vals * dims[i];
80       mxv->dims[i] = dims[i];
81    }
82    for (i=N_dims; i< SUMA_MX_VEC_MAX_DIMS; ++i) mxv->dims[i] = 1;
83    if (mxv->N_vals <= 0) {
84       SUMA_SL_Err("Negative dims");
85       SUMA_free(mxv); SUMA_RETURN(NULL);
86    }
87 
88    mxv->tp = tp;
89 
90    /* mutlipliers for first_dim_first */
91    mxv->fdfm[0] = mxv->dims[0];
92    for (i=1; i<N_dims-1; ++i) {
93       mxv->fdfm[i] = mxv->fdfm[i-1]*mxv->dims[i];
94    }
95 
96    SUMA_RETURN(mxv);
97 
98 }
99 
SUMA_MxVecInit(SUMA_MX_VEC * mxv,void * val)100 int SUMA_MxVecInit(SUMA_MX_VEC *mxv, void *val)
101 {
102    static char FuncName[]={"SUMA_MxVecInit"};
103    int i, ii;
104    byte bb;
105    short ss;
106    float ff;
107    complex cc;
108    double dd;
109 
110    SUMA_ENTRY;
111 
112    if (!mxv->v) {
113       SUMA_S_Err("null vector pointer");
114       SUMA_RETURN(0);
115    }
116    switch (mxv->tp) {
117       case SUMA_byte:
118          bb = *((byte *)val);
119          mxv->bv = (byte *)mxv->v;
120          for (i=0;i<mxv->N_vals;++i) mxv->bv[i] = bb;
121          break;
122       case SUMA_short:
123          ss = *((short *)val);
124          mxv->sv = (short *)mxv->v;
125          for (i=0;i<mxv->N_vals;++i) mxv->sv[i] = ss;
126          break;
127       case SUMA_int:
128          ii = *((int *)val);
129          mxv->iv = (int *)mxv->v;
130          for (i=0;i<mxv->N_vals;++i) mxv->iv[i] = ii;
131          break;
132       case SUMA_float:
133          ff = *((float *)val);
134          mxv->fv = (float *)mxv->v;
135          for (i=0;i<mxv->N_vals;++i) mxv->fv[i] = ff;
136          break;
137       case SUMA_double:
138          dd = *((double *)val);
139          mxv->dv = (double *)mxv->v;
140          for (i=0;i<mxv->N_vals;++i)  mxv->dv[i] = 1.0;
141          break;
142       case SUMA_complex:
143          cc = *((complex *)val);
144          mxv->cv = (complex *)mxv->v;
145          for (i=0; i<mxv->N_vals; ++i)
146             { mxv->cv[i].r = cc.r; mxv->cv[i].i = cc.i; }
147          break;
148       default:
149          SUMA_SL_Err("Bad type");
150          SUMA_RETURN(0);
151    }
152 
153    SUMA_RETURN(1);
154 }
SUMA_NewMxAllocVec(SUMA_MX_VEC * mxv)155 int SUMA_NewMxAllocVec(SUMA_MX_VEC *mxv)
156 {
157    static char FuncName[]={"SUMA_NewMxAllocVec"};
158    int i, ip;
159 
160    SUMA_ENTRY;
161 
162    if (mxv->v || mxv->bv || mxv->sv || mxv->iv || mxv->fv || mxv->dv || mxv->cv || mxv->m) {
163       SUMA_S_Err("Non null vector pointers");
164       SUMA_RETURN(0);
165    }
166 
167    switch (mxv->tp) {
168       case SUMA_byte:
169          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(byte));
170          mxv->bv = (byte *)mxv->v;
171          break;
172       case SUMA_short:
173          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(short));
174          mxv->sv = (short *)mxv->v;
175          break;
176       case SUMA_int:
177          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(int));
178          mxv->iv = (int *)mxv->v;
179          break;
180       case SUMA_float:
181          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(float));
182          mxv->fv = (float *)mxv->v;
183          break;
184       case SUMA_double:
185          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(double));
186          mxv->dv = (double *)mxv->v;
187          break;
188       case SUMA_complex:
189          mxv->v = SUMA_calloc(mxv->N_vals, sizeof(complex));
190          mxv->cv = (complex *)mxv->v;
191          if (mxv->v) {
192             for (i=0; i<mxv->N_vals; ++i) {
193                mxv->cv[i].r = 0.0; mxv->cv[i].i = 0.0;
194             }
195          }
196          break;
197       default:
198          SUMA_SL_Err("Bad type");
199          SUMA_RETURN(0);
200    }
201 
202    if (!mxv->v) {
203       SUMA_SL_Crit("Failed to allocate");
204       SUMA_RETURN(0);
205    }
206    mxv->m = NULL; /* just to be sure */
207    SUMA_RETURN(1);
208 }
209 
SUMA_NewMxVec(SUMA_VARTYPE tp,int N_dims,int * dims,byte first_dim_first)210 SUMA_MX_VEC *SUMA_NewMxVec(SUMA_VARTYPE tp, int N_dims, int *dims,
211                            byte first_dim_first)
212 {
213    static char FuncName[]={"SUMA_NewMxVec"};
214    SUMA_MX_VEC *mxv=NULL;
215    int n_vals=0, i = 0;
216    SUMA_Boolean LocalHead = NOPE;
217 
218    SUMA_ENTRY;
219 
220    /* blank holder */
221    mxv = SUMA_NewMxNullVec(tp, N_dims,  dims,  first_dim_first);
222    if (LocalHead) SUMA_ShowMxVec(mxv, 1, NULL, "\nmxv in NewMx\n");
223    /* allocator */
224    if (!SUMA_NewMxAllocVec(mxv)) {
225       SUMA_SL_Crit("Failed to allocate");
226       SUMA_free(mxv); SUMA_RETURN(NULL);
227    }
228 
229    SUMA_RETURN(mxv);
230 }
231 
SUMA_VecToMxVec(SUMA_VARTYPE tp,int N_dims,int * dims,byte first_dim_first,void * vec)232 SUMA_MX_VEC *SUMA_VecToMxVec(SUMA_VARTYPE tp, int N_dims, int *dims,
233                              byte first_dim_first, void *vec)
234 {
235    static char FuncName[]={"SUMA_VecToMxVec"};
236    SUMA_MX_VEC *mxv=NULL;
237    int n_vals=0, i = 0;
238 
239    SUMA_ENTRY;
240 
241    /* blank holder */
242    mxv = SUMA_NewMxNullVec(tp, N_dims,  dims,  first_dim_first);
243 
244    if (!vec) SUMA_RETURN(mxv);
245 
246    mxv->v = vec;
247    switch (mxv->tp) {
248       case SUMA_byte:
249          mxv->bv = (byte *)mxv->v;
250          break;
251       case SUMA_short:
252          mxv->sv = (short *)mxv->v;
253          break;
254       case SUMA_int:
255          mxv->iv = (int *)mxv->v;
256          break;
257       case SUMA_float:
258          mxv->fv = (float *)mxv->v;
259          break;
260       case SUMA_double:
261          mxv->dv = (double *)mxv->v;
262          break;
263       case SUMA_complex:
264          mxv->cv = (complex *)mxv->v;
265          break;
266       default:
267          SUMA_SL_Err("Bad type");
268          SUMA_free(mxv); SUMA_RETURN(NULL);
269    }
270 
271    SUMA_RETURN(mxv);
272 }
273 
274 
SUMA_FreeMxVec(SUMA_MX_VEC * mxv)275 SUMA_MX_VEC *SUMA_FreeMxVec(SUMA_MX_VEC *mxv)
276 {
277    static char FuncName[]={"SUMA_FreeMxVec"};
278    int i;
279    SUMA_ENTRY;
280 
281    if (mxv) {
282       if (mxv->v) SUMA_free(mxv->v);
283       if (mxv->m) {
284          if (mxv->m->elts) {
285             for (i=0; i<mxv->m->rows; ++i)
286                if (mxv->m->elts[i]) SUMA_free(mxv->m->elts[i]);
287             SUMA_free(mxv->m->elts);
288          }
289          SUMA_free(mxv->m);
290       }
291       mxv->m = NULL;
292       SUMA_free(mxv);
293    }
294 
295    SUMA_RETURN(NULL);
296 }
297 
SUMA_ShowMxVec(SUMA_MX_VEC * mxv,int detail,FILE * out,char * title)298 void SUMA_ShowMxVec (SUMA_MX_VEC *mxv, int detail, FILE *out, char *title)
299 {
300    static char FuncName[]={"SUMA_ShowMxVec"};
301    char *si = NULL;
302 
303    SUMA_ENTRY;
304 
305    if (!out) out = SUMA_STDERR;
306 
307    si = SUMA_MxVec_Info(mxv, detail, title);
308 
309    fprintf(out,"%s\n", si);
310 
311    if (si) SUMA_free(si); si = NULL;
312 
313    SUMA_RETURNe;
314 
315 }
316 
317 /*!
318    \brief Function to return info on SUMA_DSET
319 
320    - You must free the returned string on your own
321    \sa SUMA_ShowDset
322 */
SUMA_MxVec_Info(SUMA_MX_VEC * mxv,int detail,char * title)323 char *SUMA_MxVec_Info (SUMA_MX_VEC *mxv, int detail, char *title)
324 {
325    static char FuncName[]={"SUMA_MxVec_Info"};
326    int i, imx = 5, j;
327    SUMA_COL_TYPE ctp;
328    char *s=NULL, stmp[200];
329    SUMA_STRING *SS=NULL;
330    SUMA_Boolean LocalHead = NOPE;
331 
332    SUMA_ENTRY;
333 
334    SS = SUMA_StringAppend(NULL, NULL);
335 
336    if (mxv) {
337       SUMA_LH("Have mxv");
338       if (title) SS = SUMA_StringAppend_va(SS, "%s", title);
339       SS = SUMA_StringAppend_va(SS, "mxv: %p\n"
340                                     "data type: %d (%s)\n"
341                                     "fdf: %d\n"
342                                     "N_dims: %d\n"
343                                     "N_vals: %d\n"
344                                     , mxv, mxv->tp,
345                                     SUMA_VarType2CTypeName(mxv->tp), mxv->fdf,
346                                     mxv->N_dims, mxv->N_vals);
347       if (mxv->m) {
348          SUMA_LH("Working m");
349          SS = SUMA_StringAppend_va(SS,
350             "m is setup (rows: %d, cols: %d)\n", mxv->m->rows, mxv->m->cols);
351          SUMA_LH("Working m");
352          for (i=0; i < mxv->m->rows && i < imx; ++i) {
353             for (j=0; j < mxv->m->cols && j < imx; ++j) {
354                SUMA_LH("elts[][]\n");
355                SS = SUMA_StringAppend_va(SS,"%g   ", mxv->m->elts[i][j]);
356             }
357             if (mxv->m->cols > imx) SS = SUMA_StringAppend(SS,"...\n");
358             else SS = SUMA_StringAppend(SS,"\n");
359          }
360          if (mxv->m->rows > imx)
361             SS = SUMA_StringAppend(SS,"...  ...   ...   ...   ...\n");
362          else SS = SUMA_StringAppend(SS,"\n");
363          SUMA_LH("Done with m");
364       } else {
365          SUMA_LH("NULL m");
366          SS = SUMA_StringAppend(SS, "m is NULL\n");
367       }
368       SUMA_LH("dims time");
369       SS = SUMA_StringAppend_va(SS, "dims: ");
370       for (i=0; i<mxv->N_dims; ++i) {
371          SS = SUMA_StringAppend_va(SS, "%d ", mxv->dims[i]);
372       }
373       SS = SUMA_StringAppend_va(SS, "\n");
374 
375       if (mxv->v) {
376          if (detail < 0) {
377             imx = mxv->N_vals;
378          } else {
379             imx = 5*detail;
380          }
381          s = SUMA_ShowMeSome( mxv->v,
382                               mxv->tp,
383                               mxv->N_vals, imx, NULL);
384          SS = SUMA_StringAppend_va(SS, "         %s\n", s);
385          SUMA_free(s); s = NULL;
386       } else SS = SUMA_StringAppend_va(SS, "         NULL\n");
387    } else {
388       SS = SUMA_StringAppend(SS, "NULL mxv.");
389    }
390 
391    SUMA_SS2S(SS, s);
392 
393    SUMA_RETURN(s);
394 }
395 
396 /****************** Command line and Env parsing functions. ************** */
397 /****************** Help strings. Filename parsing.         ************** */
398 
399 static int no_suma_rc_found;
400 
NoSumaRcFound(void)401 int NoSumaRcFound (void) { return (no_suma_rc_found);}
402 
403 /*!
404    Return number of Icosahedron
405    depth (int) depth of subdivision (value of -ld or -rd in  CreateIcosahedron)
406    bin (byte) 1 --> equiv to -rd in CreateIcosahedron
407               0 -->          -ld in CreateIcosahedron
408    what (char) 'v' or 'n' :return number of nodes
409                't': return number of triangles
410                'e': return number of edges
411 */
SUMA_IcoNums(int depth,byte bin,char what)412 int SUMA_IcoNums(int depth, byte bin, char what) {
413    int dd=-1;
414    if (depth < 0) return (dd);
415    if (bin) { /* binary subdivisions */
416       switch (what){
417          case 'v':
418          case 'n':
419             dd = (int)(pow(2, (2*depth)))*10 + 2;
420             break;
421          case 't':
422             dd = (int)(pow(2, (2*depth)))*20;
423             break;
424          case 'e':
425             dd = (int)(pow(2, (2*depth)))*30;
426             break;
427       }
428    } else { /* linear subdivisions */
429       switch (what){
430          case 'v':
431          case 'n':
432             dd = 2 + (10 * depth * depth);
433             break;
434          case 't':
435             dd = 20 * depth * depth;
436             break;
437          case 'e':
438             dd = 30 * depth * depth;
439             break;
440       }
441    }
442    return(dd);
443 }
444 
445 /*!
446    \brief load the environment variables first from
447    $HOME/.sumarc and $HOME/.afnirc
448    if HOME is not defined then try .afnirc and .sumarc
449    Shameless wrapper for AFNI_process_environ
450 
451    No fancies here, this function is called before CommonFields
452 */
SUMA_process_environ(void)453 void SUMA_process_environ(void)
454 {
455    static char FuncName[]={"SUMA_process_environ"};
456    struct stat stbuf;
457    char *sumarc = NULL, *homeenv=NULL;
458    SUMA_Boolean LocalHead = NOPE;
459 
460    no_suma_rc_found = 0;
461 
462    if (LocalHead)
463          fprintf (SUMA_STDERR,"%s: Entering SUMA_process_environ\n", FuncName);
464 
465    sumarc = (char *)malloc( sizeof(char)*
466                            (SUMA_MAX_NAME_LENGTH+SUMA_MAX_DIR_LENGTH+1));
467 
468    /* load the environment variables from .sumarc and .afnirc*/
469    homeenv = getenv("HOME");
470 
471    if (!homeenv) sprintf(sumarc, ".sumarc");
472    else sprintf(sumarc,"%s/.sumarc", homeenv);
473    if (stat(sumarc, &stbuf) != -1) {
474       if (LocalHead)
475          fprintf (SUMA_STDERR,"%s: Loading %s ...\n", FuncName, sumarc);
476       AFNI_process_environ(sumarc);
477    } else {
478       if (LocalHead)
479          fprintf (SUMA_STDERR,"%s: No sumarc file found.\n", FuncName);
480          no_suma_rc_found = 1;
481    }
482 
483    if (!homeenv) sprintf(sumarc, ".afnirc");
484    else sprintf(sumarc,"%s/.afnirc", homeenv);
485    if (stat(sumarc, &stbuf) != -1) {
486       if (LocalHead)
487          fprintf (SUMA_STDERR,"%s: Loading %s ...\n", FuncName, sumarc);
488       AFNI_process_environ(sumarc);
489    } else {
490       if (LocalHead)
491          fprintf (SUMA_STDERR,"%s: No afnirc file found.\n", FuncName);
492    }
493 
494    if (sumarc) free(sumarc); sumarc = NULL; /* allocated before CommonFields */
495 
496    AFNI_mark_environ_done(); /* flag environment rc files as read */
497 
498    if (LocalHead)
499          fprintf (SUMA_STDERR,"%s: Exiting SUMA_process_environ\n", FuncName);
500 
501    return;
502 }
503 
SUMA_help_basics()504 char *SUMA_help_basics()
505 {
506    SUMA_STRING *SS = NULL;
507    char *s=NULL;
508    static char FuncName[]={"SUMA_help_basics"};
509 
510    SUMA_ENTRY;
511 
512    SS = SUMA_StringAppend(NULL, NULL);
513    SS = SUMA_StringAppend_va(SS,
514       "   [-novolreg]: Ignore any Rotate, Volreg, Tagalign, \n"
515       "                or WarpDrive transformations present in \n"
516       "                the Surface Volume.\n"
517       "   [-noxform]: Same as -novolreg\n"
518       "   [-setenv \"'ENVname=ENVvalue'\"]: Set environment variable ENVname\n"
519       "                to be ENVvalue. Quotes are necessary.\n"
520       "             Example: suma -setenv \"'SUMA_BackgroundColor = 1 0 1'\"\n"
521       "                See also options -update_env, -environment, etc\n"
522       "                in the output of 'suma -help'\n"
523       "  Common Debugging Options:\n"
524       "   [-trace]: Turns on In/Out debug and Memory tracing.\n"
525       "             For speeding up the tracing log, I recommend \n"
526       "             you redirect stdout to a file when using this option.\n"
527       "             For example, if you were running suma you would use:\n"
528       "             suma -spec lh.spec -sv ... > TraceFile\n"
529       "             This option replaces the old -iodbg and -memdbg.\n"
530       "   [-TRACE]: Turns on extreme tracing.\n"
531       "   [-nomall]: Turn off memory tracing.\n"
532       "   [-yesmall]: Turn on memory tracing (default).\n"
533       "  NOTE: For programs that output results to stdout\n"
534       "    (that is to your shell/screen), the debugging info\n"
535       "    might get mixed up with your results.\n"
536       "\n"
537       "\n"
538       "Global Options (available to all AFNI/SUMA programs)\n"
539       "%s\n",
540       SUMA_Offset_SLines(get_help_help(),2),     get_gopt_help() );
541    SUMA_SS2S(SS,s);
542    SUMA_RETURN(s);
543 }
544 
545 
SUMA_help_talk()546 char *SUMA_help_talk()
547 {
548    SUMA_STRING *SS = NULL;
549    char *s=NULL;
550    static char FuncName[]={"SUMA_help_talk"};
551 
552    SUMA_ENTRY;
553 
554    SS = SUMA_StringAppend(NULL, NULL);
555    SS = SUMA_StringAppend_va(SS,
556 "  SUMA communication options:\n"
557 "      -talk_suma: Send progress with each iteration to SUMA.\n"
558 "      -refresh_rate rps: Maximum number of updates to SUMA per second.\n"
559 "                         The default is the maximum speed.\n"
560 "      -send_kth kth: Send the kth element to SUMA (default is 1).\n"
561 "                     This allows you to cut down on the number of elements\n"
562 "                     being sent to SUMA.\n"
563 "      -sh <SumaHost>: Name (or IP address) of the computer running SUMA.\n"
564 "                      This parameter is optional, the default is 127.0.0.1 \n"
565 "      -ni_text: Use NI_TEXT_MODE for data transmission.\n"
566 "      -ni_binary: Use NI_BINARY_MODE for data transmission.\n"
567 "                  (default is ni_binary).\n"
568 "      -feed_afni: Send updates to AFNI via SUMA's talk.\n"
569 "%s"
570 "\n", get_np_help());
571    SUMA_SS2S(SS,s);
572    SUMA_RETURN(s);
573 }
574 
SUMA_help_dset()575 char *SUMA_help_dset()
576 {
577    SUMA_STRING *SS = NULL;
578    char *s=NULL;
579    static char FuncName[]={"SUMA_help_dset"};
580 
581    SUMA_ENTRY;
582 
583    SS = SUMA_StringAppend(NULL, NULL);
584    SS = SUMA_StringAppend(SS,
585       "  SUMA dataset input options:\n"
586       "      -input DSET: Read DSET1 as input.\n"
587       "                   In programs accepting multiple input datasets\n"
588       "                   you can use -input DSET1 -input DSET2 or \n"
589       "                   input DSET1 DSET2 ...\n"
590       "       NOTE: Selecting subsets of a dataset:\n"
591       "             Much like in AFNI, you can select subsets of a dataset\n"
592       "             by adding qualifiers to DSET.\n"
593       "           Append #SEL# to select certain nodes.\n"
594       "           Append [SEL] to select certain columns.\n"
595       "           Append {SEL} to select certain rows.\n"
596       "           The format of SEL is the same as in AFNI, see section:\n"
597       "           'INPUT DATASET NAMES' in 3dcalc -help for details.\n"
598       "           Append [i] to get the node index column from\n"
599       "                      a niml formatted dataset.\n"
600       "           *  SUMA does not preserve the selection order \n"
601       "              for any of the selectors.\n"
602       "              For example:\n"
603       "              dset[44,10..20] is the same as dset[10..20,44]\n"
604       "              Also, duplicate values are not supported.\n"
605       "              so dset[13, 13] is the same as dset[13].\n"
606       "              I am not proud of these limitations, someday I'll get\n"
607       "              around to fixing them.\n"
608       "\n");
609    SUMA_SS2S(SS,s);
610    SUMA_RETURN(s);
611 }
612 
SUMA_help_cmap()613 char *SUMA_help_cmap()
614 {
615    SUMA_STRING *SS = NULL;
616    char *s=NULL;
617    static char FuncName[]={"SUMA_help_mask"};
618 
619    SUMA_ENTRY;
620 
621    SS = SUMA_StringAppend(NULL, NULL);
622    SS = SUMA_StringAppend(SS,
623 " Selecting Colormaps: \n"
624 "    -cmap MapName:\n"
625 "       choose one of the standard colormaps available with SUMA:\n"
626 "       RGYBR20, BGYR19, BW20, GRAY20, MATLAB_DEF_BYR64, \n"
627 "       ROI64, ROI128. See Suma's colormap chooser for a list of names.\n"
628 "    -cmapdb Palfile: read color maps from AFNI .pal file\n"
629 "       In addition to the default paned AFNI colormaps, you\n"
630 "       can load colormaps from a .pal file.\n"
631 "       To access maps in the Palfile you must use the -cmap option\n"
632 "       with the label formed by the name of the palette, its sign\n"
633 "       and the number of panes. For example, to following palette:\n"
634 "       ***PALETTES deco [13]\n"
635 "       should be accessed with -cmap deco_n13\n"
636 "       ***PALETTES deco [13+]\n"
637 "       should be accessed with -cmap deco_p13\n"
638 "    -cmapfile Mapfile: read color map from Mapfile.\n"
639 "       Mapfile:1D formatted ascii file containing colormap.\n"
640 "               each row defines a color in one of two ways:\n"
641 "               R  G  B        or\n"
642 "               R  G  B  f     \n"
643 "       where R, G, B specify the red, green and blue values, \n"
644 "       between 0 and 1 and f specifies the fraction of the range\n"
645 "       reached at this color. THINK values of right of AFNI colorbar.\n"
646 "       The use of fractions (it is optional) would allow you to create\n"
647 "       non-linear color maps where colors cover differing fractions of \n"
648 "       the data range.\n"
649 "       Sample colormap with positive range only (a la AFNI):\n"
650 "               0  0  1  1.0\n"
651 "               0  1  0  0.8\n"
652 "               1  0  0  0.6\n"
653 "               1  1  0  0.4\n"
654 "               0  1  1  0.2\n"
655 "       Note the order in which the colors and fractions are specified.\n"
656 "       The bottom color of the +ve colormap should be at the bottom of the\n"
657 "       file and have the lowest +ve fraction. The fractions here define a\n"
658 "       a linear map so they are not necessary but they illustrate the format\n"
659 "       of the colormaps.\n"
660 "       Comparable colormap with negative range included:\n"
661 "               0  0  1   1.0\n"
662 "               0  1  0   0.6\n"
663 "               1  0  0   0.2\n"
664 "               1  1  0  -0.2\n"
665 "               0  1  1  -0.6\n"
666 "       The bottom color of the -ve colormap should have the \n"
667 "       lowest -ve fraction. \n"
668 "       You can use -1 -1 -1 for a color to indicate a no color\n"
669 "       (like the 'none' color in AFNI). Values mapped to this\n"
670 "       'no color' will be masked as with the -msk option.\n"
671 "       If your 1D color file has more than three or 4 columns,\n"
672 "       you can use the [] convention adopted by AFNI programs\n"
673 "       to select the columns you need.\n"
674       );
675 
676    SUMA_SS2S(SS,s);
677    SUMA_RETURN(s);
678 }
679 
SUMA_help_mask()680 char *SUMA_help_mask()
681 {
682    SUMA_STRING *SS = NULL;
683    char *s=NULL;
684    static char FuncName[]={"SUMA_help_mask"};
685 
686    SUMA_ENTRY;
687 
688    SS = SUMA_StringAppend(NULL, NULL);
689    SS = SUMA_StringAppend(SS,
690 " SUMA mask options:\n"
691 "      -n_mask INDEXMASK: Apply operations to nodes listed in\n"
692 "                            INDEXMASK  only. INDEXMASK is a 1D file.\n"
693 "      -b_mask BINARYMASK: Similar to -n_mask, except that the BINARYMASK\n"
694 "                          1D file contains 1 for nodes to filter and\n"
695 "                          0 for nodes to be ignored.\n"
696 "                          The number of rows in filter_binary_mask must be\n"
697 "                          equal to the number of nodes forming the\n"
698 "                          surface.\n"
699 "      -c_mask EXPR: Masking based on the result of EXPR. \n"
700 "                    Use like afni's -cmask options. \n"
701 "                    See explanation in 3dmaskdump -help \n"
702 "                    and examples in output of 3dVol2Surf -help\n"
703 "      NOTE: Unless stated otherwise, if n_mask, b_mask and c_mask \n"
704 "            are used simultaneously, the resultant mask is the intersection\n"
705 "            (AND operation) of all masks.\n"
706 "\n");
707    SUMA_SS2S(SS,s);
708    SUMA_RETURN(s);
709 }
710 
711 /*!
712    \brief parse command line arguments for input/output debugging and
713    memory debugging. Use no fancies in this function!
714 
715    This function is to be called after SUMAg_CF has been created,
716    if #ifdef SUMA_COMPILED
717 
718    Default for iotrace = 0
719                memtrace = 1
720    Those defaults are common to all apps
721 
722 */
723 static int Domemtrace=0, Doiotrace=0, IgnoreXforms=0;
get_Domemtrace(void)724 int get_Domemtrace(void) {
725    return (Domemtrace);
726 }
set_Domemtrace(int s)727 void set_Domemtrace(int s) {
728    Domemtrace = s;
729    return;
730 }
get_Doiotrace(void)731 int get_Doiotrace(void) {
732    return (Doiotrace);
733 }
set_Doiotrace(int s)734 void set_Doiotrace(int s) {
735    Doiotrace = s;
736    return;
737 }
get_IgnoreXforms(void)738 int get_IgnoreXforms(void) {
739    return (IgnoreXforms);
740 }
set_IgnoreXforms(int s)741 void set_IgnoreXforms(int s) {
742    IgnoreXforms = s;
743    return;
744 }
745 
746 
747 /*!
748    No fancy macros and allocation here please
749 */
SUMA_ParseInput_basics_eng(char * argv[],int argc)750 int SUMA_ParseInput_basics_eng (char *argv[], int argc)
751 {
752 
753    static char FuncName[]={"SUMA_ParseInput_basics_eng"};
754    int brk = 0;
755    int kar;
756 
757    if (!argv) return (0);
758    set_Domemtrace(1);
759    set_Doiotrace(0);
760 
761    /* if (argc < 2) return (0); why insist on two parameters? */
762 
763    kar = 1;
764    brk = 0;
765    while (kar < argc) { /* loop accross tracing and debugging
766                            command line options */
767 		if ((strcmp(argv[kar], "-memdbg") == 0) ||
768           (strcmp(argv[kar], "-yesmall") == 0) ) {
769 			fprintf(SUMA_STDOUT,"Warning %s:  running in memory trace mode.\n",
770                   FuncName);
771 			set_Domemtrace(1);
772          brk = 1;
773 		}
774 
775       if (!brk && (strcmp(argv[kar], "-nomall") == 0)) {
776 			fprintf(SUMA_STDOUT,"Warning %s:  turning off memory trace mode.\n",
777                   FuncName);
778 			set_Domemtrace(0);
779 			brk = 1;
780 		}
781 
782       if (!brk && ( (strcmp(argv[kar], "-trace") == 0) ||
783                    (strcmp(argv[kar], "-iodbg") == 0)) ){
784 			fprintf( SUMA_STDERR,
785                   "Warning %s: SUMA running in I/O trace mode.\n", FuncName);
786 			set_Doiotrace(1);
787          brk = 1;
788 		}
789 
790       if (!brk && (strcmp(argv[kar], "-TRACE") == 0)) {
791 			fprintf( SUMA_STDERR,
792                   "Warning %s: SUMA running in detailed I/O trace mode.\n",
793                   FuncName);
794 			set_Doiotrace(2);
795          brk = 1;
796 		}
797 
798       if (!brk &&
799             (  strcmp(argv[kar], "-novolreg") == 0 ||
800                strcmp(argv[kar], "-noxform") == 0)) {
801 			set_IgnoreXforms(1);
802          brk = 1;
803 		}
804 
805       brk = 0;
806       kar ++;
807    }
808 
809    return (1);
810 }
811 
812 /*!
813    No fancy macros and allocation here please
814 */
SUMA_ParseInput_basics_ns(char * argv[],int argc)815 void SUMA_ParseInput_basics_ns(char *argv[], int argc) /* for non-suma programs */
816 {
817    static char FuncName[]={"SUMA_ParseInput_basics_ns"};
818 
819    if (!argv) return;
820    /*if (argc < 2) return; why insist on two parameters? */
821 
822    if (!SUMA_ParseInput_basics_eng (argv, argc)) return;
823 
824    if (get_Doiotrace()) { SUMA_INOUT_NOTIFY_ON; }
825    if (get_Domemtrace()) { SUMA_MEMTRACE_ON; }
826 
827    /* some more special ones */
828    #ifdef USE_TRACING
829       if (get_Doiotrace() == 2) { DBG_trace = 2; }
830    #endif
831 
832    return;
833 
834 }
835 
836 /*!
837    A function version of macro SUMA_TO_LOWER
838    what gets returned is actually the same pointer
839    s but s now points to a lowercase string.
840 */
SUMA_to_lower(char * s)841 char * SUMA_to_lower(char *s) {
842    int i, d;
843    if (s) {
844       d = 'a' - 'A';
845       for (i=0; i < strlen(s); ++i) {
846          if (s[i] >= 'A' && s[i] <= 'Z') s[i] = s[i] + d;
847       }
848    }
849    return(s);
850 }
851 
852 /*!**
853 
854 Purpose :
855 
856    splits a path/filename into its path and filename components
857 
858 Usage :
859 		Ans = SUMA_StripPath (Name)
860 
861 
862 Input parameters :
863 \param   Name (char *) something like /hello/something
864 
865 Returns :
866 \return   ans (SUMA_FileName) .Path (char *) and .FileName (char *)
867 
868 Support :
869 \sa  SUMA_define.h
870 
871 NOTE: SUMA_ParseFname() is better than this function
872 
873 To Compile as stand alone:
874 gcc -DSUMA_StripPath_STAND_ALONE -Wall -o $1 $1.c -SUMA_lib.a -I/usr/X11R6/include -I./
875 ***/
SUMA_StripPath(char * FileName)876 SUMA_FileName SUMA_StripPath (char *FileName)
877 {/*SUMA_StripPath*/
878    static char FuncName[] = {"SUMA_StripPath"},  PathDelimiter[]={"/"};
879    int i, j, NotFound=1, N_FileName;
880 	SUMA_FileName NewName;
881 
882 	N_FileName = strlen(FileName);
883 	if (N_FileName ){
884 		i = N_FileName -1;
885 		while (i > -1 && NotFound) {
886 			if (FileName[i] == PathDelimiter[0]) NotFound = 0;
887 			--i;
888 		}
889 		if (!NotFound && i > -1) {
890 			NewName.Path = (char *)SUMA_malloc(sizeof(char)*(N_FileName+1));
891 			NewName.FileName = (char *)SUMA_malloc(sizeof(char)*(N_FileName+1));
892 			if (NewName.Path == NULL || NewName.FileName == NULL) {
893 				SUMA_SL_Err("Failed to allocate");
894             return (NewName);
895 			}
896 			for (j=0; j<=i+1; ++j) {
897 				NewName.Path[j] = FileName[j];
898 			}
899          NewName.Path[j] = '\0';
900 
901 			/*fprintf(stdout,"jbegin=%d/%d\n", i+2, N_FileName);*/
902 			for (j=i+2; j < N_FileName; ++j) NewName.FileName[j-i-2] = FileName[j];
903          NewName.FileName[j-i-2] = '\0';
904 
905 			/* fprintf(stdout,"All Path (%d chars)/%d: %s\n",
906                      (i+2),  strlen(NewName.Path), NewName.Path);
907 			fprintf(stdout,"All FileName (%d chars)/%d: %s\n",
908                      (N_FileName-i-2), strlen(NewName.FileName),
909                      NewName.FileName); */
910 		}
911 		else {
912 			NewName.Path = (char *)SUMA_malloc(sizeof(char)*(N_FileName+1));
913 			NewName.FileName = (char *)SUMA_malloc(sizeof(char)*(N_FileName+1));
914 			if (NewName.Path == NULL || NewName.FileName == NULL) {
915 				SUMA_SL_Err("Failed to allocate");
916             return (NewName);
917 			}
918 			sprintf(NewName.Path,"./");
919 			sprintf(NewName.FileName,"%s", FileName);
920 		}
921 	}
922 	else {
923 		NewName.Path = NULL;
924 		NewName.FileName = NULL;
925 	}
926 	return (NewName);
927 }/*SUMA_StripPath*/
928 
SUMA_ShowParsedFname(SUMA_PARSED_NAME * pn,FILE * out)929 SUMA_Boolean SUMA_ShowParsedFname(SUMA_PARSED_NAME *pn, FILE *out)
930 {
931    static char FuncName[]={"SUMA_ShowParsedFname"};
932    char *s=NULL;
933    SUMA_STRING *SS=NULL;
934    SUMA_Boolean LocalHead = NOPE;
935 
936    SUMA_ENTRY;
937 
938    if (!out) out = SUMA_STDOUT;
939 
940    SS = SUMA_StringAppend(NULL, NULL);
941    if (!pn) {
942       SS = SUMA_StringAppend_va(SS, "NULL parsed name");
943    } else {
944       SS = SUMA_StringAppend_va(SS, "AbsPath       :%s\n", pn->AbsPath);
945       SS = SUMA_StringAppend_va(SS, "RelDir        :%s\n", pn->RelDir);
946       SS = SUMA_StringAppend_va(SS, "RelPath       :%s\n", pn->RelPath);
947       SS = SUMA_StringAppend_va(SS, "Path          :%s\n", pn->Path);
948       SS = SUMA_StringAppend_va(SS, "FileName      :%s\n", pn->FileName);
949       SS = SUMA_StringAppend_va(SS, "Prefix        :%s\n", pn->Prefix);
950       SS = SUMA_StringAppend_va(SS, "View          :%s\n", pn->View);
951       SS = SUMA_StringAppend_va(SS, "Ext           :%s\n", pn->Ext);
952       SS = SUMA_StringAppend_va(SS, "TypeExt       :%s\n", pn->TypeExt);
953       SS = SUMA_StringAppend_va(SS, "StorageMode   :%d\n", pn->StorageMode);
954       SS = SUMA_StringAppend_va(SS, "StorageModeNm.:%s\n", pn->StorageModeName);
955       SS = SUMA_StringAppend_va(SS, "FileName_NoExt:%s\n", pn->FileName_NoExt);
956       SS = SUMA_StringAppend_va(SS, "FNameNoAfniExt:%s\n", \
957                                  without_afni_filename_extension(pn->FileName));
958       SS = SUMA_StringAppend_va(SS, "FNameLabel    :%s\n", \
959                                  without_afni_filename_extension(pn->Prefix));
960       SS = SUMA_StringAppend_va(SS, "Col. Selector :%s\n", pn->ColSelect);
961       SS = SUMA_StringAppend_va(SS, "Node Selector :%s\n", pn->NodeSelect);
962       SS = SUMA_StringAppend_va(SS, "Row Selector  :%s\n", pn->RowSelect);
963       SS = SUMA_StringAppend_va(SS, "Range Selector:%s\n", pn->RangeSelect);
964       SS = SUMA_StringAppend_va(SS, "Only index col:%d\n", pn->only_index);
965       SS = SUMA_StringAppend_va(SS, "FullName      :%s\n", pn->FullName);
966       SS = SUMA_StringAppend_va(SS, "FullName_NoSel:%s\n", pn->FullName_NoSel);
967       SS = SUMA_StringAppend_va(SS, "RelName       :%s%s\n",
968                                                     pn->RelPath,pn->FileName);
969       SS = SUMA_StringAppend_va(SS, "HeadName      :%s\n", pn->HeadName);
970       SS = SUMA_StringAppend_va(SS, "BrikName      :%s\n", pn->BrikName);
971       SS = SUMA_StringAppend_va(SS, "OnDisk        :%d\n", pn->OnDisk);
972       SS = SUMA_StringAppend_va(SS, "ExistsAs      :%s\n",
973                                                 pn->ExistsAs?pn->ExistsAs:"");
974       SS = SUMA_StringAppend_va(SS, "Size          :%d\n", pn->Size);
975       SS = SUMA_StringAppend_va(SS, "NameAsParsed  :%s\n", pn->NameAsParsed);
976       SS = SUMA_StringAppend_va(SS, "cwdAsParsed   :%s\n", pn->cwdAsParsed);
977 
978    }
979 
980    SUMA_SS2S(SS,s);
981 
982    fprintf(out, "%s", s); SUMA_free(s); s= NULL;
983    fflush(out);
984 
985    SUMA_RETURN(YUP);
986 }
987 
SUMA_getcwd(void)988 char *SUMA_getcwd(void)
989 {
990    static char FuncName[]={"SUMA_getcwd"};
991    char *cwd = NULL;
992 
993    SUMA_ENTRY;
994 
995    cwd = (char *)SUMA_malloc(sizeof(char)*(SUMA_MAX_DIR_LENGTH+1));
996    getcwd(cwd, SUMA_MAX_DIR_LENGTH);
997 
998    SUMA_RETURN(cwd);
999 }
1000 
SUMA_ParseFname(char * FileName,char * ucwd)1001 SUMA_PARSED_NAME * SUMA_ParseFname (char *FileName, char *ucwd)
1002 {
1003    return(SUMA_ParseFname_eng(FileName, ucwd, 1));
1004 }
1005 
1006 /*!
1007    \brief ans = SUMA_ParseFname (FileName, cwd, diskcheck);
1008    parses a file name into its elements
1009    \param FileName (char *) obvious ...
1010    \param ucwd (char *) if not null, this is the user supplied current work. dir.
1011    \param diskcheck (int) if not 0 check for file's size and existence on disk
1012    \return ans (SUMA_PARSED_NAME *) pointer to structure with following fields:
1013       .FileName (char *) containing filename without path and without selectors (see below).
1014                         if empty .FileName[0] = '\0'
1015       .Path (char *) containing path including last slash.
1016                      If no path exists, Path is "./"
1017       .AbsPath (char *) containing absolute path, which is
1018                         the same as .Path if .Path starts with '/'
1019                         or cwd/.Path if .Path does not start with '/'
1020                         AND cwd is specified.
1021                         If cwd is null then it is determined inside the function.
1022       .Ext (char *) containing extension including the dot.
1023                     If no extension exists, Ext[0] = '\0'
1024       .FileName_NoExt (char *) filename without extension.
1025       .NodeSelect (char *) Node selector part; between ## ; The () were already taken
1026       .ColSelect (char *) Column selector part; between []
1027       .RowSelect (char *) Row selector part; between {}
1028       .RangeSelect (char *) Range selector part; between <>
1029 
1030       \sa SUMA_Free_Parsed_Name, SUMA_ShowParsedFname
1031 */
SUMA_ParseFname_eng(char * FileName,char * ucwd,int diskcheck)1032 SUMA_PARSED_NAME * SUMA_ParseFname_eng (char *FileName, char *ucwd,
1033                                         int diskcheck)
1034 {/*SUMA_ParseFname*/
1035    static char FuncName[]={"SUMA_ParseFname_eng"};
1036    char PathDelimiter='/';
1037    char *cwd=NULL;
1038    int   i, j, iExt , iFile, iPath, iColSel, iRowSel, iNodeSel,
1039          iRangeSel, N_FileName, iFirstSel, nc,
1040          iAtSel, only_index;
1041 	SUMA_PARSED_NAME *NewName = NULL;
1042    SUMA_Boolean   FoundPath = NOPE,
1043                   FoundExt, FoundFile, FoundRowSel ,
1044                   FoundNodeSel, FoundColSel, FoundRangeSel;
1045 	SUMA_Boolean LocalHead = NOPE;
1046 
1047    SUMA_ENTRY;
1048 
1049 
1050    if (!FileName) {
1051       SUMA_S_Err("Null input");
1052       SUMA_RETURN(NULL);
1053    }
1054 
1055    /* work cwd */
1056    if (ucwd) {
1057       if (ucwd[0] != '/') {
1058          SUMA_S_Err("Current working directory must start with '/'");
1059          SUMA_RETURN(NULL);
1060       }
1061       cwd = SUMA_copy_string(ucwd);
1062    } else {
1063       cwd = SUMA_getcwd();
1064       if (cwd[0] != '/') {
1065          SUMA_S_Err("STRANGE! Current working directory must start with '/'");
1066          SUMA_free(cwd); cwd = NULL;
1067          SUMA_RETURN(NULL);
1068       }
1069    }
1070    nc = strlen(cwd);
1071    if (cwd[nc-1] == PathDelimiter) { cwd[nc-1] = '\0'; --nc; }
1072 
1073    SUMA_LH("Checking chars");
1074 	N_FileName = strlen(FileName);
1075    iExt = N_FileName;
1076    iPath = -1;
1077    iFile = 0;
1078    iColSel = -1;
1079    iRowSel = -1;
1080    iNodeSel = -1;
1081    iRangeSel = -1;
1082    iAtSel = -1;
1083    only_index = 0;
1084    iFirstSel = N_FileName;
1085    FoundPath = NOPE;
1086    FoundExt = NOPE;
1087    FoundRowSel = NOPE;
1088    FoundNodeSel = NOPE;
1089    FoundColSel = NOPE;
1090    FoundRangeSel = NOPE;
1091 
1092    NewName = (SUMA_PARSED_NAME *) SUMA_calloc(1,sizeof(SUMA_PARSED_NAME));
1093 	NewName->NameAsParsed = SUMA_copy_string(FileName);
1094    NewName->cwdAsParsed = SUMA_copy_string(ucwd);
1095 
1096    if (N_FileName ){
1097       i = N_FileName -1;
1098 		while (i > -1 && !FoundPath) {
1099 			if (FileName[i] == '.' && !FoundExt &&
1100              !(FoundColSel && iColSel < 0) && /* Not whilst column selection */
1101              !(FoundRowSel && iRowSel < 0) && /* Not whilst  row selection */
1102              !(FoundNodeSel && iNodeSel < 0) && /* Not whilst node selection */
1103              !(FoundRangeSel && iRangeSel < 0) /* Not whilst  range selection */          ) {
1104             iExt = i;
1105             FoundExt = YUP;
1106          } else if (FileName[i] == PathDelimiter) {
1107             FoundPath = YUP;
1108             iPath = i;
1109             iFile = i+1;
1110          } else if (FileName[i] == ']') {
1111             FoundColSel = YUP;
1112          } else if (FileName[i] == '[' && FoundColSel) {
1113             iColSel = i; if (iColSel < iFirstSel) iFirstSel = iColSel;
1114          } else if (FileName[i] == '}') {
1115             FoundRowSel = YUP;
1116          } else if (FileName[i] == '{' && FoundRowSel) {
1117             iRowSel = i; if (iRowSel < iFirstSel) iFirstSel = iRowSel;
1118          } else if (FileName[i] == '#' && !FoundNodeSel) {
1119             FoundNodeSel = YUP;
1120          } else if (FileName[i] == '#' && FoundNodeSel) {
1121             iNodeSel = i; if (iNodeSel < iFirstSel) iFirstSel = iNodeSel;
1122          } else if (FileName[i] == '>') {
1123             FoundRangeSel = YUP;
1124          } else if (FileName[i] == '<' && FoundRangeSel) {
1125             iRangeSel = i; if (iRangeSel < iFirstSel) iFirstSel = iRangeSel;
1126          } else if ( 0 &&        /* Don't need that, using [i] instead */
1127                      FileName[i] == '@' &&            /* Not whilst reading */
1128                      !FoundExt &&                     /* pre extension*/
1129                      !(FoundColSel && iColSel < 0) && /* column selection   */
1130                      !(FoundRowSel && iRowSel < 0) && /*  row selection */
1131                      !(FoundNodeSel && iNodeSel < 0) && /* node selection */
1132                      !(FoundRangeSel && iRangeSel < 0) )  {/* range selection */
1133             iAtSel = i; if (iAtSel < iFirstSel) iFirstSel = iAtSel;
1134             only_index = 1;
1135 			}
1136          --i;
1137 		}
1138 
1139       if (FoundColSel && iColSel < 0) {
1140          FoundColSel = NOPE; /* need both of [ ] */
1141       }
1142       if (FoundRowSel && iRowSel < 0) {
1143          FoundRowSel = NOPE; /* need both of { } */
1144       }
1145       if (FoundNodeSel && iNodeSel < 0) {
1146          FoundNodeSel = NOPE; /* need both of # # */
1147       }
1148       if (FoundRangeSel && iRangeSel < 0) {
1149          FoundRangeSel = NOPE; /* need both of < > */
1150       }
1151 
1152       if (iFile == iExt) {
1153          /* .file, not an extension */
1154          FoundExt = NOPE;
1155       }
1156 
1157       if (iFile ==  N_FileName) FoundFile = NOPE;
1158       else FoundFile = YUP;
1159 
1160       SUMA_LH("Storing stuff");
1161 
1162       if (FoundPath) {
1163          NewName->Path = (char *)SUMA_malloc(sizeof(char)*(iPath+2));
1164          for (i=0; i<= iPath; ++i) NewName->Path[i] = FileName[i];
1165          NewName->Path[i] = '\0';
1166       }else {
1167          NewName->Path = (char *)SUMA_malloc(sizeof(char)*(3));
1168          sprintf(NewName->Path, "./");
1169       }
1170       if (NewName->Path[0] == '/') {
1171          NewName->AbsPath = SUMA_copy_string(NewName->Path);
1172       } else {
1173          char *ptmp = NewName->Path;
1174          if (ptmp[0] == '.') {
1175             /* just searching for ./ here?                  15 Aug 2011 [rickr]
1176              * ... problem noted by Ryan from Princeton
1177              * if (strstr(NewName->Path,"./") && ptmp[1] == '/') ptmp = ptmp+2;
1178              * else ptmp = ptmp+1;                                           */
1179 
1180             if ( ptmp[1] == '/' ) ptmp = ptmp+2;
1181             else if ( ptmp[1] == '\0' ) ptmp = ptmp+1;
1182          }
1183          NewName->AbsPath = SUMA_append_replace_string(cwd, ptmp, "/", 0);
1184          ptmp = NULL;
1185       }
1186       /* store the current directory, and put back the slash */
1187       NewName->RelDir = SUMA_append_string(cwd, "/");
1188       /* get the relative path (assumes both end with / )*/
1189       {
1190          int nback=0, imatch=0;
1191          i=0; /* go as far as the directories match */
1192          while (i < strlen(NewName->RelDir) &&
1193                 i < strlen(NewName->AbsPath) &&
1194                 NewName->RelDir[i] == NewName->AbsPath[i] ) ++i;
1195          /* backup i until you hit the last '/' */
1196          while (i>=0 && NewName->RelDir[i] != '/') --i;
1197          if (NewName->RelDir[i] == '/') ++i; /* and back up one */
1198 
1199          /* how many extra directories do we have left in NewName->RelDir ?*/
1200          imatch = i;
1201          while (i < strlen(NewName->RelDir)) {
1202             if (NewName->RelDir[i] == '/') ++nback;
1203             ++i;
1204          }
1205          NewName->RelPath = SUMA_calloc(sizeof(char),
1206                                   strlen(NewName->AbsPath)-imatch+nback*3+10);
1207          NewName->RelPath[0]= '\0';
1208          for (i=0; i<nback; ++i) {
1209             strcat(NewName->RelPath, "../");
1210          }
1211          strcat(NewName->RelPath,NewName->AbsPath+imatch);
1212          if (!strlen(NewName->RelPath)) strcat(NewName->RelPath, "./");
1213       }
1214       if (FoundFile) {
1215          NewName->FileName =
1216             (char *)SUMA_malloc(sizeof(char)*(N_FileName - iFile + 2));
1217          for (i=iFile; i< iFirstSel; ++i)
1218             NewName->FileName[i-iFile] = FileName[i];
1219          NewName->FileName[i-iFile] = '\0';
1220       }else {
1221          NewName->FileName = (char *)SUMA_malloc(sizeof(char));
1222          NewName->FileName[0] = '\0';
1223       }
1224 
1225       if (FoundExt) {
1226 		   NewName->FileName_NoExt =
1227             (char *)SUMA_malloc(sizeof(char)*(N_FileName - iFile +2));
1228          NewName->Ext =
1229             (char *)SUMA_malloc(sizeof(char)*(N_FileName - iExt+2));
1230          for (i=iFile; i< iExt; ++i)
1231             NewName->FileName_NoExt[i-iFile] = FileName[i];
1232          NewName->FileName_NoExt[i-iFile] = '\0';
1233          for (i=iExt; i < iFirstSel; ++i)
1234             NewName->Ext[i-iExt] = FileName[i];
1235          NewName->Ext[i-iExt] = '\0';
1236       } else {
1237          NewName->FileName_NoExt = SUMA_copy_string(NewName->FileName);
1238          NewName->Ext = (char *)SUMA_malloc(sizeof(char));
1239          NewName->Ext[0] = '\0';
1240       }
1241 
1242       if (FoundNodeSel) {
1243          NewName->NodeSelect = (char *)SUMA_malloc(sizeof(char)*
1244                                                    (N_FileName - iNodeSel + 2));
1245          for (i=iNodeSel; i< N_FileName; ++i) {
1246             NewName->NodeSelect[i-iNodeSel] = FileName[i];
1247             if (FileName[i] == '#' && i >iNodeSel) { ++i; break; }
1248          }
1249          NewName->NodeSelect[i-iNodeSel] = '\0';
1250       }else {
1251          NewName->NodeSelect = (char *)SUMA_malloc(sizeof(char));
1252          NewName->NodeSelect[0] = '\0';
1253       }
1254 
1255       if (FoundRowSel) {
1256          NewName->RowSelect = (char *)SUMA_malloc( sizeof(char)*
1257                                                    (N_FileName - iRowSel + 2));
1258          for (i=iRowSel; i< N_FileName; ++i) {
1259             NewName->RowSelect[i-iRowSel] = FileName[i];
1260             if (FileName[i] == '}') { ++i; break; }
1261          }
1262          NewName->RowSelect[i-iRowSel] = '\0';
1263       }else {
1264          NewName->RowSelect = (char *)SUMA_malloc(sizeof(char));
1265          NewName->RowSelect[0] = '\0';
1266       }
1267 
1268       if (FoundColSel) {
1269          NewName->ColSelect =
1270             (char *)SUMA_malloc(sizeof(char)*(N_FileName - iColSel + 2));
1271          for (i=iColSel; i< N_FileName; ++i) {
1272             NewName->ColSelect[i-iColSel] = FileName[i];
1273             if (FileName[i] == ']') { ++i; break; }
1274          }
1275          NewName->ColSelect[i-iColSel] = '\0';
1276          if (NewName->ColSelect[1] == 'i') {
1277             only_index = 1;
1278             NewName->ColSelect[0] = '\0';
1279          }
1280       }else {
1281          NewName->ColSelect = (char *)SUMA_malloc(sizeof(char));
1282          NewName->ColSelect[0] = '\0';
1283       }
1284 
1285       if (FoundRangeSel) {
1286          NewName->RangeSelect =
1287             (char *)SUMA_malloc(  sizeof(char)* (N_FileName - iRangeSel + 2));
1288          for (i=iRangeSel; i< N_FileName; ++i) {
1289             NewName->RangeSelect[i-iRangeSel] = FileName[i];
1290             if (FileName[i] == '>') { ++i; break; }
1291          }
1292          NewName->RangeSelect[i-iRangeSel] = '\0';
1293       }else {
1294          NewName->RangeSelect = (char *)SUMA_malloc(sizeof(char));
1295          NewName->RangeSelect[0] = '\0';
1296       }
1297 
1298       NewName->only_index = only_index;
1299 
1300       NewName->FullName_NoSel=NULL;
1301       NewName->FullName_NoSel=
1302          SUMA_append_replace_string(NewName->FullName_NoSel,
1303                                     NewName->AbsPath, "", 1);
1304       NewName->FullName_NoSel=
1305          SUMA_append_replace_string(NewName->FullName_NoSel,
1306                                     NewName->FileName, "", 1);
1307 
1308       NewName->StorageMode = storage_mode_from_prefix(NewName->FullName_NoSel);
1309       NewName->StorageModeName = storage_mode_name(NewName->StorageMode);
1310 
1311       NewName->FullName=NULL;
1312       NewName->FullName=
1313          SUMA_append_replace_string(NewName->FullName,
1314                                     NewName->AbsPath, "", 1);
1315       NewName->FullName=
1316          SUMA_append_replace_string(NewName->FullName,
1317                                     NewName->FileName, "", 1);
1318       NewName->FullName=
1319          SUMA_append_replace_string(NewName->FullName,
1320                                     NewName->NodeSelect, "", 1);
1321       NewName->FullName=
1322          SUMA_append_replace_string(NewName->FullName,
1323                                     NewName->RowSelect, "", 1);
1324       NewName->FullName=
1325          SUMA_append_replace_string(NewName->FullName,
1326                                     NewName->ColSelect, "", 1);
1327 
1328       if (!(NewName->TypeExt = SUMA_copy_string(
1329                      find_filename_extension(NewName->FileName)))) {
1330          NewName->TypeExt = SUMA_copy_string("");
1331       }
1332 
1333       if (NewName->StorageMode == STORAGE_BY_BRICK) {
1334          int dotted = 0;
1335          NewName->Prefix = SUMA_copy_string(NewName->FileName);
1336          NewName->Prefix[strlen(NewName->FileName)-
1337                          strlen(NewName->TypeExt)]='\0';
1338          if (NewName->Prefix[strlen(NewName->Prefix)-1] == '.') {
1339             dotted = 1;
1340          }
1341          if (dotted) {
1342             if (STRING_HAS_SUFFIX(NewName->Prefix, "+orig.") ){
1343                NewName->View = SUMA_copy_string("+orig");
1344             } else if (STRING_HAS_SUFFIX(NewName->Prefix, "+acpc.") ) {
1345                NewName->View = SUMA_copy_string("+acpc");
1346             } else if (STRING_HAS_SUFFIX(NewName->Prefix, "+tlrc.") ){
1347                NewName->View = SUMA_copy_string("+tlrc");
1348             }
1349             NewName->Prefix[strlen(NewName->Prefix)-6]='\0';
1350          } else {
1351             if (STRING_HAS_SUFFIX(NewName->Prefix, "+orig") ) {
1352                NewName->View = SUMA_copy_string("+orig");
1353             } else if (STRING_HAS_SUFFIX(NewName->Prefix, "+acpc") ) {
1354                NewName->View = SUMA_copy_string("+acpc");
1355             } else if (STRING_HAS_SUFFIX(NewName->Prefix, "+tlrc") ) {
1356                NewName->View = SUMA_copy_string("+tlrc");
1357             }
1358             NewName->Prefix[strlen(NewName->Prefix)-5]='\0';
1359          }
1360 	   } else {
1361          NewName->Prefix = SUMA_copy_string(NewName->FileName);
1362          NewName->View = SUMA_copy_string("");
1363       }
1364    }
1365 
1366 
1367 
1368    if (NewName->StorageMode == STORAGE_BY_BRICK) {
1369       if (NewName->View[0] != '\0') {
1370          NewName->HeadName = SUMA_append_string(NewName->Path,NewName->Prefix);
1371          NewName->HeadName = SUMA_append_replace_string(NewName->HeadName,
1372                                                 NewName->View, "",1);
1373          NewName->BrikName = SUMA_append_string(NewName->HeadName, ".BRIK");
1374          NewName->HeadName = SUMA_append_string(NewName->HeadName, ".HEAD");
1375       } else {
1376          NewName->BrikName = SUMA_copy_string("");
1377          NewName->HeadName = SUMA_copy_string("");
1378       }
1379    } else {
1380       NewName->HeadName = SUMA_append_string(NewName->Path,NewName->FileName);
1381       NewName->BrikName = SUMA_append_string(NewName->Path,NewName->FileName);
1382    }
1383    NewName->OnDisk = -1;
1384    NewName->Size = -1;
1385    if (diskcheck) {
1386       SUMA_LH("Setting OnDisk for %s...", NewName->HeadName);
1387       NewName->OnDisk = THD_is_file(NewName->HeadName);
1388       if (NewName->OnDisk) {
1389          SUMA_LH("Setting filesize for %s...",  NewName->HeadName);
1390          NewName->Size = THD_filesize(NewName->HeadName);
1391       }
1392       if (!NewName->OnDisk) {
1393          NewName->ExistsAs = NULL;
1394          if (NewName->View[0] == '\0') {
1395             char *sss = NULL;
1396                                  /* See if anything is there with
1397                                  +orig, +acpc, +tlrc anything */
1398             if (!NewName->ExistsAs) {
1399                sss = SUMA_append_replace_string(NewName->Path,"+orig.HEAD",
1400                                                      NewName->Prefix, 0);
1401                if (THD_is_file(sss)) {
1402                   NewName->ExistsAs = sss; sss = NULL;
1403                } else {
1404                   SUMA_ifree(sss); sss = NULL;
1405                }
1406             }
1407             if (!NewName->ExistsAs) {
1408                sss = SUMA_append_replace_string(NewName->Path,"+acpc.HEAD",
1409                                                      NewName->Prefix, 0);
1410                if (THD_is_file(sss)) {
1411                   NewName->ExistsAs = sss; sss = NULL;
1412                } else {
1413                   SUMA_ifree(sss); sss = NULL;
1414                }
1415             }
1416             if (!NewName->ExistsAs) {
1417                sss = SUMA_append_replace_string(NewName->Path,"+tlrc.HEAD",
1418                                                      NewName->Prefix, 0);
1419                if (THD_is_file(sss)) {
1420                   NewName->ExistsAs = sss; sss = NULL;
1421                } else {
1422                   SUMA_ifree(sss); sss = NULL;
1423                }
1424             }
1425          }
1426       } else {
1427          NewName->ExistsAs = SUMA_copy_string(NewName->HeadName);
1428       }
1429       if (!NewName->ExistsAs) { /* no nulls please */
1430          NewName->ExistsAs = (char*)SUMA_malloc(1*sizeof(char));
1431          NewName->ExistsAs[0] = '\0';
1432       }
1433    }
1434    if (LocalHead) {
1435       SUMA_ShowParsedFname(NewName, NULL);
1436    }
1437    if (cwd) SUMA_free(cwd);
1438 
1439 	SUMA_RETURN (NewName);
1440 }/*SUMA_ParseFname_eng*/
1441 
1442 /*!
1443    \brief Lazy function calls to get at various parts of a file name without the
1444           pains of freeing and allocating. Do NOT free the returned string
1445 
1446    Valid options for sel:
1447       "pa": path
1448       "Pa": Absolute path
1449       "e": Extension
1450       "fne": filename without path and without extension
1451       "f": filename without path
1452       "F": filename with path
1453 
1454    Note that the function can leak ONE allocated and filed SUMA_PARSED_NAME in
1455    a program's life cycle, unless one calls  SUMA_FnameGet(NULL, NULL) at the end
1456 
1457    WARNING: You can't use this function more than MAX_NUM_STR_FG times as
1458             argument to a *printf command. Otherwise, you'll end up with the
1459             repeated strings for call numbers that exceed MAX_NUM_STR_FG!
1460 */
1461 #define MAX_NUM_STR_FG 10
SUMA_FnameGet(char * Fname,char * sel,char * cccwd)1462 char *SUMA_FnameGet(char *Fname, char *sel, char *cccwd)
1463 {
1464    static char FuncName[]={"SUMA_FnameGet"};
1465    static char str[MAX_NUM_STR_FG]
1466                   [SUMA_MAX_DIR_LENGTH+SUMA_MAX_NAME_LENGTH+20]={""};
1467    static char lastid[SUMA_IDCODE_LENGTH]={""};
1468    char *currid=NULL;
1469    static int istr=-1;
1470    static SUMA_PARSED_NAME *ParsedFname=NULL;
1471    SUMA_Boolean LocalHead = NOPE;
1472 
1473    SUMA_ENTRY;
1474 
1475    istr = (istr+1) % 10;
1476    str[istr][0] = '\0';
1477 
1478    if (!Fname) {
1479       /* cleanup */
1480       if (ParsedFname) SUMA_Free_Parsed_Name(ParsedFname); ParsedFname = NULL;
1481       SUMA_RETURN(str[istr]);
1482    }
1483    if (!sel) {
1484       SUMA_S_Err("no selection");
1485       SUMA_RETURN(str[istr]);
1486    }
1487 
1488 
1489    /* is this a new name?*/
1490    if (!lastid[0]) { /* a fresh start, ParsedFname should be NULL */
1491       if (ParsedFname) {
1492          SUMA_S_Err("Oh boy oh boy, that's not good!");
1493          SUMA_RETURN(str[istr]);
1494       }
1495       if (!(ParsedFname = SUMA_ParseFname(Fname, cccwd)))
1496          SUMA_RETURN(str[istr]);
1497       currid = UNIQ_hashcode(Fname);
1498       strcpy (lastid, currid);   /* store id */
1499       free(currid); currid = NULL;
1500    } else {
1501       currid = UNIQ_hashcode(Fname);
1502       if (strcmp(currid,lastid)) { /* different name */
1503          if (ParsedFname) SUMA_Free_Parsed_Name(ParsedFname);  /* free the old */
1504          if (!(ParsedFname = SUMA_ParseFname(Fname, cccwd)))
1505             SUMA_RETURN(str[istr]);
1506          strcpy (lastid, currid);   /* store id */
1507       } else { /* same name, reuse old stuff */
1508          free(currid); currid = NULL;
1509       }
1510    }
1511    /* Now that you have the parsed name, return what user wants */
1512    if       (sel[0] == 'p' && sel[1] == 'a')
1513       strcpy (str[istr], ParsedFname->Path);
1514    else if  (sel[0] == 'P' && sel[1] == 'a')
1515       strcpy (str[istr], ParsedFname->AbsPath);
1516    else if  (sel[0] == 'f' && sel[1] == '\0')
1517       strcpy (str[istr], ParsedFname->FileName);
1518    else if  (sel[0] == 'F' && sel[1] == '\0')
1519       strcpy (str[istr], ParsedFname->FullName);
1520    else if  (sel[0] == 'e' && sel[1] == '\0')
1521       strcpy (str[istr], ParsedFname->Ext);
1522    else if  (sel[0] == 'f' && sel[1] == 'n' && sel[2] == 'e' )
1523       strcpy (str[istr], ParsedFname->FileName_NoExt);
1524    else if  (sel[0] == 'l') {
1525       strcpy (str[istr], without_afni_filename_extension(ParsedFname->Prefix));
1526    } else {
1527       SUMA_S_Err("Selection not understood");
1528    }
1529 
1530    if (LocalHead) {
1531       SUMA_ShowParsedFname(ParsedFname, NULL);
1532       fprintf(SUMA_STDERR,
1533             "++   %s\n"
1534             "for >>%s<<\n"
1535             "sel >>%s<<\n"
1536             "ret.>>%s<<\n",
1537             FuncName, Fname, sel, str[istr]);
1538    }
1539 
1540    SUMA_RETURN(str[istr]);
1541 }
1542 
SUMA_ParseModifyName(char * Fname,char * what,char * val,char * cwd)1543 SUMA_PARSED_NAME * SUMA_ParseModifyName(char *Fname, char *what, char *val,
1544                                         char *cwd)
1545 {
1546    SUMA_PARSED_NAME *pn=NULL, *pno=NULL;
1547    if (!Fname || !what) return(NULL);
1548    pn = SUMA_ParseFname(Fname, cwd);
1549    if (!pn) return(NULL);
1550    pno = SUMA_ModifyParsedName (pn, what, val);
1551    SUMA_Free_Parsed_Name(pn);
1552    return(pno);
1553 }
1554 
SUMA_ModifyName(char * Fname,char * what,char * val,char * cwd)1555 char * SUMA_ModifyName(char *Fname, char *what, char *val, char *cwd)
1556 {
1557    char *oname=NULL;
1558    SUMA_PARSED_NAME *pn=NULL, *pno=NULL;
1559    if (!Fname || !what) return(NULL);
1560    pn = SUMA_ParseFname(Fname, cwd);
1561    if (!pn) return(NULL);
1562    pno = SUMA_ModifyParsedName (pn, what, val);
1563    SUMA_Free_Parsed_Name(pn);
1564    if (pno) {
1565       oname = SUMA_append_replace_string(pno->Path,pno->FileName,"",0);
1566       oname = SUMA_append_replace_string(oname, pno->NodeSelect,"",1);
1567       oname = SUMA_append_replace_string(oname, pno->RowSelect,"",1);
1568       oname = SUMA_append_replace_string(oname, pno->ColSelect,"",1);
1569       SUMA_Free_Parsed_Name(pno);
1570    }
1571    return(oname);
1572 }
1573 
SUMA_DuplicateParsedName(SUMA_PARSED_NAME * pn)1574 SUMA_PARSED_NAME * SUMA_DuplicateParsedName(SUMA_PARSED_NAME *pn) {
1575 
1576    if (pn && pn->NameAsParsed) {
1577       return(SUMA_ParseFname(pn->NameAsParsed, pn->cwdAsParsed));
1578    }
1579    return(NULL);
1580 }
1581 
SUMA_ModifyParsedName(SUMA_PARSED_NAME * pn,char * what,char * val)1582 SUMA_PARSED_NAME * SUMA_ModifyParsedName (SUMA_PARSED_NAME *pn,
1583                                           char *what, char *val)
1584 {
1585    static char FuncName[]={"SUMA_ModifyParsedName"};
1586    char *fullname=NULL;
1587    SUMA_PARSED_NAME *pno=NULL;
1588 
1589    SUMA_ENTRY;
1590 
1591    if (!what || !pn) SUMA_RETURN(NULL);
1592 
1593    if (!strcmp(what,"prepend")) {
1594       if (!val) SUMA_RETURN(NULL);
1595       if (pn->StorageMode ==  STORAGE_BY_BRICK) {
1596          fullname=NULL;
1597          if(strstr(pn->NameAsParsed,"/"))
1598             fullname = SUMA_append_replace_string(fullname,
1599                                                 pn->Path, "", 1);
1600          fullname = SUMA_append_replace_string(fullname,
1601                                                 val, "", 1);
1602          fullname = SUMA_append_replace_string(fullname,
1603                                              pn->Prefix, "", 1);
1604          fullname = SUMA_append_replace_string(fullname,
1605                                              pn->View, "",1);
1606          fullname = SUMA_append_replace_string(fullname,
1607                                              pn->TypeExt, "",1);
1608          fullname = SUMA_append_replace_string(fullname,
1609                                     pn->NodeSelect, "", 1);
1610          fullname = SUMA_append_replace_string(fullname,
1611                                     pn->RowSelect, "", 1);
1612          fullname = SUMA_append_replace_string(fullname,
1613                                     pn->ColSelect, "", 1);
1614          pno=SUMA_ParseFname(fullname, pn->RelDir);
1615          SUMA_free(fullname); fullname=NULL;
1616       } else {
1617          fullname=NULL;
1618          if(strstr(pn->NameAsParsed,"/"))
1619             fullname = SUMA_append_replace_string(fullname,
1620                                                 pn->Path, "", 1);
1621          fullname = SUMA_append_replace_string(fullname,
1622                                                 val, "", 1);
1623          fullname = SUMA_append_replace_string(fullname,
1624                                              pn->Prefix, "", 1);
1625          fullname = SUMA_append_replace_string(fullname,
1626                                     pn->NodeSelect, "", 1);
1627          fullname = SUMA_append_replace_string(fullname,
1628                                     pn->RowSelect, "", 1);
1629          fullname = SUMA_append_replace_string(fullname,
1630                                     pn->ColSelect, "", 1);
1631          pno=SUMA_ParseFname(fullname, pn->RelDir);
1632          SUMA_free(fullname); fullname=NULL;
1633       }
1634    } else if (!strcmp(what,"append")) {
1635       if (!val) SUMA_RETURN(NULL);
1636       if (pn->StorageMode ==  STORAGE_BY_BRICK) {
1637          fullname=NULL;
1638          if(strstr(pn->NameAsParsed,"/"))
1639             fullname = SUMA_append_replace_string(fullname,
1640                                                 pn->Path, "", 1);
1641          fullname = SUMA_append_replace_string(fullname,
1642                                              pn->Prefix, "", 1);
1643          fullname = SUMA_append_replace_string(fullname,
1644                                                 val, "", 1);
1645          fullname = SUMA_append_replace_string(fullname,
1646                                              pn->View, "",1);
1647          fullname = SUMA_append_replace_string(fullname,
1648                                              pn->TypeExt, "",1);
1649          fullname = SUMA_append_replace_string(fullname,
1650                                     pn->NodeSelect, "", 1);
1651          fullname = SUMA_append_replace_string(fullname,
1652                                     pn->RowSelect, "", 1);
1653          fullname = SUMA_append_replace_string(fullname,
1654                                     pn->ColSelect, "", 1);
1655          pno=SUMA_ParseFname(fullname, pn->RelDir);
1656          SUMA_free(fullname); fullname=NULL;
1657       } else {
1658          fullname=NULL;
1659          if(strstr(pn->NameAsParsed,"/"))
1660             fullname = SUMA_append_replace_string(fullname,
1661                                                 pn->Path, "", 1);
1662          fullname = SUMA_append_replace_string(fullname,
1663                                              pn->Prefix, "", 1);
1664          fullname[strlen(fullname)-strlen(pn->TypeExt)]='\0';
1665          fullname = SUMA_append_replace_string(fullname,
1666                                                 val, "", 1);
1667          fullname = SUMA_append_replace_string(fullname,
1668                                              pn->TypeExt, "",1);
1669          fullname = SUMA_append_replace_string(fullname,
1670                                     pn->NodeSelect, "", 1);
1671          fullname = SUMA_append_replace_string(fullname,
1672                                     pn->RowSelect, "", 1);
1673          fullname = SUMA_append_replace_string(fullname,
1674                                     pn->ColSelect, "", 1);
1675          pno=SUMA_ParseFname(fullname, pn->RelDir);
1676          SUMA_free(fullname); fullname=NULL;
1677       }
1678    } else if (!strcmp(what,"view")) {
1679       char vval[6]={""};
1680       if (!val) SUMA_RETURN(NULL);
1681       if (val[0] != '+') sprintf(vval,"+%c%c%c%c",val[0],val[1],val[2],val[3]);
1682       else sprintf(vval,"%c%c%c%c%c",val[0],val[1],val[2],val[3], val[4]);
1683       if (pn->StorageMode ==  STORAGE_BY_BRICK ||
1684           pn->StorageMode ==  STORAGE_UNDEFINED) {
1685          fullname=NULL;
1686          if(strstr(pn->NameAsParsed,"/"))
1687             fullname = SUMA_append_replace_string(fullname,
1688                                                 pn->Path, "", 1);
1689          fullname = SUMA_append_replace_string(fullname,
1690                                              pn->Prefix, "", 1);
1691          fullname = SUMA_append_replace_string(fullname,
1692                                               vval,"",1);
1693          fullname = SUMA_append_replace_string(fullname,
1694                                              pn->TypeExt, "",1);
1695          fullname = SUMA_append_replace_string(fullname,
1696                                     pn->NodeSelect, "", 1);
1697          fullname = SUMA_append_replace_string(fullname,
1698                                     pn->RowSelect, "", 1);
1699          fullname = SUMA_append_replace_string(fullname,
1700                                     pn->ColSelect, "", 1);
1701          pno=SUMA_ParseFname(fullname, pn->RelDir);
1702          SUMA_free(fullname); fullname=NULL;
1703       } else {
1704          pno = SUMA_DuplicateParsedName(pn);
1705       }
1706    }
1707 
1708    SUMA_RETURN(pno);
1709 }
1710 /*!
1711    \brief ans = SUMA_isExtension(filename, ext);
1712       YUP if filename has the extension ext
1713 */
SUMA_isExtension(char * filename,char * ext)1714 SUMA_Boolean SUMA_isExtension(char *filename, char *ext)
1715 {
1716    static char FuncName[]={"SUMA_isExtension"};
1717    int cnt, N_ext, N_filename;
1718 
1719    SUMA_ENTRY;
1720 
1721    if (!filename) SUMA_RETURN(NOPE);
1722    if (!ext) SUMA_RETURN(NOPE);
1723    N_ext = strlen(ext);
1724    N_filename = strlen(filename);
1725    if (N_ext > N_filename) SUMA_RETURN(NOPE);
1726 
1727    cnt = 1;
1728    while (cnt <= N_ext) {
1729       if (filename[N_filename-cnt] != ext[N_ext-cnt]) SUMA_RETURN(NOPE);
1730       ++cnt;
1731    }
1732 
1733    SUMA_RETURN(YUP);
1734 }
1735 
SUMA_CropExtension(char * filename,char * ext)1736 char * SUMA_CropExtension(char *filename, char *ext)
1737 {
1738    static char FuncName[]={"SUMA_CropExtension"};
1739    int cnt, N_ext, N_filename;
1740 
1741    SUMA_ENTRY;
1742 
1743    if (!filename) SUMA_RETURN(filename);
1744    if (!ext) SUMA_RETURN(filename);
1745    N_ext = strlen(ext);
1746    N_filename = strlen(filename);
1747    if (N_ext > N_filename) SUMA_RETURN(filename);
1748 
1749    cnt = 1;
1750    while (cnt <= N_ext) {
1751       if (filename[N_filename-cnt] != ext[N_ext-cnt]) SUMA_RETURN(filename);
1752       ++cnt;
1753    }
1754    filename[N_filename-N_ext] = '\0';
1755 
1756    SUMA_RETURN(filename);
1757 }
1758 
1759 /*!
1760    \brief ans = SUMA_Extension(filename, ext, Remove);
1761       removes or enforces an arbitrary extension from/to a filename
1762 
1763    \param filename(char *) input filename
1764    \param ext (char *) extension
1765    \param Remove (SUMA_Boolean) YUP = Remove extension if found
1766                                       Do nothing if it is not there already
1767                                 NOPE = Add extension if not there
1768                                        Do nothing if it is there already
1769    \returns ans (char*) containing modified filename
1770 
1771    - You must free ans on your own
1772    Examples:
1773       {
1774       char *ans=NULL;
1775       ans = SUMA_Extension("Junk.niml.roi", ".niml.roi", YUP);
1776       SUMA_LH(ans); SUMA_free(ans);
1777 
1778       ans = SUMA_Extension("Junk.niml.roi", ".niml.roi", NOPE);
1779       SUMA_LH(ans); SUMA_free(ans);
1780 
1781       ans = SUMA_Extension("Junk.niml.roi", ".niml.roxi", NOPE);
1782       SUMA_LH(ans); SUMA_free(ans);
1783 
1784       ans = SUMA_Extension("Junk.niml.roi", ".niml.roxi", YUP);
1785       SUMA_LH(ans); SUMA_free(ans);
1786 
1787       ans = SUMA_Extension("Junk.niml.roi", "", YUP);
1788       SUMA_LH(ans); SUMA_free(ans);
1789 
1790       ans = SUMA_Extension(".roi", "Junk.niml.roi", NOPE);
1791       SUMA_LH(ans); SUMA_free(ans);
1792 
1793       ans = SUMA_Extension("", "", NOPE);
1794       SUMA_LH(ans); SUMA_free(ans);
1795 
1796       exit(1);
1797     }
1798 
1799 */
1800 
SUMA_Extension(char * filename,char * ext,SUMA_Boolean Remove)1801 char *SUMA_Extension(char *filename, char *ext, SUMA_Boolean Remove)
1802 {
1803    static char FuncName[]={"SUMA_Extension"};
1804    char *ans = NULL;
1805    int i, next, nfilename, ifile;
1806    SUMA_Boolean NoMatch = NOPE;
1807    SUMA_Boolean LocalHead = NOPE;
1808 
1809    SUMA_ENTRY;
1810 
1811    if (!filename) SUMA_RETURN(NULL);
1812    nfilename = strlen(filename);
1813 
1814    if (!ext) {
1815       ans = (char *)SUMA_malloc((nfilename+1)*sizeof(char));
1816       ans = strcpy(ans,filename);
1817       SUMA_RETURN(ans);
1818    }
1819    next = strlen(ext);
1820 
1821    if (next > nfilename && Remove) {
1822       ans = (char *)SUMA_malloc((nfilename+1)*sizeof(char));
1823       ans = strcpy(ans,filename);
1824       SUMA_RETURN(ans);
1825    }
1826    #if 0
1827    if (nfilename < next || next < 1 || nfilename < 1) {
1828       ans = (char *)SUMA_malloc((nfilename+1)*sizeof(char));
1829       ans = strcpy(ans,filename);
1830       SUMA_RETURN(ans);
1831    }
1832    #endif
1833 
1834 
1835    ifile = nfilename - next;
1836    if (ifile > 0) {
1837       NoMatch = NOPE;
1838       i = 0;
1839       do {
1840          if (LocalHead)
1841             fprintf (SUMA_STDERR,
1842                      "%s: Comparing %c %c\n",
1843                      FuncName, filename[ifile+i], ext[i]);
1844          if (filename[ifile+i] != ext[i]) NoMatch = YUP;
1845          ++i;
1846       }  while (ifile < nfilename && i < next && !NoMatch);
1847    } else {
1848       NoMatch = YUP;
1849    }
1850 
1851    if (NoMatch) {
1852       if (Remove) { /* nothing to do */
1853          SUMA_LH("NoMatch, nothing to do");
1854          ans = (char *)SUMA_malloc((nfilename+1)*sizeof(char));
1855          ans = strcpy(ans,filename);
1856          SUMA_RETURN(ans);
1857       } else { /* add extension */
1858          SUMA_LH("NoMatch, adding extension");
1859          ans = SUMA_append_extension(filename, ext);
1860          SUMA_RETURN(ans);
1861       }
1862    }else {
1863       if (Remove) { /* remove it */
1864          SUMA_LH("Match, removing extension");
1865          ans = (char *)SUMA_malloc((nfilename - next+2)*sizeof(char));
1866          for (i=0; i< nfilename - next; ++i)  ans[i] = filename[i];
1867          ans[nfilename - next] = '\0'; /* for good measure */
1868       } else { /* nothing to do */
1869          SUMA_LH("Match, nothing to do");
1870          ans = (char *)SUMA_malloc((nfilename+1)*sizeof(char));
1871          ans = strcpy(ans,filename);
1872          SUMA_RETURN(ans);
1873       }
1874    }
1875 
1876    SUMA_RETURN (ans);
1877 
1878 }
1879 
SUMA_Free_Parsed_Name(SUMA_PARSED_NAME * Test)1880 void *SUMA_Free_Parsed_Name(SUMA_PARSED_NAME *Test)
1881 {
1882    static char FuncName[]={"SUMA_Free_Parsed_Name"};
1883 
1884    SUMA_ENTRY;
1885 
1886    if (!Test) SUMA_RETURN (NULL);
1887    if (Test->AbsPath) SUMA_free(Test->AbsPath);
1888    if (Test->RelPath) SUMA_free(Test->RelPath);
1889    if (Test->RelDir) SUMA_free(Test->RelDir);
1890    if (Test->Path) SUMA_free(Test->Path);
1891    if (Test->FileName) SUMA_free(Test->FileName);
1892    if (Test->FullName) SUMA_free(Test->FullName);
1893    if (Test->Ext) SUMA_free(Test->Ext);
1894    if (Test->FileName_NoExt) SUMA_free(Test->FileName_NoExt);
1895    if (Test->RowSelect) SUMA_free(Test->RowSelect);
1896    if (Test->ColSelect) SUMA_free(Test->ColSelect);
1897    if (Test->NodeSelect) SUMA_free(Test->NodeSelect);
1898    if (Test->RangeSelect) SUMA_free(Test->RangeSelect);
1899    if (Test->NameAsParsed) SUMA_free(Test->NameAsParsed);
1900    if (Test->cwdAsParsed) SUMA_free(Test->cwdAsParsed);
1901    if (Test->ExistsAs) SUMA_free(Test->ExistsAs);
1902 
1903    SUMA_free(Test);
1904 
1905    SUMA_RETURN (NULL);
1906 }
1907 
1908 
1909 
1910 /*! Taken from filexists
1911 returns 1 if file can be read/found
1912 */
SUMA_filexists(char * f_name)1913 int SUMA_filexists (char *f_name)
1914 {/*SUMA_filexists*/
1915     FILE *outfile;
1916     static char FuncName[]={"SUMA_filexists"};
1917 
1918    SUMA_ENTRY;
1919 
1920    outfile = fopen (f_name,"r");
1921    /*fprintf(stderr,"%s %p\n", f_name, outfile);*/
1922    if (outfile == NULL) {
1923        SUMA_RETURN(0);
1924    } else {
1925        fclose (outfile);
1926    }
1927 
1928    SUMA_RETURN(1);
1929 
1930 }/*SUMA_filexists*/
1931 
1932 /*! \brief A function that attempts to find a file that is readable.
1933    If *fname has a full path, then returns 1 if readable, 0 otherwise
1934    If *fname has no path,
1935       Try finding file under SUMAg_cwd/ if found -> 1, else, continue
1936       If a search path is given, then that path is searched
1937          if file is found -> 1 else -> 0
1938       If no search path is given, then path from user's environment is searched.
1939    If file is found, old name is freed and new one put in its place.
1940 */
SUMA_search_file(char ** fnamep,char * epath)1941 int SUMA_search_file(char **fnamep, char *epath)
1942 {
1943    static char FuncName[]={"SUMA_search_file"};
1944    SUMA_PARSED_NAME *pn = NULL;
1945    char dname[THD_MAX_NAME], ename[THD_MAX_NAME], *elocal=NULL, *af=NULL;
1946    int epos=0, ll=0, ii=0, id = 0, imode=1;
1947    SUMA_Boolean LocalHead = NOPE;
1948 
1949    SUMA_ENTRY;
1950 
1951    /* does it exist? */
1952    if ( SUMA_filexists(*fnamep) ) {
1953       SUMA_LH("Found file %s as named", *fnamep);
1954       SUMA_RETURN(1); /* all is well */
1955    }
1956 
1957    SUMA_LH("Searching for variations on %s", *fnamep);
1958    #if defined SUMA_COMPILED
1959    /* else, the hard work, first check with cwd options
1960    for suma programs*/
1961    pn = SUMA_ParseFname(*fnamep, SUMAg_CF->cwd);
1962    if ( SUMA_filexists(pn->FullName) ) {
1963       SUMA_free(*fnamep);
1964       *fnamep = SUMA_copy_string(pn->FullName);
1965       pn = SUMA_Free_Parsed_Name(pn);
1966       SUMA_RETURN(1); /* all is well */
1967    }
1968    pn = SUMA_Free_Parsed_Name(pn);
1969    /* try again perhaps for compressed data */
1970    elocal = SUMA_append_string(*fnamep, ".gz");
1971    pn = SUMA_ParseFname(elocal, SUMAg_CF->cwd);
1972    if ( SUMA_filexists(pn->FullName) ) {
1973       SUMA_free(*fnamep);
1974       *fnamep = SUMA_copy_string(pn->FullName);
1975       pn = SUMA_Free_Parsed_Name(pn);
1976       SUMA_RETURN(2); /* all is well */
1977    }
1978    pn = SUMA_Free_Parsed_Name(pn);
1979    #endif
1980 
1981    /* Now work the path (based on code form get_atlas function */
1982    if (!epath) {
1983       #if 0 /* overkill, as Yaroslav Halchenko pointed out*/
1984          epath = getenv("PATH") ;
1985          if( epath == NULL ) SUMA_RETURN(NOPE) ; /* nothing left to do */
1986       #else
1987          /* Search in AFNI's standard locations */
1988          af = find_afni_file(*fnamep, 0, NULL);
1989          if (af[0] != '\0') {
1990             SUMA_free(*fnamep);
1991             *fnamep = SUMA_copy_string(af);
1992             SUMA_RETURN(1);
1993          }
1994       #endif
1995       SUMA_RETURN(NOPE); /* miserable pathless failure */
1996    }
1997 
1998    /*----- copy path list into local memory -----*/
1999 
2000    ll = strlen(epath) ;
2001    elocal = (char *)SUMA_calloc(ll+2, sizeof(char));
2002 
2003    /*----- put a blank at the end -----*/
2004    strcpy( elocal , epath ) ; elocal[ll] = ' ' ; elocal[ll+1] = '\0' ;
2005 
2006    /*----- replace colons with blanks -----*/
2007    for( ii=0 ; ii < ll ; ii++ )
2008      if( elocal[ii] == ':' ) elocal[ii] = ' ' ;
2009 
2010    /*----- extract blank delimited strings;
2011            use as directory names to look for atlas -----*/
2012    imode = 1;
2013    while (imode < 3) {
2014       epos = 0 ;
2015       do{
2016          ii = sscanf( elocal+epos , "%s%n" , ename , &id ); /* next substring */
2017          if( ii < 1 ) break ;                               /* none -> done   */
2018 
2019          epos += id ;                               /* char after last scanned */
2020 
2021          ii = strlen(ename) ;                         /* make sure name has   */
2022          if( ename[ii-1] != '/' ){                    /* a trailing '/' on it */
2023              ename[ii]  = '/' ; ename[ii+1] = '\0' ;
2024          }
2025          strcpy(dname,ename) ;
2026          SUMA_strncat(dname,*fnamep, THD_MAX_NAME-1) ;     /* add dataset name */
2027          if (imode == 2) {
2028             SUMA_strncat(dname,".gz", THD_MAX_NAME-1); /* add compression flag */
2029          }
2030          if ( SUMA_filexists(dname) ) {
2031             SUMA_free(*fnamep); *fnamep = SUMA_copy_string(dname);
2032             SUMA_free(elocal); elocal=NULL;
2033             SUMA_RETURN(imode); /* all is well */
2034          }
2035 
2036       } while( epos < ll ) ;  /* scan until 'epos' is after end of epath */
2037       ++imode;
2038    }
2039    /* nothing */
2040    SUMA_free(elocal); elocal=NULL;
2041 
2042    SUMA_RETURN(0); /* bummer */
2043 }
2044 
2045 /*!
2046    \brief function that tests whether a string contains N numbers
2047 
2048    WARNING: This function will deform s !
2049 
2050    \param str (char *) null terminated string
2051    \param N (void *) This is an integer in disguise
2052    \return 1: If str is NULL or N numbers were found in str
2053 
2054    \sa SUMA_isNumString
2055 */
SUMA_CleanNumString(char * s,void * p)2056 int SUMA_CleanNumString (char *s, void *p)
2057 {
2058    static char FuncName[]={"SUMA_CleanNumString"};
2059    char *endp, *strtp;
2060    int nd, N;
2061    int eos, FoundTip;
2062    double d;
2063    int LocalHead = 0;
2064 
2065    SUMA_ENTRY;
2066 
2067    if (!s) SUMA_RETURN(1);
2068 
2069    #if INT_MAX < LONG_MAX
2070       N = (int)(long int)p;
2071    #else
2072       N = (int)p;
2073    #endif
2074 
2075    /* clean s by removing trailing junk then replacing non characters by space*/
2076    if (LocalHead) fprintf (stderr, "%s: string begins:%s:\n", FuncName, s);
2077    FoundTip = 0;
2078    for (nd=strlen(s)-1; nd >=0; --nd) {
2079       if (!isdigit(s[nd]) && s[nd] != '.'  && s[nd] != '-' && s[nd] != '+') {
2080          if (!FoundTip) {
2081             s[nd]= '\0'; /* remove */
2082          } else {
2083             s[nd] = ' '; /* blank */
2084          }
2085       }else {
2086          FoundTip = 1;
2087       }
2088    }
2089 
2090    if (LocalHead) fprintf (stderr, "%s: string now:%s:\n", FuncName, s);
2091    if (strlen(s) == 1 && (s[0] == '+' || s[0] == '-' || s[0] == '.')) {
2092       SUMA_RETURN(0);
2093    }
2094 
2095    /* parse s */
2096    strtp = s;
2097    endp = NULL;
2098    nd = 0;
2099    eos = 0;
2100    while (!eos) {
2101       errno=0;
2102       d = strtod(strtp, &endp);
2103       /* See SUMA_strtod() for an example on exception handling for strtod */
2104       SUMA_LHv("value %f, ERANGE: %d, EDOM %d, errno %d\n",
2105                d, ERANGE, EDOM, errno);
2106 
2107 
2108       if (endp == strtp && *endp=='\0') {
2109          eos = 1;
2110       } else {
2111          strtp = endp;
2112          ++nd;
2113          if (nd > N && nd > 1000) {
2114             SUMA_SL_Err("Fishy fish");
2115             fprintf (stderr, "%s: >>>%s<<<", FuncName, s);
2116             SUMA_RETURN(0);
2117          }
2118       }
2119    }
2120 
2121    if (LocalHead) fprintf (stderr,"%s: Read %d/%d values.\n", FuncName, nd,N);
2122    if (N != nd) {
2123       SUMA_RETURN(0);
2124    } else {
2125       SUMA_RETURN(1);
2126    }
2127 
2128 }
2129 
SUMA_CleanNumStringSide(char * s,void * p)2130 int SUMA_CleanNumStringSide (char *s, void *p)
2131 {
2132    static char FuncName[]={"SUMA_CleanNumStringSide"};
2133    char *s2=NULL, c ='\0';
2134    int nn=0;
2135 
2136    SUMA_ENTRY;
2137 
2138    if (!s) SUMA_RETURN(SUMA_CleanNumString(s,p));
2139    deblank_name(s);
2140 
2141    nn = strlen(s);
2142    if (s[0]=='r' || s[0]=='R') {
2143       c = 'R';
2144       s2 = SUMA_copy_string(s+1);
2145    } else if (s[nn-1]=='r' || s[nn-1]=='R') {
2146       c = 'R';
2147       s[nn-1]='\0'; s2 = SUMA_copy_string(s);
2148    } else if (s[0]=='l' || s[0]=='L') {
2149       c = 'L';
2150       s2 = SUMA_copy_string(s+1);
2151    } else if (s[nn-1]=='l' || s[nn-1]=='L') {
2152       c = 'L';
2153       s[nn-1]='\0'; s2 = SUMA_copy_string(s);
2154    } else {
2155       /* nothing to do */
2156       SUMA_RETURN(SUMA_CleanNumString(s,p));
2157    }
2158 
2159    /* Now clean s2 */
2160    s2 = SUMA_copy_string(s);
2161    nn = SUMA_CleanNumString(s2,p);
2162 
2163    /* Put side back in string */
2164    sprintf(s,"%c%s",c,s2);
2165    SUMA_free(s2); s2=NULL;
2166 
2167    SUMA_RETURN(nn);
2168 }
2169 
2170 /*
2171    Much like SUMA_CleanNumString, but leaves s untouched
2172 */
SUMA_isNumString(char * s,void * p)2173 int SUMA_isNumString (char *s, void *p)
2174 {
2175    static char FuncName[]={"SUMA_isNumString"};
2176    int ans;
2177    char *sc;
2178 
2179    SUMA_ENTRY;
2180 
2181    sc = SUMA_copy_string(s);
2182    ans = SUMA_CleanNumString(sc,p);
2183    if(sc) SUMA_free(sc); sc = NULL;
2184    SUMA_RETURN(ans);
2185 }
2186 
2187 
SUMA_NumStringUnits(char * s,int marktip)2188 int SUMA_NumStringUnits (char *s, int marktip)
2189 {
2190    static char FuncName[]={"SUMA_NumStringUnits"};
2191    int unt = SUMA_NO_NUM_UNITS;
2192    int FoundTip = 0, nd = 0, ndm=0, tiploc=-1;
2193    int LocalHead = 0;
2194 
2195    SUMA_ENTRY;
2196 
2197    if (!s) SUMA_RETURN(unt);
2198 
2199    /* go back until you hit the tip of the number */
2200    FoundTip = 0;
2201    ndm = strlen(s);
2202    nd=ndm-1;
2203    while ( nd >=0 && !FoundTip) {
2204       if (isdigit(s[nd]) || s[nd] == '.' || s[nd] == '-' || s[nd] == '+') {
2205          FoundTip = 1;
2206       } else {
2207          --nd;
2208       }
2209    }
2210    SUMA_LHv("Fount tip %d on %s at %d\n",FoundTip, s, nd);
2211    if (!FoundTip) SUMA_RETURN(unt);
2212 
2213 
2214    /* now move forward, skipping blanks, commas, parenthesis */
2215    SUMA_LH("Got tip, goind forward");
2216    ++nd;
2217    FoundTip = 0;
2218    tiploc = -1;
2219    while (nd < ndm && !FoundTip) {
2220       if (  isspace(s[nd]) || s[nd] == ',' ||
2221             s[nd] == '[' || s[nd] == '(' || s[nd] == '{') {
2222          ++nd;
2223       } else {
2224          FoundTip = 1;
2225          tiploc=nd;
2226       }
2227    }
2228    SUMA_LHv("Fount tip %d on %s at %d\n",FoundTip, s, nd);
2229 
2230    /* now look for unit string */
2231    SUMA_LH("%s",(s+nd));
2232    unt = SUMA_NO_NUM_UNITS;
2233    if (0) ; /* order of following else ifs matters */
2234    else if (!strncmp((s+nd), "mm", 2))
2235                               unt = SUMA_MM_UNITS;
2236    else if (!strncmp((s+nd), "p", 1))
2237                               unt = SUMA_P_VALUE_UNITS;
2238    else if (!strncmp((s+nd), "q",1))
2239                               unt = SUMA_Q_VALUE_UNITS;
2240    else if (!strncmp((s+nd), "%",1))
2241                               unt = SUMA_PERC_VALUE_UNITS;
2242 
2243    if (marktip && tiploc>-1) s[tiploc] = '\0';
2244 
2245    SUMA_RETURN(unt);
2246 }
2247 
SUMA_strtod(char * n,double * valp)2248 int SUMA_strtod(char *n, double *valp)
2249 {
2250    static char FuncName[]={"SUMA_strtod"};
2251    char *stp=NULL;
2252    SUMA_Boolean LocalHead = NOPE;
2253 
2254    SUMA_ENTRY;
2255    if (!n || !valp) SUMA_RETURN(0);
2256 
2257    errno = 0;
2258    *valp = strtod (n, &stp);
2259 
2260    #if 0
2261       /* Not all constants below are standard */
2262    SUMA_LHv("s=%s, stp=%s, val=%f, \n"
2263             "errno=%d, HUGE_VAL=%g,%Lg,%g, EINVAL=%d,ERANGE=%d\n",
2264             (char *)n, CHECK_NULL_STR(stp), *valp, errno,
2265             HUGE_VAL, HUGE_VALL, HUGE_VALF, EINVAL, ERANGE);
2266    #endif
2267 
2268    if ((errno == ERANGE &&(*valp == LONG_MAX || *valp == LONG_MIN))
2269          || (errno != 0 && *valp == 0) ||
2270          stp == n /* nothing numeric was read */) {
2271       SUMA_RETURN(0);
2272    }
2273 
2274    /* all is well */
2275    SUMA_RETURN(1);
2276 }
2277 
2278 /*!
2279    \brief function that parses a string of numbers into a float vector
2280 
2281    \param str (char *) null terminated string
2282    \param vv (void*) vector where values will be stored
2283    \param N (int) This is the number of values desired
2284    \param prec (int) 1=float 2=double
2285    \return int: This is the number of values read.
2286       The function will not register in fv more than N values
2287       (to keep from running over preallocated space), but it
2288       will return the full number of values found.
2289 
2290       -1 in case of error
2291    \sa SUMA_CleanNumString
2292    \sa SUMA_strtol_vec
2293    \sa SUMA_AdvancePastNumbers
2294    \sa SUMA_NumStringUnits
2295 */
2296 
SUMA_StringToNum(char * s,void * vv,int N,int prec)2297 int SUMA_StringToNum (char *s, void *vv, int N, int prec)
2298 {
2299    static char FuncName[]={"SUMA_StringToNum"};
2300    char *endp, *strtp;
2301    int nd;
2302    int eos, FoundTip;
2303    double d;
2304    float *fv=NULL;
2305    double *dv=NULL;
2306    int LocalHead = 0;
2307 
2308    SUMA_ENTRY;
2309 
2310    if (!s || prec < 1) SUMA_RETURN(0);
2311 
2312    if (LocalHead) fprintf (stderr, "%s: string was:%s:\n", FuncName, s);
2313    /* clean s by removing trailing junk then replacing non characters by space*/
2314    FoundTip = 0;
2315    for (nd=strlen(s)-1; nd >=0; --nd) {
2316       if (!SUMA_IS_NUM_CHAR(s,nd)) {
2317          if (!FoundTip) {
2318             s[nd]= '\0'; /* remove */
2319          } else {
2320             s[nd] = ' '; /* blank */
2321          }
2322       }else {
2323          FoundTip = 1;
2324       }
2325    }
2326 
2327    if (LocalHead) fprintf (stderr, "%s: string now:%s:\n", FuncName, s);
2328 
2329    if (prec > 1) dv = (double *)vv;
2330    else fv = (float *)vv;
2331 
2332    /* parse s */
2333    strtp = s;
2334    endp = NULL;
2335    nd = 0;
2336    eos = 0;
2337    while (!eos) {
2338       errno = 0;
2339       d = strtod(strtp, &endp);
2340       /* See SUMA_strtod() for an example on exception handling for strtod */
2341 
2342       if (endp == strtp && *endp=='\0') {
2343          eos = 1;
2344       } else {
2345          if (nd < N) {
2346             if (prec > 1) dv[nd] = d;
2347             else fv[nd] = (float)d;
2348          }
2349          strtp = endp;
2350          ++nd;
2351          if (nd > N && nd >1000) {
2352             SUMA_SL_Err("Something's fishy");
2353             fprintf (stderr, "s = >>>%s<<<\nnd = %d\n", s, nd);
2354             SUMA_RETURN(-1);
2355          }
2356       }
2357    }
2358 
2359    if (LocalHead) fprintf (stderr,"%s: Read %d/%d values.\n", FuncName, nd, N);
2360 
2361    SUMA_RETURN(nd);
2362 
2363 }
2364 
2365 /* Like SUMA_StringToNum but looks for side flags in beginning or end
2366 Those would be L or R at the very beginning or very end.
2367 Function also deblanks s
2368 */
SUMA_StringToNumSide(char * s,void * vv,int N,int prec,int * Side)2369 int SUMA_StringToNumSide(char *s, void *vv, int N, int prec, int *Side)
2370 {
2371    static char FuncName[]={"SUMA_StringToNumSide"};
2372    int nn = 0;
2373 
2374    SUMA_ENTRY;
2375 
2376    *Side = SUMA_NO_SIDE;
2377    if (!s) SUMA_RETURN(SUMA_StringToNum(s,vv,N,prec));
2378 
2379    deblank_name(s);
2380    /* Could get something like 'v"55R"' from DriveSuma. clean a little */
2381    if (s[0] == 'v') {
2382       ++s;
2383       dequote_name(s, '\0');
2384    }
2385    nn = strlen(s);
2386    if (s[0]=='r' || s[0]=='R') {
2387       *Side = SUMA_RIGHT;
2388       ++s;
2389    } else if (s[nn-1]=='r' || s[nn-1]=='R') {
2390       *Side = SUMA_RIGHT;
2391       s[nn-1]='\0';
2392    } else if (s[0]=='l' || s[0]=='L') {
2393       *Side = SUMA_LEFT;
2394       ++s;
2395    } else if (s[nn-1]=='l' || s[nn-1]=='L') {
2396       *Side = SUMA_LEFT;
2397       s[nn-1]='\0';
2398    }
2399 
2400    SUMA_RETURN(SUMA_StringToNum(s,vv,N,prec));
2401 }
2402 
2403 
2404 /*!
2405    \brief forces a string to be of a certain length.
2406    If truncation is necessary, ... are inserted at
2407    the end of the string.
2408 
2409    You need to free the returned pointer
2410 */
SUMA_set_string_length(char * buf,char cp,int n)2411 char *SUMA_set_string_length(char *buf, char cp, int n)
2412 {
2413    static char FuncName[]={"SUMA_set_string_length"};
2414    char *lbl=NULL, *lbl30=NULL;
2415 
2416    SUMA_ENTRY;
2417 
2418    if (!buf) SUMA_RETURN(NULL);
2419 
2420    lbl = SUMA_truncate_string (buf, n);
2421    if (!lbl) {
2422       SUMA_SL_Err("Failed to truncate");
2423       SUMA_RETURN(NULL);
2424    }
2425 
2426    if (strlen(lbl) != n) {
2427       lbl30 = SUMA_pad_string(lbl, cp, n, 1);
2428       SUMA_free(lbl); lbl = NULL;
2429    } else {
2430       lbl30 = lbl; lbl = NULL;
2431    }
2432 
2433    SUMA_RETURN(lbl30);
2434 }
2435 
2436 /*!
2437    \brief padds a string to a certain length.
2438    You can use this function to crop a string to
2439    the specified number of characters n
2440    Padding is done with character cp
2441    The original string is not modified.
2442 
2443    s_tr = SUMA_pad_string(s1, cp, n, add2end);
2444 
2445    \sa SUMA_pad_str
2446    \sa SUMA_truncate_string
2447    - free returned pointer with: if(s_tr) SUMA_free(s_tr);
2448 */
SUMA_pad_string(char * buf,char cp,int n,int add2end)2449 char *SUMA_pad_string(char *buf, char cp, int n, int add2end)
2450 {
2451    static char FuncName[]={"SUMA_pad_string"};
2452    char *atr = NULL;
2453    int i, ib, nb;
2454    SUMA_Boolean LocalHead = NOPE;
2455 
2456    SUMA_ENTRY;
2457 
2458    if (!buf) SUMA_RETURN(NULL);
2459 
2460    atr = (char *) SUMA_calloc(n+2, sizeof(char));
2461    nb = strlen(buf);
2462 
2463    if (add2end) { /* add to end */
2464       i=0;
2465       while (i < n) {
2466          if (i<nb) atr[i] = buf[i];
2467          else atr[i] = cp;
2468          ++i;
2469       }
2470       atr[i] = '\0';
2471    } else {
2472       atr[n] = '\0';
2473       i = n -1;
2474       ib = nb - 1;
2475       while (i >= 0) {
2476          if (ib >=0) atr[i] = buf[ib];
2477          else atr[i] = cp;
2478          --i; --ib;
2479       }
2480 
2481    }
2482 
2483    if (LocalHead) {
2484       fprintf(SUMA_STDERR,"%s:\nin\t:%s:\nout\t:%s:\n", FuncName, buf, atr);
2485    }
2486    SUMA_RETURN(atr);
2487 }
2488 
2489 /*!
2490    \brief truncates a string to a certain length.
2491    Adds ... as the last characters of the string
2492    The original string is not modified.
2493 
2494    s_tr = SUMA_truncate_string(s1, n);
2495 
2496    - free returned pointer with: if(s_tr) SUMA_free(s_tr);
2497 */
SUMA_truncate_string(char * buf,int n)2498 char *SUMA_truncate_string(char *buf, int n)
2499 {
2500    static char FuncName[]={"SUMA_truncate_string"};
2501    char *atr = NULL;
2502    int i;
2503 
2504    SUMA_ENTRY;
2505 
2506    if (!buf) SUMA_RETURN(NULL);
2507 
2508    if (n < 5) {
2509       fprintf(stderr,"Error %s:\nNot worth the effort. N < 5.", FuncName);
2510       SUMA_RETURN(NULL);
2511    }
2512 
2513    if (strlen(buf) <= n) {
2514       atr = (char *) SUMA_calloc(strlen(buf)+2, sizeof(char));
2515       sprintf(atr, "%s", buf);
2516       SUMA_RETURN (atr);
2517    }else {
2518       atr = (char *) SUMA_calloc(n+3, sizeof(char));
2519       i=0;
2520       while (i < n - 3) {
2521          atr[i] = buf[i];
2522          ++i;
2523       }
2524       atr[i] = atr[i+1] = atr[i+2] = '.';
2525       atr[i+3] = '\0';
2526    }
2527 
2528    SUMA_RETURN(atr);
2529 }
2530 
2531 /*!
2532    \brief returns a copy of a null terminated string .
2533    s_cp = SUMA_copy_string(s1);
2534 
2535    - free returned pointer with: if(s_cp) SUMA_free(s_cp);
2536 */
SUMA_copy_string(char * buf)2537 char *SUMA_copy_string(char *buf)
2538 {
2539    static char FuncName[]={"SUMA_copy_string"};
2540    char *atr = NULL;
2541    int i;
2542 
2543    SUMA_ENTRY;
2544 
2545    if (!buf) SUMA_RETURN(NULL);
2546 
2547    atr = (char *) SUMA_calloc(strlen(buf)+2, sizeof(char));
2548 
2549    i=0;
2550    while (buf[i]) {
2551       atr[i] = buf[i];
2552       ++i;
2553    }
2554    atr[i] = '\0';
2555 
2556    SUMA_RETURN(atr);
2557 }
2558 
2559 /*!
2560    brief Return a copy of string between quotes q1 and q2
2561    s (char*) input string
2562    eop (char *) if != NULL, stop when s reaches eop, else keep going
2563                 till end of s if necessary
2564    q1 (char) opening quote. If '\0', take opening quote as s[0]
2565                            after you deblank s
2566    q2 (char) closing quote. If '\0', q2 = q1
2567    is_closed (int *) on return, set to 1 if found opening and closing quotes
2568                             0 otherwise
2569    deblank (int) remove blanks after q1 and before q2
2570    withqotes (int)  if 1 then put quotes back on output
2571 
2572    returns qs (char *) the quoted string. Free with SUMA_free(qs);
2573 */
SUMA_copy_quoted(char * s,char * eop,char q1,char q2,int deblank,int withquotes,int * is_closed)2574 char *SUMA_copy_quoted( char *s, char *eop,
2575                         char q1, char q2,
2576                         int deblank, int withquotes,
2577                         int *is_closed ) {
2578    static char FuncName[]={"SUMA_copy_quoted"};
2579    char *strn=NULL;
2580    char *op=s, *op2=NULL;
2581 
2582    SUMA_ENTRY;
2583 
2584    if (!s) SUMA_RETURN(strn);
2585    SUMA_SKIP_BLANK(s,eop);
2586 
2587    op=s;
2588    if (q1 == '\0') { q1=*op;}
2589    if (q2 == '\0') { q2=q1; }
2590 
2591    SUMA_SKIP_TO_NEXT_CHAR(op, eop, q1);
2592 
2593    op2=op+1;
2594    SUMA_SKIP_TO_NEXT_CHAR(op2, eop, q2);
2595 
2596    /* decide on closure, op and op2 are at the quotes*/
2597    if (is_closed) {
2598       if (*op == q1 && *op2 == q2) *is_closed = 1;
2599       else *is_closed = 0;
2600    }
2601    /* deblanking */
2602    if (deblank) {
2603       /* move up from q1 and skip blanks */
2604       ++op;
2605       while (SUMA_IS_BLANK(*op) && op < op2) { ++op; }
2606       --op; *op=q1;/* go back one and put q1 back */
2607 
2608       /* move down from q2 and skip blanls */
2609       --op2;
2610       while (SUMA_IS_BLANK(*op2) && op2 > op) { --op2; }
2611       ++op2; *op2=q2;/* go forward one and put q2 back */
2612    }
2613 
2614    if (withquotes) { ++op2; SUMA_COPY_TO_STRING(op,op2,strn); }
2615    else { ++op; SUMA_COPY_TO_STRING(op,op2,strn);}
2616 
2617    SUMA_RETURN(strn);
2618 }
2619 
2620 /*!
2621    Put all arguments between opening and closing quotes into one string
2622    The function starts by looking for argv[*kar] that begins with opq
2623    If opq is found, it continues looking until it finds argv[K] with ends
2624    with cloq. If a closing quote is found, a catenation of all the argvs
2625    is returned. Also *kar is updated to indicate the last used argument.
2626 */
args_in_quotes(char ** argv,int * kar,int N_argv,char * opq,char * cloq,int clearused)2627 char *args_in_quotes(char **argv, int *kar, int N_argv,
2628                      char *opq, char *cloq, int clearused)
2629 {
2630    static char FuncName[]={"args_in_quotes"};
2631    char *aq=NULL;
2632    int n, closed, n2;
2633    SUMA_Boolean LocalHead=NOPE;
2634 
2635    SUMA_ENTRY;
2636 
2637    if (!argv || !N_argv || !kar || *kar >= N_argv || !opq) RETURN(aq);
2638 
2639    n = *kar;
2640    if (begins_with(argv[n], opq,1)) {
2641       aq = SUMA_copy_string(argv[n]);
2642    } else {
2643       SUMA_RETURN(NULL);
2644    }
2645    SUMA_LHv("Begin aq %s, n=%d, argv[n]=%s, N=%d\n", aq, n, argv[n], N_argv);
2646    closed = 0;
2647    while (!(closed=ends_with(argv[n],cloq,1)) && n<N_argv-1) {
2648       aq = SUMA_append_replace_string(aq,argv[++n]," ",1);
2649       SUMA_LHv("added aq %s, n=%d, argv[n]=%s, N=%d\n", aq, n, argv[n], N_argv);
2650    }
2651    if (!closed) {
2652       SUMA_LHv("Could not find closing %s\n",cloq);
2653       SUMA_free(aq);
2654       aq = NULL;
2655    } else {
2656       if (clearused) {
2657          n2 = *kar;
2658          while (n2 < n) {
2659             argv[n2][0] = '\0'; ++n2;
2660          }
2661       }
2662       *kar = n; /* the last argument to be used */
2663    }
2664 
2665    SUMA_RETURN(aq);
2666 }
2667 
args_in_niml_quotes(char ** argv,int * kar,int N_argv,int clearused)2668 char *args_in_niml_quotes(char **argv, int *kar, int N_argv, int clearused)
2669 {
2670    char *aq=NULL;
2671 
2672    if ((aq=args_in_quotes(argv, kar, N_argv,"<","/>", clearused))) {
2673       return(aq);
2674    } else if ((aq=args_in_quotes(argv, kar, N_argv,"'<","/>'", clearused))) {
2675       return(aq);
2676    } else if ((aq=args_in_quotes(argv, kar, N_argv,"\"<","/>\"", clearused))) {
2677       return(aq);
2678    }
2679    return(NULL);
2680 }
2681 
args_in_simple_quotes(char ** argv,int * kar,int N_argv,int clearused)2682 char *args_in_simple_quotes(char **argv, int *kar, int N_argv, int clearused)
2683 {
2684    char *aq=NULL;
2685 
2686    if ((aq=args_in_quotes(argv, kar, N_argv,"'","'", clearused))) {
2687       return(aq);
2688    } else if ((aq=args_in_quotes(argv, kar, N_argv,"\"","\"", clearused))) {
2689       return(aq);
2690    }
2691    return(NULL);
2692 }
2693 
SUMA_append_extension(char * s1,char * s2)2694 char * SUMA_append_extension(char *s1, char *s2)
2695 {
2696    static char FuncName[]={"SUMA_append_extension"};
2697    char *s1c = NULL;
2698    int ns1c=0;
2699 
2700    SUMA_ENTRY;
2701 
2702    /* remove last dot */
2703    if (s1) {
2704       s1c = SUMA_copy_string(s1);
2705       ns1c = strlen(s1);
2706       if (s1c[ns1c-1]=='.') s1c[ns1c-1]='\0';
2707    }
2708 
2709    /* remove first dot */
2710    if (s2 && s2[0] == '.') ++s2;
2711 
2712    /* put them together */
2713    SUMA_RETURN(SUMA_append_replace_string(s1c, s2, ".", 1));
2714 }
2715 
2716 /*!
2717    \brief appends two null terminated strings.
2718 
2719    s_ap = SUMA_append_string(s1, s2);
2720 
2721    - s1 and s2 are copied into a new string
2722    -free returned pointer with:  if(s_ap) SUMA_free(s_ap);
2723    - None of the strings passed to the function
2724    are freed.
2725 
2726    \sa SUMA_append_replace_string
2727 */
SUMA_append_string(char * s1,char * s2)2728 char * SUMA_append_string(char *s1, char *s2)
2729 {
2730    static char FuncName[]={"SUMA_append_string"};
2731    char *atr = NULL;
2732    int i,cnt, N_s2, N_s1;
2733 
2734 
2735    SUMA_ENTRY;
2736 
2737    if (!s1 && !s2) SUMA_RETURN(NULL);
2738    if (!s1) N_s1 = 0;
2739    else N_s1 = strlen(s1);
2740 
2741    if (!s2) N_s2 = 0;
2742    else N_s2 = strlen(s2);
2743 
2744    atr = (char *) SUMA_calloc(N_s1+N_s2+2, sizeof(char));
2745 
2746    /* copy first string */
2747    cnt = 0;
2748    if (N_s1){
2749       i=0;
2750       while (s1[i]) {
2751          atr[cnt] = s1[i];
2752          ++i;
2753          ++cnt;
2754       }
2755    }
2756    if (N_s2) {
2757       i=0;
2758       while (s2[i]) {
2759          atr[cnt] = s2[i];
2760          ++i;
2761          ++cnt;
2762       }
2763    }
2764    atr[cnt] = '\0';
2765 
2766    SUMA_RETURN(atr);
2767 }
2768 
2769 
2770 /*!
2771    \brief appends two null terminated strings.
2772 
2773    s_ap = SUMA_append_replace_string(s1, s2, spc, whichTofree);
2774 
2775   \param s1 (char *) string 1
2776   \param s2 (char *) string 2
2777   \param spc (char *) spacing string
2778   \param whichTofree (int) 0 free none,
2779                            1 free s1
2780                            2 free s2
2781                            3 free s1 and s2
2782    \return s_ap (char *) a string formed by "%s%s%s", s1, spc, s2
2783 
2784    - s1 and s2 are copied into a new string with spc in between
2785    - s1 (but not s2 or spc ) IS FREED inside this function
2786    -free returned pointer with:  if(s_ap) SUMA_free(s_ap);
2787 
2788    \sa SUMA_append_string, SUMA_ar_string, SUMA_append_replace_string_eng
2789 */
SUMA_append_replace_string(char * s1,char * s2,char * Spc,int whichTofree)2790 char * SUMA_append_replace_string(char *s1, char *s2, char *Spc, int whichTofree)
2791 {
2792    return(SUMA_append_replace_string_eng(s1, s2, Spc, whichTofree, 0));
2793 }
SUMA_ar_string(char * s1,char * s2,char * Spc,int whichTofree)2794 char * SUMA_ar_string(char *s1, char *s2, char *Spc, int whichTofree)
2795 {
2796    return(SUMA_append_replace_string_eng(s1, s2, Spc, whichTofree, 1));
2797 }
SUMA_append_replace_string_eng(char * s1,char * s2,char * Spc,int whichTofree,int cleanstart)2798 char * SUMA_append_replace_string_eng(char *s1, char *s2, char *Spc,
2799                                       int whichTofree, int cleanstart)
2800 {
2801    static char FuncName[]={"SUMA_append_replace_string_eng"};
2802    char *atr = NULL;
2803    int i,cnt, N_s2, N_s1, N_Spc=0;
2804 
2805 
2806    SUMA_ENTRY;
2807 
2808    if (!s1 && !s2) SUMA_RETURN(NULL);
2809 
2810    if (!s1) N_s1 = 0;
2811    else N_s1 = strlen(s1);
2812 
2813    if (!s2) N_s2 = 0;
2814    else N_s2 = strlen(s2);
2815 
2816    if (!Spc) N_Spc = 0;
2817    else N_Spc = strlen(Spc);
2818 
2819    atr = (char *) SUMA_calloc(N_s1+N_s2+N_Spc+2, sizeof(char));
2820 
2821    /* copy first string */
2822    i=0;
2823    cnt = 0;
2824    if (s1) {
2825       while (s1[i]) {
2826          atr[cnt] = s1[i];
2827          ++i;
2828          ++cnt;
2829       }
2830    }
2831 
2832    i=0;
2833    if (Spc && (N_s1 || !cleanstart)) {
2834       while (Spc[i]) {
2835          atr[cnt] = Spc[i];
2836          ++i;
2837          ++cnt;
2838       }
2839    }
2840 
2841    i=0;
2842    if (s2) {
2843       while (s2[i]) {
2844          atr[cnt] = s2[i];
2845          ++i;
2846          ++cnt;
2847       }
2848    }
2849    atr[cnt] = '\0';
2850 
2851    switch (whichTofree) {
2852       case 0:
2853          break;
2854       case 1:
2855          if (s1) free(s1);
2856          break;
2857       case 2:
2858          if (s2) free(s2);
2859          break;
2860       case 3:
2861          if (s1) free(s1);
2862          if (s2) free(s2);
2863          break;
2864       default:
2865          fprintf(stderr, "Error %s:\nBad freeing parameter\n"
2866                          "No variables were freed.\n",
2867                          FuncName);
2868          break;
2869    }
2870 
2871    SUMA_RETURN(atr);
2872 }
2873 
SUMA_replace_string(char * s1,char * s2)2874 char * SUMA_replace_string(char *s1, char *s2)
2875 {
2876    if (s1) SUMA_free(s1);
2877    return(SUMA_copy_string(s2));
2878 }
2879 
SUMA_append_replace_num(char * s1,char * form,double num,SUMA_VARTYPE tp,int whichTofree)2880 char * SUMA_append_replace_num(char *s1, char *form, double num,
2881                                SUMA_VARTYPE tp, int whichTofree)
2882 {
2883    static char FuncName[]={"SUMA_append_replace_num"};
2884    char *atr = NULL, sbuf[500];
2885    int i,cnt, N_s2, N_s1, N_Spc=0;
2886 
2887 
2888    SUMA_ENTRY;
2889 
2890    if (!form) SUMA_RETURN(NULL);
2891    if (!s1 && !form) SUMA_RETURN(NULL);
2892    if (whichTofree > 1) {
2893       SUMA_S_Err("Can only free s1");
2894       SUMA_RETURN(NULL);
2895    }
2896    if (!s1) N_s1 = 0;
2897    else N_s1 = strlen(s1);
2898 
2899    switch(tp) {
2900       case SUMA_short:
2901       case SUMA_int:
2902          snprintf(sbuf, 450, form, (int)num);
2903          break;
2904       case SUMA_float:
2905       case SUMA_double:
2906          snprintf(sbuf, 450, form, (double)num);
2907          break;
2908       default:
2909          snprintf(sbuf, 450, "NUM_FORMAT_ERROR");
2910          break;
2911    }
2912 
2913    /* fprintf(SUMA_STDERR,"%s: Have %lf num, form:>%s<, sbuf>%s<\n",
2914       FuncName, num, form, sbuf); */
2915 
2916    atr = SUMA_append_replace_string(s1, sbuf, "", whichTofree);
2917 
2918 
2919    SUMA_RETURN(atr);
2920 }
2921 
2922 /*!
2923    \brief Appends newstring to string in SS->s while taking care of resizing space allocated for s
2924 
2925    \param SS (SUMA_STRING *) pointer to string structure
2926    \param newstring (char *) pointer to string to add to SS
2927    \return SS (SUMA_STRING *) pointer to string structure
2928                               with SS->s now containing newstring
2929    - When SS is null, 1000 characters are allocated for s (initialization)
2930                                     and s[0] = '\0';
2931    - When newstring is NULL, space allocated for SS->s is resized to the
2932      correct dimension and  a null character is placed at the end.
2933    \sa SUMA_SS2S
2934 */
SUMA_StringAppend(SUMA_STRING * SS,char * newstring)2935 SUMA_STRING * SUMA_StringAppend (SUMA_STRING *SS, char *newstring)
2936 {
2937    static char FuncName[]={"SUMA_StringAppend"};
2938    int N_inc = 0, N_cur = 0;
2939    int N_chunk = 1000;
2940    int i=0;
2941    SUMA_Boolean LocalHead = NOPE;
2942 
2943    SUMA_ENTRY;
2944 
2945    if (!SS) {
2946       if (LocalHead) fprintf (SUMA_STDERR, "%s: Allocating for SS.\n", FuncName);
2947       SS = (SUMA_STRING *) SUMA_malloc (sizeof(SUMA_STRING));
2948       SS->s = (char *) SUMA_calloc (N_chunk, sizeof(char));
2949       SS->s[0] = '\0';
2950       SS->N_alloc = N_chunk;
2951       SUMA_RETURN (SS);
2952    }
2953 
2954    if (newstring) {
2955       if (LocalHead)
2956          fprintf (SUMA_STDERR, "%s: Appending to SS->s.\n", FuncName);
2957       N_inc = strlen (newstring);
2958       N_cur = strlen (SS->s);
2959       if (SS->N_alloc < N_cur+N_inc+1) { /* must reallocate */
2960          if (LocalHead)
2961             fprintf (SUMA_STDERR, "%s: Must reallocate for SS->s.\n", FuncName);
2962          SS->N_alloc = N_cur+N_inc+N_chunk+1;
2963          SS->s = (char *)SUMA_realloc (SS->s, sizeof(char)*SS->N_alloc);
2964          if (!SS->s) {
2965             fprintf (SUMA_STDERR,
2966                      "Error %s: Failed to reallocate for s.\n", FuncName);
2967             SUMA_RETURN (NULL);
2968          }
2969       }
2970       /* append */
2971       for (i=N_cur;i<N_cur+N_inc; ++i)
2972          SS->s[i] = newstring[i-N_cur];
2973       SS->s[N_cur+N_inc] = '\0';
2974    }else {
2975       /* shrink SS->s to small size */
2976       N_cur = strlen (SS->s);
2977       if (SS->N_alloc > N_cur+1) {
2978          if (LocalHead)
2979             fprintf (SUMA_STDERR, "%s: Shrink realloc for SS->s.\n", FuncName);
2980          SS->N_alloc = N_cur+1;
2981          SS->s = (char *)SUMA_realloc (SS->s, sizeof(char)*SS->N_alloc);
2982          if (!SS->s) {
2983             fprintf (SUMA_STDERR,
2984                      "Error %s: Failed to reallocate for s.\n", FuncName);
2985             SUMA_RETURN (NULL);
2986          }
2987          /*put a null at the end */
2988          SS->s[SS->N_alloc-1] = '\0';
2989       }
2990    }
2991 
2992    SUMA_RETURN (SS);
2993 
2994 }
2995 
2996 /*!
2997    \brief Appends newstring to string in SS->s while taking care of resizing space allocated for s
2998    A variable argument version of SUMA_StringAppend
2999 
3000    \param SS (SUMA_STRING *) pointer to string structure
3001    \param newstring (char *) pointer to string to add to SS
3002    \param ..... the remaining parameters a la printf manner
3003    \return SS (SUMA_STRING *) pointer to string structure with SS->s now containing newstring
3004    - When SS is null, 1000 characters are allocated for s (initialization) and s[0] = '\0';
3005    - When newstring is NULL, space allocated for SS->s is resized to the correct dimension and
3006    a null character is placed at the end.
3007 
3008    - For this function, the formatted length of newstring should not be > than MAX_APPEND-1
3009    If that occurs, the string will be trunctated and no one should get hurt
3010 
3011    NOTE: DO NOT SEND NULL pointers in the variable argument parts or crashes will occur on SUN
3012    Such NULL pointers do not result in null vararg_ptr and cause a seg fault in vsnprintf
3013 
3014    \sa SUMA_StringAppend
3015    \sa SUMA_SS2S
3016 */
3017 
3018 #define MAX_APPEND 30000
3019 
3020 
SUMA_StringAppend_va(SUMA_STRING * SS,char * newstring,...)3021 SUMA_STRING * SUMA_StringAppend_va (SUMA_STRING *SS, char *newstring, ... )
3022 {
3023    static char FuncName[]={"SUMA_StringAppend_va"};
3024    char sbuf[MAX_APPEND+2];
3025    int nout;
3026    va_list vararg_ptr ;
3027    SUMA_Boolean LocalHead = NOPE;
3028 
3029    SUMA_ENTRY;
3030 
3031    if (!SS) {
3032       SUMA_LH("NULL SS");
3033       /* let the other one handle this */
3034       SUMA_RETURN (SUMA_StringAppend(SS,newstring));
3035    }
3036 
3037    if (newstring) {
3038       SUMA_LH("newstring %s...", newstring);
3039       /* form the newstring and send it to the olde SUMA_StringAppend */
3040       va_start( vararg_ptr ,  newstring) ;
3041       if (strlen(newstring) >= MAX_APPEND -1 ) {
3042          SUMA_SL_Err("newstring too long.\nCannot use SUMA_StringAppend_va");
3043          SUMA_RETURN(SUMA_StringAppend(SS,"Error SUMA_StringAppend_va: "
3044                                           "***string too long to add ***"));
3045       }
3046       if (LocalHead) {
3047          SUMA_LH("Calling vsnprintf");
3048          if (vararg_ptr) {
3049             SUMA_LH("Non NULL vararg_ptr");
3050          } else {
3051             SUMA_LH("NULL vararg_ptr");
3052          }
3053       }
3054       nout = vsnprintf (sbuf, MAX_APPEND * sizeof(char), newstring, vararg_ptr);
3055       if (LocalHead)
3056          fprintf(SUMA_STDERR,"%s:\n Calling va_end, nout = %d\n",
3057                   FuncName, nout);
3058       va_end(vararg_ptr);  /* cleanup */
3059 
3060       if (nout < 0) {
3061          SUMA_SL_Err("Error reported by  vsnprintf");
3062          SUMA_RETURN(SUMA_StringAppend(SS,
3063                                        "Error SUMA_StringAppend_va:"
3064                                        " ***Error reported by  vsnprintf"));
3065       }
3066       if (nout >= MAX_APPEND) {
3067          SUMA_LH("String trunctated by vsnprintf");
3068          SUMA_StringAppend(SS,sbuf);
3069          SUMA_RETURN(SUMA_StringAppend(SS,
3070                                        "\nWARNING: "
3071                                        "***Previous string trunctated because "
3072                                        "of its length. ***\n"));
3073       }
3074       SUMA_LH("Calling StringAppend on %s", sbuf);
3075       SUMA_RETURN (SUMA_StringAppend(SS,sbuf));
3076    }else {
3077       SUMA_LH("NULL newstring");
3078       /* let the other one handle this */
3079       SUMA_RETURN (SUMA_StringAppend(SS,newstring));
3080    }
3081 
3082    /* should not be here */
3083    SUMA_RETURN (NULL);
3084 
3085 }
3086 
3087 /* ***************** Environment value access begin **********************/
3088 static ENV_SPEC envlist[] = {
3089    {  "Incremental arrow rotation angle in degrees",
3090       "SUMA_ArrowRotAngle",
3091       "5" } ,
3092    {  "Color pattern (AFNI, EURO, PRINT, DEFAULT)",
3093       "SUMA_ColorPattern",
3094       "EURO" },
3095    {  "Swap mouse buttons 1 and 3",
3096       "SUMA_SwapButtons_1_3",
3097       "NO" },
3098    {  "Background color r g b. No space between values",
3099       "SUMA_BackgroundColor",
3100       "0.0,0.0,0.0" },
3101    {  "ROI color map (bgyr64, roi64, roi128, roi256)",
3102       "SUMA_ROIColorMap",
3103       "ROI_i256" },
3104    {  "Number of smoothing operations to run on convexity data",
3105       "SUMA_NumConvSmooth",
3106       "5" },
3107    {  "Colormap for convexity (gray02, gray_i02, ngray20, bgyr64, etc.)",
3108       "SUMA_ConvColorMap",
3109       "gray02" },
3110    {  "Brightness factor for convexity ",
3111       "SUMA_ConvBrightFactor",
3112       "0.5" },
3113    {  "Number of smoothing operations to run on mixed foregroung color plane\n"
3114       " before mixing with background",
3115       "SUMA_NumForeSmoothing",
3116       "0" },
3117    {  "Number of smoothing operations to run on final set of mixed colors.\n"
3118       " This would be the mixed foreground and background colors",
3119       "SUMA_NumFinalSmoothing",
3120       "0" },
3121    {  "Setup the color mixing mode (ORIG, MOD1) ",
3122       "SUMA_ColorMixingMode",
3123       "ORIG" },
3124    {  "** OBSOLETE: Port for communicating with AFNI\n"
3125       "              Listening ports are derived from SUMA_AFNI_TCP_PORT\n"
3126       "              Listening port i\n"
3127       "              SUMA_AFNI_TCP_PORT + i (i > 0)",
3128       "SUMA_AFNI_TCP_PORT",
3129       "0" /* used to be 53211 */},
3130    {  "Warn before closing with the Escape key (YES/NO)",
3131       "SUMA_WarnBeforeClose",
3132       "YES" },
3133    {  "Mask node values\n"
3134       " 0 ? YES/NO",
3135       "SUMA_MaskZero",
3136       "YES" },
3137    {  "Threshold if Val < thr (NO) or | Val | < | Thr | (YES)",
3138       "SUMA_AbsThreshold",
3139       "YES" },
3140    {  "Threshold scale precision. 2 is the minimum allowed. \n"
3141       " This value might be overriden in SUMA.",
3142       "SUMA_ThresholdScalePower",
3143       "2" },
3144    {  "Center of Rotation is based on nodes used in the mesh and not \n"
3145       " on all the nodes in NodeList",
3146       "SUMA_CenterOnPatch",
3147       "NO" },
3148    {  "Use cross ticks on axis ?",
3149       "SUMA_UseCrossTicks",
3150       "NO" },
3151    {  "Warn if 1D file looks like it needs a transpose",
3152       "SUMA_1D_Transpose_Warn",
3153       "YES" },
3154    {  "Adjust roation and translation factor of mouse with changes \n"
3155       " in zoom levels ",
3156       "SUMA_AdjustMouseMotionWithZoom",
3157       "YES" },
3158    {  "Use orthographic projection ",
3159       "SUMA_ViewOrthographicProjection",
3160       "NO" },
3161    {  "Percent gain for zooming in and out with the 'z' and 'Z' keys. \n"
3162       " Typical range from 0 to 50",
3163       "SUMA_KeyZoomGain",
3164       "5" },
3165    {  "Original FOV. Set between 1.0 and 100.0 \n"
3166       " Default is 30.0, -1 == auto",
3167       "SUMA_FOV_Original",
3168       "-1" },
3169    {  "Original windows size and width in pixels \n"
3170       " :SPX:Allowed values are:\n\n"
3171       "    'TopLeft'\n\n"
3172       "    'RightOffset'\n\n"
3173       "    'X Y' Sets only the position to top left corner\n\n"
3174       "    'X Y Xwidth Ywidth' Set also width of window\n\n"
3175       " :DEF:Allowed values are: 'TopLeft'\n"
3176       "                     'RightOffset' \n"
3177       "                     'X Y' Sets only the position to top left corner\n"
3178       "                     'X Y Xwidth Ywidth' Set also width of window\n"
3179       " :SPX:",
3180       "SUMA_Position_Original",
3181       "TopLeft" },
3182    {  "light0 color",
3183       "SUMA_Light0Color",
3184       "1.0,1.0,1.0" },
3185    {  "Ambient light ",
3186       "SUMA_AmbientLight",
3187       "1.0,1.0,1.0" },
3188    {  "Allow for replacement of pre-loaded dsets",
3189       "SUMA_AllowDsetReplacement",
3190       "YES" },
3191    {  "Allow a dataset to be assigned to a surface, even if\n"
3192       "domain of dset is specified and different for the surface.\n",
3193       "SUMA_AlwaysAssignSurface",
3194       "YES" },
3195    {  "Allow for surfaces with same DomainGrandParentID to share overlays",
3196       "SUMA_ShareGrandChildrenOverlays",
3197       "NO" },
3198    {  "Increase the resolution of images recorded with 'r' button.\n"
3199       " Increase is done by taking multiple shots that once stitched  \n"
3200       " together form a high-resolution image.\n"
3201       " The maximum resolution is set by the GL_MAX_VIEWPORT_DIMS of your\n"
3202       " graphics card. I have 4096 pixels.\n"
3203       " If you exceed this number, SUMA will make adjustments automatically.\n"
3204       " Assemble images with program 2dcat.",
3205       "SUMA_SnapshotOverSampling",
3206       "1" },
3207    {  "Ignore consecutive duplicate images in recorder",
3208       "SUMA_NoDuplicatesInRecorder",
3209       "YES" },
3210    {  "start NIML (can't do this for more than one suma at a time!)",
3211       "SUMA_START_NIML",
3212       "YES" },
3213    {  "Allow (YES) datasets with the same filename but differing ID \n"
3214       " to be considered the same.\n"
3215       " This is only useful with SUMA_AllowDsetReplacement",
3216       "SUMA_AllowFilenameDsetMatch",
3217       "YES" },
3218    {  "Freeze zoom across states",
3219       "SUMA_FreezeFOVAcrossStates",
3220       "NO" },
3221    {  "Dset color map",
3222       "SUMA_DsetColorMap",
3223       "Spectrum:red_to_blue" },
3224    {  "Show only selected dset in suma's surface controller.",
3225       "SUMA_ShowOneOnly",
3226       "YES" },
3227    {  "Update graphs, even SUMA_ShowOneOnly (or suma's '1 Only') is turned on.",
3228       "SUMA_GraphHidden",
3229       "YES" },
3230    {  "Fraction of colormap to rotate with up/down arrow keys.",
3231       "SUMA_ColorMapRotationFraction",
3232       "0.05"},
3233    {  "Size of surface controller font. \n"
3234       " Values are SMALL, BIG (old style).",
3235       "SUMA_SurfContFontSize",
3236       "SMALL"},
3237    {  "Where to position SUMA window when first opened.\n"
3238       " Values are POINTER (at the mouse pointer's location)\n"
3239       "            DEFAULT (let the window manager decide)\n",
3240       "SUMA_StartUpLocation",
3241       "DEFAULT"},
3242    {  "Numer of nodes to jump with the 'alt+arrow' keys. \n"
3243       " Valid range from 1 to 10",
3244       "SUMA_KeyNodeJump",
3245       "1" },
3246    {  "Numer of seconds to wait for SUMA to respond to DriveSuma. \n"
3247       " Valid range from 0 to 60000, see also env SUMA_DriveSumaMaxCloseWait",
3248       "SUMA_DriveSumaMaxWait",
3249       "300.0" },
3250    {  "String to use in creating left hemisphere dataset wildcards.",
3251       "SUMA_LEFT_FILE_DSET_IDENTIFIER",
3252       "*lh*.dset" },
3253    {  "String to use in creating left hemisphere dataset wildcards.",
3254       "SUMA_RIGHT_FILE_DSET_IDENTIFIER",
3255       "*rh*.dset" },
3256    {  "String to use in creating left hemisphere roi wildcards.",
3257       "SUMA_LEFT_FILE_ROI_IDENTIFIER",
3258       "*lh*.roi" },
3259    {  "String to use in creating right hemisphere roi wildcards.",
3260       "SUMA_RIGHT_FILE_ROI_IDENTIFIER",
3261       "*rh*.roi" },
3262    {  "String to use in creating left hemisphere roi wildcards.",
3263       "SUMA_LEFT_FILE_OTHER_IDENTIFIER",
3264       "*lh*" },
3265    {  "String to use in creating right hemisphere roi wildcards.",
3266       "SUMA_RIGHT_FILE_OTHER_IDENTIFIER",
3267       "*rh*" },
3268    {  "Initial Convexity Datasest opacity.",
3269       "SUMA_ConvexityDsetOpacity",
3270       "0.85" },
3271    {  "Display mode of  Label Datasest specified in spec file at startup.\n"
3272       ":SPX:"
3273       "\n"
3274       "  'YES' or 'Col': Shows it in color\n\n"
3275       "  'Con': Shows only contours (see also env SUMA_ContourThickness).\n\n"
3276       "  'C&C': Shows both colors and contours \n\n"
3277       "  'XXX'or 'No': Does not show it.\n\n"
3278       ":DEF:"
3279       "  'YES' or 'Col': Shows it in color\n"
3280       "  'Con': Shows only contours (see also env SUMA_ContourThickness).\n"
3281       "  'C&C': Shows both colors and contours \n"
3282       "  'XXX'or 'No': Does not show it.\n"
3283       ":SPX:",
3284       "SUMA_ShowLabelDsetAtStartup",
3285       "XXX" },
3286    {  "Show label at cross hair in viewer\n"
3287       "You can toggle the display at such labels with F9\n",
3288       "SUMA_ShowLabelsAtCrossHair",
3289       "YES" },
3290    {  "Initial Label Datasest opacity.",
3291       "SUMA_LabelDsetOpacity",
3292       "0.2" },
3293    {  "Attempt to recover from AFNI <--> SUMA disconnection bug.\n",
3294       "SUMA_AttemptTalkRecover",
3295       "Yes" },
3296    {  "Name of directory containing user's own SUMA color maps"
3297       " (:SPX:`*`:DEF:*:SPX:.cmap)\n",
3298       "SUMA_CmapsDir",
3299       "None" },
3300    {  "Name of color map for datasets of retinotopy angles.\n"
3301       "These would be produced by 3dRetinoPhase\n",
3302       "SUMA_RetinoAngle_DsetColorMap",
3303       "rgybr20" },
3304    {  "Name of color map for VFR datasets produced by SurfRetinoMap\n",
3305       "SUMA_VFR_DsetColorMap",
3306       "afni_n2" },
3307    {  "Coordinate units of surface nodes. Choose from 'mm' or 'cm'\n"
3308       "A bad choice can make the surfaces render with many artifacts.\n",
3309       "SUMA_NodeCoordsUnits",
3310       "mm" },
3311    {  "Which anatomically correct surf. states should not NOT be sent to AFNI?\n"
3312       "This is mostly for deciding whether one of 'white' or 'smoothwm'\n"
3313       "FreeSurfer states should not be sent to AFNI.\n"
3314       "The default is to let them all go.\n"
3315       "You can specify multiple states with a , delimited list (no spaces!). \n"
3316       "By default nothing is excluded.\n",
3317       "SUMA_DoNotSendStates",
3318       "N/A" },
3319    {  "Prefix for autorecord (suma's Ctrl+R) files. \n"
3320       "FreeSurfer states should not be sent to AFNI.\n"
3321       "Add a path if you want the files to endup in a particular directory.\n"
3322       "You can also add an extension to prefix to specify the output type.\n"
3323       "Choose from .jpg, .ppm, or .1D . The fallback type is .jpg\n",
3324       "SUMA_AutoRecordPrefix",
3325       "./SUMA_Recordings/autorecord.jpg" },
3326    {  "Font for cross hair label in SUMA viewer\n"
3327       "Choose one of: f8 f9 tr10 tr24 he10 he12 he18\n",
3328       "SUMA_CrossHairLabelFont",
3329       "f9" },
3330    {  "Linking mode of I and T sub-brick selectors\n"
3331       "Choose one of: None, Same, Stat\n",
3332       "SUMA_IxT_LinkMode",
3333       "Stat" },
3334    {  "Minimum Number of sub-bricks to trigger use of arrow field for \n"
3335       "sub-brick selectors.\n",
3336       "SUMA_ArrowFieldSelectorTrigger",
3337       "200" },
3338    {  "Use symmetric Intensity range at startup? Valid options are:\n"
3339       " YES or NO: For your preference if no decision is made by the software\n"
3340       " FYES or FNO: To force your preference and keep software from deciding\n",
3341       "SUMA_Sym_I_Range",
3342       "YES" },
3343    {  "Set auto Intensity range by default (YES or NO)\n",
3344       "SUMA_Auto_I_Range",
3345       "NO" },
3346    {  "Set auto Brightness range by default (YES or NO)\n",
3347       "SUMA_Auto_B_Range",
3348       "NO" },
3349    {  "Set thickness of dataset contours\n",
3350       "SUMA_ContourThickness",
3351       "1.0" },
3352    {  "Merge separated left/right states for inflated/spherical/etc. surfaces\n"
3353       "Choose from YES or NO",
3354       "SUMA_LHunify",
3355       "YES" },
3356    {  "Put surface controllers in same window\n"
3357       "Choose from YES or NO",
3358       "SUMA_SameSurfCont",
3359       "YES" },
3360    {  "Adjust offset of Surface Viewers as they are first open\n"
3361       "Choose from AUTO or provide two X Y offsets.",
3362       "SUMA_WindowOffset",
3363       "Auto" },
3364    {  "Lock views across viewers\n"
3365       "Choose from YES or NO.",
3366       "SUMA_LockViewers",
3367       "YES" },
3368    {  "Set colormap for volumes, choose any of the standard list",
3369       "SUMA_VO_ColorMap",
3370       "bw20" },
3371    {  "Force reorienting of read volume.\nTo force reorientation,\n"
3372       "Choose from RAI, LPI, RAS. etc...\n"
3373       "Use NO to avoid reorientation. This env. is for debugging purposes.\n",
3374       "SUMA_VO_Reorient",
3375       "NO" },
3376    {  "Set maximum waiting time for proper detection of closed stream\n"
3377       "This is to avoid DriveSuma's: Failed to detect closed stream ...\n"
3378       "complaint which results in a forced stream closing. Time unit is\n"
3379       "in seconds. See also env SUMA_DriveSumaMaxWait\n",
3380       "SUMA_DriveSumaMaxCloseWait",
3381       "5" },
3382    {  "Set order in which object types are rendered. This order will affect\n"
3383       "the resultant image in the few instances where alpha transparency is\n"
3384       "used. The order can be specified for only three types of objects for \n"
3385       "now: graphs, surfaces, and volumes. If you want to render graphs first,\n"
3386       "followed by volumes then surfaces then set SUMA_ObjectDisplayOrder to\n"
3387       "something like: 'graph,vol,surf'. Do not include spaces between the\n"
3388       "type names.",
3389       "SUMA_ObjectDisplayOrder",
3390       "vol,surf,graph" },
3391    {  "Font for datasets in SUMA viewer\n"
3392       "Choose one of: f8 f9 tr10 tr24 he10 he12 he18\n",
3393       "SUMA_Dset_Font",
3394       "f9" },
3395    {  "Method for representing connections to a certain node in a graph"
3396       " dataset.\n"
3397       "Choose one of: Edge, Color, Radius, C&R, XXX\n",
3398       "SUMA_Dset_NodeConnections",
3399       "Edge" },
3400    {  "Set which slices should be shown when a volume is first loaded.\n"
3401       "You can set parameters for each of the Ax, Sa, and Co planes, and\n"
3402       "the volume rendering.\n"
3403       "Each plane gets its own string formatted as such: PL:SL:MON:INC\n"
3404       "where:\n"
3405       ":SPX:\n"
3406       "   PL is the plane (Ax, Co, Sa, or Vr)\n\n"
3407       "   SL is the slice number, you can also set the number as \n"
3408       "     a fraction of the number of slices in the volume.\n\n"
3409       "   MON is the number of montage slices\n\n"
3410       "   INC is the increment between montage slices. You can use \n"
3411       "       fractions for this parameter also.\n\n"
3412       ":DEF:"
3413       "      PL is the plane (Ax, Co, Sa, or Vr)\n"
3414       "      SL is the slice number, you can also set the number as \n"
3415       "         a fraction of the number of slices in the volume.\n"
3416       "      MON is the number of montage slices\n"
3417       "      INC is the increment between montage slices. You can use \n"
3418       "          fractions for this parameter also.\n"
3419       ":SPX:"
3420       "If you want to set parameters for a certain plane, but do not\n"
3421       "want to see it, prepend the plane name with 'h' (for hide) as in 'hAx'\n"
3422       "Note that for Vr, there are no SL, MON, and INC qualifiers\n"
3423       "Also, SUMA will force the display of at least one plane because\n"
3424       "otherwise you have no way of opening a volume controller\n"
3425       "Example: 'Ax:0.5:3:10,Co:123:2:50,Vr'",
3426       "SUMA_VO_InitSlices",
3427       "Ax:0.5,Sa:0.5:2:0.5,hCo:0.5" },
3428    {  "Allow selection of voxels on 3D rendering.\n"
3429       "Choose one of: YES or NO\n",
3430       "SUMA_VrSelectable",
3431       "YES"  },
3432    {  "Perform 'Home' call in SUMA after each prying.\n"
3433       "If YES, objects are repositioned to stay in the middle of the viewer\n"
3434       "as you pry the surfaces apart. This behavior is desired in general, \n"
3435       "unless you don't like the initial positioning in the first place.\n"
3436       "Choose from YES or NO",
3437       "SUMA_HomeAfterPrying",
3438       "YES" },
3439    {  "Assume surface in TESSCON units if range is extreme\n"
3440       "If YES, surfaces with a big difference between max and min dims are\n"
3441       "scaled by 319.7. Don't set this env to YES unless this jibber jabber \n"
3442       "means.\n"
3443       "Choose from YES or NO",
3444       "SUMA_SUMA_TESSCON_AutoScale",
3445       "NO" },
3446    {  "Turn on verbose mode for function count_procs() that checks for \n"
3447       "recursive calls to a program. Do not keep this env set to YES unless\n"
3448       "you are debugging.\n",
3449       "SUMA_CountProcs_Verb",
3450       "NO" },
3451    {  "Number of transparency levels to jump with each 'o' key press\n"
3452       "Choose one of 1, 2, 4, or 8\n",
3453       "SUMA_Transparency_Step",
3454       "4" },
3455    {  "If YES, then automatically load datasets with names matching those \n"
3456       "the surface just read.\n"
3457       "For example, if you load a surface named PATH/TOY.gii, for instance,\n"
3458       "and there exists a file called PATH/TOY.niml.dset then that file\n"
3459       "is automatically loaded onto surface TOY.gii. This would work for\n"
3460       "all surface types (e.g. TOY.ply) and dataset types (e.g. TOY.1D.dset)\n"
3461       "Choose from YES or NO\n",
3462       "SUMA_AutoLoad_Matching_Dset",
3463       "YES" },
3464    {  "Colorize labeled datasets without attempting to make colors match\n"
3465       "what would be displayed in AFNI (YES or NO). Set to YES to match\n"
3466       "old style colorization preceding the addition of this variable\n",
3467       "SUMA_Classic_Label_Colors",
3468       "NO" },
3469    {  "Multiplier for range of thresholding scale.\n",
3470       "SUMA_Range_Multiplier",
3471       "1.0" },
3472    {  "Default p value to adopt when switching to a new sub-brick.\n"
3473       "Negative values mean leave the threshold alone when switching.\n",
3474       "SUMA_pval_at_switch",
3475       "-1.0" },
3476    {  "If YES, then reduce messages to only errors while driving suma\n"
3477       "Choose from YES or NO",
3478       "SUMA_DriveSumaQuiet",
3479       "NO" },
3480    {  "If YES, then show popup message windows in suma\n"
3481       "Choose from YES or NO",
3482       "SUMA_SHOWPOPUPS",
3483       "NO" },
3484 
3485    {  NULL, NULL, NULL  }
3486 };
3487 
SUMA_envlistelement(int i)3488 ENV_SPEC SUMA_envlistelement(int i) {
3489    ENV_SPEC se = envlist[i];
3490    return(se);
3491 }
3492 
SUMA_EnvVal(char * env)3493 char * SUMA_EnvVal(char *env)
3494 {
3495    static char FuncName[]={"SUMA_EnvVal"};
3496    char *eee=NULL;
3497    int i=0;
3498 
3499    SUMA_ENTRY;
3500 
3501    if (!env) SUMA_RETURN(NULL);
3502    if ((eee = getenv(env))) { SUMA_RETURN(eee); }
3503 
3504    /* search defaults*/
3505    i = 0;
3506    while (envlist[i].envhelp) {
3507       if ( envlist[i].envname &&
3508           !strcmp(envlist[i].envname, env) ) {
3509          SUMA_RETURN(envlist[i].envval);
3510       }
3511       ++i;
3512    }
3513    SUMA_RETURN(NULL);
3514 }
3515 
3516 /* Returns non 0 if the env variable matches sval
3517 
3518    The Function can handle an env that returns
3519    some character separated list if sep is not NULL
3520    For example, say env = {"The, olde, fox"}
3521    SUMA_EnvEquals(env,"olde", 0,NULL) returns 0
3522    but
3523    SUMA_EnvEquals(env,"olde", 0,",") returns 2
3524    because olde in the second word in env
3525 */
SUMA_EnvEquals(char * env,char * sval,byte ci,char * sep)3526 int SUMA_EnvEquals(char *env, char *sval, byte ci, char *sep)
3527 {
3528    static char FuncName[]={"SUMA_EnvEquals"};
3529    char *eee=NULL;
3530    NI_str_array *sar=NULL;
3531    int i=0;
3532    SUMA_Boolean LocalHead = NOPE;
3533 
3534    SUMA_ENTRY;
3535 
3536    if (!env) SUMA_RETURN(0);
3537 
3538    if (!(eee = getenv(env))) {
3539       /* search defaults*/
3540       i = 0;
3541       while (envlist[i].envhelp && !eee) {
3542          if ( envlist[i].envname &&
3543              !strcmp(envlist[i].envname, env) ) {
3544             eee = envlist[i].envval;
3545          }
3546          ++i;
3547       }
3548    }
3549 
3550    if (eee==NULL) {
3551       if (sval==NULL) SUMA_RETURN(1);
3552       else SUMA_RETURN(0);
3553    }
3554 
3555    /* have env value of some sort */
3556    if (sval == NULL) SUMA_RETURN(0);
3557    if (LocalHead)
3558       fprintf(SUMA_STDERR,
3559            "%s: eee %s, sval %s, sep %s\n", FuncName, eee, sval, sep?sep:"NULL");
3560    if (!sep) {
3561       if (ci) SUMA_RETURN(!(strcasecmp(eee,sval)));
3562       else SUMA_RETURN(!(strcmp(eee,sval)));
3563    }
3564 
3565    /* need to breakup into subsets */
3566    if (!(sar = SUMA_NI_decode_string_list( eee , sep ))) SUMA_RETURN(0);
3567    if (LocalHead)
3568       fprintf(SUMA_STDERR,
3569               "%s: Have %d vals in %s\n", FuncName, sar->num, eee);
3570    for (i=0; i<sar->num; ++i) {
3571          if (LocalHead)
3572       fprintf(SUMA_STDERR,
3573               "%s: Comapring %s to %s\n", FuncName, sval, sar->str[i]);
3574       if ( (ci && !(strcasecmp(sval,sar->str[i]))) ||
3575            !strcmp(sval, sar->str[i]) ) {
3576          sar = SUMA_free_NI_str_array(sar);
3577          SUMA_RETURN(i+1);
3578       }
3579    }
3580    sar = SUMA_free_NI_str_array(sar);
3581    SUMA_RETURN(0);
3582 }
3583 
3584 
SUMA_env_list_help(int DEFAULT_values,TFORM targ)3585 char * SUMA_env_list_help(int DEFAULT_values, TFORM targ){
3586    static char FuncName[]={"SUMA_env_list_help"};
3587    int i=0;
3588    char *sli=NULL;
3589    SUMA_STRING *SS=NULL;
3590    char *s=NULL, *eee=NULL, *userval=NULL;
3591    ENV_SPEC se;
3592 
3593    SUMA_ENTRY;
3594 
3595    SS = SUMA_StringAppend(NULL, NULL);
3596 
3597    se = SUMA_envlistelement(i);
3598    while (se.envhelp) {
3599       if (!DEFAULT_values) {
3600          /* find the user's setting */
3601          eee = getenv(se.envname);
3602       }
3603       if (userval)
3604          SUMA_free(userval);
3605       userval=NULL;
3606       if (!eee) userval = SUMA_copy_string(se.envval);
3607       else userval = SUMA_copy_string(eee);
3608       switch (targ) {
3609          default:
3610          case TXT: /* default */
3611             sli = SUMA_ReplaceChars(se.envhelp, "\n","\n//      ");
3612             sli = SUMA_Sphinx_String_Edit(&sli, targ, 0);
3613             SS = SUMA_StringAppend_va(SS,
3614                            "// %03d-%s:\n"
3615                            "//     %s\n"
3616                            "//     default:   %s = %s\n"
3617                            "   %s = %s\n",
3618                            i, se.envname,
3619                            sli,
3620                            se.envname,
3621                            se.envval,
3622                            se.envname,
3623                            userval);
3624             SUMA_free(sli); sli = NULL;
3625             break;
3626          case ASPX:
3627          case SPX: /* Sphinxy */
3628             sli = SUMA_copy_string(se.envhelp);
3629             sli = SUMA_Sphinx_String_Edit(&sli, targ, 0);
3630             SS = SUMA_StringAppend_va(SS,
3631                            ".. _%s:\n\n"
3632                            ":ref:`%s (env)<%s>`: %s\n\n"
3633                            "  default value:   %s = %s\n\n",
3634                            se.envname,
3635                            se.envname, se.envname, sli,
3636                            se.envname, se.envval);
3637             SUMA_free(sli); sli = NULL;
3638             break;
3639       }
3640       ++i;
3641       se = SUMA_envlistelement(i);
3642    }
3643    SUMA_SS2S(SS,s);
3644 
3645    SUMA_RETURN(s);
3646 }
3647 
3648 
3649 
3650 /*!
3651    If !env return NULL
3652    if !sval and env is an environment variable, its value is returned
3653    if sval and env then env's value is compared (case insensitive) and
3654        NULL is returned if there is no match, else the environment
3655        variable value is returned
3656        Partial match is allowed
3657    DO NOT free the returned pointer
3658 */
SUMA_isEnv(char * env,char * sval)3659 char *SUMA_isEnv(char *env, char *sval) {
3660    static char FuncName[]={"SUMA_isEnv"};
3661    char *eee = NULL, svalv[256]={""}, eeev[256]={""};
3662    int i=0;
3663 
3664    SUMA_ENTRY;
3665 
3666    if (!env) SUMA_RETURN(NULL);
3667 
3668    eee = SUMA_EnvVal(env);
3669 
3670    if (!sval || !eee) SUMA_RETURN(eee);
3671    /* have eee and sval, compare them */
3672    strncpy(svalv,sval, 255);
3673    strncpy(eeev, eee, 255);
3674    SUMA_TO_LOWER(eeev); SUMA_TO_LOWER(svalv);
3675 
3676    if (!strlen(eee)) {
3677       /* set but no value. Does user seek "" ?*/
3678       if (!strlen(sval)) SUMA_RETURN(eee);
3679       else SUMA_RETURN(NULL);
3680    }
3681    for (i=0; i<strlen(svalv) && i<strlen(eeev); ++i)
3682       if (svalv[i] != eeev[i]) SUMA_RETURN(NULL);
3683 
3684    SUMA_RETURN(eee);
3685 }
3686 
SUMA_floatEnv(char * env,float defval)3687 float SUMA_floatEnv(char *env, float defval)
3688 {
3689    static char FuncName[]={"SUMA_floatEnv"};
3690    float fv = defval;
3691    char *eee=NULL, *eend=NULL;
3692    SUMA_ENTRY;
3693 
3694    if ((eee = SUMA_EnvVal(env)))  {
3695       fv = (float)strtod(eee, &eend);
3696       if (eee == eend) { /* failed */
3697          fv = defval;
3698       }
3699    }
3700 
3701    SUMA_RETURN(fv);
3702 }
3703 
3704 /* ***************** Environment value access end **********************/
3705 
3706 /*! ********** Searching/Sorting Functions ************* */
3707 
3708 /* Find the array index k where NodeIndex[n] == k
3709 Function assumes NodeIndex is monotonic ascending */
SUMA_NodeIndex_To_Index(int * NodeIndex,int N_Node,int n)3710 int SUMA_NodeIndex_To_Index(int *NodeIndex, int N_Node, int n)
3711 {
3712    static char FuncName[]={"SUMA_NodeIndex_To_Index"};
3713    if (!NodeIndex || n < 0) return(n);
3714    if (n < N_Node && NodeIndex[n]==n) return(n);
3715    else { /* have to search */
3716       return(SUMA_ibinFind(NodeIndex, N_Node, n));
3717    }
3718 }
3719 
3720 /*!
3721   SUMA_binSearch( nodeList, target, seg, ematch);
3722 
3723   This function performs a binary search.  The indices of the elements in nodeList surrounding target will be stored in (overwrite) seg; thus seg[0]=seg[1]=i implies that an exact match was found at index i.
3724   \param nodeList (float *) vector of sorted values
3725   \param target (float) value seeking
3726   \param seg (int *) contains begin and end point of segment being searched
3727   \param ematch (byte) 1: Exact match enforced. 0: Closest
3728   \return found (SUMA_Boolean) YUP if all passed correctly and target within segment, NOPE otherwise
3729 
3730   Written by Brenna Argall
3731 */
SUMA_binSearch(float * nodeList,float target,int * seg,byte ematch)3732 SUMA_Boolean SUMA_binSearch( float *nodeList, float target, int *seg,
3733                              byte ematch)
3734 {
3735    static char FuncName[]={"SUMA_binSearch"};
3736    int mid=0;
3737    int beg = seg[0], end = seg[1];
3738    SUMA_Boolean found=YUP;
3739    SUMA_Boolean LocalHead = NOPE;
3740 
3741 
3742    SUMA_LHv("%f < %f < %f\n", nodeList[beg], target, nodeList[end]);
3743    if ( end<beg) {
3744       SUMA_S_Errv("Segment must be passed with seg[0]=%d <= seg[1]=%d.\n",
3745                   seg[0], seg[1]);
3746       return (found = NOPE);
3747    }
3748    if ( nodeList[end]<nodeList[beg] ) {
3749       SUMA_S_Errv("Nodelist must be passed sorted and in ascending order.\n"
3750                   "nodeList[%d]=%f<nodeList[%d]=%f\n",
3751                   end, nodeList[end], beg, nodeList[beg]);
3752       return (found = NOPE);
3753    }
3754    if ( (nodeList[beg]>target) || (nodeList[end]<target) ) {
3755       SUMA_LHv("Don't bother, target (%f) does not lie within segment ]%f, %f[\n"
3756                   , target, nodeList[beg], nodeList[end]);
3757       return (found = NOPE);
3758    }
3759 
3760    if (beg!=end) {
3761       mid =(end-beg)/2 + beg;
3762       /**no exact match, but elements above and below found*/
3763       if (beg+1==end) {
3764          if (nodeList[end]==target) {
3765             seg[0] = end;
3766             seg[1] = end;
3767          } else if (nodeList[beg]==target) {
3768             seg[0] = beg;
3769             seg[1] = beg;
3770          } else {
3771             if (!ematch) {
3772                seg[0] = beg;
3773                seg[1] = end;
3774             } else {
3775                return(found = NOPE);
3776             }
3777          }
3778       }
3779       else if (target==nodeList[mid]) {
3780          seg[0] = mid;
3781          seg[1] = mid;
3782       }
3783       /**keep searching*/
3784       else if ( target  < nodeList[mid]) {
3785          seg[0] = beg;  seg[1] = mid;
3786          found = SUMA_binSearch( nodeList, target, seg, ematch);
3787       }
3788       else if ( target > nodeList[mid]) {
3789          seg[0] = mid;  seg[1] = end;
3790          found = SUMA_binSearch( nodeList, target, seg, ematch);
3791       }
3792    }
3793    /**exact match; beg==end or target==nodeList[ indexList[mid] ]*/
3794    else {
3795       seg[0] = mid;
3796       seg[1] = mid;
3797    }
3798 
3799    return(found);
3800 }
3801 
SUMA_binFind(float * indexList,int N_node,float target,byte ematch)3802 int SUMA_binFind( float *indexList, int N_node, float target, byte ematch) {
3803    int seg[2]={0, N_node -1};
3804    if (SUMA_binSearch(indexList, target, seg, ematch)) return(seg[0]);
3805    else return(-1);
3806 }
3807 
3808 /*!
3809   SUMA_ibinSearch( nodeList, target, seg);
3810 
3811   This function performs a binary search.  See SUMA_binSearch for comments
3812   \param nodeList (float *) vector of sorted values
3813   \param target (float) value seeking
3814   \param seg (int *) contains begin and end point of segment being searched
3815   \return found (SUMA_Boolean) YUP if all passed correctly and target within segment, NOPE otherwise
3816 
3817 */
3818 
SUMA_ibinSearch(int * indexList,int target,int * seg)3819 SUMA_Boolean SUMA_ibinSearch( int *indexList, int target, int *seg)
3820 {
3821    static char FuncName[]={"SUMA_ibinSearch"};
3822    int mid=0;
3823    int beg = seg[0], end = seg[1];
3824    SUMA_Boolean found=YUP;
3825    SUMA_Boolean LocalHead = NOPE;
3826 
3827    if ( end<beg) {
3828       SUMA_S_Errv("Segment must be passed with seg[0]=%d <= seg[1]=%d.\n",
3829                   seg[0], seg[1]);
3830       return (found = NOPE);
3831    }
3832    if  (indexList[end]<indexList[beg] ) {
3833       SUMA_S_Errv("indexList must be passed sorted and in ascending order.\n"
3834                   "indexList[%d]=%d<indexList[%d]=%d\n",
3835                   end, indexList[end], beg, indexList[beg]);
3836       return (found = NOPE);
3837    }
3838    if ( (indexList[beg]>target) || (indexList[end]<target) ) {
3839       SUMA_LHv("Don't bother, target (%d) does not lie within segment ]%d, %d[\n"
3840                   , target, indexList[beg], indexList[end]);
3841       return (found = NOPE);
3842    }
3843 
3844    if (beg!=end) {
3845       mid =(end-beg)/2 + beg;
3846       /**no exact match, but elements above and below found*/
3847       if (beg+1==end) {
3848          if (indexList[end]==target) {
3849             seg[0] = end;
3850             seg[1] = end;
3851          } else if (indexList[beg]==target) {
3852             seg[0] = beg;
3853             seg[1] = beg;
3854          } else {
3855             return (found = NOPE);
3856          }
3857       }
3858       else if (target==indexList[mid]) {
3859          seg[0] = mid;
3860          seg[1] = mid;
3861       }
3862       /**keep searching*/
3863       else if ( target  < indexList[mid]) {
3864          seg[0] = beg;  seg[1] = mid;
3865          found = SUMA_ibinSearch( indexList, target, seg);
3866       }
3867       else if ( target > indexList[mid]) {
3868          seg[0] = mid;  seg[1] = end;
3869          found = SUMA_ibinSearch( indexList, target, seg);
3870       }
3871    }
3872    /**exact match; beg==end or target==indexList[ indexList[mid] ]*/
3873    else {
3874       seg[0] = mid;
3875       seg[1] = mid;
3876    }
3877 
3878    return(found);
3879 }
3880 
SUMA_ibinFind(int * indexList,int N_node,int target)3881 int SUMA_ibinFind( int *indexList, int N_node, int target) {
3882    static char FuncName[]={"SUMA_ibinFind"};
3883    int seg[2]={0, N_node -1};
3884    if (SUMA_ibinSearch(indexList, target, seg)) return(seg[0]);
3885    else return(-1);
3886 }
3887 
3888 /*!
3889    \brief creates a reordered version of a vector
3890    yr = SUMA_reorder(y, isort, N_isort);
3891 
3892    \param y (int *) vector, if y is NULL what
3893                     you get back in yr is a copy
3894                     of isort.
3895    \param isort (int *) vector containing sorting order
3896    \param N_isort (int ) number of elements in isort
3897    \return yr (int *) reordered version of y where:
3898                      yr[i] = y[isort[i]];
3899 
3900    - you should free yr with SUMA_free(yr) when done with it
3901    - obviously it's your business to ensure that
3902             isort[i] cannot be larger than then number
3903             of elements in y
3904 */
SUMA_reorder(int * y,int * isort,int N_isort)3905 int *SUMA_reorder(int *y, int *isort, int N_isort)
3906 {
3907    static char FuncName[]={"SUMA_reorder"};
3908    int i = 0, *yr = NULL;
3909 
3910    SUMA_ENTRY;
3911 
3912    if (!isort || N_isort <= 0) SUMA_RETURN(yr);
3913 
3914    yr = (int *)SUMA_calloc( N_isort, sizeof(int));
3915    if (!yr) SUMA_RETURN(yr);
3916 
3917    if (!y) for (i=0; i<N_isort; ++i) yr[i] = isort[i];
3918    else for (i=0; i<N_isort; ++i) yr[i] = y[isort[i]];
3919 
3920    SUMA_RETURN(yr);
3921 }
3922 
3923 /* Careful to only free returned pointer after
3924 calling function is done with it. Do not free
3925 individual strings in returned pointer */
SUMA_sreorder(char ** y,int * isort,int N_isort)3926 char **SUMA_sreorder(char **y, int *isort, int N_isort)
3927 {
3928    static char FuncName[]={"SUMA_sreorder"};
3929    int i = 0;
3930    char **yr = NULL;
3931 
3932    SUMA_ENTRY;
3933 
3934    if (!y || !isort || N_isort <= 0) SUMA_RETURN(yr);
3935 
3936    yr = (char  **)SUMA_calloc( N_isort, sizeof(char*));
3937    if (!yr) SUMA_RETURN(yr);
3938 
3939    for (i=0; i<N_isort; ++i) yr[i] = y[isort[i]];
3940 
3941    SUMA_RETURN(yr);
3942 }
3943 
SUMA_breorder(byte * y,int * isort,int N_isort)3944 byte *SUMA_breorder(byte *y, int *isort, int N_isort)
3945 {
3946    static char FuncName[]={"SUMA_breorder"};
3947    int i = 0;
3948    byte *yr = NULL;
3949 
3950    SUMA_ENTRY;
3951 
3952    if (!y || !isort || N_isort <= 0) SUMA_RETURN(yr);
3953 
3954    yr = (byte *)SUMA_calloc( N_isort, sizeof(byte));
3955    if (!yr) SUMA_RETURN(yr);
3956 
3957    for (i=0; i<N_isort; ++i) yr[i] = y[isort[i]];
3958 
3959    SUMA_RETURN(yr);
3960 }
3961 
3962 
SUMA_freorder(float * y,int * isort,int N_isort)3963 float *SUMA_freorder(float *y, int *isort, int N_isort)
3964 {
3965    static char FuncName[]={"SUMA_freorder"};
3966    int i = 0;
3967    float *yr = NULL;
3968 
3969    SUMA_ENTRY;
3970 
3971    if (!y || !isort || N_isort <= 0) SUMA_RETURN(yr);
3972 
3973    yr = (float *)SUMA_calloc( N_isort, sizeof(float));
3974    if (!yr) SUMA_RETURN(yr);
3975 
3976    for (i=0; i<N_isort; ++i) yr[i] = y[isort[i]];
3977 
3978    SUMA_RETURN(yr);
3979 }
3980 
SUMA_freorder_triplets(float * y,int * isort,int N_isort)3981 float *SUMA_freorder_triplets(float *y, int *isort, int N_isort)
3982 {
3983    static char FuncName[]={"SUMA_freorder_triplets"};
3984    int i = 0;
3985    float *yr = NULL;
3986 
3987    SUMA_ENTRY;
3988 
3989    if (!y || !isort || N_isort <= 0) SUMA_RETURN(yr);
3990 
3991    yr = (float *)SUMA_calloc( N_isort*3, sizeof(float));
3992    if (!yr) SUMA_RETURN(yr);
3993 
3994    for (i=0; i<N_isort; ++i) {
3995       yr[3*i] = y[3*isort[i]];
3996       yr[3*i+1] = y[3*isort[i]+1];
3997       yr[3*i+2] = y[3*isort[i]+2];
3998    }
3999 
4000    SUMA_RETURN(yr);
4001 }
4002 
SUMA_dreorder(double * y,int * isort,int N_isort)4003 double *SUMA_dreorder(double *y, int *isort, int N_isort)
4004 {
4005    static char FuncName[]={"SUMA_dreorder"};
4006    int i = 0;
4007    double *yr = NULL;
4008 
4009    SUMA_ENTRY;
4010 
4011    if (!y || !isort || N_isort <= 0) SUMA_RETURN(yr);
4012 
4013    yr = (double *)SUMA_calloc( N_isort, sizeof(double));
4014    if (!yr) SUMA_RETURN(yr);
4015 
4016    for (i=0; i<N_isort; ++i) yr[i] = y[isort[i]];
4017 
4018    SUMA_RETURN(yr);
4019 }
4020 
4021 
SUMA_string_to_RGBA(char * s,float * here,float scl,int * Err)4022 float *SUMA_string_to_RGBA(char *s, float *here, float scl, int *Err)
4023 {
4024    static char FuncName[]={"SUMA_string_to_RGBA"};
4025 
4026    static int icall=0;
4027    static float fv[10][4];
4028    char *sc=NULL, i, err, one, twofif, N;
4029    float all[12];
4030    SUMA_Boolean LocalHead = NOPE;
4031 
4032    SUMA_ENTRY;
4033 
4034    if (!here) {
4035       ++icall; if (icall > 9) icall = 0;
4036       here = (float *)(&fv[icall]);
4037    }
4038    here[0] = here[1] = here[2] =  here[3] = 1.0;
4039    if (Err) *Err=1; /* initialize with problem */
4040 
4041    if (!s) SUMA_RETURN(here);
4042 
4043    sc = SUMA_copy_string(s);
4044 
4045    /* deblank borders */
4046    sc = deblank_name(sc);
4047    if (!strcasecmp(sc,"red") || !strcasecmp(sc,"r")) {
4048       here[0] = 1.0; here[1] = 0.0; here[2] = 0.0; here[3] = 1.0;
4049       if (Err) *Err=0;
4050    } else if (!strcasecmp(sc,"green") || !strcasecmp(sc,"g")) {
4051       here[0] = 0.0; here[1] = 1.0; here[2] = 0.0; here[3] = 1.0;
4052       if (Err) *Err=0;
4053    } else if (!strcasecmp(sc,"blue") || !strcasecmp(sc,"b")) {
4054       here[0] = 0.0; here[1] = 0.0; here[2] = 1.0; here[3] = 1.0;
4055       if (Err) *Err=0;
4056    } else if (!strcasecmp(sc,"yellow") || !strcasecmp(sc,"y")) {
4057       here[0] = 1.0; here[1] = 1.0; here[2] = 0.0; here[3] = 1.0;
4058       if (Err) *Err=0;
4059    } else if (!strcasecmp(sc,"cyan") || !strcasecmp(sc,"c")) {
4060       here[0] = 0.0; here[1] = 1.0; here[2] = 1.0; here[3] = 1.0;
4061       if (Err) *Err=0;
4062    } else if (!strcasecmp(sc,"purple") || !strcasecmp(sc,"p")) {
4063       here[0] = 1.0; here[1] = 0.0; here[2] = 1.0; here[3] = 1.0;
4064       if (Err) *Err=0;
4065    } else if (!strcasecmp(sc,"white") || !strcasecmp(sc,"w")) {
4066       here[0] = 1.0; here[1] = 1.0; here[2] = 1.0; here[3] = 1.0;
4067       if (Err) *Err=0;
4068    } else if (SUMA_CleanNumString(sc,(void *)4)) {
4069       SUMA_LH("Have RGBA");
4070       N=4;
4071       SUMA_StringToNum(sc, (void*)all, N, 1);
4072       one = 0; twofif = 0; err = 0;
4073       if (scl == 0.0f) {
4074          for (i=0; i<N; ++i) {
4075             if (all[i] >= 0.0 && all[i] <= 1.0) {
4076                ++one;
4077             } else if (all[i] >= 0.0 && all[i] <= 255.0) {
4078                ++twofif;
4079             } else {
4080                SUMA_S_Err("Bad col param %d in %s", i, sc);
4081                ++err;
4082             }
4083          }
4084          if (err) {
4085             SUMA_ifree(sc); SUMA_RETURN(here);
4086          }
4087          if (twofif == 0) scl = 1.0;
4088          else scl = 1.0/255.0;
4089       }
4090       for (i=0; i<N; ++i) { here[i] = all[i]*scl; }
4091       if (Err) *Err=0;
4092    } else if (SUMA_CleanNumString(sc,(void *)3)) {
4093       SUMA_LH("Have RGB");
4094       N=3;
4095       SUMA_StringToNum(sc, (void*)all, N, 1);
4096       one = 0; twofif = 0; err = 0;
4097       if (scl == 0.0f) {
4098          for (i=0; i<N; ++i) {
4099             if (all[i] >= 0.0 && all[i] <= 1.0) {
4100                ++one;
4101             } else if (all[i] >= 0.0 && all[i] <= 255.0) {
4102                ++twofif;
4103             } else {
4104                SUMA_S_Err("Bad col param %d in %s", i, sc);
4105                ++err;
4106             }
4107          }
4108          if (err) {
4109             SUMA_ifree(sc); SUMA_RETURN(here);
4110          }
4111          if (twofif == 0) scl = 1.0;
4112          else scl = 1.0/255.0;
4113       }
4114       for (i=0; i<N; ++i) { here[i] = all[i]*scl; }
4115       here[3] = 1.0*scl;
4116       if (Err) *Err=0;
4117    } else if (SUMA_CleanNumString(sc,(void *)1)) {
4118       SUMA_LH("Have  1 color, going gray scale");
4119       N=1;
4120       SUMA_StringToNum(sc, (void*)all, N, 1);
4121       one = 0; twofif = 0; err = 0;
4122       if (scl == 0.0f) {
4123          for (i=0; i<N; ++i) {
4124             if (all[i] >= 0.0 && all[i] <= 1.0) {
4125                ++one;
4126             } else if (all[i] >= 0.0 && all[i] <= 255.0) {
4127                ++twofif;
4128             } else {
4129                SUMA_S_Err("Bad col param %d in %s", i, sc);
4130                ++err;
4131             }
4132          }
4133          if (err) {
4134             SUMA_ifree(sc); SUMA_RETURN(here);
4135          }
4136          if (twofif == 0) scl = 1.0;
4137          else scl = 1.0/255.0;
4138       }
4139       for (i=0; i<4; ++i) { here[i] = all[0]*scl; }
4140       if (Err) *Err=0;
4141    } else if (SUMA_CleanNumString(sc,(void *)0)) {
4142       SUMA_LH("Have no numbers");
4143    }
4144    SUMA_ifree(sc);
4145    SUMA_RETURN(here);
4146 }
4147 
SUMA_floats_to_string(float * rgba,int N,float scl,char * here,int * Err,char * sep,int MVf)4148 char *SUMA_floats_to_string(float *rgba, int N, float scl, char *here, int *Err,
4149                             char *sep, int MVf)
4150 {
4151    static char FuncName[]={"SUMA_floats_to_string"};
4152    static int icall=0;
4153    static char fv[10][64];
4154    char *sc=NULL, i, err, one, twofif;
4155    float all[12];
4156    SUMA_Boolean LocalHead = NOPE;
4157 
4158    SUMA_ENTRY;
4159 
4160    if (!here) {
4161       ++icall; if (icall > 9) icall = 0;
4162       here = (char *)(&fv[icall]);
4163    }
4164    here[0] = '\0';
4165    if (Err) *Err=1; /* initialize with problem */
4166 
4167    if (!rgba) SUMA_RETURN(here);
4168    if (!sep) sep = ",";
4169    if (scl == 0.0) scl = 1.0;
4170 
4171    if (N == 4) {
4172       if (MVf > 0) {
4173          snprintf(here, 63, "%s%s%s%s%s%s%s",
4174                   MV_format_fval2(rgba[0]*scl, MVf), sep,
4175                   MV_format_fval2(rgba[1]*scl, MVf), sep,
4176                   MV_format_fval2(rgba[2]*scl, MVf), sep,
4177                   MV_format_fval2(rgba[3]*scl, MVf));
4178       } else if (MVf == 0) {
4179          snprintf(here, 63, "%f%s%f%s%f%s%f",
4180                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4181                   rgba[2]*scl, sep, rgba[3]*scl);
4182       } else if (MVf == -1) {
4183          snprintf(here, 63, "%.1f%s%.1f%s%.1f%s%.1f",
4184                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4185                   rgba[2]*scl, sep, rgba[3]*scl);
4186       } else if (MVf == -2) {
4187          snprintf(here, 63, "%.2f%s%.2f%s%.2f%s%.2f",
4188                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4189                   rgba[2]*scl, sep, rgba[3]*scl);
4190       } else if (MVf == -3) {
4191          snprintf(here, 63, "%.3f%s%.3f%s%.3f%s%.3f",
4192                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4193                   rgba[2]*scl, sep, rgba[3]*scl);
4194       }
4195    } else if (N == 3) {
4196       if (MVf > 0) {
4197          snprintf(here, 63, "%s%s%s%s%s",
4198                   MV_format_fval2(rgba[0]*scl, MVf), sep,
4199                   MV_format_fval2(rgba[1]*scl, MVf), sep,
4200                   MV_format_fval2(rgba[2]*scl, MVf));
4201       } else if (MVf == 0) {
4202          snprintf(here, 63, "%f%s%f%s%f",
4203                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4204                   rgba[2]*scl);
4205       } else if (MVf == -1) {
4206          snprintf(here, 63, "%.1f%s%.1f%s%.1f",
4207                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4208                   rgba[2]*scl);
4209       } else if (MVf == -2) {
4210          snprintf(here, 63, "%.2f%s%.2f%s%.2f",
4211                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4212                   rgba[2]*scl);
4213       } else if (MVf == -3) {
4214          snprintf(here, 63, "%.3f%s%.3f%s%.3f",
4215                   rgba[0]*scl, sep, rgba[1]*scl, sep,
4216                   rgba[2]*scl);
4217       }
4218    } else if (N == 1) {
4219       if (MVf > 0) {
4220          snprintf(here, 63, "%s",
4221                   MV_format_fval2(rgba[0]*scl, MVf));
4222       } else if (MVf == 0) {
4223          snprintf(here, 63, "%f",
4224                   rgba[0]*scl);
4225       } else if (MVf == -1) {
4226          snprintf(here, 63, "%.1f",
4227                   rgba[0]*scl);
4228       } else if (MVf == -2) {
4229          snprintf(here, 63, "%.2f",
4230                   rgba[0]*scl);
4231       } else if (MVf == -3) {
4232          snprintf(here, 63, "%.3f",
4233                   rgba[0]*scl);
4234       }
4235    }
4236    SUMA_RETURN(here);
4237 }
4238