Coverage Report

Created: 2025-08-26 06:18

/src/cups/cups/string.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * String functions for CUPS.
3
 *
4
 * Copyright © 2020-2024 by OpenPrinting.
5
 * Copyright © 2007-2019 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
#define _CUPS_STRING_C_
13
#include "cups-private.h"
14
#include "debug-internal.h"
15
#include <stddef.h>
16
#include <limits.h>
17
18
19
/*
20
 * Local globals...
21
 */
22
23
static cups_mutex_t sp_mutex = CUPS_MUTEX_INITIALIZER;
24
          /* Mutex to control access to pool */
25
static cups_array_t *stringpool = NULL;
26
          /* Global string pool */
27
28
29
/*
30
 * Local functions...
31
 */
32
33
static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b, void *data);
34
static void validate_end(char *s, char *end);
35
36
37
//
38
// 'cupsConcatString()' - Safely concatenate two UTF-8 strings.
39
//
40
// @since CUPS 2.5@
41
//
42
43
size_t          // O - Length of string
44
cupsConcatString(char       *dst, // O - Destination string
45
                 const char *src, // I - Source string
46
           size_t     dstsize)  // I - Size of destination string buffer
47
0
{
48
0
  size_t  srclen;     // Length of source string
49
0
  size_t  dstlen;     // Length of destination string
50
51
52
  // Range check input...
53
0
  if (!dst || !src || dstsize == 0)
54
0
    return (0);
55
56
  // Figure out how much room is left...
57
0
  dstlen = strlen(dst);
58
59
0
  if (dstsize < (dstlen + 1))
60
0
    return (dstlen);           // No room, return immediately...
61
62
0
  dstsize -= dstlen + 1;
63
64
  // Figure out how much room is needed...
65
0
  srclen = strlen(src);
66
67
  // Copy the appropriate amount...
68
0
  if (srclen <= dstsize)
69
0
  {
70
    // String fits, just copy over...
71
0
    memmove(dst + dstlen, src, srclen);
72
0
    dst[dstlen + srclen] = '\0';
73
0
  }
74
0
  else
75
0
  {
76
    // String too big, copy what we can and clean up the end...
77
0
    memmove(dst + dstlen, src, dstsize);
78
0
    dst[dstlen + dstsize] = '\0';
79
80
0
    validate_end(dst, dst + dstlen + dstsize);
81
0
  }
82
83
0
  return (dstlen + srclen);
84
0
}
85
86
87
//
88
// 'cupsCopyString()' - Safely copy a UTF-8 string.
89
//
90
// @since CUPS 2.5@
91
//
92
93
size_t          // O - Length of string
94
cupsCopyString(char       *dst,   // O - Destination string
95
               const char *src,   // I - Source string
96
         size_t     dstsize)  // I - Size of destination string buffer
97
5.47M
{
98
5.47M
  size_t  srclen;     // Length of source string
99
100
101
  // Range check input...
102
5.47M
  if (!dst || !src || dstsize == 0)
103
0
  {
104
0
    if (dst)
105
0
      *dst = '\0';
106
0
    return (0);
107
0
  }
108
109
  // Figure out how much room is needed...
110
5.47M
  dstsize --;
111
112
5.47M
  srclen = strlen(src);
113
114
  // Copy the appropriate amount...
115
5.47M
  if (srclen <= dstsize)
116
5.42M
  {
117
    // Source string will fit...
118
5.42M
    memmove(dst, src, srclen);
119
5.42M
    dst[srclen] = '\0';
120
5.42M
  }
121
48.9k
  else
122
48.9k
  {
123
    // Source string too big, copy what we can and clean up the end...
124
48.9k
    memmove(dst, src, dstsize);
125
48.9k
    dst[dstsize] = '\0';
126
127
48.9k
    validate_end(dst, dst + dstsize);
128
48.9k
  }
129
130
5.47M
  return (srclen);
131
5.47M
}
132
133
134
//
135
// 'cupsFormatString()' - Format a UTF-8 string into a fixed size buffer.
136
//
137
// This function formats a UTF-8 string into a fixed size buffer, escaping
138
// special/control characters as needed so they can be safely displayed or
139
// logged.
140
//
141
// @since CUPS 2.5@
142
//
143
144
ssize_t         // O - Number of bytes formatted
145
cupsFormatString(
146
    char       *buffer,     // O - Output buffer
147
    size_t     bufsize,     // O - Size of output buffer
148
    const char *format,     // I - `printf`-style format string
149
    ...)        // I - Additional arguments
