[net.sources] magtape handling, part 2 of 2, reposting

dick@tjalk.UUCP (Dick Grune) (12/23/84)

This is part 2 of 2 of my magtape handing package.

					Dick Grune
					Vrije Universiteit
					de Boelelaan 1081
					1081 HV  Amsterdam
					the Netherlands
... and my name isn't Richard!

: ------------------------------------------ cut here and feed to /bin/sh
echo x \a\n\s\i\w\.\c
cat >\a\n\s\i\w\.\c <<'<<>EndOfFile<>>'
#define	MSGUSE	"Usage: ansiw [-cfhlmignpv] [ file ... ]"
#include	"ansi.h"
#include	<sys/types.h>
#include	<pwd.h>
#include	<time.h>
#include	<ident.h>	/* declares char myname[] */

/*
 * Name: ansiw, write ANSI standard labelled tape
 * Author: Dick Grune
 * Version: 820314
 */

#define	MAXBLK	2048
#define	MINBLK	18
#define	FILLER	'^'

extern time_t time();
extern struct tm *localtime();
extern struct passwd *getpwuid();

char iflag = FALSE, gflag = FALSE, pflag = FALSE, vflag = FALSE;
char block[MAXBLK];

struct DD	{
	long size;
	long lrecl;
	char ascii95;
	char example;
} dd;
struct DD empty_dd = {0L, 0L, TRUE, EOS};

#define	divis(m, n)	((n)!=0&&(m)%(n)==0)

main(argc, argv) int argc; char *argv[];	{

	argc--; argv++;
	if (argc > 0)	{
		if (argv[0][0] == '-')	{
			char *pp = argv[0];

			while (*++pp)
			switch (*pp)	{
			case 'c':
				unit = TP_CDEV;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'f':
				unit = TP_IMAG;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'g':
				gflag = TRUE;
				break;
			case 'h':
				nmdns = TP_DENH;
				break;
			case 'i':
				iflag = TRUE;
				break;
			case 'l':
				nmdns = TP_DENL;
				break;
			case 'm':
				unit = *++pp - '0';
				break;
			case 'n':
				unit = TP_IMAG;
				nmdns = "/dev/null";
				break;
			case 'p':
				pflag = TRUE;
				break;
			case 'v':
				vflag = TRUE;
				break;
			default:
				goto Lbad;
			}
			argc--; argv++;
		}
	}
	tf = tpopen(unit, nmdns, "w");

	check_args(argc, argv);

	lblVOL1(iflag || vflag ? ASK_SUG : ASK_NO);

	while (argc-- != 0)	{
		strcpy(filename, argv[filseqnum]);
		filseqnum++;
		blkcount = 0;

		opendd(filename);
		if (file == NULL)	{
			printf(">>> File %s ", filename);
			errors("has suddenly disappeared!!!");
		}
		lblHDR1(iflag ? ASK_SUG : ASK_NO);
		lblHDR2(iflag ? ASK_SUG : ASK_NO);
		wrt_tm();
		copy();
		wrt_tm();
		lblEOF1();
		lblEOF2();
		wrt_tm();
	}

	wrt_tm();
	tpclose(tf);
	exit(0);

Lbad:
	errors(MSGUSE);
}

check_args(c, v) char *v[];	{
	char ok = TRUE;

	while (c-- > 0)	{
		int f = open(*v, 0);

		if (f < 0)	{
			printf("Cannot open %s\n", *v);
			ok = FALSE;
		}
		else	VOID(close(f));
		v++;
	}
	if (!ok)
		errors("Stop");
}

wrt_tm() /*writes a tapemark*/	{
	tpwrite(tf, "", 0);
}

long
tab(p) long p;	{	/* the position in which a tab from p would land */
	return (p/8+1)*8;
}

/*
 * `opendd' opens the file `fn' and determines its `dd' parameters
 */

opendd(fn) char *fn;	{
	int ch;
	long lr;

	dd = empty_dd;
	if ((file = fopen(fn, "r")) == NULL)
		return;

	lr = 0L;
	while ((ch = getc(file)) != EOF)	{
		dd.size++;
		if (ch == NL)	{
			if (lr > dd.lrecl)
				dd.lrecl = lr;
			lr = 0L;
		}
		else
		if (ch == TAB)	{
			lr = tab(lr);
		}
		else	{
			lr++;
			if (!Ascii95(ch))	{
				dd.ascii95 = FALSE;
				dd.example = ch;
			}
		}
	}
	if (lr > dd.lrecl)
		dd.lrecl = lr;

	VOID(fclose(file));
	file = fopen(fn, "r");
}

/*
 * the writing of labels
 */

lblVOL1(md)	{
	struct passwd *pwd = getpwuid(getuid());

	str2fld("", Whole(VOL1buf));
	str2fld("VOL1", Labidf(VOL1buf));

	enq_fld("\
	The `volume serial number' is the six-character identification\n\
	number of the tape itself, as it should appear on the sticker on\n\
	the reel. When in doubt, use the default %s.\n",
		md, "222222", Volidf(VOL1buf));
	enq_fld("\
	The `volume accessibility symbol' is a single character,\n\
	recorded on the tape, which indicates how publicly accessible the\n\
	whole tape is. It is not well defined, but a single space is\n\
	generally taken to mean: accessible by anybody.\n",
		md, " ", Volacc(VOL1buf));
	enq_fld("\
	The `owner identification' is the name of the owner, as recorded\n\
	on the tape. On some systems it interacts with the file\n\
	accessibility symbol. When in doubt, specify a short string of\n\
	letters.\n",
		pwd == NULL ? ASK_YES : md, pwd->pw_name, Ownidf(VOL1buf));
	str2fld("1", Labvers(VOL1buf));

	tpwrite(tf, Whole(VOL1buf));
}

