when it's available.
See Issue 66 and Issue 132 for more discussion.
SVN-Revision: 2992
CHECK_MD(SHA512 "LIBC;LIBC2;LIBC3;LIBSYSTEM;OPENSSL")
CHECK_HASH_WIN("MD5;SHA1;SHA256;SHA384;SHA512")
+#
+# Find iconv
+#
+LA_CHECK_INCLUDE_FILE("iconv.h" HAVE_ICONV_H)
+IF(HAVE_ICONV_H)
+ FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
+ENDIF(HAVE_ICONV_H)
+CHECK_FUNCTION_EXISTS(iconv HAVE_ICONV_IN_LIBC)
+IF(HAVE_ICONV_IN_LIBC)
+ SET(HAVE_ICONV true)
+ELSE(HAVE_ICONV_IN_LIBC)
+ # iconv isn't in libc, try libiconv
+ CHECK_LIBRARY_EXISTS(iconv iconv "" HAVE_ICONV_IN_LIBICONV)
+ IF(HAVE_ICONV_IN_LIBICONV)
+ SET(HAVE_ICONV true)
+ LIST(APPEND ADDITIONAL_LIBS "iconv")
+ ENDIF(HAVE_ICONV_IN_LIBICONV)
+ENDIF(HAVE_ICONV_IN_LIBC)
+
#
# Find Libxml2
#
LIST(APPEND ADDITIONAL_LIBS ${LIBXML2_LIBRARIES})
SET(HAVE_LIBXML2 1)
# libxml2's include files use iconv.h
- # We need a directory path of iconv.h so that it won't fail to check
- # "libxml/xmlreader.h".
- FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
- INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
SET(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR})
CHECK_INCLUDE_FILES("libxml/xmlreader.h" HAVE_LIBXML_XMLREADER_H)
CHECK_INCLUDE_FILES("libxml/xmlwriter.h" HAVE_LIBXML_XMLWRITER_H)
IF(EXPAT_FOUND)
INCLUDE_DIRECTORIES(${EXPAT_INCLUDE_DIR})
LIST(APPEND ADDITIONAL_LIBS ${EXPAT_LIBRARIES})
- SET(HAVE_LIBEXPAT 1)
+ SET(HAVE_LIBEXPAT 1)
LA_CHECK_INCLUDE_FILE("expat.h" HAVE_EXPAT_H)
ENDIF(EXPAT_FOUND)
ENDIF(LIBXML2_FOUND)
#
CHECK_FILE_OFFSET_BITS()
-
-
#
# Check for Extended Attribute libraries, headers, and functions
#
LA_CHECK_INCLUDE_FILE(sys/xattr.h HAVE_SYS_XATTR_H)
LA_CHECK_INCLUDE_FILE(sys/extattr.h HAVE_SYS_EXTATTR_H)
CHECK_LIBRARY_EXISTS(attr "setxattr" "" HAVE_ATTR_LIB)
- IF(HAVE_ATTR_LIB)
+ IF(HAVE_ATTR_LIB)
SET(CMAKE_REQUIRED_LIBRARIES "attr")
ENDIF(HAVE_ATTR_LIB)
CHECK_SYMBOL_EXISTS(EXTATTR_NAMESPACE_USER "sys/types.h;sys/extattr.h" HAVE_DECL_EXTATTR_NAMESPACE_USER)
/* Define to 1 if you have the <grp.h> header file. */
#cmakedefine HAVE_GRP_H 1
+/* Define to 1 if you have the `iconv' function. */
+#cmakedefine HAVE_ICONV 1
+
+/* Define to 1 if you have the <iconv.h> header file. */
+#cmakedefine HAVE_ICONV_H 1
+
/* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine HAVE_INTTYPES_H 1
AC_HEADER_DIRENT
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h copyfile.h ctype.h errno.h])
-AC_CHECK_HEADERS([ext2fs/ext2_fs.h fcntl.h grp.h])
+AC_CHECK_HEADERS([ext2fs/ext2_fs.h fcntl.h grp.h iconv.h])
AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h])
AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h])
AC_CHECK_HEADERS([locale.h paths.h poll.h pwd.h regex.h signal.h stdarg.h])
AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork])
AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate futimens futimes])
AC_CHECK_FUNCS([geteuid getpid getgrgid_r getgrnam_r])
-AC_CHECK_FUNCS([getpwnam_r getpwuid_r getvfsbyname gmtime_r])
+AC_CHECK_FUNCS([getpwnam_r getpwuid_r getvfsbyname gmtime_r iconv])
AC_CHECK_FUNCS([lchflags lchmod lchown link localtime_r lstat])
AC_CHECK_FUNCS([lutimes mbrtowc memmove memset mkdir mkfifo mknod mkstemp])
AC_CHECK_FUNCS([nl_langinfo openat pipe poll readlink readlinkat])
* standard permissions and include them in the returned list.
*/
int
-archive_acl_next(struct archive_acl *acl, int want_type, int *type,
+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type, int *type,
int *permset, int *tag, int *id, const char **name)
{
*name = NULL;
*permset = acl->acl_p->permset;
*tag = acl->acl_p->tag;
*id = acl->acl_p->id;
- *name = archive_mstring_get_mbs(&acl->acl_p->name);
+ *name = archive_mstring_get_mbs(a, &acl->acl_p->name);
acl->acl_p = acl->acl_p->next;
return (ARCHIVE_OK);
}
* the style of the generated ACL.
*/
const wchar_t *
-archive_acl_text_w(struct archive_acl *acl, int flags)
+archive_acl_text_w(struct archive *a, struct archive_acl *acl, int flags)
{
int count;
size_t length;
length += 8; /* "default:" */
length += 5; /* tag name */
length += 1; /* colon */
- wname = archive_mstring_get_wcs(&ap->name);
+ wname = archive_mstring_get_wcs(a, &ap->name);
if (wname != NULL)
length += wcslen(wname);
else
ap = acl->acl_head;
while (ap != NULL) {
if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
- wname = archive_mstring_get_wcs(&ap->name);
+ wname = archive_mstring_get_wcs(a, &ap->name);
*wp++ = separator;
if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
id = ap->id;
count = 0;
while (ap != NULL) {
if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
- wname = archive_mstring_get_wcs(&ap->name);
+ wname = archive_mstring_get_wcs(a, &ap->name);
if (count > 0)
*wp++ = separator;
if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
void archive_acl_copy(struct archive_acl *, struct archive_acl *);
int archive_acl_count(struct archive_acl *, int);
int archive_acl_reset(struct archive_acl *, int);
-int archive_acl_next(struct archive_acl *, int,
+int archive_acl_next(struct archive *, struct archive_acl *, int,
int *, int *, int *, int *, const char **);
int archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *);
int archive_acl_add_entry_w_len(struct archive_acl *,
int, int, int, int, const wchar_t *, size_t);
-const wchar_t *archive_acl_text_w(struct archive_acl *, int);
+const wchar_t *archive_acl_text_w(struct archive *, struct archive_acl *, int);
/*
* Private ACL parser. This is private because it handles some
const void *p;
/* Allocate new structure and copy over all of the fields. */
- entry2 = archive_entry_new();
+ /* TODO: Should we copy the archive over? Or require a new archive
+ * as an argument? */
+ entry2 = archive_entry_new2(entry->archive);
if (entry2 == NULL)
return (NULL);
entry2->ae_stat = entry->ae_stat;
entry2->ae_fflags_set = entry->ae_fflags_set;
entry2->ae_fflags_clear = entry->ae_fflags_clear;
+ /* TODO: XXX If clone can have a different archive, what do we do here if
+ * character sets are different? XXX */
archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);
archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
struct archive_entry *
archive_entry_new(void)
+{
+ return archive_entry_new2(NULL);
+}
+
+struct archive_entry *
+archive_entry_new2(struct archive *a)
{
struct archive_entry *entry;
if (entry == NULL)
return (NULL);
memset(entry, 0, sizeof(*entry));
+ entry->archive = a;
return (entry);
}
const char *f;
char *p;
- f = archive_mstring_get_mbs(&entry->ae_fflags_text);
+ f = archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text);
if (f != NULL)
return (f);
archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
free(p);
- f = archive_mstring_get_mbs(&entry->ae_fflags_text);
+ f = archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text);
return (f);
}
const char *
archive_entry_gname(struct archive_entry *entry)
{
- return (archive_mstring_get_mbs(&entry->ae_gname));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_gname));
}
const wchar_t *
archive_entry_gname_w(struct archive_entry *entry)
{
- return (archive_mstring_get_wcs(&entry->ae_gname));
+ return (archive_mstring_get_wcs(entry->archive, &entry->ae_gname));
}
const char *
archive_entry_hardlink(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_HARDLINK)
- return (archive_mstring_get_mbs(&entry->ae_hardlink));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_hardlink));
return (NULL);
}
archive_entry_hardlink_w(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_HARDLINK)
- return (archive_mstring_get_wcs(&entry->ae_hardlink));
+ return (archive_mstring_get_wcs(entry->archive, &entry->ae_hardlink));
return (NULL);
}
const char *
archive_entry_pathname(struct archive_entry *entry)
{
- return (archive_mstring_get_mbs(&entry->ae_pathname));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_pathname));
}
const wchar_t *
archive_entry_pathname_w(struct archive_entry *entry)
{
- return (archive_mstring_get_wcs(&entry->ae_pathname));
+ return (archive_mstring_get_wcs(entry->archive, &entry->ae_pathname));
}
mode_t
const char *
archive_entry_sourcepath(struct archive_entry *entry)
{
- return (archive_mstring_get_mbs(&entry->ae_sourcepath));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_sourcepath));
}
const char *
archive_entry_symlink(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_SYMLINK)
- return (archive_mstring_get_mbs(&entry->ae_symlink));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_symlink));
return (NULL);
}
archive_entry_symlink_w(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_SYMLINK)
- return (archive_mstring_get_wcs(&entry->ae_symlink));
+ return (archive_mstring_get_wcs(entry->archive, &entry->ae_symlink));
return (NULL);
}
const char *
archive_entry_uname(struct archive_entry *entry)
{
- return (archive_mstring_get_mbs(&entry->ae_uname));
+ return (archive_mstring_get_mbs(entry->archive, &entry->ae_uname));
}
const wchar_t *
archive_entry_uname_w(struct archive_entry *entry)
{
- return (archive_mstring_get_wcs(&entry->ae_uname));
+ return (archive_mstring_get_wcs(entry->archive, &entry->ae_uname));
}
/*
int
archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
{
- return (archive_mstring_update_utf8(&entry->ae_gname, name));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name));
}
#if ARCHIVE_VERSION_NUMBER < 3000000
entry->ae_set |= AE_SET_HARDLINK;
else
entry->ae_set &= ~AE_SET_HARDLINK;
- return (archive_mstring_update_utf8(&entry->ae_hardlink, target));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target));
}
void
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
{
if (entry->ae_set & AE_SET_SYMLINK)
- return (archive_mstring_update_utf8(&entry->ae_symlink, target));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target));
else
- return (archive_mstring_update_utf8(&entry->ae_hardlink, target));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target));
}
void
int
archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
{
- return (archive_mstring_update_utf8(&entry->ae_pathname, name));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_pathname, name));
}
void
entry->ae_set |= AE_SET_SYMLINK;
else
entry->ae_set &= ~AE_SET_SYMLINK;
- return (archive_mstring_update_utf8(&entry->ae_symlink, linkname));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname));
}
#if ARCHIVE_VERSION_NUMBER < 3000000
int
archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
{
- return (archive_mstring_update_utf8(&entry->ae_uname, name));
+ return (archive_mstring_update_utf8(entry->archive, &entry->ae_uname, name));
}
const void *
archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
int *permset, int *tag, int *id, const char **name)
{
- return archive_acl_next(&entry->acl, want_type, type, permset, tag, id, name);
+ return archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name);
}
/*
const wchar_t *
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
{
- return archive_acl_text_w(&entry->acl, flags);
+ return archive_acl_text_w(entry->archive, &entry->acl, flags);
}
/*
* applications (e.g., a package manager could attach special
* package-management attributes to each entry).
*/
+struct archive;
struct archive_entry;
/*
__LA_DECL void archive_entry_free(struct archive_entry *);
__LA_DECL struct archive_entry *archive_entry_new(void);
+/*
+ * This form of archive_entry_new2() will pull character-set
+ * conversion information from the specified archive handle. The
+ * older archive_entry_new(void) form is equivalent to calling
+ * archive_entry_new2(NULL) and will result in the use of an internal
+ * default character-set conversion.
+ */
+__LA_DECL struct archive_entry *archive_entry_new2(struct archive *);
+
/*
* Retrieve fields from an archive_entry.
*
* TODO: Design a good API for handling sparse files.
*/
struct archive_entry {
+ struct archive *archive;
+
/*
* Note that ae_stat.st_mode & AE_IFMT can be 0!
*
#ifndef ARCHIVE_PRIVATE_H_INCLUDED
#define ARCHIVE_PRIVATE_H_INCLUDED
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+
#include "archive.h"
#include "archive_string.h"
int archive_error_number;
const char *error;
struct archive_string error_string;
+
+#if HAVE_ICONV
+ iconv_t unicode_to_current;
+ iconv_t current_to_unicode;
+ char *current_code;
+#endif
};
/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */
a->archive.magic = ARCHIVE_READ_MAGIC;
a->archive.state = ARCHIVE_STATE_NEW;
- a->entry = archive_entry_new();
+ a->entry = archive_entry_new2(&a->archive);
a->archive.vtable = archive_read_vtable();
return (&a->archive);
if (a->entry)
archive_entry_free(a->entry);
a->archive.magic = 0;
+ __archive_clean(&a->archive);
free(a);
return (r);
}
(a->cleanup_uname)(a->lookup_uname_data);
archive_string_free(&a->archive.error_string);
a->archive.magic = 0;
+ __archive_clean(&a->archive);
free(a);
return (r);
}
/* Convert a path separator '\' -> '/' */
static void
-cab_convert_path_separator(struct cab *cab, struct archive_string *pathname,
- unsigned char attr)
+cab_convert_path_separator(struct archive_read *a, struct cab *cab,
+ struct archive_string *pathname, unsigned char attr)
{
int l, r;
return;
if ((attr & ATTR_NAME_IS_UTF) != 0 ||
- archive_wstrcpy_mbs(&(cab->ws), pathname) != 0) {
+ archive_wstrcpy_mbs(&a->archive, &(cab->ws), pathname) != 0) {
for (l = 0; pathname->s[l] != '\0'; l++) {
if (pathname->s[l] == '\\')
pathname->s[l] = '/';
}
if (r) {
archive_string_empty(&cab->mbs);
- archive_strappend_w_mbs(&cab->mbs, cab->ws.s);
+ archive_strappend_w_mbs(&a->archive, &cab->mbs, cab->ws.s);
/* If mbs length is different to pathname, we broke the
* pathname. We shouldn't use it. */
if (archive_strlen(&cab->mbs) == archive_strlen(pathname))
__archive_read_consume(a, len + 1);
cab->cab_offset += len + 1;
/* Convert a path separator '\' -> '/' */
- cab_convert_path_separator(cab, &(file->pathname),
- file->attr);
+ cab_convert_path_separator(a, cab, &(file->pathname), file->attr);
/*
* Sanity check if each data is acceptable.
* set for a filename in an archive.
*/
static void
-lha_replace_path_separator(struct lha *lha, struct archive_string *fn)
+lha_replace_path_separator(struct archive_read *a, struct lha *lha, struct archive_string *fn)
{
size_t i;
*/
/* If converting to wide character failed, force a replacement. */
- if (!archive_wstrcpy_mbs(&(lha->ws), fn)) {
+ if (!archive_wstrcpy_mbs(&a->archive, &(lha->ws), fn)) {
for (i = 0; i < archive_strlen(fn); i++) {
if (fn->s[i] == '\\')
fn->s[i] = '/';
* Sanity check that we surely did not break a filename.
*/
archive_string_empty(&(lha->mbs));
- archive_strappend_w_mbs(&(lha->mbs), lha->ws.s);
+ archive_strappend_w_mbs(&a->archive, &(lha->mbs), lha->ws.s);
/* If mbs length is different to fn, we broke the
* filename and we shouldn't use it. */
if (archive_strlen(&(lha->mbs)) == archive_strlen(fn))
return (truncated_error(a));
archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);
- lha_replace_path_separator(lha, &lha->filename);
+ lha_replace_path_separator(a, lha, &lha->filename);
lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);
sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
* without incurring additional memory allocations.
*/
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include "archive_private.h"
#include "archive_string.h"
+#if SIZEOF_WCHAR_T == 2
+#define __LA_UNICODE "UCS-2-INTERNAL"
+#else
+#define __LA_UNICODE "UCS-4-INTERNAL"
+#endif
+
static struct archive_string *
archive_string_append(struct archive_string *as, const char *p, size_t s)
{
}
/*
- * Translates a wide character string into UTF-8 and appends
- * to the archive_string. Note: returns NULL if conversion fails,
- * but still leaves a best-effort conversion in the argument as.
+ * Character set conversion functions.
+ *
+ * Conversions involving MBS use the "current locale" as
+ * stored in the archive object passed in. Conversions
+ * between UTF8 and Unicode do not require any such help,
+ * of course.
+ *
+ * TODO: Provide utility functions to set the current locale.
+ * Remember that Windows stores character encoding as an int,
+ * POSIX typically uses a string. I'd rather not bother trying
+ * to translate between the two.
+ */
+
+/*
+ * Unicode to UTF-8.
+ * Note: returns NULL if conversion fails, but still leaves a best-effort
+ * conversion in the argument as.
*/
struct archive_string *
archive_strappend_w_utf8(struct archive_string *as, const wchar_t *w)
return (return_val);
}
+/*
+ * UTF-8 ===> Unicode
+ *
+ */
+
+/*
+ * Utility to convert a single UTF-8 sequence.
+ */
static int
utf8_to_unicode(int *pwc, const char *s, size_t n)
{
return (0);
}
+#if HAVE_ICONV
+
+/*
+ * Get the "current character set" name to use with iconv.
+ * On FreeBSD, the empty character set name "" chooses
+ * the correct character encoding for the current locale,
+ * so this isn't necessary.
+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;
+ * on that system, we have to explicitly call nl_langinfo()
+ * to get the right name. Not sure about other platforms.
+ */
+static const char *
+default_iconv_charset(const char *charset) {
+ if (charset != NULL && charset[0] != '\0')
+ return charset;
+#if HAVE_NL_LANGINFO
+ return nl_langinfo(CODESET);
+#else
+ return "";
+#endif
+}
+/*
+ * Translates from the current locale character set to Unicode
+ * and appends to the archive_wstring. Note: returns NULL if conversion
+ * fails.
+ *
+ * This version uses the POSIX iconv() function.
+ */
int
-archive_wstrcpy_mbs(struct archive_wstring *dest,
+archive_wstrcpy_mbs(struct archive *a, struct archive_wstring *dest, struct archive_string *src)
+{
+ char *p;
+ char buff[256];
+ size_t remaining;
+ iconv_t cd;
+
+ if (a == NULL) {
+ cd = iconv_open(__LA_UNICODE, default_iconv_charset(NULL));
+ } else if (a->unicode_to_current == (iconv_t)(0))
+ cd = iconv_open(__LA_UNICODE, default_iconv_charset(a->current_code));
+ else {
+ /* Use the cached conversion descriptor after resetting it. */
+ cd = a->current_to_unicode;
+ iconv(cd, NULL, NULL, NULL, NULL);
+ }
+ if (cd == (iconv_t)(-1)) {
+ /* XXX do something here XXX */
+ return -1;
+ }
+
+ p = src->s;
+ remaining = archive_strlen(src);
+ for (;;) {
+ size_t avail = sizeof(buff);
+ char *outp = buff;
+ size_t result = iconv(cd, &p, &remaining, &outp, &avail);
+
+ if (avail < sizeof(buff))
+ archive_wstring_append(dest,
+ (const wchar_t *)buff,
+ (sizeof(buff) - avail) / sizeof(wchar_t));
+ if (result != (size_t)-1) {
+ break; /* Conversion completed. */
+ } else if (errno == EILSEQ) {
+ /* Skip the illegal input wchar. */
+ archive_wstrappend_wchar(dest, L'?');
+ archive_wstrappend_wchar(dest, p[0]);
+ p += 1;
+ remaining -= 1;
+ } else if (errno == E2BIG) {
+ /* Flush the output and do some more. */
+ continue;
+ } else if (errno == EINVAL) {
+ /* Final character is invalid. */
+ archive_wstrappend_wchar(dest, L'?');
+ archive_wstrappend_wchar(dest, p[0]);
+ break;
+ }
+ }
+ /* Dispose of the conversion descriptor or cache it. */
+ if (a == NULL)
+ iconv_close(cd);
+ else if (a->current_to_unicode == (iconv_t)(0))
+ a->current_to_unicode = cd;
+ return (0);
+}
+
+
+#else
+
+/*
+ * Convert MBS to Unicode.
+ */
+int
+archive_wstrcpy_mbs(struct archive *a, struct archive_wstring *dest,
struct archive_string *src)
{
size_t r;
return (-1);
}
+#endif
+
#if defined(_WIN32) && !defined(__CYGWIN__)
/*
- * Translates a wide character string into current locale character set
- * and appends to the archive_string. Note: returns NULL if conversion
- * fails.
+ * Unicode ==> MBS.
+ * Note: returns NULL if conversion fails.
*
* Win32 builds use WideCharToMultiByte from the Windows API.
* (Maybe Cygwin should too? WideCharToMultiByte will know a
* wrapper is going to know.)
*/
struct archive_string *
-archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
+archive_strappend_w_mbs(struct archive *a, struct archive_string *as, const wchar_t *w)
{
char *p;
int l, wl;
BOOL useDefaultChar = FALSE;
+ /* TODO: XXX use codepage preference from a XXX */
+ (void)a; /* UNUSED */
+
wl = (int)wcslen(w);
l = wl * 4 + 4;
p = malloc(l);
return (as);
}
+#elif HAVE_ICONV
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string. Note: returns NULL if conversion
+ * fails.
+ *
+ * This version uses the POSIX iconv() function.
+ */
+struct archive_string *
+archive_strappend_w_mbs(struct archive *a, struct archive_string *as, const wchar_t *w)
+{
+ char *p;
+ char buff[256];
+ size_t remaining;
+ iconv_t cd;
+
+ if (a == NULL)
+ cd = iconv_open(default_iconv_charset(""), __LA_UNICODE);
+ else if (a->unicode_to_current == (iconv_t)(0))
+ cd = iconv_open(default_iconv_charset(a->current_code), __LA_UNICODE);
+ else {
+ /* Use the cached conversion descriptor after resetting it. */
+ cd = a->unicode_to_current;
+ iconv(cd, NULL, NULL, NULL, NULL);
+ }
+ if (cd == (iconv_t)(-1)) {
+ /* XXX do something here XXX */
+ return NULL;
+ }
+
+ p = (char *)(uintptr_t)w;
+ remaining = wcslen(w) * sizeof(wchar_t);
+ for (;;) {
+ size_t avail = sizeof(buff);
+ char *outp = buff;
+ size_t result = iconv(cd, &p, &remaining, &outp, &avail);
+
+ if (avail < sizeof(buff))
+ archive_string_append(as, buff, sizeof(buff) - avail);
+ if (result >= 0) {
+ break; /* Conversion completed. */
+ } else if (errno == EILSEQ) {
+ /* Skip the illegal input wchar. */
+ archive_strappend_char(as, '?');
+ p += sizeof(wchar_t);
+ remaining -= sizeof(wchar_t);
+ } else if (errno == E2BIG) {
+ /* Flush the output and do some more. */
+ continue;
+ } else if (errno == EINVAL) {
+ /* Final character is invalid. */
+ archive_strappend_char(as, '?');
+ break;
+ }
+ }
+ /* Dispose of the conversion descriptor or cache it. */
+ if (a == NULL)
+ iconv_close(cd);
+ else if (a->unicode_to_current == (iconv_t)(0))
+ a->unicode_to_current = cd;
+ return (as);
+}
+
+
#else
/*
* either of these, fall back to the built-in UTF8 conversion.
*/
struct archive_string *
-archive_strappend_w_mbs(struct archive_string *as, const wchar_t *w)
+archive_strappend_w_mbs(struct archive *a, struct archive_string *as, const wchar_t *w)
{
#if !defined(HAVE_WCTOMB) && !defined(HAVE_WCRTOMB)
/* If there's no built-in locale support, fall back to UTF8 always. */
}
const char *
-archive_mstring_get_mbs(struct archive_mstring *aes)
+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes)
+{
+ const wchar_t *wc;
+
+ /* If we already have a UTF8 form, return that immediately. */
+ if (aes->aes_set & AES_SET_UTF8)
+ return (aes->aes_utf8.s);
+ /* If there's a Unicode form, convert that. */
+ 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);
+ }
+ /* TODO: Convert MBS to UTF8 in one step instead of two. */
+ wc = archive_mstring_get_wcs(a, aes);
+ if (wc != NULL) {
+ archive_string_empty(&(aes->aes_utf8));
+ archive_strappend_w_utf8(&(aes->aes_utf8), wc);
+ aes->aes_set |= AES_SET_UTF8;
+ return (aes->aes_utf8.s);
+ }
+ return (NULL);
+}
+
+const char *
+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes)
{
/* If we already have an MBS form, return that immediately. */
if (aes->aes_set & AES_SET_MBS)
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.s) != NULL) {
+ && archive_strappend_w_mbs(a, &(aes->aes_mbs), aes->aes_wcs.s) != NULL) {
aes->aes_set |= AES_SET_MBS;
return (aes->aes_mbs.s);
}
}
const wchar_t *
-archive_mstring_get_wcs(struct archive_mstring *aes)
+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes)
{
/* Return WCS form if we already have it. */
if (aes->aes_set & AES_SET_WCS)
}
/* Try converting MBS to WCS using native locale. */
if ((aes->aes_set & AES_SET_MBS)
- && !archive_wstrcpy_mbs(&(aes->aes_wcs), &(aes->aes_mbs))) {
+ && !archive_wstrcpy_mbs(a, &(aes->aes_wcs), &(aes->aes_mbs))) {
aes->aes_set |= AES_SET_WCS;
return (aes->aes_wcs.s);
}
* usable values even if some of the character conversions are failing.)
*/
int
-archive_mstring_update_utf8(struct archive_mstring *aes, const char *utf8)
+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes, const char *utf8)
{
if (utf8 == NULL) {
aes->aes_set = 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.s) == NULL)
+ if (archive_strappend_w_mbs(a, &(aes->aes_mbs), aes->aes_wcs.s) == NULL)
return (0);
aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
/* Convert a Unicode string to current locale and append the result. */
/* Returns NULL if conversion fails. */
struct archive_string *
-archive_strappend_w_mbs(struct archive_string *, const wchar_t *);
+archive_strappend_w_mbs(struct archive *, struct archive_string *, const wchar_t *);
/* Copy one archive_string to another */
#define archive_string_copy(dest, src) \
/* Translates from MBS in src to Unicode in dest. */
/* Returns non-zero if conversion failed in any way. */
-int archive_wstrcpy_mbs(struct archive_wstring *dest,
- struct archive_string *src);
+int archive_wstrcpy_mbs(struct archive *,
+ struct archive_wstring *dest, struct archive_string *src);
/* A "multistring" can hold Unicode, UTF8, or MBS versions of
void archive_mstring_clean(struct archive_mstring *);
void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);
-const char * archive_mstring_get_mbs(struct archive_mstring *);
-const wchar_t * archive_mstring_get_wcs(struct archive_mstring *);
+const char * archive_mstring_get_mbs(struct archive *, struct archive_mstring *);
+const char * archive_mstring_get_utf8(struct archive *, struct archive_mstring *);
+const wchar_t * archive_mstring_get_wcs(struct archive *, struct archive_mstring *);
int archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
+int archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);
int archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);
int archive_mstring_copy_wcs_len(struct archive_mstring *, const wchar_t *wcs, size_t);
-int archive_mstring_update_utf8(struct archive_mstring *aes, const char *utf8);
+int archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);
#endif
switch(long_flag) {
case 'l':
pw = va_arg(ap, wchar_t *);
- archive_strappend_w_mbs(as, pw);
+ archive_strappend_w_mbs(NULL, as, pw);
break;
default:
p2 = va_arg(ap, char *);
break;
case 'S':
pw = va_arg(ap, wchar_t *);
- archive_strappend_w_mbs(as, pw);
+ archive_strappend_w_mbs(NULL, as, pw);
break;
case 'o': case 'u': case 'x': case 'X':
/* Common handling for unsigned integer formats. */
#include "archive_private.h"
#include "archive_string.h"
-#if ARCHIVE_VERSION_NUMBER < 3000000
-/* These disappear in libarchive 3.0 */
-/* Deprecated. */
-int
-archive_api_feature(void)
-{
- return (ARCHIVE_API_FEATURE);
-}
-
-/* Deprecated. */
-int
-archive_api_version(void)
-{
- return (ARCHIVE_API_VERSION);
-}
-
-/* Deprecated synonym for archive_version_number() */
-int
-archive_version_stamp(void)
-{
- return (archive_version_number());
-}
-
-/* Deprecated synonym for archive_version_string() */
-const char *
-archive_version(void)
+/* Generic initialization of 'struct archive' objects. */
+void
+__archive_clean(struct archive *a)
{
- return (archive_version_string());
-}
+#if HAVE_ICONV
+ free(a->current_code);
+ if (a->unicode_to_current != (iconv_t)0)
+ iconv_close(a->unicode_to_current);
+ if (a->current_to_unicode != (iconv_t)0)
+ iconv_close(a->current_to_unicode);
#endif
+}
int
archive_version_number(void)
free((void *)(uintptr_t)(const void *)a->nulls);
archive_string_free(&a->archive.error_string);
a->archive.magic = 0;
+ __archive_clean(&a->archive);
free(a);
return (r);
}
archive_string_free(&a->archive.error_string);
archive_string_free(&a->path_safe);
a->archive.magic = 0;
+ __archive_clean(&a->archive);
free(a);
return (ret);
}
int er;
struct archive_entry *trailer;
- trailer = archive_entry_new();
+ trailer = archive_entry_new2(NULL);
/* nlink = 1 here for GNU cpio compat. */
archive_entry_set_nlink(trailer, 1);
archive_entry_set_size(trailer, 0);
linkname = archive_entry_symlink(entry);
if (linkname != NULL && strlen(linkname) > GNUTAR_linkname_size) {
size_t todo = strlen(linkname);
- struct archive_entry *temp = archive_entry_new();
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
/* Uname/gname here don't really matter since noone reads them;
* these are the values that GNU tar happens to use on FreeBSD. */
if (strlen(archive_entry_pathname(entry)) > GNUTAR_name_size) {
const char *pathname = archive_entry_pathname(entry);
size_t todo = strlen(pathname);
- struct archive_entry *temp = archive_entry_new();
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
/* Uname/gname here don't really matter since noone reads them;
* these are the values that GNU tar happens to use on FreeBSD. */
static void isofile_init_entry_list(struct iso9660 *);
static void isofile_add_entry(struct iso9660 *, struct isofile *);
static void isofile_free_all_entries(struct iso9660 *);
-static struct isofile * isofile_new(struct archive_entry *);
+static struct isofile * isofile_new(struct archive_write *, struct archive_entry *);
static void isofile_free(struct isofile *);
static void isofile_gen_utility_names(struct isofile *);
#ifdef HAVE_ZLIB_H
struct isoent **, struct isoent *);
static void _isoent_free(struct isoent *isoent);
static void isoent_free_all(struct isoent *);
-static struct isoent * isoent_create_virtual_dir(struct iso9660 *,
+static struct isoent * isoent_create_virtual_dir(struct archive_write *, struct iso9660 *,
const char *);
static int isoent_cmp_node(const struct archive_rb_node *,
const struct archive_rb_node *);
/* Create the root directory. */
iso9660->primary.rootent =
- isoent_create_virtual_dir(iso9660, "");
+ isoent_create_virtual_dir(a, iso9660, "");
if (iso9660->primary.rootent == NULL) {
free(iso9660);
archive_set_error(&a->archive, ENOMEM,
iso9660->need_multi_extent = 1;
}
- file = isofile_new(entry);
+ file = isofile_new(a, entry);
if (file == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate data");
}
static struct isofile *
-isofile_new(struct archive_entry *entry)
+isofile_new(struct archive_write *a, struct archive_entry *entry)
{
struct isofile *file;
if (entry != NULL)
file->entry = archive_entry_clone(entry);
else
- file->entry = archive_entry_new();
+ file->entry = archive_entry_new2(&a->archive);
if (file->entry == NULL) {
free(file);
return (NULL);
}
static struct isoent *
-isoent_create_virtual_dir(struct iso9660 *iso9660, const char *pathname)
+isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)
{
struct isofile *file;
struct isoent *isoent;
- file = isofile_new(NULL);
+ file = isofile_new(a, NULL);
if (file == NULL)
return (NULL);
archive_entry_set_pathname(file->entry, pathname);
as.s[as.length-1] = '\0';
as.length--;
}
- vp = isoent_create_virtual_dir(iso9660, as.s);
+ vp = isoent_create_virtual_dir(a, iso9660, as.s);
if (vp == NULL) {
archive_string_free(&as);
archive_set_error(&a->archive, ENOMEM,
/* There isn't rr_move entry.
* Create rr_move entry and insert it into the root entry.
*/
- rrmoved = isoent_create_virtual_dir(iso9660, "rr_moved");
+ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");
if (rrmoved == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
/*
* Create the entry which is the "boot.catalog" file.
*/
- file = isofile_new(NULL);
+ file = isofile_new(a, NULL);
if (file == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
const char *oname;
char *name, *bname;
size_t name_length;
- struct archive_entry *extra = archive_entry_new();
+ struct archive_entry *extra = archive_entry_new2(&a->archive);
oname = archive_entry_pathname(entry_original);
name_length = strlen(oname);
int64_t uid, gid;
int mode;
- pax_attr_entry = archive_entry_new();
+ pax_attr_entry = archive_entry_new2(&a->archive);
p = entry_name.s;
archive_entry_set_pathname(pax_attr_entry,
build_pax_attribute_name(pax_entry_name, p));
static int xar_close(struct archive_write *);
static int xar_free(struct archive_write *);
-static struct file *file_new(struct archive_entry *);
+static struct file *file_new(struct archive_write *a, struct archive_entry *);
static void file_free(struct file *);
-static struct file *file_create_virtual_dir(struct xar *,
+static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
const char *);
static int file_add_child_tail(struct file *, struct file *);
static struct file *file_find_child(struct file *, const char *);
/*
* Create the root directory.
*/
- xar->root = file_create_virtual_dir(xar, "");
+ xar->root = file_create_virtual_dir(a, xar, "");
if (xar->root == NULL) {
free(xar);
archive_set_error(&a->archive, ENOMEM,
xar->cur_file = NULL;
xar->bytes_remaining = 0;
- file = file_new(entry);
+ file = file_new(a, entry);
if (file == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate data");
}
static struct file *
-file_new(struct archive_entry *entry)
+file_new(struct archive_write *a, struct archive_entry *entry)
{
struct file *file;
static const struct archive_rb_tree_ops rb_ops = {
if (entry != NULL)
file->entry = archive_entry_clone(entry);
else
- file->entry = archive_entry_new();
+ file->entry = archive_entry_new2(&a->archive);
if (file->entry == NULL) {
free(file);
return (NULL);
}
static struct file *
-file_create_virtual_dir(struct xar *xar, const char *pathname)
+file_create_virtual_dir(struct archive_write *a, struct xar *xar, const char *pathname)
{
struct file *file;
- file = file_new(NULL);
+ file = file_new(a, NULL);
if (file == NULL)
return (NULL);
archive_entry_set_pathname(file->entry, pathname);
as.s[as.length-1] = '\0';
as.length--;
}
- vp = file_create_virtual_dir(xar, as.s);
+ vp = file_create_virtual_dir(a, xar, as.s);
if (vp == NULL) {
archive_string_free(&as);
archive_set_error(&a->archive, ENOMEM,
DEFINE_TEST(test_pax_filename_encoding_freebsd)
{
-#if !defined(__FreeBSD__)
- /* TODO: This test can probably be enabled on other systems as well. */
- skipping("FreeBSD-specific locale test");
-#else
struct archive *a;
struct archive_entry *entry;
char buff[4096];
/* Above three characters in KOI8-R should translate to the following
* three characters (two bytes each) in UTF-8. */
assertEqualMem(buff + 512, "15 path=\xD0\xBF\xD1\x80\xD0\xB8\x0A", 15);
-#endif
}