/* 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). */ /* Olsen: In order to adapt the code to ANSI some declarations and * function calls were rearranged. I also fixed two parts of * code which appeared not to work correctly. */ /* Skip prototypes. */ #define _MRARPFILE_PRIVATE 1 #include "MRARPFile.h" /* System includes. */ #include #include #include /* 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. */ #asm public _StoreTracker _StoreTracker: move.l a1,d0 rts #endasm struct DefaultTracker * StoreTracker(VOID); char * FGetsARP(char *s,LONG length,ARPFileHandle *file); static LONG FillARPFileBuffer(ARPFileHandle *file); static LONG FlushARPFileBuffer(ARPFileHandle *file); LONG FPutsARP(char *s,ARPFileHandle *file); LONG ReadARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length); LONG CloseARPFile(ARPFileHandle *file); ARPFileHandle * OpenARPFile(char *name,LONG accessMode,LONG bytes); LONG SeekARPFile(ARPFileHandle *file,LONG position,LONG mode); LONG WriteARPFile(ARPFileHandle *file,UBYTE *buffer,LONG length); /* 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 * and transfered to string . 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 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 . will be * terminated with a null byte. */ char * FGetsARP(char *s,LONG length,ARPFileHandle *file) { struct DefaultTracker *tracker; LONG bytesNeeded = length - 1; LONG bytesRead = 0; char c; char *s1 = s; /* 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); } /* Ohmigosh! No buffer? */ if(!file -> buf) { 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) 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 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(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); /* We got trubble! */ if(file -> bufLength == -1) file -> lastError = IoErr(); else { if(!file -> bufLength) 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 '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(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 to the specified . * If successful, it returns 0. On failure, it returns the negated * system error code. */ LONG FPutsARP(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 bytes from , transferring * the data to . * * 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 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(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; /* Have residual error? */ if(file -> lastError) { boofar: return(-file -> lastError); } /* No buffer? */ if(!file -> buf ) { 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) 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 which MUST have * been opened by OpenARPFile. It releases all tracked items * associated with , 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(ARPFileHandle *file) { LONG result = 0; /* Just in case... */ if(file) { 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 , with the given * (MODE_OLDFILE, MODE_NEWFILE only!) for buffered access. The * size of the local buffer is specified by . * * A zero value for 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(char *name,LONG accessMode,LONG bytes) { struct DefaultTracker *lastTracker; ARPFileHandle *theFile = NULL; BPTR fh; /* 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; return(NULL); /* This one was missing -Olsen */ } theFile -> fileTracker = lastTracker; theFile -> fh = fh; /* Does user want a buffer? */ if(bytes) { 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 to a new logical * position or report it's current position. The * parameter represets a signed offset. The 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(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. Seek(file,...) fixed * to use Seek(file -> fh,...) -Olsen. */ file -> lastPosition = Seek(file -> fh, 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 bytes from to * the buffered ARP 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 parameter. * Otherwise, it will return a negated system error code. */ LONG WriteARPFile(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; /* Catches mode and residual errors. */ if(file -> lastError) { sumbidge: return(-file -> lastError); } /* No buffer? */ if(!file -> buf) { 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); }