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