[coop] Refactor/reuse mono_value_box_handle/mono_value_box_checked and reduce raw...
[mono-project.git] / mono / utils / mono-path.c
blobdc222ac12b5edcafdcd8fcbcd89215ac7d380ecd
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)
9 * (C) 2006 Novell, Inc. http://www.novell.com
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
25 #include "mono-path.h"
27 /* Embedded systems lack MAXSYMLINKS */
28 #ifndef MAXSYMLINKS
29 #define MAXSYMLINKS 3
30 #endif
32 /* Resolves '..' and '.' references in a path. If the path provided is relative,
33 * it will be relative to the current directory */
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 */
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)
42 gchar *abspath, *pos, *lastpos, *dest;
43 int backc;
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);
53 #ifdef HOST_WIN32
54 g_strdelimit (abspath, '/', '\\');
55 #endif
56 abspath = g_strreverse (abspath);
58 backc = 0;
59 dest = lastpos = abspath;
60 pos = strchr (lastpos, G_DIR_SEPARATOR);
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;
78 lastpos = pos + 1;
79 pos = strchr (lastpos, G_DIR_SEPARATOR);
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.
86 if (*(lastpos-1) == G_DIR_SEPARATOR && *(lastpos-2) == G_DIR_SEPARATOR && *lastpos == 0)
87 lastpos = lastpos-1;
88 #endif
90 if (dest != lastpos) strcpy (dest, lastpos);
92 g_strreverse (abspath);
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;
104 return abspath;
108 * This ensures that the path that we store points to the final file
109 * not a path to a symlink.
111 #if !defined(HOST_NO_SYMLINKS)
112 static gchar *
113 resolve_symlink (const char *path)
115 char *p, *concat, *dir;
116 char buffer [PATH_MAX+1];
117 int n, iterations = 0;
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;
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);
138 g_free (p);
139 p = mono_path_canonicalize (concat);
140 g_free (concat);
141 } while (iterations < MAXSYMLINKS);
143 return p;
145 #endif
147 gchar *
148 mono_path_resolve_symlinks (const char *path)
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;
157 for (i = 0; split [i] != NULL; i++) {
158 gchar *tmp = NULL;
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);
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;
175 g_strfreev (split);
176 return p;
177 #endif
181 * mono_path_filename_in_basedir:
183 * Return \c TRUE if \p filename is in \p basedir
185 * Both paths must be absolute and using \c G_DIR_SEPARATOR for directory separators.
186 * If the file is in a subdirectory of \p basedir, returns \c FALSE.
187 * This function doesn't touch a filesystem, it looks solely at path names.
189 gboolean
190 mono_path_filename_in_basedir (const char *filename, const char *basedir)
192 const char *p = NULL;
193 if ((p = strstr (filename, basedir))) {
194 p += strlen (basedir);
195 if (*p != G_DIR_SEPARATOR)
196 return FALSE;
197 /* if it's in a subdir of the basedir, it doesn't count. */
198 if (strchr (p, G_DIR_SEPARATOR))
199 return FALSE;
200 return TRUE;
202 return FALSE;