lblHDR1(md)	{
	time_t tnow = time((time_t*)0);
	struct tm *timeptr = localtime(&tnow);

	str2fld("", Whole(HDR1buf));
	str2fld("HDR1", Labidf(HDR1buf));
	enq_fld("\
	The `file identifier' is the name of the file, as recorded on\n\
	the tape.  When in doubt, specify a six-letter mnemonic name.\n",
		md, filename, Fileidf(HDR1buf));

	fld2fld(Volidf(VOL1buf), Filesetidf(HDR1buf));
	int2fld(filsecnum, Filsecnum(HDR1buf));
	int2fld(filseqnum, Filseqnum(HDR1buf));
	enq_num("\
	The `generation number' is a counter that some operating systems\n\
	attach to a file and that is increased each time the file is\n\
	updated. Use 1.\n",
		gflag ? md : ASK_NO, 1, Gennum(HDR1buf));
	enq_num("\
	The `generation version number' tells how often an attempt to\n\
	write the file to tape has failed. Use 0.\n",
		gflag ? md : ASK_NO, 0, Genversnum(HDR1buf));
	dat2fld(timeptr->tm_year, timeptr->tm_yday+1, Creatdate(HDR1buf));
	enq_dat("\
	The `expiration date' is the date, recorded on the tape, after\n\
	which the file may be overwritten. The format is 2 digits for\n\
	the year, followed by 3 digits for the day in the year, e.g.,\n\
	82365 for the last day of 1982.  When in doubt, use today's date,\n\
	%s, to make the receiver's life easier.\n",
		md, timeptr->tm_year, timeptr->tm_yday+1, Expirdate(HDR1buf));
	enq_fld("\
	The `file accessibility symbol' is a single character, recorded\n\
	on the tape, which indicates how publicly accessible the file is.\n\
	It is not well defined, but a single space is generally taken to\n\
	mean: accessible by anybody.\n",
		md, " ", Fileacc(HDR1buf));
	int2fld(blkcount, Blkcount(HDR1buf));

	str2fld(myname /* from ident.h */, Syscode(HDR1buf));

	tpwrite(tf, Whole(HDR1buf));
}

lblHDR2(md)	{

	str2fld("", Whole(HDR2buf));
	str2fld("HDR2", Labidf(HDR2buf));

	if (!iflag)	{
		str2fld("F", Whole(rectype));
		blklength = 1920;
		reclength = 80;
	}

	if (!dd.ascii95)	{
		printf("`%s' contains non-ASCII95 characters, e.g., %s\n",
			filename, char2str(dd.example));
		printf("Perhaps the file code should have been BINARY");
		str2fld("U", Whole(rectype));
	}

	if (dd.lrecl > MAXBLK)	{
		printf("`%s' has a record length > %d\n", filename, MAXBLK);
		printf("Only U-format is possible\n");
		str2fld("U", Whole(rectype));
		recformat = format('U');
	}
	else
	inmood (!dd.ascii95 ? ASK_SUG : md)
		char *rct = enq_str("\
	The `record format' tells how lines from the disk file should be\n\
	converted to records to be packed into blocks to be recorded on\n\
	tape. Although many formats exist, only two are any good in\n\
	Information Interchange:\n\
	F (Fixed): spaces are added at the end of the line until its\n\
	    length is `record length', and `block length'/`record length'\n\
	    of these records form a block;\n\
	U (Undefined): `block length' characters are stored in a block.\n\
	Unless the disk file contains non-ASCII characters, use F.\n",
			mood, fld2str(Whole(rectype)));

		iferr (strlen(rct) != 1
			|| (recformat = format(rct[0])) == NULL
			|| recformat->cpblk == NULL)
			printf(
			"Only F- and U-formats are allowed for portability\n"
			);
		enderr;

		str2fld(rct, Whole(rectype));
	endmood;

	(*recformat->checkpar)(md);

	fld2fld(Whole(rectype), Recformat(HDR2buf));
	int2fld(blklength, Blklength(HDR2buf));
	int2fld(reclength, Reclength(HDR2buf));
	int2fld(bufoffset, Bufoffset(HDR2buf));

	tpwrite(tf, Whole(HDR2buf));
}

checkF(md)	{
	int lr;

	inmood (md)
		getBlklength(mood);
		iferr (blklength < reclength)
			printf("Block length < phys. record length (=%D)\n",
				dd.lrecl);
		enderr;
	endmood;

	lr = dd.lrecl;
	while (!divis(blklength, lr))
		lr++;
	if (lr < 80 && divis(blklength, 80))
		lr = 80;
	reclength = lr;

	inmood (md)
		reclength = enq_int("\
	The `record length' is the number of characters into which each\n\
	line (record) is stretched before it is written to tape. It must\n\
	divide the block length. Unless the receiver has been very\n\
	specific, use %s.\n",
			mood, reclength, blklength);
		iferr (reclength == 0 || !divis(blklength, reclength))
			printf(
		"The block length is not a multiple of the record length\n"
			);
		enderr;
		iferr (reclength < dd.lrecl)
			printf("Record length < phys. record length (=%D)\n",
				dd.lrecl);
		enderr;
	endmood;
}

checkU(md)	{
	getBlklength(md);
	reclength = blklength;
}

getBlklength(md)	{
	inmood (md)
		blklength =
			enq_int("\
	The `block length' is the number of characters in each physical\n\
	block written to tape. Unless the receiver has specified\n\
	something else, use %s.\n",
				mood,
				rectype[0] == 'F' && dd.lrecl > 1920 ? MAXBLK :
					!iflag ? 1920 :
					blklength,
				MAXBLK);
		iferr (blklength < MINBLK)
			printf("The minimum block length is %d\n", MINBLK);
		enderr;
	endmood;
}


lblEOF1()	{
	str2fld("EOF1", Labidf(HDR1buf));
	int2fld(blkcount, Blkcount(HDR1buf));
	tpwrite(tf, Whole(HDR1buf));
}

lblEOF2()	{
	str2fld("EOF2", Labidf(HDR2buf));
	tpwrite(tf, Whole(HDR2buf));
}

/*
 * the copying of the file
 */

copy()	{
	int size;

	blkcount = reccount = 0;

	while ((size = (*recformat->cpblk)()) > 0)	{
		while (size < MINBLK)
			block[size++] = FILLER;
		tpwrite(tf, block, size);
		++blkcount;
	}
	VOID(fclose(file));
	if (pflag)	{
		printf("File name: %s\n", filename);
		printf("Record format: %s\n", rectype);
		printf("Block length: %d; number of blocks: %d\n",
			blklength, blkcount);
		printf("Record length: %d; number of records: %d\n\n",
			reclength, reccount);
	}
}

int
cpFblk()	{
	int ch;
	int count = 0;

	while (count < blklength && (ch = getc(file)) != EOF)	{
		int rpos = 0;
		reccount++;
		while (ch != NL && ch != EOF)	{
			if (ch == TAB)	{
				int nrpos = (int)tab((long)rpos);
				while (rpos < nrpos)	{
					block[count++] = SP; rpos++;
				}
			}
			else	{
				block[count++] = ch; rpos++;
			}
			ch = getc(file);
		}
		while (rpos < reclength)	{
			block[count++] = SP; rpos++;
		}
	}
	return count;
}

int
cpUblk()	{
	int ch;
	int count = 0;

	while (count < blklength && (ch = getc(file)) != EOF)	{
		block[count++] = ch;
	}
	if (count > 0)
		reccount++;
	return count;
}

FORMAT formats[] =	{
	{'F', checkF, cpFblk},
	{'U', checkU, cpUblk},
	{'D', NULL, NULL},
	{'S', NULL, NULL},
	{EOS, NULL, NULL}
};

