ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/path.c
Revision: 1.1
Committed: Fri Feb 3 07:11:38 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UPSTREAM
Log Message:
Initial revision

File Contents

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