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 *
g_build_path(const gchar * separator,const gchar * first_element,...)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*
strrchr_seperator(const gchar * filename)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 *
g_path_get_dirname(const gchar * filename)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 *
g_path_get_basename(const char * filename)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 *
strtok_r(char * s,const char * delim,char ** last)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 *
g_find_program_in_path(const gchar * program)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
g_set_prgname(const gchar * prgname)289 g_set_prgname (const gchar *prgname)
290 {
291 	name = g_strdup (prgname);
292 }
293 
294 gchar *
g_get_prgname(void)295 g_get_prgname (void)
296 {
297 	return name;
298 }
299 
300 gboolean
g_ensure_directory_exists(const gchar * filename)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 
391