From:	CSBVAX::CSBVAX::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 18-JAN-1989 15:50
To:	MRGATE::"ARISIA::EVERHART"
Subj:	Re: VmsBackup Package


Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Tue, 17 JAN 89 17:26:04 PDT
Received: from CUNYVM.CUNY.EDU by KL.SRI.COM with TCP; Tue, 17 Jan 89 17:17:26 PST
Received: from gallua.bitnet by CUNYVM.CUNY.EDU (IBM VM SMTP R1.1) with BSMTP id 2533; Tue, 17 Jan 89 20:16:34 EST
Date: Tue, 17 Jan 89 20:06 EST
From: Timothy Stark <11TSTARK%GALLUA.BITNET@CUNYVM.CUNY.EDU>
Subject: Re: VmsBackup Package
To: info-vax@kl.sri.COM
X-VMS-To: IN%"info-vax@kl.sri.com"

Hello VAX/Ultrix users:

    I get many mails about unpacking problems. I decided to repost it
in shar form instead compress/tar form. You should be have no problems
with this. I had added b option to vmsbackup.1. have a nice day!

-- Tim Stark

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#       Run the following text with /bin/sh to create:
#       :AUTHOR
#       Makefile
#       README
#       getopt.1
#       getopt.c
#       match.c
#       vmsbackup.1
#       vmsbackup.c
# This archive created: Tue Jan 17 14:20:41 1989
cat << \SHAR_EOF > :AUTHOR
Path: plus5!wucs1!wucs2!sw1e!pyuxww!gamma!ulysses!cbosgd!mirror!sources-request
From: sources-request@mirror.UUCP
Newsgroups: mod.sources
Subject: v07i099:  Read VMS backup tapes
Message-ID: <1319@mirror.TMC.COM>
Date: 20 Jan 87 16:20:42 GMT
Sender: rs@mirror.TMC.COM
Lines: 1394
Approved: mirror!rs
Posted: Tue Jan 20 11:20:42 1987

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$  ]

SHAR_EOF
if test 631 -ne "`wc -c :AUTHOR`"
then
echo shar: error transmitting :AUTHOR '(should have been 631 characters)'
fi
cat << \SHAR_EOF > Makefile
#
#
REMOTE=                 # -DREMOTE  use remote tape
SWAP=                           # -DSWAP swap bytes
CFLAGS= $(SWAP) $(REMOTE)
LFLAGS=
LIBS=                           # remote magtape library
OWNER=tar                       # user for remote tape access
MODE=4755
BINDIR=/usr/local/bin
MANSEC=l
MANDIR=/usr/man/man$(MANSEC)
CC = cc
#
vmsbackup: vmsbackup.o match.o
        $(CC) $(LFLAGS) -o vmsbackup vmsbackup.o match.o $(LIBS)
install:
        install -m $(MODE) -o $(OWNER) -s vmsbackup $(BINDIR)
        cp vmsbackup.1 $(MANDIR)/vmsbackup.$(MANSEC)
clean:
        rm -f vmsbackup *.o core
shar:
        shar -a README vmsbackup.1 Makefile vmsbackup.c match.c \
            > vmsbackup.shar
SHAR_EOF
if test 583 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 583 characters)'
fi
cat << \SHAR_EOF > README
This progam reads a VMS backuptape.

The tape program is orginally written by John Douglas Carey and
the pattern matching routine by some unknown on the net.

The remote tape option use the rmtlib from mod.sources.

A good way to archive remotetape access for users with only
a local account is to create a "netwide" user tar and let
the remote tape programs do suid to user tar.

The program is tested on vax and sun.


Sven-Ove Westberg
Lulea University of Technology
S-951 87  Lulea,  Sweden
UUCP:  sow@luthcad.UUCP
UUCP:  {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
cat << \SHAR_EOF > getopt.1
.TH GETOPT 1 LOCAL
.SH NAME
getopt \- format flags for shell scripts
.SH SYNOPSIS
.B getopt
flag_spec argument ...
.SH DESCRIPTION
.I Getopt
is a program intended to be called by scripts to ``canonicalize'' their
arguments before processing them, just as the
.IR getopt (3)
routine does for C programs.
(This need for scripts is usually most noticeable in the way
.IR lint (1)
handles the
.B \-n
flag.)
.PP
The following two examples provide the initial parsing for a script
which takes two flags,
.B \-a
and
.BR \-b ,
the second of which takes an argument.
.RS
.ta +4n +4n +4n +4n
.nf
# For /bin/csh...
set argv = (`getopt "ab:" $*`)
if ( $status ) then
        echo "Read the documentation and try again." >/dev/tty
        exit 1
endif
set Aflag=0
set Name=NONE
while "$1" != "--"
        switch ("$1")
                case '-a':
                        set Aflag=1
                        breaksw
                case '-b':
                        shift
                        set Name=$1
                        breaksw
        endsw
        shift
end
shift
echo Aflag=$Aflag / Name=$Name / Remaining args are $*

# For /bin/sh...
set -- `getopt "d:s" $@`
if test $? != 0 ; then
        echo "Read the documentation and try again."
        exit 1
fi
Aflag=0
Name=NONE
for f
do
        case "$f" in
                -a)     Aflag=1
                        ;;
                -b)     shift
                        Name=$2
                        ;;
                --)     break
                        ;;
        esac
        shift
