ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.11
Committed: Wed Nov 4 00:02:48 2009 UTC (14 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +0 -0 lines
State: FILE REMOVED
Log Message:
agpl reorganisation

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify it under
9 * the terms of the Affero GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the Affero GNU General Public License
19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 *
22 * The authors can be reached via e-mail to <support@deliantra.net>
23 */
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <global.h>
29 #include <limits.h>
30
31 #include "define.h"
32 #include "path.h"
33
34 #ifndef PATH_MAX
35 # define PATH_MAX 8192
36 #endif
37
38 #if 0
39
40 /**
41 * Define DEBUG_PATH to enable debug output.
42 */
43 # define DEBUG_PATH
44 #endif
45
46 #if 0
47
48 /**
49 * Define TEST_PATH to compile this file as a stand-alone self-test application
50 * for the functions.
51 */
52 # define TEST_PATH
53
54 # define LOG fprintf
55 # define llevDebug stderr
56 # define llevError stderr
57 #endif
58
59
60 char *
61 path_combine (const char *src, const char *dst)
62 {
63 char *p;
64 static char path[PATH_MAX];
65
66 if (*dst == '/')
67 {
68 /* absolute destination path => ignore source path */
69 strcpy (path, dst);
70 }
71 else
72 {
73 /* relative destination path => add after last '/' of source */
74 strcpy (path, src);
75 p = strrchr (path, '/');
76 if (p)
77 p++;
78 else
79 {
80 p = path;
81 if (*src == '/')
82 *p++ = '/';
83 }
84
85 strcpy (p, dst);
86 }
87
88 #if defined(DEBUG_PATH)
89 LOG (llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
90 #endif
91 return path;
92 }
93
94 void
95 path_normalize (char *path)
96 {
97 char *p; /* points to the beginning of the path not yet processed; this is
98 either a path component or a path separator character */
99 char *q; /* points to the end of the path component p points to */
100 char *w; /* points to the end of the already normalized path; w <= p is
101 maintained */
102 size_t len; /* length of current component (which p points to) */
103
104 #if defined(DEBUG_PATH)
105 LOG (llevDebug, "path_normalize: input '%s'\n", path);
106 #endif
107
108 p = path;
109 w = p;
110 while (*p != '\0')
111 {
112
113 if (*p == '/')
114 {
115 if ((w == path && *path == '/') || (w > path && w[-1] != '/'))
116 *w++ = '/';
117 p++;
118 continue;
119 }
120
121 q = strchr (p, '/');
122 if (q == NULL)
123 q = p + strlen (p);
124 len = q - p;
125 assert (len > 0);
126
127 #if defined(DEBUG_PATH)
128 LOG (llevDebug, "path_normalize: checking '%.*s'\n", (int) len, p);
129 #endif
130
131 if (len == 1 && *p == '.')
132 {
133 /* remove current component */
134 }
135 else if (len == 2 && memcmp (p, "..", 2) == 0)
136 {
137 if (w == path || (w == path + 3 && memcmp (path, "../", 3) == 0))
138 {
139 /* keep ".." at beginning of relative path ("../x" => "../x") */
140 memmove (w, p, len);
141 w += len;
142 }
143 else if (w == path + 1 && *path == '/')
144 {
145 /* remove ".." at beginning of absolute path ("/../x" => "/x") */
146 }
147 else
148 {
149 /* remove both current component ".." and preceding one */
150 if (w > path && w[-1] == '/')
151 w--;
152 while (w > path && w[-1] != '/')
153 w--;
154 }
155 }
156 else
157 {
158 /* normal component ==> add it */
159 memmove (w, p, len);
160 w += len;
161 }
162
163 p = q;
164
165 #if defined(DEBUG_PATH)
166 LOG (llevDebug, "path_normalize: so far '%.*s'\n", (int) (w - path), path);
167 #endif
168 }
169
170 /* remove trailing slashes, but keep the one at the start of the path */
171 while (w > path + 1 && w[-1] == '/')
172 {
173 w--;
174 }
175
176 *w = '\0';
177
178 #if defined(DEBUG_PATH)
179 LOG (llevDebug, "path_normalize: result '%s'\n", path);
180 #endif
181 }
182
183 char *
184 path_combine_and_normalize (const char *src, const char *dst)
185 {
186 char *path;
187
188 path = path_combine (src, dst);
189 path_normalize (path);
190 return (path);
191 }
192
193 #if defined(TEST_PATH)
194 static void
195 check_combine (const char *src, const char *dst, const char *exp)
196 {
197 const char *res;
198
199 fprintf (stderr, "path_combine(%s, %s) = ", src, dst);
200 res = path_combine (src, dst);
201 fprintf (stderr, "%s", res);
202 if (strcmp (res, exp) != 0)
203 {
204 fprintf (stderr, ", should be %s\n", exp);
205 }
206 else
207 {
208 fprintf (stderr, " (OK)\n");
209 }
210 }
211
212 static void
213 check_normalize (const char *path, const char *exp0)
214 {
215 char tmp[PATH_MAX];
216 char exp[PATH_MAX];
217
218 strcpy (exp, exp0 == NULL ? path : exp0);
219
220 strcpy (tmp, path);
221 fprintf (stderr, "path_normalize(%s) = ", tmp);
222 path_normalize (tmp);
223 fprintf (stderr, "%s", tmp);
224 if (strcmp (tmp, exp) != 0)
225 fprintf (stderr, ", should be %s\n", exp);
226 else
227 fprintf (stderr, " (OK)\n");
228 }
229
230 static void
231 check_combine_and_normalize (const char *src, const char *dst, const char *exp)
232 {
233 const char *res;
234
235 fprintf (stderr, "path_combine_and_normalize(%s, %s) = ", src, dst);
236 res = path_combine_and_normalize (src, dst);
237 fprintf (stderr, "%s", res);
238 if (strcmp (res, exp) != 0)
239 fprintf (stderr, ", should be %s\n", exp);
240 else
241 fprintf (stderr, " (OK)\n");
242 }
243
244 int
245 main ()
246 {
247 check_combine ("/path1/file1", "/path2/file2", "/path2/file2");
248 check_combine ("path1/file1", "/path2/file2", "/path2/file2");
249 check_combine ("/path1/file1", "path2/file2", "/path1/path2/file2");
250 check_combine ("path1/file1", "path2/file2", "path1/path2/file2");
251 check_combine ("/path1", "/path2", "/path2");
252 check_combine ("path1", "/path2", "/path2");
253 check_combine ("/path1", "path2", "/path2");
254 check_combine ("path1", "path2", "path2");
255
256 check_normalize ("", NULL);
257 check_normalize ("/", NULL);
258 check_normalize ("path1/file1", NULL);
259 check_normalize ("/path1/file1", NULL);
260 check_normalize ("/path1//file1", "/path1/file1");
261 check_normalize ("//path1/file1", "/path1/file1");
262 check_normalize ("///////x////////y///////z////////", "/x/y/z");
263 check_normalize ("/a/b/../c/d/../e/../../f/g/../h", "/a/f/h");
264 check_normalize ("//a//b//..//c//d//..//e//..//..//f//g//..//h", "/a/f/h");
265 check_normalize ("../a", NULL);
266 check_normalize ("a/../../b", "../b");
267 check_normalize ("/../a", "/a");
268 check_normalize ("/a/../../b", "/b");
269 check_normalize ("./b/./c/.d/..e/./f", "b/c/.d/..e/f");
270 check_normalize ("/b/././././e", "/b/e");
271 check_normalize (".", ""); /* maybe the result should be "."? */
272 check_normalize ("/.", "/");
273 check_normalize ("./", ""); /* maybe the result should be "."? */
274 check_normalize ("/a/b/..", "/a");
275 check_normalize ("/a/b/../..", "/");
276 check_normalize ("/a/b/../../..", "/");
277 check_normalize ("a/b/..", "a");
278 check_normalize ("a/b/../..", "");
279 check_normalize ("a/b/../../..", "..");
280
281 check_combine_and_normalize ("/path1/file1", "/path2/file2", "/path2/file2");
282 check_combine_and_normalize ("path1/file1", "/path2/file2", "/path2/file2");
283 check_combine_and_normalize ("/path1/file1", "path2/file2", "/path1/path2/file2");
284 check_combine_and_normalize ("/path1", "/path2", "/path2");
285 check_combine_and_normalize ("path1", "/path2", "/path2");
286 check_combine_and_normalize ("/path1", "path2", "/path2");
287 check_combine_and_normalize ("/path1/file1/../u", "path2/x/../y/z/../a/b/..", "/path1/path2/y/a");
288 check_combine_and_normalize ("/path1/file1", "/path2//file2", "/path2/file2");
289 check_combine_and_normalize ("/path1/file1", "/..", "/");
290 check_combine_and_normalize ("/path1/file1", "../x", "/x");
291 check_combine_and_normalize ("/path1/file1", "../../../x", "/x");
292 check_combine_and_normalize ("/path1/file1", "/.x/..x/...x/x", "/.x/..x/...x/x");
293
294 return (0);
295 }
296 #endif