Index: uspace/dist/src/c/demos/edit/build
===================================================================
--- uspace/dist/src/c/demos/edit/build	(revision 2034f9849c23604f1137ee8e7826d581d548052e)
+++ uspace/dist/src/c/demos/edit/build	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -1,10 +1,13 @@
 cc -D__PCC__ -I/inc/c -E -o sheet.i sheet.c
 cc -D__PCC__ -I/inc/c -E -o edit.i edit.c
+cc -D__PCC__ -I/inc/c -E -o search.i search.c
 
 cc -S -o sheet.s sheet.i
 cc -S -o edit.s edit.i
+cc -S -o search.s search.i
 
 as -o sheet.o sheet.s
 as -o edit.o edit.s
+as -o search.o search.s
 
-ld -T /inc/_link.ld -o edit_ sheet.o edit.o /lib/libc.a /lib/libsoftint.a
+ld -T /inc/_link.ld -o edit_ sheet.o edit.o search.o /lib/libc.a /lib/libsoftint.a
Index: uspace/dist/src/c/demos/edit/clean
===================================================================
--- uspace/dist/src/c/demos/edit/clean	(revision 2034f9849c23604f1137ee8e7826d581d548052e)
+++ uspace/dist/src/c/demos/edit/clean	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -3,9 +3,12 @@
 rm edit.o
 rm sheet.o
+rm search.o
 
 rm edit.s
 rm sheet.s
+rm search.s
 
 rm edit.i
 rm sheet.i
+rm search.i
 
Index: uspace/dist/src/c/demos/edit/edit.c
===================================================================
--- uspace/dist/src/c/demos/edit/edit.c	(revision 2034f9849c23604f1137ee8e7826d581d548052e)
+++ uspace/dist/src/c/demos/edit/edit.c	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2009 Jiri Svoboda
+ * Copyright (c) 2012 Martin Sucha
  * All rights reserved.
  *
@@ -49,4 +50,5 @@
 
 #include "sheet.h"
+#include "search.h"
 
 enum redraw_flags {
@@ -78,4 +80,7 @@
 	tag_t sel_start;
 
+	/** Active keyboard modifiers */
+	keymod_t keymod;
+
 	/** 
 	 * Ideal column where the caret should try to get. This is used
@@ -83,4 +88,7 @@
 	 */
 	int ideal_column;
+	
+	char *previous_search;
+	bool previous_search_reverse;
 } pane_t;
 
@@ -91,5 +99,5 @@
 typedef struct {
 	char *file_name;
-	sheet_t sh;
+	sheet_t *sh;
 } doc_t;
 
@@ -106,5 +114,4 @@
 #define BUF_SIZE 64
 #define TAB_WIDTH 8
-#define ED_INFTY 65536
 
 /** Maximum filename length that can be entered. */
@@ -115,8 +122,12 @@
 static void cursor_setvis(bool visible);
 
+static void key_handle_press(kbd_event_t *ev);
 static void key_handle_unmod(kbd_event_t const *ev);
 static void key_handle_ctrl(kbd_event_t const *ev);
 static void key_handle_shift(kbd_event_t const *ev);
+static void key_handle_shift_ctrl(kbd_event_t const *ev);
 static void key_handle_movement(unsigned int key, bool shift);
+
+static void pos_handle(pos_event_t *ev);
 
 static int file_save(char const *fname);
@@ -125,6 +136,7 @@
 static int file_save_range(char const *fname, spt_t const *spos,
     spt_t const *epos);
-static char *filename_prompt(char const *prompt, char const *init_value);
 static char *range_get_str(spt_t const *spos, spt_t const *epos);
+
+static char *prompt(char const *prompt, char const *init_value);
 
 static void pane_text_display(void);
@@ -138,8 +150,14 @@
 static void delete_char_after(void);
 static void caret_update(void);
-static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
+static void caret_move_relative(int drow, int dcolumn, enum dir_spec align_dir, bool select);
+static void caret_move_absolute(int row, int column, enum dir_spec align_dir, bool select);
+static void caret_move(spt_t spt, bool select, bool update_ideal_column);
+static void caret_move_word_left(bool select);
+static void caret_move_word_right(bool select);
+static void caret_go_to_line_ask(void);
 
 static bool selection_active(void);
 static void selection_sel_all(void);
+static void selection_sel_range(spt_t pa, spt_t pb);
 static void selection_get_points(spt_t *pa, spt_t *pb);
 static void selection_delete(void);
@@ -147,6 +165,18 @@
 static void insert_clipboard_data(void);
 
+static void search(char *pattern, bool reverse);
+static void search_prompt(bool reverse);
+static void search_repeat(void);
+
 static void pt_get_sof(spt_t *pt);
 static void pt_get_eof(spt_t *pt);
+static void pt_get_sol(spt_t *cpt, spt_t *spt);
+static void pt_get_eol(spt_t *cpt, spt_t *ept);
+static bool pt_is_word_beginning(spt_t *pt);
+static bool pt_is_delimiter(spt_t *pt);
+static bool pt_is_punctuation(spt_t *pt);
+static spt_t pt_find_word_left(spt_t spt);
+static spt_t pt_find_word_left(spt_t spt);
+
 static int tag_cmp(tag_t const *a, tag_t const *b);
 static int spt_cmp(spt_t const *a, spt_t const *b);
