ardour
file_utils.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007-2014 Tim Mayberry
3  Copyright (C) 1998-2014 Paul Davis
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19 */
20 
21 #include <algorithm>
22 #include <vector>
23 
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 
27 #ifdef COMPILER_MINGW
28 #include <io.h> // For W_OK
29 #endif
30 
31 #include <glibmm/fileutils.h>
32 #include <glibmm/miscutils.h>
33 #include <glibmm/pattern.h>
34 
35 #include <errno.h>
36 #include <string.h> /* strerror */
37 
38 /* open() */
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 
43 /* close(), read(), write() */
44 #ifdef COMPILER_MSVC
45 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
46 #include <ardourext/misc.h>
47 #else
48 #include <unistd.h>
49 #include <regex.h>
50 #endif
51 
52 #include "pbd/compose.h"
53 #include "pbd/file_utils.h"
54 #include "pbd/debug.h"
55 #include "pbd/error.h"
56 #include "pbd/pathexpand.h"
58 #include "pbd/stl_delete.h"
59 
60 #include "i18n.h"
61 
62 using namespace std;
63 
64 namespace PBD {
65 
66 void
67 run_functor_for_paths (vector<string>& result,
68  const Searchpath& paths,
69  bool (*functor)(const string &, void *),
70  void *arg,
71  bool pass_files_only,
72  bool pass_fullpath, bool return_fullpath,
73  bool recurse)
74 {
75  for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
76  string expanded_path = path_expand (*i);
78  string_compose("Find files in expanded path: %1\n", expanded_path));
79 
80  if (!Glib::file_test (expanded_path, Glib::FILE_TEST_IS_DIR)) continue;
81 
82  try
83  {
84  Glib::Dir dir(expanded_path);
85 
86  for (Glib::DirIterator di = dir.begin(); di != dir.end(); di++) {
87 
88  string fullpath = Glib::build_filename (expanded_path, *di);
89  string basename = *di;
90 
91  bool is_dir = Glib::file_test (fullpath, Glib::FILE_TEST_IS_DIR);
92 
93  if (is_dir && recurse) {
95  string_compose("Descending into directory: %1\n",
96  fullpath));
97  run_functor_for_paths (result, fullpath, functor, arg, pass_files_only,
98  pass_fullpath, return_fullpath, recurse);
99  }
100 
101  if (is_dir && pass_files_only) {
102  continue;
103  }
104 
105  string functor_str;
106 
107  if (pass_fullpath) {
108  functor_str = fullpath;
109  } else {
110  functor_str = basename;
111  }
112 
114  string_compose("Run Functor using string: %1\n", functor_str));
115 
116  if (!functor(functor_str, arg)) {
117  continue;
118  }
119 
121  string_compose("Found file %1 matching functor\n", functor_str));
122 
123  if (return_fullpath) {
124  result.push_back(fullpath);
125  } else {
126  result.push_back(basename);
127  }
128  }
129  }
130  catch (Glib::FileError& err)
131  {
132  warning << err.what() << endmsg;
133  }
134  }
135 }
136 
137 static
138 bool accept_all_files (string const &, void *)
139 {
140  return true;
141 }
142 
143 void
144 get_paths (vector<string>& result,
145  const Searchpath& paths,
146  bool files_only,
147  bool recurse)
148 {
149  run_functor_for_paths (result, paths, accept_all_files, 0,
150  files_only, true, true, recurse);
151 }
152 
153 void
154 get_files (vector<string>& result, const Searchpath& paths)
155 {
156  return get_paths (result, paths, true, false);
157 }
158 
159 static
160 bool
161 pattern_filter (const string& str, void *arg)
162 {
163  Glib::PatternSpec* pattern = (Glib::PatternSpec*)arg;
164  return pattern->match(str);
165 }
166 
167 void
168 find_files_matching_pattern (vector<string>& result,
169  const Searchpath& paths,
170  const Glib::PatternSpec& pattern)
171 {
172  run_functor_for_paths (result, paths, pattern_filter,
173  const_cast<Glib::PatternSpec*>(&pattern),
174  true, false, true, false);
175 }
176 
177 void
178 find_files_matching_pattern (vector<string>& result,
179  const Searchpath& paths,
180  const string& pattern)
181 {
182  Glib::PatternSpec tmp(pattern);
183  find_files_matching_pattern (result, paths, tmp);
184 }
185 
186 bool
187 find_file (const Searchpath& search_path,
188  const string& filename,
189  std::string& result)
190 {
191  vector<std::string> tmp;
192 
193  find_files_matching_pattern (tmp, search_path, filename);
194 
195  if (tmp.size() == 0) {
197  string_compose("No file matching %1 found in Path: %2\n",
198  filename, search_path.to_string()));
199  return false;
200  }
201 
202  if (tmp.size() != 1) {
204  string_compose("Found more that one file matching %1 in Path: %2\n",
205  filename, search_path.to_string()));
206  }
207 
208  result = tmp.front();
209 
211  string_compose("Found file %1 in Path: %2\n",
212  filename, search_path.to_string()));
213 
214  return true;
215 }
216 
217 static
218 bool
219 regexp_filter (const string& str, void *arg)
220 {
221  regex_t* pattern = (regex_t*)arg;
222  return regexec (pattern, str.c_str(), 0, 0, 0) == 0;
223 }
224 
225 void
226 find_files_matching_regex (vector<string>& result,
227  const Searchpath& paths,
228  const std::string& regexp,
229  bool recurse)
230 {
231  int err;
232  char msg[256];
233  regex_t compiled_pattern;
234 
235  if ((err = regcomp (&compiled_pattern, regexp.c_str(),
236  REG_EXTENDED|REG_NOSUB))) {
237 
238  regerror (err, &compiled_pattern,
239  msg, sizeof (msg));
240 
241  error << "Cannot compile soundfile regexp for use ("
242  << msg
243  << ")"
244  << endmsg;
245 
246  return;
247  }
248 
250  string_compose("Matching files using regexp: %1\n", regexp));
251 
252  find_files_matching_filter (result, paths,
253  regexp_filter, &compiled_pattern,
254  true, true, recurse);
255 
256  regfree (&compiled_pattern);
257 }
258 
259 void
260 find_paths_matching_filter (vector<string>& result,
261  const Searchpath& paths,
262  bool (*filter)(const string &, void *),
263  void *arg,
264  bool pass_fullpath, bool return_fullpath,
265  bool recurse)
266 {
267  run_functor_for_paths (result, paths, filter, arg, false, pass_fullpath, return_fullpath, recurse);
268 }
269 
270 void
271 find_files_matching_filter (vector<string>& result,
272  const Searchpath& paths,
273  bool (*filter)(const string &, void *),
274  void *arg,
275  bool pass_fullpath, bool return_fullpath,
276  bool recurse)
277 {
278  run_functor_for_paths (result, paths, filter, arg, true, pass_fullpath, return_fullpath, recurse);
279 }
280 
281 bool
282 copy_file(const std::string & from_path, const std::string & to_path)
283 {
284  if (!Glib::file_test (from_path, Glib::FILE_TEST_EXISTS)) return false;
285 
286  PBD::ScopedFileDescriptor fd_from (g_open (from_path.c_str(), O_RDONLY, 0444));
287  PBD::ScopedFileDescriptor fd_to (g_open (to_path.c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666));
288 
289  char buf[4096]; // BUFSIZ ??
290  ssize_t nread;
291 
292  if ((fd_from < 0) || (fd_to < 0)) {
293  error << string_compose (_("Unable to Open files %1 to %2 for Copying(%3)"),
294  from_path, to_path, g_strerror(errno))
295  << endmsg;
296  return false;
297  }
298 
299  while (nread = ::read(fd_from, buf, sizeof(buf)), nread > 0) {
300  char *out_ptr = buf;
301  do {
302  ssize_t nwritten = ::write(fd_to, out_ptr, nread);
303  if (nwritten >= 0) {
304  nread -= nwritten;
305  out_ptr += nwritten;
306  } else if (errno != EINTR) {
307  error << string_compose (_("Unable to Copy files %1 to %2(%3)"),
308  from_path, to_path, g_strerror(errno))
309  << endmsg;
310  return false;
311  }
312  } while (nread > 0);
313  }
314 
315  return true;
316 }
317 
318 void
319 copy_files(const std::string & from_path, const std::string & to_dir)
320 {
321  vector<string> files;
322  find_files_matching_filter (files, from_path, accept_all_files, 0, true, false);
323 
324  for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
325  std::string from = Glib::build_filename (from_path, *i);
326  std::string to = Glib::build_filename (to_dir, *i);
327  copy_file (from, to);
328  }
329 }
330 
331 void
332 copy_recurse(const std::string & from_path, const std::string & to_dir)
333 {
334  vector<string> files;
335  find_files_matching_filter (files, from_path, accept_all_files, 0, false, true, true);
336 
337  const size_t prefix_len = from_path.size();
338  for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
339  std::string from = *i;
340  std::string to = Glib::build_filename (to_dir, (*i).substr(prefix_len));
341  g_mkdir_with_parents (Glib::path_get_dirname (to).c_str(), 0755);
342  copy_file (from, to);
343  }
344 }
345 
346 std::string
347 get_absolute_path (const std::string & p)
348 {
349  if (Glib::path_is_absolute(p)) return p;
350  return Glib::build_filename (Glib::get_current_dir(), p);
351 }
352 
353 std::string
354 get_suffix (const std::string & p)
355 {
356  string::size_type period = p.find_last_of ('.');
357  if (period == string::npos || period == p.length() - 1) {
358  return string();
359  }
360  return p.substr (period+1);
361 }
362 
363 bool
364 equivalent_paths (const std::string& a, const std::string& b)
365 {
366  GStatBuf bA;
367  int const rA = g_stat (a.c_str(), &bA);
368  GStatBuf bB;
369  int const rB = g_stat (b.c_str(), &bB);
370 
371  return (rA == 0 && rB == 0 && bA.st_dev == bB.st_dev && bA.st_ino == bB.st_ino);
372 }
373 
374 bool
375 path_is_within (std::string const & haystack, std::string needle)
376 {
377  while (1) {
378  if (equivalent_paths (haystack, needle)) {
379  return true;
380  }
381 
382  needle = Glib::path_get_dirname (needle);
383  if (needle == "." || needle == "/" || Glib::path_skip_root(needle).empty()) {
384  break;
385  }
386  }
387 
388  return false;
389 }
390 
391 bool
392 exists_and_writable (const std::string & p)
393 {
394  /* writable() really reflects the whole folder, but if for any
395  reason the session state file can't be written to, still
396  make us unwritable.
397  */
398 
399  GStatBuf statbuf;
400 
401  if (g_stat (p.c_str(), &statbuf) != 0) {
402  /* doesn't exist - not writable */
403  return false;
404  } else {
405  if (!(statbuf.st_mode & S_IWUSR)) {
406  /* exists and is not writable */
407  return false;
408  }
409  /* filesystem may be mounted read-only, so even though file
410  * permissions permit access, the mount status does not.
411  * access(2) seems like the best test for this.
412  */
413  if (g_access (p.c_str(), W_OK) != 0) {
414  return false;
415  }
416  }
417 
418  return true;
419 }
420 
421 int
422 remove_directory_internal (const string& dir, size_t* size, vector<string>* paths,
423  bool just_remove_files)
424 {
425  vector<string> tmp_paths;
426  GStatBuf statbuf;
427  int ret = 0;
428 
429  get_paths (tmp_paths, dir, just_remove_files, true);
430 
431  for (vector<string>::const_iterator i = tmp_paths.begin();
432  i != tmp_paths.end(); ++i) {
433 
434  if (g_stat (i->c_str(), &statbuf)) {
435  continue;
436  }
437 
438  if (::g_remove (i->c_str())) {
439  error << string_compose (_("cannot remove path %1 (%2)"), *i, strerror (errno))
440  << endmsg;
441  ret = 1;
442  }
443 
444  if (paths) {
445  paths->push_back (Glib::path_get_basename(*i));
446  }
447 
448  if (size) {
449  *size += statbuf.st_size;
450  }
451 
452  }
453 
454  return ret;
455 }
456 
457 int
458 clear_directory (const string& dir, size_t* size, vector<string>* paths)
459 {
460  return remove_directory_internal (dir, size, paths, true);
461 }
462 
463 // rm -rf <dir> -- used to remove saved plugin state
464 void
465 remove_directory (const std::string& dir)
466 {
467  remove_directory_internal (dir, 0, 0, false);
468 }
469 
470 string
471 tmp_writable_directory (const char* domain, const string& prefix)
472 {
473  std::string tmp_dir = Glib::build_filename (g_get_tmp_dir(), domain);
474  std::string dir_name;
475  std::string new_test_dir;
476  do {
477  ostringstream oss;
478  oss << prefix;
479  oss << g_random_int ();
480  dir_name = oss.str();
481  new_test_dir = Glib::build_filename (tmp_dir, dir_name);
482  if (Glib::file_test (new_test_dir, Glib::FILE_TEST_EXISTS)) continue;
483  } while (g_mkdir_with_parents (new_test_dir.c_str(), 0755) != 0);
484  return new_test_dir;
485 }
486 
487 int
488 toggle_file_existence (string const & path)
489 {
490  if (Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
491  return g_unlink (path.c_str());
492  }
493 
494  PBD::ScopedFileDescriptor fd = g_open (path.c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666);
495  return !((int) fd >= 0);
496 }
497 
498 } // namespace PBD
LIBPBD_API std::string path_expand(std::string path)
void find_paths_matching_filter(vector< string > &result, const Searchpath &paths, bool(*filter)(const string &, void *), void *arg, bool pass_fullpath, bool return_fullpath, bool recurse)
Definition: file_utils.cc:260
void remove_directory(const std::string &dir)
Definition: file_utils.cc:465
bool path_is_within(std::string const &haystack, std::string needle)
Definition: file_utils.cc:375
void find_files_matching_regex(vector< string > &result, const Searchpath &paths, const std::string &regexp, bool recurse)
Definition: file_utils.cc:226
std::string get_suffix(const std::string &p)
Definition: file_utils.cc:354
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
bool find_file(const Searchpath &search_path, const string &filename, std::string &result)
Definition: file_utils.cc:187
int toggle_file_existence(string const &path)
Definition: file_utils.cc:488
int clear_directory(const string &dir, size_t *size, vector< string > *paths)
Definition: file_utils.cc:458
static bool regexp_filter(const string &str, void *arg)
Definition: file_utils.cc:219
#define _(Text)
Definition: i18n.h:11
void copy_recurse(const std::string &from_path, const std::string &to_dir)
Definition: file_utils.cc:332
void run_functor_for_paths(vector< string > &result, const Searchpath &paths, bool(*functor)(const string &, void *), void *arg, bool pass_files_only, bool pass_fullpath, bool return_fullpath, bool recurse)
Definition: file_utils.cc:67
bool copy_file(const std::string &from_path, const std::string &to_path)
Definition: file_utils.cc:282
void find_files_matching_pattern(vector< string > &result, const Searchpath &paths, const string &pattern)
Definition: file_utils.cc:178
string tmp_writable_directory(const char *domain, const string &prefix)
Definition: file_utils.cc:471
bool exists_and_writable(const std::string &p)
Definition: file_utils.cc:392
std::string get_absolute_path(const std::string &p)
Definition: file_utils.cc:347
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
void copy_files(const std::string &from_path, const std::string &to_dir)
Definition: file_utils.cc:319
void get_paths(vector< string > &result, const Searchpath &paths, bool files_only, bool recurse)
Definition: file_utils.cc:144
static bool accept_all_files(string const &, void *)
LIBPBD_TEMPLATE_MEMBER_API const std::string to_string() const
Definition: search_path.cc:99
Definition: debug.h:30
static bool pattern_filter(const string &str, void *arg)
Definition: file_utils.cc:161
void get_files(vector< string > &result, const Searchpath &paths)
Definition: file_utils.cc:154
int remove_directory_internal(const string &dir, size_t *size, vector< string > *paths, bool just_remove_files)
Definition: file_utils.cc:422
bool equivalent_paths(const std::string &a, const std::string &b)
Definition: file_utils.cc:364
uint64_t FileUtils
Definition: debug.cc:52
void find_files_matching_filter(vector< string > &result, const Searchpath &paths, bool(*filter)(const string &, void *), void *arg, bool pass_fullpath, bool return_fullpath, bool recurse)
Definition: file_utils.cc:271
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208