]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/string.c
ad5b5bc6134ff27cceafff08b4f32d5cf749f833
[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-2012 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 size_t slen; /* Length of string */
73 _cups_sp_item_t *item, /* String pool item */
74 *key; /* Search key */
75
76
77 /*
78 * Range check input...
79 */
80
81 if (!s)
82 return (NULL);
83
84 /*
85 * Get the string pool...
86 */
87
88 _cupsMutexLock(&sp_mutex);
89
90 if (!stringpool)
91 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
92
93 if (!stringpool)
94 {
95 _cupsMutexUnlock(&sp_mutex);
96
97 return (NULL);
98 }
99
100 /*
101 * See if the string is already in the pool...
102 */
103
104 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
105
106 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
107 {
108 /*
109 * Found it, return the cached string...
110 */
111
112 item->ref_count ++;
113
114 #ifdef DEBUG_GUARDS
115 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
116 "ref_count=%d", item, item->str, s, item->guard,
117 item->ref_count));
118
119 if (item->guard != _CUPS_STR_GUARD)
120 abort();
121 #endif /* DEBUG_GUARDS */
122
123 _cupsMutexUnlock(&sp_mutex);
124
125 return (item->str);
126 }
127
128 /*
129 * Not found, so allocate a new one...
130 */
131
132 slen = strlen(s);
133 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
134 if (!item)
135 {
136 _cupsMutexUnlock(&sp_mutex);
137
138 return (NULL);
139 }
140
141 item->ref_count = 1;
142 memcpy(item->str, s, slen + 1);
143
144 #ifdef DEBUG_GUARDS
145 item->guard = _CUPS_STR_GUARD;
146
147 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
148 "ref_count=%d", item, item->str, s, item->guard,
149 item->ref_count));
150 #endif /* DEBUG_GUARDS */
151
152 /*
153 * Add the string to the pool and return it...
154 */
155
156 cupsArrayAdd(stringpool, item);
157
158 _cupsMutexUnlock(&sp_mutex);
159
160 return (item->str);
161 }
162
163
164 /*
165 * '_cupsStrFlush()' - Flush the string pool.
166 */
167
168 void
169 _cupsStrFlush(void)
170 {
171 _cups_sp_item_t *item; /* Current item */
172
173
174 DEBUG_printf(("4_cupsStrFlush: %d strings in array",
175 cupsArrayCount(stringpool)));
176
177 _cupsMutexLock(&sp_mutex);
178
179 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
180 item;
181 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
182 free(item);
183
184 cupsArrayDelete(stringpool);
185 stringpool = NULL;
186
187 _cupsMutexUnlock(&sp_mutex);
188 }
189
190
191 /*
192 * '_cupsStrFormatd()' - Format a floating-point number.
193 */
194
195 char * /* 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;
226 declen = (int)strlen(dec);
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
249 tempptr += declen;
250
251 if (*tempptr && bufptr < bufend)
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.
273 */
274
275 void
276 _cupsStrFree(const char *s) /* I - String to free */
277 {
278 _cups_sp_item_t *item, /* String pool item */
279 *key; /* Search key */
280
281
282 /*
283 * Range check input...
284 */
285
286 if (!s)
287 return;
288
289 /*
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...
295 */
296
297 if (!stringpool)
298 return;
299
300 /*
301 * See if the string is already in the pool...
302 */
303
304 _cupsMutexLock(&sp_mutex);
305
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 {
311 DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, "
312 "ref_count=%d", key, key->str, key->guard, key->ref_count));
313 abort();
314 }
315 #endif /* DEBUG_GUARDS */
316
317 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
318 item == key)
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
332 cupsArrayRemove(stringpool, item);
333
334 free(item);
335 }
336 }
337
338 _cupsMutexUnlock(&sp_mutex);
339 }
340
341
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
350 char * /* O - Pointer to string */
351 _cupsStrRetain(const char *s) /* I - String to retain */
352 {
353 _cups_sp_item_t *item; /* Pointer to string pool item */
354
355
356 if (s)
357 {
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 {
363 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
364 "ref_count=%d", item, s, item->guard, item->ref_count));
365 abort();
366 }
367 #endif /* DEBUG_GUARDS */
368
369 _cupsMutexLock(&sp_mutex);
370
371 item->ref_count ++;
372
373 _cupsMutexUnlock(&sp_mutex);
374 }
375
376 return ((char *)s);
377 }
378
379
380 /*
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
387 double /* 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
407 while (_cups_isspace(*buf))
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 {
431 /*
432 * Read fractional portion of number...
433 */
434
435 buf ++;
436
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
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
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.
520 */
521
522 size_t /* O - Number of strings */
523 _cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
524 size_t *total_bytes) /* O - Total string bytes */
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 */
531
532
533 /*
534 * Loop through strings in pool, counting everything up...
535 */
536
537 _cupsMutexLock(&sp_mutex);
538
539 for (count = 0, abytes = 0, tbytes = 0,
540 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
541 item;
542 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
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
554 _cupsMutexUnlock(&sp_mutex);
555
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 }
568
569
570 /*
571 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
572 */
573
574 void
575 _cups_strcpy(char *dst, /* I - Destination string */
576 const char *src) /* I - Source string */
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
590 char * /* O - New string pointer */
591 _cups_strdup(const char *s) /* I - String to duplicate */
592 {
593 char *t; /* New string pointer */
594 size_t slen; /* Length of string */
595
596
597 if (!s)
598 return (NULL);
599
600 slen = strlen(s);
601 if ((t = malloc(slen + 1)) == NULL)
602 return (NULL);
603
604 return (memcpy(t, s, slen + 1));
605 }
606 #endif /* !HAVE_STRDUP */
607
608
609 /*
610 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
611 */
612
613 int /* O - Result of comparison (-1, 0, or 1) */
614 _cups_strcasecmp(const char *s, /* I - First string */
615 const char *t) /* I - Second string */
616 {
617 while (*s != '\0' && *t != '\0')
618 {
619 if (_cups_tolower(*s) < _cups_tolower(*t))
620 return (-1);
621 else if (_cups_tolower(*s) > _cups_tolower(*t))
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 }
635
636 /*
637 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
638 */
639
640 int /* O - Result of comparison (-1, 0, or 1) */
641 _cups_strncasecmp(const char *s, /* I - First string */
642 const char *t, /* I - Second string */
643 size_t n) /* I - Maximum number of characters to compare */
644 {
645 while (*s != '\0' && *t != '\0' && n > 0)
646 {
647 if (_cups_tolower(*s) < _cups_tolower(*t))
648 return (-1);
649 else if (_cups_tolower(*s) > _cups_tolower(*t))
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 }
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 */