version 1.223, 2021/06/10 07:38:28 |
version 1.224, 2021/06/10 07:50:04 |
|
|
static void status_timer_callback(int, short, void *); |
static void status_timer_callback(int, short, void *); |
|
|
static char *status_prompt_find_history_file(void); |
static char *status_prompt_find_history_file(void); |
static const char *status_prompt_up_history(u_int *); |
static const char *status_prompt_up_history(u_int *, u_int); |
static const char *status_prompt_down_history(u_int *); |
static const char *status_prompt_down_history(u_int *, u_int); |
static void status_prompt_add_history(const char *); |
static void status_prompt_add_history(const char *, u_int); |
|
|
static char *status_prompt_complete(struct client *, const char *, u_int); |
static char *status_prompt_complete(struct client *, const char *, u_int); |
static char *status_prompt_complete_window_menu(struct client *, |
static char *status_prompt_complete_window_menu(struct client *, |
|
|
char flag; |
char flag; |
}; |
}; |
|
|
|
static const char *prompt_type_strings[] = { |
|
"command", |
|
"search", |
|
"target", |
|
"window-target" |
|
}; |
|
|
/* Status prompt history. */ |
/* Status prompt history. */ |
#define PROMPT_HISTORY 100 |
char **status_prompt_hlist[PROMPT_NTYPES]; |
static char **status_prompt_hlist; |
u_int status_prompt_hsize[PROMPT_NTYPES]; |
static u_int status_prompt_hsize; |
|
|
|
/* Find the history file to load/save from/to. */ |
/* Find the history file to load/save from/to. */ |
static char * |
static char * |
|
|
return (path); |
return (path); |
} |
} |
|
|
|
/* Add loaded history item to the appropriate list. */ |
|
static void |
|
status_prompt_add_typed_history(char *line) |
|
{ |
|
char *typestr; |
|
enum prompt_type type = PROMPT_TYPE_INVALID; |
|
|
|
typestr = strsep(&line, ":"); |
|
if (line != NULL) |
|
type = status_prompt_type(typestr); |
|
if (type == PROMPT_TYPE_INVALID) { |
|
/* |
|
* Invalid types are not expected, but this provides backward |
|
* compatibility with old history files. |
|
*/ |
|
if (line != NULL) |
|
*(--line) = ':'; |
|
status_prompt_add_history(typestr, PROMPT_TYPE_COMMAND); |
|
} else |
|
status_prompt_add_history(line, type); |
|
} |
|
|
/* Load status prompt history from file. */ |
/* Load status prompt history from file. */ |
void |
void |
status_prompt_load_history(void) |
status_prompt_load_history(void) |
|
|
if (length > 0) { |
if (length > 0) { |
if (line[length - 1] == '\n') { |
if (line[length - 1] == '\n') { |
line[length - 1] = '\0'; |
line[length - 1] = '\0'; |
status_prompt_add_history(line); |
status_prompt_add_typed_history(line); |
} else { |
} else { |
tmp = xmalloc(length + 1); |
tmp = xmalloc(length + 1); |
memcpy(tmp, line, length); |
memcpy(tmp, line, length); |
tmp[length] = '\0'; |
tmp[length] = '\0'; |
status_prompt_add_history(tmp); |
status_prompt_add_typed_history(tmp); |
free(tmp); |
free(tmp); |
} |
} |
} |
} |
|
|
status_prompt_save_history(void) |
status_prompt_save_history(void) |
{ |
{ |
FILE *f; |
FILE *f; |
u_int i; |
u_int i, type; |
char *history_file; |
char *history_file; |
|
|
if ((history_file = status_prompt_find_history_file()) == NULL) |
if ((history_file = status_prompt_find_history_file()) == NULL) |
|
|
} |
} |
free(history_file); |
free(history_file); |
|
|
for (i = 0; i < status_prompt_hsize; i++) { |
for (type = 0; type < PROMPT_NTYPES; type++) { |
fputs(status_prompt_hlist[i], f); |
for (i = 0; i < status_prompt_hsize[type]; i++) { |
fputc('\n', f); |
fputs(prompt_type_strings[type], f); |
|
fputc(':', f); |
|
fputs(status_prompt_hlist[type][i], f); |
|
fputc('\n', f); |
|
} |
} |
} |
fclose(f); |
fclose(f); |
|
|
|
|
void |
void |
status_prompt_set(struct client *c, struct cmd_find_state *fs, |
status_prompt_set(struct client *c, struct cmd_find_state *fs, |
const char *msg, const char *input, prompt_input_cb inputcb, |
const char *msg, const char *input, prompt_input_cb inputcb, |
prompt_free_cb freecb, void *data, int flags) |
prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type) |
{ |
{ |
struct format_tree *ft; |
struct format_tree *ft; |
char *tmp; |
char *tmp; |
|
|
c->prompt_freecb = freecb; |
c->prompt_freecb = freecb; |
c->prompt_data = data; |
c->prompt_data = data; |
|
|
c->prompt_hindex = 0; |
memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); |
|
|
c->prompt_flags = flags; |
c->prompt_flags = flags; |
|
c->prompt_type = prompt_type; |
c->prompt_mode = PROMPT_ENTRY; |
c->prompt_mode = PROMPT_ENTRY; |
|
|
if (~flags & PROMPT_INCREMENTAL) |
if (~flags & PROMPT_INCREMENTAL) |
|
|
c->prompt_buffer = utf8_fromcstr(tmp); |
c->prompt_buffer = utf8_fromcstr(tmp); |
c->prompt_index = utf8_strlen(c->prompt_buffer); |
c->prompt_index = utf8_strlen(c->prompt_buffer); |
|
|
c->prompt_hindex = 0; |
memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); |
|
|
c->flags |= CLIENT_REDRAWSTATUS; |
c->flags |= CLIENT_REDRAWSTATUS; |
|
|
|
|
} |
} |
|
|
/* |
/* |
* Translate key from emacs to vi. Return 0 to drop key, 1 to process the key |
* Translate key from vi to emacs. Return 0 to drop key, 1 to process the key |
* as an emacs key; return 2 to append to the buffer. |
* as an emacs key; return 2 to append to the buffer. |
*/ |
*/ |
static int |
static int |
|
|
goto changed; |
goto changed; |
case KEYC_UP: |
case KEYC_UP: |
case '\020': /* C-p */ |
case '\020': /* C-p */ |
histstr = status_prompt_up_history(&c->prompt_hindex); |
histstr = status_prompt_up_history(c->prompt_hindex, |
|
c->prompt_type); |
if (histstr == NULL) |
if (histstr == NULL) |
break; |
break; |
free(c->prompt_buffer); |
free(c->prompt_buffer); |
|
|
goto changed; |
goto changed; |
case KEYC_DOWN: |
case KEYC_DOWN: |
case '\016': /* C-n */ |
case '\016': /* C-n */ |
histstr = status_prompt_down_history(&c->prompt_hindex); |
histstr = status_prompt_down_history(c->prompt_hindex, |
|
c->prompt_type); |
if (histstr == NULL) |
if (histstr == NULL) |
break; |
break; |
free(c->prompt_buffer); |
free(c->prompt_buffer); |
|
|
case '\n': |
case '\n': |
s = utf8_tocstr(c->prompt_buffer); |
s = utf8_tocstr(c->prompt_buffer); |
if (*s != '\0') |
if (*s != '\0') |
status_prompt_add_history(s); |
status_prompt_add_history(s, c->prompt_type); |
if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) |
if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) |
status_prompt_clear(c); |
status_prompt_clear(c); |
free(s); |
free(s); |
|
|
|
|
/* Get previous line from the history. */ |
/* Get previous line from the history. */ |
static const char * |
static const char * |
status_prompt_up_history(u_int *idx) |
status_prompt_up_history(u_int *idx, u_int type) |
{ |
{ |
/* |
/* |
* History runs from 0 to size - 1. Index is from 0 to size. Zero is |
* History runs from 0 to size - 1. Index is from 0 to size. Zero is |
* empty. |
* empty. |
*/ |
*/ |
|
|
if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) |
if (status_prompt_hsize[type] == 0 || |
|
idx[type] == status_prompt_hsize[type]) |
return (NULL); |
return (NULL); |
(*idx)++; |
idx[type]++; |
return (status_prompt_hlist[status_prompt_hsize - *idx]); |
return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); |
} |
} |
|
|
/* Get next line from the history. */ |
/* Get next line from the history. */ |
static const char * |
static const char * |
status_prompt_down_history(u_int *idx) |
status_prompt_down_history(u_int *idx, u_int type) |
{ |
{ |
if (status_prompt_hsize == 0 || *idx == 0) |
if (status_prompt_hsize[type] == 0 || idx[type] == 0) |
return (""); |
return (""); |
(*idx)--; |
idx[type]--; |
if (*idx == 0) |
if (idx[type] == 0) |
return (""); |
return (""); |
return (status_prompt_hlist[status_prompt_hsize - *idx]); |
return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); |
} |
} |
|
|
/* Add line to the history. */ |
/* Add line to the history. */ |
static void |
static void |
status_prompt_add_history(const char *line) |
status_prompt_add_history(const char *line, u_int type) |
{ |
{ |
size_t size; |
u_int i, oldsize, newsize, freecount, hlimit, new = 1; |
|
size_t movesize; |
|
|
if (status_prompt_hsize > 0 && |
oldsize = status_prompt_hsize[type]; |
strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) |
if (oldsize > 0 && |
return; |
strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0) |
|
new = 0; |
|
|
if (status_prompt_hsize == PROMPT_HISTORY) { |
hlimit = options_get_number(global_options, "prompt-history-limit"); |
free(status_prompt_hlist[0]); |
if (hlimit > oldsize) { |
|
if (new == 0) |
|
return; |
|
newsize = oldsize + new; |
|
} else { |
|
newsize = hlimit; |
|
freecount = oldsize + new - newsize; |
|
if (freecount > oldsize) |
|
freecount = oldsize; |
|
if (freecount == 0) |
|
return; |
|
for (i = 0; i < freecount; i++) |
|
free(status_prompt_hlist[type][i]); |
|
movesize = (oldsize - freecount) * |
|
sizeof *status_prompt_hlist[type]; |
|
if (movesize > 0) { |
|
memmove(&status_prompt_hlist[type][0], |
|
&status_prompt_hlist[type][freecount], movesize); |
|
} |
|
} |
|
|
size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; |
if (newsize == 0) { |
memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); |
free(status_prompt_hlist[type]); |
|
status_prompt_hlist[type] = NULL; |
status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); |
} else if (newsize != oldsize) { |
return; |
status_prompt_hlist[type] = |
|
xreallocarray(status_prompt_hlist[type], newsize, |
|
sizeof *status_prompt_hlist[type]); |
} |
} |
|
|
status_prompt_hlist = xreallocarray(status_prompt_hlist, |
if (new == 1 && newsize > 0) |
status_prompt_hsize + 1, sizeof *status_prompt_hlist); |
status_prompt_hlist[type][newsize - 1] = xstrdup(line); |
status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); |
status_prompt_hsize[type] = newsize; |
} |
} |
|
|
/* Build completion list. */ |
/* Build completion list. */ |
|
|
s = xstrdup(spm->list[idx]); |
s = xstrdup(spm->list[idx]); |
else |
else |
xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); |
xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); |
if (c->prompt_flags & PROMPT_WINDOW) { |
if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { |
free(c->prompt_buffer); |
free(c->prompt_buffer); |
c->prompt_buffer = utf8_fromcstr(s); |
c->prompt_buffer = utf8_fromcstr(s); |
c->prompt_index = utf8_strlen(c->prompt_buffer); |
c->prompt_index = utf8_strlen(c->prompt_buffer); |
|
|
} |
} |
|
|
list = xreallocarray(list, size + 1, sizeof *list); |
list = xreallocarray(list, size + 1, sizeof *list); |
if (c->prompt_flags & PROMPT_WINDOW) { |
if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { |
xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); |
xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); |
xasprintf(&list[size++], "%d", wl->idx); |
xasprintf(&list[size++], "%d", wl->idx); |
} else { |
} else { |
|
|
u_int size = 0, i; |
u_int size = 0, i; |
|
|
if (*word == '\0' && |
if (*word == '\0' && |
((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) |
c->prompt_type != PROMPT_TYPE_TARGET && |
|
c->prompt_type != PROMPT_TYPE_WINDOW_TARGET) |
return (NULL); |
return (NULL); |
|
|
if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && |
if (c->prompt_type != PROMPT_TYPE_TARGET && |
|
c->prompt_type != PROMPT_TYPE_WINDOW_TARGET && |
strncmp(word, "-t", 2) != 0 && |
strncmp(word, "-t", 2) != 0 && |
strncmp(word, "-s", 2) != 0) { |
strncmp(word, "-s", 2) != 0) { |
list = status_prompt_complete_list(&size, word, offset == 0); |
list = status_prompt_complete_list(&size, word, offset == 0); |
|
|
goto found; |
goto found; |
} |
} |
|
|
if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { |
if (c->prompt_type == PROMPT_TYPE_TARGET || |
|
c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { |
s = word; |
s = word; |
flag = '\0'; |
flag = '\0'; |
} else { |
} else { |
|
|
} |
} |
|
|
/* If this is a window completion, open the window menu. */ |
/* If this is a window completion, open the window menu. */ |
if (c->prompt_flags & PROMPT_WINDOW) { |
if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { |
out = status_prompt_complete_window_menu(c, c->session, s, |
out = status_prompt_complete_window_menu(c, c->session, s, |
offset, '\0'); |
offset, '\0'); |
goto found; |
goto found; |
|
|
free(list); |
free(list); |
} |
} |
return (out); |
return (out); |
|
} |
|
|
|
/* Return the type of the prompt as an enum. */ |
|
enum prompt_type |
|
status_prompt_type(const char *type) |
|
{ |
|
u_int i; |
|
|
|
for (i = 0; i < PROMPT_NTYPES; i++) { |
|
if (strcmp(type, status_prompt_type_string(i)) == 0) |
|
return (i); |
|
} |
|
return (PROMPT_TYPE_INVALID); |
|
} |
|
|
|
/* Accessor for prompt_type_strings. */ |
|
const char * |
|
status_prompt_type_string(u_int type) |
|
{ |
|
if (type >= PROMPT_NTYPES) |
|
return ("invalid"); |
|
return (prompt_type_strings[type]); |
} |
} |