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