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