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

Generated on Sat Sep 15 2012 12:44:33 for netCDF. NetCDF is a Unidata library.