From: "Art Eschenlauer" <eschen@molbio.cbs.umn.edu> Subject: Routines that open blocks as filestreams in ThC 5 Date: Sat, 24 Sep 1994 12:31:39 -0500 (CDT) Here's a file to put into /mac/development/sources/snippets on mac.archive.umich.edu. Compress it as you please. /* blockio.c and blockio.h by Art Eschenlauer Routines to open blocks of memory as simple filestreams under ThinkC 5.0.4 Here are some routines to open a block of memory (Ptr or Handle) as a simple filestream (they do not support disk oriented commands like rewind and seek). I wrote these for porting unix software to ThinkC 5.0.4 using the standard libraries. I don't know if they would work on later versions of ThinkC std libraries. Bugs, comments, flames, etc. to eschen@molbio.cbs.umn.edu. Disclaimer: ++++++++++ NOTICE - GRATIS EXPERIMENTAL SOFTWARE ++++++++++ THIS IS EXPERIMENTAL SOFTWARE THAT IS PROVIDED "AS IS" FOR YOUR USE, GRATIS. NO WARRANTY OR REPRESENTATION WHATSOEVER, EITHER EXPRESS OR IMPLIED, IS GIVEN WITH RESPECT TO THIS SOFTWARE, ITS QUALITY, PERFORMANCE, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. IF YOU CHOOSE TO USE IT, YOU ASSUME ALL RISKS AS TO ITS QUALITY AND PERFORMANCE, INCLUDING (BUT NOT LIMITED TO) LOST TIME, DATA, MONEY, AND USE OF, OR DAMAGE TO, YOUR COMPUTER, YOUR BRAIN, ETC.. NO ONE SHALL IN ANY EVENT BE HELD LIABLE FOR ANY DAMAGE OR LOSS THAT MAY BE CAUSED BY, OR MAY BE ASSOCIATED WITH, THIS SOFTWARE. Translation: you didn't pay (or shouldn't have paid) for this EXPERIMENTAL software, so it's up to you to cover any damages. While I have tested this software (somewhat) and have no malicious intent in providing this software, excrement transpires, and it's up to you to wipe it up. YOU MAY NOT USE THIS SOFTWARE IF YOU DO NOT AGREE WITH THE ABOVE. */ /*-snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip-*/ /* * blockio.h - filestream i/o for pointers to memory blocks * Interface to public routines defined in blockio.c * * ThinkC 5.0.4, 7 June 1994 * notcopyright (!)) 1994 Art Eschenlauer - PUBLIC DOMAIN!!!! * * These routines are to support filestream i/o operations * to pointers, in the same manner that * <console.c> supports filestream io to the console interface and * <fopen.c> supports filestream io to macintosh files and * <stdio.c> supports filestream io to c-strings * * These routines require that blocks be allocated as Ptr's or Handle's * (excepting for freopenblock). * * I suggest that you may want to close output streams like so * fputc(0,theBlockStream); fclose(theBlockStream);. * Caveat emptor!! */ #ifndef __BLOCKSTREAM__ #define __BLOCKSTREAM__ 1 #include <stdio.h> //// Routines for dealing with Ptr's //// // (For Handle's, see below) // fopenPtr - open a (nonrelocatable) block of memory as a filestream // theBlockPtr is a Ptr to the block of memory // mode is "r", "w", "rb", or "wb" // (Nonrelocatable blocks cannot be opened in append mode.) FILE * fopenPtr( const Ptr theBlockPtr, const char *mode ); // freopenPtr - open a (nonrelocatable) block of memory as a filestream // using the file table pointed by fp. // theBlockPtr is a Ptr to the block of memory // mode is "r", "w", "rb", or "wb" // (Don't reopen stdin as "w" or "a" or reopen stdout or stderr as "r"!) FILE * freopenPtr( const Ptr theBlock, const char *mode, FILE *fp ); //// Routine for dealing with block of memory not allocated as Ptr //// // freopenblock - open a (nonrelocatable) block of memory as a filestream // using the file table pointed by fp. // theBlock is a Ptr to the block of memory // mode is "r", "w", "rb", or "wb" // NEVER use theBlockSize < 0 or theBlockSizeIncrement != 0, and be sure // that fp, theBlock, and mode are valid FILE * freopenblock( const char *theBlock, const char *mode, Size theBlockSize, Size theBlockSizeIncrement, FILE *fp ); //// Routines for dealing with Handle's //// /* * Note Well: * * Do not unlock relocatable blocks till after the filestream is closed!! * Do NOT assume that they do not relocate while the filestream is open!! * Only relocatable blocks (Handles) can be expanded as need be. * theBlockSizeIncrement (which specifies how much to grow the block * each time) must be nonzero for expansion of relocatable blocks; * use zero for theBlockSizeIncrement if using modes "r" or "w". */ // fopenHandle - open a relocatable block of memory as a filestream // theBlockHandle is a Handle to the block of memory // mode is "r", "w", or "a" // (Only relocatable blocks can be opened in append mode.) FILE * fopenHandle( const Handle theHandle, const char *mode, Size theBlockSizeIncrement ); // freopenHandle - open a block of memory as a filestream // using the file table pointed by fp. // theBlockPtr is a pointer to the block of memory // mode is "r", "w", "a", "rb", "wb", "ab" // (Don't reopen stdin as "w" or "a" or reopen stdout or stderr as "r"!) FILE * freopenHandle( const Handle theHandle, const char *mode, Size theBlockSizeIncrement, FILE *fp ); #endif __BLOCKSTREAM__ /*-snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip-*/ /* * blockio.c - filestream i/o for pointers to memory blocks * notcopyright (!)) 1994 Art Eschenlauer - PUBLIC DOMAIN!!!! * see "blockio.h" for usage info * ThinkC 5.0.4, 6 June 1994 * * To link successfully, projects using blockio.c must include the oops library. */ // stdio.h defines BUFSIZE and _IOFBF #include <stdio.h> #include <ansi_private.h> #include <errno.h> #include <string.h> // convenience includes //#include <Memory.h> // typedef long Size (see Memory.h) // typedef unsigned long size_t (see size_t.h) /// PRIVATE ROUTINE PROTOTYPES AND CLASS DECLARATION /// // i/o routine that is assigned to fp->proc static int blockio( FILE *fp, int i ); // setupblockfp actually does the work of setting up the file // table entry so that an entry can use a block of // memory, pointed by theBlockPtr and of size theBlockSize, // as the input filestream or output filestream: static FILE * setupblockfp( FILE *fp, const char *s, Size theBlockSize, Size theBlockSizeIncrement, Boolean _APPENDMODE ); // Cblock class - I do not intend other functions to use the Cblock class // I really only used it to dynamically allocate a structure that can be // referenced using fp->long in the file table. If you want to use it in other // functions, remove it from blockio.c and put it in blockio.h class Cblock : direct { public: /// Instance variables /// char *start; // first byte in block char *pos; // current position to read/write in block of memory char *end; // last byte in block Size increment; // size by which block is to expand if space runs out // (nonzero only if block is Handle opened for append) Handle hblock; // handle to block if block is relocatable char hstate; // state of handle (locked etc.) before opening block stream /// Methods /// // initialize Cblock object, true if succeeds Boolean Iblock( const char *s, Size theBlockSize, Size theBlockSizeIncrement, Boolean _APPENDMODE ); // destruct Cblock object void Dblock(void); }; /// PUBLIC ROUTINE PROTOTYPES /// #ifndef __BLOCKSTREAM__ #include "blockio.h" #endif __BLOCKSTREAM__ /// PRIVATE ROUTINE DEFINITIONS /// /* blockio - I/O proc installed into FILE structure, so that __read, * __write, and __close will all work with the block of memory * (see bufio.c). * * this is set up to point to the block associated with the fp */ static int blockio(FILE *fp, int i) { int result; register Cblock *this; register int counter; // recover reference to the object associated with the filepointer this = (Cblock *) fp->window; result = 0; switch(i) { case 0: //read //handle EOF case if (this->pos >= this->end) { fp->cnt = 0; fp->eof = 1; return EOF; } //determine # bytes to read counter = fp->cnt = (fp->size > (this->end - this->pos)) ? this->end - this->pos : fp->size; //transfer bytes to buffer while (counter--) *(fp->ptr++) = *(this->pos++); //reset fp->ptr fp->ptr = (unsigned char *) fp->buf; fp->eof = 0; if (!fp->binary) { #pragma options(honor_register) register unsigned char *s; register size_t n; register unsigned char *t; n = fp->cnt; s = (unsigned char *) fp->buf; for (; n && (t = memchr(s, '\r', n)); s = t) { *t++ = '\n'; n -= t - s; } } break; //(switch) case 1: //write // tryagain loop begins here tryagain: if ((counter = fp->cnt) > (this->end - this->pos)) { if( this->increment ) { // attempt to expand Handle size // and, if successful, try output again Size oldHandleSize; HUnlock( this->hblock ); SetHandleSize( this->hblock, this->increment + ( oldHandleSize = GetHandleSize( this->hblock ) ) ); HLock( this->hblock ); if (oldHandleSize == GetHandleSize( this->hblock )) goto bail; // break from tryagain loop // (else) this->end += ( *this->hblock - this->start ); this->pos += ( *this->hblock - this->start ); this->start = *this->hblock; fp->len += this->increment; this->end += this->increment; goto tryagain; // loop back to tryagain // tryagain loop ends here } bail: // write all you can, and // update result, counter, fp->cnt, fp->eof, and fp->pos // to reflect end of file condition result = EOF; fp->pos -= fp->cnt -= (counter = this->end - this->pos); fp->eof = 1; } else // fp->cnt <= (this->end - this->pos) fp->cnt = 0; // counter (set at tryagain) holds number of bytes to transfer // from buffer to block if (!fp->binary) { #pragma options(honor_register) register unsigned char *s; register size_t n; register unsigned char *t; n = counter; s = (unsigned char *) fp->buf; for (; n && (t = memchr(s, '\n', n)); s = t) { *t++ = '\r'; n -= t - s; } } while (counter--) *(this->pos++) = *(fp->ptr++); break; // (switch) case 2: //close //close filestream and delete object memset(fp,0,sizeof(FILE)); __checkfile(fp); this->Dblock(); } bye: return result; } static FILE * setupblockfp( FILE *fp, const char *s, Size theBlockSize, Size theBlockSizeIncrement, Boolean _APPENDMODE ) { /* setupblockfp creates and initializes the block data structure and the corresponding file structure. Actually, initialization of the block datastructure is delegated to the Iblock method, and setting up of the fp itself is handled by setupblockfp itself. */ Cblock *Oblock; //create and initialize Oblock object // that will implement io for block Oblock = new Cblock; if ( Oblock != NULL ) { if ( Oblock->Iblock( s, theBlockSize, theBlockSizeIncrement, _APPENDMODE ) ) { /// set up fp /// // set every fp->thing to zero memset(fp, 0, sizeof(FILE)); fp->len = ( theBlockSize > 0 ) ? theBlockSize : - theBlockSize ; /* __checkfile sets fp->ptr = fp->buf = &fp->one, fp->size = 1, fp->proc = nullio */ __checkfile(fp); //fp->refnum for a disk file >0, for an open stream !=0 fp->refnum = -1; //vectored routine (io proc) for block i/o fp->proc = blockio; // store object reference in fp->window // so that the blockio proc can recover it fp->window = (void *) Oblock; return fp; } Oblock->Dblock(); } return NULL; } /* Cblock::Iblock - set up instance variables for block i/o: Meanings of arguments: blocksize < 1: handle size-expansion is possible theBlockSizeIncrement > 0: handle size-expansion is requested _APPENDMODE == 1: start writing at the end of block instead of beginning */ Boolean Cblock::Iblock(const char *s, Size theBlockSize, Size theBlockSizeIncrement, Boolean _APPENDMODE ) { if (theBlockSize > 0) { this->hblock = NULL; this->start = (char *) s; } else { this->hblock = (Handle) s; this->hstate = HGetState( this->hblock ); HLock( this->hblock ); this->start = (char *) *( this->hblock ); } this->end = ( this->start + ( ( this->hblock ) ? - theBlockSize : theBlockSize ) ); this->pos = _APPENDMODE ? this->end : this->start ; // only handles can be resized because only handles can be relocated this->increment = theBlockSizeIncrement; return true; } /* Cblock::Dblock - destruct Cblock object */ void Cblock::Dblock() { // restore state to what it was before the Cblock->Iblock call if( this->hblock ) HSetState( this->hblock, this->hstate ); delete this; // self destruct } /// PUBLIC ROUTINE DEFINITIONS /// FILE * freopenblock( const char *theBlock, const char *mode, Size theBlockSize, Size theBlockSizeIncrement, FILE *fp ) { // if freopenblock fails, *fp is closed nevertheless... //be sure it is clear what to work with if (fp==NULL) goto fail2; //do not open console if you do not have to fp->std = 0; fclose(__checkfile(fp)); /* interpret "rwa" */ // not yet set up for update editing... switch ( mode[0] ) { case 'r': case 'R': case 'w': case 'W': // r & w require nonzero length blocks and zero expansion increment, // respectively if ( (0 == theBlockSize) || (0 != theBlockSizeIncrement) ) goto fail; setupblockfp( fp, theBlock, theBlockSize, 0, false ); break; // switch case 'a': case 'A': // append requires handles and nonzero expansion increment, respectively if ( (theBlockSize > 0) || (0 == theBlockSizeIncrement) ) goto fail; if ( setupblockfp( fp, ( char *) theBlock, theBlockSize, theBlockSizeIncrement, true ) ) { // the only good way out of here fp->append = 1; fp->pos = fp->len; break; // switch } // dribble through into default (like my financial planning ... ?) default: goto fail; } // the only way to this point is through the break switches // at the end of "rwa" cases /* interpret "b" */ switch( mode[1] ) // this will not even matter if strlen(mode)==1 { case 'b': case 'B': fp->binary = 1; } setvbuf(fp, NULL, _IOFBF, BUFSIZ); return fp; fail: // clean up the fp (if necessary) and return error info memset( fp, 0, sizeof(FILE) ); fail2: errno = EINVAL; return NULL; } FILE * fopenPtr( const Ptr theBlock, const char *mode ) { return freopenPtr( theBlock, mode, __getfile() ); } FILE * freopenPtr( const Ptr theBlock, const char *mode, FILE *fp ) { Size theBlockSize; theBlockSize = GetPtrSize(theBlock); if ( theBlockSize < 1 ) return(NULL); // reject errors & zero length pointers return freopenblock( (char *) theBlock, mode, theBlockSize, 0, fp ); } FILE * fopenHandle( const Handle theHandle, const char *mode, Size theBlockSizeIncrement ) { return freopenHandle( theHandle, mode, theBlockSizeIncrement, __getfile() ); } FILE * freopenHandle( const Handle theHandle, const char *mode, Size theBlockSizeIncrement, FILE *fp ) { Size theHandleSize; theHandleSize = GetHandleSize(theHandle); if ( theHandleSize < 0 ) { return(NULL); } return freopenblock( (char *) theHandle, mode, - theHandleSize, theBlockSizeIncrement, fp ); // Cblock->Iblock is responsible for using nonpositive theHandleSize // to determine that theHandle is in fact char **, not char * } /*-snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip- -snip-*/