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