/*
 ************************************************************************
 *            Translate a mainframe IBM VB record format to
 *                    	the DEC LF_stream format
 *
 * Call the routine
 *      getvb_rec tape_file_name output_file_name
 *
 * VB IBM physical record (block) has a variable length and contains
 * a 4-byte block header. Block header is of the form
 *	two-byte field specifying the size of the whole block (incl. header)
 *	two-byte field containing zeros
 * IBM block body contains a number of records of variable size. Every record
 * has the 4-byte descriptor (of the same form as block descriptor)
 *
 * The present program reads VB records from the input file and writes
 * them to the output file terminating records with LF chars.
 *
 * RMS service is used in Block I/O mode to read the tape file
 *
 ************************************************************************
 */

#include <rms.h>
#include <ssdef.h>
#include "stdio.h"
#include "assert.h"


/*
 *-----------------------------------------------------------------------
 *			Tape file handling
 */

#define Block_maxlen  8000 
static char Buffer [Block_maxlen];
static int Block_no;
static char * Buffer_ptr;
static char * Buffer_end_ptr;

static struct FAB RMS_fab;
static struct RAB RMS_rab;

static int RMS_status;

static void handle_rms_error(title)
const char * title;
{
  if( RMS_status == RMS$_NORMAL )
    return;				/* No errors occured		*/

  _error("RMS error with code %xH occured while performing %s",
	 RMS_status,title);
}


static void tape_file_open(file_name)
const char * file_name;
{
  RMS_fab = cc$rms_fab;		/* Initialize the File Access Block	*/
  RMS_fab.fab$l_fna = file_name;
  RMS_fab.fab$b_fns = strlen(file_name);
  RMS_fab.fab$w_mrs = Block_maxlen;
  RMS_fab.fab$w_bls = Block_maxlen;	/* Tape max block size		*/
  RMS_fab.fab$b_fac = FAB$M_BIO | FAB$M_GET;	/* Specify block op	*/
  RMS_fab.fab$l_fop = FAB$M_NFS;		/* Not-standard volume	*/

  RMS_rab = cc$rms_rab;		/* Initialize the Record Access Block	*/
  RMS_rab.rab$l_fab = &RMS_fab;
  RMS_rab.rab$l_rop = RAB$M_BIO | RAB$M_RAH;	/* Read ahead		*/
  RMS_rab.rab$l_bkt = 0;			/* Start with curr blk	*/
  RMS_rab.rab$l_ubf = Buffer;
  RMS_rab.rab$w_usz = Block_maxlen;

  if( (RMS_status = sys$open(&RMS_fab)) != RMS$_NORMAL )/* Open the file*/
  {
    message("Can't open the tape file '%s' due to errors listed below",
	  file_name);
    handle_rms_error("Open");
  }
  
  RMS_status = sys$connect(&RMS_rab);		/* Prepare the RAB	*/
  handle_rms_error("Connect");

  Block_no = 0;
}


          				/* Read a block in buffer	*/
static int tape_file_read()		/* Returns EOF on end-of-file	*/
{
  Block_no++;
  RMS_status = sys$read(&RMS_rab);
  if( RMS_status != RMS$_NORMAL && RMS_status != RMS$_EOF )
  {
    message("Error while reading the %d. block of the tape file",Block_no);
    handle_rms_error("Read");
  }

  Buffer_ptr = Buffer;
  Buffer_end_ptr = Buffer + RMS_rab.rab$w_rsz;

  if( RMS_status == RMS$_EOF )
  {
    message("\nEOF while reading the %d. tape block\n",Block_no);
    return EOF;
  }

  return 1;
}


static void tape_file_error(title)
const char * title;
{
  _error("\nTape file error '%s'\n- while processing the %d. block\n-\
 near the offset %xH",title,Block_no,Buffer_ptr-Buffer);
}		


static int tape_file_getc()	/* Return the current char	*/
{
  if( Buffer_ptr >= Buffer_end_ptr )
    tape_file_error("Attempt to get a char past the record read");
  return *Buffer_ptr++;
}

				/* Get a record from the Buffer read	*/
static char * tape_file_get_record(reclen)
const int reclen;
{
  register char * rec_ptr = Buffer_ptr;
  if( (Buffer_ptr += reclen) > Buffer_end_ptr )
    tape_file_error("Too long record to get");
  return rec_ptr;
}

				/* How many chars were read from the buf*/
#define chars_read() (Buffer_ptr - Buffer)

/*
 *-----------------------------------------------------------------------
 *    	    IBM variable records logical structure handling
 */

			/* Read the descriptor from the VB block header	*/
			/* return the size of item it precedes		*/
static int size_of_item()
{
  unsigned char c1, c2;
  int size;
  c1 = tape_file_getc(); c2 = tape_file_getc(); /* Two-byte size field	*/
  size = c1 * 256 + c2;
  if( size <= 4 )
    tape_file_error("Item size from the descriptor <= 4");
  c1 = tape_file_getc(); c2 = tape_file_getc(); 
  if( c1 !=0 || c2 != 0 )
  { 
    message("\nNon zero padding blanks %x %x\n",c1,c2);
    tape_file_error("No blank two-byte field found in the descriptor");
  }

  return size-4;
}


			/* Extract one record from the tape file	*/
			/* and write it to the fo			*/
			/* Files are assumed to be properly located	*/
static void process_record(fo)
FILE * fo;
{
  int record_size = size_of_item();
  register char * rec_ptr = tape_file_get_record(record_size);
  register int i;

  /*message("\n%d-bytes record is being processed",record_size);*/

  fwrite(rec_ptr,record_size,1,fo);
  fputc('\n',fo);
}


/*
 *========================================================================
 *			Initialization routines
 */

static void help()
{
  message("\n\n\t\t\tTranslate a mainframe IBM VB record format to");
  message("\n\t\t\t\tthe DEC Stream_LF standard");
  message("\n\n\n\tgetvb_rec input_file_name output_file_name");
  message("\n\n");
  exit();
}


/*
 *-----------------------------------------------------------------------
 *				Root module
 *  Parse the command string
 *  Two arguments are expecting (excluding the pgm name as the 0. arg)
 *  one     "input_file_name"
 *  another "output_file_name"
 */

main(argc,argv)
int argc;				/* No. of arguments being passed*/
char *argv[];				/* Vector of pointers to args 	*/
{
 FILE *fo;                         	/* Files under operation        */


 if( argc != 3 )                        /* Too few or too many arguments*/
   help();

 tape_file_open(argv[1]);
 fo  = fopenc(argv[2],"wb");

 for(;;)				/* Read an IBM block		*/
 {           			
   int actual_block_size;
   int block_offset;			/* Offset for the present blk*/

   if( tape_file_read() == EOF )
     break;

   actual_block_size = size_of_item();
   block_offset = chars_read();		/* Offset for the present blk*/
   message("\n\n------>Block spanned %d bytes is being processed\n",
           actual_block_size);
   while( chars_read() < block_offset + actual_block_size-1 )
      process_record(fo);
 }

 fclose(fo);
 message("\n%s has been successfully read to %s\n",argv[1],argv[2]);

}
