Update changelog
[platform/upstream/cups.git] / cups / string.c
1 /*
2  * "$Id: string.c 11173 2013-07-23 12:31:34Z msweet $"
3  *
4  *   String functions for CUPS.
5  *
6  *   Copyright 2007-2011 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 int                             /* O - Result of comparison (-1, 0, or 1) */
610 _cups_strcasecmp(const char *s, /* I - First string */
611                  const char *t) /* I - Second string */
612 {
613   while (*s != '\0' && *t != '\0')
614   {
615     if (_cups_tolower(*s) < _cups_tolower(*t))
616       return (-1);
617     else if (_cups_tolower(*s) > _cups_tolower(*t))
618       return (1);
619
620     s ++;
621     t ++;
622   }
623
624   if (*s == '\0' && *t == '\0')
625     return (0);
626   else if (*s != '\0')
627     return (1);
628   else
629     return (-1);
630 }
631
632 /*
633  * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
634  */
635
636 int                                     /* O - Result of comparison (-1, 0, or 1) */
637 _cups_strncasecmp(const char *s,        /* I - First string */
638                   const char *t,        /* I - Second string */
639                   size_t     n)         /* I - Maximum number of characters to compare */
640 {
641   while (*s != '\0' && *t != '\0' && n > 0)
642   {
643     if (_cups_tolower(*s) < _cups_tolower(*t))
644       return (-1);
645     else if (_cups_tolower(*s) > _cups_tolower(*t))
646       return (1);
647
648     s ++;
649     t ++;
650     n --;
651   }
652
653   if (n == 0)
654     return (0);
655   else if (*s == '\0' && *t == '\0')
656     return (0);
657   else if (*s != '\0')
658     return (1);
659   else
660     return (-1);
661 }
662
663
664 #ifndef HAVE_STRLCAT
665 /*
666  * '_cups_strlcat()' - Safely concatenate two strings.
667  */
668
669 size_t                                  /* O - Length of string */
670 _cups_strlcat(char       *dst,          /* O - Destination string */
671               const char *src,          /* I - Source string */
672               size_t     size)          /* I - Size of destination string buffer */
673 {
674   size_t        srclen;                 /* Length of source string */
675   size_t        dstlen;                 /* Length of destination string */
676
677
678  /*
679   * Figure out how much room is left...
680   */
681
682   dstlen = strlen(dst);
683   size   -= dstlen + 1;
684
685   if (!size)
686     return (dstlen);            /* No room, return immediately... */
687
688  /*
689   * Figure out how much room is needed...
690   */
691
692   srclen = strlen(src);
693
694  /*
695   * Copy the appropriate amount...
696   */
697
698   if (srclen > size)
699     srclen = size;
700
701   memcpy(dst + dstlen, src, srclen);
702   dst[dstlen + srclen] = '\0';
703
704   return (dstlen + srclen);
705 }
706 #endif /* !HAVE_STRLCAT */
707
708
709 #ifndef HAVE_STRLCPY
710 /*
711  * '_cups_strlcpy()' - Safely copy two strings.
712  */
713
714 size_t                                  /* O - Length of string */
715 _cups_strlcpy(char       *dst,          /* O - Destination string */
716               const char *src,          /* I - Source string */
717               size_t      size)         /* I - Size of destination string buffer */
718 {
719   size_t        srclen;                 /* Length of source string */
720
721
722  /*
723   * Figure out how much room is needed...
724   */
725
726   size --;
727
728   srclen = strlen(src);
729
730  /*
731   * Copy the appropriate amount...
732   */
733
734   if (srclen > size)
735     srclen = size;
736
737   memcpy(dst, src, srclen);
738   dst[srclen] = '\0';
739
740   return (srclen);
741 }
742 #endif /* !HAVE_STRLCPY */
743
744
745 /*
746  * 'compare_sp_items()' - Compare two string pool items...
747  */
748
749 static int                              /* O - Result of comparison */
750 compare_sp_items(_cups_sp_item_t *a,    /* I - First item */
751                  _cups_sp_item_t *b)    /* I - Second item */
752 {
753   return (strcmp(a->str, b->str));
754 }
755
756
757 /*
758  * End of "$Id: string.c 11173 2013-07-23 12:31:34Z msweet $".
759  */