150
0
{
151
0
  va_list ap;     // Pointer to additional arguments
152
0
  ssize_t ret;      // Return value
153
154
155
  // Range check input...
156
0
  if (!buffer || bufsize < 2 || !format)
157
0
    return (-1);
158
159
  // Format the string...
160
0
  va_start(ap, format);
161
0
  ret = cupsFormatStringv(buffer, bufsize, format, ap);
162
0
  va_end(ap);
163
164
  // Return the number of bytes that could have been written...
165
0
  return (ret);
166
0
}
167
168
169
//
170
// 'cupsFormatStringv()' - Format a UTF-8 string into a fixed size buffer (`va_list` version).
171
//
172
// This function formats a UTF-8 string into a fixed size buffer using a
173
// variable argument pointer, escaping special/control characters as needed so
174
// they can be safely displayed or logged.
175
//
176
// @since CUPS 2.5@
177
//
178
179
ssize_t         // O - Number of bytes formatted
180
cupsFormatStringv(
181
    char       *buffer,     // O - Output buffer
182
    size_t     bufsize,     // O - Size of output buffer
183
    const char *format,     // I - printf-style format string
184
    va_list    ap)      // I - Pointer to additional arguments
185
0
{
186
0
  char    *bufptr,    // Pointer to position in buffer
187
0
    *bufend,    // Pointer to end of buffer
188
0
    size,     // Size character (h, l, L)
189
0
    type;     // Format type character
190
0
  int   width,      // Width of field
191
0
    prec;     // Number of characters of precision
192
0
  char    tformat[100],   // Temporary format string for snprintf()
193
0
    *tptr,      // Pointer into temporary format
194
0
    temp[1024];   // Buffer for formatted numbers
195
0
  char    *s;     // Pointer to string
196
0
  ssize_t bytes;      // Total number of bytes needed
197
198
199
  // Range check input...
200
0
  if (!buffer || bufsize < 2 || !format)
201
0
    return (-1);
202
203
  // Loop through the format string, formatting as needed...
204
0
  bufptr = buffer;
205
0
  bufend = buffer + bufsize - 1;
206
0
  bytes  = 0;
207
208
0
  while (*format)
209
0
  {
210
0
    if (*format == '%')
211
0
    {
212
      // Format character...
213
0
      tptr = tformat;
214
0
      *tptr++ = *format++;
215
216
0
      if (*format == '%')
217
0
      {
218
0
        if (bufptr < bufend)
219
0
    *bufptr++ = *format;
220
0
        bytes ++;
221
0
        format ++;
222
0
  continue;
223
0
      }
224
0
      else if (strchr(" -+#\'", *format))
225
0
      {
226
0
        *tptr++ = *format++;
227
0
      }
228
229
0
      if (*format == '*')
230
0
      {
231
        // Get width from argument...
232
0
  format ++;
233
0
  width = va_arg(ap, int);
234
235
0
  snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
236
0
  tptr += strlen(tptr);
237
0
      }
238
0
      else
239
0
      {
240
0
  width = 0;
241
242
0
  while (isdigit(*format & 255))
243
0
  {
244
0
    if (tptr < (tformat + sizeof(tformat) - 1))
245
0
      *tptr++ = *format;
246
247
0
    width = width * 10 + *format++ - '0';
248
0
  }
249
0
      }
250
251
0
      if (*format == '.')
252
0
      {
253
0
  if (tptr < (tformat + sizeof(tformat) - 1))
254
0
    *tptr++ = *format;
255
256
0
        format ++;
257
258
0
        if (*format == '*')
259
0
  {
260
          // Get precision from argument...
261
0
    format ++;
262
0
    prec = va_arg(ap, int);
263
264
0
    snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
265
0
    tptr += strlen(tptr);
266
0
  }
267
0
  else
268
0
  {
269
0
    prec = 0;
270
271
0
    while (isdigit(*format & 255))
272
0
    {
273
0
      if (tptr < (tformat + sizeof(tformat) - 1))
274
0
        *tptr++ = *format;
275
276
0
      prec = prec * 10 + *format++ - '0';
277
0
    }
278
0
  }
279
0
      }
280
281
0
      if (*format == 'l' && format[1] == 'l')
282
0
      {
283
0
        size = 'L';
284
285
0
  if (tptr < (tformat + sizeof(tformat) - 2))
286
0
  {
287
0
    *tptr++ = 'l';
288
0
    *tptr++ = 'l';
289
0
  }
290
291
0
  format += 2;
292
0
      }
293
0
      else if (*format == 'h' || *format == 'l' || *format == 'L')
294
0
      {
295
0
  if (tptr < (tformat + sizeof(tformat) - 1))
296
0
    *tptr++ = *format;
297
298
0
        size = *format++;
299
0
      }
300
0
      else
301
0
      {
302
0
        size = 0;
303
0
      }
304
305
0
      if (!*format)
306
0
        break;
307
308
0
      if (tptr < (tformat + sizeof(tformat) - 1))
309
0
        *tptr++ = *format;
310
311
0
      type  = *format++;
312
0
      *tptr = '\0';
313
314
0
      switch (type)
315
0
      {
316
0
  case 'E' : // Floating point formats
317
0
  case 'G' :
318
0
  case 'e' :
319
0
  case 'f' :
320
0
  case 'g' :
321
0
      if ((size_t)(width + 2) > sizeof(temp))
322
0
        break;
323
324
0
      snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
325
326
0
            bytes += (int)strlen(temp);
327
328
0
            if (bufptr < bufend)
329
0
      {
330
0
        cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
331
0
        bufptr += strlen(bufptr);
332
0
      }
333
0
      break;
334
335
0
        case 'B' : // Integer formats
336
0
  case 'X' :
337
0
  case 'b' :
338
0
        case 'd' :
339
0
  case 'i' :
340
0
  case 'o' :
341
0
  case 'u' :
342
0
  case 'x' :
343
0
      if ((size_t)(width + 2) > sizeof(temp))
344
0
        break;
345
346
0
#  ifdef HAVE_LONG_LONG
347
0
            if (size == 'L')
348
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
349
0
      else
350
0
#  endif // HAVE_LONG_LONG
351
0
            if (size == 'l')
352
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
353
0
      else
354
0
        snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
355
356
0
            bytes += (int)strlen(temp);
357
358
0
      if (bufptr < bufend)
359
0
      {
360
0
        cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
361
0
        bufptr += strlen(bufptr);
362
0
      }
363
0
      break;
364
365
0
  case 'p' : // Pointer value
366
0
      if ((size_t)(width + 2) > sizeof(temp))
367
0
        break;
368
369
0
      snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
370
371
0
            bytes += (int)strlen(temp);
372
373
0
      if (bufptr < bufend)
374
0
      {
375
0
        cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
376
0
        bufptr += strlen(bufptr);
377
0
      }
378
0
      break;
379
380
0
        case 'c' : // Character or character array
381
0
      bytes += width;
382
383
0
      if (bufptr < bufend)
384
0
      {
385
0
        if (width <= 1)
386
0
        {
387
0
          *bufptr++ = (char)va_arg(ap, int);
388
0
        }
389
0
        else
390
0
        {
391
0
    if ((bufptr + width) > bufend)
392
0
      width = (int)(bufend - bufptr);
393
394
0
    memcpy(bufptr, va_arg(ap, char *), (size_t)width);
395
0
    bufptr += width;
396
0
        }
397
0
      }
398
0
      break;
399
400
0
  case 's' : // String
401
0
      if ((s = va_arg(ap, char *)) == NULL)
402
0
        s = "(null)";
403
404
            // Copy the C string, replacing control chars and \ with C character escapes...
405
0
            for (; *s && bufptr < bufend; s ++)
406
0
      {
407
0
        if (*s == '\n')
408
0
        {
409
0
          *bufptr++ = '\\';
410
0
          if (bufptr < bufend)
411
0
      *bufptr++ = 'n';
412
0
    bytes += 2;
413
0
        }
414
0
        else if (*s == '\r')
415
0
        {
416
0
          *bufptr++ = '\\';
417
0
          if (bufptr < bufend)
418
0
      *bufptr++ = 'r';
419
0
    bytes += 2;
420
0
        }
421
0
        else if (*s == '\t')
422
0
        {
423
0
          *bufptr++ = '\\';
424
0
          if (bufptr < bufend)
425
0
      *bufptr++ = 't';
426
0
    bytes += 2;
427
0
        }
428
0
        else if (*s == '\\')
429
0
        {
430
0
          *bufptr++ = '\\';
431
0
          if (bufptr < bufend)
432
0
      *bufptr++ = '\\';
433
0
    bytes += 2;
434
0
        }
435
0
        else if (*s == '\'')
436
0
        {
437
0
          *bufptr++ = '\\';
438
0
          if (bufptr < bufend)
439
0
      *bufptr++ = '\'';
440
0
    bytes += 2;
441
0
        }
442
0
        else if (*s == '\"')
443
0
        {
444
0
          *bufptr++ = '\\';
445
0
          if (bufptr < bufend)
446
0
      *bufptr++ = '\"';
447
0
    bytes += 2;
448
0
        }
449
0
        else if ((*s & 255) < ' ')
450
0
        {
451
0
          *bufptr++ = '\\';
452
0
          if (bufptr < bufend)
453
0
      *bufptr++ = '0';
454
0
          if (bufptr < bufend)
455
0
      *bufptr++ = '0' + *s / 8;
456
0
          if (bufptr < bufend)
457
0
      *bufptr++ = '0' + (*s & 7);
458
0
    bytes += 4;
459
0
        }
460
0
        else
461
0
        {
462
0
          *bufptr++ = *s;
463
0
    bytes ++;
464
0
        }
465
0
            }
466
467
0
            if (bufptr >= bufend)
468
0
        bytes += 2 * strlen(s);
469
0
      break;
470
471
0
  case 'n' : // Output number of chars so far
472
0
      *(va_arg(ap, int *)) = (int)bytes;
473
0
      break;
474
0
      }
475
0
    }
476
0
    else
477
0
    {
478
      // Literal character...
479
0
      bytes ++;
480
481
0
      if (bufptr < bufend)
482
0
        *bufptr++ = *format++;
483
0
      else
484
0
        format ++;
485
0
    }
486
0
  }
487
488
  // Nul-terminate the string and return the number of characters needed.
489
0
  if (bufptr < bufend)
490
0
  {
491
    // Everything fit in the buffer...
492
0
    *bufptr = '\0';
493
0
  }
494
0
  else
495
0
  {
496
    // Make sure the last characters are valid UTF-8...
497
0
    *bufend = '\0';
498
499
0
    validate_end(buffer, bufend);
500
0
  }
501
502
0
  return (bytes);
503
0
}
504
505
506
/*
507
 * '_cupsStrAlloc()' - Allocate/reference a string.
508
 */
