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