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