509
510
char *          /* O - String pointer */
511
_cupsStrAlloc(const char *s)    /* I - String */
512
672k
{
513
672k
  size_t    slen;   /* Length of string */
514
672k
  _cups_sp_item_t *item,    /* String pool item */
515
672k
      *key;   /* Search key */
516
517
518
 /*
519
  * Range check input...
520
  */
521
522
672k
  if (!s)
523
0
    return (NULL);
524
525
 /*
526
  * Get the string pool...
527
  */
528
529
672k
  cupsMutexLock(&sp_mutex);
530
531
672k
  if (!stringpool)
532
5
    stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
533
534
672k
  if (!stringpool)
535
0
  {
536
0
    cupsMutexUnlock(&sp_mutex);
537
538
0
    return (NULL);
539
0
  }
540
541
 /*
542
  * See if the string is already in the pool...
543
  */
544
545
672k
  key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
546
547
672k
  if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
548
588k
  {
549
   /*
550
    * Found it, return the cached string...
551
    */
552
553
588k
    item->ref_count ++;
554
555
#ifdef DEBUG_GUARDS
556
    DEBUG_printf("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, ref_count=%d", item, item->str, s, item->guard, item->ref_count);
557
558
    if (item->guard != _CUPS_STR_GUARD)
559
      abort();
560
#endif /* DEBUG_GUARDS */
561
562
588k
    cupsMutexUnlock(&sp_mutex);
563
564
588k
    return (item->str);
565
588k
  }
566
567
 /*
568
  * Not found, so allocate a new one...
569
  */
570
571
84.4k
  slen = strlen(s);
572
84.4k
  item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
573
84.4k
  if (!item)
574
0
  {
575
0
    cupsMutexUnlock(&sp_mutex);
576
577
0
    return (NULL);
578
0
  }
579
580
84.4k
  item->ref_count = 1;
581
84.4k
  memcpy(item->str, s, slen + 1);
582
583
#ifdef DEBUG_GUARDS
584
  item->guard = _CUPS_STR_GUARD;
585
586
  DEBUG_printf("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, ref_count=%d", item, item->str, s, item->guard, item->ref_count);
587
#endif /* DEBUG_GUARDS */
588
589
 /*
590
  * Add the string to the pool and return it...
591
  */
592
593
84.4k
  cupsArrayAdd(stringpool, item);
594
595
84.4k
  cupsMutexUnlock(&sp_mutex);
596
597
84.4k
  return (item->str);
598
84.4k
}
599
600
601
/*
602
 * '_cupsStrDate()' - Return a localized date for a given time value.
603
 *
604
 * This function works around the locale encoding issues of strftime...
605
 */
