
/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */
/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: loader.c 1.2 90/02/27 $ @(#)loader.c	1.0 1989/12"
/*
 * This is the magic module that understands all the
 * Secret Incantations.
 */

#include <stdio.h>
#include <sys/types.h>
#include <stropts.h>
#include <sys/errno.h>
#include "symtab.h"
#include <sys/kbd.h>
#include "tach.h"

extern char *gettxt();
extern int errno;
extern char *prog;

struct kbd_header hdr;
struct kbd_tab tb;
struct cornode *n;
char *text;
char *malloc();
unsigned char oneone[256];
struct strioctl sb;

loader(ifd, ofd, pub)
	register int ifd;	/* we read this */
	register int ofd;	/* and write this */
	register int pub;	/* 1 if attempting public load */
{
	register int i;

	if (! read(ifd, &hdr, sizeof(struct kbd_header)))
		trunko();
	checkmagic(&hdr);
	/*
	 * For each table, do the obvious.
	 */
	for (i = 0; i < hdr.h_ntabs; i++) {
		if (! read(ifd, &tb, sizeof(struct kbd_tab)))
			trunko();

		if (tb.t_flag == KBD_COT) { /* this does "link" stuff */
			do_link(IS_LINK, ifd, &tb, 1, 0);
			continue;
		}
		else if (tb.t_flag & KBD_ALP) {	/* do an ALP funtion */
			do_link(~IS_LINK, ifd, &tb, 1, 0);
			continue;
		}

		loadhead(&tb, ofd);	/* start the ball rolling */

		if (tb.t_flag & KBD_ONE) {
			if (! read(ifd, oneone, 256))
				trunko();
			loadone(oneone, ofd);
		}
		n = (struct cornode *)
			malloc(tb.t_nodes * sizeof(struct cornode));
		if (tb.t_nodes && (! read(ifd, n, tb.t_nodes * sizeof(struct cornode))))
			trunko();
		load_N_T(ofd, n, tb.t_nodes * sizeof(struct cornode));
		free(n);
		text = malloc(tb.t_text);
		if (tb.t_text && (! read(ifd, text, tb.t_text)))
			trunko();
		load_N_T(ofd, text, tb.t_text);
		free(text);
		loadend(ofd, pub);	/* cleanup after */
	}
}

checkmagic(h)

	struct kbd_header *h;
{
	if (strncmp(h->h_magic, KBD_MAGIC, strlen(KBD_MAGIC)) != 0) {
		fprintf(stderr, gettxt("kbdload:1", "bad magic (not a kbd object)\n"));
		exit(1);
	}
	if (h->h_magic[KBD_HOFF] > KBD_VER) {
		fprintf(stderr, gettxt("kbdload:2", "Version mismatch; this is %d, file is %d\n"),
			KBD_VER, (int) (h->h_magic[KBD_HOFF]));
		exit(1);
	}
}

char tbuf[256+ALIGNMENT]; /* temporary buffer for piecemeal download */

/*
 * Bombs away.  Drop the header, watch the feedback & figure
 * out whether to continue the operation or retreat in defeat.
 */

loadhead(t, ofd)
	struct kbd_tab *t;
	int ofd;
{
	struct kbd_load z;

	z.z_tabsize = sizeof(struct kbd_tab);
	z.z_onesize = (t->t_flag & KBD_ONE) ? 256 : 0;
	z.z_nodesize = t->t_nodes * sizeof(struct cornode);
	z.z_textsize = t->t_text;
/*	fprintf(stderr, "%s: %d\n", t->t_name,
		z.z_tabsize+z.z_onesize+z.z_nodesize+z.z_textsize); */
	strcpy(z.z_name, t->t_name);
	sb.ic_cmd = KBD_LOAD;
	sb.ic_timout = 30;
	sb.ic_len = sizeof(struct kbd_load) + ALIGNMENT;
	sb.ic_dp = tbuf;

	cpalign(tbuf, 0x3305);
	cpchar(&tbuf[ALIGNMENT], &z, sizeof(struct kbd_load));
	analyze(ioctl(ofd, I_STR, &sb), sb.ic_cmd);

	sb.ic_cmd = KBD_CONT;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct kbd_tab) + ALIGNMENT;
	sb.ic_dp = tbuf;
	cpalign(tbuf, 0x3680);
	cpchar(&tbuf[ALIGNMENT], t, sizeof(struct kbd_tab));
	analyze(ioctl(ofd, I_STR, &sb), sb.ic_cmd);
}

