Index: uspace/app/bdsh/cmds/modules/cp/cp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 6348376a1fa240dbc66b6bbe4389cfeb07aea59c)
+++ uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 20a3465b8133a4f1f5c4b5a50daf07cb90a66462)
@@ -33,4 +33,6 @@
 #include <str.h>
 #include <fcntl.h>
+#include <sys/stat.h>
+#include <dirent.h>
 #include "config.h"
 #include "util.h"
@@ -55,4 +57,37 @@
 };
 
+typedef enum {
+	TYPE_NONE,
+	TYPE_FILE,
+	TYPE_DIR
+} dentry_type_t;
+
+static int64_t copy_file(const char *src, const char *dest,
+    size_t blen, int vb);
+
+/** Get the type of a directory entry.
+ *
+ * @param path	Path of the directory entry.
+ *
+ * @return TYPE_DIR if the dentry is a directory.
+ * @return TYPE_FILE if the dentry is a file.
+ * @return TYPE_NONE if the dentry does not exists.
+ */
+static dentry_type_t get_type(const char *path)
+{
+	struct stat s;
+
+	int r = stat(path, &s);
+
+	if (r)
+		return TYPE_NONE;
+	else if (s.is_directory)
+		return TYPE_DIR;
+	else if (s.is_file)
+		return TYPE_FILE;
+
+	return TYPE_NONE;
+}
+
 static int strtoint(const char *s1)
 {
@@ -67,4 +102,218 @@
 	return (int) t1;
 }
+
+/** Get the last component of a path.
+ *
+ * e.g. /data/a  ---> a
+ *
+ * @param path	Pointer to the path.
+ *
+ * @return	Pointer to the last component or to the path itself.
+ */
+static char *get_last_path_component(char *path)
+{
+	char *ptr;
+
+	ptr = str_rchr(path, '/');
+	if (!ptr)
+		return path;
+	else
+		return ptr + 1;
+}
+
+/** Merge two paths together.
+ *
+ * e.g. (path1 = /data/dir, path2 = a/b) --> /data/dir/a/b
+ *
+ * @param path1		Path to which path2 will be appended.
+ * @param path1_size	Size of the path1 buffer.
+ * @param path2		Path that will be appended to path1.
+ */
+static void merge_paths(char *path1, size_t path1_size, char *path2)
+{
+	const char *delim = "/";
+
+	str_rtrim(path1, '/');
+	str_append(path1, path1_size, delim);
+	str_append(path1, path1_size, path2);
+}
+
+static int64_t do_copy(const char *src, const char *dest,
+    size_t blen, int vb, int recursive, int force)
+{
+	int r = -1;
+	char dest_path[PATH_MAX];
+	char src_path[PATH_MAX];
+	DIR *dir = NULL;
+	struct dirent *dp;
+
+	dentry_type_t src_type = get_type(src);
+	dentry_type_t dest_type = get_type(dest);
+
+	const size_t src_len = str_size(src);
+
+	if (src_type == TYPE_FILE) {
+		char *src_fname;
+
+		/* Initialize the src_path with the src argument */
+		str_cpy(src_path, src_len + 1, src);
+		str_rtrim(src_path, '/');
+		
+		/* Get the last component name from the src path */
+		src_fname = get_last_path_component(src_path);
+		
+		/* Initialize dest_path with the dest argument */
+		str_cpy(dest_path, PATH_MAX, dest);
+
+		if (dest_type == TYPE_DIR) {
+			/* e.g. cp file_name /data */
+			/* e.g. cp file_name /data/ */
+			
+			/* dest is a directory,
+			 * append the src filename to it.
+			 */
+			merge_paths(dest_path, PATH_MAX, src_fname);
+			dest_type = get_type(dest_path);
+		} else if (dest_type == TYPE_NONE) {
+			if (dest_path[str_size(dest_path) - 1] == '/') {
+				/* e.g. cp /textdemo /data/dirnotexists/ */
+
+				printf("The dest directory %s does not exists",
+				    dest_path);
+				goto exit;
+			}
+		}
+
+		if (dest_type == TYPE_DIR) {
+			printf("Cannot overwrite existing directory %s\n",
+			    dest_path);
+			goto exit;
+		} else if (dest_type == TYPE_FILE) {
+			/* e.g. cp file_name existing_file */
+
+			/* dest already exists, if force is set we will
+			 * try to remove it.
+			 */
+			if (force) {
+				if (unlink(dest_path)) {
+					printf("Unable to remove %s\n",
+					    dest_path);
+					goto exit;
+				}
+			} else {
+				printf("file already exists: %s\n", dest_path);
+				goto exit;
+			}
+		}
+
+		/* call copy_file and exit */
+		r = (copy_file(src, dest_path, blen, vb) < 0);
+
+	} else if (src_type == TYPE_DIR) {
+		/* e.g. cp -r /x/srcdir /y/destdir/ */
+
+		if (!recursive) {
+			printf("Cannot copy the %s directory without the "
+			    "-r option\n", src);
+			goto exit;
+		} else if (dest_type == TYPE_FILE) {
+			printf("Cannot overwrite a file with a directory\n");
+			goto exit;
+		}
+
+		char *src_dirname;
+
+		/* Initialize src_path with the content of src */
+		str_cpy(src_path, src_len + 1, src);
+		str_rtrim(src_path, '/');
+
+		src_dirname = get_last_path_component(src_path);
+
+		str_cpy(dest_path, PATH_MAX, dest);
+
+		switch (dest_type) {
+		case TYPE_DIR:
+			if (str_cmp(src_dirname, "..") &&
+			    str_cmp(src_dirname, ".")) {
+				/* The last component of src_path is
+				 * not '.' or '..'
+				 */
+				merge_paths(dest_path, PATH_MAX, src_dirname);
+
+				if (mkdir(dest_path, 0) == -1) {
+					printf("Unable to create "
+					    "dest directory %s\n", dest_path);
+					goto exit;
+				}
+			}
+			break;
+		default:
+		case TYPE_NONE:
+			/* dest does not exists, this means the user wants
+			 * to specify the name of the destination directory
+			 *
+			 * e.g. cp -r /src /data/new_dir_src
+			 */
+			if (mkdir(dest_path, 0)) {
+				printf("Unable to create "
+				    "dest directory %s\n", dest_path);
+				goto exit;
+			}
+			break;
+		}
+
+		dir = opendir(src);
+		if (!dir) {
+			/* Something strange is happening... */
+			printf("Unable to open src %s directory\n", src);
+			goto exit;
+		}
+
+		/* Copy every single directory entry of src into the
+		 * destination directory.
+		 */
+		while ((dp = readdir(dir))) {
+			struct stat src_s;
+			struct stat dest_s;
+
+			char src_dent[PATH_MAX];
+			char dest_dent[PATH_MAX];
+
+			str_cpy(src_dent, PATH_MAX, src);
+			merge_paths(src_dent, PATH_MAX, dp->d_name);
+
+			str_cpy(dest_dent, PATH_MAX, dest_path);
+			merge_paths(dest_dent, PATH_MAX, dp->d_name);
+
+			/* Check if we are copying a directory into itself */
+			stat(src_dent, &src_s);
+			stat(dest_path, &dest_s);
+
+			if (dest_s.index == src_s.index &&
+			    dest_s.fs_handle == src_s.fs_handle) {
+				printf("Cannot copy a directory "
+				    "into itself\n");
+				goto exit;
+			}
+
+			if (vb)
+				printf("copy %s %s\n", src_dent, dest_dent);
+
+			/* Recursively call do_copy() */
+			r = do_copy(src_dent, dest_dent, blen, vb, recursive,
+			    force);
+			if (r)
+				goto exit;
+
+		}
+	} else
+		printf("Unable to open source file %s\n", src);
+
+exit:
+	if (dir)
+		closedir(dir);
+	return r;
+}
+
 
 static int64_t copy_file(const char *src, const char *dest,
@@ -127,17 +376,16 @@
 	static char helpfmt[] =
 	    "Usage:  %s [options] <source> <dest>\n"
-	    "Options: (* indicates not yet implemented)\n"
+	    "Options:\n"
 	    "  -h, --help       A short option summary\n"
 	    "  -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"
-	    "* -r, --recursive  Copy entire directories\n"
-	    "  -b, --buffer ## Set the read buffer size to ##\n"
-	    "Currently, %s is under development, some options may not work.\n";
+	    "  -V, --verbose    Be annoyingly noisy about what's being done\n"
+	    "  -f, --force      Do not complain when <dest> exists\n"
+	    "  -r, --recursive  Copy entire directories\n"
+	    "  -b, --buffer ## Set the read buffer size to ##\n";
 	if (level == HELP_SHORT) {
 		printf("`%s' copies files and directories\n", cmdname);
 	} else {
 		help_cmd_cp(HELP_SHORT);
-		printf(helpfmt, cmdname, cmdname);
+		printf(helpfmt, cmdname);
 	}
 
@@ -148,5 +396,6 @@
 {
 	unsigned int argc, verbose = 0;
-	int buffer = 0;
+	int buffer = 0, recursive = 0;
+	int force = 0;
 	int c, opt_ind;
 	int64_t ret;
@@ -167,6 +416,8 @@
 			break;
 		case 'f':
+			force = 1;
 			break;
 		case 'r':
+			recursive = 1;
 			break;
 		case 'b':
@@ -194,10 +445,8 @@
 	}
 
-	ret = copy_file(argv[optind], argv[optind + 1], buffer, verbose);
-
-	if (verbose)
-		printf("%" PRId64 " bytes copied\n", ret);
-
-	if (ret >= 0)
+	ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
+	    recursive, force);
+
+	if (ret == 0)
 		return CMD_SUCCESS;
 	else
