
/*
 * ucsd [dxv01] [file].
 * Read a UCSD pascal disc on an RX01.
 */
#include <stdio.h>

#define	IS_SUC	(1)
#define IE_UPN	(-1)
#define	IO_RPB	01040
#define	TRACK	26
#define	NUL	0
#define	DLE	020
#define	BPS	128
#define	DIRORG	8

struct	dirent
{
	int	d_first;
	int	d_last;
	int	d_type;
	char	d_leng;
	char	d_name[15];
	char	d_eof;
	char	d_date[3];
};

struct	dfirst
{
	int	d_first;
	int	d_last;
	int	d_type;
	char	d_leng;
	char	d_vol[7];
	int	d_hblock;
	int	d_nfile;
};

int	fbn;
int	lbn;
char	fb[BPS];
FILE	*fd;
char	*dev	= "dx0:";
int	verb;
int	vflag;

char	*ts[] = {
	"None",
	"Xdsk",
	"Code",
	"Text",
	"Info",
	"Data",
	"Graf",
	"Foto"
};

int	sector[] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24,
		     1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25 };

main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;

	if (argc < 2) {
		fprintf(stderr, "Usage: ucsd [dxv01] [file].\n");
		exit(1);
	}
	p = argv[1];
	while (c = *p++) {
		c = tolower(c);
		switch (c) {

		case '0':
			dev = "dx0:";
			break;

		case '1':
			dev = "dx1:";
			break;

		case 'd':
		case 'x':
			verb = c;
			break;

		case 'v':
			++vflag;
			break;
		}
	}
	if ((fd = fopen(dev, "ru")) == NULL) {
		fprintf(stderr, "No disc!\n");
		exit(1);
	}
	switch (verb) {

	case 'd':
		directory();
		break;

	case 'x':
		for (i=2; i<argc; ++i) {
			if (!lookup(argv[i]))
				fprintf(stderr, "%s: not found.\n", argv[i]);
			else
				extract();
		}
	}
}

tolower(c)
register c;
{
	if (c>='A' && c<='Z')
		c += 'a'-'A';
	return (c);
}

directory()
{
	register char *p1, *p2;
	register dn;
	struct dirent db;
	int bn, nf;

	bn = DIRORG;
	read(bn++);
	nf = fb->d_nfile;
	p1 = fb + sizeof(db);
	while (nf--) {
		p2 = &db;
		dn = sizeof(db);
		while (dn--) {
			if (p1 >= &fb[BPS]) {
				read(bn++);
				p1 = fb;
			}
			*p2++ = *p1++;
		}
		if (vflag) {
			printf("%d\t%d\t", db.d_first, db.d_last);
			if (db.d_type < 0 || db.d_type > 7)
				printf("Gok\t"); else
				printf("%s\t", ts[db.d_type]);
		}
		p2 = db.d_name;
		dn = db.d_leng;
		while (dn--)
			putchar(tolower(*p2++));
		putchar('\n');
	}
}

extract()
{
	register char *p1;
	register c, xf;
	int bn;

	bn = fbn;
	xf = 0;
	while (bn < lbn) {
		read(bn++);
		p1 = fb;
		while (p1 < &fb[BPS]) {
			c = *p1++;
			if (xf) {
				c -= ' ';
				while (c-- > 0)
					putchar(' ');
				xf = 0;
			} else if (c == DLE) {
				++xf;
			} else if (c != NUL) {
				if (c == '\r')
					c = '\n';
				if (c!='\n' && c!='\t' && (c<' ' || c>'~'))
					printf("\\%03o", c);
				else
					putchar(c);
			}
		}
	}
}

/*
 * Lookup file in directory.
 * If found, set `fbn' and `lbn' to
 * describe the file, and return
 * true.
 */
lookup(fn)
char *fn;
{
	register char *p1, *p2;
	register dn;
	int nf, c, i, nl, bn;
	struct dirent db;
	char un[15];

	p1 = fn;
	nl = 0;
	while (c = *p1++) {
		if (nl < 15) {
			if (c>='a' && c<='z')
				c -= 'a'-'A';
			un[nl++] = c;
		}
	}
	bn = DIRORG;
	read(bn++);
	nf = fb->d_nfile;
	p1 = fb + sizeof(db);
	while (nf--) {
		p2 = &db;
		dn = sizeof(db);
		while (dn--) {
			if (p1 >= &fb[BPS]) {
				read(bn++);
				p1 = fb;
			}
			*p2++ = *p1++;
		}
		if (db.d_leng == nl) {
			for (i=0; i<nl; ++i)
				if (db.d_name[i] != un[i])
					break;
			if (i == nl) {
				fbn = (db.d_first+2) * 4;
				lbn = (db.d_last+2)  * 4;
				return (1);
			}
		}
	}
	return (0);
}

read(bn)
register bn;
{
	register s;
	int iosb[2];
	extern _dsw;

	bn = remap(bn) + TRACK;
	while (qio(IO_RPB, flun(fd), 1, iosb, 0, fb, BPS, 0, 0, bn, 0)) {
		if (_dsw != IE_UPN) {
			fprintf(stderr, "Qio rejected!\n");
			exit(1);
		}
		wsig();
	}
	wtse(1);
	s = iosb[0]&0377;
	if (s != IS_SUC) {
		fprintf(stderr, "Floppy error, status %d!\n", s);
		exit(1);
	}
}

remap(bn)
register bn;
{
	register s, t;

	t = bn/TRACK;
	s = (sector[bn%TRACK] + 6*t) % TRACK;
	return (t*TRACK + s);
}
