mirror of
https://github.com/tailix/libkernaux.git
synced 2024-10-30 11:54:01 -04:00
Async cmdline (#104)
This commit is contained in:
parent
dacdfede2b
commit
0765cd04e6
5 changed files with 205 additions and 158 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
])
|
||||
|
|
229
src/cmdline.c
229
src/cmdline.c
|
@ -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,56 +145,152 @@ 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];
|
||||
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;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
return true;
|
||||
|
||||
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 break; loop break after switch.
|
||||
break;
|
||||
|
||||
case INITIAL:
|
||||
if (cur == '\0') {
|
||||
state = FINAL;
|
||||
*state = FINAL;
|
||||
} else if (cur == ' ') {
|
||||
state = WHITESPACE;
|
||||
*state = WHITESPACE;
|
||||
} else if (cur == '\\') {
|
||||
state = BACKSLASH;
|
||||
*state = BACKSLASH;
|
||||
PUT_ARG;
|
||||
} else if (cur == '"') {
|
||||
state = QUOTE;
|
||||
*state = QUOTE;
|
||||
PUT_ARG;
|
||||
} else {
|
||||
state = TOKEN;
|
||||
*state = TOKEN;
|
||||
PUT_ARG_AND_CHAR(cur);
|
||||
}
|
||||
break;
|
||||
|
||||
case WHITESPACE:
|
||||
if (cur == '\0') {
|
||||
state = FINAL;
|
||||
*state = FINAL;
|
||||
} else if (cur == ' ') {
|
||||
// do nothing
|
||||
} else if (cur == '\\') {
|
||||
state = BACKSLASH;
|
||||
*state = BACKSLASH;
|
||||
PUT_ARG;
|
||||
} else if (cur == '"') {
|
||||
state = QUOTE;
|
||||
*state = QUOTE;
|
||||
PUT_ARG;
|
||||
} else {
|
||||
state = TOKEN;
|
||||
*state = TOKEN;
|
||||
PUT_ARG_AND_CHAR(cur);
|
||||
}
|
||||
break;
|
||||
|
||||
case TOKEN:
|
||||
if (cur == '\0') {
|
||||
state = FINAL;
|
||||
*state = FINAL;
|
||||
PUT_CHAR(arg_terminator);
|
||||
} else if (cur == ' ') {
|
||||
state = WHITESPACE;
|
||||
*state = WHITESPACE;
|
||||
PUT_CHAR(arg_terminator);
|
||||
} else if (cur == '\\') {
|
||||
state = BACKSLASH;
|
||||
*state = BACKSLASH;
|
||||
} else if (cur == '"') {
|
||||
FAIL("unescaped quotation mark");
|
||||
} else {
|
||||
|
@ -246,7 +302,7 @@ bool kernaux_cmdline_common(
|
|||
if (cur == '\0') {
|
||||
FAIL("EOL after backslash");
|
||||
} else {
|
||||
state = TOKEN;
|
||||
*state = TOKEN;
|
||||
PUT_CHAR(cur);
|
||||
}
|
||||
break;
|
||||
|
@ -255,9 +311,9 @@ bool kernaux_cmdline_common(
|
|||
if (cur == '\0') {
|
||||
FAIL("EOL inside quote");
|
||||
} else if (cur == '\\') {
|
||||
state = QUOTE_BACKSLASH;
|
||||
*state = QUOTE_BACKSLASH;
|
||||
} else if (cur == '"') {
|
||||
state = WHITESPACE;
|
||||
*state = WHITESPACE;
|
||||
PUT_CHAR(arg_terminator);
|
||||
} else {
|
||||
PUT_CHAR(cur);
|
||||
|
@ -268,18 +324,11 @@ bool kernaux_cmdline_common(
|
|||
if (cur == '\0') {
|
||||
FAIL("EOL after backslash inside quote");
|
||||
} else {
|
||||
state = QUOTE;
|
||||
*state = QUOTE;
|
||||
PUT_CHAR(cur);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == FINAL) break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
CLEAR;
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue