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
30static _cups_mutex_t	sp_mutex = _CUPS_MUTEX_INITIALIZER;
31					/* Mutex to control access to pool */
32static cups_array_t	*stringpool = NULL;
33					/* Global string pool */
34
35
36/*
37 * Local functions...
38 */
39
40static 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
47char *					/* 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
148char *					/* 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
179void
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
206char *					/* 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
286void
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
361char *					/* 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
398double					/* 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
533size_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
585void
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
601char 	*				/* 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
624int				/* 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
651int					/* 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
684size_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
699  if (size < (dstlen + 1))
700    return (dstlen);		        /* No room, return immediately... */
701
702  size -= dstlen + 1;
703
704 /*
705  * Figure out how much room is needed...
706  */
707
708  srclen = strlen(src);
709
710 /*
711  * Copy the appropriate amount...
712  */
713
714  if (srclen > size)
715    srclen = size;
716
717  memmove(dst + dstlen, src, srclen);
718  dst[dstlen + srclen] = '\0';
719
720  return (dstlen + srclen);
721}
722#endif /* !HAVE_STRLCAT */
723
724
725#ifndef HAVE_STRLCPY
726/*
727 * '_cups_strlcpy()' - Safely copy two strings.
728 */
729
730size_t					/* O - Length of string */
731_cups_strlcpy(char       *dst,		/* O - Destination string */
732              const char *src,		/* I - Source string */
733	      size_t      size)		/* I - Size of destination string buffer */
734{
735  size_t	srclen;			/* Length of source string */
736
737
738 /*
739  * Figure out how much room is needed...
740  */
741
742  size --;
743
744  srclen = strlen(src);
745
746 /*
747  * Copy the appropriate amount...
748  */
749
750  if (srclen > size)
751    srclen = size;
752
753  memmove(dst, src, srclen);
754  dst[srclen] = '\0';
755
756  return (srclen);
757}
758#endif /* !HAVE_STRLCPY */
759
760
761/*
762 * 'compare_sp_items()' - Compare two string pool items...
763 */
764
765static int				/* O - Result of comparison */
766compare_sp_items(_cups_sp_item_t *a,	/* I - First item */
767                 _cups_sp_item_t *b)	/* I - Second item */
768{
769  return (strcmp(a->str, b->str));
770}
771