~ubuntu-branches/ubuntu/wily/aegisub/wily-proposed

« back to all changes in this revision

Viewing changes to libaegisub/common/charset_conv.cpp

  • Committer: Package Import Robot
  • Author(s): Sebastian Reichel, Pascal De Vuyst, Juan Picca, Sebastian Reichel
  • Date: 2015-08-04 21:40:50 UTC
  • mfrom: (5.1.1 sid)
  • Revision ID: package-import@ubuntu.com-20150804214050-y2aghm9vdksoc8t7
Tags: 3.2.2+dfsg-1
[ Pascal De Vuyst ]
* Fix Typo in package description (Closes: #739219)

[ Juan Picca ]
* Add patch to fix reproducible build (Closes: #789728)

[ Sebastian Reichel ]
* New upstream release
 - remove vendor directory from orig tarball
* Update Debian Standards Version to 3.9.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
#include <cstdint>
21
21
 
22
22
#include <cassert>
23
 
#include <map>
24
 
 
25
 
#include "../config.h"
 
23
#include <boost/range/algorithm.hpp>
26
24
 
27
25
#include <libaegisub/charset_conv.h>
28
26
#include <iconv.h>
45
43
static const size_t iconv_failed = (size_t)-1;
46
44
 
47
45
namespace {
48
 
        struct ltstr {
49
 
                bool operator()(const char* s1, const char* s2) const {
50
 
                        return strcmp(s1, s2) < 0;
51
 
                }
52
 
        };
 
46
        using namespace agi::charset;
53
47
 
54
 
        agi::charset::Converter *get_converter(bool subst, const char *src, const char *dst);
 
48
        Converter *get_converter(bool subst, const char *src, const char *dst);
55
49
 
56
50
/// @brief Map a user-friendly encoding name to the real encoding name
57
 
        const char* get_real_encoding_name(const char* name) {
58
 
                static std::map<const char*, const char*, ltstr> pretty_names;
59
 
 
60
 
                if (pretty_names.empty()) {
61
 
#                       define ADD(pretty, real) pretty_names[pretty] = real
 
51
        const char *get_real_encoding_name(const char *name) {
 
52
        struct pair { const char *pretty; const char *real; };
 
53
        static pair pretty_names[] = {
 
54
#                       define ADD(pretty, real) pair{pretty, real},
62
55
#                       include <libaegisub/charsets.def>
63
56
#                       undef ADD
64
 
                }
65
 
 
66
 
                auto real = pretty_names.find(name);
67
 
                if (real != pretty_names.end())
68
 
                        return real->second;
69
 
                return name;
 
57
        };
 
58
 
 
59
        static bool init = false;
 
60
        if (!init) {
 
61
            init = true;
 
62
            boost::sort(pretty_names, [](pair a, pair b) {
 
63
                return strcmp(a.pretty, b.pretty) < 0;
 
64
            });
 
65
        }
 
66
 
 
67
        auto enc = boost::lower_bound(pretty_names, name, [](pair a, const char *b) {
 
68
                        return strcmp(a.pretty, b) < 0;
 
69
        });
 
70
 
 
71
        if (enc != std::end(pretty_names) && strcmp(enc->pretty, name) == 0)
 
72
            return enc->real;
 
73
        return name;
70
74
        }
71
75
 
72
 
        size_t get_bom_size(iconv_t cd) {
 
76
        size_t get_bom_size(Iconv& cd) {
73
77
                // Most (but not all) iconv implementations automatically insert a BOM
74
78
                // at the beginning of text converted to UTF-8, UTF-16 and UTF-32, but
75
79
                // we usually don't want this, as some of the wxString using code
83
87
                size_t srcLen = 1;
84
88
                size_t dstLen = 8;
85
89
 
86
 
                size_t res = iconv(cd, ICONV_CONST_CAST(&src), &srcLen, &dst, &dstLen);
 
90
                size_t res = cd(&src, &srcLen, &dst, &dstLen);
87
91
                assert(res != iconv_failed);
88
92
                assert(srcLen == 0);
89
93
 
98
102
                return size;
99
103
        }
100
104
 
101
 
        void eat_bom(iconv_t cd, size_t bomSize, const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
 
105
        void eat_bom(Iconv& cd, size_t bomSize, const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
102
106
                // If this encoding has a forced BOM (i.e. it's UTF-16 or UTF-32 without
103
107
                // a specified byte order), skip over it
104
108
                if (bomSize > 0 && inbytesleft && *inbytesleft) {
110
114
                        size_t dstSize = std::min((size_t)8, bomSize + *outbytesleft);
111
115
                        const char *src = *inbuf;
112
116
                        size_t srcSize = *inbytesleft;
113
 
                        iconv(cd, ICONV_CONST_CAST(&src), &srcSize, &dst, &dstSize);
 
117
                        cd(&src, &srcSize, &dst, &dstSize);
114
118
                }
115
119
        }
116
120
 
117
121
        // Calculate the size of NUL in the given character set
118
 
        size_t nul_size(const char* encoding) {
 
122
        size_t nul_size(const char *encoding) {
119
123
                // We need a character set to convert from with a known encoding of NUL
120
124
                // UTF-8 seems like the obvious choice
121
 
                std::unique_ptr<agi::charset::Converter> cd(get_converter(false, "UTF-8", encoding));
 
125
                std::unique_ptr<Converter> cd(get_converter(false, "UTF-8", encoding));
122
126
 
123
127
                char dbuff[4];
124
128
                char sbuff[] = "";
135
139
        }
136
140
 
137
141
#ifdef ICONV_POSIX
138
 
        class ConverterImpl : public agi::charset::Converter {
 
142
        class ConverterImpl final : public Converter {
139
143
                size_t bomSize;
140
 
                iconv_t cd;
 
144
                Iconv cd;
141
145
        public:
142
146
                // subst is not used here because POSIX doesn't let you disable substitution
143
147
                ConverterImpl(bool, const char* sourceEncoding, const char* destEncoding)
144
148
                {
145
149
                        const char *dstEnc = get_real_encoding_name(destEncoding);
146
 
                        cd = iconv_open(dstEnc, "UTF-8");
147
 
                        if (cd == iconv_invalid)
148
 
                                throw agi::charset::UnsupportedConversion(std::string(dstEnc) + " is not a supported character set");
 
150
                        cd = Iconv("utf-8", dstEnc);
149
151
 
150
152
                        bomSize = get_bom_size(cd);
151
 
                        iconv_close(cd);
152
 
                        cd = iconv_open(dstEnc, get_real_encoding_name(sourceEncoding));
153
 
                        if (cd == iconv_invalid)
154
 
                                throw agi::charset::UnsupportedConversion(std::string("Cannot convert from ") + sourceEncoding + " to " + destEncoding);
155
 
                }
156
 
                ~ConverterImpl() {
157
 
                        if (cd != iconv_invalid) iconv_close(cd);
158
 
                }
 
153
                        cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
 
154
                }
 
155
 
159
156
                size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
160
157
                        eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
161
158
 
162
 
                        size_t res = iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
 
159
                        size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
163
160
 
164
161
                        // This loop never does anything useful with a POSIX-compliant iconv
165
162
                        // implementation, but those don't seem to actually exist
166
163
                        while (res == iconv_failed && errno != E2BIG) {
167
164
                                ++*inbuf;
168
165
                                --*inbytesleft;
169
 
                                res = iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
 
166
                                res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
170
167
                        }
171
168
 
172
169
                        return res;
175
172
 
176
173
#else
177
174
 
178
 
        class ConverterImpl : public iconv_fallbacks, public agi::charset::Converter {
 
175
        class ConverterImpl final : public iconv_fallbacks, public Converter {
179
176
                size_t bomSize;
180
177
                char invalidRep[8];
181
178
                size_t invalidRepSize;
182
 
                iconv_t cd;
 
179
                Iconv cd;
183
180
                static void fallback(
184
181
                        unsigned int code,
185
182
                        void (*callback) (const char *buf, size_t buflen, void* callback_arg),
196
193
                                callback(self->invalidRep, self->invalidRepSize, callback_arg);
197
194
                        }
198
195
                }
199
 
                ConverterImpl(ConverterImpl const&);
200
 
                ConverterImpl& operator=(ConverterImpl const&);
 
196
 
201
197
        public:
202
198
                ConverterImpl(bool subst, const char* sourceEncoding, const char* destEncoding)
203
199
                {
204
200
                        const char *dstEnc = get_real_encoding_name(destEncoding);
205
 
                        cd = iconv_open(dstEnc, "UTF-8");
206
 
                        if (cd == iconv_invalid)
207
 
                                throw agi::charset::UnsupportedConversion(std::string(dstEnc) + " is not a supported character set");
 
201
                        cd = Iconv("utf-8", dstEnc);
208
202
 
209
203
                        bomSize = get_bom_size(cd);
210
204
 
221
215
 
222
216
                        invalidRepSize = 4 - dstLen;
223
217
 
224
 
                        iconv_close(cd);
225
 
                        cd = iconv_open(dstEnc, get_real_encoding_name(sourceEncoding));
226
 
                        if (cd == iconv_invalid)
227
 
                                throw agi::charset::UnsupportedConversion(std::string("Cannot convert from ") + sourceEncoding + " to " + destEncoding);
 
218
                        cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
228
219
 
229
220
                        if (subst) {
230
221
                                data = this;
238
229
                                iconvctl(cd, ICONV_SET_FALLBACKS, static_cast<iconv_fallbacks*>(this));
239
230
                        }
240
231
                }
241
 
                ~ConverterImpl() {
242
 
                        if (cd != iconv_invalid) iconv_close(cd);
243
 
                }
 
232
 
244
233
                size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) override {
245
234
                        eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
246
 
                        size_t res = iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
 
235
                        size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
247
236
 
248
237
                        if (res == iconv_failed && errno == E2BIG && *outbytesleft == 0) {
249
238
                                // libiconv checks if there are any bytes left in the output buffer
256
245
                                const char* in = *inbuf;
257
246
                                size_t insize = *inbytesleft;
258
247
 
259
 
                                res = iconv(cd, ICONV_CONST_CAST(&in), &insize, &out, &buffsize);
 
248
                                res = cd(&in, &insize, &out, &buffsize);
260
249
                                // If no bytes of the output buffer were used, the original
261
250
                                // conversion may have been successful
262
251
                                if (buffsize != 8) {
270
259
        };
271
260
#endif
272
261
 
273
 
        agi::charset::Converter *get_converter(bool subst, const char *src, const char *dst) {
 
262
        Converter *get_converter(bool subst, const char *src, const char *dst) {
274
263
                try {
275
264
                        return new ConverterImpl(subst, src, dst);
276
265
                }
277
 
                catch (agi::charset::UnsupportedConversion const&) {
 
266
                catch (UnsupportedConversion const&) {
278
267
                        if (strcmp(dst, "ISO-6937-2"))
279
268
                                throw;
280
 
                        return new agi::charset::Converter6937(subst, src);
 
269
                        return new Converter6937(subst, src);
281
270
                }
282
271
        }
283
272
} // namespace {
284
273
 
285
274
namespace agi { namespace charset {
 
275
Iconv::Iconv() : cd(iconv_invalid) { }
 
276
 
 
277
Iconv::Iconv(const char *source, const char *dest)
 
278
: cd(iconv_open(dest, source))
 
279
{
 
280
        if (cd == iconv_invalid)
 
281
                throw UnsupportedConversion(std::string("Cannot convert from ") + source + " to " + dest);
 
282
}
 
283
 
 
284
Iconv::~Iconv() {
 
285
        if (cd != iconv_invalid) iconv_close(cd);
 
286
}
 
287
 
 
288
size_t Iconv::operator()(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) {
 
289
        return iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
 
290
}
286
291
 
287
292
IconvWrapper::IconvWrapper(const char* sourceEncoding, const char* destEncoding, bool enableSubst)
288
293
: conv(get_converter(enableSubst, sourceEncoding, destEncoding))
292
297
        toNulLen = nul_size(destEncoding);
293
298
        fromNulLen = nul_size(sourceEncoding);
294
299
}
295
 
IconvWrapper::~IconvWrapper() {
296
 
}
297
 
 
298
 
std::string IconvWrapper::Convert(std::string const& source) {
 
300
 
 
301
IconvWrapper::~IconvWrapper() { }
 
302
 
 
303
std::string IconvWrapper::Convert(const char *source, size_t len) {
299
304
        std::string dest;
300
 
        Convert(source, dest);
 
305
        Convert(source, len, dest);
301
306
        return dest;
302
307
}
303
 
void IconvWrapper::Convert(std::string const& source, std::string &dest) {
 
308
 
 
309
void IconvWrapper::Convert(const char *src, size_t srcLen, std::string &dest) {
304
310
        char buff[512];
305
311
 
306
 
        const char *src = source.data();
307
 
        size_t srcLen = source.size();
308
312
        size_t res;
309
313
        do {
310
314
                char *dst = buff;
314
318
 
315
319
                dest.append(buff, sizeof(buff) - dstLen);
316
320
        } while (res == iconv_failed && errno == E2BIG);
317
 
        
 
321
 
318
322
        if (res == iconv_failed) {
319
323
                switch (errno) {
320
324
                        case EINVAL: