C++ Utilities  4.17.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
inifile.cpp
Go to the documentation of this file.
1 #include "./inifile.h"
2 #include "./catchiofailure.h"
3 
4 #include <iostream>
5 
6 using namespace std;
7 
8 namespace IoUtilities {
9 
18 void IniFile::parse(std::istream &inputStream)
19 {
20  // define variable for state machine
21  enum State { Init, Comment, ScopeName, Key, Value } state = Init;
22  // current character
23  char c;
24  // number of postponed whitespaces
25  unsigned int whitespace = 0;
26  // current scope, key and value
27  string scope, key, value;
28  scope.reserve(16);
29  key.reserve(16);
30  value.reserve(256);
31  // define actions for state machine
32  // called when key/value pair is complete
33  const auto finishKeyValue = [&state, &scope, &key, &value, &whitespace, this] {
34  if (key.empty() && value.empty() && state != Value) {
35  return;
36  }
37  if (m_data.empty() || m_data.back().first != scope) {
38  m_data.emplace_back(make_pair(scope, decltype(m_data)::value_type::second_type()));
39  }
40  m_data.back().second.insert(make_pair(key, value));
41  key.clear();
42  value.clear();
43  whitespace = 0;
44  };
45  // called to add current character to current key or value
46  const auto addChar = [&whitespace, &c](string &to) {
47  if (c == ' ') {
48  ++whitespace;
49  } else {
50  if (!to.empty()) {
51  while (whitespace) {
52  to += ' ';
53  --whitespace;
54  }
55  } else {
56  whitespace = 0;
57  }
58  to += c;
59  }
60  };
61  // thorw an exception when an IO error occurs
62  inputStream.exceptions(ios_base::failbit | ios_base::badbit);
63  // parse the file char by char
64  try {
65  while (inputStream.get(c)) {
66  switch (state) {
67  case Init:
68  switch (c) {
69  case '\n':
70  break;
71  case '#':
72  state = Comment;
73  break;
74  case '=':
75  whitespace = 0;
76  state = Value;
77  break;
78  case '[':
79  scope.clear();
80  state = ScopeName;
81  break;
82  default:
83  addChar(key);
84  state = Key;
85  }
86  break;
87  case Key:
88  switch (c) {
89  case '\n':
90  finishKeyValue();
91  state = Init;
92  break;
93  case '#':
94  finishKeyValue();
95  state = Comment;
96  break;
97  case '=':
98  whitespace = 0;
99  state = Value;
100  break;
101  default:
102  addChar(key);
103  }
104  break;
105  case Comment:
106  switch (c) {
107  case '\n':
108  state = Init;
109  break;
110  default:;
111  }
112  break;
113  case ScopeName:
114  switch (c) {
115  case ']':
116  state = Init;
117  break;
118  default:
119  scope += c;
120  }
121  break;
122  case Value:
123  switch (c) {
124  case '\n':
125  finishKeyValue();
126  state = Init;
127  break;
128  case '#':
129  finishKeyValue();
130  state = Comment;
131  break;
132  default:
133  addChar(value);
134  }
135  break;
136  }
137  }
138  } catch (...) {
139  const char *what = catchIoFailure();
140  if (inputStream.eof()) {
141  // we just reached the end of the file
142  // don't forget to save the last key/value pair
143  finishKeyValue();
144  } else {
145  throwIoFailure(what);
146  }
147  }
148 }
149 
153 void IniFile::make(ostream &outputStream)
154 {
155  // thorw an exception when an IO error occurs
156  outputStream.exceptions(ios_base::failbit | ios_base::badbit);
157  for (const auto &scope : m_data) {
158  outputStream << '[' << scope.first << ']' << '\n';
159  for (const auto &field : scope.second) {
160  outputStream << field.first << '=' << field.second << '\n';
161  }
162  outputStream << '\n';
163  }
164 }
165 
166 } // namespace IoUtilities
CPP_UTILITIES_EXPORT void throwIoFailure(const char *what)
Throws an std::ios_base::failure with the specified message.
Contains utility classes helping to read and write streams.
Definition: binaryreader.h:10
CPP_UTILITIES_EXPORT const char * catchIoFailure()
Provides a workaround for GCC Bug 66145.