Index: uspace/app/bdsh/input.c
===================================================================
--- uspace/app/bdsh/input.c	(revision a16220912b507493e2c8d702966000afb2389f35)
+++ uspace/app/bdsh/input.c	(revision 272f88f0092491c270b7b8e516115d98d7801676)
@@ -38,4 +38,5 @@
 #include <vfs/vfs.h>
 #include <errno.h>
+#include <assert.h>
 #include <bool.h>
 
@@ -47,4 +48,6 @@
 #include "exec.h"
 
+#define HISTORY_LEN 10
+
 typedef struct {
 	wchar_t buffer[INPUT_MAX];
@@ -53,10 +56,9 @@
 	int nc;
 	int pos;
+
+	char *history[1 + HISTORY_LEN];
+	int hnum;
+	int hpos;
 } tinput_t;
-
-typedef enum {
-	seek_cell,
-	seek_max
-} seek_dist_t;
 
 typedef enum {
@@ -130,4 +132,17 @@
 }
 
+static char *tinput_get_str(tinput_t *ti)
+{
+	char *str;
+
+	str = malloc(STR_BOUNDS(ti->nc) + 1);
+	if (str == NULL)
+		return NULL;
+
+	wstr_nstr(str, ti->buffer, STR_BOUNDS(ti->nc) + 1);
+
+	return str;
+}
+
 static void tinput_position_caret(tinput_t *ti)
 {
@@ -183,22 +198,120 @@
 }
 
-static void tinput_seek(tinput_t *ti, seek_dir_t dir, seek_dist_t dist)
-{
-	switch (dist) {
-	case seek_cell:
-		ti->pos += dir;
-		break;
-	case seek_max:
-		if (dir == seek_backward)
-			ti->pos = 0;
-		else
-			ti->pos = ti->nc;
-		break;
-	}
-		
-	if (ti->pos < 0) ti->pos = 0;
-	if (ti->pos > ti->nc) ti->pos = ti->nc;
-
-	tinput_position_caret(ti);
+static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir)
+{
+	if (dir == seek_forward) {
+		if (ti->pos < ti->nc)
+			ti->pos += 1;
+	} else {
+		if (ti->pos > 0)
+			ti->pos -= 1;
+	}
+
+	tinput_position_caret(ti);
+}
+
+static void tinput_seek_word(tinput_t *ti, seek_dir_t dir)
+{
+	if (dir == seek_forward) {
+		if (ti->pos == ti->nc)
+			return;
+
+		while (1) {
+			ti->pos += 1;
+
+			if (ti->pos == ti->nc)
+				break;
+
+			if (ti->buffer[ti->pos - 1] == ' ' &&
+			    ti->buffer[ti->pos] != ' ')
+				break;
+		}
+	} else {
+		if (ti->pos == 0)
+			return;
+
+		while (1) {
+			ti->pos -= 1;
+
+			if (ti->pos == 0)
+				break;
+
+			if (ti->buffer[ti->pos - 1] == ' ' &&
+			    ti->buffer[ti->pos] != ' ')
+				break;
+		}
+
+	}
+
+	tinput_position_caret(ti);
+}
+
+static void tinput_seek_max(tinput_t *ti, seek_dir_t dir)
+{
+	if (dir == seek_backward)
+		ti->pos = 0;
+	else
+		ti->pos = ti->nc;
+
+	tinput_position_caret(ti);
+}
+
+static void tinput_history_insert(tinput_t *ti, char *str)
+{
+	int i;
+
+	if (ti->hnum < HISTORY_LEN) {
+		ti->hnum += 1;
+	} else {
+		if (ti->history[HISTORY_LEN] != NULL)
+			free(ti->history[HISTORY_LEN]);
+	}
+
+	for (i = ti->hnum; i > 1; --i)
+		ti->history[i] = ti->history[i - 1];
+
+	ti->history[1] = str_dup(str);
+
+	if (ti->history[0] != NULL) {
+		free(ti->history[0]);
+		ti->history[0] = NULL;
+	}
+}
+
+static void tinput_set_str(tinput_t *ti, char *str)
+{
+	str_to_wstr(ti->buffer, INPUT_MAX, str);
+	ti->nc = wstr_length(ti->buffer);
+	ti->pos = ti->nc;
+}
+
+static void tinput_history_seek(tinput_t *ti, int offs)
+{
+	int pad;
+
+	if (ti->hpos + offs < 0 || ti->hpos + offs > ti->hnum)
+		return;
+
+	if (ti->history[ti->hpos] != NULL) {
+		free(ti->history[ti->hpos]);
+		ti->history[ti->hpos] = NULL;
+	}
+
+	ti->history[ti->hpos] = tinput_get_str(ti);
+	ti->hpos += offs;
+
+	pad = ti->nc - str_length(ti->history[ti->hpos]);
+	if (pad < 0) pad = 0;
+
+	tinput_set_str(ti, ti->history[ti->hpos]);
+	tinput_display_tail(ti, 0, pad);
+	tinput_position_caret(ti);
+}
+
+static void tinput_init(tinput_t *ti)
+{
+	ti->hnum = 0;
+	ti->hpos = 0;
+	ti->history[0] = NULL;
 }
 
@@ -226,31 +339,48 @@
 			continue;
 
