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