/* cpm - UNIX to CP/M 1.4 diskette interface programS
 *
 * for questions, corrections, or suggestions my address is
 *	David L. Pederson
 *	Rosemount Inc.
 *	mail station c11
 *	12001 W. 78th St.
 *	Eden Prairie, Minn. 55344
 *	(612) 937-3679
 */

/* 11/May/81 - origional release
 */

# include <stdio.h>
# include <ctype.h>
# include "cpm.h"

int cfd = -1;
int sat[GROUPS];
struct fcb dir[SLOTS];		/* in core version of directory */
char *drvnam[] = {
	"/dev/rx0c",
	"/dev/rx1c"
};
# define NUMDRVS (sizeof drvnam / sizeof drvnam[0])

char *help[] = {
	"  cpu    copy CP/M file to UNIX\n",
	"  cpc    copy UNIX file to CP/M\n",
	"  ls     list CP/M directory\n",
	"  mv     change CP/M file name\n",
	"  rm     remove a CP/M file\n",
	"  help   print this message\n",
	"  quit   quit this program\n",
	"  q      same as 'quit'\n",
	"  ! prg  run a Unix program\n"
};
# define NUMHELPS (sizeof help / sizeof help[0])




main(argc, argv)
int argc;
char **argv;
{
	int c, drive, dr[2];
	char *v[ARG+1], cmd[CMD];
	char *drvs = { "01" };

	dr[0] = dr[1] = 0;
	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
		if (getops(*argv, drvs, dr) == -1) {
			err(USAGE);
			exit(1);
		}
	if (dr[0]+dr[1] == 1)
		drive = (dr[0] == 1 ? 0 : 1);
	else {
		err("missing drive number\n");
		exit(1);
	}
	if (login(drive) == -1)
		exit(1);
	if (argc > 0)
		docmd(argc, argv);
	else
		loop {
			printf("%c> ", drvs[drive]);
			if (fgets(cmd, CMD, stdin) == NULL)
				break;
			if (! isatty(fileno(stdin)))
				printf("%s", cmd);
			if (cmd[0] == '!') {
				system(&cmd[1]);
				printf("!\n");
			}
			else {
				c = getarg(cmd, v, ARG);
				if (docmd(c, v) != 0)
					break;
			}
		}
	logout();
	exit(0);
}



login(drive)
int drive;
{
	if ((cfd = open(drvnam[drive], 2)) == -1)
		fprintf(stderr, "%s: can't open\n", drvnam[drive]);
	else
		getdir();
	return cfd;
}



getdir()
{
	int group, slot, i;
	char dirbuf[IOBUF], *p;
	struct fcb *d;

	d = &dir[0];
	for (group = 0; group < 2; group++) {
		grpio(group, dirbuf, 0);
		p = &dirbuf[0];
		for (slot = 0; slot < (SLOTS/2); slot++) {
			d->et = p[0] & 255;
			strncpy(d->fn, &p[1], 11);
			d->fn[11] = NULL;
			d->ex = p[12] & 255;
			d->rc = p[15] & 255;
			for (i = 0; i < 16; i++)
				d->dm[i] = p[i+16] & 255;
			p += 32;
			d++;
		}
	}
	buildsat();	/* just to test soundness */
	return;
}



getarg(cmd, argv, argcmax)
char *cmd;
char *argv[];
int argcmax;
{
	register int count;
	register char *p;
	int inword;

	count = inword = 0;
	for (p = cmd; *p; p++) {
		if (isspace(*p)) {
			*p = NULL;
			inword = 0;
		}
		else if (inword == 0) {
			inword = 1;
			if (count >= argcmax) {
				err("too many arguments\n");
				return 0;
			}
			argv[count++] = p;
		}
	}
	argv[count] = NULL;
	return count;
}



docmd(argc, argv)
int argc;
char **argv;
{
	int i;

	if (argc <= 0)
		return 0;
	if (strcmp("cpu", *argv) == 0)
		cpu(argc, argv);
	else if (strcmp("cpc", *argv) == 0)
		cpc(argc, argv);
	else if (strcmp("rm", *argv) == 0)
		rm(argc, argv);
	else if (strcmp("ls", *argv) == 0)
		ls(argc, argv);
	else if (strcmp("mv", *argv) == 0)
		mv(argc, argv);
	else if (strcmp(":", *argv) == 0)	/* shell type comment */
		;
	else if (strcmp("help", *argv) == 0)
		for (i = 0; i < NUMHELPS; i++)
			fputs(help[i], stdout);
	else if (strcmp("quit", *argv) == 0 || strcmp("q", *argv) == 0)
		return -1;
	else {
		fprintf(stderr, "'%s': unknown command.", *argv);
		err("  Type 'help' for a list of commands.\n");
	}
	return 0;
}



