1 #include "niml_private.h"
2
3 /**********************************************************************/
4 /******* Functions to read and write data and group elements. *********/
5 /**********************************************************************/
6
7 static int scan_for_angles( NI_stream_type *, int ) ;
8
9 #define clear_buffer(ns) ( (ns)->nbuf = (ns)->npos = 0 )
10
11 /*--------------------------------------------------------------------*/
12 /*! Check if header_stuff marks this NIML element as a group.
13 ----------------------------------------------------------------------*/
14
header_stuff_is_group(header_stuff * hs)15 static int header_stuff_is_group( header_stuff *hs ) /* 24 Feb 2005 */
16 {
17 char *atr ;
18 if( hs == NULL ) return 0 ;
19 if( strcmp(hs->name,"ni_group") == 0 ) return 1 ;
20 atr = get_header_attribute( hs , "ni_form" ) ;
21 if( atr != NULL && strcmp(atr,"ni_group") == 0 ) return 1 ;
22 return 0 ;
23 }
24
25 /*--------------------------------------------------------------------*/
26 /*! Check if header_stuff marks NIML element as a processing instruction.
27 ----------------------------------------------------------------------*/
28
header_stuff_is_procins(header_stuff * hs)29 static int header_stuff_is_procins( header_stuff *hs )
30 {
31 if( hs == NULL ) return 0 ;
32 if( hs->name != NULL && hs->name[0] == '?' ) return 1 ;
33 return 0 ;
34 }
35
36 /*--------------------------------------------------------------------*/
37 /*! Write a simple processing instruction to the stream:
38 - "<?str ?>\n" will be written
39 - Return value is the number of bytes written
40 - Return 0 means that the stream wasn't ready to write
41 - Return -1 means an error happened, and nothing was written
42 - 17 Mar 2005 - RWCox
43 ----------------------------------------------------------------------*/
44
NI_write_procins(NI_stream_type * ns,char * str)45 int NI_write_procins( NI_stream_type *ns , char *str )
46 {
47 char *buf ; int jj ;
48
49 /* check inputs for good-ositifulness */
50
51 if( !NI_stream_writeable(ns) ) return -1 ; /* stupid user */
52 if( str == NULL || !IS_STRING_CHAR(*str) ) return -1 ;
53
54 /* check if stream is ready to take data */
55
56 if( ns->bad ){ /* socket that hasn't connected yet */
57 jj = NI_stream_goodcheck(ns,666) ; /* try to connect it */
58 if( jj < 1 ) return jj ; /* 0 is nothing yet, -1 is death */
59 } else { /* check if good ns has gone bad */
60 jj = NI_stream_writecheck(ns,666) ;
61 if( jj < 0 ) return jj ;
62 }
63
64 /* write the processing instruction: "<?str ?>\n" */
65
66 buf = (char *)malloc(strlen(str)+16) ;
67 sprintf( buf , "<?%s ?>\n" , str ) ;
68 jj = NI_stream_writestring( ns , buf ) ;
69
70 free((void *)buf) ; return jj ;
71 }
72
73
74 /*--------------------------------------------------------------------*/
75
76 static int read_header_only = 0 ;
NI_set_read_header_only(int r)77 void NI_set_read_header_only( int r ){ read_header_only=r ; } /* 23 Mar 2003 */
NI_get_read_header_only(void)78 int NI_get_read_header_only( void ){ return(read_header_only); }/*ZSS Feb 2012 */
79
80 static int skip_procins = 0 ;
NI_skip_procins(int r)81 void NI_skip_procins( int r ){ skip_procins = r ; } /* 03 Jun 2005 */
82
83 /*--------------------------------------------------------------------*/
84 /*! Read only the header part of the next element.
85 ----------------------------------------------------------------------*/
86
NI_read_element_header(NI_stream_type * ns,int msec)87 void * NI_read_element_header( NI_stream_type *ns , int msec )
88 {
89 void *nini ;
90 read_header_only = 1 ;
91 nini = NI_read_element( ns , msec ) ;
92 read_header_only = 0 ;
93 return nini ;
94 }
95
96 /*--------------------------------------------------------------------*/
97 /*! Read an element (maybe a group) from the stream, waiting up to
98 msec milliseconds for the header to appear. (After that, this
99 function may wait a long time for the rest of the element to
100 appear, unless the data stream comes to a premature end.)
101
102 Return is NULL if nothing can be read at this time. Otherwise,
103 use NI_element_type(return value) to determine if the element
104 read is a data element or a group element.
105
106 Note that a header that is longer than ns->bufsize will
107 never be read properly, since we must have the entire header in
108 the buffer before processing it. This should only be a problem
109 for deranged users (e.g., Ziad). If such a vast header is encountered,
110 it will be flushed.
111
112 If header start '<' and stop '>' are encountered, then this
113 function will read data until it can create an element, or until
114 the data stream is bad (i.e., the file ends, or the socket closes).
115
116 If NULL is returned, that can be because there is no data to
117 read even in the buffer, or because the input data stream has gone
118 bad (i.e., will return no more data ever). To check for the latter
119 case, use NI_stream_readcheck().
120
121 If a "<ni_do ... />" or "<?ni_do ... ?>" element is encountered,
122 it will not be returned to the caller. Instead, the actions it
123 orders will be carried out in function NI_do(), and the function
124 will loop back to find some other input.
125 ----------------------------------------------------------------------*/
126
NI_read_element(NI_stream_type * ns,int msec)127 void * NI_read_element( NI_stream_type *ns , int msec )
128 {
129 int ii,nn,nhs , num_restart ;
130 char *cstart , *cstop ;
131 header_stuff *hs ;
132 int start_time=NI_clock_time() , mleft ;
133
134 if( ns == NULL || ns->bad == MARKED_FOR_DEATH || ns->buf == NULL )
135 return NULL ; /* bad input stream */
136
137 #ifdef NIML_DEBUG
138 NI_dpr("ENTER NI_read_element\n") ;
139 #endif
140
141 if( msec < 0 ) msec = 999999999 ; /* a long time (11+ days) */
142
143 /* if we have a socket that hasn't connected,
144 then see if it can connect now */
145
146 if( ns->bad ){
147 nn = NI_stream_goodcheck( ns , msec ) ;
148 if( nn < 1 ) return NULL ; /* didn't connect */
149 }
150
151 /*-- Try to find the element header --*/
152
153 num_restart = 0 ;
154 HeadRestart: /* loop back here to retry */
155 num_restart++ ;
156 mleft = msec - (NI_clock_time()-start_time) ; /* time left */
157 if( num_restart > 1 && mleft <= 0 ) return NULL ; /* don't allow too many loops */
158
159 #ifdef NIML_DEBUG
160 NI_dpr("NI_read_element: HeadRestart scan_for_angles; num_restart=%d\n" ,
161 num_restart ) ;
162 #endif
163
164 nn = scan_for_angles( ns , 0 ) ; /* look for '<stuff>' */
165
166 /* didn't find it */
167
168 if( nn < 0 ){
169 #ifdef NIML_DEBUG
170 NI_dpr("NI_read_element: scan_for_angles() returns %d",nn) ;
171 #endif
172 if( NI_stream_readcheck(ns,1) < 0 ) return NULL ; /* connection lost */
173 #ifdef NIML_DEBUG
174 NI_dpr(" trying again") ;
175 #endif
176 NI_sleep(2); goto HeadRestart; /* try again */
177 }
178
179 #ifdef NIML_DEBUG
180 NI_dpr("NI_read_element: found '<'\n") ;
181 #endif
182
183 /* ns->buf[ns->npos] = opening '<' ; ns->buf[nn-1] = closing '>' */
184
185 /* see if we found '<>', which is meaningless,
186 or a trailer '</stuff>', which is illegal here */
187
188 if( nn - ns->npos <= 2 || ns->buf[ns->npos+1] == '/' ){
189 ns->npos = nn; NI_reset_buffer(ns); /* toss the '<..>', try again */
190 #ifdef NIML_DEBUG
191 NI_dpr("NI_read_element: illegal header found? skipping\n") ;
192 #endif
193 goto HeadRestart ;
194 }
195
196 /*----- Parse the header data and prepare to make an element! -----*/
197
198 #ifdef NIML_DEBUG
199 NI_dpr("NI_read_element: parsing putative header\n") ;
200 #endif
201
202 hs = parse_header_stuff( nn - ns->npos , ns->buf + ns->npos , &nhs ) ;
203
204 if( hs == NULL ){ /* something bad happened there */
205 fprintf(stderr,"NI_read_element: bad element header found!\n") ;
206 ns->npos = nn; NI_reset_buffer(ns); /* toss the '<..>', try again */
207 goto HeadRestart ;
208 }
209
210 /*----- If here, have parsed a header (and will not HeadRestart).
211 First, expunge the data bytes that were consumed to make
212 the header; that is, we can then start reading data from
213 ns->buf[ns->npos] .. ns->buf[ns->nbuf-1] --*/
214
215 ns->npos = nn ;
216
217 #ifdef NIML_DEBUG
218 NI_dpr("NI_read_element: header parsed successfully\n") ;
219 #endif
220
221 /*--------------- Now make an element of some kind ---------------*/
222
223 if( header_stuff_is_procins(hs) ){ /*--- a processing instruction ---*/
224
225 NI_procins *npi ;
226
227 if( strcmp(hs->name,"?ni_do") == 0 ){ /* 19 Apr 2005: special case! */
228 NI_element *nel ;
229 nel = make_empty_data_element( hs ) ; /* temporary element */
230 destroy_header_stuff( hs ) ;
231 NI_do( ns , nel ) ; /* do the stuff it says */
232 NI_free_element( nel ) ; /* then destroy it */
233 if( ns->bad == MARKED_FOR_DEATH || ns->buf == NULL ) return NULL ;
234 num_restart = 0 ; goto HeadRestart ;
235 }
236
237 /* 03 Jun 2005: if ordered to skip these things, do so */
238
239 if( skip_procins ){
240 destroy_header_stuff( hs ) ; num_restart = 0 ; goto HeadRestart ;
241 }
242
243 /* normal case: make a procins element and give it to the caller */
244
245 npi = NI_malloc(NI_procins,sizeof(NI_procins)) ;
246 npi->type = NI_PROCINS_TYPE ;
247 npi->name = NI_strdup( hs->name + 1 ) ; /* skip the '?' */
248
249 npi->attr_num = hs->nattr ;
250 if( npi->attr_num > 0 ){
251 npi->attr_lhs = hs->lhs ; hs->lhs = NULL ;
252 npi->attr_rhs = hs->rhs ; hs->rhs = NULL ;
253 } else {
254 npi->attr_lhs = npi->attr_rhs = NULL ;
255 }
256
257 destroy_header_stuff( hs ) ;
258
259 return npi ;
260
261 } /*--- end of reading a processing instruction ---*/
262
263 else if( header_stuff_is_group(hs) ){ /*---- a group element ----*/
264
265 NI_group *ngr ;
266 void *nini ;
267 int empty=hs->empty ;
268
269 read_header_only = 0 ; /* 23 Mar 2003 */
270
271 start_time = NI_clock_time() ; /* allow up to 10 sec for next */
272 msec = 9999 ; /* element to appear, before giving up */
273
274 ngr = make_empty_group_element( hs ) ; /* copies name and attributes */
275 destroy_header_stuff( hs ) ;
276 if( empty ) return ngr ; /* 03 Jun 2002: empty group is legal */
277
278 /* we now have to read the elements within the group */
279
280 num_restart = 0 ;
281 while(1){ /* loop to find an element */
282
283 #ifdef NIML_DEBUG
284 NI_dpr("NI_read_element: ni_group scan_for_angles; num_restart=%d\n",
285 num_restart ) ;
286 #endif
287
288 nn = scan_for_angles( ns , 10 ) ; /* find header/trailer '<...>' */
289
290 mleft = msec - (NI_clock_time()-start_time) ;
291 if( mleft < 0 ) mleft = 0 ;
292
293 if( nn <= 0 ){ /* didn't find it */
294 if( NI_stream_readcheck(ns,0) < 0 ) break ; /* real bad */
295 if( num_restart > 1 && mleft == 0 ) break ; /* time's up */
296 num_restart++ ;
297 continue ; /* try again (but not forever) */
298 }
299
300 /* check if we found a trailer element '</stuff>' */
301
302 if( ns->buf[ns->npos+1] == '/' ){ /* trailer */
303 ns->npos = nn ; /* so end the group */
304 break ;
305 }
306
307 /* not a trailer, so try to make an element out of it */
308
309 nini = NI_read_element( ns , mleft ) ; /* recursion! */
310 if( nini != NULL ){
311 NI_add_to_group( ngr , nini ) ; /* this is good */
312 num_restart = 0 ;
313 start_time = NI_clock_time() ; /* restart the wait clock */
314 } else { /* this is bad */
315 if( NI_stream_readcheck(ns,0) < 0 ) break ; /* real bad */
316 mleft = msec - (NI_clock_time()-start_time) ;
317 if( num_restart > 1 && mleft <= 0 ) break ; /* time's up */
318 num_restart++ ;
319 }
320 }
321
322 /* and we are done */
323
324 return ngr ;
325
326 } /* end of reading group element */
327
328 else { /*------------------------ a data element ---------------------*/
329
330 NI_element *nel ;
331 int form, swap, nbrow , row,col ;
332
333 nel = make_empty_data_element( hs ) ;
334 destroy_header_stuff( hs ) ;
335
336 /*-- check if this is an empty element --*/
337 if( nel == NULL || /* nel == NULL should never happen. */
338 nel->vec_rank == 0 || /* These other cases are indication */
339 nel->vec_num == 0 || /* that this is an 'empty' element. */
340 nel->vec_typ == NULL || /* ==> The header is all there is. */
341 nel->vec == NULL ||
342 nel->name[0] == '!' || /* Stupid XML declaration */
343 read_header_only ){
344
345 #ifdef NIML_DEBUG
346 NI_dpr("NI_read_element: returning empty element\n") ;
347 #endif
348
349 /*-- 23 Aug 2002: do something, instead of returning data? --*/
350
351 if( nel != NULL && strcmp(nel->name,"ni_do") == 0 ){
352 NI_do( ns , nel ) ;
353 NI_free_element( nel ) ;
354 if( ns->bad == MARKED_FOR_DEATH || ns->buf == NULL ) return NULL ;
355 num_restart = 0 ; goto HeadRestart ;
356 }
357
358 if( read_header_only && nel->vec != NULL ){
359 for( ii=0 ; ii < nel->vec_num ; ii++ ) NI_free(nel->vec[ii]) ;
360 NI_free(nel->vec) ; nel->vec = NULL ;
361 }
362
363 return nel ; /* default: return element */
364 }
365
366 /*-- If here, must read data from the buffer into nel->vec --*/
367
368 /* Find the form of the input */
369
370 form = NI_TEXT_MODE ; /* default is text mode */
371 swap = 0 ; /* and (obviously) don't byte swap */
372
373 ii = string_index( "ni_form" , nel->attr_num , nel->attr_lhs ) ;
374 if( ii >= 0 && nel->attr_rhs[ii] != NULL ){ /* parse ni_form=rhs */
375
376 /* binary or base64 mode? */
377
378 if( strstr(nel->attr_rhs[ii],"binary") != NULL )
379 form = NI_BINARY_MODE ;
380 else if( strstr(nel->attr_rhs[ii],"base64") != NULL ){
381 form = NI_BASE64_MODE ;
382 ns->b64_numleft = 0 ; /* 21 Apr 2005: reset Base64 leftovers */
383 }
384
385 /* check byteorder in header vs. this CPU */
386
387 if( form != NI_TEXT_MODE ){
388 int order=NI_MSB_FIRST ; /* default input byteorder */
389 if( strstr(nel->attr_rhs[ii],"lsb") != NULL ) order = NI_LSB_FIRST;
390 swap = ( order != NI_byteorder() ) ; /* swap bytes? */
391 }
392 }
393
394 /*-- 13 Feb 2003: Use new NI_read_columns() function to get data. --*/
395
396 if( form == NI_TEXT_MODE ) ii = NI_LTEND_MASK ; /* end on '<' char */
397 else if( swap ) ii = NI_SWAP_MASK ; /* swap binary data */
398 else ii = 0 ; /* no special flag */
399
400 row = NI_read_columns( ns ,
401 nel->vec_num, nel->vec_typ,
402 nel->vec_len, nel->vec , form, ii );
403
404 nel->vec_filled = (row >= 0) ? row : 0 ;
405 /* 27 Mar 2003: allow for case where vec_len is
406 inferred from how much data we read */
407
408 if( nel->vec_len == 0 ){
409 if( nel->vec_axis_len == NULL )
410 nel->vec_axis_len = NI_malloc(int, sizeof(int)) ;
411
412 nel->vec_axis_len[0] = nel->vec_len = nel->vec_filled ;
413 nel->vec_rank = 1 ;
414 }
415
416 /*-- Now scan for the end-of-element marker '</something>' and
417 skip all input bytes up to (and including) the final '>'. --*/
418
419 num_restart = 0 ;
420 TailRestart:
421 num_restart++ ;
422
423 if( num_restart < 99 ){ /* don't loop forever, dude */
424 int is_tail ;
425
426 #ifdef NIML_DEBUG
427 NI_dpr("NI_read_element: TailRestart scan_for_angles; num_restart=%d\n" ,
428 num_restart ) ;
429 #endif
430
431 nn = scan_for_angles( ns , 99 ) ; /* find '<...>' */
432
433 /* if we didn't find '<...>' at all,
434 then if the I/O stream is bad, just exit;
435 otherwise, try scanning for '<...>' again */
436
437 if( nn < 0 ){
438 if( NI_stream_readcheck(ns,0) < 0 ) return nel ;
439 goto TailRestart ;
440 }
441
442 /* we have '<...>', but make sure it starts with '</' */
443
444 is_tail = ( ns->buf[ns->npos+1] == '/' ) ;
445
446 if( !is_tail ){ /* no '/'? */
447 ns->npos = nn ; NI_reset_buffer(ns) ; /* skip '<...>' */
448 goto TailRestart ; /* and try again */
449 }
450
451 ns->npos = nn ; /* skip '</...>' and we are done here! */
452 }
453
454 /*-- And are done with the input stream and the data element! --*/
455
456 #ifdef NIML_DEBUG
457 NI_dpr("NI_read_element: returning filled data element\n") ;
458 #endif
459
460 /*-- 23 Aug 2002: do something, instead of returning data? --*/
461
462 if( strcmp(nel->name,"ni_do") == 0 ){
463 NI_do( ns , nel ) ;
464 NI_free_element( nel ) ;
465 num_restart = 0 ; goto HeadRestart ;
466 }
467
468 return nel ;
469
470 } /* end of reading data element */
471
472 return NULL ; /* should never be reached */
473 }
474
475 /*----------------------------------------------------------------------*/
476
477 #undef NVBUF
478 #define NVBUF 127 /* max num chars for one number */
479
480 #define IS_USELESS(c) ( isspace(c) || iscntrl(c) )
481 #define IS_CRLF(c) ( (c) == 0x0D || (c) == 0x0A )
482
483 /*----------------------------------------------------------------------*/
484 /*! From the NI_stream ns, starting at buffer position ns->npos, decode
485 one number into *val.
486 - Parameter ltend != 0 means to stop at '<' character [07 Jan 2003].
487 - ltend != 0 also means to skip lines starting with '#' [20 Mar 2003].
488 - ns->npos will be altered to reflect the current buffer position
489 (one after the last character processed) when all is done.
490 - Return value of this function is 1 if we succeeded, 0 if not.
491 ------------------------------------------------------------------------*/
492
NI_decode_one_double(NI_stream_type * ns,double * val,int ltend)493 int NI_decode_one_double( NI_stream_type *ns, double *val , int ltend )
494 {
495 int epos , num_restart, need_data, nn ;
496 char vbuf[NVBUF+1] ; /* number string from buffer */
497
498 /*-- check inputs for stupidness --*/
499
500 if( ns == NULL || ns->bad == MARKED_FOR_DEATH || val == NULL ) return 0 ;
501
502 /*--- might loop back here to check if have enough data for a number ---*/
503
504 num_restart = 0 ;
505 Restart:
506 num_restart++ ; need_data = 0 ;
507 if( num_restart > 19 ) return 0 ; /*** too much ==> give up ***/
508
509 #ifdef NIML_DEBUG
510 NI_dpr(" {restart: npos=%d nbuf=%d}",ns->npos,ns->nbuf) ;
511 #endif
512
513 /*-- advance over useless characters in the buffer --*/
514
515 while( ns->npos < ns->nbuf && IS_USELESS(ns->buf[ns->npos]) ) ns->npos++ ;
516
517 /*-- check if we ran into the closing '<' prematurely
518 (before any useful characters); if we did, then we are done --*/
519
520 if( ltend && ns->npos < ns->nbuf && ns->buf[ns->npos] == '<' ) return 0 ;
521
522 /*-- 20 Mar 2003: check if we ran into a comment character '#';
523 if we did, skip to the end of the line (or '<') --*/
524
525 if( ltend && ns->npos < ns->nbuf && ns->buf[ns->npos] == '#' ){
526 int npold = ns->npos ;
527 while( ns->npos < ns->nbuf && !IS_CRLF(ns->buf[ns->npos]) ){
528 if( ns->buf[ns->npos] == '<' ) return 0 ; /* STOP HERE! */
529 ns->npos++ ;
530 }
531 if( ns->npos < ns->nbuf ){ /* found end of line, so try again */
532 num_restart = 0 ; goto Restart ;
533 }
534 /* if here, didn't find '<' or end of line in buffer */
535 /* so reset pointer back to '#', then read more data */
536 ns->npos = npold ; need_data = 1 ;
537 }
538
539 /*-- if we need some data, try to get some --*/
540
541 if( !need_data ) /* need at least 2 unused */
542 need_data = (ns->nbuf-ns->npos < 2) ; /* bytes to decode a number */
543
544 /*-- An input value is decoded from a string of non-useless
545 characters delimited by a useless character (or by the
546 element closing '<').
547 Note that the 1st character we are now at is non-useless.
548 Scan forward to see if we have a useless character later. --*/
549
550 if( !need_data ){ /* so have at least 2 characters */
551
552 #ifdef NIML_DEBUG
553 nn = ns->nbuf-ns->npos ; if( nn > 19 ) nn = 19 ;
554 NI_dpr(" {buf=%.*s}" , nn , ns->buf+ns->npos ) ;
555 #endif
556
557 for( epos=ns->npos+1 ; epos < ns->nbuf ; epos++ )
558 if( ns->buf[epos] == '<' || IS_USELESS(ns->buf[epos]) ) break ;
559
560 /*- epos is either the delimiter position, or the end of data bytes -*/
561
562 need_data = (epos == ns->nbuf) ; /* no delimiter ==> need more data */
563
564 #ifdef NIML_DEBUG
565 if( need_data ) NI_dpr(" {eob}") ;
566 #endif
567
568 /*- If the string of characters we have is not yet
569 delimited, and it is too long to be a number,
570 throw out all the data in the buffer and quit. -*/
571
572 if( need_data && epos-ns->npos > NVBUF ){ clear_buffer(ns); return 0; }
573 }
574
575 /*-- read more data now if it is needed --*/
576
577 if( need_data ){
578
579 NI_reset_buffer(ns) ; /* discard used up data in buffer */
580
581 /*- read at least 1 byte,
582 waiting up to 666 ms (unless the data stream goes bad) -*/
583
584 #ifdef NIML_DEBUG
585 NI_dpr(" {fill buf}") ;
586 #endif
587 nn = NI_stream_fillbuf( ns , 1 , 666 ) ;
588
589 if( nn >= 0 ) goto Restart ; /* check if buffer is adequate now */
590
591 /*- if here, the stream went bad. If there are still
592 data bytes in the stream, we can try to interpret them.
593 Otherwise, must quit without success. -*/
594
595 if( ns->nbuf == 0 ){ ns->npos=0; return 0; } /* quitting */
596
597 epos = ns->nbuf ;
598 }
599
600 /*-- if here, try to interpret data bytes ns->npos .. epos-1 --*/
601
602 nn = epos-ns->npos ; if( nn > NVBUF ) nn = NVBUF ; /* # bytes to read */
603 memcpy( vbuf, ns->buf+ns->npos, nn ); vbuf[nn] = '\0'; /* put bytes in vbuf */
604 *val = 0.0 ; /* initialize val */
605 sscanf( vbuf , "%lf" , val ) ; /* interpret them */
606 ns->npos = epos ; return 1 ; /* retire undefeated */
607 }
608
609 /*----------------------------------------------------------------------*/
610 /*! From the NI_stream ns, starting at buffer position ns->npos, decode
611 one string into newly NI_malloc()-ed space pointed to by *str.
612 - Parameter ltend !=0 means to stop at '<' character [07 Jan 2003].
613 - ltend != 0 also means to skip lines starting with '#' [20 Mar 2003].
614 - Return value of this function is 1 if we succeeded, 0 if not.
615 - ns->npos will be altered to reflect the current buffer position
616 (one after the last character processed) when all is done.
617 ------------------------------------------------------------------------*/
618
NI_decode_one_string(NI_stream_type * ns,char ** str,int ltend)619 int NI_decode_one_string( NI_stream_type *ns, char **str , int ltend )
620 {
621 int epos , num_restart, need_data, nn , overbuf=0 ;
622 intpair sp ;
623
624 /*-- check inputs for stupidness --*/
625
626 if( ns == NULL || ns->bad == MARKED_FOR_DEATH || str == NULL ) return 0 ;
627
628 /*--- might loop back here to check if have enough data ---*/
629
630 num_restart = 0 ;
631 Restart:
632 num_restart++ ; need_data = 0 ;
633 if( num_restart > 19 ){
634 if( overbuf ){ /* 21 Nov 2007: warn if buffer was too small */
635 static int nov=0 ;
636 if( ++nov < 7 )
637 fprintf(stderr,"** ERROR: String runs past end of NIML buffer\n");
638 }
639 return 0 ; /*** give up ***/
640 }
641 if( num_restart > 2 && overbuf ){ /* 23 Nov 2007: auto-expand buffer */
642 nn = 2*NI_stream_getbufsize(ns) ;
643 if( nn > 0 ){
644 #if 0
645 static int nov=0 ;
646 if( ++nov < 7 )
647 fprintf(stderr,"** WARNING: long String expands NIML buffer to %d bytes\n",nn) ;
648 #endif
649 nn = NI_stream_setbufsize( ns , nn ) ; /* double down */
650 if( nn < 0 ) return 0 ; /*** buffer expand fails? give up ***/
651 }
652 }
653 overbuf = 0 ;
654
655 /*-- advance over useless characters in the buffer --*/
656
657 while( ns->npos < ns->nbuf && IS_USELESS(ns->buf[ns->npos]) ) ns->npos++ ;
658
659 /*-- check if we ran into the closing '<' prematurely
660 (before any useful characters); if we did, then we are done --*/
661
662 if( ltend && ns->npos < ns->nbuf && ns->buf[ns->npos] == '<' ) return 0 ;
663
664 /*-- 20 Mar 2003: check if we ran into a comment character '#';
665 if we did, skip to the end of the line (or '<') --*/
666
667 if( ltend && ns->npos < ns->nbuf && ns->buf[ns->npos] == '#' ){
668 int npold = ns->npos ;
669 while( ns->npos < ns->nbuf && !IS_CRLF(ns->buf[ns->npos]) ){
670 if( ns->buf[ns->npos] == '<' ) return 0 ; /* STOP HERE! */
671 ns->npos++ ;
672 }
673 if( ns->npos < ns->nbuf ){ /* found end of line, so try again */
674 num_restart = 0 ; goto Restart ;
675 }
676 /* if here, didn't find '<' or end of line in buffer */
677 /* so reset pointer back to '#', then read more data */
678 ns->npos = npold ; need_data = 1 ;
679 }
680
681 /*-- if we need some data, try to get some --*/
682
683 if( !need_data ) /* need at least 2 unused */
684 need_data = (ns->nbuf-ns->npos < 2) ; /* bytes to decode a string */
685
686 if( !need_data ){ /* so have at least 2 characters */
687
688 /* search for the string from here forward */
689
690 sp = find_string( ns->npos , ns->nbuf , ns->buf ) ;
691
692 need_data = (sp.i < 0) || /* didn't find a string */
693 (sp.j <= sp.i) || /* ditto */
694 (sp.j == ns->nbuf) ; /* hit end of data bytes */
695
696 overbuf = (sp.j == ns->nbuf) ; /* 21 Nov 2007: flag buffer overrun */
697 }
698
699 /*-- read more data now if it is needed --*/
700
701 if( need_data ){
702
703 NI_reset_buffer(ns) ; /* discard used up data in buffer */
704
705 /*- read at least 1 byte,
706 waiting up to 666 ms (unless the data stream goes bad) -*/
707
708 nn = NI_stream_fillbuf( ns , 1 , 666 ) ;
709
710 if( nn >= 0 ) goto Restart ; /* check if buffer is adequate now */
711
712 /*- if here, the stream went bad. If there are still
713 data bytes in the stream, we can try to interpret them.
714 Otherwise, must quit without success. -*/
715
716 if( ns->nbuf == 0 ){ ns->npos=0; return 0; } /* quitting */
717
718 sp.i = 0 ; sp.j = ns->nbuf ;
719 }
720
721 /*-- if here, data bytes sp.i .. sp.j-1 are the string --*/
722
723 nn = sp.j - sp.i ; /* length of string */
724 *str = NI_malloc(char, nn+1) ; /* make the string */
725 memcpy( *str , ns->buf+sp.i , nn ) ; /* copy data to string */
726 (*str)[nn] = '\0' ; /* terminate string */
727
728 /* skip close quote character, if present */
729
730 if( sp.j < ns->nbuf && IS_QUOTE_CHAR(ns->buf[sp.j]) ) sp.j++ ;
731
732 ns->npos = sp.j ; return 1 ;
733 }
734
735 /*----------------------------------------------------------------------*/
736 /*! Reset the unscanned bytes in the buffer to start at position 0
737 instead of position ns->npos; then set ns->npos to 0.
738 ------------------------------------------------------------------------*/
739
NI_reset_buffer(NI_stream_type * ns)740 void NI_reset_buffer( NI_stream_type *ns )
741 {
742 if( ns == NULL || ns->npos <= 0 || ns->nbuf <= 0 ) return ;
743 if( ns->buf == NULL || ns->bad == MARKED_FOR_DEATH ) return ;
744
745 if( ns->npos < ns->nbuf ){ /* haven't used up all data yet */
746 memmove( ns->buf, ns->buf+ns->npos, ns->nbuf-ns->npos ) ;
747 ns->nbuf -= ns->npos ;
748 } else {
749 ns->nbuf = 0 ; /* all data in buffer is used up */
750 }
751 ns->npos = 0 ; /* further scanning starts at beginning */
752 }
753
754 /*----------------------------------------------------------------------*/
755 /*! Scan stream for an element header or trailer:'<characters>',
756 starting at byte offset ns->npos, and waiting msec milliseconds.
757
758 Returns with the stream buffer set so that the opening '<' is at
759 ns->buf[ns->npos] and the closing '>' is at ns->buf[q-1], where q
760 is this function's return value. Note that read operations may
761 change ns->npos from its value when this function was called.
762
763 If the return value is -1, then we couldn't find a '<stuff>' string.
764 This may be due to:
765 - there is no '<...>' in the buffer, and we can't read from
766 the input stream; call NI_readcheck(ns,0) to confirm this
767 - time ran out (alas)
768 - The '<...' part filled the entire buffer space. In this case,
769 all the input buffer is thrown away - we don't support
770 headers or trailers this long!
771
772 01 Jun 2005: skip XML comments, which are of the form
773 "<!-- arbitrary text -->".
774 ------------------------------------------------------------------------*/
775
scan_for_angles(NI_stream_type * ns,int msec)776 static int scan_for_angles( NI_stream_type *ns, int msec )
777 {
778 int nn, epos, need_data, num_restart ;
779 char goal ;
780 int start_time = NI_clock_time() , mleft , nbmin ;
781 int caseb=0 ; /* 1 => force rescan even if time is up */
782
783 #ifdef NIML_DEBUG
784 NI_dpr("ENTER scan_for_angles\n") ;
785 #endif
786
787 if( ns == NULL ) return -1 ; /* bad input */
788
789 if( ns->buf == NULL || ns->bad == MARKED_FOR_DEATH ) return -1 ;
790
791 epos = ns->npos ;
792
793 if( msec < 0 ) msec = 999999999 ; /* a long time (11+ days) */
794
795 /*-- Will loop back here if we have to re-read/re-scan --*/
796
797 goal = '<' ; /* first goal is opening '<' (second goal is '>') */
798 num_restart = 0 ;
799 Restart: /* loop back here to retry */
800 num_restart++ ;
801 mleft = msec - (NI_clock_time()-start_time) ; /* time left */
802
803 if( num_restart > 3 && mleft <= 0 && !caseb ){ /* failure */
804 NI_reset_buffer(ns) ; /* and out of time */
805 return -1 ;
806 }
807 #ifdef NIML_DEBUG
808 NI_dpr(" scan_for_angles at restart=%d epos=%d bufsize=%d",num_restart,epos,ns->nbuf) ;
809 #endif
810
811 /*-- scan ahead to find goal character in the buffer --*/
812
813 while( epos < ns->nbuf && ns->buf[epos] != goal ) epos++ ;
814
815 /*-- if we found our goal, do something about it --*/
816
817 if( epos < ns->nbuf ){
818
819 #ifdef NIML_DEBUG
820 NI_dpr(" scan_for_angles found goal '%c' at epos=%d",goal,epos) ;
821 #endif
822
823 /*-- if our goal was the closing '>', we are done! (maybe) --*/
824
825 if( goal == '>' ){
826
827 /*- 01 Jun 2005: see if we are at a comment; if so, must start over -*/
828
829 if( epos - ns->npos >= 4 && strncmp(ns->buf+ns->npos,"<!--",4) == 0 ){
830
831 if( strncmp(ns->buf+epos-2,"-->",3) == 0 ){ /* got a full comment */
832
833 #if 0
834 { int ncp = 1+epos-ns->npos ; char *cpt=malloc(10+ncp) ;
835 memcpy(cpt,ns->buf+ns->npos,ncp) ; cpt[ncp] = '\0' ;
836 fprintf(stderr, "\nSkipping NIML comment: '%s'\n",cpt); free(cpt);
837 }
838 #endif
839
840 ns->npos = epos+1 ; NI_reset_buffer(ns) ; /* skip it & try again */
841 epos = 0 ; goal = '<' ;
842 } else { /* '>' doesn't close comment! */
843 epos++ ; /* so look for another one!!! */
844 }
845 caseb = 1 ; goto Restart ;
846 }
847
848 /*** not a comment, so we can exit triumphantly! ***/
849
850 return epos+1 ; /* marks the character after '>' */
851 }
852
853 /*-- if here, our goal was the opening '<';
854 set the buffer position to this location,
855 set the new goal, and scan for the new goal --*/
856
857 ns->npos = epos ; /* mark where we found '<' */
858 goal = '>' ; /* the new goal */
859 caseb = 1 ; /* force rescan, even if time is up */
860 goto Restart ; /* scan again! */
861 }
862
863 /*-- if we get to here, we didn't find our goal:
864 (a) if the goal was the opening '<', then throw
865 away all data in the buffer, and get some more data
866 (b) if the goal was the closing '>', then we need more data
867 in the buffer, but need to keep the existing data
868 (c) UNLESS the buffer is full AND npos is zero
869 - in this case, we expand the buffer size and hope --*/
870
871 if( goal == '<' ){ /* case (a) */
872 #ifdef NIML_DEBUG
873 NI_dpr(" scan_for_angles failed to find goal '<' in buffer of size %d",ns->nbuf) ;
874 #endif
875 ns->nbuf = ns->npos = epos = 0 ; caseb = 0 ;
876
877 } else if( ns->nbuf < ns->bufsize || ns->npos > 0 ){ /* case (b) */
878 #ifdef NIML_DEBUG
879 NI_dpr(" scan_for_angles failed to find goal '>' -- retry") ;
880 #endif
881 NI_reset_buffer(ns) ; epos = ns->nbuf ; caseb = 1 ;
882
883 } else { /* case (c) */
884 #ifdef NIML_DEBUG
885 NI_dpr(" scan_for_angles failed to find goal '>' -- expand buffer and retry") ;
886 #endif
887 epos = ns->nbuf ;
888 nn = NI_stream_setbufsize(ns,2*ns->bufsize) ; /* expand buffer! */
889 if( nn < 0 ){ ns->nbuf = ns->npos = 0 ; return -1 ; } /* fails? */
890
891 }
892
893 /*-- if we are here, we need more data before scanning again --*/
894
895 /*-- read at least nbmin bytes,
896 waiting up to mleft ms (unless the data stream goes bad) --*/
897
898 if( mleft <= 0 ) mleft = 3 ;
899 nbmin = (goal == '<') ? 4 : 1 ;
900
901 #ifdef NIML_DEBUG
902 NI_dpr(" scan_for_angles calling NI_stream_fillbuf") ;
903 #endif
904 nn = NI_stream_fillbuf( ns , nbmin , mleft ) ;
905
906 if( nn >= nbmin ) caseb = 1 ; /* got new data => force rescan */
907
908 if( nn >= 0 ) goto Restart ; /* scan some more for the goal */
909
910 /*-- if here, the stream went bad, so exit --*/
911 #ifdef NIML_DEBUG
912 NI_dpr(" scan_for_angles stream failed :(") ;
913 #endif
914 ns->nbuf = ns->npos = 0 ; return -1 ;
915 }
916
917 /*------------------------------------------------------------------------*/
918 /*! Mode for writing names. */
919
920 static int name_mode = NI_NAMEMODE_NORMAL ;
921
922 /*------------------------------------------------------------------------*/
923 /*! Set the mode for writing type names:
924 - NI_NAMEMODE_NORMAL => byte , short, int , float , double , ...
925 - NI_NAMEMODE_ALIAS => uint8, int16, int32, float32, float64, ...
926 --------------------------------------------------------------------------*/
927
NI_set_typename_mode(int nmode)928 void NI_set_typename_mode( int nmode )
929 {
930 if( nmode > 0 && nmode <= NI_ATTMODE_LAST ) name_mode = nmode ;
931 else name_mode = NI_NAMEMODE_NORMAL;
932 }
933
934 /*------------------------------------------------------------------------*/
935 /*! Return the type name given the integer code. */
936
NI_type_name(int code)937 char * NI_type_name( int code )
938 {
939 return (name_mode == NI_NAMEMODE_ALIAS) ? NI_rowtype_code_to_alias(code)
940 : NI_rowtype_code_to_name (code) ;
941 }
942
943 /*------------------------------------------------------------------------*/
944 /*! Write an element (data or group) to a stream.
945 Return value is number of bytes written to the stream.
946 If return is -1, something bad happened. You should then check
947 the stream with NI_stream_goodcheck(), for example.
948
949 If the stream is temporarily unable to write (e.g., the socket
950 buffer is full), then this function will wait until it is ready.
951 If you don't want that behavior, you should use NI_stream_writecheck()
952 before calling this function.
953 --------------------------------------------------------------------------*/
954
NI_write_element(NI_stream_type * ns,void * nini,int tmode)955 int64_t NI_write_element( NI_stream_type *ns , void *nini , int tmode )
956 {
957 char *wbuf , *att=NULL , *qtt , *btt ;
958 int nwbuf , ii,jj,row,col , tt=NI_element_type(nini) ;
959 int att_len , kk , otmode=tmode ;
960 int64_t ntot=0 , nout ; /* 28 Jun 2021 */
961
962 char *bbuf , *cbuf ; /* base64 stuff */
963 int bb=0 , cc=0 ;
964
965 char *att_prefix , *att_equals , *att_trail ;
966 int header_only , header_sharp , outmode=-1 ;
967
968 /*--- 09 Mar 2005: outmode overrides tmode, if outmode is present ---*/
969
970 switch( tt ){
971 default: return -1 ; /* bad input! */
972
973 case NI_GROUP_TYPE:{
974 NI_group *ngr = (NI_group *) nini ;
975 outmode = ngr->outmode ;
976 }
977 break ;
978
979 case NI_ELEMENT_TYPE:{
980 NI_element *nel = (NI_element *) nini ;
981 outmode = nel->outmode ;
982 }
983 break ;
984
985 case NI_PROCINS_TYPE:{ /* 16 Mar 2005 */
986 outmode = NI_TEXT_MODE ;
987 }
988 break ;
989 }
990 if( outmode >= 0 ) tmode = outmode ;
991
992 /*--- determine special cases from the flags above bit #7 ---*/
993
994 header_only = ((tmode & NI_HEADERONLY_FLAG ) != 0) ; /* 20 Feb 2003 */
995 header_sharp = ((tmode & NI_HEADERSHARP_FLAG) != 0) ; /* 20 Mar 2003 */
996
997 /* ADDOUT = after writing, add byte count if OK, else quit */
998 /* AF = thing to do if ADDOUT is quitting */
999
1000 #ifdef NIML_DEBUG
1001 NI_dpr("ENTER NI_write_element\n") ;
1002 #endif
1003
1004 #undef AF
1005 #define AF
1006 #define ADDOUT(q) if(nout<0){AF;fprintf(stderr,"NIML: write abort %s nout=%lld\n",q,nout);return -1;} else ntot+=nout
1007
1008 if( !NI_stream_writeable(ns) ) return -1 ; /* stupid user */
1009
1010 if( ns->bad ){ /* socket that hasn't connected yet */
1011 #ifdef NIML_DEBUG
1012 NI_dpr("NI_write_element: write socket not connected\n") ;
1013 #endif
1014 jj = NI_stream_goodcheck(ns,666) ; /* try to connect it */
1015 if( jj < 1 ) return jj ; /* 0 is nothing yet, -1 is death */
1016 #ifdef NIML_DEBUG
1017 NI_dpr("NI_write_element: write socket now connected\n") ;
1018 #endif
1019 } else { /* check if good ns has gone bad */
1020 jj = NI_stream_writecheck(ns,666) ;
1021 if( jj < 0 ) return jj ;
1022 }
1023
1024 tmode &= 255 ;
1025 if( ns->type == NI_STRING_TYPE ) /* string output only in text mode */
1026 tmode = NI_TEXT_MODE ;
1027
1028 if( tmode != NI_TEXT_MODE ) header_sharp = 0 ; /* 20 Mar 2003 */
1029
1030 /*-- 15 Oct 2002: write attributes with lots of space, or little --*/
1031 /*-- 20 Mar 2003: modified for "# lhs = rhs" type of header --*/
1032
1033 att_prefix = (header_sharp) ? (char *)"\n# " /* write this before each attribute */
1034 : (char *)"\n " ;
1035
1036 att_equals = (header_sharp) ? (char *)" = " /* write this between lhs and rhs */
1037 : (char *)"=" ;
1038
1039 att_trail = (header_sharp) ? (char *)"\n# " /* write this before closing ">" */
1040 : (char *)" " ;
1041
1042 /*------------------ write a processing instruction ------------------*/
1043
1044 if( tt == NI_PROCINS_TYPE ){
1045
1046 NI_procins *npi = (NI_procins *)nini ;
1047
1048 if( header_sharp ){ nout = NI_stream_writestring(ns,"# "); ADDOUT("a"); }
1049
1050 nout = NI_stream_writestring( ns , "<?" ) ; ADDOUT("b") ;
1051 nout = NI_stream_writestring( ns , npi->name ) ; ADDOUT("c") ;
1052
1053 /*- attributes -*/
1054
1055 for( ii=0 ; ii < npi->attr_num ; ii++ ){
1056
1057 jj = NI_strlen( npi->attr_lhs[ii] ) ; if( jj == 0 ) continue ;
1058 nout = NI_stream_writestring( ns , " " ) ; ADDOUT("d") ;
1059 if( NI_is_name(npi->attr_lhs[ii]) ){
1060 nout = NI_stream_write( ns , npi->attr_lhs[ii] , jj ) ;
1061 } else {
1062 att = quotize_string( npi->attr_lhs[ii] ) ;
1063 nout = NI_stream_writestring( ns , att ) ; NI_free(att) ;
1064 }
1065 ADDOUT("e") ;
1066
1067 jj = NI_strlen( npi->attr_rhs[ii] ) ; if( jj == 0 ) continue ;
1068 nout = NI_stream_writestring( ns , "=" ) ; ADDOUT("f") ;
1069 att = quotize_string( npi->attr_rhs[ii] ) ;
1070 nout = NI_stream_writestring( ns , att ) ; NI_free(att) ; ADDOUT("g") ;
1071 }
1072
1073 nout = NI_stream_writestring( ns , " ?>\n" ) ; ADDOUT("h") ;
1074
1075 return ntot ; /*** done with processing instruction ***/
1076
1077 /*------------------ write a group element ------------------*/
1078
1079 } else if( tt == NI_GROUP_TYPE ){
1080
1081 NI_group *ngr = (NI_group *) nini ;
1082 char *gname ;
1083
1084 /* 24 Feb 2005: all group elements used to be named "ni_group",
1085 but no more; now they have attribute ni_form="ni_group" */
1086
1087 gname = ngr->name ;
1088 if( gname == NULL || *gname == '\0' ) gname = "ni_group" ;
1089
1090 /*- group header -*/
1091
1092 if( header_sharp ){ nout = NI_stream_writestring(ns,"# "); ADDOUT("i"); }
1093 #if 1
1094 nout = NI_stream_writestring( ns , "<" ) ; ADDOUT("j") ;
1095 nout = NI_stream_writestring( ns , gname ) ; ADDOUT("k") ;
1096 #else
1097 nout = NI_stream_writestring( ns , "<ni_group" ) ; ADDOUT("l") ;
1098 #endif
1099
1100 /*- attributes -*/
1101
1102 NI_set_attribute( ngr , "ni_form" , "ni_group" ) ; /* 24 Feb 2005 */
1103
1104 for( ii=0 ; ii < ngr->attr_num ; ii++ ){
1105
1106 jj = NI_strlen( ngr->attr_lhs[ii] ) ; if( jj == 0 ) continue ;
1107 nout = NI_stream_writestring( ns , att_prefix ) ; ADDOUT("m") ;
1108 if( NI_is_name(ngr->attr_lhs[ii]) ){
1109 nout = NI_stream_write( ns , ngr->attr_lhs[ii] , jj ) ;
1110 } else {
1111 att = quotize_string( ngr->attr_lhs[ii] ) ;
1112 nout = NI_stream_writestring( ns , att ) ; NI_free(att) ;
1113 }
1114 ADDOUT("n") ;
1115
1116 jj = NI_strlen( ngr->attr_rhs[ii] ) ; if( jj == 0 ) continue ;
1117 nout = NI_stream_writestring( ns , att_equals ) ; ADDOUT("o") ;
1118 att = quotize_string( ngr->attr_rhs[ii] ) ;
1119 nout = NI_stream_writestring( ns , att ) ; NI_free(att) ; ADDOUT("p") ;
1120 }
1121
1122 /*- close group header -*/
1123
1124 nout = NI_stream_writestring( ns , att_trail ) ; ADDOUT("q") ;
1125 nout = NI_stream_writestring( ns , ">\n" ) ; ADDOUT("r") ;
1126
1127 /*- write the group parts (recursively) -*/
1128
1129 for( ii=0 ; ii < ngr->part_num ; ii++ ){
1130 if( outmode >= 0 ){
1131 if( NI_element_type(ngr->part[ii]) == NI_ELEMENT_TYPE ){
1132 NI_element *qel = (NI_element *)ngr->part[ii] ;
1133 qel->outmode = outmode ;
1134 } else if( NI_element_type(ngr->part[ii]) == NI_GROUP_TYPE ){
1135 NI_group *qgr = (NI_group *)ngr->part[ii] ;
1136 qgr->outmode = outmode ;
1137 }
1138 }
1139 nout = NI_write_element( ns , ngr->part[ii] , otmode ) ; ADDOUT("s") ;
1140 }
1141
1142 /*- group trailer -*/
1143
1144 if( header_sharp ){ nout = NI_stream_writestring(ns,"# "); ADDOUT("t"); }
1145 #if 1
1146 nout = NI_stream_writestring( ns , "</" ) ; ADDOUT("u") ;
1147 nout = NI_stream_writestring( ns , gname ) ; ADDOUT("v") ;
1148 nout = NI_stream_writestring( ns , ">\n" ) ; ADDOUT("w") ;
1149 #else
1150 nout = NI_stream_writestring( ns , "</ni_group>\n" ) ; ADDOUT("x") ;
1151 #endif
1152
1153 return ntot ; /*** done with group element ***/
1154
1155 /*------------------ write a data element ------------------*/
1156
1157 } else if( tt == NI_ELEMENT_TYPE ){
1158
1159 NI_element *nel = (NI_element *) nini ;
1160
1161 /*- sanity check (should never fail) -*/
1162
1163 jj = NI_strlen(nel->name) ; if( jj == 0 ) return -1 ;
1164
1165 /*- select the data output mode -*/
1166
1167 /* Strings can only be written in text mode */
1168
1169 if( tmode != NI_TEXT_MODE ){
1170 for( jj=0 ; jj < nel->vec_num ; jj++ ){
1171 if( NI_has_String(NI_rowtype_find_code(nel->vec_typ[jj])) ){
1172 tmode = NI_TEXT_MODE ; break ;
1173 }
1174 }
1175 }
1176
1177 switch( tmode ){
1178 default: tmode = NI_TEXT_MODE ; break ;
1179
1180 case NI_BINARY_MODE: break ;
1181 case NI_BASE64_MODE: break ;
1182 }
1183
1184 /* space to hold attribute strings */
1185
1186 att_len = 8192 + 256*nel->vec_num + 128*nel->vec_rank ;
1187 att = NI_malloc(char, att_len ) ;
1188
1189 /* create ni_veclab attribute if needed [12 Sep 2018] */
1190
1191 NI_set_attribute_from_veclab_array( nel , nel->vec_lab ) ;
1192
1193 #undef AF
1194 #define AF NI_free(att) /* free att if we have to quit early now */
1195
1196 /* write start of header "<name" */
1197
1198 if( header_sharp ){ nout = NI_stream_writestring(ns,"# "); ADDOUT("y"); }
1199 strcpy(att,"<") ; strcat(att,nel->name) ;
1200 nout = NI_stream_writestring( ns , att ) ; ADDOUT("z") ;
1201
1202 /*- write "special" attributes, if not an empty element -*/
1203
1204 if( nel->vec_len > 0 && nel->vec_num > 0 ){
1205 int ll , tt ;
1206
1207 /* ni_form (depends on tmode) */
1208
1209 switch( tmode ){
1210 default:
1211 case NI_TEXT_MODE:
1212 *att = '\0' ; /* text form is default */
1213 break ;
1214
1215 case NI_BINARY_MODE:
1216 case NI_BASE64_MODE:
1217 sprintf(att,"%sni_form%s\"%s.%s\"" ,
1218 att_prefix , att_equals ,
1219 (tmode == NI_BINARY_MODE) ? "binary" : "base64" ,
1220 (NI_byteorder()==NI_LSB_FIRST) ? "lsbfirst" : "msbfirst" );
1221 break ;
1222 }
1223 if( *att != '\0' ){
1224 nout = NI_stream_writestring( ns , att ) ; ADDOUT("A") ;
1225 }
1226
1227 /** do ni_type **/
1228
1229 sprintf(att,"%sni_type%s\"" , att_prefix , att_equals ) ;
1230 for( ll=-1,ii=0 ; ii < nel->vec_num ; ii++ ){
1231 if( nel->vec_typ[ii] != ll ){ /* not the previous type */
1232 if( ll >= 0 ){ /* write the previous type out now */
1233 btt = att + strlen(att) ;
1234 if( jj > 1 ) sprintf(btt,"%d*%s,",jj,NI_type_name(ll)) ;
1235 else sprintf(btt,"%s," , NI_type_name(ll)) ;
1236 }
1237 ll = nel->vec_typ[ii] ; /* save new type code */
1238 jj = 1 ; /* it now has count 1 */
1239
1240 } else { /* same as previous type */
1241 jj++ ; /* so add 1 to its count */
1242 }
1243 }
1244 /* write the last type we found */
1245 btt = att + strlen(att) ;
1246 if( jj > 1 ) sprintf(btt,"%d*%s\"",jj,NI_type_name(ll)) ;
1247 else sprintf(btt,"%s\"" , NI_type_name(ll)) ;
1248
1249 nout = NI_stream_writestring( ns , att ) ; ADDOUT("B") ;
1250
1251 /** do ni_dimen **/
1252
1253 if( nel->vec_rank > 1 ){
1254 sprintf(att,"%sni_dimen%s" , att_prefix , att_equals ) ;
1255 qtt = quotize_int_vector( nel->vec_rank ,
1256 nel->vec_axis_len , ',' ) ;
1257 strcat(att,qtt) ; NI_free(qtt) ;
1258 } else {
1259 sprintf(att,"%sni_dimen%s\"%d\"",att_prefix,att_equals,nel->vec_len);
1260 }
1261 nout = NI_stream_writestring( ns , att ) ; ADDOUT("C") ;
1262
1263 #if 0
1264 /** 26 Mar 2003: write number of bytes of data contained herein **/
1265
1266 for( jj=ii=0 ; ii < nel->vec_num ; ii++ )
1267 jj += NI_size_column( NI_rowtype_find_code(nel->vec_typ[ii]) ,
1268 nel->vec_len , nel->vec[ii] ) ;
1269 sprintf(att,"%sni_datasize%s\"%d\"" , att_prefix , att_equals , jj ) ;
1270 nout = NI_stream_writestring( ns , att ) ; ADDOUT("D") ;
1271 #endif
1272
1273 #if 0
1274 /* extras: ni_veclen and ni_vecnum attributes */
1275
1276 sprintf(att,"%sni_veclen%s\"%d\"", att_prefix,att_equals,nel->vec_len) ;
1277 nout = NI_stream_writestring( ns , att ) ; ADDOUT("E") ;
1278
1279 sprintf(att,"%sni_vecnum%s\"%d\"", att_prefix,att_equals,nel->vec_num) ;
1280 nout = NI_stream_writestring( ns , att ) ; ADDOUT("F") ;
1281 #endif
1282 /* ni_delta */
1283
1284 if( nel->vec_axis_delta != NULL ){
1285 sprintf(att,"%sni_delta%s",att_prefix,att_equals) ;
1286 qtt = quotize_float_vector( nel->vec_rank ,
1287 nel->vec_axis_delta , ',' ) ;
1288 strcat(att,qtt) ; NI_free(qtt) ;
1289 nout = NI_stream_writestring( ns , att ) ; ADDOUT("G") ;
1290 }
1291
1292 /* ni_origin */
1293
1294 if( nel->vec_axis_origin != NULL ){
1295 sprintf(att,"%sni_origin%s",att_prefix,att_equals) ;
1296 qtt = quotize_float_vector( nel->vec_rank ,
1297 nel->vec_axis_origin , ',' ) ;
1298 strcat(att,qtt) ; NI_free(qtt) ;
1299 nout = NI_stream_writestring( ns , att ) ; ADDOUT("H") ;
1300 }
1301
1302 /* ni_units */
1303
1304 if( nel->vec_axis_unit != NULL ){
1305 sprintf(att,"%sni_units%s",att_prefix,att_equals) ;
1306 qtt = quotize_string_vector( nel->vec_rank ,
1307 nel->vec_axis_unit , ',' ) ;
1308 strcat(att,qtt) ; NI_free(qtt) ;
1309 nout = NI_stream_writestring( ns , att ) ; ADDOUT("I") ;
1310 }
1311
1312 /* ni_axes */
1313
1314 if( nel->vec_axis_label != NULL ){
1315 sprintf(att,"%sni_axes%s",att_prefix,att_equals) ;
1316 qtt = quotize_string_vector( nel->vec_rank ,
1317 nel->vec_axis_label , ',' ) ;
1318 strcat(att,qtt) ; NI_free(qtt) ;
1319 nout = NI_stream_writestring( ns , att ) ; ADDOUT("J") ;
1320 }
1321
1322 }
1323
1324 /*- other attributes -*/
1325
1326 for( ii=0 ; ii < nel->attr_num ; ii++ ){
1327
1328 jj = NI_strlen( nel->attr_lhs[ii] ) ; if( jj == 0 ) continue ;
1329
1330 /* skip "special" attributes */
1331
1332 if( strcmp(nel->attr_lhs[ii],"ni_form") == 0 ) continue ;
1333 if( strcmp(nel->attr_lhs[ii],"ni_type") == 0 ) continue ;
1334 if( strcmp(nel->attr_lhs[ii],"ni_dimen") == 0 ) continue ;
1335 if( strcmp(nel->attr_lhs[ii],"ni_veclen") == 0 ) continue ;
1336 if( strcmp(nel->attr_lhs[ii],"ni_vecnum") == 0 ) continue ;
1337 if( strcmp(nel->attr_lhs[ii],"ni_delta") == 0 ) continue ;
1338 if( strcmp(nel->attr_lhs[ii],"ni_origin") == 0 ) continue ;
1339 if( strcmp(nel->attr_lhs[ii],"ni_units") == 0 ) continue ;
1340 if( strcmp(nel->attr_lhs[ii],"ni_axes") == 0 ) continue ;
1341 if( strcmp(nel->attr_lhs[ii],"ni_datasize") == 0 ) continue ; /* 13 Apr 2004 */
1342
1343 kk = NI_strlen( nel->attr_rhs[ii] ) ;
1344
1345 /* do the work */
1346
1347 if( jj+kk+128 > att_len ){ /* 13 Jun 2003 */
1348 att_len = jj+kk+128 ;
1349 att = NI_realloc( att , char, att_len ) ;
1350 }
1351
1352 strcpy(att,att_prefix) ;
1353
1354 if( NI_is_name(nel->attr_lhs[ii]) ){ /* the 'normal' case */
1355 strcat(att,nel->attr_lhs[ii]) ;
1356 } else { /* not legal in XML */
1357 qtt = quotize_string( nel->attr_lhs[ii] ) ;
1358 strcat(att,qtt) ; NI_free(qtt) ;
1359 }
1360
1361 if( kk > 0 ){
1362 strcat(att,att_equals) ;
1363 qtt = quotize_string( nel->attr_rhs[ii] ) ; /* RHS always quoted */
1364 kk = strlen(qtt)+strlen(att)+32 ;
1365 if( kk > att_len ){ att_len=kk; att=NI_realloc(att,char,att_len); }
1366 strcat(att,qtt) ; NI_free(qtt) ;
1367 }
1368 nout = NI_stream_writestring( ns , att ) ; ADDOUT("K") ;
1369 }
1370
1371 NI_free(att) ; att = NULL ; /**** done with attributes ****/
1372
1373 #undef AF
1374 #define AF /* nothing to do if we have to quit early */
1375
1376 /*- close header -*/
1377
1378 if( nel->vec_len == 0 || /* An 'empty' element (no data) */
1379 nel->vec_num == 0 ||
1380 nel->vec_typ == NULL ||
1381 nel->vec == NULL ){
1382
1383 nout = NI_stream_writestring( ns , att_trail ) ; ADDOUT("L") ;
1384 nout = NI_stream_writestring( ns , "/>\n" ) ; ADDOUT("M") ;
1385
1386 #ifdef NIML_DEBUG
1387 NI_dpr("NI_write_element: empty element '%s' had %d total bytes\n",nel->name,ntot) ;
1388 #endif
1389 return ntot ; /*** done with empty data element ***/
1390 }
1391
1392 /*- if here, must write some data out -*/
1393
1394 /* first, terminate the header,
1395 and allocate space for the write buffer (1 row at a time) */
1396
1397 switch( tmode ){
1398 default:
1399 case NI_TEXT_MODE:
1400 btt = ">\n" ; /* add a newline */
1401 break ;
1402
1403 case NI_BINARY_MODE:
1404 btt = ">" ; /* no newline */
1405 break ;
1406
1407 case NI_BASE64_MODE:
1408 btt = ">\n" ; /* add a newline */
1409 break ;
1410 }
1411
1412 nout = NI_stream_writestring( ns , att_trail ) ; ADDOUT("N") ;
1413 nout = NI_stream_writestring( ns , btt ) ; ADDOUT("O") ;
1414
1415 /*-- 13 Feb 2003: data output is now done elsewhere --*/
1416
1417 if( !header_only ){
1418 nout = NI_write_columns( ns, nel->vec_num, nel->vec_typ,
1419 nel->vec_len, nel->vec , tmode ) ;
1420 ADDOUT("DATA") ;
1421 }
1422 #ifdef NIML_DEBUG
1423 else NI_dpr("NI_write_element: header_only case\n") ;
1424 #endif
1425
1426 /*- write element trailer -*/
1427
1428 if( header_sharp ){ nout = NI_stream_writestring(ns,"# "); ADDOUT("Q"); }
1429 nout = NI_stream_writestring( ns , "</" ) ; ADDOUT("R") ;
1430 nout = NI_stream_writestring( ns , nel->name ) ; ADDOUT("S") ;
1431 nout = NI_stream_writestring( ns , ">\n\n" ) ; ADDOUT("T") ;
1432
1433 #ifdef NIML_DEBUG
1434 NI_dpr("NI_write_element: data element '%s' had %d total bytes\n",nel->name,ntot) ;
1435 #endif
1436 return ntot ; /*** done with full data element ***/
1437
1438 } /* end of write data element */
1439
1440 return -1 ; /* should never be reachable */
1441 }
1442
1443 /*------------------------------------------------------------------------*/
1444 /*! Write an element (data or group) to a file. [07 Mar 2007]
1445 --------------------------------------------------------------------------*/
1446
NI_write_element_tofile(char * fname,void * nini,int tmode)1447 int64_t NI_write_element_tofile( char *fname , void *nini , int tmode )
1448 {
1449 NI_stream_type *ns ; char *nsname ; int64_t vv ;
1450
1451 if( fname == NULL || *fname == '\0' || nini == NULL ) return -1 ;
1452
1453 nsname = (char *)malloc(strlen(fname)+9) ;
1454 if( strncmp(fname,"stdout:",7) == 0 || strcmp(fname,"-") == 0 ){
1455 strcpy(nsname,"stdout:") ;
1456 } else if( strncmp(fname,"stderr:",7) == 0 ){
1457 strcpy(nsname,"stderr:") ;
1458 } else {
1459 strcpy(nsname,"file:") ; strcat(nsname,fname) ;
1460 }
1461 ns = NI_stream_open( nsname , "w" ) ; free((void *)nsname) ;
1462 if( ns == NULL ){ fprintf(stderr,"NIML: fail to open file %s for writing\n",fname); return -1; }
1463 vv = NI_write_element( ns , nini , tmode ) ;
1464 NI_stream_close( ns ) ;
1465 return vv ;
1466 }
1467
1468 /*------------------------------------------------------------------------*/
1469 /*! Read one element from a file. [12 Mar 2007]
1470 --------------------------------------------------------------------------*/
1471
NI_read_element_fromfile(char * fname)1472 void * NI_read_element_fromfile( char *fname )
1473 {
1474 NI_stream_type *ns ; char *nsname ; void *nini ;
1475
1476 if( fname == NULL || *fname == '\0' ) return NULL ;
1477
1478 if( NI_is_fifo(fname) ){ /* FIFO - only text element */
1479 char *buf = NI_suck_file( fname ) ; /* [27 Aug 2019] */
1480 if( buf == NULL ) return NULL ;
1481 nini = NI_read_element_fromstring( buf ) ;
1482 free(buf) ;
1483 return nini ;
1484 }
1485
1486 /* regular file - could have a binary element */
1487
1488 nsname = (char *)malloc(strlen(fname)+9) ;
1489 strcpy(nsname,"file:") ; strcat(nsname,fname) ;
1490 ns = NI_stream_open( nsname , "r" ) ; free((void *)nsname) ;
1491 if( ns == NULL ) return NULL ;
1492 nini = NI_read_element( ns , 999 ) ;
1493 NI_stream_close( ns ) ;
1494 return nini ;
1495 }
1496
1497 /*------------------------------------------------------------------------*/
1498 /*! Read one element from a string. [26 Feb 2010 ZSS]
1499 --------------------------------------------------------------------------*/
NI_read_element_fromstring(char * nstr)1500 void * NI_read_element_fromstring( char *nstr )
1501 {
1502 NI_stream_type *ns ; void *nini ;
1503
1504 if( nstr == NULL || *nstr == '\0' ) return (NULL) ;
1505
1506 /* convert string to a NIML element */
1507
1508 ns = NI_stream_open( "str:" , "r" ) ;
1509 NI_stream_setbuf( ns , nstr ) ;
1510 nini = NI_read_element( ns , 1 ) ;
1511 NI_stream_close( ns ) ;
1512
1513 return (nini) ;
1514 }
1515
1516 /*------------------------------------------------------------------------*/
1517 /*! Write one element to a string. [Oct 2011 ZSS, based on Bob's]
1518 --------------------------------------------------------------------------*/
NI_write_element_tostring(void * nel)1519 char * NI_write_element_tostring( void *nel )
1520 {
1521 NI_stream ns ;
1522 char *stout ;
1523 int ii,jj ;
1524
1525 if( nel == NULL ) return (NULL) ;
1526
1527 ns = NI_stream_open( "str:" , "w" ) ;
1528 (void) NI_write_element( ns , nel , NI_TEXT_MODE ) ;
1529 stout = strdup( NI_stream_getbuf(ns) ) ;
1530 NI_stream_close( ns ) ;
1531 jj = strlen(stout) ;
1532 for( ii=jj-1 ; ii > 0 && isspace(stout[ii]) ; ii-- ) ; /* trailing blanks */
1533 stout[ii+1] = '\0' ;
1534 return (stout) ;
1535 }
1536