ftp.nice.ch/pub/next/tools/printer/iwf.2.0.N.bs.tar.gz#/iwf.c

This is iwf.c in view mode; [Download] [Up]

/* iwf--Imagewriter II line printer filter
 * Eric P. Scott, San Francisco State University, November 1990
 * Copyright 1990, 1991 by Eric P. Scott.  All rights reserved.
 *
 * This file (and the accompanying iwwraps.psw) are freely
 * redistributable in their original, unmodified forms.  The
 * author grants you a nonexclusive, royalty-free license to use
 * this software, and modify it as needed provided that this
 * copyright notice remains, and any changes are clearly
 * indicated as such.  No part may be incorporated in products or
 * packages sold for profit without express permission of the
 * copyright holder.
 *
 * Send bug reports, enhancements, etc. to <eps@cs.sfsu.edu>
 *
 * This software makes certain assumptions that are valid in NeXT
 * Software Release 2.0, and may not hold in future releases.
 * NO WARRANTY is expressed or implied--use at your own risk!
 *
 * It's not the best code in the world--far from it--such is the
 * price of working under time pressure...
 *
 * What it doesn't do:
 *   8-bit ASCII
 *   use RLE (ESC V nnnn b) to reduce number of characters output
 *   trap SIGINT to guarantee writing accounting record
 *
 * Revision History
 * 11/ 6/90  EPS  1.0 release
 * 11/ 7/90  EPS  1.1 release
 *                fixed "creeping pixel" bug
 *                changed ysize default from 792 to length*12
 * internal  EPS  1.2
 *                <streams/error.h> => <objc/error.h>
 *                recognize R (Resolution) argument
 *  3/28/91  EPS  2.0 release
 *                significant rewrite to use machportdevice
 *                instead of offscreen window
 *                add "sidedoor" code for no PublicWindowServer
 */
#include <stdio.h>
#include <pwd.h>
#include <signal.h>
#include <cthreads.h>
#include <servers/netname.h>
#include <windowserver/printmessage.h>
#import <dpsclient/dpsclient.h>
#import <objc/error.h>

#ifndef lint
static char sccsid[]="@(#)iwf.c\t2.0 (SFSU) 3/28/91";
#endif
int literal;
char *host;
int indent;
int length;
char *name;
int width;
int xsize, ysize;
int linect, pagect;
int status;		/* exit status (0=ok; 1=reprint; 2=flush) */
port_t pport;		/* port for machportdevice */
netname_name_t iwfportname;	/* name for machportdevice */
NXPrintPageMessage ppm;	/* Mach messaging area */
#define KODMSGID 0xdeadbeef
mutex_t mutex1;

/* ImageWriter II setup:
 * ASCII characters
 * American language
 * 6 lpi
 * clear tabs
 * cancel headline, underline, boldface, half-high, subscript
 * slashed zero begin
 * bidirectional printing
 * paper error detector on
 * add CR before LF
 * no LF when line is full
 * black ribbon
 * end of line at CR or FF
 * ignore 8th bit
 * disable perforation skip
 * proportional dot spacing=0
 * "burp"
 */
static char setup[]="\
\033$\
\033Z\007\0\
\033A\
\0330\
\017\033Y\033\"\033W\033z\
\033D\0\001\
\033<\
\033o\
\033l0\
\033Z \0\
\033K0\
\033D@\0\
\033D\0 \
\033D\0\004\
\033s0\
\033f\n\033r\n\033f";

char *ibuf;

