NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
convert.c File Reference

Conversion between different character encodings. More...

#include "config.h"
#include <errno.h>
#include <iconv.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "mutt/lib.h"
#include "email/lib.h"
#include "lib.h"
Include dependency graph for convert.c:

Go to the source code of this file.

Functions

size_t mutt_convert_file_to (FILE *fp, const char *fromcode, struct Slist const *const tocodes, int *tocode, struct Content *info)
 Change the encoding of a file.
size_t mutt_convert_file_from_to (FILE *fp, const struct Slist *fromcodes, const struct Slist *tocodes, char **fromcode, char **tocode, struct Content *info)
 Convert a file between encodings.

Detailed Description

Conversion between different character encodings.

Authors
  • Michal Siedlaczek
  • Richard Russon

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 convert.c.

Function Documentation

◆ mutt_convert_file_to()

size_t mutt_convert_file_to ( FILE * fp,
const char * fromcode,
struct Slist const *const tocodes,
int * tocode,
struct Content * info )

Change the encoding of a file.

Parameters
[in]fpFile to convert
[in]fromcodeOriginal encoding
[in]tocodesList of target encodings
[out]tocodeChosen encoding
[out]infoEncoding information
Return values
-1Error, no conversion was possible
>0Success, number of bytes converted

Find the best charset conversion of the file from fromcode into one of the tocodes. If successful, set *tocode and Content *info and return the number of characters converted inexactly.

We convert via UTF-8 in order to avoid the condition -1(EINVAL), which would otherwise prevent us from knowing the number of inexact conversions. Where the candidate target charset is UTF-8 we avoid doing the second conversion because iconv_open("UTF-8", "UTF-8") fails with some libraries.

We assume that the output from iconv is never more than 4 times as long as the input for any pair of charsets we might be interested in.

Definition at line 64 of file convert.c.

66{
67 char bufi[256] = { 0 };
68 char bufu[512] = { 0 };
69 char bufo[4 * sizeof(bufi)] = { 0 };
70 size_t rc = ICONV_ILLEGAL_SEQ;
71
72 const iconv_t cd1 = mutt_ch_iconv_open("utf-8", fromcode, MUTT_ICONV_NONE);
73 if (!iconv_t_valid(cd1))
74 return -1;
75
76 int ncodes = tocodes->count;
77 iconv_t *cd = MUTT_MEM_CALLOC(ncodes, iconv_t);
78 size_t *score = MUTT_MEM_CALLOC(ncodes, size_t);
79 struct ContentState *states = MUTT_MEM_CALLOC(ncodes, struct ContentState);
80 struct Content *infos = MUTT_MEM_CALLOC(ncodes, struct Content);
81
82 struct ListNode *np = NULL;
83 int ni = 0;
84 STAILQ_FOREACH(np, &tocodes->head, entries)
85 {
86 if (!mutt_istr_equal(np->data, "utf-8"))
87 {
88 cd[ni] = mutt_ch_iconv_open(np->data, "utf-8", MUTT_ICONV_NONE);
89 }
90 else
91 {
92 /* Special case for conversion to UTF-8 */
93 cd[ni] = ICONV_T_INVALID;
94 score[ni] = ICONV_ILLEGAL_SEQ;
95 }
96 ni += 1;
97 }
98
99 fseek(fp, 0, SEEK_SET);
100 clearerr(fp);
101 size_t ibl = 0;
102 while (true)
103 {
104 /* Try to fill input buffer */
105 size_t n = fread(bufi + ibl, 1, sizeof(bufi) - ibl, fp);
106 ibl += n;
107
108 /* Convert to UTF-8 */
109 const char *ib = bufi;
110 char *ob = bufu;
111 size_t obl = sizeof(bufu);
112 n = iconv(cd1, (ICONV_CONST char **) ((ibl != 0) ? &ib : 0), &ibl, &ob, &obl);
113 if ((n == ICONV_ILLEGAL_SEQ) && (((errno != EINVAL) && (errno != E2BIG)) || (ib == bufi)))
114 {
116 break;
117 }
118 const size_t ubl1 = ob - bufu;
119
120 /* Convert from UTF-8 */
121 for (int i = 0; i < ncodes; i++)
122 {
123 if (iconv_t_valid(cd[i]) && (score[i] != ICONV_ILLEGAL_SEQ))
124 {
125 const char *ub = bufu;
126 size_t ubl = ubl1;
127 ob = bufo;
128 obl = sizeof(bufo);
129 n = iconv(cd[i], (ICONV_CONST char **) ((ibl || ubl) ? &ub : 0), &ubl, &ob, &obl);
130 if (n == ICONV_ILLEGAL_SEQ)
131 {
132 score[i] = ICONV_ILLEGAL_SEQ;
133 }
134 else
135 {
136 score[i] += n;
137 mutt_update_content_info(&infos[i], &states[i], bufo, ob - bufo);
138 }
139 }
140 else if (!iconv_t_valid(cd[i]) && (score[i] == ICONV_ILLEGAL_SEQ))
141 {
142 /* Special case for conversion to UTF-8 */
143 mutt_update_content_info(&infos[i], &states[i], bufu, ubl1);
144 }
145 }
146
147 if (ibl)
148 {
149 /* Save unused input */
150 memmove(bufi, ib, ibl);
151 }
152 else if (!ubl1 && (ib < bufi + sizeof(bufi)))
153 {
154 rc = 0;
155 break;
156 }
157 }
158
159 if (rc == 0)
160 {
161 /* Find best score */
163 for (int i = 0; i < ncodes; i++)
164 {
165 if (!iconv_t_valid(cd[i]) && (score[i] == ICONV_ILLEGAL_SEQ))
166 {
167 /* Special case for conversion to UTF-8 */
168 *tocode = i;
169 rc = 0;
170 break;
171 }
172 else if (!iconv_t_valid(cd[i]) || (score[i] == ICONV_ILLEGAL_SEQ))
173 {
174 continue;
175 }
176 else if ((rc == ICONV_ILLEGAL_SEQ) || (score[i] < rc))
177 {
178 *tocode = i;
179 rc = score[i];
180 if (rc == 0)
181 break;
182 }
183 }
184 if (rc != ICONV_ILLEGAL_SEQ)
185 {
186 memcpy(info, &infos[*tocode], sizeof(struct Content));
187 mutt_update_content_info(info, &states[*tocode], 0, 0); /* EOF */
188 }
189 }
190
191 FREE(&cd);
192 FREE(&infos);
193 FREE(&score);
194 FREE(&states);
195
196 return rc;
197}
void mutt_update_content_info(struct Content *info, struct ContentState *s, char *buf, size_t buflen)
Cache some info about an email.
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition charset.c:581
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define ICONV_T_INVALID
Error value for iconv functions.
Definition charset.h:111
#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_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
Info about the body of an email.
Definition content.h:56
Info about an attachment.
Definition content.h:35
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mutt_convert_file_from_to()

