Skip to content

Commit f8e7825

Browse files
committed
fdsdump: add argparser component
1 parent 3978fe6 commit f8e7825

File tree

3 files changed

+341
-0
lines changed

3 files changed

+341
-0
lines changed

src/tools/fdsdump/src/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Create a common "object" library
22
set(COMMON_SRC
3+
argParser.cpp
34
common.cpp
45
field.cpp
56
fieldView.cpp
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief Argument parser component
5+
*/
6+
7+
#include <common/argParser.hpp>
8+
9+
#include <algorithm>
10+
#include <map>
11+
12+
#include <getopt.h>
13+
14+
namespace fdsdump {
15+
16+
bool Args::has(char short_opt) const {
17+
return std::any_of(m_named_args.begin(), m_named_args.end(), [=](const NamedArg &arg) { return arg.short_opt == short_opt; });
18+
}
19+
20+
bool Args::has(const std::string &long_opt) const {
21+
return std::any_of(m_named_args.begin(), m_named_args.end(), [&](const NamedArg &arg) { return arg.long_opt == long_opt; });
22+
}
23+
24+
bool Args::has(int pos) const {
25+
return pos < int(m_pos_args.size());
26+
}
27+
28+
bool Args::count(char short_opt) const {
29+
return std::count_if(m_named_args.begin(), m_named_args.end(), [=](const NamedArg &arg) { return arg.short_opt == short_opt; });
30+
}
31+
32+
bool Args::count(const std::string &long_opt) const {
33+
return std::count_if(m_named_args.begin(), m_named_args.end(), [&](const NamedArg &arg) { return arg.long_opt == long_opt; });
34+
}
35+
36+
bool Args::pos_count() const {
37+
return m_pos_args.size();
38+
}
39+
40+
std::string Args::get(char short_opt) const {
41+
auto it = std::find_if(m_named_args.rbegin(), m_named_args.rend(), [&](const NamedArg &arg) { return arg.short_opt == short_opt; });
42+
if (it == m_named_args.rend()) {
43+
return "";
44+
}
45+
return it->value;
46+
}
47+
48+
std::string Args::get(const std::string &long_opt) const {
49+
auto it = std::find_if(m_named_args.rbegin(), m_named_args.rend(), [&](const NamedArg &arg) { return arg.long_opt == long_opt; });
50+
if (it == m_named_args.rend()) {
51+
return "";
52+
}
53+
return it->value;
54+
}
55+
56+
std::string Args::get(int pos) const {
57+
if (!has(pos)) {
58+
return "";
59+
}
60+
return m_pos_args[pos];
61+
}
62+
63+
std::vector<std::string> Args::get_all(char short_opt) const {
64+
std::vector<std::string> values;
65+
for (const auto &arg : m_named_args) {
66+
if (arg.short_opt == short_opt) {
67+
values.push_back(arg.value);
68+
}
69+
}
70+
return values;
71+
}
72+
73+
std::vector<std::string> Args::get_all(const std::string &long_opt) const {
74+
std::vector<std::string> values;
75+
for (const auto &arg : m_named_args) {
76+
if (arg.long_opt == long_opt) {
77+
values.push_back(arg.value);
78+
}
79+
}
80+
return values;
81+
}
82+
83+
void ArgParser::add(char short_opt, const std::string &long_opt, bool requires_value) {
84+
m_defs.push_back({short_opt, long_opt, requires_value});
85+
}
86+
87+
void ArgParser::add(char short_opt, bool requires_value) {
88+
m_defs.push_back({short_opt, "", requires_value});
89+
}
90+
91+
void ArgParser::add(const std::string &long_opt, bool requires_value) {
92+
m_defs.push_back({0, long_opt, requires_value});
93+
}
94+
95+
Args ArgParser::parse(int argc, char **argv) const {
96+
std::string short_opts = ":";
97+
std::vector<option> long_opts;
98+
int next_nonchar_val = 256;
99+
100+
std::map<int, const ArgDef *> val_to_arg_map;
101+
102+
for (const auto &def : m_defs) {
103+
if (def.short_opt != 0) {
104+
short_opts.push_back(def.short_opt);
105+
if (def.requires_value) {
106+
short_opts.append(":");
107+
}
108+
}
109+
110+
option getopt_option = {};
111+
112+
if (!def.long_opt.empty()) {
113+
getopt_option.name = def.long_opt.c_str();
114+
}
115+
116+
if (def.short_opt == 0) {
117+
getopt_option.val = next_nonchar_val;
118+
next_nonchar_val++;
119+
} else {
120+
getopt_option.val = def.short_opt;
121+
}
122+
123+
if (def.requires_value) {
124+
getopt_option.has_arg = required_argument;
125+
} else {
126+
getopt_option.has_arg = no_argument;
127+
}
128+
129+
long_opts.push_back(getopt_option);
130+
val_to_arg_map.emplace(getopt_option.val, &def);
131+
}
132+
133+
long_opts.push_back({0, 0, 0, 0});
134+
135+
// Parse args
136+
Args parsed;
137+
opterr = 0;
138+
int opt;
139+
140+
while ((opt = getopt_long(argc, argv, short_opts.c_str(), long_opts.data(), nullptr)) != -1) {
141+
if (opt == '?') {
142+
throw ArgParser::UnknownArgument {argv[optind - 1]};
143+
}
144+
if (opt == ':') {
145+
throw ArgParser::MissingArgument {argv[optind - 1]};
146+
}
147+
148+
const ArgDef *def = val_to_arg_map[opt];
149+
150+
Args::NamedArg arg;
151+
arg.short_opt = def->short_opt;
152+
arg.long_opt = def->long_opt;
153+
if (optarg) {
154+
arg.value = optarg;
155+
}
156+
parsed.m_named_args.push_back(arg);
157+
}
158+
159+
// Collect remaining positional arguments
160+
for (int i = optind; i < argc; i++) {
161+
parsed.m_pos_args.push_back(argv[i]);
162+
}
163+
164+
return parsed;
165+
}
166+
167+
} // fdsdump
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/**
2+
* @file
3+
* @author Michal Sedlak <[email protected]>
4+
* @brief Argument parser component
5+
*
6+
* Copyright: (C) 2024 CESNET, z.s.p.o.
7+
* SPDX-License-Identifier: BSD-3-Clause
8+
*/
9+
10+
#pragma once
11+
12+
#include <string>
13+
#include <vector>
14+
15+
namespace fdsdump {
16+
17+
class Args {
18+
friend class ArgParser;
19+
20+
public:
21+
/**
22+
* @brief Check if a short option exists.
23+
*
24+
* @param short_opt The short option character to check.
25+
* @return true if the short option exists, false otherwise.
26+
*/
27+
bool has(char short_opt) const;
28+
29+
/**
30+
* @brief Check if a long option exists.
31+
*
32+
* @param long_opt The long option string to check.
33+
* @return true if the long option exists, false otherwise.
34+
*/
35+
bool has(const std::string &long_opt) const;
36+
37+
/**
38+
* @brief Check if a positional argument exists.
39+
*
40+
* @param pos The position index of the argument.
41+
* @return true if the positional argument exists, false otherwise.
42+
*/
43+
bool has(int pos) const;
44+
45+
/**
46+
* @brief Count the occurrences of a short option.
47+
*
48+
* @param short_opt The short option character to count.
49+
* @return The count of occurrences of the short option.
50+
*/
51+
bool count(char short_opt) const;
52+
53+
/**
54+
* @brief Count the occurrences of a long option.
55+
*
56+
* @param long_opt The long option string to count.
57+
* @return The count of occurrences of the long option.
58+
*/
59+
bool count(const std::string &long_opt) const;
60+
61+
/**
62+
* @brief Check if positional arguments exist.
63+
*
64+
* @return The count of positional arguments.
65+
*/
66+
bool pos_count() const;
67+
68+
/**
69+
* @brief Get the value of a short option.
70+
*
71+
* @param short_opt The short option character.
72+
* @return The value associated with the short option.
73+
*/
74+
std::string get(char short_opt) const;
75+
76+
/**
77+
* @brief Get the value of a long option.
78+
*
79+
* @param long_opt The long option string.
80+
* @return The value associated with the long option.
81+
*/
82+
std::string get(const std::string &long_opt) const;
83+
84+
/**
85+
* @brief Get the positional argument at a specified index.
86+
*
87+
* @param pos The position index of the argument.
88+
* @return The value of the positional argument.
89+
*/
90+
std::string get(int pos) const;
91+
92+
/**
93+
* @brief Get all values associated with a short option.
94+
*
95+
* @param short_opt The short option character.
96+
* @return A vector containing all values associated with the short option.
97+
*/
98+
std::vector<std::string> get_all(char short_opt) const;
99+
100+
/**
101+
* @brief Get all values associated with a long option.
102+
*
103+
* @param long_opt The long option string.
104+
* @return A vector containing all values associated with the long option.
105+
*/
106+
std::vector<std::string> get_all(const std::string &long_opt) const;
107+
108+
private:
109+
struct NamedArg {
110+
char short_opt;
111+
std::string long_opt;
112+
std::string value;
113+
};
114+
115+
std::vector<NamedArg> m_named_args;
116+
std::vector<std::string> m_pos_args;
117+
};
118+
119+
class ArgParser {
120+
public:
121+
struct UnknownArgument {
122+
std::string arg;
123+
};
124+
125+
struct MissingArgument {
126+
std::string arg;
127+
};
128+
129+
/**
130+
* @brief Add a new option with a short option, a long option, and indication if it requires a value.
131+
*
132+
* @param short_opt The short option character.
133+
* @param long_opt The long option string.
134+
* @param requires_value Indicates whether the option requires a value.
135+
*/
136+
void add(char short_opt, const std::string &long_opt, bool requires_value);
137+
138+
/**
139+
* @brief Add a new option with a short option and indication if it requires a value.
140+
*
141+
* @param short_opt The short option character.
142+
* @param requires_value Indicates whether the option requires a value.
143+
*/
144+
void add(char short_opt, bool requires_value);
145+
146+
/**
147+
* @brief Add a new option with a long option and indication if it requires a value.
148+
*
149+
* @param long_opt The long option string.
150+
* @param requires_value Indicates whether the option requires a value.
151+
*/
152+
void add(const std::string &long_opt, bool requires_value);
153+
154+
/**
155+
* @brief Parse the command-line arguments.
156+
*
157+
* @param argc The number of command-line arguments.
158+
* @param argv The array of command-line argument strings.
159+
* @return Args object containing parsed arguments.
160+
*/
161+
Args parse(int argc, char **argv) const;
162+
163+
private:
164+
struct ArgDef {
165+
char short_opt;
166+
std::string long_opt;
167+
bool requires_value;
168+
};
169+
170+
std::vector<ArgDef> m_defs;
171+
};
172+
173+
} // fdsdump

0 commit comments

Comments
 (0)