source: gsdl/trunk/packages/isis-gdl/File.cpp@ 14171

Last change on this file since 14171 was 6127, checked in by mdewsnip, 20 years ago

IsisGdl package for reading CDS/ISIS databases. Provided by Jean-Claude Dauphin at UNESCO, and modified slightly to compile and run under Linux.

  • Property svn:keywords set to Author Date Id Revision
File size: 20.9 KB
Line 
1/**********************************************************************
2 *
3 * File.cpp
4 * Copyright (C) 2003 UNESCO
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26////////////////////////////////////////////////////////////
27// File.cpp File object methods
28////////////////////////////////////////////////////////////
29#include "stdafx.h"
30
31
32#include <assert.h>
33#include "CRC32.h"
34#include "File.h"
35
36
37
38
39static TCHAR closed_file_name[] = _T("closed-file");
40
41static const int MaxExceptionMessages = 34;
42static const TCHAR *fileExceptionMsg[MaxExceptionMessages] =
43{
44 _T("File exception: No exception reported"), // FILE_NO_ERROR
45 _T("File exception: Checksum error"), // FILE_CHECKSUM_ERROR
46 _T("File exception: Error closing file"), // FILE_CLOSE_ERROR
47 _T("File exception: File is corrupted"),
48 _T("File exception: Error creating file"), // FILE_CREATION_ERROR
49 _T("File exception: File already exists"), // FILE_EXISTS
50 _T("File exception: Trying to use a closed file"), // FILE_NOT_OPEN_ERROR
51 _T("File exception: File not ready for reading/writing"), // FILE_NOT_READY
52 _T("File exception: Could not write to file"), // FILE_NOT_WRITEABLE
53 _T("File exception: Error opening file"), // FILE_OPEN_ERROR
54 _T("File exception: Cannot obtain a file position"), // FILE_POSITION_ERROR
55 _T("File exception: Error reading from file"), // FILE_READ_ERROR
56 _T("File exception: Error seeking in file"), // FILE_SEEK_ERROR
57 _T("File exception: Error writing to file"), // FILE_WRITE_ERROR
58
59 _T("Fmgr exception: No database open"), // FMGR_NO_DATABASE_OPEN
60 _T("Fmgr exception: No such file exists"), // FMGR_NO_FILE_EXISTS
61 _T("Fmgr exception: No objects exist"), // FMGR_NO_OBJECTS_EXIST
62 _T("Fmgr exception: Accessing a null pointer"), // FMGR_NULL_PTR
63 _T("Fmgr exception: Object already exists"), // FMGR_OBJECT_EXISTS
64 _T("Fmgr exception: Another object is referencing this file"), // FMGR_OBJECT_EXISTS
65 _T("Fmgr exception: Math overflow error"), // FMGR_OVERFLOW
66 _T("Fmgr exception: Parse error"), // FMGR_PARSE_ERROR
67 _T("Fmgr exception: Invalid path"), // FMGR_PATH_ERROR
68 _T("Fmgr exception: Trying to write to a read-only file"), // FMGR_READ_ONLY_FILE
69 _T("Fmgr exception: Stack empty"), // FMGR_STACK_EMPTY
70 _T("Fmgr exception: Stack full"), // FMGR_STACK_FULL
71 _T("Fmgr exception: Synchronization Error"), // FMGR_SYNC_ERROR
72 _T("Fmgr exception: Math under-flow error"), // FMGR_UNDERFLOW
73 _T("Fmgr exception: Wrong file type"), // FMGR_WRONG_FILE_TYPE
74
75 // Persistent lock error messages
76 _T("Fmgr exception: Invalid lock type specified"), // FMGR_INVALID_LOCK_TYPE
77 _T("Fmgr exception: Cannot access the file lock header"), // FMGR_FILELOCK_ACCESS_ERROR
78 _T("Fmgr exception: Cannot lock the file"), // FMGR_FILELOCK_ERROR
79 _T("Fmgr exception: Cannot access the record lock header"), // FMGR_RECORDLOCK_ACCESS_ERROR
80 _T("Fmgr exception: Cannot lock the specified record") // FMGR_RECORDLOCK_ERROR
81};
82
83//---------------------------------------------------------------------------
84// CFileBase::CFileBase()
85//
86// Default constructor, makes a File object that isn't attached to a file
87//---------------------------------------------------------------------------
88CFileBase::CFileBase()
89{
90 _tcscpy(file_name, closed_file_name); // Set the initial file name
91 fp = 0;
92
93 // Reset the last reported error and the file status members
94 file_error = FILE_NO_ERROR;
95 is_open = 0;
96 is_ok = 0;
97 ready_for_reading = 0;
98 ready_for_writing = 0;
99}
100
101//---------------------------------------------------------------------------
102// CFileBase::~CFileBase()
103//
104// Destructor for CFileBase file object
105//---------------------------------------------------------------------------
106CFileBase::~CFileBase()
107{
108 Close();
109}
110
111//---------------------------------------------------------------------------
112// const char* CFileBase::FileExceptionMessage()
113//
114// Returns a null terminated string that can / be used to log or print a
115// file exception.
116//---------------------------------------------------------------------------
117const TCHAR *CFileBase::FileExceptionMessage()
118{
119 assert(file_error<MaxExceptionMessages);
120 return fileExceptionMsg[file_error];
121}
122
123//--------------------------------------------------------------------------------
124// int Create(const char* fname)
125//
126// Creates a new file, truncate the file if it already exists. The file open for
127// Read/Write access.
128// Returns a non-zero value to indicate an error condition or zero if successful.
129//--------------------------------------------------------------------------------
130FileError CFileBase::Create(const TCHAR *fname)
131{
132 // Close any open files
133 if (Close() != FILE_NO_ERROR)
134 return file_error;
135
136 // Create and truncate existing files
137 fp = FileSystem::Create(fname);
138
139 if (fp == 0)
140 {
141 is_ok = 0;
142 is_open = 0;
143 ready_for_writing = 0;
144 ready_for_reading = 0;
145 file_error = FILE_CREATION_ERROR;
146#ifdef __CPP_EXCEPTIONS__
147 throw CFileBaseException(file_error);
148#else
149 return file_error;
150#endif
151 }
152 else
153 {
154 is_open = 1;
155 is_ok = 1;
156 ready_for_writing = 1;
157 ready_for_reading = 1;
158 last_operation = IO_READ;
159 _tcscpy(file_name, fname);
160 }
161
162 // Returns 0 if the file was successfully created and opened.
163 return file_error = FILE_NO_ERROR;
164}
165
166//----------------------------------------------------------------------------------------
167// FileError CFileBase::Open(const TCHAR *fname, FileSystem::AccessMode mode
168// /* = FileSystem::FILE_READWRITE */ )
169//
170// Open an existing file. The "mode" variable determines if the file is opened for
171// read only or read/write access. Returns a non-zero value to indicate an error
172// condition or zero if successful.
173// NOTE: This version of the open functions will only accept:
174// FILE_READONLY and FILE_READWRITE access modes.
175//---------------------------------------------------------------------------------------
176FileError CFileBase::Open(const TCHAR *fname, FileSystem::AccessMode mode /* = FileSystem::FILE_READWRITE */)
177{
178 // Close any open files
179 if (Close() != FILE_NO_ERROR)
180 return file_error;
181
182 if (mode == FileSystem::FILE_READONLY)
183 { // Open with read only access
184 ready_for_reading = 1;
185 ready_for_writing = 0;
186 fp = FileSystem::Open(fname, FileSystem::FILE_READONLY);
187 }
188 else
189 { // Open with read/write access
190 ready_for_reading = 1;
191 ready_for_writing = 1;
192 fp = FileSystem::Open(fname, FileSystem::FILE_READWRITE);
193 }
194
195 if (fp == 0)
196 {
197 ready_for_reading = 0;
198 ready_for_writing = 0;
199 file_error = FILE_OPEN_ERROR;
200#ifdef __CPP_EXCEPTIONS__
201 throw CFileBaseException(file_error);
202#else
203 return file_error;
204#endif
205 }
206 else
207 {
208 is_open = 1;
209 is_ok = 1;
210 last_operation = IO_WRITE;
211 _tcscpy(file_name, fname);
212 }
213 return file_error = FILE_NO_ERROR;
214}
215
216//--------------------------------------------------------------------------------
217// void CFileBase::Close()
218//
219// Close the open database file. Returns a non-zero value
220// to indicate an error condition or zero if successful.
221//--------------------------------------------------------------------------------
222FileError CFileBase::Close()
223{
224 if (IsOpen())
225 {
226 if (FileSystem::Close(fp) != 0)
227 {
228 file_error = FILE_CLOSE_ERROR;
229#ifdef __CPP_EXCEPTIONS__
230 throw CFileBaseException(file_error);
231#else
232 return file_error;
233#endif
234 }
235 _tcscpy(file_name, closed_file_name); // Set the initial file name
236 }
237
238 is_open = 0;
239 is_ok = 0;
240 ready_for_reading = 0;
241 ready_for_writing = 0;
242 fp = 0; // Reset the file pointer to zero after the file is closed
243
244 return file_error = FILE_NO_ERROR;
245}
246
247//----------------------------------------------------------------------------------
248// FileError CFileBase::Seek(FAU offset, FileSystem::SeekMode mode)
249//
250// Seek to the specified offset starting at the beginning (SEEK_SET), end (SEEK_END)
251// or current offset (SEEK_CUR). Returns a non-zero value to indicate an error
252// condition or zero if successful.
253//----------------------------------------------------------------------------------
254FileError CFileBase::Seek(FileSystem::FAU_t offset, FileSystem::SeekMode mode)
255{
256 if (IsOK())
257 {
258 if (FileSystem::Seek(fp, offset, mode) == (isis::int32_t) - 1)
259 {
260 file_error = FILE_SEEK_ERROR;
261#ifdef __CPP_EXCEPTIONS__
262 throw CFileBaseException(file_error);
263#else
264 return file_error;
265#endif
266 }
267 last_operation = IO_SEEK;
268 }
269 else
270 {
271 file_error = FILE_NOT_READY;
272#ifdef __CPP_EXCEPTIONS__
273 throw CFileBaseException(file_error);
274#else
275 return file_error;
276#endif
277 }
278 return file_error = FILE_NO_ERROR;
279}
280
281//----------------------------------------------------------------------------------
282// FAU CFileBase::SeekTo(FileSystem::FAU_t file_address)
283//
284// Seek to the specified address, optimizing the seek operation by moving the
285// file position indicator based on the current stream position. Returns the
286// current file position after performing the seek operation.
287//----------------------------------------------------------------------------------
288FileSystem::FAU_t CFileBase::SeekTo(FileSystem::FAU_t file_address)
289{
290 // Get the current stream position
291 FileSystem::StreamPos pos = FilePosition();
292
293 if (file_address == CURRADDR)
294 { // Do not perform a seek operation
295 return pos;
296 }
297 else if (file_address > pos)
298 { // Seek forward to the specified address
299 FileSystem::StreamPos offset = file_address - pos;
300 Seek(offset, FileSystem::FILE_SEEK_CUR);
301 }
302 else if (file_address < pos)
303 { // Seek backward to the specified address
304 Seek(file_address, FileSystem::FILE_SEEK_BEG);
305 }
306 else
307 { // Current file position equals the specified address
308 // Find current the position
309 Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR);
310 }
311
312 return FilePosition(); // Return current file position after seeking
313}
314
315//-------------------------------------------------------------------------------
316// FileError Store(const void* buf, isis::uint32_t nBytes,
317// FAU file_addres = CFileBase::CURRADDR,
318// int flush = 1, int bit_test = 1);
319//
320// Write a specific number of bytes from a memory buffer to a specified file
321// offset. If the "flush" variable is true, the file buffers will be flushed to
322// disk with each write operation. If the bit_test variable if true, the CRC of
323// the buffer will be compared to the CRC of the actual bytes written to disk.
324// Returns a non-zero value to indicate an error condition or zero if successful.
325//
326// void* buf - Pointer to the user-supplied buffer that is to receive
327// the data read from the file.
328// isis::uint32_t nBytes - The maximum number of bytes to be read from the
329// file. Note that an CFileBase is always a binary file,
330// so a CR/LF pair will always be read or written as two bytes.
331// FAU file_address - The file byte address. The address is always interpreted to
332// be from the beginning of the file, unless dwAddr is equal
333// to CURRADDR, which means from the current position.
334//-------------------------------------------------------------------------------
335FileError CFileBase::Store(const void* buf, isis::uint32_t nBytes,
336 FileSystem::FAU_t file_address /*= CURRADDR */,
337 int flush_flag /* = 1 */,
338 int bit_test /* = 1 */)
339{
340 FileSystem::FAU_t buf_address;
341
342 if (ReadyForWriting())
343 {
344 if (file_address == CURRADDR)
345 {
346 if (last_operation == IO_READ)
347 {
348 if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR)
349 return file_error;
350 }
351 }
352 else
353 {
354 if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR)
355 return file_error;
356 }
357
358 buf_address = FilePosition();
359
360 if (file_error != FILE_NO_ERROR)
361 return file_error;
362
363 if (FileSystem::Write(fp, buf, nBytes) != nBytes)
364 {
365 file_error = FILE_WRITE_ERROR;
366#ifdef __CPP_EXCEPTIONS__
367 throw CFileBaseException(file_error);
368#else
369 return file_error;
370#endif
371 }
372 last_operation = IO_WRITE;
373 }
374 else
375 {
376 file_error = FILE_NOT_WRITEABLE;
377#ifdef __CPP_EXCEPTIONS__
378 throw CFileBaseException(file_error);
379#else
380 return file_error;
381#endif
382 }
383
384 // Allow application to flush disk buffers after each write
385 // operation to ensure the file data stays in sync during multiple
386 // file access.
387 if (flush_flag)
388 {
389 if (FileSystem::Flush(fp) != FILE_NO_ERROR)
390 {
391 file_error = FILE_WRITE_ERROR;
392#ifdef __CPP_EXCEPTIONS__
393 throw CFileBaseException(file_error);
394#else
395 return file_error;
396#endif
397 }
398 if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR)
399 return file_error;
400 }
401
402 if (bit_test)
403 {
404 isis::uint32_t w_csum = CalcCRC32((char* ) buf, nBytes);
405 isis::uint32_t r_csum = CalcChecksum(nBytes, buf_address);
406
407 // Check for file errors
408
409 if (file_error != FILE_NO_ERROR)
410 return file_error;
411
412 if (w_csum ^ r_csum)
413 {
414 file_error = FILE_CHECKSUM_ERROR;
415#ifdef __CPP_EXCEPTIONS__
416 throw CFileBaseException(file_error);
417#else
418 return file_error;
419#endif
420 }
421 }
422 return file_error = FILE_NO_ERROR;
423}
424
425//-------------------------------------------------------------------------------
426// FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes, FAU file_addres
427// /* = CFileBase::CURRADDR */)
428//
429// Read a specified number of bytes from the specified file offset into a memory
430// buffer. Returns a non-zero value to indicate an error condition or zero if
431// successful.
432// void* buf - Pointer to the user-supplied buffer that is to receive
433// the data read from the file.
434// isis::uint32_t nBytes - The maximum number of bytes to be read from the file.
435// Note that an CFileBase is always a binary file, so a
436// CR/LF pair will always be read or written as two bytes.
437// FAU file_addres - The file byte address. The address is always interpreted
438// to be from the beginning of the file, unless dwAddr is
439// equal to CURRADDR, which means from the current position.
440//-------------------------------------------------------------------------------
441FileError CFileBase::Fetch(void* buf, isis::uint32_t nBytes,
442 FileSystem::FAU_t file_address /* = CFileBase::CURRADDR */)
443{
444 if (IsOK())
445 {
446 if (file_address == CURRADDR)
447 {
448 if (last_operation == IO_WRITE)
449 {
450 if (Seek(0, FileSystem::FILE_SEEK_CUR) != FILE_NO_ERROR)
451 return file_error;
452 }
453 }
454 else
455 {
456 if (Seek(file_address, FileSystem::FILE_SEEK_BEG) != FILE_NO_ERROR)
457 return file_error;
458 }
459
460 //TRACE("\nFetchfile_address=%ld nBytes=%u",file_address, nBytes);
461 if (FileSystem::Read(fp, buf, nBytes) != nBytes)
462 {
463 file_error = FILE_READ_ERROR;
464#ifdef __CPP_EXCEPTIONS__
465 throw CFileBaseException(file_error);
466#else
467 return file_error;
468#endif
469 }
470 last_operation = IO_READ;
471 }
472 else
473 {
474 file_error = FILE_NOT_READY;
475#ifdef __CPP_EXCEPTIONS__
476 throw CFileBaseException(file_error);
477#else
478 return file_error;
479#endif
480 }
481
482 return file_error = FILE_NO_ERROR;
483}
484//-------------------------------------------------------------------------------
485// FileError Flush()
486//
487// Flush any open disk buffers. Returns a non-zero value to indicate an error
488// condition or zero if successful.
489//-------------------------------------------------------------------------------
490FileError CFileBase::Flush()
491{
492 PRECONDITION(ReadyForWriting());
493 if (FileSystem::Flush(fp) != FILE_NO_ERROR)
494 {
495 file_error = FILE_WRITE_ERROR;
496#ifdef __CPP_EXCEPTIONS__
497 throw CFileBaseException(file_error);
498#else
499 return file_error;
500#endif
501 }
502 Seek((FileSystem::FAU_t)0, FileSystem::FILE_SEEK_CUR);
503 return file_error = FILE_NO_ERROR;
504}
505
506
507//-------------------------------------------------------------------------------
508// StreamPos CFileBase::FilePosition()
509//
510// Returns the current file position or -1 to indicate an error condition.
511//-------------------------------------------------------------------------------
512FileSystem::StreamPos CFileBase::FilePosition()
513{
514 if (!IsOK())
515 {
516 file_error = FILE_NOT_READY;
517#ifdef __CPP_EXCEPTIONS__
518 throw CFileBase();
519#else
520 return (StreamPos) - 1;
521#endif
522 }
523
524 FileSystem::StreamPos pos = FileSystem::Tell(fp);
525 if (pos < 0)
526 {
527 file_error = FILE_POSITION_ERROR;
528#ifdef __CPP_EXCEPTIONS__
529 throw CFileBaseException(file_error);
530#else
531 return (StreamPos) - 1;
532#endif
533 }
534 return pos;
535}
536
537//-------------------------------------------------------------------------------
538// isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FAU file_address,
539// int mem_alloc)
540//
541// Calculate a 32-bit CRC checksum for a given number of bytes starting at the
542// specified address. Returns a 32-bit CRC value. If the mem_alloc variable is
543// true, a buffer equal to the specified number of bytes will be created in
544// memory. If the mem_alloc variable is false or memory allocation fails, the CRC
545// will be calculated byte by byte starting at the specified address.
546// NOTE: the calling function must check for disk file errors.
547// Returns the CRC value.
548//-------------------------------------------------------------------------------
549
550isis::uint32_t CFileBase::CalcChecksum(isis::uint32_t bytes, FileSystem::FAU_t file_address, int mem_alloc)
551{
552 isis::uint32_t CRC;
553 isis::uint32_t len = bytes;
554 unsigned char data;
555 char *buf = 0;
556
557 // Create a buffer equal to the object length
558 if (mem_alloc)
559 buf = new char[bytes];
560
561 if (buf)
562 {
563 if (IsOK())
564 {
565 if (Fetch(buf, bytes, file_address) != FILE_NO_ERROR) return 0;
566 CRC = CalcCRC32(buf, bytes);
567 delete[] buf;
568 }
569 }
570 else
571 {
572 if (IsOK())
573 {
574 // Seek to the specified file address
575 SeekTo(file_address);
576 if (file_error != FILE_NO_ERROR) return 0;
577 CRC = 0xffffffffL;
578 while (len--)
579 {
580 if (Fetch(&data, sizeof(data)) != FILE_NO_ERROR) return 0;
581 CRC = CalcCRC32(data, CRC);
582 }
583 CRC ^= 0xffffffffL;
584 }
585 }
586
587 return CRC;
588}
589///////////////////////////////////////////////////////////////////////////
590// General purpose file utilites
591//
592int CFileBase::Exists(const TCHAR *fname)
593// Returns true if the file exists
594{
595 return FileSystem::Exists(fname);
596}
597
598FileSystem::FAU_t CFileBase::FileSize(const TCHAR *fname)
599// Returns the file size. Use after file has been closed
600// and re-opened to ensure that all the buffers are flushed
601// to disk. Returns -1 to indicate an error condition.
602{
603 return (FileSystem::FAU_t) FileSystem::FileSize(fname);
604}
605
606int CFileBase::CanOpenForWriting(const TCHAR *fname)
607// Returns true if the file is opened
608{
609 return 1;
610 //return FileSystem::CanOpenForWriting(fname);
611}
612
613int CFileBase::CanOpenReadOnly(const TCHAR *fname)
614// Returns true if the file is opened
615{
616 return 1;
617 //return FileSystem::CanOpenReadOnly(fname);
618}
Note: See TracBrowser for help on using the repository browser.