311a3948efd132d29bd97cba67bde7ee787a7706
[ardour.git] / libs / pbd / file_utils.cc
1 /*
2     Copyright (C) 2007 Tim Mayberry 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <vector>
22
23 #include <glib.h>
24 #include <glib/gstdio.h>
25
26 #ifdef COMPILER_MINGW
27 #include <io.h> // For W_OK
28 #endif
29
30 #include <glibmm/fileutils.h>
31 #include <glibmm/miscutils.h>
32 #include <glibmm/pattern.h>
33
34 #include <giomm/file.h>
35
36 #include "pbd/compose.h"
37 #include "pbd/file_utils.h"
38 #include "pbd/error.h"
39 #include "pbd/pathscanner.h"
40 #include "pbd/stl_delete.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45
46 namespace PBD {
47
48 void
49 get_files_in_directory (const std::string& directory_path, vector<string>& result)
50 {
51         if (!Glib::file_test (directory_path, Glib::FILE_TEST_IS_DIR)) return;
52
53         try
54         {
55                 Glib::Dir dir(directory_path);
56                 std::copy(dir.begin(), dir.end(), std::back_inserter(result));
57         }
58         catch (Glib::FileError& err)
59         {
60                 warning << err.what() << endmsg;
61         }
62 }
63
64 void
65 find_matching_files_in_directory (const std::string& directory,
66                                   const Glib::PatternSpec& pattern,
67                                   vector<std::string>& result)
68 {
69         vector<string> tmp_files;
70
71         get_files_in_directory (directory, tmp_files);
72         result.reserve(tmp_files.size());
73
74         for (vector<string>::iterator file_iter = tmp_files.begin();
75                         file_iter != tmp_files.end();
76                         ++file_iter)
77         {
78                 if (!pattern.match(*file_iter)) continue;
79
80                 std::string full_path(directory);
81                 full_path = Glib::build_filename (full_path, *file_iter);
82
83                 result.push_back(full_path);
84         }
85 }
86
87 void
88 find_matching_files_in_directories (const vector<std::string>& paths,
89                                     const Glib::PatternSpec& pattern,
90                                     vector<std::string>& result)
91 {
92         for (vector<std::string>::const_iterator path_iter = paths.begin();
93                         path_iter != paths.end();
94                         ++path_iter)
95         {
96                 find_matching_files_in_directory (*path_iter, pattern, result);
97         }               
98 }
99
100 void
101 find_matching_files_in_search_path (const SearchPath& search_path,
102                                     const Glib::PatternSpec& pattern,
103                                     vector<std::string>& result)
104 {
105         find_matching_files_in_directories (search_path, pattern, result);    
106 }
107
108 bool
109 find_file_in_search_path(const SearchPath& search_path,
110                          const string& filename,
111                          std::string& result)
112 {
113         vector<std::string> tmp;
114         Glib::PatternSpec tmp_pattern(filename);
115
116         find_matching_files_in_search_path (search_path, tmp_pattern, tmp);
117
118         if (tmp.size() == 0)
119         {
120                 return false;
121         }
122
123 #if 0
124         if (tmp.size() != 1)
125         {
126                 info << string_compose
127                         (
128                          "Found more than one file matching %1 in search path %2",
129                          filename,
130                          search_path ()
131                         )
132                         << endmsg;
133         }
134 #endif
135
136         result = tmp.front();
137
138         return true;
139 }
140
141 bool
142 copy_file(const std::string & from_path, const std::string & to_path)
143 {
144         if (!Glib::file_test (from_path, Glib::FILE_TEST_EXISTS)) return false;
145
146         Glib::RefPtr<Gio::File> from_file = Gio::File::create_for_path(from_path);
147         Glib::RefPtr<Gio::File> to_file = Gio::File::create_for_path(to_path);
148
149         try
150         {
151                 from_file->copy (to_file, Gio::FILE_COPY_OVERWRITE);
152         }
153         catch(const Glib::Exception& ex)
154         {
155                 error << string_compose (_("Unable to Copy file %1 to %2 (%3)"),
156                                 from_path, to_path, ex.what())
157                         << endmsg;
158                 return false;
159         }
160         return true;
161 }
162
163 static
164 bool accept_all_files (string const &, void *)
165 {
166         return true;
167 }
168
169 void
170 copy_files(const std::string & from_path, const std::string & to_dir)
171 {
172         PathScanner scanner;
173         vector<string*>* files = scanner (from_path, accept_all_files, 0, true, false);
174
175         if (files) {
176                 for (vector<string*>::iterator i = files->begin(); i != files->end(); ++i) {
177                         std::string from = Glib::build_filename (from_path, **i);
178                         std::string to = Glib::build_filename (to_dir, **i);
179                         copy_file (from, to);
180                 }
181                 vector_delete (files);
182         }
183 }
184
185 std::string
186 get_absolute_path (const std::string & p)
187 {
188         Glib::RefPtr<Gio::File> f = Gio::File::create_for_path (p);
189         return f->get_path ();
190 }
191
192 bool
193 equivalent_paths (const std::string& a, const std::string& b)
194 {
195         struct stat bA;
196         int const rA = g_stat (a.c_str(), &bA);
197         struct stat bB;
198         int const rB = g_stat (b.c_str(), &bB);
199
200         return (rA == 0 && rB == 0 && bA.st_dev == bB.st_dev && bA.st_ino == bB.st_ino);
201 }
202
203 bool
204 path_is_within (std::string const & haystack, std::string needle)
205 {
206         while (1) {
207                 if (equivalent_paths (haystack, needle)) {
208                         return true;
209                 }
210
211                 needle = Glib::path_get_dirname (needle);
212                 if (needle == "." || needle == "/") {
213                         break;
214                 }
215         }
216
217         return false;
218 }
219
220 bool
221 exists_and_writable (const std::string & p)
222 {
223         /* writable() really reflects the whole folder, but if for any
224            reason the session state file can't be written to, still
225            make us unwritable.
226         */
227
228         struct stat statbuf;
229
230         if (g_stat (p.c_str(), &statbuf) != 0) {
231                 /* doesn't exist - not writable */
232                 return false;
233         } else {
234                 if (!(statbuf.st_mode & S_IWUSR)) {
235                         /* exists and is not writable */
236                         return false;
237                 }
238                 /* filesystem may be mounted read-only, so even though file
239                  * permissions permit access, the mount status does not.
240                  * access(2) seems like the best test for this.
241                  */
242                 if (g_access (p.c_str(), W_OK) != 0) {
243                         return false;
244                 }
245         }
246
247         return true;
248 }
249
250 } // namespace PBD