LPRng-HOWTO: Filters

Next Previous Contents

## 6. Filters

This section gives an overview of how LPRng uses filter programs, and gives a detailed discussion of how the printcap options and filters interact.

## 6.1 What are filters?

Print filters are one of the most powerful tools in BSD-style printer systems.

In general UNIX terms, a filter is a program that takes its input file(s), does something with it, and sends the result to its standard output. Most UNIX utilities are designed as filters. (But since you are a system manager, you should already know that :))

In the context of a BSD-style print spooler (and also LPRng), the term filter refers to a program that does processing on a file that is submitted to the printer. As such, it is a specific example of the general class of programs called `filters'.

Usually the filter is executed with STDIN reading from the file to be printed or program generating the output and STDOUT to the printer device. STDERR (file handle 2) is redirected to a log file, and file handle 3 to an accounting file.

A filter can be as simple as a `LF` to `CR/LF` translator (the example used before), or it can incorporate a complete accounting system, automatic file type translations, or even redirect the job to another printing system.

As part of the LPRng project, the following filters are supported. See the associated documentation for details.

- Distributed in the LPRng source distribution
  lpf a very simple CR/LF or passthrough filter.
- Distributed in the FILTERS_LPRng distribution:
  CTI-ifhp HP PCL and PJL printer filters
- psfilter PostScript printer filter

The supported filters and other facilities are available from ftp://ftp.astart.com/pub/LPRng

## 6.2 What are print formats?

Options used:

- `if`, `cf`, `df`, `gf`, `nf`, `of`, `rf`, `tf`, `vf`, X `f`, Filter programs

LPRng has inherited a set of so-called `print formats' from its BSD ancestor. The format was originally used to specify the type of file that was being printed. The `lpd` daemon would use the print format to select the required filter for processing the file. The default format is `f`.

The user can specify the format (i.e., the file type) by giving the appropriate option to `lpr`:

- `-b` or `-l`: Binary (literal) file. No processing should be done. The `l` format is recorded as the file format.
- `-c`: cifplot(1) output.
- `-d`: TeX DVI file.
- `-g`: Output from the plot(3X) routines.
- `-n` or `-t`: (di)troff output.
- `-p`: Text file that should be pre-processed by the `pr` command, and then by the standard text filter.
- `-r`: Text with FORTRAN carriage control characters in the first column. (Used to be the `-f` option.)
- `-v`: Benson Varian raster image.

Alternatively, one can also use `-Fx`, where `x` is the format specifier. (E.g., `-Fc` instead of `-c`.) This last form also allows you to use other (non-standard) format specifiers.

The filter for format `X` is the value for the `Xf` printcap option, with some minor exceptions. The following `Xf` options have a pre-defined meaning.

- `if` The `f` format filter, i.e. - for the default `f` format. All print jobs are passed through this one, unless another format is selected.
- `cf` Cifplot data filter (for `-c` format).
- `df` Filter for DVI files (`-d`).
- `gf` Graph data filter (`-g`).
- `nf` Ditroff data filter (`-n`).
- `of` This filter is used for processing the (optional) banner at the start and/or end of the print job, and also for the interjob separators. See of filter for details.
- `rf` Filter for Fortran style files (`-r`).
- `tf` Troff filter (`-t`).
- `vf` (Versatek) raster image filter (`-v`).

## 6.3 OF Filter

The `of` filter is used to process banners and job separators. The `of` filter is responsible for performing appropriate processing of this information and sending to the printer for action.

While the various file filters are invoked on a once per print file basis, the `of` filter is invoked on a once per print job basis.

This filter is the first one to be started, and should perform whatever specialized device initialization is needed. It should also do whatever accounting procedure is desired for start of job accounting.

The `of` filter will be given any banner printing or job separation information for a job. As part of its operation, it can detect a specific string, corresponding to a banner print request, and generate a banner. (See the Job Processing Steps and Printcap Options for details.)

During operation, the `lpd` server will send the special stop sequence of `\031\001` to the `of` filter. The filter must then suspend itself using a `kill -STOP` operation. The `lpd` server will detect that the `of` filter has suspended itself and then will perform other printing operations.

After the other printing operations have been completed, the `of` will then be sent a `kill -CONT` signal.

This sequence will continue until all information has been printed, and then the `of` filter's STDIN will be closed. The filter will then perform whatever cleanup operations are needed, update accounting or other information, and exit.

## 6.4 The lpr -p format and pr option

Options used:

- `pr=`pr program for p format

The `-p` format is implemented by sending the file through the program specified by the `pr` printcap utility (default is `/bin/pr`), and passing the result to the normal `:if` filter.

## 6.5 The lpr -l format and binary format

The binary (or literal) format is indicated by format type `-l`. The `if` filter is used to process the file, and is invoked with the `-c` (`c`ancel processing?) flag.

## 6.6 Job Processing and Printcap Options

Much of the flexibility of the LPRng software is obtained from the ability to control the details of each step of job processing. The following section details each step in the processing of a job, and explains the printcap options used to control each operation.

Assume the `pr` printcap entry has the form:

    pr
        :lp=/dev/lp  OR  :lp=rp@rm
        :sd=/var/spool/lpd/pr
        :lf=log
        :of=/usr/local/bin/lpf
        :if=/usr/local/bin/lpf

Assume that we have used the following command to print a set of files.

    lpr -Ppr file1 file2

This will create a control file in the `/var/spool/lpd/pr` directory with the following contents (this is an example - in practice there may be minor differences between the example and an actual control file):

    Hastart4.astart.com
    J/tmp/file1 /tmp/file2
    CA
    Lpapowell
    Ppapowell
    fdfA002230astart4.astart.com
    N/tmp/file1
    UdfA002230astart4.astart.com
    fdfB002230astart4.astart.com
    N/tmp/file2
    UdfB002230astart4.astart.com

### Opening the Output Device

Options used:

- `achk` Accounting check at start
- `af=`Accounting File
- `ar` Remote printer accounting enabled
- `as=`Accounting at start
- `connect_grace#` Time between jobs
- `connect_interval#` Connection interval
- `connect_timeout#` Connection timeout
- `control_filter=`Control file filter
- `ff` form feed
- `fo` form feed on open
- `la` Local printer accounting enabled
- `ld=`leader on open (initialization string)
- `lk` Lock IO device
- `lp=`IO device pathname
- `nb` Nonblocking device open
- `network_connect_grace#` Time between jobs
- `of=`of filter
- `retry_econnrefused#` Retry if open failed
- `retry_nolink#` Retry if open failed
- `rm` the remote machine to send the job to
- `rp` the remote print queue to send the job to
- `rw` device opened RW flag
- `server_tmp_dir=`temporary directory

Sequence of Operations:

1. During the server operations, it will try to create temporary files in the print queue spool directory. If this is not desirable, it will create them in the `server_tmp_dir` directory.
2. If the accounting file specified by `af` exists, it is opened (af_fd) and the af_fd is passed as file descriptor 3 to all filters. If the `af` value has the form `af=|/program` then the program is started and the program STDIN is used as af_fd. If the `af` value has the form `af=host%port`, then a TCP/IP connection to the corresponding port on the remote host is made and the port used as af_fd. In the latter two cases, the filter STDIN (file descriptor 0) is actualy opened read/write, and is used when information is needed from the accounting filter or remote server. See Accounting Printcap Options for more information on the LPRng accounting support.
3. If the `connect_grace` value is non-zero and the server is opening a device or `network_connect_grace` is non-zero and a network connection is being made, the server will pause the specified time. This is to accommodate devices which need a recovery time between jobs.
4. The `lp` option is checked to determine the type of IO device.

   Format | Meaning
   `/pathname` | Absolute pathname of IO device
   `pr@host` | transfer to `pr` on remote `host`
   `host%port` | open a TCP/IP connection to port on host. host can be name or IP address
   `|filter` | run the filter program; it STDIN will be used as device

5. The IO device specified by `lp` is opened write-only or read-write if the `rw` flag is true, and the resulting file descriptor is io_fd. If the `nb` flag is set, a non-blocking open will be done as well. If the `lk` (lock device) flag is true, the device will be locked against use by other LPD servers.
6. If a `host%port` combination, a TCP/IP connection will be opened to the remote port and the connection will be used as io_fd.
7. If a filter program is specified, the filter program will be run and the STDIN of the filter will be used as the device file descriptor.
8. If a `rp@rm` combination, or none of the above combinations are true and the `rm` and `rp` values are non-zero, then the job will be transferred to a remote printer. The type of operation will be a job transfer, rather than printing operation.
9. If the `connect_timeout` value is non-zero, a timeout is setup for the device or socket open. If the device or connection open does not succeed within the timeout, then the open operation fails.
10. If a connection is to a network address (i.e. - `connect()` system call) and the connection attempt fails with an `ECONNREFUSED` error, if the `retry_econnrefused` flag is set then the connection attempt is retried, but this time using an alternative port number. See RFC1179 for details. This is repeated until all of the possible originating port numbers are exhausted.
11. If the open or connect operation fails, and the `retry_nolink` flag is set, then the server will pause for a minimum of `connect_grace` plus a multiple of `connect_interval` seconds based on the number of attempts before retrying the open operation. Note that the interval may increase as the number of attempts increases.
12. If printing a job and the `of` filter is specified, it is created with its STDOUT (fd 1) attached to the io_fd. Its stdin (of_fd) will be used in the steps listed below. If there is no `of` filter, then the of_fd value will be the io_fd descriptor.
13. If transferring a job and the `control_filter` option is specified, then the program specified by the `control_filter` value will be run. It will have its STDIN set to the control file, and its STDOUT output will be used as the new value of the control file to transfer to the remote host. See Filter Command Line Flags for details of options passed to the control filter, and errorcodes for the exit codes of the filter.
14. If `la` (local accounting) is true and we are printing a job or `ar` (remote accounting) is true and we are transferring a job, the `as` value is examined. If it is a filter (program) specification, then the program is started with its STDIN attached to `/dev/null` and STDOUT to the io_fd, STDERR to the error file, and file descriptor 3 to the accounting file descriptor af_fd. The lpd program will wait until it terminates, and examine the error code for action, as for the filters (see errorcodes below). If it is a string, then it is interpreted, the escape sequences replaced with the appropriate information, and written to the accounting file.
15. If the `achk` (accounting check) flag is set, a line is read from the accounting filter af_fd file descriptor. This line should be `accept`, otherwise the job processing terminates with a JFAIL indication.
16. If the operation is a job transfer, the operation proceeds as outlined in RFC1179, and then the Normal Termination operations are carried out.
17. If the operation is a print operation and the `ld` (leader on open) value is provided, the string is translated (escapes removed) and written to the of_fd file descriptor.
18. If the `fo` (form feed on open) flag is true, then the `ff` (form feed) string is translated (escapes removed) and written to the of_fd file descriptor.

### Printing Banner At Beginning

Options used:

- `ab` Always print banner (default FALSE)
- `be=`End banner generator program
- `bl=`Short banner line format
- `bp=`Banner generator program
- `bs=`Start banner generator
- `hl` Banner (header) Last
- `of=`Banner and File Separator Filter
- `sb` Short banner (default FALSE)
- `sh` Suppress header (banners) (default FALSE)

Sequence of Operations:

1. If the `sh` (suppress header) flag is true, no banner is printed, and the actions in this section are skipped.
2. If the `hl` flag is true, the banner is printed at the end of the job, and the actions in this section are skipped.
3. If the user does not supply a banner name, (the `L` line in the control file) and `ab` (always print a banner) is false (the default), then no banner is printed. If no name is supplied and `ab` is true, then ANONYMOUS is used.
4. There are two types of banners - short and long. If the `sb` flag is set, then we send the `bl` (banner line) contents directly to the of_fd; By default the `bl` value is: `bl=$-'C:$-'n Job: $-'J Date: $-'t` (See Filter Command Line Flags for details.) This will get translated to:
   `papowell:A Job: file1 file2 Date: Thu Nov 27 23:02:04 PST 1997`
