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