diff options
Diffstat (limited to 'python/which/launcher.cpp')
-rw-r--r-- | python/which/launcher.cpp | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/python/which/launcher.cpp b/python/which/launcher.cpp new file mode 100644 index 000000000..36bbbe866 --- /dev/null +++ b/python/which/launcher.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2002-2003 ActiveState Corp. + * Author: Trent Mick (TrentM@ActiveState.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Console launch executable. + * + * This program exists solely to launch: + * python <installdir>/<exename>.py <argv> + * on Windows. "<exename>.py" must be in the same directory. + * + * Rationale: + * - On some Windows flavours .py *can* be put on the PATHEXT to be + * able to find "<exename>.py" if it is on the PATH. This is fine + * until you need shell redirection to work. It does NOT for + * extensions to PATHEXT. Redirection *does* work for "python + * <script>.py" so we will try to do that. + */ + +#ifdef WIN32 + #include <windows.h> + #include <process.h> + #include <direct.h> + #include <shlwapi.h> +#else /* linux */ + #include <unistd.h> +#endif /* WIN32 */ +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +//---- constants + +#define BUF_LENGTH 2048 +#define MAX_PYTHON_ARGS 50 +#define MAX_FILES 50 +#define MAXPATHLEN 1024 +#ifdef WIN32 + #define SEP '\\' + #define ALTSEP '/' + // path list element separator + #define DELIM ';' +#else /* linux */ + #define SEP '/' + // path list element separator + #define DELIM ':' +#endif + +#ifdef WIN32 + #define spawnvp _spawnvp + #if defined(_MSC_VER) && _MSC_VER < 1900 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #endif + //NOTE: this is for the stat *call* and the stat *struct* + #define stat _stat +#endif + + +//---- globals + +char* programName = NULL; +char* programPath = NULL; +#ifndef WIN32 /* i.e. linux */ + extern char **environ; // the user environment +#endif /* linux */ + +//---- error logging functions + +void _LogError(const char* format ...) +{ + va_list ap; + va_start(ap, format); +#if defined(WIN32) && defined(_WINDOWS) + // put up a MessageBox + char caption[BUF_LENGTH+1]; + snprintf(caption, BUF_LENGTH, "Error in %s", programName); + char msg[BUF_LENGTH+1]; + vsnprintf(msg, BUF_LENGTH, format, ap); + va_end(ap); + MessageBox(NULL, msg, caption, MB_OK | MB_ICONEXCLAMATION); +#else + fprintf(stderr, "%s: error: ", programName); + vfprintf(stderr, format, ap); + va_end(ap); +#endif /* WIN32 && _WINDOWS */ +} + + +void _LogWarning(const char* format ...) +{ + va_list ap; + va_start(ap, format); +#if defined(WIN32) && defined(_WINDOWS) + // put up a MessageBox + char caption[BUF_LENGTH+1]; + snprintf(caption, BUF_LENGTH, "Warning in %s", programName); + char msg[BUF_LENGTH+1]; + vsnprintf(msg, BUF_LENGTH, format, ap); + va_end(ap); + MessageBox(NULL, msg, caption, MB_OK | MB_ICONWARNING); +#else + fprintf(stderr, "%s: warning: ", programName); + vfprintf(stderr, format, ap); + va_end(ap); +#endif /* WIN32 && _WINDOWS */ +} + + + +//---- utilities functions + +/* _IsDir: Is the given dirname an existing directory */ +static int _IsDir(char *dirname) +{ +#ifdef WIN32 + DWORD dwAttrib; + dwAttrib = GetFileAttributes(dirname); + if (dwAttrib == -1) { + return 0; + } + if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { + return 1; + } + return 0; +#else /* i.e. linux */ + struct stat buf; + if (stat(dirname, &buf) != 0) + return 0; + if (!S_ISDIR(buf.st_mode)) + return 0; + return 1; +#endif +} + + +/* _IsLink: Is the given filename a symbolic link */ +static int _IsLink(char *filename) +{ +#ifdef WIN32 + return 0; +#else /* i.e. linux */ + struct stat buf; + if (lstat(filename, &buf) != 0) + return 0; + if (!S_ISLNK(buf.st_mode)) + return 0; + return 1; +#endif +} + + +/* Is executable file + * On Linux: check 'x' permission. On Windows: just check existence. + */ +static int _IsExecutableFile(char *filename) +{ +#ifdef WIN32 + return (int)PathFileExists(filename); +#else /* i.e. linux */ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISREG(buf.st_mode)) + return 0; + if ((buf.st_mode & 0111) == 0) + return 0; + return 1; +#endif /* WIN32 */ +} + + +/* _GetProgramPath: Determine the absolute path to the given program name. + * + * Takes into account the current working directory, etc. + * The implementations require the global 'programName' to be set. + */ +#ifdef WIN32 + static char* _GetProgramPath(void) + { + //XXX this is ugly but I didn't want to use malloc, no reason + static char progPath[MAXPATHLEN+1]; + // get absolute path to module + if (!GetModuleFileName(NULL, progPath, MAXPATHLEN)) { + _LogError("could not get absolute program name from "\ + "GetModuleFileName\n"); + exit(1); + } + // just need dirname + for (char* p = progPath+strlen(progPath); + *p != SEP && *p != ALTSEP; + --p) + { + *p = '\0'; + } + *p = '\0'; // remove the trailing SEP as well + + return progPath; + } +#else + + /* _JoinPath requires that any buffer argument passed to it has at + least MAXPATHLEN + 1 bytes allocated. If this requirement is met, + it guarantees that it will never overflow the buffer. If stuff + is too long, buffer will contain a truncated copy of stuff. + */ + static void + _JoinPath(char *buffer, char *stuff) + { + size_t n, k; + if (stuff[0] == SEP) + n = 0; + else { + n = strlen(buffer); + if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) + buffer[n++] = SEP; + } + k = strlen(stuff); + if (n + k > MAXPATHLEN) + k = MAXPATHLEN - n; + strncpy(buffer+n, stuff, k); + buffer[n+k] = '\0'; + } + + + static char* + _GetProgramPath(void) + { + /* XXX this routine does *no* error checking */ + char* path = getenv("PATH"); + static char progPath[MAXPATHLEN+1]; + + /* If there is no slash in the argv0 path, then we have to + * assume the program is on the user's $PATH, since there's no + * other way to find a directory to start the search from. If + * $PATH isn't exported, you lose. + */ + if (strchr(programName, SEP)) { + strncpy(progPath, programName, MAXPATHLEN); + } + else if (path) { + int bufspace = MAXPATHLEN; + while (1) { + char *delim = strchr(path, DELIM); + + if (delim) { + size_t len = delim - path; + if (len > bufspace) { + len = bufspace; + } + strncpy(progPath, path, len); + *(progPath + len) = '\0'; + bufspace -= len; + } + else { + strncpy(progPath, path, bufspace); + } + + _JoinPath(progPath, programName); + if (_IsExecutableFile(progPath)) { + break; + } + + if (!delim) { + progPath[0] = '\0'; + break; + } + path = delim + 1; + } + } + else { + progPath[0] = '\0'; + } + + // now we have to resolve a string of possible symlinks + // - we'll just handle the simple case of a single level of + // indirection + // + // XXX note this does not handle multiple levels of symlinks + // here is pseudo-code for that (please implement it :): + // while 1: + // if islink(progPath): + // linkText = readlink(progPath) + // if isabsolute(linkText): + // progPath = os.path.join(dirname(progPath), linkText) + // else: + // progPath = linkText + // else: + // break + if (_IsLink(progPath)) { + char newProgPath[MAXPATHLEN+1]; + readlink(progPath, newProgPath, MAXPATHLEN); + strncpy(progPath, newProgPath, MAXPATHLEN); + } + + + // prefix with the current working directory if the path is + // relative to conform with the Windows version of this + if (strlen(progPath) != 0 && progPath[0] != SEP) { + char cwd[MAXPATHLEN+1]; + char tmp[MAXPATHLEN+1]; + //XXX should check for failure retvals + getcwd(cwd, MAXPATHLEN); + snprintf(tmp, MAXPATHLEN, "%s%c%s", cwd, SEP, progPath); + strncpy(progPath, tmp, MAXPATHLEN); + } + + // 'progPath' now contains the full path to the program *and* the program + // name. The latter is not desire. + char* pLetter = progPath + strlen(progPath); + for (;pLetter != progPath && *pLetter != SEP; --pLetter) { + /* do nothing */ + } + *pLetter = '\0'; + + return progPath; + } +#endif /* WIN32 */ + + +//---- mainline + +int main(int argc, char** argv) +{ + programName = argv[0]; + programPath = _GetProgramPath(); + + // Determine the extension-less program basename. + // XXX Will not always handle app names with '.' in them (other than + // the '.' for the extension. + char programNameNoExt[MAXPATHLEN+1]; + char *pStart, *pEnd; + pStart = pEnd = programName + strlen(programName) - 1; + while (pStart != programName && *(pStart-1) != SEP) { + pStart--; + } + while (1) { + if (pEnd == pStart) { + pEnd = programName + strlen(programName) - 1; + break; + } + pEnd--; + if (*(pEnd+1) == '.') { + break; + } + } + strncpy(programNameNoExt, pStart, pEnd-pStart+1); + *(programNameNoExt+(pEnd-pStart+1)) = '\0'; + + // determine the full path to "<exename>.py" + char pyFile[MAXPATHLEN+1]; + snprintf(pyFile, MAXPATHLEN, "%s%c%s.py", programPath, SEP, + programNameNoExt); + + // Build the argument array for launching. + char* pythonArgs[MAX_PYTHON_ARGS+1]; + int nPythonArgs = 0; + pythonArgs[nPythonArgs++] = "python"; + pythonArgs[nPythonArgs++] = "-tt"; + pythonArgs[nPythonArgs++] = pyFile; + for (int i = 1; i < argc; ++i) { + pythonArgs[nPythonArgs++] = argv[i]; + } + pythonArgs[nPythonArgs++] = NULL; + + return _spawnvp(_P_WAIT, pythonArgs[0], pythonArgs); +} + + +//---- mainline for win32 subsystem:windows app +#ifdef WIN32 + int WINAPI WinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ + ) + { + return main(__argc, __argv); + } +#endif + |