Coverage Report

Created: 2023-06-07 06:24

/src/cups/cups/ppd-mark.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Option marking routines for CUPS.
3
 *
4
 * Copyright © 2007-2019 by Apple Inc.
5
 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
6
 *
7
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8
 * information.
9
 *
10
 * PostScript is a trademark of Adobe Systems, Inc.
11
 */
12
13
/*
14
 * Include necessary headers...
15
 */
16
17
#include "cups-private.h"
18
#include "ppd-private.h"
19
#include "debug-internal.h"
20
21
22
/*
23
 * Local functions...
24
 */
25
26
#ifdef DEBUG
27
static void ppd_debug_marked(ppd_file_t *ppd, const char *title);
28
#else
29
#  define ppd_debug_marked(ppd,title)
30
#endif /* DEBUG */
31
static void ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
32
static void ppd_mark_choices(ppd_file_t *ppd, const char *s);
33
static void ppd_mark_option(ppd_file_t *ppd, const char *option,
34
                    const char *choice);
35
36
37
/*
38
 * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
39
 *
40
 * This function maps the IPP "finishings", "media", "mirror",
41
 * "multiple-document-handling", "output-bin", "print-color-mode",
42
 * "print-quality", "printer-resolution", and "sides" attributes to their
43
 * corresponding PPD options and choices.
44
 */
45
46
int         /* O - 1 if conflicts exist, 0 otherwise */
47
cupsMarkOptions(
48
    ppd_file_t    *ppd,     /* I - PPD file */
49
    int           num_options,    /* I - Number of options */
50
    cups_option_t *options)   /* I - Options */
