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