Fix warnings from compiling with gcc 4.3.0
[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 {
110    pdstring result = (comp1.length() ? comp1 : comp2);
111    return result;
112 }
113
114 pdstring concat_pathname_components(const pdstring &comp1, const pdstring &comp2) 
115 {
116    if (comp1.length() == 0 || comp2.length() == 0)
117       return concat_pathname_components_simple(comp1, comp2);
118
119    bool needToAddSlash = true; // so far
120
121    // if comp1 ends in a "/" then no need to add slash
122    const char *temp = comp1.c_str();
123    if (temp[comp1.length()-1] == '/')
124       needToAddSlash = false;
125
126    // if comp2 begins with a "/" then no need to add slash
127    if (comp2.length() && comp2[0] == '/')
128            needToAddSlash = false;
129 #if 0
130    if (comp2.prefixed_by("/"))
131       needToAddSlash = false;
132 #endif
133
134    pdstring result = comp1;
135    if (needToAddSlash)
136       result += "/";
137    result += comp2;
138
139    return result;
140 }
141
142 bool extractNextPathElem(const char * &ptr, pdstring &result) 
143 {
144    // assumes that "ptr" points to the value of the PATH environment
145    // variable.  Extracts the next element (writing to result, updating
146    // ptr, returning true) if available else returns false;
147
148    if ( ptr == NULL )
149       return false;
150
151    while (isspace(*ptr))
152       ptr++;
153
154    if (*ptr == '\0')
155       return false;
156    
157    // collect from "ptr" upto but not including the next ":" or end-of-string
158    const char *start_ptr = ptr;
159
160    while (*ptr != ':' && *ptr != '\0')
161       ptr++;
162
163    unsigned len = ptr - start_ptr;
164
165    result = pdstring(start_ptr, len);
166
167    // ptr now points at a ":" or end-of-string
168    assert(*ptr == ':' || *ptr == '\0');
169    if (*ptr == ':')
170       ptr++;
171
172 //   cerr << "extractNextPathElem returning " << result << endl;
173
174    return true;
175 }
176
177 bool exists_executable(const pdstring &fullpathname) 
178 {
179    struct stat stat_buffer;
180    int result = stat(fullpathname.c_str(), &stat_buffer);
181
182    if (result == -1)
183       return false;
184
185    if (S_ISDIR(stat_buffer.st_mode))
186       return false; // that was a directory, not an executable
187
188    // more checks needed to be sure this is an executable file...
189
190    return true;
191 }
192
193 #if defined (cap_use_pdstring)
194 bool executableFromArgv0AndPathAndCwd(pdstring &result,
195                                       const pdstring &i_argv0,
196                                       const pdstring &path,
197                                       const pdstring &cwd) 
198 {
199    // return true iff successful.
200    // if successful, writes to result.
201    // "path" is the value of the PATH env var
202    // "cwd" is the current working directory, presumably from the PWD env var
203
204    // 0) if argv0 empty then forget it
205    if (i_argv0.length() == 0)
206       return false;
207
208    const pdstring &argv0 = expand_tilde_pathname(i_argv0);
209
210    // 1) If argv0 starts with a slash then we sink or swim with argv0
211    
212    if ((argv0.c_str())[0] == '/') {
213       if (exists_executable(argv0)) {
214          result = argv0;
215          return true;
216       }
217    }
218
219    // 2) search the path, trying (dir + argv0) for each path component.
220    //    But only search the path if argv0 doesn't contain any slashes.
221    //    Why?  Because if it does contain a slash than at least one
222    //    directory component prefixes the executable name, and the path
223    //    is only supposed to be searched when an executable name is specifed
224    //    alone.
225    bool contains_slash = false;
226    const char *ptr = argv0.c_str();
227    while (*ptr != '\0')
228       if (*ptr++ == '/') {
229          contains_slash = true;
230          break;
231       }
232
233    if (!contains_slash) {
234       // search the path to see what directory argv0 came from.  If found, then
235       // use dir + argv0 else use argv0.
236       ptr = path.c_str();
237       pdstring pathelem;
238       while (extractNextPathElem(ptr, pathelem)) {
239          pdstring trystr = concat_pathname_components(pathelem, argv0);
240          
241          if (exists_executable(trystr)) {
242             result = trystr;
243             return true;
244          }
245       }
246    }
247
248    // well, if we've gotten this far without success: couldn't find argv0 in the
249    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
250    pdstring trystr = concat_pathname_components(cwd, argv0);
251    if (exists_executable(trystr)) {
252       result = trystr;
253       return true;
254    }
255
256    return false;
257 }
258 #else
259
260
261 bool executableFromArgv0AndPathAndCwd(std::string &result,
262                                       const std::string &i_argv0,
263                                       const std::string &path,
264                                       const std::string &cwd) 
265 {
266    // return true iff successful.
267    // if successful, writes to result.
268    // "path" is the value of the PATH env var
269    // "cwd" is the current working directory, presumably from the PWD env var
270
271    // 0) if argv0 empty then forget it
272    if (i_argv0.length() == 0)
273       return false;
274
275    const std::string &argv0 = expand_tilde_pathname(i_argv0);
276
277    // 1) If argv0 starts with a slash then we sink or swim with argv0
278
279    if ((argv0.c_str())[0] == '/') 
280    {
281       if (exists_executable(argv0)) {
282          result = argv0;
283          return true;
284       }
285    }
286
287    // 2) search the path, trying (dir + argv0) for each path component.
288    //    But only search the path if argv0 doesn't contain any slashes.
289    //    Why?  Because if it does contain a slash than at least one
290    //    directory component prefixes the executable name, and the path
291    //    is only supposed to be searched when an executable name is specifed
292    //    alone.
293
294    bool contains_slash = false;
295    const char *ptr = argv0.c_str();
296
297    while (*ptr != '\0')
298    {
299       if (*ptr++ == '/') 
300       {
301          contains_slash = true;
302          break;
303       }
304    }
305
306    if (!contains_slash) 
307    {
308       // search the path to see what directory argv0 came from.  If found, then
309       // use dir + argv0 else use argv0.
310       ptr = path.c_str();
311       std::string pathelem;
312
313       while (extractNextPathElem(ptr, pathelem)) 
314       {
315          std::string trystr = concat_pathname_components(pathelem, argv0);
316
317          if (exists_executable(trystr)) 
318          {
319             result = trystr;
320             return true;
321          }
322       }
323    }
324
325    // well, if we've gotten this far without success: couldn't find argv0 in the
326    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
327    std::string trystr = concat_pathname_components(cwd, argv0);
328
329    if (exists_executable(trystr)) 
330    {
331       result = trystr;
332       return true;
333    }
334    return false;
335 }
336 #endif
337
338 #if defined(os_windows)
339 #define PATH_SEP ('\\')
340 #define SECOND_PATH_SEP ('/')
341 #else
342 #define PATH_SEP ('/')
343 #endif
344
345 #if defined (cap_use_pdstring)
346 pdstring extract_pathname_tail(const pdstring &path)
347 {
348    const char *path_str = path.c_str();
349    const char *path_sep = P_strrchr(path_str, PATH_SEP);
350
351 #if defined(SECOND_PATH_SEP)
352    const char *sec_path_sep = P_strrchr(path_str, SECOND_PATH_SEP);
353    if (sec_path_sep && (!path_sep || sec_path_sep > path_sep))
354       path_sep = sec_path_sep;
355 #endif
356
357    pdstring ret = (path_sep) ? (path_sep + 1) : (path_str);
358    return ret;
359 }
360
361 #else
362 std::string extract_pathname_tail(const std::string &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    std::string ret = (path_sep) ? (path_sep + 1) : (path_str);
374    return ret;
375 }
376 #endif
377
378 #if !defined (os_windows)
379 char *resolve_file_path(const char *fname, char *resolved_path)
380 {
381    // (1)  use realpath() to resolve any . or ..'s, or symbolic links
382    if (NULL == realpath(fname, resolved_path)) {
383       fprintf(stderr, "%s[%d]:  realpath(%s): %s\n", FILE__, __LINE__, fname, strerror(errno));
384       return NULL;
385    }
386    fprintf(stderr, "%s[%d]:  resolved file path: %s\n", FILE__, __LINE__, resolved_path);
387
388    // (2) if no slashes, try CWD
389    if (!strpbrk(resolved_path, "/\\")) {
390       char cwd[PATH_MAX];
391       if (NULL == getcwd(cwd, PATH_MAX)) {
392          fprintf(stderr, "%s[%d]:  getcwd: %s\n", FILE__, __LINE__, strerror(errno));
393          return NULL;
394       }
395       char resolved_path_bak[PATH_MAX];
396       strcpy(resolved_path_bak, resolved_path);
397       sprintf(resolved_path, "%s/%s", cwd, resolved_path_bak);
398    }
399
400    // (3) if it has a tilde, expand tilde pathname
401    if (!strpbrk(resolved_path, "~")) {
402       pdstring td_pathname = pdstring(resolved_path);
403       pdstring no_td_pathname = expand_tilde_pathname(td_pathname);
404       strcpy(resolved_path, no_td_pathname.c_str());
405    }
406
407    return resolved_path;
408 }
409 #else
410 char *resolve_file_path(const char *fname, char *resolved_path) 
411 {
412    //  hrm.
413    //... windows has no realpath()?
414    fprintf(stderr, "%s[%d]:  IMPLEMENT ME: %s\n", FILE__, __LINE__, fname);
415    return "bogus_path";
416 }
417 #endif
418