| /******************************************************************************* |
| * Copyright (c) 2006, 2011 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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); |
| _TCHAR* osArchArg = _T_ECLIPSE(DEFAULT_OS_ARCH); |
| _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 (_tcslen(cmdPath) == 0 || /*an empty path entry is treated as '.' */ |
| (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 |
| } |