1 /******************************************************************************
2 *
3 *  NSSDC/CDF                                            Virtual stream file.
4 *
5 *  Version 4.7a, 18-Nov-97, Hughes STX.
6 *
7 *  Modification history:
8 *
9 *   V1.0  22-Jan-91, J Love     Original version (developed for CDF V2.0).
10 *   V2.0  12-Mar-91, J Love     All fixes to V1.x.  Modified vread and vwrite
11 *                               to buffer only when necessary.
12 *   V3.0  14-May-91, J Love     Added caching (for CDF V2.1).
13 *   V3.1  31-Jul-91, J Love     Added veof.  Added 'memmove' for UNIX.  Added
14 *                               "deq - default extension quantity" if VMS.
15 *                               Changed algorithm that looks for bufferN.
16 *                               Added number of CACHE buffers as a parameter
17 *                               specified in 'Vopen'.  Renamed functions to
18 *                               avoid collisions on SGi/IRIX.
19 *   V3.2  15-Aug-91, J Love     Changed for IBM-PC/MS-DOS port.
20 *   V4.0  20-May-92, J Love     IBM PC port/CDF V2.2.
21 *   V4.1  29-Sep-92, J Love     CDF V2.3.  Dealt with EOFs not at 512-byte
22 *                               boundaries (when FTPed from a UNIX machine).
23 *   V4.2  21-Dec-93, J Love     CDF V2.4.
24 *   V4.3  12-Dec-94, J Love     CDF V2.5.
25 *   V4.3a 19-Jan-95, J Love     IRIX 6.0 (64-bit).
26 *   V4.3b 24-Feb-95, J Love     Solaris 2.3 IDL i/f.
27 *   V4.4   7-Jun-95, J Love     Virtual memory under Microsoft C 7.00.
28 *   V4.5  25-Jul-95, J Love     More virtual memory under Microsoft C 7.00.
29 *   V4.6  29-Sep-95, J Love     Improved performance...on non-VMS systems don't
30 *                               extend files a block at a time, don't clear
31 *                               bytes (anywhere), etc.
32 *   V4.7  26-Aug-96, J Love     CDF V2.6.
33 *   V4.7a 18-Nov-97, J Love	Windows NT/Visual C++.
34 *   V4.8  29-Jun-04, M Liu      Added LFS (Large File Support > 2G).
35 *   V4.9  25-Apr-07, D Berger   Changed all instances of hardcoded 512 to
36 *                               nCACHE_BUFFER_BYTEs.
37 *   V4.10 20-Jun-07, D Berger   Added initialization of variables to support
38 *                               READONLYon enhancements.
39 *   V4.11 18-Jun-08, M Liu      Modified the V_read and V_write to make sure
40 *                               the actual read/write bytes is within the
41 *                               buffer size.
42 *
43 ******************************************************************************/
44 
45 /******************************************************************************
46 * Include files.
47 ******************************************************************************/
48 
49 #include "cdflib.h"
50 #include "cdflib64.h"
51 
52 /******************************************************************************
53 * Local macros/typedef's.
54 ******************************************************************************/
55 
56 #define CLEAR_BYTES     0
57 
58 #if defined(vms) || defined(MPW_C)
59 #define EXTEND_FILE     1
60 #else
61 #define EXTEND_FILE     0
62 #endif
63 
64 #define LASTphyBLOCKn(vFp) \
65 BOO(vFp->phyLength == 0,NO_BLOCK,((vFp->phyLength - 1)/nCACHE_BUFFER_BYTEs))
66 
67 #if defined(MICROSOFTC_700) && INCLUDEvMEMORY
68 #define CACHEbufferREADfrom(cache) \
69 BOO(useVmem,LoadVMemory((MemHandle)cache->ptr,FALSE),cache->ptr)
70 #define CACHEbufferWRITEto(cache) \
71 BOO(useVmem,LoadVMemory((MemHandle)cache->ptr,TRUE),cache->ptr)
72 #else
73 #define CACHEbufferREADfrom(cache) cache->ptr
74 #define CACHEbufferWRITEto(cache) cache->ptr
75 #endif
76 
77 /******************************************************************************
78 * Local function prototypes.
79 ******************************************************************************/
80 
81 static FILE *OpenFile PROTOARGs((char *file_spec, char *a_mode));
82 static Logical FreeCache PROTOARGs((vCACHE *firstCache));
83 static vCACHE *FindCache PROTOARGs((vFILE *vFp, long blockN));
84 static Logical vRead PROTOARGs((
85   long offset, void *buffer, size_t nBytes, vFILE *vFp
86 ));
87 static Logical vWrite PROTOARGs((
88   long offset, void *buffer, size_t nBytes, vFILE *vFp
89 ));
90 static vCACHE *AllocateBuffer PROTOARGs((vFILE *vFp));
91 static vCACHE *PageIn PROTOARGs((vFILE *vFp, long blockN));
92 static Logical WriteBlockFromCache PROTOARGs((
93   vFILE *vFp, vCACHE *cache, size_t Nbytes
94 ));
95 static Logical WriteBlockFromBuffer PROTOARGs((
96   vFILE *vFp, long blockN, void *buffer, size_t Nbytes
97 ));
98 #if EXTEND_FILE
99 static Logical ExtendFile PROTOARGs((vFILE *vFp, long toBlockN));
100 #endif
101 
102 /******************************************************************************
103 * OpenFile.
104 ******************************************************************************/
105 
OpenFile(file_spec,a_mode)106 static FILE *OpenFile (file_spec, a_mode)
107 char *file_spec;
108 char *a_mode;
109 {
110 #if defined(vms)
111   char mrs[10+1];       /* Maximum record size. */
112   char deq[10+1];       /* Default allocation quantity. */
113   sprintf (mrs, "mrs=%d", nCACHE_BUFFER_BYTEs);
114   sprintf (deq, "deq=%d", VMS_DEFAULT_nALLOCATION_BLOCKS);
115   return fopen(file_spec,a_mode,"rfm=fix",mrs,deq);
116 #else
117   return FOPEN(file_spec,a_mode);
118 #endif
119 }
120 
121 /******************************************************************************
122 * FindCache.
123 ******************************************************************************/
124 
FindCache(vFp,blockN)125 static vCACHE *FindCache (vFp, blockN)
126 vFILE *vFp;
127 long blockN;
128 {
129   vCACHE *cache = vFp->cacheHead;
130   while (cache != NULL) {
131     if (cache->blockN == blockN) {
132       if (cache != vFp->cacheHead) {
133 	if (cache == vFp->cacheTail) {
134 	  cache->prev->next = NULL;
135 	  vFp->cacheTail = cache->prev;
136 	}
137 	else {
138 	  cache->next->prev = cache->prev;
139 	  cache->prev->next = cache->next;
140 	}
141 	vFp->cacheHead->prev = cache;
142 	cache->next = vFp->cacheHead;
143 	vFp->cacheHead = cache;
144 	cache->prev = NULL;
145       }
146       return cache;
147     }
148     cache = cache->next;
149   }
150   return NULL;
151 }
152 
153 /******************************************************************************
154 * FlushCache.
155 * Write cache buffers to disk from the specified starting buffer to the last
156 * buffer.
157 ******************************************************************************/
158 
FlushCache(vFp,firstCache)159 VISIBLE_PREFIX Logical FlushCache (vFp, firstCache)
160 vFILE *vFp;             /* Pointer to vFILE structure. */
161 vCACHE *firstCache;     /* Pointer to the first cache structure to flush. */
162 {
163   vCACHE *cache; long nBytes;
164   for (cache = firstCache; cache != NULL; cache = cache->next) {
165      if (cache->modified) {
166 #if defined(vms)
167        nBytes = nCACHE_BUFFER_BYTEs;
168 #else
169        nBytes = vFp->length - (cache->blockN * nCACHE_BUFFER_BYTEs);
170        nBytes = MINIMUM (nBytes, nCACHE_BUFFER_BYTEs);
171 #endif
172        if (!WriteBlockFromCache(vFp,cache,(size_t)nBytes)) return FALSE;
173        cache->modified = FALSE;
174      }
175   }
176   return TRUE;
177 }
178 
179 /******************************************************************************
180 * FreeCache.
181 ******************************************************************************/
182 
FreeCache(firstCache)183 static Logical FreeCache (firstCache)
184 vCACHE *firstCache;     /* Pointer to the first cache structure to free. */
185 {
186   vCACHE *cache = firstCache;
187   while (cache != NULL) {
188     vCACHE *nextCache = cache->next;
189 #if defined(MICROSOFTC_700) && INCLUDEvMEMORY
190     if (useVmem)
191       FreeVMemory ((MemHandle) cache->ptr);
192     else
193 #endif
194       cdf_FreeMemory (cache->ptr, NULL);
195     cdf_FreeMemory (cache, NULL);
196     cache = nextCache;
197   }
198   return TRUE;
199 }
200 
201 /******************************************************************************
202 * AllocateBuffer.
203 * Allocate a cache structure to use.  It may be necessary to page out a block
204 * to the file.  Returns a pointer to the allocated cache structure (or NULL if
205 * an error occurred).
206 ******************************************************************************/
207 
AllocateBuffer(vFp)208 static vCACHE *AllocateBuffer (vFp)
209 vFILE *vFp;
210 {
211   vCACHE *cache; long nBytes;
212 #if !defined(vms)
213   long offset;
214 #endif
215   /****************************************************************************
216   * Check if a new cache structure can be allocated.  If the allocation(s)
217   * fail, process as if the maximum number of cache buffers has already been
218   * reached.
219   ****************************************************************************/
220   if (vFp->nBuffers < vFp->maxBuffers) {
221     cache = (vCACHE *) cdf_AllocateMemory (sizeof(vCACHE), NULL);
222     if (cache != NULL) {
223 #if defined(MICROSOFTC_700) && INCLUDEvMEMORY
224       if (useVmem)
225 	cache->ptr = (void *) AllocateVMemory (nCACHE_BUFFER_BYTEs);
226       else
227 #endif
228 	cache->ptr = cdf_AllocateMemory (nCACHE_BUFFER_BYTEs, NULL);
229       if (cache->ptr != NULL) {
230 	if (vFp->cacheHead == NULL) {
231 	  vFp->cacheHead = cache;
232 	  vFp->cacheTail = cache;
233 	  cache->next = NULL;
234 	  cache->prev = NULL;
235 	}
236 	else {
237 	  vFp->cacheHead->prev = cache;
238 	  cache->next = vFp->cacheHead;
239 	  vFp->cacheHead = cache;
240 	  cache->prev = NULL;
241 	}
242 	(vFp->nBuffers)++;
243 	return cache;
244       }
245       else {
246 	cdf_FreeMemory (cache, NULL);
247 	if (vFp->nBuffers == 0) return NULL;
248       }
249     }
250   }
251   /****************************************************************************
252   * The maximum number of cache buffers have already been created.  Scan the
253   * linked list of cache structures searching for the oldest buffer which has
254   * not been modified.  If one is found, it is moved to the head of the linked
255   * list.
256   ****************************************************************************/
257   for (cache = vFp->cacheTail; cache != NULL; cache = cache->prev) {
258      if (!cache->modified) {
259        if (cache != vFp->cacheHead) {
260 	 if (cache == vFp->cacheTail) {
261 	   cache->prev->next = NULL;
262 	   vFp->cacheTail = cache->prev;
263 	 }
264 	 else {
265 	   cache->prev->next = cache->next;
266 	   cache->next->prev = cache->prev;
267 	 }
268 	 vFp->cacheHead->prev = cache;
269 	 cache->next = vFp->cacheHead;
270 	 vFp->cacheHead = cache;
271 	 cache->prev = NULL;
272        }
273        return cache;
274      }
275   }
276   /****************************************************************************
277   * An unmodified buffer was not found.  The last buffer on the linked list
278   * will be paged back out to the file and then this cache structure is moved
279   * to the head of the linked list.
280   ****************************************************************************/
281   cache = vFp->cacheTail;
282 #if defined(vms)
283   nBytes = nCACHE_BUFFER_BYTEs;
284 #else
285   offset = nCACHE_BUFFER_BYTEs * cache->blockN;
286   nBytes = vFp->length - offset;
287   nBytes = MINIMUM (nBytes, nCACHE_BUFFER_BYTEs);
288 #endif
289   if (!WriteBlockFromCache(vFp,cache,(size_t)nBytes)) return NULL;
290   if (cache != vFp->cacheHead) {
291     cache->prev->next = NULL;
292     vFp->cacheTail = cache->prev;
293     vFp->cacheHead->prev = cache;
294     cache->next = vFp->cacheHead;
295     vFp->cacheHead = cache;
296     cache->prev = NULL;
297   }
298   (vFp->nPageOuts)++;
299   return cache;
300 }
301 
302 /******************************************************************************
303 * ExtendFile.
304 * Extend the file to a specified number of blocks.
305 ******************************************************************************/
306 
307 #if EXTEND_FILE
ExtendFile(vFp,toBlockN)308 static Logical ExtendFile (vFp, toBlockN)
309 vFILE *vFp;
310 long toBlockN;
311 {
312   vCACHE *cache; long blockN;
313   /****************************************************************************
314   * First check to see if the physical end-of-file must be extended out to the
315   * next multiple of the cache/block size.
316   ****************************************************************************/
317   if (vFp->phyLength > 0) {
318     long lastPhyBlockN = LASTphyBLOCKn (vFp);
319     long nBytes = vFp->phyLength - (nCACHE_BUFFER_BYTEs * lastPhyBlockN);
320     if (nBytes < nCACHE_BUFFER_BYTEs) {
321       cache = FindCache (vFp, lastPhyBlockN);
322       if (cache != NULL) {
323 	void *buffer = CACHEbufferREADfrom (cache);
324 	if (buffer == NULL) return FALSE;
325 	if (!vWrite(nCACHE_BUFFER_BYTEs * lastPhyBlockN,
326 		    buffer,nCACHE_BUFFER_BYTEs,vFp)) return FALSE;
327 	cache->modified = FALSE;
328       }
329       else {
330 	Byte buffer[nCACHE_BUFFER_BYTEs];
331 	if (!vRead(nCACHE_BUFFER_BYTEs * lastPhyBlockN,
332 		   buffer,(size_t)nBytes,vFp)) return FALSE;
333 #if CLEAR_BYTES
334 	ClearBytes (buffer, (int) nBytes, nCACHE_BUFFER_BYTEs - 1);
335 #endif
336 	if (!vWrite(nCACHE_BUFFER_BYTEs * lastPhyBlockN,
337 		    buffer,nCACHE_BUFFER_BYTEs,vFp)) return FALSE;
338       }
339       vFp->phyLength = nCACHE_BUFFER_BYTEs * (lastPhyBlockN + 1);
340     }
341   }
342   /****************************************************************************
343   * Then extend the file the remaining blocks.
344   ****************************************************************************/
345   for (blockN = LASTphyBLOCKn(vFp) + 1; blockN <= toBlockN; blockN++) {
346      cache = FindCache (vFp, blockN);
347      if (cache != NULL) {
348        void *buffer = CACHEbufferREADfrom (cache);
349        if (buffer == NULL) return FALSE;
350        if (!vWrite(nCACHE_BUFFER_BYTEs * blockN,
351 		   buffer,nCACHE_BUFFER_BYTEs,vFp)) return FALSE;
352        cache->modified = FALSE;
353      }
354      else {
355        Byte buffer[nCACHE_BUFFER_BYTEs];
356 #if CLEAR_BYTES
357        ClearBytes (buffer, 0, nCACHE_BUFFER_BYTEs - 1);
358 #endif
359        if (!vWrite(nCACHE_BUFFER_BYTEs * blockN,
360 		   buffer,nCACHE_BUFFER_BYTEs,vFp)) return FALSE;
361      }
362      vFp->phyLength = nCACHE_BUFFER_BYTEs * (blockN + 1);
363   }
364   return TRUE;
365 }
366 #endif
367 
368 /******************************************************************************
369 * PageIn.
370 * Page in a block from the file.  Returns pointer to cache structure used (or
371 * NULL if an error occurred).
372 ******************************************************************************/
373 
PageIn(vFp,blockN)374 static vCACHE *PageIn (vFp, blockN)
375 vFILE *vFp;
376 long blockN;
377 {
378   long offset, nBytes; vCACHE *cache; void *buffer;
379   cache = AllocateBuffer (vFp);
380   if (cache == NULL) return NULL;
381   offset = blockN * nCACHE_BUFFER_BYTEs;
382   nBytes = vFp->phyLength - offset;
383   nBytes = MINIMUM (nBytes, nCACHE_BUFFER_BYTEs);
384   buffer = CACHEbufferWRITEto (cache);
385   if (buffer == NULL) return NULL;
386   if (!vRead(offset,buffer,(size_t)nBytes,vFp)) return NULL;
387 #if CLEAR_BYTES
388   ClearBytes (buffer, (int) nBytes, nCACHE_BUFFER_BYTEs - 1);
389 #endif
390   cache->blockN = blockN;
391   cache->modified = FALSE;
392   (vFp->nPageIns)++;
393   return cache;
394 }
395 
396 /******************************************************************************
397 * WriteBlockFromCache.
398 * Write a block out to the file from a cache buffer.  Returns TRUE if
399 * successful, FALSE if an error occurred.
400 ******************************************************************************/
401 
WriteBlockFromCache(vFp,cache,nBytes)402 static Logical WriteBlockFromCache (vFp, cache, nBytes)
403 vFILE *vFp;
404 vCACHE *cache;
405 size_t nBytes;
406 {
407   long offset; void *buffer;
408   offset = nCACHE_BUFFER_BYTEs * cache->blockN;
409 #if EXTEND_FILE
410   if (offset > vFp->phyLength) {
411     if (!ExtendFile(vFp,cache->blockN-1)) return FALSE;
412   }
413 #endif
414   buffer = CACHEbufferREADfrom (cache);
415   if (buffer == NULL) return FALSE;
416   if (!vWrite(offset,buffer,nBytes,vFp)) return FALSE;
417   vFp->phyLength = MaxLong (vFp->phyLength, (long) (offset + nBytes));
418   return TRUE;
419 }
420 
421 /******************************************************************************
422 * WriteBlockFromBuffer.
423 * Write a block out to the file from the caller's buffer.  Returns TRUE if
424 * successful, FALSE if an error occurred.
425 ******************************************************************************/
426 
WriteBlockFromBuffer(vFp,blockN,buffer,nBytes)427 static Logical WriteBlockFromBuffer (vFp, blockN, buffer, nBytes)
428 vFILE *vFp;
429 long blockN;
430 void *buffer;
431 size_t nBytes;
432 {
433   long offset = nCACHE_BUFFER_BYTEs * blockN;
434 #if EXTEND_FILE
435   if (offset > vFp->phyLength) {
436     if (!ExtendFile(vFp,blockN-1)) return FALSE;
437   }
438 #endif
439   if (!vWrite(offset,buffer,nBytes,vFp)) return FALSE;
440   vFp->phyLength = MaxLong (vFp->phyLength, (long) (offset + nBytes));
441   return TRUE;
442 }
443 
444 /******************************************************************************
445 * vRead.
446 ******************************************************************************/
447 
vRead(offset,buffer,nBytes,vFp)448 static Logical vRead (offset, buffer, nBytes, vFp)
449 long offset;
450 void *buffer;
451 size_t nBytes;
452 vFILE *vFp;
453 {
454   int tryN;
455   /****************************************************************************
456   * Does the scratch file exist?  It doesn't make sense for it not to.
457   ****************************************************************************/
458   if (vFp->fp == NULL) return FALSE;
459   /****************************************************************************
460   * Tally a block read.
461   ****************************************************************************/
462   (vFp->nBlockReads)++;
463   /****************************************************************************
464   * Read the block.  Multiple attempts are made for optical disks.
465   ****************************************************************************/
466   for (tryN = 1; tryN <= vMAX_TRYs; tryN++) {
467      if (fseek(vFp->fp,offset,vSEEK_SET) == EOF) return FALSE;
468      if (fread(buffer,nBytes,1,vFp->fp) == 1) return TRUE;
469   }
470   return FALSE;
471 }
472 
473 /******************************************************************************
474 * vWrite.
475 ******************************************************************************/
476 
vWrite(offset,buffer,nBytes,vFp)477 static Logical vWrite(offset,buffer,nBytes,vFp)
478 long offset;
479 void *buffer;
480 size_t nBytes;
481 vFILE *vFp;
482 {
483   int tryN;
484 #if defined(__MWERKS__)
485   int ii;
486 #endif
487   /****************************************************************************
488   * Create the scratch file if necessary.  If so, the current file path is
489   * actually the scratch directory to be used.
490   ****************************************************************************/
491   if (vFp->fp == NULL) {
492     long i; char *tmpPath; size_t pathLength;
493     pathLength = strlen(vFp->path) + 1 + 8 + 1 + EXT_LEN;
494     tmpPath = (char *) cdf_AllocateMemory (pathLength + 1, NULL);
495     if (tmpPath == NULL) return FALSE;
496     for (i = 1; i <= MAX_TMP; i++) {
497        strcpyX (tmpPath, vFp->path, 0);
498        AppendToDir (tmpPath, "");
499        sprintf (EofS(tmpPath), "TMP%05ld.%s", i, vFp->scratchExt);
500        if (!IsReg(tmpPath)) {
501 	 FILE *fp = OpenFile (tmpPath, WRITE_PLUS_a_mode);
502 	 if (fp == NULL) {
503 	   cdf_FreeMemory (tmpPath, NULL);
504 	   return FALSE;
505 	 }
506 	 cdf_FreeMemory (vFp->path, NULL);
507 	 vFp->path = tmpPath;
508 	 vFp->fp = fp;
509 	 break;
510        }
511     }
512     if (vFp->fp == NULL) {	   /* Hardly seems likely but we'll check... */
513       cdf_FreeMemory (tmpPath, NULL);
514       return FALSE;
515     }
516   }
517   /****************************************************************************
518   * Tally a block write.
519   ****************************************************************************/
520   (vFp->nBlockWrites)++;
521 
522 #if defined(__MWERKS__)
523   ii = fseek(vFp->fp, (long)0, vSEEK_END);
524 #endif
525 
526   /****************************************************************************
527   * Write the block.  Multiple attempts are made for optical disks.
528   ****************************************************************************/
529   for (tryN = 1; tryN <= vMAX_TRYs; tryN++) {
530      if (fseek(vFp->fp,offset,vSEEK_SET) == EOF) return FALSE;
531      if (fwrite(buffer,nBytes,1,vFp->fp) == 1) return TRUE;
532   }
533   return FALSE;
534 }
535 
536 /******************************************************************************
537 * V_open.
538 * Open the file and setup vFILE structure.
539 ******************************************************************************/
540 
V_open(file_spec,a_mode)541 VISIBLE_PREFIX vFILE *V_open (file_spec, a_mode)
542 char *file_spec;        /* File specification. */
543 char *a_mode;           /* Access mode. */
544 {
545   FILE *fp;             /* Temporary file pointer. */
546   vFILE *vFp;           /* Pointer to vFILE structure. */
547 #if defined(vms)
548   struct STAT st;       /* Status block from `stat'. */
549 #endif
550   /****************************************************************************
551   * Open the file.
552   ****************************************************************************/
553   fp = OpenFile (file_spec, a_mode);
554   if (fp == NULL) return NULL;
555 #if defined(vms)
556   /****************************************************************************
557   * If the file is being opened in a mode which may require it to be extended
558   * (`r+' [read/write] or `a/a+' [append]), check that the EOF offset in the
559   * last block is zero (0).  If not, rewrite the last block out to the end.
560   * `r' is not checked because it is read only.
561   * `w/w+' is not checked because a new file (with EOF == 0) will have been
562   * created.
563   ****************************************************************************/
564   if (strstr(a_mode,"r+") || strchr(a_mode,'a')) {
565     long eof; size_t EOFoffsetInBlock;
566     if (fseek(fp,0,vSEEK_END) == EOF) {
567       fclose (fp);
568       return NULL;
569     }
570     eof = ftell (fp);
571     if (eof == EOF) {
572       fclose (fp);
573       return NULL;
574     }
575     EOFoffsetInBlock = eof % nCACHE_BUFFER_BYTEs;
576     if (EOFoffsetInBlock != 0) {
577       long offsetToLastBlock; char buffer[nCACHE_BUFFER_BYTEs]; size_t numitems; int i;
578       offsetToLastBlock = nCACHE_BUFFER_BYTEs * (eof / nCACHE_BUFFER_BYTEs);
579       if (fseek(fp,offsetToLastBlock,vSEEK_SET) == EOF) {
580 	fclose (fp);
581 	return NULL;
582       }
583       for (i = 0; i < nCACHE_BUFFER_BYTEs; i++) buffer[i] = 0;
584       if (fread(buffer,EOFoffsetInBlock,1,fp) != 1) {
585 	fclose (fp);
586 	return NULL;
587       }
588       if (fseek(fp,offsetToLastBlock,vSEEK_SET) == EOF) {
589 	fclose (fp);
590 	return NULL;
591       }
592       if (fwrite(buffer,nCACHE_BUFFER_BYTEs,1,fp) != 1) {
593 	fclose (fp);
594 	return NULL;
595       }
596       if (fclose(fp) == EOF) {
597 	fclose (fp);
598 	return NULL;
599       }
600       fp = OpenFile (file_spec, a_mode);
601       if (fp == NULL) return NULL;
602     }
603   }
604 #endif
605   /****************************************************************************
606   * Allocate and load vFILE structure.
607   ****************************************************************************/
608   vFp = (vFILE *) cdf_AllocateMemory (sizeof(vFILE), NULL);
609   if (vFp == NULL) {
610     fclose (fp);
611     return NULL;
612   }
613   vFp->magic_number = VSTREAM_MAGIC_NUMBER;
614   vFp->fp = fp;
615   vFp->path = (char *) cdf_AllocateMemory (strlen(file_spec) + 1, NULL);
616   if (vFp->path == NULL) {
617     cdf_FreeMemory (vFp, NULL);
618     fclose (fp);
619     return NULL;
620   }
621   else
622     strcpyX (vFp->path, file_spec, 0);
623   vFp->scratch = FALSE;
624   vFp->error = FALSE;
625   vFp->eof = FALSE;
626   vFp->cacheHead = NULL;
627   vFp->cacheTail = NULL;
628   vFp->maxBuffers = DEFAULT_nCACHE_BUFFERs;
629   vFp->nBuffers = 0;
630   vFp->nBlockReads = 0;
631   vFp->nBlockWrites = 0;
632   vFp->nV_reads = 0;
633   vFp->nV_writes = 0;
634   vFp->nPageIns = 0;
635   vFp->nPageOuts = 0;
636   vFp->GDR = NULL;
637   vFp->GDR64 = NULL;
638   vFp->ADRList = NULL;
639   vFp->ADRList64 = NULL;
640   /****************************************************************************
641   * Determine length of file and set current offset.
642   ****************************************************************************/
643 #if defined(vms)
644   /****************************************************************************
645   * This method is used on VMS systems in case the file is on a CD-ROM.  Some
646   * VMS CD-ROM drivers do not correctly handle the EOF marker of a file.
647   * `stat' might fail, however, if the file specification contains a DECnet
648   * node.  If `stat' fails, try the other method before giving up.
649   ****************************************************************************/
650   if (stat(file_spec,&st) == 0) {
651     vFp->length = st.st_size;
652     vFp->phyLength = st.st_size;
653   }
654   else {
655 #endif
656     if (fseek(vFp->fp,0,vSEEK_END) == EOF) {
657       cdf_FreeMemory (vFp->path, NULL);
658       cdf_FreeMemory (vFp, NULL);
659       fclose (vFp->fp);
660       return NULL;
661     }
662     vFp->length = ftell (vFp->fp);
663     if (vFp->length == EOF) {
664       cdf_FreeMemory (vFp->path, NULL);
665       cdf_FreeMemory (vFp, NULL);
666       fclose (vFp->fp);
667       return NULL;
668     }
669     vFp->phyLength = vFp->length;
670 #if defined(vms)
671   }
672 #endif
673   vFp->offset = BOO(strchr(a_mode,'a') == NULL,0,vFp->length);
674   /****************************************************************************
675   * Return pointer to vFILE structure.
676   ****************************************************************************/
677   return vFp;
678 }
679 
680 /******************************************************************************
681 * V_scratch.
682 * Creates a scratch file.  Note that the file is not actually created until a
683 * block needs to be paged out.
684 ******************************************************************************/
685 
V_scratch(directory,extension)686 VISIBLE_PREFIX vFILE *V_scratch (directory, extension)
687 char *directory;	/* Directory in which to create the scratch file (if
688 			   necessary).  If NULL, use the current directory. */
689 char *extension;	/* Extension to use for the scratch file.  If NULL,
690 			   `.ich' is used. */
691 {
692   vFILE *vFp;           /* Pointer to vFILE structure. */
693   /****************************************************************************
694   * Allocate and load vFILE structure.
695   ****************************************************************************/
696   vFp = (vFILE *) cdf_AllocateMemory (sizeof(vFILE), NULL);
697   if (vFp == NULL) return NULL;
698   vFp->magic_number = VSTREAM_MAGIC_NUMBER;
699   vFp->fp = NULL;
700   vFp->fh = 0;
701   vFp->path = (char *) cdf_AllocateMemory (BOO(directory == NULL,
702 					   0,strlen(directory)) + 1, NULL);
703   if (vFp->path == NULL) {
704     cdf_FreeMemory (vFp, NULL);
705     return NULL;
706   }
707   else
708     strcpyX (vFp->path, BOO(directory == NULL,"",directory), 0);
709   strcpyX (vFp->scratchExt, BOO(extension == NULL,"ich",extension), EXT_LEN);
710   vFp->scratch = TRUE;
711   vFp->error = FALSE;
712   vFp->eof = FALSE;
713   vFp->cacheHead = NULL;
714   vFp->cacheTail = NULL;
715   vFp->maxBuffers = DEFAULT_nCACHE_BUFFERs;
716   vFp->nBuffers = 0;
717   vFp->nBlockReads = 0;
718   vFp->nBlockWrites = 0;
719   vFp->nV_reads = 0;
720   vFp->nV_writes = 0;
721   vFp->nPageIns = 0;
722   vFp->nPageOuts = 0;
723   vFp->length = 0;
724   vFp->length64 = (OFF_T) 0;
725   vFp->phyLength = 0;
726   vFp->phyLength64 = (OFF_T) 0;
727   vFp->offset = 0;
728   vFp->offset64 = (OFF_T) 0;
729   vFp->GDR = NULL;
730   vFp->GDR64 = NULL;
731   vFp->ADRList = NULL;
732   vFp->ADRList64 = NULL;
733 
734   /****************************************************************************
735   * Return pointer to vFILE structure.
736   ****************************************************************************/
737   return vFp;
738 }
739 
740 /******************************************************************************
741 * V_setcache.
742 * Set number of cache buffers.  This can be done at any time after the file
743 * is opened.  Note that in some cases the new cache size may be the same as
744 * the old cache size (do nothing).
745 ******************************************************************************/
746 
V_setcache(vFp,maxBuffers)747 VISIBLE_PREFIX int V_setcache (vFp, maxBuffers)
748 vFILE *vFp;             /* Pointer to vFILE structure. */
749 int maxBuffers;         /* New maximum number of cache buffers. */
750 {
751   if (vFp == NULL) return EOF;
752   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
753   if (vFp->error) return EOF;
754   if (maxBuffers < 1) return EOF;
755   if (maxBuffers > vFp->maxBuffers) {
756     /**************************************************************************
757     * The number of cache buffers is increasing.
758     **************************************************************************/
759     vFp->maxBuffers = maxBuffers;
760   }
761   else {
762     if (maxBuffers < vFp->maxBuffers) {
763       /************************************************************************
764       * The number of cache buffers is decreasing - flush to disk and free
765       * the buffers which are going away.
766       ************************************************************************/
767       vCACHE *cache; int count;
768       if (vFp->nBuffers > maxBuffers) {
769 	for (count = 1,
770 	     cache = vFp->cacheHead;
771 	     count < maxBuffers; count++) cache = cache->next;
772 	if (!FlushCache(vFp,cache->next)) {
773 	  vFp->error = TRUE;
774 	  return EOF;
775 	}
776 	FreeCache (cache->next);
777 	cache->next = NULL;
778 	vFp->cacheTail = cache;
779 	vFp->nBuffers = maxBuffers;
780       }
781       vFp->maxBuffers = maxBuffers;
782     }
783   }
784   return 0;
785 }
786 
787 /******************************************************************************
788 * V_seek.
789 * Seek to a position in the file.
790 ******************************************************************************/
791 
V_seek(vFp,offset,direction)792 VISIBLE_PREFIX int V_seek (vFp, offset, direction)
793 vFILE *vFp;             /* Pointer to vFILE structure. */
794 long offset;            /* New current file offset. */
795 int direction;          /* Reference for offset. */
796 {
797   if (vFp == NULL) return EOF;
798   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
799   if (vFp->error) return EOF;
800   vFp->eof = FALSE;		/* Cleared before proceeding. */
801   switch (direction) {
802     case vSEEK_SET:
803       if (offset < 0) return EOF;
804       vFp->offset = offset;
805       break;
806     case vSEEK_CUR:
807       if (vFp->offset + offset < 0) return EOF;
808       vFp->offset += offset;
809       break;
810     case vSEEK_END:
811       vFp->offset = vFp->length;
812       break;
813     default:
814       return EOF;
815   }
816   return 0;
817 }
818 
819 /******************************************************************************
820 * V_tell.
821 * Return current offset (position) in file.  This is the byte offset one past
822 * the last byte that exists.
823 ******************************************************************************/
824 
V_tell(vFp)825 VISIBLE_PREFIX long V_tell (vFp)
826 vFILE *vFp;             /* Pointer to vFILE structure. */
827 {
828   if (vFp == NULL) return EOF;
829   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
830   if (vFp->error) return EOF;
831   return vFp->offset;
832 }
833 
834 /******************************************************************************
835 * V_eof.
836 * Returns non-zero if EOF indicator is set.  A read at the EOF must occur
837 * before the EOF indicator will be set (just like `feof').
838 ******************************************************************************/
839 
V_eof(vFp)840 VISIBLE_PREFIX int V_eof (vFp)
841 vFILE *vFp;	/* Pointer to vFILE structure. */
842 {
843   if (vFp == NULL) return EOF;
844   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
845   if (vFp->eof) return 1;
846   return 0;
847 }
848 
849 /******************************************************************************
850 * V_error.
851 * Returns non-zero if error indicator is set.
852 ******************************************************************************/
853 
V_error(vFp)854 VISIBLE_PREFIX int V_error (vFp)
855 vFILE *vFp;	/* Pointer to vFILE structure. */
856 {
857   if (vFp == NULL) return EOF;
858   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
859   if (vFp->error) return 1;
860   return 0;
861 }
862 
863 /******************************************************************************
864 * V_read.
865 ******************************************************************************/
866 
V_read(buffer,item_size,n_items,vFp)867 VISIBLE_PREFIX size_t V_read (buffer, item_size, n_items, vFp)
868 void *buffer;           /* Pointer to buffer. */
869 size_t item_size;       /* Size (in bytes) of each item to read. */
870 size_t n_items;         /* Number of items to read. */
871 vFILE *vFp;             /* Pointer to vFILE structure. */
872 {
873   size_t nBytesX;       /* Total number of bytes in buffer. */
874   size_t nBytes;        /* Total number of bytes to read. */
875   long remainingItems;	/* Number of items remaining after the offset. */
876   size_t nItems;	/* Number of items to actually be read. */
877   long firstBlockN;     /* First block involved in read. */
878   long lastBlockN;      /* Last block involved in read. */
879   int bufferOffset;     /* Offset (bytes) into buffer. */
880   long fileOffset;      /* Offset (bytes) into file. */
881   size_t xBytes;        /* Number of bytes in a transfer. */
882   long blockN;          /* Block number in file (from 0). */
883   long atBlockN;        /* Block number in file (from 0) at which to read. */
884   vCACHE *cache;        /* Pointer to cache structure. */
885   Byte *cBuffer;        /* Pointer to cache buffer. */
886   int remainingBytes;	/* Number of bytes remaining in a block. */
887   /****************************************************************************
888   * Validate read.
889   ****************************************************************************/
890 #if defined(DEBUG)
891   if (getenv("READ.ERROR") != NULL) return 0;
892 #endif
893   if (vFp == NULL) return 0;
894   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return 0;
895   if (vFp->error) return 0;
896   if ((int) item_size <= 0 || (int) n_items <= 0) return 0;
897   remainingItems = (vFp->length - vFp->offset) / ((long) item_size);
898   if (remainingItems < 1) {
899     vFp->eof = TRUE;
900     vFp->offset = vFp->length;
901     return 0;
902   }
903   if ((long) n_items > remainingItems) {
904     nItems = (size_t) remainingItems;
905     vFp->eof = TRUE;		/* File offset set to EOF before returning. */
906   }
907   else
908     nItems = n_items;
909   nBytes = nItems * item_size;
910   nBytesX = n_items * item_size;
911   (vFp->nV_reads)++;
912   /****************************************************************************
913   * Read from first block...
914   ****************************************************************************/
915   firstBlockN = vFp->offset / nCACHE_BUFFER_BYTEs;
916   bufferOffset = (int) (vFp->offset % nCACHE_BUFFER_BYTEs);
917   remainingBytes = nCACHE_BUFFER_BYTEs - bufferOffset;
918   xBytes = MINIMUM (nBytes, (size_t) remainingBytes);
919   if (xBytes > nBytesX) return 0;
920   if (bufferOffset > 0 || xBytes < nCACHE_BUFFER_BYTEs) {
921     cache = FindCache (vFp, firstBlockN);
922     if (cache == NULL) cache = PageIn (vFp, firstBlockN);
923     if (cache == NULL) {
924       vFp->error = TRUE;
925       return 0;
926     }
927     cBuffer = CACHEbufferREADfrom (cache);
928     if (cBuffer == NULL) {
929       vFp->error = TRUE;
930       return 0;
931     }
932     memmove (buffer, cBuffer + bufferOffset, xBytes);
933     buffer = (Byte *) buffer + xBytes;
934     atBlockN = firstBlockN + 1;
935   }
936   else
937     atBlockN = firstBlockN;
938   /****************************************************************************
939   * Read from remaining blocks...
940   ****************************************************************************/
941   lastBlockN = (vFp->offset + nBytes - 1) / nCACHE_BUFFER_BYTEs;
942   for (blockN = atBlockN; blockN <= lastBlockN; blockN++) {
943      xBytes = (size_t) (vFp->offset + nBytes - (nCACHE_BUFFER_BYTEs * blockN));
944      xBytes = MINIMUM (xBytes, nCACHE_BUFFER_BYTEs);
945      if (xBytes > nBytesX) return 0;
946      cache = FindCache (vFp, blockN);
947      if (cache != NULL) {
948        cBuffer = CACHEbufferREADfrom (cache);
949        if (cBuffer == NULL) {
950 	 vFp->error = TRUE;
951 	 return 0;
952        }
953        memmove (buffer, cBuffer, xBytes);
954      }
955      else {
956        if (xBytes < nCACHE_BUFFER_BYTEs) {
957 	 cache = PageIn (vFp, blockN);
958 	 if (cache == NULL) {
959 	   vFp->error = TRUE;
960 	   return 0;
961 	 }
962 	 cBuffer = CACHEbufferREADfrom (cache);
963 	 if (cBuffer == NULL) {
964 	   vFp->error = TRUE;
965 	   return 0;
966 	 }
967 	 memmove (buffer, cBuffer, xBytes);
968        }
969        else {
970 	 fileOffset = nCACHE_BUFFER_BYTEs * blockN;
971 	 if (!vRead(fileOffset,buffer,nCACHE_BUFFER_BYTEs,vFp)) {
972 	   vFp->error = TRUE;
973 	   return 0;
974 	 }
975        }
976      }
977      buffer = (Byte *) buffer + xBytes;
978   }
979   /****************************************************************************
980   * Increment current file offset or set to EOF if the EOF indicator was set.
981   ****************************************************************************/
982   vFp->offset = BOO(vFp->eof,vFp->length,vFp->offset + nBytes);
983   return nItems;
984 }
985 
986 /******************************************************************************
987 * V_write.
988 ******************************************************************************/
989 
V_write(buffer,item_size,n_items,vFp)990 VISIBLE_PREFIX size_t V_write (buffer, item_size, n_items, vFp)
991 void *buffer;           /* Pointer to buffer. */
992 size_t item_size;       /* Size (in bytes) of each item to write. */
993 size_t n_items;         /* Number of items to write. */
994 vFILE *vFp;             /* Pointer to vFILE structure. */
995 {
996   size_t nBytesX;       /* Total number of bytes in buffer. */
997   size_t nBytes;        /* Total number of bytes in write. */
998   long firstBlockN;     /* First block involved in write. */
999   long lastBlockN;      /* Last block involved in write. */
1000   int bufferOffset;     /* Offset (bytes) into buffer. */
1001   long blockN;          /* Block number in file (from 0). */
1002   long atBlockN;        /* Block number in file (from 0) at which to write. */
1003   size_t xBytes;        /* Number of bytes in a transfer. */
1004   vCACHE *cache;        /* Pointer to cache structure. */
1005   Byte *cBuffer;        /* Pointer to cache buffer. */
1006   size_t nBytesInBlock;	/* Number of bytes to the end of the block. */
1007   /****************************************************************************
1008   * Validate write.
1009   ****************************************************************************/
1010 #if defined(DEBUG)
1011   if (getenv("WRITE.ERROR") != NULL) return 0;
1012 #endif
1013   if (vFp == NULL) return 0;
1014   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return 0;
1015   if (vFp->error) return 0;
1016   if ((int) item_size <= 0 || (int) n_items <= 0) return 0;
1017   vFp->eof = FALSE;		/* Cleared before proceeding. */
1018   nBytes = item_size * n_items;
1019   nBytesX = n_items * item_size;
1020   if (nBytes < 1) return 0;
1021   (vFp->nV_writes)++;
1022   /****************************************************************************
1023   * Write to first block...
1024   * Note that if this is a scratch file, the first block is always placed in
1025   * the cache (even if a full block).
1026   ****************************************************************************/
1027   firstBlockN = vFp->offset / nCACHE_BUFFER_BYTEs;
1028   bufferOffset = (int) (vFp->offset % nCACHE_BUFFER_BYTEs);
1029   nBytesInBlock = nCACHE_BUFFER_BYTEs - bufferOffset;
1030   xBytes = MINIMUM (nBytes, nBytesInBlock);
1031   if (xBytes > nBytesX) return 0;
1032   if (vFp->scratch || bufferOffset > 0 || xBytes < nCACHE_BUFFER_BYTEs) {
1033     cache = FindCache (vFp, firstBlockN);
1034     if (cache == NULL) {
1035       if (firstBlockN <= LASTphyBLOCKn(vFp)) {
1036 	cache = PageIn (vFp, firstBlockN);
1037 	if (cache == NULL) {
1038 	  vFp->error = TRUE;
1039 	  return 0;
1040 	}
1041       }
1042       else {
1043 	cache = AllocateBuffer (vFp);
1044 	if (cache == NULL) {
1045 	  vFp->error = TRUE;
1046 	  return 0;
1047 	}
1048 	cache->blockN = firstBlockN;
1049 #if CLEAR_BYTES
1050 	cBuffer = CACHEbufferWRITEto (cache);
1051 	if (cBuffer == NULL) {
1052 	  vFp->error = TRUE;
1053 	  return 0;
1054 	}
1055 	ClearBytes (cBuffer, 0, bufferOffset - 1);
1056 	ClearBytes (cBuffer, (int) (bufferOffset + xBytes),
1057 		    nCACHE_BUFFER_BYTEs - 1);
1058 #endif
1059       }
1060     }
1061     cBuffer = CACHEbufferWRITEto (cache);
1062     if (cBuffer == NULL) {
1063       vFp->error = TRUE;
1064       return 0;
1065     }
1066     memmove (cBuffer + bufferOffset, buffer, xBytes);
1067     cache->modified = TRUE;
1068     vFp->length = MaxLong (vFp->length,(long) (vFp->offset + xBytes));
1069     buffer = (Byte *) buffer + xBytes;
1070     atBlockN = firstBlockN + 1;
1071   }
1072   else
1073     atBlockN = firstBlockN;
1074   /****************************************************************************
1075   * Write to remaining blocks...
1076   ****************************************************************************/
1077   lastBlockN = (vFp->offset + nBytes - 1) / nCACHE_BUFFER_BYTEs;
1078   for (blockN = atBlockN; blockN <= lastBlockN; blockN++) {
1079      xBytes = (size_t) (vFp->offset + nBytes - (nCACHE_BUFFER_BYTEs * blockN));
1080      xBytes = MINIMUM (xBytes, nCACHE_BUFFER_BYTEs);
1081      if (xBytes > nBytesX) return 0;
1082      /*************************************************************************
1083      * Is this block in the cache?  If so, move the number of bytes to be
1084      * written at this block to its cache buffer.
1085      *************************************************************************/
1086      cache = FindCache (vFp, blockN);
1087      if (cache != NULL) {
1088        cBuffer = CACHEbufferWRITEto (cache);
1089        if (cBuffer == NULL) {
1090 	 vFp->error = TRUE;
1091 	 return 0;
1092        }
1093        memmove (cBuffer, buffer, xBytes);
1094        cache->modified = TRUE;
1095      }
1096      else {
1097        /***********************************************************************
1098        * This block is not in the cache.  Is a partial block to be written?
1099        * Note that if this is a scratch file, the block is always placed in
1100        * the cache (even if not a partial block).
1101        ***********************************************************************/
1102        if (vFp->scratch || xBytes < nCACHE_BUFFER_BYTEs) {
1103 	 if (blockN <= LASTphyBLOCKn(vFp)) {
1104 	   cache = PageIn (vFp, blockN);
1105 	   if (cache == NULL) {
1106 	     vFp->error = TRUE;
1107 	     return 0;
1108 	   }
1109 	 }
1110 	 else {
1111 	   cache = AllocateBuffer (vFp);
1112 	   if (cache == NULL) {
1113 	     vFp->error = TRUE;
1114 	     return 0;
1115 	   }
1116 	   cache->blockN = blockN;
1117 #if CLEAR_BYTES
1118 	   cBuffer = CACHEbufferWRITEto (cache);
1119 	   if (cBuffer == NULL) {
1120 	     vFp->error = TRUE;
1121 	     return 0;
1122 	   }
1123 	   ClearBytes (cBuffer, (int) xBytes, nCACHE_BUFFER_BYTEs - 1);
1124 #endif
1125 	 }
1126 	 cBuffer = CACHEbufferWRITEto (cache);
1127 	 if (cBuffer == NULL) {
1128 	   vFp->error = TRUE;
1129 	   return 0;
1130 	 }
1131 	 memmove (cBuffer, buffer, xBytes);
1132 	 cache->modified = TRUE;
1133        }
1134        else {
1135 	 /*********************************************************************
1136          * A full block is to be written.
1137 	 *********************************************************************/
1138 	 if (!WriteBlockFromBuffer(vFp,blockN,buffer,nCACHE_BUFFER_BYTEs)) {
1139 	   vFp->error = TRUE;
1140 	   return 0;
1141 	 }
1142        }
1143      }
1144      vFp->length = MaxLong (vFp->length,
1145 			    (long) ((nCACHE_BUFFER_BYTEs * blockN) + xBytes));
1146      buffer = (Byte *) buffer + xBytes;
1147   }
1148   /****************************************************************************
1149   * Increment current file offset.
1150   ****************************************************************************/
1151   vFp->offset += nBytes;
1152   return n_items;
1153 }
1154 
1155 /******************************************************************************
1156 * V_getc.
1157 ******************************************************************************/
1158 
V_getc(fp)1159 VISIBLE_PREFIX int V_getc (fp)
1160 vFILE *fp;
1161 {
1162   uByte tmp;
1163   if (V_read(&tmp,1,1,fp) != 1) return EOF;
1164   return ((int) tmp);
1165 }
1166 
1167 /******************************************************************************
1168 * V_putc.
1169 ******************************************************************************/
1170 
V_putc(value,fp)1171 VISIBLE_PREFIX int V_putc (value, fp)
1172 int value;
1173 vFILE *fp;
1174 {
1175   uByte tmp = (uByte) value;
1176   if (V_write(&tmp,1,1,fp) != 1) return EOF;
1177   return value;
1178 }
1179 
1180 /******************************************************************************
1181 * V_clear.
1182 * Marks all cache buffers as unmodified.  This is used with scratch files to
1183 * prevent blocks of unwanted data from being paged out to disk.
1184 ******************************************************************************/
1185 
V_clear(vFp)1186 VISIBLE_PREFIX int V_clear (vFp)
1187 vFILE *vFp;             /* Pointer to vFILE structure. */
1188 {
1189   vCACHE *cache;
1190   if (vFp == NULL) return EOF;
1191   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
1192   if (vFp->error) return EOF;
1193   for (cache = vFp->cacheHead; cache != NULL; cache = cache->next) {
1194      cache->modified = FALSE;
1195   }
1196   return 0;
1197 }
1198 
1199 /******************************************************************************
1200 * V_flush.
1201 * Flush the file to disk.
1202 ******************************************************************************/
1203 
V_flush(vFp)1204 VISIBLE_PREFIX int V_flush (vFp)
1205 vFILE *vFp;             /* Pointer to vFILE structure. */
1206 {
1207   /****************************************************************************
1208   * Validate.
1209   ****************************************************************************/
1210   if (vFp == NULL) return EOF;
1211   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
1212   if (vFp->error) return EOF;
1213   /****************************************************************************
1214   * Flush cache buffers.  If this is a scratch file, this will cause the file
1215   * to be created if it hasn't been already (unless nothing has been written).
1216   ****************************************************************************/
1217   if (!FlushCache(vFp,vFp->cacheHead)) {
1218     vFp->error = TRUE;
1219     return EOF;
1220   }
1221   /****************************************************************************
1222   * Flush file.  Note that the file will not be flushed if this is a scratch
1223   * file to which nothing has been written.
1224   ****************************************************************************/
1225   if (vFp->fp != NULL) {
1226     if (fflush(vFp->fp) == EOF) {
1227       vFp->error = TRUE;
1228       return EOF;
1229     }
1230   }
1231   /****************************************************************************
1232   * Return success.
1233   ****************************************************************************/
1234   return 0;
1235 }
1236 
1237 /******************************************************************************
1238 * V_close.
1239 * Returns EOF if an error occurred.
1240 ******************************************************************************/
1241 
V_close(vFp,CDF,vStats)1242 VISIBLE_PREFIX int V_close (vFp, CDF, vStats)
1243 vFILE *vFp;             /* Pointer to vFILE structure. */
1244 struct CDFstruct *CDF;  /* Indicator whether to perform check sum operation. */
1245 vSTATS *vStats;         /* Pointer to statistics structure. */
1246 {
1247   Logical error = FALSE;        /* Has an error occurred? */
1248   /****************************************************************************
1249   * Check if a valid pointer to a vFILE structure.
1250   ****************************************************************************/
1251   if (vFp == NULL) return EOF;
1252   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
1253   /****************************************************************************
1254   * Write cache buffers.  If this is a scratch file, this will cause the file
1255   * to be created if it hasn't been already (unless nothing has been written).
1256   ****************************************************************************/
1257   if (!FlushCache(vFp,vFp->cacheHead)) error = TRUE;
1258   /****************************************************************************
1259   * Close the file.  Note that the file will not be closed if this is a
1260   * scratch file to which nothing has been written.
1261   ****************************************************************************/
1262   if (vFp->fp != NULL) {
1263     if (CDF != NULL && (!CDF->readOnly || CDF->status == READ_WRITE) &&
1264         CDF->singleFile && (CDF->checksum != NONE_CHECKSUM)) {
1265 /*      if (!FLUSHv(vFp)) error = TRUE; */
1266       if (!CDFAddChecksum(CDF)) error = TRUE;
1267     }
1268     if (fclose(vFp->fp) == EOF) error = TRUE;
1269   }
1270   /****************************************************************************
1271   * Pass back statistics (if requested).
1272   ****************************************************************************/
1273   if (vStats != NULL) {
1274     vStats->maxBuffers = vFp->maxBuffers;
1275     vStats->nBuffers = vFp->nBuffers;
1276     vStats->nV_reads = vFp->nV_reads;
1277     vStats->nV_writes = vFp->nV_writes;
1278     vStats->nBlockReads = vFp->nBlockReads;
1279     vStats->nBlockWrites = vFp->nBlockWrites;
1280     vStats->nPageIns = vFp->nPageIns;
1281     vStats->nPageOuts = vFp->nPageOuts;
1282   }
1283   /****************************************************************************
1284   * Deallocate cache and vFILE structure.
1285   ****************************************************************************/
1286   FreeCache (vFp->cacheHead);
1287   cdf_FreeMemory (vFp->path, NULL);
1288   cdf_FreeMemory (vFp, NULL);
1289   /****************************************************************************
1290   * Return status.
1291   ****************************************************************************/
1292   return BOO(error,EOF,0);
1293 }
1294 
1295 /******************************************************************************
1296 * V_delete.
1297 * Returns EOF if an error occurred.
1298 ******************************************************************************/
1299 
V_delete(vFp,vStats)1300 VISIBLE_PREFIX int V_delete (vFp, vStats)
1301 vFILE *vFp;             /* Pointer to vFILE structure. */
1302 vSTATS *vStats;         /* Pointer to statistics structure. */
1303 {
1304   Logical error = FALSE;        /* Has an error occurred? */
1305   /****************************************************************************
1306   * Check if a valid pointer to a vFILE structure.
1307   ****************************************************************************/
1308   if (vFp == NULL) return EOF;
1309   if (vFp->magic_number != VSTREAM_MAGIC_NUMBER) return EOF;
1310   /****************************************************************************
1311   * Close the file.
1312   ****************************************************************************/
1313   if (vFp->fp != NULL) {
1314     if (fclose(vFp->fp) == EOF) error = TRUE;
1315   }
1316   /****************************************************************************
1317   * Delete the file (unless it was never created).
1318   ****************************************************************************/
1319   if (vFp->fp != NULL) {
1320     if (!DeleteFile(vFp->path)) error = TRUE;
1321   }
1322   /****************************************************************************
1323   * Pass back statistics (if requested).
1324   ****************************************************************************/
1325   if (vStats != NULL) {
1326     vStats->maxBuffers = vFp->maxBuffers;
1327     vStats->nBuffers = vFp->nBuffers;
1328     vStats->nV_reads = vFp->nV_reads;
1329     vStats->nV_writes = vFp->nV_writes;
1330     vStats->nBlockReads = vFp->nBlockReads;
1331     vStats->nBlockWrites = vFp->nBlockWrites;
1332     vStats->nPageIns = vFp->nPageIns;
1333     vStats->nPageOuts = vFp->nPageOuts;
1334   }
1335   /****************************************************************************
1336   * Deallocate cache and vFILE structure.
1337   ****************************************************************************/
1338   FreeCache (vFp->cacheHead);
1339   cdf_FreeMemory (vFp->path, NULL);
1340   cdf_FreeMemory (vFp, NULL);
1341   /****************************************************************************
1342   * Return status.
1343   ****************************************************************************/
1344   return BOO(error,EOF,0);
1345 }
1346