606
607
char *          /* O - Buffer */
608
_cupsStrDate(char   *buf,   /* I - Buffer */
609
             size_t bufsize,    /* I - Size of buffer */
610
       time_t timeval)    /* I - Time value */
611
0
{
612
0
  struct tm date;     /* Local date/time */
613
0
  char    temp[1024];   /* Temporary buffer */
614
0
  _cups_globals_t *cg = _cupsGlobals(); /* Per-thread globals */
615
616
617
0
  if (!cg->lang_default)
618
0
    cg->lang_default = cupsLangDefault();
619
620
0
  localtime_r(&timeval, &date);
621
622
0
  if (cg->lang_default->encoding != CUPS_UTF8)
623
0
  {
624
0
    strftime(temp, sizeof(temp), "%c", &date);
625
0
    cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding);
626
0
  }
627
0
  else
628
0
    strftime(buf, bufsize, "%c", &date);
629
630
0
  return (buf);
631
0
}
632
633
634
/*
635
 * '_cupsStrFlush()' - Flush the string pool.
636
 */
637
638
void
639
_cupsStrFlush(void)
640
0
{
641
0
  _cups_sp_item_t *item;    /* Current item */
642
643
644
0
  DEBUG_printf("4_cupsStrFlush: %d strings in array", cupsArrayCount(stringpool));
645
646
0
  cupsMutexLock(&sp_mutex);
647
648
0
  for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
649
0
       item;
650
0
       item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
651
0
    free(item);
652
653
0
  cupsArrayDelete(stringpool);
654
0
  stringpool = NULL;
655
656
0
  cupsMutexUnlock(&sp_mutex);
657
0
}
658
659
660
/*
661
 * '_cupsStrFormatd()' - Format a floating-point number.
662
 */
