LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
PostCloseFileRenamer.cc
Go to the documentation of this file.
2 
4 #include "boost/date_time/posix_time/posix_time.hpp"
5 #include "boost/filesystem.hpp"
6 #include "boost/regex.hpp"
8 
9 #include <iomanip>
10 #include <sstream>
11 #include <string>
12 
13 namespace bfs = boost::filesystem;
14 
16  : stats_{stats}
17 {}
18 
19 // For sanity. To avoid unintentionally introducing std overloads, we
20 // do not make a using declaration for boost::regex.
21 using namespace boost::regex_constants;
22 using boost::regex_match;
23 using boost::regex_search;
24 using boost::smatch;
25 
26 // Do not substitute for the "%(\\d+)?#" pattern here.
27 std::string
29  std::string const& filePattern) const
30 {
31  std::string result; // Empty
32  smatch match;
33  auto sb = cbegin(filePattern), si = sb, se = cend(filePattern);
34  while (
35  regex_search(si,
36  se,
37  match,
38  boost::regex{"%[lp]|%(\\d+)?([#rRsS])|%t([oc])|%if([bnedp])|%"
39  "ifs%([^%]*)%([^%]*)%([ig]*)%|%.",
40  ECMAScript})) {
41  // Precondition: that the regex creates the sub-matches we think it
42  // should.
43  assert(match.size() == 8);
44  // Subexpressions:
45  // 0: Entire matched expression.
46  // 1. Possible fill format digits for numeric substitution.
47  // 2. Numeric substitution specifier.
48  // 3. Timestamp substitution specifier.
49  // 4. Input file name substitution specifier.
50  // 5. Input file name regex match clause.
51  // 6. Input file name regex substitution clause.
52  // 7. Input file name regex flags.
53  //
54  // Note that we're not using named capture groups because that is
55  // not supported by C++11 to which we will eventually migrate when
56  // GCC supports the full C++11 regex functionality (reportedly
57  // 4.9.0).
58  //
59  // Add the bit before the next substitution pattern to the result.
60  result += match.prefix();
61  // Decide what we're going to add to the result instead of the
62  // substitution pattern:
63  switch (*(match[0].first + 1)) {
64  case 'l':
65  result += stats_.moduleLabel();
66  break;
67  case 'p':
68  result += stats_.processName();
69  break;
70  case 'i':
71  result += subInputFileName_(match);
72  break;
73  case 't':
74  result += subTimestamp_(match);
75  break;
76  default:
77  if (match[2].matched) { // [#rRsS]
78  result += subFilledNumericNoIndex_(match);
79  } else {
81  << "Unrecognized substitution %" << *(match[0].first + 1)
82  << " in pattern " << filePattern
83  << " -- typo or misconstructed regex?\n";
84  }
85  break;
86  }
87  si = match[0].second; // Set position for next match start.
88  }
89  result.append(si, se); // Append unmatched text at end.
90  return result;
91 }
92 
93 std::string
94 art::PostCloseFileRenamer::subInputFileName_(boost::smatch const& match) const
95 {
96  std::string result;
97  // If the filename is empty, substitute "-". If it is merely the
98  // required substitution that is empty, substitute "".
99  if (!stats_.lastOpenedInputFile().empty()) {
100  bfs::path const ifp{stats_.lastOpenedInputFile()};
101  if (match[4].matched) { // %if[bdenp]
102  switch (*(match[4].first)) {
103  case 'b': {
104  // Base name without extension.
105  result = ifp.stem().native();
106  } break;
107  case 'd': {
108  // Fully resolved path without filename.
109  result = canonical(ifp.parent_path()).native();
110  } break;
111  case 'e': {
112  // Extension.
113  result = ifp.extension().native();
114  } break;
115  case 'n': {
116  // Base name with extension.
117  result = ifp.filename().native();
118  } break;
119  case 'p': {
120  // Fully-resolved path with filename.
121  result = (canonical(ifp.parent_path()) / ifp.filename()).native();
122  } break;
123  default: // INTERNAL_ERROR.
125  << "Internal error: subInputFileName_() did not recognize "
126  "substitution code %if"
127  << *(match[4].first) << ".\n";
128  break;
129  }
130  } else if (match[5].matched) { // Regex substitution.
131  // Decompose the regex;
132  syntax_option_type sflags{ECMAScript};
133  match_flag_type mflags{match_default};
134  bool global{false};
135  if (match[7].matched) { // Options.
136  for (auto c : match[7].str()) {
137  switch (c) {
138  case 'i':
139  sflags |= icase;
140  break;
141  case 'g':
142  global = true;
143  break;
144  default: // INTERNAL ERROR.
146  << "Internal error: subInputFileName_() did not recognize "
147  "regex flag '"
148  << c << "'.\n";
149  break;
150  }
151  }
152  }
153  if (!global) {
154  mflags |= format_first_only;
155  }
156  boost::regex const dsub{match[5].str(), sflags};
157  result = regex_replace(
158  stats_.lastOpenedInputFile(), dsub, match[6].str(), mflags);
159  } else { // INTERNAL ERROR.
161  << "Internal error: subInputFileName_() called for unknown reasons "
162  "with pattern "
163  << match[0].str() << ".\n";
164  }
165  } else {
166  result = "-";
167  }
168  return result;
169 }
170 
171 std::string
172 art::PostCloseFileRenamer::subTimestamp_(boost::smatch const& match) const
173 {
174  std::string result;
175  switch (*(match[3].first)) {
176  case 'o': // Open.
177  result = boost::posix_time::to_iso_string(stats_.outputFileOpenTime());
178  break;
179  case 'c': // Close.
180  result = boost::posix_time::to_iso_string(stats_.outputFileCloseTime());
181  break;
182  default: // INTERNAL ERROR.
184  << "Internal error: subTimestamp_() did not recognize substitution "
185  "code %t"
186  << *(match[3].first) << ".\n";
187  break;
188  }
189  return result;
190 }
191 
192 std::string
194  boost::smatch const& match) const
195 {
196  bool const did_match = match[1].matched;
197  std::string const format_string = match[1].str();
198  auto zero_fill = [did_match, &format_string](std::ostringstream& os,
199  auto const& num) {
200  if (did_match) {
201  os << std::setfill('0') << std::setw(std::stoul(format_string));
202  }
203  os << num;
204  };
205 
206  std::string result;
207  std::ostringstream num_str;
208 
209  switch (*(match[2].first)) {
210  case '#':
211  // In order to get the indexing correct, we cannot yet
212  // substitute the index. We must wait until the entire filename
213  // as been assembled, with all other substitutions evaluated.
214  // At this point, we need to restore the original pattern.
215  num_str << "%" << match[1].str() << "#";
216  break;
217  case 'r':
218  if (stats_.lowestSubRunID().runID().isValid()) {
219  zero_fill(num_str, stats_.lowestSubRunID().run());
220  }
221  break;
222  case 'R':
223  if (stats_.highestSubRunID().runID().isValid()) {
224  zero_fill(num_str, stats_.highestSubRunID().run());
225  }
226  break;
227  case 's':
228  if (stats_.lowestSubRunID().isValid()) {
229  zero_fill(num_str, stats_.lowestSubRunID().subRun());
230  }
231  break;
232  case 'S':
233  if (stats_.highestSubRunID().isValid()) {
234  zero_fill(num_str, stats_.highestSubRunID().subRun());
235  }
236  break;
237  default: // INTERNAL ERROR.
238  break;
239  }
240  result = num_str.str();
241  if (result.empty()) {
242  result = "-";
243  }
244  return result;
245 }
246 
247 std::string
249 {
250  std::string const finalPattern = applySubstitutionsNoIndex_(toPattern);
251  auto const components = detail::componentsFromPattern(finalPattern);
252 
253  // Get index for the processed pattern, incrementing if a file-close
254  // has been recorded.
255  auto& index = indexForProcessedPattern_[components];
256  if (stats_.fileCloseRecorded()) {
257  ++index;
258  }
259 
260  return components.fileNameWithIndex(index);
261 }
262 
263 std::string
265  std::string const& toPattern)
266 {
267  std::string const& newFile = applySubstitutions(toPattern);
268  boost::system::error_code ec;
269  bfs::rename(inPath, newFile, ec);
270  if (ec) {
271  // Fail (different filesystems? Try copy / delete instead).
272  // This attempt will throw on failure.
273  bfs::copy_file(inPath, newFile, bfs::copy_option::overwrite_if_exists);
274  bfs::remove(inPath);
275  }
276  return newFile;
277 }
std::string subFilledNumericNoIndex_(boost::smatch const &match) const
std::map< detail::FileNameComponents, std::size_t > indexForProcessedPattern_
std::string const & processName() const
SubRunID const & lowestSubRunID() const
std::string const & moduleLabel() const
bool isValid() const
Definition: SubRunID.h:96
RunID const & runID() const
Definition: SubRunID.h:78
std::string applySubstitutionsNoIndex_(std::string const &filePattern) const
RunNumber_t run() const
Definition: SubRunID.h:84
PostCloseFileRenamer(FileStatsCollector const &stats)
boost::posix_time::ptime outputFileOpenTime() const
FileStatsCollector const & stats_
SubRunID const & highestSubRunID() const
FileNameComponents componentsFromPattern(std::string const &pattern)
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
std::string const & lastOpenedInputFile() const
std::string applySubstitutions(std::string const &filePattern)
std::string maybeRenameFile(std::string const &inPath, std::string const &toPattern)
bool isValid() const
Definition: RunID.h:69
SubRunNumber_t subRun() const
Definition: SubRunID.h:90
std::string subTimestamp_(boost::smatch const &match) const
std::string subInputFileName_(boost::smatch const &match) const
boost::posix_time::ptime outputFileCloseTime() const