getops(line, legal, flags)
char *line, *legal;
int *flags;
{
	register char *lp;
	register int *fp;
	int ok;

	if (*line != '-')		/* not an option line */
		return -1;
	while (*++line) {
		ok = 0;
		for (lp = legal, fp = flags; *lp; lp++, fp++)
			if (*line == *lp) {
				*fp = 1;
				ok = 1;
				break;
			}
		if (! ok) {		/* illegal option */
			fprintf(stderr, "'%c': illegal option\n", *line);
			return -1;
		}
	}
	return 0;
}



cpu(argc, argv)		/* copy CP/M to Unix */
int argc;
char **argv;
{
	int ab[2];
	char tmode;
	FILE *ufp;

	ab[0] = ab[1] = 0;
	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
		if (getops(*argv, "ab", ab) == -1)
			return;
	switch(ab[0]+ab[1]) {

	case 0:
		tmode = 'a';
		break;

	case 1:
		tmode = (ab[0] == 1 ? 'a' : 'b');
		break;

	default:
		err("illegal transfer modes\n");
		return;
	}
	if (argc < 1 || argc > 2) {
		err("usage: cpu [-{ab}] cpmfile [unixfile]\n");
		return;
	}
	if (copen(*argv, 'r', tmode) == -1)
		return;
	argc--;
	argv++;
	if (argc == 0)
		ufp = stdout;
	else if ((ufp = fopen(*argv, "w")) == NULL) {
		fprintf(stderr, "%s: cannot open\n", *argv);
		return;
	}
	tounix(ufp);
	cclose();
	if (ufp != stdout)
		fclose(ufp);
	return;
}



logout()
{
	close(cfd);
	return;
}



cpmname(c, u)
char *c, *u;
{
	int i, ret = -1;

	strcpy(c, "           ");
	for (i = 0; *u != NULL; u++) {
		if (*u == '.')
			i = 8;
		else if (isalnum(*u) || *u == '_') {
			ret = 0;
			if (i < 11)
				c[i++] = (islower(*u) ? toupper(*u) : *u);
		}
	}
	u[11] = NULL;
	if (ret)
		fprintf(stderr, "'%c': bad CP/M name\n", u);
	return ret;
}



grpio(group, buf, way)
int group;
char *buf;
int way;
{
	int count;
	long lseek();

	if (group < 0 || group >= GROUPS) {
		fprintf(stderr, "error: group %d was accessed\n", group);
		exit(1);
	}
	if (lseek(cfd, (long) group * (long) IOBUF, 0) == -1L) {
		err("lseek error\n");
		exit(1);
	}
	if (way == 1)	/* write */
		count = write(cfd, buf, IOBUF);
	else
		count = read(cfd, buf, IOBUF);
	if (count != IOBUF) {
		err("I/O error\n");
		exit(1);
	}
	return;
}



slotnum(name, extent)		/* find slot with name with extent */
char *name;
int extent;			/* if -1, then any slot will do */
{
	register slot;

	for (slot = 0; slot < SLOTS; slot++)
		if (dir[slot].et != MDEL
		&& strcmp(name, dir[slot].fn) == 0
		&& (extent == -1 || dir[slot].ex == extent))
			return slot;
	return -1;
}



buildsat()
{
	register group, slot, i;

	for (i = 0; i < GROUPS; i++)
		sat[i] = 0;
	sat[0] = sat[1] = 1;	/* directories occupy groups 0 and 1 */

	for (slot = 0; slot < SLOTS; slot++) {
		if (dir[slot].et == MDEL)
			continue;
		if (dir[slot].rc > 128) {
			fprintf(stderr, "bad record count(%d), slot %d\n",
			dir[slot].rc, slot);
			exit(1);
		}
		for (i = 0; i < (dir[slot].rc+7)/8; i++) {
			group = dir[slot].dm[i];
			if (group < 2 || group > GROUPS) {
				fprintf(stderr, "bad group (%d), slot %d\n",
				group, slot);
				exit(1);
			}
			else if (++sat[group] > 1) {
				fprintf(stderr,
				"group double defined(%d), slot %d\n",
				group, slot);
				exit(1);
			}
		}
	}
	return;
}



