A library for working with phylogenetic and population genetic data.
v0.32.0
fs.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2022 Lucas Czech
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 3 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, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
33 #include <cassert>
34 #include <cctype>
35 #include <cerrno>
36 #include <dirent.h>
37 #include <fcntl.h>
38 #include <fstream>
39 #include <functional>
40 #include <limits.h>
41 #include <regex>
42 #include <set>
43 #include <sstream>
44 #include <stdexcept>
45 #include <stdlib.h>
46 #include <streambuf>
47 #include <utility>
48 
49 #if defined(_WIN32) || defined(_WIN64)
50  #include <io.h>
51  #include <fileapi.h>
52  #define access _access_s
53 #else
54  #include <unistd.h>
55  #include <sys/stat.h>
56 #endif
57 
66 
67 namespace genesis {
68 namespace utils {
69 
70 // =================================================================================================
71 // File Access
72 // =================================================================================================
73 
74 bool path_exists( std::string const& path )
75 {
76  #if defined(_WIN32) || defined(_WIN64)
77  // https://stackoverflow.com/a/25450408/4184258
78  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesa
79  auto const lpath = "\\\\?\\" + path;
80  return GetFileAttributesW( lpath.c_str() ) != INVALID_FILE_ATTRIBUTES;
81 
82  // https://stackoverflow.com/a/28132053/4184258 - limited to 260 chars
83  // return 0 != PathFileExists( path.c_str() );
84  #else
85  struct stat info;
86  return ( stat( path.c_str(), &info ) == 0 );
87  #endif
88 }
89 
90 bool is_file( std::string const& path )
91 {
92  return file_is_readable( path );
93 }
94 
95 bool file_exists( std::string const& filename )
96 {
97  // There are plenty of discussions on stackoverflow on how to do this correctly, e.g.,
98  // https://stackoverflow.com/a/12774387, but there seems to be no good / portable / reliable
99  // way to check that a file exists, but is for example not a directory.
100  // None of them really worked for me here, meaning that they also returned true for directories.
101 
102  // Thus, we re-use the file readablility check here, which also makes sure that the file
103  // can actually be opened (i.e., we have access rights, and we don't have too many file handles
104  // open simultaneously).
105  return file_is_readable( filename );
106 }
107 
108 bool file_is_readable( std::string const& filename )
109 {
110  std::string err_str;
111  return file_is_readable( filename, err_str );
112 }
113 
114 bool file_is_readable( std::string const& filename, std::string& err_str )
115 {
116  errno = 0;
117  std::ifstream instream( filename );
118  instream.seekg( 0, std::ios::end );
119  auto const result = instream.good() && ! is_dir( filename );
120  if( ! result ) {
121  err_str = std::string( strerror( errno ));
122  }
123  return result;
124 }
125 
126 std::string file_read( std::string const& filename, bool detect_compression )
127 {
128  // Create string beforehand to enable copy elision.
129  std::string str;
130  errno = 0;
131 
132  if( detect_compression && is_gzip_compressed_file( filename )) {
133 
134  // Open decompressing stream
135  GzipIFStream instream( filename );
136  if( !instream.good() ) {
137  throw std::runtime_error(
138  "Cannot read from file '" + filename + "': " + std::string( strerror( errno ))
139  );
140  }
141 
142  // Can't assess file size for compressed files, so just read.
143  str.assign(
144  std::istreambuf_iterator<char>(instream),
145  std::istreambuf_iterator<char>()
146  );
147  return str;
148  }
149 
150  // Open normal stream
151  std::ifstream instream(filename);
152  if( !instream.good() ) {
153  throw std::runtime_error(
154  "Cannot read from file '" + filename + "': " + std::string( strerror( errno ))
155  );
156  }
157 
158  // Get file size, so that we do not waste time and space for string concats.
159  instream.seekg(0, std::ios::end);
160  str.reserve(instream.tellg());
161  instream.seekg(0, std::ios::beg);
162 
163  // Read content
164  str.assign(
165  std::istreambuf_iterator<char>(instream),
166  std::istreambuf_iterator<char>()
167  );
168  return str;
169 }
170 
171 std::vector<std::string> file_read_lines( std::string const& filename, bool detect_compression )
172 {
173  std::vector<std::string> result;
174  utils::InputStream it( from_file( filename, detect_compression ));
175  while( it ) {
176  result.push_back( read_to_end_of_line( it ));
177  assert( !it || *it == '\n' );
178  ++it;
179  }
180  return result;
181 }
182 
183 void file_write( std::string const& content, std::string const& filename, bool create_dirs )
184 {
185  std::ofstream ofs;
186  utils::file_output_stream( filename, ofs, std::ios_base::out, create_dirs );
187  ofs << content;
188 }
189 
190 void file_append( std::string const& content, std::string const& filename, bool create_dirs )
191 {
192  // TODO maybe merge with file_write and use mode as optional parameter.
193 
194  if( create_dirs ) {
195  auto const path = file_path( filename );
196  dir_create( path );
197  }
198 
199  errno = 0;
200  std::ofstream out_stream( filename, std::ofstream::app );
201  if( out_stream.fail() ) {
202  throw std::runtime_error(
203  "Cannot append to file '" + filename + "': " + std::string( strerror( errno ))
204  );
205  }
206  out_stream << content;
207 }
208 
209 // =================================================================================================
210 // Directory Access
211 // =================================================================================================
212 
213 bool is_dir( std::string const& path )
214 {
215  return dir_exists( path );
216 }
217 
218 bool dir_exists( std::string const& dir )
219 {
220  struct stat info;
221  if (stat (dir.c_str(), &info) != 0) {
222  return false;
223  }
224  return static_cast<bool>( info.st_mode & S_IFDIR );
225 
226  // alternative implementation:
227  // DIR* dp = opendir(dir);
228  // if (dp) {
229  // closedir(dir);
230  // return dp;
231  // } else {
232  // return false;
233  // }
234 }
235 
236 void dir_create( std::string const& path, bool with_parents )
237 {
238  mode_t mode = 0775;
239  struct stat info;
240 
241  // Checks. If it is the current dir, do nothing.
242  auto const path_no_bs = utils::trim_right( path, "/\\");
243  if( path_no_bs.empty() ) {
244  return;
245  }
246 
247  // Run recursively.
248  if( with_parents ) {
249  if( ! dir_exists( path_no_bs ) && path_no_bs.size() > 0 ) {
250  dir_create( file_path( path_no_bs ), true );
251  }
252  }
253 
254  // Try to make dir.
255  errno = 0;
256  if( stat( path.c_str(), &info ) != 0 ) {
257  if( mkdir( path.c_str(), mode ) != 0 && errno != EEXIST ) {
258  throw std::runtime_error(
259  "Cannot create directory '" + path + "': " + std::string( strerror( errno ))
260  );
261  }
262  } else if( !S_ISDIR( info.st_mode )) {
263  throw std::runtime_error(
264  "Cannot create directory '" + path + "': Path already exists, but is not a directory"
265  );
266  }
267 }
268 
269 std::string dir_normalize_path( std::string const& path )
270 {
271  return utils::trim_right( path, "/") + "/";
272 }
273 
274 static std::vector<std::string> dir_list_contents_(
275  std::string const& dir,
276  bool full_path,
277  std::string const& regex,
278  std::function<bool( std::string const& )> condition
279 ) {
280  std::vector<std::string> list;
281  auto const dir_path = dir_normalize_path( dir );
282  std::regex pattern( regex );
283 
284  DIR* dp;
285  struct dirent* dirp;
286 
287  errno = 0;
288  if(( dp = opendir( dir.c_str() )) == nullptr ) {
289  throw std::runtime_error(
290  "Cannot open directory '" + dir + "': " + std::string( strerror( errno ))
291  );
292  }
293  while(( dirp = readdir(dp) ) != nullptr ) {
294  auto const fn = std::string( dirp->d_name );
295 
296  if (fn == "." || fn == "..") {
297  continue;
298  }
299  if( ! regex.empty() && ! regex_match( fn, pattern ) ) {
300  continue;
301  }
302  if( ! condition( dir_path + fn ) ) {
303  continue;
304  }
305 
306  if( full_path ) {
307  list.push_back( dir_path + fn );
308  } else {
309  list.push_back( fn );
310  }
311  }
312  closedir(dp);
313 
314  //~ std::sort(list.begin(), list.end());
315  return list;
316 }
317 
318 std::vector<std::string> dir_list_contents(
319  std::string const& dir,
320  bool full_path,
321  std::string const& regex
322 ) {
323  return dir_list_contents_(
324  dir, full_path, regex,
325  []( std::string const& ){ return true; }
326  );
327 }
328 
329 std::vector<std::string> dir_list_files(
330  std::string const& dir,
331  bool full_path,
332  std::string const& regex
333 ) {
334  // We use file_is_readable() here, which kinda limits us to readable files only;
335  // but that should be fine, we are probably not interested in others anyway.
336  // And that was also the implicity behaviour for when we used the now defunct is_file()
337  // here instead... just not documented.
338  auto is_file_ = []( std::string const& file_name ){
339  return file_is_readable( file_name );
340  };
341 
342  return dir_list_contents_(
343  dir, full_path, regex, is_file_
344  );
345 }
346 
347 std::vector<std::string> dir_list_directories(
348  std::string const& dir,
349  bool full_path,
350  std::string const& regex
351 ) {
352  return dir_list_contents_(
353  dir, full_path, regex, is_dir
354  );
355 }
356 
357 // =================================================================================================
358 // Path Information
359 // =================================================================================================
360 
361 static std::string current_path_getcwd_()
362 {
363  // Wrapper for the standard getcwd() function to bring it into C++
364  // Adapted from https://stackoverflow.com/a/2203853/4184258
365 
366  // The local buffer of size PATH_MAX is fine for Windows, but pretty much no other OS.
367  char temp[ PATH_MAX ];
368  errno = 0;
369  if( getcwd( temp, PATH_MAX ) != nullptr ) {
370  return dir_normalize_path( std::string( temp ));
371  }
372 
373  int error = errno;
374  switch( error ) {
375  case EACCES:
376  throw std::runtime_error( "Cannot read current directory: Access denied." );
377 
378  case ENOMEM:
379  throw std::runtime_error( "Cannot read current directory: Insufficient storage." );
380 
381  default: {
382  std::ostringstream str;
383  str << "Cannot read current directory: " << std::string( strerror( errno ));
384  throw std::runtime_error( str.str() );
385  }
386  }
387 
388  // Make compilers happy.
389  return "";
390 }
391 
392 static std::string current_path_unix_()
393 {
394  // Code adapted from http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
395  // Does not work on Windows systems.
396 
397  std::string path;
398  typedef std::pair<dev_t, ino_t> file_id;
399  errno = 0;
400 
401  // Keep track of start directory, so can jump back to it later
402  int start_fd = open(".", O_RDONLY);
403  if( start_fd == -1 ) {
404  throw std::runtime_error(
405  "Cannot read current directory: " + std::string( strerror( errno ))
406  );
407  }
408 
409  struct stat sb;
410  if( fstat(start_fd, &sb) ) {
411  throw std::runtime_error(
412  "Cannot read current directory: " + std::string( strerror( errno ))
413  );
414  }
415 
416  // Get info for root directory, so we can determine when we hit it
417  file_id current_id(sb.st_dev, sb.st_ino);
418  if( stat( "/", &sb )) {
419  throw std::runtime_error(
420  "Cannot read current directory: " + std::string( strerror( errno ))
421  );
422  }
423 
424  std::vector<std::string> path_components;
425  file_id root_id(sb.st_dev, sb.st_ino);
426 
427  // If they're equal, we've obtained enough info to build the path
428  while( current_id != root_id ) {
429  bool pushed = false;
430 
431  // Keep recursing towards root each iteration
432  if( ! chdir("..") ) {
433  DIR *dir = opendir(".");
434  if( dir ) {
435  dirent *entry;
436 
437  // We loop through each entry trying to find where we came from
438  while(( entry = readdir(dir) )) {
439  if(
440  strcmp(entry->d_name, ".")
441  && strcmp(entry->d_name, "..")
442  && !lstat(entry->d_name, &sb)
443  ) {
444  file_id child_id(sb.st_dev, sb.st_ino);
445 
446  // We found where we came from, add its name to the list
447  if( child_id == current_id ) {
448  path_components.push_back(entry->d_name);
449  pushed = true;
450  break;
451  }
452  }
453  }
454  closedir(dir);
455 
456  // If we have a reason to continue, we update the current dir id
457  if( pushed && !stat(".", &sb) ) {
458  current_id = file_id(sb.st_dev, sb.st_ino);
459  }
460  // Else, Uh oh, can't read information at this level
461  }
462  }
463 
464  // If we didn't obtain any info this pass, no reason to continue
465  if( ! pushed ) {
466  break;
467  }
468  }
469 
470  // Unless they're equal, we failed above
471  if( current_id == root_id ) {
472 
473  // Built the path, will always end with a slash
474  path = "/";
475  for( auto i = path_components.rbegin(); i != path_components.rend(); ++i ) {
476  path += *i + "/";
477  }
478  } else {
479  if( errno ) {
480  throw std::runtime_error(
481  "Cannot read current directory: " + std::string( strerror( errno ))
482  );
483  } else {
484  throw std::runtime_error( "Cannot read current directory." );
485  }
486  }
487 
488  if( fchdir(start_fd) ) {
489  if( errno ) {
490  throw std::runtime_error(
491  "Cannot change directory: " + std::string( strerror( errno ))
492  );
493  } else {
494  throw std::runtime_error( "Cannot change directory." );
495  }
496  }
497  close(start_fd);
498 
499  return path;
500 }
501 
502 std::string current_path()
503 {
504  // We use our own implementations here, which depend on the OS.
505  #if defined( _WIN32 ) || defined( _WIN64 )
506  return current_path_getcwd_();
507  #else
508  return current_path_unix_();
509  #endif
510 
511  // We use conditional compilation. Silence compiler warnings about unused functions.
512  (void) current_path_getcwd_;
513  (void) current_path_unix_;
514 }
515 
516 // The following set of helper functions is adapted from
517 // http://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
518 // See there for details.
519 
520 static void relative_dir_base_split_( std::string const& path, std::string& dir, std::string& base )
521 {
522  // Find the last slash
523  auto slash_pos = path.rfind("/");
524 
525  // If there is a slash
526  if (slash_pos != std::string::npos) {
527  slash_pos++;
528 
529  // Directory is before slash
530  dir = path.substr(0, slash_pos);
531 
532  // And obviously, the file is after
533  base = path.substr(slash_pos);
534  } else {
535  // Otherwise, there is no directory present
536  dir.clear();
537  base = path;
538  }
539 }
540 
541 static std::string chdir_getcwd_( std::string const& dir )
542 {
543  // Open current directory so we can save a handle to it
544  errno = 0;
545  int start_fd = open(".", O_RDONLY);
546  if (start_fd == -1) {
547  throw std::runtime_error(
548  "Cannot open current directory: " + std::string( strerror( errno ))
549  );
550  }
551 
552  // Change to directory
553  if( chdir(dir.c_str()) ) {
554  throw std::runtime_error(
555  "Cannot change directory: " + std::string( strerror( errno ))
556  );
557  }
558 
559  // And get its path
560  auto const path = current_path();
561 
562  // And change back of course
563  if( fchdir(start_fd) ) {
564  throw std::runtime_error(
565  "Cannot change directory: " + std::string( strerror( errno ))
566  );
567  }
568  close(start_fd);
569 
570  return path;
571 }
572 
573 static std::string realpath_file_(const std::string& path )
574 {
575  std::string dir;
576  std::string base;
577  relative_dir_base_split_(path, dir, base);
578 
579  // If there is a directory, get the realpath() for it, otherwise the current directory
580  auto resolved_path = dir.size() ? chdir_getcwd_(dir) : current_path();
581  return resolved_path + base;
582 }
583 
584 static bool readlink_internal_(const std::string& path, std::string& buffer, ssize_t length)
585 {
586  bool success = false;
587  if (length > 0) {
588  // Room for Null
589  char *buf = new(std::nothrow) char[length+1];
590 
591  if (buf) {
592  // Give room for failure
593  ssize_t amount = ::readlink(path.c_str(), buf, length+1);
594 
595  // If > length, it was modified mid check
596  if ((amount > 0) && (amount <= length)) {
597  buf[amount] = 0;
598  buffer = buf;
599  success = true;
600  }
601  delete[] buf;
602  }
603  }
604 
605  return success;
606 }
607 
608 static void build_path_base_swap_(std::string &path, const std::string& newbase)
609 {
610  std::string dir;
611  std::string base;
612  relative_dir_base_split_(path, dir, base);
613 
614  if (dir.size()) {
615  path = dir + newbase;
616  } else {
617  path = newbase;
618  }
619 }
620 
621 static bool symlink_resolve_( const std::string& start, std::string& end )
622 {
623  typedef std::pair<dev_t, ino_t> file_id;
624 
625  bool success = false;
626  if (start.size()) {
627  // Need a modifyable copy
628  std::string path = start;
629  struct stat sb;
630  std::set<file_id> seen_links;
631 
632  bool resolved_link;
633  // The symlink resolve loop
634  do {
635  resolved_link = false;
636  if (!lstat(path.c_str(), &sb)) {
637  file_id current_id(sb.st_dev, sb.st_ino);
638  // Not a link we've seen
639  if (seen_links.find(current_id) == seen_links.end()) {
640  // Add to our set
641  seen_links.insert(current_id);
642 
643  // Another link
644  if (S_ISLNK(sb.st_mode)) {
645  std::string newpath;
646  if (readlink_internal_(path, newpath, sb.st_size)) {
647  // Absolute
648  if (newpath[0] == '/') {
649  path = newpath;
650  } else {
651  // We need to calculate the relative path in relation to the current
652  build_path_base_swap_(path, newpath);
653  }
654  resolved_link = true;
655  // Else, Link can't be read, time to quit
656  }
657  } else {
658  // Yay, it's not a link! got to the last part finally!
659  end = realpath_file_( path );
660  success = true;
661  }
662  // Else, Nice try, someone linked a link back into a previous link
663  // during the scan to try to trick us into an infinite loop.
664  }
665  // Else, Dangling link, can't resolve
666  }
667  } while (resolved_link);
668  }
669  return(success);
670 }
671 
672 static std::string real_path_unix_( std::string const& path, bool resolve_link )
673 {
674  // Custom implementation, using the above helper functions. Based on
675  // http://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html
676  // See there for details.
677 
678  // Edge case.
679  if( ! path.size()) {
680  return path;
681  }
682 
683  errno = 0;
684  struct stat sb;
685  if( stat(path.c_str(), &sb) ) {
686  throw std::runtime_error(
687  "Cannot read path '" + path + "': " + std::string( strerror( errno ))
688  );
689  }
690 
691  if( S_ISDIR( sb.st_mode )) {
692  return chdir_getcwd_( path );
693  } else {
694  if( resolve_link ) {
695  std::string result;
696  if( ! symlink_resolve_( path, result ) ) {
697  throw std::runtime_error(
698  "Cannot determine real path to '" + path + "': " +
699  std::string( strerror( errno ))
700  );
701  }
702  return result;
703  } else {
704  return realpath_file_( path );
705  }
706  }
707 
708  // Make compilers happy.
709  return path;
710 }
711 
712 static std::string real_path_realpath_( std::string const& path, bool resolve_link )
713 {
714  // Wrapper for the standard realpath() function to bring it into C++
715  // Works on Windows.
716 
717  // Not supported in this function...
718  (void) resolve_link;
719 
720  errno = 0;
721  char resolved_path[PATH_MAX];
722  auto ptr = realpath( path.c_str(), resolved_path );
723  if( errno != 0 ) {
724  throw std::runtime_error(
725  "Cannot determine real path to '" + path + "': " + std::string( strerror( errno ))
726  );
727  }
728  return std::string( ptr );
729 }
730 
731 std::string real_path( std::string const& path, bool resolve_link )
732 {
733  // We use our own implementations here, which depend on the OS.
734  #if defined( _WIN32 ) || defined( _WIN64 )
735  return real_path_realpath_( path, resolve_link );
736  #else
737  return real_path_unix_( path, resolve_link );
738  #endif
739 
740  // We use conditional compilation. Silence compiler warnings about unused functions.
741  (void) real_path_realpath_;
742  (void) real_path_unix_;
743 }
744 
745 // =================================================================================================
746 // File Information
747 // =================================================================================================
748 
749 std::unordered_map<std::string, std::string> file_info( std::string const& filename )
750 {
751  std::string basename = file_basename(filename);
752  std::unordered_map<std::string, std::string> res;
753 
754  res["path"] = file_path(filename);
755  res["basename"] = basename;
756  res["filename"] = file_filename(basename);
757  res["extension"] = file_extension(basename);
758 
759  return res;
760 }
761 
762 size_t file_size( std::string const& filename )
763 {
764  errno = 0;
765  auto result = filename;
766  std::ifstream in(result, std::ifstream::ate | std::ifstream::binary);
767  if( !in.good() ) {
768  throw std::runtime_error(
769  "Cannot determine file size of file '" + filename + "': " +
770  std::string( strerror( errno ))
771  );
772  }
773  return static_cast<size_t>(in.tellg());
774 }
775 
776 std::string file_path( std::string const& filename )
777 {
778  auto result = filename;
779  const size_t idx = result.find_last_of("\\/");
780  if( idx == std::string::npos ) {
781  return "";
782  }
783 
784  result.erase(idx);
785  return result;
786 }
787 
788 std::string file_basename( std::string const& filename )
789 {
790  auto result = filename;
791  const size_t idx = result.find_last_of("\\/");
792  if( idx != std::string::npos ) {
793  result.erase(0, idx + 1);
794  }
795  return result;
796 }
797 
798 std::string file_basename(
799  std::string const& filename,
800  std::vector<std::string> const& remove_extensions
801 ) {
802  auto bn = file_basename( filename );
803  for( auto const& ext : remove_extensions ) {
804  if( utils::ends_with( bn, ext ) ) {
805  bn.erase( bn.size() - ext.size() );
806  }
807  }
808  return bn;
809 }
810 
811 std::string file_filename( std::string const& filename )
812 {
813  auto result = filename;
814  const size_t idx = result.rfind('.');
815  if( idx != 0 && idx != std::string::npos ) {
816  result.erase(idx);
817  }
818  return result;
819 }
820 
821 std::string file_extension( std::string const& filename )
822 {
823  auto result = filename;
824  const size_t idx = result.rfind('.');
825  if( idx != 0 && idx != std::string::npos ) {
826  result.erase(0, idx + 1);
827  }
828  return result;
829 }
830 
831 // =================================================================================================
832 // File Names
833 // =================================================================================================
834 
835 bool is_valid_filename( std::string const& filename )
836 {
837  // No empty filenames.
838  if( trim( filename ) == "" ) {
839  return false;
840  }
841 
842  // No space at beginning or end.
843  if( starts_with( filename, " " ) || ends_with( filename, " " )) {
844  return false;
845  }
846 
847  // Check forbidden chars of Win and Unix systems.
848  if( filename.find_first_of( "<>:\"\\/|?*" ) != std::string::npos ) {
849  return false;
850  }
851 
852  // Check for non-printable chars.
853  // They might be allowed on most systems, but better be conservative here.
854  for( auto c : filename ) {
855  if( ! isprint( c ) ) {
856  return false;
857  }
858  }
859 
860  return true;
861 }
862 
863 std::string sanitize_filename( std::string const& filename )
864 {
865  // Prepare result.
866  std::string result = "";
867  result.reserve( filename.size() );
868 
869  // Copy all printable chars, drop the others.
870  for( auto c : filename ) {
871  if( isprint( c ) ) {
872  result += c;
873  }
874  }
875 
876  // No spaces around the name, and replace all forbidden chars by underscores.
877  result = trim( result );
878  result = replace_all_chars( result, "<>:\"\\/|?*", '_' );
879 
880  // Everything was deleted. WTF?!
881  if( result == "" ) {
882  throw std::runtime_error( "Invalid filename '" + filename + "'" );
883  }
884 
885  return result;
886 }
887 
888 } // namespace utils
889 } // namespace genesis
genesis::utils::current_path
std::string current_path()
Return the current (working) directory, simiar to getcwd().
Definition: fs.cpp:502
genesis::utils::readlink_internal_
static bool readlink_internal_(const std::string &path, std::string &buffer, ssize_t length)
Definition: fs.cpp:584
genesis::utils::InputStream
Stream interface for reading data from an InputSource, that keeps track of line and column counters.
Definition: input_stream.hpp:88
algorithm.hpp
Provides some valuable algorithms that are not part of the C++ 11 STL.
genesis::utils::chdir_getcwd_
static std::string chdir_getcwd_(std::string const &dir)
Definition: fs.cpp:541
genesis::utils::trim_right
std::string trim_right(std::string const &s, std::string const &delimiters)
Return a copy of the input string, with left trimmed white spaces (or any other delimiters).
Definition: string.cpp:803
genesis::utils::symlink_resolve_
static bool symlink_resolve_(const std::string &start, std::string &end)
Definition: fs.cpp:621
fs.hpp
Provides functions for accessing the file system.
genesis::utils::dir_create
void dir_create(std::string const &path, bool with_parents)
Create a directory.
Definition: fs.cpp:236
genesis::utils::file_size
size_t file_size(std::string const &filename)
Return the size of a file.
Definition: fs.cpp:762
genesis::utils::from_file
std::shared_ptr< BaseInputSource > from_file(std::string const &file_name, bool detect_compression=true)
Obtain an input source for reading from a file.
Definition: input_source.hpp:68
genesis::utils::dir_list_directories
std::vector< std::string > dir_list_directories(std::string const &dir, bool full_path, std::string const &regex)
Get a list of directories in a directory.
Definition: fs.cpp:347
genesis::utils::relative_dir_base_split_
static void relative_dir_base_split_(std::string const &path, std::string &dir, std::string &base)
Definition: fs.cpp:520
genesis::utils::file_info
std::unordered_map< std::string, std::string > file_info(std::string const &filename)
Return information about a file.
Definition: fs.cpp:749
output_stream.hpp
genesis::utils::current_path_getcwd_
static std::string current_path_getcwd_()
Definition: fs.cpp:361
genesis::tree::length
double length(Tree const &tree)
Get the length of the tree, i.e., the sum of all branch lengths.
Definition: tree/common_tree/functions.cpp:160
genesis::utils::trim
std::string trim(std::string const &s, std::string const &delimiters)
Return a copy of the input string, with trimmed white spaces (or any other delimiters).
Definition: string.cpp:827
gzip.hpp
genesis::utils::GzipIFStream
Input file stream that offers on-the-fly gzip-decompression if needed.
Definition: gzip_stream.hpp:276
genesis::utils::file_path
std::string file_path(std::string const &filename)
Return the path leading to a file.
Definition: fs.cpp:776
genesis::utils::replace_all_chars
std::string replace_all_chars(std::string const &text, std::string const &search_chars, char replace)
Replace all occurrences of the search_chars in text by the replace char.
Definition: string.cpp:768
genesis::utils::current_path_unix_
static std::string current_path_unix_()
Definition: fs.cpp:392
genesis::utils::starts_with
bool starts_with(std::string const &text, std::string const &prefix)
Return whether a string starts with another string, i.e., check for a prefix.
Definition: string.cpp:136
genesis::utils::ends_with
bool ends_with(std::string const &text, std::string const &suffix)
Return whether a string ends with another string, i.e., check for a suffix.
Definition: string.cpp:230
genesis::utils::dir_exists
bool dir_exists(std::string const &dir)
Return true iff the directory exists.
Definition: fs.cpp:218
input_source.hpp
genesis::utils::sanitize_filename
std::string sanitize_filename(std::string const &filename)
Remove or replace all invalid parts of a filename.
Definition: fs.cpp:863
genesis::utils::file_filename
std::string file_filename(std::string const &filename)
Remove extension if present.
Definition: fs.cpp:811
genesis::utils::build_path_base_swap_
static void build_path_base_swap_(std::string &path, const std::string &newbase)
Definition: fs.cpp:608
genesis::tree::path
utils::Range< IteratorPath< true > > path(ElementType const &start, ElementType const &finish)
Definition: path.hpp:337
string.hpp
Provides some commonly used string utility functions.
genesis::utils::file_read
std::string file_read(std::string const &filename, bool detect_compression)
Return the contents of a file as a string.
Definition: fs.cpp:126
input_stream.hpp
genesis::utils::real_path
std::string real_path(std::string const &path, bool resolve_link)
Return the real path of a file or directory, similar to realpath().
Definition: fs.cpp:731
genesis::utils::realpath_file_
static std::string realpath_file_(const std::string &path)
Definition: fs.cpp:573
gzip_stream.hpp
genesis::utils::is_gzip_compressed_file
bool is_gzip_compressed_file(std::string const &file_name)
Return whether a given file is gzip-compressed.
Definition: gzip.cpp:58
genesis::utils::is_dir
bool is_dir(std::string const &path)
Return true iff the provided path is a directory.
Definition: fs.cpp:213
genesis::utils::real_path_unix_
static std::string real_path_unix_(std::string const &path, bool resolve_link)
Definition: fs.cpp:672
genesis::utils::file_read_lines
std::vector< std::string > file_read_lines(std::string const &filename, bool detect_compression)
Return the contents of a file as a vector of strings, one entry for each line.
Definition: fs.cpp:171
genesis::utils::file_output_stream
void file_output_stream(std::string const &file_name, std::ofstream &out_stream, std::ios_base::openmode mode=std::ios_base::out, bool create_dirs=true)
Helper function to obtain an output stream to a file.
Definition: output_stream.hpp:72
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::path_exists
bool path_exists(std::string const &path)
Return whether a path exists, i.e., is a file or directory.
Definition: fs.cpp:74
genesis::utils::file_is_readable
bool file_is_readable(std::string const &filename)
Return whether a file is readable.
Definition: fs.cpp:108
genesis::utils::real_path_realpath_
static std::string real_path_realpath_(std::string const &path, bool resolve_link)
Definition: fs.cpp:712
genesis::utils::is_file
bool is_file(std::string const &path)
Return true iff the provided path is a (readable) file.
Definition: fs.cpp:90
genesis::utils::file_exists
bool file_exists(std::string const &filename)
Return true iff the file exists (and is in fact a file, and not, e.g., a directory).
Definition: fs.cpp:95
genesis::utils::dir_list_contents
std::vector< std::string > dir_list_contents(std::string const &dir, bool full_path, std::string const &regex)
Get a list of files and directories in a directory.
Definition: fs.cpp:318
genesis::utils::dir_list_contents_
static std::vector< std::string > dir_list_contents_(std::string const &dir, bool full_path, std::string const &regex, std::function< bool(std::string const &)> condition)
Definition: fs.cpp:274
scanner.hpp
genesis::utils::file_basename
std::string file_basename(std::string const &filename)
Remove directory name from file name if present.
Definition: fs.cpp:788
genesis::utils::is_valid_filename
bool is_valid_filename(std::string const &filename)
Check whether a file name is valid.
Definition: fs.cpp:835
genesis::utils::dir_normalize_path
std::string dir_normalize_path(std::string const &path)
Normalize a dir name, i.e., make sure that the given path ends with exaclty one slash.
Definition: fs.cpp:269
genesis::utils::file_write
void file_write(std::string const &content, std::string const &filename, bool create_dirs)
Write the content of a string to a file.
Definition: fs.cpp:183
genesis::utils::file_append
void file_append(std::string const &content, std::string const &filename, bool create_dirs)
Append the content of a string to a file.
Definition: fs.cpp:190
genesis::utils::file_extension
std::string file_extension(std::string const &filename)
Return the extension name of a file.
Definition: fs.cpp:821
genesis::utils::read_to_end_of_line
std::string read_to_end_of_line(InputStream &source)
Lexing function that reads until the end of the line (i.e., to the new line char),...
Definition: scanner.hpp:134
genesis::utils::dir_list_files
std::vector< std::string > dir_list_files(std::string const &dir, bool full_path, std::string const &regex)
Get a list of files in a directory.
Definition: fs.cpp:329