1 /*
2 * String functions for CUPS.
3 *
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #define _CUPS_STRING_C_
16 #include "cups-private.h"
17 #include "debug-internal.h"
18 #include <stddef.h>
19 #include <limits.h>
20
21
22 /*
23 * Local globals...
24 */
25
26 static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER;
27 /* Mutex to control access to pool */
28 static cups_array_t *stringpool = NULL;
29 /* Global string pool */
30
31
32 /*
33 * Local functions...
34 */
35
36 static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
37
38
39 /*
40 * '_cupsStrAlloc()' - Allocate/reference a string.
41 */
42
43 char * /* O - String pointer */
_cupsStrAlloc(const char * s)44 _cupsStrAlloc(const char *s) /* I - String */
45 {
46 size_t slen; /* Length of string */
47 _cups_sp_item_t *item, /* String pool item */
48 *key; /* Search key */
49
50
51 /*
52 * Range check input...
53 */
54
55 if (!s)
56 return (NULL);
57
58 /*
59 * Get the string pool...
60 */
61
62 _cupsMutexLock(&sp_mutex);
63
64 if (!stringpool)
65 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
66
67 if (!stringpool)
68 {
69 _cupsMutexUnlock(&sp_mutex);
70
71 return (NULL);
72 }
73
74 /*
75 * See if the string is already in the pool...
76 */
77
78 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
79
80 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
81 {
82 /*
83 * Found it, return the cached string...
84 */
85
86 item->ref_count ++;
87
88 #ifdef DEBUG_GUARDS
89 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
90 "ref_count=%d", item, item->str, s, item->guard,
91 item->ref_count));
92
93 if (item->guard != _CUPS_STR_GUARD)
94 abort();
95 #endif /* DEBUG_GUARDS */
96
97 _cupsMutexUnlock(&sp_mutex);
98
99 return (item->str);
100 }
101
102 /*
103 * Not found, so allocate a new one...
104 */
105
106 slen = strlen(s);
107 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
108 if (!item)
109 {
110 _cupsMutexUnlock(&sp_mutex);
111
112 return (NULL);
113 }
114
115 item->ref_count = 1;
116 memcpy(item->str, s, slen + 1);
117
118 #ifdef DEBUG_GUARDS
119 item->guard = _CUPS_STR_GUARD;
120
121 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
122 "ref_count=%d", item, item->str, s, item->guard,
123 item->ref_count));
124 #endif /* DEBUG_GUARDS */
125
126 /*
127 * Add the string to the pool and return it...
128 */
129
130 cupsArrayAdd(stringpool, item);
131
132 _cupsMutexUnlock(&sp_mutex);
133
134 return (item->str);
135 }
136
137
138 /*
139 * '_cupsStrDate()' - Return a localized date for a given time value.
140 *
141 * This function works around the locale encoding issues of strftime...
142 */
143
144 char * /* O - Buffer */
_cupsStrDate(char * buf,size_t bufsize,time_t timeval)145 _cupsStrDate(char *buf, /* I - Buffer */
146 size_t bufsize, /* I - Size of buffer */
147 time_t timeval) /* I - Time value */
148 {
149 struct tm date; /* Local date/time */
150 char temp[1024]; /* Temporary buffer */
151 _cups_globals_t *cg = _cupsGlobals(); /* Per-thread globals */
152
153
154 if (!cg->lang_default)
155 cg->lang_default = cupsLangDefault();
156
157 localtime_r(&timeval, &date);
158
159 if (cg->lang_default->encoding != CUPS_UTF8)
160 {
161 strftime(temp, sizeof(temp), "%c", &date);
162 cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding);
163 }
164 else
165 strftime(buf, bufsize, "%c", &date);
166
167 return (buf);
168 }
169
170
171 /*
172 * '_cupsStrFlush()' - Flush the string pool.
173 */
174
175 void
_cupsStrFlush(void)176 _cupsStrFlush(void)
177 {
178 _cups_sp_item_t *item; /* Current item */
179
180
181 DEBUG_printf(("4_cupsStrFlush: %d strings in array",
182 cupsArrayCount(stringpool)));
183
184 _cupsMutexLock(&sp_mutex);
185
186 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
187 item;
188 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
189 free(item);
190
191 cupsArrayDelete(stringpool);
192 stringpool = NULL;
193
194 _cupsMutexUnlock(&sp_mutex);
195 }
196
197
198 /*
199 * '_cupsStrFormatd()' - Format a floating-point number.
200 */
201
202 char * /* O - Pointer to end of string */
_cupsStrFormatd(char * buf,char * bufend,double number,struct lconv * loc)203 _cupsStrFormatd(char *buf, /* I - String */
204 char *bufend, /* I - End of string buffer */
205 double number, /* I - Number to format */
206 struct lconv *loc) /* I - Locale data */
207 {
208 char *bufptr, /* Pointer into buffer */
209 temp[1024], /* Temporary string */
210 *tempdec, /* Pointer to decimal point */
211 *tempptr; /* Pointer into temporary string */
212 const char *dec; /* Decimal point */
213 int declen; /* Length of decimal point */
214
215
216 /*
217 * Format the number using the "%.12f" format and then eliminate
218 * unnecessary trailing 0's.
219 */
220
221 snprintf(temp, sizeof(temp), "%.12f", number);
222 for (tempptr = temp + strlen(temp) - 1;
223 tempptr > temp && *tempptr == '0';
224 *tempptr-- = '\0');
225
226 /*
227 * Next, find the decimal point...
228 */
229
230 if (loc && loc->decimal_point)
231 {
232 dec = loc->decimal_point;
233 declen = (int)strlen(dec);
234 }
235 else
236 {
237 dec = ".";
238 declen = 1;
239 }
240
241 if (declen == 1)
242 tempdec = strchr(temp, *dec);
243 else
244 tempdec = strstr(temp, dec);
245
246 /*
247 * Copy everything up to the decimal point...
248 */
249
250 if (tempdec)
251 {
252 for (tempptr = temp, bufptr = buf;
253 tempptr < tempdec && bufptr < bufend;
254 *bufptr++ = *tempptr++);
255
256 tempptr += declen;
257
258 if (*tempptr && bufptr < bufend)
259 {
260 *bufptr++ = '.';
261
262 while (*tempptr && bufptr < bufend)
263 *bufptr++ = *tempptr++;
264 }
265
266 *bufptr = '\0';
267 }
268 else
269 {
270 strlcpy(buf, temp, (size_t)(bufend - buf + 1));
271 bufptr = buf + strlen(buf);
272 }
273
274 return (bufptr);
275 }
276
277
278 /*
279 * '_cupsStrFree()' - Free/dereference a string.
280 */
281
282 void
_cupsStrFree(const char * s)283 _cupsStrFree(const char *s) /* I - String to free */
284 {
285 _cups_sp_item_t *item, /* String pool item */
286 *key; /* Search key */
287
288
289 /*
290 * Range check input...
291 */
292
293 if (!s)
294 return;
295
296 /*
297 * Check the string pool...
298 *
299 * We don't need to lock the mutex yet, as we only want to know if
300 * the stringpool is initialized. The rest of the code will still
301 * work if it is initialized before we lock...
302 */
303
304 if (!stringpool)
305 return;
306
307 /*
308 * See if the string is already in the pool...
309 */
310
311 _cupsMutexLock(&sp_mutex);
312
313 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
314
315 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
316 item == key)
317 {
318 /*
319 * Found it, dereference...
320 */
321
322 #ifdef DEBUG_GUARDS
323 if (key->guard != _CUPS_STR_GUARD)
324 {
325 DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, ref_count=%d", key, key->str, key->guard, key->ref_count));
326 abort();
327 }
328 #endif /* DEBUG_GUARDS */
329
330 item->ref_count --;
331
332 if (!item->ref_count)
333 {
334 /*
335 * Remove and free...
336 */
337
338 cupsArrayRemove(stringpool, item);
339
340 free(item);
341 }
342 }
343
344 _cupsMutexUnlock(&sp_mutex);
345 }
346
347
348 /*
349 * '_cupsStrRetain()' - Increment the reference count of a string.
350 *
351 * Note: This function does not verify that the passed pointer is in the
352 * string pool, so any calls to it MUST know they are passing in a
353 * good pointer.
354 */
355
356 char * /* O - Pointer to string */
_cupsStrRetain(const char * s)357 _cupsStrRetain(const char *s) /* I - String to retain */
358 {
359 _cups_sp_item_t *item; /* Pointer to string pool item */
360
361
362 if (s)
363 {
364 item = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
365
366 #ifdef DEBUG_GUARDS
367 if (item->guard != _CUPS_STR_GUARD)
368 {
369 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
370 "ref_count=%d", item, s, item->guard, item->ref_count));
371 abort();
372 }
373 #endif /* DEBUG_GUARDS */
374
375 _cupsMutexLock(&sp_mutex);
376
377 item->ref_count ++;
378
379 _cupsMutexUnlock(&sp_mutex);
380 }
381
382 return ((char *)s);
383 }
384
385
386 /*
387 * '_cupsStrScand()' - Scan a string for a floating-point number.
388 *
389 * This function handles the locale-specific BS so that a decimal
390 * point is always the period (".")...
391 */
392
393 double /* O - Number */
_cupsStrScand(const char * buf,char ** bufptr,struct lconv * loc)394 _cupsStrScand(const char *buf, /* I - Pointer to number */
395 char **bufptr, /* O - New pointer or NULL on error */
396 struct lconv *loc) /* I - Locale data */
397 {
398 char temp[1024], /* Temporary buffer */
399 *tempptr; /* Pointer into temporary buffer */
400
401
402 /*
403 * Range check input...
404 */
405
406 if (!buf)
407 return (0.0);
408
409 /*
410 * Skip leading whitespace...
411 */
412
413 while (_cups_isspace(*buf))
414 buf ++;
415
416 /*
417 * Copy leading sign, numbers, period, and then numbers...
418 */
419
420 tempptr = temp;
421 if (*buf == '-' || *buf == '+')
422 *tempptr++ = *buf++;
423
424 while (isdigit(*buf & 255))
425 if (tempptr < (temp + sizeof(temp) - 1))
426 *tempptr++ = *buf++;
427 else
428 {
429 if (bufptr)
430 *bufptr = NULL;
431
432 return (0.0);
433 }
434
435 if (*buf == '.')
436 {
437 /*
438 * Read fractional portion of number...
439 */
440
441 buf ++;
442
443 if (loc && loc->decimal_point)
444 {
445 strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp));
446 tempptr += strlen(tempptr);
447 }
448 else if (tempptr < (temp + sizeof(temp) - 1))
449 *tempptr++ = '.';
450 else
451 {
452 if (bufptr)
453 *bufptr = NULL;
454
455 return (0.0);
456 }
457
458 while (isdigit(*buf & 255))
459 if (tempptr < (temp + sizeof(temp) - 1))
460 *tempptr++ = *buf++;
461 else
462 {
463 if (bufptr)
464 *bufptr = NULL;
465
466 return (0.0);
467 }
468 }
469
470 if (*buf == 'e' || *buf == 'E')
471 {
472 /*
473 * Read exponent...
474 */
475
476 if (tempptr < (temp + sizeof(temp) - 1))
477 *tempptr++ = *buf++;
478 else
479 {
480 if (bufptr)
481 *bufptr = NULL;
482
483 return (0.0);
484 }
485
486 if (*buf == '+' || *buf == '-')
487 {
488 if (tempptr < (temp + sizeof(temp) - 1))
489 *tempptr++ = *buf++;
490 else
491 {
492 if (bufptr)
493 *bufptr = NULL;
494
495 return (0.0);
496 }
497 }
498
499 while (isdigit(*buf & 255))
500 if (tempptr < (temp + sizeof(temp) - 1))
501 *tempptr++ = *buf++;
502 else
503 {
504 if (bufptr)
505 *bufptr = NULL;
506
507 return (0.0);
508 }
509 }
510
511 /*
512 * Nul-terminate the temporary string and return the value...
513 */
514
515 if (bufptr)
516 *bufptr = (char *)buf;
517
518 *tempptr = '\0';
519
520 return (strtod(temp, NULL));
521 }
522
523
524 /*
525 * '_cupsStrStatistics()' - Return allocation statistics for string pool.
526 */
527
528 size_t /* O - Number of strings */
_cupsStrStatistics(size_t * alloc_bytes,size_t * total_bytes)529 _cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
530 size_t *total_bytes) /* O - Total string bytes */
531 {
532 size_t count, /* Number of strings */
533 abytes, /* Allocated string bytes */
534 tbytes, /* Total string bytes */
535 len; /* Length of string */
536 _cups_sp_item_t *item; /* Current item */
537
538
539 /*
540 * Loop through strings in pool, counting everything up...
541 */
542
543 _cupsMutexLock(&sp_mutex);
544
545 for (count = 0, abytes = 0, tbytes = 0,
546 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
547 item;
548 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
549 {
550 /*
551 * Count allocated memory, using a 64-bit aligned buffer as a basis.
552 */
553
554 count += item->ref_count;
555 len = (strlen(item->str) + 8) & (size_t)~7;
556 abytes += sizeof(_cups_sp_item_t) + len;
557 tbytes += item->ref_count * len;
558 }
559
560 _cupsMutexUnlock(&sp_mutex);
561
562 /*
563 * Return values...
564 */
565
566 if (alloc_bytes)
567 *alloc_bytes = abytes;
568
569 if (total_bytes)
570 *total_bytes = tbytes;
571
572 return (count);
573 }
574
575
576 /*
577 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
578 */
579
580 void
_cups_strcpy(char * dst,const char * src)581 _cups_strcpy(char *dst, /* I - Destination string */
582 const char *src) /* I - Source string */
583 {
584 while (*src)
585 *dst++ = *src++;
586
587 *dst = '\0';
588 }
589
590
591 /*
592 * '_cups_strdup()' - Duplicate a string.
593 */
594
595 #ifndef HAVE_STRDUP
596 char * /* O - New string pointer */
_cups_strdup(const char * s)597 _cups_strdup(const char *s) /* I - String to duplicate */
598 {
599 char *t; /* New string pointer */
600 size_t slen; /* Length of string */
601
602
603 if (!s)
604 return (NULL);
605
606 slen = strlen(s);
607 if ((t = malloc(slen + 1)) == NULL)
608 return (NULL);
609
610 return (memcpy(t, s, slen + 1));
611 }
612 #endif /* !HAVE_STRDUP */
613
614
615 /*
616 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
617 */
618
619 int /* O - Result of comparison (-1, 0, or 1) */
_cups_strcasecmp(const char * s,const char * t)620 _cups_strcasecmp(const char *s, /* I - First string */
621 const char *t) /* I - Second string */
622 {
623 while (*s != '\0' && *t != '\0')
624 {
625 if (_cups_tolower(*s) < _cups_tolower(*t))
626 return (-1);
627 else if (_cups_tolower(*s) > _cups_tolower(*t))
628 return (1);
629
630 s ++;
631 t ++;
632 }
633
634 if (*s == '\0' && *t == '\0')
635 return (0);
636 else if (*s != '\0')
637 return (1);
638 else
639 return (-1);
640 }
641
642 /*
643 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
644 */
645
646 int /* O - Result of comparison (-1, 0, or 1) */
_cups_strncasecmp(const char * s,const char * t,size_t n)647 _cups_strncasecmp(const char *s, /* I - First string */
648 const char *t, /* I - Second string */
649 size_t n) /* I - Maximum number of characters to compare */
650 {
651 while (*s != '\0' && *t != '\0' && n > 0)
652 {
653 if (_cups_tolower(*s) < _cups_tolower(*t))
654 return (-1);
655 else if (_cups_tolower(*s) > _cups_tolower(*t))
656 return (1);
657
658 s ++;
659 t ++;
660 n --;
661 }
662
663 if (n == 0)
664 return (0);
665 else if (*s == '\0' && *t == '\0')
666 return (0);
667 else if (*s != '\0')
668 return (1);
669 else
670 return (-1);
671 }
672
673
674 #ifndef HAVE_STRLCAT
675 /*
676 * '_cups_strlcat()' - Safely concatenate two strings.
677 */
678
679 size_t /* O - Length of string */
_cups_strlcat(char * dst,const char * src,size_t size)680 _cups_strlcat(char *dst, /* O - Destination string */
681 const char *src, /* I - Source string */
682 size_t size) /* I - Size of destination string buffer */
683 {
684 size_t srclen; /* Length of source string */
685 size_t dstlen; /* Length of destination string */
686
687
688 /*
689 * Figure out how much room is left...
690 */
691
692 dstlen = strlen(dst);
693
694 if (size < (dstlen + 1))
695 return (dstlen); /* No room, return immediately... */
696
697 size -= dstlen + 1;
698
699 /*
700 * Figure out how much room is needed...
701 */
702
703 srclen = strlen(src);
704
705 /*
706 * Copy the appropriate amount...
707 */
708
709 if (srclen > size)
710 srclen = size;
711
712 memmove(dst + dstlen, src, srclen);
713 dst[dstlen + srclen] = '\0';
714
715 return (dstlen + srclen);
716 }
717 #endif /* !HAVE_STRLCAT */
718
719
720 #ifndef HAVE_STRLCPY
721 /*
722 * '_cups_strlcpy()' - Safely copy two strings.
723 */
724
725 size_t /* O - Length of string */
_cups_strlcpy(char * dst,const char * src,size_t size)726 _cups_strlcpy(char *dst, /* O - Destination string */
727 const char *src, /* I - Source string */
728 size_t size) /* I - Size of destination string buffer */
729 {
730 size_t srclen; /* Length of source string */
731
732
733 /*
734 * Figure out how much room is needed...
735 */
736
737 size --;
738
739 srclen = strlen(src);
740
741 /*
742 * Copy the appropriate amount...
743 */
744
745 if (srclen > size)
746 srclen = size;
747
748 memmove(dst, src, srclen);
749 dst[srclen] = '\0';
750
751 return (srclen);
752 }
753 #endif /* !HAVE_STRLCPY */
754
755
756 /*
757 * 'compare_sp_items()' - Compare two string pool items...
758 */
759
760 static int /* O - Result of comparison */
compare_sp_items(_cups_sp_item_t * a,_cups_sp_item_t * b)761 compare_sp_items(_cups_sp_item_t *a, /* I - First item */
762 _cups_sp_item_t *b) /* I - Second item */
763 {
764 return (strcmp(a->str, b->str));
765 }
766