1 // Copyright Vladimir Prus 2002-2004.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt
4 // or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 
7 #include <boost/program_options/parsers.hpp>
8 #include <boost/program_options/options_description.hpp>
9 #include <boost/program_options/variables_map.hpp>
10 using namespace boost::program_options;
11 // We'll use po::value everywhere to workaround vc6 bug.
12 namespace po = boost::program_options;
13 
14 #include <boost/function.hpp>
15 using namespace boost;
16 
17 #include <sstream>
18 #include <iostream>
19 using namespace std;
20 
21 #if defined(__sun)
22 #include <stdlib.h> // for putenv on solaris
23 #else
24 #include <cstdlib> // for putenv
25 #endif
26 
27 #include "minitest.hpp"
28 
29 #define TEST_CHECK_THROW(expression, exception, description) \
30     try \
31     { \
32         expression; \
33         BOOST_ERROR(description);\
34         throw 10; \
35     } \
36     catch(exception &) \
37     { \
38     }
39 
msp(const string & s1)40 pair<string, vector< vector<string> > > msp(const string& s1)
41 {
42     return std::make_pair(s1, vector< vector<string> >());
43 }
44 
45 
msp(const string & s1,const string & s2)46 pair<string, vector< vector<string> > > msp(const string& s1, const string& s2)
47 {
48     vector< vector<string> > v(1);
49     v[0].push_back(s2);
50     return std::make_pair(s1, v);
51 }
52 
check_value(const option & option,const char * name,const char * value)53 void check_value(const option& option, const char* name, const char* value)
54 {
55     BOOST_CHECK(option.string_key == name);
56     BOOST_REQUIRE(option.value.size() == 1);
57     BOOST_CHECK(option.value.front() == value);
58 }
59 
sv(const char * array[],unsigned size)60 vector<string> sv(const char* array[], unsigned size)
61 {
62     vector<string> r;
63     for (unsigned i = 0; i < size; ++i)
64         r.push_back(array[i]);
65     return r;
66 }
67 
additional_parser(const std::string &)68 pair<string, string> additional_parser(const std::string&)
69 {
70     return pair<string, string>();
71 }
72 
test_command_line()73 void test_command_line()
74 {
75     // The following commented out blocks used to test parsing
76     // command line without syntax specification behaviour.
77     // It is disabled now and probably will never be enabled again:
78     // it is not possible to figure out what command line means without
79     // user's help.
80     #if 0
81     char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" };
82 
83     options_and_arguments a1 =
84         parse_command_line(cmdline1,
85                            cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
86 
87     BOOST_REQUIRE(a1.options().size() == 4);
88     BOOST_CHECK(a1.options()[0] == msp("a", ""));
89     BOOST_CHECK(a1.options()[1] == msp("b", "12"));
90     BOOST_CHECK(a1.options()[2] == msp("-f", ""));
91     BOOST_CHECK(a1.options()[3] == msp("-g", "4"));
92     BOOST_REQUIRE(a1.arguments().size() == 2);
93     BOOST_CHECK(a1.arguments()[0] == "-");
94     BOOST_CHECK(a1.arguments()[1] == "file");
95 
96     char* cmdline2[] = { "--a", "--", "file" };
97 
98     options_and_arguments a2 =
99         parse_command_line(cmdline2,
100                            cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
101 
102     BOOST_REQUIRE(a2.options().size() == 1);
103     BOOST_CHECK(a2.options()[0] == msp("a", ""));
104     BOOST_CHECK(a2.arguments().size() == 1);
105     BOOST_CHECK(a2.arguments()[0] == "file");
106     #endif
107 
108     options_description desc;
109     desc.add_options()
110         ("foo,f", new untyped_value(), "")
111         // Explicit qualification is a workaround for vc6
112         ("bar,b", po::value<std::string>(), "")
113         ("baz", new untyped_value())
114         ("plug*", new untyped_value())
115         ;
116     const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
117                           "--plug3=10"};
118     vector<string> cmdline3 = sv(cmdline3_,
119                                  sizeof(cmdline3_)/sizeof(const char*));
120     vector<option> a3 =
121         command_line_parser(cmdline3).options(desc).run().options;
122 
123     BOOST_CHECK_EQUAL(a3.size(), 5u);
124 
125     check_value(a3[0], "foo", "12");
126     check_value(a3[1], "foo", "4");
127     check_value(a3[2], "bar", "11");
128     check_value(a3[3], "bar", "4");
129     check_value(a3[4], "plug3", "10");
130 
131     // Regression test: check that '0' as style is interpreted as
132     // 'default_style'
133     vector<option> a4 =
134         parse_command_line(sizeof(cmdline3_)/sizeof(const char*), cmdline3_,
135                                                                desc, 0, additional_parser).options;
136 
137     BOOST_CHECK_EQUAL(a4.size(), 4u);
138     check_value(a4[0], "foo", "4");
139     check_value(a4[1], "bar", "11");
140 
141     // Check that we don't crash on empty values of type 'string'
142     const char* cmdline4[] = {"", "--open", ""};
143     options_description desc2;
144     desc2.add_options()
145         ("open", po::value<string>())
146         ;
147     variables_map vm;
148     po::store(po::parse_command_line(sizeof(cmdline4)/sizeof(const char*), const_cast<char**>(cmdline4), desc2), vm);
149 
150     const char* cmdline5[] = {"", "-p7", "-o", "1", "2", "3", "-x8"};
151     options_description desc3;
152     desc3.add_options()
153         (",p", po::value<string>())
154         (",o", po::value<string>()->multitoken())
155         (",x", po::value<string>())
156         ;
157     vector<option> a5 =
158         parse_command_line(sizeof(cmdline5)/sizeof(const char*), const_cast<char**>(cmdline5),
159                                                                      desc3, 0, additional_parser).options;
160     BOOST_CHECK_EQUAL(a5.size(), 3u);
161     check_value(a5[0], "-p", "7");
162     BOOST_REQUIRE(a5[1].value.size() == 3);
163     BOOST_CHECK_EQUAL(a5[1].string_key, "-o");
164     BOOST_CHECK_EQUAL(a5[1].value[0], "1");
165     BOOST_CHECK_EQUAL(a5[1].value[1], "2");
166     BOOST_CHECK_EQUAL(a5[1].value[2], "3");
167     check_value(a5[2], "-x", "8");
168 
169 
170     po::options_description desc4( "" );
171     desc4.add_options()
172         ( "multitoken,m",
173           po::value< std::vector< std::string > >()->multitoken(),
174           "values"
175             )
176         ( "file",
177           po::value< std::string >(),
178           "the file to process"
179             )
180         ;
181 
182     po::positional_options_description p;
183     p.add( "file", 1 );
184 
185     const char* cmdline6[] = {"", "-m", "token1", "token2", "--", "some_file"};
186     vector<option> a6 =
187         command_line_parser(sizeof(cmdline6)/sizeof(const char*), const_cast<char**>(cmdline6)).options(desc4).positional(p)
188         .run().options;
189     BOOST_CHECK_EQUAL(a6.size(), 2u);
190     BOOST_REQUIRE(a6[0].value.size() == 2);
191     BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken");
192     BOOST_CHECK_EQUAL(a6[0].value[0], "token1");
193     BOOST_CHECK_EQUAL(a6[0].value[1], "token2");
194     BOOST_CHECK_EQUAL(a6[1].string_key, "file");
195     BOOST_REQUIRE(a6[1].value.size() == 1);
196     BOOST_CHECK_EQUAL(a6[1].value[0], "some_file");
197 }
198 
test_config_file(const char * config_file)199 void test_config_file(const char* config_file)
200 {
201     options_description desc;
202     desc.add_options()
203         ("gv1", new untyped_value)
204         ("gv2", new untyped_value)
205         ("empty_value", new untyped_value)
206         ("plug*", new untyped_value)
207         ("m1.v1", new untyped_value)
208         ("m1.v2", new untyped_value)
209         ("b", bool_switch())
210     ;
211 
212     const char content1[] =
213     " gv1 = 0#asd\n"
214     "empty_value = \n"
215     "plug3 = 7\n"
216     "b = true\n"
217     "[m1]\n"
218     "v1 = 1\n"
219     "\n"
220     "v2 = 2\n"
221     ;
222 
223     stringstream ss(content1);
224     vector<option> a1 = parse_config_file(ss, desc).options;
225     BOOST_REQUIRE(a1.size() == 6);
226     check_value(a1[0], "gv1", "0");
227     check_value(a1[1], "empty_value", "");
228     check_value(a1[2], "plug3", "7");
229     check_value(a1[3], "b", "true");
230     check_value(a1[4], "m1.v1", "1");
231     check_value(a1[5], "m1.v2", "2");
232 
233     // same test, but now options come from file
234     vector<option> a2 = parse_config_file<char>(config_file, desc).options;
235     BOOST_REQUIRE(a2.size() == 6);
236     check_value(a2[0], "gv1", "0");
237     check_value(a2[1], "empty_value", "");
238     check_value(a2[2], "plug3", "7");
239     check_value(a2[3], "b", "true");
240     check_value(a2[4], "m1.v1", "1");
241     check_value(a2[5], "m1.v2", "2");
242 }
243 
test_environment()244 void test_environment()
245 {
246     options_description desc;
247     desc.add_options()
248         ("foo", new untyped_value, "")
249         ("bar", new untyped_value, "")
250         ;
251 
252 #if defined(_WIN32) && ! defined(__BORLANDC__)
253     _putenv("PO_TEST_FOO=1");
254 #else
255     putenv(const_cast<char*>("PO_TEST_FOO=1"));
256 #endif
257     parsed_options p = parse_environment(desc, "PO_TEST_");
258 
259     BOOST_REQUIRE(p.options.size() == 1);
260     BOOST_CHECK(p.options[0].string_key == "foo");
261     BOOST_REQUIRE(p.options[0].value.size() == 1);
262     BOOST_CHECK(p.options[0].value[0] == "1");
263 
264     //TODO: since 'bar' does not allow a value, it cannot appear in environemt,
265     // which already has a value.
266 }
267 
test_unregistered()268 void test_unregistered()
269 {
270     options_description desc;
271 
272     const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
273     vector<string> cmdline1 = sv(cmdline1_,
274                                  sizeof(cmdline1_)/sizeof(const char*));
275     vector<option> a1 =
276         command_line_parser(cmdline1).options(desc).allow_unregistered().run()
277         .options;
278 
279     BOOST_REQUIRE(a1.size() == 3);
280     BOOST_CHECK(a1[0].string_key == "foo");
281     BOOST_CHECK(a1[0].unregistered == true);
282     BOOST_REQUIRE(a1[0].value.size() == 1);
283     BOOST_CHECK(a1[0].value[0] == "12");
284     BOOST_CHECK(a1[1].string_key == "bar");
285     BOOST_CHECK(a1[1].unregistered == true);
286     BOOST_CHECK(a1[2].string_key == "");
287     BOOST_CHECK(a1[2].unregistered == false);
288 
289 
290     vector<string> a2 = collect_unrecognized(a1, include_positional);
291     BOOST_CHECK(a2[0] == "--foo=12");
292     BOOST_CHECK(a2[1] == "--bar");
293     BOOST_CHECK(a2[2] == "1");
294 
295     // Test that storing unregisted options has no effect
296     variables_map vm;
297 
298     store(command_line_parser(cmdline1).options(desc).
299           allow_unregistered().run(),
300           vm);
301 
302     BOOST_CHECK_EQUAL(vm.size(), 0u);
303 
304 
305     const char content1[] =
306     "gv1 = 0\n"
307     "[m1]\n"
308     "v1 = 1\n"
309     ;
310 
311     stringstream ss(content1);
312     vector<option> a3 = parse_config_file(ss, desc, true).options;
313     BOOST_REQUIRE(a3.size() == 2);
314     cout << "XXX" << a3[0].value.front() << "\n";
315     check_value(a3[0], "gv1", "0");
316     check_value(a3[1], "m1.v1", "1");
317 }
318 
main(int,char * av[])319 int main(int, char* av[])
320 {
321     test_command_line();
322     test_config_file(av[1]);
323     test_environment();
324     test_unregistered();
325     return 0;
326 }
327 
328