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