/* 20-DEC-2003  SMS.  Changed to get the default BACKUP save set block
 *                    size from the file, allowing the user to override
 *                    this value with a command-line parameter.
 *
 *                    Changed to allow DEC C as well as VAX C.  DEC C
 *                    V4.0-000 gives one (harmless) warning, caused by a
 *                    lame declaration in ATRDEF.H:
 *                          unsigned int atr$l_addr;
 *                    A comment with a work-around may be found near the
 *                    line with the problem.
 *
 *                    Added a version number, V2.0.
 */

/*
 * FIXREC fixes the record size of BACKUP savesets transfered using FTP.
 * Compile and link it using VAX C (or copy the pre-compiled version,
 * FIXREC.EXE, in BINARY) mode, and define it as a foreign command:
 *
 *	$ FIXREC :== $<wherever>FIXREC
 *
 * Then copy the saveset, SAVESET.A, using BINARY mode.
 *
 *	  	BINARY is ESSENTIAL!
 *
 * The resulting file has all the right bits, but has the record size set
 * wrong (typically to 512 bytes).  Do:
 *
 *	$ FIXREC SAVESET.A
 *
 * and the record size will be changed to the correct value.
 *
 */

#include <stdio.h>      
#include <descrip.h>
#include <rms.h>
#include <ssdef.h>
#include <fibdef.h>
#include <iodef.h>
#include <atrdef.h>

/* #include <fchdef.h> */

#include stdlib
#include string
#include stsdef
#include starlet
#include lib$routines

/* #include "fatdef.h" */
/*
 *				FATDEF.H
 *
 * Definitions created by C_DEFS at 17-SEP-1988 15:33:30.67
 */
#define FAT$C_UNDEFINED	0
#define FAT$C_FIXED	1
#define FAT$C_VARIABLE	2
#define FAT$C_VFC	3
#define FAT$C_STREAM	4
#define FAT$C_STREAMLF	5
#define FAT$C_STREAMCR	6
#define FAT$C_SEQUENTIAL	0
#define FAT$C_RELATIVE	1
#define FAT$C_INDEXED	2
#define FAT$C_DIRECT	3
#define FAT$M_FORTRANCC	1
#define FAT$M_IMPLIEDCC	2
#define FAT$M_PRINTCC	4
#define FAT$M_NOSPAN	8
#define FAT$K_LENGTH	32
#define FAT$C_LENGTH	32
#define FAT$S_FATDEF	32
#define FAT$B_RTYPE	0
#define FAT$S_RTYPE	4
#define FAT$V_RTYPE	0
#define FAT$S_FILEORG	4
#define FAT$V_FILEORG	4
#define FAT$B_RATTRIB	1
#define FAT$V_FORTRANCC	0
#define FAT$V_IMPLIEDCC	1
#define FAT$V_PRINTCC	2
#define FAT$V_NOSPAN	3
#define FAT$W_RSIZE	2
#define FAT$L_HIBLK	4
#define FAT$W_HIBLKH	4
#define FAT$W_HIBLKL	6
#define FAT$L_EFBLK	8
#define FAT$W_EFBLKH	8
#define FAT$W_EFBLKL	10
#define FAT$W_FFBYTE	12
#define FAT$B_BKTSIZE	14
#define FAT$B_VFCSIZE	15
#define FAT$W_MAXREC	16
#define FAT$W_DEFEXT	18
#define FAT$W_GBC	20
#define FAT$W_VERSIONS	30
/*
 * End of C_DEFS definitions
 */

#define prog_version "V2.0"

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int LONG;
struct FAT {
  BYTE fat$b_rtype;
  BYTE fat$b_rattrib;
  WORD fat$w_rsize;
  LONG fat$l_hiblk;
  LONG fat$l_efblk;
  WORD fat$w_ffbyte;
  BYTE fat$b_bktsize;
  BYTE fat$b_vfcsize;
  WORD fat$w_maxrec;
  WORD fat$w_defext;
  WORD fat$w_gbc;
  BYTE reserved[6];
  WORD unused;
  WORD fat$w_versions;
};

struct IOSB {
  short iosb$w_value;
  short iosb$w_count;
  long  iosb$l_info;
};                  

