*BSD News Article 10692


Return to BSD News archive

Received: by minnie.vk1xwt.ampr.org with NNTP
	id AA489 ; Wed, 03 Feb 93 19:01:16 EST
Path: sserve!manuel.anu.edu.au!munnari.oz.au!sgiblab!spool.mu.edu!olivea!decwrl!csus.edu!netcom.com!alm
From: alm@netcom.com (Andrew Moore)
Newsgroups: comp.unix.bsd
Subject: [386BSD] Unofficial interruptless printer driver kit
Keywords: interruptless lpt printer driver
Message-ID: <1993Feb2.221148.102@netcom.com>
Date: 2 Feb 93 22:11:48 GMT
Organization: Netcom - Online Communication Services  (408 241-9760 guest)
Lines: 671

If I get no complaints about the following collection of lpt patches and
notes, then I will try to post it to agate.berkeley.edu and to
ref.tfs.com as interruptless-lpt-kit.shar.
-Andrew Moore <alm@netcom.com>

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 02/02/1993 21:19 UTC by alm@netcom2
# Source directory /u13/alm/tmp
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   2509 -rw-r--r-- LPT-INSTALL-NOTES
#  10070 -rw-r--r-- interruptless-lpt.c
#   1593 -rw-r--r-- sample.conf.c.patch
#   2324 -rw-r--r-- sample.lpt.filters
#
# ============= LPT-INSTALL-NOTES ==============
if test -f 'LPT-INSTALL-NOTES' -a X"$1" != X"-c"; then
	echo 'x - skipping LPT-INSTALL-NOTES (File already exists)'
else
echo 'x - extracting LPT-INSTALL-NOTES (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'LPT-INSTALL-NOTES' &&
UNOFFICIAL INTERRUPTLESS LPT DRIVER KIT
X
CONTENTS
X
-Disclaimer [please read]
-Steps for installing the driver
-Sample conf.c patch [from Eric Haug's patches to the original lpt driver]
-The interruptless lpt driver [by Wolfgang Stanglemeier with contributions 
X from Andy Valencia]
-Sample filters and printcap entries for laser printers [by Reinier Kleipool]
X
DISCLAIMER
X
Following a suggestion by Chris Flatters,  some troublesome code in the
lptprobe() routine of the lpt driver is commented out.   This allows
the printer to be recognized on boot if it  is not powered on.  If your
printer seems to misbehave, try removing the comments...
X
It is not certain whether the interruptless lpt driver will supercede
Bill Jolitz's original driver (and Eric Haug's patches to it) in future
releases of 386BSD.  In the mean time, the interruptless driver seems
to be necessary  for printing binary data.  As for performance, it is
claimed that the interruptless driver is faster than the original,
despite conventional wisdom which says that interrupt driven devices
are faster.  In addition, the interruptless driver seems to create less
of a system load.
X
All the usual warnings and disclaimers.
X
STEPS FOR INSTALLING THE INTERRUPTLESS LPT DRIVER
X
1) In the file /sys/i386/conf/YOUR_SYSTEM_NAME, add the line:
X
device		lpt1	at isa? port "IO_LPT1" tty irq 7 vector lptintr
X
2) In the file /sys/i386/conf/files.i386, add the line:
X
i386/isa/lpt.c		optional lpt device-driver
X
3) Modify the file /sys/i386/i386/conf.c following the sample patch
X   (see the file sample.conf.c.patch).  The position of entries in the
X   cdevsw[] structure determine the device major number of the /dev
X   device file.  The sample patch installs the lpt entries for device file
X   /dev/lpt major number 15.
X
4) Create character lpt device files with the commands:
X
X    $ mknod /dev/lpt0 c 15 48
X    $ mknod /dev/lpt1 c 15 49
X    $ mknod /dev/lpt2 c 15 50
X    $ ln /dev/lpt1 /dev/lp
X
X   where the major number (15) is determined by step (3) and minor numbers
X   (48, 49, 50) as per the lpt.c instructions (see the file 
X   interruptless-lpt.c).
X
5) In the file /etc/hosts.lpd file, for each machine with printer
X   access, add a line of the form:
X
machine_name.domain
X
6) Replace /sys/i386/isa/lpt.c with the interruptless printer driver
X   (see the file interruptless-lpt.c).
X
7) Recompile the kernel. 
X
8) If you have a laser printer, see the notes in the file
X   sample.lpt.filters about adding filters and configuring /etc/printcap.
-Andrew Moore <alm@netcom.com>
SHAR_EOF
chmod 0644 LPT-INSTALL-NOTES ||
echo 'restore of LPT-INSTALL-NOTES failed'
Wc_c="`wc -c < 'LPT-INSTALL-NOTES'`"
test 2509 -eq "$Wc_c" ||
	echo 'LPT-INSTALL-NOTES: original size 2509, current size' "$Wc_c"
