Index: kernel/generic/include/string.h
===================================================================
--- kernel/generic/include/string.h	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ kernel/generic/include/string.h	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -87,5 +87,5 @@
 extern void str_cpy(char *dest, size_t size, const char *src);
 extern void str_ncpy(char *dest, size_t size, const char *src, size_t n);
-extern void wstr_nstr(char *dst, const wchar_t *src, size_t size);
+extern void wstr_to_str(char *dest, size_t size, const wchar_t *src);
 
 extern char *str_chr(const char *str, wchar_t ch);
Index: kernel/generic/src/console/kconsole.c
===================================================================
--- kernel/generic/src/console/kconsole.c	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ kernel/generic/src/console/kconsole.c	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -289,5 +289,5 @@
 			
 			char tmp[STR_BOUNDS(MAX_CMDLINE)];
-			wstr_nstr(tmp, current + beg, position - beg + 1);
+			wstr_to_str(tmp, position - beg + 1, current + beg);
 			
 			int found;
@@ -665,5 +665,5 @@
 		
 		char cmdline[STR_BOUNDS(MAX_CMDLINE)];
-		wstr_nstr(cmdline, tmp, STR_BOUNDS(MAX_CMDLINE));
+		wstr_to_str(cmdline, STR_BOUNDS(MAX_CMDLINE), tmp);
 		
 		if ((!kcon) && (len == 4) && (str_lcmp(cmdline, "exit", 4) == 0))
Index: kernel/generic/src/lib/string.c
===================================================================
--- kernel/generic/src/lib/string.c	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ kernel/generic/src/lib/string.c	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -537,5 +537,5 @@
  * null-terminated and containing only complete characters.
  *
- * @param dst   Destination buffer.
+ * @param dest   Destination buffer.
  * @param count Size of the destination buffer (must be > 0).
  * @param src   Source string.
@@ -571,5 +571,5 @@
  * have to be null-terminated.
  *
- * @param dst   Destination buffer.
+ * @param dest   Destination buffer.
  * @param count Size of the destination buffer (must be > 0).
  * @param src   Source string.
@@ -596,36 +596,32 @@
 }
 
-/** Copy NULL-terminated wide string to string
- *
- * Copy source wide string @a src to destination buffer @a dst.
- * No more than @a size bytes are written. NULL-terminator is always
- * written after the last succesfully copied character (i.e. if the
- * destination buffer is has at least 1 byte, it will be always
- * NULL-terminated).
- *
- * @param src   Source wide string.
- * @param dst   Destination buffer.
- * @param count Size of the destination buffer.
- *
- */
-void wstr_nstr(char *dst, const wchar_t *src, size_t size)
-{
-	/* No space for the NULL-terminator in the buffer */
-	if (size == 0)
-		return;
-	
+/** Convert wide string to string.
+ *
+ * Convert wide string @a src to string. The output is written to the buffer
+ * specified by @a dest and @a size. @a size must be non-zero and the string
+ * written will always be well-formed.
+ *
+ * @param dest	Destination buffer.
+ * @param size	Size of the destination buffer.
+ * @param src	Source wide string.
+ */
+void wstr_to_str(char *dest, size_t size, const wchar_t *src)
+{
 	wchar_t ch;
-	size_t src_idx = 0;
-	size_t dst_off = 0;
+	size_t src_idx;
+	size_t dest_off;
+
+	/* There must be space for a null terminator in the buffer. */
+	ASSERT(size > 0);
+
+	src_idx = 0;
+	dest_off = 0;
 	
 	while ((ch = src[src_idx++]) != 0) {
-		if (chr_encode(ch, dst, &dst_off, size) != EOK)
+		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
 			break;
 	}
-	
-	if (dst_off >= size)
-		dst[size - 1] = 0;
-	else
-		dst[dst_off] = 0;
+
+	dest[dest_off] = '\0';
 }
 
Index: uspace/app/bdsh/input.c
===================================================================
--- uspace/app/bdsh/input.c	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ uspace/app/bdsh/input.c	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -134,13 +134,5 @@
 static char *tinput_get_str(tinput_t *ti)
 {
-	char *str;
-
-	str = malloc(STR_BOUNDS(ti->nc) + 1);
-	if (str == NULL)
-		return NULL;
-
-	wstr_nstr(str, ti->buffer, STR_BOUNDS(ti->nc) + 1);
-
-	return str;
+	return wstr_to_astr(ti->buffer);
 }
 
Index: uspace/app/edit/edit.c
===================================================================
--- uspace/app/edit/edit.c	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ uspace/app/edit/edit.c	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -36,4 +36,5 @@
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include <vfs/vfs.h>
@@ -101,10 +102,15 @@
 #define ED_INFTY 65536
 
+/** Maximum filename length that can be entered. */
+#define INFNAME_MAX_LEN 128
+
 static void key_handle_unmod(console_event_t const *ev);
 static void key_handle_ctrl(console_event_t const *ev);
 static int file_save(char const *fname);
+static void file_save_as(void);
 static int file_insert(char *fname);
 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 void pane_text_display(void);
 static void pane_row_display(void);
@@ -150,15 +156,15 @@
 
 	if (argc == 2) {
-		doc.file_name = argv[1];
+		doc.file_name = str_dup(argv[1]);
 	} else if (argc > 1) {
 		printf("Invalid arguments.\n");
 		return -2;
 	} else {
-		doc.file_name = "/edit.txt";
+		doc.file_name = NULL;
 	}
 
 	new_file = false;
 
-	if (file_insert(doc.file_name) != EOK)
+	if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
 		new_file = true;
 
@@ -170,6 +176,6 @@
 	pane_text_display();
 	pane_status_display();
-	if (new_file)
-		status_display("File not found. Created empty file.");
+	if (new_file && doc.file_name != NULL)
+		status_display("File not found. Starting empty file.");
 	pane_caret_display();
 
@@ -266,5 +272,11 @@
 		break;
 	case KC_S:
-		(void) file_save(doc.file_name);
+		if (doc.file_name != NULL)
+			file_save(doc.file_name);
+		else
+			file_save_as();
+		break;
+	case KC_E:
+		file_save_as();
 		break;
 	default:
@@ -272,5 +284,4 @@
 	}
 }
