ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.C
Revision: 1.3
Committed: Thu Sep 14 22:34:00 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.2: +0 -3 lines
Log Message:
indent

File Contents

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