+ return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
+ pw1->pw_uid == pw2->pw_uid &&
+ pw1->pw_gid == pw2->pw_gid &&
+ strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
+ pw1->pw_change == pw2->pw_change &&
+ pw1->pw_expire == pw2->pw_expire &&
+ strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
+ strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
+ strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
+}
+
+/*
+ * Make a passwd line out of a struct passwd.
+ */
+char *
+pw_make(struct passwd *pw)
+{
+ char *line;
+
+ asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name,
+ pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
+ pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire,
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ return line;
+}
+
+/*
+ * Copy password file from one descriptor to another, replacing or adding
+ * a single record on the way.
+ */
+int
+pw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw)
+{
+ char buf[8192], *end, *line, *p, *q, *r, t;
+ struct passwd *fpw;
+ ssize_t len;
+ int eof;
+
+ if ((line = pw_make(pw)) == NULL)
+ return (-1);
+
+ eof = 0;
+ len = 0;
+ p = q = end = buf;
+ for (;;) {
+ /* find the end of the current line */
+ for (p = q; q < end && *q != '\0'; ++q)
+ if (*q == '\n')
+ break;
+
+ /* if we don't have a complete line, fill up the buffer */
+ if (q >= end) {
+ if (eof)
+ break;
+ if (q - p >= sizeof buf) {
+ warnx("passwd line too long");
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ if (p < end) {
+ q = memmove(buf, p, end - p);
+ end -= p - buf;
+ } else {
+ p = q = end = buf;
+ }
+ len = read(ffd, end, sizeof buf - (end - buf));
+ if (len == -1)
+ goto err;
+ if (len == 0 && p == buf)
+ break;
+ end += len;
+ len = end - buf;
+ if (len < sizeof buf) {
+ eof = 1;
+ if (len > 0 && buf[len - 1] != '\n')
+ ++len, *end++ = '\n';
+ }
+ continue;
+ }
+
+ /* is it a blank line or a comment? */
+ for (r = p; r < q && isspace(*r); ++r)
+ /* nothing */ ;
+ if (r == q || *r == '#') {
+ /* yep */
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+
+ /* is it the one we're looking for? */
+ t = *q;
+ *q = '\0';
+ fpw = pw_scan(r, PWSCAN_MASTER);
+ *q = t;
+ if (old_pw == NULL || !pw_equal(pw, old_pw)) {
+ /* nope */
+ free(fpw);
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+ free(fpw);
+
+ /* it is, replace it */
+ len = strlen(line);
+ if (write(tfd, line, len) != len)
+ goto err;
+
+ /* we're done, just copy the rest over */
+ for (;;) {
+ if (write(tfd, q, end - q) != end - q)
+ goto err;
+ q = buf;
+ len = read(ffd, buf, sizeof buf);
+ if (len == 0)
+ break;
+ if (len == -1)
+ goto err;
+ end = buf + len;
+ }
+ goto done;
+ }
+
+ /* if we got here, we have a new entry */
+ len = strlen(line);
+ if (write(tfd, line, len) != len)
+ goto err;
+ done:
+ free(line);
+ return (0);
+ err:
+ free(line);
+ return (-1);
+}
+
+/*
+ * Return the current value of tempname.
+ */
+const char *
+pw_tempname(void)
+{
+
+ return (tempname);
+}
+
+/*
+ * Duplicate a struct passwd.
+ */
+struct passwd *
+pw_dup(struct passwd *pw)
+{
+ struct passwd *npw;
+ size_t len;
+
+ len = sizeof *npw +
+ (pw->pw_name ? strlen(pw->pw_name) + 1 : 0) +
+ (pw->pw_passwd ? strlen(pw->pw_passwd) + 1 : 0) +
+ (pw->pw_class ? strlen(pw->pw_class) + 1 : 0) +
+ (pw->pw_gecos ? strlen(pw->pw_gecos) + 1 : 0) +
+ (pw->pw_dir ? strlen(pw->pw_dir) + 1 : 0) +
+ (pw->pw_shell ? strlen(pw->pw_shell) + 1 : 0);
+ if ((npw = malloc(len)) == NULL)
+ return (NULL);
+ memcpy(npw, pw, sizeof *npw);
+ len = sizeof *npw;
+ if (pw->pw_name) {
+ npw->pw_name = ((char *)npw) + len;
+ len += sprintf(npw->pw_name, "%s", pw->pw_name) + 1;
+ }
+ if (pw->pw_passwd) {
+ npw->pw_passwd = ((char *)npw) + len;
+ len += sprintf(npw->pw_passwd, "%s", pw->pw_passwd) + 1;
+ }
+ if (pw->pw_class) {
+ npw->pw_class = ((char *)npw) + len;
+ len += sprintf(npw->pw_class, "%s", pw->pw_class) + 1;
+ }
+ if (pw->pw_gecos) {
+ npw->pw_gecos = ((char *)npw) + len;
+ len += sprintf(npw->pw_gecos, "%s", pw->pw_gecos) + 1;
+ }
+ if (pw->pw_dir) {
+ npw->pw_dir = ((char *)npw) + len;
+ len += sprintf(npw->pw_dir, "%s", pw->pw_dir) + 1;
+ }
+ if (pw->pw_shell) {
+ npw->pw_shell = ((char *)npw) + len;
+ len += sprintf(npw->pw_shell, "%s", pw->pw_shell) + 1;
+ }
+ return (npw);
+}
+
+#include "pw_scan.h"
+
+/*
+ * Wrapper around an internal libc function
+ */
+struct passwd *
+pw_scan(const char *line, int flags)
+{
+ struct passwd pw, *ret;
+ char *bp;
+
+ if ((bp = strdup(line)) == NULL)
+ return (NULL);
+ if (!__pw_scan(bp, &pw, flags)) {
+ free(bp);
+ return (NULL);