These are the windows fixes that I previously alluded to, combined with
[dyninst.git] / common / src / pathName.C
1 /*
2  * Copyright (c) 1996-2007 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
30  */
31
32 // pathName.C
33
34 #include <ctype.h>
35 #include "common/h/String.h"
36 #include "common/h/pathName.h"
37 #include "common/h/headers.h"  // P_strrchr()
38
39 #if defined(os_windows) //ccw 20 july 2000 : 29 mar 2001
40
41 #define S_ISDIR(x) ((x) & _S_IFDIR)
42
43 pdstring expand_tilde_pathname(const pdstring &dir) {
44    return dir;
45 }
46
47 #else
48
49 #include <pwd.h>
50
51 pdstring expand_tilde_pathname(const pdstring &dir) {
52    // e.g. convert "~tamches/hello" to "/u/t/a/tamches/hello",
53    // or convert "~/hello" to same.
54    // In the spirit of Tcl_TildeSubst
55    if (dir.length()==0)
56       return dir;
57
58    const char *dir_cstr = dir.c_str();
59    if (dir_cstr[0] != '~')
60       return dir;
61
62    // Now, there are two possibilities: a tilde by itself (e.g. ~/x/y or ~), or
63    // a tilde followed by a username.
64    if (dir_cstr[1] == '/' || dir_cstr[1] == '\0') {
65       // It's the first case.  We need to find the environment vrble HOME and use it.
66       // It it doesn't exist (but it always does, I think) then I don't know what
67       // to do.
68       char *home_dir = getenv("HOME");
69       if (home_dir == NULL)
70          return dir; // yikes
71
72       if (home_dir[strlen(home_dir)-1] == '/' && dir_cstr[1] != '\0')
73          return pdstring(home_dir) + &dir_cstr[2];
74       else
75          return pdstring(home_dir) + &dir_cstr[1];
76    }
77
78    // It's the second case.  We need to find the username.  It starts at
79    // dir_cstr[1] and ends at (but not including) the first '/' or '\0'.
80    pdstring userName;
81
82    const char *ptr = strchr(&dir_cstr[1], '/');
83    if (ptr == NULL)
84       userName = pdstring(&dir_cstr[1]);
85    else {
86       char user_name_buffer[200];
87       unsigned user_name_len = ptr - &dir_cstr[1];
88
89       for (unsigned j=0; j < user_name_len; j++)
90          user_name_buffer[j] = dir_cstr[1+j];
91
92       user_name_buffer[user_name_len] = '\0';
93       userName = user_name_buffer;
94    }
95
96    struct passwd *pwPtr = getpwnam(userName.c_str());
97    if (pwPtr == NULL) {
98       endpwent();
99       return dir; // something better needed...
100    }
101
102    pdstring result = pdstring(pwPtr->pw_dir) + pdstring(ptr);
103    endpwent();
104    return result;
105 }
106 #endif
107
108 static pdstring concat_pathname_components_simple(const pdstring &comp1, const pdstring &comp2) {
109    pdstring result = (comp1.length() ? comp1 : comp2);
110    return result;
111 }
112
113 pdstring concat_pathname_components(const pdstring &comp1, const pdstring &comp2) {
114    if (comp1.length() == 0 || comp2.length() == 0)
115       return concat_pathname_components_simple(comp1, comp2);
116
117    bool needToAddSlash = true; // so far
118
119    // if comp1 ends in a "/" then no need to add slash
120    const char *temp = comp1.c_str();
121    if (temp[comp1.length()-1] == '/')
122       needToAddSlash = false;
123
124    // if comp2 begins with a "/" then no need to add slash
125    if (comp2.prefixed_by("/"))
126       needToAddSlash = false;
127
128    pdstring result = comp1;
129    if (needToAddSlash)
130       result += "/";
131    result += comp2;
132
133    return result;
134 }
135
136 bool extractNextPathElem(const char * &ptr, pdstring &result) {
137    // assumes that "ptr" points to the value of the PATH environment
138    // variable.  Extracts the next element (writing to result, updating
139    // ptr, returning true) if available else returns false;
140
141    if( ptr == NULL )
142       return false;
143
144    while (isspace(*ptr))
145       ptr++;
146
147    if (*ptr == '\0')
148       return false;
149    
150    // collect from "ptr" upto but not including the next ":" or end-of-string
151    const char *start_ptr = ptr;
152
153    while (*ptr != ':' && *ptr != '\0')
154       ptr++;
155
156    unsigned len = ptr - start_ptr;
157
158    result = pdstring(start_ptr, len);
159
160    // ptr now points at a ":" or end-of-string
161    assert(*ptr == ':' || *ptr == '\0');
162    if (*ptr == ':')
163       ptr++;
164
165 //   cerr << "extractNextPathElem returning " << result << endl;
166
167    return true;
168 }
169
170 bool exists_executable(const pdstring &fullpathname) {
171    struct stat stat_buffer;
172    int result = stat(fullpathname.c_str(), &stat_buffer);
173    if (result == -1)
174       return false;
175
176    if (S_ISDIR(stat_buffer.st_mode))
177       return false; // that was a directory, not an executable
178
179    // more checks needed to be sure this is an executable file...
180
181    return true;
182 }
183
184 bool executableFromArgv0AndPathAndCwd(pdstring &result,
185                                       const pdstring &i_argv0,
186                                       const pdstring &path,
187                                       const pdstring &cwd) {
188    // return true iff successful.
189    // if successful, writes to result.
190    // "path" is the value of the PATH env var
191    // "cwd" is the current working directory, presumably from the PWD env var
192
193    // 0) if argv0 empty then forget it
194    if (i_argv0.length() == 0)
195       return false;
196
197    const pdstring &argv0 = expand_tilde_pathname(i_argv0);
198
199    // 1) If argv0 starts with a slash then we sink or swim with argv0
200    
201    if ((argv0.c_str())[0] == '/') {
202       if (exists_executable(argv0)) {
203          result = argv0;
204          return true;
205       }
206    }
207
208    // 2) search the path, trying (dir + argv0) for each path component.
209    //    But only search the path if argv0 doesn't contain any slashes.
210    //    Why?  Because if it does contain a slash than at least one
211    //    directory component prefixes the executable name, and the path
212    //    is only supposed to be searched when an executable name is specifed
213    //    alone.
214    bool contains_slash = false;
215    const char *ptr = argv0.c_str();
216    while (*ptr != '\0')
217       if (*ptr++ == '/') {
218          contains_slash = true;
219          break;
220       }
221
222    if (!contains_slash) {
223       // search the path to see what directory argv0 came from.  If found, then
224       // use dir + argv0 else use argv0.
225       ptr = path.c_str();
226       pdstring pathelem;
227       while (extractNextPathElem(ptr, pathelem)) {
228          pdstring trystr = concat_pathname_components(pathelem, argv0);
229          
230          if (exists_executable(trystr)) {
231             result = trystr;
232             return true;
233          }
234       }
235    }
236
237    // well, if we've gotten this far without success: couldn't find argv0 in the
238    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
239    pdstring trystr = concat_pathname_components(cwd, argv0);
240    if (exists_executable(trystr)) {
241       result = trystr;
242       return true;
243    }
244
245    return false;
246 }
247
248
249 bool executableFromArgv0AndPathAndCwd(std::string &result,
250                                       const std::string &i_argv0,
251                                       const std::string &path,
252                                       const std::string &cwd) 
253 {
254    pdstring pdresult(result.c_str());
255    pdstring pdargv0(i_argv0.c_str());
256    pdstring pdpath(path.c_str());
257    pdstring pdcwd(cwd.c_str());
258    bool ret = executableFromArgv0AndPathAndCwd(pdresult, pdargv0, pdpath, pdcwd);
259    result = std::string(pdresult.c_str());
260    return ret;
261 }
262
263 #if defined(os_windows)
264 #define PATH_SEP ('\\')
265 #define SECOND_PATH_SEP ('/')
266 #else
267 #define PATH_SEP ('/')
268 #endif
269
270 pdstring extract_pathname_tail(const pdstring &path)
271 {
272   const char *path_str = path.c_str();
273   const char *path_sep = P_strrchr(path_str, PATH_SEP);
274
275 #if defined(SECOND_PATH_SEP)
276   const char *sec_path_sep = P_strrchr(path_str, SECOND_PATH_SEP);
277   if (sec_path_sep && (!path_sep || sec_path_sep > path_sep))
278     path_sep = sec_path_sep;
279 #endif
280
281   pdstring ret = (path_sep) ? (path_sep + 1) : (path_str);
282   return ret;
283 }
284
285 std::string extract_pathname_tail(const std::string &path)
286 {
287   const char *path_str = path.c_str();
288   const char *path_sep = P_strrchr(path_str, PATH_SEP);
289
290 #if defined(SECOND_PATH_SEP)
291   const char *sec_path_sep = P_strrchr(path_str, SECOND_PATH_SEP);
292   if (sec_path_sep && (!path_sep || sec_path_sep > path_sep))
293     path_sep = sec_path_sep;
294 #endif
295
296   std::string ret = (path_sep) ? (path_sep + 1) : (path_str);
297   return ret;
298 }
299
300 #if !defined (os_windows)
301 char *resolve_file_path(const char *fname, char *resolved_path)
302 {
303    // (1)  use realpath() to resolve any . or ..'s, or symbolic links
304    if (NULL == realpath(fname, resolved_path)) {
305       fprintf(stderr, "%s[%d]:  realpath(%s): %s\n", FILE__, __LINE__, fname, strerror(errno));
306       return NULL;
307    }
308    fprintf(stderr, "%s[%d]:  resolved file path: %s\n", FILE__, __LINE__, resolved_path);
309
310    // (2) if no slashes, try CWD
311    if (!strpbrk(resolved_path, "/\\")) {
312       char cwd[PATH_MAX];
313       if (NULL == getcwd(cwd, PATH_MAX)) {
314          fprintf(stderr, "%s[%d]:  getcwd: %s\n", FILE__, __LINE__, strerror(errno));
315          return NULL;
316       }
317       char resolved_path_bak[PATH_MAX];
318       strcpy(resolved_path_bak, resolved_path);
319       sprintf(resolved_path, "%s/%s", cwd, resolved_path_bak);
320    }
321
322    // (3) if it has a tilde, expand tilde pathname
323    if (!strpbrk(resolved_path, "~")) {
324       pdstring td_pathname = pdstring(resolved_path);
325       pdstring no_td_pathname = expand_tilde_pathname(td_pathname);
326       strcpy(resolved_path, no_td_pathname.c_str());
327    }
328
329    return resolved_path;
330 }
331 #else
332 char *resolve_file_path(const char *fname, char *resolved_path) 
333 {
334    //  hrm.
335    //... windows has no realpath()?
336    fprintf(stderr, "%s[%d]:  IMPLEMENT ME: %s\n", FILE__, __LINE__, fname);
337    return "bogus_path";
338 }
339 #endif
340