2 * "$Id: commandtops.c 10373 2012-03-21 23:00:05Z mike $"
4 * PostScript command filter for CUPS.
6 * Copyright 2008-2012 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/".
17 * main() - Process a CUPS command file.
18 * auto_configure() - Automatically configure the printer using
19 * PostScript query commands and/or SNMP lookups.
20 * begin_ps() - Send the standard PostScript prolog.
21 * end_ps() - Send the standard PostScript trailer.
22 * print_self_test_page() - Print a self-test page.
23 * report_levels() - Report supply levels.
27 * Include necessary headers...
30 #include <cups/cups-private.h>
32 #include <cups/sidechannel.h>
39 static int auto_configure(ppd_file_t *ppd, const char *user);
40 static void begin_ps(ppd_file_t *ppd, const char *user);
41 static void end_ps(ppd_file_t *ppd);
42 static void print_self_test_page(ppd_file_t *ppd, const char *user);
43 static void report_levels(ppd_file_t *ppd, const char *user);
47 * 'main()' - Process a CUPS command file.
50 int /* O - Exit status */
51 main(int argc, /* I - Number of command-line arguments */
52 char *argv[]) /* I - Command-line arguments */
54 int status = 0; /* Exit status */
55 cups_file_t *fp; /* Command file */
56 char line[1024], /* Line from file */
57 *value; /* Value on line */
58 int linenum; /* Line number in file */
59 ppd_file_t *ppd; /* PPD file */
63 * Check for valid arguments...
66 if (argc < 6 || argc > 7)
69 * We don't have the correct number of arguments; write an error message
73 _cupsLangPrintf(stderr,
74 _("Usage: %s job-id user title copies options file"),
80 * Open the PPD file...
83 if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL)
85 fputs("ERROR: Unable to open PPD file!\n", stderr);
90 * Open the command file as needed...
95 if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
97 perror("ERROR: Unable to open command file - ");
102 fp = cupsFileStdin();
105 * Read the commands from the file and send the appropriate commands...
110 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
113 * Parse the command...
116 if (!_cups_strcasecmp(line, "AutoConfigure"))
117 status |= auto_configure(ppd, argv[2]);
118 else if (!_cups_strcasecmp(line, "PrintSelfTestPage"))
119 print_self_test_page(ppd, argv[2]);
120 else if (!_cups_strcasecmp(line, "ReportLevels"))
121 report_levels(ppd, argv[2]);
124 _cupsLangPrintFilter(stderr, "ERROR",
125 _("Invalid printer command \"%s\"."), line);
135 * 'auto_configure()' - Automatically configure the printer using PostScript
136 * query commands and/or SNMP lookups.
139 static int /* O - Exit status */
140 auto_configure(ppd_file_t *ppd, /* I - PPD file */
141 const char *user) /* I - Printing user */
143 int status = 0; /* Exit status */
144 ppd_option_t *option; /* Current option in PPD */
145 ppd_attr_t *attr; /* Query command attribute */
146 const char *valptr; /* Pointer into attribute value */
147 char buffer[1024], /* String buffer */
148 *bufptr; /* Pointer into buffer */
149 ssize_t bytes; /* Number of bytes read */
150 int datalen; /* Side-channel data length */
154 * See if the backend supports bidirectional I/O...
158 if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer, &datalen,
159 30.0) != CUPS_SC_STATUS_OK ||
160 buffer[0] != CUPS_SC_BIDI_SUPPORTED)
162 fputs("DEBUG: Unable to auto-configure PostScript Printer - no "
163 "bidirectional I/O available!\n", stderr);
168 * Put the printer in PostScript mode...
176 * As a lot of PPDs contain bad PostScript query code, we need to prevent one
177 * bad query sequence from affecting all auto-configuration. The following
178 * error handler allows us to log PostScript errors to cupsd.
181 puts("/cups_handleerror {\n"
182 " $error /newerror false put\n"
183 " (:PostScript error in \") print cups_query_keyword print (\": ) "
185 " $error /errorname get 128 string cvs print\n"
186 " (; offending command:) print $error /command get 128 string cvs "
187 "print (\n) print flush\n"
189 "errordict /timeout {} put\n"
190 "/cups_query_keyword (?Unknown) def\n");
194 * Wait for the printer to become connected...
202 while (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_CONNECTED, buffer, &datalen,
203 5.0) == CUPS_SC_STATUS_OK && !buffer[0]);
206 * Then loop through every option in the PPD file and ask for the current
210 fputs("DEBUG: Auto-configuring PostScript printer...\n", stderr);
212 for (option = ppdFirstOption(ppd); option; option = ppdNextOption(ppd))
215 * See if we have a query command for this option...
218 snprintf(buffer, sizeof(buffer), "?%s", option->keyword);
220 if ((attr = ppdFindAttr(ppd, buffer, NULL)) == NULL || !attr->value)
222 fprintf(stderr, "DEBUG: Skipping %s option...\n", option->keyword);
227 * Send the query code to the printer...
230 fprintf(stderr, "DEBUG: Querying %s...\n", option->keyword);
232 for (bufptr = buffer, valptr = attr->value; *valptr; valptr ++)
235 * Log the query code, breaking at newlines...
241 fprintf(stderr, "DEBUG: %s\\n\n", buffer);
244 else if (*valptr < ' ')
246 if (bufptr >= (buffer + sizeof(buffer) - 4))
249 fprintf(stderr, "DEBUG: %s\n", buffer);
258 else if (*valptr == '\t')
266 *bufptr++ = '0' + ((*valptr / 64) & 7);
267 *bufptr++ = '0' + ((*valptr / 8) & 7);
268 *bufptr++ = '0' + (*valptr & 7);
273 if (bufptr >= (buffer + sizeof(buffer) - 1))
276 fprintf(stderr, "DEBUG: %s\n", buffer);
287 fprintf(stderr, "DEBUG: %s\n", buffer);
290 printf("/cups_query_keyword (?%s) def\n", option->keyword);
291 /* Set keyword for error reporting */
292 fputs("{ (", stdout);
293 for (valptr = attr->value; *valptr; valptr ++)
295 if (*valptr == '(' || *valptr == ')' || *valptr == '\\')
299 fputs(") cvx exec } stopped { cups_handleerror } if clear\n", stdout);
300 /* Send query code */
304 cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer, &datalen, 5.0);
307 * Read the response data...
312 while ((bytes = cupsBackChannelRead(bufptr,
313 sizeof(buffer) - (bufptr - buffer) - 1,
317 * No newline at the end? Go on reading ...
324 (bufptr > buffer && bufptr[-1] != '\r' && bufptr[-1] != '\n'))
328 * Trim whitespace and control characters from both ends...
331 bytes = bufptr - buffer;
333 for (bufptr --; bufptr >= buffer; bufptr --)
334 if (isspace(*bufptr & 255) || iscntrl(*bufptr & 255))
339 for (bufptr = buffer; isspace(*bufptr & 255) || iscntrl(*bufptr & 255);
344 _cups_strcpy(buffer, bufptr);
348 fprintf(stderr, "DEBUG: Got %d bytes.\n", (int)bytes);
351 * Skip blank lines...
358 * Check the response...
361 if ((bufptr = strchr(buffer, ':')) != NULL)
364 * PostScript code for this option in the PPD is broken; show the
365 * interpreter's error message that came back...
368 fprintf(stderr, "DEBUG%s\n", bufptr);
373 * Verify the result is a valid option choice...
376 if (!ppdFindChoice(option, buffer))
378 if (!strcasecmp(buffer, "Unknown"))
387 * Write out the result and move on to the next option...
390 fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, buffer);
395 * Printer did not answer this option's query
401 "DEBUG: No answer to query for option %s within 10 seconds.\n",
419 _cupsLangPrintFilter(stderr, "WARNING",
420 _("Unable to configure printer options."));
427 * 'begin_ps()' - Send the standard PostScript prolog.
431 begin_ps(ppd_file_t *ppd, /* I - PPD file */
432 const char *user) /* I - Username */
438 fputs(ppd->jcl_begin, stdout);
439 fputs(ppd->jcl_ps, stdout);
443 puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
450 * 'end_ps()' - Send the standard PostScript trailer.
454 end_ps(ppd_file_t *ppd) /* I - PPD file */
457 fputs(ppd->jcl_end, stdout);
466 * 'print_self_test_page()' - Print a self-test page.
470 print_self_test_page(ppd_file_t *ppd, /* I - PPD file */
471 const char *user) /* I - Printing user */
474 * Put the printer in PostScript mode...
480 * Send a simple file the draws a box around the imageable area and shows
481 * the product/interpreter information...
484 puts("\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
486 "\r%%%% If you can read this, you are using the wrong driver for your "
488 "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
492 "initclip newpath clippath gsave stroke grestore pathbbox\n"
493 "exch pop exch pop exch 9 add exch 9 sub moveto\n"
494 "/Courier findfont 12 scalefont setfont\n"
495 "0 -12 rmoveto gsave product show grestore\n"
496 "0 -12 rmoveto gsave version show ( ) show revision 20 string cvs show "
498 "0 -12 rmoveto gsave serialnumber 20 string cvs show grestore\n"
510 * 'report_levels()' - Report supply levels.
514 report_levels(ppd_file_t *ppd, /* I - PPD file */
515 const char *user) /* I - Printing user */
518 * Put the printer in PostScript mode...
524 * Don't bother sending any additional PostScript commands, since we just
525 * want the backend to have enough time to collect the supply info.
537 * End of "$Id: commandtops.c 10373 2012-03-21 23:00:05Z mike $".