done
shift
echo Aflag=$Aflag / Name=$Name / Remaining args are $*
.fi
.RE
.SH DIAGNOSTICS
The program burps the standard
.IR getopt (3)
diagnostics to standard error, and exits with a non-zero status if an
error occurs.
It is arguable wrong that the diagnostics imply that the program
is named ``getopt'' rather than the real name of the script.
It is undeniably AT&T\-compatible to do this, however.
.SH "SEE ALSO"
csh(1), sh(1), getopt(3)
.SH AUTHOR
.nf
Rich $alz
Mirror Systems
(mirror!rs, rs@mirror.TMC.COM)
SHAR_EOF
if test 1707 -ne "`wc -c getopt.1`"
then
echo shar: error transmitting getopt.1 '(should have been 1707 characters)'
fi
cat << \SHAR_EOF > getopt.c
/*
**  GETOPT PROGRAM AND LIBRARY ROUTINE
**
**  I wrote main() and AT&T wrote getopt() and we both put our efforts into
**  the public domain via mod.sources.
**      Rich $alz
**      Mirror Systems
**      (mirror!rs, rs@mirror.TMC.COM)
**      August 10, 1986
*/

#include <stdio.h>


#ifndef INDEX
#define INDEX index
#endif


extern char     *INDEX();
extern int       optind;
extern char     *optarg;


main(ac, av)
    register int         ac;
    register char       *av[];
{
    register char       *flags;
    register int         c;

    /* Check usage. */
    if (ac < 2) {
        fprintf(stderr, "usage: %s flag-specification arg-list\n", av[0]);
        exit(2);
    }

    /* Play games; remember the flags (first argument), then splice
       them out so it looks like a "standard" command vector. */
    flags = av[1];
    av[1] = av[0];
    av++;
    ac--;

    /* Print flags. */
    while ((c = getopt(ac, av, flags)) != EOF) {
        if (c == '?')
            exit(1);
        /* We assume that shells collapse multiple spaces in `` expansion. */
        printf("-%c %s ", c, INDEX(flags, c)[1] == ':' ? optarg : "");
    }

    /* End of flags; print rest of options. */
    printf("-- ");
    for (av += optind; *av; av++)
        printf("%s ", *av);
    exit(0);
}









/*
**  This is the public-domain AT&T getopt(3) code.  I added the
**  #ifndef stuff because I include <stdio.h> for the program;
**  getopt, per se, doesn't need it.  I also added the INDEX/index
**  hack (the original used strchr, of course).  And, note that
**  technically the casts in the write(2) calls shouldn't be there.
*/

#ifndef NULL
#define NULL    0
#endif
#ifndef EOF
#define EOF     (-1)
#endif
#ifndef INDEX
#define INDEX index
#endif


#define ERR(s, c)       if(opterr){\
        extern int strlen(), write();\
        char errbuf[2];\
        errbuf[0] = c; errbuf[1] = '\n';\
        (void) write(2, argv[0], (unsigned)strlen(argv[0]));\
        (void) write(2, s, (unsigned)strlen(s));\
        (void) write(2, errbuf, 2);}

extern int strcmp();
extern char *INDEX();

int     opterr = 1;
int     optind = 1;
int     optopt;
char    *optarg;

int
getopt(argc, argv, opts)
int     argc;
char    **argv, *opts;
{
        static int sp = 1;
        register int c;
        register char *cp;

        if(sp == 1)
                if(optind >= argc ||
                   argv[optind][0] != '-' || argv[optind][1] == '\0')
                        return(EOF);
                else if(strcmp(argv[optind], "--") == NULL) {
                        optind++;
                        return(EOF);
                }
        optopt = c = argv[optind][sp];
        if(c == ':' || (cp=INDEX(opts, c)) == NULL) {
                ERR(": illegal option -- ", c);
                if(argv[optind][++sp] == '\0') {
                        optind++;
                        sp = 1;
                }
                return('?');
        }
        if(*++cp == ':') {
                if(argv[optind][sp+1] != '\0')
                        optarg = &argv[optind++][sp+1];
                else if(++optind >= argc) {
                        ERR(": option requires an argument -- ", c);
                        sp = 1;
                        return('?');
                } else
                        optarg = argv[optind++];
                sp = 1;
        } else {
                if(argv[optind][++sp] == '\0') {
                        sp = 1;
                        optind++;
                }
                optarg = NULL;
        }
        return(c);
}
SHAR_EOF
if test 2832 -ne "`wc -c getopt.c`"
then
echo shar: error transmitting getopt.c '(should have been 2832 characters)'
fi
cat << \SHAR_EOF > match.c
#include <stdio.h>
#include <sys/types.h>

#define ASTERISK '*'            /* The '*' metacharacter */
#define QUESTION '?'            /* The '?' metacharacter */
#define LEFT_BRACKET '['        /* The '[' metacharacter */
#define RIGHT_BRACKET ']'       /* The ']' metacharacter */

#define IS_OCTAL(ch) (ch >= '0' && ch <= '7')

typedef int BOOLEAN;
#define VOID void
#define TRUE 1
#define FALSE 0
#define EOS '\000'

static BOOLEAN do_list ();
static char nextch ();
static VOID list_parse ();









/*
 *  FUNCTION
 *
 *      match   test string for wildcard match
 *
 *  SYNOPSIS
 *
 *      BOOLEAN match (string, pattern)
 *      register char *string;
 *      register char *pattern;
 *
 *  DESCRIPTION
 *
 *      Test string for match using pattern.  The pattern may
 *      contain the normal shell metacharacters for pattern
 *      matching.  The '*' character matches any string,
 *      including the null string.  The '?' character matches
 *      any single character.  A list of characters enclosed
 *      in '[' and ']' matches any character in the list.
 *      If the first character following the beginning '['
 *      is a '!' then any character not in the list is matched.
 *
 */









