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