]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/string.c
Update usage info.
[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"
fb863569 16#include "debug-internal.h"
745129be 17#include <stddef.h>
4400e98d 18#include <limits.h>
d6ae789d 19
20
21/*
22 * Local globals...
23 */
24
6d2f911b 25static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 26 /* Mutex to control access to pool */
d6ae789d 27static cups_array_t *stringpool = NULL;
28 /* Global string pool */
4400e98d 29
30
31/*
32 * Local functions...
33 */
34
35static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
36
37
38/*
757d2cad 39 * '_cupsStrAlloc()' - Allocate/reference a string.
4400e98d 40 */
41
42char * /* O - String pointer */
757d2cad 43_cupsStrAlloc(const char *s) /* I - String */
4400e98d 44{
5a9febac 45 size_t slen; /* Length of string */
4400e98d 46 _cups_sp_item_t *item, /* String pool item */
426c6a59 47 *key; /* Search key */
4400e98d 48
49
50 /*
51 * Range check input...
52 */
53
54 if (!s)
55 return (NULL);
56
57 /*
58 * Get the string pool...
59 */
60
6d2f911b 61 _cupsMutexLock(&sp_mutex);
d6ae789d 62
63 if (!stringpool)
64 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
4400e98d 65
d6ae789d 66 if (!stringpool)
67 {
6d2f911b 68 _cupsMutexUnlock(&sp_mutex);
4400e98d 69
4400e98d 70 return (NULL);
d6ae789d 71 }
4400e98d 72
73 /*
74 * See if the string is already in the pool...
75 */
76
745129be 77 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
4400e98d 78
426c6a59 79 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
4400e98d 80 {
81 /*
82 * Found it, return the cached string...
83 */
84
85 item->ref_count ++;
86
745129be 87#ifdef DEBUG_GUARDS
e07d4801
MS
88 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
89 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
90 item->ref_count));
91
92 if (item->guard != _CUPS_STR_GUARD)
93 abort();
94#endif /* DEBUG_GUARDS */
95
6d2f911b 96 _cupsMutexUnlock(&sp_mutex);
d6ae789d 97
4400e98d 98 return (item->str);
99 }
100
101 /*
102 * Not found, so allocate a new one...
103 */
104
5a9febac
MS
105 slen = strlen(s);
106 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
4400e98d 107 if (!item)
d6ae789d 108 {
6d2f911b 109 _cupsMutexUnlock(&sp_mutex);
d6ae789d 110
4400e98d 111 return (NULL);
d6ae789d 112 }
4400e98d 113
114 item->ref_count = 1;
5a9febac 115 memcpy(item->str, s, slen + 1);
4400e98d 116
745129be
MS
117#ifdef DEBUG_GUARDS
118 item->guard = _CUPS_STR_GUARD;
119
e07d4801
MS
120 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
121 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
122 item->ref_count));
123#endif /* DEBUG_GUARDS */
124
4400e98d 125 /*
126 * Add the string to the pool and return it...
127 */
128
d6ae789d 129 cupsArrayAdd(stringpool, item);
130
6d2f911b 131 _cupsMutexUnlock(&sp_mutex);
4400e98d 132
133 return (item->str);
134}
135
136
554aa7b7
MS
137/*
138 * '_cupsStrDate()' - Return a localized date for a given time value.
139 *
140 * This function works around the locale encoding issues of strftime...
141 */
142
143char * /* O - Buffer */
144_cupsStrDate(char *buf, /* I - Buffer */
145 size_t bufsize, /* I - Size of buffer */
146 time_t timeval) /* I - Time value */
147{
148 struct tm *dateval; /* Local date/time */
149 char temp[1024]; /* Temporary buffer */
150 _cups_globals_t *cg = _cupsGlobals(); /* Per-thread globals */
151
152
153 if (!cg->lang_default)
154 cg->lang_default = cupsLangDefault();
155
156 dateval = localtime(&timeval);
157
158 if (cg->lang_default->encoding != CUPS_UTF8)
159 {
160 strftime(temp, sizeof(temp), "%c", dateval);
161 cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding);
162 }
163 else
164 strftime(buf, bufsize, "%c", dateval);
165
166 return (buf);
167}
168
169
4400e98d 170/*
426c6a59 171 * '_cupsStrFlush()' - Flush the string pool.
4400e98d 172 */
173
174void
d6ae789d 175_cupsStrFlush(void)
4400e98d 176{
177 _cups_sp_item_t *item; /* Current item */
178
179
e07d4801 180 DEBUG_printf(("4_cupsStrFlush: %d strings in array",
ae71f5de 181 cupsArrayCount(stringpool)));
d6ae789d 182
6d2f911b 183 _cupsMutexLock(&sp_mutex);
d6ae789d 184
185 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 186 item;
d6ae789d 187 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 188 free(item);
4400e98d 189
d6ae789d 190 cupsArrayDelete(stringpool);
191 stringpool = NULL;
192
6d2f911b 193 _cupsMutexUnlock(&sp_mutex);
4400e98d 194}
195
196
197/*
757d2cad 198 * '_cupsStrFormatd()' - Format a floating-point number.
199 */
200
201char * /* O - Pointer to end of string */
202_cupsStrFormatd(char *buf, /* I - String */
203 char *bufend, /* I - End of string buffer */
204 double number, /* I - Number to format */
205 struct lconv *loc) /* I - Locale data */
206{
207 char *bufptr, /* Pointer into buffer */
208 temp[1024], /* Temporary string */
209 *tempdec, /* Pointer to decimal point */
210 *tempptr; /* Pointer into temporary string */
211 const char *dec; /* Decimal point */
212 int declen; /* Length of decimal point */
213
214
215 /*
216 * Format the number using the "%.12f" format and then eliminate
217 * unnecessary trailing 0's.
218 */
219
220 snprintf(temp, sizeof(temp), "%.12f", number);
221 for (tempptr = temp + strlen(temp) - 1;
222 tempptr > temp && *tempptr == '0';
223 *tempptr-- = '\0');
224
225 /*
226 * Next, find the decimal point...
227 */
228
229 if (loc && loc->decimal_point)
230 {
231 dec = loc->decimal_point;
b86bc4cf 232 declen = (int)strlen(dec);
757d2cad 233 }
234 else
235 {
236 dec = ".";
237 declen = 1;
238 }
239
240 if (declen == 1)
241 tempdec = strchr(temp, *dec);
242 else
243 tempdec = strstr(temp, dec);
244
245 /*
246 * Copy everything up to the decimal point...
247 */
248
249 if (tempdec)
250 {
251 for (tempptr = temp, bufptr = buf;
252 tempptr < tempdec && bufptr < bufend;
253 *bufptr++ = *tempptr++);
254
7594b224 255 tempptr += declen;
757d2cad 256
7594b224 257 if (*tempptr && bufptr < bufend)
757d2cad 258 {
259 *bufptr++ = '.';
260
261 while (*tempptr && bufptr < bufend)
262 *bufptr++ = *tempptr++;
263 }
264
265 *bufptr = '\0';
266 }
267 else
268 {
07623986 269 strlcpy(buf, temp, (size_t)(bufend - buf + 1));
757d2cad 270 bufptr = buf + strlen(buf);
271 }
272
273 return (bufptr);
274}
275
276
277/*
278 * '_cupsStrFree()' - Free/dereference a string.
4400e98d 279 */
280
281void
757d2cad 282_cupsStrFree(const char *s) /* I - String to free */
4400e98d 283{
4400e98d 284 _cups_sp_item_t *item, /* String pool item */
426c6a59 285 *key; /* Search key */
4400e98d 286
287
288 /*
289 * Range check input...
290 */
291
292 if (!s)
293 return;
294
295 /*
d6ae789d 296 * Check the string pool...
297 *
298 * We don't need to lock the mutex yet, as we only want to know if
299 * the stringpool is initialized. The rest of the code will still
300 * work if it is initialized before we lock...
4400e98d 301 */
302
d6ae789d 303 if (!stringpool)
4400e98d 304 return;
305
306 /*
307 * See if the string is already in the pool...
308 */
309
6d2f911b 310 _cupsMutexLock(&sp_mutex);
d6ae789d 311
745129be
MS
312 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
313
314#ifdef DEBUG_GUARDS
315 if (key->guard != _CUPS_STR_GUARD)
316 {
e07d4801
MS
317 DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, "
318 "ref_count=%d", key, key->str, key->guard, key->ref_count));
745129be
MS
319 abort();
320 }
321#endif /* DEBUG_GUARDS */
4400e98d 322
426c6a59
MS
323 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
324 item == key)
4400e98d 325 {
326 /*
327 * Found it, dereference...
328 */
329
330 item->ref_count --;
331
332 if (!item->ref_count)
333 {
334 /*
335 * Remove and free...
336 */
337
d6ae789d 338 cupsArrayRemove(stringpool, item);
4400e98d 339
4400e98d 340 free(item);
341 }
342 }
d6ae789d 343
6d2f911b 344 _cupsMutexUnlock(&sp_mutex);
4400e98d 345}
346
347
426c6a59
MS
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
356char * /* O - Pointer to string */
e07d4801 357_cupsStrRetain(const char *s) /* I - String to retain */
426c6a59
MS
358{
359 _cups_sp_item_t *item; /* Pointer to string pool item */
360
361
362 if (s)
363 {
745129be
MS
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 {
e07d4801
MS
369 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
370 "ref_count=%d", item, s, item->guard, item->ref_count));
745129be
MS
371 abort();
372 }
373#endif /* DEBUG_GUARDS */
426c6a59 374
6d2f911b 375 _cupsMutexLock(&sp_mutex);
426c6a59
MS
376
377 item->ref_count ++;
378
6d2f911b 379 _cupsMutexUnlock(&sp_mutex);
426c6a59
MS
380 }
381
e07d4801 382 return ((char *)s);
426c6a59
MS
383}
384
385
4400e98d 386/*
757d2cad 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
393double /* O - Number */
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
7cf5915e 413 while (_cups_isspace(*buf))
757d2cad 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 {
e1d6a774 437 /*
438 * Read fractional portion of number...
439 */
440
441 buf ++;
442
757d2cad 443 if (loc && loc->decimal_point)
444 {
7e86f2f6 445 strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp));
757d2cad 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
b86bc4cf 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
757d2cad 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.
4400e98d 526 */
527
528size_t /* O - Number of strings */
757d2cad 529_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
530 size_t *total_bytes) /* O - Total string bytes */
4400e98d 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 */
4400e98d 537
538
539 /*
540 * Loop through strings in pool, counting everything up...
541 */
542
6d2f911b 543 _cupsMutexLock(&sp_mutex);
4400e98d 544
545 for (count = 0, abytes = 0, tbytes = 0,
d6ae789d 546 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 547 item;
d6ae789d 548 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 549 {
550 /*
551 * Count allocated memory, using a 64-bit aligned buffer as a basis.
552 */
553
554 count += item->ref_count;
7e86f2f6 555 len = (strlen(item->str) + 8) & (size_t)~7;
4400e98d 556 abytes += sizeof(_cups_sp_item_t) + len;
557 tbytes += item->ref_count * len;
558 }
559
6d2f911b 560 _cupsMutexUnlock(&sp_mutex);
d6ae789d 561
4400e98d 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}
ef416fc2 574
575
576/*
577 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
578 */
579
580void
581_cups_strcpy(char *dst, /* I - Destination string */
88f9aafc 582 const char *src) /* I - Source string */
ef416fc2 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
596char * /* O - New string pointer */
597_cups_strdup(const char *s) /* I - String to duplicate */
598{
5a9febac
MS
599 char *t; /* New string pointer */
600 size_t slen; /* Length of string */
ef416fc2 601
602
5a9febac 603 if (!s)
ef416fc2 604 return (NULL);
605
5a9febac
MS
606 slen = strlen(s);
607 if ((t = malloc(slen + 1)) == NULL)
ef416fc2 608 return (NULL);
609
5a9febac 610 return (memcpy(t, s, slen + 1));
ef416fc2 611}
612#endif /* !HAVE_STRDUP */
613
614
615/*
616 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
617 */
618
ef416fc2 619int /* O - Result of comparison (-1, 0, or 1) */
620_cups_strcasecmp(const char *s, /* I - First string */
88f9aafc 621 const char *t) /* I - Second string */
ef416fc2 622{
623 while (*s != '\0' && *t != '\0')
624 {
88f9aafc 625 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 626 return (-1);
88f9aafc 627 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 641
642/*
643 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
644 */
645
ef416fc2 646int /* O - Result of comparison (-1, 0, or 1) */
647_cups_strncasecmp(const char *s, /* I - First string */
0a682745 648 const char *t, /* I - Second string */
ef416fc2 649 size_t n) /* I - Maximum number of characters to compare */
650{
651 while (*s != '\0' && *t != '\0' && n > 0)
652 {
88f9aafc 653 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 654 return (-1);
88f9aafc 655 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 672
673
674#ifndef HAVE_STRLCAT
675/*
676 * '_cups_strlcat()' - Safely concatenate two strings.
677 */
678
679size_t /* O - Length of string */
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);
ef416fc2 693
e38ab40b
MS
694 if (size < (dstlen + 1))
695 return (dstlen); /* No room, return immediately... */
696
697 size -= dstlen + 1;
ef416fc2 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
7e86f2f6 712 memmove(dst + dstlen, src, srclen);
ef416fc2 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
725size_t /* O - Length of string */
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
7e86f2f6 748 memmove(dst, src, srclen);
ef416fc2 749 dst[srclen] = '\0';
750
751 return (srclen);
752}
753#endif /* !HAVE_STRLCPY */
754
755
756/*
4400e98d 757 * 'compare_sp_items()' - Compare two string pool items...
758 */
759
760static int /* O - Result of comparison */
761compare_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}