Index: uspace/app/bdsh/Makefile
===================================================================
--- uspace/app/bdsh/Makefile	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/Makefile	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -48,4 +48,6 @@
 	cmds/modules/cp/cp.c \
 	cmds/modules/mv/mv.c \
+	cmds/modules/printf/printf.c \
+	cmds/modules/echo/echo.c \
 	cmds/modules/mount/mount.c \
 	cmds/modules/unmount/unmount.c \
Index: uspace/app/bdsh/TODO
===================================================================
--- uspace/app/bdsh/TODO	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/TODO	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -30,6 +30,4 @@
 * Add wrappers for signal, sigaction to make ports to modules easier
 
-* Add 'echo' and 'printf' modules.
-
 Regarding POSIX:
 ----------------
Index: uspace/app/bdsh/cmds/builtins/cd/cd.c
===================================================================
--- uspace/app/bdsh/cmds/builtins/cd/cd.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/builtins/cd/cd.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -41,4 +41,29 @@
 static const char *cmdname = "cd";
 
+/* Previous directory variables.
+ *
+ * Declaring them static to avoid many "== NULL" checks.
+ * PATH_MAX is not that big to cause any problems with memory overhead.
+ */
+static char previous_directory[PATH_MAX] = "";
+static char previous_directory_tmp[PATH_MAX];
+static bool previous_directory_valid = true;
+static bool previous_directory_set = false;
+
+static int chdir_and_remember(const char *new_dir) {
+
+	char *ok = getcwd(previous_directory_tmp, PATH_MAX);
+	previous_directory_valid = ok != NULL;
+	previous_directory_set = true;
+
+	int rc = chdir(new_dir);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	str_cpy(previous_directory, PATH_MAX, previous_directory_tmp);
+	return EOK;
+}
+
 void help_cmd_cd(unsigned int level)
 {
@@ -55,4 +80,5 @@
 }
 
+
 /* This is a very rudamentary 'cd' command. It is not 'link smart' (yet) */
 
@@ -62,4 +88,15 @@
 
 	argc = cli_count_args(argv);
+
+	/* Handle cd -- -. Override to switch to a directory named '-' */
+	bool hyphen_override = false;
+	char *target_directory = argv[1];
+	if (argc == 3) {
+		if (!str_cmp(argv[1], "--")) {
+			hyphen_override = true;
+			argc--;
+			target_directory = argv[2];
+		}
+	}
 
 	/* We don't yet play nice with whitespace, a getopt implementation should
@@ -79,8 +116,27 @@
 	}
 
-	/* We have the correct # of arguments
-     * TODO: handle tidle (~) expansion? */
+	/* We have the correct # of arguments */
+	// TODO: handle tidle (~) expansion? */
 
-	rc = chdir(argv[1]);
+	/* Handle 'cd -' first. */
+	if (!str_cmp(target_directory, "-") && !hyphen_override) {
+		if (!previous_directory_valid) {
+			cli_error(CL_EFAIL, "Cannot switch to previous directory");
+			return CMD_FAILURE;
+		}
+		if (!previous_directory_set) {
+			cli_error(CL_EFAIL, "No previous directory to switch to");
+			return CMD_FAILURE;
+		}
+		char *prev_dup = str_dup(previous_directory);
+		if (prev_dup == NULL) {
+			cli_error(CL_ENOMEM, "Cannot switch to previous directory");
+			return CMD_FAILURE;
+		}
+		rc = chdir_and_remember(prev_dup);
+		free(prev_dup);
+	} else {
+		rc = chdir_and_remember(target_directory);
+	}
 
 	if (rc == 0) {
@@ -93,8 +149,8 @@
 			break;
 		case ENOENT:
-			cli_error(CL_ENOENT, "Invalid directory `%s'", argv[1]);
+			cli_error(CL_ENOENT, "Invalid directory `%s'", target_directory);
 			break;
 		default:
-			cli_error(CL_EFAIL, "Unable to change to `%s'", argv[1]);
+			cli_error(CL_EFAIL, "Unable to change to `%s'", target_directory);
 			break;
 		}
Index: uspace/app/bdsh/cmds/modules/cat/cat.h
===================================================================
--- uspace/app/bdsh/cmds/modules/cat/cat.h	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/cat/cat.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -4,6 +4,4 @@
 /* Prototypes for the cat command, excluding entry points */
 
-static unsigned int cat_file(const char *, size_t, bool, off64_t, off64_t, bool);
-
 #endif /* CAT_H */
 
Index: uspace/app/bdsh/cmds/modules/cp/cp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cp/cp.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/cp/cp.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -30,4 +30,6 @@
 #include <stdlib.h>
 #include <unistd.h>
+#include <io/console.h>
+#include <io/keycode.h>
 #include <getopt.h>
 #include <str.h>
@@ -46,8 +48,10 @@
 
 static const char *cmdname = "cp";
