Line data Source code
1 : // Copyright (c) 2006-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/advgetopt
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Advanced getopt implementation.
22 : *
23 : * The advgetopt class and implementation is an advanced library to parse
24 : * command line parameters from static definitions specified by the caller.
25 : *
26 : * The class supports the command line options, options found in a default
27 : * configuration file or in a user defined configuration file.
28 : *
29 : * The class also includes support for displaying error messages and help
30 : * information about all the command line arguments.
31 : */
32 :
33 : // self
34 : //
35 : #include "advgetopt/variables.h"
36 :
37 :
38 : // C++ libd
39 : //
40 : #include <iostream>
41 :
42 :
43 : // last include
44 : //
45 : #include <snapdev/poison.h>
46 :
47 :
48 :
49 : namespace advgetopt
50 : {
51 :
52 :
53 :
54 : /** \brief Canonicalize the variable name.
55 : *
56 : * This function canonicalizes the name of a variable.
57 : *
58 : * This means:
59 : *
60 : * * Replace a sequence of ':' with '::'.
61 : * * Replace a sequence of '.' with '::'.
62 : * * Replace a '_' with '-'.
63 : *
64 : * \note
65 : * Variable names that come from a configuration file will already have
66 : * been canonicalized, but the user can directly call the get_variable()
67 : * and set_variable() functions which will also benefit from this
68 : * conversion.
69 : *
70 : * \todo
71 : * Check that all section names start with a letter (not a digit).
72 : *
73 : * \param[in] name The name of the variable.
74 : */
75 43 : std::string variables::canonicalize_variable_name(std::string const & name)
76 : {
77 43 : std::string result;
78 :
79 255 : for(char const * n(name.c_str()); *n != '\0'; ++n)
80 : {
81 212 : if(*n == ':' || *n == '.')
82 : {
83 0 : while(*n == ':' || *n == '.')
84 : {
85 0 : ++n;
86 : }
87 0 : result += "::";
88 : }
89 212 : else if(*n == '_')
90 : {
91 2 : result += '-';
92 : }
93 : else
94 : {
95 210 : result += *n;
96 : }
97 : }
98 :
99 43 : return result;
100 : }
101 :
102 :
103 : /** \brief Check whether a variable is defined.
104 : *
105 : * If you want to verify that a variable is defined before retrieving
106 : * it, you can use this function. A variable can be set to the empty
107 : * string so checking the returned value of the get_variable() is
108 : * not sufficient to know whether the variable is defined or log.
109 : *
110 : * \param[in] name The name of the variable to check.
111 : *
112 : * \return true if the variable is defined.
113 : */
114 0 : bool variables::has_variable(std::string const & name) const
115 : {
116 0 : auto it(f_variables.find(canonicalize_variable_name(name)));
117 0 : return it != f_variables.end();
118 : }
119 :
120 :
121 : /** \brief Return the value of the named variable.
122 : *
123 : * This function searches for the named variable and returns its value
124 : * if defined.
125 : *
126 : * \param[in] name The name of the variable to retrieve.
127 : *
128 : * \return The variable value of an empty string.
129 : */
130 13 : std::string variables::get_variable(std::string const & name) const
131 : {
132 13 : auto it(f_variables.find(canonicalize_variable_name(name)));
133 13 : if(it != f_variables.end())
134 : {
135 13 : return it->second;
136 : }
137 :
138 0 : return std::string();
139 : }
140 :
141 :
142 : /** \brief Return a reference to the map of variables.
143 : *
144 : * This function returns a reference to the whole map of variables.
145 : *
146 : * The map is composed of named values. The first string is the name of
147 : * variables and the second string is the value.
148 : *
149 : * \note
150 : * It is not multi-thread safe since the variable make can be updated at any
151 : * time.
152 : *
153 : * \return The reference to the map of variables.
154 : */
155 0 : variables::variable_t const & variables::get_variables() const
156 : {
157 0 : return f_variables;
158 : }
159 :
160 :
161 : /** \brief Set a variable.
162 : *
163 : * This function sets a variable in the getopt object.
164 : *
165 : * The value of variables can be used to replace `${...}` entries in
166 : * parameters found on the command line or in configuration files.
167 : *
168 : * By default, if that variable already existed, then its value gets
169 : * replaced.
170 : *
171 : * You can use this function to define a default as in:
172 : *
173 : * \code
174 : * opt->set_variable("foo", "default value", false);
175 : * \endcode
176 : *
177 : * \note
178 : * The value of a variable can itself include `${...}` references.
179 : * When parsing a parameter for variables, such are replaced recursively.
180 : * See the process_value() for details.
181 : *
182 : * \param[in] name The name of the variable.
183 : * \param[in] value The value of the variable.
184 : * \param[in] overwrite Whether to overwrite the value if it already exists.
185 : */
186 15 : void variables::set_variable(
187 : std::string const & name
188 : , std::string const & value
189 : , bool overwrite)
190 : {
191 30 : std::string const var(canonicalize_variable_name(name));
192 30 : if(overwrite
193 15 : || f_variables.find(var) == f_variables.end())
194 : {
195 15 : f_variables[canonicalize_variable_name(name)] = value;
196 : }
197 15 : }
198 :
199 :
200 : /** \brief Process variables against a parameter.
201 : *
202 : * Whenever a parameter is retrieved, its value is passed through this
203 : * function and if the variable processing is allowed, it searches for
204 : * `${...}` sequances and when such are found, it replaces them with the
205 : * corresponding variable content.
206 : *
207 : * The process is recursive meaning that if a variable includes the `${...}`
208 : * sequence, that variable will itself also be replaced.
209 : *
210 : * The variables can be defined in a `[variables]` section and by the
211 : * programmer by calling the set_variable() function.
212 : *
213 : * \note
214 : * This functionality is automatically used when the
215 : * SYSTEM_OPTION_PROCESS_VARIABLES flag is set in your environment definition.
216 : * If you prefer to have it only function for a few of your parameters, then
217 : * do not set the SYSTEM_OPTION_PROCESS_VARIABLES and only call this function
218 : * for the few values you want to include variables.
219 : *
220 : * \todo
221 : * Consider having a cache, although for variables that would return system
222 : * information, it could change at any time.
223 : *
224 : * \param[in] value The parameter value to be processed.
225 : *
226 : * \return The processed value with the variables updated.
227 : */
228 11 : std::string variables::process_value(std::string const & value) const
229 : {
230 : // to support the recursivity, we call a sub-function which calls itself
231 : // whenever a variable is discovered to include another variable; that
232 : // recursivity is broken immediately if a variable includes itself;
233 : // this function is private
234 : //
235 22 : variable_names_t names;
236 22 : return recursive_process_value(value, names);
237 : }
238 :
239 :
240 : /** \brief Internal function processing variables recursively.
241 : *
242 : * This function goes through value and replaces the `${...}` with the
243 : * corresponding variable data. The content of a variable is itself
244 : * passed through this process so it is recursive.
245 : *
246 : * The function records which variables it has worked on so far to
247 : * prevent the function from re-adding the same variable (avoid infinite
248 : * loop).
249 : *
250 : * \param[in] value The value to parse.
251 : * \param[in] names A set of variable names that have already been processed.
252 : */
253 24 : std::string variables::recursive_process_value(
254 : std::string const & value
255 : , variable_names_t & names) const
256 : {
257 24 : std::string result;
258 :
259 114 : for(char const * s(value.c_str()); *s != '\0'; ++s)
260 : {
261 90 : char c(*s);
262 90 : if(c == '$' && s[1] == '{') // start variable reference?
263 : {
264 13 : s += 2;
265 13 : char const * name(s);
266 77 : for(; *s != '}' && *s != '\0'; ++s);
267 13 : if(*s == '\0')
268 : {
269 : // invalid variable reference
270 : //
271 0 : result += name;
272 0 : return result;
273 : }
274 :
275 : // TODO: add support for conversions like we have in bash
276 : // (i.e. ${var:-extension} ${var%.extension} ...
277 : // see man bash section "Parameter Expansion")
278 : //
279 : // TODO: add support for emitting errors on an undefined
280 : // variable
281 : //
282 26 : std::string var(std::string(name, s - name));
283 13 : auto allowed(names.insert(var));
284 13 : if(allowed.second)
285 : {
286 13 : result += recursive_process_value(get_variable(var), names);
287 13 : names.erase(allowed.first);
288 : }
289 : else
290 : {
291 0 : result += "<variable \"" + var + "\" loops>";
292 13 : }
293 : }
294 : else
295 : {
296 77 : result += c;
297 : }
298 : }
299 :
300 24 : return result;
301 : }
302 :
303 :
304 :
305 6 : } // namespace advgetopt
306 : // vim: ts=4 sw=4 et
|