From 0765cd04e68beb49d93983d48796a460ca8b913d Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Tue, 28 Jun 2022 02:13:40 +0300 Subject: [PATCH] Async cmdline (#104) --- .gitignore | 1 - README.md | 6 +- configure.ac | 1 - include/kernaux/{printf.h.in => printf.h} | 0 src/cmdline.c | 355 ++++++++++++---------- 5 files changed, 205 insertions(+), 158 deletions(-) rename include/kernaux/{printf.h.in => printf.h} (100%) diff --git a/.gitignore b/.gitignore index 44ef892c..932444ed 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md index b209f98b..df3c10ef 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/configure.ac b/configure.ac index ee572217..4f53663c 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ]) diff --git a/include/kernaux/printf.h.in b/include/kernaux/printf.h similarity index 100% rename from include/kernaux/printf.h.in rename to include/kernaux/printf.h diff --git a/src/cmdline.c b/src/cmdline.c index 48de810b..f87c3fee 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -6,7 +6,8 @@ #include #include -#include "libc.h" +#include +#include 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; +}