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