/src/libarchive/libarchive/archive_string.c
Line | Count | Source |
1 | | /*- |
2 | | * Copyright (c) 2003-2011 Tim Kientzle |
3 | | * Copyright (c) 2011-2012 Michihiro NAKAJIMA |
4 | | * All rights reserved. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * 1. Redistributions of source code must retain the above copyright |
10 | | * notice, this list of conditions and the following disclaimer. |
11 | | * 2. Redistributions in binary form must reproduce the above copyright |
12 | | * notice, this list of conditions and the following disclaimer in the |
13 | | * documentation and/or other materials provided with the distribution. |
14 | | * |
15 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
16 | | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
17 | | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
18 | | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
19 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
20 | | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
21 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
22 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
24 | | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | */ |
26 | | |
27 | | #include "archive_platform.h" |
28 | | |
29 | | /* |
30 | | * Basic resizable string support, to simplify manipulating arbitrary-sized |
31 | | * strings while minimizing heap activity. |
32 | | * |
33 | | * In particular, the buffer used by a string object is only grown, it |
34 | | * never shrinks, so you can clear and reuse the same string object |
35 | | * without incurring additional memory allocations. |
36 | | */ |
37 | | |
38 | | #ifdef HAVE_ERRNO_H |
39 | | #include <errno.h> |
40 | | #endif |
41 | | #ifdef HAVE_ICONV_H |
42 | | #include <iconv.h> |
43 | | #endif |
44 | | #ifdef HAVE_LANGINFO_H |
45 | | #include <langinfo.h> |
46 | | #endif |
47 | | #ifdef HAVE_LOCALCHARSET_H |
48 | | #include <localcharset.h> |
49 | | #endif |
50 | | #ifdef HAVE_STDLIB_H |
51 | | #include <stdlib.h> |
52 | | #endif |
53 | | #ifdef HAVE_STRING_H |
54 | | #include <string.h> |
55 | | #endif |
56 | | #ifdef HAVE_WCHAR_H |
57 | | #include <wchar.h> |
58 | | #endif |
59 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
60 | | #include <windows.h> |
61 | | #include <locale.h> |
62 | | #endif |
63 | | |
64 | | #include "archive_endian.h" |
65 | | #include "archive_integer.h" |
66 | | #include "archive_private.h" |
67 | | #include "archive_string.h" |
68 | | #include "archive_string_composition.h" |
69 | | |
70 | | #if !defined(HAVE_WMEMCPY) && !defined(wmemcpy) |
71 | | #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) |
72 | | #endif |
73 | | |
74 | | #if !defined(HAVE_WMEMMOVE) && !defined(wmemmove) |
75 | | #define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t)) |
76 | | #endif |
77 | | |
78 | | #undef max |
79 | 1.01k | #define max(a, b) ((a)>(b)?(a):(b)) |
80 | | |
81 | | struct archive_string_conv { |
82 | | struct archive_string_conv *next; |
83 | | char *from_charset; |
84 | | char *to_charset; |
85 | | unsigned from_cp; |
86 | | unsigned to_cp; |
87 | | /* Set 1 if from_charset and to_charset are the same. */ |
88 | | int same; |
89 | | int flag; |
90 | 866k | #define SCONV_TO_CHARSET 1 /* MBS is being converted to specified |
91 | | * charset. */ |
92 | 1.04M | #define SCONV_FROM_CHARSET (1<<1) /* MBS is being converted from |
93 | | * specified charset. */ |
94 | 463k | #define SCONV_BEST_EFFORT (1<<2) /* Copy at least ASCII code. */ |
95 | 461k | #define SCONV_WIN_CP (1<<3) /* Use Windows API for converting |
96 | | * MBS. */ |
97 | 461k | #define SCONV_UTF8_LIBARCHIVE_2 (1<<4) /* Incorrect UTF-8 made by libarchive |
98 | | * 2.x in the wrong assumption. */ |
99 | 118k | #define SCONV_NORMALIZATION_C (1<<6) /* Need normalization to be Form C. |
100 | | * Before UTF-8 characters are actually |
101 | | * processed. */ |
102 | 59.4k | #define SCONV_NORMALIZATION_D (1<<7) /* Need normalization to be Form D. |
103 | | * Before UTF-8 characters are actually |
104 | | * processed. |
105 | | * Currently this only for MAC OS X. */ |
106 | 4.46M | #define SCONV_TO_UTF8 (1<<8) /* "to charset" side is UTF-8. */ |
107 | 980k | #define SCONV_FROM_UTF8 (1<<9) /* "from charset" side is UTF-8. */ |
108 | 3.92M | #define SCONV_TO_UTF16BE (1<<10) /* "to charset" side is UTF-16BE. */ |
109 | 1.84M | #define SCONV_FROM_UTF16BE (1<<11) /* "from charset" side is UTF-16BE. */ |
110 | 3.92M | #define SCONV_TO_UTF16LE (1<<12) /* "to charset" side is UTF-16LE. */ |
111 | 1.84M | #define SCONV_FROM_UTF16LE (1<<13) /* "from charset" side is UTF-16LE. */ |
112 | 3.86M | #define SCONV_TO_UTF16 (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE) |
113 | 1.72M | #define SCONV_FROM_UTF16 (SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE) |
114 | | |
115 | | #if HAVE_ICONV |
116 | | iconv_t cd; |
117 | | iconv_t cd_w;/* Use at archive_mstring on |
118 | | * Windows. */ |
119 | | #endif |
120 | | /* A temporary buffer for normalization. */ |
121 | | struct archive_string utftmp; |
122 | | int (*converter[2])(struct archive_string *, const void *, size_t, |
123 | | struct archive_string_conv *); |
124 | | int nconverter; |
125 | | }; |
126 | | |
127 | | #define CP_C_LOCALE 0 /* "C" locale only for this file. */ |
128 | | #define CP_UTF16LE 1200 |
129 | | #define CP_UTF16BE 1201 |
130 | | |
131 | 215k | #define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF) |
132 | 46.5k | #define IS_LOW_SURROGATE_LA(uc) ((uc) >= 0xDC00 && (uc) <= 0xDFFF) |
133 | 277k | #define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF) |
134 | 864k | #define UNICODE_MAX 0x10FFFF |
135 | 569k | #define UNICODE_R_CHAR 0xFFFD /* Replacement character. */ |
136 | | /* Set U+FFFD(Replacement character) in UTF-8. */ |
137 | | static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd}; |
138 | | |
139 | | static struct archive_string_conv *find_sconv_object(struct archive *, |
140 | | const char *, const char *); |
141 | | static void add_sconv_object(struct archive *, struct archive_string_conv *); |
142 | | static struct archive_string_conv *create_sconv_object(const char *, |
143 | | const char *, unsigned, int); |
144 | | static void free_sconv_object(struct archive_string_conv *); |
145 | | static struct archive_string_conv *get_sconv_object(struct archive *, |
146 | | const char *, const char *, int); |
147 | | static unsigned make_codepage_from_charset(const char *); |
148 | | static unsigned get_current_codepage(void); |
149 | | static unsigned get_current_oemcp(void); |
150 | | static size_t mbsnbytes(const void *, size_t); |
151 | | static size_t utf16nbytes(const void *, size_t); |
152 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
153 | | static int archive_wstring_append_from_mbs_in_codepage( |
154 | | struct archive_wstring *, const char *, size_t, |
155 | | struct archive_string_conv *); |
156 | | static int archive_string_append_from_wcs_in_codepage(struct archive_string *, |
157 | | const wchar_t *, size_t, struct archive_string_conv *); |
158 | | static int strncat_in_codepage(struct archive_string *, const void *, |
159 | | size_t, struct archive_string_conv *); |
160 | | static int win_strncat_from_utf16be(struct archive_string *, const void *, |
161 | | size_t, struct archive_string_conv *); |
162 | | static int win_strncat_from_utf16le(struct archive_string *, const void *, |
163 | | size_t, struct archive_string_conv *); |
164 | | static int win_strncat_to_utf16be(struct archive_string *, const void *, |
165 | | size_t, struct archive_string_conv *); |
166 | | static int win_strncat_to_utf16le(struct archive_string *, const void *, |
167 | | size_t, struct archive_string_conv *); |
168 | | #endif |
169 | | static int best_effort_strncat_from_utf16be(struct archive_string *, |
170 | | const void *, size_t, struct archive_string_conv *); |
171 | | static int best_effort_strncat_from_utf16le(struct archive_string *, |
172 | | const void *, size_t, struct archive_string_conv *); |
173 | | static int best_effort_strncat_to_utf16be(struct archive_string *, |
174 | | const void *, size_t, struct archive_string_conv *); |
175 | | static int best_effort_strncat_to_utf16le(struct archive_string *, |
176 | | const void *, size_t, struct archive_string_conv *); |
177 | | #if defined(HAVE_ICONV) |
178 | | static int iconv_strncat_in_locale(struct archive_string *, const void *, |
179 | | size_t, struct archive_string_conv *); |
180 | | #endif |
181 | | static int best_effort_strncat_in_locale(struct archive_string *, |
182 | | const void *, size_t, struct archive_string_conv *); |
183 | | static int _utf8_to_unicode(uint32_t *, const char *, size_t); |
184 | | static int utf8_to_unicode(uint32_t *, const char *, size_t); |
185 | | static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t); |
186 | | static int cesu8_to_unicode(uint32_t *, const char *, size_t); |
187 | | static size_t unicode_to_utf8(char *, size_t, uint32_t); |
188 | | static int utf16_to_unicode(uint32_t *, const char *, size_t, int); |
189 | | static size_t unicode_to_utf16be(char *, size_t, uint32_t); |
190 | | static size_t unicode_to_utf16le(char *, size_t, uint32_t); |
191 | | static int strncat_from_utf8_libarchive2(struct archive_string *, |
192 | | const void *, size_t, struct archive_string_conv *); |
193 | | static int strncat_from_utf8_to_utf8(struct archive_string *, const void *, |
194 | | size_t, struct archive_string_conv *); |
195 | | static int archive_string_normalize_C(struct archive_string *, const void *, |
196 | | size_t, struct archive_string_conv *); |
197 | | static int archive_string_normalize_D(struct archive_string *, const void *, |
198 | | size_t, struct archive_string_conv *); |
199 | | static int archive_string_append_unicode(struct archive_string *, |
200 | | const void *, size_t, struct archive_string_conv *); |
201 | | |
202 | | #if defined __LITTLE_ENDIAN__ |
203 | | #define IS_BIG_ENDIAN 0 |
204 | | #elif defined __BIG_ENDIAN__ |
205 | | #define IS_BIG_ENDIAN 1 |
206 | | #elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) |
207 | | #define IS_BIG_ENDIAN 0 |
208 | | #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
209 | | #define IS_BIG_ENDIAN 1 |
210 | | #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64)) |
211 | | #define IS_BIG_ENDIAN 0 |
212 | | #else |
213 | | // Detect endianness at runtime. |
214 | | static int |
215 | | is_big_endian(void) |
216 | | { |
217 | | uint16_t d = 1; |
218 | | |
219 | | return (archive_be16dec(&d) == 1); |
220 | | } |
221 | | |
222 | | #define IS_BIG_ENDIAN is_big_endian() |
223 | | #endif |
224 | | |
225 | | static struct archive_string * |
226 | | archive_string_append(struct archive_string *as, const char *p, size_t s) |
227 | 35.9M | { |
228 | 35.9M | if (archive_string_ensure(as, as->length + s + 1) == NULL) |
229 | 0 | return (NULL); |
230 | 35.9M | if (s) |
231 | 35.9M | memmove(as->s + as->length, p, s); |
232 | 35.9M | as->length += s; |
233 | 35.9M | as->s[as->length] = 0; |
234 | 35.9M | return (as); |
235 | 35.9M | } |
236 | | |
237 | | static struct archive_wstring * |
238 | | archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) |
239 | 23.3k | { |
240 | 23.3k | if (archive_wstring_ensure(as, as->length + s + 1) == NULL) |
241 | 0 | return (NULL); |
242 | 23.3k | if (s) |
243 | 15.0k | wmemmove(as->s + as->length, p, s); |
244 | 23.3k | as->length += s; |
245 | 23.3k | as->s[as->length] = 0; |
246 | 23.3k | return (as); |
247 | 23.3k | } |
248 | | |
249 | | struct archive_string * |
250 | | archive_array_append(struct archive_string *as, const char *p, size_t s) |
251 | 752 | { |
252 | 752 | return archive_string_append(as, p, s); |
253 | 752 | } |
254 | | |
255 | | void |
256 | | archive_string_concat(struct archive_string *dest, struct archive_string *src) |
257 | 794 | { |
258 | 794 | if (archive_string_append(dest, src->s, src->length) == NULL) |
259 | 0 | __archive_errx(1, "Out of memory"); |
260 | 794 | } |
261 | | |
262 | | void |
263 | | archive_wstring_concat(struct archive_wstring *dest, |
264 | | struct archive_wstring *src) |
265 | 13.0k | { |
266 | 13.0k | if (archive_wstring_append(dest, src->s, src->length) == NULL) |
267 | 0 | __archive_errx(1, "Out of memory"); |
268 | 13.0k | } |
269 | | |
270 | | void |
271 | | archive_string_free(struct archive_string *as) |
272 | 20.7M | { |
273 | 20.7M | as->length = 0; |
274 | 20.7M | as->buffer_length = 0; |
275 | 20.7M | free(as->s); |
276 | 20.7M | as->s = NULL; |
277 | 20.7M | } |
278 | | |
279 | | void |
280 | | archive_wstring_free(struct archive_wstring *as) |
281 | 6.48M | { |
282 | 6.48M | as->length = 0; |
283 | 6.48M | as->buffer_length = 0; |
284 | 6.48M | free(as->s); |
285 | 6.48M | as->s = NULL; |
286 | 6.48M | } |
287 | | |
288 | | struct archive_wstring * |
289 | | archive_wstring_ensure(struct archive_wstring *as, size_t s) |
290 | 468k | { |
291 | 468k | return (struct archive_wstring *) |
292 | 468k | archive_string_ensure((struct archive_string *)as, |
293 | 468k | s * sizeof(wchar_t)); |
294 | 468k | } |
295 | | |
296 | | /* Returns NULL on any allocation failure. */ |
297 | | struct archive_string * |
298 | | archive_string_ensure(struct archive_string *as, size_t s) |
299 | 39.4M | { |
300 | 39.4M | char *p; |
301 | 39.4M | size_t new_length; |
302 | | |
303 | | /* If buffer is already big enough, don't reallocate. */ |
304 | 39.4M | if (as->s && (s <= as->buffer_length)) |
305 | 37.6M | return (as); |
306 | | |
307 | | /* |
308 | | * Growing the buffer at least exponentially ensures that |
309 | | * append operations are always linear in the number of |
310 | | * characters appended. Using a smaller growth rate for |
311 | | * larger buffers reduces memory waste somewhat at the cost of |
312 | | * a larger constant factor. |
313 | | */ |
314 | 1.71M | if (as->buffer_length < 32) |
315 | | /* Start with a minimum 32-character buffer. */ |
316 | 1.62M | new_length = 32; |
317 | 86.7k | else if (as->buffer_length < 8192) |
318 | | /* Buffers under 8k are doubled for speed. */ |
319 | 82.5k | new_length = as->buffer_length + as->buffer_length; |
320 | 4.16k | else { |
321 | | /* Buffers 8k and over grow by at least 25% each time. */ |
322 | 4.16k | if (archive_ckd_add_size(&new_length, |
323 | 4.16k | as->buffer_length, as->buffer_length / 4)) { |
324 | | /* On failure, wipe the string and return NULL. */ |
325 | 0 | archive_string_free(as); |
326 | 0 | errno = ENOMEM;/* Make sure errno has ENOMEM. */ |
327 | 0 | return (NULL); |
328 | 0 | } |
329 | 4.16k | } |
330 | | /* |
331 | | * The computation above is a lower limit to how much we'll |
332 | | * grow the buffer. In any case, we have to grow it enough to |
333 | | * hold the request. |
334 | | */ |
335 | 1.71M | if (new_length < s) |
336 | 950k | new_length = s; |
337 | | /* Now we can reallocate the buffer. */ |
338 | 1.71M | p = realloc(as->s, new_length); |
339 | 1.71M | if (p == NULL) { |
340 | | /* On failure, wipe the string and return NULL. */ |
341 | 0 | archive_string_free(as); |
342 | 0 | errno = ENOMEM;/* Make sure errno has ENOMEM. */ |
343 | 0 | return (NULL); |
344 | 0 | } |
345 | | |
346 | 1.71M | as->s = p; |
347 | 1.71M | as->buffer_length = new_length; |
348 | 1.71M | return (as); |
349 | 1.71M | } |
350 | | |
351 | | /* |
352 | | * TODO: See if there's a way to avoid scanning |
353 | | * the source string twice. Then test to see |
354 | | * if it actually helps (remember that we're almost |
355 | | * always called with pretty short arguments, so |
356 | | * such an optimization might not help). |
357 | | */ |
358 | | struct archive_string * |
359 | | archive_strncat(struct archive_string *as, const void *_p, size_t n) |
360 | 516k | { |
361 | 516k | size_t s; |
362 | 516k | const char *p, *pp; |
363 | | |
364 | 516k | p = (const char *)_p; |
365 | | |
366 | | /* Like strlen(p), except won't examine positions beyond p[n]. */ |
367 | 516k | s = 0; |
368 | 516k | pp = p; |
369 | 191M | while (s < n && *pp) { |
370 | 190M | pp++; |
371 | 190M | s++; |
372 | 190M | } |
373 | 516k | if ((as = archive_string_append(as, p, s)) == NULL) |
374 | 0 | __archive_errx(1, "Out of memory"); |
375 | 516k | return (as); |
376 | 516k | } |
377 | | |
378 | | struct archive_wstring * |
379 | | archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n) |
380 | 10.2k | { |
381 | 10.2k | size_t s; |
382 | 10.2k | const wchar_t *pp; |
383 | | |
384 | | /* Like strlen(p), except won't examine positions beyond p[n]. */ |
385 | 10.2k | s = 0; |
386 | 10.2k | pp = p; |
387 | 120k | while (s < n && *pp) { |
388 | 110k | pp++; |
389 | 110k | s++; |
390 | 110k | } |
391 | 10.2k | if ((as = archive_wstring_append(as, p, s)) == NULL) |
392 | 0 | __archive_errx(1, "Out of memory"); |
393 | 10.2k | return (as); |
394 | 10.2k | } |
395 | | |
396 | | struct archive_string * |
397 | | archive_strcat(struct archive_string *as, const void *p) |
398 | 234k | { |
399 | | /* strcat is just strncat without an effective limit. |
400 | | * Assert that we'll never get called with a source |
401 | | * string over 16MB. |
402 | | * TODO: Review all uses of strcat in the source |
403 | | * and try to replace them with strncat(). |
404 | | */ |
405 | 234k | return archive_strncat(as, p, 0x1000000); |
406 | 234k | } |
407 | | |
408 | | struct archive_wstring * |
409 | | archive_wstrcat(struct archive_wstring *as, const wchar_t *p) |
410 | 94 | { |
411 | | /* Ditto. */ |
412 | 94 | return archive_wstrncat(as, p, 0x1000000); |
413 | 94 | } |
414 | | |
415 | | struct archive_string * |
416 | | archive_strappend_char(struct archive_string *as, char c) |
417 | 35.1M | { |
418 | 35.1M | if ((as = archive_string_append(as, &c, 1)) == NULL) |
419 | 0 | __archive_errx(1, "Out of memory"); |
420 | 35.1M | return (as); |
421 | 35.1M | } |
422 | | |
423 | | struct archive_wstring * |
424 | | archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c) |
425 | 94 | { |
426 | 94 | if ((as = archive_wstring_append(as, &c, 1)) == NULL) |
427 | 0 | __archive_errx(1, "Out of memory"); |
428 | 94 | return (as); |
429 | 94 | } |
430 | | |
431 | | /* |
432 | | * Get the "current character set" name to use with iconv. |
433 | | * On FreeBSD, the empty character set name "" chooses |
434 | | * the correct character encoding for the current locale, |
435 | | * so this isn't necessary. |
436 | | * But iconv on Mac OS 10.6 doesn't seem to handle this correctly; |
437 | | * on that system, we have to explicitly call nl_langinfo() |
438 | | * to get the right name. Not sure about other platforms. |
439 | | * |
440 | | * NOTE: GNU libiconv does not recognize the character-set name |
441 | | * which some platform nl_langinfo(CODESET) returns, so we should |
442 | | * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv. |
443 | | */ |
444 | | static const char * |
445 | 463k | default_iconv_charset(const char *charset) { |
446 | 463k | if (charset != NULL && charset[0] != '\0') |
447 | 2.43k | return charset; |
448 | | #if HAVE_LOCALE_CHARSET && HAVE_LOCALCHARSET_H && !defined(__APPLE__) |
449 | | /* locale_charset() is broken on Mac OS */ |
450 | | return locale_charset(); |
451 | | #elif HAVE_NL_LANGINFO |
452 | 460k | return nl_langinfo(CODESET); |
453 | | #elif defined(__BIONIC__) |
454 | | return "UTF-8"; |
455 | | #else |
456 | | return ""; |
457 | | #endif |
458 | 463k | } |
459 | | |
460 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
461 | | |
462 | | /* |
463 | | * Convert MBS to WCS. |
464 | | * Note: returns -1 if conversion fails. |
465 | | */ |
466 | | int |
467 | | archive_wstring_append_from_mbs(struct archive_wstring *dest, |
468 | | const char *p, size_t len) |
469 | | { |
470 | | return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL); |
471 | | } |
472 | | |
473 | | static int |
474 | | archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, |
475 | | const char *s, size_t length, struct archive_string_conv *sc) |
476 | | { |
477 | | int ret = 0; |
478 | | size_t count; |
479 | | UINT from_cp; |
480 | | |
481 | | if (sc != NULL) |
482 | | from_cp = sc->from_cp; |
483 | | else |
484 | | from_cp = get_current_codepage(); |
485 | | |
486 | | if (from_cp == CP_C_LOCALE) { |
487 | | /* |
488 | | * "C" locale special processing. |
489 | | */ |
490 | | wchar_t *ws; |
491 | | const unsigned char *mp; |
492 | | |
493 | | if (NULL == archive_wstring_ensure(dest, |
494 | | dest->length + length + 1)) |
495 | | return (-1); |
496 | | |
497 | | ws = dest->s + dest->length; |
498 | | mp = (const unsigned char *)s; |
499 | | count = 0; |
500 | | while (count < length && *mp) { |
501 | | *ws++ = (wchar_t)*mp++; |
502 | | count++; |
503 | | } |
504 | | } else if (sc != NULL && |
505 | | (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) { |
506 | | /* |
507 | | * Normalize UTF-8 and UTF-16BE and convert it directly |
508 | | * to UTF-16 as wchar_t. |
509 | | */ |
510 | | struct archive_string u16; |
511 | | int saved_flag = sc->flag;/* save current flag. */ |
512 | | |
513 | | if (IS_BIG_ENDIAN) |
514 | | sc->flag |= SCONV_TO_UTF16BE; |
515 | | else |
516 | | sc->flag |= SCONV_TO_UTF16LE; |
517 | | |
518 | | if (sc->flag & SCONV_FROM_UTF16) { |
519 | | /* |
520 | | * UTF-16BE/LE NFD ===> UTF-16 NFC |
521 | | * UTF-16BE/LE NFC ===> UTF-16 NFD |
522 | | */ |
523 | | count = utf16nbytes(s, length); |
524 | | } else { |
525 | | /* |
526 | | * UTF-8 NFD ===> UTF-16 NFC |
527 | | * UTF-8 NFC ===> UTF-16 NFD |
528 | | */ |
529 | | count = mbsnbytes(s, length); |
530 | | } |
531 | | u16.s = (char *)dest->s; |
532 | | u16.length = dest->length << 1; |
533 | | u16.buffer_length = dest->buffer_length; |
534 | | if (sc->flag & SCONV_NORMALIZATION_C) |
535 | | ret = archive_string_normalize_C(&u16, s, count, sc); |
536 | | else |
537 | | ret = archive_string_normalize_D(&u16, s, count, sc); |
538 | | dest->s = (wchar_t *)u16.s; |
539 | | dest->length = u16.length >> 1; |
540 | | dest->buffer_length = u16.buffer_length; |
541 | | sc->flag = saved_flag;/* restore the saved flag. */ |
542 | | return (ret); |
543 | | } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) { |
544 | | count = utf16nbytes(s, length); |
545 | | count >>= 1; /* to be WCS length */ |
546 | | /* Allocate memory for WCS. */ |
547 | | if (NULL == archive_wstring_ensure(dest, |
548 | | dest->length + count + 1)) |
549 | | return (-1); |
550 | | wmemcpy(dest->s + dest->length, (const wchar_t *)s, count); |
551 | | if ((sc->flag & SCONV_FROM_UTF16BE) && !IS_BIG_ENDIAN) { |
552 | | uint16_t *u16 = (uint16_t *)(dest->s + dest->length); |
553 | | size_t b; |
554 | | for (b = 0; b < count; b++) { |
555 | | uint16_t val = archive_le16dec(u16+b); |
556 | | archive_be16enc(u16+b, val); |
557 | | } |
558 | | } else if ((sc->flag & SCONV_FROM_UTF16LE) && IS_BIG_ENDIAN) { |
559 | | uint16_t *u16 = (uint16_t *)(dest->s + dest->length); |
560 | | size_t b; |
561 | | for (b = 0; b < count; b++) { |
562 | | uint16_t val = archive_be16dec(u16+b); |
563 | | archive_le16enc(u16+b, val); |
564 | | } |
565 | | } |
566 | | } else { |
567 | | DWORD mbflag; |
568 | | size_t buffsize; |
569 | | |
570 | | if (sc == NULL) |
571 | | mbflag = 0; |
572 | | else if (sc->flag & SCONV_FROM_CHARSET) { |
573 | | /* Do not trust the length which comes from |
574 | | * an archive file. */ |
575 | | length = mbsnbytes(s, length); |
576 | | mbflag = 0; |
577 | | } else |
578 | | mbflag = MB_PRECOMPOSED; |
579 | | |
580 | | mbflag |= MB_ERR_INVALID_CHARS; |
581 | | |
582 | | buffsize = dest->length + length + 1; |
583 | | do { |
584 | | int r; |
585 | | |
586 | | /* MultiByteToWideChar is limited to int. */ |
587 | | if (length > (size_t)INT_MAX || |
588 | | (dest->buffer_length >> 1) > (size_t)INT_MAX) |
589 | | return (-1); |
590 | | /* Allocate memory for WCS. */ |
591 | | if (NULL == archive_wstring_ensure(dest, buffsize)) |
592 | | return (-1); |
593 | | /* Convert MBS to WCS. */ |
594 | | r = MultiByteToWideChar(from_cp, |
595 | | mbflag, s, (int)length, dest->s + dest->length, |
596 | | (int)(dest->buffer_length >> 1) -1); |
597 | | if (r == 0 && |
598 | | GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
599 | | /* Expand the WCS buffer. */ |
600 | | buffsize = dest->buffer_length << 1; |
601 | | continue; |
602 | | } |
603 | | if (r == 0 && length != 0) |
604 | | ret = -1; |
605 | | count = (size_t)r; |
606 | | break; |
607 | | } while (1); |
608 | | } |
609 | | dest->length += count; |
610 | | dest->s[dest->length] = L'\0'; |
611 | | return (ret); |
612 | | } |
613 | | |
614 | | #else |
615 | | |
616 | | /* |
617 | | * Convert MBS to WCS. |
618 | | * Note: returns -1 if conversion fails. |
619 | | */ |
620 | | int |
621 | | archive_wstring_append_from_mbs(struct archive_wstring *dest, |
622 | | const char *p, size_t len) |
623 | 428k | { |
624 | 428k | size_t r; |
625 | 428k | int ret_val = 0; |
626 | | /* |
627 | | * No single byte will be more than one wide character, |
628 | | * so this length estimate will always be big enough. |
629 | | */ |
630 | | // size_t wcs_length = len; |
631 | 428k | size_t mbs_length = len; |
632 | 428k | const char *mbs = p; |
633 | 428k | wchar_t *wcs; |
634 | 428k | #if HAVE_MBRTOWC |
635 | 428k | mbstate_t shift_state; |
636 | | |
637 | 428k | memset(&shift_state, 0, sizeof(shift_state)); |
638 | 428k | #endif |
639 | | /* |
640 | | * As we decided to have wcs_length == mbs_length == len |
641 | | * we can use len here instead of wcs_length |
642 | | */ |
643 | 428k | if (NULL == archive_wstring_ensure(dest, dest->length + len + 1)) |
644 | 0 | return (-1); |
645 | 428k | wcs = dest->s + dest->length; |
646 | | /* |
647 | | * We cannot use mbsrtowcs/mbstowcs here because those may convert |
648 | | * extra MBS when strlen(p) > len and one wide character consists of |
649 | | * multi bytes. |
650 | | */ |
651 | 25.6M | while (*mbs && mbs_length > 0) { |
652 | | /* |
653 | | * The buffer we allocated is always big enough. |
654 | | * Keep this code path in a comment if we decide to choose |
655 | | * smaller wcs_length in the future |
656 | | */ |
657 | | /* |
658 | | if (wcs_length == 0) { |
659 | | dest->length = wcs - dest->s; |
660 | | dest->s[dest->length] = L'\0'; |
661 | | wcs_length = mbs_length; |
662 | | if (NULL == archive_wstring_ensure(dest, |
663 | | dest->length + wcs_length + 1)) |
664 | | return (-1); |
665 | | wcs = dest->s + dest->length; |
666 | | } |
667 | | */ |
668 | 25.2M | #if HAVE_MBRTOWC |
669 | 25.2M | r = mbrtowc(wcs, mbs, mbs_length, &shift_state); |
670 | | #else |
671 | | r = mbtowc(wcs, mbs, mbs_length); |
672 | | #endif |
673 | 25.2M | if (r == (size_t)-1 || r == (size_t)-2) { |
674 | 37.7k | ret_val = -1; |
675 | 37.7k | break; |
676 | 37.7k | } |
677 | 25.2M | if (r == 0 || r > mbs_length) |
678 | 0 | break; |
679 | 25.2M | wcs++; |
680 | | // wcs_length--; |
681 | 25.2M | mbs += r; |
682 | 25.2M | mbs_length -= r; |
683 | 25.2M | } |
684 | 428k | dest->length = wcs - dest->s; |
685 | 428k | dest->s[dest->length] = L'\0'; |
686 | 428k | return (ret_val); |
687 | 428k | } |
688 | | |
689 | | #endif |
690 | | |
691 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
692 | | |
693 | | /* |
694 | | * WCS ==> MBS. |
695 | | * Note: returns -1 if conversion fails. |
696 | | * |
697 | | * Win32 builds use WideCharToMultiByte from the Windows API. |
698 | | * (Maybe Cygwin should too? WideCharToMultiByte will know a |
699 | | * lot more about local character encodings than the wcrtomb() |
700 | | * wrapper is going to know.) |
701 | | */ |
702 | | int |
703 | | archive_string_append_from_wcs(struct archive_string *as, |
704 | | const wchar_t *w, size_t len) |
705 | | { |
706 | | return archive_string_append_from_wcs_in_codepage(as, w, len, NULL); |
707 | | } |
708 | | |
709 | | static int |
710 | | archive_string_append_from_wcs_in_codepage(struct archive_string *as, |
711 | | const wchar_t *ws, size_t len, struct archive_string_conv *sc) |
712 | | { |
713 | | BOOL defchar_used, *dp; |
714 | | int ret = 0; |
715 | | UINT to_cp; |
716 | | size_t count, wslen = len; |
717 | | |
718 | | if (sc != NULL) |
719 | | to_cp = sc->to_cp; |
720 | | else |
721 | | to_cp = get_current_codepage(); |
722 | | |
723 | | if (to_cp == CP_C_LOCALE) { |
724 | | /* |
725 | | * "C" locale special processing. |
726 | | */ |
727 | | const wchar_t *wp = ws; |
728 | | char *p; |
729 | | |
730 | | if (NULL == archive_string_ensure(as, |
731 | | as->length + wslen +1)) |
732 | | return (-1); |
733 | | p = as->s + as->length; |
734 | | count = 0; |
735 | | defchar_used = 0; |
736 | | while (count < wslen && *wp) { |
737 | | if (*wp > 255) { |
738 | | *p++ = '?'; |
739 | | wp++; |
740 | | defchar_used = 1; |
741 | | } else |
742 | | *p++ = (char)*wp++; |
743 | | count++; |
744 | | } |
745 | | } else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) { |
746 | | uint16_t *u16; |
747 | | |
748 | | if (NULL == |
749 | | archive_string_ensure(as, as->length + len * 2 + 2)) |
750 | | return (-1); |
751 | | u16 = (uint16_t *)(as->s + as->length); |
752 | | count = 0; |
753 | | defchar_used = 0; |
754 | | if (sc->flag & SCONV_TO_UTF16BE) { |
755 | | while (count < len && *ws) { |
756 | | archive_be16enc(u16+count, *ws); |
757 | | ws++; |
758 | | count++; |
759 | | } |
760 | | } else { |
761 | | while (count < len && *ws) { |
762 | | archive_le16enc(u16+count, *ws); |
763 | | ws++; |
764 | | count++; |
765 | | } |
766 | | } |
767 | | count <<= 1; /* to be byte size */ |
768 | | } else { |
769 | | /* Make sure the MBS buffer has plenty to set. */ |
770 | | if (NULL == |
771 | | archive_string_ensure(as, as->length + len * 2 + 1)) |
772 | | return (-1); |
773 | | do { |
774 | | int r; |
775 | | |
776 | | defchar_used = 0; |
777 | | if (to_cp == CP_UTF8) |
778 | | dp = NULL; |
779 | | else |
780 | | dp = &defchar_used; |
781 | | /* WideCharToMultiByte is limited to int. */ |
782 | | if (as->buffer_length - as->length - 1 > (size_t)INT_MAX || |
783 | | wslen > (size_t)INT_MAX) |
784 | | return (-1); |
785 | | r = WideCharToMultiByte(to_cp, 0, ws, (int)wslen, |
786 | | as->s + as->length, |
787 | | (int)(as->buffer_length - as->length - 1), NULL, dp); |
788 | | if (r == 0 && wslen == 0) { |
789 | | count = 0; |
790 | | break; |
791 | | } |
792 | | if (r == 0 && |
793 | | GetLastError() == ERROR_INSUFFICIENT_BUFFER) { |
794 | | /* Expand the MBS buffer and retry. */ |
795 | | if (NULL == archive_string_ensure(as, |
796 | | as->buffer_length + len)) |
797 | | return (-1); |
798 | | continue; |
799 | | } |
800 | | if (r == 0) |
801 | | ret = -1; |
802 | | count = (size_t)r; |
803 | | break; |
804 | | } while (1); |
805 | | } |
806 | | as->length += count; |
807 | | as->s[as->length] = '\0'; |
808 | | return (defchar_used?-1:ret); |
809 | | } |
810 | | |
811 | | #elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB) |
812 | | |
813 | | /* |
814 | | * Translates a wide character string into current locale character set |
815 | | * and appends to the archive_string. Note: returns -1 if conversion |
816 | | * fails. |
817 | | */ |
818 | | int |
819 | | archive_string_append_from_wcs(struct archive_string *as, |
820 | | const wchar_t *w, size_t len) |
821 | 7.82k | { |
822 | | /* We cannot use the standard wcstombs() here because it |
823 | | * cannot tell us how big the output buffer should be. So |
824 | | * I've built a loop around wcrtomb() or wctomb() that |
825 | | * converts a character at a time and resizes the string as |
826 | | * needed. We prefer wcrtomb() when it's available because |
827 | | * it's thread-safe. */ |
828 | 7.82k | int n, ret_val = 0; |
829 | 7.82k | char *p; |
830 | 7.82k | char *end; |
831 | 7.82k | #if HAVE_WCRTOMB |
832 | 7.82k | mbstate_t shift_state; |
833 | | |
834 | 7.82k | memset(&shift_state, 0, sizeof(shift_state)); |
835 | | #else |
836 | | /* Clear the shift state before starting. */ |
837 | | wctomb(NULL, L'\0'); |
838 | | #endif |
839 | | /* |
840 | | * Allocate buffer for MBS. |
841 | | * We need this allocation here since it is possible that |
842 | | * as->s is still NULL. |
843 | | */ |
844 | 7.82k | if (archive_string_ensure(as, as->length + len + 1) == NULL) |
845 | 0 | return (-1); |
846 | | |
847 | 7.82k | p = as->s + as->length; |
848 | 7.82k | end = as->s + as->buffer_length - MB_CUR_MAX -1; |
849 | 73.4k | while (*w != L'\0' && len > 0) { |
850 | 65.6k | if (p >= end) { |
851 | 1.01k | as->length = p - as->s; |
852 | 1.01k | as->s[as->length] = '\0'; |
853 | | /* Re-allocate buffer for MBS. */ |
854 | 1.01k | if (archive_string_ensure(as, |
855 | 1.01k | as->length + max(len * 2, |
856 | 1.01k | (size_t)MB_CUR_MAX) + 1) == NULL) |
857 | 0 | return (-1); |
858 | 1.01k | p = as->s + as->length; |
859 | 1.01k | end = as->s + as->buffer_length - MB_CUR_MAX -1; |
860 | 1.01k | } |
861 | 65.6k | #if HAVE_WCRTOMB |
862 | 65.6k | n = wcrtomb(p, *w++, &shift_state); |
863 | | #else |
864 | | n = wctomb(p, *w++); |
865 | | #endif |
866 | 65.6k | if (n == -1) { |
867 | 0 | if (errno == EILSEQ) { |
868 | | /* Skip an illegal wide char. */ |
869 | 0 | *p++ = '?'; |
870 | 0 | ret_val = -1; |
871 | 0 | } else { |
872 | 0 | ret_val = -1; |
873 | 0 | break; |
874 | 0 | } |
875 | 0 | } else |
876 | 65.6k | p += n; |
877 | 65.6k | len--; |
878 | 65.6k | } |
879 | 7.82k | as->length = p - as->s; |
880 | 7.82k | as->s[as->length] = '\0'; |
881 | 7.82k | return (ret_val); |
882 | 7.82k | } |
883 | | |
884 | | #else /* HAVE_WCTOMB || HAVE_WCRTOMB */ |
885 | | |
886 | | /* |
887 | | * TODO: Test if __STDC_ISO_10646__ is defined. |
888 | | * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion |
889 | | * one character at a time. If a non-Windows platform doesn't have |
890 | | * either of these, fall back to the built-in UTF8 conversion. |
891 | | */ |
892 | | int |
893 | | archive_string_append_from_wcs(struct archive_string *as, |
894 | | const wchar_t *w, size_t len) |
895 | | { |
896 | | (void)as;/* UNUSED */ |
897 | | (void)w;/* UNUSED */ |
898 | | (void)len;/* UNUSED */ |
899 | | errno = ENOSYS; |
900 | | return (-1); |
901 | | } |
902 | | |
903 | | #endif /* HAVE_WCTOMB || HAVE_WCRTOMB */ |
904 | | |
905 | | /* |
906 | | * Find a string conversion object by a pair of 'from' charset name |
907 | | * and 'to' charset name from an archive object. |
908 | | * Return NULL if not found. |
909 | | */ |
910 | | static struct archive_string_conv * |
911 | | find_sconv_object(struct archive *a, const char *fc, const char *tc) |
912 | 463k | { |
913 | 463k | struct archive_string_conv *sc; |
914 | | |
915 | 463k | if (a == NULL) |
916 | 457k | return (NULL); |
917 | | |
918 | 14.0k | for (sc = a->sconv; sc != NULL; sc = sc->next) { |
919 | 9.86k | if (strcmp(sc->from_charset, fc) == 0 && |
920 | 2.12k | strcmp(sc->to_charset, tc) == 0) |
921 | 2.12k | break; |
922 | 9.86k | } |
923 | 6.28k | return (sc); |
924 | 463k | } |
925 | | |
926 | | /* |
927 | | * Register a string object to an archive object. |
928 | | */ |
929 | | static void |
930 | | add_sconv_object(struct archive *a, struct archive_string_conv *sc) |
931 | 4.15k | { |
932 | 4.15k | struct archive_string_conv **psc; |
933 | | |
934 | | /* Add a new sconv to sconv list. */ |
935 | 4.15k | psc = &(a->sconv); |
936 | 6.25k | while (*psc != NULL) |
937 | 2.09k | psc = &((*psc)->next); |
938 | 4.15k | *psc = sc; |
939 | 4.15k | } |
940 | | |
941 | | static void |
942 | | add_converter(struct archive_string_conv *sc, int (*converter) |
943 | | (struct archive_string *, const void *, size_t, |
944 | | struct archive_string_conv *)) |
945 | 520k | { |
946 | 520k | if (sc == NULL || sc->nconverter >= 2) |
947 | 0 | __archive_errx(1, "Programming error"); |
948 | 520k | sc->converter[sc->nconverter++] = converter; |
949 | 520k | } |
950 | | |
951 | | static void |
952 | | setup_converter(struct archive_string_conv *sc) |
953 | 461k | { |
954 | | |
955 | | /* Reset. */ |
956 | 461k | sc->nconverter = 0; |
957 | | |
958 | | /* |
959 | | * Perform special sequence for the incorrect UTF-8 filenames |
960 | | * made by libarchive2.x. |
961 | | */ |
962 | 461k | if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) { |
963 | 0 | add_converter(sc, strncat_from_utf8_libarchive2); |
964 | 0 | return; |
965 | 0 | } |
966 | | |
967 | | /* |
968 | | * Convert a string to UTF-16BE/LE. |
969 | | */ |
970 | 461k | if (sc->flag & SCONV_TO_UTF16) { |
971 | | /* |
972 | | * If the current locale is UTF-8, we can translate |
973 | | * a UTF-8 string into a UTF-16BE string. |
974 | | */ |
975 | 0 | if (sc->flag & SCONV_FROM_UTF8) { |
976 | 0 | add_converter(sc, archive_string_append_unicode); |
977 | 0 | return; |
978 | 0 | } |
979 | | |
980 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
981 | | if (sc->flag & SCONV_WIN_CP) { |
982 | | if (sc->flag & SCONV_TO_UTF16BE) |
983 | | add_converter(sc, win_strncat_to_utf16be); |
984 | | else |
985 | | add_converter(sc, win_strncat_to_utf16le); |
986 | | return; |
987 | | } |
988 | | #endif |
989 | | |
990 | 0 | #if defined(HAVE_ICONV) |
991 | 0 | if (sc->cd != (iconv_t)-1) { |
992 | 0 | add_converter(sc, iconv_strncat_in_locale); |
993 | 0 | return; |
994 | 0 | } |
995 | 0 | #endif |
996 | | |
997 | 0 | if (sc->flag & SCONV_BEST_EFFORT) { |
998 | 0 | if (sc->flag & SCONV_TO_UTF16BE) |
999 | 0 | add_converter(sc, |
1000 | 0 | best_effort_strncat_to_utf16be); |
1001 | 0 | else |
1002 | 0 | add_converter(sc, |
1003 | 0 | best_effort_strncat_to_utf16le); |
1004 | 0 | } else |
1005 | | /* Make sure we have no converter. */ |
1006 | 0 | sc->nconverter = 0; |
1007 | 0 | return; |
1008 | 0 | } |
1009 | | |
1010 | | /* |
1011 | | * Convert a string from UTF-16BE/LE. |
1012 | | */ |
1013 | 461k | if (sc->flag & SCONV_FROM_UTF16) { |
1014 | | /* |
1015 | | * At least we should normalize a UTF-16BE string. |
1016 | | */ |
1017 | 792 | if (sc->flag & SCONV_NORMALIZATION_D) |
1018 | 0 | add_converter(sc,archive_string_normalize_D); |
1019 | 792 | else if (sc->flag & SCONV_NORMALIZATION_C) |
1020 | 792 | add_converter(sc, archive_string_normalize_C); |
1021 | | |
1022 | 792 | if (sc->flag & SCONV_TO_UTF8) { |
1023 | | /* |
1024 | | * If the current locale is UTF-8, we can translate |
1025 | | * a UTF-16BE/LE string into a UTF-8 string directly. |
1026 | | */ |
1027 | 0 | if (!(sc->flag & |
1028 | 0 | (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) |
1029 | 0 | add_converter(sc, |
1030 | 0 | archive_string_append_unicode); |
1031 | 0 | return; |
1032 | 0 | } |
1033 | | |
1034 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1035 | | if (sc->flag & SCONV_WIN_CP) { |
1036 | | if (sc->flag & SCONV_FROM_UTF16BE) |
1037 | | add_converter(sc, win_strncat_from_utf16be); |
1038 | | else |
1039 | | add_converter(sc, win_strncat_from_utf16le); |
1040 | | return; |
1041 | | } |
1042 | | #endif |
1043 | | |
1044 | 792 | #if defined(HAVE_ICONV) |
1045 | 792 | if (sc->cd != (iconv_t)-1) { |
1046 | 792 | add_converter(sc, iconv_strncat_in_locale); |
1047 | 792 | return; |
1048 | 792 | } |
1049 | 0 | #endif |
1050 | | |
1051 | 0 | if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) |
1052 | 0 | == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE)) |
1053 | 0 | add_converter(sc, best_effort_strncat_from_utf16be); |
1054 | 0 | else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) |
1055 | 0 | == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE)) |
1056 | 0 | add_converter(sc, best_effort_strncat_from_utf16le); |
1057 | 0 | else |
1058 | | /* Make sure we have no converter. */ |
1059 | 0 | sc->nconverter = 0; |
1060 | 0 | return; |
1061 | 792 | } |
1062 | | |
1063 | 460k | if (sc->flag & SCONV_FROM_UTF8) { |
1064 | | /* |
1065 | | * At least we should normalize a UTF-8 string. |
1066 | | */ |
1067 | 58.7k | if (sc->flag & SCONV_NORMALIZATION_D) |
1068 | 0 | add_converter(sc,archive_string_normalize_D); |
1069 | 58.7k | else if (sc->flag & SCONV_NORMALIZATION_C) |
1070 | 58.7k | add_converter(sc, archive_string_normalize_C); |
1071 | | |
1072 | | /* |
1073 | | * Copy UTF-8 string with a check of CESU-8. |
1074 | | * Apparently, iconv does not check surrogate pairs in UTF-8 |
1075 | | * when both from-charset and to-charset are UTF-8, and then |
1076 | | * we use our UTF-8 copy code. |
1077 | | */ |
1078 | 58.7k | if (sc->flag & SCONV_TO_UTF8) { |
1079 | | /* |
1080 | | * If the current locale is UTF-8, we can translate |
1081 | | * a UTF-16BE string into a UTF-8 string directly. |
1082 | | */ |
1083 | 0 | if (!(sc->flag & |
1084 | 0 | (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C))) |
1085 | 0 | add_converter(sc, strncat_from_utf8_to_utf8); |
1086 | 0 | return; |
1087 | 0 | } |
1088 | 58.7k | } |
1089 | | |
1090 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1091 | | /* |
1092 | | * On Windows we can use Windows API for a string conversion. |
1093 | | */ |
1094 | | if (sc->flag & SCONV_WIN_CP) { |
1095 | | add_converter(sc, strncat_in_codepage); |
1096 | | return; |
1097 | | } |
1098 | | #endif |
1099 | | |
1100 | 460k | #if HAVE_ICONV |
1101 | 460k | if (sc->cd != (iconv_t)-1) { |
1102 | 460k | add_converter(sc, iconv_strncat_in_locale); |
1103 | | /* |
1104 | | * iconv generally does not support UTF-8-MAC and so |
1105 | | * we have to the output of iconv from NFC to NFD if |
1106 | | * need. |
1107 | | */ |
1108 | 460k | if ((sc->flag & SCONV_FROM_CHARSET) && |
1109 | 58.7k | (sc->flag & SCONV_TO_UTF8)) { |
1110 | 0 | if (sc->flag & SCONV_NORMALIZATION_D) |
1111 | 0 | add_converter(sc, archive_string_normalize_D); |
1112 | 0 | } |
1113 | 460k | return; |
1114 | 460k | } |
1115 | 300 | #endif |
1116 | | |
1117 | | /* |
1118 | | * Try conversion in the best effort or no conversion. |
1119 | | */ |
1120 | 300 | if ((sc->flag & SCONV_BEST_EFFORT) || sc->same) |
1121 | 300 | add_converter(sc, best_effort_strncat_in_locale); |
1122 | 0 | else |
1123 | | /* Make sure we have no converter. */ |
1124 | 0 | sc->nconverter = 0; |
1125 | 300 | } |
1126 | | |
1127 | | /* |
1128 | | * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE |
1129 | | * and CP932 which are referenced in create_sconv_object(). |
1130 | | */ |
1131 | | static const char * |
1132 | | canonical_charset_name(const char *charset) |
1133 | 922k | { |
1134 | 922k | char cs[16]; |
1135 | 922k | char *p; |
1136 | 922k | const char *s; |
1137 | | |
1138 | 922k | if (charset == NULL || charset[0] == '\0' |
1139 | 922k | || strlen(charset) > 15) |
1140 | 0 | return (charset); |
1141 | | |
1142 | | /* Copy name to uppercase. */ |
1143 | 922k | p = cs; |
1144 | 922k | s = charset; |
1145 | 9.68M | while (*s) { |
1146 | 8.76M | char c = *s++; |
1147 | 8.76M | if (c >= 'a' && c <= 'z') |
1148 | 0 | c -= 'a' - 'A'; |
1149 | 8.76M | *p++ = c; |
1150 | 8.76M | } |
1151 | 922k | *p++ = '\0'; |
1152 | | |
1153 | 922k | if (strcmp(cs, "UTF-8") == 0 || |
1154 | 462k | strcmp(cs, "UTF8") == 0) |
1155 | 460k | return ("UTF-8"); |
1156 | 462k | if (strcmp(cs, "UTF-16BE") == 0 || |
1157 | 461k | strcmp(cs, "UTF16BE") == 0) |
1158 | 587 | return ("UTF-16BE"); |
1159 | 461k | if (strcmp(cs, "UTF-16LE") == 0 || |
1160 | 461k | strcmp(cs, "UTF16LE") == 0) |
1161 | 205 | return ("UTF-16LE"); |
1162 | 461k | if (strcmp(cs, "CP932") == 0) |
1163 | 0 | return ("CP932"); |
1164 | 461k | return (charset); |
1165 | 461k | } |
1166 | | |
1167 | | /* |
1168 | | * Create a string conversion object. |
1169 | | */ |
1170 | | static struct archive_string_conv * |
1171 | | create_sconv_object(const char *fc, const char *tc, |
1172 | | unsigned current_codepage, int flag) |
1173 | 461k | { |
1174 | 461k | struct archive_string_conv *sc; |
1175 | | |
1176 | 461k | sc = calloc(1, sizeof(*sc)); |
1177 | 461k | if (sc == NULL) |
1178 | 0 | return (NULL); |
1179 | 461k | sc->next = NULL; |
1180 | 461k | sc->from_charset = strdup(fc); |
1181 | 461k | if (sc->from_charset == NULL) { |
1182 | 0 | free(sc); |
1183 | 0 | return (NULL); |
1184 | 0 | } |
1185 | 461k | sc->to_charset = strdup(tc); |
1186 | 461k | if (sc->to_charset == NULL) { |
1187 | 0 | free(sc->from_charset); |
1188 | 0 | free(sc); |
1189 | 0 | return (NULL); |
1190 | 0 | } |
1191 | 461k | archive_string_init(&sc->utftmp); |
1192 | | |
1193 | 461k | if (flag & SCONV_TO_CHARSET) { |
1194 | | /* |
1195 | | * Convert characters from the current locale charset to |
1196 | | * a specified charset. |
1197 | | */ |
1198 | 401k | sc->from_cp = current_codepage; |
1199 | 401k | sc->to_cp = make_codepage_from_charset(tc); |
1200 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1201 | | if (IsValidCodePage(sc->to_cp)) |
1202 | | flag |= SCONV_WIN_CP; |
1203 | | #endif |
1204 | 401k | } else if (flag & SCONV_FROM_CHARSET) { |
1205 | | /* |
1206 | | * Convert characters from a specified charset to |
1207 | | * the current locale charset. |
1208 | | */ |
1209 | 59.7k | sc->to_cp = current_codepage; |
1210 | 59.7k | sc->from_cp = make_codepage_from_charset(fc); |
1211 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1212 | | if (IsValidCodePage(sc->from_cp)) |
1213 | | flag |= SCONV_WIN_CP; |
1214 | | #endif |
1215 | 59.7k | } |
1216 | | |
1217 | | /* |
1218 | | * Check if "from charset" and "to charset" are the same. |
1219 | | */ |
1220 | 461k | if (strcmp(fc, tc) == 0 || |
1221 | 461k | (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp)) |
1222 | 0 | sc->same = 1; |
1223 | 461k | else |
1224 | 461k | sc->same = 0; |
1225 | | |
1226 | | /* |
1227 | | * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE. |
1228 | | */ |
1229 | 461k | if (strcmp(tc, "UTF-8") == 0) |
1230 | 401k | flag |= SCONV_TO_UTF8; |
1231 | 59.7k | else if (strcmp(tc, "UTF-16BE") == 0) |
1232 | 0 | flag |= SCONV_TO_UTF16BE; |
1233 | 59.7k | else if (strcmp(tc, "UTF-16LE") == 0) |
1234 | 0 | flag |= SCONV_TO_UTF16LE; |
1235 | 461k | if (strcmp(fc, "UTF-8") == 0) |
1236 | 58.7k | flag |= SCONV_FROM_UTF8; |
1237 | 402k | else if (strcmp(fc, "UTF-16BE") == 0) |
1238 | 587 | flag |= SCONV_FROM_UTF16BE; |
1239 | 401k | else if (strcmp(fc, "UTF-16LE") == 0) |
1240 | 205 | flag |= SCONV_FROM_UTF16LE; |
1241 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1242 | | if (sc->to_cp == CP_UTF8) |
1243 | | flag |= SCONV_TO_UTF8; |
1244 | | else if (sc->to_cp == CP_UTF16BE) |
1245 | | flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP; |
1246 | | else if (sc->to_cp == CP_UTF16LE) |
1247 | | flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP; |
1248 | | if (sc->from_cp == CP_UTF8) |
1249 | | flag |= SCONV_FROM_UTF8; |
1250 | | else if (sc->from_cp == CP_UTF16BE) |
1251 | | flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP; |
1252 | | else if (sc->from_cp == CP_UTF16LE) |
1253 | | flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP; |
1254 | | #endif |
1255 | | |
1256 | | /* |
1257 | | * Set a flag for Unicode NFD. Usually iconv cannot correctly |
1258 | | * handle it. So we have to translate NFD characters to NFC ones |
1259 | | * ourselves before iconv handles. Another reason is to prevent |
1260 | | * that the same sight of two filenames, one is NFC and other |
1261 | | * is NFD, would be in its directory. |
1262 | | * On Mac OS X, although its filesystem layer automatically |
1263 | | * convert filenames to NFD, it would be useful for filename |
1264 | | * comparing to find out the same filenames that we normalize |
1265 | | * that to be NFD ourselves. |
1266 | | */ |
1267 | 461k | if ((flag & SCONV_FROM_CHARSET) && |
1268 | 59.7k | (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) { |
1269 | | #if defined(__APPLE__) |
1270 | | if (flag & SCONV_TO_UTF8) |
1271 | | flag |= SCONV_NORMALIZATION_D; |
1272 | | else |
1273 | | #endif |
1274 | 59.4k | flag |= SCONV_NORMALIZATION_C; |
1275 | 59.4k | } |
1276 | | #if defined(__APPLE__) |
1277 | | /* |
1278 | | * In case writing an archive file, make sure that a filename |
1279 | | * going to be passed to iconv is a Unicode NFC string since |
1280 | | * a filename in HFS Plus filesystem is a Unicode NFD one and |
1281 | | * iconv cannot handle it with "UTF-8" charset. It is simpler |
1282 | | * than a use of "UTF-8-MAC" charset. |
1283 | | */ |
1284 | | if ((flag & SCONV_TO_CHARSET) && |
1285 | | (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && |
1286 | | !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) |
1287 | | flag |= SCONV_NORMALIZATION_C; |
1288 | | /* |
1289 | | * In case reading an archive file. make sure that a filename |
1290 | | * will be passed to users is a Unicode NFD string in order to |
1291 | | * correctly compare the filename with other one which comes |
1292 | | * from HFS Plus filesystem. |
1293 | | */ |
1294 | | if ((flag & SCONV_FROM_CHARSET) && |
1295 | | !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && |
1296 | | (flag & SCONV_TO_UTF8)) |
1297 | | flag |= SCONV_NORMALIZATION_D; |
1298 | | #endif |
1299 | | |
1300 | 461k | #if defined(HAVE_ICONV) |
1301 | 461k | sc->cd_w = (iconv_t)-1; |
1302 | | /* |
1303 | | * Create an iconv object. |
1304 | | */ |
1305 | 461k | if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) && |
1306 | 401k | (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) || |
1307 | 461k | (flag & SCONV_WIN_CP)) { |
1308 | | /* This case we won't use iconv. */ |
1309 | 0 | sc->cd = (iconv_t)-1; |
1310 | 461k | } else { |
1311 | 461k | sc->cd = iconv_open(tc, fc); |
1312 | 461k | if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) { |
1313 | | /* |
1314 | | * Unfortunately, all of iconv implements do support |
1315 | | * "CP932" character-set, so we should use "SJIS" |
1316 | | * instead if iconv_open failed. |
1317 | | */ |
1318 | 0 | if (strcmp(tc, "CP932") == 0) |
1319 | 0 | sc->cd = iconv_open("SJIS", fc); |
1320 | 0 | else if (strcmp(fc, "CP932") == 0) |
1321 | 0 | sc->cd = iconv_open(tc, "SJIS"); |
1322 | 0 | } |
1323 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1324 | | /* |
1325 | | * archive_mstring on Windows directly convert multi-bytes |
1326 | | * into archive_wstring in order not to depend on locale |
1327 | | * so that you can do a I18N programming. This will be |
1328 | | * used only in archive_mstring_copy_mbs_len_l so far. |
1329 | | */ |
1330 | | if (flag & SCONV_FROM_CHARSET) { |
1331 | | sc->cd_w = iconv_open("UTF-8", fc); |
1332 | | if (sc->cd_w == (iconv_t)-1 && |
1333 | | (sc->flag & SCONV_BEST_EFFORT)) { |
1334 | | if (strcmp(fc, "CP932") == 0) |
1335 | | sc->cd_w = iconv_open("UTF-8", "SJIS"); |
1336 | | } |
1337 | | } |
1338 | | #endif /* _WIN32 && !__CYGWIN__ */ |
1339 | 461k | } |
1340 | 461k | #endif /* HAVE_ICONV */ |
1341 | | |
1342 | 461k | sc->flag = flag; |
1343 | | |
1344 | | /* |
1345 | | * Set up converters. |
1346 | | */ |
1347 | 461k | setup_converter(sc); |
1348 | | |
1349 | 461k | return (sc); |
1350 | 461k | } |
1351 | | |
1352 | | /* |
1353 | | * Free a string conversion object. |
1354 | | */ |
1355 | | static void |
1356 | | free_sconv_object(struct archive_string_conv *sc) |
1357 | 461k | { |
1358 | 461k | free(sc->from_charset); |
1359 | 461k | free(sc->to_charset); |
1360 | 461k | archive_string_free(&sc->utftmp); |
1361 | 461k | #if HAVE_ICONV |
1362 | 461k | if (sc->cd != (iconv_t)-1) |
1363 | 460k | iconv_close(sc->cd); |
1364 | 461k | if (sc->cd_w != (iconv_t)-1) |
1365 | 0 | iconv_close(sc->cd_w); |
1366 | 461k | #endif |
1367 | 461k | free(sc); |
1368 | 461k | } |
1369 | | |
1370 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1371 | | # if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) |
1372 | | # define GetOEMCP() CP_OEMCP |
1373 | | # endif |
1374 | | |
1375 | | static unsigned |
1376 | | my_atoi(const char *p) |
1377 | | { |
1378 | | unsigned cp; |
1379 | | |
1380 | | cp = 0; |
1381 | | while (*p) { |
1382 | | if (*p >= '0' && *p <= '9') |
1383 | | cp = cp * 10 + (*p - '0'); |
1384 | | else |
1385 | | return (-1); |
1386 | | p++; |
1387 | | } |
1388 | | return (cp); |
1389 | | } |
1390 | | |
1391 | | /* |
1392 | | * Translate Charset name (as used by iconv) into CodePage (as used by Windows) |
1393 | | * Return -1 if failed. |
1394 | | * |
1395 | | * Note: This translation code may be insufficient. |
1396 | | */ |
1397 | | static struct charset { |
1398 | | const char *name; |
1399 | | unsigned cp; |
1400 | | } charsets[] = { |
1401 | | /* MUST BE SORTED! */ |
1402 | | {"ASCII", 1252}, |
1403 | | {"ASMO-708", 708}, |
1404 | | {"BIG5", 950}, |
1405 | | {"CHINESE", 936}, |
1406 | | {"CP367", 1252}, |
1407 | | {"CP819", 1252}, |
1408 | | {"CP1025", 21025}, |
1409 | | {"DOS-720", 720}, |
1410 | | {"DOS-862", 862}, |
1411 | | {"EUC-CN", 51936}, |
1412 | | {"EUC-JP", 51932}, |
1413 | | {"EUC-KR", 949}, |
1414 | | {"EUCCN", 51936}, |
1415 | | {"EUCJP", 51932}, |
1416 | | {"EUCKR", 949}, |
1417 | | {"GB18030", 54936}, |
1418 | | {"GB2312", 936}, |
1419 | | {"HEBREW", 1255}, |
1420 | | {"HZ-GB-2312", 52936}, |
1421 | | {"IBM273", 20273}, |
1422 | | {"IBM277", 20277}, |
1423 | | {"IBM278", 20278}, |
1424 | | {"IBM280", 20280}, |
1425 | | {"IBM284", 20284}, |
1426 | | {"IBM285", 20285}, |
1427 | | {"IBM290", 20290}, |
1428 | | {"IBM297", 20297}, |
1429 | | {"IBM367", 1252}, |
1430 | | {"IBM420", 20420}, |
1431 | | {"IBM423", 20423}, |
1432 | | {"IBM424", 20424}, |
1433 | | {"IBM819", 1252}, |
1434 | | {"IBM871", 20871}, |
1435 | | {"IBM880", 20880}, |
1436 | | {"IBM905", 20905}, |
1437 | | {"IBM924", 20924}, |
1438 | | {"ISO-8859-1", 28591}, |
1439 | | {"ISO-8859-13", 28603}, |
1440 | | {"ISO-8859-15", 28605}, |
1441 | | {"ISO-8859-2", 28592}, |
1442 | | {"ISO-8859-3", 28593}, |
1443 | | {"ISO-8859-4", 28594}, |
1444 | | {"ISO-8859-5", 28595}, |
1445 | | {"ISO-8859-6", 28596}, |
1446 | | {"ISO-8859-7", 28597}, |
1447 | | {"ISO-8859-8", 28598}, |
1448 | | {"ISO-8859-9", 28599}, |
1449 | | {"ISO8859-1", 28591}, |
1450 | | {"ISO8859-13", 28603}, |
1451 | | {"ISO8859-15", 28605}, |
1452 | | {"ISO8859-2", 28592}, |
1453 | | {"ISO8859-3", 28593}, |
1454 | | {"ISO8859-4", 28594}, |
1455 | | {"ISO8859-5", 28595}, |
1456 | | {"ISO8859-6", 28596}, |
1457 | | {"ISO8859-7", 28597}, |
1458 | | {"ISO8859-8", 28598}, |
1459 | | {"ISO8859-9", 28599}, |
1460 | | {"JOHAB", 1361}, |
1461 | | {"KOI8-R", 20866}, |
1462 | | {"KOI8-U", 21866}, |
1463 | | {"KS_C_5601-1987", 949}, |
1464 | | {"LATIN1", 1252}, |
1465 | | {"LATIN2", 28592}, |
1466 | | {"MACINTOSH", 10000}, |
1467 | | {"SHIFT-JIS", 932}, |
1468 | | {"SHIFT_JIS", 932}, |
1469 | | {"SJIS", 932}, |
1470 | | {"US", 1252}, |
1471 | | {"US-ASCII", 1252}, |
1472 | | {"UTF-16", 1200}, |
1473 | | {"UTF-16BE", 1201}, |
1474 | | {"UTF-16LE", 1200}, |
1475 | | {"UTF-8", CP_UTF8}, |
1476 | | {"X-EUROPA", 29001}, |
1477 | | {"X-MAC-ARABIC", 10004}, |
1478 | | {"X-MAC-CE", 10029}, |
1479 | | {"X-MAC-CHINESEIMP", 10008}, |
1480 | | {"X-MAC-CHINESETRAD", 10002}, |
1481 | | {"X-MAC-CROATIAN", 10082}, |
1482 | | {"X-MAC-CYRILLIC", 10007}, |
1483 | | {"X-MAC-GREEK", 10006}, |
1484 | | {"X-MAC-HEBREW", 10005}, |
1485 | | {"X-MAC-ICELANDIC", 10079}, |
1486 | | {"X-MAC-JAPANESE", 10001}, |
1487 | | {"X-MAC-KOREAN", 10003}, |
1488 | | {"X-MAC-ROMANIAN", 10010}, |
1489 | | {"X-MAC-THAI", 10021}, |
1490 | | {"X-MAC-TURKISH", 10081}, |
1491 | | {"X-MAC-UKRAINIAN", 10017}, |
1492 | | }; |
1493 | | static unsigned |
1494 | | make_codepage_from_charset(const char *charset) |
1495 | | { |
1496 | | char cs[16]; |
1497 | | char *p; |
1498 | | unsigned cp; |
1499 | | int a, b; |
1500 | | |
1501 | | if (charset == NULL || strlen(charset) > 15) |
1502 | | return -1; |
1503 | | |
1504 | | /* Copy name to uppercase. */ |
1505 | | p = cs; |
1506 | | while (*charset) { |
1507 | | char c = *charset++; |
1508 | | if (c >= 'a' && c <= 'z') |
1509 | | c -= 'a' - 'A'; |
1510 | | *p++ = c; |
1511 | | } |
1512 | | *p++ = '\0'; |
1513 | | cp = -1; |
1514 | | |
1515 | | /* Look it up in the table first, so that we can easily |
1516 | | * override CP367, which we map to 1252 instead of 367. */ |
1517 | | a = 0; |
1518 | | b = sizeof(charsets)/sizeof(charsets[0]); |
1519 | | while (b > a) { |
1520 | | int c = (b + a) / 2; |
1521 | | int r = strcmp(charsets[c].name, cs); |
1522 | | if (r < 0) |
1523 | | a = c + 1; |
1524 | | else if (r > 0) |
1525 | | b = c; |
1526 | | else |
1527 | | return charsets[c].cp; |
1528 | | } |
1529 | | |
1530 | | /* If it's not in the table, try to parse it. */ |
1531 | | switch (*cs) { |
1532 | | case 'C': |
1533 | | if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') { |
1534 | | cp = my_atoi(cs + 2); |
1535 | | } else if (strcmp(cs, "CP_ACP") == 0) |
1536 | | cp = get_current_codepage(); |
1537 | | else if (strcmp(cs, "CP_OEMCP") == 0) |
1538 | | cp = get_current_oemcp(); |
1539 | | break; |
1540 | | case 'I': |
1541 | | if (cs[1] == 'B' && cs[2] == 'M' && |
1542 | | cs[3] >= '0' && cs[3] <= '9') { |
1543 | | cp = my_atoi(cs + 3); |
1544 | | } |
1545 | | break; |
1546 | | case 'W': |
1547 | | if (strncmp(cs, "WINDOWS-", 8) == 0) { |
1548 | | cp = my_atoi(cs + 8); |
1549 | | if (cp != 874 && (cp < 1250 || cp > 1258)) |
1550 | | cp = -1;/* This may invalid code. */ |
1551 | | } |
1552 | | break; |
1553 | | } |
1554 | | return (cp); |
1555 | | } |
1556 | | |
1557 | | /* |
1558 | | * Return ANSI Code Page of current locale set by setlocale(). |
1559 | | */ |
1560 | | static unsigned |
1561 | | get_current_codepage(void) |
1562 | | { |
1563 | | char *locale, *p; |
1564 | | unsigned cp; |
1565 | | |
1566 | | locale = setlocale(LC_CTYPE, NULL); |
1567 | | if (locale == NULL) |
1568 | | return (GetACP()); |
1569 | | if (locale[0] == 'C' && locale[1] == '\0') |
1570 | | return (CP_C_LOCALE); |
1571 | | p = strrchr(locale, '.'); |
1572 | | if (p == NULL) |
1573 | | return (GetACP()); |
1574 | | if ((strcmp(p+1, "utf8") == 0) || (strcmp(p+1, "UTF-8") == 0)) |
1575 | | return CP_UTF8; |
1576 | | cp = my_atoi(p+1); |
1577 | | if ((int)cp <= 0) |
1578 | | return (GetACP()); |
1579 | | return (cp); |
1580 | | } |
1581 | | |
1582 | | /* |
1583 | | * Translation table between Locale Name and ACP/OEMCP. |
1584 | | */ |
1585 | | static struct { |
1586 | | unsigned acp; |
1587 | | unsigned ocp; |
1588 | | const char *locale; |
1589 | | } acp_ocp_map[] = { |
1590 | | { 950, 950, "Chinese_Hong Kong S.A.R." }, |
1591 | | { 950, 950, "Chinese_Macau S.A.R." }, |
1592 | | { 936, 936, "Chinese_People's Republic of China" }, |
1593 | | { 936, 936, "Chinese (Simplified)_China" }, |
1594 | | { 936, 936, "Chinese (Simplified)_People's Republic of China" }, |
1595 | | { 950, 950, "Chinese_Taiwan" }, |
1596 | | { 950, 950, "Chinese (Traditional)_Hong Kong SAR" }, |
1597 | | { 950, 950, "Chinese (Traditional)_Macau SAR" }, |
1598 | | { 950, 950, "Chinese (Traditional)_Taiwan" }, |
1599 | | { 1250, 852, "Czech_Czech Republic" }, |
1600 | | { 1252, 850, "Danish_Denmark" }, |
1601 | | { 1252, 850, "Dutch_Netherlands" }, |
1602 | | { 1252, 850, "Dutch_Belgium" }, |
1603 | | { 1252, 437, "English_United States" }, |
1604 | | { 1252, 850, "English_Australia" }, |
1605 | | { 1252, 850, "English_Canada" }, |
1606 | | { 1252, 850, "English_New Zealand" }, |
1607 | | { 1252, 850, "English_United Kingdom" }, |
1608 | | { 1252, 437, "English_United States" }, |
1609 | | { 1252, 850, "Finnish_Finland" }, |
1610 | | { 1252, 850, "French_France" }, |
1611 | | { 1252, 850, "French_Belgium" }, |
1612 | | { 1252, 850, "French_Canada" }, |
1613 | | { 1252, 850, "French_Switzerland" }, |
1614 | | { 1252, 850, "German_Germany" }, |
1615 | | { 1252, 850, "German_Austria" }, |
1616 | | { 1252, 850, "German_Switzerland" }, |
1617 | | { 1253, 737, "Greek_Greece" }, |
1618 | | { 1250, 852, "Hungarian_Hungary" }, |
1619 | | { 1252, 850, "Icelandic_Iceland" }, |
1620 | | { 1252, 850, "Italian_Italy" }, |
1621 | | { 1252, 850, "Italian_Switzerland" }, |
1622 | | { 932, 932, "Japanese_Japan" }, |
1623 | | { 949, 949, "Korean_Korea" }, |
1624 | | { 949, 949, "Korean_South Korea" }, |
1625 | | { 949, 949, "Korean (Unified)_Korea" }, |
1626 | | { 1252, 850, "Norwegian (Bokmål)_Norway" }, |
1627 | | { 1252, 850, "Norwegian (BokmOl)_Norway" }, |
1628 | | { 1252, 850, "Norwegian (Nynorsk)_Norway" }, |
1629 | | { 1252, 850, "Norwegian-Nynorsk_Norway" }, |
1630 | | { 1250, 852, "Polish_Poland" }, |
1631 | | { 1252, 850, "Portuguese_Brazil" }, |
1632 | | { 1252, 850, "Portuguese_Portugal" }, |
1633 | | { 1252, 850, "Portuguese (Brazil)_Brazil" }, |
1634 | | { 1252, 850, "Portuguese (Portugal)_Portugal" }, |
1635 | | { 1251, 866, "Russian_Russia" }, |
1636 | | { 1250, 852, "Slovak_Slovakia" }, |
1637 | | { 1250, 852, "Slovak_Slovak Republic" }, |
1638 | | { 1252, 850, "Spanish_Spain" }, |
1639 | | { 1252, 850, "Spanish_Mexico" }, |
1640 | | { 1252, 850, "Spanish_Spain" }, |
1641 | | { 1252, 850, "Swedish_Sweden" }, |
1642 | | { 1254, 857, "Turkish_Turkey" }, |
1643 | | { 0, 0, NULL} |
1644 | | }; |
1645 | | |
1646 | | /* |
1647 | | * Return OEM Code Page of current locale set by setlocale(). |
1648 | | */ |
1649 | | static unsigned |
1650 | | get_current_oemcp(void) |
1651 | | { |
1652 | | int i; |
1653 | | char *locale, *p; |
1654 | | size_t len; |
1655 | | |
1656 | | locale = setlocale(LC_CTYPE, NULL); |
1657 | | if (locale == NULL) |
1658 | | return (GetOEMCP()); |
1659 | | if (locale[0] == 'C' && locale[1] == '\0') |
1660 | | return (CP_C_LOCALE); |
1661 | | |
1662 | | p = strrchr(locale, '.'); |
1663 | | if (p == NULL) |
1664 | | return (GetOEMCP()); |
1665 | | len = p - locale; |
1666 | | for (i = 0; acp_ocp_map[i].acp; i++) { |
1667 | | if (strncmp(acp_ocp_map[i].locale, locale, len) == 0) |
1668 | | return (acp_ocp_map[i].ocp); |
1669 | | } |
1670 | | return (GetOEMCP()); |
1671 | | } |
1672 | | #else |
1673 | | |
1674 | | /* |
1675 | | * POSIX platform does not use CodePage. |
1676 | | */ |
1677 | | |
1678 | | static unsigned |
1679 | | get_current_codepage(void) |
1680 | 460k | { |
1681 | 460k | return (-1);/* Unknown */ |
1682 | 460k | } |
1683 | | static unsigned |
1684 | | make_codepage_from_charset(const char *charset) |
1685 | 461k | { |
1686 | 461k | (void)charset; /* UNUSED */ |
1687 | 461k | return (-1);/* Unknown */ |
1688 | 461k | } |
1689 | | static unsigned |
1690 | | get_current_oemcp(void) |
1691 | 3.84k | { |
1692 | 3.84k | return (-1);/* Unknown */ |
1693 | 3.84k | } |
1694 | | |
1695 | | #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ |
1696 | | |
1697 | | /* |
1698 | | * Return a string conversion object. |
1699 | | */ |
1700 | | static struct archive_string_conv * |
1701 | | get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag) |
1702 | 463k | { |
1703 | 463k | struct archive_string_conv *sc; |
1704 | 463k | unsigned current_codepage; |
1705 | | |
1706 | | /* Check if we have made the sconv object. */ |
1707 | 463k | sc = find_sconv_object(a, fc, tc); |
1708 | 463k | if (sc != NULL) |
1709 | 2.12k | return (sc); |
1710 | | |
1711 | 461k | if (a == NULL) |
1712 | 457k | current_codepage = get_current_codepage(); |
1713 | 4.15k | else |
1714 | 4.15k | current_codepage = a->current_codepage; |
1715 | | |
1716 | 461k | sc = create_sconv_object(canonical_charset_name(fc), |
1717 | 461k | canonical_charset_name(tc), current_codepage, flag); |
1718 | 461k | if (sc == NULL) { |
1719 | 0 | if (a != NULL) |
1720 | 0 | archive_set_error(a, ENOMEM, |
1721 | 0 | "Could not allocate memory for " |
1722 | 0 | "a string conversion object"); |
1723 | 0 | return (NULL); |
1724 | 0 | } |
1725 | | |
1726 | | /* |
1727 | | * If there is no converter for current string conversion object, |
1728 | | * we cannot handle this conversion. |
1729 | | */ |
1730 | 461k | if (sc->nconverter == 0) { |
1731 | 0 | if (a != NULL) { |
1732 | 0 | #if HAVE_ICONV |
1733 | 0 | archive_set_error(a, ARCHIVE_ERRNO_MISC, |
1734 | 0 | "iconv_open failed: Cannot handle ``%s''", |
1735 | 0 | (flag & SCONV_TO_CHARSET)?tc:fc); |
1736 | | #else |
1737 | | archive_set_error(a, ARCHIVE_ERRNO_MISC, |
1738 | | "A character-set conversion not fully supported " |
1739 | | "on this platform"); |
1740 | | #endif |
1741 | 0 | } |
1742 | | /* Failed; free a sconv object. */ |
1743 | 0 | free_sconv_object(sc); |
1744 | 0 | return (NULL); |
1745 | 0 | } |
1746 | | |
1747 | | /* |
1748 | | * Success! |
1749 | | */ |
1750 | 461k | if (a != NULL) |
1751 | 4.15k | add_sconv_object(a, sc); |
1752 | 461k | return (sc); |
1753 | 461k | } |
1754 | | |
1755 | | static const char * |
1756 | | get_current_charset(struct archive *a) |
1757 | 463k | { |
1758 | 463k | const char *cur_charset; |
1759 | | |
1760 | 463k | if (a == NULL) |
1761 | 457k | cur_charset = default_iconv_charset(""); |
1762 | 6.28k | else { |
1763 | 6.28k | cur_charset = default_iconv_charset(a->current_code); |
1764 | 6.28k | if (a->current_code == NULL) { |
1765 | 3.84k | a->current_code = strdup(cur_charset); |
1766 | 3.84k | a->current_codepage = get_current_codepage(); |
1767 | 3.84k | a->current_oemcp = get_current_oemcp(); |
1768 | 3.84k | } |
1769 | 6.28k | } |
1770 | 463k | return (cur_charset); |
1771 | 463k | } |
1772 | | |
1773 | | /* |
1774 | | * Make and Return a string conversion object. |
1775 | | * Return NULL if the platform does not support the specified conversion |
1776 | | * and best_effort is 0. |
1777 | | * If best_effort is set, A string conversion object must be returned |
1778 | | * unless memory allocation for the object fails, but the conversion |
1779 | | * might fail when non-ASCII code is found. |
1780 | | */ |
1781 | | struct archive_string_conv * |
1782 | | archive_string_conversion_to_charset(struct archive *a, const char *charset, |
1783 | | int best_effort) |
1784 | 401k | { |
1785 | 401k | int flag = SCONV_TO_CHARSET; |
1786 | | |
1787 | 401k | if (best_effort) |
1788 | 401k | flag |= SCONV_BEST_EFFORT; |
1789 | 401k | return (get_sconv_object(a, get_current_charset(a), charset, flag)); |
1790 | 401k | } |
1791 | | |
1792 | | struct archive_string_conv * |
1793 | | archive_string_conversion_from_charset(struct archive *a, const char *charset, |
1794 | | int best_effort) |
1795 | 61.9k | { |
1796 | 61.9k | int flag = SCONV_FROM_CHARSET; |
1797 | | |
1798 | 61.9k | if (best_effort) |
1799 | 61.9k | flag |= SCONV_BEST_EFFORT; |
1800 | 61.9k | return (get_sconv_object(a, charset, get_current_charset(a), flag)); |
1801 | 61.9k | } |
1802 | | |
1803 | | /* |
1804 | | * archive_string_default_conversion_*_archive() are provided for Windows |
1805 | | * platform because other archiver application use CP_OEMCP for |
1806 | | * MultiByteToWideChar() and WideCharToMultiByte() for the filenames |
1807 | | * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP |
1808 | | * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP). |
1809 | | * So we should make a string conversion between CP_ACP and CP_OEMCP |
1810 | | * for compatibility. |
1811 | | */ |
1812 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
1813 | | struct archive_string_conv * |
1814 | | archive_string_default_conversion_for_read(struct archive *a) |
1815 | | { |
1816 | | const char *cur_charset = get_current_charset(a); |
1817 | | char oemcp[16]; |
1818 | | |
1819 | | /* NOTE: a check of cur_charset is unneeded but we need |
1820 | | * that get_current_charset() has been surely called at |
1821 | | * this time whatever C compiler optimized. */ |
1822 | | if (cur_charset != NULL && |
1823 | | (a->current_codepage == CP_C_LOCALE || |
1824 | | a->current_codepage == a->current_oemcp)) |
1825 | | return (NULL);/* no conversion. */ |
1826 | | |
1827 | | _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); |
1828 | | /* Make sure a null termination must be set. */ |
1829 | | oemcp[sizeof(oemcp)-1] = '\0'; |
1830 | | return (get_sconv_object(a, oemcp, cur_charset, |
1831 | | SCONV_FROM_CHARSET)); |
1832 | | } |
1833 | | |
1834 | | struct archive_string_conv * |
1835 | | archive_string_default_conversion_for_write(struct archive *a) |
1836 | | { |
1837 | | const char *cur_charset = get_current_charset(a); |
1838 | | char oemcp[16]; |
1839 | | |
1840 | | /* NOTE: a check of cur_charset is unneeded but we need |
1841 | | * that get_current_charset() has been surely called at |
1842 | | * this time whatever C compiler optimized. */ |
1843 | | if (cur_charset != NULL && |
1844 | | (a->current_codepage == CP_C_LOCALE || |
1845 | | a->current_codepage == a->current_oemcp)) |
1846 | | return (NULL);/* no conversion. */ |
1847 | | |
1848 | | _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp); |
1849 | | /* Make sure a null termination must be set. */ |
1850 | | oemcp[sizeof(oemcp)-1] = '\0'; |
1851 | | return (get_sconv_object(a, cur_charset, oemcp, |
1852 | | SCONV_TO_CHARSET)); |
1853 | | } |
1854 | | #else |
1855 | | struct archive_string_conv * |
1856 | | archive_string_default_conversion_for_read(struct archive *a) |
1857 | 8.18k | { |
1858 | 8.18k | (void)a; /* UNUSED */ |
1859 | 8.18k | return (NULL); |
1860 | 8.18k | } |
1861 | | |
1862 | | struct archive_string_conv * |
1863 | | archive_string_default_conversion_for_write(struct archive *a) |
1864 | 0 | { |
1865 | 0 | (void)a; /* UNUSED */ |
1866 | 0 | return (NULL); |
1867 | 0 | } |
1868 | | #endif |
1869 | | |
1870 | | /* |
1871 | | * Dispose of all character conversion objects in the archive object. |
1872 | | */ |
1873 | | void |
1874 | | archive_string_conversion_free(struct archive *a) |
1875 | 16.8k | { |
1876 | 16.8k | struct archive_string_conv *sc; |
1877 | 16.8k | struct archive_string_conv *sc_next; |
1878 | | |
1879 | 21.0k | for (sc = a->sconv; sc != NULL; sc = sc_next) { |
1880 | 4.15k | sc_next = sc->next; |
1881 | 4.15k | free_sconv_object(sc); |
1882 | 4.15k | } |
1883 | 16.8k | a->sconv = NULL; |
1884 | 16.8k | free(a->current_code); |
1885 | 16.8k | a->current_code = NULL; |
1886 | 16.8k | } |
1887 | | |
1888 | | /* |
1889 | | * Return a conversion charset name. |
1890 | | */ |
1891 | | const char * |
1892 | | archive_string_conversion_charset_name(struct archive_string_conv *sc) |
1893 | 4.20k | { |
1894 | 4.20k | if (sc == NULL) { |
1895 | 0 | return "current locale"; |
1896 | 0 | } |
1897 | 4.20k | if (sc->flag & SCONV_TO_CHARSET) |
1898 | 0 | return (sc->to_charset); |
1899 | 4.20k | else |
1900 | 4.20k | return (sc->from_charset); |
1901 | 4.20k | } |
1902 | | |
1903 | | /* |
1904 | | * Change the behavior of a string conversion. |
1905 | | */ |
1906 | | void |
1907 | | archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt) |
1908 | 0 | { |
1909 | 0 | switch (opt) { |
1910 | | /* |
1911 | | * A filename in UTF-8 was made with libarchive 2.x in a wrong |
1912 | | * assumption that wchar_t was Unicode. |
1913 | | * This option enables simulating the assumption in order to read |
1914 | | * that filename correctly. |
1915 | | */ |
1916 | 0 | case SCONV_SET_OPT_UTF8_LIBARCHIVE2X: |
1917 | 0 | #if (defined(_WIN32) && !defined(__CYGWIN__)) \ |
1918 | 0 | || defined(__STDC_ISO_10646__) || defined(__APPLE__) |
1919 | | /* |
1920 | | * Nothing to do for it since wchar_t on these platforms |
1921 | | * is really Unicode. |
1922 | | */ |
1923 | 0 | (void)sc; /* UNUSED */ |
1924 | | #else |
1925 | | if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) { |
1926 | | sc->flag |= SCONV_UTF8_LIBARCHIVE_2; |
1927 | | /* Set up string converters. */ |
1928 | | setup_converter(sc); |
1929 | | } |
1930 | | #endif |
1931 | 0 | break; |
1932 | 0 | case SCONV_SET_OPT_NORMALIZATION_C: |
1933 | 0 | if ((sc->flag & SCONV_NORMALIZATION_C) == 0) { |
1934 | 0 | sc->flag |= SCONV_NORMALIZATION_C; |
1935 | 0 | sc->flag &= ~SCONV_NORMALIZATION_D; |
1936 | | /* Set up string converters. */ |
1937 | 0 | setup_converter(sc); |
1938 | 0 | } |
1939 | 0 | break; |
1940 | 0 | case SCONV_SET_OPT_NORMALIZATION_D: |
1941 | 0 | #if defined(HAVE_ICONV) |
1942 | | /* |
1943 | | * If iconv will take the string, do not change the |
1944 | | * setting of the normalization. |
1945 | | */ |
1946 | 0 | if (!(sc->flag & SCONV_WIN_CP) && |
1947 | 0 | (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) && |
1948 | 0 | !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8))) |
1949 | 0 | break; |
1950 | 0 | #endif |
1951 | 0 | if ((sc->flag & SCONV_NORMALIZATION_D) == 0) { |
1952 | 0 | sc->flag |= SCONV_NORMALIZATION_D; |
1953 | 0 | sc->flag &= ~SCONV_NORMALIZATION_C; |
1954 | | /* Set up string converters. */ |
1955 | 0 | setup_converter(sc); |
1956 | 0 | } |
1957 | 0 | break; |
1958 | 0 | default: |
1959 | 0 | break; |
1960 | 0 | } |
1961 | 0 | } |
1962 | | |
1963 | | /* |
1964 | | * |
1965 | | * Copy one archive_string to another in locale conversion. |
1966 | | * |
1967 | | * archive_strncat_l(); |
1968 | | * archive_strncpy_l(); |
1969 | | * |
1970 | | */ |
1971 | | |
1972 | | static size_t |
1973 | | mbsnbytes(const void *_p, size_t n) |
1974 | 746k | { |
1975 | 746k | size_t s; |
1976 | 746k | const char *p, *pp; |
1977 | | |
1978 | 746k | if (_p == NULL) |
1979 | 0 | return (0); |
1980 | 746k | p = (const char *)_p; |
1981 | | |
1982 | | /* Like strlen(p), except won't examine positions beyond p[n]. */ |
1983 | 746k | s = 0; |
1984 | 746k | pp = p; |
1985 | 146M | while (s < n && *pp) { |
1986 | 145M | pp++; |
1987 | 145M | s++; |
1988 | 145M | } |
1989 | 746k | return (s); |
1990 | 746k | } |
1991 | | |
1992 | | static size_t |
1993 | | utf16nbytes(const void *_p, size_t n) |
1994 | 4.49k | { |
1995 | 4.49k | size_t s; |
1996 | 4.49k | const char *p, *pp; |
1997 | | |
1998 | 4.49k | if (_p == NULL) |
1999 | 0 | return (0); |
2000 | 4.49k | p = (const char *)_p; |
2001 | | |
2002 | | /* Like strlen(p), except won't examine positions beyond p[n]. */ |
2003 | 4.49k | s = 0; |
2004 | 4.49k | pp = p; |
2005 | 4.49k | n >>= 1; |
2006 | 154k | while (s < n && (pp[0] || pp[1])) { |
2007 | 150k | pp += 2; |
2008 | 150k | s++; |
2009 | 150k | } |
2010 | 4.49k | return (s<<1); |
2011 | 4.49k | } |
2012 | | |
2013 | | int |
2014 | | archive_strncpy_l(struct archive_string *as, const void *_p, size_t n, |
2015 | | struct archive_string_conv *sc) |
2016 | 853k | { |
2017 | 853k | as->length = 0; |
2018 | 853k | return (archive_strncat_l(as, _p, n, sc)); |
2019 | 853k | } |
2020 | | |
2021 | | int |
2022 | | archive_strncat_l(struct archive_string *as, const void *_p, size_t n, |
2023 | | struct archive_string_conv *sc) |
2024 | 853k | { |
2025 | 853k | const void *s; |
2026 | 853k | size_t length = 0; |
2027 | 853k | int i, r = 0, r2; |
2028 | | |
2029 | 853k | if (_p != NULL && n > 0) { |
2030 | 750k | if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) |
2031 | 4.49k | length = utf16nbytes(_p, n); |
2032 | 746k | else |
2033 | 746k | length = mbsnbytes(_p, n); |
2034 | 750k | } |
2035 | | |
2036 | | /* We must allocate memory even if there is no data for conversion |
2037 | | * or copy. This simulates archive_string_append behavior. */ |
2038 | 853k | if (length == 0) { |
2039 | 139k | size_t tn = 1; |
2040 | 139k | if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) |
2041 | 0 | tn = 2; |
2042 | 139k | if (archive_string_ensure(as, as->length + tn) == NULL) |
2043 | 0 | return (-1); |
2044 | 139k | as->s[as->length] = 0; |
2045 | 139k | if (tn == 2) |
2046 | 0 | as->s[as->length+1] = 0; |
2047 | 139k | return (0); |
2048 | 139k | } |
2049 | | |
2050 | | /* |
2051 | | * If sc is NULL, we just make a copy. |
2052 | | */ |
2053 | 714k | if (sc == NULL) { |
2054 | 316k | if (archive_string_append(as, _p, length) == NULL) |
2055 | 0 | return (-1);/* No memory */ |
2056 | 316k | return (0); |
2057 | 316k | } |
2058 | | |
2059 | 398k | s = _p; |
2060 | 398k | i = 0; |
2061 | 398k | if (sc->nconverter > 1) { |
2062 | 62.9k | sc->utftmp.length = 0; |
2063 | 62.9k | r2 = sc->converter[0](&(sc->utftmp), s, length, sc); |
2064 | 62.9k | if (r2 != 0 && errno == ENOMEM) |
2065 | 0 | return (r2); |
2066 | 62.9k | if (r > r2) |
2067 | 48.5k | r = r2; |
2068 | 62.9k | s = sc->utftmp.s; |
2069 | 62.9k | length = sc->utftmp.length; |
2070 | 62.9k | ++i; |
2071 | 62.9k | } |
2072 | 398k | r2 = sc->converter[i](as, s, length, sc); |
2073 | 398k | if (r > r2) |
2074 | 24.0k | r = r2; |
2075 | 398k | return (r); |
2076 | 398k | } |
2077 | | |
2078 | | struct archive_string * |
2079 | | archive_string_dirname(struct archive_string *as) |
2080 | 1.82k | { |
2081 | | /* strip trailing separators */ |
2082 | 1.90k | while (as->length > 1 && as->s[as->length - 1] == '/') |
2083 | 79 | as->length--; |
2084 | | /* strip final component */ |
2085 | 6.96k | while (as->length > 0 && as->s[as->length - 1] != '/') |
2086 | 5.13k | as->length--; |
2087 | | /* empty path -> cwd */ |
2088 | 1.82k | if (as->length == 0) |
2089 | 876 | return (archive_strcat(as, ".")); |
2090 | | /* strip separator(s) */ |
2091 | 1.90k | while (as->length > 1 && as->s[as->length - 1] == '/') |
2092 | 951 | as->length--; |
2093 | | /* terminate */ |
2094 | 951 | as->s[as->length] = '\0'; |
2095 | 951 | return (as); |
2096 | 1.82k | } |
2097 | | |
2098 | | #if HAVE_ICONV |
2099 | | |
2100 | | /* |
2101 | | * Return -1 if conversion fails. |
2102 | | */ |
2103 | | static int |
2104 | | iconv_strncat_in_locale(struct archive_string *as, const void *_p, |
2105 | | size_t length, struct archive_string_conv *sc) |
2106 | 398k | { |
2107 | 398k | ICONV_CONST char *itp; |
2108 | 398k | size_t remaining; |
2109 | 398k | iconv_t cd; |
2110 | 398k | char *outp; |
2111 | 398k | size_t avail, bs; |
2112 | 398k | int return_value = 0; /* success */ |
2113 | 398k | size_t to_size, from_size; |
2114 | | |
2115 | 398k | if (sc->flag & SCONV_TO_UTF16) |
2116 | 0 | to_size = 2; |
2117 | 398k | else |
2118 | 398k | to_size = 1; |
2119 | 398k | if (sc->flag & SCONV_FROM_UTF16) |
2120 | 2.68k | from_size = 2; |
2121 | 395k | else |
2122 | 395k | from_size = 1; |
2123 | | |
2124 | 398k | if (archive_string_ensure(as, as->length + length*2+to_size) == NULL) |
2125 | 0 | return (-1); |
2126 | | |
2127 | 398k | cd = sc->cd; |
2128 | 398k | itp = (char *)(uintptr_t)_p; |
2129 | 398k | remaining = length; |
2130 | 398k | outp = as->s + as->length; |
2131 | 398k | avail = as->buffer_length - as->length - to_size; |
2132 | 2.85M | while (remaining >= from_size) { |
2133 | 2.84M | size_t result = iconv(cd, &itp, &remaining, &outp, &avail); |
2134 | | |
2135 | 2.84M | if (result == 0) |
2136 | 380k | break; /* Conversion completed. */ |
2137 | 2.46M | if (result != (size_t)-1) { |
2138 | | /* |
2139 | | * Conversion did not fail, but it performed |
2140 | | * some non-identical character substitutions. |
2141 | | */ |
2142 | 0 | return_value = -1; |
2143 | 0 | break; |
2144 | 0 | } |
2145 | | |
2146 | 2.46M | if (errno == EILSEQ || errno == EINVAL) { |
2147 | | /* |
2148 | | * If an output charset is UTF-8 or UTF-16BE/LE, |
2149 | | * unknown character should be U+FFFD |
2150 | | * (replacement character). |
2151 | | */ |
2152 | 2.46M | if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) { |
2153 | 480k | size_t rbytes; |
2154 | 480k | if (sc->flag & SCONV_TO_UTF8) |
2155 | 480k | rbytes = sizeof(utf8_replacement_char); |
2156 | 0 | else |
2157 | 0 | rbytes = 2; |
2158 | | |
2159 | 480k | if (avail < rbytes) { |
2160 | 1.15k | as->length = outp - as->s; |
2161 | 1.15k | bs = as->buffer_length + |
2162 | 1.15k | (remaining * to_size) + rbytes; |
2163 | 1.15k | if (NULL == |
2164 | 1.15k | archive_string_ensure(as, bs)) |
2165 | 0 | return (-1); |
2166 | 1.15k | outp = as->s + as->length; |
2167 | 1.15k | avail = as->buffer_length |
2168 | 1.15k | - as->length - to_size; |
2169 | 1.15k | } |
2170 | 480k | if (sc->flag & SCONV_TO_UTF8) |
2171 | 480k | memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char)); |
2172 | 0 | else if (sc->flag & SCONV_TO_UTF16BE) |
2173 | 0 | archive_be16enc(outp, UNICODE_R_CHAR); |
2174 | 0 | else |
2175 | 0 | archive_le16enc(outp, UNICODE_R_CHAR); |
2176 | 480k | outp += rbytes; |
2177 | 480k | avail -= rbytes; |
2178 | 1.98M | } else { |
2179 | | /* Skip the illegal input bytes. */ |
2180 | 1.98M | if (avail < 1) { |
2181 | 0 | as->length = outp - as->s; |
2182 | 0 | bs = as->buffer_length + |
2183 | 0 | (remaining * to_size) + 1; |
2184 | 0 | if (NULL == |
2185 | 0 | archive_string_ensure(as, bs)) |
2186 | 0 | return (-1); |
2187 | 0 | outp = as->s + as->length; |
2188 | 0 | avail = as->buffer_length |
2189 | 0 | - as->length - to_size; |
2190 | 0 | } |
2191 | 1.98M | *outp++ = '?'; |
2192 | 1.98M | avail--; |
2193 | 1.98M | } |
2194 | 2.46M | itp += from_size; |
2195 | 2.46M | remaining -= from_size; |
2196 | 2.46M | return_value = -1; /* failure */ |
2197 | 2.46M | } else { |
2198 | | /* E2BIG no output buffer, |
2199 | | * Increase an output buffer. */ |
2200 | 398 | as->length = outp - as->s; |
2201 | 398 | bs = as->buffer_length + remaining * 2; |
2202 | 398 | if (NULL == archive_string_ensure(as, bs)) |
2203 | 0 | return (-1); |
2204 | 398 | outp = as->s + as->length; |
2205 | 398 | avail = as->buffer_length - as->length - to_size; |
2206 | 398 | } |
2207 | 2.46M | } |
2208 | 398k | as->length = outp - as->s; |
2209 | 398k | as->s[as->length] = 0; |
2210 | 398k | if (to_size == 2) |
2211 | 0 | as->s[as->length+1] = 0; |
2212 | 398k | return (return_value); |
2213 | 398k | } |
2214 | | |
2215 | | #endif /* HAVE_ICONV */ |
2216 | | |
2217 | | |
2218 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
2219 | | |
2220 | | /* |
2221 | | * Translate a string from a some CodePage to an another CodePage by |
2222 | | * Windows APIs, and copy the result. Return -1 if conversion fails. |
2223 | | */ |
2224 | | static int |
2225 | | strncat_in_codepage(struct archive_string *as, |
2226 | | const void *_p, size_t length, struct archive_string_conv *sc) |
2227 | | { |
2228 | | const char *s = (const char *)_p; |
2229 | | struct archive_wstring aws; |
2230 | | size_t l; |
2231 | | int r, saved_flag; |
2232 | | |
2233 | | archive_string_init(&aws); |
2234 | | saved_flag = sc->flag; |
2235 | | sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C); |
2236 | | r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc); |
2237 | | sc->flag = saved_flag; |
2238 | | if (r != 0) { |
2239 | | archive_wstring_free(&aws); |
2240 | | if (errno != ENOMEM) |
2241 | | archive_string_append(as, s, length); |
2242 | | return (-1); |
2243 | | } |
2244 | | |
2245 | | l = as->length; |
2246 | | r = archive_string_append_from_wcs_in_codepage( |
2247 | | as, aws.s, aws.length, sc); |
2248 | | if (r != 0 && errno != ENOMEM && l == as->length) |
2249 | | archive_string_append(as, s, length); |
2250 | | archive_wstring_free(&aws); |
2251 | | return (r); |
2252 | | } |
2253 | | |
2254 | | /* |
2255 | | * Test whether MBS ==> WCS is okay. |
2256 | | */ |
2257 | | static int |
2258 | | invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) |
2259 | | { |
2260 | | const char *p = (const char *)_p; |
2261 | | unsigned codepage; |
2262 | | DWORD mbflag = MB_ERR_INVALID_CHARS; |
2263 | | |
2264 | | if (sc->flag & SCONV_FROM_CHARSET) |
2265 | | codepage = sc->to_cp; |
2266 | | else |
2267 | | codepage = sc->from_cp; |
2268 | | |
2269 | | if (codepage == CP_C_LOCALE) |
2270 | | return (0); |
2271 | | if (codepage != CP_UTF8) |
2272 | | mbflag |= MB_PRECOMPOSED; |
2273 | | |
2274 | | if (n > (size_t)INT_MAX) |
2275 | | return (-1); /* Invalid */ |
2276 | | if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0) |
2277 | | return (-1); /* Invalid */ |
2278 | | return (0); /* Okay */ |
2279 | | } |
2280 | | |
2281 | | #else |
2282 | | |
2283 | | /* |
2284 | | * Test whether MBS ==> WCS is okay. |
2285 | | */ |
2286 | | static int |
2287 | | invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) |
2288 | 0 | { |
2289 | 0 | const char *p = (const char *)_p; |
2290 | 0 | size_t r; |
2291 | |
|
2292 | 0 | #if HAVE_MBRTOWC |
2293 | 0 | mbstate_t shift_state; |
2294 | |
|
2295 | 0 | memset(&shift_state, 0, sizeof(shift_state)); |
2296 | | #else |
2297 | | /* Clear the shift state before starting. */ |
2298 | | mbtowc(NULL, NULL, 0); |
2299 | | #endif |
2300 | 0 | while (n) { |
2301 | 0 | wchar_t wc; |
2302 | |
|
2303 | 0 | #if HAVE_MBRTOWC |
2304 | 0 | r = mbrtowc(&wc, p, n, &shift_state); |
2305 | | #else |
2306 | | r = mbtowc(&wc, p, n); |
2307 | | #endif |
2308 | 0 | if (r == (size_t)-1 || r == (size_t)-2) |
2309 | 0 | return (-1);/* Invalid. */ |
2310 | 0 | if (r == 0) |
2311 | 0 | break; |
2312 | 0 | p += r; |
2313 | 0 | n -= r; |
2314 | 0 | } |
2315 | 0 | (void)sc; /* UNUSED */ |
2316 | 0 | return (0); /* All Okey. */ |
2317 | 0 | } |
2318 | | |
2319 | | #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ |
2320 | | |
2321 | | /* |
2322 | | * Basically returns -1 because we cannot make a conversion of charset |
2323 | | * without iconv but in some cases this would return 0. |
2324 | | * Returns 0 if all copied characters are ASCII. |
2325 | | * Returns 0 if both from-locale and to-locale are the same and those |
2326 | | * can be WCS with no error. |
2327 | | */ |
2328 | | static int |
2329 | | best_effort_strncat_in_locale(struct archive_string *as, const void *_p, |
2330 | | size_t length, struct archive_string_conv *sc) |
2331 | 14 | { |
2332 | 14 | size_t remaining; |
2333 | 14 | const uint8_t *itp; |
2334 | 14 | int return_value = 0; /* success */ |
2335 | | |
2336 | | /* |
2337 | | * If both from-locale and to-locale is the same, this makes a copy. |
2338 | | * And then this checks all copied MBS can be WCS if so returns 0. |
2339 | | */ |
2340 | 14 | if (sc->same) { |
2341 | 0 | if (archive_string_append(as, _p, length) == NULL) |
2342 | 0 | return (-1);/* No memory */ |
2343 | 0 | return (invalid_mbs(_p, length, sc)); |
2344 | 0 | } |
2345 | | |
2346 | | /* |
2347 | | * If a character is ASCII, this just copies it. If not, this |
2348 | | * assigns '?' character instead but in UTF-8 locale this assigns |
2349 | | * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD, |
2350 | | * a Replacement Character in Unicode. |
2351 | | */ |
2352 | | |
2353 | 14 | remaining = length; |
2354 | 14 | itp = (const uint8_t *)_p; |
2355 | 638 | while (remaining > 0 && *itp) { |
2356 | 624 | if (*itp > 127) { |
2357 | | // Non-ASCII: Substitute with suitable replacement |
2358 | 312 | if (sc->flag & SCONV_TO_UTF8) { |
2359 | 0 | if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) { |
2360 | 0 | __archive_errx(1, "Out of memory"); |
2361 | 0 | } |
2362 | 312 | } else { |
2363 | 312 | archive_strappend_char(as, '?'); |
2364 | 312 | } |
2365 | 312 | return_value = -1; |
2366 | 312 | } else { |
2367 | 312 | archive_strappend_char(as, *itp); |
2368 | 312 | } |
2369 | 624 | ++itp; |
2370 | 624 | --remaining; |
2371 | 624 | } |
2372 | 14 | return (return_value); |
2373 | 14 | } |
2374 | | |
2375 | | |
2376 | | /* |
2377 | | * Unicode conversion functions. |
2378 | | * - UTF-8 <===> UTF-8 in removing surrogate pairs. |
2379 | | * - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs. |
2380 | | * - UTF-8 made by libarchive 2.x ===> UTF-8. |
2381 | | * - UTF-16BE <===> UTF-8. |
2382 | | * |
2383 | | */ |
2384 | | |
2385 | | /* |
2386 | | * Utility to convert a single UTF-8 sequence. |
2387 | | * |
2388 | | * Usually return used bytes, return used byte in negative value when |
2389 | | * a unicode character is replaced with U+FFFD. |
2390 | | * See also http://unicode.org/review/pr-121.html Public Review Issue #121 |
2391 | | * Recommended Practice for Replacement Characters. |
2392 | | */ |
2393 | | static int |
2394 | | _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) |
2395 | 93.4M | { |
2396 | 93.4M | static const char utf8_count[256] = { |
2397 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ |
2398 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ |
2399 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ |
2400 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ |
2401 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ |
2402 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ |
2403 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ |
2404 | 93.4M | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ |
2405 | 93.4M | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ |
2406 | 93.4M | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ |
2407 | 93.4M | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ |
2408 | 93.4M | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ |
2409 | 93.4M | 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ |
2410 | 93.4M | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ |
2411 | 93.4M | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ |
2412 | 93.4M | 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ |
2413 | 93.4M | }; |
2414 | 93.4M | int ch, i; |
2415 | 93.4M | int cnt; |
2416 | 93.4M | uint32_t wc; |
2417 | | |
2418 | | /* Sanity check. */ |
2419 | 93.4M | if (n == 0) |
2420 | 60.9k | return (0); |
2421 | | /* |
2422 | | * Decode 1-4 bytes depending on the value of the first byte. |
2423 | | */ |
2424 | 93.3M | ch = (unsigned char)*s; |
2425 | 93.3M | if (ch == 0) |
2426 | 0 | return (0); /* Standard: return 0 for end-of-string. */ |
2427 | 93.3M | cnt = utf8_count[ch]; |
2428 | | |
2429 | | /* Invalid sequence or there are not plenty bytes. */ |
2430 | 93.3M | if (n < (size_t)cnt) { |
2431 | 2.52k | cnt = (int)n; |
2432 | 2.82k | for (i = 1; i < cnt; i++) { |
2433 | 973 | if ((s[i] & 0xc0) != 0x80) { |
2434 | 671 | cnt = i; |
2435 | 671 | break; |
2436 | 671 | } |
2437 | 973 | } |
2438 | 2.52k | goto invalid_sequence; |
2439 | 2.52k | } |
2440 | | |
2441 | | /* Make a Unicode code point from a single UTF-8 sequence. */ |
2442 | 93.3M | switch (cnt) { |
2443 | 92.6M | case 1: /* 1 byte sequence. */ |
2444 | 92.6M | *pwc = ch & 0x7f; |
2445 | 92.6M | return (cnt); |
2446 | 169k | case 2: /* 2 bytes sequence. */ |
2447 | 169k | if ((s[1] & 0xc0) != 0x80) { |
2448 | 63.3k | cnt = 1; |
2449 | 63.3k | goto invalid_sequence; |
2450 | 63.3k | } |
2451 | 106k | *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); |
2452 | 106k | return (cnt); |
2453 | 167k | case 3: /* 3 bytes sequence. */ |
2454 | 167k | if ((s[1] & 0xc0) != 0x80) { |
2455 | 53.4k | cnt = 1; |
2456 | 53.4k | goto invalid_sequence; |
2457 | 53.4k | } |
2458 | 113k | if ((s[2] & 0xc0) != 0x80) { |
2459 | 44.6k | cnt = 2; |
2460 | 44.6k | goto invalid_sequence; |
2461 | 44.6k | } |
2462 | 68.9k | wc = ((ch & 0x0f) << 12) |
2463 | 68.9k | | ((s[1] & 0x3f) << 6) |
2464 | 68.9k | | (s[2] & 0x3f); |
2465 | 68.9k | if (wc < 0x800) |
2466 | 218 | goto invalid_sequence;/* Overlong sequence. */ |
2467 | 68.7k | break; |
2468 | 68.7k | case 4: /* 4 bytes sequence. */ |
2469 | 12.1k | if ((s[1] & 0xc0) != 0x80) { |
2470 | 4.26k | cnt = 1; |
2471 | 4.26k | goto invalid_sequence; |
2472 | 4.26k | } |
2473 | 7.86k | if ((s[2] & 0xc0) != 0x80) { |
2474 | 1.36k | cnt = 2; |
2475 | 1.36k | goto invalid_sequence; |
2476 | 1.36k | } |
2477 | 6.50k | if ((s[3] & 0xc0) != 0x80) { |
2478 | 704 | cnt = 3; |
2479 | 704 | goto invalid_sequence; |
2480 | 704 | } |
2481 | 5.79k | wc = ((ch & 0x07) << 18) |
2482 | 5.79k | | ((s[1] & 0x3f) << 12) |
2483 | 5.79k | | ((s[2] & 0x3f) << 6) |
2484 | 5.79k | | (s[3] & 0x3f); |
2485 | 5.79k | if (wc < 0x10000) |
2486 | 279 | goto invalid_sequence;/* Overlong sequence. */ |
2487 | 5.51k | break; |
2488 | 348k | default: /* Others are all invalid sequence. */ |
2489 | 348k | if (ch == 0xc0 || ch == 0xc1) |
2490 | 1.13k | cnt = 2; |
2491 | 346k | else if (ch >= 0xf5 && ch <= 0xf7) |
2492 | 3.12k | cnt = 4; |
2493 | 343k | else if (ch >= 0xf8 && ch <= 0xfb) |
2494 | 4.11k | cnt = 5; |
2495 | 339k | else if (ch == 0xfc || ch == 0xfd) |
2496 | 7.71k | cnt = 6; |
2497 | 331k | else |
2498 | 331k | cnt = 1; |
2499 | 348k | if (n < (size_t)cnt) |
2500 | 2.02k | cnt = (int)n; |
2501 | 365k | for (i = 1; i < cnt; i++) { |
2502 | 32.0k | if ((s[i] & 0xc0) != 0x80) { |
2503 | 14.6k | cnt = i; |
2504 | 14.6k | break; |
2505 | 14.6k | } |
2506 | 32.0k | } |
2507 | 348k | goto invalid_sequence; |
2508 | 93.3M | } |
2509 | | |
2510 | | /* The code point larger than 0x10FFFF is not legal |
2511 | | * Unicode values. */ |
2512 | 74.2k | if (wc > UNICODE_MAX) |
2513 | 324 | goto invalid_sequence; |
2514 | | /* Correctly gets a Unicode, returns used bytes. */ |
2515 | 73.9k | *pwc = wc; |
2516 | 73.9k | return (cnt); |
2517 | 519k | invalid_sequence: |
2518 | 519k | *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ |
2519 | 519k | return (cnt * -1); |
2520 | 74.2k | } |
2521 | | |
2522 | | static int |
2523 | | utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) |
2524 | 0 | { |
2525 | 0 | int cnt; |
2526 | |
|
2527 | 0 | cnt = _utf8_to_unicode(pwc, s, n); |
2528 | | /* Any of Surrogate pair is not legal Unicode values. */ |
2529 | 0 | if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc)) |
2530 | 0 | return (-3); |
2531 | 0 | return (cnt); |
2532 | 0 | } |
2533 | | |
2534 | | static inline uint32_t |
2535 | | combine_surrogate_pair(uint32_t uc, uint32_t uc2) |
2536 | 1.96k | { |
2537 | 1.96k | uc -= 0xD800; |
2538 | 1.96k | uc *= 0x400; |
2539 | 1.96k | uc += uc2 - 0xDC00; |
2540 | 1.96k | uc += 0x10000; |
2541 | 1.96k | return (uc); |
2542 | 1.96k | } |
2543 | | |
2544 | | /* |
2545 | | * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in |
2546 | | * removing surrogate pairs. |
2547 | | * |
2548 | | * CESU-8: The Compatibility Encoding Scheme for UTF-16. |
2549 | | * |
2550 | | * Usually return used bytes, return used byte in negative value when |
2551 | | * a unicode character is replaced with U+FFFD. |
2552 | | */ |
2553 | | static int |
2554 | | cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n) |
2555 | 93.4M | { |
2556 | 93.4M | uint32_t wc = 0; |
2557 | 93.4M | int cnt; |
2558 | | |
2559 | 93.4M | cnt = _utf8_to_unicode(&wc, s, n); |
2560 | 93.4M | if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) { |
2561 | 34.1k | uint32_t wc2 = 0; |
2562 | 34.1k | if (n - 3 < 3) { |
2563 | | /* Invalid byte sequence. */ |
2564 | 152 | goto invalid_sequence; |
2565 | 152 | } |
2566 | 33.9k | cnt = _utf8_to_unicode(&wc2, s+3, n-3); |
2567 | 33.9k | if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) { |
2568 | | /* Invalid byte sequence. */ |
2569 | 32.1k | goto invalid_sequence; |
2570 | 32.1k | } |
2571 | 1.84k | wc = combine_surrogate_pair(wc, wc2); |
2572 | 1.84k | cnt = 6; |
2573 | 93.3M | } else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) { |
2574 | | /* Invalid byte sequence. */ |
2575 | 2.25k | goto invalid_sequence; |
2576 | 2.25k | } |
2577 | 93.3M | *pwc = wc; |
2578 | 93.3M | return (cnt); |
2579 | 34.5k | invalid_sequence: |
2580 | 34.5k | *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */ |
2581 | 34.5k | if (cnt > 0) |
2582 | 5.65k | cnt *= -1; |
2583 | 34.5k | return (cnt); |
2584 | 93.4M | } |
2585 | | |
2586 | | /* |
2587 | | * Convert a Unicode code point to a single UTF-8 sequence. |
2588 | | * |
2589 | | * NOTE:This function does not check if the Unicode is legal or not. |
2590 | | * Please you definitely check it before calling this. |
2591 | | */ |
2592 | | static size_t |
2593 | | unicode_to_utf8(char *p, size_t remaining, uint32_t uc) |
2594 | 655k | { |
2595 | 655k | char *_p = p; |
2596 | | |
2597 | | /* Invalid Unicode char maps to Replacement character */ |
2598 | 655k | if (uc > UNICODE_MAX) |
2599 | 0 | uc = UNICODE_R_CHAR; |
2600 | | /* Translate code point to UTF8 */ |
2601 | 655k | if (uc <= 0x7f) { |
2602 | 45.6k | if (remaining == 0) |
2603 | 543 | return (0); |
2604 | 45.0k | *p++ = (char)uc; |
2605 | 609k | } else if (uc <= 0x7ff) { |
2606 | 82.6k | if (remaining < 2) |
2607 | 163 | return (0); |
2608 | 82.4k | *p++ = 0xc0 | ((uc >> 6) & 0x1f); |
2609 | 82.4k | *p++ = 0x80 | (uc & 0x3f); |
2610 | 526k | } else if (uc <= 0xffff) { |
2611 | 524k | if (remaining < 3) |
2612 | 8.93k | return (0); |
2613 | 516k | *p++ = 0xe0 | ((uc >> 12) & 0x0f); |
2614 | 516k | *p++ = 0x80 | ((uc >> 6) & 0x3f); |
2615 | 516k | *p++ = 0x80 | (uc & 0x3f); |
2616 | 516k | } else { |
2617 | 1.86k | if (remaining < 4) |
2618 | 162 | return (0); |
2619 | 1.69k | *p++ = 0xf0 | ((uc >> 18) & 0x07); |
2620 | 1.69k | *p++ = 0x80 | ((uc >> 12) & 0x3f); |
2621 | 1.69k | *p++ = 0x80 | ((uc >> 6) & 0x3f); |
2622 | 1.69k | *p++ = 0x80 | (uc & 0x3f); |
2623 | 1.69k | } |
2624 | 645k | return (p - _p); |
2625 | 655k | } |
2626 | | |
2627 | | static int |
2628 | | utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n) |
2629 | 123k | { |
2630 | 123k | return (utf16_to_unicode(pwc, s, n, 1)); |
2631 | 123k | } |
2632 | | |
2633 | | static int |
2634 | | utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n) |
2635 | 29.5k | { |
2636 | 29.5k | return (utf16_to_unicode(pwc, s, n, 0)); |
2637 | 29.5k | } |
2638 | | |
2639 | | static int |
2640 | | utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be) |
2641 | 153k | { |
2642 | 153k | const char *utf16 = s; |
2643 | 153k | unsigned uc; |
2644 | | |
2645 | 153k | if (n == 0) |
2646 | 2.68k | return (0); |
2647 | 150k | if (n == 1) { |
2648 | | /* set the Replacement Character instead. */ |
2649 | 0 | *pwc = UNICODE_R_CHAR; |
2650 | 0 | return (-1); |
2651 | 0 | } |
2652 | | |
2653 | 150k | if (be) |
2654 | 121k | uc = archive_be16dec(utf16); |
2655 | 28.6k | else |
2656 | 28.6k | uc = archive_le16dec(utf16); |
2657 | 150k | utf16 += 2; |
2658 | | |
2659 | | /* If this is a surrogate pair, assemble the full code point.*/ |
2660 | 150k | if (IS_HIGH_SURROGATE_LA(uc)) { |
2661 | 11.8k | unsigned uc2; |
2662 | | |
2663 | 11.8k | if (n >= 4) { |
2664 | 11.8k | if (be) |
2665 | 7.74k | uc2 = archive_be16dec(utf16); |
2666 | 4.14k | else |
2667 | 4.14k | uc2 = archive_le16dec(utf16); |
2668 | 11.8k | } else |
2669 | 7 | uc2 = 0; |
2670 | 11.8k | if (IS_LOW_SURROGATE_LA(uc2)) { |
2671 | 124 | uc = combine_surrogate_pair(uc, uc2); |
2672 | 124 | utf16 += 2; |
2673 | 11.7k | } else { |
2674 | | /* Undescribed code point should be U+FFFD |
2675 | | * (replacement character). */ |
2676 | 11.7k | *pwc = UNICODE_R_CHAR; |
2677 | 11.7k | return (-2); |
2678 | 11.7k | } |
2679 | 11.8k | } |
2680 | | |
2681 | | /* |
2682 | | * Surrogate pair values(0xd800 through 0xdfff) are only |
2683 | | * used by UTF-16, so, after above calculation, the code |
2684 | | * must not be surrogate values, and Unicode has no codes |
2685 | | * larger than 0x10ffff. Thus, those are not legal Unicode |
2686 | | * values. |
2687 | | */ |
2688 | 138k | if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) { |
2689 | | /* Undescribed code point should be U+FFFD |
2690 | | * (replacement character). */ |
2691 | 3.49k | *pwc = UNICODE_R_CHAR; |
2692 | 3.49k | return (((int)(utf16 - s)) * -1); |
2693 | 3.49k | } |
2694 | 135k | *pwc = uc; |
2695 | 135k | return ((int)(utf16 - s)); |
2696 | 138k | } |
2697 | | |
2698 | | static size_t |
2699 | | unicode_to_utf16be(char *p, size_t remaining, uint32_t uc) |
2700 | 10.8k | { |
2701 | 10.8k | char *utf16 = p; |
2702 | | |
2703 | 10.8k | if (uc > 0xffff) { |
2704 | | /* We have a code point that won't fit into a |
2705 | | * wchar_t; convert it to a surrogate pair. */ |
2706 | 42 | if (remaining < 4) |
2707 | 0 | return (0); |
2708 | 42 | uc -= 0x10000; |
2709 | 42 | archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); |
2710 | 42 | archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00); |
2711 | 42 | return (4); |
2712 | 10.7k | } else { |
2713 | 10.7k | if (remaining < 2) |
2714 | 0 | return (0); |
2715 | 10.7k | archive_be16enc(utf16, (uint16_t)uc); |
2716 | 10.7k | return (2); |
2717 | 10.7k | } |
2718 | 10.8k | } |
2719 | | |
2720 | | static size_t |
2721 | | unicode_to_utf16le(char *p, size_t remaining, uint32_t uc) |
2722 | 5.05k | { |
2723 | 5.05k | char *utf16 = p; |
2724 | | |
2725 | 5.05k | if (uc > 0xffff) { |
2726 | | /* We have a code point that won't fit into a |
2727 | | * wchar_t; convert it to a surrogate pair. */ |
2728 | 82 | if (remaining < 4) |
2729 | 0 | return (0); |
2730 | 82 | uc -= 0x10000; |
2731 | 82 | archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800); |
2732 | 82 | archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00); |
2733 | 82 | return (4); |
2734 | 4.97k | } else { |
2735 | 4.97k | if (remaining < 2) |
2736 | 0 | return (0); |
2737 | 4.97k | archive_le16enc(utf16, (uint16_t)uc); |
2738 | 4.97k | return (2); |
2739 | 4.97k | } |
2740 | 5.05k | } |
2741 | | |
2742 | | /* |
2743 | | * Append new UTF-8 string to existing UTF-8 string. |
2744 | | * Existing string is assumed to already be in proper form; |
2745 | | * the new string will have invalid sequences replaced and |
2746 | | * surrogate pairs canonicalized. |
2747 | | */ |
2748 | | static int |
2749 | | strncat_from_utf8_to_utf8(struct archive_string *as, const void *_src, |
2750 | | size_t len, struct archive_string_conv *sc) |
2751 | 0 | { |
2752 | 0 | int ret = 0; |
2753 | 0 | const char *src = _src; |
2754 | 0 | (void)sc; /* UNUSED */ |
2755 | | |
2756 | | /* Pre-extend the destination */ |
2757 | 0 | if (archive_string_ensure(as, as->length + len + 1) == NULL) |
2758 | 0 | return (-1); |
2759 | | |
2760 | | /* Invariant: src points to the first UTF8 byte that hasn't |
2761 | | * been copied to the destination `as`. */ |
2762 | 0 | for (;;) { |
2763 | 0 | int n; |
2764 | 0 | uint32_t uc; |
2765 | 0 | const char *e = src; |
2766 | | |
2767 | | /* Skip UTF-8 sequences until we reach end-of-string or |
2768 | | * a code point that needs conversion. */ |
2769 | 0 | while ((n = utf8_to_unicode(&uc, e, len)) > 0) { |
2770 | 0 | e += n; |
2771 | 0 | len -= n; |
2772 | 0 | } |
2773 | | /* Copy the part that doesn't need conversion */ |
2774 | 0 | if (e > src) { |
2775 | 0 | if (archive_string_append(as, src, e - src) == NULL) |
2776 | 0 | return (-1); |
2777 | 0 | src = e; |
2778 | 0 | } |
2779 | | |
2780 | 0 | if (n == 0) { |
2781 | | /* We reached end-of-string */ |
2782 | 0 | return (ret); |
2783 | 0 | } else { |
2784 | | /* Next code point needs conversion */ |
2785 | 0 | char t[4]; |
2786 | 0 | size_t w; |
2787 | | |
2788 | | /* Try decoding a surrogate pair */ |
2789 | 0 | if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) { |
2790 | 0 | n = cesu8_to_unicode(&uc, src, len); |
2791 | 0 | } |
2792 | | /* Not a (valid) surrogate, so use a replacement char */ |
2793 | 0 | if (n < 0) { |
2794 | 0 | ret = -1; /* Return -1 if we used any replacement */ |
2795 | 0 | n *= -1; |
2796 | 0 | } |
2797 | | /* Consume converted code point */ |
2798 | 0 | src += n; |
2799 | 0 | len -= n; |
2800 | | /* Convert and append new UTF-8 sequence. */ |
2801 | 0 | w = unicode_to_utf8(t, sizeof(t), uc); |
2802 | 0 | if (archive_string_append(as, t, w) == NULL) |
2803 | 0 | return (-1); |
2804 | 0 | } |
2805 | 0 | } |
2806 | 0 | } |
2807 | | |
2808 | | static int |
2809 | | archive_string_append_unicode(struct archive_string *as, const void *_p, |
2810 | | size_t len, struct archive_string_conv *sc) |
2811 | 0 | { |
2812 | 0 | const char *s; |
2813 | 0 | char *p, *endp; |
2814 | 0 | uint32_t uc; |
2815 | 0 | size_t w; |
2816 | 0 | size_t ts, tm; |
2817 | 0 | int n, ret = 0; |
2818 | 0 | int (*parse)(uint32_t *, const char *, size_t); |
2819 | 0 | size_t (*unparse)(char *, size_t, uint32_t); |
2820 | |
|
2821 | 0 | if (sc->flag & SCONV_TO_UTF16BE) { |
2822 | 0 | unparse = unicode_to_utf16be; |
2823 | 0 | ts = 2; |
2824 | 0 | } else if (sc->flag & SCONV_TO_UTF16LE) { |
2825 | 0 | unparse = unicode_to_utf16le; |
2826 | 0 | ts = 2; |
2827 | 0 | } else if (sc->flag & SCONV_TO_UTF8) { |
2828 | 0 | unparse = unicode_to_utf8; |
2829 | 0 | ts = 1; |
2830 | 0 | } else { |
2831 | | /* |
2832 | | * This case is going to be converted to another |
2833 | | * character-set through iconv. |
2834 | | */ |
2835 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) { |
2836 | 0 | unparse = unicode_to_utf16be; |
2837 | 0 | ts = 2; |
2838 | 0 | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
2839 | 0 | unparse = unicode_to_utf16le; |
2840 | 0 | ts = 2; |
2841 | 0 | } else { |
2842 | 0 | unparse = unicode_to_utf8; |
2843 | 0 | ts = 1; |
2844 | 0 | } |
2845 | 0 | } |
2846 | |
|
2847 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) { |
2848 | 0 | parse = utf16be_to_unicode; |
2849 | 0 | tm = 1; |
2850 | 0 | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
2851 | 0 | parse = utf16le_to_unicode; |
2852 | 0 | tm = 1; |
2853 | 0 | } else { |
2854 | 0 | parse = cesu8_to_unicode; |
2855 | 0 | tm = ts; |
2856 | 0 | } |
2857 | |
|
2858 | 0 | if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) |
2859 | 0 | return (-1); |
2860 | | |
2861 | 0 | s = (const char *)_p; |
2862 | 0 | p = as->s + as->length; |
2863 | 0 | endp = as->s + as->buffer_length - ts; |
2864 | 0 | while ((n = parse(&uc, s, len)) != 0) { |
2865 | 0 | if (n < 0) { |
2866 | | /* Use a replaced unicode character. */ |
2867 | 0 | n *= -1; |
2868 | 0 | ret = -1; |
2869 | 0 | } |
2870 | 0 | s += n; |
2871 | 0 | len -= n; |
2872 | 0 | while ((w = unparse(p, endp - p, uc)) == 0) { |
2873 | | /* There is not enough output buffer so |
2874 | | * we have to expand it. */ |
2875 | 0 | as->length = p - as->s; |
2876 | 0 | if (archive_string_ensure(as, |
2877 | 0 | as->buffer_length + len * tm + ts) == NULL) |
2878 | 0 | return (-1); |
2879 | 0 | p = as->s + as->length; |
2880 | 0 | endp = as->s + as->buffer_length - ts; |
2881 | 0 | } |
2882 | 0 | p += w; |
2883 | 0 | } |
2884 | 0 | as->length = p - as->s; |
2885 | 0 | as->s[as->length] = '\0'; |
2886 | 0 | if (ts == 2) |
2887 | 0 | as->s[as->length+1] = '\0'; |
2888 | 0 | return (ret); |
2889 | 0 | } |
2890 | | |
2891 | | /* |
2892 | | * Following Constants for Hangul compositions this information comes from |
2893 | | * Unicode Standard Annex #15 http://unicode.org/reports/tr15/ |
2894 | | */ |
2895 | 63.2k | #define HC_SBASE 0xAC00 |
2896 | 64.6k | #define HC_LBASE 0x1100 |
2897 | 1.38k | #define HC_VBASE 0x1161 |
2898 | 872 | #define HC_TBASE 0x11A7 |
2899 | 27.5k | #define HC_LCOUNT 19 |
2900 | 9.02k | #define HC_VCOUNT 21 |
2901 | 14.5k | #define HC_TCOUNT 28 |
2902 | 8.24k | #define HC_NCOUNT (HC_VCOUNT * HC_TCOUNT) |
2903 | 71.4k | #define HC_SCOUNT (HC_LCOUNT * HC_NCOUNT) |
2904 | | |
2905 | | static uint32_t |
2906 | | get_nfc(uint32_t uc, uint32_t uc2) |
2907 | 143k | { |
2908 | 143k | int t, b; |
2909 | | |
2910 | 143k | t = 0; |
2911 | 143k | b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1; |
2912 | 1.45M | while (b >= t) { |
2913 | 1.33M | int m = (t + b) / 2; |
2914 | 1.33M | if (u_composition_table[m].cp1 < uc) |
2915 | 673k | t = m + 1; |
2916 | 666k | else if (u_composition_table[m].cp1 > uc) |
2917 | 543k | b = m - 1; |
2918 | 123k | else if (u_composition_table[m].cp2 < uc2) |
2919 | 62.9k | t = m + 1; |
2920 | 60.8k | else if (u_composition_table[m].cp2 > uc2) |
2921 | 32.4k | b = m - 1; |
2922 | 28.3k | else |
2923 | 28.3k | return (u_composition_table[m].nfc); |
2924 | 1.33M | } |
2925 | 115k | return (0); |
2926 | 143k | } |
2927 | | |
2928 | 132k | #define FDC_MAX 10 /* The maximum number of Following Decomposable |
2929 | | * Characters. */ |
2930 | | |
2931 | | /* |
2932 | | * Update first code point. |
2933 | | */ |
2934 | 28.6k | #define UPDATE_UC(new_uc) do { \ |
2935 | 28.6k | uc = new_uc; \ |
2936 | 28.6k | ucptr = NULL; \ |
2937 | 28.6k | } while (0) |
2938 | | |
2939 | | /* |
2940 | | * Replace first code point with second code point. |
2941 | | */ |
2942 | 92.5M | #define REPLACE_UC_WITH_UC2() do { \ |
2943 | 92.5M | uc = uc2; \ |
2944 | 92.5M | ucptr = uc2ptr; \ |
2945 | 92.5M | n = n2; \ |
2946 | 92.5M | } while (0) |
2947 | | |
2948 | 39.7k | #define EXPAND_BUFFER() do { \ |
2949 | 39.7k | as->length = p - as->s; \ |
2950 | 39.7k | if (archive_string_ensure(as, \ |
2951 | 39.7k | as->buffer_length + len * tm + ts) == NULL)\ |
2952 | 39.7k | return (-1); \ |
2953 | 39.7k | p = as->s + as->length; \ |
2954 | 39.7k | endp = as->s + as->buffer_length - ts; \ |
2955 | 39.7k | } while (0) |
2956 | | |
2957 | 698k | #define UNPARSE(p, endp, uc) do { \ |
2958 | 670k | while ((w = unparse(p, (endp) - (p), uc)) == 0) {\ |
2959 | 9.80k | EXPAND_BUFFER(); \ |
2960 | 9.80k | } \ |
2961 | 661k | p += w; \ |
2962 | 661k | } while (0) |
2963 | | |
2964 | | /* |
2965 | | * Write first code point. |
2966 | | * If the code point has not be changed from its original code, |
2967 | | * this just copies it from its original buffer pointer. |
2968 | | * If not, this converts it to UTF-8 byte sequence and copies it. |
2969 | | */ |
2970 | 92.8M | #define WRITE_UC() do { \ |
2971 | 92.8M | if (ucptr) { \ |
2972 | 92.7M | if (p + n > endp) \ |
2973 | 92.7M | EXPAND_BUFFER(); \ |
2974 | 92.7M | switch (n) { \ |
2975 | 4.32k | case 4: \ |
2976 | 4.32k | *p++ = *ucptr++; \ |
2977 | 4.32k | /* FALL THROUGH */ \ |
2978 | 32.6k | case 3: \ |
2979 | 32.6k | *p++ = *ucptr++; \ |
2980 | 32.6k | /* FALL THROUGH */ \ |
2981 | 182k | case 2: \ |
2982 | 182k | *p++ = *ucptr++; \ |
2983 | 182k | /* FALL THROUGH */ \ |
2984 | 92.7M | case 1: \ |
2985 | 92.7M | *p++ = *ucptr; \ |
2986 | 92.7M | break; \ |
2987 | 92.7M | } \ |
2988 | 92.7M | ucptr = NULL; \ |
2989 | 92.7M | } else { \ |
2990 | 29.9k | UNPARSE(p, endp, uc); \ |
2991 | 29.9k | } \ |
2992 | 92.8M | } while (0) |
2993 | | |
2994 | | /* |
2995 | | * Collect following decomposable code points. |
2996 | | */ |
2997 | 41.9k | #define COLLECT_CPS(start) do { \ |
2998 | 41.9k | int _i; \ |
2999 | 89.3k | for (_i = start; _i < FDC_MAX ; _i++) { \ |
3000 | 88.1k | nx = parse(&ucx[_i], s, len); \ |
3001 | 88.1k | if (nx <= 0) \ |
3002 | 88.1k | break; \ |
3003 | 88.1k | cx = CCC(ucx[_i]); \ |
3004 | 78.1k | if (cl >= cx && cl != 228 && cx != 228)\ |
3005 | 78.1k | break; \ |
3006 | 78.1k | s += nx; \ |
3007 | 47.3k | len -= nx; \ |
3008 | 47.3k | cl = cx; \ |
3009 | 47.3k | ccx[_i] = cx; \ |
3010 | 47.3k | } \ |
3011 | 41.9k | if (_i >= FDC_MAX) { \ |
3012 | 1.13k | ret = -1; \ |
3013 | 1.13k | ucx_size = FDC_MAX; \ |
3014 | 1.13k | } else \ |
3015 | 41.9k | ucx_size = _i; \ |
3016 | 41.9k | } while (0) |
3017 | | |
3018 | | /* |
3019 | | * Normalize UTF-8/UTF-16BE characters to Form C and copy the result. |
3020 | | * |
3021 | | * TODO: Convert composition exclusions, which are never converted |
3022 | | * from NFC,NFD,NFKC and NFKD, to Form C. |
3023 | | */ |
3024 | | static int |
3025 | | archive_string_normalize_C(struct archive_string *as, const void *_p, |
3026 | | size_t len, struct archive_string_conv *sc) |
3027 | 62.9k | { |
3028 | 62.9k | const char *s = (const char *)_p; |
3029 | 62.9k | char *p, *endp; |
3030 | 62.9k | uint32_t uc, uc2; |
3031 | 62.9k | size_t w; |
3032 | 62.9k | int always_replace, n, n2, ret = 0, spair, ts, tm; |
3033 | 62.9k | int (*parse)(uint32_t *, const char *, size_t); |
3034 | 62.9k | size_t (*unparse)(char *, size_t, uint32_t); |
3035 | | |
3036 | 62.9k | always_replace = 1; |
3037 | 62.9k | ts = 1;/* text size. */ |
3038 | 62.9k | if (sc->flag & SCONV_TO_UTF16BE) { |
3039 | 0 | unparse = unicode_to_utf16be; |
3040 | 0 | ts = 2; |
3041 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) |
3042 | 0 | always_replace = 0; |
3043 | 62.9k | } else if (sc->flag & SCONV_TO_UTF16LE) { |
3044 | 0 | unparse = unicode_to_utf16le; |
3045 | 0 | ts = 2; |
3046 | 0 | if (sc->flag & SCONV_FROM_UTF16LE) |
3047 | 0 | always_replace = 0; |
3048 | 62.9k | } else if (sc->flag & SCONV_TO_UTF8) { |
3049 | 0 | unparse = unicode_to_utf8; |
3050 | 0 | if (sc->flag & SCONV_FROM_UTF8) |
3051 | 0 | always_replace = 0; |
3052 | 62.9k | } else { |
3053 | | /* |
3054 | | * This case is going to be converted to another |
3055 | | * character-set through iconv. |
3056 | | */ |
3057 | 62.9k | always_replace = 0; |
3058 | 62.9k | if (sc->flag & SCONV_FROM_UTF16BE) { |
3059 | 1.74k | unparse = unicode_to_utf16be; |
3060 | 1.74k | ts = 2; |
3061 | 61.2k | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
3062 | 938 | unparse = unicode_to_utf16le; |
3063 | 938 | ts = 2; |
3064 | 60.2k | } else { |
3065 | 60.2k | unparse = unicode_to_utf8; |
3066 | 60.2k | } |
3067 | 62.9k | } |
3068 | | |
3069 | 62.9k | if (sc->flag & SCONV_FROM_UTF16BE) { |
3070 | 1.74k | parse = utf16be_to_unicode; |
3071 | 1.74k | tm = 1; |
3072 | 1.74k | spair = 4;/* surrogate pair size in UTF-16. */ |
3073 | 61.2k | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
3074 | 938 | parse = utf16le_to_unicode; |
3075 | 938 | tm = 1; |
3076 | 938 | spair = 4;/* surrogate pair size in UTF-16. */ |
3077 | 60.2k | } else { |
3078 | 60.2k | parse = cesu8_to_unicode; |
3079 | 60.2k | tm = ts; |
3080 | 60.2k | spair = 6;/* surrogate pair size in UTF-8. */ |
3081 | 60.2k | } |
3082 | | |
3083 | 62.9k | if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) |
3084 | 0 | return (-1); |
3085 | | |
3086 | 62.9k | p = as->s + as->length; |
3087 | 62.9k | endp = as->s + as->buffer_length - ts; |
3088 | 629k | while ((n = parse(&uc, s, len)) != 0) { |
3089 | 620k | const char *ucptr, *uc2ptr; |
3090 | | |
3091 | 620k | if (n < 0) { |
3092 | | /* Use a replaced unicode character. */ |
3093 | 408k | UNPARSE(p, endp, uc); |
3094 | 408k | s += n*-1; |
3095 | 408k | len -= n*-1; |
3096 | 408k | ret = -1; |
3097 | 408k | continue; |
3098 | 408k | } else if (n == spair || always_replace) |
3099 | | /* uc is converted from a surrogate pair. |
3100 | | * this should be treated as a changed code. */ |
3101 | 833 | ucptr = NULL; |
3102 | 211k | else |
3103 | 211k | ucptr = s; |
3104 | 212k | s += n; |
3105 | 212k | len -= n; |
3106 | | |
3107 | | /* Read second code point. */ |
3108 | 92.8M | while ((n2 = parse(&uc2, s, len)) > 0) { |
3109 | 92.6M | uint32_t ucx[FDC_MAX]; |
3110 | 92.6M | int ccx[FDC_MAX]; |
3111 | 92.6M | int cl, cx, i, nx, ucx_size; |
3112 | 92.6M | int LIndex,SIndex; |
3113 | 92.6M | uint32_t nfc; |
3114 | | |
3115 | 92.6M | if (n2 == spair || always_replace) |
3116 | | /* uc2 is converted from a surrogate pair. |
3117 | | * this should be treated as a changed code. */ |
3118 | 491 | uc2ptr = NULL; |
3119 | 92.6M | else |
3120 | 92.6M | uc2ptr = s; |
3121 | 92.6M | s += n2; |
3122 | 92.6M | len -= n2; |
3123 | | |
3124 | | /* |
3125 | | * If current second code point is out of decomposable |
3126 | | * code points, finding compositions is unneeded. |
3127 | | */ |
3128 | 92.6M | if (!IS_DECOMPOSABLE_BLOCK(uc2)) { |
3129 | 92.5M | WRITE_UC(); |
3130 | 92.5M | REPLACE_UC_WITH_UC2(); |
3131 | 92.5M | continue; |
3132 | 92.5M | } |
3133 | | |
3134 | | /* |
3135 | | * Try to combine current code points. |
3136 | | */ |
3137 | | /* |
3138 | | * We have to combine Hangul characters according to |
3139 | | * http://uniicode.org/reports/tr15/#Hangul |
3140 | | */ |
3141 | 64.6k | if (0 <= (LIndex = uc - HC_LBASE) && |
3142 | 19.2k | LIndex < HC_LCOUNT) { |
3143 | | /* |
3144 | | * Hangul Composition. |
3145 | | * 1. Two current code points are L and V. |
3146 | | */ |
3147 | 1.38k | int VIndex = uc2 - HC_VBASE; |
3148 | 1.38k | if (0 <= VIndex && VIndex < HC_VCOUNT) { |
3149 | | /* Make syllable of form LV. */ |
3150 | 282 | UPDATE_UC(HC_SBASE + |
3151 | 282 | (LIndex * HC_VCOUNT + VIndex) * |
3152 | 282 | HC_TCOUNT); |
3153 | 1.09k | } else { |
3154 | 1.09k | WRITE_UC(); |
3155 | 1.09k | REPLACE_UC_WITH_UC2(); |
3156 | 1.09k | } |
3157 | 1.38k | continue; |
3158 | 63.2k | } else if (0 <= (SIndex = uc - HC_SBASE) && |
3159 | 8.24k | SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) { |
3160 | | /* |
3161 | | * Hangul Composition. |
3162 | | * 2. Two current code points are LV and T. |
3163 | | */ |
3164 | 872 | int TIndex = uc2 - HC_TBASE; |
3165 | 872 | if (0 < TIndex && TIndex < HC_TCOUNT) { |
3166 | | /* Make syllable of form LVT. */ |
3167 | 15 | UPDATE_UC(uc + TIndex); |
3168 | 857 | } else { |
3169 | 857 | WRITE_UC(); |
3170 | 857 | REPLACE_UC_WITH_UC2(); |
3171 | 857 | } |
3172 | 872 | continue; |
3173 | 62.3k | } else if ((nfc = get_nfc(uc, uc2)) != 0) { |
3174 | | /* A composition to current code points |
3175 | | * is found. */ |
3176 | 1.14k | UPDATE_UC(nfc); |
3177 | 1.14k | continue; |
3178 | 61.2k | } else if ((cl = CCC(uc2)) == 0) { |
3179 | | /* Clearly 'uc2' the second code point is not |
3180 | | * a decomposable code. */ |
3181 | 23.7k | WRITE_UC(); |
3182 | 23.7k | REPLACE_UC_WITH_UC2(); |
3183 | 23.7k | continue; |
3184 | 23.7k | } |
3185 | | |
3186 | | /* |
3187 | | * Collect following decomposable code points. |
3188 | | */ |
3189 | 37.4k | cx = 0; |
3190 | 37.4k | ucx[0] = uc2; |
3191 | 37.4k | ccx[0] = cl; |
3192 | 37.4k | COLLECT_CPS(1); |
3193 | | |
3194 | | /* |
3195 | | * Find a composed code in the collected code points. |
3196 | | */ |
3197 | 37.4k | i = 1; |
3198 | 118k | while (i < ucx_size) { |
3199 | 81.2k | int j; |
3200 | | |
3201 | 81.2k | if ((nfc = get_nfc(uc, ucx[i])) == 0) { |
3202 | 53.9k | i++; |
3203 | 53.9k | continue; |
3204 | 53.9k | } |
3205 | | |
3206 | | /* |
3207 | | * nfc is composed of uc and ucx[i]. |
3208 | | */ |
3209 | 27.2k | UPDATE_UC(nfc); |
3210 | | |
3211 | | /* |
3212 | | * Remove ucx[i] by shifting |
3213 | | * following code points. |
3214 | | */ |
3215 | 29.6k | for (j = i; j+1 < ucx_size; j++) { |
3216 | 2.38k | ucx[j] = ucx[j+1]; |
3217 | 2.38k | ccx[j] = ccx[j+1]; |
3218 | 2.38k | } |
3219 | 27.2k | ucx_size --; |
3220 | | |
3221 | | /* |
3222 | | * Collect following code points blocked |
3223 | | * by ucx[i] the removed code point. |
3224 | | */ |
3225 | 27.2k | if (ucx_size > 0 && i == ucx_size && |
3226 | 26.6k | nx > 0 && cx == cl) { |
3227 | 4.48k | cl = ccx[ucx_size-1]; |
3228 | 4.48k | COLLECT_CPS(ucx_size); |
3229 | 4.48k | } |
3230 | | /* |
3231 | | * Restart finding a composed code with |
3232 | | * the updated uc from the top of the |
3233 | | * collected code points. |
3234 | | */ |
3235 | 27.2k | i = 0; |
3236 | 27.2k | } |
3237 | | |
3238 | | /* |
3239 | | * Apparently the current code points are not |
3240 | | * decomposed characters or already composed. |
3241 | | */ |
3242 | 37.4k | WRITE_UC(); |
3243 | 95.1k | for (i = 0; i < ucx_size; i++) |
3244 | 57.6k | UNPARSE(p, endp, ucx[i]); |
3245 | | |
3246 | | /* |
3247 | | * Flush out remaining canonical combining characters. |
3248 | | */ |
3249 | 37.4k | if (nx > 0 && cx == cl && len > 0) { |
3250 | 46.7k | while ((nx = parse(&ucx[0], s, len)) |
3251 | 46.7k | > 0) { |
3252 | 44.9k | cx = CCC(ucx[0]); |
3253 | 44.9k | if (cl > cx) |
3254 | 391 | break; |
3255 | 44.5k | s += nx; |
3256 | 44.5k | len -= nx; |
3257 | 44.5k | cl = cx; |
3258 | 44.5k | UNPARSE(p, endp, ucx[0]); |
3259 | 44.5k | } |
3260 | 2.15k | } |
3261 | 37.4k | break; |
3262 | 37.4k | } |
3263 | 212k | if (n2 < 0) { |
3264 | 120k | WRITE_UC(); |
3265 | | /* Use a replaced unicode character. */ |
3266 | 120k | UNPARSE(p, endp, uc2); |
3267 | 120k | s += n2*-1; |
3268 | 120k | len -= n2*-1; |
3269 | 120k | ret = -1; |
3270 | 120k | continue; |
3271 | 120k | } else if (n2 == 0) { |
3272 | 54.0k | WRITE_UC(); |
3273 | 54.0k | break; |
3274 | 54.0k | } |
3275 | 212k | } |
3276 | 62.9k | as->length = p - as->s; |
3277 | 62.9k | as->s[as->length] = '\0'; |
3278 | 62.9k | if (ts == 2) |
3279 | 2.68k | as->s[as->length+1] = '\0'; |
3280 | 62.9k | return (ret); |
3281 | 62.9k | } |
3282 | | |
3283 | | static int |
3284 | | get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc) |
3285 | 0 | { |
3286 | 0 | int t, b; |
3287 | | |
3288 | | /* |
3289 | | * These are not converted to NFD on Mac OS. |
3290 | | */ |
3291 | 0 | if ((uc >= 0x2000 && uc <= 0x2FFF) || |
3292 | 0 | (uc >= 0xF900 && uc <= 0xFAFF) || |
3293 | 0 | (uc >= 0x2F800 && uc <= 0x2FAFF)) |
3294 | 0 | return (0); |
3295 | | /* |
3296 | | * Those code points are not converted to NFD on Mac OS. |
3297 | | * I do not know the reason because it is undocumented. |
3298 | | * NFC NFD |
3299 | | * 1109A ==> 11099 110BA |
3300 | | * 1109C ==> 1109B 110BA |
3301 | | * 110AB ==> 110A5 110BA |
3302 | | */ |
3303 | 0 | if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB) |
3304 | 0 | return (0); |
3305 | | |
3306 | 0 | t = 0; |
3307 | 0 | b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1; |
3308 | 0 | while (b >= t) { |
3309 | 0 | int m = (t + b) / 2; |
3310 | 0 | if (u_decomposition_table[m].nfc < uc) |
3311 | 0 | t = m + 1; |
3312 | 0 | else if (u_decomposition_table[m].nfc > uc) |
3313 | 0 | b = m - 1; |
3314 | 0 | else { |
3315 | 0 | *cp1 = u_decomposition_table[m].cp1; |
3316 | 0 | *cp2 = u_decomposition_table[m].cp2; |
3317 | 0 | return (1); |
3318 | 0 | } |
3319 | 0 | } |
3320 | 0 | return (0); |
3321 | 0 | } |
3322 | | |
3323 | 0 | #define REPLACE_UC_WITH(cp) do { \ |
3324 | 0 | uc = cp; \ |
3325 | 0 | ucptr = NULL; \ |
3326 | 0 | } while (0) |
3327 | | |
3328 | | /* |
3329 | | * Normalize UTF-8 characters to Form D and copy the result. |
3330 | | */ |
3331 | | static int |
3332 | | archive_string_normalize_D(struct archive_string *as, const void *_p, |
3333 | | size_t len, struct archive_string_conv *sc) |
3334 | 0 | { |
3335 | 0 | const char *s = (const char *)_p; |
3336 | 0 | char *p, *endp; |
3337 | 0 | uint32_t uc, uc2; |
3338 | 0 | size_t w; |
3339 | 0 | int always_replace, n, n2, ret = 0, spair, ts, tm; |
3340 | 0 | int (*parse)(uint32_t *, const char *, size_t); |
3341 | 0 | size_t (*unparse)(char *, size_t, uint32_t); |
3342 | |
|
3343 | 0 | always_replace = 1; |
3344 | 0 | ts = 1;/* text size. */ |
3345 | 0 | if (sc->flag & SCONV_TO_UTF16BE) { |
3346 | 0 | unparse = unicode_to_utf16be; |
3347 | 0 | ts = 2; |
3348 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) |
3349 | 0 | always_replace = 0; |
3350 | 0 | } else if (sc->flag & SCONV_TO_UTF16LE) { |
3351 | 0 | unparse = unicode_to_utf16le; |
3352 | 0 | ts = 2; |
3353 | 0 | if (sc->flag & SCONV_FROM_UTF16LE) |
3354 | 0 | always_replace = 0; |
3355 | 0 | } else if (sc->flag & SCONV_TO_UTF8) { |
3356 | 0 | unparse = unicode_to_utf8; |
3357 | 0 | if (sc->flag & SCONV_FROM_UTF8) |
3358 | 0 | always_replace = 0; |
3359 | 0 | } else { |
3360 | | /* |
3361 | | * This case is going to be converted to another |
3362 | | * character-set through iconv. |
3363 | | */ |
3364 | 0 | always_replace = 0; |
3365 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) { |
3366 | 0 | unparse = unicode_to_utf16be; |
3367 | 0 | ts = 2; |
3368 | 0 | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
3369 | 0 | unparse = unicode_to_utf16le; |
3370 | 0 | ts = 2; |
3371 | 0 | } else { |
3372 | 0 | unparse = unicode_to_utf8; |
3373 | 0 | } |
3374 | 0 | } |
3375 | |
|
3376 | 0 | if (sc->flag & SCONV_FROM_UTF16BE) { |
3377 | 0 | parse = utf16be_to_unicode; |
3378 | 0 | tm = 1; |
3379 | 0 | spair = 4;/* surrogate pair size in UTF-16. */ |
3380 | 0 | } else if (sc->flag & SCONV_FROM_UTF16LE) { |
3381 | 0 | parse = utf16le_to_unicode; |
3382 | 0 | tm = 1; |
3383 | 0 | spair = 4;/* surrogate pair size in UTF-16. */ |
3384 | 0 | } else { |
3385 | 0 | parse = cesu8_to_unicode; |
3386 | 0 | tm = ts; |
3387 | 0 | spair = 6;/* surrogate pair size in UTF-8. */ |
3388 | 0 | } |
3389 | |
|
3390 | 0 | if (archive_string_ensure(as, as->length + len * tm + ts) == NULL) |
3391 | 0 | return (-1); |
3392 | | |
3393 | 0 | p = as->s + as->length; |
3394 | 0 | endp = as->s + as->buffer_length - ts; |
3395 | 0 | while ((n = parse(&uc, s, len)) != 0) { |
3396 | 0 | const char *ucptr; |
3397 | 0 | uint32_t cp1, cp2; |
3398 | 0 | int SIndex; |
3399 | 0 | struct { |
3400 | 0 | uint32_t uc; |
3401 | 0 | int ccc; |
3402 | 0 | } fdc[FDC_MAX]; |
3403 | 0 | int fdi, fdj; |
3404 | 0 | int ccc; |
3405 | |
|
3406 | 0 | check_first_code: |
3407 | 0 | if (n < 0) { |
3408 | | /* Use a replaced unicode character. */ |
3409 | 0 | UNPARSE(p, endp, uc); |
3410 | 0 | s += n*-1; |
3411 | 0 | len -= n*-1; |
3412 | 0 | ret = -1; |
3413 | 0 | continue; |
3414 | 0 | } else if (n == spair || always_replace) |
3415 | | /* uc is converted from a surrogate pair. |
3416 | | * this should be treated as a changed code. */ |
3417 | 0 | ucptr = NULL; |
3418 | 0 | else |
3419 | 0 | ucptr = s; |
3420 | 0 | s += n; |
3421 | 0 | len -= n; |
3422 | | |
3423 | | /* Hangul Decomposition. */ |
3424 | 0 | if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) { |
3425 | 0 | int L = HC_LBASE + SIndex / HC_NCOUNT; |
3426 | 0 | int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT; |
3427 | 0 | int T = HC_TBASE + SIndex % HC_TCOUNT; |
3428 | |
|
3429 | 0 | REPLACE_UC_WITH(L); |
3430 | 0 | WRITE_UC(); |
3431 | 0 | REPLACE_UC_WITH(V); |
3432 | 0 | WRITE_UC(); |
3433 | 0 | if (T != HC_TBASE) { |
3434 | 0 | REPLACE_UC_WITH(T); |
3435 | 0 | WRITE_UC(); |
3436 | 0 | } |
3437 | 0 | continue; |
3438 | 0 | } |
3439 | 0 | if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) { |
3440 | 0 | WRITE_UC(); |
3441 | 0 | continue; |
3442 | 0 | } |
3443 | | |
3444 | 0 | fdi = 0; |
3445 | 0 | while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) { |
3446 | 0 | int k; |
3447 | |
|
3448 | 0 | for (k = fdi; k > 0; k--) |
3449 | 0 | fdc[k] = fdc[k-1]; |
3450 | 0 | fdc[0].ccc = CCC(cp2); |
3451 | 0 | fdc[0].uc = cp2; |
3452 | 0 | fdi++; |
3453 | 0 | REPLACE_UC_WITH(cp1); |
3454 | 0 | } |
3455 | | |
3456 | | /* Read following code points. */ |
3457 | 0 | while ((n2 = parse(&uc2, s, len)) > 0 && |
3458 | 0 | (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) { |
3459 | 0 | int j, k; |
3460 | |
|
3461 | 0 | s += n2; |
3462 | 0 | len -= n2; |
3463 | 0 | for (j = 0; j < fdi; j++) { |
3464 | 0 | if (fdc[j].ccc > ccc) |
3465 | 0 | break; |
3466 | 0 | } |
3467 | 0 | if (j < fdi) { |
3468 | 0 | for (k = fdi; k > j; k--) |
3469 | 0 | fdc[k] = fdc[k-1]; |
3470 | 0 | fdc[j].ccc = ccc; |
3471 | 0 | fdc[j].uc = uc2; |
3472 | 0 | } else { |
3473 | 0 | fdc[fdi].ccc = ccc; |
3474 | 0 | fdc[fdi].uc = uc2; |
3475 | 0 | } |
3476 | 0 | fdi++; |
3477 | 0 | } |
3478 | |
|
3479 | 0 | WRITE_UC(); |
3480 | 0 | for (fdj = 0; fdj < fdi; fdj++) { |
3481 | 0 | REPLACE_UC_WITH(fdc[fdj].uc); |
3482 | 0 | WRITE_UC(); |
3483 | 0 | } |
3484 | | |
3485 | 0 | if (n2 == 0) |
3486 | 0 | break; |
3487 | 0 | REPLACE_UC_WITH(uc2); |
3488 | 0 | n = n2; |
3489 | 0 | goto check_first_code; |
3490 | 0 | } |
3491 | 0 | as->length = p - as->s; |
3492 | 0 | as->s[as->length] = '\0'; |
3493 | 0 | if (ts == 2) |
3494 | 0 | as->s[as->length+1] = '\0'; |
3495 | 0 | return (ret); |
3496 | 0 | } |
3497 | | |
3498 | | /* |
3499 | | * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption |
3500 | | * that WCS is Unicode. It is true for several platforms but some are false. |
3501 | | * And then people who did not use UTF-8 locale on the non Unicode WCS |
3502 | | * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those |
3503 | | * now cannot get right filename from libarchive 3.x and later since we |
3504 | | * fixed the wrong assumption and it is incompatible to older its versions. |
3505 | | * So we provide special option, "compat-2x.x", for resolving it. |
3506 | | * That option enable the string conversion of libarchive 2.x. |
3507 | | * |
3508 | | * Translates the wrong UTF-8 string made by libarchive 2.x into current |
3509 | | * locale character set and appends to the archive_string. |
3510 | | * Note: returns -1 if conversion fails. |
3511 | | */ |
3512 | | static int |
3513 | | strncat_from_utf8_libarchive2(struct archive_string *as, |
3514 | | const void *_p, size_t len, struct archive_string_conv *sc) |
3515 | 0 | { |
3516 | 0 | const char *s; |
3517 | 0 | int n; |
3518 | 0 | char *p; |
3519 | 0 | char *end; |
3520 | 0 | uint32_t unicode; |
3521 | 0 | #if HAVE_WCRTOMB |
3522 | 0 | mbstate_t shift_state; |
3523 | |
|
3524 | 0 | memset(&shift_state, 0, sizeof(shift_state)); |
3525 | | #else |
3526 | | /* Clear the shift state before starting. */ |
3527 | | wctomb(NULL, L'\0'); |
3528 | | #endif |
3529 | 0 | (void)sc; /* UNUSED */ |
3530 | | /* |
3531 | | * Allocate buffer for MBS. |
3532 | | * We need this allocation here since it is possible that |
3533 | | * as->s is still NULL. |
3534 | | */ |
3535 | 0 | if (archive_string_ensure(as, as->length + len + 1) == NULL) |
3536 | 0 | return (-1); |
3537 | | |
3538 | 0 | s = (const char *)_p; |
3539 | 0 | p = as->s + as->length; |
3540 | 0 | end = as->s + as->buffer_length - MB_CUR_MAX -1; |
3541 | 0 | while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) { |
3542 | 0 | wchar_t wc; |
3543 | |
|
3544 | 0 | if (p >= end) { |
3545 | 0 | as->length = p - as->s; |
3546 | | /* Re-allocate buffer for MBS. */ |
3547 | 0 | if (archive_string_ensure(as, |
3548 | 0 | as->length + max(len * 2, |
3549 | 0 | (size_t)MB_CUR_MAX) + 1) == NULL) |
3550 | 0 | return (-1); |
3551 | 0 | p = as->s + as->length; |
3552 | 0 | end = as->s + as->buffer_length - MB_CUR_MAX -1; |
3553 | 0 | } |
3554 | | |
3555 | | /* |
3556 | | * As libarchive 2.x, translates the UTF-8 characters into |
3557 | | * wide-characters in the assumption that WCS is Unicode. |
3558 | | */ |
3559 | 0 | if (n < 0) { |
3560 | 0 | n *= -1; |
3561 | 0 | wc = L'?'; |
3562 | 0 | } else |
3563 | 0 | wc = (wchar_t)unicode; |
3564 | |
|
3565 | 0 | s += n; |
3566 | 0 | len -= n; |
3567 | | /* |
3568 | | * Translates the wide-character into the current locale MBS. |
3569 | | */ |
3570 | 0 | #if HAVE_WCRTOMB |
3571 | 0 | n = (int)wcrtomb(p, wc, &shift_state); |
3572 | | #else |
3573 | | n = (int)wctomb(p, wc); |
3574 | | #endif |
3575 | 0 | if (n == -1) |
3576 | 0 | return (-1); |
3577 | 0 | p += n; |
3578 | 0 | } |
3579 | 0 | as->length = p - as->s; |
3580 | 0 | as->s[as->length] = '\0'; |
3581 | 0 | return (0); |
3582 | 0 | } |
3583 | | |
3584 | | |
3585 | | /* |
3586 | | * Conversion functions between current locale dependent MBS and UTF-16BE. |
3587 | | * strncat_from_utf16be() : UTF-16BE --> MBS |
3588 | | * strncat_to_utf16be() : MBS --> UTF16BE |
3589 | | */ |
3590 | | |
3591 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
3592 | | |
3593 | | /* |
3594 | | * Convert a UTF-16BE/LE string to current locale and copy the result. |
3595 | | * Return -1 if conversion fails. |
3596 | | */ |
3597 | | static int |
3598 | | win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, |
3599 | | struct archive_string_conv *sc, int be) |
3600 | | { |
3601 | | struct archive_string tmp; |
3602 | | const char *u16; |
3603 | | BOOL defchar; |
3604 | | char *mbs; |
3605 | | size_t mbs_size, b, ll; |
3606 | | int ret = 0; |
3607 | | |
3608 | | bytes &= ~1; |
3609 | | if (archive_string_ensure(as, as->length + bytes +1) == NULL) |
3610 | | return (-1); |
3611 | | |
3612 | | mbs = as->s + as->length; |
3613 | | mbs_size = as->buffer_length - as->length -1; |
3614 | | |
3615 | | if (sc->to_cp == CP_C_LOCALE) { |
3616 | | /* |
3617 | | * "C" locale special processing. |
3618 | | */ |
3619 | | u16 = _p; |
3620 | | ll = 0; |
3621 | | for (b = 0; b < bytes; b += 2) { |
3622 | | uint16_t val; |
3623 | | if (be) |
3624 | | val = archive_be16dec(u16+b); |
3625 | | else |
3626 | | val = archive_le16dec(u16+b); |
3627 | | if (val > 255) { |
3628 | | *mbs++ = '?'; |
3629 | | ret = -1; |
3630 | | } else |
3631 | | *mbs++ = (char)(val&0xff); |
3632 | | ll++; |
3633 | | } |
3634 | | as->length += ll; |
3635 | | as->s[as->length] = '\0'; |
3636 | | return (ret); |
3637 | | } |
3638 | | |
3639 | | archive_string_init(&tmp); |
3640 | | if (be) { |
3641 | | if (IS_BIG_ENDIAN) { |
3642 | | u16 = _p; |
3643 | | } else { |
3644 | | if (archive_string_ensure(&tmp, bytes+2) == NULL) |
3645 | | return (-1); |
3646 | | memcpy(tmp.s, _p, bytes); |
3647 | | for (b = 0; b < bytes; b += 2) { |
3648 | | uint16_t val = archive_be16dec(tmp.s+b); |
3649 | | archive_le16enc(tmp.s+b, val); |
3650 | | } |
3651 | | u16 = tmp.s; |
3652 | | } |
3653 | | } else { |
3654 | | if (!IS_BIG_ENDIAN) { |
3655 | | u16 = _p; |
3656 | | } else { |
3657 | | if (archive_string_ensure(&tmp, bytes+2) == NULL) |
3658 | | return (-1); |
3659 | | memcpy(tmp.s, _p, bytes); |
3660 | | for (b = 0; b < bytes; b += 2) { |
3661 | | uint16_t val = archive_le16dec(tmp.s+b); |
3662 | | archive_be16enc(tmp.s+b, val); |
3663 | | } |
3664 | | u16 = tmp.s; |
3665 | | } |
3666 | | } |
3667 | | |
3668 | | do { |
3669 | | int r; |
3670 | | defchar = 0; |
3671 | | /* WideCharToMultiByte is limited to int. */ |
3672 | | if (bytes > (size_t)INT_MAX || mbs_size > (size_t)INT_MAX) |
3673 | | return (-1); |
3674 | | r = WideCharToMultiByte(sc->to_cp, 0, |
3675 | | (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size, |
3676 | | NULL, &defchar); |
3677 | | /* Exit loop if we succeeded */ |
3678 | | if (r != 0 || |
3679 | | GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
3680 | | ll = (size_t)r; |
3681 | | break; |
3682 | | } |
3683 | | /* Else expand buffer and loop to try again. */ |
3684 | | r = WideCharToMultiByte(sc->to_cp, 0, |
3685 | | (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); |
3686 | | ll = (size_t)r; |
3687 | | if (archive_string_ensure(as, ll +1) == NULL) |
3688 | | return (-1); |
3689 | | mbs = as->s + as->length; |
3690 | | mbs_size = as->buffer_length - as->length -1; |
3691 | | } while (1); |
3692 | | archive_string_free(&tmp); |
3693 | | as->length += ll; |
3694 | | as->s[as->length] = '\0'; |
3695 | | if (ll == 0 || defchar) |
3696 | | ret = -1; |
3697 | | return (ret); |
3698 | | } |
3699 | | |
3700 | | static int |
3701 | | win_strncat_from_utf16be(struct archive_string *as, const void *_p, |
3702 | | size_t bytes, struct archive_string_conv *sc) |
3703 | | { |
3704 | | return (win_strncat_from_utf16(as, _p, bytes, sc, 1)); |
3705 | | } |
3706 | | |
3707 | | static int |
3708 | | win_strncat_from_utf16le(struct archive_string *as, const void *_p, |
3709 | | size_t bytes, struct archive_string_conv *sc) |
3710 | | { |
3711 | | return (win_strncat_from_utf16(as, _p, bytes, sc, 0)); |
3712 | | } |
3713 | | |
3714 | | /* |
3715 | | * Convert a current locale string to UTF-16BE/LE and copy the result. |
3716 | | * Return -1 if conversion fails. |
3717 | | */ |
3718 | | static int |
3719 | | win_strncat_to_utf16(struct archive_string *as16, const void *_p, |
3720 | | size_t length, struct archive_string_conv *sc, int bigendian) |
3721 | | { |
3722 | | const char *s = (const char *)_p; |
3723 | | char *u16; |
3724 | | size_t count, avail; |
3725 | | |
3726 | | if (archive_string_ensure(as16, |
3727 | | as16->length + (length + 1) * 2) == NULL) |
3728 | | return (-1); |
3729 | | |
3730 | | u16 = as16->s + as16->length; |
3731 | | avail = as16->buffer_length - 2; |
3732 | | if (sc->from_cp == CP_C_LOCALE) { |
3733 | | /* |
3734 | | * "C" locale special processing. |
3735 | | */ |
3736 | | count = 0; |
3737 | | while (count < length && *s) { |
3738 | | if (bigendian) |
3739 | | archive_be16enc(u16, *s); |
3740 | | else |
3741 | | archive_le16enc(u16, *s); |
3742 | | u16 += 2; |
3743 | | s++; |
3744 | | count++; |
3745 | | } |
3746 | | as16->length += count << 1; |
3747 | | as16->s[as16->length] = 0; |
3748 | | as16->s[as16->length+1] = 0; |
3749 | | return (0); |
3750 | | } |
3751 | | do { |
3752 | | int r; |
3753 | | if (length > (size_t)INT_MAX || (avail >> 1) > (size_t)INT_MAX) |
3754 | | return (-1); |
3755 | | r = MultiByteToWideChar(sc->from_cp, |
3756 | | MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1); |
3757 | | /* Exit loop if we succeeded */ |
3758 | | if (r != 0 || |
3759 | | GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
3760 | | count = (size_t)r; |
3761 | | break; |
3762 | | } |
3763 | | /* Expand buffer and try again */ |
3764 | | r = MultiByteToWideChar(sc->from_cp, |
3765 | | MB_PRECOMPOSED, s, (int)length, NULL, 0); |
3766 | | count = (size_t)r; |
3767 | | if (archive_string_ensure(as16, (count +1) * 2) |
3768 | | == NULL) |
3769 | | return (-1); |
3770 | | u16 = as16->s + as16->length; |
3771 | | avail = as16->buffer_length - 2; |
3772 | | } while (1); |
3773 | | as16->length += count * 2; |
3774 | | as16->s[as16->length] = 0; |
3775 | | as16->s[as16->length+1] = 0; |
3776 | | if (count == 0) |
3777 | | return (-1); |
3778 | | |
3779 | | if (IS_BIG_ENDIAN) { |
3780 | | if (!bigendian) { |
3781 | | while (count > 0) { |
3782 | | uint16_t v = archive_be16dec(u16); |
3783 | | archive_le16enc(u16, v); |
3784 | | u16 += 2; |
3785 | | count--; |
3786 | | } |
3787 | | } |
3788 | | } else { |
3789 | | if (bigendian) { |
3790 | | while (count > 0) { |
3791 | | uint16_t v = archive_le16dec(u16); |
3792 | | archive_be16enc(u16, v); |
3793 | | u16 += 2; |
3794 | | count--; |
3795 | | } |
3796 | | } |
3797 | | } |
3798 | | return (0); |
3799 | | } |
3800 | | |
3801 | | static int |
3802 | | win_strncat_to_utf16be(struct archive_string *as16, const void *_p, |
3803 | | size_t length, struct archive_string_conv *sc) |
3804 | | { |
3805 | | return (win_strncat_to_utf16(as16, _p, length, sc, 1)); |
3806 | | } |
3807 | | |
3808 | | static int |
3809 | | win_strncat_to_utf16le(struct archive_string *as16, const void *_p, |
3810 | | size_t length, struct archive_string_conv *sc) |
3811 | | { |
3812 | | return (win_strncat_to_utf16(as16, _p, length, sc, 0)); |
3813 | | } |
3814 | | |
3815 | | #endif /* _WIN32 && !__CYGWIN__ */ |
3816 | | |
3817 | | /* |
3818 | | * Do the best effort for conversions. |
3819 | | * We cannot handle UTF-16BE character-set without such iconv, |
3820 | | * but there is a chance if a string consists just ASCII code or |
3821 | | * a current locale is UTF-8. |
3822 | | */ |
3823 | | |
3824 | | /* |
3825 | | * Convert a UTF-16BE string to current locale and copy the result. |
3826 | | * Return -1 if conversion fails. |
3827 | | */ |
3828 | | static int |
3829 | | best_effort_strncat_from_utf16(struct archive_string *as, const void *_p, |
3830 | | size_t bytes, struct archive_string_conv *sc, int be) |
3831 | 0 | { |
3832 | 0 | const char *utf16 = (const char *)_p; |
3833 | 0 | char *mbs; |
3834 | 0 | uint32_t uc; |
3835 | 0 | int n, ret; |
3836 | |
|
3837 | 0 | (void)sc; /* UNUSED */ |
3838 | | /* |
3839 | | * Other case, we should do the best effort. |
3840 | | * If all character are ASCII(<0x7f), we can convert it. |
3841 | | * if not , we set a alternative character and return -1. |
3842 | | */ |
3843 | 0 | ret = 0; |
3844 | 0 | if (archive_string_ensure(as, as->length + bytes +1) == NULL) |
3845 | 0 | return (-1); |
3846 | 0 | mbs = as->s + as->length; |
3847 | |
|
3848 | 0 | while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) { |
3849 | 0 | if (n < 0) { |
3850 | 0 | n *= -1; |
3851 | 0 | ret = -1; |
3852 | 0 | } |
3853 | 0 | bytes -= n; |
3854 | 0 | utf16 += n; |
3855 | |
|
3856 | 0 | if (uc > 127) { |
3857 | | /* We cannot handle it. */ |
3858 | 0 | *mbs++ = '?'; |
3859 | 0 | ret = -1; |
3860 | 0 | } else |
3861 | 0 | *mbs++ = (char)uc; |
3862 | 0 | } |
3863 | 0 | as->length = mbs - as->s; |
3864 | 0 | as->s[as->length] = '\0'; |
3865 | 0 | return (ret); |
3866 | 0 | } |
3867 | | |
3868 | | static int |
3869 | | best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p, |
3870 | | size_t bytes, struct archive_string_conv *sc) |
3871 | 0 | { |
3872 | 0 | return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1)); |
3873 | 0 | } |
3874 | | |
3875 | | static int |
3876 | | best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p, |
3877 | | size_t bytes, struct archive_string_conv *sc) |
3878 | 0 | { |
3879 | 0 | return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0)); |
3880 | 0 | } |
3881 | | |
3882 | | /* |
3883 | | * Convert a current locale string to UTF-16BE/LE and copy the result. |
3884 | | * Return -1 if conversion fails. |
3885 | | */ |
3886 | | static int |
3887 | | best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p, |
3888 | | size_t length, struct archive_string_conv *sc, int bigendian) |
3889 | 0 | { |
3890 | 0 | const char *s = (const char *)_p; |
3891 | 0 | char *utf16; |
3892 | 0 | size_t remaining; |
3893 | 0 | int ret; |
3894 | |
|
3895 | 0 | (void)sc; /* UNUSED */ |
3896 | | /* |
3897 | | * Other case, we should do the best effort. |
3898 | | * If all character are ASCII(<0x7f), we can convert it. |
3899 | | * if not , we set a alternative character and return -1. |
3900 | | */ |
3901 | 0 | ret = 0; |
3902 | 0 | remaining = length; |
3903 | |
|
3904 | 0 | if (archive_string_ensure(as16, |
3905 | 0 | as16->length + (length + 1) * 2) == NULL) |
3906 | 0 | return (-1); |
3907 | | |
3908 | 0 | utf16 = as16->s + as16->length; |
3909 | 0 | while (remaining--) { |
3910 | 0 | unsigned c = *s++; |
3911 | 0 | if (c > 127) { |
3912 | | /* We cannot handle it. */ |
3913 | 0 | c = UNICODE_R_CHAR; |
3914 | 0 | ret = -1; |
3915 | 0 | } |
3916 | 0 | if (bigendian) |
3917 | 0 | archive_be16enc(utf16, (uint16_t)c); |
3918 | 0 | else |
3919 | 0 | archive_le16enc(utf16, (uint16_t)c); |
3920 | 0 | utf16 += 2; |
3921 | 0 | } |
3922 | 0 | as16->length = utf16 - as16->s; |
3923 | 0 | as16->s[as16->length] = 0; |
3924 | 0 | as16->s[as16->length+1] = 0; |
3925 | 0 | return (ret); |
3926 | 0 | } |
3927 | | |
3928 | | static int |
3929 | | best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p, |
3930 | | size_t length, struct archive_string_conv *sc) |
3931 | 0 | { |
3932 | 0 | return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1)); |
3933 | 0 | } |
3934 | | |
3935 | | static int |
3936 | | best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p, |
3937 | | size_t length, struct archive_string_conv *sc) |
3938 | 0 | { |
3939 | 0 | return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0)); |
3940 | 0 | } |
3941 | | |
3942 | | |
3943 | | /* |
3944 | | * Multistring operations. |
3945 | | */ |
3946 | | |
3947 | | void |
3948 | | archive_mstring_clean(struct archive_mstring *aes) |
3949 | 6.43M | { |
3950 | 6.43M | archive_wstring_free(&(aes->aes_wcs)); |
3951 | 6.43M | archive_string_free(&(aes->aes_mbs)); |
3952 | 6.43M | archive_string_free(&(aes->aes_utf8)); |
3953 | 6.43M | archive_string_free(&(aes->aes_mbs_in_locale)); |
3954 | 6.43M | aes->aes_set = 0; |
3955 | 6.43M | } |
3956 | | |
3957 | | void |
3958 | | archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src) |
3959 | 0 | { |
3960 | 0 | dest->aes_set = src->aes_set; |
3961 | 0 | archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); |
3962 | 0 | archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); |
3963 | 0 | archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs)); |
3964 | 0 | } |
3965 | | |
3966 | | int |
3967 | | archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes, |
3968 | | const char **p) |
3969 | 449k | { |
3970 | 449k | struct archive_string_conv *sc; |
3971 | 449k | int r; |
3972 | | |
3973 | | /* If we already have a UTF8 form, return that immediately. */ |
3974 | 449k | if (aes->aes_set & AES_SET_UTF8) { |
3975 | 42.9k | *p = aes->aes_utf8.s; |
3976 | 42.9k | return (0); |
3977 | 42.9k | } |
3978 | | |
3979 | 406k | *p = NULL; |
3980 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
3981 | | /* |
3982 | | * On Windows, first try converting from WCS because (1) there's no |
3983 | | * guarantee that the conversion to MBS will succeed, e.g. when using |
3984 | | * CP_ACP, and (2) that's more efficient than converting to MBS, just to |
3985 | | * convert back to WCS again before finally converting to UTF-8 |
3986 | | */ |
3987 | | if ((aes->aes_set & AES_SET_WCS) != 0) { |
3988 | | sc = archive_string_conversion_to_charset(a, "UTF-8", 1); |
3989 | | if (sc == NULL) |
3990 | | return (-1);/* Couldn't allocate memory for sc. */ |
3991 | | archive_string_empty(&(aes->aes_utf8)); |
3992 | | r = archive_string_append_from_wcs_in_codepage(&(aes->aes_utf8), |
3993 | | aes->aes_wcs.s, aes->aes_wcs.length, sc); |
3994 | | if (a == NULL) |
3995 | | free_sconv_object(sc); |
3996 | | if (r == 0) { |
3997 | | aes->aes_set |= AES_SET_UTF8; |
3998 | | *p = aes->aes_utf8.s; |
3999 | | return (0);/* success. */ |
4000 | | } else |
4001 | | return (-1);/* failure. */ |
4002 | | } |
4003 | | #endif |
4004 | | /* Try converting WCS to MBS first if MBS does not exist yet. */ |
4005 | 406k | if ((aes->aes_set & AES_SET_MBS) == 0) { |
4006 | 4.88k | const char *pm; /* unused */ |
4007 | 4.88k | archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */ |
4008 | 4.88k | } |
4009 | 406k | if (aes->aes_set & AES_SET_MBS) { |
4010 | 401k | sc = archive_string_conversion_to_charset(a, "UTF-8", 1); |
4011 | 401k | if (sc == NULL) |
4012 | 0 | return (-1);/* Couldn't allocate memory for sc. */ |
4013 | 401k | r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s, |
4014 | 401k | aes->aes_mbs.length, sc); |
4015 | 401k | if (a == NULL) |
4016 | 401k | free_sconv_object(sc); |
4017 | 401k | if (r == 0) { |
4018 | 380k | aes->aes_set |= AES_SET_UTF8; |
4019 | 380k | *p = aes->aes_utf8.s; |
4020 | 380k | return (0);/* success. */ |
4021 | 380k | } else |
4022 | 21.3k | return (-1);/* failure. */ |
4023 | 401k | } |
4024 | 4.88k | return (0);/* success. */ |
4025 | 406k | } |
4026 | | |
4027 | | int |
4028 | | archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes, |
4029 | | const char **p) |
4030 | 1.61M | { |
4031 | 1.61M | struct archive_string_conv *sc; |
4032 | 1.61M | int r, ret = 0; |
4033 | | |
4034 | | /* If we already have an MBS form, return that immediately. */ |
4035 | 1.61M | if (aes->aes_set & AES_SET_MBS) { |
4036 | 969k | *p = aes->aes_mbs.s; |
4037 | 969k | return (ret); |
4038 | 969k | } |
4039 | | |
4040 | 649k | *p = NULL; |
4041 | | /* If there's a WCS form, try converting with the native locale. */ |
4042 | 649k | if (aes->aes_set & AES_SET_WCS) { |
4043 | 7.82k | archive_string_empty(&(aes->aes_mbs)); |
4044 | 7.82k | r = archive_string_append_from_wcs(&(aes->aes_mbs), |
4045 | 7.82k | aes->aes_wcs.s, aes->aes_wcs.length); |
4046 | 7.82k | *p = aes->aes_mbs.s; |
4047 | 7.82k | if (r == 0) { |
4048 | 7.82k | aes->aes_set |= AES_SET_MBS; |
4049 | 7.82k | return (ret); |
4050 | 7.82k | } else |
4051 | 0 | ret = -1; |
4052 | 7.82k | } |
4053 | | |
4054 | | /* If there's a UTF-8 form, try converting with the native locale. */ |
4055 | 641k | if (aes->aes_set & AES_SET_UTF8) { |
4056 | 6.64k | archive_string_empty(&(aes->aes_mbs)); |
4057 | 6.64k | sc = archive_string_conversion_from_charset(a, "UTF-8", 1); |
4058 | 6.64k | if (sc == NULL) |
4059 | 0 | return (-1);/* Couldn't allocate memory for sc. */ |
4060 | 6.64k | r = archive_strncpy_l(&(aes->aes_mbs), |
4061 | 6.64k | aes->aes_utf8.s, aes->aes_utf8.length, sc); |
4062 | 6.64k | if (a == NULL) |
4063 | 6.64k | free_sconv_object(sc); |
4064 | 6.64k | *p = aes->aes_mbs.s; |
4065 | 6.64k | if (r == 0) { |
4066 | 0 | aes->aes_set |= AES_SET_MBS; |
4067 | 0 | ret = 0;/* success; overwrite previous error. */ |
4068 | 0 | } else |
4069 | 6.64k | ret = -1;/* failure. */ |
4070 | 6.64k | } |
4071 | 641k | return (ret); |
4072 | 641k | } |
4073 | | |
4074 | | int |
4075 | | archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes, |
4076 | | const wchar_t **wp) |
4077 | 791k | { |
4078 | 791k | int r, ret = 0; |
4079 | | |
4080 | 791k | (void)a;/* UNUSED */ |
4081 | | /* Return WCS form if we already have it. */ |
4082 | 791k | if (aes->aes_set & AES_SET_WCS) { |
4083 | 65.2k | *wp = aes->aes_wcs.s; |
4084 | 65.2k | return (ret); |
4085 | 65.2k | } |
4086 | | |
4087 | 725k | *wp = NULL; |
4088 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
4089 | | /* |
4090 | | * On Windows, prefer converting from UTF-8 directly to WCS because: |
4091 | | * (1) there's no guarantee that the string can be represented in MBS (e.g. |
4092 | | * with CP_ACP), and (2) in order to convert from UTF-8 to MBS, we're going |
4093 | | * to need to convert from UTF-8 to WCS anyway and its wasteful to throw |
4094 | | * away that intermediate result |
4095 | | */ |
4096 | | if (aes->aes_set & AES_SET_UTF8) { |
4097 | | struct archive_string_conv *sc; |
4098 | | |
4099 | | sc = archive_string_conversion_from_charset(a, "UTF-8", 1); |
4100 | | if (sc != NULL) { |
4101 | | archive_wstring_empty((&aes->aes_wcs)); |
4102 | | r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), |
4103 | | aes->aes_utf8.s, aes->aes_utf8.length, sc); |
4104 | | if (a == NULL) |
4105 | | free_sconv_object(sc); |
4106 | | if (r == 0) { |
4107 | | aes->aes_set |= AES_SET_WCS; |
4108 | | *wp = aes->aes_wcs.s; |
4109 | | return (0); |
4110 | | } |
4111 | | } |
4112 | | } |
4113 | | #endif |
4114 | | /* Try converting UTF8 to MBS first if MBS does not exist yet. */ |
4115 | 725k | if ((aes->aes_set & AES_SET_MBS) == 0) { |
4116 | 308k | const char *p; /* unused */ |
4117 | 308k | archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */ |
4118 | 308k | } |
4119 | | /* Try converting MBS to WCS using native locale. */ |
4120 | 725k | if (aes->aes_set & AES_SET_MBS) { |
4121 | 417k | archive_wstring_empty(&(aes->aes_wcs)); |
4122 | 417k | r = archive_wstring_append_from_mbs(&(aes->aes_wcs), |
4123 | 417k | aes->aes_mbs.s, aes->aes_mbs.length); |
4124 | 417k | if (r == 0) { |
4125 | 380k | aes->aes_set |= AES_SET_WCS; |
4126 | 380k | *wp = aes->aes_wcs.s; |
4127 | 380k | } else |
4128 | 37.7k | ret = -1;/* failure. */ |
4129 | 417k | } |
4130 | 725k | return (ret); |
4131 | 791k | } |
4132 | | |
4133 | | int |
4134 | | archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes, |
4135 | | const char **p, size_t *length, struct archive_string_conv *sc) |
4136 | 0 | { |
4137 | 0 | int ret = 0; |
4138 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
4139 | | int r; |
4140 | | |
4141 | | /* |
4142 | | * Internationalization programming on Windows must use Wide |
4143 | | * characters because Windows platform cannot make locale UTF-8. |
4144 | | */ |
4145 | | if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) { |
4146 | | archive_string_empty(&(aes->aes_mbs_in_locale)); |
4147 | | r = archive_string_append_from_wcs_in_codepage( |
4148 | | &(aes->aes_mbs_in_locale), aes->aes_wcs.s, |
4149 | | aes->aes_wcs.length, sc); |
4150 | | if (r == 0) { |
4151 | | *p = aes->aes_mbs_in_locale.s; |
4152 | | if (length != NULL) |
4153 | | *length = aes->aes_mbs_in_locale.length; |
4154 | | return (0); |
4155 | | } else if (errno == ENOMEM) |
4156 | | return (-1); |
4157 | | else |
4158 | | ret = -1; |
4159 | | } |
4160 | | #endif |
4161 | | |
4162 | | /* If there is not an MBS form but there is a WCS or UTF8 form, try converting |
4163 | | * with the native locale to be used for translating it to specified |
4164 | | * character-set. */ |
4165 | 0 | if ((aes->aes_set & AES_SET_MBS) == 0) { |
4166 | 0 | const char *pm; /* unused */ |
4167 | 0 | if (archive_mstring_get_mbs(a, aes, &pm) != 0) { |
4168 | | /* We have another form, but failed to convert it to |
4169 | | * the native locale. Transitively, we've failed to |
4170 | | * convert it to the specified character set. */ |
4171 | 0 | ret = -1; |
4172 | 0 | } |
4173 | 0 | } |
4174 | | /* If we already have an MBS form, use it to be translated to |
4175 | | * specified character-set. */ |
4176 | 0 | if (aes->aes_set & AES_SET_MBS) { |
4177 | 0 | if (sc == NULL) { |
4178 | | /* Conversion is unneeded. */ |
4179 | 0 | *p = aes->aes_mbs.s; |
4180 | 0 | if (length != NULL) |
4181 | 0 | *length = aes->aes_mbs.length; |
4182 | 0 | return (0); |
4183 | 0 | } |
4184 | 0 | ret = archive_strncpy_l(&(aes->aes_mbs_in_locale), |
4185 | 0 | aes->aes_mbs.s, aes->aes_mbs.length, sc); |
4186 | 0 | *p = aes->aes_mbs_in_locale.s; |
4187 | 0 | if (length != NULL) |
4188 | 0 | *length = aes->aes_mbs_in_locale.length; |
4189 | 0 | } else { |
4190 | | /* Either we have no string in any form, |
4191 | | * or conversion failed and set 'ret != 0'. */ |
4192 | 0 | *p = NULL; |
4193 | 0 | if (length != NULL) |
4194 | 0 | *length = 0; |
4195 | 0 | } |
4196 | 0 | return (ret); |
4197 | 0 | } |
4198 | | |
4199 | | int |
4200 | | archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs) |
4201 | 68.5k | { |
4202 | 68.5k | if (mbs == NULL) { |
4203 | 6.60k | aes->aes_set = 0; |
4204 | 6.60k | return (0); |
4205 | 6.60k | } |
4206 | 61.9k | return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs))); |
4207 | 68.5k | } |
4208 | | |
4209 | | int |
4210 | | archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs, |
4211 | | size_t len) |
4212 | 70.1k | { |
4213 | 70.1k | if (mbs == NULL) { |
4214 | 0 | aes->aes_set = 0; |
4215 | 0 | return (0); |
4216 | 0 | } |
4217 | 70.1k | aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ |
4218 | 70.1k | archive_strncpy(&(aes->aes_mbs), mbs, len); |
4219 | 70.1k | archive_string_empty(&(aes->aes_utf8)); |
4220 | 70.1k | archive_wstring_empty(&(aes->aes_wcs)); |
4221 | 70.1k | return (0); |
4222 | 70.1k | } |
4223 | | |
4224 | | int |
4225 | | archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs) |
4226 | 8.37k | { |
4227 | 8.37k | return archive_mstring_copy_wcs_len(aes, wcs, |
4228 | 8.37k | wcs == NULL ? 0 : wcslen(wcs)); |
4229 | 8.37k | } |
4230 | | |
4231 | | int |
4232 | | archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8) |
4233 | 0 | { |
4234 | 0 | if (utf8 == NULL) { |
4235 | 0 | aes->aes_set = 0; |
4236 | 0 | return (0); |
4237 | 0 | } |
4238 | 0 | aes->aes_set = AES_SET_UTF8; |
4239 | 0 | archive_string_empty(&(aes->aes_mbs)); |
4240 | 0 | archive_string_empty(&(aes->aes_wcs)); |
4241 | 0 | archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8)); |
4242 | 0 | return (int)strlen(utf8); |
4243 | 0 | } |
4244 | | |
4245 | | int |
4246 | | archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs, |
4247 | | size_t len) |
4248 | 8.37k | { |
4249 | 8.37k | if (wcs == NULL) { |
4250 | 0 | aes->aes_set = 0; |
4251 | 0 | return (0); |
4252 | 0 | } |
4253 | 8.37k | aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ |
4254 | 8.37k | archive_string_empty(&(aes->aes_mbs)); |
4255 | 8.37k | archive_string_empty(&(aes->aes_utf8)); |
4256 | 8.37k | archive_wstrncpy(&(aes->aes_wcs), wcs, len); |
4257 | 8.37k | return (0); |
4258 | 8.37k | } |
4259 | | |
4260 | | int |
4261 | | archive_mstring_copy_mbs_len_l(struct archive_mstring *aes, |
4262 | | const char *mbs, size_t len, struct archive_string_conv *sc) |
4263 | 403k | { |
4264 | 403k | int r; |
4265 | | |
4266 | 403k | if (mbs == NULL) { |
4267 | 6.57k | aes->aes_set = 0; |
4268 | 6.57k | return (0); |
4269 | 6.57k | } |
4270 | 396k | archive_string_empty(&(aes->aes_mbs)); |
4271 | 396k | archive_wstring_empty(&(aes->aes_wcs)); |
4272 | 396k | archive_string_empty(&(aes->aes_utf8)); |
4273 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
4274 | | /* |
4275 | | * Internationalization programming on Windows must use Wide |
4276 | | * characters because Windows platform cannot make locale UTF-8. |
4277 | | */ |
4278 | | if (sc == NULL) { |
4279 | | if (archive_string_append(&(aes->aes_mbs), |
4280 | | mbs, mbsnbytes(mbs, len)) == NULL) { |
4281 | | aes->aes_set = 0; |
4282 | | r = -1; |
4283 | | } else { |
4284 | | aes->aes_set = AES_SET_MBS; |
4285 | | r = 0; |
4286 | | } |
4287 | | #if defined(HAVE_ICONV) |
4288 | | } else if (sc != NULL && sc->cd_w != (iconv_t)-1) { |
4289 | | /* |
4290 | | * This case happens only when MultiByteToWideChar() cannot |
4291 | | * handle sc->from_cp, and we have to iconv in order to |
4292 | | * translate character-set to wchar_t,UTF-16. |
4293 | | */ |
4294 | | iconv_t cd = sc->cd; |
4295 | | unsigned from_cp; |
4296 | | int flag; |
4297 | | |
4298 | | /* |
4299 | | * Translate multi-bytes from some character-set to UTF-8. |
4300 | | */ |
4301 | | sc->cd = sc->cd_w; |
4302 | | r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc); |
4303 | | sc->cd = cd; |
4304 | | if (r != 0) { |
4305 | | aes->aes_set = 0; |
4306 | | return (r); |
4307 | | } |
4308 | | aes->aes_set = AES_SET_UTF8; |
4309 | | |
4310 | | /* |
4311 | | * Append the UTF-8 string into wstring. |
4312 | | */ |
4313 | | flag = sc->flag; |
4314 | | sc->flag &= ~(SCONV_NORMALIZATION_C |
4315 | | | SCONV_TO_UTF16| SCONV_FROM_UTF16); |
4316 | | from_cp = sc->from_cp; |
4317 | | sc->from_cp = CP_UTF8; |
4318 | | r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), |
4319 | | aes->aes_utf8.s, aes->aes_utf8.length, sc); |
4320 | | sc->flag = flag; |
4321 | | sc->from_cp = from_cp; |
4322 | | if (r == 0) |
4323 | | aes->aes_set |= AES_SET_WCS; |
4324 | | #endif |
4325 | | } else { |
4326 | | r = archive_wstring_append_from_mbs_in_codepage( |
4327 | | &(aes->aes_wcs), mbs, len, sc); |
4328 | | if (r == 0) |
4329 | | aes->aes_set = AES_SET_WCS; |
4330 | | else |
4331 | | aes->aes_set = 0; |
4332 | | } |
4333 | | #else |
4334 | 396k | r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc); |
4335 | 396k | if (r == 0) |
4336 | 390k | aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ |
4337 | 5.97k | else |
4338 | 5.97k | aes->aes_set = 0; |
4339 | 396k | #endif |
4340 | 396k | return (r); |
4341 | 403k | } |
4342 | | |
4343 | | /* |
4344 | | * The 'update' form tries to proactively update all forms of |
4345 | | * this string (WCS and MBS) and returns an error if any of |
4346 | | * them fail. This is used by the 'pax' handler, for instance, |
4347 | | * to detect and report character-conversion failures early while |
4348 | | * still allowing clients to get potentially useful values from |
4349 | | * the more tolerant lazy conversions. (get_mbs and get_wcs will |
4350 | | * strive to give the user something useful, so you can get hopefully |
4351 | | * usable values even if some of the character conversions are failing.) |
4352 | | */ |
4353 | | int |
4354 | | archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, |
4355 | | const char *utf8) |
4356 | 48.9k | { |
4357 | 48.9k | struct archive_string_conv *sc; |
4358 | 48.9k | int r; |
4359 | | |
4360 | 48.9k | if (utf8 == NULL) { |
4361 | 0 | aes->aes_set = 0; |
4362 | 0 | return (0); /* Succeeded in clearing everything. */ |
4363 | 0 | } |
4364 | | |
4365 | | /* Save the UTF8 string. */ |
4366 | 48.9k | archive_strcpy(&(aes->aes_utf8), utf8); |
4367 | | |
4368 | | /* Empty the mbs and wcs strings. */ |
4369 | 48.9k | archive_string_empty(&(aes->aes_mbs)); |
4370 | 48.9k | archive_wstring_empty(&(aes->aes_wcs)); |
4371 | | |
4372 | 48.9k | aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ |
4373 | | |
4374 | 48.9k | sc = archive_string_conversion_from_charset(a, "UTF-8", 1); |
4375 | 48.9k | if (sc == NULL) |
4376 | 0 | return (-1);/* Couldn't allocate memory for sc. */ |
4377 | | |
4378 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
4379 | | /* On Windows, there's no good way to convert from UTF8 -> MBS directly, so |
4380 | | * prefer to first convert to WCS as (1) it's wasteful to throw away the |
4381 | | * intermediate result, and (2) WCS will still be set even if we fail to |
4382 | | * convert to MBS (e.g. with ACP that can't represent the characters) */ |
4383 | | r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs), |
4384 | | aes->aes_utf8.s, aes->aes_utf8.length, sc); |
4385 | | |
4386 | | if (a == NULL) |
4387 | | free_sconv_object(sc); |
4388 | | if (r != 0) |
4389 | | return (-1); /* This will guarantee we can't convert to MBS */ |
4390 | | aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ |
4391 | | |
4392 | | /* Try converting WCS to MBS, return false on failure. */ |
4393 | | if (archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s, |
4394 | | aes->aes_wcs.length)) |
4395 | | return (-1); |
4396 | | #else |
4397 | | /* Try converting UTF-8 to MBS, return false on failure. */ |
4398 | 48.9k | r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc); |
4399 | | |
4400 | 48.9k | if (a == NULL) |
4401 | 48.9k | free_sconv_object(sc); |
4402 | 48.9k | if (r != 0) |
4403 | 38.5k | return (-1); |
4404 | 10.3k | aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */ |
4405 | | |
4406 | | /* Try converting MBS to WCS, return false on failure. */ |
4407 | 10.3k | if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s, |
4408 | 10.3k | aes->aes_mbs.length)) |
4409 | 0 | return (-1); |
4410 | 10.3k | #endif |
4411 | | |
4412 | | /* All conversions succeeded. */ |
4413 | 10.3k | aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; |
4414 | | |
4415 | 10.3k | return (0); |
4416 | 10.3k | } |