Table of Contents
Abstract
This document provides information of interest to those wishing to modify the PPR source code or to add plugin modules such as printer interface programs and programs that send messages to users.
This section describes how the various components of PPR work together.
All jobs which enter the queue pass through the program called ppr. This is true even of those which pass first through the Macintosh print server, the LPR compatible server, or Samba. (These additional server components will be described later.) The program ppr reads a PostScript file and creates several files which contain the text of the job and information indexes of the job text.. These files are placed in a spool directory and an additional file, called the queue file, is created which describes the job and the options the user has selected. Once all the files are ready, ppr sends a message to the print daemon pprd telling it that a new job is in the queue. This process is illustrated in figure Figure 1.
When pprd determines that the time has come to print a file on a printer, it invokes a program called pprdrv which reads the 3 job files and reasembles them into a PostScript file. While doing so it will consult information read from the printer configuration file and the queue file and a PostScript Printer Description (PPD) file. It may also include fonts and other resources in the data stream. This process is illustrated in figure Figure 2. (The inclusion of fonts and other resources has been ommited from the figure for the sake of simplicity.)
The program pprdrv sends the file to the printer by invoking an interface program. It is the responsibility of the interface program to accept the print job on standard input, send it the printer, if possible receive any error messages, and pass them back to pprdrv by writing them to stdout.
Notice in figure Figure 3 that printing is taking place on PRINTER 1 and PRINTER 3.
PRINTER 2 is idle, so the processes necessary to print on it are shown with dotted line boxes, indicating that they do not exist at present.
Once the job has been printed, the interfaces program exits, after which pprdrv exits. The main spooling daemon, pprd detects this and removes the job from the queue and attempts to inform the user that the job is done.
When exiting, pprdrv will return an exit code which indicates to pprd whether printing was successful or not. If the exit code indicates that a problem occured which was caused by the particular job being printed, a PostScript error for instance, then the job is placed in the "arrested" state, the user who submitted it is informed, and pprd looks for another job to print on that printer. The arrested state is a special case of the "held" state. An arrested job remains in that state until it is canceled or released.
When a job is arrested, the user who submitted it is informed, however if the arrested job is subsequently deleted by an operator, the user will not be informed as he would be if a normal job of his were canceled.
If pprdrv indicates that the problem is with the printer, then the job is returned to the queue, the printer is placed in fault mode, and pprd looks for another printer on which to print the job. When pprdrv discovers a problem with a printer, it indicates to pprd whether or not it is a problem from which the printer is likely to recover without the intervention of the system operator. Problems from which it is likely to recover on its own include those which are transient or which an ordinary user might correct, such as printer-turned-off and out-of-paper. If the problem is one from which the printer may recover, then it is placed in fault-retry mode; if it is not expected to recover, it is placed in fault-no-retry mode.
In fault-retry mode, pprd tries to restart the printer at intervals. The delay before the retry is 10 seconds the first time and increases by 10 seconds after each failure until the interval reaches about 10 minutes, at which point it does not continue to increase. In fault-no-retry mode, the printer is not restarted until a system operator restarts it. An operator may restart a printer with the command ppop start printer.
When pprdrv detects a printer fault, it writes a description of it to a file in the alerts/ directory, a file which is named after the printer. At intervals, pprd may send the contents of this file to a system operator. The interval is defined in the printer configuration file. The interval is expressed as 'every N faults.' If the interval is set to 1, the alerts log is sent after each printer fault; if the interval is set to 5, the alerts log is sent after every 5th printer fault. If the interval is set to 0, the alerts are never sent.
If the alert interval is negative, it is treated differently. First of all, the absolute value is used as the alert interval. Second, the alerts are only sent to the system operator once. Third, a notice is sent to the system operator if the printer successfully prints a job after incurring at least abs(N) alerts.
Each time a fault occurs, the alert message is appended to the alert log. The exception is that if the previous alert occured more than one hour previously, the alert log is truncated to zero length and a new one started. Whether or not the alerts have been dispatched to an operator, the alert log may be read at any time with the command ppop alerts printer.
It is in the printer configuration file that the alert interval is specified, as well as the method by which alerts are to be dispatched and the address to send them to. A typical alert dispatch method (indeed, the only one supported at this time) is mail. This means that the alerts are sent by electronic mail to the indicated address. The alert parameters may be set with the ppad alerts command.
Users on the machine running PPR can submit jobs simply by running the program ppr. But, when the user is on a remote machine connected by a network to the machine with the PPR spooler, things get a little more complicated.
Accepting network jobs requires a daemon process which waits new connexions. When it receives a connexion, it must launch a server process. The server process will accept the print job and run ppr to submit it.
PPR provides a daemon called papd which acts as a Macintosh print server. It accepts connexions from Macintosh clients, answers queries using information in the PPD files, and accepts jobs. Whenever papd accepts a connexion from a Macintosh, it forks a copy of itself to service the connexion. When the child daemon receives a print job, it launches ppr on the end of a pipe to process it.
PPR provides a daemon called lprsrv which can be launched by the internet super daemon, inetd. The daemon lprsrv implements the Berkeley LPD protocol.
The free Unix file and print server Samba has a very flexible printing system. The print command for any given print queue can be specified in Samba's configuration file. The Samba server sends jobs to PPR by writing the job to a temporary file and then invoking ppr. The -U switch should be used to tell ppr to delete the temporary file once it is done reading it. It is advisable to append & to the print command because for very large files which must be filtered, the client will time out before ppr exits. Sample Samba print commands are given in the ppr2samba(8) man page.
Configuration files which the system administrator is expected to modify directly are described within the reference manual. The files described in this section are modified automatically by programs such as ppad. That is why they are described here in the Hacker's Guide.
Each file in the directory /etc/ppr/printers/ represents one printer. It is permissible to create and edit these files manually, however the command ppad has been provided to do this automatically.
A new printer configuration file is created by the ppad interface command. The new configuration file is created by turning the arguments of the ppad interface command into a Interface: and an Address: line and appending the file /etc/ppr/newprn.conf. The newprn.conf file is normally created by the ppad new alerts command.
Any lines begining with ; or # are comments. Blank lines are ignored. All other lines should begin with a keyword which is followed by one or more values. Only the Bin: and PPDOpt: lines should occur more than once. If any other line occurs multiple times, all but the last instance are ignored.
Any line that begins with an unrecognized keyword is ignored. This is so that you can add lines of your own which will be understood and acted on by other programs. The program ppr2samba works on this principle.
This line contains a comment which describes the printer. The comment is displayed when the ppad show command is used to display the printer configuration. This line is optional.
This line is required. It gives the name of the program which the spooler should invoke in order to make contact with the printer. If the interface does not begin with a slash, then it refers to a subdirectory of the /usr/lib/ppr/interfaces/ directory. Common values for this line are atalk, tcpip, and serial.
The address string is passed to the interface program as its second parameter. The proper format for this string depends on the interface. Reasonable values for use with the interfaces named in the above paragraph are Mac Laser Printer: LaserWriter@MYZONE, smith.prn.myorg.org:9100, and /dev/ttys05. If the address contains leading or trailing spaces it should be enclosed in double quotes. Technically, this line is not required, but the default address is the name of the printer which is unlikely to be useful.
This line is optional. The value string is passed to the interface as its third parameter.
This line specifies the name, filename, or complete path of an Adobe Post Script Printer Description file which describes the printer. If the name does not begin with a slash it is looked up in the PPD file index created by ppr-index ppds, if it is not found in the index, then it is presumbed to be the name of a file in the directory /usr/lib/ppr/ppd/. This line may not be required but it really ought to be present.
This line is optional. If it is present, then messages will be sent to the designated person when faults occur on the printer.
The integer interval indicates how often these messages will be sent. For instance, if the value is 5, then a message will be sent for every 5th fault.
The method parameter indicates the method by which the message should be sent. Currently, this parameter is ignored. Set it to mail.
The address indicates the person to whom the message should be sent. Since mail is currently the only supported method, this should be a e-mail address.
This line is optional. The first integer refers to banner pages, the second to trailer pages. Four different values are allowed. They are 0 for 'never', 1 for 'preferably not', 2 for 'preferably yes', and 3 for 'always'.
This line is optional. It indicates whether or not the connexion to the printer allows it to send messages back to the spooler. There is a list of default values for all the interfaces supplied with PPR compiled into pprdrv. (This list is defined in include/interfaces.h.) If this line is not present and the interface is not in the list, it is assumed to be false. Any instances of this line which occur before the Interface: line are ignored.
This line is optional. The integer is the code number of a job break method. This line is only necessary if a job break method other than the default for the interface is desired. Default job break numbers for the interfaces which come with PPR are defined in the source file include/interfaces.h. The meaning of the values in interfaces.h can be understood by reading the ppad jobbreak section of the ppad(8) man page. Any instances of this line which occur before the Interface: line are ignored.
This line is optional. It indicates the range of character codes which the interface can transmit to the printer. The acceptable values are 1 for Clean7Bit, 2 for Clean8Bit, 3 for Binary and 4 for Binary if TBCP is used.
This line is optional. The default value is true. If this parameter is set to false, the printer will refuse to print any job which does not have color in a %%Requirements: line in its header. A GrayOK: line can be used to prevent black-and-white or grayscale jobs from begin printed on an expensive colour printer.
This line is optional. If this line is present, then the printer becomes a protected printer. Each parameter money will normally be a positive number with two decimal places. If both are 0.00, then the printer is protected, but no actual charges are mode to the user's account. The first number is the amount that should be charged for each sheet of paper printed on both sides. The second is the amount that should be charged for each sheet printed on only one side. If the second number is missing it is assumed to be the same as the first.
This line is optional. If it is absent, it is assumed that direction is Normal. If direction is set to Reverse, the spooler will cause the pages to be printed in reverse order, if possible. The value of this line can be set with the command ppad outputorder printer Normal, ppad outputorder printer Reverse, or ppad outputorder printer ppd. The command ppad outputorder printer ppd deletes any OutputOrder: line which may exist.
The binname parameter is the bin name as it appears in a *InputSlot line of the PPD file. Common values are Upper, Lower, and Cassette. If automatic bin selection and media handling are desired, there should be one Bin: line for each installed bin. Removing all the Bin: lines disables automatic bin selection and media handling.
This line is optional. If present, it contains input filter options in the form of name-value pairs. (If the user employs one or more -o switches when submitting a job with ppr, then the argument of each -o switch is appended to this list. Thus, -o switches can be used to override settings in this list.) This line is automatically updated whenever the ppad ppd command is used to select a new PPD file or the ppad ppdopts command is used to change the PPD options. An update may be forced at any time with the ppad deffiltopts command.
This line is optional. If present, it contains a compressed description of the switch settings which were saved with the ppad switchset command.
This line is optional. If present, it contains a space separated list of file types which should be passed directly through to the printer.
This line is optional. If present it indicates the maximum number of pages this printer is allowed to print in a single job. Jobs with more pages than the number indicated are rejected. If the job was submitted to a group, it may be printed on another member of the group. If no printer will print it, then it is arrested.
This line may be edited with the ppad pageslimit command.
This line can be used to enable pair of queries which fetch the printers lifetime page count before and after the printing of the job (exclusive of banner and trailer pages).
The information obtained through these queries can be logged. See printlog(5).
If this line is absent or the integer is 0, then this feature is disabled.
If the value is 1, then then the PostScript code statusdict /pagecount get exec == is used to get the page count. No attempt is made to make sure that the print engine has come to a stop and the printer has updated the count before making the query. On some printers this does not cause a problem because the page count is updated imediately. On others it may be solved by using the pjl or signal/pjl jobbreak method which causes PPR to wait until all of the pages have hit the output bin before considering the job complete.
In the future, additional values for this parameter may be defined. These additional values will use different techniques to obtain the page count.
A printer configuration file may have zero or more of these lines. Each line describes the setting of the options listed in the PPD file. These settings generally describe optional equipment which may be installed in or attatched to the printer such as additional paper trays or duplex attachements. The option is the option name from the PPD file, without the translation string (the part which may follow a slash). The value is one of the possible option values listed in the PPD file, again without the translation string. The description is formed by combining the translation strings for the option and the value. (Option settings and the related terminology are explained in Adobe's PostScript Printer Description File Format Specification.) These lines can be generated automatically with the ppad ppdopts command.
Used by ppr2samba. See the man page ppr2samba(8).
Used by ppr2samba. See the man page ppr2samba(8).
Formerly used by ppr2samba. See the man page ppr2samba(8).
Each file in the /etc/ppr/groups/ directory represents a group. It is permissible to create and edit these files manually, however it is generally more convient to use the command ppad to do it automatically. The Printer: line is the only one which should appear more than once. If any other line occurs more than once, all but the last instance are ignored. Lines with ; or # in the first column are comments. The remaining lines are of two types:
This line contains a comment describing the group. This comment is displayed by the ppad group show command. This line is optional.
The name is the name a printer that should be a member of the group. There should be one line for each group member.
This value indicates if the spooler should attempt to distribute the load evenly among the printers. If it is False, the spooler will always take the first idle printer in the group. If it is True, the spooler will attempt to use each printer in turn. The default is True.
This line is optional. If present, it contains some name-value pairs to be prepended to any the user supplies with ppr's -o switch. This line will be automatically updated by ppad whenever a group member is added or deleted.
If this line is present, it contains a compress representation if the switches saved with the ppad group switchset command.
This line is optional. If present, it contains a space separated list of file types which should be passed directly through to the printer.
Used by ppr2samba. See the man page ppr2samba(8).
This section describes the directory structure found within PPR's home directory (/usr/lib/ppr/), in its spooling directory, (/var/spool/ppr/) and in its configuration directory (/etc/ppr/).
The /var/spool/ppr/queue/ directory contains one file for each print job currently in the queue. Each file name is the same as the queue id of the job is describes. These files are created by the program ppr and removed by the print daemon pprd when the job has been printed or is canceled.
The /var/spool/ppr/jobs/ directory contains three to five files for each job currently in the queue.
The first part of each file name is the queue id of the job. The file whose name ends in -comments contains any header and trailer comment lines which have not been removed and represented by parameters in the file in the queue directory.
The file whose name ends in -pages contains the text of the document default section, if it exists, and a list of the pages in the document. The record for each page includes the offset in the -text file at which it begins and any page header and trailer comments for that page.
The file whose name ends in -text contains most of the text of the job. If the -S true switch was used when ppr was invoked, then resources will be missing from this file, having been stript out and replaced by comments which will later be used to put them back.
The files with names ending in -log contain the print job logs. If ppr was invoked with the -w log switch then any warnings will be in this file. Any text received from the printer, including printer error messages, will also be in this file. Messages which explain why the job was routed away from a particular printer will be in this file, but each line of such messages will begin with a "+". Whenever a banner or trailer page is printed, the contents of the log file is printed and the log file is deleted.
Each time a fault message is generated for a printer, it is appended to a file in this directory. There is one file for each printer. If the file is more than one hour old, the file is truncated to zero length before writing the new alert instead of appending. This ensures that only recent messages will be sent to the operator.
This file may contain one file for each printer. If present, the file contains the last status message received from the printer.
This directory contains log files. Error messages may be written in these log files when PPR component fail. If this directory contains the file printlog, PPR will append a line for each file printed. (See printlog(5).)
This directory contains a number of configuration files. In also contains subdirectories which will be described in later sections.
The purpose of newprn.conf is explained in Appendix Section , “Format of a Printer Configuration File”.
The file smb-include.conf is a segment of Samba configuration file. It is generated by the program ppr2samba.
The file charge_users.db is the user charge accounts database. The program ppuser can be used to read and modify this file.
The documentation for the file lw-messages.conf can be found in lw-messages.conf.sample.
The file media.db is the list of known media types. It is consulted by ppr and pprdrv. It may be read and modified by the ppad media series of commands.
There is one file in this directory for each printer. Each file has the same name as the printer it describes. The format of one of these files is described in Appendix Section , “Format of a Printer Configuration File”.
There is one file in this directory for each group. Each file has the same name as the group it describes. The format of these files is described in Appendix Section , “Format of a Group Configuration File”.
This directory contains one file for each printer. Each file contains a list of a printer's bins and the medium mounted on each bin. This file is created by the main print daemon pprd. When pprd starts up it looks in this directory to determine what media was mounted last time it was running. It automatically re-mounts all those media.
The program pprdrv also uses this file in order to select bins for printing banner pages and to automatically select bins for print jobs according to their required media.
This directory contains the program ppr which submits jobs, various utility programs such as ppop and ppad, and the PPR daemons pprd and papd.
This directory contains pprdrv and as well as other programs a user or administrator wouldn't normally execute directly. It also contains some configuration files which even a system administrator wouldn't ordinarily modify. These files would be modified by people who are creating new components for PPR.
The documentation for the file file lw-messages.conf is in the file itself.
The file mfmodes.conf is used by ppad when determining the default filter options for a printer or group. The format of this file is described in Section ???.
The file fontsub.conf is used to find substitutes for missing fonts. It is described in Section , “Font Substitution Configuration File”.
Each file in this directory is a program which is responsible for making contact with any printer which has a particular kind of interface. For instance, interfaces/atalk is used to communicate with printers connected through Apple's Printer Access Protocol. If you would like to write a printer interface program, see section Section , “Printer Interface Programs”.
Each file in the responders/ directory is a program which can be used to attempt to send a message to the user who submitted the job. A responder program is selected at the time the user invokes ppr, by using the -m switch. The parameter for the -m switch is the name of the program in the responders/ directory which should be used. If the -m switch is not used, the default responder, write, is used. (You can change the default by setting the environment variable PPR_RESPONDER.) The manner in which a responder is invoked is described in Appendix Section , “Requirements for a Responder”.
The programs in this directory are filters which convert various file formats to PostScript. If you would like to write a new filter, see section Section , “Input File Filtering”.
This directory contains skeletal scripts which could potentially be installed in /usr/lib/ppr/filters/ by the command ppd-index filters.
This directory contains the PostScript resources distributed with PPR. They are segregated in subdirectories by resource type. Many of the input filters distributed with PPR use resources in the encodings/ and procset/ subdirectories. The filters don't use these files directly. Instead, they insert %%IncludeResource: in their output so that pprdrv will insert the contents of the desired file.
This directory contains fonts and font metric files which are distributed with PPR. Formerly, the font files were stored in /usr/share/ppr/cache/font/, but the font index mechanism made this unnecessary.
This directory contains the PPR man pages. Packagers may choose to relocate this files to the system-wide man page directory. If not, one must either use the ppdoc command to view them or add this directories to one's MANPATH.
This is the default location for PPD files. It contains a collection of PPD files for common printers. Most of these were provided by the manufacturers or by Adobe[1] but a few are part of PPR.
This directory contains GNU Gettext message catalogs. These catalogs are used to display program messages in languages other than English. If you would like to add messages for your language, see the file README.txt in the source code directory po/.
This section provides the information you will need to write a PPR printer interface program. An interface is a program, possibly a shell script, which receives the text of the print job from pprdrv and sends it to the printer. The interface program will receive the print job text on stdin.
The interface program should should print any data received from the printer on stdout which will cause them to be sent to pprdrv for analysis. The interface program may print additional specially formatted messages on stdout in order to inform pprdrv of its progress or the current status of the printers. Any messages which pprdrv does not recognize as a progress or status message will be appended to the print job's log file.
The interface program will receive command line parameters which include the name of the printer, the printer's address, interface program options, and other parameters which describe certain aspects of pprdrv's intended interaction with the printer.
The interface program may append messages to the printer's log file by calling the alert(). It should do this to report invalid command line parameters or unexpected problems communicating with the printer.
The interface program must return an exit codes in order to inform pprdrv of its success or failure. If the code indicates failure, then pprdrv will adopt the exit code as its own. If the code indicates success, then pprdrv will adopt it only if it too achieved success.
Figure 4. A typical Printer Interface Program acts as an intermediary between pprdrv and a PostScript printer. It communicates with the printer by using TCP/IP and SNMP.

Some interface programs have a special probe mode. In probe mode they don't connect to the printer, but instead use out-of-band means to extract identifying information from the printer. An interface program's probe mode is one of the ways the ppad ppdq and ppad ppd query commands attempt to identify the printer in order to recommend an appropriate PPD file.
In the remainder of this sections, the way an interface program ought to work is explaned in more detail.
There are 11 parameters in all. That seems a lot, but it is perfectly acceptable to write an interface which uses only the first two. Most also examine parameters four through six to make sure that they are compatible with the protocol used when communicating with the printer.
If the interface program determines that the values of parameters four through six are unacceptable, it should call alert() to post an explanatory message to the printer's alert log and then exit with the code EXIT_PRNERR_NORETRY_BAD_SETTINGS.
Some of the parameters are small integers which represent enumerated values. These values are defined symbolically in the C include file include/interface.h and in the shell script fragment interface.sh.
The order to some of the parameters between 7 and 11 inclusive changed with PPR 2.00. Also, the name and semantics of what is now parameter number 7 changed slightly.
The interface is invoked with the first parameter set to the name of the printer. The interface should use this name when posting alerts using either the alert() function in libppr.a or /usr/lib/ppr/alert.
The second parameter is set to the string following the Address: keyword in the printer configuration file.
What constitutes a syntatically valid address is entirely up to the designer of the interface program. If the address is syntactically invalid, the interface must call alert() to post an error message to that effect and then exit returning the code EXIT_PRNERR_NORETRY_BAD_SETTINGS.
The third parameter is set to the string following the Options: keyword in the printer's configuration file. This will be zero or more space separated name=value pairs.
The set of valid options and acceptable values is determined by the designer of the interface program. However, when devising options for a new interface program, he should be aware of the options of similiar existing interfaces and avoid creating new options or option sementics. If the interface program does not recognize one or more of the options or finds an option with an invalid or out-of-range value, it must call alert() to post an error message to that effect and then exit with the value EXIT_PRNERR_NORETRY_BAD_SETTINGS.
The fourth parameter is the value from the printer configuration file's JobBreak: line. This value is a small integer representing an enumerated value. The JobBreak: line is set with the command ppad feedback.
Most job break values do not require any special action on the part of the interface. However, it is a good idea for the interface program to test for jobbreak settings with which it is known to be incompatible. It should only check for settings known to be incompatible. It should not limit jobbreak settings to a list of those known to be compatible since other, compatible settings may be added in future versions of PPR. The various possible jobbreak settings are described in the ppad(8) man page under the section for the ppad jobbreak command. Most interfaces will only be incompatible with JOBBREAK_SIGNAL and JOBBREAK_SIGNAL_PJL.
The jobbreak methods JOBBREAK_SIGNAL and JOBBREAK_SIGNAL_PJL require explicit support in the interface program. The interface atalk is the only one supplied with PPR which does this. If it detects that one of these jobbreak methods is in use, it will send its parent (pprdrv) SIGUSR1 as soon as it has established its own SIGUSR1 handler. Thereafter, whenever it receives SIGUSR1, it will read all bytes currently available from the pipe, send them to the printer and then send the printer an end of job indication. When atalk receives an acknowleding end of job indication from the printer, it should sends pprdrv SIGUSR1. This handshaking method is necessary because the PAP (AppleTalk Printer Access Protocol) end of file marker has no representation in the byte stream. Instead it is sent out-of-band by setting a special flag in the header of the packet.
The fifth is the value from the printer configuration file's Feedback: line. It is zero if the printer is incapable of sending data back to the computer running PPR over the communications line, non-zero if it is capable. The Feedback: line is set with the ppad feedback command.
The sixth parameter is the value from the printer configuration file's Codes: line. The value is a small integer representing an enumerated value. The Codes: line is set with the command ppad codes.
This parameter indicate the set of byte values which pprdrv believes that the interface program and the communications channel are capable of conveying all the way to the PostScript interpreter. It is recommended that the interface program examine the codes value and abort if the interface and the communications channel are not capable of passing the set of codes indicated.
The tenth parameter indicates the type of the file which the interface program will be reading from stdin, that is, the file which the interface program is begin asked transmit to the printer.
The eight parameter is set the the text of the %%Routing: comment in the print job. If the %%Routing: comment is absent, then this parameter will be blank. The routing comment is intended to convey delivery instructions to a human printer operator. For example, its value might be "deliver to room 101". If your interface program can somehow make this message appear on an operator console, it may do so. If it is an interface to an outgoing fax modem, then it might be reasonable for it to read the telephone number from this parameter. This is acceptable because a telephone number can be considered a delivery instruction. This parameter should not be used for any purpose other than to indicate wither the printed job should be sent. Particularly, it should not be used as a way to pass options which control the printing process.
The seventh parameter is set to the name of the job. (For example, chipmunk-148.0. This parameter is provided for the lpr interface which passes this information on to the remote spooler.
The ninth parameter is the text of the name of the user to whom the job belongs. This parameter is provided for the lpr interface which passes this information on to the remote spooler.
The interface command line in not particularly difficult to parse, but you might find it helpful to use the library functions which those interfaces which come with PPR and are written in C use. To use it, include the file libppr_int.h and call the function int_cmdline_set() from main() like this:
int_cmdline_set(argc, argv);
Once you have do this, the parameters will be available to you as members of the global structure int_cmdline. The structure int_cmdline is an instance of INT_CMDLINE, which is defined below:
struct INT_CMDLINE
{
gu_boolean probe; /* TRUE if --probe used */
const char *int_name; /* example: "interfaces/atalk" */
const char *int_basename; /* example: "atalk" */
const char *printer; /* example: "myprn" */
const char *address; /* example: "My Laser Printer:LaserWriter@Computing Center" */
const char *options; /* example: "idle_status_interval=60 open_retries=5" */
int jobbreak; /* example: 1 (JOBBREAK_SIGNAL) */
gu_boolean feedback; /* example: 1 (TRUE) */
enum CODES codes; /* example: 3 (CODES_Binary) */
const char *PDL; /* example: "postscript" */
const char *routing; /* example: "Call David Chappell at 2114 when ready" */
const char *jobname; /* example: "myprn-1001.0" */
const char *forline; /* example: "David Chappell" */
const char *title; /* example: "My Print Job" */
} ;
It is now very easy to write code that refers to the command line parameters by name. For example, if your interface program doesn't support probe mode, bidirectional communication, or the jobbreak methods signal and signal/pjl, you could put this right after the call to int_cmdline_set():
if(int_cmdline.probe)
{
fprintf(stderr,
_("The interface program \"%s\" does not support probing.\n"),
int_cmdline.int_basename
);
exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
}
if(int_cmdline.feedback)
{
alert(int_cmdline.printer, TRUE,
_("The PPR interface program \"%s\" is incapable of sending feedback."),
int_cmdline.int_basename
);
exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
}
if(int_cmdline.jobbreak == JOBBREAK_SIGNAL || int_cmdline.jobbreak == JOBBREAK_SIGNAL_PJL)
{
alert(int_cmdline.printer, TRUE,
_("The jobbreak methods \"signal\" and \"signal/pjl\" are not compatible with\n"
"the PPR interface program \"%s\"."), int_cmdline.int_basename);
exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
}
The exit codes which interface programs should use are defined in include/interface.h and /usr/lib/ppr/interface.sh.
This interface program should return this code if it was able to complete its jobs successfully. It doesn't actually mean that the interface knows the job was printed successfully, just that it was able to do its part. When it receives this exit code, pprdrv will exit with the same code if the interface program accepted all of the job data before exiting and if no other error was detected (such as a PostScript error).
If the interface was unable to connect to the printer or the connection was broken off, it may return this code. The printer will be fault-auto-retry mode. This is the catchall code. If there is a code listed below which better fits the specific circumstance, it would be better to return that code.
There was a printer error caused by a circumstance which will not disappear spontainiously, such as a syntactically invalid printer address. The printer will be placed in fault-no-auto-retry mode. Like EXIT_PRNERR, this is a catch-all code. If there is a code listed below that better fits the specific circumstance, then it would be better to return that code.
This code is normally only used by pprdrv. Very few interfaces would ever have cause to return this error code.
Since PostScript errors are detected by pprdrv by watching for messages from the PostScript interpreter in the data stream which the interface receives from the printer and prints on stdout, there is no need for the interface to detect PostScript errors or to report them using this exit code.
But, if the interface detects an error and has some kind of information which definitely indicates that the fault was caused by the job being printed, it should return this code. The job will be "arrested" and held for inspection by the operator.
One of the few types of interfaces which might return this code is an interace to a fax server. It might return this code if the phone number (possibly passed as a routing instruction) turned out to be invalid. In such a case it could print a message to that effect to stdout (which leads into the jobs log file) and then return this code.
When a job is canceled during printing or the printer is forced to halt during printing, pprdrv sends SIGTERM to the interface program. The interface program has the option of catching the signal and performing shutdown operations before termination. After the shutdown is finished, it should return this code. Note that an interface program is not required to catch the signal. Simply dieing is perfectly acceptable.
If the interface was unable to connect to the printer because it was busy or off-line, it should return this code. The printer state will be set to "otherwise engaged or off-line" and the operation will be retried after a short delay.
Normally this code will be used when a definite indication is present that the printer is turned on and connected but is not ready to open a session. If there is no indicatation that the printer even exists, then the code EXIT_PRNERR_NOT_RESPONDING is more appropriate. For example, a TCP connect attempt results in "connection refused", this code is probably appropriate. If however no response at all is received (i.e., the connexion attempt times out), then EXIT_PRNERR_NOT_RESPONDING is more appropriate.
An interface should not call alert() if it intends to exit with this code. An engaged printer isn't considered a condition worthy of notation in the printer's alerts log.
If the interface program cannot perform its function because a it cannot obtain a sufficient quantity of a finite system resource such as RAM or file descriptors, it should exit with this code. It should not call alert() since the problem is not related to the printer. Since the condition is presumably temporary, the operation will be retried after a short delay.
If when the interace program attempts to connect to the printer it receives unambiguous notification that the connection is refused on the basis of of access control rules or failure to successfully complete an authenticaition process, then it should call alert() to append a suitable message to the printers alert log and then exit with this code. Since correcting this condition will require administrator intervention (either by reconfiguring the printer itself to allow access reconfiguration the print queue to supply the necessary credentials), the operation will not be retried. Once the problem has been corrected, the command ppop start must be used to restart the printer queue.
If the printer did not respond to the connexion attempt in any way, then the interface program should call alert() in order to append an appropriate message to the printers alerts log and then exit with this code. The printer will be show to be in an error state and the operation will be retried after a delay.
If the printer responded indicating that it was unready or unwilling to accept the connection, then a different error code such as EXIT_PRNERR_ENGAGED or EXIT_PRNERR_NORETRY_ACCESS_DENIED should be used instead.
If the interfaces finds that any of parameters two through six are syntactically incorrect, or logically incompatible, or have values which are incompatible with the interface program or the means of communication with the printer, it should call alert() to append a message to the printers alerts log and then exit with this code. Printing will be halted until an administrator changes the settings and restarts the printer with the ppop start command.
The interface should not use this code to report that an address lookup failed. The code EXIT_PRNERR_NO_SUCH_ADDRESS should be used instead. The only address problem that should be reported with this code is an unparsable address.
This exit code should be used if an attempt to look up the specified printer address fails. This code should be used if no answer was received, the failure might be temporary, or the failure may indicate that the device is simply turned off. An appropriate message should be posted to the printers alerts log by calling alert(). The operation will be retried after a delay.
If the result is definite information that a printer with the indicated address does not exist, then EXIT_PRNERR_NORETRY_NO_SUCH_ADDRESS should be used instead.
This code should be used when printer address lookup results in an answer which unambiguously indicates that the address does not exist. An appropriate message should be posted to the printers alerts log by calling alert(). The operation will not be retried until an operator intervens by running the ppop start command.
The interface program should not return any value other than those defined above. Any undefined value will be interpreted as a EXIT_PRNERR. Note that Perl's die returns the code 255, which is not among those defined above.
An interface program can post alerts to the printer's alerts log by calling the alert() function or runing the alert command. An interface program is required to do so before exiting with any of the codes whose names begin with EXIT_PRNERR.
The function alert() requires three or more parameters. The first is the name of the printer (from argv[1]). The second is a boolean. One may build up the alert message by calling alert() several times to add lines to it. This boolean should be TRUE on the first call and FALSE on subsequent calls. The third parameter is a printf() style format string. Any addition parameters are values for interpolation into the format string.
A newline will automatically be appended to the format string. Format strings containing embedded newlines are an acceptable way to create multi-line alert messages.
Here is a simplified example of how one might do this in C. It is somewhat contrived. It verifies the address by making sure that it begins with "/dev/".
if(strncmp(argv[2], "/dev/", 5) != 0)
{
alert(argv[1], TRUE, "Address \"%s\" is syntactically invalid.", argv[2]);
exit(EXIT_PRNERR_BAD_SETTINGS);
}
For interfaces written as shell scripts, the alert() has been wrapped in a tiny program. It may be used like this:
. /interface.sh if [ "`echo $2 | cut -c1-5`" != "/dev/" ] then ./alert $1 TRUE "The address \"$2\" is syntactically invalid." exit(EXIT_PRNERR_BAD_SETTINGS); fi
If your interface program supports bidirectional communication with the printer (which is refered to elsewhere as "feedback"), care should be taken to avoid situations which could cause communications to lock up. Lockups can occur if your interface program fails to give top priority to receiving messages from the printer. If the printer has a large amount of data to transmit to the interface program, its output buffer could fill. When a printer has a message to send, it will not process additional input until it has placed the message in its output buffer. If your interface program refuses to accept data from the printer until after the printer has accepted the next data block, a lockup will occur and both parties will wait forever. A particularly insidious aspect of this problem is that it will not happen every time. It is most likely to happen when the printer has a great deal of data to send back, such as query results or status messages.
This means that your interface program must place the file descriptor connected to the printer in non-blocking mode. When activity is detected (perhaps by the select() function), data that can be read from the desciptor must be read. It should then be sent out on stdout. Since pprdrv lives by the same rules, giving highest priority to the data which your interface program sends to its stdout, it is not absolutely necessary to run stdout in non-blocking mode.
Your interface program must not block on reads from stdin. This is because, after the job has been transmitted, pprdrv tries to keep the connexion open until the job is completed. It will stop sending data. But your interface program must go on relaying messages from the printer to pprdrv. These messages are used by pprdrv not only to keep the printer status up-to-date, but also to determine when the job has been completed. If your interface program blocks on stdin, then PPR will likely get stuck at the end of each job.
The interfaces tcpip and serial are good sources of example code. They share a function call int_copy_job(). This function handles correct two-way communication between stdin, stdout, and the printer file descriptor.
In the same directory you will find several PostScript files with names such as feedback_test1.ps. If you print them using your interface program, then will do things such as commanding the printer to send huge amounts of data back. They will quickly smoke out interfaces with fragile two-way communication implementions.
There are special messages which an interface program can send to pprdrv to inform it of its progress or to report the status of the printer. These messages are similiar to those which many PostScript printers send back to the computer connecting to them.
Note that an interface program is not required to send any of these messages. However, their use may result in better user feedback.
This message indicates that the interface program has parsed its arguments without finding any problems and is not begining a potentially time-consuming address lookup. This message is simply ignored by pprdrv, but when ppad is running the interface in probe mode it will extend the timeout after receiving this message.
This message indicates that the interface is begining a the potentially time-consuming process of connecting to the printer. When pprdrv receives this message, it sets the cooresponding flag in a status file so that ppop status will show the user that a connexion attempt is in progress.
This message indicate that the connexion process has been complete successfully.
Indicates that the printer is unable to print due some disabling condition described by message. If it is among those listed in lw-messages.conf, then pprdrv will alter the printer status appropriately.
Indicates a printer condition that is either normal, transient, not critical, or not yet critical. If it is among those listed in lw-messages.conf, then pprdrv will alter the printer status appropriately.
Indicates that the printer has gone off line because it is out of paper.
Indicates that the printer explicitly indicates that it has been taken off line, generally by a user pressing a button. If the interface prints this message before exiting with the code EXIT_ENGAGED, then ppop status will show the printer status as "off line" rather than "otherwise engaged or off line".
Indicates that a printer directly connected to the server does not appear to be electrically alive at the end of its cable.
Indicates that the printer is online and ready and that any disabling PrinterError conditions (such as off line) have cleared.
This message reports the SNMP status of the printer. The value hrDeviceStatus and hrPrinterStatus are decimal integers. The value hrPrinterDetectedErrorState is an eight digit hexadecimal unsigned integer.
The ppad ppdq and ppad ppd query commands provide a way to automatically determine a printer's type. They use a number of techniques to accomplish this goal.
One technique is to connect to the printer and sending it query messages to which it will hopefully respond. These queries use langauges such as PostScript and PJL. These techniques do not generally change according to the connection method, so interface programs need only support two-way communication in order to support them.
But some connection methods, such as USB and IP may provide additional methods for obtaining information about the printer, such as alternative communications channels. For example, many printers which accept jobs over TCP/IP also response to SNMP queries. Since these methods depend very much on the method of connecting to the printer, probes of this type, called out-of-band probes, are implemented in interface programs.
Interface programs which support out-of-band probe will recognize and act on --probe. It will interrogate the printer in some way rather than connecting to it with the intention of transmitting a print job. The results of the probe are sent back to pprdrv as a series of PROBE: lines.
If the probe may take more than a few seconds, it is recommended that the interface immedately send a PROBE: line with no value in order to let ppad know that it supports probe mode. Otherwise, ppad may conclude that the interface program doesn't implement --probe and give up too soon.
If the interface is able to obtain information about the printer, it should report it by printing addional PROBE: lines. These show be in the form PROBE: name=value. The value should not be quoted, even if it contains spaces. Currently defined names are listed below. Since the probe feature is very new, this list is subject to change.
The PostScript product name. The value should be the bare name without surounding partheses.
The PostScript interpreter's version number.
The PostScript interpreter's revision number.
???
???
???
same as above
???
same as above
When running in probe mode, the interface program will be invoked with the printer name on the command line set to "-". The alert() knows about this special name. When it is asked to post an alert to the printer "-", it sends the alert to stderr instead. The result is that the messages will be visible to the user of ppad rather than being hidden away in the alert log.
This section describes who PPR converts non-PostScript files to be printed to PostScript.
One of the most noteworthy features of PPR is its ability to determine the type of the input file and automatically convert it to PostScript if necessary. The type of the input file is determined by two methods. The first method is the most reliable. The second is employed only if the first fails.
The first method is to look for a "magic number" at the start of the file. Many file format specifications dictate that files conforming to them always begin with certain signature bytes. Formats which specify a signature or magic number include DSC conforming PostScript, JFIF, GIF, PNG, DVI, WordPerfect documents, PBM, XPM and others.
If no recognized magic number is found, then the first 8192 bytes of the file are scanned and the number of times certain characters and constructs are found are counted. The things counted include ASCII control characters, non-ASCII characters, HP escape sequences, Troff-style dot commands, TeX style backslash commands, and PostScript procedure definitions. When these things have been counted, the totals are used to try to guess at the file format.
If PPR ever fails to determine a file's format correctly or you wish to print a file in a format for which auto-detection is not available, you can override the auto-detection mechanism with ppr's -T switch.
The result of automatic type detect is a file-type name such as one would use with the -T switch.
If ppr determines that the input file is not PostScript, it will seek to use a filter to convert it to PostScript. This appendix provides the information you will need to write your own PPR input filters.
The filters are found in the directory /usr/lib/ppr/filters/. Each of these files has a name that consists of filter_ followed by the PPR input type name. For example, the filter for JPEG files is called /usr/lib/ppr/filters/filter_jpeg.
A filter should read the file from STDIN and write PostScript code on STDOUT. If it must, it can write messages on STDERR. STDIN is guaranteed to be seekable. Messages sent to STDERR will go wherever STDERR was going when ppr was invoked.
The parameters are as follows:
The first parameter is the list of filter options. These are expressed as a space seperated list of name-value pairs. The name and value are joined by an equal sign. The options list is formed by concatenating the contents of the DefFiltOpts: line in the printer's or group's configuration file with the contents of any -o lines the user put on the ppr command line.
Before invoking the filter, ppr culls and pre-processes the option list. It removes any option which is intended to apply only to a specific filter other than the one being used. An option may be applied to a single filter by prefixing its name with the name of the filter and a hyphen. For example, the option noisy=yes should apply to all filters and hence won't be culled but the option dvi-noisy=yes will be deleted by ppr except when it is invoking the DVI filter. A filter, such as the tex filter, may invoke another filter such as the dvi filter to do part of its work. The first filter will generally pass its options on to the second filter, however, since the filter list has already been culled, the DVI filter will receive options whose names began with "tex-" but will not receive options whose names begin with "dvi-".
The names of the parameters (the part to the left of the equals sign) are converted to lower case. The values (the part to the right of the equals sign) are not.
A responder should ignore any option it does not recognize. If the same option appears more than once, the value from the last instance is the one that should be used.
The name of the printer or group to which the job was submitted. This will generally be ignored.
The third is the job title. This may be used by filters which format their input as pages with headers and footers.
When a filter is invoked, the environment variable IFS is set to a space and a tab, and the variable PATH is set to a value which is just adequate to find standard shell script helper programs such as test, sed, and grep. On most systems, that value of PATH is /bin:/usr/bin.
Here is an example. Suppose this line is in the printer's configuration file:
DefFiltOpts: level=2 colour=False resolution=300 freevm=1048576 mfmode=CanonCX
and the user submits a JPEG (JFIF) file with this command:
$ ppr -d myprn -o noisy=no -o 'fortran-width=130 jpeg-noisy=yes' picture.jpg
The filter will be invoked like thus:
filter_jpeg 'level=2 colour=False resolution=300 freevm=1048576 mfmode=CanonCX \ noisy=no noisy=yes' myprn 'picture.jpg'
A filter should interpret any options it recognizes and ignore any it does not. If it finds two contradictory options, it should obey the last one. In the example above, the option noisy=yes is the one that prevails. The options freevm=1048576 and mfmodes=CanonCX would be ignored simply because the JPEG filter has no code to use them.
If the filter exits with a value other than 0, the job will not be discarded. A message may be informed by printing on stderr or invoking a responder, the exect behaviour being controled by the -e switch.
When a filter is executed, the real user id is that of the user who executed ppr. The effective user id and the saved user id's are ppr. The real group id is the same as it was when ppr was executed. The effective and saved group id's are ppop.
It is possible to determine precisely what filter is being executed with what arguments by running ppr with the -G infile:filter option.
A number of filters are supplied with PPR. PPR also includes a number of shell and Perl scripts which work together with programs such as TeX, Troff, PBMPlus, and other programs often found on Unix systems to form filters. If the supporting programs can not be found, the script filters will not be installed.
The filters supplied with PPR, together with their options are described in the ppr(1) man page, under the section for the -T switch.
It should be noted that PPR does not supply a filter for every file type which it can detect or for every value of the -T switch. If PPR does not have an appropriate filter, you may be able to supply one and place it in the filters directory.
This file resides in the directory /usr/lib/ppr/lib/. It is replaced whenever a new version of PPR is installed, so if you modify it you should keep a copy of your modifications elsewhere.
The file contains a list of PostScript font names and possible substitute fonts.
Any line that has # or ; in the first column is a comment. Blank lines are ignored. A font substitution record begins with the name of the font for which there are substitutes. The name should start in column one and be on a line by itself. The list of possible substitutes follows, one per line. Each substitute font line should start with a space or tab. You may list as many substitute fonts as you like. The first substitute font to be found in the printer's PPD file, in the cache directories or in the index created with ppr-index fonts will be used.
Here is an example of four records which indicate that the IBM Courier fonts are suitable substitutes for the Adobe Courier fonts:
Courier IBMCourier Courier-Bold IBMCourier-Bold Courier-Oblique IBMCourier-Italic Courier-BoldOblique IBMCourier-BoldItalic
You have the option of specifying a PostScript transform matrix to be applied to the substitute font. Generally this will be used to adjust the width. The should appear on the substitute font line, after the substitute font name. Here is an example:
Helvetica-Condensed Helvetica [0.80 0 0 1 0 0] Helvetica-Condensed-Bold Helvetica-Bold [0.80 0 0 1 0 0] Helvetica-Condensed-Oblique Helvetica-Oblique [0.80 0 0 1 0 0] Helvetica-Condensed-BoldOblique Helvetica-BoldOblique [0.80 0 0 1 0 0]
This approximates Helvetica Condensed by scaling Helvetica to 80% of its normal width. The role of the other members of the matrix, refer to the PostScript language reference manual.
This file mfmodes.conf is used by ppad when setting the default filter options. Specifically, the mfmode= option is selected with the aid of this file. The mfmode= option is used by the DVI filter to select an appropriate MetaFont mode for a given printer.
Before consulting this file, ppad reads the printer's PPD file and extracts the values from the following lines:
*Product: *ModelName: *NickName: *DefaultResolution:
The extracted values are then compared to values on lines in the mfmodes.conf file. Each line in the mfmodes.conf file has the following format:
product:modelname:nickname:resolution:mfmode
The file is read top to bottom until a match is found or the end is reached. The information from the PPD file is compared to the first four fields of each line. A * may be used as a wildcard in any or all of the first four fields. When a match is found, the value from the fifth field is used as the value for the default filter option mfmode=.
The *Product: line from the PPD file generally identifies the manufacturer and model line of which the printer is a part. Since all printers which use one product string generally use the same print mechanism, this parameter alone is usually enough to make selection of the correct MetaFont mode possible. For this reason, most entries in the mfmodes.conf file will have the product field filled in but the modelname, nickname, and resolution fields will all be *.
For example, the following line:
LaserJet 4:*:*:*:ljfour
will match if the *Product: line from the PPD file has a value of (LaserJet 4). The fact that fields two through four contain astrisks indicates that any value is acceptable for product, modelname, and resolution.
The GhostScript interpreter has a product string of Ghostscript or Alladin GhostScript. Therefor, the printer must be identified by means of its PPD files *ModelName: line. Here are some reasonable configuration lines for printers driven by Ghostscript:
*:Dot Matrix 24 pin Ghostscript:*:*:NEChi *:HP LaserJet III Ghostscript:*:*:CanonCX *:HP DeskJet 500 Ghostscript:*:*:HPDeskJet
In a PPD file obtained from Adobe or the printer's manufacture, the *NickName: line is identical to the *ModelName: line. You might change it if you make a special hacked-up copy of the PPD file for a particular printer. For example, you might change it to David's HP DeskJet 500 Ghostscript.
There are valid reasons for using a modified PPD file, but why changes to the PPD file should dictate a different MetaFont mode is hard to say. (Changes to the *DefaultResolution: line are covered by the next section.) Unless you know a good reason not to, you should always put a "*" in this field.
Generally, you can just put * in the resolution field. There are however two possible reasons for filling in the value from the PPD file's *DefaultResolution: line.
One is if the printer's resolution can be changed. A change in resolution requires a change in the MetaFont mode. You might have several different PPD files for the same make and model of printer, one for each resolution. Here is a (fictitous) example:
*:HP LaserJet III Ghostscript:*:300dpi:CanonCX *:HP LaserJet III Ghostscript 150DPI:*:150dpi:ljlo
The other reason for putting a value other than * in the resolution field is if the line is one at the end of the file which is intened to to be a best guess for printers which have not matched any of the lines above. These are some reasonable last resort lines:
*:*:*:300dpi:CanonCX *:*:*:600dpi:ljfour *:*:*:360dpi:NEChi
The MetaFont mode names in the mfmodes.conf file that comes with PPR are taken from the modes.mf file maintained by Karl Berry. A recent version of his modes.mf file is distributed with the PPR source code, in the misc directory. His file defines a number of aliases for each mode. When adding entries to PPR's mfmodes.conf file you should try not to use two different names that both refer to the same mode in modes.mf because that would result in the generation of duplicate sets of identical pk font files. The mfmodes.conf file supplied with PPR always uses the first alias from Karl Berry's modes.mf file which may be abtained from ftp://ftp.tug.org/tex/modes.mf.
This section describes the protocol which use commands such as ppr, ppop, and ppad use to communicate with pprd. This information may assist those attempting to understand the source code. However, one should not implement this protocol since it changes from version to version. Instead one should run ppop with the -M option and parse its output. For this reason, only a few example commands are described here.
The spooler daemon, pprd receives commands over a named pipe and acts on them. The program ppr sends one such command to pprd in order to inform it that a new jobs has been placed in the queue directories. The program ppad uses two different commands which it uses to inform pprd that a printer, group configuration file has been modified. Finally, ppop has many commands which it uses to control pprd or to request information from it.
Once the job submission program ppr has created the queue files it sends a command to pprd telling it that the job is there and is ready for printing or transfer to the remote system. This command takes the form:
j destination_node destination_queue id subid home_node initial_priority
The destination_node is the node to which the job should be sent. Since transmission to remote notes is not yet supported, this will always be the name of the local node. The destination_queue is the name of the print queue on the destination node. The id and subid are the queue id and the job fragment number respectively. The subid will most often be zero. The home_node is the node name of the system on which the job originated (again, the local node).
Whenever a printer or group configuration file is modified or a new one created, pprd must be directed to read it. To inform pprd that it must read a new or revised printer configuration file, a command line of this form is sent:
NP printername
To indicate that a group configuration file must be re-read or that a new group configuration file must be read for the first time:
NG groupname
Neither command is acknowledged by pprd.
The utility ppop must communicate with pprd in order to list the queue, show the status of printers, mount forms, and do other tasks. Most of these communications take the form of a query and a reply.
As currently implemented, the interprocess communications is crude but effective. The queries are sent by writing to pprd's named pipe. The replies are sent back in temporary files. The command which ppop writes to the name pipe begin with ppop's process id. After writing the command, ppop waits for a signal, USR1 to be specific. The process ID is read by ppad which creates a temporary file called /tmp/ppr-ppop-pid where pid is the process id sent by ppop. Once pprd has finished writing the reply, it closes the temporary file and sends SIGUSR1 to ppop which opens the file, deletes it, reads its contents, and formats and displays said contents.
Many of the replies take the form of a numberic exit code for ppop and a human-language message to display. Some include many additional lines of results but only if the code in the first line is zero (indicating success).
Much of the code in the parts of ppop and pprd which communication with one another was written before any attempt was mode to internationalize PPR. This is a problem because pprd may be generating messages in one language while ppop is generating them in another. The plan is to eventually eliminate all user visible messages from these files leaving only code information. At the same time ppop will be modified to digest this information and produce text in the users language.
A responder is a small program which is used to send a user a message about his print job. All responders are stored in the responders directory /usr/lib/ppr/responders.
A responder can be very simple. Here is an example:
#!/bin/sh while [ $# -gt 0 ] do name=`echo $1 | cut -d= -f1` value=`echo $1 | cut -d= -f2` case $name in for ) for="$value" ;; responder_address ) responder_address="$value" ;; short_message ) short_message="$value" ;; esac shift done write $responder_address <<EndOfMessage Message for $for: $short_message EndOfMessage exit 0
The responder shown above is a stript down version of the responder write.
The responder which will eventually be used to tell the user what happened to the job is selected when the job is submitted. It is selected using ppr's -m. switch. The argument to the -m switch is the name of the program in the responders directory which should be used. If the -m switch is not used then the name of the responder will be read from the environment variable PPR_RESPONDER. If PPR_RESPONDER is not defined then the responder followme will be used.
A responder program is invoked with the name of the user as the for= parameter. It is the name just as it appears in queue listings and on banner pages.
The address to which the message should be sent is the responder_address= parameter. The address is specified at the time ppr is invoked by using the -r switch. The proper format for the address depends on the responder. If the -r switch is absent then the value of the environment variable PPR_RESPONDER_ADDRESS is used. If that too is absent then the name of the Unix user who invoked ppr is used. The value used in the absence of both -r and PPR_RESPONDER_ADDRESS is appropriate since the default responder is a script which invokes the Unix program write.
The short_message parameter contains one version of the suggested message text. The responder should word-wrap the text before presenting it to the user.
The long_message parameter is a longer and more detailed version of short_message.
The responder_options parameter is a space separated list of responder options. Each of this options is a name=value pair. The value of this option comes from the ppr --responder-options switch or from the environment variable PPR_RESPONDER_OPTIONS. A responder should ignore any options it does not recognize.
The response_code parameter is a code number which represents the approximate content of the message. It is provided in case a responder wants to send different types of message by different methods or wishes to provide its own wording for one or more of the messages. For example, it might want to send a popup message but then follow it up with email if the message indicated that the job was arrested. The code numbers are defined in C include file include/respond.h, the Bourne shell script include file lib/respond.sh, and the Perl include file lib/respond.ph.
The job parameter contains the complete job id. This is probably only useful if the responder is constructing its own messages since the default messages already mention the job id.
The title parameter is the title of the job. This title will be derived from a "%%Title:" line if present. Failing that, the title will be the name of the file being printed. If the file was received on stdin, this field will generally be blank.
The time parameter is the time at which the job was submitted. It is represented in Unix format (as a count of the seconds since 12:00am, January 1, 1970). The program lib/time_elapsed may be used to express this time in terms of how far in the past it is.
The pages parameter contains the number of pages printed. If this is unknown, it is "?". If the response code (the fourth parameter) has a value other than RESP_FINISHED then the contents of this field is undefined.
When the responder is run, stdin with either be connected to /dev/null or it will be connected to the job's log file. This is so that the responder may send the job log back to the user. Some of the supplied responders exploit this feature.
When exiting, the responder should return a value of zero if the message was delivered. It should also return a value of zero if the addresse was not found. Non-zero exit values should be reserved for truly abnormal conditions such as insufficient system resources or syntactically invalid addresses. If a responder does return a non-zero exit value, ppr will print a notice to that effect on stderr or pprd will put a notice in its log file, depending on which one invoked the responder.
When the responder is invoked by pprd, it will always have a real uid, effective uid and saved uid of ppr. When it is invoked by ppr (due to the use of the -e responder option) things will be different. The program ppr is setuid ppr, so the effective and saved uids are ppr and the real uid is the id of the user who invoked it. Just before executing the responder ppr sets the effective user id equal to the real user id. This last feature is not intended as a security measure, rather, the xwin responder will not work if this is not done since the X library uses access() on the .Xauthority file before trying to open it which means that the .Xauthority file in the user's home directory must be readable under both the real and the effective uids. This feature may be overridden by setting the setuid bit on the responder. If this is done then the effective uid will remain ppr.
A custom hooks is a small program which PPR runs at predetermined points in the process of sending a job to a printer. Anything that the program sends to standard output will be transmitted to the printer. The program has access to the queue file, so it can determine the characteristics of the job. For example, you could write a custom hook program which printed a banner page. If a print queue is properly configured, your program will be used to print banner pages instead of PPR's internal banner page printing code.
In the file pprdrv/pprdrv.h the following constants are defined:
#define CUSTOM_HOOK_BANNER 1 #define CUSTOM_HOOK_TRAILER 2 #define CUSTOM_HOOK_COMMENTS 4 #define CUSTOM_HOOK_PROLOG 8 #define CUSTOM_HOOK_DOCSETUP 16 #define CUSTOM_HOOK_PAGEZERO 32
We will get to what they individually mean in a minute. For now let us say that they each one of them stands for a point at which your custom hook may be called to insert additional text into the PostScript job. Notice that they are powers of two. That means that they may be added up to produce a number that represents a set of choices as a bitmap. In order to tell PPR at which insertion points in the job a custom hook should be run, one adds up the code numbers which represent the desired points.
Where does this value go? You should enter it, together with the name of your custom hook program, on a line in the printer configuration file. The line has the following format:
CustomHook: bitmask program
The bitmask is the total of the codes and the program is the name of your custom hook program. You should probably specify the complete path. Since ppad doesn't have a command for manipulating entries like this one, you should add the line using a text editor. You will find the file in /etc/ppr/printers/.
When your custom hook program is run, stdout will be connected to the printer. That is, anything you print on stdout will be transmitted to the printer. What you are expected to send to the printer depends on the insertion point and will be discussed below when the insertin points are described. Stdin will be connected to /dev/null. Stderr will be directed to the pprdrv log file (/var/spool/ppr/logs/pprdrv). While developing a custom hook program it is helpful to print debugging messages to stderr.
The custom hook program will receive three command line parameters. The first parameter is the code for the part of the PostScript document that is being generated. The second parameter gives details. For most document parts it is zero. The third parameter is the full queue ID of the job. It may be passed to ppop qquery to get details about the job or, since it is also the name of the queue file, it may be used to open the queue file in /var/spool/ppr/queue/.
And now for a description of the various points at which your custom hook program can be called.
The custom hook program is invoked to print a substitute banner page. The regular banner page is suppressed. Remember that a banner page comes before the job in the sense that it is placed in the output tray in front of page one. Whether it is actually printed before the job depends on whether PPR thinks the tray is face up or face down. If your code needs to know if is being printed (chronologically) before the job's pages or after, it should examine its second command line parameter which will be 0 for before the job and 1 for after. [Note: verify that I have that right.] Note that your custom hook program should generate a complete PostScript document. The printer's PostScript interpreter will be reset before and after your banner page.
The custom hook program is invoked to print a substitute trailer page. Again, the second parameter indicates whether the body of the job has been printed yet.
The custom hook program is invoked just before the document header comments are sent. Thus the program can add any additional document header comments it likes. Notice that each and every line generated must begin with % and be followed by a printable character other than space tab or newline.
The custom hook program is invoked at the end of the document prolog maybe so that it can insert any procedure sets it might need. It is unlikely you would want to do this, unless perhaps the intent of your custom hook is to do something like N-Up printing. (Of course, PPR can already do N-Up printing.)
The custom hook program is invoked at the end of the document setup section. This is where you would turn on your replacement N-Up printing implementation.
The custom hook program is invoked just before the first page of the document. It can insert one or more additional pages. Basically, it can be used to add a banner page inside a job. This is for use in environments where banner pages outside the job would confuse some system furthure down the line. This is the case when printing to a Xerox Docutech. Since the document setup section's code may have already set up transform matrixes which could squish or shift the page you will be generating, it should wrap itself in save initmatrix ... restore.
You may have noticed that there is no provision for calling separate custom hook programs for separate insertion points. Your custom hook program should examine its first command line parameter and select the code path that cooresponds to the current insertion point. If you really need separate program, you will have to create a shell custom hook program that examines its first parameter and then executes the desired program.
If you need to invoke any printer-specific features, such as to select a particular paper size, you should print a DSC comment line like this one:
%%%%IncludeFeature: feature setting
The features is a PPD feature name such as *PageSize and the value is on of the possible values such as Letter or A4. Before your program's output is sent to the printer, this comment will be replaced with the proper code from the printer's PPD file.
Of course, since you will be generating PostScript code or at least PostScript comments, the is always the possibility that you will turn a job into an invalid PostScript program that won't print. In that case, PPR will come to your rescue by arresting the job so that you can examine its log with ppop log in order to see what the PostScript error messages are.
You will almost certainly want to look at the final output sent to the printer in order to verify that your text is being inserted where you expect. To do that, set up a test queue using the dummy interface. It will print to a file which you can then examine.
PPR currently includes one sample custom hook program. It is installed at /usr/lib/ppr/lib/custom_hook_docutech. Among other things it demonstrates how to do CUSTOM_HOOK_PAGEZERO.
If you want to supply your own RIP to use instead of ppr-gs, you should create a program and put it in /usr/lib/ppr/lib. It should connect stdin, stdout, and stderr to the cooresponding file descriptors of the PostScript interpreter and should send the print date to file descriptor 3. The RIP options will be on the command line and the filename of the printer's PPD file will be in the environment variable PPD. If the ppr --ripopts option is used, the argument will be passed to your RIP in the environment variable PPR_RIPOPTS.
[1] Adobe makes a large collection of PPD files available by anonymouse FTP. See ftp://ftp.adobe.com/pub/adobe/ to find their latest PPD file library.