Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / eglib / gpath.c
1 /*
2  * Portable Utility Functions
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@novell.com)
6  *
7  * (C) 2006 Novell, Inc.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28 #include <config.h>
29 #include <stdio.h>
30 #include <glib.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33
34 #ifdef G_OS_WIN32
35 #include <direct.h> 
36 #endif
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 gchar *
43 g_build_path (const gchar *separator, const gchar *first_element, ...)
44 {
45         const char *elem, *next, *endptr;
46         gboolean trimmed;
47         GString *path;
48         va_list args;
49         size_t slen;
50         
51         g_return_val_if_fail (separator != NULL, NULL);
52         
53         path = g_string_sized_new (48);
54         slen = strlen (separator);
55         
56         va_start (args, first_element);
57         for (elem = first_element; elem != NULL; elem = next) {
58                 /* trim any trailing separators from @elem */
59                 endptr = elem + strlen (elem);
60                 trimmed = FALSE;
61                 
62                 while (endptr >= elem + slen) {
63                         if (strncmp (endptr - slen, separator, slen) != 0)
64                                 break;
65                         
66                         endptr -= slen;
67                         trimmed = TRUE;
68                 }
69                 
70                 /* append elem, not including any trailing separators */
71                 if (endptr > elem)
72                         g_string_append_len (path, elem, endptr - elem);
73                 
74                 /* get the next element */
75                 do {
76                         if (!(next = va_arg (args, char *)))
77                                 break;
78                         
79                         /* remove leading separators */
80                         while (!strncmp (next, separator, slen))
81                                 next += slen;
82                 } while (*next == '\0');
83                 
84                 if (next || trimmed)
85                         g_string_append_len (path, separator, slen);
86         }
87         va_end (args);
88         
89         return g_string_free (path, FALSE);
90 }
91
92 static gchar*
93 strrchr_seperator (const gchar* filename)
94 {
95 #ifdef G_OS_WIN32
96         char *p2;
97 #endif
98         char *p;
99
100         p = strrchr (filename, G_DIR_SEPARATOR);
101 #ifdef G_OS_WIN32
102         p2 = strrchr (filename, '/');
103         if (p2 > p)
104                 p = p2;
105 #endif
106
107         return p;
108 }
109
110 gchar *
111 g_path_get_dirname (const gchar *filename)
112 {
113         char *p, *r;
114         size_t count;
115         g_return_val_if_fail (filename != NULL, NULL);
116
117         p = strrchr_seperator (filename);
118         if (p == NULL)
119                 return g_strdup (".");
120         if (p == filename)
121                 return g_strdup ("/");
122         count = p - filename;
123         r = g_malloc (count + 1);
124         strncpy (r, filename, count);
125         r [count] = 0;
126
127         return r;
128 }
129
130 gchar *
131 g_path_get_basename (const char *filename)
132 {
133         char *r;
134         g_return_val_if_fail (filename != NULL, NULL);
135
136         /* Empty filename -> . */
137         if (!*filename)
138                 return g_strdup (".");
139
140         /* No separator -> filename */
141         r = strrchr_seperator (filename);
142         if (r == NULL)
143                 return g_strdup (filename);
144
145         /* Trailing slash, remove component */
146         if (r [1] == 0){
147                 char *copy = g_strdup (filename);
148                 copy [r-filename] = 0;
149                 r = strrchr_seperator (copy);
150
151                 if (r == NULL){
152                         g_free (copy);                  
153                         return g_strdup ("/");
154                 }
155                 r = g_strdup (&r[1]);
156                 g_free (copy);
157                 return r;
158         }
159
160         return g_strdup (&r[1]);
161 }
162
163 //wasm does have strtok_r even though autoconf fails to find
164 #if !defined (HAVE_STRTOK_R) && !defined (HOST_WASM)
165 // This is from BSD's strtok_r
166
167 char *
168 strtok_r(char *s, const char *delim, char **last)
169 {
170         char *spanp;
171         int c, sc;
172         char *tok;
173         
174         if (s == NULL && (s = *last) == NULL)
175                 return NULL;
176         
177         /*
178          * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
179          */
180 cont:
181         c = *s++;
182         for (spanp = (char *)delim; (sc = *spanp++) != 0; ){
183                 if (c == sc)
184                         goto cont;
185         }
186
187         if (c == 0){         /* no non-delimiter characters */
188                 *last = NULL;
189                 return NULL;
190         }
191         tok = s - 1;
192
193         /*
194          * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
195          * Note that delim must have one NUL; we stop if we see that, too.
196          */
197         for (;;){
198                 c = *s++;
199                 spanp = (char *)delim;
200                 do {
201                         if ((sc = *spanp++) == c) {
202                                 if (c == 0)
203                                         s = NULL;
204                                 else {
205                                         char *w = s - 1;
206                                         *w = '\0';
207                                 }
208                                 *last = s;
209                                 return tok;
210                         }
211                 }
212                 while (sc != 0);
213         }
214         /* NOTREACHED */
215 }
216 #endif
217
218 gchar *
219 g_find_program_in_path (const gchar *program)
220 {
221         char *p;
222         char *x, *l;
223         gchar *curdir = NULL;
224         char *save = NULL;
225 #ifdef G_OS_WIN32
226         char *program_exe;
227         char *suffix_list[5] = {".exe",".cmd",".bat",".com",NULL};
228         int listx;
229         gboolean hasSuffix;
230 #endif
231
232         g_return_val_if_fail (program != NULL, NULL);
233         x = p = g_strdup (g_getenv ("PATH"));
234
235         if (x == NULL || *x == '\0') {
236                 curdir = g_get_current_dir ();
237                 x = curdir;
238         }
239
240 #ifdef G_OS_WIN32
241         /* see if program already has a suffix */
242         listx = 0;
243         hasSuffix = FALSE;
244         while (!hasSuffix && suffix_list[listx]) {
245                 hasSuffix = g_str_has_suffix(program,suffix_list[listx++]);
246         }
247 #endif
248
249         while ((l = strtok_r (x, G_SEARCHPATH_SEPARATOR_S, &save)) != NULL){
250                 char *probe_path; 
251                 
252                 x = NULL;
253                 probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program, NULL);
254                 if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */
255                         g_free (curdir);
256                         g_free (p);
257                         return probe_path;
258                 }
259                 g_free (probe_path);
260
261 #ifdef G_OS_WIN32
262                 /* check for program with a suffix attached */
263                 if (!hasSuffix) {
264                         listx = 0;
265                         while (suffix_list[listx]) {
266                                 program_exe = g_strjoin(NULL,program,suffix_list[listx],NULL);
267                                 probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program_exe, NULL);
268                                 if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */
269                                         g_free (curdir);
270                                         g_free (p);
271                                         g_free (program_exe);
272                                         return probe_path;
273                                 }
274                                 listx++;
275                                 g_free (probe_path);
276                                 g_free (program_exe);
277                         }
278                 }
279 #endif
280         }
281         g_free (curdir);
282         g_free (p);
283         return NULL;
284 }
285
286 static char *name;
287
288 void
289 g_set_prgname (const gchar *prgname)
290 {
291         name = g_strdup (prgname);
292 }
293
294 gchar *
295 g_get_prgname (void)
296 {
297         return name;
298 }
299
300 gboolean
301 g_ensure_directory_exists (const gchar *filename)
302 {
303 #ifdef G_OS_WIN32
304         gchar *dir_utf8 = g_path_get_dirname (filename);
305         gunichar2 *p;
306         gunichar2 *dir_utf16 = NULL;
307         int retval;
308         
309         if (!dir_utf8 || !dir_utf8 [0])
310                 return FALSE;
311
312         dir_utf16 = g_utf8_to_utf16 (dir_utf8, strlen (dir_utf8), NULL, NULL, NULL);
313         g_free (dir_utf8);
314
315         if (!dir_utf16)
316                 return FALSE;
317
318         p = dir_utf16;
319
320         /* make life easy and only use one directory seperator */
321         while (*p != '\0')
322         {
323                 if (*p == '/')
324                         *p = '\\';
325                 p++;
326         }
327
328         p = dir_utf16;
329
330         /* get past C:\ )*/
331         while (*p++ != '\\')    
332         {
333         }
334
335         while (1) {
336                 gboolean bRet = FALSE;
337                 p = wcschr (p, '\\');
338                 if (p)
339                         *p = '\0';
340                 retval = _wmkdir (dir_utf16);
341                 if (retval != 0 && errno != EEXIST) {
342                         g_free (dir_utf16);
343                         return FALSE;
344                 }
345                 if (!p)
346                         break;
347                 *p++ = '\\';
348         }
349         
350         g_free (dir_utf16);
351         return TRUE;
352 #else
353         char *p;
354         gchar *dir = g_path_get_dirname (filename);
355         int retval;
356         struct stat sbuf;
357         
358         if (!dir || !dir [0]) {
359                 g_free (dir);
360                 return FALSE;
361         }
362         
363         if (stat (dir, &sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
364                 g_free (dir);
365                 return TRUE;
366         }
367         
368         p = dir;
369         while (*p == '/')
370                 p++;
371
372         while (1) {
373                 p = strchr (p, '/');
374                 if (p)
375                         *p = '\0';
376                 retval = mkdir (dir, 0777);
377                 if (retval != 0 && errno != EEXIST) {
378                         g_free (dir);
379                         return FALSE;
380                 }
381                 if (!p)
382                         break;
383                 *p++ = '/';
384         }
385         
386         g_free (dir);
387         return TRUE;
388 #endif
389 }
390