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