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


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed