Update copyright to LGPL on all files
[dyninst.git] / common / src / pathName.C
1 /*
2  * Copyright (c) 1996-2009 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 #include "common/h/headers.h"  // P_strrchr()
34 #include <ctype.h>
35 #include <assert.h>
36 #include "common/h/String.h"
37 #include "common/h/pathName.h"
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 <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52
53 #include <pwd.h>
54
55 pdstring expand_tilde_pathname(const pdstring &dir) {
56    // e.g. convert "~tamches/hello" to "/u/t/a/tamches/hello",
57    // or convert "~/hello" to same.
58    // In the spirit of Tcl_TildeSubst
59    if (dir.length()==0)
60       return dir;
61
62    const char *dir_cstr = dir.c_str();
63    if (dir_cstr[0] != '~')
64       return dir;
65
66    // Now, there are two possibilities: a tilde by itself (e.g. ~/x/y or ~), or
67    // a tilde followed by a username.
68    if (dir_cstr[1] == '/' || dir_cstr[1] == '\0') {
69       // It's the first case.  We need to find the environment vrble HOME and use it.
70       // It it doesn't exist (but it always does, I think) then I don't know what
71       // to do.
72       char *home_dir = getenv("HOME");
73       if (home_dir == NULL)
74          return dir; // yikes
75
76       if (home_dir[strlen(home_dir)-1] == '/' && dir_cstr[1] != '\0')
77          return pdstring(home_dir) + &dir_cstr[2];
78       else
79          return pdstring(home_dir) + &dir_cstr[1];
80    }
81
82    // It's the second case.  We need to find the username.  It starts at
83    // dir_cstr[1] and ends at (but not including) the first '/' or '\0'.
84    pdstring userName;
85
86    const char *ptr = strchr(&dir_cstr[1], '/');
87    if (ptr == NULL)
88       userName = pdstring(&dir_cstr[1]);
89    else {
90       char user_name_buffer[200];
91       unsigned user_name_len = ptr - &dir_cstr[1];
92
93       for (unsigned j=0; j < user_name_len; j++)
94          user_name_buffer[j] = dir_cstr[1+j];
95
96       user_name_buffer[user_name_len] = '\0';
97       userName = user_name_buffer;
98    }
99
100    struct passwd *pwPtr = getpwnam(userName.c_str());
101    if (pwPtr == NULL) {
102       endpwent();
103       return dir; // something better needed...
104    }
105
106    pdstring result = pdstring(pwPtr->pw_dir) + pdstring(ptr);
107    endpwent();
108    return result;
109 }
110 #endif
111
112 static pdstring concat_pathname_components_simple(const pdstring &comp1, const pdstring &comp2) 
113 {
114    pdstring result = (comp1.length() ? comp1 : comp2);
115    return result;
116 }
117
118 pdstring concat_pathname_components(const pdstring &comp1, const pdstring &comp2) 
119 {
120    if (comp1.length() == 0 || comp2.length() == 0)
121       return concat_pathname_components_simple(comp1, comp2);
122
123    bool needToAddSlash = true; // so far
124
125    // if comp1 ends in a "/" then no need to add slash
126    const char *temp = comp1.c_str();
127    if (temp[comp1.length()-1] == '/')
128       needToAddSlash = false;
129
130    // if comp2 begins with a "/" then no need to add slash
131    if (comp2.length() && comp2[0] == '/')
132            needToAddSlash = false;
133 #if 0
134    if (comp2.prefixed_by("/"))
135       needToAddSlash = false;
136 #endif
137
138    pdstring result = comp1;
139    if (needToAddSlash)
140       result += "/";
141    result += comp2;
142
143    return result;
144 }
145
146 bool extractNextPathElem(const char * &ptr, pdstring &result) 
147 {
148    // assumes that "ptr" points to the value of the PATH environment
149    // variable.  Extracts the next element (writing to result, updating
150    // ptr, returning true) if available else returns false;
151
152    if ( ptr == NULL )
153       return false;
154
155    while (isspace(*ptr))
156       ptr++;
157
158    if (*ptr == '\0')
159       return false;
160    
161    // collect from "ptr" upto but not including the next ":" or end-of-string
162    const char *start_ptr = ptr;
163
164    while (*ptr != ':' && *ptr != '\0')
165       ptr++;
166
167    unsigned len = ptr - start_ptr;
168
169    result = pdstring(start_ptr, len);
170
171    // ptr now points at a ":" or end-of-string
172    assert(*ptr == ':' || *ptr == '\0');
173    if (*ptr == ':')
174       ptr++;
175
176 //   cerr << "extractNextPathElem returning " << result << endl;
177
178    return true;
179 }
180
181 bool exists_executable(const pdstring &fullpathname) 
182 {
183    struct stat stat_buffer;
184    int result = stat(fullpathname.c_str(), &stat_buffer);
185
186    if (result == -1)
187       return false;
188
189    if (S_ISDIR(stat_buffer.st_mode))
190       return false; // that was a directory, not an executable
191
192    // more checks needed to be sure this is an executable file...
193
194    return true;
195 }
196
197 #if defined (cap_use_pdstring)
198 bool executableFromArgv0AndPathAndCwd(pdstring &result,
199       const pdstring &i_argv0,
200       const pdstring &path,
201       const pdstring &cwd) 
202 {
203    // return true iff successful.
204    // if successful, writes to result.
205    // "path" is the value of the PATH env var
206    // "cwd" is the current working directory, presumably from the PWD env var
207
208    // 0) if argv0 empty then forget it
209    if (i_argv0.length() == 0)
210       return false;
211
212    const pdstring &argv0 = expand_tilde_pathname(i_argv0);
213
214    // 1) If argv0 starts with a slash then we sink or swim with argv0
215
216    if ((argv0.c_str())[0] == '/') 
217    {
218       if (exists_executable(argv0)) 
219       {
220          result = argv0;
221          return true;
222       }
223    }
224
225    // 2) search the path, trying (dir + argv0) for each path component.
226    //    But only search the path if argv0 doesn't contain any slashes.
227    //    Why?  Because if it does contain a slash than at least one
228    //    directory component prefixes the executable name, and the path
229    //    is only supposed to be searched when an executable name is specifed
230    //    alone.
231    bool contains_slash = false;
232    const char *ptr = argv0.c_str();
233    while (*ptr != '\0')
234    {
235       if (*ptr++ == '/') 
236       {
237          contains_slash = true;
238          break;
239       }
240    }
241
242    if (!contains_slash) 
243    {
244       // search the path to see what directory argv0 came from.  If found, then
245       // use dir + argv0 else use argv0.
246       ptr = path.c_str();
247       pdstring pathelem;
248       while (extractNextPathElem(ptr, pathelem)) 
249       {
250          pdstring trystr = concat_pathname_components(pathelem, argv0);
251
252          if (exists_executable(trystr)) 
253          {
254             result = trystr;
255             return true;
256          }
257       }
258    }
259
260    // well, if we've gotten this far without success: couldn't find argv0 in the
261    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
262
263    pdstring trystr = concat_pathname_components(cwd, argv0);
264
265    if (exists_executable(trystr)) 
266    {
267       result = trystr;
268       return true;
269    }
270
271    return false;
272 }
273 #else
274
275
276 bool executableFromArgv0AndPathAndCwd(std::string &result,
277       const std::string &i_argv0,
278       const std::string &path,
279       const std::string &cwd) 
280 {
281    // return true iff successful.
282    // if successful, writes to result.
283    // "path" is the value of the PATH env var
284    // "cwd" is the current working directory, presumably from the PWD env var
285
286    // 0) if argv0 empty then forget it
287    if (i_argv0.length() == 0)
288       return false;
289
290    const std::string &argv0 = expand_tilde_pathname(i_argv0);
291
292    // 1) If argv0 starts with a slash then we sink or swim with argv0
293
294    if ((argv0.c_str())[0] == '/') 
295    {
296       if (exists_executable(argv0)) 
297       {
298          result = argv0;
299          return true;
300       }
301    }
302
303    // 2) search the path, trying (dir + argv0) for each path component.
304    //    But only search the path if argv0 doesn't contain any slashes.
305    //    Why?  Because if it does contain a slash than at least one
306    //    directory component prefixes the executable name, and the path
307    //    is only supposed to be searched when an executable name is specifed
308    //    alone.
309
310    bool contains_slash = false;
311    const char *ptr = argv0.c_str();
312
313    while (*ptr != '\0')
314    {
315       if (*ptr++ == '/') 
316       {
317          contains_slash = true;
318          break;
319       }
320    }
321
322    if (!contains_slash) 
323    {
324       // search the path to see what directory argv0 came from.  If found, then
325       // use dir + argv0 else use argv0.
326       ptr = path.c_str();
327       std::string pathelem;
328
329       while (extractNextPathElem(ptr, pathelem)) 
330       {
331          std::string trystr = concat_pathname_components(pathelem, argv0);
332
333          if (exists_executable(trystr)) 
334          {
335             result = trystr;
336             return true;
337          }
338       }
339    }
340
341    // well, if we've gotten this far without success: couldn't find argv0 in the
342    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
343    std::string trystr = concat_pathname_components(cwd, argv0);
344
345    if (exists_executable(trystr)) 
346    {
347       result = trystr;
348       return true;
349    }
350    return false;
351 }
352 #endif
353
354 #if defined(os_windows)
355 #define PATH_SEP ('\\')
356 #define SECOND_PATH_SEP ('/')
357 #else
358 #define PATH_SEP ('/')
359 #endif
360
361 #if defined (cap_use_pdstring)
362 pdstring extract_pathname_tail(const pdstring &path)
363 {
364    const char *path_str = path.c_str();
365    const char *path_sep = P_strrchr(path_str, PATH_SEP);
366
367 #if defined(SECOND_PATH_SEP)
368    const char *sec_path_sep = P_strrchr(path_str, SECOND_PATH_SEP);
369    if (sec_path_sep && (!path_sep || sec_path_sep > path_sep))
370       path_sep = sec_path_sep;
371 #endif
372
373    pdstring ret = (path_sep) ? (path_sep + 1) : (path_str);
374    return ret;
375 }
376
377 #else
378 std::string extract_pathname_tail(const std::string &path)
379 {
380         if (!path.length())
381         {
382                 fprintf(stderr, "%s[%d]:  extract_pathname_tail:  bad parameter\n", FILE__, __LINE__);
383                 return std::string("");
384         }
385
386    const char *path_str = path.c_str();
387    if (!path_str)
388         {
389                 fprintf(stderr, "%s[%d]:  extract_pathname_tail:  bad parameter\n", FILE__, __LINE__);
390                 return std::string("");
391         }
392
393    const char *path_sep = P_strrchr(path_str, PATH_SEP);
394
395 #if defined(SECOND_PATH_SEP)
396    const char *sec_path_sep = P_strrchr(path_str, SECOND_PATH_SEP);
397    if (sec_path_sep && (!path_sep || sec_path_sep > path_sep))
398       path_sep = sec_path_sep;
399 #endif
400
401    std::string ret = (path_sep) ? (path_sep + 1) : (path_str);
402    return ret;
403 }
404 #endif
405
406 #if !defined (os_windows)
407 char *resolve_file_path(const char *fname, char *resolved_path)
408 {
409    // (1)  use realpath() to resolve any . or ..'s, or symbolic links
410    if (NULL == realpath(fname, resolved_path)) {
411       fprintf(stderr, "%s[%d]:  realpath(%s): %s\n", FILE__, __LINE__, fname, strerror(errno));
412       return NULL;
413    }
414    //fprintf(stderr, "%s[%d]:  resolved file path: %s\n", FILE__, __LINE__, resolved_path);
415
416    // (2) if no slashes, try CWD
417    if (!strpbrk(resolved_path, "/\\")) {
418       char cwd[PATH_MAX];
419       if (NULL == getcwd(cwd, PATH_MAX)) {
420          fprintf(stderr, "%s[%d]:  getcwd: %s\n", FILE__, __LINE__, strerror(errno));
421          return NULL;
422       }
423       char resolved_path_bak[PATH_MAX];
424       strcpy(resolved_path_bak, resolved_path);
425       sprintf(resolved_path, "%s/%s", cwd, resolved_path_bak);
426    }
427
428    // (3) if it has a tilde, expand tilde pathname
429    if (!strpbrk(resolved_path, "~")) {
430       pdstring td_pathname = pdstring(resolved_path);
431       pdstring no_td_pathname = expand_tilde_pathname(td_pathname);
432       strcpy(resolved_path, no_td_pathname.c_str());
433    }
434
435    return resolved_path;
436 }
437 #else
438 char *resolve_file_path(const char *fname, char *resolved_path) 
439 {
440    //  hrm.
441    //... windows has no realpath()?
442    fprintf(stderr, "%s[%d]:  IMPLEMENT ME: %s\n", FILE__, __LINE__, fname);
443    return "bogus_path";
444 }
445 #endif
446