home *** CD-ROM | disk | FTP | other *** search
- /* File support routines to complement ARP.
- * Author: Mark R. Rinfret
- * Usenet: mrr@amanpt1.Newport.RI.US
- * BIX: markr
- * CIS: 72017, 136 (good luck!)
- *
- * 348 Indian Avenue
- * Portsmouth, RI 02871
- * 401-846-7639 (home)
- * 401-849-9390 (work)
- *
- * This package was written primarily because of _one_ missing element
- * in ARP: FGets. ARGH! ARPFFFT! It extends ARP by adding buffering to
- * the basic file type (FileHandle) and defining a new type, named
- * ARPFileHandle (hope this is OK with the ARP guys). Also, I've used the
- * convention of embedding ARP (vs. Arp in MicroSmith's stuff) in all type
- * and function names to (hopefully) avoid naming collisions.
- *
- * This package (as far as I am concerned) is public domain. Use it any
- * way you like. Some comments on the "MR" prefix: it isn't short for
- * "Mister", it comprises the initials of my first and last names;
- * I use it primarily to avoid name collisions with stuff by other
- * authors. If any other authors whose initials are "MR" start doing
- * this, we'll probably have to appeal to Electronic Arts to dole out
- * "author codes" along the lines of those issued for IFF :-). I hereby
- * stake a claim to 'MRR_'.
- *
- * A word of warning:
- *
- * DO NOT INTERMIX STANDARD AMIGADOS FILE SUPPORT CALLS WITH CALLS TO
- * THIS PACKAGE ON FILES USING THIS FILE METHOD!
- *
- * Obviously, the system doesn't know about the buffering added here
- * and will cause unpredictable results (unless, of course, you
- * take care to maintain the ARPFileHandle information).
- */
-
- /* Note: define DEBUG if you want the test program at the end. */
-
- /* Embedded documentation template (cut & paste): */
-
- /* FUNCTION
- *
- * SYNOPSIS
- *
- * DESCRIPTION
- *
- */
-
- #include "MRARPFile.h"
- #include <exec/memory.h>
- #include <functions.h>
-
- #ifndef min
- #define min(a,b) ((a) <= (b) ? (a) : (b))
- #define max(a,b) ((a) >= (b) ? (a) : (b))
- #endif
-
- /* StoreTracker is my attempt to fix an apparent bug in ARP 1.3. I have
- * found that the LastTracker kludge (via IoErr()) doesn't work reliably.
- * StoreTracker simply stuffs A1 into D0 and returns . It *MUST* be
- * called immediately after any ARP call which allocates a tracker to
- * assure that the value is correct.
- */
-
- struct DefaultTracker *StoreTracker(); /* asm routine */
-
- static LONG FillARPFileBuffer();
-
- /* FUNCTION
- * FGetsARP - get string from a buffered ARP file.
- *
- * SYNOPSIS
- * char *FGetsARP(s, length, file)
- * UBYTE *s;
- * LONG length;
- * ARPFileHandle *file;
- *
- * DESCRIPTION
- * FGetsARP models the behavior of the "standard" fgets, except that
- * it is used with a buffered ARP file. The data is read from <file>
- * and transfered to string <s>. Up to length-1 characters will be
- * read. Reading is also terminated upon receipt of a newline
- * or detection of end-of-file.
- *
- * If the <file> was opened without a buffer, one of MaxInputBuf
- * bytes will be allocated.
- *
- * If end of file or an error is detected, the return value will be
- * NULL. Otherwise, the return value will be <s>. <s> will be
- * terminated with a null byte.
- */
- char *
- FGetsARP(s, length, file)
- char *s;
- LONG length;
- ARPFileHandle *file;
- {
- LONG bytesNeeded = length - 1;
- LONG bytesRead = 0;
- char c;
- char *s1 = s;
- struct DefaultTracker *tracker;
-
- /* Set string initially empty to protect user from failure to check
- * return value.
- */
- *s1 = '\0';
- if (file->mode != MODE_OLDFILE)
- file->lastError = ERROR_READ_PROTECTED;
-
- if (file->lastError) {
- dangit:
- return NULL;
- }
- if (! file->buf ) { /* Ohmigosh! No buffer? */
- file->buf = ArpAllocMem(MaxInputBuf, MEMF_CLEAR|MEMF_PUBLIC);
- tracker = StoreTracker();
- if (! file->buf ) {
- file->lastError = ERROR_NO_FREE_STORE;
- goto dangit;
- }
- file->bufSize = MaxInputBuf; /* bufLength, bufPos are zero. */
- file->bufTracker = tracker;
- }
- while (bytesNeeded) {
- if (file->bufPos >= file->bufLength) {
- if (FillARPFileBuffer(file) < 0) goto dangit;
- if (file->bufLength == 0) break;
- }
- c = file->buf[file->bufPos++];
- ++bytesRead;
- if (c == '\n') break;
- *s1++ = c;
- --bytesNeeded;
- }
- *s1 = '\0';
- return (bytesRead ? s : NULL);
- }
-
- /* FUNCTION
- * FillARPFileBuffer - read data into ARPFile buffer.
- *
- * SYNOPSIS
- * static LONG FillARPFileBuffer(file)
- * ARPFileHandle *file;
- *
- * DESCRIPTION
- * Attempt to fill the buffer associated with <file> by reading
- * data from the associated external file. The return value will
- * be one of the following:
- * >0 => number of bytes read
- * 0 => end of file
- * -1 => a Bad Thing happened (error code in file->lastError)
- *
- * Note: this is a local routine and is thus declared as "static".
- * Please inform the author if this is a hardship.
- */
- static LONG
- FillARPFileBuffer(file)
- ARPFileHandle *file;
- {
- /* Remember where we were. The user might want to try error
- * recovery. Of course, this probably isn't enough info, but
- * it's a start.
- */
- file->lastPosition = Seek(file->fh, 0L, OFFSET_CURRENT);
- file->bufPos = 0;
- file->bufLength = Read(file->fh, file->buf, file->bufSize);
- if (file->bufLength == -1) { /* We got trubble! */
- file->lastError = IoErr();
- }
- else if (file->bufLength == 0) {
- file->endOfFile = TRUE;
- }
- return file->bufLength;
- }
-
- /* FUNCTION
- * FlushARPFileBuffer - write file buffer contents to disk.
- *
- * SYNOPSIS
- * static LONG FlushARPFileBuffer(file)
- * ARPFileHandle *file;
- * DESCRIPTION
- * FlushARPFileBuffer writes the contents of <file>'s buffer
- * (if any) to disk and resets the buffer information. The
- * return value may be any of:
- *
- * >0 => number of bytes written
- * 0 => nothing in buffer
- * <0 => negated error code
- *
- * Note: it is assumed that this function will only be used locally
- * and therefore need not be public. If you disagree, please contact
- * the author.
- */
-
- static LONG
- FlushARPFileBuffer(file)
- ARPFileHandle *file;
- {
- LONG bytesWritten = 0;
-
- /* This operation is only allowed for output files. */
-
- if (file->mode != MODE_NEWFILE) {
- file->lastError = ERROR_WRITE_PROTECTED;
- badstuff:
- return -file->lastError;
- }
-
- if (file->lastError) goto badstuff; /* Residual error? */
-
- if (file->bufLength) {
- file->lastPosition = Seek(file->fh, 0L, OFFSET_CURRENT);
- bytesWritten = Write(file->fh, file->buf, file->bufLength);
- if (bytesWritten != file->bufLength) {
- file->lastError = IoErr();
- goto badstuff;
- }
- else {
- file->bufLength = 0;
- file->bufPos = 0;
- }
- }
- return bytesWritten;
- }
-
- /* FUNCTION
- * FPutsARP - write a string to a buffered ARP file.
- *
- * SYNOPSIS
- * LONG FPutsARP(s, file)
- * char *s;
- * ARPFileHandle *file;
- *
- * DESCRIPTION
- * FPutsARP writes the contents of string <s> to the specified <file>.
- * If successful, it returns 0. On failure, it returns the negated
- * system error code.
- */
- LONG
- FPutsARP(s, file)
- char *s;
- ARPFileHandle *file;
- {
- LONG bytesLeft, bytesUsed;
- char *s1 = s;
-
- if (file->mode != MODE_NEWFILE)
- file->lastError = ERROR_WRITE_PROTECTED;
-
- if (file->lastError) {
- shucks:
- return -file->lastError;
- }
-
- bytesLeft = strlen(s);
-
- /* Attempt to be smart about this transfer. Copy the string to the
- * buffer in chunks. There is a possibility that the string is bigger
- * than the size of the buffer.
- */
- while (bytesLeft) {
- if (file->bufPos >= file->bufSize) {
- if (FlushARPFileBuffer(file) <= 0) goto shucks;
- }
- bytesUsed = min(file->bufSize - file->bufPos, bytesLeft);
- CopyMem(s1, &file->buf[file->bufPos], bytesUsed);
- s1 += bytesUsed;
- file->bufLength = (file->bufPos += bytesUsed);
- bytesLeft -= bytesUsed;
- }
- return 0;
- }
-
- /* FUNCTION
- * ReadARPFile - read from a buffered ARP file.
- *
- * SYNOPSIS
- * LONG ReadARPFile(file, buffer, length)
- * ARPFile *file;
- * UBYTE *buffer;
- * LONG length;
- *
- * DESCRIPTION
- * ReadARPFile attempts to read <length> bytes from <file>, transferring
- * the data to <buffer>.
- *
- * The return value may be any of the following:
- *
- * >0 number of bytes transferred
- * 0 end of file
- * <0 negated error code
- *
- * Note: if the lastError field of the <file> descriptor contains a
- * non-zero value, its negated value will be returned and no
- * attempt will be made to read the file. If you attempt error
- * recovery, you must clear this field to zero.
- */
- LONG
- ReadARPFile(file, buffer, length)
- ARPFileHandle *file;
- UBYTE *buffer;
- LONG length;
- {
- LONG bytesLeft;
- LONG bytesRead = 0;
- LONG needBytes = length;
- LONG pos = 0;
- LONG usedBytes = 0;
-
- /* Prevent read if this file opened for writing. */
-
- if (file->mode != MODE_OLDFILE)
- file->lastError = ERROR_READ_PROTECTED;
-
- if (file->lastError) { /* Have residual error? */
- boofar:
- return -file->lastError;
- }
-
- if ( ! file->buf ) { /* No buffer? */
- bytesRead = Read(file->fh, buffer, length);
- if (bytesRead == -1) {
- file->lastError = IoErr();
- goto boofar;
- }
- }
- else while (needBytes) {
- if (file->bufLength - file->bufPos <= 0) {
- if (FillARPFileBuffer(file) == -1) goto boofar;
- if (file->bufLength == 0) break;
- }
- bytesLeft = file->bufLength - file->bufPos;
- usedBytes = min(bytesLeft, length);
- CopyMem(&file->buf[file->bufPos], &buffer[pos], usedBytes);
- file->bufPos += usedBytes;
- pos += usedBytes;
- bytesRead += usedBytes;
- needBytes -= usedBytes;
- }
- return bytesRead;
- }
-
- /* FUNCTION
- * CloseARPFile - close buffered ARP file.
- *
- * SYNOPSIS
- * LONG CloseARPFile(file)
- * ARPFileHandle *file;
- *
- * DESCRIPTION
- * CloseARPFile closes the file described by <file> which MUST have
- * been opened by OpenARPFile. It releases all tracked items
- * associated with <file>, as well.
- *
- * The return value is only meaningful for output files. If, upon
- * flushing the buffer, a write error is detected, a system error
- * code (ERROR_DISK_FULL, etc.) will be returned.
- */
-
- LONG
- CloseARPFile(file)
- ARPFileHandle *file;
- {
- LONG result = 0;
-
- if (file) { /* Just in case... */
- if (file->fileTracker) {
- /* Any left-over stuff in the buffer? If so, we must flush
- * it to disk. However, if an error was detected in the
- * previous operation, punt.
- */
- if ( ( file->mode == MODE_NEWFILE) &&
- ! file->lastError &&
- file->bufLength) {
- if (Write(file->fh, file->buf, file->bufLength) !=
- file->bufLength)
- result = IoErr();
- }
- FreeTrackedItem(file->fileTracker);
- }
-
- if (file->bufTracker)
- FreeTrackedItem(file->bufTracker);
-
- FreeTrackedItem(file->myTracker);
- }
- return result;
- }
-
- /* FUNCTION
- * OpenARPFile - open a buffered ARP file
- *
- * SYNOPSIS
- * struct MRARPFile *OpenARPFile(name, accessMode, bytes)
- * char *name;
- * LONG accessMode, bytes;
- *
- * DESCRIPTION
- * OpenARPFile opens the file <name>, with the given <accessMode>
- * (MODE_OLDFILE, MODE_NEWFILE only!) for buffered access. The
- * size of the local buffer is specified by <bytes>.
- *
- * A zero value for <bytes> is OK and is sometimes appropriate, as
- * when performing file copy operations, etc. However, if a file
- * opened with a zero length buffer is then passed to the
- * FGetsARP function, "spontaneous buffer allocation" will occur.
- * No biggy, really, just something you should know. The ARP constant,
- * MaxInputBuf will be used for the buffer size, in this case.
- *
- * If successful, a pointer to the file tracking structure is
- * returned. Otherwise, the return value will be NULL.
- *
- * OpenARPFile uses full resource tracking for all information
- * associated with the file.
- *
- */
-
- ARPFileHandle *
- OpenARPFile(name, accessMode, bytes)
- char *name;
- LONG accessMode, bytes;
- {
- BPTR fh;
- ARPFileHandle *theFile = NULL;
- struct DefaultTracker *lastTracker;
-
- /* This package does not support READ/WRITE access! */
-
- if ( (accessMode != MODE_OLDFILE) && (accessMode != MODE_NEWFILE))
- return NULL;
-
- /*
- * Note: I'm using ArpAllocMem vs. ArpAlloc here because it appears
- * that ArpAlloc gives me a bad tracker pointer (?).
- */
- theFile = ArpAllocMem((LONG) sizeof(ARPFileHandle),
- MEMF_PUBLIC|MEMF_CLEAR);
- lastTracker = StoreTracker();
- if (theFile) {
- theFile->myTracker = lastTracker;
- theFile->mode = accessMode;
- fh = ArpOpen(name, accessMode);
- lastTracker = StoreTracker();
- if (!fh) {
- fungu:
- CloseARPFile(theFile); /* Don't worry - it's "smart". */
- theFile = NULL;
- }
- theFile->fileTracker = lastTracker;
- theFile->fh = fh;
- if ( bytes) { /* Does user want a buffer? */
- theFile->buf = ArpAllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
- lastTracker = StoreTracker();
- if (!theFile->buf) goto fungu;
- theFile->bufSize = bytes;
- theFile->bufTracker = lastTracker;
- }
- }
- return theFile;
- }
-
- /* FUNCTION
- * SeekARPFile - move to new logical position in file.
- *
- * SYNOPSIS
- * LONG SeekARPFile(file, position, mode)
- * ARPFileHandle *file;
- * LONG position;
- * LONG mode;
- *
- * DESCRIPTION
- * SeekARPFile attempts to position the <file> to a new logical
- * position or report it's current position. The <position>
- * parameter represets a signed offset. The <mode> parameter may
- * be one of:
- *
- * OFFSET_BEGINNING (-1) => from beginning of file
- * OFFSET_CURRENT (0) => from current position
- * OFFSET_END (-1) => from end of file
- *
- * On output files, the current buffer contents, if any, are
- * written to disk.
- *
- * The return value will either be a positive displacement >=0) or
- * a negated system error code.
- */
- LONG
- SeekARPFile(file, position, mode)
- ARPFileHandle *file;
- LONG position;
- LONG mode;
- {
- LONG newPosition;
-
- if (file->mode == MODE_NEWFILE && file->bufLength) {
- if (FlushARPFileBuffer(file) < 0) {
- farboo:
- return -file->lastError;
- }
- }
- /* Remember our last position. */
- file->lastPosition = Seek(file, 0L, OFFSET_CURRENT);
- if ((newPosition = Seek(file->fh, position, mode)) == -1) {
- file->lastError = IoErr();
- goto farboo;
- }
- return newPosition;
- }
-
- /* FUNCTION
- * WriteARPFile - write data to a buffered ARP file.
- *
- * SYNOPSIS
- * LONG WriteARPFile(file, buffer, length)
- * ARPFileHandle *file;
- * UBYTE *buffer;
- * LONG length;
- *
- * DESCRIPTION
- * WriteARPFile attempts to write <length> bytes from <buffer> to
- * the buffered ARP file, <file>. The file MUST have been opened
- * with OpenARPFile for MODE_NEWFILE access.
- *
- * WriteARPFile will not write to a file if the lastError field is
- * non-zero, or if the file was opened for input.
- *
- * If successful, WriteARPFile will return the <length> parameter.
- * Otherwise, it will return a negated system error code.
- */
- LONG
- WriteARPFile(file, buffer, length)
- ARPFileHandle *file;
- UBYTE *buffer;
- LONG length;
- {
- LONG bufferBytes;
- LONG bytesLeft = length;
- LONG pos = 0;
- LONG usedBytes = 0;
-
- if (file->mode != MODE_NEWFILE)
- file->lastError = ERROR_WRITE_PROTECTED;
-
- if (file->lastError) { /* Catches mode and residual errors. */
- sumbidge:
- return -file->lastError;
- }
-
- if ( ! file->buf ) { /* No buffer? */
- if (Write(file->fh, buffer, length) != length) {
- file->lastError = IoErr();
- goto sumbidge;
- }
- }
- else while (bytesLeft) {
- /* Need to flush the file's buffer? */
- if (file->bufPos >= file->bufSize) {
- if (FlushARPFileBuffer(file) < 0) {
- goto sumbidge;
- }
- }
- bufferBytes = file->bufSize - file->bufPos;
- usedBytes = min(bufferBytes, bytesLeft);
- CopyMem(&buffer[pos], &file->buf[file->bufPos], usedBytes);
- file->bufLength = (file->bufPos += usedBytes);
- pos += usedBytes;
- bytesLeft -= usedBytes;
- }
- return length;
- }
-
- #ifdef DEBUG
- /* Short test program. */
-
- struct ArpBase *ArpBase;
- struct IntuitionBase *IntuitionBase;
- struct GfxBase *GfxBase;
-
- ARPFileHandle *
- GetFile(prompt, mode, bufferSize)
- char *prompt;
- LONG mode, bufferSize;
- {
- BOOL keepTrying;
- ARPFileHandle *theFile = NULL;
- char fileName[MaxInputBuf+1];
-
- for (keepTrying = TRUE; keepTrying; ) {
- Puts("\nA null response aborts this test.");
- Puts(prompt);
- ReadLine(fileName);
- if ( ! *fileName ) break; /* Null response? */
- theFile = OpenARPFile(fileName, mode, bufferSize);
- if (theFile == NULL) {
- Puts("That file failed to open. Try again.\n");
- }
- else
- keepTrying = FALSE;
- }
- return theFile;
- }
-
- main()
- {
- #define BUFFER_SIZE 16384L
-
- static char line[81];
-
- UBYTE *buffer;
- LONG count;
- ARPFileHandle *file1, *file2;
- int keepTesting;
- LONG totalBytes;
-
- ArpBase = (struct ArpBase *) OpenLibrary(ArpName, ArpVersion);
- if (ArpBase == NULL) {
- exit(1);
- }
- GfxBase = (struct GfxBase *) ArpBase->GfxBase;
- IntuitionBase = (struct IntuitionBase *) ArpBase->IntuiBase;
-
- Puts("MRARPFile package test.\n\n");
-
- FGetsARPTest:
- Puts("FGetsARP test - echo file to console.");
- file1 = GetFile("Enter the name of a text file:", MODE_OLDFILE, 4192L);
- if ( file1) {
- while (FGetsARP(line, (LONG) sizeof(line), file1))
- Puts(line);
- CloseARPFile(file1);
- }
-
- CopyFileTest:
- buffer = ArpAllocMem(BUFFER_SIZE, MEMF_PUBLIC);
- if ( ! buffer ) {
- Printf("I couldn't allocate a %ld byte buffer!\n", BUFFER_SIZE);
- goto die;
- }
- for (keepTesting = TRUE; keepTesting; ) {
- Puts("ReadARPFile/WriteARPFile test.");
- Puts("For this test, I will copy a file.");
- file1 = GetFile("Enter input file name:", MODE_OLDFILE, 512L);
- if (!file1) break;
- file2 = GetFile("Enter output file name:", MODE_NEWFILE, 8192L);
- if (!file2) {
- CloseARPFile(file1);
- break;
- }
- keepTesting = FALSE; /* This is it... */
- totalBytes = 0;
- while (count = ReadARPFile(file1, buffer, BUFFER_SIZE)) {
- totalBytes += count;
- if (WriteARPFile(file2, buffer, count) != count) {
- Printf("Error on output file: %ld\n", file2->lastError);
- break;
- }
- if (count < BUFFER_SIZE) break;
- }
- if (file1->lastError)
- Printf("Error on input file: %ld\n", file1->lastError);
- CloseARPFile(file1);
- CloseARPFile(file2);
- Printf("Bytes transferred: %ld\n", totalBytes);
- /* Note: leave the buffer for ARP to free up. You should check
- * available memory before/after running this test to make sure
- * that ARP does proper housecleaning.
- */
- }
- die:
- CloseLibrary(ArpBase);
- exit(0);
- }
-
- #endif