Coverage Report

Created: 2025-07-11 06:21

/src/libcups/cups/options.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// Option routines for CUPS.
3
//
4
// Copyright © 2022-2024 by OpenPrinting.
5
// Copyright © 2007-2017 by Apple Inc.
6
// Copyright © 1997-2007 by Easy Software Products.
7
//
8
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
9
// information.
10
//
11
12
#include "cups-private.h"
13
14
15
//
16
// Local functions...
17
//
18
19
static int  cups_compare_options(cups_option_t *a, cups_option_t *b);
20
static size_t cups_find_option(const char *name, size_t num_options, cups_option_t *option, int *rdiff);
21
22
23
//
24
// 'cupsAddIntegerOption()' - Add an integer option to an option array.
25
//
26
// New option arrays can be initialized simply by passing 0 for the
27
// "num_options" parameter.
28
//
29
30
size_t          // O  - Number of options
31
cupsAddIntegerOption(
32
    const char    *name,    // I  - Name of option
33
    long           value,   // I  - Value of option
34
    size_t        num_options,    // I  - Number of options
35
    cups_option_t **options)    // IO - Pointer to options
36
0
{
37
0
  char  strvalue[32];     // String value
38
39
40
0
  snprintf(strvalue, sizeof(strvalue), "%ld", value);
41
42
0
  return (cupsAddOption(name, strvalue, num_options, options));
43
0
}
44
45
46
//
47
// 'cupsAddOption()' - Add an option to an option array.
48
//
49
// New option arrays can be initialized simply by passing 0 for the
50
// "num_options" parameter.
51
//
52
53
size_t          // O  - Number of options
54
cupsAddOption(const char    *name,  // I  - Name of option
55
              const char    *value, // I  - Value of option
56
        size_t        num_options,// I  - Number of options
57
              cups_option_t **options)  // IO - Pointer to options
58
0
{
59
0
  cups_option_t *temp;      // Pointer to new option
60
0
  size_t  insert;     // Insertion point
61
0
  int   diff;     // Result of search
62
63
64
0
  if (!name || !name[0] || !value || !options)
65
0
    return (num_options);
66
67
0
  if (!_cups_strcasecmp(name, "cupsPrintQuality"))
68
0
    num_options = cupsRemoveOption("print-quality", num_options, options);
69
0
  else if (!_cups_strcasecmp(name, "print-quality"))
70
0
    num_options = cupsRemoveOption("cupsPrintQuality", num_options, options);
71
72
  // Look for an existing option with the same name...
73
0
  if (num_options == 0)
74
0
  {
75
0
    insert = 0;
76
0
    diff   = 1;
77
0
  }
78
0
  else
79
0
  {
80
0
    insert = cups_find_option(name, num_options, *options, &diff);
81
82
0
    if (diff > 0)
83
0
      insert ++;
84
0
  }
85
86
0
  if (diff)
87
0
  {
88
    // No matching option name...
89
0
    if ((temp = (cups_option_t *)realloc(num_options ? *options : NULL, sizeof(cups_option_t) * (num_options + 1))) == NULL)
90
0
      return (0);
91
92
0
    *options = temp;
93
94
0
    if (insert < num_options)
95
0
      memmove(temp + insert + 1, temp + insert, (num_options - insert) * sizeof(cups_option_t));
96
97
0
    temp        += insert;
98
0
    temp->name  = _cupsStrAlloc(name);
99
0
    num_options ++;
100
0
  }
101
0
  else
102
0
  {
103
    // Match found; free the old value...
104
0
    temp = *options + insert;
105
0
    _cupsStrFree(temp->value);
106
0
  }
107
108
0
  temp->value = _cupsStrAlloc(value);
109
110
0
  return (num_options);
111
0
}
112
113
114
//
115
// 'cupsFreeOptions()' - Free all memory used by options.
116
//
117
118
void
119
cupsFreeOptions(
120
    size_t        num_options,    // I - Number of options
121
    cups_option_t *options)   // I - Pointer to options
122
0
{
123
0
  size_t  i;      // Looping var
124
125
126
0
  if (num_options == 0 || !options)
127
0
    return;
128
129
0
  for (i = 0; i < num_options; i ++)
130
0
  {
131
0
    _cupsStrFree(options[i].name);
132
0
    _cupsStrFree(options[i].value);
133
0
  }
134
135
0
  free(options);
136
0
}
137
138
139
//
140
// 'cupsGetIntegerOption()' - Get an integer option value.
141
//
142
// `LONG_MIN` is returned when the option does not exist, is not an integer, or
143
// exceeds the range of values for the `long` type.
144
//
145
146
long          // O - Option value or `LONG_MIN`
147
cupsGetIntegerOption(
148
    const char    *name,    // I - Name of option
149
    size_t        num_options,    // I - Number of options
150
    cups_option_t *options)   // I - Options
