add uft-8 support - lchat - A line oriented chat front end for ii. | |
git clone git://git.suckless.org/lchat | |
Log | |
Files | |
Refs | |
README | |
--- | |
commit 0f8af7612e393d40705e953306bc0555f026c2f7 | |
parent 569292df0c821cf2c977d224345ec009e344cb1c | |
Author: Jan Klemkow <[email protected]> | |
Date: Sat, 9 Jul 2016 20:30:21 +0200 | |
add uft-8 support | |
Diffstat: | |
M Makefile | 26 +++++++++++++++++++++----- | |
M lchat.c | 44 +++++++++++++++++++++--------… | |
M slackline.c | 151 ++++++++++++++++++++++++-----… | |
M slackline.h | 18 ++++++++++++++++-- | |
4 files changed, 183 insertions(+), 56 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
@@ -1,18 +1,34 @@ | |
-CC=cc | |
-CFLAGS=-std=c99 -pedantic -Wall -Wextra | |
+CC ?= cc | |
+CFLAGS = -std=c99 -pedantic -Wall -Wextra -g | |
-.PHONY: all clean | |
+# utf.h | |
+CFLAGS += -I/usr/local/include | |
+LIBS = -L/usr/local/lib -lutf | |
+ | |
+.PHONY: all clean test debug | |
all: lchat | |
clean: | |
- rm -f lchat *.o | |
+ rm -f lchat *.o *.core | |
+ | |
+test: sl_test | |
+ ./sl_test | |
+ | |
+debug: | |
+ gdb lchat lchat.core | |
lchat: lchat.o slackline.o | |
- $(CC) -o $@ lchat.o slackline.o | |
+ $(CC) -o $@ lchat.o slackline.o $(LIBS) | |
lchat.o: lchat.c | |
$(CC) -c $(CFLAGS) -D_BSD_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE \ | |
-o $@ lchat.c | |
+sl_test.o: sl_test.c slackline.h | |
+ $(CC) $(CFLAGS) -c -o $@ sl_test.c | |
+ | |
+sl_test: sl_test.o slackline.o slackline.h | |
+ $(CC) $(CFLAGS) -o $@ sl_test.o slackline.o $(LIBS) | |
+ | |
slackline.o: slackline.c slackline.h | |
$(CC) -c $(CFLAGS) -o $@ slackline.c | |
diff --git a/lchat.c b/lchat.c | |
@@ -112,9 +112,9 @@ line_output(struct slackline *sl, char *file) | |
err(EXIT_FAILURE, "open: %s", file); | |
/* replace NUL-terminator with newline as line separator for file */ | |
- sl->buf[sl->len] = '\n'; | |
+ sl->buf[sl->blen] = '\n'; | |
- if (write(fd, sl->buf, sl->len + 1) == -1) | |
+ if (write(fd, sl->buf, sl->blen + 1) == -1) | |
err(EXIT_FAILURE, "write"); | |
if (close(fd) == -1) | |
@@ -137,7 +137,7 @@ main(int argc, char *argv[]) | |
struct termios term; | |
struct slackline *sl = sl_init(); | |
int fd = STDIN_FILENO; | |
- int c; | |
+ char c; | |
int ch; | |
bool empty_line = false; | |
bool bell_flag = true; | |
@@ -285,15 +285,28 @@ main(int argc, char *argv[]) | |
/* handle keyboard intput */ | |
if (pfd[0].revents & POLLIN) { | |
- c = getchar(); | |
- if (c == 13) { /* return */ | |
- if (sl->len == 0 && empty_line == false) | |
+ ssize_t ret = read(fd, &c, sizeof c); | |
+ | |
+ if (ret == -1) | |
+ err(EXIT_FAILURE, "read"); | |
+ | |
+ if (ret == 0) | |
+ return EXIT_SUCCESS; | |
+ | |
+ switch (c) { | |
+ case 4: /* eot */ | |
+ return EXIT_SUCCESS; | |
+ break; | |
+ case 13: /* return */ | |
+ if (sl->rlen == 0 && empty_line == false) | |
goto out; | |
line_output(sl, in_file); | |
sl_reset(sl); | |
+ break; | |
+ default: | |
+ if (sl_keystroke(sl, c) == -1) | |
+ errx(EXIT_FAILURE, "sl_keystroke"); | |
} | |
- if (sl_keystroke(sl, c) == -1) | |
- errx(EXIT_FAILURE, "sl_keystroke"); | |
} | |
/* handle tail command error and its broken pipe */ | |
@@ -324,19 +337,22 @@ main(int argc, char *argv[]) | |
fputs(sl->buf, stdout); | |
/* save amount of overhanging lines */ | |
- loverhang = (prompt_len + sl->len) / winsize.ws_col; | |
+ loverhang = (prompt_len + sl->rlen) / winsize.ws_col; | |
/* correct line wrap handling */ | |
- if ((prompt_len + sl->len) > 0 && | |
- (prompt_len + sl->len) % winsize.ws_col == 0) | |
+ if ((prompt_len + sl->rlen) > 0 && | |
+ (prompt_len + sl->rlen) % winsize.ws_col == 0) | |
fputs("\n", stdout); | |
- if (sl->cur < sl->len) { /* move the cursor */ | |
+ if (sl->rcur < sl->rlen) { /* move the cursor */ | |
putchar('\r'); | |
/* HACK: because \033[0C does the same as \033[1C */ | |
- if (sl->cur + prompt_len > 0) | |
- printf("\033[%zuC", sl->cur + prompt_len); | |
+ if (sl->rcur + prompt_len > 0) | |
+ printf("\033[%zuC", sl->rcur + prompt_len); | |
} | |
+ | |
+ if (fflush(stdout) == EOF) | |
+ err(EXIT_FAILURE, "fflush"); | |
} | |
return EXIT_SUCCESS; | |
} | |
diff --git a/slackline.c b/slackline.c | |
@@ -1,5 +1,5 @@ | |
/* | |
- * Copyright (c) 2015 Jan Klemkow <[email protected]> | |
+ * Copyright (c) 2015-2016 Jan Klemkow <[email protected]> | |
* | |
* Permission to use, copy, modify, and distribute this software for any | |
* purpose with or without fee is hereby granted, provided that the above | |
@@ -14,10 +14,12 @@ | |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
*/ | |
+#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
-#include <stdbool.h> | |
+ | |
+#include <utf.h> | |
#include "slackline.h" | |
@@ -35,6 +37,9 @@ sl_init(void) | |
return NULL; | |
} | |
+ memset(sl->ubuf, 0, sizeof(sl->ubuf)); | |
+ sl->ubuf_len = 0; | |
+ | |
sl_reset(sl); | |
return sl; | |
@@ -51,15 +56,58 @@ void | |
sl_reset(struct slackline *sl) | |
{ | |
sl->buf[0] = '\0'; | |
- sl->len = 0; | |
- sl->cur = 0; | |
+ sl->ptr = sl->buf; | |
+ sl->last = sl->buf; | |
+ | |
+ sl->bcur = 0; | |
+ sl->blen = 0; | |
+ sl->rcur = 0; | |
+ sl->rlen = 0; | |
+ | |
sl->esc = ESC_NONE; | |
+ sl->ubuf_len = 0; | |
+} | |
+ | |
+static size_t | |
+sl_postobyte(struct slackline *sl, size_t pos) | |
+{ | |
+ char *ptr = &sl->buf[0]; | |
+ size_t byte = 0; | |
+ | |
+ for (;pos > 0; pos--) { | |
+ for (size_t i = 0;; i++) { | |
+ if (fullrune(ptr, i) == 1) { | |
+ ptr += i; | |
+ byte += i; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return byte; | |
+} | |
+ | |
+static char * | |
+sl_postoptr(struct slackline *sl, size_t pos) | |
+{ | |
+ char *ptr = &sl->buf[0]; | |
+ | |
+ for (;pos > 0; pos--) { | |
+ for (size_t i = 0;; i++) { | |
+ if (fullrune(ptr, i) == 1) { | |
+ ptr += i; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return ptr; | |
} | |
int | |
sl_keystroke(struct slackline *sl, int key) | |
{ | |
- if (sl == NULL || sl->len < sl->cur) | |
+ if (sl == NULL || sl->rlen < sl->rcur) | |
return -1; | |
/* handle escape sequences */ | |
@@ -75,55 +123,88 @@ sl_keystroke(struct slackline *sl, int key) | |
case 'B': /* down */ | |
break; | |
case 'C': /* right */ | |
- if (sl->cur < sl->len) | |
- sl->cur++; | |
+ if (sl->rcur < sl->rlen) | |
+ sl->rcur++; | |
+ sl->bcur = sl_postobyte(sl, sl->rcur); | |
break; | |
case 'D': /* left */ | |
- if (sl->cur > 0) | |
- sl->cur--; | |
+ if (sl->rcur > 0) | |
+ sl->rcur--; | |
+ sl->bcur = sl_postobyte(sl, sl->rcur); | |
break; | |
case 'H': /* Home */ | |
- sl->cur = 0; | |
+ sl->bcur = sl->rcur = 0; | |
break; | |
case 'F': /* End */ | |
- sl->cur = sl->len; | |
+ sl->rcur = sl->rlen; | |
+ sl->bcur = sl_postobyte(sl, sl->rcur); | |
break; | |
} | |
sl->esc = ESC_NONE; | |
return 0; | |
} | |
- /* add character to buffer */ | |
- if (key >= 32 && key < 127) { | |
- if (sl->cur < sl->len) { | |
- memmove(sl->buf + sl->cur + 1, sl->buf + sl->cur, | |
- sl->len - sl->cur); | |
- sl->buf[sl->cur++] = key; | |
- } else { | |
- sl->buf[sl->cur++] = key; | |
- sl->buf[sl->cur] = '\0'; | |
- } | |
- sl->len++; | |
- return 0; | |
- } | |
- | |
/* handle ctl keys */ | |
switch (key) { | |
case 27: /* Escape */ | |
sl->esc = ESC; | |
- break; | |
+ return 0; | |
case 127: /* backspace */ | |
case 8: /* backspace */ | |
- if (sl->cur == 0) | |
- break; | |
- if (sl->cur < sl->len) | |
- memmove(sl->buf + sl->cur - 1, sl->buf + sl->cur, | |
- sl->len - sl->cur); | |
- sl->cur--; | |
- sl->len--; | |
- sl->buf[sl->len] = '\0'; | |
- break; | |
+ if (sl->rcur == 0) | |
+ return 0; | |
+ | |
+ char *ncur = sl_postoptr(sl, sl->rcur - 1); | |
+ | |
+ if (sl->rcur < sl->rlen) | |
+ memmove(ncur, sl->ptr, sl->last - sl->ptr); | |
+ | |
+ sl->rcur--; | |
+ sl->rlen--; | |
+ sl->last -= sl->ptr - ncur; | |
+ sl->ptr = ncur; | |
+ sl->bcur = sl_postobyte(sl, sl->rcur); | |
+ sl->blen = sl_postobyte(sl, sl->rlen); | |
+ *sl->last = '\0'; | |
+ return 0; | |
} | |
+ /* byte-wise composing of UTF-8 runes */ | |
+ sl->ubuf[sl->ubuf_len++] = key; | |
+ if (fullrune(sl->ubuf, sl->ubuf_len) == 0) | |
+ return 0; | |
+ | |
+ if (sl->blen + sl->ubuf_len >= sl->bufsize) { | |
+ char *nbuf; | |
+ | |
+ if ((nbuf = realloc(sl->buf, sl->bufsize * 2)) == NULL) | |
+ return -1; | |
+ | |
+ sl->buf = nbuf; | |
+ sl->bufsize *= 2; | |
+ } | |
+ | |
+ /* add character to buffer */ | |
+ if (sl->rcur < sl->rlen) { /* insert into buffer */ | |
+ char *ncur = sl_postoptr(sl, sl->rcur + 1); | |
+ char *cur = sl_postoptr(sl, sl->rcur); | |
+ char *end = sl_postoptr(sl, sl->rlen); | |
+ | |
+ memmove(ncur, cur, end - cur); | |
+ } | |
+ | |
+ memcpy(sl->last, sl->ubuf, sl->ubuf_len); | |
+ | |
+ sl->ptr += sl->ubuf_len; | |
+ sl->last += sl->ubuf_len; | |
+ sl->bcur += sl->ubuf_len; | |
+ sl->blen += sl->ubuf_len; | |
+ sl->ubuf_len = 0; | |
+ | |
+ sl->rcur++; | |
+ sl->rlen++; | |
+ | |
+ *sl->last = '\0'; | |
+ | |
return 0; | |
} | |
diff --git a/slackline.h b/slackline.h | |
@@ -6,11 +6,25 @@ | |
enum esc_seq {ESC_NONE, ESC, ESC_BRACKET}; | |
struct slackline { | |
+ /* buffer */ | |
char *buf; | |
+ char *ptr; /* ptr of cursor */ | |
+ char *last; /* ptr of last byte of string */ | |
size_t bufsize; | |
- size_t len; | |
- size_t cur; | |
+ | |
+ /* byte positions */ | |
+ size_t bcur; /* first byte of the rune of the cursor */ | |
+ size_t blen; /* amount of bytes of current string */ | |
+ | |
+ /* rune positions */ | |
+ size_t rcur; /* cursor */ | |
+ size_t rlen; /* amount of runes */ | |
+ | |
enum esc_seq esc; | |
+ | |
+ /* UTF-8 handling */ | |
+ char ubuf[6]; /* UTF-8 buffer */ | |
+ size_t ubuf_len; | |
}; | |
struct slackline *sl_init(void); |