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