text: // 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 <stdio.h>
  #include <ctype.h>
  #include <string.h>
  
  #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;
  
  /* helper macro, called when done with an argument */
  #define END_ARG() do { \
      char_out = 0; \
      argv[argc++] = next_arg_start; \
      state = SS_SPACE; \
  } while(0)
  
  size_t esp_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 < argv_size - 1; ++in_ptr) {
          int char_in = (unsigned char) *in_ptr;
          if (char_in == 0) {
              break;
          }
          int char_out = -1;
  
          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 < argv_size - 1) {
          argv[argc++] = next_arg_start;
      }
      /* add a NULL at the end of argv */
      argv[argc] = NULL;
  
      return argc;
  }
  