mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-14 16:10:20 +02:00
819 lines
17 KiB
C
819 lines
17 KiB
C
/*
|
|
|| ----------------------------------------------------------------------------
|
|
||
|
|
|| HETGET.C (c) Copyright Leland Lucius, 2000-2003
|
|
|| Released under terms of the Q Public License.
|
|
||
|
|
|| Extract files from an HET file
|
|
||
|
|
|| ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "hetlib.h"
|
|
#include "sllib.h"
|
|
#include "hercules.h"
|
|
|
|
/*
|
|
|| Local volatile data
|
|
*/
|
|
#define O_NL 0x80
|
|
#define O_ASCII 0x40
|
|
#define O_STRIP 0x20
|
|
#define O_UNBLOCK 0x10
|
|
struct
|
|
{
|
|
char *ifile;
|
|
char *ofile;
|
|
int fileno;
|
|
int lrecl;
|
|
int blksize;
|
|
unsigned char flags;
|
|
unsigned char recfm;
|
|
}
|
|
opts =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
/*
|
|
|| Local constant data
|
|
*/
|
|
static const char help[] =
|
|
"%s - Extract files from an HET file\n\n"
|
|
"Usage: %s [options] hetfile outfile fileno [recfm lrecl blksize]\n\n"
|
|
"Options:\n"
|
|
" -a convert to ASCII (implies -u)\n"
|
|
" -h display usage summary\n"
|
|
" -n file is an NL (or BLP like) tape\n"
|
|
" -u unblock (removes BDWs and RDWs if RECFM=V)\n"
|
|
" -s strip trailing blanks (requires -a)\n";
|
|
|
|
/*
|
|
|| Valid record formats
|
|
*/
|
|
#define O_UNDEFINED 0x80
|
|
#define O_FIXED 0x40
|
|
#define O_VARIABLE 0x20
|
|
#define O_BLOCKED 0x08
|
|
#define O_SPANNED 0x04
|
|
|
|
static const struct
|
|
{
|
|
char *recfm;
|
|
int fmt;
|
|
}
|
|
valfm[] =
|
|
{
|
|
{ "U", O_UNDEFINED | 0 | 0 },
|
|
{ "UA", O_UNDEFINED | 0 | 0 },
|
|
{ "UM", O_UNDEFINED | 0 | 0 },
|
|
{ "F", O_FIXED | 0 | 0 },
|
|
{ "FA", O_FIXED | 0 | 0 },
|
|
{ "FM", O_FIXED | 0 | 0 },
|
|
{ "FB", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FBA", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FBM", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FS", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FSA", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FSM", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FBS", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FBSA", O_FIXED | O_BLOCKED | 0 },
|
|
{ "FBSM", O_FIXED | O_BLOCKED | 0 },
|
|
{ "V", O_VARIABLE | 0 | 0 },
|
|
{ "VA", O_VARIABLE | 0 | 0 },
|
|
{ "VM", O_VARIABLE | 0 | 0 },
|
|
{ "VB", O_VARIABLE | O_BLOCKED | 0 },
|
|
{ "VBA", O_VARIABLE | O_BLOCKED | 0 },
|
|
{ "VBM", O_VARIABLE | O_BLOCKED | 0 },
|
|
{ "VS", O_VARIABLE | 0 | O_SPANNED },
|
|
{ "VSA", O_VARIABLE | 0 | O_SPANNED },
|
|
{ "VSM", O_VARIABLE | 0 | O_SPANNED },
|
|
{ "VBS", O_VARIABLE | O_BLOCKED | O_SPANNED },
|
|
{ "VBSA", O_VARIABLE | O_BLOCKED | O_SPANNED },
|
|
{ "VBSM", O_VARIABLE | O_BLOCKED | O_SPANNED },
|
|
};
|
|
#define VALFMCNT ( sizeof( valfm ) / sizeof( valfm[ 0 ] ) )
|
|
|
|
/*
|
|
|| Block and record management
|
|
*/
|
|
unsigned char *blkptr = NULL;
|
|
int blkidx = 0;
|
|
int blklen = 0;
|
|
unsigned char *recptr = NULL;
|
|
int recidx = 0;
|
|
int reclen = 0;
|
|
|
|
#ifdef EXTERNALGUI
|
|
/*
|
|
|| Special flag to indicate whether or not we're being
|
|
|| run under the control of the external GUI facility.
|
|
*/
|
|
int extgui = 0;
|
|
/*
|
|
|| Previously reported file position
|
|
*/
|
|
static long prevpos = 0;
|
|
/*
|
|
|| Report progress every this many bytes
|
|
*/
|
|
#define PROGRESS_MASK (~0x3FFFF /* 256K */)
|
|
#endif /*EXTERNALGUI*/
|
|
|
|
/*
|
|
|| Merge DCB information from HDR2 label
|
|
*/
|
|
void
|
|
merge( SLLABEL *lab )
|
|
{
|
|
SLFMT fmt;
|
|
int i;
|
|
|
|
/*
|
|
|| Make the label more managable
|
|
*/
|
|
sl_fmtlab( &fmt, lab );
|
|
|
|
/*
|
|
|| Merge the record format;
|
|
*/
|
|
if( opts.recfm == 0 )
|
|
{
|
|
opts.recfm = O_UNDEFINED;
|
|
for( i = 0 ; i < (int)VALFMCNT ; i++ )
|
|
{
|
|
if( strcasecmp( fmt.slds2.recfm, valfm[ i ].recfm ) == 0 )
|
|
{
|
|
opts.recfm = valfm[ i ].fmt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Merge in the record length
|
|
*/
|
|
if( opts.lrecl == 0 )
|
|
{
|
|
opts.lrecl = atoi( fmt.slds2.lrecl );
|
|
}
|
|
|
|
/*
|
|
|| Merge in the block size
|
|
*/
|
|
if( opts.blksize == 0 )
|
|
{
|
|
/*
|
|
|| Try the blksize field first
|
|
*/
|
|
opts.blksize = atoi( fmt.slds2.blksize );
|
|
if( opts.blksize == 0 )
|
|
{
|
|
/*
|
|
|| Still zero, so try the lblkln field
|
|
*/
|
|
opts.blksize = atoi( fmt.slds2.lblkln );
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Locate final RECFM string
|
|
*/
|
|
for( i = 0 ; i < (int)VALFMCNT ; i++ )
|
|
{
|
|
if( strcasecmp( fmt.slds2.recfm, valfm[ i ].recfm ) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Print DCB attributes
|
|
*/
|
|
printf( "DCB Attributes used:\n" );
|
|
printf( " RECFM=%-4.4s LRECL=%-5.5d BLKSIZE=%d\n",
|
|
valfm[ i ].recfm,
|
|
opts.lrecl,
|
|
opts.blksize );
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
|| Return block length from BDW
|
|
*/
|
|
int
|
|
bdw_length( const unsigned char *ptr )
|
|
{
|
|
unsigned int len;
|
|
|
|
/*
|
|
|| Extended format BDW?
|
|
*/
|
|
if( ptr[ 0 ] & 0x80 )
|
|
{
|
|
/*
|
|
|| Length is 31 bits
|
|
*/
|
|
len = ptr[ 0 ] << 24;
|
|
len += ptr[ 1 ] << 16;
|
|
len += ptr[ 2 ] << 8;
|
|
len += ptr[ 3 ];
|
|
len &= 0x7fffffff;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
|| Length is 15 bits
|
|
*/
|
|
len = ptr[ 0 ] << 8;
|
|
len += ptr[ 1 ];
|
|
}
|
|
|
|
return( len );
|
|
}
|
|
|
|
/*
|
|
|| Return record length from RDW
|
|
*/
|
|
int
|
|
rdw_length( const unsigned char *ptr )
|
|
{
|
|
unsigned int len;
|
|
|
|
/*
|
|
|| Get the record length
|
|
*/
|
|
len = ptr[ 0 ] << 8;
|
|
len += ptr[ 1 ];
|
|
|
|
return( len );
|
|
}
|
|
|
|
/*
|
|
|| Retrieves a block from the tape file and resets variables
|
|
*/
|
|
int
|
|
getblock( HETB *hetb )
|
|
{
|
|
int rc;
|
|
|
|
/*
|
|
|| Read a block from the tape
|
|
*/
|
|
rc = het_read( hetb, blkptr );
|
|
if( rc < 0 )
|
|
{
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
|| Save the block length (should we use BDW for RECFM=V files???)
|
|
*/
|
|
blklen = rc;
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
|| Retrieve logical records from the tape - doesn't handle SPANNED records
|
|
*/
|
|
int
|
|
getrecord( HETB *hetb )
|
|
{
|
|
int rc;
|
|
|
|
/*
|
|
|| Won't be null if we've been here before
|
|
*/
|
|
if( recptr != NULL )
|
|
{
|
|
recidx += reclen;
|
|
}
|
|
|
|
/*
|
|
|| Need a new block first time through or we've exhausted current block
|
|
*/
|
|
if( ( recptr == NULL ) || ( recidx >= blklen ) )
|
|
{
|
|
/*
|
|
|| Go get another block
|
|
*/
|
|
rc = getblock( hetb );
|
|
if( rc < 0 )
|
|
{
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
|| For RECFM=V, bump index past BDW
|
|
*/
|
|
recidx = 0;
|
|
if( opts.recfm & O_VARIABLE )
|
|
{
|
|
recidx = 4;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Set the new record pointer
|
|
*/
|
|
recptr = &blkptr[ recidx ];
|
|
|
|
/*
|
|
|| Set the record length depending on record type
|
|
*/
|
|
if( opts.recfm & O_FIXED )
|
|
{
|
|
reclen = opts.lrecl;
|
|
}
|
|
else if( opts.recfm & O_VARIABLE )
|
|
{
|
|
reclen = rdw_length( recptr );
|
|
}
|
|
else
|
|
{
|
|
reclen = blklen;
|
|
}
|
|
|
|
return( reclen );
|
|
}
|
|
|
|
/*
|
|
|| Retrieve and validate a standard label
|
|
*/
|
|
int
|
|
get_sl( HETB *hetb, SLLABEL *lab )
|
|
{
|
|
int rc;
|
|
|
|
/*
|
|
|| Read a block
|
|
*/
|
|
rc = het_read( hetb, blkptr );
|
|
if( rc >= 0 )
|
|
{
|
|
/*
|
|
|| Does is look like a standard label?
|
|
*/
|
|
if( sl_islabel( lab, blkptr, rc ) == TRUE )
|
|
{
|
|
return( 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf( "%s while reading block\n", het_error( rc ) );
|
|
}
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
|| Extract the file from the tape
|
|
*/
|
|
int
|
|
getfile( HETB *hetb, FILE *outf )
|
|
{
|
|
SLFMT fmt;
|
|
SLLABEL lab;
|
|
unsigned char *ptr;
|
|
int fileno;
|
|
int rc;
|
|
|
|
/*
|
|
|| Skip to the desired file
|
|
*/
|
|
if( opts.flags & O_NL )
|
|
{
|
|
/*
|
|
|| For NL tapes, just use the specified file number
|
|
*/
|
|
fileno = opts.fileno;
|
|
|
|
/*
|
|
|| Start skipping
|
|
*/
|
|
while( --fileno )
|
|
{
|
|
/*
|
|
|| Forward space to beginning of next file
|
|
*/
|
|
rc = het_fsf( hetb );
|
|
if( rc < 0 )
|
|
{
|
|
printf( "%s while positioning to file #%d\n",
|
|
het_error( rc ),
|
|
opts.fileno );
|
|
return( rc );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
|| First block should be a VOL1 record
|
|
*/
|
|
rc = get_sl( hetb, &lab );
|
|
if( rc < 0 || !sl_isvol( &lab, 1 ) )
|
|
{
|
|
printf( "Expected VOL1 label\n" );
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
|| For SL, adjust the file # so we end up on the label before the data
|
|
*/
|
|
fileno = ( opts.fileno * 3 ) - 2;
|
|
|
|
/*
|
|
|| Start skipping
|
|
*/
|
|
while( --fileno )
|
|
{
|
|
/*
|
|
|| Forward space to beginning of next file
|
|
*/
|
|
rc = het_fsf( hetb );
|
|
if( rc < 0 )
|
|
{
|
|
printf( "%s while positioning to file #%d\n",
|
|
het_error( rc ),
|
|
opts.fileno );
|
|
return( rc );
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Get the HDR1 label.
|
|
*/
|
|
rc = get_sl( hetb, &lab );
|
|
if( rc < 0 || !sl_ishdr( &lab, 1 ) )
|
|
{
|
|
printf( "Expected HDR1 label\n" );
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
|| Make the label more managable
|
|
*/
|
|
sl_fmtlab( &fmt, &lab );
|
|
printf("File Info:\n DSN=%-17.17s\n", fmt.slds1.dsid );
|
|
|
|
/*
|
|
|| Get the HDR2 label.
|
|
*/
|
|
rc = get_sl( hetb, &lab );
|
|
if( rc < 0 || !sl_ishdr( &lab, 2 ) )
|
|
{
|
|
printf( "Expected HDR2 label\n" );
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
|| Merge the DCB information
|
|
*/
|
|
merge( &lab );
|
|
|
|
/*
|
|
|| Hop over the tapemark
|
|
*/
|
|
rc = het_fsf( hetb );
|
|
if( rc < 0 )
|
|
{
|
|
printf( "%s while spacing to start of data\n",
|
|
het_error( rc ) );
|
|
return( rc );
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Different processing when converting to ASCII
|
|
*/
|
|
if( opts.flags & ( O_ASCII | O_UNBLOCK ) )
|
|
{
|
|
/*
|
|
|| Get a record
|
|
*/
|
|
while( ( rc = getrecord( hetb ) ) >= 0 )
|
|
{
|
|
#ifdef EXTERNALGUI
|
|
if( extgui )
|
|
{
|
|
/* Report progress every nnnK */
|
|
long curpos = ftell( hetb->fd );
|
|
if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) )
|
|
{
|
|
prevpos = curpos;
|
|
fprintf( stderr, "IPOS=%ld\n", curpos );
|
|
}
|
|
}
|
|
#endif /*EXTERNALGUI*/
|
|
/*
|
|
|| Get working copy of record ptr
|
|
*/
|
|
ptr = recptr;
|
|
|
|
/*
|
|
|| Only want data portion for RECFM=V records
|
|
*/
|
|
if( opts.recfm & O_VARIABLE )
|
|
{
|
|
ptr += 4;
|
|
rc -= 4;
|
|
}
|
|
|
|
/*
|
|
|| Convert record to ASCII
|
|
*/
|
|
if( opts.flags & O_ASCII )
|
|
{
|
|
sl_etoa( NULL, ptr, rc );
|
|
}
|
|
|
|
/*
|
|
|| Strip trailing blanks
|
|
*/
|
|
if( opts.flags & O_STRIP )
|
|
{
|
|
while( ptr[ rc - 1 ] == ' ' )
|
|
{
|
|
rc--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Write the record out
|
|
*/
|
|
fwrite( ptr, rc, 1, outf );
|
|
|
|
/*
|
|
|| Put out a linefeed when converting
|
|
*/
|
|
if( opts.flags & O_ASCII )
|
|
{
|
|
fwrite( "\n", 1, 1, outf );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
|| Get a record
|
|
*/
|
|
while( ( rc = getblock( hetb ) ) >= 0 )
|
|
{
|
|
#ifdef EXTERNALGUI
|
|
if( extgui )
|
|
{
|
|
/* Report progress every nnnK */
|
|
long curpos = ftell( hetb->fd );
|
|
if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) )
|
|
{
|
|
prevpos = curpos;
|
|
fprintf( stderr, "IPOS=%ld\n", curpos );
|
|
}
|
|
}
|
|
#endif /*EXTERNALGUI*/
|
|
/*
|
|
|| Write the record out
|
|
*/
|
|
fwrite( blkptr, blklen, 1, outf );
|
|
}
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/*
|
|
|| Prints usage information
|
|
*/
|
|
void
|
|
usage( char *name )
|
|
{
|
|
printf( help, name, name );
|
|
}
|
|
|
|
/*
|
|
|| Standard main
|
|
*/
|
|
int
|
|
main( int argc, char *argv[] )
|
|
{
|
|
HETB *hetb;
|
|
FILE *outf;
|
|
int rc;
|
|
int i;
|
|
|
|
#ifdef EXTERNALGUI
|
|
if (argc >= 1 && strncmp(argv[argc-1],"EXTERNALGUI",11) == 0)
|
|
{
|
|
extgui = 1;
|
|
argc--;
|
|
}
|
|
#endif /*EXTERNALGUI*/
|
|
|
|
/* Display the program identification message */
|
|
display_version (stderr, "Hercules HET extract files program ");
|
|
|
|
/*
|
|
|| Process option switches
|
|
*/
|
|
while( TRUE )
|
|
{
|
|
rc = getopt( argc, argv, "abhnsu" );
|
|
if( rc == -1 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
switch( rc )
|
|
{
|
|
case 'a':
|
|
opts.flags |= O_ASCII;
|
|
break;
|
|
|
|
case 'h':
|
|
usage( argv[ 0 ] );
|
|
exit( 1 );
|
|
break;
|
|
|
|
case 'n':
|
|
opts.flags |= O_NL;
|
|
break;
|
|
|
|
case 's':
|
|
opts.flags |= O_STRIP;
|
|
break;
|
|
|
|
case 'u':
|
|
opts.flags |= O_UNBLOCK;
|
|
break;
|
|
|
|
default:
|
|
usage( argv[ 0 ] );
|
|
exit( 1 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Calc number of non-switch arguments
|
|
*/
|
|
argc -= optind;
|
|
|
|
/*
|
|
|| We must have at least the first 3 parms
|
|
*/
|
|
if( argc < 3 )
|
|
{
|
|
printf( "Must specify input tape, output file, and file #\n" );
|
|
exit( 1 );
|
|
}
|
|
|
|
opts.ifile = argv[ optind ];
|
|
opts.ofile = argv[ optind + 1 ];
|
|
opts.fileno = atoi( argv[ optind + 2 ] );
|
|
if( opts.fileno == 0 || opts.fileno > 9999 )
|
|
{
|
|
printf( "File number must be within 1-9999\n" );
|
|
exit( 1 );
|
|
}
|
|
|
|
/*
|
|
|| If NL tape, then we require the DCB attributes
|
|
*/
|
|
if( opts.flags & O_NL )
|
|
{
|
|
if( argc != 6 )
|
|
{
|
|
printf( "DCB attributes required for NL tapes\n" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| If specified, get the DCB attributes
|
|
*/
|
|
if( argc > 3 )
|
|
{
|
|
/*
|
|
|| Must have only three
|
|
*/
|
|
if( argc != 6 )
|
|
{
|
|
usage( argv[ 0 ] );
|
|
exit( 1 );
|
|
}
|
|
|
|
/*
|
|
|| Lookup the specified RECFM in our table
|
|
*/
|
|
opts.recfm = 0;
|
|
for( i = 0 ; i < (int)VALFMCNT ; i++ )
|
|
{
|
|
if( strcasecmp( argv[ optind + 3 ], valfm[ i ].recfm ) == 0 )
|
|
{
|
|
opts.recfm = valfm[ i ].fmt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| If we didn't find a match, show the user what the valid ones are
|
|
*/
|
|
if( opts.recfm == 0)
|
|
{
|
|
/*
|
|
|| Dump out the valid RECFMs
|
|
*/
|
|
printf( "Valid record formats are:\n" );
|
|
for( i = 0 ; i < (int)VALFMCNT ; i++ )
|
|
{
|
|
printf( " %-4.4s", valfm[ i ].recfm );
|
|
if( ( ( i + 1 ) % 3 ) == 0 )
|
|
{
|
|
printf( "\n" );
|
|
}
|
|
}
|
|
exit( 1 );
|
|
}
|
|
|
|
/*
|
|
|| Get the record length
|
|
*/
|
|
opts.lrecl = atoi( argv[ optind + 4 ] );
|
|
|
|
/*
|
|
|| Get and validate the blksize
|
|
*/
|
|
opts.blksize = atoi( argv[ optind + 5 ] );
|
|
if( opts.blksize == 0 )
|
|
{
|
|
printf( "Block size can't be zero\n" );
|
|
exit( 1 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
|| Open the tape file
|
|
*/
|
|
rc = het_open( &hetb, opts.ifile, 0 );
|
|
if( rc >= 0 )
|
|
{
|
|
/*
|
|
|| Get memory for the tape buffer
|
|
*/
|
|
blkptr = malloc( HETMAX_BLOCKSIZE );
|
|
if( blkptr != NULL )
|
|
{
|
|
/*
|
|
|| Open the output file
|
|
*/
|
|
outf = fopen( opts.ofile, "wb" );
|
|
if( outf != NULL )
|
|
{
|
|
/*
|
|
|| Go extract the file from the tape
|
|
*/
|
|
rc = getfile( hetb, outf );
|
|
|
|
/*
|
|
|| Close the output file
|
|
*/
|
|
fclose( outf );
|
|
}
|
|
|
|
/*
|
|
|| Free the buffer memory
|
|
*/
|
|
free( blkptr );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf( "het_open() returned %s\n", het_error( rc ) );
|
|
}
|
|
|
|
/*
|
|
|| Close the tape file
|
|
*/
|
|
het_close( &hetb );
|
|
|
|
return 0;
|
|
}
|