NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
string.c
Go to the documentation of this file.
1
33#include "config.h"
34#include <ctype.h>
35#include <errno.h>
36#include <stdarg.h> // IWYU pragma: keep
37#include <stdbool.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <strings.h>
42#include "exit.h"
43#include "logging2.h"
44#include "memory.h"
45#include "string2.h"
46#ifdef HAVE_SYSEXITS_H
47#include <sysexits.h>
48#endif
49
50#ifndef HAVE_STRCASESTR
57static char *strcasestr(const char *haystack, const char *needle)
58{
59 size_t haystackn = strlen(haystack);
60 size_t needlen = strlen(needle);
61
62 const char *p = haystack;
63 while (haystackn >= needlen)
64 {
65 if (strncasecmp(p, needle, needlen) == 0)
66 return (char *) p;
67 p++;
68 haystackn--;
69 }
70 return NULL;
71}
72#endif /* HAVE_STRCASESTR */
73
74#ifndef HAVE_STRSEP
83static char *strsep(char **stringp, const char *delim)
84{
85 if (!*stringp)
86 return NULL;
87
88 char *start = *stringp;
89 for (char *p = *stringp; *p != '\0'; p++)
90 {
91 for (const char *s = delim; *s != '\0'; s++)
92 {
93 if (*p == *s)
94 {
95 *p = '\0';
96 *stringp = p + 1;
97 return start;
98 }
99 }
100 }
101 *stringp = NULL;
102 return start;
103}
104#endif /* HAVE_STRSEP */
105
110{
112 const char *err_str;
113};
114
116static const struct SysExits SysExits[] = {
117#ifdef EX_USAGE
118 { 0xff & EX_USAGE, "Bad usage." },
119#endif
120#ifdef EX_DATAERR
121 { 0xff & EX_DATAERR, "Data format error." },
122#endif
123#ifdef EX_NOINPUT
124 { 0xff & EX_NOINPUT, "Can't open input." },
125#endif
126#ifdef EX_NOUSER
127 { 0xff & EX_NOUSER, "User unknown." },
128#endif
129#ifdef EX_NOHOST
130 { 0xff & EX_NOHOST, "Host unknown." },
131#endif
132#ifdef EX_UNAVAILABLE
133 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
134#endif
135#ifdef EX_SOFTWARE
136 { 0xff & EX_SOFTWARE, "Internal error." },
137#endif
138#ifdef EX_OSERR
139 { 0xff & EX_OSERR, "Operating system error." },
140#endif
141#ifdef EX_OSFILE
142 { 0xff & EX_OSFILE, "System file missing." },
143#endif
144#ifdef EX_CANTCREAT
145 { 0xff & EX_CANTCREAT, "Can't create output." },
146#endif
147#ifdef EX_IOERR
148 { 0xff & EX_IOERR, "I/O error." },
149#endif
150#ifdef EX_TEMPFAIL
151 { 0xff & EX_TEMPFAIL, "Deferred." },
152#endif
153#ifdef EX_PROTOCOL
154 { 0xff & EX_PROTOCOL, "Remote protocol error." },
155#endif
156#ifdef EX_NOPERM
157 { 0xff & EX_NOPERM, "Insufficient permission." },
158#endif
159#ifdef EX_CONFIG
160 { 0xff & EX_NOPERM, "Local configuration error." },
161#endif
162 { S_ERR, "Exec error." },
163};
164
170const char *mutt_str_sysexit(int err_num)
171{
172 for (size_t i = 0; i < mutt_array_size(SysExits); i++)
173 {
174 if (err_num == SysExits[i].err_num)
175 return SysExits[i].err_str;
176 }
177
178 return NULL;
179}
180
187char *mutt_str_sep(char **stringp, const char *delim)
188{
189 if (!stringp || !*stringp || !delim)
190 return NULL;
191 return strsep(stringp, delim);
192}
193
202static size_t startswith(const char *str, const char *prefix, bool match_case)
203{
204 if (!str || (str[0] == '\0') || !prefix || (prefix[0] == '\0'))
205 {
206 return 0;
207 }
208
209 const char *saved_prefix = prefix;
210 for (; *str && *prefix; str++, prefix++)
211 {
212 if (*str == *prefix)
213 continue;
214
215 if (!match_case && tolower(*str) == tolower(*prefix))
216 continue;
217
218 return 0;
219 }
220
221 return (*prefix == '\0') ? (prefix - saved_prefix) : 0;
222}
223
231size_t mutt_str_startswith(const char *str, const char *prefix)
232{
233 return startswith(str, prefix, true);
234}
235
243size_t mutt_istr_startswith(const char *str, const char *prefix)
244{
245 return startswith(str, prefix, false);
246}
247
254char *mutt_str_dup(const char *str)
255{
256 if (!str || (*str == '\0'))
257 return NULL;
258
259 char *p = strdup(str);
260 if (!p)
261 {
262 mutt_error("%s", strerror(errno)); // LCOV_EXCL_LINE
263 mutt_exit(1); // LCOV_EXCL_LINE
264 }
265 return p;
266}
267
281char *mutt_str_replace(char **p, const char *s)
282{
283 if (!p)
284 return NULL;
285 const char *tmp = *p;
286 *p = mutt_str_dup(s);
287 FREE(&tmp);
288 return *p;
289}
290
300void mutt_str_adjust(char **ptr)
301{
302 if (!ptr || !*ptr)
303 return;
304 MUTT_MEM_REALLOC(ptr, strlen(*ptr) + 1, char);
305}
306
314char *mutt_str_lower(char *str)
315{
316 if (!str)
317 return NULL;
318
319 char *p = str;
320
321 while (*p)
322 {
323 *p = tolower((unsigned char) *p);
324 p++;
325 }
326
327 return str;
328}
329
337char *mutt_str_upper(char *str)
338{
339 if (!str)
340 return NULL;
341
342 char *p = str;
343
344 while (*p)
345 {
346 *p = toupper((unsigned char) *p);
347 p++;
348 }
349
350 return str;
351}
352
361char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
362{
363 if (!src || !dest || (len == 0) || (dsize == 0))
364 return dest;
365
366 if (len > (dsize - 1))
367 len = dsize - 1;
368 memcpy(dest, src, len);
369 dest[len] = '\0';
370 return dest;
371}
372
381char *mutt_strn_dup(const char *begin, size_t len)
382{
383 if (!begin)
384 return NULL;
385
386 char *p = MUTT_MEM_MALLOC(len + 1, char);
387 memcpy(p, begin, len);
388 p[len] = '\0';
389 return p;
390}
391
400int mutt_str_cmp(const char *a, const char *b)
401{
402 return strcmp(NONULL(a), NONULL(b));
403}
404
413int mutt_istr_cmp(const char *a, const char *b)
414{
415 return strcasecmp(NONULL(a), NONULL(b));
416}
417
426bool mutt_strn_equal(const char *a, const char *b, size_t num)
427{
428 return strncmp(NONULL(a), NONULL(b), num) == 0;
429}
430
440int mutt_istrn_cmp(const char *a, const char *b, size_t num)
441{
442 return strncasecmp(NONULL(a), NONULL(b), num);
443}
444
454bool mutt_istrn_equal(const char *a, const char *b, size_t num)
455{
456 return strncasecmp(NONULL(a), NONULL(b), num) == 0;
457}
458
470const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
471{
472 if (!haystack || (haystack_length == 0) || !needle)
473 return NULL;
474
475 int needle_length = strlen(needle);
476 const char *haystack_end = haystack + haystack_length - needle_length;
477
478 for (const char *p = haystack_end; p >= haystack; p--)
479 {
480 for (size_t i = 0; i < needle_length; i++)
481 {
482 if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
483 goto next;
484 }
485 return p;
486
487 next:;
488 }
489 return NULL;
490}
491
497size_t mutt_str_len(const char *a)
498{
499 return a ? strlen(a) : 0;
500}
501
510int mutt_str_coll(const char *a, const char *b)
511{
512 return strcoll(NONULL(a), NONULL(b));
513}
514
522const char *mutt_istr_find(const char *haystack, const char *needle)
523{
524 if (!haystack)
525 return NULL;
526 if (!needle)
527 return haystack;
528
529 const char *p = NULL, *q = NULL;
530
531 while (*(p = haystack))
532 {
533 for (q = needle;
534 *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
535 p++, q++)
536 {
537 }
538 if ((*q == '\0'))
539 return haystack;
540 haystack++;
541 }
542 return NULL;
543}
544
552char *mutt_str_skip_whitespace(const char *p)
553{
554 if (!p)
555 return NULL;
556 SKIPWS(p);
557 return (char *) p;
558}
559
567{
568 if (!s)
569 return;
570
571 for (char *p = s + mutt_str_len(s) - 1; (p >= s) && isspace(*p); p--)
572 *p = '\0';
573}
574
582size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
583{
584 if (!dest || (dsize == 0))
585 return 0;
586 if (!src)
587 {
588 dest[0] = '\0';
589 return 0;
590 }
591
592 char *dest0 = dest;
593 while ((--dsize > 0) && (*src != '\0'))
594 *dest++ = *src++;
595
596 *dest = '\0';
597 return dest - dest0;
598}
599
609char *mutt_str_skip_email_wsp(const char *s)
610{
611 if (!s)
612 return NULL;
613
614 for (; mutt_str_is_email_wsp(*s); s++)
615 ; // Do nothing
616
617 return (char *) s;
618}
619
629size_t mutt_str_lws_len(const char *s, size_t n)
630{
631 if (!s)
632 return 0;
633
634 const char *p = s;
635 size_t len = n;
636
637 if (n == 0)
638 return 0;
639
640 for (; p < (s + n); p++)
641 {
642 if (!strchr(" \t\r\n", *p))
643 {
644 len = p - s;
645 break;
646 }
647 }
648
649 if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
650 len = 0;
651 return len;
652}
653
661bool mutt_str_equal(const char *a, const char *b)
662{
663 return (a == b) || (mutt_str_cmp(a, b) == 0);
664}
665
673bool mutt_istr_equal(const char *a, const char *b)
674{
675 return (a == b) || (mutt_istr_cmp(a, b) == 0);
676}
677
684bool mutt_str_is_ascii(const char *str, size_t len)
685{
686 if (!str)
687 return true;
688
689 for (; (*str != '\0') && (len > 0); str++, len--)
690 if ((*str & 0x80) != 0)
691 return false;
692
693 return true;
694}
695
707const char *mutt_str_find_word(const char *src)
708{
709 if (!src)
710 return NULL;
711
712 while (*src && strchr(" \t\n", *src))
713 src++;
714 while (*src && !strchr(" \t\n", *src))
715 src++;
716 return src;
717}
718
727const char *mutt_str_getenv(const char *name)
728{
729 if (!name)
730 return NULL;
731
732 const char *val = getenv(name);
733 if (val && (val[0] != '\0'))
734 return val;
735
736 return NULL;
737}
738
746int mutt_istr_remall(char *str, const char *target)
747{
748 int rc = 1;
749 if (!str || !target)
750 return rc;
751
752 // Look through an ensure all instances of the substring are gone.
753 while ((str = (char *) strcasestr(str, target)))
754 {
755 size_t target_len = mutt_str_len(target);
756 memmove(str, str + target_len, 1 + strlen(str + target_len));
757 rc = 0; // If we got here, then a substring existed and has been removed.
758 }
759
760 return rc;
761}
762
763#ifdef HAVE_VASPRINTF
772int mutt_str_asprintf(char **strp, const char *fmt, ...)
773{
774 if (!strp || !fmt)
775 return -1;
776
777 va_list ap;
778 int n;
779
780 va_start(ap, fmt);
781 n = vasprintf(strp, fmt, ap);
782 va_end(ap);
783
784 /* GNU libc man page for vasprintf(3) states that the value of *strp
785 * is undefined when the return code is -1. */
786 if (n < 0)
787 {
788 mutt_error("%s", strerror(errno)); /* LCOV_EXCL_LINE */
789 mutt_exit(1); /* LCOV_EXCL_LINE */
790 }
791
792 if (n == 0)
793 {
794 /* NeoMutt convention is to use NULL for 0-length strings */
795 FREE(strp); /* LCOV_EXCL_LINE */
796 }
797
798 return n;
799}
800#else
801/* Allocate a C-string large enough to contain the formatted string.
802 * This is essentially malloc+sprintf in one.
803 */
804int mutt_str_asprintf(char **strp, const char *fmt, ...)
805{
806 if (!strp || !fmt)
807 return -1;
808
809 int rlen = 256;
810
811 *strp = MUTT_MEM_MALLOC(rlen, char);
812 while (true)
813 {
814 va_list ap;
815 va_start(ap, fmt);
816 const int n = vsnprintf(*strp, rlen, fmt, ap);
817 va_end(ap);
818 if (n < 0)
819 {
820 FREE(strp);
821 return n;
822 }
823
824 if (n < rlen)
825 {
826 /* reduce space to just that which was used. note that 'n' does not
827 * include the terminal nul char. */
828 if (n == 0) /* convention is to use NULL for zero-length strings. */
829 FREE(strp);
830 else if (n != rlen - 1)
831 MUTT_MEM_REALLOC(strp, n + 1, char);
832 return n;
833 }
834 /* increase size and try again */
835 rlen = n + 1;
836 MUTT_MEM_REALLOC(strp, rlen, char);
837 }
838 /* not reached */
839}
840#endif /* HAVE_ASPRINTF */
841
850void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
851{
852 if (!buf || (buflen == 0) || !str)
853 return;
854
855 mutt_str_copy(buf, str, buflen);
856 for (; *buf != '\0'; buf++)
857 {
858 if (*buf == '_')
859 *buf = '-';
860 }
861}
862
888int mutt_str_inbox_cmp(const char *a, const char *b)
889{
890#define IS_INBOX(s) (mutt_istrn_equal(s, "inbox", 5) && !isalnum((s)[5]))
891#define CMP_INBOX(a, b) (IS_INBOX(b) - IS_INBOX(a))
892
893 /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
894 if ((a[0] == '+') && (b[0] == '+'))
895 {
896 return CMP_INBOX(a + 1, b + 1);
897 }
898
899 const char *a_end = strrchr(a, '/');
900 const char *b_end = strrchr(b, '/');
901
902 /* If one path contains a '/', but not the other */
903 if ((!a_end) ^ (!b_end))
904 return 0;
905
906 /* If neither path contains a '/' */
907 if (!a_end)
908 return 0;
909
910 /* Compare the subpaths */
911 size_t a_len = a_end - a;
912 size_t b_len = b_end - b;
913 size_t min = MIN(a_len, b_len);
914 int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
915 (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
916
917 if (!same)
918 return 0;
919
920 return CMP_INBOX(a + 1 + min, b + 1 + min);
921
922#undef CMP_INBOX
923#undef IS_INBOX
924}
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: exit.c:41
Leave the program NOW.
#define mutt_error(...)
Definition: logging2.h:93
int mutt_str_inbox_cmp(const char *a, const char *b)
Do two folders share the same path and one is an inbox -.
Definition: string.c:888
Logging Dispatcher.
Memory management wrappers.
#define FREE(x)
Definition: memory.h:55
#define MIN(a, b)
Definition: memory.h:32
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
#define MUTT_MEM_MALLOC(n, type)
Definition: memory.h:41
#define mutt_array_size(x)
Definition: memory.h:38
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:381
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:400
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:440
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:566
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:673
static size_t startswith(const char *str, const char *prefix, bool match_case)
Check whether a string starts with a prefix.
Definition: string.c:202
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:804
char * mutt_str_upper(char *str)
Convert all characters in the string to uppercase.
Definition: string.c:337
#define CMP_INBOX(a, b)
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:314
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:609
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:470
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:629
bool mutt_str_is_ascii(const char *str, size_t len)
Is a string ASCII (7-bit)?
Definition: string.c:684
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:413
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:661
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:426
static char * strsep(char **stringp, const char *delim)
Extract a token from a string.
Definition: string.c:83
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:707
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:361
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:746
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:727
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:522
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:231
void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
Hyphenate a snake-case string.
Definition: string.c:850
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:552
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition: string.c:300
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
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:582
static char * strcasestr(const char *haystack, const char *needle)
Find the first occurrence of needle in haystack, ignoring case.
Definition: string.c:57
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:243
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:454
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:170
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:281
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:510
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:187
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:104
#define SKIPWS(ch)
Definition: string2.h:45
Lookup table of error messages.
Definition: string.c:110
const char * err_str
Human-readable string for error.
Definition: string.c:112
int err_num
Error number, see errno(3)
Definition: string.c:111