Summary: Wanted: Up-to-date VAX/VMS backup tape reader/utility

From: Gary.Speechley@dsto.defence.gov.au
Date: Mon Nov 07 1994 - 17:37:49 CST


As a summary to my earlier posting, thanks to the following:

Me too: bolef@asu.edu (Larry Bolef)

Reply: James Pearson <jcpearso@ps.ucl.ac.uk>, attached below.

I too have hacked the code. I didn't like the default operation of listing
the contents of the first saveset (a default -s 1 option), preferring often
to perform a saveset directory (vmsbackup -t) to list the savesets. I also
played with text output a little (primarily to list file sizes in aligned
columns).

Many thanks to those who replied...

Gary

-----

Original reply from James Pearson:

I have a "hacked" version of vmsbackup that *might* be able to read your
tapes. The problem is that vmsbackup exits (with the above message) if
it comes across a record it doesn't know how to decode. My "solution"
is to ignore these records and carry on processing the saveset. Of
course, I may be throwing away useful data, but on the few times I've
used this routine, I've always extracted files as expected.

There seems to be many "versions" of vmsbackup around - each with
different bug fixes and slightly different functionality. I've included
the version I use (with my "fixes") at the end of this message. I have
no idea if it works on 2.3 as I'm running 4.1.3.

James Pearson
+------------------------------------------------------------------------+
Dept. Photogrammetry & Surveying INTERNET: j.pearson@ps.ucl.ac.uk
University College London
Gower Street
London WC1E 6BT
England

# This is a shell archive, shar, format file.
# To unarchive, feed this text into /bin/sh in the directory
# you wish the files to be in.

