From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- lib/compat/src/chdir.c | 34 +++++ lib/compat/src/fnmatch.c | 305 +++++++++++++++++++++++++++++++++++++++ lib/compat/src/getopt.c | 96 ++++++++++++ lib/compat/src/getopt_long.c | 95 ++++++++++++ lib/compat/src/getsubopt.c | 27 ++++ lib/compat/src/mockups.c | 51 +++++++ lib/compat/src/path_to_windows.c | 43 ++++++ lib/compat/src/strchrnul.c | 18 +++ lib/compat/src/strndup.c | 31 ++++ lib/compat/src/w32_perror.c | 33 +++++ lib/compat/src/w32_stdio.c | 125 ++++++++++++++++ lib/compat/src/w32_wmain.c | 83 +++++++++++ 12 files changed, 941 insertions(+) create mode 100644 lib/compat/src/chdir.c create mode 100644 lib/compat/src/fnmatch.c create mode 100644 lib/compat/src/getopt.c create mode 100644 lib/compat/src/getopt_long.c create mode 100644 lib/compat/src/getsubopt.c create mode 100644 lib/compat/src/mockups.c create mode 100644 lib/compat/src/path_to_windows.c create mode 100644 lib/compat/src/strchrnul.c create mode 100644 lib/compat/src/strndup.c create mode 100644 lib/compat/src/w32_perror.c create mode 100644 lib/compat/src/w32_stdio.c create mode 100644 lib/compat/src/w32_wmain.c (limited to 'lib/compat/src') diff --git a/lib/compat/src/chdir.c b/lib/compat/src/chdir.c new file mode 100644 index 0000000..f695e2a --- /dev/null +++ b/lib/compat/src/chdir.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * chdir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#ifdef _WIN32 +#include +#include + +int chdir(const char *path) +{ + WCHAR *wpath; + int ret; + + wpath = path_to_windows(path); + if (wpath == NULL) + return -1; + + if (!SetCurrentDirectoryW(wpath)) { + fprintf(stderr, "Switching to directory '%s': %ld\n", + path, GetLastError()); + ret = -1; + } else { + ret = 0; + } + + free(wpath); + return ret; +} +#endif diff --git a/lib/compat/src/fnmatch.c b/lib/compat/src/fnmatch.c new file mode 100644 index 0000000..ed4dde1 --- /dev/null +++ b/lib/compat/src/fnmatch.c @@ -0,0 +1,305 @@ +/* + * An implementation of what I call the "Sea of Stars" algorithm for + * POSIX fnmatch(). The basic idea is that we factor the pattern into + * a head component (which we match first and can reject without ever + * measuring the length of the string), an optional tail component + * (which only exists if the pattern contains at least one star), and + * an optional "sea of stars", a set of star-separated components + * between the head and tail. After the head and tail matches have + * been removed from the input string, the components in the "sea of + * stars" are matched sequentially by searching for their first + * occurrence past the end of the previous match. + * + * - Rich Felker, April 2012 + */ + +#include "compat.h" + +#include +#include + +#ifndef HAVE_FNMATCH +#define END 0 +#define UNMATCHABLE -2 +#define BRACKET -3 +#define QUESTION -4 +#define STAR -5 + +static int str_next(const char *str, size_t n, size_t *step) +{ + if (!n) { + *step = 0; + return 0; + } + if (str[0] >= 128U) { + wchar_t wc; + int k = mbtowc(&wc, str, n); + if (k<0) { + *step = 1; + return -1; + } + *step = k; + return wc; + } + *step = 1; + return str[0]; +} + +static int pat_next(const char *pat, size_t m, size_t *step) +{ + int esc = 0; + if (!m || !*pat) { + *step = 0; + return END; + } + *step = 1; + if (pat[0]=='\\' && pat[1]) { + *step = 2; + pat++; + esc = 1; + goto escaped; + } + if (pat[0]=='[') { + size_t k = 1; + if (k= 128U) { + wchar_t wc; + int k = mbtowc(&wc, pat, m); + if (k<0) { + *step = 0; + return UNMATCHABLE; + } + *step = k + esc; + return wc; + } + return pat[0]; +} + +static int match_bracket(const char *p, int k, int kfold) +{ + wchar_t wc; + int inv = 0; + p++; + if (*p=='^' || *p=='!') { + inv = 1; + p++; + } + if (*p==']') { + if (k==']') return !inv; + p++; + } else if (*p=='-') { + if (k=='-') return !inv; + p++; + } + wc = p[-1]; + for (; *p != ']'; p++) { + if (p[0]=='-' && p[1]!=']') { + wchar_t wc2; + int l = mbtowc(&wc2, p+1, 4); + if (l < 0) return 0; + if (wc <= wc2) + if ((unsigned)k-wc <= wc2-wc || + (unsigned)kfold-wc <= wc2-wc) + return !inv; + p += l-1; + continue; + } + if (p[0]=='[' && (p[1]==':' || p[1]=='.' || p[1]=='=')) { + const char *p0 = p+2; + int z = p[1]; + p+=3; + while (p[-1]!=z || p[0]!=']') p++; + if (z == ':' && p-1-p0 < 16) { + char buf[16]; + memcpy(buf, p0, p-1-p0); + buf[p-1-p0] = 0; + if (iswctype(k, wctype(buf)) || + iswctype(kfold, wctype(buf))) + return !inv; + } + continue; + } + if (*p < 128U) { + wc = (unsigned char)*p; + } else { + int l = mbtowc(&wc, p, 4); + if (l < 0) return 0; + p += l-1; + } + if (wc==k || wc==kfold) return !inv; + } + return inv; +} + +static int fnmatch_internal(const char *pat, size_t m, const char *str, size_t n) +{ + const char *p, *ptail, *endpat; + const char *s, *stail, *endstr; + size_t pinc, sinc, tailcnt=0; + int c, k, kfold; + + for (;;) { + switch ((c = pat_next(pat, m, &pinc))) { + case UNMATCHABLE: + return FNM_NOMATCH; + case STAR: + pat++; + m--; + break; + default: + k = str_next(str, n, &sinc); + if (k <= 0) + return (c==END) ? 0 : FNM_NOMATCH; + str += sinc; + n -= sinc; + kfold = k; + if (c == BRACKET) { + if (!match_bracket(pat, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + pat+=pinc; + m-=pinc; + continue; + } + break; + } + + /* Compute real pat length if it was initially unknown/-1 */ + m = strnlen(pat, m); + endpat = pat + m; + + /* Find the last * in pat and count chars needed after it */ + for (p=ptail=pat; pstr && tailcnt; tailcnt--) { + if (s[-1] < 128U || MB_CUR_MAX==1) s--; + else while ((unsigned char)*--s-0x80U<0x40 && s>str); + } + if (tailcnt) return FNM_NOMATCH; + stail = s; + + /* Check that the pat and str tails match */ + p = ptail; + for (;;) { + c = pat_next(p, endpat-p, &pinc); + p += pinc; + if ((k = str_next(s, endstr-s, &sinc)) <= 0) { + if (c != END) return FNM_NOMATCH; + break; + } + s += sinc; + kfold = k; + if (c == BRACKET) { + if (!match_bracket(p-pinc, k, kfold)) + return FNM_NOMATCH; + } else if (c != QUESTION && k != c && kfold != c) { + return FNM_NOMATCH; + } + } + + /* We're all done with the tails now, so throw them out */ + endstr = stail; + endpat = ptail; + + /* Match pattern components until there are none left */ + while (pat 0) str += sinc; + else for (str++; str_next(str, endstr-str, &sinc)<0; str++); + } + + return 0; +} + +int fnmatch(const char *pat, const char *str, int flags) +{ + const char *s, *p; + size_t inc; + int c; + if (flags & FNM_PATHNAME) for (;;) { + for (s=str; *s && *s!='/'; s++); + for (p=pat; (c=pat_next(p, -1, &inc))!=END && c!='/'; p+=inc); + if (c!=*s) + return FNM_NOMATCH; + if (fnmatch_internal(pat, p-pat, str, s-str)) + return FNM_NOMATCH; + if (!c) return 0; + str = s+1; + pat = p+inc; + } + return fnmatch_internal(pat, -1, str, -1); +} +#endif /* HAVE_FNMATCH */ diff --git a/lib/compat/src/getopt.c b/lib/compat/src/getopt.c new file mode 100644 index 0000000..9876a87 --- /dev/null +++ b/lib/compat/src/getopt.c @@ -0,0 +1,96 @@ +#include "compat.h" + +#include +#include +#include +#include + +#ifndef HAVE_GETOPT +char *optarg; +int optind=1, opterr=1, optopt, optpos, optreset=0; + +void __getopt_msg(const char *a, const char *b, const char *c, size_t l) +{ + fputs(a, stderr); + fwrite(b, strlen(b), 1, stderr); + fwrite(c, 1, l, stderr); + putc('\n', stderr); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i; + wchar_t c, d; + int k, l; + char *optchar; + + if (!optind || __optreset) { + optreset = 0; + optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { + k = 1; + c = 0xfffd; /* replacement char */ + } + optchar = argv[optind]+optpos; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + l = mbtowc(&d, optstring+i, MB_LEN_MAX); + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c || c == ':') { + optopt = c; + if (optstring[0] != ':' && opterr) + __getopt_msg(argv[0], ": unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + optarg = 0; + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + if (optind > argc) { + optopt = c; + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg(argv[0], + ": option requires an argument: ", + optchar, k); + return '?'; + } + } + return c; +} +#endif diff --git a/lib/compat/src/getopt_long.c b/lib/compat/src/getopt_long.c new file mode 100644 index 0000000..58354c3 --- /dev/null +++ b/lib/compat/src/getopt_long.c @@ -0,0 +1,95 @@ +#include "compat.h" + +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_GETOPT_LONG +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + (argv[optind][1] == '-' && argv[optind][2])) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match; + char *arg, *opt, *start = argv[optind]+1; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = start; + if (*opt == '-') opt++; + while (*opt && *opt != '=' && *opt == *name) + name++, opt++; + if (*opt && *opt != '=') continue; + arg = opt; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + opt = arg; + optind++; + if (*opt == '=') { + if (!longopts[i].has_arg) { + optopt = longopts[i].val; + if (colon || !opterr) + return '?'; + __getopt_msg(argv[0], + ": option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + optopt = longopts[i].val; + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg(argv[0], + ": option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + optopt = 0; + if (!colon && opterr) + __getopt_msg(argv[0], cnt ? + ": option is ambiguous: " : + ": unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} +#endif diff --git a/lib/compat/src/getsubopt.c b/lib/compat/src/getsubopt.c new file mode 100644 index 0000000..e6fea1a --- /dev/null +++ b/lib/compat/src/getsubopt.c @@ -0,0 +1,27 @@ +#include "compat.h" + +#include +#include + +#ifndef HAVE_GETSUBOPT +int getsubopt(char **opt, char *const *keys, char **val) +{ + char *s = *opt; + int i; + + *val = NULL; + *opt = strchr(s, ','); + if (*opt) *(*opt)++ = 0; + else *opt = s + strlen(s); + + for (i=0; keys[i]; i++) { + size_t l = strlen(keys[i]); + if (strncmp(keys[i], s, l)) continue; + if (s[l] == '=') + *val = s + l + 1; + else if (s[l]) continue; + return i; + } + return -1; +} +#endif diff --git a/lib/compat/src/mockups.c b/lib/compat/src/mockups.c new file mode 100644 index 0000000..4c396ec --- /dev/null +++ b/lib/compat/src/mockups.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * mockups.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include + +#ifdef _WIN32 +int fchownat(int dirfd, const char *path, int uid, int gid, int flags) +{ + if (dirfd != AT_FDCWD) { + fputs("[FIXME] fchownat stub only supports AT_FDCWD!\n", + stderr); + return -1; + } + + if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { + fputs("[FIXME] fchownat stub used with an unknown flag!\n", + stderr); + return -1; + } + + (void)path; + (void)uid; + (void)gid; + return 0; +} + +int fchmodat(int dirfd, const char *path, int mode, int flags) +{ + if (dirfd != AT_FDCWD) { + fputs("[FIXME] fchmodat stub only supports AT_FDCWD!\n", + stderr); + return -1; + } + + if (flags != 0 && flags != AT_SYMLINK_NOFOLLOW) { + fputs("[FIXME] fchmodat stub used with an unknown flag!\n", + stderr); + return -1; + } + + (void)path; + (void)mode; + return 0; +} +#endif diff --git a/lib/compat/src/path_to_windows.c b/lib/compat/src/path_to_windows.c new file mode 100644 index 0000000..ff3a5d2 --- /dev/null +++ b/lib/compat/src/path_to_windows.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * path_to_windows.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +WCHAR *path_to_windows(const char *input) +{ + WCHAR *wpath, *ptr; + DWORD length; + + length = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + if (length <= 0) { + fprintf(stderr, "Converting UTF-8 path to UTF-16: %ld\n", + GetLastError()); + return NULL; + } + + wpath = calloc(sizeof(wpath[0]), length + 1); + if (wpath == NULL) { + fprintf(stderr, + "Converting UTF-8 path to UTF-16: out of memory\n"); + return NULL; + } + + MultiByteToWideChar(CP_UTF8, 0, input, -1, wpath, length + 1); + wpath[length] = '\0'; + + for (ptr = wpath; *ptr != '\0'; ++ptr) { + if (*ptr == '/') + *ptr = '\\'; + } + + return wpath; +} +#endif diff --git a/lib/compat/src/strchrnul.c b/lib/compat/src/strchrnul.c new file mode 100644 index 0000000..7296060 --- /dev/null +++ b/lib/compat/src/strchrnul.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strchrnul.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#ifndef HAVE_STRCHRNUL +char *strchrnul(const char *s, int c) +{ + while (*s && *((unsigned char *)s) != c) + ++s; + + return (char *)s; +} +#endif diff --git a/lib/compat/src/strndup.c b/lib/compat/src/strndup.c new file mode 100644 index 0000000..7e77f6c --- /dev/null +++ b/lib/compat/src/strndup.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strndup.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifndef HAVE_STRNDUP +char *strndup(const char *str, size_t max_len) +{ + size_t len = 0; + char *out; + + while (len < max_len && str[len] != '\0') + ++len; + + out = malloc(len + 1); + + if (out != NULL) { + memcpy(out, str, len); + out[len] = '\0'; + } + + return out; +} +#endif diff --git a/lib/compat/src/w32_perror.c b/lib/compat/src/w32_perror.c new file mode 100644 index 0000000..8c84191 --- /dev/null +++ b/lib/compat/src/w32_perror.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_perror.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +void w32_perror(const char *str) +{ + DWORD nStatus = GetLastError(); + LPVOID msg = NULL; + + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, nStatus, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg, 0, NULL); + + fprintf(stderr, "%s: %s\n", str, (const char *)msg); + + if (msg != NULL) + LocalFree(msg); +} +#endif diff --git a/lib/compat/src/w32_stdio.c b/lib/compat/src/w32_stdio.c new file mode 100644 index 0000000..c7f68d0 --- /dev/null +++ b/lib/compat/src/w32_stdio.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_stdio.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#undef fputc +#undef putc +#undef fputs +#undef fprintf +#undef printf + +static HANDLE get_handle(FILE *strm) +{ + if (strm != stdout && strm != stderr) + return INVALID_HANDLE_VALUE; + + return GetStdHandle(strm == stderr ? + STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); +} + +static BOOL isatty(HANDLE hnd) +{ + if (hnd == INVALID_HANDLE_VALUE) + return FALSE; + + return (GetFileType(hnd) == FILE_TYPE_CHAR); +} + +int sqfs_tools_fputs(const char *str, FILE *strm) +{ + DWORD length; + WCHAR *wstr; + HANDLE hnd; + int ret; + + hnd = get_handle(strm); + if (!isatty(hnd)) + return fputs(str, strm); + + length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + if (length <= 0) + return EOF; + + wstr = calloc(sizeof(wstr[0]), length); + if (wstr == NULL) + return EOF; + + MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, length); + + ret = WriteConsoleW(hnd, wstr, length, NULL, NULL) ? length : EOF; + + free(wstr); + return ret; +} + +int sqfs_tools_fputc(int c, FILE *strm) +{ + char str[2]; + + str[0] = c; + str[1] = '\0'; + + return sqfs_tools_fputs(str, strm); +} + +static int sqfs_printf_common(FILE *out, const char *fmt, va_list ap) +{ + int ret, len; + char *str; + + len = _vscprintf(fmt, ap); + if (len == -1) + return -1; + + str = malloc((size_t)len + 1); + if (str == NULL) + return -1; + + ret = vsprintf(str, fmt, ap); + if (ret == -1) { + free(str); + return -1; + } + + if (sqfs_tools_fputs(str, out) == EOF) + ret = -1; + + free(str); + return ret; +} + +int sqfs_tools_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = sqfs_printf_common(stdout, fmt, ap); + va_end(ap); + return ret; +} + +int sqfs_tools_fprintf(FILE *strm, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = sqfs_printf_common(strm, fmt, ap); + va_end(ap); + return ret; +} +#endif diff --git a/lib/compat/src/w32_wmain.c b/lib/compat/src/w32_wmain.c new file mode 100644 index 0000000..9b3e354 --- /dev/null +++ b/lib/compat/src/w32_wmain.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_wmain.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#include "config.h" +#include "compat.h" + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#undef main + +int main(int argc, char **argv) +{ + WCHAR *cmdline, **argList; + int i, ret, utf8_argc; + char **utf8_argv; + (void)argc; + (void)argv; + + /* get the UTF-16 encoded command line arguments */ + cmdline = GetCommandLineW(); + argList = CommandLineToArgvW(cmdline, &utf8_argc); + if (argList == NULL) + goto fail_oom; + + /* convert to UTF-8 */ + utf8_argv = calloc(sizeof(utf8_argv[0]), utf8_argc); + if (utf8_argv == NULL) + goto fail_oom; + + for (i = 0; i < utf8_argc; ++i) { + DWORD length = WideCharToMultiByte(CP_UTF8, 0, argList[i], + -1, NULL, 0, NULL, NULL); + if (length <= 0) + goto fail_conv; + + utf8_argv[i] = calloc(1, length + 1); + if (utf8_argv[i] == NULL) + goto fail_oom; + + WideCharToMultiByte(CP_UTF8, 0, argList[i], -1, + utf8_argv[i], length + 1, NULL, NULL); + utf8_argv[i][length] = '\0'; + } + + LocalFree(argList); + argList = NULL; + + /* call the actual main function */ + ret = sqfs_tools_main(utf8_argc, utf8_argv); + + /* cleanup */ + for (i = 0; i < utf8_argc; ++i) + free(utf8_argv[i]); + + free(utf8_argv); + return ret; +fail_conv: + w32_perror("Converting UTF-16 argument to UTF-8"); + goto fail; +fail_oom: + fputs("out of memory\n", stderr); + goto fail; +fail: + if (utf8_argv != NULL) { + for (i = 0; i < utf8_argc; ++i) + free(utf8_argv[i]); + free(utf8_argv); + } + if (argList != NULL) { + LocalFree(argList); + } + return EXIT_FAILURE; +} +#endif -- cgit v1.2.3