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 FILE_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  */
47 static 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  */
88 static struct readline_history_entry *
89 history_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  */
105 static 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  */
123 static 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  */
151 static 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  */
178 static 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  */
207 static 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  */
232 void 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  */
258 int 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  */
350 char * readline ( const char *prompt ) {
351  char *line;
352 
353  readline_history ( prompt, NULL, NULL, 0, &line );
354  return line;
355 }
int getkey(unsigned long timeout)
Get single keypress.
Definition: getkey.c:72
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:465
int replace_string(struct edit_string *string, const char *replacement)
Replace editable string.
Definition: editstring.c:229
unsigned int depth
Current depth within history buffer.
Definition: readline.h:50
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:123
static struct readline_history_entry * history_entry(struct readline_history *history, unsigned int depth)
Locate history entry.
Definition: readline.c:89
Error codes.
struct readline_history_entry entries[READLINE_HISTORY_MAX_DEPTH+1]
History entries.
Definition: readline.h:39
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:151
static void history_cleanup(struct readline_history *history)
Clean up history after editing.
Definition: readline.c:207
#define DBGC(...)
Definition: compiler.h:505
int edit_string(struct edit_string *string, int key)
Edit editable string.
Definition: editstring.c:256
uint32_t string
Definition: multiboot.h:14
#define ECANCELED
Operation canceled.
Definition: errno.h:344
char * temp
Temporary copy of string.
Definition: readline.h:22
#define KEY_DOWN
Down arrow.
Definition: keys.h:107
Minmal readline.
#define KEY_UP
Up arrow.
Definition: keys.h:106
static void sync_console(struct edit_string *string)
Synchronise console with edited string.
Definition: readline.c:47
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:47
#define READLINE_HISTORY_MAX_DEPTH
Maximum depth of a readline history buffer.
Definition: readline.h:29
ring len
Length.
Definition: dwmac.h:231
#define CTRL_C
Definition: keys.h:21
User interaction.
unsigned int next
Position of next entry within buffer.
Definition: readline.h:45
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:55
A readline history buffer.
Definition: readline.h:32
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
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
int prompt(const char *text, unsigned long timeout, int key)
Prompt for keypress.
Definition: prompt.c:49
#define LF
Definition: keys.h:48
char * string
Persistent copy of string.
Definition: readline.h:16
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
Key definitions.
static void history_append(struct readline_history *history, const char *string)
Append new history entry.
Definition: readline.c:178
An editable string.
Definition: editstring.h:14
static const char * history_fetch(struct readline_history *history, unsigned int depth)
Read string from history buffer.
Definition: readline.c:105
#define CR
Definition: keys.h:49
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
void timeout(int)
uint16_t offset
Offset to command line.
Definition: bzimage.h:8
FILE_SECBOOT(PERMITTED)
A readline history entry.
Definition: readline.h:14
#define NULL
NULL pointer (VOID *)
Definition: Base.h:322
int putchar(int character)
Write a single character to each console device.
Definition: console.c:29
#define ETIMEDOUT
Connection timed out.
Definition: errno.h:670
String functions.
struct bofm_section_header done
Definition: bofm_test.c:46
union @391 key
Sense key.
Definition: scsi.h:18
void * memset(void *dest, int character, size_t len) __nonnull