51
0
{
52
0
  int   i, j;     /* Looping vars */
53
0
  char    *ptr,     /* Pointer into string */
54
0
    s[255];     /* Temporary string */
55
0
  const char  *val,     /* Pointer into value */
56
0
    *media,     /* media option */
57
0
    *output_bin,    /* output-bin option */
58
0
    *page_size,   /* PageSize option */
59
0
    *ppd_keyword,   /* PPD keyword */
60
0
    *print_color_mode,  /* print-color-mode option */
61
0
    *print_quality,   /* print-quality option */
62
0
    *sides;     /* sides option */
63
0
  cups_option_t *optptr;    /* Current option */
64
0
  ppd_attr_t  *attr;      /* PPD attribute */
65
0
  _ppd_cache_t  *cache;     /* PPD cache and mapping data */
66
67
68
 /*
69
  * Check arguments...
70
  */
71
72
0
  if (!ppd || num_options <= 0 || !options)
73
0
    return (0);
74
75
0
  ppd_debug_marked(ppd, "Before...");
76
77
 /*
78
  * Do special handling for finishings, media, output-bin, output-mode,
79
  * print-color-mode, print-quality, and PageSize...
80
  */
81
82
0
  media         = cupsGetOption("media", num_options, options);
83
0
  output_bin    = cupsGetOption("output-bin", num_options, options);
84
0
  page_size     = cupsGetOption("PageSize", num_options, options);
85
0
  print_quality = cupsGetOption("print-quality", num_options, options);
86
0
  sides         = cupsGetOption("sides", num_options, options);
87
88
0
  if ((print_color_mode = cupsGetOption("print-color-mode", num_options,
89
0
                                        options)) == NULL)
90
0
    print_color_mode = cupsGetOption("output-mode", num_options, options);
91
92
0
  if ((media || output_bin || print_color_mode || print_quality || sides) &&
93
0
      !ppd->cache)
94
0
  {
95
   /*
96
    * Load PPD cache and mapping data as needed...
97
    */
98
99
0
    ppd->cache = _ppdCacheCreateWithPPD(ppd);
100
0
  }
101
102
0
  cache = ppd->cache;
103
104
0
  if (media)
105
0
  {
106
   /*
107
    * Loop through the option string, separating it at commas and marking each
108
    * individual option as long as the corresponding PPD option (PageSize,
109
    * InputSlot, etc.) is not also set.
110
    *
111
    * For PageSize, we also check for an empty option value since some versions
112
    * of macOS use it to specify auto-selection of the media based solely on
113
    * the size.
114
    */
115
116
0
    for (val = media; *val;)
117
0
    {
118
     /*
119
      * Extract the sub-option from the string...
120
      */
121
122
0
      for (ptr = s; *val && *val != ',' && (size_t)(ptr - s) < (sizeof(s) - 1);)
123
0
  *ptr++ = *val++;
124
0
      *ptr++ = '\0';
125
126
0
      if (*val == ',')
127
0
  val ++;
128
129
     /*
130
      * Mark it...
131
      */
132
133
0
      if (!page_size || !page_size[0])
134
0
      {
135
0
        if (!_cups_strncasecmp(s, "Custom.", 7) || ppdPageSize(ppd, s))
136
0
          ppd_mark_option(ppd, "PageSize", s);
137
0
        else if ((ppd_keyword = _ppdCacheGetPageSize(cache, NULL, s, NULL)) != NULL)
138
0
    ppd_mark_option(ppd, "PageSize", ppd_keyword);
139
0
      }
140
141
0
      if (cache && cache->source_option &&
142
0
          !cupsGetOption(cache->source_option, num_options, options) &&
143
0
    (ppd_keyword = _ppdCacheGetInputSlot(cache, NULL, s)) != NULL)
144
0
  ppd_mark_option(ppd, cache->source_option, ppd_keyword);
145
146
0
      if (!cupsGetOption("MediaType", num_options, options) &&
147
0
    (ppd_keyword = _ppdCacheGetMediaType(cache, NULL, s)) != NULL)
148
0
  ppd_mark_option(ppd, "MediaType", ppd_keyword);
149
0
    }
150
0
  }
151
152
0
  if (cache)
153
0
  {
154
0
    if (!cupsGetOption("com.apple.print.DocumentTicket.PMSpoolFormat",
155
0
                       num_options, options) &&
156
0
        !cupsGetOption("APPrinterPreset", num_options, options) &&
157
0
        (print_color_mode || print_quality))
158
0
    {
159
     /*
160
      * Map output-mode and print-quality to a preset...
161
      */
162
163
0
      _pwg_print_color_mode_t pwg_pcm;/* print-color-mode index */
164
0
      _pwg_print_quality_t  pwg_pq; /* print-quality index */
165
0
      cups_option_t   *preset;/* Current preset option */
166
167
0
      if (print_color_mode && !strcmp(print_color_mode, "monochrome"))
168
0
  pwg_pcm = _PWG_PRINT_COLOR_MODE_MONOCHROME;
169
0
      else
170
0
  pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
171
172
0
      if (print_quality)
173
0
      {
174
0
  pwg_pq = (_pwg_print_quality_t)(atoi(print_quality) - IPP_QUALITY_DRAFT);
175
0
  if (pwg_pq < _PWG_PRINT_QUALITY_DRAFT)
176
0
    pwg_pq = _PWG_PRINT_QUALITY_DRAFT;
177
0
  else if (pwg_pq > _PWG_PRINT_QUALITY_HIGH)
178
0
    pwg_pq = _PWG_PRINT_QUALITY_HIGH;
179
0
      }
180
0
      else
181
0
  pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
182
183
0
      if (cache->num_presets[pwg_pcm][pwg_pq] == 0)
184
0
      {
185
       /*
186
  * Try to find a preset that works so that we maximize the chances of us
187
  * getting a good print using IPP attributes.
188
  */
189
190
0
  if (cache->num_presets[pwg_pcm][_PWG_PRINT_QUALITY_NORMAL] > 0)
191
0
    pwg_pq = _PWG_PRINT_QUALITY_NORMAL;
192
0
  else if (cache->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][pwg_pq] > 0)
193
0
    pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
194
0
  else
195
0
  {
196
0
    pwg_pq  = _PWG_PRINT_QUALITY_NORMAL;
197
0
    pwg_pcm = _PWG_PRINT_COLOR_MODE_COLOR;
198
0
  }
199
0
      }
200
201
0
      if (cache->num_presets[pwg_pcm][pwg_pq] > 0)
202
0
      {
203
       /*
204
  * Copy the preset options as long as the corresponding names are not
205
  * already defined in the IPP request...
206
  */
207
208
0
  for (i = cache->num_presets[pwg_pcm][pwg_pq],
209
0
     preset = cache->presets[pwg_pcm][pwg_pq];
210
0
       i > 0;
211
0
       i --, preset ++)
212
0
  {
213
0
    if (!cupsGetOption(preset->name, num_options, options))
214
0
      ppd_mark_option(ppd, preset->name, preset->value);
215
0
  }
216
0
      }
217
0
    }
218
219
0
    if (output_bin && !cupsGetOption("OutputBin", num_options, options) &&
220
0
  (ppd_keyword = _ppdCacheGetOutputBin(cache, output_bin)) != NULL)
221
0
    {
222
     /*
223
      * Map output-bin to OutputBin...
224
      */
225
226
0
      ppd_mark_option(ppd, "OutputBin", ppd_keyword);
227
0
    }
228
229
0
    if (sides && cache->sides_option &&
230
0
        !cupsGetOption(cache->sides_option, num_options, options))
231
0
    {
232
     /*
233
      * Map sides to duplex option...
234
      */
235
236
0
      if (!strcmp(sides, "one-sided") && cache->sides_1sided)
237
0
        ppd_mark_option(ppd, cache->sides_option, cache->sides_1sided);
238
0
      else if (!strcmp(sides, "two-sided-long-edge") &&
239
0
               cache->sides_2sided_long)
240
0
        ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_long);
241
0
      else if (!strcmp(sides, "two-sided-short-edge") &&
242
0
               cache->sides_2sided_short)
243
0
        ppd_mark_option(ppd, cache->sides_option, cache->sides_2sided_short);
244
0
    }
