diff -r 0fc0108517a8 runtime/doc/map.txt
--- a/runtime/doc/map.txt	Sun Jul 29 12:55:32 2012 +0200
+++ b/runtime/doc/map.txt	Mon Jul 30 19:44:08 2012 +0200
@@ -1244,6 +1244,7 @@
 	-complete=syntax	syntax file names |'syntax'|
 	-complete=tag		tags
 	-complete=tag_listfiles	tags, file names are shown when CTRL-D is hit
+	-complete=user		user names
 	-complete=var		user variables
 	-complete=custom,{func} custom completion, defined via {func}
 	-complete=customlist,{func} custom completion, defined via {func}
diff -r 0fc0108517a8 runtime/doc/todo.txt
--- a/runtime/doc/todo.txt	Sun Jul 29 12:55:32 2012 +0200
+++ b/runtime/doc/todo.txt	Mon Jul 30 19:44:08 2012 +0200
@@ -602,9 +602,6 @@
 not a string. (Sean Ma)  Need to add flag to call_func_retlist() to force a
 string value.
 
-":e ~br<Tab>" does not complete to ":e /home/bram/".  Would need to use
-getpwent() to find all the matches.
-
 Invalid read error in Farsi mode. (Dominique Pelle, 2009 Aug 2)
 
 For running gvim on an USB stick: avoid the OLE registration.  Use a command
diff -r 0fc0108517a8 src/auto/configure
--- a/src/auto/configure	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/auto/configure	Mon Jul 30 19:44:08 2012 +0200
@@ -10631,7 +10631,7 @@
 fi
 
 for ac_func in bcmp fchdir fchown fsync getcwd getpseudotty \
-	getpwnam getpwuid getrlimit gettimeofday getwd lstat memcmp \
+	getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat memcmp \
 	memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
 	setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
 	sigvec strcasecmp strerror strftime stricmp strncasecmp \
diff -r 0fc0108517a8 src/config.h.in
--- a/src/config.h.in	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/config.h.in	Mon Jul 30 19:44:08 2012 +0200
@@ -161,6 +161,7 @@
 #undef HAVE_FSYNC
 #undef HAVE_GETCWD
 #undef HAVE_GETPSEUDOTTY
+#undef HAVE_GETPWENT
 #undef HAVE_GETPWNAM
 #undef HAVE_GETPWUID
 #undef HAVE_GETRLIMIT
diff -r 0fc0108517a8 src/configure.in
--- a/src/configure.in	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/configure.in	Mon Jul 30 19:44:08 2012 +0200
@@ -2994,7 +2994,7 @@
 dnl Check for functions in one big call, to reduce the size of configure.
 dnl Can only be used for functions that do not require any include.
 AC_CHECK_FUNCS(bcmp fchdir fchown fsync getcwd getpseudotty \
-	getpwnam getpwuid getrlimit gettimeofday getwd lstat memcmp \
+	getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat memcmp \
 	memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \
 	setpgid setsid sigaltstack sigstack sigset sigsetjmp sigaction \
 	sigvec strcasecmp strerror strftime stricmp strncasecmp \
diff -r 0fc0108517a8 src/ex_docmd.c
--- a/src/ex_docmd.c	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/ex_docmd.c	Mon Jul 30 19:44:08 2012 +0200
@@ -3515,6 +3515,23 @@
 #endif
 	    }
 	}
+#if defined(FEAT_CMDL_COMPL)
+	/* Check for user names */
+	if (*xp->xp_pattern == '~')
+	{
+	    for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
+		;
+	    /* Complete ~user only if it partially matches a user name.
+	     * A full match ~user<Tab> will be replaced by user's home
+	     * directory i.e. something like ~user<Tab> -> /home/user/ */
+	    if (*p == NUL && p > xp->xp_pattern + 1
+			&& match_user(xp->xp_pattern + 1) == 1)
+	    {
+		xp->xp_context = EXPAND_USER;
+		++xp->xp_pattern;
+	    }
+	}
+#endif
     }
 
 /*
@@ -5396,6 +5413,7 @@
 #endif
     {EXPAND_TAGS, "tag"},
     {EXPAND_TAGS_LISTFILES, "tag_listfiles"},
+    {EXPAND_USER, "user"},
     {EXPAND_USER_VARS, "var"},
     {0, NULL}
 };
diff -r 0fc0108517a8 src/ex_getln.c
--- a/src/ex_getln.c	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/ex_getln.c	Mon Jul 30 19:44:08 2012 +0200
@@ -4334,6 +4334,7 @@
  *  EXPAND_EXPRESSION	    Complete internal or user defined function/variable
  *			    names in expressions, eg :while s^I
  *  EXPAND_ENV_VARS	    Complete environment variable names
+ *  EXPAND_USER		    Complete user names
  */
     static void
 set_expand_context(xp)
@@ -4679,6 +4680,7 @@
 	    {EXPAND_LOCALES, get_locales, TRUE, FALSE},
 #endif
 	    {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
+	    {EXPAND_USER, get_users, TRUE, FALSE},
 	};
 	int	i;
 
