]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
7e86f2f6 | 2 | * Printer option program for CUPS. |
ef416fc2 | 3 | * |
f787e1e3 | 4 | * Copyright 2007-2015 by Apple Inc. |
7e86f2f6 | 5 | * Copyright 1997-2006 by Easy Software Products. |
ef416fc2 | 6 | * |
7e86f2f6 MS |
7 | * These coded instructions, statements, and computer programs are the |
8 | * property of Apple Inc. and are protected by Federal copyright | |
9 | * law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
10 | * which should have been included with this file. If this file is | |
11 | * file is missing or damaged, see the license at "http://www.cups.org/". | |
ef416fc2 | 12 | */ |
13 | ||
14 | /* | |
15 | * Include necessary headers... | |
16 | */ | |
17 | ||
71e16022 | 18 | #include <cups/cups-private.h> |
f787e1e3 | 19 | #include <cups/ppd-private.h> |
ef416fc2 | 20 | |
21 | ||
22 | /* | |
23 | * Local functions... | |
24 | */ | |
25 | ||
dd1abb6b MS |
26 | static void list_group(ppd_file_t *ppd, ppd_group_t *group); |
27 | static void list_options(cups_dest_t *dest); | |
85dda01c | 28 | static void usage(void) __attribute__((noreturn)); |
ef416fc2 | 29 | |
30 | ||
31 | /* | |
32 | * 'main()' - Main entry. | |
33 | */ | |
34 | ||
35 | int /* O - Exit status */ | |
36 | main(int argc, /* I - Number of command-line arguments */ | |
37 | char *argv[]) /* I - Command-line arguments */ | |
38 | { | |
39 | int i, j; /* Looping vars */ | |
40 | int changes; /* Did we make changes? */ | |
41 | int num_options; /* Number of options */ | |
42 | cups_option_t *options; /* Options */ | |
43 | int num_dests; /* Number of destinations */ | |
44 | cups_dest_t *dests; /* Destinations */ | |
45 | cups_dest_t *dest; /* Current destination */ | |
46 | char *printer, /* Printer name */ | |
88f9aafc | 47 | *instance, /* Instance name */ |
ef416fc2 | 48 | *option; /* Current option */ |
49 | ||
50 | ||
07725fee | 51 | _cupsSetLocale(argv); |
d09495fa | 52 | |
ef416fc2 | 53 | /* |
54 | * Loop through the command-line arguments... | |
55 | */ | |
56 | ||
57 | dest = NULL; | |
58 | num_dests = 0; | |
59 | dests = NULL; | |
60 | num_options = 0; | |
61 | options = NULL; | |
62 | changes = 0; | |
63 | ||
64 | for (i = 1; i < argc; i ++) | |
65 | if (argv[i][0] == '-') | |
66 | { | |
67 | switch (argv[i][1]) | |
68 | { | |
69 | case 'd' : /* -d printer */ | |
70 | if (argv[i][2]) | |
71 | printer = argv[i] + 2; | |
72 | else | |
73 | { | |
74 | i ++; | |
75 | if (i >= argc) | |
76 | usage(); | |
77 | ||
78 | printer = argv[i]; | |
79 | } | |
80 | ||
81 | if ((instance = strrchr(printer, '/')) != NULL) | |
82 | *instance++ = '\0'; | |
83 | ||
84 | if (num_dests == 0) | |
85 | num_dests = cupsGetDests(&dests); | |
86 | ||
1f0275e3 MS |
87 | if (num_dests == 0 || !dests || |
88 | (dest = cupsGetDest(printer, instance, num_dests, | |
89 | dests)) == NULL) | |
ef416fc2 | 90 | { |
0837b7e8 | 91 | _cupsLangPuts(stderr, _("lpoptions: Unknown printer or class.")); |
ef416fc2 | 92 | return (1); |
93 | } | |
94 | ||
95 | /* | |
96 | * Set the default destination... | |
97 | */ | |
98 | ||
99 | for (j = 0; j < num_dests; j ++) | |
100 | dests[j].is_default = 0; | |
101 | ||
102 | dest->is_default = 1; | |
103 | ||
104 | cupsSetDests(num_dests, dests); | |
105 | ||
106 | for (j = 0; j < dest->num_options; j ++) | |
0837b7e8 MS |
107 | if (cupsGetOption(dest->options[j].name, num_options, |
108 | options) == NULL) | |
ef416fc2 | 109 | num_options = cupsAddOption(dest->options[j].name, |
110 | dest->options[j].value, | |
111 | num_options, &options); | |
112 | break; | |
113 | ||
114 | case 'h' : /* -h server */ | |
115 | if (argv[i][2]) | |
116 | cupsSetServer(argv[i] + 2); | |
117 | else | |
118 | { | |
119 | i ++; | |
120 | if (i >= argc) | |
121 | usage(); | |
122 | ||
123 | cupsSetServer(argv[i]); | |
124 | } | |
125 | break; | |
126 | ||
127 | case 'E' : /* Encrypt connection */ | |
128 | cupsSetEncryption(HTTP_ENCRYPT_REQUIRED); | |
129 | break; | |
130 | ||
131 | case 'l' : /* -l (list options) */ | |
132 | if (dest == NULL) | |
133 | { | |
134 | if (num_dests == 0) | |
135 | num_dests = cupsGetDests(&dests); | |
136 | ||
137 | if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) | |
138 | dest = dests; | |
139 | } | |
140 | ||
141 | if (dest == NULL) | |
0837b7e8 | 142 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
ef416fc2 | 143 | else |
144 | list_options(dest); | |
145 | ||
146 | changes = -1; | |
147 | break; | |
148 | ||
149 | case 'o' : /* -o option[=value] */ | |
8ca02f3c | 150 | if (dest == NULL) |
151 | { | |
152 | if (num_dests == 0) | |
153 | num_dests = cupsGetDests(&dests); | |
154 | ||
155 | if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) | |
156 | dest = dests; | |
157 | ||
7594b224 | 158 | if (dest == NULL) |
159 | { | |
0837b7e8 | 160 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
7594b224 | 161 | return (1); |
162 | } | |
163 | ||
8ca02f3c | 164 | for (j = 0; j < dest->num_options; j ++) |
165 | if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) | |
166 | num_options = cupsAddOption(dest->options[j].name, | |
167 | dest->options[j].value, | |
168 | num_options, &options); | |
169 | } | |
170 | ||
ef416fc2 | 171 | if (argv[i][2]) |
172 | num_options = cupsParseOptions(argv[i] + 2, num_options, &options); | |
173 | else | |
174 | { | |
175 | i ++; | |
176 | if (i >= argc) | |
177 | usage(); | |
178 | ||
179 | num_options = cupsParseOptions(argv[i], num_options, &options); | |
180 | } | |
181 | ||
182 | changes = 1; | |
183 | break; | |
184 | ||
185 | case 'p' : /* -p printer */ | |
186 | if (argv[i][2]) | |
187 | printer = argv[i] + 2; | |
188 | else | |
189 | { | |
190 | i ++; | |
191 | if (i >= argc) | |
192 | usage(); | |
193 | ||
194 | printer = argv[i]; | |
195 | } | |
196 | ||
197 | if ((instance = strrchr(printer, '/')) != NULL) | |
198 | *instance++ = '\0'; | |
199 | ||
200 | if (num_dests == 0) | |
201 | num_dests = cupsGetDests(&dests); | |
202 | ||
203 | if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL) | |
204 | { | |
205 | num_dests = cupsAddDest(printer, instance, num_dests, &dests); | |
206 | dest = cupsGetDest(printer, instance, num_dests, dests); | |
207 | ||
208 | if (dest == NULL) | |
209 | { | |
fa73b229 | 210 | _cupsLangPrintf(stderr, |
ef416fc2 | 211 | _("lpoptions: Unable to add printer or " |
0837b7e8 | 212 | "instance: %s"), |
ef416fc2 | 213 | strerror(errno)); |
214 | return (1); | |
215 | } | |
216 | } | |
217 | ||
218 | for (j = 0; j < dest->num_options; j ++) | |
219 | if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) | |
220 | num_options = cupsAddOption(dest->options[j].name, | |
221 | dest->options[j].value, | |
222 | num_options, &options); | |
223 | break; | |
224 | ||
225 | case 'r' : /* -r option (remove) */ | |
8ca02f3c | 226 | if (dest == NULL) |
227 | { | |
228 | if (num_dests == 0) | |
229 | num_dests = cupsGetDests(&dests); | |
230 | ||
231 | if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) == NULL) | |
232 | dest = dests; | |
233 | ||
7594b224 | 234 | if (dest == NULL) |
235 | { | |
0837b7e8 | 236 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); |
7594b224 | 237 | return (1); |
238 | } | |
239 | ||
8ca02f3c | 240 | for (j = 0; j < dest->num_options; j ++) |
0837b7e8 MS |
241 | if (cupsGetOption(dest->options[j].name, num_options, |
242 | options) == NULL) | |
8ca02f3c | 243 | num_options = cupsAddOption(dest->options[j].name, |
244 | dest->options[j].value, | |
245 | num_options, &options); | |
246 | } | |
247 | ||
ef416fc2 | 248 | if (argv[i][2]) |
249 | option = argv[i] + 2; | |
250 | else | |
251 | { | |
252 | i ++; | |
253 | if (i >= argc) | |
254 | usage(); | |
255 | ||
256 | option = argv[i]; | |
257 | } | |
258 | ||
259 | for (j = 0; j < num_options; j ++) | |
88f9aafc | 260 | if (!_cups_strcasecmp(options[j].name, option)) |
ef416fc2 | 261 | { |
262 | /* | |
263 | * Remove this option... | |
264 | */ | |
265 | ||
266 | num_options --; | |
267 | ||
268 | if (j < num_options) | |
7e86f2f6 | 269 | memmove(options + j, options + j + 1, sizeof(cups_option_t) * (size_t)(num_options - j)); |
ef416fc2 | 270 | break; |
271 | } | |
272 | ||
273 | changes = 1; | |
274 | break; | |
275 | ||
276 | case 'x' : /* -x printer */ | |
277 | if (argv[i][2]) | |
278 | printer = argv[i] + 2; | |
279 | else | |
280 | { | |
281 | i ++; | |
282 | if (i >= argc) | |
283 | usage(); | |
284 | ||
285 | printer = argv[i]; | |
286 | } | |
287 | ||
288 | if ((instance = strrchr(printer, '/')) != NULL) | |
289 | *instance++ = '\0'; | |
290 | ||
291 | if (num_dests == 0) | |
292 | num_dests = cupsGetDests(&dests); | |
293 | ||
0837b7e8 MS |
294 | if ((dest = cupsGetDest(printer, instance, num_dests, |
295 | dests)) != NULL) | |
ef416fc2 | 296 | { |
297 | cupsFreeOptions(dest->num_options, dest->options); | |
298 | ||
299 | /* | |
300 | * If we are "deleting" the default printer, then just set the | |
301 | * number of options to 0; if it is also the system default | |
302 | * then cupsSetDests() will remove it for us... | |
303 | */ | |
304 | ||
305 | if (dest->is_default) | |
306 | { | |
307 | dest->num_options = 0; | |
308 | dest->options = NULL; | |
309 | } | |
310 | else | |
311 | { | |
312 | num_dests --; | |
313 | ||
314 | j = dest - dests; | |
315 | if (j < num_dests) | |
7e86f2f6 | 316 | memmove(dest, dest + 1, (size_t)(num_dests - j) * sizeof(cups_dest_t)); |
ef416fc2 | 317 | } |
318 | } | |
319 | ||
320 | cupsSetDests(num_dests, dests); | |
321 | dest = NULL; | |
322 | changes = -1; | |
323 | break; | |
324 | ||
325 | default : | |
326 | usage(); | |
327 | } | |
328 | } | |
329 | else | |
330 | usage(); | |
331 | ||
332 | if (num_dests == 0) | |
333 | num_dests = cupsGetDests(&dests); | |
334 | ||
335 | if (dest == NULL) | |
336 | { | |
337 | if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL) | |
338 | { | |
339 | for (j = 0; j < dest->num_options; j ++) | |
340 | if (cupsGetOption(dest->options[j].name, num_options, options) == NULL) | |
341 | num_options = cupsAddOption(dest->options[j].name, | |
342 | dest->options[j].value, | |
343 | num_options, &options); | |
344 | } | |
345 | } | |
346 | ||
347 | if (dest == NULL) | |
348 | return (0); | |
349 | ||
350 | if (changes > 0) | |
351 | { | |
352 | /* | |
353 | * Set printer options... | |
354 | */ | |
355 | ||
356 | cupsFreeOptions(dest->num_options, dest->options); | |
357 | ||
358 | dest->num_options = num_options; | |
359 | dest->options = options; | |
360 | ||
361 | cupsSetDests(num_dests, dests); | |
362 | } | |
363 | else if (changes == 0) | |
364 | { | |
0837b7e8 MS |
365 | char buffer[10240], /* String for options */ |
366 | *ptr; /* Pointer into string */ | |
367 | ||
ef416fc2 | 368 | num_options = dest->num_options; |
369 | options = dest->options; | |
370 | ||
0837b7e8 MS |
371 | for (i = 0, ptr = buffer; |
372 | ptr < (buffer + sizeof(buffer) - 1) && i < num_options; | |
373 | i ++) | |
ef416fc2 | 374 | { |
375 | if (i) | |
0837b7e8 | 376 | *ptr++ = ' '; |
ef416fc2 | 377 | |
378 | if (!options[i].value[0]) | |
7e86f2f6 | 379 | strlcpy(ptr, options[i].name, sizeof(buffer) - (size_t)(ptr - buffer)); |
ef416fc2 | 380 | else if (strchr(options[i].value, ' ') != NULL || |
381 | strchr(options[i].value, '\t') != NULL) | |
7e86f2f6 | 382 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=\'%s\'", options[i].name, options[i].value); |
ef416fc2 | 383 | else |
7e86f2f6 | 384 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=%s", options[i].name, options[i].value); |
0837b7e8 MS |
385 | |
386 | ptr += strlen(ptr); | |
ef416fc2 | 387 | } |
388 | ||
0837b7e8 | 389 | _cupsLangPuts(stdout, buffer); |
ef416fc2 | 390 | } |
391 | ||
392 | return (0); | |
393 | } | |
394 | ||
395 | /* | |
396 | * 'list_group()' - List printer-specific options from the PPD group. | |
397 | */ | |
398 | ||
dd1abb6b MS |
399 | static void |
400 | list_group(ppd_file_t *ppd, /* I - PPD file */ | |
401 | ppd_group_t *group) /* I - Group to show */ | |
ef416fc2 | 402 | { |
dd1abb6b MS |
403 | int i, j; /* Looping vars */ |
404 | ppd_option_t *option; /* Current option */ | |
405 | ppd_choice_t *choice; /* Current choice */ | |
406 | ppd_group_t *subgroup; /* Current subgroup */ | |
0837b7e8 MS |
407 | char buffer[10240], /* Option string buffer */ |
408 | *ptr; /* Pointer into option string */ | |
ef416fc2 | 409 | |
410 | ||
411 | for (i = group->num_options, option = group->options; i > 0; i --, option ++) | |
412 | { | |
88f9aafc | 413 | if (!_cups_strcasecmp(option->keyword, "PageRegion")) |
dd1abb6b MS |
414 | continue; |
415 | ||
0837b7e8 | 416 | snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text); |
ef416fc2 | 417 | |
0837b7e8 MS |
418 | for (j = option->num_choices, choice = option->choices, |
419 | ptr = buffer + strlen(buffer); | |
420 | j > 0 && ptr < (buffer + sizeof(buffer) - 1); | |
dd1abb6b | 421 | j --, choice ++) |
0837b7e8 | 422 | { |
88f9aafc | 423 | if (!_cups_strcasecmp(choice->choice, "Custom")) |
dd1abb6b MS |
424 | { |
425 | ppd_coption_t *coption; /* Custom option */ | |
426 | ppd_cparam_t *cparam; /* Custom parameter */ | |
427 | static const char * const types[] = | |
428 | { /* Parameter types */ | |
429 | "CURVE", | |
430 | "INTEGER", | |
431 | "INVCURVE", | |
432 | "PASSCODE", | |
433 | "PASSWORD", | |
434 | "POINTS", | |
435 | "REAL", | |
436 | "STRING" | |
437 | }; | |
438 | ||
439 | ||
440 | if ((coption = ppdFindCustomOption(ppd, option->keyword)) == NULL || | |
441 | cupsArrayCount(coption->params) == 0) | |
7e86f2f6 | 442 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom", choice->marked ? "*" : ""); |
88f9aafc MS |
443 | else if (!_cups_strcasecmp(option->keyword, "PageSize") || |
444 | !_cups_strcasecmp(option->keyword, "PageRegion")) | |
7e86f2f6 | 445 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : ""); |
dd1abb6b MS |
446 | else |
447 | { | |
448 | cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); | |
449 | ||
450 | if (cupsArrayCount(coption->params) == 1) | |
7e86f2f6 | 451 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.%s", choice->marked ? "*" : "", types[cparam->type]); |
dd1abb6b MS |
452 | else |
453 | { | |
454 | const char *prefix; /* Prefix string */ | |
455 | ||
456 | ||
457 | if (choice->marked) | |
458 | prefix = " *{"; | |
459 | else | |
460 | prefix = " {"; | |
461 | ||
462 | while (cparam) | |
463 | { | |
7e86f2f6 | 464 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s%s=%s", prefix, cparam->name, types[cparam->type]); |
dd1abb6b MS |
465 | cparam = (ppd_cparam_t *)cupsArrayNext(coption->params); |
466 | prefix = " "; | |
0837b7e8 | 467 | ptr += strlen(ptr); |
dd1abb6b MS |
468 | } |
469 | ||
0837b7e8 | 470 | if (ptr < (buffer + sizeof(buffer) - 1)) |
7e86f2f6 | 471 | strlcpy(ptr, "}", sizeof(buffer) - (size_t)(ptr - buffer)); |
dd1abb6b MS |
472 | } |
473 | } | |
474 | } | |
475 | else if (choice->marked) | |
7e86f2f6 | 476 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " *%s", choice->choice); |
ef416fc2 | 477 | else |
7e86f2f6 | 478 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %s", choice->choice); |
0837b7e8 MS |
479 | |
480 | ptr += strlen(ptr); | |
481 | } | |
ef416fc2 | 482 | |
0837b7e8 | 483 | _cupsLangPuts(stdout, buffer); |
ef416fc2 | 484 | } |
485 | ||
486 | for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++) | |
dd1abb6b | 487 | list_group(ppd, subgroup); |
ef416fc2 | 488 | } |
489 | ||
490 | ||
491 | /* | |
492 | * 'list_options()' - List printer-specific options from the PPD file. | |
493 | */ | |
494 | ||
dd1abb6b MS |
495 | static void |
496 | list_options(cups_dest_t *dest) /* I - Destination to list */ | |
ef416fc2 | 497 | { |
dd1abb6b MS |
498 | int i; /* Looping var */ |
499 | const char *filename; /* PPD filename */ | |
500 | ppd_file_t *ppd; /* PPD data */ | |
501 | ppd_group_t *group; /* Current group */ | |
ef416fc2 | 502 | |
503 | ||
504 | if ((filename = cupsGetPPD(dest->name)) == NULL) | |
505 | { | |
0837b7e8 | 506 | _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"), |
b86bc4cf | 507 | dest->name, cupsLastErrorString()); |
ef416fc2 | 508 | return; |
509 | } | |
510 | ||
511 | if ((ppd = ppdOpenFile(filename)) == NULL) | |
512 | { | |
513 | unlink(filename); | |
0837b7e8 | 514 | _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."), |
ef416fc2 | 515 | dest->name); |
516 | return; | |
517 | } | |
518 | ||
519 | ppdMarkDefaults(ppd); | |
520 | cupsMarkOptions(ppd, dest->num_options, dest->options); | |
521 | ||
522 | for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) | |
dd1abb6b | 523 | list_group(ppd, group); |
ef416fc2 | 524 | |
525 | ppdClose(ppd); | |
526 | unlink(filename); | |
527 | } | |
528 | ||
529 | ||
530 | /* | |
531 | * 'usage()' - Show program usage and exit. | |
532 | */ | |
533 | ||
dd1abb6b | 534 | static void |
ef416fc2 | 535 | usage(void) |
536 | { | |
fa73b229 | 537 | _cupsLangPuts(stdout, |
ef416fc2 | 538 | _("Usage: lpoptions [-h server] [-E] -d printer\n" |
539 | " lpoptions [-h server] [-E] [-p printer] -l\n" | |
540 | " lpoptions [-h server] [-E] -p printer -o " | |
541 | "option[=value] ...\n" | |
0837b7e8 | 542 | " lpoptions [-h server] [-E] -x printer")); |
ef416fc2 | 543 | |
544 | exit(1); | |
545 | } |