|
1 |
| -/* Well-behaved command line aliases for w64devkit |
2 |
| - * |
3 |
| - * Unlike batch script aliases, this program will not produce an annoying |
4 |
| - * and useless "Terminate batch job (Y/N)" prompt. When compiling, define |
5 |
| - * EXE as the target executable (relative or absolute path), and define CMD |
6 |
| - * as the argv[0] replacement, including additional arguments. Example: |
7 |
| - * |
8 |
| - * $ gcc -DEXE="target.exe" -DCMD="argv0 argv1" \ |
9 |
| - * -Os -fno-asynchronous-unwind-tables \ |
10 |
| - * -Wl,--gc-sections -s -nostdlib \ |
11 |
| - * -o alias.exe alias.c -lkernel32 |
12 |
| - * |
13 |
| - * This program is compiled CRT-free in order to be as small as possible. |
14 |
| - * |
15 |
| - * This is free and unencumbered software released into the public domain. |
16 |
| - */ |
17 |
| -#define WIN32_LEAN_AND_MEAN |
18 |
| -#include <windows.h> |
19 |
| - |
20 |
| -#define FATAL "fatal: w64devkit alias failed\n" |
21 |
| -#define COUNTOF(a) (sizeof(a) / sizeof(0[a])) |
| 1 | +// Well-behaved command line aliases for w64devkit |
| 2 | +// |
| 3 | +// Unlike batch script aliases, this program will not produce an annoying |
| 4 | +// and useless "Terminate batch job (Y/N)" prompt. When compiling, define |
| 5 | +// EXE as the target executable (relative or absolute path), and define CMD |
| 6 | +// as the argv[0] replacement, including additional arguments. Example: |
| 7 | +// |
| 8 | +// $ gcc -DEXE="target.exe" -DCMD="argv0 argv1" |
| 9 | +// -Os -fno-asynchronous-unwind-tables |
| 10 | +// -s -nostartfiles -Wl,--gc-sections -o alias.exe alias.c |
| 11 | +// |
| 12 | +// This is free and unencumbered software released into the public domain. |
| 13 | + |
| 14 | +// Win32 declarations |
| 15 | +// NOTE: Parsing windows.h was by far the slowest part of the build, and |
| 16 | +// this program is compiled hundreds times for w64devkit. So instead, |
| 17 | +// define just what's needed. |
| 18 | + |
| 19 | +#define MAX_PATH 260 |
| 20 | + |
| 21 | +typedef __SIZE_TYPE__ size_t; |
| 22 | +typedef unsigned short char16_t; |
| 23 | + |
| 24 | +typedef struct { |
| 25 | + int cb; |
| 26 | + void *a, *b, *c; |
| 27 | + int d, e, f, g, h, i, j, k; |
| 28 | + short l, m; |
| 29 | + void *n, *o, *p, *q; |
| 30 | +} StartupInfo; |
| 31 | + |
| 32 | +typedef struct { |
| 33 | + void *process; |
| 34 | + void *thread; |
| 35 | + int pid; |
| 36 | + int tid; |
| 37 | +} ProcessInformation; |
| 38 | + |
| 39 | +int CreateProcessW( |
| 40 | + void *, void *, void *, void *, int, int, void *, void *, void *, void * |
| 41 | +) __attribute((dllimport,stdcall)); |
| 42 | +char16_t *GetCommandLineW(void) |
| 43 | + __attribute((dllimport,stdcall)); |
| 44 | +int GetExitCodeProcess(void *, int *) |
| 45 | + __attribute((dllimport,stdcall)); |
| 46 | +int GetModuleFileNameW(void *, char16_t *, int) |
| 47 | + __attribute((dllimport,stdcall)); |
| 48 | +void *GetStdHandle(int) |
| 49 | + __attribute((dllimport,stdcall)); |
| 50 | +int lstrlenW(void *) |
| 51 | + __attribute((dllimport,stdcall)); |
| 52 | +void *VirtualAlloc(void *, size_t, int, int) |
| 53 | + __attribute__((dllimport,stdcall,malloc)); |
| 54 | +int WriteFile(void *, void *, int, int *, void *) |
| 55 | + __attribute((dllimport,stdcall)); |
| 56 | +int WaitForSingleObject(void *, int) |
| 57 | + __attribute((dllimport,stdcall)); |
| 58 | + |
| 59 | +// Application |
| 60 | + |
| 61 | +#define FATAL "fatal: w64devkit alias failed: " |
| 62 | +#define TOOLONG "command too long\n" |
| 63 | +#define OOM "out of memory\n" |
| 64 | +#define CREATEPROC "cannot create process\n" |
| 65 | +#define COUNTOF(a) (int)(sizeof(a) / sizeof(0[a])) |
22 | 66 | #define LSTR(s) XSTR(s)
|
23 | 67 | #define XSTR(s) L ## # s
|
24 | 68 | #define LEXE LSTR(EXE)
|
25 | 69 | #define LCMD LSTR(CMD)
|
26 | 70 |
|
27 |
| -static size_t |
28 |
| -xstrlen(WCHAR *s) |
29 |
| -{ |
30 |
| - size_t n = 1; |
31 |
| - while (*s++) n++; |
32 |
| - return n; |
33 |
| -} |
| 71 | +#define STRBUF(buf, cap) {buf, cap, 0, 0} |
| 72 | +typedef struct { |
| 73 | + char16_t *buf; |
| 74 | + int cap; |
| 75 | + int len; |
| 76 | + int error; |
| 77 | +} StrBuf; |
34 | 78 |
|
35 |
| -static void |
36 |
| -xmemcpy(void *dst, void *src, size_t len) |
| 79 | +static void append(StrBuf *b, char16_t *s, int len) |
37 | 80 | {
|
38 |
| - unsigned char *d = dst, *s = src; |
39 |
| - for (size_t i = 0; i < len; i++) d[i] = s[i]; |
| 81 | + int avail = b->cap - b->len; |
| 82 | + int count = len<avail ? len : avail; |
| 83 | + for (int i = 0; i < count; i++) { |
| 84 | + b->buf[b->len+i] = s[i]; |
| 85 | + } |
| 86 | + b->len += count; |
| 87 | + b->error |= len > avail; |
40 | 88 | }
|
41 | 89 |
|
42 |
| -/* Find the end of argv[0]. */ |
43 |
| -static WCHAR * |
44 |
| -findargs(WCHAR *s) |
| 90 | +// Find the end of argv[0]. |
| 91 | +static char16_t *findargs(char16_t *s) |
45 | 92 | {
|
46 |
| - if (s[0] == 34) { |
47 |
| - /* quoted argv[0] */ |
48 |
| - for (s++;; s++) { |
| 93 | + if (s[0] == '"') { |
| 94 | + for (s++;; s++) { // quoted argv[0] |
49 | 95 | switch (*s) {
|
50 |
| - case 0: return s; |
51 |
| - case 34: return s + 1; |
| 96 | + case 0: return s; |
| 97 | + case '"': return s + 1; |
52 | 98 | }
|
53 | 99 | }
|
54 | 100 | } else {
|
55 |
| - /* unquoted argv[0] */ |
56 |
| - for (;; s++) { |
| 101 | + for (;; s++) { // unquoted argv[0] |
57 | 102 | switch (*s) {
|
58 |
| - case 0: |
59 |
| - case 9: |
60 |
| - case 32: return s; |
| 103 | + case 0: |
| 104 | + case '\t': |
| 105 | + case ' ': return s; |
61 | 106 | }
|
62 | 107 | }
|
63 | 108 | }
|
64 | 109 | }
|
65 | 110 |
|
66 |
| -/* Find the final file component. */ |
67 |
| -static WCHAR * |
68 |
| -findfile(WCHAR *s) |
| 111 | +// Return the directory component length including the last slash. |
| 112 | +static int dirname(char16_t *s) |
69 | 113 | {
|
70 |
| - for (WCHAR *r = s; ; s++) { |
71 |
| - switch (*s) { |
72 |
| - case 0: return r; |
73 |
| - case 47: |
74 |
| - case 92: r = s + 1; |
| 114 | + int len = 0; |
| 115 | + for (int i = 0; s[i]; i++) { |
| 116 | + if (s[i]=='/' || s[i]=='\\') { |
| 117 | + len = i + 1; |
75 | 118 | }
|
76 | 119 | }
|
| 120 | + return len; |
| 121 | +} |
| 122 | + |
| 123 | +static void fail(char *reason, int len) |
| 124 | +{ |
| 125 | + int dummy; |
| 126 | + void *out = GetStdHandle(-12); |
| 127 | + WriteFile(out, FATAL, COUNTOF(FATAL), &dummy, 0); |
| 128 | + WriteFile(out, reason, len, &dummy, 0); |
77 | 129 | }
|
78 | 130 |
|
79 |
| -int |
80 |
| -mainCRTStartup(void) |
| 131 | +#if __i386 |
| 132 | +__attribute((force_align_arg_pointer)) |
| 133 | +#endif |
| 134 | +__attribute((externally_visible)) |
| 135 | +int mainCRTStartup(void) |
81 | 136 | {
|
82 |
| - /* Replace alias module with adjacent target. */ |
83 |
| - WCHAR exe[MAX_PATH + COUNTOF(LEXE)]; |
84 |
| - if (LEXE[1] != ':') { |
85 |
| - /* EXE looks like a relative path */ |
86 |
| - GetModuleFileNameW(0, exe, MAX_PATH); |
87 |
| - WCHAR *file = findfile(exe); |
88 |
| - xmemcpy(file, LEXE, sizeof(LEXE)); |
| 137 | + // Replace alias module with adjacent target |
| 138 | + char16_t exebuf[MAX_PATH]; |
| 139 | + StrBuf exe = STRBUF(exebuf, COUNTOF(exebuf)); |
| 140 | + if (LEXE[1] == ':') { |
| 141 | + // EXE looks like an absolute path |
| 142 | + append(&exe, LEXE, COUNTOF(LEXE)); |
89 | 143 | } else {
|
90 |
| - /* EXE looks like an absolute path */ |
91 |
| - xmemcpy(exe, LEXE, sizeof(LEXE)); |
| 144 | + // EXE looks like a relative path |
| 145 | + char16_t module[MAX_PATH]; |
| 146 | + GetModuleFileNameW(0, module, COUNTOF(module)); |
| 147 | + int len = dirname(module); |
| 148 | + append(&exe, module, len); |
| 149 | + append(&exe, LEXE, COUNTOF(LEXE)); |
| 150 | + } |
| 151 | + |
| 152 | + // Construct a new command line string |
| 153 | + int cmdcap = 1<<15; |
| 154 | + char16_t *cmdbuf = VirtualAlloc(0, 2*cmdcap, 0x3000, 4); |
| 155 | + if (!cmdbuf) { |
| 156 | + fail(OOM, COUNTOF(OOM)-1); |
| 157 | + return -2; |
| 158 | + } |
| 159 | + StrBuf cmd = STRBUF(cmdbuf, cmdcap); |
| 160 | + append(&cmd, LCMD, COUNTOF(LCMD)-1); |
| 161 | + char16_t *args = findargs(GetCommandLineW()); |
| 162 | + append(&cmd, args, lstrlenW(args)+1); |
| 163 | + |
| 164 | + if (exe.error || cmd.error) { |
| 165 | + fail(TOOLONG, COUNTOF(TOOLONG)-1); |
| 166 | + return -3; |
92 | 167 | }
|
93 | 168 |
|
94 |
| - /* Produce a new command line string with new argv[0]. */ |
95 |
| - WCHAR *args = findargs(GetCommandLineW()); |
96 |
| - size_t argslen = xstrlen(args); |
97 |
| - size_t cmdlen = COUNTOF(LCMD) + argslen - 1; |
98 |
| - WCHAR *cmd = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*cmdlen); |
99 |
| - xmemcpy(cmd, LCMD, sizeof(WCHAR)*(COUNTOF(LCMD) - 1)); |
100 |
| - xmemcpy(cmd + COUNTOF(LCMD) - 1, args, sizeof(WCHAR)*argslen); |
101 |
| - |
102 |
| - /* Run target with identical startup but with above changes. */ |
103 |
| - STARTUPINFOW si; |
104 |
| - GetStartupInfoW(&si); |
105 |
| - PROCESS_INFORMATION pi; |
106 |
| - if (!CreateProcessW(exe, cmd, 0, 0, TRUE, 0, 0, 0, &si, &pi)) { |
107 |
| - HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); |
108 |
| - DWORD dummy; |
109 |
| - WriteFile(out, FATAL, sizeof(FATAL), &dummy, 0); |
110 |
| - return 2; |
| 169 | + StartupInfo si = {0}; |
| 170 | + si.cb = sizeof(si); |
| 171 | + ProcessInformation pi; |
| 172 | + if (!CreateProcessW(exebuf, cmdbuf, 0, 0, 1, 0, 0, 0, &si, &pi)) { |
| 173 | + fail(CREATEPROC, COUNTOF(CREATEPROC)-1); |
| 174 | + return -4; |
111 | 175 | }
|
112 | 176 |
|
113 |
| - /* Wait for target to exit. */ |
114 |
| - DWORD ret; |
115 |
| - WaitForSingleObject(pi.hProcess, INFINITE); |
116 |
| - GetExitCodeProcess(pi.hProcess, &ret); |
| 177 | + int ret; |
| 178 | + WaitForSingleObject(pi.process, -1); |
| 179 | + GetExitCodeProcess(pi.process, &ret); |
117 | 180 | return ret;
|
118 | 181 | }
|
0 commit comments