C++ Utilities  4.6.1
Common C++ classes and routines used by my applications 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  }
113  break;
114  case ScopeName:
115  switch(c) {
116  case ']':
117  state = Init;
118  break;
119  default:
120  scope += c;
121  }
122  break;
123  case Value:
124  switch(c) {
125  case '\n':
126  finishKeyValue();
127  state = Init;
128  break;
129  case '#':
130  finishKeyValue();
131  state = Comment;
132  break;
133  default:
134  addChar(value);
135  }
136  break;
137  }
138  }
139  } catch(...) {
140  const char *what = catchIoFailure();
141  if(inputStream.eof()) {
142  // we just reached the end of the file
143  // don't forget to save the last key/value pair
144  finishKeyValue();
145  } else {
146  throwIoFailure(what);
147  }
148  }
149 }
150 
154 void IniFile::make(ostream &outputStream)
155 {
156  // thorw an exception when an IO error occurs
157  outputStream.exceptions(ios_base::failbit | ios_base::badbit);
158  for(const auto &scope : m_data) {
159  outputStream << '[' << scope.first << ']' << '\n';
160  for(const auto &field : scope.second) {
161  outputStream << field.first << '=' << field.second << '\n';
162  }
163  outputStream << '\n';
164  }
165 }
166 
167 } // namespace IoUtilities
168 
CPP_UTILITIES_EXPORT void throwIoFailure(const char *what)
Throws a std::ios_base::failure with the specified message.
STL namespace.
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.