NetCDF  4.3.2
ncdump.c
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 ("&quot;");
00386             break;
00387         case '<':
00388             printf ("&lt;");
00389             break;
00390         case '>':
00391             printf ("&gt;");
00392             break;
00393         case '&':
00394             printf ("&amp;");
00395             break;
00396         case '\n':
00397             printf ("&#xA;");
00398             break;
00399         case '\r':
00400             printf ("&#xD;");
00401             break;
00402         case '\t':
00403             printf ("&#x9;");
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();
 All Data Structures Files Functions Variables Typedefs Defines