1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Copyright 1993, University Corporation for Atmospheric Research *
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions. *
4 * *
5 * Copyright by The HDF Group. *
6 * Copyright by the Board of Trustees of the University of Illinois. *
7 * All rights reserved. *
8 * *
9 * This file is part of HDF. The full HDF copyright notice, including *
10 * terms governing use, modification, and redistribution, is contained in *
11 * the COPYING file, which can be found at the root of the source code *
12 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/. *
13 * If you do not have access to either file, you may request a copy from *
14 * help@hdfgroup.org. *
15 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16
17 /* $Id$ */
18
19 #ifdef DEBUG
20 #include <assert.h>
21 #endif /* DEBUG */
22
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h> /* for access() */
26 #include "local_nc.h"
27 #include "alloc.h"
28 #include "herr.h"
29
30 /* obtain the maximum number of open files allowed, at the same time,
31 on the current system */
32 #if defined _WIN32
33 #define MAX_SYS_OPENFILES _getmaxstdio()
34 #else
35 #include <sys/resource.h>
36 struct rlimit rlim;
37 #define MAX_SYS_OPENFILES ( \
38 getrlimit((RLIMIT_NOFILE), (&rlim)), \
39 rlim.rlim_cur)
40 #endif
41
42 /* Maximum number of files can be opened at one time; subtract 3 from
43 the system allowed to account for stdin, stdout, and stderr */
44 /* On AIX 6.1 system the limit is 2GB-1; it caused our library to choke.
45 For now we will use a cap H4_MAX_AVAIL_OPENFILES on the maximum number
46 of files can be open at one time. This limit should probably
47 be in hlimits.h file in the future. EIP 2010-02-01*/
48
49 #define H4_MAX_AVAIL_OPENFILES 20000
50 #define MAX_AVAIL_OPENFILES (((MAX_SYS_OPENFILES - 3) > H4_MAX_AVAIL_OPENFILES) ? H4_MAX_AVAIL_OPENFILES : (MAX_SYS_OPENFILES - 3))
51
52 static int _curr_opened = 0 ; /* the number of files currently opened */
53 /* NOTE: _ncdf might have been the number of files currently opened, yet it
54 is not decremented when ANY file is closed but only when the file that
55 has the same index as _ncdf-1 is closed. Thus, it indicates the last
56 index in _cdfs intead of the number of files currently opened. So, I
57 added _curr_opened to keep track of the number of files currently opened.
58 QAK suggested to use atom as in other interfaces and that would eliminate
59 similar issues. - BMR - 11/03/07 */
60 static int _ncdf = 0 ; /* high water mark on open cdf's */
61 static NC **_cdfs;
62
63 #define HNDLE(id) (((id) >= 0 && (id) < _ncdf) ? _cdfs[(id)] : NULL)
64 #define STASH(id) (((id) >= 0 && (id) < _ncdf) ? \
65 HNDLE(_cdfs[(id)]->redefid) : NULL)
66
67 #ifdef NO_STDC_REMOVE
68 /* try unix 'unlink' */
69 #define remove(fpath) unlink((fpath))
70 #endif
71
72 #ifdef DOS_FS
73 #define SEP '\\' /* this separates path components on DOS */
74 #endif
75 #ifndef SEP
76 #define SEP '/' /* default, unix */
77 #endif
78
79 static intn max_NC_open = H4_MAX_NC_OPEN; /* current netCDF default */
80
81 /*
82 * Resets _cdfs
83 */
84 static void
ncreset_cdflist()85 ncreset_cdflist()
86 {
87 if (_cdfs != NULL)
88 {
89 HDfree((VOIDP)_cdfs);
90 _cdfs = NULL;
91 }
92 }
93
94 /*
95 * Allocates _cdfs and returns the allocated size if succeeds;
96 * otherwise return FAIL(-1).
97 */
98 intn
NC_reset_maxopenfiles(req_max)99 NC_reset_maxopenfiles(req_max)
100 intn req_max; /* requested max to allocate */
101 {
102 intn sys_limit = MAX_AVAIL_OPENFILES;
103 intn alloc_size;
104 NC **newlist;
105 intn i;
106 int ret_value = SUCCEED;
107
108 /* Verify arguments */
109 if (req_max < 0)
110 {
111 NCadvise(NC_EINVAL, "Invalid request: %d for maximum files", req_max);
112 HGOTO_DONE(-1) ;
113 }
114
115
116 /* If requested max is 0, allocate _cdfs with the default,
117 max_NC_open, if _cdfs is not yet allocated, otherwise, keep
118 _cdfs as is and return the current max */
119 if (req_max == 0)
120 {
121 if (!_cdfs)
122 {
123 _cdfs = (NC **)HDmalloc(sizeof(NC *) * (max_NC_open));
124
125 /* If allocation fails, return 0 for no allocation */
126 if (_cdfs == NULL)
127 {
128 /* NC_EINVAL is Invalid Argument, but must decide if
129 we just want to return 0 without error or not */
130 NCadvise(NC_EINVAL, "Unable to allocate a cdf list of %d elements", max_NC_open);
131 HGOTO_DONE(-1) ;
132 }
133 else
134 HGOTO_DONE(max_NC_open);
135 }
136 else /* return the current limit */
137 HGOTO_DONE (max_NC_open);
138 } /* if req_max == 0 */
139
140 /* If the requested max is less than the current max but there are
141 more than the requested max number of files opened, do not reset
142 the current max, since this will cause information lost. */
143 if (req_max < max_NC_open && req_max <= _ncdf)
144 HGOTO_DONE(max_NC_open);
145
146 /* If the requested max exceeds system limit, only allocate up
147 to system limit */
148 if (req_max > sys_limit)
149 alloc_size = sys_limit;
150 else
151 alloc_size = req_max;
152
153 /* Allocate a new list */
154 newlist = (NC **)HDmalloc(sizeof(NC *) * alloc_size);
155
156 /* If allocation fails, return 0 for no allocation */
157 if (newlist == NULL)
158 {
159 /* NC_EINVAL is Invalid Argument, but must decide if
160 we just want to return 0 without error or not */
161 NCadvise(NC_EINVAL, "Unable to allocate a cdf list of %d elements", alloc_size) ;
162 HGOTO_DONE(-1) ;
163 }
164
165 /* If _cdfs is already allocated, transfer pointers over to the
166 new list and deallocate the old list of pointers */
167 if (_cdfs != NULL)
168 {
169 for (i=0; i < _ncdf; i++)
170 newlist[i] = _cdfs[i];
171 HDfree(_cdfs);
172 }
173
174 /* Set _cdfs to the new list */
175 _cdfs = newlist;
176 newlist = NULL;
177
178 /* Reset current max files opened allowed in HDF to the new max */
179 max_NC_open = alloc_size;
180
181 HGOTO_DONE(max_NC_open);
182
183 done:
184 if (ret_value == FAIL)
185 { /* Failure cleanup */
186 /* Nothing yet. */
187 }
188 /* Normal cleanup */
189
190 return ret_value;
191 } /* NC_reset_maxopenfiles */
192
193 /*
194 * Returns the current # of open files allowed
195 */
196 intn
NC_get_maxopenfiles()197 NC_get_maxopenfiles()
198 {
199 return(max_NC_open);
200 } /* NC_get_maxopenfiles */
201
202 /*
203 * Returns the maximum number of open files the system allows.
204 */
205 intn
NC_get_systemlimit()206 NC_get_systemlimit()
207 {
208 return(MAX_AVAIL_OPENFILES);
209 } /* NC_get_systemlimit */
210
211 /*
212 * Returns the number of files currently being opened.
213 */
214 int
NC_get_numopencdfs()215 NC_get_numopencdfs()
216 {
217 return(_curr_opened);
218 } /* NC_get_numopencdfs */
219
220 /*
221 * Check validity of cdf handle, return pointer to NC struct or
222 * NULL on error.
223 */
224 NC *
NC_check_id(cdfid)225 NC_check_id(cdfid)
226 int cdfid ;
227 {
228 NC *handle ;
229
230 handle = ( cdfid >= 0 && cdfid < _ncdf) ? _cdfs[cdfid] : NULL ;
231 if(handle == NULL)
232 {
233 NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
234 return(NULL) ;
235 }
236 return(handle) ;
237 }
238
239
240 /*
241 * Check to see if in define mode.
242 * If 'iserr' arg is true, advise.
243 */
244 bool_t
NC_indefine(cdfid,iserr)245 NC_indefine(cdfid, iserr) /* Should be a Macro ? */
246 int cdfid ;
247 bool_t iserr ;
248 {
249 bool_t ret ;
250 ret = (cdfid >= 0 && cdfid < _ncdf) ?
251 (bool_t)(_cdfs[cdfid]->flags & NC_INDEF) : FALSE ;
252 if(!ret && iserr)
253 {
254 if(cdfid < 0 || cdfid >= _ncdf)
255 NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
256 else
257 NCadvise(NC_ENOTINDEFINE, "%s Not in define mode",
258 _cdfs[cdfid]->path) ;
259 }
260 return(ret) ;
261 }
262
263
264 /*
265 * Common code for ncopen and nccreate.
266 */
267 static int
NC_open(path,mode)268 NC_open(path, mode )
269 const char *path ; /* file name */
270 int mode ;
271 {
272 NC *handle ;
273 int cdfid;
274 intn cdfs_size;
275
276 /* Allocate _cdfs, if it is already allocated, nothing will be done */
277 if (_cdfs == NULL){
278 if (FAIL == (cdfs_size = NC_reset_maxopenfiles(0)))
279 {
280 NCadvise(NC_ENFILE, "Could not reset max open files limit");
281 return(-1);
282 }
283 }
284
285 /* find first available id */
286 for(cdfid = 0 ; cdfid < _ncdf; cdfid++)
287 if( _cdfs[cdfid] == NULL) break ;
288
289 /* if application attempts to open more files than the current max
290 allows, increase the current max to the system limit, if it's
291 not at the system limit yet */
292 if(cdfid == _ncdf && _ncdf >= max_NC_open)
293 {
294 /* if the current max already reaches the system limit, fail */
295 if (max_NC_open == MAX_AVAIL_OPENFILES)
296 {
297 NCadvise(NC_ENFILE, "maximum number of open cdfs allowed already reaches system limit %d", MAX_AVAIL_OPENFILES) ;
298 return(-1);
299 }
300 /* otherwise, increase the current max to the system limit */
301 if (FAIL == NC_reset_maxopenfiles(MAX_AVAIL_OPENFILES))
302 {
303 NCadvise(NC_ENFILE, "Could not reset max open files limit");
304 return(-1);
305 }
306 }
307
308 handle = NC_new_cdf(path, mode) ;
309 if( handle == NULL)
310 {
311 /* if the failure was due to "too many open files," simply return */
312 if(errno == EMFILE)
313 {
314 nc_serror("maximum number of open files allowed has been reached\"%s\"", path) ;
315 return(-1);
316 }
317
318 if((mode & 0x0f) == NC_CLOBBER)
319 {
320 /* only attempt to remove the file if it's not currently
321 in use - bugzilla #376 */
322 if(!HPisfile_in_use(path))
323 if( remove(path) != 0 )
324 nc_serror("couldn't remove filename \"%s\"", path) ;
325 }
326 return(-1) ;
327 }
328
329 (void) strncpy(handle->path, path, FILENAME_MAX) ;
330 _cdfs[cdfid] = handle ;
331 if(cdfid == _ncdf)
332 _ncdf++ ;
333 _curr_opened++;
334 return(cdfid) ;
335 } /* NC_open */
336
337
nccreate(path,cmode)338 int nccreate(path, cmode)
339 const char *path ; /* file name */
340 int cmode ;
341 {
342 cdf_routine_name = "nccreate" ;
343
344 if(cmode & NC_CREAT)
345 {
346 return(NC_open(path, cmode)) ;
347 }
348 NCadvise(NC_EINVAL, "Bad Flag") ;
349 return(-1) ;
350 }
351
352
ncopen(path,mode)353 int ncopen(path,mode)
354 const char *path ; /* file name */
355 int mode ;
356 {
357 cdf_routine_name = "ncopen" ;
358 if(mode & NC_CREAT)
359 {
360 NCadvise(NC_EINVAL, "Bad Flag") ;
361 return(-1) ;
362 }
363 return(NC_open(path, mode)) ;
364 }
365
366
ncsync(cdfid)367 int ncsync(cdfid)
368 int cdfid ;
369 {
370 NC *handle ;
371
372 cdf_routine_name = "ncsync" ;
373
374 handle = NC_check_id(cdfid) ;
375 if(handle == NULL)
376 return(-1) ;
377
378 if( handle->flags & NC_INDEF )
379 {
380 NCadvise(NC_EINDEFINE, "Unfinished definition") ;
381 return(-1) ;
382 }
383
384 if(handle->flags & NC_RDWR)
385 {
386 handle->xdrs->x_op = XDR_ENCODE ;
387 if(handle->flags & NC_HDIRTY)
388 {
389 if(!xdr_cdf(handle->xdrs, &handle) )
390 return(-1) ;
391 handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
392 }
393 else if(handle->flags & NC_NDIRTY)
394 {
395 if(!xdr_numrecs(handle->xdrs, handle) )
396 return(-1) ;
397 #ifdef HDF
398 if (handle->file_type != HDF_FILE)
399 #endif
400 handle->flags &= ~(NC_NDIRTY) ;
401 }
402 }
403 else /* read only */
404 {
405 /* assert(handle->xdrs->x_op == XDR_DECODE) ; */
406 /* free the stuff in handle that xdr_cdf allocates */
407 handle->xdrs->x_op = XDR_FREE ;
408 (void) xdr_cdf(handle->xdrs, &handle) ;
409 handle->xdrs->x_op = XDR_DECODE ;
410
411 if(!xdr_cdf(handle->xdrs, &handle) )
412 {
413 nc_serror("xdr_cdf") ;
414 NC_free_cdf(handle) ; /* ?? what should we do now? */
415
416 #if 0 /* not sure if we need this here, will check again - 1/26/08 BMR */
417 /* if the _cdf list is empty, deallocate and reset it to NULL */
418 if (_ncdf == 0)
419 ncreset_cdflist();
420 #endif
421 return(-1) ;
422 }
423 if( NC_computeshapes(handle) == -1)
424 return(-1) ;
425 }
426
427 (void) NCxdrfile_sync(handle->xdrs) ;
428
429 return(0) ;
430 }
431
432
433 /*
434 * In data mode, same as ncclose ;
435 * In define mode, restore previous definition ;
436 * In create, remove the file ;
437 */
ncabort(cdfid)438 int ncabort(cdfid)
439 int cdfid ;
440 {
441 NC *handle ;
442 char path[FILENAME_MAX + 1] ;
443 unsigned flags ;
444 #ifdef HDF
445 intn file_type;
446 #endif
447
448 cdf_routine_name = "ncabort" ;
449
450
451 handle = NC_check_id(cdfid) ;
452 if(handle == NULL)
453 return(-1) ;
454
455 flags = handle->flags ; /* need to save past free_cdf */
456
457 /* NC_CREAT implies NC_INDEF, in both cases need to remove handle->path */
458 if(flags & (NC_INDEF | NC_CREAT))
459 {
460 (void)strncpy(path, handle->path, FILENAME_MAX) ; /* stash path */
461 if(!(flags & NC_CREAT)) /* redef */
462 {
463 NC_free_cdf(STASH(cdfid)) ;
464
465 _cdfs[handle->redefid] = NULL ;
466 if(handle->redefid == _ncdf - 1)
467 _ncdf-- ;
468 handle->redefid = -1 ;
469 _curr_opened--; /* one less file currently opened */
470
471 /* if the _cdf list is empty, deallocate and reset it to NULL */
472 if (_ncdf == 0)
473 ncreset_cdflist();
474 }
475 }
476 else if(handle->flags & NC_RDWR)
477 {
478 handle->xdrs->x_op = XDR_ENCODE ;
479 if(handle->flags & NC_HDIRTY)
480 {
481 if(!xdr_cdf(handle->xdrs, &handle) )
482 return(-1) ;
483 }
484 else if(handle->flags & NC_NDIRTY)
485 {
486 if(!xdr_numrecs(handle->xdrs, handle) )
487 return(-1) ;
488 }
489 }
490
491 #ifdef HDF
492 file_type = handle->file_type;
493 #endif
494 NC_free_cdf(handle) ; /* calls fclose */
495
496 #ifdef HDF
497 switch(file_type)
498 {
499 case netCDF_FILE:
500 if(flags & (NC_INDEF | NC_CREAT))
501 {
502 if( remove(path) != 0 )
503 nc_serror("couldn't remove filename \"%s\"", path) ;
504 }
505 break;
506 case HDF_FILE:
507 if(flags & NC_CREAT)
508 {
509 if( remove(path) != 0 )
510 nc_serror("couldn't remove filename \"%s\"", path) ;
511 }
512 break;
513 }
514 #else
515 if(flags & (NC_INDEF | NC_CREAT))
516 {
517 if( remove(path) != 0 )
518 nc_serror("couldn't remove filename \"%s\"", path) ;
519 }
520 #endif
521
522 _cdfs[cdfid] = NULL ; /* reset pointer */
523
524 /* if current file is at the top of the list, adjust the water mark */
525 if(cdfid == _ncdf - 1)
526 _ncdf-- ;
527 _curr_opened--; /* one less file currently being opened */
528
529 /* if the _cdf list is empty, deallocate and reset it to NULL */
530 if (_ncdf == 0)
531 ncreset_cdflist();
532
533 return(0) ;
534 } /* ncabort */
535
536
537 /*
538 * Deprecated function ;
539 */
ncnobuf(cdfid)540 int ncnobuf(cdfid)
541 int cdfid ;
542 {
543 NC *handle ;
544
545 cdf_routine_name = "ncnobuf" ;
546
547 handle = NC_check_id(cdfid) ;
548 if(handle == NULL)
549 return(-1) ;
550 /* NOOP */
551 return(0);
552 }
553
554
555 /*
556 * Given the path to a file "proto",
557 * we replace the filename component with
558 * a name like one would get from tmpnam(3S).
559 * (Many implementations of tmpnam insist on giving us
560 * a directory like /usr/tmp as well. Since we are making a copy which we
561 * will eventually rename() back to proto, we want the return of NCtempname
562 * and proto to dwell on the same filesystem.)
563 */
564 static char *
NCtempname(proto)565 NCtempname(proto)
566 const char *proto ;
567 {
568 /* NO_ACCESS defined if the OS lacks the access() function */
569 #ifndef NO_ACCESS
570 # define TN_NACCES 1
571 #else
572 # define TN_NACCES 0
573 #endif /* !NO_ACCESS */
574 /* NO_GETPID defined if the OS lacks the getpid() function */
575 #ifndef NO_GETPID
576 # define TN_NDIGITS 4
577 #if defined _WIN32
578 typedef int pid_t;
579 #endif
580 pid_t getpid(void);
581 unsigned int pid ; /* OS/2 DOS (MicroSoft Lib) allows "negative" int pids */
582 #else
583 # define TN_NDIGITS 0
584 #endif /* !NO_GETPID */
585
586 static char seed[] = {'a','a','a', '\0'} ;
587 #define TN_NSEED (sizeof(seed) -1)
588 static char tnbuf[FILENAME_MAX +1] ;
589 char *begin, *cp, *sp ;
590
591 /* assert(TN_NSEED > 0) ; */
592 strcpy(tnbuf,proto) ;
593
594 #ifdef SEP
595 if ((begin = strrchr(tnbuf, SEP)) == NULL)
596 begin = tnbuf ;
597 else
598 begin++ ;
599
600 if(&tnbuf[FILENAME_MAX] - begin <= TN_NSEED + TN_NACCES + TN_NDIGITS)
601 {
602 /* not big enough */
603 tnbuf[0] = '\0' ;
604 return tnbuf ;
605 }
606 #else
607 begin = tnbuf ;
608 #endif /* SEP */
609
610 *begin = '\0' ;
611 (void) strcat(begin, seed) ;
612
613 cp = begin + TN_NSEED + TN_NACCES + TN_NDIGITS ;
614 #ifndef NO_GETPID
615 *cp = '\0' ;
616 pid = getpid() ;
617 while(--cp >= begin + TN_NSEED + TN_NACCES)
618 {
619 *cp = (pid % 10) + '0' ;
620 pid /= 10 ;
621 }
622 #else
623 *cp-- = '\0' ;
624 #endif /* !NO_GETPID */
625
626 /* update seed for next call */
627 sp = seed ;
628 while(*sp == 'z')
629 *sp++ = 'a' ;
630 if(*sp != '\0')
631 ++*sp ;
632
633 #ifndef NO_ACCESS
634 for(*cp = 'a' ; access(tnbuf, 0) == 0 ; )
635 {
636 if(++*cp > 'z')
637 {
638 /* ran out of tries */
639 tnbuf[0] = '\0' ;
640 return tnbuf ;
641 }
642 }
643 #endif /* !NO_ACCESS */
644
645 return tnbuf ;
646 }
647
648
ncredef(cdfid)649 int ncredef(cdfid)
650 int cdfid ;
651 {
652 NC *handle ;
653 NC *new ;
654 int id ;
655 char *scratchfile ;
656
657 cdf_routine_name = "ncredef" ;
658
659 handle = NC_check_id(cdfid) ;
660 if(handle == NULL)
661 return(-1) ;
662 if( handle->flags & NC_INDEF) /* in define mode already */
663 {
664 NC *stash = STASH(cdfid) ;
665 if(stash) NCadvise(NC_EINDEFINE, "%s: in define mode aleady",
666 stash->path) ;
667 return(-1) ;
668 }
669 if(!(handle->flags & NC_RDWR))
670 {
671 NCadvise(NC_EPERM, "%s: NC_NOWRITE", handle->path) ;
672 return(-1) ;
673 }
674
675
676 #ifdef HDF
677 if(handle->file_type == HDF_FILE)
678 {
679 handle->flags |= NC_INDEF ;
680 handle->redefid = TRUE;
681 return(0);
682 }
683 #endif
684
685 /* find first available id */
686 for(id = 0 ; id < _ncdf; id++)
687 if( _cdfs[id] == NULL) break ;
688
689 if(id == _ncdf && _ncdf >= max_NC_open) /* will need a new one */
690 {
691 NCadvise(NC_ENFILE, "maximum number of open cdfs %d exceeded",
692 _ncdf) ;
693 return(-1) ;
694 }
695
696 if( ncopts & NC_NOFILL )
697 {
698 /* fill last record */
699 handle->xdrs->x_op = XDR_ENCODE ;
700 if(handle->flags & NC_NDIRTY)
701 {
702 if(!xdr_numrecs(handle->xdrs, handle) )
703 return(-1) ;
704 handle->flags &= ~(NC_NDIRTY) ;
705 }
706 }
707
708 scratchfile = NCtempname(handle->path) ;
709
710 new = NC_dup_cdf(scratchfile, NC_NOCLOBBER, handle) ;
711 if(new == NULL)
712 {
713 return(-1) ;
714 }
715
716 handle->flags |= NC_INDEF ;
717 (void) strncpy(new->path, scratchfile, FILENAME_MAX) ;
718
719 /* put the old handle in the new id */
720 _cdfs[id] = handle ;
721 if(id == _ncdf)
722 _ncdf++ ;
723 _curr_opened++;
724
725 /* put the new handle in old id */
726 _cdfs[cdfid] = new ;
727
728 new->redefid = id ;
729
730 return(0) ;
731 }
732
733
734 /*
735 * Compute offsets and put into the header
736 */
737 static void
NC_begins(handle)738 NC_begins(handle)
739 NC *handle ;
740 {
741 unsigned ii ;
742 u_long index = 0 ;
743 NC_var **vpp ;
744 NC_var *last = NULL ;
745
746 if(handle->vars == NULL)
747 return ;
748
749 index = NC_xlen_cdf(handle) ;
750
751 /* loop thru vars, first pass is for the 'non-record' vars */
752 vpp = (NC_var **)handle->vars->values ;
753 for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
754 {
755 if( IS_RECVAR(*vpp) )
756 {
757 continue ; /* skip record variables on this pass */
758 }
759
760 (*vpp)->begin = index ;
761 index += (*vpp)->len ;
762 #ifdef EDEBUG
763 NCadvise(NC_NOERR, "%s pass 1 begin %d, length %d",
764 (*vpp)->name->values, (*vpp)->begin, (*vpp)->len) ;
765 #endif /* EDEBUG */
766 }
767
768 handle->begin_rec = index ;
769 handle->recsize = 0 ;
770
771 /* loop thru vars, second pass is for the 'non-record' vars */
772 vpp = (NC_var **)handle->vars->values ;
773 for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
774 {
775 if( !IS_RECVAR(*vpp) )
776 {
777 continue ; /* skip non-record variables on this pass */
778 }
779
780 (*vpp)->begin = index ;
781 #ifdef EDEBUG
782 NCadvise(NC_NOERR, "%s pass 2 begin %d, len %d, *dsizes %d",
783 (*vpp)->name->values, (*vpp)->begin, index, (*vpp)->len, *(*vpp)->dsizes ) ;
784 #endif /* EDEBUG */
785 index += (*vpp)->len ;
786 handle->recsize += (*vpp)->len ;
787 last = (*vpp) ;
788 }
789 /*
790 * for special case of exactly one record variable, pack values
791 */
792 if(last != NULL && handle->recsize == last->len)
793 handle->recsize = *last->dsizes ;
794 handle->numrecs = 0 ;
795 }
796
797
798 /*
799 * Copy nbytes bytes from source to target.
800 * Streams target and source should be positioned before the call.
801 * opaque I/O, no XDR conversion performed (or needed).
802 * The Macros XDR_GETBYTES and XDR_PUTBYTES may not be
803 * supported on your xdr implementation. If not, calls
804 * to xdr_opaque may be used.
805 */
806 bool_t
NC_dcpy(target,source,nbytes)807 NC_dcpy( target, source, nbytes)
808 XDR *target ;
809 XDR *source ;
810 long nbytes ;
811 {
812 /* you may wish to tune this: big on a cray, small on a PC? */
813 #define NC_DCP_BUFSIZE 8192
814 char buf[NC_DCP_BUFSIZE] ;
815
816 while(nbytes > sizeof(buf))
817 {
818 if(!XDR_GETBYTES(source, buf, sizeof(buf)))
819 goto err ;
820 if(!XDR_PUTBYTES(target, buf, sizeof(buf)))
821 goto err ;
822 nbytes -= sizeof(buf) ;
823 }
824 /* we know nbytes <= sizeof(buf) at this point */
825 if(!XDR_GETBYTES(source, buf, nbytes))
826 goto err ;
827 if(!XDR_PUTBYTES(target, buf, nbytes))
828 goto err ;
829 return(TRUE) ;
830 err:
831 NCadvise(NC_EXDR, "NC_dcpy") ;
832 return(FALSE) ;
833 }
834
835
836 /*
837 * XDR the data of varid in old, target is the new xdr strm
838 */
839 static bool_t
NC_vcpy(target,old,varid)840 NC_vcpy( target, old, varid)
841 XDR *target ;
842 NC *old ;
843 int varid ;
844 {
845 NC_var **vpp ;
846 vpp = (NC_var **)old->vars->values ;
847 vpp += varid ;
848
849 if( !xdr_setpos(old->xdrs, (*vpp)->begin) )
850 {
851 NCadvise(NC_EXDR, "NC_vcpy: xdr_setpos") ;
852 return(FALSE) ;
853 }
854
855 return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
856 }
857
858
859 /*
860 * XDR the data of (varid, recnum) in old, target is the new xdr strm
861 */
862 static bool_t
NC_reccpy(target,old,varid,recnum)863 NC_reccpy( target, old, varid, recnum)
864 XDR *target ;
865 NC *old ;
866 int varid ;
867 int recnum ;
868 {
869 NC_var **vpp ;
870 vpp = (NC_var **)old->vars->values ;
871 vpp += varid ;
872
873 if( !xdr_setpos(old->xdrs,
874 (*vpp)->begin + old->recsize*recnum )){
875 NCadvise(NC_EXDR, "NC_reccpy: xdr_setpos") ;
876 return(FALSE) ;
877 }
878
879 return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
880 }
881
882
883 /*
884 * Common code for ncendef, ncclose(endef)
885 */
886 static
NC_endef(cdfid,handle)887 int NC_endef( cdfid, handle )
888 int cdfid ;
889 NC *handle ;
890 {
891 XDR *xdrs ;
892 unsigned ii ;
893 unsigned jj = 0 ;
894 NC_var **vpp ;
895 NC *stash = STASH(cdfid) ; /* faster rvalue */
896
897 #ifdef HDF
898 if(handle->file_type != HDF_FILE)
899 #endif
900 NC_begins(handle) ;
901
902 xdrs = handle->xdrs ;
903 xdrs->x_op = XDR_ENCODE ;
904
905 if(!xdr_cdf(xdrs, &handle) )
906 {
907 nc_serror("xdr_cdf") ;
908 return(-1) ;
909 }
910
911 #ifdef HDF
912 /* Get rid of the temporary buffer allocated for I/O */
913 SDPfreebuf();
914
915 if(handle->file_type == HDF_FILE)
916 {
917 handle->flags &= ~(NC_CREAT | NC_INDEF | NC_NDIRTY | NC_HDIRTY) ;
918 return(0) ;
919 }
920 #endif
921
922 if(handle->vars == NULL)
923 goto done ;
924
925 /* loop thru vars, first pass is for the 'non-record' vars */
926 vpp = (NC_var **)handle->vars->values ;
927 for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
928 {
929 if( IS_RECVAR(*vpp) )
930 {
931 continue ; /* skip record variables on this pass */
932 }
933
934 #ifdef DEBUG
935 assert( (*vpp)->begin == xdr_getpos(xdrs) ) ;
936 #endif /* DEBUG */
937
938 if( !(handle->flags & NC_CREAT) && stash->vars != NULL
939 && ii < stash->vars->count)
940 {
941 /* copy data */
942 if( !NC_vcpy(xdrs, stash, ii) )
943 return(-1) ;
944 continue ;
945 } /* else */
946
947 if( !(handle->flags & NC_NOFILL) )
948 if( !xdr_NC_fill(xdrs, *vpp) )
949 return(-1) ;
950 }
951
952 if(!(handle->flags & NC_CREAT)) /* after redefinition */
953 {
954 for(jj = 0 ; jj < stash->numrecs ; jj++)
955 {
956 vpp = (NC_var **)handle->vars->values ;
957 for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
958 {
959 if( !IS_RECVAR(*vpp) )
960 {
961 continue ; /* skip non-record variables on this pass */
962 }
963 if( stash->vars != NULL && ii < stash->vars->count)
964 {
965 /* copy data */
966 if( !NC_reccpy(xdrs, stash, ii, jj) )
967 return(-1) ;
968 continue ;
969 } /* else */
970 if( !(handle->flags & NC_NOFILL) )
971 if( !xdr_NC_fill(xdrs, *vpp) )
972 return(-1) ;
973 }
974 }
975 handle->numrecs = stash->numrecs ;
976 if(!xdr_numrecs(handle->xdrs, handle) )
977 return(-1) ;
978 }
979
980 #ifdef EDEBUG
981 NCadvise(NC_NOERR, "begin %d, recsize %d, numrecs %d",
982 handle->begin_rec, handle->recsize, handle->numrecs) ;
983 #endif /* EDEBUG */
984
985 if(!(handle->flags & NC_CREAT)) /* redefine */
986 {
987 char realpath[FILENAME_MAX + 1] ;
988 strcpy(realpath, stash->path) ;
989
990 /* close stash */
991 /* NC_free_cdf(stash) ; */
992 #ifdef DOS_FS
993 xdr_destroy(handle->xdrs) ; /* close handle */
994 if( remove(realpath) != 0 )
995 nc_serror("couldn't remove filename \"%s\"", realpath) ;
996 #endif
997 if( rename(handle->path, realpath) != 0)
998 {
999 nc_serror("rename %s -> %s failed", handle->path, realpath) ;
1000 /* try to restore state prior to redef */
1001 _cdfs[cdfid] = stash ;
1002 _cdfs[handle->redefid] = NULL ;
1003 if(handle->redefid == _ncdf - 1)
1004 _ncdf-- ;
1005 _curr_opened--; /* one less file currently opened */
1006 NC_free_cdf(handle) ;
1007
1008 /* if the _cdf list is empty, deallocate and reset it to NULL */
1009 if (_ncdf == 0)
1010 ncreset_cdflist();
1011
1012 return(-1) ;
1013 }
1014 (void) strncpy(handle->path, realpath, FILENAME_MAX) ;
1015 #ifdef DOS_FS
1016 if( NCxdrfile_create( handle->xdrs, handle->path, NC_WRITE ) < 0)
1017 return -1 ;
1018 #endif
1019 NC_free_cdf(stash) ;
1020 _cdfs[handle->redefid] = NULL ;
1021 if(handle->redefid == _ncdf - 1)
1022 _ncdf-- ;
1023 _curr_opened--; /* one less file currently opened */
1024 handle->redefid = -1 ;
1025
1026 /* if the _cdf list is empty, deallocate and reset it to NULL */
1027 if (_ncdf == 0)
1028 ncreset_cdflist();
1029 }
1030
1031 done:
1032 handle->flags &= ~(NC_CREAT | NC_INDEF | NC_NDIRTY | NC_HDIRTY) ;
1033 return(0) ;
1034 }
1035
1036
ncendef(cdfid)1037 int ncendef( cdfid )
1038 int cdfid ;
1039 {
1040 NC *handle ;
1041
1042 cdf_routine_name = "ncendef" ;
1043
1044 handle = NC_check_id(cdfid) ;
1045 if(handle == NULL)
1046 return(-1) ;
1047 if( !NC_indefine(cdfid,TRUE) )
1048 return(-1) ;
1049 return( NC_endef(cdfid, handle) ) ;
1050 }
1051
1052 /*
1053 * This routine is called by SDend()? -GV
1054 */
ncclose(cdfid)1055 int ncclose( cdfid )
1056 int cdfid ;
1057 {
1058 NC *handle ;
1059
1060 cdf_routine_name = "ncclose" ;
1061
1062 handle = NC_check_id(cdfid) ;
1063 if(handle == NULL)
1064 return(-1) ;
1065
1066 if( handle->flags & NC_INDEF)
1067 {
1068 if( NC_endef(cdfid, handle) == -1 )
1069 {
1070 return( ncabort(cdfid) ) ;
1071 }
1072 }
1073 else if(handle->flags & NC_RDWR)
1074 {
1075 handle->xdrs->x_op = XDR_ENCODE ;
1076 if(handle->flags & NC_HDIRTY)
1077 {
1078 if(!xdr_cdf(handle->xdrs, &handle) )
1079 return(-1) ;
1080 }
1081 else if(handle->flags & NC_NDIRTY)
1082 {
1083 if(!xdr_numrecs(handle->xdrs, handle) )
1084 return(-1) ;
1085 }
1086 }
1087
1088 #ifdef HDF
1089 if(handle->file_type == HDF_FILE)
1090 hdf_close(handle);
1091 #endif
1092
1093 NC_free_cdf(handle) ; /* calls fclose */
1094
1095 _cdfs[cdfid] = NULL ; /* reset pointer */
1096
1097 if(cdfid == _ncdf - 1)
1098 _ncdf-- ;
1099 _curr_opened--; /* one less file currently opened */
1100
1101 /* if the _cdf list is empty, deallocate and reset it to NULL */
1102 if (_ncdf == 0)
1103 ncreset_cdflist();
1104 return(0) ;
1105 }
1106
1107 int
ncsetfill(id,fillmode)1108 ncsetfill(id, fillmode)
1109 int id ;
1110 int fillmode ;
1111 {
1112 NC *handle ;
1113 int ret = 0 ;
1114
1115 cdf_routine_name = "ncsetfill" ;
1116
1117 handle = NC_check_id(id) ;
1118 if(handle == NULL)
1119 return(-1) ;
1120
1121 if(!(handle->flags & NC_RDWR))
1122 {
1123 /* file isn't writable */
1124 NCadvise(NC_EPERM, "%s is not writable", handle->path) ;
1125 return -1 ;
1126 }
1127
1128 ret = (handle->flags & NC_NOFILL) ? NC_NOFILL : NC_FILL ;
1129
1130 if(fillmode == NC_NOFILL)
1131 handle->flags |= NC_NOFILL ;
1132 else if(fillmode == NC_FILL)
1133 {
1134 if(handle->flags & NC_NOFILL)
1135 {
1136 /*
1137 * We are changing back to fill mode
1138 * so do a sync
1139 */
1140 #ifdef HDF /* save the original x_op */
1141 enum xdr_op xdr_op = handle->xdrs->x_op;
1142
1143 if (handle->flags & NC_RDWR) /* make sure we can write */
1144 handle->xdrs->x_op = XDR_ENCODE; /* to the file */
1145 #endif
1146 if(handle->flags & NC_HDIRTY)
1147 {
1148 if(!xdr_cdf(handle->xdrs, &handle) )
1149 return(-1) ;
1150 handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
1151 }
1152 else if(handle->flags & NC_NDIRTY)
1153 {
1154 if(!xdr_numrecs(handle->xdrs, handle) )
1155 return(-1) ;
1156 #ifdef HDF
1157 if (handle->file_type != HDF_FILE)
1158 handle->flags &= ~(NC_NDIRTY) ;
1159 #else
1160 handle->flags &= ~(NC_NDIRTY) ;
1161 #endif
1162 }
1163 handle->flags &= ~NC_NOFILL ;
1164 #ifdef HDF /* re-store the x_op */
1165 handle->xdrs->x_op = xdr_op;
1166 #endif
1167 }
1168 }
1169 else
1170 {
1171 NCadvise(NC_EINVAL, "Bad fillmode") ;
1172 return -1 ;
1173 }
1174
1175
1176 return ret ;
1177 }
1178