NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
charset.c File Reference

Conversion between different character encodings. More...

#include "config.h"
#include <errno.h>
#include <iconv.h>
#include <langinfo.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "charset.h"
#include "buffer.h"
#include "list.h"
#include "logging2.h"
#include "memory.h"
#include "pool.h"
#include "queue.h"
#include "regex3.h"
#include "slist.h"
#include "string2.h"
#include <libintl.h>
+ Include dependency graph for charset.c:

Go to the source code of this file.

Data Structures

struct  IconvCacheEntry
 Cached iconv conversion descriptor. More...
 
struct  MimeNames
 MIME name lookup entry. More...
 

Macros

#define EILSEQ   EINVAL
 
#define ICONV_CACHE_SIZE   16
 Max size of the iconv cache.
 

Functions

static struct Lookuplookup_new (void)
 Create a new Lookup.
 
static void lookup_free (struct Lookup **ptr)
 Free a Lookup.
 
static const char * lookup_charset (enum LookupType type, const char *cs)
 Look for a preferred character set name.
 
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.
 
void mutt_ch_canonical_charset (char *buf, size_t buflen, const char *name)
 Canonicalise the charset of a string.
 
bool mutt_ch_chscmp (const char *cs1, const char *cs2)
 Are the names of two character sets equivalent?
 
const char * mutt_ch_get_default_charset (const struct Slist *const assumed_charset)
 Get the default character set.
 
char * mutt_ch_get_langinfo_charset (void)
 Get the user's choice of character set.
 
bool mutt_ch_lookup_add (enum LookupType type, const char *pat, const char *replace, struct Buffer *err)
 Add a new character set lookup.
 
void mutt_ch_lookup_remove (void)
 Remove all the character set lookups.
 
const char * mutt_ch_charset_lookup (const char *chs)
 Look for a replacement character set.
 
iconv_t mutt_ch_iconv_open (const char *tocode, const char *fromcode, uint8_t flags)
 Set up iconv for conversions.
 
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.
 
const char * mutt_ch_iconv_lookup (const char *chs)
 Look for a replacement character set.
 
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.
 
int mutt_ch_convert_string (char **ps, const char *from, const char *to, uint8_t flags)
 Convert a string between encodings.
 
bool mutt_ch_check_charset (const char *cs, bool strict)
 Does iconv understand a character set?
 
struct FgetConvmutt_ch_fgetconv_open (FILE *fp, const char *from, const char *to, uint8_t flags)
 Prepare a file for charset conversion.
 
void mutt_ch_fgetconv_close (struct FgetConv **ptr)
 Close an fgetconv handle.
 
int mutt_ch_fgetconv (struct FgetConv *fc)
 Convert a file's character set.
 
char * mutt_ch_fgetconvs (char *buf, size_t buflen, struct FgetConv *fc)
 Convert a file's charset into a string buffer.
 
void mutt_ch_set_charset (const char *charset)
 Update the records for a new character set.
 
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.
 
void mutt_ch_cache_cleanup (void)
 Clean up the cached iconv handles and charset strings.
 

Variables

wchar_t ReplacementChar = '?'
 When a Unicode character can't be displayed, use this instead.
 
bool CharsetIsUtf8 = false
 Is the user's current character set utf-8?
 
struct LookupList Lookups = TAILQ_HEAD_INITIALIZER(Lookups)
 Lookup table of preferred character set names.
 
static struct IconvCacheEntry IconvCache [ICONV_CACHE_SIZE]
 Cache of iconv conversion descriptors.
 
static int IconvCacheUsed = 0
 Number of iconv descriptors in the cache.
 
static const struct MimeNames PreferredMimeNames []
 Lookup table of preferred charsets.
 

Detailed Description

Conversion between different character encodings.

Authors
  • Tobias Angele
  • Richard Russon
  • Pietro Cerutti
  • Steinar H Gunderson

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file charset.c.

Macro Definition Documentation

◆ EILSEQ

#define EILSEQ   EINVAL

Definition at line 56 of file charset.c.

◆ ICONV_CACHE_SIZE

#define ICONV_CACHE_SIZE   16

Max size of the iconv cache.

Definition at line 83 of file charset.c.

Function Documentation

◆ lookup_new()

static struct Lookup * lookup_new ( void )
static

Create a new Lookup.

Return values
ptrNew Lookup

Definition at line 256 of file charset.c.

257{
258 return MUTT_MEM_CALLOC(1, struct Lookup);
259}
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Regex to String lookup table.
Definition charset.h:75
+ Here is the caller graph for this function:

◆ lookup_free()

static void lookup_free ( struct Lookup ** ptr)
static

