xdrpp
RFC4506 XDR compiler and message library
iniparse.cc
1 
2 #include <cstring>
3 #include <fstream>
4 #include <type_traits>
5 #include <xdrpp/iniparse.h>
6 
7 namespace xdr {
8 
9 using std::size_t;
10 
11 bool ini_unescape(string::const_iterator, string::const_iterator, string *);
12 
13 void
14 from_string(const string &s, string *sp)
15 {
16  *sp = s;
17 }
18 
19 void
20 from_string(const string &s, bool *bp)
21 {
22  if (s == "true")
23  *bp = true;
24  else if (s == "false")
25  *bp = false;
26  else
27  throw std::invalid_argument ("boolean must be \"true\" or \"false\"");
28 }
29 
30 template<class T> inline T
31 add_base(T (&conv)(const string &, size_t *, int), const string &s, size_t *pos)
32 {
33  return conv(s, pos, 0);
34 }
35 template<class T> inline T
36 add_base(T (&conv)(const string &, size_t *), const string &s, size_t *pos)
37 {
38  return conv(s, pos);
39 }
40 
41 template<typename T, typename U, typename ...Base> inline void
42 from_string_with(U (&conv)(const string &, size_t *, Base...),
43  const string &s, T *rp)
44 {
45  std::size_t pos;
46  U r (add_base(conv, s, &pos));
47  T t = r;
48  if (static_cast<U>(t) != r)
49  throw std::out_of_range ("value out of range");
50  else if (s.find_first_not_of("\t\n\r ", pos) != string::npos)
51  throw std::invalid_argument ("trailing garbage: " + s.substr(pos));
52  *rp = std::move(t);
53 }
54 
55 #define FROM_STRING(T, conv) \
56  void from_string(const string &s, T *rp) { from_string_with(conv, s, rp); }
57 FROM_STRING(int, std::stoi)
58 FROM_STRING(long, std::stol)
59 FROM_STRING(long long, std::stoll)
60 FROM_STRING(unsigned char, std::stoul)
61 FROM_STRING(unsigned short, std::stoul)
62 FROM_STRING(unsigned int, std::stoul)
63 FROM_STRING(unsigned long, std::stoul)
64 FROM_STRING(unsigned long long, std::stoll)
65 FROM_STRING(float, std::stof)
66 FROM_STRING(double, std::stod)
67 FROM_STRING(long double, std::stold)
68 
69 std::vector<string>
71 {
72  std::vector<string> av;
73  string a;
74  size_t i = 0, e;
75  while (i < rawvalue_.size()) {
76  e = i;
77  do {
78  e = rawvalue_.find_first_of("\t\r ", e+1);
79  } while (e != string::npos && e > 0 && rawvalue_[e-1] == '\\');
80  if (e == string::npos)
81  e = rawvalue_.size();
82  if (!ini_unescape(rawvalue_.cbegin() + i, rawvalue_.cbegin() + e, &a))
83  // This shouldn't happen, since ini_parse should already hit the error
84  throw std::invalid_argument("stray backslash at end of line");
85  av.emplace_back(a);
86  i = rawvalue_.find_first_not_of("\t\r ", e);
87  }
88  return av;
89 }
90 
91 static void
92 ignoreline(const IniLine &li)
93 {
94 }
95 
96 void
97 IniGroup::parse(const IniLine &li)
98 {
99  auto i (cbs_.find(li.key_));
100  if (i == cbs_.end()) {
101  li.warn() << "unknown property "
102  << li.group_ << '.' << li.key_ << std::endl;
103  cbs_.emplace(li.key_, ignoreline);
104  }
105  else
106  i->second(li);
107 }
108 
109 bool
110 ini_unescape(string::const_iterator i, string::const_iterator e, string *out)
111 {
112  bool escape {false};
113  string v;
114  for (; i != e; i++) {
115  if (escape) {
116  switch (*i) {
117  case 'n':
118  v.push_back('\n');
119  break;
120  case 'r':
121  v.push_back('\r');
122  break;
123  case 's':
124  v.push_back(' ');
125  break;
126  case 't':
127  v.push_back('\t');
128  break;
129  default:
130  v.push_back(*i);
131  break;
132  }
133  escape = false;
134  }
135  else if (*i == '\\')
136  escape = true;
137  else
138  v.push_back(*i);
139  }
140  if (escape)
141  return false;
142  out->assign(std::move(v));
143  return true;
144 }
145 
146 static bool
147 parsekv(const string &line, string *kout, string *vout, string *rvout)
148 {
149  string k;
150  auto li = (line.cbegin());
151 
152  while (li < line.cend() && isspace(*li))
153  li++;
154  while (li < line.cend() && !isspace(*li) && *li != '=')
155  k.push_back(*li++);
156  while (li < line.cend() && isspace(*li))
157  li++;
158  if (k.empty() || li >= line.cend() || *li++ != '=')
159  return false;
160  while (li < line.cend() && isspace(*li))
161  li++;
162 
163  if (!ini_unescape(li, line.cend(), vout))
164  return false;
165  rvout->assign(string(li, line.cend()));
166  kout->assign(std::move(k));
167  return true;
168 }
169 
170 void
171 ini_runparse(IniActions &a, IniLine &st, std::istream &s)
172 {
173  auto group = a.end();
174  string line;
175  while (getline(s, line).good()) {
176  st.lineno_++;
177  line.erase(0, line.find_first_not_of("\t\r "));
178  if (line.empty() || line[0] == '#')
179  continue;
180 
181  if (line[0] == '[') {
182  size_t e = line.rfind(']');
183  if (e == 1 || e == string::npos
184  || line.find_first_not_of("\t\r ", e+1) != string::npos)
185  st.fail() << "syntax error" << std::endl;
186  else {
187  st.group_ = line.substr(1, e-1);
188  group = a.find(st.group_);
189  if (group == a.end())
190  st.warn() << "unknown group " << st.group_ << "\n";
191  }
192  }
193  else if (st.group_.empty())
194  st.fail() << "key precedes group" << std::endl;
195  else if (group == a.end())
196  ;
197  else if (parsekv(line, &st.key_, &st.value_, &st.rawvalue_)) {
198  try { group->second.parse(st); }
199  catch (std::logic_error &e) { st.fail() << e.what() << std::endl; }
200  }
201  else
202  st.fail() << "syntax error" << std::endl;
203  }
204 }
205 
206 bool
207 ini_parse(IniActions &a, string file)
208 {
209  IniLine st;
210  st.file_ = file;
211  std::ifstream s {st.file_};
212  if (s.bad()) {
213  std::cerr << file << ": " << std::strerror (errno) << std::endl;
214  return false;
215  }
216  ini_runparse(a, st, s);
217  return !st.error();
218 }
219 
220 }
std::unordered_map< string, IniGroup > IniActions
Holds the actions to execute on various properties in the ini file.
Definition: iniparse.h:128
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
string value_
Value of the property.
Definition: iniparse.h:75
void ini_runparse(IniActions &a, IniLine &st, std::istream &s)
Run the ini parser.
Definition: iniparse.cc:171
Parser for .ini style files.
bool ini_parse(IniActions &a, string file)
Run the parser on a file.
Definition: iniparse.cc:207
string rawvalue_
Raw value of the property (with escape sequences unexpanded).
Definition: iniparse.h:77
string group_
Name of the group containing property.
Definition: iniparse.h:73
bool error() const
Returns true if there has been a parsing error in the ini file.
Definition: iniparse.h:86
string key_
Key of the property.
Definition: iniparse.h:74
Contents of a specific property.
Definition: iniparse.h:67
std::ostream & warn() const
Print a message to cerr with the file ane line number prefixed.
Definition: iniparse.h:80
std::ostream & fail() const
Like IniLine::warn, but also sets the error flag to indicate failure.
Definition: iniparse.h:84
std::vector< string > argv() const
Transform the line into a vector of words.
Definition: iniparse.cc:70