Skip to content

Commit 3c28f5a

Browse files
committed
First cut at a tool to set status
After hacking around failing to make std::regex solve the parse problem, resorted to an old-school search-and-replace algorithm. Combined with the previous list-issues filtering program, this should make it simple to change the state of issues en-masse while preparing work pre- and post-meetings.
1 parent e374153 commit 3c28f5a

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

src/set_status.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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

Comments
 (0)