+static console_ctrl_t *con;
 
 static struct option const long_options[] = {
 	{ "buffer", required_argument, 0, 'b' },
 	{ "force", no_argument, 0, 'f' },
+	{ "interactive", no_argument, 0, 'i'},
 	{ "recursive", no_argument, 0, 'r' },
 	{ "help", no_argument, 0, 'h' },
@@ -139,6 +143,39 @@
 }
 
+static bool get_user_decision(bool bdefault, const char *message, ...)
+{
+	va_list args;
+
+	va_start(args, message);
+	vprintf(message, args);
+	va_end(args);
+
+	while (true) {
+		kbd_event_t ev;
+		console_flush(con);
+		console_get_kbd_event(con, &ev);
+		if ((ev.type != KEY_PRESS)
+		    || (ev.mods & (KM_CTRL | KM_ALT)) != 0) {
+			continue;
+		}
+
+		switch(ev.key) {
+		case KC_Y:
+			printf("y\n");
+			return true;
+		case KC_N:
+			printf("n\n");
+			return false;
+		case KC_ENTER:
+			printf("%c\n", bdefault ? 'Y' : 'N');
+			return bdefault;
+		default:
+			break;
+		}
+	}
+}
+
 static int64_t do_copy(const char *src, const char *dest,
-    size_t blen, int vb, int recursive, int force)
+    size_t blen, int vb, int recursive, int force, int interactive)
 {
 	int r = -1;
@@ -192,8 +229,9 @@
 			/* e.g. cp file_name existing_file */
 
-			/* dest already exists, if force is set we will
-			 * try to remove it.
+			/* dest already exists, 
+			 * if force is set we will try to remove it.
+			 * if interactive is set user input is required.
 			 */
-			if (force) {
+			if (force && !interactive) {
 				if (unlink(dest_path)) {
 					printf("Unable to remove %s\n",
@@ -201,6 +239,21 @@
 					goto exit;
 				}
+			} else if (!force && interactive) {
+				bool overwrite = get_user_decision(false,
+				    "File already exists: %s. Overwrite? [y/N]: ",
+				    dest_path);
+				if (overwrite) {
+					printf("Overwriting file: %s\n", dest_path);
+					if (unlink(dest_path)) {
+						printf("Unable to remove %s\n", dest_path);
+						goto exit;
+					}
+				} else {
+					printf("Not overwriting file: %s\n", dest_path);
+					r = 0;
+					goto exit;
+				}
 			} else {
-				printf("file already exists: %s\n", dest_path);
+				printf("File already exists: %s\n", dest_path);
 				goto exit;
 			}
@@ -302,5 +355,5 @@
 			/* Recursively call do_copy() */
 			r = do_copy(src_dent, dest_dent, blen, vb, recursive,
-			    force);
+			    force, interactive);
 			if (r)
 				goto exit;
@@ -315,5 +368,4 @@
 	return r;
 }
-
 
 static int64_t copy_file(const char *src, const char *dest,
@@ -380,5 +432,6 @@
 	    "  -v, --version    Print version information and exit\n"
 	    "  -V, --verbose    Be annoyingly noisy about what's being done\n"
-	    "  -f, --force      Do not complain when <dest> exists\n"
+	    "  -f, --force      Do not complain when <dest> exists (overrides a previous -i)\n"
+	    "  -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n"
 	    "  -r, --recursive  Copy entire directories\n"
 	    "  -b, --buffer ## Set the read buffer size to ##\n";
@@ -397,12 +450,13 @@
 	unsigned int argc, verbose = 0;
 	int buffer = 0, recursive = 0;
-	int force = 0;
+	int force = 0, interactive = 0;
 	int c, opt_ind;
 	int64_t ret;
 
+	con = console_init(stdin, stdout);
 	argc = cli_count_args(argv);
 
 	for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
-		c = getopt_long(argc, argv, "hvVfrb:", long_options, &opt_ind);
+		c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
 		switch (c) { 
 		case 'h':
@@ -416,5 +470,10 @@
 			break;
 		case 'f':
+			interactive = 0;
 			force = 1;
+			break;
+		case 'i':
+			force = 0;
+			interactive = 1;
 			break;
 		case 'r':
@@ -426,4 +485,5 @@
 				    "(should be a number greater than zero)\n",
 				    cmdname);
+				console_done(con);
 				return CMD_FAILURE;
 			}
@@ -442,9 +502,12 @@
 		printf("%s: invalid number of arguments. Try %s --help\n",
 		    cmdname, cmdname);
+		console_done(con);
 		return CMD_FAILURE;
 	}
 
 	ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
-	    recursive, force);
+	    recursive, force, interactive);
+
+	console_done(con);
 
 	if (ret == 0)
