commit 7072e37fbe7fd4b2f3c4aa2e7563ff4ea407717c
parent 33bd1a644eee0e0d3feb9e9dae562005f9d9194d
Author: afify <hassan@afify.dev>
Date: Sat, 12 Mar 2022 20:15:39 +0300
[feat] signals
Diffstat:
M | Makefile | | | 12 | ++---------- |
A | config.def.h | | | 25 | +++++++++++++++++++++++++ |
M | nasmfm.c | | | 362 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
A | term.c | | | 252 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | term.h | | | 41 | +++++++++++++++++++++++++++++++++++++++++ |
A | utf8.c | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | utf8.h | | | 15 | +++++++++++++++ |
A | util.c | | | 44 | ++++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 17 | +++++++++++++++++ |
9 files changed, 652 insertions(+), 169 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@
include config.mk
-SRC = nasmfm.c
+SRC = nasmfm.c term.c utf8.c util.c
OBJ = ${SRC:.c=.o}
all: options nasmfm
@@ -17,7 +17,7 @@ options:
.c.o:
${CC} -c ${CFLAGS} $<
-${OBJ}: config.mk
+${OBJ}: config.h config.mk
config.h:
cp config.def.h $@
@@ -28,14 +28,6 @@ nasmfm: ${OBJ}
clean:
rm -f nasmfm ${OBJ} nasmfm-${VERSION}.tar.gz
-dist: clean
- mkdir -p nasmfm-${VERSION}
- cp -R LICENSE Makefile README.md config.def.h config.mk\
- nasmfm.1 nasmfm.png termbox.h util.h ${SRC} nasmfm-${VERSION}
- tar -cf nasmfm-${VERSION}.tar nasmfm-${VERSION}
- gzip nasmfm-${VERSION}.tar
- rm -rf nasmfm-${VERSION}
-
install: nasmfm
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f nasmfm ${DESTDIR}${PREFIX}/bin
diff --git a/config.def.h b/config.def.h
@@ -0,0 +1,25 @@
+/* See LICENSE file for copyright and license details. */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/* colors fg, bg att*/
+static const Cpair cdir = { 31, 0, NORM };
+static const Cpair cfile = { 243, 0, NORM };
+static const Cpair clnk = { 96, 0, NORM };
+static const Cpair cblk = { 95, 0, NORM };
+static const Cpair cchr = { 94, 0, NORM };
+static const Cpair cifo = { 93, 0, NORM };
+static const Cpair csock = { 92, 0, NORM };
+static const Cpair cexec = { 91, 0, NORM };
+static const Cpair cother = { 90, 0, NORM };
+
+static const Cpair cframe = { 233, 233, NORM };
+static const Cpair cpanell = { 166, 233, BOLD };
+static const Cpair cpanelr = { 5, 233, BOLD };
+static const Cpair cerr = { 124, 0, NORM };
+static const Cpair cprompt = { 33, 0, NORM };
+static const Cpair csearch = { 255, 0, NORM };
+static const Cpair cstatus = { 243, 0, NORM };
+
+#endif /* CONFIG_H */
diff --git a/nasmfm.c b/nasmfm.c
@@ -1,217 +1,261 @@
/* See LICENSE file for copyright and license details. */
-#include <sys/ioctl.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <stddef.h>
+#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
+#include "term.h"
+#include "util.h"
+
/* macros */
-#define CTRL_KEY(k) ((k)&0x1f)
-#define CLEAR_SCREEN abAppend(&ab, "\x1b[2J", 4);
-#define CURSOR_TOP_LEFT abAppend(&ab, "\x1b[H", 3);
-#define CURSOR_HIDE abAppend(&ab, "\x1b[?25l", 6);
-#define CURSOR_SHOW abAppend(&ab, "\x1b[?25h", 6);
-#define ABUF_INIT \
- { \
- NULL, 0 \
- }
+#define MAX_P 4096
+#define MAX_N 255
+#define MAX_USRI 32
+#define MAX_EXT 4
+#define MAX_STATUS 255
+#define MAX_LINE 4096
+#define MAX_USRN 32
+#define MAX_GRPN 32
+#define MAX_DTF 32
+#define CURSOR(x) (x)->direntr[(x)->hdir - 1]
/* typedef */
-/* function declarations */
+typedef struct {
+ char name[MAX_N];
+ gid_t group;
+ mode_t mode;
+ off_t size;
+ time_t dt;
+ uid_t user;
+} Entry;
+
+typedef struct {
+ //int pane_id;
+ char dirn[MAX_P]; // dir name cwd
+ //char *filter;
+ //Entry *direntr; // dir entries
+ //int dirc; // dir entries sum
+ //int hdir; // highlighted dir
+ int x_srt;
+ int x_end;
+ //int firstrow;
+ //int parent_firstrow;
+ //int parent_row; // FIX
+ Cpair dircol;
+ //int inotify_wd;
+ //int event_fd;
+} Pane;
typedef struct {
- int cx;
- int cy;
- int rows;
- int cols;
- struct termios orig_termios;
-} Term;
-
-struct abuf {
- char *b;
- int len;
-};
+ const char **ext;
+ size_t exlen;
+ const void *v;
+ size_t vlen;
+} Rule;
+
+typedef union {
+ uint16_t key; /* one of the TB_KEY_* constants */
+ uint32_t ch; /* unicode character */
+} Evkey;
+
+typedef union {
+ int i;
+ const void *v;
+} Arg;
+
+typedef struct {
+ const Evkey evkey;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
/* global variables */
-Term term;
+static Term *term;
+static pthread_t fsev_thread;
+static Pane panes[2];
+static Pane *cpane;
+static int pane_idx;
+static char *editor[2];
+static char fed[] = "vi";
+static char *shell[2];
+static char sh[] = "/bin/sh";
+//static int theight, twidth, hwidth, scrheight;
+//static int *sel_indexes;
+//static size_t sel_len = 0;
+//static char **sel_files;
+//static int cont_vmode = 0;
+//static int cont_change = 0;
+static pid_t fork_pid = 0, main_pid;
+//#if defined(_SYS_INOTIFY_H)
+//#define READEVSZ 16
+//static int inotify_fd;
+//#elif defined(_SYS_EVENT_H_)
+//#define READEVSZ 0
+//static int kq;
+//struct kevent evlist[2]; /* events we want to monitor */
+//struct kevent chlist[2]; /* events that were triggered */
+//static struct timespec gtimeout;
+//#endif
+#if defined(__linux__) || defined(__FreeBSD__)
+#define OFF_T "%ld"
+#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+#define OFF_T "%lld"
+#endif
+enum { Left, Right }; /* panes */
+enum { Wait, DontWait }; /* spawn forks */
-/* function implementations */
-void
-abAppend(struct abuf *ab, const char *s, int len)
-{
- char *new = realloc(ab->b, ab->len + len);
- if (new == NULL)
- return;
- memcpy(&new[ab->len], s, len);
- ab->b = new;
- ab->len += len;
-}
-void
-abFree(struct abuf *ab)
-{
- free(ab->b);
-}
+/* configuration, allows nested code to access above variables */
+#include "config.h"
-void
-die(const char *s)
+static void
+listdir(Pane *pane)
{
- //CLEAR_SCREEN
- //CURSOR_TOP_LEFT
- perror(s);
- exit(1);
+ // print pane cwd
+ twrite(pane->x_srt, 0, pane->dirn, strlen(pane->dirn), pane->dircol);
}
-void
-disableRawMode()
+static void
+set_panes(void)
{
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term.orig_termios) == -1)
- die("tcsetattr");
-}
+ char *home;
+ char cwd[MAX_P];
-void
-enableRawMode()
-{
- if (tcgetattr(STDIN_FILENO, &term.orig_termios) == -1)
- die("tcgetattr");
- atexit(disableRawMode);
- struct termios raw = term.orig_termios;
-
- raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
- raw.c_oflag &= ~(OPOST);
- raw.c_cflag |= (CS8);
- raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
- raw.c_cc[VMIN] = 0;
- raw.c_cc[VTIME] = 1;
-
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
- die("tcsetattr");
-}
+ home = getenv("HOME");
+ if (home == NULL)
+ home = "/";
+ if ((getcwd(cwd, sizeof(cwd)) == NULL))
+ strncpy(cwd, home, MAX_P);
-void
-editorDrawRows(struct abuf *ab)
-{
- int y;
- for (y = 0; y < term.rows; y++) {
- if (y == term.rows / 3) {
- char welcome[80];
- int welcomelen = snprintf(welcome, sizeof(welcome),
- "Kilo editor -- version nasmfm");
- if (welcomelen > term.cols)
- welcomelen = term.cols;
- int padding = (term.cols - welcomelen) / 2;
- if (padding) {
- abAppend(ab, "~", 1);
- padding--;
- }
- while (padding--)
- abAppend(ab, " ", 1);
- abAppend(ab, welcome, welcomelen);
- } else {
- abAppend(ab, "~", 1);
- }
- abAppend(ab, "\x1b[K", 3);
- if (y < term.rows - 1) {
- abAppend(ab, "\r\n", 2);
- }
- }
-}
+ pane_idx = Left; /* cursor pane */
+ cpane = &panes[pane_idx];
-void
-editorRefreshScreen()
-{
- struct abuf ab = ABUF_INIT;
- abAppend(&ab, "\x1b[?25l", 6);
- abAppend(&ab, "\x1b[H", 3);
- editorDrawRows(&ab);
- char buf[32];
- snprintf(buf, sizeof(buf), "\x1b[%d;%dH", term.cy + 1, term.cx + 1);
- abAppend(&ab, buf, strlen(buf));
- abAppend(&ab, "\x1b[?25h", 6);
- write(STDOUT_FILENO, ab.b, ab.len);
- abFree(&ab);
+ //panes[Left].pane_id = 0;
+ panes[Left].x_srt = 2;
+ panes[Left].x_end = (term->cols / 2) - 1;
+ panes[Left].dircol = cpanell;
+ //panes[Left].firstrow = 0;
+ //panes[Left].direntr = ecalloc(0, sizeof(Entry));
+ strncpy(panes[Left].dirn, cwd, MAX_P);
+ //panes[Left].hdir = 1;
+ //panes[Left].inotify_wd = -1;
+ //panes[Left].parent_row = 1;
+
+ //panes[Right].pane_id = 1;
+ panes[Right].x_srt = (term->cols / 2) + 2;
+ panes[Right].x_end = term->cols - 1;
+ panes[Right].dircol = cpanelr;
+ //panes[Right].firstrow = 0;
+ //panes[Right].direntr = ecalloc(0, sizeof(Entry));
+ strncpy(panes[Right].dirn, home, MAX_P);
+ //panes[Right].hdir = 1;
+ //panes[Right].inotify_wd = -1;
+ //panes[Right].parent_row = 1;
}
-char
-editorReadKey()
+static void
+get_editor(void)
{
- int nread;
- char c;
- while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
- if (nread == -1 && errno != EAGAIN)
- die("read");
- }
- return c;
+ editor[0] = getenv("EDITOR");
+ editor[1] = NULL;
+
+ if (editor[0] == NULL)
+ editor[0] = fed;
}
-void
-twrite(int x, int y, char *str, size_t len)
+static void
+get_shell(void)
{
- if (x > term.rows || y > term.cols)
- return;
- char buf[term.cols];
- size_t full_buf;
-
- snprintf(buf, term.cols, "\x1b[%d;%df%s", x, y, str);
- full_buf = strlen(buf);
- write(STDOUT_FILENO, buf, full_buf);
+ shell[0] = getenv("SHELL");
+ shell[1] = NULL;
+
+ if (shell[0] == NULL)
+ shell[0] = sh;
}
void
-editorProcessKeypress()
+keypress()
{
- char c = editorReadKey();
+ char c = getkey();
char buf[50];
- snprintf(buf, sizeof(buf), "THIS IS LETTER %c [%ld]=>", c, sizeof(buf));
+ Cpair col;
+ int signo = 156;
+ snprintf(buf, sizeof(buf), "<%d>THIS IS LETTER %c [%ld] => %d/%d",
+ main_pid, c, sizeof(buf), term->cols, term->rows);
switch (c) {
case 'q':
- write(STDOUT_FILENO, "\x1b[2J", 4);
- write(STDOUT_FILENO, "\x1b[H", 3);
+ CLEAR_SCREEN
+ //CURSOR_TOP_LEFT
+ CURSOR_SHOW
exit(0);
break;
default:
- twrite(10, 10, buf, strlen(buf));
+ col.bg = 0;
+ col.fg = 211;
+ col.attr = RVS;
+
+ tprintf(2, term->rows -2, col, "SIGWINCH = %d\n", signo);
+ //twrite(2, term->rows - 2, buf, strlen(buf), col);
break;
}
}
-int
-getWindowSize(int *rows, int *cols)
+void
+sighandler(int signo)
{
- struct winsize ws;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
- return -1;
- } else {
- *cols = ws.ws_col;
- *rows = ws.ws_row;
- return 0;
+ Cpair col;
+ col.bg = 0;
+ col.fg = 211;
+ col.attr = RVS;
+
+ switch (signo) {
+ case SIGWINCH:
+ get_term_size(&term->rows, &term->cols);
+ CLEAR_SCREEN
+ //CURSOR_TOP_LEFT
+ draw_frame();
+ tprintf(3,3, col, "SIGWINCH = %d [%d-%d]", signo, term->cols,term->rows);
+ break;
+ case SIGUSR1:
+ tprintf(1, 1, col, "SIGUSR1 = %d", signo);
+ break;
+ case SIGUSR2:
+ tprintf(1, 1, col, "SIGUSR2 = %d", signo);
+ break;
}
+
}
-void
-initEditor()
+static int
+start_signal(void)
{
- term.cx = 0;
- term.cy = 0;
- if (getWindowSize(&term.rows, &term.cols) == -1)
- die("getWindowSize");
+ struct sigaction sa;
+ main_pid = getpid();
+ sa.sa_handler = sighandler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &sa, 0);
+ sigaction(SIGUSR2, &sa, 0);
+ sigaction(SIGWINCH, &sa, 0);
+ return 0;
}
int
main(int argc, char *argv[])
{
- enableRawMode();
- initEditor();
- write(STDOUT_FILENO, "\x1b[2J", 4);
- write(STDOUT_FILENO, "\x1b[H", 3);
+ term = init_term();
+ draw_frame();
+ set_panes();
+ get_editor();
+ get_shell();
+ listdir(&panes[Left]);
+ listdir(&panes[Right]);
+ start_signal();
while (1) {
- //editorRefreshScreen();
- editorProcessKeypress();
+ keypress();
}
return 0;
}
diff --git a/term.c b/term.c
@@ -0,0 +1,252 @@
+/* See LICENSE file for copyright and license details. */
+
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <termios.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <stdint.h>
+
+#include "term.h"
+#include "utf8.h"
+#include "util.h"
+
+/* macros */
+/* typedef */
+/* function declarations */
+/* global variables */
+static Term oterm;
+
+/* function implementations */
+
+void
+die(const char *s)
+{
+ CLEAR_SCREEN
+ //CURSOR_TOP_LEFT
+ perror(s);
+ exit(1);
+}
+
+
+void
+disableRawMode()
+{
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &oterm.orig_termios) == -1)
+ die("tcsetattr");
+}
+
+
+void
+raw_mode()
+{
+ if (tcgetattr(STDIN_FILENO, &oterm.orig_termios) == -1)
+ die("tcgetattr");
+ atexit(disableRawMode);
+ struct termios raw = oterm.orig_termios;
+
+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_cc[VMIN] = 0;
+ raw.c_cc[VTIME] = 1;
+
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
+ die("tcsetattr");
+}
+
+int
+get_term_size(int *rows, int *cols)
+{
+ struct winsize ws;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ return -1;
+ } else {
+ *cols = ws.ws_col;
+ *rows = ws.ws_row;
+ return 0;
+ }
+}
+
+Term*
+init_term()
+{
+ raw_mode();
+ CLEAR_SCREEN
+ //CURSOR_TOP_LEFT
+ CURSOR_HIDE
+ oterm.cx = 0;
+ oterm.cy = 0;
+ if (get_term_size(&oterm.rows, &oterm.cols) == -1)
+ die("getWindowSize");
+ return &oterm;
+}
+
+
+void
+twrite(int x, int y, char *str, size_t len, Cpair col)
+{
+ if (x > oterm.cols || y > oterm.rows)
+ return;
+ char buf[oterm.cols];
+ size_t full_buf;
+
+ snprintf(buf, oterm.cols,
+ "\x1b[%d;%df\x1b[%d;48;5;%d;38;5;%dm%s\x1b[0;0m",
+ y, x, col.attr, col.bg, col.fg, str);
+ full_buf = strlen(buf);
+ write(STDOUT_FILENO, buf, full_buf);
+}
+
+void
+tprintf(int x, int y, Cpair col, const char *fmt, ...)
+{
+ if (x > oterm.cols || y > oterm.rows)
+ return;
+ char buf[oterm.cols]; // fmt result
+ char str[oterm.cols]; // with color and position
+ size_t full_buf;
+ va_list vl;
+ va_start(vl, fmt);
+ (void)vsnprintf(buf, oterm.cols, fmt, vl);
+ va_end(vl);
+
+ //print_tb(buf, x, y, col.fg, col.bg);
+ snprintf(str, oterm.cols,
+ "\x1b[%d;%df\x1b[%d;48;5;%d;38;5;%dm%s\x1b[0;0m",
+ y, x, col.attr, col.bg, col.fg, buf);
+ full_buf = strlen(str);
+ write(STDOUT_FILENO, str, full_buf);
+}
+
+void
+move_to_col(int y)
+{
+ char buf[8];
+ size_t full_buf;
+ snprintf(buf, 8, "\x1b[%dG", y);
+ full_buf = strlen(buf);
+ write(STDOUT_FILENO, buf, full_buf);
+}
+
+void
+draw_frame()
+{
+ int y;
+ char str[4];
+ size_t str_len;
+ Rune d = 0x2588;
+
+ str_len = utf8encode(d, str);
+
+ // TODO move_to_col
+ char buf[16];
+ int colframe = 233;
+ size_t full_buf;
+ snprintf(buf, 16, "\x1b[38;5;%dm", colframe);
+ full_buf = strlen(buf);
+ write(STDOUT_FILENO, buf, full_buf);
+
+ for (y = 0; y < oterm.cols ; y++) {
+ write(STDOUT_FILENO, str, str_len);
+ }
+ for (y = 1; y < oterm.rows - 2; y++) {
+ write(STDOUT_FILENO, str, str_len);
+ move_to_col(oterm.cols/2);
+ write(STDOUT_FILENO, str, str_len);
+ write(STDOUT_FILENO, str, str_len);
+ move_to_col(oterm.cols);
+ write(STDOUT_FILENO, str, str_len);
+ if (y < oterm.rows - 3) {
+ write(STDOUT_FILENO, "\r\n", 2);
+ }
+ }
+ for (y = 0; y < oterm.cols; y++) {
+ write(STDOUT_FILENO, str, str_len);
+ }
+ for (y = 0; y < oterm.cols; y++) {
+ write(STDOUT_FILENO, str, str_len);
+ }
+ write(STDOUT_FILENO, "\x1b[0;0m", 6);
+}
+
+char
+getkey()
+{
+ int nread;
+ char c;
+ while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
+ if (nread == -1 && errno != EAGAIN)
+ die("read");
+ }
+ return c;
+}
+
+//void
+//abAppend(struct abuf *ab, const char *s, int len)
+//{
+// char *new = realloc(ab->b, ab->len + len);
+// if (new == NULL)
+// return;
+// memcpy(&new[ab->len], s, len);
+// ab->b = new;
+// ab->len += len;
+//}
+//void
+//abFree(struct abuf *ab)
+//{
+// free(ab->b);
+//}
+
+//void
+//editorDrawRows(struct abuf *ab)
+//{
+// int y;
+// for (y = 0; y < term.rows; y++) {
+// if (y == term.rows / 3) {
+// char welcome[80];
+// int welcomelen = snprintf(welcome, sizeof(welcome),
+// "Kilo editor -- version nasmfm");
+// if (welcomelen > term.cols)
+// welcomelen = term.cols;
+// int padding = (term.cols - welcomelen) / 2;
+// if (padding) {
+// abAppend(ab, "~", 1);
+// padding--;
+// }
+// while (padding--)
+// abAppend(ab, " ", 1);
+// abAppend(ab, welcome, welcomelen);
+// } else {
+// abAppend(ab, "~", 1);
+// }
+// abAppend(ab, "\x1b[K", 3);
+// if (y < term.rows - 1) {
+// abAppend(ab, "\r\n", 2);
+// }
+// }
+//}
+
+//void
+//editorRefreshScreen()
+//{
+// struct abuf ab = { NULL, 0 };
+// abAppend(&ab, "\x1b[?25l", 6);
+// abAppend(&ab, "\x1b[H", 3);
+// CURSOR_TOP_LEFT
+// editorDrawRows(&ab);
+// char buf[32];
+// snprintf(buf, sizeof(buf), "\x1b[%d;%dH", term.cy + 1, term.cx + 1);
+// abAppend(&ab, buf, strlen(buf));
+// abAppend(&ab, "\x1b[?25h", 6);
+// write(STDOUT_FILENO, ab.b, ab.len);
+// abFree(&ab);
+//}
diff --git a/term.h b/term.h
@@ -0,0 +1,41 @@
+/* See LICENSE file for copyright and license details. */
+
+#ifndef TERM_H
+#define TERM_H
+
+#define CTRL_KEY(k) ((k)&0x1f)
+#define CLEAR_SCREEN write(STDOUT_FILENO, "\x1b[2J\x1b[H", 7);
+#define CURSOR_HIDE write(STDOUT_FILENO, "\x1b[?25l", 6);
+#define CURSOR_SHOW write(STDOUT_FILENO, "\x1b[?25h", 6);
+
+#define NORM 0
+#define BOLD 1
+#define RVS 7
+
+typedef struct {
+ int cx;
+ int cy;
+ int rows;
+ int cols;
+ struct termios orig_termios;
+} Term;
+
+struct abuf {
+ char *b;
+ int len;
+};
+
+typedef struct {
+ uint16_t fg;
+ uint16_t bg;
+ uint8_t attr;
+} Cpair;
+
+Term* init_term(void);
+void draw_frame(void);
+void twrite(int, int, char *, size_t, Cpair);
+void tprintf(int, int, Cpair, const char *, ...);
+void move_to_col(int);
+char getkey(void);
+int get_term_size(int *, int *);
+#endif /* TERM_H */
diff --git a/utf8.c b/utf8.c
@@ -0,0 +1,53 @@
+/* See LICENSE file for copyright and license details. */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "utf8.h"
+#include "util.h"
+
+/* macros */
+/* typedef */
+/* function declarations */
+/* global variables */
+/* function implementations */
+
+
+static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+size_t
+utf8validate(Rune *u, size_t i)
+{
+ if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for (i = 1; *u > utfmax[i]; ++i)
+ ;
+
+ return i;
+}
+
+char
+utf8encodebyte(Rune u, size_t i)
+{
+ return utfbyte[i] | (u & ~utfmask[i]);
+}
+size_t
+utf8encode(Rune u, char *c)
+{
+ size_t len, i;
+
+ len = utf8validate(&u, 0);
+ if (len > UTF_SIZ)
+ return 0;
+
+ for (i = len - 1; i != 0; --i) {
+ c[i] = utf8encodebyte(u, 0);
+ u >>= 6;
+ }
+ c[0] = utf8encodebyte(u, len);
+
+ return len;
+}
diff --git a/utf8.h b/utf8.h
@@ -0,0 +1,15 @@
+/* See LICENSE file for copyright and license details. */
+
+#ifndef UTF8_H
+#define UTF8_H
+
+#define UTF_SIZ 4
+#define UTF_INVALID 0xFFFD
+
+typedef uint_least32_t Rune;
+typedef unsigned char uchar;
+
+size_t utf8validate(Rune *, size_t);
+size_t utf8encode(Rune, char *);
+char utf8encodebyte(Rune, size_t);
+#endif /* UTF8_H */
diff --git a/util.c b/util.c
@@ -0,0 +1,44 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+ p = calloc(nmemb, size);
+ FAIL_IF(p == NULL, "calloc");
+ return p;
+}
+
+//void *
+//erealloc(void *p, size_t len)
+//{
+// if ((p = realloc(p, len)) == NULL)
+// die("realloc: %s\n", strerror(errno));
+// return p;
+//}
+
+//void
+//die(const char *fmt, ...)
+//{
+// va_list ap;
+//
+// va_start(ap, fmt);
+// (void)vfprintf(stderr, fmt, ap);
+// va_end(ap);
+//
+// if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
+// (void)fputc(' ', stderr);
+// perror(NULL);
+// } else {
+// (void)fputc('\n', stderr);
+// }
+//
+// exit(EXIT_FAILURE);
+//}
diff --git a/util.h b/util.h
@@ -0,0 +1,17 @@
+/* See LICENSE file for copyright and license details. */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define LEN(A) (sizeof(A)/sizeof(A[0]))
+#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+#define FAIL_IF(EXP, MSG) {if(EXP){fprintf(stderr, "[\033[31mFAILED %d\033[0m] %s\n", __LINE__, MSG);exit(EXIT_FAILURE);}}
+#define PERROR(EXP) {if(EXP){print_error(strerror(errno));}};
+
+void *ecalloc(size_t, size_t);
+void *erealloc(void*, size_t);
+//void die(const char *fmt, ...);
+
+#endif /* UTIL_H */