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