python/which/launcher.cpp

changeset 0
6474c204b198
     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 +

mercurial