88b5d25a71904ff89e0923b1b7f967034f0b3c34
[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
37 /* There are a couple of tests for this method in mono/test/mono-path.cs */
38 gchar *
39 mono_path_canonicalize (const char *path)
40 {
41         gchar *abspath, *pos, *lastpos, *dest;
42         int backc;
43
44         if (g_path_is_absolute (path)) {
45                 abspath = g_strdup (path);
46         } else {
47 #ifdef __native_client__
48                 gchar *tmpdir = ".";
49                 abspath = g_build_filename (tmpdir, path, NULL);
50 #else
51                 gchar *tmpdir = g_get_current_dir ();
52                 abspath = g_build_filename (tmpdir, path, NULL);
53                 g_free (tmpdir);
54 #endif
55         }
56
57 #ifdef HOST_WIN32
58         g_strdelimit (abspath, "/", '\\');
59 #endif
60         abspath = g_strreverse (abspath);
61
62         backc = 0;
63         dest = lastpos = abspath;
64         pos = strchr (lastpos, G_DIR_SEPARATOR);
65
66         while (pos != NULL) {
67                 int len = pos - lastpos;
68                 if (len == 1 && lastpos [0] == '.') {
69                         // nop
70                 } else if (len == 2 && lastpos [0] == '.' && lastpos [1] == '.') {
71                         backc++;
72                 } else if (len > 0) {
73                         if (backc > 0) {
74                                 backc--;
75                         } else {
76                                 if (dest != lastpos) 
77                                         /* The two strings can overlap */
78                                         memmove (dest, lastpos, len + 1);
79                                 dest += len + 1;
80                         }
81                 }
82                 lastpos = pos + 1;
83                 pos = strchr (lastpos, G_DIR_SEPARATOR);
84         }
85
86 #ifdef HOST_WIN32
87         /* Avoid removing the first '\' for UNC paths. We must make sure that it's indeed an UNC path
88         by checking if the \\ pair happens exactly at the end of the string.
89         */
90         if (*(lastpos-1) == G_DIR_SEPARATOR && *(lastpos-2) == G_DIR_SEPARATOR && *lastpos == 0)
91                 lastpos = lastpos-1;
92 #endif
93         
94         if (dest != lastpos) strcpy (dest, lastpos);
95         
96         g_strreverse (abspath);
97
98         /* We strip away all trailing dir separators. This is not correct for the root directory,
99          * since we'll return an empty string, so re-append a dir separator if there is none in the
100          * result */
101         if (strchr (abspath, G_DIR_SEPARATOR) == NULL) {
102                 int len = strlen (abspath);
103                 abspath = g_realloc (abspath, len + 2);
104                 abspath [len] = G_DIR_SEPARATOR;
105                 abspath [len+1] = 0;
106         }
107
108         return abspath;
109 }
110
111 /*
112  * This ensures that the path that we store points to the final file
113  * not a path to a symlink.
114  */
115 #if !defined(PLATFORM_NO_SYMLINKS)
116 static gchar *
117 resolve_symlink (const char *path)
118 {
119         char *p, *concat, *dir;
120         char buffer [PATH_MAX+1];
121         int n, iterations = 0;
122
123         p = g_strdup (path);
124         do {
125                 iterations++;
126                 n = readlink (p, buffer, sizeof (buffer)-1);
127                 if (n < 0){
128                         char *copy = p;
129                         p = mono_path_canonicalize (copy);
130                         g_free (copy);
131                         return p;
132                 }
133                 
134                 buffer [n] = 0;
135                 if (!g_path_is_absolute (buffer)) {
136                         dir = g_path_get_dirname (p);
137                         concat = g_build_filename (dir, buffer, NULL);
138                         g_free (dir);
139                 } else {
140                         concat = g_strdup (buffer);
141                 }
142                 g_free (p);
143                 p = mono_path_canonicalize (concat);
144                 g_free (concat);
145         } while (iterations < MAXSYMLINKS);
146
147         return p;
148 }
149 #endif
150
151 gchar *
152 mono_path_resolve_symlinks (const char *path)
153 {
154 #if defined(PLATFORM_NO_SYMLINKS)
155         return mono_path_canonicalize (path);
156 #else
157         gchar **split = g_strsplit (path, G_DIR_SEPARATOR_S, -1);
158         gchar *p = g_strdup ("");
159         int i;
160
161         for (i = 0; split [i] != NULL; i++) {
162                 gchar *tmp = NULL;
163
164                 // resolve_symlink of "" goes into canonicalize which resolves to cwd
165                 if (strcmp (split [i], "") != 0) {
166                         tmp = g_strdup_printf ("%s%s", p, split [i]);
167                         g_free (p);
168                         p = resolve_symlink (tmp);
169                         g_free (tmp);
170                 }
171
172                 if (split [i+1] != NULL) {
173                         tmp = g_strdup_printf ("%s%s", p, G_DIR_SEPARATOR_S);
174                         g_free (p);
175                         p = tmp;
176                 }
177         }
178
179         g_strfreev (split);
180         return p;
181 #endif
182 }
183