Use native CreateDirectory/RemoveDirectory calls on Windows
[libs/core.git] / source / fs / dir.cpp
1 #include <cstdlib>
2 #include <cerrno>
3 #include <dirent.h>
4 #ifdef WIN32
5 #include <shlobj.h>
6 #else
7 #include <unistd.h>
8 #include <sys/stat.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 #ifdef WIN32
68         (void)mode;
69         if(!CreateDirectory(path.str().c_str(), NULL))
70                 throw system_error("CreateDirectory");
71 #else
72         if(::mkdir(path.str().c_str(), mode)==-1)
73                 throw system_error("mkdir");
74 #endif
75 }
76
77 void mkpath(const Path &path, int mode)
78 {
79         Path p;
80         for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
81         {
82                 p /= *i;
83 #ifdef WIN32
84                 if(p.size()==1 && p.is_absolute())
85                         continue;
86 #endif
87                 if(exists(p))
88                 {
89                         if(!is_dir(p))
90                                 throw not_a_directory(p);
91                         continue;
92                 }
93                 else
94                         mkdir(p, mode);
95         }
96 }
97
98 void rmdir(const Path &path)
99 {
100 #ifdef WIN32
101         if(!RemoveDirectory(path.str().c_str()))
102                 throw system_error("RemoveDirectory");
103 #else
104         if(::rmdir(path.str().c_str())==-1)
105                 throw system_error("rmdir");
106 #endif
107 }
108
109 void rmpath(const Path &path)
110 {
111         list<string> files = list_files(path);
112         for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
113         {
114                 Path p = path / *i;
115                 if(is_dir(p))
116                         rmpath(p);
117                 else
118                         unlink(p);
119         }
120
121         rmdir(path);
122 }
123
124 list<string> list_files(const Path &path)
125 {
126         return list_filtered(path, string());
127 }
128
129 list<string> list_filtered(const Path &path, const string &filter)
130 {
131         Regex r_filter(filter);
132
133         list<string> result;
134         DIR *dir = opendir(path.str().c_str());
135         if(dir)
136         {
137                 while(dirent *de = readdir(dir))
138                 {
139                         const char *fn = de->d_name;
140                         if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) 
141                                 continue;
142                         if(r_filter.match(fn))
143                                 result.push_back(fn);
144                 }
145                 closedir(dir);
146         }
147
148         return result;
149 }
150
151 Path getcwd()
152 {
153         char buf[1024];
154         return ::getcwd(buf, sizeof(buf));
155 }
156
157 Path get_home_dir()
158 {
159 #ifdef WIN32
160         char home[MAX_PATH];
161         if(SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, home)==S_OK)
162                 return home;
163 #else
164         const char *home = getenv("HOME");
165         if(home)
166                 return home;
167 #endif
168         return ".";
169 }
170
171 Path get_user_data_dir(const string &appname)
172 {
173 #ifdef WIN32
174         char datadir[MAX_PATH];
175         if(SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, datadir)==S_OK)
176                 return Path(datadir)/appname;
177         return ".";
178 #else
179         return get_home_dir()/("."+appname);
180 #endif
181 }
182
183 Path get_sys_conf_dir(const string &argv0)
184 {
185         Path dir = get_bin_dir(argv0);
186
187         if(dir[-1]=="bin" || dir[-1]=="sbin")
188         {
189                 dir /= "..";
190                 if(dir[-1]=="usr")
191                         dir /= "..";
192                 return dir/"etc";
193         }
194         else
195                 return dir;
196 }
197
198 Path get_sys_data_dir(const string &argv0, const string &appname)
199 {
200         Path dir = get_bin_dir(argv0);
201
202         if(dir[-1]=="bin" || dir[-1]=="sbin")
203                 return dir/".."/"share"/appname;
204         else
205                 return dir;
206 }
207
208 Path get_sys_lib_dir(const string &argv0, const string &appname)
209 {
210         Path dir = get_bin_dir(argv0);
211
212         if(dir[-1]=="bin" || dir[-1]=="sbin")
213                 return dir/".."/"lib"/appname;
214         else
215                 return dir;
216 }
217
218 void chdir(const Path &path)
219 {
220         if(::chdir(path.str().c_str())==-1)
221                 throw system_error("chdir");
222 }
223
224 } // namespace FS
225 } // namespace Msp