exewrapper.c
174 lines
| 4.4 KiB
| text/x-c
|
CLexer
/ mercurial / exewrapper.c
Adrian Buehlmann
|
r17058 | /* | ||
exewrapper.c - wrapper for calling a python script on Windows | ||||
Copyright 2012 Adrian Buehlmann <adrian@cadifra.com> and others | ||||
This software may be used and distributed according to the terms of the | ||||
GNU General Public License version 2 or any later version. | ||||
*/ | ||||
Adrian Buehlmann
|
r17732 | #include <stdio.h> | ||
Adrian Buehlmann
|
r17058 | #include <windows.h> | ||
Adrian Buehlmann
|
r17732 | #include "hgpythonlib.h" | ||
Adrian Buehlmann
|
r17058 | |||
#ifdef __GNUC__ | ||||
int strcat_s(char *d, size_t n, const char *s) | ||||
{ | ||||
return !strncat(d, s, n); | ||||
} | ||||
Adrian Buehlmann
|
r17732 | int strcpy_s(char *d, size_t n, const char *s) | ||
{ | ||||
return !strncpy(d, s, n); | ||||
} | ||||
Adrian Buehlmann
|
r17058 | #endif | ||
static char pyscript[MAX_PATH + 10]; | ||||
Adrian Buehlmann
|
r17732 | static char pyhome[MAX_PATH + 10]; | ||
static char envpyhome[MAX_PATH + 10]; | ||||
static char pydllfile[MAX_PATH + 10]; | ||||
Adrian Buehlmann
|
r17058 | |||
int main(int argc, char *argv[]) | ||||
{ | ||||
Adrian Buehlmann
|
r17732 | char *p; | ||
Adrian Buehlmann
|
r17058 | int ret; | ||
int i; | ||||
int n; | ||||
char **pyargv; | ||||
WIN32_FIND_DATA fdata; | ||||
HANDLE hfind; | ||||
const char *err; | ||||
Adrian Buehlmann
|
r17732 | HMODULE pydll; | ||
void (__cdecl *Py_SetPythonHome)(char *home); | ||||
int (__cdecl *Py_Main)(int argc, char *argv[]); | ||||
Adrian Buehlmann
|
r17058 | |||
if (GetModuleFileName(NULL, pyscript, sizeof(pyscript)) == 0) | ||||
{ | ||||
err = "GetModuleFileName failed"; | ||||
goto bail; | ||||
} | ||||
Adrian Buehlmann
|
r17732 | p = strrchr(pyscript, '.'); | ||
if (p == NULL) { | ||||
Adrian Buehlmann
|
r17058 | err = "malformed module filename"; | ||
goto bail; | ||||
} | ||||
Adrian Buehlmann
|
r17732 | *p = 0; /* cut trailing ".exe" */ | ||
strcpy_s(pyhome, sizeof(pyhome), pyscript); | ||||
Adrian Buehlmann
|
r17058 | |||
hfind = FindFirstFile(pyscript, &fdata); | ||||
if (hfind != INVALID_HANDLE_VALUE) { | ||||
/* pyscript exists, close handle */ | ||||
FindClose(hfind); | ||||
} else { | ||||
/* file pyscript isn't there, take <pyscript>exe.py */ | ||||
strcat_s(pyscript, sizeof(pyscript), "exe.py"); | ||||
} | ||||
Adrian Buehlmann
|
r17732 | pydll = NULL; | ||
Adrian Buehlmann
|
r26664 | /* | ||
We first check, that environment variable PYTHONHOME is *not* set. | ||||
This just mimicks the behavior of the regular python.exe, which uses | ||||
PYTHONHOME to find its installation directory (if it has been set). | ||||
Note: Users of HackableMercurial are expected to *not* set PYTHONHOME! | ||||
*/ | ||||
Adrian Buehlmann
|
r17732 | if (GetEnvironmentVariable("PYTHONHOME", envpyhome, | ||
sizeof(envpyhome)) == 0) | ||||
{ | ||||
Adrian Buehlmann
|
r26664 | /* | ||
Environment var PYTHONHOME is *not* set. Let's see if we are | ||||
running inside a HackableMercurial. | ||||
*/ | ||||
Adrian Buehlmann
|
r17732 | |||
p = strrchr(pyhome, '\\'); | ||||
if (p == NULL) { | ||||
err = "can't find backslash in module filename"; | ||||
goto bail; | ||||
} | ||||
*p = 0; /* cut at directory */ | ||||
/* check for private Python of HackableMercurial */ | ||||
strcat_s(pyhome, sizeof(pyhome), "\\hg-python"); | ||||
hfind = FindFirstFile(pyhome, &fdata); | ||||
if (hfind != INVALID_HANDLE_VALUE) { | ||||
/* path pyhome exists, let's use it */ | ||||
FindClose(hfind); | ||||
strcpy_s(pydllfile, sizeof(pydllfile), pyhome); | ||||
strcat_s(pydllfile, sizeof(pydllfile), "\\" HGPYTHONLIB); | ||||
pydll = LoadLibrary(pydllfile); | ||||
if (pydll == NULL) { | ||||
Adrian Buehlmann
|
r26663 | err = "failed to load private Python DLL " | ||
HGPYTHONLIB ".dll"; | ||||
Adrian Buehlmann
|
r17732 | goto bail; | ||
} | ||||
Py_SetPythonHome = (void*)GetProcAddress(pydll, | ||||
"Py_SetPythonHome"); | ||||
if (Py_SetPythonHome == NULL) { | ||||
err = "failed to get Py_SetPythonHome"; | ||||
goto bail; | ||||
} | ||||
Py_SetPythonHome(pyhome); | ||||
} | ||||
} | ||||
if (pydll == NULL) { | ||||
pydll = LoadLibrary(HGPYTHONLIB); | ||||
if (pydll == NULL) { | ||||
Adrian Buehlmann
|
r26662 | err = "failed to load Python DLL " HGPYTHONLIB ".dll"; | ||
Adrian Buehlmann
|
r17732 | goto bail; | ||
} | ||||
} | ||||
Py_Main = (void*)GetProcAddress(pydll, "Py_Main"); | ||||
if (Py_Main == NULL) { | ||||
err = "failed to get Py_Main"; | ||||
goto bail; | ||||
} | ||||
Adrian Buehlmann
|
r17058 | /* | ||
Only add the pyscript to the args, if it's not already there. It may | ||||
Adrian Buehlmann
|
r17063 | already be there, if the script spawned a child process of itself, in | ||
Adrian Buehlmann
|
r17058 | the same way as it got called, that is, with the pyscript already in | ||
place. So we optionally accept the pyscript as the first argument | ||||
(argv[1]), letting our exe taking the role of the python interpreter. | ||||
*/ | ||||
if (argc >= 2 && strcmp(argv[1], pyscript) == 0) { | ||||
/* | ||||
pyscript is already in the args, so there is no need to copy | ||||
the args and we can directly call the python interpreter with | ||||
the original args. | ||||
*/ | ||||
return Py_Main(argc, argv); | ||||
} | ||||
/* | ||||
Start assembling the args for the Python interpreter call. We put the | ||||
name of our exe (argv[0]) in the position where the python.exe | ||||
canonically is, and insert the pyscript next. | ||||
*/ | ||||
pyargv = malloc((argc + 5) * sizeof(char*)); | ||||
if (pyargv == NULL) { | ||||
err = "not enough memory"; | ||||
goto bail; | ||||
} | ||||
n = 0; | ||||
pyargv[n++] = argv[0]; | ||||
pyargv[n++] = pyscript; | ||||
/* copy remaining args from the command line */ | ||||
for (i = 1; i < argc; i++) | ||||
pyargv[n++] = argv[i]; | ||||
/* argv[argc] is guaranteed to be NULL, so we forward that guarantee */ | ||||
pyargv[n] = NULL; | ||||
ret = Py_Main(n, pyargv); /* The Python interpreter call */ | ||||
free(pyargv); | ||||
return ret; | ||||
bail: | ||||
fprintf(stderr, "abort: %s\n", err); | ||||
return 255; | ||||
} | ||||