[net.sources] VMS <-> Unix tape transfer

neil@uk.ac.man.cs.ux (Neil Todd) (03/17/86)

wpl@burdvax.uucp was after some uucp <-> vms tape transfer software,
this might do the trick.

#! /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 the files:
#	vmsread.1
#	vmsread.c
#	vmswrite.1
#	vmswrite.c
# This archive created: Mon Mar 17 11:18:35 1986
export PATH; PATH=/bin:$PATH
echo shar: extracting "'vmsread.1'" '(3941 characters)'
if test -f 'vmsread.1'
then
	echo shar: will not over-write existing file "'vmsread.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmsread.1'
	X.TH VMSREAD 1 MAN.CS.UX
	X.SH NAME
	Xvmsread \- read a Files-11 tape
	X.SH SYNOPSIS
	X.Bvmsread 
	X[
	X.B \-cefitvD
	X] [ name ... ]
	X.SH DESCRIPTION
	X.I vmsread 
	Xreads a RSX or VMS generated Files-11 tape, converting the files
	Xto Unix format and writing the files to disc.
	XMultifile volumes with either variable length or fixed length records
	Xare the only types of Files-11 tapes handled.
	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 a
	XTape is EBCDIC IBM VB format. Convert to Ascii.
	XThis option does not handle sensibly the situation if two files
	Xhave the same names - i.e. version numbers are nonsense.
	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, however,
	Xif a file of the same name (excluding the version number) already
	Xexists in the destination directory, then rather than overwrite
	Xthe contents of that file with the tape file, the version number
	Xwill automatically be appended to the filename on tape.
	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.
	XThe default is
	X.I /dev/rmt0
	X(drive 0, raw mode, 1600 bpi).
	XThis must be a raw mode tape device.
	X.TP 8
	X.B i
	XOutput the volume id of the tape as the first line of output.
	X.TP 8
	X.B t
	XProduce a table of contents (a directory listing) on the standard output
	Xof the files on tape.
	XThe output consists of one filename per line with all alphabetic
	Xcharacters converted to lower case.
	XUnless the
	X.B c
	Xoption is also specified, the versions numbers will not be printed.
	X.TP 8
	X.B v
	XVerbose output.
	XNormally
	X.I vmsread 
	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 D
	XDebug mode.
	XAdditional information will be printed as each file is processed.
	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.
	XIf the
	X.B c
	Xoption is specified, then the filenames specified by
	X.I name
	Xmust also include the exact version number desired.
	XIf the
	X.B c
	Xoption is not specified (the default), one may not specify
	Xthe version numbers in the
	X.I name
	Xargument.
	X.SH FILES
	X/dev/rmt\fIx\fP
	X.SH SEE ALSO
	Xvmswrite(1)
	X.SH BUGS
	XBound to be some since there does not exist any one document completely
	Xdescribing the Files-11 format and since some Files-11 tapes are acceptable
	Xto VMS but not acceptable to RSX-11M.
	X.SH AUTHOR
	XThe Department of Computer Science
	X.br
	XUniversity of Manchester
	X.br
	XOxford Road
	X.br
	XManchester M13 9PL
	X.br
	XENGLAND
	X.br
	XUUCP:- ...!mcvax!ukc!uk.ac.man.cs.ux!software
	X.br
	XJANET:- software@uk.ac.man.cs.ux
	X.br
	XARPA:-  software%uk.ac.man.cs.ux@ucl.cs
	X.br
SHAR_EOF
if test 3941 -ne "`wc -c < 'vmsread.1'`"
then
	echo shar: error transmitting "'vmsread.1'" '(should have been 3941 characters)'
