]> git.tdb.fi Git - libs/core.git/blob - source/fs/dir.cpp
717c11c132d7e4fe6e99995827e4918d443062fb
[libs/core.git] / source / fs / dir.cpp
1 #include <cstdlib>
2 #include <unistd.h>
3 #include <dirent.h>
4 #include <msp/core/application.h>
5 #include <msp/core/systemerror.h>
6 #include <msp/strings/regex.h>
7 #include <msp/strings/utils.h>
8 #include "dir.h"
9 #include "path.h"
10 #include "stat.h"
11 #include "utils.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace FS {
17
18 namespace
19 {
20
21 enum
22 {
23 #ifdef _WIN32
24         ITEMSEP = ';'
25 #else
26         ITEMSEP = ':'
27 #endif
28 };
29
30 /** Helper function to determine the location of the program's executable.
31 Caches the last result to cut down filesystem access with repeated calls. */
32 const Path &get_bin_dir(const string &argv0)
33 {
34         static string last_argv0;
35         static Path bin_dir;
36
37         if(!(argv0==last_argv0))
38         {
39                 Path exe;
40                 if(argv0.find('/')==string::npos)
41                 {
42                         const char *path = getenv("PATH");
43                         vector<string> dirs = split(path, ITEMSEP);
44                         for(vector<string>::const_iterator i=dirs.begin(); i!=dirs.end(); ++i)
45                                 if(exists(Path(*i)/argv0))
46                                 {
47                                         exe = realpath(Path(*i)/argv0);
48                                         break;
49                                 }
50                 }
51
52                 if(exe.empty())
53                         exe = realpath(argv0);
54
55                 last_argv0 = argv0;
56                 bin_dir = dirname(exe);
57         }
58
59         return bin_dir;
60 }
61
62 }
63
64
65 not_a_directory::not_a_directory(const Path &p):
66         runtime_error(p.str())
67 { }
68
69
70 void mkpath(const Path &path, int mode)
71 {
72         Path p;
73         for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
74         {
75                 p /= *i;
76 #ifdef _WIN32
77                 if(p.size()==1 && p.is_absolute())
78                         continue;
79 #endif
80                 if(FS::Stat st = stat(p))
81                 {
82                         if(!st.is_directory())
83                                 throw not_a_directory(p);
84                         continue;
85                 }
86                 else
87                         mkdir(p, mode);
88         }
89 }
90
91 void rmpath(const Path &path)
92 {
93         list<string> files = list_files(path);
94         for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
95         {
96                 Path p = path / *i;
97                 if(is_dir(p))
98                         rmpath(p);
99                 else
100                         unlink(p);
101         }
102
103         rmdir(path);
104 }
105
106 list<string> list_files(const Path &path)
107 {
108         return list_filtered(path, string());
109 }
110
111 list<string> list_filtered(const Path &path, const string &filter)
112 {
113         Regex r_filter(filter);
114
115         list<string> result;
116         DIR *dir = opendir(path.str().c_str());
117         if(!dir)
118                 throw system_error("opendir");
119
120         while(dirent *de = readdir(dir))
121         {
122                 const char *fn = de->d_name;
123                 if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) 
124                         continue;
125                 if(r_filter.match(fn))
126                         result.push_back(fn);
127         }
128         closedir(dir);
129
130         return result;
131 }
132
133 Path get_user_data_dir()
134 {
135         const string &name = Application::get_name();
136         if(name.empty())
137                 throw logic_error("application name not known");
138         return get_user_data_dir(name);
139 }
140
141 Path get_sys_conf_dir(const string &argv0)
142 {
143         if(argv0.empty())
144                 throw invalid_argument("get_sys_conf_dir");
145
146         Path dir = get_bin_dir(argv0);
147
148         if(dir[-1]=="bin" || dir[-1]=="sbin")
149         {
150                 dir /= "..";
151                 if(dir[-1]=="usr")
152                         dir /= "..";
153                 return dir/"etc";
154         }
155         else
156                 return dir;
157 }
158
159 Path get_sys_conf_dir()
160 {
161         const char *argv0 = Application::get_argv0();
162         if(!argv0)
163                 throw logic_error("no startup command");
164         return get_sys_conf_dir(argv0);
165 }
166
167 Path get_sys_data_dir(const string &argv0, const string &appname)
168 {
169         if(argv0.empty() || appname.empty())
170                 throw invalid_argument("get_sys_data_dir");
171
172         Path dir = get_bin_dir(argv0);
173
174         if(dir[-1]=="bin" || dir[-1]=="sbin")
175                 return dir/".."/"share"/appname;
176         else if(dir[-1]=="MacOS")
177                 return dir/".."/"Resources";
178         else
179                 return dir;
180 }
181
182 Path get_sys_data_dir()
183 {
184         const char *argv0 = Application::get_argv0();
185         if(!argv0)
186                 throw logic_error("no startup command");
187         return get_sys_data_dir(argv0, Application::get_name());
188 }
189
190 Path get_sys_lib_dir(const string &argv0, const string &appname)
191 {
192         if(argv0.empty() || appname.empty())
193                 throw invalid_argument("get_sys_data_dir");
194
195         Path dir = get_bin_dir(argv0);
196
197         if(dir[-1]=="bin" || dir[-1]=="sbin")
198                 return dir/".."/"lib"/appname;
199         else
200                 return dir;
201 }
202
203 Path get_sys_lib_dir()
204 {
205         const char *argv0 = Application::get_argv0();
206         if(!argv0)
207                 throw logic_error("no startup command");
208         return get_sys_lib_dir(argv0, Application::get_name());
209 }
210
211 Path path_lookup(const string &name, const list<Path> &paths)
212 {
213         for(list<Path>::const_iterator i=paths.begin(); i!=paths.end(); ++i)
214         {
215                 Path full = *i/name;
216                 if(exists(full))
217                         return realpath(full);
218         }
219
220         return Path();
221 }
222
223 Path path_lookup(const string &name)
224 {
225         const char *path = getenv("PATH");
226         vector<string> dirs = split(path, ITEMSEP);
227         return path_lookup(name, list<Path>(dirs.begin(), dirs.end()));
228 }
229
230 } // namespace FS
231 } // namespace Msp