245
0
  }
246
247
 /*
248
  * Mark other options...
249
  */
250
251
0
  for (i = num_options, optptr = options; i > 0; i --, optptr ++)
252
0
  {
253
0
    if (!_cups_strcasecmp(optptr->name, "media") ||
254
0
        !_cups_strcasecmp(optptr->name, "output-bin") ||
255
0
  !_cups_strcasecmp(optptr->name, "output-mode") ||
256
0
  !_cups_strcasecmp(optptr->name, "print-quality") ||
257
0
  !_cups_strcasecmp(optptr->name, "sides"))
258
0
      continue;
259
0
    else if (!_cups_strcasecmp(optptr->name, "resolution") ||
260
0
             !_cups_strcasecmp(optptr->name, "printer-resolution"))
261
0
    {
262
0
      ppd_mark_option(ppd, "Resolution", optptr->value);
263
0
      ppd_mark_option(ppd, "SetResolution", optptr->value);
264
        /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
265
0
      ppd_mark_option(ppd, "JCLResolution", optptr->value);
266
        /* HP */
267
0
      ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
268
        /* Canon */
269
0
    }
270
0
    else if (!_cups_strcasecmp(optptr->name, "multiple-document-handling"))
271
0
    {
272
0
      if (!cupsGetOption("Collate", num_options, options) &&
273
0
          ppdFindOption(ppd, "Collate"))
274
0
      {
275
0
        if (_cups_strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
276
0
    ppd_mark_option(ppd, "Collate", "True");
277
0
  else
278
0
    ppd_mark_option(ppd, "Collate", "False");
279
0
      }
280
0
    }
281
0
    else if (!_cups_strcasecmp(optptr->name, "finishings"))
282
0
    {
283
     /*
284
      * Lookup cupsIPPFinishings attributes for each value...
285
      */
286
287
0
      for (ptr = optptr->value; *ptr;)
288
0
      {
289
       /*
290
        * Get the next finishings number...
291
  */
292
293
0
        if (!isdigit(*ptr & 255))
294
0
    break;
295
296
0
        if ((j = (int)strtol(ptr, &ptr, 10)) < 3)
297
0
    break;
298
299
       /*
300
        * Skip separator as needed...
301
  */
302
303
0
        if (*ptr == ',')
304
0
    ptr ++;
305
306
       /*
307
        * Look it up in the PPD file...
308
  */
309
310
0
  snprintf(s, sizeof(s), "%d", j);
311
312
0
        if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
313
0
    continue;
314
315
       /*
316
        * Apply "*Option Choice" settings from the attribute value...
317
  */
318
319
0
        ppd_mark_choices(ppd, attr->value);
320
0
      }
321
0
    }
322
0
    else if (!_cups_strcasecmp(optptr->name, "APPrinterPreset"))
323
0
    {
324
     /*
325
      * Lookup APPrinterPreset value...
326
      */
327
328
0
      if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
329
0
      {
330
       /*
331
        * Apply "*Option Choice" settings from the attribute value...
332
  */
333
334
0
        ppd_mark_choices(ppd, attr->value);
335
0
      }
336
0
    }
337
0
    else if (!_cups_strcasecmp(optptr->name, "mirror"))
338
0
      ppd_mark_option(ppd, "MirrorPrint", optptr->value);
339
0
    else
340
0
      ppd_mark_option(ppd, optptr->name, optptr->value);
341
0
  }
342
343
0
  if (print_quality)
344
0
  {
345
0
    int pq = atoi(print_quality);       /* print-quaity value */
346
347
0
    if (pq == IPP_QUALITY_DRAFT)
348
0
      ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
349
0
    else if (pq == IPP_QUALITY_HIGH)
350
0
      ppd_mark_option(ppd, "cupsPrintQuality", "High");
351
0
    else
352
0
      ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
353
0
  }
354
355
0
  ppd_debug_marked(ppd, "After...");
356
357
0
  return (ppdConflicts(ppd) > 0);
358
0
}
359
360
361
/*
362
 * 'ppdFindChoice()' - Return a pointer to an option choice.
363
 */
364
365
ppd_choice_t *        /* O - Choice pointer or @code NULL@ */
366
ppdFindChoice(ppd_option_t *o,    /* I - Pointer to option */
367
              const char   *choice) /* I - Name of choice */
368
0
{
369
0
  int   i;      /* Looping var */
370
0
  ppd_choice_t  *c;     /* Current choice */
371
372
373
0
  if (!o || !choice)
374
0
    return (NULL);
375
376
0
  if (choice[0] == '{' || !_cups_strncasecmp(choice, "Custom.", 7))
377
0
    choice = "Custom";
378
379
0
  for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
380
0
    if (!_cups_strcasecmp(c->choice, choice))
381
0
      return (c);
382
383
0
  return (NULL);
384
0
}
385
386
387
/*
388
 * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
389
 */
390
391
ppd_choice_t *        /* O - Pointer to choice or @code NULL@ */
392
ppdFindMarkedChoice(ppd_file_t *ppd,  /* I - PPD file */
393
                    const char *option) /* I - Keyword/option name */
394
0
{
395
0
  ppd_choice_t  key,      /* Search key for choice */
396
0
    *marked;    /* Marked choice */
397
398
399
0
  DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
400
401
0
  if ((key.option = ppdFindOption(ppd, option)) == NULL)
402
0
  {
403
0
    DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
404
0
    return (NULL);
405
0
  }
406
407
0
  marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
408
409
0
  DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
410
0
                marked ? marked->choice : "NULL"));
411
412
0
  return (marked);
413
0
}
414
415
416
/*
417
 * 'ppdFindOption()' - Return a pointer to the specified option.
418
 */
419
420
ppd_option_t *        /* O - Pointer to option or @code NULL@ */
421
ppdFindOption(ppd_file_t *ppd,    /* I - PPD file data */
422
              const char *option) /* I - Option/Keyword name */
423
0
{
424
 /*
425
  * Range check input...
426
  */
427
428
0
  if (!ppd || !option)
429
0
    return (NULL);
430
431
0
  if (ppd->options)
432
0
  {
433
   /*
434
    * Search in the array...
435
    */
436
437
0
    ppd_option_t  key;    /* Option search key */
438
439
440
0
    strlcpy(key.keyword, option, sizeof(key.keyword));
441
442
0
    return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
443
0
  }
444
0
  else
445
0
  {
446
   /*
447
    * Search in each group...
448
    */
449
450
0
    int     i, j;   /* Looping vars */
451
0
    ppd_group_t   *group;   /* Current group */
452
0
    ppd_option_t  *optptr;  /* Current option */
453
454
455
0
    for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
456
0
      for (j = group->num_options, optptr = group->options;
457
0
           j > 0;
458
0
     j --, optptr ++)
459
0
        if (!_cups_strcasecmp(optptr->keyword, option))
460
0
    return (optptr);
461
462
0
    return (NULL);
463
0
  }
464
0
}
465
466
467
/*
468
 * 'ppdIsMarked()' - Check to see if an option is marked.
469
 */
470
471
int         /* O - Non-zero if option is marked */
472
ppdIsMarked(ppd_file_t *ppd,    /* I - PPD file data */
473
            const char *option,   /* I - Option/Keyword name */
474
            const char *choice)   /* I - Choice name */
475
0
{
476
0
  ppd_choice_t  key,      /* Search key */
477
0
    *c;     /* Choice pointer */
478
479
480
0
  if (!ppd)
481
0
    return (0);
482
483
0
  if ((key.option = ppdFindOption(ppd, option)) == NULL)
484
0
    return (0);
485
486
0
  if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
487
0
    return (0);
488
489
0
  return (!strcmp(c->choice, choice));
490
0
}
491
492
493
/*
494
 * 'ppdMarkDefaults()' - Mark all default options in the PPD file.
495
 */
496
497
void
498
ppdMarkDefaults(ppd_file_t *ppd)  /* I - PPD file record */
499
0
{
500
0
  int   i;      /* Looping variables */
501
0
  ppd_group_t *g;     /* Current group */
502
0
  ppd_choice_t  *c;     /* Current choice */
503
504
505
0
  if (!ppd)
506
0
    return;
507
508
 /*
509
  * Clean out the marked array...
510
  */
511
512
0
  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
513
0
       c;
514
0
       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
515
0
  {
516
0
    cupsArrayRemove(ppd->marked, c);
517
0
    c->marked = 0;
518
0
  }
519
520
 /*
521
  * Then repopulate it with the defaults...
522
  */
523
524
0
  for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
525
0
    ppd_defaults(ppd, g);
526
527
 /*
528
  * Finally, tag any conflicts (API compatibility) once at the end.
529
  */
530
531
0
  ppdConflicts(ppd);
532
0
}
533
534
535
/*
536
 * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
537
 *                     conflicts.
538
 */
