Index: uspace/app/bdsh/input.c
===================================================================
--- uspace/app/bdsh/input.c	(revision 0902edfe630baa7edead6f5a6a722c7b9e74a40e)
+++ uspace/app/bdsh/input.c	(revision 7e0cb787bdbc14fe27e406d6eadd2c5ce285156a)
@@ -36,4 +36,5 @@
 #include <io/keycode.h>
 #include <io/style.h>
+#include <io/color.h>
 #include <vfs/vfs.h>
 #include <errno.h>
@@ -56,8 +57,10 @@
 	int nc;
 	int pos;
+	int sel_start;
 
 	char *history[1 + HISTORY_LEN];
 	int hnum;
 	int hpos;
+	bool done;
 } tinput_t;
 
@@ -70,4 +73,13 @@
 
 static char *tinput_read(tinput_t *ti);
+static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb);
+static bool tinput_sel_active(tinput_t *ti);
+static void tinput_sel_delete(tinput_t *ti);
+static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev);
+static void tinput_key_shift(tinput_t *ti, console_event_t *ev);
+static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev);
+static void tinput_key_unmod(tinput_t *ti, console_event_t *ev);
+static void tinput_pre_seek(tinput_t *ti, bool shift_held);
+static void tinput_post_seek(tinput_t *ti, bool shift_held);
 
 /* Tokenizes input from console, sees if the first word is a built-in, if so
@@ -123,9 +135,42 @@
 static void tinput_display_tail(tinput_t *ti, int start, int pad)
 {
-	int i;
+	static wchar_t dbuf[INPUT_MAX + 1];
+	int sa, sb;
+	int i, p;
+
+	tinput_sel_get_bounds(ti, &sa, &sb);
 
 	console_goto(fphone(stdout), (ti->col0 + start) % ti->con_cols,
 	    ti->row0 + (ti->col0 + start) / ti->con_cols);
-	printf("%ls", ti->buffer + start);
+	console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
+
+	p = start;
+	if (p < sa) {
+		memcpy(dbuf, ti->buffer + p, (sa - p) * sizeof(wchar_t));
+		dbuf[sa - p] = '\0';
+		printf("%ls", dbuf);
+		p = sa;
+	}
+
+	if (p < sb) {
+		fflush(stdout);
+		console_set_color(fphone(stdout), COLOR_BLACK, COLOR_RED, 0);
+		memcpy(dbuf, ti->buffer + p,
+		    (sb - p) * sizeof(wchar_t));
+		dbuf[sb - p] = '\0';
+		printf("%ls", dbuf);
+		p = sb;
+	}
+
+	fflush(stdout);
+	console_set_color(fphone(stdout), COLOR_BLACK, COLOR_WHITE, 0);
+
+	if (p < ti->nc) {
+		memcpy(dbuf, ti->buffer + p,
+		    (ti->nc - p) * sizeof(wchar_t));
+		dbuf[ti->nc - p] = '\0';
+		printf("%ls", dbuf);
+	}
+
 	for (i = 0; i < pad; ++i)
 		putchar(' ');
@@ -180,4 +225,5 @@
 	ti->nc += 1;
 	ti->buffer[ti->nc] = '\0';
+	ti->sel_start = ti->pos;
 
 	tinput_display_tail(ti, ti->pos - 1, 0);
@@ -190,4 +236,9 @@
 	int i;
 
+	if (tinput_sel_active(ti)) {
+		tinput_sel_delete(ti);
+		return;
+	}
+  
 	if (ti->pos == 0)
 		return;
@@ -198,4 +249,5 @@
 	ti->nc -= 1;
 	ti->buffer[ti->nc] = '\0';
+	ti->sel_start = ti->pos;
 
 	tinput_display_tail(ti, ti->pos, 1);
@@ -205,13 +257,22 @@
 static void tinput_delete(tinput_t *ti)
 {
+	if (tinput_sel_active(ti)) {
+		tinput_sel_delete(ti);
+		return;
+	}
+
 	if (ti->pos == ti->nc)
 		return;
 
 	ti->pos += 1;
+	ti->sel_start = ti->pos;
+
 	tinput_backspace(ti);
 }
 
-static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir)
-{
+static void tinput_seek_cell(tinput_t *ti, seek_dir_t dir, bool shift_held)
+{
+	tinput_pre_seek(ti, shift_held);
+
 	if (dir == seek_forward) {
 		if (ti->pos < ti->nc)
@@ -222,9 +283,11 @@
 	}
 
-	tinput_position_caret(ti);
-}
-
-static void tinput_seek_word(tinput_t *ti, seek_dir_t dir)
-{
+	tinput_post_seek(ti, shift_held);
+}
+
+static void tinput_seek_word(tinput_t *ti, seek_dir_t dir, bool shift_held)
+{
+	tinput_pre_seek(ti, shift_held);
+
 	if (dir == seek_forward) {
 		if (ti->pos == ti->nc)
@@ -258,9 +321,11 @@
 	}
 
-	tinput_position_caret(ti);
-}
-
-static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir)
-{
+	tinput_post_seek(ti, shift_held);
+}
+
+static void tinput_seek_vertical(tinput_t *ti, seek_dir_t dir, bool shift_held)
+{
+	tinput_pre_seek(ti, shift_held);
+
 	if (dir == seek_forward) {
 		if (ti->pos + ti->con_cols <= ti->nc)
@@ -271,9 +336,11 @@
 	}
 
-	tinput_position_caret(ti);
-}
-
-static void tinput_seek_max(tinput_t *ti, seek_dir_t dir)
-{
+	tinput_post_seek(ti, shift_held);
+}
+
+static void tinput_seek_max(tinput_t *ti, seek_dir_t dir, bool shift_held)
+{
+	tinput_pre_seek(ti, shift_held);
+
 	if (dir == seek_backward)
 		ti->pos = 0;
@@ -281,4 +348,26 @@
 		ti->pos = ti->nc;
 
+	tinput_post_seek(ti, shift_held);
+}
+
+static void tinput_pre_seek(tinput_t *ti, bool shift_held)
+{
+	if (tinput_sel_active(ti) && !shift_held) {
+		/* Unselect and redraw. */
+		ti->sel_start = ti->pos;
+		tinput_display_tail(ti, 0, 0);
+		tinput_position_caret(ti);
+	}
+}
+
+static void tinput_post_seek(tinput_t *ti, bool shift_held)
+{
+	if (shift_held) {
+		/* Selecting text. Need redraw. */
+		tinput_display_tail(ti, 0, 0);
+	} else {
+		/* Shift not held. Keep selection empty. */
+		ti->sel_start = ti->pos;
+	}
 	tinput_position_caret(ti);
 }
