2005-09-20 23:26:39 +10:00
/** \file history.c
History functions , part of the user interface .
*/
2006-08-11 11:18:35 +10:00
# include "config.h"
2005-09-20 23:26:39 +10:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <errno.h>
# include <dirent.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2006-10-21 08:36:49 +10:00
# include <sys/mman.h>
# include <fcntl.h>
# include <string.h>
# include <time.h>
# include <assert.h>
2005-09-20 23:26:39 +10:00
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "wutil.h"
# include "history.h"
# include "common.h"
2006-09-08 22:34:55 +10:00
# include "halloc.h"
# include "halloc_util.h"
2006-10-21 08:36:49 +10:00
# include "intern.h"
2006-10-19 21:50:23 +10:00
# include "path.h"
2005-09-20 23:26:39 +10:00
/*
2006-10-21 08:36:49 +10:00
# include "reader.h"
# include "env.h"
# include "sanity.h"
# include "signal.h"
2005-09-20 23:26:39 +10:00
*/
2006-10-21 08:36:49 +10:00
# define WIDE_MODE 0
# define NARROW_MODE 1
2005-09-20 23:26:39 +10:00
/**
2006-10-21 08:36:49 +10:00
Interval in seconds between automatic history save
2005-09-20 23:26:39 +10:00
*/
2006-10-21 08:36:49 +10:00
# define SAVE_INTERVAL (5*60)
2005-09-20 23:26:39 +10:00
2006-02-07 01:08:10 +10:00
/**
2006-10-21 08:36:49 +10:00
Number of new history entries to add before automatic history save
2006-02-07 01:08:10 +10:00
*/
2006-10-21 08:36:49 +10:00
# define SAVE_COUNT 5
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
typedef struct
2006-09-08 22:34:55 +10:00
{
2006-10-21 08:36:49 +10:00
const wchar_t * name ;
array_list_t item ;
int pos ;
int has_loaded ;
char * mmap_start ;
size_t mmap_length ;
array_list_t used ;
time_t save_timestamp ;
int new_count ;
void * item_context ;
2006-09-08 22:34:55 +10:00
}
2006-10-21 08:36:49 +10:00
history_mode_t ;
2006-09-08 22:34:55 +10:00
2006-10-21 08:36:49 +10:00
typedef struct
2006-09-08 22:34:55 +10:00
{
2006-10-21 08:36:49 +10:00
wchar_t * data ;
time_t timestamp ;
2006-09-08 22:34:55 +10:00
}
2006-10-21 08:36:49 +10:00
item_t ;
static hash_table_t * mode_table = 0 ;
static history_mode_t * current_mode = 0 ;
2006-09-08 22:34:55 +10:00
2006-10-08 23:50:46 +10:00
/**
Add backslashes to all newlines , so that the returning string is
suitable for writing to the history file . The memory for the return
value will be reused by subsequent calls to this function .
*/
static wchar_t * history_escape_newlines ( wchar_t * in )
{
static string_buffer_t * out = 0 ;
if ( ! out )
{
out = sb_halloc ( global_context ) ;
if ( ! out )
{
DIE_MEM ( ) ;
}
}
else
{
sb_clear ( out ) ;
}
for ( ; * in ; in + + )
{
if ( * in = = L ' \\ ' )
{
sb_append_char ( out , * in ) ;
if ( * ( in + 1 ) )
{
in + + ;
sb_append_char ( out , * in ) ;
}
else
{
/*
This is a weird special case . When we are trying to
save a string that ends with a backslash , we need to
handle it specially , otherwise this command would be
combined with the one following it . We hack around
this by adding an additional newline .
*/
sb_append_char ( out , L ' \n ' ) ;
}
}
else if ( * in = = L ' \n ' )
{
sb_append_char ( out , L ' \\ ' ) ;
sb_append_char ( out , * in ) ;
}
else
{
sb_append_char ( out , * in ) ;
}
}
return ( wchar_t * ) out - > buff ;
}
/**
Remove backslashes from all newlines . This makes a string from the
history file better formated for on screen display . The memory for
the return value will be reused by subsequent calls to this
function .
*/
static wchar_t * history_unescape_newlines ( wchar_t * in )
{
static string_buffer_t * out = 0 ;
if ( ! out )
{
out = sb_halloc ( global_context ) ;
if ( ! out )
{
DIE_MEM ( ) ;
}
}
else
{
sb_clear ( out ) ;
}
for ( ; * in ; in + + )
{
if ( * in = = L ' \\ ' )
{
if ( * ( in + 1 ) ! = L ' \n ' )
{
sb_append_char ( out , * in ) ;
}
}
else
{
sb_append_char ( out , * in ) ;
}
}
return ( wchar_t * ) out - > buff ;
}
2006-10-21 08:36:49 +10:00
static int item_is_new ( history_mode_t * m , void * d )
{
char * begin = ( char * ) d ;
2006-10-20 01:36:03 +10:00
2006-10-21 08:36:49 +10:00
if ( ! m - > has_loaded | | ! m - > mmap_start | | ( m - > mmap_start = = MAP_FAILED ) )
{
return 1 ;
}
2006-10-08 23:50:46 +10:00
2006-10-21 08:36:49 +10:00
if ( ( begin < m - > mmap_start ) | | ( begin > ( m - > mmap_start + m - > mmap_length ) ) )
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
return 1 ;
2006-10-08 23:50:46 +10:00
}
2006-10-21 08:36:49 +10:00
return 0 ;
}
static item_t * item_get ( history_mode_t * m , void * d )
{
char * begin = ( char * ) d ;
if ( item_is_new ( m , d ) )
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
return ( item_t * ) d ;
2006-10-08 23:50:46 +10:00
}
2006-10-21 08:36:49 +10:00
else
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
char * end = m - > mmap_start + m - > mmap_length ;
char * pos = begin ;
int was_backslash = 0 ;
static string_buffer_t * out = 0 ;
static item_t narrow_item ;
int first_char = 1 ;
int timestamp_mode = 0 ;
narrow_item . timestamp = 0 ;
if ( ! out )
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
out = sb_halloc ( global_context ) ;
if ( ! out )
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
DIE_MEM ( ) ;
2006-10-08 23:50:46 +10:00
}
}
2006-10-21 08:36:49 +10:00
else
2006-10-08 23:50:46 +10:00
{
2006-10-21 08:36:49 +10:00
sb_clear ( out ) ;
2006-10-08 23:50:46 +10:00
}
2006-10-21 08:36:49 +10:00
while ( 1 )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
wchar_t c ;
mbstate_t state ;
size_t res ;
memset ( & state , 0 , sizeof ( state ) ) ;
res = mbrtowc ( & c , pos , end - pos , & state ) ;
if ( res = = ( size_t ) - 1 )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
pos + + ;
continue ;
}
else if ( res = = ( size_t ) - 2 )
{
break ;
}
else if ( res = = ( size_t ) 0 )
{
pos + + ;
continue ;
}
pos + = res ;
if ( c = = L ' \n ' )
{
if ( timestamp_mode )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
wchar_t * time_string = ( wchar_t * ) out - > buff ;
while ( * time_string & & ! iswdigit ( * time_string ) )
time_string + + ;
errno = 0 ;
if ( * time_string )
{
time_t tm = ( time_t ) wcstol ( time_string , 0 , 10 ) ;
if ( tm & & ! errno )
{
narrow_item . timestamp = tm ;
}
}
sb_clear ( out ) ;
timestamp_mode = 0 ;
continue ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
if ( ! was_backslash )
break ;
}
if ( first_char )
{
if ( c = = L ' # ' )
timestamp_mode = 1 ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
first_char = 0 ;
sb_append_char ( out , c ) ;
was_backslash = ( ( c = = L ' \\ ' ) & & ! was_backslash ) ;
2006-06-22 10:12:28 +10:00
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
narrow_item . data = history_unescape_newlines ( ( wchar_t * ) out - > buff ) ;
return & narrow_item ;
2006-10-19 21:50:23 +10:00
}
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static void item_write ( FILE * f , history_mode_t * m , void * v )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
item_t * i = item_get ( m , v ) ;
fwprintf ( f , L " # %d \n %ls \n " , i - > timestamp , history_escape_newlines ( i - > data ) ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static void history_destroy_mode ( wchar_t * name , history_mode_t * m )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
halloc_free ( m - > item_context ) ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
if ( m - > mmap_start & & ( m - > mmap_start ! = MAP_FAILED ) )
{
munmap ( m - > mmap_start , m - > mmap_length ) ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
halloc_free ( m ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static history_mode_t * history_create_mode ( const wchar_t * name )
{
history_mode_t * new_mode = halloc ( 0 , sizeof ( history_mode_t ) ) ;
new_mode - > name = intern ( name ) ;
al_init ( & new_mode - > item ) ;
al_init ( & new_mode - > used ) ;
2006-06-16 02:03:27 +10:00
2006-10-21 08:36:49 +10:00
halloc_register_function ( new_mode , ( void ( * ) ( void * ) ) & al_destroy , & new_mode - > item ) ;
halloc_register_function ( new_mode , ( void ( * ) ( void * ) ) & al_destroy , & new_mode - > used ) ;
new_mode - > pos = 0 ;
new_mode - > has_loaded = 0 ;
new_mode - > mmap_start = 0 ;
new_mode - > mmap_length = 0 ;
new_mode - > save_timestamp = time ( 0 ) ;
new_mode - > new_count = 0 ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
new_mode - > item_context = halloc ( 0 , 0 ) ;
return new_mode ;
2005-09-20 23:26:39 +10:00
}
/**
2006-10-21 08:36:49 +10:00
This function tests if the search string is a match for the given string
2005-09-20 23:26:39 +10:00
*/
2006-10-21 08:36:49 +10:00
static int history_test ( const wchar_t * needle , const wchar_t * haystack )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
return ! ! wcsstr ( haystack , needle ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static wchar_t * history_filename ( void * context , const wchar_t * name , const wchar_t * suffix )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
wchar_t * path ;
wchar_t * res ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
if ( ! current_mode )
return 0 ;
2006-02-07 01:08:10 +10:00
2006-10-21 08:36:49 +10:00
path = path_get_config ( context ) ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
if ( ! path )
return 0 ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
res = wcsdupcat2 ( path , L " / " , name , L " _history " , suffix ? suffix : ( void * ) 0 , ( void * ) 0 ) ;
halloc_register_function ( context , & free , res ) ;
return res ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
static void history_populate_from_mmap ( history_mode_t * m )
{
char * begin = m - > mmap_start ;
char * end = begin + m - > mmap_length ;
char * pos ;
array_list_t tmp ;
int ignore_newline = 0 ;
int do_push = 1 ;
al_init ( & tmp ) ;
al_push_all ( & tmp , & m - > item ) ;
al_truncate ( & m - > item , 0 ) ;
for ( pos = begin ; pos < end ; pos + + )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
if ( do_push )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
ignore_newline = * pos = = ' # ' ;
al_push ( & m - > item , pos ) ;
// debug( 0, L"Push item at offset %d", pos-begin );
do_push = 0 ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
switch ( * pos )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
case ' \\ ' :
2006-09-08 22:34:55 +10:00
{
2006-10-21 08:36:49 +10:00
pos + + ;
break ;
}
case ' \n ' :
{
if ( ignore_newline )
2006-10-19 21:50:23 +10:00
{
2006-10-21 08:36:49 +10:00
ignore_newline = 0 ;
2006-10-19 21:50:23 +10:00
}
else
2006-09-08 22:34:55 +10:00
{
2006-10-21 08:36:49 +10:00
do_push = 1 ;
}
break ;
2006-09-08 22:34:55 +10:00
}
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
}
2006-06-22 10:12:28 +10:00
2006-10-21 08:36:49 +10:00
m - > pos + = al_get_count ( & m - > item ) ;
al_push_all ( & m - > item , & tmp ) ;
al_destroy ( & tmp ) ;
// debug( 0, L"History has %d items", al_get_count( &m->item ));
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static void history_load ( history_mode_t * m )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
int fd ;
int ok = 0 ;
2006-10-05 07:42:04 +10:00
2006-10-21 08:36:49 +10:00
void * context ;
wchar_t * filename ;
if ( ! m )
return ;
2006-10-05 07:42:04 +10:00
2006-10-21 08:36:49 +10:00
context = halloc ( 0 , 0 ) ;
filename = history_filename ( context , m - > name , 0 ) ;
if ( filename )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
if ( ( fd = wopen ( filename , O_RDONLY ) ) > 0 )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
off_t len = lseek ( fd , 0 , SEEK_END ) ;
if ( len ! = ( off_t ) - 1 )
{
m - > mmap_length = ( size_t ) len ;
if ( lseek ( fd , 0 , SEEK_SET ) = = 0 )
{
if ( ( m - > mmap_start = mmap ( 0 , m - > mmap_length , PROT_READ , MAP_SHARED , fd , 0 ) ) ! = MAP_FAILED )
{
ok = 1 ;
// debug( 0, L"mmap ok" );
history_populate_from_mmap ( m ) ;
}
}
}
close ( fd ) ;
}
2005-09-20 23:26:39 +10:00
}
2006-06-16 02:03:27 +10:00
2006-10-21 08:36:49 +10:00
if ( ! ok )
{
// debug( 0, L"Could not load history file" );
}
m - > has_loaded = 1 ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
halloc_free ( context ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
static int hash_item_func ( void * v )
{
item_t * i = ( item_t * ) v ;
return i - > timestamp ^ hash_wcs_func ( i - > data ) ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
static int hash_item_cmp ( void * v1 , void * v2 )
{
item_t * i1 = ( item_t * ) v1 ;
item_t * i2 = ( item_t * ) v2 ;
return ( i1 - > timestamp = = i2 - > timestamp ) & & ( wcscmp ( i1 - > data , i2 - > data ) = = 0 ) ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
static void history_save_mode ( void * n , history_mode_t * m )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
FILE * out ;
void * context ;
history_mode_t * on_disk ;
int i ;
int has_new ;
wchar_t * tmp_name ;
/*
First check if there are any new entries to save . If not , thenm we can just return
*/
for ( i = 0 ; i < al_get_count ( & m - > item ) ; i + + )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
void * ptr = al_get ( & m - > item , i ) ;
has_new = item_is_new ( m , ptr ) ;
if ( has_new )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
break ;
2006-06-22 10:12:28 +10:00
}
}
2006-10-21 08:36:49 +10:00
if ( ! has_new )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
return ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
/*
Set up on_disk variable to describe the current contents of the history file
*/
on_disk = history_create_mode ( m - > name ) ;
history_load ( on_disk ) ;
context = halloc ( 0 , 0 ) ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
tmp_name = history_filename ( context , m - > name , L " .tmp " ) ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
if ( tmp_name )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
tmp_name = wcsdup ( tmp_name ) ;
if ( ( out = wfopen ( history_filename ( context , m - > name , L " .tmp " ) , " w " ) ) )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
hash_table_t mine ;
hash_init ( & mine , & hash_item_func , & hash_item_cmp ) ;
for ( i = 0 ; i < al_get_count ( & m - > item ) ; i + + )
{
void * ptr = al_get ( & m - > item , i ) ;
int is_new = item_is_new ( m , ptr ) ;
if ( is_new )
{
hash_put ( & mine , item_get ( m , ptr ) , L " " ) ;
}
2006-06-22 10:12:28 +10:00
}
2006-02-07 01:08:10 +10:00
2006-10-21 08:36:49 +10:00
/*
Re - save the old history
*/
for ( i = 0 ; i < al_get_count ( & on_disk - > item ) ; i + + )
{
void * ptr = al_get ( & on_disk - > item , i ) ;
item_t * i = item_get ( on_disk , ptr ) ;
// assert( i );
if ( ! hash_get ( & mine , i ) )
item_write ( out , on_disk , ptr ) ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
hash_destroy ( & mine ) ;
/*
Add our own items
*/
for ( i = 0 ; i < al_get_count ( & m - > item ) ; i + + )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
void * ptr = al_get ( & m - > item , i ) ;
int is_new = item_is_new ( m , ptr ) ;
if ( is_new )
item_write ( out , m , ptr ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
if ( fclose ( out ) )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
}
else
{
wrename ( tmp_name , history_filename ( context , m - > name , 0 ) ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
}
free ( tmp_name ) ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
history_destroy_mode ( 0 , on_disk ) ;
if ( m - > mmap_start & & ( m - > mmap_start ! = MAP_FAILED ) )
munmap ( m - > mmap_start , m - > mmap_length ) ;
halloc_free ( m - > item_context ) ;
m - > item_context = halloc ( 0 , 0 ) ;
al_truncate ( & m - > item , 0 ) ;
al_truncate ( & m - > used , 0 ) ;
m - > pos = 0 ;
m - > has_loaded = 0 ;
m - > mmap_start = 0 ;
m - > mmap_length = 0 ;
m - > save_timestamp = time ( 0 ) ;
m - > new_count = 0 ;
halloc_free ( context ) ;
}
void history_add ( const wchar_t * str )
{
item_t * i ;
if ( ! current_mode )
return ;
i = halloc ( global_context , sizeof ( item_t ) ) ;
i - > data = ( wchar_t * ) halloc_wcsdup ( current_mode - > item_context , str ) ;
i - > timestamp = time ( 0 ) ;
al_push ( & current_mode - > item , i ) ;
al_truncate ( & current_mode - > used , 0 ) ;
current_mode - > pos = al_get_count ( & current_mode - > item ) ;
current_mode - > new_count + + ;
if ( ( time ( 0 ) > current_mode - > save_timestamp + SAVE_INTERVAL ) | | ( current_mode - > new_count > = SAVE_COUNT ) )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
history_save_mode ( 0 , current_mode ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
2005-09-20 23:26:39 +10:00
}
/**
2006-10-21 08:36:49 +10:00
Check if the specified string has already been used a s a search match
2005-09-20 23:26:39 +10:00
*/
2006-10-21 08:36:49 +10:00
static int history_is_used ( const wchar_t * str )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
int i ;
int res = 0 ;
item_t * it = 0 ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
for ( i = 0 ; i < al_get_count ( & current_mode - > used ) ; i + + )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
long idx = al_get_long ( & current_mode - > used , i ) ;
it = item_get ( current_mode , al_get ( & current_mode - > item , ( int ) idx ) ) ;
if ( wcscmp ( it - > data , str ) = = 0 )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
res = 1 ;
break ;
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
return res ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
const wchar_t * history_prev_match ( const wchar_t * needle )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
if ( current_mode )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
if ( current_mode - > pos > 0 )
{
for ( current_mode - > pos - - ; current_mode - > pos > = 0 ; current_mode - > pos - - )
{
item_t * i = item_get ( current_mode , al_get ( & current_mode - > item , current_mode - > pos ) ) ;
wchar_t * haystack = ( wchar_t * ) i - > data ;
if ( history_test ( needle , haystack ) )
{
int is_used ;
/*
This is ugly . Whenever we call item_get ( ) ,
there is a chance that the return value of any
previous call to item_get will become
invalid . The history_is_used function uses the
istem_get ( ) function . Therefore , we must create
a copy of the haystack string , and if the string
is unused , we must call item_get anew .
*/
haystack = wcsdup ( haystack ) ;
is_used = history_is_used ( haystack ) ;
free ( haystack ) ;
if ( ! is_used )
{
i = item_get ( current_mode , al_get ( & current_mode - > item , current_mode - > pos ) ) ;
al_push_long ( & current_mode - > used , current_mode - > pos ) ;
return i - > data ;
}
}
}
}
if ( ! current_mode - > has_loaded )
{
history_load ( current_mode ) ;
return history_prev_match ( needle ) ;
}
else
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
current_mode - > pos = - 1 ;
if ( al_peek_long ( & current_mode - > used ) ! = - 1 )
al_push_long ( & current_mode - > used , - 1 ) ;
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
return needle ;
}
wchar_t * history_get ( int idx )
{
int len ;
2006-02-07 01:08:10 +10:00
2006-10-21 08:36:49 +10:00
if ( ! current_mode )
return 0 ;
len = al_get_count ( & current_mode - > item ) ;
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
if ( ( idx > = len ) & & ! current_mode - > has_loaded )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
history_load ( current_mode ) ;
len = al_get_count ( & current_mode - > item ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
if ( idx < 0 )
return 0 ;
if ( idx > = len )
return 0 ;
return item_get ( current_mode , al_get ( & current_mode - > item , len - 1 - idx ) ) - > data ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
void history_first ( )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
if ( current_mode )
{
if ( ! current_mode - > has_loaded )
{
history_load ( current_mode ) ;
}
current_mode - > pos = 0 ;
}
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
void history_reset ( )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
if ( current_mode )
{
current_mode - > pos = al_get_count ( & current_mode - > item ) ;
al_truncate ( & current_mode - > used , 0 ) ;
}
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
const wchar_t * history_next_match ( const wchar_t * needle )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
if ( current_mode )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
if ( al_get_count ( & current_mode - > used ) )
2006-06-22 10:12:28 +10:00
{
2006-10-21 08:36:49 +10:00
al_pop ( & current_mode - > used ) ;
if ( al_get_count ( & current_mode - > used ) )
{
current_mode - > pos = ( int ) al_peek_long ( & current_mode - > used ) ;
item_t * i = item_get ( current_mode , al_get ( & current_mode - > item , current_mode - > pos ) ) ;
return i - > data ;
}
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
current_mode - > pos = al_get_count ( & current_mode - > item ) ;
2006-06-22 10:12:28 +10:00
}
2006-10-21 08:36:49 +10:00
return needle ;
}
void history_set_mode ( const wchar_t * name )
{
if ( ! mode_table )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
mode_table = malloc ( sizeof ( hash_table_t ) ) ;
hash_init ( mode_table , & hash_wcs_func , & hash_wcs_cmp ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
current_mode = ( history_mode_t * ) hash_get ( mode_table , name ) ;
if ( ! current_mode )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
current_mode = history_create_mode ( name ) ;
hash_put ( mode_table , name , current_mode ) ;
}
2005-09-20 23:26:39 +10:00
}
2006-10-21 08:36:49 +10:00
void history_init ( )
2005-09-20 23:26:39 +10:00
{
2006-10-21 08:36:49 +10:00
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
void history_destroy ( )
{
hash_foreach ( mode_table , ( void ( * ) ( void * , void * ) ) & history_save_mode ) ;
hash_foreach ( mode_table , ( void ( * ) ( void * , void * ) ) & history_destroy_mode ) ;
hash_destroy ( mode_table ) ;
free ( mode_table ) ;
mode_table = 0 ;
}
2005-09-20 23:26:39 +10:00
2006-10-21 08:36:49 +10:00
void history_sanity_check ( )
{
2005-09-20 23:26:39 +10:00
}