5. If the `sb` flag is clear, we will generate a long banner using a program instead. If `bs` (start banner) program is specified, then it is used to generate a banner, otherwise if the `bp` (banner) program is specified, then it is used to generate a banner. If no program is available, we skip the banner generation. The banner generator program is started with the normal command line flags (see Filter Command Line Flags), with its STDOUT attached to the of_fd descriptor. The short banner string described in the previous step is written to the STDIN. The banner printer is responsible for generating a banner appropriate to the printing device.
6. The `ff` (form feed) string will be interpreted and sent to the of_fd.

### Printing Job Files

Options used:

- `Xf=`Format Filter
- `direct_read` Direct connection to file
- `if=`Default F Format Filter
- `pr=`pr formatting program
- `send_job_rw_timeout=` print job read/write timeout
- `send_query_rw_timeout=` status query operation read/write timeout
- `sf` Suppress FF Print File Separators

Sequence of Operations: for each job in listed in the control file, the following operations are done in turn.

1. If there is an `of` filter present, the suspend string `\031\001` is written to of_fd and the no further action is taken until the of filter is suspended.
2. The control file line for the job is examined, and the first letter of the data file specification is used as the format.
3. If the format is `p`, the job is first processed by the program specified by the `pr` program, and the program output used as the print file.
4. If the format is `f`, `l`, or `p` then the `if` filter is used, otherwise the keyword `Xf` is used. Note that certain formats such as `p, a, l`, may not be used as formats.
5. The `direct_read` flag determines how the print file is provided to the filter. Normally, the `lpd` writes the file to the filter process, and can monitor the printing activity in this way. However, some filters require a direct connection in order to do `lseek` or other operations on the file. If the `direct_read` flag is true, then the print file is opened and passed directly to the filter process, otherwise the `lpd` program will read the file and write its contents to the filter.
6. The filter program is started with an appropriate set of command line options (see Filter Command Line Flags), and with its STDOUT attached to the printing device (io_fd), STDERR to the log file (`lf`), and file descriptor 3 to the accounting fd af_fd. If `direct_read` is false, the file is then written to the STDIN of the filter. This allows the server to monitor job progress.
7. When doing a read/write operation to a device or remote system, a timeout can be specified. When doing a print or job transfer operation, the `send_job_rw_timeout` value is used. When doing a status or query operation, the `send_query_rw_timeout` value is used. If a write or write operation does not complete within the specified timeout seconds, then we have an error condition and job processing or the query operation is terminated with JFAIL status. If the timeout value is 0, then no timeout is done.
8. `lpd` will then wait for the filter to exit. The exit status can be as follows:

       Key      Value   Meaning
       JSUCC    0       Successful
       JFAIL    1, 32   Failed - retry later
       JABORT   2, 33   Abort - terminate queue processing
       JREMOVE  3, 34   Failed - remove job
       JHOLD    6, 37   Failed - hold this job
       Other            Abort - terminate queue processing

9. If the filter exit status was JSUCC (0), or no error indicated, then processing will continue otherwise the job termination takes (see Abnormal Termination).
10. If the `of` filter is present, then it is reactivated with a `kill -CONT` signal.
11. If the `sf` (suppress FF print file separators ) is false, then the `ff` (form feed) string will be interpreted and sent to the of_fd.

### Printing Banner At End

Options used:

- `hl` Header (Banner) Last

The actions taken in this step are identical to those for the Printing Banner At Beginning, with the exception that the `be` (end banner program) is used in the procedure rather than the `bs` (start banner program).

### Normal Termination

Options used:

- `fq` Form Feed on Close
- `la` Local Printer Accounting
- `tr=`Trailer on Close
- `ae=`Accounting at end
- `save_when_done` Save when done

Sequence of Operations:

1. If we are printing and the `fq` flag is set and the `sf` (suppress interfile FF) flag is set, then the `ff` (form feed) string will be interpreted and sent to the of_fd.
2. If we are printing, the `tr` (trailer) string will be interpreted and sent to the of_fd.
3. If printing and the `la` (local printer accounting) flag is set or transferring a job and the `ar` (remote accounting) flag is set, the `ae` is examined and accounting is done as described for the `as field.`
4. If the `of` filter is present, its STDIN is closed, and the `lpd` server waits for it to exit. The exit status is used as described above.
5. The device (io_fd) is closed.
6. The job is marked as completed in the spool queue.
7. If the `save_when_done` flag is not specified, the job is removed.

### Abnormal Termination

Options used:

