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

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

mercurial