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