1 /*
2  *  Copyright (C) 2015, Northwestern University and Argonne National Laboratory
3  *  See COPYRIGHT notice in top-level directory.
4  */
5 /* $Id: ncoffsets.c 2709 2016-12-16 17:15:57Z wkliao $ */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>    /* strcpy() */
10 #include <errno.h>     /* errno, strerror() */
11 #include <sys/types.h> /* open() */
12 #include <sys/stat.h>  /* open() */
13 #include <fcntl.h>     /* open() */
14 #include <unistd.h>    /* read() */
15 #include <assert.h>    /* assert() */
16 #include <inttypes.h>  /* check for Endianness, uint32_t*/
17 
18 static int is_little_endian;
19 
20 #ifndef MIN
21 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
22 #endif
23 
24 static int verbose_debug;
25 
26 #define DEBUG_RETURN_ERROR(err) {                             \
27     if (verbose_debug)                                        \
28         fprintf(stderr, "Error code %s at line %d of %s\n",   \
29         ncmpii_err_code_name(err),__LINE__,__FILE__);         \
30     return err;                                               \
31 }
32 #define DEBUG_ASSIGN_ERROR(status, err) {                     \
33     if (verbose_debug)                                        \
34         fprintf(stderr, "Error code %s at line %d of %s\n",   \
35         ncmpii_err_code_name(err),__LINE__,__FILE__);         \
36     status = err;                                             \
37 }
38 
39 #define IS_RECVAR(vp) \
40         ((vp)->shape != NULL ? (*(vp)->shape == NC_UNLIMITED) : 0 )
41 
42 #define MALLOC_CHECK(ptr) { \
43     if ((ptr) == NULL) { \
44         fprintf(stderr, "Error at line %d: malloc out of memory",__LINE__); \
45         exit(1); \
46     } \
47 }
48 
49 #define NC_NOERR 0
50 #define	NC_EINVAL	(-36)	/**< Invalid Argument */
51 #define NC_EBADDIM	(-46)	/**< Invalid dimension id or name */
52 #define NC_EUNLIMPOS	(-47)	/**< NC_UNLIMITED in the wrong index */
53 #define NC_ENOTNC	(-51)	/**< Not a netcdf file */
54 #define NC_EVARSIZE     (-62)   /**< One or more variable sizes violate format constraints */
55 
56 #define NC_UNLIMITED 0L
57 
58 typedef enum {
59     NC_UNSPECIFIED =  0,
60     NC_DIMENSION   = 10,
61     NC_VARIABLE    = 11,
62     NC_ATTRIBUTE   = 12
63 } NCtype;
64 
65 #define NC_NAT          0       /**< Not A Type */
66 #define NC_BYTE         1       /**< signed 1 byte integer */
67 #define NC_CHAR         2       /**< ISO/ASCII character */
68 #define NC_SHORT        3       /**< signed 2 byte integer */
69 #define NC_INT          4       /**< signed 4 byte integer */
70 #define NC_LONG         NC_INT
71 #define NC_FLOAT        5       /**< single precision floating point number */
72 #define NC_DOUBLE       6       /**< double precision floating point number */
73 #define NC_UBYTE        7       /**< unsigned 1 byte int */
74 #define NC_USHORT       8       /**< unsigned 2-byte int */
75 #define NC_UINT         9       /**< unsigned 4-byte int */
76 #define NC_INT64        10      /**< signed 8-byte int */
77 #define NC_UINT64       11      /**< unsigned 8-byte int */
78 
79 #define NC_DEFAULT_CHUNKSIZE 1048576
80 
81 /* sizes of external data types */
82 #define X_SIZEOF_CHAR           1
83 #define X_SIZEOF_SHORT          2
84 #define X_SIZEOF_INT            4
85 #define X_SIZEOF_FLOAT          4
86 #define X_SIZEOF_DOUBLE         8
87 #define X_SIZEOF_UBYTE          1
88 #define X_SIZEOF_USHORT         2
89 #define X_SIZEOF_UINT           4
90 #define X_SIZEOF_LONGLONG       8
91 #define X_SIZEOF_ULONGLONG      8
92 #define X_SIZEOF_INT64          8
93 #define X_SIZEOF_UINT64         8
94 
95 #define X_UINT_MAX	4294967295U
96 
97 #define X_ALIGN                 4
98 
99 /* useful for aligning memory */
100 #define _RNDUP(x, unit)  ((((x) + (unit) - 1) / (unit)) * (unit))
101 #define M_RND_UNIT       X_SIZEOF_DOUBLE
102 #define M_RNDUP(x)       _RNDUP(x, M_RND_UNIT)
103 
104 #define ncmpix_len_char(nelems)   _RNDUP((nelems), X_ALIGN)
105 #define ncmpix_len_short(nelems)  (((nelems) + (nelems)%2)  * X_SIZEOF_SHORT)
106 #define ncmpix_len_int(nelems)    ((nelems) * X_SIZEOF_INT)
107 #define ncmpix_len_long(nelems)   ((nelems) * X_SIZEOF_LONG)
108 #define ncmpix_len_float(nelems)  ((nelems) * X_SIZEOF_FLOAT)
109 #define ncmpix_len_double(nelems) ((nelems) * X_SIZEOF_DOUBLE)
110 #define ncmpix_len_ubyte(nelems)  _RNDUP((nelems), X_ALIGN)
111 #define ncmpix_len_ushort(nelems) (((nelems) + (nelems)%2)  * X_SIZEOF_USHORT)
112 #define ncmpix_len_uint(nelems)   ((nelems) * X_SIZEOF_UINT)
113 #define ncmpix_len_int64(nelems)  ((nelems) * X_SIZEOF_INT64)
114 #define ncmpix_len_uint64(nelems) ((nelems) * X_SIZEOF_UINT64)
115 
116 typedef int nc_type;
117 
118 typedef struct {
119     long long  nchars;
120     char      *cp;     /* [nchars+1] one additional char for '\0' */
121 } NC_string;
122 
123 typedef struct {
124     NC_string *name;
125     long long  size;
126 } NC_dim;
127 
128 typedef struct NC_dimarray {
129     int      nalloc;    /* number allocated >= ndefined */
130     int      ndefined;  /* number of defined dimensions */
131     NC_dim **value;
132 } NC_dimarray;
133 
134 typedef struct {
135     long long  xsz;      /* amount of space at xvalue (4-byte aligned) */
136     NC_string *name;     /* name of the attributes */
137     nc_type    type;     /* the discriminant */
138     long long  nelems;   /* number of attribute elements */
139     void      *xvalue;   /* the actual data, in external representation */
140 } NC_attr;
141 
142 typedef struct NC_attrarray {
143     int       nalloc;    /* number allocated >= ndefined */
144     int       ndefined;  /* number of defined attributes */
145     NC_attr **value;
146 } NC_attrarray;
147 
148 typedef struct {
149     int           xsz;    /* byte size of 1 array element */
150     long long    *shape;  /* dim->size of each dim */
151     long long    *dsizes; /* the right to left product of shape */
152     NC_string    *name;   /* name of the variable */
153     int           ndims;  /* number of dimensions */
154     int          *dimids; /* array of dimension IDs */
155     NC_attrarray  attrs;  /* attribute array */
156     nc_type       type;   /* variable's data type */
157     long long     len;    /* this is the "vsize" defined in header format, the
158                              total size in bytes of the array variable.
159                              For record variable, this is the record size */
160     long long     begin;  /* starting file offset of this variable */
161 } NC_var;
162 
163 typedef struct NC_vararray {
164     int      nalloc;      /* number allocated >= ndefined */
165     int      ndefined;    /* number of defined variables */
166     int      num_rec_vars;/* number of defined record variables */
167     NC_var **value;
168 } NC_vararray;
169 
170 typedef struct NC {
171     int           flags;
172     char         *path;
173     long long     xsz;      /* external size of this header, <= var[0].begin */
174     long long     begin_var;/* file offset of the first (non-record) var */
175     long long     begin_rec;/* file offset of the first 'record' */
176 
177     long long     recsize;  /* length of 'record': sum of single record sizes
178                                of all the record variables */
179     long long     numrecs;  /* number of 'records' allocated */
180     NC_dimarray   dims;     /* dimensions defined */
181     NC_attrarray  attrs;    /* global attributes defined */
182     NC_vararray   vars;     /* variables defined */
183 } NC;
184 
185 typedef struct bufferinfo {
186     int        fd;
187     off_t      offset;   /* current read/write offset in the file */
188     int        version;  /* 1, 2, and 5 for CDF-1, 2, and 5 respectively */
189     void      *base;     /* beginning of read/write buffer */
190     void      *pos;      /* current position in buffer */
191     long long  size;     /* size of the buffer */
192 } bufferinfo;
193 
194 /*
195  * "magic number" at beginning of file: 0x43444601 (big endian)
196  */
197 static const char ncmagic1[] = {'C', 'D', 'F', 0x01};
198 static const char ncmagic2[] = {'C', 'D', 'F', 0x02};
199 static const char ncmagic5[] = {'C', 'D', 'F', 0x05};
200 
201 const char * ncmpii_err_code_name(int err);
202 
check_little_endian(void)203 static int check_little_endian(void) {
204     volatile uint32_t i=0x01234567;
205     // return 0 for big endian, 1 for little endian.
206     return (*((uint8_t*)(&i))) == 0x67;
207 }
208 
209 #define SWAP4B(a) ( ((a) << 24) | \
210                    (((a) <<  8) & 0x00ff0000) | \
211                    (((a) >>  8) & 0x0000ff00) | \
212                    (((a) >> 24) & 0x000000ff) )
213 
214 #define SWAP8B(a) ( (((a) & 0x00000000000000FFULL) << 56) | \
215                     (((a) & 0x000000000000FF00ULL) << 40) | \
216                     (((a) & 0x0000000000FF0000ULL) << 24) | \
217                     (((a) & 0x00000000FF000000ULL) <<  8) | \
218                     (((a) & 0x000000FF00000000ULL) >>  8) | \
219                     (((a) & 0x0000FF0000000000ULL) >> 24) | \
220                     (((a) & 0x00FF000000000000ULL) >> 40) | \
221                     (((a) & 0xFF00000000000000ULL) >> 56) )
222 
223 static void
swap4b(void * val)224 swap4b(void *val)
225 {
226     uint32_t *op = (uint32_t*)val;
227     *op = SWAP4B(*op);
228 }
229 
230 static void
swap8b(long long * val)231 swap8b(long long *val)
232 {
233     uint64_t *op = (uint64_t*)val;
234     *op = SWAP8B(*op);
235 }
236 
237 static unsigned long long
get_uint64(bufferinfo * gbp)238 get_uint64(bufferinfo *gbp) {
239     /* retrieve a 64bit unisgned integer and return it as unsigned long long */
240     unsigned long long tmp;
241     memcpy(&tmp, gbp->pos, 8);
242     if (is_little_endian) swap8b(&tmp);
243     gbp->pos += 8;
244     return tmp;
245 }
246 
247 static unsigned int
get_uint32(bufferinfo * gbp)248 get_uint32(bufferinfo *gbp) {
249     /* retrieve a 32bit unisgned integer and return it as unsigned int */
250     unsigned int tmp;
251     memcpy(&tmp, gbp->pos, 4);
252     if (is_little_endian) swap4b(&tmp);
253     gbp->pos += 4;
254     return tmp;
255 }
256 
257 static int
type_size(nc_type type)258 type_size(nc_type type) {
259     switch (type) {
260       case NC_BYTE:   return X_SIZEOF_CHAR;
261       case NC_CHAR:   return X_SIZEOF_CHAR;
262       case NC_SHORT:  return X_SIZEOF_SHORT;
263       case NC_INT:    return X_SIZEOF_INT;
264       case NC_FLOAT:  return X_SIZEOF_FLOAT;
265       case NC_DOUBLE: return X_SIZEOF_DOUBLE;
266       case NC_UBYTE:  return X_SIZEOF_UBYTE;
267       case NC_USHORT: return X_SIZEOF_USHORT;
268       case NC_UINT:   return X_SIZEOF_UINT;
269       case NC_INT64:  return X_SIZEOF_INT64;
270       case NC_UINT64: return X_SIZEOF_UINT64;
271       default: return -1;
272     }
273 }
274 
275 static const char *
type_name(nc_type type)276 type_name(nc_type type) {
277     switch (type) {
278       case NC_BYTE:   return "byte";
279       case NC_CHAR:   return "char";
280       case NC_SHORT:  return "short";
281       case NC_INT:    return "int";
282       case NC_FLOAT:  return "float";
283       case NC_DOUBLE: return "double";
284       case NC_UBYTE:  return "ubyte";
285       case NC_USHORT: return "ushort";
286       case NC_UINT:   return "uint";
287       case NC_INT64:  return "int64";
288       case NC_UINT64: return "uint64";
289       default: return "bogus";
290     }
291 }
292 
293 static NC_string *
ncmpii_new_NC_string(long long slen,const char * str)294 ncmpii_new_NC_string(long long   slen,
295                      const char *str)
296 {
297     /* str may not be NULL terminated */
298     NC_string *ncstrp;
299     size_t sizeof_NC_string = M_RNDUP(sizeof(NC_string));
300     size_t sz = slen + sizeof_NC_string + 1;
301     /* one char more space for NULL terminate char */
302 
303     ncstrp = (NC_string *) calloc(sz, sizeof(char));
304     if (ncstrp == NULL) return NULL;
305 
306     /* make space occupied by ncstrp->cp part of ncstrp */
307     ncstrp->nchars = slen;
308     ncstrp->cp = (char *)ncstrp + sizeof_NC_string;
309 
310     /* in PnetCDF, we want to make name->cp always NULL character terminated */
311     if (str != NULL && *str != '\0') {
312         strncpy(ncstrp->cp, str, slen);
313         ncstrp->cp[slen] = '\0';  /* NULL terminated */
314     }
315 
316     return(ncstrp);
317 }
318 
319 static NC_dim *
ncmpii_elem_NC_dimarray(const NC_dimarray * ncap,int dimid)320 ncmpii_elem_NC_dimarray(const NC_dimarray *ncap,
321                         int                dimid)
322 {
323     /* returns the dimension ID defined earlier */
324     assert(ncap != NULL);
325 
326     if (dimid < 0 || ncap->ndefined == 0 || dimid >= ncap->ndefined)
327         return NULL;
328 
329     assert(ncap->value != NULL);
330 
331     return ncap->value[dimid];
332 }
333 
334 /*----< ncmpix_len_nctype() >------------------------------------------------*/
335 static int
ncmpix_len_nctype(nc_type type)336 ncmpix_len_nctype(nc_type type) {
337     switch(type) {
338         case NC_BYTE:
339         case NC_CHAR:
340         case NC_UBYTE:  return X_SIZEOF_CHAR;
341         case NC_SHORT:
342         case NC_USHORT: return X_SIZEOF_SHORT;
343         case NC_INT:
344         case NC_UINT:   return X_SIZEOF_INT;
345         case NC_FLOAT:  return X_SIZEOF_FLOAT;
346         case NC_DOUBLE: return X_SIZEOF_DOUBLE;
347         case NC_INT64:
348         case NC_UINT64: return X_SIZEOF_INT64;
349         default: assert("ncmpix_len_nctype bad type" == 0);
350     }
351     return 0;
352 }
353 
354 static int
ncmpii_NC_var_shape64(NC * ncp,NC_var * varp,const NC_dimarray * dims)355 ncmpii_NC_var_shape64(NC                *ncp,
356                       NC_var            *varp,
357                       const NC_dimarray *dims)
358 {
359     int i;
360     long long product = 1;
361 
362     /* set the size of 1 element */
363     varp->xsz = ncmpix_len_nctype(varp->type);
364 
365     if (varp->ndims == 0) goto out;
366 
367     /*
368      * use the user supplied dimension indices to determine the shape
369      */
370     for (i=0; i<varp->ndims; i++) {
371         const NC_dim *dimp;
372 
373         if (varp->dimids[i] < 0)
374             DEBUG_RETURN_ERROR(NC_EBADDIM);
375 
376         if (varp->dimids[i] >= ((dims != NULL) ? dims->ndefined : 1))
377             DEBUG_RETURN_ERROR(NC_EBADDIM);
378 
379         /* get the pointer to the dim object */
380         dimp = ncmpii_elem_NC_dimarray(dims, varp->dimids[i]);
381         varp->shape[i] = dimp->size;
382 
383         /* check for record variable, only the highest dimension can
384          * be unlimited */
385         if (varp->shape[i] == NC_UNLIMITED && i != 0)
386             DEBUG_RETURN_ERROR(NC_EUNLIMPOS);
387     }
388 
389     /*
390      * compute the dsizes, the right to left product of shape
391      */
392     product = 1;
393     if (varp->ndims == 1) {
394         if (varp->shape[0] == NC_UNLIMITED)
395             varp->dsizes[0] = 1;
396         else {
397             varp->dsizes[0] = varp->shape[0];
398             product = varp->shape[0];
399         }
400     }
401     else { /* varp->ndims > 1 */
402         varp->dsizes[varp->ndims-1] = varp->shape[varp->ndims-1];
403         product = varp->shape[varp->ndims-1];
404         for (i=varp->ndims-2; i>=0; i--) {
405             if (varp->shape[i] != NC_UNLIMITED)
406                 product *= varp->shape[i];
407             varp->dsizes[i] = product;
408         }
409     }
410 
411 out :
412     /*
413      * For CDF-1 and CDF-2 formats, the total number of array elements
414      * cannot exceed 2^32, unless this variable is the last fixed-size
415      * variable, there is no record variable, and the file starting
416      * offset of this variable is less than 2GiB.
417      * We will check this in ncmpi_enddef() which calls ncmpii_NC_enddef()
418      * which calls ncmpii_NC_check_vlens()
419     if (ncp->flags != 5 && product >= X_UINT_MAX)
420         DEBUG_RETURN_ERROR(NC_EVARSIZE);
421      */
422 
423     /*
424      * align variable size to 4 byte boundary, required by all netcdf file
425      * formats
426      */
427     varp->len = product * varp->xsz;
428     if (varp->len % 4 > 0)
429         varp->len += 4 - varp->len % 4; /* round up */
430 
431     return NC_NOERR;
432 }
433 
434 /*
435  * Recompute the shapes of all variables
436  * Sets ncp->begin_var to start of first variable.
437  * Sets ncp->begin_rec to start of first record variable.
438  * Returns -1 on error. The only possible error is an reference
439  * to a non existent dimension, which would occur for a corrupt
440  * netcdf file.
441  */
442 static int
ncmpii_NC_computeshapes(NC * ncp)443 ncmpii_NC_computeshapes(NC *ncp)
444 {
445     NC_var **vpp = (NC_var **)ncp->vars.value;
446     NC_var *const *const end = &vpp[ncp->vars.ndefined];
447     NC_var *first_var = NULL;       /* first "non-record" var */
448     NC_var *first_rec = NULL;       /* first "record" var */
449     int status;
450 
451     ncp->begin_var = ncp->xsz;
452     ncp->begin_rec = ncp->xsz;
453     ncp->recsize = 0;
454 
455     if (ncp->vars.ndefined == 0) return NC_NOERR;
456 
457     for ( /*NADA*/; vpp < end; vpp++) {
458         /* (*vpp)->len is recomputed from dimensions in ncmpii_NC_var_shape64() */
459         status = ncmpii_NC_var_shape64(ncp, *vpp, &ncp->dims);
460 
461         if (status != NC_NOERR) return status ;
462 
463         if (IS_RECVAR(*vpp)) {
464             if (first_rec == NULL)
465                 first_rec = *vpp;
466             ncp->recsize += (*vpp)->len;
467         }
468         else {
469             if (first_var == NULL)
470             first_var = *vpp;
471             /*
472              * Overwritten each time thru.
473              * Usually overwritten in first_rec != NULL clause.
474              */
475             ncp->begin_rec = (*vpp)->begin + (*vpp)->len;
476         }
477     }
478 
479     if (first_rec != NULL) {
480         if (ncp->begin_rec > first_rec->begin)
481             DEBUG_RETURN_ERROR(NC_ENOTNC); /* not a netCDF file or corrupted */
482 
483         ncp->begin_rec = first_rec->begin;
484         /*
485          * for special case of exactly one record variable, pack value
486          */
487         if (ncp->recsize == first_rec->len)
488             ncp->recsize = *first_rec->dsizes * first_rec->xsz;
489     }
490 
491     if (first_var != NULL)
492         ncp->begin_var = first_var->begin;
493     else
494         ncp->begin_var = ncp->begin_rec;
495 
496     if (ncp->begin_var <= 0 ||
497         ncp->xsz > ncp->begin_var ||
498         ncp->begin_rec <= 0 ||
499         ncp->begin_var > ncp->begin_rec)
500         DEBUG_RETURN_ERROR(NC_ENOTNC); /* not a netCDF file or corrupted */
501 
502     return NC_NOERR;
503 }
504 
505 /*
506  * To compute how much space will the xdr'd header take
507  */
508 
509 #define X_SIZEOF_NC_TYPE X_SIZEOF_INT
510 #define X_SIZEOF_NCTYPE X_SIZEOF_INT
511 
512 /*----< hdr_len_NC_name() >--------------------------------------------------*/
513 static long long
hdr_len_NC_name(const NC_string * ncstrp,int sizeof_t)514 hdr_len_NC_name(const NC_string *ncstrp,
515                 int              sizeof_t)     /* NON_NEG */
516 {
517     /* netCDF file format:
518      * name       = nelems  namestring
519      * nelems     = NON_NEG
520      * namestring = ID1 [IDN ...] padding
521      * ID1        = alphanumeric | '_'
522      * IDN        = alphanumeric | special1 | special2
523      * padding    = <0, 1, 2, or 3 bytes to next 4-byte boundary>
524      * NON_NEG    = <non-negative INT> |  // CDF-1 and CDF-2
525      *              <non-negative INT64>  // CDF-5
526      */
527     long long sz = sizeof_t; /* nelems */
528 
529     assert(ncstrp != NULL);
530 
531     if (ncstrp->nchars != 0)  /* namestring */
532         sz += _RNDUP(ncstrp->nchars, X_ALIGN);
533 
534     return sz;
535 }
536 
537 /*----< hdr_len_NC_dim() >---------------------------------------------------*/
538 static long long
hdr_len_NC_dim(const NC_dim * dimp,int sizeof_t)539 hdr_len_NC_dim(const NC_dim *dimp,
540                int           sizeof_t)     /* NON_NEG */
541 {
542     /* netCDF file format:
543      *  ...
544      * dim        = name  dim_length
545      * dim_length = NON_NEG
546      * NON_NEG    = <non-negative INT> |  // CDF-1 and CDF-2
547      *              <non-negative INT64>  // CDF-5
548      */
549     long long sz;
550 
551     assert(dimp != NULL);
552 
553     sz = hdr_len_NC_name(dimp->name, sizeof_t); /* name */
554     sz += sizeof_t;                             /* dim_length */
555 
556     return sz;
557 }
558 
559 /*----< hdr_len_NC_dimarray() >----------------------------------------------*/
560 static long long
hdr_len_NC_dimarray(const NC_dimarray * ncap,int sizeof_t)561 hdr_len_NC_dimarray(const NC_dimarray *ncap,
562                     int                sizeof_t)     /* NON_NEG */
563 {
564     /* netCDF file format:
565      *  ...
566      * dim_list     = ABSENT | NC_DIMENSION  nelems  [dim ...]
567      * ABSENT       = ZERO  ZERO |  // list is not present for CDF-1 and 2
568      *                ZERO  ZERO64  // for CDF-5
569      * ZERO         = \x00 \x00 \x00 \x00                      // 32-bit zero
570      * ZERO64       = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
571      * NC_DIMENSION = \x00 \x00 \x00 \x0A         // tag for list of dimensions
572      * nelems       = NON_NEG       // number of elements in following sequence
573      * NON_NEG      = <non-negative INT> |        // CDF-1 and CDF-2
574      *                <non-negative INT64>        // CDF-5
575      */
576     int i;
577     long long xlen;
578 
579     xlen = X_SIZEOF_NCTYPE;           /* NC_DIMENSION */
580     xlen += sizeof_t;                 /* nelems */
581 
582     if (ncap == NULL) /* ABSENT: no dimension is defined */
583         return xlen;
584 
585     /* [dim ...] */
586     for (i=0; i<ncap->ndefined; i++)
587         xlen += hdr_len_NC_dim(ncap->value[i], sizeof_t);
588 
589     return xlen;
590 }
591 
592 /*----< hdr_len_NC_attr() >--------------------------------------------------*/
593 static long long
hdr_len_NC_attr(const NC_attr * attrp,int sizeof_t)594 hdr_len_NC_attr(const NC_attr *attrp,
595                 int            sizeof_t)     /* NON_NEG */
596 {
597     /* netCDF file format:
598      *  ...
599      * attr    = name  nc_type  nelems  [values ...]
600      * nc_type = NC_BYTE | NC_CHAR | NC_SHORT | ...
601      * nelems  = NON_NEG       // number of elements in following sequence
602      * values  = bytes | chars | shorts | ints | floats | doubles
603      * bytes   = [BYTE ...]  padding
604      * chars   = [CHAR ...]  padding
605      * shorts  = [SHORT ...]  padding
606      * ints    = [INT ...]
607      * floats  = [FLOAT ...]
608      * doubles = [DOUBLE ...]
609      * padding = <0, 1, 2, or 3 bytes to next 4-byte boundary>
610      * NON_NEG = <non-negative INT> |  // CDF-1 and CDF-2
611      *           <non-negative INT64>  // CDF-5
612      */
613     long long sz;
614 
615     assert(attrp != NULL);
616 
617     sz  = hdr_len_NC_name(attrp->name, sizeof_t); /* name */
618     sz += X_SIZEOF_NC_TYPE;                       /* nc_type */
619     sz += sizeof_t;                               /* nelems */
620     sz += attrp->xsz;                             /* [values ...] */
621 
622     return sz;
623 }
624 
625 /*----< hdr_len_NC_attrarray() >---------------------------------------------*/
626 static long long
hdr_len_NC_attrarray(const NC_attrarray * ncap,int sizeof_t)627 hdr_len_NC_attrarray(const NC_attrarray *ncap,
628                      int                 sizeof_t)     /* NON_NEG */
629 {
630     /* netCDF file format:
631      *  ...
632      * att_list     = ABSENT | NC_ATTRIBUTE  nelems  [attr ...]
633      * ABSENT       = ZERO  ZERO |  // list is not present for CDF-1 and 2
634      *                ZERO  ZERO64  // for CDF-5
635      * ZERO         = \x00 \x00 \x00 \x00                      // 32-bit zero
636      * ZERO64       = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
637      * NC_ATTRIBUTE = \x00 \x00 \x00 \x0C         // tag for list of attributes
638      * nelems       = NON_NEG       // number of elements in following sequence
639      * NON_NEG      = <non-negative INT> |        // CDF-1 and CDF-2
640      *                <non-negative INT64>        // CDF-5
641      */
642     int i;
643     long long xlen;
644 
645     xlen = X_SIZEOF_NCTYPE;        /* NC_ATTRIBUTE */
646     xlen += sizeof_t;              /* nelems */
647 
648     if (ncap == NULL) /* ABSENT: no attribute is defined */
649         return xlen;
650 
651     for (i=0; i<ncap->ndefined; i++) /* [attr ...] */
652         xlen += hdr_len_NC_attr(ncap->value[i], sizeof_t);
653 
654     return xlen;
655 }
656 
657 /*----< hdr_len_NC_var() >---------------------------------------------------*/
658 static long long
hdr_len_NC_var(const NC_var * varp,int sizeof_off_t,int sizeof_t)659 hdr_len_NC_var(const NC_var *varp,
660                int           sizeof_off_t, /* OFFSET */
661                int           sizeof_t)     /* NON_NEG */
662 {
663     /* netCDF file format:
664      * netcdf_file = header data
665      * header      = magic numrecs dim_list gatt_list var_list
666      *  ...
667      * var         = name nelems [dimid ...] vatt_list nc_type vsize begin
668      * nelems      = NON_NEG
669      * dimid       = NON_NEG
670      * vatt_list   = att_list
671      * nc_type     = NC_BYTE | NC_CHAR | NC_SHORT | ...
672      * vsize       = NON_NEG
673      * begin       = OFFSET        // Variable start location.
674      * OFFSET      = <non-negative INT> |  // CDF-1
675      *               <non-negative INT64>  // CDF-2 and CDF-5
676      * NON_NEG     = <non-negative INT> |  // CDF-1 and CDF-2
677      *               <non-negative INT64>  // CDF-5
678      */
679     long long sz;
680 
681     assert(varp != NULL);
682 
683     /* for CDF-1, sizeof_off_t == 4 && sizeof_t == 4
684      * for CDF-2, sizeof_off_t == 8 && sizeof_t == 4
685      * for CDF-5, sizeof_off_t == 8 && sizeof_t == 8
686      */
687     sz = hdr_len_NC_name(varp->name, sizeof_t);         /* name */
688     sz += sizeof_t;                                     /* nelems */
689     sz += sizeof_t * varp->ndims;                       /* [dimid ...] */
690     sz += hdr_len_NC_attrarray(&varp->attrs, sizeof_t); /* vatt_list */
691     sz += X_SIZEOF_NC_TYPE;                             /* nc_type */
692     sz += sizeof_t;                                     /* vsize */
693     sz += sizeof_off_t;                                 /* begin */
694 
695     return sz;
696 }
697 
698 /*----< hdr_len_NC_vararray() >----------------------------------------------*/
699 static long long
hdr_len_NC_vararray(const NC_vararray * ncap,int sizeof_t,int sizeof_off_t)700 hdr_len_NC_vararray(const NC_vararray *ncap,
701                     int                sizeof_t,     /* NON_NEG */
702                     int                sizeof_off_t) /* OFFSET */
703 {
704     /* netCDF file format:
705      * netcdf_file = header  data
706      * header      = magic  numrecs  dim_list  gatt_list  var_list
707      *  ...
708      * var_list    = ABSENT | NC_VARIABLE   nelems  [var ...]
709      * ABSENT      = ZERO  ZERO |  // list is not present for CDF-1 and 2
710      *               ZERO  ZERO64  // for CDF-5
711      * ZERO        = \x00 \x00 \x00 \x00                      // 32-bit zero
712      * ZERO64      = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
713      * NC_VARIABLE = \x00 \x00 \x00 \x0B         // tag for list of variables
714      * nelems      = NON_NEG       // number of elements in following sequence
715      * NON_NEG     = <non-negative INT> |        // CDF-1 and CDF-2
716      *               <non-negative INT64>        // CDF-5
717      */
718     int i;
719     long long xlen;
720 
721     xlen = X_SIZEOF_NCTYPE;           /* NC_VARIABLE */
722     xlen += sizeof_t;                 /* nelems */
723 
724     if (ncap == NULL) /* ABSENT: no variable is defined */
725         return xlen;
726 
727     /* for CDF-1, sizeof_off_t == 4 && sizeof_t == 4
728      * for CDF-2, sizeof_off_t == 8 && sizeof_t == 4
729      * for CDF-5, sizeof_off_t == 8 && sizeof_t == 8
730      */
731     for (i=0; i<ncap->ndefined; i++)  /* [var ...] */
732         xlen += hdr_len_NC_var(ncap->value[i], sizeof_off_t, sizeof_t);
733 
734     return xlen;
735 }
736 
737 /*----< ncmpii_hdr_len_NC() >------------------------------------------------*/
738 static long long
ncmpii_hdr_len_NC(const NC * ncp)739 ncmpii_hdr_len_NC(const NC *ncp)
740 {
741     /* netCDF file format:
742      * netcdf_file = header  data
743      * header      = magic  numrecs  dim_list  gatt_list  var_list
744      *  ...
745      * numrecs     = NON_NEG | STREAMING   // length of record dimension
746      * NON_NEG     = <non-negative INT> |  // CDF-1 and CDF-2
747      *               <non-negative INT64>  // CDF-5
748      */
749 
750     int sizeof_t, sizeof_off_t;
751     long long xlen;
752 
753     assert(ncp != NULL);
754 
755     if (ncp->flags == 5) {        /* CDF-5 */
756         sizeof_t     = X_SIZEOF_INT64; /* 8-byte integer for all integers */
757         sizeof_off_t = X_SIZEOF_INT64; /* 8-byte integer for var begin */
758     }
759     else if (ncp->flags == 2) { /* CDF-2 */
760         sizeof_t     = X_SIZEOF_INT; /* 4-byte integer in CDF-1 */
761         sizeof_off_t = X_SIZEOF_INT64; /* 8-byte integer for var begin */
762     }
763     else { /* CDF-1 */
764         sizeof_t     = X_SIZEOF_INT; /* 4-byte integer in CDF-1 */
765         sizeof_off_t = X_SIZEOF_INT; /* 4-byte integer in CDF-1 */
766     }
767 
768     xlen  = sizeof(ncmagic1);                                          /* magic */
769     xlen += sizeof_t;                                                  /* numrecs */
770     xlen += hdr_len_NC_dimarray(&ncp->dims,   sizeof_t);               /* dim_list */
771     xlen += hdr_len_NC_attrarray(&ncp->attrs, sizeof_t);               /* gatt_list */
772     xlen += hdr_len_NC_vararray(&ncp->vars,   sizeof_t, sizeof_off_t); /* var_list */
773 
774     return xlen; /* return the header size (not yet aligned) */
775 }
776 
777 static int
hdr_fetch(bufferinfo * gbp)778 hdr_fetch(bufferinfo *gbp) {
779     int err=NC_NOERR;
780     size_t slack;        /* any leftover data in the buffer */
781 
782     assert(gbp->base != NULL);
783 
784     slack = gbp->size - (gbp->pos - gbp->base);
785     /* if gbp->pos and gbp->base are the same, there is no leftover buffer
786      * data to worry about.
787      * In the other extreme, where gbp->size == (gbp->pos - gbp->base), then
788      * all data in the buffer has been consumed */
789     if (slack == gbp->size) slack = 0;
790 
791     memset(gbp->base, 0, (size_t)gbp->size);
792     gbp->pos = gbp->base;
793 
794     lseek(gbp->fd, (gbp->offset)-slack, SEEK_SET);
795     size_t read_amount = read(gbp->fd, gbp->base, gbp->size);
796     if (read_amount == -1) {
797         fprintf(stderr,"ERROR at line %d: read error %s\n",__LINE__,strerror(errno));
798         exit(1);
799     }
800     /* we might have had to backtrack */
801     gbp->offset += (gbp->size - slack);
802 
803     return err;
804 }
805 
806 /*----< hdr_check_buffer() >--------------------------------------------------*/
807 /* Ensure that 'nextread' bytes are available.  */
808 static int
hdr_check_buffer(bufferinfo * gbp,size_t nextread)809 hdr_check_buffer(bufferinfo *gbp,
810                  size_t      nextread)
811 {
812     if (gbp->pos + nextread <= gbp->base + gbp->size)
813         return NC_NOERR;
814 
815     /* read the next chunk from file */
816     return hdr_fetch(gbp);
817 }
818 
819 /*----< hdr_get_NCtype() >----------------------------------------------------*/
820 static int
hdr_get_NCtype(bufferinfo * gbp,NCtype * typep)821 hdr_get_NCtype(bufferinfo *gbp,
822                NCtype     *typep)
823 {
824     /* NCtype is 4-byte integer */
825     int status = hdr_check_buffer(gbp, 4);
826     if (status != NC_NOERR) return status;
827 
828     /* get a 4-byte integer */
829     *typep = get_uint32(gbp);
830 
831     return NC_NOERR;
832 }
833 
834 /*----< hdr_get_nc_type() >---------------------------------------------------*/
835 static int
hdr_get_nc_type(bufferinfo * gbp,nc_type * typep)836 hdr_get_nc_type(bufferinfo *gbp,
837                 nc_type    *typep)
838 {
839     /* nc_type is 4-byte integer, X_SIZEOF_INT */
840     int type, status;
841 
842     status = hdr_check_buffer(gbp, X_SIZEOF_INT);
843     if (status != NC_NOERR) return status;
844 
845     type = get_uint32(gbp);
846 
847     if (type != NC_BYTE    &&
848         type != NC_CHAR    &&
849         type != NC_UBYTE   &&
850         type != NC_SHORT   &&
851         type != NC_USHORT  &&
852         type != NC_INT     &&
853         type != NC_UINT    &&
854         type != NC_FLOAT   &&
855         type != NC_DOUBLE  &&
856         type != NC_INT64   &&
857         type != NC_UINT64
858        )
859         DEBUG_RETURN_ERROR(NC_EINVAL);
860 
861     *typep = (nc_type) type;
862     return NC_NOERR;
863 }
864 
865 /*----< hdr_get_NC_name() >---------------------------------------------------*/
866 static int
hdr_get_NC_name(bufferinfo * gbp,NC_string ** ncstrpp)867 hdr_get_NC_name(bufferinfo  *gbp,
868                 NC_string  **ncstrpp)
869 {
870     /* netCDF file format:
871      *  ...
872      * name       = nelems  namestring
873      * nelems     = NON_NEG
874      * namestring = ID1 [IDN ...] padding
875      * ID1        = alphanumeric | '_'
876      * IDN        = alphanumeric | special1 | special2
877      * padding    = <0, 1, 2, or 3 bytes to next 4-byte boundary>
878      * NON_NEG    = <non-negative INT> |  // CDF-1 and CDF-2
879      *              <non-negative INT64>  // CDF-5
880      */
881     int status;
882     size_t  nchars, nbytes, padding, bufremain, strcount;
883     NC_string *ncstrp;
884     char *cpos, pad[X_ALIGN-1];
885 
886     /* get nelems */
887     if (gbp->version == 5)
888         nchars = get_uint64(gbp);
889     else
890         nchars = get_uint32(gbp);
891 
892     /* Allocate a NC_string structure large enough to hold nchars characters */
893     ncstrp = ncmpii_new_NC_string(nchars, NULL);
894 
895     nbytes = nchars;
896     padding = _RNDUP(ncstrp->nchars, X_ALIGN) - ncstrp->nchars;
897     bufremain = gbp->size - (gbp->pos - gbp->base);
898     cpos = ncstrp->cp;
899 
900     /* get namestring with padding */
901     while (nbytes > 0) {
902         if (bufremain > 0) {
903             strcount = MIN(bufremain, nbytes);
904             memcpy(cpos, gbp->pos, strcount);
905             nbytes -= strcount;
906             gbp->pos = (void *)((char *)gbp->pos + strcount);
907             cpos += strcount;
908             bufremain -= strcount;
909         } else {
910             status = hdr_fetch(gbp);
911             if (status != NC_NOERR) {
912                 free(ncstrp);
913                 return status;
914             }
915             bufremain = gbp->size;
916         }
917     }
918 
919     /* handle the padding */
920     if (padding > 0) {
921         memset(pad, 0, X_ALIGN-1);
922         if (memcmp(gbp->pos, pad, padding) != 0) {
923             free(ncstrp);
924             DEBUG_RETURN_ERROR(NC_EINVAL);
925         }
926         gbp->pos = (void *)((char *)gbp->pos + padding);
927     }
928 
929     *ncstrpp = ncstrp;
930 
931     return NC_NOERR;
932 }
933 
934 static NC_dim *
ncmpii_new_x_NC_dim(NC_string * name)935 ncmpii_new_x_NC_dim(NC_string *name)
936 {
937     NC_dim *dimp;
938 
939     dimp = (NC_dim *) malloc(sizeof(NC_dim));
940     MALLOC_CHECK(dimp)
941 
942     dimp->name = name;
943     dimp->size = 0;
944 
945     return(dimp);
946 }
947 
948 /*----< hdr_get_NC_dim() >----------------------------------------------------*/
949 static int
hdr_get_NC_dim(bufferinfo * gbp,NC_dim ** dimpp)950 hdr_get_NC_dim(bufferinfo  *gbp,
951                NC_dim     **dimpp)
952 {
953     /* netCDF file format:
954      *  ...
955      * dim        = name  dim_length
956      * dim_length = NON_NEG
957      * NON_NEG    = <non-negative INT> |  // CDF-1 and CDF-2
958      *              <non-negative INT64>  // CDF-5
959      */
960     int status;
961     NC_string *ncstrp;
962     NC_dim *dimp;
963 
964     /* get name */
965     status = hdr_get_NC_name(gbp, &ncstrp);
966     if (status != NC_NOERR) return status;
967 
968     dimp = ncmpii_new_x_NC_dim(ncstrp);
969 
970     /* get dim_length */
971     if (gbp->version == 5)
972         dimp->size = get_uint64(gbp);
973     else
974         dimp->size = get_uint32(gbp);
975 
976     *dimpp = dimp;
977     return NC_NOERR;
978 }
979 
980 static void
ncmpii_free_NC_dim(NC_dim * dimp)981 ncmpii_free_NC_dim(NC_dim *dimp)
982 {
983     if (dimp == NULL) return;
984     free(dimp->name);
985     free(dimp);
986 }
987 
988 static void
ncmpii_free_NC_dimarray(NC_dimarray * ncap)989 ncmpii_free_NC_dimarray(NC_dimarray *ncap)
990 {
991     int i;
992 
993     assert(ncap != NULL);
994     if (ncap->nalloc == 0) return;
995 
996     assert(ncap->value != NULL);
997     for (i=0; i<ncap->ndefined; i++)
998         ncmpii_free_NC_dim(ncap->value[i]);
999 
1000     free(ncap->value);
1001     ncap->value    = NULL;
1002     ncap->nalloc   = 0;
1003     ncap->ndefined = 0;
1004 }
1005 
1006 
1007 /*----< hdr_get_NC_dimarray() >-----------------------------------------------*/
1008 static int
hdr_get_NC_dimarray(bufferinfo * gbp,NC_dimarray * ncap)1009 hdr_get_NC_dimarray(bufferinfo  *gbp,
1010                     NC_dimarray *ncap)
1011 {
1012     /* netCDF file format:
1013      *  ...
1014      * dim_list     = ABSENT | NC_DIMENSION  nelems  [dim ...]
1015      * ABSENT       = ZERO  ZERO |  // list is not present for CDF-1 and 2
1016      *                ZERO  ZERO64  // for CDF-5
1017      * ZERO         = \x00 \x00 \x00 \x00                      // 32-bit zero
1018      * ZERO64       = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
1019      * NC_DIMENSION = \x00 \x00 \x00 \x0A         // tag for list of dimensions
1020      * nelems       = NON_NEG       // number of elements in following sequence
1021      * NON_NEG      = <non-negative INT> |        // CDF-1 and CDF-2
1022      *                <non-negative INT64>        // CDF-5
1023      */
1024     int i, status;
1025     NCtype type = NC_UNSPECIFIED;
1026     size_t ndefined;
1027 
1028     /* get NCtype (NC_DIMENSION) */
1029     status = hdr_get_NCtype(gbp, &type);
1030     if (status != NC_NOERR) return status;
1031 
1032     /* get nelems */
1033     if (gbp->version == 5)
1034         ndefined = get_uint64(gbp);
1035     else
1036         ndefined = get_uint32(gbp);
1037     ncap->ndefined = (int)ndefined;
1038 
1039     if (ndefined == 0) {
1040         if (type != NC_DIMENSION && type != NC_UNSPECIFIED)
1041             DEBUG_RETURN_ERROR(NC_EINVAL);
1042     } else {
1043         if (type != NC_DIMENSION) DEBUG_RETURN_ERROR(NC_EINVAL);
1044 
1045         ncap->value = (NC_dim **) malloc(ndefined * sizeof(NC_dim*));
1046         MALLOC_CHECK(ncap->value)
1047         ncap->nalloc = (int)ndefined;
1048 
1049         for (i=0; i<ndefined; i++) {
1050             status = hdr_get_NC_dim(gbp, ncap->value + i);
1051             if (status != NC_NOERR) { /* error: fail to get the next dim */
1052                 ncap->ndefined = i;
1053                 ncmpii_free_NC_dimarray(ncap);
1054                 return status;
1055             }
1056         }
1057     }
1058 
1059     return NC_NOERR;
1060 }
1061 
1062 /*----< hdr_get_NC_attrV() >--------------------------------------------------*/
1063 static int
hdr_get_NC_attrV(bufferinfo * gbp,NC_attr * attrp)1064 hdr_get_NC_attrV(bufferinfo *gbp,
1065                  NC_attr    *attrp)
1066 {
1067     /* netCDF file format:
1068      *  ...
1069      * attr    = name  nc_type  nelems  [values ...]
1070      *  ...
1071      * values  = bytes | chars | shorts | ints | floats | doubles
1072      * bytes   = [BYTE ...]  padding
1073      * chars   = [CHAR ...]  padding
1074      * shorts  = [SHORT ...]  padding
1075      * ints    = [INT ...]
1076      * floats  = [FLOAT ...]
1077      * doubles = [DOUBLE ...]
1078      * padding = <0, 1, 2, or 3 bytes to next 4-byte boundary>
1079      */
1080     int status;
1081     void *value = attrp->xvalue;
1082     char pad[X_ALIGN-1];
1083     size_t nbytes, padding, bufremain, attcount;
1084 
1085     nbytes = attrp->nelems * ncmpix_len_nctype(attrp->type);
1086     padding = attrp->xsz - nbytes;
1087     bufremain = gbp->size - (gbp->pos - gbp->base);
1088 
1089     /* get values */
1090     while (nbytes > 0) {
1091         if (bufremain > 0) {
1092             attcount = MIN(bufremain, nbytes);
1093             memcpy(value, gbp->pos, (size_t)attcount);
1094             nbytes -= attcount;
1095             gbp->pos = (void *)((char *)gbp->pos + attcount);
1096             value = (void *)((char *)value + attcount);
1097             bufremain -= attcount;
1098         } else {
1099             status = hdr_fetch(gbp);
1100             if (status != NC_NOERR) return status;
1101             bufremain = gbp->size;
1102         }
1103     }
1104 
1105     /* handle the padding */
1106     if (padding > 0) {
1107         memset(pad, 0, X_ALIGN-1);
1108         if (memcmp(gbp->pos, pad, (size_t)padding) != 0)
1109             DEBUG_RETURN_ERROR(NC_EINVAL);
1110         gbp->pos = (void *)((char *)gbp->pos + padding);
1111     }
1112 
1113     return NC_NOERR;
1114 }
1115 
1116 static long long
ncmpix_len_NC_attrV(nc_type type,long long nelems)1117 ncmpix_len_NC_attrV(nc_type    type,
1118                     long long  nelems)
1119 {
1120     switch(type) {
1121         case NC_BYTE:
1122         case NC_CHAR:
1123         case NC_UBYTE:  return ncmpix_len_char(nelems);
1124         case NC_SHORT:  return ncmpix_len_short(nelems);
1125         case NC_USHORT: return ncmpix_len_ushort(nelems);
1126         case NC_INT:    return ncmpix_len_int(nelems);
1127         case NC_UINT:   return ncmpix_len_uint(nelems);
1128         case NC_FLOAT:  return ncmpix_len_float(nelems);
1129         case NC_DOUBLE: return ncmpix_len_double(nelems);
1130         case NC_INT64:  return ncmpix_len_int64(nelems);
1131         case NC_UINT64: return ncmpix_len_uint64(nelems);
1132         default: assert("ncmpix_len_NC_attr bad type" == 0);
1133     }
1134     return 0;
1135 }
1136 
1137 static NC_attr *
ncmpii_new_x_NC_attr(NC_string * strp,nc_type type,size_t nelems)1138 ncmpii_new_x_NC_attr(NC_string  *strp,
1139                      nc_type     type,
1140                      size_t      nelems)
1141 {
1142     NC_attr *attrp;
1143     const long long xsz = ncmpix_len_NC_attrV(type, nelems);
1144     size_t sz = M_RNDUP(sizeof(NC_attr));
1145 
1146     assert(!(xsz == 0 && nelems != 0));
1147 
1148     sz += (size_t)xsz;
1149 
1150     attrp = (NC_attr *) malloc(sz);
1151     MALLOC_CHECK(attrp)
1152 
1153     attrp->xsz    = xsz;
1154     attrp->name   = strp;
1155     attrp->type   = type;
1156     attrp->nelems = nelems;
1157 
1158     if (xsz != 0)
1159         attrp->xvalue = (char *)attrp + M_RNDUP(sizeof(NC_attr));
1160     else
1161         attrp->xvalue = NULL;
1162 
1163     return(attrp);
1164 }
1165 
1166 static void
ncmpii_free_NC_attr(NC_attr * attrp)1167 ncmpii_free_NC_attr(NC_attr *attrp)
1168 {
1169     if (attrp == NULL) return;
1170     free(attrp->name);
1171     free(attrp);
1172 }
1173 
1174 
1175 /*----< hdr_get_NC_attr() >---------------------------------------------------*/
1176 static int
hdr_get_NC_attr(bufferinfo * gbp,NC_attr ** attrpp)1177 hdr_get_NC_attr(bufferinfo  *gbp,
1178                 NC_attr    **attrpp)
1179 {
1180     /* netCDF file format:
1181      *  ...
1182      * attr    = name  nc_type  nelems  [values ...]
1183      * nc_type = NC_BYTE | NC_CHAR | NC_SHORT | ...
1184      * nelems  = NON_NEG       // number of elements in following sequence
1185      * NON_NEG = <non-negative INT> |  // CDF-1 and CDF-2
1186      *           <non-negative INT64>  // CDF-5
1187      */
1188     int status;
1189     NC_string *strp;
1190     nc_type type;
1191     size_t nelems;
1192     NC_attr *attrp;
1193 
1194     /* get name */
1195     status = hdr_get_NC_name(gbp, &strp);
1196     if (status != NC_NOERR) return status;
1197 
1198     /* get nc_type */
1199     status = hdr_get_nc_type(gbp, &type);
1200     if (status != NC_NOERR) {
1201         free(strp);
1202         return status;
1203     }
1204 
1205     /* get nelems */
1206     if (gbp->version == 5)
1207         nelems = get_uint64(gbp);
1208     else
1209         nelems = get_uint32(gbp);
1210 
1211     /* allocate space for attribute object */
1212     attrp = ncmpii_new_x_NC_attr(strp, type, nelems);
1213     if (attrp == NULL) {
1214         free(strp);
1215         return status;
1216     }
1217 
1218     /* get [values ...] */
1219     status = hdr_get_NC_attrV(gbp, attrp);
1220     if (status != NC_NOERR) {
1221         ncmpii_free_NC_attr(attrp); /* frees strp */
1222         return status;
1223     }
1224 
1225     *attrpp = attrp;
1226     return NC_NOERR;
1227 }
1228 
1229 static void
ncmpii_free_NC_attrarray(NC_attrarray * ncap)1230 ncmpii_free_NC_attrarray(NC_attrarray *ncap)
1231 {
1232     int i;
1233 
1234     assert(ncap != NULL);
1235     if (ncap->nalloc == 0) return;
1236     assert(ncap->value != NULL);
1237     for (i=0; i<ncap->ndefined; i++)
1238         ncmpii_free_NC_attr(ncap->value[i]);
1239 
1240     free(ncap->value);
1241     ncap->value    = NULL;
1242     ncap->nalloc   = 0;
1243     ncap->ndefined = 0;
1244 }
1245 /*----< hdr_get_NC_attrarray() >----------------------------------------------*/
1246 static int
hdr_get_NC_attrarray(bufferinfo * gbp,NC_attrarray * ncap)1247 hdr_get_NC_attrarray(bufferinfo   *gbp,
1248                      NC_attrarray *ncap)
1249 {
1250     /* netCDF file format:
1251      *  ...
1252      * att_list     = ABSENT | NC_ATTRIBUTE  nelems  [attr ...]
1253      * ABSENT       = ZERO  ZERO |  // list is not present for CDF-1 and 2
1254      *                ZERO  ZERO64  // for CDF-5
1255      * ZERO         = \x00 \x00 \x00 \x00                      // 32-bit zero
1256      * ZERO64       = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
1257      * NC_ATTRIBUTE = \x00 \x00 \x00 \x0C         // tag for list of attributes
1258      * nelems       = NON_NEG       // number of elements in following sequence
1259      * NON_NEG      = <non-negative INT> |        // CDF-1 and CDF-2
1260      *                <non-negative INT64>        // CDF-5
1261      */
1262     int i, status;
1263     NCtype type = NC_UNSPECIFIED;
1264     size_t ndefined;
1265 
1266     /* get NCtype (NC_ATTRIBUTE) */
1267     status = hdr_get_NCtype(gbp, &type);
1268     if (status != NC_NOERR) return status;
1269 
1270     /* get nelems */
1271     if (gbp->version == 5)
1272         ndefined = get_uint64(gbp);
1273     else
1274         ndefined = get_uint32(gbp);
1275     ncap->ndefined = (int)ndefined;
1276 
1277     if (ndefined == 0) {
1278         if (type != NC_ATTRIBUTE && type != NC_UNSPECIFIED)
1279             DEBUG_RETURN_ERROR(NC_EINVAL);
1280     } else {
1281         if (type != NC_ATTRIBUTE)
1282             DEBUG_RETURN_ERROR(NC_EINVAL);
1283 
1284         ncap->value = (NC_attr **)malloc((size_t)ndefined * sizeof(NC_attr*));
1285         MALLOC_CHECK(ncap->value)
1286         ncap->nalloc = (int)ndefined;
1287 
1288         /* get [attr ...] */
1289         for (i=0; i<ndefined; i++) {
1290             status = hdr_get_NC_attr(gbp, ncap->value + i);
1291             if (status != NC_NOERR) { /* Error: fail to get the next att */
1292                 ncap->ndefined = i;
1293                 ncmpii_free_NC_attrarray(ncap);
1294                 return status;
1295             }
1296         }
1297     }
1298 
1299     return NC_NOERR;
1300 }
1301 
1302 static NC_var *
ncmpii_new_x_NC_var(NC_string * strp,int ndims)1303 ncmpii_new_x_NC_var(NC_string *strp,
1304                     int        ndims)
1305 {
1306     NC_var *varp;
1307     int shape_space   = M_RNDUP(ndims * 8);
1308     int dsizes_space  = M_RNDUP(ndims * 8);
1309     int dimids_space  = M_RNDUP(ndims * 4);
1310     size_t sizeof_NC_var = M_RNDUP(sizeof(NC_var));
1311     size_t sz = sizeof_NC_var + (size_t)(shape_space + dsizes_space + dimids_space);
1312 
1313     varp = (NC_var *) malloc(sz);
1314     MALLOC_CHECK(varp)
1315 
1316     memset(varp, 0, sz);
1317 
1318     varp->name = strp;
1319     varp->ndims = ndims;
1320 
1321     if (ndims != 0) {
1322         varp->shape  = (long long *)((char *)varp + sizeof_NC_var);
1323         varp->dsizes = (long long *)((char *)varp->shape  + shape_space);
1324         varp->dimids = (int *)      ((char *)varp->dsizes + dsizes_space);
1325     }
1326 
1327     varp->xsz = 0;
1328     varp->len = 0;
1329     varp->begin = 0;
1330     return varp;
1331 }
1332 
1333 static void
ncmpii_free_NC_var(NC_var * varp)1334 ncmpii_free_NC_var(NC_var *varp)
1335 {
1336     if (varp == NULL) return;
1337     ncmpii_free_NC_attrarray(&varp->attrs);
1338     free(varp->name);
1339     free(varp);
1340 }
1341 
1342 /*----< hdr_get_NC_var() >---------------------------------------------------*/
1343 static int
hdr_get_NC_var(bufferinfo * gbp,NC_var ** varpp)1344 hdr_get_NC_var(bufferinfo  *gbp,
1345                NC_var     **varpp)
1346 {
1347     /* netCDF file format:
1348      * netcdf_file = header data
1349      * header      = magic numrecs dim_list gatt_list var_list
1350      *  ...
1351      * var         = name nelems [dimid ...] vatt_list nc_type vsize begin
1352      * nelems      = NON_NEG
1353      * dimid       = NON_NEG
1354      * vatt_list   = att_list
1355      * nc_type     = NC_BYTE | NC_CHAR | NC_SHORT | ...
1356      * vsize       = NON_NEG
1357      * begin       = OFFSET        // Variable start location.
1358      * OFFSET      = <non-negative INT> |  // CDF-1
1359      *               <non-negative INT64>  // CDF-2 and CDF-5
1360      * NON_NEG     = <non-negative INT> |  // CDF-1 and CDF-2
1361      *               <non-negative INT64>  // CDF-5
1362      */
1363     int status;
1364     NC_string *strp;
1365     size_t ndims=0, dim;
1366     NC_var *varp;
1367 
1368     /* get name */
1369     status = hdr_get_NC_name(gbp, &strp);
1370     if (status != NC_NOERR) return status;
1371 
1372     /* nelems */
1373     if (gbp->version == 5)
1374         ndims = get_uint64(gbp);
1375     else
1376         ndims = get_uint32(gbp);
1377 
1378     /* allocate space for var object */
1379     varp = ncmpii_new_x_NC_var(strp, (int)ndims);
1380 
1381     /* get [dimid ...] */
1382     for (dim=0; dim<ndims; dim++) {
1383         status = hdr_check_buffer(gbp, (gbp->version == 5 ? 8 : 4));
1384         if (status != NC_NOERR) {
1385             ncmpii_free_NC_var(varp);
1386             return status;
1387         }
1388         if (gbp->version == 5)
1389             varp->dimids[dim] = get_uint64(gbp);
1390         else
1391             varp->dimids[dim] = get_uint32(gbp);
1392     }
1393 
1394     /* get vatt_list */
1395     status = hdr_get_NC_attrarray(gbp, &varp->attrs);
1396     if (status != NC_NOERR) {
1397         ncmpii_free_NC_var(varp);
1398         return status;
1399     }
1400 
1401     /* get nc_type */
1402     status = hdr_get_nc_type(gbp, &varp->type);
1403     if (status != NC_NOERR) {
1404         ncmpii_free_NC_var(varp);
1405         return status;
1406     }
1407 
1408     /* get vsize */
1409     if (gbp->version == 5)
1410         varp->len = get_uint64(gbp);
1411     else
1412         varp->len = get_uint32(gbp);
1413 
1414     /* next element is 'begin' */
1415     status = hdr_check_buffer(gbp, (gbp->version == 1 ? 4 : 8));
1416     if (status != NC_NOERR) {
1417         ncmpii_free_NC_var(varp);
1418         return status;
1419     }
1420 
1421     /* get begin */
1422     if (gbp->version == 1)
1423         varp->begin = get_uint32(gbp);
1424     else
1425         varp->begin = get_uint64(gbp);
1426 
1427     *varpp = varp;
1428     return NC_NOERR;
1429 }
1430 
1431 static void
ncmpii_free_NC_vararray(NC_vararray * ncap)1432 ncmpii_free_NC_vararray(NC_vararray *ncap)
1433 {
1434     int i;
1435 
1436     assert(ncap != NULL);
1437     if (ncap->nalloc == 0) return;
1438 
1439     assert(ncap->value != NULL);
1440     for (i=0; i<ncap->ndefined; i++) {
1441         if (ncap->value[i] != NULL)
1442             ncmpii_free_NC_var(ncap->value[i]);
1443     }
1444 
1445     free(ncap->value);
1446     ncap->value    = NULL;
1447     ncap->nalloc   = 0;
1448     ncap->ndefined = 0;
1449 }
1450 
1451 /*----< hdr_get_NC_vararray() >----------------------------------------------*/
1452 static int
hdr_get_NC_vararray(bufferinfo * gbp,NC_vararray * ncap)1453 hdr_get_NC_vararray(bufferinfo  *gbp,
1454                     NC_vararray *ncap)
1455 {
1456     /* netCDF file format:
1457      * netcdf_file = header  data
1458      * header      = magic  numrecs  dim_list  gatt_list  var_list
1459      *  ...
1460      * var_list    = ABSENT | NC_VARIABLE   nelems  [var ...]
1461      * ABSENT      = ZERO  ZERO |  // list is not present for CDF-1 and 2
1462      *               ZERO  ZERO64  // for CDF-5
1463      * ZERO        = \x00 \x00 \x00 \x00                      // 32-bit zero
1464      * ZERO64      = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00  // 64-bit zero
1465      * NC_VARIABLE = \x00 \x00 \x00 \x0B         // tag for list of variables
1466      * nelems      = NON_NEG       // number of elements in following sequence
1467      * NON_NEG     = <non-negative INT> |        // CDF-1 and CDF-2
1468      *               <non-negative INT64>        // CDF-5
1469      */
1470     int i, status;
1471     NCtype type = NC_UNSPECIFIED;
1472     size_t ndefined;
1473 
1474     assert(gbp != NULL && gbp->pos != NULL);
1475     assert(ncap != NULL);
1476     assert(ncap->value == NULL);
1477 
1478     /* get NCtype (NC_VARIABLE) from gbp buffer */
1479     status = hdr_get_NCtype(gbp, &type);
1480     if (status != NC_NOERR) return status;
1481 
1482     /* get nelems (number of variables) from gbp buffer */
1483     if (gbp->version == 5)
1484         ndefined = get_uint64(gbp);
1485     else
1486         ndefined = get_uint32(gbp);
1487     ncap->ndefined = (int)ndefined;
1488 
1489     if (ndefined == 0) { /* no variable defined */
1490         if (type != NC_VARIABLE && type != NC_UNSPECIFIED)
1491             DEBUG_RETURN_ERROR(NC_EINVAL);
1492     } else {
1493         if (type != NC_VARIABLE)
1494             DEBUG_RETURN_ERROR(NC_EINVAL);
1495 
1496         ncap->value = (NC_var **) malloc((size_t)ndefined * sizeof(NC_var*));
1497         MALLOC_CHECK(ncap->value)
1498         ncap->nalloc = (int)ndefined;
1499 
1500         /* get [var ...] */
1501         for (i=0; i<ndefined; i++) {
1502             status = hdr_get_NC_var(gbp, ncap->value + i);
1503             if (status != NC_NOERR) { /* Error: fail to get the next var */
1504                 ncap->ndefined = i;
1505                 ncmpii_free_NC_vararray(ncap);
1506                 return status;
1507             }
1508         }
1509     }
1510 
1511     return NC_NOERR;
1512 }
1513 
1514 /*----< ncmpii_hdr_get_NC() >------------------------------------------------*/
1515 /*  CDF format specification
1516  *      netcdf_file  = header  data
1517  *      header       = magic  numrecs  dim_list  gatt_list  var_list
1518  *      magic        = 'C'  'D'  'F'  VERSION
1519  *      VERSION      = \x01 |                      // classic format
1520  *                     \x02 |                      // 64-bit offset format
1521  *                     \x05                        // 64-bit data format
1522  *      numrecs      = NON_NEG | STREAMING         // length of record dimension
1523  *      dim_list     = ABSENT | NC_DIMENSION  nelems  [dim ...]
1524  *      gatt_list    = att_list                    // global attributes
1525  *      att_list     = ABSENT | NC_ATTRIBUTE  nelems  [attr ...]
1526  *      var_list     = ABSENT | NC_VARIABLE   nelems  [var ...]
1527  */
1528 static int
ncmpii_hdr_get_NC(int fd,NC * ncp)1529 ncmpii_hdr_get_NC(int fd, NC *ncp)
1530 {
1531     int i, status;
1532     bufferinfo getbuf;
1533     char *magic;
1534 
1535     /* Initialize the get buffer that stores the header read from the file */
1536     getbuf.fd     = fd;
1537     getbuf.offset = 0;   /* read from start of the file */
1538     getbuf.size   = NC_DEFAULT_CHUNKSIZE;
1539     getbuf.base   = (void *)malloc((size_t)getbuf.size);
1540     MALLOC_CHECK(getbuf.base)
1541     getbuf.pos    = getbuf.base;
1542 
1543     /* Fetch the next header chunk. The chunk is 'gbp->size' bytes big */
1544     status = hdr_fetch(&getbuf);
1545     if (status != NC_NOERR) return status;
1546 
1547     /* processing the header from getbuf, the get buffer */
1548 
1549     /* First get the file format information, magic */
1550     magic = getbuf.base;
1551     getbuf.pos += 4;
1552 
1553     /* don't need to worry about CDF-1 or CDF-2
1554      * if the first bits are not 'CDF'
1555      */
1556     if (memcmp(magic, ncmagic1, sizeof(ncmagic1)-1) != 0) {
1557         DEBUG_ASSIGN_ERROR(status, NC_ENOTNC)
1558         goto fn_exit;
1559     }
1560 
1561     /* check version number in last byte of magic */
1562     if (magic[sizeof(ncmagic1)-1] == 0x1) {
1563         getbuf.version = 1;
1564         ncp->flags = 1;
1565     } else if (magic[sizeof(ncmagic1)-1] == 0x2) {
1566         getbuf.version = 2;
1567         ncp->flags = 2;
1568     } else if (magic[sizeof(ncmagic1)-1] == 0x5) {
1569         getbuf.version = 5;
1570         ncp->flags = 5;
1571     } else {
1572         DEBUG_ASSIGN_ERROR(status, NC_ENOTNC); /* not an netCDF file */
1573         goto fn_exit;
1574     }
1575 
1576     /** Ensure that 'nextread' bytes (numrecs) are available. */
1577     status = hdr_check_buffer(&getbuf, (getbuf.version == 5) ? 8 : 4);
1578     if(status != NC_NOERR) goto fn_exit;
1579 
1580     /* get numrecs from getbuf into ncp */
1581     if (getbuf.version == 5)
1582         ncp->numrecs = get_uint64(&getbuf);
1583     else
1584         ncp->numrecs = get_uint32(&getbuf);
1585 
1586     /* get dim_list from getbuf into ncp */
1587     status = hdr_get_NC_dimarray(&getbuf, &ncp->dims);
1588     if (status != NC_NOERR) goto fn_exit;
1589 
1590     /* get gatt_list from getbuf into ncp */
1591     status = hdr_get_NC_attrarray(&getbuf, &ncp->attrs);
1592     if (status != NC_NOERR) goto fn_exit;
1593 
1594     /* get var_list from getbuf into ncp */
1595     status = hdr_get_NC_vararray(&getbuf, &ncp->vars);
1596     if (status != NC_NOERR) goto fn_exit;
1597 
1598     /* get the un-aligned size occupied by the file header */
1599     ncp->xsz = ncmpii_hdr_len_NC(ncp);
1600 
1601     /* Recompute the shapes of all variables
1602      * Sets ncp->begin_var to start of first variable.
1603      * Sets ncp->begin_rec to start of first record variable.
1604      */
1605     status = ncmpii_NC_computeshapes(ncp);
1606 
1607     /* update the total number of record variables */
1608     ncp->vars.num_rec_vars = 0;
1609     for (i=0; i<ncp->vars.ndefined; i++)
1610         ncp->vars.num_rec_vars += IS_RECVAR(ncp->vars.value[i]);
1611 
1612 fn_exit:
1613     free(getbuf.base);
1614     return status;
1615 }
1616 
1617 /*----< ncmpii_free_NC() >----------------------------------------------------*/
1618 static void
ncmpii_free_NC(NC * ncp)1619 ncmpii_free_NC(NC *ncp)
1620 {
1621     if (ncp == NULL) return;
1622     ncmpii_free_NC_dimarray(&ncp->dims);
1623     ncmpii_free_NC_attrarray(&ncp->attrs);
1624     ncmpii_free_NC_vararray(&ncp->vars);
1625     free(ncp->path);
1626 }
1627 
1628 const char *
ncmpi_strerror(int err)1629 ncmpi_strerror(int err)
1630 {
1631    switch(err)
1632    {
1633       case NC_NOERR:
1634 	 return "No error";
1635       case NC_EINVAL:
1636 	 return "NetCDF: Invalid argument";
1637       case NC_EBADDIM:
1638 	 return "NetCDF: Invalid dimension ID or name";
1639       case NC_EUNLIMPOS:
1640 	 return "NetCDF: NC_UNLIMITED in the wrong index";
1641       case NC_ENOTNC:
1642 	 return "NetCDF: Unknown file format";
1643       case NC_EVARSIZE:
1644 	 return "NetCDF: One or more variable sizes violate format constraints";
1645       default:
1646          printf("Unknown error code %d\n",err);
1647          return "Unknown error code";
1648    }
1649 }
1650 
1651 const char *
ncmpii_err_code_name(int err)1652 ncmpii_err_code_name(int err)
1653 {
1654    switch(err)
1655    {
1656       case NC_NOERR:     return "NC_NOERR";
1657       case NC_EINVAL:    return "NC_EINVAL";
1658       case NC_EBADDIM:   return "NC_EBADDIM";
1659       case NC_EUNLIMPOS: return "NC_EUNLIMPOS";
1660       case NC_ENOTNC:    return "NC_ENOTNC";
1661       case NC_EVARSIZE : return "NC_EVARSIZE";
1662       default:
1663          printf("Unknown error code %d\n",err);
1664          return "Unknown error code";
1665    }
1666 }
1667 
1668 struct fspec {
1669     int nlvars;    /* Number of variables specified with -v
1670                     * option on command line */
1671     char** lvars;  /* list of variable names specified with -v
1672                     * option on command line */
1673     NC_var **varp;  /* [nlvars] */
1674     int    *varids; /* [nlvars] */
1675 };
1676 
1677 static void
make_lvars(char * optarg,struct fspec * fspecp)1678 make_lvars(char *optarg, struct fspec* fspecp)
1679 {
1680     char *cp = optarg;
1681     int nvars = 1;
1682     char ** cpp;
1683 
1684     /* compute number of variable names in comma-delimited list */
1685     fspecp->nlvars = 1;
1686     while (*cp++)
1687         if (*cp == ',')
1688             nvars++;
1689 
1690     fspecp->lvars = (char **) malloc(nvars * sizeof(char*));
1691     MALLOC_CHECK(fspecp->lvars)
1692 
1693     cpp = fspecp->lvars;
1694     /* copy variable names into list */
1695     for (cp = strtok(optarg, ",");
1696          cp != NULL;
1697          cp = strtok((char *) NULL, ",")) {
1698 
1699         *cpp = (char *) malloc(strlen(cp) + 1);
1700         MALLOC_CHECK(*cpp)
1701 
1702         strcpy(*cpp, cp);
1703         cpp++;
1704     }
1705     fspecp->nlvars = nvars;
1706     fspecp->varp = (NC_var**) malloc(nvars * sizeof(NC_var*));
1707     MALLOC_CHECK(fspecp->varp)
1708 }
1709 
1710 /*----< check_gap_in_fixed_vars() >------------------------------------------*/
1711 /* check whether a gap (unused space in file between) two consecutive
1712  * fixed-size variables. The gap is produced by file offset alignment which
1713  * can be set by PnetCDF hint nc_var_align_size.
1714  */
1715 static int
check_gap_in_fixed_vars(NC * ncp)1716 check_gap_in_fixed_vars(NC *ncp)
1717 {
1718     int i, j;
1719     long long prev_end;
1720     NC_var *varp, *prev;
1721 
1722     /* check all fixed-size variables */
1723     for (i=1; i<ncp->vars.ndefined; i++) {
1724         varp = ncp->vars.value[i];
1725 
1726         if (IS_RECVAR(varp)) continue;
1727 
1728         /* search for the previous fixed-size variable */
1729         prev = NULL;
1730         for (j=i-1; j>=0; j--) {
1731             if (!IS_RECVAR(ncp->vars.value[j])) {
1732                 prev = ncp->vars.value[j];
1733                 break;
1734             }
1735         }
1736         if (prev == NULL) /* first defined fixed-size variable */
1737             continue;
1738 
1739         /* not the first fixed-size variable */
1740         prev_end = prev->begin;
1741         if (prev->ndims == 0)
1742             prev_end += type_size(prev->type);
1743         else
1744             prev_end += type_size(prev->type) * prev->dsizes[0];
1745 
1746         /* check the gap between the begin of this variable from the end of
1747          * variable immediately before it */
1748         if (varp->begin - prev_end) return 1;
1749     }
1750     return 0;
1751 }
1752 
1753 static void
usage(char * cmd)1754 usage(char *cmd)
1755 {
1756     char *help =
1757 "Usage: %s [-h] | [-x] | [-sgr] [-v var1[,...]] file\n"
1758 "       [-h]            Print help\n"
1759 "       [-v var1[,...]] Output for variable(s) <var1>,... only\n"
1760 "       [-s]            Output variable size. For record variables, output\n"
1761 "                       the size of one record only\n"
1762 "       [-g]            Output gap from the previous variable\n"
1763 "       [-r]            Output offsets for all records\n"
1764 "       [-x]            Check gaps in fixed-size variables, output 1 if gaps\n"
1765 "                       are found, 0 for otherwise.\n"
1766 "       file            Input netCDF file name\n"
1767 "*Parallel netCDF library version 1.7.0\n";
1768     fprintf(stderr, help, cmd);
1769 }
1770 
1771 /*----< main() >-------------------------------------------------------------*/
main(int argc,char * argv[])1772 int main(int argc, char *argv[])
1773 {
1774     extern int optind;
1775     char *filename, *cmd, *env_str;
1776     int i, j, err, opt, nvars;
1777     int print_var_size=0, print_gap=0, check_gap=0, print_all_rec=0;
1778     NC *ncp;
1779     struct fspec *fspecp=NULL;
1780 
1781     fspecp = (struct fspec*) calloc(1, sizeof(struct fspec));
1782     cmd = (char*) malloc(strlen(argv[0])+1);
1783     strcpy(cmd,argv[0]);
1784 
1785     /* get command-line arguments */
1786     while ((opt = getopt(argc, argv, "v:sghqxr")) != EOF) {
1787         switch(opt) {
1788             case 'v': make_lvars (optarg, fspecp);
1789                       break;
1790             case 's': print_var_size = 1;
1791                       break;
1792             case 'g': print_gap = 1;
1793                       break;
1794             case 'r': print_all_rec = 1;
1795                       break;
1796             case 'x': check_gap = 1;
1797                       break;
1798             case 'h':
1799             default:  usage(cmd);
1800                       free(fspecp);
1801                       return 0;
1802         }
1803     }
1804     argc -= optind;
1805     argv += optind;
1806     if (argc != 1) {
1807         fprintf(stderr, "%s: missing file name\n", cmd);
1808         usage(cmd);
1809         if (fspecp->varp != NULL) free(fspecp->varp);
1810         for (i=0; i<fspecp->nlvars; i++)
1811             free(fspecp->lvars[i]);
1812         if (fspecp->lvars != NULL) free(fspecp->lvars);
1813         free(fspecp);
1814         return 1;
1815     }
1816     free(cmd);
1817     filename = argv[0]; /* required argument */
1818 
1819     verbose_debug = 0;
1820     env_str = getenv("PNETCDF_VERBOSE_DEBUG_MODE");
1821     if (env_str != NULL && *env_str != '0') verbose_debug = 1;
1822 
1823     /* find Endianness of the running machine */
1824     is_little_endian = check_little_endian();
1825 
1826     /* open file */
1827     int fd = open(filename, O_RDONLY, 0666);
1828     if (fd == -1) {
1829         printf("Error: file open %s (%s)\n",filename,strerror(errno));
1830         exit(1);
1831     }
1832 
1833     ncp = (NC*) calloc(1, sizeof(NC));
1834     ncp->path = (char*) malloc(strlen(filename)+1);
1835     strcpy(ncp->path, filename);
1836 
1837     /* read the header from file */
1838     err = ncmpii_hdr_get_NC(fd, ncp);
1839     if (err != NC_NOERR) {
1840         fprintf(stderr,"Error: %s\n", ncmpii_err_code_name(err));
1841         exit(1);
1842     }
1843 
1844     if (check_gap) {
1845         int ret = check_gap_in_fixed_vars(ncp);
1846         ncmpii_free_NC(ncp);
1847         free(ncp);
1848         close(fd);
1849         printf("%d\n",ret);
1850         return 0;
1851     }
1852 
1853     /* First check if all selected variables can be found in input file. */
1854     if (fspecp->nlvars > 0) {
1855         /* print a selected list of variables */
1856         fspecp->varids = (int*) malloc(fspecp->nlvars * sizeof(int));
1857         MALLOC_CHECK(fspecp->varids)
1858         for (i=0; i<fspecp->nlvars; i++) {
1859             for (j=0; j<ncp->vars.ndefined; j++) {
1860                 if (!strcmp(fspecp->lvars[i], ncp->vars.value[j]->name->cp)) {
1861                     fspecp->varp[i] = ncp->vars.value[j];
1862                     fspecp->varids[i] = j;
1863                     break;
1864                 }
1865             }
1866             if (j == ncp->vars.ndefined) {
1867                 printf("Error: variable %s not found\n",fspecp->lvars[i]);
1868                 return 1;
1869             }
1870         }
1871     }
1872 
1873     /* print file name and format */
1874     printf("netcdf %s {\n",filename);
1875     printf("// file format: CDF-%d\n",ncp->flags);
1876     printf("\n");
1877 
1878     /* print file header size and extent */
1879     printf("file header:\n");
1880     printf("\tsize   = %lld bytes\n",ncp->xsz);
1881     printf("\textent = %lld bytes\n",ncp->begin_var);
1882 
1883     /* print dimensions */
1884     if (ncp->dims.ndefined > 0) printf("\ndimensions:\n");
1885     for (i=0; i<ncp->dims.ndefined; i++) {
1886         long long size;
1887         size = ncp->dims.value[i]->size;
1888         printf("\t%s = ",ncp->dims.value[i]->name->cp);
1889         if (size == NC_UNLIMITED)
1890             printf("UNLIMITED // (%lld currently)\n",ncp->numrecs);
1891         else
1892             printf("%lld\n",size);
1893     }
1894 
1895     if (fspecp->nlvars == 0) { /* print all variables */
1896         fspecp = (struct fspec*) malloc(sizeof(struct fspec));
1897         MALLOC_CHECK(fspecp)
1898         fspecp->varp = (NC_var**) malloc(ncp->vars.ndefined * sizeof(NC_var*));
1899         MALLOC_CHECK(fspecp->varp)
1900         fspecp->varids = (int*) malloc(ncp->vars.ndefined * sizeof(int));
1901         MALLOC_CHECK(fspecp->varids)
1902         fspecp->nlvars = ncp->vars.ndefined;
1903         fspecp->lvars = NULL;
1904         for (i=0; i<ncp->vars.ndefined; i++) {
1905             fspecp->varp[i] = ncp->vars.value[i];
1906             fspecp->varids[i] = i;
1907         }
1908     }
1909 
1910     /* find how many variables are record variables */
1911     int num_rec_vars = 0;
1912     int num_fix_vars = 0;
1913     for (i=0; i<fspecp->nlvars; i++) {
1914         if (IS_RECVAR(fspecp->varp[i])) num_rec_vars++;
1915         else                            num_fix_vars++;
1916     }
1917 
1918     int last_fix_varid = -1;
1919     for (i=0; i<ncp->vars.ndefined; i++) {
1920         if (IS_RECVAR(ncp->vars.value[i])) continue;
1921         last_fix_varid = i;
1922     }
1923 
1924     int first_rec_varid = -1;
1925     for (i=0; i<ncp->vars.ndefined; i++) {
1926         if (IS_RECVAR(ncp->vars.value[i])) {
1927             first_rec_varid = i;
1928             break;
1929         }
1930     }
1931 
1932     /* print fixed-size variables first */
1933     if (num_fix_vars) printf("\nfixed-size variables:\n");
1934     for (i=0; i<fspecp->nlvars; i++) {
1935         int j, ndims;
1936         char type_str[16], str[1024], line[1024];
1937         long long size;
1938         NC_var *varp = fspecp->varp[i];
1939 
1940         if (IS_RECVAR(varp)) continue;
1941 
1942         /* calculate the size in bytes of this variable */
1943         size = type_size(varp->type);
1944         if (varp->ndims) size *= varp->dsizes[0];
1945 
1946         line[0]='\0';
1947         sprintf(type_str,"%-6s", type_name(varp->type));
1948         sprintf(line,"%s", varp->name->cp);
1949         ndims = varp->ndims;
1950         if (ndims > 0) strcat(line,"(");
1951         for (j=0; j<ndims; j++) {
1952             NC_dim *dimp = ncp->dims.value[varp->dimids[j]];
1953             if (dimp->size == NC_UNLIMITED)
1954                 size *= ncp->numrecs;
1955             sprintf(str, "%s%s", dimp->name->cp, j < ndims-1 ? ", " : ")");
1956             strcat(line, str);
1957         }
1958 
1959         /* print the data type, variable name, and its dimensions */
1960         printf("\t%6s %s:\n", type_str, line);
1961 
1962         /* print the starting and ending file offset of this variable */
1963         printf("\t       start file offset =%12lld\n", varp->begin);
1964         printf("\t       end   file offset =%12lld\n", varp->begin+size);
1965 
1966         /* print variable size in bytes */
1967         if (print_var_size)
1968             printf("\t       size in bytes     =%12lld\n", size);
1969 
1970         /* print the gap between the begin of this variable from the end of
1971          * variable immediately before it */
1972         if (print_gap) {
1973             NC_var *prev=NULL;
1974             for (j=fspecp->varids[i]-1; j>=0; j--) {
1975                 /* search for the previous fixed-size variable */
1976                 if (!IS_RECVAR(ncp->vars.value[j])) {
1977                     prev = ncp->vars.value[j];
1978                     break;
1979                 }
1980             }
1981             if (fspecp->varids[i] == 0 || prev == NULL) {
1982                 /* first defined fixed-size variable */
1983                 printf("\t       gap from prev var =%12lld\n",
1984                        varp->begin - ncp->xsz);
1985             }
1986             else {
1987                 /* not the first fixed-size variable */
1988                 long long prev_end = prev->begin;
1989                 if (prev->ndims == 0)
1990                     prev_end += type_size(prev->type);
1991                 else
1992                     prev_end += type_size(prev->type) * prev->dsizes[0];
1993                 printf("\t       gap from prev var =%12lld\n",
1994                        varp->begin - prev_end);
1995             }
1996         }
1997     }
1998 
1999     /* print record variables */
2000     if (num_rec_vars) printf("\nrecord variables:\n");
2001     for (i=0; i<fspecp->nlvars; i++) {
2002         int j, ndims;
2003         char type_str[16], str[1024], line[1024];
2004         long long var_begin, var_end, size, numrecs;
2005         NC_var *varp = fspecp->varp[i];
2006 
2007         if (!IS_RECVAR(varp)) continue;
2008 
2009         /* calculate the size in bytes of this variable */
2010         size = type_size(varp->type);
2011         if (varp->ndims) size *= varp->dsizes[0];
2012 
2013         line[0]='\0';
2014         sprintf(type_str,"%-6s", type_name(varp->type));
2015         sprintf(line,"%s", varp->name->cp);
2016         ndims = varp->ndims;
2017         if (ndims > 0) strcat(line,"(");
2018         for (j=0; j<ndims; j++) {
2019             NC_dim *dimp = ncp->dims.value[varp->dimids[j]];
2020             sprintf(str, "%s%s", dimp->name->cp, j < ndims-1 ? ", " : ")");
2021             strcat(line, str);
2022         }
2023 
2024         /* print the data type, variable name, and its dimensions */
2025         printf("\t%6s %s:\n", type_str, line);
2026 
2027         /* print the starting and ending file offset of this variable */
2028         numrecs = ncp->numrecs;
2029         var_begin = varp->begin;
2030         var_end   = varp->begin + size;
2031         if (print_all_rec == 0) numrecs = 1;
2032         for (j=0; j<numrecs; j++) {
2033             char rec_num[32];
2034                  if (j % 10 == 1) sprintf(rec_num, "%dst", j);
2035             else if (j % 10 == 2) sprintf(rec_num, "%dnd", j);
2036             else if (j % 10 == 3) sprintf(rec_num, "%drd", j);
2037             else                  sprintf(rec_num, "%dth", j);
2038             printf("\t       start file offset =%12lld    (%s record)\n", var_begin,rec_num);
2039             printf("\t       end   file offset =%12lld    (%s record)\n", var_end,rec_num);
2040             var_begin += ncp->recsize;
2041             var_end   += ncp->recsize;
2042         }
2043 
2044         /* print variable size in bytes (one record only)
2045          *   size *= ncp->numrecs;   (if for all records)
2046          */
2047         if (print_var_size)
2048             printf("\t       size in bytes     =%12lld    (of one record)\n", size);
2049 
2050         /* print the gap between the begin of this variable from the end of
2051          * variable immediately before it */
2052         if (print_gap) {
2053             NC_var *prev=NULL;
2054             long long prev_end;
2055             for (j=fspecp->varids[i]-1; j>=0; j--) {
2056                 /* search for the previous record variable */
2057                 if (IS_RECVAR(ncp->vars.value[j])) {
2058                     prev = ncp->vars.value[j];
2059                     break;
2060                 }
2061             }
2062             if (fspecp->varids[i] == 0 && last_fix_varid == -1) {
2063                 /* first record variable and no fixed-size variable */
2064                 printf("\t       gap from prev var =%12lld\n",
2065                        varp->begin - ncp->xsz);
2066             }
2067             else if (fspecp->varids[i] == first_rec_varid) {
2068                 /* first record variable and there are fixed-size variables */
2069                 prev = ncp->vars.value[last_fix_varid];
2070                 prev_end = prev->begin;
2071                 if (prev->ndims == 0)
2072                     prev_end += type_size(prev->type);
2073                 else
2074                     prev_end += type_size(prev->type) * prev->dsizes[0];
2075                 printf("\t       gap from prev var =%12lld\n",
2076                        varp->begin - prev_end);
2077             }
2078             else {
2079                 /* not the first record variable */
2080                 prev_end = prev->begin;
2081                 if (prev->ndims == 0)
2082                     prev_end += type_size(prev->type);
2083                 else
2084                     prev_end += type_size(prev->type) * prev->dsizes[0];
2085                 printf("\t       gap from prev var =%12lld\n",
2086                        varp->begin - prev_end);
2087             }
2088         }
2089     }
2090     printf("}\n");
2091 
2092     free(fspecp->varp);
2093     if (fspecp->lvars != NULL) {
2094         for (i=0; i<fspecp->nlvars; i++)
2095             free(fspecp->lvars[i]);
2096         free(fspecp->lvars);
2097     }
2098     free(fspecp->varids);
2099     free(fspecp);
2100     ncmpii_free_NC(ncp);
2101     free(ncp);
2102     close(fd);
2103 
2104     return 0;
2105 }
2106 
2107