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 Implementation of the options_to_strings() command.
22 : *
23 : * The getopt object holds all the command line options your program was
24 : * started with. Here we transforms these options back to shell command
25 : * line options so one can start the command again with the same options.
26 : */
27 :
28 : // self
29 : //
30 : #include "advgetopt/advgetopt.h"
31 :
32 :
33 : // cppthread
34 : //
35 : #include <cppthread/log.h>
36 :
37 :
38 : // last include
39 : //
40 : #include <snapdev/poison.h>
41 :
42 :
43 :
44 : namespace advgetopt
45 : {
46 :
47 :
48 : namespace
49 : {
50 :
51 : constexpr char const g_single_quote = '\'';
52 : constexpr char const g_space = ' ';
53 : constexpr char const * g_empty_string = "\"\"";
54 : constexpr char const * g_escaped_single_quotes = "'\\''";
55 : constexpr char const * g_simple_characters = "+-./0123456789=ABCEFGHIJKLMNOPQRSTUVWXYZabcefghijklmnopqrstuvwxyz_";
56 :
57 : } // no name namespace
58 :
59 :
60 : /** \brief Escape special characters from a shell argument.
61 : *
62 : * This function goes through the supplied argument. If it includes one
63 : * or more character other than `[-+0-9A-Za-z_]`, then it gets \em escaped.
64 : * This means we add single quotes at the start and end, and escape any
65 : * single quote within the argument.
66 : *
67 : * So the function may return the input string as is.
68 : *
69 : * \param[in] arg The argument to escape.
70 : *
71 : * \return The escaped argument.
72 : */
73 110 : std::string getopt::escape_shell_argument(std::string const & arg)
74 : {
75 110 : if(arg.empty())
76 : {
77 1 : return std::string(g_empty_string);
78 : }
79 :
80 109 : std::string::size_type const pos(arg.find_first_not_of(g_simple_characters));
81 109 : if(pos == std::string::npos)
82 : {
83 106 : return arg;
84 : }
85 :
86 6 : std::string result;
87 :
88 3 : result += g_single_quote;
89 3 : std::string::size_type p1(0);
90 9 : while(p1 < arg.length())
91 : {
92 5 : std::string::size_type const p2(arg.find('\'', p1));
93 5 : if(p2 == std::string::npos)
94 : {
95 2 : result += arg.substr(p1);
96 2 : break;
97 : }
98 3 : result += arg.substr(p1, p2 - p1);
99 3 : result += g_escaped_single_quotes;
100 3 : p1 = p2 + 1; // skip the '
101 : }
102 3 : result += g_single_quote;
103 :
104 3 : return result;
105 : }
106 :
107 :
108 : /** \brief Transform all the defined options back in a string.
109 : *
110 : * This function creates a string which system() can use to start the
111 : * command again with the same options. You may, of course, tweak the
112 : * options first.
113 : *
114 : * \param[in] include_progname Whether the program name should be included
115 : * in the output string. In some cases, you may want to start a different
116 : * program with similar command line options. This gives you that option.
117 : * \param[in] keep_defaults If the value is equal to the default value, it
118 : * gets ignored unless this parameter is set to true.
119 : *
120 : * \return The string representing the command line options.
121 : */
122 4 : std::string getopt::options_to_string(bool include_progname, bool keep_defaults) const
123 : {
124 4 : std::string result;
125 :
126 4 : if(include_progname)
127 : {
128 2 : result += escape_shell_argument(get_program_fullname());
129 : }
130 :
131 8 : advgetopt::option_info::pointer_t default_option;
132 72 : for(auto const & opt : f_options_by_name)
133 : {
134 68 : if(!opt.second->is_defined())
135 : {
136 48 : continue;
137 : }
138 24 : if(opt.second->is_default_option())
139 : {
140 4 : default_option = opt.second;
141 4 : continue;
142 : }
143 :
144 42 : if(!keep_defaults
145 8 : && !opt.second->has_flag(advgetopt::GETOPT_FLAG_FLAG)
146 44 : && opt.second->get_default() == opt.second->get_value())
147 : {
148 : // same as default, no need to add that parameter
149 : //
150 2 : continue;
151 : }
152 :
153 14 : if(!result.empty())
154 : {
155 12 : result += g_space;
156 : }
157 :
158 14 : result += "--";
159 14 : result += opt.second->get_name();
160 :
161 14 : if(!opt.second->has_flag(advgetopt::GETOPT_FLAG_FLAG))
162 : {
163 10 : result += g_space;
164 10 : result += escape_shell_argument(opt.second->get_value());
165 10 : std::size_t const max(opt.second->size());
166 26 : for(std::size_t idx(1); idx < max; ++idx)
167 : {
168 16 : result += g_space;
169 16 : result += escape_shell_argument(opt.second->get_value(idx));
170 : }
171 : }
172 : }
173 :
174 4 : if(default_option != nullptr)
175 : {
176 4 : result += " -- ";
177 :
178 4 : result += escape_shell_argument(default_option->get_value());
179 4 : std::size_t const max(default_option->size());
180 12 : for(std::size_t idx(1); idx < max; ++idx)
181 : {
182 8 : result += g_space;
183 8 : result += escape_shell_argument(default_option->get_value(idx));
184 : }
185 : }
186 :
187 8 : return result;
188 : }
189 :
190 :
191 :
192 6 : } // namespace advgetopt
193 : // vim: ts=4 sw=4 et
|