• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 29564081841

17 Jul 2026 07:09AM UTC coverage: 50.512% (+0.01%) from 50.498%
29564081841

push

github

web-flow
Merge pull request #2936 from mavlink/v3-backport

Backport various bugfixes to v3

162 of 257 new or added lines in 24 files covered. (63.04%)

56 existing lines in 15 files now uncovered.

19343 of 38294 relevant lines covered (50.51%)

669.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.51
/src/mavsdk/core/cli_arg.cpp
1
#include "cli_arg.h"
2
#include "log.h"
3
#include <cctype>
4
#include <algorithm>
5
#include <limits>
6
#include <cstdint>
7
#include <string>
8
#include <charconv>
9

10
namespace mavsdk {
11

12
bool CliArg::parse(const std::string& uri)
241✔
13
{
14
    // Reset
15
    protocol = {};
241✔
16

17
    const std::string udp = "udp";
241✔
18
    const std::string udpin = "udpin";
241✔
19
    const std::string udpout = "udpout";
241✔
20
    const std::string tcp = "tcp";
241✔
21
    const std::string tcpin = "tcpin";
241✔
22
    const std::string tcpout = "tcpout";
241✔
23
    const std::string serial = "serial";
241✔
24
    const std::string serial_flowcontrol = "serial_flowcontrol";
241✔
25
    const std::string delimiter = "://";
241✔
26

27
    if (uri.find(udp + delimiter) == 0) {
241✔
28
        LogWarn() << "Connection using udp:// is deprecated, please use udpin:// or udpout://";
11✔
29
        return parse_udp(std::string_view(uri).substr(udp.size() + delimiter.size()));
11✔
30
    }
31

32
    if (uri.find(udpin + delimiter) == 0) {
230✔
33
        return parse_udpin(std::string_view(uri).substr(udpin.size() + delimiter.size()));
84✔
34
    }
35

36
    if (uri.find(udpout + delimiter) == 0) {
146✔
37
        return parse_udpout(std::string_view(uri).substr(udpout.size() + delimiter.size()));
83✔
38
    }
39

40
    if (uri.find(tcp + delimiter) == 0) {
63✔
41
        LogWarn() << "Connection using tcp:// is deprecated, please use tcpin:// or tcpout://";
9✔
42
        return parse_tcp(std::string_view(uri).substr(tcp.size() + delimiter.size()));
9✔
43
    }
44

45
    if (uri.find(tcpin + delimiter) == 0) {
54✔
46
        return parse_tcpin(std::string_view(uri).substr(tcpin.size() + delimiter.size()));
7✔
47
    }
48

49
    if (uri.find(tcpout + delimiter) == 0) {
47✔
50
        return parse_tcpout(std::string_view(uri).substr(tcpout.size() + delimiter.size()));
7✔
51
    }
52

53
    if (uri.find(serial + delimiter) == 0) {
40✔
54
        return parse_serial(std::string_view(uri).substr(serial.size() + delimiter.size()), false);
13✔
55
    }
56

57
    if (uri.find(serial_flowcontrol + delimiter) == 0) {
27✔
58
        return parse_serial(
1✔
59
            std::string_view(uri).substr(serial_flowcontrol.size() + delimiter.size()), true);
2✔
60
    }
61

62
    const std::string raw = "raw";
26✔
63
    if (uri.find(raw + delimiter) == 0) {
26✔
64
        return parse_raw(std::string_view(uri).substr(raw.size() + delimiter.size()));
6✔
65
    }
66

67
    LogErr() << "Unknown protocol";
20✔
68
    return false;
20✔
69
}
241✔
70

71
bool CliArg::parse_udp(const std::string_view rest)
11✔
72
{
73
    const std::string delimiter = ":";
11✔
74
    size_t pos = rest.find(delimiter);
11✔
75
    if (pos == std::string::npos) {
11✔
76
        return false;
2✔
77
    }
78

79
    protocol = Udp{};
9✔
80
    auto& p = std::get<Udp>(protocol);
9✔
81
    p.host = rest.substr(0, pos);
9✔
82
    if (p.host.empty()) {
9✔
83
        p.host = "0.0.0.0";
1✔
84
        p.mode = Udp::Mode::In;
1✔
85
    } else if (p.host == "0.0.0.0") {
8✔
86
        p.mode = Udp::Mode::In;
4✔
87
    } else {
88
        p.mode = Udp::Mode::Out;
4✔
89
    }
90

91
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
9✔
92
        p.port = maybe_port.value();
7✔
93
        return true;
7✔
94
    } else {
95
        return false;
2✔
96
    }
97
}
11✔
98

