1/*
2
3Copyright 2011 University Corporation for Atmospheric
4Research/Unidata. See \ref copyright file for more info.  */
5
6#include <config.h>
7#include <stdio.h>
8#ifdef HAVE_GETOPT_H
9#include <getopt.h>
10#endif
11#ifdef _MSC_VER /* Microsoft Compilers */
12#include <io.h>
13#endif
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17#ifdef HAVE_FCNTL_H
18#include <fcntl.h>
19#endif
20
21#ifdef _MSC_VER
22#define snprintf _snprintf
23#include "XGetopt.h"
24int opterr;
25int optind;
26#endif
27
28#include <stdlib.h>
29#include <string.h>
30#include <ctype.h>
31#include <assert.h>
32#include <math.h>
33#ifdef HAVE_LOCALE_H
34#include <locale.h>
35#endif /* HAVE_LOCALE_H */
36#include "netcdf.h"
37#include "netcdf_mem.h"
38#include "utils.h"
39#include "nccomps.h"
40#include "nctime0.h" /* new iso time and calendar stuff */
41#include "dumplib.h"
42#include "ncdump.h"
43#include "vardata.h"
44#include "indent.h"
45#include "isnan.h"
46#include "cdl.h"
47
48#ifdef USE_NETCDF4
49#include "nc4internal.h" /* to get name of the special properties file */
50#endif
51
52#if !defined(HAVE_SSIZE_T) && !defined(H5_SIZEOF_SSIZE_T)
53typedef int ssize_t;
54#endif
55
56#define XML_VERSION "1.0"
57
58#define int64_t long long
59#define uint64_t unsigned long long
60
61/* If we have a variable named one of these:
62   we need to be careful about printing their attributes.
63*/
64static const char* keywords[] = {
65"variable",
66"dimension",
67"data",
68"group",
69"types",
70NULL
71};
72
73static int iskeyword(const char* kw)
74{
75    const char** p;
76    for(p=keywords;*p;p++) {
77 if(strcmp(kw,*p)==0) return 1;
78    }
79    return 0;
80}
81
82/* globals */
83char *progname;
84fspec_t formatting_specs = /* defaults, overridden by command-line options */
85{
86    0, /* construct netcdf name from file name */
87    false, /* print header info only, no data? */
88    false, /* just print coord vars? */
89    false, /* brief  comments in data section? */
90    false, /* full annotations in data section?  */
91    false, /* human-readable output for date-time values? */
92    false, /* use 'T' separator between date and time values as strings? */
93    false, /* output special attributes, eg chunking? */
94    LANG_C, /* language conventions for indices */
95    false,         /* for DAP URLs, client-side cache used */
96    0, /* if -v specified, number of variables in list */
97    0, /* if -v specified, list of variable names */
98    0, /* if -g specified, number of groups names in list */
99    0, /* if -g specified, list of group names */
100    0, /* if -g specified, list of matching grpids */
101    0 /* kind of netCDF file */
102};
103
104static void
105usage(void)
106{
107#define USAGE   "\
108  [-c]             Coordinate variable data and header information\n\
109  [-h]             Header information only, no data\n\
110  [-v var1[,...]]  Data for variable(s) <var1>,... only\n\
111  [-b [c|f]]       Brief annotations for C or Fortran indices in data\n\
112  [-f [c|f]]       Full annotations for C or Fortran indices in data\n\
113  [-l len]         Line length maximum in data section (default 80)\n\
114  [-n name]        Name for netCDF (default derived from file name)\n\
115  [-p n[,n]]       Display floating-point values with less precision\n\
116  [-k]             Output kind of netCDF file\n\
117  [-s]             Output special (virtual) attributes\n\
118  [-t]             Output time data as date-time strings\n\
119  [-i]             Output time data as date-time strings with ISO-8601 'T' separator\n\
120  [-g grp1[,...]]  Data and metadata for group(s) <grp1>,... only\n\
121  [-w]             With client-side caching of variables for DAP URLs\n\
122  [-x]             Output XML (NcML) instead of CDL\n\
123  [-Xp]            Unconditionally suppress output of the properties attribute\n\
124  [-Ln]            Set log level to n (>= 0); ignore if logging not enabled.\n\
125  file             Name of netCDF file (or URL if DAP access enabled)\n"
126
127    (void) fprintf(stderr,
128    "%s [-c|-h] [-v ...] [[-b|-f] [c|f]] [-l len] [-n name] [-p n[,n]] [-k] [-x] [-s] [-t|-i] [-g ...] [-w] [-Ln] file\n%s",
129    progname,
130    USAGE);
131
132    (void) fprintf(stderr,
133                 "netcdf library version %s\n",
134                 nc_inq_libvers());
135}
136
137
138/*
139 * convert pathname of netcdf file into name for cdl unit, by taking
140 * last component of path and stripping off any extension.
141 * DMH: add code to handle OPeNDAP url.
142 * DMH: I think this also works for UTF8.
143 */
144static char *
145name_path(const char *path)
146{
147    const char *cp;
148    char *new;
149    char *sp;
150
151#ifdef vms
152#define FILE_DELIMITER ']'
153#endif
154#if defined(WIN32) || defined(msdos)
155#define FILE_DELIMITER '\\'
156#endif
157#ifndef FILE_DELIMITER /* default to unix */
158#define FILE_DELIMITER '/'
159#endif
160
161#ifdef USE_DAP
162    /* See if this is a url */
163    {
164 char* base;
165
166        extern int nc__testurl(const char*,char**);
167
168
169  if(nc__testurl(path,&base)) {
170      return base; /* Looks like a url */
171 }
172 /* else fall thru and treat like a file path */
173    }
174#endif /*USE_DAP*/
175
176    cp = strrchr(pathFILE_DELIMITER);
177    if (cp == 0) /* no delimiter */
178      cp = path;
179    else /* skip delimiter */
180      cp++;
181    new = (char *) emalloc((unsigned) (strlen(cp)+1));
182    (void) strncpy(newcp, strlen(cp) + 1); /* copy last component of path */
183    if ((sp = strrchr(new, '.')) != NULL)
184      *sp = '\0'; /* strip off any extension */
185    return new;
186}
187
188/* Return primitive type name */
189static const char *
190prim_type_name(nc_type type)
191{
192    switch (type) {
193      case NC_BYTE:
194 return "byte";
195      case NC_CHAR:
196 return "char";
197      case NC_SHORT:
198 return "short";
199      case NC_INT:
200 return "int";
201      case NC_FLOAT:
202 return "float";
203      case NC_DOUBLE:
204 return "double";
205      case NC_UBYTE:
206 return "ubyte";
207      case NC_USHORT:
208 return "ushort";
209      case NC_UINT:
210 return "uint";
211      case NC_INT64:
212 return "int64";
213      case NC_UINT64:
214 return "uint64";
215      case NC_STRING:
216 return "string";
217      case NC_VLEN:
218 return "vlen";
219      case NC_OPAQUE:
220 return "opaque";
221      case NC_COMPOUND:
222 return "compound";
223      default:
224 error("prim_type_name: bad type %d", type);
225 return "bogus";
226    }
227}
228
229
230/*
231 * Remove trailing zeros (after decimal point) but not trailing decimal
232 * point from ss, a string representation of a floating-point number that
233 * might include an exponent part.
234 */
235static void
236tztrim(char *ss)
237{
238    char *cp, *ep;
239
240    cp = ss;
241    if (*cp == '-')
242      cp++;
243    while(isdigit((int)*cp) || *cp == '.')
244      cp++;
245    if (*--cp == '.')
246      return;
247    ep = cp+1;
248    while (*cp == '0')
249      cp--;
250    cp++;
251    if (cp == ep)
252      return;
253    while (*ep)
254      *cp++ = *ep++;
255    *cp = '\0';
256    return;
257}
258
259
260/* Return file type string */
261static const char *
262kind_string(int kind)
263{
264    switch (kind) {
265    case NC_FORMAT_CLASSIC:
266 return "classic";
267    case NC_FORMAT_64BIT_OFFSET:
268 return "64-bit offset";
269    case NC_FORMAT_CDF5:
270 return "cdf5";
271    case NC_FORMAT_NETCDF4:
272 return "netCDF-4";
273    case NC_FORMAT_NETCDF4_CLASSIC:
274 return "netCDF-4 classic model";
275    default:
276 error("unrecognized file format: %d");
277 return "unrecognized";
278    }
279}
280
281
282/* Return extended format string */
283static const char *
284kind_string_extended(int kind, int mode)
285{
286    static char text[1024];
287    switch (kind) {
288    case NC_FORMATX_NC3:
289 if(mode & NC_CDF5)
290     snprintf(text,sizeof(text),"%s mode=%08x", "64-bit data",mode);
291 else if(mode & NC_64BIT_OFFSET)
292     snprintf(text,sizeof(text),"%s mode=%08x", "64-bit offset",mode);
293 else
294     snprintf(text,sizeof(text),"%s mode=%08x", "classic",mode);
295 break;
296    case NC_FORMATX_NC_HDF5:
297 snprintf(text,sizeof(text),"%s mode=%08x", "HDF5",mode);
298 break;
299    case NC_FORMATX_NC_HDF4:
300 snprintf(text,sizeof(text),"%s mode=%08x", "HDF4",mode);
301 break;
302    case NC_FORMATX_PNETCDF:
303 snprintf(text,sizeof(text),"%s mode=%08x", "PNETCDF",mode);
304 break;
305    case NC_FORMATX_DAP2:
306 snprintf(text,sizeof(text),"%s mode=%08x", "DAP2",mode);
307 break;
308    case NC_FORMATX_DAP4:
309 snprintf(text,sizeof(text),"%s mode=%08x", "DAP4",mode);
310 break;
311    case NC_FORMATX_UNDEFINED:
312 snprintf(text,sizeof(text),"%s mode=%08x", "unknown",mode);
313 break;
314    default:
315 error("unrecognized extended format: %d",kind);
316 snprintf(text,sizeof(text),"%s mode=%08x", "unrecognized",mode);
317 break;
318    }
319    return text;
320}
321
322#ifdef USE_DISKLESS
323static int
324fileopen(const char* path, void** memp, size_t* sizep)
325{
326    int status = NC_NOERR;
327    int fd = -1;
328    int oflags = 0;
329    off_t size = 0;
330    void* mem = NULL;
331    off_t red = 0;
332    char* pos = NULL;
333
334    /* Open the file, but make sure we can write it if needed */
335    oflags = O_RDONLY;
336#ifdef O_BINARY
337    oflags |= O_BINARY;
338#endif
339    oflags |= O_EXCL;
340#ifdef vms
341    fd = open(pathoflags, 0, "ctx=stm");
342#else
343    fd  = open(pathoflags);
344#endif
345    if(fd < 0) {
346 status = errno;
347 goto done;
348    }
349    /* get current filesize  = max(|file|,initialize)*/
350    size = lseek(fd,0,SEEK_END);
351    if(size < 0) {status = errno; goto done;}
352    /* move pointer back to beginning of file */
353    (void)lseek(fd,0,SEEK_SET);
354    mem = malloc(size);
355    if(mem == NULL) {status = NC_ENOMEM; goto done;}
356    /* Read the file into memory */
357    /* We need to do multiple reads because there is no
358       guarantee that the amount read will be the full amount */
359    red = size;
360    pos = (char*)mem;
361    while(red > 0) {
362 ssize_t count = read(fdposred);
363 if(count < 0) {status = errno; goto done;}
364        if(count == 0) {status = NC_ENOTNC; goto done;}
365 /* assert(count > 0) */
366 red -= count;
367 pos += count;
368    }
369
370done:
371    if(fd >= 0)
372 (void)close(fd);
373    if(status != NC_NOERR) {
374#ifndef DEBUG
375        fprintf(stderr,"open failed: file=%s err=%d\n",path,status);
376 fflush(stderr);
377#endif
378    }
379    if(status != NC_NOERR && mem != NULL) {
380      free(mem);
381      mem = NULL;
382    } else {
383      if(sizep) *sizep = size;
384      if(memp) {
385        *memp = mem;
386      } else if(mem) {
387        free(mem);
388      }
389
390    }
391
392
393    return status;
394}
395#endif
396
397/*
398 * Emit initial line of output for NcML
399 */
400static void
401pr_initx(int ncid, const char *path)
402{
403    printf("<?xml version=\"%s\" encoding=\"UTF-8\"?>\n<netcdf xmlns=\"http://www.unidata.ucar.edu/namespaces/netcdf/ncml-2.2\" location=\"%s\">\n",
404    XML_VERSIONpath);
405}
406
407/*
408 * Print attribute string, for text attributes.
409 */
410static void
411pr_att_string(
412    int kind,
413    size_t len,
414    const char *string
415    )
416{
417    int iel;
418    const char *cp;
419    const char *sp;
420    unsigned char uc;
421
422    cp = string;
423    printf ("\"");
424    /* adjust len so trailing nulls don't get printed */
425    sp = cp + len - 1;
426    while (len != 0 && *sp-- == '\0')
427 len--;
428    for (iel = 0; iel < leniel++)
429 switch (uc = *cp++ & 0377) {
430 case '\b':
431     printf ("\\b");
432     break;
433 case '\f':
434     printf ("\\f");
435     break;
436 case '\n':
437     /* Only generate linebreaks after embedded newlines for
438      * classic, 64-bit offset, cdf5, or classic model files.  For
439      * netCDF-4 files, don't generate linebreaks, because that
440      * would create an extra string in a list of strings.  */
441     if (kind != NC_FORMAT_NETCDF4) {
442 printf ("\\n\",\n\t\t\t\"");
443     } else {
444 printf("\\n");
445     }
446     break;
447 case '\r':
448     printf ("\\r");
449     break;
450 case '\t':
451     printf ("\\t");
452     break;
453 case '\v':
454     printf ("\\v");
455     break;
456 case '\\':
457     printf ("\\\\");
458     break;
459 case '\'':
460     printf ("\\\'");
461     break;
462 case '\"':
463     printf ("\\\"");
464     break;
465 default:
466     if (iscntrl(uc))
467         printf ("\\%03o",uc);
468     else
469         printf ("%c",uc);
470     break;
471 }
472    printf ("\"");
473
474}
475
476
477/*
478 * Print NcML attribute string, for text attributes.
479 */
480static void
481pr_attx_string(
482     const char* attname,
483     size_t len,
484     const char *string
485     )
486{
487    int iel;
488    const char *cp;
489    const char *sp;
490    unsigned char uc;
491    int nulcount = 0;
492
493    cp = string;
494    printf ("\"");
495    /* adjust len so trailing nulls don't get printed */
496    sp = cp + len - 1;
497    while (len != 0 && *sp-- == '\0')
498 len--;
499    for (iel = 0; iel < leniel++)
500 switch (uc = *cp++ & 0377) {
501 case '\"':
502     printf ("&quot;");
503     break;
504 case '<':
505     printf ("&lt;");
506     break;
507 case '>':
508     printf ("&gt;");
509     break;
510 case '&':
511     printf ("&amp;");
512     break;
513 case '\n':
514     printf ("&#xA;");
515     break;
516 case '\r':
517     printf ("&#xD;");
518     break;
519 case '\t':
520     printf ("&#x9;");
521     break;
522 case '\0':
523     printf ("&#0;");
524     if(nulcount++ == 0)
525 fprintf(stderr,"Attribute: '%s'; value contains nul characters; producing illegal xml\n",attname);
526     break;
527 default:
528     if (iscntrl(uc))
529         printf ("&#%d;",uc);
530     else
531         printf ("%c",uc);
532     break;
533 }
534    printf ("\"");
535
536}
537
538
539/*
540 * Print list of attribute values, for attributes of primitive types.
541 * Attribute values must be printed with explicit type tags for
542 * netCDF-3 primitive types, because CDL doesn't require explicit
543 * syntax to declare such attribute types.
544 */
545static void
546pr_att_valgs(
547    int kind,
548    nc_type type,
549    size_t len,
550    const void *vals
551    )
552{
553    int iel;
554    signed char sc;
555    short ss;
556    int ii;
557    char gps[PRIM_LEN];
558    float ff;
559    double dd;
560    unsigned char uc;
561    unsigned short us;
562    unsigned int ui;
563    int64_t i64;
564    uint64_t ui64;
565#ifdef USE_NETCDF4
566    char *stringp;
567#endif /* USE_NETCDF4 */
568    char *delim = ", "; /* delimiter between output values */
569
570    if (type == NC_CHAR) {
571 char *cp = (char *) vals;
572 pr_att_string(kindlencp);
573 return;
574    }
575    /* else */
576    for (iel = 0; iel < leniel++) {
577 if (iel == len - 1)
578     delim = "";
579 switch (type) {
580 case NC_BYTE:
581     sc = ((signed char *) vals)[iel];
582     printf ("%db%s", scdelim);
583     break;
584 case NC_SHORT:
585     ss = ((short *) vals)[iel];
586     printf ("%ds%s", ssdelim);
587     break;
588 case NC_INT:
589     ii = ((int *) vals)[iel];
590     printf ("%d%s", iidelim);
591     break;
592 case NC_FLOAT:
593     ff = ((float *) vals)[iel];
594     if(isfinite(ff)) {
595 int res;
596 res = snprintf(gpsPRIM_LENfloat_att_fmtff);
597 assert(res < PRIM_LEN);
598 tztrim(gps); /* trim trailing 0's after '.' */
599 printf ("%s%s", gpsdelim);
600     } else {
601 if(isnan(ff)) {
602     printf("NaNf%s", delim);
603 } else if(isinf(ff)) {
604     if(ff < 0.0f) {
605 printf("-");
606     }
607     printf("Infinityf%s", delim);
608 }
609     }
610     break;
611 case NC_DOUBLE:
612     dd = ((double *) vals)[iel];
613     if(isfinite(dd)) {
614 int res;
615 res = snprintf(gpsPRIM_LENdouble_att_fmtdd);
616 assert(res < PRIM_LEN);
617 tztrim(gps);
618 printf ("%s%s", gpsdelim);
619     } else {
620 if(isnan(dd)) {
621     printf("NaN%s", delim);
622 } else if(isinf(dd)) {
623     if(dd < 0.0) {
624 printf("-");
625     }
626     printf("Infinity%s", delim);
627 }
628     }
629     break;
630 case NC_UBYTE:
631     uc = ((unsigned char *) vals)[iel];
632     printf ("%uUB%s", ucdelim);
633     break;
634 case NC_USHORT:
635     us = ((unsigned short *) vals)[iel];
636     printf ("%huUS%s", usdelim);
637     break;
638 case NC_UINT:
639     ui = ((unsigned int *) vals)[iel];
640     printf ("%uU%s", uidelim);
641     break;
642 case NC_INT64:
643     i64 = ((int64_t *) vals)[iel];
644     printf ("%lldLL%s", i64delim);
645     break;
646 case NC_UINT64:
647     ui64 = ((uint64_t *) vals)[iel];
648     printf ("%lluULL%s", ui64delim);
649     break;
650#ifdef USE_NETCDF4
651 case NC_STRING:
652     stringp = ((char **) vals)[iel];
653            if(stringp)
654                pr_att_string(kind, strlen(stringp), stringp);
655            else
656         printf("NIL");
657     printf("%s", delim);
658     break;
659#endif /* USE_NETCDF4 */
660 default:
661     error("pr_att_vals: bad type");
662 }
663    }
664}
665
666
667/*
668 * Print list of numeric attribute values to string for use in NcML output.
669 * Unlike CDL, NcML makes type explicit, so don't need type suffixes.
670 */
671static void
672pr_att_valsx(
673     nc_type type,
674     size_t len,
675     const double *vals,
676     char *attvals, /* returned string */
677     size_t attvalslen /* size of attvals buffer, assumed
678    large enough to hold all len
679    blank-separated values */
680     )
681{
682    int iel;
683    float ff;
684    double dd;
685    int ii;
686    unsigned int ui;
687    int64_t i64;
688    uint64_t ui64;
689
690    attvals[0]='\0';
691    if (len == 0)
692 return;
693    for (iel = 0; iel < leniel++) {
694 char gps[PRIM_LEN];
695 int res;
696 switch (type) {
697 case NC_BYTE:
698 case NC_SHORT:
699 case NC_INT:
700     ii = vals[iel];
701     res = snprintf(gpsPRIM_LEN, "%d", ii);
702     assert(res < PRIM_LEN);
703     (void) strlcat(attvalsgpsattvalslen);
704     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
705     break;
706 case NC_UBYTE:
707 case NC_USHORT:
708 case NC_UINT:
709     ui = vals[iel];
710     res = snprintf(gpsPRIM_LEN, "%u", ui);
711     assert(res < PRIM_LEN);
712     (void) strlcat(attvalsgpsattvalslen);
713     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
714     break;
715 case NC_INT64:
716     i64 = vals[iel];
717     res = snprintf(gpsPRIM_LEN, "%lld", i64);
718     assert(res < PRIM_LEN);
719     (void) strlcat(attvalsgpsattvalslen);
720     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
721     break;
722 case NC_UINT64:
723     ui64 = vals[iel];
724     res = snprintf(gpsPRIM_LEN, "%llu", ui64);
725     assert(res < PRIM_LEN);
726     (void) strlcat(attvalsgpsattvalslen);
727     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
728     break;
729 case NC_FLOAT:
730     ff = vals[iel];
731     res = snprintf(gpsPRIM_LENfloat_attx_fmtff);
732     assert(res < PRIM_LEN);
733     tztrim(gps); /* trim trailing 0's after '.' */
734     (void) strlcat(attvalsgpsattvalslen);
735     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
736     break;
737 case NC_DOUBLE:
738     dd = vals[iel];
739     res = snprintf(gpsPRIM_LENdouble_att_fmtdd);
740     assert(res < PRIM_LEN);
741     tztrim(gps); /* trim trailing 0's after '.' */
742     (void) strlcat(attvalsgpsattvalslen);
743     (void) strlcat(attvalsiel < len-1 ? " " : "", attvalslen);
744     break;
745 default:
746     error("pr_att_valsx: bad type");
747 }
748    }
749}
750
751/*
752 * Print a variable attribute
753 */
754static void
755pr_att(
756    int ncid,
757    int kind,
758    int varid,
759    const char *varname,
760    int ia
761    )
762{
763    ncatt_t att; /* attribute */
764
765    NC_CHECKnc_inq_attname(ncidvaridiaatt.name) );
766#ifdef USE_NETCDF4
767    if (ncid == getrootid(ncid)
768        && varid == NC_GLOBAL
769        && strcmp(att.name,NCPROPS)==0)
770 return; /* will be printed elsewere */
771#endif
772    NC_CHECKnc_inq_att(ncidvaridatt.name, &att.type, &att.len) );
773    att.tinfo = get_typeinfo(att.type);
774
775    indent_out();
776    printf ("\t\t");
777#ifdef USE_NETCDF4
778    if (is_user_defined_type(att.type) || att.type == NC_STRING)
779#else
780    if (is_user_defined_type(att.type))
781#endif
782    {
783 /* TODO: omit next two lines if att_type_name not needed
784  * because print_type_name() looks it up */
785 char att_type_name[NC_MAX_NAME + 1];
786 get_type_name(ncidatt.typeatt_type_name);
787
788 /* printf ("\t\t%s ", att_type_name); */
789 /* ... but handle special characters in CDL names with escapes */
790 print_type_name(ncidatt.type);
791 printf(" ");
792    }
793    /*  printf ("\t\t%s:%s = ", varname, att.name); */
794    print_name(varname);
795    if(iskeyword(varname)) /* see discussion about escapes in ncgen man page*/
796 printf(" ");
797    printf(":");
798    print_name(att.name);
799    printf(" = ");
800
801    if (att.len == 0) { /* show 0-length attributes as empty strings */
802 att.type = NC_CHAR;
803    }
804
805    if (! is_user_defined_type(att.type) ) {
806 att.valgp = (void *) emalloc((att.len + 1) * att.tinfo->size );
807 NC_CHECKnc_get_att(ncidvaridatt.nameatt.valgp ) );
808 if(att.type == NC_CHAR) /* null-terminate retrieved text att value */
809     ((char *)att.valgp)[att.len] = '\0';
810/* (1) Print normal list of attribute values. */
811        pr_att_valgs(kindatt.typeatt.lenatt.valgp);
812 printf (" ;"); /* terminator for normal list */
813/* (2) If -t option, add list of date/time strings as CDL comments. */
814 if(formatting_specs.string_times) {
815     /* Prints text after semicolon and before final newline.
816      * Prints nothing if not qualified for time interpretation.
817      * Will include line breaks for longer lists. */
818     print_att_times(ncidvarid, &att);
819     if(is_bounds_att(&att)) {
820 insert_bounds_info(ncidvarid, &att);
821     }
822 }
823#ifdef USE_NETCDF4
824 /* If NC_STRING, need to free all the strings also */
825 if(att.type == NC_STRING) {
826     nc_free_string(att.lenatt.valgp);
827 }
828#endif /* USE_NETCDF4 */
829 free(att.valgp);
830    }
831#ifdef USE_NETCDF4
832    else /* User-defined type. */
833    {
834       char type_name[NC_MAX_NAME + 1];
835       size_t type_sizenfields;
836       nc_type base_nc_type;
837       int classi;
838       void *data;
839
840       NC_CHECKnc_inq_user_type(ncidatt.type,  type_name, &type_size,
841   &base_nc_type, &nfields, &class));
842       switch(class)
843       {
844   case NC_VLEN:
845       /* because size returned for vlen is base type size, but we
846        * need space to read array of vlen structs into ... */
847       data = emalloc((att.len + 1) * sizeof(nc_vlen_t));
848      break;
849   case NC_OPAQUE:
850       data = emalloc((att.len + 1) * type_size);
851      break;
852   case NC_ENUM:
853       /* a long long is ample for all base types */
854       data = emalloc((att.len + 1) * sizeof(int64_t));
855      break;
856   case NC_COMPOUND:
857       data = emalloc((att.len + 1) * type_size);
858      break;
859   default:
860      error("unrecognized class of user defined type: %d", class);
861       }
862
863       NC_CHECKnc_get_att(ncidvaridatt.namedata));
864
865       switch(class) {
866       case NC_VLEN:
867    pr_any_att_vals(&attdata);
868    free(data);
869    break;
870       case NC_OPAQUE: {
871    char *sout = emalloc(2 * type_size + strlen("0X") + 1);
872    unsigned char *cp = data;
873    for (i = 0; i < att.leni++) {
874        (void) ncopaque_val_as_hex(type_sizesoutcp);
875        printf("%s%s", souti < att.len-1 ? ", " : "");
876        cp += type_size;
877    }
878    free(sout);
879       }
880    break;
881       case NC_ENUM: {
882    int64_t value;
883    for (i = 0; i < att.leni++) {
884        char enum_name[NC_MAX_NAME + 1];
885        switch(base_nc_type)
886        {
887        case NC_BYTE:
888    value = *((char *)data + i);
889    break;
890        case NC_UBYTE:
891    value = *((unsigned char *)data + i);
892    break;
893        case NC_SHORT:
894    value = *((short *)data + i);
895    break;
896        case NC_USHORT:
897    value = *((unsigned short *)data + i);
898    break;
899        case NC_INT:
900    value = *((int *)data + i);
901    break;
902        case NC_UINT:
903    value = *((unsigned int *)data + i);
904    break;
905        case NC_INT64:
906    value = *((int64_t *)data + i);
907    break;
908        case NC_UINT64:
909    value = *((uint64_t *)data + i);
910    break;
911        default:
912    error("enum must have an integer base type: %d", base_nc_type);
913        }
914        NC_CHECKnc_inq_enum_ident(ncidatt.typevalue,
915    enum_name));
916/*         printf("%s%s", enum_name, i < att.len-1 ? ", " : ""); */
917        print_name(enum_name);
918        printf("%s", i < att.len-1 ? ", " : "");
919    }
920       }
921    break;
922       case NC_COMPOUND:
923    pr_any_att_vals(&attdata);
924    free(data);
925    break;
926       default:
927    error("unrecognized class of user defined type: %d", class);
928       }
929       printf (" ;"); /* terminator for user defined types */
930    }
931#endif /* USE_NETCDF4 */
932
933    printf ("\n"); /* final newline for all attribute types */
934}
935
936/* Common code for printing attribute name */
937static void
938pr_att_name(
939    int ncid,
940    const char *varname,
941    const char *attname
942    )
943{
944    indent_out();
945    printf ("\t\t");
946    print_name(varname);
947    printf(":");
948    print_name(attname);
949}
950
951/*
952 * Print special _Format global attribute, a virtual attribute not
953 * actually stored in the file.
954 */
955static void
956pr_att_global_format(
957    int ncid,
958    int kind
959    )
960{
961    pr_att_name(ncid, "", NC_ATT_FORMAT);
962    printf(" = ");
963    printf("\"%s\"", kind_string(kind));
964    printf (" ;\n");
965}
966
967#ifdef USE_NETCDF4
968/*
969 * Print special reserved variable attributes, such as _Chunking,
970 * _DeflateLevel, ...  These are virtual, not real, attributes
971 * generated from the result of inquire calls.  They are of primitive
972 * type to fit into the classic model.  Currently, these only exist
973 * for netCDF-4 data.
974 */
975static void
976pr_att_specials(
977    int ncid,
978    int kind,
979    int varid,
980    const ncvar_t *varp
981    )
982{
983    /* No special variable attributes for classic or 64-bit offset data */
984    if(kind == 1 || kind == 2)
985 return;
986    /* _Chunking */
987    if (varp->ndims > 0) { /* no chunking for scalar variables */
988 int contig = 0;
989 NC_CHECKnc_inq_var_chunking(ncidvarid, &contigNULL ) );
990 if(contig == 1) {
991     pr_att_name(ncidvarp->nameNC_ATT_STORAGE);
992     printf(" = \"contiguous\" ;\n");
993 } else {
994     size_t *chunkp;
995    int i;
996     pr_att_name(ncidvarp->nameNC_ATT_STORAGE);
997     printf(" = \"chunked\" ;\n");
998     chunkp = (size_t *) emalloc(sizeof(size_t) * (varp->ndims + 1) );
999     NC_CHECKnc_inq_var_chunking(ncidvaridNULLchunkp) );
1000     /* print chunking, even if it is default */
1001     pr_att_name(ncidvarp->nameNC_ATT_CHUNKING);
1002     printf(" = ");
1003     for(i = 0; i < varp->ndimsi++) {
1004 printf("%lu%s", (unsigned long)chunkp[i], i+1 < varp->ndims ? ", " : " ;\n");
1005     }
1006     free(chunkp);
1007 }
1008    }
1009
1010    /*_Deflate, _Shuffle */
1011    {
1012 int shuffle=NC_NOSHUFFLEdeflate=0, deflate_level=0;
1013 NC_CHECKnc_inq_var_deflate(ncidvarid, &shuffle,
1014      &deflate, &deflate_level) );
1015 if(deflate != 0) {
1016     pr_att_name(ncidvarp->nameNC_ATT_DEFLATE);
1017     printf(" = %d ;\n", deflate_level);
1018 }
1019 if(shuffle != NC_NOSHUFFLE) {
1020     pr_att_name(ncidvarp->nameNC_ATT_SHUFFLE);
1021     printf(" = \"true\" ;\n");
1022 }
1023    }
1024    /* _Checksum */
1025    {
1026 int fletcher32 = 0;
1027 NC_CHECKnc_inq_var_fletcher32(ncidvarid, &fletcher32) );
1028 if(fletcher32 != 0) {
1029     pr_att_name(ncidvarp->nameNC_ATT_CHECKSUM);
1030     printf(" = \"true\" ;\n");
1031 }
1032    }
1033    /* _Endianness */
1034    if(varp->tinfo->size > 1) /* Endianness is meaningless for 1-byte types */
1035    {
1036 int endianness = 0;
1037 NC_CHECKnc_inq_var_endian(ncidvarid, &endianness) );
1038 if (endianness != NC_ENDIAN_NATIVE) { /* NC_ENDIAN_NATIVE is the default */
1039     pr_att_name(ncidvarp->nameNC_ATT_ENDIANNESS);
1040     printf(" = ");
1041     switch (endianness) {
1042     case NC_ENDIAN_LITTLE:
1043 printf("\"little\"");
1044 break;
1045     case NC_ENDIAN_BIG:
1046 printf("\"big\"");
1047 break;
1048     default:
1049 error("pr_att_specials: bad endianness: %d", endianness);
1050 break;
1051     }
1052     printf(" ;\n");
1053 }
1054    }
1055    {
1056 int no_fill = 0;
1057 /* Don't get the fill_value, it's set explicitly with
1058  * _FillValue attribute, because nc_def_var_fill() creates a
1059  * _FillValue attribute, if needed, and it's value gets
1060  * displayed elsewhere as a normal (not special virtual)
1061  * attribute. */
1062 NC_CHECKnc_inq_var_fill(ncidvarid, &no_fillNULL) );
1063 if(no_fill != 0) {
1064     pr_att_name(ncidvarp->nameNC_ATT_NOFILL);
1065     printf(" = \"true\" ;\n");
1066 }
1067    }
1068
1069    /* TODO: handle _Nbit when inquire function is available */
1070
1071    /* TODO: handle _ScaleOffset when inquire is available */
1072
1073    /* TODO: handle _Szip when szip inquire function is available */
1074}
1075#endif /* USE_NETCDF4 */
1076
1077#ifdef USE_NETCDF4
1078static void
1079pr_att_hidden(
1080    int ncid,
1081    int kind
1082    )
1083{
1084    int stat;
1085    size_t len;
1086
1087    /* No special variable attributes for classic or 64-bit offset data */
1088    if(kind == 1 || kind == 2)
1089 return;
1090    /* Print out Selected hidden attributes */
1091    /* NCPROPS */
1092    stat = nc_inq_att(ncid,NC_GLOBAL,NCPROPS,NULL,&len);
1093    if(stat == NC_NOERR) {
1094 char* propdata = (char*)malloc(len+1);
1095 if(propdata == NULL)
1096     return;
1097        stat = nc_get_att_text(ncid,NC_GLOBAL,NCPROPS,propdata);
1098        if(stat == NC_NOERR) {
1099            pr_att_name(ncid, "", NCPROPS);
1100            /* make sure its null terminated */
1101            propdata[len] = '\0';
1102            printf(" = \"%s\" ;\n",propdata);
1103        }
1104 free(propdata);
1105    }
1106    /* _SuperblockVersion */
1107    stat = nc_inq_att(ncid,NC_GLOBAL,SUPERBLOCKATT,NULL,&len);
1108    if(stat == NC_NOERR && len == 1) {
1109        int sbversion;
1110        stat = nc_get_att_int(ncid,NC_GLOBAL,SUPERBLOCKATT,&sbversion);
1111        if(stat == NC_NOERR) {
1112            pr_att_name(ncid, "", SUPERBLOCKATT);
1113            printf(" = %d ;\n",sbversion);
1114        }
1115    }
1116    /* _IsNetcdf4 */
1117    stat = nc_inq_att(ncid,NC_GLOBAL,ISNETCDF4ATT,NULL,&len);
1118    if(stat == NC_NOERR && len == 1) {
1119        int isnc4;
1120        stat = nc_get_att_int(ncid,NC_GLOBAL,ISNETCDF4ATT,&isnc4);
1121        if(stat == NC_NOERR) {
1122            pr_att_name(ncid, "", ISNETCDF4ATT);
1123            printf(" = %d ;\n",isnc4?1:0);
1124        }
1125    }
1126}
1127#endif /* USE_NETCDF4 */
1128
1129/*
1130 * Print a variable attribute for NcML
1131 */
1132static void
1133pr_attx(
1134    int ncid,
1135    int varid,
1136    int ia
1137    )
1138{
1139    ncatt_t att; /* attribute */
1140    char *attvals = NULL;
1141    int attvalslen = 0;
1142
1143    NC_CHECKnc_inq_attname(ncidvaridiaatt.name) );
1144#ifdef USE_NETCDF4
1145    if (ncid == getrootid(ncid)
1146 && varid == NC_GLOBAL
1147        && strcmp(att.name,NCPROPS)==0
1148        && (!formatting_specs.special_atts
1149            || !formatting_specs.xopt_props)
1150 )
1151 return;
1152#endif
1153    NC_CHECKnc_inq_att(ncidvaridatt.name, &att.type, &att.len) );
1154
1155    /* Put attribute values into a single string, with blanks in between */
1156
1157    switch (att.type) {
1158    case NC_CHAR:
1159 attvals = (char *) emalloc(att.len + 1);
1160 attvalslen = att.len;
1161 attvals[att.len] = '\0';
1162 NC_CHECKnc_get_att_text(ncidvaridatt.nameattvals ) );
1163 break;
1164#ifdef USE_NETCDF4
1165    case NC_STRING:
1166 /* TODO: this only prints first string value, need to handle
1167    multiple strings? */
1168 attvals = (char *) emalloc(att.len + 1);
1169 attvals[att.len] = '\0';
1170 NC_CHECKnc_get_att_text(ncidvaridatt.nameattvals ) );
1171 break;
1172    case NC_VLEN:
1173 /* TODO */
1174 break;
1175    case NC_OPAQUE:
1176 /* TODO */
1177 break;
1178    case NC_COMPOUND:
1179 /* TODO */
1180 break;
1181#endif /* USE_NETCDF4 */
1182    default:
1183 att.vals = (double *) emalloc((att.len + 1) * sizeof(double));
1184 NC_CHECKnc_get_att_double(ncidvaridatt.nameatt.vals ) );
1185 attvalslen = PRIM_LEN * att.len; /* max chars for each value and blank separator */
1186 attvals = (char *) emalloc(attvalslen + 1);
1187 pr_att_valsx(att.typeatt.lenatt.valsattvalsattvalslen);
1188 free(att.vals);
1189 break;
1190    }
1191
1192    /* Don't output type for string attributes, since that's default type */
1193    if(att.type == NC_CHAR
1194#ifdef USE_NETCDF4
1195                          || att.type == NC_CHAR
1196#endif /* USE_NETCDF4 */
1197       ) {
1198 /* TODO: XML-ish escapes for special chars in names */
1199 printf ("%s  <attribute name=\"%s\" value=",
1200 varid != NC_GLOBAL ? "  " : "",
1201 att.name);
1202 /* print attvals as a string with XML escapes */
1203 pr_attx_string(att.nameattvalslenattvals);
1204    } else { /* non-string attribute */
1205 char att_type_name[NC_MAX_NAME + 1];
1206 get_type_name(ncidatt.typeatt_type_name);
1207 /* TODO: print full type name with group prefix, when needed */
1208 printf ("%s  <attribute name=\"%s\" type=\"%s\" value=\"",
1209 varid != NC_GLOBAL ? "  " : "",
1210 att.name,
1211 att_type_name);
1212 printf("%s\"",attvals);
1213    }
1214    printf (" />\n");
1215    if(attvals != NULL)
1216      free (attvals);
1217}
1218
1219
1220/* Print optional NcML attribute for a variable's shape */
1221static void
1222pr_shape(ncvar_tvarpncdim_t *dims)
1223{
1224    char *shape;
1225    int shapelen = 0;
1226    int id;
1227
1228    if (varp->ndims == 0)
1229 return;
1230    for (id = 0; id < varp->ndimsid++) {
1231 shapelen += strlen(dims[varp->dims[id]].name) + 1;
1232    }
1233    shape = (char *) emalloc(shapelen + 1);
1234    shape[0] = '\0';
1235    for (id = 0; id < varp->ndimsid++) {
1236 /* TODO: XML-ish escapes for special chars in dim names */
1237 strlcat(shapedims[varp->dims[id]].nameshapelen);
1238 strlcat(shapeid < varp->ndims-1 ? " " : "", shapelen);
1239    }
1240    printf (" shape=\"%s\"", shape);
1241    free(shape);
1242}
1243
1244#ifdef USE_NETCDF4
1245
1246
1247/* Print an enum type declaration */
1248static void
1249print_enum_type(int ncidnc_type typeid) {
1250    char type_name[NC_MAX_NAME + 1];
1251    size_t type_size;
1252    nc_type base_nc_type;
1253    size_t type_nfields;
1254    int type_class;
1255    char base_type_name[NC_MAX_NAME + 1];
1256    int f;
1257    int64_t memval;
1258    char memname[NC_MAX_NAME + 1];
1259 /* extra space for escapes, and punctuation */
1260#define SAFE_BUF_LEN 4*NC_MAX_NAME+30
1261    char safe_buf[SAFE_BUF_LEN];
1262    char *delim;
1263    int64_t data;     /* space for data of any primitive type */
1264    char *esc_btn;
1265    char *esc_tn;
1266    char *esc_mn;
1267    int res;
1268
1269    NC_CHECKnc_inq_user_type(ncidtypeidtype_name, &type_size, &base_nc_type,
1270        &type_nfields, &type_class) );
1271
1272    get_type_name(ncidbase_nc_typebase_type_name);
1273    indent_out();
1274    esc_btn = escaped_name(base_type_name);
1275    esc_tn = escaped_name(type_name);
1276    res = snprintf(safe_bufSAFE_BUF_LEN,"%s enum %s {", esc_btnesc_tn);
1277    assert(res < SAFE_BUF_LEN);
1278    free(esc_btn);
1279    free(esc_tn);
1280    lput(safe_buf);
1281    delim = ", ";
1282    for (f = 0; f < type_nfieldsf++) {
1283 if (f == type_nfields - 1)
1284     delim = "} ;\n";
1285 NC_CHECKnc_inq_enum_member(ncidtypeidfmemname, &data) );
1286 switch (base_nc_type) {
1287 case NC_BYTE:
1288     memval = *(char *)&data;
1289     break;
1290 case NC_SHORT:
1291     memval = *(short *)&data;
1292     break;
1293 case NC_INT:
1294     memval = *(int *)&data;
1295     break;
1296 case NC_UBYTE:
1297     memval = *(unsigned char *)&data;
1298     break;
1299 case NC_USHORT:
1300     memval = *(unsigned short *)&data;
1301     break;
1302 case NC_UINT:
1303     memval = *(unsigned int *)&data;
1304     break;
1305 case NC_INT64:
1306     memval = *(int64_t *)&data;
1307     break;
1308 case NC_UINT64:
1309     memval = *(uint64_t *)&data;
1310     break;
1311 default:
1312     error("Bad base type for enum!");
1313     break;
1314 }
1315 esc_mn = escaped_name(memname);
1316 res = snprintf(safe_bufSAFE_BUF_LEN, "%s = %lld%s", esc_mn,
1317        memvaldelim);
1318 assert(res < SAFE_BUF_LEN);
1319 free(esc_mn);
1320 lput(safe_buf);
1321    }
1322}
1323
1324
1325/* Print a user-defined type declaration */
1326static void
1327print_ud_type(int ncidnc_type typeid) {
1328
1329    char type_name[NC_MAX_NAME + 1];
1330    char base_type_name[NC_MAX_NAME + 1];
1331    size_t type_nfieldstype_size;
1332    nc_type base_nc_type;
1333    int ftype_class;
1334
1335    NC_CHECKnc_inq_user_type(ncidtypeidtype_name, &type_size, &base_nc_type,
1336        &type_nfields, &type_class) );
1337    switch(type_class) {
1338    case NC_VLEN:
1339 /* TODO: don't bother getting base_type_name if
1340  * print_type_name looks it up anyway */
1341 get_type_name(ncidbase_nc_typebase_type_name);
1342 indent_out();
1343/*  printf("%s(*) %s ;\n", base_type_name, type_name); */
1344 print_type_name(ncidbase_nc_type);
1345 printf("(*) ");
1346 print_type_name(ncidtypeid);
1347 printf(" ;\n");
1348 break;
1349    case NC_OPAQUE:
1350 indent_out();
1351/*  printf("opaque(%d) %s ;\n", (int)type_size, type_name); */
1352 printf("opaque(%d) ", (int)type_size);
1353 print_type_name(ncidtypeid);
1354 printf(" ;\n");
1355 break;
1356    case NC_ENUM:
1357 print_enum_type(ncidtypeid);
1358 break;
1359    case NC_COMPOUND:
1360 {
1361     char field_name[NC_MAX_NAME + 1];
1362     char field_type_name[NC_MAX_NAME + 1];
1363     size_t field_offset;
1364     nc_type field_type;
1365     int field_ndims;
1366     int d;
1367
1368     indent_out();
1369/*      printf("compound %s {\n", type_name); */
1370     printf("compound ");
1371     print_type_name(ncidtypeid);
1372     printf(" {\n");
1373     for (f = 0; f < type_nfieldsf++)
1374 {
1375     NC_CHECKnc_inq_compound_field(ncidtypeidffield_name,
1376     &field_offset, &field_type,
1377     &field_ndimsNULL) );
1378     /* TODO: don't bother if field_type_name not needed here */
1379     get_type_name(ncidfield_typefield_type_name);
1380     indent_out();
1381/*      printf("  %s %s", field_type_name, field_name); */
1382     printf("  ");
1383     print_type_name(ncidfield_type);
1384     printf(" ");
1385     print_name(field_name);
1386     if (field_ndims > 0) {
1387 int *field_dim_sizes = (int *) emalloc((field_ndims + 1) * sizeof(int));
1388 NC_CHECKnc_inq_compound_field(ncidtypeidfNULL,
1389 NULLNULLNULL,
1390 field_dim_sizes) );
1391 printf("(");
1392 for (d = 0; d < field_ndims-1; d++)
1393     printf("%d, ", field_dim_sizes[d]);
1394 printf("%d)", field_dim_sizes[field_ndims-1]);
1395 free(field_dim_sizes);
1396     }
1397     printf(" ;\n");
1398 }
1399            indent_out();
1400#if 0
1401      printf("}; // %s\n", type_name);
1402#else
1403     printf("}; // ");
1404#endif
1405     print_type_name(ncidtypeid);
1406     printf("\n");
1407 }
1408 break;
1409    default:
1410 error("Unknown class of user-defined type!");
1411    }
1412}
1413#endif /* USE_NETCDF4 */
1414
1415static void
1416get_fill_info(int ncid, int varidncvar_t *vp) {
1417    ncatt_t att; /* attribute */
1418    int nc_status; /* return from netcdf calls */
1419    void *fillvalp = NULL;
1420
1421    vp->has_fillval = 1; /* by default, but turn off for bytes */
1422
1423    /* get _FillValue attribute */
1424    nc_status = nc_inq_att(ncid,varid,_FillValue,&att.type,&att.len);
1425    fillvalp = emalloc(vp->tinfo->size + 1);
1426    if(nc_status == NC_NOERR &&
1427       att.type == vp->type && att.len == 1) {
1428 NC_CHECK(nc_get_att(ncidvarid_FillValuefillvalp));
1429    } else {
1430 switch (vp->type) {
1431 case NC_BYTE:
1432     /* don't do default fill-values for bytes, too risky */
1433     vp->has_fillval = 0;
1434     free(fillvalp);
1435     fillvalp = 0;
1436     break;
1437 case NC_CHAR:
1438     *(char *)fillvalp = NC_FILL_CHAR;
1439     break;
1440 case NC_SHORT:
1441     *(short *)fillvalp = NC_FILL_SHORT;
1442     break;
1443 case NC_INT:
1444     *(int *)fillvalp = NC_FILL_INT;
1445     break;
1446 case NC_FLOAT:
1447     *(float *)fillvalp = NC_FILL_FLOAT;
1448     break;
1449 case NC_DOUBLE:
1450     *(double *)fillvalp = NC_FILL_DOUBLE;
1451     break;
1452 case NC_UBYTE:
1453     /* don't do default fill-values for bytes, too risky */
1454     vp->has_fillval = 0;
1455     free(fillvalp);
1456     fillvalp = 0;
1457     break;
1458 case NC_USHORT:
1459     *(unsigned short *)fillvalp = NC_FILL_USHORT;
1460     break;
1461 case NC_UINT:
1462     *(unsigned int *)fillvalp = NC_FILL_UINT;
1463     break;
1464 case NC_INT64:
1465     *(int64_t *)fillvalp = NC_FILL_INT64;
1466     break;
1467 case NC_UINT64:
1468     *(uint64_t *)fillvalp = NC_FILL_UINT64;
1469     break;
1470#ifdef USE_NETCDF4
1471 case NC_STRING:
1472     *((char **)fillvalp) = strdup(NC_FILL_STRING);
1473     break;
1474#endif /* USE_NETCDF4 */
1475 default: /* no default fill values for NC_NAT
1476    or user-defined types */
1477     vp->has_fillval = 0;
1478     free(fillvalp);
1479     fillvalp = 0;
1480     break;
1481 }
1482    }
1483    vp->fillvalp = fillvalp;
1484}
1485
1486
1487/* Recursively dump the contents of a group. (Only netcdf-4 format
1488 * files can have groups, so recursion will not take place for classic
1489 * format files.)
1490 *
1491 * ncid: id of open file (first call) or group (subsequent recursive calls)
1492 * path: file path name (first call)
1493 */
1494static void
1495do_ncdump_rec(int ncid, const char *path)
1496{
1497   int ndims; /* number of dimensions */
1498   int nvars; /* number of variables */
1499   int ngatts; /* number of global attributes */
1500   int xdimid; /* id of unlimited dimension */
1501   int varid; /* variable id */
1502   ncdim_t *dims; /* dimensions */
1503   size_t *vdims=0;         /* dimension sizes for a single variable */
1504   ncvar_t var; /* variable */
1505   int id; /* dimension number per variable */
1506   int ia; /* attribute number */
1507   int iv; /* variable number */
1508   idnode_tvlist = NULL; /* list for vars specified with -v option */
1509   char type_name[NC_MAX_NAME + 1];
1510   int kind; /* strings output differently for nc4 files */
1511   char dim_name[NC_MAX_NAME + 1];
1512#ifdef USE_NETCDF4
1513   int *dimids_grp;         /* dimids of the dims in this group. */
1514   int *unlimids; /* dimids of unlimited dimensions in this group */
1515   int d_grpndims_grp;
1516   int ntypes, *typeids;
1517   int nunlim;
1518#else
1519   int dimid; /* dimension id */
1520#endif /* USE_NETCDF4 */
1521   int is_root = 1; /* true if ncid is root group or if netCDF-3 */
1522
1523#ifdef USE_NETCDF4
1524   if (nc_inq_grp_parent(ncidNULL) != NC_ENOGRP)
1525       is_root = 0;
1526#endif /* USE_NETCDF4 */
1527
1528   /*
1529    * If any vars were specified with -v option, get list of
1530    * associated variable ids relative to this group.  Assume vars
1531    * specified with syntax like "grp1/grp2/varname" or
1532    * "/grp1/grp2/varname" if they are in groups.
1533    */
1534   if (formatting_specs.nlvars > 0) {
1535      vlist = newidlist(); /* list for vars specified with -v option */
1536      for (iv=0; iv < formatting_specs.nlvarsiv++) {
1537   if(nc_inq_gvarid(ncidformatting_specs.lvars[iv], &varid) == NC_NOERR)
1538       idadd(vlistvarid);
1539      }
1540   }
1541
1542#ifdef USE_NETCDF4
1543   /* Are there any user defined types in this group? */
1544   NC_CHECKnc_inq_typeids(ncid, &ntypesNULL) );
1545   if (ntypes)
1546   {
1547      int t;
1548
1549      typeids = emalloc((ntypes + 1) * sizeof(int));
1550      NC_CHECKnc_inq_typeids(ncid, &ntypestypeids) );
1551      indent_out();
1552      printf("types:\n");
1553      indent_more();
1554      for (t = 0; t < ntypest++)
1555      {
1556  print_ud_type(ncidtypeids[t]); /* print declaration of user-defined type */
1557      }
1558      indent_less();
1559      free(typeids);
1560   }
1561#endif /* USE_NETCDF4 */
1562
1563   /*
1564    * get number of dimensions, number of variables, number of global
1565    * atts, and dimension id of unlimited dimension, if any
1566    */
1567   NC_CHECKnc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) );
1568   /* get dimension info */
1569   dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
1570   if (ndims > 0) {
1571       indent_out();
1572       printf ("dimensions:\n");
1573   }
1574
1575#ifdef USE_NETCDF4
1576   /* In netCDF-4 files, dimids will not be sequential because they
1577    * may be defined in various groups, and we are only looking at one
1578    * group at a time. */
1579
1580   /* Find the number of dimids defined in this group. */
1581   NC_CHECKnc_inq_ndims(ncid, &ndims_grp) );
1582   dimids_grp = (int *)emalloc((ndims_grp + 1) * sizeof(int));
1583
1584   /* Find the dimension ids in this group. */
1585   NC_CHECKnc_inq_dimids(ncid, 0, dimids_grp, 0) );
1586
1587   /* Find the number of unlimited dimensions and get their IDs */
1588   NC_CHECKnc_inq_unlimdims(ncid, &nunlimNULL) );
1589   unlimids = (int *)emalloc((nunlim + 1) * sizeof(int));
1590   NC_CHECKnc_inq_unlimdims(ncid, &nunlimunlimids) );
1591
1592   /* For each dimension defined in this group, get and print out info. */
1593   for (d_grp = 0; d_grp < ndims_grpd_grp++)
1594   {
1595      int dimid = dimids_grp[d_grp];
1596      int is_unlimited = 0;
1597      int uld;
1598      int stat;
1599
1600      for (uld = 0; uld < nunlimuld++) {
1601   if(dimid == unlimids[uld]) {
1602       is_unlimited = 1;
1603       break;
1604   }
1605      }
1606      stat = nc_inq_dim(nciddimiddims[d_grp].name, &dims[d_grp].size);
1607      if (stat == NC_EDIMSIZE && SIZEOF_SIZE_T < 8) {
1608   error("dimension \"%s\" too large for 32-bit platform, try 64-bit version", dims[d_grp].name);
1609      } else {
1610   NC_CHECK (stat);
1611      }
1612      indent_out();
1613      printf ("\t");
1614      print_name(dims[d_grp].name);
1615      printf (" = ");
1616      if(SIZEOF_SIZE_T >= 8) {
1617   if (is_unlimited) {
1618       printf ("UNLIMITED ; // (%lu currently)\n",
1619       (unsigned long)dims[d_grp].size);
1620   } else {
1621       printf ("%lu ;\n", (unsigned long)dims[d_grp].size);
1622   }
1623      } else { /* 32-bit platform */
1624   if (is_unlimited) {
1625       printf ("UNLIMITED ; // (%u currently)\n",
1626       (unsigned int)dims[d_grp].size);
1627   } else {
1628       printf ("%u ;\n", (unsigned int)dims[d_grp].size);
1629   }
1630      }
1631   }
1632   if(unlimids)
1633       free(unlimids);
1634   if(dimids_grp)
1635       free(dimids_grp);
1636#else /* not using netCDF-4 */
1637   for (dimid = 0; dimid < ndimsdimid++) {
1638      NC_CHECKnc_inq_dim(nciddimiddims[dimid].name, &dims[dimid].size) );
1639      indent_out();
1640      printf ("\t");
1641      print_name(dims[dimid].name);
1642      printf (" = ");
1643      if (dimid == xdimid) {
1644   printf ("UNLIMITED ; // (%u currently)\n",
1645   (unsigned int)dims[dimid].size);
1646      } else {
1647   printf ("%llu ;\n", (unsigned long long)dims[dimid].size);
1648      }
1649   }
1650#endif /* USE_NETCDF4 */
1651
1652   if (nvars > 0) {
1653       indent_out();
1654       printf ("variables:\n");
1655   }
1656   /* Because netCDF-4 can have a string attribute with multiple
1657    * string values, we can't output strings with embedded newlines
1658    * as what look like multiple strings, as we do for classic and
1659    * 64-bit offset  and cdf5 files.  So we need to know the output file type
1660    * to know how to print strings with embedded newlines. */
1661   NC_CHECKnc_inq_format(ncid, &kind) );
1662
1663   /* For each var, get and print out info. */
1664
1665   memset((void*)&var,0,sizeof(var));
1666
1667   for (varid = 0; varid < nvarsvarid++) {
1668      NC_CHECKnc_inq_varndims(ncidvarid, &var.ndims) );
1669      if(var.dims != NULL) free(var.dims);
1670      var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
1671      NC_CHECKnc_inq_var(ncidvaridvar.name, &var.type, 0,
1672    var.dims, &var.natts) );
1673      /* TODO: don't bother if type name not needed here */
1674      get_type_name(ncidvar.typetype_name);
1675      var.tinfo = get_typeinfo(var.type);
1676      indent_out();
1677/*       printf ("\t%s %s", type_name, var.name); */
1678      printf ("\t");
1679      /* TODO: if duplicate type name and not just inherited, print
1680       * full type name. */
1681      print_type_name (ncidvar.type);
1682      printf (" ");
1683      print_name (var.name);
1684      if (var.ndims > 0)
1685  printf ("(");
1686      for (id = 0; id < var.ndimsid++) {
1687  /* This dim may be in a parent group, so let's look up the
1688   * name. */
1689  NC_CHECKnc_inq_dimname(ncidvar.dims[id], dim_name) );
1690#ifdef USE_NETCDF4
1691  /* Subtlety: The following code block is needed because
1692   * nc_inq_dimname() currently returns only a simple dimension
1693   * name, without a prefix identifying the group it came from.
1694   * That's OK unless the dimid identifies a dimension in an
1695   * ancestor group that has the same simple name as a
1696   * dimension in the current group (or some intermediate
1697   * group), in which case the simple name is ambiguous.  This
1698   * code tests for that case and provides an absolute dimname
1699   * only in the case where a simple name would be
1700   * ambiguous. */
1701  {
1702      int dimid_test; /* to see if dimname is ambiguous */
1703      int locid; /* group id where dimension is defined */
1704      NC_CHECKnc_inq_dimid(nciddim_name, &dimid_test) );
1705      locid = ncid;
1706      while(var.dims[id] != dimid_test) { /* not in locid, try ancestors */
1707  int parent_id;
1708  NC_CHECKnc_inq_grp_parent(locid, &parent_id) );
1709  locid = parent_id;
1710  NC_CHECKnc_inq_dimid(lociddim_name, &dimid_test) );
1711      }
1712      /* dimid is in group locid, prefix dimname with group name if needed */
1713      if(locid != ncid) {
1714  size_t len;
1715  char *locname; /* the group name */
1716  NC_CHECKnc_inq_grpname_full(locid, &lenNULL) );
1717  locname = emalloc(len + 1);
1718  NC_CHECKnc_inq_grpname_full(locid, &lenlocname) );
1719  print_name (locname);
1720  if(strcmp("/", locname) != 0) { /* not the root group */
1721      printf("/");  /* ensure a trailing slash */
1722  }
1723  free(locname);
1724      }
1725  }
1726#endif /* USE_NETCDF4 */
1727  print_name (dim_name);
1728  printf ("%s", id < var.ndims-1 ? ", " : ")");
1729      }
1730      printf (" ;\n");
1731
1732      /* print variable attributes */
1733      for (ia = 0; ia < var.nattsia++) { /* print ia-th attribute */
1734   pr_att(ncidkindvaridvar.nameia);
1735      }
1736#ifdef USE_NETCDF4
1737      /* Print special (virtual) attributes, if option specified */
1738      if (formatting_specs.special_atts) {
1739   pr_att_specials(ncidkindvarid, &var);
1740      }
1741#endif /* USE_NETCDF4 */
1742   }
1743
1744   if (ngatts > 0 || formatting_specs.special_atts) {
1745      printf ("\n");
1746      indent_out();
1747      if (is_root)
1748   printf("// global attributes:\n");
1749      else
1750   printf("// group attributes:\n");
1751   }
1752   for (ia = 0; ia < ngattsia++) { /* print ia-th global attribute */
1753       pr_att(ncidkindNC_GLOBAL, "", ia);
1754   }
1755   if (is_root && formatting_specs.special_atts) { /* output special attribute
1756    * for format variant */
1757#ifdef USE_NETCDF4
1758       pr_att_hidden(ncidkind);
1759#endif
1760       pr_att_global_format(ncidkind);
1761   }
1762
1763   fflush(stdout);
1764
1765   /* output variable data, unless "-h" option specified header only
1766    * or this group is not in list of groups specified by "-g"
1767    * option  */
1768   if (! formatting_specs.header_only &&
1769       group_wanted(ncidformatting_specs.nlgrpsformatting_specs.grpids) ) {
1770      if (nvars > 0) {
1771   indent_out();
1772   printf ("data:\n");
1773      }
1774      for (varid = 0; varid < nvarsvarid++) {
1775  int no_data;
1776  /* if var list specified, test for membership */
1777  if (formatting_specs.nlvars > 0 && ! idmember(vlistvarid))
1778     continue;
1779  NC_CHECKnc_inq_varndims(ncidvarid, &var.ndims) );
1780  if(var.dims != NULL) free(var.dims);
1781  var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
1782  NC_CHECKnc_inq_var(ncidvaridvar.name, &var.type, 0,
1783       var.dims, &var.natts) );
1784  var.tinfo = get_typeinfo(var.type);
1785  /* If coords-only option specified, don't get data for
1786   * non-coordinate vars */
1787  if (formatting_specs.coord_vals && !iscoordvar(ncid,varid)) {
1788     continue;
1789  }
1790  /* Collect variable's dim sizes */
1791  if (vdims) {
1792      free(vdims);
1793      vdims = 0;
1794  }
1795  vdims = (size_t *) emalloc((var.ndims + 1) * SIZEOF_SIZE_T);
1796  no_data = 0;
1797  for (id = 0; id < var.ndimsid++) {
1798      size_t len;
1799      NC_CHECKnc_inq_dimlen(ncidvar.dims[id], &len) );
1800      if(len == 0) {
1801  no_data = 1;
1802      }
1803      vdims[id] = len;
1804  }
1805  /* Don't get data for record variables if no records have
1806   * been written yet */
1807  if (no_data) {
1808      free(vdims);
1809      vdims = 0;
1810      continue;
1811  }
1812  if(var.fillvalp != NULL) free(var.fillvalp);
1813  get_fill_info(ncidvarid, &var); /* sets has_fillval, fillvalp mmbrs */
1814  if(var.timeinfo != NULL) {
1815      if(var.timeinfo->units) free(var.timeinfo->units);
1816      free(var.timeinfo);
1817  }
1818  get_timeinfo(ncidvarid, &var); /* sets has_timeval, timeinfo mmbrs */
1819  /* printf format used to print each value */
1820  var.fmt = get_fmt(ncidvaridvar.type);
1821  var.locid = ncid;
1822  set_tostring_func(&var);
1823  if (vardata(&varvdimsncidvarid) == -1) {
1824     error("can't output data for variable %s", var.name);
1825     goto done;
1826  }
1827      }
1828      if (vdims) {
1829   free(vdims);
1830   vdims = 0;
1831      }
1832   }
1833
1834#ifdef USE_NETCDF4
1835   /* For netCDF-4 compiles, check to see if the file has any
1836    * groups. If it does, this function is called recursively on each
1837    * of them. */
1838   {
1839      int gnumgrps, *ncids;
1840      char group_name[NC_MAX_NAME + 1];
1841
1842      /* See how many groups there are. */
1843      NC_CHECKnc_inq_grps(ncid, &numgrpsNULL) );
1844
1845      /* Allocate memory to hold the list of group ids. */
1846      ncids = emalloc((numgrps + 1) * sizeof(int));
1847
1848      /* Get the list of group ids. */
1849      NC_CHECKnc_inq_grps(ncidNULLncids) );
1850
1851      /* Call this function for each group. */
1852      for (g = 0; g < numgrpsg++)
1853      {
1854   NC_CHECKnc_inq_grpname(ncids[g], group_name) );
1855   printf ("\n");
1856   indent_out();
1857/*      printf ("group: %s {\n", group_name); */
1858   printf ("group: ");
1859   print_name (group_name);
1860   printf (" {\n");
1861   indent_more();
1862   do_ncdump_rec(ncids[g], NULL);
1863   indent_out();
1864/*      printf ("} // group %s\n", group_name); */
1865   printf ("} // group ");
1866   print_name (group_name);
1867   printf ("\n");
1868   indent_less();
1869      }
1870
1871      free(ncids);
1872   }
1873#endif /* USE_NETCDF4 */
1874
1875done:
1876   if(var.dims != NULL) free(var.dims);
1877   if(var.fillvalp != NULL) free(var.fillvalp);
1878   if(var.timeinfo != NULL) {
1879      if(var.timeinfo->units) free(var.timeinfo->units);
1880      free(var.timeinfo);
1881   }
1882   if (dims)
1883      free(dims);
1884   if (vlist)
1885      freeidlist(vlist);
1886}
1887
1888
1889static void
1890do_ncdump(int ncid, const char *path)
1891{
1892   char* esc_specname;
1893   /* output initial line */
1894   indent_init();
1895   indent_out();
1896   esc_specname=escaped_name(formatting_specs.name);
1897   printf ("netcdf %s {\n", esc_specname);
1898   free(esc_specname);
1899   do_ncdump_rec(ncidpath);
1900   indent_out();
1901   printf ("}\n");
1902}
1903
1904
1905static void
1906do_ncdumpx(int ncid, const char *path)
1907{
1908    int ndims; /* number of dimensions */
1909    int nvars; /* number of variables */
1910    int ngatts; /* number of global attributes */
1911    int xdimid; /* id of unlimited dimension */
1912    int dimid; /* dimension id */
1913    int varid; /* variable id */
1914    ncdim_t *dims; /* dimensions */
1915    ncvar_t var; /* variable */
1916    int ia; /* attribute number */
1917    int iv; /* variable number */
1918    idnode_tvlist = NULL;     /* list for vars specified with -v option */
1919
1920    /*
1921     * If any vars were specified with -v option, get list of associated
1922     * variable ids
1923     */
1924    if (formatting_specs.nlvars > 0) {
1925 vlist = newidlist(); /* list for vars specified with -v option */
1926 for (iv=0; iv < formatting_specs.nlvarsiv++) {
1927     NC_CHECKnc_inq_varid(ncidformatting_specs.lvars[iv], &varid) );
1928     idadd(vlistvarid);
1929 }
1930    }
1931
1932    /* output initial line */
1933    pr_initx(ncidpath);
1934
1935    /*
1936     * get number of dimensions, number of variables, number of global
1937     * atts, and dimension id of unlimited dimension, if any
1938     */
1939    /* TODO: print names with XML-ish escapes fopr special chars */
1940    NC_CHECKnc_inq(ncid, &ndims, &nvars, &ngatts, &xdimid) );
1941    /* get dimension info */
1942    dims = (ncdim_t *) emalloc((ndims + 1) * sizeof(ncdim_t));
1943    for (dimid = 0; dimid < ndimsdimid++) {
1944 NC_CHECKnc_inq_dim(nciddimiddims[dimid].name, &dims[dimid].size) );
1945 if (dimid == xdimid)
1946     printf("  <dimension name=\"%s\" length=\"%d\" isUnlimited=\"true\" />\n",
1947  dims[dimid].name, (int)dims[dimid].size);
1948 else
1949   printf ("  <dimension name=\"%s\" length=\"%d\" />\n",
1950   dims[dimid].name, (int)dims[dimid].size);
1951    }
1952
1953    /* get global attributes */
1954    for (ia = 0; ia < ngattsia++)
1955 pr_attx(ncidNC_GLOBALia); /* print ia-th global attribute */
1956
1957    /* get variable info, with variable attributes */
1958    memset((void*)&var,0,sizeof(var));
1959    for (varid = 0; varid < nvarsvarid++) {
1960 NC_CHECKnc_inq_varndims(ncidvarid, &var.ndims) );
1961 if(var.dims != NULL) free(var.dims);
1962 var.dims = (int *) emalloc((var.ndims + 1) * sizeof(int));
1963 NC_CHECKnc_inq_var(ncidvaridvar.name, &var.type, 0,
1964      var.dims, &var.natts) );
1965 printf ("  <variable name=\"%s\"", var.name);
1966 pr_shape(&vardims);
1967
1968 /* handle one-line variable elements that aren't containers
1969    for attributes or data values, since they need to be
1970    rendered as <variable ... /> instead of <variable ..>
1971    ... </variable> */
1972 if (var.natts == 0) {
1973     if (
1974 /* header-only specified */
1975 (formatting_specs.header_only) ||
1976 /* list of variables specified and this variable not in list */
1977 (formatting_specs.nlvars > 0 && !idmember(vlistvarid)) ||
1978 /* coordinate vars only and this is not a coordinate variable */
1979 (formatting_specs.coord_vals && !iscoordvar(ncidvarid)) ||
1980 /* this is a record variable, but no records have been written */
1981 (isrecvar(ncid,varid) && dims[xdimid].size == 0)
1982 ) {
1983 printf (" type=\"%s\" />\n", prim_type_name(var.type));
1984 continue;
1985     }
1986 }
1987
1988 /* else nest attributes values, data values in <variable> ... </variable> */
1989 printf (" type=\"%s\">\n", prim_type_name(var.type));
1990
1991 /* get variable attributes */
1992 for (ia = 0; ia < var.nattsia++) {
1993     pr_attx(ncidvaridia); /* print ia-th attribute */
1994 }
1995 printf ("  </variable>\n");
1996    }
1997
1998    printf ("</netcdf>\n");
1999    if (vlist)
2000 freeidlist(vlist);
2001    if(dims)
2002 free(dims);
2003}
2004
2005/*
2006 * Extract the significant-digits specifiers from the (deprecated and
2007 * undocumented) -d argument on the command-line and update the
2008 * default data formats appropriately.  This only exists because an
2009 * old version of ncdump supported the "-d" flag which did not
2010 * override the C_format attributes (if any).
2011 */
2012static void
2013set_sigdigs(const char *optarg)
2014{
2015    char *ptr1 = 0;
2016    char *ptr2 = 0;
2017    int flt_digits = FLT_DIGITS; /* default floating-point digits */
2018    int dbl_digits = DBL_DIGITS; /* default double-precision digits */
2019
2020    if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',')
2021        flt_digits = (int)strtol(optarg, &ptr1, 10);
2022
2023    if (flt_digits < 1 || flt_digits > 20) {
2024 error("unreasonable value for float significant digits: %d",
2025       flt_digits);
2026    }
2027    if (ptr1 && *ptr1 == ',') {
2028      dbl_digits = (int)strtol(ptr1+1, &ptr2, 10);
2029      if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) {
2030   error("unreasonable value for double significant digits: %d",
2031 dbl_digits);
2032      }
2033    }
2034    set_formats(flt_digitsdbl_digits);
2035}
2036
2037
2038/*
2039 * Extract the significant-digits specifiers from the -p argument on the
2040 * command-line, set flags so we can override C_format attributes (if any),
2041 * and update the default data formats appropriately.
2042 */
2043static void
2044set_precision(const char *optarg)
2045{
2046    char *ptr1 = 0;
2047    char *ptr2 = 0;
2048    int flt_digits = FLT_DIGITS; /* default floating-point digits */
2049    int dbl_digits = DBL_DIGITS; /* default double-precision digits */
2050
2051    if (optarg != 0 && (int) strlen(optarg) > 0 && optarg[0] != ',') {
2052        flt_digits = (int)strtol(optarg, &ptr1, 10);
2053 float_precision_specified = 1;
2054    }
2055
2056    if (flt_digits < 1 || flt_digits > 20) {
2057 error("unreasonable value for float significant digits: %d",
2058       flt_digits);
2059    }
2060    if (ptr1 && *ptr1 == ',') {
2061 dbl_digits = (int) strtol(ptr1+1, &ptr2, 10);
2062 double_precision_specified = 1;
2063 if (ptr2 == ptr1+1 || dbl_digits < 1 || dbl_digits > 20) {
2064     error("unreasonable value for double significant digits: %d",
2065   dbl_digits);
2066 }
2067    }
2068    set_formats(flt_digitsdbl_digits);
2069}
2070
2071
2072#ifdef USE_DAP
2073#define DAP_CLIENT_CACHE_DIRECTIVE "[cache]"
2074/* replace path string with same string prefixed by
2075 * DAP_CLIENT_NCDUMP_DIRECTIVE */
2076static
2077void adapt_url_for_cache(char **pathp) {
2078    char prefix[] = DAP_CLIENT_CACHE_DIRECTIVE;
2079    char* path = *pathp;
2080    char *tmp_path = strdup(path);
2081    path = (char *)emalloc(strlen(prefix) + strlen(tmp_path) + 1);
2082    path[0] = '\0';
2083    strncat(pathprefix, strlen(prefix));
2084    strncat(pathtmp_path, strlen(tmp_path));
2085    free(tmp_path);
2086    *pathp = path;
2087    return;
2088}
2089#endif
2090
2091int
2092main(int argc, char *argv[])
2093{
2094    int c;
2095    int i;
2096    int max_len = 80; /* default maximum line length */
2097    int nameopt = 0;
2098    bool_t xml_out = false;    /* if true, output NcML instead of CDL */
2099    bool_t kind_out = false; /* if true, just output kind of netCDF file */
2100    bool_t kind_out_extended = false; /* output inq_format vs inq_format_extended */
2101    int Xp_flag = 0;    /* indicate that -Xp flag was set */
2102
2103#if defined(WIN32) || defined(msdos) || defined(WIN64)
2104    putenv("PRINTF_EXPONENT_DIGITS=2"); /* Enforce unix/linux style exponent formatting. */
2105#endif
2106
2107#ifdef HAVE_LOCALE_H
2108    setlocale(LC_ALL, "C");     /* CDL may be ambiguous with other locales */
2109#endif /* HAVE_LOCALE_H */
2110    opterr = 1;
2111    progname = argv[0];
2112    set_formats(FLT_DIGITSDBL_DIGITS); /* default for float, double data */
2113
2114    /* If the user called ncdump without arguments, print the usage
2115     * message and return peacefully. */
2116    if (argc <= 1)
2117    {
2118       usage();
2119       exit(EXIT_SUCCESS);
2120    }
2121
2122    while ((c = getopt(argcargv, "b:cd:f:g:hikl:n:p:stv:xwKL:X:")) != EOF)
2123      switch(c) {
2124 case 'h': /* dump header only, no data */
2125   formatting_specs.header_only = true;
2126   break;
2127 case 'c': /* header, data only for coordinate dims */
2128   formatting_specs.coord_vals = true;
2129   break;
2130 case 'n': /*
2131  * provide different name than derived from
2132  * file name
2133  */
2134   formatting_specs.name = optarg;
2135   nameopt = 1;
2136   break;
2137 case 'b': /* brief comments in data section */
2138   formatting_specs.brief_data_cmnts = true;
2139   switch (tolower((int)optarg[0])) {
2140     case 'c':
2141       formatting_specs.data_lang = LANG_C;
2142       break;
2143     case 'f':
2144       formatting_specs.data_lang = LANG_F;
2145       break;
2146     default:
2147       error("invalid value for -b option: %s", optarg);
2148   }
2149   break;
2150 case 'f': /* full comments in data section */
2151   formatting_specs.full_data_cmnts = true;
2152   switch (tolower((int)optarg[0])) {
2153     case 'c':
2154       formatting_specs.data_lang = LANG_C;
2155       break;
2156     case 'f':
2157       formatting_specs.data_lang = LANG_F;
2158       break;
2159     default:
2160       error("invalid value for -f option: %s", optarg);
2161   }
2162   break;
2163 case 'l': /* maximum line length */
2164   max_len = (int) strtol(optarg, 0, 0);
2165   if (max_len < 10) {
2166       error("unreasonably small line length specified: %d", max_len);
2167   }
2168   break;
2169 case 'v': /* variable names */
2170   /* make list of names of variables specified */
2171   make_lvars (optarg, &formatting_specs.nlvars, &formatting_specs.lvars);
2172   break;
2173 case 'g': /* group names */
2174   /* make list of names of groups specified */
2175   make_lgrps (optarg, &formatting_specs.nlgrps, &formatting_specs.lgrps,
2176 &formatting_specs.grpids);
2177   break;
2178 case 'd': /* specify precision for floats (deprecated, undocumented) */
2179   set_sigdigs(optarg);
2180   break;
2181 case 'p': /* specify precision for floats, overrides attribute specs */
2182   set_precision(optarg);
2183   break;
2184        case 'x': /* XML output (NcML) */
2185   xml_out = true;
2186   break;
2187        case 'k':         /* just output what kind of netCDF file */
2188   kind_out = true;
2189   break;
2190        case 'K':         /* extended format info */
2191   kind_out_extended = true;
2192   break;
2193 case 't': /* human-readable strings for date-time values */
2194   formatting_specs.string_times = true;
2195   formatting_specs.iso_separator = false;
2196   break;
2197 case 'i': /* human-readable strings for data-time values with 'T' separator */
2198   formatting_specs.string_times = true;
2199   formatting_specs.iso_separator = true;
2200   break;
2201        case 's':     /* output special (virtual) attributes for
2202      * netCDF-4 files and variables, including
2203      * _DeflateLevel, _Chunking, _Endianness,
2204      * _Format, _Checksum, _NoFill */
2205   formatting_specs.special_atts = true;
2206   break;
2207        case 'w': /* with client-side cache for DAP URLs */
2208   formatting_specs.with_cache = true;
2209   break;
2210        case 'X': /* special options */
2211   switch (tolower((int)optarg[0])) {
2212     case 'm':
2213       formatting_specs.xopt_inmemory = 1;
2214       break;
2215     case 'p': /* suppress the properties attribute */
2216       Xp_flag = 1; /* record that this flag was set */
2217       break;
2218     default:
2219       error("invalid value for -X option: %s", optarg);
2220       break;
2221   }
2222   break;
2223        case 'L':
2224#ifdef LOGGING
2225   {
2226   int level = atoi(optarg);
2227   if(level >= 0)
2228     nc_set_log_level(level);
2229   }
2230#endif
2231   break;
2232        case '?':
2233   usage();
2234   exit(EXIT_FAILURE);
2235      }
2236
2237    /* Decide xopt_props */
2238    if(formatting_specs.special_atts && Xp_flag == 1)
2239        formatting_specs.xopt_props = 0;
2240    else if(formatting_specs.special_atts && Xp_flag == 0)
2241        formatting_specs.xopt_props = 1;
2242    else if(!formatting_specs.special_atts)
2243 formatting_specs.xopt_props = 0;
2244    else
2245 formatting_specs.xopt_props = 0;
2246
2247    set_max_len(max_len);
2248
2249    argc -= optind;
2250    argv += optind;
2251
2252    /* If no file arguments left or more than one, print usage message. */
2253    if (argc != 1)
2254    {
2255       usage();
2256       exit(EXIT_FAILURE);
2257    }
2258
2259    i = 0;
2260
2261    init_epsilons();
2262
2263    {
2264 char *path = strdup(argv[i]);
2265 if(!path)
2266     error("out of memory copying argument %s", argv[i]);
2267        if (!nameopt)
2268     formatting_specs.name = name_path(path);
2269 if (argc > 0) {
2270     int ncidnc_status;
2271     /* If path is a URL, prefix with client-side directive to
2272      * make ncdump reasonably efficient */
2273#ifdef USE_DAP
2274     if(formatting_specs.with_cache) /* by default, don't use cache directive */
2275     {
2276 extern int nc__testurl(const char*,char**);
2277 /* See if this is a url */
2278 if(nc__testurl(pathNULL)) {
2279     adapt_url_for_cache(&path);
2280 }
2281 /* else fall thru and treat like a file path */
2282     }
2283#endif /*USE_DAP*/
2284#ifdef USE_DISKLESS
2285     if(formatting_specs.xopt_inmemory) {
2286 size_t size = 0;
2287 void* mem = NULL;
2288 nc_status = fileopen(path,&mem,&size);
2289 if(nc_status == NC_NOERR)
2290             nc_status = nc_open_mem(path,NC_DISKLESS|NC_INMEMORY,size,mem,&ncid);
2291     } else
2292#endif
2293         nc_status = nc_open(pathNC_NOWRITE, &ncid);
2294     if (nc_status != NC_NOERR) {
2295 error("%s: %s", pathnc_strerror(nc_status));
2296     }
2297     NC_CHECKnc_inq_format(ncid, &formatting_specs.nc_kind) );
2298     NC_CHECKnc_inq_format_extended(ncid,
2299                                             &formatting_specs.nc_extended,
2300                                             &formatting_specs.nc_mode) );
2301     if (kind_out) {
2302 printf ("%s\n", kind_string(formatting_specs.nc_kind));
2303     } else if (kind_out_extended) {
2304 printf ("%s\n", kind_string_extended(formatting_specs.nc_extended,formatting_specs.nc_mode));
2305     } else {
2306 /* Initialize list of types. */
2307 init_types(ncid);
2308 /* Check if any vars in -v don't exist */
2309 if(missing_vars(ncidformatting_specs.nlvarsformatting_specs.lvars))
2310     exit(EXIT_FAILURE);
2311 if(formatting_specs.nlgrps > 0) {
2312     if(formatting_specs.nc_kind != NC_FORMAT_NETCDF4) {
2313 error("Group list (-g ...) only permitted for netCDF-4 file");
2314 exit(EXIT_FAILURE);
2315     }
2316     /* Check if any grps in -g don't exist */
2317     if(grp_matches(ncidformatting_specs.nlgrpsformatting_specs.lgrpsformatting_specs.grpids) == 0)
2318 exit(EXIT_FAILURE);
2319 }
2320 if (xml_out) {
2321     if(formatting_specs.nc_kind == NC_FORMAT_NETCDF4) {
2322 error("NcML output (-x) currently only permitted for netCDF classic model");
2323 exit(EXIT_FAILURE);
2324     }
2325     do_ncdumpx(ncidpath);
2326 } else {
2327     do_ncdump(ncidpath);
2328 }
2329     }
2330     NC_CHECKnc_close(ncid) );
2331 }
2332 free(path);
2333    }
2334    exit(EXIT_SUCCESS);
2335}
2336
2337
2338END_OF_MAIN()


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed