Created
November 20, 2012 05:05
-
-
Save juntalis/4116150 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Needed for registry stuff | |
| #pragma comment(lib, "advapi32.lib") | |
| // For message boxes | |
| #pragma comment(lib, "user32.lib") | |
| // Use wchar_t | |
| #define _UNICODE 1 | |
| #define UNICODE _UNICODE | |
| // Speed up build process with minimal headers. | |
| #define WIN32_LEAN_AND_MEAN | |
| #define VC_EXTRALEAN | |
| #include <windows.h> | |
| #include <strsafe.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <process.h> | |
| #include <io.h> | |
| #ifndef MAX_PATH | |
| # define MAX_PATH _MAX_PATH | |
| #endif | |
| #ifndef bool | |
| # define bool int | |
| # define true 1 | |
| # define false 0 | |
| #endif | |
| #ifndef handle | |
| # define handle HANDLE | |
| #endif | |
| #ifndef inline | |
| # define inline __forceinline | |
| #endif | |
| #ifndef null | |
| # define null NULL | |
| #endif | |
| #ifndef dword | |
| # define dword DWORD | |
| #endif | |
| #ifndef DEBUG | |
| # ifdef _DEBUG | |
| # define DEBUG _DEBUG | |
| # endif | |
| #endif | |
| #ifndef MAX_ENV | |
| # define MAX_ENV _MAX_ENV | |
| #endif | |
| #ifndef MAX_EXE_NAME | |
| // I guess if the virtual env was rooted at the drive, you could manage a filename | |
| // this long. (Filepaths can be longer on NTFS, but I'm not about to start using | |
| // a size of 32,000 for statically declared file path buffers, and having to a | |
| // dynamically allocate every buffer intended for use as a path is a pain in the | |
| // ass. | |
| # define MAX_EXE_NAME MAX_PATH - 11 | |
| #endif | |
| #define EMPTY L"" | |
| // The two macros below were grabbed off the MSDN | |
| #define WIDEN2(x) L ## x | |
| #define WIDEN(x) WIDEN2(x) | |
| #define __WFUNCTION__ WIDEN(__FUNCTION__) | |
| static bool verbose_flag = false; | |
| /** Fatal error handlers. */ | |
| #define fatal_call() fatal(0L, null) | |
| #define fatal_api_call(f, ...) fatal(0L, f, __VA_ARGS__) | |
| static void fatal(dword dw, wchar_t* message, ...) | |
| { | |
| void *lpDisplayBuf, *lpMsgBuf; | |
| // If Message is null, use the name of our function. | |
| if(message == null) { | |
| message = __WFUNCTION__; | |
| } | |
| if(dw == 0) { | |
| // If no return code was specified, we assume that the message | |
| // contains a function name that failed. In that case, we retrieve | |
| // the system error message for the last-error code | |
| dw = GetLastError(); | |
| FormatMessage( | |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS, | |
| null, | |
| dw, | |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
| (wchar_t*) &lpMsgBuf, | |
| 0, | |
| null | |
| ); | |
| // Allocate our buffer for the error message. | |
| lpDisplayBuf = (void*)LocalAlloc( | |
| LMEM_ZEROINIT, | |
| (lstrlenW((const wchar_t*)lpMsgBuf) + lstrlenW((const wchar_t*)message) + 47) * sizeof(wchar_t) | |
| ); | |
| StringCchPrintfW( | |
| (wchar_t*)lpDisplayBuf, | |
| LocalSize(lpDisplayBuf) / sizeof(wchar_t), | |
| L"FATAL: %s failed with error %d: %s", | |
| message, | |
| dw, | |
| lpMsgBuf | |
| ); | |
| } else { | |
| // Otherwise, we assume that the error message is a format string. | |
| va_list args = null; | |
| // Allocate buffer for our resulting format string. | |
| lpMsgBuf = (void*)LocalAlloc( | |
| LMEM_ZEROINIT, | |
| (lstrlenW((const wchar_t*)message) + 8) * sizeof(wchar_t) | |
| ); | |
| StringCchPrintfW( | |
| (wchar_t*)lpMsgBuf, | |
| LocalSize(lpMsgBuf) / sizeof(wchar_t), | |
| L"FATAL: %s", | |
| message | |
| ); | |
| // Might as well use the maximum allowed buffer, since there's no way I know of the | |
| // get the size of the resulting buff. | |
| lpDisplayBuf = (void*)LocalAlloc(LMEM_ZEROINIT, STRSAFE_MAX_CCH * sizeof(wchar_t)); | |
| va_start(args, lpMsgBuf); | |
| StringCbVPrintfW( | |
| (wchar_t*)lpDisplayBuf, | |
| LocalSize(lpDisplayBuf) / sizeof(wchar_t), | |
| lpMsgBuf, | |
| args | |
| ); | |
| va_end(args); | |
| } | |
| MessageBoxW(GetConsoleWindow(), (const wchar_t*)lpDisplayBuf, L"Fatal Error", MB_OK); | |
| LocalFree(lpMsgBuf); | |
| LocalFree(lpDisplayBuf); | |
| ExitProcess(dw); | |
| } | |
| /** | |
| * Inline helper for allocating a wstring buffer, or throwing | |
| * a fatal error if unable to. Will also zero out the memory | |
| * once it allocates it. | |
| */ | |
| static inline wchar_t* walloc(size_t count) | |
| { | |
| wchar_t* result = null; | |
| size_t szresult = (count + 1) * sizeof(wchar_t); | |
| if(!(result = (wchar_t*)malloc(szresult))) { | |
| fatal_call(); | |
| } | |
| memset((void*)result, 0, szresult); | |
| return result; | |
| } | |
| /** | |
| * Inline helper for safely freeing allocated data. | |
| * Along with wafree, these functions were grabbed | |
| * from the cygspawn project: | |
| * https://github.com/mturk/cygspawn | |
| */ | |
| static inline void xfree(void *m) | |
| { | |
| if (m != null) free(m); | |
| } | |
| /** Helper for freeing a wchar string array. */ | |
| static void wafree(wchar_t **array) | |
| { | |
| wchar_t **ptr = array; | |
| if (array == null) return; | |
| while (*ptr != null) xfree((void*)*(ptr++)); | |
| xfree(array); | |
| } | |
| /** Simple inline helper to check if a path exists. */ | |
| static inline bool exists(wchar_t* path) | |
| { | |
| return _waccess((const wchar_t*)path, 00) != -1; | |
| } | |
| /** | |
| * Simple inline helper to check if a given path exists, and | |
| * represent a folder. | |
| */ | |
| static inline bool is_folder(wchar_t* path) | |
| { | |
| return GetFileAttributesW((const wchar_t*)path) & FILE_ATTRIBUTE_DIRECTORY; | |
| } | |
| /** | |
| * Simple inline helper to check if a given path exists, and | |
| * represent a file. | |
| */ | |
| static inline bool is_file(wchar_t* path) | |
| { | |
| return exists(path) && !is_folder(path); | |
| } | |
| /* Platform-specific Cygwin stuff */ | |
| #define CYGWIN_SUBKEY L"rootdir" | |
| #ifdef _M_X64 | |
| # pragma message("WARNING: Compiling the 64-bit version of this app removes the ability to use Cygwin to convert paths. It is therefore suggested that you only compile use the x86 tools.") | |
| # define CYWGIN_REGKEY L"SOFTWARE\\Wow6432Node\\Cygwin\\setup" | |
| #else | |
| # define CYGWIN_REGKEY L"SOFTWARE\\Cygwin\\setup" | |
| # define CYGWIN_DLL L"cygwin1.dll" | |
| #ifndef NOCONVERTENV | |
| /** Conversion tab entry */ | |
| typedef struct { const wchar_t *name; dword flags; } env_entry; | |
| /** | |
| * Some flag to help interpret the environment | |
| * variable tab. | |
| */ | |
| #define NONE 0x000L | |
| #define SPATH 0x001L // Single path | |
| #define LPATH 0x002L // Pathlist | |
| #define VROOT 0x004L // Set to the root folder of our virtual env. | |
| #define UNSET 0x008L // Unset if set | |
| /** | |
| * All environment variables that should be converted to | |
| * their cygwin-alternatives. (if present) | |
| * | |
| * Note: To be honest, aside from maybe HOME (and even that | |
| * only needs a change if you reference it in your script), | |
| * I didn't find that any of these needed converting in Python | |
| * 2.7. Still, since I don't know all the internal workings of | |
| * Python or Cygwin, and since I don't know how older versions | |
| * of Python would handle it, I'm going to leave this in for | |
| * the time being. | |
| */ | |
| static const env_entry vars_tab[] = { | |
| { L"PYTHONSTARTUP", SPATH }, | |
| { L"PYTHONPATH", LPATH }, | |
| { L"PYTHONHOME", UNSET }, | |
| { L"VIRTUAL_ENV", (SPATH | VROOT) }, | |
| { null, 0 } | |
| }; | |
| #endif | |
| typedef HMODULE* PHMODULE; // Thought this was already declared. | |
| /** Our base cygwin function type. */ | |
| typedef void(*cygwin_func)(); | |
| /** Function tab entry */ | |
| typedef struct { const char *name; cygwin_func proc; } cygwin_func_entry; | |
| /** Typedefs taken from Python for defining ssize_t on MSVC */ | |
| typedef _W64 int ssize_t; | |
| /** Possible 'what' values in calls to cygwin_conv_path/cygwin_create_path. */ | |
| enum { | |
| CCP_POSIX_TO_WIN_A = 0, /* from is char*, to is char* */ | |
| CCP_POSIX_TO_WIN_W, /* from is char*, to is wchar_t* */ | |
| CCP_WIN_A_TO_POSIX, /* from is char*, to is char* */ | |
| CCP_WIN_W_TO_POSIX, /* from is wchar_t*, to is char* */ | |
| /* Or these values to the above as needed. */ | |
| CCP_ABSOLUTE = 0, /* Request absolute path (default). */ | |
| CCP_RELATIVE = 0x100 /* Request to keep path relative. */ | |
| }; | |
| /** Used to describe the enum values above. */ | |
| typedef unsigned int cygwin_conv_path_t; | |
| /** Finally, our function tab. */ | |
| static cygwin_func_entry cygwin_funcs[] = { | |
| /** | |
| * If size is 0, cygwin_conv_path returns the required buffer size in bytes. | |
| * Otherwise, it returns 0 on success, or -1 on error and errno is set to | |
| * one of the below values: | |
| * | |
| * EINVAL what has an invalid value. | |
| * EFAULT from or to point into nirvana. | |
| * ENAMETOOLONG the resulting path is longer than 32K, or, in case | |
| * of what == CCP_POSIX_TO_WIN_A, longer than MAX_PATH. | |
| * ENOSPC size is less than required for the conversion. | |
| */ | |
| { "cygwin_conv_path", null }, | |
| # define cygwin_conv_path ((ssize_t(*)(cygwin_conv_path_t, const void*, void*, size_t))(cygwin_funcs[0].proc)) | |
| { "cygwin_conv_path_list", null }, | |
| # define cygwin_conv_path_list ((ssize_t(*)(cygwin_conv_path_t, const void*, void*, size_t))(cygwin_funcs[1].proc)) | |
| /** | |
| * Allocate a buffer for the conversion result using malloc(3), and return | |
| * a pointer to it. Returns null if something goes wrong with errno set | |
| * to one of the above values, or to ENOMEM if malloc fails. | |
| */ | |
| { "cygwin_create_path", null }, | |
| # define cygwin_create_path ((void*(*)(cygwin_conv_path_t, const void*))(cygwin_funcs[2].proc)) | |
| /** | |
| * For the purposes of freeing the buffer allocated in the previous function, | |
| * we're also going to grab cygwin's implementation of free, just to be safe. | |
| */ | |
| { "free", null }, | |
| # define cygwin_free ((void(*)(void*))(cygwin_funcs[3].proc)) | |
| /** | |
| * Left null to allow us to iterate and setup the last 4 functions at once. | |
| */ | |
| { null, null } | |
| }; | |
| /** Load our cygwin DLL or return false on failure. */ | |
| static bool load_cygwin_library(PHMODULE phCygwin) | |
| { | |
| if(!(*phCygwin = GetModuleHandleW(CYGWIN_DLL))) | |
| if(!(*phCygwin = LoadLibraryW(CYGWIN_DLL))) | |
| return false; | |
| return true; | |
| } | |
| /** Initialize the cygwin environment or false on failure. */ | |
| static bool init_cygwin_library(HMODULE hCygwin) | |
| { | |
| cygwin_func cygwin_dll_init = null; | |
| if((cygwin_dll_init = (cygwin_func)GetProcAddress(hCygwin,"cygwin_dll_init")) == null) { | |
| FreeLibrary(hCygwin); | |
| return false; | |
| } | |
| cygwin_dll_init(); | |
| return true; | |
| } | |
| /** | |
| * Load our cygwin DLL, initialize the cygwin environment, and | |
| * populate our function tab. | |
| */ | |
| static bool setup_cygwin(PHMODULE phCygwin) | |
| { | |
| int i = -1; | |
| // Load the cygwin dll into our application. | |
| if(!load_cygwin_library(phCygwin)) return false; | |
| // Init the cygwin environment. (Required) | |
| if(!init_cygwin_library(*phCygwin)) { | |
| FreeLibrary(*phCygwin); | |
| return false; | |
| } | |
| // Populate the remaining function tab. | |
| while(cygwin_funcs[++i].name != null) { | |
| if(!(cygwin_funcs[i].proc = (cygwin_func)GetProcAddress(*phCygwin, cygwin_funcs[i].name))) { | |
| FreeLibrary(*phCygwin); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** Does any necessary path format conversion and quoting for paths/path lists needing them. */ | |
| static wchar_t* fix_path_type(PHMODULE phCygwin, wchar_t* arg, bool islist) | |
| { | |
| const char* cygpath = null; | |
| wchar_t* result = null; | |
| size_t szcount = 0, szresult = 0; | |
| ssize_t(*conversion_func)(cygwin_conv_path_t, const void*, void*, size_t); | |
| // We'll use cygwin to allocate the buffer for the original value. | |
| if(!(cygpath = (const char*)cygwin_create_path(CCP_WIN_W_TO_POSIX, (const void*)arg))) { | |
| FreeLibrary(*phCygwin); | |
| *phCygwin = null; | |
| return null; | |
| } | |
| // Figure out which function to use. | |
| if(islist) { | |
| conversion_func = cygwin_conv_path_list; | |
| } else { | |
| conversion_func = cygwin_conv_path; | |
| } | |
| // Since cygwin allocated our buffer, we can assume it's big enough to hold the output. | |
| // For now, though, we'll just use MAX_ENV+1 | |
| if(conversion_func(CCP_WIN_W_TO_POSIX, (const void*)arg, (void*)cygpath, (MAX_ENV+1) * sizeof(char)) == -1) { | |
| cygwin_free((void*)cygpath); | |
| FreeLibrary(*phCygwin); | |
| *phCygwin = null; | |
| return null; | |
| } | |
| // Finally, allocate our result buffer. | |
| szcount = lstrlenA(cygpath) + 1; | |
| szresult = szcount * sizeof(wchar_t); | |
| if(!szresult || !(result = (wchar_t*)malloc(szresult))) { | |
| cygwin_free((void*)cygpath); | |
| FreeLibrary(*phCygwin); | |
| *phCygwin = null; | |
| return null; | |
| } | |
| // Zero out the allocated data. | |
| memset((void*)result, 0, szresult); | |
| // And convert the char string to our resulting wchar string. | |
| mbstowcs(result, cygpath, szcount); | |
| // At this point, we can free up the buffer cygwin created. | |
| cygwin_free((void*)cygpath); | |
| if(verbose_flag) { | |
| wprintf(L"Converted:\n %s\nto\n %s\n\n", arg, result); | |
| } | |
| return result; | |
| } | |
| #define fix_path(x, y) fix_path_type(x, y, false) | |
| #define fix_path_list(x, y) fix_path_type(x, y, true) | |
| static wchar_t* virtRootWin = NULL; | |
| #ifndef NOCONVERTENV | |
| /* Handles changes made to our environment variables */ | |
| static void fix_env(PHMODULE phCygwin) | |
| { | |
| int i = -1; | |
| wchar_t* virtRoot; | |
| if(verbose_flag) wprintf(L"Pre-converting virtual environment root, in case var found to be missing from environment.\n\n"); | |
| virtRoot = fix_path(phCygwin, virtRootWin); | |
| while(vars_tab[++i].name && *phCygwin) { | |
| wchar_t current[MAX_ENV+1] = EMPTY, *converted; | |
| // Get our environment variable. | |
| if(GetEnvironmentVariableW(vars_tab[i].name, current, MAX_ENV+1)) { | |
| if(verbose_flag) wprintf(L"Detected environment variable, %s.\n", vars_tab[i].name); | |
| // If it exists, check against the following flags.. | |
| if(vars_tab[i].flags & UNSET) { | |
| if(verbose_flag) wprintf(L"Unsetting..\n"); | |
| converted = null; | |
| } else if(vars_tab[i].flags & SPATH) { | |
| if(verbose_flag) wprintf(L"Attempting to convert path..\n"); | |
| converted = fix_path(phCygwin, current); | |
| } else if(vars_tab[i].flags & LPATH) { | |
| if(verbose_flag) wprintf(L"Attempting to convert path list..\n"); | |
| converted = fix_path_list(phCygwin, current); | |
| } | |
| // Set it to our new value. | |
| if(!SetEnvironmentVariableW(vars_tab[i].name, converted)) { | |
| fatal_api_call(L"SetEnvironmentVariableW"); | |
| } | |
| xfree(converted); | |
| } else { | |
| // Otherwise.. | |
| if(vars_tab[i].flags & VROOT) { | |
| if(verbose_flag) { | |
| wprintf(L"Detected lack of environment variable, %s\n", vars_tab[i].name); | |
| wprintf(L"Setting to converted virtual environment path:\n %s\n\n", virtRoot); | |
| } | |
| if(!SetEnvironmentVariableW(vars_tab[i].name, virtRoot)) { | |
| fatal_api_call(L"SetEnvironmentVariableW"); | |
| } | |
| } | |
| } | |
| } | |
| xfree(virtRoot); | |
| } | |
| #endif | |
| #endif | |
| /** Allocates, and adds quotes to an arg. */ | |
| static wchar_t* quote_arg(wchar_t* arg) | |
| { | |
| wchar_t* result = null; | |
| size_t szcount = 0; | |
| // Check for empty args. If found, don't both adding quotes. | |
| szcount = lstrlenW(arg); | |
| if(szcount == 0) { | |
| return walloc(0); | |
| } | |
| szcount += 2; | |
| result = walloc(szcount); | |
| szcount = (szcount + 1) * sizeof(wchar_t); | |
| // Now set result to the value of arg, with surrounding quotes. | |
| if(FAILED(StringCchPrintfW(result, szcount, L"\"%s\"", arg))) { | |
| fatal_call(); | |
| } | |
| return result; | |
| } | |
| /** | |
| * Iterates through our args, quoting all of them and in the cases of paths, | |
| * converting them to a cygwin-compatible format. | |
| */ | |
| static wchar_t** fix_argv(int argc, wchar_t** argv) | |
| { | |
| HMODULE hCygwin; | |
| int i; | |
| bool useCygwin; | |
| wchar_t** result = null; | |
| size_t szresult; | |
| szresult = (argc + 1) * sizeof(wchar_t*); | |
| #ifdef _M_X64 | |
| // Not going to get hit by x64 anyways. | |
| # define fix_path_arg(x,y) quote_arg(y) | |
| useCygwin = false; | |
| #else | |
| useCygwin = setup_cygwin(&hCygwin); | |
| #endif | |
| // First allocate our array of arguments. | |
| if(!(result = (wchar_t**)malloc(szresult))) { | |
| fatal(ERROR_NOT_ENOUGH_MEMORY, L"Could not allocate our args buffer."); | |
| } | |
| // Zero out the allocated data. | |
| memset((void*)result, 0, szresult); | |
| result[0] = _wcsdup(argv[0]); | |
| if(result[0] == null) { | |
| fatal_api_call(L"wcsdup"); | |
| } | |
| // Now, begin the fixes. | |
| for(i = 1; i < argc; i++) { | |
| wchar_t* check = NULL; | |
| if(useCygwin && *(argv[i]) && is_file(argv[i])) { | |
| if(verbose_flag) wprintf(L"Detected path argument, %s. Attempting to convert..\n", argv[i]); | |
| if(check = fix_path(&hCygwin, argv[i])) { | |
| argv[i] = check; | |
| } else { | |
| useCygwin = false; | |
| } | |
| } | |
| result[i] = quote_arg(argv[i]); | |
| if(useCygwin && check) { | |
| xfree(check); | |
| } | |
| } | |
| if(useCygwin) { | |
| #ifndef NOCONVERTENV | |
| fix_env(&hCygwin); | |
| #endif | |
| FreeLibrary(hCygwin); | |
| } | |
| return result; | |
| } | |
| static int exec_cmd(wchar_t* cmd, int argc, wchar_t** argv) | |
| { | |
| int r; | |
| if(argc > 1) { | |
| // Fuck, now we actually have to load cygwin to convert the paths from Windows -> Cygwin format. | |
| // Though, if we're on a x64 platform, we can't do that, so we'll just quote our args and hope | |
| // for the best. | |
| wchar_t** args = fix_argv(argc, argv); | |
| r = (int)_wspawnvp(_P_WAIT, cmd, (const wchar_t* const*)args); | |
| wafree(args); | |
| } else { | |
| r = (int)_wspawnlp(_P_WAIT, cmd, cmd, null); | |
| } | |
| return r; | |
| } | |
| /** Inline helper to get the parent folder and length of a the parent quickly */ | |
| static inline bool get_dirname(wchar_t* sExecutable, size_t lpszExecutable, wchar_t* sParentOut, size_t* lpszParentOut) | |
| { | |
| if(!lstrcpynW(sParentOut, sExecutable, (int)lpszExecutable)) return false; | |
| *lpszParentOut = lpszExecutable; | |
| while(sParentOut[--*lpszParentOut] != L'\\' && *lpszParentOut) sParentOut[*lpszParentOut] = L'\0'; | |
| // Remove the trailing new line. | |
| if(*lpszParentOut) sParentOut[*lpszParentOut] = L'\0'; | |
| return (*lpszParentOut > 0) && is_folder(sParentOut); | |
| } | |
| /** | |
| * Determine the root folder of our cygwin installation by | |
| * reading the value of the registry key found at: | |
| * HKEY_LOCAL_MACHINE\SOFTWARE\Cygwin\setup | |
| * or if compiled as a 64-bit program, | |
| * HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Cygwin\setup | |
| */ | |
| static void get_cygwin_root(wchar_t* sBuffer, dword szBuffer) | |
| { | |
| HKEY hkeyCygSetup; | |
| dword dwValType; | |
| long lRet; | |
| if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, CYGWIN_REGKEY, 0L, KEY_READ , &hkeyCygSetup) != ERROR_SUCCESS) { | |
| fatal_api_call(L"RegOpenKeyExW"); | |
| } | |
| lRet = RegQueryValueExW(hkeyCygSetup, CYGWIN_SUBKEY, null, &dwValType, (LPBYTE)sBuffer, &szBuffer); | |
| if(lRet != ERROR_SUCCESS) { | |
| if(lRet == ERROR_MORE_DATA) { | |
| fatal(ERROR_MORE_DATA, L"Our buffer was not large enough to contain the value found at the registry key: HKEY_LOCAL_MACHINE\\" CYGWIN_SUBKEY); | |
| } else { | |
| fatal_api_call(L"RegQueryValueExW"); | |
| } | |
| } | |
| if(dwValType != REG_SZ && dwValType != REG_EXPAND_SZ) { | |
| fatal(2, L"Found a non-string value at the following registry key: HKEY_LOCAL_MACHINE\\" CYGWIN_SUBKEY); | |
| } | |
| if(RegCloseKey(hkeyCygSetup) != ERROR_SUCCESS) { | |
| fatal_api_call(L"RegCloseKey"); | |
| } | |
| if(!is_folder(sBuffer)) { | |
| fatal(ERROR_PATH_NOT_FOUND, L"Did not find an existing folder at %s", sBuffer); | |
| } | |
| } | |
| int wmain(int argc, wchar_t* argv[]) | |
| { | |
| wchar_t sParentDir[MAX_PATH+1] = EMPTY, | |
| sExecutable[MAX_PATH+1] = EMPTY, | |
| sTarget[MAX_PATH+1] = EMPTY, | |
| // MAX_ENV + 1 for trailing 0 | |
| sPATH[MAX_ENV+1] = EMPTY, | |
| sCygRoot[MAX_PATH+1] = EMPTY, | |
| sSystemDir[MAX_PATH+1] = EMPTY, | |
| sWinDir[MAX_PATH+1] = EMPTY, | |
| *sExecutableName; | |
| int i = 0; | |
| size_t szPath = 0, szParent = 0, szName = 0; | |
| /* First, get the path to our real executable */ | |
| // Get our module filepath. | |
| if(!(szPath = GetModuleFileNameW(null, sExecutable, MAX_PATH))) { | |
| fatal_api_call(L"GetModuleFileNameW"); | |
| } | |
| // Get our parent folder | |
| if(!get_dirname(sExecutable, szPath, sParentDir, &szParent)) { | |
| fatal(ERROR_PATH_NOT_FOUND, L"Could not get the parent folder of our executable."); | |
| } | |
| // Get a string containing just the last part of the module path (ex: \python.exe) | |
| sExecutableName = ((wchar_t*)&(sExecutable[szParent])); | |
| // We still need to get the bin folder path, so we'll continue working. | |
| if(!get_dirname(sParentDir, szParent, sParentDir, &szParent)) { | |
| fatal(ERROR_PATH_NOT_FOUND, L"Could not get the root folder of our virtual environment."); | |
| } | |
| // Possibly needed later. | |
| virtRootWin = sParentDir; | |
| // Finally, append bin\exename to the root virtualenv folder. | |
| if(FAILED(StringCchPrintfW(sTarget, MAX_PATH+1, L"%s\\bin%s", sParentDir, sExecutableName))) { | |
| fatal_api_call(L"StringCchPrintfW (Building path to real executable)"); | |
| } | |
| // Make sure that our real executable exists. | |
| if(FAILED(sTarget)) { | |
| fatal(ERROR_FILE_NOT_FOUND, L"Did not find an existing file at %s", sTarget); | |
| } | |
| /* Now that we have the real path, set up our environment variables. */ | |
| // First, find our cygwin root folder | |
| get_cygwin_root(sCygRoot, MAX_PATH + 1); | |
| // And then our system directory. | |
| if(!(szPath = (size_t)GetSystemDirectoryW(sSystemDir, MAX_PATH))) { | |
| fatal_api_call(L"GetSystemDirectoryW"); | |
| } | |
| // Get our Windows folder (parent folder of our system folder) | |
| if(!get_dirname(sSystemDir, szPath, sWinDir, &szParent)) { | |
| fatal(1, L"Could not get our Windows directory."); | |
| } | |
| // Build our PATH variable. | |
| if(FAILED(StringCchPrintfW( | |
| sPATH, | |
| MAX_ENV+1, | |
| // PATH=DirOfExe;Cygwin\bin;Cygwin\usr\bin;Cygwin\usr\local\bin;WindowsDir;SystemDir | |
| L"%s\\bin;%s\\bin;%s\\usr\\bin;%s\\usr\\local\\bin;%s;%s", | |
| sParentDir, | |
| sCygRoot, sCygRoot, sCygRoot, | |
| sWinDir, | |
| sSystemDir | |
| ))) { | |
| fatal_api_call(L"StringCchPrintfW (Building PATH variable)"); | |
| } | |
| // Handle the verbose flag by adding in our own debug output. | |
| while(++i < argc && argv[i] && argv[i][0] == L'-') { | |
| if(argv[i][1] == 'v') { | |
| wprintf(L"Detected verbose flag. Enabling debug output.\n\n"); | |
| verbose_flag = true; | |
| break; | |
| } | |
| } | |
| if(verbose_flag) { | |
| wprintf(L"Forwarding to:\n %s\n\n", sTarget); | |
| wprintf(L"Setting PATH env var to:\n %s\n\n", sPATH); | |
| } | |
| if(!SetEnvironmentVariableW(L"PATH", sPATH)) { | |
| fatal_api_call(L"SetEnvironmentVariableW"); | |
| } | |
| argv[0] = sTarget; | |
| return exec_cmd(sTarget, argc, argv); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment