From: "Art Eschenlauer" <[email protected]>
Subject: Routines that open blocks as filestreams in ThC 5
To: [email protected]
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 [email protected].

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-*/