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>
35 int _wapi_open (const char *pathname, int flags, mode_t mode)
38 gchar *located_filename;
40 if (flags & O_CREAT) {
41 located_filename = mono_portability_find_file (pathname, FALSE);
42 if (located_filename == NULL) {
43 fd = open (pathname, flags, mode);
45 fd = open (located_filename, flags, mode);
46 g_free (located_filename);
49 fd = open (pathname, flags, mode);
51 (errno == ENOENT || errno == ENOTDIR) &&
53 int saved_errno = errno;
54 located_filename = mono_portability_find_file (pathname, TRUE);
56 if (located_filename == NULL) {
61 fd = open (located_filename, flags, mode);
62 g_free (located_filename);
70 int _wapi_access (const char *pathname, int mode)
74 ret = access (pathname, mode);
76 (errno == ENOENT || errno == ENOTDIR) &&
78 int saved_errno = errno;
79 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
81 if (located_filename == NULL) {
86 ret = access (located_filename, mode);
87 g_free (located_filename);
93 int _wapi_chmod (const char *pathname, mode_t mode)
97 ret = chmod (pathname, mode);
99 (errno == ENOENT || errno == ENOTDIR) &&
100 IS_PORTABILITY_SET) {
101 int saved_errno = errno;
102 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
104 if (located_filename == NULL) {
109 ret = chmod (located_filename, mode);
110 g_free (located_filename);
116 int _wapi_utime (const char *filename, const struct utimbuf *buf)
120 ret = utime (filename, buf);
123 IS_PORTABILITY_SET) {
124 int saved_errno = errno;
125 gchar *located_filename = mono_portability_find_file (filename, TRUE);
127 if (located_filename == NULL) {
132 ret = utime (located_filename, buf);
133 g_free (located_filename);
139 int _wapi_unlink (const char *pathname)
143 ret = unlink (pathname);
145 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
146 IS_PORTABILITY_SET) {
147 int saved_errno = errno;
148 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
150 if (located_filename == NULL) {
155 ret = unlink (located_filename);
156 g_free (located_filename);
162 int _wapi_rename (const char *oldpath, const char *newpath)
165 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
167 if (located_newpath == NULL) {
168 ret = rename (oldpath, newpath);
170 ret = rename (oldpath, located_newpath);
173 (errno == EISDIR || errno == ENAMETOOLONG ||
174 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
175 IS_PORTABILITY_SET) {
176 int saved_errno = errno;
177 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
179 if (located_oldpath == NULL) {
180 g_free (located_oldpath);
181 g_free (located_newpath);
187 ret = rename (located_oldpath, located_newpath);
188 g_free (located_oldpath);
190 g_free (located_newpath);
196 int _wapi_stat (const char *path, struct stat *buf)
200 ret = stat (path, buf);
202 (errno == ENOENT || errno == ENOTDIR) &&
203 IS_PORTABILITY_SET) {
204 int saved_errno = errno;
205 gchar *located_filename = mono_portability_find_file (path, TRUE);
207 if (located_filename == NULL) {
212 ret = stat (located_filename, buf);
213 g_free (located_filename);
219 int _wapi_lstat (const char *path, struct stat *buf)
223 ret = lstat (path, buf);
225 (errno == ENOENT || errno == ENOTDIR) &&
226 IS_PORTABILITY_SET) {
227 int saved_errno = errno;
228 gchar *located_filename = mono_portability_find_file (path, TRUE);
230 if (located_filename == NULL) {
235 ret = lstat (located_filename, buf);
236 g_free (located_filename);
242 int _wapi_mkdir (const char *pathname, mode_t mode)
245 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
247 if (located_filename == NULL) {
248 ret = mkdir (pathname, mode);
250 ret = mkdir (located_filename, mode);
251 g_free (located_filename);
257 int _wapi_rmdir (const char *pathname)
261 ret = rmdir (pathname);
263 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
264 IS_PORTABILITY_SET) {
265 int saved_errno = errno;
266 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
268 if (located_filename == NULL) {
273 ret = rmdir (located_filename);
274 g_free (located_filename);
280 int _wapi_chdir (const char *path)
286 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
287 IS_PORTABILITY_SET) {
288 int saved_errno = errno;
289 gchar *located_filename = mono_portability_find_file (path, TRUE);
291 if (located_filename == NULL) {
296 ret = chdir (located_filename);
297 g_free (located_filename);
303 gchar *_wapi_basename (const gchar *filename)
305 gchar *new_filename = g_strdup (filename), *ret;
307 if (IS_PORTABILITY_SET) {
308 g_strdelimit (new_filename, "\\", '/');
311 if (IS_PORTABILITY_DRIVE &&
312 g_ascii_isalpha (new_filename[0]) &&
313 (new_filename[1] == ':')) {
314 int len = strlen (new_filename);
316 g_memmove (new_filename, new_filename + 2, len - 2);
317 new_filename[len - 2] = '\0';
320 ret = g_path_get_basename (new_filename);
321 g_free (new_filename);
326 gchar *_wapi_dirname (const gchar *filename)
328 gchar *new_filename = g_strdup (filename), *ret;
330 if (IS_PORTABILITY_SET) {
331 g_strdelimit (new_filename, "\\", '/');
334 if (IS_PORTABILITY_DRIVE &&
335 g_ascii_isalpha (new_filename[0]) &&
336 (new_filename[1] == ':')) {
337 int len = strlen (new_filename);
339 g_memmove (new_filename, new_filename + 2, len - 2);
340 new_filename[len - 2] = '\0';
343 ret = g_path_get_dirname (new_filename);
344 g_free (new_filename);
349 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
353 ret = g_dir_open (path, flags, error);
355 ((*error)->code == G_FILE_ERROR_NOENT ||
356 (*error)->code == G_FILE_ERROR_NOTDIR ||
357 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
358 IS_PORTABILITY_SET) {
359 gchar *located_filename = mono_portability_find_file (path, TRUE);
360 GError *tmp_error = NULL;
362 if (located_filename == NULL) {
366 ret = g_dir_open (located_filename, flags, &tmp_error);
367 g_free (located_filename);
368 if (tmp_error == NULL) {
369 g_clear_error (error);
378 file_compare (gconstpointer a, gconstpointer b)
380 gchar *astr = *(gchar **) a;
381 gchar *bstr = *(gchar **) b;
383 return strcmp (astr, bstr);
387 get_errno_from_g_file_error (gint error)
391 case G_FILE_ERROR_ACCES:
396 case G_FILE_ERROR_NAMETOOLONG:
397 error = ENAMETOOLONG;
401 case G_FILE_ERROR_NOENT:
406 case G_FILE_ERROR_NOTDIR:
411 case G_FILE_ERROR_NXIO:
416 case G_FILE_ERROR_NODEV:
421 case G_FILE_ERROR_ROFS:
426 case G_FILE_ERROR_TXTBSY:
431 case G_FILE_ERROR_FAULT:
436 case G_FILE_ERROR_LOOP:
441 case G_FILE_ERROR_NOSPC:
446 case G_FILE_ERROR_NOMEM:
451 case G_FILE_ERROR_MFILE:
456 case G_FILE_ERROR_NFILE:
461 case G_FILE_ERROR_BADF:
466 case G_FILE_ERROR_INVAL:
471 case G_FILE_ERROR_PIPE:
476 case G_FILE_ERROR_AGAIN:
481 case G_FILE_ERROR_INTR:
486 case G_FILE_ERROR_IO:
491 case G_FILE_ERROR_PERM:
495 case G_FILE_ERROR_FAILED:
496 error = ERROR_INVALID_PARAMETER;
503 /* scandir using glib */
504 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
507 GError *error = NULL;
511 wapi_glob_t glob_buf;
514 dir = _wapi_g_dir_open (dirname, 0, &error);
516 /* g_dir_open returns ENOENT on directories on which we don't
517 * have read/x permission */
518 gint errnum = get_errno_from_g_file_error (error->code);
519 g_error_free (error);
520 if (errnum == ENOENT &&
521 !_wapi_access (dirname, F_OK) &&
522 _wapi_access (dirname, R_OK|X_OK)) {
530 if (IS_PORTABILITY_CASE) {
531 flags = WAPI_GLOB_IGNORECASE;
534 result = _wapi_glob (dir, pattern, flags, &glob_buf);
535 if (g_str_has_suffix (pattern, ".*")) {
536 /* Special-case the patterns ending in '.*', as
537 * windows also matches entries with no extension with
540 * TODO: should this be a MONO_IOMAP option?
542 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
546 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
556 if (glob_buf.gl_pathc == 0) {
558 } else if (result != 0) {
562 names = g_ptr_array_new ();
563 for (i = 0; i < glob_buf.gl_pathc; i++) {
564 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
567 _wapi_globfree (&glob_buf);
571 g_ptr_array_sort (names, file_compare);
572 g_ptr_array_set_size (names, result + 1);
574 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
576 g_ptr_array_free (names, TRUE);