struct acp_d {
  short acp$w_count;
  short acp$w_notused;
  long  acp$a_pointer;
};

static struct fibdef fib;
static struct acp_d fib_desc;

struct NAM nam_blk;
struct FAB fab_blk;
char exp_str[NAM$C_MAXRSS];
char res_str[NAM$C_MAXRSS];
struct dsc$descriptor_s res_str_d;

/* Data buffer (read) and RAB structure for getting the record size. */

#define REC_BUF_SIZ 65534

union
   {
   char  byte[ REC_BUF_SIZ];
   short int  word[ REC_BUF_SIZ/ 2];
   } rec_buf;

struct RAB rab_blk;


         
main (argc, argv)
    int argc;
    char **argv;
{
  long status;
  char dvi[NAM$C_DVI];
  struct dsc$descriptor_s dev_desc;
  struct atrdef atr[2];
  unsigned short chan = 0;
  struct IOSB iosb;
  struct FAT  fat;
  int nrecsize = -1;

  if (argc != 2 && argc != 3)
    {  
      fprintf( stderr,
 "%s, version %s.\n\n", argv[ 0], prog_version);

       fprintf( stderr,
 "Usage: fixrec filename [recsize]\n\n");

      fprintf( stderr,
 "       Default \"recsize\" is the original BACKUP value.\n\n");

      exit(SS$_ABORT);
    }

  if (argc > 2)
   {
      nrecsize = atoi(argv[2]);
   }                
  nam_blk = cc$rms_nam;
  nam_blk.nam$l_rsa = res_str;
  nam_blk.nam$b_rss = NAM$C_MAXRSS;
  nam_blk.nam$l_esa = exp_str;
  nam_blk.nam$b_ess = NAM$C_MAXRSS;

  fab_blk = cc$rms_fab;
  fab_blk.fab$l_fop = FAB$M_NAM;
  fab_blk.fab$l_nam = &nam_blk;
  fab_blk.fab$l_fna = argv[1];


/* Prepare to open file for read access. */

  fab_blk.fab$v_get = 1;

  rab_blk = cc$rms_rab;
  rab_blk.rab$l_fab = &fab_blk;
  rab_blk.rab$l_ubf = rec_buf.byte;
  rab_blk.rab$w_usz = sizeof( rec_buf.byte);


  res_str_d.dsc$w_length = NAM$C_MAXRSS;
  res_str_d.dsc$b_dtype  = DSC$K_DTYPE_T;
  res_str_d.dsc$b_class  = DSC$K_CLASS_S;
  res_str_d.dsc$a_pointer= res_str;
                      
  fab_blk.fab$b_fns = strlen(argv[1]);
  status = sys$parse(&fab_blk);
  if ((status & 0x1) != 1)
    exit (status);        

  status = sys$search(&fab_blk);
  if ((status & 0x1) != 1)
    {
      if (status == RMS$_NMF)
        exit(1);
      exit(status);
    }
  res_str_d.dsc$w_length = nam_blk.nam$b_rsl;
  status = lib$put_output(&res_str_d);
  if ((status & 0x1) != 1)
    exit(status);
      
  dev_desc.dsc$a_pointer = &(nam_blk.nam$t_dvi[1]);
  dev_desc.dsc$w_length = nam_blk.nam$t_dvi[0];
  dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
  dev_desc.dsc$b_class = DSC$K_CLASS_S;

/* Open (and connect) the file to prepare to get the BACKUP record size. */

  status = sys$open( &fab_blk);
  if ($VMS_STATUS_SEVERITY( status) != STS$K_SUCCESS)
    {
      fprintf (stderr, "Open (read) failed.\n");
      exit(status);
    }

  status = sys$connect( &rab_blk);
  if ($VMS_STATUS_SEVERITY( status) != STS$K_SUCCESS)
    {
      fprintf (stderr, "Connect (read) failed.\n");
      exit(status);
    }

/* Read the first block of the file to get the BACKUP record size. */

  status = sys$get( &rab_blk);
  if ($VMS_STATUS_SEVERITY( status) != STS$K_SUCCESS)
    {
      fprintf (stderr, "Read failed.\n");
      exit(status);
    }

  if (nrecsize < 0)
    {
      nrecsize = rec_buf.word[ 20];
      printf( "Using BACKUP record size of %d.\n", nrecsize);
    }
  else
    {
      printf( "Using user-specified record size of %d.\n", nrecsize);
      if (nrecsize != rec_buf.word[ 20])
        {
          printf( "    WARNING: BACKUP record size is  %d.\n",
           rec_buf.word[ 20]);
        }
    }

/* Close the file. */

  status = sys$close( &fab_blk);
  if ($VMS_STATUS_SEVERITY( status) != STS$K_SUCCESS)
    {
      fprintf (stderr, "Close failed.\n");
      exit(status);
    }


  /*
   * Open channel to disk
   */
  status = sys$assign(&dev_desc, &chan, (long)0, (long)0);
  if (status != SS$_NORMAL)
    {
      fprintf (stderr, "Unable to assign disk\n");
      exit(status);
    }

  /*
   * Setup File Information Block
   */
  fib_desc.acp$a_pointer = (long) &fib;
  fib_desc.acp$w_count   = 24;

/* Deal with VAX C versus DEC C header differences. */

#if defined(__VAXC) || defined(VAXC)

#define X_fib$l_acctl fib$r_acctl_overlay.fib$l_acctl
#define X_fib$w_nmctl fib$r_nmctl_overlay.fib$w_nmctl
#define X_fib$w_fid fib$r_fid_overlay.fib$w_fid
#define X_fib$w_did fib$r_did_overlay.fib$w_did

# else /* defined(__VAXC) || defined(VAXC) */

#define X_fib$l_acctl fib$l_acctl
#define X_fib$w_nmctl fib$w_nmctl
#define X_fib$w_fid fib$w_fid
#define X_fib$w_did fib$w_did

#endif /* defined(__VAXC) || defined(VAXC) */

  /*
   * Use File id and Directory id from NAM
   */
  fib.X_fib$w_fid[0] = nam_blk.nam$w_fid[0]; 
  fib.X_fib$w_fid[1] = nam_blk.nam$w_fid[1]; 
  fib.X_fib$w_fid[2] = nam_blk.nam$w_fid[2]; 
  fib.X_fib$w_did[0] = nam_blk.nam$w_did[0]; 
  fib.X_fib$w_did[1] = nam_blk.nam$w_did[1]; 
  fib.X_fib$w_did[2] = nam_blk.nam$w_did[2]; 
  fib.X_fib$l_acctl  = 0;
  fib.X_fib$w_nmctl  = FIB$M_FINDFID;

  /*
   * Read FAT
   */
  atr[0].atr$w_type = ATR$C_RECATTR;
  atr[0].atr$w_size = ATR$S_RECATTR;

  atr[0].atr$l_addr = &fat;
/*
  For DEC C with a lame declaration in ATRDEF.H, use this instead:
  atr[0].atr$l_addr = (unsigned int) &fat;
*/

  atr[1].atr$w_size = 0;
  atr[1].atr$w_type = 0;

  status = sys$qiow((long)0, chan, (short)(IO$_ACCESS), &iosb,
                    (long)0, (long)0,
		    &fib_desc, (long)0, 0, 0,
		    atr, (long)0);
  if ((status != SS$_NORMAL) ||
      (iosb.iosb$w_value != SS$_NORMAL))
    {
      sys$dassgn (chan);
      fprintf (stderr, "Unable to access file: %d\n", status);
      exit(status);
    }

  if (fat.fat$b_rtype != FAT$C_FIXED)
    {
      fprintf (stdout, "File record type must be FIXED\n");
      exit(SS$_ABORT);
    }

  fprintf (stdout, "Old record size: %d\n", (int)fat.fat$w_rsize);
  fat.fat$w_rsize = nrecsize;
  fprintf (stdout, "New record size: %d\n", (int)fat.fat$w_rsize);

  status = sys$qiow((long)0, chan, (short)(IO$_MODIFY), &iosb,
                    (long)0, (long)0,
		    &fib_desc, (long)0, 0, 0,
		    atr, (long)0);
  if ((status != SS$_NORMAL) ||
      (iosb.iosb$w_value != SS$_NORMAL))
    {
      sys$dassgn (chan);
      fprintf (stderr, "Unable to modify file: %d\n", status);
      exit(status);
    }
}
