ALib C++ Framework
by
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 #"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" 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//==================================================================================================
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 #"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 be ignored on writing.
137 void* Custom = nullptr; ///< May be used by freely by customers of this class.
138 ///< Initialized with \c nullptr, but otherwise not touched.
139 int LineNo = -1; ///< If given, the line number in the source file.
140 bool WriteBack= false; ///< If given, a write back indicator was found for this entry.
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 /// #"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 ListMA<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 /// A pair of pointers to a section and an entry in the section.
162 struct Handle {
163 Section* SectionPointer; ///< Pointer to the INI-file's section.
164 Entry* EntryPointer; ///< Pointer to the entry in the #"SectionPointer".
165 };
166
167 //======================== Subtypes for Sections, Entries and the Hashtable ======================
168 protected:
169 /// Hash functor for nodes hashed in field #"entryTable". Ignores letter case.
170 struct EntryKey
171 {
172 const String& SectionName; ///< The name of the section.
173 const String& EntryName; ///< The name of the entry.
174
175 /// Constructor.
176 /// @param sectionName The section's name of an entry.
177 /// @param entryName The name of an entry.
178 EntryKey( const String& sectionName, const String& entryName )
179 : SectionName( sectionName)
180 , EntryName ( entryName ) {}
181
182 /// Hash functor for nodes hashed in field #"entryTable".
183 struct Hash
184 {
185 /// Calculates a hash code for \b NodeKey objects.
186 /// @param key The key to hash.
187 /// @return The hash code.
188 std::size_t operator()(const EntryKey& key) const {
191 }
192 };
193
194 /// Equality functor for nodes hashed in field #"entryTable".
195 struct EqualTo
196 {
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 system::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 system::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 system::PathString& path= system::NULL_PATH );
370};
371
372
373#if ALIB_CAMP
374//==================================================================================================
375/// Exception codes of namespace #"alib::variables;2".
376/// \par Availability
377/// This enum type is only available if the module \alib_camp is included in the \alibbuild.
378//==================================================================================================
379enum class Exceptions
380{
381 /// File not found when reading.
383
384 /// An error occurred writing the file .
386};
387#endif
388
389} // namespace alib[::config]
390} // namespace [alib]
391
392#if ALIB_CAMP
395#endif
#define ALIB_DLL
Definition alib.inl:573
#define A_CHAR(STR)
Definition alib.inl:1325
#define ALIB_WARNING(domain,...)
Definition alib.inl:1141
#define ALIB_EXPORT
Definition alib.inl:562
#define ALIB_BOXING_VTABLE_DECLARE(TMapped, Identifier)
constexpr bool IsEmpty() const
Definition string.inl:353
std::size_t HashcodeIgnoreCase() const
bool Equals(const TString< TChar > &rhs) const
Definition string.inl:519
void Reset()
Clears all data, resets the internal mono allocator.
Definition inifile.cpp:75
Entry * DeleteEntry(Section *section, const String &name)
Definition inifile.cpp:109
Section * CreateSection(const String &name)
Definition inifile.cpp:84
void Write(const system::PathString &path=system::NULL_PATH)
Definition inifile.cpp:346
Entry * CreateEntry(Section *section, const String &name)
Definition inifile.cpp:121
void AddComments(String &dest, const String &comments, const String &prefix=A_CHAR("# "))
Definition inifile.cpp:159
HashMap< MonoAllocator, EntryKey, Handle, EntryKey::Hash, EntryKey::EqualTo > entryTable
Definition inifile.inl:220
ListMA< integer > LinesWithReadErrors
Definition inifile.inl:242
system::PathString FileName
The file name.
Definition inifile.inl:235
bool startsWithCommentSymbol(String &subs)
Definition inifile.cpp:57
Handle SearchEntry(const String &section, const String &name)
Definition inifile.cpp:130
int Read(const system::CPathString &path)
Definition inifile.cpp:172
std::pair< Section *, bool > SearchOrCreateSection(const String &sectionName)
Definition inifile.cpp:151
ListMA< Section > Sections
The list of sections.
Definition inifile.inl:232
Entry * DeleteEntry(const String &sectionName, const String &name)
Definition inifile.inl:320
IniFile()
Default constructor.
Definition inifile.cpp:69
Section * DeleteSection(const String &name)
Definition inifile.cpp:90
MonoAllocator Allocator
A monotonic allocator used for allocating sections and entries.
Definition inifile.inl:122
Section * SearchSection(const String &sectionName)
Definition inifile.cpp:139
IniFile(const system::Path &path)
Definition inifile.inl:252
String FileComments
The file header which will be written out as comment lines with "# " prefixes.
Definition inifile.inl:238
#define ALIB_ENUMS_ASSIGN_RECORD(TEnum, TRecord)
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:385
@ ErrorOpeningFile
File not found when reading.
Definition inifile.inl:382
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
constexpr String NULL_STRING
A nulled string of the default character type.
Definition string.inl:2254
containers::List< T, MonoAllocator, TRecycling > ListMA
Type alias in namespace alib.
Definition list.inl:697
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.
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2172
Equality functor for nodes hashed in field #"entryTable".
Definition inifile.inl:196
bool operator()(const EntryKey &lhs, const EntryKey &rhs) const
Definition inifile.inl:202
Hash functor for nodes hashed in field #"entryTable".
Definition inifile.inl:184
std::size_t operator()(const EntryKey &key) const
Definition inifile.inl:188
EntryKey(const String &sectionName, const String &entryName)
Definition inifile.inl:178
const String & EntryName
The name of the entry.
Definition inifile.inl:173
const String & SectionName
The name of the section.
Definition inifile.inl:172
An entry in a #"IniFile::Section".
Definition inifile.inl:128
String NewValue
If this value is set, #"RawValue" will be 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
int LineNo
If given, the line number in the source file.
Definition inifile.inl:139
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.inl:140
A pair of pointers to a section and an entry in the section.
Definition inifile.inl:162
Entry * EntryPointer
Pointer to the entry in the #"SectionPointer".
Definition inifile.inl:164
Section * SectionPointer
Pointer to the INI-file's section.
Definition inifile.inl:163
A section of the INI-file.
Definition inifile.inl:145
String Comments
The comment lines of the section.
Definition inifile.inl:156
Section(MonoAllocator &monoAllocator)
Definition inifile.inl:151
ListMA< Entry, Recycling::None > Entries
The list of variables of the section.
Definition inifile.inl:157
bool WriteBack
If given, a write back indicator was found for this entry.
Definition inifile.inl:158
String Name
The name of the section.
Definition inifile.inl:155