1 /*******************************************************************************
2  * Copyright (c) 2006, 2011 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Andrew Niefer
14  * Martin Oberhuber (Wind River) - [176805] Support Solaris9 by adding setenv()
15  *******************************************************************************/
16 
17 #include "eclipseCommon.h"
18 #include "eclipseUnicode.h"
19 
20 #ifdef _WIN32
21 #include <direct.h>
22 #include <windows.h>
23 #else
24 #include <unistd.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <limits.h>
28 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33 
34 /* Global Variables */
35 _TCHAR* osArg        = _T_ECLIPSE(DEFAULT_OS);
36 _TCHAR* osArchArg    = _T_ECLIPSE(DEFAULT_OS_ARCH);
37 _TCHAR* wsArg        = _T_ECLIPSE(DEFAULT_WS);	/* the SWT supported GUI to be used */
38 
39 /* Local Variables */
40 static _TCHAR* filterPrefix = NULL;  /* prefix for the find files filter */
41 static size_t  prefixLength = 0;
42 
43 static int isFolder(const _TCHAR* path, const _TCHAR* entry);
44 
45 typedef struct {
46 	int segment[3];
47 	_TCHAR * qualifier;
48 } Version;
49 
freeVersion(Version * version)50 static void freeVersion(Version *version)
51 {
52 	if(version->qualifier)
53 		free(version->qualifier);
54 	free(version);
55 }
56 
parseVersion(const _TCHAR * str)57 static Version* parseVersion(const _TCHAR * str) {
58 	_TCHAR *copy;
59 	_TCHAR *c1, *c2 = NULL;
60 	int i = 0;
61 
62 	Version *version = malloc(sizeof(Version));
63 	memset(version, 0, sizeof(Version));
64 
65 	c1 = copy = _tcsdup(str);
66 	while (c1 && *c1 != 0)
67 	{
68 		if (i < 3) {
69 			version->segment[i] = (int)_tcstol(c1, &c2, 10);
70 			/* if the next character is not '.', then we couldn't
71 			 * parse as a int, the remainder is not valid (or we are at the end)*/
72 			if (*c2 && *c2 != _T_ECLIPSE('.'))
73 				break;
74 			c2++; /* increment past the . */
75 		} else {
76 			c2 = _tcschr(c1, _T_ECLIPSE('.'));
77 			if(c2 != NULL) {
78 				*c2 = 0;
79 				version->qualifier = _tcsdup(c1);
80 				*c2 = _T_ECLIPSE('.'); /* put the dot back */
81 			} else {
82 				if(_tcsicmp(c1, _T_ECLIPSE("jar")) == 0)
83 					version->qualifier = 0;
84 				else
85 					version->qualifier = _tcsdup(c1);
86 			}
87 			break;
88 		}
89 		c1 = c2;
90 		i++;
91 	}
92 	free(copy);
93 	return version;
94 }
95 
compareVersions(const _TCHAR * str1,const _TCHAR * str2)96 static int compareVersions(const _TCHAR* str1, const _TCHAR* str2) {
97 	int result = 0, i = 0;
98 	Version *v1 = parseVersion(str1);
99 	Version *v2 = parseVersion(str2);
100 
101 	while (result == 0 && i < 3) {
102 		result = v1->segment[i] - v2->segment[i];
103 		i++;
104 	}
105 	if(result == 0) {
106 		_TCHAR * q1 = v1->qualifier ? v1->qualifier : _T_ECLIPSE("");
107 		_TCHAR * q2 = v2->qualifier ? v2->qualifier : _T_ECLIPSE("");
108 		result =  _tcscmp(q1, q2);
109 	}
110 
111 	freeVersion(v1);
112 	freeVersion(v2);
113 	return result;
114 }
115 
116 /**
117  * Convert a wide string to a narrow one
118  * Caller must free the null terminated string returned.
119  */
toNarrow(const _TCHAR * src)120 char *toNarrow(const _TCHAR* src)
121 {
122 #ifdef UNICODE
123 	int byteCount = WideCharToMultiByte (CP_ACP, 0, (wchar_t *)src, -1, NULL, 0, NULL, NULL);
124 	char *dest = malloc(byteCount+1);
125 	dest[byteCount] = 0;
126 	WideCharToMultiByte (CP_ACP, 0, (wchar_t *)src, -1, dest, byteCount, NULL, NULL);
127 	return dest;
128 #else
129 	return (char*)_tcsdup(src);
130 #endif
131 }
132 
133 
134 /**
135  * Set an environment variable.
136  * Solaris versions <= Solaris 9 did not know setenv in libc,
137  * so emulate it here.
138  */
139 #if defined(SOLARIS) || defined(HPUX)
setenv(const char * name,const char * value,int replace)140 int setenv (const char *name, const char *value, int replace)
141 {
142 	int namelen, valuelen, rc;
143 	char *var;
144 	if (replace == 0) {
145 		const char *oldval = getenv(name);
146 		if (oldval != NULL) {
147 			return 0;
148 	    }
149 	}
150 	namelen = strlen(name);
151 	valuelen = strlen(value);
152 	var = malloc( (namelen + valuelen + 2) * sizeof(char) );
153 	if (var == NULL) {
154 		return -1;
155 	}
156 	/* Use strncpy as protection, in case a thread modifies var
157 	 * after we obtained its length */
158 	strncpy(var, name, namelen);
159 	var[namelen] = '=';
160 	strncpy( &var[namelen + 1], value, valuelen);
161 	var[namelen + valuelen + 1] = '\0';
162 	rc = putenv(var);
163 	if (rc != 0) rc = -1; /*putenv returns non-zero on error; setenv -1*/
164 	return rc;
165 }
166 #endif
167 
168  /*
169  * Find the absolute pathname to where a command resides.
170  *
171  * The string returned by the function must be freed.
172  */
173 #define EXTRA 20
findCommand(_TCHAR * command)174 _TCHAR* findCommand( _TCHAR* command )
175 {
176 	return findSymlinkCommand( command, 1 );
177 }
178 
findSymlinkCommand(_TCHAR * command,int resolve)179 _TCHAR* findSymlinkCommand( _TCHAR* command, int resolve )
180 {
181     _TCHAR*  cmdPath;
182     size_t   length;
183     _TCHAR*  ch;
184     _TCHAR*  dir;
185     _TCHAR*  path;
186     struct _stat stats;
187 
188     /* If the command was an abolute pathname, use it as is. */
189     if (IS_ABSOLUTE(command))
190     {
191         length = _tcslen( command );
192         cmdPath = malloc( (length + EXTRA) * sizeof(_TCHAR) ); /* add extra space for a possible ".exe" extension */
193         _tcscpy( cmdPath, command );
194     }
195 
196     else
197     {
198         /* If the command string contains a path separator */
199         if (firstDirSeparator( command ) != NULL)
200         {
201             /* It must be relative to the current directory. */
202             length = MAX_PATH_LENGTH + EXTRA + _tcslen( command );
203             cmdPath = malloc( length * sizeof (_TCHAR));
204             _tgetcwd( cmdPath, length );
205             length = _tcslen(cmdPath);
206             if (!IS_DIR_SEPARATOR(cmdPath[ length - 1 ]))
207             {
208                 cmdPath[ length ] = dirSeparator;
209                 cmdPath[ length+1 ] = _T_ECLIPSE('\0');
210             }
211             _tcscat( cmdPath, command );
212         }
213 
214         /* else the command must be in the PATH somewhere */
215         else
216         {
217             /* Get the directory PATH where executables reside. */
218             path = _tgetenv( _T_ECLIPSE("PATH") );
219 #ifdef _WIN32
220             /* on windows, prepend the current directory */
221             if (path == NULL)
222             	path = _T_ECLIPSE("");
223             ch = malloc((_tcslen(path) + MAX_PATH_LENGTH + 2) * sizeof(_TCHAR));
224             _tgetcwd( ch, MAX_PATH_LENGTH );
225             length = _tcslen(ch);
226             ch[length] = pathSeparator;
227             _tcscpy(&ch[length + 1], path);
228             path = ch;
229 #endif
230             if (!path)
231             {
232 	            return NULL;
233             }
234             else
235             {
236 	            length = _tcslen( path ) + _tcslen( command ) + MAX_PATH_LENGTH;
237 	            cmdPath = malloc( length * sizeof(_TCHAR));
238 
239 	            /* Foreach directory in the PATH */
240 	            dir = path;
241 	            while (dir != NULL && *dir != _T_ECLIPSE('\0'))
242 	            {
243 	                ch = _tcschr( dir, pathSeparator );
244 	                if (ch == NULL)
245 	                {
246 	                    _tcscpy( cmdPath, dir );
247 	                }
248 	                else
249 	                {
250 	                    length = ch - dir;
251 	                    _tcsncpy( cmdPath, dir, length );
252 	                    cmdPath[ length ] = _T_ECLIPSE('\0');
253 	                    ch++;
254 	                }
255 	                dir = ch; /* advance for the next iteration */
256 
257 #ifdef _WIN32
258                     /* Remove quotes */
259 	                if (_tcschr( cmdPath, _T_ECLIPSE('"') ) != NULL)
260 	                {
261 	                    size_t i = 0, j = 0;
262 	                    _TCHAR c;
263 	                    length = _tcslen( cmdPath );
264 	                    while (i < length) {
265 	                        c = cmdPath[ i++ ];
266 	                        if (c == _T_ECLIPSE('"')) continue;
267 	                        cmdPath[ j++ ] = c;
268 	                    }
269 	                    cmdPath[ j ] = _T_ECLIPSE('\0');
270 	                }
271 #endif
272 	                /* Determine if the executable resides in this directory. */
273 	                if (_tcslen(cmdPath) == 0 || /*an empty path entry is treated as '.' */
274 	                	(cmdPath[0] == _T_ECLIPSE('.') && (_tcslen(cmdPath) == 1 || (_tcslen(cmdPath) == 2 && IS_DIR_SEPARATOR(cmdPath[1])))))
275 	                {
276 	                	_tgetcwd( cmdPath, MAX_PATH_LENGTH );
277 	                }
278 	                length = _tcslen(cmdPath);
279 	                if (!IS_DIR_SEPARATOR(cmdPath[ length - 1 ]))
280 	                {
281 	                    cmdPath[ length ] = dirSeparator;
282 	                    cmdPath[ length+1 ] = _T_ECLIPSE('\0');
283 	                }
284 	                _tcscat( cmdPath, command );
285 
286 	                /* If the file is not a directory and can be executed */
287 	                if (_tstat( cmdPath, &stats ) == 0 && (stats.st_mode & S_IFREG) != 0)
288 	                {
289 	                    /* Stop searching */
290 	                    dir = NULL;
291 	                }
292 	            }
293 	        }
294         }
295     }
296 
297 #ifdef _WIN32
298 	/* If the command does not exist */
299     if (_tstat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
300     {
301     	/* If the command does not end with .exe, append it an try again. */
302     	length = _tcslen( cmdPath );
303     	if (length > 4 && _tcsicmp( &cmdPath[ length - 4 ], _T_ECLIPSE(".exe") ) != 0)
304     	    _tcscat( cmdPath, _T_ECLIPSE(".exe") );
305     }
306 #endif
307 
308     /* Verify the resulting command actually exists. */
309     if (_tstat( cmdPath, &stats ) != 0 || (stats.st_mode & S_IFREG) == 0)
310     {
311         free( cmdPath );
312         cmdPath = NULL;
313         return cmdPath;
314     }
315 
316 	if (resolve) {
317 		ch = resolveSymlinks(cmdPath);
318 		if (ch != cmdPath) {
319 			free(cmdPath);
320 			cmdPath = ch;
321 		}
322 	}
323 	return cmdPath;
324 }
325 
326 #if !defined(_WIN32) && !defined(MACOSX)
resolveSymlinks(char * path)327 char * resolveSymlinks( char * path ) {
328 	char * ch, *buffer;
329 	if(path == NULL)
330 		return path;
331 	/* resolve symlinks */
332 	ch = path;
333 	buffer = malloc(PATH_MAX);
334     path = realpath(path, buffer);
335     if (path != buffer)
336     	free(buffer);
337     if (path == NULL)
338     	return ch; /* failed to resolve the links, return original path */
339     return path;
340 }
341 #endif
342 
343 #ifdef _WIN32
filter(_TCHAR * candidate,int isFolder)344 static int filter(_TCHAR* candidate, int isFolder) {
345 #else
346 #ifdef MACOSX
347 static int filter(struct dirent *dir, int isFolder) {
348 #else
349 static int filter(const struct dirent *dir, int isFolder) {
350 #endif
351 	char * candidate = (char *)dir->d_name;
352 #endif
353 	_TCHAR *lastDot, *lastUnderscore;
354 	int result;
355 
356 	if(_tcslen(candidate) <= prefixLength)
357 		return 0;
358 	if (_tcsncmp(candidate, filterPrefix, prefixLength) != 0 ||	candidate[prefixLength] != _T_ECLIPSE('_'))
359 		return 0;
360 
361 	candidate = _tcsdup(candidate);
362 
363 	/* remove trailing .jar and .zip extensions, leave other extensions because we need the '.'  */
364 	lastDot = _tcsrchr(candidate, _T_ECLIPSE('.'));
365 	if (!isFolder && lastDot != NULL && (_tcscmp(lastDot, _T_ECLIPSE(".jar")) == 0 || _tcscmp(lastDot, _T_ECLIPSE(".zip")) == 0)) {
366 		*lastDot = 0;
367 		lastDot = _tcsrchr(candidate, _T_ECLIPSE('.'));
368 	}
369 
370 	if (lastDot < &candidate[prefixLength]) {
371 		free(candidate);
372 		return 0;
373 	}
374 
375 	lastUnderscore = _tcsrchr(candidate, _T_ECLIPSE('_'));
376 
377 	/* get past all the '_' that are part of the qualifier */
378 	while(lastUnderscore > lastDot) {
379 		*lastUnderscore = 0;
380 		lastUnderscore = _tcsrchr(candidate, _T_ECLIPSE('_'));
381 	}
382 	/* is this the underscore at the end of the prefix? */
383 	result = (lastUnderscore == &candidate[prefixLength]);
384 	free(candidate);
385 	return result;
386 }
387 
388  /*
389  * Looks for files of the form /path/prefix_version.<extension> and returns the full path to
390  * the file with the largest version number
391  */
392 _TCHAR* findFile( _TCHAR* path, _TCHAR* prefix)
393 {
394 	struct _stat stats;
395 	size_t pathLength;
396 	_TCHAR* candidate = NULL;
397 	_TCHAR* result = NULL;
398 
399 #ifdef _WIN32
400 	_TCHAR* fileName = NULL;
401 	WIN32_FIND_DATA data;
402 	HANDLE handle;
403 #else
404 	DIR *dir = NULL;
405 	struct dirent * entry = NULL;
406 #endif
407 
408 	path = _tcsdup(path);
409 	pathLength = _tcslen(path);
410 
411 	/* strip dirSeparators off the end */
412 	while (IS_DIR_SEPARATOR(path[pathLength - 1])) {
413 		path[--pathLength] = 0;
414 	}
415 
416 	/* does path exist? */
417 	if( _tstat(path, &stats) != 0 ) {
418 		free(path);
419 		return NULL;
420 	}
421 
422 	filterPrefix = prefix;
423 	prefixLength = _tcslen(prefix);
424 #ifdef _WIN32
425 	fileName = malloc( (_tcslen(path) + 1 + _tcslen(prefix) + 3) * sizeof(_TCHAR));
426 	_stprintf(fileName, _T_ECLIPSE("%s%c%s_*"), path, dirSeparator, prefix);
427 
428 	handle = FindFirstFile(fileName, &data);
429 	if(handle != INVALID_HANDLE_VALUE) {
430 		if (filter(data.cFileName, isFolder(path, data.cFileName)))
431 			candidate = _tcsdup(data.cFileName);
432 		while(FindNextFile(handle, &data) != 0) {
433 			if (filter(data.cFileName, isFolder(path, data.cFileName))) {
434 				if (candidate == NULL) {
435 					candidate = _tcsdup(data.cFileName);
436 				} else if( compareVersions(candidate + prefixLength + 1, data.cFileName + prefixLength + 1) < 0) {
437 					/* compare, take the highest version */
438 					free(candidate);
439 					candidate = _tcsdup(data.cFileName);
440 				}
441 			}
442 		}
443 		FindClose(handle);
444 	}
445 #else
446 	if ((dir = opendir(path)) == NULL) {
447 		free(path);
448 		return NULL;
449 	}
450 
451 	while ((entry = readdir(dir)) != NULL) {
452 		if (filter(entry, isFolder(path, entry->d_name))) {
453 			if (candidate == NULL) {
454 				candidate = _tcsdup(entry->d_name);
455 			} else if (compareVersions(candidate + prefixLength + 1, entry->d_name + prefixLength + 1) < 0) {
456 				free(candidate);
457 				candidate = _tcsdup(entry->d_name);
458 			}
459 		}
460 	}
461 	closedir(dir);
462 #endif
463 
464 	if(candidate != NULL) {
465 		result = malloc((pathLength + 1 + _tcslen(candidate) + 1) * sizeof(_TCHAR));
466 		_tcscpy(result, path);
467 		result[pathLength] = dirSeparator;
468 		result[pathLength + 1] = 0;
469 		_tcscat(result, candidate);
470 		free(candidate);
471 	}
472 	free(path);
473 	return result;
474 }
475 
476 int isFolder(const _TCHAR* path, const _TCHAR* entry) {
477 	int result = 0;
478 	struct _stat stats;
479 	_TCHAR * fullPath = malloc((_tcslen(path) + _tcslen(entry) + 2) * sizeof(_TCHAR));
480 	_stprintf(fullPath, _T_ECLIPSE("%s%c%s"), path, dirSeparator, entry);
481 
482 	result = _tstat(fullPath, &stats);
483 	free(fullPath);
484 	return (result == 0 && (stats.st_mode & S_IFDIR) != 0);
485 }
486 
487 /*
488  * If path is relative, attempt to make it absolute by
489  * 1) check relative to working directory
490  * 2) check relative to provided programDir
491  * If reverseOrder, then check the programDir before the working dir
492  */
493 _TCHAR* checkPath( _TCHAR* path, _TCHAR* programDir, int reverseOrder )
494 {
495 	int cwdLength = MAX_PATH_LENGTH;
496 	int i;
497 	_TCHAR * workingDir, * buffer, * result = NULL;
498 	_TCHAR * paths[2];
499 	struct _stat stats;
500 
501 	/* If the command was an abolute pathname, use it as is. */
502     if (IS_ABSOLUTE(path)) {
503     	return path;
504     }
505 
506     /* get the current working directory */
507     workingDir = malloc(cwdLength * sizeof(_TCHAR));
508     while ( _tgetcwd( workingDir, cwdLength ) == NULL ){
509     	if (errno == ERANGE) {
510     		/* ERANGE : the buffer isn't big enough, allocate more memory */
511 			cwdLength *= 2;
512 			workingDir = realloc(workingDir, cwdLength * sizeof(_TCHAR));
513 			continue;
514     	} else {
515     		/* some other error occurred, perhaps ENOENT (directory has been unlinked) */
516     		/* the contents of workingDir are undefined, set it to empty, we will end up testing against root */
517     		workingDir[0] = _T_ECLIPSE('\0');
518     		break;
519     	}
520     }
521 
522     paths[0] = reverseOrder ? programDir : workingDir;
523     paths[1] = reverseOrder ? workingDir : programDir;
524 
525     /* just make a buffer big enough to hold everything */
526     buffer = malloc((_tcslen(paths[0]) + _tcslen(paths[1]) + _tcslen(path) + 2) * sizeof(_TCHAR));
527     for ( i = 0; i < 2; i++ ) {
528     	if (_tcslen(paths[i]) == 0)
529     		continue;
530     	_stprintf(buffer, _T_ECLIPSE("%s%c%s"), paths[i], dirSeparator, path);
531     	if (_tstat(buffer, &stats) == 0) {
532     		result = _tcsdup(buffer);
533     		break;
534     	}
535     }
536 
537     free(buffer);
538     free(workingDir);
539 
540     /* if we found something, return it, otherwise, return the original */
541     return result != NULL ? result : path;
542 }
543 
544 _TCHAR * lastDirSeparator(_TCHAR* str) {
545 #ifndef _WIN32
546 	return _tcsrchr(str, dirSeparator);
547 #else
548 	int i = -1;
549 	_TCHAR * c = NULL;
550 	while (str[++i] != 0) {
551 		if (str[i] == _T_ECLIPSE('\\') || str[i] == _T_ECLIPSE('/'))
552 			c = &str[i];
553 	}
554 	return c;
555 #endif
556 }
557 
558 _TCHAR * firstDirSeparator(_TCHAR* str) {
559 #ifdef _WIN32
560 	return _tcspbrk(str, _T_ECLIPSE("\\/"));
561 #else
562 	return _tcschr(str, dirSeparator);
563 #endif
564 }
565