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