/*
 * the setting of fields
 */

int2fld(n, addr, size) char *addr;	{

	addr += size;
	while (size-- > 0)	{
		*--addr = n % 10 + '0';
		n = n / 10;
	}
}

dat2fld(y, d, date, size) char *date;	{

	if (size != 6)
		abort();
	str2fld("", Sp(date));
	int2fld(y, Year(date));
	int2fld(d, Yday(date));
}


enq_fld(expl, md, def, addr, size) char *expl, *def, *addr;	{
	char *ans;

	inmood (md)
		ans = enq_str(expl, mood, def);
		iferr (strlen(ans) == 0)
			printf("No empty answer allowed\n");
		enderr;
		iferr (strlen(ans) > size)
			printf("The %s is too long\n", expl2str(expl));
			printf("The maximum length is %d character%s\n",
				english(size));
		enderr;
		iferr (!isascstr(ans))
			printf("The %s `%s' contains non-printing chars\n",
				expl2str(expl), ans);
		enderr;
	endmood;
	str2fld(ans, addr, size);
}

enq_num(expl, md, n, addr, size) char *expl, *addr;	{
	int2fld(enq_int(expl, md, n, tento(size)-1), addr, size);
}

int
tento(n)	{
	int res = 1;

	while (n--)
		res *= 10;
	return res;
}

enq_dat(expl, md, y, d, date, size) char *expl; char *date;	{

	dat2fld(y, d, date, size);
	inmood (md)
		char *ans = enq_str(expl, mood, fld2str(date, size));
		char *adt = (*ans == SP ? ans : ans - 1);

		y = fld2int(Year(adt));
		d = fld2int(Yday(adt));
		iferr (y < 0 || d <= 0 || d > (divis(y, 4) ? 366 : 365))
			printf("`%s' is not an acceptable date\n", ans);
		enderr;
	endmood;
	dat2fld(y, d, date, size);
}

locate()	{
	return;
}
<<>EndOfFile<>>
if test "43395    12" != "`sum \a\n\s\i\w\.\c`"
then echo Checksum error; fi
echo x \c\p\t\p\.\1
cat >\c\p\t\p\.\1 <<'<<>EndOfFile<>>'
.TH CPTP I
.SH NAME
cptp \- copy from tape to tape
.SH SYNOPSIS
.B cptp
[
.B \-x
] [
.BR of= file
|
.BR if= file
]
.SH DESCRIPTION
.I Cptp
converts between real tapes and tape images on disk.
The
.B of=
parameter causes
.I cptp
to read the real magtape and produce a tape image on the indicated file.
A call with an
.B if=
parameter will write back the tape image from the indicated file to the
real magtape.
.PP
The program accepts the usual
.B \-cfhlm
parameters to describe the tape (see
.IR mag (I)).
If the
.BI \-x -option
is given,
copying will continue regardless of read-errors or consecutive tape marks
(default is stopping after 4 consecutive TMs).
.SH "SEE ALSO"
mag(I),
rawtp(I),
survey(I)
<<>EndOfFile<>>
if test "58332     1" != "`sum \c\p\t\p\.\1`"
then echo Checksum error; fi
echo x \c\p\t\p\.\c
cat >\c\p\t\p\.\c <<'<<>EndOfFile<>>'
#define	MSGUSE	"Usage is: cptp [-cfhlmx] [of=file | if=file]"
#include	<stdio.h>
#include	"tp.h"

/*
 * Name: cptp, copy tape
 * Author: Dick Grune
 * Version: 820314
 *
 * `Cptp' converts between real tapes and tape images on disk.
 */

int unit = 0;
char *nmdns = TP_DENN;
char *rx = "r";
TPFILE *from, *to;
extern FILE *tperr;
char *filename;
int size;
char buff[TP_MAXB];

main (argc, argv) char *argv[];	{
	char *arg;

	argc--; argv++;
	if (argc > 0)	{
		if (argv[0][0] == '-')	{
			char *pp = argv[0];

			while (*++pp)
			switch (*pp)	{
			case 'c':
				unit = TP_CDEV;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'f':
				unit = TP_IMAG;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'h':
				nmdns = TP_DENH;
				break;
			case 'l':
				nmdns = TP_DENL;
				break;
			case 'm':
				unit = *++pp - '0';
				break;
			case 'x':
				rx = "rx";
				break;
			default:
				goto Lbad;
			}
			argc--; argv++;
		}
	}
	if (argc != 1)
		goto Lbad;
	arg = argv[0];
	if (arg[0] == '\0' || arg[1] != 'f' || arg[2] != '=')
		goto Lbad;
	filename = &arg[3];
	tperr = stdout;
	switch (arg[0])	{
	case 'o':
		if (open(filename, 0) > 0)
			error("Output file already exists");
		from = tpopen(unit, nmdns, rx);
		to = tpopen(TP_IMAG, filename, "w");
		break;
	case 'i':
		from = tpopen(TP_IMAG, filename, "r");
		to = tpopen(unit, nmdns, "w");
		break;
	default:
		goto Lbad;
	}

	while ((size = tpread(from, buff, TP_MAXB)) != EOF)	{
		if (size == TP_MAXB)
			printf("Block too long; information may be lost\n");
		tpwrite(to, buff, size);
	}
	tpclose(from);
	tpclose(to);
	exit(0);

Lbad:
	error(MSGUSE);
}

error(str)	char *str;	{

	fprintf(stderr, "%s\n", str);
	exit(1);
}
<<>EndOfFile<>>
if test "38362     2" != "`sum \c\p\t\p\.\c`"
then echo Checksum error; fi
echo x \e\t\o\a\.\c
cat >\e\t\o\a\.\c <<'<<>EndOfFile<>>'
char _etoa[] =	{
	0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
	0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
	0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
	0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
	0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
	0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
	0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
	0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
	0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
	0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041,
	0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
	0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136,
	0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
	0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077,
	0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
	0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
	0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
	0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
	0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
	0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320,
	0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170,
	0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327,
	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
	0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
	0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
	0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
	0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
	0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
	0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
	0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377
};
<<>EndOfFile<>>
if test "55504     2" != "`sum \e\t\o\a\.\c`"
then echo Checksum error; fi
echo x \l\l\i\b\-\l\m\a\g
cat >\l\l\i\b\-\l\m\a\g <<'<<>EndOfFile<>>'
/*LINTLIBRARY*/
#include	"etoa.c"
#include	"tpread.c"
#include	"tpwrite.c"
#include	"tpopen.c"
#include	"tpclose.c"
#include	"tpname.c"
#include	"tpwtmloc.c"
#include	"tprdloc.c"
#include	"tpwloc.c"

printf(f, p1, p2, p3, p4, p5, p6, p7, p8, p9)
char *f;
int p1, p2, p3, p4, p5, p6, p7, p8, p9;
{return;}

