exewrapper.c
159 lines
| 3.9 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; | ||
Augie Fackler
|
r34637 | void(__cdecl * Py_SetPythonHome)(char *home); | ||
int(__cdecl * Py_Main)(int argc, char *argv[]); | ||||
Adrian Buehlmann
|
r17058 | |||
Augie Fackler
|
r34637 | if (GetModuleFileName(NULL, pyscript, sizeof(pyscript)) == 0) { | ||
Adrian Buehlmann
|
r17058 | 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; | ||
Kostia Balytskyi
|
r31443 | |||
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"); | ||||
Adrian Buehlmann
|
r17732 | |||
Kostia Balytskyi
|
r31443 | hfind = FindFirstFile(pyhome, &fdata); | ||
if (hfind != INVALID_HANDLE_VALUE) { | ||||
/* Path .\hg-python exists. We are probably in HackableMercurial | ||||
scenario, so let's load python dll from this dir. */ | ||||
FindClose(hfind); | ||||
strcpy_s(pydllfile, sizeof(pydllfile), pyhome); | ||||
strcat_s(pydllfile, sizeof(pydllfile), "\\" HGPYTHONLIB ".dll"); | ||||
pydll = LoadLibrary(pydllfile); | ||||
if (pydll == NULL) { | ||||
Augie Fackler
|
r34637 | err = "failed to load private Python DLL " HGPYTHONLIB | ||
".dll"; | ||||
Adrian Buehlmann
|
r17732 | goto bail; | ||
} | ||||
Augie Fackler
|
r34637 | Py_SetPythonHome = | ||
(void *)GetProcAddress(pydll, "Py_SetPythonHome"); | ||||
Kostia Balytskyi
|
r31443 | if (Py_SetPythonHome == NULL) { | ||
err = "failed to get Py_SetPythonHome"; | ||||
goto bail; | ||||
Adrian Buehlmann
|
r17732 | } | ||
Kostia Balytskyi
|
r31443 | Py_SetPythonHome(pyhome); | ||
Adrian Buehlmann
|
r17732 | } | ||
if (pydll == NULL) { | ||||
Gregory Szorc
|
r29019 | pydll = LoadLibrary(HGPYTHONLIB ".dll"); | ||
Adrian Buehlmann
|
r17732 | if (pydll == NULL) { | ||
Adrian Buehlmann
|
r26662 | err = "failed to load Python DLL " HGPYTHONLIB ".dll"; | ||
Adrian Buehlmann
|
r17732 | goto bail; | ||
} | ||||
} | ||||
Augie Fackler
|
r34637 | Py_Main = (void *)GetProcAddress(pydll, "Py_Main"); | ||
Adrian Buehlmann
|
r17732 | 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. | ||||
*/ | ||||
Augie Fackler
|
r34637 | pyargv = malloc((argc + 5) * sizeof(char *)); | ||
Adrian Buehlmann
|
r17058 | 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; | ||||
} | ||||