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