539
540
int         /* O - Number of conflicts */
541
ppdMarkOption(ppd_file_t *ppd,    /* I - PPD file record */
542
              const char *option, /* I - Keyword */
543
              const char *choice) /* I - Option name */
544
0
{
545
0
  DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
546
0
          ppd, option, choice));
547
548
 /*
549
  * Range check input...
550
  */
551
552
0
  if (!ppd || !option || !choice)
553
0
    return (0);
554
555
 /*
556
  * Mark the option...
557
  */
558
559
0
  ppd_mark_option(ppd, option, choice);
560
561
 /*
562
  * Return the number of conflicts...
563
  */
564
565
0
  return (ppdConflicts(ppd));
566
0
}
567
568
569
/*
570
 * 'ppdFirstOption()' - Return the first option in the PPD file.
571
 *
572
 * Options are returned from all groups in ascending alphanumeric order.
573
 *
574
 * @since CUPS 1.2/macOS 10.5@
575
 */
576
577
ppd_option_t *        /* O - First option or @code NULL@ */
578
ppdFirstOption(ppd_file_t *ppd)   /* I - PPD file */
579
0
{
580
0
  if (!ppd)
581
0
    return (NULL);
582
0
  else
583
0
    return ((ppd_option_t *)cupsArrayFirst(ppd->options));
584
0
}
585
586
587
/*
588
 * 'ppdNextOption()' - Return the next option in the PPD file.
589
 *
590
 * Options are returned from all groups in ascending alphanumeric order.
591
 *
592
 * @since CUPS 1.2/macOS 10.5@
593
 */
594
595
ppd_option_t *        /* O - Next option or @code NULL@ */
596
ppdNextOption(ppd_file_t *ppd)    /* I - PPD file */
597
0
{
598
0
  if (!ppd)
599
0
    return (NULL);
600
0
  else
601
0
    return ((ppd_option_t *)cupsArrayNext(ppd->options));
602
0
}
603
604
605
/*
606
 * '_ppdParseOptions()' - Parse options from a PPD file.
607
 *
608
 * This function looks for strings of the form:
609
 *
610
 *     *option choice ... *optionN choiceN
611
 *     property value ... propertyN valueN
612
 *
613
 * It stops when it finds a string that doesn't match this format.
614
 */
615
616
int         /* O  - Number of options */
617
_ppdParseOptions(
618
    const char    *s,     /* I  - String to parse */
619
    int           num_options,    /* I  - Number of options */
620
    cups_option_t **options,    /* IO - Options */
621
    _ppd_parse_t  which)    /* I  - What to parse */
622
0
{
623
0
  char  option[PPD_MAX_NAME * 2 + 1], /* Current option/property */
624
0
  choice[PPD_MAX_NAME],   /* Current choice/value */
625
0
  *ptr;       /* Pointer into option or choice */
626
627
628
0
  if (!s)
629
0
    return (num_options);
630
631
 /*
632
  * Read all of the "*Option Choice" and "property value" pairs from the
633
  * string, add them to an options array as we go...
634
  */
635
636
0
  while (*s)
637
0
  {
638
   /*
639
    * Skip leading whitespace...
640
    */
641
642
0
    while (_cups_isspace(*s))
643
0
      s ++;
644
645
   /*
646
    * Get the option/property name...
647
    */
648
649
0
    ptr = option;
650
0
    while (*s && !_cups_isspace(*s) && ptr < (option + sizeof(option) - 1))
651
0
      *ptr++ = *s++;
652
653
0
    if (ptr == s || !_cups_isspace(*s))
654
0
      break;
655
656
0
    *ptr = '\0';
657
658
   /*
659
    * Get the choice...
660
    */
661
662
0
    while (_cups_isspace(*s))
663
0
      s ++;
664
665
0
    if (!*s)
666
0
      break;
667
668
0
    ptr = choice;
669
0
    while (*s && !_cups_isspace(*s) && ptr < (choice + sizeof(choice) - 1))
670
0
      *ptr++ = *s++;
671
672
0
    if (*s && !_cups_isspace(*s))
673
0
      break;
674
675
0
    *ptr = '\0';
676
677
   /*
678
    * Add it to the options array...
679
    */
680
681
0
    if (option[0] == '*' && which != _PPD_PARSE_PROPERTIES)
682
0
      num_options = cupsAddOption(option + 1, choice, num_options, options);
683
0
    else if (option[0] != '*' && which != _PPD_PARSE_OPTIONS)
684
0
      num_options = cupsAddOption(option, choice, num_options, options);
685
0
  }
686
687
0
  return (num_options);
688
0
}
689
690
691
#ifdef DEBUG
692
/*
693
 * 'ppd_debug_marked()' - Output the marked array to stdout...
694
 */
