]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/options.c
Import CUPS 1.4svn-r7226.
[thirdparty/cups.git] / cups / options.c
CommitLineData
ef416fc2 1/*
2e4ff8af 2 * "$Id: options.c 6943 2007-09-10 23:00:33Z mike $"
ef416fc2 3 *
4 * Option routines for the Common UNIX Printing System (CUPS).
5 *
91c84a35 6 * Copyright 2007-2008 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
b423cd4c 19 * cupsAddOption() - Add an option to an option array.
20 * cupsFreeOptions() - Free all memory used by options.
21 * cupsGetOption() - Get an option value.
22 * cupsMarkOptions() - Mark command-line options in a PPD file.
23 * cupsParseOptions() - Parse options from a command-line argument.
24 * cupsRemoveOptions() - Remove an option from an option array.
355e94dc 25 * debug_marked() - Output the marked array to stdout...
09a101d6 26 * ppd_mark_choices() - Mark one or more option choices from a string.
ef416fc2 27 */
28
29/*
30 * Include necessary headers...
31 */
32
33#include "cups.h"
34#include <stdlib.h>
35#include <ctype.h>
36#include "string.h"
37#include "debug.h"
38
39
09a101d6 40/*
41 * Local functions...
42 */
43
355e94dc
MS
44#ifdef DEBUG
45static void debug_marked(ppd_file_t *ppd, const char *title);
46#else
47# define debug_marked(ppd,title)
48#endif /* DEBUG */
09a101d6 49static int ppd_mark_choices(ppd_file_t *ppd, const char *options);
50
51
ef416fc2 52/*
53 * 'cupsAddOption()' - Add an option to an option array.
54 */
55
56int /* O - Number of options */
57cupsAddOption(const char *name, /* I - Name of option */
58 const char *value, /* I - Value of option */
59 int num_options,/* I - Number of options */
60 cups_option_t **options) /* IO - Pointer to options */
61{
62 int i; /* Looping var */
63 cups_option_t *temp; /* Pointer to new option */
64
65
66 if (name == NULL || !name[0] || value == NULL ||
67 options == NULL || num_options < 0)
68 return (num_options);
69
70 /*
71 * Look for an existing option with the same name...
72 */
73
74 for (i = 0, temp = *options; i < num_options; i ++, temp ++)
2e4ff8af 75 if (!strcasecmp(temp->name, name))
ef416fc2 76 break;
77
78 if (i >= num_options)
79 {
80 /*
81 * No matching option name...
82 */
83
84 if (num_options == 0)
85 temp = (cups_option_t *)malloc(sizeof(cups_option_t));
86 else
87 temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) *
88 (num_options + 1));
89
90 if (temp == NULL)
91 return (0);
92
93 *options = temp;
94 temp += num_options;
2e4ff8af 95 temp->name = _cupsStrAlloc(name);
ef416fc2 96 num_options ++;
97 }
98 else
99 {
100 /*
101 * Match found; free the old value...
102 */
103
2e4ff8af 104 _cupsStrFree(temp->value);
ef416fc2 105 }
106
2e4ff8af 107 temp->value = _cupsStrAlloc(value);
ef416fc2 108
109 return (num_options);
110}
111
112
113/*
114 * 'cupsFreeOptions()' - Free all memory used by options.
115 */
116
117void
118cupsFreeOptions(
119 int num_options, /* I - Number of options */
120 cups_option_t *options) /* I - Pointer to options */
121{
122 int i; /* Looping var */
123
124
125 if (num_options <= 0 || options == NULL)
126 return;
127
128 for (i = 0; i < num_options; i ++)
129 {
2e4ff8af
MS
130 _cupsStrFree(options[i].name);
131 _cupsStrFree(options[i].value);
ef416fc2 132 }
133
134 free(options);
135}
136
137
138/*
139 * 'cupsGetOption()' - Get an option value.
140 */
141
142const char * /* O - Option value or NULL */
143cupsGetOption(const char *name, /* I - Name of option */
144 int num_options,/* I - Number of options */
145 cups_option_t *options) /* I - Options */
146{
147 int i; /* Looping var */
148
149
150 if (name == NULL || num_options <= 0 || options == NULL)
151 return (NULL);
152
153 for (i = 0; i < num_options; i ++)
154 if (strcasecmp(options[i].name, name) == 0)
155 return (options[i].value);
156
157 return (NULL);
158}
159
160
ef416fc2 161/*
162 * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
163 */
164
165int /* O - 1 if conflicting */
166cupsMarkOptions(
167 ppd_file_t *ppd, /* I - PPD file */
168 int num_options, /* I - Number of options */
169 cups_option_t *options) /* I - Options */
170{
171 int i, j, k; /* Looping vars */
172 int conflict; /* Option conflicts */
173 char *val, /* Pointer into value */
174 *ptr, /* Pointer into string */
175 s[255]; /* Temporary string */
fa73b229 176 const char *page_size; /* PageSize option */
ef416fc2 177 cups_option_t *optptr; /* Current option */
178 ppd_option_t *option; /* PPD option */
09a101d6 179 ppd_attr_t *attr; /* PPD attribute */
ef416fc2 180 static const char * const duplex_options[] =
181 { /* Duplex option names */
182 "Duplex", /* Adobe */
183 "EFDuplex", /* EFI */
184 "EFDuplexing", /* EFI */
185 "KD03Duplex", /* Kodak */
186 "JCLDuplex" /* Samsung */
187 };
188 static const char * const duplex_one[] =
189 { /* one-sided names */
190 "None",
191 "False"
192 };
193 static const char * const duplex_two_long[] =
194 { /* two-sided-long-edge names */
195 "DuplexNoTumble", /* Adobe */
196 "LongEdge", /* EFI */
197 "Top" /* EFI */
198 };
199 static const char * const duplex_two_short[] =
200 { /* two-sided-long-edge names */
201 "DuplexTumble", /* Adobe */
202 "ShortEdge", /* EFI */
203 "Bottom" /* EFI */
204 };
205
206
207 /*
208 * Check arguments...
209 */
210
211 if (ppd == NULL || num_options <= 0 || options == NULL)
212 return (0);
213
355e94dc
MS
214 debug_marked(ppd, "Before...");
215
ef416fc2 216 /*
217 * Mark options...
218 */
219
fa73b229 220 conflict = 0;
ef416fc2 221
222 for (i = num_options, optptr = options; i > 0; i --, optptr ++)
223 if (!strcasecmp(optptr->name, "media"))
224 {
225 /*
226 * Loop through the option string, separating it at commas and
fa73b229 227 * marking each individual option as long as the corresponding
228 * PPD option (PageSize, InputSlot, etc.) is not also set.
229 *
230 * For PageSize, we also check for an empty option value since
231 * some versions of MacOS X use it to specify auto-selection
232 * of the media based solely on the size.
ef416fc2 233 */
234
fa73b229 235 page_size = cupsGetOption("PageSize", num_options, options);
236
ef416fc2 237 for (val = optptr->value; *val;)
238 {
239 /*
240 * Extract the sub-option from the string...
241 */
242
243 for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
244 *ptr++ = *val++;
245 *ptr++ = '\0';
246
247 if (*val == ',')
248 val ++;
249
250 /*
251 * Mark it...
252 */
253
fa73b229 254 if (!page_size || !page_size[0])
ef416fc2 255 if (ppdMarkOption(ppd, "PageSize", s))
256 conflict = 1;
257
258 if (cupsGetOption("InputSlot", num_options, options) == NULL)
259 if (ppdMarkOption(ppd, "InputSlot", s))
260 conflict = 1;
261
262 if (cupsGetOption("MediaType", num_options, options) == NULL)
263 if (ppdMarkOption(ppd, "MediaType", s))
264 conflict = 1;
265
266 if (cupsGetOption("EFMediaType", num_options, options) == NULL)
355e94dc 267 if (ppdMarkOption(ppd, "EFMediaType", s)) /* EFI */
ef416fc2 268 conflict = 1;
269
270 if (cupsGetOption("EFMediaQualityMode", num_options, options) == NULL)
271 if (ppdMarkOption(ppd, "EFMediaQualityMode", s)) /* EFI */
272 conflict = 1;
273
274 if (strcasecmp(s, "manual") == 0 &&
275 cupsGetOption("ManualFeed", num_options, options) == NULL)
276 if (ppdMarkOption(ppd, "ManualFeed", "True"))
277 conflict = 1;
278 }
279 }
280 else if (!strcasecmp(optptr->name, "sides"))
281 {
282 for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
283 if (cupsGetOption(duplex_options[j], num_options, options) != NULL)
284 break;
285
286 if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
287 {
288 /*
289 * Don't override the PPD option with the IPP attribute...
290 */
291
292 continue;
293 }
294
295 if (!strcasecmp(optptr->value, "one-sided"))
296 {
297 /*
298 * Mark the appropriate duplex option for one-sided output...
299 */
300
301 for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
302 if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
303 break;
304
305 if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
306 {
307 for (k = 0; k < (int)(sizeof(duplex_one) / sizeof(duplex_one[0])); k ++)
308 if (ppdFindChoice(option, duplex_one[k]))
309 {
310 if (ppdMarkOption(ppd, duplex_options[j], duplex_one[k]))
311 conflict = 1;
312
313 break;
314 }
315 }
316 }
317 else if (!strcasecmp(optptr->value, "two-sided-long-edge"))
318 {
319 /*
320 * Mark the appropriate duplex option for two-sided-long-edge output...
321 */
322
323 for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
324 if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
325 break;
326
327 if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
328 {
329 for (k = 0; k < (int)(sizeof(duplex_two_long) / sizeof(duplex_two_long[0])); k ++)
330 if (ppdFindChoice(option, duplex_two_long[k]))
331 {
332 if (ppdMarkOption(ppd, duplex_options[j], duplex_two_long[k]))
333 conflict = 1;
334
335 break;
336 }
337 }
338 }
339 else if (!strcasecmp(optptr->value, "two-sided-short-edge"))
340 {
341 /*
342 * Mark the appropriate duplex option for two-sided-short-edge output...
343 */
344
345 for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
346 if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
347 break;
348
349 if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
350 {
351 for (k = 0; k < (int)(sizeof(duplex_two_short) / sizeof(duplex_two_short[0])); k ++)
352 if (ppdFindChoice(option, duplex_two_short[k]))
353 {
354 if (ppdMarkOption(ppd, duplex_options[j], duplex_two_short[k]))
355 conflict = 1;
356
357 break;
358 }
359 }
360 }
361 }
362 else if (!strcasecmp(optptr->name, "resolution") ||
363 !strcasecmp(optptr->name, "printer-resolution"))
364 {
365 if (ppdMarkOption(ppd, "Resolution", optptr->value))
366 conflict = 1;
367 if (ppdMarkOption(ppd, "SetResolution", optptr->value))
368 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
369 conflict = 1;
370 if (ppdMarkOption(ppd, "JCLResolution", optptr->value)) /* HP */
371 conflict = 1;
372 if (ppdMarkOption(ppd, "CNRes_PGP", optptr->value)) /* Canon */
373 conflict = 1;
374 }
375 else if (!strcasecmp(optptr->name, "output-bin"))
376 {
f7deaa1a 377 if (!cupsGetOption("OutputBin", num_options, options))
ef416fc2 378 if (ppdMarkOption(ppd, "OutputBin", optptr->value))
379 conflict = 1;
380 }
f7deaa1a 381 else if (!strcasecmp(optptr->name, "multiple-document-handling"))
382 {
383 if (!cupsGetOption("Collate", num_options, options) &&
384 ppdFindOption(ppd, "Collate"))
385 {
386 if (strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
387 {
388 if (ppdMarkOption(ppd, "Collate", "True"))
389 conflict = 1;
390 }
391 else
392 {
393 if (ppdMarkOption(ppd, "Collate", "False"))
394 conflict = 1;
395 }
396 }
397 }
09a101d6 398 else if (!strcasecmp(optptr->name, "finishings"))
399 {
400 /*
401 * Lookup cupsIPPFinishings attributes for each value...
402 */
403
404 for (ptr = optptr->value; *ptr;)
405 {
406 /*
407 * Get the next finishings number...
408 */
409
410 if (!isdigit(*ptr & 255))
411 break;
412
413 if ((j = strtol(ptr, &ptr, 10)) < 3)
414 break;
415
416 /*
417 * Skip separator as needed...
418 */
419
420 if (*ptr == ',')
421 ptr ++;
422
423 /*
424 * Look it up in the PPD file...
425 */
426
427 sprintf(s, "%d", j);
428
429 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
430 continue;
431
432 /*
433 * Apply "*Option Choice" settings from the attribute value...
434 */
435
436 if (ppd_mark_choices(ppd, attr->value))
437 conflict = 1;
438 }
439 }
355e94dc
MS
440 else if (!strcasecmp(optptr->name, "mirror"))
441 {
442 if (ppdMarkOption(ppd, "MirrorPrint", optptr->value))
443 conflict = 1;
444 }
ef416fc2 445 else if (ppdMarkOption(ppd, optptr->name, optptr->value))
446 conflict = 1;
447
355e94dc
MS
448 debug_marked(ppd, "After...");
449
ef416fc2 450 return (conflict);
451}
452
453
454/*
b423cd4c 455 * 'cupsParseOptions()' - Parse options from a command-line argument.
456 *
457 * This function converts space-delimited name/value pairs according
458 * to the PAPI text option ABNF specification. Collection values
459 * ("name={a=... b=... c=...}") are stored with the curley brackets
460 * intact - use cupsParseOptions() on the value to extract the collection
461 * attributes.
462 */
463
464int /* O - Number of options found */
465cupsParseOptions(
466 const char *arg, /* I - Argument to parse */
467 int num_options, /* I - Number of options */
468 cups_option_t **options) /* O - Options found */
469{
470 char *copyarg, /* Copy of input string */
471 *ptr, /* Pointer into string */
472 *name, /* Pointer to name */
473 *value; /* Pointer to value */
474
475
91c84a35
MS
476 /*
477 * Range check input...
478 */
479
480 if (!arg)
481 return (num_options);
482
483 if (!options || num_options < 0)
b423cd4c 484 return (0);
485
486 /*
487 * Make a copy of the argument string and then divide it up...
488 */
489
91c84a35
MS
490 if ((copyarg = strdup(arg)) == NULL)
491 return (num_options);
492
493 ptr = copyarg;
b423cd4c 494
495 /*
496 * Skip leading spaces...
497 */
498
499 while (isspace(*ptr & 255))
500 ptr ++;
501
502 /*
503 * Loop through the string...
504 */
505
506 while (*ptr != '\0')
507 {
508 /*
509 * Get the name up to a SPACE, =, or end-of-string...
510 */
511
512 name = ptr;
513 while (!isspace(*ptr & 255) && *ptr != '=' && *ptr != '\0')
514 ptr ++;
515
516 /*
517 * Avoid an empty name...
518 */
519
520 if (ptr == name)
521 break;
522
523 /*
524 * Skip trailing spaces...
525 */
526
527 while (isspace(*ptr & 255))
528 *ptr++ = '\0';
529
530 if (*ptr != '=')
531 {
532 /*
533 * Start of another option...
534 */
535
536 if (strncasecmp(name, "no", 2) == 0)
537 num_options = cupsAddOption(name + 2, "false", num_options,
538 options);
539 else
540 num_options = cupsAddOption(name, "true", num_options, options);
541
542 continue;
543 }
544
545 /*
546 * Remove = and parse the value...
547 */
548
549 *ptr++ = '\0';
550
551 if (*ptr == '\'')
552 {
553 /*
554 * Quoted string constant...
555 */
556
557 ptr ++;
558 value = ptr;
559
560 while (*ptr != '\'' && *ptr != '\0')
561 {
562 if (*ptr == '\\')
563 _cups_strcpy(ptr, ptr + 1);
564
565 ptr ++;
566 }
567
568 if (*ptr != '\0')
569 *ptr++ = '\0';
570 }
571 else if (*ptr == '\"')
572 {
573 /*
574 * Double-quoted string constant...
575 */
576
577 ptr ++;
578 value = ptr;
579
580 while (*ptr != '\"' && *ptr != '\0')
581 {
582 if (*ptr == '\\')
583 _cups_strcpy(ptr, ptr + 1);
584
585 ptr ++;
586 }
587
588 if (*ptr != '\0')
589 *ptr++ = '\0';
590 }
591 else if (*ptr == '{')
592 {
593 /*
594 * Collection value...
595 */
596
597 int depth;
598
599 value = ptr;
600
601 for (depth = 1; *ptr; ptr ++)
602 if (*ptr == '{')
603 depth ++;
604 else if (*ptr == '}')
605 {
606 depth --;
607 if (!depth)
608 {
609 ptr ++;
610
611 if (*ptr != ',')
612 break;
613 }
614 }
615 else if (*ptr == '\\')
616 _cups_strcpy(ptr, ptr + 1);
617
618 if (*ptr != '\0')
619 *ptr++ = '\0';
620 }
621 else
622 {
623 /*
624 * Normal space-delimited string...
625 */
626
627 value = ptr;
628
629 while (!isspace(*ptr & 255) && *ptr != '\0')
630 {
631 if (*ptr == '\\')
632 _cups_strcpy(ptr, ptr + 1);
633
634 ptr ++;
635 }
636 }
637
638 /*
639 * Skip trailing whitespace...
640 */
641
642 while (isspace(*ptr & 255))
643 *ptr++ = '\0';
644
645 /*
646 * Add the string value...
647 */
648
649 num_options = cupsAddOption(name, value, num_options, options);
650 }
651
652 /*
653 * Free the copy of the argument we made and return the number of options
654 * found.
655 */
656
657 free(copyarg);
658
659 return (num_options);
660}
661
662
663/*
f7deaa1a 664 * 'cupsRemoveOption()' - Remove an option from an option array.
b423cd4c 665 *
666 * @since CUPS 1.2@
667 */
668
669int /* O - New number of options */
670cupsRemoveOption(
671 const char *name, /* I - Option name */
672 int num_options, /* I - Current number of options */
673 cups_option_t **options) /* IO - Options */
674{
675 int i; /* Looping var */
676 cups_option_t *option; /* Current option */
677
678
679 /*
680 * Range check input...
681 */
682
683 if (!name || num_options < 1 || !options)
684 return (num_options);
685
686 /*
687 * Loop for the option...
688 */
689
690 for (i = num_options, option = *options; i > 0; i --, option ++)
691 if (!strcasecmp(name, option->name))
692 break;
693
694 if (i)
695 {
696 /*
697 * Remove this option from the array...
698 */
699
700 num_options --;
701 i --;
702
2e4ff8af
MS
703 _cupsStrFree(option->name);
704 _cupsStrFree(option->value);
b423cd4c 705
706 if (i > 0)
f7deaa1a 707 memmove(option, option + 1, i * sizeof(cups_option_t));
b423cd4c 708 }
709
710 /*
711 * Return the new number of options...
712 */
713
714 return (num_options);
715}
716
717
355e94dc
MS
718#ifdef DEBUG
719/*
720 * 'debug_marked()' - Output the marked array to stdout...
721 */
722
723static void
724debug_marked(ppd_file_t *ppd, /* I - PPD file data */
725 const char *title) /* I - Title for list */
726{
727 ppd_choice_t *c; /* Current choice */
728
729
730 printf("cupsMarkOptions: %s\n", title);
731
732 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
733 c;
734 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
735 printf("cupsMarkOptions: %s=%s\n", c->option->keyword, c->choice);
736}
737#endif /* DEBUG */
738
739
b423cd4c 740/*
09a101d6 741 * 'ppd_mark_choices()' - Mark one or more option choices from a string.
742 */
743
744static int /* O - 1 if there are conflicts, 0 otherwise */
745ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
746 const char *options) /* I - "*Option Choice ..." string */
747{
748 char option[PPD_MAX_NAME], /* Current option */
749 choice[PPD_MAX_NAME], /* Current choice */
750 *ptr; /* Pointer into option or choice */
751 int conflict = 0; /* Do we have a conflict? */
752
753
754 if (!options)
755 return (0);
756
757 /*
758 * Read all of the "*Option Choice" pairs from the string, marking PPD
759 * options as we go...
760 */
761
762 while (*options)
763 {
764 /*
765 * Skip leading whitespace...
766 */
767
768 while (isspace(*options & 255))
769 options ++;
770
771 if (*options != '*')
772 break;
773
774 /*
775 * Get the option name...
776 */
777
778 options ++;
779 ptr = option;
780 while (*options && !isspace(*options & 255) &&
781 ptr < (option + sizeof(option) - 1))
782 *ptr++ = *options++;
783
784 if (ptr == option)
785 break;
786
787 *ptr = '\0';
788
789 /*
790 * Get the choice...
791 */
792
793 while (isspace(*options & 255))
794 options ++;
795
796 if (!*options)
797 break;
798
799 ptr = choice;
800 while (*options && !isspace(*options & 255) &&
801 ptr < (choice + sizeof(choice) - 1))
802 *ptr++ = *options++;
803
804 *ptr = '\0';
805
806 /*
807 * Mark the option...
808 */
809
810 if (ppdMarkOption(ppd, option, choice))
811 conflict = 1;
812 }
813
814 /*
815 * Return whether we had any conflicts...
816 */
817
818 return (conflict);
819}
820
821
822/*
2e4ff8af 823 * End of "$Id: options.c 6943 2007-09-10 23:00:33Z mike $".
ef416fc2 824 */