exewrapper.c
169 lines
| 4.2 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. | ||||
*/ | ||||
Matt Harbison
|
r41012 | #include <Python.h> | ||
Adrian Buehlmann
|
r17732 | #include <stdio.h> | ||
Matt Harbison
|
r40432 | #include <tchar.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); | ||||
} | ||||
Matt Harbison
|
r40432 | |||
#define _tcscpy_s strcpy_s | ||||
#define _tcscat_s strcat_s | ||||
Yuya Nishihara
|
r40452 | #define _countof(array) (sizeof(array) / sizeof(array[0])) | ||
Adrian Buehlmann
|
r17058 | #endif | ||
Matt Harbison
|
r40432 | static TCHAR pyscript[MAX_PATH + 10]; | ||
static TCHAR pyhome[MAX_PATH + 10]; | ||||
static TCHAR pydllfile[MAX_PATH + 10]; | ||||
Adrian Buehlmann
|
r17058 | |||
Matt Harbison
|
r40432 | int _tmain(int argc, TCHAR *argv[]) | ||
Adrian Buehlmann
|
r17058 | { | ||
Matt Harbison
|
r40432 | TCHAR *p; | ||
Adrian Buehlmann
|
r17058 | int ret; | ||
int i; | ||||
int n; | ||||
Matt Harbison
|
r40432 | TCHAR **pyargv; | ||
Adrian Buehlmann
|
r17058 | WIN32_FIND_DATA fdata; | ||
HANDLE hfind; | ||||
const char *err; | ||||
Adrian Buehlmann
|
r17732 | HMODULE pydll; | ||
Yuya Nishihara
|
r40452 | void(__cdecl * Py_SetPythonHome)(TCHAR * home); | ||
Matt Harbison
|
r40432 | int(__cdecl * Py_Main)(int argc, TCHAR *argv[]); | ||
Adrian Buehlmann
|
r17058 | |||
Matt Harbison
|
r41012 | #if PY_MAJOR_VERSION >= 3 | ||
Matt Harbison
|
r48096 | _wputenv(L"PYTHONLEGACYWINDOWSSTDIO=1"); | ||
Matt Harbison
|
r41012 | #endif | ||
Matt Harbison
|
r40432 | if (GetModuleFileName(NULL, pyscript, _countof(pyscript)) == 0) { | ||
Adrian Buehlmann
|
r17058 | err = "GetModuleFileName failed"; | ||
goto bail; | ||||
} | ||||
Matt Harbison
|
r40432 | p = _tcsrchr(pyscript, '.'); | ||
Adrian Buehlmann
|
r17732 | if (p == NULL) { | ||
Adrian Buehlmann
|
r17058 | err = "malformed module filename"; | ||
goto bail; | ||||
} | ||||
Adrian Buehlmann
|
r17732 | *p = 0; /* cut trailing ".exe" */ | ||
Matt Harbison
|
r40432 | _tcscpy_s(pyhome, _countof(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 */ | ||||
Matt Harbison
|
r40432 | _tcscat_s(pyscript, _countof(pyscript), _T("exe.py")); | ||
Adrian Buehlmann
|
r17058 | } | ||
Adrian Buehlmann
|
r17732 | pydll = NULL; | ||
Kostia Balytskyi
|
r31443 | |||
Matt Harbison
|
r40432 | p = _tcsrchr(pyhome, _T('\\')); | ||
Kostia Balytskyi
|
r31443 | if (p == NULL) { | ||
err = "can't find backslash in module filename"; | ||||
goto bail; | ||||
} | ||||
*p = 0; /* cut at directory */ | ||||
/* check for private Python of HackableMercurial */ | ||||
Matt Harbison
|
r40432 | _tcscat_s(pyhome, _countof(pyhome), _T("\\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); | ||||
Matt Harbison
|
r40432 | _tcscpy_s(pydllfile, _countof(pydllfile), pyhome); | ||
Yuya Nishihara
|
r40452 | _tcscat_s(pydllfile, _countof(pydllfile), | ||
_T("\\") _T(HGPYTHONLIB) _T(".dll")); | ||||
Kostia Balytskyi
|
r31443 | pydll = LoadLibrary(pydllfile); | ||
if (pydll == NULL) { | ||||
Yuya Nishihara
|
r40452 | 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) { | ||||
Matt Harbison
|
r40432 | pydll = LoadLibrary(_T(HGPYTHONLIB) _T(".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. | ||||
*/ | ||||
Matt Harbison
|
r40432 | if (argc >= 2 && _tcscmp(argv[1], pyscript) == 0) { | ||
Adrian Buehlmann
|
r17058 | /* | ||
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. | ||||
*/ | ||||
Matt Harbison
|
r40432 | pyargv = malloc((argc + 5) * sizeof(TCHAR *)); | ||
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; | ||||
} | ||||