Crombie Tools
FileSystem.h
Go to the documentation of this file.
1 #ifndef CROMBIE_FILESYSTEM_H
2 #define CROMBIE_FILESYSTEM_H
3 
4 #include <cstdlib>
5 #include <cstring>
6 #include <vector>
7 #include <string>
8 #include <dirent.h>
9 #include <sys/stat.h>
10 #include <libgen.h>
11 
12 #include "crombie/Types.h"
13 #include "crombie/Misc.h"
14 
15 namespace crombie {
16  namespace FileSystem {
17 
18  std::string basename(const std::string& name) {
19  auto output = Misc::split(name, "/").back();
20  Debug::Debug(__PRETTY_FUNCTION__, name, output);
21  return output;
22  }
23 
24  std::string dirname(const std::string& name) {
25  std::string modname = name;
26  while (modname.back() == '/')
27  modname.pop_back();
28 
29  auto output = modname.substr(0, modname.size() - basename(modname).size());
30  Debug::Debug(__PRETTY_FUNCTION__, name, output);
31  return output;
32  }
33 
34  namespace {
35  // Is the path for xrd
36  bool is_xrd(const std::string& path) {
37  return path.find("root://") == 0;
38  }
39 
40  // Do the xrdfs call
41  Types::strings xrd_ls(const std::string& path) {
42  // Cache for listed directories to prevent too many calls
43  static Types::map<Types::strings> xrd_dir_contents;
44 
45  auto iter = xrd_dir_contents.find(path);
46  if (iter != xrd_dir_contents.end())
47  return iter->second;
48 
49  // Door will be between two "//" sequences when user is sane
50  auto parts = Misc::split(path, "//");
51  if (parts.size() != 3)
52  throw std::runtime_error{std::string("xrd path (") + path + ") doesn't seem to have a good pattern"};
53 
54  // Set the cache to the split long listing of xrdfs and return it
55  return xrd_dir_contents[path] =
56  Misc::split(Misc::shell(std::string("xrdfs root://") + parts[1] + "/ ls -l /" + parts[2] + " | sort -u"));
57  }
58 
59  // Get the xrd_ls call and return the file portion of the line
60  Types::strings xrd_list(const std::string& path) {
61  return Misc::comprehension<std::string>
62  (xrd_ls(path),
63  [] (auto& line) {
64  return basename(Misc::tokenize(line)[4]);
65  });
66  }
67 
68  // Get the listing of the directory, and then extract the size
69  unsigned long xrd_get_size(const std::string& name) {
70  // There is some tricky business about the "//" in ls outputs, so just look for the file
71  std::string filename = basename(name);
72  for (auto& line : xrd_ls(dirname(name))) {
73  if (line.find(filename) != std::string::npos)
74  return std::stoul(Misc::tokenize(line)[3]);
75  }
76  return 0;
77  }
78 
79  // Hacky way to see if path is there
80  bool xrd_exists(const std::string& path) {
81  return xrd_get_size(path);
82  }
83 
84  }
85 
86  /// Check if path exists (either directory or file)
87  bool exists(const std::string& path) {
88  if (is_xrd(path))
89  return xrd_exists(path);
90  struct stat buffer;
91  return stat(path.data(), &buffer) == 0;
92  }
93 
94  /// Get the size of a file
95  unsigned long get_size(const std::string& name) {
96  if (is_xrd(name))
97  return xrd_get_size(name);
98  struct stat file_stat;
99  stat(name.data(), &file_stat);
100  return file_stat.st_size;
101  }
102 
103  /// Create directories, recursively if needed
104  void mkdirs(const std::string& path) {
105  char path_array[512];
106  // Stick a slash on the end to trick our character flipping
107  auto addslash = path;
108  if (addslash.back() != '/')
109  addslash += '/';
110 
111  strncpy(path_array, addslash.data(), sizeof(path_array) - 1);
112  auto num_chars = strlen(path_array);
113 
114  for (unsigned i_char = 1; i_char < num_chars; i_char++) {
115 
116  if (path_array[i_char] != '/')
117  continue;
118 
119  // Flip character to null
120  path_array[i_char] = '\0';
121  if (!exists(path_array)) {
122  std::cout << "Making: " << path_array << std::endl;
123  mkdir(path_array, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
124  }
125  // Flip back
126  path_array[i_char] = '/';
127  }
128  }
129 
130  /// The name of files inside of the directory
131  Types::strings list(std::string directory) {
132  if (is_xrd(directory))
133  return xrd_list(directory);
134 
135  Types::strings output;
136 
137  auto* indir = opendir(directory.data());
138  while (auto* dir_ent = readdir(indir)) {
139  if (dir_ent->d_name[0] != '.')
140  output.emplace_back(dir_ent->d_name);
141  }
142  closedir(indir);
143 
144  return output;
145  }
146 
147 
148  /**
149  If this would overwrite a directory that exists, ask for confirmation.
150  This returns whether or not the user confirms the file to be removed.
151  */
152  bool confirm_overwrite(const std::string& path) {
153  if (exists(path)) {
154  std::string response;
155  std::cout << path << " already exists. Want to overwrite? (y/N)" << std::endl;
156  std::getline(std::cin, response);
157  return response == "y";
158  }
159  return true;
160  }
161  }
162 }
163 
164 
165 #endif