/* || ---------------------------------------------------------------------------- || || HETGET.C (c) Copyright Leland Lucius, 2000-2006 || Released under terms of the Q Public License. || || Extract files from an HET file || || ---------------------------------------------------------------------------- */ #include "hstdinc.h" #include "hercules.h" #include "hetlib.h" #include "sllib.h" #include "herc_getopt.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 /* || Previously reported file position */ static OFF_T 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 */ OFF_T curpos = FTELL( hetb->fd ); if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) ) { prevpos = curpos; fprintf( stderr, "IPOS=%" I64_FMT "d\n", (U64)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 */ OFF_T curpos = FTELL( hetb->fd ); if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) ) { prevpos = curpos; fprintf( stderr, "IPOS=%" I64_FMT "d\n", (U64)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--; setvbuf(stderr, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); } #endif /*EXTERNALGUI*/ /* Display the program identification message */ display_version (stderr, "Hercules HET extract files program ", FALSE); /* || 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 */ char pathname[MAX_PATH]; hostpath(pathname, opts.ofile, sizeof(pathname)); outf = fopen( pathname, "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; }