fprintf(ff, f, p1, p2, p3, p4, p5, p6, p7, p8, p9)
FILE *ff;
char *f;
int p1, p2, p3, p4, p5, p6, p7, p8, p9;
{return;}
<<>EndOfFile<>>
if test "40648     1" != "`sum \l\l\i\b\-\l\m\a\g`"
then echo Checksum error; fi
echo x \m\a\g\.\1
cat >\m\a\g\.\1 <<'<<>EndOfFile<>>'
.TH MAG I
.SH NAME
mag \- generalized magnetic tape
.SH SYNOPSIS
.PP
.B cptp
[
.B -x
] [
.BR of= file
|
.BR if= file
]
.PP
.B rawtp
name [
.B \-x
] [ param ... ]
.PP
.B survey
[
.B \-px
]
.PP
.B ansir
[
.B \-ijnpg
] [ name ... ]
.PP
.B ansiw
[
.B \-ignpv
] [ file ... ]
.PP
.B NOSsplit
[
.B \-s
N ] [ name ]
.SH DESCRIPTION
These programs have in common that they use a 'generalized magtape'.
Such a generalized magtape may correspond to a real magtape or to a tape image
i.e., a normal
file with a specific structure which contains all information present on
a tape, including block sizes and tape marks. Conversion between real tapes
and tape images is done by
.IR cptp (I).
.PP
The generalized magtape is described by the following parameters which
apply to all the above programs.
.TP
.BI \-m d
the magtape is real and on unit
.IR d . 
.TP
.B \-h
the magtape is real and in high density.
.TP
.B \-l
the magtape is real and in low density.
.TP
.BI \-f " name"
the magtape is a tape image on file
.IR name .
.TP
.BI \-c " name"
the magtape is the character-device
.IR name .
.SH AUTHOR
Dick Grune.
.SH BUGS
Conflicts between options are not detected.
<<>EndOfFile<>>
if test "59389     2" != "`sum \m\a\g\.\1`"
then echo Checksum error; fi
echo x \m\a\g\.\3
cat >\m\a\g\.\3 <<'<<>EndOfFile<>>'
.TH MAG III
.SH NAME
mag \- implement generalized magtape
.SH SYNOPSIS
.PP
.B "#include <tp.h>"
.PP
.B "TPFILE *tpopen(unit, nmdns, rwx) char *nmdns, *rwx;"
.PP
.B "tpclose(tf) TPFILE *tf;"
.PP
.B "int tpread(tf, buf, size) TPFILE *tf; char *buf;"
.PP
.B "tpwrite(tf, buf, size) TPFILE *tf; char *buf;"
.PP
.B "FILE *tperr;"
.SH DESCRIPTION
These routines implement the generalized magtape described in
.IR mag (I).
Such a generalized magtape is viewed as a sequence of blocks each of which
is written and read in one piece. A tape mark is represented as a block of
length 0. The blocks correspond to physical blocks on tape (as separated
by Interrecord Gaps). On a tape image each block is preceded by its length
in format "%08d".
.PP
A generalized magtape is handled by these routines as a pointer to a
TPFILE. Such a pointer is obtained from
.IR tpopen .
The
.I unit
and the
.I nmdns
describe the generalized magtape; these are the possibilities:
.PP
a real magtape:
.I unit
is a small non-negative integer (the unit number);
.I nmdns
is TP_DENL, TP_DENN or TP_DENH (for low, normal or high density).
.PP
a tape image:
.I unit
is TP_IMAG;
.I nmdns
is the file name.
.PP
a character device:
.I unit
is TP_CDEV;
.I nmdns
is the device name.
.PP
The third parameter
.I rwx
is either "r" or "rx" for reading or "w" for writing; "rx" suppresses
the recognition of end-of-file in real magtapes (see
.IR tpread ).
.PP
.I Tpclose
closes a generalized magtape; this causes a rewind and makes room for
other such files, since only a limited number (specified in _TP_MOPEN)
can be open at the same time.
.PP
.I Tpread
reads one block into
.I buf
to a maximum of
.I size
characters. The rest of the block, if present, is skipped.
It returns the number of
characters read, or 0 for a tape mark, or EOF for an end-of-file.
On a tape image file the end-of-file coincides with the actual end-of-file.
On a real magtape or on a character device three conditions can cause
an end-of-file:
.IP
4 consecutive tape marks,
.br
2 consecutive read errors, or
.br
1 read error preceded by a tape mark,
.PP
unless "rx" was specified in the call of
.IR tpopen ,
in which case only 100 consecutive read errors will cause an end-of-file
(to prevent the program from looping on end-of-reel).
.PP
.I Tpwrite
writes one block from
.I buf
if
.I size
> 0, or a tape mark if
.I size
= 0.
.SH DIAGNOSTICS
If an error condition (other than end-of-file) is found, a message is
printed on the stream
.I tperr
(default is
.IR stderr ),
and the program exits.
.SH AUTHOR
Dick Grune.
<<>EndOfFile<>>
if test "43710     3" != "`sum \m\a\g\.\3`"
then echo Checksum error; fi
echo x \r\a\w\t\p\.\1
cat >\r\a\w\t\p\.\1 <<'<<>EndOfFile<>>'
.TH RAWTP I
.SH NAME
rawtp \- read raw tape
.SH SYNOPSIS
.B rawtp
name [
.B \-x
] [ param ... ]
.SH DESCRIPTION
.I Rawtp
extracts arbitrary portions from a magtape.
.PP
The program accepts the usual
.B \-cfhlm
parameters to describe the tape (see
.IR mag (I)).
The additional
.BR \-x -option
will cause
.I rawtp
to continue reading regardless of read-errors or consecutive tape marks
(normally
.I rawtp
stops after 4 consecutive tape marks).
.PP
The tape is considered as a series of files, each terminated by a
tape mark (TM); a file is considered as a series of blocks, each
terminated by an InterRecord Gap (IRG); a block consists of characters.
.PP
An instruction
.I +t.i.c
or
.I \-t.i.c
moves the tape over
.I t
TM's,
.I i
IRG's (but not over a TM) and
.I c
characters (but not over an IRG). If the instruction begins with a
.I +
the contents are copied to a file, a
.I \-
just skips the contents.
.PP
Instructions may be concatenated into an instruction series. If an
instruction sequence is followed by
.BI x n
the effect is repeated
.I n
times. If
.I n
is absent or 0, the instruction series is repeated until it becomes
ineffective. E.g.,
.I +.1\-1x
will give you the first block of each file on tape.
Default parameter is
.I +1x
which splits the tape into its separate files.
.PP
The produced files are named
.I namepprrrii
where 
.I name
is the first argument,
.I pp
is the two-digit parameter number,
.I rrr
is a three-letter counter counting the number of repetitions of the
parameter, and
.I ii
is the two-digit instruction number within the parameter.
.PP
Example:

