Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/config/mbtable.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Type representing a multibyte character table
4
 *
5
 * @authors
6
 * Copyright (C) 2017-2018 Richard Russon <rich@flatcap.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page config_mbtable Type: Multi-byte character table
25
 *
26
 * Config type representing a multibyte character table.
27
 *
28
 * - Backed by `struct MbTable`
29
 * - Empty multibyte character table is stored as `NULL`
30
 * - Validator is passed `struct MbTable`, which may be `NULL`
31
 * - Data is freed when `ConfigSet` is freed
32
 * - Implementation: #CstMbtable
33
 */
34
35
#include "config.h"
36
#include <stdint.h>
37
#include <string.h>
38
#include <wchar.h>
39
#include "mutt/lib.h"
40
#include "mbtable.h"
41
#include "set.h"
42
#include "types.h"
43
44
/**
45
 * mbtable_parse - Parse a multibyte string into a table
46
 * @param s String of multibyte characters
47
 * @retval ptr New MbTable object
48
 */
49
struct MbTable *mbtable_parse(const char *s)
50
39.7k
{
51
39.7k
  struct MbTable *t = NULL;
52
39.7k
  size_t slen, k;
53
39.7k
  mbstate_t mbstate = { 0 };
54
39.7k
  char *d = NULL;
55
56
39.7k
  slen = mutt_str_len(s);
57
39.7k
  if (!slen)
58
0
    return NULL;
59
60
39.7k
  t = mutt_mem_calloc(1, sizeof(struct MbTable));
61
62
39.7k
  t->orig_str = mutt_str_dup(s);
63
  /* This could be more space efficient.  However, being used on tiny
64
   * strings (`$to_chars` and `$status_chars`), the overhead is not great. */
65
39.7k
  t->chars = mutt_mem_calloc(slen, sizeof(char *));
66
39.7k
  t->segmented_str = mutt_mem_calloc(slen * 2, sizeof(char));
67
39.7k
  d = t->segmented_str;
68
69
308k
  while (slen && (k = mbrtowc(NULL, s, slen, &mbstate)))
70
268k
  {
71
268k
    if ((k == ICONV_ILLEGAL_SEQ) || (k == ICONV_BUF_TOO_SMALL))
72
0
    {
73
      /* XXX put message in err buffer; fail? warning? */
74
0
      mutt_debug(LL_DEBUG1, "mbrtowc returned %zd converting %s in %s\n", k, s, t->orig_str);
75
0
      if (k == ICONV_ILLEGAL_SEQ)
76
0
        memset(&mbstate, 0, sizeof(mbstate));
77
0
      k = (k == ICONV_ILLEGAL_SEQ) ? 1 : slen;
78
0
    }
79
80
268k
    slen -= k;
81
268k
    t->chars[t->len++] = d;
82
537k
    while (k--)
83
268k
      *d++ = *s++;
84
268k
    *d++ = '\0';
85
268k
  }
86
87
39.7k
  return t;
88
39.7k
}
89
90
/**
91
 * mbtable_destroy - Destroy an MbTable object - Implements ConfigSetType::destroy() - @ingroup cfg_type_destroy
92
 */
93
static void mbtable_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
94
89.5k
{
95
89.5k
  struct MbTable **m = var;
96
89.5k
  if (!*m)
97
49.7k
    return;
98
99
39.7k
  mbtable_free(m);
100
39.7k
}
101
102
/**
103
 * mbtable_string_set - Set an MbTable by string - Implements ConfigSetType::string_set() - @ingroup cfg_type_string_set
104
 */
105
static int mbtable_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
106
                              const char *value, struct Buffer *err)
