ALib C++ Framework
by
Library Version: 2605 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_variables of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_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 #"IniFileFeeder" provides all mechanics to easily use
16/// INI-files. For this reason, there is no #"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 #"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 #"IniFileFeeder".
57/// Hence, the direct use of this class is not recommended, instead use the techniques described
58/// in chapter #"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/// - Write-back 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 #"Configuration"
88/// and associated type #"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 #"BASECAMP;2" 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 #"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//==================================================================================================
117class IniFile {
118 //======================================== Public Allocator ======================================
119 public:
120 /// A monotonic allocator used for allocating sections and entries.
122
123 //======================================= Entry and Section ======================================
124 /// An entry in a #"IniFile::Section".
125 struct Entry {
126 String Name = NULL_STRING; ///< The entry's name.
127 String Comments = NULL_STRING; ///< The entry's comments.
128 String RawValue = NULL_STRING; ///< The 'raw' value, which is everything after the
129 ///< variable name, including the equal sign <c>'='</c>.
130 String Value = NULL_STRING; ///< The trimmed value. Multiline values are likewise
131 ///< trimmed and backslashes and line feeds are removed
132 ///< <c>'\</c>.
133 ///< This value is to be used for reading a variable's
134 ///< content.
135 String NewValue = NULL_STRING; ///< If this value is set, #"RawValue" will be ignored on
136 ///< writing.
137 void* Custom = nullptr; ///< May be used by freely by customers of this class.
138 ///< Initialized with \c nullptr, but otherwise not
139 ///< touched.
140 int LineNo = -1; ///< If given, the line number in the source file.
141 int FirstLineNo= -1; ///< Set by the method ".Read" in case the entry was given
142 ///< more than once.
143 bool WriteBack = false; ///< If given, a write back indicator was found for this
144 ///< entry.
145 };
146
147 /// A section of the INI-file.
148 struct Section {
150 protected:
151 /// Constructor. Protected and thus to be used only by friend class
152 /// #"List".
153 /// @param monoAllocator The allocator of the #"%IniFile".
154 Section( MonoAllocator& monoAllocator )
155 : Entries(monoAllocator) {}
156
157 public:
158 String Name = NULL_STRING; ///< The name of the section.
159 String Comments= NULL_STRING; ///< The comment lines of the section.
160 ListMA<Entry, Recycling::None> Entries; ///< The list of variables of the section.
161 bool WriteBack= false; ///< If given, a write back indicator was found for this entry.
162 };
163
164 /// A pair of pointers to a section and an entry in the section.
165 struct Handle {
166 Section* SectionPointer; ///< Pointer to the INI-file's section.
167 Entry* EntryPointer; ///< Pointer to the entry in the #"SectionPointer".
168 };
169
170 //======================== Subtypes for Sections, Entries and the Hashtable ======================
171 protected:
172 /// Hash functor for nodes hashed in field #"entryTable". Ignores letter case.
173 struct EntryKey {
174 const String& SectionName; ///< The name of the section.
175 const String& EntryName; ///< The name of the entry.
176
177 /// Constructor.
178 /// @param sectionName The section's name of an entry.
179 /// @param entryName The name of an entry.
180 EntryKey( const String& sectionName, const String& entryName )
181 : SectionName( sectionName)
182 , EntryName ( entryName ) {}
183
184 /// Hash functor for nodes hashed in field #"entryTable".
185 struct Hash {
186 /// Calculates a hash code for #"%NodeKey" objects.
187 /// @param key The key to hash.
188 /// @return The hash code.
189 std::size_t operator()(const EntryKey& key) const {
192 }
193 };
194
195 /// Equality functor for nodes hashed in field #"entryTable".
196 struct EqualTo {
197 /// Invokes #"TString::Equals;String::Equals" on \p{lhs}, passing \p{rhs}
198 /// and returns the result.
199 /// @param lhs The first string object.
200 /// @param rhs The second string object.
201 /// @return The result of the string comparison.
202 bool operator()(const EntryKey& lhs, const EntryKey& rhs ) const {
203 return ( (lhs.SectionName.IsEmpty() && rhs.SectionName.IsEmpty() )
205 && ( (lhs.EntryName .IsEmpty() && rhs.EntryName .IsEmpty() )
207 }
208 };
209 };
210
211 //======================================== Other internals =======================================
212 protected:
213 /// The entry hash set.
214 /// This is used to find entries by section name and entry name.
215 /// The set contains all entries of all sections.
217 EntryKey,
218 Handle,
219 EntryKey::Hash,
221
222
223 /// Tests if the given string starts with <em>'#'</em>, <em>';'</em>, or <em>'//'</em>.
224 /// @param subs The string to test.
225 /// @return \c true if this is a comment line, \c false otherwise.
226 bool startsWithCommentSymbol( String& subs );
227
228
229 //====================================== Other Public fields =====================================
230 public:
231 /// The list of sections.
233
234 /// The file name.
236
237 /// The file header which will be written out as comment lines with <c>"# "</c> prefixes.
239
240 /// Filled with faulty line numbers when reading the file. (E.g., when a line is no section,
241 /// no comment and not the attribute "writeback", but still has no equal sign ('=').
243
244
245 //===================================== Constructor/Destructor ===================================
246 public:
247 /// Default constructor.
249
250 /// Constructs an instance of this class and reads the file specified with \p{path}.
251 /// @param path The filepath to the INI-file.
252 inline IniFile( const Path& path )
253 : IniFile() { Read( path ); }
254
255 /// Destructor.
257 #if ALIB_DEBUG
258 for( auto& section : Sections )
259 if( section.Name.IsNotEmpty() && section.Comments.IsNull() )
260 ALIB_WARNING( "VARIABLES",
261 "Hint: New section \"{}\", which was programmatically added to\n"
262 " INI-file \"{}\", has no comments.",
263 section.Name, FileName )
264 #endif
265 }
266
267
268 //=========================================== Interface ==========================================
269
270 /// Clears all data, resets the internal mono allocator.
271 ALIB_DLL void Reset();
272
273 /// Counts the number of entries over all sections.
274 /// @return The number of "variables" in this INI-file.
275 inline integer Count() { return entryTable.Size(); }
276
277 /// Appends a new section to the end of the list of sections.
278 /// Must be invoked only if a section with the same name does not exist, yet.
279 /// @see Method SearchOrCreateSection.
280 /// @param name The name of the section.
281 /// @return The created section.
282 ALIB_DLL Section* CreateSection( const String& name);
283
284 /// Deletes a section.
285 /// @param name The name of the section to be deleted.
286 /// @return The deleted section. If not found, \c nullptr is returned. The section will
287 /// still be a valid and accessible object within the mono allocator.
288 ALIB_DLL Section* DeleteSection( const String& name);
289
290 /// Searches the section with the given name.
291 /// @param sectionName The name of the section to be retrieved.
292 /// @return Returns the section if it was found, nullptr otherwise.
293 ALIB_DLL Section* SearchSection( const String& sectionName );
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_DLL std::pair<Section*, bool> SearchOrCreateSection( const String& sectionName );
299
300 /// Creates a new entry.
301 /// Must be invoked only if the entry does not exist, yet. The given entry name is copied to
302 /// the internal allocator.
303 /// @param section The section.
304 /// @param name The name of the entry to be created.
305 /// @return The entry found or created.
306 ALIB_DLL Entry* CreateEntry ( Section* section, const String& name );
307
308 /// Deletes an entry.
309 /// @param section The section of the entry.
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 mono allocator.
313 ALIB_DLL Entry* DeleteEntry ( Section* section, const String& name );
314
315 /// Deletes an entry. This overloaded searches the section by its name first.
316 /// @param sectionName The section.
317 /// @param name The name of the entry to be deleted.
318 /// @return The entry deleted. If not found, \c nullptr is returned. The entry will
319 /// still be a valid and accessible object within the monotonic allocator.
320 inline Entry* DeleteEntry ( const String& sectionName, const String& name ) {
321 auto* section= SearchSection( sectionName );
322 if( section == nullptr )
323 return nullptr;
324
325 return DeleteEntry( section, name );
326 }
327
328 /// Searches an entry with the given name. The search is performed case-insensitive.
329 /// @param section The name of the section
330 /// @param name The name of the entry to be searched.
331 /// @return The section and entry if found, a \e nulled pair if not found.
332 ALIB_DLL Handle SearchEntry (const String& section, const String& name );
333
334 /// Parses the comments for newline tokens and adds given comment \p{prefix} to each line
335 /// (in case no known comment prefix is present, yet). Then storage is allocated and
336 /// output reference \p{dest} is set accordingly.
337 /// @param dest The destination string to set.
338 /// @param comments The comments to add.
339 /// @param prefix The prefix to use if no other prefixes were found.
340 /// Defaults to <c>"# "</c>.
341 ALIB_DLL void AddComments ( String& dest, const String& comments,
342 const String& prefix= A_CHAR("# ") );
343
344 //=========================================== Read/Write =========================================
345 public:
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 /// @param path The file to read and write.
354 /// @return The number of entries read.
355 ///
356 /// @throws Exception( #"Exceptions::ErrorOpeningFile" ).
357 /// In the case the module \alib_camp is not included in the \alibbuild, then the
358 /// original exceptions of <c>std::ifstream</c> are thrown.
360 int Read( const CPathString& path );
361
362 /// Writes the data into the file.
363 /// @param path The file to write. If this is nulled, the default, then the
364 /// same file name as with the last #".Read" is used.
365 /// @throws Exception( #"Exceptions::ErrorOpeningFile" ).
366 /// In the case the module \alib_camp is not included in the \alibbuild, then the
367 /// original exceptions of <c>std::ifstream</c> are thrown.
369 void Write(const PathString& path= NULL_PATH );
370};
371
372
373} // namespace alib[::config]
374} // namespace [alib]
375
376#if ALIB_CAMP
379#endif
#define ALIB_DLL
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
#define ALIB_EXPORT
#define ALIB_BOXING_VTABLE_DECLARE(TMapped, Identifier)
constexpr bool IsEmpty() const
Definition string.hpp:349
std::size_t HashcodeIgnoreCase() const
bool Equals(const TString< TChar > &rhs) const
Definition string.hpp:515
void Reset()
Clears all data, resets the internal mono allocator.
Definition inifile.cpp:30
Entry * DeleteEntry(Section *section, const String &name)
Definition inifile.cpp:64
Section * CreateSection(const String &name)
Definition inifile.cpp:39
Entry * CreateEntry(Section *section, const String &name)
Definition inifile.cpp:76
void AddComments(String &dest, const String &comments, const String &prefix=A_CHAR("# "))
Definition inifile.cpp:114
HashMap< MonoAllocator, EntryKey, Handle, EntryKey::Hash, EntryKey::EqualTo > entryTable
Definition inifile.hpp:220
ListMA< integer > LinesWithReadErrors
Definition inifile.hpp:242
bool startsWithCommentSymbol(String &subs)
Definition inifile.cpp:12
Handle SearchEntry(const String &section, const String &name)
Definition inifile.cpp:85
std::pair< Section *, bool > SearchOrCreateSection(const String &sectionName)
Definition inifile.cpp:106
ListMA< Section > Sections
The list of sections.
Definition inifile.hpp:232
PathString FileName
The file name.
Definition inifile.hpp:235
IniFile(const Path &path)
Definition inifile.hpp:252
Entry * DeleteEntry(const String &sectionName, const String &name)
Definition inifile.hpp:320
IniFile()
Default constructor.
Definition inifile.cpp:24
int Read(const CPathString &path)
Definition inifile.cpp:127
Section * DeleteSection(const String &name)
Definition inifile.cpp:45
void Write(const PathString &path=NULL_PATH)
Definition inifile.cpp:293
MonoAllocator Allocator
A monotonic allocator used for allocating sections and entries.
Definition inifile.hpp:121
Section * SearchSection(const String &sectionName)
Definition inifile.cpp:94
String FileComments
The file header which will be written out as comment lines with "# " prefixes.
Definition inifile.hpp:238
#define ALIB_ENUMS_ASSIGN_RECORD(TEnum, TRecord)
Exceptions
Exception codes of namespace #"alib::variables;2".
Definition vmeta.hpp:17
Definition alox.cpp:14
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.hpp:2247
containers::List< T, MonoAllocator, TRecycling > ListMA
Type alias in namespace #"%alib".
Definition list.hpp:689
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
containers::HashMap< TAllocator, TKey, TMapped, THash, TEqual, THashCaching, TRecycling > HashMap
Type alias in namespace #"%alib".
strings::TString< character > String
Type alias in namespace #"%alib".
Definition string.hpp:2165
system::Path Path
Type alias in namespace #"%alib".
Definition path.hpp:417
strings::TCString< PathCharType > CPathString
The string-type used with this ALib Module.
Definition path.hpp:37
constexpr PathString NULL_PATH
A nulled path string.
Definition path.hpp:52
strings::TString< PathCharType > PathString
The string-type used with this ALib Module.
Definition path.hpp:34
Equality functor for nodes hashed in field #"entryTable".
Definition inifile.hpp:196
bool operator()(const EntryKey &lhs, const EntryKey &rhs) const
Definition inifile.hpp:202
Hash functor for nodes hashed in field #"entryTable".
Definition inifile.hpp:185
std::size_t operator()(const EntryKey &key) const
Definition inifile.hpp:189
EntryKey(const String &sectionName, const String &entryName)
Definition inifile.hpp:180
const String & EntryName
The name of the entry.
Definition inifile.hpp:175
const String & SectionName
The name of the section.
Definition inifile.hpp:174
An entry in a #"IniFile::Section".
Definition inifile.hpp:125
String Name
The entry's name.
Definition inifile.hpp:126
String Comments
The entry's comments.
Definition inifile.hpp:127
int LineNo
If given, the line number in the source file.
Definition inifile.hpp:140
A pair of pointers to a section and an entry in the section.
Definition inifile.hpp:165
Entry * EntryPointer
Pointer to the entry in the #"SectionPointer".
Definition inifile.hpp:167
Section * SectionPointer
Pointer to the INI-file's section.
Definition inifile.hpp:166
A section of the INI-file.
Definition inifile.hpp:148
String Comments
The comment lines of the section.
Definition inifile.hpp:159
Section(MonoAllocator &monoAllocator)
Definition inifile.hpp:154
ListMA< Entry, Recycling::None > Entries
The list of variables of the section.
Definition inifile.hpp:160
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.hpp:161
String Name
The name of the section.
Definition inifile.hpp:158