-
 
 /** Save the document. */
@@ -285,7 +296,104 @@
 
 	rc = file_save_range(fname, &sp, &ep);
-	status_display("File saved.");
+
+	switch (rc) {
+	case EINVAL:
+		status_display("Error opening file!");
+		break;
+	case EIO:
+		status_display("Error writing data!");
+		break;
+	default:
+		status_display("File saved.");
+		break;
+	}
 
 	return rc;
+}
+
+/** Change document name and save. */
+static void file_save_as(void)
+{
+	char *old_fname, *fname;
+	int rc;
+
+	old_fname = (doc.file_name != NULL) ? doc.file_name : "";
+	fname = filename_prompt("Save As", old_fname);
+	if (fname == NULL) {
+		status_display("Save cancelled.");
+		return;
+	}
+
+	rc = file_save(fname);
+	if (rc != EOK)
+		return;
+
+	if (doc.file_name != NULL)
+		free(doc.file_name);
+	doc.file_name = fname;
+}
+
+/** Ask for a file name. */
+static char *filename_prompt(char const *prompt, char const *init_value)
+{
+	console_event_t ev;
+	char *str;
+	wchar_t buffer[INFNAME_MAX_LEN + 1];
+	int max_len;
+	int nc;
+	bool done;
+
+	asprintf(&str, "%s: %s", prompt, init_value);
+	status_display(str);
+	console_goto(con, 1 + str_length(str), scr_rows - 1);
+	free(str);
+
+	console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
+
+	max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
+	str_to_wstr(buffer, max_len + 1, init_value);
+	nc = wstr_length(buffer);
+	done = false;
+
+	while (!done) {
+		console_get_event(con, &ev);
+
+		if (ev.type == KEY_PRESS) {
+			/* Handle key press. */
+			if (((ev.mods & KM_ALT) == 0) &&
+			     (ev.mods & KM_CTRL) != 0) {
+				;
+			} else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
+				switch (ev.key) {
+				case KC_ESCAPE:
+					return NULL;
+				case KC_BACKSPACE:
+					if (nc > 0) {
+						putchar('\b');
+						fflush(stdout);
+						--nc;
+					}
+					break;
+				case KC_ENTER:
+					done = true;
+					break;
+				default:
+					if (ev.c >= 32 && nc < max_len) {
+						putchar(ev.c);
+						fflush(stdout);
+						buffer[nc++] = ev.c;
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	buffer[nc] = '\0';
+	str = wstr_to_astr(buffer);
+
+	console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
+
+	return str;
 }
 
@@ -359,5 +467,6 @@
 	} while (!spt_equal(&bep, epos));
 
-	fclose(f);
+	if (fclose(f) != EOK)
+		return EIO;
 
 	return EOK;
@@ -473,4 +582,5 @@
 	spt_t caret_pt;
 	coord_t coord;
+	char *fname;
 	int n;
 
@@ -478,8 +588,10 @@
 	spt_get_coord(&caret_pt, &coord);
 
+	fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
+
 	console_goto(con, 0, scr_rows - 1);
 	console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
-	n = printf(" %d, %d: File '%s'. Ctrl-S Save  Ctrl-Q Quit",
-	    coord.row, coord.column, doc.file_name);
+	n = printf(" %d, %d: File '%s'. Ctrl-Q Quit  Ctrl-S Save  "
+	    "Ctrl-E Save As", coord.row, coord.column, fname);
 	printf("%*s", scr_columns - 1 - n, "");
 	fflush(stdout);
Index: uspace/lib/libc/generic/string.c
===================================================================
--- uspace/lib/libc/generic/string.c	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ uspace/lib/libc/generic/string.c	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -471,5 +471,5 @@
  * null-terminated and containing only complete characters.
  *
- * @param dst   Destination buffer.
+ * @param dest   Destination buffer.
  * @param count Size of the destination buffer (must be > 0).
  * @param src   Source string.
@@ -505,5 +505,5 @@
  * have to be null-terminated.
  *
- * @param dst   Destination buffer.
+ * @param dest   Destination buffer.
  * @param count Size of the destination buffer (must be > 0).
  * @param src   Source string.
@@ -537,5 +537,5 @@
  * null-terminated and containing only complete characters.
  *
- * @param dst   Destination buffer.
+ * @param dest   Destination buffer.
  * @param count Size of the destination buffer.
  * @param src   Source string.
@@ -549,43 +549,88 @@
 }
 
-/** Copy NULL-terminated wide string to string
- *
- * Copy source wide string @a src to destination buffer @a dst.
- * No more than @a size bytes are written. NULL-terminator is always
- * written after the last succesfully copied character (i.e. if the
- * destination buffer is has at least 1 byte, it will be always
- * NULL-terminated).
- *
- * @param src   Source wide string.
- * @param dst   Destination buffer.
- * @param count Size of the destination buffer.
- *
- */
-void wstr_nstr(char *dst, const wchar_t *src, size_t size)
-{
-	/* No space for the NULL-terminator in the buffer */
-	if (size == 0)
-		return;
-	
+/** Convert wide string to string.
+ *
+ * Convert wide string @a src to string. The output is written to the buffer
+ * specified by @a dest and @a size. @a size must be non-zero and the string
+ * written will always be well-formed.
+ *
+ * @param dest	Destination buffer.
+ * @param size	Size of the destination buffer.
+ * @param src	Source wide string.
+ */
+void wstr_to_str(char *dest, size_t size, const wchar_t *src)
+{
 	wchar_t ch;
-	size_t src_idx = 0;
-	size_t dst_off = 0;
-	
+	size_t src_idx;
+	size_t dest_off;
+
+	/* There must be space for a null terminator in the buffer. */
+	assert(size > 0);
+	
+	src_idx = 0;
+	dest_off = 0;
+
 	while ((ch = src[src_idx++]) != 0) {
-		if (chr_encode(ch, dst, &dst_off, size) != EOK)
+		if (chr_encode(ch, dest, &dest_off, size - 1) != EOK)
 			break;
 	}
-	
-	if (dst_off >= size)
-		dst[size - 1] = 0;
-	else
-		dst[dst_off] = 0;
-}
+
+	dest[dest_off] = '\0';
+}
+
+/** Convert wide string to new string.
+ *
+ * Convert wide string @a src to string. Space for the new string is allocated
+ * on the heap.
+ *
+ * @param src	Source wide string.
+ * @return	New string.
+ */
+char *wstr_to_astr(const wchar_t *src)
+{
+	char dbuf[STR_BOUNDS(1)];
+	char *str;
+	wchar_t ch;
+
+	size_t src_idx;
+	size_t dest_off;
+	size_t dest_size;
+
+	/* Compute size of encoded string. */
+
+	src_idx = 0;
+	dest_size = 0;
+
+	while ((ch = src[src_idx++]) != 0) {
+		dest_off = 0;
+		if (chr_encode(ch, dbuf, &dest_off, STR_BOUNDS(1)) != EOK)
+			break;
+		dest_size += dest_off;
+	}
+
+	str = malloc(dest_size + 1);
+	if (str == NULL)
+		return NULL;
+
+	/* Encode string. */
+
+	src_idx = 0;
+	dest_off = 0;
+
+	while ((ch = src[src_idx++]) != 0) {
+		if (chr_encode(ch, str, &dest_off, dest_size) != EOK)
+			break;
+	}
+
+	str[dest_size] = '\0';
+	return str;
+}
+
 
 /** Convert string to wide string.
  *
  * Convert string @a src to wide string. The output is written to the
- * buffer specified by @a dest and @a size, which must have non-zero
- * size. The output will always be null-terminated.
+ * buffer specified by @a dest and @a dlen. @a dlen must be non-zero
+ * and the wide string written will always be null-terminated.
  *
  * @param dest	Destination buffer.
Index: uspace/lib/libc/include/string.h
===================================================================
--- uspace/lib/libc/include/string.h	(revision da2bd08494c34132677cb4d768c8c263e26a0bff)
+++ uspace/lib/libc/include/string.h	(revision 8b5001bb7f39e1dc2e9579c578efe92de8592e88)
@@ -73,5 +73,6 @@
 extern void str_append(char *dest, size_t size, const char *src);
 
-extern void wstr_nstr(char *dst, const wchar_t *src, size_t size);
+extern void wstr_to_str(char *dest, size_t size, const wchar_t *src);
+extern char *wstr_to_astr(const wchar_t *src);
 extern void str_to_wstr(wchar_t *dest, size_t dlen, const char *src);
 