/*
 *  PSEUDO CODE
 *
 *      Begin match
 *          Switch on type of pattern character
 *              Case ASTERISK:
 *                  Attempt to match asterisk
 *                  Break
 *              Case QUESTION MARK:
 *                  Attempt to match question mark
 *                  Break
 *              Case EOS:
 *                  Match is result of EOS on string test
 *                  Break
 *              Case default:
 *                  If explicit match then
 *                      Match is result of submatch
 *                  Else
 *                      Match is FALSE
 *                  End if
 *                  Break
 *          End switch
 *          Return result of match test
 *      End match
 *
 */

BOOLEAN match (string, pattern)
register char *string;
register char *pattern;
{
    register BOOLEAN ismatch;

    ismatch = FALSE;
    switch (*pattern) {
        case ASTERISK:
            pattern++;
            do {
                ismatch = match (string, pattern);
            } while (!ismatch && *string++ != EOS);
            break;
        case QUESTION:
            if (*string != EOS) {
                ismatch = match (++string, ++pattern);
            }
            break;
        case EOS:
            if (*string == EOS) {
                ismatch = TRUE;
            }
            break;
        case LEFT_BRACKET:
            if (*string != EOS) {
                ismatch = do_list (string, pattern);
            }
            break;
        default:
            if (*string++ == *pattern++) {
                ismatch = match (string, pattern);
            } else {
                ismatch = FALSE;
            }
            break;
    }
    return (ismatch);
}









/*
 *  FUNCTION
 *
 *      do_list    process a list and following substring
 *
 *  SYNOPSIS
 *
 *      static BOOLEAN do_list (string, pattern)
 *      register char *string;
 *      register char *pattern;
 *
 *  DESCRIPTION
 *
 *      Called when a list is found in the pattern.  Returns
 *      TRUE if the current character matches the list and
 *      the remaining substring matches the remaining pattern.
 *
 *      Returns FALSE if either the current character fails to
 *      match the list or the list matches but the remaining
 *      substring and subpattern's don't.
 *
 *  RESTRICTIONS
 *
 *      The mechanism used to match characters in an inclusive
 *      pair (I.E. [a-d]) may not be portable to machines
 *      in which the native character set is not ASCII.
 *
 *      The rules implemented here are:
 *
 *              (1)     The backslash character may be
 *                      used to quote any special character.
 *                      I.E.  "\]" and "\-" anywhere in list,
 *                      or "\!" at start of list.
 *
 *              (2)     The sequence \nnn becomes the character
 *                      given by nnn (in octal).
 *
 *              (3)     Any non-escaped ']' marks the end of list.
 *
 *              (4)     A list beginning with the special character
 *                      '!' matches any character NOT in list.
 *                      The '!' character is only special if it
 *                      is the first character in the list.
 *
 */









/*
 *  PSEUDO CODE
 *
 *      Begin do_list
 *          Default result is no match
 *          Skip over the opening left bracket
 *          If the next pattern character is a '!' then
 *              List match gives FALSE
 *              Skip over the '!' character
 *          Else
 *              List match gives TRUE
 *          End if
 *          While not at closing bracket or EOS
 *              Get lower and upper bounds
 *              If character in bounds then
 *                  Result is same as sense flag.
 *                  Skip over rest of list
 *              End if
 *          End while
 *          If match found then
 *              If not at end of pattern then
 *                  Call match with rest of pattern
 *              End if
 *          End if
 *          Return match result
 *      End do_list
 *
 */

static BOOLEAN do_list (string, pattern)
register char *string;
char *pattern;
{
    register BOOLEAN ismatch;
    register BOOLEAN if_found;
    register BOOLEAN if_not_found;
    auto char lower;
    auto char upper;

    pattern++;
    if (*pattern == '!') {
        if_found = FALSE;
        if_not_found = TRUE;
        pattern++;
    } else {
        if_found = TRUE;
        if_not_found = FALSE;
    }
    ismatch = if_not_found;
    while (*pattern != ']' && *pattern != EOS) {
        list_parse (&pattern, &lower, &upper);
        if (*string >= lower && *string <= upper) {
            ismatch = if_found;
            while (*pattern != ']' && *pattern != EOS) {pattern++;}
        }
    }
    if (*pattern++ != ']') {
        fprintf (stderr, "warning - character class error\n");
    } else {
        if (ismatch) {
            ismatch = match (++string, pattern);
        }
    }
    return (ismatch);
}









/*
 *  FUNCTION
 *
 *      list_parse    parse part of list into lower and upper bounds
 *
 *  SYNOPSIS
 *
 *      static VOID list_parse (patp, lowp, highp)
 *      char **patp;
 *      char *lowp;
 *      char *highp;
 *
 *  DESCRIPTION
 *
 *      Given pointer to a pattern pointer (patp), pointer to
 *      a place to store lower bound (lowp), and pointer to a
 *      place to store upper bound (highp), parses part of
 *      the list, updating the pattern pointer in the process.
 *
 *      For list characters which are not part of a range,
 *      the lower and upper bounds are set to that character.
 *
 */

static VOID list_parse (patp, lowp, highp)
char **patp;
char *lowp;
char *highp;
{
    *lowp = nextch (patp);
    if (**patp == '-') {
        (*patp)++;
        *highp = nextch (patp);
    } else {
        *highp = *lowp;
    }
}









/*
 *  FUNCTION
 *
 *      nextch    determine next character in a pattern
 *
 *  SYNOPSIS
 *
 *      static char nextch (patp)
 *      char **patp;
 *
 *  DESCRIPTION
 *
 *      Given pointer to a pointer to a pattern, uses the pattern
 *      pointer to determine the next character in the pattern,
 *      subject to translation of backslash-char and backslash-octal
 *      sequences.
 *
 *      The character pointer is updated to point at the next pattern
 *      character to be processed.
 *
 */

