123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- * 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.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #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;
- }
|