1 /*
2 * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <string.h>
10
11 #define SS_FLAG_ESCAPE 0x8
12
13 typedef enum {
14 /* parsing the space between arguments */
15 SS_SPACE = 0x0,
16 /* parsing an argument which isn't quoted */
17 SS_ARG = 0x1,
18 /* parsing a quoted argument */
19 SS_QUOTED_ARG = 0x2,
20 /* parsing an escape sequence within unquoted argument */
21 SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE,
22 /* parsing an escape sequence within a quoted argument */
23 SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
24 } split_state_t;
25
26 /* helper macro, called when done with an argument */
27 #define END_ARG() do { \
28 char_out = 0; \
29 argv[argc++] = next_arg_start; \
30 state = SS_SPACE; \
31 } while(0)
32
esp_console_split_argv(char * line,char ** argv,size_t argv_size)33 size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
34 {
35 const int QUOTE = '"';
36 const int ESCAPE = '\\';
37 const int SPACE = ' ';
38 split_state_t state = SS_SPACE;
39 size_t argc = 0;
40 char *next_arg_start = line;
41 char *out_ptr = line;
42 for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
43 int char_in = (unsigned char) *in_ptr;
44 if (char_in == 0) {
45 break;
46 }
47 int char_out = -1;
48
49 switch (state) {
50 case SS_SPACE:
51 if (char_in == SPACE) {
52 /* skip space */
53 } else if (char_in == QUOTE) {
54 next_arg_start = out_ptr;
55 state = SS_QUOTED_ARG;
56 } else if (char_in == ESCAPE) {
57 next_arg_start = out_ptr;
58 state = SS_ARG_ESCAPED;
59 } else {
60 next_arg_start = out_ptr;
61 state = SS_ARG;
62 char_out = char_in;
63 }
64 break;
65
66 case SS_QUOTED_ARG:
67 if (char_in == QUOTE) {
68 END_ARG();
69 } else if (char_in == ESCAPE) {
70 state = SS_QUOTED_ARG_ESCAPED;
71 } else {
72 char_out = char_in;
73 }
74 break;
75
76 case SS_ARG_ESCAPED:
77 case SS_QUOTED_ARG_ESCAPED:
78 if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) {
79 char_out = char_in;
80 } else {
81 /* unrecognized escape character, skip */
82 }
83 state = (split_state_t) (state & (~SS_FLAG_ESCAPE));
84 break;
85
86 case SS_ARG:
87 if (char_in == SPACE) {
88 END_ARG();
89 } else if (char_in == ESCAPE) {
90 state = SS_ARG_ESCAPED;
91 } else {
92 char_out = char_in;
93 }
94 break;
95 }
96 /* need to output anything? */
97 if (char_out >= 0) {
98 *out_ptr = char_out;
99 ++out_ptr;
100 }
101 }
102 /* make sure the final argument is terminated */
103 *out_ptr = 0;
104 /* finalize the last argument */
105 if (state != SS_SPACE && argc < argv_size - 1) {
106 argv[argc++] = next_arg_start;
107 }
108 /* add a NULL at the end of argv */
109 argv[argc] = NULL;
110
111 return argc;
112 }
113