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