Free a Lookup.

Parameters
ptrLookup to free

Definition at line 265 of file charset.c.

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}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
char * replacement
Alternative charset to use.
Definition charset.h:78
struct Regex regex
Regular expression.
Definition charset.h:77
char * pattern
printable version
Definition regex3.h:86
regex_t * regex
compiled expression
Definition regex3.h:87
+ Here is the caller graph for this function:

◆ lookup_charset()

static const char * lookup_charset ( enum LookupType type,
const char * cs )
static

Look for a preferred character set name.

Parameters
typeType, e.g. MUTT_LOOKUP_CHARSET
csCharacter set
Return values
ptrCharset string

If the character set matches one of the regexes, then return the replacement name.

Definition at line 290 of file charset.c.

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}
struct LookupList Lookups
Lookup table of preferred character set names.
Definition charset.c:70
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:618
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
enum LookupType type
Lookup type.
Definition charset.h:76
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_convert_nonmime_string()

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.

Parameters
[in]assumed_charsetFrom $assumed_charset
[in]charsetFrom $charset
[in,out]psString to be converted
Return values
0Success
-1Error

Work through $assumed_charset looking for a character set conversion that works. Failing that, try mutt_ch_get_default_charset().

Definition at line 318 of file charset.c.

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}
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
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
const char * mutt_ch_get_default_charset(const struct Slist *const assumed_charset)
Get the default character set.
Definition charset.c:452
#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
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
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
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
struct ListHead head
List containing values.
Definition slist.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_canonical_charset()

void mutt_ch_canonical_charset ( char * buf,
size_t buflen,
const char * name )

Canonicalise the charset of a string.

Parameters
bufBuffer for canonical character set name
buflenLength of buffer
nameName to be canonicalised

This first ties off any charset extension such as "//TRANSLIT", canonicalizes the charset and re-adds the extension

Definition at line 361 of file charset.c.

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}
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
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
static const struct MimeNames PreferredMimeNames[]
Lookup table of preferred charsets.
Definition charset.c:108
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
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
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
String manipulation buffer.
Definition buffer.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_chscmp()

bool mutt_ch_chscmp ( const char * cs1,
const char * cs2 )

Are the names of two character sets equivalent?

Parameters
cs1First character set
cs2Second character set
Return values
trueNames are equivalent
falseNames differ

Charsets may have extensions that mutt_ch_canonical_charset() leaves intact; we expect 'cs2' to originate from neomutt code, not user input (i.e. 'cs2' does not have any extension) we simply check if the shorter string is a prefix for the longer.

Definition at line 429 of file charset.c.

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}
#define MIN(a, b)
Return the minimum of two values.
Definition memory.h:40
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition charset.c:361
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
+ Here is the call graph for this function:

◆ mutt_ch_get_default_charset()

const char * mutt_ch_get_default_charset ( const struct Slist *const assumed_charset)

Get the default character set.

Parameters
assumed_charsetFrom $assumed_charset
Return values
ptrName of the default character set
Warning
This returns a pointer to a static buffer. Do not free it.

Definition at line 452 of file charset.c.

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}
#define STAILQ_FIRST(head)
Definition queue.h:388
size_t count
Number of values in list.
Definition slist.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_get_langinfo_charset()

char * mutt_ch_get_langinfo_charset ( void )

Get the user's choice of character set.

Return values
ptrCharset string

Get the canonical character set used by the user's locale. The caller must free the returned string.

Definition at line 473 of file charset.c.

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}
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_lookup_add()

bool mutt_ch_lookup_add ( enum LookupType type,
const char * pat,
const char * replace,
struct Buffer * err )

Add a new character set lookup.

Parameters
typeType of character set, e.g. MUTT_LOOKUP_CHARSET
patPattern to match
replaceReplacement string
errBuffer for error message
Return values
trueLookup added to list
falseRegex string was invalid

Add a regex for a character set and a replacement name.

Definition at line 496 of file charset.c.

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}
static struct Lookup * lookup_new(void)
Create a new Lookup.
Definition charset.c:256
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
bool pat_not
do not match
Definition regex3.h:88
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_lookup_remove()

void mutt_ch_lookup_remove ( void )

Remove all the character set lookups.

Empty the list of replacement character set names.

Definition at line 528 of file charset.c.

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}
static void lookup_free(struct Lookup **ptr)
Free a Lookup.
Definition charset.c:265
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_charset_lookup()

const char * mutt_ch_charset_lookup ( const char * chs)

Look for a replacement character set.

Parameters
chsCharacter set to lookup
Return values
ptrReplacement character set (if a 'charset-hook' matches)
NULLNo matching hook

