eb923d64422b378eb4761476c8c3b06a891d2aad
[vrfileman] / src / fspath.cc
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include "fspath.h"
7
8
9 char *make_abs_path(const char *s)
10 {
11         char wd[1024];
12         int slen = s ? strlen(s) : 0;
13         int len = slen;
14         char *res;
15
16         if(!s || *s != '/') {
17                 getcwd(wd, sizeof wd);
18                 int wdlen = strlen(wd);
19                 len = wdlen + 1 + slen;
20
21                 res = new char[len + 1];
22                 if(s) {
23                         sprintf(res, "%s/%s", wd, s);
24                 } else {
25                         memcpy(res, wd, len + 1);
26                 }
27         } else {
28                 res = new char[len + 1];
29                 memcpy(res, s, len + 1);
30         }
31
32         return res;
33 }
34
35 FSPath::FSPath()
36 {
37         abs_path = name = suffix = parent = 0;
38 }
39
40 FSPath::FSPath(const char *s)
41 {
42         abs_path = name = suffix = parent = 0;
43         set_path(s);
44 }
45
46 FSPath::~FSPath()
47 {
48         delete [] abs_path;
49         delete [] parent;
50 }
51
52 FSPath::FSPath(const FSPath &p)
53 {
54         abs_path = name = suffix = parent = 0;
55
56         if(abs_path) {
57                 int len = strlen(p.abs_path);
58                 abs_path = new char[len + 1];
59                 memcpy(abs_path, p.abs_path, len + 1);
60
61                 if(p.name) {
62                         name = abs_path + (p.name - p.abs_path);
63                 }
64                 if(p.suffix) {
65                         suffix = abs_path + (p.suffix - p.abs_path);
66                 }
67         }
68         if(p.parent) {
69                 int len = strlen(p.parent);
70                 parent = new char[len + 1];
71                 memcpy(parent, p.parent, len + 1);
72         }
73 }
74
75 FSPath &FSPath::operator =(const FSPath &p)
76 {
77         if(&p != this) {
78                 delete [] abs_path;
79                 delete [] parent;
80                 abs_path = name = suffix = parent = 0;
81
82                 if(p.abs_path) {
83                         int len = strlen(p.abs_path);
84                         abs_path = new char[len + 1];
85                         memcpy(abs_path, p.abs_path, len + 1);
86
87                         if(p.name) {
88                                 name = abs_path + (p.name - p.abs_path);
89                         }
90                         if(p.suffix) {
91                                 suffix = abs_path + (p.suffix - p.abs_path);
92                         }
93                 }
94                 if(p.parent) {
95                         int len = strlen(p.parent);
96                         parent = new char[len + 1];
97                         memcpy(parent, p.parent, len + 1);
98                 }
99         }
100         return *this;
101 }
102
103 FSPath::FSPath(FSPath &&p)
104 {
105         abs_path = p.abs_path;
106         name = p.name;
107         suffix = p.suffix;
108         parent = p.parent;
109
110         p.abs_path = p.name = p.suffix = p.parent = 0;
111 }
112
113 FSPath &FSPath::operator =(FSPath &&p)
114 {
115         if(&p != this) {
116                 delete [] abs_path;
117                 delete [] parent;
118
119                 abs_path = p.abs_path;
120                 name = p.name;
121                 suffix = p.suffix;
122                 parent = p.parent;
123
124                 p.abs_path = p.name = p.suffix = p.parent = 0;
125         }
126         return *this;
127 }
128
129 void FSPath::sanitize()
130 {
131         printf("sanitize: \"%s\"\n", abs_path);
132
133         // first pass, convert and backslashes to slashes
134         char *s = abs_path;
135         while(*s) {
136                 if(*s == '\\') {
137                         *s = '/';
138                 }
139                 ++s;
140         }
141
142         // second pass, remove any "./" and "foo/.." parts
143         s = abs_path;
144         int len = strlen(s);
145         while((s = strstr(s, "./"))) {
146                 len -= 2;
147                 memmove(s, s + 2, len + 1);
148         }
149         assert(len == (int)strlen(abs_path));
150
151         s = abs_path;
152         while((s = strstr(s, "/.."))) {
153                 char *start = s;
154                 while(start > abs_path && *--start != '/');
155
156                 memmove(start, s + 3, len - 2);
157                 len -= (s + 3) - start;
158                 s = start;
159         }
160         assert(len == (int)strlen(abs_path));
161
162         // remove double slashes
163         while((s = strstr(abs_path, "//"))) {
164                 memmove(s, s + 1, len-- - (s - abs_path));
165         }
166         assert(len == (int)strlen(abs_path));
167
168         // remove trailing slash if abs_path is not just a slash (root)
169         if(len > 1 && abs_path[len - 1] == '/') {
170                 abs_path[--len] = 0;
171         }
172         assert(len == (int)strlen(abs_path));
173
174         // setup name and prefix pointers as necessary
175         name = suffix = 0;
176         s = abs_path;
177         while(*s) {
178                 if(*s == '/') name = s + 1;
179                 if(*s == '.') suffix = s;
180                 ++s;
181         }
182
183         if(!name) {
184                 name = abs_path;        // shouldn't happen, all paths start with a slash
185                 fprintf(stderr, "FSPath::sanitize: absolute path \"%s\" isn't absolute!\n", abs_path);
186         }
187
188         if(suffix == abs_path || suffix <= name) {
189                 suffix = 0;
190         }
191
192         // create parent name string
193         if(abs_path[0] == '/' && abs_path[1] == 0) {
194                 parent = 0;     // root dir has no parent
195         } else {
196                 int plen = name - 1 - abs_path;
197                 assert(plen >= 0);
198
199                 if(plen == 0) {
200                         plen = 1;       // our parent is root, so we need the slash
201                 }
202
203                 parent = new char[plen + 1];
204                 memcpy(parent, abs_path, plen);
205                 parent[plen] = 0;
206         }
207 }
208
209 bool FSPath::set_path(const char *s)
210 {
211         char *tmp = make_abs_path(s);
212         if(!tmp) {
213                 return false;
214         }
215
216         delete [] abs_path;
217         delete [] parent;
218         abs_path = tmp;
219         parent = 0;
220
221         sanitize();
222         return true;
223 }
224
225 const char *FSPath::get_path() const
226 {
227         return abs_path;
228 }
229
230 const char *FSPath::get_name() const
231 {
232         return name;
233 }
234
235 const char *FSPath::get_suffix() const
236 {
237         return suffix;
238 }
239
240 const char *FSPath::get_parent() const
241 {
242         return parent;
243 }
244
245 bool FSPath::append_path(const char *s)
246 {
247         if(!s || !*s) return false;
248
249         if(!abs_path) {
250                 return set_path(s);
251         }
252
253         if(*s == '/') {
254                 return false;   // can't append an absolute path
255         }
256
257         int len = strlen(abs_path) + 1 + strlen(s);
258         try {
259                 char *tmp = new char[len + 1];
260
261                 delete [] abs_path;
262                 abs_path = tmp;
263                 delete [] parent;
264                 parent = 0;
265         }
266         catch(...) {
267                 return false;
268         }
269
270         sanitize();
271         return true;
272 }
273
274 bool FSPath::exists() const
275 {
276         struct stat st;
277         return abs_path && stat(abs_path, &st) != -1;
278 }
279
280 bool FSPath::is_file() const
281 {
282         struct stat st;
283         return abs_path && stat(abs_path, &st) != -1 && S_ISREG(st.st_mode);
284 }
285
286 bool FSPath::is_dir() const
287 {
288         struct stat st;
289         return abs_path && stat(abs_path, &st) != -1 && S_ISDIR(st.st_mode);
290 }
291
292 bool FSPath::is_dev() const
293 {
294         struct stat st;
295         unsigned int devmask = S_IFBLK | S_IFCHR;
296         return abs_path && stat(abs_path, &st) != -1 && (st.st_mode & devmask);
297 }
298
299 FSPath::operator const char* () const
300 {
301         return abs_path;
302 }