.br
	rawtp  tp  \-10  +..80\-3x

will skip 10 files, and then give the first 80 characters of the first
block of every third file on the files
.IR tp02aaa01 ,
.IR tp02aab01 ,
etc, (if present.)
.SH "SEE ALSO"
mag(I),
cptp(I),
survey(I)
<<>EndOfFile<>>
if test "03147     2" != "`sum \r\a\w\t\p\.\1`"
then echo Checksum error; fi
echo x \r\a\w\t\p\.\c
cat >\r\a\w\t\p\.\c <<'<<>EndOfFile<>>'
#define	MSGUSE	"Usage is: rawtp [-cfhlmx] XX [ param ... ]"
#include	<stdio.h>
#include	"tp.h"
/*
 * Name: rawtp, read raw tape
 * Author: Dick Grune
 * Version: 820314
 *
   Selected portions are read from tape and written to files.

*/

#define	TRUE	1
#define	FALSE	0
#define	EOS	'\0'

#define	EOB	0	/* End Of Block */
#define	EOX	-1	/* End Of File (to avoid confusion with EOF) */
#define	EOT	-2	/* End Of Tape */
#define	AT_EOB	(ilength <= EOB)
#define	AT_EOX	(ilength <= EOX)
#define	AT_EOT	(ilength <= EOT)

char name [128];
char *eoname = &name[0];
FILE *ofile = NULL;

TPFILE *tape;
extern FILE *tperr;
int unit = 0;
char *nmdns = TP_DENN;
char *rx = "r";
char buff[TP_MAXB];

char *strins();

main(argc, argv) char **argv;	{
	extern int ilength;

	argc--; argv++;
	if (argc > 0)	{
		if (argv[0][0] == '-')	{
			char *pp = argv[0];

			while (*++pp)
			switch (*pp)	{
			case 'c':
				unit = TP_CDEV;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'f':
				unit = TP_IMAG;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'h':
				nmdns = TP_DENH;
				break;
			case 'l':
				nmdns = TP_DENL;
				break;
			case 'm':
				unit = *++pp - '0';
				break;
			case 'x':
				rx = "rx";
				break;
			default:
				goto Lbad;
			}
			argc--; argv++;
		}
	}
	if (argc < 1)
		goto Lbad;
	if (**argv == '+' || **argv == '-')
		goto Lbad;
	set_name(argv);
	argc--; argv++;

	ilength = EOT;	/* fake empty tape to test parameters */
	params(argc, argv);

	tape = tpopen(unit, nmdns, rx);
	tperr = stdout;
	ilength = EOB;	/* and now for keeps */
	skipIRG();
	params(argc, argv);
	tpclose(tape);
	exit(0);

Lbad:
	error(MSGUSE, "");
}

set_name(argv) char **argv;	{
	register char *pt;

	eoname = strins(eoname, *argv);
	eoname = strins(eoname, "01aaa01");
	*eoname = EOS;
	for (pt = eoname; pt > name; pt--)
		if (pt[-1] == '/') break;
	if (eoname - pt > 14)
		error("%s: file name too long", name);
}

params(argc, argv) char **argv;	{

	VOID(strins(eoname-7, "01"));
	if (!argc)
		param("+1x");
	else
		while (argc--)	{
			param(*argv++);
			incr(eoname-6);
		}
}

char *ppar;	/* parameter being processed */

param(arg) char *arg;	{
	register int repl;

	ppar = arg;
	repl = getxrepl(ppar);
	if (repl == 0) repl--;
	VOID(strins(eoname-5, "aaa"));
	while (repl-- && instr())
		incr(eoname-3);
}

int moved;

int
instr()	{
	char *p = ppar;

	moved = FALSE;
	VOID(strins(eoname-2, "01"));
	while (simp_instr(&p))
		incr(eoname-1);
	return moved;
}

int copy = FALSE;

int
simp_instr(pp) char **pp;	{
	register int cnt;

	switch (**pp)	{
	case EOS:
	case 'x':
		return FALSE;
	case '+':
		copy = TRUE;
		break;
	case '-':
		copy = FALSE;
		break;
	default:
		error("%s: bad parameter", ppar);
	}
	(*pp)++;
	cnt = getint(pp);
	while (cnt-- && copyfile()) {}
	if (**pp == '.')
		(*pp)++;
	cnt = getint(pp);
	while (cnt-- && copyblock()) {}
	if (**pp == '.')
		(*pp)++;
	cnt = getint(pp);
	while (cnt-- && copychar()) {}
	if (copy)
		dropfile();
	return TRUE;
}

int ilength;

/* ilength contains the number of characters the tape is ahead of the user;
 * or it is EOX or EOT
 */
char *iptr;

int
copyfile()	{

	if (AT_EOT)
		return FALSE;
	while (copyblock())	{}
	skipTM();
	return TRUE;
}

int
copyblock()	{

	if (AT_EOX)
		return FALSE;
	if (!copy)
		ilength = EOB;
	else
		while (copychar())	{}
	skipIRG();
	return TRUE;
}

int
copychar()	{

	if (AT_EOB)
		return FALSE;
	outchar(*iptr);
	iptr++;
	ilength--;
	return TRUE;
}

outchar(c)	{

	if (!copy)
		return;
	if (ofile == NULL)	{
		getfile();
		moved = TRUE;
	}
	putc(c, ofile);
}

/* physical tape movers */

skipTM()	{

	if (AT_EOT)
		return;
	ilength = EOB;
	skipIRG();
}

skipIRG()	{
	int size;

	if (AT_EOX)
		return;
	size = tpread(tape, buff, TP_MAXB);
	ilength = size == EOF ? EOT : size == 0 ? EOX : size;
	iptr = buff;
	if (!AT_EOT)
		moved = TRUE;
}

/* output file registration */

getfile()	{

	if ((ofile = fopen(name, "w")) == NULL)
		error("%s: cannot create", name);
}

dropfile()	{

	if (ofile != NULL)
		VOID(fclose(ofile));
	ofile = NULL;
}

/* service routines */

char *
strins(s1, s2) char *s1, *s2;	{

	while (*s2 != EOS)
		*s1++ = *s2++;
	return s1;
}

int
getint(pp) char **pp;	{
	register int val, res = 0;

	for (;;)	{
		val = **pp - '0';
		if (val < 0 || val > 9)
			return res;
		(*pp)++;
		res = res*10 + val;
	}
}

incr(p) char *p;	{

	(*p)++;
	if (*p == '9' + 1)	{
		*p = '0'; incr(p-1);
	}
	else
	if (*p == 'z' + 1)	{
		*p = 'a'; incr(p-1);
	}
}

