ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.2
Committed: Sun Sep 10 16:00:23 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +216 -171 lines
Log Message:
indent

File Contents

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