From: CSBVAX::MRGATE!montnaro@sprite@SMTP 20-MAR-1987 23:18
To: MODSRC

Received: by sprite.steinmetz (3.2/1.1x Steinmetz)
	id AA07161; Fri, 20 Mar 87 22:55:53 EST
Date: Fri, 20 Mar 87 22:55:53 EST
From: Skip Montanaro <montnaro%sprite.tcpip@csbvax>
Posted-Date: Fri, 20 Mar 87 22:55:53 EST
Message-Id: <[email-masked]>
To: [email-masked]
Subject: read-vms-backs

Path: seismo!columbia!rutgers!mit-eddie!mirror!sources-request
From: [email-masked]
Newsgroups: mod.sources
Subject: v07i099:  Read VMS backup tapes
Message-ID: <[email-masked]>
Date: 20 Jan 87 16:20:42 GMT
Sender: [email-masked]
Lines: 1394
Approved: mirror!rs

Submitted by: seismo!enea!luthcad!sow (Sven-Ove Westberg)
Mod.sources: Volume 7, Issue 99
Archive-name: read-vms-backs

This program reads a VMS backup tape, it can use the rmtlib for remote tape
access.
	Sven-Ove Westberg

[  I do not have any VMS backup tapes with which to test this.  --r$  ]

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	vmsbackup.1
#	Makefile
#	vmsbackup.c
#	match.c
# This archive created: Sun Dec  7 19:56:28 1986
echo shar: "extracting 'README'" '(575 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^	X//' << \SHAR_EOF > 'README'
	XThis progam reads a VMS backuptape.
	X
	XThe tape program is orginally written by John Douglas Carey and
	Xthe pattern matching routine by some unknown on the net. 
	X
	XThe remote tape option use the rmtlib from mod.sources.
	X
	XA good way to archive remotetape access for users with only
	Xa local account is to create a "netwide" user tar and let
	Xthe remote tape programs do suid to user tar.
	X
	XThe program is tested on vax and sun.
	X
	X
	XSven-Ove Westberg 
	XLulea University of Technology
	XS-951 87  Lulea,  Sweden
	XUUCP:  [email-masked]
	XUUCP:  {decvax,philabs,seismo}!mcvax!enea!luthcad!sow
SHAR_EOF
if test 575 -ne "`wc -c < 'README'`"
then
	echo shar: "error transmitting 'README'" '(should have been 575 characters)'
