ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
inifile.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header file is part of module \alib_config of the \aliblong.
4///
5/// \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#ifndef HPP_ALIB_CONFIG_INIFILE
9#define HPP_ALIB_CONFIG_INIFILE 1
10#pragma once
15
17
18namespace alib { namespace config {
19//==================================================================================================
20/// This class implements a rather simple foundation for reading and writing INI-Files.
21///
22/// \note This class is not considered to be used directly to interface into the \alib
23/// configuration system implemented with camp \alib_config, which introduces this class.
24/// Instead, class \alib{config;IniFileFeeder} provides all mechanics to easily use
25/// INI-files. For this reason, there is no \ref alib_nsalib_type_aliases "type alias"
26/// defined for this class in namespace #alib.
27///
28/// The design goal was to preserve any user formatting of the INI-file as much as possible.
29/// Thus, if the INI-file is written without any modification since it was read from a file,
30/// the resulting file should quite exactly in respect comments, values and even whitespaces.
31/// Exceptions are:
32/// - Whitespaces at the end of lines, are trimmed.
33/// - Sections that occur more than one time in the original file, are merged into one (the first)
34/// occurrence.
35/// - Some empty lines are removed.
36///
37/// When read, a linked list of INI-file sections is created and within each section a list of
38/// variables is created. The linked lists allow to write sections and their variables in the same
39/// order they have been read. In addition to the lists, a hashtable is created which allows
40/// finding variables quickly. To find a section, the section list is iterated.
41///
42/// After a file has been read (or also on a blank instance of this class), sections and
43/// variables can be manipulated (insert, delete, modify).
44/// It is also possible to read one or more files in a sequence and write a merged INI-file back.
45///
46/// This class does not perform any interpretation of the variable values. Neither
47/// escape sequences are converted, nor array values parsed or anything. Instead, the "raw"
48/// value of each variable, including the equal sign <c>'='</c> after the variable name is stored.
49/// It is up to other types to parse and interpret variable values and to convert a programm's
50/// variable values to strings which can be stored in INI-files (ASCII or UTF8 files).
51///
52/// \attention
53/// This class is also \b not designed to be used as a variable store with continued modifications
54/// during a run of a software. The class performs \ref alib_mods_contmono "monotonic allocation"
55/// but does not implement recycling of any objects (sections, variables, comments, values, etc.).
56/// Instead, the allocated memory is continued to grow with any operation.<br>
57/// This is a design decision and the class is meant to be used rather along the following
58/// approach:
59/// - Load an INI-file at the start of an application, transfer the values to a different place
60/// and then delete the instance of this class.
61/// - Before terminating an application, load the file again, perform appropriate changes,
62/// write the file back and delete the instance of this class.
63///
64/// \attention
65/// Such approach is implemented with type \alib{config;IniFileFeeder}.
66/// Hence, the direct use of this class is not recommended, instead use the techniques described
67/// in chapter \ref alib_config_external_ini of the Programmer's Manual of this \alibcamp.
68///
69/// Some remarks on the functionality and supported format:
70/// - Comments<br>
71/// - Lines that start (apart from whitespace) with either a double
72/// slash \c "//", a sharp sign \c '#' or a semicolon <c>';'</c> are comment lines.
73/// - Comment lines at the beginning of the file are associated with the file and are written
74/// back. Such comment block is stopped with the first single blank line.
75/// - Comment lines and empty lines before sections and variables are associated with the
76/// respective objects.
77/// - Comments cannot reside in the same line together with section names or variables.
78///
79/// - Sections<br>
80/// - Sections names are enclosed by brackets \c '[' and \c ']'.
81/// - Section names can be repeated. In this case the corresponding section is continued.
82/// As mentioned above, when the file is written, the sections are merged.
83///
84/// - Variables<br>
85/// - Variable names and their values are separated by an equal sign \c '='.
86/// - Variables definition are being continued (values are concatenated) if the line ends
87/// with a backslash \c '\\'.
88/// - Comment lines in-between continued lines are recognized as such. To continue a variable
89/// after a 'continued' comment line, the comment line needs to end with a backslash \c '\\'.
90///
91/// \anchor config_IniFile_writebackflag
92/// - Writeback Attribution<br>
93/// This is a non-standard feature of this class. Anywhere in the file, a line with the term
94/// <c>writeback</c> may appear. This flags the next section or variable to be written back
95/// by a software. This way, changes can easily be taken over into the INI-file in the right
96/// syntax that a software expects. In the case of using class \alib{config;Configuration}
97/// and associated type \alib{config;IniFileFeeder} (the intended case!), the following use-cases
98/// can be achieved:
99/// - Command line argument might be used overwrite INI-file values. Using this flag, such
100/// overwrites become persistant with a next invocation of the software without again
101/// specifying this argument.
102/// - In case of interactive software, changes may come from a user manipulating configuration
103/// dialogs. If the user had specified "writeback", this configuration
104/// change would automatically end up in the INI-file, even if the software does not provide own
105/// mechanics for this.
106/// - Session-related variables can be stored and updated in the INI-file, hence without creating
107/// a distinct temporary session file. This is useful for rather volatile variables and such
108/// that are implementing convenience features, rather than basic functionality.
109///
110/// The term <em>"writeback"</em> is a resourced using token <c>"INIWB"</c> and thus can be
111/// localized (translated to a different language) as appropriate.
112///
113/// - Erroneous lines<br>
114/// Lines with errors are ignored and not written back. Line numbers with erroneous lines
115/// are collected in field #LinesWithReadErrors.
116///
117/// @see Chapter \ref alib_config_external_ini of the Programmer's Manual of \alibcamp \alib_config
118/// for how to use INI-files with \alib.
119///\I{=============================================================================================}
120/// # Reference Documentation #
121/// @throws alib::config::Exceptions::ErrorOpeningFile
122/// @throws alib::config::Exceptions::ErrorWritingFile
123//==================================================================================================
125{
126 //==================================== Public Allocator ====================================
127 public:
128 /// A monotonic allocator used for allocating sections and entries.
130
131 //==================================== Entry and Section =====================================
132 public:
133 /// An entry in a \alib{config::IniFile;Section}.
134 struct Entry
135 {
136 String Name = NULL_STRING; ///< The entry's name.
137 String Comments = NULL_STRING; ///< The entry's comments.
138 String RawValue = NULL_STRING; ///< The 'raw' value, which is everything after the
139 ///< variable name, including the equal sign <c>'='</c>.
140 String Value = NULL_STRING; ///< The trimmed value. Multiline values are likewise trimmed
141 ///< and backslashes and line feeds are removed <c>'\</c>.
142 ///< This value is to be used used for reading a variable's content.
143 String NewValue = NULL_STRING; ///< If this value is set, #RawValue will ignored on writing.
144 bool WriteBack= false; ///< If given, a write back indicator was found for this entry.
145 void* Custom = nullptr; ///< May be used by freely by customers of this class.
146 ///< Initialized with \c nullptr, but otherwise not touched.
147
148 };
149
150 /// A section of the INI-file.
151 struct Section
152 {
154 protected:
155 /// Constructor. Protected and thus to be used only by friend class
156 /// \alib{containers;List}.
157 /// @param monoAllocator The allocator of the \b IniFile.
158 Section( MonoAllocator& monoAllocator )
159 : Entries(monoAllocator) {}
160
161 public:
162 String Name = NULL_STRING; ///< The name of the section.
163 String Comments= NULL_STRING; ///< The comment lines of the section.
164 List<MonoAllocator, Entry, Recycling::None> Entries; ///< The list of variables of the section.
165 bool WriteBack= false; ///< If given, a write back indicator was found for this entry.
166 };
167
168 //===================== Subtypes for Sections, Entries and the Hashtable =====================
169 protected:
170 /// Hash functor for nodes hashed in field #entryTable. Ignores letter case.
171 struct EntryKey
172 {
173 const String& SectionName; ///< The name of the section.
174 const String& EntryName; ///< The name of the entry.
175
176 /// Constructor.
177 /// @param sectionName The section's name of an entry.
178 /// @param entryName The name of an entry.
179 EntryKey( const String& sectionName, const String& entryName )
180 : SectionName( sectionName)
181 , EntryName ( entryName )
182 {}
183
184 /// Hash functor for nodes hashed in field #entryTable.
185 struct Hash
186 {
187 /// Calculates a hash code for \b NodeKey objects.
188 /// @param key The key to hash.
189 /// @return The hash code.
190 std::size_t operator()(const EntryKey& key) const
191 {
194 }
195 };
196
197 /// Equality functor for nodes hashed in field #entryTable.
198 struct EqualTo
199 {
200 /// Invokes \alib{strings;TString::Equals;String::Equals} on \p{lhs}, passing \p{rhs}
201 /// and returns the result.
202 /// @param lhs The first string object.
203 /// @param rhs The second string object.
204 /// @return The result of the string comparison.
205 bool operator()(const EntryKey& lhs, const EntryKey& rhs ) const
206 {
207 return ( (lhs.SectionName.IsEmpty() && rhs.SectionName.IsEmpty() )
209 && ( (lhs.EntryName .IsEmpty() && rhs.EntryName .IsEmpty() )
211 }
212 };
213 };
214
215 //===================================== Other internals =====================================
216 protected:
217 /// The entry hash set.
218 /// This is used to find entries by section name and entry name.
219 /// The set contains all entries of all sections.
221 EntryKey,
222 std::pair<Section*, Entry*>,
223 EntryKey::Hash,
225
226
227 /// Tests if the given string starts with <em>'#'</em>, <em>';'</em> or <em>'//'</em>.
228 /// @param subs The string to test.
229 /// @return \c true if this is a comment line, \c false otherwise.
230 bool startsWithCommentSymbol( String& subs );
231
232
233 //==================================== Other Public fields ===================================
234 public:
235 /// The list of sections.
237
238 /// The file name.
240
241 /// The file header which will be written out as a comment lines with "# " prefixes.
243
244 /// Filled with faulty line numbers when reading the file. (E.g., when a line is no section,
245 /// no comment and not the attribute "writeback", but still has no equal sign ('=').
247
248
249 //================================= Constructor/Destructor ==================================
250 public:
251 /// Default constructor.
253
254 /// Constructs an instance of this class and reads the file specified with \p{path}.
255 /// @param path The filepath to the INI-file.
256 inline IniFile( const lang::system::Path& path )
257 : IniFile() { Read( path ); }
258
259 /// Destructor.
261 {
262 #if ALIB_DEBUG
263 for( auto& section : Sections )
264 if( section.Name.IsNotEmpty() && section.Comments.IsNull() )
265 ALIB_WARNING( "CONFIG",
266 "Hint: New section {!Q} , which was programmatically added to\n"
267 " INI-file {!Q}, has no comments.",
268 section.Name, FileName )
269 #endif
270 }
271
272
273 //======================================== Interface ========================================
274
275 /// Clears all data, resets the internal mono allocator.
276 ALIB_API void Reset();
277
278 /// Counts the number of entries over all sections.
279 /// @return The number of "variables" in this INI-file.
280 inline integer Count() { return entryTable.Size(); }
281
282 /// Appends a new section to the end of the list of sections.
283 /// Must be invoked only if a section with the same name does not exist, yet.
284 /// @see Method SearchOrCreateSection.
285 /// @param name The name of the section.
286 /// @return The created section.
287 ALIB_API Section* CreateSection( const String& name);
288
289 /// Deletes a section.
290 /// @param name The name of the section to be deleted.
291 /// @return The deleted section. If not found, \c nullptr is returned. The section will
292 /// still be a valid and accessible object within the mono allocator.
293 ALIB_API Section* DeleteSection( const String& name);
294
295 /// Searches the section with the given name.
296 /// @param sectionName The name of the section to be retrieved.
297 /// @return Returns the section if it was found, nullptr otherwise.
298 ALIB_API Section* SearchSection( const String& sectionName );
299
300 /// Searches the section with the given name.
301 /// @param sectionName The name of the section to be retrieved.
302 /// @return Returns the section if it was found, nullptr otherwise.
303 ALIB_API std::pair<Section*, bool> SearchOrCreateSection( const String& sectionName );
304
305 /// Creates a new entry.
306 /// Must be invoked only if the entry does not exist, yet. The given entry name is copied to
307 /// the internal allocator.
308 /// @param section The section.
309 /// @param name The name of the entry to be created.
310 /// @return The entry found or created.
311 ALIB_API Entry* CreateEntry ( Section* section, const String& name );
312
313 /// Deletes an entry.
314 /// @param section The section of the entry.
315 /// @param name The name of the entry to be deleted.
316 /// @return The entry deleted. If not found, \c nullptr is returned. The entry will
317 /// still be a valid and accessible object within the mono allocator.
318 ALIB_API Entry* DeleteEntry ( Section* section, const String& name );
319
320 /// Deletes an entry. This overloaded searches the section by its name first.
321 /// @param sectionName The section.
322 /// @param name The name of the entry to be deleted.
323 /// @return The entry deleted. If not found, \c nullptr is returned. The entry will
324 /// still be a valid and accessible object within the monotonic allocator.
325 inline Entry* DeleteEntry ( const String& sectionName, const String& name )
326 {
327 auto* section= SearchSection( sectionName );
328 if( section == nullptr )
329 return nullptr;
330
331 return DeleteEntry( section, name );
332 }
333
334 /// Searches an entry with the given name. The search is performed case insensitive.
335 /// @param section The name of the section
336 /// @param name The name of the entry to be searched.
337 /// @return The section and entry if found, a \e nulled pair if not found.
338 ALIB_API std::pair<Section*, Entry*> SearchEntry (const String& section, const String& name );
339
340 /// Parses the comments for newline tokens and adds given comment \p{prefix} to each line
341 /// (in case no known comment prefix is present, yet). Then storage is allocated and
342 /// output reference \p{dest} is set accordingly.
343 /// @param dest The destination string to set.
344 /// @param comments The comments to add.
345 /// @param prefix The prefix to use if no other prefixes were found.
346 /// Defaults to <c>"# "</c>.
347 ALIB_API void AddComments ( String& dest, const String& comments,
348 const String& prefix= A_CHAR("# ") );
349
350 //====================================== Read/Write =======================================
351 public:
352 //==============================================================================================
353 /// Reads an INI-File and adds its contents to the existing data.
354 /// In case only the new entries should be contained, use method #Reset to delete existing
355 /// data before invoking this function.
356 ///
357 /// It might happen that lines are ignored or otherwise marked as faulty. All numbers of such
358 /// lines get collected in field #LinesWithReadErrors.
359 ///
360 /// Any other i/o errors throws, with the exception of "file not found", which simply
361 /// returns \c -1.
362 ///
363 /// @param path The file to read and write.
364 /// @return \c -1 if the file does not exist. Otherwise the number of entries read.
365 ///
366 /// @throws Exception( \alib{config;Exceptions;config::Exceptions::ErrorOpeningFile} ).
367 //==============================================================================================
370
371 //==============================================================================================
372 /// Writes the data into the file.
373 /// @param path The file to to write. If this is nulled, the default, then the
374 /// same file name as with the last #Read is used.
375 /// @throws Exception( \alib{config;Exceptions;config::Exceptions::ErrorOpeningFile} ).
376 //==============================================================================================
379};
380
381} // namespace alib[::config]
382
383
384} // namespace [alib]
385
386#endif // HPP_ALIB_CONFIG_INIFILE
387
lang::system::PathString FileName
The file name.
Definition inifile.hpp:239
ALIB_API void AddComments(String &dest, const String &comments, const String &prefix=A_CHAR("# "))
Definition inifile.cpp:147
List< MonoAllocator, Section > Sections
The list of sections.
Definition inifile.hpp:236
ALIB_API void Reset()
Clears all data, resets the internal mono allocator.
Definition inifile.cpp:46
ALIB_API Section * CreateSection(const String &name)
Definition inifile.cpp:56
ALIB_API std::pair< Section *, bool > SearchOrCreateSection(const String &sectionName)
Definition inifile.cpp:138
String FileComments
The file header which will be written out as a comment lines with "# " prefixes.
Definition inifile.hpp:242
HashMap< MonoAllocator, EntryKey, std::pair< Section *, Entry * >, EntryKey::Hash, EntryKey::EqualTo > entryTable
Definition inifile.hpp:224
MonoAllocator Allocator
A monotonic allocator used for allocating sections and entries.
Definition inifile.hpp:129
ALIB_API void Write(const lang::system::PathString &path=lang::system::NULL_PATH)
Definition inifile.cpp:339
ALIB_API Entry * CreateEntry(Section *section, const String &name)
Definition inifile.cpp:105
ALIB_API Section * DeleteSection(const String &name)
Definition inifile.cpp:63
ALIB_API Section * SearchSection(const String &sectionName)
Definition inifile.cpp:124
IniFile(const lang::system::Path &path)
Definition inifile.hpp:256
ALIB_API Entry * DeleteEntry(Section *section, const String &name)
Definition inifile.cpp:89
Entry * DeleteEntry(const String &sectionName, const String &name)
Definition inifile.hpp:325
List< MonoAllocator, integer > LinesWithReadErrors
Definition inifile.hpp:246
~IniFile()
Destructor.
Definition inifile.hpp:260
ALIB_API integer Read(const lang::system::CPathString &path)
Definition inifile.cpp:162
ALIB_API IniFile()
Default constructor.
Definition inifile.cpp:39
bool startsWithCommentSymbol(String &subs)
Definition inifile.cpp:26
ALIB_API std::pair< Section *, Entry * > SearchEntry(const String &section, const String &name)
Definition inifile.cpp:115
constexpr bool IsEmpty() const
Definition string.hpp:383
std::size_t HashcodeIgnoreCase() const
bool Equals(const TString< TChar > &rhs) const
Definition string.hpp:580
#define ALIB_WARNING(...)
Definition alib.hpp:1268
#define A_CHAR(STR)
#define ALIB_API
Definition alib.hpp:639
static constexpr PathString NULL_PATH
A nulled path string.
Definition path.hpp:87
Definition alib.cpp:69
strings::compatibility::std::StringWriter StringWriter
Type alias in namespace alib.
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.hpp:2549
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
Equality functor for nodes hashed in field entryTable.
Definition inifile.hpp:199
bool operator()(const EntryKey &lhs, const EntryKey &rhs) const
Definition inifile.hpp:205
Hash functor for nodes hashed in field entryTable.
Definition inifile.hpp:186
std::size_t operator()(const EntryKey &key) const
Definition inifile.hpp:190
Hash functor for nodes hashed in field entryTable. Ignores letter case.
Definition inifile.hpp:172
const String & EntryName
The name of the entry.
Definition inifile.hpp:174
const String & SectionName
The name of the section.
Definition inifile.hpp:173
EntryKey(const String &sectionName, const String &entryName)
Definition inifile.hpp:179
An entry in a Section.
Definition inifile.hpp:135
String Comments
The entry's comments.
Definition inifile.hpp:137
String NewValue
If this value is set, RawValue will ignored on writing.
Definition inifile.hpp:143
String Name
The entry's name.
Definition inifile.hpp:136
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.hpp:144
A section of the INI-file.
Definition inifile.hpp:152
Section(MonoAllocator &monoAllocator)
Definition inifile.hpp:158
String Comments
The comment lines of the section.
Definition inifile.hpp:163
String Name
The name of the section.
Definition inifile.hpp:162
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.hpp:165
List< MonoAllocator, Entry, Recycling::None > Entries
The list of variables of the section.
Definition inifile.hpp:164