Skip to content

Commit fdc0e3a

Browse files
committed
Bluetooth: Mesh: Shell util lib
Adds mesh shell string conversion API. Implements basic string to parameter convertion functions with error checking. Signed-off-by: Anders Storrø <[email protected]>
1 parent ca217fe commit fdc0e3a

File tree

7 files changed

+522
-0
lines changed

7 files changed

+522
-0
lines changed

subsys/bluetooth/mesh/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,6 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_HEALTH_CLI health_cli.c)
6060
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SELF_TEST test.c)
6161

6262
zephyr_library_sources_ifdef(CONFIG_BT_MESH_SHELL shell.c)
63+
zephyr_library_sources(shell_util.c)
6364

6465
zephyr_library_sources_ifdef(CONFIG_BT_MESH_CDB cdb.c)

subsys/bluetooth/mesh/shell_util.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Copyright (c) 2022 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <ctype.h>
8+
#include <string.h>
9+
#include <stdlib.h>
10+
#include <stdbool.h>
11+
#include <errno.h>
12+
#include "shell_util.h"
13+
14+
static size_t whitespace_trim(char *out, size_t len, const char *str)
15+
{
16+
if (len == 0) {
17+
return 0;
18+
}
19+
20+
const char *end;
21+
size_t out_size;
22+
23+
while (str[0] == ' ') {
24+
str++;
25+
}
26+
27+
if (*str == 0) {
28+
*out = 0;
29+
return 1;
30+
}
31+
32+
end = str + strlen(str) - 1;
33+
while (end > str && (end[0] == ' ')) {
34+
end--;
35+
}
36+
end++;
37+
38+
out_size = (end - str) + 1;
39+
40+
if (out_size > len) {
41+
return 0;
42+
}
43+
44+
memcpy(out, str, out_size - 1);
45+
out[out_size - 1] = 0;
46+
47+
return out_size;
48+
}
49+
50+
int bt_mesh_str2l(const char *str, long *val)
51+
{
52+
char *endptr = NULL;
53+
char c;
54+
55+
errno = 0;
56+
*val = strtol(str, &endptr, 10);
57+
if (errno == ERANGE) {
58+
return -ERANGE;
59+
} else if (errno || endptr == str) {
60+
return -EINVAL;
61+
}
62+
63+
64+
while ((c = *endptr++)) {
65+
if (!isspace(c)) {
66+
return -EINVAL;
67+
}
68+
}
69+
70+
return 0;
71+
}
72+
73+
int bt_mesh_str2ul(const char *str, unsigned long *val)
74+
{
75+
char *endptr = NULL;
76+
char c;
77+
78+
while (isspace(*str)) {
79+
str++;
80+
}
81+
if (*str == '-') {
82+
return -EINVAL;
83+
}
84+
85+
errno = 0;
86+
*val = strtoul(str, &endptr, 10);
87+
if (errno == ERANGE) {
88+
return -ERANGE;
89+
} else if (errno || endptr == str) {
90+
return -EINVAL;
91+
}
92+
93+
while ((c = *endptr++)) {
94+
if (!isspace(c)) {
95+
return -EINVAL;
96+
}
97+
}
98+
99+
return 0;
100+
}
101+
102+
int bt_mesh_str2dbl(const char *str, double *val)
103+
{
104+
char trimmed_buf[22] = { 0 };
105+
long intgr;
106+
unsigned long frac;
107+
double frac_dbl;
108+
int err = 0;
109+
110+
size_t len = whitespace_trim(trimmed_buf, sizeof(trimmed_buf), str);
111+
112+
if (len < 2) {
113+
return -EINVAL;
114+
}
115+
116+
int comma_idx = strcspn(trimmed_buf, ".");
117+
int frac_len = strlen(trimmed_buf + comma_idx + 1);
118+
119+
/* Covers corner case "." input */
120+
if (strlen(trimmed_buf) < 2 && trimmed_buf[comma_idx] != 0) {
121+
return -EINVAL;
122+
}
123+
124+
trimmed_buf[comma_idx] = 0;
125+
126+
/* Avoid fractional overflow by losing one precision point */
127+
if (frac_len > 9) {
128+
trimmed_buf[comma_idx + 10] = 0;
129+
frac_len = 9;
130+
}
131+
132+
/* Avoid doing str2long if intgr part is empty or only single sign char*/
133+
if (trimmed_buf[0] == '\0' ||
134+
(trimmed_buf[1] == '\0' && (trimmed_buf[0] == '+' || trimmed_buf[0] == '-'))) {
135+
intgr = 0;
136+
} else {
137+
err = bt_mesh_str2l(trimmed_buf, &intgr);
138+
139+
if (err) {
140+
return err;
141+
}
142+
}
143+
144+
/* Avoid doing str2ulong if fractional part is empty */
145+
if ((trimmed_buf + comma_idx + 1)[0] == '\0') {
146+
frac = 0;
147+
} else {
148+
err = bt_mesh_str2ul(trimmed_buf + comma_idx + 1, &frac);
149+
150+
if (err) {
151+
return err;
152+
}
153+
}
154+
155+
frac_dbl = (double)frac;
156+
157+
for (int i = 0; i < frac_len; i++) {
158+
frac_dbl /= 10;
159+
}
160+
161+
*val = (trimmed_buf[0] == '-') ? ((double)intgr - frac_dbl) :
162+
((double)intgr + frac_dbl);
163+
164+
return err;
165+
}
166+
167+
int bt_mesh_str2bool(const char *str, bool *val)
168+
{
169+
long temp;
170+
171+
if (!strcmp(str, "on") || !strcmp(str, "enable") || !strcmp(str, "true")) {
172+
*val = true;
173+
return 0;
174+
}
175+
176+
if (!strcmp(str, "off") || !strcmp(str, "disable") || !strcmp(str, "false")) {
177+
*val = false;
178+
return 0;
179+
}
180+
181+
int err = bt_mesh_str2l(str, &temp);
182+
183+
if (err) {
184+
return err;
185+
}
186+
187+
*val = (bool)temp;
188+
return 0;
189+
}

