/*
* history.c: stuff to handle command line history
*
*
* Written By Michael Sandrof
*
* Copyright(c) 1990
*
* See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "irc.h"
#include "ircaux.h"
#include "vars.h"
#include "history.h"
#include "output.h"
#include "input.h"
#include "tcommand.h"
static char *history_match(char *);
static void add_to_history_list(int, char *);
static char *get_from_history_buffer(int);
typedef struct HistoryStru {
int number;
char *stuff;
struct HistoryStru *next;
struct HistoryStru *prev;
} History;
/* command_history: pointer to head of command_history list */
static History *command_history_head = NULL;
static History *command_history_tail = NULL;
static History *command_history_pos = NULL;
/* hist_size: the current size of the command_history array */
static int hist_size = 0;
/* hist_count: the absolute counter for the history list */
static int hist_count = 0;
/*
* last_dir: the direction (next or previous) of the last get_from_history()
* call.... reset by add to history
*/
static int last_dir = -1;
/*
* history pointer
*/
static History *tmp = NULL;
/*
* history_match: using wild_match(), this finds the latest match in the
* history file and returns it as the function result. Returns null if there
* is no match. Note that this sticks a '*' at the end if one is not already
* there.
*/
static char *history_match(char *match)
{
char *ptr;
char *match_str = NULL;
if (*(match + strlen(match) - 1) == '*')
malloc_strcpy(&match_str, match);
else {
match_str = (char *) new_malloc(strlen(match) + 2);
strcpy(match_str, match);
strcat(match_str, "*");
}
if (get_int_var(HISTORY_VAR)) {
if ((last_dir == -1) || (tmp == (History *) NULL))
tmp = command_history_head;
else
tmp = tmp->next;
for (; tmp; tmp = tmp->next) {
ptr = tmp->stuff;
while (ptr && strchr(get_string_var(CMDCHARS_VAR), *ptr))
ptr++;
if (wild_match(match_str, ptr)) {
new_free(&match_str);
last_dir = PREV;
return (tmp->stuff);
}
}
}
last_dir = -1;
new_free(&match_str);
return NULL;
}
/* shove_to_history: a key binding that saves the current line into
* the history and then deletes the whole line. Useful for when you
* are in the middle of a big line and need to "get out" to do something
* else quick for just a second, and you dont want to have to retype
* everything all over again
*/
extern void shove_to_history(char unused, char *not_used)
{
add_to_history(get_input());
input_clear_line(unused, not_used);
}
static void add_to_history_list(int cnt, char *stuff)
{
History *new;
if (get_int_var(HISTORY_VAR) == 0)
return;
if ((hist_size == get_int_var(HISTORY_VAR)) && command_history_tail) {
if (hist_size == 1) {
malloc_strcpy(&command_history_tail->stuff, stuff);
return;
}
new = command_history_tail;
command_history_tail = command_history_tail->prev;
command_history_tail->next = NULL;
new_free(&new->stuff);
new_free((char **) &new);
if (command_history_tail == NULL)
command_history_head = NULL;
} else
hist_size++;
new = (History *) new_malloc(sizeof(History));
new->stuff = NULL;
new->number = cnt;
new->next = command_history_head;
new->prev = NULL;
malloc_strcpy(&(new->stuff), stuff);
if (command_history_head)
command_history_head->prev = new;
command_history_head = new;
if (command_history_tail == NULL)
command_history_tail = new;
command_history_pos = NULL;
}
/*
* set_history_size: adjusts the size of the command_history to be size. If
* the array is not yet allocated, it is set to size and all the entries
* nulled. If it exists, it is resized to the new size with a realloc. Any
* new entries are nulled.
*/
void set_history_size(Window * win, char *unused, int size)
{
int i, cnt;
History *ptr;
if (size < hist_size) {
cnt = hist_size - size;
for (i = 0; i < cnt; i++) {
ptr = command_history_tail;
command_history_tail = ptr->prev;
new_free(&(ptr->stuff));
new_free((char **) &ptr);
}
if (command_history_tail == NULL)
command_history_head = NULL;
else
command_history_tail->next = NULL;
hist_size = size;
}
}
/*
* add_to_history: adds the given line to the history array. The history
* array is a circular buffer, and add_to_history handles all that stuff. It
* automagically allocted and deallocated memory as needed
*/
void add_to_history(char *line)
{
char *ptr;
if (line && *line) {
while (line) {
if ((ptr = sindex(line, "\n\r")) != NULL)
*(ptr++) = '\0';
add_to_history_list(hist_count, line);
last_dir = PREV;
hist_count++;
line = ptr;
}
}
}
static char *get_from_history_buffer(int which)
{
if ((get_int_var(HISTORY_VAR) == 0) || (hist_size == 0))
return NULL;
/*
* if (last_dir != which) { last_dir = which; get_from_history(which); }
*/
if (which == NEXT) {
if (command_history_pos) {
if (command_history_pos->prev)
command_history_pos = command_history_pos->prev;
else
command_history_pos = command_history_tail;
} else {
add_to_history(get_input());
command_history_pos = command_history_tail;
}
return (command_history_pos->stuff);
} else {
if (command_history_pos) {
if (command_history_pos->next)
command_history_pos = command_history_pos->next;
else
command_history_pos = command_history_head;
} else {
add_to_history(get_input());
command_history_pos = command_history_head;
}
return (command_history_pos->stuff);
}
}
/*
* get_history: gets the next history entry, either the PREV entry or the
* NEXT entry, and sets it to the current input string
*/
extern void get_history(int which)
{
char *ptr;
if ((ptr = get_from_history(which)) != NULL) {
set_input(ptr);
update_input(UPDATE_ALL);
}
}
char *get_from_history(int which)
{
return (get_from_history_buffer(which));
}
/* history: the /HISTORY command, shows the command history buffer. */
void cmd_history(struct command *cmd, char *args)
{
int cnt, max;
char *value;
History *tmp;
if (get_int_var(HISTORY_VAR)) {
say("Command History:");
if ((value = next_arg(args, &args)) != NULL) {
if ((max = my_atol(value)) > get_int_var(HISTORY_VAR))
max = get_int_var(HISTORY_VAR);
} else
max = get_int_var(HISTORY_VAR);
for (tmp = command_history_tail, cnt = 0; tmp && (cnt < max); tmp = tmp->prev, cnt++)
put_it("%d: %s", tmp->number, tmp->stuff);
}
}
/*
* do_history: This finds the given history entry in either the history file,
* or the in memory history buffer (if no history file is given). It then
* returns the found entry as its function value, or null if the entry is not
* found for some reason. Note that this routine mallocs the string returned
*/
char *do_history(char *com, char *rest)
{
History *tmp;
int hist_num;
char *ptr, *ret = NULL;
static char *last_com = NULL;
if (!com || !*com) {
if (last_com)
com = last_com;
else
com = empty_str;
} else
malloc_strcpy(&last_com, com);
if (!is_number(com)) {
if ((ptr = history_match(com)) != NULL) {
if (rest && *rest)
ret = m_sprintf("%s %s", ptr, rest);
else
malloc_strcpy(&ret, ptr);
} else
say("No Match");
return (ret);
}
hist_num = my_atol(com);
for (tmp = command_history_head; tmp; tmp = tmp->next) {
if (tmp->number == hist_num) {
if (rest && *rest)
ret = m_sprintf("%s %s", tmp->stuff, rest);
else
malloc_strcpy(&ret, tmp->stuff);
return (ret);
}
}
say("No such history entry: %d", hist_num);
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1