mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
26bcfc7817
Version 1.1.0 obtained from https://code.google.com/p/which/ and added to tree without modifications aside from the removal of which.exe, which has no reason to be in the tree.
403 lines
11 KiB
C++
403 lines
11 KiB
C++
/*
|
|
* 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
|
|
#define snprintf _snprintf
|
|
#define vsnprintf _vsnprintf
|
|
//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
|
|
|