107
0
{
108
  /* Store empty mbtables as NULL */
109
0
  if (value && (value[0] == '\0'))
110
0
    value = NULL;
111
112
0
  struct MbTable *table = NULL;
113
114
0
  int rc = CSR_SUCCESS;
115
116
0
  if (var)
117
0
  {
118
0
    struct MbTable *curval = *(struct MbTable **) var;
119
0
    if (curval && mutt_str_equal(value, curval->orig_str))
120
0
      return CSR_SUCCESS | CSR_SUC_NO_CHANGE;
121
122
0
    table = mbtable_parse(value);
123
124
0
    if (cdef->validator)
125
0
    {
126
0
      rc = cdef->validator(cs, cdef, (intptr_t) table, err);
127
128
0
      if (CSR_RESULT(rc) != CSR_SUCCESS)
129
0
      {
130
0
        mbtable_free(&table);
131
0
        return rc | CSR_INV_VALIDATOR;
132
0
      }
133
0
    }
134
135
0
    mbtable_destroy(cs, var, cdef);
136
137
0
    *(struct MbTable **) var = table;
138
139
0
    if (!table)
140
0
      rc |= CSR_SUC_EMPTY;
141
0
  }
142
0
  else
143
0
  {
144
0
    if (cdef->type & DT_INITIAL_SET)
145
0
      FREE(&cdef->initial);
146
147
0
    cdef->type |= DT_INITIAL_SET;
148
0
    cdef->initial = (intptr_t) mutt_str_dup(value);
149
0
  }
150
151
0
  return rc;
152
0
}
153
154
/**
155
 * mbtable_string_get - Get a MbTable as a string - Implements ConfigSetType::string_get() - @ingroup cfg_type_string_get
156
 */
157
static int mbtable_string_get(const struct ConfigSet *cs, void *var,
158
                              const struct ConfigDef *cdef, struct Buffer *result)
159
0
{
160
0
  const char *str = NULL;
161
162
0
  if (var)
163
0
  {
164
0
    struct MbTable *table = *(struct MbTable **) var;
165
0
    if (!table || !table->orig_str)
166
0
      return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty string */
167
0
    str = table->orig_str;
168
0
  }
169
0
  else
170
0
  {
171
0
    str = (char *) cdef->initial;
172
0
  }
173
174
0
  buf_addstr(result, str);
175
0
  return CSR_SUCCESS;
176
0
}
177
178
/**
179
 * mbtable_dup - Create a copy of an MbTable object
180
 * @param table MbTable to duplicate
181
 * @retval ptr New MbTable object
182
 */
183
static struct MbTable *mbtable_dup(struct MbTable *table)
184
0
{
185
0
  if (!table)
186
0
    return NULL; /* LCOV_EXCL_LINE */
187
188
0
  struct MbTable *m = mutt_mem_calloc(1, sizeof(*m));
189
0
  m->orig_str = mutt_str_dup(table->orig_str);
190
0
  return m;
191
0
}
192
193
/**
194
 * mbtable_native_set - Set an MbTable config item by MbTable object - Implements ConfigSetType::native_set() - @ingroup cfg_type_native_set
195
 */
196
static int mbtable_native_set(const struct ConfigSet *cs, void *var,
197
                              const struct ConfigDef *cdef, intptr_t value,
198
                              struct Buffer *err)
199
0
{
200
0
  int rc;
201
202
0
  if (cdef->validator)
203
0
  {
204
0
    rc = cdef->validator(cs, cdef, value, err);
205
206
0
    if (CSR_RESULT(rc) != CSR_SUCCESS)
207
0
      return rc | CSR_INV_VALIDATOR;
208
0
  }
209
210
0
  mbtable_free(var);
211
212
0
  struct MbTable *table = mbtable_dup((struct MbTable *) value);
213
214
0
  rc = CSR_SUCCESS;
215
0
  if (!table)
216
0
    rc |= CSR_SUC_EMPTY;
217
218
0
  *(struct MbTable **) var = table;
219
0
  return rc;
220
0
}
221
222
/**
223
 * mbtable_native_get - Get an MbTable object from a MbTable config item - Implements ConfigSetType::native_get() - @ingroup cfg_type_native_get
224
 */
