commit 0f8af7612e393d40705e953306bc0555f026c2f7
parent 569292df0c821cf2c977d224345ec009e344cb1c
Author: Jan Klemkow <j.klemkow@wemelug.de>
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 <j.klemkow@wemelug.de>
+ * Copyright (c) 2015-2016 Jan Klemkow <j.klemkow@wemelug.de>
*
* 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);