|
|
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.