120 lines
3.6 KiB
C
120 lines
3.6 KiB
C
// 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;
|
|
}
|