diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/hci/readline.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/hci/readline.c')
-rw-r--r-- | qemu/roms/ipxe/src/hci/readline.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/hci/readline.c b/qemu/roms/ipxe/src/hci/readline.c new file mode 100644 index 000000000..40aa59787 --- /dev/null +++ b/qemu/roms/ipxe/src/hci/readline.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ipxe/console.h> +#include <ipxe/keys.h> +#include <ipxe/editstring.h> +#include <readline/readline.h> + +/** @file + * + * Minimal readline + * + */ + +#define READLINE_MAX 256 + +/** + * Synchronise console with edited string + * + * @v string Editable string + */ +static void sync_console ( struct edit_string *string ) { + unsigned int mod_start = string->mod_start; + unsigned int mod_end = string->mod_end; + unsigned int cursor = string->last_cursor; + size_t len = strlen ( string->buf ); + + /* Expand region back to old cursor position if applicable */ + if ( mod_start > string->last_cursor ) + mod_start = string->last_cursor; + + /* Expand region forward to new cursor position if applicable */ + if ( mod_end < string->cursor ) + mod_end = string->cursor; + + /* Backspace to start of region */ + while ( cursor > mod_start ) { + putchar ( '\b' ); + cursor--; + } + + /* Print modified region */ + while ( cursor < mod_end ) { + putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] ); + cursor++; + } + + /* Backspace to new cursor position */ + while ( cursor > string->cursor ) { + putchar ( '\b' ); + cursor--; + } +} + +/** + * Locate history entry + * + * @v history History buffer + * @v depth Depth within history buffer + * @ret entry History entry + */ +static struct readline_history_entry * +history_entry ( struct readline_history *history, unsigned int depth ) { + unsigned int offset; + + offset = ( ( history->next - depth ) % + ( sizeof ( history->entries ) / + sizeof ( history->entries[0] ) ) ); + return &history->entries[offset]; +} + +/** + * Read string from history buffer + * + * @v history History buffer + * @v depth Depth within history buffer + * @ret string String + */ +static const char * history_fetch ( struct readline_history *history, + unsigned int depth ) { + struct readline_history_entry *entry; + + /* Return the temporary copy if it exists, otherwise return + * the persistent copy. + */ + entry = history_entry ( history, depth ); + return ( entry->temp ? entry->temp : entry->string ); +} + +/** + * Write temporary string copy to history buffer + * + * @v history History buffer + * @v depth Depth within history buffer + * @v string String + */ +static void history_store ( struct readline_history *history, + unsigned int depth, const char *string ) { + struct readline_history_entry *entry; + char *temp; + + /* Create temporary copy of string */ + temp = strdup ( string ); + if ( ! temp ) { + /* Just discard the string; there's nothing we can do */ + DBGC ( history, "READLINE %p could not store string\n", + history ); + return; + } + + /* Store temporary copy */ + entry = history_entry ( history, depth ); + free ( entry->temp ); + entry->temp = temp; +} + +/** + * Move to new history depth + * + * @v history History buffer + * @v offset Offset by which to change depth + * @v old_string String (possibly modified) at current depth + * @ret new_string String at new depth, or NULL for no movement + */ +static const char * history_move ( struct readline_history *history, + int offset, const char *old_string ) { + unsigned int new_depth = ( history->depth + offset ); + const char * new_string = history_fetch ( history, new_depth ); + + /* Depth checks */ + if ( new_depth > READLINE_HISTORY_MAX_DEPTH ) + return NULL; + if ( ! new_string ) + return NULL; + + /* Store temporary copy of old string at current depth */ + history_store ( history, history->depth, old_string ); + + /* Update depth */ + history->depth = new_depth; + + /* Return new string */ + return new_string; +} + +/** + * Append new history entry + * + * @v history History buffer + * @v string String + */ +static void history_append ( struct readline_history *history, + const char *string ) { + struct readline_history_entry *entry; + + /* Store new entry */ + entry = history_entry ( history, 0 ); + assert ( entry->string == NULL ); + entry->string = strdup ( string ); + if ( ! entry->string ) { + /* Just discard the string; there's nothing we can do */ + DBGC ( history, "READLINE %p could not append string\n", + history ); + return; + } + + /* Increment history position */ + history->next++; + + /* Prepare empty "next" slot */ + entry = history_entry ( history, 0 ); + free ( entry->string ); + entry->string = NULL; +} + +/** + * Clean up history after editing + * + * @v history History buffer + */ +static void history_cleanup ( struct readline_history *history ) { + struct readline_history_entry *entry; + unsigned int i; + + /* Discard any temporary strings */ + for ( i = 0 ; i < ( sizeof ( history->entries ) / + sizeof ( history->entries[0] ) ) ; i++ ) { + entry = &history->entries[i]; + free ( entry->temp ); + entry->temp = NULL; + } + + /* Reset depth */ + history->depth = 0; + + /* Sanity check */ + entry = history_entry ( history, 0 ); + assert ( entry->string == NULL ); +} + +/** + * Free history buffer + * + * @v history History buffer + */ +void history_free ( struct readline_history *history ) { + struct readline_history_entry *entry; + unsigned int i; + + /* Discard any temporary strings */ + for ( i = 0 ; i < ( sizeof ( history->entries ) / + sizeof ( history->entries[0] ) ) ; i++ ) { + entry = &history->entries[i]; + assert ( entry->temp == NULL ); + free ( entry->string ); + } +} + +/** + * Read line from console (with history) + * + * @v prompt Prompt string + * @v prefill Prefill string, or NULL for no prefill + * @v history History buffer, or NULL for no history + * @ret line Line read from console (excluding terminating newline) + * @ret rc Return status code + * + * The returned line is allocated with malloc(); the caller must + * eventually call free() to release the storage. + */ +int readline_history ( const char *prompt, const char *prefill, + struct readline_history *history, char **line ) { + char buf[READLINE_MAX]; + struct edit_string string; + int key; + int move_by; + const char *new_string; + int rc; + + /* Avoid returning uninitialised data on error */ + *line = NULL; + + /* Display prompt, if applicable */ + if ( prompt ) + printf ( "%s", prompt ); + + /* Ensure cursor is visible */ + printf ( "\033[?25h" ); + + /* Initialise editable string */ + memset ( &string, 0, sizeof ( string ) ); + init_editstring ( &string, buf, sizeof ( buf ) ); + buf[0] = '\0'; + + /* Prefill string, if applicable */ + if ( prefill ) { + replace_string ( &string, prefill ); + sync_console ( &string ); + } + + while ( 1 ) { + /* Handle keypress */ + key = edit_string ( &string, getkey ( 0 ) ); + sync_console ( &string ); + move_by = 0; + switch ( key ) { + case CR: + case LF: + *line = strdup ( buf ); + rc = ( ( *line ) ? 0 : -ENOMEM ); + goto done; + case CTRL_C: + rc = -ECANCELED; + goto done; + case KEY_UP: + move_by = 1; + break; + case KEY_DOWN: + move_by = -1; + break; + default: + /* Do nothing */ + break; + } + + /* Handle history movement, if applicable */ + if ( move_by && history ) { + new_string = history_move ( history, move_by, buf ); + if ( new_string ) { + replace_string ( &string, new_string ); + sync_console ( &string ); + } + } + } + + done: + putchar ( '\n' ); + if ( history ) { + if ( *line && (*line)[0] ) + history_append ( history, *line ); + history_cleanup ( history ); + } + assert ( ( rc == 0 ) ^ ( *line == NULL ) ); + return rc; +} + +/** + * Read line from console + * + * @v prompt Prompt string + * @ret line Line read from console (excluding terminating newline) + * + * The returned line is allocated with malloc(); the caller must + * eventually call free() to release the storage. + */ +char * readline ( const char *prompt ) { + char *line; + + readline_history ( prompt, NULL, NULL, &line ); + return line; +} |