663
664
char *          /* O - Pointer to end of string */
665
_cupsStrFormatd(char         *buf,  /* I - String */
666
                char         *bufend, /* I - End of string buffer */
667
    double       number,  /* I - Number to format */
668
                struct lconv *loc)  /* I - Locale data */
669
0
{
670
0
  char    *bufptr,    /* Pointer into buffer */
671
0
    temp[1024],   /* Temporary string */
672
0
    *tempdec,   /* Pointer to decimal point */
673
0
    *tempptr;   /* Pointer into temporary string */
674
0
  const char  *dec;     /* Decimal point */
675
0
  int   declen;     /* Length of decimal point */
676
677
678
 /*
679
  * Format the number using the "%.12f" format and then eliminate
680
  * unnecessary trailing 0's.
681
  */
682
683
0
  snprintf(temp, sizeof(temp), "%.12f", number);
684
0
  for (tempptr = temp + strlen(temp) - 1;
685
0
       tempptr > temp && *tempptr == '0';
686
0
       *tempptr-- = '\0');
687
688
 /*
689
  * Next, find the decimal point...
690
  */
691
692
0
  if (loc && loc->decimal_point)
693
0
  {
694
0
    dec    = loc->decimal_point;
695
0
    declen = (int)strlen(dec);
696
0
  }
697
0
  else
698
0
  {
699
0
    dec    = ".";
700
0
    declen = 1;
701
0
  }
702
703
0
  if (declen == 1)
704
0
    tempdec = strchr(temp, *dec);
705
0
  else
706
0
    tempdec = strstr(temp, dec);
707
708
 /*
709
  * Copy everything up to the decimal point...
710
  */
711
712
0
  if (tempdec)
713
0
  {
714
0
    for (tempptr = temp, bufptr = buf;
715
0
         tempptr < tempdec && bufptr < bufend;
716
0
   *bufptr++ = *tempptr++);
717
718
0
    tempptr += declen;
719
720
0
    if (*tempptr && bufptr < bufend)
721
0
    {
722
0
      *bufptr++ = '.';
723
724
0
      while (*tempptr && bufptr < bufend)
725
0
        *bufptr++ = *tempptr++;
726
0
    }
727
728
0
    *bufptr = '\0';
729
0
  }
730
0
  else
731
0
  {
732
0
    cupsCopyString(buf, temp, (size_t)(bufend - buf + 1));
733
0
    bufptr = buf + strlen(buf);
734
0
  }
735
736
0
  return (bufptr);
737
0
}
738
739
740
/*
741
 * '_cupsStrFree()' - Free/dereference a string.
742
 */
