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.