/*
 *  plotter.c
 *  output driver for HP7470A plotter
 *  copyright 1986 Maple Lawn Farm, Inc.
 *
 *  options:
 *  -t		reads points scaled to Tektronix (4096x3120)
 *  -p		reads xmin,ymin,xmax,ymax scaling points from input
 *  -l title	left-justified title
 *  -c title	centered title
 *  -d dev	specifies output device (tty00 is default)
 *
 *  compile:  cc -O -s -o plotter plotter.c
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <termio.h>
#include <signal.h>

#define CENTER	02
#define SCALED	04
#define TEK	01
#define	BAUD 	B9600
#define HPXON	"\033.I80;;17:"		/* 80 char buffer, xon char */
#define HPXOFF	"\033.N;19:"		/* xoff char */
#define HPSTAT	"\033.O"
#define HPABORT	"\033.K"
#define HPRSERR	"\033.E"
#define	LOCK	"/usr/spool/uucp/LCK.."
#define USAGE	"usage:	%s [-tp] [-d device] [-c | -l title]\n"
#define ERR(a,b)	fprintf(stderr, "%s: ", prognm),\
			fprintf(stderr, a, b)

char	*title, 
	*prognm, 
	*plotdev = "/dev/tty00",	/* default device */
	lock[sizeof(LOCK) + 5] = LOCK; 
FILE	*plr, 
	*plw,
	*fi = stdin;
int	hflag, 				/* heading flag */
	pflag,				/* points, not HP-GL */
	die(), quit();


main(argc, argv)
int  argc;
char **argv;
{
	int	fd, hperr, alrmint();  
	char	buf[BUFSIZ];

	prognm = *argv;
	scanarg(argc, argv);		
					/* check/make lock */
	do_lock();
					/* trap exits */
	signal(SIGQUIT, die);
	signal(SIGINT, die);
	signal(SIGALRM, alrmint);
					/* line OK? */
	if ((fd=open(plotdev, O_RDWR|O_NDELAY)) < 0 ) 
		perror(plotdev), die();
					/* set line & handshake */
	setline(fd);
	fputs(HPXON, plw);
	fputs(HPXOFF, plw);
					/* check plotter */
	alarm(5);
	do {
		hperr = readhp(HPSTAT);
	} while (hperr < 0 || hperr > 40);
	if (hperr > 8)
		ERR("not ready\n", NULL);
	alarm(0);
					/* make sure we clean up */
	signal(SIGINT, quit);
					/* initialize plotter */
	fputs("in;", plw);
	if (hflag)
		heading(title);	
					/* pen 1 */	
	fputs("sp1;", plw);
					/* xy points ? */
	if (pflag)	
		points();	
					/* HP-GL instructions */
	else 			
		while (fgets(buf, sizeof buf, fi) != NULL)
			fputs(buf, plw);
					/* check for errors */
	alarm(60);
	if ((hperr = readhp("OE;")))
		ERR("HP-GL error = %d\n", hperr);
	if ((hperr = readhp(HPRSERR)))
		ERR("rs232 error = %d\n", hperr);
	alarm(0);
					/* pen home, unlock */
	fputs("sp0;", plw);
	die();
}


heading(title)
char  *title;
{
	int	tx, ty;
					/* fix location of title */
	ty = 7350;
	tx = (hflag & CENTER) ? 5150 : 500;
					/* pen 2, char size */
	fprintf(plw, "sp2;si.30,.48;pu%d,%d;", tx, ty);
	if (hflag & CENTER) 
		fprintf(plw, "cp -%d,0;", strlen(title)/2);
	fprintf(plw, "lb%s\003", title);	
}