fi
# ============= interruptless-lpt.c ==============
if test -f 'interruptless-lpt.c' -a X"$1" != X"-c"; then
	echo 'x - skipping interruptless-lpt.c (File already exists)'
else
echo 'x - extracting interruptless-lpt.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'interruptless-lpt.c' &&
The following is a complete lpt.c replacement.
X
The driver supports up to 4 printers.
The minor number is interpreted as follows:
X
X	D D P E - - # #
X
DD:	Debug,	00: no
X		01: statistical data when closing device
X		10: statistical data for every write call
X		11: lots of silly data
X
P:	Prime on open.
X
E:	Errormessage.
X		Logs "no paper", "offline", "printer error" conditions
X		to the console.
X
##:	Device,	0: lpt0
X		1: lpt1
X		2: lpt2		(on display adapter)
X
This driver uses the ideas of Piercarlo Grandis polling printer driver.
X
---- Cut Here ---- begin of /sys/i386/isa/lpt.c replacement ----
/*
X * Copyright (c) 1990 William F. Jolitz, TeleMuse
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *   This software is a component of "386BSD" developed by 
X *   William F. Jolitz, TeleMuse.
X * 4. Neither the name of the developer nor the name "386BSD"
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ 
X * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS 
X * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT. 
X * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT 
X * NOT MAKE USE OF THIS WORK.
X *
X * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
X * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN 
X * REFERENCES SUCH AS THE  "PORTING UNIX TO THE 386" SERIES 
X * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING 
X * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND 
X * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE 
X * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS 
X * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
X *
X * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X */
X
/*
X * Device Driver for AT parallel printer port
X * Written by William Jolitz 12/18/90
X * Modified to run without interrupts
X * 92-08-19  Wolfgang Stanglmeier  <wolf@dentaro.GUN.de>
X * Slight cleanup and reorganization, try to handle restarted syscalls
X * 92-09-08  Andy Valencia <jtk@netcom.com>
X */
X
#include "lpt.h"
#if NLPT > 0
X
#include "param.h"
#include "buf.h"
#include "systm.h"
#include "ioctl.h"
#include "tty.h"
#include "proc.h"
#include "user.h"
#include "uio.h"
#include "kernel.h"
#include "malloc.h"
X
#include "i386/isa/isa_device.h"
#include "i386/isa/lptreg.h"
X
/* internal used flags */
#define   OPEN        (0x01)   /* device is open */
#define   INIT        (0x02)   /* device in open procedure */
X
/* flags from minor device */
#define   LPT_PRIME   (0x20)   /* prime printer on open   */
#define   LPT_ERROR   (0x10)   /* log error conditions    */
X
#define   LPT_FLAG(x) ((x) & 0xfc)
#define   LPT_UNIT(x) ((x) & 0x03)
X
/* Printer Ready condition */
#define   LPS_INVERT  (LPS_NBSY | LPS_NACK |           LPS_SEL | LPS_NERR)
#define   LPS_MASK    (LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR)
#define   NOT_READY()   ((inb(sc->sc_stat)^LPS_INVERT)&LPS_MASK)
X
/* tsleep priority */
#define   LPPRI       ((PZERO+8) | PCATCH)
X
int lptprobe(), lptattach();
struct   isa_driver lptdriver = {lptprobe, lptattach, "lpt"};
X
/*
X *   copy usermode data into sysmode buffer
X */
#define   BUFSIZE      1024
X
/*
**   Waittimes
*/
#define   TIMEOUT   (hz*16)   /* Timeout while open device */
#define   LONG      (hz* 1)   /* Timesteps while open      */
X
#define   MAX_SPIN  255       /* max loop counter for busy wait */
X
/*   Valid Controlbits for probe ...
**
**   The lower 5 bits of controlport should be
**   readable and writable,
** 
**   .... but if my deskjet is power down, it clobbers
**   some lines, and the port will not be configured.
**   So I mask them out
*/
#define    LPC_MASK    (0xfa)
X
struct lpt_softc {
X	char	*sc_cp;		/* current data to print	*/
X	int	sc_count;	/* bytes queued in sc_inbuf	*/
X	short	sc_data;	/* printer data port		*/
X	short	sc_stat;	/* printer control port		*/
X	short	sc_ctrl;	/* printer status port		*/
X	u_char	sc_flags;	/* flags (open and internal)	*/
X	u_char	sc_unit;	/* unit-number			*/
X	u_char	sc_smax;	/* current max busy loop cnt	*/
X	char			/* buffer for data		*/
X	 *sc_inbuf;
} lpt_sc[NLPT];
X
/* In fact, I need no interrupt, but how can I explain it to config ??? */
lptintr(unit)
X	int unit;
{
X	/* dummy */ ;
}
X
/*
X * lptprobe()
X *	Probe for hardware
X */
lptprobe(idp)
X	struct isa_device *idp;
{   
X	unsigned v, w, n = 0;
X
X	/* status */
X	do {
X		if (++n >= 4)
X			return (0);
X
X		/*
X		 * Status port should be read only,
X		 * so readback value may not change
X		 */
X		outb(idp->id_iobase+lpt_status,0xf0);
X		v = inb(idp->id_iobase+lpt_status);
X		outb(idp->id_iobase+lpt_status,0);
X		w = inb(idp->id_iobase+lpt_status);
X	} while (v != w);
X
X	/* control: the lower 5 bits of controlport should read back */
/*
X	outb(idp->id_iobase+lpt_control,0xff);
X	DELAY(100);
X
X	w = inb(idp->id_iobase+lpt_control);
X	if ((w ^ 0xff) & LPC_MASK) return(0);
X
X	outb(idp->id_iobase+lpt_control,0);
X	DELAY(100);
X	w = inb(idp->id_iobase+lpt_control);
X	if ((w ^ 0xe0) & LPC_MASK)
X		return(0);
*/
X	return(1);
}
X
/*
X * lptattach()
X *	Install device
X */
lptattach(isdp)
X	struct isa_device *isdp;
{
X	struct   lpt_softc   *sc;
X
X	sc = lpt_sc + isdp->id_unit;
X	sc->sc_unit = isdp->id_unit;
X	sc->sc_data = isdp->id_iobase + lpt_data;
X	sc->sc_stat = isdp->id_iobase + lpt_status;
X	sc->sc_ctrl = isdp->id_iobase + lpt_control;
X	outb(sc->sc_ctrl, LPC_NINIT);
X	return (1);
}
X
/*
X * lptopen()
X *	New open on device.
X *
X * We forbid all but first open
X */
lptopen(dev, flag)
X	dev_t dev;
X	int flag;
{
X	struct lpt_softc *sc;
X	int delay;	/* slept time in 1/hz seconds of tsleep */
X	int err;
X	u_char sta, unit;
X
X	unit= LPT_UNIT(minor(dev));
X	sta = LPT_FLAG(minor(dev));
X
X	/* minor number out of limits ? */
X	if (unit >= NLPT)
X		return (ENXIO);
X	sc = lpt_sc + unit;
X
X	/* Attached ? */
X	if (!sc->sc_ctrl) { /* not attached */
X		return(ENXIO);
X	}
X
X	/* Printer busy ? */
X	if (sc->sc_flags) { /* too late .. */
X		return(EBUSY);
X	}
X
X	/* Have memory for buffer? */
X	sc->sc_inbuf = malloc(BUFSIZE, M_DEVBUF, M_WAITOK);
X	if (sc->sc_inbuf == 0)
X		return(ENOMEM);
X
X	/* Init printer */
X	sc->sc_flags = sta | INIT;
X	if (sc->sc_flags & LPT_PRIME) {
X		outb(sc->sc_ctrl, 0);
X	}
X
X	/* Select printer */
X	outb(sc->sc_ctrl, LPC_SEL|LPC_NINIT);
X
X	/* and wait for ready .. */
X	for (delay=0; NOT_READY(); delay+= LONG) {
X		if (delay >= TIMEOUT) { /* too long waited .. */
X			sc->sc_flags = 0;
X			return (EBUSY);
X		}
X
X		/* sleep a moment */
X		if ((err = tsleep (sc, LPPRI, "lpt: open", LONG)) !=
X				EWOULDBLOCK) {
X			sc->sc_flags = 0;
X			return (EBUSY);
X		}
X	}
X
X	/* Printer ready .. set variables */
X	sc->sc_flags |= OPEN;
X	sc->sc_count = 0;
X
X	return(0);
}
X
/*
X * pushbytes()
X *	Workhorse for actually spinning and writing bytes to printer
X */
static
pushbytes(sc)
X	struct lpt_softc *sc;
{
X	int spin, err, tic;
X	char ch;
X
X	/* loop for every character .. */
X	while (sc->sc_count > 0) {
X		/* printer data */
X		ch = *(sc->sc_cp);
X		sc->sc_cp += 1;
X		sc->sc_count -= 1;
X		outb(sc->sc_data, ch);
X
X		/* Busy wait for printer ready .. */
X		spin = tic = 0;
X		while (NOT_READY()) {
X			if (++spin >= sc->sc_smax) {
X				/*
X				 * Now sleep, every cycle a
X				 * little longer ..
X				 */
X				tic = tic + tic + 1;
X				err = tsleep(sc, LPPRI, "lpt: write", tic);
X				if (err != EWOULDBLOCK) {
X					return (err);
X				}
X			}
X		}
X
X		/* strobe */
X		outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL|LPC_STB);
X		outb(sc->sc_ctrl, LPC_NINIT|LPC_SEL);
X
X		/* Adapt busy-wait length... */
X		if (spin >= sc->sc_smax) { /* was sleep wait */
X			if (sc->sc_smax<MAX_SPIN)
X				sc->sc_smax++;
X		}
X		if (spin*2 < sc->sc_smax) {
X			sc->sc_smax--;
X		}
X	}
X	return(0);
}
X
/*
X * lptclose()
X *	Close on lp.  Try to flush data in buffer out.
X */
lptclose(dev, flag)
X	dev_t dev;
X	int flag;
{
X	struct lpt_softc *sc = lpt_sc + LPT_UNIT(minor(dev));
X
X	/* If there's queued data, try to flush it */
X	(void)pushbytes(sc);
X
X	/* really close .. quite simple :-)  */
X	outb(sc->sc_ctrl, LPC_NINIT);
X	sc->sc_flags = 0;
X	free(sc->sc_inbuf, M_DEVBUF);
X	sc->sc_inbuf = 0;	/* Sanity */
X	return(0);
}
X
/*
X * lptwrite()
X *	Copy from user's buffer, then print
X */
lptwrite(dev, uio)
X	dev_t dev;
X	struct uio *uio;
{
X	struct lpt_softc *sc = lpt_sc + LPT_UNIT(minor(dev));
X	int err;
X
X	/* Write out old bytes from interrupted syscall */
X	if (sc->sc_count > 0) {
X		err = pushbytes(sc);
X		if (err)
X			return(err);
X	}
X
X	/* main loop */
X	while ((sc->sc_count = MIN(BUFSIZE, uio->uio_resid)) > 0) {
X		/*  get from user-space  */
X		sc->sc_cp = sc->sc_inbuf;
X		uiomove(sc->sc_inbuf, sc->sc_count, uio);
X		err = pushbytes(sc);
X		if (err)
X			return(err);
X	}
X	return(0);
}
#endif /* NLP > 0 */
---- Cut Here ---- end of /sys/i386/isa/lpt.c replacement ----
X
SHAR_EOF
chmod 0644 interruptless-lpt.c ||
echo 'restore of interruptless-lpt.c failed'
Wc_c="`wc -c < 'interruptless-lpt.c'`"
test 10070 -eq "$Wc_c" ||
	echo 'interruptless-lpt.c: original size 10070, current size' "$Wc_c"
