ROSE 0.11.145.147
IntervalParser.h
1#ifndef ROSE_CommandLine_IntervalParser_H
2#define ROSE_CommandLine_IntervalParser_H
3
4#include <Rose/StringUtility/Escape.h>
5#include <Rose/StringUtility/NumberToString.h>
6
7#include <boost/lexical_cast.hpp>
8#include <boost/numeric/conversion/cast.hpp>
9#include <cstring>
10#include <ctype.h>
11#include <errno.h>
12#include <rose_strtoull.h>
13#include <Sawyer/CommandLine.h>
14#include <Sawyer/Interval.h>
15#include <Sawyer/IntervalSet.h>
16#include <string>
17
18namespace Rose {
19namespace CommandLine {
20
31template<class Interval>
33protected:
35
38
39public:
44
46 static Ptr instance() {
47 return Ptr(new IntervalParser);
48 }
49
54
56 static std::string docString(const std::string &interval = "interval", const std::string &value = "value") {
57 return ("The " + interval + " can be specified in a number of forms:"
58 " a single " + value + " represents a singleton " + interval + ";"
59 " a first and inclusive last " + value + " separated by a comma;"
60 " a begin and exclusive end " + value + " separated by a hyphen;"
61 " a begin " + value + " and count separated by a plus sign;"
62 " the word \"all\" represents the universal " + interval + ";"
63 " and the word \"empty\" or an empty string represents the empty " + interval + "."
64
65 " When the " + interval + " is specified as a range, the first " + value + " must be less than or equal to"
66 " the second value. The " + value + " can be specified in decimal, hexadecimal (leading \"0x\"),"
67 " octal (leading \"0\"), or binary (leading \"0b\"). The upper " + value + " can be the word \"max\" when"
68 " appearing after a comma.");
69 }
70
75 static Interval parse(const char *input, const char **rest) {
76 const char *s = input;
77 char *r = nullptr;
78 bool hadRangeError = false, isEmpty = false;
79 while (isspace(*s)) ++s;
80
81 if (!strcmp(s, "all")) {
82 *rest = s + 3;
83 return Interval::whole();
84 }
85
86 if (!strcmp(s, "empty")) {
87 *rest += 5;
88 return Interval();
89 }
90
91 // Minimum
92 errno = 0;
93 uint64_t least64 = rose_strtoull(s, &r, 0);
94 if (r == s)
95 throw std::runtime_error("unsigned integer expected for interval minimum");
96 if (ERANGE == errno)
97 hadRangeError = true;
98 typename Interval::Value least{};
99 try {
100 least = boost::numeric_cast<typename Interval::Value>(least64);
101 } catch (const boost::bad_numeric_cast&) {
102 hadRangeError = true;
103 }
104 s = r;
105
106 // Maximum, end, size, or nothing
107 typename Interval::Value greatest = least;
108 while (isspace(*s)) ++s;
109 if (',' == *s) { // ',' means a max value is specified
110 ++s;
111 if (0 == strncmp(s, "max", 3)) {
112 greatest = Interval::whole().greatest();
113 s += 3;
114 r = const_cast<char*>(s);
115 } else {
116 errno = 0;
117 uint64_t greatest64 = rose_strtoull(s, &r, 0);
118 if (r == s)
119 throw std::runtime_error("unsigned integer expected for interval maximum");
120 if (ERANGE == errno)
121 hadRangeError = true;
122 try {
123 greatest = boost::numeric_cast<typename Interval::Value>(greatest64);
124 } catch (const boost::bad_numeric_cast&) {
125 hadRangeError = true;
126 }
127 s = r;
128 }
129 } else if ('-' == *s) { // '-' means an exclusive end address is specified (think "-" 1)
130 ++s;
131 errno = 0;
132 uint64_t greatest64 = rose_strtoull(s, &r, 0);
133 if (r == s)
134 throw std::runtime_error("unsigned integer expected for interval end");
135 if (ERANGE == errno)
136 hadRangeError = true;
137 try {
138 greatest = boost::numeric_cast<typename Interval::Value>(greatest64);
139 } catch (const boost::bad_numeric_cast&) {
140 hadRangeError = true;
141 }
142 if (greatest == least)
143 isEmpty = true;
144 --greatest;
145 s = r;
146 } else if ('+' == *s) { // '+' means a size follows (zero is allowed)
147 ++s;
148 errno = 0;
149 uint64_t size64 = rose_strtoull(s, &r, 0);
150 if (r == s)
151 throw std::runtime_error("unsigned integer expected for interval size");
152 if (ERANGE == errno)
153 hadRangeError = true;
154 typename Interval::Value size{};
155 try {
156 size = boost::numeric_cast<typename Interval::Value>(size64);
157 } catch (const boost::bad_numeric_cast&) {
158 hadRangeError = true;
159 }
160 if (0 == size)
161 isEmpty = true;
162 greatest = least + size - 1;
163 s = r;
164 } else if (!*s) { // end-of-string means the interval is a singleton
165 /*void*/
166 }
167
168 // Successful parsing?
169 *rest = r;
170 std::string parsed(input, *rest - input);
171 if (hadRangeError)
172 throw std::range_error("overflow when parsing \"" + parsed + "\"");
173 if (greatest < least)
174 throw std::range_error("interval seems backward: \"" + parsed + "\"");
175
176 if (!isEmpty) {
177 return Interval::hull(least, greatest);
178 } else {
179 return Interval();
180 }
181 }
182
188 static Interval parse(const std::string &input) {
189 const char *s = input.c_str();
190 const char *rest = nullptr;
191 Interval retval = parse(s, &rest);
192 while (isspace(*rest)) ++rest;
193 if (*rest)
194 throw std::runtime_error("extra text after end of interval specification: \"" + StringUtility::cEscape(rest) + "\"");
195 return retval;
196 }
197
199 static std::string toString(const Interval &interval) {
200 if (interval.isEmpty()) {
201 return "empty";
202 } else if (interval == Interval::whole()) {
203 return "all";
204 } else if (interval.least() == interval.greatest()) {
205 if (interval.least() < 256) {
206 return boost::lexical_cast<std::string>(interval.least());
207 } else {
208 return StringUtility::addrToString(interval.least());
209 }
210 } else {
211 if (interval.greatest() < 256) {
212 return boost::lexical_cast<std::string>(interval.least()) + "," +
213 boost::lexical_cast<std::string>(interval.greatest());
214 } else {
215 return StringUtility::addrToString(interval.least()) + "," +
216 StringUtility::addrToString(interval.greatest());
217 }
218 }
219 }
220
221private:
222 virtual Sawyer::CommandLine::ParsedValue operator()(const char *input, const char **rest,
223 const Sawyer::CommandLine::Location &loc) override {
224 Interval val = parse(input, rest);
225 std::string parsed(input, *rest - input);
226 return Sawyer::CommandLine::ParsedValue(val, loc, parsed, valueSaver());
227 }
228};
229
230template<class Interval>
231typename IntervalParser<Interval>::Ptr intervalParser(Interval &storage) {
233}
234
235template<class Interval>
236typename IntervalParser<Interval>::Ptr intervalParser(std::vector<Interval> &storage) {
237 return IntervalParser<Interval>::instance(Sawyer::CommandLine::TypedSaver<std::vector<Interval>>::instance(storage));
238}
239
240template<class Interval>
242 return IntervalParser<Interval>
244}
245
246template<class Interval>
247typename IntervalParser<Interval>::Ptr intervalParser() {
249}
250
251} // namespace
252} // namespace
253
254#endif
static Ptr instance(const Sawyer::CommandLine::ValueSaver::Ptr &valueSaver)
Allocating constructor.
static Interval parse(const std::string &input)
Parse an interval from a C++ string.
static std::string toString(const Interval &interval)
Unparse an interval to a string.
static Ptr instance()
Default allocating constructor.
static Interval parse(const char *input, const char **rest)
Parse an interval from a C string.
Sawyer::SharedPointer< IntervalParser > Ptr
Shared-ownership pointer.
static std::string docString(const std::string &interval="interval", const std::string &value="value")
Runtime documentation.
Information about a parsed switch value.
Base class parsing a value from input.
const ValueSaver::Ptr valueSaver() const
Property: functor responsible for saving a parsed value in user storage.
A container holding a set of values.
Definition IntervalSet.h:53
ROSE_UTIL_API std::string addrToString(uint64_t value, size_t nbits=0)
Convert a virtual address to a string.
ROSE_UTIL_API std::string cEscape(const std::string &, char context='"')
Escapes characters that are special to C/C++.
The ROSE library.
Position within a command-line.