]> granicus.if.org Git - esp-idf/blob - components/console/split_argv.c
heap: test: don’t warn about oversized mallocs
[esp-idf] / components / console / split_argv.c
1 // Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <stdio.h>
16 #include <ctype.h>
17 #include <string.h>
18
19 #define SS_FLAG_ESCAPE 0x8
20
21 typedef enum {
22     /* parsing the space between arguments */
23     SS_SPACE = 0x0,
24     /* parsing an argument which isn't quoted */
25     SS_ARG = 0x1,
26     /* parsing a quoted argument */
27     SS_QUOTED_ARG = 0x2,
28     /* parsing an escape sequence within unquoted argument */
29     SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE,
30     /* parsing an escape sequence within a quoted argument */
31     SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
32 } split_state_t;
33
34 size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
35 {
36     const int QUOTE = '"';
37     const int ESCAPE = '\\';
38     const int SPACE = ' ';
39     split_state_t state = SS_SPACE;
40     int argc = 0;
41     char *next_arg_start = line;
42     char *out_ptr = line;
43     for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
44         int char_in = (unsigned char) *in_ptr;
45         if (char_in == 0) {
46             break;
47         }
48         int char_out = -1;
49
50         /* helper function, called when done with an argument */
51         void end_arg() {
52             char_out = 0;
53             argv[argc++] = next_arg_start;
54             state = SS_SPACE;
55         }
56
57         switch (state) {
58         case SS_SPACE:
59             if (char_in == SPACE) {
60                 /* skip space */
61             } else if (char_in == QUOTE) {
62                 next_arg_start = out_ptr;
63                 state = SS_QUOTED_ARG;
64             } else if (char_in == ESCAPE) {
65                 next_arg_start = out_ptr;
66                 state = SS_ARG_ESCAPED;
67             } else {
68                 next_arg_start = out_ptr;
69                 state = SS_ARG;
70                 char_out = char_in;
71             }
72             break;
73
74         case SS_QUOTED_ARG:
75             if (char_in == QUOTE) {
76                 end_arg();
77             } else if (char_in == ESCAPE) {
78                 state = SS_QUOTED_ARG_ESCAPED;
79             } else {
80                 char_out = char_in;
81             }
82             break;
83
84         case SS_ARG_ESCAPED:
85         case SS_QUOTED_ARG_ESCAPED:
86             if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) {
87                 char_out = char_in;
88             } else {
89                 /* unrecognized escape character, skip */
90             }
91             state = (split_state_t) (state & (~SS_FLAG_ESCAPE));
92             break;
93
94         case SS_ARG:
95             if (char_in == SPACE) {
96                 end_arg();
97             } else if (char_in == ESCAPE) {
98                 state = SS_ARG_ESCAPED;
99             } else {
100                 char_out = char_in;
101             }
102             break;
103         }
104         /* need to output anything? */
105         if (char_out >= 0) {
106             *out_ptr = char_out;
107             ++out_ptr;
108         }
109     }
110     /* make sure the final argument is terminated */
111     *out_ptr = 0;
112     /* finalize the last argument */
113     if (state != SS_SPACE && argc < argv_size - 1) {
114         argv[argc++] = next_arg_start;
115     }
116     /* add a NULL at the end of argv */
117     argv[argc] = NULL;
118
119     return argc;
120 }