2020-12-01 19:55:16 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <kernaux/cmdline.h>
|
2020-12-06 10:16:15 +00:00
|
|
|
#include <kernaux/stdlib.h>
|
|
|
|
|
|
|
|
#include <stddef.h>
|
2020-12-01 20:22:38 +00:00
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
enum State {
|
|
|
|
INITIAL,
|
|
|
|
FINAL,
|
|
|
|
WHITESPACE,
|
|
|
|
TOKEN,
|
2020-12-05 22:31:15 +00:00
|
|
|
BACKSLASHED,
|
2020-12-04 02:41:45 +00:00
|
|
|
};
|
|
|
|
|
2020-12-06 10:16:15 +00:00
|
|
|
bool kernaux_cmdline_parse(
|
2020-12-01 20:22:38 +00:00
|
|
|
const char *const cmdline,
|
|
|
|
char *error_msg,
|
|
|
|
unsigned int *const argc,
|
|
|
|
char **argv,
|
|
|
|
char *buffer,
|
|
|
|
const unsigned int argv_count_max,
|
|
|
|
const unsigned int arg_size_max
|
|
|
|
) {
|
|
|
|
if (
|
2020-12-06 10:16:15 +00:00
|
|
|
cmdline == NULL ||
|
|
|
|
error_msg == NULL ||
|
|
|
|
argc == NULL ||
|
|
|
|
argv == NULL ||
|
2020-12-01 20:22:38 +00:00
|
|
|
argv_count_max == 0 ||
|
|
|
|
arg_size_max == 0
|
|
|
|
) {
|
2020-12-06 10:16:15 +00:00
|
|
|
return false;
|
2020-12-01 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kernaux_memset(error_msg, '\0', KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX);
|
|
|
|
*argc = 0;
|
|
|
|
|
|
|
|
for (unsigned int index = 0; index < argv_count_max; ++index) {
|
2020-12-06 10:16:15 +00:00
|
|
|
argv[index] = NULL;
|
2020-12-01 20:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kernaux_memset(buffer, '\0', argv_count_max * arg_size_max);
|
|
|
|
|
2020-12-01 20:55:09 +00:00
|
|
|
if (cmdline[0] == '\0') {
|
2020-12-06 10:16:15 +00:00
|
|
|
return true;
|
2020-12-01 20:55:09 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
enum State state = INITIAL;
|
|
|
|
|
2020-12-05 21:47:19 +00:00
|
|
|
unsigned int buffer_size = 0;
|
2020-12-01 20:55:09 +00:00
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
for (unsigned int index = 0; ; ++index) {
|
|
|
|
const char cur = cmdline[index];
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case FINAL:
|
|
|
|
break; // Case break; loop break after switch.
|
2020-12-05 22:31:15 +00:00
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
case INITIAL:
|
|
|
|
if (cur == '\0') {
|
|
|
|
state = FINAL;
|
|
|
|
}
|
|
|
|
else if (cur == ' ') {
|
|
|
|
state = WHITESPACE;
|
|
|
|
}
|
2020-12-05 22:31:15 +00:00
|
|
|
else if (cur == '\\') {
|
|
|
|
if (*argc >= argv_count_max) {
|
|
|
|
kernaux_strncpy(error_msg, "too many args", 13);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = BACKSLASHED;
|
|
|
|
argv[(*argc)++] = buffer;
|
|
|
|
}
|
2020-12-04 02:41:45 +00:00
|
|
|
else {
|
2020-12-05 21:47:19 +00:00
|
|
|
if (*argc >= argv_count_max) {
|
|
|
|
kernaux_strncpy(error_msg, "too many args", 13);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
state = TOKEN;
|
2020-12-05 21:47:19 +00:00
|
|
|
argv[(*argc)++] = buffer;
|
|
|
|
*(buffer++) = cur;
|
|
|
|
++buffer_size;
|
2020-12-04 02:41:45 +00:00
|
|
|
}
|
|
|
|
break;
|
2020-12-05 22:31:15 +00:00
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
case WHITESPACE:
|
|
|
|
if (cur == '\0') {
|
|
|
|
state = FINAL;
|
|
|
|
}
|
|
|
|
else if (cur == ' ') {
|
|
|
|
}
|
2020-12-05 22:31:15 +00:00
|
|
|
else if (cur == '\\') {
|
|
|
|
if (*argc >= argv_count_max) {
|
|
|
|
kernaux_strncpy(error_msg, "too many args", 13);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = BACKSLASHED;
|
|
|
|
argv[(*argc)++] = buffer;
|
|
|
|
}
|
2020-12-04 02:41:45 +00:00
|
|
|
else {
|
2020-12-05 21:47:19 +00:00
|
|
|
if (*argc >= argv_count_max) {
|
|
|
|
kernaux_strncpy(error_msg, "too many args", 13);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
state = TOKEN;
|
2020-12-05 21:47:19 +00:00
|
|
|
argv[(*argc)++] = buffer;
|
|
|
|
*(buffer++) = cur;
|
|
|
|
++buffer_size;
|
2020-12-04 02:41:45 +00:00
|
|
|
}
|
|
|
|
break;
|
2020-12-05 22:31:15 +00:00
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
case TOKEN:
|
|
|
|
if (cur == '\0') {
|
2020-12-05 21:47:19 +00:00
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
state = FINAL;
|
2020-12-05 21:47:19 +00:00
|
|
|
*(buffer++) = '\0';
|
|
|
|
buffer_size = 0;
|
2020-12-04 02:41:45 +00:00
|
|
|
}
|
|
|
|
else if (cur == ' ') {
|
2020-12-05 21:47:19 +00:00
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
state = WHITESPACE;
|
2020-12-05 21:47:19 +00:00
|
|
|
*(buffer++) = '\0';
|
|
|
|
buffer_size = 0;
|
2020-12-04 02:41:45 +00:00
|
|
|
}
|
2020-12-05 22:31:15 +00:00
|
|
|
else if (cur == '\\') {
|
|
|
|
state = BACKSLASHED;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
*(buffer++) = cur;
|
|
|
|
++buffer_size;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BACKSLASHED:
|
|
|
|
if (cur == '\0') {
|
|
|
|
kernaux_strncpy(error_msg, "EOL after backslash", 19);
|
|
|
|
goto fail;
|
|
|
|
}
|
2020-12-04 02:41:45 +00:00
|
|
|
else {
|
2020-12-05 21:47:19 +00:00
|
|
|
if (buffer_size >= arg_size_max) {
|
|
|
|
kernaux_strncpy(error_msg, "arg too long", 12);
|
|
|
|
goto fail;
|
|
|
|
}
|
2020-12-04 02:41:45 +00:00
|
|
|
|
2020-12-05 22:31:15 +00:00
|
|
|
state = TOKEN;
|
2020-12-05 21:47:19 +00:00
|
|
|
*(buffer++) = cur;
|
|
|
|
++buffer_size;
|
2020-12-01 21:54:18 +00:00
|
|
|
}
|
2020-12-05 21:47:19 +00:00
|
|
|
break;
|
2020-12-01 20:55:09 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 02:41:45 +00:00
|
|
|
if (state == FINAL) {
|
2020-12-01 20:55:09 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-06 10:16:15 +00:00
|
|
|
return true;
|
2020-12-01 21:54:18 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
*argc = 0;
|
|
|
|
|
|
|
|
for (unsigned int index = 0; index < argv_count_max; ++index) {
|
2020-12-06 10:16:15 +00:00
|
|
|
argv[index] = NULL;
|
2020-12-01 21:54:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kernaux_memset(buffer, '\0', argv_count_max * arg_size_max);
|
|
|
|
|
2020-12-06 10:16:15 +00:00
|
|
|
return false;
|
2020-12-01 20:22:38 +00:00
|
|
|
}
|