Created
March 8, 2022 08:42
-
-
Save rlapz/92638a8567238220b87eae8e0549b123 to your computer and use it in GitHub Desktop.
moproj -- Moe Projector
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* moeproj -- Moe Projector | |
* | |
* # Example (file contents): | |
* | |
* -[ This is title 1 ]- | |
* This is the body | |
* | |
* -[ Title 2 ]- | |
* boooodyyy | |
* | |
* ------------------------------- | |
* Arthur Lapz <rlapz@gnuweeb.org> | |
* | |
* MIT License | |
*/ | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <signal.h> | |
/* #include <termios.h> */ | |
#include <unistd.h> | |
#include <sys/ioctl.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#define BUFFER_SIZE_MAX (1024 * 1024) | |
#define SLIDES_SIZE_MAX 32u | |
#define STR_TEXT_BOLD(X) "\033[01;39m" X "\033[00m" | |
#define STR_TITLE_SET(X) "\033]0;" X "\007" | |
#define FUNC_SCREEN_CLEAR() write(STDOUT_FILENO, "\033[2J\033[H", 7) | |
#define FUNC_CURSOR_DISABLE() write(STDOUT_FILENO, "\033[?25l", 6) | |
#define FUNC_CURSOR_ENABLE() write(STDOUT_FILENO, "\033[?25h", 6) | |
struct file { | |
int fd; | |
unsigned size; | |
const char *name; | |
char *buffer; | |
}; | |
struct slide { | |
unsigned name_start; | |
unsigned name_end; | |
unsigned body_start; | |
unsigned body_end; | |
}; | |
struct moeproj { | |
unsigned slide_size; | |
struct file file; | |
struct slide slides[SLIDES_SIZE_MAX]; | |
}; | |
static void signal_handler(int sig); | |
static int open_file(struct file *f); | |
static int load_file(struct file *f); | |
static int setup_token(struct moeproj *m); | |
static inline int key_listener(void); | |
/* Return value must be >= 0 */ | |
static int show_slide(const char *file_name); | |
static int is_interrupted = 0; | |
static void | |
signal_handler(int sig) | |
{ | |
putchar('\n'); | |
printf(STR_TITLE_SET("%s"), "Terminal"); | |
if (is_interrupted >= 1) { | |
printf("signal_handler: %d: exiting...\n", sig); | |
FUNC_CURSOR_ENABLE(); | |
exit(0); | |
} | |
is_interrupted = 1; | |
} | |
static int | |
open_file(struct file *f) | |
{ | |
int fd; | |
struct stat stat; | |
const char *err_msg; | |
if ((fd = open(f->name, O_RDONLY)) < 0) { | |
err_msg = "open_file: open"; | |
goto out2; | |
} | |
if (fstat(fd, &stat) < 0) { | |
err_msg = "open_file: fstat"; | |
goto out1; | |
} | |
if (stat.st_size >= BUFFER_SIZE_MAX) { | |
err_msg = "open_file: file size is too big"; | |
goto out0; | |
} | |
if (stat.st_size == 0) { | |
err_msg = "open_file: file is empty"; | |
goto out0; | |
} | |
f->fd = fd; | |
f->size = (unsigned)stat.st_size; | |
return 0; | |
out0: | |
errno = EINVAL; | |
out1: | |
close(fd); | |
out2: | |
fprintf(stderr, "%s: %s: %s\n", | |
err_msg, f->name, strerror(errno)); | |
return -1; | |
} | |
static int | |
load_file(struct file *f) | |
{ | |
char *new_buf; | |
new_buf = mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0); | |
if (new_buf == MAP_FAILED) { | |
perror("load_file: mmap"); | |
return -1; | |
} | |
f->buffer = new_buf; | |
return 0; | |
} | |
static int | |
setup_token(struct moeproj *m) | |
{ | |
struct slide *sl = m->slides; | |
const char *buffer = m->file.buffer; | |
unsigned slc = 0; /* slide counter */ | |
unsigned buf_end; | |
const char *start, *end; | |
/* Finding the slides (sorrounding by -[]-) | |
* | |
* -[ SLIDE ]- | |
*/ | |
for (const char *p = buffer; *p && slc <= SLIDES_SIZE_MAX; slc++) { | |
if ((start = strstr(p, "-[")) == NULL) | |
break; | |
if ((end = strstr(start, "]-")) == NULL) | |
break; | |
sl[slc].name_start = (unsigned)(start - buffer) +2u; | |
sl[slc].name_end = (unsigned)(end - buffer); | |
p = end; | |
} | |
if (slc == 0) { | |
errno = EINVAL; | |
perror("setup_token: slide is empty"); | |
return -1; | |
} | |
/* Set the Body */ | |
buf_end = m->file.size; | |
m->slide_size = slc; | |
while (slc--) { | |
sl[slc].body_start = sl[slc].name_end +3u; | |
sl[slc].body_end = buf_end; | |
buf_end -= (buf_end - (sl[slc].name_start -2u)); | |
} | |
return 0; | |
} | |
static inline int | |
key_listener(void) | |
{ | |
/* TODO: add event (key) listener */ | |
return getchar(); | |
} | |
static int | |
show_slide(const char *file_name) | |
{ | |
int ret = EXIT_FAILURE; | |
unsigned sl_size; | |
unsigned name_len; | |
unsigned body_len; | |
unsigned col_len; | |
const char *buf; | |
struct slide *sl; | |
struct winsize winsize; | |
struct moeproj moe = { .file.name = file_name }; | |
if (open_file(&moe.file) < 0) | |
return ret; | |
if (load_file(&moe.file) < 0) | |
goto cleanup; | |
close(moe.file.fd); | |
moe.file.fd = -1; | |
if (setup_token(&moe) < 0) | |
goto cleanup; | |
buf = moe.file.buffer; | |
sl = moe.slides; | |
sl_size = moe.slide_size; | |
FUNC_CURSOR_DISABLE(); | |
for (unsigned i = 0; is_interrupted == 0; i++) { | |
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) < 0) { | |
perror("show_slide: ioctl"); | |
goto cleanup; | |
} | |
if (i >= sl_size) | |
i = 0; | |
FUNC_SCREEN_CLEAR(); | |
name_len = sl[i].name_end - sl[i].name_start; | |
body_len = sl[i].body_end - sl[i].body_start; | |
printf(STR_TITLE_SET("%.*s"), name_len, buf + sl[i].name_start); | |
printf("%u [%u]\n", i+1, sl_size); | |
col_len = winsize.ws_col >= name_len ? | |
(winsize.ws_col - name_len) >> 1u : 0; | |
printf("%-*s" STR_TEXT_BOLD("%.*s")"\n\n", | |
col_len, "", | |
name_len, buf + sl[i].name_start); | |
printf("%.*s", body_len, buf + sl[i].body_start); | |
key_listener(); | |
} | |
FUNC_CURSOR_ENABLE(); | |
ret = EXIT_SUCCESS; | |
cleanup: | |
if (moe.file.buffer != MAP_FAILED) { | |
if (munmap(moe.file.buffer, moe.file.size) < 0) | |
perror("show_slide: munmap"); | |
} | |
if (moe.file.fd > 0) | |
close(moe.file.fd); | |
return ret; | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
if (argc != 2) { | |
fprintf(stderr, "%s [FILE_NAME]\n", argv[0]); | |
return EXIT_FAILURE; | |
} | |
struct sigaction act = { .sa_handler = signal_handler }; | |
if (sigaction(SIGINT, &act, NULL) < 0) | |
goto err; | |
if (sigaction(SIGTERM, &act, NULL) < 0) | |
goto err; | |
if (sigaction(SIGHUP, &act, NULL) < 0) | |
goto err; | |
return show_slide(argv[1]); | |
err: | |
perror("main: sigaction"); | |
return EXIT_FAILURE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment