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