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

Generated on Wed Aug 22 2012 14:39:40 for netCDF. NetCDF is a Unidata library.