fi
# ============= sample.conf.c.patch ==============
if test -f 'sample.conf.c.patch' -a X"$1" != X"-c"; then
	echo 'x - skipping sample.conf.c.patch (File already exists)'
else
echo 'x - extracting sample.conf.c.patch (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'sample.conf.c.patch' &&
The following patch illustrates how to set up conf.c for lpt device
major 15.
X
The definition of IO_LPT1 is in /sys/i386/isa/isa.h
you may replace "IO_LPT1" with 0x378 or wherever your hardware is at.
X
The lpt port hardware is expected to be at 0x378 and jumpered to
interrupt 7.  NOTE: a monochrome adapter will use address 0x3BC
and maybe interrupt 7. The maybe indicates that i know certain
board do not handle interrupts properly (DOS does not use them).
(the DOS idea of LPT1 is address 3BC).
X
This patch installs lpt as character major device 15
The conf.c file is in /sys/i386/i386/conf.c
X
---- Cut Here ---- begin of conf.c patch ----
*** conf.c.distr	Sat May 30 18:48:08 1992
--- conf.c	Sat Jul 18 19:01:52 1992
***************
*** 165,170 ****
--- 165,179 ----
X  #define	com_tty		NULL
X  #endif
X  
+ #include "lpt.h"
+ #if NLPT > 0
+ int	lptopen(),lptclose(),lptwrite(),lptioctl();
+ #else
+ #define lptopen		enxio
+ #define lptclose	enxio
+ #define lptwrite	enxio
+ #endif
+ 
X  int	logopen(),logclose(),logread(),logioctl(),logselect();
X  
X  int	ttselect(), seltrue();
***************
*** 214,219 ****
--- 223,234 ----
X  	{ asopen,	asclose,	rawread,	rawwrite,	/*D*/
X  	  asioctl,	enodev,		nullop,		NULL,
X  	  seltrue,	enodev,		asstrategy },
+ 	{ enodev,	enodev,		enodev,		enodev,		/*E*/
+ 	  enodev,	enodev,		nullop,		NULL,
+ 	  seltrue,	enodev,		enodev },
+ 	{ lptopen,	lptclose,	nullop,		lptwrite,	/*F*/
+ 	  enodev,	nullop,		nullop,		NULL,
+ 	  seltrue,	enodev,		enodev },
X  };
X  int	nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);
X  
---- Cut Here ---- end of conf.c patch ----
X
SHAR_EOF
chmod 0644 sample.conf.c.patch ||
echo 'restore of sample.conf.c.patch failed'
Wc_c="`wc -c < 'sample.conf.c.patch'`"
test 1593 -eq "$Wc_c" ||
	echo 'sample.conf.c.patch: original size 1593, current size' "$Wc_c"