points()
{
	int	first=0;
	double	x, y, ipx1, 
		xmin = 0.0, 		/* default tektronix scaling */
		ymin = 0.0, 
		xmax = 4096.0, 
		ymax = 3120.0; 

					/* need scaling points? */
	if (pflag & SCALED)  { 
		if (fscanf(fi, "%f%f%f%f", 
			&xmin, &ymin, &xmax, &ymax) == EOF)
			quit();
		if (xmin >= xmax || ymin >= ymax)
			ERR("invalid scaling points", NULL), quit();
	}
					/* get aspect ratio 
				         * set new p1 
					 */
	ipx1 = 10250 - (7200 * (xmax-xmin)/(ymax-ymin));
	fprintf(plw, "ip %.f,279,10250,7479;", ipx1);

					/* scale to user units */
	fprintf(plw, "sc %.f,%.f,%.f,%.f;", 
			xmin, xmax, ymin, ymax);
					/* read and output points */
	while (fscanf(fi, "%f%f", &x, &y) != EOF) 
		fprintf(plw,(++first==1) ?\
			"pu%.4f,%.4f;pd" : "%.4f,%.4f ",x,y);
	fputs("pu;", plw);
}


alrmint()
{
	ERR("no response\n", NULL);
	die();
}


die()
{
	if (unlink(lock) == -1) 
		perror(lock);
	exit(0);
}


quit()
{
	fputs(HPABORT, plw);
	fputs(" sp0;", plw);
	die();
}


scanarg(argc, argv)
int  argc;
char **argv;
{
	extern	int	optind;
	extern	char	*optarg;
	int	i;

        while ((i = getopt(argc, argv, "d:tpc:l:h?")) != EOF)
                switch (i)  {
		case 'd' :		/* device specified */
			strcpy(plotdev, optarg);  
			break;
		case 'p' :		/* scaling points */
			pflag |= SCALED;
			break;
		case 't' :		/* tektronix points */
			pflag |= TEK;
			break;
		case 'c' :		/* centered title */
			hflag |= CENTER;
                case 'l' :		/* left-justified */
			hflag |= 01;
			title = optarg;
			break;
		case 'h' :
		case '?' :
			fprintf(stderr, USAGE, prognm), exit(1);
		}
        if (argc > 1 && argc != optind)
		if ((fi=fopen(argv[optind], "r")) == NULL)
			ERR("cannot find %s\n", 
				argv[optind]), exit(1);
}


setline(fd)
int  fd;
{	
	struct termio term;

	ioctl(fd, TCGETA, &term);

	term.c_cflag &= ~CBAUD;
	term.c_cflag |= BAUD|CLOCAL;
	term.c_lflag &= ~ECHO;
	term.c_iflag |= ICRNL|IXON;
        term.c_cc[VMIN] = 1;
        term.c_cc[VTIME] = 0;
				/* drain output 
				 * flush input queue 
				 */
        ioctl(fd, TCSETAF, &term);
	if(!(plr=fopen(plotdev,"r")) || !(plw=fopen(plotdev,"w")))
		die();
				/* unbuffered output */
	setbuf(plw,0);
}


readhp(query)
char  *query;
{
	int  hperr;

 	fputs(query, plw);
	if (fscanf(plr, "%d", &hperr) == EOF) 
		die();
	return(hperr);
}


do_lock()
{
 				/* alternate lock */
	char	*cu = "/usr/spool/uucp/LCK..cul ",
		dvc[5], *strchr();
	int	ld;

				/* parse device name */
	if (strncmp(plotdev, "/dev/", 5))  {
		strcpy(dvc, plotdev);
		strcpy(plotdev, "/dev/");
		strcat(plotdev, dvc);
	}
	else 
		strcpy(dvc, strchr(plotdev, 't'));
				/* setup lock file names */	
	strcat(lock, dvc);
	cu[strlen(cu) -1] = dvc[strlen(dvc) -1];
				/* check for locks */
	if (!access(lock,0) || !access(cu,0))
		ERR("%s is locked\n", dvc), exit(1);
				/* create a lock */
	if ((ld = creat(lock, 0644)) < 0)
		perror(lock), exit(1);
	close(ld);
}
