/ components / console / split_argv.c
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  }