1 /*
2 
3 Copyright 2011 University Corporation for Atmospheric
4 Research/Unidata. See \ref copyright file for more info.  */
5 
6 #include "config.h"
7 #include <stdio.h>
8 #ifdef HAVE_GETOPT_H
9 #include <getopt.h>
10 #endif
11 #ifdef _MSC_VER /* Microsoft Compilers */
12 #include <io.h>
13 #endif
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #ifdef HAVE_FCNTL_H
18 #include <fcntl.h>
19 #endif
20 
21 #ifdef _MSC_VER
22 #define snprintf _snprintf
23 #include "XGetopt.h"
24 int opterr;
25 int optind;
26 #endif
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <assert.h>
32 #include <math.h>
33 #ifdef HAVE_LOCALE_H
34 #include <locale.h>
35 #endif	/* HAVE_LOCALE_H */
36 
37 #include "netcdf.h"
38 #include "netcdf_mem.h"
39 #include "netcdf_aux.h"
40 #include "utils.h"
41 #include "nccomps.h"
42 #include "nctime0.h"		/* new iso time and calendar stuff */
43 #include "dumplib.h"
44 #include "ncdump.h"
45 #include "vardata.h"
46 #include "indent.h"
47 #include "isnan.h"
48 #include "cdl.h"
49 #include "nclog.h"
50 #include "ncwinpath.h"
51 #include "netcdf_aux.h"
52 
53 #ifdef USE_NETCDF4
54 #include "nc4internal.h" /* to get name of the special properties file */
55 #endif
56 
57 #ifdef USE_DAP
58 extern int nc__testurl(const char*,char**);
59 #endif
60 
61 #define XML_VERSION "1.0"
62 
63 #define int64_t long long
64 #define uint64_t unsigned long long
65 
66 /* If we have a variable named one of these:
67    we need to be careful about printing their attributes.
68 */
69 static const char* keywords[] = {
70 "variable",
71 "dimension",
72 "data",
73 "group",
74 "types",
75 NULL
76 };
77 
iskeyword(const char * kw)78 static int iskeyword(const char* kw)
79 {
80     const char** p;
81     for(p=keywords;*p;p++) {
82 	if(strcmp(kw,*p)==0) return 1;
83     }
84     return 0;
85 }
86 
87 /* globals */
88 char *progname;
89 fspec_t formatting_specs =	/* defaults, overridden by command-line options */
90 {
91     0,			/* construct netcdf name from file name */
92     false,		/* print header info only, no data? */
93     false,		/* just print coord vars? */
94     false,		/* brief  comments in data section? */
95     false,		/* full annotations in data section?  */
96     false,		/* human-readable output for date-time values? */
97     false,		/* use 'T' separator between date and time values as strings? */
98     false,		/* output special attributes, eg chunking? */
99     LANG_C,		/* language conventions for indices */
100     false,	        /* for DAP URLs, client-side cache used */
101     0,			/* if -v specified, number of variables in list */
102     0,			/* if -v specified, list of variable names */
103     0,			/* if -g specified, number of groups names in list */
104     0,			/* if -g specified, list of group names */
105     0,			/* if -g specified, list of matching grpids */
106     0			/* kind of netCDF file */
107 };
108 
109 static void
usage(void)110 usage(void)
111 {
112 #define USAGE   "\
113   [-c]             Coordinate variable data and header information\n\
114   [-h]             Header information only, no data\n\
115   [-v var1[,...]]  Data for variable(s) <var1>,... only\n\
116   [-b [c|f]]       Brief annotations for C or Fortran indices in data\n\
117   [-f [c|f]]       Full annotations for C or Fortran indices in data\n\
118   [-l len]         Line length maximum in data section (default 80)\n\
119   [-n name]        Name for netCDF (default derived from file name)\n\
120   [-p n[,n]]       Display floating-point values with less precision\n\
121   [-k]             Output kind of netCDF file\n\
122   [-s]             Output special (virtual) attributes\n\
123   [-t]             Output time data as date-time strings\n\
124   [-i]             Output time data as date-time strings with ISO-8601 'T' separator\n\
125   [-g grp1[,...]]  Data and metadata for group(s) <grp1>,... only\n\
126   [-w]             With client-side caching of variables for DAP URLs\n\
127   [-x]             Output XML (NcML) instead of CDL\n\
128   [-Xp]            Unconditionally suppress output of the properties attribute\n\
129   [-Ln]            Set log level to n (>= 0); ignore if logging not enabled.\n\
130   file             Name of netCDF file (or URL if DAP access enabled)\n"
131 
132     (void) fprintf(stderr,
133 		   "%s [-c|-h] [-v ...] [[-b|-f] [c|f]] [-l len] [-n name] [-p n[,n]] [-k] [-x] [-s] [-t|-i] [-g ...] [-w] [-Ln] file\n%s",
134 		   progname,
135 		   USAGE);
136 
137     (void) fprintf(stderr,
138                  "netcdf library version %s\n",
139                  nc_inq_libvers());
140 }
141 
142 
143 /*
144  * convert pathname of netcdf file into name for cdl unit, by taking
145  * last component of path and stripping off any extension.
146  * DMH: add code to handle OPeNDAP url.
147  * DMH: I think this also works for UTF8.
148  */
149 static char *
name_path(const char * path)150 name_path(const char *path)
151 {
152     const char *cp;
153     char *new;
154     char *sp;
155 
156 #ifdef vms
157 #define FILE_DELIMITER ']'
158 #endif
159 #if defined(WIN32) || defined(msdos)
160 #define FILE_DELIMITER '\\'
161 #endif
162 #ifndef FILE_DELIMITER /* default to unix */
163 #define FILE_DELIMITER '/'
164 #endif
165 
166 #ifdef USE_DAP
167     /* See if this is a url */
168     {
169 	char* base;
170         extern int nc__testurl(const char*,char**);
171  	if(nc__testurl(path,&base)) {
172  	    return base; /* Looks like a url */
173 	}
174 	/* else fall thru and treat like a file path */
175     }
176 #endif /*USE_DAP*/
177 
178     cp = strrchr(path, FILE_DELIMITER);
179     if (cp == 0)		/* no delimiter */
180       cp = path;
181     else			/* skip delimiter */
182       cp++;
183     new = (char *) emalloc((unsigned) (strlen(cp)+1));
184     (void) strncpy(new, cp, strlen(cp) + 1);	/* copy last component of path */
185     if ((sp = strrchr(new, '.')) != NULL)
186       *sp = '\0';		/* strip off any extension */
187     return new;
188 }
189 
190 /* Return primitive type name */
191 static const char *
prim_type_name(nc_type type)192 prim_type_name(nc_type type)
193 {
194     switch (type) {
195       case NC_BYTE:
196 	return "byte";
197       case NC_CHAR:
198 	return "char";
199       case NC_SHORT:
200 	return "short";
201       case NC_INT:
202 	return "int";
203       case NC_FLOAT:
204 	return "float";
205       case NC_DOUBLE:
206 	return "double";
207       case NC_UBYTE:
208 	return "ubyte";
209       case NC_USHORT:
210 	return "ushort";
211       case NC_UINT:
212 	return "uint";
213       case NC_INT64:
214 	return "int64";
215       case NC_UINT64:
216 	return "uint64";
217       case NC_STRING:
218 	return "string";
219       case NC_VLEN:
220 	return "vlen";
221       case NC_OPAQUE:
222 	return "opaque";
223       case NC_COMPOUND:
224 	return "compound";
225       default:
226 	error("prim_type_name: bad type %d", type);
227 	return "bogus";
228     }
229 }
230 
231 
232 /*
233  * Remove trailing zeros (after decimal point) but not trailing decimal
234  * point from ss, a string representation of a floating-point number that
235  * might include an exponent part.
236  */
237 static void
tztrim(char * ss)238 tztrim(char *ss)
239 {
240     char *cp, *ep;
241 
242     cp = ss;
243     if (*cp == '-')
244       cp++;
245     while(isdigit((int)*cp) || *cp == '.')
246       cp++;
247     if (*--cp == '.')
248       return;
249     ep = cp+1;
250     while (*cp == '0')
251       cp--;
252     cp++;
253     if (cp == ep)
254       return;
255     while (*ep)
256       *cp++ = *ep++;
257     *cp = '\0';
258     return;
259 }
260 
261 
262 /* Return file type string */
263 static const char *
kind_string(int kind)264 kind_string(int kind)
265 {
266     switch (kind) {
267     case NC_FORMAT_CLASSIC:
268 	return "classic";
269     case NC_FORMAT_64BIT_OFFSET:
270 	return "64-bit offset";
271     case NC_FORMAT_CDF5:
272 	return "cdf5";
273     case NC_FORMAT_NETCDF4:
274 	return "netCDF-4";
275     case NC_FORMAT_NETCDF4_CLASSIC:
276 	return "netCDF-4 classic model";
277     default:
278        error("unrecognized file format: %d", kind);
279 	return "unrecognized";
280     }
281 }
282 
283 
284 /* Return extended format string */
285 static const char *
kind_string_extended(int kind,int mode)286 kind_string_extended(int kind, int mode)
287 {
288     static char text[1024];
289     switch (kind) {
290     case NC_FORMATX_NC3:
291 	if(mode & NC_CDF5)
292 	    snprintf(text,sizeof(text),"%s mode=%08x", "64-bit data",mode);
293 	else if(mode & NC_64BIT_OFFSET)
294 	    snprintf(text,sizeof(text),"%s mode=%08x", "64-bit offset",mode);
295 	else
296 	    snprintf(text,sizeof(text),"%s mode=%08x", "classic",mode);
297 	break;
298     case NC_FORMATX_NC_HDF5:
299 	snprintf(text,sizeof(text),"%s mode=%08x", "HDF5",mode);
300 	break;
301     case NC_FORMATX_NC_HDF4:
302 	snprintf(text,sizeof(text),"%s mode=%08x", "HDF4",mode);
303 	break;
304     case NC_FORMATX_PNETCDF:
305 	snprintf(text,sizeof(text),"%s mode=%08x", "PNETCDF",mode);
306 	break;
307     case NC_FORMATX_DAP2:
308 	snprintf(text,sizeof(text),"%s mode=%08x", "DAP2",mode);
309 	break;
310     case NC_FORMATX_DAP4:
311 	snprintf(text,sizeof(text),"%s mode=%08x", "DAP4",mode);
312 	break;
313     case NC_FORMATX_UNDEFINED:
314 	snprintf(text,sizeof(text),"%s mode=%08x", "unknown",mode);
315 	break;
316     default:
317 	error("unrecognized extended format: %d",kind);
318 	snprintf(text,sizeof(text),"%s mode=%08x", "unrecognized",mode);
319 	break;
320     }
321     return text;
322 }
323 
324 #if 0
325 static int
326 fileopen(const char* path, void** memp, size_t* sizep)
327 {
328     int status = NC_NOERR;
329     int fd = -1;
330     int oflags = 0;
331     off_t size = 0;
332     void* mem = NULL;
333     off_t red = 0;
334     char* pos = NULL;
335 
336     /* Open the file, but make sure we can write it if needed */
337     oflags = O_RDONLY;
338 #ifdef O_BINARY
339     oflags |= O_BINARY;
340 #endif
341     oflags |= O_EXCL;
342 #ifdef vms
343     fd = open(path, oflags, 0, "ctx=stm");
344 #else
345     fd  = NCopen2(path, oflags);
346 #endif
347     if(fd < 0) {
348 	status = errno;
349 	goto done;
350     }
351     /* get current filesize  = max(|file|,initialize)*/
352     size = lseek(fd,0,SEEK_END);
353     if(size < 0) {status = errno; goto done;}
354     /* move pointer back to beginning of file */
355     (void)lseek(fd,0,SEEK_SET);
356     mem = malloc(size);
357     if(mem == NULL) {status = NC_ENOMEM; goto done;}
358     /* Read the file into memory */
359     /* We need to do multiple reads because there is no
360        guarantee that the amount read will be the full amount */
361     red = size;
362     pos = (char*)mem;
363     while(red > 0) {
364 	ssize_t count = read(fd, pos, red);
365 	if(count < 0) {status = errno; goto done;}
366         if(count == 0) {status = NC_ENOTNC; goto done;}
367 	/* assert(count > 0) */
368 	red -= count;
369 	pos += count;
370     }
371 
372 done:
373     if(fd >= 0)
374 	(void)close(fd);
375     if(status != NC_NOERR) {
376 #ifndef DEBUG
377         fprintf(stderr,"open failed: file=%s err=%d\n",path,status);
378 	fflush(stderr);
379 #endif
380     }
381     if(status != NC_NOERR && mem != NULL) {
382       free(mem);
383       mem = NULL;
384     } else {
385       if(sizep) *sizep = size;
386       if(memp) {
387         *memp = mem;
388       } else if(mem) {
389         free(mem);
390       }
391 
392     }
393 
394 
395     return status;
396 }
397 #endif
398 
399 /*
400  * Emit initial line of output for NcML
401  */
402 static void
pr_initx(int ncid,const char * path)403 pr_initx(int ncid, const char *path)
404 {
405     printf("<?xml version=\"%s\" encoding=\"UTF-8\"?>\n<netcdf xmlns=\"http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2\" location=\"%s\">\n",
406 	   XML_VERSION, path);
407 }
408 
409 /*
410  * Print attribute string, for text attributes.
411  */
412 static void
pr_att_string(int kind,size_t len,const char * string)413 pr_att_string(
414     int kind,
415     size_t len,
416     const char *string
417     )
418 {
419     int iel;
420     const char *cp;
421     const char *sp;
422     unsigned char uc;
423 
424     cp = string;
425     printf ("\"");
426     /* adjust len so trailing nulls don't get printed */
427     sp = cp + len - 1;
428     while (len != 0 && *sp-- == '\0')
429 	len--;
430     for (iel = 0; iel < len; iel++)
431 	switch (uc = *cp++ & 0377) {
432 	case '\b':
433 	    printf ("\\b");
434 	    break;
435 	case '\f':
436 	    printf ("\\f");
437 	    break;
438 	case '\n':
439 	    /* Only generate linebreaks after embedded newlines for
440 	     * classic, 64-bit offset, cdf5, or classic model files.  For
441 	     * netCDF-4 files, don't generate linebreaks, because that
442 	     * would create an extra string in a list of strings.  */
443 	    if (kind != NC_FORMAT_NETCDF4) {
444 		printf ("\\n\",\n\t\t\t\"");
445 	    } else {
446 		printf("\\n");
447 	    }
448 	    break;
449 	case '\r':
450 	    printf ("\\r");
451 	    break;
452 	case '\t':
453 	    printf ("\\t");
454 	    break;
455 	case '\v':
456 	    printf ("\\v");
457 	    break;
458 	case '\\':
459 	    printf ("\\\\");
460 	    break;
461 	case '\'':
462 	    printf ("\\\'");
463 	    break;
464 	case '\"':
465 	    printf ("\\\"");
466 	    break;
467 	default:
468 	    if (iscntrl(uc))
469 	        printf ("\\%03o",uc);
470 	    else
471 	        printf ("%c",uc);
472 	    break;
473 	}
474     printf ("\"");
475 
476 }
477 
478 
479 /*
480  * Print NcML attribute string, for text attributes.
481  */
482 static void
pr_attx_string(const char * attname,size_t len,const char * string)483 pr_attx_string(
484      const char* attname,
485      size_t len,
486      const char *string
487      )
488 {
489     int iel;
490     const char *cp;
491     const char *sp;
492     unsigned char uc;
493     int nulcount = 0;
494 
495     cp = string;
496     printf ("\"");
497     /* adjust len so trailing nulls don't get printed */
498     sp = cp + len - 1;
499     while (len != 0 && *sp-- == '\0')
500 	len--;
501     for (iel = 0; iel < len; iel++)
502 	switch (uc = *cp++ & 0377) {
503 	case '\"':
504 	    printf ("&quot;");
505 	    break;
506 	case '<':
507 	    printf ("&lt;");
508 	    break;
509 	case '>':
510 	    printf ("&gt;");
511 	    break;
512 	case '&':
513 	    printf ("&amp;");
514 	    break;
515 	case '\n':
516 	    printf ("&#xA;");
517 	    break;
518 	case '\r':
519 	    printf ("&#xD;");
520 	    break;
521 	case '\t':
522 	    printf ("&#x9;");
523 	    break;
524 	case '\0':
525 	    printf ("&#0;");
526 	    if(nulcount++ == 0)
527 		fprintf(stderr,"Attribute: '%s'; value contains nul characters; producing illegal xml\n",attname);
528 	    break;
529 	default:
530 	    if (iscntrl(uc))
531 	        printf ("&#%d;",uc);
532 	    else
533 	        printf ("%c",uc);
534 	    break;
535 	}
536     printf ("\"");
537 
538 }
539 
540 
541 /*
542  * Print list of attribute values, for attributes of primitive types.
543  * Attribute values must be printed with explicit type tags for
544  * netCDF-3 primitive types, because CDL doesn't require explicit
545  * syntax to declare such attribute types.
546  */
547 static void
pr_att_valgs(int kind,nc_type type,size_t len,const void * vals)548 pr_att_valgs(
549     int kind,
550     nc_type type,
551     size_t len,
552     const void *vals
553     )
554 {
555     int iel;
556     signed char sc;
557     short ss;
558     int ii;
559     char gps[PRIM_LEN];
560     float ff;
561     double dd;
562     unsigned char uc;
563     unsigned short us;
564     unsigned int ui;
565     int64_t i64;
566     uint64_t ui64;
567 #ifdef USE_NETCDF4
568     char *stringp;
569 #endif /* USE_NETCDF4 */
570     char *delim = ", ";	/* delimiter between output values */
571 
572     if (type == NC_CHAR) {
573 	char *cp = (char *) vals;
574 	pr_att_string(kind, len, cp);
575 	return;
576     }
577     /* else */
578     for (iel = 0; iel < len; iel++) {
579 	if (iel == len - 1)
580 	    delim = "";
581 	switch (type) {
582 	case NC_BYTE:
583 	    sc = ((signed char *) vals)[iel];
584 	    printf ("%db%s", sc, delim);
585 	    break;
586 	case NC_SHORT:
587 	    ss = ((short *) vals)[iel];
588 	    printf ("%ds%s", ss, delim);
589 	    break;
590 	case NC_INT:
591 	    ii = ((int *) vals)[iel];
592 	    printf ("%d%s", ii, delim);
593 	    break;
594 	case NC_FLOAT:
595 	    ff = ((float *) vals)[iel];
596 	    if(isfinite(ff)) {
597 		int res;
598 		res = snprintf(gps, PRIM_LEN, float_att_fmt, ff);
599 		assert(res < PRIM_LEN);
600 		tztrim(gps);	/* trim trailing 0's after '.' */
601 		printf ("%s%s", gps, delim);
602 	    } else {
603 		if(isnan(ff)) {
604 		    printf("NaNf%s", delim);
605 		} else if(isinf(ff)) {
606 		    if(ff < 0.0f) {
607 			printf("-");
608 		    }
609 		    printf("Infinityf%s", delim);
610 		}
611 	    }
612 	    break;
613 	case NC_DOUBLE:
614 	    dd = ((double *) vals)[iel];
615 	    if(isfinite(dd)) {
616 		int res;
617 		res = snprintf(gps, PRIM_LEN, double_att_fmt, dd);
618 		assert(res < PRIM_LEN);
619 		tztrim(gps);
620 		printf ("%s%s", gps, delim);
621 	    } else {
622 		if(isnan(dd)) {
623 		    printf("NaN%s", delim);
624 		} else if(isinf(dd)) {
625 		    if(dd < 0.0) {
626 			printf("-");
627 		    }
628 		    printf("Infinity%s", delim);
629 		}
630 	    }
631 	    break;
632 	case NC_UBYTE:
633 	    uc = ((unsigned char *) vals)[iel];
634 	    printf ("%uUB%s", uc, delim);
635 	    break;
636 	case NC_USHORT:
637 	    us = ((unsigned short *) vals)[iel];
638 	    printf ("%huUS%s", us, delim);
639 	    break;
640 	case NC_UINT:
641 	    ui = ((unsigned int *) vals)[iel];
642 	    printf ("%uU%s", ui, delim);
643 	    break;
644 	case NC_INT64:
645 	    i64 = ((int64_t *) vals)[iel];
646 	    printf ("%lldLL%s", i64, delim);
647 	    break;
648 	case NC_UINT64:
649 	    ui64 = ((uint64_t *) vals)[iel];
650 	    printf ("%lluULL%s", ui64, delim);
651 	    break;
652 #ifdef USE_NETCDF4
653 	case NC_STRING:
654 	    stringp = ((char **) vals)[iel];
655             if(stringp)
656                 pr_att_string(kind, strlen(stringp), stringp);
657             else
658 	        printf("NIL");
659 	    printf("%s", delim);
660 	    break;
661 #endif /* USE_NETCDF4 */
662 	default:
663 	    error("pr_att_vals: bad type");
664 	}
665     }
666 }
667 
668 
669 /*
670  * Print list of numeric attribute values to string for use in NcML output.
671  * Unlike CDL, NcML makes type explicit, so don't need type suffixes.
672  */
673 static void
pr_att_valsx(nc_type type,size_t len,const double * vals,char * attvals,size_t attvalslen)674 pr_att_valsx(
675      nc_type type,
676      size_t len,
677      const double *vals,
678      char *attvals,		/* returned string */
679      size_t attvalslen		/* size of attvals buffer, assumed
680 				   large enough to hold all len
681 				   blank-separated values */
682      )
683 {
684     int iel;
685     float ff;
686     double dd;
687     int ii;
688     unsigned int ui;
689     int64_t i64;
690     uint64_t ui64;
691 
692     attvals[0]='\0';
693     if (len == 0)
694 	return;
695     for (iel = 0; iel < len; iel++) {
696 	char gps[PRIM_LEN];
697 	int res;
698 	switch (type) {
699 	case NC_BYTE:
700 	case NC_SHORT:
701 	case NC_INT:
702 	    ii = vals[iel];
703 	    res = snprintf(gps, PRIM_LEN, "%d", ii);
704 	    assert(res < PRIM_LEN);
705 	    (void) strlcat(attvals, gps, attvalslen);
706 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
707 	    break;
708 	case NC_UBYTE:
709 	case NC_USHORT:
710 	case NC_UINT:
711 	    ui = vals[iel];
712 	    res = snprintf(gps, PRIM_LEN, "%u", ui);
713 	    assert(res < PRIM_LEN);
714 	    (void) strlcat(attvals, gps, attvalslen);
715 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
716 	    break;
717 	case NC_INT64:
718 	    i64 = vals[iel];
719 	    res = snprintf(gps, PRIM_LEN, "%lld", i64);
720 	    assert(res < PRIM_LEN);
721 	    (void) strlcat(attvals, gps, attvalslen);
722 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
723 	    break;
724 	case NC_UINT64:
725 	    ui64 = vals[iel];
726 	    res = snprintf(gps, PRIM_LEN, "%llu", ui64);
727 	    assert(res < PRIM_LEN);
728 	    (void) strlcat(attvals, gps, attvalslen);
729 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
730 	    break;
731 	case NC_FLOAT:
732 	    ff = vals[iel];
733 	    res = snprintf(gps, PRIM_LEN, float_attx_fmt, ff);
734 	    assert(res < PRIM_LEN);
735 	    tztrim(gps);	/* trim trailing 0's after '.' */
736 	    (void) strlcat(attvals, gps, attvalslen);
737 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
738 	    break;
739 	case NC_DOUBLE:
740 	    dd = vals[iel];
741 	    res = snprintf(gps, PRIM_LEN, double_att_fmt, dd);
742 	    assert(res < PRIM_LEN);
743 	    tztrim(gps);	/* trim trailing 0's after '.' */
744 	    (void) strlcat(attvals, gps, attvalslen);
745 	    (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen);
746 	    break;
747 	default:
748 	    error("pr_att_valsx: bad type");
749 	}
750     }
751 }
752 
753 /*
754  * Print a variable attribute
755  */
756 static void
pr_att(int ncid,int kind,int varid,const char * varname,int ia)757 pr_att(
758     int ncid,
759     int kind,
760     int varid,
761     const char *varname,
762     int ia
763     )
764 {
765     ncatt_t att;			/* attribute */
766 
767     NC_CHECK( nc_inq_attname(ncid, varid, ia, att.name) );
768 #ifdef USE_NETCDF4
769     if (ncid == getrootid(ncid)
770         && varid == NC_GLOBAL
771         && strcmp(att.name,NCPROPS)==0)
772 	return; /* will be printed elsewere */
773 #endif
774     NC_CHECK( nc_inq_att(ncid, varid, att.name, &att.type, &att.len) );
775     att.tinfo = get_typeinfo(att.type);
776 
777     indent_out();
778     printf ("\t\t");
779 #ifdef USE_NETCDF4
780     if (is_user_defined_type(att.type) || att.type == NC_STRING)
781 #else
782     if (is_user_defined_type(att.type))
783 #endif
784     {
785 	/* TODO: omit next two lines if att_type_name not needed
786 	 * because print_type_name() looks it up */
787 	char att_type_name[NC_MAX_NAME + 1];
788 	get_type_name(ncid, att.type, att_type_name);
789 
790 	/* printf ("\t\t%s ", att_type_name); */
791 	/* ... but handle special characters in CDL names with escapes */
792 	print_type_name(ncid, att.type);
793 	printf(" ");
794     }
795     /* 	printf ("\t\t%s:%s = ", varname, att.name); */
796     print_name(varname);
797     if(iskeyword(varname)) /* see discussion about escapes in ncgen man page*/
798 	printf(" ");
799     printf(":");
800     print_name(att.name);
801     printf(" = ");
802 
803     if (att.len == 0) {	/* show 0-length attributes as empty strings */
804 	att.type = NC_CHAR;
805     }
806 
807     if (! is_user_defined_type(att.type) ) {
808 	att.valgp = (void *) emalloc((att.len + 1) * att.tinfo->size );
809 	NC_CHECK( nc_get_att(ncid, varid, att.name, att.valgp ) );
810 	if(att.type == NC_CHAR)	/* null-terminate retrieved text att value */
811 	    ((char *)att.valgp)[att.len] = '\0';
812 /* (1) Print normal list of attribute values. */
813         pr_att_valgs(kind, att.type, att.len, att.valgp);
814 	printf (" ;");			/* terminator for normal list */
815 /* (2) If -t option, add list of date/time strings as CDL comments. */
816 	if(formatting_specs.string_times) {
817 	    /* Prints text after semicolon and before final newline.
818 	     * Prints nothing if not qualified for time interpretation.
819 	     * Will include line breaks for longer lists. */
820 	    print_att_times(ncid, varid, &att);
821 	    if(is_bounds_att(&att)) {
822 		insert_bounds_info(ncid, varid, &att);
823 	    }
824 	}
825 #ifdef USE_NETCDF4
826 	/* If NC_STRING, need to free all the strings also */
827 	if(att.type == NC_STRING) {
828 	    nc_free_string(att.len, att.valgp);
829 	}
830 #endif /* USE_NETCDF4 */
831 	free(att.valgp);
832     }
833 #ifdef USE_NETCDF4
834     else /* User-defined type. */
835     {
836        char type_name[NC_MAX_NAME + 1];
837        size_t type_size, nfields;
838        nc_type base_nc_type;
839        int class, i;
840        void *data = NULL;
841 
842        NC_CHECK( nc_inq_user_type(ncid, att.type,  type_name, &type_size,
843 				  &base_nc_type, &nfields, &class));
844        switch(class)
845        {
846 	  case NC_VLEN:
847 	      /* because size returned for vlen is base type size, but we
848 	       * need space to read array of vlen structs into ... */
849               data = emalloc((att.len + 1) * sizeof(nc_vlen_t));
850 	     break;
851 	  case NC_OPAQUE:
852 	      data = emalloc((att.len + 1) * type_size);
853 	     break;
854 	  case NC_ENUM:
855 	      /* a long long is ample for all base types */
856 	      data = emalloc((att.len + 1) * sizeof(int64_t));
857 	     break;
858 	  case NC_COMPOUND:
859 	      data = emalloc((att.len + 1) * type_size);
860 	     break;
861 	  default:
862 	     error("unrecognized class of user defined type: %d", class);
863        }
864 
865        NC_CHECK( nc_get_att(ncid, varid, att.name, data));
866 
867        switch(class) {
868        case NC_VLEN:
869 	   pr_any_att_vals(&att, data);
870 	   break;
871        case NC_OPAQUE: {
872 	   char *sout = emalloc(2 * type_size + strlen("0X") + 1);
873 	   unsigned char *cp = data;
874 	   for (i = 0; i < att.len; i++) {
875 	       (void) ncopaque_val_as_hex(type_size, sout, cp);
876 	       printf("%s%s", sout, i < att.len-1 ? ", " : "");
877 	       cp += type_size;
878 	   }
879 	   free(sout);
880          } break;
881        case NC_ENUM: {
882 	   int64_t value;
883 	   for (i = 0; i < att.len; i++) {
884 	       char enum_name[NC_MAX_NAME + 1];
885 	       switch(base_nc_type)
886 	       {
887 	       case NC_BYTE:
888 		   value = *((char *)data + i);
889 		   break;
890 	       case NC_UBYTE:
891 		   value = *((unsigned char *)data + i);
892 		   break;
893 	       case NC_SHORT:
894 		   value = *((short *)data + i);
895 		   break;
896 	       case NC_USHORT:
897 		   value = *((unsigned short *)data + i);
898 		   break;
899 	       case NC_INT:
900 		   value = *((int *)data + i);
901 		   break;
902 	       case NC_UINT:
903 		   value = *((unsigned int *)data + i);
904 		   break;
905 	       case NC_INT64:
906 		   value = *((int64_t *)data + i);
907 		   break;
908 	       case NC_UINT64:
909 		   value = *((uint64_t *)data + i);
910 		   break;
911 	       default:
912 		   error("enum must have an integer base type: %d", base_nc_type);
913 	       }
914 	       NC_CHECK( nc_inq_enum_ident(ncid, att.type, value,
915 					   enum_name));
916 /* 	       printf("%s%s", enum_name, i < att.len-1 ? ", " : ""); */
917 	       print_name(enum_name);
918 	       printf("%s", i < att.len-1 ? ", " : "");
919 	   }
920          } break;
921        case NC_COMPOUND:
922 	   pr_any_att_vals(&att, data);
923 	   break;
924        default:
925 	   error("unrecognized class of user defined type: %d", class);
926        }
927        ncaux_reclaim_data(ncid,att.type,data,att.len);
928        free(data);
929        printf (" ;");		/* terminator for user defined types */
930     }
931 #endif /* USE_NETCDF4 */
932 
933     printf ("\n");		/* final newline for all attribute types */
934 }
935 
936 /* Common code for printing attribute name */
937 static void
pr_att_name(int ncid,const char * varname,const char * attname)938 pr_att_name(
939     int ncid,
940     const char *varname,
941     const char *attname
942     )
943 {
944     indent_out();
945     printf ("\t\t");
946     print_name(varname);
947     printf(":");
948     print_name(attname);
949 }
950 
951 /*
952  * Print special _Format global attribute, a virtual attribute not
953  * actually stored in the file.
954  */
955 static void
pr_att_global_format(int ncid,int kind)956 pr_att_global_format(
957     int ncid,
958     int kind
959     )
960 {
961     pr_att_name(ncid, "", NC_ATT_FORMAT);
962     printf(" = ");
963     printf("\"%s\"", kind_string(kind));
964     printf (" ;\n");
965 }
966 
967 #ifdef USE_NETCDF4
968 /*
969  * Print special reserved variable attributes, such as _Chunking,
970  * _DeflateLevel, ...  These are virtual, not real, attributes
971  * generated from the result of inquire calls.  They are of primitive
972  * type to fit into the classic model.  Currently, these only exist
973  * for netCDF-4 data.
974  */
975 static void
pr_att_specials(int ncid,int kind,int varid,const ncvar_t * varp)976 pr_att_specials(
977     int ncid,
978     int kind,
979     int varid,
980     const ncvar_t *varp
981     )
982 {
983     /* No special variable attributes for classic or 64-bit offset data */
984     if(kind == 1 || kind == 2)
985 	return;
986     /* _Chunking */
987     if (varp->ndims > 0) {	/* no chunking for scalar variables */
988 	int contig = 0;
989 	NC_CHECK( nc_inq_var_chunking(ncid, varid, &contig, NULL ) );
990 	if(contig == 1) {
991 	    pr_att_name(ncid, varp->name, NC_ATT_STORAGE);
992 	    printf(" = \"contiguous\" ;\n");
993 	} else {
994  	   size_t *chunkp;
995 	   int i;
996 	    pr_att_name(ncid, varp->name, NC_ATT_STORAGE);
997 	    printf(" = \"chunked\" ;\n");
998 	    chunkp = (size_t *) emalloc(sizeof(size_t) * (varp->ndims + 1) );
999 	    NC_CHECK( nc_inq_var_chunking(ncid, varid, NULL, chunkp) );
1000 	    /* print chunking, even if it is default */
1001 	    pr_att_name(ncid, varp->name, NC_ATT_CHUNKING);
1002 	    printf(" = ");
1003 	    for(i = 0; i < varp->ndims; i++) {
1004 		printf("%lu%s", (unsigned long)chunkp[i], i+1 < varp->ndims ? ", " : " ;\n");
1005 	    }
1006 	    free(chunkp);
1007 	}
1008     }
1009 
1010     /*_Deflate, _Shuffle */
1011     {
1012 	int shuffle=NC_NOSHUFFLE, deflate=0, deflate_level=0;
1013 	NC_CHECK( nc_inq_var_deflate(ncid, varid, &shuffle,
1014 				     &deflate, &deflate_level) );
1015 	if(deflate != 0) {
1016 	    pr_att_name(ncid, varp->name, NC_ATT_DEFLATE);
1017 	    printf(" = %d ;\n", deflate_level);
1018 	}
1019 	if(shuffle != NC_NOSHUFFLE) {
1020 	    pr_att_name(ncid, varp->name, NC_ATT_SHUFFLE);
1021 	    printf(" = \"true\" ;\n");
1022 	}
1023     }
1024     /* _Checksum */
1025     {
1026 	int fletcher32 = 0;
1027 	NC_CHECK( nc_inq_var_fletcher32(ncid, varid, &fletcher32) );
1028 	if(fletcher32 != 0) {
1029 	    pr_att_name(ncid, varp->name, NC_ATT_CHECKSUM);
1030 	    printf(" = \"true\" ;\n");
1031 	}
1032     }
1033     /* _Endianness */
1034     if(varp->tinfo->size > 1) /* Endianness is meaningless for 1-byte types */
1035     {
1036 	int endianness = 0;
1037 	NC_CHECK( nc_inq_var_endian(ncid, varid, &endianness) );
1038 	if (endianness != NC_ENDIAN_NATIVE) { /* NC_ENDIAN_NATIVE is the default */
1039 	    pr_att_name(ncid, varp->name, NC_ATT_ENDIANNESS);
1040 	    printf(" = ");
1041 	    switch (endianness) {
1042 	    case NC_ENDIAN_LITTLE:
1043 		printf("\"little\"");
1044 		break;
1045 	    case NC_ENDIAN_BIG:
1046 		printf("\"big\"");
1047 		break;
1048 	    default:
1049 		error("pr_att_specials: bad endianness: %d", endianness);
1050 		break;
1051 	    }
1052 	    printf(" ;\n");
1053 	}
1054     }
1055     /* _Filter */
1056     {
1057 	unsigned int id;
1058 	size_t nparams;
1059 	unsigned int* params = NULL;
1060 	if(nc_inq_var_filter(ncid, varid, &id, &nparams, NULL) == NC_NOERR
1061 	   && id > 0) {
1062 	    if(nparams > 0) {
1063 	        params = (unsigned int*)calloc(1,sizeof(unsigned int)*nparams);
1064 	        NC_CHECK(nc_inq_var_filter(ncid, varid, &id, &nparams, params));
1065 	    }
1066 	    pr_att_name(ncid,varp->name,NC_ATT_FILTER);
1067 	    printf(" = \"%u",id);
1068 	    if(nparams > 0) {
1069 	        int i;
1070 		for(i=0;i<nparams;i++)
1071 		    printf(",%u",params[i]);
1072 	    }
1073 	    printf("\" ;\n");
1074 	}
1075     }
1076     {
1077 	int no_fill = 0;
1078 	/* Don't get the fill_value, it's set explicitly with
1079 	 * _FillValue attribute, because nc_def_var_fill() creates a
1080 	 * _FillValue attribute, if needed, and it's value gets
1081 	 * displayed elsewhere as a normal (not special virtual)
1082 	 * attribute. */
1083 	NC_CHECK( nc_inq_var_fill(ncid, varid, &no_fill, NULL) );
1084 	if(no_fill != 0) {
1085 	    pr_att_name(ncid, varp->name, NC_ATT_NOFILL);
1086 	    printf(" = \"true\" ;\n");
1087 	}
1088     }
1089 
1090     /* TODO: handle _Nbit when inquire function is available */
1091 
1092     /* TODO: handle _ScaleOffset when inquire is available */
1093 
1094     /* TODO: handle _Szip when szip inquire function is available */
1095 }
1096 #endif /* USE_NETCDF4 */
1097 
1098 #ifdef USE_NETCDF4
1099 static void
pr_att_hidden(int ncid,int kind)1100 pr_att_hidden(
1101     int ncid,
1102     int kind
1103     )
1104 {
1105     int stat;
1106     size_t len;
1107 
1108     /* No special variable attributes for classic or 64-bit offset data */
1109     if(kind == 1 || kind == 2)
1110 	return;
1111     /* Print out Selected hidden attributes */
1112     /* NCPROPS */
1113     stat = nc_inq_att(ncid,NC_GLOBAL,NCPROPS,NULL,&len);
1114     if(stat == NC_NOERR) {
1115 	char* propdata = (char*)malloc(len+1);
1116 	if(propdata == NULL)
1117 	    return;
1118         stat = nc_get_att_text(ncid,NC_GLOBAL,NCPROPS,propdata);
1119         if(stat == NC_NOERR) {
1120             pr_att_name(ncid, "", NCPROPS);
1121             /* make sure its null terminated */
1122             propdata[len] = '\0';
1123             printf(" = \"%s\" ;\n",propdata);
1124         }
1125 	free(propdata);
1126     }
1127     /* _SuperblockVersion */
1128     stat = nc_inq_att(ncid,NC_GLOBAL,SUPERBLOCKATT,NULL,&len);
1129     if(stat == NC_NOERR && len == 1) {
1130         int sbversion;
1131         stat = nc_get_att_int(ncid,NC_GLOBAL,SUPERBLOCKATT,&sbversion);
1132         if(stat == NC_NOERR) {
1133             pr_att_name(ncid, "", SUPERBLOCKATT);
1134             printf(" = %d ;\n",sbversion);
1135         }
1136     }
1137     /* _IsNetcdf4 */
1138     stat = nc_inq_att(ncid,NC_GLOBAL,ISNETCDF4ATT,NULL,&len);
1139     if(stat == NC_NOERR && len == 1) {
1140         int isnc4;
1141         stat = nc_get_att_int(ncid,NC_GLOBAL,ISNETCDF4ATT,&isnc4);
1142         if(stat == NC_NOERR) {
1143             pr_att_name(ncid, "", ISNETCDF4ATT);
1144             printf(" = %d ;\n",isnc4?1:0);
1145         }
1146     }
1147 }
1148 #endif /* USE_NETCDF4 */
1149 
1150 /*
1151  * Print a variable attribute for NcML
1152  */
1153 static void
pr_attx(int ncid,int varid,int ia)1154 pr_attx(
1155     int ncid,
1156     int varid,
1157     int ia
1158     )
1159 {
1160     ncatt_t att;			/* attribute */
1161     char *attvals = NULL;
1162     int attvalslen = 0;
1163 
1164     NC_CHECK( nc_inq_attname(ncid, varid, ia, att.name) );
1165 #ifdef USE_NETCDF4
1166     if (ncid == getrootid(ncid)
1167 	&& varid == NC_GLOBAL
1168         && strcmp(att.name,NCPROPS)==0
1169         && (!formatting_specs.special_atts
1170             || !formatting_specs.xopt_props)
1171 	)
1172 	return;
1173 #endif
1174     NC_CHECK( nc_inq_att(ncid, varid, att.name, &att.type, &att.len) );
1175 
1176     /* Put attribute values into a single string, with blanks in between */
1177 
1178     switch (att.type) {
1179     case NC_CHAR:
1180 	attvals = (char *) emalloc(att.len + 1);
1181 	attvalslen = att.len;
1182 	attvals[att.len] = '\0';
1183 	NC_CHECK( nc_get_att_text(ncid, varid, att.name, attvals ) );
1184 	break;
1185 #ifdef USE_NETCDF4
1186     case NC_STRING:
1187 	/* TODO: this only prints first string value, need to handle
1188 	   multiple strings? */
1189 	attvals = (char *) emalloc(att.len + 1);
1190 	attvals[att.len] = '\0';
1191 	NC_CHECK( nc_get_att_text(ncid, varid, att.name, attvals ) );
1192 	break;
1193     case NC_VLEN:
1194 	/* TODO */
1195 	break;
1196     case NC_OPAQUE:
1197 	/* TODO */
1198 	break;
1199     case NC_COMPOUND:
1200 	/* TODO */
1201 	break;
1202 #endif /* USE_NETCDF4 */
1203     default:
1204 	att.vals = (double *) emalloc((att.len + 1) * sizeof(double));
1205 	NC_CHECK( nc_get_att_double(ncid, varid, att.name, att.vals ) );
1206 	attvalslen = PRIM_LEN * att.len; /* max chars for each value and blank separator */
1207 	attvals = (char *) emalloc(attvalslen + 1);
1208 	pr_att_valsx(att.type, att.len, att.vals, attvals, attvalslen);
1209 	free(att.vals);
1210 	break;
1211     }
1212 
1213     /* Don't output type for string attributes, since that's default type */
1214     if(att.type == NC_CHAR
1215 #ifdef USE_NETCDF4
1216                           || att.type == NC_CHAR
1217 #endif /* USE_NETCDF4 */
1218        ) {
1219 	/* TODO: XML-ish escapes for special chars in names */
1220 	printf ("%s  <attribute name=\"%s\" value=",
1221 		varid != NC_GLOBAL ? "  " : "",
1222 		att.name);
1223 	/* print attvals as a string with XML escapes */
1224 	pr_attx_string(att.name, attvalslen, attvals);
1225     } else {			/* non-string attribute */
1226 	char att_type_name[NC_MAX_NAME + 1];
1227 	get_type_name(ncid, att.type, att_type_name);
1228 	/* TODO: print full type name with group prefix, when needed */
1229 	printf ("%s  <attribute name=\"%s\" type=\"%s\" value=\"",
1230 		varid != NC_GLOBAL ? "  " : "",
1231 		att.name,
1232 		att_type_name);
1233 	printf("%s\"",attvals);
1234     }
1235     printf (" />\n");
1236     if(attvals != NULL)
1237       free (attvals);
1238 }
1239 
1240 
1241 /* Print optional NcML attribute for a variable's shape */
1242 static void
pr_shape(ncvar_t * varp,ncdim_t * dims)1243 pr_shape(ncvar_t* varp, ncdim_t *dims)
1244 {
1245     char *shape;
1246     int shapelen = 0;
1247     int id;
1248 
1249     if (varp->ndims == 0)
1250 	return;
1251     for (id = 0; id < varp->ndims; id++) {
1252 	shapelen += strlen(dims[varp->dims[id]].name) + 1;
1253     }
1254     shape = (char *) emalloc(shapelen + 1);
1255     shape[0] = '\0';
1256     for (id = 0; id < varp->ndims; id++) {
1257 	/* TODO: XML-ish escapes for special chars in dim names */
1258 	strlcat(shape, dims[varp->dims[id]].name, shapelen);
1259 	strlcat(shape, id < varp->ndims-1 ? " " : "", shapelen);
1260     }
1261     printf (" shape=\"%s\"", shape);
1262     free(shape);
1263 }
1264 
1265 #ifdef USE_NETCDF4
1266 
1267 
1268 /* Print an enum type declaration */
1269 static void
print_enum_type(int ncid,nc_type typeid)1270 print_enum_type(int ncid, nc_type typeid) {
1271     char type_name[NC_MAX_NAME + 1];
1272     size_t type_size;
1273     nc_type base_nc_type;
1274     size_t type_nfields;
1275     int type_class;
1276     char base_type_name[NC_MAX_NAME + 1];
1277     int f;
1278     int64_t memval;
1279     char memname[NC_MAX_NAME + 1];
1280  /* extra space for escapes, and punctuation */
1281 #define SAFE_BUF_LEN 4*NC_MAX_NAME+30
1282     char safe_buf[SAFE_BUF_LEN];
1283     char *delim;
1284     int64_t data;	    /* space for data of any primitive type */
1285     void* raw;
1286     char *esc_btn;
1287     char *esc_tn;
1288     char *esc_mn;
1289     int res;
1290 
1291     NC_CHECK( nc_inq_user_type(ncid, typeid, type_name, &type_size, &base_nc_type,
1292 			       &type_nfields, &type_class) );
1293 
1294     get_type_name(ncid, base_nc_type, base_type_name);
1295     indent_out();
1296     esc_btn = escaped_name(base_type_name);
1297     esc_tn = escaped_name(type_name);
1298     res = snprintf(safe_buf, SAFE_BUF_LEN,"%s enum %s {", esc_btn, esc_tn);
1299     assert(res < SAFE_BUF_LEN);
1300     free(esc_btn);
1301     free(esc_tn);
1302     lput(safe_buf);
1303     delim = ", ";
1304     for (f = 0; f < type_nfields; f++) {
1305 	if (f == type_nfields - 1)
1306 	    delim = "} ;\n";
1307 	NC_CHECK( nc_inq_enum_member(ncid, typeid, f, memname, &data) );
1308 	raw = (void*)&data;
1309 	switch (base_nc_type) {
1310 	case NC_BYTE:
1311 	    memval = *(char *)raw;
1312 	    break;
1313 	case NC_SHORT:
1314 	    memval = *(short *)raw;
1315 	    break;
1316 	case NC_INT:
1317 	    memval = *(int *)raw;
1318 	    break;
1319 	case NC_UBYTE:
1320 	    memval = *(unsigned char *)raw;
1321 	    break;
1322 	case NC_USHORT:
1323 	    memval = *(unsigned short *)raw;
1324 	    break;
1325 	case NC_UINT:
1326 	    memval = *(unsigned int *)raw;
1327 	    break;
1328 	case NC_INT64:
1329 	    memval = *(int64_t *)raw;
1330 	    break;
1331 	case NC_UINT64:
1332 	    memval = *(uint64_t *)raw;
1333 	    break;
1334 	default:
1335 	    error("Bad base type for enum!");
1336 	    break;
1337 	}
1338 	esc_mn = escaped_name(memname);
1339 	res = snprintf(safe_buf, SAFE_BUF_LEN, "%s = %lld%s", esc_mn,
1340 		       memval, delim);
1341 	assert(res < SAFE_BUF_LEN);
1342 	free(esc_mn);
1343 	lput(safe_buf);
1344     }
1345 }
1346 
1347 
1348 /* Print a user-defined type declaration */
1349 static void
print_ud_type(int ncid,nc_type typeid)1350 print_ud_type(int ncid, nc_type typeid) {
1351 
1352     char type_name[NC_MAX_NAME + 1];
1353     char base_type_name[NC_MAX_NAME + 1];
1354     size_t type_nfields, type_size;
1355     nc_type base_nc_type;
1356     int f, type_class;
1357 
1358     NC_CHECK( nc_inq_user_type(ncid, typeid, type_name, &type_size, &base_nc_type,
1359 			       &type_nfields, &type_class) );
1360     switch(type_class) {
1361     case NC_VLEN:
1362 	/* TODO: don't bother getting base_type_name if
1363 	 * print_type_name looks it up anyway */
1364 	get_type_name(ncid, base_nc_type, base_type_name);
1365 	indent_out();
1366 /* 	printf("%s(*) %s ;\n", base_type_name, type_name); */
1367 	print_type_name(ncid, base_nc_type);
1368 	printf("(*) ");
1369 	print_type_name(ncid, typeid);
1370 	printf(" ;\n");
1371 	break;
1372     case NC_OPAQUE:
1373 	indent_out();
1374 /* 	printf("opaque(%d) %s ;\n", (int)type_size, type_name); */
1375 	printf("opaque(%d) ", (int)type_size);
1376 	print_type_name(ncid, typeid);
1377 	printf(" ;\n");
1378 	break;
1379     case NC_ENUM:
1380 	print_enum_type(ncid, typeid);
1381 	break;
1382     case NC_COMPOUND:
1383 	{
1384 	    char field_name[NC_MAX_NAME + 1];
1385 	    char field_type_name[NC_MAX_NAME + 1];
1386 	    size_t field_offset;
1387 	    nc_type field_type;
1388 	    int field_ndims;
1389 	    int d;
1390 
1391 	    indent_out();
1392 /* 	    printf("compound %s {\n", type_name); */
1393 	    printf("compound ");
1394 	    print_type_name(ncid, typeid);
1395 	    printf(" {\n");
1396 	    for (f = 0; f < type_nfields; f++)
1397 		{
1398 		    NC_CHECK( nc_inq_compound_field(ncid, typeid, f, field_name,
1399 						    &field_offset, &field_type,
1400 						    &field_ndims, NULL) );
1401 		    /* TODO: don't bother if field_type_name not needed here */
1402 		    get_type_name(ncid, field_type, field_type_name);
1403 		    indent_out();
1404 /* 		    printf("  %s %s", field_type_name, field_name); */
1405 		    printf("  ");
1406 		    print_type_name(ncid, field_type);
1407 		    printf(" ");
1408 		    print_name(field_name);
1409 		    if (field_ndims > 0) {
1410 			int *field_dim_sizes = (int *) emalloc((field_ndims + 1) * sizeof(int));
1411 			NC_CHECK( nc_inq_compound_field(ncid, typeid, f, NULL,
1412 							NULL, NULL, NULL,
1413 							field_dim_sizes) );
1414 			printf("(");
1415 			for (d = 0; d < field_ndims-1; d++)
1416 			    printf("%d, ", field_dim_sizes[d]);
1417 			printf("%d)", field_dim_sizes[field_ndims-1]);
1418 			free(field_dim_sizes);
1419 		    }
1420 		    printf(" ;\n");
1421 		}
1422             indent_out();
1423 #if 0
1424  	    printf("}; // %s\n", type_name);
1425 #else
1426 	    printf("}; // ");
1427 #endif
1428 	    print_type_name(ncid, typeid);
1429 	    printf("\n");
1430 	}
1431 	break;
1432     default:
1433 	error("Unknown class of user-defined type!");
1434     }
1435 }
1436 #endif /* USE_NETCDF4 */
1437 
1438 static void
get_fill_info(int ncid,int varid,ncvar_t * vp)1439 get_fill_info(int ncid, int varid, ncvar_t *vp)
1440 {
1441     ncatt_t att;			/* attribute */
1442     int nc_status;			/* return from netcdf calls */
1443     void *fillvalp = NULL;
1444 
1445     vp->has_fillval = 1; /* by default, but turn off for bytes */
1446 
1447     /* get _FillValue attribute */
1448     nc_status = nc_inq_att(ncid,varid,_FillValue,&att.type,&att.len);
1449     fillvalp = ecalloc(vp->tinfo->size + 1);
1450     if(nc_status == NC_NOERR &&
1451        att.type == vp->type && att.len == 1) {
1452 	NC_CHECK(nc_get_att(ncid, varid, _FillValue, fillvalp));
1453     } else {
1454 	switch (vp->type) {
1455 	case NC_BYTE:
1456 	    /* don't do default fill-values for bytes, too risky */
1457 	    vp->has_fillval = 0;
1458 	    free(fillvalp);
1459 	    fillvalp = 0;
1460 	    break;
1461 	case NC_CHAR:
1462 	    *(char *)fillvalp = NC_FILL_CHAR;
1463 	    break;
1464 	case NC_SHORT:
1465 	    *(short *)fillvalp = NC_FILL_SHORT;
1466 	    break;
1467 	case NC_INT:
1468 	    *(int *)fillvalp = NC_FILL_INT;
1469 	    break;
1470 	case NC_FLOAT:
1471 	    *(float *)fillvalp = NC_FILL_FLOAT;
1472 	    break;
1473 	case NC_DOUBLE:
1474 	    *(double *)fillvalp = NC_FILL_DOUBLE;
1475 	    break;
1476 	case NC_UBYTE:
1477 	    /* don't do default fill-values for bytes, too risky */
1478 	    vp->has_fillval = 0;
1479 	    free(fillvalp);
1480 	    fillvalp = 0;
1481 	    break;
1482 	case NC_USHORT:
1483 	    *(unsigned short *)fillvalp = NC_FILL_USHORT;
1484 	    break;
1485 	case NC_UINT:
1486 	    *(unsigned int *)fillvalp = NC_FILL_UINT;
1487 	    break;
1488 	case NC_INT64:
1489 	    *(int64_t *)fillvalp = NC_FILL_INT64;
1490 	    break;
1491 	case NC_UINT64:
1492 	    *(uint64_t *)fillvalp = NC_FILL_UINT64;
1493 	    break;
1494 #ifdef USE_NETCDF4
1495 	case NC_STRING: {
1496 	    char* s;
1497 	    size_t len = strlen(NC_FILL_STRING);
1498 #if 0
1499 	    /* In order to avoid mem leak, allocate this string as part of fillvalp */
1500             fillvalp = erealloc(fillvalp, vp->tinfo->size + 1 + len + 1);
1501 	    s = ((char*)fillvalp) + vp->tinfo->size + 1;
1502 #else
1503 	    s = malloc(len+1);
1504 #endif
1505 	    memcpy(s,NC_FILL_STRING,len);
1506 	    s[len] = '\0';
1507 	    *((char **)fillvalp) = s;
1508 	    } break;
1509 #endif /* USE_NETCDF4 */
1510 	default:		/* no default fill values for NC_NAT
1511 				   or user-defined types */
1512 	    vp->has_fillval = 0;
1513 	    free(fillvalp);
1514 	    fillvalp = 0;
1515 	    break;
1516 	}
1517     }
1518     vp->fillvalp = fillvalp;
1519 }
1520 
1521 /* Recursively dump the contents of a group. (Only netcdf-4 format
1522  * files can have groups, so recursion will not take place for classic
1523  * format files.)
1524  *
1525  * ncid: id of open file (first call) or group (subsequent recursive calls)
1526  * path: file path name (first call)
1527  */
1528 static void
do_ncdump_rec(int ncid,const char * path)1529 do_ncdump_rec(int ncid, const char *path)
1530 {
1531    int ndims;			/* number of dimensions */
1532    int nvars;			/* number of variables */
1533    int ngatts;			/* number of global attributes */
1534    int xdimid;			/* id of unlimited dimension */
1535    int varid;			/* variable id */
1536    ncdim_t *dims;		/* dimensions */
1537    size_t *vdims=0;	        /* dimension sizes for a single variable */
1538    ncvar_t var;			/* variable */
1539    int id;			/* dimension number per variable */
1540    int ia;			/* attribute number */
1541    int iv;			/* variable number */
1542    idnode_t* vlist = NULL;	/* list for vars specified with -v option */
1543    char type_name[NC_MAX_NAME + 1];
1544    int kind;		/* strings output differently for nc4 files */
1545    char dim_name[NC_MAX_NAME + 1];
1546 #ifdef USE_NETCDF4
1547    int *dimids_grp;	        /* dimids of the dims in this group. */
1548    int *unlimids;		/* dimids of unlimited dimensions in this group */
1549    int d_grp, ndims_grp;
1550    int ntypes, *typeids;
1551    int nunlim;
1552 #else
1553    int dimid;			/* dimension id */
1554 #endif /* USE_NETCDF4 */
1555    int is_root = 1;		/* true if ncid is root group or if netCDF-3 */
1556 
1557 #ifdef USE_NETCDF4
1558    if (nc_inq_grp_parent(ncid, NULL) != NC_ENOGRP)
1559        is_root = 0;
1560 #endif /* USE_NETCDF4 */
1561 
1562    /*
1563     * If any vars were specified with -v option, get list of
1564     * associated variable ids relative to this group.  Assume vars
1565     * specified with syntax like "grp1/grp2/varname" or
1566     * "/grp1/grp2/varname" if they are in groups.
1567     */
1568    if (formatting_specs.nlvars > 0) {
1569       vlist = newidlist();	/* list for vars specified with -v option */
1570       for (iv=0; iv < formatting_specs.nlvars; iv++) {
1571 	  if(nc_inq_gvarid(ncid, formatting_specs.lvars[iv], &varid) == NC_NOERR)
1572 	      idadd(vlist, varid);
1573       }
1574    }
1575 
1576 #ifdef USE_NETCDF4
1577    /* Are there any user defined types in this group? */
1578    NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) );
1579    if (ntypes)
1580    {
1581       int t;
1582 
1583       typeids = emalloc((ntypes + 1) * sizeof(int));
1584       NC_CHECK( nc_inq_typeids(ncid, &ntypes, typeids) );
1585       indent_out();
1586       printf("types:\n");
1587       indent_more();
1588       for (t = 0; t < ntypes; t++)
1589       {
1590 	 print_ud_type(ncid, typeids[t]); /* print declaration of user-defined type */
1591       }
1592       indent_less();
1593       free(typeids);
1594    }
1595 #endif /* USE_NETCDF4 */
1596 
1597    /*
1598     * get number of dimensions, number of variables, number of global
1599     * atts, and dimension id of unlimited dimension, if any
1600     */
1601    NC_CHECK( nc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) );
1602    /* get dimension info */
1603    dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
1604    if (ndims > 0) {
1605        indent_out();
1606        printf ("dimensions:\n");
1607    }
1608 
1609 #ifdef USE_NETCDF4
1610    /* In netCDF-4 files, dimids will not be sequential because they
1611     * may be defined in various groups, and we are only looking at one
1612     * group at a time. */
1613 
1614    /* Find the number of dimids defined in this group. */
1615    NC_CHECK( nc_inq_ndims(ncid, &ndims_grp) );
1616    dimids_grp = (int *)emalloc((ndims_grp + 1) * sizeof(int));
1617 
1618    /* Find the dimension ids in this group. */
1619    NC_CHECK( nc_inq_dimids(ncid, 0, dimids_grp, 0) );
1620 
1621    /* Find the number of unlimited dimensions and get their IDs */
1622    NC_CHECK( nc_inq_unlimdims(ncid, &nunlim, NULL) );
1623    unlimids = (int *)emalloc((nunlim + 1) * sizeof(int));
1624    NC_CHECK( nc_inq_unlimdims(ncid, &nunlim, unlimids) );
1625 
1626    /* For each dimension defined in this group, get and print out info. */
1627    for (d_grp = 0; d_grp < ndims_grp; d_grp++)
1628    {
1629       int dimid = dimids_grp[d_grp];
1630       int is_unlimited = 0;
1631       int uld;
1632       int stat;
1633 
1634       for (uld = 0; uld < nunlim; uld++) {
1635 	  if(dimid == unlimids[uld]) {
1636 	      is_unlimited = 1;
1637 	      break;
1638 	  }
1639       }
1640       stat = nc_inq_dim(ncid, dimid, dims[d_grp].name, &dims[d_grp].size);
1641       if (stat == NC_EDIMSIZE && SIZEOF_SIZE_T < 8) {
1642 	  error("dimension \"%s\" too large for 32-bit platform, try 64-bit version", dims[d_grp].name);
1643       } else {
1644 	  NC_CHECK (stat);
1645       }
1646       indent_out();
1647       printf ("\t");
1648       print_name(dims[d_grp].name);
1649       printf (" = ");
1650       if(SIZEOF_SIZE_T >= 8) {
1651 	  if (is_unlimited) {
1652 	      printf ("UNLIMITED ; // (%lu currently)\n",
1653 		      (unsigned long)dims[d_grp].size);
1654 	  } else {
1655 	      printf ("%lu ;\n", (unsigned long)dims[d_grp].size);
1656 	  }
1657       } else {			/* 32-bit platform */
1658 	  if (is_unlimited) {
1659 	      printf ("UNLIMITED ; // (%u currently)\n",
1660 		      (unsigned int)dims[d_grp].size);
1661 	  } else {
1662 	      printf ("%u ;\n", (unsigned int)dims[d_grp].size);
1663 	  }
1664       }
1665    }
1666    if(unlimids)
1667        free(unlimids);
1668    if(dimids_grp)
1669        free(dimids_grp);
1670 #else /* not using netCDF-4 */
1671    for (dimid = 0; dimid < ndims; dimid++) {
1672       NC_CHECK( nc_inq_dim(ncid, dimid, dims[dimid].name, &dims[dimid].size) );
1673       indent_out();
1674       printf ("\t");
1675       print_name(dims[dimid].name);
1676       printf (" = ");
1677       if (dimid == xdimid) {
1678 	  printf ("UNLIMITED ; // (%u currently)\n",
1679 		  (unsigned int)dims[dimid].size);
1680       } else {
1681 	  printf ("%llu ;\n", (unsigned long long)dims[dimid].size);
1682       }
1683    }
1684 #endif /* USE_NETCDF4 */
1685 
1686    if (nvars > 0) {
1687        indent_out();
1688        printf ("variables:\n");
1689    }
1690    /* Because netCDF-4 can have a string attribute with multiple
1691     * string values, we can't output strings with embedded newlines
1692     * as what look like multiple strings, as we do for classic and
1693     * 64-bit offset  and cdf5 files.  So we need to know the output file type
1694     * to know how to print strings with embedded newlines. */
1695    NC_CHECK( nc_inq_format(ncid, &kind) );
1696 
1697    memset((void*)&var,0,sizeof(var));
1698 
1699    /* For each var, get and print out info. */
1700 
1701    for (varid = 0; varid < nvars; varid++) {
1702       NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) );
1703       if(var.dims != NULL) free(var.dims);
1704       var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
1705       NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0,
1706 			   var.dims, &var.natts) );
1707       /* TODO: don't bother if type name not needed here */
1708       get_type_name(ncid, var.type, type_name);
1709       var.tinfo = get_typeinfo(var.type);
1710       indent_out();
1711 /*       printf ("\t%s %s", type_name, var.name); */
1712       printf ("\t");
1713       /* TODO: if duplicate type name and not just inherited, print
1714        * full type name. */
1715       print_type_name (ncid, var.type);
1716       printf (" ");
1717       print_name (var.name);
1718       if (var.ndims > 0)
1719 	 printf ("(");
1720       for (id = 0; id < var.ndims; id++) {
1721 	 /* This dim may be in a parent group, so let's look up the
1722 	  * name. */
1723 	 NC_CHECK( nc_inq_dimname(ncid, var.dims[id], dim_name) );
1724 #ifdef USE_NETCDF4
1725 	 /* Subtlety: The following code block is needed because
1726 	  * nc_inq_dimname() currently returns only a simple dimension
1727 	  * name, without a prefix identifying the group it came from.
1728 	  * That's OK unless the dimid identifies a dimension in an
1729 	  * ancestor group that has the same simple name as a
1730 	  * dimension in the current group (or some intermediate
1731 	  * group), in which case the simple name is ambiguous.  This
1732 	  * code tests for that case and provides an absolute dimname
1733 	  * only in the case where a simple name would be
1734 	  * ambiguous. */
1735 	 {
1736 	     int dimid_test;	/* to see if dimname is ambiguous */
1737 	     int target_dimid;  /* from variable dim list */
1738 	     int locid;		/* group id where dimension is defined */
1739 	     /*Locate the innermost definition of a dimension with given name*/
1740 	     NC_CHECK( nc_inq_dimid(ncid, dim_name, &dimid_test) );
1741 
1742 	     /* Now, starting with current group, walk the parent chain
1743                 upward looking for the target dim_id */
1744 	     target_dimid = var.dims[id];
1745 	     locid = ncid;
1746 	     while(target_dimid != dimid_test) {/*not in locid, try ancestors*/
1747 		 int parent_id;
1748 		 NC_CHECK( nc_inq_grp_parent(locid, &parent_id) );
1749 		 locid = parent_id;
1750 		 /* Is dim of this name defined in this group or higher? */
1751 		 NC_CHECK( nc_inq_dimid(locid, dim_name, &dimid_test) );
1752 	     }
1753 	     /* innermost dimid with given name is in group locid.
1754                 If this is not current group, then use fully qualified
1755                 name (fqn) for the dimension name by prefixing dimname
1756                 with group name */
1757 	     if(locid != ncid) { /* We need to use fqn */
1758 		 size_t len;
1759 		 char *locname;	/* the group name */
1760 		 NC_CHECK( nc_inq_grpname_full(locid, &len, NULL) );
1761 		 locname = emalloc(len + 1);
1762 		 NC_CHECK( nc_inq_grpname_full(locid, &len, locname) );
1763 		 print_name (locname);
1764 		 if(strcmp("/", locname) != 0) { /* not the root group */
1765 		     printf("/");		 /* ensure a trailing slash */
1766 		 }
1767 		 free(locname);
1768 	     }
1769 	 }
1770 #endif	/* USE_NETCDF4 */
1771 	 print_name (dim_name);
1772 	 printf ("%s", id < var.ndims-1 ? ", " : ")");
1773       }
1774       printf (" ;\n");
1775 
1776       /* print variable attributes */
1777       for (ia = 0; ia < var.natts; ia++) { /* print ia-th attribute */
1778 	  pr_att(ncid, kind, varid, var.name, ia);
1779       }
1780 #ifdef USE_NETCDF4
1781       /* Print special (virtual) attributes, if option specified */
1782       if (formatting_specs.special_atts) {
1783 	  pr_att_specials(ncid, kind, varid, &var);
1784       }
1785 #endif /* USE_NETCDF4 */
1786    }
1787 
1788    if (ngatts > 0 || formatting_specs.special_atts) {
1789       printf ("\n");
1790       indent_out();
1791       if (is_root)
1792 	  printf("// global attributes:\n");
1793       else
1794 	  printf("// group attributes:\n");
1795    }
1796    for (ia = 0; ia < ngatts; ia++) { /* print ia-th global attribute */
1797        pr_att(ncid, kind, NC_GLOBAL, "", ia);
1798    }
1799    if (is_root && formatting_specs.special_atts) { /* output special attribute
1800 					   * for format variant */
1801 #ifdef USE_NETCDF4
1802        pr_att_hidden(ncid, kind);
1803 #endif
1804        pr_att_global_format(ncid, kind);
1805    }
1806 
1807    fflush(stdout);
1808 
1809    /* output variable data, unless "-h" option specified header only
1810     * or this group is not in list of groups specified by "-g"
1811     * option  */
1812    if (! formatting_specs.header_only &&
1813        group_wanted(ncid, formatting_specs.nlgrps, formatting_specs.grpids) ) {
1814       if (nvars > 0) {
1815 	  indent_out();
1816 	  printf ("data:\n");
1817       }
1818       for (varid = 0; varid < nvars; varid++) {
1819 	 int no_data;
1820 	 /* if var list specified, test for membership */
1821 	 if (formatting_specs.nlvars > 0 && ! idmember(vlist, varid))
1822 	    continue;
1823 	 NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) );
1824 	 if(var.dims != NULL) free(var.dims);
1825 	 var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
1826 	 NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0,
1827 			      var.dims, &var.natts) );
1828 	 var.tinfo = get_typeinfo(var.type);
1829 	 /* If coords-only option specified, don't get data for
1830 	  * non-coordinate vars */
1831 	 if (formatting_specs.coord_vals && !iscoordvar(ncid,varid)) {
1832 	    continue;
1833 	 }
1834 	 /* Collect variable's dim sizes */
1835 	 if (vdims) {
1836 	     free(vdims);
1837 	     vdims = 0;
1838 	 }
1839 	 vdims = (size_t *) emalloc((var.ndims + 1) * SIZEOF_SIZE_T);
1840 	 no_data = 0;
1841 	 for (id = 0; id < var.ndims; id++) {
1842 	     size_t len;
1843 	     NC_CHECK( nc_inq_dimlen(ncid, var.dims[id], &len) );
1844 	     if(len == 0) {
1845 		 no_data = 1;
1846 	     }
1847 	     vdims[id] = len;
1848 	 }
1849 	 /* Don't get data for record variables if no records have
1850 	  * been written yet */
1851 	 if (no_data) {
1852 	     free(vdims);
1853 	     vdims = 0;
1854 	     continue;
1855 	 }
1856 	 get_fill_info(ncid, varid, &var); /* sets has_fillval, fillvalp mmbrs */
1857 	 if(var.timeinfo != NULL) {
1858 	     if(var.timeinfo->units) free(var.timeinfo->units);
1859 	     free(var.timeinfo);
1860 	 }
1861 	 get_timeinfo(ncid, varid, &var); /* sets has_timeval, timeinfo mmbrs */
1862 	 /* printf format used to print each value */
1863 	 var.fmt = get_fmt(ncid, varid, var.type);
1864 	 var.locid = ncid;
1865 	 set_tostring_func(&var);
1866 	 if (vardata(&var, vdims, ncid, varid) == -1) {
1867 	    error("can't output data for variable %s", var.name);
1868 	    goto done;
1869 	 }
1870 	 if(var.fillvalp != NULL)
1871 	     {ncaux_reclaim_data(ncid,var.tinfo->tid,var.fillvalp,1); free(var.fillvalp); var.fillvalp = NULL;}
1872       }
1873       if (vdims) {
1874 	  free(vdims);
1875 	  vdims = 0;
1876       }
1877    }
1878 
1879 #ifdef USE_NETCDF4
1880    /* For netCDF-4 compiles, check to see if the file has any
1881     * groups. If it does, this function is called recursively on each
1882     * of them. */
1883    {
1884       int g, numgrps, *ncids;
1885       char group_name[NC_MAX_NAME + 1];
1886 
1887       /* See how many groups there are. */
1888       NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) );
1889 
1890       /* Allocate memory to hold the list of group ids. */
1891       ncids = emalloc((numgrps + 1) * sizeof(int));
1892 
1893       /* Get the list of group ids. */
1894       NC_CHECK( nc_inq_grps(ncid, NULL, ncids) );
1895 
1896       /* Call this function for each group. */
1897       for (g = 0; g < numgrps; g++)
1898       {
1899 	  NC_CHECK( nc_inq_grpname(ncids[g], group_name) );
1900 	  printf ("\n");
1901 	  indent_out();
1902 /* 	    printf ("group: %s {\n", group_name); */
1903 	  printf ("group: ");
1904 	  print_name (group_name);
1905 	  printf (" {\n");
1906 	  indent_more();
1907 	  do_ncdump_rec(ncids[g], NULL);
1908 	  indent_out();
1909 /* 	    printf ("} // group %s\n", group_name); */
1910 	  printf ("} // group ");
1911 	  print_name (group_name);
1912 	  printf ("\n");
1913 	  indent_less();
1914       }
1915 
1916       free(ncids);
1917    }
1918 #endif /* USE_NETCDF4 */
1919 
1920 done:
1921    if(var.dims != NULL) free(var.dims);
1922    if(var.fillvalp != NULL) {
1923 	/* Release any data hanging off of fillvalp */
1924 	ncaux_reclaim_data(ncid,var.tinfo->tid,var.fillvalp,1);
1925 	free(var.fillvalp);
1926 	var.fillvalp = NULL;
1927    }
1928    if(var.timeinfo != NULL) {
1929       if(var.timeinfo->units) free(var.timeinfo->units);
1930       free(var.timeinfo);
1931    }
1932    if (dims)
1933       free(dims);
1934    if (vlist)
1935       freeidlist(vlist);
1936 }
1937 
1938 
1939 static void
do_ncdump(int ncid,const char * path)1940 do_ncdump(int ncid, const char *path)
1941 {
1942    char* esc_specname;
1943    /* output initial line */
1944    indent_init();
1945    indent_out();
1946    esc_specname=escaped_name(formatting_specs.name);
1947    printf ("netcdf %s {\n", esc_specname);
1948    free(esc_specname);
1949    do_ncdump_rec(ncid, path);
1950    indent_out();
1951    printf ("}\n");
1952 }
1953 
1954 
1955 static void
do_ncdumpx(int ncid,const char * path)1956 do_ncdumpx(int ncid, const char *path)
1957 {
1958     int ndims;			/* number of dimensions */
1959     int nvars;			/* number of variables */
1960     int ngatts;			/* number of global attributes */
1961     int xdimid;			/* id of unlimited dimension */
1962     int dimid;			/* dimension id */
1963     int varid;			/* variable id */
1964     ncdim_t *dims;		/* dimensions */
1965     ncvar_t var;		/* variable */
1966     int ia;			/* attribute number */
1967     int iv;			/* variable number */
1968     idnode_t* vlist = NULL;     /* list for vars specified with -v option */
1969 
1970     /*
1971      * If any vars were specified with -v option, get list of associated
1972      * variable ids
1973      */
1974     if (formatting_specs.nlvars > 0) {
1975 	vlist = newidlist();	/* list for vars specified with -v option */
1976 	for (iv=0; iv < formatting_specs.nlvars; iv++) {
1977 	    NC_CHECK( nc_inq_varid(ncid, formatting_specs.lvars[iv], &varid) );
1978 	    idadd(vlist, varid);
1979 	}
1980     }
1981 
1982     /* output initial line */
1983     pr_initx(ncid, path);
1984 
1985     /*
1986      * get number of dimensions, number of variables, number of global
1987      * atts, and dimension id of unlimited dimension, if any
1988      */
1989     /* TODO: print names with XML-ish escapes fopr special chars */
1990     NC_CHECK( nc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) );
1991     /* get dimension info */
1992     dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
1993     for (dimid = 0; dimid < ndims; dimid++) {
1994 	NC_CHECK( nc_inq_dim(ncid, dimid, dims[dimid].name, &dims[dimid].size) );
1995 	if (dimid == xdimid)
1996   	  printf("  <dimension name=\"%s\" length=\"%d\" isUnlimited=\"true\" />\n",
1997 		 dims[dimid].name, (int)dims[dimid].size);
1998 	else
1999 	  printf ("  <dimension name=\"%s\" length=\"%d\" />\n",
2000 		  dims[dimid].name, (int)dims[dimid].size);
2001     }
2002 
2003     /* get global attributes */
2004     for (ia = 0; ia < ngatts; ia++)
2005 	pr_attx(ncid, NC_GLOBAL, ia); /* print ia-th global attribute */
2006 
2007     /* get variable info, with variable attributes */
2008     memset((void*)&var,0,sizeof(var));
2009     for (varid = 0; varid < nvars; varid++) {
2010 	NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) );
2011 	if(var.dims != NULL) free(var.dims);
2012 	var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
2013 	NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0,
2014 			     var.dims, &var.natts) );
2015 	printf ("  <variable name=\"%s\"", var.name);
2016 	pr_shape(&var, dims);
2017 
2018 	/* handle one-line variable elements that aren't containers
2019 	   for attributes or data values, since they need to be
2020 	   rendered as <variable ... /> instead of <variable ..>
2021 	   ... </variable> */
2022 	if (var.natts == 0) {
2023 	    if (
2024 		/* header-only specified */
2025 		(formatting_specs.header_only) ||
2026 		/* list of variables specified and this variable not in list */
2027 		(formatting_specs.nlvars > 0 && !idmember(vlist, varid))	||
2028 		/* coordinate vars only and this is not a coordinate variable */
2029 		(formatting_specs.coord_vals && !iscoordvar(ncid, varid)) ||
2030 		/* this is a record variable, but no records have been written */
2031 		(isrecvar(ncid,varid) && dims[xdimid].size == 0)
2032 		) {
2033 		printf (" type=\"%s\" />\n", prim_type_name(var.type));
2034 		continue;
2035 	    }
2036 	}
2037 
2038 	/* else nest attributes values, data values in <variable> ... </variable> */
2039 	printf (" type=\"%s\">\n", prim_type_name(var.type));
2040 
2041 	/* get variable attributes */
2042 	for (ia = 0; ia < var.natts; ia++) {
2043 	    pr_attx(ncid, varid, ia); /* print ia-th attribute */
2044 	}
2045 	printf ("  </variable>\n");
2046     }
2047 
2048     printf ("</netcdf>\n");
2049     if (vlist)
2050 	freeidlist(vlist);
2051     if(dims)
2052 	free(dims);
2053     if(var.dims != NULL)
2054         free(var.dims);
2055 }
2056 
2057 /*
2058  * Extract the significant-digits specifiers from the (deprecated and
2059  * undocumented) -d argument on the command-line and update the
2060  * default data formats appropriately.  This only exists because an
2061  * old version of ncdump supported the "-d" flag which did not
2062  * override the C_format attributes (if any).
2063  */
2064 static void
set_sigdigs(const char * optarg)2065 set_sigdigs(const char *optarg)
2066 {
2067     char *ptr1 = 0;
2068     char *ptr2 = 0;
2069     int flt_digits = FLT_DIGITS; /* default floating-point digits */
2070     int dbl_digits = DBL_DIGITS; /* default double-precision digits */
2071 
2072     if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',')
2073         flt_digits = (int)strtol(optarg, &ptr1, 10);
2074 
2075     if (flt_digits < 1 || flt_digits > 20) {
2076 	error("unreasonable value for float significant digits: %d",
2077 	      flt_digits);
2078     }
2079     if (ptr1 && *ptr1 == ',') {
2080       dbl_digits = (int)strtol(ptr1+1, &ptr2, 10);
2081       if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) {
2082 	  error("unreasonable value for double significant digits: %d",
2083 		dbl_digits);
2084       }
2085     }
2086     set_formats(flt_digits, dbl_digits);
2087 }
2088 
2089 
2090 /*
2091  * Extract the significant-digits specifiers from the -p argument on the
2092  * command-line, set flags so we can override C_format attributes (if any),
2093  * and update the default data formats appropriately.
2094  */
2095 static void
set_precision(const char * optarg)2096 set_precision(const char *optarg)
2097 {
2098     char *ptr1 = 0;
2099     char *ptr2 = 0;
2100     int flt_digits = FLT_DIGITS;	/* default floating-point digits */
2101     int dbl_digits = DBL_DIGITS;	/* default double-precision digits */
2102 
2103     if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') {
2104         flt_digits = (int)strtol(optarg, &ptr1, 10);
2105 	float_precision_specified = 1;
2106     }
2107 
2108     if (flt_digits < 1 || flt_digits > 20) {
2109 	error("unreasonable value for float significant digits: %d",
2110 	      flt_digits);
2111     }
2112     if (ptr1 && *ptr1 == ',') {
2113 	dbl_digits = (int) strtol(ptr1+1, &ptr2, 10);
2114 	double_precision_specified = 1;
2115 	if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) {
2116 	    error("unreasonable value for double significant digits: %d",
2117 		  dbl_digits);
2118 	}
2119     }
2120     set_formats(flt_digits, dbl_digits);
2121 }
2122 
2123 
2124 #ifdef USE_DAP
2125 #define DAP_CLIENT_CACHE_DIRECTIVE	"[cache]"
2126 /* replace path string with same string prefixed by
2127  * DAP_CLIENT_NCDUMP_DIRECTIVE */
2128 static
adapt_url_for_cache(char ** pathp)2129 void adapt_url_for_cache(char **pathp) {
2130     char prefix[] = DAP_CLIENT_CACHE_DIRECTIVE;
2131     char* path = *pathp;
2132     char *tmp_path = strdup(path);
2133     path = (char *)emalloc(strlen(prefix) + strlen(tmp_path) + 1);
2134     path[0] = '\0';
2135     strncat(path, prefix, strlen(prefix));
2136     strncat(path, tmp_path, strlen(tmp_path));
2137     if(tmp_path) free(tmp_path);
2138     if(*path) free(*pathp);
2139     *pathp = path;
2140     return;
2141 }
2142 #endif
2143 
2144 int
main(int argc,char * argv[])2145 main(int argc, char *argv[])
2146 {
2147     int ncstat = NC_NOERR;
2148     int c;
2149     int i;
2150     int max_len = 80;		/* default maximum line length */
2151     int nameopt = 0;
2152     bool_t xml_out = false;    /* if true, output NcML instead of CDL */
2153     bool_t kind_out = false;	/* if true, just output kind of netCDF file */
2154     bool_t kind_out_extended = false;	/* output inq_format vs inq_format_extended */
2155     int Xp_flag = 0;    /* indicate that -Xp flag was set */
2156     char* path = NULL;
2157     char errmsg[4096];
2158 
2159     errmsg[0] = '\0';
2160 
2161 #if defined(WIN32) || defined(msdos) || defined(WIN64)
2162     putenv("PRINTF_EXPONENT_DIGITS=2"); /* Enforce unix/linux style exponent formatting. */
2163 #endif
2164 
2165 #ifdef HAVE_LOCALE_H
2166     setlocale(LC_ALL, "C");     /* CDL may be ambiguous with other locales */
2167 #endif /* HAVE_LOCALE_H */
2168     opterr = 1;
2169     progname = argv[0];
2170     set_formats(FLT_DIGITS, DBL_DIGITS); /* default for float, double data */
2171 
2172     /* If the user called ncdump without arguments, print the usage
2173      * message and return peacefully. */
2174     if (argc <= 1)
2175     {
2176        usage();
2177        exit(EXIT_SUCCESS);
2178     }
2179 
2180     while ((c = getopt(argc, argv, "b:cd:f:g:hikl:n:p:stv:xwKL:X:")) != EOF)
2181       switch(c) {
2182 	case 'h':		/* dump header only, no data */
2183 	  formatting_specs.header_only = true;
2184 	  break;
2185 	case 'c':		/* header, data only for coordinate dims */
2186 	  formatting_specs.coord_vals = true;
2187 	  break;
2188 	case 'n':		/*
2189 				 * provide different name than derived from
2190 				 * file name
2191 				 */
2192 	  formatting_specs.name = optarg;
2193 	  nameopt = 1;
2194 	  break;
2195 	case 'b':		/* brief comments in data section */
2196 	  formatting_specs.brief_data_cmnts = true;
2197 	  switch (tolower((int)optarg[0])) {
2198 	    case 'c':
2199 	      formatting_specs.data_lang = LANG_C;
2200 	      break;
2201 	    case 'f':
2202 	      formatting_specs.data_lang = LANG_F;
2203 	      break;
2204 	    default:
2205 	      snprintf(errmsg,sizeof(errmsg),"invalid value for -b option: %s", optarg);
2206 	      goto fail;
2207 	  }
2208 	  break;
2209 	case 'f':		/* full comments in data section */
2210 	  formatting_specs.full_data_cmnts = true;
2211 	  switch (tolower((int)optarg[0])) {
2212 	    case 'c':
2213 	      formatting_specs.data_lang = LANG_C;
2214 	      break;
2215 	    case 'f':
2216 	      formatting_specs.data_lang = LANG_F;
2217 	      break;
2218 	    default:
2219 	      snprintf(errmsg,sizeof(errmsg),"invalid value for -f option: %s", optarg);
2220 	      goto fail;
2221 	  }
2222 	  break;
2223 	case 'l':		/* maximum line length */
2224 	  max_len = (int) strtol(optarg, 0, 0);
2225 	  if (max_len < 10) {
2226 	      snprintf(errmsg,sizeof(errmsg),"unreasonably small line length specified: %d", max_len);
2227 	      goto fail;
2228 	  }
2229 	  break;
2230 	case 'v':		/* variable names */
2231 	  /* make list of names of variables specified */
2232 	  make_lvars (optarg, &formatting_specs.nlvars, &formatting_specs.lvars);
2233 	  break;
2234 	case 'g':		/* group names */
2235 	  /* make list of names of groups specified */
2236 	  make_lgrps (optarg, &formatting_specs.nlgrps, &formatting_specs.lgrps,
2237 			&formatting_specs.grpids);
2238 	  break;
2239 	case 'd':		/* specify precision for floats (deprecated, undocumented) */
2240 	  set_sigdigs(optarg);
2241 	  break;
2242 	case 'p':		/* specify precision for floats, overrides attribute specs */
2243 	  set_precision(optarg);
2244 	  break;
2245         case 'x':		/* XML output (NcML) */
2246 	  xml_out = true;
2247 	  break;
2248         case 'k':	        /* just output what kind of netCDF file */
2249 	  kind_out = true;
2250 	  break;
2251         case 'K':	        /* extended format info */
2252 	  kind_out_extended = true;
2253 	  break;
2254 	case 't':		/* human-readable strings for date-time values */
2255 	  formatting_specs.string_times = true;
2256 	  formatting_specs.iso_separator = false;
2257 	  break;
2258 	case 'i':		/* human-readable strings for data-time values with 'T' separator */
2259 	  formatting_specs.string_times = true;
2260 	  formatting_specs.iso_separator = true;
2261 	  break;
2262         case 's':	    /* output special (virtual) attributes for
2263 			     * netCDF-4 files and variables, including
2264 			     * _DeflateLevel, _Chunking, _Endianness,
2265 			     * _Format, _Checksum, _NoFill */
2266 	  formatting_specs.special_atts = true;
2267 	  break;
2268         case 'w':		/* with client-side cache for DAP URLs */
2269 	  formatting_specs.with_cache = true;
2270 	  break;
2271         case 'X':		/* special options */
2272 	  switch (tolower((int)optarg[0])) {
2273 	    case 'm':
2274 	      formatting_specs.xopt_inmemory = 1;
2275 	      break;
2276 	    case 'p': /* suppress the properties attribute */
2277 	      Xp_flag = 1; /* record that this flag was set */
2278 	      break;
2279 	    default:
2280 	      snprintf(errmsg,sizeof(errmsg),"invalid value for -X option: %s", optarg);
2281 	      goto fail;
2282 	  }
2283 	  break;
2284         case 'L':
2285 #ifdef LOGGING
2286 	  {
2287 	  int level = atoi(optarg);
2288 	  if(level >= 0)
2289 	    nc_set_log_level(level);
2290 	  }
2291 #endif
2292 	  ncsetlogging(1);
2293 	  break;
2294         case '?':
2295 	  usage();
2296 	  exit(EXIT_FAILURE);
2297       }
2298 
2299     /* Decide xopt_props */
2300     if(formatting_specs.special_atts && Xp_flag == 1)
2301         formatting_specs.xopt_props = 0;
2302     else if(formatting_specs.special_atts && Xp_flag == 0)
2303         formatting_specs.xopt_props = 1;
2304     else if(!formatting_specs.special_atts)
2305 	formatting_specs.xopt_props = 0;
2306     else
2307 	formatting_specs.xopt_props = 0;
2308 
2309     set_max_len(max_len);
2310 
2311     argc -= optind;
2312     argv += optind;
2313 
2314     /* If no file arguments left or more than one, print usage message. */
2315     if (argc != 1)
2316     {
2317        usage();
2318        exit(EXIT_FAILURE);
2319     }
2320 
2321     i = 0;
2322 
2323     init_epsilons();
2324 
2325     path = strdup(argv[i]);
2326     if(!path) {
2327 	snprintf(errmsg,sizeof(errmsg),"out of memory copying argument %s", argv[i]);
2328 	goto fail;
2329     }
2330     if (!nameopt)
2331         formatting_specs.name = name_path(path);
2332     if (argc > 0) {
2333         int ncid;
2334 	/* If path is a URL, prefix with client-side directive to
2335          * make ncdump reasonably efficient */
2336 #ifdef USE_DAP
2337 	    if(formatting_specs.with_cache) { /* by default, don't use cache directive */
2338 		if(nc__testurl(path, NULL)) /* See if this is a url */
2339 		    adapt_url_for_cache(&path);
2340 		/* else fall thru and treat like a file path */
2341 	    }
2342 #endif /*USE_DAP*/
2343 	    if(formatting_specs.xopt_inmemory) {
2344 #if 0
2345 		size_t size = 0;
2346 		void* mem = NULL;
2347 		ncstat = fileopen(path,&mem,&size);
2348 		if(ncstat == NC_NOERR)
2349 	            ncstat = nc_open_mem(path,NC_INMEMORY,size,mem,&ncid);
2350 #else
2351 	        ncstat = nc_open(path,NC_DISKLESS|NC_NOWRITE,&ncid);
2352 #endif
2353 	    } else /* just a file */
2354 	        ncstat = nc_open(path, NC_NOWRITE, &ncid);
2355 	    if (ncstat != NC_NOERR) goto fail;
2356 
2357 	    NC_CHECK( nc_inq_format(ncid, &formatting_specs.nc_kind) );
2358 	    NC_CHECK( nc_inq_format_extended(ncid,
2359                                              &formatting_specs.nc_extended,
2360                                              &formatting_specs.nc_mode) );
2361 	    if (kind_out) {
2362 		printf ("%s", kind_string(formatting_specs.nc_kind));
2363 	    } else if (kind_out_extended) {
2364 		printf ("%s", kind_string_extended(formatting_specs.nc_extended,formatting_specs.nc_mode));
2365 	    } else {
2366 		/* Initialize list of types. */
2367 		init_types(ncid);
2368 		/* Check if any vars in -v don't exist */
2369 		if(missing_vars(ncid, formatting_specs.nlvars, formatting_specs.lvars)) {
2370 		    snprintf(errmsg,sizeof(errmsg),"-v: non-existent variables");
2371 		    goto fail;
2372 		}
2373 		if(formatting_specs.nlgrps > 0) {
2374 		    if(formatting_specs.nc_kind != NC_FORMAT_NETCDF4)
2375 			goto fail;
2376 		    /* Check if any grps in -g don't exist */
2377 		    if(grp_matches(ncid, formatting_specs.nlgrps, formatting_specs.lgrps, formatting_specs.grpids) == 0)
2378 			goto fail;
2379 		}
2380 		if (xml_out) {
2381 		    if(formatting_specs.nc_kind == NC_FORMAT_NETCDF4) {
2382 			snprintf(errmsg,sizeof(errmsg),"NcML output (-x) currently only permitted for netCDF classic model");
2383 			goto fail;
2384 		    }
2385 		    do_ncdumpx(ncid, path);
2386 		} else {
2387 		    do_ncdump(ncid, path);
2388 		}
2389 	    }
2390 	    NC_CHECK( nc_close(ncid) );
2391     }
2392     if(path) {free(path); path = NULL;}
2393     exit(EXIT_SUCCESS);
2394 
2395 fail: /* ncstat failures */
2396     path = (path?path:strdup("<unknown>"));
2397     if(ncstat && strlen(errmsg) == 0)
2398 	snprintf(errmsg,sizeof(errmsg),"%s: %s", path, nc_strerror(ncstat));
2399     if(strlen(errmsg) > 0)
2400 	error("%s: %s", path, errmsg);
2401     if(path) free(path);
2402     exit(EXIT_FAILURE);
2403 }
2404