add support for characters that are bigger than 1 column - sob - simple output … | |
git clone git://git.codemadness.org/sob | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 953439a2efe7bf8be200be0d384377d1d96f4d48 | |
parent 4f2bc0ed04d5afc5dfd11651fe84747eb3ec55bb | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Fri, 10 Oct 2014 18:47:55 +0000 | |
add support for characters that are bigger than 1 column | |
Diffstat: | |
M README | 4 +++- | |
M sob.c | 78 +++++++++++++++++++++++++----… | |
2 files changed, 67 insertions(+), 15 deletions(-) | |
--- | |
diff --git a/README b/README | |
@@ -10,6 +10,8 @@ Dependencies | |
Features | |
-------- | |
- Small (in size and memory), not much dependencies. | |
+- UTF-8 input and output support. | |
+ - Support for characters that are bigger than 1 column. | |
- Custom prompt (including color support). | |
- Easy to write custom completion scripts or special actions. | |
- Custom action on SIGWINCH (window resize). | |
@@ -23,7 +25,7 @@ Features | |
Known issues | |
------------ | |
-line yank doesn't work with xclip, but does work with xsel. | |
+- Line yank doesn't work with xclip, but does work with xsel. | |
Author | |
diff --git a/sob.c b/sob.c | |
@@ -12,6 +12,9 @@ | |
#include <unistd.h> | |
#include <termios.h> | |
+#define __XOPEN_SOURCE | |
+#include <wchar.h> | |
+ | |
#include "arg.h" | |
char *argv0; | |
@@ -27,6 +30,8 @@ struct line { | |
size_t utflen; /* length in characters */ | |
size_t bytepos; /* index position (in bytes) */ | |
size_t utfpos; /* pos in characters */ | |
+ size_t colpos; /* cursor position (in columns) */ | |
+ size_t collen; /* total length (in columns) */ | |
}; | |
static void line_clear(void); | |
@@ -69,10 +74,11 @@ static void setup(void); | |
static void sighandler(int); | |
static void usage(void); | |
+static size_t colw(const char *, size_t); | |
static int nonspace(int c); | |
static size_t utf8len(const char *); | |
-static size_t utfprevn(const char *, size_t , size_t); | |
-static size_t utfnextn(const char *, size_t , size_t); | |
+static size_t utfprevn(const char *, size_t, size_t); | |
+static size_t utfnextn(const char *, size_t, size_t); | |
static void utfuntilchar(size_t *, size_t *, int (*)(int), int); | |
static struct termios ttystate, ttysave; | |
@@ -92,6 +98,25 @@ nonspace(int c) | |
} | |
static size_t | |
+colw(const char *s, size_t max) | |
+{ | |
+ size_t len = 0, i; | |
+ wchar_t w = 0; | |
+ int r; | |
+ | |
+ for(i = 0; *s; s++, i++) { | |
+ if((*s & 0xc0) != 0x80) { | |
+ if((r = mbtowc(&w, s, i + 4 > max ? max - i : 4)) == -… | |
+ break; | |
+ if((r = wcwidth(w)) == -1) | |
+ break; | |
+ len += r; | |
+ } | |
+ } | |
+ return len; | |
+} | |
+ | |
+static size_t | |
utf8len(const char *s) | |
{ | |
size_t i; | |
@@ -133,7 +158,7 @@ utfnextn(const char *s, size_t p, size_t n) | |
return 0; | |
} | |
-/* b is byte start pos, u is utf pos, f is filter function, | |
+/* b is byte start pos, u is utf pos, c is column pos, f is filter function, | |
* dir is -1 or +1 for prev or next */ | |
static void | |
utfuntilchar(size_t *b, size_t *u, int (*f)(int), int dir) | |
@@ -165,12 +190,13 @@ utfuntilchar(size_t *b, size_t *u, int (*f)(int), int dir) | |
static void | |
line_inserttext(const char *s) | |
{ | |
- size_t siz, len; | |
+ size_t siz, ulen, clen; | |
siz = strlen(s); | |
if(line.bytepos + siz + 1 > sizeof(line.line)) | |
return; | |
- len = utf8len(s); | |
+ clen = colw(s, siz); | |
+ ulen = utf8len(s); | |
/* append */ | |
if(line.bytepos == line.bytesiz) { | |
memmove(&line.line[line.bytepos], s, siz); | |
@@ -184,7 +210,9 @@ line_inserttext(const char *s) | |
line.bytesiz += siz; | |
line.line[line.bytesiz + 1] = '\0'; | |
line.utflen = utf8len(line.line); | |
- line.utfpos += len; | |
+ line.utfpos += ulen; | |
+ line.colpos += clen; | |
+ line.collen = colw(line.line, line.bytesiz); | |
line_draw(); | |
} | |
@@ -202,6 +230,8 @@ line_set(const char *s) | |
line.bytepos = line.bytesiz; | |
line.utflen = utf8len(line.line); | |
line.utfpos = line.utflen; | |
+ line.collen = colw(line.line, line.bytesiz); | |
+ line.colpos = line.collen; | |
} | |
/* like mksh, toggle counting of escape codes in prompt with "\x01" */ | |
@@ -237,7 +267,7 @@ line_draw(void) | |
fprintf(outfp, "\x1b[2J\x1b[H"); /* clear */ | |
line_prompt(); | |
fwrite(line.line, 1, line.bytesiz, outfp); | |
- line_cursor_move(line.utfpos); | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -268,14 +298,16 @@ static void | |
line_cursor_wordprev(void) | |
{ | |
line_getwordposprev(line.bytepos, line.utfpos, &line.bytepos, &line.ut… | |
- line_cursor_move(line.utfpos); | |
+ line.colpos = colw(line.line, line.bytepos); | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
line_cursor_wordnext(void) | |
{ | |
line_getwordposnext(line.bytepos, line.utfpos, &line.bytepos, &line.ut… | |
- line_cursor_move(line.utfpos); | |
+ line.colpos = colw(line.line, line.bytepos); | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -283,7 +315,8 @@ line_cursor_begin(void) | |
{ | |
line.bytepos = 0; | |
line.utfpos = 0; | |
- line_cursor_move(line.utfpos); | |
+ line.colpos = 0; | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -298,7 +331,8 @@ line_cursor_prev(void) | |
line.bytepos -= n; | |
line.utfpos--; | |
- line_cursor_move(line.utfpos); | |
+ line.colpos -= colw(&line.line[line.bytepos], n); | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -311,9 +345,10 @@ line_cursor_next(void) | |
if((n = utfnextn(line.line, line.bytepos, 1)) == 0) | |
return; | |
+ line.colpos += colw(&line.line[line.bytepos], n); | |
line.bytepos += n; | |
line.utfpos++; | |
- line_cursor_move(line.utfpos); | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -321,7 +356,8 @@ line_cursor_end(void) | |
{ | |
line.bytepos = line.bytesiz; | |
line.utfpos = line.utflen; | |
- line_cursor_move(line.utfpos); | |
+ line.colpos = line.collen; | |
+ line_cursor_move(line.colpos); | |
} | |
static void | |
@@ -342,6 +378,9 @@ line_delcharnext(void) | |
if((siz = utfnextn(line.line, line.bytepos, 1)) == 0) | |
return; | |
+ | |
+ line.collen -= colw(&line.line[line.bytepos], siz); | |
+ | |
memmove(&line.line[line.bytepos], &line.line[line.bytepos + siz], | |
line.bytesiz - line.bytepos - siz); | |
@@ -354,20 +393,25 @@ line_delcharnext(void) | |
static void | |
line_delcharprev(void) | |
{ | |
- size_t siz; | |
+ size_t siz, col; | |
if(line.utfpos <= 0 || line.utflen <= 0) | |
return; | |
if((siz = utfprevn(line.line, line.bytepos, 1)) == 0) | |
return; | |
+ col = colw(&line.line[line.bytepos - siz], siz); | |
+ | |
memmove(&line.line[line.bytepos - siz], &line.line[line.bytepos], | |
line.bytesiz - line.bytepos); | |
+ | |
line.bytepos -= siz; | |
line.bytesiz -= siz; | |
line.line[line.bytesiz] = '\0'; | |
line.utflen--; | |
line.utfpos--; | |
+ line.colpos -= col; | |
+ line.collen -= col; | |
line_draw(); | |
} | |
@@ -379,6 +423,8 @@ line_deltoend(void) | |
line.bytesiz = line.bytepos; | |
line.utflen = utf8len(line.line); | |
line.utfpos = line.utflen; | |
+ line.collen = colw(line.line, line.bytesiz); | |
+ line.colpos = line.collen; | |
line_draw(); | |
} | |
@@ -400,6 +446,8 @@ line_delwordcursor(void) | |
line.line[line.bytesiz] = '\0'; | |
line.utfpos -= len; | |
line.utflen -= len; | |
+ line.collen = colw(line.line, line.bytesiz); | |
+ line.colpos = colw(line.line, bs); | |
line_draw(); | |
} | |
@@ -415,6 +463,7 @@ line_delwordprev(void) | |
line_getwordposprev(line.bytepos, line.utfpos, &bs, &us); | |
siz = line.bytepos - bs; | |
+ line.colpos -= colw(&line.line[bs], siz); | |
memmove(&line.line[bs], &line.line[line.bytepos], | |
line.bytesiz - line.bytepos); | |
@@ -425,6 +474,7 @@ line_delwordprev(void) | |
line.line[line.bytesiz] = '\0'; | |
line.utfpos -= len; | |
line.utflen -= len; | |
+ line.collen = colw(line.line, line.bytesiz); | |
line_draw(); | |
} |