ftp.nice.ch/pub/next/tools/printer/Deskjet500.N.bs.tar.gz#/djf/djf.c

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

/* Djf--Deskjet 500 printer filter
 * Reimer Mellin & Uwe Meyer-Gruhl August 1991 with help of Christian Baur
 * based on iwf by:
 *
 * Eric P. Scott, San Francisco State University, November 1990
 * Copyright 1990, 1991 by Eric P. Scott.  All rights reserved.
 *
 * This file (and the accompanying djwraps.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>
 * and <ram@ramsys.sta.sub.org>
 *
 * 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
 *   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
 *
 * Deskjet revision History:
 * August 91 ram  1.1
 *				adapted to DeskJet
 * September 91 umg  1.2
 *				changes for PIN-optimization, cleanup, dither & halftone dicts by
 *				Christian Baur
 * November  91 ram  1.3
 *				rewrite of compression-routines with help of pclcomp
 *				fixes for A4
 * December  91 umg  1.4
 *				rewrite of compression part to improve speed
 *				more fixes for A4 (larger printable area)
 *				compile-time variables to support DJ+ and DJ
 * December 91 ram 1.5
 *				ymaf (yet more A4 fixes, won 1/4" :-), final testing and bundling
 *				for release.
 */
#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<libc.h>
#include	<limits.h>
#include	<mach_error.h>
#include	<pwd.h>
#include	<signal.h>
#include	<cthreads.h>
#include	<servers/netname.h>
#include	<windowserver/printmessage.h>
#include	<sys/table.h>
#include	<servers/bootstrap.h>
#import		<dpsclient/dpsclient.h>
#import		<dpsclient/psops.h>
#import		<objc/error.h>

#ifndef lint
static char rcsid[] = "$Id: djf.c,v 1.5 91/12/06 01:10:05 ram Exp $";
static char sccsid_[] = "@(#)sidedoor.c\t1.0 (SFSU) 3/27/91";
#endif

/* moved to Makefile */
/* #define	noUSE_1		/*	slow & inefficient!	*/
/* #define	USE_3		/*	Disable for DJ+ and DJ	*/


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 djfportname;	/* name for machportdevice */
NXPrintPageMessage ppm;		/* Mach messaging area */

#define KODMSGID 0xdeadbeef
mutex_t mutex1;

/*
 * CR -> CR+LF, LF -> CR+LF
 */
static char setup[] = { 27, '&', 'k', '3', 'G' };
static char job_init[] = { 27, '&', 'l', '0', 'l', '6', 'd', '7', '2', 'p',
													'0', 'e', '7', '1', 'F' };
/* sizing > a4 gives another 1/4" */
static char job_exit[] = { 27, '&', 'l', '1', 'L' };
static char page_init[] = { 27, '*', 'p', '0', 'x', 'p', '1', 'y', 'r', '1', 'A' }; /* *p0x1yr1a gives yet another 1/8" 'headroom' */
static char page_exit[] = { 27, '*', 'r', 'B', 27, '&', 'l', '0', 'H' };
#define	sendsequence(s)	(void) fwrite(s, 1, sizeof(s), stdout)


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

/* init some vars */
	xsize = ysize = linect = pagect = status =
		width = length = indent = literal = 0;

#ifdef DEBUG
	{
		int i;

		(void) fprintf(stderr, "argc=%d,argv= ", argc);
		for (i = 0; i < argc; i++)
			(void) fprintf(stderr, "%s ", argv[i]);
		(void) fprintf(stderr, "\n");
	}
#endif

/* determine resolution from argv[0] */
	b = (char *) rindex(argv[0], '/');
	if (b == NULL)
		b = argv[0];
	else
		b++;
	if (sscanf(b, "dj%df", &res) != 1) {
		res = 300;		/* default resolution !! */
	}

#ifdef DEBUG
	(void) fprintf(stderr, "res = %d\n", res);
