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