From: Eric Biggers Date: Sat, 23 Mar 2013 21:04:41 +0000 (-0500) Subject: Char encoding updates and misc. fixes X-Git-Tag: v1.3.2~22 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=650997e4865a090b6856c7ca34b02f42994e8e29 Char encoding updates and misc. fixes --- diff --git a/Makefile.am b/Makefile.am index 8592d076..3ac56d43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,9 +1,8 @@ ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS = -I$(top_srcdir)/src $(WINDOWS_CPPFLAGS) - -AM_CFLAGS = -std=gnu99 -D_LARGEFILE_SOURCE \ - -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +AM_CPPFLAGS = -I$(top_srcdir)/src $(WINDOWS_CPPFLAGS) \ + -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +AM_CFLAGS = -std=gnu99 lib_LTLIBRARIES = libwim.la @@ -52,6 +51,7 @@ libwim_la_SOURCES = \ src/verify.c \ src/wim.c \ src/wimlib.h \ + wimlib_tchar.h \ src/wimlib_internal.h \ src/write.c \ src/xml.c \ @@ -95,11 +95,16 @@ libwim_la_CFLAGS = \ bin_PROGRAMS = imagex -imagex_SOURCES = programs/imagex.c +imagex_SOURCES = programs/imagex.c wimlib_tchar.h imagex_LDADD = $(top_builddir)/libwim.la +imagex_CFLAGS = $(AM_CFLAGS) $(WINDOWS_CFLAGS) if WINDOWS_NATIVE_BUILD -imagex_SOURCES += programs/imagex-win32.c programs/imagex-win32.h +imagex_SOURCES += programs/imagex-win32.c \ + programs/imagex-win32.h \ + programs/wgetopt.c \ + programs/wgetopt.h + libwim_la_SOURCES += src/win32.c endif diff --git a/configure.ac b/configure.ac index 71136390..fe07a3c7 100644 --- a/configure.ac +++ b/configure.ac @@ -179,6 +179,7 @@ WITH_NTFS_3G_DEFAULT="yes" WITH_FUSE_DEFAULT="yes" WINDOWS_NATIVE_BUILD="no" VISIBILITY_CFLAGS="-fvisibility=hidden" +WINDOWS_CFLAGS="" WINDOWS_CPPFLAGS="" WINDOWS_LDFLAGS="" WINDOWS_LDADD="" @@ -190,12 +191,15 @@ case "$host" in WITH_FUSE_DEFAULT="no" WINDOWS_NATIVE_BUILD="yes" VISIBILITY_CFLAGS="" + WINDOWS_CFLAGS="-municode" WINDOWS_CPPFLAGS="-D_POSIX" WINDOWS_LDFLAGS="-no-undefined" WINDOWS_LDADD="-lshlwapi" ;; *-*-cygwin*) # Cygwin (WARNING: not well supported) + AC_MSG_WARN([wimlib has not been tested with Cygwin! Please do + a Windows-native build with MinGW-w64 instead]) WITH_NTFS_3G_DEFAULT="no" WITH_FUSE_DEFAULT="no" VISIBILITY_CFLAGS="" @@ -209,6 +213,7 @@ AC_SUBST([VISIBILITY_CFLAGS], [$VISIBILITY_CFLAGS]) AC_SUBST([WINDOWS_LDFLAGS], [$WINDOWS_LDFLAGS]) AC_SUBST([WINDOWS_LDADD], [$WINDOWS_LDADD]) AC_SUBST([WINDOWS_CPPFLAGS], [$WINDOWS_CPPFLAGS]) +AC_SUBST([WINDOWS_CFLAGS], [$WINDOWS_CFLAGS]) AM_CONDITIONAL([WINDOWS_NATIVE_BUILD], [test "x$WINDOWS_NATIVE_BUILD" = "xyes"]) AC_MSG_CHECKING([whether to include support for ntfs-3g]) diff --git a/programs/imagex-win32.c b/programs/imagex-win32.c index 16648500..18581ed0 100644 --- a/programs/imagex-win32.c +++ b/programs/imagex-win32.c @@ -15,21 +15,22 @@ #include /* Replacement for glob() in Windows native builds. */ -int glob(const char *pattern, int flags, - int (*errfunc)(const char *epath, int eerrno), - glob_t *pglob) +int +win32_wglob(const wchar_t *pattern, int flags, + int (*errfunc)(const wchar_t *epath, int eerrno), + glob_t *pglob) { - WIN32_FIND_DATA dat; + WIN32_FIND_DATAW dat; DWORD err; HANDLE hFind; int ret; size_t nspaces; - const char *backslash, *forward_slash, *end_slash; + const wchar_t *backslash, *end_slash; size_t prefix_len; - backslash = strrchr(pattern, '\\'); - end_slash = strrchr(pattern, '/'); + backslash = wcsrchr(pattern, L'\\'); + end_slash = wcsrchr(pattern, L'/'); if (backslash > end_slash) end_slash = backslash; @@ -46,7 +47,7 @@ int glob(const char *pattern, int flags, assert((flags & GLOB_ERR) == GLOB_ERR); assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0); - hFind = FindFirstFileA(pattern, &dat); + hFind = FindFirstFileW(pattern, &dat); if (hFind == INVALID_HANDLE_VALUE) { err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { @@ -63,10 +64,10 @@ int glob(const char *pattern, int flags, pglob->gl_pathv = NULL; nspaces = 0; do { - char *path; + wchar_t *path; if (pglob->gl_pathc == nspaces) { size_t new_nspaces; - char **pathv; + wchar_t **pathv; new_nspaces = nspaces * 2 + 1; pathv = realloc(pglob->gl_pathv, @@ -76,17 +77,17 @@ int glob(const char *pattern, int flags, pglob->gl_pathv = pathv; nspaces = new_nspaces; } - size_t filename_len = strlen(dat.cFileName); + size_t filename_len = wcslen(dat.cFileName); size_t len_needed = prefix_len + filename_len; - path = malloc(len_needed + 1); + path = malloc(len_needed + sizeof(wchar_t)); if (!path) goto oom; - memcpy(path, pattern, prefix_len); - memcpy(path + prefix_len, dat.cFileName, filename_len + 1); + wmemcpy(path, pattern, prefix_len); + wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1); pglob->gl_pathv[pglob->gl_pathc++] = path; - } while (FindNextFileA(hFind, &dat)); + } while (FindNextFileW(hFind, &dat)); err = GetLastError(); CloseHandle(hFind); if (err == ERROR_NO_MORE_FILES) { @@ -108,7 +109,8 @@ fail_globfree: return ret; } -void globfree(glob_t *pglob) +void +globfree(glob_t *pglob) { size_t i; for (i = 0; i < pglob->gl_pathc; i++) @@ -142,13 +144,13 @@ win32_modify_privilege(const char *privilege, bool enable) CloseHandle(hToken); out: if (!ret) { - fprintf(stderr, "WARNING: Failed to %s privilege %s\n", - enable ? "enable" : "disable", privilege); - fprintf(stderr, - "WARNING: The program will continue, " - "but if permission issues are\n" - "encountered, you may need to run " - "this program as the administrator\n"); + fwprintf(stderr, L"WARNING: Failed to %ls privilege %s\n", + enable ? L"enable" : L"disable", privilege); + fwprintf(stderr, + L"WARNING: The program will continue, " + L"but if permission issues are\n" + L"encountered, you may need to run " + L"this program as the administrator\n"); } return ret; } @@ -191,3 +193,69 @@ win32_release_restore_privileges() { win32_modify_restore_privileges(false); } + +wchar_t * +win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret) +{ + if (mbs_nbytes > INT_MAX) { + fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n", + mbs_nbytes); + return NULL; + } + if (mbs_nbytes == 0) { + *num_wchars_ret = 0; + return (wchar_t*)mbs; + } + int len = MultiByteToWideChar(CP_ACP, + MB_ERR_INVALID_CHARS, + mbs, + mbs_nbytes, + NULL, + 0); + if (len <= 0) + goto out_invalid; + wchar_t *wcs = malloc(len * sizeof(wchar_t)); + if (!wcs) { + fwprintf(stderr, L"ERROR: out of memory!\n"); + return NULL; + } + int len2 = MultiByteToWideChar(CP_ACP, + MB_ERR_INVALID_CHARS, + mbs, + mbs_nbytes, + wcs, + len); + if (len2 != len) { + free(wcs); + goto out_invalid; + } + *num_wchars_ret = len; + return wcs; +out_invalid: + fwprintf(stderr, +L"ERROR: Invalid multi-byte string in the text file you provided as input!\n" +L" Maybe try converting your text file to UTF-16LE?\n" + ); + return NULL; +} + +static inline bool +is_path_separator(wchar_t c) +{ + return c == L'/' || c == L'\\'; +} + +wchar_t * +win32_wbasename(wchar_t *path) +{ + wchar_t *p = wcschr(path, L'\0'); + + p--; + while (p >= path && is_path_separator(*p)) + *p-- = '\0'; + while (p >= path && !is_path_separator(*p)) + p--; + p++; + return p; +} + diff --git a/programs/imagex-win32.h b/programs/imagex-win32.h index 1de8e984..9e18eab5 100644 --- a/programs/imagex-win32.h +++ b/programs/imagex-win32.h @@ -3,17 +3,19 @@ #include #include +#include typedef struct { - size_t gl_pathc; - char **gl_pathv; - size_t gl_offs; + size_t gl_pathc; + wchar_t **gl_pathv; + size_t gl_offs; } glob_t; /* WARNING: this is a reduced functionality replacement */ -extern int glob(const char *pattern, int flags, - int (*errfunc)(const char *epath, int eerrno), - glob_t *pglob); +extern int +win32_wglob(const wchar_t *pattern, int flags, + int (*errfunc)(const wchar_t *epath, int eerrno), + glob_t *pglob); extern void globfree(glob_t *pglob); @@ -37,4 +39,22 @@ win32_acquire_restore_privileges(); extern void win32_release_restore_privileges(); +extern wchar_t * +win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret); + +extern wchar_t * +win32_wbasename(wchar_t *path); + +#include "wgetopt.h" + +#define optarg woptarg +#define optind woptind +#define opterr wopterr +#define optopt woptopt +#define option woption + +#define getopt_long_only wgetopt_long_only +#define getopt_long wgetopt_long +#define getopt wgetopt + #endif diff --git a/programs/imagex.c b/programs/imagex.c index 0d271e43..a2e9e5ea 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -23,12 +23,11 @@ */ #include "config.h" - +#include "wimlib_tchar.h" #include "wimlib.h" #include #include -#include #include #include @@ -46,13 +45,20 @@ #ifdef __WIN32__ # include "imagex-win32.h" -#else +# define tbasename win32_wbasename +# define tglob win32_wglob +#else /* __WIN32__ */ # include -#endif +# include +# include +# define tbasename basename +# define tglob glob +#endif /* !__WIN32 */ + #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) -#define for_opt(c, opts) while ((c = getopt_long_only(argc, (char**)argv, "", \ +#define for_opt(c, opts) while ((c = getopt_long_only(argc, (tchar**)argv, T(""), \ opts, NULL)) != -1) enum imagex_op_type { @@ -75,192 +81,222 @@ static void usage(int cmd_type); static void usage_all(); -static const char *usage_strings[] = { +static const tchar *usage_strings[] = { [APPEND] = +T( IMAGEX_PROGNAME" append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" " [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n" " [--verbose] [--dereference] [--config=FILE]\n" " [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n" -" [--source-list] [--noacls]\n", +" [--source-list] [--noacls]\n" +), [APPLY] = +T( IMAGEX_PROGNAME" apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" " (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" " [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n" -" [--noacls]\n", +" [--noacls]\n" +), [CAPTURE] = +T( IMAGEX_PROGNAME" capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" " [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n" " [--flags EDITION_ID] [--verbose] [--dereference]\n" " [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n" -" [--source-list] [--noacls]\n", +" [--source-list] [--noacls]\n" +), [DELETE] = -IMAGEX_PROGNAME" delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n", +T( +IMAGEX_PROGNAME" delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n" +), [DIR] = -IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n", +T( +IMAGEX_PROGNAME" dir WIMFILE (IMAGE_NUM | IMAGE_NAME | all)\n" +), [EXPORT] = +T( IMAGEX_PROGNAME" export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n" " DEST_WIMFILE [DEST_IMAGE_NAME] [DEST_IMAGE_DESCRIPTION]\n" " [--boot] [--check] [--compress=TYPE] [--ref=\"GLOB\"]\n" -" [--threads=NUM_THREADS] [--rebuild]\n", +" [--threads=NUM_THREADS] [--rebuild]\n" +), [INFO] = +T( IMAGEX_PROGNAME" info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n" " [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" -" [--xml] [--extract-xml FILE] [--metadata]\n", +" [--xml] [--extract-xml FILE] [--metadata]\n" +), [JOIN] = -IMAGEX_PROGNAME" join [--check] WIMFILE SPLIT_WIM...\n", +T( +IMAGEX_PROGNAME" join [--check] WIMFILE SPLIT_WIM...\n" +), [MOUNT] = +T( IMAGEX_PROGNAME" mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n" " [--check] [--debug] [--streams-interface=INTERFACE]\n" -" [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n", +" [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n" +), [MOUNTRW] = +T( IMAGEX_PROGNAME" mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n" " [--check] [--debug] [--streams-interface=INTERFACE]\n" -" [--staging-dir=DIR] [--unix-data] [--allow-other]\n", +" [--staging-dir=DIR] [--unix-data] [--allow-other]\n" +), [OPTIMIZE] = -IMAGEX_PROGNAME" optimize WIMFILE [--check] [--recompress] [--compress=TYPE]\n", +T( +IMAGEX_PROGNAME" optimize WIMFILE [--check] [--recompress]\n" +), [SPLIT] = -IMAGEX_PROGNAME" split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n", +T( +IMAGEX_PROGNAME" split WIMFILE SPLIT_WIMFILE PART_SIZE_MB [--check]\n" +), [UNMOUNT] = -IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild]\n", +T( +IMAGEX_PROGNAME" unmount DIRECTORY [--commit] [--check] [--rebuild]\n" +), }; static const struct option apply_options[] = { - {"check", no_argument, NULL, 'c'}, - {"hardlink", no_argument, NULL, 'h'}, - {"symlink", no_argument, NULL, 's'}, - {"verbose", no_argument, NULL, 'v'}, - {"ref", required_argument, NULL, 'r'}, - {"unix-data", no_argument, NULL, 'U'}, - {"noacls", no_argument, NULL, 'N'}, + {T("check"), no_argument, NULL, 'c'}, + {T("hardlink"), no_argument, NULL, 'h'}, + {T("symlink"), no_argument, NULL, 's'}, + {T("verbose"), no_argument, NULL, 'v'}, + {T("ref"), required_argument, NULL, 'r'}, + {T("unix-data"), no_argument, NULL, 'U'}, + {T("noacls"), no_argument, NULL, 'N'}, {NULL, 0, NULL, 0}, }; static const struct option capture_or_append_options[] = { - {"boot", no_argument, NULL, 'b'}, - {"check", no_argument, NULL, 'c'}, - {"compress", required_argument, NULL, 'x'}, - {"config", required_argument, NULL, 'C'}, - {"dereference", no_argument, NULL, 'L'}, - {"flags", required_argument, NULL, 'f'}, - {"verbose", no_argument, NULL, 'v'}, - {"threads", required_argument, NULL, 't'}, - {"rebuild", no_argument, NULL, 'R'}, - {"unix-data", no_argument, NULL, 'U'}, - {"source-list", no_argument, NULL, 'S'}, - {"noacls", no_argument, NULL, 'N'}, + {T("boot"), no_argument, NULL, 'b'}, + {T("check"), no_argument, NULL, 'c'}, + {T("compress"), required_argument, NULL, 'x'}, + {T("config"), required_argument, NULL, 'C'}, + {T("dereference"), no_argument, NULL, 'L'}, + {T("flags"), required_argument, NULL, 'f'}, + {T("verbose"), no_argument, NULL, 'v'}, + {T("threads"), required_argument, NULL, 't'}, + {T("rebuild"), no_argument, NULL, 'R'}, + {T("unix-data"), no_argument, NULL, 'U'}, + {T("source-list"), no_argument, NULL, 'S'}, + {T("noacls"), no_argument, NULL, 'N'}, {NULL, 0, NULL, 0}, }; static const struct option delete_options[] = { - {"check", no_argument, NULL, 'c'}, - {"soft", no_argument, NULL, 's'}, + {T("check"), no_argument, NULL, 'c'}, + {T("soft"), no_argument, NULL, 's'}, {NULL, 0, NULL, 0}, }; static const struct option export_options[] = { - {"boot", no_argument, NULL, 'b'}, - {"check", no_argument, NULL, 'c'}, - {"compress", required_argument, NULL, 'x'}, - {"ref", required_argument, NULL, 'r'}, - {"threads", required_argument, NULL, 't'}, - {"rebuild", no_argument, NULL, 'R'}, + {T("boot"), no_argument, NULL, 'b'}, + {T("check"), no_argument, NULL, 'c'}, + {T("compress"), required_argument, NULL, 'x'}, + {T("ref"), required_argument, NULL, 'r'}, + {T("threads"), required_argument, NULL, 't'}, + {T("rebuild"), no_argument, NULL, 'R'}, {NULL, 0, NULL, 0}, }; static const struct option info_options[] = { - {"boot", no_argument, NULL, 'b'}, - {"check", no_argument, NULL, 'c'}, - {"extract-xml", required_argument, NULL, 'X'}, - {"header", no_argument, NULL, 'h'}, - {"lookup-table", no_argument, NULL, 'l'}, - {"metadata", no_argument, NULL, 'm'}, - {"xml", no_argument, NULL, 'x'}, + {T("boot"), no_argument, NULL, 'b'}, + {T("check"), no_argument, NULL, 'c'}, + {T("extract-xml"), required_argument, NULL, 'X'}, + {T("header"), no_argument, NULL, 'h'}, + {T("lookup-table"), no_argument, NULL, 'l'}, + {T("metadata"), no_argument, NULL, 'm'}, + {T("xml"), no_argument, NULL, 'x'}, {NULL, 0, NULL, 0}, }; static const struct option join_options[] = { - {"check", no_argument, NULL, 'c'}, + {T("check"), no_argument, NULL, 'c'}, {NULL, 0, NULL, 0}, }; static const struct option mount_options[] = { - {"check", no_argument, NULL, 'c'}, - {"debug", no_argument, NULL, 'd'}, - {"streams-interface", required_argument, NULL, 's'}, - {"ref", required_argument, NULL, 'r'}, - {"staging-dir", required_argument, NULL, 'D'}, - {"unix-data", no_argument, NULL, 'U'}, - {"allow-other", no_argument, NULL, 'A'}, + {T("check"), no_argument, NULL, 'c'}, + {T("debug"), no_argument, NULL, 'd'}, + {T("streams-interface"), required_argument, NULL, 's'}, + {T("ref"), required_argument, NULL, 'r'}, + {T("staging-dir"), required_argument, NULL, 'D'}, + {T("unix-data"), no_argument, NULL, 'U'}, + {T("allow-other"), no_argument, NULL, 'A'}, {NULL, 0, NULL, 0}, }; static const struct option optimize_options[] = { - {"check", no_argument, NULL, 'c'}, - {"recompress", no_argument, NULL, 'r'}, + {T("check"), no_argument, NULL, 'c'}, + {T("recompress"), no_argument, NULL, 'r'}, {NULL, 0, NULL, 0}, }; static const struct option split_options[] = { - {"check", no_argument, NULL, 'c'}, + {T("check"), no_argument, NULL, 'c'}, {NULL, 0, NULL, 0}, }; static const struct option unmount_options[] = { - {"commit", no_argument, NULL, 'c'}, - {"check", no_argument, NULL, 'C'}, - {"rebuild", no_argument, NULL, 'R'}, + {T("commit"), no_argument, NULL, 'c'}, + {T("check"), no_argument, NULL, 'C'}, + {T("rebuild"), no_argument, NULL, 'R'}, {NULL, 0, NULL, 0}, }; /* Print formatted error message to stderr. */ -static void imagex_error(const char *format, ...) +static void +imagex_error(const tchar *format, ...) { va_list va; va_start(va, format); - fputs("ERROR: ", stderr); - vfprintf(stderr, format, va); - putc('\n', stderr); + tfputs(T("ERROR: "), stderr); + tvfprintf(stderr, format, va); + tputc(T('\n'), stderr); va_end(va); } /* Print formatted error message to stderr. */ -static void imagex_error_with_errno(const char *format, ...) +static void +imagex_error_with_errno(const tchar *format, ...) { int errno_save = errno; va_list va; va_start(va, format); - fputs("ERROR: ", stderr); - vfprintf(stderr, format, va); - fprintf(stderr, ": %s\n", strerror(errno_save)); + tfputs(T("ERROR: "), stderr); + tvfprintf(stderr, format, va); + tfprintf(stderr, T(": %"TS"\n"), tstrerror(errno_save)); va_end(va); } -static int verify_image_exists(int image, const char *image_name, - const char *wim_name) +static int +verify_image_exists(int image, const tchar *image_name, const tchar *wim_name) { if (image == WIMLIB_NO_IMAGE) { - imagex_error("\"%s\" is not a valid image in `%s'!\n" + imagex_error(T("\"%"TS"\" is not a valid image in \"%"TS"\"!\n" " Please specify a 1-based image index or " "image name.\n" " You may use `"IMAGEX_PROGNAME" info' to list the images " - "contained in a WIM.", + "contained in a WIM."), image_name, wim_name); return -1; } return 0; } -static int verify_image_is_single(int image) +static int +verify_image_is_single(int image) { if (image == WIMLIB_ALL_IMAGES) { - imagex_error("Cannot specify all images for this action!"); + imagex_error(T("Cannot specify all images for this action!")); return -1; } return 0; } -static int verify_image_exists_and_is_single(int image, const char *image_name, - const char *wim_name) +static int +verify_image_exists_and_is_single(int image, const tchar *image_name, + const tchar *wim_name) { int ret; ret = verify_image_exists(image, image_name, wim_name); @@ -270,33 +306,36 @@ static int verify_image_exists_and_is_single(int image, const char *image_name, } /* Parse the argument to --compress */ -static int get_compression_type(const char *optarg) +static int +get_compression_type(const tchar *optarg) { - if (strcasecmp(optarg, "maximum") == 0 || strcasecmp(optarg, "lzx") == 0) + if (tstrcasecmp(optarg, T("maximum")) == 0 || tstrcasecmp(optarg, T("lzx")) == 0) return WIMLIB_COMPRESSION_TYPE_LZX; - else if (strcasecmp(optarg, "fast") == 0 || strcasecmp(optarg, "xpress") == 0) + else if (tstrcasecmp(optarg, T("fast")) == 0 || tstrcasecmp(optarg, T("xpress")) == 0) return WIMLIB_COMPRESSION_TYPE_XPRESS; - else if (strcasecmp(optarg, "none") == 0) + else if (tstrcasecmp(optarg, T("none")) == 0) return WIMLIB_COMPRESSION_TYPE_NONE; else { - imagex_error("Invalid compression type `%s'! Must be " - "\"maximum\", \"fast\", or \"none\".", optarg); + imagex_error(T("Invalid compression type \"%"TS"\"! Must be " + "\"maximum\", \"fast\", or \"none\"."), optarg); return WIMLIB_COMPRESSION_TYPE_INVALID; } } /* Returns the size of a file given its name, or -1 if the file does not exist * or its size cannot be determined. */ -static off_t file_get_size(const char *filename) +static off_t +file_get_size(const tchar *filename) { struct stat st; - if (stat(filename, &st) == 0) + if (tstat(filename, &st) == 0) return st.st_size; else return (off_t)-1; } -static const char *default_capture_config = +static const tchar *default_capture_config = +T( "[ExclusionList]\n" "\\$ntfs.log\n" "\\hiberfil.sys\n" @@ -309,46 +348,8 @@ static const char *default_capture_config = "*.mp3\n" "*.zip\n" "*.cab\n" -"\\WINDOWS\\inf\\*.pnf\n"; - -/* Read standard input until EOF and return the full contents in a malloc()ed - * buffer and the number of bytes of data in @len_ret. Returns NULL on read - * error. */ -static char *stdin_get_contents(size_t *len_ret) -{ - /* stdin can, of course, be a pipe or other non-seekable file, so the - * total length of the data cannot be pre-determined */ - char *buf = NULL; - size_t newlen = 1024; - size_t pos = 0; - size_t inc = 1024; - for (;;) { - char *p = realloc(buf, newlen); - size_t bytes_read, bytes_to_read; - if (!p) { - imagex_error("out of memory while reading stdin"); - break; - } - buf = p; - bytes_to_read = newlen - pos; - bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin); - pos += bytes_read; - if (bytes_read != bytes_to_read) { - if (feof(stdin)) { - *len_ret = pos; - return buf; - } else { - imagex_error_with_errno("error reading stdin"); - break; - } - } - newlen += inc; - inc *= 3; - inc /= 2; - } - free(buf); - return NULL; -} +"\\WINDOWS\\inf\\*.pnf\n" +); enum { PARSE_FILENAME_SUCCESS = 0, @@ -379,31 +380,32 @@ enum { * closing quote; or PARSE_FILENAME_NONE if the line ended before the * beginning of a filename was found. */ -static int parse_filename(char **line_p, size_t *len_p, char **fn_ret) +static int +parse_filename(tchar **line_p, size_t *len_p, tchar **fn_ret) { size_t len = *len_p; - char *line = *line_p; - char *fn; - char quote_char; + tchar *line = *line_p; + tchar *fn; + tchar quote_char; /* Skip leading whitespace */ for (;;) { if (len == 0) return PARSE_FILENAME_NONE; - if (!isspace(*line) && *line != '\0') + if (!istspace(*line) && *line != T('\0')) break; line++; len--; } quote_char = *line; - if (quote_char == '"' || quote_char == '\'') { + if (quote_char == T('"') || quote_char == T('\'')) { /* Quoted filename */ line++; len--; fn = line; - line = memchr(line, quote_char, len); + line = tmemchr(line, quote_char, len); if (!line) { - imagex_error("Missing closing quote: %s", fn - 1); + imagex_error(T("Missing closing quote: %"TS), fn - 1); return PARSE_FILENAME_FAILURE; } } else { @@ -412,9 +414,9 @@ static int parse_filename(char **line_p, size_t *len_p, char **fn_ret) fn = line; do { line++; - } while (!isspace(*line) && *line != '\0'); + } while (!istspace(*line) && *line != T('\0')); } - *line = '\0'; + *line = T('\0'); len -= line - fn; *len_p = len; *line_p = line; @@ -437,7 +439,7 @@ static int parse_filename(char **line_p, size_t *len_p, char **fn_ret) * * Returns true if the line was valid; false otherwise. */ static bool -parse_source_list_line(char *line, size_t len, +parse_source_list_line(tchar *line, size_t len, struct wimlib_capture_source *source) { /* SOURCE [DEST] */ @@ -453,12 +455,13 @@ parse_source_list_line(char *line, size_t len, /* Returns %true if the given line of length @len > 0 is a comment or empty line * in the source list file format. */ -static bool is_comment_line(const char *line, size_t len) +static bool +is_comment_line(const tchar *line, size_t len) { for (;;) { - if (*line == '#') + if (*line == T('#')) return true; - if (!isspace(*line) && *line != '\0') + if (!istspace(*line) && *line != T('\0')) return false; ++line; --len; @@ -485,31 +488,45 @@ static bool is_comment_line(const char *line, size_t len) * the wimlib_add_image_multisource() function to specify how a WIM image is to * be created. */ static struct wimlib_capture_source * -parse_source_list(char *source_list_contents, size_t source_list_nbytes, +parse_source_list(tchar **source_list_contents_p, size_t source_list_nchars, size_t *nsources_ret) { size_t nlines; - char *p; + tchar *p; struct wimlib_capture_source *sources; size_t i, j; + tchar *source_list_contents = *source_list_contents_p; nlines = 0; - for (i = 0; i < source_list_nbytes; i++) - if (source_list_contents[i] == '\n') + for (i = 0; i < source_list_nchars; i++) + if (source_list_contents[i] == T('\n')) nlines++; - sources = calloc(nlines, sizeof(*sources)); - if (!sources) { - imagex_error("out of memory"); - return NULL; + + /* Handle last line not terminated by a newline */ + if (source_list_nchars != 0 && + source_list_contents[source_list_nchars - 1] != T('\n')) + { + source_list_contents = realloc(source_list_contents, + (source_list_nchars + 1) * sizeof(tchar)); + if (!source_list_contents) + goto oom; + source_list_contents[source_list_nchars] = T('\n'); + *source_list_contents_p = source_list_contents; + source_list_nchars++; + nlines++; } + + sources = calloc(nlines, sizeof(*sources)); + if (!sources) + goto oom; p = source_list_contents; j = 0; for (i = 0; i < nlines; i++) { /* XXX: Could use rawmemchr() here instead, but it may not be * available on all platforms. */ - char *endp = memchr(p, '\n', source_list_nbytes); + tchar *endp = tmemchr(p, T('\n'), source_list_nchars); size_t len = endp - p + 1; - *endp = '\0'; + *endp = T('\0'); if (!is_comment_line(p, len)) { if (!parse_source_list_line(p, len, &sources[j++])) { free(sources); @@ -521,37 +538,41 @@ parse_source_list(char *source_list_contents, size_t source_list_nbytes, } *nsources_ret = j; return sources; +oom: + imagex_error(T("out of memory")); + return NULL; } /* Reads the contents of a file into memory. */ -static char *file_get_contents(const char *filename, size_t *len_ret) +static char * +file_get_contents(const tchar *filename, size_t *len_ret) { struct stat stbuf; - char *buf = NULL; + void *buf = NULL; size_t len; FILE *fp; - if (stat(filename, &stbuf) != 0) { - imagex_error_with_errno("Failed to stat the file `%s'", filename); + if (tstat(filename, &stbuf) != 0) { + imagex_error_with_errno(T("Failed to stat the file \"%"TS"\""), filename); goto out; } len = stbuf.st_size; - fp = fopen(filename, "rb"); + fp = tfopen(filename, T("rb")); if (!fp) { - imagex_error_with_errno("Failed to open the file `%s'", filename); + imagex_error_with_errno(T("Failed to open the file \"%"TS"\""), filename); goto out; } buf = malloc(len); if (!buf) { - imagex_error("Failed to allocate buffer of %zu bytes to hold " - "contents of file `%s'", len, filename); + imagex_error(T("Failed to allocate buffer of %zu bytes to hold " + "contents of file \"%"TS"\""), len, filename); goto out_fclose; } if (fread(buf, 1, len, fp) != len) { - imagex_error_with_errno("Failed to read %lu bytes from the " - "file `%s'", len, filename); + imagex_error_with_errno(T("Failed to read %zu bytes from the " + "file \"%"TS"\""), len, filename); goto out_free_buf; } *len_ret = len; @@ -565,14 +586,119 @@ out: return buf; } +/* Read standard input until EOF and return the full contents in a malloc()ed + * buffer and the number of bytes of data in @len_ret. Returns NULL on read + * error. */ +static char * +stdin_get_contents(size_t *len_ret) +{ + /* stdin can, of course, be a pipe or other non-seekable file, so the + * total length of the data cannot be pre-determined */ + char *buf = NULL; + size_t newlen = 1024; + size_t pos = 0; + size_t inc = 1024; + for (;;) { + char *p = realloc(buf, newlen); + size_t bytes_read, bytes_to_read; + if (!p) { + imagex_error(T("out of memory while reading stdin")); + break; + } + buf = p; + bytes_to_read = newlen - pos; + bytes_read = fread(&buf[pos], 1, bytes_to_read, stdin); + pos += bytes_read; + if (bytes_read != bytes_to_read) { + if (feof(stdin)) { + *len_ret = pos; + return buf; + } else { + imagex_error_with_errno(T("error reading stdin")); + break; + } + } + newlen += inc; + inc *= 3; + inc /= 2; + } + free(buf); + return NULL; +} + + +static tchar * +translate_text_to_tstr(char **text_p, size_t num_bytes, + size_t *num_tchars_ret) +{ +#ifndef __WIN32__ + /* On non-Windows, assume an ASCII-compatible encoding, such as UTF-8. + * */ + *num_tchars_ret = num_bytes; + return *text_p; +#else /* !__WIN32__ */ + /* On Windows, translate the text to UTF-16LE */ + const char *text_bytestr = *text_p; + wchar_t *text_wstr; + size_t num_wchars; + + if (num_bytes >= 2 && + ((text_bytestr[0] == 0xff && text_bytestr[1] == 0xfe) || + (text_bytestr[0] <= 0x7f && text_bytestr[1] == 0x00))) + { + /* File begins with 0xfeff, the BOM for UTF-16LE, or it begins + * with something that looks like an ASCII character encoded as + * a UTF-16LE code unit. Assume the file is encoded as + * UTF-16LE. This is not a 100% reliable check. */ + num_wchars = num_bytes / 2; + text_wstr = (wchar_t*)text_bytestr; + } else { + /* File does not look like UTF-16LE. Assume it is encoded in + * the current Windows code page. I think these are always + * ASCII-compatible, so any so-called "plain-text" (ASCII) files + * should work as expected. */ + text_wstr = win32_mbs_to_wcs(text_bytestr, + num_bytes, + &num_wchars); + } + *num_tchars_ret = num_wchars; + return text_wstr; +#endif /* __WIN32__ */ +} + +static tchar * +file_get_text_contents(const tchar *filename, size_t *num_tchars_ret) +{ + char *contents; + size_t num_bytes; + + contents = file_get_contents(filename, &num_bytes); + if (!contents) + return NULL; + return translate_text_to_tstr(&contents, num_bytes, num_tchars_ret); +} + +static tchar * +stdin_get_text_contents(size_t *num_tchars_ret) +{ + char *contents; + size_t num_bytes; + + contents = stdin_get_contents(&num_bytes); + if (!contents) + return NULL; + return translate_text_to_tstr(&contents, num_bytes, num_tchars_ret); +} + /* Return 0 if a path names a file to which the current user has write access; * -1 otherwise (and print an error message). */ -static int file_writable(const char *path) +static int +file_writable(const tchar *path) { int ret; - ret = access(path, W_OK); + ret = taccess(path, W_OK); if (ret != 0) - imagex_error_with_errno("Can't modify `%s'", path); + imagex_error_with_errno(T("Can't modify \"%"TS"\""), path); return ret; } @@ -581,22 +707,24 @@ static int file_writable(const char *path) /* Given an enumerated value for WIM compression type, return a descriptive * string. */ -static const char *get_data_type(int ctype) +static const tchar * +get_data_type(int ctype) { switch (ctype) { case WIMLIB_COMPRESSION_TYPE_NONE: - return "uncompressed"; + return T("uncompressed"); case WIMLIB_COMPRESSION_TYPE_LZX: - return "LZX-compressed"; + return T("LZX-compressed"); case WIMLIB_COMPRESSION_TYPE_XPRESS: - return "XPRESS-compressed"; + return T("XPRESS-compressed"); } return NULL; } /* Progress callback function passed to various wimlib functions. */ -static int imagex_progress_func(enum wimlib_progress_msg msg, - const union wimlib_progress_info *info) +static int +imagex_progress_func(enum wimlib_progress_msg msg, + const union wimlib_progress_info *info) { unsigned percent_done; switch (msg) { @@ -604,69 +732,70 @@ static int imagex_progress_func(enum wimlib_progress_msg msg, percent_done = TO_PERCENT(info->write_streams.completed_bytes, info->write_streams.total_bytes); if (info->write_streams.completed_streams == 0) { - const char *data_type; + const tchar *data_type; data_type = get_data_type(info->write_streams.compression_type); - printf("Writing %s data using %u thread%s\n", - data_type, info->write_streams.num_threads, - (info->write_streams.num_threads == 1) ? "" : "s"); + tprintf(T("Writing %"TS" data using %u thread%"TS"\n"), + data_type, info->write_streams.num_threads, + (info->write_streams.num_threads == 1) ? T("") : T("s")); } - printf("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) " - "written (%u%% done)", - info->write_streams.completed_bytes >> 20, - info->write_streams.total_bytes >> 20, - percent_done); + tprintf(T("\r%"PRIu64" MiB of %"PRIu64" MiB (uncompressed) " + "written (%u%% done)"), + info->write_streams.completed_bytes >> 20, + info->write_streams.total_bytes >> 20, + percent_done); if (info->write_streams.completed_bytes >= info->write_streams.total_bytes) - putchar('\n'); + tputchar(T('\n')); break; case WIMLIB_PROGRESS_MSG_SCAN_BEGIN: - printf("Scanning `%s'", info->scan.source); + tprintf(T("Scanning \"%"TS"\""), info->scan.source); if (*info->scan.wim_target_path) { - printf(" (loading as WIM path: `/%s')...\n", + tprintf(T(" (loading as WIM path: \"/%"TS"\")...\n"), info->scan.wim_target_path); } else { - printf(" (loading as root of WIM image)...\n"); + tprintf(T(" (loading as root of WIM image)...\n")); } break; case WIMLIB_PROGRESS_MSG_SCAN_DENTRY: if (info->scan.excluded) - printf("Excluding `%s' from capture\n", info->scan.cur_path); + tprintf(T("Excluding \"%"TS"\" from capture\n"), info->scan.cur_path); else - printf("Scanning `%s'\n", info->scan.cur_path); + tprintf(T("Scanning \"%"TS"\"\n"), info->scan.cur_path); break; /*case WIMLIB_PROGRESS_MSG_SCAN_END:*/ /*break;*/ case WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY: percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); - printf("\rVerifying integrity of `%s': %"PRIu64" MiB " - "of %"PRIu64" MiB (%u%%) done", - info->integrity.filename, - info->integrity.completed_bytes >> 20, - info->integrity.total_bytes >> 20, - percent_done); + tprintf(T("\rVerifying integrity of \"%"TS"\": %"PRIu64" MiB " + "of %"PRIu64" MiB (%u%%) done"), + info->integrity.filename, + info->integrity.completed_bytes >> 20, + info->integrity.total_bytes >> 20, + percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) - putchar('\n'); + tputchar(T('\n')); break; case WIMLIB_PROGRESS_MSG_CALC_INTEGRITY: percent_done = TO_PERCENT(info->integrity.completed_bytes, info->integrity.total_bytes); - printf("\rCalculating integrity table for WIM: %"PRIu64" MiB " - "of %"PRIu64" MiB (%u%%) done", - info->integrity.completed_bytes >> 20, - info->integrity.total_bytes >> 20, - percent_done); + tprintf(T("\rCalculating integrity table for WIM: %"PRIu64" MiB " + "of %"PRIu64" MiB (%u%%) done"), + info->integrity.completed_bytes >> 20, + info->integrity.total_bytes >> 20, + percent_done); if (info->integrity.completed_bytes == info->integrity.total_bytes) - putchar('\n'); + tputchar(T('\n')); break; case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN: - printf("Applying image %d (%s) from `%s' to %s `%s'\n", - info->extract.image, - info->extract.image_name, - info->extract.wimfile_name, - ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ? - "NTFS volume" : "directory"), - info->extract.target); + tprintf(T("Applying image %d (%"TS") from \"%"TS"\" " + "to %"TS" \"%"TS"\"\n"), + info->extract.image, + info->extract.image_name, + info->extract.wimfile_name, + ((info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) ? + T("NTFS volume") : T("directory")), + info->extract.target); break; /*case WIMLIB_PROGRESS_MSG_EXTRACT_DIR_STRUCTURE_BEGIN:*/ /*printf("Applying directory structure to %s\n",*/ @@ -675,24 +804,24 @@ static int imagex_progress_func(enum wimlib_progress_msg msg, case WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS: percent_done = TO_PERCENT(info->extract.completed_bytes, info->extract.total_bytes); - printf("\rExtracting files: " - "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done", - info->extract.completed_bytes >> 20, - info->extract.total_bytes >> 20, - percent_done); + tprintf(T("\rExtracting files: " + "%"PRIu64" MiB of %"PRIu64" MiB (%u%%) done"), + info->extract.completed_bytes >> 20, + info->extract.total_bytes >> 20, + percent_done); if (info->extract.completed_bytes >= info->extract.total_bytes) putchar('\n'); break; case WIMLIB_PROGRESS_MSG_EXTRACT_DENTRY: - puts(info->extract.cur_path); + tprintf(T("%"TS"\n"), info->extract.cur_path); break; case WIMLIB_PROGRESS_MSG_APPLY_TIMESTAMPS: - printf("Setting timestamps on all extracted files...\n"); + tprintf(T("Setting timestamps on all extracted files...\n")); break; case WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END: if (info->extract.extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) { - printf("Unmounting NTFS volume `%s'...\n", - info->extract.target); + tprintf(T("Unmounting NTFS volume \"%"TS"\"...\n"), + info->extract.target); } break; case WIMLIB_PROGRESS_MSG_JOIN_STREAMS: @@ -710,16 +839,17 @@ static int imagex_progress_func(enum wimlib_progress_msg msg, case WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART: percent_done = TO_PERCENT(info->split.completed_bytes, info->split.total_bytes); - printf("Writing `%s': %"PRIu64" MiB of %"PRIu64" MiB (%u%%) written\n", - info->split.part_name, - info->split.completed_bytes >> 20, - info->split.total_bytes >> 20, - percent_done); + tprintf(T("Writing \"%"TS"\": %"PRIu64" MiB of " + "%"PRIu64" MiB (%u%%) written\n"), + info->split.part_name, + info->split.completed_bytes >> 20, + info->split.total_bytes >> 20, + percent_done); break; case WIMLIB_PROGRESS_MSG_SPLIT_END_PART: if (info->split.completed_bytes == info->split.total_bytes) { - printf("Finished writing %u split WIM parts\n", - info->split.cur_part_number); + tprintf(T("Finished writing %u split WIM parts\n"), + info->split.cur_part_number); } break; default: @@ -733,11 +863,12 @@ static int imagex_progress_func(enum wimlib_progress_msg msg, * * @first_part specifies the first part of the split WIM and it may be either * included or omitted from the glob. */ -static int open_swms_from_glob(const char *swm_glob, - const char *first_part, - int open_flags, - WIMStruct ***additional_swms_ret, - unsigned *num_additional_swms_ret) +static int +open_swms_from_glob(const tchar *swm_glob, + const tchar *first_part, + int open_flags, + WIMStruct ***additional_swms_ret, + unsigned *num_additional_swms_ret) { unsigned num_additional_swms = 0; WIMStruct **additional_swms = NULL; @@ -745,14 +876,14 @@ static int open_swms_from_glob(const char *swm_glob, int ret; /* Warning: glob() is replaced in Windows native builds */ - ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + ret = tglob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); if (ret != 0) { if (ret == GLOB_NOMATCH) { - imagex_error("Found no files for glob \"%s\"", + imagex_error(T("Found no files for glob \"%"TS"\""), swm_glob); } else { - imagex_error_with_errno("Failed to process glob " - "\"%s\"", swm_glob); + imagex_error_with_errno(T("Failed to process glob \"%"TS"\""), + swm_glob); } ret = -1; goto out; @@ -760,13 +891,13 @@ static int open_swms_from_glob(const char *swm_glob, num_additional_swms = globbuf.gl_pathc; additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0])); if (!additional_swms) { - imagex_error("Out of memory"); + imagex_error(T("Out of memory")); ret = -1; goto out_globfree; } unsigned offset = 0; for (unsigned i = 0; i < num_additional_swms; i++) { - if (strcmp(globbuf.gl_pathv[i], first_part) == 0) { + if (tstrcmp(globbuf.gl_pathv[i], first_part) == 0) { offset++; continue; } @@ -792,12 +923,13 @@ out: } -static unsigned parse_num_threads(const char *optarg) +static unsigned +parse_num_threads(const tchar *optarg) { - char *tmp; - unsigned nthreads = strtoul(optarg, &tmp, 10); + tchar *tmp; + unsigned nthreads = tstrtoul(optarg, &tmp, 10); if (nthreads == UINT_MAX || *tmp || tmp == optarg) { - imagex_error("Number of threads must be a non-negative integer!"); + imagex_error(T("Number of threads must be a non-negative integer!")); return UINT_MAX; } else { return nthreads; @@ -807,7 +939,8 @@ static unsigned parse_num_threads(const char *optarg) /* Apply one image, or all images, from a WIM file into a directory, OR apply * one image from a WIM file to a NTFS volume. */ -static int imagex_apply(int argc, char **argv) +static int +imagex_apply(int argc, tchar **argv) { int c; int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; @@ -815,12 +948,12 @@ static int imagex_apply(int argc, char **argv) int num_images; WIMStruct *w; int ret; - const char *wimfile; - const char *target; - const char *image_num_or_name; + const tchar *wimfile; + const tchar *target; + const tchar *image_num_or_name; int extract_flags = WIMLIB_EXTRACT_FLAG_SEQUENTIAL; - const char *swm_glob = NULL; + const tchar *swm_glob = NULL; WIMStruct **additional_swms = NULL; unsigned num_additional_swms = 0; @@ -861,7 +994,7 @@ static int imagex_apply(int argc, char **argv) wimfile = argv[0]; if (argc == 2) { - image_num_or_name = "1"; + image_num_or_name = T("1"); target = argv[1]; } else { image_num_or_name = argv[1]; @@ -879,8 +1012,8 @@ static int imagex_apply(int argc, char **argv) num_images = wimlib_get_num_images(w); if (argc == 2 && num_images != 1) { - imagex_error("`%s' contains %d images; Please select one " - "(or all)", wimfile, num_images); + imagex_error(T("\"%"TS"\" contains %d images; Please select one " + "(or all)"), wimfile, num_images); usage(APPLY); ret = -1; goto out; @@ -896,13 +1029,14 @@ static int imagex_apply(int argc, char **argv) struct stat stbuf; - ret = stat(target, &stbuf); + ret = tstat(target, &stbuf); if (ret == 0) { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) extract_flags |= WIMLIB_EXTRACT_FLAG_NTFS; } else { if (errno != ENOENT) { - imagex_error_with_errno("Failed to stat `%s'", target); + imagex_error_with_errno(T("Failed to stat \"%"TS"\""), + target); ret = -1; goto out; } @@ -915,7 +1049,7 @@ static int imagex_apply(int argc, char **argv) additional_swms, num_additional_swms, imagex_progress_func); if (ret == 0) - printf("Done applying WIM image.\n"); + tprintf(T("Done applying WIM image.\n")); #ifdef __WIN32__ win32_release_restore_privileges(); #endif @@ -933,34 +1067,35 @@ out: * directory trees. 'wimlib-imagex capture': create a new WIM file containing * the desired image. 'wimlib-imagex append': add a new image to an existing * WIM file. */ -static int imagex_capture_or_append(int argc, char **argv) +static int +imagex_capture_or_append(int argc, tchar **argv) { int c; int open_flags = 0; int add_image_flags = 0; int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_XPRESS; - const char *wimfile; - const char *name; - const char *desc; - const char *flags_element = NULL; + const tchar *wimfile; + const tchar *name; + const tchar *desc; + const tchar *flags_element = NULL; WIMStruct *w = NULL; int ret; int cur_image; - int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND; + int cmd = tstrcmp(argv[0], T("append")) ? CAPTURE : APPEND; unsigned num_threads = 0; - char *source; + tchar *source; size_t source_name_len; - char *source_copy; + tchar *source_copy; - const char *config_file = NULL; - char *config_str = NULL; + const tchar *config_file = NULL; + tchar *config_str = NULL; size_t config_len; bool source_list = false; - size_t source_list_nbytes; - char *source_list_contents = NULL; + size_t source_list_nchars; + tchar *source_list_contents = NULL; bool capture_sources_malloced = false; struct wimlib_capture_source *capture_sources; size_t num_sources; @@ -1030,26 +1165,26 @@ static int imagex_capture_or_append(int argc, char **argv) /* Set default name to SOURCE argument, omitting any directory * prefixes and trailing slashes. This requires making a copy * of @source. */ - source_name_len = strlen(source); - source_copy = alloca(source_name_len + 1); - name = basename(strcpy(source_copy, source)); + source_name_len = tstrlen(source); + source_copy = alloca((source_name_len + 1) * sizeof(tchar)); + name = tbasename(tstrcpy(source_copy, source)); } /* Image description defaults to NULL if not given. */ desc = (argc >= 4) ? argv[3] : NULL; if (source_list) { /* Set up capture sources in source list mode */ - if (source[0] == '-' && source[1] == '\0') { - source_list_contents = stdin_get_contents(&source_list_nbytes); + if (source[0] == T('-') && source[1] == T('\0')) { + source_list_contents = stdin_get_text_contents(&source_list_nchars); } else { - source_list_contents = file_get_contents(source, - &source_list_nbytes); + source_list_contents = file_get_text_contents(source, + &source_list_nchars); } if (!source_list_contents) return -1; - capture_sources = parse_source_list(source_list_contents, - source_list_nbytes, + capture_sources = parse_source_list(&source_list_contents, + source_list_nchars, &num_sources); if (!capture_sources) { ret = -1; @@ -1067,7 +1202,7 @@ static int imagex_capture_or_append(int argc, char **argv) } if (config_file) { - config_str = file_get_contents(config_file, &config_len); + config_str = file_get_text_contents(config_file, &config_len); if (!config_str) { ret = -1; goto out; @@ -1084,16 +1219,17 @@ static int imagex_capture_or_append(int argc, char **argv) if (!source_list) { struct stat stbuf; - ret = stat(source, &stbuf); + ret = tstat(source, &stbuf); if (ret == 0) { if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) { - printf("Capturing WIM image from NTFS filesystem on `%s'\n", - source); + tprintf(T("Capturing WIM image from NTFS " + "filesystem on \"%"TS"\"\n"), source); add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS; } } else { if (errno != ENOENT) { - imagex_error_with_errno("Failed to stat `%s'", source); + imagex_error_with_errno(T("Failed to stat " + "\"%"TS"\""), source); ret = -1; goto out; } @@ -1108,7 +1244,7 @@ static int imagex_capture_or_append(int argc, char **argv) (config_str ? config_str : default_capture_config), (config_str ? config_len : - strlen(default_capture_config)), + tstrlen(default_capture_config)), add_image_flags, imagex_progress_func); if (ret != 0) @@ -1134,7 +1270,8 @@ static int imagex_capture_or_append(int argc, char **argv) if (ret == WIMLIB_ERR_REOPEN) ret = 0; if (ret != 0) - imagex_error("Failed to write the WIM file `%s'", wimfile); + imagex_error(T("Failed to write the WIM file \"%"TS"\""), + wimfile); out_release_privs: #ifdef __WIN32__ win32_release_capture_privileges(); @@ -1149,13 +1286,14 @@ out: } /* Remove image(s) from a WIM. */ -static int imagex_delete(int argc, char **argv) +static int +imagex_delete(int argc, tchar **argv) { int c; int open_flags = 0; int write_flags = 0; - const char *wimfile; - const char *image_num_or_name; + const tchar *wimfile; + const tchar *image_num_or_name; WIMStruct *w; int image; int ret; @@ -1179,9 +1317,9 @@ static int imagex_delete(int argc, char **argv) if (argc != 2) { if (argc < 1) - imagex_error("Must specify a WIM file"); + imagex_error(T("Must specify a WIM file")); if (argc < 2) - imagex_error("Must specify an image"); + imagex_error(T("Must specify an image")); usage(DELETE); return -1; } @@ -1205,7 +1343,7 @@ static int imagex_delete(int argc, char **argv) ret = wimlib_delete_image(w, image); if (ret != 0) { - imagex_error("Failed to delete image from `%s'", wimfile); + imagex_error(T("Failed to delete image from \"%"TS"\""), wimfile); goto out; } @@ -1213,8 +1351,8 @@ static int imagex_delete(int argc, char **argv) if (ret == WIMLIB_ERR_REOPEN) ret = 0; if (ret != 0) { - imagex_error("Failed to write the file `%s' with image " - "deleted", wimfile); + imagex_error(T("Failed to write the file \"%"TS"\" with image " + "deleted"), wimfile); } out: wimlib_free(w); @@ -1222,21 +1360,22 @@ out: } /* Print the files contained in an image(s) in a WIM file. */ -static int imagex_dir(int argc, char **argv) +static int +imagex_dir(int argc, tchar **argv) { - const char *wimfile; + const tchar *wimfile; WIMStruct *w; int image; int ret; int num_images; if (argc < 2) { - imagex_error("Must specify a WIM file"); + imagex_error(T("Must specify a WIM file")); usage(DIR); return -1; } if (argc > 3) { - imagex_error("Too many arguments"); + imagex_error(T("Too many arguments")); usage(DIR); return -1; } @@ -1257,8 +1396,8 @@ static int imagex_dir(int argc, char **argv) * choose that one; otherwise, print an error. */ num_images = wimlib_get_num_images(w); if (num_images != 1) { - imagex_error("The file `%s' contains %d images; Please " - "select one.", wimfile, num_images); + imagex_error(T("The file \"%"TS"\" contains %d images; Please " + "select one."), wimfile, num_images); usage(DIR); ret = -1; goto out; @@ -1274,7 +1413,8 @@ out: /* Exports one, or all, images from a WIM file to a new WIM file or an existing * WIM file. */ -static int imagex_export(int argc, char **argv) +static int +imagex_export(int argc, tchar **argv) { int c; int open_flags = 0; @@ -1282,18 +1422,18 @@ static int imagex_export(int argc, char **argv) int write_flags = 0; int compression_type = WIMLIB_COMPRESSION_TYPE_NONE; bool compression_type_specified = false; - const char *src_wimfile; - const char *src_image_num_or_name; - const char *dest_wimfile; - const char *dest_name; - const char *dest_desc; + const tchar *src_wimfile; + const tchar *src_image_num_or_name; + const tchar *dest_wimfile; + const tchar *dest_name; + const tchar *dest_desc; WIMStruct *src_w = NULL; WIMStruct *dest_w = NULL; int ret; int image; struct stat stbuf; bool wim_is_new; - const char *swm_glob = NULL; + const tchar *swm_glob = NULL; WIMStruct **additional_swms = NULL; unsigned num_additional_swms = 0; unsigned num_threads = 0; @@ -1349,14 +1489,14 @@ static int imagex_export(int argc, char **argv) /* Determine if the destination is an existing file or not. * If so, we try to append the exported image(s) to it; otherwise, we * create a new WIM containing the exported image(s). */ - if (stat(dest_wimfile, &stbuf) == 0) { + if (tstat(dest_wimfile, &stbuf) == 0) { int dest_ctype; wim_is_new = false; /* Destination file exists. */ if (!S_ISREG(stbuf.st_mode)) { - imagex_error("`%s' is not a regular file", + imagex_error(T("\"%"TS"\" is not a regular file"), dest_wimfile); ret = -1; goto out; @@ -1374,9 +1514,9 @@ static int imagex_export(int argc, char **argv) if (compression_type_specified && compression_type != dest_ctype) { - imagex_error("Cannot specify a compression type that is " - "not the same as that used in the " - "destination WIM"); + imagex_error(T("Cannot specify a compression type that is " + "not the same as that used in the " + "destination WIM")); ret = -1; goto out; } @@ -1390,7 +1530,7 @@ static int imagex_export(int argc, char **argv) if (ret != 0) goto out; } else { - imagex_error_with_errno("Cannot stat file `%s'", + imagex_error_with_errno(T("Cannot stat file \"%"TS"\""), dest_wimfile); ret = -1; goto out; @@ -1439,7 +1579,8 @@ out: /* Prints information about a WIM file; also can mark an image as bootable, * change the name of an image, or change the description of an image. */ -static int imagex_info(int argc, char **argv) +static int +imagex_info(int argc, tchar **argv) { int c; bool boot = false; @@ -1449,11 +1590,11 @@ static int imagex_info(int argc, char **argv) bool xml = false; bool metadata = false; bool short_header = true; - const char *xml_out_file = NULL; - const char *wimfile; - const char *image_num_or_name = "all"; - const char *new_name = NULL; - const char *new_desc = NULL; + const tchar *xml_out_file = NULL; + const tchar *wimfile; + const tchar *image_num_or_name = T("all"); + const tchar *new_name = NULL; + const tchar *new_desc = NULL; WIMStruct *w; FILE *fp; int image; @@ -1525,13 +1666,14 @@ static int imagex_info(int argc, char **argv) part_number = wimlib_get_part_number(w, &total_parts); image = wimlib_resolve_image(w, image_num_or_name); - if (image == WIMLIB_NO_IMAGE && strcmp(image_num_or_name, "0") != 0) { - imagex_error("The image `%s' does not exist", + if (image == WIMLIB_NO_IMAGE && tstrcmp(image_num_or_name, T("0"))) { + imagex_error(T("The image \"%"TS"\" does not exist"), image_num_or_name); - if (boot) - imagex_error("If you would like to set the boot " - "index to 0, specify image \"0\" with " - "the --boot flag."); + if (boot) { + imagex_error(T("If you would like to set the boot " + "index to 0, specify image \"0\" with " + "the --boot flag.")); + } ret = WIMLIB_ERR_INVALID_IMAGE; goto out; } @@ -1540,8 +1682,8 @@ static int imagex_info(int argc, char **argv) if (num_images == 0) { if (boot) { - imagex_error("--boot is meaningless on a WIM with no " - "images"); + imagex_error(T("--boot is meaningless on a WIM with no " + "images")); ret = WIMLIB_ERR_INVALID_IMAGE; goto out; } @@ -1549,16 +1691,16 @@ static int imagex_info(int argc, char **argv) if (image == WIMLIB_ALL_IMAGES && num_images > 1) { if (boot) { - imagex_error("Cannot specify the --boot flag " - "without specifying a specific " - "image in a multi-image WIM"); + imagex_error(T("Cannot specify the --boot flag " + "without specifying a specific " + "image in a multi-image WIM")); ret = WIMLIB_ERR_INVALID_IMAGE; goto out; } if (new_name) { - imagex_error("Cannot specify the NEW_NAME " - "without specifying a specific " - "image in a multi-image WIM"); + imagex_error(T("Cannot specify the NEW_NAME " + "without specifying a specific " + "image in a multi-image WIM")); ret = WIMLIB_ERR_INVALID_IMAGE; goto out; } @@ -1571,7 +1713,7 @@ static int imagex_info(int argc, char **argv) /* Read-only operations */ if (image == WIMLIB_NO_IMAGE) { - imagex_error("`%s' is not a valid image", + imagex_error(T("\"%"TS"\" is not a valid image"), image_num_or_name); ret = WIMLIB_ERR_INVALID_IMAGE; goto out; @@ -1585,9 +1727,9 @@ static int imagex_info(int argc, char **argv) if (lookup_table) { if (total_parts != 1) { - printf("Warning: Only showing the lookup table " - "for part %d of a %d-part WIM.\n", - part_number, total_parts); + tprintf(T("Warning: Only showing the lookup table " + "for part %d of a %d-part WIM.\n"), + part_number, total_parts); } wimlib_print_lookup_table(w); } @@ -1599,18 +1741,19 @@ static int imagex_info(int argc, char **argv) } if (xml_out_file) { - fp = fopen(xml_out_file, "wb"); + fp = tfopen(xml_out_file, T("wb")); if (!fp) { - imagex_error_with_errno("Failed to open the " - "file `%s' for " - "writing ", + imagex_error_with_errno(T("Failed to open the " + "file \"%"TS"\" for " + "writing "), xml_out_file); ret = -1; goto out; } ret = wimlib_extract_xml_data(w, fp); if (fclose(fp) != 0) { - imagex_error("Failed to close the file `%s'", + imagex_error(T("Failed to close the file " + "\"%"TS"\""), xml_out_file); ret = -1; } @@ -1631,7 +1774,7 @@ static int imagex_info(int argc, char **argv) /* Modification operations */ if (total_parts != 1) { - imagex_error("Modifying a split WIM is not supported."); + imagex_error(T("Modifying a split WIM is not supported.")); ret = -1; goto out; } @@ -1639,47 +1782,47 @@ static int imagex_info(int argc, char **argv) image = 1; if (image == WIMLIB_NO_IMAGE && new_name) { - imagex_error("Cannot specify new_name (`%s') when " - "using image 0", new_name); + imagex_error(T("Cannot specify new_name (\"%"TS"\") " + "when using image 0"), new_name); ret = -1; goto out; } if (boot) { if (image == wimlib_get_boot_idx(w)) { - printf("Image %d is already marked as " - "bootable.\n", image); + tprintf(T("Image %d is already marked as " + "bootable.\n"), image); boot = false; } else { - printf("Marking image %d as bootable.\n", - image); + tprintf(T("Marking image %d as bootable.\n"), + image); wimlib_set_boot_idx(w, image); } } if (new_name) { - if (strcmp(wimlib_get_image_name(w, image), - new_name) == 0) { - printf("Image %d is already named \"%s\".\n", - image, new_name); + if (!tstrcmp(wimlib_get_image_name(w, image), new_name)) + { + tprintf(T("Image %d is already named \"%"TS"\".\n"), + image, new_name); new_name = NULL; } else { - printf("Changing the name of image %d to " - "\"%s\".\n", image, new_name); + tprintf(T("Changing the name of image %d to " + "\"%"TS"\".\n"), image, new_name); ret = wimlib_set_image_name(w, image, new_name); if (ret != 0) goto out; } } if (new_desc) { - const char *old_desc; + const tchar *old_desc; old_desc = wimlib_get_image_description(w, image); - if (old_desc && strcmp(old_desc, new_desc) == 0) { - printf("The description of image %d is already " - "\"%s\".\n", image, new_desc); + if (old_desc && !tstrcmp(old_desc, new_desc)) { + tprintf(T("The description of image %d is already " + "\"%"TS"\".\n"), image, new_desc); new_desc = NULL; } else { - printf("Changing the description of image %d " - "to \"%s\".\n", image, new_desc); + tprintf(T("Changing the description of image %d " + "to \"%"TS"\".\n"), image, new_desc); ret = wimlib_set_image_descripton(w, image, new_desc); if (ret != 0) @@ -1708,8 +1851,8 @@ static int imagex_info(int argc, char **argv) if (ret == WIMLIB_ERR_REOPEN) ret = 0; } else { - printf("The file `%s' was not modified because nothing " - "needed to be done.\n", wimfile); + tprintf(T("The file \"%"TS"\" was not modified because nothing " + "needed to be done.\n"), wimfile); ret = 0; } } @@ -1719,12 +1862,13 @@ out: } /* Join split WIMs into one part WIM */ -static int imagex_join(int argc, char **argv) +static int +imagex_join(int argc, tchar **argv) { int c; int swm_open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; int wim_write_flags = 0; - const char *output_path; + const tchar *output_path; for_opt(c, join_options) { switch (c) { @@ -1740,13 +1884,16 @@ static int imagex_join(int argc, char **argv) argv += optind; if (argc < 2) { - imagex_error("Must specify one or more split WIM (.swm) parts " - "to join"); + imagex_error(T("Must specify one or more split WIM (.swm) " + "parts to join")); goto err; } output_path = argv[0]; - return wimlib_join((const char **)++argv, --argc, output_path, - swm_open_flags, wim_write_flags, + return wimlib_join((const tchar * const *)++argv, + --argc, + output_path, + swm_open_flags, + wim_write_flags, imagex_progress_func); err: usage(JOIN); @@ -1754,23 +1901,24 @@ err: } /* Mounts an image using a FUSE mount. */ -static int imagex_mount_rw_or_ro(int argc, char **argv) +static int +imagex_mount_rw_or_ro(int argc, tchar **argv) { int c; int mount_flags = 0; int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; - const char *wimfile; - const char *dir; + const tchar *wimfile; + const tchar *dir; WIMStruct *w; int image; int num_images; int ret; - const char *swm_glob = NULL; + const tchar *swm_glob = NULL; WIMStruct **additional_swms = NULL; unsigned num_additional_swms = 0; - const char *staging_dir = NULL; + const tchar *staging_dir = NULL; - if (strcmp(argv[0], "mountrw") == 0) + if (tstrcmp(argv[0], T("mountrw")) == 0) mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; for_opt(c, mount_options) { @@ -1785,14 +1933,15 @@ static int imagex_mount_rw_or_ro(int argc, char **argv) mount_flags |= WIMLIB_MOUNT_FLAG_DEBUG; break; case 's': - if (strcasecmp(optarg, "none") == 0) + if (tstrcasecmp(optarg, T("none")) == 0) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE; - else if (strcasecmp(optarg, "xattr") == 0) + else if (tstrcasecmp(optarg, T("xattr")) == 0) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; - else if (strcasecmp(optarg, "windows") == 0) + else if (tstrcasecmp(optarg, T("windows")) == 0) mount_flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS; else { - imagex_error("Unknown stream interface \"%s\"", optarg); + imagex_error(T("Unknown stream interface \"%"TS"\""), + optarg); goto mount_usage; } break; @@ -1833,8 +1982,8 @@ static int imagex_mount_rw_or_ro(int argc, char **argv) image = 1; num_images = wimlib_get_num_images(w); if (num_images != 1) { - imagex_error("The file `%s' contains %d images; Please " - "select one.", wimfile, num_images); + imagex_error(T("The file \"%"TS"\" contains %d images; Please " + "select one."), wimfile, num_images); usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) ? MOUNTRW : MOUNT); ret = -1; @@ -1858,7 +2007,8 @@ static int imagex_mount_rw_or_ro(int argc, char **argv) ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms, num_additional_swms, staging_dir); if (ret != 0) { - imagex_error("Failed to mount image %d from `%s' on `%s'", + imagex_error(T("Failed to mount image %d from \"%"TS"\" " + "on \"%"TS"\""), image, wimfile, dir); } @@ -1877,14 +2027,15 @@ mount_usage: } /* Rebuild a WIM file */ -static int imagex_optimize(int argc, char **argv) +static int +imagex_optimize(int argc, tchar **argv) { int c; int open_flags = 0; int write_flags = WIMLIB_WRITE_FLAG_REBUILD; int ret; WIMStruct *w; - const char *wimfile; + const tchar *wimfile; off_t old_size; off_t new_size; @@ -1918,28 +2069,28 @@ static int imagex_optimize(int argc, char **argv) return ret; old_size = file_get_size(argv[0]); - printf("`%s' original size: ", wimfile); + tprintf(T("\"%"TS"\" original size: "), wimfile); if (old_size == -1) - puts("Unknown"); + tfputs(T("Unknown\n"), stdout); else - printf("%"PRIu64" KiB\n", old_size >> 10); + tprintf(T("%"PRIu64" KiB\n"), old_size >> 10); ret = wimlib_overwrite(w, write_flags, 0, imagex_progress_func); if (ret == 0) { new_size = file_get_size(argv[0]); - printf("`%s' optimized size: ", wimfile); + tprintf(T("\"%"TS"\" optimized size: "), wimfile); if (new_size == -1) - puts("Unknown"); + tfputs(T("Unknown\n"), stdout); else - printf("%"PRIu64" KiB\n", new_size >> 10); + tprintf(T("%"PRIu64" KiB\n"), new_size >> 10); - fputs("Space saved: ", stdout); + tfputs(T("Space saved: "), stdout); if (new_size != -1 && old_size != -1) { - printf("%lld KiB\n", + tprintf(T("%lld KiB\n"), ((long long)old_size - (long long)new_size) >> 10); } else { - puts("Unknown"); + tfputs(T("Unknown\n"), stdout); } } @@ -1948,13 +2099,14 @@ static int imagex_optimize(int argc, char **argv) } /* Split a WIM into a spanned set */ -static int imagex_split(int argc, char **argv) +static int +imagex_split(int argc, tchar **argv) { int c; int open_flags = WIMLIB_OPEN_FLAG_SPLIT_OK; int write_flags = 0; unsigned long part_size; - char *tmp; + tchar *tmp; int ret; WIMStruct *w; @@ -1976,10 +2128,11 @@ static int imagex_split(int argc, char **argv) usage(SPLIT); return -1; } - part_size = strtod(argv[2], &tmp) * (1 << 20); + part_size = tstrtod(argv[2], &tmp) * (1 << 20); if (tmp == argv[2] || *tmp) { - imagex_error("Invalid part size \"%s\"", argv[2]); - imagex_error("The part size must be an integer or floating-point number of megabytes."); + imagex_error(T("Invalid part size \"%"TS"\""), argv[2]); + imagex_error(T("The part size must be an integer or " + "floating-point number of megabytes.")); return -1; } ret = wimlib_open_wim(argv[0], open_flags, &w, imagex_progress_func); @@ -1991,7 +2144,8 @@ static int imagex_split(int argc, char **argv) } /* Unmounts a mounted WIM image. */ -static int imagex_unmount(int argc, char **argv) +static int +imagex_unmount(int argc, tchar **argv) { int c; int unmount_flags = 0; @@ -2023,13 +2177,13 @@ static int imagex_unmount(int argc, char **argv) ret = wimlib_unmount_image(argv[0], unmount_flags, imagex_progress_func); if (ret != 0) - imagex_error("Failed to unmount `%s'", argv[0]); + imagex_error(T("Failed to unmount \"%"TS"\""), argv[0]); return ret; } struct imagex_command { - const char *name; - int (*func)(int , char **); + const tchar *name; + int (*func)(int , tchar **); int cmd; }; @@ -2038,39 +2192,43 @@ struct imagex_command { p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++) static const struct imagex_command imagex_commands[] = { - {"append", imagex_capture_or_append, APPEND}, - {"apply", imagex_apply, APPLY}, - {"capture", imagex_capture_or_append, CAPTURE}, - {"delete", imagex_delete, DELETE}, - {"dir", imagex_dir, DIR}, - {"export", imagex_export, EXPORT}, - {"info", imagex_info, INFO}, - {"join", imagex_join, JOIN}, - {"mount", imagex_mount_rw_or_ro, MOUNT}, - {"mountrw", imagex_mount_rw_or_ro, MOUNTRW}, - {"optimize",imagex_optimize, OPTIMIZE}, - {"split", imagex_split, SPLIT}, - {"unmount", imagex_unmount, UNMOUNT}, + {T("append"), imagex_capture_or_append, APPEND}, + {T("apply"), imagex_apply, APPLY}, + {T("capture"), imagex_capture_or_append, CAPTURE}, + {T("delete"), imagex_delete, DELETE}, + {T("dir"), imagex_dir, DIR}, + {T("export"), imagex_export, EXPORT}, + {T("info"), imagex_info, INFO}, + {T("join"), imagex_join, JOIN}, + {T("mount"), imagex_mount_rw_or_ro, MOUNT}, + {T("mountrw"), imagex_mount_rw_or_ro, MOUNTRW}, + {T("optimize"),imagex_optimize, OPTIMIZE}, + {T("split"), imagex_split, SPLIT}, + {T("unmount"), imagex_unmount, UNMOUNT}, }; -static void version() +static void +version() { - static const char *s = - IMAGEX_PROGNAME " (" PACKAGE ") " PACKAGE_VERSION "\n" - "Copyright (C) 2012, 2013 Eric Biggers\n" - "License GPLv3+; GNU GPL version 3 or later .\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n" - "\n" - "Report bugs to "PACKAGE_BUGREPORT".\n"; - fputs(s, stdout); + static const tchar *s = + T( +IMAGEX_PROGNAME " (" PACKAGE ") " PACKAGE_VERSION "\n" +"Copyright (C) 2012, 2013 Eric Biggers\n" +"License GPLv3+; GNU GPL version 3 or later .\n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +"\n" +"Report bugs to "PACKAGE_BUGREPORT".\n" + ); + tfputs(s, stdout); } -static void help_or_version(int argc, char **argv) +static void +help_or_version(int argc, tchar **argv) { int i; - const char *p; + const tchar *p; const struct imagex_command *cmd; for (i = 1; i < argc; i++) { @@ -2081,9 +2239,9 @@ static void help_or_version(int argc, char **argv) continue; if (*p == '-') p++; - if (strcmp(p, "help") == 0) { + if (!tstrcmp(p, T("help"))) { for_imagex_command(cmd) { - if (strcmp(cmd->name, argv[1]) == 0) { + if (!tstrcmp(cmd->name, argv[1])) { usage(cmd->cmd); exit(0); } @@ -2091,7 +2249,7 @@ static void help_or_version(int argc, char **argv) usage_all(); exit(0); } - if (strcmp(p, "version") == 0) { + if (!tstrcmp(p, T("version"))) { version(); exit(0); } @@ -2099,43 +2257,70 @@ static void help_or_version(int argc, char **argv) } -static void usage(int cmd_type) +static void +usage(int cmd_type) { const struct imagex_command *cmd; - printf("Usage: %s", usage_strings[cmd_type]); + tprintf(T("Usage:\n%"TS), usage_strings[cmd_type]); for_imagex_command(cmd) { - if (cmd->cmd == cmd_type) - printf("\nTry `man "IMAGEX_PROGNAME"-%s' for more details.\n", - cmd->name); + if (cmd->cmd == cmd_type) { + tprintf(T("\nTry `man "IMAGEX_PROGNAME"-%"TS"' " + "for more details.\n"), cmd->name); + } } } -static void usage_all() +static void +usage_all() { - puts("IMAGEX: Usage:"); + tfputs(T("Usage:\n"), stdout); for (int i = 0; i < ARRAY_LEN(usage_strings); i++) - printf(" %s", usage_strings[i]); - static const char *extra = + tprintf(T(" %"TS), usage_strings[i]); + static const tchar *extra = + T( " "IMAGEX_PROGNAME" --help\n" " "IMAGEX_PROGNAME" --version\n" "\n" " The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n" "\n" " Try `man "IMAGEX_PROGNAME"' for more information.\n" - ; - fputs(extra, stdout); + ); + tfputs(extra, stdout); } -/* Entry point for wimlib's ImageX implementation */ -int main(int argc, char **argv) +/* Entry point for wimlib's ImageX implementation. On UNIX the command + * arguments will just be 'char' strings (ideally UTF-8 encoded, but could be + * something else), while an Windows the command arguments will be UTF-16LE + * encoded 'wchar_t' strings. */ +int +#ifdef __WIN32__ +wmain(int argc, wchar_t **argv, wchar_t **envp) +#else +main(int argc, char **argv) +#endif { const struct imagex_command *cmd; int ret; +#ifndef __WIN32__ setlocale(LC_ALL, ""); + { + char *codeset = nl_langinfo(CODESET); + if (!strstr(codeset, "UTF-8") && + !strstr(codeset, "UTF8") && + !strstr(codeset, "utf-8") && + !strstr(codeset, "utf8")) + { + fputs( +"WARNING: Running "IMAGEX_PROGNAME" in a UTF-8 locale is recommended!\n" +" (Maybe try: `export LANG=en_US.UTF-8'?\n", stderr); + + } + } +#endif /* !__WIN32__ */ if (argc < 2) { - imagex_error("No command specified"); + imagex_error(T("No command specified")); usage_all(); return 1; } @@ -2156,13 +2341,13 @@ int main(int argc, char **argv) /* Search for the function to handle the ImageX subcommand. */ for_imagex_command(cmd) { - if (strcmp(cmd->name, *argv) == 0) { + if (!tstrcmp(cmd->name, *argv)) { ret = cmd->func(argc, argv); goto out_check_write_error; } } - imagex_error("Unrecognized command: `%s'", argv[0]); + imagex_error(T("Unrecognized command: `%"TS"'"), argv[0]); usage_all(); return 1; out_check_write_error: @@ -2172,7 +2357,7 @@ out_check_write_error: * there was a write error. */ if (cmd == &imagex_commands[INFO] || cmd == &imagex_commands[DIR]) { if (ferror(stdout) || fclose(stdout)) { - imagex_error_with_errno("output error"); + imagex_error_with_errno(T("output error")); if (ret == 0) ret = -1; } @@ -2183,11 +2368,11 @@ out: * indicates a wimlib error code from which an error message can be * printed. */ if (ret > 0) { - imagex_error("Exiting with error code %d:\n" - " %s.", ret, + imagex_error(T("Exiting with error code %d:\n" + " %"TS"."), ret, wimlib_get_error_string(ret)); if (ret == WIMLIB_ERR_NTFS_3G && errno != 0) - imagex_error_with_errno("errno"); + imagex_error_with_errno(T("errno")); } /* Make the library free any resources it's holding (not strictly diff --git a/programs/wgetopt.c b/programs/wgetopt.c new file mode 100644 index 00000000..5c9b74e4 --- /dev/null +++ b/programs/wgetopt.c @@ -0,0 +1,750 @@ +/* + * wgetopt.c: Wide-character versions of getopt, getopt_long, and + * getopt_long_only. + * + * This has been modified from the original, which is + * Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + * 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of wimlib, a library for working with WIM files. + * + * wimlib is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with wimlib; if not, see http://www.gnu.org/licenses/. + */ + +#include "wgetopt.h" +#include +#include + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +wchar_t *woptarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `woptind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int woptind = 1; + +/* Formerly, initialization of getopt depended on woptind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static wchar_t *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int wopterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int woptopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `woptind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,woptind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (wchar_t **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = woptind; + wchar_t *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (woptind - last_nonopt); + last_nonopt = woptind; +} + +/* Initialize the internal data when the first call is made. */ + +static const wchar_t * +_getopt_initialize (int argc, wchar_t *const *argv, const wchar_t *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = woptind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == L'-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == L'+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `woptind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `woptind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `wopterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `woptarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `woptarg', otherwise `woptarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct woption' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_wgetopt_internal (int argc, wchar_t *const *argv, const wchar_t *optstring, + const struct woption *longopts, int *longind, int long_only) +{ + woptarg = NULL; + + if (woptind == 0 || !__getopt_initialized) + { + if (woptind == 0) + woptind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[woptind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +# define NONOPTION_P (argv[woptind][0] != L'-' || argv[woptind][1] == L'\0') + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > woptind) + last_nonopt = woptind; + if (first_nonopt > woptind) + first_nonopt = woptind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != woptind) + exchange ((wchar_t **) argv); + else if (last_nonopt != woptind) + first_nonopt = woptind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (woptind < argc && NONOPTION_P) + woptind++; + last_nonopt = woptind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (woptind != argc && !wcscmp (argv[woptind], L"--")) + { + woptind++; + + if (first_nonopt != last_nonopt && last_nonopt != woptind) + exchange ((wchar_t **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = woptind; + last_nonopt = argc; + + woptind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (woptind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + woptind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + woptarg = argv[woptind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[woptind] + 1 + + (longopts != NULL && argv[woptind][1] == L'-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[woptind][1] == L'-' + || (long_only && (argv[woptind][2] || !wcschr (optstring, argv[woptind][1]))))) + { + wchar_t *nameend; + const struct woption *p; + const struct woption *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != L'='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!wcsncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) wcslen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (wopterr) + fwprintf (stderr, L"%ls: option `%ls' is ambiguous\n", + argv[0], argv[woptind]); + nextchar += wcslen (nextchar); + woptind++; + woptopt = 0; + return L'?'; + } + + if (pfound != NULL) + { + option_index = indfound; + woptind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + woptarg = nameend + 1; + else + { + if (wopterr) { + if (argv[woptind - 1][1] == L'-') + /* --option */ + fwprintf (stderr, + L"%ls: option `--%ls' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fwprintf (stderr, + L"%ls: option `%lc%ls' doesn't allow an argument\n", + argv[0], argv[woptind - 1][0], pfound->name); + } + + nextchar += wcslen (nextchar); + + woptopt = pfound->val; + return L'?'; + } + } + else if (pfound->has_arg == 1) + { + if (woptind < argc) + woptarg = argv[woptind++]; + else + { + if (wopterr) + fwprintf (stderr, + L"%ls: option `%ls' requires an argument\n", + argv[0], argv[woptind - 1]); + nextchar += wcslen (nextchar); + woptopt = pfound->val; + return optstring[0] == L':' ? L':' : L'?'; + } + } + nextchar += wcslen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[woptind][1] == L'-' + || wcschr (optstring, *nextchar) == NULL) + { + if (wopterr) + { + if (argv[woptind][1] == '-') + /* --option */ + fwprintf (stderr, L"%ls: unrecognized option `--%ls'\n", + argv[0], nextchar); + else + /* +option or -option */ + fwprintf (stderr, L"%ls: unrecognized option `%lc%ls'\n", + argv[0], argv[woptind][0], nextchar); + } + nextchar = (wchar_t *) L""; + woptind++; + woptopt = 0; + return L'?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + wchar_t c = *nextchar++; + wchar_t *temp = wcschr (optstring, c); + + /* Increment `woptind' when we start to process its last character. */ + if (*nextchar == L'\0') + ++woptind; + + if (temp == NULL || c == L':') + { + if (wopterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fwprintf (stderr, L"%ls: illegal option -- %lc\n", + argv[0], c); + else + fwprintf (stderr, L"%ls: invalid option -- %lc\n", + argv[0], c); + } + woptopt = c; + return L'?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == L'W' && temp[1] == L';') + { + wchar_t *nameend; + const struct woption *p; + const struct woption *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != L'\0') + { + woptarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + woptind++; + } + else if (woptind == argc) + { + if (wopterr) + { + /* 1003.2 specifies the format of this message. */ + fwprintf (stderr, L"%ls: option requires an argument -- %lc\n", + argv[0], c); + } + woptopt = c; + if (optstring[0] == L':') + c = L':'; + else + c = L'?'; + return c; + } + else + /* We already incremented `woptind' once; + increment it again when taking next ARGV-elt as argument. */ + woptarg = argv[woptind++]; + + /* woptarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = woptarg; *nameend && *nameend != L'='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!wcsncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == wcslen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (wopterr) + fwprintf (stderr, L"%ls: option `-W %ls' is ambiguous\n", + argv[0], argv[woptind]); + nextchar += wcslen (nextchar); + woptind++; + return L'?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + woptarg = nameend + 1; + else + { + if (wopterr) + fwprintf (stderr, L"\ +%ls: option `-W %ls' doesn't allow an argument\n", + argv[0], pfound->name); + + nextchar += wcslen (nextchar); + return L'?'; + } + } + else if (pfound->has_arg == 1) + { + if (woptind < argc) + woptarg = argv[woptind++]; + else + { + if (wopterr) + fwprintf (stderr, + L"%ls: option `%ls' requires an argument\n", + argv[0], argv[woptind - 1]); + nextchar += wcslen (nextchar); + return optstring[0] == L':' ? L':' : L'?'; + } + } + nextchar += wcslen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return L'W'; /* Let the application handle it. */ + } + if (temp[1] == L':') + { + if (temp[2] == L':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != L'\0') + { + woptarg = nextchar; + woptind++; + } + else + woptarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != L'\0') + { + woptarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + woptind++; + } + else if (woptind == argc) + { + if (wopterr) + { + /* 1003.2 specifies the format of this message. */ + fwprintf (stderr, + L"%ls: option requires an argument -- %lc\n", + argv[0], c); + } + woptopt = c; + if (optstring[0] == L':') + c = L':'; + else + c = L'?'; + } + else + /* We already incremented `woptind' once; + increment it again when taking next ARGV-elt as argument. */ + woptarg = argv[woptind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring) +{ + return _wgetopt_internal (argc, argv, optstring, + (const struct woption *) 0, + (int *) 0, + 0); +} + +int +wgetopt_long (int argc, wchar_t * const *argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) +{ + return _wgetopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ +int +wgetopt_long_only (int argc, wchar_t * const *argv, const wchar_t *options, + const struct woption *long_options, int *opt_index) +{ + return _wgetopt_internal (argc, argv, options, long_options, opt_index, 1); +} diff --git a/programs/wgetopt.h b/programs/wgetopt.h new file mode 100644 index 00000000..9b2f3a3b --- /dev/null +++ b/programs/wgetopt.h @@ -0,0 +1,31 @@ +#ifndef _WGETOPT_H +#define _WGETOPT_H + +#include + +extern wchar_t *woptarg; +extern int woptind, wopterr, woptopt; + +struct woption { + const wchar_t *name; + int has_arg; + int *flag; + int val; +}; + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern int +wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring); + +extern int +wgetopt_long(int argc, wchar_t * const *argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); + +extern int +wgetopt_long_only(int argc, wchar_t *const *argv, const wchar_t *options, + const struct woption *long_options, int *opt_index); + +#endif diff --git a/src/add_image.c b/src/add_image.c index f435c3f3..5f7a183c 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -41,12 +41,7 @@ #include #include #include - -#if TCHAR_IS_UTF16LE -# include -#else -# include -#endif +#include #include @@ -144,7 +139,7 @@ static int unix_build_dentry_tree(struct wim_dentry **root_ret, const char *root_disk_path, struct wim_lookup_table *lookup_table, - struct sd_set *sd, + struct sd_set *sd_set, const struct capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, @@ -526,19 +521,20 @@ init_capture_config(struct capture_config *config, if (*pp == T('\\')) *pp = T('/'); - /* Remove drive letter - * XXX maybe keep drive letter on Windows */ + /* Remove drive letter (UNIX only) */ + #ifndef __WIN32__ if (eol - p > 2 && istalpha(*p) && *(p + 1) == T(':')) p += 2; + #endif ret = 0; - if (tstrcmp(p, T("[ExclusionList]")) == 0) + if (!tstrcmp(p, T("[ExclusionList]"))) type = EXCLUSION_LIST; - else if (tstrcmp(p, T("[ExclusionException]")) == 0) + else if (!tstrcmp(p, T("[ExclusionException]"))) type = EXCLUSION_EXCEPTION; - else if (tstrcmp(p, T("[CompressionExclusionList]")) == 0) + else if (!tstrcmp(p, T("[CompressionExclusionList]"))) type = COMPRESSION_EXCLUSION_LIST; - else if (tstrcmp(p, T("[AlignmentList]")) == 0) + else if (!tstrcmp(p, T("[AlignmentList]"))) type = ALIGNMENT_LIST; else if (p[0] == T('[') && tstrrchr(p, T(']'))) { ERROR("Unknown capture configuration section \"%"TS"\"", p); @@ -640,9 +636,11 @@ exclude_path(const tchar *path, const struct capture_config *config, const tchar *basename = path_basename(path); if (exclude_prefix) { wimlib_assert(tstrlen(path) >= config->prefix_num_tchars); - if (tmemcmp(config->prefix, path, config->prefix_num_tchars) == 0 - && path[config->prefix_num_tchars] == T('/')) + if (!tmemcmp(config->prefix, path, config->prefix_num_tchars) && + path[config->prefix_num_tchars] == T('/')) + { path += config->prefix_num_tchars; + } } return match_pattern(path, basename, &config->exclusion_list) && !match_pattern(path, basename, &config->exclusion_exception); @@ -666,22 +664,12 @@ canonicalize_target_path(tchar *target_path) break; } - p = target_path + tstrlen(target_path) - 1; + p = tstrchr(target_path, T('\0')) - 1; while (*p == T('/')) *p-- = T('\0'); return target_path; } -static void -zap_backslashes(tchar *s) -{ - while (*s) { - if (*s == T('\\')) - *s = T('/'); - s++; - } -} - /* Strip leading and trailing slashes from the target paths */ static void canonicalize_targets(struct wimlib_capture_source *sources, size_t num_sources) @@ -733,7 +721,7 @@ check_sorted_sources(struct wimlib_capture_source *sources, size_t num_sources, "(the NTFS volume) in NTFS mode!"); return WIMLIB_ERR_INVALID_PARAM; } - if (sources[0].wim_target_path[0] != '\0') { + if (sources[0].wim_target_path[0] != T('\0')) { ERROR("In NTFS capture mode the target path inside " "the image must be the root directory!"); return WIMLIB_ERR_INVALID_PARAM; @@ -880,7 +868,7 @@ attach_branch(struct wim_dentry **root_p, struct wim_dentry *branch, * */ parent = *root_p; while ((slash = tstrchr(target_path, T('/')))) { - *slash = '\0'; + *slash = T('\0'); dentry = get_dentry_child_with_name(parent, target_path); if (!dentry) { ret = new_filler_directory(target_path, &dentry); @@ -1112,7 +1100,7 @@ out_destroy_imd: destroy_image_metadata(&w->image_metadata[w->hdr.image_count - 1], w->lookup_table); w->hdr.image_count--; - goto out; + goto out_destroy_sd_set; out_free_branch: free_dentry_tree(branch, w->lookup_table); out_free_dentry_tree: diff --git a/src/dentry.c b/src/dentry.c index 503dbba6..677db1db 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -33,10 +33,6 @@ #include "wimlib_internal.h" #include -#ifdef TCHAR_IS_UTF16LE -# include -#endif - /* Calculates the unaligned length, in bytes, of an on-disk WIM dentry that has * a file name and short name that take the specified numbers of bytes. This * excludes any alternate data stream entries that may follow the dentry. */ @@ -91,20 +87,20 @@ get_utf16le_name(const tchar *name, utf16lechar **name_utf16le_ret, size_t name_utf16le_nbytes; int ret; #if TCHAR_IS_UTF16LE - name_utf16le_nbytes = tstrlen(name) * 2; - name_utf16le = MALLOC(name_utf16le_nbytes + 2); + name_utf16le_nbytes = tstrlen(name) * sizeof(utf16lechar); + name_utf16le = MALLOC(name_utf16le_nbytes + sizeof(utf16lechar)); if (!name_utf16le) return WIMLIB_ERR_NOMEM; - memcpy(name_utf16le, name, name_utf16le_nbytes + 2); + memcpy(name_utf16le, name, name_utf16le_nbytes + sizeof(utf16lechar)); ret = 0; #else - ret = tstr_to_utf16le(name, strlen(name), &name_utf16le, + ret = tstr_to_utf16le(name, tstrlen(name), &name_utf16le, &name_utf16le_nbytes); if (ret == 0) { if (name_utf16le_nbytes > 0xffff) { FREE(name_utf16le); - ERROR("Multibyte string \"%s\" is too long!", name); + ERROR("Multibyte string \"%"TS"\" is too long!", name); ret = WIMLIB_ERR_INVALID_UTF8_STRING; } } @@ -278,7 +274,7 @@ for_dentry_in_tree_depth(struct wim_dentry *root, } /* Calculate the full path of @dentry. The full path of its parent must have - * already been calculated. */ + * already been calculated, or it must be the root dentry. */ int calculate_dentry_full_path(struct wim_dentry *dentry, void *ignore) { @@ -289,18 +285,15 @@ calculate_dentry_full_path(struct wim_dentry *dentry, void *ignore) dentry->parent->full_path != NULL); if (dentry_is_root(dentry)) { - full_path = TMALLOC(2); + full_path = TSTRDUP(T("/")); if (!full_path) return WIMLIB_ERR_NOMEM; - full_path[0] = T('/'); - full_path[1] = T('\0'); full_path_nbytes = 1 * sizeof(tchar); } else { + const struct wim_dentry *parent; tchar *parent_full_path; u32 parent_full_path_nbytes; - const struct wim_dentry *parent; size_t filename_nbytes; - int ret; parent = dentry->parent; if (dentry_is_root(parent)) { @@ -310,31 +303,37 @@ calculate_dentry_full_path(struct wim_dentry *dentry, void *ignore) parent_full_path = parent->full_path; parent_full_path_nbytes = parent->full_path_nbytes; } + + /* Append this dentry's name as a tchar string to the full path + * of the parent followed by the path separator */ #if TCHAR_IS_UTF16LE filename_nbytes = dentry->file_name_nbytes; #else - ret = utf16le_to_mbs_nbytes(dentry->file_name, - dentry->file_name_nbytes, - &filename_nbytes); - if (ret) - return ret; + { + int ret = utf16le_to_tstr_nbytes(dentry->file_name, + dentry->file_name_nbytes, + &filename_nbytes); + if (ret) + return ret; + } #endif full_path_nbytes = parent_full_path_nbytes + sizeof(tchar) + - dentry->file_name_nbytes; + filename_nbytes; full_path = MALLOC(full_path_nbytes + sizeof(tchar)); if (!full_path) return WIMLIB_ERR_NOMEM; memcpy(full_path, parent_full_path, parent_full_path_nbytes); - full_path[parent_full_path_nbytes] = T('/'); + full_path[parent_full_path_nbytes / sizeof(tchar)] = T('/'); #if TCHAR_IS_UTF16LE - memcpy(&full_path[parent_full_path_nbytes + 1], + memcpy(&full_path[parent_full_path_nbytes / sizeof(tchar) + 1], dentry->file_name, - dentry->file_name_nbytes + sizeof(tchar)); + filename_nbytes + sizeof(tchar)); #else - utf16le_to_mbs_buf(dentry->file_name, - dentry->file_name_nbytes, - &full_path[parent_full_path_nbytes + 1]); + utf16le_to_tstr_buf(dentry->file_name, + dentry->file_name_nbytes, + &full_path[parent_full_path_nbytes / + sizeof(tchar) + 1]); #endif } FREE(dentry->full_path); @@ -448,7 +447,7 @@ get_dentry_child_with_name(const struct wim_dentry *dentry, const tchar *name) int ret; struct wim_dentry *child; - ret = tstr_to_utf16le(name, strlen(name), + ret = tstr_to_utf16le(name, tstrlen(name) * sizeof(tchar), &utf16le_name, &utf16le_name_nbytes); if (ret) { child = NULL; @@ -469,12 +468,11 @@ get_dentry_utf16le(WIMStruct *w, const utf16lechar *path, struct wim_dentry *cur_dentry, *parent_dentry; const utf16lechar *p, *pp; - parent_dentry = wim_root_dentry(w); + cur_dentry = parent_dentry = wim_root_dentry(w); p = path; while (1) { while (*p == cpu_to_le16('/')) p++; - cur_dentry = parent_dentry; if (*p == '\0') break; pp = p; @@ -516,7 +514,7 @@ get_dentry(WIMStruct *w, const tchar *path) return NULL; dentry = get_dentry_utf16le(w, path_utf16le, path_utf16le_nbytes); FREE(path_utf16le); - return cur_dentry; + return dentry; #endif } @@ -531,6 +529,21 @@ wim_pathname_to_inode(WIMStruct *w, const tchar *path) return NULL; } +/* Takes in a path of length @len in @buf, and transforms it into a string for + * the path of its parent directory. */ +static void +to_parent_name(tchar *buf, size_t len) +{ + ssize_t i = (ssize_t)len - 1; + while (i >= 0 && buf[i] == T('/')) + i--; + while (i >= 0 && buf[i] != T('/')) + i--; + while (i >= 0 && buf[i] == T('/')) + i--; + buf[i + 1] = T('\0'); +} + /* Returns the dentry that corresponds to the parent directory of @path, or NULL * if the dentry is not found. */ struct wim_dentry * @@ -549,7 +562,7 @@ int print_dentry_full_path(struct wim_dentry *dentry, void *ignore) { if (dentry->full_path) - printf("%"TS"\n", dentry->full_path); + tprintf(T("%"TS"\n"), dentry->full_path); return 0; } @@ -616,7 +629,7 @@ print_dentry(struct wim_dentry *dentry, void *lookup_table) if (dentry_has_short_name(dentry)) wimlib_printf(T("Short Name \"%"WS"\"\n"), dentry->short_name); if (dentry->full_path) - tprintf(T("Full Path = \"%"WS"\"\n"), dentry->full_path); + tprintf(T("Full Path = \"%"TS"\"\n"), dentry->full_path); lte = inode_stream_lte(dentry->d_inode, 0, lookup_table); if (lte) { @@ -690,7 +703,8 @@ new_inode() } /* Creates an unlinked directory entry. */ -int new_dentry(const tchar *name, struct wim_dentry **dentry_ret) +int +new_dentry(const tchar *name, struct wim_dentry **dentry_ret) { struct wim_dentry *dentry; int ret; @@ -938,7 +952,6 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, if (inode->i_num_ads == 0) { return NULL; } else { - int ret; size_t stream_name_utf16le_nbytes; u16 i; struct wim_ads_entry *result; @@ -951,11 +964,15 @@ inode_get_ads_entry(struct wim_inode *inode, const tchar *stream_name, #else utf16lechar *stream_name_utf16le; - ret = tstr_to_utf16le(stream_name, tstrlen(stream_name) * sizeof(tchar), - &stream_name_utf16le, - &stream_name_utf16le_nbytes); - if (ret) - return NULL; + { + int ret = tstr_to_utf16le(stream_name, + tstrlen(stream_name) * + sizeof(tchar), + &stream_name_utf16le, + &stream_name_utf16le_nbytes); + if (ret) + return NULL; + } #endif i = 0; result = NULL; @@ -1509,9 +1526,9 @@ read_dentry(const u8 metadata_resource[], u64 metadata_resource_len, * u64 reserved1; (always 0) * u64 reserved2; (always 0) * };*/ - /*DEBUG("Dentry for file or directory `%s' has %"PRIu64" extra "*/ - /*"bytes of data",*/ - /*file_name_utf8, dentry->length - calculated_size);*/ + /*DEBUG("Dentry for file or directory `%"WS"' has %"PRIu64" "*/ + /*"extra bytes of data", file_name,*/ + /*dentry->length - calculated_size);*/ } /* Read the short filename if present. Note: if there is no short diff --git a/src/dentry.h b/src/dentry.h index 6a3affcd..e8d8328d 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -142,9 +142,10 @@ struct wim_dentry { u16 file_name_nbytes; /* Length of full path name encoded using "tchars", in bytes, not - * including the terminating null terminator. */ + * including the terminating null character. */ u32 full_path_nbytes; + /* Has this dentry been extracted yet? */ u8 is_extracted : 1; /* Only used during NTFS capture */ diff --git a/src/encoding.c b/src/encoding.c index 256103a4..f5b978ab 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -1,7 +1,5 @@ /* - * encoding.c: Convert "multibyte" strings (the locale-default encoding--- - * generally, UTF-8 or something like ISO-8859-1) to UTF-16LE strings, and vice - * versa. Also, convert UTF-8 strings to multibyte strings. + * encoding.c */ /* @@ -117,11 +115,12 @@ static bool error_message_being_printed = false; earlyreturn, \ worst_case_len_expr, \ err_return, \ - err_msg) \ + err_msg, \ + modifier) \ static ICONV_LIST(iconv_##varname1##_to_##varname2, \ longname1, longname2); \ \ -int \ +modifier int \ varname1##_to_##varname2##_nbytes(const chartype1 *in, size_t in_nbytes,\ size_t *out_nbytes_ret) \ { \ @@ -154,7 +153,7 @@ varname1##_to_##varname2##_nbytes(const chartype1 *in, size_t in_nbytes,\ return ret; \ } \ \ -int \ +modifier int \ varname1##_to_##varname2##_buf(const chartype1 *in, size_t in_nbytes, \ chartype2 *out) \ { \ @@ -186,7 +185,7 @@ varname1##_to_##varname2##_buf(const chartype1 *in, size_t in_nbytes, \ return ret; \ } \ \ -int \ +modifier int \ varname1##_to_##varname2(const chartype1 *in, size_t in_nbytes, \ chartype2 **out_ret, \ size_t *out_nbytes_ret) \ @@ -228,62 +227,63 @@ varname1##_to_##varname2(const chartype1 *in, size_t in_nbytes, \ return ret; \ } -#if 0 -DEFINE_CHAR_CONVERSION_FUNCTIONS(utf16le, "UTF-16LE", utf16lechar, - mbs, "", mbchar, - false, - in_nbytes / 2 * MB_CUR_MAX, - WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE, - ERROR("Failed to convert UTF-16LE string " - "to multibyte string!"); - ERROR("This may be because the UTF-16LE data " - "could not be represented in your " - "locale's character encoding.")) -#endif - #if !TCHAR_IS_UTF16LE DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "", tchar, utf16le, "UTF-16LE", utf16lechar, false, - in_nbytes * 2, + in_nbytes * 4, WIMLIB_ERR_INVALID_MULTIBYTE_STRING, ERROR_WITH_ERRNO("Failed to convert multibyte " "string \"%"TS"\" to UTF-16LE string!", in); ERROR("If the data you provided was UTF-8, please make sure " - "the character encoding of your current locale is UTF-8.")) + "the character encoding of your current locale is UTF-8."), + ) + +DEFINE_CHAR_CONVERSION_FUNCTIONS(utf16le, "UTF-16LE", utf16lechar, + tstr, "", tchar, + false, + in_nbytes * 2, + WIMLIB_ERR_UNICODE_STRING_NOT_REPRESENTABLE, + ERROR("Failed to convert UTF-16LE string to " + "multibyte string!"); + ERROR("This may be because the UTF-16LE string " + "could not be represented in your " + "locale's character encoding."), + ) #endif /* tchar to UTF-8 and back */ #if TCHAR_IS_UTF16LE DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "UTF16-LE", tchar, - utf8, "UTF-8", utf8char, + utf8, "UTF-8", char, false, in_nbytes * 2, - WIMLIB_ERR_INVALID_MULTIBYTE_STRING, + WIMLIB_ERR_INVALID_UTF16_STRING, ERROR_WITH_ERRNO("Failed to convert UTF-16LE " - "string \"%"TS"\" to UTF-8 string!", in); - ) + "string \"%"TS"\" to UTF-8 string!", in), + static) -DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", utf8char, +DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", char, tstr, "UTF16-LE", tchar, false, in_nbytes * 2, - WIMLIB_ERR_INVALID_MULTIBYTE_STRING, + WIMLIB_ERR_INVALID_UTF8_STRING, ERROR_WITH_ERRNO("Failed to convert UTF-8 string " - "to UTF-16LE string!"); - ) + "to UTF-16LE string!"), + static) #else DEFINE_CHAR_CONVERSION_FUNCTIONS(tstr, "", tchar, - utf8, "UTF-8", utf8char, + utf8, "UTF-8", char, wimlib_mbs_is_utf8, in_nbytes * 4, WIMLIB_ERR_INVALID_MULTIBYTE_STRING, ERROR_WITH_ERRNO("Failed to convert multibyte " "string \"%"TS"\" to UTF-8 string!", in); ERROR("If the data you provided was UTF-8, please make sure " - "the character encoding of your current locale is UTF-8.");) + "the character encoding of your current locale is UTF-8."), + static) -DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", utf8char, +DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", char, tstr, "", tchar, wimlib_mbs_is_utf8, in_nbytes * 4, @@ -292,18 +292,19 @@ DEFINE_CHAR_CONVERSION_FUNCTIONS(utf8, "UTF-8", utf8char, "multibyte string!"); ERROR("This may be because the UTF-8 data " "could not be represented in your " - "locale's character encoding.");) + "locale's character encoding."), + static) #endif int -tstr_to_utf8_simple(const tchar *tstr, utf8char **out) +tstr_to_utf8_simple(const tchar *tstr, char **out) { size_t out_nbytes; return tstr_to_utf8(tstr, tstrlen(tstr), out, &out_nbytes); } int -utf8_to_tstr_simple(const utf8char *utf8str, tchar **out) +utf8_to_tstr_simple(const char *utf8str, tchar **out) { size_t out_nbytes; return utf8_to_tstr(utf8str, strlen(utf8str), out, &out_nbytes); @@ -326,21 +327,10 @@ iconv_cleanup(struct iconv_list_head *head) void iconv_global_cleanup() { - /*iconv_cleanup(&iconv_utf16le_to_mbs);*/ + iconv_cleanup(&iconv_utf8_to_tstr); + iconv_cleanup(&iconv_tstr_to_utf8); #if !TCHAR_IS_UTF16LE - iconv_cleanup(&iconv_mbs_to_utf16le); + iconv_cleanup(&iconv_utf16le_to_tstr); + iconv_cleanup(&iconv_tstr_to_utf16le); #endif - /*iconv_cleanup(&iconv_utf8_to_mbs);*/ } - -#if 0 -bool -utf8_str_contains_nonascii_chars(const utf8char *utf8_str) -{ - do { - if ((unsigned char)*utf8_str > 127) - return true; - } while (*++utf8_str); - return false; -} -#endif diff --git a/src/export_image.c b/src/export_image.c index dfd9f058..b62f67c5 100644 --- a/src/export_image.c +++ b/src/export_image.c @@ -26,10 +26,11 @@ #include "lookup_table.h" #include "xml.h" -static int inode_allocate_needed_ltes(struct wim_inode *inode, - struct wim_lookup_table *src_lookup_table, - struct wim_lookup_table *dest_lookup_table, - struct list_head *lte_list_head) +static int +inode_allocate_needed_ltes(struct wim_inode *inode, + struct wim_lookup_table *src_lookup_table, + struct wim_lookup_table *dest_lookup_table, + struct list_head *lte_list_head) { struct wim_lookup_table_entry *src_lte, *dest_lte; unsigned i; @@ -53,10 +54,11 @@ static int inode_allocate_needed_ltes(struct wim_inode *inode, return 0; } -static void inode_move_ltes_to_table(struct wim_inode *inode, - struct wim_lookup_table *src_lookup_table, - struct wim_lookup_table *dest_lookup_table, - struct list_head *lte_list_head) +static void +inode_move_ltes_to_table(struct wim_inode *inode, + struct wim_lookup_table *src_lookup_table, + struct wim_lookup_table *dest_lookup_table, + struct list_head *lte_list_head) { struct wim_lookup_table_entry *src_lte, *dest_lte; unsigned i; @@ -92,15 +94,16 @@ static void inode_move_ltes_to_table(struct wim_inode *inode, /* * Copies an image, or all the images, from a WIM file, into another WIM file. */ -WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, - int src_image, - WIMStruct *dest_wim, - const tchar *dest_name, - const tchar *dest_description, - int export_flags, - WIMStruct **additional_swms, - unsigned num_additional_swms, - wimlib_progress_func_t progress_func) +WIMLIBAPI int +wimlib_export_image(WIMStruct *src_wim, + int src_image, + WIMStruct *dest_wim, + const tchar *dest_name, + const tchar *dest_description, + int export_flags, + WIMStruct **additional_swms, + unsigned num_additional_swms, + wimlib_progress_func_t progress_func) { int ret; struct wim_security_data *sd; @@ -208,10 +211,9 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, /* Pre-allocate the new lookup table entries that will be needed. This * way, it's not possible to run out of memory part-way through * modifying the lookup table of the destination WIM. */ - INIT_LIST_HEAD(<e_list_head); for_lookup_table_entry(src_wim->lookup_table, lte_zero_out_refcnt, NULL); src_imd = wim_get_current_image_metadata(src_wim); - + INIT_LIST_HEAD(<e_list_head); hlist_for_each_entry(inode, cur_node, &src_imd->inode_list, i_hlist) { ret = inode_allocate_needed_ltes(inode, src_wim->lookup_table, @@ -256,7 +258,7 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, goto out; out_xml_delete_image: - xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count); + xml_delete_image(&dest_wim->wim_info, dest_wim->hdr.image_count + 1); out_free_ltes: { struct wim_lookup_table_entry *lte, *tmp; diff --git a/src/extract_image.c b/src/extract_image.c index 17b12809..0f9252a3 100644 --- a/src/extract_image.c +++ b/src/extract_image.c @@ -57,11 +57,34 @@ # include #endif -#if TCHAR_IS_UTF16LE -# include -#endif - #ifndef __WIN32__ + +/* Returns the number of components of @path. */ +static unsigned +get_num_path_components(const char *path) +{ + unsigned num_components = 0; + while (*path) { + while (*path == '/') + path++; + if (*path) + num_components++; + while (*path && *path != '/') + path++; + } + return num_components; +} + +static const char * +path_next_part(const char *path) +{ + while (*path && *path != '/') + path++; + while (*path && *path == '/') + path++; + return path; +} + static int extract_regular_file_linked(struct wim_dentry *dentry, const char *output_path, @@ -110,8 +133,10 @@ extract_regular_file_linked(struct wim_dentry *dentry, p2 = lte->extracted_file; while (*p2 == '/') p2++; - while (num_output_dir_path_components--) - p2 = path_next_part(p2, NULL); + while (num_output_dir_path_components > 0) { + p2 = path_next_part(p2); + num_output_dir_path_components--; + } strcpy(p, p2); if (symlink(buf, output_path) != 0) { ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'", @@ -123,7 +148,7 @@ extract_regular_file_linked(struct wim_dentry *dentry, } static int -symlink_apply_unix_data(const mbchar *link, +symlink_apply_unix_data(const char *link, const struct wimlib_unix_data *unix_data) { if (lchown(link, unix_data->uid, unix_data->gid)) { @@ -887,19 +912,22 @@ out: return ret; } +static const tchar *filename_forbidden_chars = +T( +#ifdef __WIN32__ +"<>:\"/\\|?*" +#else +"/" +#endif +); +/* This function checks if it is okay to use a WIM image's name as a directory + * name. */ static bool image_name_ok_as_dir(const tchar *image_name) { - if (image_name == NULL) - return false; - if (image_name[0] == T('\0')) - return false; - if (tstrchr(image_name, T('/'))) - return false; - if (tstrchr(image_name, T('\\'))) - return false; - return true; + return image_name && *image_name && + !tstrpbrk(image_name, filename_forbidden_chars); } /* Extracts all images from the WIM to the directory @target, with the images @@ -921,16 +949,15 @@ extract_all_images(WIMStruct *w, if (ret) return ret; - wmemcpy(buf, target, output_path_len); + tmemcpy(buf, target, output_path_len); buf[output_path_len] = T('/'); for (image = 1; image <= w->hdr.image_count; image++) { image_name = wimlib_get_image_name(w, image); if (image_name_ok_as_dir(image_name)) { tstrcpy(buf + output_path_len + 1, image_name); } else { - /* Image name is empty, or may not be representable in - * the current locale, or contains path separators. Use - * the image number instead. */ + /* Image name is empty, or contains forbidden + * characters. */ tsprintf(buf + output_path_len + 1, T("%d"), image); } ret = extract_single_image(w, image, buf, extract_flags, diff --git a/src/hardlink.c b/src/hardlink.c index 479b1266..f8fed90e 100644 --- a/src/hardlink.c +++ b/src/hardlink.c @@ -156,7 +156,7 @@ print_inode_dentries(const struct wim_inode *inode) { struct wim_dentry *dentry; inode_for_each_dentry(dentry, inode) - printf("`%s'\n", dentry->full_path); + tprintf(T("`%"TS"'\n"), dentry->full_path); } #endif @@ -382,17 +382,18 @@ next_dentry_2: if (num_true_inodes != 1) { inode = container_of(true_inodes.first, struct wim_inode, i_hlist); - printf("Split nominal inode 0x%"PRIx64" into %zu " - "inodes:\n", - inode->i_ino, num_true_inodes); - puts("------------------------------------------------------------------------------"); + tprintf(T("Split nominal inode 0x%"PRIx64" into %zu " + "inodes:\n"), inode->i_ino, num_true_inodes); + tputs(T("----------------------------------------------------" + "--------------------------")); size_t i = 1; hlist_for_each_entry(inode, cur, &true_inodes, i_hlist) { - printf("[Split inode %zu]\n", i++); + tprintf(T("[Split inode %zu]\n"), i++); print_inode_dentries(inode); - putchar('\n'); + tputchar(T('\n')); } - puts("------------------------------------------------------------------------------"); + tputs(T("----------------------------------------------------" + "--------------------------")); } #endif diff --git a/src/header.c b/src/header.c index 8dcfcdea..dd274ca0 100644 --- a/src/header.c +++ b/src/header.c @@ -259,53 +259,53 @@ wimlib_print_header(const WIMStruct *w) { const struct wim_header *hdr = &w->hdr; - printf("Magic Characters = MSWIM\\000\\000\\000\n"); - printf("Header Size = %u\n", WIM_HEADER_DISK_SIZE); - printf("Version = 0x%x\n", WIM_VERSION); + tprintf(T("Magic Characters = MSWIM\\000\\000\\000\n")); + tprintf(T("Header Size = %u\n"), WIM_HEADER_DISK_SIZE); + tprintf(T("Version = 0x%x\n"), WIM_VERSION); - printf("Flags = 0x%x\n", hdr->flags); + tprintf(T("Flags = 0x%x\n"), hdr->flags); for (size_t i = 0; i < ARRAY_LEN(hdr_flags); i++) if (hdr_flags[i].flag & hdr->flags) - printf(" WIM_HDR_FLAG_%s is set\n", hdr_flags[i].name); + tprintf(T(" WIM_HDR_FLAG_%s is set\n"), hdr_flags[i].name); - printf("Chunk Size = %u\n", WIM_CHUNK_SIZE); - fputs ("GUID = ", stdout); + tprintf(T("Chunk Size = %u\n"), WIM_CHUNK_SIZE); + tfputs (T("GUID = "), stdout); print_byte_field(hdr->guid, WIM_GID_LEN); - putchar('\n'); - printf("Part Number = %hu\n", w->hdr.part_number); - printf("Total Parts = %hu\n", w->hdr.total_parts); - printf("Image Count = %u\n", hdr->image_count); - printf("Lookup Table Size = %"PRIu64"\n", + tputchar(T('\n')); + tprintf(T("Part Number = %hu\n"), w->hdr.part_number); + tprintf(T("Total Parts = %hu\n"), w->hdr.total_parts); + tprintf(T("Image Count = %u\n"), hdr->image_count); + tprintf(T("Lookup Table Size = %"PRIu64"\n"), (u64)hdr->lookup_table_res_entry.size); - printf("Lookup Table Flags = 0x%hhx\n", + tprintf(T("Lookup Table Flags = 0x%hhx\n"), (u8)hdr->lookup_table_res_entry.flags); - printf("Lookup Table Offset = %"PRIu64"\n", + tprintf(T("Lookup Table Offset = %"PRIu64"\n"), hdr->lookup_table_res_entry.offset); - printf("Lookup Table Original_size = %"PRIu64"\n", + tprintf(T("Lookup Table Original_size = %"PRIu64"\n"), hdr->lookup_table_res_entry.original_size); - printf("XML Data Size = %"PRIu64"\n", + tprintf(T("XML Data Size = %"PRIu64"\n"), (u64)hdr->xml_res_entry.size); - printf("XML Data Flags = 0x%hhx\n", + tprintf(T("XML Data Flags = 0x%hhx\n"), (u8)hdr->xml_res_entry.flags); - printf("XML Data Offset = %"PRIu64"\n", + tprintf(T("XML Data Offset = %"PRIu64"\n"), hdr->xml_res_entry.offset); - printf("XML Data Original Size = %"PRIu64"\n", + tprintf(T("XML Data Original Size = %"PRIu64"\n"), hdr->xml_res_entry.original_size); - printf("Boot Metadata Size = %"PRIu64"\n", + tprintf(T("Boot Metadata Size = %"PRIu64"\n"), (u64)hdr->boot_metadata_res_entry.size); - printf("Boot Metadata Flags = 0x%hhx\n", + tprintf(T("Boot Metadata Flags = 0x%hhx\n"), (u8)hdr->boot_metadata_res_entry.flags); - printf("Boot Metadata Offset = %"PRIu64"\n", + tprintf(T("Boot Metadata Offset = %"PRIu64"\n"), hdr->boot_metadata_res_entry.offset); - printf("Boot Metadata Original Size = %"PRIu64"\n", + tprintf(T("Boot Metadata Original Size = %"PRIu64"\n"), hdr->boot_metadata_res_entry.original_size); - printf("Boot Index = %u\n", hdr->boot_idx); - printf("Integrity Size = %"PRIu64"\n", + tprintf(T("Boot Index = %u\n"), hdr->boot_idx); + tprintf(T("Integrity Size = %"PRIu64"\n"), (u64)hdr->integrity.size); - printf("Integrity Flags = 0x%hhx\n", + tprintf(T("Integrity Flags = 0x%hhx\n"), (u8)hdr->integrity.flags); - printf("Integrity Offset = %"PRIu64"\n", + tprintf(T("Integrity Offset = %"PRIu64"\n"), hdr->integrity.offset); - printf("Integrity Original_size = %"PRIu64"\n", + tprintf(T("Integrity Original_size = %"PRIu64"\n"), hdr->integrity.original_size); } diff --git a/src/integrity.c b/src/integrity.c index 67119211..bbabaea2 100644 --- a/src/integrity.c +++ b/src/integrity.c @@ -45,8 +45,9 @@ struct integrity_table { u8 sha1sums[0][20]; }; -static int calculate_chunk_sha1(FILE *fp, size_t this_chunk_size, - off_t offset, u8 sha1_md[]) +static int +calculate_chunk_sha1(FILE *fp, size_t this_chunk_size, + off_t offset, u8 sha1_md[]) { int ret; u8 buf[BUFFER_SIZE]; @@ -109,10 +110,11 @@ static int calculate_chunk_sha1(FILE *fp, size_t this_chunk_size, * data. * * WIMLIB_ERR_READ: Could not read the integrity data from the WIM file. */ -static int read_integrity_table(const struct resource_entry *res_entry, - FILE *fp, - u64 num_checked_bytes, - struct integrity_table **table_ret) +static int +read_integrity_table(const struct resource_entry *res_entry, + FILE *fp, + u64 num_checked_bytes, + struct integrity_table **table_ret) { struct integrity_table *table = NULL; int ret = 0; @@ -229,12 +231,13 @@ out: * * Returns 0 on success; nonzero on failure. */ -static int calculate_integrity_table(FILE *fp, - off_t new_check_end, - const struct integrity_table *old_table, - off_t old_check_end, - wimlib_progress_func_t progress_func, - struct integrity_table **integrity_table_ret) +static int +calculate_integrity_table(FILE *fp, + off_t new_check_end, + const struct integrity_table *old_table, + off_t old_check_end, + wimlib_progress_func_t progress_func, + struct integrity_table **integrity_table_ret) { int ret = 0; size_t chunk_size = INTEGRITY_CHUNK_SIZE; @@ -360,11 +363,12 @@ static int calculate_integrity_table(FILE *fp, * * WIMLIB_ERR_READ: Could not read a chunk of data that needed * to be checked. */ -int write_integrity_table(FILE *fp, - struct resource_entry *integrity_res_entry, - off_t new_lookup_table_end, - off_t old_lookup_table_end, - wimlib_progress_func_t progress_func) +int +write_integrity_table(FILE *fp, + struct resource_entry *integrity_res_entry, + off_t new_lookup_table_end, + off_t old_lookup_table_end, + wimlib_progress_func_t progress_func) { struct integrity_table *old_table; struct integrity_table *new_table; @@ -454,10 +458,11 @@ out_free_old_table: * were no inconsistencies. * -1 (WIM_INTEGRITY_NOT_OK) if the WIM failed the integrity check. */ -static int verify_integrity(FILE *fp, const tchar *filename, - const struct integrity_table *table, - u64 bytes_to_check, - wimlib_progress_func_t progress_func) +static int +verify_integrity(FILE *fp, const tchar *filename, + const struct integrity_table *table, + u64 bytes_to_check, + wimlib_progress_func_t progress_func) { int ret; u64 offset = WIM_HEADER_DISK_SIZE; @@ -523,7 +528,8 @@ static int verify_integrity(FILE *fp, const tchar *filename, * -2 (WIM_INTEGRITY_NONEXISTENT) if the WIM contains no integrity * information. */ -int check_wim_integrity(WIMStruct *w, wimlib_progress_func_t progress_func) +int +check_wim_integrity(WIMStruct *w, wimlib_progress_func_t progress_func) { int ret; u64 bytes_to_check; diff --git a/src/lookup_table.c b/src/lookup_table.c index d1e50612..bfa0c858 100644 --- a/src/lookup_table.c +++ b/src/lookup_table.c @@ -33,7 +33,8 @@ #include #endif -struct wim_lookup_table *new_lookup_table(size_t capacity) +struct wim_lookup_table * +new_lookup_table(size_t capacity) { struct wim_lookup_table *table; struct hlist_head *array; @@ -84,7 +85,9 @@ clone_lookup_table_entry(const struct wim_lookup_table_entry *old) memcpy(new, old, sizeof(*old)); new->extracted_file = NULL; switch (new->resource_location) { +#ifdef __WIN32__ case RESOURCE_WIN32: +#endif case RESOURCE_IN_STAGING_FILE: case RESOURCE_IN_FILE_ON_DISK: BUILD_BUG_ON((void*)&old->file_on_disk != @@ -132,7 +135,8 @@ out_free: return NULL; } -void free_lookup_table_entry(struct wim_lookup_table_entry *lte) +void +free_lookup_table_entry(struct wim_lookup_table_entry *lte) { if (lte) { switch (lte->resource_location) { @@ -164,15 +168,16 @@ void free_lookup_table_entry(struct wim_lookup_table_entry *lte) } } -static int do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, - void *ignore) +static int +do_free_lookup_table_entry(struct wim_lookup_table_entry *entry, void *ignore) { free_lookup_table_entry(entry); return 0; } -void free_lookup_table(struct wim_lookup_table *table) +void +free_lookup_table(struct wim_lookup_table *table) { DEBUG2("Freeing lookup table"); if (table) { @@ -192,8 +197,9 @@ void free_lookup_table(struct wim_lookup_table *table) * @table: A pointer to the lookup table. * @lte: A pointer to the entry to insert. */ -void lookup_table_insert(struct wim_lookup_table *table, - struct wim_lookup_table_entry *lte) +void +lookup_table_insert(struct wim_lookup_table *table, + struct wim_lookup_table_entry *lte) { size_t i = lte->hash_short % table->capacity; hlist_add_head(<e->hash_list, &table->array[i]); @@ -202,7 +208,8 @@ void lookup_table_insert(struct wim_lookup_table *table, table->num_entries++; } -static void finalize_lte(struct wim_lookup_table_entry *lte) +static void +finalize_lte(struct wim_lookup_table_entry *lte) { #ifdef WITH_FUSE if (lte->resource_location == RESOURCE_IN_STAGING_FILE) { @@ -217,8 +224,9 @@ static void finalize_lte(struct wim_lookup_table_entry *lte) * reference count reaches 0, it is unlinked from the lookup table. If, * furthermore, the entry has no opened file descriptors associated with it, the * entry is freed. */ -void lte_decrement_refcnt(struct wim_lookup_table_entry *lte, - struct wim_lookup_table *table) +void +lte_decrement_refcnt(struct wim_lookup_table_entry *lte, + struct wim_lookup_table *table) { wimlib_assert(lte != NULL); wimlib_assert(lte->refcnt != 0); @@ -232,7 +240,8 @@ void lte_decrement_refcnt(struct wim_lookup_table_entry *lte, } #ifdef WITH_FUSE -void lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) +void +lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) { if (lte->num_opened_fds != 0) if (--lte->num_opened_fds == 0 && lte->refcnt == 0) @@ -242,9 +251,10 @@ void lte_decrement_num_opened_fds(struct wim_lookup_table_entry *lte) /* Calls a function on all the entries in the WIM lookup table. Stop early and * return nonzero if any call to the function returns nonzero. */ -int for_lookup_table_entry(struct wim_lookup_table *table, - int (*visitor)(struct wim_lookup_table_entry *, void *), - void *arg) +int +for_lookup_table_entry(struct wim_lookup_table *table, + int (*visitor)(struct wim_lookup_table_entry *, void *), + void *arg) { struct wim_lookup_table_entry *lte; struct hlist_node *pos, *tmp; @@ -266,7 +276,8 @@ int for_lookup_table_entry(struct wim_lookup_table *table, /* * Reads the lookup table from a WIM file. */ -int read_lookup_table(WIMStruct *w) +int +read_lookup_table(WIMStruct *w) { u64 num_entries; u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE]; @@ -400,7 +411,8 @@ out: /* * Writes a lookup table entry to the output file. */ -int write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *__out) +int +write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *__out) { FILE *out; u8 buf[WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE]; @@ -431,8 +443,9 @@ int write_lookup_table_entry(struct wim_lookup_table_entry *lte, void *__out) } /* Writes the lookup table to the output file. */ -int write_lookup_table(struct wim_lookup_table *table, FILE *out, - struct resource_entry *out_res_entry) +int +write_lookup_table(struct wim_lookup_table *table, FILE *out, + struct resource_entry *out_res_entry) { off_t start_offset, end_offset; int ret; @@ -458,19 +471,22 @@ int write_lookup_table(struct wim_lookup_table *table, FILE *out, } -int lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_zero_real_refcnt(struct wim_lookup_table_entry *lte, void *ignore) { lte->real_refcnt = 0; return 0; } -int lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_zero_out_refcnt(struct wim_lookup_table_entry *lte, void *ignore) { lte->out_refcnt = 0; return 0; } -int lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) +int +lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) { if (lte->extracted_file != NULL) { FREE(lte->extracted_file); @@ -479,8 +495,8 @@ int lte_free_extracted_file(struct wim_lookup_table_entry *lte, void *ignore) return 0; } -void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, - FILE *out) +void +print_lookup_table_entry(const struct wim_lookup_table_entry *lte, FILE *out) { if (!lte) { tputc(T('\n'), out); @@ -520,6 +536,9 @@ void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, lte->wim->filename); } break; +#ifdef __WIN32__ + case RESOURCE_WIN32: +#endif case RESOURCE_IN_FILE_ON_DISK: tfprintf(out, T("File on Disk = `%"TS"'\n"), lte->file_on_disk); @@ -534,8 +553,8 @@ void print_lookup_table_entry(const struct wim_lookup_table_entry *lte, tputc(T('\n'), out); } -static int do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, - void *fp) +static int +do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, void *fp) { print_lookup_table_entry(lte, (FILE*)fp); return 0; @@ -544,7 +563,8 @@ static int do_print_lookup_table_entry(struct wim_lookup_table_entry *lte, /* * Prints the lookup table of a WIM file. */ -WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w) +WIMLIBAPI void +wimlib_print_lookup_table(WIMStruct *w) { for_lookup_table_entry(w->lookup_table, do_print_lookup_table_entry, @@ -650,7 +670,8 @@ out: * This function always succeeds; unresolved lookup table entries are given a * NULL pointer. */ -void inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) +void +inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) { if (!inode->i_resolved) { @@ -669,7 +690,8 @@ void inode_resolve_ltes(struct wim_inode *inode, struct wim_lookup_table *table) } } -void inode_unresolve_ltes(struct wim_inode *inode) +void +inode_unresolve_ltes(struct wim_inode *inode) { if (inode->i_resolved) { if (inode->i_lte) @@ -693,7 +715,7 @@ void inode_unresolve_ltes(struct wim_inode *inode) * stream_idx = 0 means the default un-named file stream, and stream_idx >= 1 * corresponds to an alternate data stream. * - * This works for both resolved and un-resolved dentries. + * This works for both resolved and un-resolved inodes. */ struct wim_lookup_table_entry * inode_stream_lte(const struct wim_inode *inode, unsigned stream_idx, @@ -732,14 +754,15 @@ inode_unnamed_lte(const struct wim_inode *inode, return inode_unnamed_lte_unresolved(inode, table); } -static int lte_add_stream_size(struct wim_lookup_table_entry *lte, - void *total_bytes_p) +static int +lte_add_stream_size(struct wim_lookup_table_entry *lte, void *total_bytes_p) { *(u64*)total_bytes_p += lte->resource_entry.size; return 0; } -u64 lookup_table_total_stream_size(struct wim_lookup_table *table) +u64 +lookup_table_total_stream_size(struct wim_lookup_table *table) { u64 total_size = 0; for_lookup_table_entry(table, lte_add_stream_size, &total_size); diff --git a/src/lookup_table.h b/src/lookup_table.h index 1759df4e..78de61a3 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -163,7 +163,7 @@ struct wim_lookup_table_entry { WIMStruct *wim; tchar *file_on_disk; tchar *staging_file_name; - u8 *attached_buffer; + void *attached_buffer; #ifdef WITH_NTFS_3G struct ntfs_location *ntfs_loc; #endif diff --git a/src/metadata_resource.c b/src/metadata_resource.c index 13a6a962..d54641de 100644 --- a/src/metadata_resource.c +++ b/src/metadata_resource.c @@ -42,7 +42,8 @@ * * @return: Zero on success, nonzero on failure. */ -int read_metadata_resource(WIMStruct *w, struct wim_image_metadata *imd) +int +read_metadata_resource(WIMStruct *w, struct wim_image_metadata *imd) { u8 *buf; u32 dentry_offset; @@ -185,7 +186,8 @@ out_free_buf: return ret; } -static void recalculate_security_data_length(struct wim_security_data *sd) +static void +recalculate_security_data_length(struct wim_security_data *sd) { u32 total_length = sizeof(u64) * sd->num_entries + 2 * sizeof(u32); for (u32 i = 0; i < sd->num_entries; i++) @@ -196,10 +198,11 @@ static void recalculate_security_data_length(struct wim_security_data *sd) /* Like write_wim_resource(), but the resource is specified by a buffer of * uncompressed data rather a lookup table entry; also writes the SHA1 hash of * the buffer to @hash. */ -static int write_wim_resource_from_buffer(const u8 *buf, u64 buf_size, - FILE *out_fp, int out_ctype, - struct resource_entry *out_res_entry, - u8 hash[SHA1_HASH_SIZE]) +static int +write_wim_resource_from_buffer(const u8 *buf, u64 buf_size, + FILE *out_fp, int out_ctype, + struct resource_entry *out_res_entry, + u8 hash[SHA1_HASH_SIZE]) { /* Set up a temporary lookup table entry to provide to * write_wim_resource(). */ @@ -210,7 +213,7 @@ static int write_wim_resource_from_buffer(const u8 *buf, u64 buf_size, lte.resource_entry.size = buf_size; lte.resource_entry.offset = 0; lte.resource_location = RESOURCE_IN_ATTACHED_BUFFER; - lte.attached_buffer = (u8*)buf; + lte.attached_buffer = buf; zero_out_hash(lte.hash); ret = write_wim_resource(<e, out_fp, out_ctype, out_res_entry, 0); @@ -221,7 +224,8 @@ static int write_wim_resource_from_buffer(const u8 *buf, u64 buf_size, } /* Write the metadata resource for the current WIM image. */ -int write_metadata_resource(WIMStruct *w) +int +write_metadata_resource(WIMStruct *w) { u8 *buf; u8 *p; diff --git a/src/mount_image.c b/src/mount_image.c index 04ecbfef..024c82b7 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -1805,10 +1805,10 @@ wimfs_listxattr(const char *path, char *list, size_t size) size_t stream_name_mbs_nbytes; int ret; - ret = utf16le_to_mbs(inode->i_ads_entries[i].stream_name, - inode->i_ads_entries[i].stream_name_nbytes, - &stream_name_mbs, - &stream_name_mbs_nbytes); + ret = utf16le_to_tstr(inode->i_ads_entries[i].stream_name, + inode->i_ads_entries[i].stream_name_nbytes, + &stream_name_mbs, + &stream_name_mbs_nbytes); if (ret) return -errno; @@ -2014,10 +2014,10 @@ dentry_fuse_fill(struct wim_dentry *dentry, void *arg) size_t file_name_mbs_nbytes; int ret; - ret = utf16le_to_mbs(dentry->file_name, - dentry->file_name_nbytes, - &file_name_mbs, - &file_name_mbs_nbytes); + ret = utf16le_to_tstr(dentry->file_name, + dentry->file_name_nbytes, + &file_name_mbs, + &file_name_mbs_nbytes); if (ret) return -errno; diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 4d2292b7..245e0b55 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -176,10 +176,10 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct wim_dentry *dent static ntfs_inode * dentry_open_parent_ni(const struct wim_dentry *dentry, ntfs_volume *vol) { - mbchar *p; - const mbchar *dir_name; + char *p; + const char *dir_name; ntfs_inode *dir_ni; - mbchar orig; + char orig; p = dentry->full_path + dentry->full_path_nbytes; do { @@ -455,12 +455,12 @@ do_apply_dentry_ntfs(struct wim_dentry *dentry, ntfs_inode *dir_ni, /* Set DOS (short) name if given */ if (dentry_has_short_name(dentry)) { - mbchar *short_name_mbs; + char *short_name_mbs; size_t short_name_mbs_nbytes; - ret = utf16le_to_mbs(dentry->short_name, - dentry->short_name_nbytes, - &short_name_mbs, - &short_name_mbs_nbytes); + ret = utf16le_to_tstr(dentry->short_name, + dentry->short_name_nbytes, + &short_name_mbs, + &short_name_mbs_nbytes); if (ret != 0) goto out_close_dir_ni; @@ -571,7 +571,7 @@ apply_dentry_ntfs(struct wim_dentry *dentry, void *arg) * file. So, this implies that the correct ordering of function calls * to extract a NTFS file are: * - if (file has a DOS name) { + * if (file has a DOS name) { * - Call ntfs_create() to create long name associated with * the DOS name (this initially creates a POSIX name) * - Call ntfs_set_ntfs_dos_name() to associate a DOS name diff --git a/src/ntfs-capture.c b/src/ntfs-capture.c index 31c8e56b..dfc153f9 100644 --- a/src/ntfs-capture.c +++ b/src/ntfs-capture.c @@ -441,8 +441,8 @@ wim_ntfs_capture_filldir(void *dirent, const ntfschar *name, if (ret != 0 || name_type == FILE_NAME_DOS) goto out; } - ret = utf16le_to_mbs(name, name_nbytes, - &mbs_name, &mbs_name_nbytes); + ret = utf16le_to_tstr(name, name_nbytes, + &mbs_name, &mbs_name_nbytes); if (ret) goto out; diff --git a/src/resource.c b/src/resource.c index 1ab91240..d3380caf 100644 --- a/src/resource.c +++ b/src/resource.c @@ -80,7 +80,7 @@ read_compressed_resource(FILE *fp, u64 resource_compressed_size, resource_compressed_size, resource_uncompressed_size, resource_offset); - DEBUG2("resource_ctype = %s, len = %"PRIu64", offset = %"PRIu64"", + DEBUG2("resource_ctype = %"TS", len = %"PRIu64", offset = %"PRIu64"", wimlib_get_compression_type_string(resource_ctype), len, offset); /* Trivial case */ if (len == 0) diff --git a/src/security.c b/src/security.c index 88e6c847..7efc542b 100644 --- a/src/security.c +++ b/src/security.c @@ -27,129 +27,6 @@ #include "buffer_io.h" #include "security.h" - -#define SECURITY_DESCRIPTOR_REVISION 1 -#define SECURITY_DESCRIPTOR_REVISION1 1 - -/* inherit AceFlags */ -#define OBJECT_INHERIT_ACE 0x01 -#define CONTAINER_INHERIT_ACE 0x02 -#define NO_PROPAGATE_INHERIT_ACE 0x04 -#define INHERIT_ONLY_ACE 0x08 -#define INHERITED_ACE 0x10 -#define VALID_INHERIT_FLAGS 0x1F - -#define SE_OWNER_DEFAULTED 0x00000001 -#define SE_GROUP_DEFAULTED 0x00000002 -#define SE_DACL_PRESENT 0x00000004 -#define SE_DACL_DEFAULTED 0x00000008 -#define SE_SACL_PRESENT 0x00000010 -#define SE_SACL_DEFAULTED 0x00000020 -#define SE_DACL_AUTO_INHERIT_REQ 0x00000100 -#define SE_SACL_AUTO_INHERIT_REQ 0x00000200 -#define SE_DACL_AUTO_INHERITED 0x00000400 -#define SE_SACL_AUTO_INHERITED 0x00000800 -#define SE_DACL_PROTECTED 0x00001000 -#define SE_SACL_PROTECTED 0x00002000 -#define SE_RM_CONTROL_VALID 0x00004000 -#define SE_SELF_RELATIVE 0x00008000 - -/* Flags in access control entries */ -#define DELETE 0x00010000 -#define READ_CONTROL 0x00020000 -#define WRITE_DAC 0x00040000 -#define WRITE_OWNER 0x00080000 -#define SYNCHRONIZE 0x00100000 -#define STANDARD_RIGHTS_REQUIRED 0x000f0000 - -#define STANDARD_RIGHTS_READ READ_CONTROL -#define STANDARD_RIGHTS_WRITE READ_CONTROL -#define STANDARD_RIGHTS_EXECUTE READ_CONTROL - -#define STANDARD_RIGHTS_ALL 0x001f0000 - -#define SPECIFIC_RIGHTS_ALL 0x0000ffff - -#define GENERIC_READ 0x80000000 -#define GENERIC_WRITE 0x40000000 -#define GENERIC_EXECUTE 0x20000000 -#define GENERIC_ALL 0x10000000 - -#define MAXIMUM_ALLOWED 0x02000000 -#define ACCESS_SYSTEM_SECURITY 0x01000000 - -#define EVENT_QUERY_STATE 0x0001 -#define EVENT_MODIFY_STATE 0x0002 -#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) - -#define SEMAPHORE_MODIFY_STATE 0x0002 -#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) - -#define MUTEX_MODIFY_STATE 0x0001 -#define MUTEX_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1) - -#define JOB_OBJECT_ASSIGN_PROCESS 0x0001 -#define JOB_OBJECT_SET_ATTRIBUTES 0x0002 -#define JOB_OBJECT_QUERY 0x0004 -#define JOB_OBJECT_TERMINATE 0x0008 -#define JOB_OBJECT_SET_SECURITY_ATTRIBUTES 0x0010 -#define JOB_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1f) - -#define TIMER_QUERY_STATE 0x0001 -#define TIMER_MODIFY_STATE 0x0002 -#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) - -#define PROCESS_TERMINATE 0x0001 -#define PROCESS_CREATE_THREAD 0x0002 -#define PROCESS_VM_OPERATION 0x0008 -#define PROCESS_VM_READ 0x0010 -#define PROCESS_VM_WRITE 0x0020 -#define PROCESS_DUP_HANDLE 0x0040 -#define PROCESS_CREATE_PROCESS 0x0080 -#define PROCESS_SET_QUOTA 0x0100 -#define PROCESS_SET_INFORMATION 0x0200 -#define PROCESS_QUERY_INFORMATION 0x0400 -#define PROCESS_SUSPEND_RESUME 0x0800 -#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000 -#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0xfff) - -#define THREAD_TERMINATE 0x0001 -#define THREAD_SUSPEND_RESUME 0x0002 -#define THREAD_GET_CONTEXT 0x0008 -#define THREAD_SET_CONTEXT 0x0010 -#define THREAD_SET_INFORMATION 0x0020 -#define THREAD_QUERY_INFORMATION 0x0040 -#define THREAD_SET_THREAD_TOKEN 0x0080 -#define THREAD_IMPERSONATE 0x0100 -#define THREAD_DIRECT_IMPERSONATION 0x0200 -#define THREAD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3ff) - -#define THREAD_BASE_PRIORITY_LOWRT 15 -#define THREAD_BASE_PRIORITY_MAX 2 -#define THREAD_BASE_PRIORITY_MIN -2 -#define THREAD_BASE_PRIORITY_IDLE -15 - -/* predefined authority values for SID's (security identifiers) */ -enum sid_authority_value { - SECURITY_NULL_SID_AUTHORITY = 0, - SECURITY_WORLD_SID_AUTHORITY = 1, - SECURITY_LOCAL_SID_AUTHORITY = 2, - SECURITY_CREATOR_SID_AUTHORITY = 3, - SECURITY_NON_UNIQUE_AUTHORITY = 4, - SECURITY_NT_AUTHORITY = 5, -}; - -/* local administrators group */ -#define SECURITY_BUILTIN_DOMAIN_RID 32 -#define DOMAIN_ALIAS_RID_ADMINS 544 - -/* See ACEHeader. */ -enum ace_type { - ACCESS_ALLOWED_ACE_TYPE = 0, - ACCESS_DENIED_ACE_TYPE = 1, - SYSTEM_AUDIT_ACE_TYPE = 2, -}; - /* At the start of each type of access control entry. */ typedef struct { /* enum ace_type, specifies what type of ACE this is. */ @@ -463,74 +340,76 @@ write_security_data(const struct wim_security_data *sd, u8 *p) } static void -print_acl(const u8 *p, const char *type) +print_acl(const void *p, const tchar *type) { - const ACL *acl = (const ACL*)p; + const ACL *acl = p; u8 revision = acl->revision; u16 acl_size = le16_to_cpu(acl->acl_size); u16 ace_count = le16_to_cpu(acl->ace_count); - printf(" [%s ACL]\n", type); - printf(" Revision = %u\n", revision); - printf(" ACL Size = %u\n", acl_size); - printf(" ACE Count = %u\n", ace_count); + tprintf(T(" [%"TS" ACL]\n"), type); + tprintf(T(" Revision = %u\n"), revision); + tprintf(T(" ACL Size = %u\n"), acl_size); + tprintf(T(" ACE Count = %u\n"), ace_count); p += sizeof(ACL); for (u16 i = 0; i < ace_count; i++) { - const ACEHeader *hdr = (const ACEHeader*)p; - printf(" [ACE]\n"); - printf(" ACE type = %d\n", hdr->type); - printf(" ACE flags = 0x%x\n", hdr->flags); - printf(" ACE size = %u\n", hdr->size); + const ACEHeader *hdr = p; + tprintf(T(" [ACE]\n")); + tprintf(T(" ACE type = %d\n"), hdr->type); + tprintf(T(" ACE flags = 0x%x\n"), hdr->flags); + tprintf(T(" ACE size = %u\n"), hdr->size); const AccessAllowedACE *aaa = (const AccessAllowedACE*)hdr; - printf(" ACE mask = %x\n", le32_to_cpu(aaa->mask)); - printf(" SID start = %u\n", le32_to_cpu(aaa->sid_start)); + tprintf(T(" ACE mask = %x\n"), le32_to_cpu(aaa->mask)); + tprintf(T(" SID start = %u\n"), le32_to_cpu(aaa->sid_start)); p += hdr->size; } - putchar('\n'); + tputchar(T('\n')); } static void -print_sid(const u8 *p, const char *type) +print_sid(const void *p, const tchar *type) { - const SID *sid = (const SID*)p; - printf(" [%s SID]\n", type); - printf(" Revision = %u\n", sid->revision); - printf(" Subauthority count = %u\n", sid->sub_authority_count); - printf(" Identifier authority = "); + const SID *sid = p; + tprintf(T(" [%"TS" SID]\n"), type); + tprintf(T(" Revision = %u\n"), sid->revision); + tprintf(T(" Subauthority count = %u\n"), sid->sub_authority_count); + tprintf(T(" Identifier authority = ")); print_byte_field(sid->identifier_authority, sizeof(sid->identifier_authority)); - putchar('\n'); - for (u8 i = 0; i < sid->sub_authority_count; i++) - printf(" Subauthority %u = %u\n", - i, le32_to_cpu(sid->sub_authority[i])); - putchar('\n'); + tputchar(T('\n')); + for (u8 i = 0; i < sid->sub_authority_count; i++) { + tprintf(T(" Subauthority %u = %u\n"), + i, le32_to_cpu(sid->sub_authority[i])); + } + tputchar(T('\n')); } static void -print_security_descriptor(const u8 *p, u64 size) +print_security_descriptor(const void *p, u64 size) { - const SecurityDescriptor *sd = (const SecurityDescriptor*)p; + const SecurityDescriptor *sd = p; + u8 revision = sd->revision; u16 control = le16_to_cpu(sd->security_descriptor_control); u32 owner_offset = le32_to_cpu(sd->owner_offset); u32 group_offset = le32_to_cpu(sd->group_offset); u32 sacl_offset = le32_to_cpu(sd->sacl_offset); u32 dacl_offset = le32_to_cpu(sd->dacl_offset); - printf("Revision = %u\n", revision); - printf("Security Descriptor Control = %#x\n", control); - printf("Owner offset = %u\n", owner_offset); - printf("Group offset = %u\n", group_offset); - printf("System ACL offset = %u\n", sacl_offset); - printf("Discretionary ACL offset = %u\n", dacl_offset); + tprintf(T("Revision = %u\n"), revision); + tprintf(T("Security Descriptor Control = %#x\n"), control); + tprintf(T("Owner offset = %u\n"), owner_offset); + tprintf(T("Group offset = %u\n"), group_offset); + tprintf(T("System ACL offset = %u\n"), sacl_offset); + tprintf(T("Discretionary ACL offset = %u\n"), dacl_offset); if (sd->owner_offset != 0) - print_sid(p + owner_offset, "Owner"); + print_sid(p + owner_offset, T("Owner")); if (sd->group_offset != 0) - print_sid(p + group_offset, "Group"); + print_sid(p + group_offset, T("Group")); if (sd->sacl_offset != 0) - print_acl(p + sacl_offset, "System"); + print_acl(p + sacl_offset, T("System")); if (sd->dacl_offset != 0) - print_acl(p + dacl_offset, "Discretionary"); + print_acl(p + dacl_offset, T("Discretionary")); } /* @@ -541,17 +420,17 @@ print_security_data(const struct wim_security_data *sd) { wimlib_assert(sd != NULL); - puts("[SECURITY DATA]"); - printf("Length = %"PRIu32" bytes\n", sd->total_length); - printf("Number of Entries = %"PRIu32"\n", sd->num_entries); + tputs(T("[SECURITY DATA]")); + tprintf(T("Length = %"PRIu32" bytes\n"), sd->total_length); + tprintf(T("Number of Entries = %"PRIu32"\n"), sd->num_entries); for (u32 i = 0; i < sd->num_entries; i++) { - printf("[SecurityDescriptor %"PRIu32", length = %"PRIu64"]\n", - i, sd->sizes[i]); + tprintf(T("[SecurityDescriptor %"PRIu32", length = %"PRIu64"]\n"), + i, sd->sizes[i]); print_security_descriptor(sd->descriptors[i], sd->sizes[i]); - putchar('\n'); + tputchar(T('\n')); } - putchar('\n'); + tputchar(T('\n')); } void diff --git a/src/sha1.c b/src/sha1.c index b7879428..6fb18c4d 100644 --- a/src/sha1.c +++ b/src/sha1.c @@ -66,7 +66,8 @@ sha1_update(SHA_CTX *context, const void *data, size_t len) { sha1_update_intel((int*)&context->state, data, len / 64); size_t j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; context->count[1] += (len >> 29); } #include diff --git a/src/split.c b/src/split.c index 385dc2a6..71b32cef 100644 --- a/src/split.c +++ b/src/split.c @@ -28,10 +28,6 @@ #include "xml.h" #include "buffer_io.h" -#if TCHAR_IS_UTF16LE -# include -#endif - struct split_args { WIMStruct *w; tchar *swm_base_name; diff --git a/src/symlink.c b/src/symlink.c index 1e0109fd..70301b35 100644 --- a/src/symlink.c +++ b/src/symlink.c @@ -48,7 +48,7 @@ static ssize_t get_symlink_name(const void *resource, size_t resource_len, char *buf, size_t buf_len, u32 reparse_tag) { - const u8 *p = resource; + const void *p = resource; u16 substitute_name_offset; u16 substitute_name_len; u16 print_name_offset; @@ -82,17 +82,12 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf, if (header_size + substitute_name_offset + substitute_name_len > resource_len) return -EIO; - ret = utf16le_to_mbs((const utf16lechar*)(p + substitute_name_offset), - substitute_name_len, - &link_target, &link_target_len); + ret = utf16le_to_tstr((const utf16lechar*)(p + substitute_name_offset), + substitute_name_len, + &link_target, &link_target_len); if (ret) return -errno; - wimlib_assert(ret == 0); - - if (!link_target) - return -EIO; - if (link_target_len + 1 > buf_len) { ret = -ENAMETOOLONG; goto out; @@ -150,7 +145,7 @@ make_symlink_reparse_data_buf(const char *symlink_target, size_t len = 12 + name_utf16le_nbytes * 2; void *buf = MALLOC(len); if (buf) { - u8 *p = buf; + void *p = buf; p = put_u16(p, name_utf16le_nbytes); /* Substitute name offset */ p = put_u16(p, name_utf16le_nbytes); /* Substitute name length */ p = put_u16(p, 0); /* Print name offset */ @@ -269,4 +264,4 @@ out_free_symlink_buf: return ret; } -#endif /* !defined(__WIN32__) && !defined(WITH_FUSE) */ +#endif /* !defined(__WIN32__) */ diff --git a/src/util.c b/src/util.c index 1e26a6f4..07d5fc6b 100644 --- a/src/util.c +++ b/src/util.c @@ -43,7 +43,7 @@ #include /* for getpid() */ -size_t +static size_t utf16le_strlen(const utf16lechar *s) { const utf16lechar *p = s; @@ -52,48 +52,41 @@ utf16le_strlen(const utf16lechar *s) return (p - s) * sizeof(utf16lechar); } -/* Handle %W for UTF16-LE printing and %U for UTF-8 printing. +#ifdef __WIN32__ +# define wimlib_vfprintf vfwprintf +#else +/* Handle %W for UTF16-LE printing. * * TODO: this is not yet done properly--- it's assumed that if the format string - * contains %W and/or %U, then it contains no other format specifiers. + * contains %W, then it contains no other format specifiers. */ static int wimlib_vfprintf(FILE *fp, const tchar *format, va_list va) { const tchar *p; + int n; for (p = format; *p; p++) - if (*p == '%' && (*(p + 1) == T('W') || *(p + 1) == T('U'))) + if (*p == T('%') && *(p + 1) == T('W')) goto special; return tvfprintf(fp, format, va); special: - /* XXX */ - wimlib_assert(0); -#if 0 - ; - int n = 0; + n = 0; for (p = format; *p; p++) { - if (*p == T('%') && (*(p + 1) == T('W') || *(p + 1) == T('U'))) { + if (*p == T('%') && (*(p + 1) == T('W'))) { int ret; - tchar *mbs; - size_t mbs_nbytes; - - if (*(p + 1) == T('W')) { - utf16lechar *ucs = va_arg(va, utf16lechar*); - size_t ucs_nbytes = utf16le_strlen(ucs); - ret = utf16le_to_mbs(ucs, ucs_nbytes, - &mbs, &mbs_nbytes); - } else { - utf8char *ucs = va_arg(va, utf8char*); - size_t ucs_nbytes = strlen(ucs); - ret = utf8_to_mbs(ucs, ucs_nbytes, - &mbs, &mbs_nbytes); - } + tchar *tstr; + size_t tstr_nbytes; + utf16lechar *ucs = va_arg(va, utf16lechar*); + size_t ucs_nbytes = utf16le_strlen(ucs); + + ret = utf16le_to_tstr(ucs, ucs_nbytes, + &tstr, &tstr_nbytes); if (ret) { ret = tfprintf(fp, T("??????")); } else { - ret = tfprintf(fp, T("%s"), mbs); - FREE(mbs); + ret = tfprintf(fp, T("%"TS), tstr); + FREE(tstr); } if (ret < 0) return -1; @@ -101,13 +94,12 @@ special: n += ret; p++; } else { - if (putc(*p, fp) == EOF) + if (tputc(*p, fp) == EOF) return -1; n++; } } return n; -#endif } int @@ -133,6 +125,7 @@ wimlib_fprintf(FILE *fp, const tchar *format, ...) va_end(va); return ret; } +#endif #if defined(ENABLE_ERROR_MESSAGES) || defined(ENABLE_DEBUG) static void @@ -486,21 +479,6 @@ randomize_byte_array(u8 *p, size_t n) *p++ = rand(); } -/* Takes in a path of length @len in @buf, and transforms it into a string for - * the path of its parent directory. */ -void -to_parent_name(tchar *buf, size_t len) -{ - ssize_t i = (ssize_t)len - 1; - while (i >= 0 && buf[i] == T('/')) - i--; - while (i >= 0 && buf[i] != T('/')) - i--; - while (i >= 0 && buf[i] == T('/')) - i--; - buf[i + 1] = T('\0'); -} - const tchar * path_basename_with_len(const tchar *path, size_t len) { @@ -544,69 +522,6 @@ path_stream_name(const tchar *path) return stream_name + 1; } -/* - * Splits a file path into the part before the first '/', or the entire name if - * there is no '/', and the part after the first sequence of '/' characters. - * - * @path: The file path to split. - * @first_part_len_ret: A pointer to a `size_t' into which the length of the - * first part of the path will be returned. - * @return: A pointer to the next part of the path, after the first - * sequence of '/', or a pointer to the terminating - * null byte in the case of a path without any '/'. - */ -const tchar * -path_next_part(const tchar *path, size_t *first_part_len_ret) -{ - size_t i; - const tchar *next_part; - - i = 0; - while (path[i] != T('/') && path[i] != T('\0')) - i++; - if (first_part_len_ret) - *first_part_len_ret = i; - next_part = &path[i]; - while (*next_part == T('/')) - next_part++; - return next_part; -} - -/* Returns the number of components of @path. */ -int -get_num_path_components(const char *path) -{ - int num_components = 0; - while (*path) { - while (*path == '/') - path++; - if (*path) - num_components++; - while (*path && *path != '/') - path++; - } - return num_components; -} - - -/* - * Prints a string. Printable characters are printed as-is, while unprintable - * characters are printed as their octal escape codes. - */ -void -print_string(const void *string, size_t len) -{ - const u8 *p = string; - - while (len--) { - if (isprint(*p)) - putchar(*p); - else - printf("\\%03hho", *p); - p++; - } -} - u64 get_wim_timestamp() { @@ -623,3 +538,15 @@ wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len) gmtime_r(&t, &tm); tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm); } + +void +zap_backslashes(tchar *s) +{ + if (s) { + while (*s != T('\0')) { + if (*s == T('\\')) + *s = T('/'); + s++; + } + } +} diff --git a/src/util.h b/src/util.h index 07e12fe2..4b75eda2 100644 --- a/src/util.h +++ b/src/util.h @@ -1,12 +1,14 @@ #ifndef _WIMLIB_UTIL_H #define _WIMLIB_UTIL_H +#include "config.h" +#include "wimlib_tchar.h" + #include #include #include #include #include -#include "config.h" #ifdef __GNUC__ # if defined(__CYGWIN__) || defined(__WIN32__) @@ -16,9 +18,8 @@ # endif # define ALWAYS_INLINE inline __attribute__((always_inline)) # define PACKED __attribute__((packed)) -//# define FORMAT(type, format_str, args_start) \ - //__attribute__((format(type, format_str, args_start))) -# define FORMAT(type, format_str, args_start) +# define FORMAT(type, format_str, args_start) \ + /*__attribute__((format(type, format_str, args_start))) */ # if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) # define COLD __attribute__((cold)) # else @@ -54,101 +55,8 @@ typedef uint64_t u64; /* A pointer to 'utf16lechar' indicates a UTF-16LE encoded string */ typedef u16 utf16lechar; -typedef u8 utf8char; - -#ifdef __WIN32__ -/* For Windows builds, the "tchar" type will be 2 bytes and will be equivalent - * to "wchar_t" and "utf16lechar". All indicate one code unit of a UTF16-LE - * string. */ -typedef wchar_t tchar; -# define TCHAR_IS_UTF16LE 1 -# define T(text) L##text /* Make a string literal into a wide string */ -# define TS "ls" /* Format a string of "tchar" */ -# define WS "ls" /* Format a UTF-16LE string (same as above) */ - -/* For Windows builds, the following definitions replace the "tchar" functions - * with the "wide-character" functions. */ -# define tmemchr wmemchr -# define tmemcpy wmemcpy -# define tstrcpy wcscpy -# define tprintf wprintf -# define tsprintf swprintf -# define tfprintf fwprintf -# define tvfprintf vfwprintf -# define istalpha iswalpha -# define tstrcmp wcscmp -# define tstrchr wcschr -# define tstrrchr wcsrchr -# define tstrlen wcslen -# define tmemcmp wmemcmp -# define tstrftime wcsftime -# define tputchar putwchar -# define tputc putwc -# define tputs _putws -# define tfputs fputws -# define tfopen _wfopen -# define tstat _wstati64 -# define tstrtol wcstol -# define tunlink _wunlink -/* The following "tchar" functions do not have exact wide-character equivalents - * on Windows so require parameter rearrangement or redirection to a replacement - * function defined ourselves. */ -# define TSTRDUP WSTRDUP -# define tmkdir(path, mode) _wmkdir(path) -# define tstrerror_r(errnum, buf, bufsize) _wcserror_s(buf, bufsize, errnum) -# define trename win32_rename_replacement -# define ttruncate win32_truncate_replacement -#else -/* For non-Windows builds, the "tchar" type will be one byte and will specify a - * string in the locale-dependent multibyte encoding. However, only UTF-8 is - * well supported in this library. */ -typedef char tchar; -# define TCHAR_IS_UTF16LE 0 -# define T(text) text /* In this case, strings of "tchar" are simply strings of - char */ -# define TS "s" /* Similarly, a string of "tchar" is printed just as a - normal string. */ -# define WS "W" /* UTF-16LE strings must be printed using a special - extension implemented by wimlib itself. Note that - "ls" will not work here because a string of wide - characters on non-Windows systems is typically not - UTF-16LE. */ -/* For non-Windows builds, replace the "tchar" functions with the regular old - * string functions. */ -# define tmemchr memchr -# define tmemcpy memcpy -# define tstrcpy strcpy -# define tprintf printf -# define tsprintf sprintf -# define tfprintf fprintf -# define tvfprintf vfprintf -# define istalpha isalpha -# define tstrcmp strcmp -# define tstrchr strchr -# define tstrrchr strrchr -# define tstrlen strlen -# define tmemcmp memcmp -# define tstrftime strftime -# define tputchar putchar -# define tputc putc -# define tputs puts -# define tfputs fputs -# define tfopen fopen -# define tstat stat -# define tunlink unlink -# define tstrtol strtol -# define tmkdir mkdir -# define TSTRDUP STRDUP -# define tstrerror_r strerror_r -# define trename rename -# define ttruncate truncate -#endif - #define TMALLOC(n) MALLOC((n) * sizeof(tchar)) -extern size_t -utf16le_strlen(const utf16lechar *s); - /* encoding.c */ extern void iconv_global_cleanup(); @@ -159,27 +67,29 @@ extern bool wimlib_mbs_is_utf8; chartype1, chartype2) \ \ extern int \ +varname1##_to_##varname2(const chartype1 *in, size_t in_nbytes, \ + chartype2 **out_ret, \ + size_t *out_nbytes_ret); \ + \ +extern int \ varname1##_to_##varname2##_nbytes(const chartype1 *in, size_t in_nbytes,\ size_t *out_nbytes_ret); \ \ extern int \ varname1##_to_##varname2##_buf(const chartype1 *in, size_t in_nbytes, \ - chartype2 *out); \ - \ -extern int \ -varname1##_to_##varname2(const chartype1 *in, size_t in_nbytes, \ - chartype2 **out_ret, \ - size_t *out_nbytes_ret); \ + chartype2 *out); + #if !TCHAR_IS_UTF16LE DECLARE_CHAR_CONVERSION_FUNCTIONS(utf16le, tstr, utf16lechar, tchar); +DECLARE_CHAR_CONVERSION_FUNCTIONS(tstr, utf16le, tchar, utf16lechar); #endif extern int -utf8_to_tstr_simple(const utf8char *utf8str, tchar **out); +utf8_to_tstr_simple(const char *utf8str, tchar **out); extern int -tstr_to_utf8_simple(const tchar *tstr, utf8char **out); +tstr_to_utf8_simple(const tchar *tstr, char **out); #ifndef min #define min(a, b) ({ typeof(a) __a = (a); typeof(b) __b = (b); \ @@ -222,7 +132,7 @@ tstr_to_utf8_simple(const tchar *tstr, utf8char **out); #define BUFFER_SIZE 4096 static inline void FORMAT(printf, 1, 2) -dummy_printf(const char *format, ...) +dummy_tprintf(const tchar *format, ...) { } @@ -243,10 +153,10 @@ wimlib_warning_with_errno(const tchar *format, ...) FORMAT(printf, 1, 2) COLD; # define WARNING(format, ...) wimlib_warning(T(format), ## __VA_ARGS__) # define WARNING_WITH_ERRNO(format, ...) wimlib_warning(T(format), ## __VA_ARGS__) #else /* ENABLE_ERROR_MESSAGES */ -# define ERROR(format, ...) dummy_printf(format, ## __VA_ARGS__) -# define ERROR_WITH_ERRNO(format, ...) dummy_printf(format, ## __VA_ARGS__) -# define WARNING(format, ...) dummy_printf(format, ## __VA_ARGS__) -# define WARNING_WITH_ERRNO(format, ...) dummy_printf(format, ## __VA_ARGS__) +# define ERROR(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) +# define ERROR_WITH_ERRNO(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) +# define WARNING(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) +# define WARNING_WITH_ERRNO(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) #endif /* !ENABLE_ERROR_MESSAGES */ #if defined(ENABLE_MORE_DEBUG) && !defined(ENABLE_DEBUG) @@ -265,13 +175,13 @@ wimlib_debug(const tchar *file, int line, const char *func, wimlib_debug(T(__FILE__), __LINE__, __func__, T(format), ## __VA_ARGS__); #else -# define DEBUG(format, ...) dummy_printf(format, ## __VA_ARGS__) +# define DEBUG(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) #endif /* !ENABLE_DEBUG */ #ifdef ENABLE_MORE_DEBUG # define DEBUG2(format, ...) DEBUG(format, ## __VA_ARGS__) #else -# define DEBUG2(format, ...) dummy_printf(format, ## __VA_ARGS__) +# define DEBUG2(format, ...) dummy_tprintf(T(format), ## __VA_ARGS__) #endif /* !ENABLE_MORE_DEBUG */ #ifdef ENABLE_ASSERTIONS @@ -323,9 +233,6 @@ randomize_byte_array(u8 *p, size_t n); extern void randomize_char_array_with_alnum(tchar p[], size_t n); -extern const tchar * -path_next_part(const tchar *path, size_t *first_part_len_ret); - const tchar * path_basename_with_len(const tchar *path, size_t len); @@ -335,15 +242,6 @@ path_basename(const tchar *path); extern const tchar * path_stream_name(const tchar *path); -extern void -to_parent_name(tchar *buf, size_t len); - -extern void -print_string(const void *string, size_t len); - -extern int -get_num_path_components(const char *path); - static inline void print_byte_field(const u8 field[], size_t len) { @@ -367,14 +265,18 @@ bsr32(u32 n) #endif } +#ifdef __WIN32__ +# define wimlib_fprintf fwprintf +# define wimlib_printf wprintf +#else /* __WIN32__ */ extern int -wimlib_fprintf(FILE *fp, const tchar *format, ...) - //FORMAT(printf, 2, 3) - ; +wimlib_fprintf(FILE *fp, const tchar *format, ...) FORMAT(printf, 2, 3); extern int -wimlib_printf(const tchar *format, ...) - //FORMAT(printf, 1, 2) - ; +wimlib_printf(const tchar *format, ...) FORMAT(printf, 1, 2); +#endif /* !__WIN32__ */ + +extern void +zap_backslashes(tchar *s); #endif /* _WIMLIB_UTIL_H */ diff --git a/src/verify.c b/src/verify.c index 62e9547e..efaca0da 100644 --- a/src/verify.c +++ b/src/verify.c @@ -30,7 +30,8 @@ #include "dentry.h" #include "lookup_table.h" -static int verify_inode(struct wim_inode *inode, const WIMStruct *w) +static int +verify_inode(struct wim_inode *inode, const WIMStruct *w) { const struct wim_lookup_table *table = w->lookup_table; const struct wim_security_data *sd = wim_const_security_data(w); @@ -42,13 +43,13 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w) * descriptor". Anything else has to be a valid index into the WIM * image's security descriptors table. */ if (inode->i_security_id < -1) { - ERROR("Dentry `%s' has an invalid security ID (%d)", - first_dentry->full_path, inode->i_security_id); + ERROR("Dentry `%"TS"' has an invalid security ID (%d)", + first_dentry->full_path, inode->i_security_id); goto out; } if (inode->i_security_id >= sd->num_entries) { - ERROR("Dentry `%s' has an invalid security ID (%d) " + ERROR("Dentry `%"TS"' has an invalid security ID (%d) " "(there are only %u entries in the security table)", first_dentry->full_path, inode->i_security_id, sd->num_entries); @@ -68,49 +69,12 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w) lte = __lookup_resource(table, hash); if (!lte && !is_zero_hash(hash)) { ERROR("Could not find lookup table entry for stream " - "%u of dentry `%s'", i, first_dentry->full_path); + "%u of dentry `%"TS"'", + i, first_dentry->full_path); goto out; } if (lte) lte->real_refcnt += inode->i_nlink; - - /* The following is now done when required by - * wim_run_full_verifications(). */ - - #if 0 - if (lte && !w->full_verification_in_progress && - lte->real_refcnt > lte->refcnt) - { - #ifdef ENABLE_ERROR_MESSAGES - WARNING("The following lookup table entry " - "has a reference count of %u, but", - lte->refcnt); - WARNING("We found %u references to it", - lte->real_refcnt); - WARNING("(One dentry referencing it is at `%s')", - first_dentry->full_path_utf8); - - print_lookup_table_entry(lte); - #endif - /* Guess what! install.wim for Windows 8 - * contains many streams referenced by more - * dentries than the refcnt stated in the lookup - * table entry. So we will need to handle this - * case and not just make it be an error... I'm - * just setting the reference count to the - * number of references we found. - * (Unfortunately, even after doing this, the - * reference count could be too low if it's also - * referenced in other WIM images) */ - - #if 1 - lte->refcnt = lte->real_refcnt; - WARNING("Fixing reference count"); - #else - goto out; - #endif - } - #endif } } @@ -123,7 +87,7 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w) num_unnamed_streams++; } if (num_unnamed_streams > 1) { - ERROR("Dentry `%s' has multiple (%u) un-named streams", + ERROR("Dentry `%"TS"' has multiple (%u) un-named streams", first_dentry->full_path, num_unnamed_streams); goto out; } @@ -136,7 +100,7 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w) if (dentry_has_short_name(dentry)) { if (dentry_with_dos_name) { ERROR("Hard-linked file has a DOS name at " - "both `%s' and `%s'", + "both `%"TS"' and `%"TS"'", dentry_with_dos_name->full_path, dentry->full_path); goto out; @@ -147,7 +111,7 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w) /* Directories with multiple links have not been tested. XXX */ if (inode->i_nlink > 1 && inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) { - ERROR("Hard-linked directory `%s' is unsupported", + ERROR("Hard-linked directory `%"TS"' is unsupported", first_dentry->full_path); goto out; } @@ -159,14 +123,17 @@ out: } /* Run some miscellaneous verifications on a WIM dentry */ -int verify_dentry(struct wim_dentry *dentry, void *wim) +int +verify_dentry(struct wim_dentry *dentry, void *wim) { int ret; + WIMStruct *w = wim; /* Verify the associated inode, but only one time no matter how many - * dentries it has. */ - if (!dentry->d_inode->i_verified) { - ret = verify_inode(dentry->d_inode, wim); + * dentries it has (unless we are doing a full verification of the WIM, + * in which case we need to force the inode to be verified again.) */ + if (!dentry->d_inode->i_verified || w->full_verification_in_progress) { + ret = verify_inode(dentry->d_inode, w); if (ret != 0) return ret; } @@ -185,7 +152,7 @@ int verify_dentry(struct wim_dentry *dentry, void *wim) } } else { if (!dentry_has_long_name(dentry)) { - ERROR("Dentry `%s' has no long name!", + ERROR("Dentry `%"TS"' has no long name!", dentry->full_path); return WIMLIB_ERR_INVALID_DENTRY; } @@ -195,20 +162,22 @@ int verify_dentry(struct wim_dentry *dentry, void *wim) /* Check timestamps */ if (inode->i_last_access_time < inode->i_creation_time || inode->i_last_write_time < inode->i_creation_time) { - WARNING("Dentry `%s' was created after it was last accessed or " - "written to", dentry->full_path_utf8); + WARNING("Dentry `%"TS"' was created after it was last accessed or " + "written to", dentry->full_path); } #endif return 0; } -static int image_run_full_verifications(WIMStruct *w) +static int +image_run_full_verifications(WIMStruct *w) { return for_dentry_in_tree(wim_root_dentry(w), verify_dentry, w); } -static int lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr) +static int +lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr) { if (lte->refcnt != lte->real_refcnt) { #ifdef ENABLE_ERROR_MESSAGES @@ -232,7 +201,8 @@ static int lte_fix_refcnt(struct wim_lookup_table_entry *lte, void *ctr) * problem by looking at ALL the images to re-calculate the reference count of * EVERY lookup table entry. This only absolutely has to be done before an image * is deleted or before an image is mounted read-write. */ -int wim_run_full_verifications(WIMStruct *w) +int +wim_run_full_verifications(WIMStruct *w) { int ret; @@ -276,31 +246,32 @@ int wim_run_full_verifications(WIMStruct *w) * @return: * 0 on success; WIMLIB_ERR_SPLIT_INVALID if the set is not valid. */ -int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, - unsigned num_additional_swms) +int +verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, + unsigned num_additional_swms) { unsigned total_parts = w->hdr.total_parts; int ctype; const u8 *guid; if (total_parts != num_additional_swms + 1) { - ERROR("`%s' says there are %u parts in the spanned set, " - "but %s%u part%s provided", + ERROR("`%"TS"' says there are %u parts in the spanned set, " + "but %"TS"%u part%"TS" provided", w->filename, total_parts, - (num_additional_swms + 1 < total_parts) ? "only " : "", + (num_additional_swms + 1 < total_parts) ? T("only ") : T(""), num_additional_swms + 1, - (num_additional_swms) ? "s were" : " was"); + (num_additional_swms) ? T("s were") : T(" was")); return WIMLIB_ERR_SPLIT_INVALID; } if (w->hdr.part_number != 1) { - ERROR("WIM `%s' is not the first part of the split WIM.", - w->filename); + ERROR("WIM `%"TS"' is not the first part of the split WIM.", + T(w->filename)); return WIMLIB_ERR_SPLIT_INVALID; } for (unsigned i = 0; i < num_additional_swms; i++) { if (additional_swms[i]->hdr.total_parts != total_parts) { - ERROR("WIM `%s' says there are %u parts in the spanned set, " - "but %u parts were provided", + ERROR("WIM `%"TS"' says there are %u parts in the " + "spanned set, but %u parts were provided", additional_swms[i]->filename, additional_swms[i]->hdr.total_parts, total_parts); @@ -335,24 +306,24 @@ int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, return WIMLIB_ERR_SPLIT_INVALID; } if (swm->hdr.part_number == 1) { - ERROR("WIMs `%s' and `%s' both are marked as the " - "first WIM in the spanned set", + ERROR("WIMs `%"TS"' and `%"TS"' both are marked " + "as the first WIM in the spanned set", w->filename, swm->filename); return WIMLIB_ERR_SPLIT_INVALID; } if (swm->hdr.part_number == 0 || swm->hdr.part_number > total_parts) { - ERROR("WIM `%s' says it is part %u in the spanned set, " - "but the part number must be in the range " - "[1, %u]", + ERROR("WIM `%"TS"' says it is part %u in the " + "spanned set, but the part number must " + "be in the range [1, %u]", swm->filename, swm->hdr.part_number, total_parts); return WIMLIB_ERR_SPLIT_INVALID; } if (parts_to_swms[swm->hdr.part_number - 2]) { - ERROR("`%s' and `%s' are both marked as part %u of %u " - "in the spanned set", + ERROR("`%"TS"' and `%"TS"' are both marked as " + "part %u of %u in the spanned set", parts_to_swms[swm->hdr.part_number - 2]->filename, swm->filename, swm->hdr.part_number, @@ -365,4 +336,3 @@ int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, } return 0; } - diff --git a/src/wim.c b/src/wim.c index bba8487f..f2794fe8 100644 --- a/src/wim.c +++ b/src/wim.c @@ -184,7 +184,7 @@ wimlib_create_new_wim(int ctype, WIMStruct **w_ret) struct wim_lookup_table *table; int ret; - DEBUG("Creating new WIM with %s compression.", + DEBUG("Creating new WIM with %"TS" compression.", wimlib_get_compression_type_string(ctype)); /* Allocate the WIMStruct. */ @@ -304,8 +304,8 @@ wimlib_resolve_image(WIMStruct *w, const tchar *image_name_or_num) if (!image_name_or_num || !*image_name_or_num) return WIMLIB_NO_IMAGE; - if (tstrcmp(image_name_or_num, T("all")) == 0 - || tstrcmp(image_name_or_num, T("*")) == 0) + if (!tstrcasecmp(image_name_or_num, T("all")) + || !tstrcasecmp(image_name_or_num, T("*"))) return WIMLIB_ALL_IMAGES; image = tstrtol(image_name_or_num, &p, 10); if (p != image_name_or_num && *p == T('\0') && image > 0) { @@ -675,13 +675,13 @@ test_locale_ctype_utf8() { char *ctype = nl_langinfo(CODESET); - return (strstr(ctype, "UTF-8") == 0 || - strstr(ctype, "UTF8") == 0 || - strstr(ctype, "utf8") == 0 || - strstr(ctype, "utf-8") == 0); + return (!strstr(ctype, "UTF-8") || + !strstr(ctype, "UTF8") || + !strstr(ctype, "utf8") || + !strstr(ctype, "utf-8")); } -/* Get global memory allocations out of the way. Not strictly necessary in +/* Get global memory allocations out of the way, * single-threaded programs like 'imagex'. */ WIMLIBAPI int wimlib_global_init() diff --git a/src/wimlib.h b/src/wimlib.h index d1b22cd1..403d3877 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -1500,12 +1500,17 @@ wimlib_get_part_number(const WIMStruct *wim, int *total_parts_ret); /** * Since wimlib 1.2.6: Initialization function for wimlib. This is not * re-entrant. If you are calling wimlib functions concurrently in different - * threads, then you must call this function serially first. Also, since wimlib - * 1.3.0, you must call this function if the character encoding of the current - * locale is not UTF-8. Otherwise, calling this function this function is not - * required. + * threads, then you must call this function serially first. * - * This function always returns 0. + * Since wimlib 1.3.0, you must call this function if the character encoding of + * the current locale is not UTF-8. + * + * Since wimlib 1.3.2, you must call this function if using the Windows-native + * build of the library so that certain functions can be dynamically loaded from + * system DLLs. + * + * This function currently always returns 0, but it may return other error codes + * in future releases. */ extern int wimlib_global_init(); diff --git a/src/win32.c b/src/win32.c index 2b26e1cd..dfa92ffd 100644 --- a/src/win32.c +++ b/src/win32.c @@ -31,7 +31,7 @@ #include #include #include -#include /* shlwapi.h for PathMatchSpecA() */ +#include /* shlwapi.h for PathMatchSpecW() */ #ifdef ERROR /* windows.h defines this */ # undef ERROR #endif @@ -70,7 +70,7 @@ win32_global_init() if (hKernel32 == NULL) { DEBUG("Loading Kernel32.dll"); - hKernel32 = LoadLibraryA("Kernel32.dll"); + hKernel32 = LoadLibraryW(L"Kernel32.dll"); if (hKernel32 == NULL) { err = GetLastError(); WARNING("Can't load Kernel32.dll"); @@ -329,7 +329,7 @@ win32_recurse_directory(struct wim_dentry *root, } while (FindNextFileW(hFind, &dat)); err = GetLastError(); if (err != ERROR_NO_MORE_FILES) { - ERROR("Failed to read directory \"%s\"", dir_path); + ERROR("Failed to read directory \"%ls\"", dir_path); win32_error(err); if (ret == 0) ret = WIMLIB_ERR_READ; @@ -818,7 +818,7 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, size_t path_nchars; wchar_t *path; int ret; - + path_nchars = wcslen(root_disk_path); if (path_nchars > 32767) return WIMLIB_ERR_INVALID_PARAM; @@ -831,6 +831,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, if (!path) return WIMLIB_ERR_NOMEM; + wmemcpy(path, root_disk_path, path_nchars + 1); + ret = win32_build_dentry_tree_recursive(root_ret, path, path_nchars, @@ -1189,7 +1191,6 @@ win32_do_apply_dentry_timestamps(const wchar_t *path, { DWORD err; HANDLE h; - int ret; const struct wim_inode *inode = dentry->d_inode; DEBUG("Opening \"%ls\" to set timestamps", path); diff --git a/src/write.c b/src/write.c index 1c574057..633196b4 100644 --- a/src/write.c +++ b/src/write.c @@ -72,10 +72,6 @@ # define INVALID_HANDLE_VALUE ((HANDLE)(-1)) #endif -#if TCHAR_IS_UTF16LE -# include -#endif - static int fflush_and_ftruncate(FILE *fp, off_t size) { @@ -550,8 +546,8 @@ write_wim_resource(struct wim_lookup_table_entry *lte, } else if (!hashes_equal(md, lte->hash)) { ERROR("WIM resource has incorrect hash!"); if (lte->resource_location == RESOURCE_IN_FILE_ON_DISK) { - ERROR("We were reading it from `%s'; maybe it changed " - "while we were reading it.", + ERROR("We were reading it from `%"TS"'; maybe " + "it changed while we were reading it.", lte->file_on_disk); } ret = WIMLIB_ERR_INVALID_RESOURCE_HASH; @@ -1692,11 +1688,11 @@ lock_wim(WIMStruct *w, FILE *fp) static int open_wim_writable(WIMStruct *w, const tchar *path, - bool trunc, bool readable) + bool trunc, bool also_readable) { const tchar *mode; if (trunc) - if (readable) + if (also_readable) mode = T("w+b"); else mode = T("wb"); diff --git a/src/xml.c b/src/xml.c index c4382d43..4b1d708d 100644 --- a/src/xml.c +++ b/src/xml.c @@ -82,7 +82,7 @@ struct image_info { }; struct xml_string_spec { - const utf8char *name; + const char *name; size_t offset; }; @@ -126,7 +126,7 @@ get_arch(int arch) return T("x86_64"); /* XXX Are there other arch values? */ default: - return NULL; + return T("unknown"); } } @@ -149,34 +149,36 @@ node_is_text(xmlNode *node) } static inline bool -node_name_is(xmlNode *node, const utf8char *name) +node_name_is(xmlNode *node, const char *name) { /* For now, both upper case and lower case element names are accepted. */ return strcasecmp((const char *)node->name, name) == 0; } -/* Finds the text node that is a child of an element node and returns its - * content converted to a 64-bit unsigned integer. Returns 0 if no text node is - * found. */ static u64 -node_get_u64(const xmlNode *u64_node) +node_get_number(const xmlNode *u64_node, int base) { xmlNode *child; for_node_child(u64_node, child) if (node_is_text(child)) - return strtoull((const char *)child->content, NULL, 10); + return strtoull(child->content, NULL, base); return 0; } +/* Finds the text node that is a child of an element node and returns its + * content converted to a 64-bit unsigned integer. Returns 0 if no text node is + * found. */ +static u64 +node_get_u64(const xmlNode *u64_node) +{ + return node_get_number(u64_node, 10); +} + /* Like node_get_u64(), but expects a number in base 16. */ static u64 node_get_hex_u64(const xmlNode *u64_node) { - xmlNode *child; - for_node_child(u64_node, child) - if (node_is_text(child)) - return strtoull(child->content, NULL, 16); - return 0; + return node_get_number(u64_node, 16); } static int @@ -529,7 +531,7 @@ print_windows_info(const struct windows_info *windows_info) const struct windows_version *windows_version; tprintf(T("Architecture: %"TS"\n"), - get_arch(windows_info->arch) ?: T("unknown")); + get_arch(windows_info->arch)); if (windows_info->product_name) { tprintf(T("Product Name: %"TS"\n"), @@ -597,7 +599,7 @@ xml_write_string(xmlTextWriter *writer, const char *name, const tchar *tstr) { if (tstr) { - utf8char *utf8_str; + char *utf8_str; int rc = tstr_to_utf8_simple(tstr, &utf8_str); if (rc) return rc; @@ -745,7 +747,7 @@ xml_write_windows_info(xmlTextWriter *writer, /* Writes a time element to the XML document being constructed in memory. */ static int -xml_write_time(xmlTextWriter *writer, const utf8char *element_name, u64 time) +xml_write_time(xmlTextWriter *writer, const char *element_name, u64 time) { int rc; rc = xmlTextWriterStartElement(writer, element_name); @@ -813,7 +815,7 @@ xml_write_image_info(xmlTextWriter *writer, const struct image_info *image_info) if (image_info->windows_info_exists) { rc = xml_write_windows_info(writer, &image_info->windows_info); - if (rc < 0) + if (rc) return rc; } @@ -856,6 +858,8 @@ clone_windows_info(const struct windows_info *old, struct windows_info *new) ret = dup_strings_from_specs(old, new, windows_info_xml_string_specs, ARRAY_LEN(windows_info_xml_string_specs)); + if (ret) + return ret; if (old->languages) { new->languages = CALLOC(old->num_languages, sizeof(new->languages[0])); @@ -982,9 +986,9 @@ xml_delete_image(struct wim_info **wim_info_p, int image) { struct wim_info *wim_info; - DEBUG("Deleting image %d from the XML data.", image); - wim_info = *wim_info_p; + wimlib_assert(image >= 1 && image <= wim_info->num_images); + DEBUG("Deleting image %d from the XML data.", image); destroy_image_info(&wim_info->images[image - 1]); @@ -1117,8 +1121,8 @@ xml_update_image_info(WIMStruct *w, int image) image_info->dir_count = 0; image_info->total_bytes = 0; image_info->hard_link_bytes = 0; - image_info->lookup_table = w->lookup_table; + for_dentry_in_tree(w->image_metadata[image - 1].root_dentry, calculate_dentry_statistics, image_info); @@ -1295,8 +1299,7 @@ read_xml_data(FILE *fp, const struct resource_entry *res_entry, } if (!node_is_element(root) || !node_name_is(root, "WIM")) { - ERROR("Expected for the root XML element (found <%s>)", - root->name); + ERROR("Expected for the root XML element"); ret = WIMLIB_ERR_XML; goto out_free_doc; } @@ -1482,7 +1485,7 @@ wimlib_image_name_in_use(const WIMStruct *w, const tchar *name) if (!name || !*name) return false; for (int i = 1; i <= w->hdr.image_count; i++) - if (tstrcmp(w->wim_info->images[i - 1].name, name) == 0) + if (!tstrcmp(w->wim_info->images[i - 1].name, name)) return true; return false; } diff --git a/wimlib_tchar.h b/wimlib_tchar.h new file mode 100644 index 00000000..75f8e816 --- /dev/null +++ b/wimlib_tchar.h @@ -0,0 +1,111 @@ +#ifndef _WIMLIB_TCHAR_H +#define _WIMLIB_TCHAR_H + +/* Functions to act on "tchar" strings, which have a platform-dependent encoding + * and character size. */ + +#ifdef __WIN32__ +#include +/* For Windows builds, the "tchar" type will be 2 bytes and will be equivalent + * to "wchar_t" and "utf16lechar". All indicate one code unit of a UTF16-LE + * string. */ +typedef wchar_t tchar; +# define TCHAR_IS_UTF16LE 1 +# define _T(text) L##text +# define T(text) _T(text) /* Make a string literal into a wide string */ +# define TS "ls" /* Format a string of "tchar" */ +# define WS "ls" /* Format a UTF-16LE string (same as above) */ + +/* For Windows builds, the following definitions replace the "tchar" functions + * with the "wide-character" functions. */ +# define tmemchr wmemchr +# define tmemcpy wmemcpy +# define tstrcpy wcscpy +# define tprintf wprintf +# define tsprintf swprintf +# define tfprintf fwprintf +# define tvfprintf vfwprintf +# define istalpha iswalpha +# define istspace iswspace +# define tstrcmp wcscmp +# define tstrchr wcschr +# define tstrpbrk wcspbrk +# define tstrrchr wcsrchr +# define tstrlen wcslen +# define tmemcmp wmemcmp +# define tstrcasecmp _wcsicmp +# define tstrftime wcsftime +# define tputchar putwchar +# define tputc putwc +# define tputs _putws +# define tfputs fputws +# define tfopen _wfopen +# define tstat _wstati64 +# define tstrtol wcstol +# define tstrtod wcstod +# define tstrtoul wcstoul +# define tunlink _wunlink +# define tstrerror _wcserror +# define taccess _waccess +/* The following "tchar" functions do not have exact wide-character equivalents + * on Windows so require parameter rearrangement or redirection to a replacement + * function defined ourselves. */ +# define TSTRDUP WSTRDUP +# define tmkdir(path, mode) _wmkdir(path) +# define tstrerror_r(errnum, buf, bufsize) _wcserror_s(buf, bufsize, errnum) +# define trename win32_rename_replacement +# define ttruncate win32_truncate_replacement +#else /* __WIN32__ */ +/* For non-Windows builds, the "tchar" type will be one byte and will specify a + * string in the locale-dependent multibyte encoding. However, only UTF-8 is + * well supported in this library. */ +typedef char tchar; +# define TCHAR_IS_UTF16LE 0 +# define T(text) text /* In this case, strings of "tchar" are simply strings of + char */ +# define TS "s" /* Similarly, a string of "tchar" is printed just as a + normal string. */ +# define WS "W" /* UTF-16LE strings must be printed using a special + extension implemented by wimlib itself. Note that + "ls" will not work here because a string of wide + characters on non-Windows systems is typically not + UTF-16LE. */ +/* For non-Windows builds, replace the "tchar" functions with the regular old + * string functions. */ +# define tmemchr memchr +# define tmemcpy memcpy +# define tstrcpy strcpy +# define tprintf printf +# define tsprintf sprintf +# define tfprintf fprintf +# define tvfprintf vfprintf +# define istalpha isalpha +# define istspace isspace +# define tstrcmp strcmp +# define tstrchr strchr +# define tstrpbrk strpbrk +# define tstrrchr strrchr +# define tstrlen strlen +# define tmemcmp memcmp +# define tstrcasecmp strcasecmp +# define tstrftime strftime +# define tputchar putchar +# define tputc putc +# define tputs puts +# define tfputs fputs +# define tfopen fopen +# define tstat stat +# define tunlink unlink +# define tstrerror strerror +# define tstrtol strtol +# define tstrtod strtod +# define tstrtoul strtoul +# define tmkdir mkdir +# define TSTRDUP STRDUP +# define tstrerror_r strerror_r +# define trename rename +# define ttruncate truncate +# define taccess access +#endif /* !__WIN32__ */ + +#endif /* _WIMLIB_TCHAR_H */