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 (""");
505 break;
506 case '<':
507 printf ("<");
508 break;
509 case '>':
510 printf (">");
511 break;
512 case '&':
513 printf ("&");
514 break;
515 case '\n':
516 printf ("
");
517 break;
518 case '\r':
519 printf ("
");
520 break;
521 case '\t':
522 printf ("	");
523 break;
524 case '\0':
525 printf ("�");
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