Look through all the 'charset-hook's. If one matches return the replacement character set.

Definition at line 549 of file charset.c.

550{
552}
static const char * lookup_charset(enum LookupType type, const char *cs)
Look for a preferred character set name.
Definition charset.c:290
@ MUTT_LOOKUP_CHARSET
Alias for another character set.
Definition charset.h:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_iconv_open()

iconv_t mutt_ch_iconv_open ( const char * tocode,
const char * fromcode,
uint8_t flags )

Set up iconv for conversions.

Parameters
tocodeCurrent character set
fromcodeTarget character set
flagsFlags, e.g. MUTT_ICONV_HOOK_FROM
Return values
ptriconv handle for the conversion

Like iconv_open, but canonicalises the charsets, applies charset-hooks, recanonicalises, and finally applies iconv-hooks. Parameter flags=0 skips charset-hooks, while MUTT_ICONV_HOOK_FROM applies them to fromcode. Callers should use flags=0 when fromcode can safely be considered true, either some constant, or some value provided by the user; MUTT_ICONV_HOOK_FROM should be used only when fromcode is unsure, taken from a possibly wrong incoming MIME label, or such. Misusing MUTT_ICONV_HOOK_FROM leads to unwanted interactions in some setups.

Since calling iconv_open() repeatedly can be expensive, we keep a cache of the most recently used iconv_t objects, kept in LRU order. This means that you should not call iconv_close() on the object yourself. All remaining objects in the cache will exit when main() calls mutt_ch_cache_cleanup().

Note
By design charset-hooks should never be, and are never, applied to tocode.
The top-well-named MUTT_ICONV_HOOK_FROM acts on charset-hooks, not at all on iconv-hooks.

Definition at line 581 of file charset.c.

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}
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
static int IconvCacheUsed
Number of iconv descriptors in the cache.
Definition charset.c:87
const char * mutt_ch_iconv_lookup(const char *chs)
Look for a replacement character set.
Definition charset.c:769
const char * mutt_ch_charset_lookup(const char *chs)
Look for a replacement character set.
Definition charset.c:549
#define ICONV_CACHE_SIZE
Max size of the iconv cache.
Definition charset.c:83
static struct IconvCacheEntry IconvCache[ICONV_CACHE_SIZE]
Cache of iconv conversion descriptors.
Definition charset.c:85
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition charset.h:123
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_iconv()

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.

Parameters
[in]cdIconv conversion descriptor
[in,out]inbufBuffer to convert
[in,out]inbytesleftLength of buffer to convert
[in,out]outbufBuffer for the result
[in,out]outbytesleftLength of result buffer
[in]inreplsInput replacement characters
[in]outreplOutput replacement characters
[out]iconverrnoErrno if iconv() fails, 0 if it succeeds
Return values
numCharacters converted

Like iconv, but keeps going even when the input is invalid If you're supplying inrepls, the source charset should be stateless; if you're supplying an outrepl, the target charset should be.

Definition at line 685 of file charset.c.

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}
#define EILSEQ
Definition charset.c:56
#define ICONV_ILLEGAL_SEQ
Error value for iconv() - Illegal sequence.
Definition charset.h:114
+ Here is the caller graph for this function:

◆ mutt_ch_iconv_lookup()

const char * mutt_ch_iconv_lookup ( const char * chs)

Look for a replacement character set.

Parameters
chsCharacter set to lookup
Return values
ptrReplacement character set (if a 'iconv-hook' matches)
NULLNo matching hook

Look through all the 'iconv-hook's. If one matches return the replacement character set.

Definition at line 769 of file charset.c.

770{
772}
@ MUTT_LOOKUP_ICONV
Character set conversion.
Definition charset.h:63
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_check()

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.

Parameters
[in]sString to check
[in]slenLength of the string to check
[in]fromCurrent character set
[in]toTarget character set
Return values
0Success
-1Error in iconv_open()
>0Errno as set by iconv()

Definition at line 784 of file charset.c.

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}
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition charset.c:581
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_convert_string()

int mutt_ch_convert_string ( char ** ps,
const char * from,
const char * to,
uint8_t flags )

Convert a string between encodings.

Parameters
[in,out]psString to convert
[in]fromCurrent character set
[in]toTarget character set
[in]flagsFlags, e.g. MUTT_ICONV_HOOK_FROM
Return values
0Success
-1Invalid arguments or failure to open an iconv channel
errnoFailure in iconv conversion

Parameter flags is given as-is to mutt_ch_iconv_open(). See there for its meaning and usage policy.

Definition at line 819 of file charset.c.

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}
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
#define mutt_ch_is_utf8(str)
Definition charset.h:107
void mutt_str_adjust(char **ptr)
Shrink-to-fit a string.
Definition string.c:303
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_check_charset()