695
696
static void
697
ppd_debug_marked(ppd_file_t *ppd,   /* I - PPD file data */
698
             const char *title)   /* I - Title for list */
699
{
700
  ppd_choice_t  *c;     /* Current choice */
701
702
703
  DEBUG_printf(("2cupsMarkOptions: %s", title));
704
705
  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
706
       c;
707
       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
708
    DEBUG_printf(("2cupsMarkOptions: %s=%s", c->option->keyword, c->choice));
709
}
710
#endif /* DEBUG */
711
712
713
/*
714
 * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
715
 */
716
717
static void
718
ppd_defaults(ppd_file_t  *ppd,    /* I - PPD file */
719
             ppd_group_t *g)    /* I - Group to default */
720
0
{
721
0
  int   i;      /* Looping var */
722
0
  ppd_option_t  *o;     /* Current option */
723
0
  ppd_group_t *sg;      /* Current sub-group */
724
725
726
0
  for (i = g->num_options, o = g->options; i > 0; i --, o ++)
727
0
    if (_cups_strcasecmp(o->keyword, "PageRegion") != 0)
728
0
      ppd_mark_option(ppd, o->keyword, o->defchoice);
729
730
0
  for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
731
0
    ppd_defaults(ppd, sg);
732
0
}
733
734
735
/*
736
 * 'ppd_mark_choices()' - Mark one or more option choices from a string.
737
 */
738
739
static void
740
ppd_mark_choices(ppd_file_t *ppd, /* I - PPD file */
741
                 const char *s)   /* I - "*Option Choice ..." string */
742
0
{
743
0
  int   i,      /* Looping var */
744
0
    num_options;    /* Number of options */
745
0
  cups_option_t *options,   /* Options */
746
0
    *option;    /* Current option */
747
748
749
0
  if (!s)
750
0
    return;
751
752
0
  options     = NULL;
753
0
  num_options = _ppdParseOptions(s, 0, &options, 0);
754
755
0
  for (i = num_options, option = options; i > 0; i --, option ++)
756
0
    ppd_mark_option(ppd, option->name, option->value);
757
758
0
  cupsFreeOptions(num_options, options);
759
0
}
760
761
762
/*
763
 * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
764
 */
765
766
static void
767
ppd_mark_option(ppd_file_t *ppd,  /* I - PPD file */
768
                const char *option, /* I - Option name */
769
                const char *choice) /* I - Choice name */
770
0
{
771
0
  int   i, j;     /* Looping vars */
772
0
  ppd_option_t  *o;     /* Option pointer */
773
0
  ppd_choice_t  *c,     /* Choice pointer */
774
0
    *oldc,      /* Old choice pointer */
775
0
    key;      /* Search key for choice */
776
0
  struct lconv  *loc;     /* Locale data */
777
778
779
0
  DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
780
0
          ppd, option, choice));
781
782
 /*
783
  * AP_D_InputSlot is the "default input slot" on macOS, and setting
784
  * it clears the regular InputSlot choices...
785
  */
786
787
0
  if (!_cups_strcasecmp(option, "AP_D_InputSlot"))
788
0
  {
789
0
    cupsArraySave(ppd->options);
790
791
0
    if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
792
0
    {
793
0
      key.option = o;
794
0
      if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
795
0
      {
796
0
        oldc->marked = 0;
797
0
        cupsArrayRemove(ppd->marked, oldc);
798
0
      }
799
0
    }
800
801
0
    cupsArrayRestore(ppd->options);
802
0
  }
803
804
 /*
805
  * Check for custom options...
806
  */
807
808
0
  cupsArraySave(ppd->options);
809
810
0
  o = ppdFindOption(ppd, option);
811
812
0
  cupsArrayRestore(ppd->options);
813
814
0
  if (!o)
815
0
    return;
816
817
0
  loc = localeconv();
818
819
0
  if (!_cups_strncasecmp(choice, "Custom.", 7))
