|
| 1 | +// This program reads all the issues in the issues directory passed as the first command line argument. |
| 2 | +// If all documents are successfully parsed, it will generate the standard LWG Issues List documents |
| 3 | +// for an ISO SC22 WG21 mailing. |
| 4 | + |
| 5 | +// Based on code originally donated by Howard Hinnant |
| 6 | +// Since modified by Alisdair Meredith |
| 7 | + |
| 8 | +// Note that this program requires a reasonably conformant C++11 compiler, supporting at least: |
| 9 | +// auto |
| 10 | +// lambda expressions |
| 11 | +// brace-initialization |
| 12 | +// range-based for loops |
| 13 | +// raw string literals |
| 14 | +// constexpr |
| 15 | +// new function syntax (late specified return type) |
| 16 | +// noexcept |
| 17 | +// new type-alias syntax (using my_name = type) |
| 18 | + |
| 19 | +// Likewise, the following C++11 library facilities are used: |
| 20 | +// to_string |
| 21 | +// consistent overloading of 'char const *' and 'std::string' |
| 22 | + |
| 23 | +// Following the planned removal of (deprecated) bool post-increment operator, we now |
| 24 | +// require the following C++14 library facilities: |
| 25 | +// exchange |
| 26 | + |
| 27 | +// The following C++14 features are being considered for future cleanup/refactoring: |
| 28 | +// polymorphic lambdas |
| 29 | +// string UDLs |
| 30 | + |
| 31 | +// The following TS features are also desirable |
| 32 | +// filesystem |
| 33 | +// string_view |
| 34 | + |
| 35 | +// Its coding style assumes a standard library optimized with move-semantics |
| 36 | +// The only known compiler to support all of this today is the experimental gcc trunk (4.6) |
| 37 | + |
| 38 | +// TODO |
| 39 | +// . Better handling of TR "sections", and grouping of issues in "Clause X" |
| 40 | +// . Sort the Revision comments in the same order as the 'Status' reports, rather than alphabetically |
| 41 | +// . Lots of tidy and cleanup after merging the revision-generating tool |
| 42 | +// . Refactor more common text |
| 43 | +// . Split 'format' function and usage to that the issues vector can pass by const-ref in the common cases |
| 44 | +// . Document the purpose amd contract on each function |
| 45 | +// Waiting on external fix for preserving file-dates |
| 46 | +// . sort-by-last-modified-date should offer some filter or separation to see only the issues modified since the last meeting |
| 47 | + |
| 48 | +// Missing standard facilities that we work around |
| 49 | +// . Date |
| 50 | +// . Filesystem navigation |
| 51 | + |
| 52 | +// Missing standard library facilities that would probably not change this program |
| 53 | +// . XML parser |
| 54 | + |
| 55 | +// standard headers |
| 56 | +#include <fstream> |
| 57 | +#include <functional> |
| 58 | +#include <iostream> |
| 59 | +#include <iterator> |
| 60 | +#include <memory> |
| 61 | +#include <sstream> |
| 62 | +#include <stdexcept> |
| 63 | +#include <string> |
| 64 | + |
| 65 | +// platform headers - requires a Posix compatible platform |
| 66 | +// The hope is to replace all of this with the filesystem TS |
| 67 | +#include <dirent.h> |
| 68 | +#include <unistd.h> |
| 69 | +#include <sys/stat.h> |
| 70 | +#include <sys/types.h> |
| 71 | + |
| 72 | +// solution specific headers |
| 73 | +#include "issues.h" |
| 74 | +#include "sections.h" |
| 75 | + |
| 76 | + |
| 77 | +#if 0 |
| 78 | +// Revisit this after AJM works out linker issues on his Mac |
| 79 | +#if 1 |
| 80 | +// workaround until <experimental/filesystem> is widely available: |
| 81 | +##include <boost/filesystem.hpp> |
| 82 | +namespace std { |
| 83 | +namespace experimental { |
| 84 | +namespace filesystem { |
| 85 | +using namespace boost::filesystem; |
| 86 | +} |
| 87 | +} |
| 88 | +} |
| 89 | +#else |
| 90 | +#include <experimental/filesystem> |
| 91 | +#endif |
| 92 | +#endif |
| 93 | + |
| 94 | +struct bad_issue_file : std::runtime_error { |
| 95 | + bad_issue_file(std::string const & filename, std::string const & error_message) |
| 96 | + : runtime_error{"Error parsing issue file " + filename + ": " + error_message} |
| 97 | + { |
| 98 | + } |
| 99 | +}; |
| 100 | + |
| 101 | + |
| 102 | +auto read_file_into_string(std::string const & filename) -> std::string { |
| 103 | + // read a text file completely into memory, and return its contents as |
| 104 | + // a 'string' for further manipulation. |
| 105 | + |
| 106 | + std::ifstream infile{filename.c_str()}; |
| 107 | + if (!infile.is_open()) { |
| 108 | + throw std::runtime_error{"Unable to open file " + filename}; |
| 109 | + } |
| 110 | + |
| 111 | + std::istreambuf_iterator<char> first{infile}, last{}; |
| 112 | + return std::string {first, last}; |
| 113 | +} |
| 114 | + |
| 115 | +// ============================================================================================================ |
| 116 | + |
| 117 | +void check_is_directory(std::string const & directory) { |
| 118 | + struct stat sb; |
| 119 | + if (stat(directory.c_str(), &sb) != 0 or !S_ISDIR(sb.st_mode)) { |
| 120 | + throw std::runtime_error(directory + " is not an existing directory"); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +int main(int argc, char const* argv[]) { |
| 125 | + try { |
| 126 | + bool trace_on{false}; // Will pick this up from the command line later |
| 127 | + |
| 128 | + if (argc != 3) { |
| 129 | + std::cerr << "Must specify exactly one issue, followed by its new status\n"; |
| 130 | + return -2; |
| 131 | + } |
| 132 | + |
| 133 | + int const issue_number = atoi(argv[1]); |
| 134 | + |
| 135 | + std::string const new_status{argv[2]}; |
| 136 | + |
| 137 | + std::string path; |
| 138 | + char cwd[1024]; |
| 139 | + if (getcwd(cwd, sizeof(cwd)) == 0) { |
| 140 | + std::cerr << "unable to getcwd\n"; |
| 141 | + return -3; |
| 142 | + } |
| 143 | + path = cwd; |
| 144 | + if (path.back() != '/') { path.push_back('/'); } |
| 145 | + |
| 146 | + check_is_directory(path); |
| 147 | + |
| 148 | + std::string issue_file = std::string{"issue"} + argv[1] + ".xml"; |
| 149 | + auto const filename = path + "xml/" + issue_file; |
| 150 | + |
| 151 | + auto issue_data = read_file_into_string(filename); |
| 152 | + |
| 153 | + // find 'status' tag and replace it |
| 154 | + auto k = issue_data.find("<issue num=\""); |
| 155 | + if (k == std::string::npos) { |
| 156 | + throw bad_issue_file{filename, "Unable to find issue number"}; |
| 157 | + } |
| 158 | + k += sizeof("<issue num=\"") - 1; |
| 159 | + auto l = issue_data.find('"', k); |
| 160 | + if (l == std::string::npos) { |
| 161 | + throw bad_issue_file{filename, "Corrupt issue number attribute"}; |
| 162 | + } |
| 163 | + if (std::stod(issue_data.substr(k, k-l)) != issue_number) { |
| 164 | + throw bad_issue_file{filename, "Issue number does not match filename"}; |
| 165 | + } |
| 166 | + |
| 167 | + k = issue_data.find("status=\""); |
| 168 | + if (k == std::string::npos) { |
| 169 | + throw bad_issue_file{filename, "Unable to find issue status"}; |
| 170 | + } |
| 171 | + k += sizeof("status=\"") - 1; |
| 172 | + l = issue_data.find('"', k); |
| 173 | + if (l == std::string::npos) { |
| 174 | + throw bad_issue_file{filename, "Corrupt status attribute"}; |
| 175 | + } |
| 176 | + issue_data.replace(k, l-k, new_status); |
| 177 | + |
| 178 | + std::ofstream out_file{filename}; |
| 179 | + if (!out_file.is_open()) { |
| 180 | + throw std::runtime_error{"Unable to re-open file " + filename}; |
| 181 | + } |
| 182 | + |
| 183 | + out_file << issue_data; |
| 184 | + } |
| 185 | + catch(std::exception const & ex) { |
| 186 | + std::cout << ex.what() << std::endl; |
| 187 | + return -1; |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | + |
0 commit comments