@@ -311,4 +400,39 @@
 	ti->nc = wstr_length(ti->buffer);
 	ti->pos = ti->nc;
+	ti->sel_start = ti->pos;
+}
+
+static void tinput_sel_get_bounds(tinput_t *ti, int *sa, int *sb)
+{
+	if (ti->sel_start < ti->pos) {
+		*sa = ti->sel_start;
+		*sb = ti->pos;
+	} else {
+		*sa = ti->pos;
+		*sb = ti->sel_start;
+	}
+}
+
+static bool tinput_sel_active(tinput_t *ti)
+{
+	return ti->sel_start != ti->pos;
+}
+
+static void tinput_sel_delete(tinput_t *ti)
+{
+	int sa, sb;
+
+	tinput_sel_get_bounds(ti, &sa, &sb);
+	if (sa == sb)
+		return;
+
+	memmove(ti->buffer + sa, ti->buffer + sb,
+	    (ti->nc - sb) * sizeof(wchar_t));
+	ti->pos = ti->sel_start = sa;
+	ti->nc -= (sb - sa);
+	ti->buffer[ti->nc] = '\0';
+
+	tinput_display_tail(ti, sa, sb - sa);
+	tinput_position_caret(ti);
 }
 
@@ -356,13 +480,14 @@
 		return NULL;
 
-	ti->pos = 0;
+	ti->pos = ti->sel_start = 0;
 	ti->nc = 0;
 	ti->buffer[0] = '\0';
