1/*
2 * "$Id: lpoptions.c 11093 2013-07-03 20:48:42Z msweet $"
3 *
4 *   Printer option program for CUPS.
5 *
6 *   Copyright 2007-2011 by Apple Inc.
7 *   Copyright 1997-2006 by Easy Software Products.
8 *
9 *   These coded instructions, statements, and computer programs are the
10 *   property of Apple Inc. and are protected by Federal copyright
11 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 *   which should have been included with this file.  If this file is
13 *   file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 *   main()         - Main entry.
18 *   list_group()   - List printer-specific options from the PPD group.
19 *   list_options() - List printer-specific options from the PPD file.
20 *   usage()        - Show program usage and exit.
21 */
22
23/*
24 * Include necessary headers...
25 */
26
27#include <cups/cups-private.h>
28
29
30/*
31 * Local functions...
32 */
33
34static void	list_group(ppd_file_t *ppd, ppd_group_t *group);
35static void	list_options(cups_dest_t *dest);
36static void	usage(void) __attribute__((noreturn));
37
38
39/*
40 * 'main()' - Main entry.
41 */
42
43int					/* O - Exit status */
44main(int  argc,				/* I - Number of command-line arguments */
45     char *argv[])			/* I - Command-line arguments */
46{
47  int		i, j;			/* Looping vars */
48  int		changes;		/* Did we make changes? */
49  int		num_options;		/* Number of options */
50  cups_option_t	*options;		/* Options */
51  int		num_dests;		/* Number of destinations */
52  cups_dest_t	*dests;			/* Destinations */
53  cups_dest_t	*dest;			/* Current destination */
54  char		*printer,		/* Printer name */
55		*instance,		/* Instance name */
56 		*option;		/* Current option */
57
58
59  _cupsSetLocale(argv);
60
61 /*
62  * Loop through the command-line arguments...
63  */
64
65  dest        = NULL;
66  num_dests   = 0;
67  dests       = NULL;
68  num_options = 0;
69  options     = NULL;
70  changes     = 0;
71
72  for (i = 1; i < argc; i ++)
73    if (argv[i][0] == '-')
74    {
75      switch (argv[i][1])
76      {
77        case 'd' : /* -d printer */
78	    if (argv[i][2])
79	      printer = argv[i] + 2;
80	    else
81	    {
82	      i ++;
83	      if (i >= argc)
84	        usage();
85
86	      printer = argv[i];
87	    }
88
89            if ((instance = strrchr(printer, '/')) != NULL)
90	      *instance++ = '\0';
91
92	    if (num_dests == 0)
93	      num_dests = cupsGetDests(&dests);
94
95            if (num_dests == 0 || !dests ||
96	        (dest = cupsGetDest(printer, instance, num_dests,
97		                    dests)) == NULL)
98	    {
99	      _cupsLangPuts(stderr, _("lpoptions: Unknown printer or class."));
100	      return (1);
101	    }
102
103	   /*
104	    * Set the default destination...
105	    */
106
107	    for (j = 0; j < num_dests; j ++)
108	      dests[j].is_default = 0;
109
110	    dest->is_default = 1;
111
112	    cupsSetDests(num_dests, dests);
113
114	    for (j = 0; j < dest->num_options; j ++)
115	      if (cupsGetOption(dest->options[j].name, num_options,
116	                        options) == NULL)
117		num_options = cupsAddOption(dest->options[j].name,
118	                                    dest->options[j].value,
119	                                    num_options, &options);
120	    break;
121
122	case 'h' : /* -h server */
123	    if (argv[i][2])
124	      cupsSetServer(argv[i] + 2);
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 (argv[i][2])
180	      num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
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 (argv[i][2])
195	      printer = argv[i] + 2;
196	    else
197	    {
198	      i ++;
199	      if (i >= argc)
200	        usage();
201
202	      printer = argv[i];
203	    }
204
205            if ((instance = strrchr(printer, '/')) != NULL)
206	      *instance++ = '\0';
207
208	    if (num_dests == 0)
209	      num_dests = cupsGetDests(&dests);
210
211            if ((dest = cupsGetDest(printer, instance, num_dests, dests)) == NULL)
212	    {
213	      num_dests = cupsAddDest(printer, instance, num_dests, &dests);
214	      dest      = cupsGetDest(printer, instance, num_dests, dests);
215
216              if (dest == NULL)
217	      {
218	        _cupsLangPrintf(stderr,
219		                _("lpoptions: Unable to add printer or "
220				  "instance: %s"),
221				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 (argv[i][2])
257	      option = argv[i] + 2;
258	    else
259	    {
260	      i ++;
261	      if (i >= argc)
262	        usage();
263
264	      option = argv[i];
265	    }
266
267            for (j = 0; j < num_options; j ++)
268	      if (!_cups_strcasecmp(options[j].name, option))
269	      {
270	       /*
271	        * Remove this option...
272		*/
273
274	        num_options --;
275
276		if (j < num_options)
277		  memcpy(options + j, options + j + 1,
278		         sizeof(cups_option_t) * (num_options - j));
279		break;
280              }
281
282	    changes = 1;
283	    break;
284
285        case 'x' : /* -x printer */
286	    if (argv[i][2])
287	      printer = argv[i] + 2;
288	    else
289	    {
290	      i ++;
291	      if (i >= argc)
292	        usage();
293
294	      printer = argv[i];
295	    }
296
297            if ((instance = strrchr(printer, '/')) != NULL)
298	      *instance++ = '\0';
299
300	    if (num_dests == 0)
301	      num_dests = cupsGetDests(&dests);
302
303            if ((dest = cupsGetDest(printer, instance, num_dests,
304	                            dests)) != NULL)
305	    {
306              cupsFreeOptions(dest->num_options, dest->options);
307
308             /*
309	      * If we are "deleting" the default printer, then just set the
310	      * number of options to 0; if it is also the system default
311	      * then cupsSetDests() will remove it for us...
312	      */
313
314	      if (dest->is_default)
315	      {
316		dest->num_options = 0;
317		dest->options     = NULL;
318	      }
319	      else
320	      {
321		num_dests --;
322
323		j = dest - dests;
324		if (j < num_dests)
325		  memcpy(dest, dest + 1, (num_dests - j) * sizeof(cups_dest_t));
326	      }
327	    }
328
329	    cupsSetDests(num_dests, dests);
330	    dest    = NULL;
331	    changes = -1;
332	    break;
333
334	default :
335	    usage();
336      }
337    }
338    else
339      usage();
340
341  if (num_dests == 0)
342    num_dests = cupsGetDests(&dests);
343
344  if (dest == NULL)
345  {
346    if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
347    {
348      for (j = 0; j < dest->num_options; j ++)
349	if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
350	  num_options = cupsAddOption(dest->options[j].name,
351	                              dest->options[j].value,
352	                              num_options, &options);
353    }
354  }
355
356  if (dest == NULL)
357    return (0);
358
359  if (changes > 0)
360  {
361   /*
362    * Set printer options...
363    */
364
365    cupsFreeOptions(dest->num_options, dest->options);
366
367    dest->num_options = num_options;
368    dest->options     = options;
369
370    cupsSetDests(num_dests, dests);
371  }
372  else if (changes == 0)
373  {
374    char	buffer[10240],		/* String for options */
375		*ptr;			/* Pointer into string */
376
377    num_options = dest->num_options;
378    options     = dest->options;
379
380    for (i = 0, ptr = buffer;
381         ptr < (buffer + sizeof(buffer) - 1) && i < num_options;
382	 i ++)
383    {
384      if (i)
385        *ptr++ = ' ';
386
387      if (!options[i].value[0])
388        strlcpy(ptr, options[i].name, sizeof(buffer) - (ptr - buffer));
389      else if (strchr(options[i].value, ' ') != NULL ||
390               strchr(options[i].value, '\t') != NULL)
391	snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s=\'%s\'",
392	         options[i].name, options[i].value);
393      else
394	snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s=%s",
395	         options[i].name, options[i].value);
396
397      ptr += strlen(ptr);
398    }
399
400    _cupsLangPuts(stdout, buffer);
401  }
402
403  return (0);
404}
405
406/*
407 * 'list_group()' - List printer-specific options from the PPD group.
408 */
409
410static void
411list_group(ppd_file_t  *ppd,		/* I - PPD file */
412           ppd_group_t *group)		/* I - Group to show */
413{
414  int		i, j;			/* Looping vars */
415  ppd_option_t	*option;		/* Current option */
416  ppd_choice_t	*choice;		/* Current choice */
417  ppd_group_t	*subgroup;		/* Current subgroup */
418  char		buffer[10240],		/* Option string buffer */
419		*ptr;			/* Pointer into option string */
420
421
422  for (i = group->num_options, option = group->options; i > 0; i --, option ++)
423  {
424    if (!_cups_strcasecmp(option->keyword, "PageRegion"))
425      continue;
426
427    snprintf(buffer, sizeof(buffer), "%s/%s:", option->keyword, option->text);
428
429    for (j = option->num_choices, choice = option->choices,
430             ptr = buffer + strlen(buffer);
431         j > 0 && ptr < (buffer + sizeof(buffer) - 1);
432	 j --, choice ++)
433    {
434      if (!_cups_strcasecmp(choice->choice, "Custom"))
435      {
436        ppd_coption_t	*coption;	/* Custom option */
437        ppd_cparam_t	*cparam;	/* Custom parameter */
438	static const char * const types[] =
439	{				/* Parameter types */
440	  "CURVE",
441	  "INTEGER",
442	  "INVCURVE",
443	  "PASSCODE",
444	  "PASSWORD",
445	  "POINTS",
446	  "REAL",
447	  "STRING"
448	};
449
450
451        if ((coption = ppdFindCustomOption(ppd, option->keyword)) == NULL ||
452	    cupsArrayCount(coption->params) == 0)
453	  snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %sCustom",
454	           choice->marked ? "*" : "");
455        else if (!_cups_strcasecmp(option->keyword, "PageSize") ||
456	         !_cups_strcasecmp(option->keyword, "PageRegion"))
457	  snprintf(ptr, sizeof(buffer) - (ptr - buffer),
458	           " %sCustom.WIDTHxHEIGHT", choice->marked ? "*" : "");
459        else
460	{
461	  cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
462
463	  if (cupsArrayCount(coption->params) == 1)
464	    snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %sCustom.%s",
465	             choice->marked ? "*" : "", types[cparam->type]);
466	  else
467	  {
468	    const char	*prefix;	/* Prefix string */
469
470
471            if (choice->marked)
472	      prefix = " *{";
473	    else
474	      prefix = " {";
475
476	    while (cparam)
477	    {
478	      snprintf(ptr, sizeof(buffer) - (ptr - buffer), "%s%s=%s", prefix,
479	               cparam->name, types[cparam->type]);
480	      cparam = (ppd_cparam_t *)cupsArrayNext(coption->params);
481	      prefix = " ";
482	      ptr += strlen(ptr);
483	    }
484
485            if (ptr < (buffer + sizeof(buffer) - 1))
486	      strlcpy(ptr, "}", sizeof(buffer) - (ptr - buffer));
487	  }
488	}
489      }
490      else if (choice->marked)
491        snprintf(ptr, sizeof(buffer) - (ptr - buffer), " *%s", choice->choice);
492      else
493        snprintf(ptr, sizeof(buffer) - (ptr - buffer), " %s", choice->choice);
494
495      ptr += strlen(ptr);
496    }
497
498    _cupsLangPuts(stdout, buffer);
499  }
500
501  for (i = group->num_subgroups, subgroup = group->subgroups; i > 0; i --, subgroup ++)
502    list_group(ppd, subgroup);
503}
504
505
506/*
507 * 'list_options()' - List printer-specific options from the PPD file.
508 */
509
510static void
511list_options(cups_dest_t *dest)		/* I - Destination to list */
512{
513  int		i;			/* Looping var */
514  const char	*filename;		/* PPD filename */
515  ppd_file_t	*ppd;			/* PPD data */
516  ppd_group_t	*group;			/* Current group */
517
518
519  if ((filename = cupsGetPPD(dest->name)) == NULL)
520  {
521    _cupsLangPrintf(stderr, _("lpoptions: Unable to get PPD file for %s: %s"),
522		    dest->name, cupsLastErrorString());
523    return;
524  }
525
526  if ((ppd = ppdOpenFile(filename)) == NULL)
527  {
528    unlink(filename);
529    _cupsLangPrintf(stderr, _("lpoptions: Unable to open PPD file for %s."),
530		    dest->name);
531    return;
532  }
533
534  ppdMarkDefaults(ppd);
535  cupsMarkOptions(ppd, dest->num_options, dest->options);
536
537  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
538    list_group(ppd, group);
539
540  ppdClose(ppd);
541  unlink(filename);
542}
543
544
545/*
546 * 'usage()' - Show program usage and exit.
547 */
548
549static void
550usage(void)
551{
552  _cupsLangPuts(stdout,
553                _("Usage: lpoptions [-h server] [-E] -d printer\n"
554		  "       lpoptions [-h server] [-E] [-p printer] -l\n"
555		  "       lpoptions [-h server] [-E] -p printer -o "
556		  "option[=value] ...\n"
557		  "       lpoptions [-h server] [-E] -x printer"));
558
559  exit(1);
560}
561
562
563/*
564 * End of "$Id: lpoptions.c 11093 2013-07-03 20:48:42Z msweet $".
565 */
566