added concat_pathname_components, extractNextPathElem, exists_executable,
[dyninst.git] / common / src / pathName.C
1 /*
2  * Copyright (c) 1996 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  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 // pathName.C
43
44 #include <ctype.h>
45 #include <pwd.h>
46 #include "util/h/pathName.h"
47
48 string expand_tilde_pathname(const string &dir) {
49    // e.g. convert "~tamches/hello" to "/u/t/a/tamches/hello",
50    // or convert "~/hello" to same.
51    // In the spirit of Tcl_TildeSubst
52    if (dir.length()==0)
53       return dir;
54
55    const char *dir_cstr = dir.string_of();
56    if (dir_cstr[0] != '~')
57       return dir;
58
59    // Now, there are two possibilities: a tilde by itself (e.g. ~/x/y or ~), or
60    // a tilde followed by a username.
61    if (dir_cstr[1] == '/' || dir_cstr[1] == '\0') {
62       // It's the first case.  We need to find the environment vrble HOME and use it.
63       // It it doesn't exist (but it always does, I think) then I don't know what
64       // to do.
65       char *home_dir = getenv("HOME");
66       if (home_dir == NULL)
67          return dir; // yikes
68
69       if (home_dir[strlen(home_dir)-1] == '/' && dir_cstr[1] != '\0')
70          return string(home_dir) + &dir_cstr[2];
71       else
72          return string(home_dir) + &dir_cstr[1];
73    }
74
75    // It's the second case.  We need to find the username.  It starts at
76    // dir_cstr[1] and ends at (but not including) the first '/' or '\0'.
77    string userName;
78
79    const char *ptr = strchr(&dir_cstr[1], '/');
80    if (ptr == NULL)
81       userName = string(&dir_cstr[1]);
82    else {
83       char user_name_buffer[200];
84       unsigned user_name_len = ptr - &dir_cstr[1];
85
86       for (unsigned j=0; j < user_name_len; j++)
87          user_name_buffer[j] = dir_cstr[1+j];
88
89       user_name_buffer[user_name_len] = '\0';
90       userName = user_name_buffer;
91    }
92
93    struct passwd *pwPtr = getpwnam(userName.string_of());
94    if (pwPtr == NULL) {
95       endpwent();
96       return dir; // something better needed...
97    }
98
99    string result = string(pwPtr->pw_dir) + string(ptr);
100    endpwent();
101    return result;
102 }
103
104 static string concat_pathname_components_simple(const string &comp1, const string &comp2) {
105    string result = (comp1.length() ? comp1 : comp2);
106    return result;
107 }
108
109 string concat_pathname_components(const string &comp1, const string &comp2) {
110    if (comp1.length() == 0 || comp2.length() == 0)
111       return concat_pathname_components_simple(comp1, comp2);
112
113    bool needToAddSlash = true; // so far
114
115    // if comp1 ends in a "/" then no need to add slash
116    const char *temp = comp1.string_of();
117    if (temp[comp1.length()-1] == '/')
118       needToAddSlash = false;
119
120    // if comp2 begins with a "/" then no need to add slash
121    if (comp2.prefixed_by("/"))
122       needToAddSlash = false;
123
124    string result = comp1;
125    if (needToAddSlash)
126       result += "/";
127    result += comp2;
128
129    return result;
130 }
131
132 bool extractNextPathElem(const char * &ptr, string &result) {
133    // assumes that "ptr" points to the value of the PATH environment
134    // variable.  Extracts the next element (writing to result, updating
135    // ptr, returning true) if available else returns false;
136
137    while (isspace(*ptr))
138       ptr++;
139
140    if (*ptr == '\0')
141       return false;
142    
143    // collect from "ptr" upto but not including the next ":" or end-of-string
144    const char *start_ptr = ptr;
145
146    while (*ptr != ':' && *ptr != '\0')
147       ptr++;
148
149    unsigned len = ptr - start_ptr;
150
151    result = string(start_ptr, len);
152
153    // ptr now points at a ":" or end-of-string
154    assert(*ptr == ':' || *ptr == '\0');
155    if (*ptr == ':')
156       ptr++;
157
158 //   cerr << "extractNextPathElem returning " << result << endl;
159
160    return true;
161 }
162
163 bool exists_executable(const string &fullpathname) {
164    struct stat stat_buffer;
165    int result = stat(fullpathname.string_of(), &stat_buffer);
166    if (result == -1)
167       return false;
168
169    if (S_ISDIR(stat_buffer.st_mode))
170       return false; // that was a directory, not an executable
171
172    // more checks needed to be sure this is an executable file...
173
174    return true;
175 }
176
177 bool executableFromArgv0AndPathAndCwd(string &result,
178                                       const string &i_argv0,
179                                       const string &path,
180                                       const string &cwd) {
181    // return true iff successful.
182    // if successful, writes to result.
183    // "path" is the value of the PATH env var
184    // "cwd" is the current working directory, presumably from the PWD env var
185
186    // 0) if argv0 empty then forget it
187    if (i_argv0.length() == 0)
188       return false;
189
190    const string &argv0 = expand_tilde_pathname(i_argv0);
191
192    // 1) If argv0 starts with a slash then we sink or swim with argv0
193    
194    if ((argv0.string_of())[0] == '/') {
195       if (exists_executable(argv0)) {
196          result = argv0;
197          return true;
198       }
199    }
200
201    // 2) search the path, trying (dir + argv0) for each path component.
202    //    But only search the path if argv0 doesn't contain any slashes.
203    //    Why?  Because if it does contain a slash than at least one
204    //    directory component prefixes the executable name, and the path
205    //    is only supposed to be searched when an executable name is specifed
206    //    alone.
207    bool contains_slash = false;
208    const char *ptr = argv0.string_of();
209    while (*ptr != '\0')
210       if (*ptr++ == '/') {
211          contains_slash = true;
212          break;
213       }
214
215    if (!contains_slash) {
216       // search the path to see what directory argv0 came from.  If found, then
217       // use dir + argv0 else use argv0.
218       ptr = path.string_of();
219       string pathelem;
220       while (extractNextPathElem(ptr, pathelem)) {
221          string trystr = concat_pathname_components(pathelem, argv0);
222          
223          if (exists_executable(trystr)) {
224             result = trystr;
225             return true;
226          }
227       }
228    }
229
230    // well, if we've gotten this far without success: couldn't find argv0 in the
231    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
232    string trystr = concat_pathname_components(cwd, argv0);
233    if (exists_executable(trystr)) {
234       result = trystr;
235       return true;
236    }
237
238    return false;
239 }