1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *   $Header: /upc/share/CVS/netcdf-3/ncgen/util.c,v 1.4 2010/04/14 22:04:59 dmh Exp $
5  *********************************************************************/
6 
7 #include "includes.h"
8 
9 /* Track primitive symbol instances (initialized in ncgen.y) */
10 Symbol* primsymbols[PRIMNO];
11 
12 char*
append(const char * s1,const char * s2)13 append(const char* s1, const char* s2)
14 {
15     int len = (s1?strlen(s1):0)+(s2?strlen(s2):0);
16     char* result = (char*)ecalloc(len+1);
17     result[0] = '\0';
18     if(s1) strcat(result,s1);
19     if(s2) strcat(result,s2);
20     return result;
21 }
22 
23 
24 unsigned int
chartohex(char c)25 chartohex(char c)
26 {
27     switch (c) {
28         case '0': case '1': case '2': case '3': case '4':
29         case '5': case '6': case '7': case '8': case '9':
30             return (c - '0');
31         case 'A': case 'B': case 'C':
32         case 'D': case 'E': case 'F':
33             return (c - 'A') + 0x0a;
34         case 'a': case 'b': case 'c':
35         case 'd': case 'e': case 'f':
36             return (c - 'a') + 0x0a;
37     }
38     return 0;
39 }
40 
41 /*
42  * For generated Fortran, change 'e' to 'd' in exponent of double precision
43  * constants.
44  */
45 void
expe2d(char * cp)46 expe2d(
47     char *cp)			/* string containing double constant */
48 {
49     char *expchar = strrchr(cp,'e');
50     if (expchar) {
51 	*expchar = 'd';
52     }
53 }
54 
55 /* Returns non-zero if n is a power of 2, 0 otherwise */
56 int
pow2(int n)57 pow2(
58      int n)
59 {
60   int m = n;
61   int p = 1;
62 
63   while (m > 0) {
64     m /= 2;
65     p *= 2;
66   }
67   return p == 2*n;
68 }
69 
70 
71 /*
72  * Remove trailing zeros (after decimal point) but not trailing decimal
73  * point from ss, a string representation of a floating-point number that
74  * might include an exponent part.
75  */
76 void
tztrim(char * ss)77 tztrim(
78     char *ss			/* returned string representing dd */
79     )
80 {
81     char *cp, *ep;
82 
83     cp = ss;
84     if (*cp == '-')
85       cp++;
86     while(isdigit((int)*cp) || *cp == '.')
87       cp++;
88     if (*--cp == '.')
89       return;
90     ep = cp+1;
91     while (*cp == '0')
92       cp--;
93     cp++;
94     if (cp == ep)
95       return;
96     while (*ep)
97       *cp++ = *ep++;
98     *cp = '\0';
99     return;
100 }
101 
102 #if 0
103 /* Assume bytebuffer contains pointers to char**/
104 void
105 reclaimattptrs(void* buf, long count)
106 {
107     int i;
108     char** ptrs = (char**)buf;
109     for(i=0;i<count;i++) {efree((void*)ptrs[i]);}
110 }
111 #endif
112 
113 static void
freeSpecialdata(Specialdata * data)114 freeSpecialdata(Specialdata* data)
115 {
116     if(data == NULL) return;
117     reclaimdatalist(data->_Fillvalue);
118     if(data->_ChunkSizes)
119         efree(data->_ChunkSizes);
120     if(data->_Filters) {
121 	int i;
122 	for(i=0;i<data->nfilters;i++) {
123 	    NC4_Filterspec* f = (NC4_Filterspec*)data->_Filters[i];
124             efree(f->params);
125 	    efree(f);
126 	}
127 	efree(data->_Filters);
128     }
129     efree(data);
130 }
131 
132 void
freeSymbol(Symbol * sym)133 freeSymbol(Symbol* sym)
134 {
135     /* recurse first */
136     switch (sym->objectclass) {
137     case NC_VAR:
138 	freeSpecialdata(sym->var.special);
139 	listfree(sym->var.attributes);
140 	break;
141     case NC_TYPE:
142 	if(sym->typ.econst)
143 	    reclaimconstant(sym->typ.econst);
144 	if(sym->typ._Fillvalue)
145 	    reclaimdatalist(sym->typ._Fillvalue);
146 	break;
147     case NC_GRP:
148         if(sym->file.filename)
149 	    efree(sym->file.filename);
150 	break;
151     default: break;
152     }
153     /* Universal */
154     if(sym->name) efree(sym->name);
155     if(sym->fqn) efree(sym->fqn);
156     listfree(sym->prefix);
157     if(sym->data)
158         reclaimdatalist(sym->data);
159     listfree(sym->subnodes);
160     efree(sym);
161 }
162 
163 char* nctypenames[17] = {
164 "NC_NAT",
165 "NC_BYTE", "NC_CHAR", "NC_SHORT", "NC_INT",
166 "NC_FLOAT", "NC_DOUBLE",
167 "NC_UBYTE", "NC_USHORT", "NC_UINT",
168 "NC_INT64", "NC_UINT64",
169 "NC_STRING",
170 "NC_VLEN", "NC_OPAQUE", "NC_ENUM", "NC_COMPOUND"
171 };
172 
173 char* nctypenamesextend[9] = {
174 "NC_GRP", "NC_DIM", "NC_VAR", "NC_ATT", "NC_TYPE",
175 "NC_ECONST","NC_FIELD", "NC_ARRAY","NC_PRIM"
176 };
177 
178 char*
nctypename(nc_type nctype)179 nctypename(nc_type nctype)
180 {
181     char* s;
182     if(nctype >= NC_NAT && nctype <= NC_COMPOUND)
183 	return nctypenames[nctype];
184     if(nctype >= NC_GRP && nctype <= NC_PRIM)
185 	return nctypenamesextend[(nctype - NC_GRP)];
186     if(nctype == NC_FILLVALUE) return "NC_FILL";
187     if(nctype == NC_NIL) return "NC_NIL";
188     s = poolalloc(128);
189     sprintf(s,"NC_<%d>",nctype);
190     return s;
191 }
192 
193 /* These are the augmented NC_ values (0 based from NC_GRP)*/
194 char* ncclassnames[9] = {
195 "NC_GRP", "NC_DIM", "NC_VAR", "NC_ATT",
196 "NC_TYP", "NC_ECONST", "NC_FIELD", "NC_ARRAY",
197 "NC_PRIM"
198 };
199 
200 char*
ncclassname(nc_class ncc)201 ncclassname(nc_class ncc)
202 {
203     char* s;
204     if(ncc >= NC_NAT && ncc <= NC_COMPOUND)
205 	return nctypename((nc_type)ncc);
206     if(ncc == NC_FILLVALUE) return "NC_FILL";
207     if(ncc >= NC_GRP && ncc <= NC_PRIM)
208 	return ncclassnames[ncc - NC_GRP];
209     s = poolalloc(128);
210     sprintf(s,"NC_<%d>",ncc);
211     return s;
212 }
213 
214 int ncsizes[17] = {
215 0,
216 1,1,2,4,
217 4,8,
218 1,2,4,
219 8,8,
220 sizeof(char*),
221 sizeof(nc_vlen_t),
222 0,0,0
223 };
224 
225 int
ncsize(nc_type nctype)226 ncsize(nc_type nctype)
227 {
228     if(nctype >= NC_NAT && nctype <= NC_COMPOUND)
229 	return ncsizes[nctype];
230     return 0;
231 }
232 
233 int
hasunlimited(Dimset * dimset)234 hasunlimited(Dimset* dimset)
235 {
236     int i;
237     for(i=0;i<dimset->ndims;i++) {
238 	Symbol* dim = dimset->dimsyms[i];
239 	if(dim->dim.declsize == NC_UNLIMITED) return 1;
240     }
241     return 0;
242 }
243 
244 /* return 1 if first dimension is unlimited*/
245 int
isunlimited0(Dimset * dimset)246 isunlimited0(Dimset* dimset)
247 {
248    return (dimset->ndims > 0 && dimset->dimsyms[0]->dim.declsize == NC_UNLIMITED);
249 }
250 
251 
252 /* True only if dim[0] is unlimited all rest are bounded*/
253 /* or all are bounded*/
254 int
classicunlimited(Dimset * dimset)255 classicunlimited(Dimset* dimset)
256 {
257     int i;
258     int last = -1;
259     for(i=0;i<dimset->ndims;i++) {
260 	Symbol* dim = dimset->dimsyms[i];
261 	if(dim->dim.declsize == NC_UNLIMITED) last = i;
262     }
263     return (last < 1);
264 }
265 
266 /* True only iff no dimension is unlimited*/
267 int
isbounded(Dimset * dimset)268 isbounded(Dimset* dimset)
269 {
270     int i;
271     for(i=0;i<dimset->ndims;i++) {
272 	Symbol* dim = dimset->dimsyms[i];
273 	if(dim->dim.declsize == NC_UNLIMITED) return 0;
274     }
275     return 1;
276 }
277 
278 int
signedtype(nc_type nctype)279 signedtype(nc_type nctype)
280 {
281     switch (nctype) {
282     case NC_BYTE:
283     case NC_SHORT:
284     case NC_INT:
285     case NC_INT64:
286 	return nctype;
287     case NC_UBYTE: return NC_BYTE;
288     case NC_USHORT: return NC_SHORT;
289     case NC_UINT: return NC_INT;
290     case NC_UINT64: return NC_INT64;
291     default: break;
292     }
293     return nctype;
294 }
295 
296 int
unsignedtype(nc_type nctype)297 unsignedtype(nc_type nctype)
298 {
299     switch (nctype) {
300     case NC_UBYTE:
301     case NC_USHORT:
302     case NC_UINT:
303     case NC_UINT64:
304 	return nctype;
305     case NC_BYTE: return NC_UBYTE;
306     case NC_SHORT: return NC_USHORT;
307     case NC_INT: return NC_UINT;
308     case NC_INT64: return NC_UINT64;
309     default: break;
310     }
311     return nctype;
312 }
313 
314 int
isinttype(nc_type nctype)315 isinttype(nc_type nctype)
316 {
317     return (nctype != NC_CHAR)
318             && ((nctype >= NC_BYTE && nctype <= NC_INT)
319 	        || (nctype >= NC_UBYTE && nctype <= NC_UINT64));
320 }
321 
322 int
isuinttype(nc_type t)323 isuinttype(nc_type t)
324 {
325     return isinttype(t)
326 	   && t >= NC_UBYTE
327            && t <= NC_UINT64
328            && t != NC_INT64;
329 }
330 
331 int
isfloattype(nc_type nctype)332 isfloattype(nc_type nctype)
333 {
334     return (nctype == NC_FLOAT || nctype <= NC_DOUBLE);
335 }
336 
337 int
isclassicprim(nc_type nctype)338 isclassicprim(nc_type nctype)
339 {
340     return    (nctype >= NC_BYTE && nctype <= NC_DOUBLE)
341 	;
342 }
343 
344 int
isclassicprimplus(nc_type nctype)345 isclassicprimplus(nc_type nctype)
346 {
347     return    (nctype >= NC_BYTE && nctype <= NC_DOUBLE)
348 	   || (nctype == NC_STRING)
349 	;
350 }
351 
352 int
isprim(nc_type nctype)353 isprim(nc_type nctype)
354 {
355     return    (nctype >= NC_BYTE && nctype <= NC_STRING)
356 	;
357 }
358 
359 int
isprimplus(nc_type nctype)360 isprimplus(nc_type nctype)
361 {
362     return    (nctype >= NC_BYTE && nctype <= NC_STRING)
363 	   || (nctype == NC_ECONST)
364 	   || (nctype == NC_OPAQUE)
365 	 ;
366 }
367 
368 void
collectpath(Symbol * grp,List * grpstack)369 collectpath(Symbol* grp, List* grpstack)
370 {
371     while(grp != NULL) {
372         listpush(grpstack,(void*)grp);
373 	grp = grp->container;
374     }
375 }
376 
377 
378 #ifdef USE_NETCDF4
379 /* Result is pool'd*/
380 char*
prefixtostring(List * prefix,char * separator)381 prefixtostring(List* prefix, char* separator)
382 {
383     int slen=0;
384     int plen;
385     int i;
386     char* result;
387     if(prefix == NULL) return pooldup("");
388     plen = prefixlen(prefix);
389     if(plen == 0) { /* root prefix*/
390 	slen=0;
391         /* slen += strlen(separator);*/
392         slen++; /* for null terminator*/
393         result = poolalloc(slen);
394         result[0] = '\0';
395 	/*strcat(result,separator);*/
396     } else {
397         for(i=0;i<plen;i++) {
398 	    Symbol* sym = (Symbol*)listget(prefix,i);
399             slen += (strlen(separator)+strlen(sym->name));
400 	}
401         slen++; /* for null terminator*/
402         result = poolalloc(slen);
403         result[0] = '\0';
404         for(i=0;i<plen;i++) {
405 	    Symbol* sym = (Symbol*)listget(prefix,i);
406             strcat(result,separator);
407 	    strcat(result,sym->name); /* append "/<prefix[i]>"*/
408 	}
409     }
410     return result;
411 }
412 #endif
413 
414 /* Result is pool'd*/
415 char*
fullname(Symbol * sym)416 fullname(Symbol* sym)
417 {
418 #ifdef USE_NETCDF4
419     char* s1;
420     char* result;
421     char* prefix;
422     prefix = prefixtostring(sym->prefix,PATHSEPARATOR);
423     s1 = poolcat(prefix,PATHSEPARATOR);
424     result = poolcat(s1,sym->name);
425     return result;
426 #else
427     return nulldup(sym->name);
428 #endif
429 }
430 
431 int
prefixeq(List * x1,List * x2)432 prefixeq(List* x1, List* x2)
433 {
434     Symbol** l1;
435     Symbol** l2;
436     int len,i;
437     if((len=listlength(x1)) != listlength(x2)) return 0;
438     l1=(Symbol**)listcontents(x1);
439     l2=(Symbol**)listcontents(x2);
440     for(i=0;i<len;i++) {
441         if(strcmp(l1[i]->name,l2[i]->name) != 0) return 0;
442     }
443     return 1;
444 }
445 
446 List*
prefixdup(List * prefix)447 prefixdup(List* prefix)
448 {
449     List* dupseq;
450     int i;
451     if(prefix == NULL) return listnew();
452     dupseq = listnew();
453     listsetalloc(dupseq,listlength(prefix));
454     for(i=0;i<listlength(prefix);i++) listpush(dupseq,listget(prefix,i));
455     return dupseq;
456 }
457 
458 /*
459 Many of the generate routines need to construct
460 heap strings for short periods. Remembering to
461 free such space is error prone, so provide a
462 pseudo-GC to handle these short term requests.
463 The idea is to have a fixed size pool
464 tracking malloc requests and automatically
465 releasing when the pool gets full.
466 */
467 
468 /* Max number of allocated pool items*/
469 #define POOLMAX 100
470 
471 static char* pool[POOLMAX];
472 static int poolindex = -1;
473 #define POOL_DEFAULT 256
474 
475 char*
poolalloc(size_t length)476 poolalloc(size_t length)
477 {
478     if(poolindex == -1) { /* initialize*/
479 	memset((void*)pool,0,sizeof(pool));
480 	poolindex = 0;
481     }
482     if(poolindex == POOLMAX) poolindex=0;
483     if(length == 0) length = POOL_DEFAULT;
484     if(pool[poolindex] != NULL) efree(pool[poolindex]);
485     pool[poolindex] = (char*)ecalloc(length);
486     return pool[poolindex++];
487 }
488 
489 char*
pooldup(const char * s)490 pooldup(const char* s)
491 {
492     char* sdup = poolalloc(strlen(s)+1);
493     strncpy(sdup,s,(strlen(s)+1));
494     return sdup;
495 }
496 
497 char*
poolcat(const char * s1,const char * s2)498 poolcat(const char* s1, const char* s2)
499 {
500     int len1, len2;
501     char* cat;
502     if(s1 == NULL && s2 == NULL) return NULL;
503     len1 = (s1?strlen(s1):0);
504     len2 = (s2?strlen(s2):0);
505     cat = poolalloc(len1+len2+1);
506     cat[0] = '\0';
507     if(s1 != NULL) strcat(cat,s1);
508     if(s2 != NULL) strcat(cat,s2);
509     return cat;
510 }
511 
512 /* Result is malloc'd*/
513 unsigned char*
makebytestring(char * s,size_t * lenp)514 makebytestring(char* s, size_t* lenp)
515 {
516     unsigned char* bytes;
517     unsigned char* b;
518     size_t slen = strlen(s); /* # nibbles */
519     size_t blen = slen/2; /* # bytes */
520     int i;
521 
522     ASSERT((slen%2) == 0);
523     ASSERT(blen > 0);
524     bytes = (unsigned char*)ecalloc(blen);
525     b = bytes;
526     for(i=0;i<slen;i+=2) {
527 	unsigned int digit1 = chartohex(*s++);
528 	unsigned int digit2 = chartohex(*s++);
529 	unsigned int byte = (digit1 << 4) | digit2;
530 	*b++ = byte;
531     }
532     if(lenp) *lenp = blen;
533     return bytes;
534 }
535 
536 int
getpadding(int offset,int alignment)537 getpadding(int offset, int alignment)
538 {
539     int rem = (alignment==0?0:(offset % alignment));
540     int pad = (rem==0?0:(alignment - rem));
541     return pad;
542 }
543 
544 static void
reclaimSymbols(void)545 reclaimSymbols(void)
546 {
547     int i;
548     for(i=0;i<listlength(symlist);i++) {
549         Symbol* sym = listget(symlist,i);
550         freeSymbol(sym);
551     }
552 }
553 
554 void
cleanup()555 cleanup()
556 {
557   reclaimSymbols();
558 }
559 
560 /* compute the total n-dimensional size as 1 long array;
561    if stop == 0, then stop = dimset->ndims.
562 */
563 size_t
crossproduct(Dimset * dimset,int start,int stop)564 crossproduct(Dimset* dimset, int start, int stop)
565 {
566     size_t totalsize = 1;
567     int i;
568     for(i=start;i<stop;i++) {
569 	totalsize = totalsize * dimset->dimsyms[i]->dim.declsize;
570     }
571     return totalsize;
572 }
573 
574 /* Do the "complement" of crossproduct;
575    compute the total n-dimensional size of an array
576    starting at 0 thru the 'last' array index.
577    stop if we encounter an unlimited dimension
578 */
579 size_t
prefixarraylength(Dimset * dimset,int last)580 prefixarraylength(Dimset* dimset, int last)
581 {
582     return crossproduct(dimset,0,last+1);
583 }
584 
585 
586 
587 #ifdef USE_HDF5
588 extern int H5Eprint1(FILE * stream);
589 #endif
590 
591 void
check_err(const int stat,const int line,const char * file)592 check_err(const int stat, const int line, const char* file)
593 {
594     check_err2(stat,-1,line,file);
595 }
596 
check_err2(const int stat,const int cdlline,const int line,const char * file)597 void check_err2(const int stat, const int cdlline, const int line, const char* file) {
598     if (stat != NC_NOERR) {
599 	if(cdlline >= 0)
600 	    fprintf(stderr, "ncgen: cdl line %d; %s\n", cdlline, nc_strerror(stat));
601 	else
602 	    fprintf(stderr, "ncgen: %s\n", nc_strerror(stat));
603 	fprintf(stderr, "\t(%s:%d)\n", file,line);
604 #ifdef USE_HDF5
605 	H5Eprint1(stderr);
606 #endif
607 	fflush(stderr);
608         finalize_netcdf(1);
609     }
610 }
611 
612 /**
613 Find the index of the first unlimited
614 dimension at or after 'start'.
615 If no unlimited exists, return |dimset|
616 */
617 int
findunlimited(Dimset * dimset,int start)618 findunlimited(Dimset* dimset, int start)
619 {
620     for(;start<dimset->ndims;start++) {
621 	if(dimset->dimsyms[start]->dim.isunlimited)
622 	    return start;
623     }
624     return dimset->ndims;
625 }
626 
627 /**
628 Find the index of the last unlimited
629 dimension.
630 If no unlimited exists, return |dimset|
631 */
632 int
findlastunlimited(Dimset * dimset)633 findlastunlimited(Dimset* dimset)
634 {
635     int i;
636     for(i=dimset->ndims-1;i>=0;i--) {
637 	if(dimset->dimsyms[i]->dim.isunlimited)
638 	    return i;
639     }
640     return dimset->ndims;
641 }
642 
643 /**
644 Count the number of unlimited dimensions.
645 */
646 int
countunlimited(Dimset * dimset)647 countunlimited(Dimset* dimset)
648 {
649     int i, count;
650     for(count=0,i=dimset->ndims-1;i>=0;i--) {
651 	if(dimset->dimsyms[i]->dim.isunlimited)
652 	    count++;
653     }
654     return count;
655 }
656 
657 /* Return standard format string */
658 const char *
kind_string(int kind)659 kind_string(int kind)
660 {
661     switch (kind) {
662     case 1: return "classic";
663     case 2: return "64-bit offset";
664     case 3: return "netCDF-4";
665     case 4: return "netCDF-4 classic model";
666     default:
667 	derror("Unknown format index: %d\n",kind);
668     }
669     return NULL;
670 }
671