Index: uspace/app/bdsh/cmds/modules/echo/echo.c
===================================================================
--- uspace/app/bdsh/cmds/modules/echo/echo.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/echo/echo.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 Alexander Prutkov
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "config.h"
+#include "util.h"
+#include "errors.h"
+#include "entry.h"
+#include "echo.h"
+#include "cmds.h"
+#include "errno.h"
+
+static const char *cmdname = "echo";
+
+void help_cmd_echo(unsigned int level)
+{
+	if (level == HELP_SHORT) {
+		printf("`%s' prints arguments as they are, followed by a new line.\n", cmdname);
+	} else {
+		help_cmd_echo(HELP_SHORT);
+		printf("Usage:  %s [arg ...]\n", cmdname);
+	}
+
+	return;
+}
+
+/* Main entry point for echo, accepts an array of arguments */
+int cmd_echo(char **argv)
+{
+	unsigned int argc;
+
+	for (argc = 1; argv[argc] != NULL; argc ++) {
+		printf("%s ", argv[argc]);
+	}
+	printf("\n");
+	return CMD_SUCCESS;
+
+}
+
Index: uspace/app/bdsh/cmds/modules/echo/echo.h
===================================================================
--- uspace/app/bdsh/cmds/modules/echo/echo.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/echo/echo.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,8 @@
+#ifndef ECHO_H
+#define ECHO_H
+
+/* Prototypes for the echo command, excluding entry points */
+
+
+#endif /* ECHO_H */
+
Index: uspace/app/bdsh/cmds/modules/echo/echo_def.h
===================================================================
--- uspace/app/bdsh/cmds/modules/echo/echo_def.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/echo/echo_def.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,7 @@
+{
+	"echo",
+	"Prints arguments as they are",
+	&cmd_echo,
+	&help_cmd_echo,
+},
+
Index: uspace/app/bdsh/cmds/modules/echo/entry.h
===================================================================
--- uspace/app/bdsh/cmds/modules/echo/entry.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/echo/entry.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,9 @@
+#ifndef ECHO_ENTRY_H
+#define ECHO_ENTRY_H
+
+/* Entry points for the echo command */
+extern int cmd_echo(char **);
+extern void help_cmd_echo(unsigned int);
+
+#endif /* ECHO_ENTRY_H */
+
Index: uspace/app/bdsh/cmds/modules/help/help.h
===================================================================
--- uspace/app/bdsh/cmds/modules/help/help.h	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/help/help.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -3,5 +3,4 @@
 
 /* Prototypes for the help command (excluding entry points) */
-static int is_mod_or_builtin(char *);
 
 #endif
Index: uspace/app/bdsh/cmds/modules/mkdir/mkdir.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mkdir/mkdir.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/mkdir/mkdir.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -36,4 +36,7 @@
 #include <stdarg.h>
 #include <str.h>
+#include <errno.h>
+#include <str_error.h>
+#include <vfs/vfs.h>
 
 #include "config.h"
@@ -83,98 +86,80 @@
 /* This is kind of clunky, but effective for now */
 static unsigned int
