NeoMutt  2022-04-29-81-g9c5a59
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
54 static 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
80 static 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 
106 struct SysExits
107 {
108  int err_num;
109  const char *err_str;
110 };
111 
112 static 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 
166 const 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 
183 char *mutt_str_sep(char **stringp, const char *delim)
184 {
185  if (!stringp || !*stringp || !delim)
186  return NULL;
187  return strsep(stringp, delim);
188 }
189 
198 static 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 
227 size_t mutt_str_startswith(const char *str, const char *prefix)
228 {
229  return startswith(str, prefix, true);
230 }
231 
239 size_t mutt_istr_startswith(const char *str, const char *prefix)
240 {
241  return startswith(str, prefix, false);
242 }
243 
250 char *mutt_str_dup(const char *str)
251 {
252  if (!str || (*str == '\0'))
253  return NULL;
254 
255  return strdup(str);
256 }
257 
265 char *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 
294 char *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 
326 char *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 
346 void 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 
370 void mutt_str_adjust(char **ptr)
371 {
372  if (!ptr || !*ptr)
373  return;
374  mutt_mem_realloc(ptr, strlen(*ptr) + 1);
375 }
376 
384 char *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 
408 char *mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
409 {
410  if (!src || !dest || (len == 0) || (dsize == 0))
411  return dest;
412 
413  if (len > (dsize - 1))
414  len = dsize - 1;
415  memcpy(dest, src, len);
416  dest[len] = '\0';
417  return dest;
418 }
419 
428 char *mutt_strn_dup(const char *begin, size_t len)
429 {
430  if (!begin)
431  return NULL;
432 
433  char *p = mutt_mem_malloc(len + 1);
434  memcpy(p, begin, len);
435  p[len] = '\0';
436  return p;
437 }
438 
447 int mutt_str_cmp(const char *a, const char *b)
448 {
449  return strcmp(NONULL(a), NONULL(b));
450 }
451 
460 int mutt_istr_cmp(const char *a, const char *b)
461 {
462  return strcasecmp(NONULL(a), NONULL(b));
463 }
464 
473 bool mutt_strn_equal(const char *a, const char *b, size_t num)
474 {
475  return strncmp(NONULL(a), NONULL(b), num) == 0;
476 }
477 
487 int mutt_istrn_cmp(const char *a, const char *b, size_t num)
488 {
489  return strncasecmp(NONULL(a), NONULL(b), num);
490 }
491 
501 bool mutt_istrn_equal(const char *a, const char *b, size_t num)
502 {
503  return strncasecmp(NONULL(a), NONULL(b), num) == 0;
504 }
505 
517 const char *mutt_istrn_rfind(const char *haystack, size_t haystack_length, const char *needle)
518 {
519  if (!haystack || (haystack_length == 0) || !needle)
520  return NULL;
521 
522  int needle_length = strlen(needle);
523  const char *haystack_end = haystack + haystack_length - needle_length;
524 
525  for (const char *p = haystack_end; p >= haystack; --p)
526  {
527  for (size_t i = 0; i < needle_length; i++)
528  {
529  if ((tolower((unsigned char) p[i]) != tolower((unsigned char) needle[i])))
530  goto next;
531  }
532  return p;
533 
534  next:;
535  }
536  return NULL;
537 }
538 
544 size_t mutt_str_len(const char *a)
545 {
546  return a ? strlen(a) : 0;
547 }
548 
557 int mutt_str_coll(const char *a, const char *b)
558 {
559  return strcoll(NONULL(a), NONULL(b));
560 }
561 
569 const char *mutt_istr_find(const char *haystack, const char *needle)
570 {
571  if (!haystack)
572  return NULL;
573  if (!needle)
574  return haystack;
575 
576  const char *p = NULL, *q = NULL;
577 
578  while (*(p = haystack))
579  {
580  for (q = needle;
581  *p && *q && (tolower((unsigned char) *p) == tolower((unsigned char) *q));
582  p++, q++)
583  {
584  }
585  if ((*q == '\0'))
586  return haystack;
587  haystack++;
588  }
589  return NULL;
590 }
591 
599 char *mutt_str_skip_whitespace(const char *p)
600 {
601  if (!p)
602  return NULL;
603  SKIPWS(p);
604  return (char *) p;
605 }
606 
614 {
615  if (!s)
616  return;
617 
618  for (char *p = s + mutt_str_len(s) - 1; (p >= s) && IS_SPACE(*p); p--)
619  *p = '\0';
620 }
621 
629 size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
630 {
631  if (!dest || (dsize == 0))
632  return 0;
633  if (!src)
634  {
635  dest[0] = '\0';
636  return 0;
637  }
638 
639  char *dest0 = dest;
640  while ((--dsize > 0) && (*src != '\0'))
641  *dest++ = *src++;
642 
643  *dest = '\0';
644  return dest - dest0;
645 }
646 
656 char *mutt_str_skip_email_wsp(const char *s)
657 {
658  if (!s)
659  return NULL;
660 
661  for (; mutt_str_is_email_wsp(*s); s++)
662  ; // Do nothing
663 
664  return (char *) s;
665 }
666 
673 {
674  return c && strchr(" \t\r\n", c);
675 }
676 
686 size_t mutt_str_lws_len(const char *s, size_t n)
687 {
688  if (!s)
689  return 0;
690 
691  const char *p = s;
692  size_t len = n;
693 
694  if (n == 0)
695  return 0;
696 
697  for (; p < (s + n); p++)
698  {
699  if (!strchr(" \t\r\n", *p))
700  {
701  len = p - s;
702  break;
703  }
704  }
705 
706  if ((len != 0) && strchr("\r\n", *(p - 1))) /* LWS doesn't end with CRLF */
707  len = 0;
708  return len;
709 }
710 
720 size_t mutt_str_lws_rlen(const char *s, size_t n)
721 {
722  if (!s)
723  return 0;
724 
725  const char *p = s + n - 1;
726  size_t len = n;
727 
728  if (n == 0)
729  return 0;
730 
731  if (strchr("\r\n", *p)) /* LWS doesn't end with CRLF */
732  return 0;
733 
734  for (; p >= s; p--)
735  {
736  if (!strchr(" \t\r\n", *p))
737  {
738  len = s + n - 1 - p;
739  break;
740  }
741  }
742 
743  return len;
744 }
745 
753 {
754  if (!str)
755  return;
756 
757  char *w = str;
758 
759  for (; *str; str++)
760  {
761  if (*str == '\\')
762  {
763  if (!*++str)
764  break; /* error? */
765  *w++ = *str;
766  }
767  else if (*str != '\"')
768  {
769  if (w != str)
770  *w = *str;
771  w++;
772  }
773  }
774  *w = '\0';
775 }
776 
784 bool mutt_str_equal(const char *a, const char *b)
785 {
786  return (a == b) || (mutt_str_cmp(a, b) == 0);
787 }
788 
796 bool mutt_istr_equal(const char *a, const char *b)
797 {
798  return (a == b) || (mutt_istr_cmp(a, b) == 0);
799 }
800 
811 const char *mutt_str_next_word(const char *s)
812 {
813  if (!s)
814  return NULL;
815 
816  while (*s && !IS_SPACE(*s))
817  s++;
818  SKIPWS(s);
819  return s;
820 }
821 
833 const char *mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
834 {
835  if (!haystack || (haystack_length == 0) || !needle)
836  return NULL;
837 
838  int needle_length = strlen(needle);
839  const char *haystack_end = haystack + haystack_length - needle_length;
840 
841  for (const char *p = haystack_end; p >= haystack; --p)
842  {
843  for (size_t i = 0; i < needle_length; i++)
844  {
845  if (p[i] != needle[i])
846  goto next;
847  }
848  return p;
849 
850  next:;
851  }
852  return NULL;
853 }
854 
861 bool mutt_str_is_ascii(const char *str, size_t len)
862 {
863  if (!str)
864  return true;
865 
866  for (; (*str != '\0') && (len > 0); str++, len--)
867  if ((*str & 0x80) != 0)
868  return false;
869 
870  return true;
871 }
872 
884 const char *mutt_str_find_word(const char *src)
885 {
886  if (!src)
887  return NULL;
888 
889  while (*src && strchr(" \t\n", *src))
890  src++;
891  while (*src && !strchr(" \t\n", *src))
892  src++;
893  return src;
894 }
895 
904 const char *mutt_str_getenv(const char *name)
905 {
906  if (!name)
907  return NULL;
908 
909  const char *val = getenv(name);
910  if (val && (val[0] != '\0'))
911  return val;
912 
913  return NULL;
914 }
915 
926 bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
927 {
928  if (!buf || !rstr || (xlen >= buflen))
929  return false;
930 
931  size_t slen = mutt_str_len(buf + xlen);
932  size_t rlen = mutt_str_len(rstr);
933 
934  if ((slen + rlen) >= buflen)
935  return false;
936 
937  memmove(buf + rlen, buf + xlen, slen + 1);
938  memmove(buf, rstr, rlen);
939 
940  return true;
941 }
942 
950 int mutt_istr_remall(char *str, const char *target)
951 {
952  int rc = 1;
953  if (!str || !target)
954  return rc;
955 
956  // Look through an ensure all instances of the substring are gone.
957  while ((str = (char *) strcasestr(str, target)))
958  {
959  size_t target_len = mutt_str_len(target);
960  memmove(str, str + target_len, 1 + strlen(str + target_len));
961  rc = 0; // If we got here, then a substring existed and has been removed.
962  }
963 
964  return rc;
965 }
966 
967 #ifdef HAVE_VASPRINTF
976 int mutt_str_asprintf(char **strp, const char *fmt, ...)
977 {
978  if (!strp || !fmt)
979  return -1;
980 
981  va_list ap;
982  int n;
983 
984  va_start(ap, fmt);
985  n = vasprintf(strp, fmt, ap);
986  va_end(ap);
987 
988  /* GNU libc man page for vasprintf(3) states that the value of *strp
989  * is undefined when the return code is -1. */
990  if (n < 0)
991  {
992  mutt_error(_("Out of memory")); /* LCOV_EXCL_LINE */
993  mutt_exit(1); /* LCOV_EXCL_LINE */
994  }
995 
996  if (n == 0)
997  {
998  /* NeoMutt convention is to use NULL for 0-length strings */
999  FREE(strp);
1000  }
1001 
1002  return n;
1003 }
1004 #else
1005 /* Allocate a C-string large enough to contain the formatted string.
1006  * This is essentially malloc+sprintf in one.
1007  */
1008 int mutt_str_asprintf(char **strp, const char *fmt, ...)
1009 {
1010  if (!strp || !fmt)
1011  return -1;
1012 
1013  int rlen = 256;
1014 
1015  *strp = mutt_mem_malloc(rlen);
1016  while (true)
1017  {
1018  va_list ap;
1019  va_start(ap, fmt);
1020  const int n = vsnprintf(*strp, rlen, fmt, ap);
1021  va_end(ap);
1022  if (n < 0)
1023  {
1024  FREE(strp);
1025  return n;
1026  }
1027 
1028  if (n < rlen)
1029  {
1030  /* reduce space to just that which was used. note that 'n' does not
1031  * include the terminal nul char. */
1032  if (n == 0) /* convention is to use NULL for zero-length strings. */
1033  FREE(strp);
1034  else if (n != rlen - 1)
1035  mutt_mem_realloc(strp, n + 1);
1036  return n;
1037  }
1038  /* increase size and try again */
1039  rlen = n + 1;
1040  mutt_mem_realloc(strp, rlen);
1041  }
1042  /* not reached */
1043 }
1044 #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:40
#define mutt_array_size(x)
Definition: memory.h:33
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:926
static char * strsep(char **stringp, const char *delim)
Extract a token from a string.
Definition: string.c:80
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:294
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:447
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:487
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:613
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
const char * mutt_str_find_word(const char *src)
Find the end of a word (non-space)
Definition: string.c:884
void mutt_str_dequote_comment(char *str)
Un-escape characters in an email address comment.
Definition: string.c:752
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:428
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
static char * strcasestr(const char *haystack, const char *needle)
Find the first occurrence of needle in haystack, ignoring case.
Definition: string.c:54
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1008
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:569
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
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:686
bool mutt_str_is_ascii(const char *str, size_t len)
Is a string ASCII (7-bit)?
Definition: string.c:861
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:346
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:517
int mutt_istr_cmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:460
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
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:408
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:473
char * mutt_str_skip_whitespace(const char *p)
Find the first non-whitespace character in a string.
Definition: string.c:599
const char * mutt_str_next_word(const char *s)
Find the next word in a string.
Definition: string.c:811
int mutt_istr_remall(char *str, const char *target)
Remove all occurrences of substring, ignoring case.
Definition: string.c:950
static const struct SysExits sysexits[]
Definition: string.c:112
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition: string.c:370
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:656
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:672
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:384
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:904
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:629
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
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
const char * mutt_strn_rfind(const char *haystack, size_t haystack_length, const char *needle)
Find last instance of a substring.
Definition: string.c:833
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:501
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurance of any of delim characters in *stringp.
Definition: string.c:183
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:720
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:557
const char * mutt_str_sysexit(int err_num)
Return a string matching an error code.
Definition: string.c:166
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