int
getxrepl(p) char *p;	{
	register int r;

	while (*p != 'x')
		if (!*p++)
			return 1;
	p++;
	r = getint(&p);
	if (*p)
		error("%s: bad replicator", p);
	return r;
}

error(p1, p2) char *p1, *p2;	{

	printf(p1, p2);
	printf("\n");
	exit(1);
}
<<>EndOfFile<>>
if test "12914     5" != "`sum \r\a\w\t\p\.\c`"
then echo Checksum error; fi
echo x \s\u\r\v\e\y\.\1
cat >\s\u\r\v\e\y\.\1 <<'<<>EndOfFile<>>'
.TH SURVEY I
.SH NAME
survey \- survey contents of a magtape
.SH SYNOPSIS
.B survey
[
.B \-px
]
.SH DESCRIPTION
.I Survey
lists the lengths of the blocks on a magtape. If the
.B \-p
option is given, the first characters of each block are displayed
in ASCII, EBCDIC and hexadecimal.
.PP
The program accepts the usual
.B \-cfhlm
parameters to describe the tape (see
.IR mag (I)).
The additional
.BR \-x -option
will cause
.I rawtp
to continue reading regardless of read-errors or consecutive tape marks
(normally
.I rawtp
stops after 4 consecutive tape marks).
.SH SEE ALSO
mag(I)
<<>EndOfFile<>>
if test "11624     1" != "`sum \s\u\r\v\e\y\.\1`"
then echo Checksum error; fi
echo x \s\u\r\v\e\y\.\c
cat >\s\u\r\v\e\y\.\c <<'<<>EndOfFile<>>'
#define	MSGUSE	"Usage is: survey [-cfhlmpx]\n"
#include	"tp.h"
#include	<ctype.h>
#define	WIDTH	64

/*
 * Name: survey, survey contents of magtape
 * Author: Dick Grune
 * Version: 820314
 */

int unit = 0;
char *nmdns = TP_DENN;
char *rx = "r";
TPFILE *tf;
extern FILE *tperr;
char buff[TP_MAXB];
int size;

char pflag = 0;

main(argc, argv) char *argv[];	{

	argc--; argv++;
	if (argc > 0)	{
		if (argv[0][0] == '-')	{
			char *pp = argv[0];

			while (*++pp)
			switch (*pp)	{
			case 'c':
				unit = TP_CDEV;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'f':
				unit = TP_IMAG;
				if (argc < 2)
					goto Lbad;
				nmdns = argv[1];
				argc--; argv++;
				break;
			case 'h':
				nmdns = TP_DENH;
				break;
			case 'l':
				nmdns = TP_DENL;
				break;
			case 'm':
				unit = *++pp - '0';
				break;
			case 'p':
				pflag = 1;
				break;
			case 'x':
				rx = "rx";
				break;
			default:
				goto Lbad;
			}
			argc--; argv++;
		}
	}

	if (argc != 0)
		goto Lbad;

	tf = tpopen(unit, nmdns, rx);

	while ((size = tpread(tf, buff, TP_MAXB)) != EOF)	{
		printf("%6d", size);
		if (pflag)
			expose();
		printf("\n");
	}
	exit(0);

Lbad:
	fprintf(stderr, MSGUSE);
	exit(1);
}

int
hex(c)	char c;	{
	return "0123456789ABCDEF"[c&017];
}

expose()	{
	if (size == 0)
		printf("\t* * * TAPE MARK * * *\n");
	else	{
		int i;
		printf("\t");
		for (i = 0; i < WIDTH && i < size; i++)	{
			char c = buff[i];
			printf("%c", isascii(c) &&
				(isprint(c) || c == ' ') ? c : '?');
		}
		printf("\n  EBC:\t");
		for (i = 0; i < WIDTH && i < size; i++)	{
			char c = ebc2asc(buff[i]);
			printf("%c", isascii(c) &&
				(isprint(c) || c == ' ') ? c : '?');
		}
		printf("\n  HEX:\t");
		for (i = 0; i < WIDTH/2 && i < size; i++)	{
			char c = buff[i];
			printf("%c%c", hex(c>>4), hex(c));
		}
		printf("\n");
	}
}
<<>EndOfFile<>>
if test "33446     2" != "`sum \s\u\r\v\e\y\.\c`"
then echo Checksum error; fi
echo x \t\p\.\h
cat >\t\p\.\h <<'<<>EndOfFile<>>'
#ifndef TPFILE
#ifndef FILE
#include	<stdio.h>
#endif

#define	TP_CDEV	(-1)
#define	TP_IMAG	(-2)
#define	_TP_PREF	8

struct tpdes	{
	int _tp_unit;		/* unit number or TP_IMAG  or TP_CDEV  */
	char *_tp_nmdns;	/* density     or filename or filename */
	char _tp_rw;		/* 'r' for reading, 'w' for writing */
	char _tp_x;		/* 'x' for persistent, ' ' for normal */
	char _tp_fildes;	/* file descriptor after `open' */

	long _tp_blkc;		/* block counter */
	int _tp_mkc;		/* accumulative TM counter */
	int _tp_mc;		/* consecutive TM counter */
	char _tp_eof;		/* EOF-flag */
};
#define	TPFILE	struct tpdes

#define	char2int(c)	((c)&0377)
#define	ebc2asc(c)	(_etoa[char2int(c)])
#define	english(i)	(i), (i) == 1 ? "" : "s"	/* plural */
#define	n_items(a)	(sizeof (a)/sizeof (a)[0])
extern TPFILE *tpopen();
extern char *_tpname();
extern char _etoa[];
extern FILE *tperr;		/* for error messages */

#ifdef	lint
int __void__;	/* to tell `lint' not to care */
#define	VOID(x)	(__void__ = (int)(x))
#else	lint
#define	VOID(x)	(x)
#endif	lint

#include	"tploc.h"

#endif
<<>EndOfFile<>>
if test "21318     2" != "`sum \t\p\.\h`"
then echo Checksum error; fi
echo x \t\p\L\O\C\.\h
cat >\t\p\L\O\C\.\h <<'<<>EndOfFile<>>'
/* #define's which may require local modification */
/* 
 * The macros TP_DEN[LNH] define the names of the low-density,
 * normal-density and high-density tape devices, as double-strings.
 * The first string indicates the read name, the second the write name;
 * a ? is replaced by the unit number.
 */
#define	TP_DENL	"/dev/rmt?\0/dev/nrmt?"
#define	TP_DENN	TP_DENL
#define	TP_DENH	"/dev/rmth\0/dev/nrmth"

