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