225
static intptr_t mbtable_native_get(const struct ConfigSet *cs, void *var,
226
                                   const struct ConfigDef *cdef, struct Buffer *err)
227
0
{
228
0
  struct MbTable *table = *(struct MbTable **) var;
229
230
0
  return (intptr_t) table;
231
0
}
232
233
/**
234
 * mbtable_reset - Reset an MbTable to its initial value - Implements ConfigSetType::reset() - @ingroup cfg_type_reset
235
 */
236
static int mbtable_reset(const struct ConfigSet *cs, void *var,
237
                         const struct ConfigDef *cdef, struct Buffer *err)
238
49.7k
{
239
49.7k
  struct MbTable *table = NULL;
240
49.7k
  const char *initial = (const char *) cdef->initial;
241
242
49.7k
  struct MbTable *curtable = *(struct MbTable **) var;
243
49.7k
  const char *curval = curtable ? curtable->orig_str : NULL;
244
245
49.7k
  int rc = CSR_SUCCESS;
246
49.7k
  if (!curtable)
247
49.7k
    rc |= CSR_SUC_EMPTY;
248
249
49.7k
  if (mutt_str_equal(initial, curval))
250
9.94k
    return rc | CSR_SUC_NO_CHANGE;
251
252
39.7k
  if (initial)
253
39.7k
    table = mbtable_parse(initial);
254
255
39.7k
  if (cdef->validator)
256
0
  {
257
0
    rc = cdef->validator(cs, cdef, (intptr_t) table, err);
258
259
0
    if (CSR_RESULT(rc) != CSR_SUCCESS)
260
0
    {
261
0
      mbtable_destroy(cs, &table, cdef);
262
0
      return rc | CSR_INV_VALIDATOR;
263
0
    }
264
0
  }
265
266
39.7k
  if (!table)
267
0
    rc |= CSR_SUC_EMPTY;
268
269
39.7k
  mbtable_destroy(cs, var, cdef);
270
271
39.7k
  *(struct MbTable **) var = table;
272
39.7k
  return rc;
273
39.7k
}
274
275
/**
276
 * mbtable_free - Free an MbTable object
277
 * @param[out] ptr MbTable to free
278
 */
279
void mbtable_free(struct MbTable **ptr)
280
39.7k
{
281
39.7k
  if (!ptr || !*ptr)
282
0
    return;
283
284
39.7k
  struct MbTable *table = *ptr;
285
39.7k
  FREE(&table->orig_str);
286
39.7k
  FREE(&table->chars);
287
39.7k
  FREE(&table->segmented_str);
288
289
39.7k
  FREE(ptr);
290
39.7k
}
291
292
/**
293
 * mbtable_get_nth_wchar - Extract one char from a multi-byte table
294
 * @param table  Multi-byte table
295
 * @param index  Select this character
296
 * @retval ptr String pointer to the character
297
 *
298
 * Extract one multi-byte character from a string table.
299
 * If the index is invalid, then a space character will be returned.
300
 * If the character selected is '\n' (Ctrl-M), then "" will be returned.
301
 */
302
const char *mbtable_get_nth_wchar(const struct MbTable *table, int index)
303
0
{
304
0
  if (!table || !table->chars || (index < 0) || (index >= table->len))
305
0
    return " ";
306
307
0
  if (table->chars[index][0] == '\r')
308
0
    return "";
309
310
0
  return table->chars[index];
311
0
}
312
313
/**
314
 * CstMbtable - Config type representing a multi-byte table
315
 */
316
const struct ConfigSetType CstMbtable = {
317
  DT_MBTABLE,
318
  "mbtable",
319
  mbtable_string_set,
320
  mbtable_string_get,
321
  mbtable_native_set,
322
  mbtable_native_get,
323
  NULL, // string_plus_equals
324
  NULL, // string_minus_equals
325
  mbtable_reset,
326
  mbtable_destroy,
327
};