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