Throw an exception in list_files if the directory can't be opened
[libs/core.git] / source / fs / dir.cpp
1 #include <cstdlib>
2 #include <unistd.h>
3 #include <dirent.h>
4 #include <msp/core/systemerror.h>
5 #include <msp/strings/regex.h>
6 #include <msp/strings/utils.h>
7 #include "dir.h"
8 #include "path.h"
9 #include "stat.h"
10 #include "utils.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace FS {
16
17 namespace
18 {
19
20 /** Helper function to determine the location of the program's executable.
21 Caches the last result to cut down filesystem access with repeated calls. */
22 const Path &get_bin_dir(const string &argv0)
23 {
24         static string last_argv0;
25         static Path bin_dir;
26
27         if(!(argv0==last_argv0))
28         {
29                 Path exe;
30                 if(argv0.find('/')==string::npos)
31                 {
32                         const char *path = getenv("PATH");
33                         vector<string> dirs = split(path, ':');
34                         for(vector<string>::const_iterator i=dirs.begin(); i!=dirs.end(); ++i)
35                                 if(exists(Path(*i)/argv0))
36                                 {
37                                         exe = realpath(Path(*i)/argv0);
38                                         break;
39                                 }
40                 }
41                 else
42                         exe = realpath(argv0);
43
44                 last_argv0 = argv0;
45                 bin_dir = dirname(exe);
46         }
47
48         return bin_dir;
49 }
50
51 }
52
53
54 not_a_directory::not_a_directory(const Path &p):
55         runtime_error(p.str())
56 { }
57
58
59 void mkpath(const Path &path, int mode)
60 {
61         Path p;
62         for(Path::Iterator i=path.begin(); i!=path.end(); ++i)
63         {
64                 p /= *i;
65 #ifdef WIN32
66                 if(p.size()==1 && p.is_absolute())
67                         continue;
68 #endif
69                 if(FS::Stat st = stat(p))
70                 {
71                         if(!st.is_directory())
72                                 throw not_a_directory(p);
73                         continue;
74                 }
75                 else
76                         mkdir(p, mode);
77         }
78 }
79
80 void rmpath(const Path &path)
81 {
82         list<string> files = list_files(path);
83         for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
84         {
85                 Path p = path / *i;
86                 if(is_dir(p))
87                         rmpath(p);
88                 else
89                         unlink(p);
90         }
91
92         rmdir(path);
93 }
94
95 list<string> list_files(const Path &path)
96 {
97         return list_filtered(path, string());
98 }
99
100 list<string> list_filtered(const Path &path, const string &filter)
101 {
102         Regex r_filter(filter);
103
104         list<string> result;
105         DIR *dir = opendir(path.str().c_str());
106         if(!dir)
107                 throw system_error("opendir");
108
109         while(dirent *de = readdir(dir))
110         {
111                 const char *fn = de->d_name;
112                 if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) 
113                         continue;
114                 if(r_filter.match(fn))
115                         result.push_back(fn);
116         }
117         closedir(dir);
118
119         return result;
120 }
121
122 Path getcwd()
123 {
124         char buf[1024];
125         return ::getcwd(buf, sizeof(buf));
126 }
127
128 Path get_sys_conf_dir(const string &argv0)
129 {
130         Path dir = get_bin_dir(argv0);
131
132         if(dir[-1]=="bin" || dir[-1]=="sbin")
133         {
134                 dir /= "..";
135                 if(dir[-1]=="usr")
136                         dir /= "..";
137                 return dir/"etc";
138         }
139         else
140                 return dir;
141 }
142
143 Path get_sys_data_dir(const string &argv0, const string &appname)
144 {
145         Path dir = get_bin_dir(argv0);
146
147         if(dir[-1]=="bin" || dir[-1]=="sbin")
148                 return dir/".."/"share"/appname;
149         else if(dir[-1]=="MacOS")
150                 return dir/".."/"Resources";
151         else
152                 return dir;
153 }
154
155 Path get_sys_lib_dir(const string &argv0, const string &appname)
156 {
157         Path dir = get_bin_dir(argv0);
158
159         if(dir[-1]=="bin" || dir[-1]=="sbin")
160                 return dir/".."/"lib"/appname;
161         else
162                 return dir;
163 }
164
165 void chdir(const Path &path)
166 {
167         if(::chdir(path.str().c_str())==-1)
168                 throw system_error("chdir");
169 }
170
171 } // namespace FS
172 } // namespace Msp