NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "logging2.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)
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
113static const struct SysExits SysExits[] = {
114#ifdef EX_USAGE
115 { 0xff & EX_USAGE, "Bad usage." },
116#endif
117#ifdef EX_DATAERR
118 { 0xff & EX_DATAERR, "Data format error." },
119#endif
120#ifdef EX_NOINPUT
121 { 0xff & EX_NOINPUT, "Can't open input." },
122#endif
123#ifdef EX_NOUSER
124 { 0xff & EX_NOUSER, "User unknown." },
125#endif
126#ifdef EX_NOHOST
127 { 0xff & EX_NOHOST, "Host unknown." },
128#endif
129#ifdef EX_UNAVAILABLE
130 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
131#endif
132#ifdef EX_SOFTWARE
133 { 0xff & EX_SOFTWARE, "Internal error." },
134#endif
135#ifdef EX_OSERR
136 { 0xff & EX_OSERR, "Operating system error." },
137#endif
138#ifdef EX_OSFILE
139 { 0xff & EX_OSFILE, "System file missing." },
140#endif
141#ifdef EX_CANTCREAT
142 { 0xff & EX_CANTCREAT, "Can't create output." },
143#endif
144#ifdef EX_IOERR
145 { 0xff & EX_IOERR, "I/O error." },
146#endif
147#ifdef EX_TEMPFAIL
148 { 0xff & EX_TEMPFAIL, "Deferred." },
149#endif
150#ifdef EX_PROTOCOL
151 { 0xff & EX_PROTOCOL, "Remote protocol error." },
152#endif
153#ifdef EX_NOPERM
154 { 0xff & EX_NOPERM, "Insufficient permission." },
155#endif
156#ifdef EX_CONFIG
157 { 0xff & EX_NOPERM, "Local configuration error." },
158#endif
159 { S_ERR, "Exec error." },
160};
161
167const char *mutt_str_sysexit(int err_num)
168{
169 for (size_t i = 0; i < mutt_array_size(SysExits); i++)
170 {
171 if (err_num == SysExits[i].err_num)
172 return SysExits[i].err_str;
173 }
174
175 return NULL;
176}
177
184char *mutt_str_sep(char **stringp, const char *delim)
185{
186 if (!stringp || !*stringp || !delim)
187 return NULL;
188 return strsep(stringp, delim);
189}
190
199static size_t startswith(const char *str, const char *prefix, bool match_case)
200{
201 if (!str || (str[0] == '\0') || !prefix || (prefix[0] == '\0'))
202 {
203 return 0;
204 }
205
206 const char *saved_prefix = prefix;
207 for (; *str && *prefix; str++, prefix++)
208 {
209 if (*str == *prefix)
210 continue;
211
212 if (!match_case && tolower(*str) == tolower(*prefix))
213 continue;
214
215 return 0;
216 }
217
218 return (*prefix == '\0') ? (prefix - saved_prefix) : 0;
219}
220
228size_t mutt_str_startswith(const char *str, const char *prefix)
229{
230 return startswith(str, prefix, true);
231}
232
240size_t mutt_istr_startswith(const char *str, const char *prefix)
241{
242 return startswith(str, prefix, false);
243}
244
251char *mutt_str_dup(const char *str)
252{
253 if (!str || (*str == '\0'))
254 return NULL;
255
256 return strdup(str);
257}
258
266char *mutt_str_cat(char *buf, size_t buflen, const char *s)
267{
268 if (!buf || (buflen == 0) || !s)
269 return buf;
270
271 char *p = buf;
272
273 buflen--; /* Space for the trailing '\0'. */
274
275 for (; (*buf != '\0') && buflen; buflen--)
276 buf++;
277 for (; *s && buflen; buflen--)
278 *buf++ = *s++;
279
280 *buf = '\0';
281
282 return p;
283}
284
295char *mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
296{
297 if (!d || (l == 0) || !s)
298 return d;
299
300 char *p = d;
301
302 l--; /* Space for the trailing '\0'. */
303
304 for (; *d && l; l--)
305 d++;
306 for (; *s && l && sl; l--, sl--)
307 *d++ = *s++;
308
309 *d = '\0';
310
311 return p;
312}
313
327char *mutt_str_replace(char **p, const char *s)
328{
329 if (!p)
330 return NULL;
331 const char *tmp = *p;
332 *p = mutt_str_dup(s);
333 FREE(&tmp);
334 return *p;
335}
336
347void mutt_str_append_item(char **str, const char *item, char sep)
348{
349 if (!str || !item)
350 return;
351
352 size_t sz = mutt_str_len(item);
353 size_t ssz = mutt_str_len(*str);
354
355 mutt_mem_realloc(str, ssz + (((ssz > 0) && (sep != '\0')) ? 1 : 0) + sz + 1);
356 char *p = *str + ssz;
357 if ((ssz > 0) && (sep != '\0'))
358 *p++ = sep;
359 memcpy(p, item, sz + 1);
360}
361
371void mutt_str_adjust(char **ptr)
372{
373 if (!ptr || !*ptr)
374 return;
375 mutt_mem_realloc(ptr, strlen(*ptr) + 1);
376}
377
385char *mutt_str_lower(char *str)
386{
387 if (!str)
388 return NULL;
389
390 char *p = str;
391
392 while (*p)
393 {
394 *p = tolower((unsigned char) *p);
395 p++;
396 }
397
398 return str;
399}
400
408char *mutt_str_upper(char *str)
409{
410 if (!str)
411 return NULL;
412
413 char *p = str;
414
415 while (*p)
416 {
417 *p = toupper((unsigned char) *p);
418 p++;
419 }
420
421 return str;
422}
423
432char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
433{
434 if (!src || !dest || (len == 0) || (dsize == 0))
435 return dest;
436
437 if (len > (dsize - 1))
438 len = dsize - 1;
439 memcpy(dest, src, len);
440 dest[len] = '\0';
441 return dest;
442}
443
452char *mutt_strn_dup(const char *begin, size_t len)
453{
454 if (!begin)
455 return NULL;
456
457 char *p = mutt_mem_malloc(len + 1);
458 memcpy(p, begin, len);
459 p[len] = '\0';
460 return p;
461}
462
471int mutt_str_cmp(const char *a, const char *b)
472{
473 return strcmp(NONULL(a), NONULL(b));
474}
475
484int mutt_istr_cmp(const char *a, const char *b)
485{
486 return strcasecmp(NONULL(a), NONULL(b));
487}
488
497bool mutt_strn_equal(const char *a, const char *b, size_t num)
498{
499 return strncmp(NONULL(a), NONULL(b), num) == 0;
500}
501
511int mutt_istrn_cmp(const char *a, const char *b, size_t num)
512{
513 return strncasecmp(NONULL(a), NONULL(b), num);
514}
515
525bool mutt_istrn_equal(const char *a, const char *b, size_t num)
526{
527 return strncasecmp(NONULL(a), NONULL(b), num) == 0;
528}
529
541const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
542{
543 if (!haystack || (haystack_length == 0) || !needle)
544 return NULL;
545
546 int needle_length = strlen(needle);
547 const char *haystack_end = haystack + haystack_length - needle_length;
548
549 for (const char *p = haystack_end; p >= haystack; --p)
550 {
551 for (size_t i = 0; i < needle_length; i++)
552 {
553 if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
554 goto next;
555 }
556 return p;
557
558 next:;
559 }
560 return NULL;
561}
562
568size_t mutt_str_len(const char *a)
569{
570 return a ? strlen(a) : 0;
571}
572
581int mutt_str_coll(const char *a, const char *b)
582{
583 return strcoll(NONULL(a), NONULL(b));
584}
585
593const char *mutt_istr_find(const char *haystack, const char *needle)
594{
595 if (!haystack)
596 return NULL;
597 if (!needle)
598 return haystack;
599
600 const char *p = NULL, *q = NULL;
601
602 while (*(p = haystack))
603 {
604 for (q = needle;
605 *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
606 p++, q++)
607 {
608 }
609 if ((*q == '\0'))
610 return haystack;
611 haystack++;
612 }
613 return NULL;
614}
615
623char *mutt_str_skip_whitespace(const char *p)
624{
625 if (!p)
626 return NULL;
627 SKIPWS(p);
628 return (char *) p;
629}
630
638{
639 if (!s)
640 return;
641
642 for (char *p = s + mutt_str_len(s) - 1; (p >= s) && isspace(*p); p--)
643 *p = '\0';
644}
645
653size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
654{
655 if (!dest || (dsize == 0))
656 return 0;
657 if (!src)
658 {
659 dest[0] = '\0';
660 return 0;
661 }
662
663 char *dest0 = dest;
664 while ((--dsize > 0) && (*src != '\0'))
665 *dest++ = *src++;
666
667 *dest = '\0';
668 return dest - dest0;
669}
670
680char *mutt_str_skip_email_wsp(const char *s)
681{
682 if (!s)
683 return NULL;
684
685 for (; mutt_str_is_email_wsp(*s); s++)
686 ; // Do nothing
687
688 return (char *) s;
689}
690
700size_t mutt_str_lws_len(const char *s, size_t n)
701{
702 if (!s)
703 return 0;
704
705 const char *p = s;
706 size_t len = n;
707
708 if (n == 0)
709 return 0;
710
711 for (; p < (s + n); p++)
712 {
713 if (!strchr(" \t\r\n", *p))
714 {
715 len = p - s;
716 break;
717 }
718 }
719
720 if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
721 len = 0;
722 return len;
723}
724
734size_t mutt_str_lws_rlen(const char *s, size_t n)
735{
736 if (!s)
737 return 0;
738
739 const char *p = s + n - 1;
740 size_t len = n;
741
742 if (n == 0)
743 return 0;
744
745 if (strchr("\r\n", *p)) /* LWS doesn't end with CRLF */
746 return 0;
747
748 for (; p >= s; p--)
749 {
750 if (!strchr(" \t\r\n", *p))
751 {
752 len = s + n - 1 - p;
753 break;
754 }
755 }
756
757 return len;
758}
759
767{
768 if (!str)
769 return;
770
771 char *w = str;
772
773 for (; *str; str++)
774 {
775 if (*str == '\\')
776 {
777 if (!*++str)
778 break; /* error? */
779 *w++ = *str;
780 }
781 else if (*str != '\"')
782 {
783 if (w != str)
784 *w = *str;
785 w++;
786 }
787 }
788 *w = '\0';
789}
790
798bool mutt_str_equal(const char *a, const char *b)
799{
800 return (a == b) || (mutt_str_cmp(a, b) == 0);
801}
802
810bool mutt_istr_equal(const char *a, const char *b)
811{
812 return (a == b) || (mutt_istr_cmp(a, b) == 0);
813}
814
825const char *mutt_str_next_word(const char *s)
826{
827 if (!s)
828 return NULL;
829
830 while (*s && !isspace(*s))
831 s++;
832 SKIPWS(s);
833 return s;
834}
835
847const char *mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
848{
849 if (!haystack || (haystack_length == 0) || !needle)
850 return NULL;
851
852 int needle_length = strlen(needle);
853 const char *haystack_end = haystack + haystack_length - needle_length;
854
855 for (const char *p = haystack_end; p >= haystack; --p)
856 {
857 for (size_t i = 0; i < needle_length; i++)
858 {
859 if (p[i] != needle[i])
860 goto next;
861 }
862 return p;
863
864 next:;
865 }
866 return NULL;
867}
868
875bool mutt_str_is_ascii(const char *str, size_t len)
876{
877 if (!str)
878 return true;
879
880 for (; (*str != '\0') && (len > 0); str++, len--)
881 if ((*str & 0x80) != 0)
882 return false;
883
884 return true;
885}
886
898const char *mutt_str_find_word(const char *src)
899{
900 if (!src)
901 return NULL;
902
903 while (*src && strchr(" \t\n", *src))
904 src++;
905 while (*src && !strchr(" \t\n", *src))
906 src++;
907 return src;
908}
909
918const char *mutt_str_getenv(const char *name)
919{
920 if (!name)
921 return NULL;
922
923 const char *val = getenv(name);
924 if (val && (val[0] != '\0'))
925 return val;
926
927 return NULL;
928}
929
940bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
941{
942 if (!buf || !rstr || (xlen >= buflen))
943 return false;
944
945 size_t slen = mutt_str_len(buf + xlen);
946 size_t rlen = mutt_str_len(rstr);
947
948 if ((slen + rlen) >= buflen)
949 return false;
950
951 memmove(buf + rlen, buf + xlen, slen + 1);
952 memmove(buf, rstr, rlen);
953
954 return true;
955}
956
964int mutt_istr_remall(char *str, const char *target)
965{
966 int rc = 1;
967 if (!str || !target)
968 return rc;
969
970 // Look through an ensure all instances of the substring are gone.
971 while ((str = (char *) strcasestr(str, target)))
972 {
973 size_t target_len = mutt_str_len(target);
974 memmove(str, str + target_len, 1 + strlen(str + target_len));
975 rc = 0; // If we got here, then a substring existed and has been removed.
976 }
977
978 return rc;
979}
980
981#ifdef HAVE_VASPRINTF
990int mutt_str_asprintf(char **strp, const char *fmt, ...)
991{
992 if (!strp || !fmt)
993 return -1;
994
995 va_list ap;
996 int n;
997
998 va_start(ap, fmt);
999 n = vasprintf(strp, fmt, ap);
1000 va_end(ap);
1001
1002 /* GNU libc man page for vasprintf(3) states that the value of *strp
1003 * is undefined when the return code is -1. */
1004 if (n < 0)
1005 {
1006 mutt_error(_("Out of memory")); /* LCOV_EXCL_LINE */
1007 mutt_exit(1); /* LCOV_EXCL_LINE */
1008 }
1009
1010 if (n == 0)
1011 {
1012 /* NeoMutt convention is to use NULL for 0-length strings */
1013 FREE(strp); /* LCOV_EXCL_LINE */
1014 }
1015
1016 return n;
1017}
1018#else
1019/* Allocate a C-string large enough to contain the formatted string.
1020 * This is essentially malloc+sprintf in one.
1021 */
1022int mutt_str_asprintf(char **strp, const char *fmt, ...)
1023{
1024 if (!strp || !fmt)
1025 return -1;
1026
1027 int rlen = 256;
1028
1029 *strp = mutt_mem_malloc(rlen);
1030 while (true)
1031 {
1032 va_list ap;
1033 va_start(ap, fmt);
1034 const int n = vsnprintf(*strp, rlen, fmt, ap);
1035 va_end(ap);
1036 if (n < 0)
1037 {
1038 FREE(strp);
1039 return n;
1040 }
1041
1042 if (n < rlen)
1043 {
1044 /* reduce space to just that which was used. note that 'n' does not
1045 * include the terminal nul char. */
1046 if (n == 0) /* convention is to use NULL for zero-length strings. */
1047 FREE(strp);
1048 else if (n != rlen - 1)
1049 mutt_mem_realloc(strp, n + 1);
1050 return n;
1051 }
1052 /* increase size and try again */
1053 rlen = n + 1;
1054 mutt_mem_realloc(strp, rlen);
1055 }
1056 /* not reached */
1057}
1058#endif /* HAVE_ASPRINTF */
1059
1068void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
1069{
1070 if (!buf || (buflen == 0) || !str)
1071 return;
1072
1073 mutt_str_copy(buf, str, buflen);
1074 for (; *buf != '\0'; buf++)
1075 {
1076 if (*buf == '_')
1077 *buf = '-';
1078 }
1079}
Leave the program NOW.
#define mutt_error(...)
Definition: logging2.h:92
Logging Dispatcher.
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:228
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:45
#define mutt_array_size(x)
Definition: memory.h:38
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:940
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:452
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:471
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:511
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:637
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
void mutt_str_dequote_comment(char *str)
Un-escape characters in an email address comment.
Definition: string.c:766
static size_t startswith(const char *str, const char *prefix, bool match_case)
Check whether a string starts with a prefix.
Definition: string.c:199
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
char * mutt_str_upper(char *str)
Convert all characters in the string to uppercase.
Definition: string.c:408
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:385
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:680
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:541
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:700
bool mutt_str_is_ascii(const char *str, size_t len)
Is a string ASCII (7-bit)?
Definition: string.c:875
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:347
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:484
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
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:497
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:898
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:295
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:432
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:964
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:918
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:593
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
Hyphenate a snake-case string.
Definition: string.c:1068
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:623
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition: string.c:371
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
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:825
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:240
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:525
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:167
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
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:734
const char * mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:847
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:581
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:184
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:266
String manipulation functions.
#define S_ERR
Definition: string2.h:40
#define NONULL(x)
Definition: string2.h:37
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string2.h:111
#define SKIPWS(ch)
Definition: string2.h:45
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