NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
charset.c
Go to the documentation of this file.
1
25
31
32#include "config.h"
33#include <errno.h>
34#include <iconv.h>
35#include <langinfo.h>
36#include <limits.h>
37#include <stdbool.h>
38#include <stdint.h>
39#include <stdio.h>
40#include <string.h>
41#include "charset.h"
42#include "buffer.h"
43#include "list.h"
44#include "logging2.h"
45#include "memory.h"
46#include "pool.h"
47#include "queue.h"
48#include "regex3.h"
49#include "slist.h"
50#include "string2.h"
51#ifdef ENABLE_NLS
52#include <libintl.h>
53#endif
54
55#ifndef EILSEQ
56#define EILSEQ EINVAL
57#endif
58
62wchar_t ReplacementChar = '?';
63
67bool CharsetIsUtf8 = false;
68
71
76{
77 char *fromcode1;
78 char *tocode1;
79 iconv_t cd;
80};
81
83#define ICONV_CACHE_SIZE 16
87static int IconvCacheUsed = 0;
88
93{
94 const char *key;
95 const char *pref;
96};
97
108static const struct MimeNames PreferredMimeNames[] = {
109 // clang-format off
110 { "ansi_x3.4-1968", "us-ascii" },
111 { "iso-ir-6", "us-ascii" },
112 { "iso_646.irv:1991", "us-ascii" },
113 { "ascii", "us-ascii" },
114 { "iso646-us", "us-ascii" },
115 { "us", "us-ascii" },
116 { "ibm367", "us-ascii" },
117 { "cp367", "us-ascii" },
118 { "csASCII", "us-ascii" },
119
120 { "csISO2022KR", "iso-2022-kr" },
121 { "csEUCKR", "euc-kr" },
122 { "csISO2022JP", "iso-2022-jp" },
123 { "csISO2022JP2", "iso-2022-jp-2" },
124
125 { "ISO_8859-1:1987", "iso-8859-1" },
126 { "iso-ir-100", "iso-8859-1" },
127 { "iso_8859-1", "iso-8859-1" },
128 { "latin1", "iso-8859-1" },
129 { "l1", "iso-8859-1" },
130 { "IBM819", "iso-8859-1" },
131 { "CP819", "iso-8859-1" },
132 { "csISOLatin1", "iso-8859-1" },
133
134 { "ISO_8859-2:1987", "iso-8859-2" },
135 { "iso-ir-101", "iso-8859-2" },
136 { "iso_8859-2", "iso-8859-2" },
137 { "latin2", "iso-8859-2" },
138 { "l2", "iso-8859-2" },
139 { "csISOLatin2", "iso-8859-2" },
140
141 { "ISO_8859-3:1988", "iso-8859-3" },
142 { "iso-ir-109", "iso-8859-3" },
143 { "ISO_8859-3", "iso-8859-3" },
144 { "latin3", "iso-8859-3" },
145 { "l3", "iso-8859-3" },
146 { "csISOLatin3", "iso-8859-3" },
147
148 { "ISO_8859-4:1988", "iso-8859-4" },
149 { "iso-ir-110", "iso-8859-4" },
150 { "ISO_8859-4", "iso-8859-4" },
151 { "latin4", "iso-8859-4" },
152 { "l4", "iso-8859-4" },
153 { "csISOLatin4", "iso-8859-4" },
154
155 { "ISO_8859-6:1987", "iso-8859-6" },
156 { "iso-ir-127", "iso-8859-6" },
157 { "iso_8859-6", "iso-8859-6" },
158 { "ECMA-114", "iso-8859-6" },
159 { "ASMO-708", "iso-8859-6" },
160 { "arabic", "iso-8859-6" },
161 { "csISOLatinArabic", "iso-8859-6" },
162
163 { "ISO_8859-7:1987", "iso-8859-7" },
164 { "iso-ir-126", "iso-8859-7" },
165 { "ISO_8859-7", "iso-8859-7" },
166 { "ELOT_928", "iso-8859-7" },
167 { "ECMA-118", "iso-8859-7" },
168 { "greek", "iso-8859-7" },
169 { "greek8", "iso-8859-7" },
170 { "csISOLatinGreek", "iso-8859-7" },
171
172 { "ISO_8859-8:1988", "iso-8859-8" },
173 { "iso-ir-138", "iso-8859-8" },
174 { "ISO_8859-8", "iso-8859-8" },
175 { "hebrew", "iso-8859-8" },
176 { "csISOLatinHebrew", "iso-8859-8" },
177
178 { "ISO_8859-5:1988", "iso-8859-5" },
179 { "iso-ir-144", "iso-8859-5" },
180 { "ISO_8859-5", "iso-8859-5" },
181 { "cyrillic", "iso-8859-5" },
182 { "csISOLatinCyrillic", "iso-8859-5" },
183
184 { "ISO_8859-9:1989", "iso-8859-9" },
185 { "iso-ir-148", "iso-8859-9" },
186 { "ISO_8859-9", "iso-8859-9" },
187 { "latin5", "iso-8859-9" }, /* this is not a bug */
188 { "l5", "iso-8859-9" },
189 { "csISOLatin5", "iso-8859-9" },
190
191 { "ISO_8859-10:1992", "iso-8859-10" },
192 { "iso-ir-157", "iso-8859-10" },
193 { "latin6", "iso-8859-10" }, /* this is not a bug */
194 { "l6", "iso-8859-10" },
195 { "csISOLatin6", "iso-8859-10" },
196
197 { "csKOI8r", "koi8-r" },
198
199 { "MS_Kanji", "Shift_JIS" }, /* Note the underscore! */
200 { "csShiftJis", "Shift_JIS" },
201
202 { "Extended_UNIX_Code_Packed_Format_for_Japanese",
203 "euc-jp" },
204 { "csEUCPkdFmtJapanese", "euc-jp" },
205
206 { "csGB2312", "gb2312" },
207 { "csbig5", "big5" },
208
209 /* End of official brain damage.
210 * What follows has been taken from glibc's localedata files. */
211
212 { "iso_8859-13", "iso-8859-13" },
213 { "iso-ir-179", "iso-8859-13" },
214 { "latin7", "iso-8859-13" }, /* this is not a bug */
215 { "l7", "iso-8859-13" },
216
217 { "iso_8859-14", "iso-8859-14" },
218 { "latin8", "iso-8859-14" }, /* this is not a bug */
219 { "l8", "iso-8859-14" },
220
221 { "iso_8859-15", "iso-8859-15" },
222 { "latin9", "iso-8859-15" }, /* this is not a bug */
223
224 /* Suggested by Ionel Mugurel Ciobica <tgakic@sg10.chem.tue.nl> */
225 { "latin0", "iso-8859-15" }, /* this is not a bug */
226
227 { "iso_8859-16", "iso-8859-16" },
228 { "latin10", "iso-8859-16" }, /* this is not a bug */
229
230 { "646", "us-ascii" },
231
232 /* http://www.sun.com/software/white-papers/wp-unicode/ */
233
234 { "eucJP", "euc-jp" },
235 { "PCK", "Shift_JIS" },
236 { "ko_KR-euc", "euc-kr" },
237 { "zh_TW-big5", "big5" },
238
239 /* seems to be common on some systems */
240
241 { "sjis", "Shift_JIS" },
242 { "euc-jp-ms", "eucJP-ms" },
243
244 /* If you happen to encounter system-specific brain-damage with respect to
245 * character set naming, please add it above this comment, and submit a patch
246 * to <neomutt-devel@neomutt.org> */
247
248 { NULL, NULL },
249 // clang-format on
250};
251
256static struct Lookup *lookup_new(void)
257{
258 return MUTT_MEM_CALLOC(1, struct Lookup);
259}
260
265static void lookup_free(struct Lookup **ptr)
266{
267 if (!ptr || !*ptr)
268 return;
269
270 struct Lookup *l = *ptr;
271 FREE(&l->replacement);
272 FREE(&l->regex.pattern);
273 if (l->regex.regex)
274 regfree(l->regex.regex);
275 FREE(&l->regex.regex);
276 FREE(&l->regex);
277
278 FREE(ptr);
279}
280
290static const char *lookup_charset(enum LookupType type, const char *cs)
291{
292 if (!cs)
293 return NULL;
294
295 struct Lookup *l = NULL;
296
297 TAILQ_FOREACH(l, &Lookups, entries)
298 {
299 if (l->type != type)
300 continue;
301 if (mutt_regex_match(&l->regex, cs))
302 return l->replacement;
303 }
304 return NULL;
305}
306
318int mutt_ch_convert_nonmime_string(const struct Slist *const assumed_charset,
319 const char *charset, char **ps)
320{
321 if (!ps)
322 return -1;
323
324 char *u = *ps;
325 const size_t ulen = mutt_str_len(u);
326 if (ulen == 0)
327 return 0;
328
329 const struct ListNode *np = NULL;
330 STAILQ_FOREACH(np, &assumed_charset->head, entries)
331 {
332 char const *c = np->data;
333 size_t n = mutt_str_len(c);
334 char *fromcode = MUTT_MEM_MALLOC(n + 1, char);
335 mutt_str_copy(fromcode, c, n + 1);
336 char *s = mutt_strn_dup(u, ulen);
337 int m = mutt_ch_convert_string(&s, fromcode, charset, MUTT_ICONV_NONE);
338 FREE(&fromcode);
339 if (m == 0)
340 {
341 FREE(ps);
342 *ps = s;
343 return 0;
344 }
345 FREE(&s);
346 }
348 charset, MUTT_ICONV_HOOK_FROM);
349 return -1;
350}
351
361void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
362{
363 if (!buf || !name)
364 return;
365
366 char in[1024] = { 0 };
367 char scratch[1024 + 10] = { 0 };
368 struct Buffer *canon = buf_pool_get();
369
370 mutt_str_copy(in, name, sizeof(in));
371 char *ext = strchr(in, '/');
372 if (ext)
373 *ext++ = '\0';
374
375 if (mutt_istr_equal(in, "utf-8") || mutt_istr_equal(in, "utf8"))
376 {
377 buf_strcpy(canon, "utf-8");
378 goto out;
379 }
380
381 /* catch some common iso-8859-something misspellings */
382 size_t plen;
383 if ((plen = mutt_istr_startswith(in, "8859")) && (in[plen] != '-'))
384 snprintf(scratch, sizeof(scratch), "iso-8859-%s", in + plen);
385 else if ((plen = mutt_istr_startswith(in, "8859-")))
386 snprintf(scratch, sizeof(scratch), "iso-8859-%s", in + plen);
387 else if ((plen = mutt_istr_startswith(in, "iso8859")) && (in[plen] != '-'))
388 snprintf(scratch, sizeof(scratch), "iso_8859-%s", in + plen);
389 else if ((plen = mutt_istr_startswith(in, "iso8859-")))
390 snprintf(scratch, sizeof(scratch), "iso_8859-%s", in + plen);
391 else
392 mutt_str_copy(scratch, in, sizeof(scratch));
393
394 for (size_t i = 0; PreferredMimeNames[i].key; i++)
395 {
396 if (mutt_istr_equal(scratch, PreferredMimeNames[i].key))
397 {
398 buf_strcpy(canon, PreferredMimeNames[i].pref);
399 goto out;
400 }
401 }
402
403 buf_strcpy(canon, scratch);
404 buf_lower(canon); // for cosmetics' sake
405
406out:
407 if (ext && (*ext != '\0'))
408 {
409 buf_addch(canon, '/');
410 buf_addstr(canon, ext);
411 }
412
413 mutt_str_copy(buf, buf_string(canon), buflen);
414 buf_pool_release(&canon);
415}
416
429bool mutt_ch_chscmp(const char *cs1, const char *cs2)
430{
431 if (!cs1 || !cs2)
432 return false;
433
434 char buf[256] = { 0 };
435
436 mutt_ch_canonical_charset(buf, sizeof(buf), cs1);
437
438 int len1 = mutt_str_len(buf);
439 int len2 = mutt_str_len(cs2);
440
441 return mutt_istrn_equal(((len1 > len2) ? buf : cs2),
442 ((len1 > len2) ? cs2 : buf), MIN(len1, len2));
443}
444
452const char *mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
453{
454 static char fcharset[128];
455 const char *c = NULL;
456
457 if (assumed_charset && (assumed_charset->count > 0))
458 c = STAILQ_FIRST(&assumed_charset->head)->data;
459 else
460 c = "us-ascii";
461
462 mutt_str_copy(fcharset, c, sizeof(fcharset));
463 return fcharset;
464}
465
474{
475 char buf[1024] = { 0 };
476
477 mutt_ch_canonical_charset(buf, sizeof(buf), nl_langinfo(CODESET));
478
479 if (buf[0] != '\0')
480 return mutt_str_dup(buf);
481
482 return mutt_str_dup("iso-8859-1");
483}
484
496bool mutt_ch_lookup_add(enum LookupType type, const char *pat,
497 const char *replace, struct Buffer *err)
498{
499 if (!pat || !replace)
500 return false;
501
502 regex_t *rx = MUTT_MEM_CALLOC(1, regex_t);
503 int rc = REG_COMP(rx, pat, REG_ICASE);
504 if (rc != 0)
505 {
506 regerror(rc, rx, err->data, err->dsize);
507 FREE(&rx);
508 return false;
509 }
510
511 struct Lookup *l = lookup_new();
512 l->type = type;
513 l->replacement = mutt_str_dup(replace);
514 l->regex.pattern = mutt_str_dup(pat);
515 l->regex.regex = rx;
516 l->regex.pat_not = false;
517
518 TAILQ_INSERT_TAIL(&Lookups, l, entries);
519
520 return true;
521}
522
529{
530 struct Lookup *l = NULL;
531 struct Lookup *tmp = NULL;
532
533 TAILQ_FOREACH_SAFE(l, &Lookups, entries, tmp)
534 {
535 TAILQ_REMOVE(&Lookups, l, entries);
536 lookup_free(&l);
537 }
538}
539
549const char *mutt_ch_charset_lookup(const char *chs)
550{
552}
553
581iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
582{
583 char tocode1[128] = { 0 };
584 char fromcode1[128] = { 0 };
585 const char *tocode2 = NULL;
586 const char *fromcode2 = NULL;
587 const char *tmp = NULL;
588
589 /* transform to MIME preferred charset names */
590 mutt_ch_canonical_charset(tocode1, sizeof(tocode1), tocode);
591 mutt_ch_canonical_charset(fromcode1, sizeof(fromcode1), fromcode);
592
593 /* maybe apply charset-hooks and recanonicalise fromcode,
594 * but only when caller asked us to sanitize a potentially wrong
595 * charset name incoming from the wild exterior. */
596 if (flags & MUTT_ICONV_HOOK_FROM)
597 {
598 tmp = mutt_ch_charset_lookup(fromcode1);
599 if (tmp)
600 mutt_ch_canonical_charset(fromcode1, sizeof(fromcode1), tmp);
601 }
602
603 /* check if we have this pair cached already */
604 for (int i = 0; i < IconvCacheUsed; i++)
605 {
606 if (strcmp(tocode1, IconvCache[i].tocode1) == 0 &&
607 strcmp(fromcode1, IconvCache[i].fromcode1) == 0)
608 {
609 iconv_t cd = IconvCache[i].cd;
610
611 /* make room for this one at the top */
612 struct IconvCacheEntry top = IconvCache[i];
613 for (int j = i - 1; j >= 0; j--)
614 {
615 IconvCache[j + 1] = IconvCache[j];
616 }
617 IconvCache[0] = top;
618
619 if (iconv_t_valid(cd))
620 {
621 /* reset state */
622 iconv(cd, NULL, NULL, NULL, NULL);
623 }
624 return cd;
625 }
626 }
627
628 /* not found in cache */
629 /* always apply iconv-hooks to suit system's iconv tastes */
630 tocode2 = mutt_ch_iconv_lookup(tocode1);
631 tocode2 = tocode2 ? tocode2 : tocode1;
632 fromcode2 = mutt_ch_iconv_lookup(fromcode1);
633 fromcode2 = fromcode2 ? fromcode2 : fromcode1;
634
635 /* call system iconv with names it appreciates */
636 iconv_t cd = iconv_open(tocode2, fromcode2);
637
639 {
640 mutt_debug(LL_DEBUG2, "iconv: dropping %s -> %s from the cache\n",
643 /* get rid of the oldest entry */
647 {
648 iconv_close(IconvCache[IconvCacheUsed - 1].cd);
649 }
651 }
652
653 /* make room for this one at the top */
654 for (int j = IconvCacheUsed - 1; j >= 0; j--)
655 {
656 IconvCache[j + 1] = IconvCache[j];
657 }
658
660
661 mutt_debug(LL_DEBUG2, "iconv: adding %s -> %s to the cache\n", fromcode1, tocode1);
662 IconvCache[0].fromcode1 = strdup(fromcode1);
663 IconvCache[0].tocode1 = strdup(tocode1);
664 IconvCache[0].cd = cd;
665
666 return cd;
667}
668
685size_t mutt_ch_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft,
686 char **outbuf, size_t *outbytesleft, const char **inrepls,
687 const char *outrepl, int *iconverrno)
688{
689 size_t rc = 0;
690 const char *ib = *inbuf;
691 size_t ibl = *inbytesleft;
692 char *ob = *outbuf;
693 size_t obl = *outbytesleft;
694
695 while (true)
696 {
697 errno = 0;
698 const size_t ret1 = iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
699 if (ret1 != ICONV_ILLEGAL_SEQ)
700 rc += ret1;
701 if (iconverrno)
702 *iconverrno = errno;
703
704 if (ibl && obl && (errno == EILSEQ))
705 {
706 if (inrepls)
707 {
708 /* Try replacing the input */
709 const char **t = NULL;
710 for (t = inrepls; *t; t++)
711 {
712 const char *ib1 = *t;
713 size_t ibl1 = strlen(*t);
714 char *ob1 = ob;
715 size_t obl1 = obl;
716 iconv(cd, (ICONV_CONST char **) &ib1, &ibl1, &ob1, &obl1);
717 if (ibl1 == 0)
718 {
719 ib++;
720 ibl--;
721 ob = ob1;
722 obl = obl1;
723 rc++;
724 break;
725 }
726 }
727 if (*t)
728 continue;
729 }
730 /* Replace the output */
731 if (!outrepl)
732 outrepl = "?";
733 iconv(cd, NULL, NULL, &ob, &obl);
734 if (obl)
735 {
736 int n = strlen(outrepl);
737 if (n > obl)
738 {
739 outrepl = "?";
740 n = 1;
741 }
742 memcpy(ob, outrepl, n);
743 ib++;
744 ibl--;
745 ob += n;
746 obl -= n;
747 rc++;
748 iconv(cd, NULL, NULL, NULL, NULL); /* for good measure */
749 continue;
750 }
751 }
752 *inbuf = ib;
753 *inbytesleft = ibl;
754 *outbuf = ob;
755 *outbytesleft = obl;
756 return rc;
757 }
758}
759
769const char *mutt_ch_iconv_lookup(const char *chs)
770{
772}
773
784int mutt_ch_check(const char *s, size_t slen, const char *from, const char *to)
785{
786 if (!s || !from || !to)
787 return -1;
788
789 int rc = 0;
790 iconv_t cd = mutt_ch_iconv_open(to, from, MUTT_ICONV_NONE);
791 if (!iconv_t_valid(cd))
792 return -1;
793
794 size_t outlen = MB_LEN_MAX * slen;
795 char *out = MUTT_MEM_MALLOC(outlen + 1, char);
796 char *saved_out = out;
797
798 const size_t convlen = iconv(cd, (ICONV_CONST char **) &s, &slen, &out, &outlen);
799 if (convlen == ICONV_ILLEGAL_SEQ)
800 rc = errno;
801
802 FREE(&saved_out);
803 return rc;
804}
805
819int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
820{
821 if (!ps)
822 return -1;
823
824 char *s = *ps;
825
826 if (!s || (*s == '\0'))
827 return 0;
828
829 if (!to || !from)
830 return -1;
831
832 const char *repls[] = { "\357\277\275", "?", 0 };
833 int rc = 0;
834
835 iconv_t cd = mutt_ch_iconv_open(to, from, flags);
836 if (!iconv_t_valid(cd))
837 return -1;
838
839 const char **inrepls = NULL;
840 const char *outrepl = NULL;
841
842 if (mutt_ch_is_utf8(to))
843 outrepl = "\357\277\275";
844 else if (mutt_ch_is_utf8(from))
845 inrepls = repls;
846 else
847 outrepl = "?";
848
849 const char *ib = s;
850 size_t ibl = strlen(s);
851 if (ibl >= (SIZE_MAX / MB_LEN_MAX))
852 {
853 return -1;
854 }
855 size_t obl = MB_LEN_MAX * ibl;
856 char *buf = MUTT_MEM_MALLOC(obl + 1, char);
857 char *ob = buf;
858
859 mutt_ch_iconv(cd, &ib, &ibl, &ob, &obl, inrepls, outrepl, &rc);
860 iconv(cd, 0, 0, &ob, &obl);
861
862 *ob = '\0';
863
864 FREE(ps);
865 *ps = buf;
866
867 mutt_str_adjust(ps);
868 return rc;
869}
870
882bool mutt_ch_check_charset(const char *cs, bool strict)
883{
884 if (!cs)
885 return false;
886
887 if (mutt_ch_is_utf8(cs))
888 return true;
889
890 if (!strict)
891 {
892 for (int i = 0; PreferredMimeNames[i].key; i++)
893 {
894 if (mutt_istr_equal(PreferredMimeNames[i].key, cs) ||
896 {
897 return true;
898 }
899 }
900 }
901
902 iconv_t cd = mutt_ch_iconv_open(cs, cs, MUTT_ICONV_NONE);
903 if (iconv_t_valid(cd))
904 {
905 return true;
906 }
907
908 return false;
909}
910
921struct FgetConv *mutt_ch_fgetconv_open(FILE *fp, const char *from, const char *to, uint8_t flags)
922{
923 iconv_t cd = ICONV_T_INVALID;
924
925 if (from && to)
926 cd = mutt_ch_iconv_open(to, from, flags);
927
928 struct FgetConv *fc = MUTT_MEM_CALLOC(1, struct FgetConv);
929 fc->fp = fp;
930 fc->cd = cd;
931
932 if (iconv_t_valid(cd))
933 {
934 static const char *repls[] = { "\357\277\275", "?", 0 };
935
936 fc->p = fc->bufo;
937 fc->ob = fc->bufo;
938 fc->ib = fc->bufi;
939 fc->ibl = 0;
940 fc->inrepls = mutt_ch_is_utf8(to) ? repls : repls + 1;
941 }
942
943 return fc;
944}
945
951{
952 if (!ptr || !*ptr)
953 return;
954
955 FREE(ptr);
956}
957
969{
970 if (!fc)
971 return EOF;
972 if (!iconv_t_valid(fc->cd))
973 return fgetc(fc->fp);
974 if (!fc->p)
975 return EOF;
976 if (fc->p < fc->ob)
977 return (unsigned char) *(fc->p)++;
978
979 /* Try to convert some more */
980 fc->p = fc->bufo;
981 fc->ob = fc->bufo;
982 if (fc->ibl)
983 {
984 size_t obl = sizeof(fc->bufo);
985 iconv(fc->cd, (ICONV_CONST char **) &fc->ib, &fc->ibl, &fc->ob, &obl);
986 if (fc->p < fc->ob)
987 return (unsigned char) *(fc->p)++;
988 }
989
990 /* If we trusted iconv a bit more, we would at this point
991 * ask why it had stopped converting ... */
992
993 /* Try to read some more */
994 if ((fc->ibl == sizeof(fc->bufi)) ||
995 (fc->ibl && (fc->ib + fc->ibl < fc->bufi + sizeof(fc->bufi))))
996 {
997 fc->p = 0;
998 return EOF;
999 }
1000 if (fc->ibl)
1001 memmove(fc->bufi, fc->ib, fc->ibl);
1002 fc->ib = fc->bufi;
1003 fc->ibl += fread(fc->ib + fc->ibl, 1, sizeof(fc->bufi) - fc->ibl, fc->fp);
1004
1005 /* Try harder this time to convert some */
1006 if (fc->ibl)
1007 {
1008 size_t obl = sizeof(fc->bufo);
1009 mutt_ch_iconv(fc->cd, (const char **) &fc->ib, &fc->ibl, &fc->ob, &obl,
1010 fc->inrepls, 0, NULL);
1011 if (fc->p < fc->ob)
1012 return (unsigned char) *(fc->p)++;
1013 }
1014
1015 /* Either the file has finished or one of the buffers is too small */
1016 fc->p = 0;
1017 return EOF;
1018}
1019
1030char *mutt_ch_fgetconvs(char *buf, size_t buflen, struct FgetConv *fc)
1031{
1032 if (!buf)
1033 return NULL;
1034
1035 size_t r;
1036 for (r = 0; (r + 1) < buflen;)
1037 {
1038 const int c = mutt_ch_fgetconv(fc);
1039 if (c == EOF)
1040 break;
1041 buf[r++] = (char) c;
1042 if (c == '\n')
1043 break;
1044 }
1045 buf[r] = '\0';
1046
1047 if (r > 0)
1048 return buf;
1049
1050 return NULL;
1051}
1052
1063void mutt_ch_set_charset(const char *charset)
1064{
1065 char buf[256] = { 0 };
1066
1067 mutt_ch_canonical_charset(buf, sizeof(buf), charset);
1068
1069 if (mutt_ch_is_utf8(buf))
1070 {
1071 CharsetIsUtf8 = true;
1072 ReplacementChar = 0xfffd; /* replacement character */
1073 }
1074 else
1075 {
1076 CharsetIsUtf8 = false;
1077 ReplacementChar = '?';
1078 }
1079
1080#if defined(HAVE_BIND_TEXTDOMAIN_CODESET) && defined(ENABLE_NLS)
1081 bind_textdomain_codeset(PACKAGE, buf);
1082#endif
1083}
1084
1096char *mutt_ch_choose(const char *fromcode, const struct Slist *charsets,
1097 const char *u, size_t ulen, char **d, size_t *dlen)
1098{
1099 if (!fromcode || !charsets)
1100 return NULL;
1101
1102 char *e = NULL;
1103 char *tocode = NULL;
1104 size_t elen = 0;
1105 size_t bestn = 0;
1106
1107 const struct ListNode *np = NULL;
1108 STAILQ_FOREACH(np, &charsets->head, entries)
1109 {
1110 char *t = mutt_str_dup(np->data);
1111 if (!t)
1112 continue;
1113
1114 size_t n = mutt_str_len(t);
1115 char *s = mutt_strn_dup(u, ulen);
1116 const int rc = d ? mutt_ch_convert_string(&s, fromcode, t, MUTT_ICONV_NONE) :
1117 mutt_ch_check(s, ulen, fromcode, t);
1118 if (rc)
1119 {
1120 FREE(&t);
1121 FREE(&s);
1122 continue;
1123 }
1124 size_t slen = mutt_str_len(s);
1125
1126 if (!tocode || (n < bestn))
1127 {
1128 bestn = n;
1129 FREE(&tocode);
1130 tocode = t;
1131 if (d)
1132 {
1133 FREE(&e);
1134 e = s;
1135 }
1136 else
1137 {
1138 FREE(&s);
1139 }
1140 elen = slen;
1141 }
1142 else
1143 {
1144 FREE(&t);
1145 FREE(&s);
1146 }
1147 }
1148 if (tocode)
1149 {
1150 if (d)
1151 *d = e;
1152 if (dlen)
1153 *dlen = elen;
1154
1155 char canonical_buf[1024] = { 0 };
1156 mutt_ch_canonical_charset(canonical_buf, sizeof(canonical_buf), tocode);
1157 mutt_str_replace(&tocode, canonical_buf);
1158 }
1159 return tocode;
1160}
1161
1166{
1167 for (int i = 0; i < IconvCacheUsed; i++)
1168 {
1169 FREE(&IconvCache[i].fromcode1);
1170 FREE(&IconvCache[i].tocode1);
1171 if (iconv_t_valid(IconvCache[i].cd))
1172 {
1173 iconv_close(IconvCache[i].cd);
1174 }
1175 }
1176 IconvCacheUsed = 0;
1177}
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
void buf_lower(struct Buffer *buf)
Sets a buffer to lowercase.
Definition buffer.c:740
General purpose object for storing and parsing strings.
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Singly-linked list type.
Logging Dispatcher.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
Memory management wrappers.
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
bool mutt_ch_check_charset(const char *cs, bool strict)
Does iconv understand a character set?
Definition charset.c:882
size_t mutt_ch_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft, const char **inrepls, const char *outrepl, int *iconverrno)
Change the encoding of a string.
Definition charset.c:685
void mutt_ch_lookup_remove(void)
Remove all the character set lookups.
Definition charset.c:528
static int IconvCacheUsed
Number of iconv descriptors in the cache.
Definition charset.c:87
char * mutt_ch_choose(const char *fromcode, const struct Slist *charsets, const char *u, size_t ulen, char **d, size_t *dlen)
Figure the best charset to encode a string.
Definition charset.c:1096
int mutt_ch_convert_nonmime_string(const struct Slist *const assumed_charset, const char *charset, char **ps)
Try to convert a string using a list of character sets.
Definition charset.c:318
struct LookupList Lookups
Lookup table of preferred character set names.
Definition charset.c:70
char * mutt_ch_get_langinfo_charset(void)
Get the user's choice of character set.
Definition charset.c:473
static const struct MimeNames PreferredMimeNames[]
Lookup table of preferred charsets.
Definition charset.c:108
bool mutt_ch_lookup_add(enum LookupType type, const char *pat, const char *replace, struct Buffer *err)
Add a new character set lookup.
Definition charset.c:496
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition charset.c:361
void mutt_ch_cache_cleanup(void)
Clean up the cached iconv handles and charset strings.
Definition charset.c:1165
const char * mutt_ch_iconv_lookup(const char *chs)
Look for a replacement character set.
Definition charset.c:769
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
void mutt_ch_set_charset(const char *charset)
Update the records for a new character set.
Definition charset.c:1063
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition charset.c:67
static const char * lookup_charset(enum LookupType type, const char *cs)
Look for a preferred character set name.
Definition charset.c:290
int mutt_ch_check(const char *s, size_t slen, const char *from, const char *to)
Check whether a string can be converted between encodings.
Definition charset.c:784
const char * mutt_ch_charset_lookup(const char *chs)
Look for a replacement character set.
Definition charset.c:549
static struct Lookup * lookup_new(void)
Create a new Lookup.
Definition charset.c:256
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file's character set.
Definition charset.c:968
#define ICONV_CACHE_SIZE
Max size of the iconv cache.
Definition charset.c:83
static void lookup_free(struct Lookup **ptr)
Free a Lookup.
Definition charset.c:265
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:62
#define EILSEQ
Definition charset.c:56
struct FgetConv * mutt_ch_fgetconv_open(FILE *fp, const char *from, const char *to, uint8_t flags)
Prepare a file for charset conversion.
Definition charset.c:921
static struct IconvCacheEntry IconvCache[ICONV_CACHE_SIZE]
Cache of iconv conversion descriptors.
Definition charset.c:85
char * mutt_ch_fgetconvs(char *buf, size_t buflen, struct FgetConv *fc)
Convert a file's charset into a string buffer.
Definition charset.c:1030
bool mutt_ch_chscmp(const char *cs1, const char *cs2)
Are the names of two character sets equivalent?
Definition charset.c:429
void mutt_ch_fgetconv_close(struct FgetConv **ptr)
Close an fgetconv handle.
Definition charset.c:950
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition charset.c:581
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition charset.c:452
Conversion between different character encodings.
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define MUTT_ICONV_HOOK_FROM
apply charset-hooks to fromcode
Definition charset.h:67
#define ICONV_T_INVALID
Error value for iconv functions.
Definition charset.h:111
#define mutt_ch_is_utf8(str)
Definition charset.h:107
LookupType
Types of character set lookups.
Definition charset.h:61
@ MUTT_LOOKUP_ICONV
Character set conversion.
Definition charset.h:63
@ MUTT_LOOKUP_CHARSET
Alias for another character set.
Definition charset.h:62
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition charset.h:123
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:618
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition string.c:303
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:587
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
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:457
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
A global pool of Buffers.
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define STAILQ_FIRST(head)
Definition queue.h:388
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
Manage regular expressions.
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
A separated list of strings.
String manipulation functions.
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
Cursor for converting a file's encoding.
Definition charset.h:45
char bufi[512]
Input buffer.
Definition charset.h:48
iconv_t cd
iconv conversion descriptor
Definition charset.h:47
char bufo[512]
Output buffer.
Definition charset.h:49
size_t ibl
Input buffer length.
Definition charset.h:53
FILE * fp
File to read from.
Definition charset.h:46
char * p
Current position in output buffer.
Definition charset.h:50
const char ** inrepls
Replacement characters.
Definition charset.h:54
char * ib
Current position in input buffer.
Definition charset.h:52
char * ob
End of output buffer.
Definition charset.h:51
Cached iconv conversion descriptor.
Definition charset.c:76
char * tocode1
Destination character set.
Definition charset.c:78
char * fromcode1
Source character set.
Definition charset.c:77
iconv_t cd
iconv conversion descriptor
Definition charset.c:79
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Regex to String lookup table.
Definition charset.h:75
char * replacement
Alternative charset to use.
Definition charset.h:78
enum LookupType type
Lookup type.
Definition charset.h:76
struct Regex regex
Regular expression.
Definition charset.h:77
MIME name lookup entry.
Definition charset.c:93
const char * key
Charset alias.
Definition charset.c:94
const char * pref
Preferred MIME name.
Definition charset.c:95
char * pattern
printable version
Definition regex3.h:86
bool pat_not
do not match
Definition regex3.h:88
regex_t * regex
compiled expression
Definition regex3.h:87
String list.
Definition slist.h:37
struct ListHead head
List containing values.
Definition slist.h:38
size_t count
Number of values in list.
Definition slist.h:39