Next Previous Contents

## 8. Using the Printcap Database

As described in the Print Spooling Overview, the heart of the LPRng system is information in the `/etc/printcap` file. The printcap information specifies:

1. The print queues available to users.
2. How client programs communicate with the `lpc` print server.
3. The configuration, location, and other information for each print queue on the print server.
4. How the `lpd` server processes jobs in each print queue.

In order to explain a complex subject, we will start with a set of simple printer configurations, and explain the purpose and effect of each entry in the printcap.

For details about individual printcap options, see the `printcap(5)` man page from the LPRng distribution, or use the Index To All The Configuration and Printcap Options to find a specific printcap option and its effects.

## 8.1 Simple Client Printcap Entry

Options used:

- `rm=`remote host (machine)
- `rp=`remote printer
- `lp=`destination printer information
- `client` client only printcap FLAG

I'll use this simple example to explain the basics of the LPRng printcap format and introduce some of the LPRng network configuration options. Here is a simple printcap file used to provide client programs (`lpr, lprm,` etc) with remote printer and server information.

> `

>     # printer lp1
>     lp1|printer1
>       :rm=localhost
>     # printer lp2 with continuation
>     lp2:\
>       :lp=pr@10.0.0.1:client
>     # printcap lp3, to printer pr, with overrides
>     lp3:rp=pr:rm=hpprinter.astart.com
>       :force_localhost@
>     # Simplest possible printcap entry - defaults for everything
>     lp4

> `

