+
+/* The *pwent() functions do not exist on NT. #### The NT equivalent
+ is NetUserEnum(), and rewriting to use it is not hard.*/
+#ifndef WIN32_NATIVE
+
+static Lisp_Object user_name_completion (Lisp_Object user,
+ int all_flag,
+ int *uniq);
+
+DEFUN ("user-name-completion", Fuser_name_completion, 1, 1, 0, /*
+Complete user name from PARTIAL-USERNAME.
+Return the longest prefix common to all user names starting with
+PARTIAL-USERNAME. If there is only one and PARTIAL-USERNAME matches
+it exactly, returns t. Return nil if there is no user name starting
+with PARTIAL-USERNAME.
+*/
+ (partial_username))
+{
+ return user_name_completion (partial_username, 0, NULL);
+}
+
+DEFUN ("user-name-completion-1", Fuser_name_completion_1, 1, 1, 0, /*
+Complete user name from PARTIAL-USERNAME.
+
+This function is identical to `user-name-completion', except that
+the cons of the completion and an indication of whether the
+completion was unique is returned.
+
+The car of the returned value is the longest prefix common to all user
+names that start with PARTIAL-USERNAME. If there is only one and
+PARTIAL-USERNAME matches it exactly, the car is t. The car is nil if
+there is no user name starting with PARTIAL-USERNAME. The cdr of the
+result is non-nil if and only if the completion returned in the car
+was unique.
+*/
+ (partial_username))
+{
+ int uniq;
+ Lisp_Object completed = user_name_completion (partial_username, 0, &uniq);
+ return Fcons (completed, uniq ? Qt : Qnil);
+}
+
+DEFUN ("user-name-all-completions", Fuser_name_all_completions, 1, 1, 0, /*
+Return a list of all user name completions from PARTIAL-USERNAME.
+These are all the user names which begin with PARTIAL-USERNAME.
+*/
+ (partial_username))
+{
+ return user_name_completion (partial_username, 1, NULL);
+}
+
+struct user_name
+{
+ Bufbyte *ptr;
+ size_t len;
+};
+
+struct user_cache
+{
+ struct user_name *user_names;
+ int length;
+ int size;
+ EMACS_TIME last_rebuild_time;
+};
+static struct user_cache user_cache;
+
+static void
+free_user_cache (struct user_cache *cache)
+{
+ int i;
+ for (i = 0; i < cache->length; i++)
+ xfree (cache->user_names[i].ptr);
+ xfree (cache->user_names);
+ xzero (*cache);
+}
+
+static Lisp_Object
+user_name_completion_unwind (Lisp_Object cache_incomplete_p)
+{
+ endpwent ();
+ speed_up_interrupts ();
+
+ if (! NILP (XCAR (cache_incomplete_p)))
+ free_user_cache (&user_cache);
+
+ free_cons (XCONS (cache_incomplete_p));
+
+ return Qnil;
+}
+
+#define USER_CACHE_TTL (24*60*60) /* Time to live: 1 day, in seconds */
+
+static Lisp_Object
+user_name_completion (Lisp_Object user, int all_flag, int *uniq)
+{
+ /* This function can GC */
+ int matchcount = 0;
+ Lisp_Object bestmatch = Qnil;
+ Charcount bestmatchsize = 0;
+ Charcount user_name_length;
+ EMACS_TIME t;
+ int i;
+ struct gcpro gcpro1, gcpro2;
+
+ GCPRO2 (user, bestmatch);
+
+ CHECK_STRING (user);
+
+ user_name_length = XSTRING_CHAR_LENGTH (user);
+
+ /* Cache user name lookups because it tends to be quite slow.
+ * Rebuild the cache occasionally to catch changes */
+ EMACS_GET_TIME (t);
+ if (user_cache.user_names &&
+ (EMACS_SECS (t) - EMACS_SECS (user_cache.last_rebuild_time)
+ > USER_CACHE_TTL))
+ free_user_cache (&user_cache);
+
+ if (!user_cache.user_names)
+ {
+ struct passwd *pwd;
+ Lisp_Object cache_incomplete_p = noseeum_cons (Qt, Qnil);
+ int speccount = specpdl_depth ();
+
+ slow_down_interrupts ();
+ setpwent ();
+ record_unwind_protect (user_name_completion_unwind, cache_incomplete_p);
+ while ((pwd = getpwent ()))
+ {
+ QUIT;
+ DO_REALLOC (user_cache.user_names, user_cache.size,
+ user_cache.length + 1, struct user_name);
+ TO_INTERNAL_FORMAT (C_STRING, pwd->pw_name,
+ MALLOC,
+ (user_cache.user_names[user_cache.length].ptr,
+ user_cache.user_names[user_cache.length].len),
+ Qnative);
+ user_cache.length++;
+ }
+ XCAR (cache_incomplete_p) = Qnil;
+ unbind_to (speccount, Qnil);
+
+ EMACS_GET_TIME (user_cache.last_rebuild_time);
+ }
+
+ for (i = 0; i < user_cache.length; i++)
+ {
+ Bufbyte *u_name = user_cache.user_names[i].ptr;
+ Bytecount len = user_cache.user_names[i].len;
+ /* scmp() works in chars, not bytes, so we have to compute this: */
+ Charcount cclen = bytecount_to_charcount (u_name, len);
+
+ QUIT;
+
+ if (cclen < user_name_length
+ || 0 <= scmp_1 (u_name, XSTRING_DATA (user), user_name_length, 0))
+ continue;
+
+ matchcount++; /* count matching completions */
+
+ if (all_flag || NILP (bestmatch))
+ {
+ Lisp_Object name = Qnil;
+ struct gcpro ngcpro1;
+ NGCPRO1 (name);
+ /* This is a possible completion */
+ name = make_string (u_name, len);
+ if (all_flag)
+ {
+ bestmatch = Fcons (name, bestmatch);
+ }
+ else
+ {
+ bestmatch = name;
+ bestmatchsize = XSTRING_CHAR_LENGTH (name);
+ }
+ NUNGCPRO;
+ }
+ else
+ {
+ Charcount compare = min (bestmatchsize, cclen);
+ Bufbyte *p1 = XSTRING_DATA (bestmatch);
+ Bufbyte *p2 = u_name;
+ Charcount matchsize = scmp_1 (p1, p2, compare, 0);
+
+ if (matchsize < 0)
+ matchsize = compare;
+
+ bestmatchsize = matchsize;
+ }
+ }
+
+ UNGCPRO;
+
+ if (uniq)
+ *uniq = (matchcount == 1);
+
+ if (all_flag || NILP (bestmatch))
+ return bestmatch;
+ if (matchcount == 1 && bestmatchsize == user_name_length)
+ return Qt;
+ return Fsubstring (bestmatch, Qzero, make_int (bestmatchsize));
+}
+#endif /* ! defined WIN32_NATIVE */
+
+\f