main(int argc, char *argv[])
{
	extern void *malloc();	/* stupid ANSI!!! */
	extern void minit(int w, int h, char *portname);
	extern int optind;
	extern char *optarg;
	void _textpl(DPSContext ctx, char *buf, unsigned long count);
	int c, l;
	char *b;
	FILE *af;
	DPSContext pctx;
	struct passwd *pw;
	cthread_t rthread;	/* reader thread */
	static msg_header_t kod;
	char buf[8192];

	while ((c=getopt(argc, argv, "cf:h:i:l:n:p:w:x:y:R:"))!=EOF)
		switch (c) {
	case 'c':
		literal++;
		break;
	case 'f':	/* file--NeXT extension */
		break;
	case 'h':
		host=optarg;
		break;
	case 'i':
		indent=atoi(optarg);
		break;
	case 'l':
		length=atoi(optarg);
		if (length>416) length=416;
		break;
	case 'n':
		name=optarg;
		break;
	case 'p':	/* printer--NeXT extension */
		break;
	case 'w':
		width=atoi(optarg);
		if (width>160) width=160;
		break;
	case 'x':
		xsize=atoi(optarg);
		if (xsize>1600) xsize=1600;
		break;
	case 'y':
		ysize=atoi(optarg);
		if (ysize>16376) ysize=16376;
		break;
	case 'R':	/* Resolution--NeXT extension [2.0] */
		break;
	default:
	usage:
		(void)fprintf(stderr, "Usage: %s [options] [acctfile]\n",
			*argv);
		(void)fputs("\t-c  don't interpret control characters\n",
			stderr);
		(void)fputs("\t-hhost  host name of job owner\n", stderr);
		(void)fputs("\t-i# indent\n", stderr);
		(void)fputs("\t-l# page length in lines\n", stderr);
		(void)fputs("\t-nname  login name of job owner\n", stderr);
		(void)fputs("\t-w# page width in characters\n", stderr);
		(void)fputs("\t-x# horizontal page size in pixels\n", stderr);
		(void)fputs("\t-y# vertical page size in pixels\n", stderr);
		exit(2);	/* can't use <sysexits.h>!!! */
		break;
	}
	af=(FILE *)NULL;
	switch (argc-optind) {
	case 0:
		break;
	case 1:
		if (argv[optind][0]&&!(af=fopen(argv[optind], "a"))) {
			perror(argv[optind]);
			exit(2);
		}
		break;
	default:
		goto usage;
	}
	if (length<=0) length=66;
	if (width<=0) width=132;
	if (indent>=width) indent=width-1;
	if (xsize<=0) xsize=612;
	if (ysize<=0) ysize=length*12;
	ysize=(ysize+7)/8;	/* this is "wrong" but simplifies things */
	ysize*=8;
	/* <stdio.h> declares this as int but man page says void */
	(void)setvbuf(stdout, (char *)NULL, _IOFBF, 8192);
	(void)fwrite(setup, 1, (sizeof setup)-1, stdout);
	(void)printf("\033L%03u\033H%04u", indent, length*24);
	if ((c=getchar())==EOF) exit(0);
	if (c!='%') {
	normal:	/* text mode filter */
		(void)seteuid(getuid());
		putchar('\033');	/* set type size */
		putchar(width<=72 ? 'n' :
			width<=80 ? 'N' :
			width<=96 ? 'E' :
			width<=107 ? 'e' :
			width<=120 ? 'q' :
			width<=136 ? 'Q' :
			width<=144 ? 'p' : 'P');
		/* tab stops */
		for (l=9;l<width;l+=8) (void)printf("\033u%03u", l);
		do {
		rerun:
			switch (c) {	/* "but it's one instr. on a VAX" */
			case '\0':
			case '\001':
			case '\002':
			case '\003':
			case '\004':
			case '\005':
			case '\006':
			case '\007':
			case '\013':
			case '\016':
			case '\017':
			case '\020':
			case '\021':
			case '\022':
			case '\023':
			case '\024':
			case '\025':
			case '\026':
			case '\027':
			case '\030':
			case '\032':
			case '\033':
			case '\034':
			case '\035':
			case '\036':
			case '\037':
				putchar(literal ? c : ' ');
				break;
			case '\031':	/* Only at Berkeley... */
				if ((c=getchar())==EOF) {
					l='\031';
					putchar(literal ? l : ' ');
					goto done;
				}
				if (c=='\001') {
					(void)fflush(stdout);
					(void)kill(getpid(), SIGSTOP);
					continue;
				}
				putchar(literal ? '\031' : ' ');
				goto rerun;
			case '\n':
				linect++;
				if (linect<length) goto anyway;
				/*FALL THROUGH*/
			case '\f':
				pagect++; linect=0;
				/*FALL THROUGH*/
			case '\b':	/* you expected intelligence? */
			case '\t':
			case '\r':
			default:
			anyway:
				putchar(c);
				break;
			}
			l=c;
		} while ((c=getchar())!=EOF);
	done:
		switch (l) {
		default:
			putchar('\n');
			linect++; if (linect>=length) pagect++;
			/*FALL THROUGH*/
		case '\n': case '\r':
			putchar('\f');
			pagect++;
			/*FALL THROUGH*/
		case '\f':
			break;
		}
	}
	else {
		int reader(any_t arg);
      
		if ((c=getchar())==EOF) {
			(void)seteuid(getuid());
			l='%';
			putchar(l);
			goto done;
		}
		if (c!='!') {
			putchar('%'); goto normal;
		}
		/* At this point, we are committed to PostScript */
		if (!(ibuf=(char *)malloc(xsize))) abort();	/* maybe not */
		bzero(ibuf, xsize);
		pctx=(DPSContext)NULL;
	NX_DURING
		/* attempt to connect to Window Server using defaults */
		pctx=DPSCreateContext((void *)NULL, (void *)NULL,
			_textpl, DPSDefaultErrorProc);
	NX_HANDLER
		if (NXLocalHandler.code!=dps_err_cantConnect) NX_RERAISE();
		NX_DURING
			void sidedoor();
			sidedoor();	/* try to climb in through side door */
			pctx=DPSCreateContext((void *)NULL, (void *)NULL,
				_textpl, DPSDefaultErrorProc);
		NX_HANDLER
			if (NXLocalHandler.code!=dps_err_cantConnect)
				NX_RERAISE();
		NX_ENDHANDLER
		if (!pctx) {
/* This is so lame.  NeXT should make _NXDpsReporter public!!! */
			(void)fputs(
"DPS client library error: Could not form connection, host ", stderr);
			(void)fputs((char *)NXLocalHandler.data2, stderr);
			putc('\n', stderr);
		}
	NX_ENDHANDLER
		if (!pctx) exit(2);
		DPSSetContext(pctx); /* per postscript.321 this is unneeded */
		(void)seteuid(getuid());
		if ((c=port_allocate(task_self(), &pport))!=KERN_SUCCESS) {
			mach_error("port_allocate failed", c);
			exit(2);
		}
		(void)sprintf(iwfportname, "iwf-%d", getpid());
		if ((c=netname_check_in(name_server_port, iwfportname,
			PORT_NULL, pport))!=KERN_SUCCESS) {
			mach_error("netname_check_in failed", c);
			exit(2);
		}
		putchar('\033'); putchar('>');	/* unidirectional printing */
		putchar('\033'); putchar('n');	/* 72 dpi */
		putchar('\033'); putchar('T'); putchar('1'); putchar('6');
		if (pw=getpwnam(name ? name : "nobody"))
			DPSPrintf(pctx, "%d %d setjobuser\n", pw->pw_uid,
			pw->pw_gid);
		/* endpwent() ? */
		minit(xsize, ysize, iwfportname);	/* from iwwraps.psw */
		DPSWaitContext(pctx);
		/* I don't want to rewrite things to use DPSAddPort()
		 * and friends, and C Threads are probably the lesser
		 * of several evils.
		 */
		mutex1=mutex_alloc();
		rthread=cthread_fork((cthread_fn_t)reader, (any_t)0);
		mutex_lock(mutex1);
		DPSPrintf(pctx, "/-saveIW-%d- save def\n/note {} def\n\
/envelope {} def\n%%%%BeginDocument: IWGraphic\n", getpid());
		mutex_unlock(mutex1);
	NX_DURING
		b=buf; *b++='%'; *b++='!';
		l=0;	/* sick, disgusting! */
		while ((c=getchar())!=EOF) {
			*b++=c;
			if (c=='\n'||b>&buf[sizeof buf]) {
				/* what a crock... */
				if (buf[0]=='%') {
					if (buf[1]=='%') {
						if (!l) DPSWaitContext(pctx);
						l=1;
					}
				}
				else l=0;
				DPSWritePostScript(pctx, buf, b-buf);
				b=buf;
			}
		}
		if (b>buf) {
			*b++='\n';
			DPSWritePostScript(pctx, buf, b-buf);
		}
		DPSWaitContext(pctx);
		if (pagect==0) {	/* wrong, but expected */
			mutex_lock(mutex1);
			DPSPrintf(pctx, "\nshowpage\n");
			mutex_unlock(mutex1);
			DPSWaitContext(pctx);
		}
	NX_HANDLER
		if (NXLocalHandler.code!=dps_err_ps) NX_RERAISE();
		else {
			mutex_lock(mutex1);
			DPSPrintError(stderr,
				(DPSBinObjSeq)(NXLocalHandler.data2));
			putc('\n', stderr);
			mutex_unlock(mutex1);
		}
		status=2;
	NX_ENDHANDLER
		mutex_lock(mutex1);
		DPSPrintf(pctx, "%%%%EndDocument\n-saveIW-%d- restore\n",
			getpid());
		mutex_unlock(mutex1);
		DPSWaitContext(pctx);
		PSnulldevice();
		DPSWaitContext(pctx);
		/* fake "EOF" on port */
		kod.msg_simple=TRUE;
		kod.msg_size=sizeof kod;
		kod.msg_type=MSG_TYPE_NORMAL;
		kod.msg_local_port=PORT_NULL;
		kod.msg_remote_port=pport;
		kod.msg_id=KODMSGID;
                if ((c=msg_send(&kod, MSG_OPTION_NONE, 0))!=SEND_SUCCESS) {
			mutex_lock(mutex1);
                        mach_error("msg_send failed", c);
			mutex_unlock(mutex1);
		}
		else pagect=(int)cthread_join(rthread);
		if (*iwfportname&&
			(c=netname_check_out(name_server_port, iwfportname,
			PORT_NULL))!=KERN_SUCCESS) {
			mutex_lock(mutex1);
			mach_error("netname_check_out failed", c);
			mutex_unlock(mutex1);
		}
		(void)port_deallocate(task_self(), pport);
		DPSDestroySpace(DPSSpaceFromContext(pctx));
	}
	if (af) {
		if (name) {
			mutex_lock(mutex1);
			(void)fprintf(af, "%4d.00\t", pagect);
			if (host) (void)fprintf(af, "%s:", host);
			(void)fprintf(af, "%s\n", name);
			mutex_unlock(mutex1);
		}
		(void)fclose(af);
	}
	exit(status);
}

