2 * io-portability.c: Optional filename mangling to try to cope with
3 * badly-written non-portable windows apps
6 * Dick Porter (dick@ximian.com)
8 * Copyright (c) 2006 Novell, Inc.
20 #include <sys/types.h>
28 #include <mono/io-layer/error.h>
29 #include <mono/io-layer/wapi_glob.h>
30 #include <mono/io-layer/io-portability.h>
31 #include <mono/utils/mono-io-portability.h>
33 #include <mono/utils/mono-mutex.h>
37 int _wapi_open (const char *pathname, int flags, mode_t mode)
40 gchar *located_filename;
42 if (flags & O_CREAT) {
43 located_filename = mono_portability_find_file (pathname, FALSE);
44 if (located_filename == NULL) {
45 fd = open (pathname, flags, mode);
47 fd = open (located_filename, flags, mode);
48 g_free (located_filename);
51 fd = open (pathname, flags, mode);
53 (errno == ENOENT || errno == ENOTDIR) &&
55 int saved_errno = errno;
56 located_filename = mono_portability_find_file (pathname, TRUE);
58 if (located_filename == NULL) {
63 fd = open (located_filename, flags, mode);
64 g_free (located_filename);
72 int _wapi_access (const char *pathname, int mode)
76 ret = access (pathname, mode);
78 (errno == ENOENT || errno == ENOTDIR) &&
80 int saved_errno = errno;
81 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
83 if (located_filename == NULL) {
88 ret = access (located_filename, mode);
89 g_free (located_filename);
95 int _wapi_chmod (const char *pathname, mode_t mode)
99 ret = chmod (pathname, mode);
101 (errno == ENOENT || errno == ENOTDIR) &&
102 IS_PORTABILITY_SET) {
103 int saved_errno = errno;
104 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
106 if (located_filename == NULL) {
111 ret = chmod (located_filename, mode);
112 g_free (located_filename);
118 int _wapi_utime (const char *filename, const struct utimbuf *buf)
122 ret = utime (filename, buf);
125 IS_PORTABILITY_SET) {
126 int saved_errno = errno;
127 gchar *located_filename = mono_portability_find_file (filename, TRUE);
129 if (located_filename == NULL) {
134 ret = utime (located_filename, buf);
135 g_free (located_filename);
141 int _wapi_unlink (const char *pathname)
145 ret = unlink (pathname);
147 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
148 IS_PORTABILITY_SET) {
149 int saved_errno = errno;
150 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
152 if (located_filename == NULL) {
157 ret = unlink (located_filename);
158 g_free (located_filename);
164 int _wapi_rename (const char *oldpath, const char *newpath)
167 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
169 if (located_newpath == NULL) {
170 ret = rename (oldpath, newpath);
172 ret = rename (oldpath, located_newpath);
175 (errno == EISDIR || errno == ENAMETOOLONG ||
176 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
177 IS_PORTABILITY_SET) {
178 int saved_errno = errno;
179 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
181 if (located_oldpath == NULL) {
182 g_free (located_oldpath);
183 g_free (located_newpath);
189 ret = rename (located_oldpath, located_newpath);
190 g_free (located_oldpath);
192 g_free (located_newpath);
198 int _wapi_stat (const char *path, struct stat *buf)
202 ret = stat (path, buf);
204 (errno == ENOENT || errno == ENOTDIR) &&
205 IS_PORTABILITY_SET) {
206 int saved_errno = errno;
207 gchar *located_filename = mono_portability_find_file (path, TRUE);
209 if (located_filename == NULL) {
214 ret = stat (located_filename, buf);
215 g_free (located_filename);
221 int _wapi_lstat (const char *path, struct stat *buf)
225 ret = lstat (path, buf);
227 (errno == ENOENT || errno == ENOTDIR) &&
228 IS_PORTABILITY_SET) {
229 int saved_errno = errno;
230 gchar *located_filename = mono_portability_find_file (path, TRUE);
232 if (located_filename == NULL) {
237 ret = lstat (located_filename, buf);
238 g_free (located_filename);
244 int _wapi_mkdir (const char *pathname, mode_t mode)
247 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
249 if (located_filename == NULL) {
250 ret = mkdir (pathname, mode);
252 ret = mkdir (located_filename, mode);
253 g_free (located_filename);
259 int _wapi_rmdir (const char *pathname)
263 ret = rmdir (pathname);
265 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
266 IS_PORTABILITY_SET) {
267 int saved_errno = errno;
268 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
270 if (located_filename == NULL) {
275 ret = rmdir (located_filename);
276 g_free (located_filename);
282 int _wapi_chdir (const char *path)
288 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
289 IS_PORTABILITY_SET) {
290 int saved_errno = errno;
291 gchar *located_filename = mono_portability_find_file (path, TRUE);
293 if (located_filename == NULL) {
298 ret = chdir (located_filename);
299 g_free (located_filename);
305 gchar *_wapi_basename (const gchar *filename)
307 gchar *new_filename = g_strdup (filename), *ret;
309 if (IS_PORTABILITY_SET) {
310 g_strdelimit (new_filename, "\\", '/');
313 if (IS_PORTABILITY_DRIVE &&
314 g_ascii_isalpha (new_filename[0]) &&
315 (new_filename[1] == ':')) {
316 int len = strlen (new_filename);
318 g_memmove (new_filename, new_filename + 2, len - 2);
319 new_filename[len - 2] = '\0';
322 ret = g_path_get_basename (new_filename);
323 g_free (new_filename);
328 gchar *_wapi_dirname (const gchar *filename)
330 gchar *new_filename = g_strdup (filename), *ret;
332 if (IS_PORTABILITY_SET) {
333 g_strdelimit (new_filename, "\\", '/');
336 if (IS_PORTABILITY_DRIVE &&
337 g_ascii_isalpha (new_filename[0]) &&
338 (new_filename[1] == ':')) {
339 int len = strlen (new_filename);
341 g_memmove (new_filename, new_filename + 2, len - 2);
342 new_filename[len - 2] = '\0';
345 ret = g_path_get_dirname (new_filename);
346 g_free (new_filename);
351 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
355 ret = g_dir_open (path, flags, error);
357 ((*error)->code == G_FILE_ERROR_NOENT ||
358 (*error)->code == G_FILE_ERROR_NOTDIR ||
359 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
360 IS_PORTABILITY_SET) {
361 gchar *located_filename = mono_portability_find_file (path, TRUE);
362 GError *tmp_error = NULL;
364 if (located_filename == NULL) {
368 ret = g_dir_open (located_filename, flags, &tmp_error);
369 g_free (located_filename);
370 if (tmp_error == NULL) {
371 g_clear_error (error);
380 file_compare (gconstpointer a, gconstpointer b)
382 gchar *astr = *(gchar **) a;
383 gchar *bstr = *(gchar **) b;
385 return strcmp (astr, bstr);
389 get_errno_from_g_file_error (gint error)
393 case G_FILE_ERROR_ACCES:
398 case G_FILE_ERROR_NAMETOOLONG:
399 error = ENAMETOOLONG;
403 case G_FILE_ERROR_NOENT:
408 case G_FILE_ERROR_NOTDIR:
413 case G_FILE_ERROR_NXIO:
418 case G_FILE_ERROR_NODEV:
423 case G_FILE_ERROR_ROFS:
428 case G_FILE_ERROR_TXTBSY:
433 case G_FILE_ERROR_FAULT:
438 case G_FILE_ERROR_LOOP:
443 case G_FILE_ERROR_NOSPC:
448 case G_FILE_ERROR_NOMEM:
453 case G_FILE_ERROR_MFILE:
458 case G_FILE_ERROR_NFILE:
463 case G_FILE_ERROR_BADF:
468 case G_FILE_ERROR_INVAL:
473 case G_FILE_ERROR_PIPE:
478 case G_FILE_ERROR_AGAIN:
483 case G_FILE_ERROR_INTR:
488 case G_FILE_ERROR_IO:
493 case G_FILE_ERROR_PERM:
497 case G_FILE_ERROR_FAILED:
498 error = ERROR_INVALID_PARAMETER;
505 /* scandir using glib */
506 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
509 GError *error = NULL;
513 wapi_glob_t glob_buf;
516 dir = _wapi_g_dir_open (dirname, 0, &error);
518 /* g_dir_open returns ENOENT on directories on which we don't
519 * have read/x permission */
520 gint errnum = get_errno_from_g_file_error (error->code);
521 g_error_free (error);
522 if (errnum == ENOENT &&
523 !_wapi_access (dirname, F_OK) &&
524 _wapi_access (dirname, R_OK|X_OK)) {
532 if (IS_PORTABILITY_CASE) {
533 flags = WAPI_GLOB_IGNORECASE;
536 result = _wapi_glob (dir, pattern, flags, &glob_buf);
537 if (g_str_has_suffix (pattern, ".*")) {
538 /* Special-case the patterns ending in '.*', as
539 * windows also matches entries with no extension with
542 * TODO: should this be a MONO_IOMAP option?
544 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
548 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
558 if (glob_buf.gl_pathc == 0) {
560 } else if (result != 0) {
564 names = g_ptr_array_new ();
565 for (i = 0; i < glob_buf.gl_pathc; i++) {
566 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
569 _wapi_globfree (&glob_buf);
573 g_ptr_array_sort (names, file_compare);
574 g_ptr_array_set_size (names, result + 1);
576 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
578 g_ptr_array_free (names, TRUE);