| /* |
| * 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 |
| |