1 #include "mrilib.h"
2 #include "niml.h"
3 
4 static int nerr=0 ;
SYM_expand_errcount(void)5 int SYM_expand_errcount(void){ return nerr; }  /* 03 May 2007 */
6 
7 /*----------------------------------------------------------------------------*/
8 
9 #define INIT_OUTBUF                                                         \
10  do{ if( outbuf == NULL ){                                                  \
11        outbuf = THD_zzprintf(outbuf,"***** Scanned GLT messages *****\n") ; \
12        outbuf = THD_zzprintf(outbuf,"++ INFO: -gltsym is: '%s'\n",gltsym) ; \
13  } } while(0)
14 
15 #define NLAST_TEST 999999
16 
17 /*----------------------------------------------------------------------------*/
18 /* This funcion is for testing a gltsym string for basic validity.
19    It returns a string that contains the messages generated during the tests.
20     * varlist = space (or comma or semicolon) delimited list of variable names
21     * gltsym  = expression for GLT
22    If NULL is returned, there were no problems detected.  [01 May 2015]
23 *//*--------------------------------------------------------------------------*/
24 
SYM_test_gltsym(char * varlist,char * gltsym)25 char * SYM_test_gltsym( char *varlist , char *gltsym )
26 {
27    char *outbuf=NULL ; int nbad=0 , nerrtemp=0 ;
28    NI_str_array *vsar=NULL ; int vv=0 ; char *vnam=NULL , *mbuf=NULL ;
29    int nrang=0 ; SYM_irange *rang=NULL ;
30    floatvecvec *fvv=NULL ;
31 
32 ENTRY("SYM_test_gltsym") ;
33 
34    /* check for bad inputs */
35 
36    if( gltsym == NULL || *gltsym == '\0' ){
37      gltsym = "<EMPTY STRING>" ;  /* must have something for INIT_OUTBUF */
38      INIT_OUTBUF ;
39      outbuf = THD_zzprintf(outbuf,"** ERROR: cannot continue\n") ; nbad++ ; nerr++ ;
40      RETURN(outbuf) ;
41    }
42 
43    INIT_OUTBUF ;
44    if( varlist == NULL || *varlist == '\0' ){
45      outbuf = THD_zzprintf(outbuf,"** ERROR: Allowed variable list is empty\n") ; nbad++ ; nerr++ ;
46    } else {
47      outbuf = THD_zzprintf(outbuf,"++ INFO: Allowed variable list is '%s'\n",varlist) ;
48    }
49 
50    /* decode the variable list into individual names */
51 
52    STATUS("scanning varlist") ;
53    vsar = NI_decode_string_list( varlist , ",;" ) ;
54    if( vsar == NULL ){
55      outbuf = THD_zzprintf(outbuf,"** ERROR: Cannot decode variable list names!\n") ; nbad++ ; nerr++ ;
56      RETURN(outbuf) ;
57    }
58 
59    /* make list of variable for parsing, with made up data ranges */
60 
61    STATUS("creating fictional SYM_irange") ;
62    rang = (SYM_irange *)calloc(sizeof(SYM_irange),vsar->num) ;
63    for( nrang=vv=0 ; vv < vsar->num ; vv++ ){
64      vnam = vsar->str[vv] ;
65      if( vnam == NULL || *vnam == '\0' ) continue ; /* bad ==> skip */
66      if( !isalpha(vnam[0]) ){
67        outbuf = THD_zzprintf(outbuf,"** ERROR: Variable name '%s' doesn't start with alphabetic character\n",vnam) ;
68        nbad++ ; nerr++ ;
69      } else if( strlen(vnam) > 63 ){
70        outbuf = THD_zzprintf(outbuf,"** ERROR: Variable name '%s' is too long (> 63)\n",vnam) ;
71        nbad++ ; nerr++ ;
72      } else {
73        rang[nrang].nbot = 0 ;
74        rang[nrang].ntop = NLAST_TEST ;
75        rang[nrang].gbot = 0 ;
76        NI_strncpy(rang[nrang].name,vnam,64) ; nrang++ ;
77      }
78    }
79 
80    NI_delete_str_array(vsar);
81 
82    /* now parse gltsym to see if it works OKAY */
83 
84    nerrtemp = nerr ;
85    SET_message_outbuf(1) ;
86 
87    fvv = SYM_expand_ranges( NLAST_TEST , nrang , rang , gltsym ) ;
88 
89    if( fvv != NULL ){
90      int iv ; floatvec *fv ;
91      STATUS("freeing fvv") ;
92      for( iv=0 ; iv < fvv->nvec ; iv++ ){
93        fv = fvv->fvar + iv ;
94        if( fv->ar != NULL ) free(fv->ar) ;
95      }
96      free(fvv->fvar) ; free(fvv) ;
97    }
98 
99    nbad += nerr - nerrtemp ;
100 
101    mbuf  = GET_message_outbuf() ;
102    if( mbuf != NULL ){
103      size_t ll = strlen(outbuf) + strlen(mbuf) + 32 ;
104      outbuf = (char *)realloc(outbuf,ll) ; strcat(outbuf,mbuf) ;
105    }
106    SET_message_outbuf(0) ;
107 
108    if( nbad == 0 )
109      outbuf = THD_zzprintf(outbuf,"++ INFO: This gltsym appears to be OKAY :-)\n") ;
110    else
111      outbuf = THD_zzprintf(outbuf,"** SORRY: This gltsym appears to be BAD :-(\n") ;
112 
113    RETURN(outbuf) ;
114 }
115 
116 /*----------------------------------------------------------------------------*/
117 /*! Expand a string like "Fred 2*Jed -Ned[1..3]" into a float vector
118     that comprises the weights for each beta element in 3dDeconvolve.
119 
120     Each SYM_irange struct has 4 fields
121       - name = string that names this field
122       - nbot,ntop = range of indexes valid for this name (nbot <= ntop, please)
123       - gbot = global index that maps to nbot
124 
125     The set of structs in rang[] should collectively span global indexes
126     from 0..nlast (inclusive).  The returned floatvec will have nlast+1 entries.
127 ------------------------------------------------------------------------------*/
128 
SYM_expand_ranges(int nlast,int nrang,SYM_irange * rang,char * str)129 floatvecvec * SYM_expand_ranges( int nlast, int nrang, SYM_irange *rang, char *str )
130 {
131    floatvec *fv ;
132    floatvecvec *fvv=NULL ;
133    int rr , ii , ss , gg, *qlist , nvec=0 , iv ;
134    NI_str_array *sar ;
135    char qname[64] , *qstr , *qpt , *qls ;
136    float fac ;
137 
138 ENTRY("SYM_expand_ranges") ;
139 
140    if( nlast < 0 ) RETURN(NULL) ;  /* bad input */
141 
142    /* check if have anything to scan for */
143 
144    if( nrang < 1 || rang == NULL || str == NULL || *str == '\0' ) RETURN(NULL) ;
145 
146    /* check if input line is a comment */
147 
148    for( ii=0 ; str[ii] != '\0' && isspace(str[ii]) ; ii++ ) ;  /*nada*/
149 
150    if( str[ii] == '\0' ||                   /* all blank */
151        str[ii] == '#'  ||                   /* starts with "#" */
152       (str[ii] == '/' && str[ii+1] == '/')  /* starts with "//" */
153    ) RETURN(NULL) ;
154 
155    fv      = (floatvec *)malloc(sizeof(floatvec)) ;    /* create empty output */
156    fv->nar = nlast+1 ;
157    fv->ar  = (float *)calloc(sizeof(float),nlast+1) ;
158 
159    /* break input string into separate chunks */
160 
161    sar = NI_decode_string_list( str , "~" ) ;
162    if( sar == NULL ){
163      fvv  = (floatvecvec *)malloc(sizeof(floatvecvec)) ;
164      fvv->nvec = 1 ;
165      fvv->fvar = fv ;
166      ERROR_message("empty line in -gltsym?") ; nerr++ ;
167      RETURN(fvv) ;
168    }
169 
170    /* scan chunks and merge isolated '+' or '-' with next chunk [01 May 2015] */
171 
172 #if 1
173    for( ss=0 ; ss < sar->num ; ss++ ){
174      qstr = sar->str[ss] ;
175      if( qstr == NULL || *qstr == '\0' ) continue ;          /* bad entry? */
176      if( *qstr == '#' ||                              /* comment ends line */
177         (*qstr == '/' && *(qstr+1) == '/') ) break ;
178 
179      /* process a string of length=1 and is either "+" or "-" */
180 
181      if( strlen(qstr) == 1 && (*qstr == '+' || *qstr == '-') ){
182        if( ss == sar->num-1 ){                     /* at end ==> mark to ignore this */
183          ERROR_message("-gltsym: isolated '%s' at end of -gltsym? IGNORING!",qstr) ;
184          qstr[0] = '\0' ; nerr++ ;
185        } else if ( *qstr == '+' ){                 /* can just ignore this "+"! */
186          INFO_message("INFO: -gltsym: isolated '+' is being ignored") ;
187          qstr[0] = '\0' ;
188        } else {                                    /* this is a "-" */
189          qpt = sar->str[ss+1] ;                    /* the next string in the list */
190          if( qpt == NULL || *qpt == '\0' ){        /* is it bad?? should never happen */
191            ERROR_message("-gltsym: isolated '%s' is followed by NULL entry in? IGNORING!",qstr) ;
192            qstr[0] = '\0' ; nerr++ ;
193          } else if( *qpt == '+' || *qpt == '-' ){  /* next string should not already be signed! */
194            ERROR_message("-gltsym: isolated '-' is followed by '%s' which is already signed? IGNORING!",qpt) ;
195            qstr[0] = '\0' ; nerr++ ;
196          } else {                                  /* everything is cool: attach sign to next string */
197            /* alloc with NIML and do not lose '-'   25 Jul 2016 [rickr] */
198            qls = (char *)NI_malloc(char, sizeof(char)*(strlen(qpt)+4)) ;
199            strcpy(qls,qstr) ; strcat(qls,qpt) ;
200            INFO_message("INFO: -gltsym: isolated '-' is merged with following '%s'",qpt) ;
201            NI_free(qpt) ; sar->str[ss+1] = qls ; qstr[0] = '\0' ;
202          }
203        }
204      }
205    }
206 #endif
207 
208    /* process each chunk */
209 
210    for( ss=0 ; ss < sar->num ; ss++ ){
211      qstr = sar->str[ss] ;
212      if( qstr == NULL || *qstr == '\0' ) continue ;          /* bad entry? */
213      if( *qstr == '#' ||                              /* comment ends line */
214         (*qstr == '/' && *(qstr+1) == '/') ) break ;
215 
216      qstr  = strdup(sar->str[ss]) ;               /* duplicate for surgery */
217      qls   = strchr(qstr,'[') ;      /* find and decode "[...]" subscripts */
218      qlist = NULL ;                        /* if they are present, that is */
219      if( qls != NULL ){
220        *qls  = '\0' ;                  /* cut string off at '[' subscripts */
221        qls++ ;                      /* will scan for intlist starting here */
222      }
223 
224      qpt = strchr(qstr,'*') ;           /* find and decode factor in front */
225      if( qpt != NULL ){                       /* if it is present, that is */
226        char *ept ;
227        fac = (float)strtod(qstr,&ept) ;
228        if( fac == 0.0f && ept == qstr ){     /* bad factor interpretation? */
229          fac = 1.0f ;            /* ==> replace with 1, and bitch about it */
230          WARNING_message(
231            "-gltsym: Can't interpret '*' scale factor in '%s' -- replaced by 1",
232            qstr) ;
233        }
234        if( ept != qpt )  /* 27 May 2008 */
235          WARNING_message(
236            "-gltsym: '*' scale factor in '%s' not at start of string?",qstr ) ;
237        qpt++ ;
238      } else if( *qstr == '+' ){                  /* "+" is same as "+1.0*" */
239        qpt = qstr+1 ; fac =  1.0 ;
240      } else if( *qstr == '-' ){                  /* "-" is same as "-1.0*" */
241        qpt = qstr+1 ; fac = -1.0 ;
242      } else {                                            /* default is "+" */
243        qpt = qstr   ; fac =  1.0 ;
244      }
245 
246      for( rr=0 ; rr < nrang ; rr++ )                 /* match name in list */
247        if( strcmp(qpt,rang[rr].name) == 0 ) break ;
248      if( rr == nrang ){                                      /* no match!? */
249        ERROR_message("-gltsym: can't match symbolic name '%s'\n",qpt) ;
250        nerr++ ; free((void *)qstr) ; continue ;
251      }
252                                        /* now scan for intlist, if present */
253      if( qls != NULL ){
254        MCW_intlist_allow_negative( (rang[rr].nbot < 0) ) ;
255        qlist = MCW_get_intlist( rang[rr].ntop+1 , qls ) ;
256 
257        if( qlist != NULL && *qls == '[' ){  /** [[...]] type of subscript **/
258          if( nvec == 0 ){
259            nvec = qlist[0] ;
260            fvv  = (floatvecvec *)malloc(sizeof(floatvecvec)) ;
261            fvv->nvec = nvec ;
262            fvv->fvar = (floatvec *)calloc(sizeof(floatvec),nvec) ;
263            for( iv=0 ; iv < nvec ; iv++ ){
264              fvv->fvar[iv].nar = nlast+1 ;
265              fvv->fvar[iv].ar  = (float *)calloc(sizeof(float),nlast+1) ;
266            }
267          } else if( qlist[0] != nvec ){
268            ERROR_message("mismatch in use of -gltsym [[...]]: '%s'\n",
269                    sar->str[ss] ) ;
270            nerr++ ;free((void *)qlist) ; free((void *)qstr) ;
271            continue ;
272          }
273          for( iv=0 ; iv < nvec ; iv++ ){
274            gg = qlist[iv+1] - rang[rr].nbot + rang[rr].gbot ;
275            if( gg >= 0 && gg <= nlast ) fvv->fvar[iv].ar[gg] = fac ;
276          }
277          free((void *)qlist) ; free((void *)qstr) ;
278          continue ;          /** skip to next one, since this was special **/
279        }
280      }
281                                          /* make up a fake list, if needed */
282      if( qlist == NULL ){
283        qlist = (int *)malloc(sizeof(int)*(rang[rr].ntop-rang[rr].nbot+2)) ;
284        qlist[0] = rang[rr].ntop-rang[rr].nbot+1 ;
285        for( ii=0 ; ii < qlist[0] ; ii++ ) qlist[ii+1] = rang[rr].nbot+ii ;
286      }
287                                          /* insert values into output list */
288 
289      for( ii=0 ; ii < qlist[0] ; ii++ ){
290        if( qlist[ii+1] < rang[rr].nbot || qlist[ii+1] > rang[rr].ntop ){
291          ERROR_message("-gltsym subscript %s[%d] out of range %d..%d\n",
292                  rang[rr].name , qlist[ii+1] , rang[rr].nbot,rang[rr].ntop ) ;
293          nerr++ ; continue ;
294        }
295        gg = qlist[ii+1] - rang[rr].nbot + rang[rr].gbot ;
296        if( gg >= 0 && gg <= nlast ) fv->ar[gg] = fac ;
297      }
298 
299      free((void *)qlist) ; free((void *)qstr) ;
300    }
301    MCW_intlist_allow_negative(0) ;
302 
303    NI_delete_str_array(sar);
304 
305    /* if had no [[...]] subscripts, only have 1 vector for output */
306 
307    if( nvec == 0 ){
308      fvv  = (floatvecvec *)malloc(sizeof(floatvecvec)) ;
309      fvv->nvec = 1 ;
310      fvv->fvar = fv ;
311    } else {              /* have multiple outputs */
312      for( iv=0 ; iv < nvec ; iv++ ){
313        for( gg=0 ; gg <= nlast ; gg++ ){
314         if( fvv->fvar[iv].ar[gg] == 0.0f ) fvv->fvar[iv].ar[gg] = fv->ar[gg] ;
315        }
316      }
317      KILL_floatvec(fv) ;
318    }
319 
320    RETURN(fvv) ;
321 }
322