ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
inifile.hpp
Go to the documentation of this file.
1/** ************************************************************************************************
2 * \file
3 * This header file is part of module \alib_config of the \aliblong.
4 *
5 * \emoji :copyright: 2013-2024 A-Worx GmbH, Germany.
6 * Published under \ref mainpage_license "Boost Software License".
7 **************************************************************************************************/
8#ifndef HPP_ALIB_CONFIG_INI_FILE
9#define HPP_ALIB_CONFIG_INI_FILE 1
10
11#if !defined (HPP_ALIB_CONFIG_INMEMORY_PLUGIN)
13#endif
14#if !defined(HPP_ALIB_CAMP_MESSAGE_REPORT)
16#endif
17
19
20namespace alib { namespace config {
21/** ************************************************************************************************
22 * Specialization of class #InMemoryPlugin, which reads and writes a simple configuration file
23 * consisting of sections containing key/value pairs.
24 *
25 * This class is provided for the case that no other configuration mechanism is available.
26 * In general, application specific configuration mechanisms already exist in other libraries
27 * used. Those should be adopted by creating a lean interface plug-in for \alib.
28 *
29 * Some remarks on the functionality and supported format:
30 * - Comments
31 * - Comment lines at the beginning of the file are associated with the file and are written
32 * back. Such comment block is stopped with a single blank line.
33 * - Lines that start (apart from whitespace) with either a double
34 * slash \c "//", a sharp sign \c '#' or a semicolon <c>';'</c> are comment lines.
35 * - Comment lines that are added to variables in the software are using the symbol defined in
36 * filed #DefaultCommentPrefix, which defaults to \c '#'. If this is changed in the file,
37 * such changes are preserved.
38 * - Comment lines before sections and variables are associated with the respective objects
39 * and are not overwritten by comments set in the code. However, variables without
40 * comments which are overwritten in code including comments, get such comment appended.
41 * - Comments can not reside in the same line together with section names or variables.
42 * - Commented variables receive a blank line before the comment on writing.
43 * - Commented Sections receive two blank lines before the comment on writing.
44 * One if they are not commented.
45 * - Before a file is written, new sections (those that have been created during the use of
46 * the object) can be identified by a \e nulled comment variable, while the string is
47 * not \e nulled (but maybe empty) when read from the file. This information may be used
48 * to add comments to new sections, prior to writing the file, while preventing to restore
49 * comments that have been deleted by the user.
50 *
51 * - Sections:
52 * - Sections names are enclosed by brackets \c '[' and \c ']'.
53 * - Section names can be repeated. In this case the corresponding section is continued.
54 * When the file is written, the sections are merged. Otherwise the order of sections and
55 * the variables within the section is kept intact on writing.
56 *
57 * - Variables
58 * - Variable names and their values are separated by an equal sign \c '='.
59 * - New variables inserted are formatted according to other variables found. E.g. the
60 * equal sign of all variables within a section are aligned on the same column.
61 * - Formats of variables added or changed by the user are kept intact, as long as the
62 * software does not store a value.
63 *
64 * - Continued Lines:
65 * - Variables definition are being continued (values are concatenated) if the line ends
66 * with a backslash \c '\\'.
67 * - Comment lines in-between continued lines are recognized as such. To continue a variable
68 * after a 'continued' comment line, the comment line needs to end with a backslash \c '\\'.
69 * - Variables with multiple values created (or modified) in the software, are written
70 * in continued lines, with each line showing one value, ending with the variables'
71 * delimiter character and a trailing \c '\\'.
72 * - Comment lines within Variables defined in multiple lines are removed when a variable
73 * is written back.
74 *
75 * - Escaping values
76 * - Spaces <c>' '</c> and tabulators \c '\\t' are ignored at the start and end of each line and before
77 * and after the equal sign \c '='.
78 * - Consequently, whitespaces at the start or end of a value either need to be escaped
79 * using <c>'\\ '</c> or the whole value has to be surrounded by double quotes <c>\"</c>.
80 * - Values added or modified by the software that contain spaces at the start or end
81 * are surrounded by double quotes (instead of escaping them)
82 * - Double quotes in values are always escaped when writing values and have to be escaped
83 * when editing the file.
84 * - Values may consist of a list of double quoted values. Whitespaces between such
85 * values are ignored. Consequently, long strings may be enclosed in double quotes
86 * and continued in the next line when the line ends with a backslash \c '\\'.
87 * - Almost any character can be escaped. E.g <c>"\\a"</c> is read as \c 'a'.
88 * - On writing only non-printable characters and double quotation marks are escaped.
89 *
90 * - Other remarks
91 * - Sequences of blank lines are reduced to one blank line, when writing the file.
92 * - Erroneous lines are ignored and not written back. Line numbers with erroneous lines
93 * are collected in field #LinesWithReadErrors.
94 *
95\I{################################################################################################}
96 * # Reference Documentation #
97 * @throws alib::config::Exceptions::ErrorOpeningFile
98 * @throws alib::config::Exceptions::ErrorWritingFile
99 **************************************************************************************************/
100 class IniFile : public InMemoryPlugin
101{
102 // #############################################################################################
103 // Public fields
104 // #############################################################################################
105 public:
106
107 /** If this is set to \c true, any variable change will lead to writing the file immediately
108 by invoking #WriteFile. Defaults to false */
109 bool AutoSave = false;
110
111 /** The standard file extension used for \alib configuration files.
112 * Defaults to <em>".ini"</em>. */
114
115 /** The file name. This might include a path or not. Should be set properly before
116 the file is read. */
118
119 /** The file header which will be written out as a comment lines with "# " prefixes */
121
122 /** The desired maximum width of the INI-file. Defaults to <c>100</c>.
123 * This value is used with utility method #AddResourcedSectionComments */
124 int LineWidth = 100;
125
126 /** Is cleared and filled with faulty line numbers when reading the file. (E.g. when a
127 line is no section and no comment but still has no equal sign ('='). */
129
130 /**
131 * The prefix that is used for comment lines of sections or variables that have been
132 * added 'in code' (variables that have not been read from the file).
133 * Comments that were read from the file preserve their prefix.
134 * If comments including one of the valid prefixes are added to a variable or section
135 * 'in code', such prefix is preserved. */
137
138 /** Denotes if a space should be written before a delimiter. */
140
141 /** Denotes if a space should be written after a delimiter. (Applies only to single
142 line mode of writing attributes.) */
144
145 /** Denotes whether the spaces that are inserted when aligning attributes are
146 * located before or behind the delimiter. */
148
149 // #############################################################################################
150 // Constructor/destructor
151 // #############################################################################################
152 public:
153 /** ****************************************************************************************
154 * Constructs an instance of this class and reads the file.
155 * If no file name is given, the file name is set to the process name with extension
156 * found in public static field #DefaultFileExtension.
157 *
158 * If the given file name equals <c>'*'</c>, no file is read and field #AutoSave is set
159 * to \c false.
160 *
161 * If the given name does not start with either a path separation character or a
162 * dot character <c>.</c>, then \alib{lang::basecamp;Directory::SpecialFolder::HomeConfig} is
163 * prepended to the given name.
164 *
165 * @param filePathAndName The name (and path) of the file to read and write.
166 * Provide "*" to suppress reading a file.
167 * Defaults to nullptr.
168 ******************************************************************************************/
169 ALIB_API IniFile( const String& filePathAndName= nullptr );
170
171 /** ****************************************************************************************
172 * Virtual Destructor.
173 ******************************************************************************************/
174 virtual ~IniFile() override
175 {
176 #if ALIB_DEBUG
177 for( auto& section : sections )
178 if( section.Name().IsNotEmpty() && section.Comments.IsNull() )
179 ALIB_WARNING( "CONFIG",
180 "Hint: New section {!Q} , which was programatically added to\n"
181 " INI-file {!Q}, has no comments.\n "
182 " The use of method IniFile::AddResourcedSectionComments() is recommended.",
183 section.Name(), FileName )
184 #endif
185 }
186
187 // #############################################################################################
188 // Specific (new) Interface
189 // #############################################################################################
190 /** ****************************************************************************************
191 * Clears all configuration data and reads the file. It might happen that lines are
192 * ignored or otherwise marked as faulty. All numbers of such lines get collected in
193 * field LinesWithReadErrors.
194 * @throws Exception( \alib{config;Exceptions;config::Exceptions::ErrorOpeningFile} ).
195 ******************************************************************************************/
197 void ReadFile();
198
199 /** ****************************************************************************************
200 * Write all configuration data into the file.
201 * @throws Exception( \alib{config;Exceptions;config::Exceptions::ErrorOpeningFile} ).
202 ******************************************************************************************/
204 void WriteFile();
205
206 /** ****************************************************************************************
207 * This is a static utility function that reads section comments from
208 * \ref alib_basecamp_resources "externalized string resources".
209 *
210 * All sections of all INI-files of given \p{config} are processed, but resourced comments
211 * are only added in the case that a section's comment string is \c nulled. This is not the
212 * case if a section was read from from an INI-file, as even if no comments are given,
213 * the field is empty, but not \e nulled. In other words, only sections that have been
214 * programatically added during the run of a software are changed<br>
215 * This approach allows a user to remove the comments, without the software restoring them.
216 *
217 * The resource names are assembled from given \p{resourceNamePrefix} and the section
218 * name.
219 * The resource strings found are processed using method
220 * \alib{lang::format;Paragraphs.AddMarked}. This allows to use text macros like <b>'\@HL'</b>
221 * to format the text.
222 *
223 * This method is best be invoked in phase \alib{ShutdownPhases::Announce} of
224 * method \alib{lang;Camp::shutdown}.
225 *
226 * \see
227 * Field #LineWidth, which is respected when formatting comment lines.
228 * @param config The configuration that is searched for INI-file plug-ins.
229 * @param resourcePool The resource pool to use.
230 * @param resourceCategory The category of the resourced comments.
231 * @param resourceNamePrefix A prefix of the resource name.
232 ******************************************************************************************/
233 ALIB_API static
235 ResourcePool& resourcePool,
236 const NString& resourceCategory,
237 const NString& resourceNamePrefix );
238
239 // #############################################################################################
240 // ConfigurationPlugin overrides
241 // #############################################################################################
242 public:
245
246 /** ****************************************************************************************
247 * Return the plug-in name, in this case, the file name.
248 * @return The name of the plug-in.
249 ******************************************************************************************/
250 virtual String Name() const override
251 {
252 return FileName;
253 }
254
255 /** ****************************************************************************************
256 * Creates or replaces existing variable in our storage. If #AutoSave is set, the file
257 * is written
258 *
259 * @param variable The variable to retrieve.
260 * @return \c true if the variable was written, \c false if not. The latter might only
261 * happen if the variable given was illegal, e.g. empty name.
262 ******************************************************************************************/
263 virtual bool Store( Variable& variable ) override
264 {
265 InMemoryPlugin::Store( variable );
266 if ( AutoSave )
267 WriteFile();
268 return true;
269 }
270
271 // #############################################################################################
272 // InMemoryPlugin overrides
273 // #############################################################################################
274 /** ****************************************************************************************
275 * Clears all configuration data.
276 ******************************************************************************************/
278 virtual void Clear() override;
279
280 /**
281 * Overrides default method. If we have not parsed the INI file's text value, yet,
282 * we do this now.
283 *
284 * @param entry The entry to convert.
285 * @param variable The variable to fill with our values.
286 */
288 virtual void ToVariable( Entry& entry, Variable& variable ) const override;
289
290 /**
291 * Overrides default method. Clears the raw value, and calls base method.
292 *
293 * @param entry The entry to convert.
294 * @param variable The variable to fill with our values.
295 */
297 virtual void FromVariable( Entry& entry, Variable& variable ) const override;
298
299 protected:
300 // #############################################################################################
301 // Protected methods
302 // #############################################################################################
303 /** ****************************************************************************************
304 * Writes a list of comments to the file. Comment lines are started with '#'.
305 * @param os The encapsulated output stream to write to.
306 * @param comments The comment lines for the section.
307 ******************************************************************************************/
310 const AString& comments );
311
312};
313
314
315} // namespace alib[::onfig]
316
317/// Type alias in namespace \b alib.
319
320} // namespace [alib]
321
322#endif // HPP_ALIB_CONFIG_INI_FILE
virtual ALIB_API bool Load(Variable &variable, bool searchOnly=false) override
virtual ALIB_API bool Store(Variable &variable) override
virtual ALIB_API void ToVariable(Entry &entry, Variable &variable) const override
Definition inifile.cpp:60
ALIB_API void WriteFile()
Definition inifile.cpp:400
static String DefaultFileExtension
Definition inifile.hpp:113
ALIB_API void ReadFile()
Definition inifile.cpp:225
virtual ~IniFile() override
Definition inifile.hpp:174
virtual ALIB_API void FromVariable(Entry &entry, Variable &variable) const override
Definition inifile.cpp:151
static ALIB_API void AddResourcedSectionComments(Configuration &config, ResourcePool &resourcePool, const NString &resourceCategory, const NString &resourceNamePrefix)
Definition inifile.cpp:616
ALIB_API void writeComments(strings::compatibility::std::StringWriter &os, const AString &comments)
Definition inifile.cpp:363
virtual String Name() const override
Definition inifile.hpp:250
virtual ALIB_API void Clear() override
Definition inifile.cpp:203
virtual bool Store(Variable &variable) override
Definition inifile.hpp:263
bool FormatIncludeDelimInAttrAlignment
Definition inifile.hpp:147
List< integer > LinesWithReadErrors
Definition inifile.hpp:128
#define ALIB_WARNING(...)
Definition alib.hpp:981
#define A_CHAR(STR)
#define ALIB_API
Definition alib.hpp:538
Definition alib.cpp:57
strings::compatibility::std::StringWriter StringWriter
Type alias in namespace alib.
config::IniFile IniFile
Type alias in namespace alib.
Definition inifile.hpp:318