Line data Source code
1 : /*
2 : * File:
3 : * advgetopt/utils.cpp -- a replacement to the Unix getopt() implementation
4 : *
5 : * License:
6 : * Copyright (c) 2006-2019 Made to Order Software Corp. All Rights Reserved
7 : *
8 : * https://snapwebsites.org/
9 : * contact@m2osw.com
10 : *
11 : * This program is free software; you can redistribute it and/or modify
12 : * it under the terms of the GNU General Public License as published by
13 : * the Free Software Foundation; either version 2 of the License, or
14 : * (at your option) any later version.
15 : *
16 : * This program is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : * GNU General Public License for more details.
20 : *
21 : * You should have received a copy of the GNU General Public License along
22 : * with this program; if not, write to the Free Software Foundation, Inc.,
23 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 : *
25 : * Authors:
26 : * Alexis Wilke alexis@m2osw.com
27 : * Doug Barbieri doug@m2osw.com
28 : */
29 :
30 :
31 : /** \file
32 : * \brief Implementation of utility functions.
33 : *
34 : * This file includes various utility functions that are not specifically
35 : * attached to a class.
36 : */
37 :
38 : // self
39 : //
40 : #include "advgetopt/utils.h"
41 :
42 :
43 : // boost lib
44 : //
45 : #include <boost/algorithm/string/trim.hpp>
46 :
47 :
48 : // last include
49 : //
50 : #include <snapdev/poison.h>
51 :
52 :
53 :
54 : namespace advgetopt
55 : {
56 :
57 :
58 :
59 : /** \brief Remove single (') or double (") quotes from a string.
60 : *
61 : * If a string starts and ends with the same quotation mark, then it
62 : * gets removed.
63 : *
64 : * If no quotes appear, then the function returns a copy of the input as is.
65 : *
66 : * \param[in] s The string to unquote.
67 : *
68 : * \return The unquoted string.
69 : */
70 53 : std::string unquote(std::string const & s)
71 : {
72 53 : if(s.length() >= 2)
73 : {
74 108 : if((s[0] == '"' && s.back() == '"')
75 89 : || (s[0] == '\'' && s.back() == '\''))
76 : {
77 16 : return s.substr(1, s.length() - 2);
78 : }
79 : }
80 :
81 37 : return s;
82 : }
83 :
84 :
85 : /** \brief Split a string in sub-strings separated by \p separators.
86 : *
87 : * This function searches for any of the \p separators in \p str and
88 : * split at those locations.
89 : *
90 : * For example, to split a comma separated list of strings, use the
91 : * following:
92 : *
93 : * \code
94 : * string_list_t result;
95 : * option_info::split_string(string_to_split, result, {","});
96 : * \endcode
97 : *
98 : * If `string_to_split` is set to "a, b, c", then the `result` vector
99 : * will have three strings as a result: `a`, `b`, and `c`. Note that
100 : * the function automatically trims all strings and it never keeps
101 : * empty strings. So two separators one after another is accepted and
102 : * no empty string results.
103 : *
104 : * The trimming happens after the split occurs. This allows for the
105 : * list of separators to include spaces as separators.
106 : *
107 : * The function does not clear the result vector. This allows you to
108 : * call this function multiple times with various strings and the
109 : * results will be cumulated.
110 : *
111 : * \note
112 : * This function is a static so it can be used from anywhere to split
113 : * strings as required. You do not need to have an option_info instance.
114 : *
115 : * \todo
116 : * See to fix the fact that `a"b"c` becomes `{"a", "b", "c"}` when
117 : * there are not separators between `a`, `"b"`, and `c`. To the minimum
118 : * we may want to generate an error when such is found (i.e. when a
119 : * quote is found and `start < pos` is true.
120 : *
121 : * \param[in] str The string to split.
122 : * \param[in] result The vector where the split strings are saved.
123 : * \param[in] separators The vector of strings used as separators.
124 : */
125 91 : void split_string(std::string const & str
126 : , string_list_t & result
127 : , string_list_t const & separators)
128 : {
129 91 : std::string::size_type pos(0);
130 91 : std::string::size_type start(0);
131 12149 : while(pos < str.length())
132 : {
133 6029 : if(str[pos] == '\'' || str[pos] == '"')
134 : {
135 14 : if(start < pos)
136 : {
137 12 : std::string v(str.substr(start, pos - start));
138 6 : boost::trim(v);
139 6 : if(!v.empty())
140 : {
141 4 : result.push_back(v);
142 : }
143 6 : start = pos;
144 : }
145 :
146 : // quoted parameters are handled without the separators
147 : //
148 14 : char const quote(str[pos]);
149 14 : for(++pos; pos < str.length() && str[pos] != quote; ++pos);
150 :
151 28 : std::string const v(str.substr(start + 1, pos - (start + 1)));
152 14 : if(!v.empty())
153 : {
154 11 : result.push_back(v);
155 : }
156 14 : if(pos < str.length())
157 : {
158 : // skip the closing quote
159 : //
160 12 : ++pos;
161 : }
162 14 : start = pos;
163 : }
164 : else
165 : {
166 6015 : bool found(false);
167 12097 : for(auto const & sep : separators)
168 : {
169 6365 : if(str.length() - pos >= sep.length())
170 : {
171 6365 : if(str.compare(pos, sep.length(), sep) == 0)
172 : {
173 : // match! cut here
174 : //
175 283 : if(start < pos)
176 : {
177 522 : std::string v(str.substr(start, pos - start));
178 261 : boost::trim(v);
179 261 : if(!v.empty())
180 : {
181 261 : result.push_back(v);
182 : }
183 : }
184 283 : pos += sep.length();
185 283 : start = pos;
186 283 : found = true;
187 283 : break;
188 : }
189 : }
190 : }
191 :
192 6015 : if(!found)
193 : {
194 5732 : ++pos;
195 : }
196 : }
197 : }
198 :
199 91 : if(start < pos)
200 : {
201 168 : std::string v(str.substr(start, pos - start));
202 84 : boost::trim(v);
203 84 : if(!v.empty())
204 : {
205 84 : result.push_back(v);
206 : }
207 : }
208 91 : }
209 :
210 :
211 : /** \brief Insert the project name in the filename.
212 : *
213 : * This function inserts the name of the project in the specified full path
214 : * filename. It gets added right before the basename. So for example you
215 : * have a path such as:
216 : *
217 : * /etc/snapwebsites/advgetopt.conf
218 : *
219 : * and a project name such as:
220 : *
221 : * adventure
222 : *
223 : * The resulting path is:
224 : *
225 : * /etc/snapwebsites/adventure.d/advgetopt.conf
226 : *
227 : * Notice that the function adds a ".d" as well.
228 : *
229 : * \param[in] filename The filename where the project name gets injected.
230 : * \param[in] project_name The name of the project to inject in the filename.
231 : *
232 : * \return The new filename or an empty string if no project name or filename
233 : * are specified.
234 : */
235 509 : std::string insert_project_name(std::string const & filename
236 : , char const * project_name)
237 : {
238 509 : if(project_name == nullptr
239 508 : || *project_name == '\0'
240 1016 : || filename.empty())
241 : {
242 3 : return std::string();
243 : }
244 :
245 506 : std::string::size_type const pos(filename.find_last_of('/'));
246 506 : if(pos != std::string::npos
247 230 : && pos > 0)
248 : {
249 460 : return filename.substr(0, pos + 1)
250 460 : + project_name
251 460 : + ".d"
252 690 : + filename.substr(pos);
253 : }
254 :
255 276 : return project_name + (".d/" + filename);
256 : }
257 :
258 :
259 : /** \brief Replace a starting `~/...` with the contents of the \$HOME variable.
260 : *
261 : * This function checks the beginning of \p filename. If it starts with `'~/'`
262 : * then it replaces the `'~'` character with the contents of the \$HOME
263 : * environment variable.
264 : *
265 : * If \p filename is just `"~"`, then the function returns the contents of
266 : * the \$HOME environment variable by itself.
267 : *
268 : * If somehow the \$HOME environment variable is empty, the function does
269 : * nothing.
270 : *
271 : * \param[in] filename The filename to check for a tilde (~).
272 : *
273 : * \return The input as is unless the \$HOME path can be prepended to replace
274 : * the tilde (~) character.
275 : */
276 562 : std::string handle_user_directory(std::string const & filename)
277 : {
278 562 : char const * const home(getenv("HOME"));
279 562 : if(home != nullptr
280 404 : && *home != '\0')
281 : {
282 804 : if(!filename.empty()
283 402 : && filename[0] == '~'
284 458 : && (filename.length() == 1 || filename[1] == '/'))
285 : {
286 56 : return home + filename.substr(1);
287 : }
288 : }
289 :
290 506 : return filename;
291 : }
292 :
293 :
294 :
295 : } // namespace advgetopt
296 : // vim: ts=4 sw=4 et
|