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