python/which/launcher.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 * Copyright (c) 2002-2003 ActiveState Corp.
michael@0 3 * Author: Trent Mick (TrentM@ActiveState.com)
michael@0 4 *
michael@0 5 * Permission is hereby granted, free of charge, to any person obtaining a
michael@0 6 * copy of this software and associated documentation files (the
michael@0 7 * "Software"), to deal in the Software without restriction, including
michael@0 8 * without limitation the rights to use, copy, modify, merge, publish,
michael@0 9 * distribute, sublicense, and/or sell copies of the Software, and to
michael@0 10 * permit persons to whom the Software is furnished to do so, subject to
michael@0 11 * the following conditions:
michael@0 12 *
michael@0 13 * The above copyright notice and this permission notice shall be included
michael@0 14 * in all copies or substantial portions of the Software.
michael@0 15 *
michael@0 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
michael@0 17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
michael@0 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
michael@0 19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
michael@0 20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
michael@0 21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
michael@0 22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
michael@0 23 */
michael@0 24
michael@0 25 /* Console launch executable.
michael@0 26 *
michael@0 27 * This program exists solely to launch:
michael@0 28 * python <installdir>/<exename>.py <argv>
michael@0 29 * on Windows. "<exename>.py" must be in the same directory.
michael@0 30 *
michael@0 31 * Rationale:
michael@0 32 * - On some Windows flavours .py *can* be put on the PATHEXT to be
michael@0 33 * able to find "<exename>.py" if it is on the PATH. This is fine
michael@0 34 * until you need shell redirection to work. It does NOT for
michael@0 35 * extensions to PATHEXT. Redirection *does* work for "python
michael@0 36 * <script>.py" so we will try to do that.
michael@0 37 */
michael@0 38
michael@0 39 #ifdef WIN32
michael@0 40 #include <windows.h>
michael@0 41 #include <process.h>
michael@0 42 #include <direct.h>
michael@0 43 #include <shlwapi.h>
michael@0 44 #else /* linux */
michael@0 45 #include <unistd.h>
michael@0 46 #endif /* WIN32 */
michael@0 47 #include <sys/stat.h>
michael@0 48 #include <errno.h>
michael@0 49 #include <stdio.h>
michael@0 50 #include <stdlib.h>
michael@0 51 #include <string.h>
michael@0 52 #include <stdarg.h>
michael@0 53
michael@0 54 //---- constants
michael@0 55
michael@0 56 #define BUF_LENGTH 2048
michael@0 57 #define MAX_PYTHON_ARGS 50
michael@0 58 #define MAX_FILES 50
michael@0 59 #define MAXPATHLEN 1024
michael@0 60 #ifdef WIN32
michael@0 61 #define SEP '\\'
michael@0 62 #define ALTSEP '/'
michael@0 63 // path list element separator
michael@0 64 #define DELIM ';'
michael@0 65 #else /* linux */
michael@0 66 #define SEP '/'
michael@0 67 // path list element separator
michael@0 68 #define DELIM ':'
michael@0 69 #endif
michael@0 70
michael@0 71 #ifdef WIN32
michael@0 72 #define spawnvp _spawnvp
michael@0 73 #define snprintf _snprintf
michael@0 74 #define vsnprintf _vsnprintf
michael@0 75 //NOTE: this is for the stat *call* and the stat *struct*
michael@0 76 #define stat _stat
michael@0 77 #endif
michael@0 78
michael@0 79
michael@0 80 //---- globals
michael@0 81
michael@0 82 char* programName = NULL;
michael@0 83 char* programPath = NULL;
michael@0 84 #ifndef WIN32 /* i.e. linux */
michael@0 85 extern char **environ; // the user environment
michael@0 86 #endif /* linux */
michael@0 87
michael@0 88 //---- error logging functions
michael@0 89
michael@0 90 void _LogError(const char* format ...)
michael@0 91 {
michael@0 92 va_list ap;
michael@0 93 va_start(ap, format);
michael@0 94 #if defined(WIN32) && defined(_WINDOWS)
michael@0 95 // put up a MessageBox
michael@0 96 char caption[BUF_LENGTH+1];
michael@0 97 snprintf(caption, BUF_LENGTH, "Error in %s", programName);
michael@0 98 char msg[BUF_LENGTH+1];
michael@0 99 vsnprintf(msg, BUF_LENGTH, format, ap);
michael@0 100 va_end(ap);
michael@0 101 MessageBox(NULL, msg, caption, MB_OK | MB_ICONEXCLAMATION);
michael@0 102 #else
michael@0 103 fprintf(stderr, "%s: error: ", programName);
michael@0 104 vfprintf(stderr, format, ap);
michael@0 105 va_end(ap);
michael@0 106 #endif /* WIN32 && _WINDOWS */
michael@0 107 }
michael@0 108
michael@0 109
michael@0 110 void _LogWarning(const char* format ...)
michael@0 111 {
michael@0 112 va_list ap;
michael@0 113 va_start(ap, format);
michael@0 114 #if defined(WIN32) && defined(_WINDOWS)
michael@0 115 // put up a MessageBox
michael@0 116 char caption[BUF_LENGTH+1];
michael@0 117 snprintf(caption, BUF_LENGTH, "Warning in %s", programName);
michael@0 118 char msg[BUF_LENGTH+1];
michael@0 119 vsnprintf(msg, BUF_LENGTH, format, ap);
michael@0 120 va_end(ap);
michael@0 121 MessageBox(NULL, msg, caption, MB_OK | MB_ICONWARNING);
michael@0 122 #else
michael@0 123 fprintf(stderr, "%s: warning: ", programName);
michael@0 124 vfprintf(stderr, format, ap);
michael@0 125 va_end(ap);
michael@0 126 #endif /* WIN32 && _WINDOWS */
michael@0 127 }
michael@0 128
michael@0 129
michael@0 130
michael@0 131 //---- utilities functions
michael@0 132
michael@0 133 /* _IsDir: Is the given dirname an existing directory */
michael@0 134 static int _IsDir(char *dirname)
michael@0 135 {
michael@0 136 #ifdef WIN32
michael@0 137 DWORD dwAttrib;
michael@0 138 dwAttrib = GetFileAttributes(dirname);
michael@0 139 if (dwAttrib == -1) {
michael@0 140 return 0;
michael@0 141 }
michael@0 142 if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
michael@0 143 return 1;
michael@0 144 }
michael@0 145 return 0;
michael@0 146 #else /* i.e. linux */
michael@0 147 struct stat buf;
michael@0 148 if (stat(dirname, &buf) != 0)
michael@0 149 return 0;
michael@0 150 if (!S_ISDIR(buf.st_mode))
michael@0 151 return 0;
michael@0 152 return 1;
michael@0 153 #endif
michael@0 154 }
michael@0 155
michael@0 156
michael@0 157 /* _IsLink: Is the given filename a symbolic link */
michael@0 158 static int _IsLink(char *filename)
michael@0 159 {
michael@0 160 #ifdef WIN32
michael@0 161 return 0;
michael@0 162 #else /* i.e. linux */
michael@0 163 struct stat buf;
michael@0 164 if (lstat(filename, &buf) != 0)
michael@0 165 return 0;
michael@0 166 if (!S_ISLNK(buf.st_mode))
michael@0 167 return 0;
michael@0 168 return 1;
michael@0 169 #endif
michael@0 170 }
michael@0 171
michael@0 172
michael@0 173 /* Is executable file
michael@0 174 * On Linux: check 'x' permission. On Windows: just check existence.
michael@0 175 */
michael@0 176 static int _IsExecutableFile(char *filename)
michael@0 177 {
michael@0 178 #ifdef WIN32
michael@0 179 return (int)PathFileExists(filename);
michael@0 180 #else /* i.e. linux */
michael@0 181 struct stat buf;
michael@0 182 if (stat(filename, &buf) != 0)
michael@0 183 return 0;
michael@0 184 if (!S_ISREG(buf.st_mode))
michael@0 185 return 0;
michael@0 186 if ((buf.st_mode & 0111) == 0)
michael@0 187 return 0;
michael@0 188 return 1;
michael@0 189 #endif /* WIN32 */
michael@0 190 }
michael@0 191
michael@0 192
michael@0 193 /* _GetProgramPath: Determine the absolute path to the given program name.
michael@0 194 *
michael@0 195 * Takes into account the current working directory, etc.
michael@0 196 * The implementations require the global 'programName' to be set.
michael@0 197 */
michael@0 198 #ifdef WIN32
michael@0 199 static char* _GetProgramPath(void)
michael@0 200 {
michael@0 201 //XXX this is ugly but I didn't want to use malloc, no reason
michael@0 202 static char progPath[MAXPATHLEN+1];
michael@0 203 // get absolute path to module
michael@0 204 if (!GetModuleFileName(NULL, progPath, MAXPATHLEN)) {
michael@0 205 _LogError("could not get absolute program name from "\
michael@0 206 "GetModuleFileName\n");
michael@0 207 exit(1);
michael@0 208 }
michael@0 209 // just need dirname
michael@0 210 for (char* p = progPath+strlen(progPath);
michael@0 211 *p != SEP && *p != ALTSEP;
michael@0 212 --p)
michael@0 213 {
michael@0 214 *p = '\0';
michael@0 215 }
michael@0 216 *p = '\0'; // remove the trailing SEP as well
michael@0 217
michael@0 218 return progPath;
michael@0 219 }
michael@0 220 #else
michael@0 221
michael@0 222 /* _JoinPath requires that any buffer argument passed to it has at
michael@0 223 least MAXPATHLEN + 1 bytes allocated. If this requirement is met,
michael@0 224 it guarantees that it will never overflow the buffer. If stuff
michael@0 225 is too long, buffer will contain a truncated copy of stuff.
michael@0 226 */
michael@0 227 static void
michael@0 228 _JoinPath(char *buffer, char *stuff)
michael@0 229 {
michael@0 230 size_t n, k;
michael@0 231 if (stuff[0] == SEP)
michael@0 232 n = 0;
michael@0 233 else {
michael@0 234 n = strlen(buffer);
michael@0 235 if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN)
michael@0 236 buffer[n++] = SEP;
michael@0 237 }
michael@0 238 k = strlen(stuff);
michael@0 239 if (n + k > MAXPATHLEN)
michael@0 240 k = MAXPATHLEN - n;
michael@0 241 strncpy(buffer+n, stuff, k);
michael@0 242 buffer[n+k] = '\0';
michael@0 243 }
michael@0 244
michael@0 245
michael@0 246 static char*
michael@0 247 _GetProgramPath(void)
michael@0 248 {
michael@0 249 /* XXX this routine does *no* error checking */
michael@0 250 char* path = getenv("PATH");
michael@0 251 static char progPath[MAXPATHLEN+1];
michael@0 252
michael@0 253 /* If there is no slash in the argv0 path, then we have to
michael@0 254 * assume the program is on the user's $PATH, since there's no
michael@0 255 * other way to find a directory to start the search from. If
michael@0 256 * $PATH isn't exported, you lose.
michael@0 257 */
michael@0 258 if (strchr(programName, SEP)) {
michael@0 259 strncpy(progPath, programName, MAXPATHLEN);
michael@0 260 }
michael@0 261 else if (path) {
michael@0 262 int bufspace = MAXPATHLEN;
michael@0 263 while (1) {
michael@0 264 char *delim = strchr(path, DELIM);
michael@0 265
michael@0 266 if (delim) {
michael@0 267 size_t len = delim - path;
michael@0 268 if (len > bufspace) {
michael@0 269 len = bufspace;
michael@0 270 }
michael@0 271 strncpy(progPath, path, len);
michael@0 272 *(progPath + len) = '\0';
michael@0 273 bufspace -= len;
michael@0 274 }
michael@0 275 else {
michael@0 276 strncpy(progPath, path, bufspace);
michael@0 277 }
michael@0 278
michael@0 279 _JoinPath(progPath, programName);
michael@0 280 if (_IsExecutableFile(progPath)) {
michael@0 281 break;
michael@0 282 }
michael@0 283
michael@0 284 if (!delim) {
michael@0 285 progPath[0] = '\0';
michael@0 286 break;
michael@0 287 }
michael@0 288 path = delim + 1;
michael@0 289 }
michael@0 290 }
michael@0 291 else {
michael@0 292 progPath[0] = '\0';
michael@0 293 }
michael@0 294
michael@0 295 // now we have to resolve a string of possible symlinks
michael@0 296 // - we'll just handle the simple case of a single level of
michael@0 297 // indirection
michael@0 298 //
michael@0 299 // XXX note this does not handle multiple levels of symlinks
michael@0 300 // here is pseudo-code for that (please implement it :):
michael@0 301 // while 1:
michael@0 302 // if islink(progPath):
michael@0 303 // linkText = readlink(progPath)
michael@0 304 // if isabsolute(linkText):
michael@0 305 // progPath = os.path.join(dirname(progPath), linkText)
michael@0 306 // else:
michael@0 307 // progPath = linkText
michael@0 308 // else:
michael@0 309 // break
michael@0 310 if (_IsLink(progPath)) {
michael@0 311 char newProgPath[MAXPATHLEN+1];
michael@0 312 readlink(progPath, newProgPath, MAXPATHLEN);
michael@0 313 strncpy(progPath, newProgPath, MAXPATHLEN);
michael@0 314 }
michael@0 315
michael@0 316
michael@0 317 // prefix with the current working directory if the path is
michael@0 318 // relative to conform with the Windows version of this
michael@0 319 if (strlen(progPath) != 0 && progPath[0] != SEP) {
michael@0 320 char cwd[MAXPATHLEN+1];
michael@0 321 char tmp[MAXPATHLEN+1];
michael@0 322 //XXX should check for failure retvals
michael@0 323 getcwd(cwd, MAXPATHLEN);
michael@0 324 snprintf(tmp, MAXPATHLEN, "%s%c%s", cwd, SEP, progPath);
michael@0 325 strncpy(progPath, tmp, MAXPATHLEN);
michael@0 326 }
michael@0 327
michael@0 328 // 'progPath' now contains the full path to the program *and* the program
michael@0 329 // name. The latter is not desire.
michael@0 330 char* pLetter = progPath + strlen(progPath);
michael@0 331 for (;pLetter != progPath && *pLetter != SEP; --pLetter) {
michael@0 332 /* do nothing */
michael@0 333 }
michael@0 334 *pLetter = '\0';
michael@0 335
michael@0 336 return progPath;
michael@0 337 }
michael@0 338 #endif /* WIN32 */
michael@0 339
michael@0 340
michael@0 341 //---- mainline
michael@0 342
michael@0 343 int main(int argc, char** argv)
michael@0 344 {
michael@0 345 programName = argv[0];
michael@0 346 programPath = _GetProgramPath();
michael@0 347
michael@0 348 // Determine the extension-less program basename.
michael@0 349 // XXX Will not always handle app names with '.' in them (other than
michael@0 350 // the '.' for the extension.
michael@0 351 char programNameNoExt[MAXPATHLEN+1];
michael@0 352 char *pStart, *pEnd;
michael@0 353 pStart = pEnd = programName + strlen(programName) - 1;
michael@0 354 while (pStart != programName && *(pStart-1) != SEP) {
michael@0 355 pStart--;
michael@0 356 }
michael@0 357 while (1) {
michael@0 358 if (pEnd == pStart) {
michael@0 359 pEnd = programName + strlen(programName) - 1;
michael@0 360 break;
michael@0 361 }
michael@0 362 pEnd--;
michael@0 363 if (*(pEnd+1) == '.') {
michael@0 364 break;
michael@0 365 }
michael@0 366 }
michael@0 367 strncpy(programNameNoExt, pStart, pEnd-pStart+1);
michael@0 368 *(programNameNoExt+(pEnd-pStart+1)) = '\0';
michael@0 369
michael@0 370 // determine the full path to "<exename>.py"
michael@0 371 char pyFile[MAXPATHLEN+1];
michael@0 372 snprintf(pyFile, MAXPATHLEN, "%s%c%s.py", programPath, SEP,
michael@0 373 programNameNoExt);
michael@0 374
michael@0 375 // Build the argument array for launching.
michael@0 376 char* pythonArgs[MAX_PYTHON_ARGS+1];
michael@0 377 int nPythonArgs = 0;
michael@0 378 pythonArgs[nPythonArgs++] = "python";
michael@0 379 pythonArgs[nPythonArgs++] = "-tt";
michael@0 380 pythonArgs[nPythonArgs++] = pyFile;
michael@0 381 for (int i = 1; i < argc; ++i) {
michael@0 382 pythonArgs[nPythonArgs++] = argv[i];
michael@0 383 }
michael@0 384 pythonArgs[nPythonArgs++] = NULL;
michael@0 385
michael@0 386 return _spawnvp(_P_WAIT, pythonArgs[0], pythonArgs);
michael@0 387 }
michael@0 388
michael@0 389
michael@0 390 //---- mainline for win32 subsystem:windows app
michael@0 391 #ifdef WIN32
michael@0 392 int WINAPI WinMain(
michael@0 393 HINSTANCE hInstance, /* handle to current instance */
michael@0 394 HINSTANCE hPrevInstance, /* handle to previous instance */
michael@0 395 LPSTR lpCmdLine, /* pointer to command line */
michael@0 396 int nCmdShow /* show state of window */
michael@0 397 )
michael@0 398 {
michael@0 399 return main(__argc, __argv);
michael@0 400 }
michael@0 401 #endif
michael@0 402

mercurial