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.