static char nextch (patp)
char **patp;
{
    register char ch;
    register char chsum;
    register int count;

    ch = *(*patp)++;
    if (ch == '\\') {
        ch = *(*patp)++;
        if (IS_OCTAL (ch)) {
            chsum = 0;
            for (count = 0; count < 3 && IS_OCTAL (ch); count++) {
                chsum *= 8;
                chsum += ch - '0';
                ch = *(*patp)++;
            }
            (*patp)--;
            ch = chsum;
        }
    }
    return (ch);
}
SHAR_EOF
if test 6723 -ne "`wc -c match.c`"
then
echo shar: error transmitting match.c '(should have been 6723 characters)'
fi
cat << \SHAR_EOF > vmsbackup.1
.TH VMSBACKUP 1
.SH NAME
vmsbackup \- read a VMS backup tape
.SH SYNOPSIS
.B vmsbackup
.B \-{tx}[bcdevw][s setnumber][f tapefile]
[ name ... ]
.SH DESCRIPTION
.I vmsbackup
reads a VMS generated backup tape, converting the files
to Unix format and writing the files to disc.
The default operation of the program is to go through an entire
tape, extracting every file and writing it to disc.
This may be modified by the following options.
.TP 8
.B b blocksize
Set block size of saveset file. Default is 32256 bytes per record.
.TP 8
.B c
Use complete filenames, including the version number.
A colon and the octal version number will be appended to all filenames.
A colon, rather than a semicolon, is used since the Unix Shell
uses the semicolon as the line separator.
Using a colon prevents the user from having to escape the semicolon
when referencing the filename.
This option is useful only when multiple versions of the same file
are on a single tape or when a file of the same name already
exists in the destination directory.
The default is to ignore version numbers.
.TP 8
.B d
use the directory structure from VMS, the default value is off.
.TP 8
.B e
Process all filename extensions.
Since this program is mainly intended to move source code and possibly
data from a DEC system to a Unix system, the default is to ignore
all files whose filename extension specifies system dependent data.
The file types which will be ignored, unless the
.B e
option is specified, are
.IP "" 10
exe     VMS executable file
.br
lib     VMS object library file
.br
obj     RSX object file
.br
odl     RSX overlay description file
.br
olb     RSX object library file
.br
pmd     RSX post mortem dump file
.br
stb     RSX task symbol table file
.br
sys     RSX bootable system file
.br
tsk     RSX executable task file
.PP
.TP 8
.B f
Use the next argument in the command line as the tape device to
be used, rather than the default.
.sp
If vmsbackup is compiled with the remote tape option
and the file name has the form
.IR system [. user ]:/dev/???
.I vmsbackup
will use the tape drive /dev/??? on the remote system
.IR system ,
via
.IR rsh (1),
and
.IR rmt (8).
The optional
.I user
portion of the pathname specifies the login name to use on the
remote system.
If it is not supplied, the current user's login name will be used.
In all the cases, the user must have the appropriate
permissions on the remote machine, in order to use this facility.
The default is
.I /dev/rmt8
(drive 0, raw mode, 1600 bpi).
This must be a raw mode tape device.
.TP 8
.B s saveset
Process only the given saveset number.
.TP 8
.B t
Produce a table of contents (a directory listing) on the standard output
of the files on tape.
.TP 8
.B v
Verbose output.
Normally
.I vmsbackup
does its work silently.
The verbose option will cause the filenames of the files being read from
tape to disk to be output on the standard output.
.TP 8
.B w
.I vmsbackup
prints the action to be taken followed by file name, then
wait for user confirmation. If a word beginning with `y'
is given, the action is done. Any other input means don't do it.
.TP 8
.B x
extract the named files from the tape.
.TP 8
The optional
.I name
argument specifies one or more filenames to be
searched for specifically on the tape and only those files are to be processed.
The name may contain the usal sh(1) meta-characters *?![] \nnn.
.SH FILES
/dev/rmt\fIx\fP
.SH SEE ALSO
rmtops(3)
.SH BUGS
The filename match uses the complete VMS file names.

.SH AUTHOR
John Douglas Carey
.br
Sven-Ove Westberg
SHAR_EOF
if test 3533 -ne "`wc -c vmsbackup.1`"
then
echo shar: error transmitting vmsbackup.1 '(should have been 3533 characters)'
fi
cat << \SHAR_EOF > vmsbackup.c
/*
 *
 *  Title:
 *      Backup
 *
 *  Decription:
 *      Program to read VMS backup tape
 *
 *  Author:
 *      John Douglas CAREY.
 *      Sven-Ove Westberg    (version 3.0)
 *
 *  Net-addess:
 *      john%monu1.oz@seismo.ARPA
 *      luthcad!sow@enea.UUCP
 *
 *  History:
 *      Version 1.0 - September 1984
 *              Can only read variable length records
 *      Version 1.1
 *              Cleaned up the program from the original hack
 *              Can now read stream files
 *      Version 1.2
 *              Now convert filename from VMS to UNIX
 *                      and creates sub-directories
 *      Version 1.3
 *              Works on the Pyramid if SWAP is defined
 *      Version 1.4
 *              Reads files spanning multiple tape blocks
 *      Version 1.5
 *              Always reset reclen = 0 on file open
 *              Now output fixed length records
 *
 *      Version 2.0 - July 1985
 *              VMS Version 4.0 causes a rethink !!
 *              Now use mtio operations instead of opening and closing file
 *              Blocksize now grabed from the label
 *
 *      Version 2.1 - September 1985
 *              Handle variable length records of zero length.
 *
 *      Version 2.2 - July 1986
 *              Handle FORTRAN records of zero length.
 *              Inserted exit(0) at end of program.
 *              Distributed program in aus.sources
 *
 *      Version 2.3 - August 1986
 *              Handle FORTRAN records with record length fields
 *              at the end of a block
 *              Put debug output to a file.
 *              Distributed program in net.sources
 *
 *      Version 3.0 - December 1986
 *              Handle multiple saveset
 *              Remote tape
 *              Interactive mode
 *              File name selection with meta-characters
 *              Convert ; to : in VMS filenames
 *              Flag for usage of VMS directory structure
 *              Flag for "useless" files  eg. *.exe
 *              Flag for use VMS version in file names
 *              Flag for verbose mode
 *              Flag to list the contents of the tape
 *              Distributed to mod.sources
 *
 *      Version 4.0 - November 1988 by Timothy Stark (11TSTARK@GALLUA)
 *              Handle disk saveset
 *              Add -b option to set block size for disk saveset
 *
 *  Installation:
 *
 *      Computer Centre
 *      Monash University
 *      Wellington Road
 *      Clayton
 *      Victoria        3168
 *      AUSTRALIA
 *
 */
#include        <stdio.h>
#include        <ctype.h>

#include        <sys/ioctl.h>
#include        <sys/types.h>
#ifdef REMOTE
#include        <local/rmt.h>
#include        <sys/stat.h>
#endif
#include        <sys/mtio.h>
#include        <sys/file.h>

#ifdef pyr
#define SWAP
#endif pyr

#ifdef sun
#define SWAP
#endif

struct bbh {
        short   bbh_dol_w_size;
        short   bbh_dol_w_opsys;
        short   bbh_dol_w_subsys;
        short   bbh_dol_w_applic;
        long    bbh_dol_l_number;
        char    bbh_dol_t_spare_1[20];
        short   bbh_dol_w_struclev;
        short   bbh_dol_w_volnum;
        long    bbh_dol_l_crc;
        long    bbh_dol_l_blocksize;
        long    bbh_dol_l_flags;
        char    bbh_dol_t_ssname[32];
        short   bbh_dol_w_fid[3];
        short   bbh_dol_w_did[3];
        char    bbh_dol_t_filename[128];
        char    bbh_dol_b_rtype;
        char    bbh_dol_b_rattrib;
        short   bbh_dol_w_rsize;
        char    bbh_dol_b_bktsize;
        char    bbh_dol_b_vfcsize;
        short   bbh_dol_w_maxrec;
        long    bbh_dol_l_filesize;
        char    bbh_dol_t_spare_2[22];
        short   bbh_dol_w_checksum;
} *block_header;

struct brh {
        short   brh_dol_w_rsize;
        short   brh_dol_w_rtype;
        long    brh_dol_l_flags;
        long    brh_dol_l_address;
        long    brh_dol_l_spare;
} *record_header;

/* define record types */

#define brh_dol_k_null  0
#define brh_dol_k_summary       1
#define brh_dol_k_volume        2
#define brh_dol_k_file  3
#define brh_dol_k_vbn   4
#define brh_dol_k_physvol       5
#define brh_dol_k_lbn   6
#define brh_dol_k_fid   7

struct bsa {
        short   bsa_dol_w_size;
        short   bsa_dol_w_type;
        char    bsa_dol_t_text[1];
} *data_item;

#ifdef  STREAM
char    *def_tapefile = "/dev/rts8";
#else
char    *def_tapefile = "/dev/rmt8";
#endif
char    *tapefile;

char    filename[128];
int     filesize;

char    recfmt;         /* record format */

#define                 FAB_dol_C_UDF   0       /* undefined */
#define                 FAB_dol_C_FIX   1       /* fixed-length record */
#define                 FAB_dol_C_VAR   2       /* variable-length record */
#define                 FAB_dol_C_VFC   3       /* variable-length with fixed-le
ngth control record */
#define                 FAB_dol_C_STM   4       /* RMS-11 stream record (valid o
nly for sequential org
   ) */
#define                 FAB_dol_C_STMLF 5       /* stream record delimited by LF
 (sequential org onl
   y) */
#define                 FAB_dol_C_STMCR 6       /* stream record delimited by CR
 (sequential org onl
   y) */
#define                 FAB_dol_C_MAXRFM        6       /* maximum rfm supported
 */

char    recatt;         /* record attributes */

#define                 FAB_dol_V_FTN   0       /* FORTRAN carriage control char
acter */
#define                 FAB_dol_V_CR    1       /* line feed - record -carriage
return */
#define                 FAB_dol_V_PRN   2       /* print-file carriage control *
/
#define                 FAB_dol_V_BLK   3       /* records don't cross block bou
ndaries */

#define FANO    20

#ifdef  pyr
static struct   bsa     *file_table[FANO];
#else
struct  bsa     *file_table[FANO];
#endif

FILE    *f      = NULL;
int     file_count;
short   reclen;
short   fix;
short   recsize;
int     vfcsize;

#ifdef  NEWD
FILE    *lf;
#endif  NEWD

int     fd;             /* tape file descriptor */
int     cflag, dflag, eflag, fflag, sflag, tflag, vflag, wflag, xflag;
int     setnr;
char    **gargv;
int     goptind, gargc;

#define LABEL_SIZE      80
char    label[LABEL_SIZE];

char    *block;
int     blocksize;

struct  mtop    op;

FILE *
openfile(fn)
char    *fn;
{
        char    ufn[256];
        char    ans[80];
        char    *p, *q, s, *ext;
        int     procf;

        procf = 1;
        /* copy fn to ufn and convert to lower case */
        p = fn;
        q = ufn;
        while (*p) {
                if (isupper(*p))
                        *q = *p - 'A' + 'a';
                else
                        *q = *p;
                p++;
                q++;
        }
        *q = '\0';

        /* convert the VMS to UNIX and make the directory path */
        p = ufn;
        q = ++p;
        while (*q) {
                if (*q == '.' || *q == ']') {
                        s = *q;
                        *q = '\0';
                        if(procf && dflag) mkdir(p, 0777);
                        *q = '/';
                        if (s == ']')
                                break;
                }
                *q++;
        }
        *q++;
        if(!dflag) p=q;
        /* strip off the version number */
        while (*q && *q != ';') {
                if( *q == '.') ext = q;
                q++;
        }
        if (cflag) {
                *q = ':';
        }
        else {
                *q = '\0';
        }
        if(!eflag && procf) procf = typecmp(++ext);
        if(procf && wflag) {
                printf("extract %s [ny]",filename);
                fflush(stdout);
                gets(ans);
                if(*ans != 'y') procf = NULL;
        }
        if(procf)
                /* open the file for writing */
                return(fopen(p, "w"));
        else
                return(NULL);
}

typecmp(str)    /* Compare the filename type in str with our list
                   of file type to be ignored.  Return 0 if the
                   file is to be ignored, return 1 if the
                   file is not in our list and should not be ignored. */
register char   *str;
{
        static char *type[] = {
                "exe",          /* vms executable image */
                "lib",          /* vms object library */
                "obj",          /* rsx object file */
                "odl",          /* rsx overlay description file */
                "olb",          /* rsx object library */
                "pmd",          /* rsx post mortem dump */
                "stb",          /* rsx symbol table */
                "sys",          /* rsx bootable system image */
                "tsk",          /* rsx executable image */
                "dir",
                "upd",
                "tlo",
                "tlb",
                ""              /* null string terminates list */
        };
        register int    i;

        i = -1;
        while (*type[++i])
                if (strncmp(str, type[i],3) == 0)
                        return(0);      /* found a match, file to be ignored */
        return(1);                      /* no match found */
}

process_file(buffer)
char    *buffer;
{
        int     i, n;
        char    *p, *q;
        short   dsize, nblk, lnch;

        int     c;
        short   *s;

        int     procf;

        s = (short *) buffer;

        /* check the header word */
        if (*s != 257) {
                printf("Snark: invalid data header\n");
                exit(1);
        }

        c = 2;
        for (i = 0; i < FANO; i++) {
                file_table[i] = (struct bsa *) &buffer[c];
#ifndef SWAP
                dsize = file_table[i]->bsa_dol_w_size;
#else
                swap(&file_table[i]->bsa_dol_w_size, &dsize, sizeof(short));
#endif
                c += dsize + 4;
        }

        /* extract file name */
#ifndef SWAP
        dsize = file_table[0]->bsa_dol_w_size;
#else
        swap(&file_table[0]->bsa_dol_w_size, &dsize, sizeof(short));
#endif
        p = file_table[0]->bsa_dol_t_text;
        q = filename;
        for (i = 0; i < dsize; i++)
                *q++ = *p++;
        *q = '\0';

        /* extract file's record attributes */
#ifndef SWAP
        dsize = file_table[5]->bsa_dol_w_size;
#else
        swap(&file_table[5]->bsa_dol_w_size, &dsize, sizeof(short));
#endif
        p = file_table[5]->bsa_dol_t_text;
        recfmt = p[0];
        recatt = p[1];
#ifndef SWAP
        bcopy(&p[2], &recsize, sizeof(short));
#else
        swap(&p[2], &recsize, sizeof(short));
#endif
        vfcsize = p[15];
        if (vfcsize == 0)
                vfcsize = 2;
#ifdef  DEBUG
        printf("recfmt = %d\n", recfmt);
        printf("recatt = %d\n", recatt);
        printf("reclen = %d\n", recsize);
        printf("vfcsize = %d\n", vfcsize);
#endif
#ifndef SWAP
        bcopy(&p[10], &nblk, sizeof(short));
        bcopy(&p[12], &lnch, sizeof(short));
#else
        swap(&p[10], &nblk, sizeof(short));
        swap(&p[12], &lnch, sizeof(short));
#endif
        filesize = (nblk-1)*512 + lnch;
#ifdef DEBUG
        printf("nbk = %d, lnch = %d\n", nblk, lnch);
        printf("filesize = 0x%x\n", filesize);
#endif

        /* open the file */
        if (f != NULL) {
                fclose(f);
                file_count = 0;
                reclen = 0;
        }
        procf = 0;
        if (goptind < gargc)
                for(i=goptind; i < gargc; i++) {
                        procf |= match(filename,gargv[i]);
                }
        else
                procf = 1;
        if (tflag && procf)
                printf( " %-35s %8d \n",filename,filesize);
        if (xflag && procf) {
                /* open file */
                f = openfile(filename);
                if(f != NULL && vflag) printf("extracting %s\n", filename);
        }
}
/*
 *
 *  process a virtual block record (file record)
 *
 */
process_vbn(buffer, rsize)
char            *buffer;
unsigned short  rsize;
{
        int     c, i;

        if (f == NULL) {
                return;
        }
        i = 0;
        while (file_count+i < filesize && i < rsize) {
                switch (recfmt) {
                case FAB_dol_C_FIX:
                        if (reclen == 0) {
                                reclen = recsize;
                        }
                        fputc(buffer[i], f);
                        i++;
                        reclen--;
                        break;

                case FAB_dol_C_VAR:
                case FAB_dol_C_VFC:
                        if (reclen == 0) {
                                reclen = *((short *) &buffer[i]);
#ifdef  SWAP
                                swap(&reclen, &reclen, sizeof(short));
#endif
#ifdef  NEWD
                                fprintf(lf, "---\n");
                                fprintf(lf, "reclen = %d\n", reclen);
                                fprintf(lf, "i = %d\n", i);
                                fprintf(lf, "rsize = %d\n", rsize);
#endif  NEWD
                                fix = reclen;
                                i += 2;
                                if (recfmt == FAB_dol_C_VFC) {
                                        i += vfcsize;
                                        reclen -= vfcsize;
                                }
                        } else if (reclen == fix
                                        && recatt == (1 << FAB_dol_V_FTN)) {
                                        /****
                                        if (buffer[i] == '0')
                                                fputc('\n', f);
                                        else if (buffer[i] == '1')
                                                fputc('\f', f);
                                        *** sow ***/
                                        fputc(buffer[i],f); /** sow **/
                                        i++;
                                        reclen--;
                        } else {
                                fputc(buffer[i], f);
                                i++;
                                reclen--;
                        }
                        if (reclen == 0) {
                                fputc('\n', f);
                                if (i & 1)
                                        i++;
                        }
                        break;

                case FAB_dol_C_STM:
                case FAB_dol_C_STMLF:
                        if (reclen < 0) {
                                printf("SCREAM\n");
                        }
                        if (reclen == 0) {
                                reclen = 512;
                        }
                        c = buffer[i++];
                        reclen--;
                        if (c == '\n') {
                                reclen = 0;
                        }
                        fputc(c, f);
                        break;

                case FAB_dol_C_STMCR:
                        c = buffer[i++];
                        if (c == '\r')
                                fputc('\n', f);
                        else
                                fputc(c, f);
                        break;

                default:
                        fclose(f);
                        unlink(filename);
                        fprintf(stderr, "Invalid record format = %d\n", recfmt);
                        return;
                }
        }
        file_count += i;
}
#ifdef  SWAP
/*
 *
 *  do swapping for Motorola type architectures
 *
 */
swap(from, to, nbytes)
char    *from, *to;
int     nbytes;
{
        int     i, j;
        char    temp[100];

        for (i = 0; i < nbytes; i++)
                temp[i] = from[i];
        for (i = 0, j = nbytes-1; i < nbytes; i++, j--)
                to[i] = temp[j];
}
#endif
/*
 *
 *  process a backup block
 *
 */
process_block(block, blocksize)
char    *block;
int     blocksize;
{

        unsigned short  bhsize, rsize, rtype;
        unsigned long   bsize, i;

        i = 0;

        /* read the backup block header */
        block_header = (struct bbh *) &block[i];
        i += sizeof(struct bbh);

        bhsize = block_header->bbh_dol_w_size;
        bsize = block_header->bbh_dol_l_blocksize;
#ifdef  SWAP
        swap(&bhsize, &bhsize, sizeof(short));
        swap(&bsize, &bsize, sizeof(long));
#endif

        /* check the validity of the header block */
        if (bhsize != sizeof(struct bbh)) {
                fprintf(stderr, "Snark: Invalid header block size\n");
                exit(1);
        }
        if (bsize != 0 && bsize != blocksize) {
                fprintf(stderr, "Snark: Invalid block size\n");
                exit(1);
        }
#ifdef  DEBUG
        printf("new block: i = %d, bsize = %d\n", i, bsize);
#endif

        /* read the records */
        while (i < bsize) {
                /* read the backup record header */
                record_header = (struct brh *) &block[i];
                i += sizeof(struct brh);

                rtype = record_header->brh_dol_w_rtype;
                rsize = record_header->brh_dol_w_rsize;
#ifdef  SWAP
                swap(&rtype, &rtype, sizeof(short));
                swap(&rsize, &rsize, sizeof(short));
#endif
#ifdef  DEBUG
                printf("rtype = %d\n", rtype);
                printf("rsize = %d\n", rsize);
                printf("flags = 0x%x\n", record_header->brh_dol_l_flags);
                printf("addr = 0x%x\n", record_header->brh_dol_l_address);
                printf("i = %d\n", i);
#endif

                switch (rtype) {

                case brh_dol_k_null:
#ifdef  DEBUG
                        printf("rtype = null\n");
#endif
                        break;

                case brh_dol_k_summary:
#ifdef  DEBUG
                        printf("rtype = summary\n");
#endif
                        break;

                case brh_dol_k_file:
#ifdef  DEBUG
                        printf("rtype = file\n");
#endif
                        process_file(&block[i]);
                        break;

                case brh_dol_k_vbn:
#ifdef  DEBUG
                        printf("rtype = vbn\n");
#endif
                        process_vbn(&block[i], rsize);
                        break;

                case brh_dol_k_physvol:
#ifdef  DEBUG
                        printf("rtype = physvol\n");
#endif
                        break;

                case brh_dol_k_lbn:
#ifdef  DEBUG
                        printf("rtype = lbn\n");
#endif
                        break;

                case brh_dol_k_fid:
#ifdef  DEBUG
                        printf("rtype = fid\n");
#endif
                        break;

                default:
                        fprintf(stderr, " Snark: invalid record type\n");
                        fprintf(stderr, " record type = %d\n", rtype);
                        exit(1);
                }
#ifdef pyr
                i = i + rsize;
#else
                i += rsize;
#endif
        }
}

rdhead()
{
        int i, nfound;
        char name[80];
        nfound = 1;
        /* read the tape label - 4 records of 80 bytes */
        while ((i = read(fd, label, LABEL_SIZE)) != 0) {
                if (i != LABEL_SIZE) {
                        fprintf(stderr, "Snark: bad label record\n");
                        exit(1);
                }
                if (strncmp(label, "VOL1",4) == 0) {
                        sscanf(label+4, "%14s", name);
                        if(vflag || tflag) printf("Volume: %s\n",name);
                }
                if (strncmp(label, "HDR1",4) == 0) {
                        sscanf(label+4, "%14s", name);
                        sscanf(label+31, "%4d", &setnr);
                }
                /* get the block size */
                if (strncmp(label, "HDR2", 4) == 0) {
                        nfound = 0;
                        sscanf(label+5, "%5d", &blocksize);
#ifdef  DEBUG
                        printf("blocksize = %d\n", blocksize);
#endif
                }
        }
        if((vflag || tflag) && !nfound)
                printf("Saveset name: %s   number: %d\n",name,setnr);
        /* get the block buffer */
        block = (char *) malloc(blocksize);
        if (block == (char *) 0) {
                fprintf(stderr, "memory allocation for block failed\n");
                exit(1);
        }
        return(nfound);
}

rdtail()
{
        int i;
        char name[80];
        /* read the tape label - 4 records of 80 bytes */
        while ((i = read(fd, label, LABEL_SIZE)) != 0) {
                if (i != LABEL_SIZE) {
                        fprintf(stderr, "Snark: bad label record\n");
                        exit(1);
                }
                if (strncmp(label, "EOF1",4) == 0) {
                        sscanf(label+4, "%14s", name);
                        if(vflag || tflag)
                                printf("End of saveset: %s\n\n\n",name);
                }
        }
}

usage(progname)
char    *progname;
{
        fprintf(stderr,
          "Usage:  %s -{tx}[cdevw][-b blocksize][-s setnumber][-f tapefile]\n",p
rogname
   );
}
main(argc, argv)
int     argc;
char    *argv[];
{

        char *progname;
        int     c, i, eoffl;
        int     selset;
        extern int optind;
        extern char *optarg;

        progname = argv[0];
        if(argc < 2){
                usage(progname);
                exit(1);
        }
        gargv = argv;
        gargc = argc;
        blocksize = 32256;
        tapefile = def_tapefile;
        cflag=dflag=eflag=fflag=sflag=tflag=vflag=wflag=xflag=0;
        while((c=getopt(argc,argv,"bcdef:s:tvwx")) != EOF)
                switch(c){
                case 'b':
                        sscanf(optarg,"%d",&blocksize);
                        break;
                case 'c':
                        cflag++;
                        break;
                case 'd':
                        dflag++;
                        break;
                case 'e':
                        eflag++;
                        break;
                case 'f':
                        fflag++;
                        tapefile = optarg;
                        break;
                case 's':
                        sflag++;
                        sscanf(optarg,"%d",&selset);
                        break;
                case 't':
                        tflag++;
                        break;
                case 'v':
                        vflag++;
                        break;
                case 'w':
                        wflag++;
                        break;
                case 'x':
                        xflag++;
                        break;
                case '?':
                        usage(progname);
                        exit(1);
                        break;
                };
        if(!tflag && !xflag) {
                usage(progname);
                exit(1);
        }
        goptind = optind;

#ifdef  NEWD
        /* open debug file */
        lf = fopen("log", "w");
        if (lf == NULL) {
                perror("log");
                exit(1);
        }
#endif

        /* open the tape/disk file */
        fd = open(tapefile, O_RDONLY);
        if (fd < 0) {
                perror(tapefile);
                exit(1);
        }

        if(!fflag) {
                /* rewind the tape */
                op.mt_op = MTREW;
                op.mt_count = 1;
                i = ioctl(fd, MTIOCTOP, &op);
                if (i < 0) {
                        perror(tapefile);
                        exit(1);
                }
                eoffl = rdhead();
        }
        else {
                eoffl = 0;
                block = (char *) malloc(blocksize);
        }

        /* read the backup tape blocks until end of tape */
        while (!eoffl) {
                if(sflag && setnr != selset && !fflag) {
                        op.mt_op = MTFSF;
                        op.mt_count = 1;
                        i = ioctl(fd, MTIOCTOP, &op);
                        if (i < 0) {
                                perror(tapefile);
                                exit(1);
                        }
                        i = 0;
                }
                else
                        i = read(fd, block, blocksize);
                if(i == 0 && !fflag) {
                        rdtail();
                        eoffl=rdhead();
                }
                else if(i == 0) break;

                if (i != blocksize) {
                        fprintf(stderr, "bad block read i = %d\n", i);
                        exit(1);
                }
                else{
                        eoffl = 0;
                        process_block(block, blocksize);
                }
        }
        if(vflag || tflag) printf("End of tape\n");

        /* close the tape */
        close(fd);

#ifdef  NEWD
        /* close debug file */
        fclose(lf);
#endif  NEWD

        /* exit cleanly */
        exit(0);
}
SHAR_EOF
if test 16988 -ne "`wc -c vmsbackup.c`"
then
echo shar: error transmitting vmsbackup.c '(should have been 16988 characters)'
fi
#       End of shell archive
exit 0
------------------------Cut---------Cut------------Cut--------------


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Timothy Stark          |   Bitnet:      11tstark@gallua.bitnet           VMS
Gallaudet University   |   Internet:    11tstark@gallux.gallaudet.edu    UNIX
P.O. Box 1453          |   GEnie:       T.STARK1
Washington, DC 20002   |   People/Link: OCS130
U.S.A.                 |   QuantumLink: TimS18
Earth                  |   "Gallaudet University is the only university for
Solar System           |    the deaf in the world."
Milky Way              |
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

