Coverage Report

Created: 2025-12-31 07:31

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