annotate DEPENDENCIES/generic/include/boost/property_tree/ini_parser.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 // ----------------------------------------------------------------------------
Chris@16 2 // Copyright (C) 2002-2006 Marcin Kalicinski
Chris@16 3 // Copyright (C) 2009 Sebastian Redl
Chris@16 4 //
Chris@16 5 // Distributed under the Boost Software License, Version 1.0.
Chris@16 6 // (See accompanying file LICENSE_1_0.txt or copy at
Chris@16 7 // http://www.boost.org/LICENSE_1_0.txt)
Chris@16 8 //
Chris@16 9 // For more information, see www.boost.org
Chris@16 10 // ----------------------------------------------------------------------------
Chris@16 11 #ifndef BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
Chris@16 12 #define BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
Chris@16 13
Chris@16 14 #include <boost/property_tree/ptree.hpp>
Chris@16 15 #include <boost/property_tree/detail/ptree_utils.hpp>
Chris@16 16 #include <boost/property_tree/detail/file_parser_error.hpp>
Chris@16 17 #include <fstream>
Chris@16 18 #include <string>
Chris@16 19 #include <sstream>
Chris@16 20 #include <stdexcept>
Chris@16 21 #include <locale>
Chris@16 22
Chris@16 23 namespace boost { namespace property_tree { namespace ini_parser
Chris@16 24 {
Chris@16 25
Chris@16 26 /**
Chris@16 27 * Determines whether the @c flags are valid for use with the ini_parser.
Chris@16 28 * @param flags value to check for validity as flags to ini_parser.
Chris@16 29 * @return true if the flags are valid, false otherwise.
Chris@16 30 */
Chris@16 31 inline bool validate_flags(int flags)
Chris@16 32 {
Chris@16 33 return flags == 0;
Chris@16 34 }
Chris@16 35
Chris@16 36 /** Indicates an error parsing INI formatted data. */
Chris@16 37 class ini_parser_error: public file_parser_error
Chris@16 38 {
Chris@16 39 public:
Chris@16 40 /**
Chris@16 41 * Construct an @c ini_parser_error
Chris@16 42 * @param message Message describing the parser error.
Chris@16 43 * @param filename The name of the file being parsed containing the
Chris@16 44 * error.
Chris@16 45 * @param line The line in the given file where an error was
Chris@16 46 * encountered.
Chris@16 47 */
Chris@16 48 ini_parser_error(const std::string &message,
Chris@16 49 const std::string &filename,
Chris@16 50 unsigned long line)
Chris@16 51 : file_parser_error(message, filename, line)
Chris@16 52 {
Chris@16 53 }
Chris@16 54 };
Chris@16 55
Chris@16 56 /**
Chris@16 57 * Read INI from a the given stream and translate it to a property tree.
Chris@16 58 * @note Clears existing contents of property tree. In case of error
Chris@16 59 * the property tree is not modified.
Chris@16 60 * @throw ini_parser_error If a format violation is found.
Chris@16 61 * @param stream Stream from which to read in the property tree.
Chris@16 62 * @param[out] pt The property tree to populate.
Chris@16 63 */
Chris@16 64 template<class Ptree>
Chris@16 65 void read_ini(std::basic_istream<
Chris@16 66 typename Ptree::key_type::value_type> &stream,
Chris@16 67 Ptree &pt)
Chris@16 68 {
Chris@16 69 typedef typename Ptree::key_type::value_type Ch;
Chris@16 70 typedef std::basic_string<Ch> Str;
Chris@16 71 const Ch semicolon = stream.widen(';');
Chris@16 72 const Ch hash = stream.widen('#');
Chris@16 73 const Ch lbracket = stream.widen('[');
Chris@16 74 const Ch rbracket = stream.widen(']');
Chris@16 75
Chris@16 76 Ptree local;
Chris@16 77 unsigned long line_no = 0;
Chris@16 78 Ptree *section = 0;
Chris@16 79 Str line;
Chris@16 80
Chris@16 81 // For all lines
Chris@16 82 while (stream.good())
Chris@16 83 {
Chris@16 84
Chris@16 85 // Get line from stream
Chris@16 86 ++line_no;
Chris@16 87 std::getline(stream, line);
Chris@16 88 if (!stream.good() && !stream.eof())
Chris@16 89 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 90 "read error", "", line_no));
Chris@16 91
Chris@16 92 // If line is non-empty
Chris@16 93 line = property_tree::detail::trim(line, stream.getloc());
Chris@16 94 if (!line.empty())
Chris@16 95 {
Chris@16 96 // Comment, section or key?
Chris@16 97 if (line[0] == semicolon || line[0] == hash)
Chris@16 98 {
Chris@16 99 // Ignore comments
Chris@16 100 }
Chris@16 101 else if (line[0] == lbracket)
Chris@16 102 {
Chris@16 103 // If the previous section was empty, drop it again.
Chris@16 104 if (section && section->empty())
Chris@16 105 local.pop_back();
Chris@16 106 typename Str::size_type end = line.find(rbracket);
Chris@16 107 if (end == Str::npos)
Chris@16 108 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 109 "unmatched '['", "", line_no));
Chris@16 110 Str key = property_tree::detail::trim(
Chris@16 111 line.substr(1, end - 1), stream.getloc());
Chris@16 112 if (local.find(key) != local.not_found())
Chris@16 113 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 114 "duplicate section name", "", line_no));
Chris@16 115 section = &local.push_back(
Chris@16 116 std::make_pair(key, Ptree()))->second;
Chris@16 117 }
Chris@16 118 else
Chris@16 119 {
Chris@16 120 Ptree &container = section ? *section : local;
Chris@16 121 typename Str::size_type eqpos = line.find(Ch('='));
Chris@16 122 if (eqpos == Str::npos)
Chris@16 123 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 124 "'=' character not found in line", "", line_no));
Chris@16 125 if (eqpos == 0)
Chris@16 126 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 127 "key expected", "", line_no));
Chris@16 128 Str key = property_tree::detail::trim(
Chris@16 129 line.substr(0, eqpos), stream.getloc());
Chris@16 130 Str data = property_tree::detail::trim(
Chris@16 131 line.substr(eqpos + 1, Str::npos), stream.getloc());
Chris@16 132 if (container.find(key) != container.not_found())
Chris@16 133 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 134 "duplicate key name", "", line_no));
Chris@16 135 container.push_back(std::make_pair(key, Ptree(data)));
Chris@16 136 }
Chris@16 137 }
Chris@16 138 }
Chris@16 139 // If the last section was empty, drop it again.
Chris@16 140 if (section && section->empty())
Chris@16 141 local.pop_back();
Chris@16 142
Chris@16 143 // Swap local ptree with result ptree
Chris@16 144 pt.swap(local);
Chris@16 145
Chris@16 146 }
Chris@16 147
Chris@16 148 /**
Chris@16 149 * Read INI from a the given file and translate it to a property tree.
Chris@16 150 * @note Clears existing contents of property tree. In case of error the
Chris@16 151 * property tree unmodified.
Chris@16 152 * @throw ini_parser_error In case of error deserializing the property tree.
Chris@16 153 * @param filename Name of file from which to read in the property tree.
Chris@16 154 * @param[out] pt The property tree to populate.
Chris@16 155 * @param loc The locale to use when reading in the file contents.
Chris@16 156 */
Chris@16 157 template<class Ptree>
Chris@16 158 void read_ini(const std::string &filename,
Chris@16 159 Ptree &pt,
Chris@16 160 const std::locale &loc = std::locale())
Chris@16 161 {
Chris@16 162 std::basic_ifstream<typename Ptree::key_type::value_type>
Chris@16 163 stream(filename.c_str());
Chris@16 164 if (!stream)
Chris@16 165 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 166 "cannot open file", filename, 0));
Chris@16 167 stream.imbue(loc);
Chris@16 168 try {
Chris@16 169 read_ini(stream, pt);
Chris@16 170 }
Chris@16 171 catch (ini_parser_error &e) {
Chris@16 172 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 173 e.message(), filename, e.line()));
Chris@16 174 }
Chris@16 175 }
Chris@16 176
Chris@16 177 namespace detail
Chris@16 178 {
Chris@16 179 template<class Ptree>
Chris@16 180 void check_dupes(const Ptree &pt)
Chris@16 181 {
Chris@16 182 if(pt.size() <= 1)
Chris@16 183 return;
Chris@16 184 const typename Ptree::key_type *lastkey = 0;
Chris@16 185 typename Ptree::const_assoc_iterator it = pt.ordered_begin(),
Chris@16 186 end = pt.not_found();
Chris@16 187 lastkey = &it->first;
Chris@16 188 for(++it; it != end; ++it) {
Chris@16 189 if(*lastkey == it->first)
Chris@16 190 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 191 "duplicate key", "", 0));
Chris@16 192 lastkey = &it->first;
Chris@16 193 }
Chris@16 194 }
Chris@101 195
Chris@101 196 template <typename Ptree>
Chris@101 197 void write_keys(std::basic_ostream<
Chris@101 198 typename Ptree::key_type::value_type
Chris@101 199 > &stream,
Chris@101 200 const Ptree& pt,
Chris@101 201 bool throw_on_children)
Chris@101 202 {
Chris@101 203 typedef typename Ptree::key_type::value_type Ch;
Chris@101 204 for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
Chris@101 205 it != end; ++it)
Chris@101 206 {
Chris@101 207 if (!it->second.empty()) {
Chris@101 208 if (throw_on_children) {
Chris@101 209 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@101 210 "ptree is too deep", "", 0));
Chris@101 211 }
Chris@101 212 continue;
Chris@101 213 }
Chris@101 214 stream << it->first << Ch('=')
Chris@101 215 << it->second.template get_value<
Chris@101 216 std::basic_string<Ch> >()
Chris@101 217 << Ch('\n');
Chris@101 218 }
Chris@101 219 }
Chris@101 220
Chris@101 221 template <typename Ptree>
Chris@101 222 void write_top_level_keys(std::basic_ostream<
Chris@101 223 typename Ptree::key_type::value_type
Chris@101 224 > &stream,
Chris@101 225 const Ptree& pt)
Chris@101 226 {
Chris@101 227 write_keys(stream, pt, false);
Chris@101 228 }
Chris@101 229
Chris@101 230 template <typename Ptree>
Chris@101 231 void write_sections(std::basic_ostream<
Chris@101 232 typename Ptree::key_type::value_type
Chris@101 233 > &stream,
Chris@101 234 const Ptree& pt)
Chris@101 235 {
Chris@101 236 typedef typename Ptree::key_type::value_type Ch;
Chris@101 237 for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
Chris@101 238 it != end; ++it)
Chris@101 239 {
Chris@101 240 if (!it->second.empty()) {
Chris@101 241 check_dupes(it->second);
Chris@101 242 if (!it->second.data().empty())
Chris@101 243 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@101 244 "mixed data and children", "", 0));
Chris@101 245 stream << Ch('[') << it->first << Ch(']') << Ch('\n');
Chris@101 246 write_keys(stream, it->second, true);
Chris@101 247 }
Chris@101 248 }
Chris@101 249 }
Chris@16 250 }
Chris@16 251
Chris@16 252 /**
Chris@16 253 * Translates the property tree to INI and writes it the given output
Chris@16 254 * stream.
Chris@16 255 * @pre @e pt cannot have data in its root.
Chris@16 256 * @pre @e pt cannot have keys both data and children.
Chris@16 257 * @pre @e pt cannot be deeper than two levels.
Chris@16 258 * @pre There cannot be duplicate keys on any given level of @e pt.
Chris@16 259 * @throw ini_parser_error In case of error translating the property tree to
Chris@16 260 * INI or writing to the output stream.
Chris@16 261 * @param stream The stream to which to write the INI representation of the
Chris@16 262 * property tree.
Chris@16 263 * @param pt The property tree to tranlsate to INI and output.
Chris@16 264 * @param flags The flags to use when writing the INI file.
Chris@16 265 * No flags are currently supported.
Chris@16 266 */
Chris@16 267 template<class Ptree>
Chris@16 268 void write_ini(std::basic_ostream<
Chris@16 269 typename Ptree::key_type::value_type
Chris@16 270 > &stream,
Chris@16 271 const Ptree &pt,
Chris@16 272 int flags = 0)
Chris@16 273 {
Chris@16 274 BOOST_ASSERT(validate_flags(flags));
Chris@16 275 (void)flags;
Chris@16 276
Chris@16 277 if (!pt.data().empty())
Chris@16 278 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 279 "ptree has data on root", "", 0));
Chris@101 280 detail::check_dupes(pt);
Chris@16 281
Chris@101 282 detail::write_top_level_keys(stream, pt);
Chris@101 283 detail::write_sections(stream, pt);
Chris@16 284 }
Chris@16 285
Chris@16 286 /**
Chris@16 287 * Translates the property tree to INI and writes it the given file.
Chris@16 288 * @pre @e pt cannot have data in its root.
Chris@16 289 * @pre @e pt cannot have keys both data and children.
Chris@16 290 * @pre @e pt cannot be deeper than two levels.
Chris@16 291 * @pre There cannot be duplicate keys on any given level of @e pt.
Chris@16 292 * @throw info_parser_error In case of error translating the property tree
Chris@16 293 * to INI or writing to the file.
Chris@16 294 * @param filename The name of the file to which to write the INI
Chris@16 295 * representation of the property tree.
Chris@16 296 * @param pt The property tree to tranlsate to INI and output.
Chris@16 297 * @param flags The flags to use when writing the INI file.
Chris@16 298 * The following flags are supported:
Chris@16 299 * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The
Chris@16 300 * validity check covers the preconditions but takes <tt>O(n log n)</tt>
Chris@16 301 * time.
Chris@16 302 * @param loc The locale to use when writing the file.
Chris@16 303 */
Chris@16 304 template<class Ptree>
Chris@16 305 void write_ini(const std::string &filename,
Chris@16 306 const Ptree &pt,
Chris@16 307 int flags = 0,
Chris@16 308 const std::locale &loc = std::locale())
Chris@16 309 {
Chris@16 310 std::basic_ofstream<typename Ptree::key_type::value_type>
Chris@16 311 stream(filename.c_str());
Chris@16 312 if (!stream)
Chris@16 313 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 314 "cannot open file", filename, 0));
Chris@16 315 stream.imbue(loc);
Chris@16 316 try {
Chris@16 317 write_ini(stream, pt, flags);
Chris@16 318 }
Chris@16 319 catch (ini_parser_error &e) {
Chris@16 320 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
Chris@16 321 e.message(), filename, e.line()));
Chris@16 322 }
Chris@16 323 }
Chris@16 324
Chris@16 325 } } }
Chris@16 326
Chris@16 327 namespace boost { namespace property_tree
Chris@16 328 {
Chris@16 329 using ini_parser::ini_parser_error;
Chris@16 330 using ini_parser::read_ini;
Chris@16 331 using ini_parser::write_ini;
Chris@16 332 } }
Chris@16 333
Chris@16 334 #endif