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