1/*
2 * "$Id: string.c 11093 2013-07-03 20:48:42Z msweet $"
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
52static _cups_mutex_t	sp_mutex = _CUPS_MUTEX_INITIALIZER;
53					/* Mutex to control access to pool */
54static cups_array_t	*stringpool = NULL;
55					/* Global string pool */
56
57
58/*
59 * Local functions...
60 */
61
62static 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
69char *					/* 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
168void
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
195char *					/* 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
275void
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
350char *					/* 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
387double					/* 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
522size_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
574void
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
590char 	*				/* 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
613int				/* 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
640int					/* 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
673size_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
718size_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
753static int				/* O - Result of comparison */
754compare_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 11093 2013-07-03 20:48:42Z msweet $".
763 */
764