Reenable disabled test
[mono.git] / mono / utils / mono-path.c
1 /*
2  * mono-path.c: Routines for handling path names.
3  * 
4  * Authors:
5  *      Gonzalo Paniagua Javier (gonzalo@novell.com)
6  *      Miguel de Icaza (miguel@novell.com)
7  *
8  * (C) 2006 Novell, Inc.  http://www.novell.com
9  *
10  */
11 #include <config.h>
12 #include <glib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 /* This is only needed for the mono_path_canonicalize code, MAXSYMLINKS, could be moved */
20 #ifdef HAVE_SYS_PARAM_H
21 #include <sys/param.h>
22 #endif
23
24 #include "mono-path.h"
25
26 /* Embedded systems lack MAXSYMLINKS */
27 #ifndef MAXSYMLINKS
28 #define MAXSYMLINKS 3
29 #endif
30
31 /* Resolves '..' and '.' references in a path. If the path provided is relative,
32  * it will be relative to the current directory */
33
34 /* For Native Client, the above is not true.  Since there is no getcwd we fill */
35 /* in the file being passed in relative to '.' and don't resolve it            */
36 gchar *
37 mono_path_canonicalize (const char *path)
38 {
39         gchar *abspath, *pos, *lastpos, *dest;
40         int backc;
41
42         if (g_path_is_absolute (path)) {
43                 abspath = g_strdup (path);
44         } else {
45 #ifdef __native_client__
46                 gchar *tmpdir = ".";
47                 abspath = g_build_filename (tmpdir, path, NULL);
48 #else
49                 gchar *tmpdir = g_get_current_dir ();
50                 abspath = g_build_filename (tmpdir, path, NULL);
51                 g_free (tmpdir);
52 #endif
53         }
54
55 #ifdef HOST_WIN32
56         g_strdelimit (abspath, "/", '\\');
57 #endif
58         abspath = g_strreverse (abspath);
59
60         backc = 0;
61         dest = lastpos = abspath;
62         pos = strchr (lastpos, G_DIR_SEPARATOR);
63
64         while (pos != NULL) {
65                 int len = pos - lastpos;
66                 if (len == 1 && lastpos [0] == '.') {
67                         // nop
68                 } else if (len == 2 && lastpos [0] == '.' && lastpos [1] == '.') {
69                         backc++;
70                 } else if (len > 0) {
71                         if (backc > 0) {
72                                 backc--;
73                         } else {
74                                 if (dest != lastpos) 
75                                         /* The two strings can overlap */
76                                         memmove (dest, lastpos, len + 1);
77                                 dest += len + 1;
78                         }
79                 }
80                 lastpos = pos + 1;
81                 pos = strchr (lastpos, G_DIR_SEPARATOR);
82         }
83
84 #ifdef HOST_WIN32
85         /* Avoid removing the first '\' for UNC paths. We must make sure that it's indeed an UNC path
86         by checking if the \\ pair happens exactly at the end of the string.
87         */
88         if (*(lastpos-1) == G_DIR_SEPARATOR && *(lastpos-2) == G_DIR_SEPARATOR && *lastpos == 0)
89                 lastpos = lastpos-1;
90 #endif
91         
92         if (dest != lastpos) strcpy (dest, lastpos);
93         return g_strreverse (abspath);
94 }
95
96 /*
97  * This ensures that the path that we store points to the final file
98  * not a path to a symlink.
99  */
100 #if !defined(PLATFORM_NO_SYMLINKS)
101 static gchar *
102 resolve_symlink (const char *path)
103 {
104         char *p, *concat, *dir;
105         char buffer [PATH_MAX+1];
106         int n, iterations = 0;
107
108         p = g_strdup (path);
109         do {
110                 iterations++;
111                 n = readlink (p, buffer, sizeof (buffer)-1);
112                 if (n < 0){
113                         char *copy = p;
114                         p = mono_path_canonicalize (copy);
115                         g_free (copy);
116                         return p;
117                 }
118                 
119                 buffer [n] = 0;
120                 if (!g_path_is_absolute (buffer)) {
121                         dir = g_path_get_dirname (p);
122                         concat = g_build_filename (dir, buffer, NULL);
123                         g_free (dir);
124                 } else {
125                         concat = g_strdup (buffer);
126                 }
127                 g_free (p);
128                 p = mono_path_canonicalize (concat);
129                 g_free (concat);
130         } while (iterations < MAXSYMLINKS);
131
132         return p;
133 }
134 #endif
135
136 gchar *
137 mono_path_resolve_symlinks (const char *path)
138 {
139 #if defined(PLATFORM_NO_SYMLINKS)
140         return mono_path_canonicalize (path);
141 #else
142         gchar **split = g_strsplit (path, G_DIR_SEPARATOR_S, -1);
143         gchar *p = g_strdup ("");
144         int i;
145
146         for (i = 0; split [i] != NULL; i++) {
147                 gchar *tmp = NULL;
148
149                 // resolve_symlink of "" goes into canonicalize which resolves to cwd
150                 if (strcmp (split [i], "") != 0) {
151                         tmp = g_strdup_printf ("%s%s", p, split [i]);
152                         g_free (p);
153                         p = resolve_symlink (tmp);
154                         g_free (tmp);
155                 }
156
157                 if (split [i+1] != NULL) {
158                         tmp = g_strdup_printf ("%s%s", p, G_DIR_SEPARATOR_S);
159                         g_free (p);
160                         p = tmp;
161                 }
162         }
163
164         g_strfreev (split);
165         return p;
166 #endif
167 }
168