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