sob

Simple output bar
git clone https://git.sinitax.com/codemadness/sob
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

commit 517a1cac03d09213419cabd41dc481c08ad16c9d
parent d52ae758f565c6a59c5ddb96995b2e364c507c4f
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Fri,  3 Oct 2014 23:49:15 +0000

support utf8 input and output

Diffstat:
Msob.c | 374++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 255 insertions(+), 119 deletions(-)

diff --git a/sob.c b/sob.c @@ -22,9 +22,11 @@ char *argv0; #define MIN(A, B) ((A) < (B) ? (A) : (B)) struct line { - char line[BUFSIZ]; - size_t len; - size_t pos; + char line[BUFSIZ]; /* static line buffer */ + size_t bytesiz; /* length in bytes */ + size_t utflen; /* length in characters */ + size_t bytepos; /* index position (in bytes) */ + size_t utfpos; /* pos in characters */ }; static void line_clear(void); @@ -36,14 +38,17 @@ static void line_cursor_next(void); static void line_cursor_prev(void); static void line_cursor_wordprev(void); static void line_cursor_wordnext(void); -static void line_delcharback(void); +static void line_delcharprev(void); static void line_delcharnext(void); static void line_deltoend(void); -static void line_delwordback(void); +static void line_delwordprev(void); static void line_delwordcursor(void); static void line_draw(void); static void line_exit(void); -static void line_getwordpos(size_t *, size_t *); +static void line_getwordpos(size_t, size_t, size_t *, size_t *, size_t *, + size_t *); +static void line_getwordposprev(size_t, size_t, size_t *, size_t *); +static void line_getwordposnext(size_t, size_t, size_t *, size_t *); static void line_inserttext(const char *); static void line_newline(void); static void line_out(void); @@ -51,7 +56,7 @@ static void line_prompt(void); static int line_promptlen(void); static int line_pipeto(char **); static void line_set(const char *); -static void line_wordpipeto(char **); +static int line_wordpipeto(char **); static int pipe_readline(int, int, char *, char *, size_t); static int pipe_cmd(char *[], char *, char *, size_t); @@ -64,6 +69,12 @@ static void setup(void); static void sighandler(int); static void usage(void); +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 void utfuntilchar(size_t *, size_t *, int (*)(int), int); + static struct termios ttystate, ttysave; static struct line line; @@ -74,35 +85,123 @@ static FILE * lineoutfp = NULL; #include "config.h" +static int +nonspace(int c) +{ + return !isspace(c); +} + +static size_t +utf8len(const char *s) +{ + size_t i; + + for(i = 0; *s; s++) { + if((*s & 0xc0) != 0x80) + i++; + } + return i; +} + +/* returns amount of bytes needed to go to previous utf char + * p is index in bytes. */ +static size_t +utfprevn(const char *s, size_t p, size_t n) +{ + size_t i; + + for(i = 0; p > 0; p--) { + i++; + if((s[p - 1] & 0xc0) != 0x80 && !--n) + return i; + } + return 0; +} + +/* returns amount of bytes needed to go to next utf char + * p is index in bytes. */ +static size_t +utfnextn(const char *s, size_t p, size_t n) +{ + size_t i; + + for(i = 0; s[p]; p++) { + i++; + if((s[p + 1] & 0xc0) != 0x80 && !--n) + return i; + } + return 0; +} + +/* b is byte start pos, u is utf 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) +{ + size_t n; + + if(dir > 0) { + while(*u < line.utflen && *b < line.bytesiz) { + if(f(line.line[*b])) + break; + if((n = utfnextn(line.line, *b, 1)) == 0) + break; + *b += n; + (*u)++; + } + + } else { + while(*u > 0) { + if(f(line.line[*b - 1])) + break; + if((n = utfprevn(line.line, *b, 1)) == 0) + break; + *b -= n; + (*u)--; + } + } +} + static void line_inserttext(const char *s) { - size_t len; + size_t siz, len; - len = strlen(s); - if(line.pos + len + 1 > sizeof(line.line)) + siz = strlen(s); + if(line.bytepos + siz + 1 > sizeof(line.line)) return; + len = utf8len(s); /* append */ - if(line.pos == line.len) { - memmove(&line.line[line.pos], s, len); + if(line.bytepos == line.bytesiz) { + memmove(&line.line[line.bytepos], s, siz); } else { /* insert */ - memmove(&line.line[line.pos + len], &line.line[line.pos], - line.len - line.pos); - memcpy(&line.line[line.pos], s, len); + memmove(&line.line[line.bytepos + siz], &line.line[line.bytepos], + line.bytesiz - line.bytepos); + memcpy(&line.line[line.bytepos], s, siz); } - line.len += len; - line.pos += len; - line.line[line.len + 1] = '\0'; + line.bytepos += siz; + line.bytesiz += siz; + line.line[line.bytesiz + 1] = '\0'; + line.utflen = utf8len(line.line); + line.utfpos += len; line_draw(); } static void line_set(const char *s) { + char *p; + strlcpy(line.line, s, sizeof(line.line)); - line.len = strlen(line.line); - line.pos = line.len; + /* remove linefeed and return from string */ + if((p = strpbrk(line.line, "\r\n"))) + *p = '\0'; + + line.bytesiz = strlen(line.line); + line.bytepos = line.bytesiz; + line.utflen = utf8len(line.line); + line.utfpos = line.utflen; } /* like mksh, toggle counting of escape codes in prompt with "\x01" */ @@ -115,7 +214,7 @@ line_promptlen(void) for(i = 0; prompt[i]; i++) { if(prompt[i] == 1) t = !t; - else if(!t) + else if(!t && (prompt[i] & 0xc0) != 0x80) n++; } return n; @@ -135,16 +234,10 @@ line_prompt(void) static void line_draw(void) { - size_t n; - - /* clear */ - fprintf(outfp, "\x1b[2J\x1b[H"); - + fprintf(outfp, "\x1b[2J\x1b[H"); /* clear */ line_prompt(); - for(n = 0; line.line[n] && n < line.len; n++) - fputc(line.line[n], outfp); - - line_cursor_move(line.pos); + fwrite(line.line, 1, line.bytesiz, outfp); + line_cursor_move(line.utfpos); } static void @@ -169,143 +262,158 @@ line_cursor_move(size_t newpos) } fprintf(outfp, "\x1b[%lu;%luH", y + 1, x + 1); fflush(outfp); - line.pos = newpos; } static void line_cursor_wordprev(void) { - size_t s, e; - - line_getwordpos(&s, &e); - if(s == line.pos) { - while(s > 0 && isspace(line.line[s - 1])) - s--; - } - line_cursor_move(s); + line_getwordposprev(line.bytepos, line.utfpos, &line.bytepos, &line.utfpos); + line_cursor_move(line.utfpos); } static void line_cursor_wordnext(void) { - size_t s, e; - - line_getwordpos(&s, &e); - if(e == line.pos) { - while(e < line.len && line.line[e] && isspace(line.line[e])) - e++; - } - line_cursor_move(e); + line_getwordposnext(line.bytepos, line.utfpos, &line.bytepos, &line.utfpos); + line_cursor_move(line.utfpos); } static void line_cursor_begin(void) { - line_cursor_move(0); + line.utfpos = 0; + line.bytepos = 0; + line_cursor_move(line.utfpos); } static void line_cursor_prev(void) { - if(line.pos > 0) - line_cursor_move(line.pos - 1); + if(line.utfpos <= 0) + return; + + line.bytepos -= utfprevn(line.line, line.bytepos, 1); + line.utfpos--; + line_cursor_move(line.utfpos); } static void line_cursor_next(void) { - if(line.pos < line.len) - line_cursor_move(line.pos + 1); + if(line.utfpos >= line.utflen) + return; + + line.bytepos += utfnextn(line.line, line.bytepos, 1); + line.utfpos++; + line_cursor_move(line.utfpos); } static void line_cursor_end(void) { - line_cursor_move(line.len); + line.bytepos = line.bytesiz; + line.utfpos = line.utflen; + line_cursor_move(line.utfpos); } static void line_clear(void) { line_cursor_begin(); - line.line[0] = '\0'; - line.len = 0; + line_set(""); line_draw(); } static void line_delcharnext(void) { - if(line.pos == line.len || line.len <= 0) + size_t siz; + + if(line.utfpos == line.utflen || line.utflen <= 0) return; - memmove(&line.line[line.pos], &line.line[line.pos + 1], - line.len - line.pos - 1); - line.len--; - line.line[line.len] = '\0'; + siz = utfnextn(line.line, line.bytepos, 1); + memmove(&line.line[line.bytepos], &line.line[line.bytepos + siz], + line.bytesiz - line.bytepos - siz); + + line.bytesiz -= siz; + line.line[line.bytesiz] = '\0'; line_draw(); } static void -line_delcharback(void) +line_delcharprev(void) { - if(line.pos <= 0 || line.len <= 0) + size_t siz; + + if(line.utfpos <= 0 || line.utflen <= 0) return; - memmove(&line.line[line.pos - 1], &line.line[line.pos], - line.len - line.pos); - line.len--; - line.line[line.len] = '\0'; - line_cursor_prev(); + siz = utfprevn(line.line, line.bytepos, 1); + + memmove(&line.line[line.bytepos - siz], &line.line[line.bytepos], + line.bytesiz - line.bytepos); + line.utflen--; + line.utfpos--; + line.bytepos -= siz; + line.bytesiz -= siz; + line.line[line.bytesiz] = '\0'; line_draw(); } static void line_deltoend(void) { - line.line[line.pos] = '\0'; - line.len = line.pos; - line_cursor_end(); + line.line[line.bytepos] = '\0'; + line.bytesiz = line.bytepos; + line.utflen = utf8len(line.line); + line.utfpos = line.utflen; line_draw(); } static void line_delwordcursor(void) { - size_t len, s, e; + size_t len, siz, bs, be, us, ue; + + line_getwordpos(line.bytepos, line.utfpos, &bs, &be, &us, &ue); + + siz = be - bs; + len = ue - us; - line_getwordpos(&s, &e); + memmove(&line.line[bs], &line.line[be], line.bytesiz - be); + + line.bytesiz -= siz; + line.bytepos -= siz; + line.utfpos -= len; + line.utflen -= len; + line.line[line.bytesiz] = '\0'; - memmove(&line.line[s], &line.line[e], line.len - e); - len = e - s; - line.len -= len; - line.pos = s; - line.line[line.len] = '\0'; line_draw(); } static void -line_delwordback(void) +line_delwordprev(void) { - size_t i, len; + size_t bs, us, siz, len; - if(line.pos <= 0 || line.len <= 0) + if(line.utfpos <= 0 || line.utflen <= 0) return; - i = line.pos; - while(i > 0 && isspace(line.line[i - 1])) - i--; - while(i > 0 && !isspace(line.line[i - 1])) - i--; - - len = line.len - line.pos; - if(len > 0) - memmove(&line.line[i], &line.line[line.pos], - line.len - line.pos); - len = line.pos - i; - line.pos = i; - line.len -= len; - line.line[line.len] = '\0'; + line_getwordposprev(line.bytepos, line.utfpos, &bs, &us); + + siz = line.bytepos - bs; + memmove(&line.line[bs], &line.line[line.bytepos], + line.bytesiz - line.bytepos); + + len = line.utfpos - us; + + line.bytesiz -= siz; + line.bytepos -= siz; + line.utfpos -= len; + line.utflen -= len; + line.line[line.bytesiz] = '\0'; + line_draw(); } @@ -319,38 +427,65 @@ line_newline(void) static void line_exit(void) { - line_out(); + fprintf(outfp, "\n"); + fflush(outfp); isrunning = 0; } static void -line_getwordpos(size_t *start, size_t *end) +line_getwordpos(size_t b, size_t u, size_t *bs, size_t *be, + size_t *us, size_t *ue) { - size_t i; + size_t tb = b, tu = u; - i = line.pos; - while(i > 0 && !isspace(line.line[i - 1])) - i--; - if(start) - *start = i; - i = line.pos; - while(line.line[i] && i < line.len && !isspace(line.line[i])) - i++; - if(end) - *end = i; + utfuntilchar(&b, &u, isspace, -1); + if(bs) + *bs = b; + if(us) + *us = u; + + /* seek from original specified position */ + utfuntilchar(&tb, &tu, isspace, +1); + if(be) + *be = tb; + if(ue) + *ue = tu; +} + +static void +line_getwordposprev(size_t sb, size_t su, size_t *b, size_t *u) +{ + utfuntilchar(&sb, &su, nonspace, -1); + utfuntilchar(&sb, &su, isspace, -1); + if(b) + *b = sb; + if(u) + *u = su; +} + +static void +line_getwordposnext(size_t sb, size_t su, size_t *b, size_t *u) +{ + utfuntilchar(&sb, &su, nonspace, +1); + utfuntilchar(&sb, &su, isspace, +1); + if(b) + *b = sb; + if(u) + *u = su; } static void line_copywordcursor(char *buf, size_t bufsiz) { - size_t s, e, len; + size_t bs, be, len; + + line_getwordpos(line.bytepos, line.utfpos, &bs, &be, NULL, NULL); + len = be - bs; - line_getwordpos(&s, &e); - len = e - s; /* truncate */ if(len + 1 > bufsiz) len = bufsiz - 1; - memcpy(buf, &line.line[s], len); + memcpy(buf, &line.line[bs], len); buf[len + 1] = '\0'; } @@ -375,7 +510,7 @@ pipe_readline(int fd_in, int fd_out, char *writestr, char *outbuf, } memset(&tv, 0, sizeof(tv)); tv.tv_sec = 0; - tv.tv_usec = 200; + tv.tv_usec = 50000; /* 50 ms */ if((r = select(maxfd + 1, haswritten ? &fdr : NULL, haswritten ? NULL : &fdw, NULL, &tv)) == -1) @@ -468,21 +603,19 @@ static int line_pipeto(char **cmd) { char buf[BUFSIZ]; - size_t len; if(pipe_cmd(cmd, line.line, buf, sizeof(buf)) == -1) return -1; if(buf[0] == '\0') return -1; - len = strlcpy(line.line, buf, sizeof(line.line)); - line.len = len; + line_set(buf); line_cursor_end(); line_draw(); return 0; } /* pipe word under cursor and replace it */ -static void +static int line_wordpipeto(char **cmd) { char wordbuf[BUFSIZ], outbuf[BUFSIZ]; @@ -493,13 +626,15 @@ line_wordpipeto(char **cmd) if(pipe_cmd((char**)cmd, wordbuf, outbuf, sizeof(outbuf)) == -1) - return; + return -1; if(outbuf[0] == '\0') - return; + return -1; line_delwordcursor(); line_inserttext(outbuf); line_draw(); + + return 0; } static void @@ -619,7 +754,7 @@ run(void) memset(&tv, 0, sizeof(tv)); tv.tv_sec = 0; - tv.tv_usec = 50000; /* 50 ms */ + tv.tv_usec = 32000; /* 32 ms */ errno = 0; if((r = select(STDIN_FILENO + 1, &fdr, NULL, NULL, &tv)) == -1) { @@ -667,6 +802,7 @@ main(int argc, char **argv) lineoutfp = stdout; outfp = stderr; + setlocale(LC_ALL, ""); setup(); run();