ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.4
Committed: Thu May 17 21:32:08 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1
Changes since 1.3: +24 -0 lines
Log Message:
- prepare common/ for head_ => head change
- add some copyrights for files where they were missing

File Contents

# User Rev Content
1 root 1.4 /*
2     * CrossFire, A Multiplayer game
3     *
4     * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) Mark Wedel & Crossfire Development Team
6     * Copyright (C) Frank Tore Johansen
7     *
8     * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your 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 GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     *
22     * The authors can be reached via e-mail at <crossfire@schmorp.de>
23     */
24    
25 elmex 1.1 #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 root 1.2
36 elmex 1.1 /**
37     * Define DEBUG_PATH to enable debug output.
38     */
39 root 1.2 # define DEBUG_PATH
40 elmex 1.1 #endif
41    
42     #if 0
43 root 1.2
44 elmex 1.1 /**
45     * Define TEST_PATH to compile this file as a stand-alone self-test application
46     * for the functions.
47     */
48 root 1.2 # define TEST_PATH
49 elmex 1.1
50 root 1.2 # define LOG fprintf
51     # define llevDebug stderr
52     # define llevError stderr
53 elmex 1.1 #endif
54    
55    
56 root 1.2 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 elmex 1.1 }
82 root 1.2 strcpy (p, dst);
83 elmex 1.1 }
84    
85     #if defined(DEBUG_PATH)
86 root 1.2 LOG (llevDebug, "path_combine(%s, %s) = %s\n", src, dst, path);
87 elmex 1.1 #endif
88 root 1.2 return (path);
89 elmex 1.1 }
90    
91 root 1.2 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 elmex 1.1
101     #if defined(DEBUG_PATH)
102 root 1.2 LOG (llevDebug, "path_normalize: input '%s'\n", path);
103 elmex 1.1 #endif
104    
105 root 1.2 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 elmex 1.1 }
117    
118 root 1.2 q = strchr (p, '/');
119     if (q == NULL)
120     q = p + strlen (p);
121     len = q - p;
122     assert (len > 0);
123 elmex 1.1
124     #if defined(DEBUG_PATH)
125 root 1.2 LOG (llevDebug, "path_normalize: checking '%.*s'\n", (int) len, p);
126 elmex 1.1 #endif
127    
128 root 1.2 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 elmex 1.1 }
152 root 1.2 }
153     else
154     {
155     /* normal component ==> add it */
156     memmove (w, p, len);
157     w += len;
158 elmex 1.1 }
159    
160 root 1.2 p = q;
161 elmex 1.1
162     #if defined(DEBUG_PATH)
163 root 1.2 LOG (llevDebug, "path_normalize: so far '%.*s'\n", (int) (w - path), path);
164 elmex 1.1 #endif
165     }
166    
167 root 1.2 /* remove trailing slashes, but keep the one at the start of the path */
168     while (w > path + 1 && w[-1] == '/')
169     {
170     w--;
171 elmex 1.1 }
172    
173 root 1.2 *w = '\0';
174 elmex 1.1
175     #if defined(DEBUG_PATH)
176 root 1.2 LOG (llevDebug, "path_normalize: result '%s'\n", path);
177 elmex 1.1 #endif
178     }
179    
180 root 1.2 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 elmex 1.1
209 root 1.2 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 elmex 1.1 }
230    
231 root 1.2 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 elmex 1.1
249 root 1.2 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 elmex 1.1
299 root 1.2 return (0);
300 elmex 1.1 }
301     #endif