1 /* ----------------------------------------------------------------------------
2  * nmakehlp.c --
3  *
4  *	This is used to fix limitations within nmake and the environment.
5  *
6  * Copyright (c) 2002 by David Gravereaux.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  * ----------------------------------------------------------------------------
11  */
12 #include <windows.h>
13 #pragma comment (lib, "user32.lib")
14 #pragma comment (lib, "kernel32.lib")
15 #include <stdio.h>
16 #include <math.h>
17 
18 /* protos */
19 int CheckForCompilerFeature (const char *option);
20 int CheckForLinkerFeature (const char *option);
21 int IsIn (const char *string, const char *substring);
22 int GrepForDefine (const char *file, const char *string);
23 DWORD WINAPI ReadFromPipe (LPVOID args);
24 
25 /* globals */
26 #define CHUNK	25
27 #define STATICBUFFERSIZE    1000
28 typedef struct {
29     HANDLE pipe;
30     char buffer[STATICBUFFERSIZE];
31 } pipeinfo;
32 
33 pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
34 pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
35 
36 
37 
38 /* exitcodes: 0 == no, 1 == yes, 2 == error */
39 int
main(int argc,char * argv[])40 main (int argc, char *argv[])
41 {
42     char msg[300];
43     DWORD dwWritten;
44     int chars;
45 
46     /* make sure children (cl.exe and link.exe) are kept quiet. */
47     SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
48 
49     /* Make sure the compiler and linker aren't effected by the outside world. */
50     SetEnvironmentVariable("CL", "");
51     SetEnvironmentVariable("LINK", "");
52 
53     if (argc > 1 && *argv[1] == '-') {
54 	switch (*(argv[1]+1)) {
55 	case 'c':
56 	    if (argc != 3) {
57 		chars = wsprintf(msg, "usage: %s -c <compiler option>\n"
58 			"Tests for whether cl.exe supports an option\n"
59 			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
60 		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
61 		return 2;
62 	    }
63 	    return CheckForCompilerFeature(argv[2]);
64 	case 'l':
65 	    if (argc != 3) {
66 		chars = wsprintf(msg, "usage: %s -l <linker option>\n"
67 			"Tests for whether link.exe supports an option\n"
68 			"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
69 		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
70 		return 2;
71 	    }
72 	    return CheckForLinkerFeature(argv[2]);
73 	case 'f':
74 	    if (argc == 2) {
75 		chars = wsprintf(msg, "usage: %s -f <string> <substring>\n"
76 		    "Find a substring within another\n"
77 		    "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
78 		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
79 		return 2;
80 	    } else if (argc == 3) {
81 		/* if the string is blank, there is no match */
82 		return 0;
83 	    } else {
84 		return IsIn(argv[2], argv[3]);
85 	    }
86 	case 'g':
87 	    if (argc == 2) {
88 		chars = wsprintf(msg, "usage: %s -g <file> <string>\n"
89 		    "grep for a #define\n"
90 		    "exitcodes: integer of the found string (no decimals)\n", argv[0]);
91 		WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
92 		return 2;
93 	    }
94 	    return GrepForDefine(argv[2], argv[3]);
95 	}
96     }
97     chars = wsprintf(msg, "usage: %s -c|-l|-f ...\n"
98 	    "This is a little helper app to equalize shell differences between WinNT and\n"
99 	    "Win9x and get nmake.exe to accomplish its job.\n",
100 	    argv[0]);
101     WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
102     return 2;
103 }
104 
105 int
CheckForCompilerFeature(const char * option)106 CheckForCompilerFeature (const char *option)
107 {
108     STARTUPINFO si;
109     PROCESS_INFORMATION pi;
110     SECURITY_ATTRIBUTES sa;
111     DWORD threadID;
112     char msg[300];
113     BOOL ok;
114     HANDLE hProcess, h, pipeThreads[2];
115     char cmdline[100];
116 
117     hProcess = GetCurrentProcess();
118 
119     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
120     ZeroMemory(&si, sizeof(STARTUPINFO));
121     si.cb = sizeof(STARTUPINFO);
122     si.dwFlags   = STARTF_USESTDHANDLES;
123     si.hStdInput = INVALID_HANDLE_VALUE;
124 
125     ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
126     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
127     sa.lpSecurityDescriptor = NULL;
128     sa.bInheritHandle = FALSE;
129 
130     /* create a non-inheritible pipe. */
131     CreatePipe(&Out.pipe, &h, &sa, 0);
132 
133     /* dupe the write side, make it inheritible, and close the original. */
134     DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput,
135 	    0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
136 
137     /* Same as above, but for the error side. */
138     CreatePipe(&Err.pipe, &h, &sa, 0);
139     DuplicateHandle(hProcess, h, hProcess, &si.hStdError,
140 	    0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
141 
142     /* base command line */
143     strcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X ");
144     /* append our option for testing */
145     strcat(cmdline, option);
146     /* filename to compile, which exists, but is nothing and empty. */
147     strcat(cmdline, " .\\nul");
148 
149     ok = CreateProcess(
150 	    NULL,	    /* Module name. */
151 	    cmdline,	    /* Command line. */
152 	    NULL,	    /* Process handle not inheritable. */
153 	    NULL,	    /* Thread handle not inheritable. */
154 	    TRUE,	    /* yes, inherit handles. */
155 	    DETACHED_PROCESS, /* No console for you. */
156 	    NULL,	    /* Use parent's environment block. */
157 	    NULL,	    /* Use parent's starting directory. */
158 	    &si,	    /* Pointer to STARTUPINFO structure. */
159 	    &pi);	    /* Pointer to PROCESS_INFORMATION structure. */
160 
161     if (!ok) {
162 	DWORD err = GetLastError();
163 	int chars = wsprintf(msg, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
164 
165 	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
166 		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID) &msg[chars],
167 		(300-chars), 0);
168 	WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, strlen(msg), &err, NULL);
169 	return 2;
170     }
171 
172     /* close our references to the write handles that have now been inherited. */
173     CloseHandle(si.hStdOutput);
174     CloseHandle(si.hStdError);
175 
176     WaitForInputIdle(pi.hProcess, 5000);
177     CloseHandle(pi.hThread);
178 
179     /* start the pipe reader threads. */
180     pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
181     pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
182 
183     /* block waiting for the process to end. */
184     WaitForSingleObject(pi.hProcess, INFINITE);
185     CloseHandle(pi.hProcess);
186 
187     /* wait for our pipe to get done reading, should it be a little slow. */
188     WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
189     CloseHandle(pipeThreads[0]);
190     CloseHandle(pipeThreads[1]);
191 
192     /* look for the commandline warning code in both streams. */
193     return !(strstr(Out.buffer, "D4002") != NULL || strstr(Err.buffer, "D4002") != NULL);
194 }
195 
196 int
CheckForLinkerFeature(const char * option)197 CheckForLinkerFeature (const char *option)
198 {
199     STARTUPINFO si;
200     PROCESS_INFORMATION pi;
201     SECURITY_ATTRIBUTES sa;
202     DWORD threadID;
203     char msg[300];
204     BOOL ok;
205     HANDLE hProcess, h, pipeThreads[2];
206     char cmdline[100];
207 
208     hProcess = GetCurrentProcess();
209 
210     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
211     ZeroMemory(&si, sizeof(STARTUPINFO));
212     si.cb = sizeof(STARTUPINFO);
213     si.dwFlags   = STARTF_USESTDHANDLES;
214     si.hStdInput = INVALID_HANDLE_VALUE;
215 
216     ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
217     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
218     sa.lpSecurityDescriptor = NULL;
219     sa.bInheritHandle = TRUE;
220 
221     /* create a non-inheritible pipe. */
222     CreatePipe(&Out.pipe, &h, &sa, 0);
223 
224     /* dupe the write side, make it inheritible, and close the original. */
225     DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput,
226 	    0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
227 
228     /* Same as above, but for the error side. */
229     CreatePipe(&Err.pipe, &h, &sa, 0);
230     DuplicateHandle(hProcess, h, hProcess, &si.hStdError,
231 	    0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
232 
233     /* base command line */
234     strcpy(cmdline, "link.exe -nologo ");
235     /* append our option for testing */
236     strcat(cmdline, option);
237 
238     ok = CreateProcess(
239 	    NULL,	    /* Module name. */
240 	    cmdline,	    /* Command line. */
241 	    NULL,	    /* Process handle not inheritable. */
242 	    NULL,	    /* Thread handle not inheritable. */
243 	    TRUE,	    /* yes, inherit handles. */
244 	    DETACHED_PROCESS, /* No console for you. */
245 	    NULL,	    /* Use parent's environment block. */
246 	    NULL,	    /* Use parent's starting directory. */
247 	    &si,	    /* Pointer to STARTUPINFO structure. */
248 	    &pi);	    /* Pointer to PROCESS_INFORMATION structure. */
249 
250     if (!ok) {
251 	DWORD err = GetLastError();
252 	int chars = wsprintf(msg, "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
253 
254 	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
255 		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID) &msg[chars],
256 		(300-chars), 0);
257 	WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, strlen(msg), &err, NULL);
258 	return 2;
259     }
260 
261     /* close our references to the write handles that have now been inherited. */
262     CloseHandle(si.hStdOutput);
263     CloseHandle(si.hStdError);
264 
265     WaitForInputIdle(pi.hProcess, 5000);
266     CloseHandle(pi.hThread);
267 
268     /* start the pipe reader threads. */
269     pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
270     pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
271 
272     /* block waiting for the process to end. */
273     WaitForSingleObject(pi.hProcess, INFINITE);
274     CloseHandle(pi.hProcess);
275 
276     /* wait for our pipe to get done reading, should it be a little slow. */
277     WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
278     CloseHandle(pipeThreads[0]);
279     CloseHandle(pipeThreads[1]);
280 
281     /* look for the commandline warning code in the stderr stream. */
282     return !(strstr(Out.buffer, "LNK1117") != NULL || strstr(Err.buffer, "LNK1117") != NULL);
283 }
284 
285 DWORD WINAPI
ReadFromPipe(LPVOID args)286 ReadFromPipe (LPVOID args)
287 {
288     pipeinfo *pi = (pipeinfo *) args;
289     char *lastBuf = pi->buffer;
290     DWORD dwRead;
291     BOOL ok;
292 
293 again:
294     if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
295 	CloseHandle(pi->pipe);
296 	return -1;
297     }
298     ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
299     if (!ok || dwRead == 0) {
300 	CloseHandle(pi->pipe);
301 	return 0;
302     }
303     lastBuf += dwRead;
304     goto again;
305 
306     return 0;  /* makes the compiler happy */
307 }
308 
309 int
IsIn(const char * string,const char * substring)310 IsIn (const char *string, const char *substring)
311 {
312     return (strstr(string, substring) != NULL);
313 }
314 
315 /*
316  *  Find a specified #define by name.
317  *
318  *  If the line is '#define TCL_VERSION "8.5"', it returns
319  *  85 as the result.
320  */
321 
322 int
GrepForDefine(const char * file,const char * string)323 GrepForDefine (const char *file, const char *string)
324 {
325     FILE *f;
326     char s1[51], s2[51], s3[51];
327     int r = 0;
328     double d1;
329 
330     f = fopen(file, "rt");
331     if (f == NULL) {
332 	return 0;
333     }
334 
335     do {
336 	r = fscanf(f, "%50s", s1);
337 	if (r == 1 && !strcmp(s1, "#define")) {
338 	    /* get next two words */
339 	    r = fscanf(f, "%50s %50s", s2, s3);
340 	    if (r != 2) continue;
341 	    /* is the first word what we're looking for? */
342 	    if (!strcmp(s2, string)) {
343 		fclose(f);
344 		/* add 1 past first double quote char. "8.5" */
345 		d1 = atof(s3 + 1);		  /*    8.5  */
346 		while (floor(d1) != d1) {
347 		    d1 *= 10.0;
348 		}
349 		return ((int) d1);		  /*    85   */
350 	    }
351 	}
352     } while (!feof(f));
353 
354     fclose(f);
355     return 0;
356 }
357