ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
cli_parser.c
Go to the documentation of this file.
1/*
2 * ether2ser - Ethernet <-> synchronous V.24 (RS-232/V.28) bridge
3 *
4 * File: src/system/cli_parser.c
5 * Purpose: CLI line parsing and pin lookup.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Copyright (c) 2026 Florian <f.leuze@outlook.de>
10 */
11
12// Related headers
13#include "cli_parser.h"
14
15// Standard library headers
16#include <errno.h>
17#include <limits.h>
18#include <stdbool.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25// Project Headers
26#include "drivers/v24_config.h"
27#include "platform/pinmap.h"
28#include "system/common.h"
29#include "system/error.h"
30
31// Generated headers
32// Todo: Get these entries from main
33static const pin_info_t pin_table[] = {
34 {"txd", V24_TXD, true}, {"rxd", V24_RXD, false},
35 {"rts", V24_RTS, true}, {"cts", V24_CTS, false},
36 {"dtr", V24_DTR, true}, {"dsr", V24_DSR, false},
37 {"dcd", V24_DCD, false}, {"tx_active", V24_TX_ACTIVE, true},
38 {"tck", V24_TXC_DTE, true}, {"rck", V24_RXC, false},
39 {"led", V24_STATUS_LED, true}};
40
41#define NUM_PINS ARRAY_LEN(pin_table)
42#define DECIMAL_BASE 10U
43
44size_t get_num_pins(void)
45{
46 return NUM_PINS;
47}
48
49// cli_parser.c
50static bool prefix_to_mask(uint8_t prefix, uint8_t netmask[4])
51{
52 if (prefix > 32)
53 {
54 return false;
55 }
56 uint32_t mask_word = prefix == 0 ? 0 : UINT32_ALL_ONES << (32 - prefix);
57 netmask[0] = (mask_word >> 24) & 0xFF;
58 netmask[1] = (mask_word >> 16) & 0xFF;
59 netmask[2] = (mask_word >> 8) & 0xFF;
60 netmask[3] = (mask_word >> 0) & 0xFF;
61 return true;
62}
63
64static bool parse_u32_strict(const char* input_str, uint32_t* output_value)
65{
66 if (input_str == NULL || output_value == NULL || *input_str == '\0')
67 {
68 return false;
69 }
70
71 errno = 0;
72 char* end_ptr = NULL;
73 unsigned long parsed_value = strtoul(input_str, &end_ptr, DECIMAL_BASE);
74 if (errno == ERANGE || end_ptr == input_str || *end_ptr != '\0' || parsed_value > UINT32_MAX)
75 {
76 return false;
77 }
78
79 *output_value = (uint32_t)parsed_value;
80 return true;
81}
82
83static bool parse_i32_strict(const char* input_str, int32_t* output_value)
84{
85 if (input_str == NULL || output_value == NULL || *input_str == '\0')
86 {
87 return false;
88 }
89
90 errno = 0;
91 char* end_ptr = NULL;
92 long parsed_value = strtol(input_str, &end_ptr, DECIMAL_BASE);
93 if (errno == ERANGE || end_ptr == input_str || *end_ptr != '\0' || parsed_value < INT32_MIN ||
94 parsed_value > INT32_MAX)
95 {
96 return false;
97 }
98
99 *output_value = (int32_t)parsed_value;
100 return true;
101}
102
103static bool parse_ipv4_strict(const char* input_str, uint8_t ip_addr[4])
104{
105 if (input_str == NULL || ip_addr == NULL)
106 {
107 return false;
108 }
109
110 const char* cursor = input_str;
111 for (size_t octet_index = 0; octet_index < 4; octet_index++)
112 {
113 if (*cursor == '\0')
114 {
115 return false;
116 }
117
118 errno = 0;
119 char* end_ptr = NULL;
120 unsigned long octet_value = strtoul(cursor, &end_ptr, DECIMAL_BASE);
121 if (errno == ERANGE || end_ptr == cursor || octet_value > UINT8_MAX)
122 {
123 return false;
124 }
125
126 if (octet_index < 3)
127 {
128 if (*end_ptr != '.')
129 {
130 return false;
131 }
132 cursor = end_ptr + 1;
133 }
134 else if (*end_ptr != '\0')
135 {
136 return false;
137 }
138
139 ip_addr[octet_index] = (uint8_t)octet_value;
140 }
141
142 return true;
143}
144
145static e2s_error_t parse_ipv4_with_optional_prefix(const char* args, const char* prefix,
146 uint8_t ip_addr[4])
147{
148 const char* input_str = args;
149 if (prefix != NULL)
150 {
151 size_t prefix_len = strlen(prefix);
152 if (strncmp(args, prefix, prefix_len) == 0 && args[prefix_len] == ' ')
153 {
154 input_str = args + prefix_len + 1;
155 }
156 }
157
158 if (!parse_ipv4_strict(input_str, ip_addr))
159 {
161 }
162 return E2S_OK;
163}
164
165static e2s_error_t parse_u16_with_optional_prefix(const char* args, const char* prefix,
166 uint16_t* value)
167{
168 const char* input_str = args;
169 if (prefix != NULL)
170 {
171 size_t prefix_len = strlen(prefix);
172 if (strncmp(args, prefix, prefix_len) == 0 && args[prefix_len] == ' ')
173 {
174 input_str = args + prefix_len + 1;
175 }
176 }
177
178 uint32_t parsed_value = 0;
179 if (!parse_u32_strict(input_str, &parsed_value) || parsed_value > UINT16_MAX)
180 {
182 }
183 *value = (uint16_t)parsed_value;
184 return E2S_OK;
185}
186
187e2s_error_t parse_set_ip_args(const char* args, uint8_t ip_addr[4], uint8_t netmask[4])
188{
189 if (args == NULL || ip_addr == NULL || netmask == NULL)
190 {
192 }
193
194 const char* input_str = args;
195 if (strncmp(input_str, "ip ", 3) == 0)
196 {
197 input_str += 3;
198 }
199
200 char address_buf[32];
201 size_t input_len = strnlen(input_str, sizeof(address_buf));
202 if (input_len == sizeof(address_buf))
203 {
205 }
206 memcpy(address_buf, input_str, input_len + 1);
207
208 char* slash_pos = strchr(address_buf, '/');
209 if (slash_pos == NULL)
210 {
212 }
213 *slash_pos = '\0';
214
215 uint32_t prefix = 0;
216 if (!parse_u32_strict(slash_pos + 1, &prefix))
217 {
219 }
220 if (!parse_ipv4_strict(address_buf, ip_addr))
221 {
223 }
224 if (!prefix_to_mask((uint8_t)prefix, netmask))
225 {
227 }
228 return E2S_OK;
229}
230
231e2s_error_t parse_set_gpio_args(const char* args, char* pin_name, int* value,
232 const pin_info_t** pin)
233{
234 if (args == NULL || pin_name == NULL || value == NULL || pin == NULL)
235 {
237 }
238
239 char buf[64];
240 size_t len = strnlen(args, sizeof(buf));
241 if (len == sizeof(buf))
242 {
244 }
245 memcpy(buf, args, len + 1);
246
247 char* saveptr = NULL;
248 char* tok_pin = strtok_r(buf, " ", &saveptr);
249 const char* tok_val = strtok_r(NULL, " ", &saveptr);
250 const char* tok_extra = strtok_r(NULL, " ", &saveptr);
251 if (tok_pin == NULL || tok_val == NULL || tok_extra != NULL)
252 {
254 }
255
256 size_t pin_len = strnlen(tok_pin, 16);
257 if (pin_len == 16)
258 {
260 }
261 memcpy(pin_name, tok_pin, pin_len + 1);
262
263 int32_t parsed_value = 0;
264 if (!parse_i32_strict(tok_val, &parsed_value) || (parsed_value != 0 && parsed_value != 1))
265 {
267 }
268 *value = (int)parsed_value;
269
270 *pin = find_pin(pin_name);
271 if (!*pin)
272 {
274 }
275
276 if (!(*pin)->is_output)
277 {
279 }
280 return E2S_OK;
281}
282
283e2s_error_t parse_set_net_ip_args(const char* args, uint8_t ip_addr[4], uint8_t netmask[4])
284{
285 if (strncmp(args, "net ", 4) != 0)
286 {
288 }
289 return parse_set_ip_args(args + 4, ip_addr, netmask);
290}
291
292e2s_error_t parse_set_ip_remote_args(const char* args, uint8_t ip_addr[4])
293{
294 return parse_ipv4_with_optional_prefix(args, "ip", ip_addr);
295}
296
297e2s_error_t parse_set_gateway_args(const char* args, uint8_t ip_addr[4])
298{
299 return parse_ipv4_with_optional_prefix(args, "gateway", ip_addr);
300}
301
302e2s_error_t parse_set_udp_port_local_args(const char* args, uint16_t* port)
303{
304 return parse_u16_with_optional_prefix(args, "port", port);
305}
306
307e2s_error_t parse_set_udp_port_remote_args(const char* args, uint16_t* port)
308{
309 return parse_u16_with_optional_prefix(args, "port", port);
310}
311
313{
314 if (polarities == NULL)
315 {
317 }
318
319 memset(polarities, 0, sizeof(*polarities));
320
321 if (args == NULL)
322 {
323 return E2S_OK;
324 }
325
326 while (*args == ' ')
327 {
328 args++;
329 }
330 if (*args == '\0')
331 {
332 return E2S_OK;
333 }
334
335 const char* token_list = args;
336 if (strncmp(token_list, "invert ", (CHAR_BIT - 1U)) == 0)
337 {
338 token_list += (CHAR_BIT - 1U);
339 }
340
341 char token_buf[64];
342 size_t token_len = strnlen(token_list, sizeof(token_buf));
343 if (token_len == sizeof(token_buf))
344 {
346 }
347 memcpy(token_buf, token_list, token_len + 1);
348
349 char* token_ptr = strtok(token_buf, ",");
350 while (token_ptr != NULL)
351 {
352 while (*token_ptr == ' ')
353 {
354 token_ptr++;
355 }
356 char* token_end = token_ptr + strlen(token_ptr);
357 while (token_end > token_ptr && token_end[-1] == ' ')
358 {
359 token_end--;
360 }
361 *token_end = '\0';
362
363 if (*token_ptr == '\0')
364 {
366 }
367
368 const pin_info_t* pin_info = find_pin(token_ptr);
369 if (pin_info == NULL)
370 {
372 }
373
374 if (strcmp(token_ptr, "txd") == 0)
375 {
376 polarities->tx_polarities.txd_inverted = true;
377 }
378 else if (strcmp(token_ptr, "rts") == 0)
379 {
380 polarities->tx_polarities.rts_inverted = true;
381 }
382 else if (strcmp(token_ptr, "cts") == 0)
383 {
384 polarities->tx_polarities.cts_inverted = true;
385 }
386 else if (strcmp(token_ptr, "dtr") == 0)
387 {
388 polarities->tx_polarities.dtr_inverted = true;
389 }
390 else if (strcmp(token_ptr, "rxd") == 0)
391 {
392 polarities->rx_polarities.rxd_inverted = true;
393 }
394 else if (strcmp(token_ptr, "dcd") == 0)
395 {
396 polarities->rx_polarities.dcd_inverted = true;
397 }
398 else if (strcmp(token_ptr, "tck") == 0)
399 {
400 polarities->tx_polarities.txc_inverted = true;
401 }
402 else if (strcmp(token_ptr, "rck") == 0)
403 {
404 polarities->rx_polarities.rxc_inverted = true;
405 }
406 else
407 {
409 }
410
411 token_ptr = strtok(NULL, ",");
412 }
413
414 return E2S_OK;
415}
416
418{
419 if (baudrate == NULL)
420 {
422 }
423
424 const char* input_str = args;
425 if (input_str == NULL)
426 {
428 }
429 while (*input_str == ' ')
430 {
431 input_str++;
432 }
433 if (*input_str == '\0')
434 {
436 }
437
438 uint32_t baud_value = 0;
439 if (!parse_u32_strict(input_str, &baud_value))
440 {
442 }
443
444 switch (baud_value)
445 {
446 case V24_BAUD_1200:
447 case V24_BAUD_2400:
448 case V24_BAUD_4800:
449 case V24_BAUD_9600:
450 case V24_BAUD_16000:
451 case V24_BAUD_19200:
452 case V24_BAUD_38400:
453 case V24_BAUD_57600:
454 case V24_BAUD_115200:
455 *baudrate = (V24_BAUDRATE_T)baud_value;
456 return E2S_OK;
457 default:
459 }
460}
461
462e2s_error_t parse_set_v24_clockmode(const char* args, bool* clockmode)
463{
464 if (clockmode == NULL)
465 {
467 }
468
469 const char* input_str = args;
470 if (input_str == NULL)
471 {
473 }
474 while (*input_str == ' ')
475 {
476 input_str++;
477 }
478 if (*input_str == '\0')
479 {
481 }
482
483 uint32_t clock_mode_value = 0U;
484 if (!parse_u32_strict(input_str, &clock_mode_value))
485 {
487 }
488 if (clock_mode_value > 1U)
489 {
491 }
492
493 *clockmode = (clock_mode_value == 1U);
494 return E2S_OK;
495}
496
498{
499 return pin_table;
500}
501
502// Helper function to find pin by name
503const pin_info_t* find_pin(const char* name)
504{
505 for (size_t pin_index = 0; pin_index < NUM_PINS; pin_index++)
506 {
507 if (strcmp(name, pin_table[pin_index].name) == 0)
508 {
509 return &pin_table[pin_index];
510 }
511 }
512 return NULL;
513}
514
515e2s_error_t parse_get_args(const char* args, char* pin_name, const pin_info_t** pin)
516{
517
518 if (sscanf(args, "%15s", pin_name) != 1)
519 {
521 }
522
523 *pin = find_pin(pin_name);
524 if (!*pin)
525 {
527 }
528 return E2S_OK;
529}
530
531e2s_error_t cli_parse(const char* line, char* cmd, size_t cmd_cap, char* args, size_t args_cap)
532
533{
534 if (!line || !cmd || !args || cmd_cap == 0 || args_cap == 0)
535 {
537 }
538
539 if (line[0] == '\0')
540 {
541 cmd[0] = '\0';
542 args[0] = '\0';
544 }
545
546 // Skip leading spaces
547 while (*line == ' ')
548 {
549 line++;
550 }
551 if (*line == '\0')
552 {
553 cmd[0] = '\0';
554 args[0] = '\0';
556 }
557
558 // cmd = first token
559 const char* cmd_end = line;
560 while (*cmd_end && *cmd_end != ' ')
561 {
562 cmd_end++;
563 }
564
565 size_t cmd_len = (size_t)(cmd_end - line);
566 if (cmd_len + 1 > cmd_cap)
567 {
569 }
570
571 memcpy(cmd, line, cmd_len);
572 cmd[cmd_len] = '\0';
573
574 // args = remainder (trim leading spaces)
575 const char* arg_start = cmd_end;
576 while (*arg_start == ' ')
577 {
578 arg_start++;
579 }
580
581 int written = snprintf(args, args_cap, "%s", arg_start);
582 if (written < 0)
583 {
585 }
586 if ((size_t)written >= args_cap)
587 {
589 }
590
591 return E2S_OK;
592}
e2s_error_t parse_set_v24_baudrate(const char *args, V24_BAUDRATE_T *baudrate)
Parse V.24 baudrate argument.
Definition cli_parser.c:417
e2s_error_t cli_parse(const char *line, char *cmd, size_t cmd_cap, char *args, size_t args_cap)
Split one CLI line into command and argument string.
Definition cli_parser.c:531
e2s_error_t parse_set_ip_args(const char *args, uint8_t ip_addr[4], uint8_t netmask[4])
Parse ip_addr and optional netmask values.
Definition cli_parser.c:187
static bool parse_ipv4_strict(const char *input_str, uint8_t ip_addr[4])
Definition cli_parser.c:103
static bool parse_i32_strict(const char *input_str, int32_t *output_value)
Definition cli_parser.c:83
#define NUM_PINS
Definition cli_parser.c:41
e2s_error_t parse_set_gateway_args(const char *args, uint8_t ip_addr[4])
Parse gateway ip_addr argument.
Definition cli_parser.c:297
e2s_error_t parse_get_args(const char *args, char *pin_name, const pin_info_t **pin)
Parse get command arguments and resolve pin metadata.
Definition cli_parser.c:515
static e2s_error_t parse_u16_with_optional_prefix(const char *args, const char *prefix, uint16_t *value)
Definition cli_parser.c:165
e2s_error_t parse_set_v24_clockmode(const char *args, bool *clockmode)
Parse V.24 tx clock mode argument.
Definition cli_parser.c:462
#define DECIMAL_BASE
Definition cli_parser.c:42
const pin_info_t * find_pin(const char *name)
Lookup pin metadata by name.
Definition cli_parser.c:503
static bool prefix_to_mask(uint8_t prefix, uint8_t netmask[4])
Definition cli_parser.c:50
static bool parse_u32_strict(const char *input_str, uint32_t *output_value)
Definition cli_parser.c:64
static const pin_info_t pin_table[]
Definition cli_parser.c:33
static e2s_error_t parse_ipv4_with_optional_prefix(const char *args, const char *prefix, uint8_t ip_addr[4])
Definition cli_parser.c:145
e2s_error_t parse_set_udp_port_remote_args(const char *args, uint16_t *port)
Parse remote UDP port argument.
Definition cli_parser.c:307
e2s_error_t parse_set_gpio_args(const char *args, char *pin_name, int *value, const pin_info_t **pin)
Parse GPIO set command arguments.
Definition cli_parser.c:231
const pin_info_t * get_pin_table(void)
Return pointer to static pin metadata table.
Definition cli_parser.c:497
e2s_error_t parse_set_net_ip_args(const char *args, uint8_t ip_addr[4], uint8_t netmask[4])
Parse network local ip_addr/subnet arguments.
Definition cli_parser.c:283
e2s_error_t parse_set_udp_port_local_args(const char *args, uint16_t *port)
Parse local UDP port argument.
Definition cli_parser.c:302
size_t get_num_pins(void)
Return number of entries in the CLI pin table.
Definition cli_parser.c:44
e2s_error_t parse_set_ip_remote_args(const char *args, uint8_t ip_addr[4])
Parse remote ip_addr argument.
Definition cli_parser.c:292
e2s_error_t parse_set_v24_polarities(const char *args, V24_POLARITIES_T *polarities)
Parse V.24 polarities argument list.
Definition cli_parser.c:312
#define UINT32_ALL_ONES
Definition common.h:81
e2s_error_t
Common error codes returned by ether2ser modules.
Definition error.h:27
@ E2S_ERR_CLI_EMPTY_LINE
Definition error.h:32
@ E2S_ERR_CLI_UNKNOWN_PIN
Definition error.h:37
@ E2S_ERR_CLI_LINE_FORMAT
Definition error.h:39
@ E2S_OK
Definition error.h:28
@ E2S_ERR_CLI_USAGE_SET
Definition error.h:34
@ E2S_ERR_CLI_LINE_TRUNCATED
Definition error.h:40
@ E2S_ERR_CLI_PIN_INPUT_ONLY
Definition error.h:38
@ E2S_ERR_CLI_USAGE_GET
Definition error.h:35
#define V24_RXC
Definition pinmap.h:28
#define V24_DCD
Definition pinmap.h:30
#define V24_DTR
Definition pinmap.h:37
#define V24_TXD
Definition pinmap.h:35
#define V24_RTS
Definition pinmap.h:34
#define V24_TXC_DTE
Definition pinmap.h:29
#define V24_STATUS_LED
Definition pinmap.h:39
#define V24_RXD
Definition pinmap.h:33
#define V24_DSR
Definition pinmap.h:31
#define V24_CTS
Definition pinmap.h:32
#define V24_TX_ACTIVE
Definition pinmap.h:38
Combined TX and RX polarity configuration.
Definition v24_config.h:78
V24_RX_POLARITIES_T rx_polarities
Definition v24_config.h:80
V24_TX_POLARITIES_T tx_polarities
Definition v24_config.h:79
Pin metadata entry used by CLI lookup and validation.
Definition cli_parser.h:33
V24_BAUDRATE_T
Supported synchronous V.24 baudrates.
Definition v24_config.h:32
@ V24_BAUD_1200
Definition v24_config.h:33
@ V24_BAUD_2400
Definition v24_config.h:34
@ V24_BAUD_115200
Definition v24_config.h:41
@ V24_BAUD_19200
Definition v24_config.h:38
@ V24_BAUD_57600
Definition v24_config.h:40
@ V24_BAUD_38400
Definition v24_config.h:39
@ V24_BAUD_4800
Definition v24_config.h:35
@ V24_BAUD_9600
Definition v24_config.h:36
@ V24_BAUD_16000
Definition v24_config.h:37