Async cmdline (#104)

This commit is contained in:
Alex Kotov 2022-06-28 02:13:40 +03:00 committed by GitHub
parent dacdfede2b
commit 0765cd04e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 205 additions and 158 deletions

1
.gitignore vendored
View File

@ -103,7 +103,6 @@
/include/kernaux.h
/include/kernaux/drivers/console.h
/include/kernaux/multiboot2.h
/include/kernaux/printf.h
/include/kernaux/version.h
/examples/assert

View File

@ -66,18 +66,18 @@ zero). Work-in-progress APIs can change at any time.
* Data formats
* [ELF](/include/kernaux/elf.h) (*work in progress*)
* [MBR](/include/kernaux/mbr.h) (*work in progress*)
* [Multiboot 2 (GRUB 2)](/include/kernaux/multiboot2.h) (*work in progress*)
* [Multiboot 2 (GRUB 2)](/include/kernaux/multiboot2.h.in) (*work in progress*)
* Utilities
* [Measurement units utils](/include/kernaux/units.h) (*work in progress*)
* [Example: To human](/examples/units_human.c)
* [Memory map](/include/kernaux/memmap.h.in) (*non-breaking since* **0.4.0**)
* [Memory map](/include/kernaux/memmap.h) (*non-breaking since* **0.4.0**)
* [Example](/examples/memmap.c)
* [printf format parser](/include/kernaux/printf_fmt.h) (*work in progress*)
* [Example](/examples/printf_fmt.c)
* Usual functions
* [itoa/ftoa replacement](/include/kernaux/ntoa.h) (*non-breaking since* **0.4.0**)
* [Example](/examples/ntoa.c)
* [printf replacement](/include/kernaux/printf.h.in) (*non-breaking since* **?.?.?**)
* [printf replacement](/include/kernaux/printf.h) (*non-breaking since* **?.?.?**)
* [Example: fprintf](/examples/printf_file.c)
* [Example: vfprintf](/examples/printf_file_va.c)
* [Example: snprintf](/examples/printf_str.c)

View File

@ -32,7 +32,6 @@ AC_CONFIG_FILES([
include/kernaux.h
include/kernaux/drivers/console.h
include/kernaux/multiboot2.h
include/kernaux/printf.h
include/kernaux/version.h
tests/Makefile
])

View File