/*
 * Load the "oneone" table, if any.
 */

loadone(one, ofd)
	char *one;
	int ofd;
{
	/* magic 3680 */
	sb.ic_cmd = KBD_CONT;
	sb.ic_timout = 15;
	sb.ic_len = 256 + ALIGNMENT;
	sb.ic_dp = tbuf;
	cpalign(tbuf, 0x3680);
	cpchar(&tbuf[ALIGNMENT], one, 256);
	analyze(ioctl(ofd, I_STR, &sb), sb.ic_cmd);
}

/*
 * Load the nodes (piecemeal), and the text (piecemeal).
 */

load_N_T(ofd, addr, size)
	int ofd;
	char *addr;
	int size;
{
	register char *beg, *end;
	/* magic 3680 */

	beg = addr;
	end = beg + size;
	while (beg < end) {
		sb.ic_cmd = KBD_CONT;
		sb.ic_timout = 15;
		sb.ic_dp = tbuf;
		cpalign(tbuf, 0x3680);
		if (size > 250) {
			sb.ic_len = 250 + ALIGNMENT;
			cpchar(&tbuf[ALIGNMENT], beg, 250);
			beg += 250;
			size -= 250;
		}
		else {
			sb.ic_len = size + ALIGNMENT;
			cpchar(&tbuf[ALIGNMENT], beg, size);
			beg += size;
			size = 0;
		}
		analyze(ioctl(ofd, I_STR, &sb), sb.ic_cmd);
	}
}

/*
 * Dump out the END ioctl with appropriate incantations.
 */

loadend(ofd, pub)
	register int ofd, pub;
{
	static struct kbd_ctl cx;

	/* magic 2212 */
	sb.ic_cmd = KBD_END;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct kbd_ctl);
	sb.ic_dp = (char *) &cx;
	cx.c_arg = 0x2212;
	cx.c_type = pub ? Z_PUBLIC : Z_PRIVATE;
	analyze(ioctl(ofd, I_STR, &sb), sb.ic_cmd);
}

/*
 * This is the "unloader"; it unloads tables.
 */

unloader(name)
	char *name;
{
	struct strioctl sb;
	struct kbd_tach t;

	sb.ic_cmd = KBD_UNLOAD;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct kbd_tach) + ALIGNMENT;
	sb.ic_dp = tbuf;

	strcpy(t.t_table, name);
	cpalign(tbuf, 0x6361);
	cpchar(&tbuf[ALIGNMENT], &t, sizeof(struct kbd_tach));
	analyze(ioctl(0, I_STR, &sb), sb.ic_cmd);
}

/*
 * do_link	Make a link table.  On disk, a link table is a "kbd_tab"
 *		followed immediatedly by a STRING that is "t.t_max"
 *		bytes long.  We just take the string and ship it to
 *		the module, and it worries about parsing.
 */

do_link(flag, ifd, t, pub, loc)
	int 			flag;	/* link indicator */
	int 			ifd;	/* input fdesc to read, if not "local" */
	register struct kbd_tab *t;	/* table, if not local */
	register int 		pub;	/* 1 if public */
	register char 		*loc;	/* for cmd-line table loading ("local") */
{
	struct strioctl sb;
	register int i, rval;

	if (flag == IS_LINK) {
		oneone[0] = pub ? ~Z_PUBLIC : ~Z_PRIVATE;
		oneone[1] = 0x69;
		sb.ic_cmd = KBD_LINK;
	}
	else if (flag == IS_EXT) {
		/* RESERVED */
		;
	}
	if (t) {	/* if "t", it's real */
		if (read(ifd, &oneone[2], t->t_max) != t->t_max)
			trunko();
		sb.ic_len = t->t_max + 2;	/* tmax includes null terminator in length */
	}
	else {		/* is a link or extern */
		strcpy(&oneone[2], loc);
		sb.ic_len = strlen(loc) + 3;	/* include null terminator */
	}
	sb.ic_timout = 15;
	sb.ic_dp = (char *) &oneone[0];
#if 0
printf("~Z=%x, [0]=%x,[1]=%x,[2]=%s\n", ~Z_PUBLIC, oneone[0], oneone[1],
		&(oneone[2]));
#endif
	analyze(rval = ioctl(0, I_STR, &sb), sb.ic_cmd);
		
}