fi
fi
echo shar: "extracting 'vmsbackup.1'" '(3444 characters)'
if test -f 'vmsbackup.1'
then
	echo shar: "will not over-write existing file 'vmsbackup.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmsbackup.1'
	X.TH VMSBACKUP 1 
	X.SH NAME
	Xvmsbackup \- read a VMS backup tape
	X.SH SYNOPSIS
	X.B vmsbackup
	X.B \-{tx}[cdevw][s setnumber][f tapefile] 
	X[ name ... ]
	X.SH DESCRIPTION
	X.I vmsbackup 
	Xreads a VMS generated backup tape, converting the files
	Xto Unix format and writing the files to disc.
	XThe default operation of the program is to go through an entire
	Xtape, extracting every file and writing it to disc.
	XThis may be modified by the following options.
	X.TP 8
	X.B c
	XUse complete filenames, including the version number.
	XA colon and the octal version number will be appended to all filenames.
	XA colon, rather than a semicolon, is used since the Unix Shell
	Xuses the semicolon as the line separator.
	XUsing a colon prevents the user from having to escape the semicolon
	Xwhen referencing the filename.
	XThis option is useful only when multiple versions of the same file
	Xare on a single tape or when a file of the same name already
	Xexists in the destination directory.
	XThe default is to ignore version numbers.
	X.TP 8
	X.B d
	Xuse the directory structure from VMS, the default value is off.
	X.TP 8
	X.B e
	XProcess all filename extensions.
	XSince this program is mainly intended to move source code and possibly
	Xdata from a DEC system to a Unix system, the default is to ignore
	Xall files whose filename extension specifies system dependent data.
	XThe file types which will be ignored, unless the
	X.B e
	Xoption is specified, are
	X.IP "" 10
	Xexe     VMS executable file
	X.br
	Xlib     VMS object library file
	X.br
	Xobj     RSX object file
	X.br
	Xodl     RSX overlay description file
	X.br
	Xolb     RSX object library file
	X.br
	Xpmd     RSX post mortem dump file
	X.br
	Xstb     RSX task symbol table file
	X.br
	Xsys     RSX bootable system file
	X.br
	Xtsk     RSX executable task file
	X.PP
	X.TP 8
	X.B f
	XUse the next argument in the command line as the tape device to
	Xbe used, rather than the default.
	X.sp
	XIf vmsbackup is compiled with the remote tape option
	Xand the file name has the form
	X.IR system [. user ]:/dev/???
	X.I vmsbackup
	Xwill use the tape drive /dev/??? on the remote system
	X.IR system ,
	Xvia
	X.IR rsh (1),
	Xand
	X.IR rmt (8).
	XThe optional
	X.I user
	Xportion of the pathname specifies the login name to use on the
	Xremote system.
	XIf it is not supplied, the current user's login name will be used.
	XIn all the cases, the user must have the appropriate
	Xpermissions on the remote machine, in order to use this facility.
	XThe default is
	X.I /dev/rmt8
	X(drive 0, raw mode, 1600 bpi).
	XThis must be a raw mode tape device.
	X.TP 8
	X.B s saveset
	XProcess only the given saveset number.
	X.TP 8
	X.B t
	XProduce a table of contents (a directory listing) on the standard output
	Xof the files on tape.
	X.TP 8
	X.B v
	XVerbose output.
	XNormally
	X.I vmsbackup 
	Xdoes its work silently.
	XThe verbose option will cause the filenames of the files being read from
	Xtape to disk to be output on the standard output.
	X.TP 8
	X.B w
	X.I vmsbackup
	Xprints the action to be taken followed by file name, then
	Xwait for user confirmation. If a word beginning with `y'
	Xis given, the action is done. Any other input means don't do it.
	X.TP 8
	X.B x
	Xextract the named files from the tape.
	X.TP 8
	XThe optional 
	X.I name
	Xargument specifies one or more filenames to be
	Xsearched for specifically on the tape and only those files are to be processed.
	XThe name may contain the usal sh(1) meta-characters *?![] \nnn.
	X.SH FILES
	X/dev/rmt\fIx\fP
	X.SH SEE ALSO
	Xrmtops(3)
	X.SH BUGS
	XThe filename match uses the complete VMS file names.
	X
	X.SH AUTHOR
	XJohn Douglas Carey
	X.br
	XSven-Ove Westberg
SHAR_EOF
if test 3444 -ne "`wc -c < 'vmsbackup.1'`"
then
	echo shar: "error transmitting 'vmsbackup.1'" '(should have been 3444 characters)'
fi
fi
echo shar: "extracting 'Makefile'" '(605 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
sed 's/^	X//' << \SHAR_EOF > 'Makefile'
	X#
	X#
	XREMOTE=-DREMOTE			# -DREMOTE  use remote tape
	XSWAP=				# -DSWAP swap bytes
	XCFLAGS= $(SWAP) $(REMOTE)
	XLFLAGS=
	XLIBS= -lrmt   			# remote magtape library
	XOWNER=tar			# user for remote tape access
	XMODE=4755
	XBINDIR=/usr/local/bin
	XMANSEC=l
	XMANDIR=/usr/man/man$(MANSEC)
	X
	X#
	Xvmsbackup: vmsbackup.o getopt.o match.o
	X	cc $(LFLAGS) -o vmsbackup vmsbackup.o match.o getopt.o $(LIBS)
	Xinstall:
	X	install -m $(MODE) -o $(OWNER) -s vmsbackup $(BINDIR)	
	X	cp vmsbackup.1 $(MANDIR)/vmsbackup.$(MANSEC)
	Xclean:
	X	rm -f vmsbackup *.o core
	Xshar:
	X	shar -a README vmsbackup.1 Makefile vmsbackup.c match.c \
	X	    > vmsbackup.shar
SHAR_EOF
if test 605 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 605 characters)'
fi
fi
echo shar: "extracting 'vmsbackup.c'" '(16594 characters)'
if test -f 'vmsbackup.c'
then
	echo shar: "will not over-write existing file 'vmsbackup.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmsbackup.c'
	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 *	[email-masked]
	X *	[email-masked]
	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#ifdef REMOTE
	X#include	<local/rmt.h>
	X#include	<sys/stat.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
	Xstruct bbh {
	X	short	bbh_dol_w_size;
	X	short	bbh_dol_w_opsys;
	X	short	bbh_dol_w_subsys;
	X	short	bbh_dol_w_applic;
	X	long	bbh_dol_l_number;
	X	char	bbh_dol_t_spare_1[20];
	X	short	bbh_dol_w_struclev;
	X	short	bbh_dol_w_volnum;
	X	long	bbh_dol_l_crc;
	X	long	bbh_dol_l_blocksize;
	X	long	bbh_dol_l_flags;
	X	char	bbh_dol_t_ssname[32];
	X	short	bbh_dol_w_fid[3];
	X	short	bbh_dol_w_did[3];
	X	char	bbh_dol_t_filename[128];
	X	char	bbh_dol_b_rtype;
	X	char	bbh_dol_b_rattrib;
	X	short	bbh_dol_w_rsize;
	X	char	bbh_dol_b_bktsize;
	X	char	bbh_dol_b_vfcsize;
	X	short	bbh_dol_w_maxrec;
	X	long	bbh_dol_l_filesize;
	X	char	bbh_dol_t_spare_2[22];
	X	short	bbh_dol_w_checksum;
	X} *block_header;
	X
	Xstruct brh {
	X	short	brh_dol_w_rsize;
	X	short	brh_dol_w_rtype;
	X	long	brh_dol_l_flags;
	X	long	brh_dol_l_address;
	X	long	brh_dol_l_spare;
	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
	Xstruct 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
	Xchar	*def_tapefile = "/dev/rts8";
	X#else
	Xchar	*def_tapefile = "/dev/rmt8";
	X#endif
	Xchar	*tapefile;
	X
	Xchar	filename[128];
	Xint	filesize;
	X
	Xchar	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
	Xchar	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
	Xstatic struct	bsa	*file_table[FANO];
	X#else
	Xstruct	bsa	*file_table[FANO];
	X#endif
	X
	XFILE	*f	= NULL;
	Xint	file_count;
	Xshort	reclen;
	Xshort	fix;
	Xshort	recsize;
	Xint	vfcsize;
	X
	X#ifdef	NEWD
	XFILE	*lf;
	X#endif	NEWD
	X
	Xint	fd;		/* tape file descriptor */
	Xint 	cflag, dflag, eflag, sflag, tflag, vflag, wflag, xflag;
	Xint	setnr;
	Xchar	**gargv;
	Xint 	goptind, gargc;
	X
	X#define	LABEL_SIZE	80
	Xchar	label[LABEL_SIZE];
	X
	Xchar	*block;
	Xint	blocksize;
	X
	Xstruct	mtop	op;
	X
	XFILE *
	Xopenfile(fn)
	Xchar	*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
	Xtypecmp(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. */
	Xregister 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
	Xprocess_file(buffer)
	Xchar	*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 */
	Xprocess_vbn(buffer, rsize)
	Xchar		*buffer;
	Xunsigned 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 */
	Xswap(from, to, nbytes)
	Xchar	*from, *to;
	Xint	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 */
	Xprocess_block(block, blocksize)
	Xchar	*block;
	Xint	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
	Xrdhead()
	X{
	X	int i, nfound;
	X	char name[80];
	X	nfound = 1;
	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	if((vflag || tflag) && !nfound) 
	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
	Xrdtail()
	X{
	X	int i;
	X	char name[80];
	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, "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
	Xusage(progname)
	Xchar	*progname;
	X{
	X	fprintf(stderr,
	X	  "Usage:  %s -{tx}[cdevw][-s setnumber][-f tapefile]\n",progname);
	X}
	Xmain(argc, argv)
	Xint	argc;
	Xchar	*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	/* 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	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}
SHAR_EOF
if test 16594 -ne "`wc -c < 'vmsbackup.c'`"
then
	echo shar: "error transmitting 'vmsbackup.c'" '(should have been 16594 characters)'
fi
fi
echo shar: "extracting 'match.c'" '(6723 characters)'
if test -f 'match.c'
then
	echo shar: "will not over-write existing file 'match.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'match.c'
	X#include <stdio.h>
	X#include <sys/types.h>
	X
	X#define ASTERISK '*'		/* The '*' metacharacter */
	X#define QUESTION '?'		/* The '?' metacharacter */
	X#define LEFT_BRACKET '['	/* The '[' metacharacter */
	X#define RIGHT_BRACKET ']'	/* The ']' metacharacter */
	X
	X#define IS_OCTAL(ch) (ch >= '0' && ch <= '7')
	X
	Xtypedef int BOOLEAN;
	X#define VOID void
	X#define TRUE 1
	X#define FALSE 0
	X#define EOS '\000'
	X
	Xstatic BOOLEAN do_list ();
	Xstatic char nextch ();
	Xstatic VOID list_parse ();
	X
	X
	X/*
	X *  FUNCTION
	X *
	X *	match	test string for wildcard match
	X *
	X *  SYNOPSIS
	X *
	X *	BOOLEAN match (string, pattern)
	X *	register char *string;
	X *	register char *pattern;
	X *
	X *  DESCRIPTION
	X *
	X *	Test string for match using pattern.  The pattern may
	X *	contain the normal shell metacharacters for pattern
	X *	matching.  The '*' character matches any string,
	X *	including the null string.  The '?' character matches
	X *	any single character.  A list of characters enclosed
	X *	in '[' and ']' matches any character in the list.
	X *	If the first character following the beginning '['
	X *	is a '!' then any character not in the list is matched.
	X *
	X */
	X
	X
	X/*
	X *  PSEUDO CODE
	X *
	X *	Begin match
	X *	    Switch on type of pattern character
	X *		Case ASTERISK:
	X *		    Attempt to match asterisk
	X *		    Break
	X *		Case QUESTION MARK:
	X *		    Attempt to match question mark
	X *		    Break
	X *		Case EOS:
	X *		    Match is result of EOS on string test
	X *		    Break
	X *		Case default:
	X *		    If explicit match then
	X *			Match is result of submatch
	X *		    Else
	X *			Match is FALSE
	X *		    End if
	X *		    Break
	X *	    End switch
	X *	    Return result of match test
	X *	End match
	X *
	X */
	X
	XBOOLEAN match (string, pattern)
	Xregister char *string;
	Xregister char *pattern;
	X{
	X    register BOOLEAN ismatch;
	X
	X    ismatch = FALSE;
	X    switch (*pattern) {
	X	case ASTERISK:
	X	    pattern++;
	X	    do {
	X		ismatch = match (string, pattern);
	X	    } while (!ismatch && *string++ != EOS);
	X	    break;
	X	case QUESTION:
	X	    if (*string != EOS) {
	X		ismatch = match (++string, ++pattern);
	X	    }
	X	    break;
	X	case EOS:
	X	    if (*string == EOS) {
	X		ismatch = TRUE;
	X	    }
	X	    break;
	X	case LEFT_BRACKET:
	X	    if (*string != EOS) {
	X		ismatch = do_list (string, pattern);
	X	    }
	X	    break;
	X	default:
	X	    if (*string++ == *pattern++) {
	X		ismatch = match (string, pattern);
	X	    } else {
	X		ismatch = FALSE;
	X	    }
	X	    break;
	X    }
	X    return (ismatch);
	X}
	X
	X
	X/*
	X *  FUNCTION
	X *
	X *	do_list    process a list and following substring
	X *
	X *  SYNOPSIS
	X *
	X *	static BOOLEAN do_list (string, pattern)
	X *	register char *string;
	X *	register char *pattern;
	X *
	X *  DESCRIPTION
	X *
	X *	Called when a list is found in the pattern.  Returns
	X *	TRUE if the current character matches the list and
	X *	the remaining substring matches the remaining pattern.
	X *
	X *	Returns FALSE if either the current character fails to
	X *	match the list or the list matches but the remaining
	X *	substring and subpattern's don't.
	X *
	X *  RESTRICTIONS
	X *
	X *	The mechanism used to match characters in an inclusive
	X *	pair (I.E. [a-d]) may not be portable to machines
	X *	in which the native character set is not ASCII.
	X *
	X *	The rules implemented here are:
	X *
	X *		(1)	The backslash character may be
	X *			used to quote any special character.
	X *			I.E.  "\]" and "\-" anywhere in list,
	X *			or "\!" at start of list.
	X *
	X *		(2)	The sequence \nnn becomes the character
	X *			given by nnn (in octal).
	X *
	X *		(3)	Any non-escaped ']' marks the end of list.
	X *
	X *		(4)	A list beginning with the special character
	X *			'!' matches any character NOT in list.
	X *			The '!' character is only special if it
	X *			is the first character in the list.
	X *
	X */
	X
	X
	X/*
	X *  PSEUDO CODE
	X *
	X *	Begin do_list
	X *	    Default result is no match
	X *	    Skip over the opening left bracket
	X *	    If the next pattern character is a '!' then
	X *		List match gives FALSE
	X *		Skip over the '!' character
	X *	    Else
	X *		List match gives TRUE
	X *	    End if
	X *	    While not at closing bracket or EOS
	X *		Get lower and upper bounds
	X *		If character in bounds then
	X *		    Result is same as sense flag.
	X *		    Skip over rest of list
	X *		End if
	X *	    End while
	X *	    If match found then
	X *		If not at end of pattern then
	X *		    Call match with rest of pattern
	X *		End if
	X *	    End if
	X *	    Return match result
	X *	End do_list
	X *
	X */
	X
	Xstatic BOOLEAN do_list (string, pattern)
	Xregister char *string;
	Xchar *pattern;
	X{
	X    register BOOLEAN ismatch;
	X    register BOOLEAN if_found;
	X    register BOOLEAN if_not_found;
	X    auto char lower;
	X    auto char upper;
	X
	X    pattern++;
	X    if (*pattern == '!') {
	X	if_found = FALSE;
	X	if_not_found = TRUE;
	X	pattern++;
	X    } else {
	X	if_found = TRUE;
	X	if_not_found = FALSE;
	X    }
	X    ismatch = if_not_found;
	X    while (*pattern != ']' && *pattern != EOS) {
	X	list_parse (&pattern, &lower, &upper);
	X	if (*string >= lower && *string <= upper) {
	X	    ismatch = if_found;
	X	    while (*pattern != ']' && *pattern != EOS) {pattern++;}
	X	}
	X    }
	X    if (*pattern++ != ']') {
	X	fprintf (stderr, "warning - character class error\n");
	X    } else {
	X	if (ismatch) {
	X	    ismatch = match (++string, pattern);
	X	}
	X    }
	X    return (ismatch);
	X}
	X
	X
	X/*
	X *  FUNCTION
	X *
	X *	list_parse    parse part of list into lower and upper bounds
	X *
	X *  SYNOPSIS
	X *
	X *	static VOID list_parse (patp, lowp, highp)
	X *	char **patp;
	X *	char *lowp;
	X *	char *highp;
	X *
	X *  DESCRIPTION
	X *
	X *	Given pointer to a pattern pointer (patp), pointer to
	X *	a place to store lower bound (lowp), and pointer to a
	X *	place to store upper bound (highp), parses part of
	X *	the list, updating the pattern pointer in the process.
	X *
	X *	For list characters which are not part of a range,
	X *	the lower and upper bounds are set to that character.
	X *
	X */
	X
	Xstatic VOID list_parse (patp, lowp, highp)
	Xchar **patp;
	Xchar *lowp;
	Xchar *highp;
	X{
	X    *lowp = nextch (patp);
	X    if (**patp == '-') {
	X	(*patp)++;
	X	*highp = nextch (patp);
	X    } else {
	X	*highp = *lowp;
	X    }
	X}
	X
	X
	X/*
	X *  FUNCTION
	X *
	X *	nextch	  determine next character in a pattern
	X *
	X *  SYNOPSIS
	X *
	X *	static char nextch (patp)
	X *	char **patp;
	X *
	X *  DESCRIPTION
	X *
	X *	Given pointer to a pointer to a pattern, uses the pattern
	X *	pointer to determine the next character in the pattern,
	X *	subject to translation of backslash-char and backslash-octal
	X *	sequences.
	X *
	X *	The character pointer is updated to point at the next pattern
	X *	character to be processed.
	X *
	X */
	X
	Xstatic char nextch (patp)
	Xchar **patp;
	X{
	X    register char ch;
	X    register char chsum;
	X    register int count;
	X
	X    ch = *(*patp)++;
	X    if (ch == '\\') {
	X	ch = *(*patp)++;
	X	if (IS_OCTAL (ch)) {
	X	    chsum = 0;
	X	    for (count = 0; count < 3 && IS_OCTAL (ch); count++) {
	X		chsum *= 8;
	X		chsum += ch - '0';
	X		ch = *(*patp)++;
	X	    }
	X	    (*patp)--;
	X	    ch = chsum;
	X	}
	X    }
	X    return (ch);
	X}
SHAR_EOF
if test 6723 -ne "`wc -c < 'match.c'`"
then
	echo shar: "error transmitting 'match.c'" '(should have been 6723 characters)'
fi
fi
exit 0
#	End of shell archive