blob: 56c1a715181a0d980b2fa875c6914df488817932 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Andrew Niefer
* Martin Oberhuber (Wind River) - [176805] Support Solaris9 by adding setenv()
*******************************************************************************/
#include "eclipseCommon.h"
#include "eclipseUnicode.h"
#ifdef _WIN32
#include <direct.h>
#include <windows.h>
#else
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
/* Global Variables */
_TCHAR* osArg = _T_ECLIPSE(DEFAULT_OS);
#ifdef MACOSX
/* on the mac we have a universal binary, decide ppc vs x86 based on endianness */
#ifdef __BIG_ENDIAN__
_TCHAR* osArchArg = _T_ECLIPSE("ppc");
#else
_TCHAR* osArchArg = _T_ECLIPSE(DEFAULT_OS_ARCH);
#endif
#else
_TCHAR* osArchArg = _T_ECLIPSE(DEFAULT_OS_ARCH);
#endif
_TCHAR* wsArg = _T_ECLIPSE(DEFAULT_WS); /* the SWT supported GUI to be used */
/* Local Variables */
static _TCHAR* filterPrefix = NULL; /* prefix for the find files filter */
static size_t prefixLength = 0;
static int isFolder(const _TCHAR* path, const _TCHAR* entry);
typedef struct {
int segment[3];
_TCHAR * qualifier;
} Version;
static void freeVersion(Version *version)
{
if(version->qualifier)
free(version->qualifier);
free(version);
}
static Version* parseVersion(const _TCHAR * str) {
_TCHAR *copy;
_TCHAR *c1, *c2 = NULL;
int i = 0;
Version *version = malloc(sizeof(Version));
memset(version, 0, sizeof(Version));
c1 = copy = _tcsdup(str);
while (c1 && *c1 != 0)
{
if (i < 3) {
version->segment[i] = (int)_tcstol(c1, &c2, 10);
/* if the next character is not '.', then we couldn't
* parse as a int, the remainder is not valid (or we are at the end)*/
if (*c2 && *c2 != _T_ECLIPSE('.'))
break;
c2++; /* increment past the . */
} else {
c2 = _tcschr(c1, _T_ECLIPSE('.'));
if(c2 != NULL) {
*c2 = 0;
version->qualifier = _tcsdup(c1);
*c2 = _T_ECLIPSE('.'); /* put the dot back */
} else {
if(_tcsicmp(c1, _T_ECLIPSE("jar")) == 0)
version->qualifier = 0;
else
version->qualifier = _tcsdup(c1);
}
break;
}
c1 = c2;
i++;
}
free(copy);
return version;
}
static int compareVersions(const _TCHAR* str1, const _TCHAR* str2) {
int result = 0, i = 0;
Version *v1 = parseVersion(str1);
Version *v2 = parseVersion(str2);
while (result == 0 && i < 3) {
result = v1->segment[i] - v2->segment[i];
i++;
}
if(result == 0) {
_TCHAR * q1 = v1->qualifier ? v1->qualifier : _T_ECLIPSE("");
_TCHAR * q2 = v2->qualifier ? v2->qualifier : _T_ECLIPSE("");
result = _tcscmp(q1, q2);
}
freeVersion(v1);
freeVersion(v2);
return result;
}
/**
* Convert a wide string to a narrow one
* Caller must free the null terminated string returned.
*/
char *toNarrow(const _TCHAR* src)
{
#ifdef UNICODE
int byteCount = WideCharToMultiByte (CP_ACP, 0, (wchar_t *)src, -1, NULL, 0, NULL, NULL);
char *dest = malloc(byteCount+1);
dest[byteCount] = 0;
WideCharToMultiByte (CP_ACP, 0, (wchar_t *)src, -1, dest, byteCount, NULL, NULL);
return dest;
#else
return (char*)_tcsdup(src);
#endif
}
/**
* Set an environment variable.
* Solaris versions <= Solaris 9 did not know setenv in libc,
* so emulate it here.
*/
#if defined(SOLARIS) || defined(HPUX)
int setenv (const char *name, const char *value, int replace)
{
int namelen, valuelen, rc;
char *var;
if (replace == 0) {
const char *oldval = getenv(name);
if (oldval != NULL) {
return 0;
}
}
namelen = strlen(name);
valuelen = strlen(value);
var = malloc( (namelen + valuelen + 2) * sizeof(char) );
if (var == NULL) {
return -1;
}
/* Use strncpy as protection, in case a thread modifies var
* after we obtained its length */
strncpy(var, name, namelen);
var[namelen] = '=';
strncpy( &var[namelen + 1], value, valuelen);
var[namelen + valuelen + 1] = '\0';
rc = putenv(var);
if (rc != 0) rc = -1; /*putenv returns non-zero on error; setenv -1*/
return rc;
}
#endif
/*
* Find the absolute pathname to where a command resides.
*
* The string returned by the function must be freed.
*/
#define EXTRA 20
_TCHAR* findCommand( _TCHAR* command )
{
return findSymlinkCommand( command, 1 );
}
_TCHAR* findSymlinkCommand( _TCHAR* command, int resolve )
{
_TCHAR* cmdPath;
size_t length;
_TCHAR* ch;
_TCHAR* dir;
_TCHAR* path;
struct _stat stats;
/* If the command was an abolute pathname, use it as is. */
if (IS_ABSOLUTE(command))
{
length = _tcslen( command );
cmdPath = malloc( (length + EXTRA) * sizeof(_TCHAR) ); /* add extra space for a possible ".exe" extension */
_tcscpy( cmdPath, command );
}
else
{
/* If the command string contains a path separator */
if (firstDirSeparator( command ) != NULL)
{
/* It must be relative to the current directory. */
length = MAX_PATH_LENGTH + EXTRA + _tcslen( command );
cmdPath = malloc( length * sizeof (_TCHAR));
_tgetcwd( cmdPath, length );
length = _tcslen(cmdPath);
if (!IS_DIR_SEPARATOR(cmdPath[ length - 1 ]))
{
cmdPath[ length ] = dirSeparator;
cmdPath[ length+1 ] = _T_ECLIPSE('\0');
}
_tcscat( cmdPath, command );
}
/* else the command must be in the PATH somewhere */
else
{
/* Get the directory PATH where executables reside. */
path = _tgetenv( _T_ECLIPSE("PATH") );
#ifdef _WIN32
/* on windows, prepend the current directory */
if (path == NULL)
path = _T_ECLIPSE("");
ch = malloc((_tcslen(path) + MAX_PATH_LENGTH + 2) * sizeof(_TCHAR));
_tgetcwd( ch, MAX_PATH_LENGTH );
length = _tcslen(ch);
ch[length] = pathSeparator;
_tcscpy(&ch[length + 1], path);
path = ch;
#endif
if (!path)
{
return NULL;
}
else
{
length = _tcslen( path ) + _tcslen( command ) + MAX_PATH_LENGTH;
cmdPath = malloc( length * sizeof(_TCHAR));
/* Foreach directory in the PATH */
dir = path;
while (dir != NULL && *dir != _T_ECLIPSE('\0'))
{
ch = _tcschr( dir, pathSeparator );
if (ch == NULL)
{
_tcscpy( cmdPath, dir );
}
else
{
length = ch - dir;
_tcsncpy( cmdPath, dir, length );
cmdPath[ length ] = _T_ECLIPSE('\0');
ch++;
}
dir = ch; /* advance for the next iteration */
#ifdef _WIN32
/* Remove quotes */
if (_tcschr( cmdPath, _T_ECLIPSE('"') ) != NULL)
{
size_t i = 0, j = 0;
_TCHAR c;
length = _tcslen( cmdPath );
while (i < length) {
c = cmdPath[ i++ ];
if (c == _T_ECLIPSE('"')) continue;
cmdPath[ j++ ] = c;
}
cmdPath[ j ] = _T_ECLIPSE('\0');
}
#endif
/* Determine if the executable resides in this directory. */
if (cmdPath[0] == _T_ECLIPSE('.') &&
(_tcslen(cmdPath) == 1 || (_tcslen(cmdPath) == 2 && IS_DIR_SEPARATOR(cmdPath[1]))))
{
_tgetcwd( cmdPath, MAX_PATH_LENGTH );
}
length = _tcslen(cmdPath);
if (!IS_DIR_SEPARATOR(cmdPath[ length - 1 ]))
{
cmdPath[ length ] = dirSeparator;
cmdPath[ length+1 ] = _T_ECLIPSE('\0');
}
_tcscat( cmdPath, command );
/* If the file is not a directory and can be executed */
if (_tstat( cmdPath, &stats ) == 0 && (stats.st_mode & S_IFREG) != 0)
{
/* Stop searching */
dir = NULL;
}
}
}
}
}
#ifdef _WIN32
/* If the command does not exist */
if (_tstat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
{
/* If the command does not end with .exe, append it an try again. */
length = _tcslen( cmdPath );
if (length > 4 && _tcsicmp( &cmdPath[ length - 4 ], _T_ECLIPSE(".exe") ) != 0)
_tcscat( cmdPath, _T_ECLIPSE(".exe") );
}
#endif
/* Verify the resulting command actually exists. */
if (_tstat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
{
free( cmdPath );
cmdPath = NULL;
return cmdPath;
}
if (resolve) {
ch = resolveSymlinks(cmdPath);
if (ch != cmdPath) {
free(cmdPath);
cmdPath = ch;
}
}
return cmdPath;
}
#if !defined(_WIN32) && !defined(MACOSX)
char * resolveSymlinks( char * path ) {
char * ch, *buffer;
if(path == NULL)
return path;
/* resolve symlinks */
ch = path;
buffer = malloc(PATH_MAX);
path = realpath(path, buffer);
if (path != buffer)
free(buffer);
if (path == NULL)
return ch; /* failed to resolve the links, return original path */
return path;
}
#endif
#ifdef _WIN32
static int filter(_TCHAR* candidate, int isFolder) {
#else
#ifdef MACOSX
static int filter(struct dirent *dir, int isFolder) {
#else
static int filter(const struct dirent *dir, int isFolder) {
#endif
char * candidate = (char *)dir->d_name;
#endif
_TCHAR *lastDot, *lastUnderscore;
int result;
if(_tcslen(candidate) <= prefixLength)
return 0;
if (_tcsncmp(candidate, filterPrefix, prefixLength) != 0 || candidate[prefixLength] != _T_ECLIPSE('_'))
return 0;
candidate = _tcsdup(candidate);
/* remove trailing .jar and .zip extensions, leave other extensions because we need the '.' */
lastDot = _tcsrchr(candidate, _T_ECLIPSE('.'));
if (!isFolder && lastDot != NULL && (_tcscmp(lastDot, _T_ECLIPSE(".jar")) == 0 || _tcscmp(lastDot, _T_ECLIPSE(".zip")) == 0)) {
*lastDot = 0;
lastDot = _tcsrchr(candidate, _T_ECLIPSE('.'));
}
if (lastDot < &candidate[prefixLength]) {
free(candidate);
return 0;
}
lastUnderscore = _tcsrchr(candidate, _T_ECLIPSE('_'));
/* get past all the '_' that are part of the qualifier */
while(lastUnderscore > lastDot) {
*lastUnderscore = 0;
lastUnderscore = _tcsrchr(candidate, _T_ECLIPSE('_'));
}
/* is this the underscore at the end of the prefix? */
result = (lastUnderscore == &candidate[prefixLength]);
free(candidate);
return result;
}
/*
* Looks for files of the form /path/prefix_version.<extension> and returns the full path to
* the file with the largest version number
*/
_TCHAR* findFile( _TCHAR* path, _TCHAR* prefix)
{
struct _stat stats;
size_t pathLength;
_TCHAR* candidate = NULL;
_TCHAR* result = NULL;
#ifdef _WIN32
_TCHAR* fileName = NULL;
WIN32_FIND_DATA data;
HANDLE handle;
#else
DIR *dir = NULL;
struct dirent * entry = NULL;
#endif
path = _tcsdup(path);
pathLength = _tcslen(path);
/* strip dirSeparators off the end */
while (IS_DIR_SEPARATOR(path[pathLength - 1])) {
path[--pathLength] = 0;
}
/* does path exist? */
if( _tstat(path, &stats) != 0 ) {
free(path);
return NULL;
}
filterPrefix = prefix;
prefixLength = _tcslen(prefix);
#ifdef _WIN32
fileName = malloc( (_tcslen(path) + 1 + _tcslen(prefix) + 3) * sizeof(_TCHAR));
_stprintf(fileName, _T_ECLIPSE("%s%c%s_*"), path, dirSeparator, prefix);
handle = FindFirstFile(fileName, &data);
if(handle != INVALID_HANDLE_VALUE) {
if (filter(data.cFileName, isFolder(path, data.cFileName)))
candidate = _tcsdup(data.cFileName);
while(FindNextFile(handle, &data) != 0) {
if (filter(data.cFileName, isFolder(path, data.cFileName))) {
if (candidate == NULL) {
candidate = _tcsdup(data.cFileName);
} else if( compareVersions(candidate + prefixLength + 1, data.cFileName + prefixLength + 1) < 0) {
/* compare, take the highest version */
free(candidate);
candidate = _tcsdup(data.cFileName);
}
}
}
FindClose(handle);
}
#else
if ((dir = opendir(path)) == NULL) {
free(path);
return NULL;
}
while ((entry = readdir(dir)) != NULL) {
if (filter(entry, isFolder(path, entry->d_name))) {
if (candidate == NULL) {
candidate = _tcsdup(entry->d_name);
} else if (compareVersions(candidate + prefixLength + 1, entry->d_name + prefixLength + 1) < 0) {
free(candidate);
candidate = _tcsdup(entry->d_name);
}
}
}
closedir(dir);
#endif
if(candidate != NULL) {
result = malloc((pathLength + 1 + _tcslen(candidate) + 1) * sizeof(_TCHAR));
_tcscpy(result, path);
result[pathLength] = dirSeparator;
result[pathLength + 1] = 0;
_tcscat(result, candidate);
free(candidate);
}
free(path);
return result;
}
int isFolder(const _TCHAR* path, const _TCHAR* entry) {
int result = 0;
struct _stat stats;
_TCHAR * fullPath = malloc((_tcslen(path) + _tcslen(entry) + 2) * sizeof(_TCHAR));
_stprintf(fullPath, _T_ECLIPSE("%s%c%s"), path, dirSeparator, entry);
result = _tstat(fullPath, &stats);
free(fullPath);
return (result == 0 && (stats.st_mode & S_IFDIR) != 0);
}
/*
* If path is relative, attempt to make it absolute by
* 1) check relative to working directory
* 2) check relative to provided programDir
* If reverseOrder, then check the programDir before the working dir
*/
_TCHAR* checkPath( _TCHAR* path, _TCHAR* programDir, int reverseOrder )
{
int cwdLength = MAX_PATH_LENGTH;
int i;
_TCHAR * workingDir, * buffer, * result = NULL;
_TCHAR * paths[2];
struct _stat stats;
/* If the command was an abolute pathname, use it as is. */
if (IS_ABSOLUTE(path)) {
return path;
}
/* get the current working directory */
workingDir = malloc(cwdLength * sizeof(_TCHAR));
while ( _tgetcwd( workingDir, cwdLength ) == NULL ){
if (errno == ERANGE) {
/* ERANGE : the buffer isn't big enough, allocate more memory */
cwdLength *= 2;
workingDir = realloc(workingDir, cwdLength * sizeof(_TCHAR));
continue;
} else {
/* some other error occurred, perhaps ENOENT (directory has been unlinked) */
/* the contents of workingDir are undefined, set it to empty, we will end up testing against root */
workingDir[0] = _T_ECLIPSE('\0');
break;
}
}
paths[0] = reverseOrder ? programDir : workingDir;
paths[1] = reverseOrder ? workingDir : programDir;
/* just make a buffer big enough to hold everything */
buffer = malloc((_tcslen(paths[0]) + _tcslen(paths[1]) + _tcslen(path) + 2) * sizeof(_TCHAR));
for ( i = 0; i < 2; i++ ) {
if (_tcslen(paths[i]) == 0)
continue;
_stprintf(buffer, _T_ECLIPSE("%s%c%s"), paths[i], dirSeparator, path);
if (_tstat(buffer, &stats) == 0) {
result = _tcsdup(buffer);
break;
}
}
free(buffer);
free(workingDir);
/* if we found something, return it, otherwise, return the original */
return result != NULL ? result : path;
}
_TCHAR * lastDirSeparator(_TCHAR* str) {
#ifndef _WIN32
return _tcsrchr(str, dirSeparator);
#else
int i = -1;
_TCHAR * c = NULL;
while (str[++i] != 0) {
if (str[i] == _T_ECLIPSE('\\') || str[i] == _T_ECLIPSE('/'))
c = &str[i];
}
return c;
#endif
}
_TCHAR * firstDirSeparator(_TCHAR* str) {
#ifdef _WIN32
return _tcspbrk(str, _T_ECLIPSE("\\/"));
#else
return _tcschr(str, dirSeparator);
#endif
}