ALib C++ Library
Library Version: 2511 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 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 /// A section of the INI-file.
143 struct Section
144 {
146 protected:
147 /// Constructor. Protected and thus to be used only by friend class
148 /// \alib{containers;List}.
149 /// @param monoAllocator The allocator of the \b IniFile.
150 Section( MonoAllocator& monoAllocator )
151 : Entries(monoAllocator) {}
152
153 public:
154 String Name = NULL_STRING; ///< The name of the section.
155 String Comments= NULL_STRING; ///< The comment lines of the section.
156 ListMA<Entry, Recycling::None> Entries; ///< The list of variables of the section.
157 bool WriteBack= false; ///< If given, a write back indicator was found for this entry.
158 };
159
160 //======================== Subtypes for Sections, Entries and the Hashtable ======================
161 protected:
162 /// Hash functor for nodes hashed in field #entryTable. Ignores letter case.
163 struct EntryKey
164 {
165 const String& SectionName; ///< The name of the section.
166 const String& EntryName; ///< The name of the entry.
167
168 /// Constructor.
169 /// @param sectionName The section's name of an entry.
170 /// @param entryName The name of an entry.
171 EntryKey( const String& sectionName, const String& entryName )
172 : SectionName( sectionName)
173 , EntryName ( entryName ) {}
174
175 /// Hash functor for nodes hashed in field #entryTable.
176 struct Hash
177 {
178 /// Calculates a hash code for \b NodeKey objects.
179 /// @param key The key to hash.
180 /// @return The hash code.
181 std::size_t operator()(const EntryKey& key) const {
184 }
185 };
186
187 /// Equality functor for nodes hashed in field #entryTable.
188 struct EqualTo
189 {
190 /// Invokes \alib{strings;TString::Equals;String::Equals} on \p{lhs}, passing \p{rhs}
191 /// and returns the result.
192 /// @param lhs The first string object.
193 /// @param rhs The second string object.
194 /// @return The result of the string comparison.
195 bool operator()(const EntryKey& lhs, const EntryKey& rhs ) const {
196 return ( (lhs.SectionName.IsEmpty() && rhs.SectionName.IsEmpty() )
198 && ( (lhs.EntryName .IsEmpty() && rhs.EntryName .IsEmpty() )
200 }
201 };
202 };
203
204 //======================================== Other internals =======================================
205 protected:
206 /// The entry hash set.
207 /// This is used to find entries by section name and entry name.
208 /// The set contains all entries of all sections.
210 EntryKey,
211 std::pair<Section*, Entry*>,
212 EntryKey::Hash,
214
215
216 /// Tests if the given string starts with <em>'#'</em>, <em>';'</em> or <em>'//'</em>.
217 /// @param subs The string to test.
218 /// @return \c true if this is a comment line, \c false otherwise.
219 bool startsWithCommentSymbol( String& subs );
220
221
222 //====================================== Other Public fields =====================================
223 public:
224 /// The list of sections.
226
227 /// The file name.
229
230 /// The file header which will be written out as a comment lines with "# " prefixes.
232
233 /// Filled with faulty line numbers when reading the file. (E.g., when a line is no section,
234 /// no comment and not the attribute "writeback", but still has no equal sign ('=').
236
237
238 //===================================== Constructor/Destructor ===================================
239 public:
240 /// Default constructor.
242
243 /// Constructs an instance of this class and reads the file specified with \p{path}.
244 /// @param path The filepath to the INI-file.
245 inline IniFile( const system::Path& path )
246 : IniFile() { Read( path ); }
247
248 /// Destructor.
250 #if ALIB_DEBUG
251 for( auto& section : Sections )
252 if( section.Name.IsNotEmpty() && section.Comments.IsNull() )
253 ALIB_WARNING( "VARIABLES",
254 "Hint: New section \"{}\", which was programmatically added to\n"
255 " INI-file \"{}\", has no comments.",
256 section.Name, FileName )
257 #endif
258 }
259
260
261 //=========================================== Interface ==========================================
262
263 /// Clears all data, resets the internal mono allocator.
264 ALIB_DLL void Reset();
265
266 /// Counts the number of entries over all sections.
267 /// @return The number of "variables" in this INI-file.
268 inline integer Count() { return entryTable.Size(); }
269
270 /// Appends a new section to the end of the list of sections.
271 /// Must be invoked only if a section with the same name does not exist, yet.
272 /// @see Method SearchOrCreateSection.
273 /// @param name The name of the section.
274 /// @return The created section.
275 ALIB_DLL Section* CreateSection( const String& name);
276
277 /// Deletes a section.
278 /// @param name The name of the section to be deleted.
279 /// @return The deleted section. If not found, \c nullptr is returned. The section will
280 /// still be a valid and accessible object within the mono allocator.
281 ALIB_DLL Section* DeleteSection( const String& name);
282
283 /// Searches the section with the given name.
284 /// @param sectionName The name of the section to be retrieved.
285 /// @return Returns the section if it was found, nullptr otherwise.
286 ALIB_DLL Section* SearchSection( const String& sectionName );
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 std::pair<Section*, bool> SearchOrCreateSection( const String& sectionName );
292
293 /// Creates a new entry.
294 /// Must be invoked only if the entry does not exist, yet. The given entry name is copied to
295 /// the internal allocator.
296 /// @param section The section.
297 /// @param name The name of the entry to be created.
298 /// @return The entry found or created.
299 ALIB_DLL Entry* CreateEntry ( Section* section, const String& name );
300
301 /// Deletes an entry.
302 /// @param section The section of the entry.
303 /// @param name The name of the entry to be deleted.
304 /// @return The entry deleted. If not found, \c nullptr is returned. The entry will
305 /// still be a valid and accessible object within the mono allocator.
306 ALIB_DLL Entry* DeleteEntry ( Section* section, const String& name );
307
308 /// Deletes an entry. This overloaded searches the section by its name first.
309 /// @param sectionName The section.
310 /// @param name The name of the entry to be deleted.
311 /// @return The entry deleted. If not found, \c nullptr is returned. The entry will
312 /// still be a valid and accessible object within the monotonic allocator.
313 inline Entry* DeleteEntry ( const String& sectionName, const String& name ) {
314 auto* section= SearchSection( sectionName );
315 if( section == nullptr )
316 return nullptr;
317
318 return DeleteEntry( section, name );
319 }
320
321 /// Searches an entry with the given name. The search is performed case-insensitive.
322 /// @param section The name of the section
323 /// @param name The name of the entry to be searched.
324 /// @return The section and entry if found, a \e nulled pair if not found.
325 ALIB_DLL std::pair<Section*, Entry*> SearchEntry (const String& section, const String& name );
326
327 /// Parses the comments for newline tokens and adds given comment \p{prefix} to each line
328 /// (in case no known comment prefix is present, yet). Then storage is allocated and
329 /// output reference \p{dest} is set accordingly.
330 /// @param dest The destination string to set.
331 /// @param comments The comments to add.
332 /// @param prefix The prefix to use if no other prefixes were found.
333 /// Defaults to <c>"# "</c>.
334 ALIB_DLL void AddComments ( String& dest, const String& comments,
335 const String& prefix= A_CHAR("# ") );
336
337 //=========================================== Read/Write =========================================
338 public:
339 /// Reads an INI-File and adds its contents to the existing data.
340 /// In case only the new entries should be contained, use method #Reset to delete existing
341 /// data before invoking this function.
342 ///
343 /// It might happen that lines are ignored or otherwise marked as faulty. All numbers of such
344 /// lines get collected in field #LinesWithReadErrors.
345 ///
346 /// Any other i/o errors throws, with the exception of "file not found", which simply
347 /// returns \c -1.
348 ///
349 /// @param path The file to read and write.
350 /// @return \c -1 if the file does not exist. Otherwise the number of entries read.
351 ///
352 /// @throws Exception( \alib{variables;Exceptions;variables::Exceptions::ErrorOpeningFile} ).
353 /// In the case the module \alib_camp is not included in the \alibbuild, then a
354 /// <c>std::runtime_error("ErrorOpeningFile")</c> may be thrown.
356 integer Read( const system::CPathString& path );
357
358 /// Writes the data into the file.
359 /// @param path The file to write. If this is nulled, the default, then the
360 /// same file name as with the last #Read is used.
361 /// @throws Exception( \alib{variables;Exceptions;variables::Exceptions::ErrorOpeningFile} ).
362 /// In the case the module \alib_camp is not included in the \alibbuild, then a
363 /// <c>std::runtime_error("ErrorWritingFile")</c> may be thrown.
365 void Write(const system::PathString& path= system::NULL_PATH );
366};
367
368
369#if ALIB_CAMP
370//==================================================================================================
371/// Exception codes of namespace #alib::variables.
372/// \par Availability
373/// This enum type is only available if the module \alib_camp is included in the \alibbuild.
374//==================================================================================================
375enum class Exceptions
376{
377 /// File not found when reading.
379
380 /// An error occurred writing the file .
382};
383#endif
384
385} // namespace alib[::config]
386} // namespace [alib]
387
388#if ALIB_CAMP
391#endif
constexpr bool IsEmpty() const
Definition string.inl:365
std::size_t HashcodeIgnoreCase() const
bool Equals(const TString< TChar > &rhs) const
Definition string.inl:531
ALIB_DLL void Reset()
Clears all data, resets the internal mono allocator.
Definition inifile.cpp:75
ALIB_DLL Entry * DeleteEntry(Section *section, const String &name)
Definition inifile.cpp:109
ALIB_DLL Section * CreateSection(const String &name)
Definition inifile.cpp:84
ALIB_DLL void Write(const system::PathString &path=system::NULL_PATH)
Definition inifile.cpp:342
ALIB_DLL Entry * CreateEntry(Section *section, const String &name)
Definition inifile.cpp:121
ALIB_DLL void AddComments(String &dest, const String &comments, const String &prefix=A_CHAR("# "))
Definition inifile.cpp:159
ListMA< integer > LinesWithReadErrors
Definition inifile.inl:235
system::PathString FileName
The file name.
Definition inifile.inl:228
bool startsWithCommentSymbol(String &subs)
Definition inifile.cpp:57
HashMap< MonoAllocator, EntryKey, std::pair< Section *, Entry * >, EntryKey::Hash, EntryKey::EqualTo > entryTable
Definition inifile.inl:213
ALIB_DLL std::pair< Section *, bool > SearchOrCreateSection(const String &sectionName)
Definition inifile.cpp:151
ListMA< Section > Sections
The list of sections.
Definition inifile.inl:225
ALIB_DLL std::pair< Section *, Entry * > SearchEntry(const String &section, const String &name)
Definition inifile.cpp:130
ALIB_DLL integer Read(const system::CPathString &path)
Definition inifile.cpp:172
Entry * DeleteEntry(const String &sectionName, const String &name)
Definition inifile.inl:313
ALIB_DLL IniFile()
Default constructor.
Definition inifile.cpp:69
ALIB_DLL Section * DeleteSection(const String &name)
Definition inifile.cpp:90
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:139
IniFile(const system::Path &path)
Definition inifile.inl:245
String FileComments
The file header which will be written out as a comment lines with "# " prefixes.
Definition inifile.inl:231
#define ALIB_DLL
Definition alib.inl:503
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
Definition alib.inl:1063
#define ALIB_ENUMS_ASSIGN_RECORD(TEnum, TRecord)
#define ALIB_BOXING_VTABLE_DECLARE(TMapped, Identifier)
#define ALIB_EXPORT
Definition alib.inl:497
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:381
@ ErrorOpeningFile
File not found when reading.
Definition inifile.inl:378
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.inl:2271
containers::List< T, MonoAllocator, TRecycling > ListMA
Type alias in namespace alib.
Definition list.inl:693
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:2189
Equality functor for nodes hashed in field entryTable.
Definition inifile.inl:189
bool operator()(const EntryKey &lhs, const EntryKey &rhs) const
Definition inifile.inl:195
Hash functor for nodes hashed in field entryTable.
Definition inifile.inl:177
std::size_t operator()(const EntryKey &key) const
Definition inifile.inl:181
EntryKey(const String &sectionName, const String &entryName)
Definition inifile.inl:171
const String & EntryName
The name of the entry.
Definition inifile.inl:166
const String & SectionName
The name of the section.
Definition inifile.inl:165
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:155
Section(MonoAllocator &monoAllocator)
Definition inifile.inl:150
ListMA< Entry, Recycling::None > Entries
The list of variables of the section.
Definition inifile.inl:156
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.inl:157
String Name
The name of the section.
Definition inifile.inl:154