
/* 
 * $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: main.c 1.2 90/02/27 $ @(#)main.c	1.0 1989/12"

/*
 * kbdpipe.c	Uses "kbd" tables in a pipeline.
 *
 *	Usage:
 *
 *	kbdpipe -t table [-f loadfile] [-F] [-o outfile] [infile]
 *
 *	The process forks, with the child handling final output and
 *	reading a pipeline.
 *	The parent pushes "kbd" into the "read" pipeline.
 *		If the table is not publicly resident, an attempt is made
 *		to load & attach the table.  Load is from trusted dir.
 *	The parent sends stdin to the pipe, the child reads it and
 *	writes the output.
 *
 *	-t table	Tries to attach "table".  If it fails, it tries
 *			to do kbdmap on file named /usr/lib/kbd/table.
 *			then attach that table.
 *
 *	-f file		If "table" can't be attached, tries to load
 *			"file" via kbdmap, then attach "table".  If the
 *			filename begins with "/", it is tried.  If it
 *			does NOT begin with "/", then "/usr/lib/kbd" is
 *			pre-pended, and that is tried.  If it succeeds in
 *			loading "file", it still tries to attach "table".
 *
 *	-F		Forces outfile to be overwritten if it exists.
 *
 *	-o outfile	Output goes to "outfile" instead of stdout.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stropts.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <signal.h>
#include <sys/kbd.h>

#define assuming(x)	if (x) { while (x)
#define otherwise	} else
#define loop

extern char *gettxt();
extern int errno, optind, opterr;
extern char *optarg;
char *prog;
int opttrunc;

#ifdef DEBUG
char trusted[256] = "./";
#else
char trusted[256] = "/usr/lib/kbd/";	/* THE trusted place */
#endif

#define LOADER	"kbdload"	/* name of loader program */

#define READSIDE	0
#define WRITESIDE	1

int pfd[2];	/* pipe fds */

main(argc, argv)
	int argc;
	char **argv;
{
	register char *path, *table, *output, *fname;
	register int c, chile;
	struct stat st;

	table = output = path = fname = (char *) 0;
	opttrunc = opterr = 0;
	prog = *argv;
	while ((c = getopt(argc, argv, "f:t:o:F")) != EOF) {
		switch (c) {
		case 'f':	fname = optarg; break;
		case 't':	table = optarg; break;
		case 'o':	output = optarg; break;
		case 'F':	opttrunc = 1; break;
		default:
				usage();
		}
	}
	if (! table)
		usage();
	if (output) {
		if (! opttrunc) {	/* don't overwrite */
			if (stat(output, &st) == 0) {	/* file exists */
				fprintf(stderr, gettxt("kbdpipe:1", "%s: %s: file exists.\n"),
					prog, output);
				exit(1);
			}
		}
		if (!freopen(output, "w", stdout)) {
			fprintf(stderr, gettxt("kbdpipe:2", "%s: can't re-open stdout\n"),
				prog);
			exit(1);
		}
	}
	/*
	 * open pipes & try to push stuff
	 */
	dopipes(table, fname);
	switch (chile = fork()) {
		case 0:		/* child */
			close(pfd[WRITESIDE]);
			doread(optind < argc ? 512 : 1);
			exit(0);
		case -1:	/* failure */
			fprintf(stderr, gettxt("kbdpipe:3", "%s: can't fork\n"), prog);
			exit(1);
		default:	/* parent */
			break;
	}
	/*
	 * Parent gets this far; child stops above.
	 */
	close(pfd[READSIDE]);	/* parent doesn't need READSIDE */
	assuming (optind < argc) loop {
		if (! freopen(argv[optind], "r", stdin)) {
			kill(chile, SIGKILL);
			fprintf(stderr, gettxt("kbdpipe:4", "%s: can't reopen stdin on %s\n"),
				prog, argv[optind]);
			exit(1);
		}
		dowrite();
		++optind;
	}
	otherwise {
		dowrite();
	}
	close(pfd[WRITESIDE]);
	exit(0);
}

usage()

{
	fprintf(stderr,
		gettxt("kbdpipe:5", "Usage: %s -t table [-f path] [-o outfile] [-F] [infile(s)]\n"), prog);
	exit(1);
}

/*
 * read stdin, write pfd[WRITESIDE]
 */

dowrite()

{
	register int c;
	char ch;	/* no register */

	while ((c = getchar()) != EOF) {
		ch = c;
		write(pfd[WRITESIDE], &ch, 1);
	}
}

/*
 * Child: read pfd[READSIDE], write stdout.  Try to do
 * reasonable chunks...  This produces the final output.
 */

char rbuf[514];

doread(n)
	int n;
{
	register int i, j;

	while ((i = read(pfd[READSIDE], rbuf, n)) > 0) {
		j = 0;
		while (i--) {
			putchar(rbuf[j]);
			++j;
		}
	}
}


char cmd[512];

/*
 * dopipes	sets up the pipe (or Stream Pipe) so we can WRITE
 *		to one and READ from the other.
 */
