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