]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/string.c
Greatly simplify the man page handling.
[thirdparty/cups.git] / cups / string.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * String functions for CUPS.
ef416fc2 3 *
86c184ff
MS
4 * Copyright © 2007-2019 by Apple Inc.
5 * Copyright © 1997-2007 by Easy Software Products.
ef416fc2 6 *
86c184ff
MS
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
ef416fc2 9 */
10
11/*
12 * Include necessary headers...
13 */
14
7cf5915e 15#define _CUPS_STRING_C_
554aa7b7 16#include "cups-private.h"
fb863569 17#include "debug-internal.h"
745129be 18#include <stddef.h>
4400e98d 19#include <limits.h>
d6ae789d 20
21
22/*
23 * Local globals...
24 */
25
6d2f911b 26static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 27 /* Mutex to control access to pool */
d6ae789d 28static cups_array_t *stringpool = NULL;
29 /* Global string pool */
4400e98d 30
31
32/*
33 * Local functions...
34 */
35
36static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
37
38
39/*
757d2cad 40 * '_cupsStrAlloc()' - Allocate/reference a string.
4400e98d 41 */
42
43char * /* O - String pointer */
757d2cad 44_cupsStrAlloc(const char *s) /* I - String */
4400e98d 45{
5a9febac 46 size_t slen; /* Length of string */
4400e98d 47 _cups_sp_item_t *item, /* String pool item */
426c6a59 48 *key; /* Search key */
4400e98d 49
50
51 /*
52 * Range check input...
53 */
54
55 if (!s)
56 return (NULL);
57
58 /*
59 * Get the string pool...
60 */
61
6d2f911b 62 _cupsMutexLock(&sp_mutex);
d6ae789d 63
64 if (!stringpool)
65 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL);
4400e98d 66
d6ae789d 67 if (!stringpool)
68 {
6d2f911b 69 _cupsMutexUnlock(&sp_mutex);
4400e98d 70
4400e98d 71 return (NULL);
d6ae789d 72 }
4400e98d 73
74 /*
75 * See if the string is already in the pool...
76 */
77
745129be 78 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
4400e98d 79
426c6a59 80 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
4400e98d 81 {
82 /*
83 * Found it, return the cached string...
84 */
85
86 item->ref_count ++;
87
745129be 88#ifdef DEBUG_GUARDS
e07d4801
MS
89 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, "
90 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
91 item->ref_count));
92
93 if (item->guard != _CUPS_STR_GUARD)
94 abort();
95#endif /* DEBUG_GUARDS */
96
6d2f911b 97 _cupsMutexUnlock(&sp_mutex);
d6ae789d 98
4400e98d 99 return (item->str);
100 }
101
102 /*
103 * Not found, so allocate a new one...
104 */
105
5a9febac
MS
106 slen = strlen(s);
107 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
4400e98d 108 if (!item)
d6ae789d 109 {
6d2f911b 110 _cupsMutexUnlock(&sp_mutex);
d6ae789d 111
4400e98d 112 return (NULL);
d6ae789d 113 }
4400e98d 114
115 item->ref_count = 1;
5a9febac 116 memcpy(item->str, s, slen + 1);
4400e98d 117
745129be
MS
118#ifdef DEBUG_GUARDS
119 item->guard = _CUPS_STR_GUARD;
120
e07d4801
MS
121 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, "
122 "ref_count=%d", item, item->str, s, item->guard,
745129be
MS
123 item->ref_count));
124#endif /* DEBUG_GUARDS */
125
4400e98d 126 /*
127 * Add the string to the pool and return it...
128 */
129
d6ae789d 130 cupsArrayAdd(stringpool, item);
131
6d2f911b 132 _cupsMutexUnlock(&sp_mutex);
4400e98d 133
134 return (item->str);
135}
136
137
554aa7b7
MS
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
144char * /* O - Buffer */
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 *dateval; /* 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 dateval = localtime(&timeval);
158
159 if (cg->lang_default->encoding != CUPS_UTF8)
160 {
161 strftime(temp, sizeof(temp), "%c", dateval);
162 cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding);
163 }
164 else
165 strftime(buf, bufsize, "%c", dateval);
166
167 return (buf);
168}
169
170
4400e98d 171/*
426c6a59 172 * '_cupsStrFlush()' - Flush the string pool.
4400e98d 173 */
174
175void
d6ae789d 176_cupsStrFlush(void)
4400e98d 177{
178 _cups_sp_item_t *item; /* Current item */
179
180
e07d4801 181 DEBUG_printf(("4_cupsStrFlush: %d strings in array",
ae71f5de 182 cupsArrayCount(stringpool)));
d6ae789d 183
6d2f911b 184 _cupsMutexLock(&sp_mutex);
d6ae789d 185
186 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 187 item;
d6ae789d 188 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 189 free(item);
4400e98d 190
d6ae789d 191 cupsArrayDelete(stringpool);
192 stringpool = NULL;
193
6d2f911b 194 _cupsMutexUnlock(&sp_mutex);
4400e98d 195}
196
197
198/*
757d2cad 199 * '_cupsStrFormatd()' - Format a floating-point number.
200 */
201
202char * /* O - Pointer to end of string */
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;
b86bc4cf 233 declen = (int)strlen(dec);
757d2cad 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
7594b224 256 tempptr += declen;
757d2cad 257
7594b224 258 if (*tempptr && bufptr < bufend)
757d2cad 259 {
260 *bufptr++ = '.';
261
262 while (*tempptr && bufptr < bufend)
263 *bufptr++ = *tempptr++;
264 }
265
266 *bufptr = '\0';
267 }
268 else
269 {
07623986 270 strlcpy(buf, temp, (size_t)(bufend - buf + 1));
757d2cad 271 bufptr = buf + strlen(buf);
272 }
273
274 return (bufptr);
275}
276
277
278/*
279 * '_cupsStrFree()' - Free/dereference a string.
4400e98d 280 */
281
282void
757d2cad 283_cupsStrFree(const char *s) /* I - String to free */
4400e98d 284{
4400e98d 285 _cups_sp_item_t *item, /* String pool item */
426c6a59 286 *key; /* Search key */
4400e98d 287
288
289 /*
290 * Range check input...
291 */
292
293 if (!s)
294 return;
295
296 /*
d6ae789d 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...
4400e98d 302 */
303
d6ae789d 304 if (!stringpool)
4400e98d 305 return;
306
307 /*
308 * See if the string is already in the pool...
309 */
310
6d2f911b 311 _cupsMutexLock(&sp_mutex);
d6ae789d 312
745129be
MS
313 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
314
426c6a59
MS
315 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
316 item == key)
4400e98d 317 {
318 /*
319 * Found it, dereference...
320 */
321
86c184ff
MS
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
4400e98d 330 item->ref_count --;
331
332 if (!item->ref_count)
333 {
334 /*
335 * Remove and free...
336 */
337
d6ae789d 338 cupsArrayRemove(stringpool, item);
4400e98d 339
4400e98d 340 free(item);
341 }
342 }
d6ae789d 343
6d2f911b 344 _cupsMutexUnlock(&sp_mutex);
4400e98d 345}
346
347
426c6a59
MS
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
356char * /* O - Pointer to string */
e07d4801 357_cupsStrRetain(const char *s) /* I - String to retain */
426c6a59
MS
358{
359 _cups_sp_item_t *item; /* Pointer to string pool item */
360
361
362 if (s)
363 {
745129be
MS
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 {
e07d4801
MS
369 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, "
370 "ref_count=%d", item, s, item->guard, item->ref_count));
745129be
MS
371 abort();
372 }
373#endif /* DEBUG_GUARDS */
426c6a59 374
6d2f911b 375 _cupsMutexLock(&sp_mutex);
426c6a59
MS
376
377 item->ref_count ++;
378
6d2f911b 379 _cupsMutexUnlock(&sp_mutex);
426c6a59
MS
380 }
381
e07d4801 382 return ((char *)s);
426c6a59
MS
383}
384
385
4400e98d 386/*
757d2cad 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
393double /* O - Number */
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
7cf5915e 413 while (_cups_isspace(*buf))
757d2cad 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 {
e1d6a774 437 /*
438 * Read fractional portion of number...
439 */
440
441 buf ++;
442
757d2cad 443 if (loc && loc->decimal_point)
444 {
7e86f2f6 445 strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp));
757d2cad 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
b86bc4cf 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
757d2cad 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.
4400e98d 526 */
527
528size_t /* O - Number of strings */
757d2cad 529_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
530 size_t *total_bytes) /* O - Total string bytes */
4400e98d 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 */
4400e98d 537
538
539 /*
540 * Loop through strings in pool, counting everything up...
541 */
542
6d2f911b 543 _cupsMutexLock(&sp_mutex);
4400e98d 544
545 for (count = 0, abytes = 0, tbytes = 0,
d6ae789d 546 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool);
4400e98d 547 item;
d6ae789d 548 item = (_cups_sp_item_t *)cupsArrayNext(stringpool))
4400e98d 549 {
550 /*
551 * Count allocated memory, using a 64-bit aligned buffer as a basis.
552 */
553
554 count += item->ref_count;
7e86f2f6 555 len = (strlen(item->str) + 8) & (size_t)~7;
4400e98d 556 abytes += sizeof(_cups_sp_item_t) + len;
557 tbytes += item->ref_count * len;
558 }
559
6d2f911b 560 _cupsMutexUnlock(&sp_mutex);
d6ae789d 561
4400e98d 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}
ef416fc2 574
575
576/*
577 * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
578 */
579
580void
581_cups_strcpy(char *dst, /* I - Destination string */
88f9aafc 582 const char *src) /* I - Source string */
ef416fc2 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
596char * /* O - New string pointer */
597_cups_strdup(const char *s) /* I - String to duplicate */
598{
5a9febac
MS
599 char *t; /* New string pointer */
600 size_t slen; /* Length of string */
ef416fc2 601
602
5a9febac 603 if (!s)
ef416fc2 604 return (NULL);
605
5a9febac
MS
606 slen = strlen(s);
607 if ((t = malloc(slen + 1)) == NULL)
ef416fc2 608 return (NULL);
609
5a9febac 610 return (memcpy(t, s, slen + 1));
ef416fc2 611}
612#endif /* !HAVE_STRDUP */
613
614
615/*
616 * '_cups_strcasecmp()' - Do a case-insensitive comparison.
617 */
618
ef416fc2 619int /* O - Result of comparison (-1, 0, or 1) */
620_cups_strcasecmp(const char *s, /* I - First string */
88f9aafc 621 const char *t) /* I - Second string */
ef416fc2 622{
623 while (*s != '\0' && *t != '\0')
624 {
88f9aafc 625 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 626 return (-1);
88f9aafc 627 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 641
642/*
643 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
644 */
645
ef416fc2 646int /* O - Result of comparison (-1, 0, or 1) */
647_cups_strncasecmp(const char *s, /* I - First string */
0a682745 648 const char *t, /* I - Second string */
ef416fc2 649 size_t n) /* I - Maximum number of characters to compare */
650{
651 while (*s != '\0' && *t != '\0' && n > 0)
652 {
88f9aafc 653 if (_cups_tolower(*s) < _cups_tolower(*t))
ef416fc2 654 return (-1);
88f9aafc 655 else if (_cups_tolower(*s) > _cups_tolower(*t))
ef416fc2 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}
ef416fc2 672
673
674#ifndef HAVE_STRLCAT
675/*
676 * '_cups_strlcat()' - Safely concatenate two strings.
677 */
678
679size_t /* O - Length of string */
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);
ef416fc2 693
e38ab40b
MS
694 if (size < (dstlen + 1))
695 return (dstlen); /* No room, return immediately... */
696
697 size -= dstlen + 1;
ef416fc2 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
7e86f2f6 712 memmove(dst + dstlen, src, srclen);
ef416fc2 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
725size_t /* O - Length of string */
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
7e86f2f6 748 memmove(dst, src, srclen);
ef416fc2 749 dst[srclen] = '\0';
750
751 return (srclen);
752}
753#endif /* !HAVE_STRLCPY */
754
755
756/*
4400e98d 757 * 'compare_sp_items()' - Compare two string pool items...
758 */
759
760static int /* O - Result of comparison */
761compare_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}