Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/filter/commandtops.c
1090 views
1
/*
2
* PostScript command filter for CUPS.
3
*
4
* Copyright 2008-2014 by Apple Inc.
5
*
6
* Licensed under Apache License v2.0. See the file "LICENSE" for more information.
7
*/
8
9
/*
10
* Include necessary headers...
11
*/
12
13
#include <cups/cups-private.h>
14
#include <cups/ppd.h>
15
#include <cups/sidechannel.h>
16
17
18
/*
19
* Local functions...
20
*/
21
22
static int auto_configure(ppd_file_t *ppd, const char *user);
23
static void begin_ps(ppd_file_t *ppd, const char *user);
24
static void end_ps(ppd_file_t *ppd);
25
static void print_self_test_page(ppd_file_t *ppd, const char *user);
26
static void report_levels(ppd_file_t *ppd, const char *user);
27
28
29
/*
30
* 'main()' - Process a CUPS command file.
31
*/
32
33
int /* O - Exit status */
34
main(int argc, /* I - Number of command-line arguments */
35
char *argv[]) /* I - Command-line arguments */
36
{
37
int status = 0; /* Exit status */
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
56
_cupsLangPrintf(stderr,
57
_("Usage: %s job-id user title copies options [file]"),
58
argv[0]);
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
99
if (!_cups_strcasecmp(line, "AutoConfigure"))
100
status |= auto_configure(ppd, argv[2]);
101
else if (!_cups_strcasecmp(line, "PrintSelfTestPage"))
102
print_self_test_page(ppd, argv[2]);
103
else if (!_cups_strcasecmp(line, "ReportLevels"))
104
report_levels(ppd, argv[2]);
105
else
106
{
107
_cupsLangPrintFilter(stderr, "ERROR",
108
_("Invalid printer command \"%s\"."), line);
109
status = 1;
110
}
111
}
112
113
return (status);
114
}
115
116
117
/*
118
* 'auto_configure()' - Automatically configure the printer using PostScript
119
* query commands and/or SNMP lookups.
120
*/
121
122
static int /* O - Exit status */
123
auto_configure(ppd_file_t *ppd, /* I - PPD file */
124
const char *user) /* I - Printing user */
125
{
126
int status = 0; /* Exit status */
127
ppd_option_t *option; /* Current option in PPD */
128
ppd_attr_t *attr; /* Query command attribute */
129
const char *valptr; /* Pointer into attribute value */
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);
147
return (1);
148
}
149
150
/*
151
* Put the printer in PostScript mode...
152
*/
153
154
begin_ps(ppd, user);
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");
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]);
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);
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 */
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
293
bufptr = buffer;
294
buffer[0] = '\0';
295
while ((bytes = cupsBackChannelRead(bufptr, sizeof(buffer) - (size_t)(bufptr - buffer) - 1, 10.0)) > 0)
296
{
297
/*
298
* No newline at the end? Go on reading ...
299
*/
300
301
bufptr += bytes;
302
*bufptr = '\0';
303
304
if (bytes == 0 ||
305
(bufptr > buffer && bufptr[-1] != '\r' && bufptr[-1] != '\n'))
306
continue;
307
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))
316
*bufptr = '\0';
317
else
318
break;
319
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
}
328
329
fprintf(stderr, "DEBUG: Got %d bytes.\n", (int)bytes);
330
331
/*
332
* Skip blank lines...
333
*/
334
335
if (!buffer[0])
336
continue;
337
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
353
/*
354
* Verify the result is a valid option choice...
355
*/
356
357
if (!ppdFindChoice(option, buffer))
358
{
359
if (!strcasecmp(buffer, "Unknown"))
360
break;
361
362
bufptr = buffer;
363
buffer[0] = '\0';
364
continue;
365
}
366
367
/*
368
* Write out the result and move on to the next option...
369
*/
370
371
fprintf(stderr, "PPD: Default%s=%s\n", option->keyword, buffer);
372
break;
373
}
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
}
386
}
387
388
/*
389
* Finish the job...
390
*/
391
392
fflush(stdout);
393
end_ps(ppd);
394
395
/*
396
* Return...
397
*/
398
399
if (status)
400
_cupsLangPrintFilter(stderr, "WARNING",
401
_("Unable to configure printer options."));
402
403
return (0);
404
}
405
406
407
/*
408
* 'begin_ps()' - Send the standard PostScript prolog.
409
*/
410
411
static void
412
begin_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");
425
426
fflush(stdout);
427
}
428
429
430
/*
431
* 'end_ps()' - Send the standard PostScript trailer.
432
*/
433
434
static void
435
end_ps(ppd_file_t *ppd) /* I - PPD file */
436
{
437
if (ppd->jcl_end)
438
fputs(ppd->jcl_end, stdout);
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
450
static void
451
print_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
458
begin_ps(ppd, user);
459
460
/*
461
* Send a simple file the draws a box around the imageable area and shows
462
* the product/interpreter information...
463
*/
464
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"
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
486
end_ps(ppd);
487
}
488
489
490
/*
491
* 'report_levels()' - Report supply levels.
492
*/
493
494
static void
495
report_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
502
begin_ps(ppd, user);
503
504
/*
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.
507
*/
508
509
/*
510
* Finish the job...
511
*/
512
513
end_ps(ppd);
514
}
515
516