#define	TP_MAXB	32766	/* largest size parameter to `read' & `write' */
#define	_TP_MOPEN 2	/* maximum number of simultaneously open tape-files */
<<>EndOfFile<>>
if test "11998     1" != "`sum \t\p\L\O\C\.\h`"
then echo Checksum error; fi
echo x \t\p\c\l\o\s\e\.\c
cat >\t\p\c\l\o\s\e\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"

tpclose(tf) TPFILE *tf;	{

	VOID(close(tf->_tp_fildes));
	if (tf->_tp_rw == 'w')	{
		tf->_tp_rw = 'r';
		VOID(close(open(_tpname(tf), 0)));
	}
	tf->_tp_fildes = 0;
}
<<>EndOfFile<>>
if test "57249     1" != "`sum \t\p\c\l\o\s\e\.\c`"
then echo Checksum error; fi
echo x \t\p\l\o\c\.\h
cat >\t\p\l\o\c\.\h <<'<<>EndOfFile<>>'
/* #define's which may require local modification */
/* 
 * The macros TP_DEN[LNH] define the names of the low-density,
 * normal-density and high-density tape devices, as double-strings.
 * The first string indicates the read name, the second the write name;
 * a ? is replaced by the unit number.
 */
#define	TP_DENL	"/dev/rmt0\0/dev/nrmt0"
#define	TP_DENN	TP_DENL
#define	TP_DENH	"/dev/rmt8\0/dev/nrmt8"

#define	TP_MAXB	32766	/* largest size parameter to `read' & `write' */
#define	_TP_MOPEN 2	/* maximum number of simultaneously open tape-files */
<<>EndOfFile<>>
if test "46780     1" != "`sum \t\p\l\o\c\.\h`"
then echo Checksum error; fi
echo x \t\p\n\a\m\e\.\c
cat >\t\p\n\a\m\e\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"

char * /* transient */
_tpname(tf) TPFILE *tf;	{
	static char name[20];
	int unit = tf->_tp_unit;
	char *nmdns = tf->_tp_nmdns;
	char *nm = name;

	if (unit == TP_IMAG || unit == TP_CDEV)
		return nmdns;

	if (tf->_tp_rw == 'w')
		while (*nmdns++) {}
	do
		*nm++ = *nmdns == '?' ? unit + '0' : *nmdns;
	while (*nmdns++);

	return name;
}
<<>EndOfFile<>>
if test "46315     1" != "`sum \t\p\n\a\m\e\.\c`"
then echo Checksum error; fi
echo x \t\p\o\p\e\n\.\c
cat >\t\p\o\p\e\n\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"

TPFILE *
tpopen(unit, nmdns, rwx) char *nmdns, *rwx;	{
	char *name = NULL, *err;
	static TPFILE tflist[_TP_MOPEN];
	TPFILE *tf;

	for (tf = &tflist[0]; tf->_tp_fildes != 0; tf++)
		if (tf == &tflist[_TP_MOPEN-1])	{
			err = "Too many tapes";
			goto Lerr;
		}

	tf->_tp_unit = unit; tf->_tp_nmdns = nmdns;
	tf->_tp_rw = 'r'; tf->_tp_x = ' ';
	while (*rwx)	switch (*rwx++)	{
	case 'r':
		tf->_tp_rw = 'r';
		break;
	case 'w':
		tf->_tp_rw = 'w';
		break;
	case 'x':
		tf->_tp_x = 'x';
		break;
	default:
		err = "Bad option in tpopen";
		goto Lerr;
	}
	tf->_tp_blkc = tf->_tp_mkc = tf->_tp_mc = tf->_tp_eof = 0;

	name = _tpname(tf);

	if (tf->_tp_rw == 'w')	{
		if ((tf->_tp_fildes = creat(name, 0666)) < 0)	{
			err = "cannot create";
			goto Lerr;
		}
	}
       	else	{
	       	if ((tf->_tp_fildes = open(name, 0)) < 0)	{
	       		err = "cannot open";
			goto Lerr;
		}
	}

	return tf;

Lerr:
	if (name != NULL)
		fprintf(tperr, "%s: ", name);
	fprintf(tperr, "%s\n", err);
	exit(1);
	return NULL;
}

FILE *tperr = stderr;

_tprwerr(msg, tf) char *msg; TPFILE *tf;	{
	fprintf(tperr,
		"After %d tape mark%s, after %D block%s: %s on %s\n",
		english(tf->_tp_mkc), english(tf->_tp_blkc), msg, _tpname(tf));
}
<<>EndOfFile<>>
if test "55927     2" != "`sum \t\p\o\p\e\n\.\c`"
then echo Checksum error; fi
echo x \t\p\r\d\L\O\C\.\c
cat >\t\p\r\d\L\O\C\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"
/*
 * _tprdloc reads one block from a character device.
 * It must take care of local restrictions.
 *
 * This is the PDP11/45 version (even block size).
 */

int
_tprdloc(tf, buf, size) TPFILE *tf; char *buf;	{
	char ch = buf[size];
	int sz = read(tf->_tp_fildes, buf, size % 2 ? size + 1 : size);
	buf[size] = ch;
	return sz > size ? size : sz;
}
<<>EndOfFile<>>
if test "24065     1" != "`sum \t\p\r\d\L\O\C\.\c`"
then echo Checksum error; fi
echo x \t\p\r\d\l\o\c\.\c
cat >\t\p\r\d\l\o\c\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"
/*
 * _tprdloc reads one block from a character device.
 * It must take care of local restrictions.
 *
 * This is the VAX version (anything goes).
 */

int
_tprdloc(tf, buf, size) TPFILE *tf; char *buf;	{
	return read(tf->_tp_fildes, buf, size);
}
<<>EndOfFile<>>
if test "65025     1" != "`sum \t\p\r\d\l\o\c\.\c`"
then echo Checksum error; fi
echo x \t\p\r\e\a\d\.\c
cat >\t\p\r\e\a\d\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"

int
tpread(tf, buf, size) TPFILE *tf; char *buf;	{
	int res;

	if (tf->_tp_eof)
		return EOF;

	if (tf->_tp_unit != TP_IMAG && tf->_tp_x == ' ' && tf->_tp_mc >= 4)
		res = EOF;
	else
		res = _tp_rd(tf, buf, size);

	if (res > 0)	{
		tf->_tp_blkc++;
		tf->_tp_mc = 0;
	}
	else
	if (res == 0)	{
		tf->_tp_mkc++;
		tf->_tp_mc++;
		tf->_tp_blkc = 0;
	}
	else
	if (res == EOF)	{
		tf->_tp_mc = 0;
	}

	return size < res ? size : res;
}