99
bool CliArg::parse_udpin(const std::string_view rest)
84✔
100
{
101
    const std::string delimiter = ":";
84✔
102
    size_t pos = rest.find(delimiter);
84✔
103
    if (pos == std::string::npos) {
84✔
104
        return false;
×
105
    }
106

107
    protocol = Udp{};
84✔
108
    auto& p = std::get<Udp>(protocol);
84✔
109
    p.host = rest.substr(0, pos);
84✔
110
    p.mode = Udp::Mode::In;
84✔
111

112
    if (p.host.empty()) {
84✔
113
        LogErr() << "No network interface supplied (use 0.0.0.0 for all network interfaces)";
1✔
114
        return false;
1✔
115
    }
116

117
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
83✔
118
        p.port = maybe_port.value();
83✔
119
        return true;
83✔
120
    } else {
121
        return false;
×
122
    }
123
}
84✔
124

125
bool CliArg::parse_udpout(const std::string_view rest)
83✔
126
{
127
    const std::string delimiter = ":";
83✔
128
    size_t pos = rest.find(delimiter);
83✔
129
    if (pos == std::string::npos) {
83✔
130
        return false;
×
131
    }
132

133
    protocol = Udp{};
83✔
134
    auto& p = std::get<Udp>(protocol);
83✔
135
    p.host = rest.substr(0, pos);
83✔
136
    p.mode = Udp::Mode::Out;
83✔
137

138
    if (p.host == "0.0.0.0") {
83✔
139
        LogErr() << "0.0.0.0 is invalid for UDP out address. "
2✔
140
                    "Can only listen on all interfaces, but not send.";
1✔
141
        return false;
1✔
142
    }
143

144
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
82✔
145
        p.port = maybe_port.value();
82✔
146
        return true;
82✔
147
    } else {
148
        return false;
×
149
    }
150
}
83✔
151

152
bool CliArg::parse_tcp(const std::string_view rest)
9✔
153
{
154
    const std::string delimiter = ":";
9✔
155
    size_t pos = rest.find(delimiter);
9✔
156
    if (pos == std::string::npos) {
9✔
157
        return false;
2✔
158
    }
159

160
    protocol = Tcp{};
7✔
161
    auto& p = std::get<Tcp>(protocol);
7✔
162
    p.host = rest.substr(0, pos);
7✔
163
    if (p.host.empty()) {
7✔
164
        p.host = "0.0.0.0";
1✔
165
        p.mode = Tcp::Mode::In;
1✔
166
    } else if (p.host == "0.0.0.0") {
6✔
167
        p.mode = Tcp::Mode::In;
×
168
    } else {
169
        p.mode = Tcp::Mode::Out;
6✔
170
    }
171

172
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
7✔
173
        p.port = maybe_port.value();
5✔
174
        return true;
5✔
175
    } else {
176
        return false;
2✔
177
    }
178
}
9✔
179

180
bool CliArg::parse_tcpin(const std::string_view rest)
7✔
181
{
182
    const std::string delimiter = ":";
7✔
183
    size_t pos = rest.find(delimiter);
7✔
184
    if (pos == std::string::npos) {
7✔
185
        return false;
×
186
    }
187

188
    protocol = Tcp{};
7✔
189
    auto& p = std::get<Tcp>(protocol);
7✔
190
    p.host = rest.substr(0, pos);
7✔
191
    p.mode = Tcp::Mode::In;
7✔
192

193
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
7✔
194
        p.port = maybe_port.value();
7✔
195
        return true;
7✔
196
    } else {
197
        return false;
×
198
    }
199
}
7✔
200