151
0
{
152
0
  const char  *value = cupsGetOption(name, num_options, options);
153
          // String value of option
154
0
  char    *ptr;     // Pointer into string value
155
0
  long    lvalue;     // Integer value
156
157
158
0
  if (!value || !*value)
159
0
    return (LONG_MIN);
160
161
0
  lvalue = strtol(value, &ptr, 10);
162
163
0
  if (*ptr)
164
0
    return (LONG_MIN);
165
0
  else
166
0
    return (lvalue);
167
0
}
168
169
170
//
171
// 'cupsGetOption()' - Get an option value.
172
//
173
174
const char *        // O - Option value or @code NULL@
175
cupsGetOption(const char    *name,  // I - Name of option
176
              size_t        num_options,// I - Number of options
177
              cups_option_t *options) // I - Options
178
0
{
179
0
  int   diff;     // Result of comparison
180
0
  size_t  match;      // Matching index
181
182
183
0
  if (!name || num_options == 0 || !options)
184
0
    return (NULL);
185
186
0
  match = cups_find_option(name, num_options, options, &diff);
187
188
0
  if (!diff)
189
0
    return (options[match].value);
190
191
0
  return (NULL);
192
0
}
193
194
195
//
196
// 'cupsParseOptions()' - Parse options from a command-line argument.
197
//
198
// This function converts space-delimited name/value pairs according
199
// to the PAPI text option ABNF specification. Collection values
200
// ("name={a=... b=... c=...}") are stored with the curley brackets
201
// intact - use @code cupsParseOptions@ on the value to extract the
202
// collection attributes.
203
//
204
// The "end" argument, if not `NULL`, receives a pointer to the end of the
205
// options.
206
//
207
208
size_t          // O - Number of options found
209
cupsParseOptions(
210
    const char    *arg,     // I - Argument to parse
211
    const char    **end,    // O - Pointer to end of options or `NULL` for "don't care"
212
    size_t        num_options,    // I - Number of options
213
    cups_option_t **options)    // O - Options found
214
0
{
215
0
  char  *copyarg,     // Copy of input string
216
0
  *ptr,       // Pointer into string
217
0
  *name,        // Pointer to name
218
0
  *value,       // Pointer to value
219
0
  sep,        // Separator character
220
0
  quote;        // Quote character
221
222
223
  // Range check input...
224
0
  if (end)
225
0
    *end = NULL;
226
227
0
  if (!arg)
228
0
    return (num_options);
229
230
0
  if (!options)
231
0
    return (0);
232
233
  // Make a copy of the argument string and then divide it up...
234
0
  if ((copyarg = strdup(arg)) == NULL)
235
0
  {
236
0
    DEBUG_puts("1cupsParseOptions: Unable to copy arg string");
237
0
    return (num_options);
238
0
  }
239
240
0
  if (*copyarg == '{')
241
0
    ptr  = copyarg + 1;
242
0
  else
243
0
    ptr = copyarg;
244
245
  // Skip leading spaces...
246
0
  while (_cups_isspace(*ptr))
247
0
    ptr ++;
248
249
  // Loop through the string...
250
0
  while (*ptr != '\0')
251
0
  {
252
    // Get the name up to a SPACE, =, or end-of-string...
253
0
    name = ptr;
254
0
    while (!strchr("\f\n\r\t\v =", *ptr) && *ptr)
255
0
      ptr ++;
256
257
    // Avoid an empty name...
258
0
    if (ptr == name)
259
0
      break;
260
261
    // End after the closing brace...
262
0
    if (*ptr == '}' && *copyarg == '{')
263
0
    {
264
0
      *ptr++ = '\0';
265
0
      break;
266
0
    }
267
268
    // Skip trailing spaces...
269
0
    while (_cups_isspace(*ptr))
270
0
      *ptr++ = '\0';
271
272
0
    if ((sep = *ptr) == '=')
273
0
      *ptr++ = '\0';
274
275
0
    if (sep != '=')
276
0
    {
277
      // Boolean option...
278
0
      if (!_cups_strncasecmp(name, "no", 2))
279
0
        num_options = cupsAddOption(name + 2, "false", num_options, options);
280
0
      else
281
0
        num_options = cupsAddOption(name, "true", num_options, options);
282
283
0
      continue;
284
0
    }
285
286
    // Remove = and parse the value...
287
0
    value = ptr;
288
289
0
    while (*ptr && !_cups_isspace(*ptr))
290
0
    {
291
0
      if (*ptr == ',')
292
0
      {
293
0
        ptr ++;
294
0
      }
295
0
      else if (*ptr == '\'' || *ptr == '\"')
296
0
      {
297
        // Quoted string constant...
298
0
  quote = *ptr;
299
0
  _cups_strcpy(ptr, ptr + 1);
300
301
0
  while (*ptr != quote && *ptr)
302
0
  {
303
0
    if (*ptr == '\\' && ptr[1])
304
0
      _cups_strcpy(ptr, ptr + 1);
305
306
0
    ptr ++;
307
0
  }
308
309
0
  if (*ptr)
310
0
    _cups_strcpy(ptr, ptr + 1);
311
0
      }
312
0
      else if (*ptr == '{')
313
0
      {
314
        // Collection value...
315
0
  int depth;      // Nesting depth for braces
316
317
0
  for (depth = 0; *ptr; ptr ++)
318
0
  {
319
0
    if (*ptr == '{')
320
0
    {
321
0
      depth ++;
322
0
    }
323
0
    else if (*ptr == '}')
324
0
    {
325
0
      depth --;
326
0
      if (!depth)
327
0
      {
328
0
        ptr ++;
329
0
        break;
330
0
      }
331
0
    }
332
0
    else if (*ptr == '\\' && ptr[1])
333
0
    {
334
0
      _cups_strcpy(ptr, ptr + 1);
335
0
    }
336
0
  }
337
0
      }
338
0
      else
339
0
      {
340
        // Normal space-delimited string...
341
0
  while (*ptr && !_cups_isspace(*ptr))
342
0
  {
343
0
    if (*ptr == '}' && *copyarg == '{')
344
0
    {
345
0
      *ptr++ = '\0';
346
0
      break;
347
0
    }
348
349
0
    if (*ptr == '\\' && ptr[1])
350
0
      _cups_strcpy(ptr, ptr + 1);
351
352
0
    ptr ++;
353
0
  }
354
0
      }
355
0
    }
356
357
0
    if (*ptr != '\0')
358
0
      *ptr++ = '\0';
359
360
    // Skip trailing whitespace...
361
0
    while (_cups_isspace(*ptr))
362
0
      ptr ++;
363
364
    // Add the string value...
365
0
    num_options = cupsAddOption(name, value, num_options, options);
366
0
  }
367
368
  // Save the progress in the input string...
369
0
  if (end)
370
0
    *end = arg + (ptr - copyarg);
371
372
  // Free the copy of the argument we made and return the number of options found.
373
0
  free(copyarg);
374
375
0
  return (num_options);
376
0
}
377
378
379
//
380
// 'cupsRemoveOption()' - Remove an option from an option array.
381
//
382
383
size_t          // O  - New number of options
384
cupsRemoveOption(
385
    const char    *name,    // I  - Option name
386
    size_t        num_options,    // I  - Current number of options
387
    cups_option_t **options)    // IO - Options