fi
chmod +x 'vmsread.1'
fi # end of overwriting check
echo shar: extracting "'vmsread.c'" '(22516 characters)'
if test -f 'vmsread.c'
then
	echo shar: will not over-write existing file "'vmsread.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmsread.c'
	X/*
	X * vmsread  [ -adfitv ]  [ name ... ]
	X *
	X * Read a Files-11 tape from RSX or VMS.
	X * Presently we handle multifile volumes only.
	X * Variable length records are read and written to the
	X * file named in the HDR1 label.
	X * Fixed length records are also handled correctly.
	X *
	X * NCT @ MUCS
	X * 6-SEPT-84    Added -D flag for debugging
	X * 6-SEPT-84    Fixed dodata to handle FIXLENGTH type records,
	X *                              previously this mode generated empty file names.
	X * 6-SEPT-84    Fix up name clashes
	X * Modified by CCK
	X * 12-NOV-85	Fixed problem connected with trailing ^ characters in a block.
	X *			These were always removed - even when not filler !
	X *
	X * 		!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	X *		!!					!!
	X *		!! UUCP :- ...!mcvax!ukc!man.cs.ux!neil !!
	X *		!! JANET:- neil@uk.ac.man.cs.ux		!!
	X *		!! ARPA :- neil%uk.ac.man.cs.ux@ucl.cs  !!
	X *		!!					!!
	X *		!!					!!
	X * 		!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	X */
	X
	X#include        <stdio.h>
	X#include        <ctype.h>
	X
	X#define MAXBUF          8192
	X#define MAXLINE         1024
	X#define MAXRECLEN       1024
	X#define VARLENGTH       1
	X#define FIXLENGTH       2
	X#define TEOF            0               /* Version 7 */
	X
	Xchar    bufin[MAXBUF], bufout[MAXBUF], fname[17], fnamesav[17],
	X        ftype[4];
	Xchar    **argvsave;
	Xchar    *pname;
	Xint     tapefd, fdout, rformat, length, procfile, reclength,
	X        numfnames, bufiptr, bufoptr;
	Xint     crlf = 0;       /* if true, terminate each line with cr/lf */
	X
	Xint     verbose = 0;    /* verbose output */
	Xint     procall = 1;    /* process all files on tape */
	Xint     versionf = 0;   /* append version# to filename */
	Xint     tableofc = 0;   /* table of contents (directory listing) only */
	Xint     prvolid = 0;    /* print volume id as first line out */
	Xint     select = 0;     /* remaining args are filenames */
	Xint     allextents = 0; /* process all file types */
	Xint     afrome = 0;     /* convert to ascii from ebcdic */
	Xint debug = 0;          /* debug mode on */
	Xint nctmode = 0;        /* fix up name clashes */
	X
	Xchar    *usefile;
	Xchar    tapename[] = "/dev/rmt0";       /* default tape device */
	X
	Xmain(argc, argv)
	Xint     argc;
	Xchar    *argv[];
	X{
	X        char    *s;
	X
	X        pname = argv[0];
	X        usefile = tapename;
	X        while (--argc > 0  &&  (*++argv)[0] == '-')
	X                for (s = argv[0]+1; *s != '\0'; s++)
	X                        switch (*s) {
	X                        case 'c':       /* complete filename, append version# */
	X                                versionf = 1;
	X                                break;
	X
	X                        case 'e':       /* process all file extensions */
	X                                allextents = 1;
	X                                break;
	X
	X                        case 'f':       /* use next argument as filename */
	X                                if (--argc <= 0)
	X                                   error("-f option requires another argument", NULL);
	X                                usefile = *++argv;
	X                                break;
	X
	X                        case 'i':       /* print volume id */
	X                                prvolid = 1;
	X                                break;
	X
	X                        case 't':       /* produce table of contents (directory listing) only */
	X                                tableofc = 1;
	X                                procall = select = 0;
	X                                break;
	X
	X                        case 'v':       /* verbose option */
	X                                verbose = 1;
	X                                break;
	X
	X                        case 'a':       /* convert to ascii */
	X                                afrome = 1;
	X                                break;
	X
	X                        case 'D':       /* Debug on */
	X                                debug = 1;
	X                                break;
	X
	X                        case 'n':       /* fix name clashes */
	X                                nctmode = 1;
	X                                break;
	X
	X                        default:
	X                                error("illegal option %c", *s);
	X                        }
	X
	X        if (argc > 0)  {        /* additional arguments are names of files
	X                                   on the tape to process */
	X                if (tableofc)
	X                        error("can't specify filename arguments with -t option", NULL);
	X                procall = 0;
	X                select = 1;     
	X                numfnames = argc;
	X                argvsave = argv;
	X        }
	X
	X        if ((tapefd = open(usefile, 0)) == -1)
	X                error("can't open %8s", usefile);
	X        length = read(tapefd, bufin, MAXBUF);
	X        if (afrome) ascii(bufin,length) ; /* convert to ascii */
	X        if (length == TEOF)
	X                error("unexpected eof on tape before VOL1", NULL);
	X        if (length != 80  ||  strcmp("VOL1", bufin))
	X                error("missing VOL1", NULL);
	X        vol1();
	X
	X        while ((length = read(tapefd, bufin, MAXBUF)) != TEOF)  {
	X                if (afrome) ascii(bufin,length) ;
	X                if (length != 80  ||  strcmp("HDR1", bufin))
	X                        error("missing HDR1", NULL);
	X                hdr1();
	X
	X                if ((length = read(tapefd, bufin, MAXBUF)) == TEOF)
	X                        error("unexpected eof on tape after HDR1", NULL);
	X                if (afrome) ascii(bufin,length) ;
	X                if (length != 80  ||  strcmp("HDR2", bufin))
	X                        error("missing HDR2", NULL);
	X                hdr2();
	X
	X                while ((length = read(tapefd, bufin, MAXBUF)) != TEOF)
	X                        ;  /* skip over any other HDRn labels, up to
	X                              and over end-of-file */
	X
	X                if (procfile)  {  /* process this file */
	X                        bufiptr = MAXBUF + 1;
	X                        dodata();
	X                } else
	X                        length = read(tapefd, bufin, MAXBUF);
	X                        /* skip into data portion */
	X
	X                if (length != TEOF)
	X                        while ((length = read(tapefd, bufin, MAXBUF)) != TEOF)
	X                                ;  /* skip over any unprocessed data
	X                                      records, up to and over the eof
	X                                      following the data records */
	X
	X                if ((length = read(tapefd, bufin, MAXBUF)) == TEOF)
	X                        error ("unexpected eof on tape before EOF1", NULL);
	X                if (afrome) ascii(bufin,length) ;
	X                if (length != 80  ||  strcmp("EOF1", bufin))
	X                        error("missing EOF1", NULL);
	X                eof1();
	X
	X                if ((length = read(tapefd, bufin, MAXBUF)) == TEOF)
	X                        error("unexpected eof on tape after EOF1", NULL);
	X                if (afrome) ascii(bufin,length) ;
	X                if (length != 80  ||  strcmp("EOF2", bufin))
	X                        error("missing EOF2", NULL);
	X                eof2();
	X
	X                while((length = read(tapefd, bufin, MAXBUF)) != TEOF)
	X                        ;  /* skip over any additional EOFn labels,
	X                              up to and over the eof preceding the
	X                              next HDR1 label */
	X        }
	X        exit(0);
	X}
	X
	Xerror(str1, str2)       /* print error message and die */
	Xchar    *str1, *str2;
	X{
	X        fprintf(stderr, "%s: ", pname);
	X        fprintf(stderr, str1, str2);
	X        fprintf(stderr, "\n");
	X}
	X
	Xstrcpy(str1, str2)      /* copy str2 to str1 */
	Xregister char   *str1, *str2;
	X{
	X        while (*str1++ = *str2++)
	X                ;
	X}
	X
	Xstrcmp(str1, str2)      /* compare two character strings.
	X                           return <0  if str1 < str2
	X                                   0  if str1 = str2
	X                                  >0  if str1 > str2
	X                           str1 must be zero terminated, str2 need not
	X                           be zero terminated.  If str1 is a null string
	X                           we return 1 as they are not equal */
	Xregister char   *str1, *str2;
	X{
	X        if (*str1 == '\0')
	X                return(1);
	X
	X        for ( ; *str1 == *str2;  str1++, str2++)
	X                if (*str1 == '\0')
	X                        return(0);
	X        if (*str1 == '\0')
	X                return(0);
	X        return(*str1 - *str2);
	X}
	X
	Xnamecmp(str)    /* Compare the filename pointed to by str with
	X                   the list of filenames specified by the -f option.
	X                   Return 1 if file is to be processed,
	X                          0  if file is not to be processed */
	Xregister char   *str;
	X{
	X        register char   **argv;
	X        register int    i;
	X
	X        for (i = 1, argv = argvsave; i <= numfnames; i++, argv++)
	X                if (strcmp(*argv, str) == 0)
	X                        return(1);
	X        return(0);
	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                ""              /* null string terminates list */
	X        };
	X        register int    i;
	X
	X        i = -1;
	X        while (*type[++i])
	X                if (strcmp(str, type[i]) == 0)
	X                        return(0);      /* found a match, file to be ignored */
	X        return(1);                      /* no match found */
	X}
	X
	Xlower(str)      /* convert any upper case alphabetics in str to lower */
	Xregister char   *str;
	X{
	X        for ( ; *str != '\0'; str++)
	X                if (isupper(*str))
	X                        *str = tolower(*str);
	X}
	X
	Xvol1()          /* process VOL1 label */
	X{
	X        bufin[10] = '\0';
	X        if (prvolid)
	X                printf("volume label: %6s\n", &bufin[4]);
	X}
	X
	Xhdr1()          /* process HDR1 label */
	X{
	X        register int    in, out, count;
	X        int             genver, gennum, version, digit, sig, typeptr;
	X        char            temp;
	X
	X        out = 0;
	X        /* copy up to period or 9 characters */
	X        for (in = 4; in <= 12; in++)
	X                if (bufin[in] == '.')
	X                        break;
	X                else
	X                        if (bufin[in] != ' ')
	X                                fname[out++] = bufin[in];
	X
	X        fname[out++] = '.';
	X        if (bufin[in] == '.')
	X                in++;
	X
	X        typeptr = 0;
	X        /* copy up to space or 3 character file type */
	X        for (count = 1;  count <= 3;  count++)
	X                if (bufin[in] == ' ')
	X                        break;
	X                else  {
	X                        fname[out++] = bufin[in];
	X                        ftype[typeptr++] = bufin[in++];
	X                }
	X        
	X        ftype[typeptr] = '\0';  /* null terminte file type */
	X        lower(ftype);           /* convert file type to lower case */
	X        fname[out] = '\0';      /* null terminate filename */
	X        lower(fname);           /* convert filename to lower case */
	X
	X        if (versionf || (access(fname, 6) != -1))  {    /* process version# */
	X                temp = bufin[39];
	X                bufin[39] = '\0';
	X                gennum = atoi(&bufin[35]);
	X                bufin[39] = temp;
	X                temp = bufin[41];
	X                bufin[41] = '\0';
	X                genver = atoi(&bufin[39]);
	X                bufin[41] = temp;
	X                version = (gennum - 1)*64 + genver + 1;
	X                if (!nctmode) fname[out++] = ':';       /* no : if resolving clashes */
	X                sig = 0;
	X                digit = version / 4096;
	X                if (digit)  {
	X                        fname[out++] = digit + '0';
	X                        sig = 1;
	X                }
	X                digit = (version / 512) % 8;
	X                if (digit || sig)  {
	X                        fname[out++] = digit + '0';
	X                        sig = 1;
	X                }
	X                digit = (version / 64) % 8;
	X                if (digit || sig)  {
	X                        fname[out++] = digit + '0';
	X                        sig = 1;
	X                }
	X                digit = (version / 8) % 8;
	X                if (digit || sig)
	X                        fname[out++] = digit + '0';
	X                digit = version % 8;
	X                fname[out++] = digit + '0';
	X                fname[out] = '\0';      /* null terminate filename */
	X                if (nctmode)
	X                /* resolve name clashes */
	X                {
	X                        fname[out+1] = '\0';
	X                        do
	X                        {
	X                                count = open(fname,1);
	X                                if (count >= 0)
	X                                /* file exists */
	X                                {
	X                                        close(count);
	X                                        if (fname[out]>= 'A' && fname[out] < 'Z')
	X                                                fname[out]++;
	X                                        else
	X                                        if (fname[out] == 'Z')
	X                                        {
	X                                                printf("Unresolvable ");
	X                                                break;
	X                                        }
	X                                        else fname[out] = 'A';
	X                                }
	X                        } while (count != -1);
	X                        if (!fname[out]) printf("Name clash ");
	X                }
	X        }
	X
	X        procfile = procall;
	X        if (select)
	X                /* if we are selecting files by name, compare this name
	X                   with the list specified by operator */
	X                procfile = namecmp(fname);
	X
	X        else if (procall & (!allextents))
	X                /* if we are processing all files and we are to be selective
	X                   about the file types, then see if this type is to be
	X                   processed or ignored */
	X                procfile = typecmp(ftype);
	X
	X        if ((verbose && procfile) || (tableofc))
	X                printf("%s\n", fname);
	X
	X        if (procfile)
	X                if ((fdout = creat(fname, 0644)) == -1)
	X                        error("HDR1: error creating file %s", fname);
	X}
	X
	Xhdr2()          /* process HDR2 label */
	X{
	X        register int    temp;
	X
	X        if (afrome) return;
	X        if (bufin[4] == 'F')  {
	X                rformat = FIXLENGTH;
	X                temp = bufin[15];
	X                bufin[15] = '\0';
	X                reclength = atoi(&bufin[10]);
	X                if (debug)      printf("Fixed length records size %d\n",reclength);
	X                bufin[15] = temp;
	X                if (reclength < 0  ||  reclength > MAXRECLEN)
	X                        error("HDR2: invalid record length", NULL);
	X        }
	X        else if (bufin[4] == 'D') {
	X                rformat = VARLENGTH;
	X                if (bufin[36] == ' ')
	X                        crlf = 1;       /* append cr/lf to every record */
	X                else if (bufin[36] == 'M')
	X                        crlf = 0;       /* record has all forms control */
	X                else if (bufin[36] == 'A')
	X                        crlf = 1;       /* record has Fortran carriage control,
	X                                           leave first char alone, add cr/lf */
	X                else
	X                        error("HDR2: unknown form control character %c", bufin[36]);
	X        } else
	X                error("HDR2: unknown record type %c", bufin[4]);
	X
	X}
	X
	Xeof1()          /* process EOF1 label */
	X{
	X}
	X
	Xeof2()          /* process EOF2 label */
	X{
	X}
	X
	Xgbytea()        /* get next ascii byte from tape bufinfer */
	X{
	X        if (bufiptr >= length)  {  /* read next tape record */
	X                if ((length = read(tapefd, bufin, MAXBUF)) == TEOF)
	X                        return(-1);  /* eof on tape */
	X                bufiptr = 0;
	X        }
	X
	X        return(bufin[bufiptr++] & 255);
	X}
	X
	Xgbyteb()        /* get next binary byte from bufinfer */
	X{
	X        if (bufiptr >= length)  {  /* read next tape record */
	X                if ((length = read(tapefd, bufin, MAXBUF)) == TEOF)
	X                        return(-1);  /* eof on tape */
	X                bufiptr = 0;
	X        }
	X        return(bufin[bufiptr++] & 255);
	X}
	X
	Xgetcount()      /* get 4-byte ascii decimal/bin count field */
	X{
	X        char          cstr[5];
	X        register int  data, i;
	X
	X        if (afrome)
	X		data = gbyteb();
	X	else
	X		while ((data = gbytea()) == '^') ;
	X	if (data == -1)
	X                return(-1);
	X        cstr[0] = data;
	X
	X        for (i = 1;  i <= 3;  i++)  {
	X                if ((data = (afrome?(gbyteb()):gbytea())) == -1)
	X                        error("getcount: unexpected end of data", NULL);
	X                cstr[i] = data;
	X        }
	X        cstr[4] = '\0';
	X        return(afrome?(binnum(cstr)):atoi(cstr));
	X}
	X
	Xdodata()        /* process the data */
	X{
	X        register int  data, count, i;
	X
	X        if (afrome) { doedata() ; return; }
	X        bufoptr = 0;
	X        if (rformat == VARLENGTH)  {
	X                while ((count = getcount()) != -1) {
	X                        /* process one line at a time */
	X                        count -= 4;
	X                        if (count < 0  ||  count >= MAXLINE)
	X                                error("dodata: invalid count", NULL);
	X                        for (i = 1;  i <= count;  i++)  {
	X                                if ((data = gbytea()) == -1)
	X                                        error("dodata: unexpected end of data", NULL);
	X                                else  {
	X                                        bufout[bufoptr++] = data;
	X                                        if (bufoptr >= MAXBUF)  {
	X                                           if (write(fdout, bufout, bufoptr) != bufoptr)
	X                                                error("dodata: error writing data", NULL);
	X                                           bufoptr = 0;
	X                                        }
	X                                }
	X                        }
	X                        if (crlf)
	X                                bufout[bufoptr++] = '\n'; /* terminate line */
	X                        if (bufoptr >= MAXBUF)  {
	X                           if (write(fdout, bufout, bufoptr) != bufoptr)
	X                              error("dodata: error writing data", NULL);
	X                           bufoptr = 0;
	X                        }
	X                }
	X        }
	X        else if (rformat == FIXLENGTH)  {
	X        /* FIXLENGTH records didn't work previously because someone forgot
	X         * the brackets after the while statement - this meant that one
	X         * generated all the correct file names but no data
	X         * NCT fixed this.
	X         */
	X                while ((length = read(tapefd, bufin, MAXBUF)) != TEOF)
	X                {
	X                        if (afrome) ascii(bufin,length) ;
	X                        if (write(fdout, bufin, length) == -1)
	X                                error("dodata: error writing data", NULL);
	X                }
	X        }
	X        else error("dodata: invalid rformat value", NULL);
	X
	X        if (bufoptr)  /* any data left to flush out */
	X                if (write(fdout, bufout, bufoptr) != bufoptr)
	X                        error("dodata: error writing data", NULL);
	X        if (close(fdout) == -1)
	X                error("dodata: error closing disc file", NULL);
	X}
	Xchar    etoa[] = {
	X        0000,0001,0002,0003,0234,0011,0206,0177,
	X        0227,0215,0216,0013,0014,0015,0016,0017,
	X        0020,0021,0022,0023,0235,0205,0010,0207,
	X        0030,0031,0222,0217,0034,0035,0036,0037,
	X        0200,0201,0202,0203,0204,0012,0027,0033,
	X        0210,0211,0212,0213,0214,0005,0006,0007,
	X        0220,0221,0026,0223,0224,0225,0226,0004,
	X        0230,0231,0232,0233,0024,0025,0236,0032,
	X        0040,0240,0241,0242,0243,0244,0245,0246,
	X        0247,0250,0133,0056,0074,0050,0053,0041,
	X        0046,0251,0252,0253,0254,0255,0256,0257,
	X        0260,0261,0135,0044,0052,0051,0073,0136,
	X        0055,0057,0262,0263,0264,0265,0266,0267,
	X        0270,0271,0174,0054,0045,0137,0076,0077,
	X        0272,0273,0274,0275,0276,0277,0300,0301,
	X        0302,0140,0072,0043,0100,0047,0075,0042,
	X        0303,0141,0142,0143,0144,0145,0146,0147,
	X        0150,0151,0304,0305,0306,0307,0310,0311,
	X        0312,0152,0153,0154,0155,0156,0157,0160,
	X        0161,0162,0313,0314,0315,0316,0317,0320,
	X        0321,0176,0163,0164,0165,0166,0167,0170,
	X        0171,0172,0322,0323,0324,0325,0326,0327,
	X        0330,0331,0332,0333,0334,0335,0336,0337,
	X        0340,0341,0342,0343,0344,0345,0346,0347,
	X        0173,0101,0102,0103,0104,0105,0106,0107,
	X        0110,0111,0350,0351,0352,0353,0354,0355,
	X        0175,0112,0113,0114,0115,0116,0117,0120,
	X        0121,0122,0356,0357,0360,0361,0362,0363,
	X        0134,0237,0123,0124,0125,0126,0127,0130,
	X        0131,0132,0364,0365,0366,0367,0370,0371,
	X        0060,0061,0062,0063,0064,0065,0066,0067,
	X        0070,0071,0372,0373,0374,0375,0376,0377,
	X};
	Xascii(buff,length)
	Xchar *buff;
	Xint length;
	X{
	X        register int i, c;
	X        for (i=0; i<length; i++) {
	X                c = buff[i];
	X                c &= 0xff;
	X                buff[i] = etoa[c] & 0377;
	X        }
	X}
	X
	Xdoedata ()
	X{ int blocklength,reclength ;
	X
	X                while ((blocklength = getcount()) != -1) {
	X                blocklength-=4 ;
	X                while (blocklength) {
	X                        blocklength-=(reclength=getcount());
	X                        if (reclength == -1) error("unexpected end of file", NULL);
	X                        ascii(&bufin[bufiptr],reclength-=4);
	X                        if ((write(fdout,&bufin[bufiptr],reclength) != reclength) || (write(fdout,"\n",1) != 1))
	X                                error("doedata: error writing data",NULL);
	X                bufiptr += reclength;
	X                }}
	X        if (close(fdout) == -1)
	X                error("doedata: error closing file",NULL);
	X}
	X
	Xbinnum(c)
	Xchar *c;
	X{ register int j,k;
	X        j = c[0];
	X        j = (j & 0xff) << 8;
	X        k = c[1]; k &= 0xff ;
	X        return k+j;
	X}