743
744
void
745
_cupsStrFree(const char *s)   /* I - String to free */
746
762k
{
747
762k
  _cups_sp_item_t *item,    /* String pool item */
748
762k
      *key;   /* Search key */
749
750
751
 /*
752
  * Range check input...
753
  */
754
755
762k
  if (!s)
756
100k
    return;
757
758
 /*
759
  * See if the string is already in the pool...
760
  */
761
762
661k
  cupsMutexLock(&sp_mutex);
763
764
661k
  key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
765
766
661k
  if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL && item == key)
767
661k
  {
768
   /*
769
    * Found it, dereference...
770
    */
771
772
#ifdef DEBUG_GUARDS
773
    if (key->guard != _CUPS_STR_GUARD)
774
    {
775
      DEBUG_printf("5_cupsStrFree: Freeing string %p(%s), guard=%08x, ref_count=%d", key, key->str, key->guard, key->ref_count);
776
      abort();
777
    }
778
#endif /* DEBUG_GUARDS */
779
780
661k
    item->ref_count --;
781
782
661k
    if (!item->ref_count)
783
84.4k
    {
784
     /*
785
      * Remove and free...
786
      */
787
788
84.4k
      cupsArrayRemove(stringpool, item);
789
790
84.4k
      free(item);
791
84.4k
    }
792
661k
  }
793
794
661k
  cupsMutexUnlock(&sp_mutex);
795
661k
}
796
797
798
/*
799
 * '_cupsStrRetain()' - Increment the reference count of a string.
800
 *
801
 * Note: This function does not verify that the passed pointer is in the
802
 *       string pool, so any calls to it MUST know they are passing in a
803
 *       good pointer.
804
 */
805
806
char *          /* O - Pointer to string */
807
_cupsStrRetain(const char *s)   /* I - String to retain */
808
0
{
809
0
  _cups_sp_item_t *item;    /* Pointer to string pool item */
810
811
812
0
  if (s)
813
0
  {
814
0
    item = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
815
816
0
    cupsMutexLock(&sp_mutex);
817
818
#ifdef DEBUG_GUARDS
819
    if (item->guard != _CUPS_STR_GUARD)
820
    {
821
      DEBUG_printf("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, ref_count=%d", item, s, item->guard, item->ref_count);
822
      abort();
823
    }
824
#endif /* DEBUG_GUARDS */
825
826
0
    item->ref_count ++;
827
828
0
    cupsMutexUnlock(&sp_mutex);
829
0
  }
830
831
0
  return ((char *)s);
832
0
}
833
834
835
/*
836
 * '_cupsStrScand()' - Scan a string for a floating-point number.
837
 *
838
 * This function handles the locale-specific BS so that a decimal
839
 * point is always the period (".")...
840
 */
841
842
double          /* O - Number */
843
_cupsStrScand(const char   *buf,  /* I - Pointer to number */
844
              char         **bufptr,  /* O - New pointer or NULL on error */
845
              struct lconv *loc)  /* I - Locale data */
846
174k
{
847
174k
  char  temp[1024],     /* Temporary buffer */
848
174k
  *tempptr;     /* Pointer into temporary buffer */
849
850
851
 /*
852
  * Range check input...
853
  */
854
855
174k
  if (!buf)
856
0
    return (0.0);
857
858
 /*
859
  * Skip leading whitespace...
860
  */
861
862
202k
  while (_cups_isspace(*buf))
863
27.4k
    buf ++;
864
865
 /*
866
  * Copy leading sign, numbers, period, and then numbers...
867
  */
868
869
174k
  tempptr = temp;
870
174k
  if (*buf == '-' || *buf == '+')
871
10.1k
    *tempptr++ = *buf++;
872
873
174k
  while (isdigit(*buf & 255))
874
166k
    if (tempptr < (temp + sizeof(temp) - 1))
875
166k
      *tempptr++ = *buf++;
876
1
    else
877
1
    {
878
1
      if (bufptr)
879
1
  *bufptr = NULL;
880
881
1
      return (0.0);
882
1
    }
883
884
174k
  if (*buf == '.')
885
8.79k
  {
886
   /*
887
    * Read fractional portion of number...
888
    */
889
890
8.79k
    buf ++;
891
892
8.79k
    if (loc && loc->decimal_point)
893
8.79k
    {
894
8.79k
      cupsCopyString(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp));
895
8.79k
      tempptr += strlen(tempptr);
896
8.79k
    }
897
0
    else if (tempptr < (temp + sizeof(temp) - 1))
898
0
      *tempptr++ = '.';
899
0
    else
900
0
    {
901
0
      if (bufptr)
902
0
        *bufptr = NULL;
903
904
0
      return (0.0);
905
0
    }
906
907
8.79k
    while (isdigit(*buf & 255))
908
8.51k
      if (tempptr < (temp + sizeof(temp) - 1))
909
8.51k
  *tempptr++ = *buf++;
910
1
      else
911
1
      {
912
1
  if (bufptr)
913
1
    *bufptr = NULL;
914
915
1
  return (0.0);
916
1
      }
917
8.79k
  }
