ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.9
Committed: Mon Oct 12 14:00:57 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_81
Changes since 1.8: +7 -6 lines
Log Message:
clarify license

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
30 #include "define.h"
31 #include "path.h"
32
33
34 #if 0
35
36 /**
37 * Define DEBUG_PATH to enable debug output.
38 */
39 # define DEBUG_PATH
40 #endif
41
42 #if 0
43
44 /**
45 * Define TEST_PATH to compile this file as a stand-alone self-test application
46 * for the functions.
47 */
48 # define TEST_PATH
49
50 # define LOG fprintf
51 # define llevDebug stderr
52 # define llevError stderr
53 #endif
54
55
56 char *
57 path_combine (const char *src, const char *dst)
58 {
59 char *p;
60 static char path[HUGE_BUF];
61
62 if (*dst == '/')
63 {
64 /* absolute destination path => ignore source path */
65 strcpy (path, dst);
66 }
67 else
68 {
69 /* relative destination path => add after last '/' of source */
70 strcpy (path, src);
71 p = strrchr (path, '/');
72 if (p != NULL)
73 {
74 p++;
75 }
76 else
77 {
78 p = path;
79 if (*src == '/')
80 *p++ = '/';
81 }
82 strcpy (p, dst);
83 }
84
85 #if defined(DEBUG_PATH)
86 LOG (llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
87 #endif
88 return (path);
89 }
90
91 void
92 path_normalize (char *path)
93 {
94 char *p; /* points to the beginning of the path not yet processed; this is
95 either a path component or a path separator character */
96 char *q; /* points to the end of the path component p points to */
97 char *w; /* points to the end of the already normalized path; w <= p is
98 maintained */
99 size_t len; /* length of current component (which p points to) */
100
101 #if defined(DEBUG_PATH)
102 LOG (llevDebug, "path_normalize: input '%s'\n", path);
103 #endif
104
105 p = path;
106 w = p;
107 while (*p != '\0')
108 {
109
110 if (*p == '/')
111 {
112 if ((w == path && *path == '/') || (w > path && w[-1] != '/'))
113 *w++ = '/';
114 p++;
115 continue;
116 }
117
118 q = strchr (p, '/');
119 if (q == NULL)
120 q = p + strlen (p);
121 len = q - p;
122 assert (len > 0);
123
124 #if defined(DEBUG_PATH)
125 LOG (llevDebug, "path_normalize: checking '%.*s'\n", (int) len, p);
126 #endif
127
128 if (len == 1 && *p == '.')
129 {
130 /* remove current component */
131 }
132 else if (len == 2 && memcmp (p, "..", 2) == 0)
133 {
134 if (w == path || (w == path + 3 && memcmp (path, "../", 3) == 0))
135 {
136 /* keep ".." at beginning of relative path ("../x" => "../x") */
137 memmove (w, p, len);
138 w += len;
139 }
140 else if (w == path + 1 && *path == '/')
141 {
142 /* remove ".." at beginning of absolute path ("/../x" => "/x") */
143 }
144 else
145 {
146 /* remove both current component ".." and preceding one */
147 if (w > path && w[-1] == '/')
148 w--;
149 while (w > path && w[-1] != '/')
150 w--;
151 }
152 }
153 else
154 {
155 /* normal component ==> add it */
156 memmove (w, p, len);
157 w += len;
158 }
159
160 p = q;
161
162 #if defined(DEBUG_PATH)
163 LOG (llevDebug, "path_normalize: so far '%.*s'\n", (int) (w - path), path);
164 #endif
165 }
166
167 /* remove trailing slashes, but keep the one at the start of the path */
168 while (w > path + 1 && w[-1] == '/')
169 {
170 w--;
171 }
172
173 *w = '\0';
174
175 #if defined(DEBUG_PATH)
176 LOG (llevDebug, "path_normalize: result '%s'\n", path);
177 #endif
178 }
179
180 char *
181 path_combine_and_normalize (const char *src, const char *dst)
182 {
183 char *path;
184
185 path = path_combine (src, dst);
186 path_normalize (path);
187 return (path);
188 }
189
190 #if defined(TEST_PATH)
191 static void
192 check_combine (const char *src, const char *dst, const char *exp)
193 {
194 const char *res;
195
196 fprintf (stderr, "path_combine(%s, %s) = ", src, dst);
197 res = path_combine (src, dst);
198 fprintf (stderr, "%s", res);
199 if (strcmp (res, exp) != 0)
200 {
201 fprintf (stderr, ", should be %s\n", exp);
202 }
203 else
204 {
205 fprintf (stderr, " (OK)\n");
206 }
207 }
208
209 static void
210 check_normalize (const char *path, const char *exp0)
211 {
212 char tmp[HUGE_BUF];
213 char exp[HUGE_BUF];
214
215 strcpy (exp, exp0 == NULL ? path : exp0);
216
217 strcpy (tmp, path);
218 fprintf (stderr, "path_normalize(%s) = ", tmp);
219 path_normalize (tmp);
220 fprintf (stderr, "%s", tmp);
221 if (strcmp (tmp, exp) != 0)
222 {
223 fprintf (stderr, ", should be %s\n", exp);
224 }
225 else
226 {
227 fprintf (stderr, " (OK)\n");
228 }
229 }
230
231 static void
232 check_combine_and_normalize (const char *src, const char *dst, const char *exp)
233 {
234 const char *res;
235
236 fprintf (stderr, "path_combine_and_normalize(%s, %s) = ", src, dst);
237 res = path_combine_and_normalize (src, dst);
238 fprintf (stderr, "%s", res);
239 if (strcmp (res, exp) != 0)
240 {
241 fprintf (stderr, ", should be %s\n", exp);
242 }
243 else
244 {
245 fprintf (stderr, " (OK)\n");
246 }
247 }
248
249 int
250 main ()
251 {
252 check_combine ("/path1/file1", "/path2/file2", "/path2/file2");
253 check_combine ("path1/file1", "/path2/file2", "/path2/file2");
254 check_combine ("/path1/file1", "path2/file2", "/path1/path2/file2");
255 check_combine ("path1/file1", "path2/file2", "path1/path2/file2");
256 check_combine ("/path1", "/path2", "/path2");
257 check_combine ("path1", "/path2", "/path2");
258 check_combine ("/path1", "path2", "/path2");
259 check_combine ("path1", "path2", "path2");
260
261 check_normalize ("", NULL);
262 check_normalize ("/", NULL);
263 check_normalize ("path1/file1", NULL);
264 check_normalize ("/path1/file1", NULL);
265 check_normalize ("/path1//file1", "/path1/file1");
266 check_normalize ("//path1/file1", "/path1/file1");
267 check_normalize ("///////x////////y///////z////////", "/x/y/z");
268 check_normalize ("/a/b/../c/d/../e/../../f/g/../h", "/a/f/h");
269 check_normalize ("//a//b//..//c//d//..//e//..//..//f//g//..//h", "/a/f/h");
270 check_normalize ("../a", NULL);
271 check_normalize ("a/../../b", "../b");
272 check_normalize ("/../a", "/a");
273 check_normalize ("/a/../../b", "/b");
274 check_normalize ("./b/./c/.d/..e/./f", "b/c/.d/..e/f");
275 check_normalize ("/b/././././e", "/b/e");
276 check_normalize (".", ""); /* maybe the result should be "."? */
277 check_normalize ("/.", "/");
278 check_normalize ("./", ""); /* maybe the result should be "."? */
279 check_normalize ("/a/b/..", "/a");
280 check_normalize ("/a/b/../..", "/");
281 check_normalize ("/a/b/../../..", "/");
282 check_normalize ("a/b/..", "a");
283 check_normalize ("a/b/../..", "");
284 check_normalize ("a/b/../../..", "..");
285
286 check_combine_and_normalize ("/path1/file1", "/path2/file2", "/path2/file2");
287 check_combine_and_normalize ("path1/file1", "/path2/file2", "/path2/file2");
288 check_combine_and_normalize ("/path1/file1", "path2/file2", "/path1/path2/file2");
289 check_combine_and_normalize ("/path1", "/path2", "/path2");
290 check_combine_and_normalize ("path1", "/path2", "/path2");
291 check_combine_and_normalize ("/path1", "path2", "/path2");
292 check_combine_and_normalize ("/path1/file1/../u", "path2/x/../y/z/../a/b/..", "/path1/path2/y/a");
293 check_combine_and_normalize ("/path1/file1", "/path2//file2", "/path2/file2");
294 check_combine_and_normalize ("/path1/file1", "/..", "/");
295 check_combine_and_normalize ("/path1/file1", "../x", "/x");
296 check_combine_and_normalize ("/path1/file1", "../../../x", "/x");
297 check_combine_and_normalize ("/path1/file1", "/.x/..x/...x/x", "/.x/..x/...x/x");
298
299 return (0);
300 }
301 #endif