diff options
author | PJ Eby <distutils-sig@python.org> | 2006-12-29 17:43:39 +0000 |
---|---|---|
committer | PJ Eby <distutils-sig@python.org> | 2006-12-29 17:43:39 +0000 |
commit | 39063f8ef647375681b542376070d98347705582 (patch) | |
tree | b29cd7ff9baa69216dcc963f900b8134ebef60ce /launcher.c | |
parent | 2d14d749076f993ff90c91791086796f231c3370 (diff) | |
download | python-setuptools-bitbucket-39063f8ef647375681b542376070d98347705582.tar.gz |
Overhauled Windows script wrapping to support ``bdist_wininst`` better.
Scripts installed with ``bdist_wininst`` will always use ``#!python.exe`` or
``#!pythonw.exe`` as the executable name (even when built on non-Windows
platforms!), and the wrappers will look for the executable in the script's
parent directory (which should find the right version of Python).
(backport from trunk)
Diffstat (limited to 'launcher.c')
-rwxr-xr-x | launcher.c | 187 |
1 files changed, 121 insertions, 66 deletions
@@ -22,6 +22,7 @@ starting. So, we have to use spawnv() and wait for Python to exit before continuing. :( */ + #include <stdlib.h> #include <stdio.h> #include <unistd.h> @@ -29,20 +30,25 @@ #include "windows.h" int fail(char *format, char *data) { - /* Print error message to stderr and return 1 */ + /* Print error message to stderr and return 2 */ fprintf(stderr, format, data); return 2; } + + + + char *quoted(char *data) { - int i, l = strlen(data), nb; + int i, ln = strlen(data), nb; + /* We allocate twice as much space as needed to deal with worse-case of having to escape everything. */ - char *result = calloc(l*2+3, sizeof(char)); + char *result = calloc(ln*2+3, sizeof(char)); char *presult = result; *presult++ = '"'; - for (nb=0, i=0; i < l; i++) + for (nb=0, i=0; i < ln; i++) { if (data[i] == '\\') nb += 1; @@ -56,6 +62,7 @@ char *quoted(char *data) { nb = 0; *presult++ = data[i]; } + for (; nb > 0; nb--) /* Deal w trailing slashes */ *presult++ = '\\'; @@ -64,49 +71,108 @@ char *quoted(char *data) { return result; } -char *getpyopt(char *python) -{ - /* Search a Python command string, read from a #! line for an - option. An option must be separated from an executable name by - one or more spaces. An option consistes of a hyphen followed by - one or more letters. - */ - static char *letters = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - ; - char *p = python + strlen(python) - 1; - if (strchr(letters, *p) == NULL) - return NULL; /* Path doen't end with a letter. Odd. */ - while (p > python && strchr(letters, *p) != NULL) - p--; - if (p == python || *p != '-') - return NULL; /* Can't be an option */ - p--; - if (p > python && isspace(*p)) - { /* BINGO, we have an option */ - char *pyopt = p+1; - /* strip trailing spaces from remainder of python command */ - while (p > python && isspace(*p)) - *p-- = '\0'; - return pyopt; + + + + + + + + + +char *loadable_exe(char *exename) { + HINSTANCE hPython; /* DLL handle for python executable */ + char *result; + + hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hPython) return NULL; + + /* Return the absolute filename for spawnv */ + result = calloc(MAX_PATH, sizeof(char)); + if (result) GetModuleFileName(hPython, result, MAX_PATH); + + FreeLibrary(hPython); + return result; +} + + +char *find_exe(char *exename, char *script) { + char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT]; + char path[_MAX_PATH], c, *result; + + /* convert slashes to backslashes for uniform search below */ + result = exename; + while (c = *result++) if (c=='/') result[-1] = '\\'; + + _splitpath(exename, drive, dir, fname, ext); + if (drive[0] || dir[0]=='\\') { + return loadable_exe(exename); /* absolute path, use directly */ } - else - return NULL; + /* Use the script's parent directory, which should be the Python home + (This should only be used for bdist_wininst-installed scripts, because + easy_install-ed scripts use the absolute path to python[w].exe + */ + _splitpath(script, drive, dir, fname, ext); + result = dir + strlen(dir) -1; + if (*result == '\\') result--; + while (*result != '\\' && result>=dir) *result-- = 0; + _makepath(path, drive, dir, exename, NULL); + return loadable_exe(path); } + +char **parse_argv(char *cmdline, int *argc) +{ + /* Parse a command line in-place using MS C rules */ + + char **result = calloc(strlen(cmdline), sizeof(char *)); + char *output = cmdline; + char c; + int nb = 0; + *argc = 0; + + result[0] = output; + while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ + + do { + c = *cmdline++; + if (!c || isspace(c)) { + while (nb) {*output++ = '\\'; nb--; } + *output++ = 0; + result[++*argc] = output; + if (!c) return result; + while (isspace(*cmdline)) cmdline++; /* skip leading spaces */ + continue; + } + if (c == '\\') + ++nb; /* count \'s */ + else { + if (c == '"') { + if (!(nb & 1)) c = 0; /* skip " unless odd # of \ */ + nb = nb >> 1; /* cut \'s in half */ + } + while (nb) {*output++ = '\\'; nb--; } + if (c) *output++ = c; + } + } while (1); +} + + + + + + int run(int argc, char **argv, int is_gui) { char python[256]; /* python executable's filename*/ char *pyopt; /* Python option */ char script[256]; /* the script's filename */ - HINSTANCE hPython; /* DLL handle for python executable */ int scriptf; /* file descriptor for script file */ - char **newargs, **newargsp; /* argument array for exec */ + char **newargs, **newargsp, **parsedargs; /* argument array for exec */ char *ptr, *end; /* working pointers for string manipulation */ - int i; /* loop counter */ + int i, parsedargc; /* loop counter */ /* compute script name from our .exe name*/ GetModuleFileName(NULL, script, sizeof(script)); @@ -126,57 +192,51 @@ int run(int argc, char **argv, int is_gui) { close(scriptf); ptr = python-1; - while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') { - if (*ptr=='/') - *ptr='\\'; /* convert slashes to avoid LoadLibrary crashes... */ - } + while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;} *ptr-- = '\0'; - while (ptr>python && isspace(*ptr)) *ptr-- = '\0'; /* strip trailing sp */ + if (strncmp(python, "#!", 2)) { /* default to python.exe if no #! header */ strcpy(python, "#!python.exe"); } - /* Check for Python options */ - pyopt = getpyopt(python); - - /* At this point, the python buffer contains "#!pythonfilename" */ + parsedargs = parse_argv(python+2, &parsedargc); /* Using spawnv() can fail strangely if you e.g. find the Cygwin Python, so we'll make sure Windows can find and load it */ - hPython = LoadLibraryEx(python+2, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (!hPython) { - return fail("Cannot find Python executable %s\n", python+2); + + ptr = find_exe(parsedargs[0], script); + if (!ptr) { + return fail("Cannot find Python executable %s\n", parsedargs[0]); } - /* And we'll use the absolute filename for spawnv */ - GetModuleFileName(hPython, python, sizeof(python)); + /* printf("Python executable: %s\n", ptr); */ - /* printf("Python executable: %s\n", python); */ + /* Argument array needs to be + parsedargc + argc, plus 1 for null sentinel */ - /* Argument array needs to be - argc+1 for python executable, - plus 1 for possible python opts, - plus 1 for null sentinel */ - newargs = (char **)calloc(argc+3, sizeof(char *)); + newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *)); newargsp = newargs; - *newargsp++ = quoted(python); - if (pyopt) - *newargsp++ = pyopt; + + *newargsp++ = quoted(ptr); + for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]); + *newargsp++ = quoted(script); - for (i = 1; i < argc; i++) - *newargsp++ = quoted(argv[i]); + for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]); + *newargsp++ = NULL; /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */ + if (is_gui) { /* Use exec, we don't need to wait for the GUI to finish */ execv(python, (const char * const *)(newargs)); return fail("Could not exec %s", python); /* shouldn't get here! */ } + /* We *do* need to wait for a CLI to finish, so use spawn */ - return spawnv(P_WAIT, python, (const char * const *)(newargs)); + return spawnv(P_WAIT, ptr, (const char * const *)(newargs)); } @@ -184,8 +244,3 @@ int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) { return run(__argc, __argv, GUI); } - - - - - |