mirror of
https://github.com/NikitaIvanovV/ctpv.git
synced 2024-11-24 05:43:08 +01:00
Add custom previews support
This commit is contained in:
parent
55cd11f545
commit
ab47acdf04
208
config.c
Normal file
208
config.c
Normal file
@ -0,0 +1,208 @@
|
||||
#include "lexer.h"
|
||||
#include "error.h"
|
||||
#include "preview.h"
|
||||
|
||||
#define CHECK(f, cond) \
|
||||
do { \
|
||||
int x = (f); \
|
||||
if (cond) \
|
||||
return x; \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_OK(f) CHECK(f, x != STAT_OK)
|
||||
#define CHECK_NULL(f) CHECK(f, x != STAT_NULL)
|
||||
#define CHECK_OK_NULL(f) CHECK(f, x != STAT_OK || x != STAT_NULL)
|
||||
|
||||
enum {
|
||||
STAT_OK,
|
||||
STAT_ERR,
|
||||
STAT_NULL,
|
||||
};
|
||||
|
||||
static Lexer *lexer;
|
||||
static Token token;
|
||||
static VectorPreview *previews;
|
||||
static char *any_type;
|
||||
|
||||
static void add_preview(char *name, char *script, char *type, char *subtype,
|
||||
char *ext)
|
||||
{
|
||||
if (type && strcmp(type, any_type) == 0)
|
||||
type = NULL;
|
||||
|
||||
if (subtype && strcmp(subtype, any_type) == 0)
|
||||
subtype = NULL;
|
||||
|
||||
Preview p = (Preview){
|
||||
.name = name,
|
||||
.script = script,
|
||||
.script_len = strlen(script),
|
||||
.type = type,
|
||||
.subtype = subtype,
|
||||
.ext = ext,
|
||||
.priority = 1 /* custom previews are always prioritized */
|
||||
};
|
||||
|
||||
vectorPreview_append(previews, p);
|
||||
}
|
||||
|
||||
static inline void next_token(void)
|
||||
{
|
||||
token = lexer_get_token(lexer);
|
||||
}
|
||||
|
||||
static int accept(enum TokenType type)
|
||||
{
|
||||
if (token.type == type) {
|
||||
next_token();
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
return STAT_NULL;
|
||||
}
|
||||
|
||||
static int expect(enum TokenType type)
|
||||
{
|
||||
if (accept(type) == STAT_OK)
|
||||
return STAT_OK;
|
||||
|
||||
if (token.type == TOK_ERR)
|
||||
return STAT_ERR;
|
||||
|
||||
print_errorf("unexpected token: %s, expected: %s",
|
||||
lexer_token_type_str(token.type),
|
||||
lexer_token_type_str(type));
|
||||
return STAT_ERR;
|
||||
}
|
||||
|
||||
static inline char *get_str(Token tok)
|
||||
{
|
||||
return lexer_get_string(lexer, tok);
|
||||
}
|
||||
|
||||
static int preview_type_ext(char **ext)
|
||||
{
|
||||
CHECK_OK(accept(TOK_DOT));
|
||||
|
||||
Token tok = token;
|
||||
CHECK_OK(expect(TOK_STR));
|
||||
*ext = get_str(tok);
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
static int preview_type_mime_part(char **s)
|
||||
{
|
||||
*s = NULL;
|
||||
CHECK_NULL(accept(TOK_STAR));
|
||||
|
||||
Token t = token;
|
||||
CHECK_OK(expect(TOK_STR));
|
||||
*s = get_str(t);
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
static int preview_type_mime(char **type, char **subtype)
|
||||
{
|
||||
CHECK_OK(preview_type_mime_part(type));
|
||||
CHECK_OK(expect(TOK_SLASH));
|
||||
CHECK_OK(preview_type_mime_part(subtype));
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
static int preview_type(char **type, char **subtype, char **ext)
|
||||
{
|
||||
CHECK_NULL(preview_type_ext(ext));
|
||||
return preview_type_mime(type, subtype);
|
||||
}
|
||||
|
||||
static int new_preview(void)
|
||||
{
|
||||
Token name = token;
|
||||
CHECK_OK(expect(TOK_STR));
|
||||
|
||||
char *type = NULL;
|
||||
char *subtype = NULL;
|
||||
char *ext = NULL;
|
||||
CHECK_OK(preview_type(&type, &subtype, &ext));
|
||||
|
||||
CHECK_OK(expect(TOK_BLK_OPEN));
|
||||
|
||||
Token script = token;
|
||||
CHECK_OK(expect(TOK_STR));
|
||||
|
||||
CHECK_OK(expect(TOK_BLK_CLS));
|
||||
|
||||
add_preview(get_str(name), get_str(script), type, subtype, ext);
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
static int priority(void)
|
||||
{
|
||||
print_error("priority is not supported yet");
|
||||
return STAT_ERR;
|
||||
}
|
||||
|
||||
static int command(void)
|
||||
{
|
||||
Token cmd = token;
|
||||
CHECK_OK(expect(TOK_STR));
|
||||
|
||||
char *cmd_str = get_str(cmd);
|
||||
if (strcmp(cmd_str, "preview") == 0)
|
||||
return new_preview();
|
||||
else if (strcmp(cmd_str, "priority") == 0)
|
||||
return priority();
|
||||
|
||||
print_errorf("unknown command: %s", cmd_str);
|
||||
return STAT_ERR;
|
||||
}
|
||||
|
||||
static int commands(void)
|
||||
{
|
||||
accept(TOK_END);
|
||||
|
||||
while (1) {
|
||||
CHECK_NULL(accept(TOK_EOF));
|
||||
CHECK_OK(command());
|
||||
CHECK_OK_NULL(accept(TOK_END));
|
||||
}
|
||||
}
|
||||
|
||||
static int parse(void)
|
||||
{
|
||||
next_token();
|
||||
|
||||
if (commands() == STAT_ERR)
|
||||
return ERR;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int config_load(VectorPreview *prevs, char *filename, char *any_type_)
|
||||
{
|
||||
int ret = OK;
|
||||
|
||||
FILE *f = fopen(filename, "r");
|
||||
ERRCHK_GOTO(!f, ret, exit, FUNCFAILED("fopen"), ERRNOS);
|
||||
|
||||
lexer = lexer_init(f);
|
||||
previews = prevs;
|
||||
any_type = any_type_;
|
||||
|
||||
ERRCHK_GOTO_OK(parse(), ret, file);
|
||||
|
||||
file:
|
||||
fclose(f);
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void config_cleanup(void)
|
||||
{
|
||||
if (lexer)
|
||||
lexer_free(lexer);
|
||||
}
|
9
config.h
Normal file
9
config.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include "preview.h"
|
||||
|
||||
int config_load(VectorPreview *prevs, char *filename, char *any_type_);
|
||||
void config_cleanup(void);
|
||||
|
||||
#endif
|
56
ctpv.c
56
ctpv.c
@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <magic.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
@ -9,13 +10,14 @@
|
||||
|
||||
#include "error.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "server.h"
|
||||
#include "preview.h"
|
||||
#include "previews.h"
|
||||
|
||||
#define ANY_TYPE "*"
|
||||
|
||||
static const char any_type[] = ANY_TYPE;
|
||||
static char any_type[] = ANY_TYPE;
|
||||
|
||||
static magic_t magic;
|
||||
|
||||
@ -31,11 +33,16 @@ static struct {
|
||||
char *server_id_s;
|
||||
} ctpv = { .mode = MODE_PREVIEW };
|
||||
|
||||
static VectorPreview *previews;
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
cleanup_previews();
|
||||
config_cleanup();
|
||||
if (magic != NULL)
|
||||
magic_close(magic);
|
||||
if (previews)
|
||||
vectorPreview_free(previews);
|
||||
}
|
||||
|
||||
static int init_magic(void)
|
||||
@ -49,9 +56,41 @@ static int init_magic(void)
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void init_previews_v(void)
|
||||
static int create_dir(char *buf, size_t len)
|
||||
{
|
||||
init_previews(previews, LEN(previews));
|
||||
char dir[len];
|
||||
strncpy(dir, buf, LEN(dir) - 1);
|
||||
ERRCHK_RET(mkpath(dir, 0700) == -1, FUNCFAILED("mkpath"), ERRNOS);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int get_config_file(char *buf, size_t len)
|
||||
{
|
||||
ERRCHK_RET_OK(get_config_dir(buf, len, "ctpv/"));
|
||||
ERRCHK_RET_OK(create_dir(buf, len));
|
||||
|
||||
strncat(buf, "config", len - 1);
|
||||
|
||||
if (access(buf, F_OK) != 0)
|
||||
close(creat(buf, 0600));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int init_previews_v(void)
|
||||
{
|
||||
previews = vectorPreview_new(LEN(b_previews));
|
||||
vectorPreview_append_arr(previews, b_previews, LEN(b_previews));
|
||||
|
||||
char config_file[FILENAME_MAX];
|
||||
get_config_file(config_file, LEN(config_file));
|
||||
|
||||
ERRCHK_RET_OK(config_load(previews, config_file, any_type));
|
||||
|
||||
init_previews(previews->buf, previews->len);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static const char *get_mimetype(const char *path)
|
||||
@ -133,12 +172,7 @@ static void md5_string(char *buf, size_t len, char *s)
|
||||
static int get_cache_file(char *buf, size_t len, char *file)
|
||||
{
|
||||
ERRCHK_RET_OK(get_cache_dir(buf, len, "ctpv/"));
|
||||
|
||||
{
|
||||
char dir[len];
|
||||
strncpy(dir, buf, LEN(dir) - 1);
|
||||
ERRCHK_RET(mkpath(dir, 0700) == -1, FUNCFAILED("mkpath"), ERRNOS);
|
||||
}
|
||||
ERRCHK_RET_OK(create_dir(buf, len));
|
||||
|
||||
char name[64];
|
||||
md5_string(name, LEN(name) - 1, file);
|
||||
@ -173,7 +207,7 @@ static int preview(int argc, char *argv[])
|
||||
|
||||
ERRCHK_RET_OK(init_magic());
|
||||
|
||||
init_previews_v();
|
||||
ERRCHK_RET_OK(init_previews_v());
|
||||
|
||||
const char *mimetype;
|
||||
ERRCHK_RET(!(mimetype = get_mimetype(f)));
|
||||
@ -209,7 +243,7 @@ static int end(void)
|
||||
|
||||
static int list(void)
|
||||
{
|
||||
init_previews_v();
|
||||
ERRCHK_RET_OK(init_previews_v());
|
||||
|
||||
size_t len;
|
||||
Preview p, **list = get_previews_list(&len);
|
||||
|
100
lexer.c
100
lexer.c
@ -9,15 +9,17 @@
|
||||
print_errorf("config parse error:%u:%u " format, (c).line, (c).col \
|
||||
__VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
#define TOK_TYPE_ALIAS(t) ((Token){ .type = t })
|
||||
#define TOK_TYPE(t) ((Token){ .type = t })
|
||||
|
||||
#define NULL_TOK TOK_TYPE_ALIAS(TOK_NULL)
|
||||
#define EOF_TOK TOK_TYPE_ALIAS(TOK_EOF)
|
||||
#define END_TOK TOK_TYPE_ALIAS(TOK_END)
|
||||
#define ERR_TOK TOK_TYPE_ALIAS(TOK_ERR)
|
||||
#define NULL_TOK TOK_TYPE(TOK_NULL)
|
||||
#define EOF_TOK TOK_TYPE(TOK_EOF)
|
||||
#define END_TOK TOK_TYPE(TOK_END)
|
||||
#define ERR_TOK TOK_TYPE(TOK_ERR)
|
||||
|
||||
#define READ_PUNCT(c, t, s) read_punct((c), (t), (s), LEN(s) - 1)
|
||||
|
||||
#define EOF_CHAR (-1)
|
||||
|
||||
typedef int (*Predicate)(int);
|
||||
|
||||
typedef struct {
|
||||
@ -38,7 +40,11 @@ struct Lexer {
|
||||
VectorChar *text_buf;
|
||||
};
|
||||
|
||||
static char block_open[] = "{{{", block_close[] = "}}}";
|
||||
static char block_open[] = "{{{",
|
||||
block_close[] = "}}}",
|
||||
slash[] = "/",
|
||||
star[] = "*",
|
||||
dot[] = ".";
|
||||
|
||||
static void add_token_queue(Lexer *ctx, Token tok)
|
||||
{
|
||||
@ -74,7 +80,7 @@ static int peekn_char(Lexer *ctx, unsigned int i)
|
||||
goto exit;
|
||||
|
||||
if (b->eof || (i > 0 && i >= b->len))
|
||||
return -1;
|
||||
return EOF_CHAR;
|
||||
|
||||
if (i > 0) {
|
||||
assert(i < LEN(b->buf));
|
||||
@ -93,7 +99,7 @@ static int peekn_char(Lexer *ctx, unsigned int i)
|
||||
PRINTINTERR("fread() failed");
|
||||
|
||||
if (b->len == 0)
|
||||
return -1;
|
||||
return EOF_CHAR;
|
||||
}
|
||||
|
||||
exit:
|
||||
@ -217,28 +223,20 @@ static void read_while(Lexer *ctx, Predicate p, int add)
|
||||
add_text_buf(ctx, '\0');
|
||||
}
|
||||
|
||||
static inline Token read_eof(Lexer *ctx)
|
||||
static inline Token read_end(Lexer *ctx)
|
||||
{
|
||||
char c = peek_char(ctx);
|
||||
Token tok = NULL_TOK;
|
||||
|
||||
if (c >= 0)
|
||||
return NULL_TOK;
|
||||
while (peek_char(ctx) == '\n') {
|
||||
char c = peek_char(ctx);
|
||||
if (c != '\n')
|
||||
break;
|
||||
|
||||
next_char(ctx);
|
||||
next_char(ctx);
|
||||
tok = END_TOK;
|
||||
}
|
||||
|
||||
return EOF_TOK;
|
||||
}
|
||||
|
||||
static inline Token read_newline(Lexer *ctx)
|
||||
{
|
||||
char c = peek_char(ctx);
|
||||
|
||||
if (c != '\n')
|
||||
return NULL_TOK;
|
||||
|
||||
next_char(ctx);
|
||||
|
||||
return END_TOK;
|
||||
return tok;
|
||||
}
|
||||
|
||||
static inline Token read_symbol(Lexer *ctx)
|
||||
@ -274,7 +272,7 @@ static Token read_punct(Lexer *ctx, int type, char *s, int n)
|
||||
{
|
||||
Token tok;
|
||||
|
||||
if (peek_char(ctx) < 0)
|
||||
if (peek_char(ctx) == EOF_CHAR)
|
||||
return EOF_TOK;
|
||||
|
||||
int ret = cmp_nextn(ctx, n, s);
|
||||
@ -337,6 +335,15 @@ static Token read_block(Lexer *ctx)
|
||||
return t; \
|
||||
} while (0)
|
||||
|
||||
#define ATTEMPT_READ_CHAR(ctx, ch, type) \
|
||||
do { \
|
||||
char c = peek_char(ctx); \
|
||||
if (c == (ch)) { \
|
||||
next_char(ctx); \
|
||||
return (type); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
Token lexer_get_token(Lexer *ctx)
|
||||
{
|
||||
if (!is_empty_token_queue(ctx))
|
||||
@ -344,8 +351,12 @@ Token lexer_get_token(Lexer *ctx)
|
||||
|
||||
read_while(ctx, isblank, 0);
|
||||
|
||||
ATTEMPT_READ(ctx, read_eof);
|
||||
ATTEMPT_READ(ctx, read_newline);
|
||||
ATTEMPT_READ_CHAR(ctx, EOF_CHAR, EOF_TOK);
|
||||
ATTEMPT_READ_CHAR(ctx, '/', TOK_TYPE(TOK_SLASH));
|
||||
ATTEMPT_READ_CHAR(ctx, '*', TOK_TYPE(TOK_STAR));
|
||||
ATTEMPT_READ_CHAR(ctx, '.', TOK_TYPE(TOK_DOT));
|
||||
|
||||
ATTEMPT_READ(ctx, read_end);
|
||||
ATTEMPT_READ(ctx, read_symbol);
|
||||
ATTEMPT_READ(ctx, read_digit);
|
||||
ATTEMPT_READ(ctx, read_block);
|
||||
@ -361,3 +372,34 @@ char *lexer_get_string(Lexer *ctx, Token tok)
|
||||
|
||||
return get_text_buf_at(ctx, tok.val.sp);
|
||||
}
|
||||
|
||||
char *lexer_token_type_str(enum TokenType type)
|
||||
{
|
||||
switch (type) {
|
||||
case TOK_NULL:
|
||||
return "<null>";
|
||||
case TOK_EOF:
|
||||
return "<end of file>";
|
||||
case TOK_ERR:
|
||||
return "<TOKEN ERROR>";
|
||||
case TOK_END:
|
||||
return "<end>";
|
||||
case TOK_BLK_OPEN:
|
||||
return block_open;
|
||||
case TOK_BLK_CLS:
|
||||
return block_close;
|
||||
case TOK_SLASH:
|
||||
return slash;
|
||||
case TOK_STAR:
|
||||
return star;
|
||||
case TOK_DOT:
|
||||
return dot;
|
||||
case TOK_INT:
|
||||
return "<integer>";
|
||||
case TOK_STR:
|
||||
return "<string>";
|
||||
}
|
||||
|
||||
PRINTINTERR("unknown type: %d", type);
|
||||
abort();
|
||||
}
|
||||
|
6
lexer.h
6
lexer.h
@ -7,13 +7,16 @@
|
||||
typedef struct Lexer Lexer;
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
enum TokenType {
|
||||
TOK_NULL,
|
||||
TOK_EOF,
|
||||
TOK_ERR,
|
||||
TOK_END,
|
||||
TOK_BLK_OPEN,
|
||||
TOK_BLK_CLS,
|
||||
TOK_SLASH,
|
||||
TOK_STAR,
|
||||
TOK_DOT,
|
||||
TOK_INT,
|
||||
TOK_STR,
|
||||
} type;
|
||||
@ -27,5 +30,6 @@ Lexer *lexer_init(FILE *f);
|
||||
void lexer_free(Lexer *ctx);
|
||||
Token lexer_get_token(Lexer *ctx);
|
||||
char *lexer_get_string(Lexer *ctx, Token tok);
|
||||
char *lexer_token_type_str(enum TokenType type);
|
||||
|
||||
#endif
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#define PREVP_SIZE sizeof(Preview *)
|
||||
|
||||
VECTOR_GEN_SOURCE(Preview, Preview)
|
||||
|
||||
static struct {
|
||||
size_t len;
|
||||
Preview **list;
|
||||
|
@ -3,12 +3,16 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
typedef struct {
|
||||
char *name, *ext, *type, *subtype, *script;
|
||||
int priority;
|
||||
size_t script_len;
|
||||
} Preview;
|
||||
|
||||
VECTOR_GEN_HEADER(Preview, Preview)
|
||||
|
||||
typedef struct {
|
||||
char *f, *w, *h, *x, *y, *id;
|
||||
char *cache_file;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#define PP(e, t, s, n, p) { #n, e, t, s, PNAME(n), p, LEN(PNAME(n)) }
|
||||
#define PR(e, t, s, n) PP(e, t, s, n, 0)
|
||||
|
||||
Preview previews[] = {
|
||||
Preview b_previews[] = {
|
||||
PP(NULL, NULL, NULL, wrapper, INT_MAX),
|
||||
PR(NULL, "text", NULL, bat),
|
||||
PR(NULL, "text", NULL, highlight),
|
||||
|
22
utils.c
22
utils.c
@ -87,22 +87,32 @@ int strlennull(const char *s)
|
||||
return s ? strlen(s) : 0;
|
||||
}
|
||||
|
||||
int get_cache_dir(char *buf, size_t len, char *name)
|
||||
static int get_xdg_dir(char *buf, size_t len, char *var, char *var_sub, char *name)
|
||||
{
|
||||
char *home, *cache_d, cache_d_buf[FILENAME_MAX];
|
||||
char *home, *dir, dir_buf[FILENAME_MAX];
|
||||
|
||||
if (!(cache_d = getenv("XDG_CACHE_HOME"))) {
|
||||
if (!(dir = getenv(var))) {
|
||||
home = getenv("HOME");
|
||||
ERRCHK_RET(!home, "HOME env var does not exist");
|
||||
|
||||
snprintf(cache_d_buf, LEN(cache_d_buf)-1, "%s/.cache", home);
|
||||
cache_d = cache_d_buf;
|
||||
snprintf(dir_buf, LEN(dir_buf)-1, "%s/%s", home, var_sub);
|
||||
dir = dir_buf;
|
||||
}
|
||||
|
||||
snprintf(buf, len - 1, "%s/%s", cache_d, name);
|
||||
snprintf(buf, len - 1, "%s/%s", dir, name);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int get_cache_dir(char *buf, size_t len, char *name)
|
||||
{
|
||||
return get_xdg_dir(buf, len, "XDG_CACHE_HOME", ".cache", name);
|
||||
}
|
||||
|
||||
int get_config_dir(char *buf, size_t len, char *name)
|
||||
{
|
||||
return get_xdg_dir(buf, len, "XDG_CONFIG_HOME", ".config", name);
|
||||
}
|
||||
|
||||
int mkpath(char* file_path, mode_t mode)
|
||||
{
|
||||
for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
|
||||
|
Loading…
Reference in New Issue
Block a user