iPXE
readline.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <ipxe/console.h>
32#include <ipxe/keys.h>
33#include <ipxe/editstring.h>
34#include <readline/readline.h>
35
36/** @file
37 *
38 * Minimal readline
39 *
40 */
41
42/**
43 * Synchronise console with edited string
44 *
45 * @v string Editable string
46 */
47static void sync_console ( struct edit_string *string ) {
48 unsigned int mod_start = string->mod_start;
49 unsigned int mod_end = string->mod_end;
50 unsigned int cursor = string->last_cursor;
51 const char *buf = *(string->buf);
52 size_t len = strlen ( buf );
53
54 /* Expand region back to old cursor position if applicable */
55 if ( mod_start > string->last_cursor )
56 mod_start = string->last_cursor;
57
58 /* Expand region forward to new cursor position if applicable */
59 if ( mod_end < string->cursor )
60 mod_end = string->cursor;
61
62 /* Backspace to start of region */
63 while ( cursor > mod_start ) {
64 putchar ( '\b' );
65 cursor--;
66 }
67
68 /* Print modified region */
69 while ( cursor < mod_end ) {
70 putchar ( ( cursor >= len ) ? ' ' : buf[cursor] );
71 cursor++;
72 }
73
74 /* Backspace to new cursor position */
75 while ( cursor > string->cursor ) {
76 putchar ( '\b' );
77 cursor--;
78 }
79}
80
81/**
82 * Locate history entry
83 *
84 * @v history History buffer
85 * @v depth Depth within history buffer
86 * @ret entry History entry
87 */
88static struct readline_history_entry *
89history_entry ( struct readline_history *history, unsigned int depth ) {
90 unsigned int offset;
91
92 offset = ( ( history->next - depth ) %
93 ( sizeof ( history->entries ) /
94 sizeof ( history->entries[0] ) ) );
95 return &history->entries[offset];
96}
97
98/**
99 * Read string from history buffer
100 *
101 * @v history History buffer
102 * @v depth Depth within history buffer
103 * @ret string String
104 */
105static const char * history_fetch ( struct readline_history *history,
106 unsigned int depth ) {
107 struct readline_history_entry *entry;
108
109 /* Return the temporary copy if it exists, otherwise return
110 * the persistent copy.
111 */
112 entry = history_entry ( history, depth );
113 return ( entry->temp ? entry->temp : entry->string );
114}
115
116/**
117 * Write temporary string copy to history buffer
118 *
119 * @v history History buffer
120 * @v depth Depth within history buffer
121 * @v string String
122 */
123static void history_store ( struct readline_history *history,
124 unsigned int depth, const char *string ) {
125 struct readline_history_entry *entry;
126 char *temp;
127
128 /* Create temporary copy of string */
129 temp = strdup ( string );
130 if ( ! temp ) {
131 /* Just discard the string; there's nothing we can do */
132 DBGC ( history, "READLINE %p could not store string\n",
133 history );
134 return;
135 }
136
137 /* Store temporary copy */
138 entry = history_entry ( history, depth );
139 free ( entry->temp );
140 entry->temp = temp;
141}
142
143/**
144 * Move to new history depth
145 *
146 * @v history History buffer
147 * @v offset Offset by which to change depth
148 * @v old_string String (possibly modified) at current depth
149 * @ret new_string String at new depth, or NULL for no movement
150 */
151static const char * history_move ( struct readline_history *history,
152 int offset, const char *old_string ) {
153 unsigned int new_depth = ( history->depth + offset );
154 const char * new_string = history_fetch ( history, new_depth );
155
156 /* Depth checks */
157 if ( new_depth > READLINE_HISTORY_MAX_DEPTH )
158 return NULL;
159 if ( ! new_string )
160 return NULL;
161
162 /* Store temporary copy of old string at current depth */
163 history_store ( history, history->depth, old_string );
164
165 /* Update depth */
166 history->depth = new_depth;
167
168 /* Return new string */
169 return new_string;
170}
171
172/**
173 * Append new history entry
174 *
175 * @v history History buffer
176 * @v string String
177 */
178static void history_append ( struct readline_history *history,
179 const char *string ) {
180 struct readline_history_entry *entry;
181
182 /* Store new entry */
183 entry = history_entry ( history, 0 );
184 assert ( entry->string == NULL );
185 entry->string = strdup ( string );
186 if ( ! entry->string ) {
187 /* Just discard the string; there's nothing we can do */
188 DBGC ( history, "READLINE %p could not append string\n",
189 history );
190 return;
191 }
192
193 /* Increment history position */
194 history->next++;
195
196 /* Prepare empty "next" slot */
197 entry = history_entry ( history, 0 );
198 free ( entry->string );
199 entry->string = NULL;
200}
201
202/**
203 * Clean up history after editing
204 *
205 * @v history History buffer
206 */
207static void history_cleanup ( struct readline_history *history ) {
208 struct readline_history_entry *entry;
209 unsigned int i;
210
211 /* Discard any temporary strings */
212 for ( i = 0 ; i < ( sizeof ( history->entries ) /
213 sizeof ( history->entries[0] ) ) ; i++ ) {
214 entry = &history->entries[i];
215 free ( entry->temp );
216 entry->temp = NULL;
217 }
218
219 /* Reset depth */
220 history->depth = 0;
221
222 /* Sanity check */
223 entry = history_entry ( history, 0 );
224 assert ( entry->string == NULL );
225}
226
227/**
228 * Free history buffer
229 *
230 * @v history History buffer
231 */
232void history_free ( struct readline_history *history ) {
233 struct readline_history_entry *entry;
234 unsigned int i;
235
236 /* Discard any temporary strings */
237 for ( i = 0 ; i < ( sizeof ( history->entries ) /
238 sizeof ( history->entries[0] ) ) ; i++ ) {
239 entry = &history->entries[i];
240 assert ( entry->temp == NULL );
241 free ( entry->string );
242 }
243}
244
245/**
246 * Read line from console (with history)
247 *
248 * @v prompt Prompt string
249 * @v prefill Prefill string, or NULL for no prefill
250 * @v history History buffer, or NULL for no history
251 * @v timeout Timeout period, in ticks (0=indefinite)
252 * @ret line Line read from console (excluding terminating newline)
253 * @ret rc Return status code
254 *
255 * The returned line is allocated with malloc(); the caller must
256 * eventually call free() to release the storage.
257 */
258int readline_history ( const char *prompt, const char *prefill,
259 struct readline_history *history, unsigned long timeout,
260 char **line ) {
261 struct edit_string string;
262 int key;
263 int move_by;
264 const char *new_string;
265 int rc;
266
267 /* Display prompt, if applicable */
268 if ( prompt )
269 printf ( "%s", prompt );
270
271 /* Ensure cursor is visible */
272 printf ( "\033[?25h" );
273
274 /* Initialise editable string */
275 *line = NULL;
276 memset ( &string, 0, sizeof ( string ) );
277 init_editstring ( &string, line );
278
279 /* Prefill string */
280 if ( ( rc = replace_string ( &string, prefill ) ) != 0 )
281 goto error;
282 sync_console ( &string );
283
284 while ( 1 ) {
285
286 /* Get keypress */
287 key = getkey ( timeout );
288 if ( key < 0 ) {
289 rc = -ETIMEDOUT;
290 goto error;
291 }
292 timeout = 0;
293
294 /* Handle keypress */
295 key = edit_string ( &string, key );
296 sync_console ( &string );
297 move_by = 0;
298 switch ( key ) {
299 case CR:
300 case LF:
301 rc = 0;
302 goto done;
303 case CTRL_C:
304 rc = -ECANCELED;
305 goto error;
306 case KEY_UP:
307 move_by = 1;
308 break;
309 case KEY_DOWN:
310 move_by = -1;
311 break;
312 default:
313 /* Do nothing for unrecognised keys or edit errors */
314 break;
315 }
316
317 /* Handle history movement, if applicable */
318 if ( move_by && history ) {
319 new_string = history_move ( history, move_by, *line );
320 if ( new_string ) {
321 replace_string ( &string, new_string );
322 sync_console ( &string );
323 }
324 }
325 }
326
327 error:
328 free ( *line );
329 *line = NULL;
330 done:
331 putchar ( '\n' );
332 if ( history ) {
333 if ( *line && (*line)[0] )
334 history_append ( history, *line );
335 history_cleanup ( history );
336 }
337 assert ( ( rc == 0 ) ^ ( *line == NULL ) );
338 return rc;
339}
340
341/**
342 * Read line from console
343 *
344 * @v prompt Prompt string
345 * @ret line Line read from console (excluding terminating newline)
346 *
347 * The returned line is allocated with malloc(); the caller must
348 * eventually call free() to release the storage.
349 */
350char * readline ( const char *prompt ) {
351 char *line;
352
353 readline_history ( prompt, NULL, NULL, 0, &line );
354 return line;
355}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
union @162305117151260234136356364136041353210355154177 key
Sense key.
Definition scsi.h:3
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
struct arbelprm_completion_with_error error
Definition arbel.h:1
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
struct bofm_section_header done
Definition bofm_test.c:46
uint16_t offset
Offset to command line.
Definition bzimage.h:3
int putchar(int character)
Write a single character to each console device.
Definition console.c:29
void timeout(int)
ring len
Length.
Definition dwmac.h:226
int edit_string(struct edit_string *string, int key)
Edit editable string.
Definition editstring.c:256
int replace_string(struct edit_string *string, const char *replacement)
Replace editable string.
Definition editstring.c:229
Editable strings.
static __nonnull void init_editstring(struct edit_string *string, char **buf)
Initialise editable string.
Definition editstring.h:47
Error codes.
int getkey(unsigned long timeout)
Get single keypress.
Definition getkey.c:72
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ETIMEDOUT
Connection timed out.
Definition errno.h:670
#define ECANCELED
Operation canceled.
Definition errno.h:344
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
User interaction.
String functions.
void * memset(void *dest, int character, size_t len) __nonnull
Key definitions.
#define KEY_DOWN
Down arrow.
Definition keys.h:107
#define LF
Definition keys.h:48
#define CTRL_C
Definition keys.h:21
#define CR
Definition keys.h:49
#define KEY_UP
Up arrow.
Definition keys.h:106
uint32_t mod_end
Definition multiboot.h:1
uint32_t string
Definition multiboot.h:2
uint32_t mod_start
Definition multiboot.h:0
int prompt(const char *text, unsigned long timeout, int key)
Prompt for keypress.
Definition prompt.c:49
static void sync_console(struct edit_string *string)
Synchronise console with edited string.
Definition readline.c:47
static struct readline_history_entry * history_entry(struct readline_history *history, unsigned int depth)
Locate history entry.
Definition readline.c:89
static void history_cleanup(struct readline_history *history)
Clean up history after editing.
Definition readline.c:207
static const char * history_fetch(struct readline_history *history, unsigned int depth)
Read string from history buffer.
Definition readline.c:105
static void history_store(struct readline_history *history, unsigned int depth, const char *string)
Write temporary string copy to history buffer.
Definition readline.c:123
char * readline(const char *prompt)
Read line from console.
Definition readline.c:350
void history_free(struct readline_history *history)
Free history buffer.
Definition readline.c:232
int readline_history(const char *prompt, const char *prefill, struct readline_history *history, unsigned long timeout, char **line)
Read line from console (with history)
Definition readline.c:258
static void history_append(struct readline_history *history, const char *string)
Append new history entry.
Definition readline.c:178
static const char * history_move(struct readline_history *history, int offset, const char *old_string)
Move to new history depth.
Definition readline.c:151
Minmal readline.
#define READLINE_HISTORY_MAX_DEPTH
Maximum depth of a readline history buffer.
Definition readline.h:29
static void(* free)(struct refcnt *refcnt))
Definition refcnt.h:55
char * strdup(const char *src)
Duplicate string.
Definition string.c:394
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
An editable string.
Definition editstring.h:14
A readline history entry.
Definition readline.h:14
char * temp
Temporary copy of string.
Definition readline.h:22
char * string
Persistent copy of string.
Definition readline.h:16
A readline history buffer.
Definition readline.h:32
struct readline_history_entry entries[READLINE_HISTORY_MAX_DEPTH+1]
History entries.
Definition readline.h:39
unsigned int next
Position of next entry within buffer.
Definition readline.h:45
unsigned int depth
Current depth within history buffer.
Definition readline.h:50
int printf(const char *fmt,...)
Write a formatted string to the console.
Definition vsprintf.c:465