Skip to content

Instantly share code, notes, and snippets.

@rlapz
Created March 8, 2022 08:42
Show Gist options
  • Save rlapz/92638a8567238220b87eae8e0549b123 to your computer and use it in GitHub Desktop.
Save rlapz/92638a8567238220b87eae8e0549b123 to your computer and use it in GitHub Desktop.
moproj -- Moe Projector
/* 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