Files
org-hyperion-cules/hetget.c
Jan Jaeger 69ca87cc62 Rollback a number of changes :-(
git-svn-id: file:///home/jj/hercules.svn/trunk@1403 956126f8-22a0-4046-8f4a-272fa8102e63
2003-06-02 13:16:07 +00:00

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;
}