mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-14 07:55:24 +02:00
1049 lines
26 KiB
C
1049 lines
26 KiB
C
/* VMFPLC2.C (c) Copyright Ivan S. Warren 2010-2012 */
|
|
/* Utility to create VMFPLC2 formatted tapes */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#include "hercules.h"
|
|
#include "tapedev.h"
|
|
#include "hetlib.h"
|
|
#include "ftlib.h"
|
|
#include "sllib.h"
|
|
#include "herc_getopt.h"
|
|
|
|
#define UTILITY_NAME "vmfplc2"
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Maximum sized tape I/O buffers... */
|
|
/*-------------------------------------------------------------------*/
|
|
static BYTE bfr[ MAX_BLKLEN ];
|
|
static BYTE bfr2[ MAX_BLKLEN ];
|
|
|
|
/* The blocksize of 4000 is the block size expected by VMFPLC2 */
|
|
#define TAPE_BLOCKSIZE 4000
|
|
/* The last data record must have a modulo of 800 */
|
|
#define TAPE_BLKSIZE_MODULO 800
|
|
/* NOTE : The tape blocks will always be 5 more to account */
|
|
/* for record headers */
|
|
|
|
|
|
/* The File Status Table (as expected by VMFPLC2) */
|
|
/* The "Basic" version is the FST for VM/370 */
|
|
/* while the EDF extention is for support of VM */
|
|
/* version starting with BSEPP. */
|
|
|
|
struct FST
|
|
{
|
|
/* Basic (short) FST */
|
|
unsigned char fn[8]; /* File name */
|
|
unsigned char ft[8]; /* File Type */
|
|
unsigned char dt[4]; /* date : MMDDHHmm (In DCB) */
|
|
unsigned char wp[2]; /* Write ptr */
|
|
unsigned char rp[2]; /* Read ptr */
|
|
unsigned char fm[2]; /* File Mode */
|
|
unsigned char reccount[2]; /* 16 bit record count */
|
|
unsigned char fcl[2]; /* First chain link */
|
|
unsigned char recfm; /* Record fmt (F or V) */
|
|
unsigned char flag1; /* Flags */
|
|
unsigned char lrecl[4]; /* 32 bit record length */
|
|
unsigned char db[2]; /* Data Block Count */
|
|
unsigned char year[2]; /* Year (EBCDIC) */
|
|
/* The following 2 fields are VMFPLC2 specific */
|
|
unsigned char lastsz[4]; /* Number of 800 byte blocks in last record */
|
|
unsigned char numblk[4]; /* Count of complete 4000 byte records */
|
|
/* Extended EDF FST */
|
|
unsigned char afop[4]; /* Alt File Origin Ptr */
|
|
unsigned char adbc[4]; /* Alt Data Block Count */
|
|
unsigned char aic[4]; /* Alt Item Count */
|
|
unsigned char levels; /* Levels of indirection */
|
|
unsigned char ptrsz; /* Size of Pointers */
|
|
unsigned char adt[6]; /* Alt Date (YYMMDDHHMMSS) (In DCB) */
|
|
unsigned char extra[4]; /* Reserved */
|
|
/* Global length MUST be 72 */
|
|
};
|
|
|
|
/* The FST tape record (with VMFPLC2 header) */
|
|
struct FST_BLOCK
|
|
{
|
|
unsigned char hdr[5];
|
|
struct FST fst;
|
|
};
|
|
|
|
/* This structure contains processing options */
|
|
/* and some runtime status information */
|
|
|
|
struct options
|
|
{
|
|
char *cmd;
|
|
char *procfile;
|
|
char *tapefile;
|
|
#define VMFPLC2DUMP 0
|
|
#define VMFPLC2LOAD 1
|
|
#define VMFPLC2SCAN 2
|
|
char verb;
|
|
#define OPT_FLAG_EDF 0x01
|
|
unsigned int flags;
|
|
HETB *hetfd;
|
|
};
|
|
|
|
/* Utility function to perform ascii to ebcdic translation on a string */
|
|
void host_to_guest_str(unsigned char *d,char *s)
|
|
{
|
|
int i;
|
|
|
|
for(i=0;s[i];i++)
|
|
{
|
|
d[i]=host_to_guest(s[i]);
|
|
}
|
|
}
|
|
|
|
/* Generic 'usage' routine in case of incorrect */
|
|
/* invocation. */
|
|
int usage(char *cmd)
|
|
{
|
|
char *bcmd;
|
|
|
|
bcmd=basename(cmd);
|
|
fprintf(stderr,"Usage :\n");
|
|
fprintf(stderr,"\t%s DUMP filelist tapefile\n",bcmd);
|
|
fprintf(stderr,"\t%s LOAD tapefile\n",bcmd);
|
|
fprintf(stderr,"\t%s SCAN tapefile\n",bcmd);
|
|
return 0;
|
|
}
|
|
|
|
/* Parse the command line */
|
|
int parse_parms(int ac, char **av, struct options *opts)
|
|
{
|
|
opts->cmd=basename(av[0]);
|
|
if(ac<2)
|
|
{
|
|
usage(av[0]);
|
|
return 1;
|
|
}
|
|
do
|
|
{
|
|
if(strcasecmp(av[1],"DUMP")==0)
|
|
{
|
|
opts->verb=VMFPLC2DUMP;
|
|
break;
|
|
}
|
|
if(strcasecmp(av[1],"SCAN")==0)
|
|
{
|
|
opts->verb=VMFPLC2SCAN;
|
|
break;
|
|
}
|
|
if(strcasecmp(av[1],"LOAD")==0)
|
|
{
|
|
opts->verb=VMFPLC2LOAD;
|
|
break;
|
|
}
|
|
fprintf(stderr,"Invalid function %s\n",av[1]);
|
|
usage(av[0]);
|
|
return 1;
|
|
} while(0);
|
|
switch(opts->verb)
|
|
{
|
|
case VMFPLC2SCAN:
|
|
case VMFPLC2LOAD:
|
|
if(ac<3)
|
|
{
|
|
fprintf(stderr,"Tape file not specified\n");
|
|
usage(av[0]);
|
|
return 1;
|
|
}
|
|
opts->tapefile=av[2];
|
|
break;
|
|
case VMFPLC2DUMP:
|
|
if(ac<3)
|
|
{
|
|
fprintf(stderr,"file list not specified\n");
|
|
usage(av[0]);
|
|
return 1;
|
|
}
|
|
if(ac<4)
|
|
{
|
|
fprintf(stderr,"Tape file not specified\n");
|
|
usage(av[0]);
|
|
return 1;
|
|
}
|
|
opts->procfile=av[2];
|
|
opts->tapefile=av[3];
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Valid character for CMS file names and file types */
|
|
static char *validchars="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#@+-:_";
|
|
/* Valid CMS file mode numbers */
|
|
static char *validfmnum="0123456";
|
|
|
|
|
|
/* This function validates a CMS File Name or File Type */
|
|
int validate_fnft(char *s)
|
|
{
|
|
int i,j,found;
|
|
|
|
for(i=0;s[i];i++)
|
|
{
|
|
for(found=0,j=0;validchars[j];j++)
|
|
{
|
|
if(s[i]==validchars[j])
|
|
{
|
|
found=1;
|
|
break;
|
|
}
|
|
}
|
|
if(!found) return i+1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Validate a CMS FN FT FM construct */
|
|
char *validate_cmsfile(char *fn,char *ft,char *fm)
|
|
{
|
|
int i,found;
|
|
static char msg[1024];
|
|
|
|
if(strlen(fn)>8)
|
|
{
|
|
return "CMS File name too long";
|
|
}
|
|
if(strlen(ft)>8)
|
|
{
|
|
return "CMS File type too long";
|
|
}
|
|
if(strlen(fm)>2)
|
|
{
|
|
return "CMS File mode too long";
|
|
}
|
|
if((i=validate_fnft(fn))!=0)
|
|
{
|
|
sprintf(msg,"Invalid character in CMS file name at position %d",i);
|
|
return msg;
|
|
}
|
|
if((i=validate_fnft(ft))!=0)
|
|
{
|
|
sprintf(msg,"Invalid character in CMS file type at position %d",i);
|
|
return msg;
|
|
}
|
|
if(!isalpha(fm[0]))
|
|
{
|
|
return "CMS File mode must start with a letter";
|
|
}
|
|
if(strlen(fm)>1)
|
|
{
|
|
found=0;
|
|
for(i=0;validfmnum[i];i++)
|
|
{
|
|
if(fm[1]==validfmnum[i])
|
|
{
|
|
found=1;
|
|
break;
|
|
}
|
|
}
|
|
if(!found)
|
|
{
|
|
return "CMS File mode number must be 1-6";
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Utility routine to translate a C string to upper case */
|
|
void str_toupper(char *s)
|
|
{
|
|
int i;
|
|
|
|
for(i=0;s[i];i++)
|
|
{
|
|
if(islower(s[i])) s[i]=toupper(s[i]);
|
|
}
|
|
}
|
|
|
|
/*****************************************************/
|
|
/* Tape management structures and functions */
|
|
/*****************************************************/
|
|
|
|
/* the TAPE_BLOCK structure represents a single tape block */
|
|
struct TAPE_BLOCK
|
|
{
|
|
unsigned char *data;
|
|
size_t sz;
|
|
struct TAPE_BLOCK *next;
|
|
};
|
|
|
|
/* The TAPE_BLOCKS structure represents a collection of tape blocks */
|
|
struct TAPE_BLOCKS
|
|
{
|
|
unsigned char *hdr; /* Header to append to every record */
|
|
size_t hdrsz; /* Size of header */
|
|
size_t blksz; /* Maximum block size */
|
|
size_t blk_modulo; /* Must be a multiple of this */
|
|
struct TAPE_BLOCK *first; /* First block in chain */
|
|
struct TAPE_BLOCK *current; /* Working block */
|
|
};
|
|
|
|
/* Write a single tape mark */
|
|
int write_tape_mark(struct options *opts)
|
|
{
|
|
het_tapemark(opts->hetfd);
|
|
return 0;
|
|
}
|
|
|
|
/* Write a block of data to tape */
|
|
int write_tape_block_data(struct options *opts,unsigned char *bfr,int sz)
|
|
{
|
|
het_write(opts->hetfd,bfr,sz);
|
|
return 0;
|
|
}
|
|
|
|
/* Write a TAPE_BLOCK and adjust the block size to a modulo (without counting the header */
|
|
int write_tape_block(struct options *opts,struct TAPE_BLOCK *tb,size_t mod,size_t hdrsz)
|
|
{
|
|
size_t sz;
|
|
|
|
sz=(tb->sz-hdrsz)/mod;
|
|
if((tb->sz-hdrsz)%mod)
|
|
{
|
|
sz++;
|
|
}
|
|
sz*=mod;
|
|
sz+=hdrsz;
|
|
return write_tape_block_data(opts,tb->data,(int)sz);
|
|
}
|
|
|
|
/* Write a collection of blocks to tape */
|
|
int write_tape_blocks(struct options *opts,struct TAPE_BLOCKS *tbs)
|
|
{
|
|
int rc;
|
|
struct TAPE_BLOCK *tb;
|
|
|
|
tb=tbs->first;
|
|
while(tb)
|
|
{
|
|
rc=write_tape_block(opts,tb,tbs->blk_modulo,tbs->hdrsz);
|
|
if(rc!=0) break;
|
|
tb=tb->next;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Initialize a collection of tape blocks */
|
|
struct TAPE_BLOCKS *init_blocks(size_t blksz,size_t modulo,unsigned char *hdr,int hdrsz)
|
|
{
|
|
struct TAPE_BLOCKS *tbs;
|
|
|
|
tbs=malloc(sizeof(struct TAPE_BLOCKS));
|
|
tbs->first=NULL;
|
|
tbs->current=NULL;
|
|
tbs->hdr=hdr;
|
|
tbs->hdrsz=hdrsz;
|
|
tbs->blksz=blksz;
|
|
tbs->blk_modulo=modulo;
|
|
return tbs;
|
|
}
|
|
|
|
#if 0
|
|
/* DEBUGGING FUNCTION */
|
|
static void hexdump(unsigned char *bfr,int sz)
|
|
{
|
|
int i;
|
|
for(i=0;i<sz;i++)
|
|
{
|
|
if(i%16==0)
|
|
{
|
|
if(i) printf("\n");
|
|
printf("%4.4X :",i);
|
|
}
|
|
if(i%4==0)
|
|
{
|
|
printf(" ");
|
|
}
|
|
printf("%2.2X",bfr[i]);
|
|
}
|
|
if(i%16) printf("\n");
|
|
}
|
|
#endif
|
|
|
|
/* Add a chunk of data to a collection of tape blocks */
|
|
void append_data(struct TAPE_BLOCKS *tbs,unsigned char *bfr,size_t sz)
|
|
{
|
|
int dsz;
|
|
int of;
|
|
struct TAPE_BLOCK *tb;
|
|
|
|
of=0;
|
|
while(sz)
|
|
{
|
|
if(tbs->current==NULL || tbs->current->sz==(tbs->blksz+tbs->hdrsz))
|
|
{
|
|
tb=malloc(sizeof(struct TAPE_BLOCK));
|
|
tb->next=NULL;
|
|
tb->sz=0;
|
|
if(tbs->first==NULL)
|
|
{
|
|
tbs->first=tb;
|
|
tbs->current=tb;
|
|
}
|
|
else
|
|
{
|
|
tbs->current->next=tb;
|
|
tbs->current=tb;
|
|
}
|
|
tb->data=malloc(tbs->blksz+tbs->hdrsz);
|
|
memset(tb->data, 0, tbs->blksz+tbs->hdrsz);
|
|
memcpy(tb->data,tbs->hdr,tbs->hdrsz);
|
|
tb->sz=tbs->hdrsz;
|
|
}
|
|
else
|
|
{
|
|
tb=tbs->current;
|
|
}
|
|
dsz=(int)MIN(sz,(tbs->blksz+tbs->hdrsz)-tb->sz);
|
|
memcpy(&tb->data[tb->sz],&bfr[of],dsz);
|
|
tb->sz+=dsz;
|
|
sz-=dsz;
|
|
of+=dsz;
|
|
}
|
|
}
|
|
|
|
/* Release a collection of tape blocks */
|
|
void free_blocks(struct TAPE_BLOCKS *tbs)
|
|
{
|
|
struct TAPE_BLOCK *tb,*ntb;
|
|
|
|
tb=tbs->first;
|
|
while(tb)
|
|
{
|
|
free(tb->data);
|
|
ntb=tb->next;
|
|
free(tb);
|
|
tb=ntb;
|
|
}
|
|
free(tbs);
|
|
}
|
|
|
|
/*****************************************************/
|
|
/* File records management structures & functions */
|
|
/*****************************************************/
|
|
|
|
/* The RECS strucure represents a file with its */
|
|
/* collection of tape blocks */
|
|
struct RECS
|
|
{
|
|
size_t bc;
|
|
size_t bts;
|
|
struct TAPE_BLOCKS *blocks;
|
|
char recfm;
|
|
int reclen;
|
|
int reccount;
|
|
size_t filesz;
|
|
};
|
|
|
|
|
|
/* Initialize a new RECS structure */
|
|
struct RECS *initrecs(char recfm,int recl,unsigned char *hdr,int hdrsz)
|
|
{
|
|
struct RECS *recs;
|
|
|
|
recs=malloc(sizeof(struct RECS));
|
|
recs->blocks=init_blocks(TAPE_BLOCKSIZE,TAPE_BLKSIZE_MODULO,hdr,hdrsz);
|
|
recs->reccount=0;
|
|
recs->recfm=recfm;
|
|
recs->reclen=recl;
|
|
recs->filesz=0;
|
|
return recs;
|
|
}
|
|
|
|
/* Add 1 (recfm V) or multiple (recfm F) records to a RECS structure */
|
|
/* and to the related tape block collection ù */
|
|
void addrecs(struct RECS *recs,unsigned char *bfr,int sz)
|
|
{
|
|
unsigned char recd[2];
|
|
|
|
recs->filesz+=sz;
|
|
switch(recs->recfm)
|
|
{
|
|
case 'V':
|
|
recd[1]=sz&0xff;
|
|
recd[0]=(sz>>8)&0xff;
|
|
append_data(recs->blocks,recd,2);
|
|
append_data(recs->blocks,bfr,sz);
|
|
recs->reccount++;
|
|
recs->reclen=MAX(recs->reclen,sz);
|
|
break;
|
|
case 'F':
|
|
append_data(recs->blocks,bfr,sz);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Close a RECS structure. The RECS structue is released and the collection */
|
|
/* of tape block is returned for subsequent writing */
|
|
/* For empty files, a dummy record is created since CMS does not support */
|
|
/* empty files. */
|
|
/* The last record of a RECFM F file is also padded if necessary */
|
|
|
|
struct TAPE_BLOCKS *flushrecs( struct RECS *recs,
|
|
int *recl,
|
|
int *recc,
|
|
size_t *filesz,
|
|
unsigned char pad )
|
|
{
|
|
struct TAPE_BLOCKS *blks;
|
|
int residual;
|
|
|
|
if(recs->filesz==0)
|
|
{
|
|
append_data(recs->blocks,&pad,1);
|
|
recs->filesz=1;
|
|
if(recs->recfm=='V')
|
|
{
|
|
recs->reccount=1;
|
|
recs->reclen=1;
|
|
}
|
|
}
|
|
if(recs->recfm=='F')
|
|
{
|
|
residual=recs->reclen-recs->filesz%recs->reclen;
|
|
residual=residual%recs->reclen;
|
|
if(residual)
|
|
{
|
|
unsigned char *padbfr;
|
|
recs->filesz+=residual;
|
|
padbfr=malloc(residual);
|
|
memset(padbfr,pad,residual);
|
|
append_data(recs->blocks,padbfr,residual);
|
|
free(padbfr);
|
|
|
|
}
|
|
recs->reccount=(int)recs->filesz/recs->reclen;
|
|
}
|
|
*recc=recs->reccount;
|
|
*recl=recs->reclen;
|
|
*filesz=recs->filesz;
|
|
blks=recs->blocks;
|
|
free(recs);
|
|
return blks;
|
|
}
|
|
|
|
/* PLCD (VMFPLC2 Data block) record header */
|
|
static unsigned char plcd_hdr[5]={0x02,0xd7,0xd3,0xc3,0xc4};
|
|
/* PLCH (VMFPLC2 Header block) record header */
|
|
static unsigned char plch_hdr[5]={0x02,0xd7,0xd3,0xc3,0xc8};
|
|
|
|
/* Load a binary file for DUMP function */
|
|
struct TAPE_BLOCKS *load_binary_file(char *infile,char recfm,int *recl,int *recc,size_t *filesz)
|
|
{
|
|
int rsz;
|
|
FILE *ifile;
|
|
int maxsize;
|
|
struct RECS *recs;
|
|
|
|
if(recfm=='V')
|
|
maxsize=MAX_BLKLEN-1;
|
|
else
|
|
maxsize=MAX_BLKLEN;
|
|
|
|
ifile=fopen(infile,"r");
|
|
if(ifile==NULL)
|
|
{
|
|
perror(infile);
|
|
return NULL;
|
|
}
|
|
|
|
recs=initrecs(recfm,*recl,plcd_hdr,5);
|
|
while((rsz=(int)fread(bfr,1,maxsize,ifile))!=0)
|
|
{
|
|
addrecs(recs,bfr,rsz);
|
|
}
|
|
fclose(ifile);
|
|
return flushrecs(recs,recl,recc,filesz,0x40);
|
|
}
|
|
|
|
/* Load a textual file for DUMP function */
|
|
struct TAPE_BLOCKS *load_text_file(char *infile,char recfm,int *recl,int *recc,size_t *filesz)
|
|
{
|
|
int rsz;
|
|
FILE *ifile;
|
|
struct RECS *recs;
|
|
char *rec;
|
|
|
|
ifile=fopen(infile,"r");
|
|
if(ifile==NULL)
|
|
{
|
|
perror(infile);
|
|
return NULL;
|
|
}
|
|
|
|
recs=initrecs(recfm,*recl,plcd_hdr,5);
|
|
while((rec=fgets((char*)bfr,sizeof(bfr),ifile))!=NULL)
|
|
{
|
|
rsz=(int)strlen(rec)-1;
|
|
if(recfm=='F')
|
|
{
|
|
if(rsz<(*recl))
|
|
{
|
|
memset(&rec[rsz],' ',(*recl)-rsz);
|
|
}
|
|
rsz=(*recl);
|
|
}
|
|
rec[rsz]=0;
|
|
host_to_guest_str(bfr2,rec);
|
|
addrecs(recs,bfr2,rsz);
|
|
}
|
|
fclose(ifile);
|
|
return flushrecs(recs,recl,recc,filesz,0x40);
|
|
}
|
|
|
|
/* Load a structured file for DUMP function */
|
|
struct TAPE_BLOCKS *load_structured_file(char *infile,char recfm,int *recl,int *recc,size_t *filesz)
|
|
{
|
|
int rsz,rc;
|
|
FILE *ifile;
|
|
uint16_t rlbfr;
|
|
struct RECS *recs;
|
|
|
|
ifile=fopen(infile,"r");
|
|
if(ifile==NULL)
|
|
{
|
|
perror(infile);
|
|
return NULL;
|
|
}
|
|
if(recfm!='V')
|
|
{
|
|
fprintf(stderr,"Structured input files for output to RECFM F files not supported (yet)\n");
|
|
return NULL; /* Structured files only for RECFM V for the time being */
|
|
}
|
|
recs=initrecs(recfm,*recl,plcd_hdr,5);
|
|
while(((int)fread(&rlbfr,1,2,ifile))==2)
|
|
{
|
|
rsz=bswap_16(rlbfr);
|
|
rc=(int)fread(bfr,1,rsz,ifile);
|
|
if(rc!=rsz)
|
|
{
|
|
fprintf(stderr,"Expected %d bytes from file %s, but only %d file read\n",rsz,infile,rc);
|
|
return NULL;
|
|
}
|
|
addrecs(recs,bfr,rsz);
|
|
}
|
|
fclose(ifile);
|
|
return flushrecs(recs,recl,recc,filesz,0x40);
|
|
}
|
|
|
|
/* Load file for DUMP function */
|
|
struct TAPE_BLOCKS *load_file(char *infile,char *filefmt,char recfm,int *recl,int *recc,size_t *filesz)
|
|
{
|
|
struct TAPE_BLOCKS *blks;
|
|
switch(filefmt[0])
|
|
{
|
|
case 'B':
|
|
blks=load_binary_file(infile,recfm,recl,recc,filesz);
|
|
break;
|
|
case 'T':
|
|
blks=load_text_file(infile,recfm,recl,recc,filesz);
|
|
break;
|
|
case 'S':
|
|
blks=load_structured_file(infile,recfm,recl,recc,filesz);
|
|
break;
|
|
default:
|
|
fprintf(stderr,"*** INTERNAL ERROR 0001\n");
|
|
return NULL;
|
|
}
|
|
return blks;
|
|
}
|
|
|
|
|
|
/* Convert a value to 16 bit Big Endian format */
|
|
void big_endian_16(unsigned char *d,int s)
|
|
{
|
|
d[0]=(s>>8) & 0xff;
|
|
d[1]=s&0xff;
|
|
}
|
|
|
|
/* Convert a value to 32 bit Big Endian format */
|
|
void big_endian_32(unsigned char *d,int s)
|
|
{
|
|
d[0]=(s>>24) & 0xff;
|
|
d[1]=(s>>16) & 0xff;
|
|
d[2]=(s>>8) & 0xff;
|
|
d[3]=s&0xff;
|
|
}
|
|
|
|
/* Convert a single byte decimal value to DCB (Decimal Coded Binary) format */
|
|
unsigned char to_dcb(int v)
|
|
{
|
|
return((v/10)*16+v%10);
|
|
}
|
|
|
|
/* Create the VMFPLC2 Header record */
|
|
struct FST_BLOCK *format_fst(char *fn,char *ft,char *fm,char recfm,int lrecl,int reccount,int filesz,time_t dt)
|
|
{
|
|
int numfull;
|
|
int lastsz;
|
|
char bfr[3];
|
|
struct tm *ttm;
|
|
struct FST_BLOCK *fstb;
|
|
|
|
fstb=malloc(sizeof(struct FST_BLOCK));
|
|
memset(fstb, 0,sizeof(struct FST_BLOCK));
|
|
memcpy(fstb->hdr,plch_hdr,5);
|
|
|
|
memset(fstb->fst.fn,0x40,8);
|
|
memset(fstb->fst.ft,0x40,8);
|
|
memset(fstb->fst.fm,0x40,2);
|
|
|
|
host_to_guest_str(fstb->fst.fn,fn);
|
|
host_to_guest_str(fstb->fst.ft,ft);
|
|
host_to_guest_str(fstb->fst.fm,fm);
|
|
|
|
ttm=localtime(&dt);
|
|
fstb->fst.dt[0]=to_dcb(ttm->tm_mon);
|
|
fstb->fst.dt[1]=to_dcb(ttm->tm_mday);
|
|
fstb->fst.dt[2]=to_dcb(ttm->tm_hour);
|
|
fstb->fst.dt[3]=to_dcb(ttm->tm_min);
|
|
snprintf(bfr,3,"%2.2u",ttm->tm_year%100);
|
|
bfr[2]=0;
|
|
host_to_guest_str(fstb->fst.year,bfr);
|
|
|
|
fstb->fst.adt[0]=to_dcb(ttm->tm_year%100);
|
|
fstb->fst.adt[1]=to_dcb(ttm->tm_mon);
|
|
fstb->fst.adt[2]=to_dcb(ttm->tm_mday);
|
|
fstb->fst.adt[3]=to_dcb(ttm->tm_hour);
|
|
fstb->fst.adt[4]=to_dcb(ttm->tm_min);
|
|
fstb->fst.adt[5]=to_dcb(ttm->tm_sec);
|
|
|
|
fstb->fst.recfm=host_to_guest(recfm);
|
|
|
|
big_endian_32(fstb->fst.lrecl,lrecl);
|
|
big_endian_16(fstb->fst.reccount,reccount);
|
|
big_endian_16(fstb->fst.wp,reccount+1);
|
|
big_endian_16(fstb->fst.rp,1);
|
|
big_endian_32(fstb->fst.aic,reccount);
|
|
|
|
if(recfm=='V')
|
|
{
|
|
filesz+=reccount*2;
|
|
}
|
|
numfull=filesz/TAPE_BLOCKSIZE;
|
|
lastsz=filesz%TAPE_BLOCKSIZE;
|
|
if(lastsz%TAPE_BLKSIZE_MODULO==0)
|
|
{
|
|
lastsz/=TAPE_BLKSIZE_MODULO;
|
|
}
|
|
else
|
|
{
|
|
lastsz=lastsz/TAPE_BLKSIZE_MODULO+1;
|
|
}
|
|
|
|
big_endian_32(fstb->fst.numblk,numfull);
|
|
big_endian_32(fstb->fst.lastsz,lastsz);
|
|
|
|
|
|
return fstb;
|
|
}
|
|
|
|
/* Process a control file entry */
|
|
int process_entry(struct options *opts,char *orec,int recno)
|
|
{
|
|
int ignore;
|
|
int badentry;
|
|
char *fn,*ft,*fm;
|
|
char *recfm,*lrecl;
|
|
char *filefmt;
|
|
char *infile;
|
|
int reclen;
|
|
char *msg;
|
|
char *rec;
|
|
char *endptr;
|
|
int reccount;
|
|
size_t filesz;
|
|
struct TAPE_BLOCKS *blks;
|
|
struct FST_BLOCK *fstb;
|
|
struct stat stt;
|
|
char *strtok_str = NULL;
|
|
|
|
ignore=0;
|
|
rec=strdup(orec);
|
|
do
|
|
{
|
|
badentry=1;
|
|
fn=strtok_r(rec," ",&strtok_str);
|
|
if(fn==NULL)
|
|
{
|
|
/* blank line : ignore */
|
|
ignore=1;
|
|
break;
|
|
}
|
|
if(strcmp(fn,"*")==0)
|
|
{
|
|
/* comment : ignore */
|
|
ignore=1;
|
|
break;
|
|
}
|
|
if(fn[0]=='@')
|
|
{
|
|
if(strcasecmp(fn,"@TM")==0)
|
|
{
|
|
write_tape_mark(opts);
|
|
ignore=1;
|
|
break;
|
|
}
|
|
}
|
|
ft=strtok_r(NULL," ",&strtok_str);
|
|
if(ft==NULL)
|
|
{
|
|
msg="File type missing";
|
|
break;
|
|
}
|
|
fm=strtok_r(NULL," ",&strtok_str);
|
|
if(fm==NULL)
|
|
{
|
|
msg="File mode missing";
|
|
break;
|
|
}
|
|
str_toupper(fn);
|
|
str_toupper(ft);
|
|
str_toupper(fm);
|
|
msg=validate_cmsfile(fn,ft,fm);
|
|
if(msg!=NULL) break;
|
|
recfm=strtok_r(NULL," ",&strtok_str);
|
|
if(recfm==NULL)
|
|
{
|
|
msg="Record format missing";
|
|
break;
|
|
}
|
|
str_toupper(recfm);
|
|
if(strlen(recfm)>1 || ( recfm[0]!='F' && recfm[0]!='V'))
|
|
{
|
|
msg="Record format must be F or V";
|
|
break;
|
|
}
|
|
if(recfm[0]=='F')
|
|
{
|
|
lrecl=strtok_r(NULL," ",&strtok_str);
|
|
if(lrecl==NULL)
|
|
{
|
|
msg="Logical Record Length missing for F record format file";
|
|
break;
|
|
}
|
|
errno=0;
|
|
reclen=strtoul(lrecl,&endptr,0);
|
|
if(errno || endptr[0]!=0)
|
|
{
|
|
msg="Logical Record Length must be a numeric value";
|
|
break;
|
|
}
|
|
if(reclen<1 || reclen>=MAX_BLKLEN)
|
|
{
|
|
msg="Logical Record Length must be between 1 and 65535";
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
reclen=0;
|
|
}
|
|
filefmt=strtok_r(NULL," ",&strtok_str);
|
|
if(filefmt==NULL)
|
|
{
|
|
msg="File format missing";
|
|
break;
|
|
}
|
|
str_toupper(filefmt);
|
|
if(strlen(filefmt)>1 || ( filefmt[0]!='B' && filefmt[0]!='S' && filefmt[0]!='T'))
|
|
{
|
|
msg="File format must be B (Binary) S (Structured) or T (Text)";
|
|
break;
|
|
}
|
|
infile=strtok_r(NULL,"",&strtok_str); /* allow spaces here */
|
|
if(infile==NULL)
|
|
{
|
|
msg="Input file name missing";
|
|
break;
|
|
}
|
|
badentry=0;
|
|
} while(0);
|
|
if(ignore)
|
|
{
|
|
free(rec);
|
|
return 0;
|
|
}
|
|
if(badentry)
|
|
{
|
|
fprintf(stderr,">>> %s\n",orec);
|
|
fprintf(stderr,"*** Bad entry at line %d in file \"%s\"\n",recno,opts->procfile);
|
|
fprintf(stderr,"*** Cause : %s\n",msg);
|
|
free(rec);
|
|
return 1;
|
|
}
|
|
printf(" %-8s %-8s %-2s\n",fn,ft,fm);
|
|
blks=load_file(infile,filefmt,recfm[0],&reclen,&reccount,&filesz);
|
|
if(blks==NULL)
|
|
{
|
|
free(rec);
|
|
return 1;
|
|
}
|
|
stat(infile,&stt);
|
|
fstb=format_fst(fn,ft,fm,recfm[0],reclen,reccount,(int)filesz,stt.st_mtime);
|
|
if(fstb==NULL)
|
|
{
|
|
free(rec);
|
|
free_blocks(blks);
|
|
return 1;
|
|
}
|
|
(void)write_tape_block_data(opts,(unsigned char *)fstb,sizeof(struct FST_BLOCK));
|
|
(void)write_tape_blocks(opts,blks);
|
|
|
|
|
|
free(rec);
|
|
free(fstb);
|
|
free_blocks(blks);
|
|
return 0;
|
|
}
|
|
|
|
/* Process the DUMP control file */
|
|
int process_procfile(struct options *opts)
|
|
{
|
|
FILE *pfile;
|
|
char *rec;
|
|
int recno=0;
|
|
int errcount=0;
|
|
|
|
pfile=fopen(opts->procfile,"r");
|
|
if(pfile==NULL)
|
|
{
|
|
perror(opts->procfile);
|
|
return 1;
|
|
}
|
|
while((rec=fgets((char*)bfr,sizeof(bfr),pfile))!=NULL)
|
|
{
|
|
recno++;
|
|
rec[strlen(rec)-1]=0;
|
|
if ( process_entry(opts,rec,recno) != 0 )
|
|
{
|
|
fprintf(stderr,"*** Entry ignored\n\n");
|
|
errcount++;
|
|
}
|
|
}
|
|
fclose(pfile);
|
|
if(errcount>0)
|
|
{
|
|
fprintf(stderr,"%d errors encountered\n",errcount);
|
|
}
|
|
write_tape_mark(opts);
|
|
write_tape_mark(opts);
|
|
return errcount?1:0;
|
|
}
|
|
|
|
/* Open the tape file for output */
|
|
int open_tapefile(struct options *opts,int w)
|
|
{
|
|
if(w)
|
|
{
|
|
het_open(&opts->hetfd,opts->tapefile,HETOPEN_CREATE);
|
|
het_cntl(opts->hetfd,HETCNTL_SET|HETCNTL_COMPRESS,1);
|
|
}
|
|
else
|
|
{
|
|
het_open(&opts->hetfd,opts->tapefile,HETOPEN_READONLY);
|
|
het_cntl(opts->hetfd,HETCNTL_SET|HETCNTL_DECOMPRESS,1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Perform vmfplc2 DUMP operation */
|
|
int dodump(struct options *opts)
|
|
{
|
|
printf(" DUMPING.....\n");
|
|
open_tapefile(opts,1);
|
|
return process_procfile(opts);
|
|
}
|
|
|
|
/* Perform vmfplc2 LOAD operation */
|
|
int doload(struct options *opts)
|
|
{
|
|
UNREFERENCED(opts);
|
|
printf("LOAD function not implemented yet\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Perform vmfplc2 SCAN operation */
|
|
int doscan(struct options *opts)
|
|
{
|
|
UNREFERENCED(opts);
|
|
printf("SCAN function not implemented yet\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Main routine */
|
|
int main(int argc,char **argv)
|
|
{
|
|
int rc;
|
|
struct options opts;
|
|
char *pgmname; /* prog name in host format */
|
|
char *pgm; /* less any extension (.ext) */
|
|
char msgbuf[512]; /* message build work area */
|
|
char *strtok_str = NULL;
|
|
|
|
/* Set program name */
|
|
if ( argc > 0 )
|
|
{
|
|
if ( strlen(argv[0]) == 0 )
|
|
{
|
|
pgmname = strdup( UTILITY_NAME );
|
|
}
|
|
else
|
|
{
|
|
char path[MAX_PATH];
|
|
#if defined( _MSVC_ )
|
|
GetModuleFileName( NULL, path, MAX_PATH );
|
|
#else
|
|
strncpy( path, argv[0], sizeof( path ) );
|
|
#endif
|
|
pgmname = strdup(basename(path));
|
|
#if !defined( _MSVC_ )
|
|
strncpy( path, argv[0], sizeof(path) );
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pgmname = strdup( UTILITY_NAME );
|
|
}
|
|
|
|
pgm = strtok_r( strdup(pgmname), ".", &strtok_str);
|
|
INITIALIZE_UTILITY( pgmname );
|
|
|
|
/* Display the program identification message */
|
|
MSGBUF( msgbuf, MSG_C( HHC02499, "I", pgm, "VM/CMS VMFPLC2 Utility" ) );
|
|
display_version (stderr, msgbuf+10, FALSE);
|
|
|
|
|
|
if(parse_parms(argc,argv,&opts)!=0)
|
|
{
|
|
return 1;
|
|
}
|
|
switch(opts.verb)
|
|
{
|
|
case VMFPLC2DUMP:
|
|
rc=dodump(&opts);
|
|
break;
|
|
case VMFPLC2LOAD:
|
|
rc=doload(&opts);
|
|
break;
|
|
case VMFPLC2SCAN:
|
|
rc=doscan(&opts);
|
|
break;
|
|
default:
|
|
rc=0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|