]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Printer option program for CUPS. | |
3 | * | |
4 | * Copyright 2007-2015 by Apple Inc. | |
5 | * Copyright 1997-2006 by Easy Software Products. | |
6 | * | |
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/". | |
12 | */ | |
13 | ||
14 | /* | |
15 | * Include necessary headers... | |
16 | */ | |
17 | ||
18 | #include <cups/cups-private.h> | |
19 | #include <cups/ppd-private.h> | |
20 | ||
21 | ||
22 | /* | |
23 | * Local functions... | |
24 | */ | |
25 | ||
26 | static void list_group(ppd_file_t *ppd, ppd_group_t *group); | |
27 | static void list_options(cups_dest_t *dest); | |
28 | static void usage(void) __attribute__((noreturn)); | |
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 */ | |
47 | *instance, /* Instance name */ | |
48 | *option; /* Current option */ | |
49 | ||
50 | ||
51 | _cupsSetLocale(argv); | |
52 | ||
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 | ||
87 | if (num_dests == 0 || !dests || | |
88 | (dest = cupsGetDest(printer, instance, num_dests, | |
89 | dests)) == NULL) | |
90 | { | |
91 | _cupsLangPuts(stderr, _("lpoptions: Unknown printer or class.")); | |
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 ++) | |
107 | if (cupsGetOption(dest->options[j].name, num_options, | |
108 | options) == NULL) | |
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) | |
142 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); | |
143 | else | |
144 | list_options(dest); | |
145 | ||
146 | changes = -1; | |
147 | break; | |
148 | ||
149 | case 'o' : /* -o option[=value] */ | |
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 | ||
158 | if (dest == NULL) | |
159 | { | |
160 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); | |
161 | return (1); | |
162 | } | |
163 | ||
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 | ||
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 | { | |
210 | _cupsLangPrintf(stderr, | |
211 | _("lpoptions: Unable to add printer or " | |
212 | "instance: %s"), | |
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) */ | |
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 | ||
234 | if (dest == NULL) | |
235 | { | |
236 | _cupsLangPuts(stderr, _("lpoptions: No printers.")); | |
237 | return (1); | |
238 | } | |
239 | ||
240 | for (j = 0; j < dest->num_options; j ++) | |
241 | if (cupsGetOption(dest->options[j].name, num_options, | |
242 | options) == NULL) | |
243 | num_options = cupsAddOption(dest->options[j].name, | |
244 | dest->options[j].value, | |
245 | num_options, &options); | |
246 | } | |
247 | ||
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 ++) | |
260 | if (!_cups_strcasecmp(options[j].name, option)) | |
261 | { | |
262 | /* | |
263 | * Remove this option... | |
264 | */ | |
265 | ||
266 | num_options --; | |
267 | ||
268 | if (j < num_options) | |
269 | memmove(options + j, options + j + 1, sizeof(cups_option_t) * (size_t)(num_options - j)); | |
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 | ||
294 | if ((dest = cupsGetDest(printer, instance, num_dests, | |
295 | dests)) != NULL) | |
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) | |
316 | memmove(dest, dest + 1, (size_t)(num_dests - j) * sizeof(cups_dest_t)); | |
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 | { | |
365 | char buffer[10240], /* String for options */ | |
366 | *ptr; /* Pointer into string */ | |
367 | ||
368 | num_options = dest->num_options; | |
369 | options = dest->options; | |
370 | ||
371 | for (i = 0, ptr = buffer; | |
372 | ptr < (buffer + sizeof(buffer) - 1) && i < num_options; | |
373 | i ++) | |
374 | { | |
375 | if (i) | |
376 | *ptr++ = ' '; | |
377 | ||
378 | if (!options[i].value[0]) | |
379 | strlcpy(ptr, options[i].name, sizeof(buffer) - (size_t)(ptr - buffer)); | |
380 | else if (strchr(options[i].value, ' ') != NULL || | |
381 | strchr(options[i].value, '\t') != NULL) | |
382 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=\'%s\'", options[i].name, options[i].value); | |
383 | else | |
384 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s=%s", options[i].name, options[i].value); | |
385 | ||
386 | ptr += strlen(ptr); | |
387 | } | |
388 | ||
389 | _cupsLangPuts(stdout, buffer); | |
390 | } | |
391 | ||
392 | return (0); | |
393 | } | |
394 | ||
395 | /* | |
396 | * 'list_group()' - List printer-specific options from the PPD group. | |
397 | */ | |
398 | ||
399 | static void | |
400 | list_group(ppd_file_t *ppd, /* I - PPD file */ | |
401 | ppd_group_t *group) /* I - Group to show */ | |
402 | { | |
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 */ | |
407 | char buffer[10240], /* Option string buffer */ | |
408 | *ptr; /* Pointer into option string */ | |
409 | ||
410 | ||
411 | for (i = group->num_options, option = group->options; i > 0; i --, option ++) | |
412 | { | |
413 | if (!_cups_strcasecmp(option->keyword, "PageRegion")) | |
414 | continue; | |
415 | ||
416 | snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text); | |
417 | ||
418 | for (j = option->num_choices, choice = option->choices, | |
419 | ptr = buffer + strlen(buffer); | |
420 | j > 0 && ptr < (buffer + sizeof(buffer) - 1); | |
421 | j --, choice ++) | |
422 | { | |
423 | if (!_cups_strcasecmp(choice->choice, "Custom")) | |
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) | |
442 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom", choice->marked ? "*" : ""); | |
443 | else if (!_cups_strcasecmp(option->keyword, "PageSize") || | |
444 | !_cups_strcasecmp(option->keyword, "PageRegion")) | |
445 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : ""); | |
446 | else | |
447 | { | |
448 | cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params); | |
449 | ||
450 | if (cupsArrayCount(coption->params) == 1) | |
451 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %sCustom.%s", choice->marked ? "*" : "", types[cparam->type]); | |
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 | { | |
464 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), "%s%s=%s", prefix, cparam->name, types[cparam->type]); | |
465 | cparam = (ppd_cparam_t *)cupsArrayNext(coption->params); | |
466 | prefix = " "; | |
467 | ptr += strlen(ptr); | |
468 | } | |
469 | ||
470 | if (ptr < (buffer + sizeof(buffer) - 1)) | |
471 | strlcpy(ptr, "}", sizeof(buffer) - (size_t)(ptr - buffer)); | |
472 | } | |
473 | } | |
474 | } | |
475 | else if (choice->marked) | |
476 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " *%s", choice->choice); | |
477 | else | |
478 | snprintf(ptr, sizeof(buffer) - (size_t)(ptr - buffer), " %s", choice->choice); | |
479 | ||
480 | ptr += strlen(ptr); | |
481 | } | |
482 | ||
483 | _cupsLangPuts(stdout, buffer); | |
484 | } | |
485 | ||
486 | for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++) | |
487 | list_group(ppd, subgroup); | |
488 | } | |
489 | ||
490 | ||
491 | /* | |
492 | * 'list_options()' - List printer-specific options from the PPD file. | |
493 | */ | |
494 | ||
495 | static void | |
496 | list_options(cups_dest_t *dest) /* I - Destination to list */ | |
497 | { | |
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 */ | |
502 | ||
503 | ||
504 | if ((filename = cupsGetPPD(dest->name)) == NULL) | |
505 | { | |
506 | _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"), | |
507 | dest->name, cupsLastErrorString()); | |
508 | return; | |
509 | } | |
510 | ||
511 | if ((ppd = ppdOpenFile(filename)) == NULL) | |
512 | { | |
513 | unlink(filename); | |
514 | _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."), | |
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 ++) | |
523 | list_group(ppd, group); | |
524 | ||
525 | ppdClose(ppd); | |
526 | unlink(filename); | |
527 | } | |
528 | ||
529 | ||
530 | /* | |
531 | * 'usage()' - Show program usage and exit. | |
532 | */ | |
533 | ||
534 | static void | |
535 | usage(void) | |
536 | { | |
537 | _cupsLangPuts(stdout, | |
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" | |
542 | " lpoptions [-h server] [-E] -x printer")); | |
543 | ||
544 | exit(1); | |
545 | } |