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