Do not go beyond our string boundaries
[mono.git] / eglib / src / 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
33 #ifdef G_OS_WIN32
34 #include <direct.h> 
35 #endif
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40
41 gchar *
42 g_build_path (const gchar *separator, const gchar *first_element, ...)
43 {
44         GString *result;
45         const char *s, *p, *next;
46         size_t slen;
47         va_list args;
48         
49         g_return_val_if_fail (separator != NULL, NULL);
50
51         if (first_element == NULL)
52                 return g_strdup ("");
53
54         result = g_string_sized_new (48);
55
56         slen = strlen (separator);
57         
58         va_start (args, first_element);
59         for (s = first_element; s != NULL; s = next){
60                 next = va_arg (args, char *);
61                 p = (s + strlen (s));
62
63                 if (next && p - slen > s){
64                         for (; strncmp (p-slen, separator, slen) == 0; ){
65                                 p -= slen;
66                         }
67                 }
68                 g_string_append_len (result, s, p - s);
69
70                 if (next && *next){
71                         int reslen = strlen (result->str);
72                         
73                         if (reslen >= slen && strncmp (separator, result->str + reslen - slen, slen))
74                                 g_string_append (result, separator);
75
76                         for (; strncmp (next, separator, slen) == 0; )
77                                 next += slen;
78                 }
79         }
80         g_string_append_c (result, 0);
81         va_end (args);
82
83         return g_string_free (result, FALSE);
84 }
85
86 gchar *
87 g_path_get_dirname (const gchar *filename)
88 {
89         char *p, *r;
90         size_t count;
91         g_return_val_if_fail (filename != NULL, NULL);
92
93         p = strrchr (filename, G_DIR_SEPARATOR);
94         if (p == NULL)
95                 return g_strdup (".");
96         if (p == filename)
97                 return g_strdup ("/");
98         count = p - filename;
99         r = g_malloc (count + 1);
100         strncpy (r, filename, count);
101         r [count] = 0;
102
103         return r;
104 }
105
106 gchar *
107 g_path_get_basename (const char *filename)
108 {
109         char *r;
110         g_return_val_if_fail (filename != NULL, NULL);
111
112         /* Empty filename -> . */
113         if (!*filename)
114                 return g_strdup (".");
115
116         /* No separator -> filename */
117         r = strrchr (filename, G_DIR_SEPARATOR);
118         if (r == NULL)
119                 return g_strdup (filename);
120
121         /* Trailing slash, remove component */
122         if (r [1] == 0){
123                 char *copy = g_strdup (filename);
124                 copy [r-filename] = 0;
125                 r = strrchr (copy, G_DIR_SEPARATOR);
126
127                 if (r == NULL){
128                         g_free (copy);                  
129                         return g_strdup ("/");
130                 }
131                 r = g_strdup (&r[1]);
132                 g_free (copy);
133                 return r;
134         }
135
136         return g_strdup (&r[1]);
137 }
138
139 #ifndef HAVE_STRTOK_R
140 // This is from BSD's strtok_r
141
142 char *
143 strtok_r(char *s, const char *delim, char **last)
144 {
145         char *spanp;
146         int c, sc;
147         char *tok;
148         
149         if (s == NULL && (s = *last) == NULL)
150                 return NULL;
151         
152         /*
153          * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
154          */
155 cont:
156         c = *s++;
157         for (spanp = (char *)delim; (sc = *spanp++) != 0; ){
158                 if (c == sc)
159                         goto cont;
160         }
161
162         if (c == 0){         /* no non-delimiter characters */
163                 *last = NULL;
164                 return NULL;
165         }
166         tok = s - 1;
167
168         /*
169          * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
170          * Note that delim must have one NUL; we stop if we see that, too.
171          */
172         for (;;){
173                 c = *s++;
174                 spanp = (char *)delim;
175                 do {
176                         if ((sc = *spanp++) == c) {
177                                 if (c == 0)
178                                         s = NULL;
179                                 else {
180                                         char *w = s - 1;
181                                         *w = '\0';
182                                 }
183                                 *last = s;
184                                 return tok;
185                         }
186                 }
187                 while (sc != 0);
188         }
189         /* NOTREACHED */
190 }
191 #endif
192
193 gchar *
194 g_find_program_in_path (const gchar *program)
195 {
196         char *p = g_strdup (g_getenv ("PATH"));
197         char *x = p, *l;
198         gchar *curdir = NULL;
199         char *save;
200 #ifdef G_OS_WIN32
201         char *program_exe;
202         char *suffix_list[5] = {".exe",".cmd",".bat",".com",NULL};
203         int listx;
204         gboolean hasSuffix;
205 #endif
206
207         g_return_val_if_fail (program != NULL, NULL);
208
209         if (x == NULL || *x == '\0') {
210                 curdir = g_get_current_dir ();
211                 x = curdir;
212         }
213
214 #ifdef G_OS_WIN32
215         /* see if program already has a suffix */
216         listx = 0;
217         hasSuffix = FALSE;
218         while (!hasSuffix && suffix_list[listx]) {
219                 hasSuffix = g_str_has_suffix(program,suffix_list[listx++]);
220         }
221 #endif
222
223         while ((l = strtok_r (x, G_SEARCHPATH_SEPARATOR_S, &save)) != NULL){
224                 char *probe_path; 
225                 
226                 x = NULL;
227                 probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program, NULL);
228                 if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */
229                         g_free (curdir);
230                         g_free (p);
231                         return probe_path;
232                 }
233                 g_free (probe_path);
234
235 #ifdef G_OS_WIN32
236                 /* check for program with a suffix attached */
237                 if (!hasSuffix) {
238                         listx = 0;
239                         while (suffix_list[listx]) {
240                                 program_exe = g_strjoin(NULL,program,suffix_list[listx],NULL);
241                                 probe_path = g_build_path (G_DIR_SEPARATOR_S, l, program_exe, NULL);
242                                 if (access (probe_path, X_OK) == 0){ /* FIXME: on windows this is just a read permissions test */
243                                         g_free (curdir);
244                                         g_free (p);
245                                         g_free (program_exe);
246                                         return probe_path;
247                                 }
248                                 listx++;
249                                 g_free (probe_path);
250                                 g_free (program_exe);
251                         }
252                 }
253 #endif
254         }
255         g_free (curdir);
256         g_free (p);
257         return NULL;
258 }
259
260 static char *name;
261
262 void
263 g_set_prgname (const gchar *prgname)
264 {
265         name = g_strdup (prgname);
266 }
267
268 gchar *
269 g_get_prgname (void)
270 {
271         return name;
272 }