Skip to content

Commit 98a7d85

Browse files
committed
Improve alias.c with a near rewrite
I've gotten a lot better at CRT-free since I first wrote this. I'm more confident about this version, it produces better error messages, and it compiles around 10x faster. The last point matters because w64devkit currently compiles this program 181 times.
1 parent d266834 commit 98a7d85

File tree

1 file changed

+148
-85
lines changed

1 file changed

+148
-85
lines changed

src/alias.c

Lines changed: 148 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,181 @@
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]))
2266
#define LSTR(s) XSTR(s)
2367
#define XSTR(s) L ## # s
2468
#define LEXE LSTR(EXE)
2569
#define LCMD LSTR(CMD)
2670

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;
3478

35-
static void
36-
xmemcpy(void *dst, void *src, size_t len)
79+
static void append(StrBuf *b, char16_t *s, int len)
3780
{
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;
4088
}
4189

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)
4592
{
46-
if (s[0] == 34) {
47-
/* quoted argv[0] */
48-
for (s++;; s++) {
93+
if (s[0] == '"') {
94+
for (s++;; s++) { // quoted argv[0]
4995
switch (*s) {
50-
case 0: return s;
51-
case 34: return s + 1;
96+
case 0: return s;
97+
case '"': return s + 1;
5298
}
5399
}
54100
} else {
55-
/* unquoted argv[0] */
56-
for (;; s++) {
101+
for (;; s++) { // unquoted argv[0]
57102
switch (*s) {
58-
case 0:
59-
case 9:
60-
case 32: return s;
103+
case 0:
104+
case '\t':
105+
case ' ': return s;
61106
}
62107
}
63108
}
64109
}
65110

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)
69113
{
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;
75118
}
76119
}
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);
77129
}
78130

79-
int
80-
mainCRTStartup(void)
131+
#if __i386
132+
__attribute((force_align_arg_pointer))
133+
#endif
134+
__attribute((externally_visible))
135+
int mainCRTStartup(void)
81136
{
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));
89143
} 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;
92167
}
93168

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;
111175
}
112176

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);
117180
return ret;
118181
}

0 commit comments

Comments
 (0)