/* Output a horizontal band (pointer to data, number of columns) */
/* This function really should be a lot smarter */
void bitout(char *ib, int il)
{
	if (il>0) {
		if ((il&7)==0) (void)printf("\033g%03u", il/8);
		else (void)printf("\033G%04u", il);
		(void)fwrite(ib, 1, il, stdout);
	}
	putchar('\n'); (void)fflush(stdout);
}

/* msg_receive blocks until something happens, so this runs in
 * its own thread.  I'm trying to do a minimum amount of mutex
 * locking (set fingers=crossed).
 *
 * This code could stand a serious rethink...
 */
int reader(any_t arg) {
	register char *colp;
	register int rmask;
	register char *p, *r, *w, *eop;
	register msg_return_t m;
	char *cmax;

	for (;;) {
		ppm.msgHeader.msg_size=sizeof ppm;
		ppm.msgHeader.msg_local_port=pport;
		if ((m=msg_receive(&ppm.msgHeader, RCV_INTERRUPT|
			RCV_NO_SENDERS, 0))!=RCV_SUCCESS) {
			mutex_lock(mutex1);
			mach_error("msg_receive failed", m);
			mutex_unlock(mutex1);
			break;
		}
		if (ppm.msgHeader.msg_id==KODMSGID) break;
		if (ppm.msgHeader.msg_id!=NX_PRINTPAGEMSGID||
			ppm.printPageVersion!=3) continue;	/* munchers */
		if (*iwfportname) {	/* recall send rights */
			register kern_return_t s;
			if ((s=netname_check_out(name_server_port,
				iwfportname, PORT_NULL))!=KERN_SUCCESS) {
				mutex_lock(mutex1);
				mach_error("netname_check_out failed", s);
				mutex_unlock(mutex1);
			}
			*iwfportname='\0';
			if (ppm.oolImageParam.msg_type_long_size!=8) {
				/* woke up in the wrong universe... */
				if (port_deallocate(task_self(), pport)==
					KERN_SUCCESS) pport=PORT_NULL;
				return(0);
			}
		}
		if (ppm.pixelsWide>xsize) ppm.pixelsWide=xsize;	/* uh-oh! */
		/* The Window Server returns horizontal scan-lines, but
		 * the printhead's pins are arranged vertically.  Take
		 * eight scan lines and form a band.
		 */
#ifdef UPSIDEDOWN
		rmask=128;
#else
		rmask=1;
#endif
		colp=ibuf; cmax=colp;
		p=(char *)ppm.printerData;
		r=p+ppm.bytesPerRow;
		w=ibuf+ppm.pixelsWide;
		eop=p+ppm.oolImageParam.msg_type_long_number;
		while (p<eop) {
			/* unrolled loop */
			if ((*p&0x80)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&0x40)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&0x20)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&0x10)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&8)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&4)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if ((*p&2)!=0) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) goto foo;
			if (*p&1) {
				*colp++|=rmask;
				if (colp>cmax) cmax=colp;
			}
			else colp++;
			if (colp>=w) {
			foo:
				p=r;	/* raster could be padded */
				r=p+ppm.bytesPerRow;
				colp=ibuf;
#ifdef UPSIDEDOWN
				if ((rmask/=2)==0) {
					rmask=128;
#else
				if ((rmask*=2)>128) {
					rmask=1;
#endif
					mutex_lock(mutex1);
					bitout(ibuf, cmax-ibuf);
					mutex_unlock(mutex1);
					cmax=colp;
					bzero(ibuf, ppm.pixelsWide);
				}
			}
			else p++;
		}
		pagect++;
		/* unmap and ACK page */
		(void)vm_deallocate(task_self(),
			(vm_address_t)ppm.printerData,
			(vm_size_t)ppm.oolImageParam.msg_type_long_number);
		ppm.msgHeader.msg_simple=TRUE;
		ppm.msgHeader.msg_size=sizeof ppm.msgHeader;
		ppm.msgHeader.msg_type=MSG_TYPE_NORMAL;
		ppm.msgHeader.msg_local_port=PORT_NULL;
		ppm.msgHeader.msg_id=NX_PRINTPAGEMSGID;
		if ((m=msg_send(&ppm.msgHeader, MSG_OPTION_NONE, 0))!=
			SEND_SUCCESS) {
			mutex_lock(mutex1);
			mach_error("msg_send failed", m);
			mutex_unlock(mutex1);
			break;
		}
	}
	return(pagect);
}

