/*
 * Define substitute routines for the fopen/fclose/fread/fgetc calls done by
 * the gif decoder.  Map the gif file to memory and access via page faults.
 */
#include stdio
#include atrdef
#include fab
#include fibdef
#include nam
#include iodef
#include secdef
typedef struct {
    unsigned char *pos;		/* points to next char to read */
    unsigned char *start;
    unsigned char *end;
    int chan, size;
} map_block;

static int map_block_in_use = 0;
static int max_mapped = 0;	/* largest address region used */
static unsigned char *max_region = NULL;
static map_block giffile;
/*
 * Open file for access.  A returned size of -1 indicates error.
 */
map_block *gif_fopen ( Filestring )
    char *Filestring;
{
    unsigned char *address, *inadr[2], *retadr[2];
    int size, status, channel, func_code;
    int sys$assign(), sys$qiow(), sys$crmpsc(), sys$expreg();
    short int iosb[4];
    struct { long length; char *adr; } desc, fib_desc;
    struct {
	unsigned char rtype, rattrib;
	unsigned short int  rsize, hiblk[2], efblk[2], ffbyte;
	unsigned char bktsize, vfcsize;
	unsigned short int maxrec, defext, gbc, fill[4], versions;
    } fat;			/* File attributes block (RMS) */
   struct FAB fab;		/* File access block (RMS) */
   struct NAM nam;		/* Name block (RMS) */
   struct fibdef fib;		/* File information block (XQP) */
   struct atrdef atr[2];	/* Read attributes descriptor list */
   char esa[256], rsa[256];	/* aux storage for nam block */

   /*
    * Close currently open file and init block to error status (size = -1).
    */
   if ( map_block_in_use ) (void) gif_fclose();
   giffile.start = giffile.end = giffile.pos = "";
   giffile.size = -1;

      /* Use RMS services to lookup fspec, initialize FAB and NAM blocks */

      fab = cc$rms_fab;
      fab.fab$l_fna = Filestring; fab.fab$b_fns = strlen ( Filestring );
      fab.fab$l_dna = ".GIF"; fab.fab$b_dns = strlen ( fab.fab$l_dna );
      fab.fab$l_fop = FAB$M_NAM;	/* file options */
      fab.fab$l_nam = &nam;
      nam = cc$rms_nam;
      nam.nam$b_ess = 255;
      nam.nam$l_esa = &esa;
      nam.nam$b_rss = 255;
      nam.nam$l_rsa = &rsa;

      /* use $parse and $search for find file, device and FID will end up in 
         nam  block */

      status = sys$parse ( &fab );  if ((status&3) != 1) return &giffile;
      status = sys$search ( &fab ); if ((status&3) != 1) return &giffile;

      /* Define descriptor to assign and load FID in FIB */

      desc.length = nam.nam$t_dvi[0];		/* length */
      desc.adr = &nam.nam$t_dvi[1];
      func_code = IO$_ACCESS + IO$M_ACCESS; /* actually open the file */
      fib.fib$r_fid_overlay.fib$w_fid[0] = nam.nam$w_fid[0];
      fib.fib$r_fid_overlay.fib$w_fid[1] = nam.nam$w_fid[1];
      fib.fib$r_fid_overlay.fib$w_fid[2] = nam.nam$w_fid[2];

   /* build atrribute descriptor list for returning record attributes */

   atr[0].atr$w_size = sizeof(fat);
   atr[0].atr$w_type = ATR$C_RECATTR;
   atr[0].atr$l_addr = &fat;
   atr[1].atr$w_size = 0; atr[1].atr$w_type = 0;

   /* assign channel to disk specified in NAM block. */

   channel = 0;
   status = sys$assign ( &desc, &channel, 0, 0 );
   if ((status&3) != 1) return &giffile;
    
    /* build FIB and access (open file) */
   fib_desc.length = 10; fib_desc.adr = (char *) &fib;
    fib.fib$r_acctl_overlay.fib$l_acctl = 0;
    status = sys$qiow 
	( 0, channel, func_code, &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 );
    if ( (status&1) == 1 ) status = iosb[0];
    if ( (status&1) != 1 ) return &giffile;

    /* Determine size of file and create section */
    size = fat.efblk[0];
    size = (size<<16) | fat.efblk[1];	/* invert word order */
    size = (size-1)*512 + fat.ffbyte;

    /*
     * Make a large region of P0 space to hold contents of file plus
     * 500 blocks spare.
     */
    if ( max_mapped <= size ) {
	int pages;
	pages = ((size+511)/512) + 500;
        status = sys$expreg ( pages, &retadr, 0, 0 );
        if ( (status&1) != 1 ) return &giffile;
        max_region = retadr[0];
        max_mapped = (pages*512) - 1;
    }

    /* Map file into address space. */
    inadr[0] = max_region;
    inadr[1] = &max_region[size-1];
    status = sys$crmpsc ( &inadr, &retadr, 0, 0, 0, 0, 0,
		channel, (size+511)/512, 0, 0, 64 );
    if ( (status&1) != 1 ) return &giffile;
    address = retadr[0];
    map_block_in_use = 1;

    /*
     * Allocate structure to store file state and initialize it.
     */
    giffile.start = address;
    giffile.end = &address[size];
    giffile.pos = address;
    giffile.chan = channel;
    giffile.size = size;
    return &giffile;
}

int gif_fclose ( )
{
    void sys$dassgn();
    if ( map_block_in_use && (giffile.size > 0) ) sys$purgws ( &giffile.start );
    map_block_in_use = 0;
    sys$dassgn ( giffile.chan );
    return 0; 
}

int gif_fgetc ( )
{
    int result;
    if ( giffile.pos >= giffile.end ) return -1;
    result = *giffile.pos++;
    return result;
}

int gif_fread ( buffer, unit, count )
    unsigned char *buffer;
    unsigned int unit, count;	/* max count is 65535 */
{
    unsigned char *next_pos;
    /* determin number of charaters to move */
    count *= unit;
    if ( giffile.pos >= giffile.end ) return 0;
    next_pos = &giffile.pos[count];
    while ( next_pos >= giffile.end ) next_pos = &giffile.pos[--count];

    LIB$MOVC3 ( &count, giffile.pos, buffer );
    giffile.pos = next_pos;
    return count;
}

unsigned char *gif_freadm ( unit, count )
    unsigned int unit, count;	/* max count is 65535 */
{
    unsigned char *cur_pos, *next_pos;
    /* determin number of charaters to move */
    count *= unit;
    if ( giffile.pos >= giffile.end ) return NULL;
    cur_pos = giffile.pos;
    next_pos = &cur_pos[count];
    while ( next_pos >= giffile.end ) --next_pos;
    giffile.pos = next_pos;
    return cur_pos;
}

int gif_fget_counted ( map, desc )
    map_block *map;
    struct { int len; char *addr; } *desc;
{
    int length;
    if ( map->pos < map->end ) {
	/* Get length of record and truncate to EOF if needed */
	for ( length = *map->pos++; &map->pos[length] > map->end; --length);

	/* fill out descriptor and update position pointer */
	desc->len = length;
	desc->addr = map->pos;
	map->pos = &map->pos[length];
	return 1;
    }
    return 2160;
}
