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