From:	CRDGW2::CRDGW2::MRGATE::"SMTP::SANDIA.GOV::JRREDHO" 30-AUG-1989 18:12
To:	MRGATE::"ARISIA::EVERHART"
Subj:	RE: VMS Backup reader for unix

Message-Id:  <8908302155.AA20972@crdgw1.ge.com>
Date: 30 Aug 89 15:03:00 MDT
From: "1524 RED-HORSE, JOHN R." <jrredho@sandia.gov>
Subject: RE: VMS Backup reader for unix
To: "everhart" <everhart@arisia.decnet>

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  vmsbackup.c~
# Wrapped by jrr@suntan on Wed Aug 30 12:24:10 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'vmsbackup.c~' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vmsbackup.c~'\"
else
echo shar: Extracting \"'vmsbackup.c~'\" \(19671 characters\)
sed "s/^X//" >'vmsbackup.c~' <<'END_OF_FILE'
X/*
X *
X *  Title:
X *	Backup
X *
X *  Decription:
X *	Program to read VMS backup tape
X *
X *  Author:
X *	John Douglas CAREY.
X *	Sven-Ove Westberg    (version 3.0)
X *
X *  Net-addess:
X *	john%monu1.oz@seismo.ARPA
X *	luthcad!sow@enea.UUCP
X *
X *  History:
X *	Version 1.0 - September 1984
X *		Can only read variable length records
X *	Version 1.1
X *		Cleaned up the program from the original hack
X *		Can now read stream files
X *	Version 1.2
X *		Now convert filename from VMS to UNIX
X *			and creates sub-directories
X *	Version 1.3
X *		Works on the Pyramid if SWAP is defined
X *	Version 1.4
X *		Reads files spanning multiple tape blocks
X *	Version 1.5
X *		Always reset reclen = 0 on file open
X *		Now output fixed length records
X *
X *      Version 2.0 - July 1985
X *		VMS Version 4.0 causes a rethink !!
X *		Now use mtio operations instead of opening and closing file
X *		Blocksize now grabed from the label
X *
X *	Version 2.1 - September 1985
X *		Handle variable length records of zero length.
X *
X *	Version 2.2 - July 1986
X *		Handle FORTRAN records of zero length.
X *		Inserted exit(0) at end of program.
X *		Distributed program in aus.sources
X *
X *	Version 2.3 - August 1986
X *		Handle FORTRAN records with record length fields
X *		at the end of a block
X *		Put debug output to a file.
X *		Distributed program in net.sources
X *
X *	Version 3.0 - December 1986
X *		Handle multiple saveset 
X *		Remote tape
X *		Interactive mode
X *		File name selection with meta-characters
X *		Convert ; to : in VMS filenames
X *		Flag for usage of VMS directory structure
X *		Flag for "useless" files  eg. *.exe
X *		Flag for use VMS version in file names
X *		Flag for verbose mode
X *		Flag to list the contents of the tape
X *		Distributed to mod.sources
X *
X *
X *  Installation:
X *
X *	Computer Centre
X *	Monash University
X *	Wellington Road
X *	Clayton
X *	Victoria	3168
X *	AUSTRALIA
X *
X */
X#include	<stdio.h>
X#include	<ctype.h>
X
X#include	<sys/ioctl.h>
X#include	<sys/types.h>
X#include	<sys/stat.h>	/* I changed this in order to do a file stat */
X				/* regardless of whether or not REMOTE was */
X				/* #define`d.  This was necessary for */
X				/* checking to see if tapefile was a TAPE */
X				/* or a FILE. jrr 8/25/89. */
X#ifdef REMOTE
X#include	<local/rmt.h>
X#endif
X#include	<sys/mtio.h>
X#include	<sys/file.h>
X
X#ifdef pyr
X#define SWAP
X#endif pyr
X
X#ifdef sun
X#define SWAP
X#endif
X
struct bbh {			/* Bytes this line:   Running Total:    */
X        short	bbh_dol_w_size;	/*                 2                2   */
X	short	bbh_dol_w_opsys; /*                2                4   */
X	short	bbh_dol_w_subsys; /*               2                6   */
X	short	bbh_dol_w_applic; /*               2                8   */
X	long	bbh_dol_l_number; /*               4               12   */
X	char	bbh_dol_t_spare_1[20]; /*         20               32   */
X	short	bbh_dol_w_struclev; /*             2               34   */
X	short	bbh_dol_w_volnum; /*               2               36   */
X	long	bbh_dol_l_crc;	/*                 4               40   */
X	long	bbh_dol_l_blocksize; /*            4               44   */
X	long	bbh_dol_l_flags; /*                4               48   */
X	char	bbh_dol_t_ssname[32]; /*          32               80   */
X	short	bbh_dol_w_fid[3]; /*               6               86   */
X	short	bbh_dol_w_did[3]; /*               6               92   */
X	char	bbh_dol_t_filename[128]; /*      128              220   */
X	char	bbh_dol_b_rtype; /*                1              221   */
X	char	bbh_dol_b_rattrib; /*              1              222   */
X	short	bbh_dol_w_rsize; /*                2              224   */
X	char	bbh_dol_b_bktsize; /*              1              225   */
X	char	bbh_dol_b_vfcsize; /*              1              226   */
X	short	bbh_dol_w_maxrec; /*               2              228   */
X	long	bbh_dol_l_filesize; /*             4              232   */
X	char	bbh_dol_t_spare_2[22]; /*         22              254   */
X	short	bbh_dol_w_checksum; /*             2              256   */
X} *block_header;
X
struct brh {			/* Bytes this line:   Running Total:    */
X	short	brh_dol_w_rsize; /*                2                2   */
X	short	brh_dol_w_rtype; /*                2                4   */
X	long	brh_dol_l_flags; /*                4                8   */
X	long	brh_dol_l_address; /*              4               12   */
X	long	brh_dol_l_spare; /*                4               16   */
X} *record_header;
X
X/* define record types */
X
X#define	brh_dol_k_null	0
X#define	brh_dol_k_summary	1
X#define	brh_dol_k_volume	2
X#define	brh_dol_k_file	3
X#define	brh_dol_k_vbn	4
X#define brh_dol_k_physvol	5
X#define brh_dol_k_lbn	6
X#define	brh_dol_k_fid	7
X
struct bsa {
X	short	bsa_dol_w_size;
X	short	bsa_dol_w_type;
X	char	bsa_dol_t_text[1];
X} *data_item;
X
X#ifdef	STREAM
char	*def_tapefile = "/dev/rts8";
X#else
char	*def_tapefile = "/dev/rmt8";
X#endif
char	*tapefile;
X
char	filename[128];
int	filesize;
X
char	recfmt;		/* record format */
X
X#define			FAB_dol_C_UDF	0	/* undefined */
X#define			FAB_dol_C_FIX	1	/* fixed-length record */
X#define			FAB_dol_C_VAR	2	/* variable-length record */
X#define			FAB_dol_C_VFC	3	/* variable-length with fixed-length control record */
X#define 		FAB_dol_C_STM	4	/* RMS-11 stream record (valid only for sequential org) */
X#define			FAB_dol_C_STMLF	5	/* stream record delimited by LF (sequential org only) */
X#define 		FAB_dol_C_STMCR	6	/* stream record delimited by CR (sequential org only) */
X#define			FAB_dol_C_MAXRFM	6	/* maximum rfm supported */
X
char	recatt;		/* record attributes */
X
X#define			FAB_dol_V_FTN	0	/* FORTRAN carriage control character */
X#define			FAB_dol_V_CR	1	/* line feed - record -carriage return */
X#define			FAB_dol_V_PRN	2	/* print-file carriage control */
X#define			FAB_dol_V_BLK	3	/* records don't cross block boundaries */
X
X#define	FANO	20
X
X#ifdef	pyr
static struct	bsa	*file_table[FANO];
X#else
struct	bsa	*file_table[FANO];
X#endif
X
X/*-------------------------------------------------------------------------*/
X                 		/* ************************** */
X                        /* My stuff added to try to hack this into */
X			/* a code capable of reading files as well as tapes  */
X				/* jrr 8/25/89. */
X               			/* ************************** */
X#define YES    1
X#define NO     0
X
struct stat stbuf;
short IsTape;
X
X/*------------------------------------------------------------------------*/
X
XFILE	*f	= NULL;
int	file_count;
short	reclen;
short	fix;
short	recsize;
int	vfcsize;
X
X#ifdef	NEWD
XFILE	*lf;
X#endif	NEWD
X
int	fd;		/* tape file descriptor */
int 	cflag, dflag, eflag, sflag, tflag, vflag, wflag, xflag;
int	setnr;
char	**gargv;
int 	goptind, gargc;
X
X#define	LABEL_SIZE	80
char	label[LABEL_SIZE];
X
char	*block;
int	blocksize;
X
struct	mtop	op;
X
XFILE *
openfile(fn)
char	*fn;
X{
X	char	ufn[256];
X	char	ans[80];
X	char	*p, *q, s, *ext;
X	int	procf;
X
X	procf = 1;
X	/* copy fn to ufn and convert to lower case */
X	p = fn;
X	q = ufn;
X	while (*p) {
X		if (isupper(*p))
X			*q = *p - 'A' + 'a';
X		else
X			*q = *p;
X		p++;
X		q++;
X	}
X	*q = '\0';
X
X	/* convert the VMS to UNIX and make the directory path */
X	p = ufn;
X	q = ++p;
X	while (*q) {
X		if (*q == '.' || *q == ']') {
X			s = *q;
X			*q = '\0';
X			if(procf && dflag) mkdir(p, 0777);
X			*q = '/';
X			if (s == ']')
X				break;
X		}
X		*q++;
X	}
X	*q++;
X	if(!dflag) p=q;
X	/* strip off the version number */
X	while (*q && *q != ';') {
X		if( *q == '.') ext = q;
X		q++;
X	}
X	if (cflag) {
X		*q = ':';
X	}
X	else {
X		*q = '\0';
X	}
X	if(!eflag && procf) procf = typecmp(++ext);
X	if(procf && wflag) {
X		printf("extract %s [ny]",filename);
X		fflush(stdout);
X		gets(ans);
X		if(*ans != 'y') procf = NULL;
X	}
X	if(procf)
X		/* open the file for writing */
X		return(fopen(p, "w"));
X	else
X		return(NULL);
X}
X
typecmp(str)    /* Compare the filename type in str with our list
X                   of file type to be ignored.  Return 0 if the
X                   file is to be ignored, return 1 if the
X                   file is not in our list and should not be ignored. */
register char   *str;
X{
X        static char *type[] = {
X                "exe",          /* vms executable image */
X                "lib",          /* vms object library */
X                "obj",          /* rsx object file */
X                "odl",          /* rsx overlay description file */
X                "olb",          /* rsx object library */
X                "pmd",          /* rsx post mortem dump */
X                "stb",          /* rsx symbol table */
X                "sys",          /* rsx bootable system image */
X                "tsk",          /* rsx executable image */
X		"dir",
X		"upd",
X		"tlo",
X		"tlb",
X                ""              /* null string terminates list */
X        };
X        register int    i;
X
X        i = -1;
X        while (*type[++i])
X                if (strncmp(str, type[i],3) == 0)
X                        return(0);      /* found a match, file to be ignored */
X        return(1);                      /* no match found */
X}
X
process_file(buffer)
char	*buffer;
X{
X	int	i, n;
X	char	*p, *q;
X	short	dsize, nblk, lnch;
X
X	int	c;
X	short	*s;
X
X	int 	procf;
X
X	s = (short *) buffer;
X
X	/* check the header word */
X	if (*s != 257) {
X		printf("Snark: invalid data header\n");
X		exit(1);
X	}
X
X	c = 2;
X	for (i = 0; i < FANO; i++) {
X		file_table[i] = (struct bsa *) &buffer[c];
X#ifndef	SWAP
X		dsize = file_table[i]->bsa_dol_w_size;
X#else
X		swap(&file_table[i]->bsa_dol_w_size, &dsize, sizeof(short));
X#endif
X		c += dsize + 4;
X	}
X
X	/* extract file name */
X#ifndef	SWAP
X	dsize = file_table[0]->bsa_dol_w_size;
X#else
X	swap(&file_table[0]->bsa_dol_w_size, &dsize, sizeof(short));
X#endif
X	p = file_table[0]->bsa_dol_t_text;
X	q = filename;
X	for (i = 0; i < dsize; i++)
X		*q++ = *p++;
X	*q = '\0';
X
X	/* extract file's record attributes */
X#ifndef	SWAP
X	dsize = file_table[5]->bsa_dol_w_size;
X#else
X	swap(&file_table[5]->bsa_dol_w_size, &dsize, sizeof(short));
X#endif
X	p = file_table[5]->bsa_dol_t_text;
X	recfmt = p[0];
X	recatt = p[1];
X#ifndef	SWAP
X	bcopy(&p[2], &recsize, sizeof(short));
X#else
X	swap(&p[2], &recsize, sizeof(short));
X#endif
X	vfcsize = p[15];
X	if (vfcsize == 0)
X		vfcsize = 2;
X#ifdef	DEBUG
X	printf("recfmt = %d\n", recfmt);
X	printf("recatt = %d\n", recatt);
X	printf("reclen = %d\n", recsize);
X	printf("vfcsize = %d\n", vfcsize);
X#endif
X#ifndef	SWAP
X	bcopy(&p[10], &nblk, sizeof(short));
X	bcopy(&p[12], &lnch, sizeof(short));
X#else
X	swap(&p[10], &nblk, sizeof(short));
X	swap(&p[12], &lnch, sizeof(short));
X#endif
X	filesize = (nblk-1)*512 + lnch;
X#ifdef DEBUG
X	printf("nbk = %d, lnch = %d\n", nblk, lnch);
X	printf("filesize = 0x%x\n", filesize);
X#endif
X
X	/* open the file */
X	if (f != NULL) {
X		fclose(f);
X		file_count = 0;
X		reclen = 0;
X	}
X	procf = 0;
X	if (goptind < gargc) 
X		for(i=goptind; i < gargc; i++) {
X			procf |= match(filename,gargv[i]);
X		}
X	else
X		procf = 1;
X	if (tflag && procf) 
X		printf( " %-35s %8d \n",filename,filesize);
X	if (xflag && procf) {
X		/* open file */
X		f = openfile(filename);
X		if(f != NULL && vflag) printf("extracting %s\n", filename);
X	}
X}
X/*
X *
X *  process a virtual block record (file record)
X *
X */
process_vbn(buffer, rsize)
char		*buffer;
unsigned short	rsize;
X{
X	int	c, i;
X
X	if (f == NULL) {
X		return;
X	}
X	i = 0;
X	while (file_count+i < filesize && i < rsize) {
X		switch (recfmt) {
X		case FAB_dol_C_FIX:
X			if (reclen == 0) {
X				reclen = recsize;
X			}
X			fputc(buffer[i], f);
X			i++;
X			reclen--;
X			break;
X
X		case FAB_dol_C_VAR:
X		case FAB_dol_C_VFC:
X			if (reclen == 0) {
X				reclen = *((short *) &buffer[i]);
X#ifdef	SWAP
X				swap(&reclen, &reclen, sizeof(short));
X#endif
X#ifdef	NEWD
X				fprintf(lf, "---\n");
X				fprintf(lf, "reclen = %d\n", reclen);
X				fprintf(lf, "i = %d\n", i);
X				fprintf(lf, "rsize = %d\n", rsize);
X#endif	NEWD
X				fix = reclen;
X				i += 2;
X				if (recfmt == FAB_dol_C_VFC) {
X					i += vfcsize;
X					reclen -= vfcsize;
X				}
X			} else if (reclen == fix
X					&& recatt == (1 << FAB_dol_V_FTN)) {
X					/****
X					if (buffer[i] == '0')
X						fputc('\n', f);
X					else if (buffer[i] == '1')
X						fputc('\f', f);
X					*** sow ***/
X					fputc(buffer[i],f); /** sow **/
X					i++;
X					reclen--;
X			} else {
X				fputc(buffer[i], f);
X				i++;
X				reclen--;
X			}
X			if (reclen == 0) {
X				fputc('\n', f);
X				if (i & 1)
X					i++;
X			}
X			break;
X
X		case FAB_dol_C_STM:
X		case FAB_dol_C_STMLF:
X			if (reclen < 0) {
X				printf("SCREAM\n");
X			}
X			if (reclen == 0) {
X				reclen = 512;
X			}
X			c = buffer[i++];
X			reclen--;
X			if (c == '\n') {
X				reclen = 0;
X			}
X			fputc(c, f);
X			break;
X
X		case FAB_dol_C_STMCR:
X			c = buffer[i++];
X			if (c == '\r')
X				fputc('\n', f);
X			else
X				fputc(c, f);
X			break;
X
X		default:
X			fclose(f);
X			unlink(filename);
X			fprintf(stderr, "Invalid record format = %d\n", recfmt);
X			return;
X		}
X	}
X	file_count += i;
X}
X#ifdef	SWAP
X/*
X *
X *  do swapping for Motorola type architectures
X *
X */
swap(from, to, nbytes)
char	*from, *to;
int	nbytes;
X{
X	int	i, j;
X	char	temp[100];
X
X	for (i = 0; i < nbytes; i++)
X		temp[i] = from[i];
X	for (i = 0, j = nbytes-1; i < nbytes; i++, j--)
X		to[i] = temp[j];
X}
X#endif
X/*
X *
X *  process a backup block
X *
X */
process_block(block, blocksize)
char	*block;
int	blocksize;
X{
X
X	unsigned short	bhsize, rsize, rtype;
X	unsigned long	bsize, i;
X
X	i = 0;
X
X	/* read the backup block header */
X	block_header = (struct bbh *) &block[i];
X	i += sizeof(struct bbh);
X
X	bhsize = block_header->bbh_dol_w_size;
X	bsize = block_header->bbh_dol_l_blocksize;
X#ifdef	SWAP
X	swap(&bhsize, &bhsize, sizeof(short));
X	swap(&bsize, &bsize, sizeof(long));
X#endif
X
X	/* check the validity of the header block */
X	if (bhsize != sizeof(struct bbh)) {
X		fprintf(stderr, "Snark: Invalid header block size\n");
X		exit(1);
X	}
X	if (bsize != 0 && bsize != blocksize) {
X		fprintf(stderr, "Snark: Invalid block size\n");
X		exit(1);
X	}
X#ifdef	DEBUG
X	printf("new block: i = %d, bsize = %d\n", i, bsize);
X#endif
X
X	/* read the records */
X	while (i < bsize) {
X		/* read the backup record header */
X		record_header = (struct brh *) &block[i];
X		i += sizeof(struct brh);
X
X		rtype = record_header->brh_dol_w_rtype;
X		rsize = record_header->brh_dol_w_rsize;
X#ifdef	SWAP
X		swap(&rtype, &rtype, sizeof(short));
X		swap(&rsize, &rsize, sizeof(short));
X#endif
X#ifdef	DEBUG
X		printf("rtype = %d\n", rtype);
X		printf("rsize = %d\n", rsize);
X		printf("flags = 0x%x\n", record_header->brh_dol_l_flags);
X		printf("addr = 0x%x\n", record_header->brh_dol_l_address);
X		printf("i = %d\n", i);
X#endif
X
X		switch (rtype) {
X
X		case brh_dol_k_null:
X#ifdef	DEBUG
X			printf("rtype = null\n");
X#endif
X			break;
X
X		case brh_dol_k_summary:
X#ifdef	DEBUG
X			printf("rtype = summary\n");
X#endif
X			break;
X
X		case brh_dol_k_file:
X#ifdef	DEBUG
X			printf("rtype = file\n");
X#endif
X			process_file(&block[i]);
X			break;
X
X		case brh_dol_k_vbn:
X#ifdef	DEBUG
X			printf("rtype = vbn\n");
X#endif
X			process_vbn(&block[i], rsize);
X			break;
X
X		case brh_dol_k_physvol:
X#ifdef	DEBUG
X			printf("rtype = physvol\n");
X#endif
X			break;
X
X		case brh_dol_k_lbn:
X#ifdef	DEBUG
X			printf("rtype = lbn\n");
X#endif
X			break;
X
X		case brh_dol_k_fid:
X#ifdef	DEBUG
X			printf("rtype = fid\n");
X#endif
X			break;
X
X		default:
X			fprintf(stderr, " Snark: invalid record type\n");
X			fprintf(stderr, " record type = %d\n", rtype);
X			exit(1);
X		}
X#ifdef pyr
X		i = i + rsize;
X#else
X		i += rsize;
X#endif
X	}
X}
X
rdhead()
X{
X	int i, nfound;
X	char name[80];
X	char Answer[80];	/* Changed---jrr 8/25/89. */
X	short Count;		/* Ditto  */
X
X	nfound = 1;
X	Count = 0;		/* Changed---jrr 8/25/89. */
X
X	if ( IsTape == YES ){
X	  /* read the tape label - 4 records of 80 bytes */
X	  while ((i = read(fd, label, LABEL_SIZE)) != 0) {
X	    if (i != LABEL_SIZE) {
X	      fprintf(stderr, "Snark: bad label record\n");
X	      exit(1);
X	    }
X	    if (strncmp(label, "VOL1",4) == 0) {
X	      sscanf(label+4, "%14s", name);
X	      if(vflag || tflag) printf("Volume: %s\n",name);
X	    }
X	    if (strncmp(label, "HDR1",4) == 0) {
X	      sscanf(label+4, "%14s", name);
X	      sscanf(label+31, "%4d", &setnr);
X	    }
X	    /* get the block size */
X	    if (strncmp(label, "HDR2", 4) == 0) {
X	      nfound = 0;
X	      sscanf(label+5, "%5d", &blocksize);
X#ifdef	DEBUG
X	      printf("blocksize = %d\n", blocksize);
X#endif
X	    }
X	  }
X	}
X	else {
X	  if ( Count <= 0 ) {
X	    ++Count;		/* Track the number of times through when */
X				/* dealing with regular files. */
X	    printf("Enter the file blocksize:[Default 32256] ");
X	    nfound = 0;
X	    gets( Answer );
X	    if ( *Answer != '\0' ) {
X	      sscanf( Answer, "%d", &blocksize );
X	    }
X	    else {
X	      blocksize = 32256;
X	    }
X	  }
X	if((vflag || tflag) && !nfound && (IsTape == YES)) 
X		printf("Saveset name: %s   number: %d\n",name,setnr);
X	/* get the block buffer */
X	block = (char *) malloc(blocksize);
X	if (block == (char *) 0) {
X		fprintf(stderr, "memory allocation for block failed\n");
X		exit(1);
X	}
X	return(nfound);
X}
X
rdtail()
X{
X	int i;
X	char name[80];
X	/* read the tape label - 4 records of 80 bytes */
X
X	if ( IsTape == YES ) {
X	  while ((i = read(fd, label, LABEL_SIZE)) != 0) {
X	    if (i != LABEL_SIZE) {
X	      fprintf(stderr, "Snark: bad label record\n");
X	      exit(1);
X	    }
X	    if (strncmp(label, "EOF1",4) == 0) {
X	      sscanf(label+4, "%14s", name);
X	      if(vflag || tflag)
X		printf("End of saveset: %s\n\n\n",name);
X	    }
X	  }
X	}
X	else {			/* Then it's a regular file */
X	  if(vflag || tflag)
X	    printf("End of saveset: %s\n\n\n",tapefile);
X	}
X}
X
usage(progname)
char	*progname;
X{
X	fprintf(stderr,
X	  "Usage:  %s -{tx}[cdevw][-s setnumber][-f tapefile]\n",progname);
X}
main(argc, argv)
int	argc;
char	*argv[];
X{
X	
X	char *progname;
X	int	c, i, eoffl;
X	int	selset;
X	extern int optind;
X	extern char *optarg;
X
X	progname = argv[0];
X	if(argc < 2){
X		usage(progname);
X		exit(1);
X	}
X	gargv = argv;
X	gargc = argc;
X	tapefile = def_tapefile;
X	cflag=dflag=eflag=sflag=tflag=vflag=wflag=xflag=0;
X	while((c=getopt(argc,argv,"cdef:s:tvwx")) != EOF)
X		switch(c){
X		case 'c':
X			cflag++;
X			break;
X		case 'd':
X			dflag++;
X			break;
X		case 'e':
X			eflag++;
X			break;
X		case 'f':
X			tapefile = optarg;
X			break;
X		case 's':
X			sflag++;
X			sscanf(optarg,"%d",&selset);
X			break;
X		case 't':
X			tflag++;
X			break;
X		case 'v':
X			vflag++;
X			break;
X		case 'w':
X			wflag++;
X			break;
X		case 'x':
X			xflag++;
X			break;
X		case '?':
X			usage(progname);
X			exit(1);
X			break;
X		};
X	if(!tflag && !xflag) {
X		usage(progname);
X		exit(1);
X	}
X	goptind = optind;
X
X#ifdef	NEWD
X	/* open debug file */
X	lf = fopen("log", "w");
X	if (lf == NULL) {
X		perror("log");
X		exit(1);
X	}
X#endif
X
X	/* open the tape file */
X	fd = open(tapefile, O_RDONLY);
X	if (fd < 0) {
X		perror(tapefile);
X		exit(1);
X	}
X
X	/* See if tapefile is a regular file */
X	if ( stat( tapefile, &stbuf ) == -1 ) {
X	       fprintf( stderr, "vmsbackup: can't access %s\n", tapefile );
X	       exit(99);
X	     }
X	if (( stbuf.st_mode & S_IFMT ) != S_IFREG ){ /* Then assume it's */
X						     /* a TAPE!!!        */
X	  IsTape = YES;
X	}
X
X	if ( IsTape == YES ){
X	  /* rewind the tape */
X	  op.mt_op = MTREW;
X	  op.mt_count = 1;
X	  i = ioctl(fd, MTIOCTOP, &op);
X	  if (i < 0) {
X	    perror(tapefile);
X	    exit(1);
X	  }
X	}
X
X	eoffl = rdhead();
X	/* read the backup tape blocks until end of tape */ 
X	while (!eoffl) {
X		if(sflag && setnr != selset) {
X			op.mt_op = MTFSF;
X			op.mt_count = 1;
X			i = ioctl(fd, MTIOCTOP, &op);
X			if (i < 0) {
X				perror(tapefile);
X				exit(1);
X			}
X			i = 0;
X		}
X		else
X			i = read(fd, block, blocksize);
X		if(i == 0) {
X			rdtail();
X			eoffl=rdhead();
X		}
X		else if (i != blocksize) {
X			fprintf(stderr, "bad block read i = %d\n", i);
X			exit(1);
X		}
X		else{
X			eoffl = 0;
X			process_block(block, blocksize);
X		}
X	}
X	if(vflag || tflag) printf("End of tape\n");
X
X	/* close the tape */
X	close(fd);
X
X#ifdef	NEWD
X	/* close debug file */
X	fclose(lf);
X#endif	NEWD
X
X	/* exit cleanly */
X	exit(0);
X}
END_OF_FILE
if test 19671 -ne `wc -c <'vmsbackup.c~'`; then
    echo shar: \"'vmsbackup.c~'\" unpacked with wrong size!
fi
# end of 'vmsbackup.c~'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

