]> git.ipfire.org Git - thirdparty/cups.git/blame - filter/commandtops.c
Greatly simplify the man page handling.
[thirdparty/cups.git] / filter / commandtops.c
CommitLineData
7a14d768 1/*
7e86f2f6 2 * PostScript command filter for CUPS.
7a14d768 3 *
7e86f2f6 4 * Copyright 2008-2014 by Apple Inc.
7a14d768 5 *
e3101897 6 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
7a14d768
MS
7 */
8
9/*
10 * Include necessary headers...
11 */
12
0837b7e8 13#include <cups/cups-private.h>
aaf19ab0 14#include <cups/ppd.h>
7a14d768
MS
15#include <cups/sidechannel.h>
16
17
18/*
19 * Local functions...
20 */
21
82cc1f9a 22static int auto_configure(ppd_file_t *ppd, const char *user);
4509bb49
MS
23static void begin_ps(ppd_file_t *ppd, const char *user);
24static void end_ps(ppd_file_t *ppd);
7a14d768
MS
25static void print_self_test_page(ppd_file_t *ppd, const char *user);
26static void report_levels(ppd_file_t *ppd, const char *user);
27
28
29/*
30 * 'main()' - Process a CUPS command file.
31 */
32
33int /* O - Exit status */
34main(int argc, /* I - Number of command-line arguments */
35 char *argv[]) /* I - Command-line arguments */
36{
82cc1f9a 37 int status = 0; /* Exit status */
7a14d768
MS
38 cups_file_t *fp; /* Command file */
39 char line[1024], /* Line from file */
40 *value; /* Value on line */
41 int linenum; /* Line number in file */
42 ppd_file_t *ppd; /* PPD file */
43
44
45 /*
46 * Check for valid arguments...
47 */
48
49 if (argc < 6 || argc > 7)
50 {
51 /*
52 * We don't have the correct number of arguments; write an error message
53 * and return.
54 */
55
0837b7e8 56 _cupsLangPrintf(stderr,
f3c17241 57 _("Usage: %s job-id user title copies options [file]"),
0837b7e8 58 argv[0]);
7a14d768
MS
59 return (1);
60 }
61
62 /*
63 * Open the PPD file...
64 */
65
66 if ((ppd = ppdOpenFile(getenv("PPD"))) == NULL)
67 {
68 fputs("ERROR: Unable to open PPD file!\n", stderr);
69 return (1);
70 }
71
72 /*
73 * Open the command file as needed...
74 */
75
76 if (argc == 7)
77 {
78 if ((fp = cupsFileOpen(argv[6], "r")) == NULL)
79 {
80 perror("ERROR: Unable to open command file - ");
81 return (1);
82 }
83 }
84 else
85 fp = cupsFileStdin();
86
87 /*
88 * Read the commands from the file and send the appropriate commands...
89 */
90
91 linenum = 0;
92
93 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
94 {
95 /*
96 * Parse the command...
97 */
98
88f9aafc 99 if (!_cups_strcasecmp(line, "AutoConfigure"))
82cc1f9a 100 status |= auto_configure(ppd, argv[2]);
88f9aafc 101 else if (!_cups_strcasecmp(line, "PrintSelfTestPage"))
7a14d768 102 print_self_test_page(ppd, argv[2]);
88f9aafc 103 else if (!_cups_strcasecmp(line, "ReportLevels"))
7a14d768
MS
104 report_levels(ppd, argv[2]);
105 else
82cc1f9a
MS
106 {
107 _cupsLangPrintFilter(stderr, "ERROR",
108 _("Invalid printer command \"%s\"."), line);
109 status = 1;
110 }
7a14d768
MS
111 }
112
82cc1f9a 113 return (status);
7a14d768
MS
114}
115
116
117/*
118 * 'auto_configure()' - Automatically configure the printer using PostScript
119 * query commands and/or SNMP lookups.
120 */
121
82cc1f9a 122static int /* O - Exit status */
7a14d768
MS
123auto_configure(ppd_file_t *ppd, /* I - PPD file */
124 const char *user) /* I - Printing user */
125{
82cc1f9a 126 int status = 0; /* Exit status */
7a14d768
MS
127 ppd_option_t *option; /* Current option in PPD */
128 ppd_attr_t *attr; /* Query command attribute */
82cc1f9a 129 const char *valptr; /* Pointer into attribute value */
7a14d768
MS
130 char buffer[1024], /* String buffer */
131 *bufptr; /* Pointer into buffer */
132 ssize_t bytes; /* Number of bytes read */
133 int datalen; /* Side-channel data length */
134
135
136 /*
137 * See if the backend supports bidirectional I/O...
138 */
139
140 datalen = 1;
141 if (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer, &datalen,
142 30.0) != CUPS_SC_STATUS_OK ||
143 buffer[0] != CUPS_SC_BIDI_SUPPORTED)
144 {
145 fputs("DEBUG: Unable to auto-configure PostScript Printer - no "
146 "bidirectional I/O available!\n", stderr);
82cc1f9a 147 return (1);
7a14d768
MS
148 }
149
150 /*
151 * Put the printer in PostScript mode...
152 */
153
4509bb49 154 begin_ps(ppd, user);
82cc1f9a
MS
155
156 /*
157 * (STR #4028)
158 *
159 * As a lot of PPDs contain bad PostScript query code, we need to prevent one
160 * bad query sequence from affecting all auto-configuration. The following
161 * error handler allows us to log PostScript errors to cupsd.
162 */
163
164 puts("/cups_handleerror {\n"
165 " $error /newerror false put\n"
166 " (:PostScript error in \") print cups_query_keyword print (\": ) "
167 "print\n"
168 " $error /errorname get 128 string cvs print\n"
169 " (; offending command:) print $error /command get 128 string cvs "
170 "print (\n) print flush\n"
171 "} bind def\n"
172 "errordict /timeout {} put\n"
173 "/cups_query_keyword (?Unknown) def\n");
f14324a7
MS
174 fflush(stdout);
175
176 /*
177 * Wait for the printer to become connected...
178 */
179
180 do
181 {
182 sleep(1);
183 datalen = 1;
184 }
185 while (cupsSideChannelDoRequest(CUPS_SC_CMD_GET_CONNECTED, buffer, &datalen,
186 5.0) == CUPS_SC_STATUS_OK && !buffer[0]);
7a14d768
MS
187
188 /*
189 * Then loop through every option in the PPD file and ask for the current
190 * value...
191 */
192
193 fputs("DEBUG: Auto-configuring PostScript printer...\n", stderr);
194
195 for (option = ppdFirstOption(ppd); option; option = ppdNextOption(ppd))
196 {
197 /*
198 * See if we have a query command for this option...
199 */
200
201 snprintf(buffer, sizeof(buffer), "?%s", option->keyword);
202
203 if ((attr = ppdFindAttr(ppd, buffer, NULL)) == NULL || !attr->value)
204 {
205 fprintf(stderr, "DEBUG: Skipping %s option...\n", option->keyword);
206 continue;
207 }
208
209 /*
210 * Send the query code to the printer...
211 */
212
213 fprintf(stderr, "DEBUG: Querying %s...\n", option->keyword);
82cc1f9a
MS
214
215 for (bufptr = buffer, valptr = attr->value; *valptr; valptr ++)
216 {
217 /*
218 * Log the query code, breaking at newlines...
219 */
220
221 if (*valptr == '\n')
222 {
223 *bufptr = '\0';
224 fprintf(stderr, "DEBUG: %s\\n\n", buffer);
225 bufptr = buffer;
226 }
227 else if (*valptr < ' ')
228 {
229 if (bufptr >= (buffer + sizeof(buffer) - 4))
230 {
231 *bufptr = '\0';
232 fprintf(stderr, "DEBUG: %s\n", buffer);
233 bufptr = buffer;
234 }
235
236 if (*valptr == '\r')
237 {
238 *bufptr++ = '\\';
239 *bufptr++ = 'r';
240 }
241 else if (*valptr == '\t')
242 {
243 *bufptr++ = '\\';
244 *bufptr++ = 't';
245 }
246 else
247 {
248 *bufptr++ = '\\';
249 *bufptr++ = '0' + ((*valptr / 64) & 7);
250 *bufptr++ = '0' + ((*valptr / 8) & 7);
251 *bufptr++ = '0' + (*valptr & 7);
252 }
253 }
254 else
255 {
256 if (bufptr >= (buffer + sizeof(buffer) - 1))
257 {
258 *bufptr = '\0';
259 fprintf(stderr, "DEBUG: %s\n", buffer);
260 bufptr = buffer;
261 }
262
263 *bufptr++ = *valptr;
264 }
265 }
266
267 if (bufptr > buffer)
268 {
269 *bufptr = '\0';
270 fprintf(stderr, "DEBUG: %s\n", buffer);
271 }
272
273 printf("/cups_query_keyword (?%s) def\n", option->keyword);
274 /* Set keyword for error reporting */
275 fputs("{ (", stdout);
276 for (valptr = attr->value; *valptr; valptr ++)
277 {
278 if (*valptr == '(' || *valptr == ')' || *valptr == '\\')
279 putchar('\\');
280 putchar(*valptr);
281 }
282 fputs(") cvx exec } stopped { cups_handleerror } if clear\n", stdout);
283 /* Send query code */
7a14d768
MS
284 fflush(stdout);
285
286 datalen = 0;
287 cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer, &datalen, 5.0);
288
289 /*
290 * Read the response data...
291 */
292
82cc1f9a
MS
293 bufptr = buffer;
294 buffer[0] = '\0';
7e86f2f6 295 while ((bytes = cupsBackChannelRead(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer) - 1, 10.0)) > 0)
7a14d768
MS
296 {
297 /*
82cc1f9a 298 * No newline at the end? Go on reading ...
7a14d768
MS
299 */
300
82cc1f9a
MS
301 bufptr += bytes;
302 *bufptr = '\0';
303
304 if (bytes == 0 ||
305 (bufptr > buffer && bufptr[-1] != '\r' && bufptr[-1] != '\n'))
306 continue;
7a14d768 307
82cc1f9a
MS
308 /*
309 * Trim whitespace and control characters from both ends...
310 */
311
312 bytes = bufptr - buffer;
313
314 for (bufptr --; bufptr >= buffer; bufptr --)
315 if (isspace(*bufptr & 255) || iscntrl(*bufptr & 255))
7a14d768
MS
316 *bufptr = '\0';
317 else
318 break;
319
82cc1f9a
MS
320 for (bufptr = buffer; isspace(*bufptr & 255) || iscntrl(*bufptr & 255);
321 bufptr ++);
322
323 if (bufptr > buffer)
324 {
325 _cups_strcpy(buffer, bufptr);
326 bufptr = buffer;
327 }
7a14d768 328
82cc1f9a 329 fprintf(stderr, "DEBUG: Got %d bytes.\n", (int)bytes);
f14324a7 330
7a14d768
MS
331 /*
332 * Skip blank lines...
333 */
334
82cc1f9a 335 if (!buffer[0])
7a14d768
MS
336 continue;
337
82cc1f9a
MS
338 /*
339 * Check the response...
340 */
341
342 if ((bufptr = strchr(buffer, ':')) != NULL)
343 {
344 /*
345 * PostScript code for this option in the PPD is broken; show the
346 * interpreter's error message that came back...
347 */
348
349 fprintf(stderr, "DEBUG%s\n", bufptr);
350 break;
351 }
352
f14324a7
MS
353 /*
354 * Verify the result is a valid option choice...
355 */
356
82cc1f9a
MS
357 if (!ppdFindChoice(option, buffer))
358 {
359 if (!strcasecmp(buffer, "Unknown"))
360 break;
361
362 bufptr = buffer;
363 buffer[0] = '\0';
f14324a7 364 continue;
82cc1f9a 365 }
f14324a7 366
7a14d768
MS
367 /*
368 * Write out the result and move on to the next option...
369 */
370
82cc1f9a 371 fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, buffer);
7a14d768
MS
372 break;
373 }
82cc1f9a
MS
374
375 /*
376 * Printer did not answer this option's query
377 */
378
379 if (bytes <= 0)
380 {
381 fprintf(stderr,
382 "DEBUG: No answer to query for option %s within 10 seconds.\n",
383 option->keyword);
384 status = 1;
385 }
7a14d768
MS
386 }
387
388 /*
389 * Finish the job...
390 */
391
82cc1f9a 392 fflush(stdout);
4509bb49 393 end_ps(ppd);
82cc1f9a
MS
394
395 /*
396 * Return...
397 */
398
399 if (status)
400 _cupsLangPrintFilter(stderr, "WARNING",
401 _("Unable to configure printer options."));
402
403 return (0);
4509bb49
MS
404}
405
406
407/*
408 * 'begin_ps()' - Send the standard PostScript prolog.
409 */
410
411static void
412begin_ps(ppd_file_t *ppd, /* I - PPD file */
413 const char *user) /* I - Username */
414{
415 (void)user;
416
417 if (ppd->jcl_begin)
418 {
419 fputs(ppd->jcl_begin, stdout);
420 fputs(ppd->jcl_ps, stdout);
421 }
422
423 puts("%!");
424 puts("userdict dup(\\004)cvn{}put (\\004\\004)cvn{}put\n");
82cc1f9a 425
4509bb49
MS
426 fflush(stdout);
427}
428
429
430/*
431 * 'end_ps()' - Send the standard PostScript trailer.
432 */
433
434static void
435end_ps(ppd_file_t *ppd) /* I - PPD file */
436{
c168a833
MS
437 if (ppd->jcl_end)
438 fputs(ppd->jcl_end, stdout);
7a14d768
MS
439 else
440 putchar(0x04);
441
442 fflush(stdout);
443}
444
445
446/*
447 * 'print_self_test_page()' - Print a self-test page.
448 */
449
450static void
451print_self_test_page(ppd_file_t *ppd, /* I - PPD file */
452 const char *user) /* I - Printing user */
453{
454 /*
455 * Put the printer in PostScript mode...
456 */
457
4509bb49 458 begin_ps(ppd, user);
7a14d768
MS
459
460 /*
461 * Send a simple file the draws a box around the imageable area and shows
462 * the product/interpreter information...
463 */
464
82cc1f9a
MS
465 puts("\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
466 "%%%%%%%%%%%%%\n"
467 "\r%%%% If you can read this, you are using the wrong driver for your "
468 "printer. %%%%\n"
469 "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"
470 "%%%%%%%%%%%%%\n"
7a14d768
MS
471 "0 setgray\n"
472 "2 setlinewidth\n"
473 "initclip newpath clippath gsave stroke grestore pathbbox\n"
474 "exch pop exch pop exch 9 add exch 9 sub moveto\n"
475 "/Courier findfont 12 scalefont setfont\n"
476 "0 -12 rmoveto gsave product show grestore\n"
477 "0 -12 rmoveto gsave version show ( ) show revision 20 string cvs show "
478 "grestore\n"
479 "0 -12 rmoveto gsave serialnumber 20 string cvs show grestore\n"
480 "showpage");
481
482 /*
483 * Finish the job...
484 */
485
4509bb49 486 end_ps(ppd);
7a14d768
MS
487}
488
489
490/*
491 * 'report_levels()' - Report supply levels.
492 */
493
494static void
495report_levels(ppd_file_t *ppd, /* I - PPD file */
496 const char *user) /* I - Printing user */
497{
498 /*
499 * Put the printer in PostScript mode...
500 */
501
4509bb49 502 begin_ps(ppd, user);
7a14d768 503
75bd9771 504 /*
4509bb49
MS
505 * Don't bother sending any additional PostScript commands, since we just
506 * want the backend to have enough time to collect the supply info.
75bd9771
MS
507 */
508
7a14d768
MS
509 /*
510 * Finish the job...
511 */
512
4509bb49 513 end_ps(ppd);
7a14d768 514}