]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/string.c
Fix a typo in snmp.c
[thirdparty/cups.git] / cups / string.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * String functions for CUPS.
ef416fc2 3 *
7e86f2f6
MS
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 */
9
10/*
11 * Include necessary headers...
12 */
13
7cf5915e 14#define _CUPS_STRING_C_
554aa7b7 15#include "cups-private.h"
745129be 16#include <stddef.h>
4400e98d 17#include <limits.h>
d6ae789d 18
19
20/*
21 * Local globals...
22 */
23
6d2f911b 24static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 25 /* Mutex to control access to pool */
d6ae789d 26static cups_array_t *stringpool = NULL;
27 /* Global string pool */
4400e98d 28
29
30/*
31 * Local functions...
32 */
33
34static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
35
36
37/*
757d2cad 38 * '_cupsStrAlloc()' - Allocate/reference a string.
4400e98d 39 */
40
41char * /* O - String pointer */
757d2cad 42_cupsStrAlloc(const char *s) /* I - String */
4400e98d 43{
5a9febac 44 size_t slen; /* Length of string */
4400e98d 45 _cups_sp_item_t *item, /* String pool item */
426c6a59 46 *key; /* Search key */
4400e98d 47
48
49 /*
50 * Range check input...
51 */
52
53 if (!s)
54 return (NULL);
55
56 /*
57 * Get the string pool...
58 */
59
6d2f911b 60 _cupsMutexLock(&sp_mutex);
d6ae789d 61
62 if (!stringpool)
63 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
4400e98d 64
d6ae789d 65 if (!stringpool)
66 {
6d2f911b 67 _cupsMutexUnlock(&sp_mutex);
4400e98d 68
4400e98d 69 return (NULL);
d6ae789d 70 }
4400e98d 71
72 /*
73 * See if the string is already in the pool...
74 */
75
745129be 76 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
4400e98d 77
426c6a59 78 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
4400e98d 79 {
80 /*
81 * Found it, return the cached string...
82 */
83
84 item->ref_count ++;
85
745129be 86#ifdef DEBUG_GUARDS
e07d4801
MS
87 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
88 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
89 item->ref_count));
90
91 if (item->guard != _CUPS_STR_GUARD)
92 abort();
93#endif /* DEBUG_GUARDS */
94
6d2f911b 95 _cupsMutexUnlock(&sp_mutex);
d6ae789d 96
4400e98d 97 return (item->str);
98 }
99
100 /*
101 * Not found, so allocate a new one...
102 */
103
5a9febac
MS
104 slen = strlen(s);
105 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
4400e98d 106 if (!item)
d6ae789d 107 {
6d2f911b 108 _cupsMutexUnlock(&sp_mutex);
d6ae789d 109
4400e98d 110 return (NULL);
d6ae789d 111 }
4400e98d 112
113 item->ref_count = 1;
5a9febac 114 memcpy(item->str, s, slen + 1);
4400e98d 115
745129be
MS
116#ifdef DEBUG_GUARDS
117 item->guard = _CUPS_STR_GUARD;
118
e07d4801
MS
119 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
120 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
121 item->ref_count));
122#endif /* DEBUG_GUARDS */
123
4400e98d 124 /*
125 * Add the string to the pool and return it...
126 */
127
d6ae789d 128 cupsArrayAdd(stringpool, item);
129
6d2f911b 130 _cupsMutexUnlock(&sp_mutex);
4400e98d 131
132 return (item->str);
133}
134
135
554aa7b7
MS
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
142char * /* 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
4400e98d 169/*
426c6a59 170 * '_cupsStrFlush()' - Flush the string pool.
4400e98d 171 */
172
173void
d6ae789d 174_cupsStrFlush(void)
4400e98d 175{
176 _cups_sp_item_t *item; /* Current item */
177
178
e07d4801 179 DEBUG_printf(("4_cupsStrFlush: %d strings in array",
ae71f5de 180 cupsArrayCount(stringpool)));
d6ae789d 181
6d2f911b 182 _cupsMutexLock(&sp_mutex);
d6ae789d 183
184 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 185 item;
d6ae789d 186 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 187 free(item);
4400e98d 188
d6ae789d 189 cupsArrayDelete(stringpool);
190 stringpool = NULL;
191
6d2f911b 192 _cupsMutexUnlock(&sp_mutex);
4400e98d 193}
194
195
196/*
757d2cad 197 * '_cupsStrFormatd()' - Format a floating-point number.
198 */
199
200char * /* 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;
b86bc4cf 231 declen = (int)strlen(dec);
757d2cad 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
7594b224 254 tempptr += declen;
757d2cad 255
7594b224 256 if (*tempptr && bufptr < bufend)
757d2cad 257 {
258 *bufptr++ = '.';
259
260 while (*tempptr && bufptr < bufend)
261 *bufptr++ = *tempptr++;
262 }
263
264 *bufptr = '\0';
265 }
266 else
267 {
07623986 268 strlcpy(buf, temp, (size_t)(bufend - buf + 1));
757d2cad 269 bufptr = buf + strlen(buf);
270 }
271
272 return (bufptr);
273}
274
275
276/*
277 * '_cupsStrFree()' - Free/dereference a string.
4400e98d 278 */
279
280void
757d2cad 281_cupsStrFree(const char *s) /* I - String to free */
4400e98d 282{
4400e98d 283 _cups_sp_item_t *item, /* String pool item */
426c6a59 284 *key; /* Search key */
4400e98d 285
286
287 /*
288 * Range check input...
289 */
290
291 if (!s)
292 return;
293
294 /*
d6ae789d 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...
4400e98d 300 */
301
d6ae789d 302 if (!stringpool)
4400e98d 303 return;
304
305 /*
306 * See if the string is already in the pool...
307 */
308
6d2f911b 309 _cupsMutexLock(&sp_mutex);
d6ae789d 310
745129be
MS
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 {
e07d4801
MS
316 DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, "
317 "ref_count=%d", key, key->str, key->guard, key->ref_count));
745129be
MS
318 abort();
319 }
320#endif /* DEBUG_GUARDS */
4400e98d 321
426c6a59
MS
322 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
323 item == key)
4400e98d 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
d6ae789d 337 cupsArrayRemove(stringpool, item);
4400e98d 338
4400e98d 339 free(item);
340 }
341 }
d6ae789d 342
6d2f911b 343 _cupsMutexUnlock(&sp_mutex);
4400e98d 344}
345
346
426c6a59
MS
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
355char * /* O - Pointer to string */
e07d4801 356_cupsStrRetain(const char *s) /* I - String to retain */
426c6a59
MS
357{
358 _cups_sp_item_t *item; /* Pointer to string pool item */
359
360
361 if (s)
362 {
745129be
MS
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 {
e07d4801
MS
368 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
369 "ref_count=%d", item, s, item->guard, item->ref_count));
745129be
MS
370 abort();
371 }
372#endif /* DEBUG_GUARDS */
426c6a59 373
6d2f911b 374 _cupsMutexLock(&sp_mutex);
426c6a59
MS
375
376 item->ref_count ++;
377
6d2f911b 378 _cupsMutexUnlock(&sp_mutex);
426c6a59
MS
379 }
380
e07d4801 381 return ((char *)s);
426c6a59
MS
382}
383
384
4400e98d 385/*
757d2cad 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
392double /* 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
7cf5915e 412 while (_cups_isspace(*buf))
757d2cad 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 {
e1d6a774 436 /*
437 * Read fractional portion of number...
438 */
439
440 buf ++;
441
757d2cad 442 if (loc && loc->decimal_point)
443 {
7e86f2f6 444 strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp));
757d2cad 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
b86bc4cf 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
757d2cad 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.
4400e98d 525 */
526
527size_t /* O - Number of strings */
757d2cad 528_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
529 size_t *total_bytes) /* O - Total string bytes */
4400e98d 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 */
4400e98d 536
537
538 /*
539 * Loop through strings in pool, counting everything up...
540 */
541
6d2f911b 542 _cupsMutexLock(&sp_mutex);
4400e98d 543
544 for (count = 0, abytes = 0, tbytes = 0,
d6ae789d 545 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 546 item;
d6ae789d 547 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 548 {
549 /*
550 * Count allocated memory, using a 64-bit aligned buffer as a basis.
551 */
552
553 count += item->ref_count;
7e86f2f6 554 len = (strlen(item->str) + 8) & (size_t)~7;
4400e98d 555 abytes += sizeof(_cups_sp_item_t) + len;
556 tbytes += item->ref_count * len;
557 }
558
6d2f911b 559 _cupsMutexUnlock(&sp_mutex);
d6ae789d 560
4400e98d 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}
ef416fc2 573
574
575/*
576 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
577 */
578
579void
580_cups_strcpy(char *dst, /* I - Destination string */
88f9aafc 581 const char *src) /* I - Source string */
ef416fc2 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
595char * /* O - New string pointer */
596_cups_strdup(const char *s) /* I - String to duplicate */
597{
5a9febac
MS
598 char *t; /* New string pointer */
599 size_t slen; /* Length of string */
ef416fc2 600
601
5a9febac 602 if (!s)
ef416fc2 603 return (NULL);
604
5a9febac
MS
605 slen = strlen(s);
606 if ((t = malloc(slen + 1)) == NULL)
ef416fc2 607 return (NULL);
608
5a9febac 609 return (memcpy(t, s, slen + 1));
ef416fc2 610}
611#endif /* !HAVE_STRDUP */
612
613
614/*
615 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
616 */
617
ef416fc2 618int /* O - Result of comparison (-1, 0, or 1) */
619_cups_strcasecmp(const char *s, /* I - First string */
88f9aafc 620 const char *t) /* I - Second string */
ef416fc2 621{
622 while (*s != '\0' && *t != '\0')
623 {
88f9aafc 624 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 625 return (-1);
88f9aafc 626 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 640
641/*
642 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
643 */
644
ef416fc2 645int /* O - Result of comparison (-1, 0, or 1) */
646_cups_strncasecmp(const char *s, /* I - First string */
0a682745 647 const char *t, /* I - Second string */
ef416fc2 648 size_t n) /* I - Maximum number of characters to compare */
649{
650 while (*s != '\0' && *t != '\0' && n > 0)
651 {
88f9aafc 652 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 653 return (-1);
88f9aafc 654 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 671
672
673#ifndef HAVE_STRLCAT
674/*
675 * '_cups_strlcat()' - Safely concatenate two strings.
676 */
677
678size_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);
ef416fc2 692
e38ab40b
MS
693 if (size < (dstlen + 1))
694 return (dstlen); /* No room, return immediately... */
695
696 size -= dstlen + 1;
ef416fc2 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
7e86f2f6 711 memmove(dst + dstlen, src, srclen);
ef416fc2 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
724size_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
7e86f2f6 747 memmove(dst, src, srclen);
ef416fc2 748 dst[srclen] = '\0';
749
750 return (srclen);
751}
752#endif /* !HAVE_STRLCPY */
753
754
755/*
4400e98d 756 * 'compare_sp_items()' - Compare two string pool items...
757 */
758
759static int /* O - Result of comparison */
760compare_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}