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.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include <sys/types.h>
29 #include <mono/io-layer/error.h>
30 #include <mono/io-layer/wapi_glob.h>
31 #include <mono/io-layer/io-portability.h>
32 #include <mono/utils/mono-io-portability.h>
36 int _wapi_open (const char *pathname, int flags, mode_t mode)
39 gchar *located_filename;
41 if (flags & O_CREAT) {
42 located_filename = mono_portability_find_file (pathname, FALSE);
43 if (located_filename == NULL) {
44 fd = open (pathname, flags, mode);
46 fd = open (located_filename, flags, mode);
47 g_free (located_filename);
50 fd = open (pathname, flags, mode);
52 (errno == ENOENT || errno == ENOTDIR) &&
54 int saved_errno = errno;
55 located_filename = mono_portability_find_file (pathname, TRUE);
57 if (located_filename == NULL) {
62 fd = open (located_filename, flags, mode);
63 g_free (located_filename);
71 int _wapi_access (const char *pathname, int mode)
75 ret = access (pathname, mode);
77 (errno == ENOENT || errno == ENOTDIR) &&
79 int saved_errno = errno;
80 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
82 if (located_filename == NULL) {
87 ret = access (located_filename, mode);
88 g_free (located_filename);
94 int _wapi_chmod (const char *pathname, mode_t mode)
98 ret = chmod (pathname, mode);
100 (errno == ENOENT || errno == ENOTDIR) &&
101 IS_PORTABILITY_SET) {
102 int saved_errno = errno;
103 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
105 if (located_filename == NULL) {
110 ret = chmod (located_filename, mode);
111 g_free (located_filename);
117 int _wapi_utime (const char *filename, const struct utimbuf *buf)
121 ret = utime (filename, buf);
124 IS_PORTABILITY_SET) {
125 int saved_errno = errno;
126 gchar *located_filename = mono_portability_find_file (filename, TRUE);
128 if (located_filename == NULL) {
133 ret = utime (located_filename, buf);
134 g_free (located_filename);
140 int _wapi_unlink (const char *pathname)
144 ret = unlink (pathname);
146 (errno == ENOENT || errno == ENOTDIR || errno == EISDIR) &&
147 IS_PORTABILITY_SET) {
148 int saved_errno = errno;
149 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
151 if (located_filename == NULL) {
156 ret = unlink (located_filename);
157 g_free (located_filename);
163 int _wapi_rename (const char *oldpath, const char *newpath)
166 gchar *located_newpath = mono_portability_find_file (newpath, FALSE);
168 if (located_newpath == NULL) {
169 ret = rename (oldpath, newpath);
171 ret = rename (oldpath, located_newpath);
174 (errno == EISDIR || errno == ENAMETOOLONG ||
175 errno == ENOENT || errno == ENOTDIR || errno == EXDEV) &&
176 IS_PORTABILITY_SET) {
177 int saved_errno = errno;
178 gchar *located_oldpath = mono_portability_find_file (oldpath, TRUE);
180 if (located_oldpath == NULL) {
181 g_free (located_oldpath);
182 g_free (located_newpath);
188 ret = rename (located_oldpath, located_newpath);
189 g_free (located_oldpath);
191 g_free (located_newpath);
197 int _wapi_stat (const char *path, struct stat *buf)
201 ret = stat (path, buf);
203 (errno == ENOENT || errno == ENOTDIR) &&
204 IS_PORTABILITY_SET) {
205 int saved_errno = errno;
206 gchar *located_filename = mono_portability_find_file (path, TRUE);
208 if (located_filename == NULL) {
213 ret = stat (located_filename, buf);
214 g_free (located_filename);
220 int _wapi_lstat (const char *path, struct stat *buf)
224 ret = lstat (path, buf);
226 (errno == ENOENT || errno == ENOTDIR) &&
227 IS_PORTABILITY_SET) {
228 int saved_errno = errno;
229 gchar *located_filename = mono_portability_find_file (path, TRUE);
231 if (located_filename == NULL) {
236 ret = lstat (located_filename, buf);
237 g_free (located_filename);
243 int _wapi_mkdir (const char *pathname, mode_t mode)
246 gchar *located_filename = mono_portability_find_file (pathname, FALSE);
248 if (located_filename == NULL) {
249 ret = mkdir (pathname, mode);
251 ret = mkdir (located_filename, mode);
252 g_free (located_filename);
258 int _wapi_rmdir (const char *pathname)
262 ret = rmdir (pathname);
264 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
265 IS_PORTABILITY_SET) {
266 int saved_errno = errno;
267 gchar *located_filename = mono_portability_find_file (pathname, TRUE);
269 if (located_filename == NULL) {
274 ret = rmdir (located_filename);
275 g_free (located_filename);
281 int _wapi_chdir (const char *path)
287 (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) &&
288 IS_PORTABILITY_SET) {
289 int saved_errno = errno;
290 gchar *located_filename = mono_portability_find_file (path, TRUE);
292 if (located_filename == NULL) {
297 ret = chdir (located_filename);
298 g_free (located_filename);
304 gchar *_wapi_basename (const gchar *filename)
306 gchar *new_filename = g_strdup (filename), *ret;
308 if (IS_PORTABILITY_SET) {
309 g_strdelimit (new_filename, "\\", '/');
312 if (IS_PORTABILITY_DRIVE &&
313 g_ascii_isalpha (new_filename[0]) &&
314 (new_filename[1] == ':')) {
315 int len = strlen (new_filename);
317 g_memmove (new_filename, new_filename + 2, len - 2);
318 new_filename[len - 2] = '\0';
321 ret = g_path_get_basename (new_filename);
322 g_free (new_filename);
327 gchar *_wapi_dirname (const gchar *filename)
329 gchar *new_filename = g_strdup (filename), *ret;
331 if (IS_PORTABILITY_SET) {
332 g_strdelimit (new_filename, "\\", '/');
335 if (IS_PORTABILITY_DRIVE &&
336 g_ascii_isalpha (new_filename[0]) &&
337 (new_filename[1] == ':')) {
338 int len = strlen (new_filename);
340 g_memmove (new_filename, new_filename + 2, len - 2);
341 new_filename[len - 2] = '\0';
344 ret = g_path_get_dirname (new_filename);
345 g_free (new_filename);
350 GDir *_wapi_g_dir_open (const gchar *path, guint flags, GError **error)
354 ret = g_dir_open (path, flags, error);
356 ((*error)->code == G_FILE_ERROR_NOENT ||
357 (*error)->code == G_FILE_ERROR_NOTDIR ||
358 (*error)->code == G_FILE_ERROR_NAMETOOLONG) &&
359 IS_PORTABILITY_SET) {
360 gchar *located_filename = mono_portability_find_file (path, TRUE);
361 GError *tmp_error = NULL;
363 if (located_filename == NULL) {
367 ret = g_dir_open (located_filename, flags, &tmp_error);
368 g_free (located_filename);
369 if (tmp_error == NULL) {
370 g_clear_error (error);
379 file_compare (gconstpointer a, gconstpointer b)
381 gchar *astr = *(gchar **) a;
382 gchar *bstr = *(gchar **) b;
384 return strcmp (astr, bstr);
388 get_errno_from_g_file_error (gint error)
392 case G_FILE_ERROR_ACCES:
397 case G_FILE_ERROR_NAMETOOLONG:
398 error = ENAMETOOLONG;
402 case G_FILE_ERROR_NOENT:
407 case G_FILE_ERROR_NOTDIR:
412 case G_FILE_ERROR_NXIO:
417 case G_FILE_ERROR_NODEV:
422 case G_FILE_ERROR_ROFS:
427 case G_FILE_ERROR_TXTBSY:
432 case G_FILE_ERROR_FAULT:
437 case G_FILE_ERROR_LOOP:
442 case G_FILE_ERROR_NOSPC:
447 case G_FILE_ERROR_NOMEM:
452 case G_FILE_ERROR_MFILE:
457 case G_FILE_ERROR_NFILE:
462 case G_FILE_ERROR_BADF:
467 case G_FILE_ERROR_INVAL:
472 case G_FILE_ERROR_PIPE:
477 case G_FILE_ERROR_AGAIN:
482 case G_FILE_ERROR_INTR:
487 case G_FILE_ERROR_IO:
492 case G_FILE_ERROR_PERM:
496 case G_FILE_ERROR_FAILED:
497 error = ERROR_INVALID_PARAMETER;
504 /* scandir using glib */
505 gint _wapi_io_scandir (const gchar *dirname, const gchar *pattern,
508 GError *error = NULL;
512 wapi_glob_t glob_buf;
515 dir = _wapi_g_dir_open (dirname, 0, &error);
517 /* g_dir_open returns ENOENT on directories on which we don't
518 * have read/x permission */
519 gint errnum = get_errno_from_g_file_error (error->code);
520 g_error_free (error);
521 if (errnum == ENOENT &&
522 !_wapi_access (dirname, F_OK) &&
523 _wapi_access (dirname, R_OK|X_OK)) {
531 if (IS_PORTABILITY_CASE) {
532 flags = WAPI_GLOB_IGNORECASE;
535 result = _wapi_glob (dir, pattern, flags, &glob_buf);
536 if (g_str_has_suffix (pattern, ".*")) {
537 /* Special-case the patterns ending in '.*', as
538 * windows also matches entries with no extension with
541 * TODO: should this be a MONO_IOMAP option?
543 gchar *pattern2 = g_strndup (pattern, strlen (pattern) - 2);
547 result2 = _wapi_glob (dir, pattern2, flags | WAPI_GLOB_APPEND | WAPI_GLOB_UNIQUE, &glob_buf);
557 if (glob_buf.gl_pathc == 0) {
559 } else if (result != 0) {
563 names = g_ptr_array_new ();
564 for (i = 0; i < glob_buf.gl_pathc; i++) {
565 g_ptr_array_add (names, g_strdup (glob_buf.gl_pathv[i]));
568 _wapi_globfree (&glob_buf);
572 g_ptr_array_sort (names, file_compare);
573 g_ptr_array_set_size (names, result + 1);
575 *namelist = (gchar **) g_ptr_array_free (names, FALSE);
577 g_ptr_array_free (names, TRUE);