Add common_ancestor function
[libs/core.git] / source / fs / utils.cpp
1 #include <cstdio>
2 #ifndef WIN32
3 #include <unistd.h>
4 #endif
5 #include <msp/core/systemerror.h>
6 #include <msp/strings/utils.h>
7 #include "dir.h"
8 #include "path.h"
9 #include "stat.h"
10 #include "utils.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace FS {
16
17 string basename(const Path &p)
18 {
19         return p[-1];
20 }
21
22 Path dirname(const Path &p)
23 {
24         if(p.size()==1)
25         {
26                 if(p.is_absolute())
27                         return p;
28                 return ".";
29         }
30         return p.subpath(0, p.size()-1);
31 }
32
33 string basepart(const string &fn)
34 {
35         unsigned dot = fn.rfind('.');
36         return fn.substr(0, dot);
37 }
38
39 string extpart(const string &fn)
40 {
41         string::size_type dot = fn.rfind('.');
42         if(dot==string::npos)
43                 return string();
44         return fn.substr(dot);
45 }
46
47 Path fix_case(const Path &path)
48 {
49         bool found = true;
50         Path result;
51         for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
52         {
53                 if(!found || *i=="/")
54                         result /= *i;
55                 else
56                 {
57                         list<string> files;
58                         if(result.size())
59                                 files = list_files(result);
60                         else
61                                 files = list_files(".");
62
63                         found = false;
64                         for(list<string>::iterator j=files.begin(); (j!=files.end() && !found); ++j)
65                                 if(!strcasecmp(*j,*i))
66                                 {
67                                         result /= *j;
68                                         found = true;
69                                 }
70
71                         if(!found)
72                                 result /= *i;
73                 }
74         }
75
76         return result;
77 }
78
79 Path readlink(const Path &link)
80 {
81 #ifdef WIN32
82         (void)link;
83         throw logic_error("no symbolic links on win32");
84 #else
85         char buf[4096];
86         int len = ::readlink(link.str().c_str(), buf, sizeof(buf));
87         if(len==-1)
88                 throw system_error("readlink");
89         return string(buf, len);
90 #endif
91 }
92
93 Path realpath(const Path &path)
94 {
95 #ifdef WIN32
96         if(path.is_absolute())
97                 return path;
98         else
99                 return getcwd()/path;
100 #else
101         list<string> queue(path.begin(), path.end());
102         if(!path.is_absolute())
103         {
104                 Path cwd = getcwd();
105                 queue.insert(queue.begin(), cwd.begin(), cwd.end());
106         }
107
108         Path real;
109         unsigned n_links = 0;
110         while(!queue.empty())
111         {
112                 Path next = real/queue.front();
113                 queue.pop_front();
114
115                 if(is_link(next))
116                 {
117                         if(++n_links>64)
118                                 throw runtime_error("too many symbolic links");
119                         Path link = readlink(next);
120                         queue.insert(queue.begin(), link.begin(), link.end());
121                 }
122                 else
123                         real = next;
124         }
125
126         return real;
127 #endif
128 }
129
130 void rename(const Path &from, const Path &to)
131 {
132         if(::rename(from.str().c_str(), to.str().c_str())==-1)
133                 throw system_error("rename");
134 }
135
136 void unlink(const Path &path)
137 {
138         if(::unlink(path.str().c_str())==-1)
139                 throw system_error("unlink");
140 }
141
142 Path relative(const Path &path, const Path &base)
143 {
144         Path::Iterator i = path.begin();
145         Path::Iterator j = base.begin();
146         for(; (i!=path.end() && j!=base.end() && *i==*j); ++i, ++j) ;
147
148         Path result;
149         for(; j!=base.end(); ++j)
150                 result /= "..";
151         for(; i!=path.end(); ++i)
152                 result /= *i;
153
154         return result;
155 }
156
157 Path common_ancestor(const Path &path1, const Path &path2)
158 {
159         Path::Iterator i = path1.begin();
160         Path::Iterator j = path2.begin();
161         Path result;
162         for(; (i!=path1.end() && j!=path2.end() && *i==*j); ++i, ++j)
163                 result /= *i;
164         return result;
165 }
166
167 int descendant_depth(const Path &path, const Path &parent)
168 {
169         Path::Iterator i = path.begin();
170         Path::Iterator j = parent.begin();
171         for(; (i!=path.end() && j!=parent.end() && *i==*j); ++i, ++j) ;
172
173         if(j!=parent.end())
174                 return -1;
175         
176         int result = 0;
177         for(; i!=path.end(); ++i)
178                 ++result;
179
180         return result;
181 }
182
183 } // namespace FS
184 } // namespace Msp