// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #define SS_FLAG_ESCAPE 0x8 typedef enum { /* parsing the space between arguments */ SS_SPACE = 0x0, /* parsing an argument which isn't quoted */ SS_ARG = 0x1, /* parsing a quoted argument */ SS_QUOTED_ARG = 0x2, /* parsing an escape sequence within unquoted argument */ SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE, /* parsing an escape sequence within a quoted argument */ SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE, } split_state_t; size_t console_split_argv(char *line, char **argv, size_t argv_size) { const int QUOTE = '"'; const int ESCAPE = '\\'; const int SPACE = ' '; split_state_t state = SS_SPACE; int argc = 0; char *next_arg_start = line; char *out_ptr = line; for (char *in_ptr = line; argc < (int)(argv_size - 1); ++in_ptr) { int char_in = (unsigned char) *in_ptr; if (char_in == 0) { break; } int char_out = -1; /* helper function, called when done with an argument */ void end_arg() { char_out = 0; argv[argc++] = next_arg_start; state = SS_SPACE; } switch (state) { case SS_SPACE: if (char_in == SPACE) { /* skip space */ } else if (char_in == QUOTE) { next_arg_start = out_ptr; state = SS_QUOTED_ARG; } else if (char_in == ESCAPE) { next_arg_start = out_ptr; state = SS_ARG_ESCAPED; } else { next_arg_start = out_ptr; state = SS_ARG; char_out = char_in; } break; case SS_QUOTED_ARG: if (char_in == QUOTE) { end_arg(); } else if (char_in == ESCAPE) { state = SS_QUOTED_ARG_ESCAPED; } else { char_out = char_in; } break; case SS_ARG_ESCAPED: case SS_QUOTED_ARG_ESCAPED: if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) { char_out = char_in; } else { /* unrecognized escape character, skip */ } state = (split_state_t) (state & (~SS_FLAG_ESCAPE)); break; case SS_ARG: if (char_in == SPACE) { end_arg(); } else if (char_in == ESCAPE) { state = SS_ARG_ESCAPED; } else { char_out = char_in; } break; } /* need to output anything? */ if (char_out >= 0) { *out_ptr = char_out; ++out_ptr; } } /* make sure the final argument is terminated */ *out_ptr = 0; /* finalize the last argument */ if (state != SS_SPACE && argc < ((int)argv_size - 1)) { argv[argc++] = next_arg_start; } /* add a NULL at the end of argv */ argv[argc] = NULL; return argc; }