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

Generated on Tue Jul 9 2013 19:17:28 for netCDF. NetCDF is a Unidata library.