-		if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) != 0)
-			continue;
-
-		switch(ev.key) {
-		case KC_ENTER:
-		case KC_NENTER:
-			goto done;
-		case KC_BACKSPACE:
-			tinput_backspace(ti);
-			break;
-		case KC_DELETE:
-			tinput_delete(ti);
-			break;
-		case KC_LEFT:
-			tinput_seek(ti, seek_backward, seek_cell);
-			break;
-		case KC_RIGHT:
-			tinput_seek(ti, seek_forward, seek_cell);
-
-			break;
-		case KC_HOME:
-			tinput_seek(ti, seek_backward, seek_max);
-			break;
-		case KC_END:
-			tinput_seek(ti, seek_forward, seek_max);
-			break;
+		if ((ev.mods & KM_CTRL) != 0 &&
+		    (ev.mods & (KM_ALT | KM_SHIFT)) == 0) {
+			switch (ev.key) {
+			case KC_LEFT:
+				tinput_seek_word(ti, seek_backward);
+				break;
+			case KC_RIGHT:
+				tinput_seek_word(ti, seek_forward);
+				break;
+			}
 		}
+
+		if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
+			switch (ev.key) {
+			case KC_ENTER:
+			case KC_NENTER:
+				goto done;
+			case KC_BACKSPACE:
+				tinput_backspace(ti);
+				break;
+			case KC_DELETE:
+				tinput_delete(ti);
+				break;
+			case KC_LEFT:
+				tinput_seek_cell(ti, seek_backward);
+				break;
+			case KC_RIGHT:
+				tinput_seek_cell(ti, seek_forward);
+				break;
+			case KC_HOME:
+				tinput_seek_max(ti, seek_backward);
+				break;
+			case KC_END:
+				tinput_seek_max(ti, seek_forward);
+				break;
+			case KC_UP:
+				tinput_history_seek(ti, +1);
+				break;
+			case KC_DOWN:
+				tinput_history_seek(ti, -1);
+				break;
+			}
+		}
+
 		if (ev.c >= ' ') {
 			tinput_insert_char(ti, ev.c);
@@ -261,10 +391,9 @@
 	putchar('\n');
 
-	ti->buffer[ti->nc] = '\0';
-	str = malloc(STR_BOUNDS(ti->nc) + 1);
-	if (str == NULL)
-		return NULL;
-
-	wstr_nstr(str, ti->buffer, STR_BOUNDS(ti->nc) + 1);
+	str = tinput_get_str(ti);
+	if (str_cmp(str, "") != 0)
+		tinput_history_insert(ti, str);
+
+	ti->hpos = 0;
 
 	return str;
@@ -292,2 +421,7 @@
 	return;
 }
+
+void input_init(void)
+{
+	tinput_init(&tinput);
+}
Index: uspace/app/bdsh/input.h
===================================================================
--- uspace/app/bdsh/input.h	(revision a16220912b507493e2c8d702966000afb2389f35)
+++ uspace/app/bdsh/input.h	(revision 272f88f0092491c270b7b8e516115d98d7801676)
@@ -8,4 +8,5 @@
 extern void get_input(cliuser_t *);
 extern int tok_input(cliuser_t *);
+extern void input_init(void);
 
 #endif
Index: uspace/app/bdsh/scli.c
===================================================================
--- uspace/app/bdsh/scli.c	(revision a16220912b507493e2c8d702966000afb2389f35)
+++ uspace/app/bdsh/scli.c	(revision 272f88f0092491c270b7b8e516115d98d7801676)
@@ -64,4 +64,7 @@
 	usr->prompt = (char *) NULL;
 	usr->lasterr = 0;
+
+	input_init();
+
 	return (int) cli_set_prompt(usr);
 }
Index: uspace/lib/libc/generic/string.c
===================================================================
--- uspace/lib/libc/generic/string.c	(revision a16220912b507493e2c8d702966000afb2389f35)
+++ uspace/lib/libc/generic/string.c	(revision 272f88f0092491c270b7b8e516115d98d7801676)
@@ -583,4 +583,36 @@
 }
 
+/** Convert string to wide string.
+ *
+ * Convert string @a src to wide string. The output is written to the
+ * buffer specified by @a dest and @a size, which must have non-zero
+ * size. The output will always be null-terminated.
+ *
+ * @param dest	Destination buffer.
+ * @param dlen	Length of destination buffer (number of wchars).
+ * @param src	Source string.
+ */
+void str_to_wstr(wchar_t *dest, size_t dlen, const char *src)
+{
+	size_t offset;
+	size_t di;
+	wchar_t c;
+
+	assert(dlen > 0);
+
+	offset = 0;
+	di = 0;
+
+	do {
+		if (di >= dlen - 1)
+			break;
+
+		c = str_decode(src, &offset, STR_NO_LIMIT);
+		dest[di++] = c;
+	} while (c != '\0');
+
+	dest[dlen - 1] = '\0';
+}
+
 /** Find first occurence of character in string.
  *
Index: uspace/lib/libc/include/string.h
===================================================================
--- uspace/lib/libc/include/string.h	(revision a16220912b507493e2c8d702966000afb2389f35)
+++ uspace/lib/libc/include/string.h	(revision 272f88f0092491c270b7b8e516115d98d7801676)
@@ -74,4 +74,5 @@
 
 extern void wstr_nstr(char *dst, const wchar_t *src, size_t size);
+extern void str_to_wstr(wchar_t *dest, size_t dlen, const char *src);
 
 extern char *str_chr(const char *str, wchar_t ch);
