Coverage Report

Created: 2025-06-24 07:01

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