@@ -158,9 +188,7 @@
 int main(int argc, char *argv[])
 {
-	kbd_event_t ev;
-	coord_t coord;
+	cons_event_t ev;
 	bool new_file;
-
-	spt_t pt;
+	int rc;
 
 	con = console_init(stdin, stdout);
@@ -175,11 +203,15 @@
 
 	/* Start with an empty sheet. */
-	sheet_init(&doc.sh);
+	rc = sheet_create(&doc.sh);
+	if (rc != EOK) {
+		printf("Out of memory.\n");
+		return -1;
+	}
 
 	/* Place caret at the beginning of file. */
-	coord.row = coord.column = 1;
-	sheet_get_cell_pt(&doc.sh, &coord, dir_before, &pt);
-	sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
-	pane.ideal_column = coord.column;
+	spt_t sof;
+	pt_get_sof(&sof);
+	sheet_place_tag(doc.sh, &sof, &pane.caret_pos);
+	pane.ideal_column = 1;
 
 	if (argc == 2) {
@@ -197,10 +229,10 @@
 		new_file = true;
 
+	/* Place selection start tag. */
+	sheet_place_tag(doc.sh, &sof, &pane.sel_start);
+
 	/* Move to beginning of file. */
-	caret_move(-ED_INFTY, -ED_INFTY, dir_before);
-
-	/* Place selection start tag. */
-	tag_get_pt(&pane.caret_pos, &pt);
-	sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
+	pt_get_sof(&sof);
+	caret_move(sof, true, true);
 
 	/* Initial display */
@@ -219,20 +251,16 @@
 
 	while (!done) {
-		console_get_kbd_event(con, &ev);
+		console_get_event(con, &ev);
 		pane.rflags = 0;
 
-		if (ev.type == KEY_PRESS) {
-			/* Handle key press. */
-			if (((ev.mods & KM_ALT) == 0) &&
-			    ((ev.mods & KM_SHIFT) == 0) &&
-			     (ev.mods & KM_CTRL) != 0) {
-				key_handle_ctrl(&ev);
-			} else if (((ev.mods & KM_ALT) == 0) &&
-			    ((ev.mods & KM_CTRL) == 0) &&
-			     (ev.mods & KM_SHIFT) != 0) {
-				key_handle_shift(&ev);
-			} else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
-				key_handle_unmod(&ev);
-			}
+		switch (ev.type) {
+		case CEV_KEY:
+			pane.keymod = ev.ev.key.mods;
+			if (ev.ev.key.type == KEY_PRESS)
+				key_handle_press(&ev.ev.key);
+			break;
+		case CEV_POS:
+			pos_handle(&ev.ev.pos);
+			break;
 		}
 
@@ -256,4 +284,24 @@
 
 	return 0;
+}
+
+/* Handle key press. */
+static void key_handle_press(kbd_event_t *ev)
+{
+	if (((ev->mods & KM_ALT) == 0) &&
+	    ((ev->mods & KM_SHIFT) == 0) &&
+	     (ev->mods & KM_CTRL) != 0) {
+		key_handle_ctrl(ev);
+	} else if (((ev->mods & KM_ALT) == 0) &&
+	    ((ev->mods & KM_CTRL) == 0) &&
+	     (ev->mods & KM_SHIFT) != 0) {
+		key_handle_shift(ev);
+	} else if (((ev->mods & KM_ALT) == 0) &&
+	    ((ev->mods & KM_CTRL) != 0) &&
+	     (ev->mods & KM_SHIFT) != 0) {
+		key_handle_shift_ctrl(ev);
+	} else if ((ev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
+		key_handle_unmod(ev);
+	}
 }
 
@@ -346,4 +394,5 @@
 static void key_handle_ctrl(kbd_event_t const *ev)
 {
+	spt_t pt;
 	switch (ev->key) {
 	case KC_Q:
@@ -377,4 +426,27 @@
 		selection_sel_all();
 		break;
+	case KC_RIGHT:
+		caret_move_word_right(false);
+		break;
+	case KC_LEFT:
+		caret_move_word_left(false);
+		break;
+	case KC_L:
+		caret_go_to_line_ask();
+		break;
+	case KC_F:
+		search_prompt(false);
+		break;
+	case KC_N:
+		search_repeat();
+		break;
+	case KC_HOME:
+		pt_get_sof(&pt);
+		caret_move(pt, false, true);
+		break;
+	case KC_END:
+		pt_get_eof(&pt);
+		caret_move(pt, false, true);
+		break;
 	default:
 		break;
@@ -382,56 +454,72 @@
 }
 
-static void key_handle_movement(unsigned int key, bool select)
+static void key_handle_shift_ctrl(kbd_event_t const *ev)
 {
 	spt_t pt;
-	spt_t caret_pt;
+	switch(ev->key) {
+	case KC_LEFT:
+		caret_move_word_left(true);
+		break;
+	case KC_RIGHT:
+		caret_move_word_right(true);
+		break;
+	case KC_F:
+		search_prompt(true);
+		break;
+	case KC_HOME:
+		pt_get_sof(&pt);
+		caret_move(pt, true, true);
+		break;
+	case KC_END:
+		pt_get_eof(&pt);
+		caret_move(pt, true, true);
+		break;
+	default:
+		break;
+	}
+}
+
+static void pos_handle(pos_event_t *ev)
+{
+	coord_t bc;
+	spt_t pt;
+	bool select;
+
+	if (ev->type == POS_PRESS && ev->vpos < (unsigned)pane.rows) {
+		bc.row = pane.sh_row + ev->vpos;
+		bc.column = pane.sh_column + ev->hpos;
+		sheet_get_cell_pt(doc.sh, &bc, dir_before, &pt);
+
+		select = (pane.keymod & KM_SHIFT) != 0;
+
+		caret_move(pt, select, true);
+	}
+}
+
+/** Move caret while preserving or resetting selection. */
+static void caret_move(spt_t new_caret_pt, bool select, bool update_ideal_column)
+{
+	spt_t old_caret_pt, old_sel_pt;
 	coord_t c_old, c_new;
 	bool had_sel;
 
 	/* Check if we had selection before. */
-	tag_get_pt(&pane.caret_pos, &caret_pt);
-	tag_get_pt(&pane.sel_start, &pt);
-	had_sel = !spt_equal(&caret_pt, &pt);
-
-	switch (key) {
-	case KC_LEFT:
-		caret_move(0, -1, dir_before);
-		break;
-	case KC_RIGHT:
-		caret_move(0, 0, dir_after);
-		break;
-	case KC_UP:
-		caret_move(-1, 0, dir_before);
-		break;
-	case KC_DOWN:
-		caret_move(+1, 0, dir_before);
-		break;
-	case KC_HOME:
-		caret_move(0, -ED_INFTY, dir_before);
-		break;
-	case KC_END:
-		caret_move(0, +ED_INFTY, dir_before);
-		break;
-	case KC_PAGE_UP:
-		caret_move(-pane.rows, 0, dir_before);
-		break;
-	case KC_PAGE_DOWN:
-		caret_move(+pane.rows, 0, dir_before);
-		break;
-	default:
-		break;
-	}
+	tag_get_pt(&pane.caret_pos, &old_caret_pt);
+	tag_get_pt(&pane.sel_start, &old_sel_pt);
+	had_sel = !spt_equal(&old_caret_pt, &old_sel_pt);
+
+	/* Place tag of the caret */
+	sheet_remove_tag(doc.sh, &pane.caret_pos);
+	sheet_place_tag(doc.sh, &new_caret_pt, &pane.caret_pos);
 
 	if (select == false) {
 		/* Move sel_start to the same point as caret. */
-		sheet_remove_tag(&doc.sh, &pane.sel_start);
-		tag_get_pt(&pane.caret_pos, &pt);
-		sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
-	}
-
+		sheet_remove_tag(doc.sh, &pane.sel_start);
+		sheet_place_tag(doc.sh, &new_caret_pt, &pane.sel_start);
+	}
+
+	spt_get_coord(&new_caret_pt, &c_new);
 	if (select) {
-		tag_get_pt(&pane.caret_pos, &pt);
-		spt_get_coord(&caret_pt, &c_old);
-		spt_get_coord(&pt, &c_new);
+		spt_get_coord(&old_caret_pt, &c_old);
 
 		if (c_old.row == c_new.row)
@@ -444,4 +532,46 @@
 		pane.rflags |= REDRAW_TEXT;
 	}
+	
+	if (update_ideal_column)
+		pane.ideal_column = c_new.column;
+	
+	caret_update();
+}
+
+static void key_handle_movement(unsigned int key, bool select)
+{
+	spt_t pt;
+	switch (key) {
+	case KC_LEFT:
+		caret_move_relative(0, -1, dir_before, select);
+		break;
+	case KC_RIGHT:
+		caret_move_relative(0, 0, dir_after, select);
+		break;
+	case KC_UP:
+		caret_move_relative(-1, 0, dir_before, select);
+		break;
+	case KC_DOWN:
+		caret_move_relative(+1, 0, dir_before, select);
+		break;
+	case KC_HOME:
+		tag_get_pt(&pane.caret_pos, &pt);
+		pt_get_sol(&pt, &pt);
+		caret_move(pt, select, true);
+		break;
+	case KC_END:
+		tag_get_pt(&pane.caret_pos, &pt);
+		pt_get_eol(&pt, &pt);
+		caret_move(pt, select, true);
+		break;
+	case KC_PAGE_UP:
+		caret_move_relative(-pane.rows, 0, dir_before, select);
+		break;
+	case KC_PAGE_DOWN:
+		caret_move_relative(+pane.rows, 0, dir_before, select);
+		break;
+	default:
+		break;
+	}
 }
 
@@ -479,5 +609,5 @@
 	char *fname;
 	
-	fname = filename_prompt("Save As", old_fname);
+	fname = prompt("Save As", old_fname);
 	if (fname == NULL) {
 		status_display("Save cancelled.");
@@ -494,8 +624,9 @@
 }
 
-/** Ask for a file name. */
-static char *filename_prompt(char const *prompt, char const *init_value)
-{
-	kbd_event_t ev;
+/** Ask for a string. */
+static char *prompt(char const *prompt, char const *init_value)
+{
+	cons_event_t ev;
+	kbd_event_t *kev;
 	char *str;
 	wchar_t buffer[INFNAME_MAX_LEN + 1];
@@ -517,13 +648,15 @@
 
 	while (!done) {
-		console_get_kbd_event(con, &ev);
-
-		if (ev.type == KEY_PRESS) {
+		console_get_event(con, &ev);
+
+		if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
+			kev = &ev.ev.key;
+
 			/* Handle key press. */
-			if (((ev.mods & KM_ALT) == 0) &&
-			     (ev.mods & KM_CTRL) != 0) {
+			if (((kev->mods & KM_ALT) == 0) &&
+			     (kev->mods & KM_CTRL) != 0) {
 				;
-			} else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
-				switch (ev.key) {
+			} else if ((kev->mods & (KM_CTRL | KM_ALT)) == 0) {
+				switch (kev->key) {
 				case KC_ESCAPE:
 					return NULL;
@@ -539,8 +672,8 @@
 					break;
 				default:
-					if (ev.c >= 32 && nc < max_len) {
-						putchar(ev.c);
+					if (kev->c >= 32 && nc < max_len) {
+						putchar(kev->c);
 						console_flush(con);
-						buffer[nc++] = ev.c;
+						buffer[nc++] = kev->c;
 					}
 					break;
@@ -616,5 +749,5 @@
 
 	do {
-		sheet_copy_out(&doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
+		sheet_copy_out(doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
 		bytes = str_size(buf);
 
@@ -651,5 +784,5 @@
 
 	while (true) {
-		sheet_copy_out(&doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
+		sheet_copy_out(doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
 		    &bep);
 		bytes = str_size(&buf[bpos]);
@@ -673,5 +806,5 @@
 	int sh_rows, rows;
 
-	sheet_get_num_rows(&doc.sh, &sh_rows);
+	sheet_get_num_rows(doc.sh, &sh_rows);
 	rows = min(sh_rows - pane.sh_row + 1, pane.rows);
 
@@ -743,13 +876,13 @@
 		rbc.row = pane.sh_row + i;
 		rbc.column = pane.sh_column;
-		sheet_get_cell_pt(&doc.sh, &rbc, dir_before, &rb);
+		sheet_get_cell_pt(doc.sh, &rbc, dir_before, &rb);
 
 		/* Ending point for row display */
 		rec.row = pane.sh_row + i;
 		rec.column = pane.sh_column + pane.columns;
-		sheet_get_cell_pt(&doc.sh, &rec, dir_before, &re);
+		sheet_get_cell_pt(doc.sh, &rec, dir_before, &re);
 
 		/* Copy the text of the row to the buffer. */
-		sheet_copy_out(&doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
+		sheet_copy_out(doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
 
 		/* Display text from the buffer. */
@@ -801,6 +934,6 @@
 		/* Fill until the end of display area. */
 
-		if (str_length(row_buf) < (unsigned) scr_columns)
-			fill = scr_columns - str_length(row_buf);
+		if ((unsigned)s_column - 1 < scr_columns)
+			fill = scr_columns - (s_column - 1);
 		else
 			fill = 0;
@@ -820,14 +953,17 @@
 	spt_t caret_pt;
 	coord_t coord;
+	int last_row;
 
 	tag_get_pt(&pane.caret_pos, &caret_pt);
 	spt_get_coord(&caret_pt, &coord);
 
+	sheet_get_num_rows(doc.sh, &last_row);
+
 	const char *fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
 
 	console_set_pos(con, 0, scr_rows - 1);
 	console_set_style(con, STYLE_INVERTED);
-	int n = printf(" %d, %d: File '%s'. Ctrl-Q Quit  Ctrl-S Save  "
-	    "Ctrl-E Save As", coord.row, coord.column, fname);
+	int n = printf(" %d, %d (%d): File '%s'. Ctrl-Q Quit  Ctrl-S Save  "
+	    "Ctrl-E Save As", coord.row, coord.column, last_row, fname);
 	
 	int pos = scr_columns - 1 - n;
@@ -865,5 +1001,5 @@
 	cbuf[offs] = '\0';
 
-	(void) sheet_insert(&doc.sh, &pt, dir_before, cbuf);
+	(void) sheet_insert(doc.sh, &pt, dir_before, cbuf);
 
 	pane.rflags |= REDRAW_ROW;
@@ -882,7 +1018,7 @@
 
 	coord.column -= 1;
-	sheet_get_cell_pt(&doc.sh, &coord, dir_before, &sp);
-
-	(void) sheet_delete(&doc.sh, &sp, &ep);
+	sheet_get_cell_pt(doc.sh, &coord, dir_before, &sp);
+
+	(void) sheet_delete(doc.sh, &sp, &ep);
 
 	pane.rflags |= REDRAW_ROW;
@@ -900,8 +1036,8 @@
 	spt_get_coord(&sp, &sc);
 
-	sheet_get_cell_pt(&doc.sh, &sc, dir_after, &ep);
+	sheet_get_cell_pt(doc.sh, &sc, dir_after, &ep);
 	spt_get_coord(&ep, &ec);
 
-	(void) sheet_delete(&doc.sh, &sp, &ep);
+	(void) sheet_delete(doc.sh, &sp, &ep);
 
 	pane.rflags |= REDRAW_ROW;
@@ -950,5 +1086,5 @@
 }
 
-/** Change the caret position.
+/** Relatively move caret position.
  *
  * Moves caret relatively to the current position. Looking at the first
@@ -956,6 +1092,9 @@
  * to a new character cell, and thus a new character. Then we either go to the
  * point before the the character or after it, depending on @a align_dir.
+ *
+ * @param select true if the selection tag should stay where it is
  */
-static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
+static void caret_move_relative(int drow, int dcolumn, enum dir_spec align_dir,
+    bool select)
 {
 	spt_t pt;
@@ -970,7 +1109,14 @@
 	/* Clamp coordinates. */
 	if (drow < 0 && coord.row < 1) coord.row = 1;
-	if (dcolumn < 0 && coord.column < 1) coord.column = 1;
+	if (dcolumn < 0 && coord.column < 1) {
+		if (coord.row < 2)
+			coord.column = 1;
+		else {
+			coord.row--;
+			sheet_get_row_width(doc.sh, coord.row, &coord.column);
+		}
+	}
 	if (drow > 0) {
-		sheet_get_num_rows(&doc.sh, &num_rows);
+		sheet_get_num_rows(doc.sh, &num_rows);
 		if (coord.row > num_rows) coord.row = num_rows;
 	}
@@ -985,15 +1131,231 @@
 	 * coordinates. The character can be wider than one cell (e.g. tab).
 	 */
-	sheet_get_cell_pt(&doc.sh, &coord, align_dir, &pt);
-	sheet_remove_tag(&doc.sh, &pane.caret_pos);
-	sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
+	sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
 
 	/* For non-vertical movement set the new value for @c ideal_column. */
-	if (!pure_vertical) {
-		spt_get_coord(&pt, &coord);
-		pane.ideal_column = coord.column;
-	}
-
-	caret_update();
+	caret_move(pt, select, !pure_vertical);
+}
+
+/** Absolutely move caret position.
+ *
+ * Moves caret to a specified position. We get to a new character cell, and
+ * thus a new character. Then we either go to the point before the the character
+ * or after it, depending on @a align_dir.
+ *
+ * @param select true if the selection tag should stay where it is
+ */
+static void caret_move_absolute(int row, int column, enum dir_spec align_dir,
+    bool select)
+{
+	coord_t coord;
+	coord.row = row;
+	coord.column = column;
+	
+	spt_t pt;
+	sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
+	
+	caret_move(pt, select, true);
+}
+
+/** Find beginning of a word to the left of spt */
+static spt_t pt_find_word_left(spt_t spt) 
+{
+	do {
+		spt_prev_char(spt, &spt);
+	} while (!pt_is_word_beginning(&spt));
+	return spt;
+}
+
+/** Find beginning of a word to the right of spt */
+static spt_t pt_find_word_right(spt_t spt) 
+{
+	do {
+		spt_next_char(spt, &spt);
+	} while (!pt_is_word_beginning(&spt));
+	return spt;
+}
+
+static void caret_move_word_left(bool select) 
+{
+	spt_t pt;
+	tag_get_pt(&pane.caret_pos, &pt);
+	spt_t word_left = pt_find_word_left(pt);
+	caret_move(word_left, select, true);
+}
+
+static void caret_move_word_right(bool select) 
+{
+	spt_t pt;
+	tag_get_pt(&pane.caret_pos, &pt);
+	spt_t word_right = pt_find_word_right(pt);
+	caret_move(word_right, select, true);
+}
+
+/** Ask for line and go to it. */
+static void caret_go_to_line_ask(void)
+{
+	char *sline;
+	
+	sline = prompt("Go to line", "");
+	if (sline == NULL) {
+		status_display("Go to line cancelled.");
+		return;
+	}
+	
+	char *endptr;
+	int line = strtol(sline, &endptr, 10);
+	if (*endptr != '\0') {
+		free(sline);
+		status_display("Invalid number entered.");
+		return;
+	}
+	free(sline);
+	
+	caret_move_absolute(line, pane.ideal_column, dir_before, false);
+}
+
+/* Search operations */
+static int search_spt_producer(void *data, wchar_t *ret)
+{
+	assert(data != NULL);
+	assert(ret != NULL);
+	spt_t *spt = data;
+	*ret = spt_next_char(*spt, spt);
+	return EOK;
+}
+
+static int search_spt_reverse_producer(void *data, wchar_t *ret)
+{
+	assert(data != NULL);
+	assert(ret != NULL);
+	spt_t *spt = data;
+	*ret = spt_prev_char(*spt, spt);
+	return EOK;
+}
+
+static int search_spt_mark(void *data, void **mark)
+{
+	assert(data != NULL);
+	assert(mark != NULL);
+	spt_t *spt = data;
+	spt_t *new = calloc(1, sizeof(spt_t));
+	*mark = new;
+	if (new == NULL)
+		return ENOMEM;
+	*new = *spt;
+	return EOK;
+}
+
+static void search_spt_mark_free(void *data)
+{
+	free(data);
+}
+
+static search_ops_t search_spt_ops = {
+	.equals = char_exact_equals,
+	.producer = search_spt_producer,
+	.mark = search_spt_mark,
+	.mark_free = search_spt_mark_free,
+};
+
+static search_ops_t search_spt_reverse_ops = {
+	.equals = char_exact_equals,
+	.producer = search_spt_reverse_producer,
+	.mark = search_spt_mark,
+	.mark_free = search_spt_mark_free,
+};
+
+/** Ask for line and go to it. */
+static void search_prompt(bool reverse)
+{
+	char *pattern;
+	
+	const char *prompt_text = "Find next";
+	if (reverse)
+		prompt_text = "Find previous";
+	
+	const char *default_value = "";
+	if (pane.previous_search)
+		default_value = pane.previous_search;
+	
+	pattern = prompt(prompt_text, default_value);
+	if (pattern == NULL) {
+		status_display("Search cancelled.");
+		return;
+	}
+	
+	if (pane.previous_search)
+		free(pane.previous_search);
+	pane.previous_search = pattern;
+	pane.previous_search_reverse = reverse;
+	
+	search(pattern, reverse);
+}
+
+static void search_repeat(void)
+{
+	if (pane.previous_search == NULL) {
+		status_display("No previous search to repeat.");
+		return;
+	}
+	
+	search(pane.previous_search, pane.previous_search_reverse);
+}
+
+static void search(char *pattern, bool reverse)
+{
+	status_display("Searching...");
+	
+	spt_t sp, producer_pos;
+	tag_get_pt(&pane.caret_pos, &sp);
+	
+	/* Start searching on the position before/after caret */
+	if (!reverse) {
+		spt_next_char(sp, &sp);
+	}
+	else {
+		spt_prev_char(sp, &sp);
+	}
+	producer_pos = sp;
+	
+	search_ops_t ops = search_spt_ops;
+	if (reverse)
+		ops = search_spt_reverse_ops;
+	
+	search_t *search = search_init(pattern, &producer_pos, ops, reverse);
+	if (search == NULL) {
+		status_display("Failed initializing search.");
+		return;
+	}
+	
+	match_t match;
+	int rc = search_next_match(search, &match);
+	if (rc != EOK) {
+		status_display("Failed searching.");
+		search_fini(search);
+	}
+	
+	if (match.end) {
+		status_display("Match found.");
+		assert(match.end != NULL);
+		spt_t *end = match.end;
+		caret_move(*end, false, true);
+		while (match.length > 0) {
+			match.length--;
+			if (reverse) {
+				spt_next_char(*end, end);
+			}
+			else {
+				spt_prev_char(*end, end);
+			}
+		}
+		caret_move(*end, true, true);
+		free(end);
+	}
+	else {
+		status_display("Not found.");
+	}
+	
+	search_fini(search);
 }
 
@@ -1035,7 +1397,7 @@
 
 	if (rel < 0)
-		sheet_delete(&doc.sh, &pa, &pb);
+		sheet_delete(doc.sh, &pa, &pb);
 	else
-		sheet_delete(&doc.sh, &pb, &pa);
+		sheet_delete(doc.sh, &pb, &pa);
 
 	if (ca.row == cb.row)
@@ -1045,4 +1407,5 @@
 }
 
+/** Select all text in the editor */
 static void selection_sel_all(void)
 {
@@ -1051,8 +1414,15 @@
 	pt_get_sof(&spt);
 	pt_get_eof(&ept);
-	sheet_remove_tag(&doc.sh, &pane.sel_start);
-	sheet_place_tag(&doc.sh, &spt, &pane.sel_start);
-	sheet_remove_tag(&doc.sh, &pane.caret_pos);
-	sheet_place_tag(&doc.sh, &ept, &pane.caret_pos);
+
+	selection_sel_range(spt, ept);
+}
+
+/** Select select all text in a given range with the given direction */
+static void selection_sel_range(spt_t pa, spt_t pb)
+{
+	sheet_remove_tag(doc.sh, &pane.sel_start);
+	sheet_place_tag(doc.sh, &pa, &pane.sel_start);
+	sheet_remove_tag(doc.sh, &pane.caret_pos);
+	sheet_place_tag(doc.sh, &pb, &pane.caret_pos);
 
 	pane.rflags |= REDRAW_TEXT;
@@ -1103,5 +1473,5 @@
 
 	coord.row = coord.column = 1;
-	sheet_get_cell_pt(&doc.sh, &coord, dir_before, pt);
+	sheet_get_cell_pt(doc.sh, &coord, dir_before, pt);
 }
 
@@ -1112,9 +1482,133 @@
 	int num_rows;
 
-	sheet_get_num_rows(&doc.sh, &num_rows);
+	sheet_get_num_rows(doc.sh, &num_rows);
 	coord.row = num_rows + 1;
 	coord.column = 1;
 
-	sheet_get_cell_pt(&doc.sh, &coord, dir_after, pt);
+	sheet_get_cell_pt(doc.sh, &coord, dir_after, pt);
+}
+
+/** Get start-of-line s-point for given s-point cpt */
+static void pt_get_sol(spt_t *cpt, spt_t *spt)
+{
+	coord_t coord;
+
+	spt_get_coord(cpt, &coord);
+	coord.column = 1;
+
+	sheet_get_cell_pt(doc.sh, &coord, dir_before, spt);
+}
+
+/** Get end-of-line s-point for given s-point cpt */
+static void pt_get_eol(spt_t *cpt, spt_t *ept)
+{
+	coord_t coord;
+	int row_width;
+
+	spt_get_coord(cpt, &coord);
+	sheet_get_row_width(doc.sh, coord.row, &row_width);
+	coord.column = row_width - 1;
+
+	sheet_get_cell_pt(doc.sh, &coord, dir_after, ept);
+}
+
+/** Check whether the spt is at a beginning of a word */
+static bool pt_is_word_beginning(spt_t *pt)
+{
+	spt_t lp, sfp, efp, slp, elp;
+	coord_t coord;
+
+	pt_get_sof(&sfp);
+	pt_get_eof(&efp);
+	pt_get_sol(pt, &slp);
+	pt_get_eol(pt, &elp);
+
+	/* the spt is at the beginning or end of the file or line */
+	if ((spt_cmp(&sfp, pt) == 0) || (spt_cmp(&efp, pt) == 0)
+	    || (spt_cmp(&slp, pt) == 0) || (spt_cmp(&elp, pt) == 0))
+		return true;
+
+	/* the spt is a delimiter */
+	if (pt_is_delimiter(pt))
+		return false;
+
+	spt_get_coord(pt, &coord);
+
+	coord.column -= 1;
+	sheet_get_cell_pt(doc.sh, &coord, dir_before, &lp);
+
+	return pt_is_delimiter(&lp)
+	    || (pt_is_punctuation(pt) && !pt_is_punctuation(&lp))
+	    || (pt_is_punctuation(&lp) && !pt_is_punctuation(pt));
+}
+
+static wchar_t get_first_wchar(const char *str)
+{
+	size_t offset = 0;
+	return str_decode(str, &offset, str_size(str));
+}
+
+static bool pt_is_delimiter(spt_t *pt)
+{
+	spt_t rp;
+	coord_t coord;
+	char *ch = NULL;
+
+	spt_get_coord(pt, &coord);
+
+	coord.column += 1;
+	sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
+
+	ch = range_get_str(pt, &rp);
+	if (ch == NULL)
+		return false;
+
+	wchar_t first_char = get_first_wchar(ch);
+	switch(first_char) {
+	case ' ':
+	case '\t':
+	case '\n':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool pt_is_punctuation(spt_t *pt)
+{
+	spt_t rp;
+	coord_t coord;
+	char *ch = NULL;
+
+	spt_get_coord(pt, &coord);
+
+	coord.column += 1;
+	sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
+
+	ch = range_get_str(pt, &rp);
+	if (ch == NULL)
+		return false;
+
+	wchar_t first_char = get_first_wchar(ch);
+	switch(first_char) {
+	case ',':
+	case '.':
+	case ';':
+	case ':':
+	case '/':
+	case '?':
+	case '\\':
+	case '|':
+	case '_':
+	case '+':
+	case '-':
+	case '*':
+	case '=':
+	case '<':
+	case '>':
+		return true;
+	default:
+		return false;
+	}
 }
 
Index: uspace/dist/src/c/demos/edit/search.c
===================================================================
--- uspace/dist/src/c/demos/edit/search.c	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
+++ uspace/dist/src/c/demos/edit/search.c	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup edit
+ * @{
+ */
+/**
+ * @file
+ * @brief Simple searching facility.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "search.h"
+#include "search_impl.h"
+
+search_t *search_init(const char *pattern, void *client_data, search_ops_t ops,
+    bool reverse)
+{
+	search_t *search = calloc(1, sizeof(search_t));
+	if (search == NULL)
+		return NULL;
+	
+	wchar_t *p = str_to_awstr(pattern);
+	if (p == NULL) {
+		free(search);
+		return NULL;
+	}
+	
+	search->pattern_length = wstr_length(p);
+	
+	if (reverse) {
+		/* Reverse the pattern */
+		size_t pos, half;
+		half = search->pattern_length / 2;
+		for (pos = 0; pos < half; pos++) {
+			wchar_t tmp = p[pos];
+			p[pos] = p[search->pattern_length - pos - 1];
+			p[search->pattern_length - pos - 1] = tmp;
+		}
+	}
+	
+	search->pattern = p;
+	
+	search->client_data = client_data;
+	search->ops = ops;
+	search->back_table = calloc(search->pattern_length, sizeof(ssize_t));
+	if (search->back_table == NULL) {
+		free(search->pattern);
+		free(search);
+		return NULL;
+	}
+	
+	search->pattern_pos = 0;
+	
+	search->back_table[0] = -1;
+	search->back_table[1] = 0;
+	size_t table_idx = 2;
+	size_t pattern_idx = 0;
+	while (table_idx < search->pattern_length) {
+		if (ops.equals(search->pattern[table_idx - 1],
+		    search->pattern[pattern_idx])) {
+			pattern_idx++;
+			search->back_table[table_idx] = pattern_idx;
+			table_idx++;
+		}
+		else if (pattern_idx > 0) {
+			pattern_idx = search->back_table[pattern_idx];
+		}
+		else {
+			pattern_idx = 0;
+			table_idx++;
+		}
+	}
+	
+	return search;
+}
+
+int search_next_match(search_t *s, match_t *match)
+{
+	search_equals_fn eq = s->ops.equals;
+	
+	wchar_t cur_char;
+	int rc = EOK;
+	while ((rc = s->ops.producer(s->client_data, &cur_char)) == EOK && cur_char > 0) {
+		/* Deal with mismatches */
+		while (s->pattern_pos > 0 && !eq(cur_char, s->pattern[s->pattern_pos])) {
+			s->pattern_pos = s->back_table[s->pattern_pos];
+		}
+		/* Check if the character matched */
+		if (eq(cur_char, s->pattern[s->pattern_pos])) {
+			s->pattern_pos++;
+			if (s->pattern_pos == s->pattern_length) {
+				s->pattern_pos = s->back_table[s->pattern_pos];
+				rc = s->ops.mark(s->client_data, &match->end);
+				if (rc != EOK)
+					return rc;
+				match->length = s->pattern_length;
+				return EOK;
+			}
+		}
+	}
+	
+	match->end = NULL;
+	match->length = 0;
+	
+	return rc;
+}
+
+void search_fini(search_t *search)
+{
+	free(search->pattern);
+	free(search->back_table);
+	
+}
+
+bool char_exact_equals(const wchar_t a, const wchar_t b)
+{
+	return a == b;
+}
+
+/** @}
+ */
Index: uspace/dist/src/c/demos/edit/search.h
===================================================================
--- uspace/dist/src/c/demos/edit/search.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
+++ uspace/dist/src/c/demos/edit/search.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup edit
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef SEARCH_H__
+#define SEARCH_H__
+
+#include <str.h>
+
+struct search;
+typedef struct search search_t;
+typedef bool (*search_equals_fn)(const wchar_t, const wchar_t);
+typedef int (*search_producer_fn)(void *, wchar_t *);
+typedef int (*search_mark_fn)(void *, void **);
+typedef void (*search_mark_free_fn)(void *);
+
+typedef struct match {
+	aoff64_t length;
+	void *end;
+} match_t;
+
+typedef struct search_ops {
+	search_equals_fn equals;
+	search_producer_fn producer;
+	search_mark_fn mark;
+	search_mark_free_fn mark_free;
+} search_ops_t;
+
+extern bool char_exact_equals(const wchar_t, const wchar_t);
+extern search_t *search_init(const char *, void *, search_ops_t, bool);
+extern int search_next_match(search_t *, match_t *);
+extern void search_fini(search_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/dist/src/c/demos/edit/search_impl.h
===================================================================
--- uspace/dist/src/c/demos/edit/search_impl.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
+++ uspace/dist/src/c/demos/edit/search_impl.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup edit
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef SEARCH_IMPL_H__
+#define SEARCH_IMPL_H__
+
+#include "search.h"
+
+/** Search state */
+struct search {
+	/* Note: This structure is opaque for the user. */
+
+	const wchar_t *pattern;
+	size_t pattern_length;
+	ssize_t *back_table;
+	size_t pattern_pos;
+	void *client_data;
+	search_ops_t ops;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/dist/src/c/demos/edit/sheet.c
===================================================================
--- uspace/dist/src/c/demos/edit/sheet.c	(revision 2034f9849c23604f1137ee8e7826d581d548052e)
+++ uspace/dist/src/c/demos/edit/sheet.c	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -57,4 +57,5 @@
 
 #include "sheet.h"
+#include "sheet_impl.h"
 
 enum {
@@ -66,6 +67,12 @@
 
 /** Initialize an empty sheet. */
-int sheet_init(sheet_t *sh)
-{
+int sheet_create(sheet_t **rsh)
+{
+	sheet_t *sh;
+
+	sh = calloc(1, sizeof(sheet_t));
+	if (sh == NULL)
+		return ENOMEM;
+
 	sh->dbuf_size = INITIAL_SIZE;
 	sh->text_size = 0;
@@ -77,4 +84,5 @@
 	list_initialize(&sh->tags);
 
+	*rsh = sh;
 	return EOK;
 }
@@ -97,5 +105,4 @@
 	char *ipp;
 	size_t sz;
-	tag_t *tag;
 	char *newp;
 
@@ -120,7 +127,5 @@
 	/* Adjust tags. */
 
-	list_foreach(sh->tags, link) {
-		tag = list_get_instance(link, tag_t, link);
-
+	list_foreach(sh->tags, link, tag_t, tag) {
 		if (tag->b_off > pos->b_off)
 			tag->b_off += sz;
@@ -146,5 +151,4 @@
 	char *spp;
 	size_t sz;
-	tag_t *tag;
 	char *newp;
 	size_t shrink_size;
@@ -157,7 +161,5 @@
 
 	/* Adjust tags. */
-	list_foreach(sh->tags, link) {
-		tag = list_get_instance(link, tag_t, link);
-
+	list_foreach(sh->tags, link, tag_t, tag) {
 		if (tag->b_off >= epos->b_off)
 			tag->b_off -= sz;
@@ -264,5 +266,5 @@
 	sheet_get_cell_pt(sh, &coord, dir_before, &pt);
 	spt_get_coord(&pt, &coord);
-	*length = coord.column - 1;
+	*length = coord.column;
 }
 
@@ -315,4 +317,21 @@
 }
 
+/** Get a character at spt and return next spt */
+wchar_t spt_next_char(spt_t spt, spt_t *next)
+{
+	wchar_t ch = str_decode(spt.sh->data, &spt.b_off, spt.sh->text_size);
+	if (next)
+		*next = spt;
+	return ch;
+}
+
+wchar_t spt_prev_char(spt_t spt, spt_t *prev)
+{
+	wchar_t ch = str_decode_reverse(spt.sh->data, &spt.b_off, spt.sh->text_size);
+	if (prev)
+		*prev = spt;
+	return ch;
+}
+
 /** Place a tag on the specified s-point. */
 void sheet_place_tag(sheet_t *sh, spt_t const *pt, tag_t *tag)
Index: uspace/dist/src/c/demos/edit/sheet.h
===================================================================
--- uspace/dist/src/c/demos/edit/sheet.h	(revision 2034f9849c23604f1137ee8e7826d581d548052e)
+++ uspace/dist/src/c/demos/edit/sheet.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -50,13 +50,6 @@
 
 /** Sheet */
-typedef struct {
-	/* Note: This structure is opaque for the user. */
-
-	size_t text_size;
-	size_t dbuf_size;
-	char *data;
-
-	list_t tags;
-} sheet_t;
+struct sheet;
+typedef struct sheet sheet_t;
 
 /** Character cell coordinates
@@ -97,5 +90,5 @@
 } tag_t;
 
-extern int sheet_init(sheet_t *);
+extern int sheet_create(sheet_t **);
 extern int sheet_insert(sheet_t *, spt_t *, enum dir_spec, char *);
 extern int sheet_delete(sheet_t *, spt_t *, spt_t *);
@@ -108,4 +101,6 @@
 extern void spt_get_coord(spt_t const *, coord_t *);
 extern bool spt_equal(spt_t const *, spt_t const *);
+extern wchar_t spt_next_char(spt_t, spt_t *);
+extern wchar_t spt_prev_char(spt_t, spt_t *);
 
 extern void sheet_place_tag(sheet_t *, spt_t const *, tag_t *);
Index: uspace/dist/src/c/demos/edit/sheet_impl.h
===================================================================
--- uspace/dist/src/c/demos/edit/sheet_impl.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
+++ uspace/dist/src/c/demos/edit/sheet_impl.h	(revision 5792d6e8f9a1b96afd4602a1462efbc3b3a44d8f)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup edit
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef SHEET_IMPL_H__
+#define SHEET_IMPL_H__
+
+#include "sheet.h"
+
+/** Sheet */
+struct sheet {
+	/* Note: This structure is opaque for the user. */
+
+	size_t text_size;
+	size_t dbuf_size;
+	char *data;
+
+	list_t tags;
+};
+
+#endif
+
+/** @}
+ */