size_t mutt_convert_file_from_to ( FILE * fp,
const struct Slist * fromcodes,
const struct Slist * tocodes,
char ** fromcode,
char ** tocode,
struct Content * info )

Convert a file between encodings.

Parameters
[in]fpFile to read from
[in]fromcodesCharsets to try converting FROM
[in]tocodesCharsets to try converting TO
[out]fromcodeFrom charset selected
[out]tocodeTo charset selected
[out]infoInfo about the file
Return values
numCharacters converted
ICONV_ILLEGAL_SEQError (as a size_t)

Find the first of the fromcodes that gives a valid conversion and the best charset conversion of the file into one of the tocodes. If successful, set *fromcode and *tocode to dynamically allocated strings, set Content *info, and return the number of characters converted inexactly. If no conversion was possible, return -1.

Definition at line 216 of file convert.c.

219{
220 char **tcode = NULL;
221 size_t rc;
222 int cn = 0;
223 struct ListNode *np = NULL;
224
225 /* Copy them */
226 tcode = MUTT_MEM_CALLOC(tocodes->count, char *);
227 np = NULL;
228 STAILQ_FOREACH(np, &tocodes->head, entries)
229 {
230 tcode[cn++] = mutt_str_dup(np->data);
231 }
232
234 np = NULL;
235 cn = 0;
236 STAILQ_FOREACH(np, &fromcodes->head, entries)
237 {
238 /* Try each fromcode in turn */
239 rc = mutt_convert_file_to(fp, np->data, tocodes, &cn, info);
240 if (rc != ICONV_ILLEGAL_SEQ)
241 {
242 *fromcode = mutt_str_dup(np->data);
243 *tocode = tcode[cn];
244 tcode[cn] = 0;
245 break;
246 }
247 }
248
249 /* Free memory */
250 for (cn = 0; cn < tocodes->count; cn++)
251 FREE(&tcode[cn]);
252
253 FREE(&tcode);
254
255 return rc;
256}
size_t mutt_convert_file_to(FILE *fp, const char *fromcode, struct Slist const *const tocodes, int *tocode, struct Content *info)
Change the encoding of a file.
Definition convert.c:64
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
struct ListHead head
List containing values.
Definition slist.h:38
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: