mirror of https://github.com/tailix/libkernaux.git
Async cmdline (#104)
This commit is contained in:
parent
dacdfede2b
commit
0765cd04e6
|
@ -103,7 +103,6 @@
|
||||||
/include/kernaux.h
|
/include/kernaux.h
|
||||||
/include/kernaux/drivers/console.h
|
/include/kernaux/drivers/console.h
|
||||||
/include/kernaux/multiboot2.h
|
/include/kernaux/multiboot2.h
|
||||||
/include/kernaux/printf.h
|
|
||||||
/include/kernaux/version.h
|
/include/kernaux/version.h
|
||||||
|
|
||||||
/examples/assert
|
/examples/assert
|
||||||
|
|
|
@ -66,18 +66,18 @@ zero). Work-in-progress APIs can change at any time.
|
||||||
* Data formats
|
* Data formats
|
||||||
* [ELF](/include/kernaux/elf.h) (*work in progress*)
|
* [ELF](/include/kernaux/elf.h) (*work in progress*)
|
||||||
* [MBR](/include/kernaux/mbr.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
|
* Utilities
|
||||||
* [Measurement units utils](/include/kernaux/units.h) (*work in progress*)
|
* [Measurement units utils](/include/kernaux/units.h) (*work in progress*)
|
||||||
* [Example: To human](/examples/units_human.c)
|
* [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)
|
* [Example](/examples/memmap.c)
|
||||||
* [printf format parser](/include/kernaux/printf_fmt.h) (*work in progress*)
|
* [printf format parser](/include/kernaux/printf_fmt.h) (*work in progress*)
|
||||||
* [Example](/examples/printf_fmt.c)
|
* [Example](/examples/printf_fmt.c)
|
||||||
* Usual functions
|
* Usual functions
|
||||||
* [itoa/ftoa replacement](/include/kernaux/ntoa.h) (*non-breaking since* **0.4.0**)
|
* [itoa/ftoa replacement](/include/kernaux/ntoa.h) (*non-breaking since* **0.4.0**)
|
||||||
* [Example](/examples/ntoa.c)
|
* [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: fprintf](/examples/printf_file.c)
|
||||||
* [Example: vfprintf](/examples/printf_file_va.c)
|
* [Example: vfprintf](/examples/printf_file_va.c)
|
||||||
* [Example: snprintf](/examples/printf_str.c)
|
* [Example: snprintf](/examples/printf_str.c)
|
||||||
|
|
|
@ -32,7 +32,6 @@ AC_CONFIG_FILES([
|
||||||
include/kernaux.h
|
include/kernaux.h
|
||||||
include/kernaux/drivers/console.h
|
include/kernaux/drivers/console.h
|
||||||
include/kernaux/multiboot2.h
|
include/kernaux/multiboot2.h
|
||||||
include/kernaux/printf.h
|
|
||||||
include/kernaux/version.h
|
include/kernaux/version.h
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
])
|
])
|
||||||
|
|
355
src/cmdline.c
355
src/cmdline.c
|
@ -6,7 +6,8 @@
|
||||||
#include <kernaux/cmdline.h>
|
#include <kernaux/cmdline.h>
|
||||||
#include <kernaux/generic/file.h>
|
#include <kernaux/generic/file.h>
|
||||||
|
|
||||||
#include "libc.h"
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
INITIAL,
|
INITIAL,
|
||||||
|
@ -31,9 +32,24 @@ static bool kernaux_cmdline_common(
|
||||||
size_t buffer_size
|
size_t buffer_size
|
||||||
);
|
);
|
||||||
|
|
||||||
/*****************************
|
static bool kernaux_cmdline_iter(
|
||||||
* Implementations: main API *
|
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(
|
bool kernaux_cmdline(
|
||||||
const char *const cmdline,
|
const char *const cmdline,
|
||||||
|
@ -93,9 +109,9 @@ bool kernaux_cmdline_file(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************
|
/*********************************
|
||||||
* Implementation: main internal function *
|
* Implementation: main function *
|
||||||
******************************************/
|
*********************************/
|
||||||
|
|
||||||
#define CLEAR do { \
|
#define CLEAR do { \
|
||||||
*argc = 0; \
|
*argc = 0; \
|
||||||
|
@ -104,62 +120,6 @@ bool kernaux_cmdline_file(
|
||||||
if (arg_idxs) memset(arg_idxs, 0, sizeof(size_t) * arg_count_max); \
|
if (arg_idxs) memset(arg_idxs, 0, sizeof(size_t) * arg_count_max); \
|
||||||
} while (0)
|
} 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(
|
bool kernaux_cmdline_common(
|
||||||
const char *const cmdline,
|
const char *const cmdline,
|
||||||
char *const error_msg,
|
char *const error_msg,
|
||||||
|
@ -185,96 +145,22 @@ bool kernaux_cmdline_common(
|
||||||
enum State state = INITIAL;
|
enum State state = INITIAL;
|
||||||
size_t buffer_or_file_pos = 0;
|
size_t buffer_or_file_pos = 0;
|
||||||
|
|
||||||
for (size_t index = 0; ; ++index) {
|
for (size_t index = 0; state != FINAL; ++index) {
|
||||||
const char cur = cmdline[index];
|
const bool result = kernaux_cmdline_iter(
|
||||||
|
cmdline[index],
|
||||||
switch (state) {
|
&state,
|
||||||
case FINAL:
|
&buffer_or_file_pos,
|
||||||
break; // Case break; loop break after switch.
|
error_msg,
|
||||||
|
argc,
|
||||||
case INITIAL:
|
arg_terminator,
|
||||||
if (cur == '\0') {
|
argv,
|
||||||
state = FINAL;
|
buffer,
|
||||||
} else if (cur == ' ') {
|
file,
|
||||||
state = WHITESPACE;
|
arg_idxs,
|
||||||
} else if (cur == '\\') {
|
arg_count_max,
|
||||||
state = BACKSLASH;
|
buffer_size
|
||||||
PUT_ARG;
|
);
|
||||||
} else if (cur == '"') {
|
if (!result) goto fail;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -283,3 +169,166 @@ fail:
|
||||||
CLEAR;
|
CLEAR;
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue