mirror of
https://github.com/tmux/tmux.git
synced 2026-07-22 08:51:17 +00:00
256 lines
6.0 KiB
C
256 lines
6.0 KiB
C
/* $OpenBSD: prompt-history.c,v 1.2 2026/07/13 19:35:22 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 = NULL;
|
|
size_t length = 0;
|
|
ssize_t got;
|
|
|
|
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);
|
|
|
|
while ((got = getline(&line, &length, f)) != -1) {
|
|
if (got > 0 && line[got - 1] == '\n')
|
|
line[got - 1] = '\0';
|
|
if (got > 0)
|
|
prompt_add_typed_history(line);
|
|
}
|
|
free(line);
|
|
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;
|
|
}
|