e6677bd24181099e56db8bc778152735f2186f57
[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         // first pass, convert and backslashes to slashes
132         char *s = abs_path;
133         while(*s) {
134                 if(*s == '\\') {
135                         *s = '/';
136                 }
137                 ++s;
138         }
139
140         // second pass, remove any "./" and "foo/.." parts
141         s = abs_path;
142         int len = strlen(s);
143         while((s = strstr(s, "./"))) {
144                 len -= 2;
145                 memmove(s, s + 2, len + 1);
146         }
147         assert(len == (int)strlen(abs_path));
148
149         s = abs_path;
150         while((s = strstr(s, "/.."))) {
151                 char *start = s;
152                 while(start > abs_path && *--start != '/');
153
154                 memmove(start, s + 3, len - 2);
155                 len -= (s + 3) - start;
156                 s = start;
157         }
158         assert(len == (int)strlen(abs_path));
159
160         // remove double slashes
161         while((s = strstr(abs_path, "//"))) {
162                 memmove(s, s + 1, len-- - (s - abs_path));
163         }
164         assert(len == (int)strlen(abs_path));
165
166         // remove trailing slash
167         if(abs_path[len - 1] == '/') {
168                 abs_path[--len] = 0;
169         }
170         assert(len == (int)strlen(abs_path));
171
172         // setup name and prefix pointers as necessary
173         name = suffix = 0;
174         s = abs_path;
175         while(*s) {
176                 if(*s == '/') name = s + 1;
177                 if(*s == '.') suffix = s;
178                 ++s;
179         }
180
181         if(!name) {
182                 name = abs_path;        // shouldn't happen, all paths start with a slash
183                 fprintf(stderr, "FSPath::sanitize: absolute path \"%s\" isn't absolute!\n", abs_path);
184         }
185
186         if(suffix == abs_path || suffix <= name) {
187                 suffix = 0;
188         }
189
190         // create parent name string
191         if(abs_path[0] == '/' && abs_path[1] == 0) {
192                 parent = 0;     // root dir has no parent
193         } else {
194                 int plen = name - 1 - abs_path;
195                 assert(plen > 0);
196
197                 parent = new char[plen + 1];
198                 memcpy(parent, abs_path, plen);
199                 parent[plen] = 0;
200         }
201 }
202
203 bool FSPath::set_path(const char *s)
204 {
205         char *tmp = make_abs_path(s);
206         if(!tmp) {
207                 return false;
208         }
209
210         delete [] abs_path;
211         delete [] parent;
212         abs_path = tmp;
213         parent = 0;
214
215         sanitize();
216         return true;
217 }
218
219 const char *FSPath::get_path() const
220 {
221         return abs_path;
222 }
223
224 const char *FSPath::get_name() const
225 {
226         return name;
227 }
228
229 const char *FSPath::get_suffix() const
230 {
231         return suffix;
232 }
233
234 const char *FSPath::get_parent() const
235 {
236         return parent;
237 }
238
239 bool FSPath::append_path(const char *s)
240 {
241         if(!s || !*s) return false;
242
243         if(!abs_path) {
244                 return set_path(s);
245         }
246
247         if(*s == '/') {
248                 return false;   // can't append an absolute path
249         }
250
251         int len = strlen(abs_path) + 1 + strlen(s);
252         try {
253                 char *tmp = new char[len + 1];
254
255                 delete [] abs_path;
256                 abs_path = tmp;
257                 delete [] parent;
258                 parent = 0;
259         }
260         catch(...) {
261                 return false;
262         }
263
264         sanitize();
265         return true;
266 }
267
268 bool FSPath::exists() const
269 {
270         struct stat st;
271         return abs_path && stat(abs_path, &st) != -1;
272 }
273
274 bool FSPath::is_file() const
275 {
276         struct stat st;
277         return abs_path && stat(abs_path, &st) != -1 && S_ISREG(st.st_mode);
278 }
279
280 bool FSPath::is_dir() const
281 {
282         struct stat st;
283         return abs_path && stat(abs_path, &st) != -1 && S_ISDIR(st.st_mode);
284 }
285
286 bool FSPath::is_dev() const
287 {
288         struct stat st;
289         unsigned int devmask = S_IFBLK | S_IFCHR;
290         return abs_path && stat(abs_path, &st) != -1 && (st.st_mode & devmask);
291 }
292
293 FSPath::operator const char* () const
294 {
295         return abs_path;
296 }