GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/command_line.cpp
Date: 2026-09-07 02:36:43
Exec Total Coverage
Lines: 385 659 58.4%
Functions: 44 59 74.6%
Branches: 396 1266 31.3%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/basic/command_line.h>
41 #include <geogram/basic/command_line_args.h>
42 #include <geogram/basic/environment.h>
43 #include <geogram/basic/file_system.h>
44 #include <geogram/basic/logger.h>
45 #include <geogram/basic/stopwatch.h>
46 #include <geogram/basic/process.h>
47 #include <geogram/bibliography/bibliography.h>
48 #include <geogram/NL/nl.h>
49 #include <iostream>
50 #include <iomanip>
51
52 #if defined(GEO_OS_LINUX) || defined(GEO_OS_APPLE)
53 #include <sys/ioctl.h>
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <termios.h>
57 #endif
58
59 #ifdef GEO_OS_WINDOWS
60 #include <io.h> // for _isatty()
61 #endif
62
63 /**
64 * \brief Checks argument type
65 * \details Checks if argument type \p type is in allowed types \p
66 * allowed_types or is undefined. Multiple types can be specified in \p
67 * allowed_types by bit-OR'ing the values defined in enum
68 * GEO::CmdLine::ArgType.
69 * \param[in] type the type to be tested
70 * \param[in] allowed_types argument types allowed for \p type.
71 * \retval true if type is in \p allowed_types
72 * \retval false otherwise
73 * \see GEO::CmdLine::ArgType
74 */
75 #define geo_assert_arg_type(type, allowed_types) \
76 geo_assert(((type) & ~(allowed_types)) == 0)
77
78 namespace {
79
80 using namespace GEO;
81 using namespace CmdLine;
82
83 std::string config_file_name = "geogram.ini";
84 bool auto_create_args = false;
85 bool loaded_config_file = false;
86
87 int geo_argc = 0;
88 char** geo_argv = nullptr;
89
90 // True if displaying help in a way that
91 // it will be easily processed by help2man
92 bool man_mode = false;
93
94 Process::spinlock lock = GEOGRAM_SPINLOCK_INIT;
95
96 /**
97 * \brief Command line argument
98 * \details Arg stores information about command line arguments:
99 */
100 struct Arg {
101 /** \brief The argument name */
102 std::string name;
103 /** \brief The argument description */
104 std::string desc;
105 /** \brief The argument type */
106 ArgType type;
107 /** \brief The argument flags */
108 ArgFlags flags;
109 };
110
111 /** \brief Stores command line arguments by name */
112 typedef std::map<std::string, Arg> Args;
113
114 /**
115 * \brief Stores the list of all arguments in a group
116 * \see ArgGroup
117 */
118 typedef std::vector<std::string> GroupArgs;
119
120 /**
121 * \brief Command line argument group
122 * \details ArgGroup stores information about command line argument groups
123 */
124 struct Group {
125 /** \brief The group name */
126 std::string name;
127 /** \brief The group description */
128 std::string desc;
129 /** \brief The group flags */
130 ArgFlags flags;
131 /** \brief The list of arguments in the group */
132 GroupArgs args;
133 };
134
135 /** \brief Stores command line argument groups by name */
136 typedef std::map<std::string, Group> Groups;
137
138 /** \brief List of group names ordered in declaration order */
139 typedef std::vector<std::string> GroupNames;
140
141 /**
142 * \brief Command line private data
143 */
144 struct CommandLineDesc {
145 /** \brief Program name */
146 std::string argv0;
147 /** \brief Table of all declared arguments indexed by name */
148 Args args;
149 /** \brief Table of all declared groups indexed by name */
150 Groups groups;
151 /** \brief Oredered list of declared group names */
152 GroupNames group_names;
153 };
154
155 /** \brief Maximum length of a feature name */
156 const unsigned int feature_max_length = 12;
157
158 /** \brief Pointer to command line private data */
159 CommandLineDesc* desc_ = nullptr;
160
161 /**
162 * \brief Checks if an argument name matches a sub-strung
163 * \param[in] arg_in the sub-string to match
164 * \param[in] arg_name the argument name
165 * \retval true if \p arg_name contains \p arg_in
166 * \retval false otherwise
167 */
168 2464 bool arg_matches(
169 const std::string& arg_in, const std::string& arg_name
170 ) {
171 2464 return arg_name.find(arg_in) != std::string::npos;
172 }
173
174 /**
175 * \brief Prints bad argument value error message
176 * \param[in] name the argument name
177 * \param[in] s the bad value
178 * \param[in] type the argument type
179 * \return \c false
180 */
181 2 bool arg_value_error(
182 const std::string& name,
183 const std::string& s, const char* type
184 ) {
185 2 Logger::instance()->set_quiet(false);
186
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 Logger::err("CmdLine")
187 << "Argument " << name << " received a bad value: '"
188
7/14
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 2 times.
✗ Branch 20 not taken.
2 << s << "' is not a " << type << " value"
189
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 << std::endl;
190 2 return false;
191 }
192
193 /**
194 * \brief Checks the value of an argument
195 * \details This function is used by parse_internal() to check the values
196 * passed to arguments on the command line. It verifies that the string \p
197 * s is convertible to the declared type of argument \p name (see
198 * declare_arg()).
199 * \param[in] name the argument name
200 * \param[in] s the string value of the argument
201 * \retval true if string \p s is convertible to the type of the argument
202 * \retval false otherwise
203 */
204 2270 bool check_arg_value(
205 const std::string& name, const std::string& s
206 ) {
207 2270 ArgType type = get_arg_type(name);
208
4/4
✓ Branch 0 taken 954 times.
✓ Branch 1 taken 1316 times.
✓ Branch 2 taken 396 times.
✓ Branch 3 taken 558 times.
2270 if(type == ARG_UNDEFINED || type == ARG_STRING) {
209 1712 return true;
210 }
211
212
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 453 times.
558 if(type == ARG_INT) {
213 int value;
214
3/4
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 104 times.
105 if(!String::from_string(s, value)) {
215
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 return arg_value_error(name, s, "integer");
216 } else {
217 104 return true;
218 }
219 }
220
221
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 396 times.
453 if(type == ARG_DOUBLE) {
222 double value;
223
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 56 times.
57 if(!String::from_string(s, value)) {
224
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 return arg_value_error(name, s, "floating point");
225 } else {
226 56 return true;
227 }
228 }
229
230
2/2
✓ Branch 0 taken 316 times.
✓ Branch 1 taken 80 times.
396 if(type == ARG_BOOL) {
231 bool value;
232
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 316 times.
316 if(!String::from_string(s, value)) {
233 return arg_value_error(name, s, "boolean");
234 } else {
235 316 return true;
236 }
237 }
238
239
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 if(type == ARG_PERCENT) {
240
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 std::string s2 = s;
241
4/8
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 80 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 80 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 80 times.
✗ Branch 10 not taken.
80 if(s2.length() > 0 && s2[s2.length() - 1] == '%') {
242
1/2
✓ Branch 2 taken 80 times.
✗ Branch 3 not taken.
80 s2.resize(s2.length() - 1);
243 }
244 double value;
245
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
80 if(!String::from_string(s2, value)) {
246 return arg_value_error(name, s, "percentage");
247 } else {
248 80 return true;
249 }
250 80 }
251
252 return false;
253 }
254
255
256 /**
257 * \brief Parses the configuration file in the home directory.
258 * \details The configuration file "geogram.ini" in the home directory
259 * has name=value pairs for pre-initializing command line arguments.
260 * In addition it has sections indicated by square-breacketed names.
261 * Only the arguments in the section with the same name as the program
262 * are taken into account. Section [*] refers to all possible programs.
263 * \param[in] config_filename the name of the configuration file
264 * \param[in] program_name the name of the program
265 */
266 249 void parse_config_file(
267 const std::string& config_filename, const std::string& program_name
268 ) {
269
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 std::string section = "*";
270
2/4
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
249 if(FileSystem::is_file(config_filename)) {
271 std::ifstream in(config_filename.c_str());
272 std::string line;
273 while(std::getline(in,line)) {
274 if(
275 line.length() >= 3 && line[0] == '[' &&
276 line[line.length()-1] == ']'
277 ) {
278 section = String::to_uppercase(
279 line.substr(1,line.length()-2)
280 );
281 } else if(section == program_name || section == "*") {
282 size_t pos = line.find("=");
283 if(pos != std::string::npos) {
284 std::string argname = line.substr(0,pos);
285 std::string argval = line.substr(
286 pos+1,line.length()-pos-1
287 );
288 if(CmdLine::arg_is_declared(argname)) {
289 CmdLine::set_arg(argname, argval);
290 } else {
291 if(auto_create_args) {
292 CmdLine::declare_arg(argname, argval, "...");
293 } else {
294 Logger::warn("config") << argname
295 << "=" << argval
296 << " ignored"
297 << std::endl;
298 }
299 }
300 }
301 }
302 }
303 loaded_config_file= true;
304 }
305 249 }
306
307 /**
308 * \brief Parses the configuration file in the home directory.
309 * \details The configuration file "geogram.ini" in the home directory
310 * has name=value pairs for pre-initializing command line arguments.
311 * In addition it has sections indicated by square-breacketed names.
312 * Only the arguments in the section with the same name as the program
313 * are taken into account. Section [*] refers to all possible programs.
314 * \param[in] argc number of arguments passed to main()
315 * \param[in] argv array of command line arguments passed to main()
316 */
317 493 void parse_config_file(int argc, char** argv) {
318
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 493 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
493 geo_assert(argc >= 1);
319 std::string program_name = String::to_uppercase(
320
2/4
✓ Branch 1 taken 493 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 493 times.
✗ Branch 5 not taken.
986 FileSystem::base_name(argv[0])
321
1/2
✓ Branch 1 taken 493 times.
✗ Branch 2 not taken.
493 );
322 static bool init = false;
323
2/2
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 249 times.
493 if(init) {
324 244 return;
325 }
326 249 init = true;
327
2/4
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
747 Logger::out("config")
328
2/4
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
249 << "Configuration file name:" << config_file_name
329
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 << std::endl;
330
2/4
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
249 Logger::out("config")
331
3/6
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 8 not taken.
498 << "Home directory:" << FileSystem::home_directory()
332
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 << std::endl;
333 std::string config_filename =
334
3/6
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 8 not taken.
249 FileSystem::home_directory() + "/" + config_file_name;
335
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 parse_config_file(config_filename, program_name);
336
2/2
✓ Branch 2 taken 249 times.
✓ Branch 3 taken 244 times.
493 }
337
338 /**
339 * \brief Parses the command line arguments
340 * \details This analyzes command line arguments passed to the main()
341 * program in \p argc and \p argv. Arguments not matching program
342 * options declared with declare_arg() are stored in output vector \p
343 * unparsed_args.
344 * \param[in] argc number of arguments passed to main()
345 * \param[in] argv array of command line arguments passed to main()
346 * \param[out] unparsed_args output vector of unparsed arguments
347 * \retval true if the command line arguments are successfully parsed
348 * \retval false otherwise
349 */
350 493 bool parse_internal(
351 int argc, char** argv, std::vector<std::string>& unparsed_args
352 ) {
353 493 geo_argc = argc;
354 493 geo_argv = argv;
355
356 493 parse_config_file(argc, argv);
357
358 493 bool ok = true;
359 493 desc_->argv0 = argv[0];
360 493 unparsed_args.clear();
361
362
2/2
✓ Branch 0 taken 1401 times.
✓ Branch 1 taken 493 times.
1894 for(int i = 1; i < argc; i++) {
363 1401 std::vector<std::string> parsed_arg;
364
2/4
✓ Branch 1 taken 1401 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1401 times.
✗ Branch 5 not taken.
1401 String::split_string(argv[i], '=', parsed_arg);
365
2/2
✓ Branch 1 taken 656 times.
✓ Branch 2 taken 745 times.
1401 if(parsed_arg.size() != 2) {
366
2/4
✓ Branch 1 taken 656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 656 times.
✗ Branch 5 not taken.
1312 unparsed_args.push_back(argv[i]);
367 } else {
368 745 if(
369
9/16
✓ Branch 1 taken 745 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 745 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 723 times.
✓ Branch 8 taken 22 times.
✓ Branch 9 taken 695 times.
✓ Branch 10 taken 28 times.
✓ Branch 12 taken 745 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 717 times.
✓ Branch 15 taken 28 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
3703 String::string_starts_with(parsed_arg[0], "dbg:") ||
370
2/4
✓ Branch 3 taken 723 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 745 times.
✗ Branch 7 not taken.
1468 desc_->args.find(parsed_arg[0]) != desc_->args.end()
371 ) {
372
3/4
✓ Branch 3 taken 717 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 715 times.
717 if(!set_arg(parsed_arg[0], parsed_arg[1])) {
373 2 ok = false;
374 }
375 } else {
376
377 28 std::vector<std::string> matches;
378
2/2
✓ Branch 5 taken 2464 times.
✓ Branch 6 taken 28 times.
2492 for( auto& it : desc_->args) {
379
2/2
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 2438 times.
2464 if(arg_matches(parsed_arg[0], it.first)) {
380
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 matches.push_back(it.first);
381 }
382 }
383
384
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 2 times.
28 if(matches.size() == 1) {
385
2/4
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
26 if(!set_arg(matches[0], parsed_arg[1])) {
386 ok = false;
387 }
388
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 } else if(matches.size() >= 2) {
389 ok = false;
390 Logger::instance()->set_quiet(false);
391 Logger::err("CmdLine")
392 << "Argument is ambiguous: " << argv[i]
393 << std::endl
394 << "Possible matches: "
395 << String::join_strings(matches, ' ')
396 << std::endl;
397 } else {
398 2 ok = false;
399
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 Logger::instance()->set_quiet(false);
400
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 Logger::err("CmdLine")
401
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
2 << "Invalid argument: " << parsed_arg[0]
402
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 << std::endl;
403 }
404 28 }
405 }
406 1401 }
407 493 return ok;
408 }
409
410 /**
411 * \brief Extracts the group name from an argument
412 * \details If the command line argument \p name has the form
413 * "group:arg", then "group" is returned, otherwise the argument is
414 * considered to be global and the "global" group is returned.
415 * \param[in] name the name of the argument
416 * \return the group name or "global"
417 */
418 11233 std::string arg_group(const std::string& name) {
419 11233 size_t pos = name.find(':');
420 return pos == std::string::npos
421
2/4
✓ Branch 0 taken 2081 times.
✓ Branch 1 taken 9152 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13314 ? std::string("global")
422
4/6
✓ Branch 0 taken 2081 times.
✓ Branch 1 taken 9152 times.
✓ Branch 3 taken 2081 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 9152 times.
✗ Branch 7 not taken.
24547 : name.substr(0, pos);
423 }
424
425
426 /**
427 * \brief Gets an argument as a string for display.
428 * \param[in] arg_name the name of the argument.
429 * \return a string with the value of the argument for
430 * display.
431 * \details It ignores least significant digits for floating
432 * point arguments.
433 */
434 70 std::string get_display_arg(const std::string& arg_name) {
435 70 CmdLine::ArgType arg_type = get_arg_type(arg_name);
436 70 std::string result;
437
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 64 times.
70 if(arg_type == CmdLine::ARG_DOUBLE) {
438
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 double x = CmdLine::get_arg_double(arg_name);
439
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 result = String::to_display_string(x);
440
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 53 times.
64 } else if(arg_type == CmdLine::ARG_PERCENT) {
441
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 double x = CmdLine::get_arg_percent(arg_name, 100.0);
442
2/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
11 result = String::to_display_string(x) + "%";
443 } else {
444
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 result = CmdLine::get_arg(arg_name);
445
2/4
✓ Branch 2 taken 53 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 53 times.
53 if(result.length() > ui_terminal_width()/2) {
446 // TODO: fix display long lines in terminal
447 // (that trigger infinite loop for now)
448 result = "...";
449 }
450 }
451 70 return result;
452 }
453
454 /**
455 * \brief Private data used for printing ArgGroup details
456 */
457 struct Line {
458 std::string name;
459 std::string value;
460 std::string desc;
461 };
462
463 /**
464 * \brief Prints arguments of a group
465 * \details This function is called by show_usage() to display help on a
466 * group of arguments. It displays a fancy box that summarizes all
467 * arguments of the group \p group. By default, only standard arguments
468 * are displayed. If parameter \p advanced is set to \c true, the function
469 * also displays advanced arguments.
470 * \param[in] group the group name
471 * \param[in] advanced boolean flag that controls the display of
472 * advanced arguments in the group.
473 * \see show_usage()
474 */
475 59 void show_group(const std::string& group, bool advanced) {
476
477
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 auto it = desc_->groups.find(group);
478
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 59 times.
59 if(it == desc_->groups.end()) {
479 45 return;
480 }
481
482 59 const Group& g = it->second;
483 59 bool advanced_group = g.flags & ARG_ADVANCED;
484
3/4
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✓ Branch 3 taken 14 times.
59 if(!advanced && advanced_group) {
485 45 return;
486 }
487
488
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(advanced_group) {
489 ui_separator(g.desc, "*" + g.name);
490 } else {
491
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 ui_separator(g.desc, g.name);
492 }
493
494 // Step 1:
495 // Build a vector of lines that contain the various elements to
496 // print for the argument, and compute the maximum width of the
497 // argument names followed by their default value. This will allow
498 // us to align the closing paren of the default value.
499
500 14 std::vector<Line> lines;
501 14 index_t max_left_width = 0;
502
503
2/2
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 14 times.
117 for(size_t i = 0; i < g.args.size(); i++) {
504
1/2
✓ Branch 2 taken 103 times.
✗ Branch 3 not taken.
103 auto ita = desc_->args.find(g.args[i]);
505
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 103 times.
103 if(ita == desc_->args.end()) {
506 33 continue;
507 }
508
509 103 const Arg& arg = ita->second;
510 103 bool advanced_arg = arg.flags & ARG_ADVANCED;
511
3/4
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 70 times.
103 if(!advanced && advanced_arg) {
512 33 continue;
513 }
514
515
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
70 const char* name_marker = (advanced_arg && !man_mode) ? "*" : " ";
516
517 70 Line line;
518
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 line.name = name_marker + arg.name;
519
3/6
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 70 times.
✗ Branch 8 not taken.
70 line.value = " (=" + get_display_arg(arg.name) + ") : ";
520
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 line.desc = arg.desc;
521
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 lines.push_back(line);
522
523 140 max_left_width = std::max(
524 70 index_t(line.name.length() + line.value.length()),
525 max_left_width
526 );
527 70 }
528
529 // Step 2:
530 // Print the lines constructed in step 1 with default values
531 // right aligned.
532
533
2/2
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 14 times.
84 for(size_t i = 0; i < lines.size(); i++) {
534 70 const Line& line = lines[i];
535 70 int value_width = int(max_left_width - line.name.length());
536
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 std::ostringstream os;
537 70 os << line.name
538 70 << std::setw(value_width) << line.value
539
3/6
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 70 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 70 times.
✗ Branch 10 not taken.
70 << line.desc
540
1/2
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
70 << std::endl;
541
2/4
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
70 ui_message(os.str(), max_left_width);
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if(man_mode) {
543 ui_message("\n");
544 }
545 70 }
546 14 }
547
548 }
549
550 /****************************************************************************/
551
552 namespace GEO {
553
554 namespace CmdLine {
555
556 249 void initialize() {
557
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 249 times.
249 desc_ = new CommandLineDesc;
558
3/6
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 249 times.
✗ Branch 8 not taken.
747 declare_arg_group("global", "");
559 249 }
560
561 249 void terminate() {
562 249 ui_close_separator();
563
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 delete desc_;
564 249 desc_ = nullptr;
565 249 }
566
567 int argc() {
568 return geo_argc;
569 }
570
571 char** argv() {
572 return geo_argv;
573 }
574
575 void set_config_file_name(
576 const std::string& filename, bool auto_create
577 ) {
578 config_file_name = filename;
579 auto_create_args = auto_create;
580 }
581
582 std::string get_config_file_name() {
583 return config_file_name;
584 }
585
586 void load_config(
587 const std::string& filename, const std::string& program_name
588 ) {
589 parse_config_file(filename, program_name);
590 }
591
592 void save_config(const std::string& filename) {
593 std::ofstream out(filename);
594 if(!out) {
595 Logger::err("CmdLine") << filename << ": could not create"
596 << std::endl;
597 }
598 for(const auto& arg: desc_->args) {
599 const std::string& argname = arg.first;
600 out << argname << "=" << get_arg(argname) << std::endl;
601 }
602 }
603
604
605 bool config_file_loaded() {
606 return loaded_config_file;
607 }
608
609 249 bool parse(
610 int argc, char** argv, std::vector<std::string>& unparsed_args,
611 const std::string& additional_arg_specs
612 ) {
613
3/4
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 245 times.
249 if(!parse_internal(argc, argv, unparsed_args)) {
614 4 return false;
615 }
616
617
3/6
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 245 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 245 times.
✗ Branch 8 not taken.
490 if(arg_is_declared("profile")) {
618
2/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 245 times.
✗ Branch 5 not taken.
245 std::string profile = get_arg("profile");
619
2/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 245 times.
✗ Branch 4 not taken.
245 if(profile != "default") {
620
3/4
✓ Branch 1 taken 245 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 244 times.
245 if(!set_profile(profile)) {
621 1 return false;
622 }
623 // Re-parse args to override values set by profiles
624 244 unparsed_args.clear();
625
1/2
✓ Branch 1 taken 244 times.
✗ Branch 2 not taken.
244 parse_internal(argc, argv, unparsed_args);
626 }
627
2/2
✓ Branch 1 taken 244 times.
✓ Branch 2 taken 1 times.
245 }
628
629
2/2
✓ Branch 1 taken 323 times.
✓ Branch 2 taken 244 times.
567 for(index_t i = 0; i < unparsed_args.size(); ++i) {
630 323 const std::string& arg = unparsed_args[i];
631 323 if(
632
1/2
✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
323 arg == "-h" ||
633
2/4
✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 323 times.
✗ Branch 4 not taken.
323 arg == "-?" ||
634
4/8
✓ Branch 0 taken 323 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 323 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 323 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 323 times.
969 arg == "/h" ||
635
2/4
✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 323 times.
323 arg == "/?"
636 ) {
637 show_usage(additional_arg_specs, true);
638 exit(0);
639 }
640
2/4
✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 323 times.
323 if(arg == "--help") {
641 CmdLine::set_arg("log:pretty",false);
642 man_mode = true;
643 show_usage(additional_arg_specs, true);
644 exit(0);
645 }
646
5/10
✓ Branch 1 taken 323 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 323 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 323 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 323 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 323 times.
323 if(arg == "--version" || arg == "--v") {
647 std::cout << std::endl;
648 std::cout << " " << FileSystem::base_name(argv[0])
649 << " "
650 << Environment::instance()->get_value("version")
651 << " (built "
652 << Environment::instance()->get_value(
653 "release_date")
654 << ")"
655 << std::endl
656 << " Copyright (C) Inria 2000-2022"
657 << std::endl
658 << " License: <https://github.com/BrunoLevy/geogram/blob/main/LICENSE>"
659 << std::endl
660 << " Website: <https://github.com/BrunoLevy/geogram>"
661 << std::endl;
662 std::cout << std::endl;
663 exit(0);
664 }
665 }
666
667 244 index_t min_unparsed = 0;
668 244 index_t max_unparsed = 0;
669 244 std::vector<std::string> additional_args;
670
1/2
✓ Branch 1 taken 244 times.
✗ Branch 2 not taken.
244 String::split_string(additional_arg_specs, ' ', additional_args);
671
2/2
✓ Branch 1 taken 447 times.
✓ Branch 2 taken 244 times.
691 for(index_t i = 0; i < additional_args.size(); ++i) {
672 447 const std::string& arg = additional_args[i];
673
5/6
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 245 times.
✓ Branch 5 taken 202 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 202 times.
✓ Branch 8 taken 245 times.
447 if(arg[0] == '<' && arg[arg.length() - 1] == '>') {
674 202 ++max_unparsed;
675 245 } else if(
676 245 arg[0] == '<' &&
677
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 245 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 245 times.
245 arg[arg.length() - 2] == '>' &&
678 arg[arg.length() - 1] == '*'
679 ) {
680 min_unparsed=0;
681 max_unparsed=100000;
682 } else {
683 245 ++max_unparsed;
684 245 ++min_unparsed;
685 }
686 }
687
688 244 if(
689
6/6
✓ Branch 1 taken 241 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 237 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 237 times.
485 unparsed_args.size() > max_unparsed ||
690 241 unparsed_args.size() < min_unparsed
691 ) {
692
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 show_usage(additional_arg_specs);
693 7 return false;
694 }
695
696 #ifndef GEOGRAM_PSM
697
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 nlPrintfFuncs(geogram_printf, geogram_fprintf);
698
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 nlInitialize(argc, argv);
699 #endif
700 237 if(
701
5/14
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 237 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 237 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 237 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 237 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
1185 CmdLine::arg_is_declared("nl:CUDA") &&
702
6/16
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 237 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 237 times.
✓ Branch 8 taken 237 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 237 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 237 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
711 CmdLine::get_arg_bool("nl:CUDA")
703 ) {
704 geo_cite("DBLP:journals/paapp/BuatoisCL09");
705 }
706
707 // Re-initialize stopwatch so that it will enable
708 // global log if sys:stats is set.
709
1/2
✓ Branch 1 taken 237 times.
✗ Branch 2 not taken.
237 Stopwatch::initialize();
710
711 237 return true;
712 244 }
713
714 2 bool parse(int argc, char** argv) {
715 2 std::vector<std::string> unparsed_args;
716
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 return parse(argc, argv, unparsed_args, "");
717 2 }
718
719 1915 void declare_arg_group(
720 const std::string& name,
721 const std::string& description,
722 ArgFlags flags
723 ) {
724
2/4
✓ Branch 2 taken 1915 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1915 times.
1915 if(desc_->groups.find(name) != desc_->groups.end()) {
725 Logger::err("CmdLine")
726 << "Group is multiply defined: " << name
727 << std::endl;
728 return;
729 }
730
731 1915 Group group;
732
1/2
✓ Branch 1 taken 1915 times.
✗ Branch 2 not taken.
1915 group.name = name;
733
1/2
✓ Branch 1 taken 1915 times.
✗ Branch 2 not taken.
1915 group.desc = description;
734 1915 group.flags = flags;
735
2/4
✓ Branch 1 taken 1915 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1915 times.
✗ Branch 5 not taken.
1915 desc_->groups[name] = group;
736
1/2
✓ Branch 1 taken 1915 times.
✗ Branch 2 not taken.
1915 desc_->group_names.push_back(name);
737 1915 }
738
739 11233 void declare_arg(
740 const std::string& name,
741 ArgType type,
742 const std::string& default_value,
743 const std::string& description,
744 ArgFlags flags
745 ) {
746
2/4
✓ Branch 2 taken 11233 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 11233 times.
11233 if(desc_->args.find(name) != desc_->args.end()) {
747 Logger::err("CmdLine")
748 << "Argument is multiply defined: " << name
749 << std::endl;
750 return;
751 }
752
753 11233 Arg arg;
754
1/2
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
11233 arg.name = name;
755 11233 arg.type = type;
756
1/2
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
11233 arg.desc = description;
757 11233 arg.flags = flags;
758
2/4
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11233 times.
✗ Branch 5 not taken.
11233 desc_->args[name] = arg;
759
760
2/4
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11233 times.
✗ Branch 5 not taken.
11233 Environment::instance()->set_value(name, default_value);
761
762
1/2
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
11233 std::string group = arg_group(name);
763
1/2
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
11233 auto it = desc_->groups.find(group);
764
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 11233 times.
11233 if(it == desc_->groups.end()) {
765 Logger::err("CmdLine")
766 << "Argument group does not exist: " << name
767 << std::endl;
768 return;
769 }
770
771
1/2
✓ Branch 2 taken 11233 times.
✗ Branch 3 not taken.
11233 it->second.args.push_back(name);
772
2/4
✓ Branch 1 taken 11233 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11233 times.
✗ Branch 5 not taken.
11233 }
773
774 7962 ArgType get_arg_type(const std::string& name) {
775
1/2
✓ Branch 1 taken 7962 times.
✗ Branch 2 not taken.
7962 auto it = desc_->args.find(name);
776 15924 return it == desc_->args.end()
777
2/2
✓ Branch 0 taken 3083 times.
✓ Branch 1 taken 4879 times.
7962 ? ARG_UNDEFINED
778 15924 : it->second.type;
779 }
780
781 std::string get_arg_desc(const std::string& name) {
782 auto it = desc_->args.find(name);
783 return it == desc_->args.end()
784 ? ""
785 : it->second.desc;
786 }
787
788 7864 std::string get_arg(const std::string& name) {
789 7864 Process::acquire_spinlock(lock);
790 7864 std::string result = Environment::instance()->get_value(name);
791 7864 Process::release_spinlock(lock);
792 7864 return result;
793 }
794
795 1466 bool arg_is_declared(const std::string& name) {
796 1466 return get_arg_type(name) != ARG_UNDEFINED;
797 }
798
799 72 int get_arg_int(const std::string& name) {
800 72 ArgType type = get_arg_type(name);
801
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
72 geo_assert_arg_type(type, ARG_INT);
802
2/4
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 72 times.
✗ Branch 5 not taken.
72 return String::to_int(get_arg(name));
803 }
804
805 186 unsigned int get_arg_uint(const std::string& name) {
806 186 ArgType type = get_arg_type(name);
807
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 186 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
186 geo_assert_arg_type(type, ARG_INT);
808
2/4
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 186 times.
✗ Branch 5 not taken.
186 return String::to_uint(get_arg(name));
809 }
810
811 125 double get_arg_double(const std::string& name) {
812 125 ArgType type = get_arg_type(name);
813
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 125 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
125 geo_assert_arg_type(type, ARG_DOUBLE);
814
2/4
✓ Branch 1 taken 125 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 125 times.
✗ Branch 5 not taken.
125 return String::to_double(get_arg(name));
815 }
816
817 149 double get_arg_percent(
818 const std::string& name, double reference
819 ) {
820
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 ArgType type = get_arg_type(name);
821
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 149 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
149 geo_assert_arg_type(type, ARG_PERCENT);
822 double result;
823
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 std::string s = get_arg(name);
824
4/8
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 149 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 149 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 149 times.
✗ Branch 10 not taken.
149 if(s.length() > 0 && s[s.length() - 1] == '%') {
825
1/2
✓ Branch 2 taken 149 times.
✗ Branch 3 not taken.
149 s.resize(s.length() - 1);
826
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 result = String::to_double(s) * reference * 0.01;
827
2/4
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149 times.
✗ Branch 5 not taken.
298 Logger::out("CmdLine")
828
4/8
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 149 times.
✗ Branch 11 not taken.
149 << "using " << name << "=" << result
829
4/8
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 149 times.
✗ Branch 11 not taken.
298 << "(" << get_arg(name) << ")"
830
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 << std::endl;
831 } else {
832 result = String::to_double(s);
833 Logger::out("CmdLine")
834 << "using " << name << "=" << result
835 << std::endl;
836 }
837 149 return result;
838 149 }
839
840 2192 bool get_arg_bool(const std::string& name) {
841 2192 ArgType type = get_arg_type(name);
842
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 2192 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
2192 geo_assert_arg_type(type, ARG_BOOL);
843
4/6
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2192 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2147 times.
✓ Branch 7 taken 45 times.
4339 return Environment::instance()->has_value(name) &&
844
6/10
✓ Branch 1 taken 2147 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2147 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 595 times.
✓ Branch 7 taken 1552 times.
✓ Branch 8 taken 2147 times.
✓ Branch 9 taken 45 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
4339 String::to_bool(get_arg(name));
845 }
846
847 2270 bool set_arg(const std::string& name, const std::string& value) {
848
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2268 times.
2270 if(!check_arg_value(name, value)) {
849 2 return false;
850 }
851 2268 Process::acquire_spinlock(lock);
852 2268 Environment::instance()->set_value(name, value);
853 2268 Process::release_spinlock(lock);
854 2268 return true;
855 }
856
857 228 void set_arg(const std::string& name, Numeric::int32 value) {
858 228 ArgType type = get_arg_type(name);
859
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 228 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
228 geo_assert_arg_type(
860 type, ARG_INT | ARG_DOUBLE | ARG_PERCENT | ARG_STRING
861 );
862
2/4
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 228 times.
✗ Branch 5 not taken.
228 set_arg(name, String::to_string(value));
863 228 }
864
865 void set_arg(const std::string& name, Numeric::uint32 value) {
866 ArgType type = get_arg_type(name);
867 geo_assert_arg_type(
868 type, ARG_INT | ARG_DOUBLE | ARG_PERCENT | ARG_STRING
869 );
870 set_arg(name, String::to_string(value));
871 }
872
873 void set_arg(const std::string& name, Numeric::int64 value) {
874 ArgType type = get_arg_type(name);
875 geo_assert_arg_type(
876 type, ARG_INT | ARG_DOUBLE | ARG_PERCENT | ARG_STRING
877 );
878 set_arg(name, String::to_string(value));
879 }
880
881 18 void set_arg(const std::string& name, Numeric::uint64 value) {
882 18 ArgType type = get_arg_type(name);
883
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
18 geo_assert_arg_type(
884 type, ARG_INT | ARG_DOUBLE | ARG_PERCENT | ARG_STRING
885 );
886
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 set_arg(name, String::to_string(value));
887 18 }
888
889 228 void set_arg(const std::string& name, double value) {
890 228 ArgType type = get_arg_type(name);
891
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 228 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
228 geo_assert_arg_type(type, ARG_DOUBLE | ARG_PERCENT | ARG_STRING);
892
2/4
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 228 times.
✗ Branch 5 not taken.
228 set_arg(name, String::to_string(value));
893 228 }
894
895 272 void set_arg(const std::string& name, bool value) {
896 272 ArgType type = get_arg_type(name);
897
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 272 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
272 geo_assert_arg_type(type, ARG_BOOL | ARG_STRING);
898
2/4
✓ Branch 1 taken 272 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 272 times.
✗ Branch 5 not taken.
272 set_arg(name, String::to_string(value));
899 272 }
900
901 686 void set_arg_percent(const std::string& name, double value) {
902 686 ArgType type = get_arg_type(name);
903
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 686 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
686 geo_assert_arg_type(type, ARG_PERCENT | ARG_STRING);
904
3/6
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 686 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 686 times.
✗ Branch 8 not taken.
686 set_arg(name, String::to_string(value) + "%");
905 686 }
906
907 void get_arg_groups(std::vector<std::string>& groups) {
908 groups.clear();
909 for(auto& it : desc_->group_names) {
910 groups.push_back(it);
911 }
912 }
913
914 void get_arg_names_in_group(
915 const std::string& group, std::vector<std::string>& arg_names
916 ) {
917 arg_names.clear();
918 auto it = desc_->groups.find(group);
919 if(it == desc_->groups.end()) {
920 return;
921 }
922 const Group& g = it->second;
923 for(auto jt: g.args) {
924 arg_names.push_back(jt);
925 }
926 }
927
928 7 void show_usage(const std::string& additional_args, bool advanced) {
929
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 std::string program_name = FileSystem::base_name(desc_->argv0);
930
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
7 Logger::instance()->set_quiet(false);
931
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 Logger::out("")
932 << "Usage: " << program_name << " "
933 << additional_args
934
6/12
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 7 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 7 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 17 not taken.
7 << " <parameter=value>*" << std::endl;
935
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(!advanced) {
936
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
21 Logger::out("")
937 << "Showing basic parameters (use " << program_name
938
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
7 << " -h to see advanced parameters)"
939
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 << std::endl;
940 }
941
942
2/2
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 7 times.
73 for(auto& it : desc_->group_names) {
943
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 show_group(it, advanced);
944 }
945 7 }
946
947 105 void get_args(std::vector<std::string>& args) {
948 105 args.clear();
949
2/2
✓ Branch 5 taken 4363 times.
✓ Branch 6 taken 105 times.
4468 for(auto& it : desc_->args) {
950
3/6
✓ Branch 1 taken 4363 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4363 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4363 times.
✗ Branch 8 not taken.
4363 std::string cur_arg = it.first + "=" + get_arg(it.first);
951
1/2
✓ Branch 1 taken 4363 times.
✗ Branch 2 not taken.
4363 args.push_back(cur_arg);
952 4363 }
953 105 }
954 }
955 }
956
957 // =================== Console logging utilies =====================
958
959 namespace {
960
961 using namespace GEO;
962 using namespace CmdLine;
963
964 /** Keeps length of a feature name */
965 bool ui_separator_opened = false;
966
967 /** Width of the current terminal */
968 index_t ui_term_width = 79;
969
970 /** Terminal left margin */
971 index_t ui_left_margin = 0;
972
973 /** Terminal right margin */
974 index_t ui_right_margin = 0;
975
976 /** Characters of the progress wheel */
977 const char working[] = {'|', '/', '-', '\\'};
978
979 /** Current index in the progress wheel */
980 index_t working_index = 0;
981
982 /** Characters of the progress wave */
983 const char waves[] = {',', '.', 'o', 'O', '\'', 'O', 'o', '.', ','};
984
985 /**
986 * \brief Gets the console output stream
987 * \return a reference to a std::ostream
988 */
989 6468 inline std::ostream& ui_out() {
990 6468 return std::cout;
991 }
992
993 /**
994 * \brief Prints a sequence of identical chars
995 * \param[in] c the character to print
996 * \param[in] nb the number of characters to print
997 */
998 inline void ui_pad(char c, size_t nb) {
999 for(index_t i = 0; i < nb; i++) {
1000 std::cout << c;
1001 }
1002 }
1003
1004 /**
1005 * \brief Checks if the standard output is redirected
1006 * \details It is used by the various console formatting functions to
1007 * determine if messages are printed in pretty mode or not.
1008 * \retval true if the console is redirected to a file
1009 * \retval false otherwise
1010 */
1011 25453 bool is_redirected() {
1012 static bool initialized = false;
1013 static bool result;
1014
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 25204 times.
25453 if(!initialized) {
1015 #ifdef GEO_OS_WINDOWS
1016 result = !_isatty(1);
1017 #else
1018 249 result = !isatty(1);
1019 #endif
1020 249 initialized = true;
1021 }
1022
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 25453 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
25453 return result || !Logger::instance()->is_pretty();
1023 }
1024
1025 /**
1026 * \brief Recomputes the width of the terminal
1027 */
1028 53 void update_ui_term_width() {
1029 #ifdef GEO_OS_EMSCRIPTEN
1030 return; // ioctl not implemented under emscripten
1031 #else
1032 #ifndef GEO_OS_WINDOWS
1033
2/4
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 53 times.
✗ Branch 4 not taken.
53 if(is_redirected()) {
1034 53 return;
1035 }
1036 struct winsize w;
1037 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1038 ui_term_width = w.ws_col;
1039 if(ui_term_width < 20) {
1040 ui_term_width = 79;
1041 }
1042 if(ui_term_width <= 82) {
1043 ui_left_margin = 0;
1044 ui_right_margin = 0;
1045 } else if(ui_term_width < 90) {
1046 ui_left_margin = 2;
1047 ui_right_margin = 2;
1048 } else {
1049 ui_left_margin = 4;
1050 ui_right_margin = 4;
1051 }
1052 #endif
1053 #endif
1054 }
1055
1056 /**
1057 * \brief Safe unsigned subtraction
1058 * \return a - b if a > b, 0 otherwise
1059 */
1060 inline size_t sub(size_t a, size_t b) {
1061 return a > b ? a - b : 0;
1062 }
1063 }
1064
1065 namespace GEO {
1066
1067 namespace CmdLine {
1068
1069 53 index_t ui_terminal_width() {
1070 53 index_t ui_term_width_bkp = ui_term_width;
1071
1/2
✓ Branch 1 taken 53 times.
✗ Branch 2 not taken.
53 update_ui_term_width();
1072 53 ui_term_width = std::min(ui_term_width, ui_term_width_bkp);
1073 53 return ui_term_width;
1074 }
1075
1076 5850 void ui_separator() {
1077
3/6
✓ Branch 2 taken 5850 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5850 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 5850 times.
✗ Branch 8 not taken.
5850 if(Logger::instance()->is_quiet() || is_redirected()) {
1078 5850 return;
1079 }
1080
1081 update_ui_term_width();
1082 ui_separator_opened = true;
1083
1084 ui_out() << " ";
1085 ui_pad(' ', ui_left_margin);
1086 ui_pad(
1087 '_',
1088 sub(ui_terminal_width(), 2 + ui_left_margin + ui_right_margin)
1089 );
1090 ui_out() << " " << std::endl;
1091
1092 // Force a blank line under the separator
1093 ui_message("\n");
1094 }
1095
1096 309 void ui_separator(
1097 const std::string& title,
1098 const std::string& short_title
1099 ) {
1100
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 309 times.
309 if(Logger::instance()->is_quiet()) {
1101 return;
1102 }
1103
1104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 309 times.
309 if(man_mode) {
1105 if(title == "") {
1106 return;
1107 }
1108 ui_out() << std::endl;
1109 std::string shortt = short_title;
1110 if(shortt.length() > 0 && shortt[0] == '*') {
1111 shortt = shortt.substr(1, shortt.length()-1);
1112 ui_out() << title << " (\"" << shortt << ":*\" options, advanced)"
1113 << std::endl;
1114 } else {
1115 ui_out() << title << " (\"" << shortt << ":*\" options)"
1116 << std::endl;
1117 }
1118 ui_out() << std::endl << std::endl;
1119 return;
1120 }
1121
1122
1/2
✓ Branch 1 taken 309 times.
✗ Branch 2 not taken.
309 if(is_redirected()) {
1123 309 ui_out() << std::endl;
1124
6/6
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 295 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 302 times.
309 if(short_title != "" && title != "") {
1125 7 ui_out() << "=[" << short_title << "]=["
1126 7 << title << "]=" << std::endl;
1127 } else {
1128
1/2
✓ Branch 1 taken 302 times.
✗ Branch 2 not taken.
302 std::string s = title + short_title;
1129
4/8
✓ Branch 2 taken 302 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 302 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 302 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 302 times.
✗ Branch 12 not taken.
302 ui_out() << "=[" << s << "]=" << std::endl;
1130 302 }
1131 309 return;
1132 }
1133
1134 update_ui_term_width();
1135 ui_separator_opened = true;
1136
1137 size_t L = title.length() + short_title.length();
1138
1139 ui_out() << " ";
1140 ui_pad(' ', ui_left_margin);
1141 ui_pad('_', L + 14);
1142 ui_out() << std::endl;
1143
1144 ui_pad(' ', ui_left_margin);
1145 if(short_title != "" && title != "") {
1146 ui_out() << " _/ ==[" << short_title << "]====["
1147 << title << "]== \\";
1148 } else {
1149 std::string s = title + short_title;
1150 ui_out() << " _/ =====[" << s << "]===== \\";
1151 }
1152
1153 ui_pad(
1154 '_',
1155 sub(
1156 ui_terminal_width(),
1157 19 + L + ui_left_margin + ui_right_margin
1158 )
1159 );
1160 ui_out() << std::endl;
1161
1162 // Force a blank line under the separator
1163 ui_message("\n");
1164 }
1165
1166 5780 void ui_message(const std::string& message) {
1167 // By default, wrap to the column that is right after the feature
1168 // name and its decorations.
1169 5780 ui_message(message, feature_max_length + 5);
1170 5780 }
1171
1172 5850 void ui_message(
1173 const std::string& message,
1174 index_t wrap_margin
1175 ) {
1176
2/4
✓ Branch 1 taken 5850 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 5850 times.
5850 if(Logger::instance()->is_quiet()) {
1177 5850 return;
1178 }
1179
1180
1/2
✓ Branch 0 taken 5850 times.
✗ Branch 1 not taken.
5850 if(!ui_separator_opened) {
1181
1/2
✓ Branch 1 taken 5850 times.
✗ Branch 2 not taken.
5850 ui_separator();
1182 }
1183
1184
2/4
✓ Branch 1 taken 5850 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5850 times.
✗ Branch 4 not taken.
5850 if(is_redirected()) {
1185
1/2
✓ Branch 2 taken 5850 times.
✗ Branch 3 not taken.
5850 ui_out() << message;
1186 5850 return;
1187 }
1188
1189 std::string cur = message;
1190 size_t maxL =
1191 sub(ui_terminal_width(), 4 + ui_left_margin + ui_right_margin);
1192 index_t wrap = 0;
1193
1194 for(;;) {
1195 std::size_t newline = cur.find('\n');
1196 if(newline != std::string::npos && newline < maxL) {
1197 // Got a new line that occurs before the right border
1198 // We cut before the newline and pad with spaces
1199 ui_pad(' ', ui_left_margin);
1200 ui_out() << "| ";
1201 ui_pad(' ', wrap);
1202 ui_out() << cur.substr(0, newline);
1203 ui_pad(' ', sub(maxL,newline));
1204 ui_out() << " |" << std::endl;
1205 cur = cur.substr(newline + 1);
1206 } else if(cur.length() > maxL) {
1207 // The line length runs past the right border
1208 // We cut the string just before the border
1209 ui_pad(' ', ui_left_margin);
1210 ui_out() << "| ";
1211 ui_pad(' ', wrap);
1212 ui_out() << cur.substr(0, maxL);
1213 ui_out() << " |" << std::endl;
1214 cur = cur.substr(maxL);
1215 } else if(cur.length() != 0) {
1216 // Print the remaining portion of the string
1217 // and pad with spaces
1218 ui_pad(' ', ui_left_margin);
1219 ui_out() << "| ";
1220 ui_pad(' ', wrap);
1221 ui_out() << cur;
1222 ui_pad(' ', sub(maxL,cur.length()));
1223 ui_out() << " |";
1224 break;
1225 } else {
1226 // No more chars to print
1227 break;
1228 }
1229
1230 if(wrap == 0) {
1231 wrap = wrap_margin;
1232 maxL = sub(maxL,wrap_margin);
1233 }
1234 }
1235 }
1236
1237 44 void ui_clear_line() {
1238
3/6
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 44 times.
✗ Branch 8 not taken.
44 if(Logger::instance()->is_quiet() || is_redirected()) {
1239 44 return;
1240 }
1241
1242 ui_pad('\b', ui_terminal_width());
1243 ui_out() << std::flush;
1244 }
1245
1246 249 void ui_close_separator() {
1247
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 if(!ui_separator_opened) {
1248 249 return;
1249 }
1250
1251 if(Logger::instance()->is_quiet() || is_redirected()) {
1252 return;
1253 }
1254
1255 ui_pad(' ', ui_left_margin);
1256 ui_out() << '\\';
1257 ui_pad(
1258 '_',
1259 sub(ui_terminal_width(), 2 + ui_left_margin + ui_right_margin)
1260 );
1261 ui_out() << '/';
1262 ui_out() << std::endl;
1263
1264 ui_separator_opened = false;
1265 }
1266
1267 1041 void ui_progress(
1268 const std::string& task_name, index_t val, index_t percent,
1269 bool clear
1270 ) {
1271
5/10
✓ Branch 1 taken 1041 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1041 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1041 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1041 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1041 times.
✗ Branch 12 not taken.
1041 if(Logger::instance()->is_quiet() || is_redirected()) {
1272 1041 return;
1273 }
1274
1275 working_index++;
1276
1277 std::ostringstream os;
1278
1279 if(percent != val) {
1280 os << ui_feature(task_name)
1281 << "("
1282 << working[(working_index % sizeof(working))]
1283 << ")-["
1284 << std::setw(3) << percent
1285 << "%]-["
1286 << std::setw(3) << val
1287 << "]--[";
1288 } else {
1289 os << ui_feature(task_name)
1290 << "("
1291 << working[(working_index % sizeof(working))]
1292 << ")-["
1293 << std::setw(3) << percent
1294 << "%]--------[";
1295 }
1296
1297 size_t max_L =
1298 sub(ui_terminal_width(), 43 + ui_left_margin + ui_right_margin);
1299
1300 max_L -= size_t(std::log10(std::max(double(val),1.0)));
1301 max_L += 2;
1302
1303 if(val > max_L) {
1304 // No space enough to expand the progress bar
1305 // Do some animation...
1306 for(index_t i = 0; i < max_L; i++) {
1307 os << waves[((val - i + working_index) % sizeof(waves))];
1308 }
1309 } else {
1310 for(index_t i = 0; i < val; i++) {
1311 os << "o";
1312 }
1313 }
1314 os << " ]";
1315
1316 if(clear) {
1317 ui_clear_line();
1318 }
1319 ui_message(os.str());
1320 }
1321
1322 37 void ui_progress_time(
1323 const std::string& task_name, double elapsed, bool clear
1324 ) {
1325
2/4
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 37 times.
37 if(Logger::instance()->is_quiet()) {
1326 return;
1327 }
1328
1329
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 std::ostringstream os;
1330
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
74 os << ui_feature(task_name)
1331
3/6
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 37 times.
✗ Branch 8 not taken.
37 << "Elapsed time: " << elapsed
1332
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 << "s\n";
1333
1334
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if(clear) {
1335
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 ui_clear_line();
1336 }
1337
2/4
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37 times.
✗ Branch 5 not taken.
37 ui_message(os.str());
1338 37 }
1339
1340 void ui_progress_canceled(
1341 const std::string& task_name,
1342 double elapsed, index_t percent, bool clear
1343 ) {
1344 if(Logger::instance()->is_quiet()) {
1345 return;
1346 }
1347
1348 std::ostringstream os;
1349 os << ui_feature(task_name)
1350 << "Task canceled after " << elapsed
1351 << "s (" << percent << "%)\n";
1352
1353 if(clear) {
1354 ui_clear_line();
1355 }
1356 ui_message(os.str());
1357 }
1358
1359 12412 std::string ui_feature(
1360 const std::string& feat_in, bool show
1361 ) {
1362
2/2
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 12333 times.
12412 if(feat_in.empty()) {
1363
1/2
✓ Branch 1 taken 79 times.
✗ Branch 2 not taken.
79 return feat_in;
1364 }
1365
1366
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 12306 times.
12333 if(!show) {
1367
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
54 return std::string(feature_max_length + 5, ' ');
1368 }
1369
1370
1/2
✓ Branch 1 taken 12306 times.
✗ Branch 2 not taken.
12306 std::string result = feat_in;
1371
2/4
✓ Branch 1 taken 12306 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12306 times.
12306 if(!is_redirected()) {
1372 result = result.substr(0, feature_max_length);
1373 }
1374
2/2
✓ Branch 1 taken 12223 times.
✓ Branch 2 taken 83 times.
12306 if(result.length() < feature_max_length) {
1375
1/2
✓ Branch 2 taken 12223 times.
✗ Branch 3 not taken.
12223 result.append(feature_max_length - result.length(), ' ');
1376 }
1377
2/4
✓ Branch 1 taken 12306 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12306 times.
✗ Branch 5 not taken.
12306 return "o-[" + result + "] ";
1378 12306 }
1379 }
1380 }
1381
1382 #ifdef GEO_OS_ANDROID
1383 namespace {
1384 android_app* android_app_ = nullptr;
1385 }
1386
1387 namespace GEO {
1388 namespace CmdLine {
1389 void set_android_app(android_app* app) {
1390 android_app_ = app;
1391 }
1392
1393 android_app* get_android_app() {
1394 return android_app_;
1395 }
1396 }
1397 }
1398 #endif
1399