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