#endif


	while ((c = getopt(argc, argv, "cf:h:i:l:n:p:w:x:y:R:")) != EOF) {
		switch (c) {
			/* see lpd(1) */

		/* cifplot generated file ?? */
		case 'c':
			literal++;
			break;

		/* formatted file-name ?? */
		case 'f':		/* file--NeXT extension */
			break;

		/* hostname */
		case 'h':
			host = optarg;
			break;

		/* number of chars to indent */
		case 'i':
			indent = atoi(optarg);
			break;

		/* length in chars ?? */
		case 'l':
			length = atoi(optarg);
			if (length > 198)
				length = 198; /* 11" * 72 pt /4 pt */
			break;

		/* */
		case 'n':
			name = optarg;
			break;

		/* printer name */
		case 'p':		/* printer--NeXT extension */
			break;

		/* width in char ? */
		case 'w':
			width = atoi(optarg);
			if (width > 266)
				width = 266; /* 33.33cpi */
			break;

		/* width in pixel */
		case 'x':
			xsize = atoi(optarg);
			break;

		/* height in pixel */
		case 'y':
			ysize = atoi(optarg);
			break;

		/* resolution : 300|400 always 400 */
		case 'R':		/* Resolution--NeXT extension [2.0] */
			res = atoi( optarg );
			if( (res != 75) && (res != 100) && (res != 150) && (res != 300))
				res = 300;
			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"))) {
				(void) perror(argv[optind]);
				exit(2);
			}
			break;
		default:
			goto usage;
	}

	if (length <= 0)
		length = 66;		/* lines */
	if (width <= 0)
		width = 132;		/* chars */
	if (indent >= width)
		indent = width - 1;
	if (xsize <= 0  || xsize > (8 * res))
		xsize = 8 * res;
	if (ysize <= 0 || ysize > ( 11.315 * res)) /* 11.69 - 1/4 - 1/8 */
		ysize = 11.315 * res; /* change to 11 - 1/4 - 1/8 for US Letter */

/* <stdio.h> declares this as int but man page says void */

	(void) setvbuf(stdout, (char *) NULL, _IOFBF, 32768);

	sendsequence(setup);

	if ((c = getchar()) == EOF)
		exit(0);

	if (c != '%') {
normal:			/* text mode filter */
		(void) seteuid(getuid());

/* indent in chars, length = # lines */
#ifdef IWF
		(void) printf("\033L%03u\033H%04u", indent, length * 24);
#else
		(void) printf("\033&a%dL", indent);
#endif

/* width = # chars in line */
#ifdef IWF
		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);
#else
		/* TESTEN !!! */
		(void) printf("\033(s%dH", width / 8);
