1 #include "niml_private.h"
2 
3 /*-----------------------------------------------------------------------*/
4 /* Vector labels [12 Sep 2018] */
5 
NI_set_veclab_from_stringlist(NI_element * nel,char * vstr)6 void NI_set_veclab_from_stringlist( NI_element *nel , char *vstr )
7 {
8    if( NI_element_type(nel) != NI_ELEMENT_TYPE || vstr == NULL ) return ;
9 
10    if( nel->vec_num > 0 ){
11      int pp ;
12      NI_str_array *sar = NI_decode_string_list( vstr , "," ) ;
13      if( sar != NULL && sar->num > 0 ){
14        int ns=sar->num  ;
15        if( nel->vec_lab != NULL ) NI_free(nel->vec_lab) ;
16        nel->vec_lab = (char **)NI_malloc(char*,sizeof(char *)*nel->vec_num) ;
17        for( pp=0 ; pp < nel->vec_num ; pp++ ){
18           nel->vec_lab[pp] = (pp < ns) ? NI_strdup(sar->str[pp])
19                                        : NI_strdup("\0")        ;
20        }
21      }
22    }
23    return ;
24 }
25 
26 /*-----------------------------------------------------------------------*/
27 
NI_set_attribute_from_veclab_array(NI_element * nel,char ** vec_lab)28 void NI_set_attribute_from_veclab_array( NI_element *nel , char **vec_lab )
29 {
30    char *vla ; int nvla , ii ;
31 
32    if( NI_element_type(nel) != NI_ELEMENT_TYPE || nel->vec_num == 0 ) return ;
33 
34    if( vec_lab == NULL ) vec_lab = nel->vec_lab ;
35    if( vec_lab == NULL ) return ;
36 
37    for( nvla=ii=0 ; ii < nel->vec_num ; ii++ )
38      nvla += NI_strlen(vec_lab[ii])+8 ;
39 
40    vla = (char *)malloc(sizeof(char)*nvla) ; vla[0] = '\0' ;
41    for( ii=0 ; ii < nel->vec_num ; ii++ ){
42      sprintf( vla+strlen(vla) , "%s%s" ,
43               (vec_lab[ii] != NULL) ? vec_lab[ii] : "\0" ,
44               (ii < nel->vec_num-1) ? ","         : "\0"  ) ;
45    }
46 
47    NI_set_attribute(nel,"ni_veclab",vla) ;
48    free(vla) ;
49    return ;
50 }
51 
52 /*-----------------------------------------------------------------------*/
53 /*! Construct an empty data element from a header.
54     - The data vectors will have space allocated, but they will be
55       filled with all zero bytes.
56     - If the header was "empty" (ended in "/>"), then no vectors will
57       be allocated, and nel->vec_num=0.
58     - This function is used by NI_read_element() to create the
59       data element after the header has been parsed.
60     - 27 Mar 2003: modified to allow vec_len=0, indicating vector
61       length to be inferred from amount of data
62 -------------------------------------------------------------------------*/
63 
make_empty_data_element(header_stuff * hs)64 NI_element * make_empty_data_element( header_stuff *hs )
65 {
66    NI_element *nel ;
67    int ii ;
68 
69    if( hs == NULL || hs->name == NULL ) return NULL ;
70 
71 #ifdef NIML_DEBUG
72 NI_dpr("ENTER make_empty_data_element\n") ;
73 #endif
74 
75    nel = NI_malloc(NI_element, sizeof(NI_element) ) ;
76 
77    nel->type = NI_ELEMENT_TYPE ;
78 
79    nel->outmode = -1 ;   /* 29 Mar 2005 */
80 
81    /* move name and attributes from hs to new element */
82 
83    nel->name = hs->name ; hs->name = NULL ;
84 
85    nel->attr_num = hs->nattr ;
86 
87    if( nel->attr_num > 0 ){
88       nel->attr_lhs = hs->lhs ; hs->lhs = NULL ;
89       nel->attr_rhs = hs->rhs ; hs->rhs = NULL ;
90    } else {
91       nel->attr_lhs = nel->attr_rhs = NULL ;
92    }
93 
94    /* set default vector parameters [indicating no data] */
95 
96    nel->vec_num = 0 ;
97    nel->vec_len = 0 ;
98    nel->vec_typ = NULL ;
99    nel->vec     = NULL ;
100    nel->vec_lab = NULL ;  /* 11 Sep 2018 */
101    nel->filename= NULL ;  /* 16 Jun 2020 */
102 
103    nel->vec_filled = 0 ;  /* no data has been filled into vectors */
104 
105    nel->vec_rank        = 0 ;
106    nel->vec_axis_len    = NULL ;
107    nel->vec_axis_delta  = NULL ;
108    nel->vec_axis_origin = NULL ;
109    nel->vec_axis_unit   = NULL ;
110    nel->vec_axis_label  = NULL ;
111 
112    if( !hs->empty ){  /* find and process ni_* attributes about vectors */
113 
114      /* ni_type attribute: set types of vectors */
115 
116      ii = string_index( "ni_type" , nel->attr_num , nel->attr_lhs ) ;
117 
118      if( ii >= 0 && nel->attr_rhs[ii] != NULL ){
119        int_array *iar = decode_type_string( nel->attr_rhs[ii] ) ;
120        if( iar != NULL ){
121          nel->vec_num = iar->num ;  /* number of vectors */
122          nel->vec_typ = iar->ar ;   /* vector types */
123          NI_free(iar) ;             /* just the shell of the struct */
124        }
125      }
126 
127      /* ni_dimen attribute: set vector length and rank */
128 
129      ii = string_index( "ni_dimen" , nel->attr_num , nel->attr_lhs ) ;
130 
131      if( ii >= 0 && nel->attr_rhs[ii] != NULL ){
132         int_array *dar = decode_dimen_string( nel->attr_rhs[ii] ) ;
133         if( dar != NULL && dar->num > 0 ){
134            int nd=dar->num , qq,pp ;
135            /* compute product of all dimensions */
136            for( qq=1,pp=0 ; pp < nd ; pp++ ) qq *= dar->ar[pp] ;
137            nel->vec_len      = qq ;      /* length of vectors */
138            nel->vec_rank     = nd ;      /* number of dimensions */
139            nel->vec_axis_len = dar->ar ; /* array of dimension lengths */
140            NI_free(dar) ;                /* just the struct shell */
141            if( nel->vec_len == 0 )       /* 27 Mar 2003 */
142              nel->vec_rank = 1 ;
143         }
144      }
145 
146      /* if we had ni_dimen, also try ni_delta */
147 
148      ii = string_index( "ni_delta" , nel->attr_num , nel->attr_lhs ) ;
149      if( ii >= 0 && nel->vec_rank > 0 ){
150         NI_str_array *sar = NI_decode_string_list( nel->attr_rhs[ii] , NULL ) ;
151         if( sar != NULL && sar->num > 0 ){
152            int ns=sar->num , nd=nel->vec_rank , pp ;
153            nel->vec_axis_delta = NI_malloc(float,sizeof(float)*nd) ;
154            if( nd > ns ) nd = ns ;
155            for( pp=0 ; pp < nd ; pp++ )
156              sscanf( sar->str[pp] , "%f" , nel->vec_axis_delta+pp ) ;
157            NI_delete_str_array(sar) ;
158         }
159      }
160 
161      /* if we had ni_dimen, also try ni_origin */
162 
163      ii = string_index( "ni_origin" , nel->attr_num , nel->attr_lhs ) ;
164      if( ii >= 0 && nel->vec_rank > 0 ){
165         NI_str_array *sar = NI_decode_string_list( nel->attr_rhs[ii] , NULL ) ;
166         if( sar != NULL && sar->num > 0 ){
167            int ns=sar->num , nd=nel->vec_rank , pp ;
168            nel->vec_axis_origin = NI_malloc(float,sizeof(float)*nd) ;
169            if( nd > ns ) nd = ns ;
170            for( pp=0 ; pp < nd ; pp++ )
171              sscanf( sar->str[pp] , "%f" , nel->vec_axis_origin+pp ) ;
172            NI_delete_str_array(sar) ;
173         }
174      }
175 
176      /* if we had ni_dimen, also try ni_units */
177 
178      ii = string_index( "ni_units" , nel->attr_num , nel->attr_lhs ) ;
179      if( ii >= 0 && nel->vec_rank > 0 ){
180         NI_str_array *sar = NI_decode_string_list( nel->attr_rhs[ii] , NULL ) ;
181         if( sar != NULL && sar->num > 0 ){
182            int ns=sar->num , nd=nel->vec_rank , pp ;
183            nel->vec_axis_unit = NI_malloc(char*,sizeof(char *)*nd) ;
184            if( nd > ns ) nd = ns ;
185            for( pp=0 ; pp < nd ; pp++ )
186              nel->vec_axis_unit[pp] = NI_strdup(sar->str[pp]) ;
187            NI_delete_str_array(sar) ;
188         }
189      }
190 
191      /* if we had ni_dimen, also try ni_axes */
192 
193      ii = string_index( "ni_axes" , nel->attr_num , nel->attr_lhs ) ;
194      if( ii >= 0 && nel->vec_rank > 0 ){
195         NI_str_array *sar = NI_decode_string_list( nel->attr_rhs[ii] , NULL ) ;
196         if( sar != NULL && sar->num > 0 ){
197            int ns=sar->num , nd=nel->vec_rank , pp ;
198            nel->vec_axis_label = NI_malloc(char*,sizeof(char *)*nd) ;
199            if( nd > ns ) nd = ns ;
200            for( pp=0 ; pp < nd ; pp++ )
201              nel->vec_axis_label[pp] = NI_strdup(sar->str[pp]) ;
202            NI_delete_str_array(sar) ;
203         }
204      }
205 
206      /* see if we have vector label strings [11 Sep 2018] */
207 
208      ii = string_index( "ni_veclab" , nel->attr_num , nel->attr_lhs ) ;
209      if( nel->vec_num > 0 && ii > 0 ){
210        NI_set_veclab_from_stringlist( nel , nel->attr_rhs[ii] ) ;
211      }
212 
213      /* supply vector parameters if none was given */
214      /* (remember, we DON'T have an empty element) */
215 
216      if( nel->vec_num == 0 ){                    /* default type */
217         nel->vec_num    = 1 ;
218         nel->vec_typ    = NI_malloc(int,sizeof(int)) ;
219         nel->vec_typ[0] = NI_BYTE ;
220      }
221 
222      if( nel->vec_rank == 0 ){                  /* default dimensions */
223         nel->vec_len         = 0 ;
224         nel->vec_rank        = 1 ;
225         nel->vec_axis_len    = NI_malloc(int, sizeof(int)) ;
226         nel->vec_axis_len[0] = 1 ;
227      }
228 
229      /* now allocate space for vectors defined above */
230 
231      nel->vec = NI_malloc(void*, sizeof(void *)*nel->vec_num ) ;
232 
233      /* 27 Mar 2003: only allocate space if we know how long they are */
234 
235      if( nel->vec_len > 0 ){
236        for( ii=0 ; ii < nel->vec_num ; ii++ )
237          nel->vec[ii] = NI_malloc(void,
238                                   NI_type_size(nel->vec_typ[ii])*nel->vec_len) ;
239      } else {
240        for( ii=0 ; ii < nel->vec_num ; ii++ )
241          nel->vec[ii] = NULL ;
242      }
243 
244    } /* end of processing non-empty header stuff */
245 
246    return nel ;
247 }
248 
249 /*-------------------------------------------------------------------------*/
250 /*! Make an empty group element from parsed header info.
251     The attributes in the header are assigned to the group, and the group
252     parts are initialized to nothing.
253 ---------------------------------------------------------------------------*/
254 
make_empty_group_element(header_stuff * hs)255 NI_group * make_empty_group_element( header_stuff *hs )
256 {
257    NI_group *ngr ;
258 
259    if( hs == NULL || hs->name == NULL ) return NULL ;
260 
261    ngr = NI_malloc(NI_group, sizeof(NI_group) ) ;
262 
263    ngr->type = NI_GROUP_TYPE ;
264 
265    ngr->name = hs->name ; hs->name = NULL ;  /* 24 Feb 2005 */
266 
267    ngr->outmode = -1 ;   /* 29 Mar 2005 */
268 
269    /* move attributes from hs to new element */
270 
271    ngr->attr_num = hs->nattr ;
272 
273    if( ngr->attr_num > 0 ){
274      ngr->attr_lhs = hs->lhs ; hs->lhs = NULL ;
275      ngr->attr_rhs = hs->rhs ; hs->rhs = NULL ;
276    } else {
277      ngr->attr_lhs = ngr->attr_rhs = NULL ;
278    }
279 
280    /* have no pieces-parts yet */
281 
282    ngr->part_num = 0 ;
283    ngr->part_typ = NULL ;
284    ngr->part     = NULL ;
285    ngr->filename = NULL ; /* 16 Jun 2020 */
286 
287    return ngr ;
288 }
289 
290 /*-------------------------------------------------------------------------*/
291 /*! Byte size of a given integer type code.
292     Modified 13 Feb 2003 to use the new rowtype stuff.
293 ---------------------------------------------------------------------------*/
294 
NI_type_size(int tval)295 int NI_type_size( int tval )
296 {
297    int ii = NI_rowtype_code_to_size( tval ) ;
298    return (ii > 0) ? ii : 0 ;
299 }
300 
301 /*************************************************************************/
302 /********** Functions to create NIML data and group elements *************/
303 /*************************************************************************/
304 
305 /*-----------------------------------------------------------------------*/
306 /*! Return the type of something that points to a NI element.
307     - The input should be point to a NI_element, NI_group, or NI_procins.
308     - The return value is NI_ELEMENT_TYPE, NI_GROUP_TYPE, NI_PROCINS_TYPE,
309       or -1 if the type is anything else or unknowable.
310 -------------------------------------------------------------------------*/
311 
NI_element_type(void * nini)312 int NI_element_type( void *nini )
313 {
314    NI_element *nel = (NI_element *) nini ;
315    NI_group   *ngr = (NI_group *)   nini ;
316    NI_procins *npi = (NI_procins *) nini ;  /* 16 Mar 2005 */
317 
318    if( nini == NULL ) return -1 ;
319 
320    if( nel->type == NI_ELEMENT_TYPE ) return NI_ELEMENT_TYPE ;
321    if( ngr->type == NI_GROUP_TYPE   ) return NI_GROUP_TYPE   ;
322    if( npi->type == NI_PROCINS_TYPE ) return NI_PROCINS_TYPE ;
323 
324    return -1 ;
325 }
326 
327 /*-----------------------------------------------------------------------*/
328 /*! Return the name of a NI element.  If the input is bad, returns
329     a NULL pointer.  Do not free this pointer!  It points to the
330     name string inside the element struct.
331 -------------------------------------------------------------------------*/
332 
NI_element_name(void * nini)333 char * NI_element_name( void *nini )
334 {
335    NI_element *nel = (NI_element *) nini ;
336    NI_group   *ngr = (NI_group *)   nini ;
337    NI_procins *npi = (NI_procins *) nini ;
338 
339    if( nini == NULL ) return NULL ;
340 
341    if( nel->type == NI_ELEMENT_TYPE ) return nel->name ;
342    if( ngr->type == NI_GROUP_TYPE   ) return ngr->name ;
343    if( npi->type == NI_PROCINS_TYPE ) return npi->name ;
344 
345    return NULL ;
346 }
347 
348 /*-----------------------------------------------------------------------*/
349 /*! Expunge a data or group element and its contents from the universe.
350 -------------------------------------------------------------------------*/
351 
NI_free_element(void * nini)352 void NI_free_element( void *nini )
353 {
354    int ii , tt=NI_element_type(nini) ;
355 
356    if( tt < 0 ) return ; /* bad input */
357 
358    /*-- erase contents of data element --*/
359 
360    if( tt == NI_ELEMENT_TYPE ){
361       NI_element *nel = (NI_element *)nini ;
362 
363       NI_free(nel->name) ;
364       for( ii=0 ; ii < nel->attr_num ; ii++ ){
365          NI_free( nel->attr_lhs[ii] ) ;
366          NI_free( nel->attr_rhs[ii] ) ;
367       }
368       NI_free( nel->attr_lhs ) ;
369       NI_free( nel->attr_rhs ) ;
370 
371       /* 14 Feb 2003: NI_free_column() will also free var dim arrays */
372 
373       if( nel->vec != NULL ){
374         for( ii=0 ; ii < nel->vec_num ; ii++ ){
375            NI_free_column( NI_rowtype_find_code(nel->vec_typ[ii]) ,
376                            nel->vec_len , nel->vec[ii]             ) ;
377         }
378       }
379 
380       NI_free( nel->vec_typ  ) ;
381       NI_free( nel->vec ) ;
382 
383       NI_free(nel->vec_axis_len) ;
384       NI_free(nel->vec_axis_delta) ;
385       NI_free(nel->vec_axis_origin) ;
386       NI_free(nel->vec_axis_unit) ;
387       NI_free(nel->vec_axis_label) ;
388 
389       if( nel->vec_lab != NULL ){  /* 12 Sep 2018 */
390         for( ii=0 ; ii < nel->vec_num ; ii++ ) NI_free(nel->vec_lab[ii]) ;
391         NI_free(nel->vec_lab) ;
392       }
393 
394       NI_free( nel->filename ) ;  /* 16 Jun 2020 */
395 
396       NI_free( nel ) ;
397 
398    /*-- erase contents of group element --*/
399 
400    } else if( tt == NI_GROUP_TYPE ){
401       NI_group *ngr = (NI_group *)nini ;
402 
403       for( ii=0 ; ii < ngr->attr_num ; ii++ ){
404         NI_free( ngr->attr_lhs[ii] ) ;
405         NI_free( ngr->attr_rhs[ii] ) ;
406       }
407       NI_free( ngr->attr_lhs ) ;
408       NI_free( ngr->attr_rhs ) ;
409 
410       if( ngr->part != NULL ){
411         for( ii=0 ; ii < ngr->part_num ; ii++ )
412           NI_free_element( ngr->part[ii] ) ;     /* recursion */
413       }
414 
415       NI_free( ngr->part_typ ) ;
416       NI_free( ngr->part ) ;
417       NI_free( ngr->name ) ;    /* 03 Jun 2002 */
418       NI_free( ngr->filename ); /* 16 Jun 2020 */
419       NI_free( ngr ) ;
420 
421    /*-- erase contents of processing instruction --*/
422 
423    } else if( tt == NI_PROCINS_TYPE ){
424       NI_procins *npi = (NI_procins *)nini ;
425 
426       for( ii=0 ; ii < npi->attr_num ; ii++ ){
427         NI_free( npi->attr_lhs[ii] ) ;
428         NI_free( npi->attr_rhs[ii] ) ;
429       }
430       NI_free( npi->attr_lhs ) ;
431       NI_free( npi->attr_rhs ) ;
432 
433       NI_free( npi->name ) ;    /* 03 Jun 2002 */
434       NI_free( npi ) ;
435    }
436 
437    return ;
438 }
439 
440 /*-----------------------------------------------------------------------*/
441 /*! Expunge all data from element or group.           17 Jul 2006 [rickr]
442 -------------------------------------------------------------------------*/
443 
NI_free_element_data(void * nini)444 void NI_free_element_data( void *nini )
445 {
446    int ii , tt=NI_element_type(nini) ;
447 
448    if( tt < 0 ) return ; /* bad input */
449 
450    /*-- if element, nuke data --*/
451 
452    if( tt == NI_ELEMENT_TYPE ){
453       NI_element *nel = (NI_element *)nini ;
454 
455       if( nel->vec != NULL ){
456         for( ii=0 ; ii < nel->vec_num ; ii++ )
457            NI_free_column( NI_rowtype_find_code(nel->vec_typ[ii]) ,
458                            nel->vec_len , nel->vec[ii]             ) ;
459          NI_free( nel->vec ) ;
460          nel->vec = NULL ;
461       }
462 
463    /*-- if group, recur --*/
464 
465    } else if( tt == NI_GROUP_TYPE ){
466       NI_group *ngr = (NI_group *)nini ;
467 
468       if( ngr->part != NULL ){
469         for( ii=0 ; ii < ngr->part_num ; ii++ )
470           NI_free_element_data( ngr->part[ii] ) ;     /* recursion */
471       }
472    }
473 
474    /*-- no other cases for data --*/
475 
476    return ;
477 }
478 
479 /*-----------------------------------------------------------------------*/
480 
NI_init_veclen(NI_element * nel,int veclen)481 static void NI_init_veclen( NI_element *nel , int veclen )
482 {
483    if( veclen == 0 ){                      /* empty element */
484      nel->vec_len      = 0 ;
485      nel->vec_filled   = 0 ;
486      nel->vec_rank     = 0 ;
487      nel->vec_axis_len = NULL ;
488    } else {                                /* element with data to */
489      nel->vec_len         = veclen ;       /* come via NI_add_column */
490      nel->vec_filled      = veclen ;
491      nel->vec_rank        = 1 ;
492      nel->vec_axis_len    = NI_malloc(int, sizeof(int)) ;
493      nel->vec_axis_len[0] = veclen ;
494    }
495 }
496 
497 /*-----------------------------------------------------------------------*/
498 /*! Create a new data element.
499 
500     - name   = string name for header.
501     - veclen = size (length) of vectors (ni_dimen attribute).
502                - Vectors are added with NI_add_column().
503                - Set this to zero for "empty" elements (those with only
504                  headers, no data).
505 
506     Return is NULL if inputs are stupid or criminal or insane.
507 -------------------------------------------------------------------------*/
508 
NI_new_data_element(char * name,int veclen)509 NI_element * NI_new_data_element( char *name , int veclen )
510 {
511    NI_element *nel ;
512 
513    if( name == NULL || name[0] == '\0' || veclen < 0 ) return NULL ;
514 
515    nel = NI_malloc(NI_element, sizeof(NI_element) ) ;
516 
517    nel->type = NI_ELEMENT_TYPE ;  /* mark as being a data element */
518 
519    nel->outmode = -1 ;   /* 29 Mar 2005 */
520 
521    nel->name = NI_strdup(name) ;
522    nel->attr_num = 0 ;
523    nel->attr_lhs = nel->attr_rhs = NULL ;  /* no attributes yes */
524 
525    nel->vec_num = 0 ;                      /* no vectors yet */
526    nel->vec_typ = NULL ;
527    nel->vec     = NULL ;
528    nel->vec_lab = NULL ;  /* 11 Sep 2018 */
529    nel->filename= NULL ;  /* 16 Jun 2020 */
530 
531    NI_init_veclen( nel , veclen ) ;  /* 19 Sep 2008 */
532 
533    nel->vec_axis_delta  = NULL ;
534    nel->vec_axis_origin = NULL ;
535    nel->vec_axis_unit   = NULL ;
536    nel->vec_axis_label  = NULL ;
537 
538    return nel ;
539 }
540 
541 /*-----------------------------------------------------------------------*/
542 /*! Insert a label for column #cc into a data element.
543     Note that if column #cc does not exist, nothing will happen,
544     so this function should be called AFTER NI_add_column(). [11 Sep 2018]
545 -------------------------------------------------------------------------*/
546 
NI_set_column_label(NI_element * nel,int cc,char * lab)547 void NI_set_column_label( NI_element *nel , int cc , char *lab )
548 {
549 
550    if( nel == NULL || nel->vec_len <= 0 )            return ;
551    if( nel->type != NI_ELEMENT_TYPE )                return ;
552    if( cc < 0 || cc >= nel->vec_num || lab == NULL ) return ;
553 
554    if( nel->vec_lab == NULL ){  /* create empty label array */
555      int pp ;
556      nel->vec_lab = (char **)NI_malloc(char*,sizeof(char *)*nel->vec_num) ;
557      for( pp=0 ; pp < nel->vec_num ; pp++ ) nel->vec_lab[pp] = NI_strdup("\0") ;
558    }
559 
560    if( nel->vec_lab[cc] != NULL ) NI_free(nel->vec_lab[cc]) ;
561    nel->vec_lab[cc] = NI_strdup(lab) ;
562    return ;
563 }
564 
565 /*-----------------------------------------------------------------------*/
566 /*! Add a vector (column) of data to a data element.
567 
568     - nel = data element to modify
569     - typ = integer type code of data (e.g., NI_FLOAT)
570     - arr = pointer to data values - must be an array of length veclen
571             (same value as used in NI_new_data_element() call)
572     - if arr is NULL, then will add a zero-filled column of the given
573       type to the data element
574 
575     The data array is copied into the element.  If the element was
576     specified with veclen=0, then this function will do nothing.
577     Since this function has no return value, the only way to check for
578     such an error is to see if nel->vec_num was incremented.  Or don't
579     be so stupid as to make this error.
580 -------------------------------------------------------------------------*/
581 
NI_add_column(NI_element * nel,int typ,void * arr)582 void NI_add_column( NI_element *nel , int typ , void *arr )
583 {
584    int nn ;
585    NI_rowtype *rt ;
586 
587    /* check for reasonable inputs */
588 
589    if( nel == NULL || nel->vec_len <= 0 )            return ;
590    if( nel->type != NI_ELEMENT_TYPE )                return ;
591    rt = NI_rowtype_find_code(typ) ; if( rt == NULL ) return ;
592 
593    /* get number of vectors currently in element */
594 
595    nn = nel->vec_num ;
596 
597    /* add 1 to the vec_typ array */
598 
599    nel->vec_typ     = NI_realloc( nel->vec_typ, int, sizeof(int)*(nn+1) ) ;
600    nel->vec_typ[nn] = typ ;
601 
602    /* add 1 element to the vec array, and copy data into it */
603 
604    nel->vec = NI_realloc( nel->vec , void*, sizeof(void *)*(nn+1) ) ;
605    if( arr != NULL )
606      nel->vec[nn] = NI_copy_column( rt , nel->vec_len , arr ) ;
607    else
608      nel->vec[nn] = NI_malloc(void, rt->size * nel->vec_len ) ;
609 
610    if( nel->vec_lab != NULL ){  /* 11 Sep 2018 */
611      nel->vec_lab     = NI_realloc( nel->vec_lab , char* , sizeof(char *)*(nn+1) ) ;
612      nel->vec_lab[nn] = NI_strdup("\0") ;
613    }
614 
615    /* add 1 to the count of vectors */
616 
617    nel->vec_num = nn+1 ;
618 
619    /* if element has "ni_type" attribute, adjust it   14 Jul 2006 [rickr] */
620    if( NI_get_attribute(nel, "ni_type") )
621       NI_set_ni_type_atr(nel) ;
622 
623    return ;
624 }
625 
626 /*-------------------------------------------------------------------------*/
627 /*!
628    Like add_column, but inserts the column at nel->vec[icol] rather than
629    at the end.
630    if icol < 0 || icol > nel->vec_num then icol = nel->vec_num
631 */
NI_insert_column(NI_element * nel,int typ,void * arr,int icol)632 void NI_insert_column( NI_element *nel , int typ , void *arr, int icol )
633 {
634    int nn, ii ;
635    NI_rowtype *rt ;
636 
637    /* check for reasonable inputs */
638 
639    if( nel == NULL || nel->vec_len <= 0 )            return ;
640    if( nel->type != NI_ELEMENT_TYPE )                return ;
641    rt = NI_rowtype_find_code(typ) ; if( rt == NULL ) return ;
642 
643    /* get number of vectors currently in element */
644    nn = nel->vec_num ;
645 
646    if (icol > nn || icol < 0) icol = nn;
647 
648    /* call add column */
649    NI_add_column(nel, typ, arr);
650 
651    /* check on success */
652    if (nel->vec_num != nn+1) return ;  /* misere */
653    nn = nel->vec_num ;  /* the new number of vectors */
654 
655    NI_move_column(nel, nn-1, icol);
656 
657    return ;
658 }
659 
660 /*-------------------------------------------------------------------------*/
661 /*!
662    move a column from index ibefore to iafter
663    if ibefore (or iafter) is (< 0 || > nel->vec_num) then
664       ibefore (or iafter) = nel->vec_num-1
665 */
NI_move_column(NI_element * nel,int ibefore,int iafter)666 void NI_move_column(NI_element *nel, int ibefore, int iafter)
667 {
668    int nn, ii ;
669    int typ_buf;
670    void *col_buf;
671    char *lab_buf=NULL ;  /* 11 Sep 2018 */
672 
673    if (nel == NULL || nel->vec_len <= 0 )            return ;
674 
675    nn = nel->vec_num ;
676    if (ibefore < 0 || ibefore >= nn) ibefore = nn-1;
677    if (iafter  < 0 || iafter  >= nn) iafter  = nn-1;
678 
679    /* nothing to see here? */
680    if (ibefore == iafter) return;
681 
682    /* do the deed */
683    /* store the initial values */
684    typ_buf = nel->vec_typ[ibefore];
685    col_buf = nel->vec[ibefore];
686    if( nel->vec_lab != NULL ) lab_buf = nel->vec_lab[ibefore] ;
687    /* shift */
688    if (ibefore > iafter) {
689       /* shift columns to left*/
690       for (ii=ibefore; ii > iafter; --ii) {
691          nel->vec[ii] = nel->vec[ii-1];
692          nel->vec_typ[ii] = nel->vec_typ[ii-1];
693          if( nel->vec_lab != NULL )
694            nel->vec_lab[ii] = nel->vec_lab[ii-1] ;
695       }
696    } else {
697       /* shift columns to right*/
698       for (ii=ibefore; ii < iafter; ++ii) {
699          nel->vec[ii] = nel->vec[ii+1];
700          nel->vec_typ[ii] = nel->vec_typ[ii+1];
701          if( nel->vec_lab != NULL )
702            nel->vec_lab[ii] = nel->vec_lab[ii+1] ;
703       }
704    }
705 
706    /* insert the trouble maker back*/
707    nel->vec[iafter] = col_buf;
708    nel->vec_typ[iafter] = typ_buf;
709    if( nel->vec_lab != NULL )
710      nel->vec_lab[iafter] = lab_buf ;
711 
712    /* house keeping */
713    /* if element has "ni_type" attribute, adjust it   14 Jul 2006 [rickr] */
714    if( NI_get_attribute(nel, "ni_type") )
715       NI_set_ni_type_atr(nel) ;
716 
717    return ;
718 }
719 
720 /*-------------------------------------------------------------------------*/
721 /*!
722    Do we really need to document this too?        [[Cox says YES]]
723    Removes column irm from nel. If irm < 0 or
724    irm >= nel->vec_num irm = nel->vec_num -1
725 */
NI_remove_column(NI_element * nel,int irm)726 void NI_remove_column(NI_element *nel, int irm)
727 {
728    int nn;
729 
730    if (nel == NULL || nel->vec_len <= 0 )            return ;
731 
732    if (!(nn = nel->vec_num)) return;
733 
734    if (irm < 0 || irm >= nn) irm = nn-1;
735 
736    /* move irm to last column */
737    NI_move_column(nel, irm, -1);
738 
739    /* free the last column */
740    NI_free_column( NI_rowtype_find_code(nel->vec_typ[nn-1]) ,
741                            nel->vec_len , nel->vec[nn-1]             ) ;
742    nel->vec[nn-1] = NULL; /* to be sure */
743 
744    /* decrease the number of columns */
745    --nn;
746    nel->vec_num = nn;
747 
748    /* get rid of extra space */
749    nel->vec_typ = NI_realloc( nel->vec_typ, int, sizeof(int)*(nn) ) ;
750    nel->vec = NI_realloc( nel->vec , void*, sizeof(void *)*(nn) ) ;
751    if( nel->vec_lab != NULL )
752      nel->vec_lab = NI_realloc( nel->vec_lab , char*, sizeof(char *)*(nn) ) ;
753 
754    /* if element has "ni_type" attribute, adjust it   14 Jul 2006 [rickr] */
755    if( NI_get_attribute(nel, "ni_type") )
756       NI_set_ni_type_atr(nel) ;
757 
758    return;
759 }
760 
761 /*------------------------------------------------------------------------*/
762 /*! Change the length of all the columns in a data element.
763      - If the columns are longer, they will be zero filled.
764      - New values can be inserted later with NI_insert_value().
765      - If the columns are shorter, data will be lost.
766      - You can use this to convert an element from empty to non-empty
767        by entering newlen > 0 when the original vec_len is 0.
768      - But, you cannot use this to convert an element to empty from
769        non-empty, by entering newlen == 0 when vec_len > 0!
770 --------------------------------------------------------------------------*/
771 
NI_alter_veclen(NI_element * nel,int newlen)772 void NI_alter_veclen( NI_element *nel , int newlen )
773 {
774    int oldlen , ii ;
775    NI_rowtype *rt ;
776    char *pt ;
777 
778    if( nel    == NULL || nel->type != NI_ELEMENT_TYPE ) return ;
779    if( newlen <= 0                                    ) return ;
780 
781    if( nel->vec_num == 0 ){                       /* if have no data yet */
782      nel->vec_len = nel->vec_filled = newlen; return;
783    }
784 
785    if( nel->vec_len == 0 ) NI_init_veclen( nel , newlen ) ;  /* 19 Sep 2008 */
786 
787    oldlen = nel->vec_len ; if( oldlen == newlen ) return ;
788 
789    for( ii=0 ; ii < nel->vec_num ; ii++ ){
790      rt = NI_rowtype_find_code( nel->vec_typ[ii] ) ;
791      nel->vec[ii] = NI_realloc( nel->vec[ii] , void , rt->size * newlen ) ;
792      if( oldlen < newlen ){
793        pt = ((char *)nel->vec[ii]) + (rt->size * oldlen) ; /* zero fill */
794        memset( pt , 0 , (newlen-oldlen)*rt->size ) ;       /* new data! */
795      }
796    }
797 
798    nel->vec_len = nel->vec_filled = newlen ; return ;
799 }
800 
801 /*------------------------------------------------------------------------*/
802 /*! As in NI_add_column(), but adding every stride-th element from arr.
803     Thus, arr should be at least nel->vec_len * stride elements long.
804 --------------------------------------------------------------------------*/
805 
NI_add_column_stride(NI_element * nel,int typ,void * arr,int stride)806 void NI_add_column_stride( NI_element *nel, int typ, void *arr, int stride )
807 {
808    int nn , ii ;
809    NI_rowtype *rt ;
810    char *idat ;
811 
812    /* check for reasonable inputs */
813 
814    if( nel == NULL || nel->vec_len <= 0 )            return ;
815    if( nel->type != NI_ELEMENT_TYPE )                return ;
816    rt = NI_rowtype_find_code(typ) ; if( rt == NULL ) return ;
817 
818    /* add an empty column */
819 
820    NI_add_column( nel , typ , NULL ) ;
821    if( arr == NULL ) return ;          /* no input data ==> we're done */
822 
823    /* loop over inputs and put them in one at a time */
824 
825    nn   = nel->vec_num-1 ;
826    idat = (char *) arr ;
827 
828    for( ii=0 ; ii < nel->vec_len ; ii++ )
829      NI_insert_value( nel , ii , nn , idat + (ii*stride*rt->size) ) ;
830 
831    return ;
832 }
833 
834 /*-------------------------------------------------------------------------*/
835 /*!
836    See NI_insert_column for inspiration
837 */
NI_insert_column_stride(NI_element * nel,int typ,void * arr,int stride,int icol)838 void NI_insert_column_stride( NI_element *nel, int typ, void *arr, int stride, int icol )
839 {
840    int nn , ii ;
841    NI_rowtype *rt ;
842    char *idat ;
843 
844    /* check for reasonable inputs */
845 
846    if( nel == NULL || nel->vec_len <= 0 )            return ;
847    if( nel->type != NI_ELEMENT_TYPE )                return ;
848    rt = NI_rowtype_find_code(typ) ; if( rt == NULL ) return ;
849 
850    /* get number of vectors currently in element */
851    nn = nel->vec_num ;
852 
853    if (icol > nn || icol < 0) icol = nn;
854 
855    /* call add column_stride */
856    NI_add_column_stride(nel, typ, arr, stride);
857 
858    /* check on success */
859    if (nel->vec_num != nn+1) return ;  /* misere */
860    nn = nel->vec_num ;  /* the new number of vectors */
861 
862    NI_move_column(nel, nn-1, icol);
863 
864    return ;
865 }
866 
867 
868 /*------------------------------------------------------------------------*/
869 /*! ZSS; Fills an already created column with values up to vec_filled
870          the values in arr are inserted into nel->vec[nn]
871 --------------------------------------------------------------------------*/
872 
NI_fill_column_stride(NI_element * nel,int typ,void * arr,int nn,int stride)873 void NI_fill_column_stride( NI_element *nel, int typ,
874                             void *arr, int nn, int stride )
875 {
876    int  ii , nf;
877    NI_rowtype *rt ;
878    char *idat ;
879 
880    /* check for reasonable inputs */
881 
882    if( nel == NULL || nel->vec_len <= 0 )            return ;
883    if( nel->type != NI_ELEMENT_TYPE )                return ;
884    rt = NI_rowtype_find_code(typ) ; if( rt == NULL ) return ;
885 
886    /* check for NULL column or other similar errors*/
887 
888    if( arr == NULL )                                 return ;
889    if( nel->vec[nn] == NULL )                        return ;
890    if( nn < 0 || nn >= nel->vec_num )                return ;
891    if( typ != nel->vec_typ[nn] )                     return ;
892 
893    /* loop over inputs and put them in */
894 
895    if( nel->vec_filled > 0 && nel->vec_filled <= nel->vec_len )
896      nf = nel->vec_filled ;
897    else
898      nf = nel->vec_len ;
899 
900    idat = (char *) arr ;
901 
902    for( ii=0 ; ii < nf ; ii++ )
903      NI_insert_value( nel , ii , nn , idat + (ii*stride*rt->size) ) ;
904 
905    return ;
906 }
907 
908 /*------------------------------------------------------------------------*/
909 /*! Replace the row-th value in the col-th column of the data element.
910      - dat is the pointer to the data values to copy into the element.
911      - The column must have been created with NI_add_column() before
912        calling this function!
913      - NOTE WELL: When the column type is NI_STRING, it is a mistake
914        to call this function with dat being a pointer to the C string
915        to insert.  Instead, dat should be a pointer to the pointer to
916        the C string.  For example:
917         - char *me = "RWCox" ;
918         - WRONG:  NI_insert_value ( nel, 3,5,  me ) ; [Seg Fault ensues]
919         - RIGHT:  NI_insert_value ( nel, 3,5, &me ) ;
920         - RIGHT:  NI_insert_string( nel, 3,5,  me ) ;
921         - The last case illustrates the NI_insert_string() function,
922           which can be used to simplify insertion into a column
923           of Strings; that function is just a simple wrapper to call
924           NI_insert_value() properly.
925         - The reason the first example is WRONG is that dat is supposed
926           to point to the data to be stored.  In the case of a String,
927           the data is the pointer to the C string.
928 --------------------------------------------------------------------------*/
929 
NI_insert_value(NI_element * nel,int row,int col,void * dat)930 void NI_insert_value( NI_element *nel, int row, int col, void *dat )
931 {
932    NI_rowtype *rt ;
933    char *cdat , *idat=(char *)dat , *qpt ;
934    int jj , kk ;
935 
936    /* check for reasonable inputs */
937 
938    if( nel == NULL || idat == NULL        ) return ;
939    if( nel->type    != NI_ELEMENT_TYPE    ) return ;
940    if( nel->vec_len <= 0                  ) return ;
941    if( row < 0     || row >= nel->vec_len ) return ;
942    if( col < 0     || col >= nel->vec_num ) return ;
943 
944    rt = NI_rowtype_find_code( nel->vec_typ[col] ) ;
945    if( rt == NULL )                         return ;
946 
947    cdat = (char *) nel->vec[col] ;   /* points to column data */
948    cdat = cdat + rt->size * row ;    /* points to data to alter */
949 
950    /* shallow copy of input data over data now present */
951 
952    memcpy( cdat , idat , rt->size ) ;  /* cdat now contains input data */
953 
954    /* copy any var dim arrays inside */
955 
956    if( ROWTYPE_is_varsize(rt) ){
957      for( jj=0 ; jj < rt->part_num ; jj++ ){            /* loop over parts */
958 
959        if( rt->part_typ[jj] == NI_STRING ){                   /* a string part */
960          char **apt = (char **)(cdat+rt->part_off[jj]) ; /* *apt => input data */
961          qpt = NI_strdup(*apt) ;                 /* qpt = copy of input string */
962         *apt = qpt ;                     /* reset *apt to copy of input string */
963 
964        } else if( rt->part_dim[jj] >= 0 ){                /* var dim array */
965          char **apt = (char **)(cdat+rt->part_off[jj]) ;   /* *apt => data */
966          if( *apt != NULL ){
967            kk  = ROWTYPE_part_dimen(rt,cdat,jj) * rt->part_rtp[jj]->size ;
968            qpt = NI_malloc(char, kk) ; memcpy(qpt,*apt,kk) ; *apt = qpt ;
969          }
970        }
971      }
972    }
973 
974    return ;
975 }
976 
977 /*------------------------------------------------------------------------*/
978 
NI_insert_string(NI_element * nel,int row,int col,char * str)979 void NI_insert_string( NI_element *nel, int row, int col, char *str )
980 {
981    if( nel == NULL || str == NULL         ) return ;
982    if( nel->type   != NI_ELEMENT_TYPE     ) return ;
983    if( row < 0     || row >= nel->vec_len ) return ;
984    if( col < 0     || col >= nel->vec_num ) return ;
985    if( nel->vec_typ[col] != NI_STRING     ) return ;
986 
987    NI_insert_value( nel , row,col , &str ); return ;
988 }
989 
990 /*------------------------------------------------------------------------*/
991 /* Return a float value. If the data type does not pass NI_IS_NUMERIC_TYPE,
992    or if any other thing is non-copasetic, then return value is 0.0f.
993 *//*----------------------------------------------------------------------*/
994 
NI_extract_float_value(NI_element * nel,int row,int col)995 float NI_extract_float_value( NI_element *nel , int row , int col )
996 {
997    char *cdat ;
998    NI_rowtype *rt ;
999 
1000    /* check for stoopid stufff */
1001 
1002    if( nel         == NULL                     ) return 0.0f ;
1003    if( nel->type   != NI_ELEMENT_TYPE          ) return 0.0f ;
1004    if( row < 0     || row >= nel->vec_len      ) return 0.0f ;
1005    if( col < 0     || col >= nel->vec_num      ) return 0.0f ;
1006    if( ! NI_IS_NUMERIC_TYPE(nel->vec_typ[col]) ) return 0.0f ;
1007 
1008    rt = NI_rowtype_find_code( nel->vec_typ[col] ) ;
1009    if( rt == NULL )                             return 0.0f ;
1010 
1011    cdat = (char *) nel->vec[col] ;   /* points to column data */
1012    cdat = cdat + rt->size * row ;    /* points to data to get */
1013 
1014    switch( nel->vec_typ[col] ){
1015       default: return 0.0f ;         /* should not happen */
1016 
1017       case NI_FLOAT:{
1018         float *ar = (float *)cdat ; return ar[0] ;
1019       }
1020 
1021       case NI_BYTE:{
1022         byte *ar = (byte *)cdat ; return (float)ar[0] ;
1023       }
1024 
1025       case NI_SHORT:{
1026         short *ar = (short *)cdat ; return (float)ar[0] ;
1027       }
1028 
1029       case NI_INT:{
1030         int *ar = (int *)cdat ; return (float)ar[0] ;
1031       }
1032 
1033       case NI_DOUBLE:{
1034         double *ar = (double *)cdat ; return (float)ar[0] ;
1035       }
1036 
1037       case NI_COMPLEX:{
1038         complex *ar = (complex *)cdat ;
1039         double x = (double)ar[0].r , y = (double)ar[0].i ;
1040         return (float)sqrt(x*x+y*y) ;
1041       }
1042 
1043       case NI_RGB:{
1044         rgb *ar = (rgb *)cdat ;
1045         float rr = (float)ar[0].r, gg = (float)ar[0].g, bb = (float)ar[0].b ;
1046         return (0.299f*rr+0.587f*gg+0.114f*bb) ;
1047       }
1048 
1049       case NI_RGBA:{
1050         rgba *ar = (rgba *)cdat ;
1051         float rr = (float)ar[0].r, gg = (float)ar[0].g, bb = (float)ar[0].b ;
1052         float aa = (float)ar[0].a ;
1053         return (0.299f*rr+0.587f*gg+0.114f*bb)*(aa/255.0f) ;
1054       }
1055 
1056     }
1057 
1058     return 0.0f ; /* unreachable */
1059 }
1060 
1061 /*------------------------------------------------------------------------*/
1062 /* Return a malloc()-ed string value from one value in a data element.
1063    Adapted from NI_write_columns().
1064    Result should be free()-ed when you are done with it.
1065                                           [19 Jun 2020 - RWCox == NIMLman]
1066 *//*----------------------------------------------------------------------*/
1067 
NI_extract_text_value(NI_element * nel,int row,int col)1068 char * NI_extract_text_value( NI_element *nel , int row , int col )
1069 {
1070    int fsiz , col_typ , ii ;
1071    NI_rowtype *rt ;
1072    char *ptr , *col_dat , *outstr ;
1073 
1074    /* check for stoopid stufff */
1075 
1076    if( nel       == NULL                ) return NULL ;
1077    if( nel->type != NI_ELEMENT_TYPE     ) return NULL ;
1078    if( row < 0   || row >= nel->vec_len ) return NULL ;
1079    if( col < 0   || col >= nel->vec_num ) return NULL ;
1080 
1081    col_typ = nel->vec_typ[col] ;             /* type of data in column */
1082    rt      = NI_rowtype_find_code( col_typ ) ;
1083    if( rt == NULL                       ) return NULL ; /* impossible? */
1084 
1085    fsiz    = rt->size ;                 /* fixed size of struct (w/padding) */
1086    col_dat = (char *)nel->vec[col] ;    /* ptr to column data */
1087    ptr     = col_dat + fsiz*row ;       /* ptr to row-th struct in column */
1088 
1089    outstr = (char *)malloc(sizeof(char)*999*rt->part_num) ; outstr[0] = '\0' ;
1090 
1091    /* write each part of this struct into the buffer */
1092 
1093    for( ii=0 ; ii < rt->part_num ; ii++ ){  /*-- loop over parts --*/
1094 
1095      if( rt->part_dim[ii] < 0 ){             /*-- a single value --*/
1096        NI_val_to_text( rt->part_rtp[ii],
1097                        ptr+rt->part_off[ii], outstr ) ;
1098      } else {      /* a compound data type with variable size data */
1099        int dim ;
1100        char **apt = (char **)(ptr+rt->part_off[ii]); /* data in struct */
1101                                                      /* is ptr to array */
1102 
1103        dim = ROWTYPE_part_dimen(rt,ptr,ii) ;         /* dimension of part */
1104        if( dim > 0 && *apt != NULL ){
1105          NI_multival_to_text( rt->part_rtp[ii] , dim , *apt , outstr ) ;
1106        }
1107      }
1108    }
1109 
1110    /* pare down the output string size */
1111 
1112    ii     = strlen(outstr) ;
1113    outstr = realloc( outstr , ii+1 ) ;
1114    return outstr ;
1115 }
1116 
1117 /*------------------------------------------------------------------------*/
1118 /*! Remove an attribute, if it exists from a data or group element.
1119                                     ZSS Feb 09
1120 --------------------------------------------------------------------------*/
1121 
NI_kill_attribute(void * nini,char * attname)1122 void NI_kill_attribute( void *nini , char *attname  )
1123 {
1124    int nn , tt=NI_element_type(nini) ;
1125 
1126    if( tt < 0 || attname == NULL || attname[0] == '\0' ) return ;
1127 
1128    /* input is a data element */
1129 
1130    if( tt == NI_ELEMENT_TYPE ){
1131       NI_element *nel = (NI_element *) nini ;
1132 
1133       /* see if name is already in element header */
1134 
1135       for( nn=0 ; nn < nel->attr_num ; nn++ )
1136          if( strcmp(nel->attr_lhs[nn],attname) == 0 ) break ;
1137 
1138       if( nn == nel->attr_num ){ /* not found, return */
1139         return;
1140       } else {
1141         NI_free(nel->attr_lhs[nn]) ;  /* free old attribute */
1142         NI_free(nel->attr_rhs[nn]) ;
1143         if ( nn < nel->attr_num-1 ) { /* move last attr to nn */
1144          nel->attr_lhs[nn] = nel->attr_lhs[nel->attr_num-1];
1145          nel->attr_lhs[nel->attr_num-1] = NULL;
1146          nel->attr_rhs[nn] = nel->attr_rhs[nel->attr_num-1];
1147          nel->attr_rhs[nel->attr_num-1] = NULL;
1148         }
1149         --nel->attr_num;
1150         /* reallocate */
1151         nel->attr_lhs = NI_realloc( nel->attr_lhs,
1152                                     char*, sizeof(char *)*(nel->attr_num) );
1153         nel->attr_rhs = NI_realloc( nel->attr_rhs,
1154                                     char*, sizeof(char *)*(nel->attr_num) );
1155       }
1156 
1157    /* input is a group element */
1158 
1159    } else if( tt == NI_GROUP_TYPE ){
1160       NI_group *ngr = (NI_group *) nini ;
1161 
1162       for( nn=0 ; nn < ngr->attr_num ; nn++ )
1163          if( strcmp(ngr->attr_lhs[nn],attname) == 0 ) break ;
1164 
1165       if( nn == ngr->attr_num ){
1166         return;
1167       } else {
1168         NI_free(ngr->attr_lhs[nn]) ;
1169         NI_free(ngr->attr_rhs[nn]) ;
1170         if ( nn < ngr->attr_num-1 ) { /* move last attr to nn */
1171          ngr->attr_lhs[nn] = ngr->attr_lhs[ngr->attr_num-1];
1172          ngr->attr_lhs[ngr->attr_num-1] = NULL;
1173          ngr->attr_rhs[nn] = ngr->attr_rhs[ngr->attr_num-1];
1174          ngr->attr_rhs[ngr->attr_num-1] = NULL;
1175         }
1176         --ngr->attr_num;
1177         /* reallocate */
1178         ngr->attr_lhs = NI_realloc( ngr->attr_lhs,
1179                                     char*, sizeof(char *)*(ngr->attr_num) );
1180         ngr->attr_rhs = NI_realloc( ngr->attr_rhs,
1181                                     char*, sizeof(char *)*(ngr->attr_num) );
1182       }
1183 
1184    /* input is a processing instruction */
1185 
1186    } else if( tt == NI_PROCINS_TYPE ){
1187       NI_procins *npi = (NI_procins *) nini ;
1188 
1189       for( nn=0 ; nn < npi->attr_num ; nn++ )
1190         if( strcmp(npi->attr_lhs[nn],attname) == 0 ) break ;
1191 
1192       if( nn == npi->attr_num ){
1193         return;
1194       } else {
1195         NI_free(npi->attr_lhs[nn]) ;
1196         NI_free(npi->attr_rhs[nn]) ;
1197         if ( nn < npi->attr_num-1 ) { /* move last attr to nn */
1198          npi->attr_lhs[nn] = npi->attr_lhs[npi->attr_num-1];
1199          npi->attr_lhs[npi->attr_num-1] = NULL;
1200          npi->attr_rhs[nn] = npi->attr_rhs[npi->attr_num-1];
1201          npi->attr_rhs[npi->attr_num-1] = NULL;
1202         }
1203         --npi->attr_num;
1204         /* reallocate */
1205         npi->attr_lhs = NI_realloc( npi->attr_lhs,
1206                                     char*, sizeof(char *)*(npi->attr_num) );
1207         npi->attr_rhs = NI_realloc( npi->attr_rhs,
1208                                     char*, sizeof(char *)*(npi->attr_num) );
1209       }
1210 
1211    }
1212 
1213    return ;
1214 }
1215 
1216 /*------------------------------------------------------------------------*/
1217 /*! Add an attribute to a data or group element.
1218     If an attribute with the same attname already exists, then
1219     it will be replaced with this one.
1220 --------------------------------------------------------------------------*/
1221 
NI_set_attribute(void * nini,char * attname,char * attvalue)1222 void NI_set_attribute( void *nini , char *attname , char *attvalue )
1223 {
1224    int nn , tt=NI_element_type(nini) ;
1225 
1226    if( tt < 0 || attname == NULL || attname[0] == '\0' ) return ;
1227 
1228    /* input is a data element */
1229 
1230    if( tt == NI_ELEMENT_TYPE ){
1231       NI_element *nel = (NI_element *) nini ;
1232 
1233       /* see if name is already in element header */
1234 
1235       for( nn=0 ; nn < nel->attr_num ; nn++ )
1236          if( strcmp(nel->attr_lhs[nn],attname) == 0 ) break ;
1237 
1238       /* if not, then add a header attribute */
1239 
1240       if( nn == nel->attr_num ){
1241         nel->attr_lhs = NI_realloc( nel->attr_lhs, char*, sizeof(char *)*(nn+1) );
1242         nel->attr_rhs = NI_realloc( nel->attr_rhs, char*, sizeof(char *)*(nn+1) );
1243         nel->attr_num = nn+1 ;
1244       } else {
1245         NI_free(nel->attr_lhs[nn]) ;  /* free old attribute */
1246         NI_free(nel->attr_rhs[nn]) ;
1247       }
1248 
1249       nel->attr_lhs[nn] = NI_strdup(attname) ;
1250       nel->attr_rhs[nn] = NI_strdup(attvalue);
1251 
1252    /* input is a group element */
1253 
1254    } else if( tt == NI_GROUP_TYPE ){
1255       NI_group *ngr = (NI_group *) nini ;
1256 
1257       for( nn=0 ; nn < ngr->attr_num ; nn++ )
1258          if( strcmp(ngr->attr_lhs[nn],attname) == 0 ) break ;
1259 
1260       if( nn == ngr->attr_num ){
1261         ngr->attr_lhs = NI_realloc( ngr->attr_lhs, char*, sizeof(char *)*(nn+1) );
1262         ngr->attr_rhs = NI_realloc( ngr->attr_rhs, char*, sizeof(char *)*(nn+1) );
1263         ngr->attr_num = nn+1 ;
1264       } else {
1265         NI_free(ngr->attr_lhs[nn]) ;
1266         NI_free(ngr->attr_rhs[nn]) ;
1267       }
1268 
1269       ngr->attr_lhs[nn] = NI_strdup(attname) ;
1270       ngr->attr_rhs[nn] = NI_strdup(attvalue);
1271 
1272    /* input is a processing instruction */
1273 
1274    } else if( tt == NI_PROCINS_TYPE ){
1275       NI_procins *npi = (NI_procins *) nini ;
1276 
1277       for( nn=0 ; nn < npi->attr_num ; nn++ )
1278         if( strcmp(npi->attr_lhs[nn],attname) == 0 ) break ;
1279 
1280       if( nn == npi->attr_num ){
1281         npi->attr_lhs = NI_realloc( npi->attr_lhs, char*, sizeof(char *)*(nn+1) );
1282         npi->attr_rhs = NI_realloc( npi->attr_rhs, char*, sizeof(char *)*(nn+1) );
1283         npi->attr_num = nn+1 ;
1284       } else {
1285         NI_free(npi->attr_lhs[nn]) ;
1286         NI_free(npi->attr_rhs[nn]) ;
1287       }
1288 
1289       npi->attr_lhs[nn] = NI_strdup(attname) ;
1290       npi->attr_rhs[nn] = NI_strdup(attvalue);
1291    }
1292 
1293    return ;
1294 }
1295 
1296 /*------------------------------------------------------------------------*/
1297 /*! Copy all attributes from one element to the next.
1298     The function is not recursive and must have a target nel
1299     with no attributes.    ZSS Oct 2010
1300 --------------------------------------------------------------------------*/
1301 
NI_copy_all_attributes(void * nisrc,void * nitrg)1302 void NI_copy_all_attributes( void *nisrc , void *nitrg )
1303 {
1304    int nn , ttsrc=NI_element_type(nisrc), tttrg=NI_element_type(nitrg) ;
1305 
1306    if( ttsrc < 0 || tttrg < 0 ) return ;
1307    if( tttrg != ttsrc) {
1308       fprintf(stderr,"Error NI_copy_all_attributes:\n"
1309                      "Src and trg elements must have same type.\n");
1310       return;
1311    }
1312    /* input is a data element */
1313    if( ttsrc == NI_ELEMENT_TYPE ){
1314       NI_element *nelsrc = (NI_element *) nisrc ;
1315       NI_element *neltrg = (NI_element *) nitrg ;
1316       if( neltrg->attr_num != 0) {
1317          /* don't allow for this now. Else, you need to clear
1318             all pre-existing ones, or just common pre-existing ones
1319             then continue below */
1320          fprintf(stderr,"Error NI_copy_all_attributes:\n"
1321                         "Must have no attributes in target element.\n");
1322          return;
1323       }
1324 
1325       neltrg->attr_lhs = NI_realloc ( neltrg->attr_lhs,
1326                                      char*, sizeof(char *)*nelsrc->attr_num);
1327       neltrg->attr_rhs = NI_realloc ( neltrg->attr_rhs,
1328                                      char*, sizeof(char *)*nelsrc->attr_num);
1329       neltrg->attr_num = nelsrc->attr_num;
1330 
1331       for( nn=0 ; nn < nelsrc->attr_num ; nn++ ) {
1332          neltrg->attr_lhs[nn] = NI_strdup(nelsrc->attr_lhs[nn]);
1333          neltrg->attr_rhs[nn] = NI_strdup(nelsrc->attr_rhs[nn]);
1334       }
1335 
1336    /* input is a group element */
1337 
1338    } else if( ttsrc == NI_GROUP_TYPE ){
1339       NI_group *ngrsrc = (NI_group *) nisrc ;
1340       NI_group *ngrtrg = (NI_group *) nitrg ;
1341       if( ngrtrg->attr_num != 0) {
1342          /* don't allow for this now. Else, you need to clear
1343             all pre-existing ones, or just common pre-existing ones
1344             then continue below */
1345          fprintf(stderr,"Error NI_copy_all_attributes:\n"
1346                         "Must have no attributes in target element.\n");
1347          return;
1348       }
1349 
1350       ngrtrg->attr_lhs = NI_realloc ( ngrtrg->attr_lhs,
1351                                      char*, sizeof(char *)*ngrsrc->attr_num);
1352       ngrtrg->attr_rhs = NI_realloc ( ngrtrg->attr_rhs,
1353                                      char*, sizeof(char *)*ngrsrc->attr_num);
1354       ngrtrg->attr_num = ngrsrc->attr_num;
1355 
1356       for( nn=0 ; nn < ngrsrc->attr_num ; nn++ ) {
1357          ngrtrg->attr_lhs[nn] = NI_strdup(ngrsrc->attr_lhs[nn]);
1358          ngrtrg->attr_rhs[nn] = NI_strdup(ngrsrc->attr_rhs[nn]);
1359       }
1360 
1361    /* input is a processing instruction */
1362 
1363    } else if( ttsrc == NI_PROCINS_TYPE ){
1364       NI_procins *npisrc = (NI_procins *) nisrc ;
1365       NI_procins *npitrg = (NI_procins *) nitrg ;
1366       if( npitrg->attr_num != 0) {
1367          /* don't allow for this now. Else, you need to clear
1368             all pre-existing ones, or just common pre-existing ones
1369             then continue below */
1370          fprintf(stderr,"Error NI_copy_all_attributes:\n"
1371                         "Must have no attributes in target element.\n");
1372          return;
1373       }
1374 
1375       npitrg->attr_lhs = NI_realloc ( npitrg->attr_lhs,
1376                                      char*, sizeof(char *)*npisrc->attr_num);
1377       npitrg->attr_rhs = NI_realloc ( npitrg->attr_rhs,
1378                                      char*, sizeof(char *)*npisrc->attr_num);
1379       npitrg->attr_num = npisrc->attr_num;
1380 
1381       for( nn=0 ; nn < npisrc->attr_num ; nn++ ) {
1382          npitrg->attr_lhs[nn] = NI_strdup(npisrc->attr_lhs[nn]);
1383          npitrg->attr_rhs[nn] = NI_strdup(npisrc->attr_rhs[nn]);
1384       }
1385    }
1386 
1387    return ;
1388 }
1389 
1390 /*------------------------------------------------------------------------*/
1391 /*! Duplicate a niml group or element.
1392    Function tested with test program niccc and -dup option  ZSS Oct 2010
1393 --------------------------------------------------------------------------*/
1394 
NI_duplicate(void * vel,byte with_data)1395 void *NI_duplicate(void *vel, byte with_data)
1396 {
1397    void *vdup=NULL;
1398    int tt=-1;
1399 
1400    if (!vel) return(vdup);
1401    tt = NI_element_type(vel);
1402    if (tt == NI_ELEMENT_TYPE) {
1403       return(NI_duplicate_element(vel, with_data));
1404    } else if (tt == NI_GROUP_TYPE){
1405       return(NI_duplicate_group(vel, with_data));
1406    } else {
1407       fprintf(stderr,
1408          "Error NI_duplicate:\n"
1409          "Can only deal with elements on group types\n");
1410       return(NULL);
1411    }
1412    return(NULL);
1413 }
1414 
1415 /*------------------------------------------------------------------------*/
1416 /*! Duplicate niml element only.
1417     Better use function NI_duplicate                     ZSS Oct 2010
1418 --------------------------------------------------------------------------*/
1419 
NI_duplicate_element(void * vel,byte with_data)1420 void *NI_duplicate_element (void *vel, byte with_data)
1421 {
1422    NI_element *ndup = NULL;
1423    NI_element *nel = (NI_element *)vel;
1424    NI_group *gel = (NI_group *)vel;
1425    NI_group *gdup = NULL;
1426    int tt=-1, i=0;
1427    void *vdup=NULL;
1428 
1429    if (!vel) return(vdup);
1430 
1431    tt = NI_element_type(vel);
1432    if (tt != NI_ELEMENT_TYPE) {
1433       fprintf(stderr,
1434          "Error NI_duplicate_element:\n"
1435          "Can only deal with elements\n");
1436       return(vdup);
1437    }
1438 
1439    ndup = NI_new_data_element(nel->name, nel->vec_len);
1440 
1441    /* copy the attributes */
1442    NI_copy_all_attributes(nel, ndup);
1443 
1444    if (with_data) {
1445       /* copy the columns */
1446       for (i=0; i<nel->vec_num; ++i) {
1447          NI_add_column(ndup, nel->vec_typ[i],(void *)nel->vec[i]);
1448       }
1449    }
1450 
1451    if( nel->vec_lab != NULL && ndup->vec_num > 0 ){  /* 12 Sep 2018 */
1452      NI_set_attribute_from_veclab_array( ndup , nel->vec_lab ) ;
1453    }
1454 
1455    if (tt == NI_ELEMENT_TYPE) return((void *)ndup);
1456    else return(vdup);
1457 }
1458 
1459 /*------------------------------------------------------------------------*/
1460 /* Make a new element from a list of columns in the old element */
1461 
NI_extract_columns(NI_element * nel,int nc,int * cc)1462 NI_element * NI_extract_columns( NI_element *nel , int nc , int *cc )
1463 {
1464    NI_element *vnel ; int ii , jj ;
1465 
1466    if( NI_element_type(nel) != NI_ELEMENT_TYPE ) return(NULL) ;
1467    if( nc < 1 || cc == NULL                    ) return(NULL) ;
1468 
1469    vnel = NI_new_data_element(nel->name, nel->vec_len);
1470 
1471    NI_copy_all_attributes( nel, vnel ) ;
1472 
1473    /* copy desired columns */
1474 
1475    for( ii=0 ; ii < nc ; ii++ ){
1476      jj = cc[ii] ; if( jj < 0 || jj >= nel->vec_num ) continue ;
1477      NI_add_column( vnel , nel->vec_typ[jj] , (void *)nel->vec[jj] ) ;
1478      if( nel->vec_lab != NULL )
1479        NI_set_column_label( vnel , ii , nel->vec_lab[jj] ) ;
1480    }
1481 
1482    return(vnel) ;
1483 }
1484 
1485 /*------------------------------------------------------------------------*/
1486 /*! Duplicate niml group only.
1487     Better use function NI_duplicate                        ZSS Oct 2010
1488 --------------------------------------------------------------------------*/
1489 
NI_duplicate_group(void * vel,byte with_data)1490 void *NI_duplicate_group (void *vel, byte with_data)
1491 {
1492    NI_group *gel = (NI_group *)vel;
1493    NI_group *gdup = NULL;
1494    void *vv=NULL;
1495    int tt=-1, i=0;
1496    void *vdup=NULL, *gg=NULL;
1497 
1498    if (!vel) return(vdup);
1499 
1500    tt = NI_element_type(vel);
1501    if (tt != NI_GROUP_TYPE) {
1502       fprintf(stderr,
1503          "Error NI_duplicate_group:\n"
1504          "Can only deal with groups in this function\n");
1505       return(vdup);
1506    }
1507 
1508    gdup = NI_new_group_element();
1509    NI_rename_group(gdup, gel->name);
1510 
1511    /* copy the attributes to gdup*/
1512    NI_copy_all_attributes(gel, gdup);
1513 
1514    /* copy all the elements */
1515    for (i=0; i<gel->part_num; ++i) {
1516       switch( gel->part_typ[i] ){
1517          /*-- a sub-group ==> recursion! --*/
1518          case NI_GROUP_TYPE:
1519             if (!(gg = NI_duplicate_group(gel->part[i], with_data))) {
1520                fprintf(stderr,
1521                   "Error NI_duplicate_group:\n"
1522                   "Failed at recursion\n");
1523                return(NULL);
1524             }
1525             NI_add_to_group(gdup, gg);
1526             break ;
1527          case NI_ELEMENT_TYPE:
1528             if (!(gg = NI_duplicate_element(gel->part[i],with_data))) {
1529                fprintf(stderr,
1530                   "Error NI_duplicate_element:\n"
1531                   "Failed at recursion\n");
1532                return(NULL);
1533             }
1534             NI_add_to_group(gdup, gg);
1535             break;
1536          default:
1537             fprintf(stderr,
1538                   "Error NI_duplicate_group:\n"
1539                   "No duplication implemented for type %d, ignoring.\n",
1540                   gel->part_typ[i]);
1541             break;
1542       }
1543    }
1544    return(gdup);
1545 }
1546 
1547 /*-----------------------------------------------------------------------*/
1548 /*! Get an attribute with the given LHS name.  Returns a pointer to the
1549     RHS field in the element if the attribute name is found; otherwise
1550     returns NULL.  If the LHS is found, but the RHS is NULL, returns
1551     a pointer to an empty C string ("\0").  Do not free() the result
1552     from this function, since it points to the internal field
1553     of the element!
1554 -------------------------------------------------------------------------*/
1555 
NI_get_attribute_generic(void * nini,char * attname,int (* STRcomp)(const char *,const char *))1556 char * NI_get_attribute_generic( void *nini , char *attname ,
1557                                  int (*STRcomp)(const char *, const char *) )
1558 {
1559    int nn , tt=NI_element_type(nini) ;
1560    static char *zorkon = "\0" ;
1561 
1562    if( tt < 0 || attname == NULL || attname[0] == '\0' ) return NULL ;
1563 
1564    /* input is a data element */
1565 
1566    if( tt == NI_ELEMENT_TYPE ){
1567       NI_element *nel = (NI_element *) nini ;
1568 
1569       for( nn=0 ; nn < nel->attr_num ; nn++ )
1570          if( STRcomp(nel->attr_lhs[nn],attname) == 0 ) break ;
1571 
1572       if( nn == nel->attr_num ) return NULL ;
1573 
1574       if( nel->attr_rhs[nn] == NULL ) return zorkon ;
1575 
1576       return nel->attr_rhs[nn] ;
1577 
1578    /* input is a group element */
1579 
1580    } else if( tt == NI_GROUP_TYPE ){
1581       NI_group *ngr = (NI_group *) nini ;
1582 
1583       for( nn=0 ; nn < ngr->attr_num ; nn++ )
1584          if( STRcomp(ngr->attr_lhs[nn],attname) == 0 ) break ;
1585 
1586       if( nn == ngr->attr_num ) return NULL ;
1587 
1588       if( ngr->attr_rhs[nn] == NULL ) return zorkon ;
1589 
1590       return ngr->attr_rhs[nn] ;
1591 
1592    /* input is a processing instruction */
1593 
1594    } else if( tt == NI_PROCINS_TYPE ){
1595       NI_procins *npi = (NI_procins *) nini ;
1596 
1597       for( nn=0 ; nn < npi->attr_num ; nn++ )
1598         if( STRcomp(npi->attr_lhs[nn],attname) == 0 ) break ;
1599 
1600       if( nn == npi->attr_num ) return NULL ;
1601 
1602       if( npi->attr_rhs[nn] == NULL ) return zorkon ;
1603 
1604       return npi->attr_rhs[nn] ;
1605    }
1606 
1607    return NULL ; /* should never be reached */
1608 }
1609 
1610 /*-----------------------------------------------------------------------*/
1611 /* This stuff added 20 Aug 2019 [RWC] */
1612 
NI_get_attribute(void * nini,char * attname)1613 char * NI_get_attribute( void *nini , char *attname )
1614 {
1615    return NI_get_attribute_generic( nini , attname, strcmp ) ;
1616 }
1617 
NI_get_attribute_nocase(void * nini,char * attname)1618 char * NI_get_attribute_nocase( void *nini , char *attname )
1619 {
1620    return NI_get_attribute_generic( nini , attname, strcasecmp ) ;
1621 }
1622 
1623 /*-----------------------------------------------------------------------*/
1624 /*! Set the dimen attribute for a data element.
1625 -------------------------------------------------------------------------*/
1626 
NI_set_dimen(NI_element * nel,int rank,int * nd)1627 void NI_set_dimen( NI_element *nel , int rank , int *nd )
1628 {
1629    int ii , ntot ;
1630 
1631    if( nel == NULL || nel->type != NI_ELEMENT_TYPE ||
1632        rank < 1    || nd == NULL                     ) return ; /* bad */
1633 
1634    for( ntot=1,ii=0 ; ii < rank ; ii++ ){
1635       if( nd[ii] <= 0 ) return ;                                /* bad */
1636       ntot *= nd[ii] ;
1637    }
1638    if( ntot != nel->vec_len ) return ;                          /* bad */
1639 
1640    nel->vec_rank = rank ;
1641    nel->vec_axis_len = NI_realloc( nel->vec_axis_len, int, sizeof(int)*rank ) ;
1642    memcpy( nel->vec_axis_len , nd , sizeof(int)*rank ) ;
1643    return ;
1644 }
1645 
1646 /*-----------------------------------------------------------------------*/
1647 /*! Set the delta attribute for a data element.
1648     Do not call this function until NI_set_dimen() has been called,
1649     unless there is only 1 dimension (which is the default).
1650 -------------------------------------------------------------------------*/
1651 
NI_set_delta(NI_element * nel,float * del)1652 void NI_set_delta( NI_element *nel , float *del )
1653 {
1654    if( nel == NULL       || nel->type != NI_ELEMENT_TYPE ||
1655        nel->vec_rank < 1 || del == NULL                    ) return ;
1656 
1657    nel->vec_axis_delta = NI_realloc( nel->vec_axis_delta , float,
1658                                      nel->vec_rank * sizeof(float) ) ;
1659    memcpy( nel->vec_axis_delta , del , nel->vec_rank * sizeof(float) ) ;
1660    return ;
1661 }
1662 
1663 /*-----------------------------------------------------------------------*/
1664 /*! Set the origin attribute for a data element.
1665     Do not call this function until NI_set_dimen() has been called,
1666     unless there is only 1 dimension (which is the default).
1667 -------------------------------------------------------------------------*/
1668 
NI_set_origin(NI_element * nel,float * org)1669 void NI_set_origin( NI_element *nel , float *org )
1670 {
1671    if( nel == NULL       || nel->type != NI_ELEMENT_TYPE ||
1672        nel->vec_rank < 1 || org == NULL                    ) return ;
1673 
1674    nel->vec_axis_origin = NI_realloc( nel->vec_axis_origin , float,
1675                                       nel->vec_rank * sizeof(float) ) ;
1676    memcpy( nel->vec_axis_origin , org , nel->vec_rank * sizeof(float) ) ;
1677    return ;
1678 }
1679 
1680 /*-----------------------------------------------------------------------*/
1681 /*! Set the units attribute for a data element.
1682     Do not call this function until NI_set_dimen() has been called,
1683     unless there is only 1 dimension (which is the default).
1684 -------------------------------------------------------------------------*/
1685 
NI_set_units(NI_element * nel,char ** units)1686 void NI_set_units( NI_element *nel , char **units )
1687 {
1688    int ii ;
1689 
1690    if( nel == NULL       || nel->type != NI_ELEMENT_TYPE ||
1691        nel->vec_rank < 1 || units == NULL                  ) return ;
1692 
1693    nel->vec_axis_unit = NI_realloc( nel->vec_axis_unit , char*,
1694                                     nel->vec_rank * sizeof(char *) ) ;
1695    for( ii=0 ; ii < nel->vec_rank ; ii++ )
1696       nel->vec_axis_unit[ii] = NI_strdup( units[ii] ) ;
1697    return ;
1698 }
1699 
1700 /*-----------------------------------------------------------------------*/
1701 /*! Set the axes attribute for a data element.
1702     Do not call this function until NI_set_dimen() has been called,
1703     unless there is only 1 dimension (which is the default).
1704 -------------------------------------------------------------------------*/
1705 
NI_set_axes(NI_element * nel,char ** ax)1706 void NI_set_axes( NI_element *nel , char **ax )
1707 {
1708    int ii ;
1709 
1710    if( nel == NULL       || nel->type != NI_ELEMENT_TYPE ||
1711        nel->vec_rank < 1 || ax == NULL                     ) return ;
1712 
1713    nel->vec_axis_label = NI_realloc( nel->vec_axis_label , char*,
1714                                      nel->vec_rank * sizeof(char *) ) ;
1715    for( ii=0 ; ii < nel->vec_rank ; ii++ )
1716       nel->vec_axis_label[ii] = NI_strdup( ax[ii] ) ;
1717    return ;
1718 }
1719 
1720 /*-----------------------------------------------------------------------*/
1721 /*! Create a new processing instruction with a given 'target' name.
1722 -------------------------------------------------------------------------*/
1723 
NI_new_processing_instruction(char * name)1724 NI_procins * NI_new_processing_instruction( char *name )
1725 {
1726    NI_procins *npi ;
1727 
1728    if( name == NULL || name[0] == '\0' ) return NULL ;
1729 
1730    npi = NI_malloc(NI_procins,sizeof(NI_procins)) ;
1731 
1732    npi->type = NI_PROCINS_TYPE ;
1733    npi->name = NI_strdup(name) ;
1734 
1735    npi->attr_num = 0 ;
1736    npi->attr_lhs = npi->attr_rhs = NULL ;
1737 
1738    return npi ;
1739 }
1740 
1741 /*-----------------------------------------------------------------------*/
1742 /*! Create a new group element.
1743 -------------------------------------------------------------------------*/
1744 
NI_new_group_element(void)1745 NI_group * NI_new_group_element(void)
1746 {
1747    NI_group *ngr ;
1748 
1749    ngr = NI_malloc(NI_group, sizeof(NI_group) ) ;
1750 
1751    ngr->type = NI_GROUP_TYPE ;
1752 
1753    ngr->outmode = -1 ;   /* 29 Mar 2005 */
1754 
1755    ngr->attr_num = 0 ;
1756    ngr->attr_lhs = ngr->attr_rhs = NULL ;
1757 
1758    ngr->part_num = 0 ;
1759    ngr->part_typ = NULL ;
1760    ngr->part     = NULL ;
1761    ngr->name     = NULL ;  /* 03 Jun 2002 */
1762    ngr->filename = NULL ;  /* 16 Jun 2020 */
1763 
1764    return ngr ;
1765 }
1766 
1767 /*-----------------------------------------------------------------------*/
1768 /*! Add an element to a group element.
1769 -------------------------------------------------------------------------*/
1770 
NI_add_to_group(NI_group * ngr,void * nini)1771 void NI_add_to_group( NI_group *ngr , void *nini )
1772 {
1773    int nn , tt=NI_element_type(nini) ;
1774 
1775    if( ngr == NULL || ngr->type != NI_GROUP_TYPE || tt < 0 ) return ;
1776 
1777    nn = ngr->part_num ;
1778 
1779    ngr->part_typ     = NI_realloc( ngr->part_typ , int, sizeof(int)*(nn+1) ) ;
1780    ngr->part_typ[nn] = tt ;
1781    ngr->part         = NI_realloc( ngr->part , void*, sizeof(void *)*(nn+1) );
1782    ngr->part[nn]     = nini ;
1783    ngr->part_num     = nn+1 ;
1784    return ;
1785 }
1786 
1787 /*-----------------------------------------------------------------------*/
1788 /*! Remove an element from a group.  Does NOT delete the element;
1789     that is the caller's responsibility, if desired.
1790 -------------------------------------------------------------------------*/
1791 
NI_remove_from_group(NI_group * ngr,void * nini)1792 void NI_remove_from_group( NI_group *ngr , void *nini )  /* 16 Apr 2005 */
1793 {
1794    int ii , nn , jj ;
1795 
1796    if( ngr == NULL || ngr->type != NI_GROUP_TYPE || nini == NULL ) return ;
1797 
1798    nn = ngr->part_num ;
1799    for( ii=0 ; ii < nn ; ii++ )       /* search for part */
1800      if( nini == ngr->part[ii] ) break ;
1801    if( ii == nn ) return ;            /* not found */
1802 
1803    for( jj=ii+1 ; jj < nn ; jj++ ){   /* move parts above down */
1804      ngr->part_typ[jj-1] = ngr->part_typ[jj] ;
1805      ngr->part    [jj-1] = ngr->part    [jj] ;
1806    }
1807    ngr->part[nn-1] = NULL ;    /* NULL-ify last part to be safe */
1808    ngr->part_num -- ;          /* reduce part count */
1809    return ;
1810 }
1811 
1812 /*-----------------------------------------------------------------------*/
1813 /*! Rename a group element from the default - 03 Jun 2002.
1814 -------------------------------------------------------------------------*/
1815 
NI_rename_group(NI_group * ngr,char * nam)1816 void NI_rename_group( NI_group *ngr , char *nam )
1817 {
1818    if( ngr == NULL || ngr->type != NI_GROUP_TYPE ) return ;
1819    NI_free( ngr->name ) ;
1820    ngr->name = NI_strdup(nam) ;
1821    return ;
1822 }
1823 
1824 /*-----------------------------------------------------------------------*/
1825 /*! Return a list of all elements in a group that have a given name.
1826       - This is a 'shallow' search: if the group itself contains
1827         groups, these sub-groups are not searched.
1828       - Return value of function is number of elements found (might be 0).
1829       - If something is found, then *nipt is an array of 'void *', each
1830         of which points to a matching element.
1831       - The returned elements might be group or data elements.
1832       - Sample usage:
1833           - int n,i ; void **nelar ;
1834           - n = NI_search_group_shallow( ngr , "fred" , &nelar ) ;
1835           - for( i=0 ; i < n ; i++ ) do_something( nelar[ii] ) ;
1836           - if( n > 0 ) NI_free(nelar) ;
1837 -------------------------------------------------------------------------*/
1838 
NI_search_group_shallow(NI_group * ngr,char * enam,void *** nipt)1839 int NI_search_group_shallow( NI_group *ngr , char *enam , void ***nipt )
1840 {
1841    void **nelar=NULL , *nini ;
1842    int ii , nn=0 ;
1843    char *nm ;
1844 
1845    if( ngr  == NULL || ngr->type != NI_GROUP_TYPE    ) return 0 ;
1846    if( enam == NULL || *enam == '\0' || nipt == NULL ) return 0 ;
1847    if( ngr->part_num == 0                            ) return 0 ;
1848 
1849    for( ii=0 ; ii < ngr->part_num ; ii++ ){
1850      nini = ngr->part[ii] ;
1851      nm   = NI_element_name( nini ) ;
1852      if( nm != NULL && strcmp(nm,enam) == 0 ){
1853                                   /* added *size  11 Jul 2006 [rickr] */
1854        nelar = (void **) NI_realloc(nelar,void*,(nn+1)*sizeof(void *)) ;
1855        nelar[nn++] = nini ;
1856      }
1857    }
1858 
1859    if( nn > 0 ) *nipt = nelar ;
1860    return nn ;
1861 }
1862 
1863 /*-----------------------------------------------------------------------*/
1864 /*! Return a list of all elements in a group that have a given name.
1865       - This is a 'deep' search: if the group itself contains
1866         groups, these sub-groups are searched, etc.
1867       - If a group element has the name 'enam' AND a data element within
1868         that group has the name 'enam' as well, they will BOTH be returned
1869         in this list.
1870       - Return value of function is number of elements found (might be 0).
1871       - If something is found, then *nipt is an array of 'void *', each
1872         of which points to a matching element.
1873       - The returned elements might be group or data elements.
1874       - Sample usage:
1875           - int n,i ; void **nelar ;
1876           - n = NI_search_group_shallow( ngr , "fred" , &nelar ) ;
1877           - for( i=0 ; i < n ; i++ ) do_something( nelar[ii] ) ;
1878           - if( n > 0 ) NI_free(nelar) ;
1879 -------------------------------------------------------------------------*/
1880 
NI_search_group_deep(NI_group * ngr,char * enam,void *** nipt)1881 int NI_search_group_deep( NI_group *ngr , char *enam , void ***nipt )
1882 {
1883    void **nelar=NULL , *nini ;
1884    int ii , nn=0 ;
1885    char *nm ;
1886 
1887    if( ngr  == NULL || ngr->type != NI_GROUP_TYPE    ) return 0 ;
1888    if( enam == NULL || *enam == '\0' || nipt == NULL ) return 0 ;
1889    if( ngr->part_num == 0                            ) return 0 ;
1890 
1891    for( ii=0 ; ii < ngr->part_num ; ii++ ){
1892      nini = ngr->part[ii] ;
1893      nm   = NI_element_name( nini ) ;
1894      if( nm != NULL && strcmp(nm,enam) == 0 ){
1895        nelar = (void **) NI_realloc(nelar,void*,(nn+1)*sizeof(void *)) ;
1896        nelar[nn++] = nini ;
1897      }
1898      if( NI_element_type(nini) == NI_GROUP_TYPE ){  /* recursion */
1899        int nsub , jj ; void **esub ;
1900        nsub = NI_search_group_deep( nini , enam , &esub ) ;
1901        if( nsub > 0 ){
1902          nelar = (void **) NI_realloc(nelar,void*,(nn+nsub)*sizeof(void *)) ;
1903          for( jj=0 ; jj < nsub ; jj++ ) nelar[nn++] = esub[jj] ;
1904          NI_free(esub) ;
1905        }
1906      }
1907    }
1908 
1909    if( nn > 0 ) *nipt = nelar ;
1910    return nn ;
1911 }
1912 
1913 /*-----------------------------------------------------------------------*/
1914 /*! add a ni_type attribute to the element            14 Jul 2006 [rickr]
1915     (based on NI_write_element())
1916 -------------------------------------------------------------------------*/
NI_set_ni_type_atr(NI_element * nel)1917 void NI_set_ni_type_atr( NI_element * nel )
1918 {
1919    char * buf ;  /* alloc in blocks of ~1K (bob noted names up to 255 chars) */
1920    char * posn ;
1921    int    ii, prev, count=0 ;
1922    int    req_len, total_len=1024 ;
1923 
1924    if( ! nel || nel->vec_num <= 0 ) return ;
1925 
1926    /* make enough space for a list of buffers, and init. to empty */
1927    /* -- NI_rowtype_define uses 255 as a max rowtype name length  */
1928    buf = (char *)NI_malloc(char, total_len * sizeof(char)) ;
1929    buf[0] = '\0' ;
1930 
1931    for( prev=-1, ii=0; ii < nel->vec_num; ii++ ){
1932       if( nel->vec_typ[ii] != prev ){   /* not the previous type */
1933          if( prev >= 0 ){               /* apply previous type now */
1934             posn = buf + strlen(buf);
1935             if( count > 1 ) sprintf(posn, "%d*%s,", count, NI_type_name(prev));
1936             else            sprintf(posn, "%s,",           NI_type_name(prev));
1937          }
1938          prev = nel->vec_typ[ii] ;   /* save new type code */
1939          count = 1 ;                 /* have 1 such type   */
1940 
1941          /* make sure there is enough space for the new code        */
1942          /* (old, new, and space for "5280*")   17 Jul 2006 [rickr] */
1943          req_len = strlen(buf) + strlen(NI_type_name(prev)) + 10 ;
1944          if( total_len < req_len )
1945             buf = (char *)NI_realloc(buf, char, (req_len+1024) * sizeof(char)) ;
1946 
1947       } else {                       /* same as previous type  */
1948          count++ ;                   /* so increment its count */
1949       }
1950    }
1951 
1952    /* now write the last type found */
1953    posn = buf + strlen(buf) ;
1954    if( count > 1 ) sprintf(posn, "%d*%s", count, NI_type_name(prev));
1955    else            sprintf(posn, "%s",           NI_type_name(prev));
1956 
1957    /* now add the string as an attribute, and nuke the old string */
1958    NI_set_attribute(nel, "ni_type", buf) ;
1959 
1960    NI_free(buf) ;
1961 
1962    return ;
1963 }
1964 
1965 /*-----------------------------------------------------------------------*/
1966 /* Return a preview string for the top of a data element [22 Jun 2020].
1967    This string should be free()-ed after you are done with it.
1968 *//*---------------------------------------------------------------------*/
1969 
NI_preview_string(NI_element * nel,int ntop,char * title)1970 char * NI_preview_string( NI_element *nel , int ntop , char *title )
1971 {
1972    int *cwid , csum , ii , jj , qq , nview , ncol ;
1973    char *qpt,*zpt , *vstring ;
1974 
1975    if( NI_element_type(nel) != NI_ELEMENT_TYPE ) return NULL ;
1976 
1977    ncol = nel->vec_num ; if( ncol <= 0 ) return NULL ;
1978    cwid = (int *)malloc(sizeof(int)*ncol) ; /* column width */
1979    if( ntop < 1 ) ntop = 1 ;
1980    nview = (ntop < nel->vec_len) ? ntop : nel->vec_len ;
1981 
1982    /* scan for max width of each column */
1983 
1984    NI_set_raw_val_to_text(1) ;
1985 
1986    for( jj=0 ; jj < ncol ; jj++ ){         /* col #jj */
1987      cwid[jj] = 4 ;
1988      if( nel->vec_lab != NULL ){           /* col label */
1989        qq = NI_strlen(nel->vec_lab[jj]) ;
1990        if( qq > cwid[jj] ) cwid[jj] = qq ;
1991      }
1992      qpt = NI_rowtype_code_to_name( nel->vec_typ[jj] ) ; /* type name */
1993      qq  = NI_strlen(qpt) ;                       /* do NOT free qpt! */
1994      if( qq > cwid[jj] ) cwid[jj] = qq ;
1995      for( ii=0 ; ii < nview ; ii++ ){  /* col values down the rows */
1996        qpt = NI_extract_text_value( nel , ii , jj ) ;
1997        qq  = NI_strlen(qpt) ; free(qpt) ;
1998        if( qq > cwid[jj] ) cwid[jj] = qq ;
1999      }
2000    }
2001 
2002    /* sum up all widths, create output string */
2003 
2004    for( csum=jj=0 ; jj < ncol ; jj++ ) csum += cwid[jj]+2 ;
2005    if( title == NULL ) title = nel->filename ;
2006    jj = NI_strlen( title ) ;
2007    if( csum < jj+2 ) csum = jj+2 ;
2008 
2009    vstring = (char *)calloc( sizeof(char), (nview+5)*(csum+1)+666 ) ;
2010 
2011    if( jj > 0 ){
2012      jj = (csum-jj)/2 ;  /* spacing to center title */
2013      sprintf( vstring , "%-*s%s\n" , jj , " " , title ) ;
2014    }
2015 
2016    /* fill up output string */
2017    /* first row of printout = column labels (if any) */
2018 
2019    for( jj=0 ; jj < ncol ; jj++ ){         /* col #jj */
2020      qpt = (nel->vec_lab != NULL ) ? nel->vec_lab[jj] : NULL ;
2021      if( qpt != NULL ){
2022        sprintf( vstring+strlen(vstring), " %-*s ", cwid[jj], qpt ) ;
2023      } else {
2024        char qstr[32] ;
2025        sprintf(qstr,"[%d]",jj) ;
2026        sprintf( vstring+strlen(vstring), " %-*s ", cwid[jj], qstr ) ;
2027      }
2028    }
2029    sprintf( vstring+strlen(vstring), "\n" ) ;
2030 
2031 #if 0
2032    /* next row = column type labels (e.g., "float") */
2033 
2034    for( jj=0 ; jj < ncol ; jj++ ){
2035      qpt = NI_rowtype_code_to_name( nel->vec_typ[jj] ) ;
2036      sprintf( vstring+strlen(vstring), " %-*s ", cwid[jj], qpt ) ;
2037    }
2038    sprintf( vstring+strlen(vstring), "\n" ) ;
2039 #endif
2040 
2041 #if 1
2042    /* next row = underlining of column labels */
2043 #define DASHES "---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
2044    for( jj=0 ; jj < ncol ; jj++ ){
2045      sprintf( vstring+strlen(vstring) ,
2046               " %*.*s " , cwid[jj] , cwid[jj] , DASHES ) ;
2047    }
2048    sprintf( vstring+strlen(vstring), "\n" ) ;
2049 #endif
2050 
2051    /* then do data rows */
2052 
2053    for( ii=0 ; ii < nview ; ii++ ){
2054      for( jj=0 ; jj < ncol ; jj++ ){
2055        qpt = NI_extract_text_value( nel, ii, jj ) ;
2056        sprintf( vstring+strlen(vstring), " %-*s ", cwid[jj], qpt ) ;
2057        free(qpt) ; /* unlike the type labels, we free this string */
2058      }
2059      if( ii < nview-1 ) sprintf( vstring+strlen(vstring), "\n" ) ;
2060    }
2061 
2062    NI_set_raw_val_to_text(0) ;
2063    free(cwid) ;
2064    return vstring ;
2065 }
2066