Support UTF-8 entry into the command prompt.

This commit is contained in:
nicm
2016-10-11 07:11:40 +00:00
parent a81685bfac
commit 8b804fb589
3 changed files with 230 additions and 117 deletions

316
status.c
View File

@ -662,18 +662,21 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
struct format_tree *ft; struct format_tree *ft;
int keys; int keys;
time_t t; time_t t;
char *tmp;
ft = format_create(NULL, 0); ft = format_create(NULL, 0);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, c, NULL, NULL, NULL);
t = time(NULL); t = time(NULL);
tmp = format_expand_time(ft, input, t);
status_message_clear(c); status_message_clear(c);
status_prompt_clear(c); status_prompt_clear(c);
c->prompt_string = format_expand_time(ft, msg, t); c->prompt_string = format_expand_time(ft, msg, t);
c->prompt_buffer = format_expand_time(ft, input, t); c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_callbackfn = callbackfn; c->prompt_callbackfn = callbackfn;
c->prompt_freefn = freefn; c->prompt_freefn = freefn;
@ -692,6 +695,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
free(tmp);
format_free(ft); format_free(ft);
} }
@ -723,22 +727,26 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
{ {
struct format_tree *ft; struct format_tree *ft;
time_t t; time_t t;
char *tmp;
ft = format_create(NULL, 0); ft = format_create(NULL, 0);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, c, NULL, NULL, NULL);
t = time(NULL); t = time(NULL);
tmp = format_expand_time(ft, input, t);
free(c->prompt_string); free(c->prompt_string);
c->prompt_string = format_expand_time(ft, msg, t); c->prompt_string = format_expand_time(ft, msg, t);
free(c->prompt_buffer); free(c->prompt_buffer);
c->prompt_buffer = format_expand_time(ft, input, t); c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_hindex = 0; c->prompt_hindex = 0;
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
free(tmp);
format_free(ft); format_free(ft);
} }
@ -746,57 +754,80 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
int int
status_prompt_redraw(struct client *c) status_prompt_redraw(struct client *c)
{ {
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct session *s = c->session; struct session *s = c->session;
struct screen old_status; struct screen old_status;
size_t i, size, left, len, off; u_int i, offset, left, start, pcursor, pwidth, width;
struct grid_cell gc; struct grid_cell gc, cursorgc;
if (c->tty.sx == 0 || c->tty.sy == 0) if (c->tty.sx == 0 || c->tty.sy == 0)
return (0); return (0);
memcpy(&old_status, &c->status, sizeof old_status); memcpy(&old_status, &c->status, sizeof old_status);
screen_init(&c->status, c->tty.sx, 1, 0); screen_init(&c->status, c->tty.sx, 1, 0);
len = screen_write_strlen("%s", c->prompt_string);
if (len > c->tty.sx)
len = c->tty.sx;
off = 0;
/* Change colours for command mode. */
if (c->prompt_mdata.mode == 1) if (c->prompt_mdata.mode == 1)
style_apply(&gc, s->options, "message-command-style"); style_apply(&gc, s->options, "message-command-style");
else else
style_apply(&gc, s->options, "message-style"); style_apply(&gc, s->options, "message-style");
memcpy(&cursorgc, &gc, sizeof cursorgc);
cursorgc.attr ^= GRID_ATTR_REVERSE;
start = screen_write_strlen("%s", c->prompt_string);
if (start > c->tty.sx)
start = c->tty.sx;
screen_write_start(&ctx, NULL, &c->status); screen_write_start(&ctx, NULL, &c->status);
screen_write_cursormove(&ctx, 0, 0); screen_write_cursormove(&ctx, 0, 0);
screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string); screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
while (c->status.cx < screen_size_x(&c->status))
screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, start, 0);
left = c->tty.sx - len; left = c->tty.sx - start;
if (left != 0) { if (left == 0)
size = screen_write_strlen("%s", c->prompt_buffer); goto finished;
if (c->prompt_index >= left) {
off = c->prompt_index - left + 1; pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
if (c->prompt_index == size) pwidth = utf8_strwidth(c->prompt_buffer, -1);
left--; if (pcursor >= left) {
size = left; /*
* The cursor would be outside the screen so start drawing
* with it on the right.
*/
offset = (pcursor - left) + 1;
pwidth = left;
} else
offset = 0;
if (pwidth > left)
pwidth = left;
width = 0;
for (i = 0; c->prompt_buffer[i].size != 0; i++) {
if (width < offset) {
width += c->prompt_buffer[i].width;
continue;
} }
screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer + if (width >= offset + pwidth)
off); break;
width += c->prompt_buffer[i].width;
if (width > offset + pwidth)
break;
for (i = len + size; i < c->tty.sx; i++) if (i != c->prompt_index) {
screen_write_putc(&ctx, &gc, ' '); utf8_copy(&gc.data, &c->prompt_buffer[i]);
screen_write_cell(&ctx, &gc);
} else {
utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
screen_write_cell(&ctx, &cursorgc);
}
} }
if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i)
screen_write_putc(&ctx, &cursorgc, ' ');
finished:
screen_write_stop(&ctx); screen_write_stop(&ctx);
/* Apply fake cursor. */
off = len + c->prompt_index - off;
grid_view_get_cell(c->status.grid, off, 0, &gc);
gc.attr ^= GRID_ATTR_REVERSE;
grid_view_set_cell(c->status.grid, off, 0, &gc);
if (grid_compare(c->status.grid, old_status.grid) == 0) { if (grid_compare(c->status.grid, old_status.grid) == 0) {
screen_free(&old_status); screen_free(&old_status);
return (0); return (0);
@ -805,19 +836,37 @@ status_prompt_redraw(struct client *c)
return (1); return (1);
} }
/* Is this a separator? */
static int
status_prompt_in_list(const char *ws, const struct utf8_data *ud)
{
if (ud->size != 1 || ud->width != 1)
return (0);
return (strchr(ws, *ud->data) != NULL);
}
/* Is this a space? */
static int
status_prompt_space(const struct utf8_data *ud)
{
if (ud->size != 1 || ud->width != 1)
return (0);
return (*ud->data == ' ');
}
/* Handle keys in prompt. */ /* Handle keys in prompt. */
void void
status_prompt_key(struct client *c, key_code key) status_prompt_key(struct client *c, key_code key)
{ {
struct session *sess = c->session; struct options *oo = c->session->options;
struct options *oo = sess->options;
struct paste_buffer *pb; struct paste_buffer *pb;
char *s, *first, *last, word[64], swapc; char *s, word[64];
const char *histstr, *bufdata, *wsep = NULL; const char *histstr, *bufdata, *ws = NULL;
u_char ch; u_char ch;
size_t size, n, off, idx, bufsize; size_t size, n, off, idx, bufsize, used;
struct utf8_data tmp, *first, *last, *ud;
size = strlen(c->prompt_buffer); size = utf8_strlen(c->prompt_buffer);
switch (mode_key_lookup(&c->prompt_mdata, key, NULL, NULL)) { switch (mode_key_lookup(&c->prompt_mdata, key, NULL, NULL)) {
case MODEKEYEDIT_CURSORLEFT: case MODEKEYEDIT_CURSORLEFT:
if (c->prompt_index > 0) { if (c->prompt_index > 0) {
@ -856,7 +905,7 @@ status_prompt_key(struct client *c, key_code key)
} }
break; break;
case MODEKEYEDIT_COMPLETE: case MODEKEYEDIT_COMPLETE:
if (*c->prompt_buffer == '\0') if (c->prompt_buffer[0].size == 0)
break; break;
idx = c->prompt_index; idx = c->prompt_index;
@ -864,40 +913,50 @@ status_prompt_key(struct client *c, key_code key)
idx--; idx--;
/* Find the word we are in. */ /* Find the word we are in. */
first = c->prompt_buffer + idx; first = &c->prompt_buffer[idx];
while (first > c->prompt_buffer && *first != ' ') while (first > c->prompt_buffer && !status_prompt_space(first))
first--; first--;
while (*first == ' ') while (first->size != 0 && status_prompt_space(first))
first++; first++;
last = c->prompt_buffer + idx; last = &c->prompt_buffer[idx];
while (*last != '\0' && *last != ' ') while (last->size != 0 && !status_prompt_space(last))
last++; last++;
while (*last == ' ') while (last > c->prompt_buffer && status_prompt_space(last))
last--; last--;
if (*last != '\0') if (last->size != 0)
last++; last++;
if (last <= first || if (last <= first)
((size_t) (last - first)) > (sizeof word) - 1)
break; break;
memcpy(word, first, last - first);
word[last - first] = '\0'; used = 0;
for (ud = first; ud < last; ud++) {
if (used + ud->size >= sizeof word)
break;
memcpy(word + used, ud->data, ud->size);
used += ud->size;
}
if (ud != last)
break;
word[used] = '\0';
/* And try to complete it. */ /* And try to complete it. */
if ((s = status_prompt_complete(sess, word)) == NULL) if ((s = status_prompt_complete(c->session, word)) == NULL)
break; break;
/* Trim out word. */ /* Trim out word. */
n = size - (last - c->prompt_buffer) + 1; /* with \0 */ n = size - (last - c->prompt_buffer) + 1; /* with \0 */
memmove(first, last, n); memmove(first, last, n * sizeof *c->prompt_buffer);
size -= last - first; size -= last - first;
/* Insert the new word. */ /* Insert the new word. */
size += strlen(s); size += strlen(s);
off = first - c->prompt_buffer; off = first - c->prompt_buffer;
c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
sizeof *c->prompt_buffer);
first = c->prompt_buffer + off; first = c->prompt_buffer + off;
memmove(first + strlen(s), first, n); memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
memcpy(first, s, strlen(s)); for (idx = 0; idx < strlen(s); idx++)
utf8_set(&first[idx], s[idx]);
c->prompt_index = (first - c->prompt_buffer) + strlen(s); c->prompt_index = (first - c->prompt_buffer) + strlen(s);
free(s); free(s);
@ -907,11 +966,12 @@ status_prompt_key(struct client *c, key_code key)
case MODEKEYEDIT_BACKSPACE: case MODEKEYEDIT_BACKSPACE:
if (c->prompt_index != 0) { if (c->prompt_index != 0) {
if (c->prompt_index == size) if (c->prompt_index == size)
c->prompt_buffer[--c->prompt_index] = '\0'; c->prompt_buffer[--c->prompt_index].size = 0;
else { else {
memmove(c->prompt_buffer + c->prompt_index - 1, memmove(c->prompt_buffer + c->prompt_index - 1,
c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index,
size + 1 - c->prompt_index); (size + 1 - c->prompt_index) *
sizeof *c->prompt_buffer);
c->prompt_index--; c->prompt_index--;
} }
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
@ -922,38 +982,39 @@ status_prompt_key(struct client *c, key_code key)
if (c->prompt_index != size) { if (c->prompt_index != size) {
memmove(c->prompt_buffer + c->prompt_index, memmove(c->prompt_buffer + c->prompt_index,
c->prompt_buffer + c->prompt_index + 1, c->prompt_buffer + c->prompt_index + 1,
size + 1 - c->prompt_index); (size + 1 - c->prompt_index) *
sizeof *c->prompt_buffer);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
} }
break; break;
case MODEKEYEDIT_DELETELINE: case MODEKEYEDIT_DELETELINE:
case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
*c->prompt_buffer = '\0'; c->prompt_buffer[0].size = 0;
c->prompt_index = 0; c->prompt_index = 0;
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_DELETETOENDOFLINE: case MODEKEYEDIT_DELETETOENDOFLINE:
case MODEKEYEDIT_SWITCHMODECHANGELINE: case MODEKEYEDIT_SWITCHMODECHANGELINE:
if (c->prompt_index < size) { if (c->prompt_index < size) {
c->prompt_buffer[c->prompt_index] = '\0'; c->prompt_buffer[c->prompt_index].size = 0;
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
} }
break; break;
case MODEKEYEDIT_DELETEWORD: case MODEKEYEDIT_DELETEWORD:
wsep = options_get_string(oo, "word-separators"); ws = options_get_string(oo, "word-separators");
idx = c->prompt_index; idx = c->prompt_index;
/* Find a non-separator. */ /* Find a non-separator. */
while (idx != 0) { while (idx != 0) {
idx--; idx--;
if (!strchr(wsep, c->prompt_buffer[idx])) if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
/* Find the separator at the beginning of the word. */ /* Find the separator at the beginning of the word. */
while (idx != 0) { while (idx != 0) {
idx--; idx--;
if (strchr(wsep, c->prompt_buffer[idx])) { if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
/* Go back to the word. */ /* Go back to the word. */
idx++; idx++;
break; break;
@ -962,53 +1023,55 @@ status_prompt_key(struct client *c, key_code key)
memmove(c->prompt_buffer + idx, memmove(c->prompt_buffer + idx,
c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index,
size + 1 - c->prompt_index); (size + 1 - c->prompt_index) *
sizeof *c->prompt_buffer);
memset(c->prompt_buffer + size - (c->prompt_index - idx), memset(c->prompt_buffer + size - (c->prompt_index - idx),
'\0', c->prompt_index - idx); '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
c->prompt_index = idx; c->prompt_index = idx;
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_NEXTSPACE: case MODEKEYEDIT_NEXTSPACE:
wsep = " "; ws = " ";
/* FALLTHROUGH */ /* FALLTHROUGH */
case MODEKEYEDIT_NEXTWORD: case MODEKEYEDIT_NEXTWORD:
if (wsep == NULL) if (ws == NULL)
wsep = options_get_string(oo, "word-separators"); ws = options_get_string(oo, "word-separators");
/* Find a separator. */ /* Find a separator. */
while (c->prompt_index != size) { while (c->prompt_index != size) {
c->prompt_index++; idx = ++c->prompt_index;
if (strchr(wsep, c->prompt_buffer[c->prompt_index])) if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
/* Find the word right after the separation. */ /* Find the word right after the separator. */
while (c->prompt_index != size) { while (c->prompt_index != size) {
c->prompt_index++; idx = ++c->prompt_index;
if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_NEXTSPACEEND: case MODEKEYEDIT_NEXTSPACEEND:
wsep = " "; ws = " ";
/* FALLTHROUGH */ /* FALLTHROUGH */
case MODEKEYEDIT_NEXTWORDEND: case MODEKEYEDIT_NEXTWORDEND:
if (wsep == NULL) if (ws == NULL)
wsep = options_get_string(oo, "word-separators"); ws = options_get_string(oo, "word-separators");
/* Find a word. */ /* Find a word. */
while (c->prompt_index != size) { while (c->prompt_index != size) {
c->prompt_index++; idx = ++c->prompt_index;
if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
/* Find the separator at the end of the word. */ /* Find the separator at the end of the word. */
while (c->prompt_index != size) { while (c->prompt_index != size) {
c->prompt_index++; idx = ++c->prompt_index;
if (strchr(wsep, c->prompt_buffer[c->prompt_index])) if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
@ -1020,23 +1083,23 @@ status_prompt_key(struct client *c, key_code key)
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_PREVIOUSSPACE: case MODEKEYEDIT_PREVIOUSSPACE:
wsep = " "; ws = " ";
/* FALLTHROUGH */ /* FALLTHROUGH */
case MODEKEYEDIT_PREVIOUSWORD: case MODEKEYEDIT_PREVIOUSWORD:
if (wsep == NULL) if (ws == NULL)
wsep = options_get_string(oo, "word-separators"); ws = options_get_string(oo, "word-separators");
/* Find a non-separator. */ /* Find a non-separator. */
while (c->prompt_index != 0) { while (c->prompt_index != 0) {
c->prompt_index--; idx = --c->prompt_index;
if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
break; break;
} }
/* Find the separator at the beginning of the word. */ /* Find the separator at the beginning of the word. */
while (c->prompt_index != 0) { while (c->prompt_index != 0) {
c->prompt_index--; idx = --c->prompt_index;
if (strchr(wsep, c->prompt_buffer[c->prompt_index])) { if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
/* Go back to the word. */ /* Go back to the word. */
c->prompt_index++; c->prompt_index++;
break; break;
@ -1050,8 +1113,8 @@ status_prompt_key(struct client *c, key_code key)
if (histstr == NULL) if (histstr == NULL)
break; break;
free(c->prompt_buffer); free(c->prompt_buffer);
c->prompt_buffer = xstrdup(histstr); c->prompt_buffer = utf8_fromcstr(histstr);
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_HISTORYDOWN: case MODEKEYEDIT_HISTORYDOWN:
@ -1059,8 +1122,8 @@ status_prompt_key(struct client *c, key_code key)
if (histstr == NULL) if (histstr == NULL)
break; break;
free(c->prompt_buffer); free(c->prompt_buffer);
c->prompt_buffer = xstrdup(histstr); c->prompt_buffer = utf8_fromcstr(histstr);
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_PASTE: case MODEKEYEDIT_PASTE:
@ -1069,20 +1132,28 @@ status_prompt_key(struct client *c, key_code key)
bufdata = paste_buffer_data(pb, &bufsize); bufdata = paste_buffer_data(pb, &bufsize);
for (n = 0; n < bufsize; n++) { for (n = 0; n < bufsize; n++) {
ch = (u_char)bufdata[n]; ch = (u_char)bufdata[n];
if (ch < 32 || ch == 127) if (ch < 32 || ch >= 127)
break; break;
} }
c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
sizeof *c->prompt_buffer);
if (c->prompt_index == size) { if (c->prompt_index == size) {
memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); for (idx = 0; idx < n; idx++) {
ud = &c->prompt_buffer[c->prompt_index + idx];
utf8_set(ud, bufdata[idx]);
}
c->prompt_index += n; c->prompt_index += n;
c->prompt_buffer[c->prompt_index] = '\0'; c->prompt_buffer[c->prompt_index].size = 0;
} else { } else {
memmove(c->prompt_buffer + c->prompt_index + n, memmove(c->prompt_buffer + c->prompt_index + n,
c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index,
size + 1 - c->prompt_index); (size + 1 - c->prompt_index) *
memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); sizeof *c->prompt_buffer);
for (idx = 0; idx < n; idx++) {
ud = &c->prompt_buffer[c->prompt_index + idx];
utf8_set(ud, bufdata[idx]);
}
c->prompt_index += n; c->prompt_index += n;
} }
@ -1093,42 +1164,55 @@ status_prompt_key(struct client *c, key_code key)
if (idx < size) if (idx < size)
idx++; idx++;
if (idx >= 2) { if (idx >= 2) {
swapc = c->prompt_buffer[idx - 2]; utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1]; utf8_copy(&c->prompt_buffer[idx - 2],
c->prompt_buffer[idx - 1] = swapc; &c->prompt_buffer[idx - 1]);
utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
c->prompt_index = idx; c->prompt_index = idx;
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
} }
break; break;
case MODEKEYEDIT_ENTER: case MODEKEYEDIT_ENTER:
if (*c->prompt_buffer != '\0') s = utf8_tocstr(c->prompt_buffer);
status_prompt_add_history(c->prompt_buffer); if (*s != '\0')
if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0) status_prompt_add_history(s);
if (c->prompt_callbackfn(c->prompt_data, s) == 0)
status_prompt_clear(c); status_prompt_clear(c);
free(s);
break; break;
case MODEKEYEDIT_CANCEL: case MODEKEYEDIT_CANCEL:
if (c->prompt_callbackfn(c->prompt_data, NULL) == 0) if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
status_prompt_clear(c); status_prompt_clear(c);
break; break;
case MODEKEY_OTHER: case MODEKEY_OTHER:
if (key <= 0x1f || key >= 0x7f) if (key <= 0x1f || key >= KEYC_BASE)
break; break;
c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2); if (utf8_split(key, &tmp) != UTF8_DONE)
break;
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
sizeof *c->prompt_buffer);
if (c->prompt_index == size) { if (c->prompt_index == size) {
c->prompt_buffer[c->prompt_index++] = key; utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
c->prompt_buffer[c->prompt_index] = '\0'; c->prompt_index++;
c->prompt_buffer[c->prompt_index].size = 0;
} else { } else {
memmove(c->prompt_buffer + c->prompt_index + 1, memmove(c->prompt_buffer + c->prompt_index + 1,
c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index,
size + 1 - c->prompt_index); (size + 1 - c->prompt_index) *
c->prompt_buffer[c->prompt_index++] = key; sizeof *c->prompt_buffer);
utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
c->prompt_index++;
} }
if (c->prompt_flags & PROMPT_SINGLE) { if (c->prompt_flags & PROMPT_SINGLE) {
if (c->prompt_callbackfn(c->prompt_data, s = utf8_tocstr(c->prompt_buffer);
c->prompt_buffer) == 0) if (strlen(s) != 1)
status_prompt_clear(c); status_prompt_clear(c);
else if (c->prompt_callbackfn(c->prompt_data, s) == 0)
status_prompt_clear(c);
free(s);
} }
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
@ -1247,7 +1331,7 @@ status_prompt_complete_prefix(const char **list, u_int size)
/* Complete word. */ /* Complete word. */
static char * static char *
status_prompt_complete(struct session *sess, const char *s) status_prompt_complete(struct session *session, const char *s)
{ {
const char **list = NULL, *colon; const char **list = NULL, *colon;
u_int size = 0, i; u_int size = 0, i;
@ -1300,7 +1384,7 @@ status_prompt_complete(struct session *sess, const char *s)
colon = ""; colon = "";
if (*s == ':') { if (*s == ':') {
RB_FOREACH(wl, winlinks, &sess->windows) { RB_FOREACH(wl, winlinks, &session->windows) {
xasprintf(&tmp, ":%s", wl->window->name); xasprintf(&tmp, ":%s", wl->window->name);
if (strncmp(tmp, s, strlen(s)) == 0){ if (strncmp(tmp, s, strlen(s)) == 0){
list = xreallocarray(list, size + 1, list = xreallocarray(list, size + 1,

4
tmux.h
View File

@ -1300,7 +1300,7 @@ struct client {
TAILQ_HEAD(, message_entry) message_log; TAILQ_HEAD(, message_entry) message_log;
char *prompt_string; char *prompt_string;
char *prompt_buffer; struct utf8_data *prompt_buffer;
size_t prompt_index; size_t prompt_index;
int (*prompt_callbackfn)(void *, const char *); int (*prompt_callbackfn)(void *, const char *);
void (*prompt_freefn)(void *); void (*prompt_freefn)(void *);
@ -2345,6 +2345,8 @@ enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *);
enum utf8_state utf8_split(wchar_t, struct utf8_data *); enum utf8_state utf8_split(wchar_t, struct utf8_data *);
int utf8_strvis(char *, const char *, size_t, int); int utf8_strvis(char *, const char *, size_t, int);
char *utf8_sanitize(const char *); char *utf8_sanitize(const char *);
size_t utf8_strlen(const struct utf8_data *);
u_int utf8_strwidth(const struct utf8_data *, ssize_t);
struct utf8_data *utf8_fromcstr(const char *); struct utf8_data *utf8_fromcstr(const char *);
char *utf8_tocstr(struct utf8_data *); char *utf8_tocstr(struct utf8_data *);
u_int utf8_cstrwidth(const char *); u_int utf8_cstrwidth(const char *);

27
utf8.c
View File

@ -236,6 +236,33 @@ utf8_sanitize(const char *src)
return (dst); return (dst);
} }
/* Get UTF-8 buffer length. */
size_t
utf8_strlen(const struct utf8_data *s)
{
size_t i;
for (i = 0; s[i].size != 0; i++)
/* nothing */;
return (i);
}
/* Get UTF-8 string width. */
u_int
utf8_strwidth(const struct utf8_data *s, ssize_t n)
{
ssize_t i;
u_int width;
width = 0;
for (i = 0; s[i].size != 0; i++) {
if (n != -1 && n == i)
break;
width += s[i].width;
}
return (width);
}
/* /*
* Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0.
* Caller frees. * Caller frees.