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