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