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