1212#include "common.h"
1313
1414// the below is only necessary when we need to emulate execvpe.
15- #if !defined(HAVE_execvpe )
15+ #if !defined(HAVE_EXECVPE )
1616
17- /* Return true if the given file exists and is an executable. */
18- static bool is_executable (const char * path ) {
19- return access (path , X_OK ) == 0 ;
17+ /* A quick check for whether the given path is absolute. */
18+ static bool is_absolute (const char * path ) {
19+ return path [0 ] == '/' ;
20+ }
21+
22+ static char * concat_paths (const char * path1 , const char * path2 ) {
23+ if (is_absolute (path2 )) {
24+ return strdup (path2 );
25+ } else {
26+ int len = strlen (path1 ) + 1 + strlen (path2 ) + 1 ;
27+ char * tmp = malloc (len );
28+ int ret = snprintf (tmp , len , "%s/%s" , path1 , path2 );
29+ if (ret < 0 ) {
30+ free (tmp );
31+ return NULL ;
32+ }
33+ return tmp ;
34+ }
35+ }
36+
37+ /* Return true if the given file exists and is an executable, optionally
38+ * relative to the given working directory.
39+ */
40+ static bool is_executable (char * working_dir , const char * path ) {
41+ if (working_dir && !is_absolute (path )) {
42+ char * tmp = concat_paths (working_dir , path );
43+ bool ret = access (tmp , X_OK ) == 0 ;
44+ free (tmp );
45+ return ret ;
46+ } else {
47+ return access (path , X_OK ) == 0 ;
48+ }
2049}
2150
2251/* Find an executable with the given filename in the given search path. The
2352 * result must be freed by the caller. Returns NULL if a matching file is not
2453 * found.
2554 */
26- static char * find_in_search_path (char * search_path , const char * filename ) {
55+ static char * find_in_search_path (char * working_dir , char * search_path , const char * filename ) {
56+ int workdir_len = strlen (working_dir );
2757 const int filename_len = strlen (filename );
2858 char * tokbuf ;
2959 char * path = strtok_r (search_path , ":" , & tokbuf );
@@ -34,13 +64,21 @@ static char *find_in_search_path(char *search_path, const char *filename) {
3464#pragma GCC diagnostic push
3565#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
3666#endif
37- const int tmp_len = filename_len + 1 + strlen (path ) + 1 ;
67+ char * tmp ;
68+ if (is_absolute (path )) {
69+ const int tmp_len = strlen (path ) + 1 + filename_len + 1 ;
70+ tmp = malloc (tmp_len );
71+ snprintf (tmp , tmp_len , "%s/%s" , path , filename );
72+ } else {
73+ const int tmp_len = workdir_len + 1 + strlen (path ) + 1 + filename_len + 1 ;
74+ tmp = malloc (tmp_len );
75+ snprintf (tmp , tmp_len , "%s/%s/%s" , working_dir , path , filename );
76+ }
3877#if defined(__GNUC__ ) && __GNUC__ == 6 && __GNUC_MINOR__ == 3
3978#pragma GCC diagnostic pop
4079#endif
41- char * tmp = malloc (tmp_len );
42- snprintf (tmp , tmp_len , "%s/%s" , path , filename );
43- if (is_executable (tmp )) {
80+
81+ if (is_executable (working_dir , tmp )) {
4482 return tmp ;
4583 } else {
4684 free (tmp );
@@ -74,15 +112,36 @@ static char *get_executable_search_path(void) {
74112 return strdup (":" );
75113}
76114
77- /* Find the given executable in the executable search path. */
78- char * find_executable (char * filename ) {
79- /* If it's an absolute or relative path name, it's easy. */
80- if (strchr (filename , '/' ) && is_executable (filename )) {
115+ /* Find the given executable in the executable search path relative to
116+ * workingDirectory (or the current directory, if NULL).
117+ * N.B. the caller is responsible for free()ing the result.
118+ */
119+ char * find_executable (char * working_dir , char * filename ) {
120+ /* Drop trailing slash from working directory if necessary */
121+ if (working_dir ) {
122+ int workdir_len = strlen (working_dir );
123+ if (working_dir [workdir_len - 1 ] == '/' ) {
124+ working_dir [workdir_len - 1 ] = '\0' ;
125+ }
126+ }
127+
128+ if (is_absolute (filename )) {
129+ /* If it's an absolute path name, it's easy. */
81130 return filename ;
131+
132+ } else if (strchr (filename , '/' )) {
133+ /* If it's a relative path name, we must look for executables relative
134+ * to the working directory. */
135+ if (is_executable (working_dir , filename )) {
136+ return filename ;
137+ }
82138 }
83139
140+ /* Otherwise look through the search path... */
84141 char * search_path = get_executable_search_path ();
85- return find_in_search_path (search_path , filename );
142+ char * result = find_in_search_path (working_dir , search_path , filename );
143+ free (search_path );
144+ return result ;
86145}
87146
88- #endif
147+ #endif
0 commit comments