Coverage Report

Created: 2025-07-11 06:22

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