NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
string.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <ctype.h>
32#include <stdarg.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <strings.h>
38#include "exit.h"
39#include "logging.h"
40#include "memory.h"
41#include "message.h"
42#include "string2.h"
43#ifdef HAVE_SYSEXITS_H
44#include <sysexits.h>
45#endif
46
47#ifndef HAVE_STRCASESTR
54static char *strcasestr(const char *haystack, const char *needle)
55{
56 size_t haystackn = strlen(haystack);
57 size_t needlen = strlen(needle);
58
59 const char *p = haystack;
60 while (haystackn >= needlen)
61 {
62 if (strncasecmp(p, needle, needlen) == 0)
63 return (char *) p;
64 p++;
65 haystackn--;
66 }
67 return NULL;
68}
69#endif /* HAVE_STRCASESTR */
70
71#ifndef HAVE_STRSEP
80static char *strsep(char **stringp, const char *delim)
81{
82 if (*stringp == NULL)
83 return NULL;
84
85 char *start = *stringp;
86 for (char *p = *stringp; *p != '\0'; p++)
87 {
88 for (const char *s = delim; *s != '\0'; s++)
89 {
90 if (*p == *s)
91 {
92 *p = '\0';
93 *stringp = p + 1;
94 return start;
95 }
96 }
97 }
98 *stringp = NULL;
99 return start;
100}
101#endif /* HAVE_STRSEP */
102
107{
109 const char *err_str;
110};
111
112static const struct SysExits sysexits[] = {
113#ifdef EX_USAGE
114 { 0xff & EX_USAGE, "Bad usage." },
115#endif
116#ifdef EX_DATAERR
117 { 0xff & EX_DATAERR, "Data format error." },
118#endif
119#ifdef EX_NOINPUT
120 { 0xff & EX_NOINPUT, "Can't open input." },
121#endif
122#ifdef EX_NOUSER
123 { 0xff & EX_NOUSER, "User unknown." },
124#endif
125#ifdef EX_NOHOST
126 { 0xff & EX_NOHOST, "Host unknown." },
127#endif
128#ifdef EX_UNAVAILABLE
129 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
130#endif
131#ifdef EX_SOFTWARE
132 { 0xff & EX_SOFTWARE, "Internal error." },
133#endif
134#ifdef EX_OSERR
135 { 0xff & EX_OSERR, "Operating system error." },
136#endif
137#ifdef EX_OSFILE
138 { 0xff & EX_OSFILE, "System file missing." },
139#endif
140#ifdef EX_CANTCREAT
141 { 0xff & EX_CANTCREAT, "Can't create output." },
142#endif
143#ifdef EX_IOERR
144 { 0xff & EX_IOERR, "I/O error." },
145#endif
146#ifdef EX_TEMPFAIL
147 { 0xff & EX_TEMPFAIL, "Deferred." },
148#endif
149#ifdef EX_PROTOCOL
150 { 0xff & EX_PROTOCOL, "Remote protocol error." },
151#endif
152#ifdef EX_NOPERM
153 { 0xff & EX_NOPERM, "Insufficient permission." },
154#endif
155#ifdef EX_CONFIG
156 { 0xff & EX_NOPERM, "Local configuration error." },
157#endif
158 { S_ERR, "Exec error." },
159};
160
166const char *mutt_str_sysexit(int err_num)
167{
168 for (size_t i = 0; i < mutt_array_size(sysexits); i++)
169 {
170 if (err_num == sysexits[i].err_num)
171 return sysexits[i].err_str;
172 }
173
174 return NULL;
175}
176
183char *mutt_str_sep(char **stringp, const char *delim)
184{
185 if (!stringp || !*stringp || !delim)
186 return NULL;
187 return strsep(stringp, delim);
188}
189
198static size_t startswith(const char *str, const char *prefix, bool match_case)
199{
200 if (!str || (str[0] == '\0') || !prefix || (prefix[0] == '\0'))
201 {
202 return 0;
203 }
204
205 const char *saved_prefix = prefix;
206 for (; *str && *prefix; str++, prefix++)
207 {
208 if (*str == *prefix)
209 continue;
210
211 if (!match_case && tolower(*str) == tolower(*prefix))
212 continue;
213
214 return 0;
215 }
216
217 return (*prefix == '\0') ? (prefix - saved_prefix) : 0;
218}
219
227size_t mutt_str_startswith(const char *str, const char *prefix)
228{
229 return startswith(str, prefix, true);
230}
231
239size_t mutt_istr_startswith(const char *str, const char *prefix)
240{
241 return startswith(str, prefix, false);
242}
243
250char *mutt_str_dup(const char *str)
251{
252 if (!str || (*str == '\0'))
253 return NULL;
254
255 return strdup(str);
256}
257
265char *mutt_str_cat(char *buf, size_t buflen, const char *s)
266{
267 if (!buf || (buflen == 0) || !s)
268 return buf;
269
270 char *p = buf;
271
272 buflen--; /* Space for the trailing '\0'. */
273
274 for (; (*buf != '\0') && buflen; buflen--)
275 buf++;
276 for (; *s && buflen; buflen--)
277 *buf++ = *s++;
278
279 *buf = '\0';
280
281 return p;
282}
283
294char *mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
295{
296 if (!d || (l == 0) || !s)
297 return d;
298
299 char *p = d;
300
301 l--; /* Space for the trailing '\0'. */
302
303 for (; *d && l; l--)
304 d++;
305 for (; *s && l && sl; l--, sl--)
306 *d++ = *s++;
307
308 *d = '\0';
309
310 return p;
311}
312
326char *mutt_str_replace(char **p, const char *s)
327{
328 if (!p)
329 return NULL;
330 const char *tmp = *p;
331 *p = mutt_str_dup(s);
332 FREE(&tmp);
333 return *p;
334}
335
346void mutt_str_append_item(char **str, const char *item, char sep)
347{
348 if (!str || !item)
349 return;
350
351 size_t sz = mutt_str_len(item);
352 size_t ssz = mutt_str_len(*str);
353
354 mutt_mem_realloc(str, ssz + (((ssz > 0) && (sep != '\0')) ? 1 : 0) + sz + 1);
355 char *p = *str + ssz;
356 if ((ssz > 0) && (sep != '\0'))
357 *p++ = sep;
358 memcpy(p, item, sz + 1);
359}
360
370void mutt_str_adjust(char **ptr)
371{
372 if (!ptr || !*ptr)
373 return;
374 mutt_mem_realloc(ptr, strlen(*ptr) + 1);
375}
376
384char *mutt_str_lower(char *str)
385{
386 if (!str)
387 return NULL;
388
389 char *p = str;
390
391 while (*p)
392 {
393 *p = tolower((unsigned char) *p);
394 p++;
395 }
396
397 return str;
398}
399
407char *mutt_str_upper(char *str)
408{
409 if (!str)
410 return NULL;
411
412 char *p = str;
413
414 while (*p)
415 {
416 *p = toupper((unsigned char) *p);
417 p++;
418 }
419
420 return str;
421}
422
431char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
432{
433 if (!src || !dest || (len == 0) || (dsize == 0))
434 return dest;
435
436 if (len > (dsize - 1))
437 len = dsize - 1;
438 memcpy(dest, src, len);
439 dest[len] = '\0';
440 return dest;
441}
442
451char *mutt_strn_dup(const char *begin, size_t len)
452{
453 if (!begin)
454 return NULL;
455
456 char *p = mutt_mem_malloc(len + 1);
457 memcpy(p, begin, len);
458 p[len] = '\0';
459 return p;
460}
461
470int mutt_str_cmp(const char *a, const char *b)
471{
472 return strcmp(NONULL(a), NONULL(b));
473}
474
483int mutt_istr_cmp(const char *a, const char *b)
484{
485 return strcasecmp(NONULL(a), NONULL(b));
486}
487
496bool mutt_strn_equal(const char *a, const char *b, size_t num)
497{
498 return strncmp(NONULL(a), NONULL(b), num) == 0;
499}
500
510int mutt_istrn_cmp(const char *a, const char *b, size_t num)
511{
512 return strncasecmp(NONULL(a), NONULL(b), num);
513}
514
524bool mutt_istrn_equal(const char *a, const char *b, size_t num)
525{
526 return strncasecmp(NONULL(a), NONULL(b), num) == 0;
527}
528
540const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
541{
542 if (!haystack || (haystack_length == 0) || !needle)
543 return NULL;
544
545 int needle_length = strlen(needle);
546 const char *haystack_end = haystack + haystack_length - needle_length;
547
548 for (const char *p = haystack_end; p >= haystack; --p)
549 {
550 for (size_t i = 0; i < needle_length; i++)
551 {
552 if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
553 goto next;
554 }
555 return p;
556
557 next:;
558 }
559 return NULL;
560}
561
567size_t mutt_str_len(const char *a)
568{
569 return a ? strlen(a) : 0;
570}
571
580int mutt_str_coll(const char *a, const char *b)
581{
582 return strcoll(NONULL(a), NONULL(b));
583}
584
592const char *mutt_istr_find(const char *haystack, const char *needle)
593{
594 if (!haystack)
595 return NULL;
596 if (!needle)
597 return haystack;
598
599 const char *p = NULL, *q = NULL;
600
601 while (*(p = haystack))
602 {
603 for (q = needle;
604 *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
605 p++, q++)
606 {
607 }
608 if ((*q == '\0'))
609 return haystack;
610 haystack++;
611 }
612 return NULL;
613}
614
622char *mutt_str_skip_whitespace(const char *p)
623{
624 if (!p)
625 return NULL;
626 SKIPWS(p);
627 return (char *) p;
628}
629
637{
638 if (!s)
639 return;
640
641 for (char *p = s + mutt_str_len(s) - 1; (p >= s) && IS_SPACE(*p); p--)
642 *p = '\0';
643}
644
652size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
653{
654 if (!dest || (dsize == 0))
655 return 0;
656 if (!src)
657 {
658 dest[0] = '\0';
659 return 0;
660 }
661
662 char *dest0 = dest;
663 while ((--dsize > 0) && (*src != '\0'))
664 *dest++ = *src++;
665
666 *dest = '\0';
667 return dest - dest0;
668}
669
679char *mutt_str_skip_email_wsp(const char *s)
680{
681 if (!s)
682 return NULL;
683
684 for (; mutt_str_is_email_wsp(*s); s++)
685 ; // Do nothing
686
687 return (char *) s;
688}
689
696{
697 return c && strchr(" \t\r\n", c);
698}
699
709size_t mutt_str_lws_len(const char *s, size_t n)
710{
711 if (!s)
712 return 0;
713
714 const char *p = s;
715 size_t len = n;
716
717 if (n == 0)
718 return 0;
719
720 for (; p < (s + n); p++)
721 {
722 if (!strchr(" \t\r\n", *p))
723 {
724 len = p - s;
725 break;
726 }
727 }
728
729 if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
730 len = 0;
731 return len;
732}
733
743size_t mutt_str_lws_rlen(const char *s, size_t n)
744{
745 if (!s)
746 return 0;
747
748 const char *p = s + n - 1;
749 size_t len = n;
750
751 if (n == 0)
752 return 0;
753
754 if (strchr("\r\n", *p)) /* LWS doesn't end with CRLF */
755 return 0;
756
757 for (; p >= s; p--)
758 {
759 if (!strchr(" \t\r\n", *p))
760 {
761 len = s + n - 1 - p;
762 break;
763 }
764 }
765
766 return len;
767}
768
776{
777 if (!str)
778 return;
779
780 char *w = str;
781
782 for (; *str; str++)
783 {
784 if (*str == '\\')
785 {
786 if (!*++str)
787 break; /* error? */
788 *w++ = *str;
789 }
790 else if (*str != '\"')
791 {
792 if (w != str)
793 *w = *str;
794 w++;
795 }
796 }
797 *w = '\0';
798}
799
807bool mutt_str_equal(const char *a, const char *b)
808{
809 return (a == b) || (mutt_str_cmp(a, b) == 0);
810}
811
819bool mutt_istr_equal(const char *a, const char *b)
820{
821 return (a == b) || (mutt_istr_cmp(a, b) == 0);
822}
823
834const char *mutt_str_next_word(const char *s)
835{
836 if (!s)
837 return NULL;
838
839 while (*s && !IS_SPACE(*s))
840 s++;
841 SKIPWS(s);
842 return s;
843}
844
856const char *mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
857{
858 if (!haystack || (haystack_length == 0) || !needle)
859 return NULL;
860
861 int needle_length = strlen(needle);
862 const char *haystack_end = haystack + haystack_length - needle_length;
863
864 for (const char *p = haystack_end; p >= haystack; --p)
865 {
866 for (size_t i = 0; i < needle_length; i++)
867 {
868 if (p[i] != needle[i])
869 goto next;
870 }
871 return p;
872
873 next:;
874 }
875 return NULL;
876}
877
884bool mutt_str_is_ascii(const char *str, size_t len)
885{
886 if (!str)
887 return true;
888
889 for (; (*str != '\0') && (len > 0); str++, len--)
890 if ((*str & 0x80) != 0)
891 return false;
892
893 return true;
894}
895
907const char *mutt_str_find_word(const char *src)
908{
909 if (!src)
910 return NULL;
911
912 while (*src && strchr(" \t\n", *src))
913 src++;
914 while (*src && !strchr(" \t\n", *src))
915 src++;
916 return src;
917}
918
927const char *mutt_str_getenv(const char *name)
928{
929 if (!name)
930 return NULL;
931
932 const char *val = getenv(name);
933 if (val && (val[0] != '\0'))
934 return val;
935
936 return NULL;
937}
938
949bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
950{
951 if (!buf || !rstr || (xlen >= buflen))
952 return false;
953
954 size_t slen = mutt_str_len(buf + xlen);
955 size_t rlen = mutt_str_len(rstr);
956
957 if ((slen + rlen) >= buflen)
958 return false;
959
960 memmove(buf + rlen, buf + xlen, slen + 1);
961 memmove(buf, rstr, rlen);
962
963 return true;
964}
965
973int mutt_istr_remall(char *str, const char *target)
974{
975 int rc = 1;
976 if (!str || !target)
977 return rc;
978
979 // Look through an ensure all instances of the substring are gone.
980 while ((str = (char *) strcasestr(str, target)))
981 {
982 size_t target_len = mutt_str_len(target);
983 memmove(str, str + target_len, 1 + strlen(str + target_len));
984 rc = 0; // If we got here, then a substring existed and has been removed.
985 }
986
987 return rc;
988}
989
990#ifdef HAVE_VASPRINTF
999int mutt_str_asprintf(char **strp, const char *fmt, ...)
1000{
1001 if (!strp || !fmt)
1002 return -1;
1003
1004 va_list ap;
1005 int n;
1006
1007 va_start(ap, fmt);
1008 n = vasprintf(strp, fmt, ap);
1009 va_end(ap);
1010
1011 /* GNU libc man page for vasprintf(3) states that the value of *strp
1012 * is undefined when the return code is -1. */
1013 if (n < 0)
1014 {
1015 mutt_error(_("Out of memory")); /* LCOV_EXCL_LINE */
1016 mutt_exit(1); /* LCOV_EXCL_LINE */
1017 }
1018
1019 if (n == 0)
1020 {
1021 /* NeoMutt convention is to use NULL for 0-length strings */
1022 FREE(strp);
1023 }
1024
1025 return n;
1026}
1027#else
1028/* Allocate a C-string large enough to contain the formatted string.
1029 * This is essentially malloc+sprintf in one.
1030 */
1031int mutt_str_asprintf(char **strp, const char *fmt, ...)
1032{
1033 if (!strp || !fmt)
1034 return -1;
1035
1036 int rlen = 256;
1037
1038 *strp = mutt_mem_malloc(rlen);
1039 while (true)
1040 {
1041 va_list ap;
1042 va_start(ap, fmt);
1043 const int n = vsnprintf(*strp, rlen, fmt, ap);
1044 va_end(ap);
1045 if (n < 0)
1046 {
1047 FREE(strp);
1048 return n;
1049 }
1050
1051 if (n < rlen)
1052 {
1053 /* reduce space to just that which was used. note that 'n' does not
1054 * include the terminal nul char. */
1055 if (n == 0) /* convention is to use NULL for zero-length strings. */
1056 FREE(strp);
1057 else if (n != rlen - 1)
1058 mutt_mem_realloc(strp, n + 1);
1059 return n;
1060 }
1061 /* increase size and try again */
1062 rlen = n + 1;
1063 mutt_mem_realloc(strp, rlen);
1064 }
1065 /* not reached */
1066}
1067#endif /* HAVE_ASPRINTF */
Leave the program NOW.
#define mutt_error(...)
Definition: logging.h:87
Logging Dispatcher.
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:248
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
Memory management wrappers.
#define FREE(x)
Definition: memory.h:43
#define mutt_array_size(x)
Definition: memory.h:36
Message logging.
#define _(a)
Definition: message.h:28
bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
Replace the beginning of a string.
Definition: string.c:949
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:470
int mutt_istrn_cmp(const char *a, const char *b, size_t num)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:510
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
void mutt_str_dequote_comment(char *str)
Un-escape characters in an email address comment.
Definition: string.c:775
static size_t startswith(const char *str, const char *prefix, bool match_case)
Check whether a string starts with a prefix.
Definition: string.c:198
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
char * mutt_str_upper(char *str)
Convert all characters in the string to uppercase.
Definition: string.c:407
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:384
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
const char * mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring, ignoring case.
Definition: string.c:540
size_t mutt_str_lws_len(const char *s, size_t n)
Measure the linear-white-space at the beginning of a string.
Definition: string.c:709
bool mutt_str_is_ascii(const char *str, size_t len)
Is a string ASCII (7-bit)?
Definition: string.c:884
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:346
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:483
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:496
static char * strsep(char **stringp, const char *delim)
Extract a token from a string.
Definition: string.c:80
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:907
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:294
char * mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
Copy a sub-string into a buffer.
Definition: string.c:431
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:973
static const struct SysExits sysexits[]
Definition: string.c:112
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:927
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:592
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:622
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition: string.c:370
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:695
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:652
static char * strcasestr(const char *haystack, const char *needle)
Find the first occurrence of needle in haystack, ignoring case.
Definition: string.c:54
const char * mutt_str_next_word(const char *s)
Find the next word in a string.
Definition: string.c:834
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:524
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:166
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
size_t mutt_str_lws_rlen(const char *s, size_t n)
Measure the linear-white-space at the end of a string.
Definition: string.c:743
const char * mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:856
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:580
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurance of any of delim characters in *stringp.
Definition: string.c:183
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
String manipulation functions.
#define S_ERR
Definition: string2.h:41
#define NONULL(x)
Definition: string2.h:37
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
Lookup table of error messages.
Definition: string.c:107
const char * err_str
Human-readable string for error.
Definition: string.c:109
int err_num
Error number, see errno(3)
Definition: string.c:108