1 /*********************************************************************
2 * Copyright 1993, University Corporation for Atmospheric Research
3 * See netcdf/README file for copying and redistribution conditions.
4 * $Header: /upc/share/CVS/netcdf-3/ncdump/dumplib.c,v 1.85 2010/05/05 22:15:39 dmh Exp $
5 *********************************************************************/
6
7 /*
8 * We potentially include <stdarg.h> before <stdio.h> in order to obtain a
9 * definition for va_list from the GNU C compiler.
10 */
11
12 #include "config.h"
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <assert.h>
19 #ifndef NO_FLOAT_H
20 #include <float.h> /* for FLT_EPSILON, DBL_EPSILON */
21 #endif /* NO_FLOAT_H */
22 #include <math.h>
23 #include <netcdf.h>
24 #include "utils.h"
25 #include "nccomps.h"
26 #include "dumplib.h"
27 #include "ncdump.h"
28 #include "isnan.h"
29 #include "nctime0.h"
30
31 static float float_eps;
32 static double double_eps;
33
34 extern fspec_t formatting_specs; /* set from command-line options */
35
36 static float
float_epsilon(void)37 float_epsilon(void)
38 {
39 float float_eps;
40 #ifndef NO_FLOAT_H
41 float_eps = FLT_EPSILON;
42 #else /* NO_FLOAT_H */
43 {
44 float etop, ebot, eps;
45 float one = 1.0;
46 float two = 2.0;
47 etop = 1.0;
48 ebot = 0.0;
49 eps = ebot + (etop - ebot)/two;
50 while (eps != ebot && eps != etop) {
51 float epsp1;
52
53 epsp1 = one + eps;
54 if (epsp1 > one)
55 etop = eps;
56 else
57 ebot = eps;
58 eps = ebot + (etop - ebot)/two;
59 }
60 float_eps = two * etop;
61 }
62 #endif /* NO_FLOAT_H */
63 return float_eps;
64 }
65
66
67 static double
double_epsilon(void)68 double_epsilon(void)
69 {
70 double double_eps;
71 #ifndef NO_FLOAT_H
72 double_eps = DBL_EPSILON;
73 #else /* NO_FLOAT_H */
74 {
75 double etop, ebot, eps;
76 double one = 1.0;
77 double two = 2.0;
78 etop = 1.0;
79 ebot = 0.0;
80 eps = ebot + (etop - ebot)/two;
81 while (eps != ebot && eps != etop) {
82 double epsp1;
83
84 epsp1 = one + eps;
85 if (epsp1 > one)
86 etop = eps;
87 else
88 ebot = eps;
89 eps = ebot + (etop - ebot)/two;
90 }
91 double_eps = two * etop;
92 }
93 #endif /* NO_FLOAT_H */
94 return double_eps;
95 }
96
97
98 void
init_epsilons(void)99 init_epsilons(void)
100 {
101 float_eps = float_epsilon();
102 double_eps = double_epsilon();
103 }
104
105
106 static char* has_c_format_att(int ncid, int varid);
107
108 int float_precision_specified = 0; /* -p option specified float precision */
109 int double_precision_specified = 0; /* -p option specified double precision */
110 char float_var_fmt[] = "%.NNg";
111 char double_var_fmt[] = "%.NNg";
112 char float_att_fmt[] = "%#.NNgf";
113 char float_attx_fmt[] = "%#.NNg";
114 char double_att_fmt[] = "%#.NNg";
115
116 /* magic number stored in a safebuf and checked, hoping it will be
117 * changed if buffer was overwritten inadvertently */
118 #define SAFEBUF_CERT 2147114711
119
120 /* expression for where SAFEBUF_CERT is stored within safebuf (at end
121 * of buffer, after data) */
122 #define SAFEBUF_EXPR(sbuf) (*(int *)((sbuf)->buf + (sbuf)->len))
123
124 /* expression to be checked whenever a safebuf is used */
125 #define SAFEBUF_CHECK(sbuf) (SAFEBUF_EXPR(sbuf) == SAFEBUF_CERT)
126
127 /* somewhat arbitrary initial size of safebufs, grow as needed */
128 #define SAFEBUF_INIT_LEN 128
129
130 /* initialize safe buffer */
131 safebuf_t *
sbuf_new()132 sbuf_new() {
133 size_t len = SAFEBUF_INIT_LEN;
134 safebuf_t *sb;
135 sb = (safebuf_t *) emalloc(sizeof(safebuf_t));
136 sb->buf = (char *)emalloc(len + sizeof(int));
137 sb->len = len;
138 /* write a "stamp" in last 4 bytes of buffer for id and to check for overflow */
139 SAFEBUF_EXPR(sb) = SAFEBUF_CERT;
140 sb->buf[0] = 0;
141 sb->cl = strlen(sb->buf);
142 assert(SAFEBUF_CHECK(sb));
143 return sb;
144 }
145
146
147 /* grow buffer to at least len bytes, copying previous contents if
148 * necessary */
149 void
sbuf_grow(safebuf_t * sb,size_t len)150 sbuf_grow(safebuf_t *sb, size_t len) {
151 size_t m = sb->len;
152 void *tmp;
153 assert(SAFEBUF_CHECK(sb));
154 if (len <= m)
155 return;
156
157 /* Make sure we at least double size of buffer to get what's
158 * needed. If we just used realloc(), no guarantee that length
159 * would be expanded by a multiple, which we want. */
160 while(len > m) {
161 m *= 2;
162 }
163 tmp = emalloc(m + sizeof(int));
164 memcpy(tmp, sb->buf, sb->len);
165 sb->len = m;
166 free(sb->buf);
167 sb->buf = tmp;
168 SAFEBUF_EXPR(sb) = SAFEBUF_CERT;
169 assert(SAFEBUF_CHECK(sb));
170 }
171
172 /* Copy string s2 to safe buffer, growing if necessary */
173 void
sbuf_cpy(safebuf_t * sb,const char * s2)174 sbuf_cpy(safebuf_t *sb, const char *s2) {
175 size_t s2len;
176 assert(SAFEBUF_CHECK(sb));
177 s2len = strlen(s2);
178 sbuf_grow(sb, 1 + s2len);
179 strncpy(sb->buf, s2, sb->len);
180 sb->cl = s2len;
181 assert(SAFEBUF_CHECK(sb));
182 }
183
184 /* Concatenate string s2 to end of string in safe buffer, growing if necessary */
185 void
sbuf_cat(safebuf_t * sb,const char * s2)186 sbuf_cat(safebuf_t *sb, const char *s2) {
187 size_t s2len;
188 size_t res;
189 assert(SAFEBUF_CHECK(sb));
190 s2len = strlen(s2);
191 sbuf_grow(sb, 1 + sb->cl + s2len);
192 res = strlcat(sb->buf + sb->cl, s2, sb->len);
193 assert( res < sb->len );
194 sb->cl += s2len;
195 assert(SAFEBUF_CHECK(sb));
196 }
197
198 /* Concatenate string in safebuf s2 to end of string in safebuf s1,
199 * growing if necessary */
200 void
sbuf_catb(safebuf_t * s1,const safebuf_t * s2)201 sbuf_catb(safebuf_t *s1, const safebuf_t *s2) {
202 size_t s2len;
203 size_t res;
204 assert(SAFEBUF_CHECK(s1));
205 assert(SAFEBUF_CHECK(s2));
206 s2len = sbuf_len(s2);
207 sbuf_grow(s1, 1 + s1->cl + s2len);
208 res = strlcat(s1->buf + s1->cl, s2->buf, s1->len);
209 assert( res < s1->len );
210 s1->cl += s2len;
211 assert(SAFEBUF_CHECK(s1));
212 }
213
214 /* Return length of string in sbuf */
215 size_t
sbuf_len(const safebuf_t * sb)216 sbuf_len(const safebuf_t *sb) {
217 assert(SAFEBUF_CHECK(sb));
218 return sb->cl;
219 }
220
221 /* Return C string in an sbuf */
222 char *
sbuf_str(const safebuf_t * sb)223 sbuf_str(const safebuf_t *sb) {
224 assert(SAFEBUF_CHECK(sb));
225 return sb->buf;
226 }
227
228 /* free safe buffer */
229 void
sbuf_free(safebuf_t * sb)230 sbuf_free(safebuf_t *sb) {
231 assert(SAFEBUF_CHECK(sb));
232 free(sb->buf);
233 free(sb);
234 }
235
236
237 /* In case different formats specified with -d option, set them here. */
238 void
set_formats(int float_digits,int double_digits)239 set_formats(int float_digits, int double_digits)
240 {
241 int res;
242 res = snprintf(float_var_fmt, strlen(float_var_fmt) + 1, "%%.%dg",
243 float_digits) + 1;
244 assert(res <= sizeof(float_var_fmt));
245 res = snprintf(double_var_fmt, strlen(double_var_fmt) + 1, "%%.%dg",
246 double_digits) + 1;
247 assert(res <= sizeof(double_var_fmt));
248 res = snprintf(float_att_fmt, strlen(float_att_fmt) + 1, "%%#.%dgf",
249 float_digits) + 1;
250 assert(res <= sizeof(float_att_fmt));
251 res = snprintf(float_attx_fmt, strlen(float_attx_fmt) + 1, "%%#.%dg",
252 float_digits) + 1;
253 assert(res <= sizeof(float_attx_fmt));
254 res = snprintf(double_att_fmt, strlen(double_att_fmt) + 1, "%%#.%dg",
255 double_digits) + 1;
256 assert(res <= sizeof(double_att_fmt));
257 }
258
259
260 static char *
has_c_format_att(int ncid,int varid)261 has_c_format_att(
262 int ncid, /* netcdf id */
263 int varid /* variable id */
264 )
265 {
266 nc_type cfmt_type;
267 size_t cfmt_len;
268 #define C_FMT_NAME "C_format" /* name of C format attribute */
269 #define MAX_CFMT_LEN 100 /* max length of C format attribute */
270 static char cfmt[MAX_CFMT_LEN];
271
272 /* we expect nc_inq_att to fail if there is no "C_format" attribute */
273 int nc_stat = nc_inq_att(ncid, varid, "C_format", &cfmt_type, &cfmt_len);
274
275 switch(nc_stat) {
276 case NC_NOERR:
277 if (cfmt_type == NC_CHAR && cfmt_len != 0 && cfmt_len < MAX_CFMT_LEN) {
278 int nc_stat = nc_get_att_text(ncid, varid, "C_format", cfmt);
279 if(nc_stat != NC_NOERR) {
280 fprintf(stderr, "Getting 'C_format' attribute %s\n",
281 nc_strerror(nc_stat));
282 (void) fflush(stderr);
283 }
284 cfmt[cfmt_len] = '\0';
285 return &cfmt[0];
286 }
287 break;
288 case NC_ENOTATT:
289 break;
290 default:
291 fprintf(stderr, "Inquiring about 'C_format' attribute %s\n",
292 nc_strerror(nc_stat));
293 (void) fflush(stderr);
294 break;
295 }
296 return 0;
297 }
298
299
300 /* Return default format to use for a primitive type */
301 const char *
get_default_fmt(nc_type typeid)302 get_default_fmt(nc_type typeid) {
303 /* Otherwise return sensible default. */
304 switch (typeid) {
305 case NC_BYTE:
306 return "%d";
307 case NC_CHAR:
308 return "%s";
309 case NC_SHORT:
310 return "%d";
311 case NC_INT:
312 return "%d";
313 case NC_FLOAT:
314 return float_var_fmt;
315 case NC_DOUBLE:
316 return double_var_fmt;
317 case NC_UBYTE:
318 return "%u";
319 case NC_USHORT:
320 return "%u";
321 case NC_UINT:
322 return "%u";
323 case NC_INT64:
324 return "%lld";
325 case NC_UINT64:
326 return "%llu";
327 case NC_STRING:
328 return "\"%s\"";
329 default:
330 break;
331 }
332 return ""; /* user-defined types don't use fmt member */
333 }
334
335 /*
336 * Determine print format to use for each primitive value for this
337 * variable. Use value of attribute C_format if it exists, otherwise
338 * a sensible default.
339 */
340 const char *
get_fmt(int ncid,int varid,nc_type typeid)341 get_fmt(
342 int ncid,
343 int varid,
344 nc_type typeid
345 )
346 {
347 char *c_format_att;
348
349 /* float or double precision specified with -p option overrides any
350 C_format attribute value, so check for that first. */
351 if (float_precision_specified && typeid == NC_FLOAT)
352 return float_var_fmt;
353 if (double_precision_specified && typeid == NC_DOUBLE)
354 return double_var_fmt;
355 /* If C_format attribute exists, return it */
356 c_format_att = has_c_format_att(ncid, varid);
357 if (c_format_att)
358 return c_format_att;
359 return get_default_fmt(typeid);
360 }
361
362 /* Return primitive type name */
363 static const char *
prim_type_name(nc_type type)364 prim_type_name(nc_type type)
365 {
366 switch (type) {
367 case NC_BYTE:
368 return "byte";
369 case NC_CHAR:
370 return "char";
371 case NC_SHORT:
372 return "short";
373 case NC_INT:
374 return "int";
375 case NC_FLOAT:
376 return "float";
377 case NC_DOUBLE:
378 return "double";
379 case NC_UBYTE:
380 return "ubyte";
381 case NC_USHORT:
382 return "ushort";
383 case NC_UINT:
384 return "uint";
385 case NC_INT64:
386 return "int64";
387 case NC_UINT64:
388 return "uint64";
389 case NC_STRING:
390 return "string";
391 default:
392 error("prim_type_name: bad type %d", type);
393 return "bogus";
394 }
395 }
396
397 static int max_type = 0;
398 static int max_atomic_type = 0;
399 static nctype_t **nctypes = 0; /* holds all types in a netCDF dataset */
400
401
402 #ifdef USE_NETCDF4
403 /* return number of user-defined types in a group and all its subgroups */
404 static int
count_udtypes(int ncid)405 count_udtypes(int ncid) {
406 int ntypes = 0;
407 int numgrps;
408 int *ncids;
409 int i;
410 int format;
411
412 NC_CHECK( nc_inq_format(ncid, &format) );
413
414 if (format == NC_FORMAT_NETCDF4) {
415 /* Get number of types in this group */
416 NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) ) ;
417 NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) ) ;
418 ncids = (int *) emalloc(sizeof(int) * (numgrps + 1));
419 NC_CHECK( nc_inq_grps(ncid, NULL, ncids) ) ;
420 /* Add number of types in each subgroup, if any */
421 for (i=0; i < numgrps; i++) {
422 ntypes += count_udtypes(ncids[i]);
423 }
424 free(ncids);
425 }
426 return ntypes;
427 }
428 #endif /*USE_NETCDF4*/
429
430 /* This routine really is intended to return the max atomic typeid */
431 static int
max_typeid(int ncid)432 max_typeid(int ncid) {
433 int maxtypes = NC_NAT;
434 int maxatomictypes = NC_NAT;
435 int format = 0;
436 int err = NC_NOERR;
437
438 /* get the file type */
439 err = nc_inq_format(ncid,&format);
440 if(err) {
441 fprintf(stderr,"%s: Cannot get file format.\n",nc_strerror(err));
442 return 0;
443 }
444 switch (format) {
445 case NC_FORMAT_CLASSIC:
446 case NC_FORMAT_NETCDF4_CLASSIC:
447 case NC_FORMAT_64BIT_OFFSET:
448 maxatomictypes = (maxtypes = NC_DOUBLE); /*ignore NC_NAT?*/
449 break;
450 case NC_FORMAT_64BIT_DATA:
451 maxatomictypes = (maxtypes = NC_UINT64);
452 break;
453 case NC_FORMAT_NETCDF4:
454 #ifdef USE_NETCDF4
455 {
456 int nuser = 0;
457 maxatomictypes = (maxtypes = NC_STRING); /* extra netCDF-4 primitive types */
458 maxtypes += 4; /* user-defined classes */
459 nuser = count_udtypes(ncid);
460 if(nuser > 0)
461 maxtypes = NC_FIRSTUSERTYPEID + (nuser - 1);
462 } break;
463 #else
464 /* fallthru */
465 #endif
466 default:
467 fprintf(stderr,"Unexpected file format: %d\n",format);
468 return 0;
469 }
470 max_type = maxtypes;
471 max_atomic_type = maxatomictypes;
472 return maxtypes;
473 }
474
typeadd(nctype_t * typep)475 void typeadd(nctype_t *typep) {
476 nctypes[typep->tid] = typep;
477 }
478
479 /* From type id, get full type info */
480 nctype_t *
get_typeinfo(int typeid)481 get_typeinfo ( int typeid ) {
482 if(typeid < 0 || typeid > max_type)
483 error("ncdump: %d is an invalid type id", typeid);
484 return nctypes[typeid];
485 }
486
487 /* void */
488 /* xfree_typeinfo(int ncid) { */
489 /* int i; */
490 /* for (i = 0; i < number_of_types; i++) { */
491 /* nctype_t *tinfop = nctypes[i]; */
492 /* if (tinfop) { */
493 /* if(tinfop->name) */
494 /* free(tinfop->name); */
495 /* if(tinfop->grps) */
496 /* free(tinfop->grps); */
497 /* free(tinfop); */
498 /* } */
499 /* } */
500 /* } */
501
502
503 bool_t
ncbyte_val_equals(const nctype_t * this,const void * v1p,const void * v2p)504 ncbyte_val_equals(const nctype_t *this,
505 const void *v1p, const void *v2p) {
506 return ( *(signed char* )v1p == *(signed char* )v2p);
507 }
508
509 bool_t
ncchar_val_equals(const nctype_t * this,const void * v1p,const void * v2p)510 ncchar_val_equals(const nctype_t *this,
511 const void *v1p, const void *v2p) {
512 return ( *(char* )v1p == *(char* )v2p);
513 }
514
515 bool_t
ncshort_val_equals(const nctype_t * this,const void * v1p,const void * v2p)516 ncshort_val_equals(const nctype_t *this,
517 const void *v1p, const void *v2p) {
518 return ( *(short* )v1p == *(short* )v2p);
519 }
520
521 bool_t
ncint_val_equals(const nctype_t * this,const void * v1p,const void * v2p)522 ncint_val_equals(const nctype_t *this,
523 const void *v1p, const void *v2p) {
524 return ( *(int* )v1p == *(int* )v2p);
525 }
526
527 #define absval(x) ( (x) < 0 ? -(x) : (x) )
528
529 /*
530 * Return ( *(float* )v1p == *(float* )v2p);
531 * except use floating epsilon to compare very close vals as equal
532 * and handle IEEE NaNs and infinities.
533 */
534 bool_t
ncfloat_val_equals(const nctype_t * this,const void * v1p,const void * v2p)535 ncfloat_val_equals(const nctype_t *this,
536 const void *v1p, const void *v2p) {
537 float v1 = *(float* )v1p;
538 float v2 = *(float* )v2p;
539 if((v1 > 0.0f) != (v2 > 0.0f)) /* avoid overflow */
540 return false;
541 if(isfinite(v1) && isfinite(v2))
542 return (absval(v1 - v2) <= absval(float_eps * v2)) ;
543 if(isnan(v1) && isnan(v2))
544 return true;
545 if(isinf(v1) && isinf(v2))
546 return true;
547 return false;
548 }
549
550 /*
551 * Return ( *(double* )v1p == *(double* )v2p);
552 * except use floating epsilon to compare very close vals as equal
553 * and handle IEEE NaNs and infinities.
554 */
555 bool_t
ncdouble_val_equals(const nctype_t * this,const void * v1p,const void * v2p)556 ncdouble_val_equals(const nctype_t *this,
557 const void *v1p, const void *v2p) {
558 double v1 = *(double* )v1p;
559 double v2 = *(double* )v2p;
560 if((v1 > 0.0) != (v2 > 0.0)) /* avoid overflow */
561 return false;
562 if(isfinite(v1) && isfinite(v2))
563 return (absval(v1 - v2) <= absval(double_eps * v2)) ;
564 if(isnan(v1) && isnan(v2))
565 return true;
566 if(isinf(v1) && isinf(v2))
567 return true;
568 return false;
569 }
570
571 bool_t
ncubyte_val_equals(const nctype_t * this,const void * v1p,const void * v2p)572 ncubyte_val_equals(const nctype_t *this,
573 const void *v1p, const void *v2p) {
574 return ( *(unsigned char* )v1p == *(unsigned char* )v2p);
575 }
576
577 bool_t
ncushort_val_equals(const nctype_t * this,const void * v1p,const void * v2p)578 ncushort_val_equals(const nctype_t *this,
579 const void *v1p, const void *v2p) {
580 return ( *(unsigned short* )v1p == *(unsigned short* )v2p);
581 }
582
583 bool_t
ncuint_val_equals(const nctype_t * this,const void * v1p,const void * v2p)584 ncuint_val_equals(const nctype_t *this,
585 const void *v1p, const void *v2p) {
586 return ( *(unsigned int* )v1p == *(unsigned int* )v2p);
587 }
588
589 bool_t
ncint64_val_equals(const nctype_t * this,const void * v1p,const void * v2p)590 ncint64_val_equals(const nctype_t *this,
591 const void *v1p, const void *v2p) {
592 return ( *(long long* )v1p == *(long long* )v2p);
593 }
594
595 bool_t
ncuint64_val_equals(const nctype_t * this,const void * v1p,const void * v2p)596 ncuint64_val_equals(const nctype_t *this,
597 const void *v1p, const void *v2p) {
598 return ( *(unsigned long long* )v1p == *(unsigned long long* )v2p);
599 }
600
601 bool_t
ncstring_val_equals(const nctype_t * this,const void * v1p,const void * v2p)602 ncstring_val_equals(const nctype_t *this,
603 const void *v1p, const void *v2p) {
604 if (NULL == *((char **)v1p) && NULL == *((char **)v2p))
605 return(1);
606 else if (NULL != *((char **)v1p) && NULL == *((char **)v2p))
607 return(0);
608 else if (NULL == *((char **)v1p) && NULL != *((char **)v2p))
609 return(0);
610 return (strcmp(*((char **)v1p), *((char **)v2p)) == 0);
611 }
612
613 #ifdef USE_NETCDF4
614 bool_t
ncopaque_val_equals(const nctype_t * this,const void * v1p,const void * v2p)615 ncopaque_val_equals(const nctype_t *this,
616 const void *v1p, const void *v2p) {
617 size_t nbytes = this->size;
618 const char *c1p = (const char *) v1p;
619 const char *c2p = (const char *) v2p;
620 int i;
621 for (i=0; i < nbytes; i++) {
622 if (*c1p++ != *c2p++)
623 return false;
624 }
625 return true;
626 }
627
628 bool_t
ncvlen_val_equals(const nctype_t * this,const void * v1p,const void * v2p)629 ncvlen_val_equals(const nctype_t *this,
630 const void *v1p, const void *v2p) {
631 size_t v1len = ((nc_vlen_t *)v1p)->len;
632 size_t v2len = ((nc_vlen_t *)v2p)->len;
633 if (v1len != v2len)
634 return false;
635 {
636 size_t base_size = this->size;
637 nc_type base_type = this->base_tid;
638 nctype_t *base_info = get_typeinfo(base_type);
639 val_equals_func base_val_equals = base_info->val_equals;
640 const char *v1dat = ((nc_vlen_t *)v1p)->p;
641 const char *v2dat = ((nc_vlen_t *)v2p)->p;
642 size_t i;
643 for(i = 0; i < v1len; i++) {
644 if (base_val_equals(base_info, (const void *)v1dat,
645 (const void *)v2dat) != true)
646 return false;
647 v1dat += base_size;
648 v2dat += base_size;
649 }
650 }
651 return true;
652 }
653
654 /* Determine if two compound values are equal, by testing eqaulity of
655 * each member field. */
656 bool_t
nccomp_val_equals(const nctype_t * this,const void * v1p,const void * v2p)657 nccomp_val_equals(const nctype_t *this,
658 const void *v1p, const void *v2p) {
659 int nfields = this->nfields;
660 int fidx; /* field id */
661
662 for (fidx = 0; fidx < nfields; fidx++) {
663 size_t offset = this->offsets[fidx];
664 nc_type fid = this->fids[fidx]; /* field type id */
665 nctype_t *finfo = get_typeinfo(fid);
666 if(finfo->ranks == 0 || finfo->ranks[fidx] == 0) {
667 if(! finfo->val_equals(finfo,
668 (char *)v1p + offset, (char *)v2p + offset))
669 return false;
670 } else { /* this field is an array */
671 int i; /* array element counter when rank > 0 */
672 void *v1elem = (char *)v1p + offset;
673 void *v2elem = (char *)v2p + offset;
674 for(i = 0; i < finfo->nvals[fidx]; i++) {
675 if(! finfo->val_equals(finfo, v1elem, v2elem))
676 return false;
677 v1elem = (char *)v1elem + finfo->size;
678 v2elem = (char *)v1elem + finfo->size;
679 }
680 }
681 }
682 return true;
683 }
684 #endif /* USE_NETCDF4 */
685
686 int
ncbyte_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)687 ncbyte_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
688 char sout[PRIM_LEN];
689 int res;
690 res = snprintf(sout, PRIM_LEN, typ->fmt, *(signed char *)valp);
691 assert(res < PRIM_LEN);
692 sbuf_cpy(sfbf, sout);
693 return sbuf_len(sfbf);
694 }
695
696 int
ncchar_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)697 ncchar_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
698 char sout[PRIM_LEN];
699 int res;
700 res = snprintf(sout, PRIM_LEN, typ->fmt, *(char *)valp);
701 assert(res < PRIM_LEN);
702 sbuf_cpy(sfbf, sout);
703 return sbuf_len(sfbf);
704 }
705
706 int
ncshort_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)707 ncshort_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
708 char sout[PRIM_LEN];
709 int res;
710 res = snprintf(sout, PRIM_LEN, typ->fmt, *(short *)valp);
711 assert(res < PRIM_LEN);
712 sbuf_cpy(sfbf, sout);
713 return sbuf_len(sfbf);
714 }
715
716 int
ncint_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)717 ncint_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
718 char sout[PRIM_LEN];
719 int res;
720 res = snprintf(sout, PRIM_LEN, typ->fmt, *(int *)valp);
721 assert(res < PRIM_LEN);
722 sbuf_cpy(sfbf, sout);
723 return sbuf_len(sfbf);
724 }
725
726 /* CDL canonical representations of some special floating point values */
727 #define NCDL_NANF "NaNf"
728 #define NCDL_NAN "NaN"
729 #define NCDL_INFF "Infinityf"
730 #define NCDL_INF "Infinity"
731
732 /* Convert a float NaN or Infinity to an allocated string large enough
733 * to hold it (at least PRIM_LEN chars) */
734 static void
float_special_tostring(float vv,char * sout)735 float_special_tostring(float vv, char *sout) {
736 if(isnan(vv)) {
737 snprintf(sout, PRIM_LEN, "%s", NCDL_NANF);
738 } else if(isinf(vv)) {
739 if(vv < 0.0) {
740 snprintf(sout, PRIM_LEN, "-%s", NCDL_INFF);
741 } else {
742 snprintf(sout, PRIM_LEN, "%s", NCDL_INFF);
743 }
744 } else
745 assert(false); /* vv was finite */
746 }
747
748 /* Convert a double NaN or Infinity to an allocated string large enough
749 * to hold it (at least PRIM_LEN chars) */
750 static void
double_special_tostring(double vv,char * sout)751 double_special_tostring(double vv, char *sout) {
752 if(isnan(vv)) {
753 snprintf(sout, PRIM_LEN, "%s", NCDL_NAN);
754 } else if(isinf(vv)) {
755 if(vv < 0.0) {
756 snprintf(sout, PRIM_LEN, "-%s", NCDL_INF);
757 } else {
758 snprintf(sout, PRIM_LEN, "%s", NCDL_INF);
759 }
760 } else
761 assert(false); /* vv was finite */
762 }
763
764 int
ncfloat_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)765 ncfloat_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
766 char sout[PRIM_LEN];
767 float vv = *(float *)valp;
768 if(isfinite(vv)) {
769 int res;
770 res = snprintf(sout, PRIM_LEN, typ->fmt, vv);
771 assert(res < PRIM_LEN);
772 } else {
773 float_special_tostring(vv, sout);
774 }
775 sbuf_cpy(sfbf, sout);
776 return sbuf_len(sfbf);
777 }
778
779 int
ncdouble_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)780 ncdouble_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
781 char sout[PRIM_LEN];
782 double vv = *(double *)valp;
783 if(isfinite(vv)) {
784 int res;
785 res = snprintf(sout, PRIM_LEN, typ->fmt, vv);
786 assert(res < PRIM_LEN);
787 } else {
788 double_special_tostring(vv, sout);
789 }
790 sbuf_cpy(sfbf, sout);
791 return sbuf_len(sfbf);
792 }
793
794 int
ncubyte_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)795 ncubyte_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
796 char sout[PRIM_LEN];
797 int res;
798 res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned char *)valp);
799 assert(res < PRIM_LEN);
800 sbuf_cpy(sfbf, sout);
801 return sbuf_len(sfbf);
802 }
803
804 int
ncushort_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)805 ncushort_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
806 char sout[PRIM_LEN];
807 int res;
808 res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned short *)valp);
809 assert(res < PRIM_LEN);
810 sbuf_cpy(sfbf, sout);
811 return sbuf_len(sfbf);
812 }
813
814 int
ncuint_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)815 ncuint_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
816 char sout[PRIM_LEN];
817 int res;
818 res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned int *)valp);
819 assert(res < PRIM_LEN);
820 sbuf_cpy(sfbf, sout);
821 return sbuf_len(sfbf);
822 }
823
824 int
ncint64_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)825 ncint64_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
826 char sout[PRIM_LEN];
827 int res;
828 res = snprintf(sout, PRIM_LEN, typ->fmt, *(long long *)valp);
829 assert(res < PRIM_LEN);
830 sbuf_cpy(sfbf, sout);
831 return sbuf_len(sfbf);
832 }
833
834 int
ncuint64_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)835 ncuint64_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
836 char sout[PRIM_LEN];
837 int res;
838 res = snprintf(sout, PRIM_LEN, typ->fmt, *(unsigned long long *)valp);
839 assert(res < PRIM_LEN);
840 sbuf_cpy(sfbf, sout);
841 return sbuf_len(sfbf);
842 }
843
ncstring_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)844 int ncstring_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp)
845 {
846 const char *cp;
847
848 cp = ((char **)valp)[0];
849 if(cp) {
850 size_t slen;
851 char *sout;
852 char *sp;
853 unsigned char uc;
854
855 slen = 4 + 5 * strlen(cp); /* need "'s around string, and extra space to escape control characters */
856 slen++; /* nul term */
857 sout = emalloc(slen);
858 sp = sout;
859 *sp++ = '"' ;
860 while(*cp) {
861 switch (uc = *cp++ & 0377) {
862 case '\b':
863 *sp++ = '\\';
864 *sp++ = 'b' ;
865 break;
866 case '\f':
867 *sp++ = '\\';
868 *sp++ = 'f';
869 break;
870 case '\n':
871 *sp++ = '\\';
872 *sp++ = 'n';
873 break;
874 case '\r':
875 *sp++ = '\\';
876 *sp++ = 'r';
877 break;
878 case '\t':
879 *sp++ = '\\';
880 *sp++ = 't';
881 break;
882 case '\v':
883 *sp++ = '\\';
884 *sp++ = 'n';
885 break;
886 case '\\':
887 *sp++ = '\\';
888 *sp++ = '\\';
889 break;
890 case '\'':
891 *sp++ = '\\';
892 *sp++ = '\'';
893 break;
894 case '\"':
895 *sp++ = '\\';
896 *sp++ = '\"';
897 break;
898 default:
899 if (iscntrl(uc)) {
900 snprintf(sp,4+1,"\\%03o",uc); /* +1 for nul */
901 sp += 4;
902 }
903 else
904 *sp++ = uc;
905 break;
906 }
907 }
908 *sp++ = '"' ;
909 *sp = '\0' ;
910 sbuf_cpy(sfbf, sout);
911 free(sout);
912 }
913 else {
914 sbuf_cpy(sfbf, "NIL");
915 }
916 return sbuf_len(sfbf);
917 }
918
919 #ifdef USE_NETCDF4
920 int
ncenum_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)921 ncenum_typ_tostring(const nctype_t *typ, safebuf_t *sfbf, const void *valp) {
922 char symbol[NC_MAX_NAME + 1];
923 long long val = 0;
924
925 switch (typ->base_tid) {
926 case NC_BYTE:
927 val = *(signed char *)valp;
928 break;
929 case NC_UBYTE:
930 val = *(unsigned char *)valp;
931 break;
932 case NC_SHORT:
933 val = *(short *)valp;
934 break;
935 case NC_USHORT:
936 val = *(unsigned short *)valp;
937 break;
938 case NC_INT:
939 val = *(int *)valp;
940 break;
941 case NC_UINT:
942 val = *(unsigned int *)valp;
943 break;
944 case NC_INT64:
945 val = *(long long *)valp;
946 break;
947 case NC_UINT64:
948 val = *(long long *)valp;
949 break;
950 default:
951 error("bad base type for enum");
952 break;
953 }
954 NC_CHECK( nc_inq_enum_ident(typ->ncid, typ->tid, val, symbol));
955 sbuf_cpy(sfbf, symbol);
956 return sbuf_len(sfbf);
957 }
958
959 /* Given an opaque type size and opaque value, convert to a string,
960 * represented as hexadecimal characters, returning number of chars in
961 * output string */
962 int
ncopaque_val_as_hex(size_t size,char * sout,const void * valp)963 ncopaque_val_as_hex(size_t size, char *sout, const void *valp) {
964 const unsigned char *cp = valp;
965 char *sp = sout;
966 int i;
967 char *prefix = "0X";
968 int prelen = strlen(prefix);
969
970 snprintf(sp, prelen + 1, "%s", prefix);
971 sp += prelen;
972 for(i = 0; i < size; i++) {
973 int res;
974 res = snprintf(sp, prelen + 1, "%.2X", *cp++);
975 assert (res == 2);
976 sp += 2;
977 }
978 *sp = '\0';
979 return 2*size + prelen;
980 }
981
982 /* Convert an opaque value to a string, represented as hexadecimal
983 * characters */
984 int
ncopaque_typ_tostring(const nctype_t * typ,safebuf_t * sfbf,const void * valp)985 ncopaque_typ_tostring(const nctype_t *typ, safebuf_t *sfbf,
986 const void *valp) {
987 char* sout = (char *) emalloc(2 * typ->size + strlen("0X") + 1);
988 (void) ncopaque_val_as_hex(typ->size, sout, valp);
989 sbuf_cpy(sfbf, sout);
990 free(sout);
991 return sbuf_len(sfbf);
992 }
993
994 /* Convert a vlen value to a string, by using tostring function for base type */
995 int
ncvlen_typ_tostring(const nctype_t * tinfo,safebuf_t * sfbf,const void * valp)996 ncvlen_typ_tostring(const nctype_t *tinfo, safebuf_t *sfbf, const void *valp) {
997 nc_type base_type = tinfo->base_tid;
998 nctype_t *base_info = get_typeinfo(base_type);
999 size_t base_size = base_info->size;
1000 size_t len = ((nc_vlen_t *)valp)->len;
1001 typ_tostring_func base_typ_tostring = base_info->typ_tostring;
1002 size_t i;
1003 const char *vp; /* instead of void* so can increment to next */
1004 safebuf_t* sout2 = sbuf_new();
1005
1006 sbuf_cpy(sfbf, "{");
1007 /* put each val in sout2, then append sout2 to sfbf */
1008 vp = ((nc_vlen_t *)valp)->p;
1009 for(i = 0; i < len; i++) {
1010 (void) base_typ_tostring(base_info, sout2, vp);
1011 sbuf_catb(sfbf, sout2);
1012 if(i < len - 1) {
1013 sbuf_cat(sfbf, ", ");
1014 }
1015 vp += base_size;
1016 }
1017 sbuf_cat(sfbf, "}");
1018 sbuf_free(sout2);
1019 return sbuf_len(sfbf);
1020 }
1021
1022 /*
1023 * Print a number of char values as a text string.
1024 */
1025 static int
chars_tostring(safebuf_t * sbuf,size_t len,const char * vals)1026 chars_tostring(
1027 safebuf_t *sbuf, /* for output */
1028 size_t len, /* number of characters */
1029 const char *vals /* pointer to block of values */
1030 )
1031 {
1032 long iel;
1033 const char *sp;
1034 char *sout = (char *)emalloc(4*len + 5); /* max len of string */
1035 char *cp = sout;
1036 *cp++ = '"';
1037
1038 /* adjust len so trailing nulls don't get printed */
1039 sp = vals + len;
1040 while (len != 0 && *--sp == '\0')
1041 len--;
1042 for (iel = 0; iel < len; iel++) {
1043 unsigned char uc;
1044 switch (uc = *vals++ & 0377) {
1045 case '\b':
1046 case '\f':
1047 case '\n':
1048 case '\r':
1049 case '\t':
1050 case '\v':
1051 case '\\':
1052 case '\'':
1053 case '\"':
1054 *cp++ = '\\';
1055 *cp++ = *(char *)&uc; /* just copy, even if char is signed */
1056 break;
1057 default:
1058 if (isprint(uc))
1059 *cp++ = *(char *)&uc; /* just copy, even if char is signed */
1060 else {
1061 sprintf(cp,"\\%.3o",uc);
1062 cp += 4;
1063 }
1064 break;
1065 }
1066 }
1067 *cp++ = '"';
1068 *cp = '\0';
1069 sbuf_cpy(sbuf, sout);
1070 free(sout);
1071 return sbuf_len(sbuf);
1072 }
1073
1074
1075 /* Convert a compound value to a string, by using tostring function for
1076 each member field */
1077 int
nccomp_typ_tostring(const nctype_t * tinfo,safebuf_t * sfbf,const void * valp)1078 nccomp_typ_tostring(const nctype_t *tinfo, safebuf_t *sfbf, const void *valp) {
1079 int nfields = tinfo->nfields;
1080 int fidx; /* field id */
1081 safebuf_t* sout2 = sbuf_new();
1082
1083 sbuf_cpy(sfbf, "{");
1084 /* put each val in sout2, then append sout2 to sfbf if enough room */
1085 for (fidx = 0; fidx < nfields; fidx++) {
1086 size_t offset = tinfo->offsets[fidx];
1087 nc_type fid = tinfo->fids[fidx]; /* field type id */
1088 nctype_t *finfo = get_typeinfo(fid);
1089
1090 if(tinfo->ranks[fidx] == 0) {
1091 if(finfo->tid == NC_CHAR) { /* aggregate char rows into strings */
1092 chars_tostring(sout2, 1, ((char *)valp + offset));
1093 } else {
1094 finfo->typ_tostring(finfo, sout2, ((char *)valp + offset));
1095 }
1096 } else { /* this field is an array */
1097 int i; /* array element counter when rank > 0 */
1098 void *vp = (char *)valp + offset;
1099 safebuf_t *sout3 = sbuf_new();
1100 sbuf_cpy(sout2, "{");
1101 if(finfo->tid == NC_CHAR) { /* aggregate char rows into strings */
1102 int rank = tinfo->ranks[fidx];
1103 size_t nstrings;
1104 size_t slen;
1105 int j;
1106 slen = tinfo->sides[fidx][rank-1];
1107 nstrings = 1; /* product of all but last array dimension */
1108 for(j=0; j < rank-1; j++) {
1109 nstrings *= tinfo->sides[fidx][j];
1110 }
1111 for(i=0; i < nstrings; i++) { /* loop on product of all but
1112 last index of array */
1113 chars_tostring(sout3, slen, (char *)vp);
1114 vp = (char *)vp + slen;
1115 if(i < nstrings - 1) {
1116 sbuf_cat(sout3, ", ");
1117 }
1118 sbuf_catb(sout2, sout3);
1119 }
1120 } else {
1121 for(i = 0; i < tinfo->nvals[fidx]; i++) {
1122 (void) finfo->typ_tostring(finfo, sout3, vp);
1123 vp = (char *)vp + finfo->size;
1124 if(i < tinfo->nvals[fidx] - 1) {
1125 sbuf_cat(sout3, ", ");
1126 }
1127 sbuf_catb(sout2, sout3);
1128 }
1129 }
1130 sbuf_cat(sout2, "}");
1131 sbuf_free(sout3);
1132 }
1133 sbuf_catb(sfbf, sout2);
1134 if(fidx < nfields - 1) {
1135 sbuf_cat(sfbf, ", ");
1136 }
1137 }
1138 sbuf_cat(sfbf, "}");
1139 sbuf_free(sout2);
1140 return sbuf_len(sfbf);
1141 }
1142 #endif /* USE_NETCDF4 */
1143
1144 int
ncbyte_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1145 ncbyte_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1146 char sout[PRIM_LEN];
1147 int res;
1148 res = snprintf(sout, PRIM_LEN, varp->fmt, *(signed char *)valp);
1149 assert(res < PRIM_LEN);
1150 sbuf_cpy(sfbf, sout);
1151 return sbuf_len(sfbf);
1152 }
1153
1154 int
ncchar_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1155 ncchar_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1156 char sout[PRIM_LEN];
1157 int res;
1158 res = snprintf(sout, PRIM_LEN, varp->fmt, *(char *)valp);
1159 assert(res < PRIM_LEN);
1160 sbuf_cpy(sfbf, sout);
1161 return sbuf_len(sfbf);
1162 }
1163
1164 int
ncshort_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1165 ncshort_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1166 char sout[PRIM_LEN];
1167 int res;
1168 res = snprintf(sout, PRIM_LEN, varp->fmt, *(short *)valp);
1169 assert(res < PRIM_LEN);
1170 sbuf_cpy(sfbf, sout);
1171 return sbuf_len(sfbf);
1172 }
1173
1174 int
ncint_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1175 ncint_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1176 char sout[PRIM_LEN];
1177 int res;
1178 res = snprintf(sout, PRIM_LEN, varp->fmt, *(int *)valp);
1179 assert(res < PRIM_LEN);
1180 sbuf_cpy(sfbf, sout);
1181 return sbuf_len(sfbf);
1182 }
1183
1184 int
ncfloat_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1185 ncfloat_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1186 char sout[PRIM_LEN];
1187 float vv = *(float *)valp;
1188 if(isfinite(vv)) {
1189 int res;
1190 res = snprintf(sout, PRIM_LEN, varp->fmt, vv);
1191 assert(res < PRIM_LEN);
1192 } else {
1193 float_special_tostring(vv, sout);
1194 }
1195 sbuf_cpy(sfbf, sout);
1196 return sbuf_len(sfbf);
1197 }
1198
1199 int
ncdouble_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1200 ncdouble_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1201 char sout[PRIM_LEN];
1202 double vv = *(double *)valp;
1203 if(isfinite(vv)) {
1204 int res;
1205 res = snprintf(sout, PRIM_LEN, varp->fmt, vv);
1206 assert(res < PRIM_LEN);
1207 } else {
1208 double_special_tostring(vv, sout);
1209 }
1210 sbuf_cpy(sfbf, sout);
1211 return sbuf_len(sfbf);
1212 }
1213
1214 /* Convert value of any numeric type to a double. Beware, this may
1215 * lose precision for values of type NC_INT64 or NC_UINT64 */
1216 static
to_double(const ncvar_t * varp,const void * valp)1217 double to_double(const ncvar_t *varp, const void *valp) {
1218 double dd = 0.0;
1219 switch (varp->type) {
1220 case NC_BYTE:
1221 dd = *(signed char *)valp;
1222 break;
1223 case NC_SHORT:
1224 dd = *(short *)valp;
1225 break;
1226 case NC_INT:
1227 dd = *(int *)valp;
1228 break;
1229 case NC_FLOAT:
1230 dd = *(float *)valp;
1231 break;
1232 case NC_DOUBLE:
1233 dd = *(double *)valp;
1234 break;
1235 case NC_UBYTE:
1236 dd = *(unsigned char *)valp;
1237 break;
1238 case NC_USHORT:
1239 dd = *(unsigned short *)valp;
1240 break;
1241 case NC_UINT:
1242 dd = *(unsigned int *)valp;
1243 break;
1244 case NC_INT64:
1245 dd = *(long long *)valp;
1246 break;
1247 case NC_UINT64:
1248 dd = *(unsigned long long *)valp;
1249 break;
1250 default:
1251 error("to_double: type not numeric primitive");
1252 }
1253 return dd;
1254 }
1255
1256 int
nctime_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1257 nctime_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1258 char sout[PRIM_LEN];
1259 double vv = to_double(varp, valp);
1260 int separator = formatting_specs.iso_separator ? 'T' : ' ';
1261 if(isfinite(vv)) {
1262 int oldopts = 0;
1263 int newopts = 0;
1264 int res;
1265 sout[0]='"';
1266 /* Make nctime dump error messages */
1267 oldopts = cdSetErrOpts(0);
1268 newopts = oldopts | CU_VERBOSE;
1269 cdSetErrOpts(newopts);
1270 cdRel2Iso(varp->timeinfo->calendar, varp->timeinfo->units, separator, vv, &sout[1]);
1271 cdSetErrOpts(oldopts);
1272 res = strlen(sout);
1273 sout[res++] = '"';
1274 sout[res] = '\0';
1275 assert(res < PRIM_LEN);
1276 } else {
1277 double_special_tostring(vv, sout);
1278 }
1279 sbuf_cpy(sfbf, sout);
1280 return sbuf_len(sfbf);
1281 }
1282
1283 int
ncubyte_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1284 ncubyte_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1285 char sout[PRIM_LEN];
1286 int res;
1287 res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned char *)valp);
1288 assert(res < PRIM_LEN);
1289 sbuf_cpy(sfbf, sout);
1290 return sbuf_len(sfbf);
1291 }
1292
1293 int
ncushort_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1294 ncushort_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1295 char sout[PRIM_LEN];
1296 int res;
1297 res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned short *)valp);
1298 assert(res < PRIM_LEN);
1299 sbuf_cpy(sfbf, sout);
1300 return sbuf_len(sfbf);
1301 }
1302
1303 int
ncuint_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1304 ncuint_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1305 char sout[PRIM_LEN];
1306 int res;
1307 res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned int *)valp);
1308 assert(res < PRIM_LEN);
1309 sbuf_cpy(sfbf, sout);
1310 return sbuf_len(sfbf);
1311 }
1312
1313 int
ncint64_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1314 ncint64_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1315 char sout[PRIM_LEN];
1316 int res;
1317 res = snprintf(sout, PRIM_LEN, varp->fmt, *(long long *)valp);
1318 assert(res < PRIM_LEN);
1319 sbuf_cpy(sfbf, sout);
1320 return sbuf_len(sfbf);
1321 }
1322
1323 int
ncuint64_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1324 ncuint64_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1325 char sout[PRIM_LEN];
1326 int res;
1327 res = snprintf(sout, PRIM_LEN, varp->fmt, *(unsigned long long *)valp);
1328 assert(res < PRIM_LEN);
1329 sbuf_cpy(sfbf, sout);
1330 return sbuf_len(sfbf);
1331 }
1332
1333 int
ncstring_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1334 ncstring_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1335 return ncstring_typ_tostring(varp->tinfo, sfbf, valp);
1336 }
1337
1338 #ifdef USE_NETCDF4
1339 int
ncenum_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1340 ncenum_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1341 return ncenum_typ_tostring(varp->tinfo, sfbf, valp);
1342 }
1343
1344 /* Convert an opaque value to a string, represented as hexadecimal
1345 * characters */
1346 int
ncopaque_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1347 ncopaque_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1348 return ncopaque_typ_tostring(varp->tinfo, sfbf, valp);
1349 }
1350
1351 /* Convert a vlen value to a string, by using tostring function for base type */
1352 int
ncvlen_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1353 ncvlen_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1354 return ncvlen_typ_tostring(varp->tinfo, sfbf, valp);
1355 }
1356
1357 int
nccomp_val_tostring(const ncvar_t * varp,safebuf_t * sfbf,const void * valp)1358 nccomp_val_tostring(const ncvar_t *varp, safebuf_t *sfbf, const void *valp) {
1359 return nccomp_typ_tostring(varp->tinfo, sfbf, valp);
1360 }
1361 #endif /*USE_NETCDF4*/
1362
1363 static val_equals_func eq_funcs[] = {
1364 ncbyte_val_equals,
1365 ncchar_val_equals,
1366 ncshort_val_equals,
1367 ncint_val_equals,
1368 ncfloat_val_equals,
1369 ncdouble_val_equals,
1370 ncubyte_val_equals,
1371 ncushort_val_equals,
1372 ncuint_val_equals,
1373 ncint64_val_equals,
1374 ncuint64_val_equals,
1375 ncstring_val_equals
1376 };
1377
1378 static typ_tostring_func ts_funcs[] = {
1379 ncbyte_typ_tostring,
1380 ncchar_typ_tostring,
1381 ncshort_typ_tostring,
1382 ncint_typ_tostring,
1383 ncfloat_typ_tostring,
1384 ncdouble_typ_tostring,
1385 ncubyte_typ_tostring,
1386 ncushort_typ_tostring,
1387 ncuint_typ_tostring,
1388 ncint64_typ_tostring,
1389 ncuint64_typ_tostring,
1390 ncstring_typ_tostring
1391 };
1392
1393
1394 /* Set function pointer of function to convert a value to a string for
1395 * the variable pointed to by varp. */
1396 void
set_tostring_func(ncvar_t * varp)1397 set_tostring_func(ncvar_t *varp) {
1398 val_tostring_func tostring_funcs[] = {
1399 ncbyte_val_tostring,
1400 ncchar_val_tostring,
1401 ncshort_val_tostring,
1402 ncint_val_tostring,
1403 ncfloat_val_tostring,
1404 ncdouble_val_tostring,
1405 ncubyte_val_tostring,
1406 ncushort_val_tostring,
1407 ncuint_val_tostring,
1408 ncint64_val_tostring,
1409 ncuint64_val_tostring,
1410 ncstring_val_tostring
1411 };
1412 if(varp->has_timeval && formatting_specs.string_times) {
1413 varp->val_tostring = (val_tostring_func) nctime_val_tostring;
1414 return;
1415 }
1416 if( !is_user_defined_type(varp->type) ) {
1417 varp->val_tostring = tostring_funcs[varp->type - 1];
1418 return;
1419 }
1420 #ifdef USE_NETCDF4
1421 switch(varp->tinfo->class) {
1422 case NC_VLEN:
1423 varp->val_tostring = (val_tostring_func) ncvlen_val_tostring;
1424 break;
1425 case NC_OPAQUE:
1426 varp->val_tostring = (val_tostring_func) ncopaque_val_tostring;
1427 break;
1428 case NC_ENUM:
1429 varp->val_tostring = (val_tostring_func) ncenum_val_tostring;
1430 break;
1431 case NC_COMPOUND:
1432 varp->val_tostring = (val_tostring_func) nccomp_val_tostring;
1433 break;
1434 default:
1435 error("unrecognized class of user defined type: %d",
1436 varp->tinfo->class);
1437 }
1438 #endif /* USE_NETCDF4 */
1439 return;
1440 }
1441
1442
1443 /* Initialize typelist with primitive types. For netCDF-3 only need primitive
1444 types. */
1445 static void
init_prim_types(int ncid)1446 init_prim_types(int ncid) {
1447 nctype_t *tp;
1448 int i;
1449 int types[] =
1450 {
1451 NC_BYTE,
1452 NC_CHAR,
1453 NC_SHORT,
1454 NC_INT,
1455 NC_FLOAT,
1456 NC_DOUBLE,
1457 NC_UBYTE,
1458 NC_USHORT,
1459 NC_UINT,
1460 NC_INT64,
1461 NC_UINT64,
1462 NC_STRING
1463 };
1464 size_t sizes[] = {
1465 sizeof(char),
1466 sizeof(char),
1467 sizeof(short),
1468 sizeof(int),
1469 sizeof(float),
1470 sizeof(double),
1471 sizeof(unsigned char),
1472 sizeof(unsigned short),
1473 sizeof(unsigned int),
1474 sizeof(long long),
1475 sizeof(unsigned long long),
1476 sizeof(char **)
1477 };
1478
1479 #if 0
1480 for(i=0; i < sizeof(types)/sizeof(int); i++) {
1481 #else
1482 for(i=0; i < max_atomic_type; i++) {
1483 #endif
1484 tp = (nctype_t *)emalloc(sizeof(nctype_t));
1485 tp->ncid = ncid;
1486 tp->tid = types[i];
1487 tp->name = strdup(prim_type_name(tp->tid));
1488 tp->grps = 0;
1489 tp->class = 0; /* primitive type */
1490 tp->size = sizes[i];
1491 tp->base_tid = NC_NAT; /* not used for primitive types */
1492 tp->nfields = 0; /* not used for primitive types */
1493 tp->fmt = get_default_fmt(types[i]);
1494 tp->fids = 0; /* not used for primitive types */
1495 tp->offsets = 0; /* not used for primitive types */
1496 tp->ranks = 0; /* not used for primitive types */
1497 tp->sides = 0; /* not used for primitive types */
1498 tp->nvals = 0; /* not used for primitive types */
1499 tp->val_equals = (val_equals_func) eq_funcs[i];
1500 tp->typ_tostring = (typ_tostring_func) ts_funcs[i];
1501 typeadd(tp);
1502 }
1503 }
1504
1505 /* Initialize typelist.
1506 *
1507 * This must be done over all groups in netCDF-4, because
1508 * variables in one group may be declared using types in a
1509 * different group. For netCDF-3, this is just the info about
1510 * primitive types.
1511 */
1512 void
1513 init_types(int ncid) {
1514 #ifdef USE_NETCDF4
1515 int ntypes;
1516 #endif
1517 if (max_type == 0) { /* if called for first time */
1518 int maxtype = max_typeid(ncid);
1519 int i;
1520 nctypes = (nctype_t **) emalloc((maxtype + 2) * sizeof(nctype_t *));
1521 for(i=0; i < maxtype+1; i++)
1522 nctypes[i] = NULL; /* so can later skip over unused type slots */
1523 init_prim_types(ncid);
1524 }
1525
1526 #ifdef USE_NETCDF4
1527 /* Are there any user defined types in this group? */
1528 NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) );
1529 if (ntypes)
1530 {
1531 int t;
1532 int *typeids = emalloc((ntypes + 1) * sizeof(int));
1533 NC_CHECK( nc_inq_typeids(ncid, NULL, typeids) );
1534 for (t = 0; t < ntypes; t++) {
1535 nctype_t *tinfo; /* details about the type */
1536 char type_name[NC_MAX_NAME + 1];
1537 size_t group_name_len;
1538 char* group_name;
1539 int fidx; /* for compound type, field index */
1540
1541 tinfo = (nctype_t *) emalloc(sizeof(nctype_t));
1542
1543 NC_CHECK( nc_inq_user_type(ncid, typeids[t], type_name, &tinfo->size,
1544 &tinfo->base_tid, &tinfo->nfields,
1545 &tinfo->class) );
1546 tinfo->tid = typeids[t];
1547 tinfo->ncid = ncid;
1548 tinfo->name = strdup(type_name);
1549 tinfo->grps = 0;
1550 if(tinfo->class == NC_VLEN) {
1551 tinfo->size = sizeof(nc_vlen_t); /* not size of base type */
1552 }
1553 NC_CHECK( nc_inq_grpname_full(ncid, &group_name_len, NULL) );
1554 group_name = (char *) emalloc(group_name_len + 1);
1555 NC_CHECK( nc_inq_grpname_full(ncid, &group_name_len, group_name) );
1556
1557 tinfo->grps = strdup(group_name);
1558 free(group_name);
1559 switch(tinfo->class) {
1560 case NC_ENUM:
1561 tinfo->val_equals = eq_funcs[tinfo->base_tid-1];
1562 tinfo->typ_tostring = (typ_tostring_func) ncenum_typ_tostring;
1563 break;
1564 case NC_COMPOUND:
1565 tinfo->val_equals = (val_equals_func) nccomp_val_equals;
1566 tinfo->typ_tostring = (typ_tostring_func) nccomp_typ_tostring;
1567 tinfo->fids = (nc_type *) emalloc((tinfo->nfields + 1)
1568 * sizeof(nc_type));
1569 tinfo->offsets = (size_t *) emalloc((tinfo->nfields + 1)
1570 * sizeof(size_t));
1571 tinfo->ranks = (int *) emalloc((tinfo->nfields + 1)
1572 * sizeof(int));
1573 tinfo->sides = (int **) emalloc((tinfo->nfields + 1)
1574 * sizeof(int *));
1575 tinfo->nvals = (int *) emalloc((tinfo->nfields + 1)
1576 * sizeof(int));
1577 for (fidx = 0; fidx < tinfo->nfields; fidx++) {
1578 size_t offset;
1579 nc_type ftype;
1580 int rank;
1581 int *sides;
1582 int i;
1583 sides = NULL;
1584 NC_CHECK( nc_inq_compound_field(ncid, tinfo->tid, fidx, NULL,
1585 &offset, &ftype, &rank,
1586 sides) );
1587 if(rank > 0) sides = (int *) emalloc(rank * sizeof(int));
1588 NC_CHECK( nc_inq_compound_field(ncid, tinfo->tid, fidx, NULL,
1589 NULL, NULL, NULL, sides) );
1590 tinfo->fids[fidx] = ftype;
1591 tinfo->offsets[fidx] = offset;
1592 tinfo->ranks[fidx] = rank;
1593 if (rank > 0)
1594 tinfo->sides[fidx] = (int *) emalloc(rank * sizeof(int));
1595 tinfo->nvals[fidx] = 1;
1596 for(i = 0; i < rank; i++) {
1597 tinfo->sides[fidx][i] = sides[i];
1598 tinfo->nvals[fidx] *= sides[i];
1599 }
1600 if (rank > 0)
1601 free(sides);
1602 }
1603 break;
1604 case NC_VLEN:
1605 tinfo->val_equals = (val_equals_func) ncvlen_val_equals;
1606 tinfo->typ_tostring = (typ_tostring_func) ncvlen_typ_tostring;
1607 break;
1608 case NC_OPAQUE:
1609 tinfo->val_equals = (val_equals_func) ncopaque_val_equals;
1610 tinfo->typ_tostring = (typ_tostring_func) ncopaque_typ_tostring;
1611 break;
1612 default:
1613 error("bad class: %d", tinfo->class);
1614 break;
1615 }
1616
1617 typeadd(tinfo);
1618 }
1619 free(typeids);
1620 }
1621 /* For netCDF-4, check to see if this group has any subgroups and call
1622 * recursively on each of them. */
1623 {
1624 int g, numgrps, *ncids;
1625
1626 /* See how many groups there are. */
1627 NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) );
1628 if (numgrps > 0) {
1629 ncids = (int *) emalloc(numgrps * sizeof(int));
1630 /* Get the list of group ids. */
1631 NC_CHECK( nc_inq_grps(ncid, NULL, ncids) );
1632 /* Call this function for each group. */
1633 for (g = 0; g < numgrps; g++) {
1634 init_types(ncids[g]);
1635 }
1636 free(ncids);
1637 }
1638 }
1639 #endif /* USE_NETCDF4 */
1640 }
1641
1642
1643 /*
1644 * return 1 if varid identifies a coordinate variable
1645 * else return 0
1646 */
1647 int
1648 iscoordvar(int ncid, int varid)
1649 {
1650 int ndims, ndims1;
1651 int dimid;
1652 int* dimids = 0;
1653 ncdim_t *dims = 0;
1654 #ifdef USE_NETCDF4
1655 int include_parents = 1;
1656 #endif
1657 int is_coord = 0; /* true if variable is a coordinate variable */
1658 char varname[NC_MAX_NAME];
1659 int varndims;
1660
1661 do { /* be safe in case someone is currently adding
1662 * dimensions */
1663 #ifdef USE_NETCDF4
1664 NC_CHECK( nc_inq_dimids(ncid, &ndims, NULL, include_parents ) );
1665 #else
1666 NC_CHECK( nc_inq_ndims(ncid, &ndims) );
1667 #endif
1668 if (dims)
1669 free(dims);
1670 dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
1671 if (dimids)
1672 free(dimids);
1673 dimids = (int *) emalloc((ndims + 1) * sizeof(int));
1674 #ifdef USE_NETCDF4
1675 NC_CHECK( nc_inq_dimids(ncid, &ndims1, dimids, include_parents ) );
1676 #else
1677 {
1678 int i;
1679 for(i = 0; i < ndims; i++) {
1680 dimids[i] = i; /* for netCDF-3, dimids are 0, 1, ..., ndims-1 */
1681 }
1682 NC_CHECK( nc_inq_ndims(ncid, &ndims1) );
1683 }
1684 #endif /* USE_NETCDF4 */
1685 } while (ndims != ndims1);
1686
1687 for (dimid = 0; dimid < ndims; dimid++) {
1688 NC_CHECK( nc_inq_dimname(ncid, dimids[dimid], dims[dimid].name) );
1689 }
1690 NC_CHECK( nc_inq_varname(ncid, varid, varname) );
1691 NC_CHECK( nc_inq_varndims(ncid, varid, &varndims) );
1692
1693 for (dimid = 0; dimid < ndims; dimid++) {
1694 if (strcmp(dims[dimid].name, varname) == 0 && varndims == 1) {
1695 is_coord = 1;
1696 break;
1697 }
1698 }
1699 if(dims)
1700 free(dims);
1701 if(dimids)
1702 free(dimids);
1703 return is_coord;
1704 }
1705
1706
1707 /* Return true if user-defined type */
1708 int
1709 is_user_defined_type(nc_type type) {
1710 nctype_t *typeinfop = get_typeinfo(type);
1711 return (typeinfop->class > 0);
1712 }
1713
1714
1715 /*
1716 * Return name of type in user-allocated space, whether built-in
1717 * primitive type or user-defined type. Note: name must have enough
1718 * space allocated to hold type name.
1719 */
1720 void
1721 get_type_name(int ncid, nc_type type, char *name)
1722 {
1723 #ifdef USE_NETCDF4
1724 if (is_user_defined_type(type)) {
1725 NC_CHECK(nc_inq_user_type(ncid, type, name, NULL, NULL, NULL, NULL));
1726 } else {
1727 strncpy(name, prim_type_name(type), NC_MAX_NAME + 1);
1728 }
1729 #else
1730 strncpy(name, prim_type_name(type), NC_MAX_NAME + 1);
1731 #endif /* USE_NETCDF4 */
1732 }
1733
1734 /*
1735 * Print type name with CDL escapes for special characters. locid is
1736 * the id of the group in which the type is referenced, which is
1737 * needed to determine whether an absolute type name must be printed.
1738 * If the type is defined in the referenced group or in some ancestor
1739 * group, only the simple type name is printed. If the type is
1740 * defined in some other non-ancestor group, an absolute path for the
1741 * typename is printed instead.
1742 */
1743 void
1744 print_type_name(int locid, int typeid) {
1745 char *ename;
1746 #ifdef USE_NETCDF4
1747 char name[NC_MAX_NAME+1];
1748 int type_inherited = 0;
1749 int curlocid; /* group we are searching in */
1750 int parent_groupid = locid;
1751 int ntypes;
1752 int stat;
1753 #endif
1754
1755 assert(typeid > 0 && typeid <= max_type);
1756 ename = escaped_name(nctypes[typeid]->name);
1757 #ifdef USE_NETCDF4
1758 if(is_user_defined_type(typeid)) {
1759 /* determine if type is inherited, that is if defined in this
1760 * group or any ancestor group */
1761 name[NC_MAX_NAME] = '\0';
1762 strncpy(name,nctypes[typeid]->name,NC_MAX_NAME);
1763 do {
1764 curlocid = parent_groupid;
1765 NC_CHECK( nc_inq_typeids(curlocid, &ntypes, NULL) );
1766 if(ntypes > 0) {
1767 int *typeids = (int *) emalloc((ntypes + 1) * sizeof(int));
1768 int i;
1769 NC_CHECK( nc_inq_typeids(curlocid, &ntypes, typeids) );
1770 for(i = 0; i < ntypes; i++) {
1771 char curname[NC_MAX_NAME];
1772 NC_CHECK( nc_inq_type(curlocid, typeids[i], curname, NULL) );
1773 if(strncmp(name, curname, NC_MAX_NAME) == 0) {
1774 type_inherited = 1;
1775 break;
1776 }
1777 }
1778 free(typeids);
1779 if(type_inherited)
1780 break;
1781 }
1782 stat = nc_inq_grp_parent(curlocid, &parent_groupid);
1783 } while (stat != NC_ENOGRP && stat != NC_ENOTNC4);
1784
1785 if (type_inherited == 0) {
1786 char *gname = nctypes[typeid]->grps;
1787 print_name(gname);
1788 fputs("/", stdout);
1789 }
1790 }
1791 #endif /* USE_NETCDF4 */
1792 fputs(ename, stdout);
1793 free(ename);
1794 }
1795
1796 /* Allocate and initialize table of unlimited dimensions for ncid, for
1797 * use by is_unlim_dim() function. If ncid is a subgroup of a netCDF
1798 * dataset, the table will still be initialized for the whole dataset
1799 * in which the subgroup resides. */
1800 #ifdef USE_NETCDF4
1801 static int
1802 init_is_unlim(int ncid, int **is_unlim_p)
1803 {
1804 int num_grps; /* total number of groups */
1805 int num_dims = 0; /* total number of dimensions in all groups */
1806 int num_undims = 0; /* total number of unlimited dimensions in all groups */
1807 int *grpids = NULL; /* temporary list of all grpids */
1808 int igrp;
1809 int grpid;
1810
1811 /* if ncid is not root group, find its ancestor root group id */
1812 int status = nc_inq_grp_parent(ncid, &grpid);
1813 while(status == NC_NOERR && grpid != ncid) {
1814 ncid = grpid;
1815 status = nc_inq_grp_parent(ncid, &grpid);
1816 }
1817 if (status != NC_ENOGRP)
1818 return NC_EBADGRPID;
1819 /* Now ncid is root group. Get total number of groups and their ids */
1820 NC_CHECK( nc_inq_grps_full(ncid, &num_grps, NULL) );
1821 grpids = emalloc((num_grps + 1) * sizeof(int));
1822 NC_CHECK( nc_inq_grps_full(ncid, &num_grps, grpids) );
1823 #define DONT_INCLUDE_PARENTS 0
1824 /* Get all dimensions in groups and info about which ones are unlimited */
1825 for(igrp = 0; igrp < num_grps; igrp++) {
1826 int ndims;
1827 grpid = grpids[igrp];
1828 NC_CHECK( nc_inq_dimids(grpid, &ndims, NULL, DONT_INCLUDE_PARENTS) );
1829 num_dims += ndims;
1830 }
1831 *is_unlim_p = emalloc((num_dims + 1) * sizeof(int));
1832 for(igrp = 0; igrp < num_grps; igrp++) {
1833 int ndims, idim, *dimids, nundims;
1834 grpid = grpids[igrp];
1835 NC_CHECK( nc_inq_dimids(grpid, &ndims, NULL, DONT_INCLUDE_PARENTS) );
1836 dimids = emalloc((ndims + 1) * sizeof(int));
1837 NC_CHECK( nc_inq_dimids(grpid, &ndims, dimids, DONT_INCLUDE_PARENTS) );
1838 /* mark all dims in this group as fixed-size */
1839 for(idim = 0; idim < ndims; idim++) {
1840 (*is_unlim_p)[dimids[idim]] = 0;
1841 }
1842 NC_CHECK( nc_inq_unlimdims(grpid, &nundims, dimids) );
1843 assert(nundims <= ndims);
1844 /* mark the subset of dims in this group that are unlimited */
1845 for(idim = 0; idim < nundims; idim++) {
1846 (*is_unlim_p)[dimids[idim]] = 1;
1847 num_undims++;
1848 }
1849 if(dimids)
1850 free(dimids);
1851 }
1852 free(grpids);
1853 return NC_NOERR;
1854 }
1855 #endif /* USE_NETCDF4 */
1856
1857 /* TODO: make list of these arrays for multiple open datasets, such as
1858 * the idnode_t lists above. For now, we just have one of these, for
1859 * the unique input dataset for this invocation of ncdump. */
1860
1861 #define UNLIM_NOT_INITIALIZED (-1)
1862
1863 /* Is dimid the dimension ID of an unlimited dimension? */
1864 bool_t
1865 is_unlim_dim(int ncid, int dimid) {
1866 bool_t result; /* 0 if fixed, 1 if unlimited size */
1867 static int for_ncid = UNLIM_NOT_INITIALIZED; /* ensure only ever called for one ncid */
1868 #ifdef USE_NETCDF4
1869 static int *is_unlim = NULL; /* gets allocated by init_is_unlim() */
1870 if(for_ncid == UNLIM_NOT_INITIALIZED) {
1871 NC_CHECK(init_is_unlim(ncid, &is_unlim));
1872 for_ncid = ncid;
1873 }
1874 assert(is_unlim);
1875 result = is_unlim[dimid]; /* 0 if fixed, 1 if unlimited size */
1876 #else
1877 static int unlimdimid;
1878 if(for_ncid == UNLIM_NOT_INITIALIZED) {
1879 NC_CHECK( nc_inq_unlimdim(ncid, &unlimdimid) );
1880 for_ncid = ncid;
1881 }
1882 result = (dimid == unlimdimid) ;
1883 #endif /* USE_NETCDF4 */
1884 return result;
1885 }
1886