Provide a default progname on Windows [guillem@hadrons.org: - Remove .exe extension from default program name. - Call reallocarray() once by switching to a «do {} while» loop. - Minor coding style fixes. ] Signed-off-by: Guillem Jover <guillem@hadrons.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
diff --git a/src/progname.c b/src/progname.c
index 3edbf24..bebf714 100644
--- a/src/progname.c
+++ b/src/progname.c
@@ -1,6 +1,7 @@
/*
* Copyright © 2006 Robert Millan
* Copyright © 2010-2012 Guillem Jover <guillem@hadrons.org>
+ * Copyright © 2018 Facebook, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,11 +31,16 @@
* <https://sourceware.org/ml/libc-alpha/2006-03/msg00125.html>.
*/
+#include <sys/param.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
+#ifdef _WIN32
+#include <Windows.h>
+#include <shlwapi.h>
+#endif
-#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
+#ifdef _WIN32
#define LIBBSD_IS_PATHNAME_SEPARATOR(c) ((c) == '/' || (c) == '\\')
#else
#define LIBBSD_IS_PATHNAME_SEPARATOR(c) ((c) == '/')
@@ -56,6 +62,70 @@ getprogname(void)
/* getexecname(3) returns an absolute pathname, normalize it. */
if (__progname == NULL)
setprogname(getexecname());
+#elif defined(_WIN32)
+ if (__progname == NULL) {
+ WCHAR *wpath = NULL;
+ WCHAR *wname = NULL;
+ WCHAR *wext = NULL;
+ DWORD wpathsiz = MAX_PATH / 2;
+ DWORD len, i;
+ char *mbname = NULL;
+ int mbnamesiz;
+
+ /* Use the Unicode version of this function to support long
+ * paths. MAX_PATH isn't actually the maximum length of a
+ * path in this case. */
+ do {
+ WCHAR *wpathnew;
+
+ wpathsiz *= 2;
+ wpathsiz = MIN(wpathsiz, UNICODE_STRING_MAX_CHARS);
+ wpathnew = reallocarray(wpath, wpathsiz, sizeof(*wpath));
+ if (wpathnew == NULL)
+ goto done;
+ wpath = wpathnew;
+
+ len = GetModuleFileNameW(NULL, wpath, wpathsiz);
+ if (wpathsiz == UNICODE_STRING_MAX_CHARS)
+ goto done;
+ } while (wpathsiz == len);
+ if (len == 0)
+ goto done;
+
+ /* GetModuleFileNameW() retrieves an absolute path. Locate the
+ * filename now to only convert necessary characters and save
+ * memory. */
+ wname = wpath;
+ for (i = len; i > 0; i--) {
+ if (LIBBSD_IS_PATHNAME_SEPARATOR(wpath[i - 1])) {
+ wname = wpath + i;
+ break;
+ }
+ }
+
+ /* Remove any trailing extension, such as '.exe', to make the
+ * behavior mach the non-Windows systems. */
+ wext = PathFindExtensionW(wname);
+ wext[0] = '\0';
+
+ mbnamesiz = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL,
+ 0, NULL, NULL);
+ if (mbnamesiz == 0)
+ goto done;
+ mbname = malloc(mbnamesiz);
+ if (mbname == NULL)
+ goto done;
+ mbnamesiz = WideCharToMultiByte(CP_UTF8, 0, wname, -1, mbname,
+ mbnamesiz, NULL, NULL);
+ if (mbnamesiz == 0)
+ goto done;
+ __progname = mbname;
+ mbname = NULL;
+
+done:
+ free(wpath);
+ free(mbname);
+ }
#endif
return __progname;