918
919
174k
  if (*buf == 'e' || *buf == 'E')
920
15.0k
  {
921
   /*
922
    * Read exponent...
923
    */
924
925
15.0k
    if (tempptr < (temp + sizeof(temp) - 1))
926
15.0k
      *tempptr++ = *buf++;
927
1
    else
928
1
    {
929
1
      if (bufptr)
930
1
  *bufptr = NULL;
931
932
1
      return (0.0);
933
1
    }
934
935
15.0k
    if (*buf == '+' || *buf == '-')
936
3.22k
    {
937
3.22k
      if (tempptr < (temp + sizeof(temp) - 1))
938
3.22k
  *tempptr++ = *buf++;
939
1
      else
940
1
      {
941
1
  if (bufptr)
942
1
    *bufptr = NULL;
943
944
1
  return (0.0);
945
1
      }
946
3.22k
    }
947
948
15.0k
    while (isdigit(*buf & 255))
949
14.8k
      if (tempptr < (temp + sizeof(temp) - 1))
950
14.8k
  *tempptr++ = *buf++;
951
1
      else
952
1
      {
953
1
  if (bufptr)
954
1
    *bufptr = NULL;
955
956
1
  return (0.0);
957
1
      }
958
15.0k
  }
959
960
 /*
961
  * Nul-terminate the temporary string and return the value...
962
  */
963
964
174k
  if (bufptr)
965
157k
    *bufptr = (char *)buf;
966
967
174k
  *tempptr = '\0';
968
969
174k
  return (atof(temp));
970
174k
}
971
972
973
/*
974
 * '_cupsStrStatistics()' - Return allocation statistics for string pool.
975
 */
976
977
size_t          /* O - Number of strings */
978
_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
979
                   size_t *total_bytes) /* O - Total string bytes */
