]>
Commit | Line | Data |
---|---|---|
e78998df MS |
1 | /* |
2 | * "$Id$" | |
3 | * | |
4 | * Option marking routines for the Common UNIX Printing System (CUPS). | |
5 | * | |
c168a833 | 6 | * Copyright 2007-2009 by Apple Inc. |
e78998df MS |
7 | * Copyright 1997-2007 by Easy Software Products, all rights reserved. |
8 | * | |
9 | * These coded instructions, statements, and computer programs are the | |
10 | * property of Apple Inc. and are protected by Federal copyright | |
11 | * law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
12 | * which should have been included with this file. If this file is | |
13 | * file is missing or damaged, see the license at "http://www.cups.org/". | |
14 | * | |
15 | * PostScript is a trademark of Adobe Systems, Inc. | |
16 | * | |
17 | * This file is subject to the Apple OS-Developed Software exception. | |
18 | * | |
19 | * Contents: | |
20 | * | |
d2354e63 MS |
21 | * cupsGetConflicts() - Get a list of conflicting options in a marked |
22 | * PPD. | |
06d4e77b | 23 | * cupsResolveConflicts() - Resolve conflicts in a marked PPD. |
e78998df MS |
24 | * ppdConflicts() - Check to see if there are any conflicts among |
25 | * the marked option choices. | |
26 | * ppdInstallableConflict() - Test whether an option choice conflicts with an | |
27 | * installable option. | |
e78998df MS |
28 | * ppd_is_installable() - Determine whether an option is in the |
29 | * InstallableOptions group. | |
30 | * ppd_load_constraints() - Load constraints from a PPD file. | |
31 | * ppd_test_constraints() - See if any constraints are active. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * Include necessary headers... | |
36 | */ | |
37 | ||
38 | #include "ppd-private.h" | |
39 | #include "string.h" | |
40 | #include "debug.h" | |
41 | ||
42 | ||
43 | /* | |
44 | * Local constants... | |
45 | */ | |
46 | ||
47 | enum | |
48 | { | |
49 | _PPD_NORMAL_CONSTRAINTS, | |
50 | _PPD_INSTALLABLE_CONSTRAINTS, | |
51 | _PPD_ALL_CONSTRAINTS | |
52 | }; | |
53 | ||
54 | ||
55 | /* | |
56 | * Local functions... | |
57 | */ | |
58 | ||
59 | static int ppd_is_installable(ppd_group_t *installable, | |
60 | const char *option); | |
61 | static void ppd_load_constraints(ppd_file_t *ppd); | |
06d4e77b MS |
62 | static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, |
63 | const char *option, | |
64 | const char *choice, | |
65 | int num_options, | |
e78998df MS |
66 | cups_option_t *options, |
67 | int which); | |
68 | ||
69 | ||
d2354e63 MS |
70 | /* |
71 | * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD. | |
72 | * | |
73 | * This function gets a list of options that would conflict if "option" and | |
74 | * "choice" were marked in the PPD. You would typically call this function | |
75 | * after marking the currently selected options in the PPD in order to | |
76 | * determine whether a new option selection would cause a conflict. | |
77 | * | |
78 | * The number of conflicting options are returned with "options" pointing to | |
79 | * the conflicting options. The returned option array must be freed using | |
80 | * @link cupsFreeOptions@. | |
81 | * | |
82 | * @since CUPS 1.4@ | |
83 | */ | |
84 | ||
85 | int /* O - Number of conflicting options */ | |
86 | cupsGetConflicts( | |
87 | ppd_file_t *ppd, /* I - PPD file */ | |
88 | const char *option, /* I - Option to test */ | |
89 | const char *choice, /* I - Choice to test */ | |
90 | cups_option_t **options) /* O - Conflicting options */ | |
91 | { | |
92 | int i, /* Looping var */ | |
93 | num_options; /* Number of conflicting options */ | |
94 | cups_array_t *active; /* Active conflicts */ | |
95 | _ppd_cups_uiconsts_t *c; /* Current constraints */ | |
96 | _ppd_cups_uiconst_t *cptr; /* Current constraint */ | |
97 | ||
98 | ||
99 | /* | |
100 | * Range check input... | |
101 | */ | |
102 | ||
103 | if (options) | |
104 | *options = NULL; | |
105 | ||
106 | if (!ppd || !option || !choice || !options) | |
107 | return (0); | |
108 | ||
109 | /* | |
110 | * Test for conflicts... | |
111 | */ | |
112 | ||
113 | active = ppd_test_constraints(ppd, option, choice, 0, NULL, | |
114 | _PPD_ALL_CONSTRAINTS); | |
115 | ||
116 | /* | |
117 | * Loop through all of the UI constraints and add any options that conflict... | |
118 | */ | |
119 | ||
120 | for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active); | |
121 | c; | |
122 | c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) | |
123 | { | |
124 | for (i = c->num_constraints, cptr = c->constraints; | |
125 | i > 0; | |
126 | i --, cptr ++) | |
127 | if (strcasecmp(cptr->option->keyword, option)) | |
128 | num_options = cupsAddOption(cptr->option->keyword, cptr->choice->choice, | |
129 | num_options, options); | |
130 | } | |
131 | ||
132 | cupsArrayDelete(active); | |
133 | ||
134 | return (num_options); | |
135 | } | |
136 | ||
137 | ||
e78998df MS |
138 | /* |
139 | * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD. | |
140 | * | |
141 | * This function attempts to resolve any conflicts in a marked PPD, returning | |
06d4e77b MS |
142 | * a list of option changes that are required to resolve them. On input, |
143 | * "num_options" and "options" contain any pending option changes that have | |
144 | * not yet been marked, while "option" and "choice" contain the most recent | |
e78998df MS |
145 | * selection which may or may not be in "num_options" or "options". |
146 | * | |
147 | * On successful return, "num_options" and "options" are updated to contain | |
148 | * "option" and "choice" along with any changes required to resolve conflicts | |
06d4e77b MS |
149 | * specified in the PPD file and 1 is returned. |
150 | * | |
151 | * If option conflicts cannot be resolved, "num_options" and "options" are not | |
152 | * changed and 0 is returned. | |
e78998df | 153 | * |
06d4e77b MS |
154 | * When resolving conflicts, @code cupsResolveConflicts@ does not consider |
155 | * changes to the current page size (@code media@, @code PageSize@, and | |
156 | * @code PageRegion@) or to the most recent option specified in "option". | |
157 | * Thus, if the only way to resolve a conflict is to change the page size | |
158 | * or the option the user most recently changed, @code cupsResolveConflicts@ | |
159 | * will return 0 to indicate it was unable to resolve the conflicts. | |
160 | * | |
161 | * The @code cupsResolveConflicts@ function uses one of two sources of option | |
162 | * constraint information. The preferred constraint information is defined by | |
e78998df | 163 | * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this |
06d4e77b | 164 | * case, the PPD file provides constraint resolution actions. |
e78998df | 165 | * |
06d4e77b | 166 | * The backup constraint information is defined by the |
e78998df | 167 | * @code UIConstraints@ and @code NonUIConstraints@ attributes. These |
06d4e77b MS |
168 | * constraints are resolved algorithmically by first selecting the default |
169 | * choice for the conflicting option, then iterating over all possible choices | |
170 | * until a non-conflicting option choice is found. | |
e78998df MS |
171 | * |
172 | * @since CUPS 1.4@ | |
173 | */ | |
174 | ||
175 | int /* O - 1 on success, 0 on failure */ | |
176 | cupsResolveConflicts( | |
177 | ppd_file_t *ppd, /* I - PPD file */ | |
178 | const char *option, /* I - Newly selected option or @code NULL@ for none */ | |
179 | const char *choice, /* I - Newly selected choice or @code NULL@ for none */ | |
180 | int *num_options, /* IO - Number of additional selected options */ | |
181 | cups_option_t **options) /* IO - Additional selected options */ | |
182 | { | |
183 | int i, /* Looping var */ | |
06d4e77b MS |
184 | num_newopts, /* Number of new options */ |
185 | num_resopts; /* Number of resolver options */ | |
186 | cups_option_t *newopts, /* New options */ | |
187 | *resopts; /* Resolver options */ | |
e78998df MS |
188 | cups_array_t *active, /* Active constraints */ |
189 | *pass, /* Resolvers for this pass */ | |
190 | *resolvers; /* Resolvers we have used */ | |
191 | _ppd_cups_uiconsts_t *consts; /* Current constraints */ | |
192 | _ppd_cups_uiconst_t *constptr; /* Current constraint */ | |
193 | ppd_attr_t *resolver; /* Current resolver */ | |
194 | const char *value; /* Selected option value */ | |
195 | int changed; /* Did we change anything? */ | |
196 | ppd_choice_t *marked; /* Marked choice */ | |
e78998df MS |
197 | |
198 | ||
199 | /* | |
200 | * Range check input... | |
201 | */ | |
202 | ||
203 | if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL)) | |
204 | return (0); | |
205 | ||
206 | /* | |
207 | * Build a shadow option array... | |
208 | */ | |
209 | ||
210 | num_newopts = 0; | |
211 | newopts = NULL; | |
212 | ||
213 | for (i = 0; i < *num_options; i ++) | |
214 | num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value, | |
215 | num_newopts, &newopts); | |
005dd1eb | 216 | if (option && strcasecmp(option, "Collate")) |
e78998df MS |
217 | num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); |
218 | ||
219 | /* | |
220 | * Loop until we have no conflicts... | |
221 | */ | |
222 | ||
223 | cupsArraySave(ppd->sorted_attrs); | |
224 | ||
225 | resolvers = NULL; | |
226 | pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL); | |
227 | ||
06d4e77b | 228 | while ((active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts, |
e78998df MS |
229 | _PPD_ALL_CONSTRAINTS)) != NULL) |
230 | { | |
231 | if (!resolvers) | |
232 | resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL); | |
233 | ||
234 | for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0; | |
235 | consts; | |
236 | consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) | |
237 | { | |
238 | if (consts->resolver[0]) | |
239 | { | |
240 | /* | |
241 | * Look up the resolver... | |
242 | */ | |
243 | ||
244 | if (cupsArrayFind(pass, consts->resolver)) | |
245 | continue; /* Already applied this resolver... */ | |
246 | ||
247 | if (cupsArrayFind(resolvers, consts->resolver)) | |
248 | { | |
249 | /* | |
250 | * Resolver loop! | |
251 | */ | |
252 | ||
253 | DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n", | |
254 | consts->resolver)); | |
255 | goto error; | |
256 | } | |
257 | ||
258 | if ((resolver = ppdFindAttr(ppd, "cupsUIResolver", | |
259 | consts->resolver)) == NULL) | |
260 | { | |
261 | DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n", | |
262 | consts->resolver)); | |
263 | goto error; | |
264 | } | |
265 | ||
266 | if (!resolver->value) | |
267 | { | |
268 | DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n", | |
269 | consts->resolver)); | |
270 | goto error; | |
271 | } | |
272 | ||
273 | /* | |
274 | * Add the options from the resolver... | |
275 | */ | |
276 | ||
277 | cupsArrayAdd(pass, consts->resolver); | |
278 | cupsArrayAdd(resolvers, consts->resolver); | |
279 | ||
06d4e77b MS |
280 | if (option && choice) |
281 | { | |
282 | resopts = NULL; | |
283 | num_resopts = _ppdParseOptions(resolver->value, 0, &resopts); | |
284 | ||
285 | if ((value = cupsGetOption(option, num_newopts, newopts)) != NULL && | |
286 | !strcasecmp(value, choice)) | |
287 | { | |
288 | DEBUG_printf(("cupsResolveConflicts: Resolver %s changes %s!\n", | |
289 | consts->resolver, option)); | |
290 | cupsFreeOptions(num_resopts, resopts); | |
291 | goto error; | |
292 | } | |
293 | ||
294 | cupsFreeOptions(num_resopts, resopts); | |
295 | } | |
296 | ||
e78998df | 297 | num_newopts = _ppdParseOptions(resolver->value, num_newopts, &newopts); |
06d4e77b | 298 | changed = 1; |
e78998df MS |
299 | } |
300 | else | |
301 | { | |
302 | /* | |
303 | * Try resolving by choosing the default values for non-installable | |
06d4e77b | 304 | * options, then by iterating through the possible choices... |
e78998df MS |
305 | */ |
306 | ||
06d4e77b MS |
307 | int j; /* Looping var */ |
308 | ppd_choice_t *cptr; /* Current choice */ | |
309 | cups_array_t *test; /* Test array for conflicts */ | |
d1c13e16 | 310 | ppd_size_t *size; /* Current page size */ |
06d4e77b MS |
311 | |
312 | ||
313 | for (i = consts->num_constraints, constptr = consts->constraints; | |
c168a833 | 314 | i > 0 && !changed; |
e78998df MS |
315 | i --, constptr ++) |
316 | { | |
c168a833 MS |
317 | /* |
318 | * Can't resolve by changing an installable option... | |
319 | */ | |
320 | ||
321 | if (constptr->installable) | |
e78998df MS |
322 | continue; |
323 | ||
c168a833 MS |
324 | /* |
325 | * Is this the option we are changing? | |
326 | */ | |
327 | ||
d1c13e16 MS |
328 | if (option && |
329 | (!strcasecmp(constptr->option->keyword, option) || | |
330 | (!strcasecmp(option, "PageSize") && | |
331 | !strcasecmp(constptr->option->keyword, "PageRegion")) || | |
332 | (!strcasecmp(option, "PageRegion") && | |
333 | !strcasecmp(constptr->option->keyword, "PageSize")))) | |
e78998df | 334 | continue; |
e78998df | 335 | |
c168a833 MS |
336 | /* |
337 | * Get the current option choice... | |
338 | */ | |
339 | ||
e78998df MS |
340 | if ((value = cupsGetOption(constptr->option->keyword, num_newopts, |
341 | newopts)) == NULL) | |
342 | { | |
d1c13e16 MS |
343 | if (!strcasecmp(constptr->option->keyword, "PageSize") || |
344 | !strcasecmp(constptr->option->keyword, "PageRegion")) | |
345 | { | |
346 | if ((value = cupsGetOption("PageSize", num_newopts, | |
347 | newopts)) == NULL) | |
348 | value = cupsGetOption("PageRegion", num_newopts, newopts); | |
349 | ||
350 | if (!value) | |
351 | { | |
352 | if ((size = ppdPageSize(ppd, NULL)) != NULL) | |
353 | value = size->name; | |
354 | else | |
355 | value = ""; | |
356 | } | |
357 | } | |
358 | else | |
359 | { | |
360 | marked = ppdFindMarkedChoice(ppd, constptr->option->keyword); | |
361 | value = marked ? marked->choice : ""; | |
362 | } | |
e78998df MS |
363 | } |
364 | ||
d1c13e16 MS |
365 | if (!strncasecmp(value, "Custom.", 7)) |
366 | value = "Custom"; | |
367 | ||
06d4e77b MS |
368 | /* |
369 | * Try the default choice... | |
e78998df MS |
370 | */ |
371 | ||
06d4e77b | 372 | test = NULL; |
e78998df | 373 | |
06d4e77b MS |
374 | if (strcasecmp(value, constptr->option->defchoice) && |
375 | (test = ppd_test_constraints(ppd, constptr->option->keyword, | |
376 | constptr->option->defchoice, | |
377 | num_newopts, newopts, | |
378 | _PPD_ALL_CONSTRAINTS)) == NULL) | |
e78998df | 379 | { |
06d4e77b MS |
380 | /* |
381 | * That worked... | |
382 | */ | |
383 | ||
384 | num_newopts = cupsAddOption(constptr->option->keyword, | |
385 | constptr->option->defchoice, | |
e78998df MS |
386 | num_newopts, &newopts); |
387 | changed = 1; | |
388 | } | |
06d4e77b MS |
389 | else |
390 | { | |
391 | /* | |
392 | * Try each choice instead... | |
393 | */ | |
394 | ||
06d4e77b MS |
395 | for (j = constptr->option->num_choices, |
396 | cptr = constptr->option->choices; | |
397 | j > 0; | |
398 | j --, cptr ++) | |
399 | { | |
c168a833 | 400 | cupsArrayDelete(test); |
06d4e77b MS |
401 | test = NULL; |
402 | ||
403 | if (strcasecmp(value, cptr->choice) && | |
404 | strcasecmp(constptr->option->defchoice, cptr->choice) && | |
d1c13e16 | 405 | strcasecmp("Custom", cptr->choice) && |
06d4e77b MS |
406 | (test = ppd_test_constraints(ppd, constptr->option->keyword, |
407 | cptr->choice, num_newopts, | |
408 | newopts, | |
409 | _PPD_ALL_CONSTRAINTS)) == NULL) | |
410 | { | |
411 | /* | |
412 | * This choice works... | |
413 | */ | |
414 | ||
415 | num_newopts = cupsAddOption(constptr->option->keyword, | |
416 | cptr->choice, num_newopts, | |
417 | &newopts); | |
418 | changed = 1; | |
419 | break; | |
420 | } | |
06d4e77b | 421 | } |
c168a833 MS |
422 | |
423 | cupsArrayDelete(test); | |
06d4e77b MS |
424 | } |
425 | } | |
e78998df MS |
426 | } |
427 | ||
428 | if (!changed) | |
429 | { | |
430 | DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve " | |
431 | "constraint!"); | |
432 | goto error; | |
433 | } | |
434 | } | |
435 | ||
436 | cupsArrayClear(pass); | |
437 | cupsArrayDelete(active); | |
438 | } | |
439 | ||
440 | /* | |
005dd1eb | 441 | * Free the caller's option array... |
e78998df MS |
442 | */ |
443 | ||
005dd1eb MS |
444 | cupsFreeOptions(*num_options, *options); |
445 | ||
446 | /* | |
447 | * If Collate is the option we are testing, add it here. Otherwise, remove | |
448 | * any Collate option from the resolve list since the filters automatically | |
449 | * handle manual collation... | |
450 | */ | |
451 | ||
452 | if (option && !strcasecmp(option, "Collate")) | |
453 | num_newopts = cupsAddOption(option, choice, num_newopts, &newopts); | |
e78998df | 454 | else |
005dd1eb MS |
455 | num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts); |
456 | ||
457 | /* | |
458 | * Return the new list of options to the caller... | |
459 | */ | |
460 | ||
461 | *num_options = num_newopts; | |
462 | *options = newopts; | |
e78998df MS |
463 | |
464 | cupsArrayDelete(pass); | |
465 | cupsArrayDelete(resolvers); | |
466 | ||
467 | cupsArrayRestore(ppd->sorted_attrs); | |
468 | ||
d1c13e16 MS |
469 | DEBUG_printf(("cupsResolveConflicts: Returning %d options:", num_newopts)); |
470 | #ifdef DEBUG | |
471 | for (i = 0; i < num_newopts; i ++) | |
472 | DEBUG_printf(("cupsResolveConflicts: options[%d]: %s=%s", i, | |
473 | newopts[i].name, newopts[i].value)); | |
474 | #endif /* DEBUG */ | |
475 | ||
e78998df MS |
476 | return (1); |
477 | ||
478 | /* | |
479 | * If we get here, we failed to resolve... | |
480 | */ | |
481 | ||
482 | error: | |
483 | ||
484 | cupsFreeOptions(num_newopts, newopts); | |
485 | ||
486 | cupsArrayDelete(pass); | |
487 | cupsArrayDelete(resolvers); | |
488 | ||
489 | cupsArrayRestore(ppd->sorted_attrs); | |
490 | ||
d1c13e16 MS |
491 | DEBUG_puts("cupsResolveConflicts: Unable to resolve conflicts!"); |
492 | ||
e78998df MS |
493 | return (0); |
494 | } | |
495 | ||
496 | ||
06d4e77b MS |
497 | /* |
498 | * 'ppdConflicts()' - Check to see if there are any conflicts among the | |
499 | * marked option choices. | |
500 | * | |
501 | * The returned value is the same as returned by @link ppdMarkOption@. | |
502 | */ | |
503 | ||
504 | int /* O - Number of conflicts found */ | |
505 | ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */ | |
506 | { | |
507 | int i, /* Looping variable */ | |
508 | conflicts; /* Number of conflicts */ | |
509 | cups_array_t *active; /* Active conflicts */ | |
510 | _ppd_cups_uiconsts_t *c; /* Current constraints */ | |
511 | _ppd_cups_uiconst_t *cptr; /* Current constraint */ | |
512 | ppd_option_t *o; /* Current option */ | |
513 | ||
514 | ||
515 | if (!ppd) | |
516 | return (0); | |
517 | ||
518 | /* | |
519 | * Clear all conflicts... | |
520 | */ | |
521 | ||
522 | for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd)) | |
523 | o->conflicted = 0; | |
524 | ||
525 | /* | |
526 | * Test for conflicts... | |
527 | */ | |
528 | ||
529 | active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL, | |
530 | _PPD_ALL_CONSTRAINTS); | |
531 | conflicts = cupsArrayCount(active); | |
532 | ||
533 | /* | |
534 | * Loop through all of the UI constraints and flag any options | |
535 | * that conflict... | |
536 | */ | |
537 | ||
538 | for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active); | |
539 | c; | |
540 | c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active)) | |
541 | { | |
542 | for (i = c->num_constraints, cptr = c->constraints; | |
543 | i > 0; | |
544 | i --, cptr ++) | |
545 | cptr->option->conflicted = 1; | |
546 | } | |
547 | ||
548 | cupsArrayDelete(active); | |
549 | ||
550 | /* | |
551 | * Return the number of conflicts found... | |
552 | */ | |
553 | ||
554 | return (conflicts); | |
555 | } | |
556 | ||
557 | ||
558 | /* | |
559 | * 'ppdInstallableConflict()' - Test whether an option choice conflicts with | |
560 | * an installable option. | |
561 | * | |
562 | * This function tests whether a particular option choice is available based | |
563 | * on constraints against options in the "InstallableOptions" group. | |
564 | * | |
565 | * @since CUPS 1.4@ | |
566 | */ | |
567 | ||
568 | int /* O - 1 if conflicting, 0 if not conflicting */ | |
569 | ppdInstallableConflict( | |
570 | ppd_file_t *ppd, /* I - PPD file */ | |
571 | const char *option, /* I - Option */ | |
572 | const char *choice) /* I - Choice */ | |
573 | { | |
574 | cups_array_t *active; /* Active conflicts */ | |
575 | ||
576 | ||
577 | /* | |
578 | * Range check input... | |
579 | */ | |
580 | ||
581 | if (!ppd || !option || !choice) | |
582 | return (0); | |
583 | ||
584 | /* | |
585 | * Test constraints using the new option... | |
586 | */ | |
587 | ||
588 | active = ppd_test_constraints(ppd, option, choice, 0, NULL, | |
589 | _PPD_INSTALLABLE_CONSTRAINTS); | |
590 | ||
591 | cupsArrayDelete(active); | |
592 | ||
593 | return (active != NULL); | |
594 | } | |
595 | ||
596 | ||
e78998df MS |
597 | /* |
598 | * 'ppd_is_installable()' - Determine whether an option is in the | |
599 | * InstallableOptions group. | |
600 | */ | |
601 | ||
602 | static int /* O - 1 if installable, 0 if normal */ | |
603 | ppd_is_installable( | |
604 | ppd_group_t *installable, /* I - InstallableOptions group */ | |
605 | const char *name) /* I - Option name */ | |
606 | { | |
607 | if (installable) | |
608 | { | |
609 | int i; /* Looping var */ | |
610 | ppd_option_t *option; /* Current option */ | |
611 | ||
612 | ||
613 | for (i = installable->num_options, option = installable->options; | |
614 | i > 0; | |
615 | i --, option ++) | |
616 | if (!strcasecmp(option->keyword, name)) | |
617 | return (1); | |
618 | } | |
619 | ||
620 | return (0); | |
621 | } | |
622 | ||
623 | ||
624 | /* | |
625 | * 'ppd_load_constraints()' - Load constraints from a PPD file. | |
626 | */ | |
627 | ||
628 | static void | |
629 | ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */ | |
630 | { | |
631 | int i; /* Looping var */ | |
632 | ppd_const_t *oldconst; /* Current UIConstraints data */ | |
633 | ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */ | |
634 | _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */ | |
635 | _ppd_cups_uiconst_t *constptr; /* Current constraint */ | |
636 | ppd_group_t *installable; /* Installable options group */ | |
637 | const char *vptr; /* Pointer into constraint value */ | |
638 | char option[PPD_MAX_NAME], /* Option name/MainKeyword */ | |
639 | choice[PPD_MAX_NAME], /* Choice/OptionKeyword */ | |
640 | *ptr; /* Pointer into option or choice */ | |
641 | ||
642 | ||
643 | /* | |
644 | * Create an array to hold the constraint data... | |
645 | */ | |
646 | ||
647 | ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL); | |
648 | ||
649 | /* | |
650 | * Find the installable options group if it exists... | |
651 | */ | |
652 | ||
653 | for (i = ppd->num_groups, installable = ppd->groups; | |
654 | i > 0; | |
655 | i --, installable ++) | |
656 | if (!strcasecmp(installable->name, "InstallableOptions")) | |
657 | break; | |
658 | ||
659 | if (i <= 0) | |
660 | installable = NULL; | |
661 | ||
662 | /* | |
28b9d139 | 663 | * Load old-style [Non]UIConstraints data... |
e78998df MS |
664 | */ |
665 | ||
28b9d139 | 666 | for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++) |
e78998df MS |
667 | { |
668 | /* | |
28b9d139 MS |
669 | * Weed out nearby duplicates, since the PPD spec requires that you |
670 | * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"... | |
e78998df MS |
671 | */ |
672 | ||
28b9d139 MS |
673 | if (i > 1 && |
674 | !strcasecmp(oldconst[0].option1, oldconst[1].option2) && | |
675 | !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) && | |
676 | !strcasecmp(oldconst[0].option2, oldconst[1].option1) && | |
677 | !strcasecmp(oldconst[0].choice2, oldconst[1].choice1)) | |
678 | continue; | |
e78998df | 679 | |
28b9d139 MS |
680 | /* |
681 | * Allocate memory... | |
682 | */ | |
e78998df | 683 | |
28b9d139 MS |
684 | if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL) |
685 | { | |
686 | DEBUG_puts("ppd_load_constraints: Unable to allocate memory for " | |
687 | "UIConstraints!"); | |
688 | return; | |
689 | } | |
e78998df | 690 | |
28b9d139 MS |
691 | if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL) |
692 | { | |
693 | free(consts); | |
694 | DEBUG_puts("ppd_load_constraints: Unable to allocate memory for " | |
695 | "UIConstraints!"); | |
696 | return; | |
697 | } | |
e78998df | 698 | |
28b9d139 MS |
699 | /* |
700 | * Fill in the information... | |
701 | */ | |
e78998df | 702 | |
28b9d139 MS |
703 | consts->num_constraints = 2; |
704 | consts->constraints = constptr; | |
e78998df | 705 | |
28b9d139 MS |
706 | if (!strncasecmp(oldconst->option1, "Custom", 6) && |
707 | !strcasecmp(oldconst->choice1, "True")) | |
708 | { | |
709 | constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6); | |
710 | constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom"); | |
711 | constptr[0].installable = 0; | |
712 | } | |
713 | else | |
714 | { | |
715 | constptr[0].option = ppdFindOption(ppd, oldconst->option1); | |
716 | constptr[0].choice = ppdFindChoice(constptr[0].option, | |
717 | oldconst->choice1); | |
718 | constptr[0].installable = ppd_is_installable(installable, | |
719 | oldconst->option1); | |
720 | } | |
e78998df | 721 | |
28b9d139 MS |
722 | if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0])) |
723 | { | |
724 | DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n", | |
725 | oldconst->option1, oldconst->choice1)); | |
726 | free(consts->constraints); | |
727 | free(consts); | |
728 | continue; | |
729 | } | |
e78998df | 730 | |
28b9d139 MS |
731 | if (!strncasecmp(oldconst->option2, "Custom", 6) && |
732 | !strcasecmp(oldconst->choice2, "True")) | |
733 | { | |
734 | constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6); | |
735 | constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom"); | |
736 | constptr[1].installable = 0; | |
737 | } | |
738 | else | |
739 | { | |
740 | constptr[1].option = ppdFindOption(ppd, oldconst->option2); | |
741 | constptr[1].choice = ppdFindChoice(constptr[1].option, | |
742 | oldconst->choice2); | |
743 | constptr[1].installable = ppd_is_installable(installable, | |
744 | oldconst->option2); | |
745 | } | |
e78998df | 746 | |
28b9d139 MS |
747 | if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0])) |
748 | { | |
749 | DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n", | |
750 | oldconst->option2, oldconst->choice2)); | |
751 | free(consts->constraints); | |
752 | free(consts); | |
753 | continue; | |
754 | } | |
e78998df | 755 | |
28b9d139 | 756 | consts->installable = constptr[0].installable || constptr[1].installable; |
e78998df | 757 | |
28b9d139 MS |
758 | /* |
759 | * Add it to the constraints array... | |
760 | */ | |
e78998df | 761 | |
28b9d139 MS |
762 | cupsArrayAdd(ppd->cups_uiconstraints, consts); |
763 | } | |
e78998df | 764 | |
28b9d139 MS |
765 | /* |
766 | * Then load new-style constraints... | |
767 | */ | |
e78998df | 768 | |
28b9d139 MS |
769 | for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL); |
770 | constattr; | |
771 | constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL)) | |
772 | { | |
773 | if (!constattr->value) | |
774 | { | |
775 | DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!"); | |
776 | continue; | |
777 | } | |
e78998df | 778 | |
28b9d139 MS |
779 | for (i = 0, vptr = strchr(constattr->value, '*'); |
780 | vptr; | |
781 | i ++, vptr = strchr(vptr + 1, '*')); | |
e78998df | 782 | |
28b9d139 MS |
783 | if (i == 0) |
784 | { | |
785 | DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!"); | |
786 | continue; | |
e78998df | 787 | } |
e78998df | 788 | |
28b9d139 | 789 | if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL) |
e78998df | 790 | { |
28b9d139 MS |
791 | DEBUG_puts("ppd_load_constraints: Unable to allocate memory for " |
792 | "cupsUIConstraints!"); | |
793 | return; | |
794 | } | |
e78998df | 795 | |
28b9d139 MS |
796 | if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL) |
797 | { | |
798 | free(consts); | |
799 | DEBUG_puts("ppd_load_constraints: Unable to allocate memory for " | |
800 | "cupsUIConstraints!"); | |
801 | return; | |
802 | } | |
e78998df | 803 | |
28b9d139 MS |
804 | consts->num_constraints = i; |
805 | consts->constraints = constptr; | |
e78998df | 806 | |
28b9d139 | 807 | strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver)); |
e78998df | 808 | |
28b9d139 MS |
809 | for (i = 0, vptr = strchr(constattr->value, '*'); |
810 | vptr; | |
811 | i ++, vptr = strchr(vptr, '*'), constptr ++) | |
812 | { | |
e78998df | 813 | /* |
28b9d139 | 814 | * Extract "*Option Choice" or just "*Option"... |
e78998df MS |
815 | */ |
816 | ||
28b9d139 MS |
817 | for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++) |
818 | if (ptr < (option + sizeof(option) - 1)) | |
819 | *ptr++ = *vptr; | |
e78998df | 820 | |
28b9d139 | 821 | *ptr = '\0'; |
e78998df | 822 | |
28b9d139 MS |
823 | while (isspace(*vptr & 255)) |
824 | vptr ++; | |
e78998df | 825 | |
28b9d139 MS |
826 | if (*vptr == '*') |
827 | choice[0] = '\0'; | |
e78998df MS |
828 | else |
829 | { | |
28b9d139 MS |
830 | for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++) |
831 | if (ptr < (choice + sizeof(choice) - 1)) | |
832 | *ptr++ = *vptr; | |
833 | ||
834 | *ptr = '\0'; | |
e78998df MS |
835 | } |
836 | ||
28b9d139 | 837 | if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True")) |
e78998df | 838 | { |
28b9d139 MS |
839 | _cups_strcpy(option, option + 6); |
840 | strcpy(choice, "Custom"); | |
e78998df MS |
841 | } |
842 | ||
28b9d139 MS |
843 | constptr->option = ppdFindOption(ppd, option); |
844 | constptr->choice = ppdFindChoice(constptr->option, choice); | |
845 | constptr->installable = ppd_is_installable(installable, option); | |
846 | consts->installable |= constptr->installable; | |
e78998df | 847 | |
28b9d139 MS |
848 | if (!constptr->option || (!constptr->choice && choice[0])) |
849 | { | |
850 | DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n", | |
851 | option, choice)); | |
852 | break; | |
853 | } | |
854 | } | |
e78998df | 855 | |
28b9d139 | 856 | if (!vptr) |
e78998df | 857 | cupsArrayAdd(ppd->cups_uiconstraints, consts); |
28b9d139 MS |
858 | else |
859 | { | |
860 | free(consts->constraints); | |
861 | free(consts); | |
e78998df MS |
862 | } |
863 | } | |
864 | } | |
865 | ||
866 | ||
e78998df MS |
867 | /* |
868 | * 'ppd_test_constraints()' - See if any constraints are active. | |
869 | */ | |
870 | ||
871 | static cups_array_t * /* O - Array of active constraints */ | |
872 | ppd_test_constraints( | |
873 | ppd_file_t *ppd, /* I - PPD file */ | |
06d4e77b MS |
874 | const char *option, /* I - Current option */ |
875 | const char *choice, /* I - Current choice */ | |
e78998df MS |
876 | int num_options, /* I - Number of additional options */ |
877 | cups_option_t *options, /* I - Additional options */ | |
878 | int which) /* I - Which constraints to test */ | |
879 | { | |
880 | int i; /* Looping var */ | |
881 | _ppd_cups_uiconsts_t *consts; /* Current constraints */ | |
882 | _ppd_cups_uiconst_t *constptr; /* Current constraint */ | |
883 | ppd_choice_t key, /* Search key */ | |
884 | *marked; /* Marked choice */ | |
885 | cups_array_t *active = NULL; /* Active constraints */ | |
886 | const char *value; /* Current value */ | |
06d4e77b | 887 | |
e78998df | 888 | |
06d4e77b MS |
889 | DEBUG_printf(("ppd_test_constraints(ppd=%p, num_options=%d, options=%p, " |
890 | "which=%d)\n", ppd, num_options, options, which)); | |
e78998df MS |
891 | |
892 | if (!ppd->cups_uiconstraints) | |
893 | ppd_load_constraints(ppd); | |
894 | ||
06d4e77b MS |
895 | DEBUG_printf(("ppd_test_constraints: %d constraints!\n", |
896 | cupsArrayCount(ppd->cups_uiconstraints))); | |
897 | ||
e78998df MS |
898 | cupsArraySave(ppd->marked); |
899 | ||
900 | for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints); | |
901 | consts; | |
902 | consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints)) | |
903 | { | |
06d4e77b MS |
904 | DEBUG_printf(("ppd_test_constraints: installable=%d, resolver=\"%s\", " |
905 | "num_constraints=%d option1=\"%s\", choice1=\"%s\", " | |
906 | "option2=\"%s\", choice2=\"%s\", ...\n", | |
907 | consts->installable, consts->resolver, consts->num_constraints, | |
908 | consts->constraints[0].option->keyword, | |
909 | consts->constraints[0].choice ? | |
910 | consts->constraints[0].choice->choice : "", | |
911 | consts->constraints[1].option->keyword, | |
912 | consts->constraints[1].choice ? | |
913 | consts->constraints[1].choice->choice : "")); | |
914 | ||
e78998df MS |
915 | if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable) |
916 | continue; | |
917 | ||
06d4e77b MS |
918 | DEBUG_puts("ppd_test_constraints: Testing..."); |
919 | ||
d1c13e16 | 920 | for (i = consts->num_constraints, constptr = consts->constraints; |
e78998df MS |
921 | i > 0; |
922 | i --, constptr ++) | |
923 | { | |
06d4e77b MS |
924 | DEBUG_printf(("ppd_test_constraints: %s=%s?\n", constptr->option->keyword, |
925 | constptr->choice ? constptr->choice->choice : "")); | |
926 | ||
e78998df MS |
927 | if (constptr->choice && |
928 | (!strcasecmp(constptr->option->keyword, "PageSize") || | |
929 | !strcasecmp(constptr->option->keyword, "PageRegion"))) | |
930 | { | |
931 | /* | |
932 | * PageSize and PageRegion are used depending on the selected input slot | |
933 | * and manual feed mode. Validate against the selected page size instead | |
934 | * of an individual option... | |
935 | */ | |
936 | ||
06d4e77b MS |
937 | if (option && choice && |
938 | (!strcasecmp(option, "PageSize") || | |
939 | !strcasecmp(option, "PageRegion"))) | |
940 | { | |
941 | value = choice; | |
06d4e77b MS |
942 | } |
943 | else if ((value = cupsGetOption("PageSize", num_options, | |
944 | options)) == NULL) | |
e78998df MS |
945 | if ((value = cupsGetOption("PageRegion", num_options, |
946 | options)) == NULL) | |
947 | if ((value = cupsGetOption("media", num_options, options)) == NULL) | |
948 | { | |
949 | ppd_size_t *size = ppdPageSize(ppd, NULL); | |
950 | ||
951 | if (size) | |
952 | value = size->name; | |
953 | } | |
954 | ||
d1c13e16 MS |
955 | if (value && !strncasecmp(value, "Custom.", 7)) |
956 | value = "Custom"; | |
957 | ||
e78998df | 958 | if (!value || strcasecmp(value, constptr->choice->choice)) |
06d4e77b MS |
959 | { |
960 | DEBUG_puts("ppd_test_constraints: NO"); | |
e78998df | 961 | break; |
06d4e77b | 962 | } |
e78998df MS |
963 | } |
964 | else if (constptr->choice) | |
965 | { | |
06d4e77b MS |
966 | if (option && choice && !strcasecmp(option, constptr->option->keyword)) |
967 | { | |
d1c13e16 MS |
968 | if (!strncasecmp(choice, "Custom.", 7)) |
969 | value = "Custom"; | |
970 | else | |
971 | value = choice; | |
06d4e77b | 972 | |
d1c13e16 MS |
973 | if (strcasecmp(value, constptr->choice->choice)) |
974 | { | |
975 | DEBUG_puts("ppd_test_constraints: NO"); | |
976 | break; | |
977 | } | |
06d4e77b MS |
978 | } |
979 | else if ((value = cupsGetOption(constptr->option->keyword, num_options, | |
980 | options)) != NULL) | |
e78998df | 981 | { |
d1c13e16 MS |
982 | if (!strncasecmp(value, "Custom.", 7)) |
983 | value = "Custom"; | |
984 | ||
e78998df | 985 | if (strcasecmp(value, constptr->choice->choice)) |
06d4e77b MS |
986 | { |
987 | DEBUG_puts("ppd_test_constraints: NO"); | |
e78998df | 988 | break; |
06d4e77b | 989 | } |
e78998df MS |
990 | } |
991 | else if (!constptr->choice->marked) | |
06d4e77b MS |
992 | { |
993 | DEBUG_puts("ppd_test_constraints: NO"); | |
994 | break; | |
995 | } | |
996 | } | |
997 | else if (option && choice && | |
998 | !strcasecmp(option, constptr->option->keyword)) | |
999 | { | |
1000 | if (!strcasecmp(choice, "None") || !strcasecmp(choice, "Off") || | |
1001 | !strcasecmp(choice, "False")) | |
d1c13e16 MS |
1002 | { |
1003 | DEBUG_puts("ppd_test_constraints: NO"); | |
e78998df | 1004 | break; |
d1c13e16 | 1005 | } |
e78998df MS |
1006 | } |
1007 | else if ((value = cupsGetOption(constptr->option->keyword, num_options, | |
1008 | options)) != NULL) | |
1009 | { | |
1010 | if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") || | |
1011 | !strcasecmp(value, "False")) | |
06d4e77b MS |
1012 | { |
1013 | DEBUG_puts("ppd_test_constraints: NO"); | |
1014 | break; | |
1015 | } | |
e78998df MS |
1016 | } |
1017 | else | |
1018 | { | |
1019 | key.option = constptr->option; | |
1020 | ||
1021 | if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) | |
1022 | != NULL && | |
1023 | (!strcasecmp(marked->choice, "None") || | |
1024 | !strcasecmp(marked->choice, "Off") || | |
1025 | !strcasecmp(marked->choice, "False"))) | |
06d4e77b MS |
1026 | { |
1027 | DEBUG_puts("ppd_test_constraints: NO"); | |
e78998df | 1028 | break; |
06d4e77b | 1029 | } |
e78998df MS |
1030 | } |
1031 | } | |
1032 | ||
d1c13e16 | 1033 | if (i <= 0) |
e78998df MS |
1034 | { |
1035 | if (!active) | |
1036 | active = cupsArrayNew(NULL, NULL); | |
1037 | ||
1038 | cupsArrayAdd(active, consts); | |
06d4e77b | 1039 | DEBUG_puts("ppd_test_constraints: Added..."); |
e78998df MS |
1040 | } |
1041 | } | |
1042 | ||
1043 | cupsArrayRestore(ppd->marked); | |
1044 | ||
06d4e77b MS |
1045 | DEBUG_printf(("ppd_test_constraints: Found %d active constraints!\n", |
1046 | cupsArrayCount(active))); | |
1047 | ||
e78998df MS |
1048 | return (active); |
1049 | } | |
1050 | ||
1051 | ||
1052 | /* | |
1053 | * End of "$Id$". | |
1054 | */ |