Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cups/cups/options.c
Line
Count
Source
1
/*
2
 * Option routines for CUPS.
3
 *
4
 * Copyright 2007-2017 by Apple Inc.
5
 * Copyright 1997-2007 by Easy Software Products.
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
 * This file is subject to the Apple OS-Developed Software exception.
14
 */
15
16
/*
17
 * Include necessary headers...
18
 */
19
20
#include "cups-private.h"
21
22
23
/*
24
 * Local functions...
25
 */
26
27
static int  cups_compare_options(cups_option_t *a, cups_option_t *b);
28
static int  cups_find_option(const char *name, int num_options,
29
                           cups_option_t *option, int prev, int *rdiff);
30
31
32
/*
33
 * 'cupsAddIntegerOption()' - Add an integer option to an option array.
34
 *
35
 * New option arrays can be initialized simply by passing 0 for the
36
 * "num_options" parameter.
37
 *
38
 * @since CUPS 2.2.4/macOS 10.13@
39
 */
40
41
int         /* O  - Number of options */
42
cupsAddIntegerOption(
43
    const char    *name,    /* I  - Name of option */
44
    int           value,    /* I  - Value of option */
45
    int           num_options,    /* I  - Number of options */
46
    cups_option_t **options)    /* IO - Pointer to options */
47
0
{
48
0
  char  strvalue[32];     /* String value */
49
50
51
0
  snprintf(strvalue, sizeof(strvalue), "%d", value);
52
53
0
  return (cupsAddOption(name, strvalue, num_options, options));
54
0
}
55
56
57
/*
58
 * 'cupsAddOption()' - Add an option to an option array.
59
 *
60
 * New option arrays can be initialized simply by passing 0 for the
61
 * "num_options" parameter.
62
 */
63
64
int         /* O  - Number of options */
65
cupsAddOption(const char    *name,  /* I  - Name of option */
66
              const char    *value, /* I  - Value of option */
67
        int           num_options,/* I  - Number of options */
68
              cups_option_t **options)  /* IO - Pointer to options */
69
0
{
70
0
  cups_option_t *temp;      /* Pointer to new option */
71
0
  int   insert,     /* Insertion point */
72
0
    diff;     /* Result of search */
73
74
75
0
  DEBUG_printf(("2cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, options=%p)", name, value, num_options, (void *)options));
76
77
0
  if (!name || !name[0] || !value || !options || num_options < 0)
78
0
  {
79
0
    DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
80
0
    return (num_options);
81
0
  }
82
83
0
  if (!_cups_strcasecmp(name, "cupsPrintQuality"))
84
0
    num_options = cupsRemoveOption("print-quality", num_options, options);
85
0
  else if (!_cups_strcasecmp(name, "print-quality"))
86
0
    num_options = cupsRemoveOption("cupsPrintQuality", num_options, options);
87
88
 /*
89
  * Look for an existing option with the same name...
90
  */
91
92
0
  if (num_options == 0)
93
0
  {
94
0
    insert = 0;
95
0
    diff   = 1;
96
0
  }
97
0
  else
98
0
  {
99
0
    insert = cups_find_option(name, num_options, *options, num_options - 1,
100
0
                              &diff);
101
102
0
    if (diff > 0)
103
0
      insert ++;
104
0
  }
105
106
0
  if (diff)
107
0
  {
108
   /*
109
    * No matching option name...
110
    */
111
112
0
    DEBUG_printf(("4cupsAddOption: New option inserted at index %d...",
113
0
                  insert));
114
115
0
    if (num_options == 0)
116
0
      temp = (cups_option_t *)malloc(sizeof(cups_option_t));
117
0
    else
118
0
      temp = (cups_option_t *)realloc(*options, sizeof(cups_option_t) * (size_t)(num_options + 1));
119
120
0
    if (!temp)
121
0
    {
122
0
      DEBUG_puts("3cupsAddOption: Unable to expand option array, returning 0");
123
0
      return (0);
124
0
    }
125
126
0
    *options = temp;
127
128
0
    if (insert < num_options)
129
0
    {
130
0
      DEBUG_printf(("4cupsAddOption: Shifting %d options...",
131
0
                    (int)(num_options - insert)));
132
0
      memmove(temp + insert + 1, temp + insert, (size_t)(num_options - insert) * sizeof(cups_option_t));
133
0
    }
134
135
0
    temp        += insert;
136
0
    temp->name  = _cupsStrAlloc(name);
137
0
    num_options ++;
138
0
  }
139
0
  else
140
0
  {
141
   /*
142
    * Match found; free the old value...
143
    */
144
145
0
    DEBUG_printf(("4cupsAddOption: Option already exists at index %d...",
146
0
                  insert));
147
148
0
    temp = *options + insert;
149
0
    _cupsStrFree(temp->value);
150
0
  }
151
152
0
  temp->value = _cupsStrAlloc(value);
153
154
0
  DEBUG_printf(("3cupsAddOption: Returning %d", num_options));
155
156
0
  return (num_options);
157
0
}
158
159
160
/*
161
 * 'cupsFreeOptions()' - Free all memory used by options.
162
 */
