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 02:08:49 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 02:08:49 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/config.h.in
--- a/src/config.h.in	Sun Jul 29 12:55:32 2012 +0200
+++ b/src/config.h.in	Mon Jul 30 02:08:49 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 02:08:49 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 02:08:49 2012 +0200
@@ -3515,6 +3515,21 @@
 #endif
 	    }
 	}
+#if defined(FEAT_CMDL_COMPL)
+	/* Check for user names */
+	if (*xp->xp_pattern == '~')
+	{
+	    for (p = xp->xp_pattern + 1; *p != NUL; ++p)
+		;
+	    /* Complete ~user unless it fully matches a user name since
+	     * in that case, it will be replaced by user's home directory. */
+	    if (p > xp->xp_pattern + 1 && !is_user(xp->xp_pattern + 1))
+	    {
+		xp->xp_context = EXPAND_USER;
+		++xp->xp_pattern;
+	    }
+	}
+#endif
     }
 
 /*
@@ -5396,6 +5411,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 02:08:49 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 02:08:49 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,73 @@
     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;
+}
+
+/*
+ * Return TRUE if name matches a user name.
+ */
+int is_user(name)
+    char_u* name;
+{
+    init_users();
+    int i;
+
+    /* Linear search. I assume it's not worth sorting users array to
+     * do a binary search here. */
+    for (i = 0; i < ga_users.ga_len; i++)
+	if (STRCMP(((char_u **)ga_users.ga_data)[i], name) == 0)
+	    return TRUE;
+    return FALSE;
+}
 #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 02:08:49 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 02:08:49 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 is_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));
