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 /* helper macro, called when done with an argument */ 35 #define END_ARG() do { \ 36 char_out = 0; \ 37 argv[argc++] = next_arg_start; \ 38 state = SS_SPACE; \ 39 } while(0) 40 41 size_t esp_console_split_argv(char *line, char **argv, size_t argv_size) 42 { 43 const int QUOTE = '"'; 44 const int ESCAPE = '\\'; 45 const int SPACE = ' '; 46 split_state_t state = SS_SPACE; 47 int argc = 0; 48 char *next_arg_start = line; 49 char *out_ptr = line; 50 for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) { 51 int char_in = (unsigned char) *in_ptr; 52 if (char_in == 0) { 53 break; 54 } 55 int char_out = -1; 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 }