summaryrefslogtreecommitdiffstats
path: root/python/which/launcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'python/which/launcher.cpp')
-rw-r--r--python/which/launcher.cpp404
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
+