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