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