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