1 #include "mrilib.h"
2 
3 #undef  NLL
4 #define NLL 32766  /* lbuf length below -- should be enuf */
5 
6 #undef  LVEC
7 #define LVEC 666
8 
9 #undef  ERREX
10 #define ERREX(str) do{ ERROR_message(str); RETURN(NULL); } while(0)
11 
12 #undef  TRBUF
13 #define TRBUF(bb) \
14   do{ int lb=strlen(bb) ; if( bb[lb-1] == '\n' ) bb[lb-1] = '\0' ; } while(0)
15 
16 /*** Modified 20-21 Jul 2011 to allow column selectors []
17      for the table.  Column #0 must be the first one!
18      Row selectors {} don't really make sense, given that
19      the actual rows used are supposed to be taken using
20      column #0 as a key, and unselected rows are to be ignored. ***/
21 
22 /*------------------------------------------------------------------------*/
23 /* Simple table: first column = labels, the rest are numbers. */
24 
THD_simple_table_read(char * fname)25 NI_element * THD_simple_table_read( char *fname )
26 {
27    NI_str_array *sar ;
28    char *dname , *cpt , *dpt , *qpt , lbuf[NLL] ;
29    int ii,jj , ibot , nlab , row=0 , *ivlist=NULL , niv=0 ;
30    NI_element *nel ;
31    FILE *fts ;
32    float val ;
33    int verb = AFNI_yesenv("AFNI_DEBUG_TABLE") ;
34 
35 ENTRY("THD_simple_table_read") ;
36 
37    /*--  check for bad-ositiness --*/
38 
39    if( fname == NULL || *fname == '\0' )                 ERREX("Table: bad filename") ;
40    ii = strlen(fname) ;
41    if( (ii <= 2 && fname[0] == '-')                  ||
42        (ii <= 6 && strncmp(fname,"stdin"   ,5) == 0) ||
43        (ii <= 9 && strncmp(fname,"/dev/fd0",8) == 0)   ) ERREX("Table: stdin not allowed") ;
44    if( strncmp(fname,"1D:",3) == 0 )                     ERREX("Table: 1D: not allowed") ;
45 
46    /* copy filename to local variable for editing purposes */
47 
48    dname = strdup(fname) ; if( dname[ii-1] == '\'' ) dname[ii-1] = '\0' ;
49 
50    /*-- split filename and subvector list --*/
51 
52    cpt = strstr(fname,"[") ;  /* column list */
53    dpt = strstr(fname,"{") ;  /* we don't use this row list */
54 
55    if( cpt == fname || dpt == fname ){  /* can't be at start of filename! */
56      free(dname) ; ERREX("Table: '[ or {' selectors at start of filename") ;
57    } else {                             /* got a subvector list */
58      if( cpt != NULL ){ ii = cpt-fname; dname[ii] = '\0'; }
59      if( dpt != NULL ){ ii = dpt-fname; dname[ii] = '\0'; }
60    }
61 
62    /* open input file */
63 
64    if( verb ) INFO_message("Simple Table: processing file %s",dname) ;
65 
66    fts = fopen(dname,"r") ; if( fts == NULL ){ free(dname); ERREX("Table: can't open file"); }
67 
68    /* read lines until we get a useful line */
69 
70    while(1){
71      lbuf[0] = '\0' ;
72      dpt = afni_fgets( lbuf , NLL , fts ) ;            /* read a line of data */
73      if( dpt == NULL ){ fclose(fts); free(dname); ERREX("Table: can't read first line"); }
74      ii = strlen(lbuf) ; if( ii == 0 ) continue ;         /* nada => loopback */
75      if( ii == 1 && isspace(lbuf[0]) ) continue ; /* 1 blank only => loopback */
76      ibot = (lbuf[0] == '#') ? 1 : 0 ;                   /* start of scanning */
77      for( ii=ibot ; isspace(lbuf[ii]) ; ii++ ) continue ;      /* skip blanks */
78      if( lbuf[ii] == '\0' ) continue ;              /* all blanks => loopback */
79      ibot = ii ; break ;                             /* can process this line */
80    }
81 
82    TRBUF(lbuf) ;
83    if( verb ) ININFO_message("  first line = '%s'",lbuf+ibot) ;
84 
85    /* break line into sub-strings */
86 
87    sar = NI_decode_string_list( lbuf+ibot , ";" ) ;
88    if( sar == NULL ){ fclose(fts); free(dname); ERREX("Table: can't decode first line"); }    /* nuthin? */
89 
90    nlab = sar->num ;
91    if( verb ) ININFO_message("  found %d labels in first line",nlab) ;
92    if( nlab <= 1 ){
93      if( nlab == 1 )                      /* need to have at least 2 columns! */
94        ERROR_message("Short table line (missing label or data?) -- %s",dname) ;
95      else
96        ERROR_message("No valid table line found in file %s",dname) ;
97      fclose(fts) ; NI_delete_str_array(sar) ; free(dname) ; RETURN(NULL) ;
98    }
99 
100    /* 20 Jul 2011 -- get column list, if present */
101 
102    if( cpt != NULL ){
103      if( verb ) ININFO_message("  processing column selector '%s'",cpt) ;
104      ivlist = MCW_get_intlist( nlab , cpt ) ;
105      if( ivlist != NULL ){
106        if( ivlist[0] <= 1 || ivlist[1] != 0 ){  /* must have col #0 first */
107          free(ivlist) ; ivlist=NULL ;
108          WARNING_message("Ignoring selector '%s' for table file '%s'",cpt,dname) ;
109        } else {
110          niv = ivlist[0] ;  /* number of columns */
111        }
112      }
113    }
114 
115    /* setup output data structure */
116 
117    nel = NI_new_data_element( "AFNI_table" , LVEC ) ;
118 
119    NI_add_column( nel , NI_STRING , NULL ) ;
120    if( niv <= 1 )
121      for( ii=1 ; ii < nlab ; ii++ ) NI_add_column( nel , NI_FLOAT , NULL ) ;
122    else
123      for( jj=2 ; jj <= niv ; jj++ ) NI_add_column( nel , NI_FLOAT , NULL ) ;
124 
125    strcpy( lbuf , sar->str[0] ) ;
126    if( niv <= 1 ){
127      for( ii=1 ; ii < nlab ; ii++ ){
128        strcat( lbuf , ";" ) ; strcat( lbuf , sar->str[ii] ) ;
129      }
130    } else {
131      for( jj=2 ; jj <= niv ; jj++ ){
132        ii = ivlist[jj] ;
133        strcat( lbuf , ";" ) ; strcat( lbuf , sar->str[ii] ) ;
134      }
135    }
136    NI_set_attribute( nel , "Labels" , lbuf ) ;
137    NI_delete_str_array(sar) ;
138 
139    /* now read lines and process them */
140 
141    while(1){
142 
143      /* scan ahead for next good input line */
144 
145      while(1){
146        lbuf[0] = '\0' ; ibot = -1 ;
147        dpt = afni_fgets( lbuf , NLL , fts ) ;          /* read a line of data */
148        if( dpt == NULL ) break ;                                     /* error */
149        ii = strlen(lbuf) ; if( ii <= 2*nlab ) continue ;  /* nada => loopback */
150        if( lbuf[0] == '#' ) continue ;                 /* comment => loopback */
151        ibot = (lbuf[0] == '#') ? 1 : 0 ;                 /* start of scanning */
152        for( ii=ibot ; isspace(lbuf[ii]) ; ii++ ) continue ;    /* skip blanks */
153        if( lbuf[ii] == '\0' ) continue ;            /* all blanks => loopback */
154        ibot = ii ; break ;                           /* can process this line */
155      } /* loop to get next line */
156 
157      if( ibot < 0 ) break ;           /* end of input ==> done with this file */
158 
159      TRBUF(lbuf) ;
160      if( verb ) ININFO_message("  processing row #%d = '%s'",row,lbuf+ibot) ;
161 
162      sar = NI_decode_string_list( lbuf+ibot , ";" ) ;
163      if( sar == NULL ) continue ;                      /* nuthin ==> loopback */
164 
165      if( row >= nel->vec_len )
166        NI_alter_veclen( nel , nel->vec_len + LVEC ) ; /* need more data space */
167 
168      /* put values from this line into this row of the table */
169 
170      NI_insert_string( nel , row , 0 , sar->str[0] ) ;           /* column #0 */
171      if( niv <= 1 ){                                        /* do all columns */
172        for( ii=1 ; ii < nlab ; ii++ ){
173          if( ii < sar->num ){
174            val = (float)strtod( sar->str[ii] , &qpt ) ;
175            if( *qpt != '\0' )
176              WARNING_message("value '%s' in table file '%s' row #%d col #%d is non-float",
177                               sar->str[ii] , dname , row+1 , ii ) ;
178          } else {
179            val = 0.0f ;
180            WARNING_message("table file '%s' row #%d col #%d : no value present :-(",
181                            dname , row+1 , ii ) ;
182          }
183          NI_insert_value( nel , row , ii , &val ) ;
184        }
185      } else {                                            /* just some columns */
186        for( jj=2 ; jj <= niv ; jj++ ){
187          ii = ivlist[jj] ;
188          if( ii < sar->num ){
189            val = (float)strtod( sar->str[ii] , &qpt ) ;
190            if( *qpt != '\0' )
191              WARNING_message("value '%s' in table file '%s' row #%d col #%d is non-float",
192                               sar->str[ii] , dname , row+1 , ii ) ;
193          } else {
194            val = 0.0f ;
195            WARNING_message("table file '%s' row #%d col #%d : no value present :-(",
196                            dname , row+1 , ii ) ;
197          }
198          NI_insert_value( nel , row , jj-1 , &val ) ;
199        }
200      }
201 
202      row++ ;  /* have one more row! */
203      NI_delete_str_array(sar) ;
204 
205    } /* loop to get next row */
206 
207    /* cleanup and exit */
208 
209    fclose(fts) ;
210    if( row == 0 ){ NI_free_element(nel); free(dname); ERREX("Table: no data lines found"); }
211    if( ivlist != NULL ) free(ivlist) ;
212 
213    NI_alter_veclen(nel,row) ;
214 
215    /* check for duplicate first column labels */
216 
217    if( verb ) ININFO_message("checking for duplicate labels") ;
218 
219    for( ii=0 ; ii < nel->vec_len ; ii++ ){
220      cpt = ((char **)(nel->vec[0]))[ii] ;
221      for( jj=ii+1 ; jj < nel->vec_len ; jj++ ){
222        dpt = ((char **)(nel->vec[0]))[jj] ;
223        if( strcmp(cpt,dpt) == 0 )
224          WARNING_message("Table file '%s': rows %d & %d have same label %s",
225                          dname , ii+1,jj+1,cpt) ;
226      }
227    }
228 
229    if( verb ){
230      ININFO_message("Table element follows::") ;
231      NI_write_element_tofile( "stdout:" , nel , NI_TEXT_MODE ) ;
232    }
233 
234    free(dname) ; RETURN(nel) ;
235 }
236 
237 /*------------------------------------------------------------------------*/
238 
string_ectomy(char * src,char * bad)239 void string_ectomy( char *src , char *bad )  /* 20 Jun 2012 */
240 {
241    int nsrc , nbad , is , io , ib ;
242    char *out , ccc ;
243 
244    if( src == NULL || bad == NULL || *src == '\0' || *bad == '\0' ) return ;
245 
246    nsrc = strlen(src) ; out = calloc(sizeof(char),(nsrc+1)) ;
247    nbad = strlen(bad) ;
248 
249    for( io=is=0 ; is < nsrc ; is++ ){
250      ccc = src[is] ;
251      if( (unsigned char)ccc       == 194u &&
252          (unsigned char)src[is+1] == 160u   ){         /* nonbreaking space */
253        out[io++] = ' ' ; is++ ; continue ;     /* replaced by regular space */
254      }
255      if( !isprint(ccc) ) continue ;                /* non-printable == skip */
256      for( ib=0 ; ib < nbad && bad[ib] != ccc ; ib++ ) ; /*nada*/
257      if( ib == nbad ) out[io++] = ccc ;         /* if not in bad list, keep */
258    }
259 
260    if( io < nsrc ){
261 #if 0
262      ININFO_message("Table reading: replaced string  %s  -with-  %s",src,out) ;
263 #endif
264      strcpy(src,out) ;
265    }
266 
267    free(out) ; return ;
268 }
269 
270 /*------------------------------------------------------------------------*/
271 /* This table has column #0 as strings (labels), and remaining
272    columns are numbers or strings.  Each column is 'pure' in type.
273 *//*----------------------------------------------------------------------*/
274 
THD_mixed_table_read(char * fname)275 NI_element * THD_mixed_table_read( char *fname )
276 {
277    NI_str_array *sar ;
278    char *dname , *cpt , *dpt , *qpt , lbuf[NLL] ;
279    int ii,jj , ibot , nlab , row=0 , *ivlist=NULL , niv=0 ;
280    NI_element *nel ;
281    FILE *fts ;
282    float val ;
283    int verb = AFNI_yesenv("AFNI_DEBUG_TABLE") ;
284 
285 ENTRY("THD_mixed_table_read") ;
286 
287    /*--  check for bad-ositiness --*/
288 
289    if( fname == NULL || *fname == '\0' )                 ERREX("Table: bad filename") ;
290    ii = strlen(fname) ;
291    if( (ii <= 2 && fname[0] == '-')                  ||
292        (ii <= 6 && strncmp(fname,"stdin"   ,5) == 0) ||
293        (ii <= 9 && strncmp(fname,"/dev/fd0",8) == 0)   ) ERREX("Table: stdin not allowed") ;
294    if( strncmp(fname,"1D:",3) == 0 )                     ERREX("Table: 1D: not allowed") ;
295 
296    /* copy filename to local variable for editing purposes */
297 
298    dname = strdup(fname) ; if( dname[ii-1] == '\'' ) dname[ii-1] = '\0' ;
299 
300    /*-- split filename and subvector list --*/
301 
302    cpt = strstr(fname,"[") ;  /* column list */
303    dpt = strstr(fname,"{") ;  /* we don't use this row list */
304 
305    if( cpt == fname || dpt == fname ){  /* can't be at start of filename! */
306      free(dname) ; ERREX("Table: '[{' selector at start of filename") ;
307    } else {                             /* got a subvector list */
308      if( cpt != NULL ){ ii = cpt-fname; dname[ii] = '\0'; }
309      if( dpt != NULL ){ ii = dpt-fname; dname[ii] = '\0'; }
310    }
311 
312    /* open input file */
313 
314    if( verb ) INFO_message("Mixed Table: processing file %s",dname) ;
315 
316    fts = fopen(dname,"r") ; if( fts == NULL ){ free(dname); ERREX("Table: can't open file"); }
317 
318    /* read lines until we get a useful line */
319 
320    while(1){
321      lbuf[0] = '\0' ;
322      dpt = afni_fgets( lbuf , NLL , fts ) ;            /* read a line of data */
323      if( dpt == NULL ){ fclose(fts); free(dname); ERREX("Table: Can't read first line"); }
324      ii = strlen(lbuf) ; if( ii == 0 ) continue ;         /* nada => loopback */
325      if( ii == 1 && isspace(lbuf[0]) ) continue ; /* 1 blank only => loopback */
326      ibot = (lbuf[0] == '#') ? 1 : 0 ;                   /* start of scanning */
327      for( ii=ibot ; isspace(lbuf[ii]) ; ii++ ) continue ;      /* skip blanks */
328      if( lbuf[ii] == '\0' ) continue ;              /* all blanks => loopback */
329      ibot = ii ; break ;                             /* can process this line */
330    }
331 
332    TRBUF(lbuf) ;
333    if( verb ) ININFO_message("  first line = '%s'",lbuf+ibot) ;
334 
335    /* break line into sub-strings */
336 
337    sar = NI_decode_string_list( lbuf+ibot , ";" ) ;
338    if( sar == NULL ){ fclose(fts); free(dname); ERREX("Table: Can't decode first line"); }    /* nuthin? */
339 
340    nlab = sar->num ;         /* number of labels = number of separate strings */
341    if( verb ) ININFO_message("  found %d labels in first line",nlab) ;
342    if( nlab <= 1 ){
343      if( nlab == 1 )                      /* need to have at least 2 columns! */
344        ERROR_message("short table line (missing label or data?) -- %s",dname) ;
345      else
346        ERROR_message("no valid table line found in file %s",dname) ;
347      fclose(fts) ; NI_delete_str_array(sar) ; free(dname) ; RETURN(NULL) ;
348    }
349 
350    /* 21 Jul 2011 -- get column list, if present */
351 
352    if( cpt != NULL ){
353      if( verb ) ININFO_message("  processing column selector '%s'",cpt) ;
354      ivlist = MCW_get_intlist( nlab , cpt ) ;
355      if( ivlist != NULL ){
356        if( ivlist[0] <= 1 || ivlist[1] != 0 ){
357          free(ivlist) ; ivlist=NULL ;
358          WARNING_message("Ignoring selector '%s' for table file '%s'",cpt,dname) ;
359        } else {
360          niv = ivlist[0] ;
361        }
362      }
363    }
364 
365    /* setup output data structure */
366 
367    nel = NI_new_data_element( "AFNI_table" , LVEC ) ;
368 
369    NI_add_column( nel , NI_STRING , NULL ) ;
370 
371    strcpy( lbuf , sar->str[0] ) ;
372    if( niv <= 1 ){
373      for( ii=1 ; ii < nlab ; ii++ ){
374        strcat( lbuf , ";" ) ; strcat( lbuf , sar->str[ii] ) ;
375      }
376    } else {
377      for( jj=2 ; jj <= niv ; jj++ ){
378        ii = ivlist[jj] ;
379        strcat( lbuf , ";" ) ; strcat( lbuf , sar->str[ii] ) ;
380      }
381    }
382    NI_set_attribute( nel , "Labels" , lbuf ) ;
383    NI_delete_str_array(sar) ;
384 
385    /* now read following lines and process them */
386 
387    while(1){
388 
389      /* scan ahead for next good input line */
390 
391      while(1){
392        lbuf[0] = '\0' ; ibot = -1 ;
393        dpt = afni_fgets( lbuf , NLL , fts ) ;          /* read a line of data */
394        if( dpt == NULL ) break ;                                     /* error */
395        ii = strlen(lbuf) ; if( ii <= 2*nlab ) continue ;  /* nada => loopback */
396        if( lbuf[0] == '#' ) continue ;                 /* comment => loopback */
397        ibot = (lbuf[0] == '#') ? 1 : 0 ;                 /* start of scanning */
398        for( ii=ibot ; isspace(lbuf[ii]) ; ii++ ) continue ;    /* skip blanks */
399        if( lbuf[ii] == '\0' ) continue ;            /* all blanks => loopback */
400        ibot = ii ; break ;                           /* can process this line */
401      } /* loop to get next line */
402 
403      if( ibot < 0 ) break ;           /* end of input ==> done with this file */
404 
405      TRBUF(lbuf) ;
406      if( verb ) ININFO_message("  processing row #%d = '%s'",row,lbuf+ibot) ;
407 
408      sar = NI_decode_string_list( lbuf+ibot , ";" ) ;
409      if( sar == NULL ) continue ;                      /* nuthin ==> loopback */
410 
411      if( row == 0 ){   /* first row ==> figure out format */
412        if( sar->num < nlab ){
413          ERROR_message("First row of values in '%s' is too short!",dname) ;
414          NI_delete_str_array(sar) ; NI_free_element(nel) ; fclose(fts) ;
415          free(dname) ; RETURN(NULL) ;
416        }
417        if( niv <= 1 ){
418          if( verb ) ININFO_message("  deciding format of %d columns",nlab-1) ;
419          for( ii=1 ; ii < nlab ; ii++ ){
420            val = (float)strtod( sar->str[ii] , &qpt ) ;
421            if( *qpt == '\0' ){
422              NI_add_column( nel , NI_FLOAT  , NULL ) ;
423              if( verb ) ININFO_message("  -- col#%d is numeric",ii) ;
424            } else {
425              NI_add_column( nel , NI_STRING , NULL ) ;
426              if( verb ) ININFO_message("  -- col#%d is string",ii) ;
427            }
428          }
429        } else {
430          if( verb ) ININFO_message("  deciding format of %d chosen columns",niv-1) ;
431          for( jj=2 ; jj <= niv ; jj++ ){
432            ii = ivlist[jj] ;
433            val = (float)strtod( sar->str[ii] , &qpt ) ;
434            if( *qpt == '\0' ){
435              NI_add_column( nel , NI_FLOAT  , NULL ) ;
436              if( verb ) ININFO_message("  -- col#%d is numeric",ii) ;
437            } else {
438              NI_add_column( nel , NI_STRING , NULL ) ;
439              if( verb ) ININFO_message("  -- col#%d is string",ii) ;
440            }
441          }
442        }
443      } /* end of decoding format */
444 
445      if( row >= nel->vec_len )
446        NI_alter_veclen( nel , nel->vec_len + LVEC ) ; /* need more data space */
447 
448      /* put values from this line into this row of the table */
449 
450      NI_insert_string( nel , row , 0 , sar->str[0] ) ;
451      if( niv <= 1 ){
452        for( ii=1 ; ii < nlab ; ii++ ){
453          if( nel->vec_typ[ii] == NI_FLOAT ){
454            if( ii < sar->num ){
455              val = (float)strtod( sar->str[ii] , &qpt ) ;
456              if( *qpt != '\0' )
457                WARNING_message("value '%s' in table file '%s' row #%d col #%d is non-float",
458                                 sar->str[ii] , dname , row+1 , ii ) ;
459            } else{
460              val = 0.0f ;
461              WARNING_message("table file '%s' row #%d col #%d : no value present :-(",
462                              dname , row+1 , ii ) ;
463            }
464            NI_insert_value( nel , row , ii , &val ) ;
465          } else {
466            if( ii < sar->num ) dpt = sar->str[ii] ;
467            else                dpt = "N/A" ;
468            string_ectomy( dpt , "\"'" ) ;
469            NI_insert_string( nel , row , ii , dpt ) ;
470          }
471        }
472      } else {
473        for( jj=2 ; jj <= niv ; jj++ ){
474          ii = ivlist[jj] ;
475          /* vec_typ[ii] here would likely mean that K final columns would be
476             lost in the case of K text columns (after label)
477             --> issue noted by Phoebe from Harvard       26 Jul 2012 [rickr] */
478          if( nel->vec_typ[jj-1] == NI_FLOAT ){
479            if( ii < sar->num ){
480              val = (float)strtod( sar->str[ii] , &qpt ) ;
481              if( *qpt != '\0' )
482                WARNING_message("value '%s' in table file '%s' row #%d col #%d is non-float",
483                                 sar->str[ii] , dname , row+1 , ii ) ;
484            } else{
485              val = 0.0f ;
486              WARNING_message("table file '%s' row #%d col #%d : no value present :-(",
487                              dname , row+1 , ii ) ;
488            }
489            NI_insert_value( nel , row , jj-1 , &val ) ;
490          } else {
491            if( ii < sar->num ) dpt = sar->str[ii] ;
492            else                dpt = "N/A" ;
493            string_ectomy( dpt , "\"'" ) ;
494            NI_insert_string( nel , row , jj-1 , dpt ) ;
495          }
496        }
497      }
498 
499      row++ ;  /* have one more row! */
500      NI_delete_str_array(sar) ;
501 
502    } /* loop to get next row */
503 
504    /* cleanup and exit */
505 
506    fclose(fts) ;
507    if( ivlist != NULL ) free(ivlist) ;
508    if( row == 0 ){ NI_free_element(nel); free(dname); ERREX("Table: no data in file"); }
509 
510    NI_alter_veclen(nel,row) ;
511 
512    /* check for duplicate first column labels */
513 
514    if( verb ) ININFO_message("checking for duplicate labels") ;
515 
516    for( ii=0 ; ii < nel->vec_len ; ii++ ){
517      cpt = ((char **)(nel->vec[0]))[ii] ;
518      for( jj=ii+1 ; jj < nel->vec_len ; jj++ ){
519        dpt = ((char **)(nel->vec[0]))[jj] ;
520        if( strcmp(cpt,dpt) == 0 )
521          WARNING_message("Table file '%s': rows %d & %d have same label %s",
522                          dname,ii+1,jj+1,cpt) ;
523      }
524    }
525 
526    if( verb ){
527      ININFO_message("Table element follows::") ;
528      NI_write_element_tofile( "stdout:" , nel , NI_TEXT_MODE ) ;
529    }
530 
531    free(dname) ; RETURN(nel) ;
532 }
533 
534 /*------------------------------------------------------------------------*/
535 /* This table has only strings, and no header labels.
536    Bit flags:
537      1 = read as tsv (tab separated values)
538      2 = skip column selectors, if present
539      4 = read as csv (comma separated values)
540    If neither 1 nor 4 is set, then whitespace and/or ';' are separators.
541    If 1 and 4 are both set, the separator is determined from fname;
542     however, if the suffix isn't .tsv or .csv (case-insensitive),
543     then you are back in the case where neither 1 nor 4 is set :(
544 *//*----------------------------------------------------------------------*/
545 
THD_string_table_read(char * fname,int flags)546 NI_element * THD_string_table_read( char *fname , int flags )
547 {
548    NI_str_array *sar ;
549    char *dname , *cpt , *dpt , *qpt , lbuf[NLL] ;
550    int ii,jj , ibot , ncol=0 , row=0 , *ivlist=NULL , niv=0 ;
551    NI_element *nel ;
552    FILE *fts ;
553    float val ;
554    int verb = AFNI_yesenv("AFNI_DEBUG_TABLE") ;
555    int do_tsv   = (flags & 1) != 0 ;
556    int skip_sel = (flags & 2) != 0 ;
557    int do_csv   = (flags & 4) != 0 ;  /* 15 Apr 2019 */
558 
559 ENTRY("THD_string_table_read") ;
560 
561    /*--  check for bad-ositiness --*/
562 
563    if( fname == NULL || *fname == '\0' )                 ERREX("Table: bad filename") ;
564    ii = strlen(fname) ;
565    if( (ii <= 2 && fname[0] == '-')                  ||
566        (ii <= 6 && strncmp(fname,"stdin"   ,5) == 0) ||
567        (ii <= 9 && strncmp(fname,"/dev/fd0",8) == 0)   ) ERREX("Table: stdin not allowed") ;
568    if( strncmp(fname,"1D:",3) == 0 )                     ERREX("Table: 1D: not allowed") ;
569 
570    /* copy filename to local variable for editing purposes */
571 
572    dname = strdup(fname) ; if( dname[ii-1] == '\'' ) dname[ii-1] = '\0' ;
573 
574    /*-- split filename and subvector list --*/
575 
576    cpt = strstr(fname,"[") ;  /* column list */
577    dpt = strstr(fname,"{") ;  /* we don't use this row list */
578 
579    if( cpt == fname || dpt == fname ){  /* can't be at start of filename! */
580      free(dname) ; ERREX("Table: '[{' selector at start of filename") ;
581    } else {                             /* got a subvector list */
582      if( cpt != NULL ){ ii = cpt-fname; dname[ii] = '\0'; }
583      if( dpt != NULL ){ ii = dpt-fname; dname[ii] = '\0'; }
584    }
585 
586    /* open input file */
587 
588    if( verb ) INFO_message("String Table: processing file %s",dname) ;
589 
590    fts = fopen(dname,"r") ; if( fts == NULL ){ free(dname); ERREX("Table: can't open file"); }
591 
592    /* 21 Jul 2011 -- get column list, if present */
593 
594    if( cpt != NULL && !skip_sel ){
595      if( verb ) ININFO_message("  processing column selector '%s'",cpt) ;
596      ivlist = MCW_get_intlist( 6666 , cpt ) ;
597      if( ivlist != NULL ){
598        if( ivlist[0] < 1 ){
599          free(ivlist) ; ivlist=NULL ;
600          WARNING_message("Ignoring selector '%s' for table file '%s'",cpt,dname) ;
601        } else {
602          niv = ivlist[0] ;
603        }
604      }
605    }
606 
607    /* setup output data structure */
608 
609    nel = NI_new_data_element( "AFNI_table" , LVEC ) ;
610 
611    /* now read lines and process them */
612 
613    while(1){
614 
615      /* scan ahead for next good input line */
616 
617      while(1){
618        lbuf[0] = '\0' ; ibot = -1 ;
619        dpt = afni_fgets( lbuf , NLL , fts ) ;          /* read a line of data */
620        if( dpt == NULL ) break ;                                     /* error */
621        ii = strlen(lbuf) ; if( ii == 0 ) continue ;       /* nada => loopback */
622        if( lbuf[0] == '#' ) continue ;                 /* comment => loopback */
623        ibot = 0 ;                                        /* start of scanning */
624        for( ii=ibot ; isspace(lbuf[ii]) ; ii++ ) continue ;    /* skip blanks */
625        if( lbuf[ii] == '\0' ) continue ;            /* all blanks => loopback */
626        ibot = ii ; break ;                           /* can process this line */
627      } /* loop to get next line */
628 
629      if( ibot < 0 ) break ;           /* end of input ==> done with this file */
630 
631      TRBUF(lbuf) ;
632      if( verb ) ININFO_message("  processing row #%d = '%s'",row,lbuf+ibot) ;
633 
634      if( do_tsv && do_csv ){  /* 12 Jun 2020 */
635        do_tsv =            STRING_HAS_SUFFIX_CASE( dname , ".tsv" ) ;
636        do_csv = !do_tsv && STRING_HAS_SUFFIX_CASE( dname , ".csv" ) ;
637      }
638 
639      if( do_tsv )
640        sar = NI_strict_decode_string_list( lbuf+ibot, "\t" ) ; /* 08 Feb 2018 */
641      else if( do_csv )
642        sar = NI_strict_decode_string_list( lbuf+ibot, "," ) ;  /* 15 Apr 2019 */
643      else
644        sar = NI_decode_string_list( lbuf+ibot , ";" ) ;
645      if( sar == NULL ) continue ;                      /* nuthin ==> loopback */
646 
647      if( row == 0 ){   /* first row ==> format element */
648        ncol = (niv <= 0) ? sar->num : niv ;
649        for( ii=1 ; ii <= ncol ; ii++ )
650          NI_add_column( nel , NI_STRING , NULL ) ;
651      }
652 
653      if( row >= nel->vec_len )
654        NI_alter_veclen( nel , nel->vec_len + LVEC ) ; /* need more data space */
655 
656      /* put values from this line into this row of the table */
657 
658      if( niv <= 0 ){
659        for( ii=0 ; ii < ncol ; ii++ ){
660          if( ii < sar->num ) dpt = sar->str[ii] ;
661          else                dpt = "N/A" ;
662          string_ectomy( dpt , "\"'" ) ;
663          NI_insert_string( nel , row , ii , dpt ) ;
664        }
665      } else {
666        for( jj=1 ; jj <= niv ; jj++ ){
667          ii = ivlist[jj] ;
668          if( ii < sar->num ) dpt = sar->str[ii] ;
669          else                dpt = "N/A" ;
670          string_ectomy( dpt , "\"'" ) ;
671          NI_insert_string( nel , row , jj-1 , dpt ) ;
672        }
673      }
674 
675      row++ ;  /* have one more row! */
676      NI_delete_str_array(sar) ;
677 
678    } /* loop to get next row */
679 
680    /* cleanup and exit */
681 
682    fclose(fts) ;
683    if( ivlist != NULL ) free(ivlist) ;
684    if( row == 0 ){ NI_free_element(nel); free(dname); ERREX("Table: no data in file"); }
685 
686    NI_alter_veclen(nel,row) ;
687 
688    if( verb ){
689      ININFO_message("Table element follows::") ;
690      NI_write_element_tofile( "stdout:" , nel , NI_TEXT_MODE ) ;
691    }
692 
693    free(dname) ; RETURN(nel) ;
694 }
695 
696 /*----------------------------------------------------------------------------*/
697 /* Read a tab-separated file, and convert to a NI_element with
698    vector labels (vec_lab) and with numeric columns where possible.
699 *//*--------------------------------------------------------------------------*/
700 
THD_read_tsv(char * fname)701 NI_element * THD_read_tsv( char *fname )
702 {
703    NI_element *tnel , *fnel=NULL ;
704    int ii,jj , vnum,vlen , nbad ;
705    char **vec_lab , **cpt , *dpt ;
706 
707 ENTRY("THD_read_tsv") ;
708 
709    /* try to read as a table of strings */
710 
711    tnel = THD_string_table_read( fname , 3 ) ;
712    if( tnel == NULL )                           RETURN(NULL) ;
713 
714    vnum = tnel->vec_num ;     /* number of columns */
715    vlen = tnel->vec_len - 1 ; /* first row is labels */
716    if( vnum < 1 && vlen < 2 )                   RETURN(NULL) ;
717 
718    /* extract labels from first string of each column vector */
719 
720 /* INFO_message("TSV data from %s - %d cols %d rows",fname,vnum,vlen) ; */
721    vec_lab = NI_malloc( char* , sizeof(char *)*vnum ) ;
722    for( ii=0 ; ii < vnum ; ii++ ){
723      cpt = (char **)tnel->vec[ii] ;
724      vec_lab[ii] = NI_strdup( cpt[0] ) ;
725 /* ININFO_message(" vec_lab[%d] = %s",ii,vec_lab[ii]) ; */
726    }
727 
728    fnel = NI_new_data_element( "tsv" , vlen ) ;
729 
730 #undef  FBAD
731 #define FBAD(sss)                       \
732  ( strcasecmp ((sss),"N/A"  ) == 0 ||   \
733    strncasecmp((sss),"NAN",3) == 0 ||   \
734    strncasecmp((sss),"INF",3) == 0   )
735 
736 /* ININFO_message("---columns---")  ; */
737    for( ii=0 ; ii < vnum ; ii++ ){
738      cpt = (char **)tnel->vec[ii] ;
739      jj  = NI_count_numbers( vlen , cpt+1 ) ;
740      if( jj == vlen ){  /* pure numbers */
741        float *far = (float *)malloc(sizeof(float)*vlen) ;
742        for( jj=0 ; jj < vlen ; jj++ ){
743          far[jj] = (float)strtod(cpt[jj+1],NULL) ;
744        }
745        nbad = thd_floatscan( vlen , far ) ;
746 
747        /* repair bad things [17 Oct 2018] */
748        { int ngood=0 ; float sgood=0.0f ;
749          for( jj=0 ; jj < vlen ; jj++ ){
750            if( !FBAD(cpt[jj+1]) ){ ngood++ ; sgood += far[jj]; }
751          }
752          if( ngood < vlen ){
753            if( ngood > 0 ) sgood /= ngood ;
754            for( jj=0 ; jj < vlen ; jj++ ){
755              if( FBAD(cpt[jj+1]) ) far[jj] = sgood ;
756            }
757          }
758        }
759 
760        NI_add_column( fnel , NI_FLOAT , far ) ;
761        NI_set_column_label( fnel , ii , vec_lab[ii] ) ;
762        free(far) ;
763 /* ININFO_message(" column %s: floats with %d errors",vec_lab[ii],nbad) ; */
764      } else {           /* strings */
765        NI_add_column( fnel , NI_STRING , cpt+1 ) ;
766        NI_set_column_label( fnel , ii , vec_lab[ii] ) ;
767 /* ININFO_message(" column %s: strings",vec_lab[ii]) ; */
768      }
769    }
770 
771    NI_free_element(tnel) ; /* old stuff gets thrown out */
772 
773    /* see if we have to deal with column selectors */
774 
775    dpt = strstr(fname,"[") ;
776    if( dpt != NULL ){
777      int *ivlist ;
778      ivlist = MCW_get_labels_intlist( fnel->vec_lab , vnum , dpt ) ;
779      if( ivlist != NULL && ivlist[0] > 0 ){ /* extract subset of columns */
780        NI_element *qnel = NI_extract_columns( fnel , ivlist[0] , ivlist+1 ) ;
781        if( qnel != NULL ){ NI_free_element(fnel) ; fnel = qnel ; }
782      }
783    }
784 
785 /*   NI_write_element_tofile( "stderr:" , fnel , NI_TEXT_MODE ) ; */
786 
787 #if 0 /* debug */
788 INFO_message("=========== tsv dump ==========") ;
789    THD_write_tsv( "stdout:" , fnel ) ;
790 INFO_message("===============================") ;
791 #endif
792 
793    RETURN(fnel) ;
794 }
795 
796 /*----------------------------------------------------------------------------*/
797 
THD_write_tsv(char * fname,NI_element * nel)798 void THD_write_tsv( char *fname , NI_element *nel )
799 {
800    char ssep[2] ; int notfirst,ii,kk ;
801    FILE *fp ;
802 
803 ENTRY("THD_write_tsv") ;
804 
805    if( fname == NULL || NI_element_type(nel) != NI_ELEMENT_TYPE ) EXRETURN ;
806    if( nel->vec_lab == NULL || nel->vec_num == 0 )                EXRETURN ;
807 
808    fp = fopen_maybe(fname) ; if( fp == NULL ) EXRETURN ;
809 
810 #undef  SET_SEPCHAR
811 #define SET_SEPCHAR  do{ ssep[0] = ( (notfirst++) ? '\t' : '\0' ) ; } while(0)
812    ssep[1] = '\0' ;
813 
814    /* header row */
815 
816    for( notfirst=kk=0 ; kk < nel->vec_num ; kk++ ){
817      SET_SEPCHAR ;
818      fprintf(fp,"%s%s",ssep,nel->vec_lab[kk]) ;
819    }
820    fprintf(fp,"\n") ;
821 
822    /* data rows */
823 
824    for( ii=0 ; ii < nel->vec_len ; ii++ ){
825      for( notfirst=kk=0 ; kk < nel->vec_num ; kk++ ){
826        SET_SEPCHAR ;
827        if( nel->vec_typ[kk] == NI_FLOAT ){
828          float *far = (float *)nel->vec[kk] ;
829          fprintf(fp,"%s%g",ssep,far[ii]) ;
830        } else if( nel->vec_typ[kk] == NI_STRING ){
831          char **cpt = (char **)nel->vec[kk] ;
832          fprintf(fp,"%s%s" , ssep , (cpt[ii] != NULL) ? cpt[ii] : "(null)" ) ;
833        }
834      }
835      fprintf(fp,"\n") ;
836    }
837 
838    fclose_maybe(fp) ;
839    EXRETURN ;
840 }
841 
842 /*----------------------------------------------------------------------------*/
843 
THD_set_tsv_column_labels(NI_element * fnel,char ** clab)844 void THD_set_tsv_column_labels( NI_element *fnel , char **clab )
845 {
846    int jj ;
847 
848 ENTRY("THD_set_tsv_column_labels") ;
849 
850    if( NI_element_type(fnel) != NI_ELEMENT_TYPE ||
851        fnel->vec_num         == 0               ||
852        clab                  == NULL              ) EXRETURN ;
853 
854    for( jj=0 ; jj < fnel->vec_num ; jj++ )
855       NI_set_column_label( fnel , jj , clab[jj] ) ;
856 
857    EXRETURN ;
858 }
859 
860 /*----------------------------------------------------------------------------*/
861 /* convert 1D or 2D MRI_IMAGE to a TSV NI_element */
862 
THD_mri_to_tsv_element(MRI_IMAGE * imin,char ** clab)863 NI_element * THD_mri_to_tsv_element( MRI_IMAGE *imin , char **clab )
864 {
865    MRI_IMAGE *qim ;
866    int nx,ny , jj ;
867    float *far ;
868    NI_element *fnel ;
869    char qlab[32] ;
870 
871 ENTRY("THD_mri_to_tsv_element") ;
872 
873    if( imin == NULL || imin->nz > 1 ) RETURN(NULL) ;
874 
875    nx = imin->nx ; ny = imin->ny ;
876    if( nx < 1 || ny < 1 )             RETURN(NULL) ;
877 
878    if( imin->kind != MRI_float ) qim = mri_to_float(imin) ;
879    else                          qim = imin ;
880 
881    far = MRI_FLOAT_PTR(qim) ;
882 
883    fnel = NI_new_data_element( "tsv" , nx ) ;
884 
885    for( jj=0 ; jj < ny ; jj++ ){
886      NI_add_column( fnel , NI_FLOAT , far+jj*nx ) ;
887      if( clab != NULL ){
888        NI_set_column_label( fnel , jj , clab[jj] ) ;
889      } else {
890        sprintf(qlab,"Col#%d",jj) ;
891        NI_set_column_label( fnel , jj , qlab ) ;
892      }
893    }
894 
895    if( qim != imin ) mri_free(qim) ;
896 
897    RETURN(fnel) ;
898 }
899 
900 /*----------------------------------------------------------------------------*/
901 /* Read a comma-separated file, and convert to a NI_element with
902    vector labels (vec_lab) and with numeric columns where possible.
903 *//*--------------------------------------------------------------------------*/
904 
THD_read_csv(char * fname)905 NI_element * THD_read_csv( char *fname )
906 {
907    NI_element *tnel , *fnel=NULL ;
908    int ii,jj , vnum,vlen , nbad ;
909    char **vec_lab , **cpt , *dpt ;
910 
911 ENTRY("THD_read_csv") ;
912 
913    /* try to read as a table of strings */
914 
915    tnel = THD_string_table_read( fname , 5 ) ;
916    if( tnel == NULL )                           RETURN(NULL) ;
917 
918    vnum = tnel->vec_num ;     /* number of columns */
919    vlen = tnel->vec_len - 1 ; /* first row is labels */
920    if( vnum < 1 && vlen < 2 )                   RETURN(NULL) ;
921 
922    /* extract labels from first string of each column vector */
923 
924 /* INFO_message("CSV data from %s - %d cols %d rows",fname,vnum,vlen) ; */
925    vec_lab = NI_malloc( char* , sizeof(char *)*vnum ) ;
926    for( ii=0 ; ii < vnum ; ii++ ){
927      cpt = (char **)tnel->vec[ii] ;
928      vec_lab[ii] = NI_strdup( cpt[0] ) ;
929 /* ININFO_message(" vec_lab[%d] = %s",ii,vec_lab[ii]) ; */
930    }
931 
932    fnel = NI_new_data_element( "csv" , vlen ) ;
933 
934 #undef  FBAD
935 #define FBAD(sss)                       \
936  ( strcasecmp ((sss),"N/A"  ) == 0 ||   \
937    strncasecmp((sss),"NAN",3) == 0 ||   \
938    strncasecmp((sss),"INF",3) == 0   )
939 
940 /* ININFO_message("---columns---")  ; */
941    for( ii=0 ; ii < vnum ; ii++ ){
942      cpt = (char **)tnel->vec[ii] ;
943      jj  = NI_count_numbers( vlen , cpt+1 ) ;
944      if( jj == vlen ){  /* pure numbers */
945        float *far = (float *)malloc(sizeof(float)*vlen) ;
946        for( jj=0 ; jj < vlen ; jj++ ){
947          far[jj] = (float)strtod(cpt[jj+1],NULL) ;
948        }
949        nbad = thd_floatscan( vlen , far ) ;
950 
951        /* repair bad things [17 Oct 2018] */
952        { int ngood=0 ; float sgood=0.0f ;
953          for( jj=0 ; jj < vlen ; jj++ ){
954            if( !FBAD(cpt[jj+1]) ){ ngood++ ; sgood += far[jj]; }
955          }
956          if( ngood < vlen ){
957            if( ngood > 0 ) sgood /= ngood ;
958            for( jj=0 ; jj < vlen ; jj++ ){
959              if( FBAD(cpt[jj+1]) ) far[jj] = sgood ;
960            }
961          }
962        }
963 
964        NI_add_column( fnel , NI_FLOAT , far ) ;
965        NI_set_column_label( fnel , ii , vec_lab[ii] ) ;
966        free(far) ;
967 /* ININFO_message(" column %s: floats with %d errors",vec_lab[ii],nbad) ; */
968      } else {           /* strings */
969        NI_add_column( fnel , NI_STRING , cpt+1 ) ;
970        NI_set_column_label( fnel , ii , vec_lab[ii] ) ;
971 /* ININFO_message(" column %s: strings",vec_lab[ii]) ; */
972      }
973    }
974 
975    NI_free_element(tnel) ; /* old stuff gets thrown out */
976 
977    /* see if we have to deal with column selectors */
978 
979    dpt = strstr(fname,"[") ;
980    if( dpt != NULL ){
981      int *ivlist ;
982      ivlist = MCW_get_labels_intlist( fnel->vec_lab , vnum , dpt ) ;
983      if( ivlist != NULL && ivlist[0] > 0 ){ /* extract subset of columns */
984        NI_element *qnel = NI_extract_columns( fnel , ivlist[0] , ivlist+1 ) ;
985        if( qnel != NULL ){ NI_free_element(fnel) ; fnel = qnel ; }
986      }
987    }
988 
989 /*   NI_write_element_tofile( "stderr:" , fnel , NI_TEXT_MODE ) ; */
990 
991 #if 0 /* debug */
992 INFO_message("=========== csv dump ==========") ;
993    THD_write_csv( "stdout:" , fnel ) ;
994 INFO_message("===============================") ;
995 #endif
996 
997    RETURN(fnel) ;
998 }
999 
1000 /*----------------------------------------------------------------------------*/
1001 
THD_write_csv(char * fname,NI_element * nel)1002 void THD_write_csv( char *fname , NI_element *nel )
1003 {
1004    char ssep[2] ; int notfirst,ii,kk ;
1005    FILE *fp ;
1006 
1007 ENTRY("THD_write_csv") ;
1008 
1009    if( fname == NULL || NI_element_type(nel) != NI_ELEMENT_TYPE ) EXRETURN ;
1010    if( nel->vec_lab == NULL || nel->vec_num == 0 )                EXRETURN ;
1011 
1012    fp = fopen_maybe(fname) ; if( fp == NULL ) EXRETURN ;
1013 
1014 #undef  SET_SEPCHAR
1015 #define SET_SEPCHAR  do{ ssep[0] = ( (notfirst++) ? ',' : '\0' ) ; } while(0)
1016    ssep[1] = '\0' ;
1017 
1018    /* header row */
1019 
1020    for( notfirst=kk=0 ; kk < nel->vec_num ; kk++ ){
1021      SET_SEPCHAR ;
1022      fprintf(fp,"%s%s",ssep,nel->vec_lab[kk]) ;
1023    }
1024    fprintf(fp,"\n") ;
1025 
1026    /* data rows */
1027 
1028    for( ii=0 ; ii < nel->vec_len ; ii++ ){
1029      for( notfirst=kk=0 ; kk < nel->vec_num ; kk++ ){
1030        SET_SEPCHAR ;
1031        if( nel->vec_typ[kk] == NI_FLOAT ){
1032          float *far = (float *)nel->vec[kk] ;
1033          fprintf(fp,"%s%g",ssep,far[ii]) ;
1034        } else if( nel->vec_typ[kk] == NI_STRING ){
1035          char **cpt = (char **)nel->vec[kk] ;
1036          fprintf(fp,"%s%s" , ssep , (cpt[ii] != NULL) ? cpt[ii] : "(null)" ) ;
1037        }
1038      }
1039      fprintf(fp,"\n") ;
1040    }
1041 
1042    fclose_maybe(fp) ;
1043    EXRETURN ;
1044 }
1045 
1046 /*----------------------------------------------------------------------------*/
1047 
THD_set_csv_column_labels(NI_element * fnel,char ** clab)1048 void THD_set_csv_column_labels( NI_element *fnel , char **clab )
1049 {
1050    int jj ;
1051 
1052 ENTRY("THD_set_csv_column_labels") ;
1053 
1054    if( NI_element_type(fnel) != NI_ELEMENT_TYPE ||
1055        fnel->vec_num         == 0               ||
1056        clab                  == NULL              ) EXRETURN ;
1057 
1058    for( jj=0 ; jj < fnel->vec_num ; jj++ )
1059       NI_set_column_label( fnel , jj , clab[jj] ) ;
1060 
1061    EXRETURN ;
1062 }
1063 
1064 /*----------------------------------------------------------------------------*/
1065 /* convert 1D or 2D MRI_IMAGE to a CSV NI_element */
1066 
THD_mri_to_csv_element(MRI_IMAGE * imin,char ** clab)1067 NI_element * THD_mri_to_csv_element( MRI_IMAGE *imin , char **clab )
1068 {
1069    MRI_IMAGE *qim ;
1070    int nx,ny , jj ;
1071    float *far ;
1072    NI_element *fnel ;
1073    char qlab[32] ;
1074 
1075 ENTRY("THD_mri_to_csv_element") ;
1076 
1077    if( imin == NULL || imin->nz > 1 ) RETURN(NULL) ;
1078 
1079    nx = imin->nx ; ny = imin->ny ;
1080    if( nx < 1 || ny < 1 )             RETURN(NULL) ;
1081 
1082    if( imin->kind != MRI_float ) qim = mri_to_float(imin) ;
1083    else                          qim = imin ;
1084 
1085    far = MRI_FLOAT_PTR(qim) ;
1086 
1087    fnel = NI_new_data_element( "csv" , nx ) ;
1088 
1089    for( jj=0 ; jj < ny ; jj++ ){
1090      NI_add_column( fnel , NI_FLOAT , far+jj*nx ) ;
1091      if( clab != NULL ){
1092        NI_set_column_label( fnel , jj , clab[jj] ) ;
1093      } else {
1094        sprintf(qlab,"Col#%d",jj) ;
1095        NI_set_column_label( fnel , jj , qlab ) ;
1096      }
1097    }
1098 
1099    if( qim != imin ) mri_free(qim) ;
1100 
1101    RETURN(fnel) ;
1102 }
1103 
1104 /*----------------------------------------------------------------------------*/
1105 /* convert NIML data element to an MRI_IMAGE;
1106    only numeric columns are included in the output;
1107    if none are found, NULL is returned.
1108 *//*--------------------------------------------------------------------------*/
1109 
THD_niml_to_mri(NI_element * nel)1110 MRI_IMAGE * THD_niml_to_mri( NI_element *nel )
1111 {
1112    int ncol , *icol , ii,jj , nx ;
1113    MRI_IMAGE *outim=NULL ;
1114    float *outar , *far ;
1115    char *comlab=NULL ;
1116 
1117 ENTRY("THD_niml_to_mri") ;
1118 
1119    if( NI_element_type(nel) != NI_ELEMENT_TYPE ) RETURN(NULL) ;
1120    if( nel->vec_num < 1 || nel->vec_len < 1 )    RETURN(NULL) ;
1121 
1122    /* find numeric columns */
1123 
1124    icol = (int *)malloc(sizeof(int)*nel->vec_num) ;
1125    for( ncol=jj=0 ; jj < nel->vec_num ; jj++ ){
1126      if( NI_IS_NUMERIC_TYPE(nel->vec_typ[jj]) ) icol[ncol++] = jj ;
1127    }
1128    if( ncol == 0 ){ free(icol) ; RETURN(NULL) ; } /* nothing */
1129 
1130    nx    = nel->vec_len ;
1131    outim = mri_new( nx , ncol , MRI_float ) ;
1132    outar = MRI_FLOAT_PTR(outim) ;
1133    for( jj=0 ; jj < ncol ; jj++ ){
1134      far = outar + jj*nx ;
1135      for( ii=0 ; ii < nx ; ii++ )
1136        far[ii] = NI_extract_float_value(nel,ii,icol[jj]) ;
1137      /* extract label */
1138      if( nel->vec_lab != NULL ){
1139        char *ccc = nel->vec_lab[icol[jj]] ;
1140        if( ccc == NULL || *ccc == '\0' ) ccc = "Fred" ;
1141        if( comlab == NULL ){
1142          comlab = (char *)calloc(16,sizeof(char)) ;
1143          strcpy(comlab,"LABELS:\t") ;
1144        }
1145        comlab = (char *)realloc( comlab , sizeof(char)*(strlen(comlab)+strlen(ccc)+4) ) ;
1146        if( jj > 0 ) strcat(comlab,"\t") ;
1147        strcat(comlab,ccc) ;
1148      }
1149    }
1150 
1151    free(icol) ;
1152    if( comlab != NULL ) outim->comments = comlab ;
1153    RETURN(outim) ;
1154 }
1155