1 /*********************************************************************
2 * Copyright 1993, University Corporation for Atmospheric Research
3 * See netcdf/README file for copying and redistribution conditions.
4 * $Id$
5 *********************************************************************/
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include <unistd.h> /* for getopt() */
13 #include "local_nc.h"
14
15 #include "ncdump.h"
16 #include "dumplib.h"
17 #include "vardata.h"
18
19 static const char * type_name(nc_type type);
20
21 char *progname;
22 struct ncdim dims[H4_MAX_NC_DIMS]; /* dimensions */
23 long vdims[H4_MAX_NC_DIMS]; /* dimension sizes for a single variable */
24
25 static void
usage()26 usage()
27 {
28 #define USAGE "\
29 [-V] Display version of the HDF4 library and exit\n\
30 [-c] Coordinate variable data and header information\n\
31 [-h] Header information only, no data\n\
32 [-u] Replace nonalpha-numerics in names with underscores\n\
33 [-v var1[,...]] Data for variable(s) <var1>,... only\n\
34 [-b [c|f]] Brief annotations for C or Fortran indices in data\n\
35 [-f [c|f]] Full annotations for C or Fortran indices in data\n\
36 [-l len] Line length maximum in data section (default 80)\n\
37 [-n name] Name for netCDF (default derived from file name)\n\
38 [-d n[,n]] Approximate floating-point values with less precision\n\
39 file File name of input netCDF file\n"
40
41 (void) fprintf(stderr,
42 "%s [-V|-c|-h|-u] [-v ...] [[-b|-f] [c|f]] [-l len] [-n name] [-d n[,n]] file\n%s",
43 progname,
44 USAGE);
45 exit(EXIT_FAILURE);
46 }
47
48
49 /*
50 * convert pathname of netcdf file into name for cdl unit, by taking
51 * last component of path and stripping off any extension.
52 */
53 static char *
name_path(path)54 name_path(path)
55 char *path;
56 {
57 char *cp, *new;
58
59 #ifdef MSDOS
60 #define FILE_DELIMITER '\\'
61 #endif
62 #ifndef FILE_DELIMITER /* default to unix */
63 #define FILE_DELIMITER '/'
64 #endif
65 cp = strrchr(path, FILE_DELIMITER);
66 if (cp == 0) /* no delimiter */
67 cp = path;
68 else /* skip delimeter */
69 cp++;
70 new = (char *) malloc((unsigned) (strlen(cp)+1));
71 if (new == 0) {
72 error("out of memory!");
73 exit(EXIT_FAILURE);
74 }
75 (void) strcpy(new, cp); /* copy last component of path */
76 if ((cp = strrchr(new, '.')) != NULL)
77 *cp = '\0'; /* strip off any extension */
78 return new;
79 }
80
81 static const char *
type_name(type)82 type_name(type)
83 nc_type type;
84 {
85 switch (type) {
86 case NC_BYTE:
87 return "byte";
88 case NC_CHAR:
89 return "char";
90 case NC_SHORT:
91 return "short";
92 case NC_LONG:
93 return "long";
94 case NC_FLOAT:
95 return "float";
96 case NC_DOUBLE:
97 return "double";
98 default:
99 error("type_name: bad type %d", type);
100 return "bogus";
101 }
102 }
103
104 /*
105 * Remove trailing zeros (after decimal point) but not trailing decimal
106 * point from ss, a string representation of a floating-point number that
107 * might include an exponent part.
108 */
109 static void
tztrim(ss)110 tztrim(ss)
111 char *ss; /* returned string representing dd */
112 {
113 char *cp, *ep;
114
115 cp = ss;
116 if (*cp == '-')
117 cp++;
118 while(isdigit((int)*cp) || *cp == '.')
119 cp++;
120 if (*--cp == '.')
121 return;
122 ep = cp+1;
123 while (*cp == '0')
124 cp--;
125 cp++;
126 if (cp == ep)
127 return;
128 while (*ep)
129 *cp++ = *ep++;
130 *cp = '\0';
131 return;
132 }
133
134 /*
135 * Print list of attribute values. Attribute values must be printed with
136 * explicit type tags, because their types are not declared.
137 */
138 static void
pr_att_vals(type,len,vals)139 pr_att_vals(type, len, vals)
140 nc_type type;
141 int len;
142 void *vals;
143 {
144 int iel;
145 union {
146 char *cp;
147 short *sp;
148 nclong *lp;
149 float *fp;
150 double *dp;
151 } gp;
152 char *sp;
153 unsigned char uc;
154 char gps[30]; /* for ascii of a float or double precision */
155 char *f_fmt = "%#.8g";
156 char *d_fmt = "%#.16g";
157
158 switch (type) {
159 case NC_BYTE:
160 gp.cp = (char *) vals;
161 for (iel = 0; iel < len; iel++)
162 if (isprint(uc = *gp.cp++ & 0377))
163 Printf ("'%c'%s", uc, iel<len-1 ? ", " : "");
164 else
165 Printf ("'\\%o'%s", uc, iel<len-1 ? ", " : "");
166 break;
167 case NC_CHAR:
168 gp.cp = (char *) vals;
169 Printf ("\"");
170 /* adjust len so trailing nulls don't get printed */
171 sp = gp.cp + len - 1;
172 while (*sp-- == '\0' && len > 0)
173 len--;
174 for (iel = 0; iel < len; iel++)
175 switch (uc = *gp.cp++ & 0377) {
176 case '\b':
177 Printf ("\\b");
178 break;
179 case '\f':
180 Printf ("\\f");
181 break;
182 case '\n': /* generate linebreaks after new-lines */
183 Printf ("\\n\",\n \"");
184 break;
185 case '\r':
186 Printf ("\\r");
187 break;
188 case '\t':
189 Printf ("\\t");
190 break;
191 case '\v':
192 Printf ("\\v");
193 break;
194 case '\\':
195 Printf ("\\\\");
196 break;
197 case '\'':
198 Printf ("\\'");
199 break;
200 case '\"':
201 Printf ("\\\"");
202 break;
203 default:
204 Printf ("%c",uc);
205 break;
206 }
207 Printf ("\"");
208 break;
209 case NC_SHORT:
210 gp.sp = (short *) vals;
211 for (iel = 0; iel < len; iel++)
212 Printf ("%ds%s",*gp.sp++,iel<len-1 ? ", " : "");
213 break;
214 case NC_LONG:
215 gp.lp = (nclong *) vals;
216 for (iel = 0; iel < len; iel++)
217 Printf ("%d%s",(int)*gp.lp++,iel<len-1 ? ", " : "");
218 break;
219 case NC_FLOAT:
220 gp.fp = (float *) vals;
221 for (iel = 0; iel < len; iel++) {
222 int ll;
223 (void) sprintf(gps, f_fmt, * gp.fp++);
224 /* append a trailing "f" for floating-point attributes */
225 ll = strlen(gps);
226 gps[ll + 1] = '\0';
227 gps[ll] = 'f';
228 tztrim(gps); /* trim trailing 0's after '.' */
229 Printf ("%s%s", gps, iel<len-1 ? ", " : "");
230 }
231 break;
232 case NC_DOUBLE:
233 gp.dp = (double *) vals;
234 for (iel = 0; iel < len; iel++) {
235 (void) sprintf(gps, d_fmt, *gp.dp++);
236 tztrim(gps); /* trim trailing 0's after '.' */
237 Printf ("%s%s", gps, iel<len-1 ? ", " : "");
238 }
239 break;
240 default:
241 error("pr_att_vals: bad type");
242 }
243 }
244
245 /*
246 * fixstr
247 *
248 * If the string contains characters other than alpha-numerics,
249 * an underscore, or a hyphen, convert it to an underscore.
250 *
251 * Modification:
252 * - Added "fix_str" to determine whether the conversion should be
253 * carried out, based on user's request. - bug #934, BMR 7/3/2005
254 */
fixstr(char * str,bool fix_str)255 char *fixstr(char *str, bool fix_str)
256 {
257 #ifndef __GNUC__
258 char *strdup(const char *);
259 #endif /* linux */
260 char *new_str, *ptr;
261
262 if (!str)
263 return NULL;
264
265 ptr = new_str = strdup(str);
266
267 if (!ptr) {
268 error("Out of memory!");
269 return NULL;
270 }
271
272 if (fix_str)
273 {
274 for (; *ptr; ptr++)
275 if (!isalnum(*ptr) && *ptr != '_' && *ptr != '-')
276 *ptr = '_';
277 }
278
279 return new_str;
280 }
281
282 static void
do_ncdump(char * path,struct fspec * specp)283 do_ncdump(char *path, struct fspec* specp)
284 /* char *path;
285 struct fspec* specp;
286 */
287 {
288 int ndims; /* number of dimensions */
289 int nvars; /* number of variables */
290 int ngatts; /* number of global attributes */
291 int xdimid; /* id of unlimited dimension */
292 int dimid; /* dimension id */
293 int varid; /* variable id */
294 struct ncvar var; /* variable */
295 struct ncatt att; /* attribute */
296 int id; /* dimension number per variable */
297 int ia; /* attribute number */
298 int iv; /* variable number */
299 int is_coord; /* true if variable is a coordinate variable */
300 int isempty = 0; /* true if an old hdf dim has no scale values */
301
302 int ncid = ncopen(path, NC_NOWRITE); /* netCDF id */
303 vnode* vlist = newvlist(); /* list for vars specified with -v option */
304
305 /* don't crash on error */
306 ncopts = 0;
307
308 if (ncid == -1) {
309 error("ncopen failed on %s", path);
310 return;
311 }
312
313 /*
314 * If any vars were specified with -v option, get list of associated
315 * variable ids
316 */
317 for (iv = 0; iv < specp->nlvars; iv++) {
318 varid = ncvarid(ncid, specp->lvars[iv]);
319 varadd(vlist, varid);
320 }
321
322 /* if name not specified, derive it from path */
323 if (specp->name == NULL)
324 specp->name = name_path (path);
325
326 Printf ("netcdf %s {\n", specp->name);
327
328 /*
329 * get number of dimensions, number of variables, number of global
330 * atts, and dimension id of unlimited dimension, if any
331 */
332 (void)ncinquire(ncid, &ndims, &nvars, &ngatts, &xdimid);
333
334 /* get dimension info */
335 if (ndims > 0) {
336 Printf ("dimensions:\n");
337
338 for (dimid = 0; dimid < ndims; dimid++) {
339 char *fixed_str;
340
341 (void)ncdiminq(ncid, dimid, dims[dimid].name,
342 &dims[dimid].size);
343 fixed_str = fixstr(dims[dimid].name, specp->fix_str);
344
345 if (!fixed_str && dims[dimid].name) {
346 /* strdup(3) failed */
347 (void) ncclose(ncid);
348 return;
349 }
350
351 if (dimid == xdimid)
352 Printf ("\t%s = %s ; // (%d currently)\n",
353 fixed_str, "UNLIMITED",
354 (int)dims[dimid].size);
355 else
356 Printf ("\t%s = %ld ;\n",
357 fixed_str, dims[dimid].size);
358
359 free(fixed_str);
360 }
361 }
362
363 Printf ("\nvariables:\n");
364
365 /* get variable info, with variable attributes */
366 for (varid = 0; varid < nvars; varid++) {
367 char *fixed_var;
368
369 (void) ncvarinq(ncid, varid, var.name, &var.type, &var.ndims,
370 var.dims, &var.natts);
371 fixed_var = fixstr(var.name, specp->fix_str);
372
373 if (!fixed_var && var.name) {
374 /* strdup(3) failed */
375 (void) ncclose(ncid);
376 return;
377 }
378
379 Printf ("\t%s %s", type_name(var.type), fixed_var);
380
381 if (var.ndims > 0)
382 Printf ("(");
383
384 for (id = 0; id < var.ndims; id++) {
385 char *fixed_dim = fixstr(dims[var.dims[id]].name,
386 specp->fix_str);
387
388 if (!fixed_dim && dims[var.dims[id]].name) {
389 /* strdup(3) failed */
390 (void) ncclose(ncid);
391 free(fixed_var);
392 return;
393 }
394
395 Printf ("%s%s", fixed_dim,
396 id < var.ndims - 1 ? ", " : ")");
397 free(fixed_dim);
398 }
399
400 Printf (" ;\n");
401
402 /* get variable attributes */
403 for (ia = 0; ia < var.natts; ia++) {
404 char *fixed_att;
405
406 (void) ncattname(ncid, varid, ia, att.name);
407 fixed_att = fixstr(att.name, specp->fix_str);
408
409 if (!fixed_att) {
410 (void) ncclose(ncid);
411 free(fixed_var);
412 return;
413 }
414
415 Printf ("\t\t%s:%s = ", fixed_var, fixed_att);
416 (void) ncattinq(ncid, varid, att.name,
417 &att.type, &att.len);
418 att.val = (void *) malloc((unsigned)att.len * nctypelen(att.type));
419
420 if (!att.val) {
421 error("Out of memory!");
422 (void) ncclose(ncid);
423 free(fixed_att);
424 free(fixed_var);
425 return;
426 }
427
428 (void) ncattget(ncid, varid, att.name, att.val);
429 pr_att_vals(att.type, att.len, att.val);
430 Printf (" ;\n");
431 free(att.val);
432 free(fixed_att);
433 }
434
435 free(fixed_var);
436 }
437
438 /* get global attributes */
439 if (ngatts > 0)
440 Printf ("\n// global attributes:\n");
441
442 for (ia = 0; ia < ngatts; ia++) {
443 char *fixed_att;
444
445 (void) ncattname(ncid, NC_GLOBAL, ia, att.name);
446 fixed_att = fixstr(att.name, specp->fix_str);
447
448 if (!fixed_att) {
449 (void) ncclose(ncid);
450 return;
451 }
452
453 Printf ("\t\t:%s = ", fixed_att);
454
455 (void) ncattinq(ncid, NC_GLOBAL, att.name,
456 &att.type, &att.len);
457 att.val = malloc((unsigned)(att.len * nctypelen(att.type)));
458
459 if (!att.val) {
460 error("Out of memory!");
461 (void) ncclose(ncid);
462 free(fixed_att);
463 return;
464 }
465
466 (void) ncattget(ncid, NC_GLOBAL, att.name, att.val);
467 pr_att_vals(att.type, att.len, att.val);
468 Printf (" ;\n");
469 free(att.val);
470 free(fixed_att);
471 }
472
473 if (! specp->header_only) {
474 if (nvars > 0)
475 Printf ("\ndata:\n");
476
477 /* output variable data */
478 for (varid = 0; varid < nvars; varid++) {
479 /* if var list specified, test for membership */
480 if (specp->nlvars > 0 && ! varmember(vlist, varid))
481 continue;
482
483 (void) ncvarinq(ncid, varid, var.name, &var.type,
484 &var.ndims, var.dims, &var.natts);
485
486 if (specp->coord_vals) {
487 /* Find out if this is a coordinate variable */
488 is_coord = 0;
489
490 for (dimid = 0; dimid < ndims; dimid++) {
491 if (strcmp(dims[dimid].name, var.name) == 0 &&
492 var.ndims == 1) {
493 is_coord = 1;
494 break;
495 }
496 }
497
498 if (! is_coord)
499 /* don't get data for non-coordinate vars */
500 continue;
501 }
502
503 /*
504 * Only get data for variable if it is not a record variable,
505 * or if it is a record variable and at least one record has
506 * been written.
507 */
508 #ifdef HDF
509 /* skip the dimension vars which have dim strings only. */
510 {
511 NC *handle ;
512 NC_var *vp;
513 #ifdef OLD_WAY
514 NC_var *NC_hlookupvar() ;
515 #endif /* OLD_WAY */
516
517 isempty = 0;
518 handle = NC_check_id(ncid);
519
520 if (handle->file_type == HDF_FILE) {
521 vp = NC_hlookupvar(handle, varid) ;
522 /* This is set up to take care of
523 * cases where an array has been
524 * defined but no data */
525 /* has yet been added. */
526 if ((vp->data_tag == DFTAG_SDS ||
527 vp->data_tag == DFTAG_SD) &&
528 (vp->data_ref == 0))
529 isempty = 1;
530 }
531 }
532
533 #endif /* HDF */
534 if (isempty)
535 continue;
536
537 if (var.ndims == 0 || var.dims[0] != xdimid || dims[xdimid].size != 0) {
538 int ret = 0;
539
540 /* Collect variable's dim sizes */
541 for (id = 0; id < var.ndims; id++)
542 vdims[id] = dims[var.dims[id]].size;
543
544 /* if (vardata(&var, vdims, ncid, varid, specp) == -1)
545 */
546 ret = vardata(&var, vdims, ncid, varid, specp);
547 /* Keep the original failure detection until further improvement */
548 if (ret == -1) {
549 error("can't output data for variable %s", var.name);
550 (void) ncclose(ncid);
551 return;
552 }
553 /* This failure indicates that vardata failed
554 because ncvarget fails. (HDFFR-1468)
555 -BMR, 2015/01/20) */
556 if (ret == ERR_READFAIL) {
557 error("Reading failed for variable %s, possibly the data is corrupted.", var.name);
558 (void) ncclose(ncid);
559 return;
560 }
561 }
562 }
563 }
564
565 Printf ("}\n");
566 (void) ncclose(ncid);
567 }
568
569 static void
make_lvars(optarg,fspecp)570 make_lvars(optarg, fspecp)
571 char *optarg;
572 struct fspec* fspecp;
573 {
574 char *cp = optarg;
575 int nvars = 1;
576 char ** cpp;
577
578 /* compute number of variable names in comma-delimited list */
579 fspecp->nlvars = 1;
580 while (*cp++)
581 if (*cp == ',')
582 nvars++;
583
584 fspecp->lvars = (char **) malloc(nvars * sizeof(char*));
585 if (!fspecp->lvars) {
586 error("out of memory");
587 exit(EXIT_FAILURE);
588 }
589
590 cpp = fspecp->lvars;
591 /* copy variable names into list */
592 for (cp = strtok(optarg, ",");
593 cp != NULL;
594 cp = strtok((char *) NULL, ",")) {
595
596 *cpp = (char *) malloc(strlen(cp) + 1);
597 if (!*cpp) {
598 error("out of memory");
599 exit(EXIT_FAILURE);
600 }
601 strcpy(*cpp, cp);
602 cpp++;
603 }
604 fspecp->nlvars = nvars;
605 }
606
607
608 /*
609 * Extract the significant-digits specifiers from the -d argument on the
610 * command-line and update the default data formats appropriately.
611 */
612 static void
set_sigdigs(optarg)613 set_sigdigs(optarg)
614 char *optarg;
615 {
616 char *ptr = optarg;
617 char *ptr2 = 0;
618 long flt_digits = 7; /* default floating-point digits */
619 long dbl_digits = 15; /* default double-precision digits */
620 char flt_fmt[6];
621 char dbl_fmt[6];
622
623 if (optarg != 0 && strlen(optarg) > 0 && optarg[0] != ',')
624 flt_digits=strtol(optarg, &ptr, 10);
625
626 if (flt_digits < 1 || flt_digits > 10) {
627 error("unreasonable value for float significant digits: %d",
628 flt_digits);
629 exit(EXIT_FAILURE);
630 }
631 if (*ptr == ',')
632 dbl_digits = strtol(ptr+1, &ptr2, 10);
633 if (ptr2 == ptr+1 || dbl_digits < 1 || dbl_digits > 20) {
634 error("unreasonable value for double significant digits: %d",
635 dbl_digits);
636 exit(EXIT_FAILURE);
637 }
638 (void) sprintf(flt_fmt, "%%.%dg", (int)flt_digits);
639 (void) sprintf(dbl_fmt, "%%.%dg", (int)dbl_digits);
640 set_formats(flt_fmt, dbl_fmt);
641 }
642
643
644 int
main(argc,argv)645 main(argc, argv)
646 int argc;
647 char *argv[];
648 {
649 extern int optind;
650 extern int opterr;
651 extern char *optarg;
652 static struct fspec fspec = /* defaults, overridden on command line */
653 {
654 0, /* construct netcdf name from file name */
655 false, /* print header info only, no data? */
656 false, /* just print coord vars? */
657 false, /* brief comments in data section? */
658 false, /* full annotations in data section? */
659 false, /* replace nonalpha-numeric with underscore? */
660 LANG_NONE, /* language conventions for indices */
661 0, /* if -v specified, number of variables */
662 0 /* if -v specified, list of variable names */
663 };
664 int c;
665 int i;
666 int max_len = 80; /* default maximum line length */
667
668 opterr = 1;
669 progname = argv[0];
670
671 if (1 == argc) /* if no arguments given, print help and exit */
672 usage();
673
674 while ((c = getopt(argc, argv, "b:cf:hul:n:v:d:V")) != EOF)
675 switch(c) {
676 case 'V': /* display version of the library */
677 printf("%s, %s\n\n", argv[0], LIBVER_STRING );
678 exit(EXIT_SUCCESS);
679 case 'h': /* dump header only, no data */
680 fspec.header_only = true;
681 break;
682 case 'c': /* header, data only for coordinate dims */
683 fspec.coord_vals = true;
684 break;
685 case 'n': /*
686 * provide different name than derived from
687 * file name
688 */
689 fspec.name = optarg;
690 break;
691 case 'u': /* replace nonalpha-numerics with underscores */
692 fspec.fix_str = true;
693 break;
694 case 'b': /* brief comments in data section */
695 fspec.brief_data_cmnts = true;
696 switch (tolower(optarg[0])) {
697 case 'c':
698 fspec.data_lang = LANG_C;
699 break;
700 case 'f':
701 fspec.data_lang = LANG_F;
702 break;
703 default:
704 error("invalid value for -b option: %s", optarg);
705 exit(EXIT_FAILURE);
706 }
707 break;
708 case 'f': /* full comments in data section */
709 fspec.full_data_cmnts = true;
710 switch (tolower(optarg[0])) {
711 case 'c':
712 fspec.data_lang = LANG_C;
713 break;
714 case 'f':
715 fspec.data_lang = LANG_F;
716 break;
717 default:
718 error("invalid value for -b option: %s", optarg);
719 exit(EXIT_FAILURE);
720 }
721 break;
722 case 'l': /* maximum line length */
723 max_len = strtol(optarg, 0, 0);
724 if (max_len < 10) {
725 error("unreasonably small line length specified: %d", max_len);
726 exit(EXIT_FAILURE);
727 }
728 break;
729 case 'v': /* variable names */
730 /* make list of names of variables specified */
731 make_lvars (optarg, &fspec);
732 break;
733 case 'd': /* specify precision for floats */
734 #ifdef OLD_WAY
735 set_sigdigs(optarg, &fspec);
736 #else /* OLD_WAY */
737 set_sigdigs(optarg);
738 #endif /* OLD_WAY */
739 break;
740 case '?':
741 usage();
742 break;
743 }
744
745 set_max_len(max_len);
746
747 argc -= optind;
748 argv += optind;
749
750 i = 0;
751 do {
752 if (argc > 0)
753 do_ncdump(argv[i], &fspec);
754 } while (++i < argc);
755 return EXIT_SUCCESS;
756 }
757