echo x - README 1>&2
sed 's/^X//' > README << 'End of 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: sow@luthcad.UUCP
XUUCP: {decvax,philabs,seismo}!mcvax!enea!luthcad!sow
End of README
chmod u=r--,g=r--,o=r-- README
echo x - vmsbackup.1 1>&2
sed 's/^X//' > vmsbackup.1 << 'End of vmsbackup.1'
X.TH VMSBACKUP 1 "12 May 1994"
X.SH NAME
Xvmsbackup \- read a VMS backup tape
X.SH SYNOPSIS
X.B vmsbackup
X.B \-{tx}[cdevwB][b blocksize][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 file to
Xbe used, rather than the default.
XThe file may be a tape device or a disk file.
X.\" In the latter case the
X.\" .B b
X.\" option should be used to set the block size.
XThe disk file is assumed to contain a saveset image.
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 remote
Xsystem. If it is not supplied, the current user's login name
Xwill be used. In 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. If the environment variable
X.I TAPE
Xis set, then this is used as the default tape device.
X.TP 8
X.B b
XUse the next argument in the command line as the block size for a
Xsaveset on disk. This option is now redundant as the block size is now
Xextracted from the file. If the
X.I vmsbackup
Xfails with an incorrect block size, then it is likely that the file is
Xnot a saveset.
X.TP 8
X.B s
XUse the next argument as the number of the only saveset on the tape
Xto process.
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
X.B B
XForce Variable Length Record files to be output as binary. The default
Xis text format.
X.PP
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 .
X.SH FILES
X/dev/rmt\fIx\fP
X.SH ENVIRONMENT
X.TP 8
X.B TAPE
XIf specified, in the environment, the value of TAPE indicates the
Xdefault tape device.
X.SH SEE ALSO
Xansitar(1)
X.SH BUGS
XThe filename match uses the complete VMS file names.
X.SH AUTHORS
XJohn Douglas Carey (john%monu1.oz@seismo.ARPA)
X.br
XSven-Ove Westberg (luthcad!sow@enea.UUCP)
X.br
XKen Yap (ken@cs.rochester.edu)
X.br
XJames Pearson UCL P&S (j.pearson@ps.ucl.ac.uk) - Bug fixes and tidy up 6/9/91 - 19/5/94
End of vmsbackup.1
chmod u=rw-,g=r--,o=r-- vmsbackup.1
echo x - vmsbackup.c 1>&2
sed 's/^X//' > vmsbackup.c << 'End of vmsbackup.c'
X#ifndef lint
Xstatic char SCCSid[]="@(#)vmsbackup.c 1.3 5/9/87 LuthCad";
X#endif
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 * Ken Yap (version 3.1)
X *
X * Net-addresses:
X * john%monu1.oz@seismo.ARPA
X * luthcad!sow@enea.UUCP
X * ken@cs.rochester.edu
X * j.pearson@ps.ucl.ac.uk
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 * Version 3.1 - April 1987
X * Added facility to read backup savesets from disk files
X *
X * Version 3.1.1 - September 1991
X * Cleaned up error messages James Pearson UCL P&S
X * Skips "incomplete blocks" when reading from tape
X * Should be able to read multi-volume data
X *
X * Version 3.1.2 - January 1992
X * Skips "incomplete blocks" when reading from disk
X * James Pearson UCL P&S
X *
X * Version 3.1.3
X * Force Variable Length Record files to be output as
X * binary (-B option). The default is text format.
X *
X * Version 3.1.4 - May 1994
X * Automatically set the block size when input read
X * from disk (don't understand why this wasn't done when
X * disk savesets were added). JCP UCL P&S 12/5/94
X *
X * Version 3.1.5 - May 1994
X * Allow "undefined" record formats and treat them as
X * fixed length records. Ignore unknown record types.
X * JCP UCL P&S 18/5/94
X *
X * Version 3.1.6 - May 1994
X * "Fixed" a problem in match.c that caused a "match"
X * failure in the first character in the file name
X * is a '[' - which is a valid VMS character. Unix
X * uses this as one of the shell "metacharacters" used
X * in pattern matching. JCP UCL P&S 19/5/94
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#include <stdlib.h>
X#include <sys/ioctl.h>
X#include <sys/types.h>
X#ifdef REMOTE
X#include <local/rmt.h>
X#endif
X#include <sys/stat.h>
X#include <sys/mtio.h>
X#include <sys/file.h>
X
X#ifdef pyr
X#define SWAP
X#endif
X
X#ifdef sun
X#define SWAP
X#endif
X
X/* JCP hack - Some default SGI tape devices are swapped - but we tend to use
X the "ns" devices by default. Also this will cause problems with diskfiles */
X#ifdef __sgi
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*/
X#define FAB_dol_C_VAR 2 /* variable-length reco
Xrd */
X#define FAB_dol_C_VFC 3 /* variable-length with
X fixed-length control record */
X#define FAB_dol_C_STM 4 /* RMS-11 stream record (valid
Xonly for sequential org) */
X#define FAB_dol_C_STMLF 5 /* stream record delimi
Xted by LF (sequential org only) */
X#define FAB_dol_C_STMCR 6 /* stream record delimited by C
XR (sequential org only) */
X#define FAB_dol_C_MAXRFM 6 /* maximum rfm
Xsupported */
X
Xchar recatt; /* record attributes */
X
X#define FAB_dol_V_FTN 0 /* FORTRAN carriage con
Xtrol character */
X#define FAB_dol_V_CR 1 /* line feed - record -
Xcarriage return */
X#define FAB_dol_V_PRN 2 /* print-file carriage
Xcontrol */
X#define FAB_dol_V_BLK 3 /* records don't cross
Xblock 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
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
Xchar *progname, *prog;
Xchar *rindex();
X
Xint exp_blk = 1, this_blk = 0;
Xint new_tape = 1;
Xint binary = 0;
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/
X return(1); /* no match found */
X}
X
Xprocess_file(buffer)
Xchar *buffer;
X{
X int i, n;
X char *p, *q;
X unsigned short dsize, nblk, lnch, bblk;
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 perr(prog,"invalid data header");
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 bcopy(&p[8], &bblk, sizeof(short));
X#else
X swap(&p[10], &nblk, sizeof(short));
X swap(&p[12], &lnch, sizeof(short));
X swap(&p[8], &bblk, sizeof(short));
X#endif
X
X filesize = bblk*65536*512 + (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 /* JCP hack - assume undefined is fixed-length */
X case FAB_dol_C_UDF:
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
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 if (!binary)
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 /* JCP hack */
X if (binary)
X break;
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;
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
X this_blk = block_header->bbh_dol_l_number;
X
X#ifdef SWAP
X swap(&bhsize, &bhsize, sizeof(short));
X swap(&bsize, &bsize, sizeof(long));
X swap(&this_blk, &this_blk, sizeof(long));
X#endif
X
X if(this_blk!=exp_blk)
X perr(prog,"shouldn't happen");
X
X /* check the validity of the header block */
X if (bhsize != sizeof(struct bbh)) {
X perr(prog,"Invalid header block size");
X }
X if (bsize != 0 && bsize != blocksize) {
X return;
X/* perr(prog,"Invalid block size"); */
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
X#ifdef SWAP
X swap(&rtype, &rtype, sizeof(short));
X swap(&rsize, &rsize, sizeof(short));
X#endif
X
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 /* yet another JCP hack - ignore these unknown records
X and reject the rest of the block (only seen this happen
X with the last block of a save set after all the files
X have been extracted */
X default:
X#ifdef DEBUG
X printf("rtype = unknown\n");
X#endif
X i = bsize - rsize;
X break;
X/*
X default:
X perr(prog,"invalid record type, record type = %d",rtype);
X*/
X }
X#ifdef pyr
X i = i + rsize;
X#else
X i += rsize;
X#endif
X /* yet another JCP hack - if record size is not even
X then it will cause problems next time through the
X loop. So "assume" the rest of the record is not
X needed. This only seems to happen with the last block
X of a save set after all the files have been extracted */
X if (rsize%2)
X i = bsize;
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 perr(prog,"bad label record");
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 if (new_tape) {
X block = (char *) malloc(blocksize);
X if (block == (char *) 0) {
X perr(prog,"memory allocation for block failed");
X }
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 perr(prog,"bad label record");
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 if (strncmp(label, "EOV1",4) == 0) {
X /* rewind the tape */
X if(vflag) {
X printf("Tape rewinding\n");
X fflush(stdout);
X }
X
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 printf("Load Next Volume\n");
X printf("Press RETURN when ready\n");
X fflush(stdout);
X i = getchar();
X new_tape = 0;
X break;
X }
X }
X}
X
Xusage(a)
Xchar *a;
X{
X char *b;
X
X /* strip off any leading pathname components */
X if((b = rindex(a,'/')) == 0)
X b = a;
X else
X b++;
X
X fprintf(stderr,
X "Usage: %s -{tx}[cdevw][-b blocksize][-s setnumber][-f tapefile]\n",
Xb);
X}
X
X/*
X** Backup images on disk do not have the ANSI headers and trailers
X*/
Xdiskfile()
X{
X register int i;
X
X /* if blocksize is not given, then try to get it from the file
X - this is a guess at its location - but it seems to work ... */
X if (blocksize < 512 || blocksize > 64 * 1024) {
X
X struct bbh tmp;
X
X if(read(fd,&tmp,sizeof(struct bbh)) == 0)
X perr(prog,"Unexpected EOF");
X
X lseek(fd,0,0);
X
X blocksize = tmp.bbh_dol_l_blocksize;
X#ifdef SWAP
X swap(&blocksize, &blocksize, sizeof(long));
X#endif
X /* check that blocksize is reasonable */
X if (blocksize < 512 || blocksize > 64 * 1024)
X perr(prog,"Use the -b blocksize option to set a reasonable block size");
X }
X
X if ((block = (char *)malloc(blocksize)) == (char *)0) {
X perr(prog,"memory allocation for block failed");
X }
X
X while ((i = read(fd, block, blocksize)) > 0) {
X/* process_block(block, i); */
X exp_blk = this_blk + 1;
X
X bcopy(&block[8],&this_blk,sizeof(long));
X#ifdef SWAP
X swap(&this_blk, &this_blk, sizeof(long));
X#endif
X if (this_blk != exp_blk) {
X
X int blk;
X
X do {
X i = read(fd, block, blocksize);
X
X if(i == 0)
X perr(prog,"unexpected EOF");
X
X bcopy(&block[8],&blk,sizeof(long));
X#ifdef SWAP
X swap(&blk, &blk, sizeof(long));
X#endif
X }
X while(blk != exp_blk);
X }
X process_block(block, i);
X }
X
X if(vflag || tflag) printf("End of saveset\n");
X
X /* close the file */
X close(fd);
X
X#ifdef NEWD
X /* close debug file */
X fclose(lf);
X#endif
X
X /* exit cleanly */
X exit(0);
X}
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X
X int c, i, eoffl;
X int selset;
X struct stat statb;
X extern int optind;
X extern char *optarg;
X
X progname = prog = argv[0];
X if(argc < 2){
X usage(progname);
X exit(1);
X }
X gargv = argv;
X gargc = argc;
X if ((tapefile = getenv("TAPE")) == NULL)
X tapefile = def_tapefile;
X cflag=dflag=eflag=sflag=tflag=vflag=wflag=xflag=0;
X blocksize = -1;
X while((c=getopt(argc,argv,"Bb:cdef:s:tvwx")) != EOF)
X switch(c){
X case 'B':
X binary = 1;
X break;
X case 'b':
X blocksize = atoi(optarg);
X break;
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 /* find out what kind of file it is */
X if (fstat(fd, &statb) < 0) {
X perror(tapefile);
X exit(1);
X }
X
X /* if a regular file, branch to regular file reading routine */
X if ((statb.st_mode & S_IFMT) == S_IFREG)
X diskfile(); /* never return */
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
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 exp_blk = this_blk + 1;
X
X bcopy(&block[8],&this_blk,sizeof(long));
X#ifdef SWAP
X swap(&this_blk, &this_blk, sizeof(long));
X#endif
X }
X if(i == 0) {
X rdtail();
X eoffl=rdhead();
X }
X else if (i != blocksize || this_blk != exp_blk) {
X
X int blk;
X
X do {
X i = read(fd, block, blocksize);
X
X if(i == 0)
X perr(prog,"unexpected EOF");
X
X bcopy(&block[8],&blk,sizeof(long));
X#ifdef SWAP
X swap(&blk, &blk, sizeof(long));
X#endif
X }
X while(blk != exp_blk || i != blocksize);
X
X eoffl = 0;
X process_block(block, blocksize);
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
X
X /* exit cleanly */
X exit(0);
X}
X/*
X** perr : print error message and exit
X**
X** usage : perr(prog_name, format [ , arg ] ... )
X** char *prog_name, *format;
X**
X** Prints out an error message on stderr and exiting. Used im much
X** the same way as sprintf etc.
X**
X** Uses "varargs" to accept varible number of arguments.
X**
X** James Pearson UCL P&S 4/6/91
X*/
X
X#include <stdio.h>
X#include <varargs.h>
X
Xperr(va_alist)
Xva_dcl
X{
X va_list args;
X char *fmt, *a, *b;
X
X va_start(args);
X
X /* get first argument */
X a = va_arg(args, char *);
X
X /* strip off any leading pathname components */
X if((b = rindex(a,'/')) == 0)
X b = a;
X else
X b++;
X
X /* print routine name */
X fprintf(stderr,"%s: ",b);
X
X /* get rest of the arguments */
X fmt = va_arg(args, char *);
X
X /* print error message */
X vfprintf(stderr, fmt, args);
X
X fputc('\n',stderr);
X
X va_end(args);
X
X exit (1);
X}
Xwarn(va_alist)
Xva_dcl
X{
X va_list args;
X char *fmt, *a, *b;
X
X va_start(args);
X
X /* get first argument */
X a = va_arg(args, char *);
X
X /* strip off any leading pathname components */
X if((b = rindex(a,'/')) == 0)
X b = a;
X else
X b++;
X
X /* print routine name */
X fprintf(stderr,"%s: ",b);
X
X /* get rest of the arguments */
X fmt = va_arg(args, char *);
X
X /* print error message */
X vfprintf(stderr, fmt, args);
X
X fputc('\n',stderr);
X
X va_end(args);
X
X return (0);
X}
End of vmsbackup.c
chmod u=rw-,g=r--,o=r-- vmsbackup.c
echo x - match.c 1>&2
sed 's/^X//' > match.c << 'End of 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 int firstc = 1;
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/* JCP hack - a '[' is a valid VMS first filename character, so treat
X treat it as such ... */
X case LEFT_BRACKET:
X if (firstc == 0) {
X if (*string != EOS) {
X ismatch = do_list (string, pattern);
X break;
X }
X }
X else
X firstc = 0; /* ... and fall through to default */
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}
End of match.c
chmod u=rw-,g=r--,o=r-- match.c
echo x - getopt.c 1>&2
sed 's/^X//' > getopt.c << 'End of getopt.c'
X/*
X I got this off net.sources from Henry Spencer.
X It is a public domain getopt(3) like in System V.
X I have made the following modifications:
X
X index(s,c) was added because too many people could
X not compile getopt without it.
X
X A test main program was added, ifdeffed by GETOPT.
X This main program is a public domain implementation
X of the getopt(1) program like in System V. The getopt
X program can be used to standardize shell option handling.
X e.g. cc -DGETOPT getopt.c -o getopt
X*/
X#include <stdio.h>
X
X#ifndef lint
Xstatic char sccsfid[] = "@(#) getopt.c 5.0 (UTZoo) 1985";
X#endif
X
X#define ARGCH (int)':'
X#define BADCH (int)'?'
X#define EMSG ""
X#define ENDARGS "--"
X
X/* this is included because index is not on some UNIX systems */
Xstatic
Xchar *
Xindex (s, c)
Xregister char *s;
Xregister int c;
X {
X while (*s)
X if (c == *s) return (s);
X else s++;
X return (NULL);
X }
X
X/*
X * get option letter from argument vector
X */
Xint opterr = 1, /* useless, never set or used */
X optind = 1, /* index into parent argv vector */
X optopt; /* character checked for validity */
Xchar *optarg; /* argument associated with option */
X
X#define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \
X fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
X
X
Xgetopt(nargc,nargv,ostr)
Xint nargc;
Xchar **nargv,
X *ostr;
X{
X static char *place = EMSG; /* option letter processing */
X register char *oli; /* option letter list index */
X char *index();
X
X if(!*place) { /* update scanning pointer */
X if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
X if (*place == '-') { /* found "--" */
X ++optind;
X return(EOF);
X }
X } /* option letter okay? */
X if ((optopt = (int)*place++) == ARGCH || !(oli = index(ostr,optopt))) {
X if(!*place) ++optind;
X tell(": illegal option -- ");
X }
X if (*++oli != ARGCH) { /* don't need argument */
X optarg = NULL;
X if (!*place) ++optind;
X }
X else { /* need an argument */
X if (*place) optarg = place; /* no white space */
X else if (nargc <= ++optind) { /* no arg */
X place = EMSG;
X tell(": option requires an argument -- ");
X }
X else optarg = nargv[optind]; /* white space */
X place = EMSG;
X ++optind;
X }
X return(optopt); /* dump back option letter */
X}
X
X
X#ifdef GETOPT
X
X#ifndef lint
Xstatic char sccspid[] = "@(#) getopt.c 5.1 (WangInst) 6/15/85";
X#endif
X
Xmain (argc, argv) char **argv;
X {
X char *optstring = argv[1];
X char *argv0 = argv[0];
X extern int optind;
X extern char *optarg;
X int opterr = 0;
X int C;
X char *opi;
X if (argc == 1)
X {
X fprintf (stderr, "Usage: %s optstring args\n", argv0);
X exit (1);
X }
X argv++;
X argc--;
X argv[0] = argv0;
X while ((C = getopt (argc, argv, optstring)) != EOF)
X {
X if (C == BADCH) opterr++;
X printf ("-%c ", C);
X opi = index (optstring, C);
X if (opi && opi[1] == ARGCH)
X if (optarg)
X printf ("\"%s\" ", optarg);
X else opterr++;
X }
X printf ("%s", ENDARGS);
X while (optind < argc)
X printf (" \"%s\"", argv[optind++]);
X putchar ('\n');
X exit (opterr);
X }
X
X#endif
End of getopt.c
chmod u=r--,g=r--,o=r-- getopt.c
echo x - makefile 1>&2
sed 's/^X//' > makefile << 'End of makefile'
X#
X#
X#REMOTE=-DREMOTE # -DREMOTE use remote tape
XREMOTE=
XSWAP= # -DSWAP swap bytes
XCFLAGS=-O $(SWAP) $(REMOTE)
X#LFLAGS=
X#LIBS= -lrmt # remote magtape library
XOWNER=tar # user for remote tape access
XMODE=0755
XBINDIR=/usr/local/bin
XMANSEC=l
XMANDIR=/usr/man/man$(MANSEC)
XSHAR=shar
XDIST= README vmsbackup.1 vmsbackup.c match.c getopt.c
X
Xvmsbackup: vmsbackup.o getopt.o match.o
X cc $(CFLAGS) -o vmsbackup vmsbackup.o match.o getopt.o $(LIBS)
X
Xinstall:
X install -m $(MODE) -o $(OWNER) -s vmsbackup $(BINDIR)
X cp vmsbackup.1 $(MANDIR)/vmsbackup.$(MANSEC)
X
X#clean:
X# sccs clean
X rm -f vmsbackup vmsbackup.shar *.o core
X#
Xshar:
X# sccs get $(DIST)
X $(SHAR) $(DIST) makefile > vmsbackup.shar
X# sccs clean
X#
X#ushar:
X# sccs get $(DIST)
X# $(SHAR) -au $(DIST) Makefile > vmsbackup.shar
X# sccs clean
End of makefile
chmod u=rw-,g=r--,o=r-- makefile

--

#include <Std_Caveats.h>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- D E P A R T M E N T O F D E F E N C E -------------------<>------------------ Defence Science & Technology Organisation

Contact Details: Gary.Speechley@dsto.defence.gov.au

Gary C Speechley CIS: [100236,2246] DSTO Australia Wk: +61 2 692 1432 ph PO Box 44 +61 2 660 0019 fx PYRMONT NSW 2009 Hm: +61 2 570 3870 ph/fx

o==()()==o Ask me about BMW Club NSW o==()()==o



This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:09:14 CDT