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