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