980
0
{
981
0
  size_t    count,    /* Number of strings */
982
0
      abytes,   /* Allocated string bytes */
983
0
      tbytes,   /* Total string bytes */
984
0
      len;    /* Length of string */
985
0
  _cups_sp_item_t *item;    /* Current item */
986
987
988
 /*
989
  * Loop through strings in pool, counting everything up...
990
  */
991
992
0
  cupsMutexLock(&sp_mutex);
993
994
0
  for (count = 0, abytes = 0, tbytes = 0,
995
0
           item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
996
0
       item;
997
0
       item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
998
0
  {
999
   /*
1000
    * Count allocated memory, using a 64-bit aligned buffer as a basis.
1001
    */
1002
1003
0
    count  += item->ref_count;
1004
0
    len    = (strlen(item->str) + 8) & (size_t)~7;
1005
0
    abytes += sizeof(_cups_sp_item_t) + len;
1006
0
    tbytes += item->ref_count * len;
1007
0
  }
1008
1009
0
  cupsMutexUnlock(&sp_mutex);
1010
1011
 /*
1012
  * Return values...
1013
  */
1014
1015
0
  if (alloc_bytes)
1016
0
    *alloc_bytes = abytes;
1017
1018
0
  if (total_bytes)
1019
0
    *total_bytes = tbytes;
1020
1021
0
  return (count);
1022
0
}
1023
1024
1025
/*
1026
 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
1027
 */
1028
1029
void
1030
_cups_strcpy(char       *dst,   /* I - Destination string */
1031
             const char *src)   /* I - Source string */
1032
147k
{
1033
1.23M
  while (*src)
1034
1.09M
    *dst++ = *src++;
1035
1036
147k
  *dst = '\0';
1037
147k
}
1038
1039
1040
/*
1041
 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
1042
 */
1043
1044
int       /* O - Result of comparison (-1, 0, or 1) */
1045
_cups_strcasecmp(const char *s, /* I - First string */
1046
                 const char *t) /* I - Second string */
1047
313M
{
1048
313M
  int diff;
1049
1050
1051
1.11G
  while (*s != '\0' && *t != '\0')
1052
810M
  {
1053
810M
    diff = _cups_tolower(*s) - _cups_tolower(*t);
1054
1055
810M
    if (diff < 0)
1056
5.01M
      return (-1);
1057
805M
    else if (diff > 0)
1058
2.62M
      return (1);
1059
1060
803M
    s ++;
1061
803M
    t ++;
1062
803M
  }
1063
1064
305M
  if (*s == '\0' && *t == '\0')
1065
298M
    return (0);
1066
7.02M
  else if (*s != '\0')
1067
4.02M
    return (1);
1068
3.00M
  else
1069
3.00M
    return (-1);
1070
305M
}
1071
1072
1073
/*
1074
 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
1075
 */
1076
1077
int         /* O - Result of comparison (-1, 0, or 1) */
1078
_cups_strncasecmp(const char *s,  /* I - First string */
1079
                  const char *t,  /* I - Second string */
1080
      size_t     n)   /* I - Maximum number of characters to compare */
1081
1.97M
{
1082
1.97M
  int diff;
1083
1084
1085
3.44M
  while (*s != '\0' && *t != '\0' && n > 0)
1086
2.16M
  {
1087
2.16M
    diff = _cups_tolower(*s) - _cups_tolower(*t);
1088
2.16M
    if (diff < 0)
1089
475k
      return (-1);
1090
1.69M
    else if (diff > 0)
1091
221k
      return (1);
1092
1093
1.47M
    s ++;
1094
1.47M
    t ++;
1095
1.47M
    n --;
1096
1.47M
  }
1097
1098
1.27M
  if (n == 0)
1099
35.1k
    return (0);
1100
1.23M
  else if (*s == '\0' && *t == '\0')
1101
0
    return (0);
1102
1.23M
  else if (*s != '\0')
1103
0
    return (1);
1104
1.23M
  else
1105
1.23M
    return (-1);
1106
1.27M
}
1107
1108
1109
/*
1110
 * 'compare_sp_items()' - Compare two string pool items...
1111
 */
1112
1113
static int        /* O - Result of comparison */
1114
compare_sp_items(_cups_sp_item_t *a,  /* I - First item */
1115
                 _cups_sp_item_t *b,  /* I - Second item */
1116
                 void            *data)         /* I - Unused */
1117
3.39M
{
1118
3.39M
  (void)data;
1119
 
1120
3.39M
 return (strcmp(a->str, b->str));
1121
3.39M
}
1122
1123
1124
//
1125
// 'validate_end()' - Validate the last UTF-8 character in a buffer.
1126
//
1127
1128
static void
1129
validate_end(char *s,     // I - Pointer to start of string
1130
             char *end)     // I - Pointer to end of string
1131
48.9k
{
1132
48.9k
  char *ptr = end - 1;      // Pointer into string
1133
1134
1135
48.9k
  if (ptr > s && *ptr & 0x80)
1136
11.0k
  {
1137
47.1k
    while ((*ptr & 0xc0) == 0x80 && ptr > s)
1138
36.1k
      ptr --;
1139
1140
11.0k
    if ((*ptr & 0xe0) == 0xc0)
1141
2.72k
    {
1142
      // Verify 2-byte UTF-8 sequence...
1143
2.72k
      if ((end - ptr) != 2)
1144
2.21k
        *ptr = '\0';
1145
2.72k
    }
1146
8.29k
    else if ((*ptr & 0xf0) == 0xe0)
1147
2.08k
    {
1148
      // Verify 3-byte UTF-8 sequence...
1149
2.08k
      if ((end - ptr) != 3)
1150
1.06k
        *ptr = '\0';
1151
2.08k
    }
1152
6.21k
    else if ((*ptr & 0xf8) == 0xf0)
1153
1.76k
    {
1154
      // Verify 4-byte UTF-8 sequence...
1155
1.76k
      if ((end - ptr) != 4)
1156
1.02k
        *ptr = '\0';
1157
1.76k
    }
1158
4.44k
    else if (*ptr & 0x80)
1159
2.90k
    {
1160
      // Invalid sequence at end...
1161
2.90k
      *ptr = '\0';
1162
2.90k
    }
1163
11.0k
  }
1164
48.9k
}
1165
1166
1167
/*
1168
 * '_cupsArrayStrcasecmp()' - Compare two strings...
1169
 */
1170
1171
int /* O - Result of comparison */
1172
_cupsArrayStrcasecmp(const char *s, /* I - First string */
1173
                         const char *t, /* I - Second string */
1174
                         void *data)    /* I - Unused */
1175
0
{
1176
0
  (void)data;
1177
0
  return (_cups_strcasecmp(s, t));
1178
0
}