388
0
{
389
0
  size_t  i;      // Looping var
390
0
  cups_option_t *option;    // Current option
391
392
393
  // Range check input...
394
0
  if (!name || num_options == 0 || !options)
395
0
    return (num_options);
396
397
  // Loop for the option...
398
0
  for (i = num_options, option = *options; i > 0; i --, option ++)
399
0
  {
400
0
    if (!_cups_strcasecmp(name, option->name))
401
0
      break;
402
0
  }
403
404
0
  if (i)
405
0
  {
406
    // Remove this option from the array...
407
0
    num_options --;
408
0
    i --;
409
410
0
    _cupsStrFree(option->name);
411
0
    _cupsStrFree(option->value);
412
413
0
    if (i > 0)
414
0
      memmove(option, option + 1, i * sizeof(cups_option_t));
415
0
  }
416
417
  // Return the new number of options...
418
0
  return (num_options);
419
0
}
420
421
422
//
423
// 'cups_compare_options()' - Compare two options.
424
//
425
426
static int        // O - Result of comparison
427
cups_compare_options(cups_option_t *a,  // I - First option
428
         cups_option_t *b)  // I - Second option
429
0
{
430
0
  return (_cups_strcasecmp(a->name, b->name));
431
0
}
432
433
434
//
435
// 'cups_find_option()' - Find an option using a binary search.
436
//
437
438
static size_t       // O - Index of match
439
cups_find_option(
440
    const char    *name,    // I - Option name
441
    size_t        num_options,    // I - Number of options
442
    cups_option_t *options,   // I - Options
443
    int           *rdiff)   // O - Difference of match
444
0
{
445
0
  size_t  left,     // Low mark for binary search
446
0
    right,      // High mark for binary search
447
0
    current;    // Current index
448
0
  int   diff;     // Result of comparison
449
0
  cups_option_t key;      // Search key
450
451
452
0
  key.name = (char *)name;
453
454
  // Start search in the middle...
455
0
  left  = 0;
456
0
  right = num_options - 1;
457
458
0
  do
459
0
  {
460
0
    current = (left + right) / 2;
461
0
    diff    = cups_compare_options(&key, options + current);
462
463
0
    if (diff == 0)
464
0
      break;
465
0
    else if (diff < 0)
466
0
      right = current;
467
0
    else
468
0
      left = current;
469
0
  }
470
0
  while ((right - left) > 1);
471
472
0
  if (diff != 0)
473
0
  {
474
    // Check the last 1 or 2 elements...
475
0
    if ((diff = cups_compare_options(&key, options + left)) <= 0)
476
0
    {
477
0
      current = left;
478
0
    }
479
0
    else
480
0
    {
481
0
      diff    = cups_compare_options(&key, options + right);
482
0
      current = right;
483
0
    }
484
0
  }
485
486
  // Return the closest destination and the difference...
487
0
  *rdiff = diff;
488
489
0
  return (current);
490
0
}