aboutsummaryrefslogtreecommitdiff
path: root/lib/compat/src
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 11:21:30 +0100
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2023-01-31 13:51:49 +0100
commitcdccc69c62579b0c13b35fad0728079652b8f3c9 (patch)
tree9fa54c710f73c5e08a9c8466e7a712eb63ee07ac /lib/compat/src
parent2182129c8f359c4fa1390eaba7a65b595ccd4182 (diff)
Move library source into src sub-directory
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/compat/src')
-rw-r--r--lib/compat/src/chdir.c34
-rw-r--r--lib/compat/src/fnmatch.c305
-rw-r--r--lib/compat/src/getopt.c96
-rw-r--r--lib/compat/src/getopt_long.c95
-rw-r--r--lib/compat/src/getsubopt.c27
-rw-r--r--lib/compat/src/mockups.c51
-rw-r--r--lib/compat/src/path_to_windows.c43
-rw-r--r--lib/compat/src/strchrnul.c18
-rw-r--r--lib/compat/src/strndup.c31
-rw-r--r--lib/compat/src/w32_perror.c33
-rw-r--r--lib/compat/src/w32_stdio.c125
-rw-r--r--lib/compat/src/w32_wmain.c83
12 files changed, 941 insertions, 0 deletions
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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#ifdef _WIN32
+#include <stdlib.h>
+#include <stdio.h>
+
+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 <string.h>
+#include <stdlib.h>
+
+#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<m) if (pat[k] == '^' || pat[k] == '!') k++;
+ if (k<m) if (pat[k] == ']') k++;
+ for (; k<m && pat[k] && pat[k]!=']'; k++) {
+ if (k+1<m && pat[k+1] && pat[k]=='[' && (pat[k+1]==':' || pat[k+1]=='.' || pat[k+1]=='=')) {
+ int z = pat[k+1];
+ k+=2;
+ if (k<m && pat[k]) k++;
+ while (k<m && pat[k] && (pat[k-1]!=z || pat[k]!=']')) k++;
+ if (k==m || !pat[k]) break;
+ }
+ }
+ if (k==m || !pat[k]) {
+ *step = 1;
+ return '[';
+ }
+ *step = k+1;
+ return BRACKET;
+ }
+ if (pat[0] == '*')
+ return STAR;
+ if (pat[0] == '?')
+ return QUESTION;
+escaped:
+ if (pat[0] >= 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; p<endpat; p+=pinc) {
+ switch (pat_next(p, endpat-p, &pinc)) {
+ case UNMATCHABLE:
+ return FNM_NOMATCH;
+ case STAR:
+ tailcnt=0;
+ ptail = p+1;
+ break;
+ default:
+ tailcnt++;
+ break;
+ }
+ }
+
+ /* Past this point we need not check for UNMATCHABLE in pat,
+ * because all of pat has already been parsed once. */
+
+ /* Compute real str length if it was initially unknown/-1 */
+ n = strnlen(str, n);
+ endstr = str + n;
+ if (n < tailcnt) return FNM_NOMATCH;
+
+ /* Find the final tailcnt chars of str, accounting for UTF-8.
+ * On illegal sequences we may get it wrong, but in that case
+ * we necessarily have a matching failure anyway. */
+ for (s=endstr; s>str && 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<endpat) {
+ p = pat;
+ s = str;
+ for (;;) {
+ c = pat_next(p, endpat-p, &pinc);
+ p += pinc;
+ /* Encountering * completes/commits a component */
+ if (c == STAR) {
+ pat = p;
+ str = s;
+ break;
+ }
+ k = str_next(s, endstr-s, &sinc);
+ if (!k)
+ return FNM_NOMATCH;
+ kfold = k;
+ if (c == BRACKET) {
+ if (!match_bracket(p-pinc, k, kfold))
+ break;
+ } else if (c != QUESTION && k != c && kfold != c) {
+ break;
+ }
+ s += sinc;
+ }
+ if (c == STAR) continue;
+ /* If we failed, advance str, by 1 char if it's a valid
+ * char, or past all invalid bytes otherwise. */
+ k = str_next(str, endstr-str, &sinc);
+ if (k > 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 <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#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 <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 <stdlib.h>
+#include <string.h>
+
+#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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <stdio.h>
+
+#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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 <goliath@infraroot.at>
+ */
+#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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <stdio.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stdarg.h>
+
+#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 <goliath@infraroot.at>
+ */
+#include "config.h"
+#include "compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+
+#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