int
_tp_rd(tf, buf, size) TPFILE *tf; char *buf;	{
	int sz;
	char ch;

	if (size <= 0)	{
		buf = &ch;
		size = 1;
	}

	if (tf->_tp_unit == TP_IMAG)	{
		char pref[_TP_PREF];
		int n;
		char ch;

		n = read(tf->_tp_fildes, pref, _TP_PREF);
		if (n == 0)
			return EOF;
		if (n != _TP_PREF)
			goto Lformerr;
		sz = 0;
		for (n = 0; n < _TP_PREF; n++)	{
			int dig = pref[n] - '0';
			if (dig < 0 || dig > 9)
				goto Lformerr;
			sz = sz*10 + dig;
		}
		n = sz < size ? sz : size;
		if (n > 0)
			if (read(tf->_tp_fildes, buf, n) != n)
				goto Lformerr;
		while (sz-- > size)
			if (read(tf->_tp_fildes, &ch, 1) != 1)
				goto Lformerr;
		return n;

	Lformerr:
		_tprwerr("tape image error", tf);
		exit(1);
	}
	else	{
		int erc = 0;

		while ((sz = _tprdloc(tf, buf, size)) < 0)	{
			_tprwerr("garbage", tf);
			erc++;
			if (	(tf->_tp_x == ' ' &&
					(tf->_tp_mc > 0 || erc >= 2))
				||
				(tf->_tp_x == 'x' &&
					erc >= 100)
			)	return EOF;
		}
		return sz;
	}
	return EOF;	/* stupid `lint' */
}
<<>EndOfFile<>>
if test "27746     2" != "`sum \t\p\r\e\a\d\.\c`"
then echo Checksum error; fi
echo x \t\p\w\L\O\C\.\c
cat >\t\p\w\L\O\C\.\c <<'<<>EndOfFile<>>'

#include	"tp.h"
/*
 * _tpwloc writes one block of non-zero length to a character device.
 * It must take care of local restrictions.
 *
 * This is the PDP11/45 version (even block size).
 */

int
_tpwloc(tf, buf, size) TPFILE *tf; char *buf;	{
	int sz = write(tf->_tp_fildes, buf, size % 2 ? size + 1 : size);
	return sz > size ? size : sz;
}
<<>EndOfFile<>>
if test "13756     1" != "`sum \t\p\w\L\O\C\.\c`"
then echo Checksum error; fi
echo x \t\p\w\l\o\c\.\c
cat >\t\p\w\l\o\c\.\c <<'<<>EndOfFile<>>'

#include	"tp.h"
/*
 * _tpwloc writes one block of non-zero length to a character device.
 * It must take care of local restrictions.
 *
 * This is the VAX version (anything goes).
 */

int
_tpwloc(tf, buf, size) TPFILE *tf; char *buf;	{
	return write(tf->_tp_fildes, buf, size);
}
<<>EndOfFile<>>
if test "64955     1" != "`sum \t\p\w\l\o\c\.\c`"
then echo Checksum error; fi
echo x \t\p\w\r\i\t\e\.\c
cat >\t\p\w\r\i\t\e\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"

tpwrite(tf, buf, size) TPFILE *tf; char *buf;	{

	if (tf->_tp_unit == TP_IMAG)	{
		int i, n = size;
		char pref[_TP_PREF];
		for (i = _TP_PREF; i--; )	{
			pref[i] = n % 10 + '0';
			n = n / 10;
		}
		if (write(tf->_tp_fildes, pref, _TP_PREF) != _TP_PREF)
			goto Lerr;
		if (size > 0)
			if (write(tf->_tp_fildes, buf, size) != size)
				goto Lerr;
	}
	else	{
		if (size == 0)
			_tpwtmloc(tf);
		else
		if (_tpwloc(tf, buf, size) != size)
			goto Lerr;
	}

	if (size == 0)	{
		tf->_tp_mkc++;
		tf->_tp_blkc = 0;
	}
	else	{
		tf->_tp_blkc++;
	}

	return;

Lerr:
	_tprwerr("write error", tf);
	exit(1);
}
<<>EndOfFile<>>
if test "61518     1" != "`sum \t\p\w\r\i\t\e\.\c`"
then echo Checksum error; fi
echo x \t\p\w\t\m\l\o\c\.\c
cat >\t\p\w\t\m\l\o\c\.\c <<'<<>EndOfFile<>>'
#include	"tp.h"
/*
 * _tpwtmloc writes a tape mark to a character device.
 * It must take care of the local restrictions.
 */

_tpwtmloc(tf) TPFILE *tf;	{

	VOID(close(tf->_tp_fildes));
	tf->_tp_fildes = open(_tpname(tf), 1);
}
<<>EndOfFile<>>
if test "33727     1" != "`sum \t\p\w\t\m\l\o\c\.\c`"
then echo Checksum error; fi
echo x \t\e\s\t\.\i\m\a\g\e
cat >\t\e\s\t\.\i\m\a\g\e <<'<<>EndOfFile<>>'
00000080VOL1222222                           dick                                      100000080HDR1READ_ME.TEST     22222200010001000100 82106 82106 000000Math. Centre        00000080HDR2F0192000080                                   00                            0000000000001920SALES TALK                                                                              If you have one or more magtape units and want to do more with          them than just run `tar', this is for you.                                                                                                                              On the `shell' level this package offers programs for                     - getting a quick look at a tape,                                               - making exact copies of tapes even if you've got only one magtape                unit,                                                                         - extracting arbitrary portions from tapes and                                  - the reading and writing of ANSI standard labelled tapes.                    All these programs work equally well on real tapes and on tape images           on disk.                                                                                                                                                                On the C-level it supplies routines for handling real tapes and         tape images on disk as a unified concept (`generalized magtape').                                                                                               INSTALLATION                                                                            Take a look at the 4 small files                                                                                                                                tploc.h  tprdloc.c  tpwloc.c  tpwtmloc.c                                                                                                                and decide if they look reasonable on your system. If not, change them          or use the files `tp*LOC.?' which come from the PDP11/45. Then call             `make all' and the shell commands as described in mag(I) will appear.           00001200        To install the shell commands, change the macro USR (and                possibly BIN, LIB, and INC) in the `makefile' and do `make install'. To         install the C-routine library `libt.a', do `make crout'.                                                                                                                Now do                                                                                                                                                          ansir -fp test.image                                                                                                                                    to get a feel for what it does. (Then do it a second time!).                                                                                                    CYBER                                                                                   If you happen to have a Control Data Cyber around, running              SCOPE or NOS/BE, call `make NOS' to get `NOSsplit', a program for               reading Cyber SI-format tapes, and `NOStr' which converts from various          Cyber character codes.                                                          0000000000000080EOF1READ_ME.TEST     22222200010001000100 82106 82106 000002Math. Centre        00000080EOF2F0192000080                                   00                            0000000000000000
<<>EndOfFile<>>
if test "11352     4" != "`sum \t\e\s\t\.\i\m\a\g\e`"
then echo Checksum error; fi