bool mutt_ch_check_charset ( const char * cs,
bool strict )

Does iconv understand a character set?

Parameters
csCharacter set to check
strictCheck strictly by using iconv
Return values
trueCharacter set is valid

If strict is false, then finding a matching character set in PreferredMimeNames will be enough. If strict is true, or the charset is not in PreferredMimeNames, then iconv() with be run.

Definition at line 882 of file charset.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_fgetconv_open()

struct FgetConv * mutt_ch_fgetconv_open ( FILE * fp,
const char * from,
const char * to,
uint8_t flags )

Prepare a file for charset conversion.

Parameters
fpFILE ptr to prepare
fromCurrent character set
toDestination character set
flagsFlags, e.g. MUTT_ICONV_HOOK_FROM
Return values
ptrfgetconv handle

Parameter flags is given as-is to mutt_ch_iconv_open().

Definition at line 921 of file charset.c.

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}
#define ICONV_T_INVALID
Error value for iconv functions.
Definition charset.h:111
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_fgetconv_close()

void mutt_ch_fgetconv_close ( struct FgetConv ** ptr)

Close an fgetconv handle.

Parameters
[out]ptrfgetconv handle

Definition at line 950 of file charset.c.

951{
952 if (!ptr || !*ptr)
953 return;
954
955 FREE(ptr);
956}
+ Here is the caller graph for this function:

◆ mutt_ch_fgetconv()

int mutt_ch_fgetconv ( struct FgetConv * fc)

Convert a file's character set.

Parameters
fcFgetConv handle
Return values
numNext character in the converted file
EOFError

A file is read into a buffer and its character set is converted. Each call to this function will return one converted character. The buffer is refilled automatically when empty.

Definition at line 968 of file charset.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_fgetconvs()

char * mutt_ch_fgetconvs ( char * buf,
size_t buflen,
struct FgetConv * fc )

Convert a file's charset into a string buffer.

Parameters
bufBuffer for result
buflenLength of buffer
fcFgetConv handle
Return values
ptrSuccess, result buffer
NULLError

Read a file into a buffer, converting the character set as it goes.

Definition at line 1030 of file charset.c.

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}
int mutt_ch_fgetconv(struct FgetConv *fc)
Convert a file's character set.
Definition charset.c:968
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_set_charset()

void mutt_ch_set_charset ( const char * charset)

Update the records for a new character set.

Parameters
charsetNew character set

Check if this character set is utf-8 and pick a suitable replacement character for unprintable characters.

Note
This calls bind_textdomain_codeset() which will affect future message translations.

Definition at line 1063 of file charset.c.

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}
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition charset.c:67
wchar_t ReplacementChar
When a Unicode character can't be displayed, use this instead.
Definition charset.c:62
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_choose()

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.

Parameters
[in]fromcodeOriginal charset of the string
[in]charsetsList of potential charsets to use
[in]uString to encode
[in]ulenLength of the string to encode
[out]dIf not NULL, point it to the converted string
[out]dlenIf not NULL, point it to the length of the d string
Return values
ptrBest performing charset
NULLNone could be found

Definition at line 1096 of file charset.c.

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}
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
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_ch_cache_cleanup()

void mutt_ch_cache_cleanup ( void )

Clean up the cached iconv handles and charset strings.

Definition at line 1165 of file charset.c.

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}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ ReplacementChar

wchar_t ReplacementChar = '?'

When a Unicode character can't be displayed, use this instead.

Definition at line 62 of file charset.c.

◆ CharsetIsUtf8

bool CharsetIsUtf8 = false

Is the user's current character set utf-8?

Definition at line 67 of file charset.c.

◆ Lookups

struct LookupList Lookups = TAILQ_HEAD_INITIALIZER(Lookups)

Lookup table of preferred character set names.

Definition at line 70 of file charset.c.

◆ IconvCache

struct IconvCacheEntry IconvCache[ICONV_CACHE_SIZE]
static

Cache of iconv conversion descriptors.

Definition at line 85 of file charset.c.

◆ IconvCacheUsed

int IconvCacheUsed = 0
static

Number of iconv descriptors in the cache.

Definition at line 87 of file charset.c.

◆ PreferredMimeNames

const struct MimeNames PreferredMimeNames[]
static

Lookup table of preferred charsets.

The following list has been created manually from the data under: http://www.isi.edu/in-notes/iana/assignments/character-sets Last update: 2000-09-07

Note
It includes only the subset of character sets for which a preferred MIME name is given.

Definition at line 108 of file charset.c.

108 {
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};