-
-	while (true) {
+	ti->done = false;
+
+	while (!ti->done) {
 		fflush(stdout);
 		if (!console_get_event(fphone(stdin), &ev))
 			return NULL;
-		
+
 		if (ev.type != KEY_PRESS)
 			continue;
@@ -370,58 +495,28 @@
 		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;
-			case KC_UP:
-				tinput_seek_vertical(ti, seek_backward);
-				break;
-			case KC_DOWN:
-				tinput_seek_vertical(ti, seek_forward);
-				break;
-			}
+			tinput_key_ctrl(ti, &ev);
 		}
 
+		if ((ev.mods & KM_SHIFT) != 0 &&
+		    (ev.mods & (KM_CTRL | KM_ALT)) == 0) {
+			tinput_key_shift(ti, &ev);
+		}
+
+		if ((ev.mods & KM_CTRL) != 0 &&
+		    (ev.mods & KM_SHIFT) != 0 &&
+		    (ev.mods & KM_ALT) == 0) {
+			tinput_key_ctrl_shift(ti, &ev);
+		}
+
 		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;
-			}
+			tinput_key_unmod(ti, &ev);
 		}
 
 		if (ev.c >= ' ') {
+			tinput_sel_delete(ti);
 			tinput_insert_char(ti, ev.c);
 		}
 	}
 
-done:
 	ti->pos = ti->nc;
 	tinput_position_caret(ti);
@@ -436,4 +531,107 @@
 	return str;
 }
+
+static void tinput_key_ctrl(tinput_t *ti, console_event_t *ev)
+{
+	switch (ev->key) {
+	case KC_LEFT:
+		tinput_seek_word(ti, seek_backward, false);
+		break;
+	case KC_RIGHT:
+		tinput_seek_word(ti, seek_forward, false);
+		break;
+	case KC_UP:
+		tinput_seek_vertical(ti, seek_backward, false);
+		break;
+	case KC_DOWN:
+		tinput_seek_vertical(ti, seek_forward, false);
+		break;
+	default:
+		break;
+	}
+}
+
+static void tinput_key_ctrl_shift(tinput_t *ti, console_event_t *ev)
+{
+	switch (ev->key) {
+	case KC_LEFT:
+		tinput_seek_word(ti, seek_backward, true);
+		break;
+	case KC_RIGHT:
+		tinput_seek_word(ti, seek_forward, true);
+		break;
+	case KC_UP:
+		tinput_seek_vertical(ti, seek_backward, true);
+		break;
+	case KC_DOWN:
+		tinput_seek_vertical(ti, seek_forward, true);
+		break;
+	default:
+		break;
+	}
+}
+
+static void tinput_key_shift(tinput_t *ti, console_event_t *ev)
+{
+	switch (ev->key) {
+	case KC_LEFT:
+		tinput_seek_cell(ti, seek_backward, true);
+		break;
+	case KC_RIGHT:
+		tinput_seek_cell(ti, seek_forward, true);
+		break;
+	case KC_UP:
+		tinput_seek_vertical(ti, seek_backward, true);
+		break;
+	case KC_DOWN:
+		tinput_seek_vertical(ti, seek_forward, true);
+		break;
+	case KC_HOME:
+		tinput_seek_max(ti, seek_backward, true);
+		break;
+	case KC_END:
+		tinput_seek_max(ti, seek_forward, true);
+		break;
+	default:
+		break;
+	}
+}
+
+static void tinput_key_unmod(tinput_t *ti, console_event_t *ev)
+{
+	switch (ev->key) {
+	case KC_ENTER:
+	case KC_NENTER:
+		ti->done = true;
+		break;
+	case KC_BACKSPACE:
+		tinput_backspace(ti);
+		break;
+	case KC_DELETE:
+		tinput_delete(ti);
+		break;
+	case KC_LEFT:
+		tinput_seek_cell(ti, seek_backward, false);
+		break;
+	case KC_RIGHT:
+		tinput_seek_cell(ti, seek_forward, false);
+		break;
+	case KC_HOME:
+		tinput_seek_max(ti, seek_backward, false);
+		break;
+	case KC_END:
+		tinput_seek_max(ti, seek_forward, false);
+		break;
+	case KC_UP:
+		tinput_history_seek(ti, +1);
+		break;
+	case KC_DOWN:
+		tinput_history_seek(ti, -1);
+		break;
+	default:
+		break;
+	}
+}
+
 
 void get_input(cliuser_t *usr)
