Make config reading functions re-entrant

This commit is contained in:
Nikita Ivanov 2022-07-14 22:51:56 +05:00
parent 3cfe9a5f79
commit a187ad9dde
No known key found for this signature in database
GPG Key ID: 6E656AC5B97B5133
3 changed files with 126 additions and 111 deletions

View File

@ -1,6 +1,7 @@
#include "ctpv.h" #include "ctpv.h"
#include "lexer.h" #include "lexer.h"
#include "error.h" #include "error.h"
#include "config.h"
#include "preview.h" #include "preview.h"
#define CHECK(f, cond) \ #define CHECK(f, cond) \
@ -13,9 +14,9 @@
#define CHECK_OK(f) CHECK(f, x == STAT_OK) #define CHECK_OK(f) CHECK(f, x == STAT_OK)
#define CHECK_NULL(f) CHECK(f, x == STAT_NULL) #define CHECK_NULL(f) CHECK(f, x == STAT_NULL)
#define EXPECT(x) CHECK_OK(expect(x)) #define EXPECT(c, x) CHECK_OK(expect(c, x))
#define ACCEPT(x) CHECK_OK(accept(x)) #define ACCEPT(c, x) CHECK_OK(accept(c, x))
#define NOT_ACCEPT(x) CHECK_NULL(accept(x)) #define NOT_ACCEPT(c, x) CHECK_NULL(accept(c, x))
#define DEF_OPTION(name, type, val) { (#name), (type), { .val = &ctpv.opts.name } } #define DEF_OPTION(name, type, val) { (#name), (type), { .val = &ctpv.opts.name } }
#define DEF_OPTION_BOOL(name) DEF_OPTION(name, OPTION_BOOL, i) #define DEF_OPTION_BOOL(name) DEF_OPTION(name, OPTION_BOOL, i)
@ -24,6 +25,12 @@
#define TYPE_SET_EMPTY (struct TypeSet){ NULL, NULL, NULL } #define TYPE_SET_EMPTY (struct TypeSet){ NULL, NULL, NULL }
struct Parser {
Lexer *lexer;
Token token;
VectorPreview *previews;
};
struct Option { struct Option {
char *name; char *name;
enum { enum {
@ -47,10 +54,6 @@ enum {
STAT_NULL, STAT_NULL,
}; };
static Lexer *lexer;
static Token token;
static VectorPreview *previews;
static struct Option options[] = { static struct Option options[] = {
DEF_OPTION_BOOL(forcekitty), DEF_OPTION_BOOL(forcekitty),
DEF_OPTION_BOOL(forcekittyanim), DEF_OPTION_BOOL(forcekittyanim),
@ -66,10 +69,10 @@ static void any_type_null(char **s)
*s = NULL; *s = NULL;
} }
static void add_preview(char *name, char *script, struct TypeSet *set, static void add_preview(Parser *ctx, char *name, char *script, struct TypeSet *set,
unsigned int set_len) unsigned int set_len)
{ {
if (!previews) if (!ctx->previews)
return; return;
size_t script_len = strlen(script) + 1; size_t script_len = strlen(script) + 1;
@ -90,131 +93,131 @@ static void add_preview(char *name, char *script, struct TypeSet *set,
.priority = 0 .priority = 0
}; };
vectorPreview_append(previews, p); vectorPreview_append(ctx->previews, p);
} }
} }
static int add_priority(char *name, int priority) static int add_priority(Parser *ctx, char *name, int priority)
{ {
if (!previews) if (!ctx->previews)
return OK; return OK;
int found = 0; int found = 0;
for (size_t i = 0; i < previews->len; i++) { for (size_t i = 0; i < ctx->previews->len; i++) {
if (strcmp(previews->buf[i].name, name) != 0) if (strcmp(ctx->previews->buf[i].name, name) != 0)
continue; continue;
previews->buf[i].priority = priority; ctx->previews->buf[i].priority = priority;
found = 1; found = 1;
} }
return found ? OK : ERR; return found ? OK : ERR;
} }
static int remove_preview(char *name) static int remove_preview(Parser *ctx, char *name)
{ {
if (!previews) if (!ctx->previews)
return OK; return OK;
int found = 0; int found = 0;
for (ssize_t i = previews->len - 1; i >= 0; i--) { for (ssize_t i = ctx->previews->len - 1; i >= 0; i--) {
if (strcmp(previews->buf[i].name, name) != 0) if (strcmp(ctx->previews->buf[i].name, name) != 0)
continue; continue;
vectorPreview_remove(previews, i); vectorPreview_remove(ctx->previews, i);
found = 1; found = 1;
} }
return found ? OK : ERR; return found ? OK : ERR;
} }
static inline void next_token(void) static inline void next_token(Parser *ctx)
{ {
token = lexer_get_token(lexer); ctx->token = lexer_get_token(ctx->lexer);
} }
static int accept(enum TokenType type) static int accept(Parser *ctx, enum TokenType type)
{ {
if (token.type == type) { if (ctx->token.type == type) {
next_token(); next_token(ctx);
return STAT_OK; return STAT_OK;
} }
return STAT_NULL; return STAT_NULL;
} }
static int expect(enum TokenType type) static int expect(Parser *ctx, enum TokenType type)
{ {
if (accept(type) == STAT_OK) if (accept(ctx, type) == STAT_OK)
return STAT_OK; return STAT_OK;
if (token.type == TOK_ERR) if (ctx->token.type == TOK_ERR)
return STAT_ERR; return STAT_ERR;
PARSEERROR(token, "unexpected token: %s, expected: %s", PARSEERROR(ctx->token, "unexpected token: %s, expected: %s",
lexer_token_type_str(token.type), lexer_token_type_str(ctx->token.type),
lexer_token_type_str(type)); lexer_token_type_str(type));
return STAT_ERR; return STAT_ERR;
} }
static int preview_type_ext(char **ext) static int preview_type_ext(Parser *ctx, char **ext)
{ {
ACCEPT(TOK_DOT); ACCEPT(ctx, TOK_DOT);
Token tok = token; Token tok = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
*ext = tok.val.s; *ext = tok.val.s;
return STAT_OK; return STAT_OK;
} }
static int preview_type_mime_part(char **s) static int preview_type_mime_part(Parser *ctx, char **s)
{ {
NOT_ACCEPT(TOK_STAR); NOT_ACCEPT(ctx, TOK_STAR);
Token tok = token; Token tok = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
*s = tok.val.s; *s = tok.val.s;
return STAT_OK; return STAT_OK;
} }
static int preview_type_mime(char **type, char **subtype) static int preview_type_mime(Parser *ctx, char **type, char **subtype)
{ {
CHECK_OK(preview_type_mime_part(type)); CHECK_OK(preview_type_mime_part(ctx, type));
EXPECT(TOK_SLASH); EXPECT(ctx, TOK_SLASH);
CHECK_OK(preview_type_mime_part(subtype)); CHECK_OK(preview_type_mime_part(ctx, subtype));
return STAT_OK; return STAT_OK;
} }
static inline void num_is_text(void) static inline void num_is_text(Parser *ctx)
{ {
lexer_set_opts(lexer, LEX_OPT_NUMISTEXT); lexer_set_opts(ctx->lexer, LEX_OPT_NUMISTEXT);
} }
static inline void reset_lexer_opts(void) static inline void reset_lexer_opts(Parser *ctx)
{ {
lexer_set_opts(lexer, LEX_OPT_NONE); lexer_set_opts(ctx->lexer, LEX_OPT_NONE);
} }
static int preview_type(struct TypeSet *set) static int preview_type(Parser *ctx, struct TypeSet *set)
{ {
int ret; int ret;
*set = TYPE_SET_EMPTY; *set = TYPE_SET_EMPTY;
num_is_text(); num_is_text(ctx);
if ((ret = preview_type_ext(&set->ext)) != STAT_NULL) if ((ret = preview_type_ext(ctx, &set->ext)) != STAT_NULL)
goto exit; goto exit;
ret = preview_type_mime(&set->type, &set->subtype); ret = preview_type_mime(ctx, &set->type, &set->subtype);
exit: exit:
reset_lexer_opts(); reset_lexer_opts(ctx);
return ret; return ret;
} }
@ -228,10 +231,10 @@ static struct Option *get_option(char *name)
return NULL; return NULL;
} }
static int cmd_set(void) static int cmd_set(Parser *ctx)
{ {
Token name = token; Token name = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
struct Option *opt = get_option(name.val.s); struct Option *opt = get_option(name.val.s);
if (!opt) { if (!opt) {
@ -239,18 +242,18 @@ static int cmd_set(void)
return STAT_ERR; return STAT_ERR;
} }
Token value = token; Token value = ctx->token;
switch (opt->arg_type) { switch (opt->arg_type) {
case OPTION_BOOL: case OPTION_BOOL:
*opt->arg_val.i = accept(TOK_INT) == STAT_OK ? value.val.i : 1; *opt->arg_val.i = accept(ctx, TOK_INT) == STAT_OK ? value.val.i : 1;
break; break;
case OPTION_INT: case OPTION_INT:
EXPECT(TOK_INT); EXPECT(ctx, TOK_INT);
*opt->arg_val.i = value.val.i; *opt->arg_val.i = value.val.i;
break; break;
case OPTION_STR: case OPTION_STR:
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
*opt->arg_val.s = value.val.s; *opt->arg_val.s = value.val.s;
break; break;
default: default:
@ -261,42 +264,42 @@ static int cmd_set(void)
return STAT_OK; return STAT_OK;
} }
static int cmd_preview(void) static int cmd_preview(Parser *ctx)
{ {
Token name = token; Token name = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
struct TypeSet types[16]; struct TypeSet types[16];
unsigned int types_len = 0; unsigned int types_len = 0;
while (accept(TOK_BLK_OPEN) == STAT_NULL) { while (accept(ctx, TOK_BLK_OPEN) == STAT_NULL) {
if (types_len >= LEN(types)) { if (types_len >= LEN(types)) {
PARSEERROR(name, "a preview can only have up through %lu types", PARSEERROR(name, "a preview can only have up through %lu types",
LEN(types)); LEN(types));
return STAT_ERR; return STAT_ERR;
} }
CHECK_OK(preview_type(types + types_len++)); CHECK_OK(preview_type(ctx, types + types_len++));
} }
Token script = token; Token script = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
EXPECT(TOK_BLK_CLS); EXPECT(ctx, TOK_BLK_CLS);
add_preview(name.val.s, script.val.s, types, types_len); add_preview(ctx, name.val.s, script.val.s, types, types_len);
return STAT_OK; return STAT_OK;
} }
static int cmd_priority(Token tok) static int cmd_priority(Parser *ctx)
{ {
Token name = token; Token name = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
Token number = token; Token number = ctx->token;
int i = accept(TOK_INT) == STAT_OK ? number.val.i : 1; int i = accept(ctx, TOK_INT) == STAT_OK ? number.val.i : 1;
if (add_priority(name.val.s, i) != OK) { if (add_priority(ctx, name.val.s, i) != OK) {
PARSEERROR(name, "preview '%s' not found", name.val.s); PARSEERROR(name, "preview '%s' not found", name.val.s);
return STAT_ERR; return STAT_ERR;
} }
@ -304,12 +307,12 @@ static int cmd_priority(Token tok)
return STAT_OK; return STAT_OK;
} }
static int cmd_remove(Token tok) static int cmd_remove(Parser *ctx)
{ {
Token name = token; Token name = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
if (remove_preview(name.val.s) != OK) { if (remove_preview(ctx, name.val.s) != OK) {
PARSEERROR(name, "preview '%s' not found", name.val.s); PARSEERROR(name, "preview '%s' not found", name.val.s);
return STAT_ERR; return STAT_ERR;
} }
@ -317,58 +320,58 @@ static int cmd_remove(Token tok)
return STAT_OK; return STAT_OK;
} }
static int command(void) static int command(Parser *ctx)
{ {
Token cmd = token; Token cmd = ctx->token;
EXPECT(TOK_STR); EXPECT(ctx, TOK_STR);
if (strcmp(cmd.val.s, "set") == 0) if (strcmp(cmd.val.s, "set") == 0)
return cmd_set(); return cmd_set(ctx);
else if (strcmp(cmd.val.s, "preview") == 0) else if (strcmp(cmd.val.s, "preview") == 0)
return cmd_preview(); return cmd_preview(ctx);
else if (strcmp(cmd.val.s, "priority") == 0) else if (strcmp(cmd.val.s, "priority") == 0)
return cmd_priority(cmd); return cmd_priority(ctx);
else if (strcmp(cmd.val.s, "remove") == 0) else if (strcmp(cmd.val.s, "remove") == 0)
return cmd_remove(cmd); return cmd_remove(ctx);
PARSEERROR(cmd, "unknown command: %s", cmd.val.s); PARSEERROR(cmd, "unknown command: %s", cmd.val.s);
return STAT_ERR; return STAT_ERR;
} }
static void newlines(void) static void newlines(Parser *ctx)
{ {
while (1) { while (1) {
if (accept(TOK_NEW_LN) != STAT_OK) if (accept(ctx, TOK_NEW_LN) != STAT_OK)
break; break;
} }
} }
static int end(void) static int end(Parser *ctx)
{ {
NOT_ACCEPT(TOK_EOF); NOT_ACCEPT(ctx, TOK_EOF);
EXPECT(TOK_NEW_LN); EXPECT(ctx, TOK_NEW_LN);
newlines(); newlines(ctx);
return STAT_OK; return STAT_OK;
} }
static int commands(void) static int commands(Parser *ctx)
{ {
newlines(); newlines(ctx);
while (1) { while (1) {
NOT_ACCEPT(TOK_EOF); NOT_ACCEPT(ctx, TOK_EOF);
CHECK_OK(command()); CHECK_OK(command(ctx));
CHECK_OK(end()); CHECK_OK(end(ctx));
} }
} }
static int parse(void) static int parse(Parser *ctx)
{ {
#ifdef DEBUG_LEXER #ifdef DEBUG_LEXER
while (1) { while (1) {
next_token(); next_token(ctx);
if (token.type == TOK_EOF) if (token.type == TOK_EOF)
break; break;
printf("%s", lexer_token_type_str(token.type)); printf("%s", lexer_token_type_str(token.type));
@ -386,25 +389,30 @@ static int parse(void)
} }
#endif #endif
#ifndef DEBUG_LEXER #ifndef DEBUG_LEXER
next_token(); next_token(ctx);
if (commands() == STAT_ERR) if (commands(ctx) == STAT_ERR)
return ERR; return ERR;
#endif #endif
return OK; return OK;
} }
int config_load(VectorPreview *prevs, char *filename) int config_load(Parser **ctx, VectorPreview *prevs, char *filename)
{ {
int ret = OK; int ret = OK;
FILE *f; FILE *f;
ERRCHK_GOTO_ERN(!(f = fopen(filename, "r")), ret, exit); ERRCHK_GOTO_ERN(!(f = fopen(filename, "r")), ret, exit);
lexer = lexer_init(f); if (!(*ctx = malloc(sizeof(**ctx)))) {
previews = prevs; FUNCFAILED("malloc", strerror(errno));
abort();
}
ERRCHK_GOTO_OK(parse(), ret, file); (*ctx)->lexer = lexer_init(f);
(*ctx)->previews = prevs;
ERRCHK_GOTO_OK(parse(*ctx), ret, file);
file: file:
fclose(f); fclose(f);
@ -413,11 +421,13 @@ exit:
return ret; return ret;
} }
void config_cleanup(void) void config_cleanup(Parser *ctx)
{ {
if (!lexer) if (!ctx->lexer)
return; return;
lexer_free(lexer); lexer_free(ctx->lexer);
lexer = NULL; ctx->lexer = NULL;
free(ctx);
} }

View File

@ -3,7 +3,9 @@
#include "preview.h" #include "preview.h"
int config_load(VectorPreview *prevs, char *filename); typedef struct Parser Parser;
void config_cleanup(void);
int config_load(Parser **ctx, VectorPreview *prevs, char *filename);
void config_cleanup(Parser *ctx);
#endif #endif

View File

@ -26,12 +26,15 @@ const char any_type[] = ANY_TYPE;
static magic_t magic; static magic_t magic;
static Parser *parser;
static VectorPreview *previews; static VectorPreview *previews;
static void cleanup(void) static void cleanup(void)
{ {
previews_cleanup(); previews_cleanup();
config_cleanup(); if (parser)
config_cleanup(parser);
if (magic != NULL) if (magic != NULL)
magic_close(magic); magic_close(magic);
if (previews) if (previews)
@ -74,7 +77,7 @@ static int config(int prevs)
char config_file[FILENAME_MAX]; char config_file[FILENAME_MAX];
get_config_file(config_file, LEN(config_file)); get_config_file(config_file, LEN(config_file));
ERRCHK_RET_OK(config_load(prevs ? previews : NULL, config_file)); ERRCHK_RET_OK(config_load(&parser, prevs ? previews : NULL, config_file));
return OK; return OK;
} }