vmstape - read/write VMS format magtapes under 4.{2,3} BSD
Nike Horton
horton at harvard.ARPA
Sat Jun 22 05:41:39 AEST 1985
'vmstape' is a Harvard utility to read and write files in a format
compatible with VMS systems. It does not do particularly smart
things with incompatible filenames. It has a syntax reminiscent of
'tar'.
Please send bugs to 'manager at harvard.ARPA', or 'harvard!manager'.
------------------------ cut here for best results -------------------------
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# READ_ME vmstape.1 Makefile vmstape.h field.c header3.c skiptm.c vmstape.c vt_append.c vt_extract.c vt_list.c vt_write.c
echo x - READ_ME
cat > "READ_ME" << '//E*O*F READ_ME//'
By default, the makefile will compile the vmstape assuming
the new 4.2 directory structure emulation by libndir.a and ndir.h.
If these files do not exist, the makefile must be changed (as noted in the file)
to add and remove definitions for the variables DEFS and LIBS.
Authors: Glen Dudek and Steve Kaufer
Harvard University Science Center
//E*O*F READ_ME//
echo x - vmstape.1
cat > "vmstape.1" << '//E*O*F vmstape.1//'
.TH VMSTAPE 1H
.SH NAME
vmstape \- manipulate tapes in accordance with VAX-VMS standards
.SH SYNOPSIS
vmstape [tcxrvfbdFRHV] [files]
.SH DESCRIPTION
.LP
.I Vmstape
is a program that manipulates the tape format that is the standard for
VMS systems. The format is based on the ANSI standard for tapes. The
switches to the
.I vmstape
program are as similar to the switches for
.I tar
as possible.
.SH SWITCHES
.LP
.I c
is used to
.I create
a new tape with the named files on it.
All old information on the tape is overwritten.
.LP
.I r
appends the named files to the end of the tape. All old information
is unchanged on the tape.
.LP
If a directory name is passed as an argument to the
.I c
or
.I r
switches, all the files in the directory will be written on the tape.
.LP
.I t
reports a directory listing of the files on the tape.
.LP
.I x extracts
the named files from the tape. If no individual files are
specified, then all files are extracted from the tape.
.LP
All other switches are modifiers to these basic commands. Not all modifiers
are applicable to all the commands.
.LP
.I v
causes a
.I verbose
output describing the programs actions on the tape.
.LP
.I f
is used to specify an alternate magtape device. The default
assumes the tape is 1600 bpi density and will rewind the tape when the
program is done.
.LP
.I d
divides the tape into sections so that the VMS "directory" command will
list the tape in sections. This is useful when writing subdirectories in
UNIX.
.LP
.I b
is used to change the default block size to the length specified.
.LP
.I V
is used to specify an alternate volume label.
.LP
.I F
is used to indicate that files are NOT textual, and should therefore be
written in a fixed length format.
.LP
.I R
is used to change the default fixed length record size to the length
specified.
.LP
.I H
provides a help screen.
.SH SEE ALSO
tar(1), tp(1), mtb(8), retrieve(8)
.SH DIAGNOSTICS
.LP
Improperly formatted tapes, not made in agreement with the VMS standard,
may generate missing tape mark errors.
.LP
Problems with the tape and/or magtape drive will generate messages to the
effect that the program was unable to read or write the tape.
.SH BUGS
.LP
As DEC changes the standard for VAX-VMS tapes,
the program will need alterations.
.LP
If the user attemps to write a non-textual file without the F (or R) switch,
the file will not be completely written.
//E*O*F vmstape.1//
echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
CFILES = field.c header3.c skiptm.c vmstape.c vt_append.c \
vt_extract.c vt_list.c vt_write.c
OBJECTS = field.o header3.o skiptm.o vmstape.o vt_append.o \
vt_extract.o vt_list.o vt_write.o
CMD = vmstape
DESTIN = /usr/local/bin
CFLAGS = -O $(DEFS)
DEFS = -DNEWDIR
# to compile for ver 4.2 BSD OR any version that CANNOT emulate
# the 4.2 directory structure with the files ndir.h and libndir.a:
# define in DEFS '-Dnewdir'.
# do not define '-lndir' in LIBS.
# to compile for any version of unix WITH the 4.2 directory EMULATION package:
# do not define '-Dnewdir' in DEFS.
# define '-lndir' in LIBS.
LIBS =
OWNER =
MODE = 0755
$(CMD) : $(OBJECTS) /lib/libc.a
ld /lib/crt0.o $(OBJECTS) -o $(CMD) $(LIBS) -lc -lg
install : $(DESTIN)/$(CMD)
$(DESTIN)/$(CMD) : $(CMD)
cp $(CMD) $(DESTIN)/$(CMD)
strip $(DESTIN)/$(CMD)
chmod $(MODE) $(DESTIN)/$(CMD)
depend :
@makedepend $(CFILES)
clean :
-rm $(OBJECTS) $(CMD)
print :
print -h $(CFILES)
lint :
lint -h $(CFILES) $(DEFS)
# DO NOT DELETE THIS LINE -- make depends on it
header3.o : vmstape.h
skiptm.o : /usr/include/sys/types.h /usr/include/sys/mtio.h \
vmstape.h /usr/include/sys/ioctl.h
vmstape.o : /usr/include/stdio.h vmstape.h
vt_append.o : /usr/include/stdio.h /usr/include/sys/types.h \
/usr/include/sys/stat.h /usr/include/sys/mtio.h vmstape.h \
/usr/include/sys/ioctl.h
vt_extract.o : /usr/include/stdio.h /usr/include/ctype.h \
/usr/include/sys/types.h /usr/include/sys/stat.h vmstape.h
vt_list.o : vmstape.h
vt_write.o : /usr/include/stdio.h /usr/include/sys/types.h \
/usr/include/sys/stat.h /usr/include/sys/dir.h vmstape.h
//E*O*F Makefile//
echo x - vmstape.h
cat > "vmstape.h" << '//E*O*F vmstape.h//'
#ifndef MAXNAMLEN /* maxnamlen is defined in the new directory package, but
not the old. This is our way of telling if we
are using the new package or not */
#define MAXNAMLEN 14 /* This will set maxnamlen for old dir structs*/
#define OLD_DIR_STRUCT 1 /* for compilation of proper subrs */
#endif
#define TRUE 1
#define FALSE 0
#define streq(a,b) (!strcmp(a,b))
/* exit status codes for program
*/
#define SUCCESS 0
#define FAILURE (-1)
#define MAGTAPE "/dev/rmt8"
int blocksz;
#define BLOCKSZ 2048
#define MAXBLOCKSZ 2048 /* random choice */
int fixreclen;
#define FIXRECLEN 128 /* should be multiple of BLOCKSZ */
#define MAXFIXRECLEN MAXBLOCKSZ
#define RECSIZE 80 /* size of Header and Trailer
* records
*/
/*
* The ANSI Standard Magtape produced by a VAX looks like
*
* Volume Label 80 bytes
* --------------------------------
* Header 1 80 bytes
* Header 2 80 bytes
* (Header 3) (80 bytes) ... optional
* TAPE MARK
* Data Block 2048 bytes
* ....
* TAPE MARK
* Trailer 1 80 bytes
* Trailer 2 80 bytes
* (Trailer 3) (80 bytes) ... optional
* TAPE MARK
* ---------------------------------
* TAPE MARK
*
* The information between the dashed lines is repeated for
* every file on the tape. See appendix B of VAX-11 RMS Reference
* for additional detailed information.
*/
/* The definitions used here reflect the fact that in C, counting starts
* at 0 and not 1
*/
/*
* fields of Volume Label
*/
#define __XXX 0,2 /* alphabetic characters "VOL" */
#define __YYY 3,3 /* numberic character "1" */
#define VLABEL 4,9
#define VPROT 10,10 /* protection on volume */
#define VRES1 11,36
#define VOWNER 37,49
#define VDSV 50,50 /* DIGITAL standard version */
#define VRES2 51,78
#define VLSV 79,79 /* Label standard version */
/*
* fields of Header 1
*/
#define __AAA 0,2 /* alphabetic characters "HDR" */
#define __BBB 3,3 /* numeric character "1" */
#define H1_FNAME 4, 20 /* filename */
#define __CCC 21,26 /* file set identifier */
#define __DDD 27,30 /* file section number */
#define __EEE 31,34 /* file sequence number */
#define __FFF 35,38 /* generation number */
#define __GGG 39,40 /* generation version */
#define H1_CDATE 41,46 /* creation date */
#define H1_XDATE 47,52 /* expiration date */
#define H1_ACCESS 53,53 /* access security */
#define H1_BCOUNT 54,59 /* block count (always 0) */
#define H1_SYSTEM 60,72 /* system that produced it */
#define __HHH 73,79 /* reserved */
/*
* fields of Header 2
*/
#define __III 0,2 /* alphabetic characters "HDR" */
#define __JJJ 3,3 /* numeric character "2" */
#define H2_FMT 4,4 /* record format */
#define H2_BSZ 5,9 /* # characters per block */
#define H2_RECLEN 10,14 /* record length for fixed length records */
#define __KKK 15,35 /* system dependent info */
#define H2_FORMC 36,36 /* form control
* 'A' : first byte of record contains
* fortran control characters
* 'M' : record contains all form control
* info
* ' ' : lf/cr to be inserted between
* records.
*/
#define __LLL 37,49 /* system dependent info */
#define __MMM 50,51 /* buffer offset = "00" */
#define __NNN 52,79 /* reserved */
/*
* fields of Header 3
*/
#define __OOO 0,2 /* alphabetic characters "HDR" */
#define __PPP 3,3 /* numeric character "3" */
#define __QQQ 4,67 /* FILES-11 attributes if made on VMS system */
#define __RRR 68,79 /* system dependent info. spaces for VMS */
/*
* DEFINITIONS FOR WRITING TAPES
*/
#define VOL_LABEL "UNIX" /* default volume label */
#define CREATION " 70001" /* Julian date: yyddd */
#define EXPIRATION " 99365" /* Julian date: yyddd */
#define FILLCHAR '^'
int magtape;
char databuf[MAXBLOCKSZ+1];
char vol_label[80];
/*
* These flags represent which operation is to be performed
*/
int createflag; /* Create a new tape */
int listflag; /* List the tape contents */
int extractflag; /* Extract the files from tape */
/*
* These flags represent modifiers to the basic operations
*/
int rawflag;
int binflag;
int verbose;
int fixed_length_flag;
int section_flag;
char *field();
char pad_record[MAXFIXRECLEN];
//E*O*F vmstape.h//
echo x - field.c
cat > "field.c" << '//E*O*F field.c//'
#define SIZE 80
char *
field(p, begin, end)
char *p;
int begin;
int end;
{
register int i;
register int j;
static char buffer[SIZE];
j = 0;
for(i = begin ; i <= end ; i++)
buffer[j++] = p[i];
buffer[j] = '\0';
if( j >= SIZE )
printf("field: buffer overflow. j = %d\n", j);
return(buffer);
}
//E*O*F field.c//
echo x - header3.c
cat > "header3.c" << '//E*O*F header3.c//'
#include "vmstape.h"
/* these functions really simulate r_record, but with these names
* make reading the code easier
*/
/* is there a Header 3 area on the tape?
*/
header3()
{
char buf[RECSIZE];
if( read(magtape, buf, RECSIZE) != 0 )
return(1);
return(0);
}
trailer3()
{
char buf[RECSIZE];
if( read(magtape, buf, RECSIZE) != 0 )
return(1);
return(0);
}
//E*O*F header3.c//
echo x - skiptm.c
cat > "skiptm.c" << '//E*O*F skiptm.c//'
#include <sys/types.h>
#include <sys/mtio.h>
#include <sys/ioctl.h>
#include "vmstape.h"
skiptm(n)
int n;
{
struct mtop m;
m.mt_count = n;
m.mt_op = MTFSF;
ioctl(magtape, MTIOCTOP, &m);
}
w_tapemark()
{
struct mtop m;
m.mt_count = 1;
m.mt_op = MTWEOF;
ioctl(magtape, MTIOCTOP, &m);
}
//E*O*F skiptm.c//
echo x - vmstape.c
cat > "vmstape.c" << '//E*O*F vmstape.c//'
#include <stdio.h>
#include "vmstape.h"
/* NOTE -- FILES MUST BE TEXTUAL (NOT CONTAINING A NULL BYTE) */
/* arguments to open system call
*/
#define READ 0
#define WRITE 1
#define RDWT 2
#define CREATE 1
#define LIST 2
#define EXTRACT 3
#define APPEND 4
int open_file();
main(argc, argv)
int argc;
char **argv;
{
register char *p;
int function;
int func_count;
int mode;
char device[100];
if(argc < 2) {
fprintf(stderr, "Usage (H = HELP): vmstape [crtxvfdbFRVH] [ filename ]\n");
exit(FAILURE);
}
strcpy(device, MAGTAPE);
strcpy(vol_label, VOL_LABEL);
blocksz = BLOCKSZ ; /* default */
fixreclen = FIXRECLEN; /* default */
func_count = 0;
for(p = argv[1]; *p != '\0' ; p++)
switch(*p) {
case 'H':
fprintf(stdout,"Help option specified. All others ignored.\n");
fprintf(stdout,"OPTIONS: c = create tape\n");
fprintf(stdout," r = append to tape\n");
fprintf(stdout," t = list tape\n");
fprintf(stdout," x = extract from tape specified file name\n");
fprintf(stdout," v = verbose messages\n");
fprintf(stdout," f = specify new device\n");
fprintf(stdout," d = if appending, make new section\n");
fprintf(stdout," b = specify new blocksize.(default:2048b/block.)\n");
fprintf(stdout," F = write fixed length records (non text files).\n");
fprintf(stdout," R = specify new fixed record length.(default:128b/rec.)\n");
fprintf(stdout," V = specify new volume label\n");
fprintf(stdout," H = see this help screen\n");
exit(FAILURE);
case 'R':
fixreclen = atoi(argv[2]);
if (fixreclen > MAXFIXRECLEN)
{
fprintf(stderr,"ERROR:Fixed record length cannot be greater than %d\n",MAXFIXRECLEN);
exit(FAILURE);
}
argv++;argc--;
/* Fall through to writing fixed length recs */;
case 'F':
fixed_length_flag = 1;
break;
case 'b':
blocksz = atoi(argv[2]);
if (blocksz > MAXBLOCKSZ)
{
fprintf(stderr,"ERROR:Blocksz cannot be greater than %d\n",MAXBLOCKSZ);
exit(FAILURE);
}
argv++;argc--;
break;
case 'c':
function = CREATE;
func_count++;
mode = WRITE;
break;
case 't':
function = LIST;
func_count++;
mode = READ;
break;
case 'x':
function = EXTRACT;
func_count++;
mode = READ;
break;
case 'r':
function = APPEND;
func_count++;
mode = RDWT;
break;
case 'd':
/* appending , but starting the file offset count
all over again so that a VMS dir command will split
the tape directory into sections. */
section_flag = 1;
break;
case 'f':
strcpy(device, argv[2]);
argv++ ; argc-- ;
break;
#ifdef OLD
/* could use these for writing tapes.
*/
case 'B':
binflag = TRUE;
break;
case 'R':
rawflag = TRUE;
break;
#endif
case 'V':
strcpy(vol_label, argv[2]);
argv++ ; argc--;
break;
case 'v':
verbose = TRUE;
break;
default:
printf("Unknown key: %c\n", *p);
exit(FAILURE);
}
if (blocksz % fixreclen ) {
fprintf(stderr,"ERROR:The blocksize must be a multiple of the fixed record length.\n");
exit (FAILURE);
}
if(func_count == 0) {
fprintf(stderr,"No function specified\n");
exit(FAILURE);
}
if(func_count > 1) {
fprintf(stderr,"Too many functions specified\n");
exit(FAILURE);
}
#ifdef OLD
magtape = open(device, mode);
if(magtape < 0) {
fprintf(stderr, "Cannot open %s\n", device);
exit(FAILURE);
}
#endif
switch(function) {
case CREATE:
vt_create(&argv[2], device, mode);
break;
case LIST:
vt_list(device, mode);
break;
case EXTRACT:
vt_extract(&argv[2], device, mode);
break;
case APPEND:
vt_append(&argv[2], device, mode);
break;
default:
printf("This can never happen!\n");
break;
}
close(magtape);
exit(SUCCESS);
}
make_pad_record()
{
int i;
for (i=0;i<MAXFIXRECLEN;i++) pad_record[i] = '^';
}
int open_file (device, mode)
char device[];
int mode;
{
int descrip;
descrip = open(device, mode);
if(descrip < 0) {
fprintf(stderr, "Cannot open %s\n", device);
exit(FAILURE);
}
return (descrip);
}
//E*O*F vmstape.c//
echo x - vt_append.c
cat > "vt_append.c" << '//E*O*F vt_append.c//'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mtio.h>
#include <sys/ioctl.h>
#include "vmstape.h"
int open_file();
vt_append(argv, device, mode)
char **argv, *device;
int mode;
{
char buf[RECSIZE];
int i,offset;
int nblocks;
if ( !(*argv) ) {
printf("r: no arguments??\n");
return;
}
/* open tape file */
magtape = open_file (device, mode);
/* read the volume label */
r_record(buf);
offset = 1;
while (r_record(buf))
{
++offset; /* counting files */
skiptm(3);
}
if (section_flag) offset = 1; /* if we want to make sections ... */
/* back up one tape mark */
backtm();
for(i = 0 ; argv[i] ; i++) {
if(verbose)
printf("r %s\n", argv[i]);
if( subdir(argv[i]) )
continue;
if( size0(argv[i]) )
continue;
if ( isdir(argv[i]) ) {
printf("'%s' is a directory...writing all files within\n", argv[i]);
w_dir(argv[i], &offset);
}
else {
w_file(argv[i], offset);
++offset;
}
}
w_tapemark();
}
backtm()
{
int i;
struct mtop m;
m.mt_count = 1;
m.mt_op = MTBSF;
i = ioctl(magtape, MTIOCTOP, &m);
}
//E*O*F vt_append.c//
echo x - vt_extract.c
cat > "vt_extract.c" << '//E*O*F vt_extract.c//'
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "vmstape.h"
static int nomore = 0;
int open_file();
vt_extract(files, device, mode)
char **files, *device;
int mode;
{
register int blocksize;
int nblocks;
int i;
char buf[RECSIZE];
char filename[RECSIZE];
char recformat;
char formc;
/* open tape file */
magtape = open_file (device, mode);
/* get volume label */
r_record(buf);
while( r_record(buf) ){
rawflag = 0;
binflag = 0;
strcpy(filename, field(buf, H1_FNAME));
strip(filename);
r_record(buf);
blocksize = atoi(field(buf, H2_BSZ));
/* set flags for extraction of the file.
*
* variable length crlf form control --> no flags set
* this is your normal text file on VMS
*
* |length|record| --> |record|'\n'
*
* fixed length --> binflag set
* file records extracted as is. no interpretation
* done on blocks of file. (ie, file is full of records
* and no information about record length). This type
* of file is an array of records which have no control
* (record length) info).
*
* |record| --> |record|
*
* variable length no form control --> raw flag set
*
* |length|record| --> |length|record|
*/
recformat = *field(buf, H2_FMT);
fixreclen = atoi(field(buf, H2_RECLEN));
if( recformat == 'F' ) /* is fixed length */
binflag = 1;
else {
if( recformat != 'D' ){
printf("UNKNOWN RECORD FORMAT <%c>\n",
recformat);
exit(FAILURE);
}
/* if recformat == 'D', then
* is variable length. set no flags */
}
formc = *field(buf, H2_FORMC);
if( formc == 'M')
rawflag = 1; /* stuff record onto disk with
* its control area defining its
* length.
*/
else {
if( formc != ' '){
printf("UNKNOWN FORM CONTROL <%c>\n",
formc);
exit(FAILURE);
}
/* is the default whith crlf
* stuff done at the end of each record.
*/
}
if( header3() )
r_tapemark();
if( isarg(filename, files) )
r_data(blocksize, filename, files);
else{
/* skip tape mark at end of data area and
* after Trailers.
*/
skiptm(2);
continue;
}
if( nomore ) /* got all files wanted */
break;
/* grap Trailer areas and tape mark separating files
*/
r_record(buf);
r_record(buf);
if( trailer3() )
r_tapemark();
}
for( i=0; files[i]; i++)
if (files[i] != (char *)(-1))
printf("'%s' not found on tape\n", files[i]);
}
r_data(blksize, filename, argv)
register int blksize;
char *filename;
char **argv;
{
register int i;
FILE *outfile;
char *pathname;
if(blksize > MAXBLOCKSZ) {
fprintf("BUFFER SIZE EXCEEDED\n");
exit(FAILURE);
}
if(exists(filename)) {
fprintf(stderr, "x: %s already exists. Not extracted.\n",
filename);
while( read(magtape, databuf, blksize) > 0)
continue;
return;
}
outfile = fopen(filename, "w");
if(outfile == NULL) {
fprintf(stderr, "x: cannot create %s\n", filename);
while( read(magtape, databuf, blksize) > 0)
continue;
return;
}
if (verbose) printf("Extracting %s\n", filename);
while( (i = read(magtape, databuf, blksize)) > 0 )
ext(databuf, i, outfile);
fclose(outfile);
}
exists(filename)
char *filename;
{
struct stat statbuf;
if( stat(filename, &statbuf) < 0 )
return(FALSE);
return(TRUE);
}
ext(p, blksize, outfile)
register char *p;
int blksize;
register FILE *outfile;
{
register int i;
register int seen;
int thisline;
char nbuf[5];
char outbuf[MAXFIXRECLEN];
int filler = 1;
/* if binflag, then global fixreclen is set to record size */
if(binflag) {
while (blksize>0) {
for(i = 0 ; i < fixreclen ; i++) {
outbuf[i] = *p;
if (*p++ != FILLCHAR) filler = 0;
}
if (filler) return;
filler = 1;
for(i = 0;i<fixreclen;i++) {
putc(outbuf[i], outfile);
}
blksize -= fixreclen;
}
}
else
for(seen = 0 ; seen < blksize ; ) {
for(i = 0 ; i < 4 ; i++)
nbuf[i] = *p++;
nbuf[4] = '\0';
seen += 4;
if(!isdigit(nbuf[0]))
break;
thisline = atoi(nbuf) - 4;
if(rawflag)
putw(thisline, outfile);
for(i = 0 ; i < thisline ; i++)
fputc(*p++, outfile);
seen += thisline;
if(!rawflag)
putc('\n', outfile);
}
}
r_record(p)
char *p;
{
int n_read;
n_read = read(magtape, p, RECSIZE);
return(n_read);
}
isarg(str, argv)
char *str;
char **argv;
{
int i;
int count; /* of number left */
int retval;
/* argv vector is null terminated.
* if no files listed, then extract all
*/
if( !(*argv) )
return(1);
count = 0;
retval = 0;
for(i = 0 ; argv[i] ; i++){
if( argv[i] == ((char *)(-1)) )
continue;
if( streq(str, argv[i]) ) {
argv[i] = ((char *)(-1));
retval = 1;
}
count++;
}
if( (!count) && (!retval) )
nomore = 1; /* all done */
return(retval);
}
strip(p)
register char *p;
{
if(*p == '\0')
return;
while(*p != '\0')
p++;
p--;
while(*p == ' ')
p--;
*++p = '\0';
}
r_tapemark()
{
if(read(magtape, databuf, MAXBLOCKSZ) != 0) {
fprintf(stderr, "MISSING TAPE MARK??\n");
exit(FAILURE);
}
}
//E*O*F vt_extract.c//
echo x - vt_list.c
cat > "vt_list.c" << '//E*O*F vt_list.c//'
#include "vmstape.h"
int open_file();
vt_list(device, mode)
char *device;
int mode;
{
register int blocksize;
register int recformat;
register int formcntrl;
int nblocks;
char buf[RECSIZE];
char filename[RECSIZE];
char format[40];
char formc[40];
int n_read;
int reclen;
/* open tape file */
magtape = open_file (device, mode);
/* get volume label */
r_record(buf);
printf("\nVolume Label: %s\n\n", field(buf, VLABEL));
if( verbose )
printf("%-20s%-15s %5s %6s %7s %7s\n",
"filename", "record format", "bsize",
"reclen", "formc", "nblocks"
);
else
printf("%-20s\n",
"filename"
);
while( r_record(buf) ){
strcpy(filename, field(buf, H1_FNAME));
strip(filename);
r_record(buf);
blocksize = atoi(field(buf, H2_BSZ));
if( blocksize > MAXBLOCKSZ ){
printf("blocksize %d too large for program !\n",
blocksize);
exit(-1);
}
recformat = *field(buf, H2_FMT);
if( recformat == 'D' )
strcpy(format,"var length");
else if( recformat == 'F' )
strcpy(format,"fixed length");
else
strcpy(format,"unknown");
reclen = atoi(field(buf, H2_RECLEN));
if( recformat == 'D' )
reclen -= 4; /* 4 = size of control area
* of for variable length records
*
* the control area defines the length
* of the record, including the control
* area.
*/
formcntrl = *field(buf, H2_FORMC);
if( formcntrl == 'A' )
strcpy(formc, "fortran");
else if( formcntrl == 'M' )
strcpy(formc, "none");
else if( formcntrl == ' ' )
strcpy(formc, "crlf"); /* to be put between records */
else
strcpy(formc,"unknown");
if( header3() )
r_tapemark();
if( verbose )
printf("%-20s%-15s %-5d %-6d %7s ",
filename, format, blocksize, reclen, formc);
else
printf("%-20s\n",
filename);
nblocks = 0;
while( read(magtape, databuf, blocksize) > 0 )
nblocks++;
/*
if (recformat == 'F') nblocks = (nblocks % (blocksize/reclen))
? nblocks/(blocksize/reclen)+1
: nblocks/(blocksize/reclen);
*/
/* Because when we were reading in
blocksize chunks of data, we
were only getting reclen size peices. */
if( verbose )
printf("%-7d\n", nblocks);
/* the condition that causes the exit from the while loop
* reads in the tape mark after the data area
*/
r_record(buf);
r_record(buf);
if( trailer3() )
r_tapemark();
}
}
//E*O*F vt_list.c//
echo x - vt_write.c
cat > "vt_write.c" << '//E*O*F vt_write.c//'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
/* for either the old directory struct or new, but not emulation*/
#include <sys/dir.h>
#include "vmstape.h"
struct dirlis {
char *names; /* newline sep'd list of files */
int n; /* number of files there */
};
struct carray {
char *block; /* block w/text stored in it */
char **array; /* block of pointers into above */
int num; /* number of 'valid' pointers */
};
int open_file();
/* makerec assumes normal textual files
* note the check for zero byte means such is not valid to have
* in textual files. All material following the zero byte is
* lost.
*
* fgets has filled in the variable "in" with an asciz string
* and so the variable "in" is null-terminated.
*/
makerec(in, out, max)
char *in;
char *out;
{
register char *p;
register int count;
count = 0;
for(p = in ; *p != '\n' ; p++){
count++;
if( count + 4> max ){ /* we need the 4 to have control info*/
printf("GASP! More than %d characters in the file not seperated by a newline.\nGASP! File Incomplete.\n",max-4);
return 0; /* error condition */
}
if( !(*p) )
break;
}
*p = '\0';
sprintf(out, "%04d%s", strlen(in) + 4, in);
}
bflush(seen)
register int seen;
{
if(seen == 0)
return;
for( ; seen < blocksz ; seen++)
databuf[seen] = FILLCHAR;
if(write(magtape, databuf, blocksz) != blocksz) {
fprintf(stderr, "FATAL WRITE ERROR\n");
exit(FAILURE);
}
databuf[0] = '\0'; /* for strcat() */
}
flush_blk(buf,charcount)
char *buf;
long charcount;
{
int i;
if (charcount)
for (i = charcount;i<blocksz;i++) buf[i] = FILLCHAR;
if (write(magtape,buf,blocksz) != blocksz)
{
fprintf(stderr,"FATAL WRITE ERROR\n");
exit (FAILURE);
}
}
vt_create(argv, device, mode)
char **argv, *device;
int mode;
{
int i;
int offset; /* offset for filenumbers if any arguments */
/* are directory names instead of just files */
if( !(*argv) ){
printf("c: no args?\n");
return;
};
/* open tape file */
magtape = open_file (device, mode);
w_label(); /* volume label */
offset = 1; /* first file sequence number */
for(i = 0 ; argv[i] ; i++) {
if(verbose)
printf("c %s\n", argv[i]);
if( subdir(argv[i]) )
continue;
if( size0(argv[i]) )
continue;
if ( isdir(argv[i]) ) {
printf("'%s' is a directory...writing all files within\n", argv[i]);
w_dir(argv[i], &offset);
}
else {
w_file(argv[i], offset);
++offset;
}
}
w_tapemark();
}
w_dir(dirname, offset)
char *dirname;
int *offset;
{
struct carray files, gath();
char *p, *malloc();
int i;
p = malloc(256);
files = gath(dirname);
for(i = 0; i < files.num; i++) {
if(*files.array[i] == '.')
continue;
if(verbose)
printf("%s: %s\n", dirname, files.array[i]);
strcpy(p, dirname);
strcat(p, "/");
strcat(p, files.array[i]);
if ( isdir(p) ) {
printf("%s is a subdirectory. Not added to tape\n", p);
continue;
}
if( size0(p) )
continue;
w_file(p, *offset );
++*offset; /* inc file sequence number */
}
}
int w_data(filename)
char *filename;
{
FILE *ioptr;
char c,buf[MAXBLOCKSZ+1];
struct stat filestat;
long totalchar=0,charcount=0;
long nrecs;
int nblocks = 0;
int length,outcount;
char rec[MAXBLOCKSZ+1];
ioptr = fopen(filename, "r");
if(ioptr == NULL) {
fprintf(stderr, "Cannot open %s\n", filename);
}
else {
if (fixed_length_flag) {
stat(filename,&filestat);
nrecs = (filestat.st_size%fixreclen )
? filestat.st_size/fixreclen +1
: filestat.st_size/fixreclen ;
totalchar = fixreclen * nrecs; /*this will be an even rec size*/
while (charcount < totalchar)
{
c = getc(ioptr);
buf[charcount++ % blocksz] = c;
if ((charcount % blocksz) == 0 ) flush_blk(buf,0);
}
if ((charcount % blocksz) != 0 )
flush_blk(buf,charcount % blocksz);
fclose(ioptr);
nblocks = nrecs/(blocksz/fixreclen);
if (nrecs % (blocksz/fixreclen)) nblocks++;
}
else { /* variable length records */
outcount = 0;
for(;;){
length = (int)fgets(buf, blocksz, ioptr);
/* scratch variable for the moment */
if(length == NULL)
break;
if (makerec(buf, rec, blocksz) == 0)
{
nblocks = 0;
break; /* error */
}
length = strlen(rec);
if(outcount + length > blocksz) {
bflush(outcount);
nblocks++;
outcount = 0;
}
strcat(databuf, rec);
outcount += length;
}
if( outcount ){
bflush(outcount);
nblocks++;
}
fclose(ioptr);
}
}
return(nblocks);
}
w_file(path, number)
char *path;
int number;
{
int nblocks;
char *file, *malloc();
file = malloc(256);
strcpy(file, path);
while(*file != '\0' && *file !='/') file++;
if (*file == '\0') file = path;
else file++;
w_hdr1(file, number);
w_hdr2();
w_tapemark();
nblocks = w_data(path);
w_tapemark();
w_eof1(file, number, nblocks);
w_eof2();
w_tapemark();
if( nblocks == 0 ){
fprintf(stderr,"%s caused no data area to be written on tape??\n",
file);
w_tapemark(); /* make greaceful crash (all other files are okay )*/
exit(FAILURE);
}
}
w_eof1(filename, filenum, blkcount)
char *filename;
int filenum;
int blkcount;
{
char buf[81];
sprintf(buf,
"EOF1%-17s%-6s0001%04d000101%-6s%-6s %06dDECFILE11A ",
filename, vol_label, filenum, CREATION, EXPIRATION, blkcount);
if(write(magtape, buf, 80) != 80) {
fprintf(stderr, "WRITE -- FATAL ERROR\n");
exit(FAILURE);
}
}
w_eof2()
{
char buf[82];
sprintf(buf,
"EOF2%c%05d%05d %c 00 ",(fixed_length_flag)?'F':'D',
blocksz,(fixed_length_flag)?fixreclen:blocksz-4,
(fixed_length_flag) ? 'M' : ' ');
if(write(magtape, buf, 80) != 80) {
fprintf(stderr, "WRITE -- FATAL ERROR\n");
exit(FAILURE);
}
}
w_hdr1(filename, filenum)
char *filename;
int filenum;
{
char buf[81];
sprintf(buf,
"HDR1%-17s%-6s0001%04d000101%-6s%-6s 000000DECFILE11A ",
filename, vol_label, filenum, CREATION, EXPIRATION);
if(write(magtape, buf, 80) != 80) {
fprintf(stderr, "WRITE -- FATAL ERROR\n");
exit(FAILURE);
}
}
w_hdr2()
{
char hdr2_label[RECSIZE];
int i;
sprintf( hdr2_label, "HDR2%c%05d%05d",(fixed_length_flag)?'F':'D',blocksz,(fixed_length_flag) ? fixreclen: MAXFIXRECLEN);
i = strlen(hdr2_label);
for( ; i<RECSIZE ; i++){
hdr2_label[i] = ' '; /* By default */
/* Form control info */
if (i == 36 && (fixed_length_flag)) hdr2_label[i] = 'M';
/* buffer offset = "00" */
if( i == 50 || i == 51 ) hdr2_label[i] = '0';
}
if(write(magtape, hdr2_label, RECSIZE) != RECSIZE) {
fprintf(stderr, "WRITE -- FATAL ERROR\n");
exit(FAILURE);
}
}
w_label()
{
char buf[RECSIZE];
int i;
sprintf(buf,"VOL1%-6s", vol_label);
i = strlen(buf);
for( ; i<RECSIZE ; i++){
/* DIGITAL standard version */
if( i == 50 ){
buf[i] = '1';
continue;
}
/* label standard version */
if( i == 79 ){
buf[i] = '3';
continue;
}
buf[i] = ' ';
}
if(write(magtape, buf, RECSIZE) != RECSIZE) {
fprintf(stderr, "WRITE -- FATAL ERROR\n");
exit(FAILURE);
}
}
size0(filename)
char *filename;
{
struct stat statbuf;
if( stat(filename, &statbuf) < 0 ){
printf("'%s' does not exist...not added to tape\n", filename);
return(1);
}
if( statbuf.st_size == 0 ){
printf("'%s' is of 0 size...skipped\n", filename);
return(1);
}
return(0);
}
subdir(filename)
char *filename;
{
register char *p;
for(p = filename; *p != '\0'; p++)
if (*p == '/') {
printf("File '%s' from a subdirectory...not added to tape\n", filename);
return(1);
}
return(0);
}
isdir(file)
char *file;
{
struct stat sb;
register int t;
stat(file, &sb);
t = sb.st_mode & S_IFMT;
return(t == S_IFDIR);
}
/* DIRSIZ, to return the size of the (directory) passed it.
* used as an estimate for the amount of space to keep for
* matches. ERROR if longer than 16 bit's worth.
* From Dave Brownell's help program
*/
int dirsiz(dir) char *dir;
{
struct stat entry;
if (stat(dir,&entry) == EOF) {
printf("Can't stat() %s\n", dir);
exit(1);
}
if (entry.st_size > 65000L) { /* lazy */
printf("Directory exceeds 64K bytes in length\n");
exit(1);
}
else return((int) entry.st_size);
}
/* NAMGET, to get the names in a directory and put them in
* a string, separated by newlines. will also count the number
* of files. From Dave Brownell's help program.
* Modified to reflect 4.2 file changes as of 6/13/84. -- S.K.
*/
struct dirlis namget(dir) char *dir;
{
#ifdef OLD_DIR_STRUCT
struct direct entry;
#else
struct direct *entry;
DIR *dp;
#endif
struct dirlis ret;
register int i;
register char *t;
register FILE *file;
extern char *malloc();
ret.n = 0;
t = ret.names = malloc(dirsiz(dir));
#ifdef OLD_DIR_STRUCT
if ((file = fopen(dir,"r")) == NULL) return (ret);
while (read(file,&entry,sizeof(struct direct))) {
if (entry.d_ino != 0) {
for (i = 0 ; entry.d_name[i] != 0 && i < MAXNAMLEN ; i++)
*t++ = entry.d_name[i]; /* copy the name */
*t++ = '\n';
ret.n++;
}
}
*t++ = '\0';
fclose(file);
return(ret);
}
#else
if ((dp = opendir(dir)) == NULL) return (ret);
while( entry = readdir(dp)) {
if (entry->d_ino != 0) {
for (i = 0 ; entry->d_name[i] != 0 && i < MAXNAMLEN ; i++)
*t++ = entry->d_name[i]; /* copy the name */
*t++ = '\n';
ret.n++;
}
}
*t++ = '\0';
closedir(dp);
return(ret);
}
#endif
/* GATH, to return an array of strings representing the names
* of the files in the directory. This is not sorted.
* Note that a 'struct carray' has two pointers to blocks which
* must be free()ed.
* The newlines in the block returned by namget() are changed
* to nulls. From Dave Brownell's help program.
*/
struct carray gath(dir) char *dir;
{
struct carray ret;
struct dirlis names;
char *calloc();
names = namget(dir);
ret.block = names.names;
ret.array = (char **) calloc(names.n + 1, sizeof(char *));
for (ret.num = 0; ret.num < names.n; ret.num++) {
(ret.array)[ret.num] = names.names;
while (*(names.names) != '\n') (names.names)++;
*(names.names)++ = '\0';
}
ret.array[ret.num] = NULL;
return(ret);
}
//E*O*F vt_write.c//
echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
9 56 351 READ_ME
91 437 2391 vmstape.1
63 204 1626 Makefile
156 686 4467 vmstape.h
22 56 359 field.c
26 58 373 header3.c
24 40 300 skiptm.c
204 514 4290 vmstape.c
71 155 1118 vt_append.c
279 744 5247 vt_extract.c
114 303 2440 vt_list.c
504 1433 10252 vt_write.c
1563 4686 33214 total
!!!
wc READ_ME vmstape.1 Makefile vmstape.h field.c header3.c skiptm.c vmstape.c vt_append.c vt_extract.c vt_list.c vt_write.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
--
Nicholas Horton System Manager
Cambridge, MA Aiken Computation Lab
UUCP: decvax!genrad!wjh12!horton
{seismo,ihnp4,allegra,ut-sally}!harvard!horton
ARPA: horton at harvard BITNET: HORTON at HARVUNXH
More information about the Comp.sources.unix
mailing list