From 5dce1023bc5a3cf15b4f5d8159e198f3499f4009 Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Tue, 27 Dec 2016 15:23:19 -0800 Subject: [PATCH] Create mbchar_table type for multibyte character arrays. (see #3024) This type is to allow multibyte characters in to_chars and status_chars while preserving efficient indexing to each character. The arrays are tokenized during initialization, and are re-tokenized as the values are unset, reset, and set. --- doc/makedoc.c | 17 ++++++++---- init.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++- init.h | 3 +- mutt.h | 11 ++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) diff --git a/doc/makedoc.c b/doc/makedoc.c index bcc727142..8ad4519d2 100644 --- a/doc/makedoc.c +++ b/doc/makedoc.c @@ -358,7 +358,8 @@ enum DT_RX, DT_MAGIC, DT_SYN, - DT_ADDR + DT_ADDR, + DT_MBCHARTBL }; struct @@ -379,6 +380,7 @@ types[] = { "DT_MAGIC", "folder magic" }, { "DT_SYN", NULL }, { "DT_ADDR", "e-mail address" }, + { "DT_MBCHARTBL", "string" }, { NULL, NULL } }; @@ -520,6 +522,7 @@ static void pretty_default (char *t, size_t l, const char *s, int type) case DT_RX: case DT_ADDR: case DT_PATH: + case DT_MBCHARTBL: { if (!strcmp (s, "0")) break; @@ -658,7 +661,8 @@ static void print_confline (const char *varname, int type, const char *val, FILE /* configuration file */ case F_CONF: { - if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH) + if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH || + type == DT_MBCHARTBL) { fprintf (out, "\n# set %s=\"", varname); conf_print_strval (val, out); @@ -669,7 +673,8 @@ static void print_confline (const char *varname, int type, const char *val, FILE fprintf (out, "\n#\n# Name: %s", varname); fprintf (out, "\n# Type: %s", type2human (type)); - if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH) + if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH || + type == DT_MBCHARTBL) { fputs ("\n# Default: \"", out); conf_print_strval (val, out); @@ -688,7 +693,8 @@ static void print_confline (const char *varname, int type, const char *val, FILE fprintf (out, "\n.TP\n.B %s\n", varname); fputs (".nf\n", out); fprintf (out, "Type: %s\n", type2human (type)); - if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH) + if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH || + type == DT_MBCHARTBL) { fputs ("Default: \"", out); man_print_strval (val, out); @@ -715,7 +721,8 @@ static void print_confline (const char *varname, int type, const char *val, FILE fprintf (out, "\nType: %s", type2human (type)); - if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH) + if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH || + type == DT_MBCHARTBL) { if (val && *val) { diff --git a/init.c b/init.c index e3e33f80e..861834349 100644 --- a/init.c +++ b/init.c @@ -731,6 +731,59 @@ static int parse_ifdef (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) return 0; } +static void free_mbchar_table (mbchar_table **t) +{ + if (!t || !*t) + return; + + FREE (&(*t)->chars); + FREE (&(*t)->segmented_str); + FREE (&(*t)->orig_str); + FREE (t); /* __FREE_CHECKED__ */ +} + +static mbchar_table *parse_mbchar_table (const char *s) +{ + mbchar_table *t; + size_t slen, k; + mbstate_t mbstate; + char *d; + + t = safe_calloc (1, sizeof (mbchar_table)); + slen = mutt_strlen (s); + if (!slen) + return t; + + t->orig_str = safe_strdup (s); + /* This could be more space efficient. However, being used on tiny + * strings (Tochars and StChars), the overhead is not great. */ + t->chars = safe_calloc (slen, sizeof (char *)); + d = t->segmented_str = safe_calloc (slen * 2, sizeof (char)); + + memset (&mbstate, 0, sizeof (mbstate)); + while (slen && (k = mbrtowc (NULL, s, slen, &mbstate))) + { + if (k == (size_t)(-1) || k == (size_t)(-2)) + { + dprint (1, (debugfile, + "parse_mbchar_table: mbrtowc returned %d converting %s in %s\n", + (k == (size_t)(-1)) ? -1 : -2, + s, t->orig_str)); + if (k == (size_t)(-1)) + memset (&mbstate, 0, sizeof (mbstate)); + k = (k == (size_t)(-1)) ? 1 : slen; + } + + slen -= k; + t->chars[t->len++] = d; + while (k--) + *d++ = *s++; + *d++ = '\0'; + } + + return t; +} + static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) { do @@ -1663,6 +1716,10 @@ static void mutt_restore_default (struct option_t *p) case DT_STR: mutt_str_replace ((char **) p->data, (char *) p->init); break; + case DT_MBCHARTBL: + free_mbchar_table ((mbchar_table **)p->data); + *((mbchar_table **) p->data) = parse_mbchar_table ((char *) p->init); + break; case DT_PATH: FREE((char **) p->data); /* __FREE_CHECKED__ */ if (p->init) @@ -2090,7 +2147,8 @@ static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) } else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR || DTYPE (MuttVars[idx].type) == DT_PATH || - DTYPE (MuttVars[idx].type) == DT_ADDR) + DTYPE (MuttVars[idx].type) == DT_ADDR || + DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) { if (unset) { @@ -2099,6 +2157,8 @@ static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) myvar_del (myvar); else if (DTYPE (MuttVars[idx].type) == DT_ADDR) rfc822_free_address ((ADDRESS **) MuttVars[idx].data); + else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) + free_mbchar_table ((mbchar_table **) MuttVars[idx].data); else /* MuttVars[idx].data is already 'char**' (or some 'void**') or... * so cast to 'void*' is okay */ @@ -2135,6 +2195,11 @@ static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) mutt_pretty_mailbox (_tmp, sizeof (_tmp)); val = _tmp; } + else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) + { + mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data)); + val = mbt ? NONULL (mbt->orig_str) : ""; + } else val = *((char **) MuttVars[idx].data); @@ -2189,6 +2254,11 @@ static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) if (mutt_strcmp (MuttVars[idx].option, "charset") == 0) mutt_set_charset (Charset); } + else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) + { + free_mbchar_table ((mbchar_table **) MuttVars[idx].data); + *((mbchar_table **) MuttVars[idx].data) = parse_mbchar_table (tmp->data); + } else { rfc822_free_address ((ADDRESS **) MuttVars[idx].data); @@ -3128,6 +3198,11 @@ static int var_to_string (int idx, char* val, size_t len) if (DTYPE (MuttVars[idx].type) == DT_PATH) mutt_pretty_mailbox (tmp, sizeof (tmp)); } + else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) + { + mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data)); + strfcpy (tmp, mbt ? NONULL (mbt->orig_str) : "", sizeof (tmp)); + } else if (DTYPE (MuttVars[idx].type) == DT_ADDR) { rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0); diff --git a/init.h b/init.h index add06ad8f..6c39ac800 100644 --- a/init.h +++ b/init.h @@ -38,7 +38,8 @@ #define DT_MAGIC 8 /* mailbox type */ #define DT_SYN 9 /* synonym for another variable */ #define DT_ADDR 10 /* e-mail address */ -#define DT_HCACHE 11 /* header cache backend */ +#define DT_MBCHARTBL 11 /* multibyte char table */ +#define DT_HCACHE 12 /* header cache backend */ #define DTYPE(x) ((x) & DT_MASK) diff --git a/mutt.h b/mutt.h index 1d693e0c7..6e44a4cf3 100644 --- a/mutt.h +++ b/mutt.h @@ -1106,6 +1106,17 @@ typedef struct regex_t minor_rx; } ATTACH_MATCH; +/* multibyte character table. + * Allows for direct access to the individual multibyte characters in a + * string. This is used for the Tochars and StChars option types. */ +typedef struct +{ + int len; /* number of characters */ + char **chars; /* the array of multibyte character strings */ + char *segmented_str; /* each chars entry points inside this string */ + char *orig_str; +} mbchar_table; + #define MUTT_PARTS_TOPLEVEL (1<<0) /* is the top-level part */ #include "ascii.h" -- 2.40.0