201
bool CliArg::parse_tcpout(const std::string_view rest)
7✔
202
{
203
    const std::string delimiter = ":";
7✔
204
    size_t pos = rest.find(delimiter);
7✔
205
    if (pos == std::string::npos) {
7✔
206
        return false;
×
207
    }
208

209
    protocol = Tcp{};
7✔
210
    auto& p = std::get<Tcp>(protocol);
7✔
211
    p.host = rest.substr(0, pos);
7✔
212
    p.mode = Tcp::Mode::Out;
7✔
213

214
    if (p.host == "0.0.0.0") {
7✔
215
        LogErr() << "0.0.0.0 is invalid for TCP out address. "
2✔
216
                    "Can only listen on all interfaces, but not send.";
1✔
217
        return false;
1✔
218
    }
219

220
    if (auto maybe_port = port_from_str(rest.substr(pos + 1))) {
6✔
221
        p.port = maybe_port.value();
6✔
222
        return true;
6✔
223
    } else {
224
        return false;
×
225
    }
226
}
7✔
227

228
std::optional<int> CliArg::port_from_str(std::string_view str)
194✔
229
{
230
    int value;
231
    auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
194✔
232

233
    // from_chars stops at the first non-digit and reports success, so also require
234
    // that the whole string was consumed to reject inputs like "8080abc".
235
    if (static_cast<bool>(ec) || ptr != str.data() + str.size() || value < 1 || value > 65535) {
194✔
236
        return {};
4✔
237
    }
238
    return {value};
190✔
239
}
240

241
bool CliArg::parse_serial(const std::string_view rest, bool flow_control_enabled)
14✔
242
{
243
    protocol = Serial{};
14✔
244
    auto& p = std::get<Serial>(protocol);
14✔
245
    p.flow_control_enabled = flow_control_enabled;
14✔
246

247
    const std::string delimiter = ":";
14✔
248
    size_t pos = rest.find(delimiter);
14✔
249
    if (pos == std::string::npos) {
14✔
250
        return false;
4✔
251
    }
252

253
    p.path = rest.substr(0, pos);
10✔
254

255
    const auto path_is_only_numbers =
256
        std::all_of(p.path.begin(), p.path.end(), [](unsigned char c) { return std::isdigit(c); });
20✔
257

258
    if (path_is_only_numbers) {
10✔
259
        LogErr() << "Path can't be numbers only.";
×
260
        return false;
×
261
    }
262

263
    if (p.path.find('/') == 0) {
10✔
264
        // A Linux/macOS path needs to start with '/'.
265
    } else if (p.path.find("COM") == 0) {
7✔
266
        // On Windows a path starting with 'COM' is ok but needs to be followed by digits.
267
        for (const auto& digit : p.path.substr(3, p.path.length() - 3)) {
7✔
268
            if (!std::isdigit(digit)) {
3✔
269
                LogErr() << "COM port number invalid.";
×
270
                return false;
×
271
            }
272
        }
4✔
273

274
        if (p.path.length() == 3) {
4✔
275
            LogErr() << "COM port number missing";
2✔
276
            return false;
2✔
277
        }
278
    } else {
279
        LogErr() << "serial port needs to start with / or COM on Windows";
3✔
280
        return false;
3✔
281
    }
282

283
    auto baudrate_str = rest.substr(pos + 1);
5✔
284

285
    int value;
286
    auto [ptr, ec] =
287
        std::from_chars(baudrate_str.data(), baudrate_str.data() + baudrate_str.size(), value);
5✔
288

289
    // Reject trailing garbage (e.g. "57600xyz"); from_chars would otherwise accept it.
290
    if (static_cast<bool>(ec) || ptr != baudrate_str.data() + baudrate_str.size()) {
5✔
UNCOV
291
        return {};
×
292
    }
293

294
    if (value < 0) {
5✔
295
        LogErr() << "Baudrate can't be negative.";
1✔
296
        return false;
1✔
297
    }
298

299
    p.baudrate = value;
4✔
300

301
    return true;
4✔
302
}
14✔
303

304
bool CliArg::parse_raw(const std::string_view rest)
6✔
305
{
306
    // raw:// connection has no parameters
307
    if (!rest.empty()) {
6✔
308
        LogErr() << "raw:// connection should not have parameters";
3✔
309
        return false;
3✔
310
    }
311

312
    protocol = Raw{};
3✔
313
    return true;
3✔
314
}
315

316
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc