1 #include "mrilib.h"
2 
3 static int tcat_open_verb = 0;
4 
5 static NI_str_array * NI_get_wildcard_list(char * pattern);
6 static char * update_sar_with_selectors(NI_str_array * sar, char * sel);
7 
8 /*---------------------------------------------------------------------------*/
9 /*! Open a dataset that is an impromptu catenation of multiple dataset.      */
10 /*---------------------------------------------------------------------------*/
11 
THD_open_tcat(char * dlist)12 THD_3dim_dataset * THD_open_tcat( char *dlist )
13 {
14    THD_3dim_dataset *dset_out , **dset_in ;
15    int ndset_in , dd , nerr , new_nvals, sb=0 , ivout;
16    NI_str_array *sar ;
17    double angle=0.0;
18    char *dp, *dlocal = dlist;   /* local dlist, in case it is altered */
19    char *sel=NULL;
20 
21 ENTRY("THD_open_tcat") ;
22 
23    if( dlocal == NULL || *dlocal == '\0' ) RETURN(NULL) ;
24 
25    /* allow file list to be read from a file   23 Jul 2012 [rickr] */
26    if( ! strncmp(dlocal, "filelist:", 9) ) {
27       dlocal = AFNI_suck_file(dlocal+9) ;
28       if ( ! dlocal ) {
29          ERROR_message("THD_open_tcat: failed to open '%s' as filelist",
30                        dlocal+9);
31          RETURN(NULL) ;
32       }
33       /* make it look more like expected */
34       for( dd=0, dp=dlocal; dd < strlen(dlocal); dd++, dp++ )
35          if( *dp == '\n' || *dp  == '\r' ) *dp = ' ';
36    }
37 
38    if( strchr(dlocal,' ') == NULL && ! HAS_WILDCARD(dlocal) ) {
39      dset_out = THD_open_dataset(dlocal) ; RETURN(dset_out) ;
40    }
41 
42    /* save selectors, akin to wildcard selection         4 Apr 2016 [rickr] */
43    /* (use by default; consider future vals: FOR_COMPOSITE and FOR_INDIVID) */
44    tcat_open_verb = AFNI_numenv_def("AFNI_TCAT_SELECTORS_VERB", 0);
45    if( tcat_open_verb > 1 )
46       INFO_message("THD_open_tcat: processing wildcards in '%s'\n", dlocal);
47    if( ! AFNI_noenv("AFNI_TCAT_SELECTORS") ) {
48       for( dd=0; dd < strlen(dlocal); dd++ ) {
49          if( dlocal[dd] == '[' || dlocal[dd] == '<' || dlocal[dd] == '{' ) {
50             char * cp = strchr(dlocal+dd, ' ');
51             int    dcp;
52             if( cp ) {
53                /* whitespace follows selector...
54                   If something besides white space follows, then this is
55                   not considered to be a general selector.
56                */
57                for( dcp=1; cp[dcp] && isspace(cp[dcp]); dcp++ )
58                   ;
59 
60                /* if non-nul do not use global tcat selector. */
61                if( cp[dcp] ) {
62                  if( tcat_open_verb )
63                    INFO_message("THD_open_tcat: skip general tcat selector\n");
64                  break;
65                }
66             }
67 
68             /* we have selectors! */
69             sel = strdup(dlocal+dd);
70 
71             /* altering string, so be sure it is a local, modifiable one */
72             if( dlocal == dlist ) dlocal = strdup(dlocal);
73             dlocal[dd] = '\0'; /* terminate */
74 
75             if( tcat_open_verb )
76                INFO_message("THD_open_tcat, have tcat selector %s\n", sel);
77 
78             break;
79          }
80       }
81    }
82 
83    /* check for failure here       14 Jul 2016 [rickr] */
84    if( strchr(dlocal,' ') ) {
85       sar = NI_decode_string_list( dlocal , "~" ) ;
86 
87       if( ! sar ) {
88          if( tcat_open_verb )
89             WARNING_message("THD_open_tcat: no space list from '%s'", dlocal);
90          RETURN(NULL);
91       }
92    } else if( HAS_WILDCARD(dlocal) ) {
93       sar = NI_get_wildcard_list( dlocal );
94 
95       if( ! sar ) {
96          if( tcat_open_verb )
97             WARNING_message("THD_open_tcat: no wildcard match for '%s'",dlocal);
98          RETURN(NULL);
99       }
100    } else {
101       WARNING_message("THD_open_tcat: should find wildcard or space in %s",
102                       dlocal);
103       RETURN(NULL) ;
104    }
105 
106    /* if selectors and/or WILDCARD, append to sar elements and create new */
107    /* 'dlocal' string                                  5 Apr 2016 [rickr] */
108    if( sel || HAS_WILDCARD(dlocal) ) {
109       if( dlocal != dlist ) free(dlocal);    /* free if locally allocated */
110       dlocal = update_sar_with_selectors(sar, sel);
111       if(tcat_open_verb>1) INFO_message("THD_open_tcat: new dlocal %s",dlocal);
112 
113       /* free selectors (if they exist)  18 Apr 2016 [rickr] */
114       if( sel ) { free(sel); sel = NULL; }
115    }
116 
117    /* open all of the input datasets, possibly with selectors */
118    ndset_in = sar->num ;
119    dset_in  = (THD_3dim_dataset **)malloc(sizeof(THD_3dim_dataset *)*sar->num) ;
120    for( nerr=dd=0 ; dd < ndset_in ; dd++ ){
121      if( tcat_open_verb > 1 )
122         INFO_message("THD_open_tcat: opening dset %s ...", sar->str[dd]);
123      dset_in[dd] = THD_open_dataset( sar->str[dd] );
124 
125      if( dset_in[dd] == NULL ){
126        WARNING_message("THD_open_tcat: can't open dataset %s\n", sar->str[dd]);
127        nerr++ ;
128      }
129    }
130    if( nerr > 0 ){
131      for( dd=0 ; dd < ndset_in ; dd++ )
132        if( dset_in[dd] != NULL ) DSET_delete(dset_in[dd]) ;
133      free((void *)dset_in) ;
134      NI_delete_str_array(sar) ;
135      RETURN(NULL) ;
136    }
137    if( ndset_in == 1 ){
138      dset_out = dset_in[0] ;
139      free((void *)dset_in) ;
140      NI_delete_str_array(sar) ;
141      RETURN(dset_out) ;
142    }
143 
144    (void)THD_check_for_duplicates( sar->num , sar->str , 1 ) ;  /* 31 May 2007 */
145 
146    for( nerr=0,dd=1 ; dd < ndset_in ; dd++ ){
147      if( DSET_NX(dset_in[0]) != DSET_NX(dset_in[dd]) ||
148          DSET_NY(dset_in[0]) != DSET_NY(dset_in[dd]) ||
149          DSET_NZ(dset_in[0]) != DSET_NZ(dset_in[dd])   ){
150        ERROR_message(
151                "THD_open_tcat: %s [%dx%dx%d] doesn't match %s [%dx%dx%d]\n",
152                sar->str[0] ,DSET_NX(dset_in[0]) ,
153                             DSET_NY(dset_in[0]) ,DSET_NZ(dset_in[0]) ,
154                sar->str[dd],DSET_NX(dset_in[dd]),
155                             DSET_NY(dset_in[dd]),DSET_NZ(dset_in[dd]) ) ;
156        nerr++ ;
157      } else {
158        if( !EQUIV_DATAXES(dset_in[dd]->daxes,dset_in[0]->daxes) ){
159          WARNING_message(
160                   "THD_open_tcat: %s grid mismatch with %s\n",
161                   sar->str[0] , sar->str[dd] ) ;  /* don't increment nerr! */
162        }
163        /* allow for small differences    22 May 2015 [rickr] */
164        angle = dset_obliquity_angle_diff(dset_in[dd], dset_in[0],
165                                          OBLIQ_ANGLE_THRESH);
166        if (angle > 0.0) {
167          WARNING_message(
168             "dataset %s has an obliquity difference of %f degress with %s\n",
169             dset_in[dd] ,
170             angle, dset_in[0] );
171        }
172      }
173    }
174    if( nerr > 0 ){
175      for( dd=0 ; dd < ndset_in ; dd++ )
176        if( dset_in[dd] != NULL ) DSET_delete(dset_in[dd]) ;
177      free((void *)dset_in) ;
178      NI_delete_str_array(sar) ;
179      RETURN(NULL) ;
180    }
181 
182    /*-- Check for type problems                    ZSS: Aug 27 2012 --*/
183    for (nerr=0,dd=0; dd < ndset_in ; dd++) {
184       for (sb=0; sb < DSET_NVALS(dset_in[dd]); ++sb) {
185          if ( DSET_BRICK_TYPE(dset_in[0],0) !=
186               DSET_BRICK_TYPE(dset_in[dd],sb) ) {
187             ++nerr;
188          }
189       }
190    }
191    if (nerr > 0) { /* don't die, just complain */
192       WARNING_message(
193       "Command-line catenated dataset has %d sub-bricks that differ \n"
194       "  in data type from the first sub-brick of the first set.\n"
195       "  Mme Irma sees potential for grief if you go down that path. \n"
196       "  Use 3dinfo -datum on each input to understand why this is happening.\n"
197       "  You can use 3dcalc's -datum option to rewrite the dataset with \n"
198       "  all sub-bricks set to the same type then start over.\n\n",
199             nerr);
200       nerr=0;
201    }
202 
203    /*-- OK, start making new dataset --*/
204 
205    new_nvals = 0 ;
206    for( dd=0 ; dd < ndset_in ; dd++ )
207      new_nvals += DSET_NVALS(dset_in[dd]) ;
208 
209    for( dd=0 ; dd < ndset_in ; dd++ )
210       if( DSET_TIMESTEP(dset_in[dd]) > 0.0 ) break ;  /* 1st 3D+time */
211    if( dd == ndset_in ) dd = 0 ;
212 
213    dset_out = EDIT_empty_copy( dset_in[dd] ) ;
214 
215    /* since this is basically an input dataset, set the storage_mode
216     * to match                                   27 Jul 2010 [rickr] */
217    if( DSET_ONDISK(dset_out) && IS_VALID_NON_AFNI_DSET(dset_in[dd]) )
218       THD_set_storage_mode(dset_out, dset_in[dd]->dblk->diskptr->storage_mode);
219 
220    EDIT_dset_items( dset_out ,
221                       ADN_prefix    , "tcat" ,
222                       ADN_func_type , ISANAT(dset_in[dd]) ? ANAT_EPI_TYPE
223                                                           : FUNC_FIM_TYPE ,
224                       ADN_ntt       , new_nvals ,
225                       ADN_nvals     , new_nvals ,
226                     ADN_none ) ;
227    DSET_mallocize( dset_out ) ;
228 
229    /* get factors and labels here, not at load time   4 Apr 2016 [rickr] */
230    ivout = 0;
231    for( dd=0 ; dd < ndset_in ; dd++ ) {
232       for (sb=0; sb < DSET_NVALS(dset_in[dd]); ++sb) {
233          EDIT_BRICK_FACTOR(dset_out, ivout, DSET_BRICK_FACTOR(dset_in[dd],sb));
234          EDIT_BRICK_LABEL (dset_out, ivout, DSET_BRICK_LABEL (dset_in[dd],sb));
235          ivout++;
236       }
237    }
238 
239    /* check if we have a valid time axis; if not, make one up */
240 
241    if( DSET_TIMESTEP(dset_out) <= 0.0f ){
242       float TR=1.0f , torg=0.0f , tdur=0.0f ;
243       int tunits=UNITS_SEC_TYPE ;
244       EDIT_dset_items( dset_out ,
245                           ADN_tunits , tunits ,
246                           ADN_ttdel  , TR ,
247                           ADN_ttorg  , torg ,
248                           ADN_ttdur  , tdur ,
249                        ADN_none ) ;
250    }
251 
252    /* if dlocal is not new memory, must copy, else steal */
253    if( dlocal == dlist ) dset_out->tcat_list = strdup( dlocal ) ;
254    else                  dset_out->tcat_list = dlocal;
255 
256    if( tcat_open_verb > 2 )
257       INFO_message("setting tcat_list[%d] = %s", ndset_in, dlocal);
258 
259    dset_out->tcat_num  = ndset_in ;
260    dset_out->tcat_len  = (int *)malloc(sizeof(int)*ndset_in) ;
261    for( dd=0 ; dd < ndset_in ; dd++ ){
262      dset_out->tcat_len[dd] = DSET_NVALS(dset_in[dd]) ;
263      DSET_delete(dset_in[dd]) ;
264       if( tcat_open_verb > 2 )
265          INFO_message("  tcat: including %d volumes", dset_out->tcat_len[dd]);
266    }
267    free((void *)dset_in) ;
268    NI_delete_str_array(sar) ;
269 
270 #if 0
271 fprintf(stderr,"THD_open_tcat('%s'):",dset_out->tcat_list);
272 for(dd=0;dd<ndset_in;dd++)fprintf(stderr," %d",dset_out->tcat_len[dd]);
273 fprintf(stderr,"\n");
274 #endif
275 
276    RETURN(dset_out) ;
277 }
278 
279 /*---------------------------------------------------------------------------*/
280 /*! append selectors to elements of a NI_str_array list                      */
281 /*  - return a catenated string (space delimited)         7 Apr 2016 [rickr] */
282 /*---------------------------------------------------------------------------*/
update_sar_with_selectors(NI_str_array * sar,char * sel)283 static char * update_sar_with_selectors(NI_str_array * sar, char * sel)
284 {
285    char * lptr, * dlist;
286    int    dd, len, fulllen, nsel=0;
287 
288    if( ! sar ) return NULL;
289    if( sel ) nsel = strlen(sel);
290 
291    /* first allocate for catenation string with repeated selectors */
292    fulllen = 1; /* for trailing nul */
293    for( dd=0 ; dd < sar->num ; dd++ )
294       /* extra +1 is for separation spaces */
295       fulllen += strlen(sar->str[dd]) + nsel + 1;
296    dlist = (char *)malloc(fulllen * sizeof(char));
297 
298    /* empty dlist and fill with updated sar names and separation spaces */
299    dlist[0] = '\0';
300    lptr = dlist;
301    for( dd=0 ; dd < sar->num ; dd++ ) {
302       len = strlen(sar->str[dd]) + nsel;
303       sar->str[dd] = NI_realloc(sar->str[dd], char, (len+1)*sizeof(char));
304       if( sel ) strcat(sar->str[dd], sel);
305 
306       /* and append to new dlist string */
307       strcpy(lptr, sar->str[dd]);
308       strcat(lptr, " ");
309       lptr += len+1;
310    }
311 
312    return dlist;
313 }
314 
315 
316 /*---------------------------------------------------------------------------*/
317 /*! Expand the wildcard selection and populate a NI_str_array struct.        */
318 /*  - a wildcard version of NI_decode_string_list         7 Apr 2016 [rickr] */
319 /*---------------------------------------------------------------------------*/
NI_get_wildcard_list(char * pattern)320 static NI_str_array * NI_get_wildcard_list(char * pattern)
321 {
322    NI_str_array * sar=NULL;
323    int            nexp=0, ind;
324    char        ** fexp=NULL;
325 
326 ENTRY("NI_get_wildcard_list");
327 
328    MCW_file_expand(1, &pattern, &nexp, &fexp);
329    if( nexp == 0 ) RETURN(NULL);
330 
331    sar = NI_malloc(NI_str_array, sizeof(NI_str_array));
332    sar->num = nexp;
333    sar->str = NI_malloc(char *, nexp*sizeof(char *));
334 
335    for( ind=0; ind<nexp; ind++ )
336       sar->str[ind] = NI_strdup(fexp[ind]);
337 
338    MCW_free_expand(nexp, fexp);
339 
340    RETURN(sar);
341 }
342 
343 /*---------------------------------------------------------------------------*/
344 /*! Load from disk the impromptu catenation.                                 */
345 /*---------------------------------------------------------------------------*/
346 
THD_load_tcat(THD_datablock * dblk)347 void THD_load_tcat( THD_datablock *dblk )
348 {
349    int ivout , dd , iv ;
350    THD_3dim_dataset *dset_in , *dset_out ;
351    NI_str_array *sar ;
352 
353 ENTRY("THD_load_tcat") ;
354 
355    if( !ISVALID_DBLK(dblk) ) EXRETURN ;
356    dset_out = (THD_3dim_dataset *)dblk->parent ;
357    if( !ISVALID_DSET(dset_out) ) EXRETURN ;
358    sar = NI_decode_string_list( dset_out->tcat_list , "~" ) ;
359    if( sar == NULL ) EXRETURN ;
360    if( sar->num != dset_out->tcat_num ){ NI_delete_str_array(sar); EXRETURN; }
361 
362    ivout = 0 ;
363    for( dd=0 ; dd < sar->num ; dd++ ){
364      dset_in = THD_open_dataset( sar->str[dd] ) ;
365      if( dset_in == NULL ){
366         if( tcat_open_verb )
367            INFO_message("THD_load_tcat: failed for dset %s", sar->str[dd]);
368        NI_delete_str_array(sar) ; DSET_unload(dset_out) ;
369        EXRETURN ;
370      }
371      DSET_mallocize(dset_in) ; DSET_load(dset_in) ;
372      if( !DSET_LOADED(dset_in) ){
373        NI_delete_str_array(sar) ; DSET_unload(dset_out) ; DSET_delete(dset_in) ;
374        EXRETURN ;
375      }
376 
377      for( iv=0 ; iv < DSET_NVALS(dset_in) ; iv++ ){
378        EDIT_substitute_brick( dset_out , ivout ,
379                               DSET_BRICK_TYPE(dset_in,iv), DSET_ARRAY(dset_in,iv) );
380        mri_fix_data_pointer( NULL , DSET_BRICK(dset_in,iv) ) ;
381        ivout++ ;
382      }
383      DSET_delete(dset_in) ;
384    }
385 
386    NI_delete_str_array(sar) ; EXRETURN ;
387 }
388