• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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