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