@ -6,7 +6,8 @@
#include <kernaux/cmdline.h>
#include <kernaux/generic/file.h>
#include "libc.h"
#include <stddef.h>
#include <string.h>
enum State {
INITIAL,
@ -31,9 +32,24 @@ static bool kernaux_cmdline_common(
size_t buffer_size
);
/*****************************
* Implementations: main API *
*****************************/
static bool kernaux_cmdline_iter(
char cur,
enum State *state,
size_t *buffer_or_file_pos,
char *error_msg,
size_t *argc,
char arg_terminator,
char **argv,
char *buffer,
KernAux_File file,
size_t *arg_idxs,
size_t arg_count_max,
size_t buffer_size
);
/*******************************
* Implementations: public API *
*******************************/
bool kernaux_cmdline(
const char *const cmdline,
@ -93,9 +109,9 @@ bool kernaux_cmdline_file(
);
}
/******************************************
* Implementation: main internal function *
******************************************/
/*********************************
* Implementation: main function *
*********************************/
#define CLEAR do { \
*argc = 0; \
@ -104,62 +120,6 @@ bool kernaux_cmdline_file(
if (arg_idxs) memset(arg_idxs, 0, sizeof(size_t) * arg_count_max); \
} while (0)
#define FAIL(msg) do { \
strcpy(error_msg, msg); \
goto fail; \
} while (0)
#define PUT_CHAR(char) do { \
if (buffer_size && buffer_or_file_pos >= buffer_size) { \
FAIL("EOF or buffer overflow"); \
} \
if (buffer) { \
buffer[buffer_or_file_pos] = char; \
} \
if (file) { \
if (KernAux_File_putc(file, char) == KERNAUX_EOF) { \
FAIL("EOF or buffer overflow"); \
} \
} \
++buffer_or_file_pos; \
} while (0)
#define PUT_ARG do { \
if (arg_count_max && *argc >= arg_count_max) { \
FAIL("too many args"); \
} \
if (argv && buffer) { \
argv[*argc] = &buffer[buffer_or_file_pos]; \
} \
if (arg_idxs) { \
arg_idxs[*argc] = buffer_or_file_pos; \
} \
++(*argc); \
} while (0)
#define PUT_ARG_AND_CHAR(char) do { \
if (arg_count_max && *argc >= arg_count_max) { \
FAIL("too many args"); \
} \
if (buffer_size && buffer_or_file_pos >= buffer_size) { \
FAIL("EOF or buffer overflow"); \
} \
if (argv && buffer) { \
argv[*argc] = &buffer[buffer_or_file_pos]; \
buffer[buffer_or_file_pos] = char; \
} \
if (file) { \
if (KernAux_File_putc(file, char) == KERNAUX_EOF) { \
FAIL("EOF or buffer overflow"); \
} \
} \
if (arg_idxs) { \
arg_idxs[*argc] = buffer_or_file_pos; \
} \
++(*argc); \
++buffer_or_file_pos; \
} while (0)
bool kernaux_cmdline_common(
const char *const cmdline,
char *const error_msg,
@ -185,96 +145,22 @@ bool kernaux_cmdline_common(
enum State state = INITIAL;
size_t buffer_or_file_pos = 0;
for (size_t index = 0; ; ++index) {
const char cur = cmdline[index];
switch (state) {
case FINAL:
break; // Case break; loop break after switch.
case INITIAL:
if (cur == '\0') {
state = FINAL;
} else if (cur == ' ') {
state = WHITESPACE;
} else if (cur == '\\') {
state = BACKSLASH;
PUT_ARG;
} else if (cur == '"') {
state = QUOTE;
PUT_ARG;
} else {
state = TOKEN;
PUT_ARG_AND_CHAR(cur);
}
break;
case WHITESPACE:
if (cur == '\0') {
state = FINAL;
} else if (cur == ' ') {
// do nothing
} else if (cur == '\\') {
state = BACKSLASH;
PUT_ARG;
} else if (cur == '"') {
state = QUOTE;
PUT_ARG;
} else {
state = TOKEN;
PUT_ARG_AND_CHAR(cur);
}
break;
case TOKEN:
if (cur == '\0') {
state = FINAL;
PUT_CHAR(arg_terminator);
} else if (cur == ' ') {
state = WHITESPACE;
PUT_CHAR(arg_terminator);
} else if (cur == '\\') {
state = BACKSLASH;
} else if (cur == '"') {
FAIL("unescaped quotation mark");
} else {
PUT_CHAR(cur);
}
break;
case BACKSLASH:
if (cur == '\0') {
FAIL("EOL after backslash");
} else {
state = TOKEN;
PUT_CHAR(cur);
}
break;
case QUOTE:
if (cur == '\0') {
FAIL("EOL inside quote");
} else if (cur == '\\') {
state = QUOTE_BACKSLASH;
} else if (cur == '"') {
state = WHITESPACE;
PUT_CHAR(arg_terminator);
} else {
PUT_CHAR(cur);
}
break;
case QUOTE_BACKSLASH:
if (cur == '\0') {
FAIL("EOL after backslash inside quote");
} else {
state = QUOTE;
PUT_CHAR(cur);
}
break;
}
if (state == FINAL) break;
for (size_t index = 0; state != FINAL; ++index) {
const bool result = kernaux_cmdline_iter(
cmdline[index],
&state,
&buffer_or_file_pos,
error_msg,
argc,
arg_terminator,
argv,
buffer,
file,
arg_idxs,
arg_count_max,
buffer_size
);
if (!result) goto fail;
}
return true;
@ -283,3 +169,166 @@ fail:
CLEAR;
return false;
}
/**************************************
* Implementation: iteration function *
**************************************/
#define FAIL(msg) do { \
strcpy(error_msg, msg); \
return false; \
} while (0)
#define PUT_CHAR(char) do { \
if (buffer_size && *buffer_or_file_pos >= buffer_size) { \
FAIL("EOF or buffer overflow"); \
} \
if (buffer) { \
buffer[*buffer_or_file_pos] = char; \
} \
if (file) { \
if (KernAux_File_putc(file, char) == KERNAUX_EOF) { \
FAIL("EOF or buffer overflow"); \
} \
} \
++(*buffer_or_file_pos); \
} while (0)
#define PUT_ARG do { \
if (arg_count_max && *argc >= arg_count_max) { \
FAIL("too many args"); \
} \
if (argv && buffer) { \
argv[*argc] = &buffer[*buffer_or_file_pos]; \
} \
if (arg_idxs) { \
arg_idxs[*argc] = *buffer_or_file_pos; \
} \
++(*argc); \
} while (0)
#define PUT_ARG_AND_CHAR(char) do { \
if (arg_count_max && *argc >= arg_count_max) { \
FAIL("too many args"); \
} \
if (buffer_size && *buffer_or_file_pos >= buffer_size) { \
FAIL("EOF or buffer overflow"); \
} \
if (argv && buffer) { \
argv[*argc] = &buffer[*buffer_or_file_pos]; \
buffer[*buffer_or_file_pos] = char; \
} \
if (file) { \
if (KernAux_File_putc(file, char) == KERNAUX_EOF) { \
FAIL("EOF or buffer overflow"); \
} \
} \
if (arg_idxs) { \
arg_idxs[*argc] = *buffer_or_file_pos; \
} \
++(*argc); \
++(*buffer_or_file_pos); \
} while (0)
bool kernaux_cmdline_iter(
const char cur,
enum State *const state,
size_t *const buffer_or_file_pos,
char *const error_msg,
size_t *const argc,
char arg_terminator,
char **const argv,
char *const buffer,
const KernAux_File file,
size_t *const arg_idxs,
const size_t arg_count_max,
const size_t buffer_size
) {
switch (*state) {
case FINAL:
break;
case INITIAL:
if (cur == '\0') {
*state = FINAL;
} else if (cur == ' ') {
*state = WHITESPACE;
} else if (cur == '\\') {
*state = BACKSLASH;
PUT_ARG;
} else if (cur == '"') {
*state = QUOTE;
PUT_ARG;
} else {
*state = TOKEN;
PUT_ARG_AND_CHAR(cur);
}
break;
case WHITESPACE:
if (cur == '\0') {
*state = FINAL;
} else if (cur == ' ') {
// do nothing
} else if (cur == '\\') {
*state = BACKSLASH;
PUT_ARG;
} else if (cur == '"') {
*state = QUOTE;
PUT_ARG;
} else {
*state = TOKEN;
PUT_ARG_AND_CHAR(cur);
}
break;
case TOKEN:
if (cur == '\0') {
*state = FINAL;
PUT_CHAR(arg_terminator);
} else if (cur == ' ') {
*state = WHITESPACE;
PUT_CHAR(arg_terminator);
} else if (cur == '\\') {
*state = BACKSLASH;
} else if (cur == '"') {
FAIL("unescaped quotation mark");
} else {
PUT_CHAR(cur);
}
break;
case BACKSLASH:
if (cur == '\0') {
FAIL("EOL after backslash");
} else {
*state = TOKEN;
PUT_CHAR(cur);
}
break;
case QUOTE:
if (cur == '\0') {
FAIL("EOL inside quote");
} else if (cur == '\\') {
*state = QUOTE_BACKSLASH;
} else if (cur == '"') {
*state = WHITESPACE;
PUT_CHAR(arg_terminator);
} else {
PUT_CHAR(cur);
}
break;
case QUOTE_BACKSLASH:
if (cur == '\0') {
FAIL("EOL after backslash inside quote");
} else {
*state = QUOTE;
PUT_CHAR(cur);
}
break;
}
return true;
}