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