163
164
void
165
cupsFreeOptions(
166
    int           num_options,    /* I - Number of options */
167
    cups_option_t *options)   /* I - Pointer to options */
168
0
{
169
0
  int i;        /* Looping var */
170
171
172
0
  DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)", num_options, (void *)options));
173
174
0
  if (num_options <= 0 || !options)
175
0
    return;
176
177
0
  for (i = 0; i < num_options; i ++)
178
0
  {
179
0
    _cupsStrFree(options[i].name);
180
0
    _cupsStrFree(options[i].value);
181
0
  }
182
183
0
  free(options);
184
0
}
185
186
187
/*
188
 * 'cupsGetIntegerOption()' - Get an integer option value.
189
 *
190
 * INT_MIN is returned when the option does not exist, is not an integer, or
191
 * exceeds the range of values for the "int" type.
192
 *
193
 * @since CUPS 2.2.4/macOS 10.13@
194
 */
195
196
int         /* O - Option value or @code INT_MIN@ */
197
cupsGetIntegerOption(
198
    const char    *name,    /* I - Name of option */
199
    int           num_options,    /* I - Number of options */
200
    cups_option_t *options)   /* I - Options */
201
0
{
202
0
  const char  *value = cupsGetOption(name, num_options, options);
203
          /* String value of option */
204
0
  char    *ptr;     /* Pointer into string value */
205
0
  long    intvalue;   /* Integer value */
206
207
208
0
  if (!value || !*value)
209
0
    return (INT_MIN);
210
211
0
  intvalue = strtol(value, &ptr, 10);
212
0
  if (intvalue < INT_MIN || intvalue > INT_MAX || *ptr)
213
0
    return (INT_MIN);
214
215
0
  return ((int)intvalue);
216
0
}
217
218
219
/*
220
 * 'cupsGetOption()' - Get an option value.
221
 */
222
223
const char *        /* O - Option value or @code NULL@ */
224
cupsGetOption(const char    *name,  /* I - Name of option */
225
              int           num_options,/* I - Number of options */
226
              cups_option_t *options) /* I - Options */
