From: Tim Kientzle Date: Sun, 27 Jun 2010 01:28:00 +0000 (-0400) Subject: Merge branches/wstring: This adds a new archive_wstring X-Git-Tag: v3.0.0a~932 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e900bcd29efbb48d019fdef2d6ce3fd287d15b44;p=thirdparty%2Flibarchive.git Merge branches/wstring: This adds a new archive_wstring type for unicode strings and uses it to simplify some of the code in archive_entry. SVN-Revision: 2508 --- diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index 0a736a193..b0b935f58 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -176,10 +176,7 @@ static size_t wcslen(const wchar_t *s) static void aes_clean(struct aes *aes) { - if (aes->aes_wcs) { - free((wchar_t *)(uintptr_t)aes->aes_wcs); - aes->aes_wcs = NULL; - } + archive_wstring_free(&(aes->aes_wcs)); archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); aes->aes_set = 0; @@ -188,33 +185,10 @@ aes_clean(struct aes *aes) static void aes_copy(struct aes *dest, struct aes *src) { - wchar_t *wp; - dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); - - if (src->aes_wcs != NULL) { - wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) - * sizeof(wchar_t)); - if (wp == NULL) - __archive_errx(1, "No memory for aes_copy()"); - wcscpy(wp, src->aes_wcs); - dest->aes_wcs = wp; - } -} - -static const char * -aes_get_utf8(struct aes *aes) -{ - if (aes->aes_set & AES_SET_UTF8) - return (aes->aes_utf8.s); - if ((aes->aes_set & AES_SET_WCS) - && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) { - aes->aes_set |= AES_SET_UTF8; - return (aes->aes_utf8.s); - } - return (NULL); + archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs)); } static const char * @@ -225,50 +199,38 @@ aes_get_mbs(struct aes *aes) return (aes->aes_mbs.s); /* If there's a WCS form, try converting with the native locale. */ if ((aes->aes_set & AES_SET_WCS) - && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) { + && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs.s) != NULL) { aes->aes_set |= AES_SET_MBS; return (aes->aes_mbs.s); } /* We'll use UTF-8 for MBS if all else fails. */ - return (aes_get_utf8(aes)); + if (aes->aes_set & AES_SET_UTF8) + return (aes->aes_utf8.s); + if ((aes->aes_set & AES_SET_WCS) + && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs.s) != NULL) { + aes->aes_set |= AES_SET_UTF8; + return (aes->aes_utf8.s); + } + return (NULL); } static const wchar_t * aes_get_wcs(struct aes *aes) { - wchar_t *w; - size_t r; - /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) - return (aes->aes_wcs); - - if (aes->aes_set & AES_SET_MBS) { - /* Try converting MBS to WCS using native locale. */ - /* - * No single byte will be more than one wide character, - * so this length estimate will always be big enough. - */ - size_t wcs_length = aes->aes_mbs.length; - - w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); - if (w == NULL) - __archive_errx(1, "No memory for aes_get_wcs()"); - r = mbstowcs(w, aes->aes_mbs.s, wcs_length); - if (r != (size_t)-1 && r != 0) { - w[r] = 0; - aes->aes_set |= AES_SET_WCS; - return (aes->aes_wcs = w); - } - free(w); + return (aes->aes_wcs.s); + /* Try converting UTF8 to WCS. */ + if ((aes->aes_set & AES_SET_UTF8) + && !__archive_wstrappend_utf8(&(aes->aes_wcs), &(aes->aes_utf8))) { + aes->aes_set |= AES_SET_WCS; + return (aes->aes_wcs.s); } - - if (aes->aes_set & AES_SET_UTF8) { - /* Try converting UTF8 to WCS. */ - aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); - if (aes->aes_wcs != NULL) - aes->aes_set |= AES_SET_WCS; - return (aes->aes_wcs); + /* Try converting MBS to WCS using native locale. */ + if ((aes->aes_set & AES_SET_MBS) + && !__archive_wstrappend_mbs(&(aes->aes_wcs), &(aes->aes_mbs))) { + aes->aes_set |= AES_SET_WCS; + return (aes->aes_wcs.s); } return (NULL); } @@ -289,10 +251,7 @@ aes_copy_mbs(struct aes *aes, const char *mbs) aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strcpy(&(aes->aes_mbs), mbs); archive_string_empty(&(aes->aes_utf8)); - if (aes->aes_wcs) { - free((wchar_t *)(uintptr_t)aes->aes_wcs); - aes->aes_wcs = NULL; - } + archive_wstring_empty(&(aes->aes_wcs)); return (0); } @@ -319,10 +278,7 @@ aes_update_utf8(struct aes *aes, const char *utf8) /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); - if (aes->aes_wcs) { - free((wchar_t *)(uintptr_t)aes->aes_wcs); - aes->aes_wcs = NULL; - } + archive_wstring_empty(&(aes->aes_wcs)); aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ @@ -333,13 +289,12 @@ aes_update_utf8(struct aes *aes, const char *utf8) * succeed.) */ /* Try converting UTF8 to WCS, return false on failure. */ - aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); - if (aes->aes_wcs == NULL) + if (__archive_wstrappend_utf8(&(aes->aes_wcs), &(aes->aes_utf8))) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ /* Try converting WCS to MBS, return false on failure. */ - if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL) + if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs.s) == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; @@ -356,25 +311,13 @@ aes_copy_wcs(struct aes *aes, const wchar_t *wcs) static int aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) { - wchar_t *w; - if (wcs == NULL) { aes->aes_set = 0; - return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); - if (aes->aes_wcs) { - free((wchar_t *)(uintptr_t)aes->aes_wcs); - aes->aes_wcs = NULL; - } - w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); - if (w == NULL) - __archive_errx(1, "No memory for aes_copy_wcs()"); - wmemcpy(w, wcs, len); - w[len] = L'\0'; - aes->aes_wcs = w; + archive_wstrncpy(&(aes->aes_wcs), wcs, len); return (0); } diff --git a/libarchive/archive_entry_private.h b/libarchive/archive_entry_private.h index 3351baa33..e5d48efee 100644 --- a/libarchive/archive_entry_private.h +++ b/libarchive/archive_entry_private.h @@ -42,7 +42,7 @@ struct aes { struct archive_string aes_mbs; struct archive_string aes_utf8; - const wchar_t *aes_wcs; + struct archive_wstring aes_wcs; /* Bitmap of which of the above are valid. Because we're lazy * about malloc-ing and reusing the underlying storage, we * can't rely on NULL pointers to indicate whether a string diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 4e57d62e1..12e0f47b0 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -58,6 +58,17 @@ __archive_string_append(struct archive_string *as, const char *p, size_t s) return (as); } +struct archive_wstring * +__archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s) +{ + if (__archive_wstring_ensure(as, as->length + s + 1) == NULL) + __archive_errx(1, "Out of memory"); + memcpy(as->s + as->length, p, s * sizeof(wchar_t)); + as->s[as->length + s] = 0; + as->length += s; + return (as); +} + void __archive_string_copy(struct archive_string *dest, struct archive_string *src) { @@ -72,6 +83,20 @@ __archive_string_copy(struct archive_string *dest, struct archive_string *src) } } +void +__archive_wstring_copy(struct archive_wstring *dest, struct archive_wstring *src) +{ + if (src->length == 0) + dest->length = 0; + else { + if (__archive_wstring_ensure(dest, src->length + 1) == NULL) + __archive_errx(1, "Out of memory"); + memcpy(dest->s, src->s, src->length * sizeof(wchar_t)); + dest->length = src->length; + dest->s[dest->length] = 0; + } +} + void __archive_string_concat(struct archive_string *dest, struct archive_string *src) { @@ -95,6 +120,14 @@ __archive_string_free(struct archive_string *as) } } +struct archive_wstring * +__archive_wstring_ensure(struct archive_wstring *as, size_t s) +{ + return (struct archive_wstring *) + __archive_string_ensure((struct archive_string *)as, + s * sizeof(wchar_t)); +} + /* Returns NULL on any allocation failure. */ struct archive_string * __archive_string_ensure(struct archive_string *as, size_t s) @@ -165,6 +198,12 @@ __archive_strappend_char(struct archive_string *as, char c) return (__archive_string_append(as, &c, 1)); } +struct archive_wstring * +__archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c) +{ + return (__archive_wstring_append(as, &c, 1)); +} + /* * Translates a wide character string into UTF-8 and appends * to the archive_string. Note: returns NULL if conversion fails, @@ -283,38 +322,32 @@ utf8_to_unicode(int *pwc, const char *s, size_t n) * Return a wide-character Unicode string by converting this archive_string * from UTF-8. We assume that systems with 16-bit wchar_t always use * UTF16 and systems with 32-bit wchar_t can accept UCS4. + * Returns 0 on success, non-zero if conversion fails. */ -wchar_t * -__archive_string_utf8_w(struct archive_string *as) +int +__archive_wstrappend_utf8(struct archive_wstring *dest, struct archive_string *src) { - wchar_t *ws, *dest; int wc, wc2;/* Must be large enough for a 21-bit Unicode code point. */ - const char *src; + const char *p; int n; - ws = (wchar_t *)malloc((as->length + 1) * sizeof(wchar_t)); - if (ws == NULL) - __archive_errx(1, "Out of memory"); - dest = ws; - src = as->s; - while (*src != '\0') { - n = utf8_to_unicode(&wc, src, 8); + p = src->s; + while (*p != '\0') { + n = utf8_to_unicode(&wc, p, 8); if (n == 0) break; if (n < 0) { - free(ws); - return (NULL); + return (-1); } - src += n; + p += n; if (wc >= 0xDC00 && wc <= 0xDBFF) { /* This is a leading surrogate; some idiot * has translated UTF16 to UTF8 without combining * surrogates; rebuild the full code point before * continuing. */ - n = utf8_to_unicode(&wc2, src, 8); + n = utf8_to_unicode(&wc2, p, 8); if (n < 0) { - free(ws); - return (NULL); + return (-1); } if (n == 0) /* Ignore the leading surrogate */ break; @@ -323,10 +356,9 @@ __archive_string_utf8_w(struct archive_string *as) * trailing surrogate, then someone * has really screwed up and this is * invalid. */ - free(ws); - return (NULL); + return (-1); } else { - src += n; + p += n; wc -= 0xD800; wc *= 0x400; wc += wc2 - 0xDC00; @@ -337,13 +369,35 @@ __archive_string_utf8_w(struct archive_string *as) /* We have a code point that won't fit into a * wchar_t; convert it to a surrogate pair. */ wc -= 0x10000; - *dest++ = ((wc >> 10) & 0x3ff) + 0xD800; - *dest++ = (wc & 0x3ff) + 0xDC00; + __archive_wstrappend_wchar(dest, + ((wc >> 10) & 0x3ff) + 0xD800); + __archive_wstrappend_wchar(dest, + (wc & 0x3ff) + 0xDC00); } else - *dest++ = wc; + __archive_wstrappend_wchar(dest, wc); } - *dest = L'\0'; - return (ws); + return (0); +} + + +int +__archive_wstrappend_mbs(struct archive_wstring *dest, + struct archive_string *src) +{ + int r; + /* + * No single byte will be more than one wide character, + * so this length estimate will always be big enough. + */ + size_t wcs_length = src->length; + if (NULL == __archive_wstring_ensure(dest, wcs_length + 1)) + __archive_errx(1, "No memory for aes_get_wcs()"); + r = mbstowcs(dest->s, src->s, wcs_length); + if (r != (size_t)-1 && r != 0) { + dest->s[r] = 0; + return (0); + } + return (-1); } #if defined(_WIN32) && !defined(__CYGWIN__) diff --git a/libarchive/archive_string.h b/libarchive/archive_string.h index 782c1b162..ecaf5187c 100644 --- a/libarchive/archive_string.h +++ b/libarchive/archive_string.h @@ -63,6 +63,12 @@ struct archive_string { size_t buffer_length; /* Length of malloc-ed storage */ }; +struct archive_wstring { + wchar_t *s; /* Pointer to the storage */ + size_t length; /* Length of 's' in characters */ + size_t buffer_length; /* Length of malloc-ed storage */ +}; + /* Initialize an archive_string object on the stack or elsewhere. */ #define archive_string_init(a) \ do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0) @@ -86,12 +92,18 @@ __archive_strappend_w_mbs(struct archive_string *, const wchar_t *); /* Basic append operation. */ struct archive_string * __archive_string_append(struct archive_string *as, const char *p, size_t s); +struct archive_wstring * +__archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s); /* Copy one archive_string to another */ void __archive_string_copy(struct archive_string *dest, struct archive_string *src); -#define archive_string_copy(dest, src) \ - __archive_string_copy(dest, src) +#define archive_string_copy(dest, src) \ + __archive_string_copy((dest), (src)) +void +__archive_wstring_copy(struct archive_wstring *dest, struct archive_wstring *src); +#define archive_wstring_copy(dest, src) \ + __archive_wstring_copy((dest), (src)) /* Concatenate one archive_string to another */ void @@ -104,6 +116,10 @@ struct archive_string * __archive_string_ensure(struct archive_string *, size_t); #define archive_string_ensure __archive_string_ensure +struct archive_wstring * +__archive_wstring_ensure(struct archive_wstring *, size_t); +#define archive_wstring_ensure __archive_wstring_ensure + /* Append C string, which may lack trailing \0. */ /* The source is declared void * here because this gets used with * "signed char *", "unsigned char *" and "char *" arguments. @@ -118,21 +134,26 @@ __archive_strncat(struct archive_string *, const void *, size_t); /* Copy a C string to an archive_string, resizing as necessary. */ #define archive_strcpy(as,p) \ - ((as)->length = 0, __archive_string_append((as), (p), p == NULL ? 0 : strlen(p))) + archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p))) /* Copy a C string to an archive_string with limit, resizing as necessary. */ #define archive_strncpy(as,p,l) \ ((as)->length=0, archive_strncat((as), (p), (l))) +#define archive_wstrncpy(as,p,l) \ + ((as)->length = 0, __archive_wstring_append((as), (p), (l))) /* Return length of string. */ #define archive_strlen(a) ((a)->length) /* Set string length to zero. */ #define archive_string_empty(a) ((a)->length = 0) +#define archive_wstring_empty(a) ((a)->length = 0) /* Release any allocated storage resources. */ void __archive_string_free(struct archive_string *); #define archive_string_free __archive_string_free +#define archive_wstring_free(ws) \ + __archive_string_free((struct archive_string *)(ws)) /* Like 'vsprintf', but resizes the underlying string as necessary. */ void __archive_string_vsprintf(struct archive_string *, const char *, @@ -143,9 +164,14 @@ void __archive_string_sprintf(struct archive_string *, const char *, ...) __LA_PRINTF(2, 3); #define archive_string_sprintf __archive_string_sprintf -/* Allocates a fresh buffer and converts as (assumed to be UTF-8) into it. - * Returns NULL if conversion failed in any way. */ -wchar_t *__archive_string_utf8_w(struct archive_string *as); +/* Translates from UTF8 in src to Unicode in dest. */ +/* Returns non-zero if conversion failed in any way. */ +int __archive_wstrappend_utf8(struct archive_wstring *dest, + struct archive_string *src); +/* Translates from MBS in src to Unicode in dest. */ +/* Returns non-zero if conversion failed in any way. */ +int __archive_wstrappend_mbs(struct archive_wstring *dest, + struct archive_string *src); #endif