Build identifier class
[dyninst.git] / pdutil / 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 "util/h/pathName.h"
46
47 #if defined(i386_unknown_nt4_0)
48
49 #define S_ISDIR(x) ((x) & _S_IFDIR)
50
51 string expand_tilde_pathname(const string &dir) {
52    return dir;
53 }
54
55 #else
56
57 #include <pwd.h>
58
59 string expand_tilde_pathname(const string &dir) {
60    // e.g. convert "~tamches/hello" to "/u/t/a/tamches/hello",
61    // or convert "~/hello" to same.
62    // In the spirit of Tcl_TildeSubst
63    if (dir.length()==0)
64       return dir;
65
66    const char *dir_cstr = dir.string_of();
67    if (dir_cstr[0] != '~')
68       return dir;
69
70    // Now, there are two possibilities: a tilde by itself (e.g. ~/x/y or ~), or
71    // a tilde followed by a username.
72    if (dir_cstr[1] == '/' || dir_cstr[1] == '\0') {
73       // It's the first case.  We need to find the environment vrble HOME and use it.
74       // It it doesn't exist (but it always does, I think) then I don't know what
75       // to do.
76       char *home_dir = getenv("HOME");
77       if (home_dir == NULL)
78          return dir; // yikes
79
80       if (home_dir[strlen(home_dir)-1] == '/' && dir_cstr[1] != '\0')
81          return string(home_dir) + &dir_cstr[2];
82       else
83          return string(home_dir) + &dir_cstr[1];
84    }
85
86    // It's the second case.  We need to find the username.  It starts at
87    // dir_cstr[1] and ends at (but not including) the first '/' or '\0'.
88    string userName;
89
90    const char *ptr = strchr(&dir_cstr[1], '/');
91    if (ptr == NULL)
92       userName = string(&dir_cstr[1]);
93    else {
94       char user_name_buffer[200];
95       unsigned user_name_len = ptr - &dir_cstr[1];
96
97       for (unsigned j=0; j < user_name_len; j++)
98          user_name_buffer[j] = dir_cstr[1+j];
99
100       user_name_buffer[user_name_len] = '\0';
101       userName = user_name_buffer;
102    }
103
104    struct passwd *pwPtr = getpwnam(userName.string_of());
105    if (pwPtr == NULL) {
106       endpwent();
107       return dir; // something better needed...
108    }
109
110    string result = string(pwPtr->pw_dir) + string(ptr);
111    endpwent();
112    return result;
113 }
114 #endif
115
116 static string concat_pathname_components_simple(const string &comp1, const string &comp2) {
117    string result = (comp1.length() ? comp1 : comp2);
118    return result;
119 }
120
121 string concat_pathname_components(const string &comp1, const string &comp2) {
122    if (comp1.length() == 0 || comp2.length() == 0)
123       return concat_pathname_components_simple(comp1, comp2);
124
125    bool needToAddSlash = true; // so far
126
127    // if comp1 ends in a "/" then no need to add slash
128    const char *temp = comp1.string_of();
129    if (temp[comp1.length()-1] == '/')
130       needToAddSlash = false;
131
132    // if comp2 begins with a "/" then no need to add slash
133    if (comp2.prefixed_by("/"))
134       needToAddSlash = false;
135
136    string result = comp1;
137    if (needToAddSlash)
138       result += "/";
139    result += comp2;
140
141    return result;
142 }
143
144 bool extractNextPathElem(const char * &ptr, string &result) {
145    // assumes that "ptr" points to the value of the PATH environment
146    // variable.  Extracts the next element (writing to result, updating
147    // ptr, returning true) if available else returns false;
148
149    while (isspace(*ptr))
150       ptr++;
151
152    if (*ptr == '\0')
153       return false;
154    
155    // collect from "ptr" upto but not including the next ":" or end-of-string
156    const char *start_ptr = ptr;
157
158    while (*ptr != ':' && *ptr != '\0')
159       ptr++;
160
161    unsigned len = ptr - start_ptr;
162
163    result = string(start_ptr, len);
164
165    // ptr now points at a ":" or end-of-string
166    assert(*ptr == ':' || *ptr == '\0');
167    if (*ptr == ':')
168       ptr++;
169
170 //   cerr << "extractNextPathElem returning " << result << endl;
171
172    return true;
173 }
174
175 bool exists_executable(const string &fullpathname) {
176    struct stat stat_buffer;
177    int result = stat(fullpathname.string_of(), &stat_buffer);
178    if (result == -1)
179       return false;
180
181    if (S_ISDIR(stat_buffer.st_mode))
182       return false; // that was a directory, not an executable
183
184    // more checks needed to be sure this is an executable file...
185
186    return true;
187 }
188
189 bool executableFromArgv0AndPathAndCwd(string &result,
190                                       const string &i_argv0,
191                                       const string &path,
192                                       const string &cwd) {
193    // return true iff successful.
194    // if successful, writes to result.
195    // "path" is the value of the PATH env var
196    // "cwd" is the current working directory, presumably from the PWD env var
197
198    // 0) if argv0 empty then forget it
199    if (i_argv0.length() == 0)
200       return false;
201
202    const string &argv0 = expand_tilde_pathname(i_argv0);
203
204    // 1) If argv0 starts with a slash then we sink or swim with argv0
205    
206    if ((argv0.string_of())[0] == '/') {
207       if (exists_executable(argv0)) {
208          result = argv0;
209          return true;
210       }
211    }
212
213    // 2) search the path, trying (dir + argv0) for each path component.
214    //    But only search the path if argv0 doesn't contain any slashes.
215    //    Why?  Because if it does contain a slash than at least one
216    //    directory component prefixes the executable name, and the path
217    //    is only supposed to be searched when an executable name is specifed
218    //    alone.
219    bool contains_slash = false;
220    const char *ptr = argv0.string_of();
221    while (*ptr != '\0')
222       if (*ptr++ == '/') {
223          contains_slash = true;
224          break;
225       }
226
227    if (!contains_slash) {
228       // search the path to see what directory argv0 came from.  If found, then
229       // use dir + argv0 else use argv0.
230       ptr = path.string_of();
231       string pathelem;
232       while (extractNextPathElem(ptr, pathelem)) {
233          string trystr = concat_pathname_components(pathelem, argv0);
234          
235          if (exists_executable(trystr)) {
236             result = trystr;
237             return true;
238          }
239       }
240    }
241
242    // well, if we've gotten this far without success: couldn't find argv0 in the
243    // path and argv0 wasn't a full path.  Last resort: try current directory + argv0
244    string trystr = concat_pathname_components(cwd, argv0);
245    if (exists_executable(trystr)) {
246       result = trystr;
247       return true;
248    }
249
250    return false;
251 }