You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

readline.c 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <errno.h>
  24. #include <ipxe/console.h>
  25. #include <ipxe/keys.h>
  26. #include <ipxe/editstring.h>
  27. #include <readline/readline.h>
  28. /** @file
  29. *
  30. * Minimal readline
  31. *
  32. */
  33. #define READLINE_MAX 256
  34. /**
  35. * Synchronise console with edited string
  36. *
  37. * @v string Editable string
  38. */
  39. static void sync_console ( struct edit_string *string ) {
  40. unsigned int mod_start = string->mod_start;
  41. unsigned int mod_end = string->mod_end;
  42. unsigned int cursor = string->last_cursor;
  43. size_t len = strlen ( string->buf );
  44. /* Expand region back to old cursor position if applicable */
  45. if ( mod_start > string->last_cursor )
  46. mod_start = string->last_cursor;
  47. /* Expand region forward to new cursor position if applicable */
  48. if ( mod_end < string->cursor )
  49. mod_end = string->cursor;
  50. /* Backspace to start of region */
  51. while ( cursor > mod_start ) {
  52. putchar ( '\b' );
  53. cursor--;
  54. }
  55. /* Print modified region */
  56. while ( cursor < mod_end ) {
  57. putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] );
  58. cursor++;
  59. }
  60. /* Backspace to new cursor position */
  61. while ( cursor > string->cursor ) {
  62. putchar ( '\b' );
  63. cursor--;
  64. }
  65. }
  66. /**
  67. * Locate history entry
  68. *
  69. * @v history History buffer
  70. * @v depth Depth within history buffer
  71. * @ret entry History entry
  72. */
  73. static struct readline_history_entry *
  74. history_entry ( struct readline_history *history, unsigned int depth ) {
  75. unsigned int offset;
  76. offset = ( ( history->next - depth ) %
  77. ( sizeof ( history->entries ) /
  78. sizeof ( history->entries[0] ) ) );
  79. return &history->entries[offset];
  80. }
  81. /**
  82. * Read string from history buffer
  83. *
  84. * @v history History buffer
  85. * @v depth Depth within history buffer
  86. * @ret string String
  87. */
  88. static const char * history_fetch ( struct readline_history *history,
  89. unsigned int depth ) {
  90. struct readline_history_entry *entry;
  91. /* Return the temporary copy if it exists, otherwise return
  92. * the persistent copy.
  93. */
  94. entry = history_entry ( history, depth );
  95. return ( entry->temp ? entry->temp : entry->string );
  96. }
  97. /**
  98. * Write temporary string copy to history buffer
  99. *
  100. * @v history History buffer
  101. * @v depth Depth within history buffer
  102. * @v string String
  103. */
  104. static void history_store ( struct readline_history *history,
  105. unsigned int depth, const char *string ) {
  106. struct readline_history_entry *entry;
  107. char *temp;
  108. /* Create temporary copy of string */
  109. temp = strdup ( string );
  110. if ( ! temp ) {
  111. /* Just discard the string; there's nothing we can do */
  112. DBGC ( history, "READLINE %p could not store string\n",
  113. history );
  114. return;
  115. }
  116. /* Store temporary copy */
  117. entry = history_entry ( history, depth );
  118. free ( entry->temp );
  119. entry->temp = temp;
  120. }
  121. /**
  122. * Move to new history depth
  123. *
  124. * @v history History buffer
  125. * @v offset Offset by which to change depth
  126. * @v old_string String (possibly modified) at current depth
  127. * @ret new_string String at new depth, or NULL for no movement
  128. */
  129. static const char * history_move ( struct readline_history *history,
  130. int offset, const char *old_string ) {
  131. unsigned int new_depth = ( history->depth + offset );
  132. const char * new_string = history_fetch ( history, new_depth );
  133. /* Depth checks */
  134. if ( new_depth > READLINE_HISTORY_MAX_DEPTH )
  135. return NULL;
  136. if ( ! new_string )
  137. return NULL;
  138. /* Store temporary copy of old string at current depth */
  139. history_store ( history, history->depth, old_string );
  140. /* Update depth */
  141. history->depth = new_depth;
  142. /* Return new string */
  143. return new_string;
  144. }
  145. /**
  146. * Append new history entry
  147. *
  148. * @v history History buffer
  149. * @v string String
  150. */
  151. static void history_append ( struct readline_history *history,
  152. const char *string ) {
  153. struct readline_history_entry *entry;
  154. /* Store new entry */
  155. entry = history_entry ( history, 0 );
  156. assert ( entry->string == NULL );
  157. entry->string = strdup ( string );
  158. if ( ! entry->string ) {
  159. /* Just discard the string; there's nothing we can do */
  160. DBGC ( history, "READLINE %p could not append string\n",
  161. history );
  162. return;
  163. }
  164. /* Increment history position */
  165. history->next++;
  166. /* Prepare empty "next" slot */
  167. entry = history_entry ( history, 0 );
  168. free ( entry->string );
  169. entry->string = NULL;
  170. }
  171. /**
  172. * Clean up history after editing
  173. *
  174. * @v history History buffer
  175. */
  176. static void history_cleanup ( struct readline_history *history ) {
  177. struct readline_history_entry *entry;
  178. unsigned int i;
  179. /* Discard any temporary strings */
  180. for ( i = 0 ; i < ( sizeof ( history->entries ) /
  181. sizeof ( history->entries[0] ) ) ; i++ ) {
  182. entry = &history->entries[i];
  183. free ( entry->temp );
  184. entry->temp = NULL;
  185. }
  186. /* Reset depth */
  187. history->depth = 0;
  188. /* Sanity check */
  189. entry = history_entry ( history, 0 );
  190. assert ( entry->string == NULL );
  191. }
  192. /**
  193. * Free history buffer
  194. *
  195. * @v history History buffer
  196. */
  197. void history_free ( struct readline_history *history ) {
  198. struct readline_history_entry *entry;
  199. unsigned int i;
  200. /* Discard any temporary strings */
  201. for ( i = 0 ; i < ( sizeof ( history->entries ) /
  202. sizeof ( history->entries[0] ) ) ; i++ ) {
  203. entry = &history->entries[i];
  204. assert ( entry->temp == NULL );
  205. free ( entry->string );
  206. }
  207. }
  208. /**
  209. * Read line from console (with history)
  210. *
  211. * @v prompt Prompt string
  212. * @v prefill Prefill string, or NULL for no prefill
  213. * @v history History buffer, or NULL for no history
  214. * @ret line Line read from console (excluding terminating newline)
  215. * @ret rc Return status code
  216. *
  217. * The returned line is allocated with malloc(); the caller must
  218. * eventually call free() to release the storage.
  219. */
  220. int readline_history ( const char *prompt, const char *prefill,
  221. struct readline_history *history, char **line ) {
  222. char buf[READLINE_MAX];
  223. struct edit_string string;
  224. int key;
  225. int move_by;
  226. const char *new_string;
  227. int rc;
  228. /* Avoid returning uninitialised data on error */
  229. *line = NULL;
  230. /* Display prompt, if applicable */
  231. if ( prompt )
  232. printf ( "%s", prompt );
  233. /* Ensure cursor is visible */
  234. printf ( "\033[?25h" );
  235. /* Initialise editable string */
  236. memset ( &string, 0, sizeof ( string ) );
  237. init_editstring ( &string, buf, sizeof ( buf ) );
  238. buf[0] = '\0';
  239. /* Prefill string, if applicable */
  240. if ( prefill ) {
  241. replace_string ( &string, prefill );
  242. sync_console ( &string );
  243. }
  244. while ( 1 ) {
  245. /* Handle keypress */
  246. key = edit_string ( &string, getkey ( 0 ) );
  247. sync_console ( &string );
  248. move_by = 0;
  249. switch ( key ) {
  250. case CR:
  251. case LF:
  252. *line = strdup ( buf );
  253. rc = ( ( *line ) ? 0 : -ENOMEM );
  254. goto done;
  255. case CTRL_C:
  256. rc = -ECANCELED;
  257. goto done;
  258. case KEY_UP:
  259. move_by = 1;
  260. break;
  261. case KEY_DOWN:
  262. move_by = -1;
  263. break;
  264. default:
  265. /* Do nothing */
  266. break;
  267. }
  268. /* Handle history movement, if applicable */
  269. if ( move_by && history ) {
  270. new_string = history_move ( history, move_by, buf );
  271. if ( new_string ) {
  272. replace_string ( &string, new_string );
  273. sync_console ( &string );
  274. }
  275. }
  276. }
  277. done:
  278. putchar ( '\n' );
  279. if ( history ) {
  280. if ( *line && (*line)[0] )
  281. history_append ( history, *line );
  282. history_cleanup ( history );
  283. }
  284. assert ( ( rc == 0 ) ^ ( *line == NULL ) );
  285. return rc;
  286. }
  287. /**
  288. * Read line from console
  289. *
  290. * @v prompt Prompt string
  291. * @ret line Line read from console (excluding terminating newline)
  292. *
  293. * The returned line is allocated with malloc(); the caller must
  294. * eventually call free() to release the storage.
  295. */
  296. char * readline ( const char *prompt ) {
  297. char *line;
  298. readline_history ( prompt, NULL, NULL, &line );
  299. return line;
  300. }