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

# User Rev Content
1 root 1.4 /*
2 root 1.7 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.4 *
4 root 1.8 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7 root 1.4 *
8 root 1.9 * 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 root 1.4 *
13 root 1.6 * 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 root 1.4 *
18 root 1.9 * 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 root 1.5 *
22 root 1.7 * The authors can be reached via e-mail to <support@deliantra.net>
23 root 1.4 */
24    
25 elmex 1.1 #include <assert.h>
26     #include <stdio.h>
27     #include <string.h>
28     #include <global.h>
29 root 1.10 #include <limits.h>
30 elmex 1.1
31     #include "define.h"
32     #include "path.h"
33    
34 root 1.10 #ifndef PATH_MAX
35     # define PATH_MAX 8192
36     #endif
37 elmex 1.1
38     #if 0
39 root 1.2
40 elmex 1.1 /**
41     * Define DEBUG_PATH to enable debug output.
42     */
43 root 1.2 # define DEBUG_PATH
44 elmex 1.1 #endif
45    
46     #if 0
47 root 1.2
48 elmex 1.1 /**
49     * Define TEST_PATH to compile this file as a stand-alone self-test application
50     * for the functions.
51     */
52 root 1.2 # define TEST_PATH
53 elmex 1.1
54 root 1.2 # define LOG fprintf
55     # define llevDebug stderr
56     # define llevError stderr
57 elmex 1.1 #endif
58    
59    
60 root 1.2 char *
61     path_combine (const char *src, const char *dst)
62     {
63     char *p;
64 root 1.10 static char path[PATH_MAX];
65 root 1.2
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 root 1.10 if (p)
77     p++;
78 root 1.2 else
79     {
80     p = path;
81     if (*src == '/')
82     *p++ = '/';
83 elmex 1.1 }
84 root 1.10
85 root 1.2 strcpy (p, dst);
86 elmex 1.1 }
87    
88     #if defined(DEBUG_PATH)
89 root 1.2 LOG (llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
90 elmex 1.1 #endif
91 root 1.10 return path;
92 elmex 1.1 }
93    
94 root 1.2 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 elmex 1.1
104     #if defined(DEBUG_PATH)
105 root 1.2 LOG (llevDebug, "path_normalize: input '%s'\n", path);
106 elmex 1.1 #endif
107    
108 root 1.2 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 elmex 1.1 }
120    
121 root 1.2 q = strchr (p, '/');
122     if (q == NULL)
123     q = p + strlen (p);
124     len = q - p;
125     assert (len > 0);
126 elmex 1.1
127     #if defined(DEBUG_PATH)
128 root 1.2 LOG (llevDebug, "path_normalize: checking '%.*s'\n", (int) len, p);
129 elmex 1.1 #endif
130    
131 root 1.2 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 elmex 1.1 }
155 root 1.2 }
156     else
157     {
158     /* normal component ==> add it */
159     memmove (w, p, len);
160     w += len;
161 elmex 1.1 }
162    
163 root 1.2 p = q;
164 elmex 1.1
165     #if defined(DEBUG_PATH)
166 root 1.2 LOG (llevDebug, "path_normalize: so far '%.*s'\n", (int) (w - path), path);
167 elmex 1.1 #endif
168     }
169    
170 root 1.2 /* remove trailing slashes, but keep the one at the start of the path */
171     while (w > path + 1 && w[-1] == '/')
172     {
173     w--;
174 elmex 1.1 }
175    
176 root 1.2 *w = '\0';
177 elmex 1.1
178     #if defined(DEBUG_PATH)
179 root 1.2 LOG (llevDebug, "path_normalize: result '%s'\n", path);
180 elmex 1.1 #endif
181     }
182    
183 root 1.2 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 elmex 1.1
212 root 1.2 static void
213     check_normalize (const char *path, const char *exp0)
214     {
215 root 1.10 char tmp[PATH_MAX];
216     char exp[PATH_MAX];
217 root 1.2
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 root 1.10 fprintf (stderr, ", should be %s\n", exp);
226 root 1.2 else
227 root 1.10 fprintf (stderr, " (OK)\n");
228 elmex 1.1 }
229    
230 root 1.2 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 root 1.10 fprintf (stderr, ", should be %s\n", exp);
240 root 1.2 else
241 root 1.10 fprintf (stderr, " (OK)\n");
242 root 1.2 }
243 elmex 1.1
244 root 1.2 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 elmex 1.1
294 root 1.2 return (0);
295 elmex 1.1 }
296     #endif