netCDF 4.2.1.1
|
00001 /* 00002 00003 Copyright 2011 University Corporation for Atmospheric 00004 Research/Unidata. See \ref copyright file for more info. */ 00005 00006 #include <config.h> 00007 #include <stdio.h> 00008 #ifdef HAVE_GETOPT_H 00009 #include <getopt.h> 00010 #endif 00011 #ifndef _WIN32 00012 #include <unistd.h> 00013 #endif 00014 #include <stdlib.h> 00015 #include <string.h> 00016 #include <ctype.h> 00017 #include <assert.h> 00018 #include <math.h> 00019 #ifdef HAVE_LOCALE_H 00020 #include <locale.h> 00021 #endif /* HAVE_LOCALE_H */ 00022 #include <netcdf.h> 00023 #include "utils.h" 00024 #include "nccomps.h" 00025 #include "nctime0.h" /* new iso time and calendar stuff */ 00026 #include "dumplib.h" 00027 #include "ncdump.h" 00028 #include "vardata.h" 00029 #include "indent.h" 00030 #include "isnan.h" 00031 #include "cdl.h" 00032 00033 #define int64_t long long 00034 #define uint64_t unsigned long long 00035 00036 /* globals */ 00037 char *progname; 00038 fspec_t formatting_specs = /* defaults, overridden by command-line options */ 00039 { 00040 0, /* construct netcdf name from file name */ 00041 false, /* print header info only, no data? */ 00042 false, /* just print coord vars? */ 00043 false, /* brief comments in data section? */ 00044 false, /* full annotations in data section? */ 00045 false, /* human-readable output for date-time values? */ 00046 false, /* use 'T' separator between date and time values as strings? */ 00047 false, /* output special attributes, eg chunking? */ 00048 LANG_C, /* language conventions for indices */ 00049 false, /* for DAP URLs, client-side cache used */ 00050 0, /* if -v specified, number of variables in list */ 00051 0, /* if -v specified, list of variable names */ 00052 0, /* if -g specified, number of groups names in list */ 00053 0, /* if -g specified, list of group names */ 00054 0, /* if -g specified, list of matching grpids */ 00055 0 /* kind of netCDF file */ 00056 }; 00057 00058 static void 00059 usage(void) 00060 { 00061 #define USAGE "\ 00062 [-c] Coordinate variable data and header information\n\ 00063 [-h] Header information only, no data\n\ 00064 [-v var1[,...]] Data for variable(s) <var1>,... only\n\ 00065 [-b [c|f]] Brief annotations for C or Fortran indices in data\n\ 00066 [-f [c|f]] Full annotations for C or Fortran indices in data\n\ 00067 [-l len] Line length maximum in data section (default 80)\n\ 00068 [-n name] Name for netCDF (default derived from file name)\n\ 00069 [-p n[,n]] Display floating-point values with less precision\n\ 00070 [-k] Output kind of netCDF file\n\ 00071 [-s] Output special (virtual) attributes\n\ 00072 [-t] Output time data as date-time strings\n\ 00073 [-i] Output time data as date-time strings with ISO-8601 'T' separator\n\ 00074 [-g grp1[,...]] Data and metadata for group(s) <grp1>,... only\n\ 00075 [-w] With client-side caching of variables for DAP URLs\n\ 00076 [-x] Output XML (NcML) instead of CDL\n\ 00077 file Name of netCDF file (or URL if DAP access enabled)\n" 00078 00079 (void) fprintf(stderr, 00080 "%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", 00081 progname, 00082 USAGE); 00083 00084 (void) fprintf(stderr, 00085 "netcdf library version %s\n", 00086 nc_inq_libvers()); 00087 } 00088 00089 00090 /* 00091 * convert pathname of netcdf file into name for cdl unit, by taking 00092 * last component of path and stripping off any extension. 00093 * DMH: add code to handle OPeNDAP url. 00094 * DMH: I think this also works for UTF8. 00095 */ 00096 static char * 00097 name_path(const char *path) 00098 { 00099 const char *cp; 00100 char *new; 00101 char *sp; 00102 00103 #ifdef vms 00104 #define FILE_DELIMITER ']' 00105 #endif 00106 #if defined(WIN32) || defined(msdos) 00107 #define FILE_DELIMITER '\\' 00108 #endif 00109 #ifndef FILE_DELIMITER /* default to unix */ 00110 #define FILE_DELIMITER '/' 00111 #endif 00112 00113 #ifdef USE_DAP 00114 /* See if this is a url */ 00115 { 00116 char* base; 00117 00118 extern int nc__testurl(const char*,char**); 00119 00120 00121 if(nc__testurl(path,&base)) { 00122 return base; /* Looks like a url */ 00123 } 00124 /* else fall thru and treat like a file path */ 00125 } 00126 #endif /*USE_DAP*/ 00127 00128 cp = strrchr(path, FILE_DELIMITER); 00129 if (cp == 0) /* no delimiter */ 00130 cp = path; 00131 else /* skip delimeter */ 00132 cp++; 00133 new = (char *) emalloc((unsigned) (strlen(cp)+1)); 00134 (void) strncpy(new, cp, strlen(cp) + 1); /* copy last component of path */ 00135 if ((sp = strrchr(new, '.')) != NULL) 00136 *sp = '\0'; /* strip off any extension */ 00137 return new; 00138 } 00139 00140 /* Return primitive type name */ 00141 static const char * 00142 prim_type_name(nc_type type) 00143 { 00144 switch (type) { 00145 case NC_BYTE: 00146 return "byte"; 00147 case NC_CHAR: 00148 return "char"; 00149 case NC_SHORT: 00150 return "short"; 00151 case NC_INT: 00152 return "int"; 00153 case NC_FLOAT: 00154 return "float"; 00155 case NC_DOUBLE: 00156 return "double"; 00157 #ifdef USE_NETCDF4 00158 case NC_UBYTE: 00159 return "ubyte"; 00160 case NC_USHORT: 00161 return "ushort"; 00162 case NC_UINT: 00163 return "uint"; 00164 case NC_INT64: 00165 return "int64"; 00166 case NC_UINT64: 00167 return "uint64"; 00168 case NC_STRING: 00169 return "string"; 00170 case NC_VLEN: 00171 return "vlen"; 00172 case NC_OPAQUE: 00173 return "opaque"; 00174 case NC_COMPOUND: 00175 return "compound"; 00176 #endif /* USE_NETCDF4 */ 00177 default: 00178 error("prim_type_name: bad type %d", type); 00179 return "bogus"; 00180 } 00181 } 00182 00183 00184 /* 00185 * Remove trailing zeros (after decimal point) but not trailing decimal 00186 * point from ss, a string representation of a floating-point number that 00187 * might include an exponent part. 00188 */ 00189 static void 00190 tztrim(char *ss) 00191 { 00192 char *cp, *ep; 00193 00194 cp = ss; 00195 if (*cp == '-') 00196 cp++; 00197 while(isdigit((int)*cp) || *cp == '.') 00198 cp++; 00199 if (*--cp == '.') 00200 return; 00201 ep = cp+1; 00202 while (*cp == '0') 00203 cp--; 00204 cp++; 00205 if (cp == ep) 00206 return; 00207 while (*ep) 00208 *cp++ = *ep++; 00209 *cp = '\0'; 00210 return; 00211 } 00212 00213 00214 /* Return file type string */ 00215 static const char * 00216 kind_string(int kind) 00217 { 00218 switch (kind) { 00219 case NC_FORMAT_CLASSIC: 00220 return "classic"; 00221 case NC_FORMAT_64BIT: 00222 return "64-bit offset"; 00223 case NC_FORMAT_NETCDF4: 00224 return "netCDF-4"; 00225 case NC_FORMAT_NETCDF4_CLASSIC: 00226 return "netCDF-4 classic model"; 00227 default: 00228 error("unrecognized file format: %d"); 00229 return "unrecognized"; 00230 } 00231 } 00232 00233 00234 /* 00235 * Emit initial line of output for NcML 00236 */ 00237 static void 00238 pr_initx(int ncid, const char *path) 00239 { 00240 printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<netcdf xmlns=\"http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2\" location=\"%s\">\n", 00241 path); 00242 } 00243 00244 00245 /* 00246 * Print attribute string, for text attributes. 00247 */ 00248 static void 00249 pr_att_string( 00250 int kind, 00251 size_t len, 00252 const char *string 00253 ) 00254 { 00255 int iel; 00256 const char *cp; 00257 const char *sp; 00258 unsigned char uc; 00259 00260 cp = string; 00261 printf ("\""); 00262 /* adjust len so trailing nulls don't get printed */ 00263 sp = cp + len - 1; 00264 while (len != 0 && *sp-- == '\0') 00265 len--; 00266 for (iel = 0; iel < len; iel++) 00267 switch (uc = *cp++ & 0377) { 00268 case '\b': 00269 printf ("\\b"); 00270 break; 00271 case '\f': 00272 printf ("\\f"); 00273 break; 00274 case '\n': 00275 /* Only generate linebreaks after embedded newlines for 00276 * classic, 64-bit offset, or classic model files. For 00277 * netCDF-4 files, don't generate linebreaks, because that 00278 * would create an extra string in a list of strings. */ 00279 if (kind != NC_FORMAT_NETCDF4) { 00280 printf ("\\n\",\n\t\t\t\""); 00281 } else { 00282 printf("\\n"); 00283 } 00284 break; 00285 case '\r': 00286 printf ("\\r"); 00287 break; 00288 case '\t': 00289 printf ("\\t"); 00290 break; 00291 case '\v': 00292 printf ("\\v"); 00293 break; 00294 case '\\': 00295 printf ("\\\\"); 00296 break; 00297 case '\'': 00298 printf ("\\'"); 00299 break; 00300 case '\"': 00301 printf ("\\\""); 00302 break; 00303 default: 00304 if (iscntrl(uc)) 00305 printf ("\\%03o",uc); 00306 else 00307 printf ("%c",uc); 00308 break; 00309 } 00310 printf ("\""); 00311 00312 } 00313 00314 00315 /* 00316 * Print NcML attribute string, for text attributes. 00317 */ 00318 static void 00319 pr_attx_string( 00320 size_t len, 00321 const char *string 00322 ) 00323 { 00324 int iel; 00325 const char *cp; 00326 const char *sp; 00327 unsigned char uc; 00328 00329 cp = string; 00330 printf ("\""); 00331 /* adjust len so trailing nulls don't get printed */ 00332 sp = cp + len - 1; 00333 while (len != 0 && *sp-- == '\0') 00334 len--; 00335 for (iel = 0; iel < len; iel++) 00336 switch (uc = *cp++ & 0377) { 00337 case '\"': 00338 printf ("""); 00339 break; 00340 case '<': 00341 printf ("<"); 00342 break; 00343 case '>': 00344 printf (">"); 00345 break; 00346 case '&': 00347 printf ("&"); 00348 break; 00349 case '\n': 00350 printf ("
"); 00351 break; 00352 case '\r': 00353 printf ("
"); 00354 break; 00355 case '\t': 00356 printf ("	"); 00357 break; 00358 default: 00359 if (iscntrl(uc)) 00360 printf ("&#%d;",uc); 00361 else 00362 printf ("%c",uc); 00363 break; 00364 } 00365 printf ("\""); 00366 00367 } 00368 00369 00370 /* 00371 * Print list of attribute values, for attributes of primitive types. 00372 * Attribute values must be printed with explicit type tags for 00373 * netCDF-3 primitive types, because CDL doesn't require explicit 00374 * syntax to declare such attribute types. 00375 */ 00376 static void 00377 pr_att_valgs( 00378 int kind, 00379 nc_type type, 00380 size_t len, 00381 const void *vals 00382 ) 00383 { 00384 int iel; 00385 signed char sc; 00386 short ss; 00387 int ii; 00388 char gps[PRIM_LEN]; 00389 float ff; 00390 double dd; 00391 #ifdef USE_NETCDF4 00392 unsigned char uc; 00393 unsigned short us; 00394 unsigned int ui; 00395 int64_t i64; 00396 uint64_t ui64; 00397 char *stringp; 00398 #endif /* USE_NETCDF4 */ 00399 char *delim = ", "; /* delimiter between output values */ 00400 00401 if (type == NC_CHAR) { 00402 char *cp = (char *) vals; 00403 pr_att_string(kind, len, cp); 00404 return; 00405 } 00406 /* else */ 00407 for (iel = 0; iel < len; iel++) { 00408 if (iel == len - 1) 00409 delim = ""; 00410 switch (type) { 00411 case NC_BYTE: 00412 sc = ((signed char *) vals)[iel]; 00413 printf ("%db%s", sc, delim); 00414 break; 00415 case NC_SHORT: 00416 ss = ((short *) vals)[iel]; 00417 printf ("%ds%s", ss, delim); 00418 break; 00419 case NC_INT: 00420 ii = ((int *) vals)[iel]; 00421 printf ("%d%s", ii, delim); 00422 break; 00423 case NC_FLOAT: 00424 ff = ((float *) vals)[iel]; 00425 if(isfinite(ff)) { 00426 int res; 00427 res = snprintf(gps, PRIM_LEN, float_att_fmt, ff); 00428 assert(res < PRIM_LEN); 00429 tztrim(gps); /* trim trailing 0's after '.' */ 00430 printf ("%s%s", gps, delim); 00431 } else { 00432 if(isnan(ff)) { 00433 printf("NaNf%s", delim); 00434 } else if(isinf(ff)) { 00435 if(ff < 0.0f) { 00436 printf("-"); 00437 } 00438 printf("Infinityf%s", delim); 00439 } 00440 } 00441 break; 00442 case NC_DOUBLE: 00443 dd = ((double *) vals)[iel]; 00444 if(isfinite(dd)) { 00445 int res; 00446 res = snprintf(gps, PRIM_LEN, double_att_fmt, dd); 00447 assert(res < PRIM_LEN); 00448 tztrim(gps); 00449 printf ("%s%s", gps, delim); 00450 } else { 00451 if(isnan(dd)) { 00452 printf("NaN%s", delim); 00453 } else if(isinf(dd)) { 00454 if(dd < 0.0) { 00455 printf("-"); 00456 } 00457 printf("Infinity%s", delim); 00458 } 00459 } 00460 break; 00461 #ifdef USE_NETCDF4 00462 case NC_UBYTE: 00463 uc = ((unsigned char *) vals)[iel]; 00464 printf ("%uUB%s", uc, delim); 00465 break; 00466 case NC_USHORT: 00467 us = ((unsigned short *) vals)[iel]; 00468 printf ("%huUS%s", us, delim); 00469 break; 00470 case NC_UINT: 00471 ui = ((unsigned int *) vals)[iel]; 00472 printf ("%uU%s", ui, delim); 00473 break; 00474 case NC_INT64: 00475 i64 = ((int64_t *) vals)[iel]; 00476 printf ("%lldL%s", i64, delim); 00477 break; 00478 case NC_UINT64: 00479 ui64 = ((uint64_t *) vals)[iel]; 00480 printf ("%lluUL%s", ui64, delim); 00481 break; 00482 case NC_STRING: 00483 stringp = ((char **) vals)[iel]; 00484 pr_att_string(kind, strlen(stringp), stringp); 00485 printf("%s", delim); 00486 break; 00487 #endif /* USE_NETCDF4 */ 00488 default: 00489 error("pr_att_vals: bad type"); 00490 } 00491 } 00492 } 00493 00494 00495 /* 00496 * Print list of numeric attribute values to string for use in NcML output. 00497 * Unlike CDL, NcML makes type explicit, so don't need type suffixes. 00498 */ 00499 static void 00500 pr_att_valsx( 00501 nc_type type, 00502 size_t len, 00503 const double *vals, 00504 char *attvals, /* returned string */ 00505 size_t attvalslen /* size of attvals buffer, assumed 00506 large enough to hold all len 00507 blank-separated values */ 00508 ) 00509 { 00510 int iel; 00511 float ff; 00512 double dd; 00513 int ii; 00514 #ifdef USE_NETCDF4 00515 unsigned int ui; 00516 int64_t i64; 00517 uint64_t ui64; 00518 #endif /* USE_NETCDF4 */ 00519 00520 attvals[0]='\0'; 00521 if (len == 0) 00522 return; 00523 for (iel = 0; iel < len; iel++) { 00524 char gps[PRIM_LEN]; 00525 int res; 00526 switch (type) { 00527 case NC_BYTE: 00528 case NC_SHORT: 00529 case NC_INT: 00530 ii = vals[iel]; 00531 res = snprintf(gps, PRIM_LEN, "%d", ii); 00532 assert(res < PRIM_LEN); 00533 (void) strlcat(attvals, gps, attvalslen); 00534 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00535 break; 00536 #ifdef USE_NETCDF4 00537 case NC_UBYTE: 00538 case NC_USHORT: 00539 case NC_UINT: 00540 ui = vals[iel]; 00541 res = snprintf(gps, PRIM_LEN, "%u", ui); 00542 assert(res < PRIM_LEN); 00543 (void) strlcat(attvals, gps, attvalslen); 00544 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00545 break; 00546 case NC_INT64: 00547 i64 = vals[iel]; 00548 res = snprintf(gps, PRIM_LEN, "%lld", i64); 00549 assert(res < PRIM_LEN); 00550 (void) strlcat(attvals, gps, attvalslen); 00551 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00552 break; 00553 case NC_UINT64: 00554 ui64 = vals[iel]; 00555 res = snprintf(gps, PRIM_LEN, "%llu", ui64); 00556 assert(res < PRIM_LEN); 00557 (void) strlcat(attvals, gps, attvalslen); 00558 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00559 break; 00560 #endif /* USE_NETCDF4 */ 00561 case NC_FLOAT: 00562 ff = vals[iel]; 00563 res = snprintf(gps, PRIM_LEN, float_attx_fmt, ff); 00564 assert(res < PRIM_LEN); 00565 tztrim(gps); /* trim trailing 0's after '.' */ 00566 (void) strlcat(attvals, gps, attvalslen); 00567 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00568 break; 00569 case NC_DOUBLE: 00570 dd = vals[iel]; 00571 res = snprintf(gps, PRIM_LEN, double_att_fmt, dd); 00572 assert(res < PRIM_LEN); 00573 tztrim(gps); /* trim trailing 0's after '.' */ 00574 (void) strlcat(attvals, gps, attvalslen); 00575 (void) strlcat(attvals, iel < len-1 ? " " : "", attvalslen); 00576 break; 00577 default: 00578 error("pr_att_valsx: bad type"); 00579 } 00580 } 00581 } 00582 00583 /* 00584 * Print a variable attribute 00585 */ 00586 static void 00587 pr_att( 00588 int ncid, 00589 int kind, 00590 int varid, 00591 const char *varname, 00592 int ia 00593 ) 00594 { 00595 ncatt_t att; /* attribute */ 00596 00597 NC_CHECK( nc_inq_attname(ncid, varid, ia, att.name) ); 00598 NC_CHECK( nc_inq_att(ncid, varid, att.name, &att.type, &att.len) ); 00599 att.tinfo = get_typeinfo(att.type); 00600 00601 indent_out(); 00602 printf ("\t\t"); 00603 #ifdef USE_NETCDF4 00604 if (is_user_defined_type(att.type) || att.type == NC_STRING) 00605 #else 00606 if (is_user_defined_type(att.type)) 00607 #endif 00608 { 00609 /* TODO: omit next two lines if att_type_name not needed 00610 * because print_type_name() looks it up */ 00611 char att_type_name[NC_MAX_NAME + 1]; 00612 get_type_name(ncid, att.type, att_type_name); 00613 00614 /* printf ("\t\t%s ", att_type_name); */ 00615 /* ... but handle special characters in CDL names with escapes */ 00616 print_type_name(ncid, att.type); 00617 printf(" "); 00618 } 00619 /* printf ("\t\t%s:%s = ", varname, att.name); */ 00620 print_name(varname); 00621 printf(":"); 00622 print_name(att.name); 00623 printf(" = "); 00624 00625 if (att.len == 0) { /* show 0-length attributes as empty strings */ 00626 att.type = NC_CHAR; 00627 } 00628 00629 if (! is_user_defined_type(att.type) ) { 00630 att.valgp = (void *) emalloc((att.len + 1) * att.tinfo->size ); 00631 NC_CHECK( nc_get_att(ncid, varid, att.name, att.valgp ) ); 00632 if(att.type == NC_CHAR) /* null-terminate retrieved text att value */ 00633 ((char *)att.valgp)[att.len] = '\0'; 00634 /* (1) Print normal list of attribute values. */ 00635 pr_att_valgs(kind, att.type, att.len, att.valgp); 00636 printf (" ;"); /* terminator for normal list */ 00637 /* (2) If -t option, add list of date/time strings as CDL comments. */ 00638 if(formatting_specs.string_times) { 00639 /* Prints text after semicolon and before final newline. 00640 * Prints nothing if not qualified for time interpretation. 00641 * Will include line breaks for longer lists. */ 00642 print_att_times(ncid, varid, att); 00643 if(is_bounds_att(&att)) { 00644 insert_bounds_info(ncid, varid, att); 00645 } 00646 } 00647 #ifdef USE_NETCDF4 00648 /* If NC_STRING, need to free all the strings also */ 00649 if(att.type == NC_STRING) { 00650 nc_free_string(att.len, att.valgp); 00651 } 00652 #endif /* USE_NETCDF4 */ 00653 free(att.valgp); 00654 } 00655 #ifdef USE_NETCDF4 00656 else /* User-defined type. */ 00657 { 00658 char type_name[NC_MAX_NAME + 1]; 00659 size_t type_size, nfields; 00660 nc_type base_nc_type; 00661 int class, i; 00662 void *data; 00663 00664 NC_CHECK( nc_inq_user_type(ncid, att.type, type_name, &type_size, 00665 &base_nc_type, &nfields, &class)); 00666 switch(class) 00667 { 00668 case NC_VLEN: 00669 /* because size returned for vlen is base type size, but we 00670 * need space to read array of vlen structs into ... */ 00671 data = emalloc((att.len + 1) * sizeof(nc_vlen_t)); 00672 break; 00673 case NC_OPAQUE: 00674 data = emalloc((att.len + 1) * type_size); 00675 break; 00676 case NC_ENUM: 00677 /* a long long is ample for all base types */ 00678 data = emalloc((att.len + 1) * sizeof(int64_t)); 00679 break; 00680 case NC_COMPOUND: 00681 data = emalloc((att.len + 1) * type_size); 00682 break; 00683 default: 00684 error("unrecognized class of user defined type: %d", class); 00685 } 00686 00687 NC_CHECK( nc_get_att(ncid, varid, att.name, data)); 00688 00689 switch(class) { 00690 case NC_VLEN: 00691 pr_any_att_vals(&att, data); 00692 free(data); 00693 break; 00694 case NC_OPAQUE: { 00695 char *sout = emalloc(2 * type_size + strlen("0X") + 1); 00696 unsigned char *cp = data; 00697 for (i = 0; i < att.len; i++) { 00698 (void) ncopaque_val_as_hex(type_size, sout, cp); 00699 printf("%s%s", sout, i < att.len-1 ? ", " : ""); 00700 cp += type_size; 00701 } 00702 free(sout); 00703 } 00704 break; 00705 case NC_ENUM: { 00706 int64_t value; 00707 for (i = 0; i < att.len; i++) { 00708 char enum_name[NC_MAX_NAME + 1]; 00709 switch(base_nc_type) 00710 { 00711 case NC_BYTE: 00712 value = *((char *)data + i); 00713 break; 00714 case NC_UBYTE: 00715 value = *((unsigned char *)data + i); 00716 break; 00717 case NC_SHORT: 00718 value = *((short *)data + i); 00719 break; 00720 case NC_USHORT: 00721 value = *((unsigned short *)data + i); 00722 break; 00723 case NC_INT: 00724 value = *((int *)data + i); 00725 break; 00726 case NC_UINT: 00727 value = *((unsigned int *)data + i); 00728 break; 00729 case NC_INT64: 00730 value = *((int64_t *)data + i); 00731 break; 00732 case NC_UINT64: 00733 value = *((uint64_t *)data + i); 00734 break; 00735 default: 00736 error("enum must have an integer base type: %d", base_nc_type); 00737 } 00738 NC_CHECK( nc_inq_enum_ident(ncid, att.type, value, 00739 enum_name)); 00740 /* printf("%s%s", enum_name, i < att.len-1 ? ", " : ""); */ 00741 print_name(enum_name); 00742 printf("%s", i < att.len-1 ? ", " : ""); 00743 } 00744 } 00745 break; 00746 case NC_COMPOUND: 00747 pr_any_att_vals(&att, data); 00748 free(data); 00749 break; 00750 default: 00751 error("unrecognized class of user defined type: %d", class); 00752 } 00753 printf (" ;"); /* terminator for user defined types */ 00754 } 00755 #endif /* USE_NETCDF4 */ 00756 00757 printf ("\n"); /* final newline for all attribute types */ 00758 } 00759 00760 /* Common code for printing attribute name */ 00761 static void 00762 pr_att_name( 00763 int ncid, 00764 const char *varname, 00765 const char *attname 00766 ) 00767 { 00768 indent_out(); 00769 printf ("\t\t"); 00770 print_name(varname); 00771 printf(":"); 00772 print_name(attname); 00773 } 00774 00775 /* 00776 * Print special _Format global attribute, a virtual attribute not 00777 * actually stored in the file. 00778 */ 00779 static void 00780 pr_att_global_format( 00781 int ncid, 00782 int kind 00783 ) 00784 { 00785 pr_att_name(ncid, "", NC_ATT_FORMAT); 00786 printf(" = "); 00787 printf("\"%s\"", kind_string(kind)); 00788 printf (" ;\n"); 00789 } 00790 00791 00792 #ifdef USE_NETCDF4 00793 /* 00794 * Print special reserved variable attributes, such as _Chunking, 00795 * _DeflateLevel, ... These are virtual, not real, attributes 00796 * generated from the result of inquire calls. They are of primitive 00797 * type to fit into the classic model. Currently, these only exist 00798 * for netCDF-4 data. 00799 */ 00800 static void 00801 pr_att_specials( 00802 int ncid, 00803 int kind, 00804 int varid, 00805 const ncvar_t *varp 00806 ) 00807 { 00808 /* No special variable attributes for classic or 64-bit offset data */ 00809 if(kind == 1 || kind == 2) 00810 return; 00811 /* _Chunking */ 00812 if (varp->ndims > 0) { /* no chunking for scalar variables */ 00813 int contig = 0; 00814 NC_CHECK( nc_inq_var_chunking(ncid, varid, &contig, NULL ) ); 00815 if(contig == 1) { 00816 pr_att_name(ncid, varp->name, NC_ATT_STORAGE); 00817 printf(" = \"contiguous\" ;\n"); 00818 } else { 00819 size_t *chunkp; 00820 int i; 00821 pr_att_name(ncid, varp->name, NC_ATT_STORAGE); 00822 printf(" = \"chunked\" ;\n"); 00823 chunkp = (size_t *) emalloc(sizeof(size_t) * (varp->ndims + 1) ); 00824 NC_CHECK( nc_inq_var_chunking(ncid, varid, NULL, chunkp) ); 00825 /* print chunking, even if it is default */ 00826 pr_att_name(ncid, varp->name, NC_ATT_CHUNKING); 00827 printf(" = "); 00828 for(i = 0; i < varp->ndims; i++) { 00829 printf("%lu%s", (unsigned long)chunkp[i], i+1 < varp->ndims ? ", " : " ;\n"); 00830 } 00831 free(chunkp); 00832 } 00833 } 00834 00835 /*_Deflate, _Shuffle */ 00836 { 00837 int shuffle=NC_NOSHUFFLE, deflate=0, deflate_level=0; 00838 NC_CHECK( nc_inq_var_deflate(ncid, varid, &shuffle, 00839 &deflate, &deflate_level) ); 00840 if(deflate != 0) { 00841 pr_att_name(ncid, varp->name, NC_ATT_DEFLATE); 00842 printf(" = %d ;\n", deflate_level); 00843 } 00844 if(shuffle != NC_NOSHUFFLE) { 00845 pr_att_name(ncid, varp->name, NC_ATT_SHUFFLE); 00846 printf(" = \"true\" ;\n"); 00847 } 00848 } 00849 /* _Checksum */ 00850 { 00851 int fletcher32 = 0; 00852 NC_CHECK( nc_inq_var_fletcher32(ncid, varid, &fletcher32) ); 00853 if(fletcher32 != 0) { 00854 pr_att_name(ncid, varp->name, NC_ATT_CHECKSUM); 00855 printf(" = \"true\" ;\n"); 00856 } 00857 } 00858 /* _Endianness */ 00859 if(varp->tinfo->size > 1) /* Endianness is meaningless for 1-byte types */ 00860 { 00861 int endianness = 0; 00862 NC_CHECK( nc_inq_var_endian(ncid, varid, &endianness) ); 00863 if(endianness != 0) { 00864 pr_att_name(ncid, varp->name, NC_ATT_ENDIANNESS); 00865 printf(" = "); 00866 switch (endianness) { 00867 case NC_ENDIAN_LITTLE: 00868 printf("\"little\""); 00869 break; 00870 case NC_ENDIAN_BIG: 00871 printf("\"big\""); 00872 break; 00873 case NC_ENDIAN_NATIVE: 00874 printf("\"native\""); 00875 break; 00876 default: 00877 error("pr_att_specials: bad endianness: %d", endianness); 00878 break; 00879 } 00880 printf(" ;\n"); 00881 } 00882 } 00883 { 00884 int no_fill = 0; 00885 /* Don't get the fill_value, it's set explicitly with 00886 * _FillValue attribute, because nc_def_var_fill() creates a 00887 * _FillValue attribute, if needed, and it's value gets 00888 * displayed elsewhere as a normal (not special virtual) 00889 * attribute. */ 00890 NC_CHECK( nc_inq_var_fill(ncid, varid, &no_fill, NULL) ); 00891 if(no_fill != 0) { 00892 pr_att_name(ncid, varp->name, NC_ATT_NOFILL); 00893 printf(" = \"true\" ;\n"); 00894 } 00895 } 00896 /* TODO: handle _Nbit when inquire function is available */ 00897 00898 /* TODO: handle _ScaleOffset when inquire is available */ 00899 00900 /* TODO: handle _Szip when szip inquire function is available */ 00901 } 00902 #endif /* USE_NETCDF4 */ 00903 00904 00905 /* 00906 * Print a variable attribute for NcML 00907 */ 00908 static void 00909 pr_attx( 00910 int ncid, 00911 int varid, 00912 int ia 00913 ) 00914 { 00915 ncatt_t att; /* attribute */ 00916 char *attvals = ""; 00917 int attvalslen = 0; 00918 00919 NC_CHECK( nc_inq_attname(ncid, varid, ia, att.name) ); 00920 NC_CHECK( nc_inq_att(ncid, varid, att.name, &att.type, &att.len) ); 00921 att.tinfo = get_typeinfo(att.type); 00922 00923 /* Put attribute values into a single string, with blanks in between */ 00924 00925 switch (att.type) { 00926 case NC_CHAR: 00927 attvals = (char *) emalloc(att.len + 1); 00928 attvalslen = att.len; 00929 attvals[att.len] = '\0'; 00930 NC_CHECK( nc_get_att_text(ncid, varid, att.name, attvals ) ); 00931 break; 00932 #ifdef USE_NETCDF4 00933 case NC_STRING: 00934 /* TODO: this only prints first string value, need to handle 00935 multiple strings? */ 00936 attvals = (char *) emalloc(att.len + 1); 00937 attvals[att.len] = '\0'; 00938 NC_CHECK( nc_get_att_text(ncid, varid, att.name, attvals ) ); 00939 break; 00940 case NC_VLEN: 00941 /* TODO */ 00942 break; 00943 case NC_OPAQUE: 00944 /* TODO */ 00945 break; 00946 case NC_COMPOUND: 00947 /* TODO */ 00948 break; 00949 #endif /* USE_NETCDF4 */ 00950 default: 00951 att.vals = (double *) emalloc((att.len + 1) * sizeof(double)); 00952 NC_CHECK( nc_get_att_double(ncid, varid, att.name, att.vals ) ); 00953 attvalslen = 20*att.len; /* max 20 chars for each value and blank separator */ 00954 attvals = (char *) emalloc(attvalslen + 1); 00955 pr_att_valsx(att.type, att.len, att.vals, attvals, attvalslen); 00956 free(att.vals); 00957 break; 00958 } 00959 00960 /* Don't output type for string attributes, since that's default type */ 00961 if(att.type == NC_CHAR 00962 #ifdef USE_NETCDF4 00963 || att.type == NC_CHAR 00964 #endif /* USE_NETCDF4 */ 00965 ) { 00966 /* TODO: XML-ish escapes for special chars in names */ 00967 printf ("%s <attribute name=\"%s\" value=", 00968 varid != NC_GLOBAL ? " " : "", 00969 att.name); 00970 /* print attvals as a string with XML escapes */ 00971 pr_attx_string(attvalslen, attvals); 00972 } else { /* non-string attribute */ 00973 char att_type_name[NC_MAX_NAME + 1]; 00974 get_type_name(ncid, att.type, att_type_name); 00975 /* TODO: print full type name with group prefix, when needed */ 00976 printf ("%s <attribute name=\"%s\" type=\"%s\" value=\"", 00977 varid != NC_GLOBAL ? " " : "", 00978 att.name, 00979 att_type_name); 00980 printf("%s\"",attvals); 00981 } 00982 printf (" />\n"); 00983 free (attvals); 00984 } 00985 00986 00987 /* Print optional NcML attribute for a variable's shape */ 00988 static void 00989 pr_shape(ncvar_t* varp, ncdim_t *dims) 00990 { 00991 char *shape; 00992 int shapelen = 0; 00993 int id; 00994 00995 if (varp->ndims == 0) 00996 return; 00997 for (id = 0; id < varp->ndims; id++) { 00998 shapelen += strlen(dims[varp->dims[id]].name) + 1; 00999 } 01000 shape = (char *) emalloc(shapelen + 1); 01001 shape[0] = '\0'; 01002 for (id = 0; id < varp->ndims; id++) { 01003 /* TODO: XML-ish escapes for special chars in dim names */ 01004 strlcat(shape, dims[varp->dims[id]].name, shapelen); 01005 strlcat(shape, id < varp->ndims-1 ? " " : "", shapelen); 01006 } 01007 printf (" shape=\"%s\"", shape); 01008 free(shape); 01009 } 01010 01011 #ifdef USE_NETCDF4 01012 01013 01014 /* Print an enum type declaration */ 01015 static void 01016 print_enum_type(int ncid, nc_type typeid) { 01017 char type_name[NC_MAX_NAME + 1]; 01018 size_t type_size; 01019 nc_type base_nc_type; 01020 size_t type_nfields; 01021 int type_class; 01022 char base_type_name[NC_MAX_NAME + 1]; 01023 int f; 01024 int64_t memval; 01025 char memname[NC_MAX_NAME + 1]; 01026 /* extra space for escapes, and punctuation */ 01027 #define SAFE_BUF_LEN 4*NC_MAX_NAME+30 01028 char safe_buf[SAFE_BUF_LEN]; 01029 char *delim; 01030 int64_t data; /* space for data of any primitive type */ 01031 char *esc_btn; 01032 char *esc_tn; 01033 char *esc_mn; 01034 int res; 01035 01036 NC_CHECK( nc_inq_user_type(ncid, typeid, type_name, &type_size, &base_nc_type, 01037 &type_nfields, &type_class) ); 01038 01039 get_type_name(ncid, base_nc_type, base_type_name); 01040 indent_out(); 01041 esc_btn = escaped_name(base_type_name); 01042 esc_tn = escaped_name(type_name); 01043 res = snprintf(safe_buf, SAFE_BUF_LEN,"%s enum %s {", esc_btn, esc_tn); 01044 assert(res < SAFE_BUF_LEN); 01045 free(esc_btn); 01046 free(esc_tn); 01047 lput(safe_buf); 01048 delim = ", "; 01049 for (f = 0; f < type_nfields; f++) { 01050 if (f == type_nfields - 1) 01051 delim = "} ;\n"; 01052 NC_CHECK( nc_inq_enum_member(ncid, typeid, f, memname, &data) ); 01053 switch (base_nc_type) { 01054 case NC_BYTE: 01055 memval = *(char *)&data; 01056 break; 01057 case NC_SHORT: 01058 memval = *(short *)&data; 01059 break; 01060 case NC_INT: 01061 memval = *(int *)&data; 01062 break; 01063 #ifdef USE_NETCDF4 01064 case NC_UBYTE: 01065 memval = *(unsigned char *)&data; 01066 break; 01067 case NC_USHORT: 01068 memval = *(unsigned short *)&data; 01069 break; 01070 case NC_UINT: 01071 memval = *(unsigned int *)&data; 01072 break; 01073 case NC_INT64: 01074 memval = *(int64_t *)&data; 01075 break; 01076 case NC_UINT64: 01077 memval = *(uint64_t *)&data; 01078 break; 01079 #endif /* USE_NETCDF4 */ 01080 default: 01081 error("Bad base type for enum!"); 01082 break; 01083 } 01084 esc_mn = escaped_name(memname); 01085 res = snprintf(safe_buf, SAFE_BUF_LEN, "%s = %lld%s", esc_mn, 01086 memval, delim); 01087 assert(res < SAFE_BUF_LEN); 01088 free(esc_mn); 01089 lput(safe_buf); 01090 } 01091 } 01092 01093 01094 /* Print a user-defined type declaration */ 01095 static void 01096 print_ud_type(int ncid, nc_type typeid) { 01097 01098 char type_name[NC_MAX_NAME + 1]; 01099 char base_type_name[NC_MAX_NAME + 1]; 01100 size_t type_nfields, type_size; 01101 nc_type base_nc_type; 01102 int f, type_class; 01103 01104 NC_CHECK( nc_inq_user_type(ncid, typeid, type_name, &type_size, &base_nc_type, 01105 &type_nfields, &type_class) ); 01106 switch(type_class) { 01107 case NC_VLEN: 01108 /* TODO: don't bother getting base_type_name if 01109 * print_type_name looks it up anyway */ 01110 get_type_name(ncid, base_nc_type, base_type_name); 01111 indent_out(); 01112 /* printf("%s(*) %s ;\n", base_type_name, type_name); */ 01113 print_type_name(ncid, base_nc_type); 01114 printf("(*) "); 01115 print_type_name(ncid, typeid); 01116 printf(" ;\n"); 01117 break; 01118 case NC_OPAQUE: 01119 indent_out(); 01120 /* printf("opaque(%d) %s ;\n", (int)type_size, type_name); */ 01121 printf("opaque(%d) ", (int)type_size); 01122 print_type_name(ncid, typeid); 01123 printf(" ;\n"); 01124 break; 01125 case NC_ENUM: 01126 print_enum_type(ncid, typeid); 01127 break; 01128 case NC_COMPOUND: 01129 { 01130 char field_name[NC_MAX_NAME + 1]; 01131 char field_type_name[NC_MAX_NAME + 1]; 01132 size_t field_offset; 01133 nc_type field_type; 01134 int field_ndims; 01135 int d; 01136 01137 indent_out(); 01138 /* printf("compound %s {\n", type_name); */ 01139 printf("compound "); 01140 print_type_name(ncid, typeid); 01141 printf(" {\n"); 01142 for (f = 0; f < type_nfields; f++) 01143 { 01144 NC_CHECK( nc_inq_compound_field(ncid, typeid, f, field_name, 01145 &field_offset, &field_type, 01146 &field_ndims, NULL) ); 01147 /* TODO: don't bother if field_type_name not needed here */ 01148 get_type_name(ncid, field_type, field_type_name); 01149 indent_out(); 01150 /* printf(" %s %s", field_type_name, field_name); */ 01151 printf(" "); 01152 print_type_name(ncid, field_type); 01153 printf(" "); 01154 print_name(field_name); 01155 if (field_ndims > 0) { 01156 int *field_dim_sizes = (int *) emalloc((field_ndims + 1) * sizeof(int)); 01157 NC_CHECK( nc_inq_compound_field(ncid, typeid, f, NULL, 01158 NULL, NULL, NULL, 01159 field_dim_sizes) ); 01160 printf("("); 01161 for (d = 0; d < field_ndims-1; d++) 01162 printf("%d, ", field_dim_sizes[d]); 01163 printf("%d)", field_dim_sizes[field_ndims-1]); 01164 free(field_dim_sizes); 01165 } 01166 printf(" ;\n"); 01167 } 01168 indent_out(); 01169 /* printf("}; // %s\n", type_name); */ 01170 printf("}; // "); 01171 print_type_name(ncid, typeid); 01172 printf("\n"); 01173 } 01174 break; 01175 default: 01176 error("Unknown class of user-defined type!"); 01177 } 01178 } 01179 #endif /* USE_NETCDF4 */ 01180 01181 static void 01182 get_fill_info(int ncid, int varid, ncvar_t *vp) { 01183 ncatt_t att; /* attribute */ 01184 int nc_status; /* return from netcdf calls */ 01185 void *fillvalp = NULL; 01186 01187 vp->has_fillval = 1; /* by default, but turn off for bytes */ 01188 01189 /* get _FillValue attribute */ 01190 nc_status = nc_inq_att(ncid,varid,_FillValue,&att.type,&att.len); 01191 fillvalp = emalloc(vp->tinfo->size + 1); 01192 if(nc_status == NC_NOERR && 01193 att.type == vp->type && att.len == 1) { 01194 NC_CHECK(nc_get_att(ncid, varid, _FillValue, fillvalp)); 01195 } else { 01196 switch (vp->type) { 01197 case NC_BYTE: 01198 /* don't do default fill-values for bytes, too risky */ 01199 vp->has_fillval = 0; 01200 free(fillvalp); 01201 fillvalp = 0; 01202 break; 01203 case NC_CHAR: 01204 *(char *)fillvalp = NC_FILL_CHAR; 01205 break; 01206 case NC_SHORT: 01207 *(short *)fillvalp = NC_FILL_SHORT; 01208 break; 01209 case NC_INT: 01210 *(int *)fillvalp = NC_FILL_INT; 01211 break; 01212 case NC_FLOAT: 01213 *(float *)fillvalp = NC_FILL_FLOAT; 01214 break; 01215 case NC_DOUBLE: 01216 *(double *)fillvalp = NC_FILL_DOUBLE; 01217 break; 01218 #ifdef USE_NETCDF4 01219 case NC_UBYTE: 01220 /* don't do default fill-values for bytes, too risky */ 01221 vp->has_fillval = 0; 01222 free(fillvalp); 01223 fillvalp = 0; 01224 break; 01225 case NC_USHORT: 01226 *(unsigned short *)fillvalp = NC_FILL_USHORT; 01227 break; 01228 case NC_UINT: 01229 *(unsigned int *)fillvalp = NC_FILL_UINT; 01230 break; 01231 case NC_INT64: 01232 *(int64_t *)fillvalp = NC_FILL_INT64; 01233 break; 01234 case NC_UINT64: 01235 *(uint64_t *)fillvalp = NC_FILL_UINT64; 01236 break; 01237 case NC_STRING: 01238 *((char **)fillvalp) = NC_FILL_STRING; 01239 break; 01240 #endif /* USE_NETCDF4 */ 01241 default: /* no default fill values for NC_NAT 01242 or user-defined types */ 01243 vp->has_fillval = 0; 01244 free(fillvalp); 01245 fillvalp = 0; 01246 break; 01247 } 01248 } 01249 vp->fillvalp = fillvalp; 01250 } 01251 01252 01253 /* Recursively dump the contents of a group. (Only netcdf-4 format 01254 * files can have groups, so recursion will not take place for classic 01255 * format files.) 01256 * 01257 * ncid: id of open file (first call) or group (subsequent recursive calls) 01258 * path: file path name (first call) 01259 */ 01260 static void 01261 do_ncdump_rec(int ncid, const char *path) 01262 { 01263 int ndims; /* number of dimensions */ 01264 int nvars; /* number of variables */ 01265 int ngatts; /* number of global attributes */ 01266 int xdimid; /* id of unlimited dimension */ 01267 int varid; /* variable id */ 01268 ncdim_t *dims; /* dimensions */ 01269 size_t *vdims=0; /* dimension sizes for a single variable */ 01270 ncvar_t var; /* variable */ 01271 int id; /* dimension number per variable */ 01272 int ia; /* attribute number */ 01273 int iv; /* variable number */ 01274 idnode_t* vlist = 0; /* list for vars specified with -v option */ 01275 char type_name[NC_MAX_NAME + 1]; 01276 int kind; /* strings output differently for nc4 files */ 01277 char dim_name[NC_MAX_NAME + 1]; 01278 #ifdef USE_NETCDF4 01279 int *dimids_grp; /* dimids of the dims in this group. */ 01280 int *unlimids; /* dimids of unlimited dimensions in this group */ 01281 int d_grp, ndims_grp; 01282 int ntypes, *typeids; 01283 int nunlim; 01284 #else 01285 int dimid; /* dimension id */ 01286 #endif /* USE_NETCDF4 */ 01287 int is_root = 1; /* true if ncid is root group or if netCDF-3 */ 01288 01289 #ifdef USE_NETCDF4 01290 if (nc_inq_grp_parent(ncid, NULL) != NC_ENOGRP) 01291 is_root = 0; 01292 #endif /* USE_NETCDF4 */ 01293 01294 /* 01295 * If any vars were specified with -v option, get list of 01296 * associated variable ids relative to this group. Assume vars 01297 * specified with syntax like "grp1/grp2/varname" or 01298 * "/grp1/grp2/varname" if they are in groups. 01299 */ 01300 if (formatting_specs.nlvars > 0) { 01301 vlist = newidlist(); /* list for vars specified with -v option */ 01302 for (iv=0; iv < formatting_specs.nlvars; iv++) { 01303 if(nc_inq_gvarid(ncid, formatting_specs.lvars[iv], &varid) == NC_NOERR) 01304 idadd(vlist, varid); 01305 } 01306 } 01307 01308 #ifdef USE_NETCDF4 01309 /* Are there any user defined types in this group? */ 01310 NC_CHECK( nc_inq_typeids(ncid, &ntypes, NULL) ); 01311 if (ntypes) 01312 { 01313 int t; 01314 01315 typeids = emalloc((ntypes + 1) * sizeof(int)); 01316 NC_CHECK( nc_inq_typeids(ncid, &ntypes, typeids) ); 01317 indent_out(); 01318 printf("types:\n"); 01319 indent_more(); 01320 for (t = 0; t < ntypes; t++) 01321 { 01322 print_ud_type(ncid, typeids[t]); /* print declaration of user-defined type */ 01323 } 01324 indent_less(); 01325 free(typeids); 01326 } 01327 #endif /* USE_NETCDF4 */ 01328 01329 /* 01330 * get number of dimensions, number of variables, number of global 01331 * atts, and dimension id of unlimited dimension, if any 01332 */ 01333 NC_CHECK( nc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) ); 01334 /* get dimension info */ 01335 dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t)); 01336 if (ndims > 0) { 01337 indent_out(); 01338 printf ("dimensions:\n"); 01339 } 01340 01341 #ifdef USE_NETCDF4 01342 /* In netCDF-4 files, dimids will not be sequential because they 01343 * may be defined in various groups, and we are only looking at one 01344 * group at a time. */ 01345 01346 /* Find the number of dimids defined in this group. */ 01347 NC_CHECK( nc_inq_ndims(ncid, &ndims_grp) ); 01348 dimids_grp = (int *)emalloc((ndims_grp + 1) * sizeof(int)); 01349 01350 /* Find the dimension ids in this group. */ 01351 NC_CHECK( nc_inq_dimids(ncid, 0, dimids_grp, 0) ); 01352 01353 /* Find the number of unlimited dimensions and get their IDs */ 01354 NC_CHECK( nc_inq_unlimdims(ncid, &nunlim, NULL) ); 01355 unlimids = (int *)emalloc((nunlim + 1) * sizeof(int)); 01356 NC_CHECK( nc_inq_unlimdims(ncid, &nunlim, unlimids) ); 01357 01358 /* For each dimension defined in this group, get and print out info. */ 01359 for (d_grp = 0; d_grp < ndims_grp; d_grp++) 01360 { 01361 int dimid = dimids_grp[d_grp]; 01362 int is_unlimited = 0; 01363 int uld; 01364 int stat; 01365 01366 for (uld = 0; uld < nunlim; uld++) { 01367 if(dimid == unlimids[uld]) { 01368 is_unlimited = 1; 01369 break; 01370 } 01371 } 01372 stat = nc_inq_dim(ncid, dimid, dims[d_grp].name, &dims[d_grp].size); 01373 if (stat == NC_EDIMSIZE && SIZEOF_SIZE_T < 8) { 01374 error("dimension \"%s\" too large for 32-bit platform, try 64-bit version", dims[d_grp].name); 01375 } else { 01376 NC_CHECK (stat); 01377 } 01378 indent_out(); 01379 printf ("\t"); 01380 print_name(dims[d_grp].name); 01381 printf (" = "); 01382 if(SIZEOF_SIZE_T >= 8) { 01383 if (is_unlimited) { 01384 printf ("UNLIMITED ; // (%lu currently)\n", 01385 (unsigned long)dims[d_grp].size); 01386 } else { 01387 printf ("%lu ;\n", (unsigned long)dims[d_grp].size); 01388 } 01389 } else { /* 32-bit platform */ 01390 if (is_unlimited) { 01391 printf ("UNLIMITED ; // (%u currently)\n", 01392 (unsigned int)dims[d_grp].size); 01393 } else { 01394 printf ("%u ;\n", (unsigned int)dims[d_grp].size); 01395 } 01396 } 01397 } 01398 if(unlimids) 01399 free(unlimids); 01400 if(dimids_grp) 01401 free(dimids_grp); 01402 #else /* not using netCDF-4 */ 01403 for (dimid = 0; dimid < ndims; dimid++) { 01404 NC_CHECK( nc_inq_dim(ncid, dimid, dims[dimid].name, &dims[dimid].size) ); 01405 indent_out(); 01406 printf ("\t"); 01407 print_name(dims[dimid].name); 01408 printf (" = "); 01409 if (dimid == xdimid) { 01410 printf ("UNLIMITED ; // (%u currently)\n", 01411 (unsigned int)dims[dimid].size); 01412 } else { 01413 printf ("%u ;\n", (unsigned int)dims[dimid].size); 01414 } 01415 } 01416 #endif /* USE_NETCDF4 */ 01417 01418 if (nvars > 0) { 01419 indent_out(); 01420 printf ("variables:\n"); 01421 } 01422 /* Because netCDF-4 can have a string attribute with multiple 01423 * string values, we can't output strings with embedded newlines 01424 * as what look like multiple strings, as we do for classic and 01425 * 64-bit offset files. So we need to know the output file type 01426 * to know how to print strings with embedded newlines. */ 01427 NC_CHECK( nc_inq_format(ncid, &kind) ); 01428 01429 /* For each var, get and print out info. */ 01430 01431 memset((void*)&var,0,sizeof(var)); 01432 01433 for (varid = 0; varid < nvars; varid++) { 01434 NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) ); 01435 if(var.dims != NULL) free(var.dims); 01436 var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int)); 01437 NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0, 01438 var.dims, &var.natts) ); 01439 /* TODO: don't bother if type name not needed here */ 01440 get_type_name(ncid, var.type, type_name); 01441 var.tinfo = get_typeinfo(var.type); 01442 indent_out(); 01443 /* printf ("\t%s %s", type_name, var.name); */ 01444 printf ("\t"); 01445 /* TODO: if duplicate type name and not just inherited, print 01446 * full type name. */ 01447 print_type_name (ncid, var.type); 01448 printf (" "); 01449 print_name (var.name); 01450 if (var.ndims > 0) 01451 printf ("("); 01452 for (id = 0; id < var.ndims; id++) { 01453 /* This dim may be in a parent group, so let's look up the 01454 * name. */ 01455 NC_CHECK( nc_inq_dimname(ncid, var.dims[id], dim_name) ); 01456 #ifdef USE_NETCDF4 01457 /* Subtlety: The following code block is needed because 01458 * nc_inq_dimname() currently returns only a simple dimension 01459 * name, without a prefix identifying the group it came from. 01460 * That's OK unless the dimid identifies a dimension in an 01461 * ancestor group that has the same simple name as a 01462 * dimension in the current group (or some intermediate 01463 * group), in which case the simple name is ambiguous. This 01464 * code tests for that case and provides an absolute dimname 01465 * only in the case where a simple name would be 01466 * ambiguous. */ 01467 { 01468 int dimid_test; /* to see if dimname is ambiguous */ 01469 int locid; /* group id where dimension is defined */ 01470 NC_CHECK( nc_inq_dimid(ncid, dim_name, &dimid_test) ); 01471 locid = ncid; 01472 while(var.dims[id] != dimid_test) { /* not in locid, try ancestors */ 01473 int parent_id; 01474 NC_CHECK( nc_inq_grp_parent(locid, &parent_id) ); 01475 locid = parent_id; 01476 NC_CHECK( nc_inq_dimid(locid, dim_name, &dimid_test) ); 01477 } 01478 /* dimid is in group locid, prefix dimname with group name if needed */ 01479 if(locid != ncid) { 01480 size_t len; 01481 char *locname; /* the group name */ 01482 NC_CHECK( nc_inq_grpname_full(locid, &len, NULL) ); 01483 locname = emalloc(len + 1); 01484 NC_CHECK( nc_inq_grpname_full(locid, &len, locname) ); 01485 print_name (locname); 01486 if(strcmp("/", locname) != 0) { /* not the root group */ 01487 printf("/"); /* ensure a trailing slash */ 01488 } 01489 free(locname); 01490 } 01491 } 01492 #endif /* USE_NETCDF4 */ 01493 print_name (dim_name); 01494 printf ("%s", id < var.ndims-1 ? ", " : ")"); 01495 } 01496 printf (" ;\n"); 01497 01498 /* print variable attributes */ 01499 for (ia = 0; ia < var.natts; ia++) { /* print ia-th attribute */ 01500 pr_att(ncid, kind, varid, var.name, ia); 01501 } 01502 #ifdef USE_NETCDF4 01503 /* Print special (virtual) attributes, if option specified */ 01504 if (formatting_specs.special_atts) { 01505 pr_att_specials(ncid, kind, varid, &var); 01506 } 01507 #endif /* USE_NETCDF4 */ 01508 } 01509 01510 /* get global attributes */ 01511 if (ngatts > 0 || formatting_specs.special_atts) { 01512 printf ("\n"); 01513 indent_out(); 01514 if (is_root) 01515 printf("// global attributes:\n"); 01516 else 01517 printf("// group attributes:\n"); 01518 } 01519 for (ia = 0; ia < ngatts; ia++) { /* print ia-th global attribute */ 01520 pr_att(ncid, kind, NC_GLOBAL, "", ia); 01521 } 01522 if (is_root && formatting_specs.special_atts) { /* output special attribute 01523 * for format variant */ 01524 pr_att_global_format(ncid, kind); 01525 } 01526 01527 /* output variable data, unless "-h" option specified header only 01528 * or this group is not in list of groups specified by "-g" 01529 * option */ 01530 if (! formatting_specs.header_only && group_wanted(ncid) ) { 01531 if (nvars > 0) { 01532 indent_out(); 01533 printf ("data:\n"); 01534 } 01535 for (varid = 0; varid < nvars; varid++) { 01536 int no_data; 01537 /* if var list specified, test for membership */ 01538 if (formatting_specs.nlvars > 0 && ! idmember(vlist, varid)) 01539 continue; 01540 NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) ); 01541 if(var.dims != NULL) free(var.dims); 01542 var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int)); 01543 NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0, 01544 var.dims, &var.natts) ); 01545 var.tinfo = get_typeinfo(var.type); 01546 /* If coords-only option specified, don't get data for 01547 * non-coordinate vars */ 01548 if (formatting_specs.coord_vals && !iscoordvar(ncid,varid)) { 01549 continue; 01550 } 01551 /* Collect variable's dim sizes */ 01552 if (vdims) { 01553 free(vdims); 01554 vdims = 0; 01555 } 01556 vdims = (size_t *) emalloc((var.ndims + 1) * SIZEOF_SIZE_T); 01557 no_data = 0; 01558 for (id = 0; id < var.ndims; id++) { 01559 size_t len; 01560 NC_CHECK( nc_inq_dimlen(ncid, var.dims[id], &len) ); 01561 if(len == 0) { 01562 no_data = 1; 01563 } 01564 vdims[id] = len; 01565 } 01566 /* Don't get data for record variables if no records have 01567 * been written yet */ 01568 if (no_data) { 01569 free(vdims); 01570 vdims = 0; 01571 continue; 01572 } 01573 if(var.fillvalp != NULL) free(var.fillvalp); 01574 get_fill_info(ncid, varid, &var); /* sets has_fillval, fillvalp mmbrs */ 01575 if(var.timeinfo != NULL) { 01576 if(var.timeinfo->units) free(var.timeinfo->units); 01577 free(var.timeinfo); 01578 } 01579 get_timeinfo(ncid, varid, &var); /* sets has_timeval, timeinfo mmbrs */ 01580 /* printf format used to print each value */ 01581 var.fmt = get_fmt(ncid, varid, var.type); 01582 var.locid = ncid; 01583 set_tostring_func(&var); 01584 if (vardata(&var, vdims, ncid, varid) == -1) { 01585 error("can't output data for variable %s", var.name); 01586 goto done; 01587 } 01588 } 01589 if (vdims) { 01590 free(vdims); 01591 vdims = 0; 01592 } 01593 } 01594 01595 #ifdef USE_NETCDF4 01596 /* For netCDF-4 compiles, check to see if the file has any 01597 * groups. If it does, this function is called recursively on each 01598 * of them. */ 01599 { 01600 int g, numgrps, *ncids; 01601 char group_name[NC_MAX_NAME + 1]; 01602 01603 /* See how many groups there are. */ 01604 NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) ); 01605 01606 /* Allocate memory to hold the list of group ids. */ 01607 ncids = emalloc((numgrps + 1) * sizeof(int)); 01608 01609 /* Get the list of group ids. */ 01610 NC_CHECK( nc_inq_grps(ncid, NULL, ncids) ); 01611 01612 /* Call this function for each group. */ 01613 for (g = 0; g < numgrps; g++) 01614 { 01615 NC_CHECK( nc_inq_grpname(ncids[g], group_name) ); 01616 printf ("\n"); 01617 indent_out(); 01618 /* printf ("group: %s {\n", group_name); */ 01619 printf ("group: "); 01620 print_name (group_name); 01621 printf (" {\n"); 01622 indent_more(); 01623 do_ncdump_rec(ncids[g], NULL); 01624 indent_out(); 01625 /* printf ("} // group %s\n", group_name); */ 01626 printf ("} // group "); 01627 print_name (group_name); 01628 printf ("\n"); 01629 indent_less(); 01630 } 01631 01632 free(ncids); 01633 } 01634 #endif /* USE_NETCDF4 */ 01635 01636 done: 01637 if(var.dims != NULL) free(var.dims); 01638 if(var.fillvalp != NULL) free(var.fillvalp); 01639 if(var.timeinfo != NULL) { 01640 if(var.timeinfo->units) free(var.timeinfo->units); 01641 free(var.timeinfo); 01642 } 01643 if (dims) 01644 free(dims); 01645 if (vlist) 01646 free(vlist); 01647 } 01648 01649 01650 static void 01651 do_ncdump(int ncid, const char *path) 01652 { 01653 char* esc_specname; 01654 /* output initial line */ 01655 indent_init(); 01656 indent_out(); 01657 esc_specname=escaped_name(formatting_specs.name); 01658 printf ("netcdf %s {\n", esc_specname); 01659 free(esc_specname); 01660 do_ncdump_rec(ncid, path); 01661 indent_out(); 01662 printf ("}\n"); 01663 } 01664 01665 01666 static void 01667 do_ncdumpx(int ncid, const char *path) 01668 { 01669 int ndims; /* number of dimensions */ 01670 int nvars; /* number of variables */ 01671 int ngatts; /* number of global attributes */ 01672 int xdimid; /* id of unlimited dimension */ 01673 int dimid; /* dimension id */ 01674 int varid; /* variable id */ 01675 ncdim_t *dims; /* dimensions */ 01676 ncvar_t var; /* variable */ 01677 int ia; /* attribute number */ 01678 int iv; /* variable number */ 01679 idnode_t* vlist = 0; /* list for vars specified with -v option */ 01680 01681 /* 01682 * If any vars were specified with -v option, get list of associated 01683 * variable ids 01684 */ 01685 if (formatting_specs.nlvars > 0) { 01686 vlist = newidlist(); /* list for vars specified with -v option */ 01687 for (iv=0; iv < formatting_specs.nlvars; iv++) { 01688 NC_CHECK( nc_inq_varid(ncid, formatting_specs.lvars[iv], &varid) ); 01689 idadd(vlist, varid); 01690 } 01691 } 01692 01693 /* output initial line */ 01694 pr_initx(ncid, path); 01695 01696 /* 01697 * get number of dimensions, number of variables, number of global 01698 * atts, and dimension id of unlimited dimension, if any 01699 */ 01700 /* TODO: print names with XML-ish escapes fopr special chars */ 01701 NC_CHECK( nc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) ); 01702 /* get dimension info */ 01703 dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t)); 01704 for (dimid = 0; dimid < ndims; dimid++) { 01705 NC_CHECK( nc_inq_dim(ncid, dimid, dims[dimid].name, &dims[dimid].size) ); 01706 if (dimid == xdimid) 01707 printf(" <dimension name=\"%s\" length=\"%d\" isUnlimited=\"true\" />\n", 01708 dims[dimid].name, (int)dims[dimid].size); 01709 else 01710 printf (" <dimension name=\"%s\" length=\"%d\" />\n", 01711 dims[dimid].name, (int)dims[dimid].size); 01712 } 01713 01714 /* get global attributes */ 01715 for (ia = 0; ia < ngatts; ia++) 01716 pr_attx(ncid, NC_GLOBAL, ia); /* print ia-th global attribute */ 01717 01718 /* get variable info, with variable attributes */ 01719 memset((void*)&var,0,sizeof(var)); 01720 for (varid = 0; varid < nvars; varid++) { 01721 NC_CHECK( nc_inq_varndims(ncid, varid, &var.ndims) ); 01722 if(var.dims != NULL) free(var.dims); 01723 var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int)); 01724 NC_CHECK( nc_inq_var(ncid, varid, var.name, &var.type, 0, 01725 var.dims, &var.natts) ); 01726 printf (" <variable name=\"%s\"", var.name); 01727 pr_shape(&var, dims); 01728 01729 /* handle one-line variable elements that aren't containers 01730 for attributes or data values, since they need to be 01731 rendered as <variable ... /> instead of <variable ..> 01732 ... </variable> */ 01733 if (var.natts == 0) { 01734 if ( 01735 /* header-only specified */ 01736 (formatting_specs.header_only) || 01737 /* list of variables specified and this variable not in list */ 01738 (formatting_specs.nlvars > 0 && !idmember(vlist, varid)) || 01739 /* coordinate vars only and this is not a coordinate variable */ 01740 (formatting_specs.coord_vals && !iscoordvar(ncid, varid)) || 01741 /* this is a record variable, but no records have been written */ 01742 (isrecvar(ncid,varid) && dims[xdimid].size == 0) 01743 ) { 01744 printf (" type=\"%s\" />\n", prim_type_name(var.type)); 01745 continue; 01746 } 01747 } 01748 01749 /* else nest attributes values, data values in <variable> ... </variable> */ 01750 printf (" type=\"%s\">\n", prim_type_name(var.type)); 01751 01752 /* get variable attributes */ 01753 for (ia = 0; ia < var.natts; ia++) { 01754 pr_attx(ncid, varid, ia); /* print ia-th attribute */ 01755 } 01756 printf (" </variable>\n"); 01757 } 01758 01759 printf ("</netcdf>\n"); 01760 if (vlist) 01761 free(vlist); 01762 if(dims) 01763 free(dims); 01764 } 01765 01766 static void 01767 make_lvars(char *optarg) 01768 { 01769 char *cp = optarg; 01770 int nvars = 1; 01771 char ** cpp; 01772 01773 /* compute number of variable names in comma-delimited list */ 01774 formatting_specs.nlvars = 1; 01775 while (*cp++) 01776 if (*cp == ',') 01777 nvars++; 01778 formatting_specs.nlvars = nvars; 01779 formatting_specs.lvars = (char **) emalloc(nvars * sizeof(char*)); 01780 cpp = formatting_specs.lvars; 01781 /* copy variable names into list */ 01782 for (cp = strtok(optarg, ","); cp != NULL; cp = strtok((char *) NULL, ",")) { 01783 *cpp = strdup(cp); 01784 cpp++; 01785 } 01786 } 01787 01788 static void 01789 make_lgrps(char *optarg) 01790 { 01791 char *cp = optarg; 01792 int ngrps = 1; 01793 char ** cpp; 01794 01795 /* compute number of group names in comma-delimited list */ 01796 while (*cp++) 01797 if (*cp == ',') 01798 ngrps++; 01799 formatting_specs.nlgrps = ngrps; 01800 formatting_specs.lgrps = (char **) emalloc(ngrps * sizeof(char*)); 01801 cpp = formatting_specs.lgrps; 01802 /* copy group names into list */ 01803 for (cp = strtok(optarg, ","); cp != NULL; cp = strtok((char *) NULL, ",")) { 01804 *cpp = strdup(cp); 01805 cpp++; 01806 } 01807 /* make empty list of grpids, to be filled in after input file opened */ 01808 formatting_specs.grpids = newidlist(); 01809 } 01810 01811 01812 /* 01813 * Extract the significant-digits specifiers from the (deprecated and 01814 * undocumented) -d argument on the command-line and update the 01815 * default data formats appropriately. This only exists because an 01816 * old version of ncdump supported the "-d" flag which did not 01817 * override the C_format attributes (if any). 01818 */ 01819 static void 01820 set_sigdigs(const char *optarg) 01821 { 01822 char *ptr1 = 0; 01823 char *ptr2 = 0; 01824 int flt_digits = FLT_DIGITS; /* default floating-point digits */ 01825 int dbl_digits = DBL_DIGITS; /* default double-precision digits */ 01826 01827 if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') 01828 flt_digits = (int)strtol(optarg, &ptr1, 10); 01829 01830 if (flt_digits < 1 || flt_digits > 20) { 01831 error("unreasonable value for float significant digits: %d", 01832 flt_digits); 01833 } 01834 if (ptr1 && *ptr1 == ',') { 01835 dbl_digits = (int)strtol(ptr1+1, &ptr2, 10); 01836 if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) { 01837 error("unreasonable value for double significant digits: %d", 01838 dbl_digits); 01839 } 01840 } 01841 set_formats(flt_digits, dbl_digits); 01842 } 01843 01844 01845 /* 01846 * Extract the significant-digits specifiers from the -p argument on the 01847 * command-line, set flags so we can override C_format attributes (if any), 01848 * and update the default data formats appropriately. 01849 */ 01850 static void 01851 set_precision(const char *optarg) 01852 { 01853 char *ptr1 = 0; 01854 char *ptr2 = 0; 01855 int flt_digits = FLT_DIGITS; /* default floating-point digits */ 01856 int dbl_digits = DBL_DIGITS; /* default double-precision digits */ 01857 01858 if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') { 01859 flt_digits = (int)strtol(optarg, &ptr1, 10); 01860 float_precision_specified = 1; 01861 } 01862 01863 if (flt_digits < 1 || flt_digits > 20) { 01864 error("unreasonable value for float significant digits: %d", 01865 flt_digits); 01866 } 01867 if (ptr1 && *ptr1 == ',') { 01868 dbl_digits = (int) strtol(ptr1+1, &ptr2, 10); 01869 double_precision_specified = 1; 01870 if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) { 01871 error("unreasonable value for double significant digits: %d", 01872 dbl_digits); 01873 } 01874 } 01875 set_formats(flt_digits, dbl_digits); 01876 } 01877 01878 /* Determine whether a variable named varname exists in any group in 01879 an open netCDF file with id ncid. If so, return the count of how 01880 many matching variables were found, else return a count of 0. The 01881 variable name can be absolute such as "/foo" or "/GRP1/GRP1A/foo", 01882 in which case there is only one group to look in, given by the path 01883 from the root group. Alternatively, the variable name can be 01884 relative, such as "foo" or "GRPA/GRPB/foo", in which case every 01885 group is examined for a variable with that relative name. */ 01886 size_t 01887 nc_inq_varname_count(int ncid, char *varname) { 01888 /* 01889 count = 0; 01890 status = nc_inq_gvarid(ncid, varname, varid); 01891 if (status == NC_NOERR) 01892 count++; 01893 for each subgroup gid { 01894 count += nc_inq_varname_count(gid, varname); 01895 } 01896 return count; 01897 */ 01898 size_t count = 0; 01899 int varid; 01900 /* look in this group */ 01901 int status = nc_inq_gvarid(ncid, varname, &varid); 01902 #ifdef USE_NETCDF4 01903 int numgrps; 01904 int *ncids; 01905 int g; 01906 #endif 01907 01908 if (status == NC_NOERR) 01909 count++; 01910 01911 #ifdef USE_NETCDF4 01912 /* if this group has subgroups, call recursively on each of them */ 01913 NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) ); 01914 01915 /* Allocate memory to hold the list of group ids. */ 01916 ncids = emalloc((numgrps + 1) * sizeof(int)); 01917 01918 /* Get the list of group ids. */ 01919 NC_CHECK( nc_inq_grps(ncid, NULL, ncids) ); 01920 01921 /* Call this function for each group. */ 01922 for (g = 0; g < numgrps; g++) { 01923 count += nc_inq_varname_count(ncids[g], varname); 01924 } 01925 free(ncids); 01926 #endif /* USE_NETCDF4 */ 01927 return count; 01928 01929 } 01930 01931 01932 /* Check if any variable names specified with "-v var1,...,varn" are 01933 * missing. Returns 0 if no missing variables detected, otherwise 01934 * exits. */ 01935 static int 01936 missing_vars(int ncid) { 01937 int iv; 01938 for (iv=0; iv < formatting_specs.nlvars; iv++) { 01939 if(nc_inq_varname_count(ncid, formatting_specs.lvars[iv]) == 0) { 01940 error("%s: No such variable", formatting_specs.lvars[iv]); 01941 } 01942 } 01943 return 0; 01944 } 01945 01946 /* Determine whether a group named formatting_specs.lgrps[igrp] exists 01947 * in a netCDF file or group with id ncid. If so, return the count of 01948 * how many matching groups were found, else return a count of 0. If 01949 * the name begins with "/", it is interpreted as an absolute group 01950 * name, in which case only 0 or 1 is returned. Otherwise, interpret 01951 * it as a relative name, and the total number of occurrences within 01952 * the file/group identified by ncid is returned. 01953 * 01954 * Also has side effect of updating the ngrpids and the associate 01955 * grpids array that represent the group list specified by the -g 01956 * option. TODO: put this in its own function instead. 01957 */ 01958 static size_t 01959 nc_inq_grpname_count(int ncid, int igrp) { 01960 size_t count = 0; 01961 #ifdef USE_NETCDF4 01962 int numgrps; 01963 int *ncids; 01964 int g; 01965 int grpid; 01966 int status; 01967 #endif 01968 char *grpname=formatting_specs.lgrps[igrp]; 01969 01970 /* permit empty string to also designate root group */ 01971 if(grpname[0] == '\0' || STREQ(grpname,"/")) { 01972 count = 1; 01973 idadd(formatting_specs.grpids, ncid); 01974 return count; 01975 } 01976 #ifdef USE_NETCDF4 01977 /* Handle absolute group names */ 01978 if(grpname[0] == '/') { 01979 int grpid; 01980 status = nc_inq_grp_full_ncid(ncid, grpname, &grpid); 01981 if(status == NC_NOERR) { 01982 count = 1; 01983 idadd(formatting_specs.grpids, grpid); 01984 } else if(status == NC_ENOGRP) { 01985 count = 0; 01986 } else { 01987 error("when looking up group %s: %s ", grpname, nc_strerror(status)); 01988 } 01989 return count; 01990 } 01991 01992 /* look in this group */ 01993 status = nc_inq_grp_ncid(ncid, grpname, &grpid); 01994 if (status == NC_NOERR) { 01995 count++; 01996 idadd(formatting_specs.grpids, grpid); 01997 } 01998 /* if this group has subgroups, call recursively on each of them */ 01999 NC_CHECK( nc_inq_grps(ncid, &numgrps, NULL) ); 02000 if(numgrps > 0) { 02001 /* Allocate memory to hold the list of group ids. */ 02002 ncids = emalloc(numgrps * sizeof(int)); 02003 /* Get the list of group ids. */ 02004 NC_CHECK( nc_inq_grps(ncid, NULL, ncids) ); 02005 /* Call this function recursively for each group. */ 02006 for (g = 0; g < numgrps; g++) { 02007 count += nc_inq_grpname_count(ncids[g], igrp); 02008 } 02009 free(ncids); 02010 } 02011 #endif /* USE_NETCDF4 */ 02012 return count; 02013 } 02014 02015 /* Check if any group names specified with "-g grp1,...,grpn" are 02016 * missing. Returns total number of matching groups if no missing 02017 * groups detected, otherwise exits. */ 02018 static int 02019 grp_matches(int ncid) { 02020 int ig; 02021 size_t total = 0; 02022 02023 for (ig=0; ig < formatting_specs.nlgrps; ig++) { 02024 size_t count = nc_inq_grpname_count(ncid, ig); 02025 if(count == 0) { 02026 error("%s: No such group", formatting_specs.lgrps[ig]); 02027 return 0; 02028 } 02029 total += count; 02030 } 02031 return total; 02032 } 02033 02034 #ifdef USE_DAP 02035 #define DAP_CLIENT_CACHE_DIRECTIVE "[cache]" 02036 /* replace path string with same string prefixed by 02037 * DAP_CLIENT_NCDUMP_DIRECTIVE */ 02038 static 02039 void adapt_url_for_cache(char **pathp) { 02040 char prefix[] = DAP_CLIENT_CACHE_DIRECTIVE; 02041 char* path = *pathp; 02042 char *tmp_path = strdup(path); 02043 path = (char *)emalloc(strlen(prefix) + strlen(tmp_path) + 1); 02044 path[0] = '\0'; 02045 strncat(path, prefix, strlen(prefix)); 02046 strncat(path, tmp_path, strlen(tmp_path)); 02047 free(tmp_path); 02048 *pathp = path; 02049 return; 02050 } 02051 #endif 02052 02318 int 02319 main(int argc, char *argv[]) 02320 { 02321 int c; 02322 int i; 02323 int max_len = 80; /* default maximum line length */ 02324 int nameopt = 0; 02325 boolean xml_out = false; /* if true, output NcML instead of CDL */ 02326 boolean kind_out = false; /* if true, just output kind of netCDF file */ 02327 02328 #if defined(WIN32) || defined(msdos) || defined(WIN64) 02329 putenv("PRINTF_EXPONENT_DIGITS=2"); /* Enforce unix/linux style exponent formatting. */ 02330 #endif 02331 02332 #ifdef HAVE_LOCALE_H 02333 setlocale(LC_ALL, "C"); /* CDL may be ambiguous with other locales */ 02334 #endif /* HAVE_LOCALE_H */ 02335 opterr = 1; 02336 progname = argv[0]; 02337 set_formats(FLT_DIGITS, DBL_DIGITS); /* default for float, double data */ 02338 02339 /* If the user called ncdump without arguments, print the usage 02340 * message and return peacefully. */ 02341 if (argc <= 1) 02342 { 02343 usage(); 02344 #ifdef vms 02345 exit(EXIT_SUCCESS); 02346 #else 02347 return EXIT_SUCCESS; 02348 #endif 02349 } 02350 02351 while ((c = getopt(argc, argv, "b:cd:f:g:hikl:n:p:stv:xw")) != EOF) 02352 switch(c) { 02353 case 'h': /* dump header only, no data */ 02354 formatting_specs.header_only = true; 02355 break; 02356 case 'c': /* header, data only for coordinate dims */ 02357 formatting_specs.coord_vals = true; 02358 break; 02359 case 'n': /* 02360 * provide different name than derived from 02361 * file name 02362 */ 02363 formatting_specs.name = optarg; 02364 nameopt = 1; 02365 break; 02366 case 'b': /* brief comments in data section */ 02367 formatting_specs.brief_data_cmnts = true; 02368 switch (tolower(optarg[0])) { 02369 case 'c': 02370 formatting_specs.data_lang = LANG_C; 02371 break; 02372 case 'f': 02373 formatting_specs.data_lang = LANG_F; 02374 break; 02375 default: 02376 error("invalid value for -b option: %s", optarg); 02377 } 02378 break; 02379 case 'f': /* full comments in data section */ 02380 formatting_specs.full_data_cmnts = true; 02381 switch (tolower(optarg[0])) { 02382 case 'c': 02383 formatting_specs.data_lang = LANG_C; 02384 break; 02385 case 'f': 02386 formatting_specs.data_lang = LANG_F; 02387 break; 02388 default: 02389 error("invalid value for -f option: %s", optarg); 02390 } 02391 break; 02392 case 'l': /* maximum line length */ 02393 max_len = (int) strtol(optarg, 0, 0); 02394 if (max_len < 10) { 02395 error("unreasonably small line length specified: %d", max_len); 02396 } 02397 break; 02398 case 'v': /* variable names */ 02399 /* make list of names of variables specified */ 02400 make_lvars (optarg); 02401 break; 02402 case 'g': /* group names */ 02403 /* make list of names of groups specified */ 02404 make_lgrps (optarg); 02405 break; 02406 case 'd': /* specify precision for floats (deprecated, undocumented) */ 02407 set_sigdigs(optarg); 02408 break; 02409 case 'p': /* specify precision for floats, overrides attribute specs */ 02410 set_precision(optarg); 02411 break; 02412 case 'x': /* XML output (NcML) */ 02413 xml_out = true; 02414 break; 02415 case 'k': /* just output what kind of netCDF file */ 02416 kind_out = true; 02417 break; 02418 case 't': /* human-readable strings for date-time values */ 02419 formatting_specs.string_times = true; 02420 formatting_specs.iso_separator = false; 02421 break; 02422 case 'i': /* human-readable strings for data-time values with 'T' separator */ 02423 formatting_specs.string_times = true; 02424 formatting_specs.iso_separator = true; 02425 break; 02426 case 's': /* output special (virtual) attributes for 02427 * netCDF-4 files and variables, including 02428 * _DeflateLevel, _Chunking, _Endianness, 02429 * _Format, _Checksum, _NoFill */ 02430 formatting_specs.special_atts = true; 02431 break; 02432 case 'w': /* with client-side cache for DAP URLs */ 02433 formatting_specs.with_cache = true; 02434 break; 02435 case '?': 02436 usage(); 02437 return EXIT_FAILURE; 02438 } 02439 02440 set_max_len(max_len); 02441 02442 argc -= optind; 02443 argv += optind; 02444 02445 /* If no file arguments left or more than one, print usage message. */ 02446 if (argc != 1) 02447 { 02448 usage(); 02449 return EXIT_FAILURE; 02450 } 02451 02452 i = 0; 02453 02454 init_epsilons(); 02455 02456 { 02457 char *path = strdup(argv[i]); 02458 if(!path) 02459 error("out of memory copying argument %s", argv[i]); 02460 if (!nameopt) 02461 formatting_specs.name = name_path(path); 02462 if (argc > 0) { 02463 int ncid, nc_status; 02464 /* If path is a URL, prefix with client-side directive to 02465 * make ncdump reasonably efficient */ 02466 #ifdef USE_DAP 02467 if(formatting_specs.with_cache) /* by default, don't use cache directive */ 02468 { 02469 extern int nc__testurl(const char*,char**); 02470 /* See if this is a url */ 02471 if(nc__testurl(path, NULL)) { 02472 adapt_url_for_cache(&path); 02473 } 02474 /* else fall thru and treat like a file path */ 02475 } 02476 #endif /*USE_DAP*/ 02477 nc_status = nc_open(path, NC_NOWRITE, &ncid); 02478 if (nc_status != NC_NOERR) { 02479 error("%s: %s", path, nc_strerror(nc_status)); 02480 } 02481 NC_CHECK( nc_inq_format(ncid, &formatting_specs.nc_kind) ); 02482 if (kind_out) { 02483 printf ("%s\n", kind_string(formatting_specs.nc_kind)); 02484 } else { 02485 /* Initialize list of types. */ 02486 init_types(ncid); 02487 /* Check if any vars in -v don't exist */ 02488 if(missing_vars(ncid)) 02489 return EXIT_FAILURE; 02490 if(formatting_specs.nlgrps > 0) { 02491 if(formatting_specs.nc_kind != NC_FORMAT_NETCDF4) { 02492 error("Group list (-g ...) only permitted for netCDF-4 file"); 02493 return EXIT_FAILURE; 02494 } 02495 /* Check if any grps in -g don't exist */ 02496 if(grp_matches(ncid) == 0) 02497 return EXIT_FAILURE; 02498 } 02499 if (xml_out) { 02500 if(formatting_specs.nc_kind == NC_FORMAT_NETCDF4) { 02501 error("NcML output (-x) currently only permitted for netCDF classic model"); 02502 return EXIT_FAILURE; 02503 } 02504 do_ncdumpx(ncid, path); 02505 } else { 02506 do_ncdump(ncid, path); 02507 } 02508 } 02509 NC_CHECK( nc_close(ncid) ); 02510 } 02511 free(path); 02512 } 02513 #ifdef vms 02514 exit(EXIT_SUCCESS); 02515 #else 02516 return EXIT_SUCCESS; 02517 #endif 02518 } 02519 END_OF_MAIN();