diff --git a/CHANGES b/CHANGES index 6f0d1df4..1775435c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ +06 Jul 2010 Add FTLIB.C FakeTape library; add support in hetmap and hetinit for + FAKETAPE. 22 Jun 2010 CAPPING feature. Capping can be set in hercules.cnf with the keyword CAPPING and a number, representing the maximum MIPS for CPs. Capping does not apply to specialty engines. diff --git a/Hercules.vcproj b/Hercules.vcproj index 8b96c0d4..9f22cdb7 100644 --- a/Hercules.vcproj +++ b/Hercules.vcproj @@ -729,6 +729,10 @@ RelativePath=".\hetlib.h" > + + @@ -765,6 +769,10 @@ RelativePath=".\hetlib.c" > + + diff --git a/Makefile.am b/Makefile.am index 3a444db6..07799087 100644 --- a/Makefile.am +++ b/Makefile.am @@ -143,6 +143,7 @@ dyndev_SRC = commadpt.c \ tapeccws.c \ sllib.c \ hetlib.c \ + ftlib.c \ awstape.c \ faketape.c \ hettape.c \ @@ -335,6 +336,7 @@ endif # libherct_la_SOURCES = sllib.c \ + ftlib.c \ hetlib.c libherct_la_LDFLAGS = $(LIB_LD_FLAGS) @@ -719,6 +721,7 @@ noinst_HEADERS = fishhang.h \ hbyteswp.h \ dasdblks.h \ hetlib.h \ + ftlib.h \ version.h \ parser.h \ dasdtab.h \ diff --git a/ftlib.c b/ftlib.c new file mode 100644 index 00000000..3899b8b5 --- /dev/null +++ b/ftlib.c @@ -0,0 +1,792 @@ +/* FTLIB.C (c) Copyright TurboHercules, SAS 2010 */ +/* Hercules Tape Function Lib for FAKETAPE */ +/* */ +/* Released under "The Q Public License Version 1" */ +/* (http://www.hercules-390.org/herclic.html) as modifications to */ +/* Hercules. */ + +// $Id: ftlib.c 6000 2010-05-18 14:30:48Z pgorlinsky $ + + +/*-------------------------------------------------------------------*/ +/* This module contains the FAKETAPE emulated tape format library. */ +/* Note: this library was built from the original FAKETAPE.C source. */ +/*-------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------*/ +/* Reference information: */ +/* FSIMS100 Faketape manual */ +/*-------------------------------------------------------------------*/ + +#include "hstdinc.h" +#define _FTLIB_C_ +#define _HTAPE_DLL_ +#include "hercules.h" /* need Hercules control blocks */ +#include "ftlib.h" /* Main faketape header file */ + +// Local constant data NOTE: Keep in sync with hetlib.c + +static const char *fet_errstr[] = +{ + "No error", // FETE_OK + "File error", // FETE_ERROR + "Tapemark read", // FETE_TAPEMARK + "Beginning of tape", // FETE_BOT + "End of tape", // FETE_EOT + "BOR not found", // FETE_BADBOR + "EOR not found", // FETE_BADEOR + "Unexpected tapemark", // FETE_BADMARK + "Buffer not big enough", // FETE_OVERFLOW + "Premature EOF", // FETE_PREMEOF + "Decompression error", + "Unknown compression method", + "Compression error", + "Specified length to big", // FETE_BADLEN + "Write protected", // FETE_PROTECTED + "Bad function code passed", // FETE_BADFUNC + "Bad compression method", + "Bad compression level", + "Bad write chunk size", + "Invalid direction specified", // FETE_BADDIR + "Insufficient memory", // FETE_NOMEM + "Couldn't read block header", // FETE_BADHDR + "Inconsistent compression flags", + "Block is short", // FETE_BADBLOCK + "Location error", // FETE_BADLOC + "Invalid error code", // unknown error +}; +#define FET_ERRSTR_MAX ( sizeof( fet_errstr) / sizeof( fet_errstr[ 0 ] ) ) + +/*-------------------------------------------------------------------*/ +/* Open a FAKETAPE format file */ +/* */ +/* If successful, the file descriptor is stored in the device block */ +/* and the return value is zero. Otherwise the return value is < 0. */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_open ( FETB **fetb, char *filename, int flags ) +{ +int rc = -1; /* Return code */ +int fd = -1; +char pathname[MAX_PATH]; /* file path in host format */ +FETB *tfetb; +char *omode; +int oflags; + + + /* Open the FAKETAPE file */ + *fetb = NULL; + + hostpath(pathname, filename, sizeof(pathname)); + + /* + || Allocate a new FETB + */ + tfetb = calloc( 1, sizeof( FETB ) ); + if( tfetb == NULL ) + { + return( FETE_NOMEM ); + } + + /* + || clear FETOPEN_CREATE if FETOPEN_READONLY is specified + */ + if(flags & FETOPEN_READONLY) + { + flags&=~FETOPEN_CREATE; + } + /* + || Translate HET create flag to filesystem flag + */ + oflags = ( ( flags & FETOPEN_CREATE ) ? O_CREAT : 0 ); + + omode = "r+b"; + if( !( flags & FETOPEN_READONLY ) ) + { + fd = open( pathname, O_RDWR | O_BINARY | oflags, S_IRUSR | S_IWUSR | S_IRGRP ); + } + + /* If file is read-only, attempt to open again */ + if ( ( flags & FETOPEN_READONLY ) || (fd < 0 && (EROFS == errno || EACCES == errno))) + { + omode = "rb"; + tfetb->writeprotect = TRUE; + fd = open( pathname, O_RDONLY | O_BINARY, S_IRUSR | S_IRGRP ); + } + + /* + || Error out if both opens failed + */ + if( fd == -1 ) + { + free( tfetb ); + return( FETE_ERROR ); + } + + /* + || Associate stream with file descriptor + */ + tfetb->fd = fdopen( fd, omode ); + + if( tfetb->fd == NULL ) + { + rc = errno; + close( fd ); + errno = rc; + free( tfetb ); + return( FETE_ERROR ); + } + + /* + || If uninitialized tape, write 2 tapemarks to make it a valid NL tape + */ + rc = fet_read_header( tfetb, 0L, NULL, NULL ); + if( rc < 0 && rc != FETE_TAPEMARK ) + { + if( rc != FETE_EOT ) + { + return( rc ); + } + + rc = fet_tapemark( tfetb ); + if( rc < 0 ) + { + return( rc ); + } + + rc = fet_tapemark( tfetb ); + if( rc < 0 ) + { + return( rc ); + } + } + + /* + || Reposition tape to load point + */ + rc = fet_rewind( tfetb ); + if( rc < 0 ) + { + return( rc ); + } + + /* + || Give the caller the new FETB + */ + *fetb = tfetb; + + return( 0 ); + +} /* end function fet_open */ + +/*-------------------------------------------------------------------*/ +/* Close a FAKETAPE format file */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_close ( FETB **fetb ) +{ + if( *(fetb) != NULL ) + { + if( (*fetb)->fd != NULL ) + { + fclose( (*fetb)->fd ); + } + free( *(fetb) ); + } + *fetb = NULL; + return( 0 ); +} + +/*-------------------------------------------------------------------*/ +/* Read a FAKETAPE block header */ +/* */ +/* If successful, return value is zero, and prvblkl and curblkl are */ +/* set to the previous and current block lengths respectively. */ +/* If error, return value is -1 and unitstat is set to CE+DE+UC */ +/* and prvblkl and curblkl are undefined. Either or both of prvblkl */ +/* and/or curblkl may be NULL. */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_read_header( FETB *fetb, off_t blkpos, + U16* pprvblkl, U16* pcurblkl ) +{ +int rc; /* Return code */ +off_t rcoff; /* Return code from lseek() */ +FAKETAPE_BLKHDR fakehdr; /* FakeTape block header */ +char sblklen[5]; /* work for converting hdr */ +int prvblkl; /* Previous block length */ +int curblkl; /* Current block length */ +int xorblkl; /* XOR check of block lens */ + + /* Reposition file to the requested block header */ + rc = fseek( fetb->fd, blkpos, SEEK_SET ); + if (rc < 0) + { + return FETE_ERROR; + } + else + rcoff = ftell( fetb->fd ); + + /* Read the 12-ASCII-hex-character block header */ + rc = (int)fread( &fakehdr, 1, sizeof(FAKETAPE_BLKHDR), fetb->fd ); + + /* Handle read error condition */ + if (rc < 0) + { + return FETE_ERROR; + } + + /* Handle end of file (uninitialized tape) condition */ + if (rc == 0) + { + return FETE_EOT; + } + + /* Handle end of file within block header */ + if (rc < (int)sizeof(FAKETAPE_BLKHDR)) + { + return FETE_BADHDR; + } + + /* Convert the ASCII-hex-character block lengths to binary */ + strncpy( sblklen, fakehdr.sprvblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &prvblkl ); + strncpy( sblklen, fakehdr.scurblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &curblkl ); + strncpy( sblklen, fakehdr.sxorblkl, 4 ); sblklen[4] = 0; sscanf( sblklen, "%x", &xorblkl ); + + /* Verify header integrity using the XOR header field */ + if ( (prvblkl ^ curblkl) != xorblkl ) + { + return FETE_ERROR; + } + + /* Return the converted value(s) to the caller */ + if (pprvblkl) *pprvblkl = prvblkl; + if (pcurblkl) *pcurblkl = curblkl; + + /* Successful return */ + return FETE_OK; + +} /* end function fet_read_header */ + +/*-------------------------------------------------------------------*/ +/* Read a block from a FAKETAPE format file */ +/* */ +/* If successful, return value is block length read. */ +/* If a tapemark was read, the return value is zero, and the */ +/* current file number in the device block is incremented. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_read ( FETB *fetb, BYTE *buf ) +{ +int rc; /* Return code */ +off_t blkpos; /* Offset of block header */ +U16 curblkl; /* Current block length */ + + /* Initialize current block position */ + blkpos = fetb->nxtblkpos; + + /* Read the block header to obtain the current block length */ + rc = fet_read_header( fetb, blkpos, NULL, &curblkl ); + if ( rc == FETE_EOT ) return FETE_EOT; + if (rc < 0) + return FETE_BADHDR; /* (error message already issued) */ + ASSERT( curblkl >= 0 ); + + /* Calculate the offset of the next block header */ + blkpos += sizeof(FAKETAPE_BLKHDR) + curblkl; + + /* If not a tapemark, read the data block */ + if (curblkl > 0) + { + rc = (int)fread( buf, 1, curblkl, fetb->fd ); + + /* Handle read error condition */ + if (rc < 0) + { + return FETE_ERROR; + } + + /* Handle end of file within data block */ + if (rc < curblkl) + { + return FETE_BADBLOCK; + } + } + + /* Calculate the offsets of the next and previous blocks */ + fetb->prvblkpos = fetb->nxtblkpos; + fetb->nxtblkpos = blkpos; + + /* Increment the block number */ + fetb->blockid++; + + /* Increment file number and return zero if tapemark was read */ + if (curblkl == 0) + { + fetb->curfilen++; + return FETE_TAPEMARK; /* UX will be set by caller */ + } + + /* Return block length */ + return curblkl; + +} /* end function fet_read */ + +/*-------------------------------------------------------------------*/ +/* Write a FAKETAPE block header */ +/* */ +/* If successful, return value is zero. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ + +DLL_EXPORT int +fet_write_header ( FETB *fetb, off_t blkpos, + U16 prvblkl, U16 curblkl ) +{ +int rc; /* Return code */ +off_t rcoff; /* Return code from lseek() */ +FAKETAPE_BLKHDR fakehdr; /* FAKETAPE block header */ +char sblklen[5]; /* work buffer */ + + /* Position file to where block header is to go */ + rc = fseek( fetb->fd, blkpos, SEEK_SET ); + if (rc < 0) + { + return FETE_BADLOC; + } + else + rcoff = ftell( fetb->fd ); + + /* Build the 12-ASCII-hex-character block header */ + snprintf( sblklen, sizeof(sblklen), "%4.4X", prvblkl ); + strncpy( fakehdr.sprvblkl, sblklen, sizeof(fakehdr.sprvblkl) ); + snprintf( sblklen, sizeof(sblklen), "%4.4X", curblkl ); + strncpy( fakehdr.scurblkl, sblklen, sizeof(fakehdr.scurblkl) ); + snprintf( sblklen, sizeof(sblklen), "%4.4X", prvblkl ^ curblkl ); + strncpy( fakehdr.sxorblkl, sblklen, sizeof(fakehdr.sxorblkl) ); + + /* Write the block header */ + rc = (int)fwrite( &fakehdr, 1, sizeof(FAKETAPE_BLKHDR), fetb->fd ); + if (rc < (int)sizeof(FAKETAPE_BLKHDR)) + { + if(errno==ENOSPC) + { + /* Disk FULL */ + return FETE_EOT; + } + + return FETE_ERROR; + } + + return 0; + +} /* end function fet_write_header */ + +/*-------------------------------------------------------------------*/ +/* Write a block to a FAKETAPE format file */ +/* */ +/* If successful, return value is zero. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_write( FETB *fetb, BYTE *buf, U16 blklen ) +{ +int rc; /* Return code */ +off_t rcoff; /* Return code from lseek() */ +off_t blkpos; /* Offset of block header */ +U16 prvblkl; /* Length of previous block */ + + /* Initialize current block position and previous block length */ + blkpos = fetb->nxtblkpos; + prvblkl = 0; + + /* Determine previous block length if not at start of tape */ + if (fetb->nxtblkpos > 0) + { + /* Retrieve the previous block length */ + rc = fet_read_header( fetb, fetb->prvblkpos, NULL, &prvblkl ); + if (rc < 0) return FETE_ERROR; + + /* Recalculate the offset of the next block */ + blkpos = fetb->prvblkpos + sizeof(FAKETAPE_BLKHDR) + prvblkl; + } + + /* Reposition file to the new block header */ + rc = fseek( fetb->fd, blkpos, SEEK_SET ); + if (rc < 0) + { + return FETE_BADLOC; + } + else + rcoff = ftell( fetb->fd ); + + if(fetb->maxsize>0) + { + if((off_t)(fetb->nxtblkpos+blklen+sizeof(FAKETAPE_BLKHDR)) > fetb->maxsize ) + { + return FETE_EOT; + } + } + + /* Write the block header */ + rc = fet_write_header( fetb, rcoff, prvblkl, blklen ); + if (rc < 0) + return FETE_EOT; /* (error message already issued) */ + + /* Calculate the offsets of the next and previous blocks */ + fetb->nxtblkpos = blkpos + sizeof(FAKETAPE_BLKHDR) + blklen; + fetb->prvblkpos = blkpos; + + /* Write the data block */ + rc = (int)fwrite( buf, 1, blklen, fetb->fd); + if (rc < blklen) + { + if(errno==ENOSPC) + { + /* Disk FULL */ + return FETE_ERROR; + } + + return FETE_EOT; + } + + /* Increment the block number */ + fetb->blockid++; + + /* Set new physical EOF */ + + + do rc = ftruncate( fileno( fetb->fd ), fetb->nxtblkpos ); + while (EINTR == rc); + + if (rc != 0) + { + return FETE_EOT; + } + + /* Return normal status */ + return 0; + +} /* end function fet_write */ + +/*-------------------------------------------------------------------*/ +/* Write a tapemark to a FAKETAPE format file */ +/* */ +/* If successful, return value is zero. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_tapemark( FETB *fetb ) +{ +int rc; /* Return code */ +off_t rcoff; /* Return code from lseek() */ +off_t blkpos; /* Offset of block header */ +U16 prvblkl; /* Length of previous block */ + + /* Initialize current block position and previous block length */ + blkpos = fetb->nxtblkpos; + prvblkl = 0; + + /* Determine previous block length if not at start of tape */ + if (fetb->nxtblkpos > 0) + { + /* Retrieve the previous block length */ + rc = fet_read_header( fetb, fetb->prvblkpos, NULL, &prvblkl ); + if (rc < 0) + return rc; + + /* Recalculate the offset of the next block */ + blkpos = fetb->prvblkpos + sizeof(FAKETAPE_BLKHDR) + prvblkl; + } + + /* Reposition file to the new block header */ + rc = fseek( fetb->fd, blkpos, SEEK_SET ); + if (rc < 0) + { + return FETE_BADLOC; + } + else + rcoff = ftell( fetb->fd ); + + if(fetb->maxsize>0) + { + if((off_t)(fetb->nxtblkpos+sizeof(FAKETAPE_BLKHDR)) > fetb->maxsize) + { + return FETE_EOT; + } + } + + /* Write the block header */ + rc = fet_write_header( fetb, rcoff, prvblkl, 0 ); + if (rc < 0) + return rc; /* (error message already issued) */ + + /* Increment the block number */ + fetb->blockid++; + + /* Calculate the offsets of the next and previous blocks */ + fetb->nxtblkpos = blkpos + sizeof(FAKETAPE_BLKHDR); + fetb->prvblkpos = blkpos; + + /* Set new physical EOF */ + rc = ftell( fetb->fd ); + + do rc = ftruncate( fileno( fetb->fd ), fetb->nxtblkpos ); + while (EINTR == rc); + + if (rc != 0) + { + return FETE_PROTECTED; + } + + /* Return normal status */ + return 0; + +} /* end function fet_tapemark */ + +/*-------------------------------------------------------------------*/ +/* Synchronize a FAKETAPE format file (i.e. flush buffers to disk) */ +/* */ +/* If successful, return value is zero. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_sync( FETB *fetb ) +{ + /* Unit check if tape is write-protected */ + if (fetb->writeprotect) + { + return FETE_PROTECTED; + } + + /* Perform sync. Return error on failure. */ + if (fdatasync( fileno( fetb->fd ) ) < 0) + { + return FETE_ERROR; + } + + /* Return normal status */ + return 0; + +} /* end function fet_sync */ + +/*-------------------------------------------------------------------*/ +/* Forward space over next block of a FAKETAPE format file */ +/* */ +/* If successful, return value is the length of the block skipped. */ +/* If the block skipped was a tapemark, the return value is zero, */ +/* and the current file number in the device block is incremented. */ +/* If error, return value is < 0. */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_fsb( FETB *fetb ) +{ +int rc; /* Return code */ +off_t blkpos; /* Offset of block header */ +U16 blklen; /* Block length */ + + /* Initialize current block position */ + blkpos = fetb->nxtblkpos; + + /* Read the block header to obtain the current block length */ + rc = fet_read_header( fetb, blkpos, NULL, &blklen ); + if (rc < 0) + return rc; /* (error message already issued) */ + + /* Calculate the offset of the next block */ + blkpos += sizeof(FAKETAPE_BLKHDR) + blklen; + + /* Calculate the offsets of the next and previous blocks */ + fetb->prvblkpos = fetb->nxtblkpos; + fetb->nxtblkpos = blkpos; + + /* Increment current file number if tapemark was skipped */ + if (blklen == 0) + fetb->curfilen++; + + /* Increment the block number */ + fetb->blockid++; + + /* Return block length or zero if tapemark */ + return blklen; + +} /* end function fet_fsb */ + +/*-------------------------------------------------------------------*/ +/* Backspace to previous block of a FAKETAPE format file */ +/* */ +/* If successful, return value is the length of the block. */ +/* If the block is a tapemark, the return value is zero, */ +/* and the current file number in the device block is decremented. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_bsb( FETB *fetb ) +{ +int rc; /* Return code */ +U16 curblkl; /* Length of current block */ +U16 prvblkl; /* Length of previous block */ +off_t blkpos; /* Offset of block header */ + + /* Unit check if already at start of tape */ + if (fetb->nxtblkpos == 0) + { + return FETE_BOT; + } + + /* Backspace to previous block position */ + blkpos = fetb->prvblkpos; + + /* Read the block header to obtain the block lengths */ + rc = fet_read_header( fetb, blkpos, &prvblkl, &curblkl ); + if (rc < 0) + return rc; /* (error message already issued) */ + + /* Calculate the offset of the previous block */ + fetb->prvblkpos = blkpos - sizeof(FAKETAPE_BLKHDR) - prvblkl; + fetb->nxtblkpos = blkpos; + + /* Decrement current file number if backspaced over tapemark */ + if (curblkl == 0) + fetb->curfilen--; + + /* Decrement the block number */ + fetb->blockid--; + + /* Return block length or zero if tapemark */ + return curblkl; + +} /* end function fet_bsb */ + +/*-------------------------------------------------------------------*/ +/* Forward space to next logical file of a FAKETAPE format file */ +/* */ +/* For FAKETAPE files, the forward space file operation is achieved */ +/* by forward spacing blocks until positioned just after a tapemark. */ +/* */ +/* If successful, return value is zero, and the current file number */ +/* in the device block is incremented by fet_fsb. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_fsf( FETB *fetb ) +{ +int rc; /* Return code */ + + while (1) + { + /* Forward space over next block */ + rc = fet_fsb( fetb ); + if (rc < 0) + return rc; /* (error message already issued) */ + + /* Exit loop if spaced over a tapemark */ + if (rc == 0) + break; + + } /* end while */ + + /* Return normal status */ + return 0; + +} /* end function fet_fsf */ + +/*-------------------------------------------------------------------*/ +/* Backspace to previous logical file of a FAKETAPE format file */ +/* */ +/* For FAKETAPE files, the backspace file operation is achieved */ +/* by backspacing blocks until positioned just before a tapemark */ +/* or until positioned at start of tape. */ +/* */ +/* If successful, return value is zero, and the current file number */ +/* in the device block is decremented by fet_bsb. */ +/* If error, return value is < 0 */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_bsf( FETB *fetb ) +{ +int rc; /* Return code */ + + while (1) + { + /* Exit if now at start of tape */ + if (fetb->nxtblkpos == 0) + { + return FETE_BOT; + } + + /* Backspace to previous block position */ + rc = fet_bsb( fetb ); + if (rc < 0) + return rc; /* (error message already issued) */ + + /* Exit loop if backspaced over a tapemark */ + if (rc == 0) + break; + + } /* end while */ + + /* Return normal status */ + return 0; + +} /* end function fet_bsf */ + + +/*-------------------------------------------------------------------*/ +/* Rewinds a FAKETAPE format file */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_rewind ( FETB *fetb ) +{ +int rc; +off_t rcoff; + + rc = fseek( fetb->fd, 0L, SEEK_SET ); + if ( rc < 0 ) + { + return FETE_ERROR; + } + else + rcoff = ftell( fetb->fd ); + + fetb->nxtblkpos=0; + fetb->prvblkpos=-1; + fetb->curfilen=1; + fetb->blockid=0; + return 0; +} + +/*-------------------------------------------------------------------*/ +/* Determines if a FAKETAPE has passed a virtual EOT marker */ +/*-------------------------------------------------------------------*/ +DLL_EXPORT int +fet_passedeot ( FETB *fetb ) +{ + if(fetb->nxtblkpos==0) + fetb->eotwarning = FALSE; + else + if(fetb->maxsize==0) + fetb->eotwarning = TRUE; + else + if(fetb->nxtblkpos+fetb->eotmargin > fetb->maxsize) + fetb->eotwarning = TRUE; + else + fetb->eotwarning = FALSE; + return fetb->eotwarning; +} + +DLL_EXPORT const char * +fet_error( int rc ) +{ + // no error, return OK + if( rc > -1 ) + rc = 0; + + rc *= -1; // make positive for indexing + + if( rc >= (int)FET_ERRSTR_MAX ) // detect mismatch errors vs error strings + rc = FET_ERRSTR_MAX - 1; + + return( fet_errstr[ rc ] ); +} diff --git a/ftlib.h b/ftlib.h new file mode 100644 index 00000000..cbd94d66 --- /dev/null +++ b/ftlib.h @@ -0,0 +1,134 @@ +/* FTLIB.H (c) Copyright TurboHercules, SAS 2010 */ +/* Header files for FAKETAPE. */ +/* */ +/* Released under "The Q Public License Version 1" */ +/* (http://www.hercules-390.org/herclic.html) as modifications to */ +/* Hercules. */ + +// $Id: ftlib.h 6033 2010-05-25 03:55:37Z pgorlinsky $ + +#if !defined( _FTLIB_H_ ) +#define _FTLIB_H_ + +#include "hercules.h" + +#ifndef _FTLIB_C_ +#ifndef _HTAPE_DLL_ +#define FET_DLL_IMPORT DLL_IMPORT +#else /* _HTAPE_DLL_ */ +#define FET_DLL_IMPORT extern +#endif /* _HTAPE_DLL_ */ +#else +#define FET_DLL_IMPORT DLL_EXPORT +#endif + +/*-------------------------------------------------------------------*/ +/* Structure definition for Flex FakeTape block headers */ +/*-------------------------------------------------------------------*/ +/* + * The character length fields in a Flex FakeTape header are in BIG + * endian ASCII hex. That is to say, when the length field is ASCII + * "0123" (i.e. 0x30, 0x31, 0x32, 0x33), the length of the block is + * decimal 291 bytes (0x0123 == 291). + * + * The two block length fields are followed by an XOR "check" field + * calculated as the XOR of the two preceding length fields and is + * used to verify the integrity of the header. + * + * The Flex FakeTape tape format does not support any flag fields + * in its header and thus does not support any type of compression. + */ +typedef struct _FAKETAPE_BLKHDR +{ /* + * PROGRAMMING NOTE: note that for Flex FakeTapes, the "previous + * chunk size" comes FIRST, followed by the "current chunk size" + * second. This is the complete opposite from the way it is for + * AWS tape files. Also note that for Flex FakeTape the size fields + * are in BIG endian ASCII hex-string whereas for AWS tapes + * they're LITTLE endian binary. + */ + char sprvblkl[4]; /* length of previous block */ + char scurblkl[4]; /* length of this block */ + char sxorblkl[4]; /* XOR both lengths together */ +} +FAKETAPE_BLKHDR; + +/* +|| Control block for FakeTape Emulated Tape files +*/ +typedef struct _fetb +{ + FILE *fd; /* Tape file descriptor */ + char filename[MAX_PATH]; /* filename */ + + off_t nxtblkpos; /* Offset from start of file + to next block */ + off_t prvblkpos; /* Offset from start of file + to previous block */ + off_t eotmargin; /* Amount of space left before + reporting EOT (in bytes) */ + off_t maxsize; /* Maximum allowed TAPE file + size */ + U16 curfilen; /* Current file number */ + U32 blockid; /* Current device block ID */ + FAKETAPE_BLKHDR chdr; /* Current block header */ + u_int writeprotect:1; /* TRUE=write protected */ + u_int readlast:1; /* TRUE=last i/o was read */ + u_int truncated:1; /* TRUE=file truncated */ + u_int eotwarning:1; /* TRUE=EOT warning area reached */ + +} FETB; + +/* +|| Flags for fet_open() +*/ +#define FETOPEN_CREATE 0x01 /* Create NL tape, if file missing */ +#define FETOPEN_READONLY 0x02 /* Force the open read-only */ + +/* +|| Error definitions NOTE: Keep these in sync with hetlib.h +*/ +#define FETE_OK 0 /* No error */ +#define FETE_ERROR -1 /* File error (check errno) */ +#define FETE_TAPEMARK -2 /* Tapemark read */ +#define FETE_BOT -3 /* Beginning of tape */ +#define FETE_EOT -4 /* End of tape */ +#define FETE_BADBOR -5 /* BOR not found */ +#define FETE_BADEOR -6 /* EOR not found */ +#define FETE_BADMARK -7 /* Unexpected tapemark */ +#define FETE_OVERFLOW -8 /* Buffer not big enough */ +#define FETE_PREMEOF -9 /* Premature EOF */ +#define FETE_DECERR -10 // unused +#define FETE_UNKMETH -11 // unused +#define FETE_COMPERR -12 // unused +#define FETE_BADLEN -13 /* Specified length to big */ +#define FETE_PROTECTED -14 /* Write protected */ +#define FETE_BADFUNC -15 /* Bad function code passed */ +#define FETE_BADMETHOD -16 // unused +#define FETE_BADLEVEL -17 // unused +#define FETE_BADMSIZE -18 // unused +#define FETE_BADDIR -19 /* Invalid direction specified */ +#define FETE_NOMEM -20 /* Insufficient memory */ +#define FETE_BADHDR -21 /* Couldn't read block header */ +#define FETE_BADCOMPRESS -22 // unused +#define FETE_BADBLOCK -23 /* Block is short */ +#define FETE_BADLOC -24 /* Location error */ + +// Function Prototypes + +FET_DLL_IMPORT int fet_open( FETB **fetb, char *filename, int flags ); +FET_DLL_IMPORT int fet_close( FETB **fetb ); +FET_DLL_IMPORT int fet_read_header( FETB *fetb, off_t blkpos, U16* pprvblkl, U16* pcurblkl ); +FET_DLL_IMPORT int fet_read( FETB *fetb, void *sbuf ); +FET_DLL_IMPORT int fet_write_header( FETB *fetb, off_t blkpos, U16 prvblkl, U16 curblkl ); +FET_DLL_IMPORT int fet_write( FETB *fetb, void *sbuf, U16 slen ); +FET_DLL_IMPORT int fet_tapemark( FETB *fetb ); +FET_DLL_IMPORT int fet_sync( FETB *fetb ); +FET_DLL_IMPORT int fet_bsb( FETB *fetb ); +FET_DLL_IMPORT int fet_fsb( FETB *fetb ); +FET_DLL_IMPORT int fet_bsf( FETB *fetb ); +FET_DLL_IMPORT int fet_fsf( FETB *fetb ); +FET_DLL_IMPORT int fet_rewind( FETB *fetb ); +FET_DLL_IMPORT int fet_passedeot( FETB *fetb ); +FET_DLL_IMPORT const char *fet_error( int rc ); +#endif /* defined( _FTLIB_H_ ) */ diff --git a/hetinit.c b/hetinit.c index 76758421..c5505e92 100644 --- a/hetinit.c +++ b/hetinit.c @@ -1,4 +1,5 @@ /* HETINIT.C (c) Copyright Leland Lucius, 2000-2009 */ +/* (c) Copyright TurboHercules, SAS 2010 */ /* Creates IEHINITT or NL format Hercules Emulated Tapes */ /* */ /* Released under "The Q Public License Version 1" */ @@ -22,6 +23,7 @@ #include "hercules.h" #include "hetlib.h" +#include "ftlib.h" #include "sllib.h" #include "herc_getopt.h" @@ -59,10 +61,12 @@ main( int argc, char *argv[] ) char msgbuf[512]; /* message build work area */ int rc; SLLABEL lab; - HETB *hetb; + HETB *hetb; /* used for aws and het tapes*/ + FETB *fetb; /* used for faketapes */ int o_iehinitt; int o_nl; int o_compress; + int o_faketape; char *o_filename; char *o_owner; char *o_volser; @@ -105,7 +109,9 @@ main( int argc, char *argv[] ) display_version (stderr, msgbuf+10, FALSE); hetb = NULL; + fetb = NULL; o_filename = NULL; + o_faketape = FALSE; o_iehinitt = TRUE; o_nl = FALSE; o_compress = TRUE; @@ -158,6 +164,11 @@ main( int argc, char *argv[] ) o_filename = argv[ optind ]; + if ( ( rc = (int)strlen( o_filename ) ) > 4 && ( rc = strcasecmp( &o_filename[rc-4], ".fkt" ) ) == 0 ) + { + o_faketape = TRUE; + } + if( o_iehinitt ) { if( argc == 2 ) @@ -191,18 +202,31 @@ main( int argc, char *argv[] ) if( o_owner ) het_string_to_upper( o_owner ); - rc = het_open( &hetb, o_filename, HETOPEN_CREATE ); - if( rc < 0 ) + if ( o_faketape ) { - printf( MSG( HHC00075, "E", "het_open()", het_error( rc ) ) ); - goto exit; - } + rc = fet_open( &fetb, o_filename, FETOPEN_CREATE ); + if ( rc < 0 ) + { + printf( MSG( HHC00075, "E", "fet_open()", fet_error( rc ) ) ); + goto exit; + } - rc = het_cntl( hetb, HETCNTL_SET | HETCNTL_COMPRESS, o_compress ); - if( rc < 0 ) + } + else { - printf( MSG( HHC00075, "E", "het_cntl()", het_error( rc ) ) ); - goto exit; + rc = het_open( &hetb, o_filename, HETOPEN_CREATE ); + if( rc < 0 ) + { + printf( MSG( HHC00075, "E", "het_open()", het_error( rc ) ) ); + goto exit; + } + + rc = het_cntl( hetb, HETCNTL_SET | HETCNTL_COMPRESS, o_compress ); + if( rc < 0 ) + { + printf( MSG( HHC00075, "E", "het_cntl()", het_error( rc ) ) ); + goto exit; + } } if( o_iehinitt ) @@ -214,10 +238,16 @@ main( int argc, char *argv[] ) goto exit; } - rc = het_write( hetb, &lab, sizeof( lab ) ); + if ( o_faketape ) + rc = fet_write( fetb, &lab, (U16)sizeof( lab ) ); + else + rc = het_write( hetb, &lab, sizeof( lab ) ); if( rc < 0 ) { - printf( MSG( HHC00075, "E", "het_write() for VOL1", het_error( rc ) ) ); + if ( o_faketape ) + printf( MSG( HHC00075, "E", "fet_write() for VOL1", fet_error( rc ) ) ); + else + printf( MSG( HHC00075, "E", "het_write() for VOL1", het_error( rc ) ) ); goto exit; } @@ -227,34 +257,55 @@ main( int argc, char *argv[] ) printf( MSG( HHC00075, "E", "sl_hdr1()", sl_error( rc ) ) ); goto exit; } - - rc = het_write( hetb, &lab, sizeof( lab ) ); + + if ( o_faketape ) + rc = fet_write( fetb, &lab, (U16)sizeof(lab) ); + else + rc = het_write( hetb, &lab, sizeof( lab ) ); if( rc < 0 ) { - printf( MSG( HHC00075, "E", "het_write() for HDR1", het_error( rc ) ) ); + if ( o_faketape ) + printf( MSG( HHC00075, "E", "fet_write() for HDR1", fet_error( rc ) ) ); + else + printf( MSG( HHC00075, "E", "het_write() for HDR1", het_error( rc ) ) ); goto exit; } } else if( o_nl ) { - rc = het_tapemark( hetb ); + if ( o_faketape ) + rc = fet_tapemark( fetb ); + else + rc = het_tapemark( hetb ); if( rc < 0 ) { - printf( MSG( HHC00075, "E", "het_tapemark()", het_error( rc ) ) ); + if ( o_faketape ) + printf( MSG( HHC00075, "E", "fet_tapemark()", fet_error( rc ) ) ); + else + printf( MSG( HHC00075, "E", "het_tapemark()", het_error( rc ) ) ); goto exit; } } - rc = het_tapemark( hetb ); + if ( o_faketape ) + rc = fet_tapemark( fetb ); + else + rc = het_tapemark( hetb ); if( rc < 0 ) { - printf( MSG( HHC00075, "E", "het_tapemark()", het_error( rc ) ) ); + if ( o_faketape ) + printf( MSG( HHC00075, "E", "fet_tapemark()", fet_error( rc ) ) ); + else + printf( MSG( HHC00075, "E", "het_tapemark()", het_error( rc ) ) ); goto exit; } exit: - het_close( &hetb ); + if ( o_faketape) + fet_close( &fetb ); + else + het_close( &hetb ); return( rc < 0 ); } diff --git a/hetlib.c b/hetlib.c index 26e5438d..12bf543c 100644 --- a/hetlib.c +++ b/hetlib.c @@ -30,7 +30,7 @@ #undef HETDEBUGW /* -|| Local constant data +|| Local constant data NOTE: Keep in sync with ftlib.c */ static const char *het_errstr[] = { @@ -57,6 +57,8 @@ static const char *het_errstr[] = "Insufficient memory", "Couldn't read block header", "Inconsistent compression flags", + "Block is short", + "Location error", "Invalid error code", }; #define HET_ERRSTR_MAX ( sizeof( het_errstr) / sizeof( het_errstr[ 0 ] ) ) diff --git a/hetlib.h b/hetlib.h index 1b04ca16..752703f7 100644 --- a/hetlib.h +++ b/hetlib.h @@ -173,6 +173,8 @@ typedef struct _hetb #define HETE_NOMEM -20 /* Insufficient memory */ #define HETE_BADHDR -21 /* Couldn't read block header */ #define HETE_BADCOMPRESS -22 /* Inconsistent compression flags */ +#define HETE_BADBLOCK -23 // unused +#define HETE_BADLOC -24 // unused /* || Public functions diff --git a/hetmap.c b/hetmap.c index c815ba6c..77c6f11c 100644 --- a/hetmap.c +++ b/hetmap.c @@ -24,6 +24,7 @@ #include "hercules.h" #include "hetlib.h" +#include "ftlib.h" #include "sllib.h" #include "herc_getopt.h" @@ -189,7 +190,7 @@ static SInt32 Print_Block_Data ( SInt32 ); */ static const char sep[] = "---------------------\n"; static const char help_hetmap[] = - "%s - Print a map of an HET or AWS tape file\n\n" + "%s - Print a map of an AWS, HET or FakeTape tape file\n\n" "Usage: %s [options] filename\n\n" "Options:\n" " -a print all label and file information (default: on)\n" @@ -200,7 +201,7 @@ static const char help_hetmap[] = " -s print dump of each data file (SLANAL format) (default: off)\n" " -t print TAPEMAP-compatible format output (default: off)\n"; static const char help_tapemap[] = - "%s - Print a map of an HET or AWS tape file\n\n" + "%s - Print a map of an AWS, HET or FakeTape tape file\n\n" "Usage: %s filename\n\n"; #ifdef EXTERNALGUI @@ -228,6 +229,9 @@ main( int argc, char *argv[] ) { Boolean lProcStdLbl = FALSE; HETB *hetb; + FETB *fetb; + char *i_filename; + int i_faketape = FALSE; SInt32 rc; SInt32 fileno; SInt32 i; @@ -240,7 +244,7 @@ main( int argc, char *argv[] ) U32 totblocks; U32 totubytes; U32 totcbytes; - U32 opts; + U32 opts = 0; SInt32 lResidue = max_bytes_dsply; /* amount of space left to print */ char pgmpath[MAX_PATH]; char *pgm; @@ -284,7 +288,7 @@ main( int argc, char *argv[] ) INITIALIZE_UTILITY(pgm); /* Display the program identification message */ - display_version (stderr, "Hercules HET and AWS tape map program", FALSE); + display_version (stderr, "Hercules AWS, HET and FakeTape tape map program", FALSE); if (! (opts & O_TAPEMAP_INVOKED) ) { @@ -295,45 +299,36 @@ main( int argc, char *argv[] ) { rc = getopt( argc, argv, "adfhlst" ); if( rc == -1 ) - { break; - } switch( rc ) { case 'a': opts = O_ALL; - break; - + break; case 'd': opts = O_DATASETS; - break; - + break; case 'f': opts = O_FILES; - break; - + break; case 'h': Print_Usage( pgm ); exit( 1 ); - break; - + break; case 'l': opts = O_LABELS; - break; - + break; case 's': opts = O_SLANAL_OUT; break; - case 't': opts = O_TAPEMAP_OUTPUT; - break; - + break; default: Print_Usage( pgm ); exit( 1 ); - break; + break; } } @@ -352,11 +347,29 @@ main( int argc, char *argv[] ) printf( "%-20.20s: %s\n", "Filename", argv[ optind ] ); } - rc = het_open( &hetb, argv[ optind ], 0 ); + i_filename = argv[ optind ]; + + if ( ( rc = (int)strlen( i_filename ) ) > 4 && ( rc = strcasecmp( &i_filename[rc-4], ".fkt" ) ) == 0 ) + { + i_faketape = TRUE; + } + + if ( i_faketape ) + rc = fet_open( &fetb, i_filename, FETOPEN_READONLY ); + else + rc = het_open( &hetb, i_filename, HETOPEN_READONLY ); if( rc < 0 ) { - printf( "het_open() returned %d\n", rc ); - het_close( &hetb ); + if ( i_faketape ) + { + printf( "fet_open() returned %d\n", rc ); + fet_close( &fetb ); + } + else + { + printf( "het_open() returned %d\n", rc ); + het_close( &hetb ); + } exit( 1 ); } @@ -381,8 +394,12 @@ main( int argc, char *argv[] ) #ifdef EXTERNALGUI if( extgui ) { + off_t curpos; /* Report progress every nnnK */ - off_t curpos = ftell( hetb->fd ); + if ( i_faketape ) + curpos = ftell( fetb->fd ); + else + curpos = ftell( hetb->fd ); if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) ) { prevpos = curpos; @@ -391,8 +408,13 @@ main( int argc, char *argv[] ) } #endif /*EXTERNALGUI*/ - rc = het_read( hetb, gBuffer ); - if( rc == HETE_EOT ) + if ( i_faketape) + { + rc = fet_read( fetb, gBuffer ); + } + else + rc = het_read( hetb, gBuffer ); + if( rc == HETE_EOT ) // FETE and HETE enums are the same { if( opts & O_TAPEMAP_OUTPUT ) { @@ -400,7 +422,7 @@ main( int argc, char *argv[] ) } break; } - + if( rc == HETE_TAPEMARK ) { fileno += 1; @@ -420,10 +442,13 @@ main( int argc, char *argv[] ) printf( "%-20.20s: %d\n", "Blocks", gBlkCount ); printf( "%-20.20s: %d\n", "Min Blocksize", uminsz ); printf( "%-20.20s: %d\n", "Max Blocksize", umaxsz ); - printf( "%-20.20s: %d\n", "Uncompressed bytes", ubytes ); - printf( "%-20.20s: %d\n", "Min Blocksize-Comp", cminsz ); - printf( "%-20.20s: %d\n", "Max Blocksize-Comp", cmaxsz ); - printf( "%-20.20s: %d\n", "Compressed bytes", cbytes ); + if ( !i_faketape ) + { + printf( "%-20.20s: %d\n", "Uncompressed bytes", ubytes ); + printf( "%-20.20s: %d\n", "Min Blocksize-Comp", cminsz ); + printf( "%-20.20s: %d\n", "Max Blocksize-Comp", cmaxsz ); + printf( "%-20.20s: %d\n", "Compressed bytes", cbytes ); + } } totblocks += gBlkCount; @@ -445,18 +470,24 @@ main( int argc, char *argv[] ) if( rc < 0 ) { - printf( "het_read() returned %d\n", rc ); + if ( i_faketape ) + printf( "fet_read() returned %d\n", rc ); + else + printf( "het_read() returned %d\n", rc ); break; } gBlkCount += 1; - ubytes += hetb->ublksize; - cbytes += hetb->cblksize; + if ( !i_faketape ) + { + ubytes += hetb->ublksize; + cbytes += hetb->cblksize; - if( uminsz == 0 || hetb->ublksize < uminsz ) uminsz = hetb->ublksize; - if( hetb->ublksize > umaxsz ) umaxsz = hetb->ublksize; - if( cminsz == 0 || hetb->cblksize < cminsz ) cminsz = hetb->cblksize; - if( hetb->cblksize > cmaxsz ) cmaxsz = hetb->cblksize; + if( uminsz == 0 || hetb->ublksize < uminsz ) uminsz = hetb->ublksize; + if( hetb->ublksize > umaxsz ) umaxsz = hetb->ublksize; + if( cminsz == 0 || hetb->cblksize < cminsz ) cminsz = hetb->cblksize; + if( hetb->cblksize > cmaxsz ) cmaxsz = hetb->cblksize; + } if ( rc >= 80 ) { @@ -541,12 +572,18 @@ main( int argc, char *argv[] ) printf( "%-20.20s:\n", "Summary" ); printf( "%-20.20s: %d\n", "Files", fileno ); printf( "%-20.20s: %d\n", "Blocks", totblocks ); - printf( "%-20.20s: %d\n", "Uncompressed bytes", totubytes ); - printf( "%-20.20s: %d\n", "Compressed bytes", totcbytes ); - printf( "%-20.20s: %d\n", "Reduction", totubytes - totcbytes ); + if ( !i_faketape ) + { + printf( "%-20.20s: %d\n", "Uncompressed bytes", totubytes ); + printf( "%-20.20s: %d\n", "Compressed bytes", totcbytes ); + printf( "%-20.20s: %d\n", "Reduction", totubytes - totcbytes ); + } } - het_close( &hetb ); + if ( i_faketape ) + fet_close( &fetb ); + else + fet_close( &fetb ); return 0; } diff --git a/makefile.generic b/makefile.generic index d1d52358..bec3b028 100644 --- a/makefile.generic +++ b/makefile.generic @@ -183,10 +183,10 @@ dasdcopy_OBJ = dasdcopy.o dasdutil.o codepage.o hostinfo.o version.o \ hetget_OBJ = hetget.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o pttrace.o hsys.o -hetinit_OBJ = hetinit.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ +hetinit_OBJ = hetinit.o hetlib.o ftlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o pttrace.o hsys.o -hetmap_OBJ = hetmap.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ +hetmap_OBJ = hetmap.o hetlib.o ftlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o pttrace.o hsys.o hetupd_OBJ = hetupd.o hetlib.o sllib.o codepage.o hostinfo.o version.o logmsg.o pttrace.o hsys.o @@ -199,7 +199,7 @@ HEADERS = codepage.h commadpt.h cpuint.h ctcadpt.h dasdblks.h \ dasdtab.h dat.h devtype.h esa390.h feat370.h feat390.h \ feat900.h featall.h featchk.h feature.h fishhang.h \ fthreads.h wthreads.h hbyteswp.h hercifc.h hercnls.h hercules.h \ - hetlib.h hostinfo.h httpmisc.h htypes.h ieee-w32.h \ + hetlib.h ftlib.h hostinfo.h httpmisc.h htypes.h ieee-w32.h \ inline.h linklist.h logger.h machdep.h opcode.h chsc.h \ parser.h sllib.h tapedev.h tt32api.h tuntap.h version.h \ vstore.h w32chan.h w32ctca.h shared.h hdl.h crypto.h \ diff --git a/makefile.w32 b/makefile.w32 index 164867e3..1075c785 100644 --- a/makefile.w32 +++ b/makefile.w32 @@ -194,10 +194,10 @@ dasdcopy_OBJ = dasdcopy.o dasdutil.o codepage.o hostinfo.o version.o \ hetget_OBJ = hetget.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o hsys.o pttrace.o -hetinit_OBJ = hetinit.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ +hetinit_OBJ = hetinit.o hetlib.o ftlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o hsys.o pttrace.o -hetmap_OBJ = hetmap.o hetlib.o sllib.o hostinfo.o version.o logmsg.o \ +hetmap_OBJ = hetmap.o hetlib.o ftlib.o sllib.o hostinfo.o version.o logmsg.o \ codepage.o hsys.o pttrace.o hetupd_OBJ = hetupd.o hetlib.o sllib.o codepage.o hostinfo.o version.o \ @@ -211,7 +211,7 @@ dyngui_OBJ = dyngui.o HEADERS = codepage.h commadpt.h cpuint.h ctcadpt.h dasdblks.h \ dasdtab.h dat.h devtype.h esa390.h feat370.h feat390.h \ - feat900.h featall.h featchk.h feature.h fishhang.h \ + feat900.h featall.h featchk.h feature.h fishhang.h ftlib.h \ fthreads.h wthreads.h hbyteswp.h hercifc.h hercnls.h hercules.h \ hetlib.h hostinfo.h httpmisc.h htypes.h ieee-w32.h \ inline.h linklist.h logger.h machdep.h opcode.h chsc.h \ diff --git a/msvc.makefile.includes/OBJ_CODE.msvc b/msvc.makefile.includes/OBJ_CODE.msvc index e3778345..9878fa38 100644 --- a/msvc.makefile.includes/OBJ_CODE.msvc +++ b/msvc.makefile.includes/OBJ_CODE.msvc @@ -52,6 +52,7 @@ hdasd_OBJ = \ htape_OBJ = \ $(O)hetlib.obj \ + $(O)ftlib.obj \ $(O)sllib.obj \ $(O)w32stape.obj