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