dopipes(t, fn)
	register unsigned char *t;	/* table name */
	register unsigned char *fn;	/* file name if can't attach */
{
	register int i;
	struct strioctl sb;
	int rval;	/* for child return */

#ifdef SVR4
	if (pipe(&pfd[0])) {
		fprintf(stderr, gettxt("kbdpipe:6", "%s: can't open pipe\n"), prog);
		exit(1);
	}
	if (ioctl(pfd[READSIDE], I_PUSH, "kbd")) {	/* push kbd */
		fprintf(stderr, gettxt("kbdpipe:7", "%s: unable to push \"kbd\" module.\n"), prog);
		exit(1);
	}
#else
	/*
	 * On SVR 3.x, use "/dev/spx" (the SP driver); push just after
	 * open, before link, so don't do PUSH here.
	 */
	if (spipe(&pfd[0])) {
		fprintf(stderr, gettxt("kbdpipe:8", "%s: can't open pipe\n"), prog);
		exit(1);
	}
#endif
	if (tach(t, pfd[READSIDE])) {
		/*
		 * If we can attach the table, no sweat.
		 */
		if (turnon(pfd[READSIDE]))
			return;
		fprintf(stderr, gettxt("kbdpipe:9", "%s: unable to turn on \"kbd\" module.\n"), prog);
		exit(1);
	}
	/*
	 * Otherwise, try to load and then attach.  It's a pain
	 * because we need stdin/stdout to be the pipe when we call
	 * the loader.  We fork and let a child take care of it.
	 */
	switch(fork()) {

		case 0:	 
			break;	/* child continues */

		case -1: 
			fprintf(stderr, gettxt("kbdpipe:10", "%s: cannot fork\n"), prog);
			exit(1);

		default: 
			if ((wait(&rval) == (-1)) && (errno != ECHILD)) {
				fprintf(stderr, gettxt("kbdpipe:11", "(Interrupted)"));
				exit(2);
			}
			if ((rval != 0) && (errno != ECHILD)) {
			 	exit(1);
			}
			return;	/* whew! */
	}
	/*
	 * The child gets this far; the parent is waiting
	 * above, and will NEVER get this far.
	 */

	close(0); close(1);
	if ((dup(pfd[READSIDE]) == (-1)) || (dup(pfd[WRITESIDE]) == (-1))) {
		fprintf(stderr, gettxt("kbdpipe:13", "%s: dup system call failed\n"), prog);
		exit(1);
	}

	close(pfd[READSIDE]);
	close(pfd[WRITESIDE]);

	/*
	 * Use "system" here so we don't have to fork, exec, and wait
	 * around for the return value, which simplifies things considerably
	 * at this juncture.  "exec" would probably speed it up, but
	 * would complicate things.  Use of "tach" and "turnon" directly
	 * instead of calling "kbdkey" reduces out one "system()" call;
	 * if the system is set up to cater to its user community, we'll
	 * rarely get this far...right?
	 */
	
			/* Try loading table as specified by -t option. */	
	if (fn == NULL) {  
		if (t[0] == '/') {
			fprintf(stderr, gettxt("kbdpipe:17", "%s: illegal table name.\n"), t);
			exit(1);
		}
		sprintf(cmd, "%s %s%s", LOADER, trusted, t);
		if (system(cmd)) {
			fprintf(stderr, gettxt("kbdpipe:14", "%s: load failed.\n"), prog);
			exit(1);
		}
	}		/* Try loading table as specified by -f option. */	
	else { 
		sprintf(cmd, "%s %s%s", LOADER, (fn[0] == '/' ? "" : trusted), fn);
		if (system(cmd)) {
			fprintf(stderr, gettxt("kbdpipe:14", "%s: load failed.\n"), prog);
			exit(1);
		}
	}
	if (tach(t, 0))
		turnon(0);
	else {
		fprintf(stderr, gettxt("kbdpipe:18",
			"%s: could not attach table \"%s\".\n"), prog, t);
		exit(1);
	}

	exit(0);	/* child must exit */
}

#ifndef SVR4
/*
 * spipe	Open "/dev/spx" and obtain a Streams Pipe.  This function
 *		"borrowed" from UTS library source with some mods.  You
 *		only need it on SVR3.?.
 */

#define SPCLONE	"/dev/spx"

spipe(fds)
	int *fds;
{
        struct strfdinsert fdi;
	long dummy;

	if ((fds[0] = open(SPCLONE, O_RDWR)) < 0)
		return(-1);
	if (ioctl(fds[0], I_PUSH, "kbd")) {
		fprintf(stderr, gettxt("kbdpipe:15", "%s: unable to push \"kbd\" module.\n"), prog);
		exit(1);
	}
	if ((fds[1] = open(SPCLONE, O_RDWR)) < 0)
		return(-1);
	fdi.databuf.maxlen = fdi.databuf.len = -1;
	fdi.databuf.buf = NULL;
	fdi.ctlbuf.maxlen = fdi.ctlbuf.len = sizeof(long);
	fdi.ctlbuf.buf = (caddr_t)&dummy;
	fdi.offset = 0;
	fdi.fildes = fds[1];
	fdi.flags = 0;
	if (ioctl(fds[0], I_FDINSERT, &fdi) < 0)
		return(-1);

	return(0);
}
#endif	/* ! SVR4 */

tach(name, fd)
	char *name;
	int fd;
{
	struct strioctl sb;
	struct kbd_tach t;
	register int rval;

	sb.ic_cmd = KBD_ATTACH;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct kbd_tach);
	sb.ic_dp = (char *) &t;
	strcpy(t.t_table, name);
	t.t_type = Z_UP;
	rval = ioctl(fd, I_STR, &sb);
	if (rval) 
		return(0);	/* attach failed */

	return(1);
}

turnon(fd)
	int fd;
{
	struct strioctl sb;
	struct kbd_ctl c;
	register int rval;

	sb.ic_cmd = KBD_ON;
	sb.ic_timout = 15;
	sb.ic_len = sizeof(struct kbd_ctl);
	sb.ic_dp = (char *) &c;
	c.c_type = Z_UP;
	rval = ioctl(fd, I_STR, &sb);
	if (rval) {
		fprintf(stderr, gettxt("kbdpipe:16", "%s: can't turn on mapping (%d).\n"), prog, errno);
		return(0);
	}
	return(1);
}
