Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / native_client / tools / redirector.c
1 /*
2  * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6
7 #include <wchar.h>
8 #include <windows.h>
9 #include "redirector.h"
10
11 #pragma comment(linker, "/entry:entry")
12
13 #define FILE_NOT_FOUND_ERROR_MESSAGE "Can not find filename to execute!\r\n"
14
15 /*
16  * The toolchain redirector reads it's name and invokes the appropriate binary
17  * from a location with cygwin1.dll nearby so that all cygwin-oriented
18  * toolchain programs can start (this works for Cygwin version >= 1.7).
19  *
20  * Supports arbitrary unicode names in it's base directory name.
21  *
22  * The executable must be placed in nacl/bin or nacl64/bin or just bin (in the
23  * latter case the name of the binary should be prefixed with nacl- or nacl64-)
24  *
25  * The binary should be built by visual studio (not cygwin) since when it starts
26  * cygwin has not been located yet.
27  *
28  * Use the following commands:
29  *   cl /c /O2 /GS- redirector.c
30  *   link /subsystem:console /MERGE:.rdata=.text /NODEFAULTLIB
31  *        redirector.obj kernel32.lib user32.lib
32  */
33
34 /*
35  * Wraps GetModuleFileNameW to always succeed in finding a buffer of
36  * appropriate size.
37  */
38 static
39 int get_module_name_safely(wchar_t **oldpath) {
40   int length = 128;
41   int real_size;
42   wchar_t *result;
43   *oldpath = NULL;
44   while (1) {
45     result = HeapAlloc(
46       GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(wchar_t) * length);
47     if (result == NULL) return 0;
48     real_size = GetModuleFileNameW(NULL, result, length);
49     if (real_size == 0) {
50       HeapFree(GetProcessHeap(), 0, result);
51       return 0;
52     }
53     if (real_size < length - 1) {
54       /* Success */
55       *oldpath = result;
56       return real_size;
57     }
58     length <<= 1;
59     HeapFree(GetProcessHeap(), 0, result);
60   }
61 }
62
63
64 static
65 const wchar_t* find_last_slash_or_backslash_delimiter(const wchar_t *start) {
66   const wchar_t *delimiter = start + lstrlenW(start);
67   while (delimiter > start && *delimiter != L'/' && *delimiter != L'\\')
68     --delimiter;
69   return delimiter;
70 }
71
72 static
73 wchar_t* find_program_arguments() {
74   wchar_t *arguments;
75
76   arguments = GetCommandLineW();
77   if (!arguments) return NULL;
78   if (arguments[0] == L'\"') {
79     arguments++;
80     while (arguments[0] && arguments[0] != L'\"') {
81       if (arguments[0] == L'\\' &&
82           (arguments[1] == L'\"' || arguments[1] == L'\\'))
83         arguments++;
84       arguments++;
85     }
86     arguments++;
87   }
88   while (arguments[0] && arguments[0] != L' ') arguments++;
89   if (arguments[0]) while (arguments[1] == ' ') arguments++;
90   return arguments;
91 }
92
93 static
94 wchar_t reduce_wchar(wchar_t c) {
95   if (c == L'/')
96     return L'\\';
97   return (wchar_t)CharLowerW((LPWSTR)c);
98 }
99
100 /*
101  * Returns whether path ends with redirect
102  */
103 static
104 int check_path (const wchar_t *path, const wchar_t *redirect) {
105   int path_len = lstrlenW(path);
106   int redirect_len = lstrlenW(redirect);
107   int path_offset;
108   int i;
109   if (path_len < redirect_len)
110     return 0;
111   path_offset = path_len - redirect_len;
112   for (i = 0; i < redirect_len; i++) {
113     if (reduce_wchar(path[i+path_offset]) != reduce_wchar(redirect[i]))
114       return 0;
115   }
116   return 1;
117 }
118
119 static
120 int is_driver(const wchar_t *option) {
121   return lstrcmpW(option, L"-m32") == 0 || lstrcmpW(option, L"-m64") == 0;
122 }
123
124 static
125 void println_redirect(const redirect_t *redirect) {
126   const wchar_t *str;
127   int tmp;
128   HANDLE output;
129   output = GetStdHandle(STD_OUTPUT_HANDLE);
130   for (str = redirect->from; *str; ++str)
131     WriteFile(output, str, 1, &tmp, NULL);
132   WriteFile(output, L"|", 1, &tmp, NULL);
133   for (str = redirect->to; *str; ++str)
134     WriteFile(output, str, 1, &tmp, NULL);
135   WriteFile(output, L"|", 1, &tmp, NULL);
136   for (str = redirect->args; *str; ++str)
137     WriteFile(output, str, 1, &tmp, NULL);
138   WriteFile(output, L"\n", 1, &tmp, NULL);
139 }
140
141 void entry() {
142   wchar_t *newpath = NULL, *oldpath = NULL;
143   const wchar_t *cmdline, *arguments, *selector;
144   int length, done;
145   int redirect_index;
146   int length_from;
147   int length_to;
148   int i;
149   int tmp;
150   HANDLE output;
151   int n_redirects = sizeof(redirects)/sizeof(redirect_t);
152   static STARTUPINFOW si = {sizeof(STARTUPINFOW), 0};
153   static PROCESS_INFORMATION pi;
154
155   length = get_module_name_safely(&oldpath);
156   if (length == 0) goto ShowErrorMessage;
157   /* If redirector is called as "redirector.exe", dump redirector table.  */
158   if (check_path(oldpath, L"redirector.exe")) {
159     output = GetStdHandle(STD_OUTPUT_HANDLE);
160     for (redirect_index = 0;
161          redirect_index < n_redirects;
162          redirect_index++) {
163       println_redirect(redirects + redirect_index);
164     }
165     ExitProcess(0);
166   }
167   for (redirect_index = 0;
168        redirect_index < n_redirects;
169        redirect_index++) {
170     if (check_path(oldpath, redirects[redirect_index].from))
171       break;
172   }
173   if (redirect_index >= n_redirects) goto ShowErrorMessage;
174   newpath = HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t)*(length + 64));
175   if (!newpath) goto ShowErrorMessage;
176   length_from = lstrlenW(redirects[redirect_index].from);
177   length_to = lstrlenW(redirects[redirect_index].to);
178   lstrcpynW(newpath, oldpath, length - length_from + 1);
179   lstrcpyW(newpath + length - length_from, redirects[redirect_index].to);
180
181   selector = redirects[redirect_index].args;
182   cmdline = redirects[redirect_index].to;
183   if (!is_driver(selector)) {
184     cmdline = find_last_slash_or_backslash_delimiter (cmdline);
185   } else {
186     cmdline = newpath;
187   }
188   HeapFree(GetProcessHeap(), 0, oldpath);
189   oldpath = NULL;
190   arguments = find_program_arguments();
191   if (!arguments) goto ShowErrorMessage;
192   length = lstrlenW(cmdline);
193   done = lstrlenW(selector);
194   oldpath = HeapAlloc(GetProcessHeap(), 0,
195     (lstrlenW(arguments) + length + done + 2) * sizeof(wchar_t));
196   if (!oldpath) goto ShowErrorMessage;
197   lstrcpyW(oldpath, cmdline);
198   /*
199    * cmdline == newpath means it's some kind of gcc driver and we need to
200    * handle this case specially: we need to put the -V option to be the first.
201    */
202   if (is_driver(selector) && arguments[1] == L'-' && arguments[2] == L'V') {
203     oldpath[length++] = (arguments++)[0];
204     oldpath[length++] = (arguments++)[0];
205     oldpath[length++] = (arguments++)[0];
206     if (arguments[0] == L' ')
207       while (arguments[1] == ' ')
208         arguments++;
209     if (arguments[0] != 0) {
210       oldpath[length++] = (arguments++)[0];
211       while (arguments[0] && arguments[0] != L' ')
212         oldpath[length++] = (arguments++)[0];
213     } else {
214       selector = L"";
215       done = 0;
216     }
217   }
218   lstrcpyW(oldpath + length, L" ");
219   lstrcpyW(oldpath + length + 1, selector);
220   lstrcpyW(oldpath + length + done + 1, arguments);
221   if (CreateProcessW(newpath, oldpath, NULL, NULL, TRUE,
222                      CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi) != 0) {
223     WaitForSingleObject(pi.hProcess, INFINITE);
224     HeapFree(GetProcessHeap(), 0, newpath);
225     HeapFree(GetProcessHeap(), 0, oldpath);
226     if (GetExitCodeProcess(pi.hProcess, &done)) {
227       CloseHandle(pi.hProcess);
228       CloseHandle(pi.hThread);
229       ExitProcess(done);
230     }
231     CloseHandle(pi.hProcess);
232     CloseHandle(pi.hThread);
233   }
234 ShowErrorMessage:
235   if (newpath) HeapFree(GetProcessHeap(), 0, newpath);
236   if (oldpath) HeapFree(GetProcessHeap(), 0, oldpath);
237   WriteFile(GetStdHandle(STD_ERROR_HANDLE), FILE_NOT_FOUND_ERROR_MESSAGE,
238             sizeof(FILE_NOT_FOUND_ERROR_MESSAGE) - 1, &length, NULL);
239   ExitProcess(1);
240 }