1 /**
2 * @file
3 * String manipulation functions
4 *
5 * @authors
6 * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
7 * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
8 *
9 * @copyright
10 * This program is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 2 of the License, or (at your option) any later
13 * version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /**
25 * @page mutt_string String manipulation functions
26 *
27 * Lots of commonly-used string manipulation routines.
28 */
29
30 #include "config.h"
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include "exit.h"
41 #include "logging.h"
42 #include "memory.h"
43 #include "message.h"
44 #include "string2.h"
45 #ifdef HAVE_SYSEXITS_H
46 #include <sysexits.h>
47 #endif
48
49 /**
50 * struct SysExits - Lookup table of error messages
51 */
52 struct SysExits
53 {
54 int err_num; ///< Error number, see errno(3)
55 const char *err_str; ///< Human-readable string for error
56 };
57
58 static const struct SysExits sysexits[] = {
59 #ifdef EX_USAGE
60 { 0xff & EX_USAGE, "Bad usage." },
61 #endif
62 #ifdef EX_DATAERR
63 { 0xff & EX_DATAERR, "Data format error." },
64 #endif
65 #ifdef EX_NOINPUT
66 { 0xff & EX_NOINPUT, "Can't open input." },
67 #endif
68 #ifdef EX_NOUSER
69 { 0xff & EX_NOUSER, "User unknown." },
70 #endif
71 #ifdef EX_NOHOST
72 { 0xff & EX_NOHOST, "Host unknown." },
73 #endif
74 #ifdef EX_UNAVAILABLE
75 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
76 #endif
77 #ifdef EX_SOFTWARE
78 { 0xff & EX_SOFTWARE, "Internal error." },
79 #endif
80 #ifdef EX_OSERR
81 { 0xff & EX_OSERR, "Operating system error." },
82 #endif
83 #ifdef EX_OSFILE
84 { 0xff & EX_OSFILE, "System file missing." },
85 #endif
86 #ifdef EX_CANTCREAT
87 { 0xff & EX_CANTCREAT, "Can't create output." },
88 #endif
89 #ifdef EX_IOERR
90 { 0xff & EX_IOERR, "I/O error." },
91 #endif
92 #ifdef EX_TEMPFAIL
93 { 0xff & EX_TEMPFAIL, "Deferred." },
94 #endif
95 #ifdef EX_PROTOCOL
96 { 0xff & EX_PROTOCOL, "Remote protocol error." },
97 #endif
98 #ifdef EX_NOPERM
99 { 0xff & EX_NOPERM, "Insufficient permission." },
100 #endif
101 #ifdef EX_CONFIG
102 { 0xff & EX_NOPERM, "Local configuration error." },
103 #endif
104 { S_ERR, "Exec error." },
105 };
106
107 /**
108 * mutt_str_sysexit - Return a string matching an error code
109 * @param err_num Error code, e.g. EX_NOPERM
110 * @retval ptr string representing the error code
111 */
mutt_str_sysexit(int err_num)112 const char *mutt_str_sysexit(int err_num)
113 {
114 for (size_t i = 0; i < mutt_array_size(sysexits); i++)
115 {
116 if (err_num == sysexits[i].err_num)
117 return sysexits[i].err_str;
118 }
119
120 return NULL;
121 }
122
123 /**
124 * startswith - Check whether a string starts with a prefix
125 * @param str String to check
126 * @param prefix Prefix to match
127 * @param match_case True if case needs to match
128 * @retval num Length of prefix if str starts with prefix
129 * @retval 0 str does not start with prefix
130 */
startswith(const char * str,const char * prefix,bool match_case)131 static size_t startswith(const char *str, const char *prefix, bool match_case)
132 {
133 if (!str || (str[0] == '\0') || !prefix || (prefix[0] == '\0'))
134 {
135 return 0;
136 }
137
138 const char *saved_prefix = prefix;
139 for (; *str && *prefix; str++, prefix++)
140 {
141 if (*str == *prefix)
142 continue;
143
144 if (!match_case && tolower(*str) == tolower(*prefix))
145 continue;
146
147 return 0;
148 }
149
150 return (*prefix == '\0') ? (prefix - saved_prefix) : 0;
151 }
152
153 /**
154 * mutt_str_startswith - Check whether a string starts with a prefix
155 * @param str String to check
156 * @param prefix Prefix to match
157 * @retval num Length of prefix if str starts with prefix
158 * @retval 0 str does not start with prefix
159 */
mutt_str_startswith(const char * str,const char * prefix)160 size_t mutt_str_startswith(const char *str, const char *prefix)
161 {
162 return startswith(str, prefix, true);
163 }
164
165 /**
166 * mutt_istr_startswith - Check whether a string starts with a prefix, ignoring case
167 * @param str String to check
168 * @param prefix Prefix to match
169 * @retval num Length of prefix if str starts with prefix
170 * @retval 0 str does not start with prefix
171 */
mutt_istr_startswith(const char * str,const char * prefix)172 size_t mutt_istr_startswith(const char *str, const char *prefix)
173 {
174 return startswith(str, prefix, false);
175 }
176
177 /**
178 * mutt_str_atol - Convert ASCII string to a long
179 * @param[in] str String to read
180 * @param[out] dst Store the result
181 * @retval 0 Success
182 * @retval -1 Error
183 * @retval -2 Overflow
184 *
185 * This is a strtol() wrapper with range checking.
186 * errno may be set on error, e.g. ERANGE
187 */
mutt_str_atol(const char * str,long * dst)188 int mutt_str_atol(const char *str, long *dst)
189 {
190 if (dst)
191 *dst = 0;
192
193 if (!str || (*str == '\0')) /* no input: 0 */
194 return 0;
195
196 char *e = NULL;
197 errno = 0;
198
199 long res = strtol(str, &e, 10);
200 if (dst)
201 *dst = res;
202 if (((res == LONG_MIN) || (res == LONG_MAX)) && (errno == ERANGE))
203 return -2;
204 if (e && (*e != '\0'))
205 return -1;
206 return 0;
207 }
208
209 /**
210 * mutt_str_atos - Convert ASCII string to a short
211 * @param[in] str String to read
212 * @param[out] dst Store the result
213 * @retval 0 Success
214 * @retval -1 Error
215 * @retval -2 Error, overflow
216 *
217 * This is a strtol() wrapper with range checking.
218 * If @a dst is NULL, the string will be tested only (without conversion).
219 *
220 * errno may be set on error, e.g. ERANGE
221 */
mutt_str_atos(const char * str,short * dst)222 int mutt_str_atos(const char *str, short *dst)
223 {
224 if (dst)
225 *dst = 0;
226
227 long res = 0;
228 int rc = mutt_str_atol(str, &res);
229 if (rc < 0)
230 return rc;
231 if ((res < SHRT_MIN) || (res > SHRT_MAX))
232 return -2;
233
234 if (dst)
235 *dst = (short) res;
236
237 return 0;
238 }
239
240 /**
241 * mutt_str_atoi - Convert ASCII string to an integer
242 * @param[in] str String to read
243 * @param[out] dst Store the result
244 * @retval 0 Success
245 * @retval -1 Error
246 * @retval -2 Error, overflow
247 *
248 * This is a strtol() wrapper with range checking.
249 * If @a dst is NULL, the string will be tested only (without conversion).
250 * errno may be set on error, e.g. ERANGE
251 */
mutt_str_atoi(const char * str,int * dst)252 int mutt_str_atoi(const char *str, int *dst)
253 {
254 if (dst)
255 *dst = 0;
256
257 long res = 0;
258 int rc = mutt_str_atol(str, &res);
259 if (rc < 0)
260 return rc;
261 if ((res < INT_MIN) || (res > INT_MAX))
262 return -2;
263
264 if (dst)
265 *dst = (int) res;
266
267 return 0;
268 }
269
270 /**
271 * mutt_str_atoui - Convert ASCII string to an unsigned integer
272 * @param[in] str String to read
273 * @param[out] dst Store the result
274 * @retval 1 Successful conversion, with trailing characters
275 * @retval 0 Successful conversion
276 * @retval -1 Invalid input
277 * @retval -2 Input out of range
278 *
279 * @note This function's return value differs from the other functions.
280 * They return -1 if there is input beyond the number.
281 */
mutt_str_atoui(const char * str,unsigned int * dst)282 int mutt_str_atoui(const char *str, unsigned int *dst)
283 {
284 if (dst)
285 *dst = 0;
286
287 unsigned long res = 0;
288 int rc = mutt_str_atoul(str, &res);
289 if (rc < 0)
290 return rc;
291 if (res > UINT_MAX)
292 return -2;
293
294 if (dst)
295 *dst = (unsigned int) res;
296
297 return rc;
298 }
299
300 /**
301 * mutt_str_atoul - Convert ASCII string to an unsigned long
302 * @param[in] str String to read
303 * @param[out] dst Store the result
304 * @retval 1 Successful conversion, with trailing characters
305 * @retval 0 Successful conversion
306 * @retval -1 Invalid input
307 *
308 * @note This function's return value differs from the other functions.
309 * They return -1 if there is input beyond the number.
310 */
mutt_str_atoul(const char * str,unsigned long * dst)311 int mutt_str_atoul(const char *str, unsigned long *dst)
312 {
313 if (dst)
314 *dst = 0;
315
316 if (!str || (*str == '\0')) /* no input: 0 */
317 return 0;
318
319 char *e = NULL;
320 errno = 0;
321
322 unsigned long res = strtoul(str, &e, 10);
323 if (dst)
324 *dst = res;
325 if ((res == ULONG_MAX) && (errno == ERANGE))
326 return -1;
327 if (e && (*e != '\0'))
328 return 1;
329 return 0;
330 }
331
332 /**
333 * mutt_str_atoull - Convert ASCII string to an unsigned long long
334 * @param[in] str String to read
335 * @param[out] dst Store the result
336 * @retval 1 Successful conversion, with trailing characters
337 * @retval 0 Successful conversion
338 * @retval -1 Invalid input
339 *
340 * @note This function's return value differs from the other functions.
341 * They return -1 if there is input beyond the number.
342 */
mutt_str_atoull(const char * str,unsigned long long * dst)343 int mutt_str_atoull(const char *str, unsigned long long *dst)
344 {
345 if (dst)
346 *dst = 0;
347
348 if (!str || (*str == '\0')) /* no input: 0 */
349 return 0;
350
351 char *e = NULL;
352 errno = 0;
353
354 unsigned long long res = strtoull(str, &e, 10);
355 if (dst)
356 *dst = res;
357 if ((res == ULLONG_MAX) && (errno == ERANGE))
358 return -1;
359 if (e && (*e != '\0'))
360 return 1;
361 return 0;
362 }
363
364 /**
365 * mutt_str_dup - Copy a string, safely
366 * @param str String to copy
367 * @retval ptr Copy of the string
368 * @retval NULL str was NULL or empty
369 */
mutt_str_dup(const char * str)370 char *mutt_str_dup(const char *str)
371 {
372 if (!str || (*str == '\0'))
373 return NULL;
374
375 return strdup(str);
376 }
377
378 /**
379 * mutt_str_cat - Concatenate two strings
380 * @param buf Buffer containing source string
381 * @param buflen Length of buffer
382 * @param s String to add
383 * @retval ptr Start of the buffer
384 */
mutt_str_cat(char * buf,size_t buflen,const char * s)385 char *mutt_str_cat(char *buf, size_t buflen, const char *s)
386 {
387 if (!buf || (buflen == 0) || !s)
388 return buf;
389
390 char *p = buf;
391
392 buflen--; /* Space for the trailing '\0'. */
393
394 for (; (*buf != '\0') && buflen; buflen--)
395 buf++;
396 for (; *s && buflen; buflen--)
397 *buf++ = *s++;
398
399 *buf = '\0';
400
401 return p;
402 }
403
404 /**
405 * mutt_strn_cat - Concatenate two strings
406 * @param d Buffer containing source string
407 * @param l Length of buffer
408 * @param s String to add
409 * @param sl Maximum amount of string to add
410 * @retval ptr Start of joined string
411 *
412 * Add a string to a maximum of @a sl bytes.
413 */
mutt_strn_cat(char * d,size_t l,const char * s,size_t sl)414 char *mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
415 {
416 if (!d || (l == 0) || !s)
417 return d;
418
419 char *p = d;
420
421 l--; /* Space for the trailing '\0'. */
422
423 for (; *d && l; l--)
424 d++;
425 for (; *s && l && sl; l--, sl--)
426 *d++ = *s++;
427
428 *d = '\0';
429
430 return p;
431 }
432
433 /**
434 * mutt_str_replace - Replace one string with another
435 * @param[out] p String to replace
436 * @param[in] s New string
437 * @retval ptr Replaced string
438 *
439 * This function free()s the original string, strdup()s the new string and
440 * overwrites the pointer to the first string.
441 *
442 * This function alters the pointer of the caller.
443 *
444 * @note Free *p afterwards to handle the case that *p and s reference the same memory
445 */
mutt_str_replace(char ** p,const char * s)446 char *mutt_str_replace(char **p, const char *s)
447 {
448 if (!p)
449 return NULL;
450 const char *tmp = *p;
451 *p = mutt_str_dup(s);
452 FREE(&tmp);
453 return *p;
454 }
455
456 /**
457 * mutt_str_append_item - Add string to another separated by sep
458 * @param[out] str String appended
459 * @param[in] item String to append
460 * @param[in] sep separator between string item
461 *
462 * Append a string to another, separating them by sep if needed.
463 *
464 * This function alters the pointer of the caller.
465 */
mutt_str_append_item(char ** str,const char * item,char sep)466 void mutt_str_append_item(char **str, const char *item, char sep)
467 {
468 if (!str || !item)
469 return;
470
471 size_t sz = mutt_str_len(item);
472 size_t ssz = mutt_str_len(*str);
473
474 mutt_mem_realloc(str, ssz + (((ssz > 0) && (sep != '\0')) ? 1 : 0) + sz + 1);
475 char *p = *str + ssz;
476 if ((ssz > 0) && (sep != '\0'))
477 *p++ = sep;
478 memcpy(p, item, sz + 1);
479 }
480
481 /**
482 * mutt_str_adjust - Shrink-to-fit a string
483 * @param[out] ptr String to alter
484 *
485 * Take a string which is allocated on the heap, find its length and reallocate
486 * the memory to be exactly the right size.
487 *
488 * This function alters the pointer of the caller.
489 */
mutt_str_adjust(char ** ptr)490 void mutt_str_adjust(char **ptr)
491 {
492 if (!ptr || !*ptr)
493 return;
494 mutt_mem_realloc(ptr, strlen(*ptr) + 1);
495 }
496
497 /**
498 * mutt_str_lower - Convert all characters in the string to lowercase
499 * @param str String to lowercase
500 * @retval ptr Lowercase string
501 *
502 * The string is transformed in place.
503 */
mutt_str_lower(char * str)504 char *mutt_str_lower(char *str)
505 {
506 if (!str)
507 return NULL;
508
509 char *p = str;
510
511 while (*p)
512 {
513 *p = tolower((unsigned char) *p);
514 p++;
515 }
516
517 return str;
518 }
519
520 /**
521 * mutt_strn_copy - Copy a sub-string into a buffer
522 * @param dest Buffer for the result
523 * @param src Start of the string to copy
524 * @param len Length of the string to copy
525 * @param dsize Destination buffer size
526 * @retval ptr Destination buffer
527 */
mutt_strn_copy(char * dest,const char * src,size_t len,size_t dsize)528 char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
529 {
530 if (!src || !dest || (len == 0) || (dsize == 0))
531 return dest;
532
533 if (len > (dsize - 1))
534 len = dsize - 1;
535 memcpy(dest, src, len);
536 dest[len] = '\0';
537 return dest;
538 }
539
540 /**
541 * mutt_strn_dup - Duplicate a sub-string
542 * @param begin Start of the string to copy
543 * @param len Length of string to copy
544 * @retval ptr New string
545 *
546 * The caller must free the returned string.
547 */
mutt_strn_dup(const char * begin,size_t len)548 char *mutt_strn_dup(const char *begin, size_t len)
549 {
550 if (!begin)
551 return NULL;
552
553 char *p = mutt_mem_malloc(len + 1);
554 memcpy(p, begin, len);
555 p[len] = '\0';
556 return p;
557 }
558
559 /**
560 * mutt_str_cmp - Compare two strings, safely
561 * @param a First string to compare
562 * @param b Second string to compare
563 * @retval -1 a precedes b
564 * @retval 0 a and b are identical
565 * @retval 1 b precedes a
566 */
mutt_str_cmp(const char * a,const char * b)567 int mutt_str_cmp(const char *a, const char *b)
568 {
569 return strcmp(NONULL(a), NONULL(b));
570 }
571
572 /**
573 * mutt_istr_cmp - Compare two strings ignoring case, safely
574 * @param a First string to compare
575 * @param b Second string to compare
576 * @retval -1 a precedes b
577 * @retval 0 a and b are identical
578 * @retval 1 b precedes a
579 */
mutt_istr_cmp(const char * a,const char * b)580 int mutt_istr_cmp(const char *a, const char *b)
581 {
582 return strcasecmp(NONULL(a), NONULL(b));
583 }
584
585 /**
586 * mutt_strn_equal - Check for equality of two strings (to a maximum), safely
587 * @param a First string to compare
588 * @param b Second string to compare
589 * @param num Maximum number of bytes to compare
590 * @retval true First num chars of both strings are equal
591 * @retval false First num chars of both strings not equal
592 */
mutt_strn_equal(const char * a,const char * b,size_t num)593 bool mutt_strn_equal(const char *a, const char *b, size_t num)
594 {
595 return strncmp(NONULL(a), NONULL(b), num) == 0;
596 }
597
598 /**
599 * mutt_istrn_cmp - Compare two strings ignoring case (to a maximum), safely
600 * @param a First string to compare
601 * @param b Second string to compare
602 * @param num Maximum number of bytes to compare
603 * @retval -1 a precedes b
604 * @retval 0 a and b are identical
605 * @retval 1 b precedes a
606 */
mutt_istrn_cmp(const char * a,const char * b,size_t num)607 int mutt_istrn_cmp(const char *a, const char *b, size_t num)
608 {
609 return strncasecmp(NONULL(a), NONULL(b), num);
610 }
611
612 /**
613 * mutt_istrn_equal - Check for equality of two strings ignoring case (to a maximum), safely
614 * @param a First string to compare
615 * @param b Second string to compare
616 * @param num Maximum number of bytes to compare
617 * @retval -1 a precedes b
618 * @retval true First num chars of both strings are equal, ignoring case
619 * @retval false First num chars of both strings not equal, ignoring case
620 */
mutt_istrn_equal(const char * a,const char * b,size_t num)621 bool mutt_istrn_equal(const char *a, const char *b, size_t num)
622 {
623 return strncasecmp(NONULL(a), NONULL(b), num) == 0;
624 }
625
626 /**
627 * mutt_istrn_rfind - Find last instance of a substring, ignoring case
628 * @param haystack String to search through
629 * @param haystack_length Length of the string
630 * @param needle String to find
631 * @retval NULL String not found
632 * @retval ptr Location of string
633 *
634 * Return the last instance of needle in the haystack, or NULL.
635 * Like strcasestr(), only backwards, and for a limited haystack length.
636 */
mutt_istrn_rfind(const char * haystack,size_t haystack_length,const char * needle)637 const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
638 {
639 if (!haystack || (haystack_length == 0) || !needle)
640 return NULL;
641
642 int needle_length = strlen(needle);
643 const char *haystack_end = haystack + haystack_length - needle_length;
644
645 for (const char *p = haystack_end; p >= haystack; --p)
646 {
647 for (size_t i = 0; i < needle_length; i++)
648 {
649 if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
650 goto next;
651 }
652 return p;
653
654 next:;
655 }
656 return NULL;
657 }
658
659 /**
660 * mutt_str_len - Calculate the length of a string, safely
661 * @param a String to measure
662 * @retval num Length in bytes
663 */
mutt_str_len(const char * a)664 size_t mutt_str_len(const char *a)
665 {
666 return a ? strlen(a) : 0;
667 }
668
669 /**
670 * mutt_str_coll - Collate two strings (compare using locale), safely
671 * @param a First string to compare
672 * @param b Second string to compare
673 * @retval -1 a precedes b
674 * @retval 0 a and b are identical
675 * @retval 1 b precedes a
676 */
mutt_str_coll(const char * a,const char * b)677 int mutt_str_coll(const char *a, const char *b)
678 {
679 return strcoll(NONULL(a), NONULL(b));
680 }
681
682 /**
683 * mutt_istr_find - Find first occurrence of string (ignoring case)
684 * @param haystack String to search through
685 * @param needle String to find
686 * @retval ptr First match of the search string
687 * @retval NULL No match, or an error
688 */
mutt_istr_find(const char * haystack,const char * needle)689 const char *mutt_istr_find(const char *haystack, const char *needle)
690 {
691 if (!haystack)
692 return NULL;
693 if (!needle)
694 return haystack;
695
696 const char *p = NULL, *q = NULL;
697
698 while (*(p = haystack))
699 {
700 for (q = needle;
701 *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
702 p++, q++)
703 {
704 }
705 if ((*q == '\0'))
706 return haystack;
707 haystack++;
708 }
709 return NULL;
710 }
711
712 /**
713 * mutt_str_skip_whitespace - Find the first non-whitespace character in a string
714 * @param p String to search
715 * @retval ptr
716 * - First non-whitespace character
717 * - Terminating NUL character, if the string was entirely whitespace
718 */
mutt_str_skip_whitespace(const char * p)719 char *mutt_str_skip_whitespace(const char *p)
720 {
721 if (!p)
722 return NULL;
723 SKIPWS(p);
724 return (char *) p;
725 }
726
727 /**
728 * mutt_str_remove_trailing_ws - Trim trailing whitespace from a string
729 * @param s String to trim
730 *
731 * The string is modified in place.
732 */
mutt_str_remove_trailing_ws(char * s)733 void mutt_str_remove_trailing_ws(char *s)
734 {
735 if (!s)
736 return;
737
738 for (char *p = s + mutt_str_len(s) - 1; (p >= s) && IS_SPACE(*p); p--)
739 *p = '\0';
740 }
741
742 /**
743 * mutt_str_copy - Copy a string into a buffer (guaranteeing NUL-termination)
744 * @param dest Buffer for the result
745 * @param src String to copy
746 * @param dsize Destination buffer size
747 * @retval num Destination string length
748 */
mutt_str_copy(char * dest,const char * src,size_t dsize)749 size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
750 {
751 if (!dest || (dsize == 0))
752 return 0;
753 if (!src)
754 {
755 dest[0] = '\0';
756 return 0;
757 }
758
759 char *dest0 = dest;
760 while ((--dsize > 0) && (*src != '\0'))
761 *dest++ = *src++;
762
763 *dest = '\0';
764 return dest - dest0;
765 }
766
767 /**
768 * mutt_str_skip_email_wsp - Skip over whitespace as defined by RFC5322
769 * @param s String to search
770 * @retval ptr
771 * - First non-whitespace character
772 * - Terminating NUL character, if the string was entirely whitespace
773 *
774 * This is used primarily for parsing header fields.
775 */
mutt_str_skip_email_wsp(const char * s)776 char *mutt_str_skip_email_wsp(const char *s)
777 {
778 if (!s)
779 return NULL;
780
781 for (; mutt_str_is_email_wsp(*s); s++)
782 ; // Do nothing
783
784 return (char *) s;
785 }
786
787 /**
788 * mutt_str_is_email_wsp - Is this a whitespace character (for an email header)
789 * @param c Character to test
790 * @retval true It is whitespcae
791 */
mutt_str_is_email_wsp(char c)792 bool mutt_str_is_email_wsp(char c)
793 {
794 return c && strchr(" \t\r\n", c);
795 }
796
797 /**
798 * mutt_str_lws_len - Measure the linear-white-space at the beginning of a string
799 * @param s String to check
800 * @param n Maximum number of characters to check
801 * @retval num Count of whitespace characters
802 *
803 * Count the number of whitespace characters at the beginning of a string.
804 * They can be `<space>`, `<tab>`, `<cr>` or `<lf>`.
805 */
mutt_str_lws_len(const char * s,size_t n)806 size_t mutt_str_lws_len(const char *s, size_t n)
807 {
808 if (!s)
809 return 0;
810
811 const char *p = s;
812 size_t len = n;
813
814 if (n == 0)
815 return 0;
816
817 for (; p < (s + n); p++)
818 {
819 if (!strchr(" \t\r\n", *p))
820 {
821 len = p - s;
822 break;
823 }
824 }
825
826 if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
827 len = 0;
828 return len;
829 }
830
831 /**
832 * mutt_str_lws_rlen - Measure the linear-white-space at the end of a string
833 * @param s String to check
834 * @param n Maximum number of characters to check
835 * @retval num Count of whitespace characters
836 *
837 * Count the number of whitespace characters at the end of a string.
838 * They can be `<space>`, `<tab>`, `<cr>` or `<lf>`.
839 */
mutt_str_lws_rlen(const char * s,size_t n)840 size_t mutt_str_lws_rlen(const char *s, size_t n)
841 {
842 if (!s)
843 return 0;
844
845 const char *p = s + n - 1;
846 size_t len = n;
847
848 if (n == 0)
849 return 0;
850
851 if (strchr("\r\n", *p)) /* LWS doesn't end with CRLF */
852 return 0;
853
854 for (; p >= s; p--)
855 {
856 if (!strchr(" \t\r\n", *p))
857 {
858 len = s + n - 1 - p;
859 break;
860 }
861 }
862
863 return len;
864 }
865
866 /**
867 * mutt_str_dequote_comment - Un-escape characters in an email address comment
868 * @param str String to be un-escaped
869 *
870 * @note The string is changed in-place
871 */
mutt_str_dequote_comment(char * str)872 void mutt_str_dequote_comment(char *str)
873 {
874 if (!str)
875 return;
876
877 char *w = str;
878
879 for (; *str; str++)
880 {
881 if (*str == '\\')
882 {
883 if (!*++str)
884 break; /* error? */
885 *w++ = *str;
886 }
887 else if (*str != '\"')
888 {
889 if (w != str)
890 *w = *str;
891 w++;
892 }
893 }
894 *w = '\0';
895 }
896
897 /**
898 * mutt_str_equal - Compare two strings
899 * @param a First string
900 * @param b Second string
901 * @retval true The strings are equal
902 * @retval false The strings are not equal
903 */
mutt_str_equal(const char * a,const char * b)904 bool mutt_str_equal(const char *a, const char *b)
905 {
906 return (a == b) || (mutt_str_cmp(a, b) == 0);
907 }
908
909 /**
910 * mutt_istr_equal - Compare two strings, ignoring case
911 * @param a First string
912 * @param b Second string
913 * @retval true The strings are equal
914 * @retval false The strings are not equal
915 */
mutt_istr_equal(const char * a,const char * b)916 bool mutt_istr_equal(const char *a, const char *b)
917 {
918 return (a == b) || (mutt_istr_cmp(a, b) == 0);
919 }
920
921 /**
922 * mutt_str_next_word - Find the next word in a string
923 * @param s String to examine
924 * @retval ptr Next word
925 *
926 * If the s is pointing to a word (non-space) is is skipped over.
927 * Then, any whitespace is skipped over.
928 *
929 * @note What is/isn't a word is determined by isspace()
930 */
mutt_str_next_word(const char * s)931 const char *mutt_str_next_word(const char *s)
932 {
933 if (!s)
934 return NULL;
935
936 while (*s && !IS_SPACE(*s))
937 s++;
938 SKIPWS(s);
939 return s;
940 }
941
942 /**
943 * mutt_strn_rfind - Find last instance of a substring
944 * @param haystack String to search through
945 * @param haystack_length Length of the string
946 * @param needle String to find
947 * @retval NULL String not found
948 * @retval ptr Location of string
949 *
950 * Return the last instance of needle in the haystack, or NULL.
951 * Like strstr(), only backwards, and for a limited haystack length.
952 */
mutt_strn_rfind(const char * haystack,size_t haystack_length,const char * needle)953 const char *mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
954 {
955 if (!haystack || (haystack_length == 0) || !needle)
956 return NULL;
957
958 int needle_length = strlen(needle);
959 const char *haystack_end = haystack + haystack_length - needle_length;
960
961 for (const char *p = haystack_end; p >= haystack; --p)
962 {
963 for (size_t i = 0; i < needle_length; i++)
964 {
965 if (p[i] != needle[i])
966 goto next;
967 }
968 return p;
969
970 next:;
971 }
972 return NULL;
973 }
974
975 /**
976 * mutt_str_is_ascii - Is a string ASCII (7-bit)?
977 * @param str String to examine
978 * @param len Length of string to examine
979 * @retval true There are no 8-bit chars
980 */
mutt_str_is_ascii(const char * str,size_t len)981 bool mutt_str_is_ascii(const char *str, size_t len)
982 {
983 if (!str)
984 return true;
985
986 for (; (*str != '\0') && (len > 0); str++, len--)
987 if ((*str & 0x80) != 0)
988 return false;
989
990 return true;
991 }
992
993 /**
994 * mutt_str_find_word - Find the end of a word (non-space)
995 * @param src String to search
996 * @retval ptr End of the word
997 *
998 * Skip to the end of the current word.
999 * Skip past any whitespace characters.
1000 *
1001 * @note If there aren't any more words, this will return a pointer to the
1002 * final NUL character.
1003 */
mutt_str_find_word(const char * src)1004 const char *mutt_str_find_word(const char *src)
1005 {
1006 if (!src)
1007 return NULL;
1008
1009 while (*src && strchr(" \t\n", *src))
1010 src++;
1011 while (*src && !strchr(" \t\n", *src))
1012 src++;
1013 return src;
1014 }
1015
1016 /**
1017 * mutt_str_getenv - Get an environment variable
1018 * @param name Environment variable to get
1019 * @retval ptr Value of variable
1020 * @retval NULL Variable isn't set, or is empty
1021 *
1022 * @warning The caller must not free the returned pointer.
1023 */
mutt_str_getenv(const char * name)1024 const char *mutt_str_getenv(const char *name)
1025 {
1026 if (!name)
1027 return NULL;
1028
1029 const char *val = getenv(name);
1030 if (val && (val[0] != '\0'))
1031 return val;
1032
1033 return NULL;
1034 }
1035
1036 /**
1037 * mutt_str_inline_replace - Replace the beginning of a string
1038 * @param buf Buffer to modify
1039 * @param buflen Length of buffer
1040 * @param xlen Length of string to overwrite
1041 * @param rstr Replacement string
1042 * @retval true Success
1043 *
1044 * String (`XX<OOOOOO>......`, 16, 2, `RRRR`) becomes `RRRR<OOOOOO>....`
1045 */
mutt_str_inline_replace(char * buf,size_t buflen,size_t xlen,const char * rstr)1046 bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
1047 {
1048 if (!buf || !rstr || (xlen >= buflen))
1049 return false;
1050
1051 size_t slen = mutt_str_len(buf + xlen);
1052 size_t rlen = mutt_str_len(rstr);
1053
1054 if ((slen + rlen) >= buflen)
1055 return false;
1056
1057 memmove(buf + rlen, buf + xlen, slen + 1);
1058 memmove(buf, rstr, rlen);
1059
1060 return true;
1061 }
1062
1063 /**
1064 * mutt_istr_remall - Remove all occurrences of substring, ignoring case
1065 * @param str String containing the substring
1066 * @param target Target substring for removal
1067 * @retval 0 String contained substring and substring was removed successfully
1068 * @retval 1 String did not contain substring
1069 */
mutt_istr_remall(char * str,const char * target)1070 int mutt_istr_remall(char *str, const char *target)
1071 {
1072 int rc = 1;
1073 if (!str || !target)
1074 return rc;
1075
1076 // Look through an ensure all instances of the substring are gone.
1077 while ((str = (char *) strcasestr(str, target)))
1078 {
1079 size_t target_len = mutt_str_len(target);
1080 memmove(str, str + target_len, 1 + strlen(str + target_len));
1081 rc = 0; // If we got here, then a substring existed and has been removed.
1082 }
1083
1084 return rc;
1085 }
1086
1087 #ifdef HAVE_VASPRINTF
1088 /**
1089 * mutt_str_asprintf - Format a string, allocating space as necessary
1090 * @param[out] strp New string saved here
1091 * @param[in] fmt Format string
1092 * @param[in] ... Format arguments
1093 * @retval num Characters written
1094 * @retval -1 Error
1095 */
mutt_str_asprintf(char ** strp,const char * fmt,...)1096 int mutt_str_asprintf(char **strp, const char *fmt, ...)
1097 {
1098 if (!strp || !fmt)
1099 return -1;
1100
1101 va_list ap;
1102 int n;
1103
1104 va_start(ap, fmt);
1105 n = vasprintf(strp, fmt, ap);
1106 va_end(ap);
1107
1108 /* GNU libc man page for vasprintf(3) states that the value of *strp
1109 * is undefined when the return code is -1. */
1110 if (n < 0)
1111 {
1112 mutt_error(_("Out of memory")); /* LCOV_EXCL_LINE */
1113 mutt_exit(1); /* LCOV_EXCL_LINE */
1114 }
1115
1116 if (n == 0)
1117 {
1118 /* NeoMutt convention is to use NULL for 0-length strings */
1119 FREE(strp);
1120 }
1121
1122 return n;
1123 }
1124 #else
1125 /* Allocate a C-string large enough to contain the formatted string.
1126 * This is essentially malloc+sprintf in one.
1127 */
mutt_str_asprintf(char ** strp,const char * fmt,...)1128 int mutt_str_asprintf(char **strp, const char *fmt, ...)
1129 {
1130 if (!strp || !fmt)
1131 return -1;
1132
1133 int rlen = 256;
1134
1135 *strp = mutt_mem_malloc(rlen);
1136 while (true)
1137 {
1138 va_list ap;
1139 va_start(ap, fmt);
1140 const int n = vsnprintf(*strp, rlen, fmt, ap);
1141 va_end(ap);
1142 if (n < 0)
1143 {
1144 FREE(strp);
1145 return n;
1146 }
1147
1148 if (n < rlen)
1149 {
1150 /* reduce space to just that which was used. note that 'n' does not
1151 * include the terminal nul char. */
1152 if (n == 0) /* convention is to use NULL for zero-length strings. */
1153 FREE(strp);
1154 else if (n != rlen - 1)
1155 mutt_mem_realloc(strp, n + 1);
1156 return n;
1157 }
1158 /* increase size and try again */
1159 rlen = n + 1;
1160 mutt_mem_realloc(strp, rlen);
1161 }
1162 /* not reached */
1163 }
1164 #endif /* HAVE_ASPRINTF */
1165