fi
# ============= sample.lpt.filters ==============
if test -f 'sample.lpt.filters' -a X"$1" != X"-c"; then
	echo 'x - skipping sample.lpt.filters (File already exists)'
else
echo 'x - extracting sample.lpt.filters (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'sample.lpt.filters' &&
Following illustrates how to setup /etc/printcap for laser printers.
X
>I successfully installed the lp patches, and the port is seen.
>But when I print, the port or printer seem to just hang. The form feed
>light on the laser comes on, but that is all. Nothing else happens.
>lpc reports the status as printing, but thats it. If I cat directly to 
>/dev/lp ( set up as c 14 0) the same happens.
>
>Can anyone please help me ?
X
X
----------
X
Yes!
X
X   Probably the CR definition of the laserjet isn't setup correctly.
I use an input filter script to set all the correct parameters on the ljet.
X
here is my /etc/printcap file:
------------------------------ /etc/printcap ----------------------------
X
#	@(#)printcap	5.3 (Berkeley) 6/30/90
X
lp|local line printer:\
X	:lp=/dev/lpt1:sd=/var/spool/lpd:lf=/var/log/lpd-errs:sh:\
X	:if=/usr/local/filters/if_ljet3:\
X	:vf=/usr/local/filters/vf_lett3:\
X	:df=/usr/local/filters/df_ljet3:
------------------------------------------------------------------------
And this is my homebrew if= input filter script:
( be shure you have the execve magic number patch installed, otherwise
X  #!/bin/sh will not work!)
-------------------------- /usr/local/filters/if_ljet3 -----------------
X
#!/bin/sh
printf "\033E\033&k2G"
cat -
printf "\033E"
--------------------------------------------------------------------------
And here an filter that attempts to print two pages side by side. (use lpr -d)
------------------------- /usr/local/filters/df_ljet3 --------------------
X
#!/bin/sh
# Page layout variables
LINES=76
TOPMARGIN=5
LEFTMARGIN=0
VMI=5
GUTTER=7
# internal variables: DO NOT CHANGE
width=`expr 162 + 2 \* $GUTTER`
# Reset the printer
printf "\033E"
# set line termination to cr->cr; lf->cr+lf; ff->cr+ff
printf "\033&k2G"
# select landscape orientation
printf "\033&l1O"
# setup the page: vmi, enable perf skip
printf "\033&l${VMI}c1L"
# Top margin TOPMARGIN lines
printf "\033&l${TOPMARGIN}E"
# Text Length $LINES lines
printf "\033&l${LINES}F"
# left margin of LEFTMARGIN chars
printf "\033&a${LEFTMARGIN}L"
# select 16.6 pitch font
printf "\033(s16.6H"
cat - | sed "//d" | pr -2 -n:${GUTTER} -w${width} -e -f -l${LINES}  -v -
printf "\033E"
---------------------------------------------------------------------------
X
Hope this helps,
X
Reinier Kleipool.
reink@hpuamsa.neth.hp.com
SHAR_EOF
chmod 0644 sample.lpt.filters ||
echo 'restore of sample.lpt.filters failed'
Wc_c="`wc -c < 'sample.lpt.filters'`"
test 2324 -eq "$Wc_c" ||
	echo 'sample.lpt.filters: original size 2324, current size' "$Wc_c"
fi
exit 0