1. Lines starting with a `#` sign are comments, and all leading and trailing whitespace, i.e. - spaces, tabs, etc, are ignored. Empty lines are ignored as well.
2. A printcap entry starts with the printcap entry name, followed by one or more aliases, followed by one or more options. In the above example we have three printcap entries: `lp1` with an alias `printer1` and `lp2`, `lp3`, and `lp4` with no aliases.
3. Aliases start with the `|` character and options with the `:` character; tabs and spaces before and after the `|` or `:` characters and at the start and end of lines are ignored. You can use backslash (`\`) at the end of a line to create a multi-line value for an option. The backslash will cause the next line to be appended to the current line; watch out for comments and ends of printcap entries if you use this facility. As you can see from the example, there is no `Name` printcap entry - this is part of the `cm` option on the previous line.
4. Options take the form of a keyword/value pair, i.e.-

       :option=value
       :option#value   (legacy, not advised for new systems)
       :option
       :option@

5. Option names are case insensitive, but option values are not. While `Ts` and `ts` are the same option name, `ts=Testing` and `ts=testing` have their case preserved. A string or integer value is specified by `option=value` or `option#value`.
6. The use of the legacy `option#value` form is NOT recommended as some preprocessors and database systems will treat # as the start of a comment and delete the remainder of the line. This has caused great consternation for sysadmins who wonder why their NIS distributed printcap entries have been mysteriously truncated.
7. If you want to set a string option to empty value, use `option=`. The `option` will set it to `1`. If an option value contains a colon, then use the C (or Perl or Tck/Tk) string escape `\072` to represent the value.
8. Boolean options are set TRUE (1) if no value follows the keyword and FALSE (0) by appending a `@`. For example `sh` will set `sh` to TRUE and `sh@` to FALSE.

There may be multiple options on the same line, separated by colons. However, this does make the file less readable. The next tip was supplied by James H. Young <`jhy@gsu.edu`>:

> My personal preference for readability is to always put each option on its own line. Putting each option on its own line is worth the trouble even though it detracts from the usability of certain grepping techniques when trying to maintain these types of files.

Now let's examine the first printcap entry in detail. It is reproduced here for convenience:

> `

>     # printer lp1
>     lp1|printer1
>       :rm=localhost

> `

1. We start with a comment, followed by the printcap entry name and and alias. Aliases are useful when you want to refer to a single printer or print queue by different names. This can be useful in advanced printcap and print queue setups. By default, the remote printer name is the printcap entry name.
2. The `rm` (remote machine or host) option specifies the name or IP address of the `lpd` host running `lpd`. In this example the remote host is `localhost` or the machine that the client is running on and we assume that the `lpd` server is running on the localhost. Thus, we would communicate with printer `lp1@localhost`.

Let's look at the next printcap entry:

> `

>     # printer lp2 with continuation
>     lp2:\
>       :lp=pr@10.0.0.1:client

> `

1. The `lp2` printcap entry illustrates the use (and abuse) of the `\` continuation. If you think about this, we have really defined a printcap entry of the form:

   > `

   >     lp2: :lp=pr@10.0.0.1:client

   > `

   Luckily, LPRng ignores empty options like `: :`. While it is strongly recommended that `\` be avoided it may be necessary for compatibility with other system utilities.

2. The `lp=pr@10.0.0.1` option is an alternate way to specify a remote queue and server. If the `force_localhost` default is being used, then the LPRng clients will ignore the `10.0.0.1` address and still connect to `pr@localhost`. There is further discussion about this in the next section.
3. The `client` option explicitly labels client only printcap information. The `lpd` server will ignore any printcap with the `client` option. When constructing complex printcaps, this option is used to keep ensure that you have consistent printcap information.

The following printcap entry shows how to override the `force_localhost` default, and force the LPRng clients to connect directly to a remote server:

> `

>     lp3:rp=pr:rm=hpprinter.astart.com
>       :force_localhost@

> `

1. The `rp=` (remote printer) remote print queue name to used when sending commands to the `lpd` print server.
2. The `force_localhost@` option is an example of a flag option. The `@` sets the option value to 0 (false). We set `force_localhost` to false, which now allows the LPRng clients to connect directly to the specified remote printer. In this example, the `hpprinter.astart.com` could be a HP LaserJet Printer with a JetDirect interface, which supports the RFC1179 protocol.
3. One disadvantages of sending a job directly to a printer using the above method is that `lpr` program will not terminate or exit until all of the files have been transferred to the printer, and this may take a long time as the printer processes the files as they are received.

Now let's look at the last printcap entry:

> `

>     # Simplest possible printcap entry - defaults for everything
>     lp4

> `

The last example is the simplest possible printcap entry. This will cause LPRng clients to use the default values for everything. The printer will be `lp4`, i.e. - the name of the printcap, and the server will be `localhost` if `force_localhost` is set, or the value of the `default_remote_host` configuration option if it is not.

## 8.2 Simple Server Printcap Example

Options used:

- `cm=`comment for status
- `if=`default job file filter
- `lf=`log file
- `mx=`maximum job size
- `lp=`output device
- `sd=`spool directory file
- `sh` suppress headers (banners)
- `sf` suppress form feeds between files

The previous section discussed printcap entries for use by the client programs. Now we will discuss printcap entries for use by the `lpd` server. In simple configurations or when we have the `force_localhost` option enabled we can use the same printcap for both LPRng clients and the `lpd` server.

> `

>     # Local ASCII printer
>     lp1|printer
>       :server
>       :cm=Dumb printer
>       :lp=/dev/lp1
>       :sd=/var/spool/lpd/lp1
>       :lf=log:af=acct
>       :if=/usr/local/sbin/lpf
>       :mx=0:sh:sf

> `

1. The printcap entry name is `lp1`. This information will be displayed when requesting status information using the `lpq` program.
2. The `printer` alias. This allows a single spool queue to have multiple names.
3. The `server` corresponds to the `client` flag and indicates this printcap entry is for the `lpd` server only.
4. The `cm` field supplies a information field for `lpq` (printer status) output.
5. The `lp` value specifies the destination file, device or remote spool queue to which data is sent. In this example it is the device `/dev/lp1`. By default, IO devices are opened for write-only operation.
6. The `sd` specifies the spool directory where print job files are stored until they are printed.
7. The `lf` and `af` options specify the names for the log and accounting files, respectively. These have the default values `log` and `acct` respectively, and if not absolute pathnames are relative to the spool queue directory. A log file is highly recommended. If these files don't exist, they will not be created, and no logging or accounting will be done. You will need to create them manually (e.g., by using `touch`) or by using the `checkpc` program.
8. The `if` option specifies a filter program to be used for jobs with the `f` or default job format format. Filters and print formats are discussed in section Filters. The `lpf` filter translates `LF` (line feed) to `CR/LF` (carriage return/line feed) sequences, eliminating staircase output.
9. `mx` indicates the maximum file size for a print job. Specifying 0 means that there is no limit.
10. The `sh` suppress headers flag will suppress printing banner pages.
11. The `sf` suppress form feeds flag will suppress form feeds between the files of a multi-file print job.

## 8.3 The Printcap Parsing Rules

Options used:

- `client` printcap entry valid only for client programs
- `oh` printcap entry valid only on these hosts
- `server` printcap entry valid only for lpd server
- `tc` add named printcap entry contents

In this section, we will discuss the remaining tricky parts of the LPRng printcap database: combined client and server printcaps, host specific printcap entries, and the `tc` include facility.

The following is a complete description of how a printcap file is processed:

1. When processing a printcap file, the LPRng software reads and parses each entry individually. Leading whitespace is removed. Lines starting with `#` and blank lines are ignored.
2. Lines ending with `\` will have the `\` discarded, and all lines of a printcap entry are joined by removing the line separators (`\n`) and replacing them with a space.
3. The printcap entry is parsed, and the printcap name, aliases, and options are determined. Colons `:` act as option separators, and leading and trailing whitespaces are removed.
4. Options are sorted and except for the `tc=...` option only the last option setting is retained.
5. Client programs will discard a printcap entry with a `server` option and server programs will discard a printcap entry with a `client` options.
6. The `oh` (on this host) option specifies a list of IP addresses and mask pairs or glob strings which are used to determine if this printcap entry is valid for this host (see discussion below).
7. After the above processing, if there is an existing termcap entry with the same name, the two sets of options are combined, with the last option setting retained except for the `tc` entries which are combined.
8. When a printcap entry is actually used, the printcap entries listed by the `tc` include option are extracted and combined in order. This allows include entries to appear after the referring printcap entry. Then printcap options will be combined with the included ones. This has the effect that the options specified in the printcap entry will override the ones from the `tc` included entries.
9. Finally, each string printcap option with a %X value has %X replaced by the following values. Unspecified values will not be modified.

   > `

   >     %P           printcap name
   >     %h           short host name  (host)
   >     %H           fully qualified host name  (host.dns.whatever)
   >     %R           remote printer (rp value)
   >     %M           remote host (rm value)
   >     %D           date in YYYY-MM-DD format

   > `

10. When parsing multiple printcap files, these are processed in order, and all of their printcap entries are combined according to the above procedures. The `tc` resolution and %X expansion is done after all the files have been processed.

The following examples show how to use the above rules to your advantage. You can combine both client and server printcap information in a single file as well as dividing a printcap entry into several parts. Here is an example:

> `

>     # seen by both client and server
>     lp1:lp=lp@pr1:mx=100
>     lp1:sd=/usr/local/spool/lp1:mx=0
>     # seen only by client
>     lp2:lp=lp@pr2:client
>     # seen only by server
>     lp2:lp=/dev/lp:server

> `

1. Printcap entries with the same name are combined. The first printcap entry, `lp1`, the information is seen by both client and server. The next printcap entry, with the same name `lp1`, will be combined with the first one. The order of options is important - the entries are scanned in order and an option will have the last value set. Thus, after having read both the `lp1` printcap entries, both client and server will have:

   > `

   >     lp1:lp=lp@pr1
   >       :mx=0
   >       :sd=/usr/local/spool/lp1

   > `

2. The `lp2` has a client and server version. This is recommended when complex printcaps on multiple hosts and servers are used. Thus, the LPRng clients will see:

   > `

   >     lp1
   >       :lp=lp@pr1
   >       :mx=0
   >       :sd=/usr/local/spool/lp1
   >     lp2
   >       :client
   >       :lp=lp@pr2

   > `

   and the server will see:

   > `

   >     lp1
   >       :lp=lp@pr1
   >       :mx=0
   >       :sd=/usr/local/spool/lp1
   >     lp2
   >       :lp=/dev/lp
   >       :server

   > `

If you have multiple printers of the same type whose configuration is almost identical, then you can define a set of tc only printcap entries containing common information and use the `tc` include facility.

By convention, all printer names start with an alphanumeric character and contain only alphanumeric values, underscore (`_`) or hyphen (`-`). A printcap entry starting with period (`.`), question mark (`?`), or exclamation mark (`!`), is processed by LPRng but can only be used with the `tc` include facility. For example:

> `

>     .hp:
>       :sd=/usr/local/spool/%P
>       :mx=0:sf:sh
>     hp1:tc=.hp,.filter
>       :lp=lp@10.0.0.1
>     hp2:tc=.hp,.filter
>       :lp=lp@10.0.0.2
>     .filter
>       :if=/usr/local/libexec/filters/ifhp
>       :lpd_bounce

> `

1. The `.hp` and `.filter` printcap entities will not be used as real printcaps by LPRng, but can be referenced by the `tc` printcap include facility. After `tc` include processing is completed, the printcap information would resemble:

   > `

   >     hp1
   >       :lp=lp@10.0.0.1
   >       :if=/usr/local/libexec/filters/ifhp
   >       :lpd_bounce
   >       :mx=0
   >       :sd=/usr/local/spool/%P
   >       :sf
   >       :sh
   >     hp2
   >       :lp=lp@10.0.0.2
   >       :if=/usr/local/libexec/filters/ifhp
   >       :lpd_bounce
   >       :mx=0
   >       :sd=/usr/local/spool/%P
   >       :sf
   >       :sh

   > `

2. The %X processing will replace `%P` with the printcap name, so we would have:

   > `

   >     hp1
   >       :lp=lp@10.0.0.1
   >       :if=/usr/local/libexec/filters/ifhp
   >       :lpd_bounce
   >       :mx=0
   >       :sd=/usr/local/spool/hp1
   >       :sf
   >       :sh
   >     hp2
   >       :lp=lp@10.0.0.2
   >       :if=/usr/local/libexec/filters/ifhp
   >       :lpd_bounce
   >       :mx=0
   >       :sd=/usr/local/spool/hp2
   >       :sf
   >       :sh

   > `

When administering a large number of printers over a large area, it is sometimes desirable to have a default printer for each host. This default printer may be different for each host, and can be selected by using the `oh` entry. The `oh` value is a list of the following entries

> `

>     IP/n     - address + mask length    10.0.0.0/8
>     IP/IP    - address + mask           10.0.0.0/255.0.0.0
>     vvv      - glob for hostname        pc*.org.com

> `

The LPRng software will determine the hostnames and IP addresses assigned to the host and then check to see if there is a match in the listed hostnames or IP addresses. If there is a match, the printcap entry will be used. If not, then the entry will be discarded. For example:

> `

>     lp:oh=*.admin.org.com,10.0.0.5,10.2.0.0/16:lp=pr1@server1
>     lp:oh=*.eng.org.com:lp=hp@server2

> `

1. In the above example, if our host name is `booster.admin.org.com`, then we would use `lp=pr1@server1`, as the `*.admin.org.com` glob pattern would match our host name.
2. if our host name is `booster.dev.org.com` and our IP address is 10.2.0.1, then we would use `lp=pr1@server1`, as the `10.2.0.0/16` ip address would be in the specified address range.

## 8.4 Displaying Printcap Information

If you are generating complex printcap entries, you might need to find out exactly what the LPRng servers or clients will actually see. The LPRng software has several diagnostic tools to help you. The most simple to use is the `lpc` program.

The `lpc client all` and `lpc server all` commands will display the printcap information that the LPRng clients and `lpc` server would see when executing on the host. For example:

> `

>     #> lpc client all
>     lp1
>       :lp=lp@pr1
>       :mx=0
>       :sd=/usr/local/spool/lp1
>     lp2
>       :client
>       :lp=/dev/lp

> `

## 8.5 Remote Printer Using RFC1179

Options used:

- `lp=`destination
- `rm=`remote host (machine)
- `rp=`remote printer (machine)

You can have the `lpd` server forward jobs to another server or print which supports the RFC1179 protocol by using the following printcap:

> `

>     # Simplest
>     remote|Remote Printer
>        :lp=raw@server
>     # historical
>     remote:
>       rp=raw:rm=server
>     # Sometimes you have to connect to a non-standard port
>     special:lp=lp@server%2000

> `

1. If the `lp` printer entry is present, it will override the `rm` and `rp` printer entries.
2. The `lp=pr@host` format specifies that the output device is actually a remote spool queue, and jobs should be transferred using RFC1179 protocol.
3. By default, LPRng will attempt to sanitize all jobs that it originates or forwards. This sanitization will result in an RFC1179 compliant `control file`, and will not modify any of the job information.

## 8.6 Remote Printer Using Socket API

If the spool queue destination is a remote printer supporting the Socket API, then you can have LPRng open a connection directly to the printer. These include the older Apple printers with TCP/IP support and the HP JetDirect supported printers.

> `

>     # Simplest
>     remote
>        :lp=10.24.2.3%9100
>        :sh:sf

> `

1. The `lp=server%port` or `lp=IPaddr%port` format specifies that `lpd` should open a TCP/IP connection to the remote host and simply transfer verbatum the files to be printed.
2. The `sh` and `sf` will prevent `lpd` from trying to generate banner pages or put form feeds between jobs.

While this is the simplest printcap, it is also the most dangerous as there is nothing to prevent a malformed job from being sent to the printer. The next printcap example is much more robust:

> `

>     # Simplest
>     remote
>        :lp=10.24.2.3%9100
>        :of=/usr/local/libexec/filters/ifhp
>        :if=/usr/local/libexec/filters/ifhp
>        :sh:sf

> `

1. This version will use the `ifhp` filter to precondition the printer and to process jobs. See IFHP Filter for details. The `ifhp` filter will perform the appropriate printer resets, translate job information, and ensure correct printer operation in the presence of errors. It will also produce voluminous error messages and status information.

## 8.7 Parallel Printer

The parallel printer printcap is very simple.

> `

>     # parallel printer
>     lp:
>        :lp=/dev/lpr
>        :sh:sf

> `

1. The `lp=/dev/lpr` specifies that `lpd` should open the device for APPEND and simply transfer job files to it.
2. The `sh` and `sf` will prevent `lpd` from trying to generate banner pages or put form feeds between jobs.

If you discover that UNIX print jobs result in a staircase appearance, then you need to force your printer to do `LF` (linefeed) to `CR/LF` (carriage return/line feed) translation, or do the translation yourself.

> `

>     # Simple parallel printer
>     lp:
>        :lp=/dev/lpr
>        :if=/usr/local/bin/lpf
>        :sh:sf

> `

By using the `if=...lpf` filter, the job will be passed through the `lpf` filter, which will do the `LF` to `CR/LF` translation.

If you have a more complex printer that handles PostScript, PCL, and PJL, then you will need to use the more powerful `ifhp` filter:

> `

>     # Simple parallel printer
>     lp:
>        :lp=/dev/lpr
>        :ifhp=model=hp4,status@
>        :of=/usr/local/libexec/filters/ifhp
>        :if=/usr/local/libexec/filters/ifhp
>        :sh:sf

> `

See IFHP Filter for details. This entry will specify that the printer is an HP4, and that no status information is available. This is usually the case with a parallel port.

## 8.8 A serial printer queue

Options used:

- `rw` device opened RW flag
- `br#`serial port bit rate
- `stty=`stty options for serial port configuration

The following is a typical printcap for a serial printer:

> `

>     # Local Serial ASCII printer
>     lp2
>       :lp=/dev/ttya
>       :rw
>       :cm=Serial printer
>       :sd=/var/spool/lpd/lp2
>       :stty=9600 -echo -crmod -raw -oddp -evenp pass8 cbreak ixon
>       :if=/usr/local/sbin/lpf
>       :mx=0:sh

> `

Let's examine the new options:

1. A serial port is usually bidirectional, and printers will report errors back to the host computer. The `rw` flag will cause the printer port to be opened `read-write`, and the `lpd` server will report status information.
2. The `stty` option specifies the `stty(1)` flags and line speed needed to configure the serial line (See Serial Printers for details).
3. The legacy `br` (bit rate) option can be used to specify the line speed as well.

## 8.9 Single Printcap File for Large Installation

One of the major problems faced by administrators of large sites is how to distribute printcap information. They would like to have a single printcap file either distributed by a file server (NFS) or by some other method such as `rdist`. By using the `server` and `oh` tags, information for the specific sites can be separated out. For example:

> `

>     #/etc/printcap file
>     pr1:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24
>     pr2:lp=pr1@serverhost1:oh=*.eng.site.com,130.191.12.0/24
>     pr1:lp=pr2@serverhost2:oh=*.admin.site.com
>     pr2:lp=pr2@serverhost2:oh=*.admin.site.com
>     pr1:server:oh=serverhost1.eng.com:lp=/dev/lp:tc=.common
>     pr2:server:oh=serverhost2.admin.com:lp=/dev/lp:tc=.common
>     .common:sd=/usr/local/lpd/%P

> `

The above example has some interesting effects. The `pattern` is used as a glob pattern and is applied to the fully qualified domain name (FQDN) of the host reading the printcap file. For example, `*.eng.site.com` would match host `h1.eng.site.com` but would not match `h1.admin.site.com`. Thus, the effects of the first couple of entries would be to specify that the `pr1` and `pr2` printers on the `eng` hosts would be `pr1@serverhost1`, and on the `admin` hosts would be `pr2@serverhost2`,

Also, the lpd daemons on `serverhost1` and `serverhost2` would extract the additional information for `pr1` and `pr2` respectively, overriding the common `lp` entries.

## 8.10 Bounce queues

Options used:

- `lpd_bounce` lpd filters and then forwards
- `bq=`destination for filtered job
- `bq_format=`format of filtered job

When the destination of a spool queue is another spool queue the job is simply forwarded without any modifications. However, sometimes it is essential that the job be modified before forwarding, as when the remote spool queue is actually a printer, and jobs need to be converted to the format acceptable by the remote printer or banner pages added.

The `lpd_bounce` flag marks a spool queue as a bounce queue. `Lpd` will perform all of the usually job processing steps, such as banner generation, filtering files, etc, but saves the output to a file. This file is then sent to the destination print queue for further processing.

> `

>     # Simple example of a bounce queue
>     bounce:lp=bounce@bouncehost
>     bounce:server
>         :lp=lp@remote
>         :lpd_bounce
>         # LEGACY
>         #bq=lp@remote
>         :sd=/usr/spool/lpd/%P
>         :if=/usr/local/bin/lpf
>         :vf=/usr/local/bin/lpf
>         :bq_format=l
>         # uncomment ab if you want banner
>         #ab

> `

Some comments:

1. The `lpd_bounce` option marks the job as a bounce queue, and the `lpd` server will process the job through the appropriate filter programs.
2. The legacy `bq=host` has the same effect as `lpd_bounce`. This option is retained for compatibility with previous versions of LPRng. It is recommended that this option not be used.
3. The printcap has filter specifications for different job formats. These are the programs that will be used by LPRng to process the job.
4. The `bq_format` specifies the job format for the output file sent to the remote spool queue. If not specified, it defaults to `l` (literal or binary).
5. The `ab` (always print a banner) flag will force a banner to be added to the job. The banner generation is done as discussed in Banner Printing.

## 8.11 Changing Job Formats

Options used:

- `translate_format=`change outgoing job file formats
- `translate_incoming_format=`change incoming job file formats

A rarely encountered problem is when a job is printed with one format but for compatibility needs to be processed with another. The simple `translate_format=vlxf` option will rename format `x` files to `f` format.

This can be used to resolve problems with PC based software, which spools jobs using the `v` format. Unfortunately, many RCF1179 print spoolers do not understand the `v` format and mishandle the job. A simple forwarding queue with the following entries will rename `v` format to `l` (binary) format.

> `

>     lp
>       :sd=/usr/spool/lpd/%P
>       :translate_format=vl
>       :lp=lp@printerserver

> `

The `translate_incoming_format` will do the same thing, but this time on incoming jobs.

## 8.12 LPR Filters Files

Options used:

- `lpr_bounce` lpr does filtering

Some users would like the advantages of the filtering and processing capabilities of a lpd daemon without running a lpd daemon on their system. By having the `lpr` program process the job by passing it through the various filters and then send the output of the filters as the print job you can get the desired effect.

> `

>     # Simple example of an lpr_bounce entry
>     bounce
>       :lpr_bounce
>       :lp=lp@remote
>       :if=/usr/local/bin/lpf

> `

The `lpr_bounce` flag, if present in the printcap entry, will force `lpr` to process the job using the specified filters and send the outputs of the filters to the remote printer for further processing.

## 8.13 Dynamic Routing

Options used:

- `destinations=`destinations for jobs
- `router=`router program

LPRng has the ability to route a job to one or more destinations in a dynamic manner. This is not the same as load balancing, as the destinations are hard coded and not able to be changed. This is accomplished by having a `router` filter generate a set of destinations. Here is a sample printcap entry:

> `

>     t2|Test Printer 2
>         :sd=/var/spool/LPD/t2
>         :lf=log
>         :destinations=t1@server1,t1@server2,t1@localhost
>         :router=/usr/local/LPD/router

> `

When a job arrives at the `lpd` server, the 'router' filter is invoked with the standard filter options which include the user, host, and other information obtained from the control file. STDIN is connected to a temporary copy of the control file, and the CONTROL environment variable is set to the value of the actual control file itself.

The routing filter exit status is used as follows:

- 0 (JSUCC) - normal processing
- 37 (JHOLD) - job is held
- any other value - job is deleted from queue

The router filter writes to STDOUT a file specifying the destinations for the job. The destinations entries in this file file have the following format. Entry order is not important, but each destination must end with the 'end' tag.

> `

>     dest (destination queue)
>     copies (number of copies to be made)
>     priority (priority letter)
>     X(controlfile modifications)
>     end

> `

Example of router output:

> `

>     dest t1@localhost
>     copies 2
>     CA
>     priority B
>     end
>     dest t2@localhost
>     CZ
>     priority Z
>     end

> `

In this example, two copies of the job will be sent to the t1 and t2 spool queue servers. The Class (C letter value) and job priority information will be rewritten with the indicated values.

If routing information is specified by the router filter the job will be sent to the default destination.

LPQ will display job information in a slightly different format for multiple destination jobs. For example:

    Printer: t2@astart2 'Test Printer 2' (routed/bounce queue to 't1@astart2.astart.com')
      Queue: 1 printable jobs in queue
     Rank  Owner/ID        Class Job Files                           Size Time
    active  papowell@astart2+707 A 707  /tmp/hi                         3 10:04:49
     - actv papowell@astart2+707.1 A 707 ->t1@localhost <cpy 1/2>       3 10:04:49
     -      papowell@astart2+707.2 A 707 ->t2@localhost                 3 10:04:49

The routing information is displayed below the main job information. Each destination will have its transfer status displayed as it is transferred. By convention, the job identifier of the routed jobs will have a suffix of the form .N added; copies will have CN added as well. For example, papowell@astart2+707.1C2 will be the job sent to the first destination, copy two.

Routed jobs can be held, removed, etc., just as normal jobs. In addition, the individual destination jobs can be manipulated as well. The LPC functionality has been extended to recognize destination jobids as well as the main job id for control and/or selection operations.

The optional `destinations` entry specifies the possible set of destinations that the job can be sent to, and is for informational purposes only. In order for LPQ/LPRM to find the job once it has passed through LPD, LPQ/LPRM uses the list of printers in the `destinations`, and loop over all the names in the list looking for the "job" that you are interested in. If there is no `destinations` information, the `bq` information will be used.

Lars Anderson <`lsa@business.auc.dk`> supplied this example (slightly edited):

This script will attempt to distribute print jobs evenly on 2 printers hpl5a and hpl5b when sending to hpl5bounce.

> `

>     hpl5bounce|for PLP/LPRng software - network based HP JetDirect card:
>             :lpd_bounce
>             #default
>             :rp=hpl5b
>             :destinations=hp5a,hp5b
>             :router=/usr/local/admscripts/bouncer.pl
>     hpl5a|for PLP/LPRng software - network based HP JetDirect card:
>             :lp=hpl5a%9100
>             :tc=.hplcommon
>     hpl5b|for PLP/LPRng software - network based HP JetDirect card:
>             :lp=hpl5b%9100
>             :tc=.hplcommon
>     # Common settings
>     .hplcommon:
>             :sd=/var/spool/lpd/%P
>             :rw:sh:ps=status
>             :fx=flp
>             :if=/usr/local/libexec/filters/ifhp -Tbanner=on
>             :of=/usr/local/libexec/filters/ofhp -Tbanner=on

> `

The perl script `bouncer.pl` looks like this:

> `

>     #!/usr/bin/perl
>     #
>     # Script for printjob load sharing
>     #   This is static, not dynamic balancing
>     #
>     # Printqueues to check
>     $printer1="hpl5a\@localhost";
>     $printer2="hpl5b\@localhost";
>     # obtain number of jobs in each printqueue
>     $lpq1=`/usr/local/bin/lpq -s -P$printer1`;
>     $lpq2=`/usr/local/bin/lpq -s -P$printer2`;
>     $lpq1=~ (/(\d+) jobs?/); $numjobs1=$1;
>     $lpq2=~ (/(\d+) jobs?/); $numjobs2=$1;
>     if ($numjobs1 == 0) {
>         print "dest $printer1\nCA\nend\n";
>         exit;
>     }
>     if ($numjobs1 > $numjobs2) {
>         print "dest $printer2\nCA\nend\n";
>         exit;
>     }
>     print "dest $printer1\nCA\nend\n";

> `

## 8.14 Printer Load Balancing

In a large site, you could have several equivalent printers, which will be used by many people. The reason for this is, of course, to increase the printer output by enabling several jobs to be printed at once.

LPRng supplies mechanisms to define a `virtual' printer for such a set of real printers. If properly set up, print jobs will be distributed evenly over all printers.

### Multi-server print queue

Options used:

- `ss=`queue served by printer
- `sv=`printers where jobs are sent (servers)

A multi-server print queue is one that feeds jobs to other queues. The main queue `sv=q1,q2,...` printcap entry specifies the names of the printers that will be sent jobs. These printers must have their spool queues on this LPD server.

Servers that are fed jobs have a `ss=`mainqueue printcap entry. This informs the `lpd` server that the queue operates under the control of the mainqueue print queue, and is fed jobs from it.

During normal operation, when the `lpd` server has a job to print in the mainqueue, it will check to see if there is an idle service queue. If there is, it will transfer the job to the service queue spooling directory and start the service queue printing activities.

Users can send jobs directly to the individual printers serving a queue.

The next example (and the comments underneath) was supplied by John Perkins <`john@cs.wisc.edu`> (slightly edited).

Here's how I've set up a bounce queue that feeds 6 LaserWriters:

> `

>     laser|pi|Room 1359 LaserWriters
>         :lp=laser@server.com
>     laser|pi|Room 1359 LaserWriters
>         :server
>         :lf=/usr/adm/laser-log
>         :sv=laser1,laser2,laser3,laser4,laser5,laser6
>         :sd=/usr/spool/laser
>     .commonlaser
>         :sd=/usr/spool/%P
>         :rw:mx=0:sh
>         :lf=/usr/adm/laser1-log
>         :if=/s/lprng/lib/filters/cappsif
>         :of=/s/lprng/depend/cap/bin/papof
>         :ss=laser
>         :fx=fdginpt
>     laser1|pi1|Room 1359 LaserWriter #1
>         :lp=laser1@server.com
>     laser1|pi1|Room 1359 LaserWriter #1
>         :server
>         :lp=/dev/laser1
>         :tc=.commonlaser
>     laser2|pi2|Room 1359 LaserWriter #1
>         :lp=laser2@server.com
>     laser2|pi2|Room 1359 LaserWriter #2
>         :server
>         :lp=/dev/laser2
>         :tc=.commonlaser

> `

and so on for the other 4 `laser` N queues.

This will forward a job from `laser` to `laser` N, once one of those queues is available. It will hold jobs in the ```laser`'' queue until one of the other queues is empty.

Even though the queues are not meant for direct use, people can print directly to individual queues. This allows a specific load sharing printer to be used. If you wanted to hide the load sharing printers, i.e. - not allow direct spooling to them, then you would simply remove the non-server entries from the printcap.

### Checking Busy Status of Server Queues

Options used:

- `check_idle=`check for idle printer program

The previous section outlined how LPRng uses the `sv` and `ss` flags to indicate that the server spool queue has multiple destination queues. However, there is a problem when the actual printer being served by the destination queue is a remote device, and can be busy or offline.

The `check_idle` option specifies a program that is invoked by the `lpd` server to determine if the spool queue device is available.

The program is invoked with the standard filter options, STDIN and STDOUT connected to `/dev/null`, and STDERR to the status file.

The program should make a connection to the remote device or system and should determine that the remote device is available for use, and then exit with the following status.

> `

>     Key      Value   Meaning
>     JSUCC    0       Successful - printer is idle
>     JABORT   non-zero Printer is not accepting jobs

> `

If the printer is accepting jobs but is temporarily busy, the program should poll the printer until it becomes free, only exiting when it is available for use. If the printer is not accepting jobs, the program should exit with a non-zero exit code.

The following is a sample printcap entry, showing how the `check_idle` facility can be used.

> `

>     pr:
>       :lp=laserjet%9100
>       :check_idle=/usr/local/filters/remote_check lp@laserjet
>       :if=/usr/local/filters/ifhp

> `

The following perl program shows how to generate a query to the remote printer by simulating an `lpq` query and checking for returned status.

> `

>     #!/usr/local/bin/perl
>     # Usage:
>     #  remote_check printer@host[%port] [-options]
>     #   -Tflag[,flags]*
>     #  flag
>     #    debug  - turns debugging on
>     #    long   - use long status format
>     #
>     # query the remote printer whose name is passed on the command line
>     #
>     # Note that -Txxx options are passed AFTER the printer
>     use English;
>     use IO::Socket;

>     my $JSUCC = 0;
>     my $JABORT = 33;
>     my $JNOSPOOL = 38;
>     my $JNOPRINT = 39;

>     my $debug = 0;
>     my $optind;

>     # pull out the options
>     my($key,$value,$opt,$long,$opt_c);

>     $printer = $ARGV[0];

>     for( $i = 1; $i < @ARGV; ++$i ){
>         $opt = $ARGV[$i];
>         print STDERR "XX opt= $opt\n" if $debug;
>         if( $opt eq '-c' ){
>             $opt_c = 1;
>         } elsif( ($key, $value) = ($opt =~ /^-(.)(.*)/) ){
>             if( $value eq "" ){
>                 $value = $ARGV[++$i];
>             }
>             ${"opt_$key"} = $value;
>             print STDERR "XX opt_$key = " . ${"opt_$key"} . "\n" if $debug;
>         } else {
>             $optind = $i;
>             last;
>         }
>         print STDERR "XX opt_P = $opt_P\n" if $debug;
>     }

>     $long = 0;  # short

>     if( defined($opt_T) ){
>         print STDERR "XX CHECK_REMOTE opt_T=$opt_T\n" if $debug;
>         if( $opt_T =~ /debug/ ){
>             $debug = 1;
>         }
>         if( $opt_T =~ /short/ ){
>             $long = 1;
>         }
>         if( $opt_T =~ /long/ ){
>             $long = 0;
>         }
>     }

>     print STDERR "XX CHECK_REMOTE " . join(" ",@ARGV) . "\n" if $debug;

>     if( !defined($printer) or $printer =~ /^-/ ){
>         print STDERR "$0: no printer value\n";
>         exit( $JABORT );
>     }

>     while( checkstatus( $printer, $long ) ){
>         print STDERR "XX CHECK_REMOTE sleeping\n" if $debug;
>         sleep(10);
>     }

>     exit $JSUCC;

>     sub checkstatus {
>         my ($printer,$long) = @_;
>         my ($remote,$port);
>         my ($count, $socket, $line);

>         if( $long ){
>             $long = 4;
>         } else {
>             $long = 3;
>         }
>         if( $printer =~ /@/ ){
>             ($printer,$remote) = $printer =~ m/(.*)@(.*)/;
>         }
>         $remote="localhost" unless $remote;

>         if( $remote =~ /%/ ){
>             ($remote,$port) = $remote =~ m/(.*)%(.*)/;
>         }
>         $port = 515 unless $port;
>         print STDERR "XX CHECK_REMOTE remote='$remote',"
>             . " port='$port', pr='$printer', op='$long'\n" if $debug;

>         $socket = getconnection( $remote, $port );

>         $count = -1;
>         # send the command
>         printf $socket "%c%s\n", $long, $printer;

>         while ( defined( $line = <$socket>) && $count < 0 ){
>             chomp $line;
>             print STDERR "XX CHECKREMOTE '$line'\n" if $debug;
>             if( $line =~ /printing disa/ ){
>                 print STDERR "XX CHECKREMOTE printing disable\n" if $debug;
>                 exit $JNOPRINT;
>             } elsif( $line =~ /spooling disa/ ){
>                 print STDERR "XX CHECKREMOTE printing disable\n" if $debug;
>                 exit $JNOSPOOL;
>             } elsif( $line =~ /([0-9]*)\s+job.?$/ ){
>                 $count = $1;
>                 print STDERR "XX CHECKREMOTE $count jobs\n" if $debug;
>             }
>         }
>         close $socket;
>         if( $count < 0 ){
>             print STDERR "CHECKREMOTE cannot decode status\n";
>             exit $JABORT;
>         }
>         return $count;
>     }

>     sub getconnection {
>         my ($remote,$port) = @_;
>         my ($socket);
>         print STDERR "XX CHECK_REMOTE remote='$remote', port=$port\n" if $debug;
>         $socket = IO::Socket::INET->new(
>             Proto => "tcp",
>             PeerAddr => $remote,
>             PeerPort => $port,
>             );
>         if( !$socket ){
>             print STDERR "CHECK_REMOTE IO::Socket::INET failed - $!\n";
>             exit $JABORT;
>         }
>         $socket->autoflush(1);
>         $socket;
>     }

> `

The example of the previous section can be modified now so that it uses the `check_idle` facility. The master queue will send jobs only to the server queue queues which report idle status.

> `

>     laser1|pi1|Room 1359 LaserWriter #1
>         :server:check_idle=/usr/local/libexec/filters/remote_check pr@laser1
>         :lp=laser1%9100
>         :tc=@commonlaser
>     laser2|pi2|Room 1359 LaserWriter #2
>         :server:check_idle=/usr/local/libexec/filters/remote_check pr@laser1
>         :lp=laser2%9100
>         :tc=@commonlaser

> `

## 8.15 Locations of Printcap Files

Options used:

- `printcap_path=`printcap file locations
- `lpd_printcap_path=`additional server printcap file locations

The `printcap_path` and `lpd_printcap_path` configuration options (see lpd.conf(5)) specify a set of paths for the printcap information. Client programs use only `printcap_path` and the `lpd` server uses both `printcap_path` and `lpd_printcap_path`. The path names can be separated with whitespace, commas, semicolons, or colons. The default values are:

> `

>     printcap_path      /etc/printcap /usr/etc/printcap
>     lpd_printcap_path  /etc/lpd_printcap /usr/etc/lpd_printcap

> `

### Separate Server and Client Printcap Files

Since only the LPD server uses the printcap file specified by the `lpd_printcap_path`, you can place server specific information there. This allows you to have a common printcap file for clients and an additional one for the lpd servers.

### ALL Printcap Entry

The `all` printcap name and `all` option is reserved to provide a list of printers available for use by the spooling software. This is a desperation, last ditch, back to the wall option for administrators with systems that do not have ways to provide a list of printcap entries. The 'all' printcap entry has the form:

> `

>     all:all=pr1,pr2,...

> `

The value of the `all` option should be a list of printcap names whose values will then be extracted.

## 8.16 Management Strategies for Large Installations

One very effective way to organize print spooling is to have a small number of print servers running a `lpd` daemon, and to have all the other systems send their jobs directly to them. By using the above methods of specifying the printer and server host you eliminate the need for more complex management strategies.

However, you still need to inform users of the names and existence of these printers, and how to contact them. One method is to use a common `/etc/printcap` file which is periodically updated and transfered to all sites. Another method is to distribute the information using the NIS or some other database. LPRng has provided a very flexible method of obtaining and distributing database information: see Using Programs To Get Printcap Information for details.

## 8.17 Using Programs To Get Printcap Information

In the `lpd.conf` file you can specify:

> `

>     printcap_path=|program

> `

This will cause the LPRng software to execute the specified program, which should then provide the printcap information. The program is invoked with the standard filter options, and has the name of the printcap entry provided on `STDIN`. The filter should supply the printcap information on `stdout` and exit with a 0 (success) error code. By convention, the printcap name 'all' requests a printcap entry that lists all printers.

This technique has been used to interface to the Sun Microsystem NIS and NIS+ databases with great success. By having the invoked program a simple shell script or front end to the `nismatch` or `ypmatch` programs, the complexity of incorporating vendor specific code is avoided.

### How to use NIS and LPRng

This note is based on material sent to the `lprng@lprng.org` mailing list by Paul Haldane `<paul@ucs.ed.ac.uk>`.

> `

>      # From: Paul Haldane <paul@ucs.ed.ac.uk>
>      # To: lprng@lprng.org
>      # Subject: Re: Problem using plp with NIS
>      #

> `

We generally don't use NIS for printcap files (we've moved to hesiod) but I can show you what we've done in the past.

The input to NIS is a normal printcap file:

> `

>     # Classical printcap entry
>     lp23a|lp23|lp|main printhost printer - KB, EUCS front Door:\
>             :lp=lp23a@printhost:\
>             :sd=/usr/spool/lpr/lp23a:

>     #lprng printcap entry
>     lplabel|lpl|TEST - Labels printer:
>             :lp=:rm=printhost:rp=lplabel:
>             :sd=/usr/spool/lpr/lplabel:
>             :rg=lpadm:mx=1:

> `

To build the NIS printcap.byname map we add the following to the NIS makefile (along the other bits and pieces that the makefile needs to know about a new map).

> `

>     PRINTCAP=$(DIR)/printcap
>     #PRINTCAP=/etc/printcap
>     # warning : [  ] is actually [<space><tab>] in the script
>     printcap.time: $(PRINTCAP) Makefile
>       if [ -f $(PRINTCAP) ]; then \
>         sed < $(PRINTCAP) \
>           -e 's/[   ][  ]*$$//' -e '/\\$$/s/\\$$/ /' \
>         | awk '$$1 ~ /^#/{next;} $$1 ~ /^[:|]/ {printf "%s", $$0; next;} \
>             {printf "\n%s", $$0 }' \
>         | sed -e 's/[   ]*:[  ]*:/:/g' -e 's/[  ]*|[  ]*/|/g' \
>           -e '/^[   ]*$$/d' > .printcap.$$$$; \
>         cat .printcap.$$$$; \
>         if [ $$? = 0 -a -s .printcap.$$$$ ]; then \
>           awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
>               n = split($$1, names, "|"); \
>               for (i=1; i<=n; i++) \
>                   if (length(names[i]) > 0 \
>                   && names[i] !~ /[ \t]/) \
>                       print names[i], $$0; \
>           }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.byname; \
>           awk <.printcap.$$$$ '{ FS=":"; OFS="\t"; } { \
>               n = split($$1, names, "|"); \
>               if (n && length(names[1]) > 0 && names[1] !~ /[ \t]/) \
>                   print names[1], $$0; \
>           }' | $(MAKEDBM) - $(YPDBDIR)/$(DOM)/printcap.bykey; \
>           rm -f .printcap.$$$$; \
>           touch printcap.time; echo "updated printcap"; \
>         fi \
>       fi
>       @if [ ! $(NOPUSH) -a -f $(PRINTCAP) ]; then \
>           $(YPPUSH) printcap.byname; \
>           $(YPPUSH) printcap.bykey; \
>           touch printcap.time; echo "pushed printcap"; \
>       fi

