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