|
1 /* |
|
2 * Copyright (c) 2002-2003 ActiveState Corp. |
|
3 * Author: Trent Mick (TrentM@ActiveState.com) |
|
4 * |
|
5 * Permission is hereby granted, free of charge, to any person obtaining a |
|
6 * copy of this software and associated documentation files (the |
|
7 * "Software"), to deal in the Software without restriction, including |
|
8 * without limitation the rights to use, copy, modify, merge, publish, |
|
9 * distribute, sublicense, and/or sell copies of the Software, and to |
|
10 * permit persons to whom the Software is furnished to do so, subject to |
|
11 * the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included |
|
14 * in all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 /* Console launch executable. |
|
26 * |
|
27 * This program exists solely to launch: |
|
28 * python <installdir>/<exename>.py <argv> |
|
29 * on Windows. "<exename>.py" must be in the same directory. |
|
30 * |
|
31 * Rationale: |
|
32 * - On some Windows flavours .py *can* be put on the PATHEXT to be |
|
33 * able to find "<exename>.py" if it is on the PATH. This is fine |
|
34 * until you need shell redirection to work. It does NOT for |
|
35 * extensions to PATHEXT. Redirection *does* work for "python |
|
36 * <script>.py" so we will try to do that. |
|
37 */ |
|
38 |
|
39 #ifdef WIN32 |
|
40 #include <windows.h> |
|
41 #include <process.h> |
|
42 #include <direct.h> |
|
43 #include <shlwapi.h> |
|
44 #else /* linux */ |
|
45 #include <unistd.h> |
|
46 #endif /* WIN32 */ |
|
47 #include <sys/stat.h> |
|
48 #include <errno.h> |
|
49 #include <stdio.h> |
|
50 #include <stdlib.h> |
|
51 #include <string.h> |
|
52 #include <stdarg.h> |
|
53 |
|
54 //---- constants |
|
55 |
|
56 #define BUF_LENGTH 2048 |
|
57 #define MAX_PYTHON_ARGS 50 |
|
58 #define MAX_FILES 50 |
|
59 #define MAXPATHLEN 1024 |
|
60 #ifdef WIN32 |
|
61 #define SEP '\\' |
|
62 #define ALTSEP '/' |
|
63 // path list element separator |
|
64 #define DELIM ';' |
|
65 #else /* linux */ |
|
66 #define SEP '/' |
|
67 // path list element separator |
|
68 #define DELIM ':' |
|
69 #endif |
|
70 |
|
71 #ifdef WIN32 |
|
72 #define spawnvp _spawnvp |
|
73 #define snprintf _snprintf |
|
74 #define vsnprintf _vsnprintf |
|
75 //NOTE: this is for the stat *call* and the stat *struct* |
|
76 #define stat _stat |
|
77 #endif |
|
78 |
|
79 |
|
80 //---- globals |
|
81 |
|
82 char* programName = NULL; |
|
83 char* programPath = NULL; |
|
84 #ifndef WIN32 /* i.e. linux */ |
|
85 extern char **environ; // the user environment |
|
86 #endif /* linux */ |
|
87 |
|
88 //---- error logging functions |
|
89 |
|
90 void _LogError(const char* format ...) |
|
91 { |
|
92 va_list ap; |
|
93 va_start(ap, format); |
|
94 #if defined(WIN32) && defined(_WINDOWS) |
|
95 // put up a MessageBox |
|
96 char caption[BUF_LENGTH+1]; |
|
97 snprintf(caption, BUF_LENGTH, "Error in %s", programName); |
|
98 char msg[BUF_LENGTH+1]; |
|
99 vsnprintf(msg, BUF_LENGTH, format, ap); |
|
100 va_end(ap); |
|
101 MessageBox(NULL, msg, caption, MB_OK | MB_ICONEXCLAMATION); |
|
102 #else |
|
103 fprintf(stderr, "%s: error: ", programName); |
|
104 vfprintf(stderr, format, ap); |
|
105 va_end(ap); |
|
106 #endif /* WIN32 && _WINDOWS */ |
|
107 } |
|
108 |
|
109 |
|
110 void _LogWarning(const char* format ...) |
|
111 { |
|
112 va_list ap; |
|
113 va_start(ap, format); |
|
114 #if defined(WIN32) && defined(_WINDOWS) |
|
115 // put up a MessageBox |
|
116 char caption[BUF_LENGTH+1]; |
|
117 snprintf(caption, BUF_LENGTH, "Warning in %s", programName); |
|
118 char msg[BUF_LENGTH+1]; |
|
119 vsnprintf(msg, BUF_LENGTH, format, ap); |
|
120 va_end(ap); |
|
121 MessageBox(NULL, msg, caption, MB_OK | MB_ICONWARNING); |
|
122 #else |
|
123 fprintf(stderr, "%s: warning: ", programName); |
|
124 vfprintf(stderr, format, ap); |
|
125 va_end(ap); |
|
126 #endif /* WIN32 && _WINDOWS */ |
|
127 } |
|
128 |
|
129 |
|
130 |
|
131 //---- utilities functions |
|
132 |
|
133 /* _IsDir: Is the given dirname an existing directory */ |
|
134 static int _IsDir(char *dirname) |
|
135 { |
|
136 #ifdef WIN32 |
|
137 DWORD dwAttrib; |
|
138 dwAttrib = GetFileAttributes(dirname); |
|
139 if (dwAttrib == -1) { |
|
140 return 0; |
|
141 } |
|
142 if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { |
|
143 return 1; |
|
144 } |
|
145 return 0; |
|
146 #else /* i.e. linux */ |
|
147 struct stat buf; |
|
148 if (stat(dirname, &buf) != 0) |
|
149 return 0; |
|
150 if (!S_ISDIR(buf.st_mode)) |
|
151 return 0; |
|
152 return 1; |
|
153 #endif |
|
154 } |
|
155 |
|
156 |
|
157 /* _IsLink: Is the given filename a symbolic link */ |
|
158 static int _IsLink(char *filename) |
|
159 { |
|
160 #ifdef WIN32 |
|
161 return 0; |
|
162 #else /* i.e. linux */ |
|
163 struct stat buf; |
|
164 if (lstat(filename, &buf) != 0) |
|
165 return 0; |
|
166 if (!S_ISLNK(buf.st_mode)) |
|
167 return 0; |
|
168 return 1; |
|
169 #endif |
|
170 } |
|
171 |
|
172 |
|
173 /* Is executable file |
|
174 * On Linux: check 'x' permission. On Windows: just check existence. |
|
175 */ |
|
176 static int _IsExecutableFile(char *filename) |
|
177 { |
|
178 #ifdef WIN32 |
|
179 return (int)PathFileExists(filename); |
|
180 #else /* i.e. linux */ |
|
181 struct stat buf; |
|
182 if (stat(filename, &buf) != 0) |
|
183 return 0; |
|
184 if (!S_ISREG(buf.st_mode)) |
|
185 return 0; |
|
186 if ((buf.st_mode & 0111) == 0) |
|
187 return 0; |
|
188 return 1; |
|
189 #endif /* WIN32 */ |
|
190 } |
|
191 |
|
192 |
|
193 /* _GetProgramPath: Determine the absolute path to the given program name. |
|
194 * |
|
195 * Takes into account the current working directory, etc. |
|
196 * The implementations require the global 'programName' to be set. |
|
197 */ |
|
198 #ifdef WIN32 |
|
199 static char* _GetProgramPath(void) |
|
200 { |
|
201 //XXX this is ugly but I didn't want to use malloc, no reason |
|
202 static char progPath[MAXPATHLEN+1]; |
|
203 // get absolute path to module |
|
204 if (!GetModuleFileName(NULL, progPath, MAXPATHLEN)) { |
|
205 _LogError("could not get absolute program name from "\ |
|
206 "GetModuleFileName\n"); |
|
207 exit(1); |
|
208 } |
|
209 // just need dirname |
|
210 for (char* p = progPath+strlen(progPath); |
|
211 *p != SEP && *p != ALTSEP; |
|
212 --p) |
|
213 { |
|
214 *p = '\0'; |
|
215 } |
|
216 *p = '\0'; // remove the trailing SEP as well |
|
217 |
|
218 return progPath; |
|
219 } |
|
220 #else |
|
221 |
|
222 /* _JoinPath requires that any buffer argument passed to it has at |
|
223 least MAXPATHLEN + 1 bytes allocated. If this requirement is met, |
|
224 it guarantees that it will never overflow the buffer. If stuff |
|
225 is too long, buffer will contain a truncated copy of stuff. |
|
226 */ |
|
227 static void |
|
228 _JoinPath(char *buffer, char *stuff) |
|
229 { |
|
230 size_t n, k; |
|
231 if (stuff[0] == SEP) |
|
232 n = 0; |
|
233 else { |
|
234 n = strlen(buffer); |
|
235 if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) |
|
236 buffer[n++] = SEP; |
|
237 } |
|
238 k = strlen(stuff); |
|
239 if (n + k > MAXPATHLEN) |
|
240 k = MAXPATHLEN - n; |
|
241 strncpy(buffer+n, stuff, k); |
|
242 buffer[n+k] = '\0'; |
|
243 } |
|
244 |
|
245 |
|
246 static char* |
|
247 _GetProgramPath(void) |
|
248 { |
|
249 /* XXX this routine does *no* error checking */ |
|
250 char* path = getenv("PATH"); |
|
251 static char progPath[MAXPATHLEN+1]; |
|
252 |
|
253 /* If there is no slash in the argv0 path, then we have to |
|
254 * assume the program is on the user's $PATH, since there's no |
|
255 * other way to find a directory to start the search from. If |
|
256 * $PATH isn't exported, you lose. |
|
257 */ |
|
258 if (strchr(programName, SEP)) { |
|
259 strncpy(progPath, programName, MAXPATHLEN); |
|
260 } |
|
261 else if (path) { |
|
262 int bufspace = MAXPATHLEN; |
|
263 while (1) { |
|
264 char *delim = strchr(path, DELIM); |
|
265 |
|
266 if (delim) { |
|
267 size_t len = delim - path; |
|
268 if (len > bufspace) { |
|
269 len = bufspace; |
|
270 } |
|
271 strncpy(progPath, path, len); |
|
272 *(progPath + len) = '\0'; |
|
273 bufspace -= len; |
|
274 } |
|
275 else { |
|
276 strncpy(progPath, path, bufspace); |
|
277 } |
|
278 |
|
279 _JoinPath(progPath, programName); |
|
280 if (_IsExecutableFile(progPath)) { |
|
281 break; |
|
282 } |
|
283 |
|
284 if (!delim) { |
|
285 progPath[0] = '\0'; |
|
286 break; |
|
287 } |
|
288 path = delim + 1; |
|
289 } |
|
290 } |
|
291 else { |
|
292 progPath[0] = '\0'; |
|
293 } |
|
294 |
|
295 // now we have to resolve a string of possible symlinks |
|
296 // - we'll just handle the simple case of a single level of |
|
297 // indirection |
|
298 // |
|
299 // XXX note this does not handle multiple levels of symlinks |
|
300 // here is pseudo-code for that (please implement it :): |
|
301 // while 1: |
|
302 // if islink(progPath): |
|
303 // linkText = readlink(progPath) |
|
304 // if isabsolute(linkText): |
|
305 // progPath = os.path.join(dirname(progPath), linkText) |
|
306 // else: |
|
307 // progPath = linkText |
|
308 // else: |
|
309 // break |
|
310 if (_IsLink(progPath)) { |
|
311 char newProgPath[MAXPATHLEN+1]; |
|
312 readlink(progPath, newProgPath, MAXPATHLEN); |
|
313 strncpy(progPath, newProgPath, MAXPATHLEN); |
|
314 } |
|
315 |
|
316 |
|
317 // prefix with the current working directory if the path is |
|
318 // relative to conform with the Windows version of this |
|
319 if (strlen(progPath) != 0 && progPath[0] != SEP) { |
|
320 char cwd[MAXPATHLEN+1]; |
|
321 char tmp[MAXPATHLEN+1]; |
|
322 //XXX should check for failure retvals |
|
323 getcwd(cwd, MAXPATHLEN); |
|
324 snprintf(tmp, MAXPATHLEN, "%s%c%s", cwd, SEP, progPath); |
|
325 strncpy(progPath, tmp, MAXPATHLEN); |
|
326 } |
|
327 |
|
328 // 'progPath' now contains the full path to the program *and* the program |
|
329 // name. The latter is not desire. |
|
330 char* pLetter = progPath + strlen(progPath); |
|
331 for (;pLetter != progPath && *pLetter != SEP; --pLetter) { |
|
332 /* do nothing */ |
|
333 } |
|
334 *pLetter = '\0'; |
|
335 |
|
336 return progPath; |
|
337 } |
|
338 #endif /* WIN32 */ |
|
339 |
|
340 |
|
341 //---- mainline |
|
342 |
|
343 int main(int argc, char** argv) |
|
344 { |
|
345 programName = argv[0]; |
|
346 programPath = _GetProgramPath(); |
|
347 |
|
348 // Determine the extension-less program basename. |
|
349 // XXX Will not always handle app names with '.' in them (other than |
|
350 // the '.' for the extension. |
|
351 char programNameNoExt[MAXPATHLEN+1]; |
|
352 char *pStart, *pEnd; |
|
353 pStart = pEnd = programName + strlen(programName) - 1; |
|
354 while (pStart != programName && *(pStart-1) != SEP) { |
|
355 pStart--; |
|
356 } |
|
357 while (1) { |
|
358 if (pEnd == pStart) { |
|
359 pEnd = programName + strlen(programName) - 1; |
|
360 break; |
|
361 } |
|
362 pEnd--; |
|
363 if (*(pEnd+1) == '.') { |
|
364 break; |
|
365 } |
|
366 } |
|
367 strncpy(programNameNoExt, pStart, pEnd-pStart+1); |
|
368 *(programNameNoExt+(pEnd-pStart+1)) = '\0'; |
|
369 |
|
370 // determine the full path to "<exename>.py" |
|
371 char pyFile[MAXPATHLEN+1]; |
|
372 snprintf(pyFile, MAXPATHLEN, "%s%c%s.py", programPath, SEP, |
|
373 programNameNoExt); |
|
374 |
|
375 // Build the argument array for launching. |
|
376 char* pythonArgs[MAX_PYTHON_ARGS+1]; |
|
377 int nPythonArgs = 0; |
|
378 pythonArgs[nPythonArgs++] = "python"; |
|
379 pythonArgs[nPythonArgs++] = "-tt"; |
|
380 pythonArgs[nPythonArgs++] = pyFile; |
|
381 for (int i = 1; i < argc; ++i) { |
|
382 pythonArgs[nPythonArgs++] = argv[i]; |
|
383 } |
|
384 pythonArgs[nPythonArgs++] = NULL; |
|
385 |
|
386 return _spawnvp(_P_WAIT, pythonArgs[0], pythonArgs); |
|
387 } |
|
388 |
|
389 |
|
390 //---- mainline for win32 subsystem:windows app |
|
391 #ifdef WIN32 |
|
392 int WINAPI WinMain( |
|
393 HINSTANCE hInstance, /* handle to current instance */ |
|
394 HINSTANCE hPrevInstance, /* handle to previous instance */ |
|
395 LPSTR lpCmdLine, /* pointer to command line */ |
|
396 int nCmdShow /* show state of window */ |
|
397 ) |
|
398 { |
|
399 return main(__argc, __argv); |
|
400 } |
|
401 #endif |
|
402 |