1 /*
2 * Copyright 1996, University Corporation for Atmospheric Research
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5
6 #if HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <assert.h>
14 #include "nc3internal.h"
15 #include "rnd.h"
16 #include "ncx.h"
17
18 /*
19 * This module defines the external representation
20 * of the "header" of a netcdf version one file and
21 * the version two variant that uses 64-bit file
22 * offsets instead of the 32-bit file offsets in version
23 * one files.
24 * For each of the components of the NC structure,
25 * There are (static) ncx_len_XXX(), v1h_put_XXX()
26 * and v1h_get_XXX() functions. These define the
27 * external representation of the components.
28 * The exported entry points for the whole NC structure
29 * are built up from these.
30 */
31
32
33 /*
34 * "magic number" at beginning of file: 0x43444601 (big endian)
35 * assert(sizeof(ncmagic) % X_ALIGN == 0);
36 */
37 static const schar ncmagic[] = {'C', 'D', 'F', 0x02};
38 static const schar ncmagic1[] = {'C', 'D', 'F', 0x01};
39 static const schar ncmagic5[] = {'C', 'D', 'F', 0x05};
40
41 /*
42 * v1hs == "Version 1 Header Stream"
43 *
44 * The netcdf file version 1 header is
45 * of unknown and potentially unlimited size.
46 * So, we don't know how much to get() on
47 * the initial read. We build a stream, 'v1hs'
48 * on top of ncio to do the header get.
49 */
50 typedef struct v1hs {
51 ncio *nciop;
52 off_t offset; /* argument to nciop->get() */
53 size_t extent; /* argument to nciop->get() */
54 int flags; /* set to RGN_WRITE for write */
55 int version; /* format variant: NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET or NC_FORMAT_CDF5 */
56 void *base; /* beginning of current buffer */
57 void *pos; /* current position in buffer */
58 void *end; /* end of current buffer = base + extent */
59 } v1hs;
60
61
62 /*
63 * Release the stream, invalidate buffer
64 */
65 static int
rel_v1hs(v1hs * gsp)66 rel_v1hs(v1hs *gsp)
67 {
68 int status;
69 if(gsp->offset == OFF_NONE || gsp->base == NULL)
70 return NC_NOERR;
71 status = ncio_rel(gsp->nciop, gsp->offset,
72 gsp->flags == RGN_WRITE ? RGN_MODIFIED : 0);
73 gsp->end = NULL;
74 gsp->pos = NULL;
75 gsp->base = NULL;
76 return status;
77 }
78
79
80 /*
81 * Release the current chunk and get the next one.
82 * Also used for initialization when gsp->base == NULL.
83 */
84 static int
fault_v1hs(v1hs * gsp,size_t extent)85 fault_v1hs(v1hs *gsp, size_t extent)
86 {
87 int status;
88
89 if(gsp->base != NULL)
90 {
91 const ptrdiff_t incr = (char *)gsp->pos - (char *)gsp->base;
92 status = rel_v1hs(gsp);
93 if(status)
94 return status;
95 gsp->offset += incr;
96 }
97
98 if(extent > gsp->extent)
99 gsp->extent = extent;
100
101 status = ncio_get(gsp->nciop,
102 gsp->offset, gsp->extent,
103 gsp->flags, &gsp->base);
104 if(status)
105 return status;
106
107 gsp->pos = gsp->base;
108
109 gsp->end = (char *)gsp->base + gsp->extent;
110 return NC_NOERR;
111 }
112
113
114 /*
115 * Ensure that 'nextread' bytes are available.
116 */
117 static int
check_v1hs(v1hs * gsp,size_t nextread)118 check_v1hs(v1hs *gsp, size_t nextread)
119 {
120
121 #if 0 /* DEBUG */
122 fprintf(stderr, "nextread %lu, remaining %lu\n",
123 (unsigned long)nextread,
124 (unsigned long)((char *)gsp->end - (char *)gsp->pos));
125 #endif
126 if((char *)gsp->pos + nextread <= (char *)gsp->end)
127 return NC_NOERR;
128
129 return fault_v1hs(gsp, nextread);
130 }
131
132 /* End v1hs */
133
134 /* Write a size_t to the header */
135 static int
v1h_put_size_t(v1hs * psp,const size_t * sp)136 v1h_put_size_t(v1hs *psp, const size_t *sp)
137 {
138 int status;
139 if (psp->version == 5) /* all integers in CDF-5 are 64 bits */
140 status = check_v1hs(psp, X_SIZEOF_INT64);
141 else
142 status = check_v1hs(psp, X_SIZEOF_SIZE_T);
143 if(status != NC_NOERR)
144 return status;
145 if (psp->version == 5) {
146 unsigned long long tmp = (unsigned long long) (*sp);
147 return ncx_put_uint64(&psp->pos, tmp);
148 }
149 else
150 return ncx_put_size_t(&psp->pos, sp);
151 }
152
153 /* Read a size_t from the header */
154 static int
v1h_get_size_t(v1hs * gsp,size_t * sp)155 v1h_get_size_t(v1hs *gsp, size_t *sp)
156 {
157 int status;
158 if (gsp->version == 5) /* all integers in CDF-5 are 64 bits */
159 status = check_v1hs(gsp, X_SIZEOF_INT64);
160 else
161 status = check_v1hs(gsp, X_SIZEOF_SIZE_T);
162 if(status != NC_NOERR)
163 return status;
164 if (gsp->version == 5) {
165 unsigned long long tmp=0;
166 status = ncx_get_uint64((const void **)(&gsp->pos), &tmp);
167 *sp = (size_t)tmp;
168 return status;
169 }
170 else
171 return ncx_get_size_t((const void **)(&gsp->pos), sp);
172 }
173
174 /* Begin nc_type */
175
176 #define X_SIZEOF_NC_TYPE X_SIZEOF_INT
177
178 /* Write a nc_type to the header */
179 static int
v1h_put_nc_type(v1hs * psp,const nc_type * typep)180 v1h_put_nc_type(v1hs *psp, const nc_type *typep)
181 {
182 const unsigned int itype = (unsigned int) *typep;
183 int status = check_v1hs(psp, X_SIZEOF_INT);
184 if(status != NC_NOERR) return status;
185 status = ncx_put_uint32(&psp->pos, itype);
186 return status;
187 }
188
189
190 /* Read a nc_type from the header */
191 static int
v1h_get_nc_type(v1hs * gsp,nc_type * typep)192 v1h_get_nc_type(v1hs *gsp, nc_type *typep)
193 {
194 unsigned int type = 0;
195 int status = check_v1hs(gsp, X_SIZEOF_INT);
196 if(status != NC_NOERR) return status;
197 status = ncx_get_uint32((const void**)(&gsp->pos), &type);
198 if(status != NC_NOERR)
199 return status;
200
201 assert(type == NC_BYTE
202 || type == NC_CHAR
203 || type == NC_SHORT
204 || type == NC_INT
205 || type == NC_FLOAT
206 || type == NC_DOUBLE
207 || type == NC_UBYTE
208 || type == NC_USHORT
209 || type == NC_UINT
210 || type == NC_INT64
211 || type == NC_UINT64
212 || type == NC_STRING);
213
214 /* else */
215 *typep = (nc_type) type;
216
217 return NC_NOERR;
218 }
219
220 /* End nc_type */
221 /* Begin NCtype (internal tags) */
222
223 #define X_SIZEOF_NCTYPE X_SIZEOF_INT
224
225 /* Write a NCtype to the header */
226 static int
v1h_put_NCtype(v1hs * psp,NCtype type)227 v1h_put_NCtype(v1hs *psp, NCtype type)
228 {
229 const unsigned int itype = (unsigned int) type;
230 int status = check_v1hs(psp, X_SIZEOF_INT);
231 if(status != NC_NOERR) return status;
232 status = ncx_put_uint32(&psp->pos, itype);
233 return status;
234 }
235
236 /* Read a NCtype from the header */
237 static int
v1h_get_NCtype(v1hs * gsp,NCtype * typep)238 v1h_get_NCtype(v1hs *gsp, NCtype *typep)
239 {
240 unsigned int type = 0;
241 int status = check_v1hs(gsp, X_SIZEOF_INT);
242 if(status != NC_NOERR) return status;
243 status = ncx_get_uint32((const void**)(&gsp->pos), &type);
244 if(status != NC_NOERR) return status;
245 /* else */
246 *typep = (NCtype) type;
247 return NC_NOERR;
248 }
249
250 /* End NCtype */
251 /* Begin NC_string */
252
253 /*
254 * How much space will the xdr'd string take.
255 * Formerly
256 NC_xlen_string(cdfstr)
257 */
258 static size_t
ncx_len_NC_string(const NC_string * ncstrp,int version)259 ncx_len_NC_string(const NC_string *ncstrp, int version)
260 {
261 size_t sz = (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_INT; /* nchars */
262
263 assert(ncstrp != NULL);
264
265 if(ncstrp->nchars != 0)
266 {
267 #if 0
268 assert(ncstrp->nchars % X_ALIGN == 0);
269 sz += ncstrp->nchars;
270 #else
271 sz += _RNDUP(ncstrp->nchars, X_ALIGN);
272 #endif
273 }
274 return sz;
275 }
276
277
278 /* Write a NC_string to the header */
279 static int
v1h_put_NC_string(v1hs * psp,const NC_string * ncstrp)280 v1h_put_NC_string(v1hs *psp, const NC_string *ncstrp)
281 {
282 int status;
283
284 #if 0
285 assert(ncstrp->nchars % X_ALIGN == 0);
286 #endif
287
288 status = v1h_put_size_t(psp, &ncstrp->nchars);
289 if(status != NC_NOERR)
290 return status;
291 status = check_v1hs(psp, _RNDUP(ncstrp->nchars, X_ALIGN));
292 if(status != NC_NOERR)
293 return status;
294 status = ncx_pad_putn_text(&psp->pos, ncstrp->nchars, ncstrp->cp);
295 if(status != NC_NOERR)
296 return status;
297
298 return NC_NOERR;
299 }
300
301
302 /* Read a NC_string from the header */
303 static int
v1h_get_NC_string(v1hs * gsp,NC_string ** ncstrpp)304 v1h_get_NC_string(v1hs *gsp, NC_string **ncstrpp)
305 {
306 int status = 0;
307 size_t nchars = 0;
308 NC_string *ncstrp = NULL;
309 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
310 size_t padding = 0;
311 #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
312
313 status = v1h_get_size_t(gsp, &nchars);
314 if(status != NC_NOERR)
315 return status;
316
317 ncstrp = new_NC_string(nchars, NULL);
318 if(ncstrp == NULL)
319 {
320 return NC_ENOMEM;
321 }
322
323 #if 0
324 /* assert(ncstrp->nchars == nchars || ncstrp->nchars - nchars < X_ALIGN); */
325 assert(ncstrp->nchars % X_ALIGN == 0);
326 status = check_v1hs(gsp, ncstrp->nchars);
327 #else
328
329 status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN));
330 #endif
331 if(status != NC_NOERR)
332 goto unwind_alloc;
333
334 status = ncx_pad_getn_text((const void **)(&gsp->pos),
335 nchars, ncstrp->cp);
336 if(status != NC_NOERR)
337 goto unwind_alloc;
338
339 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
340 padding = _RNDUP(X_SIZEOF_CHAR * ncstrp->nchars, X_ALIGN)
341 - X_SIZEOF_CHAR * ncstrp->nchars;
342
343 if (padding > 0) {
344 /* CDF specification: Header padding uses null (\x00) bytes. */
345 char pad[X_ALIGN-1];
346 memset(pad, 0, X_ALIGN-1);
347 if (memcmp((char*)gsp->pos-padding, pad, padding) != 0) {
348 free_NC_string(ncstrp);
349 return NC_ENULLPAD;
350 }
351 }
352 #endif
353
354 *ncstrpp = ncstrp;
355
356 return NC_NOERR;
357
358 unwind_alloc:
359 free_NC_string(ncstrp);
360 return status;
361 }
362
363 /* End NC_string */
364 /* Begin NC_dim */
365
366 /*
367 * How much space will the xdr'd dim take.
368 * Formerly
369 NC_xlen_dim(dpp)
370 */
371 static size_t
ncx_len_NC_dim(const NC_dim * dimp,int version)372 ncx_len_NC_dim(const NC_dim *dimp, int version)
373 {
374 size_t sz;
375
376 assert(dimp != NULL);
377
378 sz = ncx_len_NC_string(dimp->name, version);
379 sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T;
380
381 return(sz);
382 }
383
384
385 /* Write a NC_dim to the header */
386 static int
v1h_put_NC_dim(v1hs * psp,const NC_dim * dimp)387 v1h_put_NC_dim(v1hs *psp, const NC_dim *dimp)
388 {
389 int status;
390
391 status = v1h_put_NC_string(psp, dimp->name);
392 if(status != NC_NOERR)
393 return status;
394
395 status = v1h_put_size_t(psp, &dimp->size);
396 if(status != NC_NOERR)
397 return status;
398
399 return NC_NOERR;
400 }
401
402 /* Read a NC_dim from the header */
403 static int
v1h_get_NC_dim(v1hs * gsp,NC_dim ** dimpp)404 v1h_get_NC_dim(v1hs *gsp, NC_dim **dimpp)
405 {
406 int status;
407 NC_string *ncstrp;
408 NC_dim *dimp;
409
410 status = v1h_get_NC_string(gsp, &ncstrp);
411 if(status != NC_NOERR)
412 return status;
413
414 dimp = new_x_NC_dim(ncstrp);
415 if(dimp == NULL)
416 {
417 status = NC_ENOMEM;
418 goto unwind_name;
419 }
420
421 status = v1h_get_size_t(gsp, &dimp->size);
422 if(status != NC_NOERR)
423 {
424 free_NC_dim(dimp); /* frees name */
425 return status;
426 }
427
428 *dimpp = dimp;
429
430 return NC_NOERR;
431
432 unwind_name:
433 free_NC_string(ncstrp);
434 return status;
435 }
436
437
438 /* How much space in the header is required for this NC_dimarray? */
439 static size_t
ncx_len_NC_dimarray(const NC_dimarray * ncap,int version)440 ncx_len_NC_dimarray(const NC_dimarray *ncap, int version)
441 {
442 size_t xlen = X_SIZEOF_NCTYPE; /* type */
443 xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
444 if(ncap == NULL)
445 return xlen;
446 /* else */
447 {
448 const NC_dim **dpp = (const NC_dim **)ncap->value;
449 const NC_dim *const *const end = &dpp[ncap->nelems];
450 for( /*NADA*/; dpp < end; dpp++)
451 {
452 xlen += ncx_len_NC_dim(*dpp,version);
453 }
454 }
455 return xlen;
456 }
457
458
459 /* Write a NC_dimarray to the header */
460 static int
v1h_put_NC_dimarray(v1hs * psp,const NC_dimarray * ncap)461 v1h_put_NC_dimarray(v1hs *psp, const NC_dimarray *ncap)
462 {
463 int status;
464
465 assert(psp != NULL);
466
467 if(ncap == NULL
468 #if 1
469 /* Backward:
470 * This clause is for 'byte for byte'
471 * backward compatibility.
472 * Strickly speaking, it is 'bug for bug'.
473 */
474 || ncap->nelems == 0
475 #endif
476 )
477 {
478 /*
479 * Handle empty netcdf
480 */
481 const size_t nosz = 0;
482
483 status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
484 if(status != NC_NOERR)
485 return status;
486 status = v1h_put_size_t(psp, &nosz);
487 if(status != NC_NOERR)
488 return status;
489 return NC_NOERR;
490 }
491 /* else */
492
493 status = v1h_put_NCtype(psp, NC_DIMENSION);
494 if(status != NC_NOERR)
495 return status;
496 status = v1h_put_size_t(psp, &ncap->nelems);
497 if(status != NC_NOERR)
498 return status;
499
500 {
501 const NC_dim **dpp = (const NC_dim **)ncap->value;
502 const NC_dim *const *const end = &dpp[ncap->nelems];
503 for( /*NADA*/; dpp < end; dpp++)
504 {
505 status = v1h_put_NC_dim(psp, *dpp);
506 if(status)
507 return status;
508 }
509 }
510 return NC_NOERR;
511 }
512
513
514 /* Read a NC_dimarray from the header */
515 static int
v1h_get_NC_dimarray(v1hs * gsp,NC_dimarray * ncap)516 v1h_get_NC_dimarray(v1hs *gsp, NC_dimarray *ncap)
517 {
518 int status;
519 NCtype type = NC_UNSPECIFIED;
520
521 assert(gsp != NULL && gsp->pos != NULL);
522 assert(ncap != NULL);
523 assert(ncap->value == NULL);
524
525 status = v1h_get_NCtype(gsp, &type);
526 if(status != NC_NOERR)
527 return status;
528
529 status = v1h_get_size_t(gsp, &ncap->nelems);
530 if(status != NC_NOERR)
531 return status;
532
533 if(ncap->nelems == 0)
534 return NC_NOERR;
535 /* else */
536 if(type != NC_DIMENSION)
537 return EINVAL;
538
539 ncap->value = (NC_dim **) calloc(1,ncap->nelems * sizeof(NC_dim *));
540 if(ncap->value == NULL)
541 return NC_ENOMEM;
542 ncap->nalloc = ncap->nelems;
543
544 ncap->hashmap = NC_hashmapnew(ncap->nelems);
545
546 {
547 NC_dim **dpp = ncap->value;
548 NC_dim *const *const end = &dpp[ncap->nelems];
549 for( /*NADA*/; dpp < end; dpp++)
550 {
551 status = v1h_get_NC_dim(gsp, dpp);
552 if(status)
553 {
554 ncap->nelems = (size_t)(dpp - ncap->value);
555 free_NC_dimarrayV(ncap);
556 return status;
557 }
558 {
559 int dimid = (size_t)(dpp - ncap->value);
560 NC_hashmapadd(ncap->hashmap, (uintptr_t)dimid, (*dpp)->name->cp,strlen((*dpp)->name->cp));
561 }
562 }
563 }
564
565 return NC_NOERR;
566 }
567
568
569 /* End NC_dim */
570 /* Begin NC_attr */
571
572
573 /*
574 * How much space will 'attrp' take in external representation?
575 * Formerly
576 NC_xlen_attr(app)
577 */
578 static size_t
ncx_len_NC_attr(const NC_attr * attrp,int version)579 ncx_len_NC_attr(const NC_attr *attrp, int version)
580 {
581 size_t sz;
582
583 assert(attrp != NULL);
584
585 sz = ncx_len_NC_string(attrp->name, version);
586 sz += X_SIZEOF_NC_TYPE; /* type */
587 sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */
588 sz += attrp->xsz;
589
590 return(sz);
591 }
592
593
594 #undef MIN
595 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
596
597 /*----< ncmpix_len_nctype() >------------------------------------------------*/
598 /* return the length of external data type */
599 static int
ncmpix_len_nctype(nc_type type)600 ncmpix_len_nctype(nc_type type) {
601 switch(type) {
602 case NC_BYTE:
603 case NC_CHAR:
604 case NC_UBYTE: return X_SIZEOF_CHAR;
605 case NC_SHORT: return X_SIZEOF_SHORT;
606 case NC_USHORT: return X_SIZEOF_USHORT;
607 case NC_INT: return X_SIZEOF_INT;
608 case NC_UINT: return X_SIZEOF_UINT;
609 case NC_FLOAT: return X_SIZEOF_FLOAT;
610 case NC_DOUBLE: return X_SIZEOF_DOUBLE;
611 case NC_INT64: return X_SIZEOF_INT64;
612 case NC_UINT64: return X_SIZEOF_UINT64;
613 default: fprintf(stderr,"ncmpix_len_nctype bad type %d\n",type);
614 assert(0);
615 }
616 return 0;
617 }
618
619 /*
620 * Put the values of an attribute
621 * The loop is necessary since attrp->nelems
622 * could potentially be quite large.
623 */
624 static int
v1h_put_NC_attrV(v1hs * psp,const NC_attr * attrp)625 v1h_put_NC_attrV(v1hs *psp, const NC_attr *attrp)
626 {
627 int status = 0;
628 const size_t perchunk = psp->extent;
629 size_t remaining = attrp->xsz;
630 void *value = attrp->xvalue;
631 size_t nbytes = 0, padding = 0;
632
633 assert(psp->extent % X_ALIGN == 0);
634
635 do {
636 nbytes = MIN(perchunk, remaining);
637
638 status = check_v1hs(psp, nbytes);
639 if(status != NC_NOERR)
640 return status;
641
642 (void) memcpy(psp->pos, value, nbytes);
643
644 psp->pos = (void *)((char *)psp->pos + nbytes);
645 value = (void *)((char *)value + nbytes);
646 remaining -= nbytes;
647
648 } while(remaining != 0);
649
650
651 padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
652 if (padding > 0) {
653 /* CDF specification: Header padding uses null (\x00) bytes. */
654 memset((char*)psp->pos-padding, 0, padding);
655 }
656
657 return NC_NOERR;
658 }
659
660 /* Write a NC_attr to the header */
661 static int
v1h_put_NC_attr(v1hs * psp,const NC_attr * attrp)662 v1h_put_NC_attr(v1hs *psp, const NC_attr *attrp)
663 {
664 int status;
665
666 status = v1h_put_NC_string(psp, attrp->name);
667 if(status != NC_NOERR)
668 return status;
669
670 status = v1h_put_nc_type(psp, &attrp->type);
671 if(status != NC_NOERR)
672 return status;
673
674 status = v1h_put_size_t(psp, &attrp->nelems);
675 if(status != NC_NOERR)
676 return status;
677
678 status = v1h_put_NC_attrV(psp, attrp);
679 if(status != NC_NOERR)
680 return status;
681
682 return NC_NOERR;
683 }
684
685
686 /*
687 * Get the values of an attribute
688 * The loop is necessary since attrp->nelems
689 * could potentially be quite large.
690 */
691 static int
v1h_get_NC_attrV(v1hs * gsp,NC_attr * attrp)692 v1h_get_NC_attrV(v1hs *gsp, NC_attr *attrp)
693 {
694 int status;
695 const size_t perchunk = gsp->extent;
696 size_t remaining = attrp->xsz;
697 void *value = attrp->xvalue;
698 size_t nget;
699 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
700 size_t padding;
701 #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
702
703 do {
704 nget = MIN(perchunk, remaining);
705
706 status = check_v1hs(gsp, nget);
707 if(status != NC_NOERR)
708 return status;
709
710 (void) memcpy(value, gsp->pos, nget);
711 gsp->pos = (void*)((unsigned char *)gsp->pos + nget);
712
713 value = (void *)((signed char *)value + nget);
714
715 remaining -= nget;
716
717 } while(remaining != 0);
718
719 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
720 padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
721 if (padding > 0) {
722 /* CDF specification: Header padding uses null (\x00) bytes. */
723 char pad[X_ALIGN-1];
724 memset(pad, 0, X_ALIGN-1);
725 if (memcmp((char*)gsp->pos-padding, pad, (size_t)padding) != 0)
726 return NC_ENULLPAD;
727 }
728 #endif
729
730 return NC_NOERR;
731 }
732
733
734 /* Read a NC_attr from the header */
735 static int
v1h_get_NC_attr(v1hs * gsp,NC_attr ** attrpp)736 v1h_get_NC_attr(v1hs *gsp, NC_attr **attrpp)
737 {
738 NC_string *strp;
739 int status;
740 nc_type type;
741 size_t nelems;
742 NC_attr *attrp;
743
744 status = v1h_get_NC_string(gsp, &strp);
745 if(status != NC_NOERR)
746 return status;
747
748 status = v1h_get_nc_type(gsp, &type);
749 if(status != NC_NOERR)
750 goto unwind_name;
751
752 status = v1h_get_size_t(gsp, &nelems);
753 if(status != NC_NOERR)
754 goto unwind_name;
755
756 attrp = new_x_NC_attr(strp, type, nelems);
757 if(attrp == NULL)
758 {
759 status = NC_ENOMEM;
760 goto unwind_name;
761 }
762
763 status = v1h_get_NC_attrV(gsp, attrp);
764 if(status != NC_NOERR)
765 {
766 free_NC_attr(attrp); /* frees strp */
767 return status;
768 }
769
770 *attrpp = attrp;
771
772 return NC_NOERR;
773
774 unwind_name:
775 free_NC_string(strp);
776 return status;
777 }
778
779
780 /* How much space in the header is required for this NC_attrarray? */
781 static size_t
ncx_len_NC_attrarray(const NC_attrarray * ncap,int version)782 ncx_len_NC_attrarray(const NC_attrarray *ncap, int version)
783 {
784 size_t xlen = X_SIZEOF_NCTYPE; /* type */
785 xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
786 if(ncap == NULL)
787 return xlen;
788 /* else */
789 {
790 const NC_attr **app = (const NC_attr **)ncap->value;
791 const NC_attr *const *const end = &app[ncap->nelems];
792 for( /*NADA*/; app < end; app++)
793 {
794 xlen += ncx_len_NC_attr(*app,version);
795 }
796 }
797 return xlen;
798 }
799
800
801 /* Write a NC_attrarray to the header */
802 static int
v1h_put_NC_attrarray(v1hs * psp,const NC_attrarray * ncap)803 v1h_put_NC_attrarray(v1hs *psp, const NC_attrarray *ncap)
804 {
805 int status;
806
807 assert(psp != NULL);
808
809 if(ncap == NULL
810 #if 1
811 /* Backward:
812 * This clause is for 'byte for byte'
813 * backward compatibility.
814 * Strickly speaking, it is 'bug for bug'.
815 */
816 || ncap->nelems == 0
817 #endif
818 )
819 {
820 /*
821 * Handle empty netcdf
822 */
823 const size_t nosz = 0;
824
825 status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
826 if(status != NC_NOERR)
827 return status;
828 status = v1h_put_size_t(psp, &nosz);
829 if(status != NC_NOERR)
830 return status;
831 return NC_NOERR;
832 }
833 /* else */
834
835 status = v1h_put_NCtype(psp, NC_ATTRIBUTE);
836 if(status != NC_NOERR)
837 return status;
838 status = v1h_put_size_t(psp, &ncap->nelems);
839 if(status != NC_NOERR)
840 return status;
841
842 {
843 const NC_attr **app = (const NC_attr **)ncap->value;
844 const NC_attr *const *const end = &app[ncap->nelems];
845 for( /*NADA*/; app < end; app++)
846 {
847 status = v1h_put_NC_attr(psp, *app);
848 if(status)
849 return status;
850 }
851 }
852 return NC_NOERR;
853 }
854
855
856 /* Read a NC_attrarray from the header */
857 static int
v1h_get_NC_attrarray(v1hs * gsp,NC_attrarray * ncap)858 v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap)
859 {
860 int status;
861 NCtype type = NC_UNSPECIFIED;
862
863 assert(gsp != NULL && gsp->pos != NULL);
864 assert(ncap != NULL);
865 assert(ncap->value == NULL);
866
867 status = v1h_get_NCtype(gsp, &type);
868 if(status != NC_NOERR)
869 return status;
870 status = v1h_get_size_t(gsp, &ncap->nelems);
871 if(status != NC_NOERR)
872 return status;
873
874 if(ncap->nelems == 0)
875 return NC_NOERR;
876 /* else */
877 if(type != NC_ATTRIBUTE)
878 return EINVAL;
879
880 ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));
881 if(ncap->value == NULL)
882 return NC_ENOMEM;
883 ncap->nalloc = ncap->nelems;
884
885 {
886 NC_attr **app = ncap->value;
887 NC_attr *const *const end = &app[ncap->nelems];
888 for( /*NADA*/; app < end; app++)
889 {
890 status = v1h_get_NC_attr(gsp, app);
891 if(status)
892 {
893 ncap->nelems = (size_t)(app - ncap->value);
894 free_NC_attrarrayV(ncap);
895 return status;
896 }
897 }
898 }
899
900 return NC_NOERR;
901 }
902
903 /* End NC_attr */
904 /* Begin NC_var */
905
906 /*
907 * How much space will the xdr'd var take.
908 * Formerly
909 NC_xlen_var(vpp)
910 */
911 static size_t
ncx_len_NC_var(const NC_var * varp,size_t sizeof_off_t,int version)912 ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version)
913 {
914 size_t sz;
915
916 assert(varp != NULL);
917 assert(sizeof_off_t != 0);
918
919 sz = ncx_len_NC_string(varp->name, version);
920 if (version == 5) {
921 sz += X_SIZEOF_INT64; /* ndims */
922 sz += ncx_len_int64(varp->ndims); /* dimids */
923 }
924 else {
925 sz += X_SIZEOF_SIZE_T; /* ndims */
926 sz += ncx_len_int(varp->ndims); /* dimids */
927 }
928 sz += ncx_len_NC_attrarray(&varp->attrs, version);
929 sz += X_SIZEOF_NC_TYPE; /* nc_type */
930 sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* vsize */
931 sz += sizeof_off_t; /* begin */
932
933 return(sz);
934 }
935
936
937 /* Write a NC_var to the header */
938 static int
v1h_put_NC_var(v1hs * psp,const NC_var * varp)939 v1h_put_NC_var(v1hs *psp, const NC_var *varp)
940 {
941 int status;
942 size_t vsize;
943
944 status = v1h_put_NC_string(psp, varp->name);
945 if(status != NC_NOERR)
946 return status;
947
948 status = v1h_put_size_t(psp, &varp->ndims);
949 if(status != NC_NOERR)
950 return status;
951
952 if (psp->version == 5) {
953 status = check_v1hs(psp, ncx_len_int64(varp->ndims));
954 if(status != NC_NOERR)
955 return status;
956 status = ncx_putn_longlong_int(&psp->pos,
957 varp->ndims, varp->dimids, NULL);
958 if(status != NC_NOERR)
959 return status;
960 }
961 else {
962 status = check_v1hs(psp, ncx_len_int(varp->ndims));
963 if(status != NC_NOERR)
964 return status;
965 status = ncx_putn_int_int(&psp->pos,
966 varp->ndims, varp->dimids, NULL);
967 if(status != NC_NOERR)
968 return status;
969 }
970
971 status = v1h_put_NC_attrarray(psp, &varp->attrs);
972 if(status != NC_NOERR)
973 return status;
974
975 status = v1h_put_nc_type(psp, &varp->type);
976 if(status != NC_NOERR)
977 return status;
978
979 /* write vsize to header.
980 * CDF format specification: The vsize field is actually redundant, because
981 * its value may be computed from other information in the header. The
982 * 32-bit vsize field is not large enough to contain the size of variables
983 * that require more than 2^32 - 4 bytes, so 2^32 - 1 is used in the vsize
984 * field for such variables.
985 */
986 vsize = varp->len;
987 if (varp->len > 4294967292UL && (psp->version == NC_FORMAT_CLASSIC ||
988 psp->version == NC_FORMAT_64BIT_OFFSET))
989 vsize = 4294967295UL; /* 2^32-1 */
990 status = v1h_put_size_t(psp, &vsize);
991 if(status != NC_NOERR) return status;
992
993 status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/
994 if(status != NC_NOERR)
995 return status;
996 status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8);
997 if(status != NC_NOERR)
998 return status;
999
1000 return NC_NOERR;
1001 }
1002
1003
1004 /* Read a NC_var from the header */
1005 static int
v1h_get_NC_var(v1hs * gsp,NC_var ** varpp)1006 v1h_get_NC_var(v1hs *gsp, NC_var **varpp)
1007 {
1008 NC_string *strp;
1009 int status;
1010 size_t ndims;
1011 NC_var *varp;
1012
1013 status = v1h_get_NC_string(gsp, &strp);
1014 if(status != NC_NOERR)
1015 return status;
1016
1017 status = v1h_get_size_t(gsp, &ndims);
1018 if(status != NC_NOERR)
1019 goto unwind_name;
1020
1021 varp = new_x_NC_var(strp, ndims);
1022 if(varp == NULL)
1023 {
1024 status = NC_ENOMEM;
1025 goto unwind_name;
1026 }
1027
1028 if (gsp->version == 5) {
1029 status = check_v1hs(gsp, ncx_len_int64(ndims));
1030 if(status != NC_NOERR)
1031 goto unwind_alloc;
1032 status = ncx_getn_longlong_int((const void **)(&gsp->pos),
1033 ndims, varp->dimids);
1034 if(status != NC_NOERR)
1035 goto unwind_alloc;
1036 }
1037 else {
1038 status = check_v1hs(gsp, ncx_len_int(ndims));
1039 if(status != NC_NOERR)
1040 goto unwind_alloc;
1041 status = ncx_getn_int_int((const void **)(&gsp->pos),
1042 ndims, varp->dimids);
1043 if(status != NC_NOERR)
1044 goto unwind_alloc;
1045 }
1046 status = v1h_get_NC_attrarray(gsp, &varp->attrs);
1047 if(status != NC_NOERR)
1048 goto unwind_alloc;
1049 status = v1h_get_nc_type(gsp, &varp->type);
1050 if(status != NC_NOERR)
1051 goto unwind_alloc;
1052
1053 size_t tmp;
1054 status = v1h_get_size_t(gsp, &tmp);
1055 varp->len = tmp;
1056 if(status != NC_NOERR)
1057 goto unwind_alloc;
1058
1059 status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8);
1060 if(status != NC_NOERR)
1061 goto unwind_alloc;
1062 status = ncx_get_off_t((const void **)&gsp->pos,
1063 &varp->begin, gsp->version == 1 ? 4 : 8);
1064 if(status != NC_NOERR)
1065 goto unwind_alloc;
1066
1067 *varpp = varp;
1068 return NC_NOERR;
1069
1070 unwind_alloc:
1071 free_NC_var(varp); /* frees name */
1072 return status;
1073
1074 unwind_name:
1075 free_NC_string(strp);
1076 return status;
1077 }
1078
1079
1080 /* How much space in the header is required for this NC_vararray? */
1081 static size_t
ncx_len_NC_vararray(const NC_vararray * ncap,size_t sizeof_off_t,int version)1082 ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version)
1083 {
1084 size_t xlen = X_SIZEOF_NCTYPE; /* type */
1085 xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
1086 if(ncap == NULL)
1087 return xlen;
1088 /* else */
1089 {
1090 const NC_var **vpp = (const NC_var **)ncap->value;
1091 const NC_var *const *const end = &vpp[ncap->nelems];
1092 for( /*NADA*/; vpp < end; vpp++)
1093 {
1094 xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version);
1095 }
1096 }
1097 return xlen;
1098 }
1099
1100
1101 /* Write a NC_vararray to the header */
1102 static int
v1h_put_NC_vararray(v1hs * psp,const NC_vararray * ncap)1103 v1h_put_NC_vararray(v1hs *psp, const NC_vararray *ncap)
1104 {
1105 int status;
1106
1107 assert(psp != NULL);
1108
1109 if(ncap == NULL
1110 #if 1
1111 /* Backward:
1112 * This clause is for 'byte for byte'
1113 * backward compatibility.
1114 * Strickly speaking, it is 'bug for bug'.
1115 */
1116 || ncap->nelems == 0
1117 #endif
1118 )
1119 {
1120 /*
1121 * Handle empty netcdf
1122 */
1123 const size_t nosz = 0;
1124
1125 status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
1126 if(status != NC_NOERR)
1127 return status;
1128 status = v1h_put_size_t(psp, &nosz);
1129 if(status != NC_NOERR)
1130 return status;
1131 return NC_NOERR;
1132 }
1133 /* else */
1134
1135 status = v1h_put_NCtype(psp, NC_VARIABLE);
1136 if(status != NC_NOERR)
1137 return status;
1138 status = v1h_put_size_t(psp, &ncap->nelems);
1139 if(status != NC_NOERR)
1140 return status;
1141
1142 {
1143 const NC_var **vpp = (const NC_var **)ncap->value;
1144 const NC_var *const *const end = &vpp[ncap->nelems];
1145 for( /*NADA*/; vpp < end; vpp++)
1146 {
1147 status = v1h_put_NC_var(psp, *vpp);
1148 if(status)
1149 return status;
1150 }
1151 }
1152 return NC_NOERR;
1153 }
1154
1155
1156 /* Read a NC_vararray from the header */
1157 static int
v1h_get_NC_vararray(v1hs * gsp,NC_vararray * ncap)1158 v1h_get_NC_vararray(v1hs *gsp, NC_vararray *ncap)
1159 {
1160 int status;
1161 NCtype type = NC_UNSPECIFIED;
1162
1163 assert(gsp != NULL && gsp->pos != NULL);
1164 assert(ncap != NULL);
1165 assert(ncap->value == NULL);
1166
1167 status = v1h_get_NCtype(gsp, &type);
1168 if(status != NC_NOERR)
1169 return status;
1170
1171 status = v1h_get_size_t(gsp, &ncap->nelems);
1172 if(status != NC_NOERR)
1173 return status;
1174
1175 if(ncap->nelems == 0)
1176 return NC_NOERR;
1177 /* else */
1178 if(type != NC_VARIABLE)
1179 return EINVAL;
1180
1181 ncap->value = (NC_var **) calloc(1,ncap->nelems * sizeof(NC_var *));
1182 if(ncap->value == NULL)
1183 return NC_ENOMEM;
1184 ncap->nalloc = ncap->nelems;
1185
1186 ncap->hashmap = NC_hashmapnew(ncap->nelems);
1187 {
1188 NC_var **vpp = ncap->value;
1189 NC_var *const *const end = &vpp[ncap->nelems];
1190 for( /*NADA*/; vpp < end; vpp++)
1191 {
1192 status = v1h_get_NC_var(gsp, vpp);
1193 if(status)
1194 {
1195 ncap->nelems = (size_t)(vpp - ncap->value);
1196 free_NC_vararrayV(ncap);
1197 return status;
1198 }
1199 {
1200 int varid = (size_t)(vpp - ncap->value);
1201 NC_hashmapadd(ncap->hashmap, (uintptr_t)varid, (*vpp)->name->cp,strlen((*vpp)->name->cp));
1202 }
1203 }
1204 }
1205
1206 return NC_NOERR;
1207 }
1208
1209
1210 /* End NC_var */
1211 /* Begin NC */
1212
1213 /*
1214 * Recompute the shapes of all variables
1215 * Sets ncp->begin_var to start of first variable.
1216 * Sets ncp->begin_rec to start of first record variable.
1217 * Returns -1 on error. The only possible error is a reference
1218 * to a non existent dimension, which could occur for a corrupted
1219 * netcdf file.
1220 */
1221 static int
NC_computeshapes(NC3_INFO * ncp)1222 NC_computeshapes(NC3_INFO* ncp)
1223 {
1224 NC_var **vpp = (NC_var **)ncp->vars.value;
1225 NC_var *const *const end = &vpp[ncp->vars.nelems];
1226 NC_var *first_var = NULL; /* first "non-record" var */
1227 NC_var *first_rec = NULL; /* first "record" var */
1228 int status;
1229
1230 ncp->begin_var = (off_t) ncp->xsz;
1231 ncp->begin_rec = (off_t) ncp->xsz;
1232 ncp->recsize = 0;
1233
1234 if(ncp->vars.nelems == 0)
1235 return(0);
1236
1237 for( /*NADA*/; vpp < end; vpp++)
1238 {
1239 status = NC_var_shape(*vpp, &ncp->dims);
1240 if(status != NC_NOERR)
1241 return(status);
1242
1243 if(IS_RECVAR(*vpp))
1244 {
1245 if(first_rec == NULL)
1246 first_rec = *vpp;
1247 ncp->recsize += (*vpp)->len;
1248 }
1249 else
1250 {
1251 if(first_var == NULL)
1252 first_var = *vpp;
1253 /*
1254 * Overwritten each time thru.
1255 * Usually overwritten in first_rec != NULL clause below.
1256 */
1257 ncp->begin_rec = (*vpp)->begin + (off_t)(*vpp)->len;
1258 }
1259 }
1260
1261 if(first_rec != NULL)
1262 {
1263 if(ncp->begin_rec > first_rec->begin)
1264 return(NC_ENOTNC); /* not a netCDF file or corrupted */
1265 ncp->begin_rec = first_rec->begin;
1266 /*
1267 * for special case of exactly one record variable, pack value
1268 */
1269 if(ncp->recsize == first_rec->len)
1270 ncp->recsize = *first_rec->dsizes * first_rec->xsz;
1271 }
1272
1273 if(first_var != NULL)
1274 {
1275 ncp->begin_var = first_var->begin;
1276 }
1277 else
1278 {
1279 ncp->begin_var = ncp->begin_rec;
1280 }
1281
1282 if(ncp->begin_var <= 0 ||
1283 ncp->xsz > (size_t)ncp->begin_var ||
1284 ncp->begin_rec <= 0 ||
1285 ncp->begin_var > ncp->begin_rec)
1286 return(NC_ENOTNC); /* not a netCDF file or corrupted */
1287
1288 return(NC_NOERR);
1289 }
1290
1291 /* How much space in the header is required for the NC data structure? */
1292 size_t
ncx_len_NC(const NC3_INFO * ncp,size_t sizeof_off_t)1293 ncx_len_NC(const NC3_INFO* ncp, size_t sizeof_off_t)
1294 {
1295 int version=1;
1296 size_t xlen = sizeof(ncmagic);
1297
1298 assert(ncp != NULL);
1299 if (fIsSet(ncp->flags, NC_64BIT_DATA)) /* CDF-5 */
1300 version = 5;
1301 else if (fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* CDF-2 */
1302 version = 2;
1303
1304 xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* numrecs */
1305 xlen += ncx_len_NC_dimarray(&ncp->dims, version);
1306 xlen += ncx_len_NC_attrarray(&ncp->attrs, version);
1307 xlen += ncx_len_NC_vararray(&ncp->vars, sizeof_off_t, version);
1308
1309 return xlen;
1310 }
1311
1312
1313 /* Write the file header */
1314 int
ncx_put_NC(const NC3_INFO * ncp,void ** xpp,off_t offset,size_t extent)1315 ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent)
1316 {
1317 int status = NC_NOERR;
1318 v1hs ps; /* the get stream */
1319
1320 assert(ncp != NULL);
1321
1322 /* Initialize stream ps */
1323
1324 ps.nciop = ncp->nciop;
1325 ps.flags = RGN_WRITE;
1326
1327 if (ncp->flags & NC_64BIT_DATA)
1328 ps.version = 5;
1329 else if (ncp->flags & NC_64BIT_OFFSET)
1330 ps.version = 2;
1331 else
1332 ps.version = 1;
1333
1334 if(xpp == NULL)
1335 {
1336 /*
1337 * Come up with a reasonable stream read size.
1338 */
1339 extent = ncp->xsz;
1340 if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ))
1341 {
1342 /* first time read */
1343 extent = ncp->chunk;
1344 /* Protection for when ncp->chunk is huge;
1345 * no need to read hugely. */
1346 if(extent > 4096)
1347 extent = 4096;
1348 }
1349 else if(extent > ncp->chunk)
1350 extent = ncp->chunk;
1351
1352 ps.offset = 0;
1353 ps.extent = extent;
1354 ps.base = NULL;
1355 ps.pos = ps.base;
1356
1357 status = fault_v1hs(&ps, extent);
1358 if(status)
1359 return status;
1360 }
1361 else
1362 {
1363 ps.offset = offset;
1364 ps.extent = extent;
1365 ps.base = *xpp;
1366 ps.pos = ps.base;
1367 ps.end = (char *)ps.base + ps.extent;
1368 }
1369
1370 if (ps.version == 5)
1371 status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5, NULL);
1372 else if (ps.version == 2)
1373 status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic, NULL);
1374 else
1375 status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1, NULL);
1376 if(status != NC_NOERR)
1377 goto release;
1378
1379 {
1380 const size_t nrecs = NC_get_numrecs(ncp);
1381 if (ps.version == 5) {
1382 unsigned long long tmp = (unsigned long long) nrecs;
1383 status = ncx_put_uint64(&ps.pos, tmp);
1384 }
1385 else
1386 status = ncx_put_size_t(&ps.pos, &nrecs);
1387 if(status != NC_NOERR)
1388 goto release;
1389 }
1390
1391 assert((char *)ps.pos < (char *)ps.end);
1392
1393 status = v1h_put_NC_dimarray(&ps, &ncp->dims);
1394 if(status != NC_NOERR)
1395 goto release;
1396
1397 status = v1h_put_NC_attrarray(&ps, &ncp->attrs);
1398 if(status != NC_NOERR)
1399 goto release;
1400
1401 status = v1h_put_NC_vararray(&ps, &ncp->vars);
1402 if(status != NC_NOERR)
1403 goto release;
1404
1405 release:
1406 (void) rel_v1hs(&ps);
1407
1408 return status;
1409 }
1410
1411
1412 /* Make the in-memory NC structure from reading the file header */
1413 int
nc_get_NC(NC3_INFO * ncp)1414 nc_get_NC(NC3_INFO* ncp)
1415 {
1416 int status;
1417 v1hs gs; /* the get stream */
1418
1419 assert(ncp != NULL);
1420
1421 /* Initialize stream gs */
1422
1423 gs.nciop = ncp->nciop;
1424 gs.offset = 0; /* beginning of file */
1425 gs.extent = 0;
1426 gs.flags = 0;
1427 gs.version = 0;
1428 gs.base = NULL;
1429 gs.pos = gs.base;
1430
1431 {
1432 /*
1433 * Come up with a reasonable stream read size.
1434 */
1435 off_t filesize;
1436 size_t extent = ncp->xsz;
1437
1438 if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ))
1439 {
1440 status = ncio_filesize(ncp->nciop, &filesize);
1441 if(status)
1442 return status;
1443 if(filesize < sizeof(ncmagic)) { /* too small, not netcdf */
1444
1445 status = NC_ENOTNC;
1446 return status;
1447 }
1448 /* first time read */
1449 extent = ncp->chunk;
1450 /* Protection for when ncp->chunk is huge;
1451 * no need to read hugely. */
1452 if(extent > 4096)
1453 extent = 4096;
1454 if(extent > filesize)
1455 extent = filesize;
1456 }
1457 else if(extent > ncp->chunk)
1458 extent = ncp->chunk;
1459
1460 /*
1461 * Invalidate the I/O buffers to force a read of the header
1462 * region.
1463 */
1464 status = ncio_sync(gs.nciop);
1465 if(status)
1466 return status;
1467
1468 status = fault_v1hs(&gs, extent);
1469 if(status)
1470 return status;
1471 }
1472
1473 /* get the header from the stream gs */
1474
1475 {
1476 /* Get & check magic number */
1477 schar magic[sizeof(ncmagic)];
1478 (void) memset(magic, 0, sizeof(magic));
1479
1480 status = ncx_getn_schar_schar(
1481 (const void **)(&gs.pos), sizeof(magic), magic);
1482 if(status != NC_NOERR)
1483 goto unwind_get;
1484
1485 if(memcmp(magic, ncmagic, sizeof(ncmagic)-1) != 0)
1486 {
1487 status = NC_ENOTNC;
1488 goto unwind_get;
1489 }
1490 /* Check version number in last byte of magic */
1491 if (magic[sizeof(ncmagic)-1] == 0x1) {
1492 gs.version = 1;
1493 } else if (magic[sizeof(ncmagic)-1] == 0x2) {
1494 gs.version = 2;
1495 fSet(ncp->flags, NC_64BIT_OFFSET);
1496 /* Now we support version 2 file access on non-LFS systems -- rkr */
1497 #if 0
1498 if (sizeof(off_t) != 8) {
1499 fprintf(stderr, "NETCDF WARNING: Version 2 file on 32-bit system.\n");
1500 }
1501 #endif
1502 } else if (magic[sizeof(ncmagic)-1] == 0x5) {
1503 gs.version = 5;
1504 fSet(ncp->flags, NC_64BIT_DATA);
1505 } else {
1506 status = NC_ENOTNC;
1507 goto unwind_get;
1508 }
1509 }
1510
1511 {
1512 size_t nrecs = 0;
1513 if (gs.version == 5) {
1514 unsigned long long tmp = 0;
1515 status = ncx_get_uint64((const void **)(&gs.pos), &tmp);
1516 nrecs = (size_t)tmp;
1517 }
1518 else
1519 status = ncx_get_size_t((const void **)(&gs.pos), &nrecs);
1520 if(status != NC_NOERR)
1521 goto unwind_get;
1522 NC_set_numrecs(ncp, nrecs);
1523 }
1524
1525 assert((char *)gs.pos < (char *)gs.end);
1526
1527 status = v1h_get_NC_dimarray(&gs, &ncp->dims);
1528 if(status != NC_NOERR)
1529 goto unwind_get;
1530
1531 status = v1h_get_NC_attrarray(&gs, &ncp->attrs);
1532 if(status != NC_NOERR)
1533 goto unwind_get;
1534
1535 status = v1h_get_NC_vararray(&gs, &ncp->vars);
1536 if(status != NC_NOERR)
1537 goto unwind_get;
1538
1539 ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8);
1540
1541 status = NC_computeshapes(ncp);
1542 if(status != NC_NOERR)
1543 goto unwind_get;
1544
1545 status = NC_check_vlens(ncp);
1546 if(status != NC_NOERR)
1547 goto unwind_get;
1548
1549 status = NC_check_voffs(ncp);
1550 if(status != NC_NOERR)
1551 goto unwind_get;
1552
1553 unwind_get:
1554 (void) rel_v1hs(&gs);
1555 return status;
1556 }
1557