> `

To specify that you want YP database rather than file access, use the following entry in your `/etc/lpd.conf` file:

> `

>     printcap_path |/usr/local/libexec/pcfilter

> `

Put the following shell script in /usr/local/libexec/pcfilter

> `

>     #!/bin/sh
>     #/usr/local/libexec/filters/pcfilter
>     read key
>     ypmatch "$key" printcap.byname

> `

### How to use NIS and LPRng - Sven Rudolph

> `

>      Date: Wed, 11 Sep 1996 00:11:02 +0200
>      From: Sven Rudolph <sr1@os.inf.tu-dresden.de>
>      To: lprng@lprng.org
>      Subject: Using :oh=server: with NIS

> `

When I use a cluster-wide printcap, two entries for each printer will appear, e. g.:

> `

>     ---------- start of /etc/printcap snippet
>     lp1
>      :lp=lp1@server
>     lp2
>      :lp=lp2@server
>     lp1
>      :server:oh=servername
>      :sd=/var/spool/lpd/lp1
>      :lp=/dev/lp1
>      :sh:mx=0
>     ---------- end of /etc/printcap snippet

> `

When I create a NIS map out of this, the printer name is used as a key and must be unique. So NIS' makedbm decides to drop all but the last entry for each printer. This makes the printer on the clients unavailable. I solved this by a hack where the second entry is called lp1.server and the NIS client script has to request the right entry.

1. Assumptions

   Perl is available at the YP server in /usr/bin/perl . A Bourne Shell is available at all clients in /bin/sh The printcap that is to be exported is in /etc/printcap . The printcap is written in the new format.

   In the examples the printer is called lp1 .

2. Add the following to your YP Makefile (/var/yp/Makefile) on the YP server :

   > `

   >     ---------- start of /var/yp/Makefile snippet
   >     PRINTCAP  = /etc/printcap
   >     printcap: $(PRINTCAP)
   >         @echo "Updating $@..."
   >         $(CAT) $(PRINTCAP) | \
   >             /usr/lib/yp/normalize_printcap | $(DBLOAD) -i $(PRINTCAP) \
   >             -o $(YPMAPDIR)/$@ - $@
   >         @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
   >         @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
   >     ---------- end of /var/yp/Makefile snippet

   > `

   (These lines are for Debian GNU/Linux, other systems might require other modifications)

3. Install the programs `match_printcap` and `normalize_printcap` to `/usr/lib/yp`. normalize_printcap is only required on the YP server. The `normalize_printcap` processes only the LPRng printcap format.

   > `

   >     ---------- start of /usr/lib/yp/normalize_printcap
   >     #! /usr/bin/perl
   >     $debug = 0;
   >     $line = "";
   >     $new = "";
   >     while (<>) {
   >         chomp;
   >         next if ( /^\s*\#.*/ );
   >         s/^\s*$//;
   >         next if ( $_ eq '' );
   >         print "new: " . $_ . "\n" if $debug;;
   >         if (/^\s/) { # continuation line
   >             $line = $line.$_;
   >             print "continued: $line\n" if $debug;
   >             next;
   >         } else {
   >             $line =~ s/\s+\:/:/g;
   >             $line =~ s/\:\s+/:/g;
   >             $line =~ s/\:\s*\:/:/g;
   >             print "line: $line\n" if $debug;
   >             push(@lines, $line) if $line;
   >             $line = $_;
   >         }
   >     }
   >     $line =~ s/\s+\:/:/g;
   >     $line =~ s/\:\s+/:/g;
   >     $line =~ s/\:\s*\:/:/g;
   >     push(@lines,$line) if $line;
   >     @lines = sort(@lines);
   >     foreach $line (@lines) {
   >         ($printers) = split(/\:/,$line);
   >         @printers = split(/\|/,$printers);
   >         foreach $printer (@printers) {
   >           $num{$printer}++;
   >           push(@allprinters,$printer);
   >           print "allprinters: @allprinters\n" if $debug;
   >           print $printer."_".$num{$printer}."\t$line\n";
   >         }
   >     }
   >     @pr = keys %num;
   >     print "printers @pr\n" if $debug;
   >     if ($#allprinters >=0) {
   >         print "all_1\tall:all=".join(",",@pr)."\n";
   >     }
   >     ---------- end of /usr/lib/yp/normalize_printcap

   > `

   The result of processing the sample printcap file is:

   > `

   >     lp1_1 lp1:lp=lp1@server
   >     lp1_2 lp1:server:oh=servername:sd=/var/spool/lpd/lp1:lp=/dev/lp1:sh:mx=0
   >     lp2_1 lp2:lp=lp2@server
   >     all_1 all:all=lp1,lp2

   > `

   Observe that each of the real printer entries has a key consisting of the printer name with a numerical suffix. This leads to the following method of extracting the printcap information using `ypmatch`:

   > `

   >     ---------- start of /usr/lib/yp/match_printcap
   >     #!/bin/sh
   >     read p
   >     n=1
   >     while ypmatch "${p}_${n}" printcap 2>/dev/null; do
   >         n=`expr $n + 1`
   >     done
   >     ---------- end of /usr/lib/yp/match_printcap

   > `

4. Now test the YP arrangement:

   > `

   >     $ cd /var/yp; make # this should create the printcap map
   >     $ ypcat printcap # should provide the whole normalized printcap
   >     $ echo lp1 |/usr/lib/yp/match_printcap # yields lp1 printcap

   > `

5. Add the printcap_path entry to `/etc/lpd.conf`:

   > `

   >     printcap_path=|/usr/lib/yp/match_printcap

   > `

6. Test the use of the printcap path entry:

   > `

   >     $ lpq -Plp1 # shows the status of lp1

   > `

7. Restart the lpd server and check to see that it accesses the right printcap information. Use the same `lpq` command, and then try `lpc printcap lp1`.

Next Previous Contents