SHAR_EOF
if test 22516 -ne "`wc -c < 'vmsread.c'`"
then
	echo shar: error transmitting "'vmsread.c'" '(should have been 22516 characters)'
fi
chmod +x 'vmsread.c'
fi # end of overwriting check
echo shar: extracting "'vmswrite.1'" '(3449 characters)'
if test -f 'vmswrite.1'
then
	echo shar: will not over-write existing file "'vmswrite.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmswrite.1'
	X.TH VMSWRITE 1 MAN.CS.UX
	X.SH NAME
	Xvmswrite \- write a Files-11 tape
	X.SH SYNOPSIS
	X.B vmswrite 
	X[
	X.B \-abfnrv
	X] name ...
	X.SH DESCRIPTION
	X.I vmswrite 
	Xwrites a Files-11 tape which may be read on an RSX or VMS system.
	XA multifile, single volume tape is written, consisting of the
	Xspecified files.
	XThe tape is created as though it was written on a PDP-11 with RSX-11M.
	XTapes written by this program have been successfully read under
	XRSX-11M (Version 3.2) and VMS (Version 2.2, 2.3 and 2.4).
	X.PP
	XThe
	X.B -a
	Xoption will append the files to the end of an existing Files-11
	Xtape, that is, the VOL1 record is not written.
	XTo do this, the tape is rewound and the files are counted, up
	Xto the double end-of-file that marks the logical end-of-tape.
	XThe new files are appended to the tape, with the file number
	Xentry of the HDR1 label picking up where the previous files left off.
	X.PP
	XThe
	X.B -b
	Xoption will use the next command line argument as the tape block
	Xsize in bytes, rather than the default of 2048 bytes.
	XAny value greater than 512 will force one to use something like
	X``/BS:2048.'' on the PIP command line under RSX.
	X.PP
	XThe
	X.B -f
	Xoption will use the next command line argument as the tape device,
	Xrather than the default of
	X.I /dev/rmt0
	X(drive 0, raw mode, 1600 bpi).
	XThe argument must be a tape device name.
	X.PP
	XThe
	X.B -n
	Xoption will not rewind the tape at the end.
	XIf this option is not specified, then by default the tape is rewound
	Xwhen finished.
	XThis option is of no practical interest.
	X.PP
	XThe
	X.B -r
	Xoption will use the next command line argument as the record size,
	Ximplying that fixed-length records are to be written.
	XThe block size (specified by the
	X.B -b
	Xoption, or the default of 2048 bytes), must be evenly divisible
	Xby the fixed-length record size.
	XFurthermore, the size of the UNIX file must be evenly divisible
	Xby the fixed-length record size.
	XThis option specifies that
	X.I all
	Xfiles being written to the tape are fixed-length records having the
	Xsame size records.
	XIf you wish to intermix variable-length records (normal text files)
	Xwith fixed-length records, use the
	X.B -a
	Xoption and execute
	X.I vmswrite 
	Xas many times as needed.
	X.PP
	XThe
	X.B -v
	Xoption may be used to produce a verbose output, with the name
	Xof every file being written to the terminal as the file is written
	Xto tape.
	XNormally
	X.B vmswrite 
	Xdoes its work silently.
	X.PP
	XAll lower case alphabetics in the filenames are converted
	Xto upper case, since this is required by RSX (but not VMS!).
	XAll directory prefixes are removed from the filenames
	X(all characters up to, and including, the final '/') so that
	Xa filename of the form "/usr/xyz/file1.f" would have a filename
	Xon the HDR1 label of "FILE1.F".
	XThe volume id of the tape is set to the six character string
	X"BSD4.2".
	XThe creation date and the expiration date on the HDR1 label are set
	Xto the date of last modification of the UNIX file.
	XThis date corresponds to the date shown with the "ls -l" command.
	X.SH SEE ALSO
	Xvmsread(1)
	X.SH FILES
	X/dev/rmt?
	X.br
	X/dev/nrmt?
	X.SH BUGS
	XThe UNIX protection of the file is not converted to the DEC
	Xequivalent, instead the protection of the files on tape are
	X"RWED" for everybody.
	XIt should be possible to change the volume id.
	X.SH AUTHOR
	XThe Department of Computer Science
	X.br
	XUniversity of Manchester
	X.br
	XOxford Road
	X.br
	XManchester M13 9PL
	X.br
	XENGLAND
	X.br
	XUUCP:- ...!mcvax!ukc!uk.ac.man.cs.ux!software
	X.br
	XJANET:- software@uk.ac.man.cs.ux
	X.br
	XARPA:-  software%uk.ac.man.cs.ux@ucl.cs
	X.br