diff -r 0fc0108517a8 src/misc1.c
--- a/src/misc1.c	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/misc1.c	Mon Jul 30 19:44:08 2012 +0200
@@ -18,6 +18,11 @@
 static char_u *remove_tail __ARGS((char_u *p, char_u *pend, char_u *name));
 static int copy_indent __ARGS((int size, char_u	*src));
 
+/* All user names (for ~user completion as done by shell) */
+#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
+static garray_T	ga_users;
+#endif
+
 /*
  * Count the size (in window cells) of the indent in the current line.
  */
@@ -3780,6 +3785,14 @@
 {
     vim_free(homedir);
 }
+
+# ifdef FEAT_CMDL_COMPL
+    void
+free_users()
+{
+    ga_clear_strings(&ga_users);
+}
+# endif
 #endif
 
 /*
@@ -4449,6 +4462,80 @@
     return name;
 # endif
 }
+
+/*
+ * Find all user names for user completion.
+ * Done only once and then cached.
+ */
+    static void
+init_users() {
+    static int	lazy_init_done = FALSE;
+
+    if (lazy_init_done)
+	return;
+
+    lazy_init_done = TRUE;
+    ga_init2(&ga_users, sizeof(char_u *), 1);
+
+# if defined(HAVE_GETPWENT) && defined(HAVE_PWD_H)
+    {
+	char_u*		user;
+	struct passwd*	pw;
+
+	setpwent();
+	while ((pw = getpwent()) != NULL)
+	    /* pw->pw_name shouldn't be NULL but just in case... */
+	    if (pw->pw_name != NULL)
+	    {
+		if (ga_grow(&ga_users, 1) == FAIL)
+		    break;
+		user = vim_strsave((char_u*)pw->pw_name);
+		if (user == NULL)
+		    break;
+		((char_u **)(ga_users.ga_data))[ga_users.ga_len++] = user;
+	    }
+	endpwent();
+    }
+# endif
+}
+
+/*
+ * Function given to ExpandGeneric() to obtain an user names.
+ */
+    char_u*
+get_users(xp, idx)
+    expand_T	*xp UNUSED;
+    int		idx;
+{
+    init_users();
+    if (idx < ga_users.ga_len)
+	return ((char_u **)ga_users.ga_data)[idx];
+    return NULL;
+}
+
+/*
+ * Check whether name matches a user name. Return:
+ * 0 if name does not match any user name.
+ * 1 if name partially matches the beginning of a user name.
+ * 2 is name fully matches a user name.
+ */
+int match_user(name)
+    char_u* name;
+{
+    int i;
+    int n = STRLEN(name);
+    int result = 0;
+
+    init_users();
+    for (i = 0; i < ga_users.ga_len; i++)
+    {
+	if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0)
+	    return 2; /* full match */
+	if (STRNCMP(((char_u **)ga_users.ga_data)[i], name, n) == 0)
+	    result = 1; /* partial match */
+    }
+    return result;
+}
 #endif
 
 /*
diff -r 0fc0108517a8 src/misc2.c
--- a/src/misc2.c	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/misc2.c	Mon Jul 30 19:44:08 2012 +0200
@@ -1110,6 +1110,9 @@
     free_all_marks();
     alist_clear(&global_alist);
     free_homedir();
+# if defined(FEAT_CMDL_COMPL)
+    free_users();
+# endif
     free_search_patterns();
     free_old_sub();
     free_last_insert();
diff -r 0fc0108517a8 src/proto/misc1.pro
--- a/src/proto/misc1.pro	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/proto/misc1.pro	Mon Jul 30 19:44:08 2012 +0200
@@ -50,6 +50,7 @@
 void vim_beep __ARGS((void));
 void init_homedir __ARGS((void));
 void free_homedir __ARGS((void));
+void free_users __ARGS((void));
 char_u *expand_env_save __ARGS((char_u *src));
 char_u *expand_env_save_opt __ARGS((char_u *src, int one));
 void expand_env __ARGS((char_u *src, char_u *dst, int dstlen));
@@ -57,6 +58,8 @@
 char_u *vim_getenv __ARGS((char_u *name, int *mustfree));
 void vim_setenv __ARGS((char_u *name, char_u *val));
 char_u *get_env_name __ARGS((expand_T *xp, int idx));
+char_u *get_users __ARGS((expand_T *xp, int idx));
+int match_user __ARGS((char_u* name));
 void home_replace __ARGS((buf_T *buf, char_u *src, char_u *dst, int dstlen, int one));
 char_u *home_replace_save __ARGS((buf_T *buf, char_u *src));
 int fullpathcmp __ARGS((char_u *s1, char_u *s2, int checkname));
diff -r 0fc0108517a8 src/vim.h
--- a/src/vim.h	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/vim.h	Mon Jul 30 19:44:08 2012 +0200
@@ -782,6 +782,7 @@
 #define EXPAND_OWNSYNTAX	39
 #define EXPAND_LOCALES		40
 #define EXPAND_HISTORY		41
+#define EXPAND_USER		42
 
 /* Values for exmode_active (0 is no exmode) */
 #define EXMODE_NORMAL		1
