source: trunk/tools/common/kFile.cpp@ 4402

Last change on this file since 4402 was 4402, checked in by bird, 25 years ago

Implemented .Def to WLINK directives/options converter.

File size: 14.5 KB
Line 
1/* $Id: kFile.cpp,v 1.6 2000-10-03 05:42:38 bird Exp $
2 *
3 * kFile - Simple (for the time being) file class.
4 *
5 * Copyright (c) 2000 knut st. osmundsen (knut.stange.osmundsen@mynd.no)
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSFILEMGR
15#define INCL_DOSERRORS
16
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <os2.h>
22
23#include <malloc.h>
24#include <string.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29#include "kFile.h"
30
31/*******************************************************************************
32* Global Variables *
33*******************************************************************************/
34kFile kFile::StdIn((HFILE)0, TRUE);
35kFile kFile::StdOut((HFILE)1, FALSE);
36kFile kFile::StdErr((HFILE)2, FALSE);
37
38
39/**
40 * Updates the internal filestatus struct.
41 * @returns Success indicator.
42 * On success filestatus is refreshed.
43 * @remark
44 */
45BOOL kFile::refreshFileStatus()
46{
47 if (fStdDev)
48 return fStatusClean = TRUE;
49
50 if (!fStatusClean)
51 {
52 rc = DosQueryFileInfo(hFile, FIL_QUERYEASIZE, &filestatus, sizeof(filestatus));
53 fStatusClean = (rc == NO_ERROR);
54 if (!fStatusClean && fThrowErrors)
55 throw ((int)rc);
56 }
57 else
58 rc = NO_ERROR;
59
60 return fStatusClean;
61}
62
63
64/**
65 * Changes the real file position to match the virtual file position.
66 * @returns Success indicator.
67 */
68BOOL kFile::position()
69{
70 /*
71 * If virtual file offset is different from the real,
72 * issue a set file pointer call.
73 */
74 if (offVirtual != offReal)
75 {
76 ULONG off;
77 rc = DosSetFilePtr(hFile, offVirtual, FILE_BEGIN, &off);
78 if (rc != NO_ERROR || off != offVirtual)
79 {
80 if (fThrowErrors)
81 throw ((int)rc);
82 return FALSE;
83 }
84 offReal = offVirtual;
85 }
86
87 return TRUE;
88}
89
90/**
91 * Creates a kFile object for a file that is opened allready.
92 * Intended use for the three standard handles only.
93 *
94 * @returns <object> with state updated.
95 * @param pszFilename Filename.
96 * @param fReadOnly TRUE: Open the file readonly.
97 * FALSE: Open the file readwrite appending
98 * existing files.
99 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
100 */
101kFile::kFile(HFILE hFile, BOOL fReadOnly)
102: fReadOnly(fReadOnly),
103 fStatusClean(FALSE),
104 fThrowErrors(FALSE),
105 offVirtual(0),
106 offReal(0),
107 pszFilename(NULL),
108 hFile(hFile),
109 fStdDev(TRUE)
110{
111 if (!refreshFileStatus())
112 throw ((int)rc);
113 this->pszFilename = strdup("");
114}
115
116
117/**
118 * Opens a file for binary reading or readwrite.
119 * @returns <object> with state updated.
120 * Throws OS/2 error on error.
121 * @param pszFilename Filename.
122 * @param fReadOnly TRUE: Open the file readonly. (default)
123 * FALSE: Open the file readwrite appending
124 * existing files.
125 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
126 */
127kFile::kFile(const char *pszFilename, BOOL fReadOnly/*=TRUE*/)
128: fReadOnly(fReadOnly),
129 fStatusClean(FALSE),
130 fThrowErrors(FALSE),
131 offVirtual(0),
132 offReal(0),
133 pszFilename(NULL),
134 fStdDev(FALSE)
135{
136 ULONG fulOpenFlags;
137 ULONG fulOpenMode;
138 ULONG ulAction;
139
140 /*
141 * Determin open flags according to fReadOnly.
142 */
143 if (fReadOnly)
144 {
145 fulOpenFlags = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
146 fulOpenMode = OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY;
147 }
148 else
149 {
150 fulOpenFlags = OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
151 fulOpenMode = OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READWRITE;
152 }
153
154 rc = DosOpen((PCSZ)pszFilename, &hFile, &ulAction, 0, FILE_NORMAL,
155 fulOpenFlags, fulOpenMode, NULL);
156 if (rc != NO_ERROR)
157 throw ((int)rc);
158
159 if (!refreshFileStatus())
160 throw ((int)rc);
161
162 char szFullName[CCHMAXPATH];
163 if (DosQueryPathInfo(pszFilename, FIL_QUERYFULLNAME, szFullName, sizeof(szFullName)))
164 strcpy(szFullName, pszFilename);
165 this->pszFilename = strdup(szFullName);
166 if (this->pszFilename == NULL)
167 throw (ERROR_NOT_ENOUGH_MEMORY);
168}
169
170
171/**
172 * Closes the file.
173 */
174kFile::~kFile()
175{
176 DosClose(hFile);
177}
178
179
180/**
181 * Reads <cbBuffer> bytes from the current file posistion into the buffer.
182 * @returns success indicator. (TRUE/FALSE)
183 * @param pvBuffer Output buffer.
184 * @param cbBuffer Amount of bytes to read.
185 */
186BOOL kFile::read(void *pvBuffer, long cbBuffer)
187{
188 if (position())
189 {
190 ULONG cbRead;
191 rc = DosRead(hFile, pvBuffer, cbBuffer, &cbRead);
192 if (rc == NO_ERROR)
193 {
194 offVirtual = offReal += cbRead;
195 return TRUE;
196 }
197 }
198
199 if (fThrowErrors)
200 throw ((int)rc);
201 return FALSE;
202}
203
204
205/**
206 * Reads <cbBuffer> bytes at file offset <off>.
207 * @returns success indicator. (TRUE/FALSE)
208 * @param pvBuffer Output buffer.
209 * @param cbBuffer Amount of bytes to read.
210 * @param off Absolute file offset.
211 */
212BOOL kFile::readAt(void *pvBuffer, long cbBuffer, long off)
213{
214 return set(off) && read(pvBuffer, cbBuffer);
215}
216
217
218/**
219 * Reads the entire file into a single memory block.
220 * (The memory block has a '\0' at the end just in case you
221 * are using it as a long string.)
222 * @returns Pointer to file in memory.
223 */
224void * kFile::readFile() throw(int)
225{
226 void *pv;
227
228 /* allocate memory for the file */
229 pv = calloc((size_t)this->getSize() + 1, 1);
230 if (pv == NULL)
231 {
232 if (fThrowErrors)
233 throw(ERROR_NOT_ENOUGH_MEMORY);
234 return NULL;
235 }
236
237 /* go the start of the file and read it. */
238 if (start() && read(pv, this->getSize()))
239 return pv; // successfull exit!
240
241 /* we failed, cleanup and return NULL */
242 free(pv);
243 return NULL;
244}
245
246
247/**
248 * Reads a single line from the file into the given buffer.
249 * Newline is stripped!
250 * @returns Success indicator.
251 * @param pszBuffer Pointer to string buffer.
252 * Will hold a zero-string upon successful return.
253 * @param cchBuffer Buffer size.
254 * @sketch
255 * @status partially implemented.
256 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
257 * @remark Should implemented buffered read ASAP!
258 */
259BOOL kFile::readln(char *pszBuffer, long cchBuffer) throw (int)
260{
261 char *psz;
262 long cbRead = min(max((long)filestatus.cbFile - (long)offVirtual, 0), cchBuffer-1);
263
264 /*
265 * Read full buffer length or remining part of file into the buffer.
266 * Look for line end.
267 * Found lineend: cut buffer there and rewind file back to that point (but skipping the newline).
268 */
269 if (cbRead == 0 || !read(pszBuffer, cbRead) )
270 return FALSE;
271
272 pszBuffer[cbRead] = '\0';
273
274 psz = strpbrk(pszBuffer, "\r\n");
275 if (psz != NULL)
276 {
277 cbRead -= psz - pszBuffer;
278 if (*psz == '\r')
279 {
280 if (psz[1] == '\n')
281 cbRead -= 2;
282 else
283 cbRead--;
284 }
285 else if (*psz == '\n')
286 cbRead--;
287
288 *psz = '\0';
289
290 return move(-cbRead);
291 }
292
293 return TRUE;
294}
295
296
297/**
298 * Writes <cbBuffer> bytes to the file at the current file position.
299 * @returns success indicator. (TRUE/FALSE)
300 * @param pvBuffer Output buffer.
301 * @param cbBuffer Amount of bytes to write.
302 */
303BOOL kFile::write(void *pvBuffer, long cbBuffer)
304{
305 if (fReadOnly)
306 rc = ERROR_ACCESS_DENIED;
307 else
308 {
309 if (position())
310 {
311 ULONG cbWrote;
312
313 rc = DosWrite(hFile, pvBuffer, cbBuffer, &cbWrote);
314 if (rc == NO_ERROR)
315 {
316 fStatusClean = FALSE;
317 offVirtual = offReal += cbWrote;
318 return TRUE;
319 }
320 }
321 }
322
323 if (fThrowErrors)
324 throw ((int)rc);
325 return FALSE;
326}
327
328
329/**
330 * Write <cbBuffer> bytes at file offset <off> from <pvBuffer>.
331 * @returns success indicator. (TRUE/FALSE)
332 * @param pvBuffer Output buffer.
333 * @param cbBuffer Amount of bytes to write.
334 * @param off Absolute file offset.
335 */
336BOOL kFile::writeAt(void *pvBuffer, long cbBuffer, long off)
337{
338 return set(off) && write(pvBuffer, cbBuffer);
339}
340
341
342/**
343 * printf - formatted write.
344 *
345 * Lonely '\n's are prefixed with a '\r' to make output conform with
346 * PC line ending.
347 *
348 * @returns Number of bytes written.
349 * @param pszFormat Format string.
350 * @param ... Ellipcis.
351 * @remark Currently limited to 64KB of result data.
352 */
353int kFile::printf(const char *pszFormat, ...) throw (int)
354{
355 long offStart = getPos();
356 va_list arg;
357
358 /* !QUICK AND DIRTY! */
359 char * psz, * pszEnd;
360 char * pszBuffer = (char*)malloc(1024*64); //64KB should normally be enough...
361
362 va_start(arg, pszFormat);
363 pszEnd = vsprintf(pszBuffer, pszFormat, arg) + pszBuffer;
364 va_end(arg);
365
366 psz = pszEnd;
367 while (psz > pszBuffer)
368 {
369 if (*psz-- == '\n' && *psz != '\r')
370 {
371 memmove(psz+2, psz+1, pszEnd - psz + 1);
372 psz[1] = '\r';
373 pszEnd++;
374 }
375 }
376
377 write(pszBuffer, pszEnd - pszBuffer);
378 free(pszBuffer);
379
380 return (int)(getPos() - offStart);
381}
382
383
384/**
385 * Sets the filesize.
386 * @returns Success indicator.
387 * @param cbFile New filesize.
388 * Defaults to 0xffffffff, which results in
389 * cutting the file at the current position.
390 */
391BOOL kFile::setSize(unsigned long cbFile/*= ~0UL*/)
392{
393 if (cbFile == ~0UL)
394 cbFile = offVirtual;
395 rc = DosSetFileSize(hFile, cbFile);
396 if (rc != NO_ERROR && fThrowErrors)
397 throw ((int)rc);
398
399 return rc == NO_ERROR;
400}
401
402
403
404/**
405 * Appends the AppendFile to this file.
406 * @returns Reference to this file.
407 * @param AppendFile Reference to the file we're to append.
408 */
409kFile & kFile::operator+=(kFile &AppendFile)
410{
411 long cb;
412 char * pachBuffer = new char[1024*256];
413 long pos = AppendFile.getPos();
414 BOOL fAppend = AppendFile.fThrowErrors;
415 BOOL fThis = fThrowErrors;
416
417 setThrowOnErrors();
418 AppendFile.setThrowOnErrors();
419
420 end();
421 AppendFile.start();
422 AppendFile.refreshFileStatus();
423
424 cb = min(1024*256, AppendFile.filestatus.cbFile);
425 while (cb > 0)
426 {
427 AppendFile.read(pachBuffer, cb);
428 write(pachBuffer, cb);
429 cb = min(1024*256, (long)AppendFile.filestatus.cbFile - (long)AppendFile.offVirtual);
430 }
431
432 delete pachBuffer;
433 AppendFile.set(pos);
434 AppendFile.fThrowErrors = fAppend;
435 fThrowErrors = fThis;
436
437 return *this;
438}
439
440
441
442/**
443 * Seek relative to the current position.
444 * @returns Success indicator.
445 * @param off Relative reposition.
446 */
447BOOL kFile::move(long off)
448{
449 if ((off + offVirtual) & 0x80000000UL) /* above 2GB or negative */
450 rc = ERROR_NEGATIVE_SEEK;
451 else
452 {
453 if (off + offVirtual > filestatus.cbFile && fReadOnly) /* can't expand readonly file. */
454 rc = ERROR_HANDLE_EOF;
455 else
456 {
457 offVirtual += off;
458 return TRUE;
459 }
460 }
461
462 if (fThrowErrors)
463 throw ((int)rc);
464 return FALSE;
465}
466
467
468/**
469 * Seek to an absolute position in the file (off).
470 * @returns Success indicator.
471 * @param off New file position.
472 */
473BOOL kFile::set(long off)
474{
475 if (off < 0)
476 rc = ERROR_NEGATIVE_SEEK;
477 else
478 {
479 if ((unsigned long)off > filestatus.cbFile && fReadOnly)
480 rc = ERROR_HANDLE_EOF;
481 else
482 {
483 offVirtual = off;
484 rc = NO_ERROR;
485 return TRUE;
486 }
487 }
488 if (fThrowErrors)
489 throw ((int)rc);
490 return FALSE;
491}
492
493
494/**
495 * Seek to the end of the file.
496 * @returns Success indicator. TRUE / FALSE.
497 * @remark Will only throw error if refreshFileStatus failes.
498 */
499BOOL kFile::end()
500{
501 if (!refreshFileStatus())
502 return FALSE;
503 offVirtual = filestatus.cbFile; //?? or +1
504 rc = NO_ERROR;
505 return TRUE;
506}
507
508
509/**
510 * Seek to the start of the file.
511 * @returns TRUE.
512 * @remark Will never throw errors.
513 */
514BOOL kFile::start()
515{
516 offVirtual = 0;
517 rc = NO_ERROR;
518 return TRUE;
519}
520
521
522/**
523 * Get the size of the file.
524 * @returns Returns file size on success.
525 * -1 on error.
526 * @remark Will only throw error if refreshFileStatus failes.
527 */
528long kFile::getSize()
529{
530 if (!refreshFileStatus())
531 return -1;
532
533 return filestatus.cbFile;
534}
535
536
537/**
538 * Get current position.
539 * @returns The current file position.
540 * @remark Will only throw error if refreshFileStatus failes.
541 */
542long kFile::getPos() const
543{
544 return offVirtual;
545}
546
547
548/**
549 * Checks if we have reached the file end.
550 * @returns TRUE if end-of-file is reached.
551 * FALSE is not end-of-file.
552 * @remark Will only throw error if refreshFileStatus failes.
553 */
554BOOL kFile::isEOF()
555{
556 #if 0
557 throw(ERROR_NOT_SUPPORTED); //this method don't currently work! Need to use flag!
558 #else
559 if (!fReadOnly && !refreshFileStatus())
560 return (BOOL)-1;
561
562 return filestatus.cbFile <= offVirtual; //??? - !!!
563 #endif
564}
565
566
567/**
568 * Set error behaviour to fail by throwing the OS/2 return code when an
569 * error occures.
570 * @returns TRUE;
571 * @remark Will never throw errors.
572 */
573BOOL kFile::setThrowOnErrors()
574{
575 fThrowErrors = TRUE;
576 rc = NO_ERROR;
577 return TRUE;
578}
579
580
581/**
582 * Set error behaviour to fail by return FALSE when an error has occures.
583 * @returns TRUE;
584 * @remark Will never throw errors.
585 */
586BOOL kFile::setFailOnErrors()
587{
588 fThrowErrors = FALSE;
589 rc = NO_ERROR;
590 return TRUE;
591}
592
593
594/**
595 * Gets the last error code.
596 * @returns OS/2 error code for the last operation.
597 * @remark Will never throw errors.
598 */
599int kFile::getLastError() const
600{
601 return rc;
602}
603
Note: See TracBrowser for help on using the repository browser.