From 1e06ec41dc0bc9e99674d11ad3c810b579450e12 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 25 Jun 2009 15:47:07 +0000 Subject: [PATCH] Add a UTF-8 aware string length function and make UTF-8 in status-left/status-right work properly. At the moment any top-bit-set characters are assumed to be UTF-8: a status-utf8 option to configure this will come shortly. --- screen-write.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++--- status.c | 21 ++++----- tmux.h | 11 +++-- 3 files changed, 130 insertions(+), 18 deletions(-) diff --git a/screen-write.c b/screen-write.c index 92dc607d..08156d54 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,4 +1,4 @@ -/* $Id: screen-write.c,v 1.45 2009-05-04 17:58:27 nicm Exp $ */ +/* $OpenBSD: screen-write.c,v 1.2 2009/06/03 16:05:46 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -52,20 +52,126 @@ screen_write_putc( screen_write_cell(ctx, gc, NULL); } +/* Calculate string length. */ +size_t printflike1 +screen_write_strlen(const char *fmt, ...) +{ + va_list ap; + char *msg; + u_char *ptr, utf8buf[4]; + size_t left, size = 0; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + ptr = msg; + while (*ptr != '\0') { + if (*ptr > 0x7f) { /* Assume this is UTF-8. */ + memset(utf8buf, 0xff, sizeof utf8buf); + + left = strlen(ptr); + if (*ptr >= 0xc2 && *ptr <= 0xdf && left >= 2) { + memcpy(utf8buf, ptr, 2); + ptr += 2; + } else if (*ptr >= 0xe0 && *ptr <= 0xef && left >= 3) { + memcpy(utf8buf, ptr, 3); + ptr += 3; + } else if (*ptr >= 0xf0 && *ptr <= 0xf4 && left >= 4) { + memcpy(utf8buf, ptr, 4); + ptr += 4; + } else { + *utf8buf = *ptr; + ptr++; + } + size += utf8_width(utf8buf); + } else { + size++; + ptr++; + } + } + + return (size); +} + /* Write string. */ void printflike3 screen_write_puts( struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) { va_list ap; - char *msg, *ptr; va_start(ap, fmt); - xvasprintf(&msg, fmt, ap); + screen_write_vnputs(ctx, -1, gc, fmt, ap); va_end(ap); +} - for (ptr = msg; *ptr != '\0'; ptr++) - screen_write_putc(ctx, gc, (u_char) *ptr); +/* Write string with length limit (-1 for unlimited). */ +void printflike4 +screen_write_nputs(struct screen_write_ctx *ctx, + ssize_t maxlen, struct grid_cell *gc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + screen_write_vnputs(ctx, maxlen, gc, fmt, ap); + va_end(ap); +} + +void +screen_write_vnputs(struct screen_write_ctx *ctx, + ssize_t maxlen, struct grid_cell *gc, const char *fmt, va_list ap) +{ + char *msg; + u_char *ptr, utf8buf[4]; + size_t left, size = 0; + int width; + + xvasprintf(&msg, fmt, ap); + + ptr = msg; + while (*ptr != '\0') { + if (*ptr > 0x7f) { /* Assume this is UTF-8. */ + memset(utf8buf, 0xff, sizeof utf8buf); + + left = strlen(ptr); + if (*ptr >= 0xc2 && *ptr <= 0xdf && left >= 2) { + memcpy(utf8buf, ptr, 2); + ptr += 2; + } else if (*ptr >= 0xe0 && *ptr <= 0xef && left >= 3) { + memcpy(utf8buf, ptr, 3); + ptr += 3; + } else if (*ptr >= 0xf0 && *ptr <= 0xf4 && left >= 4) { + memcpy(utf8buf, ptr, 4); + ptr += 4; + } else { + *utf8buf = *ptr; + ptr++; + } + + width = utf8_width(utf8buf); + if (maxlen > 0 && size + width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; + } + size += width; + + gc->flags |= GRID_FLAG_UTF8; + screen_write_cell(ctx, gc, utf8buf); + gc->flags &= ~GRID_FLAG_UTF8; + + } else { + if (maxlen > 0 && size > (size_t) maxlen) + break; + + size++; + screen_write_putc(ctx, gc, *ptr); + ptr++; + } + } xfree(msg); } diff --git a/status.c b/status.c index bd7b4d2a..ab9a11bb 100644 --- a/status.c +++ b/status.c @@ -1,4 +1,4 @@ -/* $Id: status.c,v 1.83 2009-05-29 05:40:56 nicm Exp $ */ +/* $OpenBSD: status.c,v 1.2 2009/06/03 16:05:46 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -47,8 +47,8 @@ status_redraw(struct client *c) struct window_pane *wp; struct screen *sc = NULL, old_status; char *left, *right, *text, *ptr; - size_t llen, rlen, offset, xx, yy, sy; - size_t size, start, width; + size_t llen, llen2, rlen, rlen2, offset; + size_t xx, yy, sy, size, start, width; struct grid_cell stdgc, gc; int larrow, rarrow; @@ -78,15 +78,16 @@ status_redraw(struct client *c) left = status_replace(s, options_get_string( &s->options, "status-left"), c->status_timer.tv_sec); llen = options_get_number(&s->options, "status-left-length"); - if (strlen(left) < llen) - llen = strlen(left); - left[llen] = '\0'; + llen2 = screen_write_strlen("%s", left); + if (llen2 < llen) + llen = llen2; right = status_replace(s, options_get_string( &s->options, "status-right"), c->status_timer.tv_sec); rlen = options_get_number(&s->options, "status-right-length"); - if (strlen(right) < rlen) - rlen = strlen(right); + rlen2 = screen_write_strlen("%s", right); + if (rlen2 < rlen) + rlen = rlen2; right[rlen] = '\0'; /* @@ -163,7 +164,7 @@ draw: screen_write_start(&ctx, NULL, &c->status); if (llen != 0) { screen_write_cursormove(&ctx, 0, yy); - screen_write_puts(&ctx, &stdgc, "%s ", left); + screen_write_nputs(&ctx, llen + 1, &stdgc, "%s ", left); if (larrow) screen_write_putc(&ctx, &stdgc, ' '); } else { @@ -220,7 +221,7 @@ draw: /* Draw the last item. */ if (rlen != 0) { screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, yy); - screen_write_puts(&ctx, &stdgc, " %s", right); + screen_write_nputs(&ctx, rlen + 1, &stdgc, " %s", right); } /* Draw the arrows. */ diff --git a/tmux.h b/tmux.h index cea3e1a4..21afb619 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.328 2009-06-25 15:46:09 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.3 2009/06/03 16:05:46 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1468,8 +1468,13 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); void screen_write_start( struct screen_write_ctx *, struct window_pane *, struct screen *); void screen_write_stop(struct screen_write_ctx *); -void printflike3 screen_write_puts( - struct screen_write_ctx *, struct grid_cell *, const char *, ...); +size_t printflike1 screen_write_strlen(const char *, ...); +void printflike3 screen_write_puts(struct screen_write_ctx *, + struct grid_cell *, const char *, ...); +void printflike4 screen_write_nputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +void screen_write_vnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, va_list); void screen_write_putc( struct screen_write_ctx *, struct grid_cell *, u_char); void screen_write_copy(struct screen_write_ctx *,