/*
 * For the "-L" and "-l" options, fakes up a link table on the fly
 * and loads it.  For the "-e" option, similarly does an "external".
 * (Could be a #define macro).
 */

fakelink(s, opt, type)
	char *s;
	int opt;
	int type;
{
	do_link(type, ~IS_LINK & ~IS_EXT, (struct kbd_tab *) 0, opt, s);
}

/*
 * Figure out what to do with the return value from "ioctl".
 * On 'zero', we just return, because it succeeded.
 */
analyze(rval, cmd)

	int cmd;
{
	if (rval == 0)
		return;
	switch (errno) {
	case EACCES:	/* invalid access (pub/priv) for link */
			fprintf(stderr, gettxt("kbdload:3", "Invalid access (pub/priv)\n"));
			break;
	case EPERM:	/* permission denied to mere mortals */
			if (cmd == KBD_END)
			  fprintf(stderr, gettxt("kbdload:4", "Table sanity check failed.\n"));
			else
			  fprintf(stderr, gettxt("kbdload:5", "Permission denied.\n"));
			break;
	case EPROTO:	/* misc protocol errors */
			fprintf(stderr, gettxt("kbdload:6", "Protocol failure.\n"));
			break;
	case ENOSPC:	/* out of public/private space */
			fprintf(stderr, gettxt("kbdload:7", "Out of space for table load.\n"));
			break;
	case ENOMEM:	/* malloc failed */
			fprintf(stderr, gettxt("kbdload:8", "System out of memory.\n"));
			break;
	case EFAULT:	/* END/CONT protocol violation */
			fprintf(stderr, gettxt("kbdload:9", "Protocol fault during download.\n"));
			break;
	case E2BIG:	/* load bigger than originally declared */
			fprintf(stderr, gettxt("kbdload:10", "Declared/loaded size mismatch.\n"));
			break;
	case EEXIST:	/* table exists already */
			fprintf(stderr, gettxt("kbdload:11", "A table of that name is already loaded.\n"));
			break;
	case EFBIG:	/* smaller than a breadbox or larger than a whale */
			fprintf(stderr, gettxt("kbdload:12", "Table size out of range.\n"));
			break;
	case ENOENT:	/* named table cannot be found */
			if (cmd == KBD_EXT)
				fprintf(stderr, gettxt("kbdload:13", "External %s not found.\n"),
					&(oneone[2]));
			else
				fprintf(stderr,
				gettxt("kbdload:14", "Table not found (or not attached).\n"));
			break;
	case ESRCH:	/* table not found on second pass of unload */
			fprintf(stderr, gettxt("kbdload:15", "Unload failed (table not found).\n"));
			break;
	case EMLINK:	/* already attached */
			fprintf(stderr, gettxt("kbdload:16", "Table attached.\n"));
			break;
	case ENOSR:	/* not enough tablinks */
			fprintf(stderr,
				gettxt("kbdload:17", "Resource depletion (link structures).\n"));
			break;
	case EBUSY:	/* too many attachments */
			fprintf(stderr, gettxt("kbdload:18", "Too many tables attached.\n"));
			break;
	case EXDEV:	/* Secret Incantation Failure (but we can't SAY it) */
	default:	/* mysterious error */
			fprintf(stderr, gettxt("kbdload:19", "I/O Error (%d) in download.\n"),
				errno);
	}
	exit(1);
}

trunko()

{
	fprintf(stderr, gettxt("kbdload:20", "Unexpected EOF in loaded file.\n"));
	exit(1);
}