#endif

		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 {
		/* Postscript mode */

		int reader(any_t arg);

#ifdef DEBUG
		(void) fprintf(stderr, "postscript mode: xsize = %d, ysize = %d\n",
			xsize, ysize);
#endif
		if ((c = getchar()) == EOF) {
			(void) seteuid(getuid());
			l = '%';
			putchar(l);
			goto done;
		}

		if (c != '!') {
			putchar('%');
			goto normal;
		}

		/*	Now PostScript	*/
		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(djfportname, "djf-%d", getpid());
		if ((c = netname_check_in(name_server_port, djfportname,
					  PORT_NULL, pport)) != KERN_SUCCESS) {
			mach_error("netname_check_in failed", c);
			exit(2);
		}

		(void) printf("\033E\033*t%dR", res);	/* reset, res DPI */

		if (pw = getpwnam(name ? name : "nobody"))
			DPSPrintf(pctx, "%d %d setjobuser\n", pw->pw_uid,
				  pw->pw_gid);
		/* endpwent() ? */

#ifdef DEBUG
		(void) fprintf(stderr, "calling minitxxx()\n");
#endif
		switch (res) {

		case 75:
			minit75(xsize, ysize, djfportname);	/* from djwraps.psw */
			break;

		case 100:
			minit100(xsize, ysize, djfportname);	/* from djwraps.psw */
			break;

		case 150:
			minit150(xsize, ysize, djfportname);	/* from djwraps.psw */
			break;

		case 300:
			minit300(xsize, ysize, djfportname);
			/* from djwraps.psw */
			break;

		default:
			(void) fprintf(stderr,"djf: Unknown resolution of %d chosen.\n",res);
			break;
		}

		DPSWaitContext(pctx);
#ifdef DEBUG
		(void) fprintf(stderr, "making threads\n");
#endif
/*
 * 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);
#ifdef DEBUG
		(void) fprintf(stderr, "after making thread\n");
#endif
		DPSPrintf(pctx, "/-saveDJ-%d- save def\n/note {} def\n\
/envelope {} def\n%%%%BeginDocument: DJGraphic\n", getpid());
		mutex_unlock(mutex1);
		NX_DURING
#ifdef DEBUG
		(void) fprintf(stderr, "before printing postscript\n");
#endif
		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);
#ifdef DEBUG
		(void) fprintf(stderr, "before showpage\n");
#endif
		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-saveDJ-%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 (*djfportname &&
			(c = netname_check_out(name_server_port, djfportname,
					   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);
}

/*
 * Beginn der Deskjet 500 Codierung;
 * Hier erstmal alle statischen Variablen zur Compression
 * werden nur von PrintPage() und PrintScanLine() benutzt
 */

#define MODES		4			/* Mode 0-3 */

static	int		in_mode;		/* current mode */
static	unsigned char	*seed_row;
static	unsigned char	obuf1[1024], obuf2[1024];
static	unsigned char	*testrow = obuf1, *bestrow = obuf2;
static	int		testsize, bestsize;

/*
 *  Compression Routines taken from pclcomp by  Tony Parkhurst  
 *  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
 */

#ifdef	USE_1
/*
**  Output_1() does mode 1 compression (run length encoding)
*/

int
Output_1(src, dest, count)
unsigned char *src, *dest;
register int count;
{
	unsigned char *optr = dest, *iptr;
	int k,c;

	iptr = src;

	while ( count ){
		
		c = *iptr++;		/* get value to work with */
		count--;

		k = 0;

		while ( *iptr == c && k < 255 && count ){
			k++;
			iptr++;
			count--;
		}

		*optr++ = k;		/* output repeat count */
		*optr++ = c;		/* output value */
	}

	count = optr - dest;		/* for return value */

	return ( count );
}
#endif


/*
******************************************************************************
**
**       Output_2() does PCL compression mode 2 on the data. 
**       This mode is a combination of modes 0 and 1.
**
******************************************************************************
*/

int
Output_2(src, dest, count)
unsigned char *src, *dest;
register int count;
{
	unsigned char	*outptr, *inptr;
	unsigned char	*saveptr;

	unsigned char	data;			/* data byte */
	unsigned char	lastbyte;		/* last byte */
	int		repcount;		/* repeat count */
	int		litcount;		/* literal count */

	/*
	**  src points to the intput data.
	**  dest points to the output buffer.
	**  count is the number of intput bytes.
	*/

	inptr = src;
	outptr = dest;

	/*
	**  Start loop thru data.  Check for possible repeat at beginning.
	*/

	while ( count )
	{
		data = *inptr++;	/* get value to work with */
		count--;

		repcount = 0;		/* no repeat count yet */


		/* 
		**  Check for repeat, since we are not in the middle
		**  of a literal run, it does not have to be more than
		**  two bytes of similar data.
		*/

		while ( count && *inptr == data )
		{
			repcount++;
			inptr++;
			count--;
		}

		/*
		**  Now, if we are out of data (count == 0), then
		**  if the repeated byte was zero, then ignore it
		**  completely (don't bother outputing the trailing zeros).
		**
		**  To always strip zero's, simply remove the "zerostrip"
		**  from the test.
		*/

		if ( count == 0 && data == 0)
			break;			/* done */


		/*
		**  If there was a repeat (repcount > 0), then we
		**  can output the command here, otherwise, we
		**  need to go into literal run mode.
		**
		**  Note:  This is a while loop because the repeat count
		**  may actually be greater than 127.
		*/

		if ( repcount >= 1 )		/* repeat mode */
		{
			while (repcount > 127)
			{
				*outptr++ = 129;		/* count 127 */
				*outptr++ = data;		/* value */
				repcount-= 128;			/* offset */
			}

			if (repcount > 0)
			{
				*outptr++ = 256 - repcount;	/* count */
				*outptr++ = data;		/* value */

				/*
				**  Now pop to the top of the loop 
				**  looking for more repeat counts.
				*/

				continue;			/* top of loop */
			}

			/*
			**  Special case.  If we have arrived at this point,
			**  then repcount is now equal to 0.  This means
			**  that when we entered this section, repcount
			**  was a multiple of 128 (i.e. 128 :-).
			**
			**  This means that there were 129 identical bytes,
			**  so the output does a replicate of 127 which
			**  gives 128 bytes, and we now have one byte left
			**  over which should NOT be output as a repeat
			**  run, rather it should be merged into the following
			**  literal run (if it exists).
			**
			**  So, we will simply fall thru to the next section
			**  of code which assumes that we are working on 
			**  a literal run.
			*/

		}

		/*
		**  Literal run.  At this point, the current data byte
		**  does NOT match the following byte.  We will transfer
		**  these non-identical bytes until:
		**
		**       1)  we run out of input data (count == 0).
		**       2)  we run out of room in this output block (128)
		**       3)  we come across a value which occurs at least
		**           three times in a row.  A value occuring only
		**           twice in a row does NOT justify dropping
		**           out of a literal run.
		**
		**  Special case:  If we run out of room in the output block
		**  (which is 128 bytes), the last two values are the same,
		**  AND there is more input, it makes sense to restart
		**  the repeat detector in case the following bytes are
		**  repeats of the two.  A simple check of the following
		**  byte will determine this.
		**  (This case falls out with the test for triples below).
		**
		**  Special case:  If we run out of room in the output block
		**  (which is 128 bytes), the last value is the same as
		**  the next one on the input, then it is better to let
		**  that byte be used in a possible replicate run following
		**  the literal run.  If the last byte matches ONLY the
		**  following byte, (and not the one after that, it is
		**  a wash, but for best results, we will test the
		**  following two bytes.
		**
		*/

		litcount = 0;
		saveptr = outptr++;	/* save location of the command byte */

		*outptr++ = data;	/* save the first byte. */

		lastbyte = data;	/* remember for testing */

		while ( count && litcount < 127 )
		{
			data = *inptr++;
			count--;
			litcount++;
			*outptr++ = data;

			/*
			**  Test to see if this byte matched the last one.
			**  If so, check the next one for a triple.
			*/

			if ( lastbyte == data && count && *inptr == data )
			{
				/*
				**  We have a triple, adjust accordingly.
				**
				**  Add two bytes back onto the input.
				*/

				count += 2;
				inptr -= 2;
				outptr -= 2;
				litcount -= 2;

				break;		/* out of loop */
			}

			lastbyte = data;	/* save data byte */
		}

		/*
		**  Check the special case number 2 above.
		*/

		if ( litcount == 127  &&  count > 1  &&  data == *inptr
		    &&  data == inptr[1] )
		{
			/*  Restore the last byte to the input stream */

			count += 1;
			inptr -= 1;
			outptr -= 1;
			litcount -= 1;
		}


		/*
		**  Save the literal run count.
		*/

		*saveptr = litcount;

		/*
		**  Now go back to the top and look for repeats.
		*/
	}

	count = outptr - dest;		/* for return value */

	return ( count );
}



#ifdef	USE_3
/*
**  Output_3() does mode 3 compression (delta row encoding).
*/

int
Output_3( new, dest, count)
unsigned char *new, *dest;
int count;
{
	unsigned char *sptr=seed_row, *nptr=new, *dptr=dest;
	int i,j;
	unsigned char	command;

					/* can't calc difference if no seed */
	if (seed_row == NULL)
		return INT_MAX;

	while ( count > 0 ){
		i = 0;

					/* find first diff */
		while ( *sptr == *nptr && i < count ){
			i++;
			sptr++;
			nptr++;
		}

		if ( i >= count )	/* too far to find diff */
			return(dptr - dest);	/* bail */

		count -= i;
		
		/* now count how many bytes to change */

		for ( j = 1; j < 8; j++)	/* j == 0 is already known */
			if ( j > count || sptr[j] == nptr[j] )
				break;
		
		j--;	/* adjust */

		command = (j << 5);
		command += MIN( i, 31 );
		*dptr++ = command;

		if ( i == 31 )
			*dptr++ = 0;
		
		i -= MIN (i, 31);

		while( i ){
			*dptr++ = MIN ( i, 255 );

			if ( i == 255 )
				*dptr++ = 0;
			
			i -= MIN ( i, 255 );
		}

		while (j-- >= 0){
			*dptr++ = *nptr++;
			sptr++;
			count--;
		}
	}

	return ( dptr - dest );
}
#endif


/*
 * print a scanline and select the modes
 */ 

void PrintScanLine(char *buf, int anz)
{
	int		minmode;
	unsigned char	*temprow;
	
	printf("\033*b"); /* prepare */
	
	/*	Anything is better than this	*/
	bestsize = INT_MAX;

#ifdef	USE_3
	testsize = Output_3( buf, testrow, anz);
	if (testsize < bestsize) {
		temprow = testrow;
		testrow = bestrow;

		bestrow = temprow;
		bestsize = testsize;
		minmode = 3;
	}
#endif

	/*	mode 2 is always tested					*/
	testsize = Output_2( buf, testrow, anz);
	if (testsize < bestsize) {
		temprow = testrow;
		testrow = bestrow;

		bestrow = temprow;
		bestsize = testsize;
		minmode = 2;
	}

#ifdef	USE_1
/*	Don't check mode 1 (it is almost never better than mode 2) and	*/
/*	mode 0 (mode 2 is about 1% longer in worst case)		*/

	testsize = Output_1( buf, testrow, anz);
	if (testsize < bestsize) {
		temprow = testrow;
		testrow = bestrow;

		bestrow = temprow;
		bestsize = testsize;
		minmode = 1;
	}
#endif

/* Mode 0 is cheap and always there */
	testsize = anz;
	if (testsize < bestsize) {
		bestrow = buf;
		bestsize = testsize;
		minmode = 0;
	}
	if ( in_mode != minmode ) {
		printf("%1dm", minmode);
		in_mode = minmode;
	}
	
	/* <esc>*b has already been output */

	printf("%1dW", bestsize);

	if ( fwrite( bestrow, 1, bestsize, stdout) < bestsize ) {
		/* check for error and exit */

		if ( ferror(stdout) )
		{
			perror("Output error");
			exit(-2);
		}
	}
}

/*
 * small improvement for speed:
 * if we know that we are printing (one of the pins 0-49 is active) it is
 * faster to print a null line, than skipping it via *b%dY. This way the
 * printing head doesn't need to print, move vertically and restart printing.
 */
#define	PINS	50

void PrintPage(void)
{
	/* output the whole Page to the printer */
	register char *sp, *p;
	register int i;
	int in_seq = -1, empty_lines = 0;

	sendsequence(page_init);	/* start plot at current position */

	in_mode = -1;			/* invalidate some vars */
	seed_row = NULL;

	for (i = ppm.pixelsHigh, sp = (char *) ppm.printerData;
	     i > 0; i--, sp += ppm.bytesPerRow) {

		/*	Im Druck einer 50er Zeile?	*/
		if (in_seq >= 0) {
			mutex_lock(mutex1);
			PrintScanLine(sp, ppm.bytesPerRow);
			mutex_unlock(mutex1);
			/*	Ende der ganzen Zeile?	*/
			if (++in_seq == PINS)
				in_seq = -1;
		}
		else {

			for (p = sp + ppm.bytesPerRow - 1; p >= sp && *p == 0; p--)
				;
					
			/*	Leere Zeile?	*/
			if (p < sp)
				empty_lines++;
			else {
				/*	Volle Zeile nach leeren Zeilen	*/
				if (empty_lines > 0) {
					(void) fprintf(stdout, "\033*b%dY", empty_lines);
				}
				mutex_lock(mutex1);
				PrintScanLine(sp, ppm.bytesPerRow);
				mutex_unlock(mutex1);
				empty_lines = 0;
				/*	Erster Pin	*/
				in_seq = 0;
			}
		}
		seed_row = sp;
	}
	
	sendsequence(page_exit);	/* Eject page	*/
}

/* 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 msg_return_t m;

	sendsequence(job_init);

	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 (*djfportname) {	/* recall send rights */
			register kern_return_t s;

			if ((s = netname_check_out(name_server_port,
				 djfportname, PORT_NULL)) != KERN_SUCCESS) {
				mutex_lock(mutex1);
				mach_error("netname_check_out failed", s);
				mutex_unlock(mutex1);
			}

			*djfportname = '\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! */

		PrintPage();
		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;
		}
	}

	sendsequence(job_exit);		/* Perforationssprung ein */

	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; djf must be set-uid root if
 * we're to be standalone (not running under control of lpd).
 * 
 */

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

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? */
}

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