-create_directory(const char *path, unsigned int p)
+create_directory(const char *user_path, bool create_parents)
 {
-	DIR *dirp;
-	char *tmp = NULL, *buff = NULL, *wdp = NULL;
-	char *dirs[255];
-	unsigned int absolute = 0, i = 0, ret = 0;
-
-	/* Its a good idea to allocate path, plus we (may) need a copy of
-	 * path to tokenize if parents are specified */
-	if (NULL == (tmp = str_dup(path))) {
+	/* Ensure we would always work with absolute and canonified path. */
+	char *path = absolutize(user_path, NULL);
+	if (path == NULL) {
 		cli_error(CL_ENOMEM, "%s: path too big?", cmdname);
 		return 1;
 	}
 
-	if (NULL == (wdp = (char *) malloc(PATH_MAX))) {
-		cli_error(CL_ENOMEM, "%s: could not alloc cwd", cmdname);
-		free(tmp);
-		return 1;
-	}
-
-	/* The only reason for wdp is to be (optionally) verbose */
-	getcwd(wdp, PATH_MAX);
-
-	/* Typical use without specifying the creation of parents */
-	if (p == 0) {
-		dirp = opendir(tmp);
-		if (dirp) {
-			cli_error(CL_EEXISTS, "%s: can not create %s, try -p", cmdname, path);
-			closedir(dirp);
-			goto finit;
-		}
-		if (-1 == (mkdir(tmp, 0))) {
-			cli_error(CL_EFAIL, "%s: could not create %s", cmdname, path);
-			goto finit;
-		}
-	}
-
-	/* Parents need to be created, path has to be broken up */
-
-	/* See if path[0] is a slash, if so we have to remember to append it */
-	if (tmp[0] == '/')
-		absolute = 1;
-
-	/* TODO: Canonify the path prior to tokenizing it, see below */
-	dirs[i] = strtok(tmp, "/");
-	while (dirs[i] && i < 255)
-		dirs[++i] = strtok(NULL, "/");
-
-	if (NULL == dirs[0])
-		return 1;
-
-	if (absolute == 1) {
-		asprintf(&buff, "/%s", dirs[0]);
-		mkdir(buff, 0);
-		chdir(buff);
-		free(buff);
-		getcwd(wdp, PATH_MAX);
-		i = 1;
+	int rc;
+	int ret = 0;
+
+	if (!create_parents) {
+		rc = mkdir(path, 0);
+		if (rc != EOK) {
+			cli_error(CL_EFAIL, "%s: could not create %s (%s)",
+			    cmdname, path, str_error(rc));
+			ret = 1;
+		}
 	} else {
-		i = 0;
-	}
-
-	while (dirs[i] != NULL) {
-		/* Sometimes make or scripts conjoin odd paths. Account for something
-		 * like this: ../../foo/bar/../foo/foofoo/./bar */
-		if (!str_cmp(dirs[i], "..") || !str_cmp(dirs[i], ".")) {
-			if (0 != (chdir(dirs[i]))) {
-				cli_error(CL_EFAIL, "%s: impossible path: %s",
-					cmdname, path);
-				ret ++;
-				goto finit;
-			}
-			getcwd(wdp, PATH_MAX);
-		} else {
-			if (-1 == (mkdir(dirs[i], 0))) {
-				cli_error(CL_EFAIL,
-					"%s: failed at %s/%s", wdp, dirs[i]);
-				ret ++;
-				goto finit;
-			}
-			if (0 != (chdir(dirs[i]))) {
-				cli_error(CL_EFAIL, "%s: failed creating %s\n",
-					cmdname, dirs[i]);
-				ret ++;
+		/* Create the parent directories as well. */
+		size_t off = 0;
+		while (1) {
+			size_t prev_off = off;
+			wchar_t cur_char = str_decode(path, &off, STR_NO_LIMIT);
+			if ((cur_char == 0) || (cur_char == U_SPECIAL)) {
 				break;
 			}
-		}
-		i++;
-	}
-	goto finit;
-
-finit:
-	free(wdp);
-	free(tmp);
+			if (cur_char != '/') {
+				continue;
+			}
+			if (prev_off == 0) {
+				continue;
+			}
+			/*
+			 * If we are here, it means that:
+			 * - we found /
+			 * - it is not the first / (no need to create root
+			 *   directory)
+			 *
+			 * We would now overwrite the / with 0 to terminate the
+			 * string (that shall be okay because we are
+			 * overwriting at the beginning of UTF sequence).
+			 * That would allow us to create the directories
+			 * in correct nesting order.
+			 *
+			 * Notice that we ignore EEXIST errors as some of
+			 * the parent directories may already exist.
+			 */
+			char slash_char = path[prev_off];
+			path[prev_off] = 0;
+			rc = mkdir(path, 0);
+			if (rc == EEXIST) {
+				rc = EOK;
+			}
+
+			if (rc != EOK) {
+				cli_error(CL_EFAIL, "%s: could not create %s (%s)",
+				    cmdname, path, str_error(rc));
+				ret = 1;
+				goto leave;
+			}
+
+			path[prev_off] = slash_char;
+		}
+		/* Create the final directory. */
+		rc = mkdir(path, 0);
+		if (rc != EOK) {
+			cli_error(CL_EFAIL, "%s: could not create %s (%s)",
+			    cmdname, path, str_error(rc));
+			ret = 1;
+		}
+	}
+
+leave:
+	free(path);
 	return ret;
 }
@@ -182,8 +167,7 @@
 int cmd_mkdir(char **argv)
 {
-	unsigned int argc, create_parents = 0, i, ret = 0, follow = 0;
-	unsigned int verbose = 0;
+	unsigned int argc, i, ret = 0;
+	bool create_parents = false, follow = false, verbose = false;
 	int c, opt_ind;
-	char *cwd;
 
 	argc = cli_count_args(argv);
@@ -193,8 +177,8 @@
 		switch (c) {
 		case 'p':
-			create_parents = 1;
+			create_parents = true;
 			break;
 		case 'v':
-			verbose = 1;
+			verbose = true;
 			break;
 		case 'h':
@@ -205,5 +189,5 @@
 			return CMD_SUCCESS;
 		case 'f':
-			follow = 1;
+			follow = true;
 			break;
 		case 'm':
@@ -221,14 +205,6 @@
 	}
 
-	if (NULL == (cwd = (char *) malloc(PATH_MAX))) {
-		cli_error(CL_ENOMEM, "%s: could not allocate cwd", cmdname);
-		return CMD_FAILURE;
-	}
-
-	memset(cwd, 0, sizeof(cwd));
-	getcwd(cwd, PATH_MAX);
-
 	for (i = optind; argv[i] != NULL; i++) {
-		if (verbose == 1)
+		if (verbose)
 			printf("%s: creating %s%s\n",
 				cmdname, argv[i],
@@ -237,8 +213,7 @@
 	}
 
-	if (follow == 0)
-		chdir(cwd);
-
-	free(cwd);
+	if (follow && (argv[optind] != NULL)) {
+		chdir(argv[optind]);
+	}
 
 	if (ret)
Index: uspace/app/bdsh/cmds/modules/mkdir/mkdir.h
===================================================================
--- uspace/app/bdsh/cmds/modules/mkdir/mkdir.h	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/mkdir/mkdir.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -4,5 +4,4 @@
 /* Prototypes for the mkdir command, excluding entry points */
 
-static unsigned int create_directory(const char *, unsigned int);
 #endif /* MKDIR_H */
 
Index: uspace/app/bdsh/cmds/modules/modules.h
===================================================================
--- uspace/app/bdsh/cmds/modules/modules.h	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/modules.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -61,4 +61,6 @@
 #include "unmount/entry.h"
 #include "kcon/entry.h"
+#include "printf/entry.h"
+#include "echo/entry.h"
 
 /* Each .def function fills the module_t struct with the individual name, entry
@@ -82,4 +84,6 @@
 #include "unmount/unmount_def.h"
 #include "kcon/kcon_def.h"
+#include "printf/printf_def.h"
+#include "echo/echo_def.h"
 
 	{NULL, NULL, NULL, NULL}
Index: uspace/app/bdsh/cmds/modules/printf/TODO
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/TODO	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/printf/TODO	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,13 @@
+At this stage printf is very young and limited, as it hopefully
+will be developed along with BDSH. Functionality is heavily related
+on libc printf already available. BDSH printf implements only 3 format  
+flags, with no options/precision/width modifiers allowed. Also, 
+one output control is available - '\n'. '\' character stands
+as an escape character, i.e. every special character after it 
+will be treated as general char and will be printed out.
+What's missing:
+ * Error checking for format string.
+ * Add more output controls (\t \r ...)
+ * Add width/precision options for number printings
+ * Add more format flags (%f %b ...)
+ 
Index: uspace/app/bdsh/cmds/modules/printf/entry.h
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/entry.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/printf/entry.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,9 @@
+#ifndef PRINTF_ENTRY_H
+#define PRINTF_ENTRY_H
+
+/* Entry points for the printf command */
+extern int cmd_printf(char **);
+extern void help_cmd_printf(unsigned int);
+
+#endif /* PRINTF_ENTRY_H */
+
Index: uspace/app/bdsh/cmds/modules/printf/printf.c
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/printf.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/printf/printf.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2012 Alexander Prutkov
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "config.h"
+#include "util.h"
+#include "errors.h"
+#include "entry.h"
+#include "printf.h"
+#include "cmds.h"
+#include "str.h"
+
+static const char *cmdname = "printf";
+
+/* Dispays help for printf in various levels */
+void help_cmd_printf(unsigned int level)
+{
+	if (level == HELP_SHORT) {
+		printf("`%s' prints formatted data.\n", cmdname);
+	} else {
+		help_cmd_printf(HELP_SHORT);
+		printf(
+		    "Usage:  %s FORMAT [ARGS ...] \n"
+		    "Prints ARGS according to FORMAT. Number of expected arguments in\n"
+		    "FORMAT must be equals to the number of ARGS. Currently supported\n"
+		    "format flags are:\n",
+		    cmdname);
+	}
+
+	
+	return;
+}
+
+/** Print a formatted data with lib printf.
+ * 
+ * Currently available format flags are:
+ * '%d' - integer.
+ * '%u' - unsigned integer.
+ * '%s' - null-terminated string.
+ ***** 
+ * @param ch  formatted flag.
+ * @param arg string with data to print.
+ */
+static int print_arg(wchar_t ch, const char* arg)
+{
+	switch(ch) {
+	case 'd':
+		printf("%d", (int)(strtol(arg, NULL, 10)));
+		break;
+	case 'u':
+		printf("%u", (unsigned int)(strtoul(arg, NULL, 10)));
+		break;
+	case 's':
+		printf("%s", arg);
+		break;
+	default:
+		return CMD_FAILURE;
+	}
+	return CMD_SUCCESS;
+}
+
+/** Process a control character.
+ * 
+ * Currently available characters are:
+ * '\n' - new line.
+ ***** 
+ * @param ch  Control character.
+ */
+static int process_ctl(wchar_t ch)
+{
+	switch(ch) {
+	case 'n':
+		printf("\n");
+		break;
+	default:
+		return CMD_FAILURE;
+	}
+	return CMD_SUCCESS;
+}
+
+
+/** Prints formatted data. 
+ *
+ * Accepted format flags:
+ * %d - print an integer
+ * %u - print an unsigned integer
+ * %s - print a null terminated string
+ *****
+ * Accepted output controls:
+ * \n - new line
+ */
+int cmd_printf(char **argv)
+{
+	unsigned int argc;
+	char* fmt;
+	size_t pos, fmt_sz;
+	wchar_t ch;
+	bool esc_flag = false;
+	unsigned int carg;     // Current argument
+
+	/* Count the arguments */
+	for (argc = 0; argv[argc] != NULL; argc ++);
+
+	if (argc < 2) {
+		printf("Usage:  %s FORMAT [ARGS ...] \n", cmdname);
+		return CMD_SUCCESS;
+	}
+
+	fmt = argv[1];
+	fmt_sz = str_size(fmt);
+	pos = 0;
+	carg = 2;
+
+	while ((ch = str_decode(fmt, &pos, fmt_sz))) {
+		switch(ch) {
+
+		case '\\':
+			if (esc_flag) 
+				goto emit;
+			esc_flag = true;
+			break;
+
+		case '%':
+			if (esc_flag) 
+				goto emit;
+			ch = str_decode(fmt, &pos, fmt_sz);
+			if (!ch) { 
+				putchar('%');
+				break;
+			}
+			if (carg == argc) {
+				printf("\nBad parameter number. Aborted.\n");
+				return CMD_FAILURE;
+			}
+			print_arg(ch, argv[carg]);
+			++carg;
+			break;
+
+		default:
+			if (esc_flag) {
+				process_ctl(ch);
+				esc_flag = false;
+				break;
+			}
+			putchar(ch);	
+			break;
+
+		emit:
+			putchar(ch);
+			esc_flag = false;
+		}
+	}	
+
+	return CMD_SUCCESS;
+}
Index: uspace/app/bdsh/cmds/modules/printf/printf.h
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/printf.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/printf/printf.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,8 @@
+#ifndef PRINTF_H
+#define PRINTF_H
+
+/* Prototypes for the printf command, excluding entry points */
+
+
+#endif /* PRINTF_H */
+
Index: uspace/app/bdsh/cmds/modules/printf/printf_def.h
===================================================================
--- uspace/app/bdsh/cmds/modules/printf/printf_def.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
+++ uspace/app/bdsh/cmds/modules/printf/printf_def.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -0,0 +1,7 @@
+{
+	"printf",
+	"Format and print data",
+	&cmd_printf,
+	&help_cmd_printf,
+},
+
Index: uspace/app/bdsh/cmds/modules/rm/rm.c
===================================================================
--- uspace/app/bdsh/cmds/modules/rm/rm.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/rm/rm.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -46,6 +46,4 @@
 #define RM_VERSION "0.0.1"
 
-static rm_job_t rm;
-
 static struct option const long_options[] = {
 	{ "help", no_argument, 0, 'h' },
@@ -57,4 +55,38 @@
 };
 
+/* Return values for rm_scope() */
+#define RM_BOGUS 0
+#define RM_FILE  1
+#define RM_DIR   2
+
+/* Flags for rm_update() */
+#define _RM_ENTRY   0
+#define _RM_ADVANCE 1
+#define _RM_REWIND  2
+#define _RM_EXIT    3
+
+/* A simple job structure */
+typedef struct {
+	/* Options set at run time */
+	unsigned int force;      /* -f option */
+	unsigned int recursive;  /* -r option */
+	unsigned int safe;       /* -s option */
+
+	/* Keeps track of the job in progress */
+	int advance; /* How far deep we've gone since entering */
+	DIR *entry;  /* Entry point to the tree being removed */
+	char *owd;   /* Where we were when we invoked rm */
+	char *cwd;   /* Current directory being transversed */
+	char *nwd;   /* Next directory to be transversed */
+
+	/* Counters */
+	int f_removed; /* Number of files unlinked */
+	int d_removed; /* Number of directories unlinked */
+} rm_job_t;
+
+static rm_job_t rm;
+
+static unsigned int rm_recursive(const char *);
+
 static unsigned int rm_start(rm_job_t *rm)
 {
@@ -95,4 +127,33 @@
 	if (NULL != rm->cwd)
 		free(rm->cwd);
+}
+
+static unsigned int rm_single(const char *path)
+{
+	if (unlink(path)) {
+		cli_error(CL_EFAIL, "rm: could not remove file %s", path);
+		return 1;
+	}
+	return 0;
+}
+
+static unsigned int rm_scope(const char *path)
+{
+	int fd;
+	DIR *dirp;
+
+	dirp = opendir(path);
+	if (dirp) {
+		closedir(dirp);
+		return RM_DIR;
+	}
+
+	fd = open(path, O_RDONLY);
+	if (fd > 0) {
+		close(fd);
+		return RM_FILE;
+	}
+
+	return RM_BOGUS;
 }
 
@@ -154,33 +215,4 @@
 
 	return ret + 1;
-}
-
-static unsigned int rm_single(const char *path)
-{
-	if (unlink(path)) {
-		cli_error(CL_EFAIL, "rm: could not remove file %s", path);
-		return 1;
-	}
-	return 0;
-}
-
-static unsigned int rm_scope(const char *path)
-{
-	int fd;
-	DIR *dirp;
-
-	dirp = opendir(path);
-	if (dirp) {
-		closedir(dirp);
-		return RM_DIR;
-	}
-
-	fd = open(path, O_RDONLY);
-	if (fd > 0) {
-		close(fd);
-		return RM_FILE;
-	}
-
-	return RM_BOGUS;
 }
 
Index: uspace/app/bdsh/cmds/modules/rm/rm.h
===================================================================
--- uspace/app/bdsh/cmds/modules/rm/rm.h	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/rm/rm.h	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -2,41 +2,5 @@
 #define RM_H
 
-/* Return values for rm_scope() */
-#define RM_BOGUS 0
-#define RM_FILE  1
-#define RM_DIR   2
-
-/* Flags for rm_update() */
-#define _RM_ENTRY   0
-#define _RM_ADVANCE 1
-#define _RM_REWIND  2
-#define _RM_EXIT    3
-
-/* A simple job structure */
-typedef struct {
-	/* Options set at run time */
-	unsigned int force;      /* -f option */
-	unsigned int recursive;  /* -r option */
-	unsigned int safe;       /* -s option */
-
-	/* Keeps track of the job in progress */
-	int advance; /* How far deep we've gone since entering */
-	DIR *entry;  /* Entry point to the tree being removed */
-	char *owd;   /* Where we were when we invoked rm */
-	char *cwd;   /* Current directory being transversed */
-	char *nwd;   /* Next directory to be transversed */
-
-	/* Counters */
-	int f_removed; /* Number of files unlinked */
-	int d_removed; /* Number of directories unlinked */
-} rm_job_t;
-
-
 /* Prototypes for the rm command, excluding entry points */
-static unsigned int rm_start(rm_job_t *);
-static void rm_end(rm_job_t *rm);
-static unsigned int rm_recursive(const char *);
-static unsigned int rm_single(const char *);
-static unsigned int rm_scope(const char *);
 
 #endif /* RM_H */
Index: uspace/app/bdsh/cmds/modules/sleep/sleep.c
===================================================================
--- uspace/app/bdsh/cmds/modules/sleep/sleep.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/bdsh/cmds/modules/sleep/sleep.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -27,6 +27,8 @@
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 #include "config.h"
 #include "util.h"
@@ -41,7 +43,68 @@
 void help_cmd_sleep(unsigned int level)
 {
-	printf("This is the %s help for '%s'.\n",
-		level ? EXT_HELP : SHORT_HELP, cmdname);
+	if (level == HELP_SHORT) {
+		printf("`%s' pauses for a given time interval\n", cmdname);
+	} else {
+		help_cmd_sleep(HELP_SHORT);
+		printf(
+		    "Usage:  %s <duration>\n"
+		    "The duration is a decimal number of seconds.\n",
+		    cmdname);
+	}
+
 	return;
+}
+
+/** Convert string containing decimal seconds to useconds_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param result Result of the conversion.
+ * @return EOK if conversion was successful.
+ */
+static int decimal_to_useconds(const char *nptr, useconds_t *result)
+{
+	int ret;
+	uint64_t whole_seconds;
+	uint64_t frac_seconds;
+	char *endptr;
+
+	/* Check for whole seconds */
+	if (*nptr == '.') {
+		whole_seconds = 0;
+		endptr = (char *)nptr;
+	} else {
+		ret = str_uint64_t(nptr, &endptr, 10, false, &whole_seconds);
+		if (ret != EOK)
+			return ret;
+	}
+
+	/* Check for fractional seconds */
+	if (*endptr == '\0') {
+		frac_seconds = 0;
+	} else if (*endptr == '.' && endptr[1] == '\0') {
+		frac_seconds = 0;
+	} else if (*endptr == '.') {
+		nptr = endptr + 1;
+		ret = str_uint64_t(nptr, &endptr, 10, true, &frac_seconds);
+		if (ret != EOK)
+			return ret;
+
+		int ndigits = endptr - nptr;
+		for (; ndigits < 6; ndigits++) 
+			frac_seconds *= 10;
+		for (; ndigits > 6; ndigits--)
+			frac_seconds /= 10;
+	} else {
+		return EINVAL;
+	}
+
+	/* Check for overflow */
+	useconds_t total = whole_seconds * 1000000 + frac_seconds;
+	if (total / 1000000 != whole_seconds)
+		return EOVERFLOW;
+
+	*result = total;
+
+	return EOK;
 }
 
@@ -49,21 +112,24 @@
 int cmd_sleep(char **argv)
 {
+	int ret;
 	unsigned int argc;
-	unsigned int i;
+	useconds_t duration;
 
 	/* Count the arguments */
-	for (argc = 0; argv[argc] != NULL; argc ++);
+	argc = cli_count_args(argv);
 
-	printf("%s %s\n", TEST_ANNOUNCE, cmdname);
-	printf("%d arguments passed to %s", argc - 1, cmdname);
-
-	if (argc < 2) {
-		printf("\n");
-		return CMD_SUCCESS;
+	if (argc != 2) {
+		printf("%s - incorrect number of arguments. Try `help %s'\n",
+		    cmdname, cmdname);
+		return CMD_FAILURE;
 	}
 
-	printf(":\n");
-	for (i = 1; i < argc; i++)
-		printf("[%d] -> %s\n", i, argv[i]);
+	ret = decimal_to_useconds(argv[1], &duration);
+	if (ret != EOK) {
+		printf("%s - invalid duration.\n", cmdname);
+		return CMD_FAILURE;
+	}
+
+	(void) usleep(duration);
 
 	return CMD_SUCCESS;
Index: uspace/app/edit/edit.c
===================================================================
--- uspace/app/edit/edit.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/edit/edit.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -118,4 +118,5 @@
 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);
 
@@ -139,7 +140,12 @@
 static void caret_update(void);
 static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
+static void caret_move_word_left(void);
+static void caret_move_word_right(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_sel_prev_word(void);
+static void selection_sel_next_word(void);
 static void selection_get_points(spt_t *pa, spt_t *pb);
 static void selection_delete(void);
@@ -149,4 +155,9 @@
 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 int tag_cmp(tag_t const *a, tag_t const *b);
 static int spt_cmp(spt_t const *a, spt_t const *b);
@@ -232,4 +243,8 @@
 			     (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);
@@ -376,4 +391,30 @@
 	case KC_A:
 		selection_sel_all();
+		break;
+	case KC_W:
+		if (selection_active())
+			break;
+		selection_sel_prev_word();
+		selection_delete();
+		break;
+	case KC_RIGHT:
+		caret_move_word_right();
+		break;
+	case KC_LEFT:
+		caret_move_word_left();
+		break;
+	default:
+		break;
+	}
+}
+
+static void key_handle_shift_ctrl(kbd_event_t const *ev)
+{
+	switch(ev->key) {
+	case KC_LEFT:
+		selection_sel_prev_word();
+		break;
+	case KC_RIGHT:
+		selection_sel_next_word();
 		break;
 	default:
@@ -970,5 +1011,12 @@
 	/* 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);
@@ -998,4 +1046,36 @@
 }
 
+static void caret_move_word_left(void) 
+{
+	spt_t pt;
+
+	do {
+		caret_move(0, -1, dir_before);
+
+		tag_get_pt(&pane.caret_pos, &pt);
+
+		sheet_remove_tag(&doc.sh, &pane.sel_start);
+		sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
+	} while (!pt_is_word_beginning(&pt));
+
+	pane.rflags |= REDRAW_TEXT;
+}
+
+static void caret_move_word_right(void) 
+{
+	spt_t pt;
+
+	do {
+		caret_move(0, 0, dir_after);
+
+		tag_get_pt(&pane.caret_pos, &pt);
+
+		sheet_remove_tag(&doc.sh, &pane.sel_start);
+		sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
+	} while (!pt_is_word_beginning(&pt));
+
+	pane.rflags |= REDRAW_TEXT;
+}
+
 /** Check for non-empty selection. */
 static bool selection_active(void)
@@ -1045,4 +1125,5 @@
 }
 
+/** Select all text in the editor */
 static void selection_sel_all(void)
 {
@@ -1051,11 +1132,52 @@
 	pt_get_sof(&spt);
 	pt_get_eof(&ept);
+
+	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, &spt, &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, &ept, &pane.caret_pos);
+	sheet_place_tag(&doc.sh, &pb, &pane.caret_pos);
 
 	pane.rflags |= REDRAW_TEXT;
 	caret_update();
+}
+
+/** Add the previous word to the selection */
+static void selection_sel_prev_word(void)
+{
+	spt_t cpt, wpt, spt, ept;
+
+	selection_get_points(&spt, &ept);
+
+	tag_get_pt(&pane.caret_pos, &cpt);
+	caret_move_word_left();
+	tag_get_pt(&pane.caret_pos, &wpt);
+
+	if (spt_cmp(&spt, &cpt) == 0)
+		selection_sel_range(ept, wpt);
+	else
+		selection_sel_range(spt, wpt);
+}
+
+/** Add the next word to the selection */
+static void selection_sel_next_word(void)
+{
+	spt_t cpt, wpt, spt, ept;
+
+	selection_get_points(&spt, &ept);
+
+	tag_get_pt(&pane.caret_pos, &cpt);
+	caret_move_word_right();
+	tag_get_pt(&pane.caret_pos, &wpt);
+
+	if (spt_cmp(&ept, &cpt) == 0)
+		selection_sel_range(spt, wpt);
+	else
+		selection_sel_range(ept, wpt);
 }
 
@@ -1117,4 +1239,128 @@
 
 	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/app/edit/sheet.c
===================================================================
--- uspace/app/edit/sheet.c	(revision c19667106913b684f8620943caecb3fdab81ea38)
+++ uspace/app/edit/sheet.c	(revision db675dd70825d2c8a787f0a80caff4c910e5239c)
@@ -264,5 +264,5 @@
 	sheet_get_cell_pt(sh, &coord, dir_before, &pt);
 	spt_get_coord(&pt, &coord);
-	*length = coord.column - 1;
+	*length = coord.column;
 }
 