/* DPSCreateContext[WithTimeoutFromZone] looks in two places for
 * a port to the Window Server:
 *
 * 1) The Bootstrap Server for "WindowServer"
 * 2) The Network Name Server for "NextStep(tm) Window Server"
 *
 * If PublicWindowServer is *not* set, then (2) won't be checked
 * in, and (1) will be checked in with a subset bootstrap port
 * and available only to descendents of loginwindow (i.e.
 * Workspace and its descendents).
 *
 * This horrible hack set us up to inherit from loginwindow, as
 * if we were a proper descendent.
 * 
 * Most is lifted straight from the 2.0 Reference.
 * table() is undocumented according to NextAnswers.
 *
 * N.B. when a user with PublicWindowServer==No logs out, the
 * subset port is destroyed and neither (1) nor (2) will work.
 * No one said life was fair.
 *
 * This code *must* run as root; iwf must be set-uid root if
 * we're to be standalone (not running under control of lpd).
 * 
 */
#include <sys/table.h>
#include <servers/bootstrap.h>
#ifndef lint
static char sccsid_[]="@(#)sidedoor.c\t1.0 (SFSU) 3/27/91";
#endif
void sidedoor() {
    register host_priv_t hpriv;
    register unsigned int hidx, tidx;
    processor_set_name_array_t hlist;
    unsigned int hcount;
    processor_set_t ppriv;
    task_array_t tlist;
    unsigned int tcount;
    int pid, mypid;
    port_t b;
    struct tbl_procinfo pi;

    if (!(hpriv=host_priv_self())) {	/* must run as root! */
	(void)fputs("couldn't get host privileged port\n", stderr);
	return;
    }
    /* superuser privileges are not required beyond this point */
    if (host_processor_sets(hpriv, &hlist, &hcount)!=KERN_SUCCESS)
	(void)fputs("couldn't get processor sets\n", stderr);
    else {
	mypid=getpid();
	for (hidx=0;hidx<hcount;hidx++) {
	    if (host_processor_set_priv(hpriv, hlist[hidx], &ppriv)!=
		KERN_SUCCESS) ppriv=hlist[hidx];
	    if (processor_set_tasks(ppriv, &tlist, &tcount)==KERN_SUCCESS) {
		for (tidx=0;tidx<tcount;tidx++) {
		    if (unix_pid(tlist[tidx], &pid)==KERN_SUCCESS&&pid>3) {
			if (pid==mypid) continue;
			if (table(TBL_PROCINFO, pid, (char *)&pi, 1,
			    sizeof pi)==1&&
			    pi.pi_status==PI_ACTIVE&&pi.pi_ppid==1&&
			    pi.pi_ttyd<0&&pi.pi_uid==0&&
			    !strncmp(pi.pi_comm, "loginwindow", PI_COMLEN)&&
			    task_get_bootstrap_port(tlist[tidx], &b)==
			    KERN_SUCCESS&&b!=PORT_NULL) {
			    if (task_set_bootstrap_port(task_self(), b)==
				KERN_SUCCESS) bootstrap_port=b;
			    else (void)port_deallocate(task_self(), b);
			}
		    }
		    (void)port_deallocate(task_self(), tlist[tidx]);
		}
		(void)vm_deallocate(task_self(), (vm_address_t)tlist,
		    tcount*sizeof (port_t));
	    }
	    else (void)fprintf(stderr,
		"couldn't get tasks for processor set %d\n", hidx);
	    (void)port_deallocate(task_self(), ppriv);
	    if (hlist[hidx]!=ppriv)
		(void)port_deallocate(task_self(), hlist[hidx]);
	}
	(void)vm_deallocate(task_self(), (vm_address_t)hlist,
	    hcount*sizeof (processor_set_name_t));
    }
    (void)port_deallocate(task_self(), hpriv);
    /* have I deallocated everything I should? */
}

/* this shouldn't ever be called... */
void _textpl(DPSContext ctx, char *buf, unsigned long count)
{
	(void)write(2, buf, count);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.