820
0
  {
821
   /*
822
    * Handle a custom option...
823
    */
824
825
0
    if ((c = ppdFindChoice(o, "Custom")) == NULL)
826
0
      return;
827
828
0
    if (!_cups_strcasecmp(option, "PageSize"))
829
0
    {
830
     /*
831
      * Handle custom page sizes...
832
      */
833
834
0
      ppdPageSize(ppd, choice);
835
0
    }
836
0
    else
837
0
    {
838
     /*
839
      * Handle other custom options...
840
      */
841
842
0
      ppd_coption_t *coption; /* Custom option */
843
0
      ppd_cparam_t  *cparam;  /* Custom parameter */
844
0
      char    *units;   /* Custom points units */
845
846
847
0
      if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
848
0
      {
849
0
        if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
850
0
    return;
851
852
0
        switch (cparam->type)
853
0
  {
854
0
    case PPD_CUSTOM_UNKNOWN :
855
0
        break;
856
857
0
    case PPD_CUSTOM_CURVE :
858
0
    case PPD_CUSTOM_INVCURVE :
859
0
    case PPD_CUSTOM_REAL :
860
0
        cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
861
0
                                                           NULL, loc);
862
0
        break;
863
864
0
    case PPD_CUSTOM_POINTS :
865
0
        cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
866
0
                                                             &units,
867
0
                                                             loc);
868
869
0
              if (units)
870
0
        {
871
0
          if (!_cups_strcasecmp(units, "cm"))
872
0
            cparam->current.custom_points *= 72.0f / 2.54f;
873
0
          else if (!_cups_strcasecmp(units, "mm"))
874
0
            cparam->current.custom_points *= 72.0f / 25.4f;
875
0
          else if (!_cups_strcasecmp(units, "m"))
876
0
            cparam->current.custom_points *= 72.0f / 0.0254f;
877
0
          else if (!_cups_strcasecmp(units, "in"))
878
0
            cparam->current.custom_points *= 72.0f;
879
0
          else if (!_cups_strcasecmp(units, "ft"))
880
0
            cparam->current.custom_points *= 12.0f * 72.0f;
881
0
              }
882
0
        break;
883
884
0
    case PPD_CUSTOM_INT :
885
0
        cparam->current.custom_int = atoi(choice + 7);
886
0
        break;
887
888
0
    case PPD_CUSTOM_PASSCODE :
889
0
    case PPD_CUSTOM_PASSWORD :
890
0
    case PPD_CUSTOM_STRING :
891
0
        if (cparam->current.custom_string)
892
0
          free(cparam->current.custom_string);
893
894
0
        cparam->current.custom_string = strdup(choice + 7);
895
0
        break;
896
0
  }
897
0
      }
898
0
    }
899
900
   /*
901
    * Make sure that we keep the option marked below...
902
    */
903
904
0
    choice = "Custom";
905
0
  }
906
0
  else if (choice[0] == '{')
907
0
  {
908
   /*
909
    * Handle multi-value custom options...
910
    */
911
912
0
    ppd_coption_t *coption; /* Custom option */
913
0
    ppd_cparam_t  *cparam;  /* Custom parameter */
914
0
    char    *units;   /* Custom points units */
915
0
    int     num_vals; /* Number of values */
916
0
    cups_option_t *vals,    /* Values */
917
0
      *val;   /* Value */
918
919
920
0
    if ((c = ppdFindChoice(o, "Custom")) == NULL)
921
0
      return;
922
923
0
    if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
924
0
    {
925
0
      num_vals = cupsParseOptions(choice, 0, &vals);
926
927
0
      for (i = 0, val = vals; i < num_vals; i ++, val ++)
928
0
      {
929
0
        if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
930
0
    continue;
931
932
0
  switch (cparam->type)
933
0
  {
934
0
    case PPD_CUSTOM_UNKNOWN :
935
0
        break;
936
937
0
    case PPD_CUSTOM_CURVE :
938
0
    case PPD_CUSTOM_INVCURVE :
939
0
    case PPD_CUSTOM_REAL :
940
0
        cparam->current.custom_real = (float)_cupsStrScand(val->value,
941
0
                                                           NULL, loc);
942
0
        break;
943
944
0
    case PPD_CUSTOM_POINTS :
945
0
        cparam->current.custom_points = (float)_cupsStrScand(val->value,
946
0
                                                             &units,
947
0
                                                             loc);
948
949
0
        if (units)
950
0
        {
951
0
          if (!_cups_strcasecmp(units, "cm"))
952
0
      cparam->current.custom_points *= 72.0f / 2.54f;
953
0
          else if (!_cups_strcasecmp(units, "mm"))
954
0
      cparam->current.custom_points *= 72.0f / 25.4f;
955
0
          else if (!_cups_strcasecmp(units, "m"))
956
0
      cparam->current.custom_points *= 72.0f / 0.0254f;
957
0
          else if (!_cups_strcasecmp(units, "in"))
958
0
      cparam->current.custom_points *= 72.0f;
959
0
          else if (!_cups_strcasecmp(units, "ft"))
960
0
      cparam->current.custom_points *= 12.0f * 72.0f;
961
0
        }
962
0
        break;
963
964
0
    case PPD_CUSTOM_INT :
965
0
        cparam->current.custom_int = atoi(val->value);
966
0
        break;
967
968
0
    case PPD_CUSTOM_PASSCODE :
969
0
    case PPD_CUSTOM_PASSWORD :
970
0
    case PPD_CUSTOM_STRING :
971
0
        if (cparam->current.custom_string)
972
0
    free(cparam->current.custom_string);
973
974
0
        cparam->current.custom_string = strdup(val->value);
975
0
        break;
976
0
  }
977
0
      }
978
979
0
      cupsFreeOptions(num_vals, vals);
980
0
    }
981
0
  }
