]> git.tdb.fi Git - libs/core.git/blob - source/fs/dir.cpp
Merge branch 'fs-master'
[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/except.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 void mkdir(const Path &path, int mode)
58 {
59         int err;
60 #ifdef WIN32
61         // The win32 version of this function doesn't take the mode argument.  Go figure.
62         (void)mode;
63         err = ::mkdir(path.str().c_str());
64 #else
65         err = ::mkdir(path.str().c_str(), mode);
66 #endif
67
68         if(err==-1)
69                 throw SystemError("mkdir failed", errno);
70 }
71
72 void mkpath(const Path &path, int mode)
73 {
74         Path p;
75         for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
76         {
77                 p /= *i;
78 #ifdef WIN32
79                 if(p.size()==1 && p.is_absolute())
80                         continue;
81 #endif
82                 struct stat st;
83                 int err = stat(p, st);
84                 if(err==0)
85                 {
86                         if(!S_ISDIR(st.st_mode))
87                                 throw Exception("A component exists and is not a directory");
88                         continue;
89                 }
90                 else if(errno!=ENOENT)
91                         throw SystemError("stat failed", errno);
92                 else
93                         mkdir(p, mode);
94         }
95 }
96
97 void rmdir(const Path &path)
98 {
99         if(::rmdir(path.str().c_str())==-1)
100                 throw SystemError("rmdir failed", errno);
101 }
102
103 void rmdirs(const Path &path)
104 {
105         list<string> files = list_files(path);
106         for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
107         {
108                 Path p = path / *i;
109                 struct stat st = stat(p.str().c_str());
110                 if(S_ISDIR(st.st_mode))
111                         rmdirs(p);
112                 else
113                         unlink(p);
114         }
115
116         rmdir(path);
117 }
118
119 list<string> list_files(const Path &path)
120 {
121         return list_filtered(path, string());
122 }
123
124 list<string> list_filtered(const Path &path, const string &filter)
125 {
126         Regex r_filter(filter);
127
128         list<string> result;
129         DIR *dir = opendir(path.str().c_str());
130         if(dir)
131         {
132                 while(dirent *de = readdir(dir))
133                 {
134                         const char *fn = de->d_name;
135                         if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) 
136                                 continue;
137                         if(r_filter.match(fn))
138                                 result.push_back(fn);
139                 }
140                 closedir(dir);
141         }
142
143         return result;
144 }
145
146 Path getcwd()
147 {
148         char buf[1024];
149         return ::getcwd(buf, sizeof(buf));
150 }
151
152 Path get_home_dir()
153 {
154 #ifdef WIN32
155         char home[MAX_PATH];
156         if(SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, home)==S_OK)
157                 return home;
158 #else
159         const char *home = getenv("HOME");
160         if(home)
161                 return home;
162 #endif
163         return ".";
164 }
165
166 Path get_user_data_dir(const string &appname)
167 {
168 #ifdef WIN32
169         char datadir[MAX_PATH];
170         if(SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0, 0, datadir)==S_OK)
171                 return Path(datadir)/appname;
172         return ".";
173 #else
174         return get_home_dir()/("."+appname);
175 #endif
176 }
177
178 Path get_sys_conf_dir(const string &argv0)
179 {
180         Path dir = get_bin_dir(argv0);
181
182         if(dir[-1]=="bin" || dir[-1]=="sbin")
183         {
184                 dir /= "..";
185                 if(dir[-1]=="usr")
186                         dir /= "..";
187                 return dir/"etc";
188         }
189         else
190                 return dir;
191 }
192
193 Path get_sys_data_dir(const string &argv0, const string &appname)
194 {
195         Path dir = get_bin_dir(argv0);
196
197         if(dir[-1]=="bin" || dir[-1]=="sbin")
198                 return dir/".."/"share"/appname;
199         else
200                 return dir;
201 }
202
203 Path get_sys_lib_dir(const string &argv0, const string &appname)
204 {
205         Path dir = get_bin_dir(argv0);
206
207         if(dir[-1]=="bin" || dir[-1]=="sbin")
208                 return dir/".."/"lib"/appname;
209         else
210                 return dir;
211 }
212
213 void chdir(const Path &path)
214 {
215         if(::chdir(path.str().c_str())==-1)
216                 throw SystemError("chdir failed", errno);
217 }
218
219 } // namespace FS
220 } // namespace Msp