]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/ppd-mark.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / cups / ppd-mark.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Option marking routines for CUPS.
ef416fc2 3 *
40cc612a 4 * Copyright 2007-2017 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 *
7e86f2f6 9 * PostScript is a trademark of Adobe Systems, Inc.
ef416fc2 10 */
11
12/*
13 * Include necessary headers...
14 */
15
71e16022 16#include "cups-private.h"
f787e1e3 17#include "ppd-private.h"
ef416fc2 18
19
20/*
21 * Local functions...
22 */
23
5a738aea 24#ifdef DEBUG
ba55dc12 25static void ppd_debug_marked(ppd_file_t *ppd, const char *title);
5a738aea 26#else
ba55dc12 27# define ppd_debug_marked(ppd,title)
5a738aea 28#endif /* DEBUG */
ef416fc2 29static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
66ab9486
MS
30static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
31static void ppd_mark_option(ppd_file_t *ppd, const char *option,
32 const char *choice);
ef416fc2 33
34
35/*
5a738aea
MS
36 * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
37 *
38 * This function maps the IPP "finishings", "media", "mirror",
f14324a7
MS
39 * "multiple-document-handling", "output-bin", "print-color-mode",
40 * "print-quality", "printer-resolution", and "sides" attributes to their
41 * corresponding PPD options and choices.
5a738aea
MS
42 */
43
66ab9486 44int /* O - 1 if conflicts exist, 0 otherwise */
5a738aea
MS
45cupsMarkOptions(
46 ppd_file_t *ppd, /* I - PPD file */
47 int num_options, /* I - Number of options */
48 cups_option_t *options) /* I - Options */
49{
c7017ecc 50 int i, j; /* Looping vars */
d2354e63 51 char *ptr, /* Pointer into string */
5a738aea 52 s[255]; /* Temporary string */
d2354e63
MS
53 const char *val, /* Pointer into value */
54 *media, /* media option */
0268488e 55 *output_bin, /* output-bin option */
54afec33 56 *page_size, /* PageSize option */
0268488e 57 *ppd_keyword, /* PPD keyword */
f14324a7 58 *print_color_mode, /* print-color-mode option */
0268488e
MS
59 *print_quality, /* print-quality option */
60 *sides; /* sides option */
5a738aea 61 cups_option_t *optptr; /* Current option */
5a738aea 62 ppd_attr_t *attr; /* PPD attribute */
f14324a7 63 _ppd_cache_t *cache; /* PPD cache and mapping data */
5a738aea
MS
64
65
66 /*
67 * Check arguments...
68 */
69
70 if (!ppd || num_options <= 0 || !options)
71 return (0);
72
ba55dc12 73 ppd_debug_marked(ppd, "Before...");
5a738aea
MS
74
75 /*
0268488e
MS
76 * Do special handling for finishings, media, output-bin, output-mode,
77 * print-color-mode, print-quality, and PageSize...
5a738aea
MS
78 */
79
0268488e
MS
80 media = cupsGetOption("media", num_options, options);
81 output_bin = cupsGetOption("output-bin", num_options, options);
0268488e
MS
82 page_size = cupsGetOption("PageSize", num_options, options);
83 print_quality = cupsGetOption("print-quality", num_options, options);
84 sides = cupsGetOption("sides", num_options, options);
d2354e63 85
f14324a7
MS
86 if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
87 options)) == NULL)
88 print_color_mode = cupsGetOption("output-mode", num_options, options);
89
90 if ((media || output_bin || print_color_mode || print_quality || sides) &&
91 !ppd->cache)
d2354e63
MS
92 {
93 /*
f14324a7 94 * Load PPD cache and mapping data as needed...
d2354e63
MS
95 */
96
f14324a7 97 ppd->cache = _ppdCacheCreateWithPPD(ppd);
0268488e
MS
98 }
99
f14324a7 100 cache = ppd->cache;
5a738aea 101
0268488e
MS
102 if (media)
103 {
d2354e63 104 /*
0268488e
MS
105 * Loop through the option string, separating it at commas and marking each
106 * individual option as long as the corresponding PPD option (PageSize,
107 * InputSlot, etc.) is not also set.
d2354e63 108 *
0268488e 109 * For PageSize, we also check for an empty option value since some versions
d4874933 110 * of macOS use it to specify auto-selection of the media based solely on
0268488e 111 * the size.
d2354e63 112 */
5a738aea 113
d2354e63
MS
114 for (val = media; *val;)
115 {
116 /*
117 * Extract the sub-option from the string...
118 */
5a738aea 119
7e86f2f6 120 for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);)
d2354e63
MS
121 *ptr++ = *val++;
122 *ptr++ = '\0';
123
124 if (*val == ',')
125 val ++;
126
127 /*
128 * Mark it...
129 */
130
cc754834
MS
131 if (!page_size || !page_size[0])
132 {
88f9aafc 133 if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
cc754834 134 ppd_mark_option(ppd, "PageSize", s);
f14324a7 135 else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
cc754834
MS
136 ppd_mark_option(ppd, "PageSize", ppd_keyword);
137 }
d2354e63 138
f14324a7
MS
139 if (cache && cache->source_option &&
140 !cupsGetOption(cache->source_option, num_options, options) &&
141 (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL)
142 ppd_mark_option(ppd, cache->source_option, ppd_keyword);
d2354e63 143
54afec33 144 if (!cupsGetOption("MediaType", num_options, options) &&
f14324a7 145 (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
54afec33 146 ppd_mark_option(ppd, "MediaType", ppd_keyword);
5a738aea 147 }
d2354e63
MS
148 }
149
f14324a7 150 if (cache)
0268488e
MS
151 {
152 if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
153 num_options, options) &&
154 !cupsGetOption("APPrinterPreset", num_options, options) &&
f14324a7 155 (print_color_mode || print_quality))
0268488e
MS
156 {
157 /*
158 * Map output-mode and print-quality to a preset...
159 */
160
f14324a7 161 _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */
0268488e
MS
162 _pwg_print_quality_t pwg_pq; /* print-quality index */
163 cups_option_t *preset;/* Current preset option */
164
f14324a7
MS
165 if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
166 pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
0268488e 167 else
f14324a7 168 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
169
170 if (print_quality)
171 {
7e86f2f6 172 pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT);
0268488e
MS
173 if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT)
174 pwg_pq = _PWG_PRINT_QUALITY_DRAFT;
175 else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH)
176 pwg_pq = _PWG_PRINT_QUALITY_HIGH;
177 }
178 else
179 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
180
f14324a7 181 if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
0268488e
MS
182 {
183 /*
184 * Try to find a preset that works so that we maximize the chances of us
185 * getting a good print using IPP attributes.
186 */
187
f14324a7 188 if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
0268488e 189 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
f14324a7
MS
190 else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
191 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
192 else
193 {
f14324a7
MS
194 pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
195 pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
0268488e
MS
196 }
197 }
198
f14324a7 199 if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
0268488e
MS
200 {
201 /*
202 * Copy the preset options as long as the corresponding names are not
203 * already defined in the IPP request...
204 */
205
f14324a7
MS
206 for (i = cache->num_presets[pwg_pcm][pwg_pq],
207 preset = cache->presets[pwg_pcm][pwg_pq];
0268488e
MS
208 i > 0;
209 i --, preset ++)
210 {
211 if (!cupsGetOption(preset->name, num_options, options))
212 ppd_mark_option(ppd, preset->name, preset->value);
213 }
214 }
215 }
216
217 if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
88f9aafc 218 (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
0268488e
MS
219 {
220 /*
221 * Map output-bin to OutputBin...
222 */
223
224 ppd_mark_option(ppd, "OutputBin", ppd_keyword);
225 }
226
f14324a7
MS
227 if (sides && cache->sides_option &&
228 !cupsGetOption(cache->sides_option, num_options, options))
0268488e
MS
229 {
230 /*
231 * Map sides to duplex option...
232 */
233
a4845881 234 if (!strcmp(sides, "one-sided") && cache->sides_1sided)
f14324a7 235 ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
a4845881
MS
236 else if (!strcmp(sides, "two-sided-long-edge") &&
237 cache->sides_2sided_long)
f14324a7 238 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
a4845881
MS
239 else if (!strcmp(sides, "two-sided-short-edge") &&
240 cache->sides_2sided_short)
f14324a7 241 ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
0268488e
MS
242 }
243 }
244
d2354e63
MS
245 /*
246 * Mark other options...
247 */
248
249 for (i = num_options, optptr = options; i > 0; i --, optptr ++)
40cc612a 250 {
88f9aafc
MS
251 if (!_cups_strcasecmp(optptr->name, "media") ||
252 !_cups_strcasecmp(optptr->name, "output-bin") ||
253 !_cups_strcasecmp(optptr->name, "output-mode") ||
254 !_cups_strcasecmp(optptr->name, "print-quality") ||
255 !_cups_strcasecmp(optptr->name, "sides"))
d2354e63 256 continue;
88f9aafc
MS
257 else if (!_cups_strcasecmp(optptr->name, "resolution") ||
258 !_cups_strcasecmp(optptr->name, "printer-resolution"))
5a738aea 259 {
66ab9486
MS
260 ppd_mark_option(ppd, "Resolution", optptr->value);
261 ppd_mark_option(ppd, "SetResolution", optptr->value);
5a738aea 262 /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
66ab9486
MS
263 ppd_mark_option(ppd, "JCLResolution", optptr->value);
264 /* HP */
265 ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
266 /* Canon */
5a738aea 267 }
88f9aafc 268 else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
5a738aea
MS
269 {
270 if (!cupsGetOption("Collate", num_options, options) &&
271 ppdFindOption(ppd, "Collate"))
272 {
88f9aafc 273 if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
66ab9486 274 ppd_mark_option(ppd, "Collate", "True");
5a738aea 275 else
66ab9486 276 ppd_mark_option(ppd, "Collate", "False");
5a738aea
MS
277 }
278 }
88f9aafc 279 else if (!_cups_strcasecmp(optptr->name, "finishings"))
5a738aea
MS
280 {
281 /*
282 * Lookup cupsIPPFinishings attributes for each value...
283 */
284
285 for (ptr = optptr->value; *ptr;)
286 {
287 /*
288 * Get the next finishings number...
289 */
290
291 if (!isdigit(*ptr & 255))
292 break;
293
7e86f2f6 294 if ((j = (int)strtol(ptr, &ptr, 10)) < 3)
5a738aea
MS
295 break;
296
297 /*
298 * Skip separator as needed...
299 */
300
301 if (*ptr == ',')
302 ptr ++;
303
304 /*
305 * Look it up in the PPD file...
306 */
307
308 sprintf(s, "%d", j);
309
310 if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
311 continue;
312
313 /*
314 * Apply "*Option Choice" settings from the attribute value...
315 */
316
66ab9486 317 ppd_mark_choices(ppd, attr->value);
5a738aea
MS
318 }
319 }
88f9aafc 320 else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
ef416fc2 321 {
322 /*
66ab9486 323 * Lookup APPrinterPreset value...
ef416fc2 324 */
325
66ab9486
MS
326 if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
327 {
328 /*
329 * Apply "*Option Choice" settings from the attribute value...
330 */
ef416fc2 331
66ab9486
MS
332 ppd_mark_choices(ppd, attr->value);
333 }
ef416fc2 334 }
88f9aafc 335 else if (!_cups_strcasecmp(optptr->name, "mirror"))
66ab9486
MS
336 ppd_mark_option(ppd, "MirrorPrint", optptr->value);
337 else
338 ppd_mark_option(ppd, optptr->name, optptr->value);
40cc612a
MS
339 }
340
341 if (print_quality)
342 {
343 int pq = atoi(print_quality); /* print-quaity value */
344
345 if (pq == IPP_QUALITY_DRAFT)
346 ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
347 else if (pq == IPP_QUALITY_HIGH)
348 ppd_mark_option(ppd, "cupsPrintQuality", "High");
349 else
350 ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
351 }
355e94dc 352
ba55dc12 353 ppd_debug_marked(ppd, "After...");
ef416fc2 354
66ab9486 355 return (ppdConflicts(ppd) > 0);
ef416fc2 356}
357
358
359/*
360 * 'ppdFindChoice()' - Return a pointer to an option choice.
361 */
362
5a738aea 363ppd_choice_t * /* O - Choice pointer or @code NULL@ */
ef416fc2 364ppdFindChoice(ppd_option_t *o, /* I - Pointer to option */
365 const char *choice) /* I - Name of choice */
366{
b94498cf 367 int i; /* Looping var */
368 ppd_choice_t *c; /* Current choice */
ef416fc2 369
370
bdd6c45b 371 if (!o || !choice)
ef416fc2 372 return (NULL);
373
88f9aafc 374 if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
bdd6c45b
MS
375 choice = "Custom";
376
ef416fc2 377 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
88f9aafc 378 if (!_cups_strcasecmp(c->choice, choice))
ef416fc2 379 return (c);
380
381 return (NULL);
382}
383
384
385/*
386 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
387 */
388
5a738aea 389ppd_choice_t * /* O - Pointer to choice or @code NULL@ */
ef416fc2 390ppdFindMarkedChoice(ppd_file_t *ppd, /* I - PPD file */
391 const char *option) /* I - Keyword/option name */
392{
d1c13e16
MS
393 ppd_choice_t key, /* Search key for choice */
394 *marked; /* Marked choice */
ef416fc2 395
396
e07d4801 397 DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
d1c13e16 398
b94498cf 399 if ((key.option = ppdFindOption(ppd, option)) == NULL)
d1c13e16 400 {
e07d4801 401 DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
ef416fc2 402 return (NULL);
d1c13e16
MS
403 }
404
405 marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
406
e07d4801 407 DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
d1c13e16 408 marked ? marked->choice : "NULL"));
ef416fc2 409
d1c13e16 410 return (marked);
ef416fc2 411}
412
413
414/*
415 * 'ppdFindOption()' - Return a pointer to the specified option.
416 */
417
5a738aea 418ppd_option_t * /* O - Pointer to option or @code NULL@ */
ef416fc2 419ppdFindOption(ppd_file_t *ppd, /* I - PPD file data */
420 const char *option) /* I - Option/Keyword name */
421{
fa73b229 422 /*
423 * Range check input...
424 */
ef416fc2 425
fa73b229 426 if (!ppd || !option)
ef416fc2 427 return (NULL);
428
f301802f 429 if (ppd->options)
430 {
431 /*
432 * Search in the array...
433 */
434
435 ppd_option_t key; /* Option search key */
436
437
438 strlcpy(key.keyword, option, sizeof(key.keyword));
ef416fc2 439
f301802f 440 return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
441 }
442 else
443 {
444 /*
445 * Search in each group...
446 */
ef416fc2 447
f301802f 448 int i, j; /* Looping vars */
449 ppd_group_t *group; /* Current group */
450 ppd_option_t *optptr; /* Current option */
451
452
453 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
454 for (j = group->num_options, optptr = group->options;
455 j > 0;
456 j --, optptr ++)
88f9aafc 457 if (!_cups_strcasecmp(optptr->keyword, option))
f301802f 458 return (optptr);
459
460 return (NULL);
461 }
ef416fc2 462}
463
464
465/*
5a738aea 466 * 'ppdIsMarked()' - Check to see if an option is marked.
ef416fc2 467 */
468
b94498cf 469int /* O - Non-zero if option is marked */
470ppdIsMarked(ppd_file_t *ppd, /* I - PPD file data */
471 const char *option, /* I - Option/Keyword name */
472 const char *choice) /* I - Choice name */
ef416fc2 473{
b94498cf 474 ppd_choice_t key, /* Search key */
475 *c; /* Choice pointer */
ef416fc2 476
477
b94498cf 478 if (!ppd)
ef416fc2 479 return (0);
480
b94498cf 481 if ((key.option = ppdFindOption(ppd, option)) == NULL)
ef416fc2 482 return (0);
483
b94498cf 484 if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
ef416fc2 485 return (0);
486
b94498cf 487 return (!strcmp(c->choice, choice));
ef416fc2 488}
489
490
491/*
492 * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
493 */
494
495void
b94498cf 496ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */
ef416fc2 497{
b94498cf 498 int i; /* Looping variables */
499 ppd_group_t *g; /* Current group */
500 ppd_choice_t *c; /* Current choice */
ef416fc2 501
502
b94498cf 503 if (!ppd)
ef416fc2 504 return;
505
b94498cf 506 /*
507 * Clean out the marked array...
508 */
509
510 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
511 c;
512 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
7cf5915e 513 {
b94498cf 514 cupsArrayRemove(ppd->marked, c);
7cf5915e
MS
515 c->marked = 0;
516 }
b94498cf 517
518 /*
519 * Then repopulate it with the defaults...
520 */
521
ef416fc2 522 for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
523 ppd_defaults(ppd, g);
93a5da07
MS
524
525 /*
526 * Finally, tag any conflicts (API compatibility) once at the end.
527 */
528
529 ppdConflicts(ppd);
ef416fc2 530}
531
532
533/*
66ab9486
MS
534 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
535 * conflicts.
ef416fc2 536 */
537
538int /* O - Number of conflicts */
539ppdMarkOption(ppd_file_t *ppd, /* I - PPD file record */
540 const char *option, /* I - Keyword */
541 const char *choice) /* I - Option name */
542{
e07d4801 543 DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
bd7854cb 544 ppd, option, choice));
545
fa73b229 546 /*
547 * Range check input...
548 */
549
550 if (!ppd || !option || !choice)
ef416fc2 551 return (0);
552
66ab9486
MS
553 /*
554 * Mark the option...
555 */
556
557 ppd_mark_option(ppd, option, choice);
558
559 /*
560 * Return the number of conflicts...
561 */
562
563 return (ppdConflicts(ppd));
564}
565
566
567/*
568 * 'ppdFirstOption()' - Return the first option in the PPD file.
569 *
570 * Options are returned from all groups in ascending alphanumeric order.
571 *
8072030b 572 * @since CUPS 1.2/macOS 10.5@
66ab9486
MS
573 */
574
575ppd_option_t * /* O - First option or @code NULL@ */
576ppdFirstOption(ppd_file_t *ppd) /* I - PPD file */
577{
578 if (!ppd)
579 return (NULL);
580 else
581 return ((ppd_option_t *)cupsArrayFirst(ppd->options));
582}
583
584
585/*
586 * 'ppdNextOption()' - Return the next option in the PPD file.
587 *
588 * Options are returned from all groups in ascending alphanumeric order.
589 *
8072030b 590 * @since CUPS 1.2/macOS 10.5@
66ab9486
MS
591 */
592
593ppd_option_t * /* O - Next option or @code NULL@ */
594ppdNextOption(ppd_file_t *ppd) /* I - PPD file */
595{
596 if (!ppd)
597 return (NULL);
598 else
599 return ((ppd_option_t *)cupsArrayNext(ppd->options));
600}
601
602
603/*
604 * '_ppdParseOptions()' - Parse options from a PPD file.
605 *
606 * This function looks for strings of the form:
607 *
608 * *option choice ... *optionN choiceN
c7017ecc 609 * property value ... propertyN valueN
66ab9486
MS
610 *
611 * It stops when it finds a string that doesn't match this format.
612 */
613
614int /* O - Number of options */
615_ppdParseOptions(
616 const char *s, /* I - String to parse */
617 int num_options, /* I - Number of options */
c7017ecc
MS
618 cups_option_t **options, /* IO - Options */
619 _ppd_parse_t which) /* I - What to parse */
66ab9486 620{
7cf5915e 621 char option[PPD_MAX_NAME * 2 + 1], /* Current option/property */
c7017ecc 622 choice[PPD_MAX_NAME], /* Current choice/value */
66ab9486
MS
623 *ptr; /* Pointer into option or choice */
624
625
626 if (!s)
627 return (num_options);
628
629 /*
c7017ecc
MS
630 * Read all of the "*Option Choice" and "property value" pairs from the
631 * string, add them to an options array as we go...
66ab9486
MS
632 */
633
634 while (*s)
635 {
636 /*
637 * Skip leading whitespace...
638 */
639
7cf5915e 640 while (_cups_isspace(*s))
66ab9486
MS
641 s ++;
642
66ab9486 643 /*
c7017ecc 644 * Get the option/property name...
66ab9486
MS
645 */
646
66ab9486 647 ptr = option;
7cf5915e 648 while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
66ab9486
MS
649 *ptr++ = *s++;
650
7cf5915e 651 if (ptr == s || !_cups_isspace(*s))
66ab9486
MS
652 break;
653
654 *ptr = '\0';
655
656 /*
657 * Get the choice...
658 */
659
7cf5915e 660 while (_cups_isspace(*s))
66ab9486
MS
661 s ++;
662
663 if (!*s)
664 break;
665
666 ptr = choice;
7cf5915e 667 while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
66ab9486
MS
668 *ptr++ = *s++;
669
7cf5915e 670 if (*s && !_cups_isspace(*s))
c7017ecc
MS
671 break;
672
66ab9486
MS
673 *ptr = '\0';
674
675 /*
676 * Add it to the options array...
677 */
678
c7017ecc
MS
679 if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES)
680 num_options = cupsAddOption(option + 1, choice, num_options, options);
681 else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS)
682 num_options = cupsAddOption(option, choice, num_options, options);
66ab9486
MS
683 }
684
685 return (num_options);
686}
687
688
689#ifdef DEBUG
690/*
ba55dc12 691 * 'ppd_debug_marked()' - Output the marked array to stdout...
66ab9486
MS
692 */
693
694static void
ba55dc12 695ppd_debug_marked(ppd_file_t *ppd, /* I - PPD file data */
66ab9486
MS
696 const char *title) /* I - Title for list */
697{
698 ppd_choice_t *c; /* Current choice */
699
700
e07d4801 701 DEBUG_printf(("2cupsMarkOptions: %s", title));
66ab9486
MS
702
703 for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
704 c;
705 c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
e07d4801 706 DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
66ab9486
MS
707}
708#endif /* DEBUG */
709
710
711/*
712 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
713 */
714
715static void
716ppd_defaults(ppd_file_t *ppd, /* I - PPD file */
717 ppd_group_t *g) /* I - Group to default */
718{
719 int i; /* Looping var */
720 ppd_option_t *o; /* Current option */
721 ppd_group_t *sg; /* Current sub-group */
722
723
724 for (i = g->num_options, o = g->options; i > 0; i --, o ++)
88f9aafc 725 if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
93a5da07 726 ppd_mark_option(ppd, o->keyword, o->defchoice);
66ab9486
MS
727
728 for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
729 ppd_defaults(ppd, sg);
730}
731
732
733/*
734 * 'ppd_mark_choices()' - Mark one or more option choices from a string.
735 */
736
737static void
738ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
739 const char *s) /* I - "*Option Choice ..." string */
740{
741 int i, /* Looping var */
742 num_options; /* Number of options */
743 cups_option_t *options, /* Options */
744 *option; /* Current option */
745
746
5d6412a9 747 if (!s)
66ab9486
MS
748 return;
749
750 options = NULL;
c7017ecc 751 num_options = _ppdParseOptions(s, 0, &options, 0);
66ab9486
MS
752
753 for (i = num_options, option = options; i > 0; i --, option ++)
754 ppd_mark_option(ppd, option->name, option->value);
755
756 cupsFreeOptions(num_options, options);
757}
758
759
760/*
761 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
762 */
763
764static void
765ppd_mark_option(ppd_file_t *ppd, /* I - PPD file */
766 const char *option, /* I - Option name */
767 const char *choice) /* I - Choice name */
768{
769 int i, j; /* Looping vars */
770 ppd_option_t *o; /* Option pointer */
771 ppd_choice_t *c, /* Choice pointer */
772 *oldc, /* Old choice pointer */
773 key; /* Search key for choice */
774 struct lconv *loc; /* Locale data */
775
776
e07d4801 777 DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
66ab9486
MS
778 ppd, option, choice));
779
fa73b229 780 /*
d4874933 781 * AP_D_InputSlot is the "default input slot" on macOS, and setting
fa73b229 782 * it clears the regular InputSlot choices...
783 */
ef416fc2 784
88f9aafc 785 if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
fa73b229 786 {
ef55b745
MS
787 cupsArraySave(ppd->options);
788
fa73b229 789 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
b94498cf 790 {
791 key.option = o;
792 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
793 {
794 oldc->marked = 0;
795 cupsArrayRemove(ppd->marked, oldc);
796 }
797 }
ef55b745
MS
798
799 cupsArrayRestore(ppd->options);
ef416fc2 800 }
801
fa73b229 802 /*
803 * Check for custom options...
804 */
805
ef55b745
MS
806 cupsArraySave(ppd->options);
807
808 o = ppdFindOption(ppd, option);
809
810 cupsArrayRestore(ppd->options);
811
812 if (!o)
66ab9486 813 return;
ef416fc2 814
757d2cad 815 loc = localeconv();
ef416fc2 816
88f9aafc 817 if (!_cups_strncasecmp(choice, "Custom.", 7))
ef416fc2 818 {
819 /*
fa73b229 820 * Handle a custom option...
ef416fc2 821 */
822
fa73b229 823 if ((c = ppdFindChoice(o, "Custom")) == NULL)
66ab9486 824 return;
ef416fc2 825
88f9aafc 826 if (!_cups_strcasecmp(option, "PageSize"))
fa73b229 827 {
828 /*
829 * Handle custom page sizes...
830 */
ef416fc2 831
fa73b229 832 ppdPageSize(ppd, choice);
833 }
834 else
ef416fc2 835 {
836 /*
fa73b229 837 * Handle other custom options...
ef416fc2 838 */
839
fa73b229 840 ppd_coption_t *coption; /* Custom option */
841 ppd_cparam_t *cparam; /* Custom parameter */
757d2cad 842 char *units; /* Custom points units */
fa73b229 843
8ca02f3c 844
fa73b229 845 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
ef416fc2 846 {
fa73b229 847 if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
66ab9486 848 return;
fa73b229 849
850 switch (cparam->type)
851 {
852 case PPD_CUSTOM_CURVE :
853 case PPD_CUSTOM_INVCURVE :
854 case PPD_CUSTOM_REAL :
b86bc4cf 855 cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
856 NULL, loc);
fa73b229 857 break;
858
859 case PPD_CUSTOM_POINTS :
b86bc4cf 860 cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
861 &units,
862 loc);
757d2cad 863
864 if (units)
865 {
88f9aafc 866 if (!_cups_strcasecmp(units, "cm"))
ef55b745 867 cparam->current.custom_points *= 72.0f / 2.54f;
88f9aafc 868 else if (!_cups_strcasecmp(units, "mm"))
ef55b745 869 cparam->current.custom_points *= 72.0f / 25.4f;
88f9aafc 870 else if (!_cups_strcasecmp(units, "m"))
ef55b745 871 cparam->current.custom_points *= 72.0f / 0.0254f;
88f9aafc 872 else if (!_cups_strcasecmp(units, "in"))
ef55b745 873 cparam->current.custom_points *= 72.0f;
88f9aafc 874 else if (!_cups_strcasecmp(units, "ft"))
ef55b745 875 cparam->current.custom_points *= 12.0f * 72.0f;
757d2cad 876 }
fa73b229 877 break;
878
879 case PPD_CUSTOM_INT :
880 cparam->current.custom_int = atoi(choice + 7);
881 break;
882
883 case PPD_CUSTOM_PASSCODE :
884 case PPD_CUSTOM_PASSWORD :
885 case PPD_CUSTOM_STRING :
886 if (cparam->current.custom_string)
2e4ff8af 887 _cupsStrFree(cparam->current.custom_string);
fa73b229 888
2e4ff8af 889 cparam->current.custom_string = _cupsStrAlloc(choice + 7);
fa73b229 890 break;
891 }
ef416fc2 892 }
893 }
8ca02f3c 894
895 /*
896 * Make sure that we keep the option marked below...
897 */
898
899 choice = "Custom";
fa73b229 900 }
b423cd4c 901 else if (choice[0] == '{')
902 {
903 /*
904 * Handle multi-value custom options...
905 */
906
907 ppd_coption_t *coption; /* Custom option */
908 ppd_cparam_t *cparam; /* Custom parameter */
757d2cad 909 char *units; /* Custom points units */
b423cd4c 910 int num_vals; /* Number of values */
911 cups_option_t *vals, /* Values */
912 *val; /* Value */
913
914
915 if ((c = ppdFindChoice(o, "Custom")) == NULL)
66ab9486 916 return;
b423cd4c 917
918 if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
919 {
ee571f26 920 num_vals = cupsParseOptions(choice, 0, &vals);
b423cd4c 921
922 for (i = 0, val = vals; i < num_vals; i ++, val ++)
923 {
924 if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
925 continue;
926
927 switch (cparam->type)
928 {
929 case PPD_CUSTOM_CURVE :
930 case PPD_CUSTOM_INVCURVE :
931 case PPD_CUSTOM_REAL :
b86bc4cf 932 cparam->current.custom_real = (float)_cupsStrScand(val->value,
933 NULL, loc);
b423cd4c 934 break;
935
936 case PPD_CUSTOM_POINTS :
b86bc4cf 937 cparam->current.custom_points = (float)_cupsStrScand(val->value,
938 &units,
939 loc);
757d2cad 940
941 if (units)
942 {
88f9aafc 943 if (!_cups_strcasecmp(units, "cm"))
b86bc4cf 944 cparam->current.custom_points *= 72.0f / 2.54f;
88f9aafc 945 else if (!_cups_strcasecmp(units, "mm"))
b86bc4cf 946 cparam->current.custom_points *= 72.0f / 25.4f;
88f9aafc 947 else if (!_cups_strcasecmp(units, "m"))
b86bc4cf 948 cparam->current.custom_points *= 72.0f / 0.0254f;
88f9aafc 949 else if (!_cups_strcasecmp(units, "in"))
b86bc4cf 950 cparam->current.custom_points *= 72.0f;
88f9aafc 951 else if (!_cups_strcasecmp(units, "ft"))
b86bc4cf 952 cparam->current.custom_points *= 12.0f * 72.0f;
757d2cad 953 }
b423cd4c 954 break;
955
956 case PPD_CUSTOM_INT :
957 cparam->current.custom_int = atoi(val->value);
958 break;
959
960 case PPD_CUSTOM_PASSCODE :
961 case PPD_CUSTOM_PASSWORD :
962 case PPD_CUSTOM_STRING :
963 if (cparam->current.custom_string)
2e4ff8af 964 _cupsStrFree(cparam->current.custom_string);
b423cd4c 965
426c6a59 966 cparam->current.custom_string = _cupsStrRetain(val->value);
b423cd4c 967 break;
968 }
969 }
970
971 cupsFreeOptions(num_vals, vals);
972 }
973 }
fa73b229 974 else
975 {
976 for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
88f9aafc 977 if (!_cups_strcasecmp(c->choice, choice))
fa73b229 978 break;
ef416fc2 979
fa73b229 980 if (!i)
66ab9486 981 return;
fa73b229 982 }
ef416fc2 983
fa73b229 984 /*
985 * Option found; mark it and then handle unmarking any other options.
986 */
987
fa73b229 988 if (o->ui != PPD_UI_PICKMANY)
989 {
990 /*
991 * Unmark all other choices...
992 */
993
b94498cf 994 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
995 {
996 oldc->marked = 0;
997 cupsArrayRemove(ppd->marked, oldc);
998 }
999
88f9aafc 1000 if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
b94498cf 1001 {
1002 /*
1003 * Mark current page size...
1004 */
1005
1006 for (j = 0; j < ppd->num_sizes; j ++)
88f9aafc 1007 ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
b94498cf 1008 choice);
1009
1010 /*
1011 * Unmark the current PageSize or PageRegion setting, as
1012 * appropriate...
1013 */
1014
ef55b745
MS
1015 cupsArraySave(ppd->options);
1016
88f9aafc 1017 if (!_cups_strcasecmp(option, "PageSize"))
fa73b229 1018 {
b94498cf 1019 if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
1020 {
1021 key.option = o;
1022 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1023 {
1024 oldc->marked = 0;
1025 cupsArrayRemove(ppd->marked, oldc);
1026 }
1027 }
1028 }
1029 else
1030 {
1031 if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
1032 {
1033 key.option = o;
1034 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1035 {
1036 oldc->marked = 0;
1037 cupsArrayRemove(ppd->marked, oldc);
1038 }
1039 }
1040 }
ef55b745
MS
1041
1042 cupsArrayRestore(ppd->options);
b94498cf 1043 }
88f9aafc 1044 else if (!_cups_strcasecmp(option, "InputSlot"))
b94498cf 1045 {
1046 /*
355e94dc 1047 * Unmark ManualFeed option...
b94498cf 1048 */
fa73b229 1049
ef55b745
MS
1050 cupsArraySave(ppd->options);
1051
b94498cf 1052 if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
1053 {
1054 key.option = o;
1055 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1056 {
1057 oldc->marked = 0;
1058 cupsArrayRemove(ppd->marked, oldc);
1059 }
1060 }
ef55b745
MS
1061
1062 cupsArrayRestore(ppd->options);
b94498cf 1063 }
88f9aafc
MS
1064 else if (!_cups_strcasecmp(option, "ManualFeed") &&
1065 !_cups_strcasecmp(choice, "True"))
b94498cf 1066 {
1067 /*
1068 * Unmark InputSlot option...
1069 */
fa73b229 1070
ef55b745
MS
1071 cupsArraySave(ppd->options);
1072
b94498cf 1073 if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
1074 {
1075 key.option = o;
1076 if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1077 {
1078 oldc->marked = 0;
1079 cupsArrayRemove(ppd->marked, oldc);
1080 }
fa73b229 1081 }
ef55b745
MS
1082
1083 cupsArrayRestore(ppd->options);
b94498cf 1084 }
ef416fc2 1085 }
1086
b94498cf 1087 c->marked = 1;
1088
1089 cupsArrayAdd(ppd->marked, c);
5a738aea 1090}