4 * PostScript command filter for CUPS.
6 * Copyright 2008-2014 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * which should have been included with this file. If this file is
12 * file is missing or damaged, see the license at "http://www.cups.org/".
16 * Include necessary headers...
19 #include <cups/cups-private.h>
21 #include <cups/sidechannel.h>
28 static int auto_configure(ppd_file_t
*ppd
, const char *user
);
29 static void begin_ps(ppd_file_t
*ppd
, const char *user
);
30 static void end_ps(ppd_file_t
*ppd
);
31 static void print_self_test_page(ppd_file_t
*ppd
, const char *user
);
32 static void report_levels(ppd_file_t
*ppd
, const char *user
);
36 * 'main()' - Process a CUPS command file.
39 int /* O - Exit status */
40 main(int argc
, /* I - Number of command-line arguments */
41 char *argv
[]) /* I - Command-line arguments */
43 int status
= 0; /* Exit status */
44 cups_file_t
*fp
; /* Command file */
45 char line
[1024], /* Line from file */
46 *value
; /* Value on line */
47 int linenum
; /* Line number in file */
48 ppd_file_t
*ppd
; /* PPD file */
52 * Check for valid arguments...
55 if (argc
< 6 || argc
> 7)
58 * We don't have the correct number of arguments; write an error message
62 _cupsLangPrintf(stderr
,
63 _("Usage: %s job-id user title copies options [file]"),
69 * Open the PPD file...
72 if ((ppd
= ppdOpenFile(getenv("PPD"))) == NULL
)
74 fputs("ERROR: Unable to open PPD file!\n", stderr
);
79 * Open the command file as needed...
84 if ((fp
= cupsFileOpen(argv
[6], "r")) == NULL
)
86 perror("ERROR: Unable to open command file - ");
94 * Read the commands from the file and send the appropriate commands...
99 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
102 * Parse the command...
105 if (!_cups_strcasecmp(line
, "AutoConfigure"))
106 status
|= auto_configure(ppd
, argv
[2]);
107 else if (!_cups_strcasecmp(line
, "PrintSelfTestPage"))
108 print_self_test_page(ppd
, argv
[2]);
109 else if (!_cups_strcasecmp(line
, "ReportLevels"))
110 report_levels(ppd
, argv
[2]);
113 _cupsLangPrintFilter(stderr
, "ERROR",
114 _("Invalid printer command \"%s\"."), line
);
124 * 'auto_configure()' - Automatically configure the printer using PostScript
125 * query commands and/or SNMP lookups.
128 static int /* O - Exit status */
129 auto_configure(ppd_file_t
*ppd
, /* I - PPD file */
130 const char *user
) /* I - Printing user */
132 int status
= 0; /* Exit status */
133 ppd_option_t
*option
; /* Current option in PPD */
134 ppd_attr_t
*attr
; /* Query command attribute */
135 const char *valptr
; /* Pointer into attribute value */
136 char buffer
[1024], /* String buffer */
137 *bufptr
; /* Pointer into buffer */
138 ssize_t bytes
; /* Number of bytes read */
139 int datalen
; /* Side-channel data length */
143 * See if the backend supports bidirectional I/O...
147 if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI
, buffer
, &datalen
,
148 30.0) != CUPS_SC_STATUS_OK
||
149 buffer
[0] != CUPS_SC_BIDI_SUPPORTED
)
151 fputs("DEBUG: Unable to auto-configure PostScript Printer - no "
152 "bidirectional I/O available!\n", stderr
);
157 * Put the printer in PostScript mode...
165 * As a lot of PPDs contain bad PostScript query code, we need to prevent one
166 * bad query sequence from affecting all auto-configuration. The following
167 * error handler allows us to log PostScript errors to cupsd.
170 puts("/cups_handleerror {\n"
171 " $error /newerror false put\n"
172 " (:PostScript error in \") print cups_query_keyword print (\": ) "
174 " $error /errorname get 128 string cvs print\n"
175 " (; offending command:) print $error /command get 128 string cvs "
176 "print (\n) print flush\n"
178 "errordict /timeout {} put\n"
179 "/cups_query_keyword (?Unknown) def\n");
183 * Wait for the printer to become connected...
191 while (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_CONNECTED
, buffer
, &datalen
,
192 5.0) == CUPS_SC_STATUS_OK
&& !buffer
[0]);
195 * Then loop through every option in the PPD file and ask for the current
199 fputs("DEBUG: Auto-configuring PostScript printer...\n", stderr
);
201 for (option
= ppdFirstOption(ppd
); option
; option
= ppdNextOption(ppd
))
204 * See if we have a query command for this option...
207 snprintf(buffer
, sizeof(buffer
), "?%s", option
->keyword
);
209 if ((attr
= ppdFindAttr(ppd
, buffer
, NULL
)) == NULL
|| !attr
->value
)
211 fprintf(stderr
, "DEBUG: Skipping %s option...\n", option
->keyword
);
216 * Send the query code to the printer...
219 fprintf(stderr
, "DEBUG: Querying %s...\n", option
->keyword
);
221 for (bufptr
= buffer
, valptr
= attr
->value
; *valptr
; valptr
++)
224 * Log the query code, breaking at newlines...
230 fprintf(stderr
, "DEBUG: %s\\n\n", buffer
);
233 else if (*valptr
< ' ')
235 if (bufptr
>= (buffer
+ sizeof(buffer
) - 4))
238 fprintf(stderr
, "DEBUG: %s\n", buffer
);
247 else if (*valptr
== '\t')
255 *bufptr
++ = '0' + ((*valptr
/ 64) & 7);
256 *bufptr
++ = '0' + ((*valptr
/ 8) & 7);
257 *bufptr
++ = '0' + (*valptr
& 7);
262 if (bufptr
>= (buffer
+ sizeof(buffer
) - 1))
265 fprintf(stderr
, "DEBUG: %s\n", buffer
);
276 fprintf(stderr
, "DEBUG: %s\n", buffer
);
279 printf("/cups_query_keyword (?%s) def\n", option
->keyword
);
280 /* Set keyword for error reporting */
281 fputs("{ (", stdout
);
282 for (valptr
= attr
->value
; *valptr
; valptr
++)
284 if (*valptr
== '(' || *valptr
== ')' || *valptr
== '\\')
288 fputs(") cvx exec } stopped { cups_handleerror } if clear\n", stdout
);
289 /* Send query code */
293 cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT
, buffer
, &datalen
, 5.0);
296 * Read the response data...
301 while ((bytes
= cupsBackChannelRead(bufptr
, sizeof(buffer
) - (size_t)(bufptr
- buffer
) - 1, 10.0)) > 0)
304 * No newline at the end? Go on reading ...
311 (bufptr
> buffer
&& bufptr
[-1] != '\r' && bufptr
[-1] != '\n'))
315 * Trim whitespace and control characters from both ends...
318 bytes
= bufptr
- buffer
;
320 for (bufptr
--; bufptr
>= buffer
; bufptr
--)
321 if (isspace(*bufptr
& 255) || iscntrl(*bufptr
& 255))
326 for (bufptr
= buffer
; isspace(*bufptr
& 255) || iscntrl(*bufptr
& 255);
331 _cups_strcpy(buffer
, bufptr
);
335 fprintf(stderr
, "DEBUG: Got %d bytes.\n", (int)bytes
);
338 * Skip blank lines...
345 * Check the response...
348 if ((bufptr
= strchr(buffer
, ':')) != NULL
)
351 * PostScript code for this option in the PPD is broken; show the
352 * interpreter's error message that came back...
355 fprintf(stderr
, "DEBUG%s\n", bufptr
);
360 * Verify the result is a valid option choice...
363 if (!ppdFindChoice(option
, buffer
))
365 if (!strcasecmp(buffer
, "Unknown"))
374 * Write out the result and move on to the next option...
377 fprintf(stderr
, "PPD: Default%s=%s\n", option
->keyword
, buffer
);
382 * Printer did not answer this option's query
388 "DEBUG: No answer to query for option %s within 10 seconds.\n",
406 _cupsLangPrintFilter(stderr
, "WARNING",
407 _("Unable to configure printer options."));
414 * 'begin_ps()' - Send the standard PostScript prolog.
418 begin_ps(ppd_file_t
*ppd
, /* I - PPD file */
419 const char *user
) /* I - Username */
425 fputs(ppd
->jcl_begin
, stdout
);
426 fputs(ppd
->jcl_ps
, stdout
);
430 puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
437 * 'end_ps()' - Send the standard PostScript trailer.
441 end_ps(ppd_file_t
*ppd
) /* I - PPD file */
444 fputs(ppd
->jcl_end
, stdout
);
453 * 'print_self_test_page()' - Print a self-test page.
457 print_self_test_page(ppd_file_t
*ppd
, /* I - PPD file */
458 const char *user
) /* I - Printing user */
461 * Put the printer in PostScript mode...
467 * Send a simple file the draws a box around the imageable area and shows
468 * the product/interpreter information...
471 puts("\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
473 "\r%%%% If you can read this, you are using the wrong driver for your "
475 "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
479 "initclip newpath clippath gsave stroke grestore pathbbox\n"
480 "exch pop exch pop exch 9 add exch 9 sub moveto\n"
481 "/Courier findfont 12 scalefont setfont\n"
482 "0 -12 rmoveto gsave product show grestore\n"
483 "0 -12 rmoveto gsave version show ( ) show revision 20 string cvs show "
485 "0 -12 rmoveto gsave serialnumber 20 string cvs show grestore\n"
497 * 'report_levels()' - Report supply levels.
501 report_levels(ppd_file_t
*ppd
, /* I - PPD file */
502 const char *user
) /* I - Printing user */
505 * Put the printer in PostScript mode...
511 * Don't bother sending any additional PostScript commands, since we just
512 * want the backend to have enough time to collect the supply info.