|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2006 Michael Brown <[email protected]>. ! 3: * ! 4: * This program is free software; you can redistribute it and/or ! 5: * modify it under the terms of the GNU General Public License as ! 6: * published by the Free Software Foundation; either version 2 of the ! 7: * License, or any later version. ! 8: * ! 9: * This program is distributed in the hope that it will be useful, but ! 10: * WITHOUT ANY WARRANTY; without even the implied warranty of ! 11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 12: * General Public License for more details. ! 13: * ! 14: * You should have received a copy of the GNU General Public License ! 15: * along with this program; if not, write to the Free Software ! 16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: #include <stdio.h> ! 22: #include <string.h> ! 23: #include <stdlib.h> ! 24: #include <ipxe/console.h> ! 25: #include <ipxe/keys.h> ! 26: #include <ipxe/editstring.h> ! 27: #include <readline/readline.h> ! 28: ! 29: /** @file ! 30: * ! 31: * Minimal readline ! 32: * ! 33: */ ! 34: ! 35: #define READLINE_MAX 256 ! 36: ! 37: /** ! 38: * Synchronise console with edited string ! 39: * ! 40: * @v string Editable string ! 41: */ ! 42: static void sync_console ( struct edit_string *string ) { ! 43: unsigned int mod_start = string->mod_start; ! 44: unsigned int mod_end = string->mod_end; ! 45: unsigned int cursor = string->last_cursor; ! 46: size_t len = strlen ( string->buf ); ! 47: ! 48: /* Expand region back to old cursor position if applicable */ ! 49: if ( mod_start > string->last_cursor ) ! 50: mod_start = string->last_cursor; ! 51: ! 52: /* Expand region forward to new cursor position if applicable */ ! 53: if ( mod_end < string->cursor ) ! 54: mod_end = string->cursor; ! 55: ! 56: /* Backspace to start of region */ ! 57: while ( cursor > mod_start ) { ! 58: putchar ( '\b' ); ! 59: cursor--; ! 60: } ! 61: ! 62: /* Print modified region */ ! 63: while ( cursor < mod_end ) { ! 64: putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] ); ! 65: cursor++; ! 66: } ! 67: ! 68: /* Backspace to new cursor position */ ! 69: while ( cursor > string->cursor ) { ! 70: putchar ( '\b' ); ! 71: cursor--; ! 72: } ! 73: } ! 74: ! 75: /** ! 76: * Locate history entry ! 77: * ! 78: * @v history History buffer ! 79: * @v depth Depth within history buffer ! 80: * @ret entry History entry ! 81: */ ! 82: static struct readline_history_entry * ! 83: history_entry ( struct readline_history *history, unsigned int depth ) { ! 84: unsigned int offset; ! 85: ! 86: offset = ( ( history->next - depth ) % ! 87: ( sizeof ( history->entries ) / ! 88: sizeof ( history->entries[0] ) ) ); ! 89: return &history->entries[offset]; ! 90: } ! 91: ! 92: /** ! 93: * Read string from history buffer ! 94: * ! 95: * @v history History buffer ! 96: * @v depth Depth within history buffer ! 97: * @ret string String ! 98: */ ! 99: static const char * history_fetch ( struct readline_history *history, ! 100: unsigned int depth ) { ! 101: struct readline_history_entry *entry; ! 102: ! 103: /* Return the temporary copy if it exists, otherwise return ! 104: * the persistent copy. ! 105: */ ! 106: entry = history_entry ( history, depth ); ! 107: return ( entry->temp ? entry->temp : entry->string ); ! 108: } ! 109: ! 110: /** ! 111: * Write temporary string copy to history buffer ! 112: * ! 113: * @v history History buffer ! 114: * @v depth Depth within history buffer ! 115: * @v string String ! 116: */ ! 117: static void history_store ( struct readline_history *history, ! 118: unsigned int depth, const char *string ) { ! 119: struct readline_history_entry *entry; ! 120: char *temp; ! 121: ! 122: /* Create temporary copy of string */ ! 123: temp = strdup ( string ); ! 124: if ( ! temp ) { ! 125: /* Just discard the string; there's nothing we can do */ ! 126: DBGC ( history, "READLINE %p could not store string\n", ! 127: history ); ! 128: return; ! 129: } ! 130: ! 131: /* Store temporary copy */ ! 132: entry = history_entry ( history, depth ); ! 133: free ( entry->temp ); ! 134: entry->temp = temp; ! 135: } ! 136: ! 137: /** ! 138: * Move to new history depth ! 139: * ! 140: * @v history History buffer ! 141: * @v offset Offset by which to change depth ! 142: * @v old_string String (possibly modified) at current depth ! 143: * @ret new_string String at new depth, or NULL for no movement ! 144: */ ! 145: static const char * history_move ( struct readline_history *history, ! 146: int offset, const char *old_string ) { ! 147: unsigned int new_depth = ( history->depth + offset ); ! 148: const char * new_string = history_fetch ( history, new_depth ); ! 149: ! 150: /* Depth checks */ ! 151: if ( new_depth > READLINE_HISTORY_MAX_DEPTH ) ! 152: return NULL; ! 153: if ( ! new_string ) ! 154: return NULL; ! 155: ! 156: /* Store temporary copy of old string at current depth */ ! 157: history_store ( history, history->depth, old_string ); ! 158: ! 159: /* Update depth */ ! 160: history->depth = new_depth; ! 161: ! 162: /* Return new string */ ! 163: return new_string; ! 164: } ! 165: ! 166: /** ! 167: * Append new history entry ! 168: * ! 169: * @v history History buffer ! 170: * @v string String ! 171: */ ! 172: static void history_append ( struct readline_history *history, ! 173: const char *string ) { ! 174: struct readline_history_entry *entry; ! 175: ! 176: /* Store new entry */ ! 177: entry = history_entry ( history, 0 ); ! 178: assert ( entry->string == NULL ); ! 179: entry->string = strdup ( string ); ! 180: if ( ! entry->string ) { ! 181: /* Just discard the string; there's nothing we can do */ ! 182: DBGC ( history, "READLINE %p could not append string\n", ! 183: history ); ! 184: return; ! 185: } ! 186: ! 187: /* Increment history position */ ! 188: history->next++; ! 189: ! 190: /* Prepare empty "next" slot */ ! 191: entry = history_entry ( history, 0 ); ! 192: free ( entry->string ); ! 193: entry->string = NULL; ! 194: } ! 195: ! 196: /** ! 197: * Clean up history after editing ! 198: * ! 199: * @v history History buffer ! 200: */ ! 201: static void history_cleanup ( struct readline_history *history ) { ! 202: struct readline_history_entry *entry; ! 203: unsigned int i; ! 204: ! 205: /* Discard any temporary strings */ ! 206: for ( i = 0 ; i < ( sizeof ( history->entries ) / ! 207: sizeof ( history->entries[0] ) ) ; i++ ) { ! 208: entry = &history->entries[i]; ! 209: free ( entry->temp ); ! 210: entry->temp = NULL; ! 211: } ! 212: ! 213: /* Reset depth */ ! 214: history->depth = 0; ! 215: ! 216: /* Sanity check */ ! 217: entry = history_entry ( history, 0 ); ! 218: assert ( entry->string == NULL ); ! 219: } ! 220: ! 221: /** ! 222: * Free history buffer ! 223: * ! 224: * @v history History buffer ! 225: */ ! 226: void history_free ( struct readline_history *history ) { ! 227: struct readline_history_entry *entry; ! 228: unsigned int i; ! 229: ! 230: /* Discard any temporary strings */ ! 231: for ( i = 0 ; i < ( sizeof ( history->entries ) / ! 232: sizeof ( history->entries[0] ) ) ; i++ ) { ! 233: entry = &history->entries[i]; ! 234: assert ( entry->temp == NULL ); ! 235: free ( entry->string ); ! 236: } ! 237: } ! 238: ! 239: /** ! 240: * Read line from console (with history) ! 241: * ! 242: * @v prompt Prompt string ! 243: * @v history History buffer, or NULL for no history ! 244: * @ret line Line read from console (excluding terminating newline) ! 245: * ! 246: * The returned line is allocated with malloc(); the caller must ! 247: * eventually call free() to release the storage. ! 248: */ ! 249: char * readline_history ( const char *prompt, ! 250: struct readline_history *history ) { ! 251: char buf[READLINE_MAX]; ! 252: struct edit_string string; ! 253: int key; ! 254: int move_by; ! 255: const char *new_string; ! 256: char *line; ! 257: ! 258: /* Display prompt, if applicable */ ! 259: if ( prompt ) ! 260: printf ( "%s", prompt ); ! 261: ! 262: /* Initialise editable string */ ! 263: memset ( &string, 0, sizeof ( string ) ); ! 264: init_editstring ( &string, buf, sizeof ( buf ) ); ! 265: buf[0] = '\0'; ! 266: ! 267: while ( 1 ) { ! 268: /* Handle keypress */ ! 269: key = edit_string ( &string, getkey ( 0 ) ); ! 270: sync_console ( &string ); ! 271: move_by = 0; ! 272: switch ( key ) { ! 273: case CR: ! 274: case LF: ! 275: line = strdup ( buf ); ! 276: if ( ! line ) ! 277: printf ( "\nOut of memory" ); ! 278: goto done; ! 279: case CTRL_C: ! 280: line = NULL; ! 281: goto done; ! 282: case KEY_UP: ! 283: move_by = 1; ! 284: break; ! 285: case KEY_DOWN: ! 286: move_by = -1; ! 287: break; ! 288: default: ! 289: /* Do nothing */ ! 290: break; ! 291: } ! 292: ! 293: /* Handle history movement, if applicable */ ! 294: if ( move_by && history ) { ! 295: new_string = history_move ( history, move_by, buf ); ! 296: if ( new_string ) { ! 297: replace_string ( &string, new_string ); ! 298: sync_console ( &string ); ! 299: } ! 300: } ! 301: } ! 302: ! 303: done: ! 304: putchar ( '\n' ); ! 305: if ( history ) { ! 306: if ( line && line[0] ) ! 307: history_append ( history, line ); ! 308: history_cleanup ( history ); ! 309: } ! 310: return line; ! 311: } ! 312: ! 313: /** ! 314: * Read line from console ! 315: * ! 316: * @v prompt Prompt string ! 317: * @ret line Line read from console (excluding terminating newline) ! 318: * ! 319: * The returned line is allocated with malloc(); the caller must ! 320: * eventually call free() to release the storage. ! 321: */ ! 322: char * readline ( const char *prompt ) { ! 323: return readline_history ( prompt, NULL ); ! 324: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.