- `mail_from=`Mail from user name
- `mail_operator_on_error=`Mail to operator on error
- `rt#` Maximum Print or Transfer Attempts
- `send_try#` (alias for `rt`
- `save_on_error` Do not delete on error
- `send_failure_action=`Action on Failure
- `sendmail=`sendmail path name and options
- `stop_on_abort` Stop processing queue on filter abort

If the job processing terminates abnormally, the following sequence of events occurs:

1. The job is marked as having an error during processing.
2. The LPD server will attempt to kill all filters and other associated process by using a sequence of `kill -INT`, `kill -QUIT`, and finally `kill -KILL` operations.
3. If there is a `mail_operator_on_error` value, the specified operator will be mailed an error indication. The `sendmail` option specifies the pathname of the sendmail program and the options needed to have it read mail addresses from its standard input. For example, `sendmail=/usr/sbin/sendmail -oi -t` is a commonly used set of options.
4. The `mail_from` value specifies the user name used for mail origination. If not specified, the default is to use the print spool queue or printer name.
5. If there is a `send_failure_action` specified, then it is decoded and the corresponding action taken. If the value is `remove`, `hold`, `abort`, or `retry`, then the job is removed, held, aborted, or retried. If the value is `|/program`, the program is executed and the number of attempts are written to the filter STDIN. The exit status of the filter will be used to determine the consequent actions. That is, JSUCC (0) will be success, and the standard success action will be taken; JFAIL will cause retry, JREMOVE will cause the job to be removed, JHOLD will cause the job to be held, JABORT or other status will abort processing.
6. If the status is ABORT and the `stop_on_abort` flag is set, then further processing of jobs is terminated. The job is not removed from the queue.
7. If the error status indicates removal, and the `save_on_error` flag is clear then the job is removed from the spool queue.
8. If the error status indicates that no further operations should be performed on the queue, then the `lpd` server will stop processing jobs.
9. If the error code indicated that the job should be retried, and the `rt` value is 0 or the number of attempts is less than the `rt` value, then the job is retried. Between each attempt to transfer a job to a remote site. This pause will double after each attempt, reaching a maximum of `max_connect_interval` seconds. If `max_connect_interval` is 0, there is no limit on the interval value.

### LPD Spool Queue Processing

Options used:

- `lpd_force_poll=`Force LPD to periodically poll print queues
- `lpd_poll_time#`Time between polls
- `max_servers_active#`Maximum number of active servers

When the `lpd` server starts, it will fork a set of subserver processes, each which will handle an individual queue.

If a system has a large number of queues, then this forking operation may result in the `lpd` server exhausting the process resources. To control this, the `max_servers_active` value restricts the number of active children to the specified value. If this value is 0, then 50% of the maximum system processes value will be used.

Due to the limits on the number of processes, there may be times when a job is placed in a queue, but the `lpd` server is unable to start handling the job. When all of the children of the main `lpd` server have exited, the server starts a timer. After `lpd_poll_time` seconds, it will scan the queues, looking for jobs to process, and starts a process to service them. If it does not find any jobs it remains idle.

The `lpd_force_poll` flag causes the server to periodically poll the queues. This is useful when there is a high possibility that jobs could fail to be printed due to high loads on the server.

## 6.7 Filter Command Line Flags

Options used:

- `bk_filter_options=`Backwards Compatible Filter options
- `bk_of_filter_options=`Backwards Compatible OF Filter options
- `bkf` Backwards Compatible Filters
- `filter_ld_path=`Filter LD_LIBRARY_PATH environment
- `filter_options=`Filter options
- `filter_path=`Filter PATH environment
- `of_filter_options=`OF Filter options
- `pass_env=`Environment variables to copy to Filter environment
- `pl#`line count for page
- `pw#`column count for page
- `px#`pixel width for page
- `py#`pixel length for page

A filter (or program) specification in the LPRng printcap database usually has the form:

    :option=| [flags] /path [arguments]
    :option=[flags] /path [arguments]

The first case is used where the option value can be a string or filter, and the second where a program is always expected. The following procedure is used to run a filter program.

The sequence of operations to run a filter is as follows:

1. The program must be specified with an absolute path name.
2. By default, the program is run as the user if invoked from a client program such as `lpr`, `lpc`, etc. If invoked from `lpd`, it is run as the `server_user` user (default `daemon`) configuration entry.
3. The flags control how the program is to be run. The following flags are supported:
   - ROOT This opens a horrible security loophole, as it will run the program as ROOT. To enable this option, you must set various compilation flags, and perform other arcane operations. This is deliberately done to make administrators read the warnings and admonitions.

     The alternative to ROOT is to have a setuid ROOT executable. Under NO circumstances should you run a shell script setuid ROOT, with general execute permissions on it.

   - -$ This very odd looking flag is used to suppress the addition of additional command line arguments specified by the value of `filter_options` to the program command line.
4. If the -$ flag is not specified, the arguments determined by the value of the `bkf` (Berkeley LPD filter compatible flag) flag are added to the filter command line. If `bkf` is false the `filter_options` are added for OF filters and `of_filter_options` are added for non-OF filters; if it is true, then the `bk_filter_options` and `bk_of_filter_options` are added for OF and non-OF filters respectively.

   Option | DefaultValue
   `filter_options` | $C $F $H $J $L $P $Q $R $Z $a $c $d $e $f $h $i $j $k $l $n $p$r $s $w $x $y $-a
   `of_filter_options` | (same as `filter_options`)
   `bk_filter_options` | $P $w $l $x $y $F $c $L $i $J $C $0n $0h $-a
   `bk_of_filter_options` | $w $l $x $y

5. By default, for programs that are not being invoked as print job file filters, the `filter_options` arguments are added. For print job filters, if the `bkf` flag is set, then the `bk_filter_options` and `bk_of_filter_options` entries are used. The default `bk` filter options are the same as originally used with the BSD LPR filters. For the `of` filter, either the `of_filter_options` or `bk_of_filter_options` arguments will be added.
6. The program arguments will then be scanned and interpreted. Arguments of the form `$`letter will be translated into values from the print job control file and/or printcap entry. The letters have the following meaning:

   Letter | TranslatedValue
   `a` | printcap `af` (accounting file name)
   `b` | job size (in K bytes)
   `c` | binary file (`l` format for print file)
   `d` | printcap `cd` or `sd` entry
   `e` | print job data file name (currently being processed)
   `f` | print job original name when spooled for printing (N info from control file)
   `h` | print job originating host (H info from control file)
   `i` | indent request (I info from control file)
   `j` | job number in spool queue
   `k` | print job control file name
   `l` | printcap `pl` (page length)
   `m` | printcap `co`
   `n` | user name (L info from control file)
   `p` | remote printer (when processing for bounce queue)
   `r` | remote host (when processing for bounce queue)
   `s` | printcap `sf` (status file)
   `t` | time in common UNIX format
   `w` | printcap `pw` (page width)
   `x` | printcap `px` (page x dimension)
   `y` | printcap `py` (page y dimension)
   `F` | print file format
   `P` | printer name
   `S` | printcap `cm` (comment field)
   Capital letter | Corresponding line from control file
   {key} | printcap value for `key`

7. If there is no value for the specified argument, then the argument is removed from the list. If there is a value, the actual form of the substitution is controlled by additional flags as follows.

   Form | TranslatedValue
   `$x` | `'-xvalue'`
   `$-x` | `'value'`
   `$0x` | `-x 'value'`
   `$'x` | `-x value`

   Each entry in quotes is treated as a single value, as in /bin/sh. The `$'x` does not quote the value. Combinations of the various flags are allowed. For example, `$-x` would simply substitute the value for `x`, and then pass the whitespace separated components as individual arguments. This last form is useful for adding in additional flags on the command line.

8. The command line is parsed, metacharacters are ruthlessly stripped from all arguments and pathnames and replaced by `_` (underscores), and an argument list suitable for the `execve` system call is formed.
9. A sanitized environment is set up for the program execution, with the following environment variables.

   `USER` | User name (client only)
   `LOGNAME` | L control file info
   `HOME` | Home directory (client only)
   `LOGDIR` | Home directory (client only)
   `PATH` | `filter_path` configuration information
   `LD_LIBRARY_PATH` | `filter_ld_path` configuration information
   `SHELL` | `/bin/sh`
   `IFS` | `" \t"`
   `TZ` | Time zone
   `SPOOL_DIR` | `sd` printcap info
   `CONTROL_DIR` | `cd` printcap info
   `PRINTCAP_ENTRY` | printcap info
   `CONTROL` | control file

10. If the filter is to be run by a client program such as `lpr`, then the environment variables specified by the `pass_env` configuration or printcap option will be extracted from the environment, have any metacharacters removed, and then placed in the environment variable list. Commonly, the `PGPPASS` and `PGPPATH` are specified.
11. The program is started, with STDIN, STDOUT, and STDERR attached to the appropriate files or file descriptors. If none is specified, then they are attached to `/dev/null`.

## 6.8 Bounce queues and filters: caveats

There are a few situations in which a filter of a bounce queue will behave differently from an ordinary queue.

## 6.9 The lpr -p format and :pr filter

The `-p` format doesn't behave as expected. Instead of running `pr | if`, the daemon will try to call the `:pf` filter.

After filtering, the file might be of a different type than before. Since the result is transfered to another print service (which might do its own filtering), it is important that the right file type (by means of the print format) is passed on to the second queue.

Use the `translate_format =oNoN...` printcap option. Its value takes the form of old/new pairs of formats. For example:

    translate_format=pf

The `-p` format file will now be renamed with the `f` format.

## 6.10 LPRng Supported Filters

There already exists a large library of ready-to-use filters. Some of them have LPRng-specific versions, which can be found at the LPRng ftp mirror sites.

### Filter Distribution Conventions

By convention, most filters are either totally standalone (very rare), or require a set of support files. There are two types of support files: per print queue configuration information and global support information.

Since a print filter will execute with the current directory set to the spool queue directory, most filters expect that per print queue configuration information should be kept in the spool directory. Most vintage filters insist on having these files hidden with names such as `.setup`. This can make it difficult for administrators to determine where the configuration files are.

It is strongly recommended that filters and information be placed in commonly accessible directories such as `/usr/local/lib/filters`, and the executables in subdirectories. This allows the LPRng administrator to set the privileges on these directories such that only the `lpd` process can access them.

Most of the LPRng supported filters can either be used as a `if` or `of` filter. The filter will examine the format type passed by the `-FX` command line argument, and if it is `o` it will perform as an `of` filter.

Alternatively, the filter will check the filename in the pathname by which is was invoked. If the name has the substring `of` in the filename, then it assumes it is to act as an `of` filter. This allows symbolic links to be made to a common filter executable, each of which corresponds to the filter name by which it is to be invoked.

When a filter is invoked, it is passed a large number of options, many of which are totally ignored in filter operation. However, for many purposes it is necessary to provide options to the filters to tailor their operation to the particular spool queue needs.

By convention, all LPRng supported filters use the

    -Tkey=value[,key=value]

convention for specifying filter configuration option values.

### lpf

Source code: LPRng Distribution

This filter is distributed as part of the LPRng source code, and has a very limited functionality. By default, it only translates `\n` to `\r\n` sequences, and detects the OF Filter Stop sequence when invoked as an OF filter.

- Options:
  `-Tcrlf` - suppress `\n` to `\r\n` translation

## 6.11 CTI-ifhp ifhp Filters

Source code: LPRng Distribution, CTI-ifhp-<em>version</em>.tgz

This filter supports a wide variety of Hewlett-Packard printers, or to be more specific, printers which support the Hewlett-Packard PCL and/or PJL languages. In addition, they try to detect PostScript jobs and send the correct commands to the printers to enable PostScript rather than PJL operation.

This filter was originally developed by Panos Dimakopoulos, Systems Programmer, of the CTI-Print project at the Division of Computing Facilities of the Computer Technology Institute (CTI), Patras, Greece. The code has been heavily modified by Patrick Powell `<papowell@astart.com>` to support newer versions of HP Printers. It is intended to replace the HPJetDirect drivers supplied by Hewlett-Packard.

### Printer Capabilities

As explained in Setting Up Your Printer, you can have a parallel (unidirectional), serial (bidirectional), or network (bidirectional) connection. When using a bidirectional connection, you can sometime obtain or gratuitously receive error and/or status information from the printer.

Some printers will spontaneously generate error messages when printing a job on a bidirectional interface. Usually, though, it it necessary to force the printer to provide status in a reasonable format.

Some printers have the capability of printing either PCL or PostScript; some require special setup commands and some will autosense which type of job is being printed.

If you are printing text, and not using a Page Description Language like PostScript or PCL, then you may want to download a font to the printer. This is especially the case when you are trying to print text files in a non-English font.

Some printers will provide a hardware page counter value when requested; however, the means of requesting differ from model to model.

Sometimes you want to generate a special banner for a particular printer, and need to put in some dynamic information. While this can be done by the `lpd` server using the `bp` program specification, it turns out that non-LPRng systems which want to use the `ifhp` want to have the same facilities. Thus, you need to have some way to get the same effect as the `bp` option, but at the filter level.

Having done `lpd` banner generation and printing, why not have the filter run an accounting script as well?

At this point, I suspect that the reader is beginning to suspect that making a general purpose filter to support all of these possibilities is difficult. That is incorrect. It is extremely difficult.

### hpif Options

These options are specified by the `-Tkey=value [key=value]*` on the command line.

Option | Purpose
`accounting=accounting_script_pathname` | Invoke the accounting script with a subset of theoptions passed to the filter. In addition, the-bpagecount option indicates the number of pagesprinted for the job.
`autodetect=[on|off*]` | The printer has or does not have job type autode-tect capability. Do not download fonts or try todetermine job type if autodetect is on.
`banner=[on|off*]` | If banner is on, then the ifhp filter will attemptto print a banner using information passed on thecommand line or on the standard input. The titleoption can be used to specify additional titleinformation on the banner. See BANNERS below fordetails.
`cartridge=[*on|off]` | (Alias for postscript)If cartridge is on, the printer has PostScript sup-port. The filter will try to determine if a job ispostscript and send Printer Job Language commandsto put the printer in PostScript mode.
`debug=debuglevel` | Sets the debugging level; 2 is the default; alarger number causes more verbose error messages.
`defaultfont=fontname` | Sets the default font to be downloaded; default isNONE.
`dev=/device or dev=host%port` | Open the specified device or connection to remotehost; by default ifhp filter uses file descriptor 1(stdout). If the optional orig_port is specified,connections will be originated from this port.Some printers require that connections originatefrom a port in the range 1-1024.
`infostatus=[*on|off]` | The PJL INFOSTATUS request is not supported on someHP printers. Use this to turn the status requestoff. Note that you cannot get real time reports ofthe printer status if you do this. This will alsosuppress getting pagecount information using thePJL facilities.
`forcepagecount=[on|off*]` | If you have a printer that has PostScript pagecount information support, you can set infostatusto OFF and forcepagecount to ON. This will causethe PostScript facility to be used. If you setcartridge or postscript to OFF then this will notbe done.
`logall` | Save all of the error and information messages fromthe printer in the log file. This is useful whenyou wish to examine returned status from theprinter.
`model=(C5M|III|IIID|IIISi|IV*)` | The model of HP printer. C5M is Color 5M, III isHP LaserJet 3, IIID is HP LaserJet 3D, etc. Addi-tional printers may be added or defined at varioustimes - please consult the source for details.This selects various timing and format characteristic-tics. This is a desperation parameter for userswith antique or non-conforming PJL based equipment;read the source code for details on the particularpeculiarities.
`pagecount=[on*|off]` | Get the hardware pagecounter value for accounting.Some printers such as the HP LJ4s do not have hard-ware support for pagecounters, and return bogusnumbers. Use this to suppress attempting to getvalid information. If your printer does supportPostScript, then you can get the page count valueusing PostScript by setting forcepagecount to ON.
`plp=[on|off*]` | Return PLP status values on exit; by default LPRngstatus values are returned.
`postscript=[on*|off]` | The printer has postscript support.
`quiet=[on|off*]` | If set, do not report common status messages.
`retries=count` | The number of times to retry connecting to theprinter.
`sleep=time` | The number of seconds to wait before trying to con-nect to the printer.
`status=[*on|off]` | When on, the printer is treated as a write onlydevice and is not queried for pagecount and statusinformation. Set status=OFF for parallel printers.If status is OFF, then the ifhp filter simply addsjob control language headers, fonts, and trailersto the jobs.
`stty=stty flags` | if the output device is a serial line, set the linecharacters according to the stty flags. Theseflags are (most likely) identical to those avail-able with the stty(1) command on the host system.
`summary=[filename|host%port]` | This option specifies that summary or informationalmessages should be placed in the specified file orsent, using the UDP protocol, to the indicated hostand port address. This allows remote monitoring ofthe printing and error activity. The undocumentedprogram included with the filter distribution is asimple program that can be used to perform the mon-itoring.
`sync=[*on|off]` | Try to synchronize communications with printer.This will ensure that the printer has been reset,and no problems involving the previous job willresult.
`tbcp=[on|*off]` | When invoked as an IF filter and transferring aPostScript job, the filter will use the AdobeTagged Binary Communications protocol. This allowsbinary data to be transferred and not interpretedas control information.
`title=line[/line]*` | The title information is printed on the bannerpage; it consists of a list of / separated lineswhich are added to the banner information.
`wrap=[on|off*]` | enables or disables line wrapping in PCLmode.

### Parallel Port Printer

On a parallel port printer, you cannot get status, or do much besides set up the printer to either handle PostScript or do autosensee. The following is a typical printcap entry:

    pr:.... options
      :of=/usr/local/lib/filters/ifhp -Tstatus=off
      :if=/usr/local/lib/filters/ifhp -Tstatus=off

You might want to also look at the `autodetect` or `postscript` options.

### Printing Banners

By default, the `ifhp` filter when used as an OF filter will interpret the first line to it as a short banner line, and use the information on this line to produce a PCL based banner. The short banner line should have the format:

class:username `Job:` jobinfo `Date:` dateformat

    Example:
      A:papowell Job: (stdin) Date: Sun Dec 14 07:13:34 PST 1997

This is produced by the default short banner line option value:

    bl=$-'C:$-'n Job: $-'J Date: $-'t

If you want to suppress banner printing, then you need to suppress generation of this short banner line. If you want to have the `lpd` program to generate the default long special banner, then you need to suppress `ifhp` from interpreting the information sent to is as banner information. Finally, you may want to have `lpd` invoke the `bp` (banner program) and have its output used as the banner. Here are the various possible ways:

    # no banner at all, use :sh: - suppress headers
    lp:....
      :sh
      :of=/usr/local/lib/filters/ifhp
    # have ifhp generate banner from short banner input
    lp:....
      :sb
      :of=/usr/local/lib/filters/ifhp
    # have LPD generate long banner, have of filter pass it
    lp:...
      :sb@
      :of=/usr/local/lib/filters/ifhp -Tbanner=off
    # have LPD invoke bp banner generation program, have of filter pass it
    # bp programs require short banner on STDIN to work, so we need to
    # generate short banner
    lp:...
      :sb
      :bp=/usr/local/lib/filters/banner_program
      :of=/usr/local/lib/filters/ifhp -Tbanner=off

The `ifhp` banner is generated in PCL, and uses the minimum PCL facilities. Since when you send a banner to an autosensing printer you cause it to enter the requested mode, the `if` filter (`ifhp`) will need to reset the printer to autosense mode. The `ifhp` filter automatically does this.

If you want very fancy banners, the `banner.sh` (PCL) and `psbanner.sh` (PostScript) banner generating programs in the CTI-ifhp distribution make a good starting point.

### Error Logging

Error logging and reporting is done by the `ifhp` filter as follows.

1. Messages are produced by the actions of the `ifhp` software. This are logged to the STDERR output of the filter.
2. Messages are produced by status returned from the printer, when the `-Tstatus=on` (default) option is enabled. These are classified according to the Hewlett-Packard Printer Job Language error status definitions, and logged to the STDERR output of the filter.
3. In addition to error messages, ongoing status messages are also produced. If the printcap entry has a `ps=`statusfile entry and the statusfile exists and is writeable, then the error and status messages will be written to the log file.
4. If the message concerns a serious matter or has been returned from the printer as an 'ALERT' in it, then the message can also be sent to a 'summaryfile'. This file can be either a file OR a UDP socket on a host. This is specified with the `-Tsummary=`summaryfile option. For example,

               ifhp -Tsummary=taco%3000

   would send messages to UDP port 3000 on host taco.
5. If you do not want the filter to report status on its STDERR output, use the `-Tquiet` option to suppress this, or compile it with the -DQUIET option.

### Accounting Information

Doing printer accounting is not simple. Read LPRng Accounting for more information.

In order to help aid in accounting, by default the `ifhp` filter will query the printer to get the current value of the hardware page counter value, if there is such a thing on the printer. Unfortunately, due to different types of printers and errors in their PJL, PCL, and PostScript implementations, several different methods need to be used.

1. Only a printer with a bidirectional port will return status, so you need to have a bidirectional connection.
2. If the printer is still printing a job, then getting the value of the hardware page counter will be useless; you need to wait until the printer is idle, i.e. - synchronize your operations with the printer.

   Unfortunately, some printers return an `idle` indication even when they are printing pages of the previous job. This means that the printer has to be polled, and only when it is idle and the pagecounter value has been stable for a reasonable time (5 seconds?) can you trust the page counter value. This slows down job printing very seriously.

   Some of the newer PJL printers have a `PJL TEOJ`, or return end of job indication when the last page of a job has been printed. If you have this capability, you can speed up printing.

3. If your printer supports Hewlett-Packard Printer Job Language `PJL INFO PAGECOUNT` facility, then it will first be tried to get the page count.
4. If your printer does not return pagecount information using the PJL facility and it has PostScript support (default), then a small PostScript job will be sent to the printer requesting the systemdict pagecounter value. Unfortunately, different implementations and versions of PostScript will need different programs. The PostScript Printer Definition file for the printer will have the correct script that is needed. The default script that is used is:

        /ps { print flush } def
        (\tPAGECOUNT ) ps
        statusdict begin pagecount end == flush

5. To confuse matters totally, some printers which can do PostScript interpretation do not support PJL PAGECOUNT reporting. You can use the PostScript method to get the pagecount information, but you cannot get status.
6. The pagecounter information is obtained at the start and end of processing a job, and is printed in the accounting file and also on File Descriptor 3 (if it is open). This information has the format:

         start -ppagecounter -Ff -kjob -uuser -hhost -R...
         end  -ppages -qpagecounter -Ff -kjob -uuser -hhost -R...

   When we use the OF filter and/or banners, we will see the individual jobs bracketed by the OF filter records:

           start -p100 -Fo -kcfA100taco -uuser -hhost -R...
           start -p101 -Ff -kcfA100taco -uuser -hhost -R...
           end  -p1 -q102 -Ff -kcfA100taco -uuser -hhost -R...
           start -p102 -Ff -kcfA100taco -uuser -hhost -R...

   We can use the various job numbers and other information to track page usage.

The following are a selected set of printcap entries that can be used to get page counting information:

    # use defaults, try to get pagecount using all methods, wait for stable
    # value of pagecount before proceeding
    pr:...
      :of=/usr/local/lib/filter/ifhp
    # printer support PJL True End of Job and PAGECOUNT
    pr:...
      :of=/usr/local/lib/filter/ifhp -Ttrue_eoj=on
    # no PJL INFO status available, but you can get page count using postscript
    pr:...
      :of=/usr/local/lib/filter/ifhp -Tinfostatus=off,forcepagecount

You should try connecting to your printer directly and testing the accounting facilities. You can do this by using the `ifhp -Tdev=...` facility. For example:

    ifhp '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
    -evenp ixon pass8 -ixany cbreak' -Tdebug=5 <ellipse.ps
    ifhp -Tdev=astart14%9100 -Tdebug=5 -Ttrue_eoj <ellipse.ps

## 6.12 psfilter PostScript Printer Filter

Source code: LPRng Distribution, psfilter-<version>.tgz

The `psfilter` is similar to the CTI-ifhp filter in its operation and functionality. Its general operation and procedures are similar, but it has the following additions:

1. It will translate text jobs to PostScript and print them using PostScript unless the jobs are literal or binary formats.
2. It will use the PostScript serial line status query support to obtain status and other error information.
3. When you are sending a PostScript file with binary information, you need to either suppress status gathering, or you need to use the Transparent Binary Communications Protocol. Note that older PostScript printers do not support this.

### IF and OF Filter Support

During the installation process, `psif` and `psof` symbolic links are made to the `psfilter` executable. Thus, the following printcap entries can be used:

    # common use:
    lp:...
      :of=/usr/local/lib/filters/psof
      :if=/usr/local/lib/filters/psif
    # same effect:
    lp:...
      :of=/usr/local/lib/filters/psfilter
      :if=/usr/local/lib/filters/psfilter

### Options

The `-Tkey=value[,key=value]` convention is used to pass options to the psfilter. The following options are supported. Many of these are identical in intent to CTI-ifhp options.

Option | Purpose
`accounting=/pathname_to_executable` | When the -Taccounting is specified, the specifiedprogram is executed with the same options as thefilter.
`debug=level` | set debugging level. The default level is 2;higher values produce more verbose output.
`dev=/device` | dev=host%portOpen a connection to either the device or to theport on the host and send output to it. Bydefault, output is send to stdout.
`endpause=delay` | When used as the OF filter, at the start and end ofthe job psfilter will query the printer for thevalue of the hardware pagecounter. The differencebetween the start and end values is reported as thenumber of pages used for the job. Unfortunately,some printers such as the HP LaserJet 4 and HPLaserJet 5 report the current value of the pagecounter at the time of request, and it is difficultto determine if the last page has been completelyprinted by examining returned PostScript status.Before querying the printer at the end of a job,the psfilter software will wait endpause seconds(default is 5). On a 12 page per minute printer,this appears to be sufficient to allow the lastpage to be completely printed. However, if theprinter jams on the last page, it will still not bereported and the page count will be off by one.
`forcepagecount=[off*|on]` | To find the pagecount value, a PostScript programis sent to the printer. This overrides any otherflags and forces the program to be sent. Usuallyused in combination with -Tstatus=off -Tforceps,and -Tnosync flags. See the comments below forprinter specific problems.
`forceps=[off*|on]` | Forces a dummy PostScript job to be sent at thestart of operations. This usually tricks autosense-ing printers into going into PostScript mode andresponding to the PostScript conventions. See thecomments below for printer specific problems.
`maxresponse=seconds(default30seconds)` | Specifies how long to wait for a status responsebefore giving an error indication.
`nosync=[off*|on]` | Suppresses trying to get status from the PostScriptprinter, as would normally be returned by sending a^T. See the comments below for printer specificproblems.
`pagecount=[off|on*]` | Get the pagecounter value from the printer and usefor accounting information. Note that some HPprinters (LJ4s) do not appear to have pagecounters.(Default is on).
`reverse=[off*|on]` | Reverse page order on output.
`status=[off|on*]` | Query the print device for status and page countinformation. If status is off, then psfilter sim-ply formats input into postscript and does not doaccounting.
`stty=stty_options` | If the output device is a serial port, set the con-figuration according to the stty options. Theseoptions should be identical to those used bystty(1).
`summary=destination` | Write a one line status summary to the destination.The destination can be a file (i.e. - /tmp/status)or a UDP port on a host (host%port). The host namecan be an hostname or an IP address (i.e.-info.sdsu.edu%2000 or 130.191.163.56%1000).
`tbcp=[off*|on]` | Use the Adobe Tagged Binary Communications protocolto send the document when invoked as an IF filter.The OF filter cannot handle binary PostScript docu-ments as it will remove various control sequences.The -Ztbcp LPRng option will also enable TBCP oper-ations.
`udp_status_port=host%port` | The HP5m and possibly other printers use port 9101(UDP) to request and send status. When sent a UDPpacket on this port, the printer responds by sendingstatus back to the sender. This option forces theuse of this port for sending status. However, pagecounts and other information still use the standardconnection to the printer.

### Accounting

The `psfilter` uses the same methods for doing accounting as the `CTI-ifhp` filter. See IFHP Accounting for details.

Consult your printer's PostScript Printer Description file to determine the PostScript script needed to do accounting. You may need to modify the default one supplied in the `psfilter` code.

Always test that the printer returns the right accounting information using a test similar to the following:

    psfilter -Tdebug=8 '-Tdev=/dev/ttyb,stty=38400 -echo -crmod -raw -oddp \
    -evenp ixon pass8 -ixany cbreak' <ellipse.ps
    psfilter -Tdebug=8 -Tdev=astart14%9100 -Ttrue_eoj <ellipse.ps

## 6.13 lp_pipe Filters

Source code: LPRng Distribution, part of FILTERS_LPRng-<version>.tgz

The `lp_pipe` family of filters was developed to act as a network pipe to network devices. They are largely replaced by the `lp=host%port`, facility.

- `tcp-pipe`: uses a tcp socket (OBSOLETED by `lp=host%port`), but good starting point if you have special device requirements;
- `annex-pipe`: supports annex terminal server.

## 6.14 apsfilter Filter

Source code: LPRng Distribution, apsfilter-<version>.tgz

The `apsfilter` is basically a simple front end to the `a2ps` program (See: http://www-inf.enst.fr/~demaille/a2ps/ for details), and is an example of a MagicFilter that has powerful processing capability. The `apsfilter` program sets up options for the `a2ps` program and then invokes it.

The `a2ps` program can convert just about any type of file into PostScript, and then by using the GhostScript facility can convert this to the output compatible with a particular printer.

Combined with the LPRng `qq` and `force_queuename` options, we can set up virtual queues that do various types of reformatting. Here is a sample set of printcap entries:

    # seen by users - note that the queue name is put into control file,
    #  and we then send it to the frontend@host queue for processing
    raw:qq:lp=frontend@host
    twoup:qq:lp=frontend@host
    landscape:qq:lp=frontend@host
    frontend:lp=frontend@host:force_queuename=raw
    # frontend does the job conversions and accounting
    frontend:server
      :lp=/dev/lp:force_queuename=raw
      :if=/usr/local/lib/filter/apsfilter

## 6.15 Using your own filters

If you already have a working setup, with its own specific filter programs, you might want to keep them. Or, you might want to write a set of your own.

See the source code in the LPRng Distribution, FILTERS_LPRng-<version>.tgz files for examples.

Next Previous Contents