NeoMutt  2024-03-23-142-g2b2e76
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
string.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <ctype.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 "logging2.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#ifndef HAVE_STRCASESTR
56static char *strcasestr(const char *haystack, const char *needle)
57{
58 size_t haystackn = strlen(haystack);
59 size_t needlen = strlen(needle);
60
61 const char *p = haystack;
62 while (haystackn >= needlen)
63 {
64 if (strncasecmp(p, needle, needlen) == 0)
65 return (char *) p;
66 p++;
67 haystackn--;
68 }
69 return NULL;
70}
71#endif /* HAVE_STRCASESTR */
72
73#ifndef HAVE_STRSEP
82static char *strsep(char **stringp, const char *delim)
83{
84 if (!*stringp)
85 return NULL;
86
87 char *start = *stringp;
88 for (char *p = *stringp; *p != '\0'; p++)
89 {
90 for (const char *s = delim; *s != '\0'; s++)
91 {
92 if (*p == *s)
93 {
94 *p = '\0';
95 *stringp = p + 1;
96 return start;
97 }
98 }
99 }
100 *stringp = NULL;
101 return start;
102}
103#endif /* HAVE_STRSEP */
104
109{
111 const char *err_str;
112};
113
115static const struct SysExits SysExits[] = {
116#ifdef EX_USAGE
117 { 0xff & EX_USAGE, "Bad usage." },
118#endif
119#ifdef EX_DATAERR
120 { 0xff & EX_DATAERR, "Data format error." },
121#endif
122#ifdef EX_NOINPUT
123 { 0xff & EX_NOINPUT, "Can't open input." },
124#endif
125#ifdef EX_NOUSER
126 { 0xff & EX_NOUSER, "User unknown." },
127#endif
128#ifdef EX_NOHOST
129 { 0xff & EX_NOHOST, "Host unknown." },
130#endif
131#ifdef EX_UNAVAILABLE
132 { 0xff & EX_UNAVAILABLE, "Service unavailable." },
133#endif
134#ifdef EX_SOFTWARE
135 { 0xff & EX_SOFTWARE, "Internal error." },
136#endif
137#ifdef EX_OSERR
138 { 0xff & EX_OSERR, "Operating system error." },
139#endif
140#ifdef EX_OSFILE
141 { 0xff & EX_OSFILE, "System file missing." },
142#endif
143#ifdef EX_CANTCREAT
144 { 0xff & EX_CANTCREAT, "Can't create output." },
145#endif
146#ifdef EX_IOERR
147 { 0xff & EX_IOERR, "I/O error." },
148#endif
149#ifdef EX_TEMPFAIL
150 { 0xff & EX_TEMPFAIL, "Deferred." },
151#endif
152#ifdef EX_PROTOCOL
153 { 0xff & EX_PROTOCOL, "Remote protocol error." },
154#endif
155#ifdef EX_NOPERM
156 { 0xff & EX_NOPERM, "Insufficient permission." },
157#endif
158#ifdef EX_CONFIG
159 { 0xff & EX_NOPERM, "Local configuration error." },
160#endif
161 { S_ERR, "Exec error." },
162};
163
169const char *mutt_str_sysexit(int err_num)
170{
171 for (size_t i = 0; i < mutt_array_size(SysExits); i++)
172 {
173 if (err_num == SysExits[i].err_num)
174 return SysExits[i].err_str;
175 }
176
177 return NULL;
178}
179
186char *mutt_str_sep(char **stringp, const char *delim)
187{
188 if (!stringp || !*stringp || !delim)
189 return NULL;
190 return strsep(stringp, delim);
191}
192
201static size_t startswith(const char *str, const char *prefix, bool match_case)
202{
203 if (!str || (str[0] == '\0') || !prefix || (prefix[0] == '\0'))
204 {
205 return 0;
206 }
207
208 const char *saved_prefix = prefix;
209 for (; *str && *prefix; str++, prefix++)
210 {
211 if (*str == *prefix)
212 continue;
213
214 if (!match_case && tolower(*str) == tolower(*prefix))
215 continue;
216
217 return 0;
218 }
219
220 return (*prefix == '\0') ? (prefix - saved_prefix) : 0;
221}
222
230size_t mutt_str_startswith(const char *str, const char *prefix)
231{
232 return startswith(str, prefix, true);
233}
234
242size_t mutt_istr_startswith(const char *str, const char *prefix)
243{
244 return startswith(str, prefix, false);
245}
246
253char *mutt_str_dup(const char *str)
254{
255 if (!str || (*str == '\0'))
256 return NULL;
257
258 return strdup(str);
259}
260
268char *mutt_str_cat(char *buf, size_t buflen, const char *s)
269{
270 if (!buf || (buflen == 0) || !s)
271 return buf;
272
273 char *p = buf;
274
275 buflen--; /* Space for the trailing '\0'. */
276
277 for (; (*buf != '\0') && buflen; buflen--)
278 buf++;
279 for (; *s && buflen; buflen--)
280 *buf++ = *s++;
281
282 *buf = '\0';
283
284 return p;
285}
286
297char *mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
298{
299 if (!d || (l == 0) || !s)
300 return d;
301
302 char *p = d;
303
304 l--; /* Space for the trailing '\0'. */
305
306 for (; *d && l; l--)
307 d++;
308 for (; *s && l && sl; l--, sl--)
309 *d++ = *s++;
310
311 *d = '\0';
312
313 return p;
314}
315
329char *mutt_str_replace(char **p, const char *s)
330{
331 if (!p)
332 return NULL;
333 const char *tmp = *p;
334 *p = mutt_str_dup(s);
335 FREE(&tmp);
336 return *p;
337}
338
348void mutt_str_adjust(char **ptr)
349{
350 if (!ptr || !*ptr)
351 return;
352 mutt_mem_realloc(ptr, strlen(*ptr) + 1);
353}
354
362char *mutt_str_lower(char *str)
363{
364 if (!str)
365 return NULL;
366
367 char *p = str;
368
369 while (*p)
370 {
371 *p = tolower((unsigned char) *p);
372 p++;
373 }
374
375 return str;
376}
377
385char *mutt_str_upper(char *str)
386{
387 if (!str)
388 return NULL;
389
390 char *p = str;
391
392 while (*p)
393 {
394 *p = toupper((unsigned char) *p);
395 p++;
396 }
397
398 return str;
399}
400
409char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
410{
411 if (!src || !dest || (len == 0) || (dsize == 0))
412 return dest;
413
414 if (len > (dsize - 1))
415 len = dsize - 1;
416 memcpy(dest, src, len);
417 dest[len] = '\0';
418 return dest;
419}
420
429char *mutt_strn_dup(const char *begin, size_t len)
430{
431 if (!begin)
432 return NULL;
433
434 char *p = mutt_mem_malloc(len + 1);
435 memcpy(p, begin, len);
436 p[len] = '\0';
437 return p;
438}
439
448int mutt_str_cmp(const char *a, const char *b)
449{
450 return strcmp(NONULL(a), NONULL(b));
451}
452
461int mutt_istr_cmp(const char *a, const char *b)
462{
463 return strcasecmp(NONULL(a), NONULL(b));
464}
465
474bool mutt_strn_equal(const char *a, const char *b, size_t num)
475{
476 return strncmp(NONULL(a), NONULL(b), num) == 0;
477}
478
488int mutt_istrn_cmp(const char *a, const char *b, size_t num)
489{
490 return strncasecmp(NONULL(a), NONULL(b), num);
491}
492
502bool mutt_istrn_equal(const char *a, const char *b, size_t num)
503{
504 return strncasecmp(NONULL(a), NONULL(b), num) == 0;
505}
506
518const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
519{
520 if (!haystack || (haystack_length == 0) || !needle)
521 return NULL;
522
523 int needle_length = strlen(needle);
524 const char *haystack_end = haystack + haystack_length - needle_length;
525
526 for (const char *p = haystack_end; p >= haystack; --p)
527 {
528 for (size_t i = 0; i < needle_length; i++)
529 {
530 if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
531 goto next;
532 }
533 return p;
534
535 next:;
536 }
537 return NULL;
538}
539
545size_t mutt_str_len(const char *a)
546{
547 return a ? strlen(a) : 0;
548}
549
558int mutt_str_coll(const char *a, const char *b)
559{
560 return strcoll(NONULL(a), NONULL(b));
561}
562
570const char *mutt_istr_find(const char *haystack, const char *needle)
571{
572 if (!haystack)
573 return NULL;
574 if (!needle)
575 return haystack;
576
577 const char *p = NULL, *q = NULL;
578
579 while (*(p = haystack))
580 {
581 for (q = needle;
582 *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
583 p++, q++)
584 {
585 }
586 if ((*q == '\0'))
587 return haystack;
588 haystack++;
589 }
590 return NULL;
591}
592
600char *mutt_str_skip_whitespace(const char *p)
601{
602 if (!p)
603 return NULL;
604 SKIPWS(p);
605 return (char *) p;
606}
607
615{
616 if (!s)
617 return;
618
619 for (char *p = s + mutt_str_len(s) - 1; (p >= s) && isspace(*p); p--)
620 *p = '\0';
621}
622
630size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
631{
632 if (!dest || (dsize == 0))
633 return 0;
634 if (!src)
635 {
636 dest[0] = '\0';
637 return 0;
638 }
639
640 char *dest0 = dest;
641 while ((--dsize > 0) && (*src != '\0'))
642 *dest++ = *src++;
643
644 *dest = '\0';
645 return dest - dest0;
646}
647
657char *mutt_str_skip_email_wsp(const char *s)
658{
659 if (!s)
660 return NULL;
661
662 for (; mutt_str_is_email_wsp(*s); s++)
663 ; // Do nothing
664
665 return (char *) s;
666}
667
677size_t mutt_str_lws_len(const char *s, size_t n)
678{
679 if (!s)
680 return 0;
681
682 const char *p = s;
683 size_t len = n;
684
685 if (n == 0)
686 return 0;
687
688 for (; p < (s + n); p++)
689 {
690 if (!strchr(" \t\r\n", *p))
691 {
692 len = p - s;
693 break;
694 }
695 }
696
697 if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
698 len = 0;
699 return len;
700}
701
709bool mutt_str_equal(const char *a, const char *b)
710{
711 return (a == b) || (mutt_str_cmp(a, b) == 0);
712}
713
721bool mutt_istr_equal(const char *a, const char *b)
722{
723 return (a == b) || (mutt_istr_cmp(a, b) == 0);
724}
725
732bool mutt_str_is_ascii(const char *str, size_t len)
733{
734 if (!str)
735 return true;
736
737 for (; (*str != '\0') && (len > 0); str++, len--)
738 if ((*str & 0x80) != 0)
739 return false;
740
741 return true;
742}
743
755const char *mutt_str_find_word(const char *src)
756{
757 if (!src)
758 return NULL;
759
760 while (*src && strchr(" \t\n", *src))
761 src++;
762 while (*src && !strchr(" \t\n", *src))
763 src++;
764 return src;
765}
766
775const char *mutt_str_getenv(const char *name)
776{
777 if (!name)
778 return NULL;
779
780 const char *val = getenv(name);
781 if (val && (val[0] != '\0'))
782 return val;
783
784 return NULL;
785}
786
794int mutt_istr_remall(char *str, const char *target)
795{
796 int rc = 1;
797 if (!str || !target)
798 return rc;
799
800 // Look through an ensure all instances of the substring are gone.
801 while ((str = (char *) strcasestr(str, target)))
802 {
803 size_t target_len = mutt_str_len(target);
804 memmove(str, str + target_len, 1 + strlen(str + target_len));
805 rc = 0; // If we got here, then a substring existed and has been removed.
806 }
807
808 return rc;
809}
810
811#ifdef HAVE_VASPRINTF
820int mutt_str_asprintf(char **strp, const char *fmt, ...)
821{
822 if (!strp || !fmt)
823 return -1;
824
825 va_list ap;
826 int n;
827
828 va_start(ap, fmt);
829 n = vasprintf(strp, fmt, ap);
830 va_end(ap);
831
832 /* GNU libc man page for vasprintf(3) states that the value of *strp
833 * is undefined when the return code is -1. */
834 if (n < 0)
835 {
836 mutt_error(_("Out of memory")); /* LCOV_EXCL_LINE */
837 mutt_exit(1); /* LCOV_EXCL_LINE */
838 }
839
840 if (n == 0)
841 {
842 /* NeoMutt convention is to use NULL for 0-length strings */
843 FREE(strp); /* LCOV_EXCL_LINE */
844 }
845
846 return n;
847}
848#else
849/* Allocate a C-string large enough to contain the formatted string.
850 * This is essentially malloc+sprintf in one.
851 */
852int mutt_str_asprintf(char **strp, const char *fmt, ...)
853{
854 if (!strp || !fmt)
855 return -1;
856
857 int rlen = 256;
858
859 *strp = mutt_mem_malloc(rlen);
860 while (true)
861 {
862 va_list ap;
863 va_start(ap, fmt);
864 const int n = vsnprintf(*strp, rlen, fmt, ap);
865 va_end(ap);
866 if (n < 0)
867 {
868 FREE(strp);
869 return n;
870 }
871
872 if (n < rlen)
873 {
874 /* reduce space to just that which was used. note that 'n' does not
875 * include the terminal nul char. */
876 if (n == 0) /* convention is to use NULL for zero-length strings. */
877 FREE(strp);
878 else if (n != rlen - 1)
879 mutt_mem_realloc(strp, n + 1);
880 return n;
881 }
882 /* increase size and try again */
883 rlen = n + 1;
884 mutt_mem_realloc(strp, rlen);
885 }
886 /* not reached */
887}
888#endif /* HAVE_ASPRINTF */
889
898void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
899{
900 if (!buf || (buflen == 0) || !str)
901 return;
902
903 mutt_str_copy(buf, str, buflen);
904 for (; *buf != '\0'; buf++)
905 {
906 if (*buf == '_')
907 *buf = '-';
908 }
909}
Leave the program NOW.
#define mutt_error(...)
Definition: logging2.h:92
Logging Dispatcher.
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:268
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
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:429
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:448
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:488
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:614
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:721
static size_t startswith(const char *str, const char *prefix, bool match_case)
Check whether a string starts with a prefix.
Definition: string.c:201
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:852
char * mutt_str_upper(char *str)
Convert all characters in the string to uppercase.
Definition: string.c:385
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:362
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:657
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:518
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:677
bool mutt_str_is_ascii(const char *str, size_t len)
Is a string ASCII (7-bit)?
Definition: string.c:732
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:461
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:709
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:474
static char * strsep(char **stringp, const char *delim)
Extract a token from a string.
Definition: string.c:82
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:755
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:297
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:409
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:794
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:775
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:570
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
Hyphenate a snake-case string.
Definition: string.c:898
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:600
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition: string.c:348
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:545
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:630
static char * strcasestr(const char *haystack, const char *needle)
Find the first occurrence of needle in haystack, ignoring case.
Definition: string.c:56
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
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:502
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:169
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:329
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:558
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:186
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:268
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:105
#define SKIPWS(ch)
Definition: string2.h:45
Lookup table of error messages.
Definition: string.c:109
const char * err_str
Human-readable string for error.
Definition: string.c:111
int err_num
Error number, see errno(3)
Definition: string.c:110