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