SHAR_EOF
if test 3449 -ne "`wc -c < 'vmswrite.1'`"
then
	echo shar: error transmitting "'vmswrite.1'" '(should have been 3449 characters)'
fi
chmod +x 'vmswrite.1'
fi # end of overwriting check
echo shar: extracting "'vmswrite.c'" '(14357 characters)'
if test -f 'vmswrite.c'
then
	echo shar: will not over-write existing file "'vmswrite.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'vmswrite.c'
	X/*
	X * vmswrite [ -abfinrv ] name ...
	X *
	X * Write a Files-11 tape.
	X * Variable length records (text files) are the common case.
	X * If the "-b" option is specified, then all the files will be written
	X * as fixed length records.  We further require that the fixed length record
	X * size be evenly divisible into the block size (2048).  Typically, the
	X * only use for fixed length records will be Forth blocks.
	X *
	X * Modified by NCT @ MUCS
	X *
	X *
	X * 		!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	X *		!!					!!
	X *		!! UUCP :- ...!mcvax!ukc!man.cs.ux!neil !!
	X *		!! JANET:- neil@uk.ac.man.cs.ux		!!
	X *		!! ARPA :- neil%uk.ac.man.cs.ux@ucl.cs  !!
	X *		!!					!!
	X *		!!					!!
	X * 		!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	X */
	X
	X
	X#include        <stdio.h>
	X#include        <ctype.h>
	X#include        <sys/time.h>
	X#include        <sys/types.h>
	X#include        <sys/stat.h>
	X
	X#define LINELENGTH      512     /* line length of input */
	X#define TAPEBUFFLEN     2050    /* tape buffer length */
	X#define TEOF            0       /* Version 7 */
	X#define TERR            (-1)    /* Version 7 */
	X#define VARLENGTH       1       /* write variable length records */
	X#define FIXLENGTH       2       /* write fixed length records */
	X
	Xchar    vol1buff[82], hdr1buff[82], hdr2buff[82];
	Xchar    line[LINELENGTH], tapebuff[TAPEBUFFLEN];
	X
	Xchar    *usefile;
	Xchar    deftape[] = "0";                /* default is /dev/rmt0 & /dev/nrmt0 */
	Xchar    tapename[12]  = "/dev/nrmt";    /* default output device */
	Xchar    tapername[12] = "/dev/rmt";     /* tape name for rewinding at end */
	X                                        /* suffixes are added later */
	X
	Xchar    vollabel[]      = "BSD4.3";
	Xchar    access[]        = " ";          /* DEC file security */
	Xchar    volowner[]      = "D%B4444300300"; /* written on PDP-11, all access */
	Xchar    credate[]       = "81001";
	Xchar    expdate[]       = "91001";
	Xchar    syscode[]       = "DECFILE11";  /* written on PDP-11 */
	Xchar    recformat[]     = "D";          /* variable length records */
	Xchar    formcontrol[]   = " ";          /* lf,cr to be inserted after */
	X
	Xint     rformat = VARLENGTH;
	Xint     rlength = 0;                    /* record length for fixed length records */
	Xint     tapefd;
	Xint     append = 0;
	Xint     norewind = 0;
	Xint     verbose = 0;
	Xchar    *pname;
	X
	Xint     filenum;                /* file # on tape */
	Xint     blklength = 2048;       /* #bytes of data per tape buffer */
	Xint     bufflen;                /* #bytes of data in tape buffer */
	Xchar    *buffptr;               /* pointer to next byte to store in tape buff */
	Xint     blkcount;               /* count of data records per tape file */
	X
	Xmain(argc, argv)
	Xint   argc;
	Xchar  *argv[];
	X{
	X        FILE   *fp, *fopen();
	X        char   *s;
	X        int    i;
	X
	X        usefile = deftape;
	X        pname = argv[0];
	X        while ((--argc > 0) && ((*++argv)[0] == '-'))
	X                for (s = argv[0]+1; *s != '\0'; s++)
	X                        switch (*s) {
	X                        case 'a':               /* append to tape, no vol1 */
	X                                append = 1;
	X                                break;
	X
	X                        case 'b':               /* next arg is tape block length */
	X                                if (--argc <= 0)
	X                                   error("-b option requires another argument", NULL);
	X                                blklength = atoi(*++argv);
	X                                if (blklength < 0)
	X                                        error("invalid block length with -b option", NULL);
	X                                break;
	X
	X                        case 'f':               /* next arg is tape name */
	X                                if (--argc <= 0)
	X                                   error("-f option requires another argument", NULL);
	X                                usefile = *++argv;
	X                                break;
	X
	X                        case 'n':
	X                                norewind = 1;
	X                                break;
	X
	X                        case 'r':               /* next arg is fixed length record size */
	X                                if (--argc <= 0)
	X                                   error("-r option requires another argument", NULL);
	X                                rlength = atoi(*++argv);
	X                                if (rlength < 0)
	X                                   error("invalid record length with -r option", NULL);
	X                                rformat = FIXLENGTH;
	X                                break;
	X
	X                        case 'v':
	X                                verbose = 1;    /* verbose output */
	X                                break;
	X
	X                        default:
	X                                error("illegal option %c", *s);
	X                        }
	X
	X        if (rformat == FIXLENGTH) {
	X                if (rlength > blklength)
	X                        error("invalid record/block length", NULL);
	X                if (blklength % rlength)
	X                        error("block length must be a multiple of the record length", NULL);
	X        }
	X
	X        nametape();
	X        if (append)
	X                filenum = findleot() / 3;
	X        else    
	X                filenum = 0;
	X
	X        if (argc <= 0)
	X                error("filename argument(s) required", NULL);
	X        if ((tapefd = open(tapename, 1)) < 0)
	X                error("can't open %s", tapename);
	X        i = 0;
	X        do {
	X                if ((argc > 0) && (fp = fopen(argv[i], "r")) == NULL) {
	X                        fprintf(stderr, "%s: can't open %s\n", pname, argv[i]);
	X                        continue;
	X                }
	X                if ((filenum++ == 0) && (!append))
	X                        vol1();
	X                hdr1(fp, argv[i]);
	X                hdr2();
	X                tapemark();
	X                (rformat == VARLENGTH) ? vardata(fp) : fixdata(fp);
	X                tapemark();
	X                eof1();
	X                eof2();
	X                tapemark();
	X                fclose(fp);
	X        } while (++i < argc);
	X        if (!norewind)
	X                rewind();
	X}
	X
	Xvol1()
	X{
	X        char *ptr;
	X
	X        ptr = vol1buff;
	X        sprintf(ptr, "VOL1%-6.6s%1s%26s%-13.13s %28s1",
	X                vollabel, access, " ", volowner, " ");
	X
	X        if (write(tapefd, vol1buff, 80) != 80)
	X                error("error writing VOL1", NULL);
	X}
	X
	Xhdr1(fp, filename)
	XFILE  *fp;
	Xchar  *filename;
	X{
	X        char            *ptr;
	X        int             filesectnum, gennum, genver;
	X        long            cretime;
	X        struct stat     statbuff;
	X        struct tm       *tmptr, *localtime();
	X
	X        if (fstat(fileno(fp), &(statbuff)) < 0)
	X                error("error from fstat", NULL); /* get info on the file */
	X        cretime = statbuff.st_ctime;            /* get creation time */
	X        tmptr = localtime(&cretime);
	X        sprintf(credate, "%02d%03d", tmptr->tm_year, (tmptr->tm_yday)+1);
	X        strcpy(expdate, credate);               /* set exp date = cre date */
	X
	X        filesectnum = 1;        /* we only write single volume tapes */
	X        blkcount    = 0;        /* start counter of data blocks in this file */
	X        gennum      = 1;        /* generation # */
	X        genver      = 0;        /* generation version */
	X                                /* above two lines give version number of 1 */
	X        rmprefix(filename);     /* remove any directory prefixes */
	X        upper(filename);        /* convert filename to upper case */
	X        if (verbose)
	X                fprintf(stderr, "%s\n", filename);
	X
	X        ptr = hdr1buff;
	X        sprintf(ptr, "HDR1%-17.17s%-6.6s%04d%04d%04d%02d%6s%6s%1s%06d%-13.13s%7s",
	X                filename, vollabel, filesectnum, filenum,
	X                gennum, genver, credate, expdate,
	X                access, blkcount, syscode, " ");
	X
	X        if (write(tapefd, hdr1buff, 80) != 80)
	X                error("error writing HDR1", NULL);
	X}
	X
	Xhdr2()          /* write the header 2 record */
	X{
	X        char *ptr;
	X        int  reclength, buffoffset;
	X
	X        if (rformat == VARLENGTH) {
	X                reclength = 512;
	X                recformat[0] = 'D';
	X                formcontrol[0] = ' ';
	X        } else {
	X                reclength = rlength;
	X                recformat[0] = 'F';
	X                formcontrol[0] = 'M';
	X        }
	X        buffoffset = 0;
	X        ptr = hdr2buff;
	X        sprintf(ptr, "HDR2%s%05d%05d%21s%1s%13s%02d%28s",
	X                recformat, blklength, reclength, " ",
	X                formcontrol, " ", buffoffset, " ");
	X
	X        if (write(tapefd, hdr2buff, 80) != 80)
	X                error("error writing HDR2", NULL);
	X}
	X
	Xeof1()
	X{
	X        strncpy(hdr1buff, "EOF1", 4);
	X        sprintf(hdr1buff+54, "%06d%-13.13s%7s", blkcount,syscode," ");
	X
	X        if (write(tapefd, hdr1buff, 80) != 80)
	X                error("error writing EOF1", NULL);
	X}
	X
	Xeof2()
	X{
	X        strncpy(hdr2buff, "EOF2", 4);
	X
	X        if (write(tapefd, hdr2buff, 80) != 80)
	X                error("error writing EOF2", NULL);
	X
	X}
	X
	Xvardata(fp)     /* write an entire file of variable length records to tape */
	XFILE *fp;
	X{
	X        char     *fgets();
	X        register int  length;
	X
	X        buffptr = &tapebuff[0];
	X        bufflen = 0;
	X        while (fgets(line, LINELENGTH, fp) != NULL) {
	X                length = strlen(line) + 3;
	X                                /* -1 for newline at end */
	X                                /* +4 for our four count bytes at front */
	X                if ((bufflen + length) > blklength)
	X                        writebuff(blklength);
	X
	X                sprintf(buffptr, "%04d", length);
	X                buffptr += 4;
	X                strncpy(buffptr, line, length-4);
	X                buffptr += (length - 4);
	X                bufflen += length;
	X        }
	X        writebuff(blklength);
	X}
	X
	Xfixdata(fp)             /* write a file of fixed length records to tape */
	XFILE  *fp;
	X{
	X        int     fd, nperblock, recnum, nbytes;
	X
	X        recnum = 0;
	X        fd = fileno(fp);        /* avoid the stdio library for binary data */
	X        nperblock = blklength / rlength;
	X        buffptr = &(tapebuff[0]);
	X        bufflen = 0;
	X
	X        while (1) {
	X                nbytes = read(fd, buffptr, rlength);
	X                if (nbytes == 0)
	X                        break;          /* end of file */
	X                if (nbytes < 0)
	X                        error("fixdata: read error", NULL);
	X                if (nbytes != rlength)
	X                        error("fixdata: didn't read enough", NULL);
	X                buffptr += rlength;
	X                bufflen += rlength;
	X                if ((++recnum % nperblock) == 0)
	X                        writebuff(blklength);
	X        }
	X        if (bufflen > 0)
	X                writebuff(bufflen);
	X                /* Final record of fixed length data may be smaller than
	X                   the block length. */
	X}
	X        
	Xwritebuff(reclen)
	Xint  reclen;    /* record length to write */
	X{
	X        register int  i, fillchar;
	X
	X        if (bufflen <= 0)
	X                return;
	X
	X        fillchar = (rformat == VARLENGTH) ? '^' : '\0';
	X        for (i = bufflen; i < reclen; i++)
	X                *buffptr++ = fillchar;
	X
	X        if (write(tapefd, tapebuff, reclen) != reclen)
	X                error("error writing data record", NULL);
	X
	X        buffptr = &tapebuff[0];
	X        bufflen = 0;
	X        blkcount++;
	X}
	X
	Xtapemark()
	X{
	X        if (close(tapefd) < 0)
	X                error("error closing tape", NULL);
	X        if ((tapefd = open(tapename, 1)) < 0)
	X                error("error opening tape after close", NULL);
	X}
	X
	Xrewind()                /* rewind the tape */
	X{
	X        if (close(tapefd) < 0)
	X                error("error closing tape", NULL);
	X        if ((tapefd = open(tapername, 0)) < 0)
	X                error("error opening %s", NULL);
	X        close(tapefd);
	X}
	X
	Xerror(str1, str2)
	Xchar *str1, *str2;
	X{
	X        fprintf(stderr, "%s: ", pname);
	X        fprintf(stderr, str1, str2);
	X        fprintf(stderr, "\n");
	X        exit(1);
	X}
	X
	Xupper(str)
	Xchar *str;
	X{
	X        for ( ; *str != '\0'; str++)
	X                if (islower(*str))
	X                        *str = toupper(*str);
	X}
	X
	Xrmprefix(filename)
	Xchar    *filename;
	X{
	X        register char  *ptr;
	X
	X        if ((ptr = rindex(filename, '/')) == 0)
	X                return;
	X
	X        strcpy(filename, ++ptr);
	X}
	X
	Xfindleot()              /* find the logical end-of-tape */
	X{
	X        register int  end, nfile, nread;
	X        int           numfiles;
	X
	X        /* first rewind the tape, just in case */
	X        if ((tapefd = open(tapername, 0)) < 0)
	X                error("can't open %s", tapername);
	X        if (close(tapefd) < 0)
	X                error("error closing %s", tapername);
	X        if ((tapefd = open(tapername, 0)) < 0)
	X                error("can't open %s", tapername);
	X
	X        /* now read up to double eof, counting files */
	X        end   = 0;
	X        nfile = 1;
	X        while (end != 2) {              /* stop on double eof */
	X                if ((nread = read(tapefd, tapebuff, TAPEBUFFLEN)) == TEOF) {
	X                        nfile++;
	X                        end++;
	X                } else if (nread == TERR)
	X                        fprintf(stderr, "read error; file: %4d\n", nfile);
	X                else
	X                        end = 0;
	X        }
	X
	X        if (close(tapefd) < 0)
	X                error("error closing %s", tapername);
	X        if ((tapefd = open(tapename, 0)) < 0)
	X                error("can't open %s", tapename);
	X
	X        /* now read up to, but not over, the double eof */
	X        numfiles = nfile - 1;           /* number of filemarks to cross */
	X        nfile = 1;
	X        end   = 0;
	X        for (;;) {
	X                if ((nread = read(tapefd, tapebuff, TAPEBUFFLEN)) == TEOF) {
	X                        if (++nfile == numfiles)
	X                                break;
	X                        end++;
	X                } else if (nread == TERR)
	X                        fprintf(stderr, "read error; file: %4d\n", nfile);
	X                else
	X                        end = 0;
	X        }
	X        close(tapefd);
	X        return(numfiles);
	X}
	X
	Xnametape()      /* fix the tape device names by appending the numeric
	X                   characters to the end */
	X{
	X        register char  *ptr, c;
	X
	X        ptr = usefile;
	X        while (c = *ptr++)
	X                if (isdigit(c))
	X                        goto found;
	X        error("illegal tape device name", NULL);
	X
	Xfound:  --ptr;
	X        strcat(tapename, ptr);
	X        strcat(tapername, ptr);
	X}
SHAR_EOF
if test 14357 -ne "`wc -c < 'vmswrite.c'`"
then
	echo shar: error transmitting "'vmswrite.c'" '(should have been 14357 characters)'
fi
chmod +x 'vmswrite.c'
fi # end of overwriting check
#	End of shell archive
exit 0