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