Coverage Report

Created: 2025-07-18 07:04

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