libkernaux/src/cmdline.c

249 lines
6.4 KiB
C
Raw Normal View History

2020-12-07 04:46:37 +00:00
#ifdef HAVE_CONFIG_H
2020-12-01 19:55:16 +00:00
#include "config.h"
2020-12-07 04:46:37 +00:00
#endif
2020-12-01 19:55:16 +00:00
#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,
BACKSLASHED,
2021-12-12 15:39:17 +00:00
QUOTED,
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);
if (cmdline[0] == '\0') {
2020-12-06 10:16:15 +00:00
return true;
}
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-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-04 02:41:45 +00:00
case INITIAL:
if (cur == '\0') {
state = FINAL;
}
else if (cur == ' ') {
state = WHITESPACE;
}
else if (cur == '\\') {
if (*argc >= argv_count_max) {
kernaux_strncpy(error_msg, "too many args", 13);
goto fail;
}
state = BACKSLASHED;
argv[(*argc)++] = buffer;
}
2021-12-12 15:39:17 +00:00
else if (cur == '"') {
if (*argc >= argv_count_max) {
kernaux_strncpy(error_msg, "too many args", 13);
goto fail;
}
state = QUOTED;
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-04 02:41:45 +00:00
case WHITESPACE:
if (cur == '\0') {
state = FINAL;
}
else if (cur == ' ') {
}
else if (cur == '\\') {
if (*argc >= argv_count_max) {
kernaux_strncpy(error_msg, "too many args", 13);
goto fail;
}
state = BACKSLASHED;
argv[(*argc)++] = buffer;
}
2021-12-12 15:39:17 +00:00
else if (cur == '"') {
if (*argc >= argv_count_max) {
kernaux_strncpy(error_msg, "too many args", 13);
goto fail;
}
state = QUOTED;
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-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
}
else if (cur == '\\') {
state = BACKSLASHED;
}
2021-12-12 15:39:17 +00:00
else if (cur == '"') {
kernaux_strncpy(error_msg, "unescaped quotation mark", 24);
goto fail;
}
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
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;
2021-12-12 15:39:17 +00:00
case QUOTED:
if (cur == '\0') {
kernaux_strncpy(error_msg, "EOL inside quote", 16);
goto fail;
}
else if (cur == '"') {
if (buffer_size >= arg_size_max) {
kernaux_strncpy(error_msg, "arg too long", 12);
goto fail;
}
state = WHITESPACE;
*(buffer++) = '\0';
buffer_size = 0;
}
else {
if (buffer_size >= arg_size_max) {
kernaux_strncpy(error_msg, "arg too long", 12);
goto fail;
}
*(buffer++) = cur;
++buffer_size;
}
break;
}
2020-12-04 02:41:45 +00:00
if (state == FINAL) {
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
}