1 #include "mrilib.h"
2 #include "thd.h"
3 #include "suma_objs.h" /* 21 Apr 2020 */
4 /*------------------------------------------------------------*/
5 
6 static int               einit = 0 ;
7 static THD_string_array *elist = NULL ;
8 
get_elist(void)9 THD_string_array *get_elist(void) {
10    if( !einit ){
11       einit = 1 ;
12       elist = THD_getpathprogs(NULL, 1) ;
13    }
14    return(elist);
15 }
16 
17 /* local prototypes */
18 static int local_clean_zsh_punct(char * str);
19 
20 /*----------------------------------------------------------------------------*/
21 /*! Find an executable in the PATH by its name, if it exists.
22     If not, NULL is returned.  If it exists, a pointer to static storage
23     is returned (i.e., don't free() this pointer!).
24 ------------------------------------------------------------------------------*/
25 
THD_find_executable(char * ename)26 char * THD_find_executable( char *ename )
27 {
28    char *etr , *str ;
29    int ii ;
30 
31 ENTRY("THD_find_executable") ;
32 
33    if( !einit ){ einit = 1 ; elist = THD_getpathprogs(NULL, 1) ; }
34    if( elist == NULL ) RETURN(NULL) ;
35 
36    etr = THD_trailname( ename , 0 ) ;
37 
38    for( ii=0 ; ii < elist->num ; ii++ ){
39       str = THD_trailname( elist->ar[ii] , 0 ) ;
40       if( strcmp(str,etr) == 0 ) RETURN(elist->ar[ii]) ;
41    }
42 
43    RETURN(NULL) ;
44 }
45 
46 /*----------------------------------------------------------------------------*/
47 /*! Find afni's bin directory if it exists.
48     If not, NULL is returned.  If it exists, a pointer to the path is returned.
49     Do free it with free()
50 ------------------------------------------------------------------------------*/
THD_abindir(byte withslash)51 char * THD_abindir (byte withslash)
52 {
53    char *afr = NULL, *af=NULL;
54    int  nn = 0, N_afni=strlen("afni");
55    THD_string_array *elist=NULL;
56 
57    if (!(elist = get_elist()) ||
58        !(af = THD_find_executable("afni"))) {
59       ERROR_message("Could not find afni, we're doomed daddy!");
60       RETURN(NULL);
61    }
62 
63    /* remove afni from the end to get the path */
64    nn = strlen(af);
65    if (strcmp(af+nn-N_afni,"afni")) {
66       ERROR_message("This should not be (%s)!", af+nn-N_afni);
67       RETURN(NULL);
68    }
69 
70    afr = strdup(af);
71    afr[strlen(af)-N_afni]='\0';
72 
73    /* remove slash */
74    while ( (nn=strlen(afr)-1) && afr[nn] == '/')
75       afr[nn] = '\0';
76 
77    if (withslash) {
78       nn=strlen(afr);
79       afr[nn] = '/'; afr[nn+1]='\0';
80    }
81    return(afr);
82 }
83 
find_readme_file(char * str)84 char *find_readme_file(char *str)
85 {
86    char **ws=NULL, *sout=NULL;
87    int N_ws=0, i;
88 
89    ENTRY("find_readme_file");
90    if (!(ws = approx_str_sort_readmes(str, &N_ws))) {
91       ERROR_message("Could not find README files.\n"
92                      "They should have been in directory %s on your machine\n",
93                      THD_abindir(0));
94       RETURN(NULL);
95    }
96 
97    if (strcasestr(ws[0],str)) sout = strdup(ws[0]);
98    for (i=0; i<N_ws; ++i) if (ws[i]) free(ws[i]);
99    free(ws);
100    RETURN(sout);
101 }
102 
THD_facedir(byte withslash)103 char * THD_facedir(byte withslash)
104 {
105    char *ss=NULL, *so=NULL;
106 
107    if (!(ss = THD_abindir(1))) return(NULL);
108    so = (char *)calloc(strlen(ss)+50, sizeof(char));
109    strcat(so,ss);
110    strcat(so,"funstuff/");
111    free(ss); ss = NULL;
112    if( !THD_is_directory(so) ) {
113       free(so); free(ss); return(NULL);
114    }
115    if (!withslash) so[strlen(so)-1]='\0';
116    return(so);
117 }
118 
119 /*----------------------------------------------------------------------------*/
120 /*! Find a regular file in the PATH by its name, if it exists.
121     Does not include directories.
122     If not, NULL is returned.
123     If it exists, a pointer to malloc-ed storage is returned
124     (e.g., free it when you are done).
125 
126     thispath is a user supplied ':' delimited path string of the form
127       somewhere/here:/over/there . If null then path is taken from
128       the env PATH
129 ------------------------------------------------------------------------------*/
130 
THD_find_regular_file(char * ename,char * thispath)131 char * THD_find_regular_file( char *ename, char *thispath )
132 {
133    char *fullname , *str ;
134    int id , ii ;
135    char *epath;
136 ENTRY("THD_find_regular_file") ;
137 
138    if (!thispath) epath = my_getenv( "PATH" ) ;
139    else epath = thispath;
140 
141    if( epath != NULL ){
142       int epos =0 , ll = strlen(epath) ;
143       char *elocal ;
144       char dirname[THD_MAX_NAME] ;
145 
146       /* copy path list into local memory */
147 
148       elocal = (char *) malloc( sizeof(char) * (ll+2) ) ;
149       strcpy( elocal , epath ) ; elocal[ll] = ' ' ; elocal[ll+1] = '\0' ;
150       fullname = (char *) malloc( sizeof(char) * THD_MAX_NAME);
151 
152       /* replace colons with blanks */
153       for( ii=0 ; ii < ll ; ii++ )
154          if( elocal[ii] == ':' ) elocal[ii] = ' ' ;
155 
156       /* extract blank delimited strings,
157          use as directory names to get timeseries files */
158 
159       do{
160          ii = sscanf( elocal+epos , "%s%n" , dirname , &id ) ;
161          if( ii < 1 ) break ;  /* no read ==> end of work */
162          epos += id ;          /* epos = char after last one scanned */
163 
164          ii = strlen(dirname) ;                      /* make sure name has */
165          if( dirname[ii-1] != '/' ){                 /* a trailing '/' on it */
166             dirname[ii]  = '/' ; dirname[ii+1] = '\0' ;
167          }
168          if( !THD_is_directory(dirname) ) continue ;    /* 25 Feb 2002 */
169 
170          sprintf(fullname, "%s%s",dirname,ename);
171          if( THD_is_file(fullname) ) {
172             /* found the file in the current directory */
173             free(elocal) ;
174             RETURN(fullname);
175          }
176 
177       } while( epos < ll ) ;  /* scan until 'epos' is after end of epath */
178 
179       free(elocal) ; free(fullname);
180    }
181 
182    RETURN(NULL) ;
183 }
184 
185 /*
186    Find a file somewhere afniish
187    Do not free returned pointer
188    Empty string means nothing was found
189    if altpath is not NULL, and nimlname does not
190    have an absolute path, altpath is considere before diving into the
191    default locations
192 */
find_afni_file(char * nimlname,int niname,char * altpath)193 char *find_afni_file(char * nimlname, int niname, char *altpath)
194 {
195    static char filestr[5][1024];
196    static int icall = -1;
197    static char *envlist[]={"AFNI_PLUGINPATH",
198                            "AFNI_PLUGIN_PATH",
199                            "AFNI_TTAPATH",
200                            "AFNI_TTATLAS_DATASET", NULL };
201    char namebuf[1024];
202    char *fstr, *epath, *abpath=NULL;
203    int kk = 0;
204 
205    ENTRY("find_afni_file");
206 
207    ++icall; if (icall > 4) icall = 0;
208    filestr[icall][0]='\0';
209    namebuf[0] = '\0';
210 
211    if(wami_verb() > 1)
212       INFO_message("trying to open %s \n",nimlname);
213    snprintf(namebuf, 1000*sizeof(char),
214              "%s", nimlname);
215    if (THD_is_file(namebuf)) goto GOTIT;
216 
217    if(wami_verb() > 1)
218       INFO_message("%s not found, trying different paths, if no path is set.\n"
219                      ,nimlname);
220 
221    if (nimlname[0] == '/') { /* not found and have abs path, get out */
222       RETURN(filestr[icall]);
223    }
224 
225    if (altpath) {
226       fstr = THD_find_regular_file(nimlname, altpath);
227       snprintf(namebuf, 1000*sizeof(char), "%s", fstr);
228       if (THD_is_file(namebuf)) goto GOTIT;
229    }
230 
231    /* okay that didn't work, try the AFNI plugin directory */
232    kk = 0;
233    while (envlist[kk]) {
234       namebuf[0]='\0';
235                           epath = getenv(envlist[kk]) ;
236       if( epath == NULL ) epath = getenv(envlist[kk]) ;
237       if( epath != NULL ) {
238          if(wami_verb() > 1)
239             INFO_message("trying to open %s in %s directory %s\n",
240                  nimlname, envlist[kk], epath);
241          fstr = THD_find_regular_file(nimlname, epath);
242          if(fstr) {
243             if(wami_verb() > 1)
244                INFO_message("found %s in %s", nimlname, fstr);
245             snprintf(namebuf, 1000*sizeof(char), "%s", fstr);
246             if (THD_is_file(namebuf)) goto GOTIT;
247             if(wami_verb() > 1)
248                INFO_message("failed to open %s as %s\n",
249                             nimlname, namebuf);
250          }
251       }
252       ++kk;
253    }
254 
255    /* Look in AFNI data directory */
256    namebuf[0]='\0';
257    epath = THD_datadir(1);
258    if( epath[0] == '\0' ) RETURN(filestr[icall]) ;  /* should not happen */
259    if(wami_verb() > 1)
260       INFO_message("trying to open %s in path as regular file\n  %s\n",
261                      nimlname, epath);
262 
263    fstr = THD_find_regular_file(nimlname, epath);
264    if(fstr) {
265       if(wami_verb() > 1)
266          INFO_message("found %s in %s", nimlname, fstr);
267       snprintf(namebuf, 1000*sizeof(char), "%s", fstr);
268       if (THD_is_file(namebuf)) goto GOTIT;
269       if(wami_verb() > 1)
270          INFO_message("failed to open %s as %s\n",
271                       nimlname, namebuf);
272    }
273 
274    /* still can't find it. Maybe it's in the afni path */
275    namebuf[0]='\0';
276    abpath = THD_abindir(1);
277    if( abpath == NULL ) RETURN(filestr[icall]) ;  /* bad-who has no afni?*/
278    if(wami_verb() > 1)
279       INFO_message("trying to open %s in path as regular file\n  %s\n",
280                      nimlname, abpath);
281 
282    fstr = THD_find_regular_file(nimlname, abpath);
283    if(fstr) {
284       if(wami_verb() > 1)
285          INFO_message("found %s in %s", nimlname, fstr);
286       snprintf(namebuf, 1000*sizeof(char), "%s", fstr);
287       if (THD_is_file(namebuf)) goto GOTIT;
288       if(wami_verb() > 1)
289          INFO_message("failed to open %s as %s\n",
290                       nimlname, namebuf);
291    }
292 
293    if (abpath) free(abpath);
294    RETURN(filestr[icall]);
295 
296    GOTIT:
297    if (niname) {
298       snprintf(filestr[icall], 1000*sizeof(char),
299                "file:%s", namebuf);
300    } else {
301       snprintf(filestr[icall], 1000*sizeof(char),
302                "%s", namebuf);
303    }
304 
305    if (abpath) free(abpath);
306    RETURN(filestr[icall]);
307 }
308 
309 /*===========================================================================*/
310 /*! Return a list of all executable files in the PATH and the dlist. */
311 
THD_getpathprogs(THD_string_array * dlist,char exec_flag)312 THD_string_array * THD_getpathprogs( THD_string_array *dlist, char exec_flag )
313 {
314    int id , ii , ndir ;
315    char *epath , *eee ;
316    THD_string_array *elist , *tlist , *qlist ;
317 
318 ENTRY("THD_getpathprogs") ;
319 
320    /*----- sanity check and initialize -----*/
321 
322    epath = my_getenv( "PATH" ) ;
323    ndir  = (dlist != NULL) ? dlist->num : 0 ;
324 
325    if( ndir == 0 && epath == NULL ) RETURN(NULL) ;
326 
327    INIT_SARR(elist) ;
328    INIT_SARR(qlist) ;  /* 04 Feb 2002: list of searched directories */
329 
330    /*----- for each input directory, find all files / executable files -----*/
331 
332    for( id=0 ; id < ndir ; id++ ){
333 
334       tlist = THD_get_all_files( dlist->ar[id], exec_flag ) ;
335       if( tlist == NULL ) continue ;
336 
337       for( ii=0 ; ii < tlist->num ; ii++ )  /* copy names to output array */
338          ADDTO_SARR( elist , tlist->ar[ii] ) ;
339 
340       ADDTO_SARR(qlist,dlist->ar[id]) ;     /* 04 Feb 2002 */
341 
342       DESTROY_SARR(tlist) ;
343    }
344 
345    /*----- also do directories in environment path, if any -----*/
346 
347    if( epath != NULL ){
348       int epos =0 , ll = strlen(epath) ;
349       char *elocal ;
350       char ename[THD_MAX_NAME] ;
351 
352       /* copy path list into local memory */
353 
354       elocal = (char *) malloc( sizeof(char) * (ll+2) ) ;
355       strcpy( elocal , epath ) ; elocal[ll] = ' ' ; elocal[ll+1] = '\0' ;
356 
357       /* replace colons with blanks */
358 
359       for( ii=0 ; ii < ll ; ii++ )
360          if( elocal[ii] == ':' ) elocal[ii] = ' ' ;
361 
362       /* extract blank delimited strings,
363          use as directory names to get timeseries files */
364 
365       do{
366          ii = sscanf( elocal+epos , "%s%n" , ename , &id ) ;
367          if( ii < 1 ) break ;  /* no read ==> end of work */
368          epos += id ;          /* epos = char after last one scanned */
369 
370          ii = strlen(ename) ;                         /* make sure name has */
371          if( ename[ii-1] != '/' ){                    /* a trailing '/' on it */
372             ename[ii]  = '/' ; ename[ii+1] = '\0' ;
373          }
374          if( !THD_is_directory(ename) ) continue ;    /* 25 Feb 2002 */
375 
376          /* 04 Feb 2002: check if we already searched this directory */
377 
378          for( ii=0 ; ii < qlist->num ; ii++ )
379             if( THD_equiv_files(qlist->ar[ii],ename) ) break ;
380          if( ii < qlist->num ) continue ;  /* skip this directory */
381          ADDTO_SARR(qlist,ename) ;
382 
383          /* read this directory */
384          tlist = THD_get_all_files( ename, exec_flag ) ;
385          if( tlist != NULL ){
386             for( ii=0 ; ii < tlist->num ; ii++ )    /* move names to output */
387                ADDTO_SARR( elist , tlist->ar[ii] ) ;
388             DESTROY_SARR(tlist) ;
389          }
390 
391       } while( epos < ll ) ;  /* scan until 'epos' is after end of epath */
392 
393       free(elocal) ;
394    }
395 
396    if( SARR_NUM(elist) == 0 ) DESTROY_SARR(elist) ;
397 
398    DESTROY_SARR(qlist) ;  /* 04 Feb 2002 */
399    RETURN(elist) ;
400 }
401 
402 /*--------------------------------------------------*/
403 /*! Read all regular files or executable filenames from a directory. */
404 
THD_get_all_files(char * dname,char exec_flag)405 THD_string_array * THD_get_all_files( char *dname, char exec_flag )
406 {
407    int ir , ll , ii ;
408    char *fname , *tname ;
409    float *far ;
410    THD_string_array *outar, *alist, *rlist ;
411 
412 ENTRY("THD_get_all_files") ;
413 
414    /*----- sanity check and initialize -----*/
415 
416    if( dname == NULL || strlen(dname) == 0 ) RETURN(NULL) ;
417 
418    /*----- find all regular files -----*/
419 
420 if(PRINT_TRACING){
421 char str[256];sprintf(str,"call THD_get_all_filenames(%s)",dname); STATUS(str);
422 }
423    alist = THD_get_all_filenames( dname ) ;
424 
425    if( alist == NULL ) RETURN(NULL) ;
426 STATUS("call THD_extract_regular_files") ;
427    rlist = THD_extract_regular_files( alist ) ;
428    DESTROY_SARR( alist ) ;
429    if( rlist == NULL ) RETURN(NULL) ;
430 
431    /* return regular list if not looking for executables */
432    if(!exec_flag) RETURN(rlist);
433 
434    INIT_SARR( outar ) ;
435 
436    /* 04 Feb 2002: don't include .so libraries, etc. */
437 
438    for( ir=0 ; ir < rlist->num ; ir++ ){
439       fname = rlist->ar[ir] ;
440       if( THD_is_executable(fname) &&
441           !strstr(fname,".so")     &&
442           !strstr(fname,".la")       ) {
443           ADDTO_SARR(outar,fname) ;
444       }
445    }
446 
447    DESTROY_SARR(rlist) ;
448 
449    if( SARR_NUM(outar) == 0 ) DESTROY_SARR(outar) ;
450 
451    RETURN( outar );
452 }
453 
454 /*! Get all executables in directory where afni resides */
THD_get_all_afni_executables(void)455 THD_string_array * THD_get_all_afni_executables(void )
456 {
457    THD_string_array *outar=NULL, *elist=NULL;
458    char *af=NULL, *etr=NULL;
459    int N_af, iaf=0, ii=0, smode, *isrt=NULL;
460    char scomm[256]={""};
461 
462    ENTRY("THD_get_all_afni_executables");
463 
464    if (!(elist = get_elist()) ||
465        !(af = THD_abindir(1)) ) {
466       ERROR_message("Could not find afni, we're doomed daddy!");
467       RETURN(outar);
468    }
469 
470    N_af = strlen(af);
471 
472    /* Now get all executables under af */
473    INIT_SARR( outar );
474    for (ii=0, iaf=0; ii<elist->num ; ii++ ){
475       smode = storage_mode_from_filename(elist->ar[ii]);
476       etr = THD_trailname( elist->ar[ii] , 0 ) ;
477       if (
478           !THD_is_directory(elist->ar[ii]) &&
479           !strncmp(af, elist->ar[ii], N_af) &&
480           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".xml") &&
481           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".html") &&
482           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".jpg") &&
483           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".a") &&
484           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".c") &&
485           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".h") &&
486           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".f") &&
487           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".xbm") &&
488           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".tex") &&
489           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".lib") &&
490           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".dylib") &&
491           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".o") &&
492           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".so") &&
493           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".la") &&
494           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".txt") &&
495           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".R") &&
496           !(STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".py") &&
497              /* add a couple of python types to skip  20 Jul 2012 [rickr] */
498              (!strncmp(etr,"lib_",4) || !strncmp(etr,"gui_",4) ||
499               !strncmp(etr,"ui_",3)) ) &&
500           (smode <= STORAGE_UNDEFINED || smode >= LAST_STORAGE_MODE)  &&
501           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".sumarc") &&
502           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".afnirc")&&
503           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], "lib.py") &&
504           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".pyc") &&
505           !STRING_HAS_SUFFIX_CASE(elist->ar[ii], ".Xdefaults") &&
506           /* skip README files, processed as scripts     20 Jul 2012 [rickr]
507              of note: README.atlas_building starts with the line:
508                       README.atlas_building
509              such process recursion is not good for the system... */
510           strncmp(etr, "README.", 7)
511               )  {
512          ADDTO_SARR( outar , elist->ar[ii] ) ; ++iaf;
513          /* fprintf(stderr," %d- %s\n", iaf, etr); */
514       } else {
515          /* fprintf(stderr," skip %s (%s) %d--%d--%d isd %d\n",
516                elist->ar[ii], af, STORAGE_UNDEFINED, smode, LAST_STORAGE_MODE,
517                THD_is_directory(elist->ar[ii])); */
518       }
519    }
520 
521    qsort(outar->ar, outar->num, sizeof(char*),
522       (int(*) (const void *, const void *))compare_string);
523 
524    if( SARR_NUM(outar) == 0 ) DESTROY_SARR(outar) ;
525 
526    if (af) free(af); af = NULL;
527 
528    RETURN( outar );
529 }
530 
531 /*! Get all readme files in directory where afni resides */
THD_get_all_afni_readmes(void)532 THD_string_array * THD_get_all_afni_readmes(void )
533 {
534    THD_string_array *outar=NULL, *elist=NULL;
535    char *af=NULL, *etr=NULL, *key="README.";
536    int N_af, N_afni=strlen("afni"), iaf=0, ii=0, *isrt=NULL, N_key=0;
537    char scomm[256]={""};
538 
539    ENTRY("THD_get_all_afni_readmes");
540 
541    if (!(elist = get_elist()) ||
542        !(af = THD_abindir(1))) {
543       ERROR_message("Could not find afni, we're doomed daddy!");
544       RETURN(outar);
545    }
546 
547    /* remove afni from the end to get the path */
548    N_af = strlen(af);
549 
550    elist = THD_get_all_files(af,'\0');
551 
552    /* Now get all readmes under af */
553    N_key = strlen(key);
554    INIT_SARR( outar );
555    for (ii=0, iaf=0; ii<elist->num ; ii++ ){
556       etr = THD_trailname( elist->ar[ii] , 0 ) ;
557       if (!THD_is_directory(elist->ar[ii]) &&
558           !strncmp(af, elist->ar[ii], N_af)  &&
559           !strncmp(key, etr, N_key)
560               )  {
561          ADDTO_SARR( outar , elist->ar[ii] ) ; ++iaf;
562          /* fprintf(stderr," %d- %s (%s)\n", iaf, elist->ar[ii], etr); */
563       } else {
564          /* fprintf(stderr," skip %s (%s)\n", elist->ar[ii], af); */
565       }
566    }
567 
568    qsort(outar->ar, outar->num, sizeof(char*),
569       (int(*) (const void *, const void *))compare_string);
570 
571    if( SARR_NUM(outar) == 0 ) DESTROY_SARR(outar) ;
572    if (af) free(af); af = NULL;
573    RETURN( outar );
574 }
575 
576 /*! get all 3D datasets in directory where afni resides */
THD_get_all_afni_dsets(void)577 THD_string_array * THD_get_all_afni_dsets(void )
578 {
579    THD_string_array *outar=NULL, *elist=NULL;
580    char *af=NULL, *etr=NULL;
581    int N_af, N_afni=strlen("afni"), iaf=0, ii=0, smode, *isrt=NULL;
582    char scomm[256]={""};
583 
584    ENTRY("THD_get_all_afni_dsets");
585 
586    if (!(elist = get_elist()) ||
587        !(af = THD_abindir(1))) {
588       ERROR_message("Could not find afni, we're doomed daddy!");
589       RETURN(outar);
590    }
591 
592    N_af = strlen(af);
593 
594    elist = THD_get_all_files(af,'\0');
595 
596    /* Now get all dsets under af */
597    INIT_SARR( outar );
598    for (ii=0, iaf=0; ii<elist->num ; ii++ ){
599       smode = storage_mode_from_filename(elist->ar[ii]);
600       etr = THD_trailname( elist->ar[ii] , 0 ) ;
601       if (
602           !THD_is_directory(elist->ar[ii]) &&
603           !strncmp(af, elist->ar[ii], N_af) &&
604           (smode > STORAGE_UNDEFINED && smode <= LAST_STORAGE_MODE) &&
605           (smode != STORAGE_BY_BRICK ||  /* don't want the .BRICK, just .HEAD */
606                STRING_HAS_SUFFIX(elist->ar[ii], ".HEAD")) &&
607           (smode != STORAGE_BY_NIFTI ||        /* don't want the .img */
608                !STRING_HAS_SUFFIX(elist->ar[ii], ".img")) &&
609           strcmp(etr,"AFNI_atlas_spaces.niml")
610               )  {
611          ADDTO_SARR( outar , elist->ar[ii] ) ; ++iaf;
612          /*fprintf(stderr," %d- %s smode %d[%d]%d\n", iaf, etr,
613                         STORAGE_UNDEFINED, smode, LAST_STORAGE_MODE); */
614       } else {
615          /*fprintf(stderr," skip %s (%s) smode %d[%d]%d\n",
616             elist->ar[ii], af, STORAGE_UNDEFINED, smode, LAST_STORAGE_MODE); */
617       }
618    }
619 
620    qsort(outar->ar, outar->num, sizeof(char*),
621       (int(*) (const void *, const void *))compare_string);
622 
623    if( SARR_NUM(outar) == 0 ) DESTROY_SARR(outar) ;
624    if (af) free(af); af = NULL;
625    RETURN( outar );
626 }
627 
list_afni_files(int type,int withpath,int withnum)628 int list_afni_files(int type, int withpath, int withnum)
629 {
630    int nprogs=0, ii=0;
631    char *etr=NULL, s[12];
632    THD_string_array *progs=NULL;
633 
634    switch (type) {
635       case 0:
636          if (!(progs = THD_get_all_afni_executables())) {
637             ERROR_message(
638                "Cannot get list of programs from your afni bin directory %s",
639                THD_abindir(1));
640             RETURN(0);
641          }
642          break;
643       case 1:
644          if (!(progs = THD_get_all_afni_readmes())) {
645             ERROR_message(
646                "Cannot get list of readmes from your afni bin directory %s",
647                THD_abindir(1));
648             RETURN(0);
649          }
650          break;
651       case 2:
652          if (!(progs = THD_get_all_afni_dsets())) {
653             ERROR_message(
654                "Cannot get list of dsets from your afni bin directory %s",
655                THD_abindir(1));
656             RETURN(0);
657          }
658          break;
659       default:
660          ERROR_message("Whatchyoutalkinboutwillis?");
661          RETURN(0);
662          break;
663    }
664 
665    for (ii=0; ii<progs->num ; ii++ ){
666       if (withpath) etr = progs->ar[ii];
667       else etr = THD_trailname( progs->ar[ii] , 0 ) ;
668       if (withnum) {
669          sprintf(s,"%d", ii);
670          fprintf(stdout,"  %3s.   %s\n", s, etr);
671       } else {
672          fprintf(stdout,"%s\n", etr);
673       }
674    }
675    nprogs = progs->num;
676 
677    DESTROY_SARR(progs);
678 
679    return(nprogs);
680 }
681 
list_afni_programs(int withpath,int withnum)682 int list_afni_programs(int withpath, int withnum) {
683    return(list_afni_files(0, withpath, withnum));
684 }
685 
list_afni_readmes(int withpath,int withnum)686 int list_afni_readmes(int withpath, int withnum) {
687    return(list_afni_files(1, withpath, withnum));
688 }
689 
list_afni_dsets(int withpath,int withnum)690 int list_afni_dsets(int withpath, int withnum) {
691    return(list_afni_files(2, withpath, withnum));
692 }
693 
694 /* Include file that has C struct containing programs and all their options */
695 typedef struct {
696   char *program;
697   char *options;
698   int N_options;
699 } PROG_OPTS;
700 
701 #include "prog_opts.c"
702 
form_C_progopt_string_from_struct(PROG_OPTS po)703 char *form_C_progopt_string_from_struct(PROG_OPTS po)
704 {
705    char *sout=NULL, sbuf[128];
706    int maxch=0, i, jj, N_opts=0;
707 
708    if (!po.program) return(NULL);
709 
710    maxch = strlen(po.program)+strlen(po.options)+100;
711    if (!(sout = (char *)calloc((maxch+1), sizeof(char)))) {
712       ERROR_message("Failed to allocate for %d chars!", maxch+1);
713       return(NULL);
714    }
715 
716    sout[0]='\0';
717    strncat(sout,"{ \"", maxch-1);
718    strncat(sout,po.program, maxch-strlen(sout)-1);
719    strncat(sout,"\", \"", maxch-strlen(sout)-1);
720    strncat(sout,po.options, maxch-strlen(sout)-1);
721    sprintf(sbuf,"\", %d", N_opts); strncat(sout,sbuf, maxch-strlen(sout)-1);
722 
723    strncat(sout,"}", maxch-strlen(sout)-1);
724    if (strlen(sout)>=maxch-1) {
725       ERROR_message("Truncated complete string possible");
726       free(sout); sout=NULL;
727       return(sout);
728    }
729 
730    return(sout);
731 
732 }
733 
form_C_progopt_string(char * prog,char ** ws,int N_ws)734 char *form_C_progopt_string(char *prog, char **ws, int N_ws)
735 {
736    char *sout=NULL, sbuf[128], *wsptr;
737    int maxch=0, i, jj, N_opts=0;
738    NI_str_array *nisa=NULL;
739 
740    if (!prog || !ws) {
741       return(NULL);
742    }
743 
744    maxch = 256;
745    for (i=0; i<N_ws; ++i) {
746       if (ws[i]) {
747          maxch+=strlen(ws[i])+10;
748          if (strlen(ws[i]) > 127) {
749             WARNING_message("Truncating atrocious option %s\n", ws[i]);
750             wsptr = ws[i]+127;
751             *wsptr = '\0';
752          }
753       }
754    }
755    if (!(sout = (char *)calloc((maxch+1), sizeof(char)))) {
756       ERROR_message("Failed to allocate for %d chars!", maxch+1);
757       return(NULL);
758    }
759    sout[0]='\0';
760    strncat(sout,"{ \"", maxch-1);
761    strncat(sout,prog, maxch-strlen(sout)-1);
762    strncat(sout,"\", \"", maxch-strlen(sout)-1);
763 
764    N_opts = 0;
765    for (i=0; i<N_ws; ++i) {
766       if (ws[i] && (nisa = NI_strict_decode_string_list(ws[i] ,"/"))) {
767          for (jj=0; jj<nisa->num; ++jj) {
768             if (ws[i][0]=='-' && nisa->str[jj][0] != '-') {
769                snprintf(sbuf,127,"-%s; ", nisa->str[jj]);
770             } else {
771                snprintf(sbuf,127,"%s; ", nisa->str[jj]);
772             }
773             ++N_opts;
774             strncat(sout,sbuf, maxch-strlen(sout)-1);
775             NI_free(nisa->str[jj]);
776          }
777          if (nisa->str) NI_free(nisa->str);
778          NI_free(nisa); nisa=NULL;
779       }
780    }
781    sprintf(sbuf,"\", %d", N_opts); strncat(sout,sbuf, maxch-strlen(sout)-1);
782 
783 
784    strncat(sout,"}", maxch-strlen(sout)-1);
785    if (strlen(sout)>=maxch-1) {
786       ERROR_message("Truncated complete string possible");
787       free(sout); sout=NULL;
788       return(sout);
789    }
790 
791    return(sout);
792 }
793 
794 /*
795    Generate C array that lists all afni programs and their options
796 
797    There is a most unholy relationship between this function and
798    the include line: #include "prog_opts.c"
799 
800    This function is for internal machinations having to do with
801    automatic generation of help web pages. It is not for mass
802    consumption.
803 
804    \param fout (FILE *): Pointer to output stream
805    \param verb (int): verbosity
806    \param thisprog (char *): If not NULL, then update the list of options for
807                              program thisprog. Existing other programs option
808                              are preserved (appendmode is forced to 1)
809                              If NULL, then do this for all programs recognized
810                              by THD_get_all_afni_executables()
811    \param appendmode (int): 1 --> Keep existing information about programs not
812                                   in  THD_get_all_afni_executables() or other
813                                   than  thisprog
814                             0 --> Just output information on programs from
815                                   THD_get_all_afni_executables(). This is only
816                                   allowed when thisprog == 0
817 */
progopt_C_array(FILE * fout,int verb,char * thisprog,int appendmode)818 int progopt_C_array(FILE *fout, int verb, char *thisprog, int appendmode)
819 {
820    char **ws=NULL, *sout=NULL;
821    float *ws_score=NULL;
822    int N_ws=0, ii = 0, jj = 0, found=0;
823    THD_string_array *progs=NULL;
824 
825    ENTRY("progopt_C_array");
826 
827    if (!fout) fout = stdout;
828 
829    if (thisprog) {
830       if (!appendmode) {
831          WARNING_message("Forcing append mode for one program");
832          appendmode = 1;
833       }
834       INIT_SARR( progs );
835       ADDTO_SARR( progs, thisprog );
836    } else {
837       if (!(progs = THD_get_all_afni_executables()) || progs->num < 1) {
838          ERROR_message("Could not get list of executables");
839          RETURN(1);
840       }
841    }
842 
843    fprintf(fout,
844       "#ifndef PROG_OPTS_INCLUDED\n"
845       "#define PROG_OPTS_INCLUDED\n"
846       "\n"
847       "/* \n"
848       "   ***********    Manual Edits Can Get CLOBBERED!    ***********\n"
849       "   *************** File created automatically ******************\n"
850       "\n"
851       "   This file was initially created by function progopt_C_array(), \n"
852       "   via program apsearch with:\n"
853       "        apsearch -C_all_prog_opt_array > prog_opts.c\n\n"
854       "   To update entry for just one program (PROG) best use:\n"
855       "        apsearch -C_all_prog_opt_array PROG > prog_opts.c\n\n"
856       "\n"
857       "You'll need to also touch thd_getpathprogs.c before rebuilding \n"
858       "libmri.a, etc.\n"
859       "*/\n\n"
860       "#if 0\n"
861       "static PROG_OPTS poptslist[] = {\n"
862       "   {NULL, NULL, 0}\n"
863       "}\n"
864       "#else\n"
865       "static PROG_OPTS poptslist[] = {\n");
866 
867    if (appendmode) { /* Keep programs not in list being sent*/
868       while (poptslist[jj].program != NULL) {
869          found = 0;
870          for (ii=0; ii<progs->num && !found; ++ii) {
871             if (!strcmp(THD_trailname(progs->ar[ii],0), poptslist[jj].program)) {
872                found = 1;
873             }
874          }
875          if (!found) { /* add it */
876             if ((sout = form_C_progopt_string_from_struct(poptslist[jj]))){
877                fprintf(fout, "%s,\n", sout);
878                free(sout); sout = NULL;
879             }
880          }
881          ++jj;
882       }
883    }
884 
885    for (ii=0; ii<progs->num; ++ii) {
886       if (verb) fprintf(stderr,"Prog %d/%d: %s ", ii+1, progs->num,
887                      THD_trailname(progs->ar[ii],0) );
888       if ((ws = approx_str_sort_all_popts(progs->ar[ii], 0, &N_ws,
889                    1, &ws_score,
890                    NULL, NULL, 1, 0, '\\'))) {
891          if (verb) fprintf(stderr,"%d opts\t ", N_ws);
892          if ((sout = form_C_progopt_string(
893                         THD_trailname(progs->ar[ii], 0), ws, N_ws))){
894             fprintf(fout, "%s,\n", sout);
895             free(sout); sout = NULL;
896          }
897          for (jj=0; jj<N_ws; ++jj) if (ws[jj]) free(ws[jj]);
898          free(ws); ws = NULL;
899          if (ws_score) free(ws_score); ws_score=NULL;
900       }
901    }
902    fprintf(fout, "   {  NULL, NULL, 0  }\n};\n\n"
903                  "#endif\n\n\n"
904                  "#endif /* For #ifdef PROG_OPTS_INCLUDED */\n");
905 
906    DESTROY_SARR(progs) ;
907 
908    RETURN(0);
909 }
910 
phelp_cmd(char * prog,TFORM targ,char cmd[512],char fout[128],int verb)911 int phelp_cmd(char *prog, TFORM targ, char cmd[512], char fout[128], int verb )
912 {
913    char uid[64];
914    char *hopt;
915 
916    ENTRY("phelp_cmd");
917 
918    if (!prog ) RETURN(0);
919    fout[0] = '\0';
920    cmd[0] = '\0';
921 
922    switch(targ){
923       case WEB:
924       case NO_FORMAT:
925          hopt = "-h_raw";
926          if (!program_supports(prog, hopt, NULL, verb)) hopt = "-HELP";
927          if (!program_supports(prog, hopt, NULL, verb)) hopt = "-help";
928          break;
929       case ASPX:
930       case SPX:
931          hopt = "-h_spx";
932          if (!program_supports(prog, hopt, NULL, verb)) hopt = "-HELP";
933          if (!program_supports(prog, hopt, NULL, verb)) hopt = "-help";
934         break;
935       case TXT:
936          hopt = "-help";
937          break;
938       default:
939          ERROR_message("I hate myself for failing you with %d", targ);
940          RETURN(0);
941    }
942 
943    UNIQ_idcode_fill(uid);
944    sprintf(fout,"/tmp/%s.%s.txt", APSEARCH_TMP_PREF, uid);
945    snprintf(cmd,500*sizeof(char),"\\echo '' 2>&1 | %s %s > %s 2>&1 ",
946             prog, hopt, fout);
947 
948    RETURN(1);
949 }
950 
phelp(char * prog,TFORM targ,int verb)951 char *phelp(char *prog, TFORM targ, int verb)
952 {
953    char cmd[512], tout[128];
954    char *help=NULL;
955 
956    ENTRY("phelp");
957 
958    if (!prog ) RETURN(help);
959 
960    if (!phelp_cmd(prog, targ, cmd, tout, verb)) {
961       ERROR_message("Failed to get help command");
962       RETURN(0);
963    }
964 
965    if (system(cmd)) {
966       if (0) {/* many programs finish help and set status afterwards. Naughty. */
967          ERROR_message("Failed to get help for %s\nCommand: %s\n", prog, cmd);
968          return 0;
969       }
970    }
971 
972    if (!(help = AFNI_suck_file(tout))) {
973       if (verb) ERROR_message("File %s could not be read\n", tout);
974       RETURN(help);
975    }
976 
977    snprintf(cmd,500*sizeof(char),"\\rm -f %s", tout);
978    system(cmd);
979 
980    help = sphelp(prog, &help, targ, verb);
981 
982    RETURN(help);
983 }
984 
sphelp(char * prog,char ** str,TFORM targ,int verb)985 char *sphelp(char *prog, char **str, TFORM targ, int verb)
986 {
987    char cmd[512], tout[128];
988    char *help=NULL;
989 
990    ENTRY("sphelp");
991 
992    if (!prog || !str || !*str) RETURN(help);
993 
994    switch(targ){
995       case WEB:
996       case NO_FORMAT:
997       case SPX:
998       case TXT:
999          /* This might be a repeated call in some instances */
1000          help = SUMA_Sphinx_String_Edit(str, targ, 0);
1001          break;
1002       case ASPX:
1003          if (!(help = sphinxize_prog_shelp(prog, *str, verb))) {
1004             if (verb) ERROR_message("Failed to autosphinxize string.");
1005             RETURN(*str);
1006          }
1007          free(*str); *str = help;
1008          break;
1009       default:
1010          ERROR_message("Sorry no formatting for you with %d", targ);
1011          help = *str;
1012    }
1013    RETURN(help);
1014 }
1015 
1016 /* Check the static list in prog_opts.c to find whether
1017 or not an option exists for a particular program.
1018 Return  1: If program is found and the option exists
1019         0: If program found and option does not exist
1020        -1: If program was not in the list
1021        -2: If the caller needs brains.
1022 */
check_for_opt_in_prog_opts(char * prog,char * opt)1023 int check_for_opt_in_prog_opts(char *prog, char *opt)
1024 {
1025    PROG_OPTS PO;
1026    int i=0;
1027    char sbuf[64]={""}, *found;
1028 
1029    if (!prog || !opt) return(-2);
1030    PO = poptslist[i++];
1031    while (PO.program) {
1032       if (!strcmp(THD_trailname(prog, 0),PO.program)) {
1033          snprintf(sbuf, 64, "%s;", opt);
1034          /* fprintf(stderr,"%s, %s-->%s, %s\n",
1035             prog, sbuf, PO.program, PO.options); */
1036          if ((found=strstr(PO.options,sbuf))) {
1037             return(1);
1038          } else {
1039             return(0);
1040          }
1041       }
1042       PO = poptslist[i++];
1043    }
1044    /* program not found */
1045    return(-1);
1046 }
1047 
1048 /*
1049    Return 1 if program uprog has option option opt
1050           0 otherwsise
1051 
1052    The function first checks if the program has an
1053    entry in array poptslist from file prog_opts.c included above.
1054 
1055    If an entry is found, the decision is based on whether or not
1056    opt is listed for that program. Otherwise, if no entry is found,
1057    the function resorts to running the program with option opt
1058    followed by value oval (if not NULL). If the program returns a status
1059    of 1, OR creates no output in response to the option then the
1060    option is considered non-existent.
1061 
1062    Obviously, this is not a general purpose option checker.
1063    It was written for the purpose of checking whether or not
1064    a program supports the newfangled -h_raw, etc. options.
1065 
1066    If uprog is "ALL", the function check all existing programs
1067    for opt and returns the total number of programs that seem
1068    to support it.
1069 */
program_supports(char * uprog,char * opt,char * oval,int verb)1070 int program_supports(char *uprog, char *opt, char *oval, int verb)
1071 {
1072    char cmd[512], uid[64], tout[128], *prog=NULL;
1073    int sup=0, ii=0, quick=0;
1074    THD_string_array *progs=NULL;
1075 
1076    ENTRY("program_supports");
1077 
1078    if (!uprog || !opt) RETURN(sup);
1079 
1080    if (!strcmp(uprog,"ALL")) {
1081       if (!(progs = THD_get_all_afni_executables()) || progs->num < 1) {
1082          ERROR_message("Could not get list of executables");
1083          RETURN(sup);
1084       }
1085       prog = progs->ar[ii++];
1086    } else {
1087       prog = uprog;
1088    }
1089 
1090    if (!oval) oval = "";
1091    sup = 0;
1092    do {
1093       switch (quick = check_for_opt_in_prog_opts(prog, opt)) {
1094          case 1:
1095             sup += 1;
1096             if (verb) {
1097                fprintf(stderr,"%s -- OK for %s %s (quick)\n",
1098                                  prog, opt, oval);
1099             }
1100             break;
1101          case 0:
1102             sup += 0;
1103             if (verb) {
1104                fprintf(stderr,"%s -- No support for %s %s (quick)\n",
1105                                  prog, opt, oval);
1106             }
1107             break;
1108          case -1:
1109             /* DO NOT attempt to query program itself to find whether or
1110             not it supports an option. For scripts, that ask apsearch
1111             for help when they don't recognize an option, this will
1112             cause an ugly recursion. */
1113             #if 0
1114             UNIQ_idcode_fill(uid);
1115             sprintf(tout,"/tmp/%s.%s.ps.txt", APSEARCH_TMP_PREF, uid);
1116             snprintf(cmd,500*sizeof(char),"\\echo '' 2>&1 | %s %s %s > %s 2>&1 ",
1117                      prog, opt, oval, tout);
1118             if (system(cmd) || !THD_filesize(tout)) {
1119                sup += 0;
1120                if (verb) {
1121                   fprintf(stderr,"%s -- No support for %s %s\n",
1122                                  prog, opt, oval);
1123                }
1124             } else {
1125                sup += 1;
1126                if (verb) {
1127                   fprintf(stderr,"%s -- OK for %s %s\n", prog, opt, oval);
1128                }
1129             }
1130             snprintf(cmd,500*sizeof(char),"\\rm -f %s", tout);
1131             system(cmd);
1132             #else
1133             sup += 0;
1134             if (verb) {
1135                fprintf(stderr,"** No entry for %s in prog_opts.c \n",
1136                                  prog);
1137             }
1138             #endif
1139             break;
1140           case -2:
1141             ERROR_message("Nonesense here?");
1142             break;
1143       }
1144 
1145       if (progs && ii < progs->num) {
1146          prog = progs->ar[ii++];
1147       }else prog = NULL;
1148    } while (prog);
1149 
1150    if (progs) {
1151       DESTROY_SARR(progs) ;
1152    }
1153 
1154    RETURN(sup);
1155 }
1156 
find_popt(char * sh,char * opt,int * nb)1157 char *find_popt(char *sh, char *opt, int *nb)
1158 {
1159    char *loc=NULL, *other=NULL;
1160    int ne = 0;
1161 
1162    ENTRY("find_popt");
1163 
1164    if (!sh || !opt) {
1165       ERROR_message("NULL option or null string");
1166       RETURN(loc);
1167    }
1168 
1169    loc = line_begins_with(sh, opt, nb, "\t :]", "[]<>()", 5);
1170 
1171    if (loc) { /* Check that we do not have more than one */
1172       if ((other = line_begins_with(loc+*nb+1, opt, NULL, "\t :]", "[]<>()", 5))) {
1173          char sbuf[128]={""}, *strt;
1174          snprintf(sbuf,127,
1175                   "*+ WARNING: More than one match for 'opt' %s in \n>>",
1176                       opt);
1177          strt = MAX(other-60,loc+*nb+1);
1178          write_string(strt, sbuf,
1179                      "<<  Returning first hit\n",
1180                      (other-strt)+10,1,stderr);
1181       }
1182    }
1183 
1184    RETURN(loc);
1185 }
1186 
form_complete_command_string(char * prog,char ** ws,int N_ws,int shtp)1187 char *form_complete_command_string(char *prog, char **ws, int N_ws, int shtp) {
1188    char *sout=NULL, sbuf[128], *wsptr;
1189    int maxch=0, i, jj;
1190    int nargs = 0;
1191    NI_str_array *nisa=NULL;
1192 
1193    if (!prog || !ws || shtp < 0) {
1194       return(NULL);
1195    }
1196 
1197    maxch = 256;
1198    nargs = 0;
1199    for (i=0; i<N_ws; ++i) {
1200       if (ws[i]) {
1201          nargs++; /* count valid args, in case there are none */
1202          maxch+=strlen(ws[i])+10;
1203          if (strlen(ws[i]) > 127) {
1204             WARNING_message("Truncating atrocious option %s\n", ws[i]);
1205             wsptr = ws[i]+127;
1206             *wsptr = '\0';
1207          }
1208       }
1209    }
1210 
1211    if (!(sout = (char *)calloc((maxch+1), sizeof(char)))) {
1212       ERROR_message("Failed to allocate for %d chars!", maxch+1);
1213       return(NULL);
1214    }
1215    sout[0]='\0';
1216 
1217    /* if the list is empty, just say so                  */
1218    /*   - zsh might whine if ARGS is an empty list list) */
1219    /*   - do not leave an empty or non-existent file     */
1220    if( nargs == 0 ) {
1221       sprintf(sbuf, "# %s : empty ARGS list\n", prog);
1222       strcpy(sout, sbuf);
1223       return(sout);
1224    }
1225 
1226    switch (shtp) {
1227       default:
1228       case 0: /* csh/tcsh */
1229          strncat(sout,"set ARGS=(",maxch-strlen(sout)-1);
1230          break;
1231       case 1: /* bash */
1232       case 2: /* zsh - same main syntax as bash */
1233          strncat(sout,"ARGS=(",maxch-strlen(sout)-1);
1234          break;
1235    }
1236 
1237    for (i=0; i<N_ws; ++i) {
1238       /* for zsh, remove any bad punctuation characters (=,>,|) */
1239       /* (note that '=' is in -dxyz=1, for example)             */
1240       /* ('|' is a separator in 3dToyProg and 3dproject)        */
1241       if( shtp == 2 )
1242          local_clean_zsh_punct(ws[i]);
1243 
1244       if (ws[i] && (nisa = NI_strict_decode_string_list(ws[i] ,"/"))) {
1245          for (jj=0; jj<nisa->num; ++jj) {
1246             if (ws[i][0]=='-' && nisa->str[jj][0] != '-') {
1247                snprintf(sbuf,127,"'-%s' ", nisa->str[jj]);
1248             } else {
1249                snprintf(sbuf,127,"'%s' ", nisa->str[jj]);
1250             }
1251             strncat(sout,sbuf, maxch-strlen(sout)-1);
1252             NI_free(nisa->str[jj]);
1253          }
1254          if (nisa->str) NI_free(nisa->str);
1255          NI_free(nisa); nisa=NULL;
1256       }
1257    }
1258 
1259    switch (shtp) {
1260       default:
1261       case 0: /* csh/tcsh */
1262          snprintf(sbuf,127,") ; "
1263                "complete %s \"C/-/($ARGS)/\" \"p/*/f:/\" ; ##%s##\n",prog, prog);
1264          break;
1265       case 1: /* bash */
1266          snprintf(sbuf,127,") ; "
1267                "complete -W \"${ARGS[*]}\" -o bashdefault -o default %s ; "
1268                "##%s##\n",prog, prog);
1269          break;
1270       case 2: /* zsh - syntax matches that of bash, mostly */
1271          snprintf(sbuf,127,") ; "
1272                "complete -W \"${ARGS[*]}\" -o bashdefault -o default %s ; "
1273                "##%s##\n",prog, prog);
1274          break;
1275    }
1276    if (strlen(sbuf) >= 127) {
1277       ERROR_message("Too short a buffer for complete command %s\n");
1278       free(sout); sout=NULL;
1279       return(sout);
1280    }
1281    strncat(sout,sbuf, maxch-strlen(sout)-1);
1282    if (strlen(sout)>=maxch-1) {
1283       ERROR_message("Truncated complete string possible");
1284       free(sout); sout=NULL;
1285       return(sout);
1286    }
1287 
1288    return(sout);
1289 }
1290 
prog_complete_command(char * prog,char * ofileu,int shtp)1291 int prog_complete_command (char *prog, char *ofileu, int shtp) {
1292    char **ws=NULL, *sout=NULL, *ofile=NULL;
1293    float *ws_score=NULL;
1294    int N_ws=0, ishtp=0, shtpmax = 0, i;
1295    FILE *fout=NULL;
1296 
1297    if (!prog || !(ws = approx_str_sort_all_popts(prog, 0, &N_ws,
1298                    1, &ws_score,
1299                    NULL, NULL, 1, 0, '\\'))) {
1300       return 0;
1301    }
1302 
1303    /* shell types are:
1304     *    0: tcsh
1305     *    1: bash
1306     *    2: zsh
1307     * set shtpmax to largest+1 (so 3, now) */
1308    if (shtp < 0) { shtp=0; shtpmax = 3;}
1309    else { shtpmax = shtp+1; }
1310 
1311    for (ishtp=shtp; ishtp<shtpmax; ++ishtp) {
1312       if (ofileu) {
1313           if (shtpmax != shtp+1) { /* autoname */
1314             switch (ishtp) {
1315                default:
1316                case 0:
1317                   ofile = strdup(ofileu);
1318                   break;
1319                case 1:
1320                   ofile = (char*)calloc((strlen(ofileu)+20), sizeof(char));
1321                   strcat(ofile, ofileu);
1322                   strcat(ofile, ".bash");
1323                   break;
1324                case 2:
1325                   ofile = (char*)calloc((strlen(ofileu)+20), sizeof(char));
1326                   strcat(ofile, ofileu);
1327                   strcat(ofile, ".zsh");
1328                   break;
1329             }
1330           } else {
1331             ofile = strdup(ofileu);
1332           }
1333 
1334           if (!(fout = fopen(ofile,"w"))) {
1335             ERROR_message("Failed to open %s for writing\n", ofile);
1336             return(0);
1337           }
1338 
1339       } else {
1340          fout = stdout;
1341       }
1342 
1343       if ((sout = form_complete_command_string(prog, ws, N_ws, ishtp))){
1344          fprintf(fout, "%s", sout);
1345          free(sout); sout = NULL;
1346       }
1347       if (ofileu) fclose(fout); fout=NULL;
1348       if (ofile) free(ofile); ofile=NULL;
1349    }
1350 
1351    for (i=0; i<N_ws; ++i) if (ws[i]) free(ws[i]);
1352    free(ws); ws = NULL;
1353    if (ws_score) free(ws_score); ws_score=NULL;
1354    return 0;
1355 }
1356 
1357 
1358 
view_prog_help(char * prog)1359 void view_prog_help(char *prog)
1360 {
1361    char *viewer=NULL, *hname=NULL;
1362    char *progname=NULL;
1363 
1364    if (!prog) return;
1365    if (!(progname = THD_find_executable(prog))) {
1366       ERROR_message("Could not find executable %s.\n",
1367                      prog);
1368       return;
1369    }
1370    if (!(viewer = GetAfniTextEditor())) {
1371       ERROR_message("No GUI editor defined, and guessing game failed.\n"
1372               "Set AFNI_GUI_EDITOR in your .afnirc for this option to work.\n");
1373       return;
1374    }
1375 
1376    hname = get_updated_help_file(0, 0, progname, -1);
1377    if (hname[0]=='\0') { /* failed, no help file ... */
1378       ERROR_message("No help file for %s\n", progname);
1379       return;
1380    }
1381 
1382    if (!(view_text_file(hname))) {
1383       ERROR_message("Failed to view %s\n", hname);
1384    }
1385    return;
1386 }
1387 
web_prog_help_link(char * prog,int style)1388 char *web_prog_help_link(char *prog, int style)
1389 {
1390    char *progname=NULL;
1391    static char weblinka[10][1024]={""}, *weblink;
1392    static int n;
1393    /* point to the new AFNI program help page     16 Mar 2020 [rickr] */
1394    const char * sphinx_help =
1395       "https://afni.nimh.nih.gov/pub/dist/doc/htmldoc/programs";
1396    const char * old_help =
1397       "https://afni.nimh.nih.gov/pub/dist/doc/program_help";
1398 
1399    ++n; if (n>9) n = 0;
1400    weblink = (char *)weblinka[n]; weblink[0]='\0';
1401 
1402    if (!prog) return(weblink);
1403 
1404    if (!strcmp(prog,"ALL")) {
1405       if (style == 0) {
1406          snprintf(weblink,1020*sizeof(char), "%s/%s.html", sphinx_help,
1407                "main_toc");
1408       } else {
1409          /* Nothing yet, return old */
1410          snprintf(weblink,1020*sizeof(char), "%s/%s.html", old_help,
1411                "all-of-them");
1412       }
1413    } else {
1414       if (!(progname = THD_find_executable(prog))) {
1415          ERROR_message("Could not find executable %s.\n",
1416                         prog);
1417          return(weblink);
1418       }
1419 
1420       if (style == 0) {
1421          snprintf(weblink,1020*sizeof(char), "%s/%s_sphx.html", sphinx_help,
1422                THD_trailname(progname,0));
1423       } else {
1424          /* Nothing yet, return old */
1425          snprintf(weblink,1020*sizeof(char), "%s/%s_sphx.html", sphinx_help,
1426                THD_trailname(progname,0));
1427       }
1428    }
1429 
1430    return(weblink);
1431 }
1432 
web_prog_help(char * prog,int style)1433 void web_prog_help(char *prog, int style)
1434 {
1435    char *progname=NULL;
1436    char *weblink;
1437 
1438    if (!prog) return;
1439 
1440    weblink = web_prog_help_link(prog, style);
1441    if (weblink[0] == '\0') return;
1442 
1443    if (!(view_web_link(weblink,NULL))) {
1444       ERROR_message("Failed to web view %s\n", weblink);
1445       return;
1446    }
1447 
1448    return;
1449 }
1450 
web_class_docs(char * prog)1451 void web_class_docs(char *prog)
1452 {
1453    char weblink[1024]={""};
1454 
1455    if (prog) {
1456       ERROR_message("Not ready for prog input %s.\n",
1457                      prog);
1458       return;
1459    } else {
1460       snprintf(weblink,1020*sizeof(char),
1461                "https://afni.nimh.nih.gov/pub/dist/edu/latest");
1462    }
1463 
1464    if (!(view_web_link(weblink,NULL))) {
1465       ERROR_message("Failed to web view %s\n", weblink);
1466       return;
1467    }
1468 
1469    return;
1470 }
1471 
view_web_link(char * link,char * browser)1472 int view_web_link(char *link, char *browser)
1473 {
1474    char cmd[1024];
1475    if (!link) return(0);
1476    if (!browser) browser = GetAfniWebBrowser();
1477 
1478    if (!browser) {
1479       ERROR_message("No Web browse defined.\n"
1480               "Set AFNI_WEB_BROWSER in your .afnirc for this option to work.\n");
1481       return(0);
1482    }
1483 
1484    snprintf(cmd,1020*sizeof(char),"%s %s &", browser, link);
1485    system(cmd);
1486    return(1);
1487 }
1488 
view_text_file(char * progname)1489 int view_text_file(char *progname)
1490 {
1491    char *viewer=NULL, cmd[256];
1492 
1493    if (!progname) {
1494       ERROR_message("No input!");
1495       return(0);
1496    }
1497    if (!THD_is_ondisk(progname)) {
1498       ERROR_message("file %s not on disk.\n", progname);
1499       return(0);
1500    }
1501    if (!(viewer = GetAfniTextEditor())) {
1502       ERROR_message("No GUI editor defined, and guessing game failed.\n"
1503               "Set AFNI_GUI_EDITOR in your .afnirc for this option to work.\n");
1504       return(0);
1505    }
1506    /* open help file in editor*/
1507    snprintf(cmd,250*sizeof(char),"%s %s &", viewer, progname);
1508    system(cmd);
1509    return(1);
1510 }
1511 
1512 /* particularly for the case of zsh, set any of {'=', '>', '|'} to space,
1513  * which should result in them not being part of any option */
local_clean_zsh_punct(char * str)1514 static int local_clean_zsh_punct(char * str)
1515 {
1516    char * cp;
1517 
1518    if( !str ) return 0;
1519 
1520    /* convert bad chars to space; rely on null termination */
1521    for( cp=str; *cp; cp++ )
1522       if( *cp == '=' || *cp == '>' || *cp == '|' )
1523           *cp = ' ';
1524 
1525    return 0;
1526 }
1527