ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.8
Committed: Tue May 6 16:55:25 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_80, rel-2_6, rel-2_7, rel-2_72, rel-2_73, rel-2_71, rel-2_76, rel-2_77, rel-2_74, rel-2_75, rel-2_54, rel-2_55, rel-2_78, rel-2_79, rel-2_56, rel-2_61
Changes since 1.7: +1 -1 lines
Log Message:
update copyright

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