227
0
{
228
0
  int diff,       /* Result of comparison */
229
0
  match;        /* Matching index */
230
231
232
0
  DEBUG_printf(("2cupsGetOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
233
234
0
  if (!name || num_options <= 0 || !options)
235
0
  {
236
0
    DEBUG_puts("3cupsGetOption: Returning NULL");
237
0
    return (NULL);
238
0
  }
239
240
0
  match = cups_find_option(name, num_options, options, -1, &diff);
241
242
0
  if (!diff)
243
0
  {
244
0
    DEBUG_printf(("3cupsGetOption: Returning \"%s\"", options[match].value));
245
0
    return (options[match].value);
246
0
  }
247
248
0
  DEBUG_puts("3cupsGetOption: Returning NULL");
249
0
  return (NULL);
250
0
}
251
252
253
/*
254
 * 'cupsParseOptions()' - Parse options from a command-line argument.
255
 *
256
 * This function converts space-delimited name/value pairs according
257
 * to the PAPI text option ABNF specification. Collection values
258
 * ("name={a=... b=... c=...}") are stored with the curley brackets
259
 * intact - use @code cupsParseOptions@ on the value to extract the
260
 * collection attributes.
261
 */
262
263
int         /* O - Number of options found */
264
cupsParseOptions(
265
    const char    *arg,     /* I - Argument to parse */
266
    int           num_options,    /* I - Number of options */
267
    cups_option_t **options)    /* O - Options found */
268
0
{
269
0
  char  *copyarg,     /* Copy of input string */
270
0
  *ptr,       /* Pointer into string */
271
0
  *name,        /* Pointer to name */
272
0
  *value,       /* Pointer to value */
273
0
  sep,        /* Separator character */
274
0
  quote;        /* Quote character */
275
276
277
0
  DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)", arg, num_options, (void *)options));
278
279
 /*
280
  * Range check input...
281
  */
282
283
0
  if (!arg)
284
0
  {
285
0
    DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
286
0
    return (num_options);
287
0
  }
288
289
0
  if (!options || num_options < 0)
290
0
  {
291
0
    DEBUG_puts("1cupsParseOptions: Returning 0");
292
0
    return (0);
293
0
  }
294
295
 /*
296
  * Make a copy of the argument string and then divide it up...
297
  */
298
299
0
  if ((copyarg = strdup(arg)) == NULL)
300
0
  {
301
0
    DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
302
0
    DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
303
0
    return (num_options);
304
0
  }
305
306
0
  if (*copyarg == '{')
307
0
  {
308
   /*
309
    * Remove surrounding {} so we can parse "{name=value ... name=value}"...
310
    */
311
312
0
    if ((ptr = copyarg + strlen(copyarg) - 1) > copyarg && *ptr == '}')
313
0
    {
314
0
      *ptr = '\0';
315
0
      ptr  = copyarg + 1;
316
0
    }
317
0
    else
318
0
      ptr = copyarg;
319
0
  }
320
0
  else
321
0
    ptr = copyarg;
322
323
 /*
324
  * Skip leading spaces...
325
  */
326
327
0
  while (_cups_isspace(*ptr))
328
0
    ptr ++;
329
330
 /*
331
  * Loop through the string...
332
  */
333
334
0
  while (*ptr != '\0')
335
0
  {
336
   /*
337
    * Get the name up to a SPACE, =, or end-of-string...
338
    */
339
340
0
    name = ptr;
341
0
    while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
342
0
      ptr ++;
343
344
   /*
345
    * Avoid an empty name...
346
    */
347
348
0
    if (ptr == name)
349
0
      break;
350
351
   /*
352
    * Skip trailing spaces...
353
    */
354
355
0
    while (_cups_isspace(*ptr))
356
0
      *ptr++ = '\0';
357
358
0
    if ((sep = *ptr) == '=')
359
0
      *ptr++ = '\0';
360
361
0
    DEBUG_printf(("2cupsParseOptions: name=\"%s\"", name));
362
363
0
    if (sep != '=')
364
0
    {
365
     /*
366
      * Boolean option...
367
      */
368
369
0
      if (!_cups_strncasecmp(name, "no", 2))
370
0
        num_options = cupsAddOption(name + 2, "false", num_options,
371
0
                              options);
372
0
      else
373
0
        num_options = cupsAddOption(name, "true", num_options, options);
374
375
0
      continue;
376
0
    }
377
378
   /*
379
    * Remove = and parse the value...
380
    */
381
382
0
    value = ptr;
383
384
0
    while (*ptr && !_cups_isspace(*ptr))
385
0
    {
386
0
      if (*ptr == ',')
387
0
        ptr ++;
388
0
      else if (*ptr == '\'' || *ptr == '\"')
389
0
      {
390
       /*
391
  * Quoted string constant...
392
  */
393
394
0
  quote = *ptr;
395
0
  _cups_strcpy(ptr, ptr + 1);
396
397
0
  while (*ptr != quote && *ptr)
398
0
  {
399
0
    if (*ptr == '\\' && ptr[1])
400
0
      _cups_strcpy(ptr, ptr + 1);
401
402
0
    ptr ++;
403
0
  }
404
405
0
  if (*ptr)
406
0
    _cups_strcpy(ptr, ptr + 1);
407
0
      }
408
0
      else if (*ptr == '{')
409
0
      {
410
       /*
411
  * Collection value...
412
  */
413
414
0
  int depth;
415
416
0
  for (depth = 0; *ptr; ptr ++)
417
0
  {
418
0
    if (*ptr == '{')
419
0
      depth ++;
420
0
    else if (*ptr == '}')
421
0
    {
422
0
      depth --;
423
0
      if (!depth)
424
0
      {
425
0
        ptr ++;
426
0
        break;
427
0
      }
428
0
    }
429
0
    else if (*ptr == '\\' && ptr[1])
430
0
      _cups_strcpy(ptr, ptr + 1);
431
0
  }
432
0
      }
433
0
      else
434
0
      {
435
       /*
436
  * Normal space-delimited string...
437
  */
438
439
0
  while (*ptr && !_cups_isspace(*ptr))
440
0
  {
441
0
    if (*ptr == '\\' && ptr[1])
442
0
      _cups_strcpy(ptr, ptr + 1);
443
444
0
    ptr ++;
445
0
  }
446
0
      }
447
0
    }
448
449
0
    if (*ptr != '\0')
450
0
      *ptr++ = '\0';
451
452
0
    DEBUG_printf(("2cupsParseOptions: value=\"%s\"", value));
453
454
   /*
455
    * Skip trailing whitespace...
456
    */
457
458
0
    while (_cups_isspace(*ptr))
459
0
      ptr ++;
460
461
   /*
462
    * Add the string value...
463
    */
464
465
0
    num_options = cupsAddOption(name, value, num_options, options);
466
0
  }
467
468
 /*
469
  * Free the copy of the argument we made and return the number of options
470
  * found.
471
  */
472
473
0
  free(copyarg);
474
475
0
  DEBUG_printf(("1cupsParseOptions: Returning %d", num_options));
476
477
0
  return (num_options);
478
0
}
479
480
481
/*
482
 * 'cupsRemoveOption()' - Remove an option from an option array.
483
 *
484
 * @since CUPS 1.2/macOS 10.5@
485
 */
486
487
int         /* O  - New number of options */
488
cupsRemoveOption(
489
    const char    *name,    /* I  - Option name */
490
    int           num_options,    /* I  - Current number of options */
491
    cups_option_t **options)    /* IO - Options */
492
0
{
493
0
  int   i;      /* Looping var */
494
0
  cups_option_t *option;    /* Current option */
495
496
497
0
  DEBUG_printf(("2cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)", name, num_options, (void *)options));
498
499
 /*
500
  * Range check input...
501
  */
502
503
0
  if (!name || num_options < 1 || !options)
504
0
  {
505
0
    DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
506
0
    return (num_options);
507
0
  }
508
509
 /*
510
  * Loop for the option...
511
  */
512
513
0
  for (i = num_options, option = *options; i > 0; i --, option ++)
514
0
    if (!_cups_strcasecmp(name, option->name))
515
0
      break;
516
517
0
  if (i)
518
0
  {
519
   /*
520
    * Remove this option from the array...
521
    */
522
523
0
    DEBUG_puts("4cupsRemoveOption: Found option, removing it...");
524
525
0
    num_options --;
526
0
    i --;
527
528
0
    _cupsStrFree(option->name);
529
0
    _cupsStrFree(option->value);
530
531
0
    if (i > 0)
532
0
      memmove(option, option + 1, (size_t)i * sizeof(cups_option_t));
533
0
  }
534
535
 /*
536
  * Return the new number of options...
537
  */
538
539
0
  DEBUG_printf(("3cupsRemoveOption: Returning %d", num_options));
540
0
  return (num_options);
541
0
}
542
543
544
/*
545
 * '_cupsGet1284Values()' - Get 1284 device ID keys and values.
546
 *
547
 * The returned dictionary is a CUPS option array that can be queried with
548
 * cupsGetOption and freed with cupsFreeOptions.
549
 */
550
551
int         /* O - Number of key/value pairs */
552
_cupsGet1284Values(
553
    const char *device_id,    /* I - IEEE-1284 device ID string */
554
    cups_option_t **values)   /* O - Array of key/value pairs */
555
0
{
556
0
  int   num_values;   /* Number of values */
557
0
  char    key[256],   /* Key string */
558
0
    value[256],   /* Value string */
559
0
    *ptr;     /* Pointer into key/value */
560
561
562
 /*
563
  * Range check input...
564
  */
565
566
0
  if (values)
567
0
    *values = NULL;
568
569
0
  if (!device_id || !values)
570
0
    return (0);
571
572
 /*
573
  * Parse the 1284 device ID value into keys and values.  The format is
574
  * repeating sequences of:
575
  *
576
  *   [whitespace]key:value[whitespace];
577
  */
578
579
0
  num_values = 0;
580
0
  while (*device_id)
581
0
  {
582
0
    while (_cups_isspace(*device_id))
583
0
      device_id ++;
584
585
0
    if (!*device_id)
586
0
      break;
587
588
0
    for (ptr = key; *device_id && *device_id != ':'; device_id ++)
589
0
      if (ptr < (key + sizeof(key) - 1))
590
0
        *ptr++ = *device_id;
591
592
0
    if (!*device_id)
593
0
      break;
594
595
0
    while (ptr > key && _cups_isspace(ptr[-1]))
596
0
      ptr --;
597
598
0
    *ptr = '\0';
599
0
    device_id ++;
600
601
0
    while (_cups_isspace(*device_id))
602
0
      device_id ++;
603
604
0
    if (!*device_id)
605
0
      break;
606
607
0
    for (ptr = value; *device_id && *device_id != ';'; device_id ++)
608
0
      if (ptr < (value + sizeof(value) - 1))
609
0
        *ptr++ = *device_id;
610
611
0
    if (!*device_id)
612
0
      break;
613
614
0
    while (ptr > value && _cups_isspace(ptr[-1]))
615
0
      ptr --;
616
617
0
    *ptr = '\0';
618
0
    device_id ++;
619
620
0
    num_values = cupsAddOption(key, value, num_values, values);
621
0
  }
622
623
0
  return (num_values);
624
0
}
625
626
627
/*
628
 * 'cups_compare_options()' - Compare two options.
629
 */
630
631
static int        /* O - Result of comparison */
632
cups_compare_options(cups_option_t *a,  /* I - First option */
633
         cups_option_t *b)  /* I - Second option */
634
0
{
635
0
  return (_cups_strcasecmp(a->name, b->name));
636
0
}
637
638
639
/*
640
 * 'cups_find_option()' - Find an option using a binary search.
641
 */
642
643
static int        /* O - Index of match */
644
cups_find_option(
645
    const char    *name,    /* I - Option name */
646
    int           num_options,    /* I - Number of options */
647
    cups_option_t *options,   /* I - Options */
648
    int           prev,     /* I - Previous index */
649
    int           *rdiff)   /* O - Difference of match */
650
0
{
651
0
  int   left,     /* Low mark for binary search */
652
0
    right,      /* High mark for binary search */
653
0
    current,    /* Current index */
654
0
    diff;     /* Result of comparison */
655
0
  cups_option_t key;      /* Search key */
656
657
658
0
  DEBUG_printf(("7cups_find_option(name=\"%s\", num_options=%d, options=%p, prev=%d, rdiff=%p)", name, num_options, (void *)options, prev, (void *)rdiff));
659
660
#ifdef DEBUG
661
  for (left = 0; left < num_options; left ++)
662
    DEBUG_printf(("9cups_find_option: options[%d].name=\"%s\", .value=\"%s\"",
663
                  left, options[left].name, options[left].value));
664
#endif /* DEBUG */
665
666
0
  key.name = (char *)name;
667
668
0
  if (prev >= 0)
669
0
  {
670
   /*
671
    * Start search on either side of previous...
672
    */
673
674
0
    if ((diff = cups_compare_options(&key, options + prev)) == 0 ||
675
0
        (diff < 0 && prev == 0) ||
676
0
  (diff > 0 && prev == (num_options - 1)))
677
0
    {
678
0
      *rdiff = diff;
679
0
      return (prev);
680
0
    }
681
0
    else if (diff < 0)
682
0
    {
683
     /*
684
      * Start with previous on right side...
685
      */
686
687
0
      left  = 0;
688
0
      right = prev;
689
0
    }
690
0
    else
691
0
    {
692
     /*
693
      * Start wih previous on left side...
694
      */
695
696
0
      left  = prev;
697
0
      right = num_options - 1;
698
0
    }
699
0
  }
700
0
  else
701
0
  {
702
   /*
703
    * Start search in the middle...
704
    */
705
706
0
    left  = 0;
707
0
    right = num_options - 1;
708
0
  }
709
710
0
  do
711
0
  {
712
0
    current = (left + right) / 2;
713
0
    diff    = cups_compare_options(&key, options + current);
714
715
0
    if (diff == 0)
716
0
      break;
717
0
    else if (diff < 0)
718
0
      right = current;
719
0
    else
720
0
      left = current;
721
0
  }
722
0
  while ((right - left) > 1);
723
724
0
  if (diff != 0)
725
0
  {
726
   /*
727
    * Check the last 1 or 2 elements...
728
    */
729
730
0
    if ((diff = cups_compare_options(&key, options + left)) <= 0)
731
0
      current = left;
732
0
    else
733
0
    {
734
0
      diff    = cups_compare_options(&key, options + right);
735
0
      current = right;
736
0
    }
737
0
  }
738
739
 /*
740
  * Return the closest destination and the difference...
741
  */
742
743
0
  *rdiff = diff;
744
745
0
  return (current);
746
0
}