putdir()
{
	register char *p;
	register slot;
	register struct fcb *d;
	int i, group;
	char dirbuf[IOBUF];

	d = &dir[0];
	for (group = 0; group < 2; group++) {
		p = &dirbuf[0];
		for (slot = 0; slot < (SLOTS/2); slot++) {
			p[0] = d->et;
			strncpy(&p[1], d->fn, 11);
			p[12] = d->ex;
			p[13] = 0;
			p[14] = 0;
			p[15] = d->rc;
			for (i = 0; i < 16; i++)
				p[i+16] = d->dm[i];
			p += 32;
			d++;
		}
		grpio(group, dirbuf, 1);
	}
	return;
}



cpc(argc, argv)		/* copy Unix to CP/M */
int argc;
char **argv;
{
	int ab[2];
	char tmode = 'a';
	FILE *ufp;

	ab[0] = ab[1] = 0;
	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
		if (getops(*argv, "ab", ab) == -1)
			return;
	switch(ab[0]+ab[1]) {

	case 0:
		tmode = 'a';
		break;

	case 1:
		tmode = (ab[0] == 1 ? 'a' : 'b');
		break;

	default:
		err("illegal transfer modes\n");
		return;
	}
	if (argc < 1 || argc > 2) {
		err("usage: cpc [-{ab}] [unixfile] cpmfile\n");
		return;
	}
	if (argc == 1)
		ufp = stdin;
	else {
		if ((ufp = fopen(*argv, "r")) == NULL) {
			fprintf(stderr, "%s: cannot open\n", *argv);
			return;
		}
		argc--;
		argv++;
	}
	if (copen(*argv, 'w', tmode) == -1)
		return;
	tocpm(ufp);
	cclose();
	if (ufp != stdin)
		fclose(ufp);
	return;
}



rm(argc, argv)
int argc;
char **argv;
{
	char name[12];
	register int slot;
	int all[1];

	all[0] = 0;
	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
		if (getops(*argv, "a", all) == -1)
			return;
	if (all[0]) {
		if (argc != 0)
			err("illegal option use\n");
		else {
			for (slot = 0; slot < SLOTS; slot++)
				dir[slot].et = MDEL;
		}
		putdir();
		return;
	}
	for (; argc > 0; argc--, argv++) {
		if (cpmname(name, *argv) == -1)
			return;
		if (slotnum(name, -1) == -1) {
			fprintf(stderr, "%.8s.%.3s not found\n",
			name, &name[8]);
		}
		while ((slot = slotnum(name, -1)) >= 0)
			dir[slot].et = MDEL;
	}
	putdir();
	return;
}



ls(argc, argv)		/* give a directory listing */
int argc;
char **argv;
{
	register int slot;
	int subt, totl = 0, extent, loc;
	int extra = 0;

	for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
		if (getops(*argv, "l", &extra) == -1)
			return;
	if (argc == 0) {	/* list all */
		for (slot = 0; slot < SLOTS; slot++)
			if (dir[slot].et != MDEL && dir[slot].ex == 0) {
				printf("%.8s.", dir[slot].fn);
				printf("%.3s", &dir[slot].fn[8]);
				subt = 0;
				extent = 0;
				while ((loc = slotnum(dir[slot].fn,
				extent++)) != -1)
					subt += dir[loc].rc;
				if (extra)
					printf(" %3d.%1dK", subt/8, subt%8);
				totl += subt;
				putchar('\n');
			}
		if (extra)
			printf("%4dK total.\n", (totl+7)/8);
	}
	return;
}



mv(argc, argv)
int argc;
char **argv;
{
	int slot, c;
	char *v[ARG];
	char rmbuf[20];
	char cold[12], cnew[12];

	argc--;
	argv++;
	if (argc != 2) {
		err("usage: mv oldname newname\n");
		return;
	}
	if (cpmname(cold, *argv) == -1 || cpmname(cnew, *++argv) == -1)
		return;
	if (slotnum(cold, 0) == -1) {
		fprintf(stderr, "%.8s.%.3s not found\n", cold, &cold[8]);
		return;
	}
	if (slotnum(cnew, 0) >= 0) {
		strcpy(rmbuf, "rm ");
		strcat(rmbuf, cnew);
		c = getarg(rmbuf, v, ARG);
		rm(c, v);
	}
		while ((slot = slotnum(cold, -1)) != -1)
			strcpy(dir[slot].fn, cnew);
	putdir();
	return;
}
