1 #include <stdio.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include "gifti_io.h"
5 
6 #define GXML_MIN_BSIZE 2048
7 #define GXML_DEF_BSIZE 32768
8 
9 /* local prototypes */
10 static int  append_to_cdata     (gxml_data *, const char *, int);
11 static int  append_to_data      (gxml_data *, const char *, int);
12 static int  append_to_data_ascii(gxml_data *, const char *, int);
13 static int  append_to_data_b64  (gxml_data *, char*,long long,const char*, int);
14 /*
15 static int  append_to_data_b64gz(gxml_data *, const char *, int);
16 */
17 
18 static int  add_label_rgba      (gxml_data *, giiLabelTable *, float *);
19 static int  append_to_xform     (gxml_data *, const char *, int);
20 static int  apply_da_list_order (gxml_data *, const int *, int);
21 static int  int_compare         (const void * v0, const void * v1);
22 static int  copy_b64_data       (gxml_data *, const char *, char *, int, int*);
23 static int  decode_ascii       (gxml_data*,char*,int,int,void*,long long*,int*);
24 static int  decode_b64          (gxml_data*, char*, int, char *, long long *);
25 static int  disp_gxml_data      (char *, gxml_data *, int);
26 static int  ename2type          (const char *);
27 static int  epush               (gxml_data *, int, const char *, const char **);
28 static int  epop                (gxml_data *, int, const char *);
29 static int  free_xd_data        (gxml_data *);
30 static int  get_label_attrs     (gxml_data *, const char **, int *, float *);
31 static int  init_gxml_data      (gxml_data *, int, const int *, int);
32 static int  partial_buf_size    (long long);
33 
34 static int  push_gifti          (gxml_data *, const char **);
35 static int  push_meta           (gxml_data *);
36 static int  push_md             (gxml_data *);
37 static int  push_name           (gxml_data *);
38 static int  push_value          (gxml_data *);
39 static int  push_LT             (gxml_data *);
40 static int  push_label          (gxml_data *, const char **);
41 static int  push_darray         (gxml_data *, const char **);
42 static int  push_cstm           (gxml_data *);
43 static int  push_data           (gxml_data *);
44 static int  push_dspace         (gxml_data *);
45 static int  push_xspace         (gxml_data *);
46 static int  push_xform          (gxml_data *);
47 static int  reset_xml_buf       (gxml_data *, char **, int *);
48 
49 static void show_attrs          (gxml_data *,int,const char **);
50 static void show_depth          (int, int, FILE *);
51 static void show_enames         (FILE *);
52 static int  show_stack          (char *, gxml_data *);
53 
54 static int  short_sorted_da_list(gxml_data *dp, const int * dalist, int len);
55 static int  stack_is_valid      (gxml_data *);
56 static int  whitespace_len      (const char *, int);
57 static int  update_xml_buf_size (gxml_data *, long long);
58 static int  update_partial_buffer(char **, int *, long long, int);
59 
60 static int  count_bad_b64_chars (const char *, int);
61 static int  show_bad_b64_chars  (const char *, int);
62 
63 static giiMetaData * find_current_MetaData(gxml_data *, int);
64 
65 #ifndef XMLCALL
66 /* XMLCALL was added to expat in version 1.95.7 to define a calling convention,
67  * as cdecl
68  */
69 #if defined(XML_USE_MSC_EXTENSIONS)
70 #define XMLCALL __cdecl
71 #elif defined(__GNUC__) && defined(__i386)
72 #define XMLCALL __attribute__((cdecl))
73 #else
74 #define XMLCALL
75 #endif
76 #endif /* not defined XMLCALL */
77 
78 #ifndef XML_STATUS_ERROR
79 #define XML_STATUS_ERROR 0
80 #endif
81 #ifndef XML_STATUS_OK
82 #define XML_STATUS_OK 1
83 #endif
84 
85 static void XMLCALL cb_start_ele    (void *, const char *, const char **);
86 static void XMLCALL cb_end_ele      (void *, const char *);
87 static void XMLCALL cb_char         (void *, const char *, int);
88 static void XMLCALL cb_instr        (void *, const char *, const char *);
89 static void XMLCALL cb_comment      (void *, const char *);
90 static void XMLCALL cb_cdata_start  (void *);
91 static void XMLCALL cb_cdata_end    (void *);
92 static void XMLCALL cb_default      (void *, const char *, int);
93 static void XMLCALL cb_xml_dec      (void *, const char *, const char * , int);
94 static void XMLCALL cb_start_doctype(void *, const char *, const char *,
95                                      const char *, int);
96 static void XMLCALL cb_end_doctype  (void *);
97 static void XMLCALL cb_elem_dec     (void *, const char *, XML_Content *);
98 static XML_Parser init_xml_parser   (void *);
99 
100 /* writing functions */
101 static int  gxml_write_gifti        (gxml_data *, FILE *);
102 static int  gxml_write_preamble     (FILE *);
103 
104 static int  ewrite_text_ele         (int, const char *, const char *,
105                                      int, int, FILE *);
106 static int  ewrite_coordsys         (gxml_data *, giiCoordSystem *, FILE *);
107 static int  ewrite_data             (gxml_data *, giiDataArray *, FILE *);
108 static int  ewrite_data_line        (void *, int, long long, long long,
109                                      int, FILE *);
110 static int  ewrite_double_line      (double *, int, int, FILE *);
111 static int  ewrite_int_attr         (const char *, int, int, int, FILE *);
112 static int  ewrite_long_long_attr   (const char *, long long, int, int, FILE *);
113 static int  ewrite_str_attr         (const char*, const char*, int, int, FILE*);
114 static int  ewrite_darray           (gxml_data *, giiDataArray *, FILE *);
115 static int  ewrite_ex_atrs          (gxml_data *, nvpairs *, int, int, FILE *);
116 static int  ewrite_LT               (gxml_data*, giiLabelTable*, int, FILE*);
117 static int  ewrite_meta             (gxml_data *, giiMetaData *, FILE *);
118 
119 static int  gxml_disp_b64_data      (const char *, const void *, int, FILE *);
120 
121 /* these should match GXML_ETYPE_* defines */
122 static char * enames[GXML_MAX_ELEN] = {
123     "Invalid", "GIFTI", "MetaData", "MD", "Name", "Value", "LabelTable",
124     "Label", "DataArray", "CoordinateSystemTransformMatrix", "Data",
125     "DataSpace", "TransformedSpace", "MatrixData", "CDATA"
126 };
127 
128 /* ---------------------------------------------------------------------- */
129 /* GIFTI XML global struct and access functions */
130 static gxml_data GXD = {
131     1,          /* verb, default to 1 (0 means quiet)         */
132     1,          /* dstore, flag whether to store data         */
133     3,          /* indent, spaces per indent level            */
134     GXML_DEF_BSIZE, /* buf_size, allocated for XML parsing    */
135     GIFTI_B64_CHECK_SKIPNCOUNT, /* b64_check, for b64 errors  */
136     1,          /* assume it is okay to update metadata       */
137     GZ_DEFAULT_COMPRESSION, /* zlevel, compress level, -1..9  */
138     1,          /* perm_by_iord: permute by index order       */
139 
140     NULL,       /* da_list, list of DA indices to store       */
141     0,          /* da_len, length of da_list                  */
142     0,          /* da_ind, current index into da_list         */
143 
144     0,          /* eleDA, number of DA elements found         */
145     0,          /* expDA, number of DA elements expected      */
146     0,          /* b64_errors, number of errors found         */
147     0,          /* errors, number of encountered errors       */
148     0,          /* skip depth (at positive depth, 0 is clear) */
149     0,          /* depth, current stack depth                 */
150     {0},        /* stack, ints, max depth GXML_MAX_DEPTH      */
151 
152     0,          /* dind, index into decoded data array        */
153     0,          /* clen, length of current CDATA string       */
154     0,          /* xlen, length of current xform buffer       */
155     0,          /* dlen, length of current Data buffer        */
156     0,          /* doff, offset into current data buffer      */
157     0,          /* zlen, length of compression buffer         */
158     NULL,       /* cdata, CDATA char pointer                  */
159     NULL,       /* xdata, xform buffer pointer                */
160     NULL,       /* ddata, Data buffer pointer                 */
161     NULL,       /* zdata, compression buffer pointer          */
162     NULL        /* gim, gifti_image *, for results            */
163 };
164 
165 #ifndef HAVE_ZLIB  /* so we can print a callback message once per file */
166     static int g_first_zlib_err_msg = 1;
167 #endif
168 
169 /*--- Base64 binary encoding and decoding tables ---*/
170 
171 /* encoding: converting values 0-63 to characters */
172 static unsigned char b64_encode_table[64] = {
173     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',             /* 26 upper case */
174     'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
175     'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
176     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',             /* 26 lower case */
177     'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
178     'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
179     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',   /* 10 digits */
180     '+', '/'
181 };
182 
183 /* decoding: converting characters A-Z, a-z, 0-9, +, /, to integers 0-63
184  * (other characters are considered invalid, though '=' is mapped to 0,
185  * to account for the end of input (didn't Arnold make a movie about that?))
186  */
187 static unsigned char b64_decode_table[256] = {
188     128, 128, 128, 128, 128, 128, 128, 128,   /*   0 -   7 */
189     128, 128, 128, 128, 128, 128, 128, 128,   /*   8 -  15 */
190     128, 128, 128, 128, 128, 128, 128, 128,   /*  16 -  23 */
191     128, 128, 128, 128, 128, 128, 128, 128,   /*  24 -  31 */
192     128, 128, 128, 128, 128, 128, 128, 128,   /*  32 -  39 */
193 
194     /* d['+'] = d[43] = 62,   d['/'] = d[47] = 63          */
195     128, 128, 128,  62, 128, 128, 128,  63,   /*  40 -  47 */
196 
197     /* d['0'] = d[48] = 52,  ...   d['9'] = 61, d['='] = 0 */
198      52,  53,  54,  55,  56,  57,  58,  59,   /*  48 -  55 */
199      60,  61, 128, 128, 128,   0, 128, 128,   /*  56 -  63 */
200 
201     /* d['A'] = d[65] =  0,   ...   d['Z'] = 25            */
202     128,   0,   1,   2,   3,   4,   5,   6,   /*  64 -  71 */
203       7,   8,   9,  10,  11,  12,  13,  14,   /*  72 -  79 */
204      15,  16,  17,  18,  19,  20,  21,  22,   /*  80 -  87 */
205      23,  24,  25, 128, 128, 128, 128, 128,   /*  88 -  95 */
206 
207     /* d['a'] = d[97] = 26,   ...   d['a'] = 51            */
208     128,  26,  27,  28,  29,  30,  31,  32,   /*  96 - 103 */
209      33,  34,  35,  36,  37,  38,  39,  40,   /* 104 - 111 */
210      41,  42,  43,  44,  45,  46,  47,  48,   /* 112 - 119 */
211      49,  50,  51, 128, 128, 128, 128, 128,   /* 120 - 127 */
212 
213     /* ... and the rest, are heeere in deecode liiiiiist!  poor Mary Ann :( */
214     128, 128, 128, 128, 128, 128, 128, 128,   /* 128 - 135 */
215     128, 128, 128, 128, 128, 128, 128, 128,   /* 136 - 143 */
216     128, 128, 128, 128, 128, 128, 128, 128,   /* 144 - 151 */
217     128, 128, 128, 128, 128, 128, 128, 128,   /* 152 - 159 */
218     128, 128, 128, 128, 128, 128, 128, 128,   /* 160 - 167 */
219     128, 128, 128, 128, 128, 128, 128, 128,   /* 168 - 175 */
220     128, 128, 128, 128, 128, 128, 128, 128,   /* 176 - 183 */
221     128, 128, 128, 128, 128, 128, 128, 128,   /* 184 - 191 */
222     128, 128, 128, 128, 128, 128, 128, 128,   /* 192 - 199 */
223     128, 128, 128, 128, 128, 128, 128, 128,   /* 200 - 207 */
224     128, 128, 128, 128, 128, 128, 128, 128,   /* 208 - 215 */
225     128, 128, 128, 128, 128, 128, 128, 128,   /* 216 - 223 */
226     128, 128, 128, 128, 128, 128, 128, 128,   /* 224 - 231 */
227     128, 128, 128, 128, 128, 128, 128, 128,   /* 232 - 239 */
228     128, 128, 128, 128, 128, 128, 128, 128,   /* 240 - 247 */
229     128, 128, 128, 128, 128, 128, 128, 128    /* 248 - 255 */
230 };
231 
232 /* note: the buffer needs to be large enough to contain any contiguous
233          piece of (CDATA?) text, o.w. it will require parsing in pieces */
gxml_read_image(const char * fname,int read_data,const int * dalist,int dalen)234 gifti_image * gxml_read_image(const char * fname, int read_data,
235                              const int * dalist, int dalen)
236 {
237     gxml_data  * xd = &GXD;     /* point to global struct */
238     XML_Parser   parser;
239     unsigned     blen;
240     FILE       * fp;
241     char       * buf = NULL;
242     int          bsize;    /* be sure it doesn't change at some point */
243     int          done = 0, pcount = 1;
244 
245     if( init_gxml_data(xd, 0, dalist, dalen) ) /* reset non-user variables */
246         return NULL;
247 
248     xd->dstore = read_data;  /* store for global access */
249 
250     if( !fname ) {
251         fprintf(stderr,"** gxml_read_image: missing filename\n");
252         return NULL;
253     }
254 
255     fp = fopen(fname, "r");
256     if( !fp ) {
257         fprintf(stderr,"** failed to open GIFTI XML file '%s'\n", fname);
258         return NULL;
259     }
260 
261     /* create a new buffer */
262     bsize = 0;
263     if( reset_xml_buf(xd, &buf, &bsize) ) { fclose(fp); return NULL; }
264 
265     if(xd->verb > 1) {
266         fprintf(stderr,"-- reading gifti image '%s'\n", fname);
267         if(xd->da_list) fprintf(stderr,"   (length %d DA list)\n", xd->da_len);
268         fprintf(stderr,"-- using %d byte XML buffer\n",bsize);
269         if(xd->verb > 4) show_enames(stderr);
270     }
271 
272     /* allocate return structure */
273     xd->gim = (gifti_image *)calloc(1,sizeof(gifti_image));
274     if( !xd->gim ) {
275         fprintf(stderr,"** failed to alloc initial gifti_image\n");
276         free(buf);
277         return NULL;
278     }
279 
280     /* create parser, init handlers */
281     parser = init_xml_parser((void *)xd);
282 
283     while( !done )
284     {
285         if( reset_xml_buf(xd, &buf, &bsize) )  /* fail out */
286             { gifti_free_image(xd->gim); xd->gim = NULL; break; }
287 
288         blen = fread(buf, 1, bsize, fp);
289         done = blen < bsize;
290 
291         if(xd->verb > 3) fprintf(stderr,"-- XML_Parse # %d\n", pcount);
292         pcount++;
293         if( XML_Parse(parser, buf, blen, done) == XML_STATUS_ERROR) {
294             fprintf(stderr,"** %s at line %u\n",
295                     XML_ErrorString(XML_GetErrorCode(parser)),
296                     (unsigned int)XML_GetCurrentLineNumber(parser));
297             gifti_free_image(xd->gim);
298             xd->gim = NULL;
299             break;
300         }
301     }
302 
303     if(xd->verb > 1) {
304         if(xd->gim)
305             fprintf(stderr,"-- have gifti image '%s', "
306                            "(%d DA elements = %lld MB)\n",
307                     fname, xd->gim->numDA, gifti_gim_DA_size(xd->gim,1));
308         else fprintf(stderr,"** gifti image '%s', failure\n", fname);
309     }
310 
311     fclose(fp);
312     if( buf ) free(buf);        /* parser buffer */
313     XML_ParserFree(parser);
314 
315     if( dalist && xd->da_list )
316         if( apply_da_list_order(xd, dalist, dalen) ) {
317             fprintf(stderr,"** failed apply_da_list_order\n");
318             gifti_free_image(xd->gim);
319             xd->gim = NULL;
320         }
321 
322     free_xd_data(xd);  /* free data buffers */
323 
324     /* if auto-permute, convert to row major order (appropriate for C) */
325     if( xd->perm_by_iord && read_data ) {
326       if( gifti_convert_ind_ord(xd->gim, GIFTI_IND_ORD_ROW_MAJOR) > 0 )
327         if( xd->verb > 0 )
328           fprintf(stderr,"++ converted data to row major order: %s\n",fname);
329     }
330 
331     return xd->gim;
332 }
333 
334 
335 /* note: the buffer needs to be large enough to contain any contiguous
336          piece of (CDATA?) text, o.w. it will require parsing in pieces */
gxml_read_image_buf(const char * buf_in,long long bin_len,const int * dalist,int dalen)337 gifti_image * gxml_read_image_buf(const char * buf_in, long long bin_len,
338                                   const int * dalist, int dalen)
339 {
340     gxml_data  * xd = &GXD;     /* point to global struct */
341     XML_Parser   parser;
342     long long    bin_remain = bin_len;  /* remaining bytes to process */
343     const char * bin_ptr = buf_in;      /* current buffer location */
344     const char * fname = "FROM_BUFFER";
345     unsigned     blen;
346     char       * buf = NULL;
347     int          bsize;    /* be sure it doesn't change at some point */
348     int          done = 0, pcount = 1;
349 
350     if( init_gxml_data(xd, 0, dalist, dalen) ) /* reset non-user variables */
351         return NULL;
352 
353     xd->dstore = 1;  /* store for global access */
354 
355     if( !buf_in || bin_len < 0 ) {
356         fprintf(stderr,"** gxml_read_image_buf: missing buffer\n");
357         return NULL;
358     }
359 
360     /* create a new buffer */
361     bsize = 0;
362     if( reset_xml_buf(xd, &buf, &bsize) ) return NULL;
363 
364     if(xd->verb > 1) {
365         fprintf(stderr,"-- reading gifti image '%s'\n", fname);
366         if(xd->da_list) fprintf(stderr,"   (length %d DA list)\n", xd->da_len);
367         fprintf(stderr,"-- using %d byte XML buffer\n",bsize);
368         if(xd->verb > 4) show_enames(stderr);
369     }
370 
371     /* allocate return structure */
372     xd->gim = (gifti_image *)calloc(1,sizeof(gifti_image));
373     if( !xd->gim ) {
374         fprintf(stderr,"** failed to alloc initial gifti_image\n");
375         free(buf);
376         return NULL;
377     }
378 
379     /* create parser, init handlers */
380     parser = init_xml_parser((void *)xd);
381 
382     while( !done )
383     {
384         if( reset_xml_buf(xd, &buf, &bsize) )  /* fail out */
385             { gifti_free_image(xd->gim); xd->gim = NULL; break; }
386 
387         /*--- replace fread with buffer copy ---*/
388 
389         /* decide how much to copy and copy */
390         if( bin_remain >= bsize ) blen = bsize;
391         else                      blen = bin_remain;
392 
393         memcpy(buf, bin_ptr, bsize);
394 
395         /* update bytes remaining to process and decide if done */
396         bin_remain -= bsize;
397         done = bin_remain <= 0;
398 
399         if(xd->verb > 3) fprintf(stderr,"-- XML_Parse # %d\n", pcount);
400         pcount++;
401         if( XML_Parse(parser, buf, blen, done) == XML_STATUS_ERROR) {
402             fprintf(stderr,"** %s at line %u\n",
403                     XML_ErrorString(XML_GetErrorCode(parser)),
404                     (unsigned int)XML_GetCurrentLineNumber(parser));
405             gifti_free_image(xd->gim);
406             xd->gim = NULL;
407             break;
408         }
409     }
410 
411     if(xd->verb > 1) {
412         if(xd->gim)
413             fprintf(stderr,"-- have gifti image '%s', "
414                            "(%d DA elements = %lld MB)\n",
415                     fname, xd->gim->numDA, gifti_gim_DA_size(xd->gim,1));
416         else fprintf(stderr,"** gifti image '%s', failure\n", fname);
417     }
418 
419     if( buf ) free(buf);        /* parser buffer */
420     XML_ParserFree(parser);
421 
422     if( dalist && xd->da_list )
423         if( apply_da_list_order(xd, dalist, dalen) ) {
424             fprintf(stderr,"** failed apply_da_list_order\n");
425             gifti_free_image(xd->gim);
426             xd->gim = NULL;
427         }
428 
429     free_xd_data(xd);  /* free data buffers */
430 
431     return xd->gim;
432 }
433 
434 
435 /* free da_list and buffers */
free_xd_data(gxml_data * xd)436 static int free_xd_data(gxml_data * xd)
437 {
438     if( xd->da_list ){ free(xd->da_list); xd->da_list = NULL; }
439 
440     if( xd->xdata ){ free(xd->xdata); xd->xdata = NULL; } /* xform matrix  */
441     if( xd->zdata) { free(xd->zdata); xd->zdata = NULL; } /* compress buff */
442     if( xd->ddata ){ free(xd->ddata); xd->ddata = NULL; } /* Data buffer   */
443 
444     return 0;
445 }
446 
447 
448 /* verify that the current lengths match
449  * create a new list of NULL DA pointers
450  * create a 'taken' list, matching xd->da_len (but unset)
451  * for each index, if it is not set, find one and copy
452  */
apply_da_list_order(gxml_data * xd,const int * orig,int len)453 static int apply_da_list_order(gxml_data * xd, const int * orig, int len)
454 {
455     giiDataArray ** newlist;
456     int           * taken;      /* whether xd->da_list[i] was used */
457     int             newc, oldc, errors = 0, nDA;
458 
459     if( !xd || !xd->gim || !orig || len <= 0 || !xd->da_list || xd->da_len<=0 )
460         return 0;  /* do nothing, or start small, diversionary fire, hmmm... */
461 
462     nDA = xd->gim->numDA;       /* this may replace da_len */
463     if( nDA <= 0 ) return 0;
464 
465     /* create new DA list */
466     newlist = (giiDataArray **)malloc(len * sizeof(giiDataArray *));
467     if(!newlist){ fprintf(stderr,"** ADLO: no alloc for DAlist\n"); return 1; }
468 
469     /* create taken list (of all 0) */
470     taken = (int *)calloc(nDA, sizeof(int));
471     if(!taken){ fprintf(stderr,"** ADLO: no alloc for taken\n"); return 1; }
472 
473     /* insert pointers to current or copied DA */
474     for( newc = 0; newc < len; newc++ ) {
475         newlist[newc] = NULL;      /* in case there are failures */
476 
477         /* find old index in da_list */
478         for( oldc = 0; oldc < nDA; oldc++ )
479             if( xd->da_list[oldc] == orig[newc] )
480                 break;
481         if( oldc >= nDA ) {      /* should never happen */
482             fprintf(stderr,"** ADLO: failed to find index %d in da_list\n",
483                     orig[newc]);
484             errors++;
485             continue;
486         } else if ( xd->verb > 3 )
487             fprintf(stderr,"++ found orig index %d at sorted list %d\n",
488                     orig[newc], oldc);
489 
490         /* either steal the pointer or copy the DA */
491         if( !taken[oldc] ) {
492             taken[oldc] = 1;    /* mark it as taken */
493             newlist[newc] = xd->gim->darray[oldc];
494         } else { /* we must make a new copy */
495             newlist[newc] = gifti_copy_DataArray( xd->gim->darray[oldc], 1);
496             if( ! newlist[newc] ) errors++;
497         }
498     }
499 
500     /* at this point, we should be good, but check...  */
501 
502     /* if anything has not been taken, free them and fail */
503     for( oldc = 0; oldc < nDA; oldc++ ) {
504         if( !taken[oldc] ) {
505             fprintf(stderr,"** ADLO: taken list is not all set\n");
506             errors++;
507             free(xd->gim->darray[oldc]);
508         }
509     }
510     free(taken);        /* we're done with that */
511 
512     /* free old DA list, since all the pointers are free'd or taken */
513     free(xd->gim->darray);
514     xd->gim->darray = newlist;
515     xd->gim->numDA  = len;
516 
517     /* last check for errors, is the current darray filled? */
518     for( newc = 0; newc < len; newc++ )
519         if( ! xd->gim->darray[newc] ) {
520             fprintf(stderr,"** ADLO: copied darray not full\n");
521             errors++;
522         }
523 
524     return errors;      /* we just need to free gim on return */
525 }
526 
527 
528 /* return 0 on success */
gxml_write_image(gifti_image * gim,const char * fname,int write_data)529 int gxml_write_image(gifti_image * gim, const char * fname, int write_data)
530 {
531     gxml_data * xd = &GXD;     /* point to global struct */
532     FILE      * fp;
533 
534     if( !gim ) {
535         fprintf(stderr,"** GXML write: no gifti_image\n");
536         return 1;
537     } else if ( !fname ) {
538         fprintf(stderr,"** GXML write: no filename\n");
539         return 1;
540     }
541 
542     if(GXD.verb > 1) {
543         fprintf(stderr,"++ writing gifti image (%s data) to '%s'",
544                 write_data?"with":"no", fname);
545         if( write_data )
546             fprintf(stderr," (%d DA elements = %lld MB)",
547                     gim->numDA, gifti_gim_DA_size(gim,1));
548         fputc('\n', stderr);
549     }
550 
551 #ifndef HAVE_ZLIB  /* if the data is to be compressed, fail */
552     if( gim->darray && gim->darray[0] &&
553                        gim->darray[0]->encoding == GIFTI_ENCODING_B64GZ ) {
554         fprintf(stderr,"** no ZLIB for compression...\n");
555         gifti_set_atr_in_DAs(gim, "Encoding", "Base64Binary", NULL, 0);
556     }
557 #endif
558 
559     init_gxml_data(xd, 0, NULL, 0);    /* reset non-user variables */
560     xd->dstore = write_data;  /* store for global access */
561     xd->gim = gim;
562 
563     /* note the library version */
564     if( xd->update_ok ) {
565         if( xd->verb > 2 )
566             fprintf(stderr,"++ setting GIFTI MD: gifticlib-version to %s\n",
567                     gifticlib_version());
568         gifti_add_to_meta(&gim->meta,"gifticlib-version",gifticlib_version(),1);
569     }
570 
571     fp = fopen(fname, "w");
572     if( !fp ) {
573         fprintf(stderr,"** failed to open '%s' for gifti write\n", fname);
574         return 1;
575     }
576 
577     (void)gxml_write_gifti(xd, fp);
578 
579     if( xd->xdata ){ free(xd->xdata);  xd->xdata = NULL; }
580     if( xd->zdata) { free(xd->zdata);  xd->zdata = NULL; }
581 
582     fclose(fp);
583 
584     return 0;
585 }
586 
587 
588 /*---------- accessor functions for user-controllable variables ----------*/
589 /*----------     (one can pass -1 to set the default value)     ----------*/
590 
591 /*! verb is the vebose level, with 0 meaning "only report errors" (up to 7) */
gxml_get_verb(void)592 int gxml_get_verb( void    ){ return GXD.verb; }
gxml_set_verb(int val)593 int gxml_set_verb( int val )
594 {
595     if     ( val == -1 ) GXD.verb = 1;
596     else if( val >=  0 ) GXD.verb = val;
597     return 0;
598 }
599 
600 /*! the dstore flag controls whether data is read from a GIFTI file */
gxml_get_dstore(void)601 int gxml_get_dstore( void    ){ return GXD.dstore; }
gxml_set_dstore(int val)602 int gxml_set_dstore( int val )
603 {
604     if( val ) GXD.dstore = 1;   /* includes -1 as applying the default */
605     else      GXD.dstore = 0;
606     return 0;
607 }
608 
609 /*! indent is the number of spaces per indent level written to a GIFTI file */
gxml_get_indent(void)610 int gxml_get_indent( void    ){ return GXD.indent; }
gxml_set_indent(int val)611 int gxml_set_indent( int val )
612 {
613     if      ( val == -1 ) GXD.indent = 3;
614     else if ( val >=  0 ) GXD.indent = val;
615     else return 1;  /* failure - no action */
616     return 0;
617 }
618 
619 /*! buf_size is the size of the XML I/O buffer given to expat */
gxml_get_buf_size(void)620 int gxml_get_buf_size( void    ){ return GXD.buf_size; }
gxml_set_buf_size(int val)621 int gxml_set_buf_size( int val )
622 {
623     if      ( val == -1 ) GXD.buf_size = GXML_DEF_BSIZE;
624     else if ( val  >  0 ) GXD.buf_size = val;
625     else return 1;      /* failure - no action */
626     return 0;
627 }
628 
629 /*! b64_check is the checking level for base64 binary data (see gifti_io.h) */
gxml_get_b64_check(void)630 int gxml_get_b64_check( void    ){ return GXD.b64_check; }
gxml_set_b64_check(int val)631 int gxml_set_b64_check( int val ) /* -1 means apply default */
632 {
633     if( val == -1 )
634         GXD.b64_check = GIFTI_B64_CHECK_SKIPNCOUNT;
635     else if ( val > GIFTI_B64_CHECK_UNDEF && val <= GIFTI_B64_CHECK_MAX )
636         GXD.b64_check = val;
637     else return 1;      /* failure - no action */
638     return 0;
639 }
640 
641 /*! update_ok tells the library whether it is allowed to modify metadata */
gxml_get_update_ok(void)642 int gxml_get_update_ok( void    ){ return GXD.update_ok; }
gxml_set_update_ok(int val)643 int gxml_set_update_ok( int val )
644 {
645     if      ( val == -1 ) GXD.update_ok = 1;    /* default is yes */
646     else if ( val >=  0 ) GXD.update_ok = val;
647     else return 1;  /* failure - no action */
648     return 0;
649 }
650 
651 /*! zlevel is the zlib compression level, with -1 implying the default
652     (of 6, I believe) and value levels being in {0..9} (with 0 meaning
653     no compression) */
gxml_get_zlevel(void)654 int gxml_get_zlevel( void    ){ return GXD.zlevel; }
gxml_set_zlevel(int val)655 int gxml_set_zlevel( int val )
656 {
657     if( val == -1 )
658         GXD.zlevel = GZ_DEFAULT_COMPRESSION;
659     else if ( val >= 0 && val <= 9 )
660         GXD.zlevel = val;
661     else return 1;      /* failure - no action */
662     return 0;
663 }
664 
665 /*! perm_by_iord specifies whether to permute the data to row major
666     index order (since this is C) upon read
667     (-1 is used to initialize to the default)      28 Apr 2017 [rickr] */
gxml_get_perm_by_iord(void)668 int gxml_get_perm_by_iord( void    ){ return GXD.perm_by_iord; }
gxml_set_perm_by_iord(int val)669 int gxml_set_perm_by_iord( int val )
670 {
671     if( val == -1 )
672         GXD.perm_by_iord = 1;
673     else if ( val >= 0 && val <= 1 )
674         GXD.perm_by_iord = val;
675     else return 1;      /* failure - no action */
676     return 0;
677 }
678 /*----------------------- END accessor functions -----------------------*/
679 
680 
init_gxml_data(gxml_data * dp,int doall,const int * dalist,int len)681 static int init_gxml_data(gxml_data *dp, int doall, const int *dalist, int len)
682 {
683     int errs = 0;
684 
685     if( doall ) {           /* user modifiable - init all to defaults */
686         dp->verb      = 1;
687         dp->dstore    = 1;
688         dp->indent    = 3;
689         dp->buf_size  = GXML_DEF_BSIZE;
690         dp->b64_check = GIFTI_B64_CHECK_SKIPNCOUNT;
691         dp->update_ok = 1;
692         dp->zlevel    = GZ_DEFAULT_COMPRESSION;
693         dp->perm_by_iord = 1;
694     }
695 
696     if( dalist && len > 0 ) {
697         if( short_sorted_da_list(dp, dalist, len) ) errs++;  /* continue */
698     } else {
699         dp->da_list = NULL;
700         dp->da_len  = 0;
701     }
702     dp->da_ind  = 0;
703 
704     /* maybe show the user */
705     if( dp->verb > 2 )
706         disp_gxml_data("-- user opts: ", dp, dp->verb > 3);
707 
708     dp->eleDA = 0;
709     dp->expDA = 0;
710     dp->b64_errors = 0;
711     dp->errors = 0;
712     dp->skip = 0;
713     dp->depth = 0;
714     memset(dp->stack, 0, sizeof(dp->stack));
715 
716     dp->dind = 0;
717     dp->clen = 0;
718     dp->xlen = 0;
719     dp->dlen = 0;
720     dp->doff = 0;
721     dp->zlen = 0;
722     dp->cdata = NULL;
723     dp->xdata = NULL;
724     dp->ddata = NULL;
725     dp->zdata = NULL;
726     dp->gim   = NULL;
727 
728 #ifndef HAVE_ZLIB  /* if we don't have this (and need it), print warnings */
729     g_first_zlib_err_msg = 1;
730 #endif
731 
732     return errs;
733 }
734 
short_sorted_da_list(gxml_data * dp,const int * dalist,int len)735 static int short_sorted_da_list(gxml_data *dp, const int * dalist, int len)
736 {
737     int * da_copy, c, cind;
738 
739     /* first, duplicate list */
740     da_copy = (int *)malloc(len*sizeof(int));
741     if( !da_copy ) {
742         fprintf(stderr,"** cannot duplicate da_list of %d elements\n", len);
743         return 1;
744     }
745     for( c = 0; c < len; c++ ) da_copy[c] = dalist[c];
746 
747     /* now sort */
748     qsort(da_copy, len, sizeof(int), int_compare);
749 
750     /* remove duplicates */
751     for( c = 1, cind = 0; c < len; c++ ) {
752         if( da_copy[c] != da_copy[cind] ) {
753             cind++;
754             if( cind < c ) da_copy[cind] = da_copy[c];
755         }
756     }
757 
758     dp->da_list = da_copy;
759     dp->da_len = cind+1;
760 
761     if( dp->verb > 1 ) {
762         fprintf(stderr,"-- original da_list:");
763         for(c = 0; c < len; c++ )
764             fprintf(stderr," %d", dalist[c]);
765         fputc('\n', stderr);
766         fprintf(stderr,"++ unique, sorted da_list:");
767         for(c = 0; c < dp->da_len; c++ )
768             fprintf(stderr," %d", dp->da_list[c]);
769         fputc('\n', stderr);
770     }
771 
772     return 0;
773 }
774 
775 /* for qsort */
int_compare(const void * v0,const void * v1)776 static int int_compare(const void * v0, const void * v1)
777 {
778     int * i0 = (int *)v0;
779     int * i1 = (int *)v1;
780 
781     if( *i0  < *i1 ) return -1;
782     if( *i0 == *i1 ) return  0;
783     return 1;
784 }
785 
786 /* ---------------------------------------------------------------------- */
787 
show_depth(int depth,int show,FILE * fp)788 static void show_depth( int depth, int show, FILE * fp )
789 {
790     if( show ) fprintf(fp, "%*s %02d ", 3*depth, "", depth);
791     else       fprintf(fp, "%*s    ", 3*depth, "");
792 }
793 
show_enames(FILE * fp)794 static void show_enames( FILE * fp )
795 {
796     int c;
797     fprintf(fp, "-------------------------------\n"
798                 "++ ename list :\n");
799     for( c = 0; c <= GXML_ETYPE_LAST; c++ )
800         fprintf(fp,"    %02d : %s\n", c, enames[c]);
801     fprintf(fp, "-------------------------------\n");
802 }
803 
ename2type(const char * name)804 static int ename2type( const char * name )
805 {
806     int etype;
807     for( etype = GXML_ETYPE_LAST; etype > GXML_ETYPE_INVALID; etype-- )
808         if( !strcmp(name, enames[etype]) )
809             break;
810     return etype;
811 }
812 
813 /* name should be null terminated */
epush(gxml_data * xd,int etype,const char * ename,const char ** attr)814 static int epush( gxml_data * xd, int etype, const char * ename,
815                                                   const char ** attr )
816 {
817     if( xd->depth < 0 || xd->depth > GXML_MAX_DEPTH ) {
818         fprintf(stderr,"** push: stack depth %d out of [0,%d] range\n",
819                 xd->depth, GXML_MAX_DEPTH);
820         xd->errors++;
821         return 1;
822     }
823 
824     if( xd->verb > 4 ) {       /* maybe we want to print something */
825         show_depth(xd->depth, 1, stderr);
826         fprintf(stderr,"++ push %02d: '%s'\n", etype, enames[etype]);
827     }
828 
829     xd->stack[xd->depth] = etype;
830     xd->depth++;
831 
832     /* if we are in a skip block, do nothing but monitor stack */
833     if( xd->skip ) {
834         if( xd->verb > 2 )
835             fprintf(stderr,"-- skip=%d, depth=%d, skipping element '%s'\n",
836                     xd->skip, xd->depth, ename);
837         return 0;
838     }
839 
840     /* determine whether we should enter a skip block */
841     if( etype == GXML_ETYPE_INVALID ) {
842         if(xd->verb > 0)
843             fprintf(stderr,"** pushed invalid element, '%s', skip depth %d\n",
844                     ename, xd->depth);
845         xd->skip = xd->depth;
846         return 1;
847     }
848 
849     if ( xd->verb > 5 ) show_stack("++ ", xd);
850     if( !stack_is_valid(xd) ) return 1;
851 
852     /* call appropriate XML processing function */
853     switch( etype ) {
854         case GXML_ETYPE_GIFTI      : return push_gifti (xd, attr);
855         case GXML_ETYPE_META       : return push_meta  (xd);
856         case GXML_ETYPE_MD         : return push_md    (xd);
857         case GXML_ETYPE_NAME       : return push_name  (xd);
858         case GXML_ETYPE_VALUE      : return push_value (xd);
859         case GXML_ETYPE_LABELTABLE : return push_LT    (xd);
860         case GXML_ETYPE_LABEL      : return push_label (xd, attr);
861         case GXML_ETYPE_DATAARRAY  : return push_darray(xd, attr);
862         case GXML_ETYPE_CSTM       : return push_cstm  (xd);
863         case GXML_ETYPE_DATA       : return push_data  (xd);
864         case GXML_ETYPE_DATASPACE  : return push_dspace(xd);
865         case GXML_ETYPE_XFORMSPACE : return push_xspace(xd);
866         case GXML_ETYPE_MATRIXDATA : return push_xform (xd);
867         case GXML_ETYPE_CDATA      : return 0;  /* do nothing */
868         default:
869             fprintf(stderr,"** epush, unknow type '%s'\n",enames[etype]);
870             break;
871     }
872 
873     return 1;
874 }
875 
876 /* initialize the gifti_element and set attributes */
push_gifti(gxml_data * xd,const char ** attr)877 static int push_gifti(gxml_data * xd, const char ** attr )
878 {
879     gifti_image *     gim;
880     int               c;
881 
882     if( !xd ) return 1;
883 
884     /* be explicit with pointers (struct should be clear) */
885     gim = xd->gim;
886     gifti_clear_gifti_image(gim);
887 
888     if( !attr ) return 0;
889 
890     for(c = 0; attr[c]; c+= 2 )
891         if( gifti_str2attr_gifti(gim, attr[c], attr[c+1]) )
892             if( gifti_add_to_nvpairs(&gim->ex_atrs,attr[c],attr[c+1]) )
893                 return 1;
894 
895     if( xd->verb > 1 ) fprintf(stderr,"++ set %d GIFTI attr(s)\n",c/2);
896     if( xd->verb > 3 ) gifti_disp_gifti_image("push:", gim, 0);
897 
898     /* now store any gim->numDA, and use gim to count as they are added */
899     if( gim->numDA >= 0 ) {
900         xd->expDA = gim->numDA;
901         gim->numDA = 0;  /* clear for counting */
902         if( xd->verb > 1 )
903             fprintf(stderr,"-- expecting %d DA elements\n", xd->expDA);
904     }
905 
906     return 0;
907 }
908 
909 /* simply verify that we have not been here before */
push_meta(gxml_data * xd)910 static int push_meta(gxml_data * xd)
911 {
912     giiMetaData * md = find_current_MetaData(xd, 0);
913 
914     if( md->length != 0 || md->name || md->value ) {
915         fprintf(stderr,"** push meta: already initialized??\n");
916         return 1;
917     }
918 
919     return 0;
920 }
921 
922 /* find the parent struct, and return its meta field */
find_current_MetaData(gxml_data * xd,int cdepth)923 static giiMetaData * find_current_MetaData(gxml_data * xd, int cdepth)
924 {
925     giiDataArray * da;
926     giiMetaData  * md;
927     int            da_ind, parent;
928 
929     if( !xd || cdepth < 0 || xd->depth < (2+cdepth) ) {
930         fprintf(stderr,"FMeta: bad params (%p,%d)\n",(void *)xd,cdepth);
931         return NULL;
932     }
933 
934     /* find the appropriate parent struct */
935     parent = xd->stack[xd->depth-2-cdepth];
936     if( parent == GXML_ETYPE_GIFTI )
937         md = &xd->gim->meta;
938     else if( parent == GXML_ETYPE_DATAARRAY ) {
939         if( !xd->gim->darray ) {
940             fprintf(stderr,"** FMeta: gim->darry not initialized\n");
941             return NULL;
942         }
943         da_ind = xd->gim->numDA-1;
944         da = xd->gim->darray[da_ind];
945         if( !da ) {
946             fprintf(stderr,"** FMeta: gim->darry[%d] not initialized\n",da_ind);
947             return NULL;
948         }
949         md = &da->meta;
950     } else {
951         fprintf(stderr,"** FMeta: child of invalid parent '%s'\n",
952                 enames[parent]);
953         return NULL;
954     }
955 
956     return md;
957 }
958 
959 /* we will add a pair, so update length and allocate pointers */
push_md(gxml_data * xd)960 static int push_md(gxml_data * xd)
961 {
962     giiMetaData * md = find_current_MetaData(xd, 1);  /* MD is 1 below */
963 
964     if( !md ) return 1;  /* error were printed */
965 
966     md->length++;
967     md->name = (char **)realloc(md->name, md->length * sizeof(char *));
968     md->value = (char **)realloc(md->value, md->length * sizeof(char *));
969 
970     if( !md->name || !md->value ) {
971         fprintf(stderr,"** failed to realloc %d MD pointers\n",md->length);
972         md->length = 0;
973         return 1;
974     }
975 
976     /* and clear the new pointers */
977     md->name[md->length-1] = NULL;
978     md->value[md->length-1] = NULL;
979 
980     return 0;
981 }
982 
983 /* set cdata to the current meta->name address, and clear it */
push_name(gxml_data * xd)984 static int push_name(gxml_data * xd)
985 {
986     giiMetaData * md = find_current_MetaData(xd, 2);  /* name is 2 below */
987 
988     if( !md ) return 1;
989 
990     xd->cdata = &md->name[md->length-1];  /* use cdata to fill */
991     *xd->cdata = NULL;                    /* init to empty */
992     xd->clen = 0;
993 
994     return 0;
995 }
996 
997 /* set cdata to the current meta->value address, and clear it */
push_value(gxml_data * xd)998 static int push_value(gxml_data * xd)
999 {
1000     giiMetaData * md = find_current_MetaData(xd, 2);  /* value is 2 below */
1001 
1002     if( !md ) return 1;
1003 
1004     xd->cdata = &md->value[md->length-1];  /* use cdata to fill */
1005     *xd->cdata = NULL;                     /* init to empty */
1006     xd->clen = 0;
1007 
1008     return 0;
1009 }
1010 
1011 /* check that LT is currently empty */
push_LT(gxml_data * xd)1012 static int push_LT(gxml_data * xd)
1013 {
1014     giiLabelTable * lt = &xd->gim->labeltable;
1015 
1016     if( lt->length || lt->key || lt->label ) {
1017         fprintf(stderr,"** multiple giiLabelTables?\n");
1018     }
1019 
1020     return 0;
1021 }
1022 
1023 /* increase LabelTable length by 1, and fill new entries
1024  * (note that the Key (or Index) attribute is required)
1025  *
1026  * 'Index' attribute has been replaced by 'Key'    7 Mar 2010 */
push_label(gxml_data * xd,const char ** attr)1027 static int push_label(gxml_data * xd, const char ** attr)
1028 {
1029     giiLabelTable * lt = &xd->gim->labeltable;
1030     float           rgba[4]={0.0, 0.0, 0.0, 0.0};
1031     int             key=0, rv;
1032 
1033     lt->length++;
1034     lt->key = (int *)realloc(lt->key, lt->length * sizeof(int));
1035     lt->label = (char **)realloc(lt->label, lt->length * sizeof(char *));
1036 
1037     if( !lt->key || !lt->label ) {
1038         fprintf(stderr,"** gifti alloc failure for label %d\n",lt->length);
1039         return 1;
1040     }
1041 
1042     /* set key from the attributes */
1043     if( !attr ) {
1044         fprintf(stderr,"** Label %d missing attributes\n", lt->length-1);
1045         lt->key[lt->length-1] = 0;
1046     } else {
1047         /* get any known attributes */
1048         rv = get_label_attrs(xd, attr, &key, rgba);
1049         if ( rv == 1 ) {
1050            lt->key[lt->length-1] = key;
1051         } else if ( rv == 2 ) {
1052            lt->key[lt->length-1] = key;
1053            (void) add_label_rgba(xd, lt, rgba);
1054         } /* else error already printed */
1055     }
1056 
1057     xd->cdata = lt->label + (lt->length-1); /* addr of newest (char *) */
1058     *xd->cdata = NULL;                      /* init to empty */
1059     xd->clen = 0;
1060 
1061     return 0;
1062 }
1063 
1064 
1065 /* add the rgba entries to the LabelTable, length is already updated */
add_label_rgba(gxml_data * xd,giiLabelTable * lt,float * rgba)1066 static int add_label_rgba(gxml_data * xd, giiLabelTable * lt, float * rgba)
1067 {
1068     if( !xd || !lt || !rgba ) {
1069         fprintf(stderr,"** add_label_rgba, bad params\n");
1070         return 1;
1071     }
1072 
1073     if( lt->length > 1 && !lt->rgba ) {
1074         fprintf(stderr,"** first RGBA at Label %d, so table is incomplete\n",
1075                        lt->length-1);
1076         return 1;
1077     }
1078 
1079     lt->rgba = (float *)realloc(lt->rgba, lt->length * 4 * sizeof(float));
1080     if( !lt->rgba ) {
1081         fprintf(stderr,"** failed to malloc rgba of length %d\n", lt->length);
1082         return 1;
1083     }
1084 
1085     memcpy(lt->rgba + 4*(lt->length-1), rgba, 4*sizeof(float));
1086 
1087     if(xd->verb > 4)
1088         fprintf(stderr,"-- adding Label RGBA %g %g %g %g\n",
1089                    lt->rgba[0], lt->rgba[1], lt->rgba[2], lt->rgba[3]);
1090 
1091     return 0;
1092 }
1093 
1094 /* get key and RGBA attributes, if they exist
1095  * return 1 if key, 2 if key+rgba, 0 if neither, -1 on error */
get_label_attrs(gxml_data * xd,const char ** attr,int * key,float * rgba)1096 static int get_label_attrs(gxml_data * xd, const char ** attr, int * key,
1097                            float * rgba)
1098 {
1099     giiLabelTable  * lt = &xd->gim->labeltable;
1100     const char    ** aptr;
1101     char           * endp;  /* for verifying float read */
1102     int              found, lind;
1103 
1104     if( !xd || !key || !rgba ) {
1105         fprintf(stderr,"** GLA: missing params\n");
1106         return -1;
1107     }
1108     if( !attr || !*attr ) return 0;
1109 
1110     /* note label index */
1111     lind = lt->length - 1;
1112 
1113     found = 0;  /* bitmask, key,R,G,B,A (in 0..31, should end as 1 or 31) */
1114     for( aptr = attr; *aptr ; aptr += 2 ) {
1115         if( !aptr[1] ) {
1116             fprintf(stderr,"** label %d, attr %s, missing value\n",lind,*aptr);
1117             return -1;
1118         }
1119         if( !strcmp(*aptr, "Key") ) {
1120             *key = atoi(aptr[1]);
1121             found |= (1<<0);
1122         }
1123         else if( !strcmp(*aptr, "Index") ) { /* old form of Key */
1124             *key = atoi(aptr[1]);
1125             found |= (1<<0);
1126         }
1127         else if( !strcmp(*aptr, "Red") ) {
1128             rgba[0] = strtod(aptr[1], &endp);
1129             if( endp <= aptr[1] ) {
1130                 fprintf(stderr,"** bad GIFTI label %d Red attr\n", lind);
1131                 show_attrs(xd, GXML_ETYPE_LABEL, attr);
1132                 return -1;
1133             }
1134             found |= (1<<1);
1135         }
1136         else if( !strcmp(*aptr, "Green") ) {
1137             rgba[1] = strtod(aptr[1], &endp);
1138             if( endp <= aptr[1] ) {
1139                 fprintf(stderr,"** bad GIFTI label %d Green attr\n", lind);
1140                 show_attrs(xd, GXML_ETYPE_LABEL, attr);
1141                 return -1;
1142             }
1143             found |= (1<<2);
1144         }
1145         else if( !strcmp(*aptr, "Blue") ) {
1146             rgba[2] = strtod(aptr[1], &endp);
1147             if( endp <= aptr[1] ) {
1148                 fprintf(stderr,"** bad GIFTI label %d Blue attr\n", lind);
1149                 show_attrs(xd, GXML_ETYPE_LABEL, attr);
1150                 return -1;
1151             }
1152             found |= (1<<3);
1153         }
1154         else if( !strcmp(*aptr, "Alpha") ) {
1155             rgba[3] = strtod(aptr[1], &endp);
1156             if( endp <= aptr[1] ) {
1157                 fprintf(stderr,"** bad GIFTI label %d Alpha attr\n", lind);
1158                 show_attrs(xd, GXML_ETYPE_LABEL, attr);
1159                 return -1;
1160             }
1161             found |= (1<<4);
1162         } else {
1163             fprintf(stderr,"** unknown GIFTI label %d attr\n", lind);
1164             show_attrs(xd, GXML_ETYPE_LABEL, attr);
1165             return -1;
1166         }
1167     }
1168 
1169     if( found == 0 ) {
1170         fprintf(stderr,"** GIFTI label %d, missing 'Key' attr\n", lind);
1171         return 0;
1172     } else if( found != 1 && found != 31 ) {
1173         fprintf(stderr,"** GIFTI label %d, partial attributes\n", lind);
1174         show_attrs(xd, GXML_ETYPE_LABEL, attr);
1175         return -1;
1176     }
1177 
1178     if(xd->verb > 2) {
1179         if( found == 1 ) fprintf(stderr,"-- have Label Key %d\n", *key);
1180         else fprintf(stderr,"-- have Label Key %d, RGBA %g %g %g %g\n",
1181                      *key, rgba[0], rgba[1], rgba[2], rgba[3]);
1182     }
1183 
1184     if( found == 1 ) return 1;
1185     return 2;
1186 }
1187 
1188 
1189 /* initialize the gifti_element and set attributes */
push_darray(gxml_data * xd,const char ** attr)1190 static int push_darray(gxml_data * xd, const char ** attr)
1191 {
1192     giiDataArray * da;
1193 
1194     /* maintain a count of the number seen */
1195     xd->eleDA++;
1196 
1197     if( xd->da_list ) {
1198         if( (xd->da_ind < xd->da_len) &&
1199             (xd->da_list[xd->da_ind] == xd->eleDA-1) )    /* keeper */
1200         {
1201             if(xd->verb > 1) fprintf(stderr,"++ keeping DA[%d]\n",xd->eleDA-1);
1202             xd->da_ind++;
1203         } else {
1204             if(xd->verb > 1) fprintf(stderr,"++ skipping DA[%d]\n",xd->eleDA-1);
1205             xd->skip = xd->depth;
1206             return 1;   /* return and skip this element */
1207         }
1208     }
1209 
1210     if( gifti_add_empty_darray(xd->gim, 1) ) return 1;
1211 
1212     da = xd->gim->darray[xd->gim->numDA-1];  /* get new pointer */
1213 
1214     /* fill the struct from the attributes (store ex_atrs) */
1215     if( gifti_set_DA_atrs(da, attr, 0, 1) ) return 1;
1216     (void)gifti_valid_DataArray(da, xd->verb > 1);
1217 
1218     /* make a request to potentially update the XML buffer size */
1219     if( da->nvals>0 && da->nbyper>0 )
1220         update_xml_buf_size(xd, da->nvals*da->nbyper);
1221 
1222     if( xd->verb > 4 ) gifti_disp_DataArray("push:", da, 0);
1223 
1224     return 0;
1225 }
1226 
1227 /* check for base64 errors, needed uncompression, and byte swapping */
pop_darray(gxml_data * xd)1228 static int pop_darray(gxml_data * xd)
1229 {
1230     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
1231 
1232     if( !da ) return 1;
1233 
1234     /* check for and clear any b64 errors */
1235     if( xd->b64_errors > 0 ) {
1236         if( xd->b64_check == GIFTI_B64_CHECK_DETECT )
1237             fprintf(stderr,"** bad base64 chars found in DataArray[%d]\n",
1238                     xd->gim->numDA-1);
1239         else if( xd->b64_check == GIFTI_B64_CHECK_COUNT ||
1240                  xd->b64_check == GIFTI_B64_CHECK_SKIPNCOUNT )
1241             fprintf(stderr,"** %d bad base64 chars found in DataArray[%d]\n",
1242                     xd->b64_errors, xd->gim->numDA-1);
1243         xd->b64_errors = 0;
1244     }
1245 
1246     if( da->encoding == GIFTI_ENCODING_B64GZ && da->data ) {
1247 #ifdef HAVE_ZLIB   /* for compiling, higher level test elsewhere */
1248         long long olen;  /* to avoid warnings printing outlen */
1249         uLongf    outlen = da->nvals*da->nbyper;
1250         int       rv = 0;
1251 
1252         /* unzip zdata to da->data */
1253 
1254         if( xd->verb > 2 )
1255             fprintf(stderr,"-- uncompressing %lld bytes into %lld\n",
1256                            xd->dind, (long long)outlen);
1257 
1258         rv = uncompress(da->data, &outlen, (Bytef*)xd->zdata, xd->dind);
1259         olen = outlen;
1260 
1261         if( rv != Z_OK ) {
1262             fprintf(stderr,"** uncompress fails for DA[%d]\n",xd->gim->numDA-1);
1263             if( rv == Z_MEM_ERROR )
1264                 fprintf(stderr,"   (zlib failure, not enough memory)\n");
1265             else if ( rv == Z_BUF_ERROR )
1266                 fprintf(stderr,"   (zlib failure, output buffer too short)\n");
1267             else if ( rv == Z_DATA_ERROR )
1268                 fprintf(stderr,"   (zlib failure, corrupted data)\n");
1269             else if ( rv != Z_OK )
1270                 fprintf(stderr,"   (zlib failure, unknown error %d)\n", rv);
1271         } else if ( xd->verb > 2 || (xd->verb > 1 && xd->gim->numDA == 1 ))
1272             fprintf(stderr,"-- uncompressed buffer (%.2f%% of %lld bytes)\n",
1273                     100.0*xd->dind/olen, olen);
1274 
1275         if( olen != da->nvals*da->nbyper ) {
1276             fprintf(stderr,"** uncompressed buf is %lld bytes, expected %lld\n",
1277                     olen, da->nvals*da->nbyper);
1278         }
1279 #endif
1280 
1281         xd->gim->compressed = 1;   /* flag whether some data was compressed */
1282     }
1283 
1284     /* possibly read data from an external file */
1285     if( da->ext_fname && *da->ext_fname )
1286         (void)gifti_read_extern_DA_data(da); /* nothing to do on failure */
1287 
1288     /* possibly perform byte-swapping on data */
1289     if( da->data && da->encoding != GIFTI_ENCODING_ASCII ) {
1290         long long nvals;
1291         int       swapsize;
1292 
1293         gifti_datatype_sizes(da->datatype, NULL, &swapsize);
1294         if( swapsize <= 0 ) {
1295             fprintf(stderr,"** bad swapsize %d for dtype %d\n",
1296                     swapsize, da->datatype);
1297             return 1;
1298         }
1299 
1300         nvals = da->nvals * da->nbyper / swapsize;
1301         if( gifti_check_swap(da->data, da->endian, nvals, swapsize) )
1302             xd->gim->swapped = 1;       /* flag that it happened */
1303     }
1304 
1305     return 0;
1306 }
1307 
1308 
1309 /* make space for a new CS structure in the current DataArray */
push_cstm(gxml_data * xd)1310 static int push_cstm(gxml_data * xd)
1311 {
1312     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
1313 
1314     if( da->intent != NIFTI_INTENT_POINTSET && xd->verb > 0 )
1315         fprintf(stderr,"** DA[%d] has coordsys with intent %s (should be %s)\n",
1316                 xd->gim->numDA-1, gifti_intent_to_string(da->intent),
1317                 gifti_intent_to_string(NIFTI_INTENT_POINTSET));
1318 
1319     if( gifti_add_empty_CS(da) ) return 1;
1320 
1321     return 0;
1322 }
1323 
1324 /* verify the processing buffer space, alloc data space */
push_data(gxml_data * xd)1325 static int push_data(gxml_data * xd)
1326 {
1327     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
1328     int            zsize;
1329 
1330     xd->dind = 0;       /* init for filling */
1331     xd->doff = 0;
1332 
1333     if( ! xd->dstore ) {
1334         if( xd->verb > 3 )
1335             fprintf(stderr,"-- skipping data[%d]\n",xd->gim->numDA-1);
1336         xd->skip = xd->depth;
1337         return 1;
1338     }
1339 
1340     if( update_partial_buffer(&xd->ddata, &xd->dlen, da->nbyper*da->nvals, 0) )
1341         return 1;
1342 
1343     if( da->encoding == GIFTI_ENCODING_B64GZ ) {
1344 
1345 #ifndef HAVE_ZLIB  /* we don't know the encoding until push_darray */
1346         if( g_first_zlib_err_msg ) {
1347             fprintf(stderr,"** no ZLIB: skipping all compressed data\n");
1348             g_first_zlib_err_msg = 0;
1349         }
1350         xd->skip = xd->depth;
1351         return 1;   /* return and skip this element */
1352 #endif
1353 
1354         zsize = da->nbyper*da->nvals * 1.01 + 12; /* zlib.net */
1355 
1356         if( xd->verb > 2 )
1357             fprintf(stderr,"++ creating extra zdata for zlib extraction\n");
1358         if( update_partial_buffer(&xd->zdata, &xd->zlen, zsize, 1) )
1359             return 1;
1360     }
1361 
1362     /* allocate space for data */
1363     if( da->nvals <= 0 || da->nbyper <= 0 ) {
1364         fprintf(stderr,"** PD: bad vals,bytes = %u, %d\n",
1365                 (unsigned)da->nvals,da->nbyper);
1366         return 1;
1367     }
1368 
1369     da->data = calloc(da->nvals, da->nbyper);
1370     if( ! da->data ) {
1371         fprintf(stderr,"** PD: failed to alloc %lld bytes for darray[%d]\n",
1372                 da->nvals*da->nbyper, xd->gim->numDA-1);
1373         return 1;
1374     } else if ( xd->verb > 3 )
1375         fprintf(stderr,"++ PD: alloc %lld bytes for darray[%d]\n",
1376                 da->nvals*da->nbyper, xd->gim->numDA-1);
1377 
1378     return 0;
1379 }
1380 
1381 /* point cdata to the correct location and init */
push_dspace(gxml_data * xd)1382 static int push_dspace(gxml_data * xd)
1383 {
1384     int CSind = xd->gim->darray[xd->gim->numDA-1]->numCS-1;
1385     if( CSind < 0 ) {
1386         fprintf(stderr,"** PD: bad numCS %d in darray %d, skipping...",
1387                 CSind+1, xd->gim->numDA-1);
1388         xd->skip = xd->depth;
1389         return 1;
1390     }
1391 
1392     if( !xd->gim->darray[xd->gim->numDA-1]->coordsys[CSind] ) {
1393         fprintf(stderr,"** found dataspace without coordsys, skipping...\n");
1394         xd->skip = xd->depth;
1395         return 1;
1396     }
1397 
1398     xd->cdata = &xd->gim->darray[xd->gim->numDA-1]->coordsys[CSind]->dataspace;
1399     *xd->cdata = NULL;                      /* init to empty */
1400     xd->clen = 0;
1401     return 0;
1402 }
1403 
1404 /* point cdata to the correct location and init */
push_xspace(gxml_data * xd)1405 static int push_xspace(gxml_data * xd)
1406 {
1407     int CSind = xd->gim->darray[xd->gim->numDA-1]->numCS-1;
1408     if( CSind < 0 ) {
1409         fprintf(stderr,"** PX: bad numCS %d in darray %d, skipping...",
1410                 CSind+1, xd->gim->numDA-1);
1411         xd->skip = xd->depth;
1412         return 1;
1413     }
1414 
1415     if( !xd->gim->darray[xd->gim->numDA-1]->coordsys[CSind] ) {
1416         fprintf(stderr,"** found xformspace without coordsys, skipping...\n");
1417         xd->skip = xd->depth;
1418         return 1;
1419     }
1420 
1421     xd->cdata = &xd->gim->darray[xd->gim->numDA-1]->coordsys[CSind]->xformspace;
1422     *xd->cdata = NULL;                      /* init to empty */
1423     xd->clen = 0;
1424     return 0;
1425 }
1426 
1427 /* verify the processing buffer space */
push_xform(gxml_data * xd)1428 static int push_xform(gxml_data * xd)
1429 {
1430     int CSind = xd->gim->darray[xd->gim->numDA-1]->numCS-1;
1431     if( CSind < 0 ) {
1432         fprintf(stderr,"** PXform: bad numCS %d in darray %d, skipping...",
1433                 CSind+1, xd->gim->numDA-1);
1434         xd->skip = xd->depth;
1435         return 1;
1436     }
1437 
1438     if( !xd->gim->darray[xd->gim->numDA-1]->coordsys[CSind] ) {
1439         fprintf(stderr,"** found xform without coordsys, skipping...\n");
1440         xd->skip = xd->depth;
1441         return 1;
1442     }
1443 
1444     /* just make sure we have a text buffer to work with */
1445     if( !xd->xdata || xd->xlen <= 0 ) {
1446         xd->xlen = 2048;
1447         xd->xdata = (char *)malloc(xd->xlen * sizeof(char));
1448         if( !xd->xdata ) {
1449             fprintf(stderr,"** cannot alloc %d bytes for xform\n",xd->xlen);
1450             return 1;
1451         }
1452     }
1453 
1454     xd->dind = 0;       /* init for filling */
1455     xd->doff = 0;
1456 
1457     return 0;
1458 }
1459 
epop(gxml_data * xd,int etype,const char * ename)1460 static int epop( gxml_data * xd, int etype, const char * ename )
1461 {
1462     giiDataArray * da;
1463 
1464     xd->cdata = NULL;                   /* clear fields for future use */
1465     xd->clen = 0;
1466 
1467     if( xd->skip == xd->depth ) {       /* just completed skip element */
1468         if( xd->verb > 2 )
1469             fprintf(stderr,"-- popping skip element '%s' at depth %d\n",
1470                     ename, xd->depth);
1471         xd->skip = 0;  /* clear skip level */
1472     } else {    /* may peform pop action for this element */
1473         switch( etype ) {
1474             default: /* do nothing special */
1475                 break;
1476             case GXML_ETYPE_DATA :
1477                 if(xd->verb>3)fprintf(stderr,"-- data dind = %lld\n",xd->dind);
1478                 /* if we have not read data, but allocated for it, free */
1479                 da = xd->gim->darray[xd->gim->numDA-1];
1480                 if( da->data && xd->dind == 0 ) {
1481                     if( xd->verb > 3 ) fprintf(stderr,"   (freeing data)\n");
1482                     free(da->data);
1483                     da->data = NULL;
1484                 }
1485                 break;
1486 
1487             case GXML_ETYPE_DATAARRAY  :
1488                 pop_darray(xd);
1489                 break;
1490 
1491             case GXML_ETYPE_GIFTI      :
1492                 if(xd->eleDA != xd->expDA)
1493                     fprintf(stderr,"** found %d DAs, expected %d\n",
1494                             xd->eleDA, xd->expDA);
1495                 else if(xd->da_list && (xd->da_len != xd->da_ind))
1496                     fprintf(stderr,"** stored %d DAs, wanted %d\n",
1497                             xd->da_ind, xd->da_len);
1498                 if(xd->verb > 2)
1499                     gifti_disp_gifti_image("pop:", xd->gim, xd->verb > 4);
1500 
1501                 if(xd->verb > 1) {      /* check flags */
1502                     if(xd->gim->swapped)
1503                         fprintf(stderr,"++ data was byte-swapped\n");
1504                     if(xd->gim->compressed)
1505                         fprintf(stderr,"++ data was compressed\n");
1506                 }
1507 
1508                 break;
1509         }
1510     }
1511 
1512     xd->depth--;
1513 
1514     if( xd->verb > 5 )
1515     {
1516         show_depth(xd->depth, 1, stderr);
1517         fprintf(stderr,"++ pop %02d : '%s'\n", etype, enames[etype]);
1518     }
1519 
1520     if( xd->depth < 0 || xd->depth > GXML_MAX_DEPTH ) {
1521         fprintf(stderr,"** pop: stack depth %d out of [0,%d] range\n",
1522                 xd->depth, GXML_MAX_DEPTH);
1523         xd->errors++;
1524         return -1;
1525     }
1526 
1527     return 0;
1528 }
1529 
1530 /* return the number of bytes of leading whitespace, up to a max of len */
whitespace_len(const char * str,int len)1531 static int whitespace_len(const char * str, int len)
1532 {
1533     int c;
1534     if( !str || !*str || len < 1 ) return 0;
1535     for( c = 0; c < len; c++ )
1536         if( !isspace(str[c]) ) return c;
1537 
1538     return len;
1539 }
1540 
show_attrs(gxml_data * xd,int etype,const char ** attr)1541 static void show_attrs(gxml_data * xd, int etype, const char ** attr)
1542 {
1543     int count;
1544     show_depth(xd->depth, 1, stderr);
1545     fprintf(stderr, ": element %s\n", enames[etype]);
1546     for( count = 0; attr[count]; count += 2 ){
1547         show_depth(xd->depth, 0, stderr);
1548         fprintf(stderr,"      attr: %s='%s'\n", attr[count], attr[count+1]);
1549     }
1550 }
1551 
1552 
cb_start_ele(void * udata,const char * ename,const char ** attr)1553 static void XMLCALL cb_start_ele(void *udata, const char *ename,
1554                                               const char **attr)
1555 {
1556     gxml_data * xd = (gxml_data *)udata;
1557     int         etype;
1558 
1559     etype = ename2type(ename);
1560     if( xd->verb > 3 ) show_attrs(xd, etype, attr);
1561 
1562     /* process attributes and push() */
1563 
1564     (void)epush(xd, etype, ename, attr);
1565 }
1566 
1567 /* if ending Data, clear prev_end_check */
cb_end_ele(void * udata,const char * ename)1568 static void XMLCALL cb_end_ele(void *udata, const char * ename)
1569 {
1570     gxml_data * xd = (gxml_data *)udata;
1571 
1572     epop(xd, ename2type(ename), ename);
1573 }
1574 
1575 /* May divide Data, but apparently not attributes, perhaps because
1576    the Data section is longer than the buffer is wide.
1577 
1578    if in Data:
1579         if prev_end_check
1580             if( ! char_is_whitespace(first) && concat_is_number() )
1581                 concatencate as number to adjust previous number
1582                 verify rest is whitespace
1583                 return  (we don't expect to start a new number)
1584             else
1585                 apply previous number
1586         if( !char_is_whitespace(last) )
1587             store trailing non-space in concat_buf
1588         else
1589             prev_end_check = 0
1590         apply number (though it may change later)
1591 */
cb_char(void * udata,const char * cdata,int length)1592 static void XMLCALL cb_char(void *udata, const char * cdata, int length)
1593 {
1594     gxml_data  * xd = (gxml_data *)udata;
1595     const char * str = cdata;
1596     int          len = length, wlen = 0, parent;
1597 
1598     if( xd->skip > 0 ) {
1599         if(xd->verb > 3) fprintf(stderr,"-- skipping char [%d]\n",len);
1600         return;
1601     }
1602 
1603     /* act based on the parent type */
1604     parent = xd->stack[xd->depth-1];
1605     if( parent == GXML_ETYPE_CDATA ) parent = xd->stack[xd->depth-2];
1606 
1607     if( parent != GXML_ETYPE_DATA ) wlen = whitespace_len(str,length);
1608 
1609     switch( parent ) {
1610         case GXML_ETYPE_DATA       :
1611             (void)append_to_data(xd, cdata, length);
1612             break;
1613         case GXML_ETYPE_MATRIXDATA :
1614             (void)append_to_xform(xd, cdata, length);
1615             break;
1616 
1617         case GXML_ETYPE_GIFTI      :
1618         case GXML_ETYPE_META       :
1619         case GXML_ETYPE_MD         :
1620         case GXML_ETYPE_LABELTABLE :
1621         case GXML_ETYPE_DATAARRAY  :
1622         case GXML_ETYPE_CSTM       :
1623             if( wlen != length && xd->verb ) {
1624                 fprintf(stderr,"** invalid chars under %s: '%.*s'\n",
1625                         enames[parent], length, cdata);
1626             }
1627             break;
1628 
1629         case GXML_ETYPE_NAME       :
1630         case GXML_ETYPE_VALUE      :
1631         case GXML_ETYPE_LABEL      :
1632         case GXML_ETYPE_DATASPACE  :
1633         case GXML_ETYPE_XFORMSPACE :
1634             if( xd->verb > 4 )
1635                 fprintf(stderr,"++ append cdata, parent %s\n",enames[parent]);
1636             /* append only if cdata   23 Fef 2016 [rdvincent] */
1637             if( xd->cdata )
1638                 (void)append_to_cdata(xd, cdata, length);
1639             else if ( xd->verb > 4 )
1640                 fprintf(stderr, "   missing cdata...\n");
1641             break;
1642 
1643         case GXML_ETYPE_CDATA      :
1644             fprintf(stderr,"** CDATA is the parent of CDATA???\n");
1645             return;
1646 
1647         default: /* drop through */
1648             fprintf(stderr,"** unknown parent of char: %d\n", parent);
1649             return;
1650     }
1651 
1652     if( wlen == length ) {      /* if it is all whitespace */
1653         if( xd->verb < 5 ) return;
1654         str = "whitespace";     /* just note the whitespace */
1655         len = strlen(str);
1656     }
1657 
1658     if( xd->verb > 4 ) {
1659         show_depth(xd->depth, 1, stderr);
1660         if( parent == GXML_ETYPE_DATA && len > 40 ) len = 40;
1661         fprintf(stderr, "char[%d]: %.*s\n", length, len, str);
1662     }
1663 }
1664 
1665 
1666 /* ----------------------------------------------------------------------
1667  * xd->cdata points to one of:
1668  *      md->name[k], md->value[k], lt->label[k],
1669  *      da[k]->coordsys->dataspace, da[k]->coordsys->xformspace
1670  *
1671  * append the new data and null terminate
1672  * ---------------------------------------------------------------------- */
append_to_cdata(gxml_data * xd,const char * cdata,int len)1673 static int append_to_cdata(gxml_data * xd, const char * cdata, int len)
1674 {
1675     int offset;
1676     if( !xd || !cdata || len <= 0 ) {
1677         fprintf(stderr,"** A2CD, bad params (%p,%p,%d)\n",
1678                 (void *)xd,(void *)cdata, len);
1679         return 1;
1680     }
1681     if( !*xd->cdata ) {
1682         offset = 0;
1683         xd->clen = len + 1;  /* first time, alloc for null */
1684     }
1685     else {
1686         offset = xd->clen - 1;
1687         xd->clen += len;
1688     }
1689 
1690     if( xd->verb > 4 )
1691         fprintf(stderr,"++ a2cdata, len %d, clen %d, data '%.*s'\n",
1692                 len, xd->clen, len, cdata);
1693 
1694     *xd->cdata = (char *)realloc(*xd->cdata, xd->clen*sizeof(char));
1695     if(!*xd->cdata) {
1696         fprintf(stderr,"** A2CD, failed to realloc %d bytes\n",xd->clen);
1697         return 1;
1698     }
1699 
1700     memcpy(*xd->cdata + offset, cdata, len);    /* append the new data */
1701     (*xd->cdata)[xd->clen-1] = '\0';            /* and null terminate */
1702 
1703     return 0;
1704 }
1705 
1706 
1707 /* this must go to the data of the latest darray struct */
append_to_data(gxml_data * xd,const char * cdata,int len)1708 static int append_to_data(gxml_data * xd, const char * cdata, int len)
1709 {
1710     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
1711 
1712     if( !da || !xd->dlen || !xd->ddata || xd->dind < 0 ) {
1713         fprintf(stderr,"** A2D: bad setup (%p,%d,%p,%lld)\n",
1714                 (void *)da, xd->dlen, (void *)xd->ddata, xd->dind);
1715         return 1;
1716     } else if( !da->data ) {
1717         fprintf(stderr,"** A2D: no data allocated\n");
1718         return 1;
1719     }
1720 
1721     switch( da->encoding ){
1722         case GIFTI_ENCODING_ASCII:
1723             return append_to_data_ascii(xd, cdata, len);
1724 
1725         case GIFTI_ENCODING_B64BIN:
1726             return append_to_data_b64(xd, (char *)da->data,
1727                                       da->nvals*da->nbyper, cdata, len);
1728         case GIFTI_ENCODING_B64GZ:
1729             return append_to_data_b64(xd, xd->zdata, xd->zlen, cdata, len);
1730 
1731         default:
1732             fprintf(stderr,"** A2D: invalid encoding value %d (%s)\n",
1733                     da->encoding,
1734                     gifti_list_index2string(gifti_encoding_list,da->encoding));
1735             return 1;
1736     }
1737 }
1738 
1739 
1740 /* decode the b64 data, inserting it into da->data
1741  *
1742  * use intermediate buffer (ddata), length dlen+1, reprocess length doff
1743  * ddata format, in bytes per section:
1744  *       +------+----------+--------+---+
1745  *       | doff | copy_len | unused | 1 |  (last 1 assures null termination)
1746  *       +------+----------+--------+---+
1747  *
1748  * - while there are bytes left to process (rem_bytes_in > 0)
1749  *       copy_len = bytes to process now
1750  *       copy that many bytes to ddata
1751  *       rem_bytes_out = number of output bytes left to fill in da->data
1752  *       doff = decode_b64(xd, ddata, doff, da->data+dind, &rem_bytes_out)
1753  *            (returns number of unprocessed bytes, in [0..3])
1754  *       if( doff > 0 ) mv last doff bytes to beginning of ddata
1755  *       rem_bytes_in -= copy_len
1756  *       dind = updated offset into output da->data buffer
1757  */
append_to_data_b64(gxml_data * xd,char * dest,long long tot_bytes,const char * cdata,int cdlen)1758 static int append_to_data_b64(gxml_data * xd, char * dest, long long tot_bytes,
1759                               const char * cdata, int cdlen)
1760 {
1761     const char   * cptr;
1762     long long      rem_bytes_out;        /* remaining length in darray->data */
1763     int            rem_bytes_in = cdlen; /* remaining length in cdata */
1764     int            copy_len, apply_len, unused;
1765 
1766     if( xd->verb > 4 )
1767         fprintf(stderr,"++ appending %d base64 binary bytes to data\n",cdlen);
1768 
1769     /* Copy cdata to local buffer in pieces, for storage of trailing
1770        characters from a previous call.  Given that, processing data
1771        as is done with ASCII seems reasonable. */
1772     while( rem_bytes_in > 0 ) {
1773         /*--- prepare intermediate buffer ---*/
1774 
1775         /* point to the current location */
1776         cptr = cdata + cdlen - rem_bytes_in;
1777 
1778         /* decide how many bytes to copy (avail space w/max of rem_bytes_in) */
1779         copy_len = xd->dlen - xd->doff - 1;
1780         if( copy_len > rem_bytes_in ) {
1781             unused = copy_len - rem_bytes_in;  /* unused at end of buffer */
1782             copy_len = rem_bytes_in;
1783         } else unused = 0;
1784 
1785         /* copy the data to our intermediate buffer
1786            (if we allow bad characters, skipping them, do it here) */
1787         (void)copy_b64_data(xd, cptr, xd->ddata+xd->doff, copy_len, &apply_len);
1788 
1789         /*--- process the data ---*/
1790 
1791         /* note how many bytes remain to be computed */
1792         rem_bytes_out = tot_bytes - xd->dind;
1793         if(xd->verb > 5)
1794             fprintf(stderr,"-- %lld bytes left at offset %lld\n",
1795                     rem_bytes_out, xd->dind);
1796 
1797         /* convert to binary bytes */
1798         xd->doff = decode_b64(xd,
1799                         xd->ddata,              /* data source */
1800                         xd->doff+apply_len,     /* data length */
1801                         dest + xd->dind,        /* output destination  */
1802                         &rem_bytes_out          /* nbytes left to fill */
1803                         );
1804 
1805         /*--- check results --- */
1806         if( xd->doff < 0 ) { xd->doff = 0; return 1; } /* error */
1807         if( xd->doff >= xd->dlen - 1 ) {
1808             if(xd->verb)
1809                 fprintf(stderr,"** A2Db64: failed to process buffer\n");
1810             fprintf(stderr,"** rem = %d\n", xd->doff);
1811             xd->doff = 0;        /* blow away the buffer and continue */
1812         }
1813 
1814         /*--- adjust intermediate buffer ---*/
1815 
1816         /* move any unused bytes to the beginning (last doff, before unused) */
1817         if( xd->doff > 0 ) {
1818             if( xd->verb > 5 )
1819                 fprintf(stderr,"++ A2Db64: move %d bytes from %d (blen %d)\n",
1820                     xd->doff, xd->dlen - unused - xd->doff, xd->dlen);
1821             /* (subtract unused+1, since 1 byte is saved for null */
1822             memmove(xd->ddata, xd->ddata+xd->dlen -(unused+1) - xd->doff,
1823                     xd->doff);
1824             if( xd->verb > 6 )
1825                 fprintf(stderr,"   bytes are '%.*s'\n",xd->doff,
1826                         (char *)xd->ddata);
1827         }
1828 
1829         /* adjust remaining bytes for next time */
1830         rem_bytes_in -= copy_len;  /* more than apply_len, if bad chars */
1831         xd->dind = tot_bytes - rem_bytes_out;
1832     }
1833 
1834     return 0;
1835 }
1836 
1837 /* Copy the base64 data to the dest buffer, possibly noting, counting
1838  * and/or skipping any invalid characters, based on b64_check.
1839  *
1840  * return the number of bad characters noted
1841 */
copy_b64_data(gxml_data * xd,const char * src,char * dest,int src_len,int * dest_len)1842 static int copy_b64_data(gxml_data * xd, const char * src, char * dest,
1843                          int src_len, int * dest_len)
1844 {
1845     const unsigned char * usrc = (const unsigned char *)src;
1846     int c, errs = 0, apply_len;
1847 
1848     if( xd->verb > 1 ) {  /* in verbose mode, perform automatic check */
1849         c = count_bad_b64_chars(src, src_len);
1850         if( c > 0 ) {
1851             fprintf(stderr, "CB64D: found %d bad b64 chars\n", c);
1852             if( xd->verb > 5 ) show_bad_b64_chars(src, src_len);
1853         }
1854     }
1855 
1856     switch( xd->b64_check ){
1857         default:
1858             fprintf(stderr,"** CB64D: b64_check = %d\n", xd->b64_check);
1859             /* whine and fall through */
1860 
1861         case GIFTI_B64_CHECK_NONE:   /* basic case - just copy the data */
1862             memcpy(dest, src, src_len);
1863             apply_len = src_len;
1864             break;
1865 
1866         case GIFTI_B64_CHECK_DETECT:   /* check for existence of bad chars */
1867             for(c = 0; c < src_len; c++)
1868                 if( b64_decode_table[usrc[c]] == (unsigned char)0x80 ) {
1869                     errs++;
1870                     break;
1871                 }
1872             memcpy(dest, src, src_len);
1873             apply_len = src_len;
1874             break;
1875 
1876         case GIFTI_B64_CHECK_COUNT:   /* count bad characters */
1877             for(c = 0; c < src_len; c++)
1878                 if( b64_decode_table[usrc[c]] == (unsigned char)0x80 )
1879                     errs++;
1880             memcpy(dest, src, src_len);
1881             apply_len = src_len;
1882             break;
1883 
1884         case GIFTI_B64_CHECK_SKIP: /* skip bad characters, but don't count */
1885             apply_len = 0;
1886             for(c = 0; c < src_len; c++){
1887                 if( b64_decode_table[usrc[c]] != (unsigned char)0x80 )
1888                     dest[apply_len++] = src[c];
1889             }
1890             break;
1891         case GIFTI_B64_CHECK_SKIPNCOUNT:    /* skip and count bad characters */
1892             apply_len = 0;
1893             for(c = 0; c < src_len; c++){
1894                 if( b64_decode_table[usrc[c]] == (unsigned char)0x80 )
1895                     errs++;
1896                 else
1897                     dest[apply_len++] = src[c];
1898             }
1899             break;
1900     }
1901 
1902     /* and null terminate */
1903     xd->ddata[xd->doff+apply_len] = '\0';
1904 
1905     /* note length and any errors */
1906     *dest_len = apply_len;
1907     xd->b64_errors += errs;
1908 
1909     return errs;
1910 }
1911 
count_bad_b64_chars(const char * src,int len)1912 static int count_bad_b64_chars(const char * src, int len)
1913 {
1914     const unsigned char * usrc = (const unsigned char *)src;
1915     int c, bad = 0;
1916 
1917     for(c = 0; c < len; c++)
1918         if( b64_decode_table[usrc[c]] == (unsigned char)0x80 )
1919             bad++;
1920 
1921     return bad;
1922 }
1923 
show_bad_b64_chars(const char * src,int len)1924 static int show_bad_b64_chars(const char * src, int len)
1925 {
1926     const unsigned char * usrc = (const unsigned char *)src;
1927     int c, bad = 0;
1928 
1929     fprintf(stderr,"-- bad b64 chars:");
1930     for(c = 0; c < len; c++)
1931         if( b64_decode_table[usrc[c]] == (unsigned char)0x80 ) {
1932             bad++;
1933             fprintf(stderr," 0x%02x", usrc[c]);
1934         }
1935     if( bad ) fputc('\n', stderr);
1936     else      fprintf(stderr," none");
1937 
1938     return bad;
1939 }
1940 
1941 /* this must go to the data of the latest darray struct */
append_to_data_ascii(gxml_data * xd,const char * cdata,int len)1942 static int append_to_data_ascii(gxml_data * xd, const char * cdata, int len)
1943 {
1944     static int     mod_prev = 0;
1945     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
1946 
1947     char      * dptr;
1948     char      * cptr;
1949     long long   rem_vals;
1950     int         rem_len = len, copy_len, unused;
1951     int         type = da->datatype;
1952 
1953     if( xd->verb > 4 )
1954         fprintf(stderr,"++ appending %d ASCII bytes to data\n",len);
1955 
1956     /* if there is only whitespace, blow outta here */
1957     if( whitespace_len(cdata, len) == len ) { xd->doff = 0; return 0; }
1958 
1959     /* Copy cdata to local buffer in pieces, for null termination and for
1960        storage of trailing characters that may need to be processed again
1961        (after more characters are read by the parser).                    */
1962     while( rem_len > 0 ) {
1963         /*--- prepare intermediate buffer ---*/
1964 
1965         /* point to the current location */
1966         cptr = (char *)cdata + len - rem_len;
1967 
1968         /* if we're looking at whitespace, any unused data is garbage */
1969         if( isspace(*cptr)) xd->doff = 0;
1970 
1971         /* decide how many bytes to copy (avail space w/max of rem_len) */
1972         copy_len = xd->dlen - xd->doff - 1;
1973         if( copy_len > rem_len ) {
1974             unused = copy_len - rem_len;  /* unused at end of buffer */
1975             copy_len = rem_len;
1976         } else unused = 0;
1977 
1978         /* copy it to our buffer and null terminate */
1979         memcpy(xd->ddata+xd->doff, cptr, copy_len);
1980         xd->ddata[xd->doff+copy_len] = '\0';
1981 
1982         /*--- process the ascii data ---*/
1983 
1984         /* note how many values remain to be computed */
1985         rem_vals = da->nvals - xd->dind;
1986         if(xd->verb > 5)
1987             fprintf(stderr,"-- %lld vals left at offset %lld, nbyper %d\n",
1988                     rem_vals, xd->dind, da->nbyper);
1989 
1990         if( xd->dind == 0 ) mod_prev = 0;       /* nothing to modify at first */
1991         dptr = (char *)da->data + (xd->dind)*da->nbyper;
1992         xd->doff = decode_ascii(xd,
1993                         xd->ddata,              /* data source */
1994                         xd->doff+copy_len,      /* data length */
1995                         type,                   /* data type */
1996                         dptr,                   /* starting destination */
1997                         &rem_vals,              /* nvals to read */
1998                         &mod_prev               /* can we mod previous val */
1999                         );
2000 
2001         /*--- check results --- */
2002         if( xd->doff < 0 ) { xd->doff = 0; return 1; } /* error */
2003         if( xd->doff >= xd->dlen - 1 ) {
2004             if(xd->verb) fprintf(stderr,"** A2D: failed to process buffer\n");
2005             fprintf(stderr,"** rem = %d\n", xd->doff);
2006             xd->doff = 0;        /* blow away the buffer and continue */
2007         }
2008 
2009         /*--- adjust intermediate buffer ---*/
2010 
2011         /* move any unused bytes to the beginning (last doff, before unused) */
2012         if( xd->doff > 0 ) {
2013             if( xd->verb > 5 )
2014                 fprintf(stderr,"++ A2D: move %d bytes from %d (blen %d)\n",
2015                     xd->doff, xd->dlen - unused - xd->doff, xd->dlen);
2016             /* (subtract unused+1, since 1 byte is saved for null */
2017             memmove(xd->ddata, xd->ddata+xd->dlen -(unused+1) - xd->doff,
2018                     xd->doff);
2019             if( xd->verb > 6 )
2020                 fprintf(stderr,"   bytes are '%.*s'\n",xd->doff,
2021                         (char *)xd->ddata);
2022         }
2023 
2024         /* adjust rem_len for next time */
2025         rem_len -= copy_len;
2026         xd->dind = da->nvals - rem_vals;  /* note remaining values */
2027     }
2028 
2029     return 0;
2030 }
2031 
2032 /* this must go to the xform of the latest darray struct */
2033 /* (process as 1-D array) */
append_to_xform(gxml_data * xd,const char * cdata,int len)2034 static int append_to_xform(gxml_data * xd, const char * cdata, int len)
2035 {
2036     static int  mod_prev = 0;
2037     giiDataArray * da = xd->gim->darray[xd->gim->numDA-1]; /* current DA */
2038 
2039     double    * dptr;
2040     char      * cptr;
2041     long long   rem_vals;
2042     int         rem_len = len, copy_len, unused, CSind;
2043     int         type = gifti_str2datatype("NIFTI_TYPE_FLOAT64"); /* double */
2044 
2045     if( !da || !xd->xlen || !xd->xdata || xd->dind < 0 || da->numCS <= 0) {
2046         fprintf(stderr,"** A2X: bad setup (%p,%d,%p,%lld,%d)\n",
2047                 (void *)da, xd->xlen, (void *)xd->xdata, xd->dind, da->numCS);
2048         return 1;
2049     } else if( xd->verb > 4 )
2050         fprintf(stderr,"++ appending %d bytes to xform\n",len);
2051 
2052     CSind = da->numCS-1;
2053     if( !da->coordsys || !da->coordsys[CSind] ) {
2054         fprintf(stderr,"** A2X: bad coordsys[%d]\n", CSind);
2055         return 1;
2056     }
2057 
2058     /* if there is only whitespace, blow outta here */
2059     if( whitespace_len(cdata, len) == len ) { xd->doff = 0; return 0; }
2060 
2061     /* Copy cdata to local buffer in pieces, for null termination and for
2062        storage of trailing characters that may need to be processed again
2063        (after more characters are read by the parser).                    */
2064     while( rem_len > 0 ) {
2065         /*--- prepare intermediate buffer ---*/
2066 
2067         /* point to the current location */
2068         cptr = (char *)cdata + len - rem_len;
2069 
2070         /* if we're looking at whitespace, any unused data is garbage */
2071         if( isspace(*cptr)) xd->doff = 0;
2072 
2073         /* decide how many bytes to copy (avail space w/max of rem_len) */
2074         copy_len = xd->xlen - xd->doff - 1;
2075         if( copy_len > rem_len ) {
2076             unused = copy_len - rem_len;  /* unused at end of buffer */
2077             copy_len = rem_len;
2078         } else unused = 0;
2079 
2080         /* copy it to our buffer and null terminate */
2081         memcpy(xd->xdata+xd->doff, cptr, copy_len);
2082         xd->xdata[xd->doff+copy_len] = '\0';
2083 
2084         /* note how many values remain to be computed */
2085         rem_vals = 16 - xd->dind;
2086 
2087         /*--- process the ascii data ---*/
2088         if( xd->dind == 0 ) mod_prev = 0;       /* nothing to modify at first */
2089         dptr = (double *)da->coordsys[CSind]->xform + (xd->dind); /* as array */
2090         xd->doff = decode_ascii(xd,
2091                         xd->xdata,              /* data source */
2092                         xd->doff+copy_len,      /* data length */
2093                         type,                   /* data type */
2094                         dptr,                   /* starting destination */
2095                         &rem_vals,              /* nvals to read */
2096                         &mod_prev               /* can we mod previous val */
2097                         );
2098 
2099         /*--- check results --- */
2100         if( xd->doff < 0 ) { xd->doff = 0; return 1; } /* error */
2101         if( xd->doff >= xd->xlen - 1 ) {
2102             if(xd->verb) fprintf(stderr,"** A2X: failed to process buffer\n");
2103             fprintf(stderr,"** rem = %d\n", xd->doff);
2104             xd->doff = 0;        /* blow away the buffer and continue */
2105         }
2106 
2107         /*--- adjust intermediate buffer ---*/
2108 
2109         /* move any unused bytes to the beginning (last doff, before unused) */
2110         if( xd->doff > 0 ) {
2111             if( xd->verb > 5 )
2112                 fprintf(stderr,"++ A2X: move %d bytes from %d (blen %d)\n",
2113                         xd->doff, xd->dlen - unused - xd->doff, xd->dlen);
2114                 /* (subtract unused+1, since 1 bytes is saved for null */
2115                 memmove(xd->xdata, xd->xdata+xd->xlen -(unused+1) -xd->doff,
2116                         xd->doff);
2117             if( xd->verb > 6 )
2118                 fprintf(stderr,"   bytes are '%.*s'\n",xd->doff,
2119                         (char *)xd->ddata);
2120         }
2121 
2122         /* adjust rem_len for next time */
2123         rem_len -= copy_len;
2124         xd->dind = 16 - rem_vals;  /* note remaining values */
2125     }
2126 
2127     return 0;
2128 }
2129 
2130 #undef GII_B64_decode4
2131 #define GII_B64_decode4(w,x,y,z,a,b,c)                                  \
2132      ( a = (b64_decode_table[w] << 2) | (b64_decode_table[x] >> 4) ,    \
2133        b = (b64_decode_table[x] << 4) | (b64_decode_table[y] >> 2) ,    \
2134        c = (b64_decode_table[y] << 6) | b64_decode_table[z]         )
2135 
2136 /*  given: source pointer, length, dest loc and nbytes to set,
2137           (cdata is null-terminated)
2138     modify: needed (bytes) left for output
2139     return: nbytes unprocessed, so 0-3 (< 0 on error)
2140 
2141     Convert the base64 character data into binary.
2142         - read failure happens only when no characters are processed
2143         - characters are not checked for validity (maybe already done)
2144 
2145     note: the base64 defaults will be applied
2146             o EOL use is not allowed
2147             o padding is expected (using '=')
2148 */
decode_b64(gxml_data * xd,char * cdata,int cdlen,char * dptr,long long * needed)2149 static int decode_b64(gxml_data * xd, char * cdata, int cdlen,
2150                       char * dptr, long long * needed)
2151 {
2152     unsigned char * din = (unsigned char *)cdata;
2153     unsigned char * dout = (unsigned char *)dptr;
2154     int             blocks = cdlen/4, rem = cdlen % 4;
2155     int             ind, assigned;
2156 
2157     if( xd->verb > 4)
2158         fprintf(stderr,"-- DB64: decode len %d, remain %lld\n", cdlen,*needed);
2159     if( *needed <= 0 ) {
2160         if( cdlen > 0 )
2161             fprintf(stderr,"** DB64: %d bytes left without a home\n", cdlen);
2162         return 0;
2163     }
2164 
2165     for( ind = 0; ind < blocks && *needed >= 3; ind++, *needed -= 3 ){
2166         GII_B64_decode4(din[0],din[1],din[2],din[3], dout[0],dout[1],dout[2]);
2167 
2168         din  += 4;
2169         dout += 3;
2170     }
2171     assigned = 3*ind;
2172 
2173     /* the first blocks-1 sets should just work */
2174     if( ind < blocks-1 || (ind < blocks && *needed == 0) ){
2175         if( xd->verb > 6 )
2176             gifti_disp_hex_data("decoded b64: 0x ", dptr, assigned, stderr);
2177         fprintf(stderr,"** decode_b64: more data than space\n");
2178         return -1;
2179     }
2180 
2181     /* if we didn't finish, try to fill a partial block */
2182     if( ind < blocks ) { /* so *needed < 3 */
2183         unsigned char a, b, c;
2184         GII_B64_decode4(din[0],din[1],din[2],din[3], a, b, c);
2185         if( *needed >= 1 ) dout[0] = a;
2186         if( *needed >= 2 ) dout[1] = b;
2187         assigned += *needed;
2188         *needed = 0;
2189     }
2190 
2191     if( xd->verb > 6 )
2192         gifti_disp_hex_data("decoded b64: 0x ", dptr, assigned, stderr);
2193 
2194     return rem;
2195 }
2196 
2197 
2198 /* given: source pointer, remaining length, nvals desired, dest loc and type
2199           (cdata is null-terminated)
2200    modify: nvals left for output, mod_prev for next call
2201    return: nbytes that may still need to processed (< 0 on error)
2202 
2203    read failure happens only when no characters are processed
2204 */
decode_ascii(gxml_data * xd,char * cdata,int cdlen,int type,void * dptr,long long * nvals,int * mod_prev)2205 static int decode_ascii(gxml_data * xd, char * cdata, int cdlen, int type,
2206                         void * dptr, long long * nvals, int * mod_prev)
2207 {
2208     char * p1, *p2;     /* for strtoX */
2209     char * prev;        /* for remain */
2210     double dval;        /* for strtod */
2211     long   lval;        /* for strtol */
2212     int    remain = 0;  /* use bytes remaining */
2213     int    vals = 0;
2214 
2215     if( xd->verb > 4)
2216         fprintf(stderr,"-- DA: type %s, len %d, nvals %lld\n",
2217                 gifti_datatype2str(type),cdlen,*nvals);
2218 
2219     /* if reprocessing, maybe let the user know */
2220     if( xd->doff > 0 && *mod_prev ) {
2221         if( xd->verb > 4)
2222             fprintf(stderr,"++ DA: re-proc '%.*s' from '%.*s'...\n",
2223                     xd->doff, cdata, xd->doff+15, cdata);
2224         vals--;  /* back up */
2225     }
2226 
2227     switch( type ) {
2228         default :
2229             fprintf(stderr,"** decode_ascii cannot decode type %d\n",type);
2230             return -1;
2231         case NIFTI_TYPE_UINT8: {
2232             unsigned char * ptr = (unsigned char *)dptr;
2233             p1 = cdata;
2234             prev = p1;
2235             /* vals could be < 0, but we must care for promotion to size_t */
2236             while( (vals < 0 || vals < *nvals) && p1 ) {
2237                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2238                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2239                 prev = p1;              /* store old success ptr */
2240                 p1 = p2;                /* move to next posn */
2241                 ptr[vals] = lval;       /* assign new value  */
2242                 if(xd->verb>6)fprintf(stderr,"  v %d (%ld)",ptr[vals],lval);
2243                 vals++;                 /* count new value   */
2244             }
2245             if(xd->verb > 6) fputc('\n', stderr);
2246             break;
2247         }
2248         case NIFTI_TYPE_INT16: {
2249             short * ptr = (short *)dptr;
2250             p1 = cdata;
2251             prev = p1;
2252             while( (vals < 0 || vals < *nvals) && p1 ) {
2253                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2254                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2255                 prev = p1;              /* store old success ptr */
2256                 p1 = p2;                /* move to next posn */
2257                 ptr[vals] = lval;       /* assign new value  */
2258                 if(xd->verb>6)fprintf(stderr,"  v %d (%ld)",ptr[vals],lval);
2259                 vals++;                 /* count new value   */
2260             }
2261             if(xd->verb > 6) fputc('\n', stderr);
2262             break;
2263         }
2264         case NIFTI_TYPE_INT32: {
2265             int * ptr = (int *)dptr;
2266             p1 = cdata;
2267             prev = p1;
2268             while( (vals < 0 || vals < *nvals) && p1 ) {
2269                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2270                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2271                 prev = p1;              /* store old success ptr */
2272                 p1 = p2;                /* move to next posn */
2273                 ptr[vals] = lval;       /* assign new value  */
2274                 if(xd->verb>6)fprintf(stderr,"  v %d (%ld)",ptr[vals],lval);
2275                 vals++;                 /* count new value   */
2276             }
2277             if(xd->verb > 6) fputc('\n', stderr);
2278             break;
2279         }
2280         case NIFTI_TYPE_FLOAT32: {
2281             float * ptr = (float *)dptr;
2282             p1 = cdata;
2283             prev = p1;
2284             while( (vals < 0 || vals < *nvals) && p1 ) {
2285                 dval = strtod(p1, &p2); /* try to read next value */
2286                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2287                 prev = p1;              /* store old success ptr */
2288                 p1 = p2;                /* move to next posn */
2289                 ptr[vals] = dval;       /* assign new value  */
2290                 if(xd->verb>6)fprintf(stderr,"  v %f (%f)",ptr[vals],dval);
2291                 vals++;                 /* count new value   */
2292             }
2293             if(xd->verb > 6) fputc('\n', stderr);
2294             break;
2295         }
2296         case NIFTI_TYPE_COMPLEX64: {
2297             /* there are 2 pieces to each complex, so read and insert 1 at a
2298                time, and increment 'vals' only after each second piece */
2299             static int piece = 0;
2300             float * ptr = (float *)dptr;
2301             p1 = cdata;
2302             prev = p1;
2303             while( (vals < 0 || vals < *nvals) && p1 ) {
2304                 dval = strtod(p1, &p2); /* try to read next value */
2305                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2306                 prev = p1;              /* store old success ptr */
2307                 p1 = p2;                /* move to next posn */
2308                 ptr[2*vals+piece] = dval; /* assign new value  */
2309                 if(xd->verb>6) fprintf(stderr,"  v %f (%f)", ptr[2*vals],dval);
2310                 if( piece == 1 ) vals++; /* have a complete data value */
2311                 piece = 1-piece;
2312             }
2313             if(xd->verb > 6) fputc('\n', stderr);
2314             break;
2315         }
2316         case NIFTI_TYPE_FLOAT64: {
2317             double * ptr = (double *)dptr;
2318             p1 = cdata;
2319             prev = p1;
2320             while( (vals < 0 || vals < *nvals) && p1 ) {
2321                 dval = strtod(p1, &p2); /* try to read next value */
2322                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2323                 prev = p1;              /* store old success ptr */
2324                 p1 = p2;                /* move to next posn */
2325                 ptr[vals] = dval;       /* assign new value  */
2326                 if(xd->verb>6)fprintf(stderr,"  v %f (%f)",ptr[vals],dval);
2327                 vals++;                 /* count new value   */
2328             }
2329             if(xd->verb > 6) fputc('\n', stderr);
2330             break;
2331         }
2332         case NIFTI_TYPE_RGB24: {
2333             /* there are 3 pieces to each RGB24, so read and insert 1 at a
2334                time, and increment 'vals' only after each third piece */
2335             static int piece = 0;
2336             unsigned char * ptr = (unsigned char *)dptr;
2337             p1 = cdata;
2338             prev = p1;
2339             /* vals could be < 0, but we must care for promotion to size_t */
2340             while( (vals < 0 || vals < *nvals) && p1 ) {
2341                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2342                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2343                 prev = p1;              /* store old success ptr */
2344                 p1 = p2;                /* move to next posn */
2345                 ptr[3*vals+piece] = lval; /* assign new value  */
2346                 if(xd->verb>6)
2347                     fprintf(stderr,"  v %u (%ld)", ptr[3*vals],lval);
2348                 if( piece == 2 ) vals++; /* have a complete data value */
2349                 piece = (piece + 1) % 3;
2350             }
2351             if(xd->verb > 6) fputc('\n', stderr);
2352             break;
2353         }
2354         case NIFTI_TYPE_INT8: {
2355             char * ptr = (char *)dptr;
2356             p1 = cdata;
2357             prev = p1;
2358             /* vals could be < 0, but we must care for promotion to size_t */
2359             while( (vals < 0 || vals < *nvals) && p1 ) {
2360                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2361                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2362                 prev = p1;              /* store old success ptr */
2363                 p1 = p2;                /* move to next posn */
2364                 ptr[vals] = lval;       /* assign new value  */
2365                 if(xd->verb>6)fprintf(stderr,"  v %d (%ld)",ptr[vals],lval);
2366                 vals++;                 /* count new value   */
2367             }
2368             if(xd->verb > 6) fputc('\n', stderr);
2369             break;
2370         }
2371         case NIFTI_TYPE_UINT16: {
2372             unsigned short * ptr = (unsigned short *)dptr;
2373             p1 = cdata;
2374             prev = p1;
2375             while( (vals < 0 || vals < *nvals) && p1 ) {
2376                 lval = strtol(p1, &p2, 10);   /* try to read next value */
2377                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2378                 prev = p1;              /* store old success ptr */
2379                 p1 = p2;                /* move to next posn */
2380                 ptr[vals] = lval;       /* assign new value  */
2381                 if(xd->verb>6)fprintf(stderr,"  v %d (%ld)",ptr[vals],lval);
2382                 vals++;                 /* count new value   */
2383             }
2384             if(xd->verb > 6) fputc('\n', stderr);
2385             break;
2386         }
2387         case NIFTI_TYPE_INT64: {
2388             long long * ptr = (long long *)dptr;
2389             long long llval;
2390             p1 = cdata;
2391             prev = p1;
2392             while( (vals < 0 || vals < *nvals) && p1 ) {
2393 #if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
2394                 llval = strtol(p1, &p2, 10);   /* There is no strtoll defined in MS VC++ */
2395 #else
2396                 llval = strtoll(p1, &p2, 10);   /* try to read next value */
2397 #endif
2398                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2399                 prev = p1;              /* store old success ptr */
2400                 p1 = p2;                /* move to next posn */
2401                 ptr[vals] = llval;      /* assign new value  */
2402                 if(xd->verb>6)fprintf(stderr,"  v %lld (%lld)",ptr[vals],llval);
2403                 vals++;                 /* count new value   */
2404             }
2405             if(xd->verb > 6) fputc('\n', stderr);
2406             break;
2407         }
2408         case NIFTI_TYPE_COMPLEX128: {
2409             /* there are 2 pieces to each complex, so read and insert 1 at a
2410                time, and increment 'vals' only after each second piece */
2411             static int piece = 0;
2412             double * ptr = (double *)dptr;
2413             p1 = cdata;
2414             prev = p1;
2415             while( (vals < 0 || vals < *nvals) && p1 ) {
2416                 dval = strtod(p1, &p2); /* try to read next value */
2417                 if( p1 == p2 ) break;   /* nothing read, terminate loop */
2418                 prev = p1;              /* store old success ptr */
2419                 p1 = p2;                /* move to next posn */
2420                 ptr[2*vals+piece] = dval; /* assign new value  */
2421                 if(xd->verb>6) fprintf(stderr,"  v %f (%f)", ptr[2*vals],dval);
2422                 if( piece == 1 ) vals++; /* have a complete data value */
2423                 piece = 1-piece;
2424             }
2425             if(xd->verb > 6) fputc('\n', stderr);
2426             break;
2427         }
2428     }
2429 
2430     /* update the number of values processed */
2431     if( vals > 0 ) (*nvals) -= vals;
2432 
2433     /* ponder remaining: if *p1 is space, look from there, else from prev */
2434     if( p1 ){
2435         if( isspace(*p1) ) {
2436             remain = cdlen - (p1 - cdata);
2437             *mod_prev = 0;
2438         }
2439         else if( prev ) {
2440             remain = cdlen - (prev - cdata);
2441             *mod_prev = 1;  /* still looking at previous val */
2442         }
2443     }
2444 
2445     /* if only whitespace left, ignore */
2446     if( whitespace_len(cdata + (cdlen-remain), remain) == remain )
2447         remain = 0;
2448 
2449     if(xd->verb > 6) fprintf(stderr,"-- DA: remain = %d\n", remain);
2450 
2451     return remain;
2452 }
2453 
cb_instr(void * udata,const char * target,const char * data)2454 static void XMLCALL cb_instr(void *udata, const char *target, const char *data)
2455 {
2456     gxml_data * xd = (gxml_data *)udata;
2457     if( xd->verb > 3 ){
2458         show_depth(xd->depth, 1, stderr);
2459         fprintf(stderr, "instr: %s='%s'\n",target,data);
2460     }
2461 }
2462 
cb_comment(void * udata,const char * str)2463 static void XMLCALL cb_comment(void *udata, const char * str)
2464 {
2465     gxml_data * xd = (gxml_data *)udata;
2466     if( xd->verb > 1 ){
2467         show_depth(xd->depth, 1, stderr);
2468         fprintf(stderr, "comment: '%s'\n",str);
2469     }
2470 }
2471 
cb_cdata_start(void * udata)2472 static void XMLCALL cb_cdata_start(void *udata)
2473 {
2474     gxml_data * xd = (gxml_data *)udata;
2475 
2476     if( xd->verb > 3 ){
2477         show_depth(xd->depth, 1, stderr);
2478         fprintf(stderr, "cdata_start\n");
2479     }
2480     (void)epush(xd, GXML_ETYPE_CDATA, enames[GXML_ETYPE_CDATA], NULL);
2481 }
2482 
cb_cdata_end(void * udata)2483 static void XMLCALL cb_cdata_end(void *udata)
2484 {
2485     gxml_data * xd = (gxml_data *)udata;
2486     epop(xd, GXML_ETYPE_CDATA, enames[GXML_ETYPE_CDATA]);
2487 }
2488 
cb_default(void * udata,const char * str,int length)2489 static void XMLCALL cb_default(void *udata, const char * str, int length)
2490 {
2491     gxml_data * xd = (gxml_data *)udata;
2492     int wlen = whitespace_len(str,length);
2493     int len = length;
2494 
2495     if( len == wlen )
2496     {
2497         if( xd->verb < 4 ) return;
2498 
2499         str = "whitespace";     /* just note the whitespace */
2500         len = strlen(str);
2501     }
2502 
2503     if( xd->verb > 3 ){
2504         show_depth(xd->depth, 1, stderr);
2505         fprintf(stderr, "default XML element [%d]: '%.*s'\n",length,len,str);
2506     }
2507 }
2508 
cb_xml_dec(void * udata,const char * ver,const char * enc,int standalone)2509 static void XMLCALL cb_xml_dec(void *udata, const char * ver,
2510                                const char * enc, int standalone)
2511 {
2512     gxml_data * xd = (gxml_data *)udata;
2513     if( xd->verb > 2 ){
2514         show_depth(xd->depth, 1, stderr);
2515         fprintf(stderr, "xmldec ver = %s, enc = %s, standalone = %d\n",
2516                 ver,enc,standalone);
2517     }
2518 }
2519 
cb_start_doctype(void * udata,const char * doctype,const char * sysid,const char * pubid,int has_subset)2520 static void XMLCALL cb_start_doctype(void *udata, const char * doctype,
2521                 const char * sysid, const char * pubid, int has_subset )
2522 {
2523     gxml_data * xd = (gxml_data *)udata;
2524     if( xd->verb > 2 ){
2525         show_depth(xd->depth, 1, stderr);
2526         /* check for NULL in optional strings   4 Mar 2010 */
2527         fprintf(stderr, "start_doctype, dt='%s', sid='%s',pid='%s', sub=%d\n",
2528                doctype, sysid?sysid:"NULL", pubid?pubid:"NULL", has_subset);
2529     }
2530 }
2531 
cb_end_doctype(void * udata)2532 static void XMLCALL cb_end_doctype(void *udata)
2533 {
2534     gxml_data * xd = (gxml_data *)udata;
2535     if( xd->verb > 2 ){
2536         show_depth(xd->depth, 1, stderr);
2537         fprintf(stderr, "end_doctype\n");
2538     }
2539 }
2540 
cb_elem_dec(void * udata,const char * ename,XML_Content * content)2541 static void XMLCALL cb_elem_dec(void *udata, const char * ename,
2542                                              XML_Content * content)
2543 {
2544     gxml_data * xd = (gxml_data *)udata;
2545     if( xd->verb > 2 ){
2546         show_depth(xd->depth, 1, stderr);
2547         fprintf(stderr,"%s: type=%d, quant=%d, name=%s, numc=%d, cp=%p\n",
2548                 ename, content->type, content->quant, content->name,
2549                 content->numchildren, (void *)content->children);
2550     }
2551 }
2552 
init_xml_parser(void * user_data)2553 static XML_Parser init_xml_parser( void * user_data )
2554 {
2555     XML_Parser parser;
2556 
2557     parser = XML_ParserCreate(NULL);
2558     XML_SetUserData(parser, user_data);
2559     XML_SetStartElementHandler(parser, cb_start_ele);
2560     XML_SetEndElementHandler(parser, cb_end_ele);
2561     XML_SetCharacterDataHandler(parser, cb_char);
2562     XML_SetProcessingInstructionHandler(parser, cb_instr);
2563     XML_SetCommentHandler(parser, cb_comment);
2564     XML_SetStartCdataSectionHandler(parser, cb_cdata_start);
2565     XML_SetEndCdataSectionHandler(parser, cb_cdata_end);
2566     XML_SetDefaultHandler(parser, cb_default);
2567     XML_SetXmlDeclHandler(parser, cb_xml_dec);
2568     XML_SetStartDoctypeDeclHandler(parser, cb_start_doctype);
2569     XML_SetEndDoctypeDeclHandler(parser, cb_end_doctype);
2570     XML_SetElementDeclHandler(parser, cb_elem_dec);
2571 
2572     if( GXD.verb > 3 ) fprintf(stderr,"-- parser initialized\n");
2573 
2574     return parser;
2575 }
2576 
2577 
show_stack(char * mesg,gxml_data * xd)2578 static int show_stack(char * mesg, gxml_data * xd)
2579 {
2580     int c;
2581     if( !xd ) return 1;
2582     if( mesg ) fputs(mesg, stderr);
2583     fprintf(stderr,"stack[%d]", xd->depth);
2584     for( c = 0; c < xd->depth; c++ )
2585         fprintf(stderr," : %s", enames[xd->stack[c]]);
2586     fputc('\n', stderr);
2587     return 0;
2588 }
2589 
stack_is_valid(gxml_data * xd)2590 static int stack_is_valid(gxml_data * xd)
2591 {
2592     int valid, etype, parent, bad_parent;
2593 
2594     if( xd->depth  < 0 ) return 0;
2595     if( xd->depth == 0 ) return 1;
2596 
2597     etype = xd->stack[xd->depth-1];         /* depth is at least 1 */
2598 
2599     /* process depth 1 separately, so we can assume a parent later */
2600     if( xd->depth == 1 ) {
2601         if( etype != GXML_ETYPE_GIFTI ) {
2602             show_stack("** invalid element on ", xd);
2603             return 0;
2604         }
2605         return 1;
2606     }
2607 
2608     /* verify proper parent (or invalid type) */
2609     valid = 1;
2610     bad_parent = 0;
2611     parent = xd->stack[xd->depth-2];    /* depth is at least 2 */
2612     switch( etype ) {
2613         default:
2614         case GXML_ETYPE_INVALID:
2615         case GXML_ETYPE_GIFTI:   /* should only be at depth 1 */
2616             valid = 0;
2617             break;
2618         case GXML_ETYPE_META:
2619             if( parent != GXML_ETYPE_GIFTI &&
2620                 parent != GXML_ETYPE_DATAARRAY )       bad_parent = 1;
2621             break;
2622         case GXML_ETYPE_MD:
2623             if( parent != GXML_ETYPE_META )            bad_parent = 1;
2624             break;
2625         case GXML_ETYPE_NAME:
2626             if( parent != GXML_ETYPE_MD )              bad_parent = 1;
2627             break;
2628         case GXML_ETYPE_VALUE:
2629             if( parent != GXML_ETYPE_MD )              bad_parent = 1;
2630             break;
2631         case GXML_ETYPE_LABELTABLE:
2632             if( parent != GXML_ETYPE_GIFTI )           bad_parent = 1;
2633             break;
2634         case GXML_ETYPE_LABEL:
2635             if( parent != GXML_ETYPE_LABELTABLE )      bad_parent = 1;
2636             break;
2637         case GXML_ETYPE_DATAARRAY:
2638             if( parent != GXML_ETYPE_GIFTI )           bad_parent = 1;
2639             break;
2640         case GXML_ETYPE_CSTM:
2641             if( parent != GXML_ETYPE_DATAARRAY )       bad_parent = 1;
2642             break;
2643         case GXML_ETYPE_DATA:
2644             if( parent != GXML_ETYPE_DATAARRAY )       bad_parent = 1;
2645             break;
2646         case GXML_ETYPE_DATASPACE:
2647             if( parent != GXML_ETYPE_CSTM )            bad_parent = 1;
2648             break;
2649         case GXML_ETYPE_XFORMSPACE:
2650             if( parent != GXML_ETYPE_CSTM )            bad_parent = 1;
2651             break;
2652         case GXML_ETYPE_MATRIXDATA:
2653             if( parent != GXML_ETYPE_CSTM )            bad_parent = 1;
2654             break;
2655         case GXML_ETYPE_CDATA:
2656             if( parent != GXML_ETYPE_NAME       &&
2657                 parent != GXML_ETYPE_VALUE      &&
2658                 parent != GXML_ETYPE_LABEL      &&
2659                 parent != GXML_ETYPE_DATASPACE  &&
2660                 parent != GXML_ETYPE_XFORMSPACE &&
2661                 parent != GXML_ETYPE_MATRIXDATA )      bad_parent = 1;
2662             break;
2663     }
2664 
2665     /* possibly print a message if the stack looks bad */
2666     if( bad_parent && GXD.verb )
2667         fprintf(stderr,"** %s: bad parent '%s'\n",enames[etype],enames[parent]);
2668     if( (!valid || bad_parent) && GXD.verb > 1 ) show_stack("** invalid ", xd);
2669 
2670     return valid;
2671 }
2672 
2673 /* if bsize is no longer correct, update it and realloc the buffer */
reset_xml_buf(gxml_data * xd,char ** buf,int * bsize)2674 static int reset_xml_buf(gxml_data * xd, char ** buf, int * bsize)
2675 {
2676     if( *bsize == xd->buf_size ) {
2677         if( xd->verb > 3 )
2678             fprintf(stderr,"-- buffer kept at %d bytes\n", *bsize);
2679         return 0;
2680     }
2681 
2682     if( xd->verb > 2 )
2683         fprintf(stderr,"++ update buf, %d to %d bytes\n",*bsize,xd->buf_size);
2684 
2685     *bsize = xd->buf_size;
2686     *buf = (char *)realloc(*buf, *bsize * sizeof(char));
2687 
2688     if( ! *buf ) {
2689         fprintf(stderr,"** failed to alloc %d bytes of xml buf!\n", *bsize);
2690         *bsize = 0;
2691         return 1;
2692     }
2693 
2694     return 0;
2695 }
2696 
2697 /* decide how big a processing buffer should be
2698    (either for a small xform matrix or a Data element)
2699 */
partial_buf_size(long long nbytes)2700 static int partial_buf_size(long long nbytes)
2701 {
2702     int ibytes = (int)nbytes;    /* never more than 10 MB, anyway */
2703 
2704     if( ibytes <= GXML_MIN_BSIZE ) return GXML_MIN_BSIZE;
2705     if( ibytes <= 64*1024 )        return ibytes;
2706 
2707     if( ibytes <= 10*1024*1024 )  /* divide by 10, but round up to a block */
2708         return (ibytes/10 + 0xfff) & ~0xfff;
2709 
2710     return 1024*1024;
2711 }
2712 
2713 /* update xd->buf_size, used prior to reset_xml_buf */
update_xml_buf_size(gxml_data * xd,long long bytes)2714 static int update_xml_buf_size(gxml_data * xd, long long bytes)
2715 {
2716     int new_size;
2717 
2718     if( !xd || bytes < 0 ){
2719         if( xd->verb > 1 )
2720             fprintf(stderr,"** bad update_xml_buf_size with %p and %lld\n",
2721                     (void *)xd,bytes);
2722         return 0;
2723     }
2724 
2725     new_size = partial_buf_size(bytes);
2726     if( new_size != xd->buf_size ){
2727         if( xd->verb > 2 )
2728             fprintf(stderr,"++ update XML buf size, %d to %d (for %lld)\n",
2729                     xd->buf_size, new_size, bytes);
2730         xd->buf_size = new_size;
2731     }
2732 
2733     return 0;
2734 }
2735 
2736 
2737 /* used to update any buffer, as the pointer address is passed in */
update_partial_buffer(char ** buf,int * blen,long long bytes,int full)2738 static int update_partial_buffer(char ** buf, int * blen, long long bytes,
2739                                  int full)
2740 {
2741     int bsize = partial_buf_size(bytes);
2742 
2743     if( full ) bsize = bytes;   /* want entire buffer */
2744 
2745     if( !buf || !blen || bytes <= 0 ) {
2746         fprintf(stderr,"** UPB: bad params (%p,%p,%lld)\n",
2747                 (void *)buf, (void *)blen, bytes);
2748         return 1;
2749     }
2750 
2751     /* just make sure we have a text buffer to work with */
2752     if( *buf || *blen != bsize ) {
2753         if( GXD.verb > 2 )
2754             fprintf(stderr,"++ UPB, alloc %d bytes (from %lld, %d) for buff\n",
2755                     bsize, bytes, full);
2756         *buf = (char *)realloc(*buf, bsize * sizeof(char));
2757         if( !*buf ) {
2758             fprintf(stderr,"** UPB: cannot alloc %d bytes for buffer\n",bsize);
2759             return 1;
2760         }
2761         *blen = bsize;
2762     }
2763 
2764     return 0;
2765 }
2766 
gxml_write_gifti(gxml_data * xd,FILE * fp)2767 static int gxml_write_gifti(gxml_data * xd, FILE * fp)
2768 {
2769     gifti_image * gim = xd->gim;
2770 
2771     int c, offset;
2772     int first = 1;  /* first attr to print? */
2773 
2774     if( !gim || !fp ) return 1;
2775 
2776     if( xd->verb > 1 )
2777         fprintf(stderr,"++ gifti image, numDA = %d, size = %lld MB\n",
2778                 gim->numDA, gifti_gim_DA_size(gim,1));
2779 
2780     gxml_write_preamble(fp);
2781     fprintf(fp,"<%s",enames[GXML_ETYPE_GIFTI]);
2782     if(gim->version){ fprintf(fp," Version=\"%s\"", gim->version); first = 0; }
2783     /* add space if no version, requested by E Anderson */
2784     fprintf(fp,"%sNumberOfDataArrays=\"%d\"", first ? " " : "  ", gim->numDA);
2785 
2786     /* add any extra attributes */
2787     offset = strlen(enames[GXML_ETYPE_GIFTI]) + 2;
2788     ewrite_ex_atrs(xd, &gim->ex_atrs, offset, 0, fp);
2789     fputs(">\n",fp);
2790 
2791     xd->depth++;
2792     ewrite_meta(xd, &gim->meta, fp);
2793     ewrite_LT(xd, &gim->labeltable, 1, fp);
2794 
2795     /* write the giiDataArray */
2796     if(!gim->darray) {
2797         if( xd->verb > 0 ) fprintf(stderr,"** gifti_image, missing darray\n");
2798     } else {
2799         for( c = 0; c < gim->numDA; c++ )
2800             ewrite_darray(xd, gim->darray[c], fp);
2801     }
2802 
2803     xd->depth--;
2804     fprintf(fp,"</%s>\n",enames[GXML_ETYPE_GIFTI]);
2805 
2806     return 0;
2807 }
2808 
ewrite_darray(gxml_data * xd,giiDataArray * da,FILE * fp)2809 static int ewrite_darray(gxml_data * xd, giiDataArray * da, FILE * fp)
2810 {
2811     int  spaces = xd->indent * xd->depth;
2812     int  offset, c;
2813     char dimstr[5] = "Dim0";
2814 
2815     if( xd->verb > 3 ) fprintf(stderr,"++ write giiDataArray\n");
2816 
2817     if( !da ) return 0;
2818 
2819     offset = strlen(enames[GXML_ETYPE_DATAARRAY]) + 2 + spaces;
2820     fprintf(fp, "%*s<DataArray", spaces, "");
2821 
2822     /* print attributes */
2823     ewrite_str_attr("Intent", gifti_intent_to_string(da->intent), offset,1,fp);
2824     ewrite_str_attr("DataType", gifti_datatype2str(da->datatype), offset,0,fp);
2825     ewrite_str_attr("ArrayIndexingOrder",
2826                     gifti_list_index2string(gifti_index_order_list,da->ind_ord),
2827                     offset,0,fp);
2828     ewrite_int_attr("Dimensionality", da->num_dim, offset, 0, fp);
2829     for( c = 0; c < da->num_dim; c++ ) {
2830         ewrite_int_attr(dimstr, da->dims[c], offset, 0, fp);
2831         dimstr[3]++;  /* too devious??  iterate '0', '1', ... */
2832     }
2833     ewrite_str_attr("Encoding",
2834         gifti_list_index2string(gifti_encoding_list,da->encoding),offset,0,fp);
2835     ewrite_str_attr("Endian",   /* set endian to that of this CPU */
2836         gifti_list_index2string(gifti_endian_list, gifti_get_this_endian()),
2837                                 offset,0,fp);
2838     ewrite_str_attr("ExternalFileName", da->ext_fname, offset, 0, fp);
2839     if( da->ext_fname && *da->ext_fname )
2840         ewrite_long_long_attr("ExternalFileOffset",da->ext_offset, offset,0,fp);
2841     else
2842         ewrite_str_attr("ExternalFileOffset", NULL, offset, 0, fp);
2843     fprintf(fp, ">\n");
2844 
2845     /* write sub-elements */
2846     xd->depth++;
2847     ewrite_meta(xd, &da->meta, fp);
2848     for( c = 0; c < da->numCS; c++ )
2849         ewrite_coordsys(xd, da->coordsys[c], fp);
2850     ewrite_data(xd, da, fp);
2851     xd->depth--;
2852 
2853     fprintf(fp, "%*s</DataArray>\n", spaces, "");
2854 
2855     return 0;
2856 }
2857 
2858 
2859 /* this depends on ind_ord, how to write out lines */
ewrite_data(gxml_data * xd,giiDataArray * da,FILE * fp)2860 static int ewrite_data(gxml_data * xd, giiDataArray * da, FILE * fp)
2861 {
2862     long long c, rows, cols;
2863     int       spaces = xd->indent * xd->depth;
2864     int       errs = 0;
2865 
2866     if( !da ) return 0;         /* okay, may not exist */
2867 
2868     if( xd->verb > 3 )
2869         fprintf(stderr,"++ write %s Data\n",
2870                 gifti_list_index2string(gifti_encoding_list, da->encoding));
2871 
2872     /* maybe there is no data to write */
2873     if( !da->data || da->nvals <= 0 || da->nbyper <= 0 ) {
2874         fprintf(fp, "%*s<%s/>\n", spaces, "", enames[GXML_ETYPE_DATA]);
2875         return 0;
2876     }
2877 
2878     if (da->encoding == GIFTI_ENCODING_EXTBIN) /* then write as empty */
2879         fprintf(fp, "%*s<%s/>\n", spaces, "", enames[GXML_ETYPE_DATA]);
2880     else  /* write normal Data tag */
2881         fprintf(fp, "%*s<%s>", spaces, "", enames[GXML_ETYPE_DATA]);
2882 
2883     if( xd->dstore ) {
2884         if( da->encoding == GIFTI_ENCODING_ASCII ) {
2885             fprintf(fp, "\n");
2886             gifti_DA_rows_cols(da, &rows, &cols);  /* product will be nvals */
2887             for(c = 0; c < rows; c++ )
2888                 ewrite_data_line(da->data,da->datatype,c,cols,
2889                                  spaces+xd->indent,fp);
2890             fprintf(fp, "%*s", spaces, "");
2891         } else if( da->encoding == GIFTI_ENCODING_B64BIN ) {
2892             gxml_disp_b64_data(NULL, da->data, da->nvals*da->nbyper, fp);
2893         } else if( da->encoding == GIFTI_ENCODING_B64GZ ) {
2894 #ifdef HAVE_ZLIB   /* for compiling, higher level test elsewhere */
2895             uLongf blen = da->nvals*da->nbyper * 1.01 + 12; /* zlib.net */
2896             int    rv = 0;
2897             if( update_partial_buffer(&xd->zdata, &xd->zlen, blen, 1) )
2898                 return 1;
2899 
2900             rv = compress2((Bytef *)xd->zdata, &blen, da->data,
2901                            da->nvals*da->nbyper, xd->zlevel);
2902             if ( xd->verb > 2 )
2903                 fprintf(stderr,"-- compress buffer (%.2f%% of %lld bytes)...\n",
2904                         100.0*blen/(da->nvals*da->nbyper),da->nvals*da->nbyper);
2905             if( rv != Z_OK ) {
2906                 fprintf(stderr,"** zlib compression failure: ");
2907                 if( rv == Z_MEM_ERROR ) fprintf(stderr,"not enough memory\n");
2908                 if( rv == Z_BUF_ERROR ) fprintf(stderr,"buffer too short\n");
2909                 else                    fprintf(stderr,"unknown error %d\n",rv);
2910                 errs++;
2911             } else if ( xd->verb > 2 )
2912                 fprintf(stderr,"-- compression succeeded\n");
2913 
2914             gxml_disp_b64_data(NULL, xd->zdata, blen, fp);
2915 #else
2916             fprintf(stderr,"** ewrite_data: no ZLIB to compress with\n");
2917 #endif
2918         } else if (da->encoding == GIFTI_ENCODING_EXTBIN)  {
2919             /* write to external file */
2920             if( gifti_write_extern_DA_data(da) ) errs = 1;
2921         } else {
2922             fprintf(stderr,"** unknown data encoding, %d\n", da->encoding);
2923             errs = 1;
2924         }
2925     }
2926 
2927     if (da->encoding != GIFTI_ENCODING_EXTBIN)
2928         fprintf(fp, "</%s>\n", enames[GXML_ETYPE_DATA]);
2929 
2930     return errs;
2931 }
2932 
2933 #undef GII_B64_encode3
2934 #define GII_B64_encode3(a,b,c,w,x,y,z)                       \
2935      ( w = b64_encode_table[(a)>>2]                      ,   \
2936        x = b64_encode_table[((a & 3) << 4) | (b >> 4)]   ,   \
2937        y = b64_encode_table[((b & 0xF) << 2) | (c >> 6)] ,   \
2938        z = b64_encode_table[c & 0x3F]                     )
gxml_disp_b64_data(const char * mesg,const void * data,int len,FILE * fp)2939 static int gxml_disp_b64_data(const char *mesg, const void *data, int len,
2940                               FILE *fp)
2941 {
2942     const unsigned char * dp = (const unsigned char *)data;
2943     unsigned char         w, x, y, z;
2944     FILE                * stream;
2945     int                   c, rem = len % 3;
2946 
2947     stream = fp ? fp : stdout;
2948 
2949     if( !data || len < 1 ) return -1;
2950 
2951     if( mesg ) fputs(mesg, stream);
2952 
2953     /* first get all of the 3-byte blocks */
2954     for( c = 0; c < len/3; c++, dp += 3 ) {
2955         GII_B64_encode3(dp[0], dp[1], dp[2], w, x, y, z);
2956         fprintf(stream, "%c%c%c%c", w, x, y, z);
2957     }
2958 
2959     /* finish off the last bytes */
2960     if( rem == 1 ) {
2961         GII_B64_encode3(dp[0], 0, 0, w, x, y, z);
2962         fprintf(stream, "%c%c==", w, x);
2963     } else if ( rem == 2 ) {
2964         GII_B64_encode3(dp[0], dp[1], 0, w, x, y, z);
2965         fprintf(stream, "%c%c%c=", w, x, y);
2966     }
2967     /* else we're done */
2968 
2969     return 0;
2970 }
2971 
2972 
ewrite_coordsys(gxml_data * xd,giiCoordSystem * cs,FILE * fp)2973 static int ewrite_coordsys(gxml_data * xd, giiCoordSystem * cs, FILE * fp)
2974 {
2975     int c, spaces = xd->indent * xd->depth;
2976 
2977     if( !cs ) return 0;         /* okay, may not exist */
2978 
2979     if( xd->verb > 3 ) fprintf(stderr,"++ write giiCoordSystem\n");
2980 
2981     fprintf(fp, "%*s<%s>\n", spaces, "", enames[GXML_ETYPE_CSTM]);
2982     spaces += xd->indent;
2983 
2984     ewrite_text_ele(GXML_ETYPE_DATASPACE, cs->dataspace, NULL, spaces, 1, fp);
2985     ewrite_text_ele(GXML_ETYPE_XFORMSPACE, cs->xformspace, NULL, spaces, 1, fp);
2986 
2987     fprintf(fp, "%*s<MatrixData>\n", spaces, "");
2988     for(c = 0; c < 4; c++ )
2989         ewrite_double_line(cs->xform[c], 4, spaces+xd->indent, fp);
2990     fprintf(fp, "%*s</MatrixData>\n", spaces, "");
2991 
2992     spaces -= xd->indent;
2993     fprintf(fp, "%*s</%s>\n", spaces, "", enames[GXML_ETYPE_CSTM]);
2994 
2995     return 0;
2996 }
2997 
2998 
2999 /* write one 'row' ('cols' values) of data in text */
ewrite_data_line(void * data,int type,long long row,long long cols,int space,FILE * fp)3000 static int ewrite_data_line(void * data, int type, long long row,
3001                             long long cols, int space, FILE * fp)
3002 {
3003     int c;
3004     if( !data || row < 0 || cols <= 0 || !fp ) return 1;
3005 
3006     fprintf(fp, "%*s", space, "");
3007     switch( type ) {
3008         default :
3009             fprintf(stderr,"** write_data_line, unknown type %d\n",type);
3010             return -1;
3011         case NIFTI_TYPE_UINT8: {
3012             unsigned char * ptr = (unsigned char *)data + row * cols;
3013             for( c = 0; c < cols; c++ ) fprintf(fp, "%u ", ptr[c]);
3014             break;
3015         }
3016         case NIFTI_TYPE_INT16: {
3017             short * ptr = (short *)data + row * cols;
3018             for( c = 0; c < cols; c++ ) fprintf(fp, "%d ", ptr[c]);
3019             break;
3020         }
3021         case NIFTI_TYPE_INT32: {
3022             int * ptr = (int *)data + row * cols;
3023             for( c = 0; c < cols; c++ ) fprintf(fp, "%d ", ptr[c]);
3024             break;
3025         }
3026         case NIFTI_TYPE_FLOAT32: {
3027             float * ptr = (float *)data + row * cols;
3028             for( c = 0; c < cols; c++ ) fprintf(fp, "%f ", ptr[c]);
3029             break;
3030         }
3031         case NIFTI_TYPE_COMPLEX64: {
3032             float * ptr = (float *)data + row * 2 * cols;
3033             for(c = 0; c < 2*cols; c+=2)fprintf(fp, "%f %f   ",ptr[c],ptr[c+1]);
3034             break;
3035         }
3036         case NIFTI_TYPE_FLOAT64: {
3037             double * ptr = (double *)data + row * cols;
3038             for( c = 0; c < cols; c++ ) fprintf(fp, "%f ", ptr[c]);
3039             break;
3040         }
3041         case NIFTI_TYPE_RGB24: {
3042             unsigned char * ptr = (unsigned char *)data + row * 3 * cols;
3043             for( c = 0; c < 3*cols; c+=3 )
3044                 fprintf(fp, "%u %u %u   ", ptr[c], ptr[c+1], ptr[c+2]);
3045             break;
3046         }
3047         case NIFTI_TYPE_INT8: {
3048             char * ptr = (char *)data + row * cols;
3049             for( c = 0; c < cols; c++ ) fprintf(fp, "%d ", ptr[c]);
3050             break;
3051         }
3052         case NIFTI_TYPE_UINT16: {
3053             unsigned short * ptr = (unsigned short *)data + row * cols;
3054             for( c = 0; c < cols; c++ ) fprintf(fp, "%u ", ptr[c]);
3055             break;
3056         }
3057         case NIFTI_TYPE_UINT32: {
3058             unsigned int * ptr = (unsigned int *)data + row * cols;
3059             for( c = 0; c < cols; c++ ) fprintf(fp, "%u ", ptr[c]);
3060             break;
3061         }
3062         case NIFTI_TYPE_INT64: {
3063             long long * ptr = (long long *)data + row * cols;
3064             for( c = 0; c < cols; c++ ) fprintf(fp, "%lld ", ptr[c]);
3065             break;
3066         }
3067         case NIFTI_TYPE_UINT64: {
3068             unsigned long long * ptr = (unsigned long long *)data + row * cols;
3069             for( c = 0; c < cols; c++ ) fprintf(fp, "%llu ", ptr[c]);
3070             break;
3071         }
3072         case NIFTI_TYPE_FLOAT128: {
3073             long double * ptr = (long double *)data + row * cols;
3074             for( c = 0; c < cols; c++ ) fprintf(fp, "%Lf ", ptr[c]);
3075             break;
3076         }
3077         case NIFTI_TYPE_COMPLEX128: {
3078             double * ptr = (double *)data + row * 2 * cols;
3079             for(c = 0; c < 2*cols; c+=2)fprintf(fp, "%f %f   ",ptr[c],ptr[c+1]);
3080             break;
3081         }
3082         case NIFTI_TYPE_COMPLEX256: {
3083             long double * ptr = (long double *)data + row * 2 * cols;
3084             for(c = 0; c<2*cols; c+=2)fprintf(fp, "%Lf %Lf   ",ptr[c],ptr[c+1]);
3085             break;
3086         }
3087     }
3088 
3089     fputc('\n', fp);
3090 
3091     return 0;
3092 }
3093 
3094 
ewrite_double_line(double * data,int nvals,int space,FILE * fp)3095 static int ewrite_double_line(double * data, int nvals, int space, FILE * fp)
3096 {
3097     int c;
3098     if( !data || nvals <= 0 || !fp ) return 1;
3099 
3100     fprintf(fp, "%*s", space, "");
3101     for( c = 0; c < nvals; c++ )        /* duplicate trailing space for diff */
3102         fprintf(fp, "%f ", data[c]);
3103     fputc('\n', fp);
3104 
3105     return 0;
3106 }
3107 
3108 
ewrite_text_ele(int ele,const char * cdata,const char * attr,int spaces,int in_CDATA,FILE * fp)3109 static int ewrite_text_ele(int ele, const char * cdata, const char * attr,
3110                            int spaces, int in_CDATA, FILE * fp)
3111 {
3112     int index = ele;
3113 
3114     if(ele < 0 || ele > GXML_MAX_ELEN) index = 0;  /* be safe */
3115 
3116     fprintf(fp, "%*s<%s%s>%s%s%s</%s>\n",
3117             spaces, "", enames[index],
3118             attr ? attr : "",
3119             in_CDATA ? "<![CDATA[" : "",
3120             cdata ? cdata : "",
3121             in_CDATA ? "]]>" : "",
3122             enames[index]);
3123 
3124     return 0;
3125 }
3126 
ewrite_LT(gxml_data * xd,giiLabelTable * lt,int in_CDATA,FILE * fp)3127 static int ewrite_LT(gxml_data *xd, giiLabelTable *lt, int in_CDATA, FILE *fp)
3128 {
3129     char    attr[256] = "";
3130     float * rgba;
3131     int     c, spaces = xd->indent * xd->depth;
3132 
3133     if( xd->verb > 3 ) fprintf(stderr,"++ write giiLabelTable\n");
3134 
3135     if( !lt || lt->length == 0 || !lt->key || !lt->label ) {
3136         fprintf(fp, "%*s<LabelTable/>\n", spaces, "");
3137         return 0;
3138     }
3139 
3140     fprintf(fp, "%*s<LabelTable>\n", spaces, "");
3141     rgba = lt->rgba;
3142     for( c = 0; c < lt->length; c++ ) {
3143         if( !lt->label[c] ) {
3144             if(xd->verb > 1) fprintf(stderr,"** label[%d] unset\n", c);
3145             continue;
3146         }
3147 
3148         /* store the Key and optional RGBA attributes */
3149         if( lt->rgba ) {
3150            sprintf(attr, " Key=\"%d\""
3151                          " Red=\"%g\" Green=\"%g\" Blue=\"%g\" Alpha=\"%g\"",
3152                    lt->key[c], rgba[0], rgba[1], rgba[2], rgba[3]);
3153            rgba += 4;
3154         } else
3155             sprintf(attr, " Key=\"%d\"", lt->key[c]);
3156 
3157         ewrite_text_ele(GXML_ETYPE_LABEL, lt->label[c], attr,
3158                         spaces+xd->indent, in_CDATA, fp);
3159     }
3160     fprintf(fp, "%*s</LabelTable>\n", spaces, "");
3161 
3162     return 0;
3163 }
3164 
3165 
ewrite_meta(gxml_data * xd,giiMetaData * md,FILE * fp)3166 static int ewrite_meta(gxml_data * xd, giiMetaData * md, FILE * fp)
3167 {
3168     int c, spaces = xd->indent * xd->depth;
3169 
3170     if( xd->verb > 3 ) fprintf(stderr,"++ write giiMetaData\n");
3171 
3172     if( !md || md->length == 0 || !md->name || !md->value ) {
3173         fprintf(fp, "%*s<MetaData/>\n", spaces, "");
3174         return 0;
3175     }
3176 
3177     if( xd->verb > 3 ) fprintf(stderr,"   MD length = %d\n", md->length);
3178 
3179     fprintf(fp, "%*s<MetaData>\n", spaces, "");
3180     for( c = 0; c < md->length; c++ ) {
3181         if( !md->name[c] ) {  /* allow empty value, but not name */
3182             if(xd->verb > 1) fprintf(stderr,"** MD[%d] unset\n", c);
3183             continue;
3184         }
3185 
3186         spaces += xd->indent;
3187         fprintf(fp,"%*s<MD>\n", spaces, "");
3188 
3189         spaces += xd->indent;
3190         ewrite_text_ele(GXML_ETYPE_NAME, md->name[c], NULL, spaces, 1,fp);
3191         ewrite_text_ele(GXML_ETYPE_VALUE,md->value[c],NULL, spaces, 1,fp);
3192         spaces -= xd->indent;
3193 
3194         fprintf(fp,"%*s</MD>\n", spaces, "");
3195         spaces -= xd->indent;
3196     }
3197     fprintf(fp, "%*s</MetaData>\n", spaces, "");
3198 
3199     return 0;
3200 }
3201 
3202 
3203 /* print a list of attributes, indented to the same level */
ewrite_ex_atrs(gxml_data * xd,nvpairs * nvp,int offset,int first,FILE * fp)3204 static int ewrite_ex_atrs(gxml_data * xd, nvpairs * nvp, int offset,
3205                           int first, FILE * fp)
3206 {
3207     int c, spaces = xd->indent * xd->depth + offset;
3208 
3209     if(xd->verb > 2) fprintf(stderr,"++ write %d ex_atr's\n", nvp->length);
3210 
3211     for( c = 0; c < nvp->length; c++ ) {
3212         ewrite_str_attr(nvp->name[c], nvp->value[c], spaces, first, fp);
3213         if( first ) first = 0;
3214     }
3215 
3216     return 0;
3217 }
3218 
3219 
ewrite_int_attr(const char * name,int value,int spaces,int first,FILE * fp)3220 static int ewrite_int_attr(const char *name, int value, int spaces,
3221                            int first, FILE * fp)
3222 {
3223     fprintf(fp, "%s%*s%s=\"%d\"",
3224             (first) ? "" : "\n",        /* maybe a newline   */
3225             (first) ?  1 : spaces, "",  /* 1 or many spaces  */
3226             name, value);
3227     return 0;
3228 }
3229 
3230 
ewrite_long_long_attr(const char * name,long long value,int spaces,int first,FILE * fp)3231 static int ewrite_long_long_attr(const char *name, long long value, int spaces,
3232                                  int first, FILE * fp)
3233 {
3234     fprintf(fp, "%s%*s%s=\"%lld\"",
3235             (first) ? "" : "\n",        /* maybe a newline   */
3236             (first) ?  1 : spaces, "",  /* 1 or many spaces  */
3237             name, value);
3238     return 0;
3239 }
3240 
3241 
ewrite_str_attr(const char * name,const char * value,int spaces,int first,FILE * fp)3242 static int ewrite_str_attr(const char * name, const char * value, int spaces,
3243                            int first, FILE * fp)
3244 {
3245     fprintf(fp, "%s%*s%s=\"%s\"",
3246             (first) ? "" : "\n",        /* maybe a newline   */
3247             (first) ?  1 : spaces, "",  /* 1 or many spaces  */
3248             name, value ? value : "");
3249     return 0;
3250 }
3251 
3252 
gxml_write_preamble(FILE * fp)3253 static int gxml_write_preamble(FILE * fp)
3254 {
3255     char * version  = GIFTI_XML_VERSION;
3256     char * encoding = GIFTI_XML_ENCODING;
3257     char * dtd      = GIFTI_XML_DTD_SOURCE;
3258 
3259     fprintf(fp, "<?xml version=\"%s\" encoding=\"%s\"?>\n", version, encoding);
3260     fprintf(fp, "<!DOCTYPE GIFTI SYSTEM \"%s\">\n", dtd);
3261 
3262     return 0;
3263 }
3264 
disp_gxml_data(char * mesg,gxml_data * dp,int show_all)3265 static int disp_gxml_data(char * mesg, gxml_data * dp, int show_all )
3266 {
3267     if( mesg ) fputs(mesg, stderr);
3268 
3269     if( !dp ) return 1;
3270 
3271     fprintf(stderr,"gxml_data :\n"
3272                 "   verb        : %d\n"
3273                 "   dstore      : %d\n"
3274                 "   indent      : %d\n"
3275                 "   buf_size    : %d\n"
3276                 "   b64_check   : %d\n"
3277                 "   zlevel      : %d\n"
3278                 "   perm_by_iord: %d\n"
3279                 "   da_len      : %d\n"
3280            , dp->verb, dp->dstore, dp->indent, dp->buf_size, dp->b64_check,
3281              dp->zlevel, dp->perm_by_iord, dp->da_len);
3282 
3283     if( show_all )
3284         fprintf(stderr,
3285                 "   da_list     : %p\n"
3286                 "   da_ind      : %d\n"
3287                 "   eleDA       : %d\n"
3288                 "   expDA       : %d\n"
3289                 "   b64_errors  : %d\n"
3290                 "   errors      : %d\n"
3291                 "   skip        : %d\n"
3292                 "   depth       : %d\n"
3293                 "   dind        : %lld\n"
3294                 "   clen        : %d\n"
3295                 "   doff        : %d\n"
3296                 "   zlen        : %d\n"
3297                 "   cdata       : %p\n"
3298                 "   xdata       : %p\n"
3299                 "   ddata       : %p\n"
3300                 "   zdata       : %p\n"
3301                 "   gim         : %p\n"
3302            , (void *)dp->da_list, dp->da_ind, dp->eleDA, dp->expDA,
3303              dp->b64_errors, dp->errors, dp->skip, dp->depth, dp->dind,
3304              dp->clen, dp->doff, dp->zlen,
3305              (void *)dp->cdata, (void *)dp->xdata, (void *)dp->ddata,
3306              (void *)dp->zdata, (void *)dp->gim);
3307 
3308     return 0;
3309 }
3310