mirror of
https://github.com/tmux/tmux.git
synced 2026-10-07 10:14:28 +00:00
additional payload (to reduce problems with lifetime of objects) and are delivered to one or more event sinks. This is more powerful and reduces the complex dependencies between control mode and hooks. Events are now used for hooks, control mode notifications and for monitors (set-hook -B). wait-for can now wait for an event to fire (-E flag, with -F to for filter), with -v to print the payload, as well as listing (-l) waiting clients on an event and forcing one to wake (-w). A few additional hooks are also now available (pane-created, pane-resized, etc) and some of the existing ones have additional format variables available.
265 lines
6.1 KiB
C
265 lines
6.1 KiB
C
/* $OpenBSD: prompt-history.c,v 1.1 2026/06/25 11:39:11 nicm Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
static char *prompt_find_history_file(void);
|
|
static void prompt_add_typed_history(char *);
|
|
|
|
/* Prompt history. */
|
|
static char **prompt_hlist[PROMPT_NTYPES];
|
|
static u_int prompt_hsize[PROMPT_NTYPES];
|
|
|
|
/* Find the history file to load/save from/to. */
|
|
static char *
|
|
prompt_find_history_file(void)
|
|
{
|
|
const char *home, *history_file;
|
|
char *path;
|
|
|
|
history_file = options_get_string(global_options, "history-file");
|
|
if (*history_file == '\0')
|
|
return (NULL);
|
|
if (*history_file == '/')
|
|
return (xstrdup(history_file));
|
|
|
|
if (history_file[0] != '~' || history_file[1] != '/')
|
|
return (NULL);
|
|
if ((home = find_home()) == NULL)
|
|
return (NULL);
|
|
xasprintf(&path, "%s%s", home, history_file + 1);
|
|
return (path);
|
|
}
|
|
|
|
/* Add loaded history item to the appropriate list. */
|
|
static void
|
|
prompt_add_typed_history(char *line)
|
|
{
|
|
char *typestr;
|
|
enum prompt_type type = PROMPT_TYPE_INVALID;
|
|
|
|
typestr = strsep(&line, ":");
|
|
if (line != NULL)
|
|
type = 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) = ':';
|
|
prompt_add_history(typestr, PROMPT_TYPE_COMMAND);
|
|
} else
|
|
prompt_add_history(line, type);
|
|
}
|
|
|
|
/* Load prompt history from file. */
|
|
void
|
|
prompt_load_history(void)
|
|
{
|
|
FILE *f;
|
|
char *history_file, *line, *tmp;
|
|
size_t length;
|
|
|
|
if ((history_file = prompt_find_history_file()) == NULL)
|
|
return;
|
|
log_debug("loading history from %s", history_file);
|
|
|
|
f = fopen(history_file, "r");
|
|
if (f == NULL) {
|
|
log_debug("%s: %s", history_file, strerror(errno));
|
|
free(history_file);
|
|
return;
|
|
}
|
|
free(history_file);
|
|
|
|
for (;;) {
|
|
if ((line = fgetln(f, &length)) == NULL)
|
|
break;
|
|
|
|
if (length > 0) {
|
|
if (line[length - 1] == '\n') {
|
|
line[length - 1] = '\0';
|
|
prompt_add_typed_history(line);
|
|
} else {
|
|
tmp = xmalloc(length + 1);
|
|
memcpy(tmp, line, length);
|
|
tmp[length] = '\0';
|
|
prompt_add_typed_history(tmp);
|
|
free(tmp);
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
}
|
|
|
|
/* Save prompt history to file. */
|
|
void
|
|
prompt_save_history(void)
|
|
{
|
|
FILE *f;
|
|
u_int i, type;
|
|
char *history_file;
|
|
|
|
if ((history_file = prompt_find_history_file()) == NULL)
|
|
return;
|
|
log_debug("saving history to %s", history_file);
|
|
|
|
f = fopen(history_file, "w");
|
|
if (f == NULL) {
|
|
log_debug("%s: %s", history_file, strerror(errno));
|
|
free(history_file);
|
|
return;
|
|
}
|
|
free(history_file);
|
|
|
|
for (type = 0; type < PROMPT_NTYPES; type++) {
|
|
for (i = 0; i < prompt_hsize[type]; i++) {
|
|
fputs(prompt_type_string(type), f);
|
|
fputc(':', f);
|
|
fputs(prompt_hlist[type][i], f);
|
|
fputc('\n', f);
|
|
}
|
|
}
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
/* Get previous line from the history. */
|
|
const char *
|
|
prompt_up_history(u_int *idx, u_int type)
|
|
{
|
|
/*
|
|
* History runs from 0 to size - 1. Index is from 0 to size. Zero is
|
|
* empty.
|
|
*/
|
|
|
|
if (type >= PROMPT_NTYPES)
|
|
return (NULL);
|
|
if (prompt_hsize[type] == 0 || idx[type] == prompt_hsize[type])
|
|
return (NULL);
|
|
idx[type]++;
|
|
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
|
|
}
|
|
|
|
/* Get next line from the history. */
|
|
const char *
|
|
prompt_down_history(u_int *idx, u_int type)
|
|
{
|
|
if (type >= PROMPT_NTYPES)
|
|
return ("");
|
|
if (prompt_hsize[type] == 0 || idx[type] == 0)
|
|
return ("");
|
|
idx[type]--;
|
|
if (idx[type] == 0)
|
|
return ("");
|
|
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
|
|
}
|
|
|
|
/* Add line to the history. */
|
|
void
|
|
prompt_add_history(const char *line, u_int type)
|
|
{
|
|
u_int i, oldsize, newsize, freecount, hlimit, new = 1;
|
|
size_t movesize;
|
|
|
|
if (type >= PROMPT_NTYPES)
|
|
return;
|
|
|
|
oldsize = prompt_hsize[type];
|
|
if (oldsize > 0 &&
|
|
strcmp(prompt_hlist[type][oldsize - 1], line) == 0)
|
|
new = 0;
|
|
|
|
hlimit = options_get_number(global_options, "prompt-history-limit");
|
|
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(prompt_hlist[type][i]);
|
|
movesize = (oldsize - freecount) *
|
|
sizeof *prompt_hlist[type];
|
|
if (movesize > 0) {
|
|
memmove(&prompt_hlist[type][0],
|
|
&prompt_hlist[type][freecount], movesize);
|
|
}
|
|
}
|
|
|
|
if (newsize == 0) {
|
|
free(prompt_hlist[type]);
|
|
prompt_hlist[type] = NULL;
|
|
} else if (newsize != oldsize) {
|
|
prompt_hlist[type] =
|
|
xreallocarray(prompt_hlist[type], newsize,
|
|
sizeof *prompt_hlist[type]);
|
|
}
|
|
|
|
if (new == 1 && newsize > 0)
|
|
prompt_hlist[type][newsize - 1] = xstrdup(line);
|
|
prompt_hsize[type] = newsize;
|
|
}
|
|
|
|
/* Get history size. */
|
|
u_int
|
|
prompt_history_size(enum prompt_type type)
|
|
{
|
|
if (type >= PROMPT_NTYPES)
|
|
return (0);
|
|
return (prompt_hsize[type]);
|
|
}
|
|
|
|
/* Get history entry. */
|
|
const char *
|
|
prompt_history_get(enum prompt_type type, u_int idx)
|
|
{
|
|
if (type >= PROMPT_NTYPES)
|
|
return (NULL);
|
|
if (idx >= prompt_hsize[type])
|
|
return (NULL);
|
|
return (prompt_hlist[type][idx]);
|
|
}
|
|
|
|
/* Clear prompt history. */
|
|
void
|
|
prompt_history_clear(enum prompt_type type)
|
|
{
|
|
u_int idx;
|
|
|
|
if (type >= PROMPT_NTYPES)
|
|
return;
|
|
for (idx = 0; idx < prompt_hsize[type]; idx++)
|
|
free(prompt_hlist[type][idx]);
|
|
free(prompt_hlist[type]);
|
|
prompt_hlist[type] = NULL;
|
|
prompt_hsize[type] = 0;
|
|
}
|