subsys/bluetooth/mesh/shell_util.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2022 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef BT_MESH_SHELL_UTIL_H__
8+
#define BT_MESH_SHELL_UTIL_H__
9+
10+
#include <stdint.h>
11+
12+
#ifdef __cplusplus
13+
extern "C" {
14+
#endif
15+
16+
/*
17+
* @brief Convert string to long param.
18+
*
19+
* @note On failure the passed value reference will not be altered.
20+
*
21+
* @param str Input string
22+
* @param val Converted value
23+
*
24+
* @return 0 on success.
25+
* @return -EINVAL on invalid string input.
26+
* @return -ERANGE if numeric string input is to large to convert.
27+
*/
28+
int bt_mesh_str2l(const char *str, long *val);
29+
30+
/*
31+
* @brief Convert string to unsigned long param.
32+
*
33+
* @note On failure the passed value reference will not be altered.
34+
*
35+
* @param str Input string
36+
* @param val Converted value
37+
*
38+
* @return 0 on success.
39+
* @return -EINVAL on invalid string input.
40+
* @return -ERANGE if numeric string input is to large to convert.
41+
*/
42+
int bt_mesh_str2ul(const char *str, unsigned long *val);
43+
44+
/*
45+
* @brief Convert string to double param.
46+
*
47+
* @note On failure the passed value reference will not be altered.
48+
*
49+
* @param str Input string
50+
* @param val Converted value
51+
*
52+
* @return 0 on success.
53+
* @return -EINVAL on invalid string input.
54+
* @return -ERANGE if numeric string input is to large to convert.
55+
*/
56+
int bt_mesh_str2dbl(const char *str, double *val);
57+
58+
/*
59+
* @brief Convert string to bool param.
60+
*
61+
* @note On failure the passed value reference will not be altered.
62+
*
63+
* @param str Input string
64+
* @param val Converted value
65+
*
66+
* @return 0 on success.
67+
* @return -EINVAL on invalid string input.
68+
* @return -ERANGE if numeric string input is to large to convert.
69+
*/
70+
int bt_mesh_str2bool(const char *str, bool *val);
71+
72+
#ifdef __cplusplus
73+
}
74+
#endif
75+
76+
#endif /* BT_MESH_SHELL_UTIL_H__ */
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(mesh_shell_string_conv)
6+
7+
FILE(GLOB app_sources src/*.c)
8+
target_sources(app PRIVATE ${app_sources})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_BT=y
3+
CONFIG_BT_OBSERVER=y
4+
CONFIG_BT_BROADCASTER=y
5+
CONFIG_BT_MESH=y
6+
CONFIG_FPU=y

0 commit comments

Comments
 (0)