1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/which/launcher.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,402 @@ 1.4 +/* 1.5 + * Copyright (c) 2002-2003 ActiveState Corp. 1.6 + * Author: Trent Mick (TrentM@ActiveState.com) 1.7 + * 1.8 + * Permission is hereby granted, free of charge, to any person obtaining a 1.9 + * copy of this software and associated documentation files (the 1.10 + * "Software"), to deal in the Software without restriction, including 1.11 + * without limitation the rights to use, copy, modify, merge, publish, 1.12 + * distribute, sublicense, and/or sell copies of the Software, and to 1.13 + * permit persons to whom the Software is furnished to do so, subject to 1.14 + * the following conditions: 1.15 + * 1.16 + * The above copyright notice and this permission notice shall be included 1.17 + * in all copies or substantial portions of the Software. 1.18 + * 1.19 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1.20 + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1.21 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1.22 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 1.23 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 1.24 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 1.25 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1.26 + */ 1.27 + 1.28 +/* Console launch executable. 1.29 + * 1.30 + * This program exists solely to launch: 1.31 + * python <installdir>/<exename>.py <argv> 1.32 + * on Windows. "<exename>.py" must be in the same directory. 1.33 + * 1.34 + * Rationale: 1.35 + * - On some Windows flavours .py *can* be put on the PATHEXT to be 1.36 + * able to find "<exename>.py" if it is on the PATH. This is fine 1.37 + * until you need shell redirection to work. It does NOT for 1.38 + * extensions to PATHEXT. Redirection *does* work for "python 1.39 + * <script>.py" so we will try to do that. 1.40 + */ 1.41 + 1.42 +#ifdef WIN32 1.43 + #include <windows.h> 1.44 + #include <process.h> 1.45 + #include <direct.h> 1.46 + #include <shlwapi.h> 1.47 +#else /* linux */ 1.48 + #include <unistd.h> 1.49 +#endif /* WIN32 */ 1.50 +#include <sys/stat.h> 1.51 +#include <errno.h> 1.52 +#include <stdio.h> 1.53 +#include <stdlib.h> 1.54 +#include <string.h> 1.55 +#include <stdarg.h> 1.56 + 1.57 +//---- constants 1.58 + 1.59 +#define BUF_LENGTH 2048 1.60 +#define MAX_PYTHON_ARGS 50 1.61 +#define MAX_FILES 50 1.62 +#define MAXPATHLEN 1024 1.63 +#ifdef WIN32 1.64 + #define SEP '\\' 1.65 + #define ALTSEP '/' 1.66 + // path list element separator 1.67 + #define DELIM ';' 1.68 +#else /* linux */ 1.69 + #define SEP '/' 1.70 + // path list element separator 1.71 + #define DELIM ':' 1.72 +#endif 1.73 + 1.74 +#ifdef WIN32 1.75 + #define spawnvp _spawnvp 1.76 + #define snprintf _snprintf 1.77 + #define vsnprintf _vsnprintf 1.78 + //NOTE: this is for the stat *call* and the stat *struct* 1.79 + #define stat _stat 1.80 +#endif 1.81 + 1.82 + 1.83 +//---- globals 1.84 + 1.85 +char* programName = NULL; 1.86 +char* programPath = NULL; 1.87 +#ifndef WIN32 /* i.e. linux */ 1.88 + extern char **environ; // the user environment 1.89 +#endif /* linux */ 1.90 + 1.91 +//---- error logging functions 1.92 + 1.93 +void _LogError(const char* format ...) 1.94 +{ 1.95 + va_list ap; 1.96 + va_start(ap, format); 1.97 +#if defined(WIN32) && defined(_WINDOWS) 1.98 + // put up a MessageBox 1.99 + char caption[BUF_LENGTH+1]; 1.100 + snprintf(caption, BUF_LENGTH, "Error in %s", programName); 1.101 + char msg[BUF_LENGTH+1]; 1.102 + vsnprintf(msg, BUF_LENGTH, format, ap); 1.103 + va_end(ap); 1.104 + MessageBox(NULL, msg, caption, MB_OK | MB_ICONEXCLAMATION); 1.105 +#else 1.106 + fprintf(stderr, "%s: error: ", programName); 1.107 + vfprintf(stderr, format, ap); 1.108 + va_end(ap); 1.109 +#endif /* WIN32 && _WINDOWS */ 1.110 +} 1.111 + 1.112 + 1.113 +void _LogWarning(const char* format ...) 1.114 +{ 1.115 + va_list ap; 1.116 + va_start(ap, format); 1.117 +#if defined(WIN32) && defined(_WINDOWS) 1.118 + // put up a MessageBox 1.119 + char caption[BUF_LENGTH+1]; 1.120 + snprintf(caption, BUF_LENGTH, "Warning in %s", programName); 1.121 + char msg[BUF_LENGTH+1]; 1.122 + vsnprintf(msg, BUF_LENGTH, format, ap); 1.123 + va_end(ap); 1.124 + MessageBox(NULL, msg, caption, MB_OK | MB_ICONWARNING); 1.125 +#else 1.126 + fprintf(stderr, "%s: warning: ", programName); 1.127 + vfprintf(stderr, format, ap); 1.128 + va_end(ap); 1.129 +#endif /* WIN32 && _WINDOWS */ 1.130 +} 1.131 + 1.132 + 1.133 + 1.134 +//---- utilities functions 1.135 + 1.136 +/* _IsDir: Is the given dirname an existing directory */ 1.137 +static int _IsDir(char *dirname) 1.138 +{ 1.139 +#ifdef WIN32 1.140 + DWORD dwAttrib; 1.141 + dwAttrib = GetFileAttributes(dirname); 1.142 + if (dwAttrib == -1) { 1.143 + return 0; 1.144 + } 1.145 + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { 1.146 + return 1; 1.147 + } 1.148 + return 0; 1.149 +#else /* i.e. linux */ 1.150 + struct stat buf; 1.151 + if (stat(dirname, &buf) != 0) 1.152 + return 0; 1.153 + if (!S_ISDIR(buf.st_mode)) 1.154 + return 0; 1.155 + return 1; 1.156 +#endif 1.157 +} 1.158 + 1.159 + 1.160 +/* _IsLink: Is the given filename a symbolic link */ 1.161 +static int _IsLink(char *filename) 1.162 +{ 1.163 +#ifdef WIN32 1.164 + return 0; 1.165 +#else /* i.e. linux */ 1.166 + struct stat buf; 1.167 + if (lstat(filename, &buf) != 0) 1.168 + return 0; 1.169 + if (!S_ISLNK(buf.st_mode)) 1.170 + return 0; 1.171 + return 1; 1.172 +#endif 1.173 +} 1.174 + 1.175 + 1.176 +/* Is executable file 1.177 + * On Linux: check 'x' permission. On Windows: just check existence. 1.178 + */ 1.179 +static int _IsExecutableFile(char *filename) 1.180 +{ 1.181 +#ifdef WIN32 1.182 + return (int)PathFileExists(filename); 1.183 +#else /* i.e. linux */ 1.184 + struct stat buf; 1.185 + if (stat(filename, &buf) != 0) 1.186 + return 0; 1.187 + if (!S_ISREG(buf.st_mode)) 1.188 + return 0; 1.189 + if ((buf.st_mode & 0111) == 0) 1.190 + return 0; 1.191 + return 1; 1.192 +#endif /* WIN32 */ 1.193 +} 1.194 + 1.195 + 1.196 +/* _GetProgramPath: Determine the absolute path to the given program name. 1.197 + * 1.198 + * Takes into account the current working directory, etc. 1.199 + * The implementations require the global 'programName' to be set. 1.200 + */ 1.201 +#ifdef WIN32 1.202 + static char* _GetProgramPath(void) 1.203 + { 1.204 + //XXX this is ugly but I didn't want to use malloc, no reason 1.205 + static char progPath[MAXPATHLEN+1]; 1.206 + // get absolute path to module 1.207 + if (!GetModuleFileName(NULL, progPath, MAXPATHLEN)) { 1.208 + _LogError("could not get absolute program name from "\ 1.209 + "GetModuleFileName\n"); 1.210 + exit(1); 1.211 + } 1.212 + // just need dirname 1.213 + for (char* p = progPath+strlen(progPath); 1.214 + *p != SEP && *p != ALTSEP; 1.215 + --p) 1.216 + { 1.217 + *p = '\0'; 1.218 + } 1.219 + *p = '\0'; // remove the trailing SEP as well 1.220 + 1.221 + return progPath; 1.222 + } 1.223 +#else 1.224 + 1.225 + /* _JoinPath requires that any buffer argument passed to it has at 1.226 + least MAXPATHLEN + 1 bytes allocated. If this requirement is met, 1.227 + it guarantees that it will never overflow the buffer. If stuff 1.228 + is too long, buffer will contain a truncated copy of stuff. 1.229 + */ 1.230 + static void 1.231 + _JoinPath(char *buffer, char *stuff) 1.232 + { 1.233 + size_t n, k; 1.234 + if (stuff[0] == SEP) 1.235 + n = 0; 1.236 + else { 1.237 + n = strlen(buffer); 1.238 + if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) 1.239 + buffer[n++] = SEP; 1.240 + } 1.241 + k = strlen(stuff); 1.242 + if (n + k > MAXPATHLEN) 1.243 + k = MAXPATHLEN - n; 1.244 + strncpy(buffer+n, stuff, k); 1.245 + buffer[n+k] = '\0'; 1.246 + } 1.247 + 1.248 + 1.249 + static char* 1.250 + _GetProgramPath(void) 1.251 + { 1.252 + /* XXX this routine does *no* error checking */ 1.253 + char* path = getenv("PATH"); 1.254 + static char progPath[MAXPATHLEN+1]; 1.255 + 1.256 + /* If there is no slash in the argv0 path, then we have to 1.257 + * assume the program is on the user's $PATH, since there's no 1.258 + * other way to find a directory to start the search from. If 1.259 + * $PATH isn't exported, you lose. 1.260 + */ 1.261 + if (strchr(programName, SEP)) { 1.262 + strncpy(progPath, programName, MAXPATHLEN); 1.263 + } 1.264 + else if (path) { 1.265 + int bufspace = MAXPATHLEN; 1.266 + while (1) { 1.267 + char *delim = strchr(path, DELIM); 1.268 + 1.269 + if (delim) { 1.270 + size_t len = delim - path; 1.271 + if (len > bufspace) { 1.272 + len = bufspace; 1.273 + } 1.274 + strncpy(progPath, path, len); 1.275 + *(progPath + len) = '\0'; 1.276 + bufspace -= len; 1.277 + } 1.278 + else { 1.279 + strncpy(progPath, path, bufspace); 1.280 + } 1.281 + 1.282 + _JoinPath(progPath, programName); 1.283 + if (_IsExecutableFile(progPath)) { 1.284 + break; 1.285 + } 1.286 + 1.287 + if (!delim) { 1.288 + progPath[0] = '\0'; 1.289 + break; 1.290 + } 1.291 + path = delim + 1; 1.292 + } 1.293 + } 1.294 + else { 1.295 + progPath[0] = '\0'; 1.296 + } 1.297 + 1.298 + // now we have to resolve a string of possible symlinks 1.299 + // - we'll just handle the simple case of a single level of 1.300 + // indirection 1.301 + // 1.302 + // XXX note this does not handle multiple levels of symlinks 1.303 + // here is pseudo-code for that (please implement it :): 1.304 + // while 1: 1.305 + // if islink(progPath): 1.306 + // linkText = readlink(progPath) 1.307 + // if isabsolute(linkText): 1.308 + // progPath = os.path.join(dirname(progPath), linkText) 1.309 + // else: 1.310 + // progPath = linkText 1.311 + // else: 1.312 + // break 1.313 + if (_IsLink(progPath)) { 1.314 + char newProgPath[MAXPATHLEN+1]; 1.315 + readlink(progPath, newProgPath, MAXPATHLEN); 1.316 + strncpy(progPath, newProgPath, MAXPATHLEN); 1.317 + } 1.318 + 1.319 + 1.320 + // prefix with the current working directory if the path is 1.321 + // relative to conform with the Windows version of this 1.322 + if (strlen(progPath) != 0 && progPath[0] != SEP) { 1.323 + char cwd[MAXPATHLEN+1]; 1.324 + char tmp[MAXPATHLEN+1]; 1.325 + //XXX should check for failure retvals 1.326 + getcwd(cwd, MAXPATHLEN); 1.327 + snprintf(tmp, MAXPATHLEN, "%s%c%s", cwd, SEP, progPath); 1.328 + strncpy(progPath, tmp, MAXPATHLEN); 1.329 + } 1.330 + 1.331 + // 'progPath' now contains the full path to the program *and* the program 1.332 + // name. The latter is not desire. 1.333 + char* pLetter = progPath + strlen(progPath); 1.334 + for (;pLetter != progPath && *pLetter != SEP; --pLetter) { 1.335 + /* do nothing */ 1.336 + } 1.337 + *pLetter = '\0'; 1.338 + 1.339 + return progPath; 1.340 + } 1.341 +#endif /* WIN32 */ 1.342 + 1.343 + 1.344 +//---- mainline 1.345 + 1.346 +int main(int argc, char** argv) 1.347 +{ 1.348 + programName = argv[0]; 1.349 + programPath = _GetProgramPath(); 1.350 + 1.351 + // Determine the extension-less program basename. 1.352 + // XXX Will not always handle app names with '.' in them (other than 1.353 + // the '.' for the extension. 1.354 + char programNameNoExt[MAXPATHLEN+1]; 1.355 + char *pStart, *pEnd; 1.356 + pStart = pEnd = programName + strlen(programName) - 1; 1.357 + while (pStart != programName && *(pStart-1) != SEP) { 1.358 + pStart--; 1.359 + } 1.360 + while (1) { 1.361 + if (pEnd == pStart) { 1.362 + pEnd = programName + strlen(programName) - 1; 1.363 + break; 1.364 + } 1.365 + pEnd--; 1.366 + if (*(pEnd+1) == '.') { 1.367 + break; 1.368 + } 1.369 + } 1.370 + strncpy(programNameNoExt, pStart, pEnd-pStart+1); 1.371 + *(programNameNoExt+(pEnd-pStart+1)) = '\0'; 1.372 + 1.373 + // determine the full path to "<exename>.py" 1.374 + char pyFile[MAXPATHLEN+1]; 1.375 + snprintf(pyFile, MAXPATHLEN, "%s%c%s.py", programPath, SEP, 1.376 + programNameNoExt); 1.377 + 1.378 + // Build the argument array for launching. 1.379 + char* pythonArgs[MAX_PYTHON_ARGS+1]; 1.380 + int nPythonArgs = 0; 1.381 + pythonArgs[nPythonArgs++] = "python"; 1.382 + pythonArgs[nPythonArgs++] = "-tt"; 1.383 + pythonArgs[nPythonArgs++] = pyFile; 1.384 + for (int i = 1; i < argc; ++i) { 1.385 + pythonArgs[nPythonArgs++] = argv[i]; 1.386 + } 1.387 + pythonArgs[nPythonArgs++] = NULL; 1.388 + 1.389 + return _spawnvp(_P_WAIT, pythonArgs[0], pythonArgs); 1.390 +} 1.391 + 1.392 + 1.393 +//---- mainline for win32 subsystem:windows app 1.394 +#ifdef WIN32 1.395 + int WINAPI WinMain( 1.396 + HINSTANCE hInstance, /* handle to current instance */ 1.397 + HINSTANCE hPrevInstance, /* handle to previous instance */ 1.398 + LPSTR lpCmdLine, /* pointer to command line */ 1.399 + int nCmdShow /* show state of window */ 1.400 + ) 1.401 + { 1.402 + return main(__argc, __argv); 1.403 + } 1.404 +#endif 1.405 +