/*--gio.c----------------------------------------------------------------- * Buffered i/o. */ #include "main.h" #include #include #define local static /* --- Segment of optional buffer for speeding-up i/o. */ local UWORD ioBufferSeg = NULL; local UWORD ioBufSize = 0, ioBufNext, ioBufLimit; local BOOL ioWriting; /* ---------- OpenIOBuffer ---------- */ void OpenIOBuffer(BOOL forWriting) { register WORD bufSize; auto WORD tempSeg; ioWriting = forWriting; /* --- Allocate i/o buffer. */ if (ioBufferSeg == NULL) { for (bufSize = 16 * 1024; bufSize >= 512; bufSize >>= 1) { ioBufferSeg = DAlloc(bufSize); if (ioBufferSeg) { /* --- Leave at least 4 KB free for other uses. */ tempSeg = DAlloc(4096); if (tempSeg) { tempSeg = SFree(tempSeg); break; /* Success; leave loop. */ } else ioBufferSeg = SFree(ioBufferSeg); } } if (ioBufferSeg != NULL) ioBufSize = bufSize; } ioBufNext = ioBufLimit = 0; /* Reading: When next==limit, buffer is empty. * Writing: When next== 0, buffer is empty. */ } /* ---------- CloseIOBuffer ----------------- * Writing: flush any remaining data to file. * Free the i/o buffer. Do this whenever finish i/o, so RAM not tied up. */ void CloseIOBuffer(WORD file) { if (ioWriting) GFlush(file); /* TBD: ignoring error. */ ioBufferSeg = SFree(ioBufferSeg); ioBufSize = 0; } /* ---------- GRead --------------------------------------------- * Like DOS "read" function, RETURNS # bytes read, or 0 at EOF, * or -1 at error. */ WORD GRead(WORD file, UBYTE *buffer, UWORD nBytes) { UWORD nRemaining, nAvail, nThisTime; if (ioBufferSeg) { nAvail = ioBufLimit - ioBufNext; if (!nAvail && nBytes >= ioBufSize) goto readDirectly; /* More efficient to read buffer directly. */ /* --- Remaining # bytes that need reading. */ for (nRemaining = nBytes; nRemaining; nRemaining -= nThisTime) { if (!nAvail) { ioBufLimit = nAvail = readdos(file, ioBufferSeg, 0, ioBufSize); if (nAvail == 0xffff || nAvail == 0) { ioBufLimit = 0; return (nAvail); } ioBufNext = 0; } nThisTime = MIN(nRemaining, nAvail); far_movmem(ioBufferSeg, ioBufNext, dataseg(), (UWORD)buffer, (UWORD)nThisTime); buffer += nThisTime; ioBufNext += nThisTime; nAvail -= nThisTime; } return (nBytes); } else /* No ioBuf */ readDirectly: return (read(file, buffer, nBytes)); } /* --------------- GReadOne -------------------------------- * Like "GRead", but read a single byte, with less overhead. */ WORD GReadOne(WORD file, UBYTE *buffer) { if (!ioBufferSeg || ioBufLimit == ioBufNext) return GRead(file, buffer, 1); *buffer = *BYTE_FAR_PTR(ioBufferSeg, ioBufNext); ioBufNext++; return (1); } /* ---------- GFlush ------------------------------ * Write out any bytes remaining in the buffer. * Return what writedos does, or 0 if nothing to do. */ WORD GFlush(WORD file) { auto WORD result = 0; if (ioBufferSeg && ioBufNext && ioWriting) { result = writedos(file, ioBufferSeg, 0, ioBufNext); if (result < ioBufNext) result = -1; /* Disk must be full, turn it into an error. */ ioBufNext = 0; /* Writing: buffer is empty. */ } return (result); } /* ---------- GSeek ---------------------------------------------------- * When reading, ASSUMES seeking forwards. * When writing, flush buffer and use lseek. * RETURNS 0 if succeeded in seeking w/i buffer. DIFFERENT THAN LSEEK. */ LONG GSeek(WORD file, LONG count, WORD seekType) { UWORD nAvail; if (ioBufferSeg) { if (ioWriting && ioBufNext) { GFlush(file); /* TBD: Ignoring error code. */ goto seekDirectly; } nAvail = ioBufLimit - ioBufNext; if (seekType != SEEK_CUR) { /* Check for absolute positioning */ goto emptyAndSeekDirectly; } if (count > nAvail) { count -= nAvail; /* Adjust seek for bytes already read ahead. */ emptyAndSeekDirectly: ioBufNext = ioBufLimit; /* Reading: buffer is empty. */ goto seekDirectly; } ioBufNext += (UWORD)count; /* Seek within the buffer. */ return (0); /* DIFFERENT THAN LSEEK */ } else seekDirectly: return lseek(file, count, seekType); } /* ---------- GWrite --------------------------------------------- * Like DOS "write" function, RETURNS # bytes written, or -1 at error. * DIFFERENCE: if can't write them all, returns -1 instead of # written, * UNLESS "writeDirectly", which just uses write(). */ WORD GWrite(WORD file, UBYTE * buffer, UWORD nBytes) { UWORD nRemaining, nUnused, nThisTime, nWritten; if (ioBufferSeg) { if (!ioBufNext && nBytes >= ioBufSize) goto writeDirectly; /* More efficient to write buffer directly. */ nUnused = ioBufSize - ioBufNext; /* # bytes unused in buffer. */ /* Remaining # bytes that need writing. */ for (nRemaining = nBytes; nRemaining; nRemaining -= nThisTime) { if (!nUnused) { nWritten = writedos(file, ioBufferSeg, 0, ioBufNext); if (nWritten != ioBufNext) { ioBufNext = 0; /* Flush the data -- it is lost. */ return (-1); /* ERROR */ } ioBufNext = 0; nUnused = ioBufSize; } nThisTime = MIN(nRemaining, nUnused); far_movmem(dataseg(), (UWORD)buffer, ioBufferSeg, ioBufNext, (UWORD)nThisTime); buffer += nThisTime; ioBufNext += nThisTime; nUnused -= nThisTime; } return (nBytes); /* OKAY */ } else /* No ioBuf */ writeDirectly: return (write(file, buffer, nBytes)); }