version 1.203, 2020/05/16 15:01:31 |
version 1.204, 2020/05/16 15:06:03 |
|
|
static const char *status_prompt_down_history(u_int *); |
static const char *status_prompt_down_history(u_int *); |
static void status_prompt_add_history(const char *); |
static void status_prompt_add_history(const char *); |
|
|
static char **status_prompt_complete_list(u_int *, const char *); |
static char *status_prompt_complete(struct client *, const char *, u_int); |
static char *status_prompt_complete_prefix(char **, u_int); |
|
static char *status_prompt_complete(struct session *, const char *); |
|
|
|
|
struct status_prompt_menu { |
|
struct client *c; |
|
u_int start; |
|
u_int size; |
|
char **list; |
|
char flag; |
|
}; |
|
|
/* Status prompt history. */ |
/* Status prompt history. */ |
#define PROMPT_HISTORY 100 |
#define PROMPT_HISTORY 100 |
static char **status_prompt_hlist; |
static char **status_prompt_hlist; |
|
|
return (1); |
return (1); |
} |
} |
|
|
|
/* Finish completion. */ |
|
static int |
|
status_prompt_replace_complete(struct client *c, const char *s) |
|
{ |
|
char word[64], *allocated = NULL; |
|
size_t size, n, off, idx, used; |
|
struct utf8_data *first, *last, *ud; |
|
|
|
if (c->prompt_buffer[0].size == 0) |
|
return (0); |
|
size = utf8_strlen(c->prompt_buffer); |
|
|
|
idx = c->prompt_index; |
|
if (idx != 0) |
|
idx--; |
|
|
|
/* Find the word we are in. */ |
|
first = &c->prompt_buffer[idx]; |
|
while (first > c->prompt_buffer && !status_prompt_space(first)) |
|
first--; |
|
while (first->size != 0 && status_prompt_space(first)) |
|
first++; |
|
last = &c->prompt_buffer[idx]; |
|
while (last->size != 0 && !status_prompt_space(last)) |
|
last++; |
|
while (last > c->prompt_buffer && status_prompt_space(last)) |
|
last--; |
|
if (last->size != 0) |
|
last++; |
|
if (last <= first) |
|
return (0); |
|
if (s == NULL) { |
|
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) |
|
return (0); |
|
word[used] = '\0'; |
|
} |
|
|
|
/* Try to complete it. */ |
|
if (s == NULL) { |
|
allocated = status_prompt_complete(c, word, |
|
first - c->prompt_buffer); |
|
if (allocated == NULL) |
|
return (0); |
|
s = allocated; |
|
} |
|
|
|
/* Trim out word. */ |
|
n = size - (last - c->prompt_buffer) + 1; /* with \0 */ |
|
memmove(first, last, n * sizeof *c->prompt_buffer); |
|
size -= last - first; |
|
|
|
/* Insert the new word. */ |
|
size += strlen(s); |
|
off = first - c->prompt_buffer; |
|
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, |
|
sizeof *c->prompt_buffer); |
|
first = c->prompt_buffer + off; |
|
memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); |
|
for (idx = 0; idx < strlen(s); idx++) |
|
utf8_set(&first[idx], s[idx]); |
|
c->prompt_index = (first - c->prompt_buffer) + strlen(s); |
|
|
|
free(allocated); |
|
return (1); |
|
} |
|
|
/* Handle keys in prompt. */ |
/* Handle keys in prompt. */ |
int |
int |
status_prompt_key(struct client *c, key_code key) |
status_prompt_key(struct client *c, key_code key) |
{ |
{ |
struct options *oo = c->session->options; |
struct options *oo = c->session->options; |
char *s, *cp, word[64], prefix = '='; |
char *s, *cp, prefix = '='; |
const char *histstr, *ws = NULL, *keystring; |
const char *histstr, *ws = NULL, *keystring; |
size_t size, n, off, idx, used; |
size_t size, idx; |
struct utf8_data tmp, *first, *last, *ud; |
struct utf8_data tmp; |
int keys; |
int keys; |
|
|
if (c->prompt_flags & PROMPT_KEY) { |
if (c->prompt_flags & PROMPT_KEY) { |
|
|
} |
} |
break; |
break; |
case '\011': /* Tab */ |
case '\011': /* Tab */ |
if (c->prompt_buffer[0].size == 0) |
if (status_prompt_replace_complete(c, NULL)) |
break; |
goto changed; |
|
break; |
idx = c->prompt_index; |
|
if (idx != 0) |
|
idx--; |
|
|
|
/* Find the word we are in. */ |
|
first = &c->prompt_buffer[idx]; |
|
while (first > c->prompt_buffer && !status_prompt_space(first)) |
|
first--; |
|
while (first->size != 0 && status_prompt_space(first)) |
|
first++; |
|
last = &c->prompt_buffer[idx]; |
|
while (last->size != 0 && !status_prompt_space(last)) |
|
last++; |
|
while (last > c->prompt_buffer && status_prompt_space(last)) |
|
last--; |
|
if (last->size != 0) |
|
last++; |
|
if (last <= first) |
|
break; |
|
|
|
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. */ |
|
if ((s = status_prompt_complete(c->session, word)) == NULL) |
|
break; |
|
|
|
/* Trim out word. */ |
|
n = size - (last - c->prompt_buffer) + 1; /* with \0 */ |
|
memmove(first, last, n * sizeof *c->prompt_buffer); |
|
size -= last - first; |
|
|
|
/* Insert the new word. */ |
|
size += strlen(s); |
|
off = first - c->prompt_buffer; |
|
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, |
|
sizeof *c->prompt_buffer); |
|
first = c->prompt_buffer + off; |
|
memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); |
|
for (idx = 0; idx < strlen(s); idx++) |
|
utf8_set(&first[idx], s[idx]); |
|
|
|
c->prompt_index = (first - c->prompt_buffer) + strlen(s); |
|
free(s); |
|
|
|
goto changed; |
|
case KEYC_BSPACE: |
case KEYC_BSPACE: |
case '\010': /* C-h */ |
case '\010': /* C-h */ |
if (c->prompt_index != 0) { |
if (c->prompt_index != 0) { |
|
|
} |
} |
|
|
/* Build completion list. */ |
/* Build completion list. */ |
char ** |
static char ** |
status_prompt_complete_list(u_int *size, const char *s) |
status_prompt_complete_list(u_int *size, const char *s, int at_start) |
{ |
{ |
char **list = NULL; |
char **list = NULL; |
const char **layout, *value, *cp; |
const char **layout, *value, *cp; |
const struct cmd_entry **cmdent; |
const struct cmd_entry **cmdent; |
const struct options_table_entry *oe; |
const struct options_table_entry *oe; |
u_int idx; |
|
size_t slen = strlen(s), valuelen; |
size_t slen = strlen(s), valuelen; |
struct options_entry *o; |
struct options_entry *o; |
struct options_array_item *a; |
struct options_array_item *a; |
|
|
list[(*size)++] = xstrdup((*cmdent)->name); |
list[(*size)++] = xstrdup((*cmdent)->name); |
} |
} |
} |
} |
for (oe = options_table; oe->name != NULL; oe++) { |
|
if (strncmp(oe->name, s, slen) == 0) { |
|
list = xreallocarray(list, (*size) + 1, sizeof *list); |
|
list[(*size)++] = xstrdup(oe->name); |
|
} |
|
} |
|
for (layout = layouts; *layout != NULL; layout++) { |
|
if (strncmp(*layout, s, slen) == 0) { |
|
list = xreallocarray(list, (*size) + 1, sizeof *list); |
|
list[(*size)++] = xstrdup(*layout); |
|
} |
|
} |
|
o = options_get_only(global_options, "command-alias"); |
o = options_get_only(global_options, "command-alias"); |
if (o != NULL) { |
if (o != NULL) { |
a = options_array_first(o); |
a = options_array_first(o); |
|
|
a = options_array_next(a); |
a = options_array_next(a); |
} |
} |
} |
} |
for (idx = 0; idx < (*size); idx++) |
if (at_start) |
log_debug("complete %u: %s", idx, list[idx]); |
return (list); |
|
|
|
for (oe = options_table; oe->name != NULL; oe++) { |
|
if (strncmp(oe->name, s, slen) == 0) { |
|
list = xreallocarray(list, (*size) + 1, sizeof *list); |
|
list[(*size)++] = xstrdup(oe->name); |
|
} |
|
} |
|
for (layout = layouts; *layout != NULL; layout++) { |
|
if (strncmp(*layout, s, slen) == 0) { |
|
list = xreallocarray(list, (*size) + 1, sizeof *list); |
|
list[(*size)++] = xstrdup(*layout); |
|
} |
|
} |
return (list); |
return (list); |
} |
} |
|
|
|
|
return (out); |
return (out); |
} |
} |
|
|
|
/* Complete word menu callback. */ |
|
static void |
|
status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, |
|
void *data) |
|
{ |
|
struct status_prompt_menu *spm = data; |
|
struct client *c = spm->c; |
|
u_int i; |
|
char *s; |
|
|
|
if (key != KEYC_NONE) { |
|
idx += spm->start; |
|
if (spm->flag == '\0') |
|
s = xstrdup(spm->list[idx]); |
|
else |
|
xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); |
|
if (status_prompt_replace_complete(c, s)) |
|
c->flags |= CLIENT_REDRAWSTATUS; |
|
free(s); |
|
} |
|
|
|
for (i = 0; i < spm->size; i++) |
|
free(spm->list[i]); |
|
free(spm->list); |
|
} |
|
|
|
/* Show complete word menu. */ |
|
static int |
|
status_prompt_complete_list_menu(struct client *c, char **list, u_int size, |
|
u_int offset, char flag) |
|
{ |
|
struct menu *menu; |
|
struct menu_item item; |
|
struct status_prompt_menu *spm; |
|
u_int lines = status_line_size(c), height, i; |
|
u_int py; |
|
|
|
if (size <= 1) |
|
return (0); |
|
if (c->tty.sy - lines < 3) |
|
return (0); |
|
|
|
spm = xmalloc(sizeof *spm); |
|
spm->c = c; |
|
spm->size = size; |
|
spm->list = list; |
|
spm->flag = flag; |
|
|
|
height = c->tty.sy - lines - 2; |
|
if (height > 10) |
|
height = 10; |
|
if (height > size) |
|
height = size; |
|
spm->start = size - height; |
|
|
|
menu = menu_create(""); |
|
for (i = spm->start; i < size; i++) { |
|
item.name = list[i]; |
|
item.key = '0' + (i - spm->start); |
|
item.command = NULL; |
|
menu_add_item(menu, &item, NULL, NULL, NULL); |
|
} |
|
|
|
if (options_get_number(c->session->options, "status-position") == 0) |
|
py = lines; |
|
else |
|
py = c->tty.sy - 3 - height; |
|
offset += utf8_cstrwidth(c->prompt_string); |
|
if (offset > 2) |
|
offset -= 2; |
|
else |
|
offset = 0; |
|
|
|
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, |
|
py, c, NULL, status_prompt_menu_callback, spm) != 0) { |
|
menu_free(menu); |
|
free(spm); |
|
return (0); |
|
} |
|
return (1); |
|
} |
|
|
|
/* Show complete word menu. */ |
|
static char * |
|
status_prompt_complete_window_menu(struct client *c, struct session *s, |
|
u_int offset, char flag) |
|
{ |
|
struct menu *menu; |
|
struct menu_item item; |
|
struct status_prompt_menu *spm; |
|
struct winlink *wl; |
|
char **list = NULL, *tmp; |
|
u_int lines = status_line_size(c), height; |
|
u_int py, size = 0; |
|
|
|
if (c->tty.sy - lines < 3) |
|
return (NULL); |
|
|
|
spm = xmalloc(sizeof *spm); |
|
spm->c = c; |
|
spm->flag = flag; |
|
|
|
height = c->tty.sy - lines - 2; |
|
if (height > 10) |
|
height = 10; |
|
spm->start = 0; |
|
|
|
menu = menu_create(""); |
|
RB_FOREACH(wl, winlinks, &s->windows) { |
|
list = xreallocarray(list, size + 1, sizeof *list); |
|
xasprintf(&list[size++], "%s:%d", s->name, wl->idx); |
|
|
|
xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx, |
|
wl->window->name); |
|
item.name = tmp; |
|
item.key = '0' + size - 1; |
|
item.command = NULL; |
|
menu_add_item(menu, &item, NULL, NULL, NULL); |
|
free(tmp); |
|
|
|
if (size == height) |
|
break; |
|
} |
|
if (size == 1) { |
|
menu_free(menu); |
|
xasprintf(&tmp, "-%c%s", flag, list[0]); |
|
free(list[0]); |
|
free(list); |
|
return (tmp); |
|
} |
|
if (height > size) |
|
height = size; |
|
|
|
spm->size = size; |
|
spm->list = list; |
|
|
|
if (options_get_number(c->session->options, "status-position") == 0) |
|
py = lines; |
|
else |
|
py = c->tty.sy - 3 - height; |
|
offset += utf8_cstrwidth(c->prompt_string); |
|
if (offset > 2) |
|
offset -= 2; |
|
else |
|
offset = 0; |
|
|
|
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, |
|
py, c, NULL, status_prompt_menu_callback, spm) != 0) { |
|
menu_free(menu); |
|
free(spm); |
|
return (NULL); |
|
} |
|
return (NULL); |
|
} |
|
|
|
/* Sort complete list. */ |
|
static int |
|
status_prompt_complete_sort(const void *a, const void *b) |
|
{ |
|
const char **aa = (const char **)a, **bb = (const char **)b; |
|
|
|
return (strcmp(*aa, *bb)); |
|
} |
|
|
/* Complete word. */ |
/* Complete word. */ |
static char * |
static char * |
status_prompt_complete(struct session *session, const char *s) |
status_prompt_complete(struct client *c, const char *word, u_int offset) |
{ |
{ |
char **list = NULL; |
struct session *session, *loop; |
const char *colon; |
const char *s, *colon; |
|
size_t slen; |
|
char **list = NULL, *copy = NULL, *out = NULL, *tmp; |
|
char flag = '\0'; |
u_int size = 0, i; |
u_int size = 0, i; |
struct session *s_loop; |
|
struct winlink *wl; |
|
struct window *w; |
|
char *copy, *out, *tmp; |
|
|
|
if (*s == '\0') |
if (*word == '\0') |
return (NULL); |
return (NULL); |
out = NULL; |
|
|
|
if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { |
if (strncmp(word, "-t", 2) != 0 && strncmp(word, "-s", 2) != 0) { |
list = status_prompt_complete_list(&size, s); |
list = status_prompt_complete_list(&size, word, offset == 0); |
if (size == 0) |
if (size == 0) |
out = NULL; |
out = NULL; |
else if (size == 1) |
else if (size == 1) |
xasprintf(&out, "%s ", list[0]); |
xasprintf(&out, "%s ", list[0]); |
else |
else |
out = status_prompt_complete_prefix(list, size); |
out = status_prompt_complete_prefix(list, size); |
for (i = 0; i < size; i++) |
goto found; |
free(list[i]); |
|
free(list); |
|
return (out); |
|
} |
} |
copy = xstrdup(s); |
|
|
|
colon = ":"; |
s = word + 2; |
if (copy[strlen(copy) - 1] == ':') |
slen = strlen(s); |
copy[strlen(copy) - 1] = '\0'; |
|
else |
|
colon = ""; |
|
s = copy + 2; |
|
|
|
RB_FOREACH(s_loop, sessions, &sessions) { |
flag = word[1]; |
if (strncmp(s_loop->name, s, strlen(s)) == 0) { |
offset += 2; |
|
|
|
colon = strchr(s, ':'); |
|
|
|
/* If there is no colon, complete as a session. */ |
|
if (colon == NULL) { |
|
RB_FOREACH(loop, sessions, &sessions) { |
|
if (strncmp(loop->name, s, strlen(s)) != 0) |
|
continue; |
list = xreallocarray(list, size + 2, sizeof *list); |
list = xreallocarray(list, size + 2, sizeof *list); |
list[size++] = s_loop->name; |
xasprintf(&list[size++], "%s:", loop->name); |
} |
} |
} |
|
if (size == 1) { |
|
out = xstrdup(list[0]); |
|
if (session_find(list[0]) != NULL) |
|
colon = ":"; |
|
} else if (size != 0) |
|
out = status_prompt_complete_prefix(list, size); |
out = status_prompt_complete_prefix(list, size); |
if (out != NULL) { |
if (out != NULL) { |
xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); |
xasprintf(&tmp, "-%c%s", flag, out); |
free(out); |
free(out); |
out = tmp; |
out = tmp; |
|
} |
goto found; |
goto found; |
} |
} |
|
|
colon = ""; |
/* If there is a colon but no period, find session and show a menu. */ |
if (*s == ':') { |
if (strchr(colon + 1, '.') == NULL) { |
RB_FOREACH(wl, winlinks, &session->windows) { |
if (*s == ':') |
xasprintf(&tmp, ":%s", wl->window->name); |
session = c->session; |
if (strncmp(tmp, s, strlen(s)) == 0){ |
else { |
list = xreallocarray(list, size + 1, |
copy = xstrdup(s); |
sizeof *list); |
*strchr(copy, ':') = '\0'; |
list[size++] = tmp; |
session = session_find(copy); |
continue; |
free(copy); |
} |
if (session == NULL) |
free(tmp); |
goto found; |
|
|
xasprintf(&tmp, ":%d", wl->idx); |
|
if (strncmp(tmp, s, strlen(s)) == 0) { |
|
list = xreallocarray(list, size + 1, |
|
sizeof *list); |
|
list[size++] = tmp; |
|
continue; |
|
} |
|
free(tmp); |
|
} |
} |
} else { |
out = status_prompt_complete_window_menu(c, session, offset, |
RB_FOREACH(s_loop, sessions, &sessions) { |
flag); |
RB_FOREACH(wl, winlinks, &s_loop->windows) { |
if (out == NULL) |
w = wl->window; |
return (NULL); |
|
} |
|
|
xasprintf(&tmp, "%s:%s", s_loop->name, w->name); |
found: |
if (strncmp(tmp, s, strlen(s)) == 0) { |
if (size != 0) { |
list = xreallocarray(list, size + 1, |
qsort(list, size, sizeof *list, status_prompt_complete_sort); |
sizeof *list); |
for (i = 0; i < size; i++) |
list[size++] = tmp; |
log_debug("complete %u: %s", i, list[i]); |
continue; |
} |
} |
|
free(tmp); |
|
|
|
xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); |
if (out != NULL && strcmp(word, out) == 0) { |
if (strncmp(tmp, s, strlen(s)) == 0) { |
free(out); |
list = xreallocarray(list, size + 1, |
out = NULL; |
sizeof *list); |
|
list[size++] = tmp; |
|
continue; |
|
} |
|
free(tmp); |
|
} |
|
} |
|
} |
} |
if (size == 1) { |
if (out != NULL || |
out = xstrdup(list[0]); |
!status_prompt_complete_list_menu(c, list, size, offset, flag)) { |
colon = " "; |
for (i = 0; i < size; i++) |
} else if (size != 0) |
free(list[i]); |
out = status_prompt_complete_prefix(list, size); |
free(list); |
if (out != NULL) { |
|
xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); |
|
out = tmp; |
|
} |
} |
|
|
for (i = 0; i < size; i++) |
|
free((void *)list[i]); |
|
|
|
found: |
|
free(copy); |
|
free(list); |
|
return (out); |
return (out); |
} |
} |