diff options
author | 2006-10-08 23:50:46 +1000 | |
---|---|---|
committer | 2006-10-08 23:50:46 +1000 | |
commit | 179f575593bf56a95bfabcd2601507bbd27b2df8 (patch) | |
tree | fa850d22a30a76ffb4d5410dd0252185bf8f7cbf /history.c | |
parent | 6400b60bdd8f10f8922643da8e8d235fb8874404 (diff) |
Update history loading/saving to handle multiline editing
darcs-hash:20061008135046-ac50b-e830fe313e23f632b9c645227f41c49c8ce600f4.gz
Diffstat (limited to 'history.c')
-rw-r--r-- | history.c | 197 |
1 files changed, 181 insertions, 16 deletions
@@ -104,6 +104,11 @@ static int is_loaded=0; static ll_node_t *unused = 0; +/** + Create a new ll_node_t struct. The struct is allocated using + halloc, and will be automatically free'd on shutdown, but not + before. +*/ static ll_node_t *history_create_node() { ll_node_t *res; @@ -119,6 +124,10 @@ static ll_node_t *history_create_node() } +/** + Return a ll_node_t struct to the pool of unused such structs, so + that it can later be reused by a call to history_create_node(). +*/ static void history_free_node( ll_node_t *n ) { n->next = unused; @@ -126,13 +135,178 @@ static void history_free_node( ll_node_t *n ) } /** + 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; +} + +/** + Read a complete histor entry from the specified FILE. +*/ +static wchar_t *history_load_entry( FILE *in ) +{ + int was_backslash = 0; + static string_buffer_t *out = 0; + int first_char = 1; + int ignore = 0; + + if( !out ) + { + out = sb_halloc( global_context ); + if( !out ) + { + DIE_MEM(); + } + } + else + { + sb_clear( out ); + } + + while( 1 ) + { + wint_t c; + + c = getwc( in ); + + + if( errno == EILSEQ ) + { + getc( in ); + continue; + } + + if( c == WEOF ) + break; + + + if( c == L'\n' ) + { + if( ignore ) + { + ignore = 0; + continue; + } + if( !was_backslash ) + break; + } + + if( first_char ) + { + if( c == L'#' ) + ignore = 1; + } + + first_char = 0; + + if( !ignore ) + sb_append_char( out, c ); + + was_backslash = ( (c == L'\\') && !was_backslash); + + } + + return (wchar_t *)out->buff; +} + + +/** Load history from file */ static int history_load() { wchar_t *fn; wchar_t *buff=0; - int buff_len=0; FILE *in_stream; hash_table_t used; int res = 0; @@ -158,18 +332,7 @@ static int history_load() { while( !feof( in_stream ) ) { - int buff_read = fgetws2( &buff, &buff_len, in_stream ); - if( buff_read == -1 ) - { - debug( 1, L"The following non-fatal error occurred while reading command history from \'%ls\':", mode_name ); - wperror( L"fgetws2" ); - fclose( in_stream ); - free( fn ); - free( buff ); - signal_unblock(); - return -1; - } - + buff = history_unescape_newlines(history_load_entry( in_stream )); /* We do not call history_add here, since that would make history_load() take quadratic time, and may be @@ -206,7 +369,7 @@ static int history_load() { if( errno != ENOENT ) { - debug( 1, L"The following non-fatal error occurred while reading command history from \'%ls\':", mode_name ); + debug( 1, _(L"The following non-fatal error occurred while reading command history from \'%ls\':"), mode_name ); wperror( L"fopen" ); res = -1; @@ -217,7 +380,6 @@ static int history_load() hash_destroy( &used ); - free( buff ); free( fn ); last_loaded = history_last; signal_unblock(); @@ -309,10 +471,13 @@ void history_set_mode( wchar_t *name ) */ static void history_save_node( ll_node_t *n, FILE *out ) { + wchar_t *escaped; if( n==0 ) return; history_save_node( n->prev, out ); - fwprintf(out, L"%ls\n", (wchar_t *)(n->data) ); + + escaped = history_escape_newlines( (wchar_t *)(n->data) ); + fwprintf(out, L"#\n%ls\n", escaped ); } /** |