982
0
  else
983
0
  {
984
0
    for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
985
0
      if (!_cups_strcasecmp(c->choice, choice))
986
0
        break;
987
988
0
    if (!i)
989
0
      return;
990
0
  }
991
992
 /*
993
  * Option found; mark it and then handle unmarking any other options.
994
  */
995
996
0
  if (o->ui != PPD_UI_PICKMANY)
997
0
  {
998
   /*
999
    * Unmark all other choices...
1000
    */
1001
1002
0
    if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
1003
0
    {
1004
0
      oldc->marked = 0;
1005
0
      cupsArrayRemove(ppd->marked, oldc);
1006
0
    }
1007
1008
0
    if (!_cups_strcasecmp(option, "PageSize") || !_cups_strcasecmp(option, "PageRegion"))
1009
0
    {
1010
     /*
1011
      * Mark current page size...
1012
      */
1013
1014
0
      for (j = 0; j < ppd->num_sizes; j ++)
1015
0
  ppd->sizes[j].marked = !_cups_strcasecmp(ppd->sizes[j].name,
1016
0
                               choice);
1017
1018
     /*
1019
      * Unmark the current PageSize or PageRegion setting, as
1020
      * appropriate...
1021
      */
1022
1023
0
      cupsArraySave(ppd->options);
1024
1025
0
      if (!_cups_strcasecmp(option, "PageSize"))
1026
0
      {
1027
0
  if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
1028
0
        {
1029
0
          key.option = o;
1030
0
          if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1031
0
          {
1032
0
            oldc->marked = 0;
1033
0
            cupsArrayRemove(ppd->marked, oldc);
1034
0
          }
1035
0
        }
1036
0
      }
1037
0
      else
1038
0
      {
1039
0
  if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
1040
0
        {
1041
0
          key.option = o;
1042
0
          if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1043
0
          {
1044
0
            oldc->marked = 0;
1045
0
            cupsArrayRemove(ppd->marked, oldc);
1046
0
          }
1047
0
        }
1048
0
      }
1049
1050
0
      cupsArrayRestore(ppd->options);
1051
0
    }
1052
0
    else if (!_cups_strcasecmp(option, "InputSlot"))
1053
0
    {
1054
     /*
1055
      * Unmark ManualFeed option...
1056
      */
1057
1058
0
      cupsArraySave(ppd->options);
1059
1060
0
      if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
1061
0
      {
1062
0
        key.option = o;
1063
0
        if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1064
0
        {
1065
0
          oldc->marked = 0;
1066
0
          cupsArrayRemove(ppd->marked, oldc);
1067
0
        }
1068
0
      }
1069
1070
0
      cupsArrayRestore(ppd->options);
1071
0
    }
1072
0
    else if (!_cups_strcasecmp(option, "ManualFeed") &&
1073
0
       !_cups_strcasecmp(choice, "True"))
1074
0
    {
1075
     /*
1076
      * Unmark InputSlot option...
1077
      */
1078
1079
0
      cupsArraySave(ppd->options);
1080
1081
0
      if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
1082
0
      {
1083
0
        key.option = o;
1084
0
        if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
1085
0
        {
1086
0
          oldc->marked = 0;
1087
0
          cupsArrayRemove(ppd->marked, oldc);
1088
0
        }
1089
0
      }
1090
1091
0
      cupsArrayRestore(ppd->options);
1092
0
    }
1093
0
  }
1094
1095
0
  c->marked = 1;
1096
1097
0
  cupsArrayAdd(ppd->marked, c);
1098
0
}