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