1 #include "niml_private.h"
2
3 /****************************************************************************/
4 /****************** Functions to process a NIML header **********************/
5 /****************************************************************************/
6
7 /*! Macro to define skippable characters. */
8
9 #define SKIPABL(c) ((c)=='#' || isspace(c))
10
11 /*--------------------------------------------------------------------------*/
12 /*! Find an attribute in a header_stuff struct. Returns the RHS or NULL.
13 ----------------------------------------------------------------------------*/
14
get_header_attribute(header_stuff * hs,char * attname)15 char * get_header_attribute( header_stuff *hs , char *attname )
16 {
17 int nn ;
18 static char *zorkon = "\0" ;
19
20 if( hs == NULL ) return NULL ;
21
22 for( nn=0 ; nn < hs->nattr ; nn++ )
23 if( strcmp(hs->lhs[nn],attname) == 0 ) break ;
24
25 if( nn == hs->nattr ) return NULL ;
26
27 if( hs->rhs[nn] == NULL ) return zorkon ;
28
29 return hs->rhs[nn] ;
30 }
31
32 /*--------------------------------------------------------------------------*/
33 /*! Deallocate a header_stuff struct.
34 ----------------------------------------------------------------------------*/
35
destroy_header_stuff(header_stuff * hs)36 void destroy_header_stuff( header_stuff *hs )
37 {
38 int ii ;
39 if( hs == NULL ) return ;
40 NI_free(hs->name) ;
41 for( ii=0 ; ii < hs->nattr ; ii++ ){
42 if( hs->lhs != NULL ) NI_free( hs->lhs[ii] ) ;
43 if( hs->rhs != NULL ) NI_free( hs->rhs[ii] ) ;
44 }
45 NI_free( hs ) ;
46 }
47
48 /*-------------------------------------------------------------------------*/
49 /*! Find an isolated string in the input array of char.
50
51 - nst = start position
52 - nch = total number of data bytes
53 - ch = array of data bytes
54
55 Return value is an intpair with the .i component indicating the
56 start position of the string in the data and the .j indicating
57 the byte AFTER the end of the string. If the .i component is
58 negative, then no string was found.
59 ---------------------------------------------------------------------------*/
60
find_string(int nst,int nch,char * ch)61 intpair find_string( int nst, int nch, char *ch )
62 {
63 intpair ans = {-1,-1} ; /* default answer ==> nothing found */
64 int ii,jj ;
65 char quot ;
66
67 #ifdef NIML_DEBUG
68 NI_dpr(" ENTER find_string: nst=%d nch=%d\n",nst,nch) ;
69 #endif
70
71 if( nst >= nch || nch < 2 || ch == NULL ) return ans; /* bad input */
72
73 for( ii=nst; ii<nch && !IS_STRING_CHAR(ch[ii]); ii++ ) ; /* skip to start */
74
75 if( ii >= nch ) return ans ; /* bad input */
76
77 if( IS_QUOTE_CHAR(ch[ii]) ){ /* quoted string */
78 if( ii == nch-1 ) return ans ; /* bad input */
79 quot = ch[ii] ; ii++ ;
80 for( jj=ii ; jj<nch && ch[jj] != quot ; jj++ ) ; /* skip to close */
81 } else {
82 for( jj=ii+1 ; jj<nch && IS_STRING_CHAR(ch[jj]) ; jj++ ) ; /* to blank */
83 }
84
85 ans.i = ii ; ans.j = jj ; /* answer starts at ch[ii] and goes to ch[jj-1] */
86 return ans ;
87 }
88
89 /*--------------------------------------------------------------------------*/
90 /*! Parse into strings a <header and=its attributes="stuff">.
91
92 - ndat = number of data bytes input
93 - dat = data bytes input
94 - *nused = output number of bytes consumed (=index of byte AFTER the closing '>').
95
96 Return value is a pointer to a header_stuff struct;
97 if NULL is returned, something real bad happened (and *nused won't
98 be assigned).
99 ----------------------------------------------------------------------------*/
100
parse_header_stuff(int ndat,char * dat,int * nused)101 header_stuff * parse_header_stuff( int ndat, char *dat, int *nused )
102 {
103 header_stuff *hs ; /* return value */
104 int id,jd , nn ;
105 intpair ss ;
106
107 if( ndat < 2 || dat == NULL ) return NULL ; /* bad input */
108
109 #ifdef NIML_DEBUG
110 NI_dpr("ENTER parse_header_stuff: %.*s\n",ndat,dat) ;
111 #endif
112
113 for( id=0 ; id < ndat && dat[id] != '<' ; id++ ) ; /* skip to opening '<' */
114
115 if( id >= ndat-1 ) return NULL ; /* bad input */
116
117 hs = NI_malloc(header_stuff,sizeof(header_stuff)); /* make output struct */
118 hs->nattr = hs->empty = 0 ;
119 hs->name = NULL ;
120 hs->lhs = hs->rhs = NULL ;
121
122 /* find and assign name string (immediately after '<') */
123
124 ss = find_string( id+1 , ndat , dat ) ;
125
126 if( ss.i < 0 || ss.j <= ss.i ){
127 destroy_header_stuff( hs ) ; return NULL ; /* no name string */
128 }
129
130 nn = ss.j - ss.i ; /* string length */
131 hs->name = NI_malloc(char, nn+1) ;
132 NI_strncpy( hs->name , dat+ss.i , nn+1 ) ;
133
134 #ifdef NIML_DEBUG
135 NI_dpr(" parse_header_stuff: name = %s\n",hs->name) ;
136 #endif
137
138 /* start scanning for next string at location id */
139
140 id = ss.j ; if( IS_QUOTE_CHAR(dat[id]) ) id++ ;
141
142 /* find and assign attribute strings */
143
144 while(1){
145
146 #ifdef NIML_DEBUG
147 NI_dpr(" parse_header_stuff: scan start at id=%d\n",id) ;
148 #endif
149
150 for( ; id < ndat && SKIPABL(dat[id]) ; id++ ) ; /* skip blanks */
151
152 if( id >= ndat ) break ; /* end of input found */
153
154 if( dat[id] == '>' ) break ; /* ">" end found */
155
156 if( dat[id] == '/' ){ /* "/>" end found */
157 if( id < ndat-1 ) id++ ; /* skip the '>' */
158 hs->empty = 1 ; /* mark header as 'empty' */
159 break ; /* done with scanning for attributes */
160 }
161
162 if( dat[id] == '?' ){ /* "?>" found */
163 if( id < ndat-1 ) id++ ; /* ==> this is a procins! */
164 hs->empty = 1 ;
165 break ;
166 }
167
168 /* find next string */
169
170 ss = find_string( id , ndat , dat ) ;
171
172 if( ss.i < 0 || ss.j <= ss.i ) break ; /* didn't find a string */
173
174 #ifdef NIML_DEBUG
175 NI_dpr(" parse_header_stuff: next string = %.*s\n",ss.j-ss.i,dat+ss.i) ;
176 #endif
177
178 /* extend size of attribute arrays */
179
180 hs->lhs = NI_realloc( hs->lhs , char*, sizeof(char *)*(hs->nattr+1) ) ;
181 hs->rhs = NI_realloc( hs->rhs , char*, sizeof(char *)*(hs->nattr+1) ) ;
182
183 /* this is the LHS string */
184
185 nn = ss.j - ss.i ; /* length of string */
186 hs->lhs[hs->nattr] = NI_malloc(char, nn+1) ;
187 NI_strncpy( hs->lhs[hs->nattr] , dat+ss.i , nn+1 ) ;
188 unescape_inplace( hs->lhs[hs->nattr] ) ;
189
190 hs->rhs[hs->nattr] = NULL ; /* in case there is no RHS */
191
192 id = ss.j ;
193 if( id >= ndat ) break ; /* end of input ? */
194 if( IS_QUOTE_CHAR(dat[id]) ) id++ ; /* skip close quote */
195 while( id < ndat && SKIPABL(dat[id]) ) id++ ; /* skip blanks */
196 if( id >= ndat ) break ; /* end of input ? */
197
198 if( dat[id] != '=' ){ /* no '=' means no RHS */
199 (hs->nattr)++ ; /* count the LHS and */
200 continue ; /* go get next attribute */
201 }
202
203 id++ ; /* skip the '=' */
204 while( id < ndat && SKIPABL(dat[id]) ) id++ ; /* skip blanks */
205 if( id >= ndat ) break ; /* end of input ? */
206
207 /* find next string (the RHS) */
208
209 ss = find_string( id , ndat , dat ) ;
210
211 if( ss.i < 0 || ss.j <= ss.i ) break ; /* didn't find a string */
212
213 #ifdef NIML_DEBUG
214 NI_dpr(" parse_header_stuff: next string = %.*s\n",ss.j-ss.i,dat+ss.i) ;
215 #endif
216
217 /* this is the RHS string */
218
219 nn = ss.j - ss.i ; /* length of string */
220 hs->rhs[hs->nattr] = NI_malloc(char, nn+1) ;
221 NI_strncpy( hs->rhs[hs->nattr] , dat+ss.i , nn+1 ) ;
222 unescape_inplace( hs->rhs[hs->nattr] ) ;
223
224 (hs->nattr)++ ; /* increment attribute count */
225
226 /* start scanning for next string at location id */
227
228 id = ss.j ;
229 if( IS_QUOTE_CHAR(dat[id]) ) id++ ; /* skip closing quote */
230
231 } /* end of loop over input */
232
233 if( nused != NULL ){
234 if( id >= ndat ) id = ndat-1 ;
235 *nused = id+1 ; /* number of bytes used from dat */
236 }
237
238 return hs ; /* the goal of all that work */
239 }
240
241 /*--------------------------------------------------------------------*/
242 /*! Decode a single type field. Return value is an intpair with
243 the .i component being the type code and the .j component being
244 the number of characters consumed. If the .i component is -1,
245 then no legal type was found (.j is still the number of chars
246 used in the scan).
247 ----------------------------------------------------------------------*/
248
decode_type_field(char * tf)249 intpair decode_type_field( char *tf )
250 {
251 intpair ans = {-1,1} ; /* default answer */
252 char tname[256] ;
253 int jj ;
254
255 /* check input for goodness */
256
257 if( tf == NULL || !isalpha(*tf) ) return ans ; /* prima facie bad */
258
259 #if 1 /*** The new way! Look for full names, not initials - RWCcox ***/
260
261 /* copy input into local string,
262 as long as 'name' characters are found,
263 then get the integer code for this type name */
264
265 for( jj=0 ; jj < 255 && IS_NAME_CHAR(tf[jj]) ; jj++ ) tname[jj] = tf[jj];
266 tname[jj] = '\0' ;
267 ans.i = NI_rowtype_name_to_code( tname ) ; /* where the names are stored */
268 ans.j = jj ;
269
270 #else /*** This is the old way! Replaced on 12 Feb 2003 - RWCox ***/
271
272 /* check if tf[0] starts a full datum name,
273 or if it is just an initial */
274
275 switch( tf[0] ){
276
277 default: break ; /* not a legal datum character */
278
279 case 'b':
280 ans.i = NI_BYTE ;
281 if( strncmp(tf,"byte" ,4) == 0 ) ans.j = 4 ;
282 break ;
283
284 case 's':
285 ans.i = NI_SHORT ;
286 if( strncmp(tf,"short" ,5) == 0 ) ans.j = 5 ;
287 break ;
288
289 case 'i':
290 ans.i = NI_INT ;
291 if( strncmp(tf,"int" ,3) == 0 ) ans.j = 3 ;
292 break ;
293
294 case 'f':
295 ans.i = NI_FLOAT ;
296 if( strncmp(tf,"float" ,5) == 0 ) ans.j = 5 ;
297 break ;
298
299 case 'c':
300 ans.i = NI_COMPLEX ;
301 if( strncmp(tf,"complex",7) == 0 ) ans.j = 7 ;
302 break ;
303
304 case 'd':
305 ans.i = NI_DOUBLE ;
306 if( strncmp(tf,"double" ,6) == 0 ) ans.j = 6 ;
307 break ;
308
309 case 'r':
310 ans.i = NI_RGB ;
311 if( strncmp(tf,"rgb" ,3) == 0 ) ans.j = 3 ;
312 break ;
313
314 case 'R':
315 ans.i = NI_RGBA ;
316 if( strncmp(tf,"RGBA" ,4) == 0 ) ans.j = 4 ;
317 break ;
318
319 case 'S':
320 ans.i = NI_STRING ;
321 if( strncmp(tf,"STRING" ,6) == 0 ) ans.j = 6 ;
322 break ;
323 }
324 #endif
325
326 return ans ;
327 }
328
329 /*--------------------------------------------------------------------*/
330 /*! Decode a single string into a bunch of strings, separated
331 by characters from the list in sep or by a space.
332 - Passing sep in as NULL means to use "," as the separator.
333 - In each sub-string, leading and trailing blanks will be excised.
334 - This can result in 0 length strings (e.g., "1,,2," will result
335 in the second and fourth output strings having 0 length).
336 (see also NI_strict_decode_string_list ZSS Dec 2010 )
337 ----------------------------------------------------------------------*/
338
NI_decode_string_list(char * ss,char * sep)339 NI_str_array * NI_decode_string_list( char *ss , char *sep )
340 {
341 NI_str_array *sar ;
342 int num , nn,id,jd , lss ;
343
344 if( ss == NULL || ss[0] == '\0' ) return NULL ; /* bad input */
345
346 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
347
348 sar = NI_malloc(NI_str_array, sizeof(NI_str_array)) ; /* create output */
349 sar->num = 0 ; sar->str = NULL ;
350
351 /* scan for sub-strings */
352
353 lss = NI_strlen(ss) ;
354 num = id = 0 ;
355 while( id < lss ){
356
357 /* skip current position ahead over whitespace */
358
359 while( id < lss && isspace(ss[id]) ) id++ ;
360 if( id == lss ) break ; /* ran out of string */
361
362 jd = id ; /* save current position (start of new string) */
363
364 /* skip ahead until ss[id] is a separator [or a space - 10 Dec 2002] */
365
366 while( id < lss && strchr(sep,ss[id]) == NULL && !isspace(ss[id]) ) id++;
367 if( id == jd ){ id++; continue; } /* is only a separator? */
368
369 /* new sub-string runs from ss[jd] to ss[id-1] */
370
371 sar->str = NI_realloc( sar->str , char*, sizeof(char *)*(num+1) ) ;
372
373 nn = id-jd ; /* length of sub-string */
374 #if 0
375 while( nn > 0 && isspace(ss[jd+nn-1]) ) nn-- ; /* clip trailing blanks */
376 #endif
377 sar->str[num] = NI_malloc(char, nn+1) ; /* make output string */
378 if( nn > 0 ) memcpy(sar->str[num],ss+jd,nn) ; /* copy sub-string */
379 sar->str[num++][nn] = '\0' ; /* terminate output */
380
381 id++ ; /* skip separator */
382 }
383
384 sar->num = num ; return sar ;
385 }
386
387 /*--------------------------------------------------------------------*/
388 /*! Like NI_decode_string_list, but will not use space as a delimiter
389 ZSS Dec 2010
390 ----------------------------------------------------------------------*/
391
NI_strict_decode_string_list(char * ss,char * sep)392 NI_str_array * NI_strict_decode_string_list( char *ss , char *sep )
393 {
394 NI_str_array *sar ;
395 int num , nn,id,jd , lss ;
396
397 if( ss == NULL || ss[0] == '\0' ) return NULL ; /* bad input */
398
399 if( sep == NULL || sep[0] == '\0' ) sep = "," ; /* default sep */
400
401 sar = NI_malloc(NI_str_array, sizeof(NI_str_array)) ; /* create output */
402 sar->num = 0 ; sar->str = NULL ;
403
404 /* scan for sub-strings */
405
406 lss = NI_strlen(ss) ;
407 num = id = 0 ;
408 while( id < lss ){
409
410 /* skip current position ahead over whitespace */
411
412 while( id < lss && isspace(ss[id]) ) id++ ;
413 if( id == lss ) break ; /* ran out of string */
414
415 jd = id ; /* save current position (start of new string) */
416
417 /* skip ahead until ss[id] is a separator */
418
419 while( id < lss && strchr(sep,ss[id]) == NULL ) id++;
420 if( id == jd ){ id++; continue; } /* is only a separator? */
421
422 /* new sub-string runs from ss[jd] to ss[id-1] */
423
424 sar->str = NI_realloc( sar->str , char*, sizeof(char *)*(num+1) ) ;
425
426 nn = id-jd ; /* length of sub-string */
427 #if 0
428 while( nn > 0 && isspace(ss[jd+nn-1]) ) nn-- ; /* clip trailing blanks */
429 #endif
430 sar->str[num] = NI_malloc(char, nn+1) ; /* make output string */
431 if( nn > 0 ) memcpy(sar->str[num],ss+jd,nn) ; /* copy sub-string */
432 sar->str[num++][nn] = '\0' ; /* terminate output */
433
434 id++ ; /* skip separator */
435 }
436
437 sar->num = num ; return sar ;
438 }
439
440 /*--------------------------------------------------------------------*/
441 /*! Return value = -1 if not found, otherwise is index [20 May 2010]. */
442
NI_str_array_find(char * targ,NI_str_array * sar)443 int NI_str_array_find( char *targ , NI_str_array *sar )
444 {
445 int ii ;
446
447 if( targ == NULL || *targ == '\0' || sar == NULL || sar->num < 1 ) return -1;
448
449 for( ii=0 ; ii < sar->num ; ii++ )
450 if( strcmp(targ,sar->str[ii]) == 0 ) return ii ;
451
452 return -1 ;
453 }
454
455 /*--------------------------------------------------------------------*/
456 /*! Decode a string that gives a list of floats [10 Jun 2007].
457 * Modified to allow ranges specified by a/b/c [06 Mar 2019].
458 *//*------------------------------------------------------------------*/
459
NI_decode_float_list(char * ss,char * sep)460 NI_float_array * NI_decode_float_list( char *ss , char *sep )
461 {
462 NI_float_array *far ; float *ar,val ; int num , ii,jj , nadd ;
463 NI_str_array *sar ; char *cc, *dd , *sl=NULL ;
464 int allow_slash=0 , nv=0 ; float v1,v2,dv=0.0f ;
465
466 sar = NI_decode_string_list( ss , sep ) ;
467 if( sar == NULL ) return NULL ;
468
469 far = NI_malloc(NI_float_array,sizeof(NI_float_array)) ;
470 ar = NULL ;
471 num = 0 ;
472
473 allow_slash = (sep == NULL || strstr(sep,"/") == NULL) ; /* 06 Mar 2019 */
474
475 for( jj=0 ; jj < sar->num ; jj++ ){
476 cc = sar->str[jj] ; dd = strstr(cc,"@") ;
477 if( allow_slash ) sl = strstr(cc,"/") ;
478 nadd = nv = 0 ;
479 if( dd != NULL ){
480 (void)sscanf(cc,"%d@%f",&nadd,&val) ;
481 if( nadd <= 0 ) continue ; /* bad */
482 } else if( sl != NULL ){ /* 06 Mar 2019 */
483 v1 = v2 = 0.0f ;
484 (void)sscanf(cc,"%f/%f/%d",&v1,&v2,&nv) ;
485 if( nv < 2 ){ nv = 0 ; nadd = 1 ; val = v1 ; }
486 else { dv = (v2-v1)/(nv-1.0f) ; }
487 } else {
488 val = (float)strtod(cc,NULL) ; nadd = 1 ;
489 }
490 if( nv <= 0 ){
491 ar = NI_realloc( ar , float , sizeof(float)*(num+nadd) ) ;
492 for( ii=0 ; ii < nadd ; ii++ ) ar[num++] = val ;
493 } else { /* 06 Mar 2019 */
494 ar = NI_realloc( ar , float , sizeof(float)*(num+nv) ) ;
495 for( ii=0 ; ii < nv ; ii++ ) ar[num++] = v1+ii*dv ;
496 }
497 }
498
499 NI_delete_str_array(sar) ;
500 far->ar = ar ; far->num = num ; return far ;
501 }
502
503 /*--------------------------------------------------------------------*/
504
NI_encode_float_list(NI_float_array * far,char * sep)505 char * NI_encode_float_list( NI_float_array *far , char *sep )
506 {
507 float *ar,val ; int num,jj,ii,ff ; char *car, cc='\0', fbuf[32] ;
508
509 if( far == NULL || far->num < 1 ) return NULL ;
510 if( sep != NULL ) cc = *sep ;
511 if( cc == '\0' ) cc = ',' ;
512
513 num = far->num ; ar = far->ar ;
514 car = NI_malloc(char,sizeof(char)*num*16) ; *car = '\0' ;
515
516 for( jj=0 ; jj < num ; ){ /* jj will be incremented inside */
517
518 /* encode ar[jj] value into fbuf */
519
520 val = ar[jj] ; ff = (int)val ;
521 if( val != (float)ff ) sprintf(fbuf,"%12.6g",val) ;
522 else sprintf(fbuf,"%d",ff) ;
523
524 /* strip trailing and leading blanks */
525
526 for( ff=strlen(fbuf) ; fbuf[ff]==' ' ; ff-- ) fbuf[ff] = '\0' ;
527 for( ff=0 ; fbuf[ff] == ' ' ; ff++ ) ;
528
529 /** last one? just print it and quit */
530
531 if( jj == num-1 ){
532 sprintf(car+strlen(car),"%s",fbuf+ff) ; break ;
533 }
534
535 /* scan for identical sequence of values */
536
537 for( ii=jj+1 ; ii < num && ar[ii]==val ; ii++ ) ; /*nada*/
538
539 if( ii > jj+1 ) /* encode values [jj..ii-1] */
540 sprintf(car+strlen(car),"%d@%s",ii-jj,fbuf+ff) ;
541 else
542 sprintf(car+strlen(car),"%s",fbuf+ff) ; /* just the [jj] value */
543 jj = ii ; /* the next one to process */
544 if( jj < num ) sprintf(car+strlen(car),"%c",cc) ; /* add the separator */
545
546 }
547
548 num = strlen(car) ;
549 car = NI_realloc( car , char , sizeof(char)*(num+1) ) ;
550 return car ;
551 }
552
553 /*--------------------------------------------------------------------*/
554 /*! Decode a string that gives a list of ints [21 Jun 2007]. */
555
NI_decode_int_list(char * ss,char * sep)556 NI_int_array * NI_decode_int_list( char *ss , char *sep )
557 {
558 NI_int_array *iar ; int *ar, num,jj , vv,ww,nadd,aa,da,ii; char *cc,*dd ;
559 NI_str_array *sar ;
560
561 sar = NI_decode_string_list( ss , sep ) ;
562 if( sar == NULL ) return NULL ;
563
564 iar = NI_malloc(NI_int_array,sizeof(NI_int_array)) ;
565 num = 0 ;
566 ar = NULL ;
567
568 for( jj=0 ; jj < sar->num ; jj++ ){
569 cc = sar->str[jj] ; dd = strstr(cc,"..") ;
570 if( dd == NULL ){
571 dd = strstr(cc,"@") ;
572 if( dd == NULL ){ /* a single number */
573 vv = (int)strtol( cc , NULL , 10 ) ;
574 nadd = 1 ; da = 0 ;
575 } else { /* repetitions of the same number */
576 aa = sscanf(cc,"%d@%d",&nadd,&vv) ;
577 if( nadd <= 0 ) continue ; /* bad */
578 da = 0 ;
579 }
580 } else { /* a sequence of numbers */
581 vv = (int)strtol( cc , NULL , 10 ) ;
582 ww = (int)strtol( dd+2, NULL , 10 ) ;
583 nadd = ww-vv ; da = 1 ;
584 if( nadd < 0 ){ nadd = -nadd; da = -1; }
585 nadd++ ;
586 }
587 ar = NI_realloc( ar , int , sizeof(int)*(num+nadd) ) ;
588 for( aa=vv,ii=0 ; ii < nadd ; ii++,aa+=da ) ar[num++] = aa ;
589 }
590
591 NI_delete_str_array(sar) ;
592 iar->num = num ; iar->ar = ar ; return iar ;
593 }
594
595 /*--------------------------------------------------------------------*/
596
NI_encode_int_list(NI_int_array * iar,char * sep)597 char * NI_encode_int_list( NI_int_array *iar , char *sep )
598 {
599 int *ar ; int ii,num,jj ; char *car , cc='\0' , fbuf[32] ;
600
601 if( iar == NULL || iar->num < 1 ) return NULL ;
602 if( sep != NULL ) cc = *sep ;
603 if( cc == '\0' ) cc = ',' ;
604
605 num = iar->num ; ar = iar->ar ;
606 car = NI_malloc(char,sizeof(char)*num*9) ; *car = '\0' ;
607
608 for( jj=0 ; jj < num ; ){
609
610 /** last one? just do it and quit */
611 if( jj == num-1 ){
612 sprintf(car+strlen(car),"%d",ar[jj]) ; break ;
613 }
614
615 /* scan for identical sequence */
616 for( ii=jj+1 ; ii < num && ar[ii]-ar[ii-1]==0 ; ii++ ) ; /*nada*/
617
618 if( ii > jj+1 ){ /* encode values [jj..ii-1] */
619 sprintf(fbuf,"%d@%d",ii-jj,ar[jj]) ;
620 } else { /* scan for increasing sequence */
621 for( ii=jj+1 ; ii < num && ar[ii]-ar[ii-1]==1 ; ii++ ) ; /*nada*/
622
623 if( ii == jj+1 )
624 sprintf(fbuf,"%d",ar[jj]); /* encode one value */
625 else if( ii == jj+2 )
626 sprintf(fbuf,"%d%c%d",ar[jj],cc,ar[jj+1]); /* encode 2 values */
627 else
628 sprintf(fbuf,"%d..%d",ar[jj],ar[ii-1]); /* encode values [jj..ii-1] */
629 }
630 jj = ii ;
631 if( jj < num ) sprintf(car+strlen(car),"%s%c",fbuf,cc) ;
632 else sprintf(car+strlen(car),"%s" ,fbuf ) ;
633 }
634
635 num = strlen(car) ;
636 car = NI_realloc( car , char , sizeof(char)*(num+1) ) ;
637 return car ;
638 }
639
640 /*--------------------------------------------------------------------*/
641 /*! Decode a ni_dimen string into an array of integers.
642 Returns NULL if the input is bad bad bad.
643 ----------------------------------------------------------------------*/
644
decode_dimen_string(char * ds)645 int_array * decode_dimen_string( char *ds )
646 {
647 int num , dd,nn,id,jd , lds ;
648 int_array *iar ;
649
650 if( ds == NULL || ds[0] == '\0' ) return NULL ;
651
652 iar = NI_malloc(int_array, sizeof(int_array)) ; /* create output */
653 iar->num = 0 ; iar->ar = NULL ;
654
655 /* scan string for integers */
656
657 num = id = 0 ;
658 lds = NI_strlen(ds) ;
659 do{
660 /* skip ahead until ds[id] is a digit */
661
662 while( id < lds && !isdigit(ds[id]) ) id++ ;
663 if( id == lds ) break ; /* end of input */
664
665 /* decode integer starting here */
666
667 nn = jd = 0 ;
668 sscanf( ds+id , "%d%n" , &jd , &nn ) ; /* get the count */
669 if( jd < 0 || nn <= 0 ) break ; /* something bad */
670 id += nn ; /* skip these chars */
671
672 /* extend output array, store new dimension in it */
673
674 iar->ar = NI_realloc( iar->ar , int, sizeof(int)*(num+1) ) ;
675 iar->ar[num++] = jd ;
676 } while(1) ;
677
678 if( num == 0 ){ NI_free(iar); return NULL; } /* bad */
679
680 iar->num = num ; return iar ;
681 }
682
683 /*--------------------------------------------------------------------*/
684 /*! Decode a data type string into an array of integer codes.
685 Returns NULL if the input is bad bad bad.
686 ----------------------------------------------------------------------*/
687
decode_type_string(char * ts)688 int_array * decode_type_string( char *ts )
689 {
690 int num, typ, lts, id,jd, nn,kk ;
691 int_array *iar ;
692 intpair dc ;
693
694 if( ts == NULL || ts[0] == '\0' ) return NULL ;
695
696 iar = NI_malloc(int_array, sizeof(int_array)) ; /* create output */
697 iar->num = 0 ; iar->ar = NULL ;
698
699 /* scan type string to find counts/fields and add to output */
700
701 lts = NI_strlen(ts) ;
702 num = 0 ; /* will be count of fields */
703
704 for( id=kk=0 ; id < lts ; ){ /* loop over input string */
705
706 if( isdigit(ts[id]) ){ /* a count prefix */
707 jd = nn = 0 ;
708 sscanf( ts+id , "%d%n" , &jd , &nn ) ; /* get the count */
709 if( jd <= 0 || nn <= 0 ){ /* shouldn't happen */
710 NI_free(iar->ar) ; NI_free(iar) ; return NULL ;
711 }
712 id += nn ; /* skip count prefix characters */
713 if( ts[id] == '*' ) id++ ; /* allow for "3*float" */
714
715 } else if( isalpha(ts[id]) ){ /* start of a type name */
716 jd = 1 ; /* default count of 1 */
717
718 } else {
719 id++ ; continue ; /* skip this character */
720 }
721
722 dc = decode_type_field( ts+id ) ;
723
724 /* dc.i = type code; dc.j = character count used to get type code */
725
726 id += dc.j ; /* skip these characters */
727 if( dc.i < 0 ) continue ; /* bad type code */
728
729 num += jd ; /* this many fields so far */
730
731 /* extend output array length */
732
733 iar->ar = NI_realloc( iar->ar , int, sizeof(int)*num ) ;
734
735 /* put values into output array */
736
737 for( nn=0 ; nn < jd ; nn++ ) iar->ar[kk++] = dc.i ;
738
739 } /* end of loop over input string */
740
741 /* nothing found? */
742
743 if( num <= 0 ){
744 NI_free(iar->ar) ; NI_free(iar) ; return NULL ; /* bad */
745 }
746
747 iar->num = num ; return iar ;
748 }
749