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 <j.klemkow@wemelug.de>
Date:   Sat,  9 Jul 2016 20:30:21 +0200

add uft-8 support

Diffstat:
MMakefile | 26+++++++++++++++++++++-----
Mlchat.c | 44++++++++++++++++++++++++++++++--------------
Mslackline.c | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mslackline.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);