root/lib/libc/gen/getpwent.c
/*      $NetBSD: getpwent.c,v 1.85 2026/04/19 19:37:01 rillig Exp $     */

/*-
 * Copyright (c) 1997-2000, 2004-2005 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Luke Mewburn.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1988, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Portions Copyright (c) 1994, 1995, Jason Downs.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)getpwent.c  8.2 (Berkeley) 4/27/95";
#else
__RCSID("$NetBSD: getpwent.c,v 1.85 2026/04/19 19:37:01 rillig Exp $");
#endif
#endif /* LIBC_SCCS and not lint */

#include "namespace.h"
#include "reentrant.h"

#include <sys/param.h>

#include <assert.h>
#include <db.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netgroup.h>
#include <nsswitch.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "pw_private.h"

#ifdef HESIOD
#include <hesiod.h>
#endif

#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif

#include "pw_private.h"

#define _PASSWD_COMPAT  /* "passwd" defaults to compat, so always provide it */

#ifdef __weak_alias
__weak_alias(endpwent,_endpwent)
__weak_alias(setpassent,_setpassent)
__weak_alias(setpwent,_setpwent)
#endif

#ifdef _REENTRANT
static  mutex_t                 _pwmutex = MUTEX_INITIALIZER;
#endif

const char __yp_token[] = "__YP!";      /* Let pwd_mkdb pull this in. */


/*
 * The pwd.db lookup techniques and data extraction code here must be kept
 * in sync with that in `pwd_mkdb'.
 */

#if defined(YP) || defined(HESIOD)
/*
 * _pw_parse
 *      Parses entry using pw_scan(3) (without the trailing \n)
 *      after copying to buf, and fills in pw with corresponding values.
 *      If old is non-zero, entry is in _PASSWORD_OLDFMT.
 *      Returns 1 if parsed successfully, 0 on parse failure.
 */
static int
_pw_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
        int old)
{
        int     flags;

        _DIAGASSERT(entry != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buf != NULL);

        if (strlcpy(buf, entry, buflen) >= buflen)
                return 0;
        flags = _PASSWORD_NOWARN;
        if (old)
                flags |= _PASSWORD_OLDFMT;
        return __pw_scan(buf, pw, &flags);
}
#endif /* YP || HESIOD */

/*
 * _pw_opendb
 *      if *db is NULL, dbopen(3) /etc/spwd.db or /etc/pwd.db (depending
 *      upon permissions, etc)
 */
static int
_pw_opendb(DB **db, int *version)
{
        static int      warned;
        DBT             key;
        DBT             value;

        const char      *dbfile = NULL;

        _DIAGASSERT(db != NULL);
        _DIAGASSERT(version != NULL);
        if (*db != NULL)                                        /* open *db */
                return NS_SUCCESS;

        if (geteuid() == 0) {
                dbfile = _PATH_SMP_DB;
                *db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
        }
        if (*db == NULL) {
                dbfile = _PATH_MP_DB;
                *db = dbopen(dbfile, O_RDONLY, 0, DB_HASH, NULL);
        }
        if (*db == NULL) {
                if (!warned) {
                        int     serrno = errno;
                        syslog(LOG_ERR, "%s: %m", dbfile);
                        errno = serrno;
                }
                warned = 1;
                return NS_UNAVAIL;
        }
        key.data = __UNCONST("VERSION");
        key.size = strlen((char *)key.data) + 1;
        switch ((*(*db)->get)(*db, &key, &value, 0)) {
        case 0:
                if (sizeof(*version) != value.size)
                        return NS_UNAVAIL;
                (void)memcpy(version, value.data, value.size);
                break;                  /* found */
        case 1:
                *version = 0;           /* not found */
                break;
        case -1:
                return NS_UNAVAIL;      /* error in db routines */
        default:
                abort();
        }
        return NS_SUCCESS;
}

/*
 * _pw_getkey
 *      Lookup key in *db, filling in pw
 *      with the result, allocating memory from buffer (size buflen).
 *      (The caller may point key.data to buffer on entry; the contents
 *      of key.data will be invalid on exit.)
 */
static int
_pw_getkey(DB *db, DBT *key,
        struct passwd *pw, char *buffer, size_t buflen, int *pwflags,
        int version)
{
        char            *p, *t;
        DBT             data;

        _DIAGASSERT(db != NULL);
        _DIAGASSERT(key != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        /* pwflags may be NULL (if we don't care about them */

        if (db == NULL)                 /* this shouldn't happen */
                return NS_UNAVAIL;

        switch ((db->get)(db, key, &data, 0)) {
        case 0:
                break;                  /* found */
        case 1:
                return NS_NOTFOUND;     /* not found */
        case -1:
                return NS_UNAVAIL;      /* error in db routines */
        default:
                abort();
        }

        p = (char *)data.data;
        if (data.size > buflen) {
                errno = ERANGE;
                return NS_UNAVAIL;
        }

                        /*
                         * THE DECODING BELOW MUST MATCH THAT IN pwd_mkdb.
                         */
        t = buffer;
#define MACRO(a)        do { a } while (0)
#define EXPAND(e)       MACRO(e = t; while ((*t++ = *p++));)
#define SCALAR(v)       MACRO(memmove(&(v), p, sizeof v); p += sizeof v;)
        EXPAND(pw->pw_name);
        EXPAND(pw->pw_passwd);
        SCALAR(pw->pw_uid);
        SCALAR(pw->pw_gid);
        if (version == 0) {
                int32_t tmp;
                SCALAR(tmp);
                pw->pw_change = tmp;
        } else
                SCALAR(pw->pw_change);
        EXPAND(pw->pw_class);
        EXPAND(pw->pw_gecos);
        EXPAND(pw->pw_dir);
        EXPAND(pw->pw_shell);
        if (version == 0) {
                int32_t tmp;
                SCALAR(tmp);
                pw->pw_expire = tmp;
        } else
                SCALAR(pw->pw_expire);
        if (pwflags) {
                /* See if there's any data left.  If so, read in flags. */
                if (data.size > (size_t) (p - (char *)data.data)) {
                        SCALAR(*pwflags);
                } else {                                /* default */
                        *pwflags = _PASSWORD_NOUID|_PASSWORD_NOGID;
                }
        }

        return NS_SUCCESS;
}

/*
 * _pw_memfrombuf
 *      Obtain want bytes from buffer (of size buflen) and return a pointer
 *      to the available memory after adjusting buffer/buflen.
 *      Returns NULL if there is insufficient space.
 */
static char *
_pw_memfrombuf(size_t want, char **buffer, size_t *buflen)
{
        char    *rv;

        if (want > *buflen) {
                errno = ERANGE;
                return NULL;
        }
        rv = *buffer;
        *buffer += want;
        *buflen -= want;
        return rv;
}

/*
 * _pw_copy
 *      Copy the contents of frompw to pw; memory for strings
 *      and arrays will be allocated from buf (of size buflen).
 *      If proto != NULL, use various fields in proto in preference to frompw.
 *      Returns 1 if copied successfully, 0 on copy failure.
 *      NOTE: frompw must not use buf for its own pointers.
 */
static int
_pw_copy(const struct passwd *frompw, struct passwd *pw,
        char *buf, size_t buflen, const struct passwd *protopw, int protoflags)
{
        size_t  count;
        int     useproto;

        _DIAGASSERT(frompw != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buf != NULL);
        /* protopw may be NULL */

        useproto = protopw && protopw->pw_name;

#define COPYSTR(to, from) \
        do { \
                count = strlen((from)); \
                (to) = _pw_memfrombuf(count+1, &buf, &buflen); \
                if ((to) == NULL) \
                        return 0; \
                memmove((to), (from), count); \
                to[count] = '\0'; \
        } while (0)

#define COPYFIELD(field)        COPYSTR(pw->field, frompw->field)

#define COPYPROTOFIELD(field)   COPYSTR(pw->field, \
                (useproto && *protopw->field ? protopw->field : frompw->field))

        COPYFIELD(pw_name);

#ifdef PW_OVERRIDE_PASSWD
        COPYPROTOFIELD(pw_passwd);
#else
        COPYFIELD(pw_passwd);
#endif

        if (useproto && !(protoflags & _PASSWORD_NOUID))
                pw->pw_uid = protopw->pw_uid;
        else
                pw->pw_uid = frompw->pw_uid;

        if (useproto && !(protoflags & _PASSWORD_NOGID))
                pw->pw_gid = protopw->pw_gid;
        else
                pw->pw_gid = frompw->pw_gid;

        pw->pw_change = frompw->pw_change;
        COPYFIELD(pw_class);
        COPYPROTOFIELD(pw_gecos);
        COPYPROTOFIELD(pw_dir);
        COPYPROTOFIELD(pw_shell);

#undef COPYSTR
#undef COPYFIELD
#undef COPYPROTOFIELD

        return 1;
}


                /*
                 *      files methods
                 */

        /* state shared between files methods */
struct files_state {
        int      stayopen;              /* see getpassent(3) */
        DB      *db;                    /* passwd file handle */
        int      keynum;                /* key counter, -1 if no more */
        int      version;
};

static struct files_state       _files_state;
                                        /* storage for non _r functions */
static struct passwd            _files_passwd;
static char                     _files_passwdbuf[_GETPW_R_SIZE_MAX];

static int
_files_start(struct files_state *state)
{
        int     rv;

        _DIAGASSERT(state != NULL);

        state->keynum = 0;
        rv = _pw_opendb(&state->db, &state->version);
        if (rv != NS_SUCCESS)
                return rv;
        return NS_SUCCESS;
}

static int
_files_end(struct files_state *state)
{

        _DIAGASSERT(state != NULL);

        state->keynum = 0;
        if (state->db) {
                (void)(state->db->close)(state->db);
                state->db = NULL;
        }
        return NS_SUCCESS;
}

/*
 * _files_pwscan
 *      Search state->db for the next desired entry.
 *      If search is _PW_KEYBYNUM, look for state->keynum.
 *      If search is _PW_KEYBYNAME, look for name.
 *      If search is _PW_KEYBYUID, look for uid.
 *      Sets *retval to the errno if the result is not NS_SUCCESS
 *      or NS_NOTFOUND.
 */
static int
_files_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
        struct files_state *state, int search, const char *name, uid_t uid)
{
        const void      *from;
        size_t           fromlen;
        DBT              key;
        int              rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(state != NULL);
        /* name is NULL to indicate searching for uid */

        *retval = 0;

        if (state->db == NULL) {        /* only start if file not open yet */
                rv = _files_start(state);
                if (rv != NS_SUCCESS)
                        goto filespwscan_out;
        }

        for (;;) {                              /* search for a match */
                switch (search) {
                case _PW_KEYBYNUM:
                        if (state->keynum == -1)
                                return NS_NOTFOUND;     /* no more records */
                        state->keynum++;
                        from = &state->keynum;
                        fromlen = sizeof(state->keynum);
                        break;
                case _PW_KEYBYNAME:
                        from = name;
                        fromlen = strlen(name);
                        break;
                case _PW_KEYBYUID:
                        from = &uid;
                        fromlen = sizeof(uid);
                        break;
                default:
                        abort();
                }

                if (buflen <= fromlen) {                /* buffer too small */
                        *retval = ERANGE;
                        return NS_UNAVAIL;
                }
                buffer[0] = search;                     /* setup key */
                memmove(buffer + 1, from, fromlen);
                key.size = fromlen + 1;
                key.data = (u_char *)buffer;

                                                        /* search for key */
                rv = _pw_getkey(state->db, &key, pw, buffer, buflen, NULL,
                    state->version);
                if (rv != NS_SUCCESS)                   /* no match */
                        break;
                if (pw->pw_name[0] == '+' || pw->pw_name[0] == '-') {
                                                /* if a compat line */
                        if (search == _PW_KEYBYNUM)
                                continue;       /* read next if pwent */
                        rv = NS_NOTFOUND;       /* don't match if pw{nam,uid} */
                        break;
                }
                break;
        }

        if (rv == NS_NOTFOUND && search == _PW_KEYBYNUM)
                state->keynum = -1;             /* flag `no more records' */

        if (rv == NS_SUCCESS) {
                if ((search == _PW_KEYBYUID && pw->pw_uid != uid) ||
                    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0))
                        rv = NS_NOTFOUND;
        }

 filespwscan_out:
        if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
                *retval = errno;
        return rv;
}

/*ARGSUSED*/
static int
_files_setpwent(void *nsrv, void *nscb, va_list ap)
{

        _files_state.stayopen = 0;
        return _files_start(&_files_state);
}

/*ARGSUSED*/
static int
_files_setpassent(void *nsrv, void *nscb, va_list ap)
{
        int     *retval         = va_arg(ap, int *);
        int      stayopen       = va_arg(ap, int);

        int     rv;

        _files_state.stayopen = stayopen;
        rv = _files_start(&_files_state);
        *retval = (rv == NS_SUCCESS);
        return rv;
}

/*ARGSUSED*/
static int
_files_endpwent(void *nsrv, void *nscb, va_list ap)
{

        _files_state.stayopen = 0;
        return _files_end(&_files_state);
}

/*ARGSUSED*/
static int
_files_getpwent(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _files_pwscan(&rerror, &_files_passwd,
            _files_passwdbuf, sizeof(_files_passwdbuf),
            &_files_state, _PW_KEYBYNUM, NULL, 0);
        if (rv == NS_SUCCESS)
                *retval = &_files_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_files_getpwent_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        rv = _files_pwscan(retval, pw, buffer, buflen, &_files_state,
            _PW_KEYBYNUM, NULL, 0);
        if (rv == NS_SUCCESS)
                *result = pw;
        else
                *result = NULL;
        return rv;
}

/*ARGSUSED*/
static int
_files_getpwnam(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        const char      *name   = va_arg(ap, const char *);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _files_start(&_files_state);
        if (rv != NS_SUCCESS)
                return rv;
        rv = _files_pwscan(&rerror, &_files_passwd,
            _files_passwdbuf, sizeof(_files_passwdbuf),
            &_files_state, _PW_KEYBYNAME, name, 0);
        if (!_files_state.stayopen)
                _files_end(&_files_state);
        if (rv == NS_SUCCESS)
                *retval = &_files_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_files_getpwnam_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        const char      *name   = va_arg(ap, const char *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct files_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        rv = _files_pwscan(retval, pw, buffer, buflen, &state,
            _PW_KEYBYNAME, name, 0);
        _files_end(&state);
        if (rv == NS_SUCCESS)
                *result = pw;
        return rv;
}

/*ARGSUSED*/
static int
_files_getpwuid(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        uid_t            uid    = va_arg(ap, uid_t);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _files_start(&_files_state);
        if (rv != NS_SUCCESS)
                return rv;
        rv = _files_pwscan(&rerror, &_files_passwd,
            _files_passwdbuf, sizeof(_files_passwdbuf),
            &_files_state, _PW_KEYBYUID, NULL, uid);
        if (!_files_state.stayopen)
                _files_end(&_files_state);
        if (rv == NS_SUCCESS)
                *retval = &_files_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_files_getpwuid_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        uid_t            uid    = va_arg(ap, uid_t);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct files_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        rv = _files_pwscan(retval, pw, buffer, buflen, &state,
            _PW_KEYBYUID, NULL, uid);
        _files_end(&state);
        if (rv == NS_SUCCESS)
                *result = pw;
        return rv;
}


#ifdef HESIOD
                /*
                 *      dns methods
                 */

        /* state shared between dns methods */
struct dns_state {
        int      stayopen;              /* see getpassent(3) */
        void    *context;               /* Hesiod context */
        int      num;                   /* passwd index, -1 if no more */
};

static struct dns_state         _dns_state;
                                        /* storage for non _r functions */
static struct passwd            _dns_passwd;
static char                     _dns_passwdbuf[_GETPW_R_SIZE_MAX];

static int
_dns_start(struct dns_state *state)
{

        _DIAGASSERT(state != NULL);

        state->num = 0;
        if (state->context == NULL) {                   /* setup Hesiod */
                if (hesiod_init(&state->context) == -1)
                        return NS_UNAVAIL;
        }

        return NS_SUCCESS;
}

static int
_dns_end(struct dns_state *state)
{

        _DIAGASSERT(state != NULL);

        state->num = 0;
        if (state->context) {
                hesiod_end(state->context);
                state->context = NULL;
        }
        return NS_SUCCESS;
}

/*
 * _dns_pwscan
 *      Look for the Hesiod name provided in buffer in the NULL-terminated
 *      list of zones,
 *      and decode into pw/buffer/buflen.
 */
static int
_dns_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
        struct dns_state *state, const char **zones)
{
        const char      **curzone;
        char            **hp, *ep;
        int             rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(state != NULL);
        _DIAGASSERT(zones != NULL);

        *retval = 0;

        if (state->context == NULL) {   /* only start if Hesiod not setup */
                rv = _dns_start(state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

        hp = NULL;
        rv = NS_NOTFOUND;

        for (curzone = zones; *curzone; curzone++) {    /* search zones */
                hp = hesiod_resolve(state->context, buffer, *curzone);
                if (hp != NULL)
                        break;
                if (errno != ENOENT) {
                        rv = NS_UNAVAIL;
                        goto dnspwscan_out;
                }
        }
        if (*curzone == NULL)
                goto dnspwscan_out;

        if ((ep = strchr(hp[0], '\n')) != NULL)
                *ep = '\0';                             /* clear trailing \n */
        if (_pw_parse(hp[0], pw, buffer, buflen, 1))    /* validate line */
                rv = NS_SUCCESS;
        else
                rv = NS_UNAVAIL;

 dnspwscan_out:
        if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
                *retval = errno;
        if (hp)
                hesiod_free_list(state->context, hp);
        return rv;
}

/*ARGSUSED*/
static int
_dns_setpwent(void *nsrv, void *nscb, va_list ap)
{

        _dns_state.stayopen = 0;
        return _dns_start(&_dns_state);
}

/*ARGSUSED*/
static int
_dns_setpassent(void *nsrv, void *nscb, va_list ap)
{
        int     *retval         = va_arg(ap, int *);
        int      stayopen       = va_arg(ap, int);

        int     rv;

        _dns_state.stayopen = stayopen;
        rv = _dns_start(&_dns_state);
        *retval = (rv == NS_SUCCESS);
        return rv;
}

/*ARGSUSED*/
static int
_dns_endpwent(void *nsrv, void *nscb, va_list ap)
{

        _dns_state.stayopen = 0;
        return _dns_end(&_dns_state);
}

/*ARGSUSED*/
static int
_dns_getpwent(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);

        char    **hp, *ep;
        int       rv;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;

        if (_dns_state.num == -1)                       /* exhausted search */
                return NS_NOTFOUND;

        if (_dns_state.context == NULL) {
                        /* only start if Hesiod not setup */
                rv = _dns_start(&_dns_state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

 next_dns_entry:
        hp = NULL;
        rv = NS_NOTFOUND;

                                                        /* find passwd-NNN */
        snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
            "passwd-%u", _dns_state.num);
        _dns_state.num++;

        hp = hesiod_resolve(_dns_state.context, _dns_passwdbuf, "passwd");
        if (hp == NULL) {
                if (errno == ENOENT)
                        _dns_state.num = -1;
                else
                        rv = NS_UNAVAIL;
        } else {
                if ((ep = strchr(hp[0], '\n')) != NULL)
                        *ep = '\0';                     /* clear trailing \n */
                                                        /* validate line */
                if (_pw_parse(hp[0], &_dns_passwd,
                    _dns_passwdbuf, sizeof(_dns_passwdbuf), 1))
                        rv = NS_SUCCESS;
                else {                          /* dodgy entry, try again */
                        hesiod_free_list(_dns_state.context, hp);
                        goto next_dns_entry;
                }
        }

        if (hp)
                hesiod_free_list(_dns_state.context, hp);
        if (rv == NS_SUCCESS)
                *retval = &_dns_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_dns_getpwent_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        char    **hp, *ep;
        int       rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *retval = 0;

        if (_dns_state.num == -1)                       /* exhausted search */
                return NS_NOTFOUND;

        if (_dns_state.context == NULL) {
                        /* only start if Hesiod not setup */
                rv = _dns_start(&_dns_state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

 next_dns_entry:
        hp = NULL;
        rv = NS_NOTFOUND;

                                                        /* find passwd-NNN */
        snprintf(buffer, buflen, "passwd-%u", _dns_state.num);
        _dns_state.num++;

        hp = hesiod_resolve(_dns_state.context, buffer, "passwd");
        if (hp == NULL) {
                if (errno == ENOENT)
                        _dns_state.num = -1;
                else
                        rv = NS_UNAVAIL;
        } else {
                if ((ep = strchr(hp[0], '\n')) != NULL)
                        *ep = '\0';                     /* clear trailing \n */
                                                        /* validate line */
                if (_pw_parse(hp[0], pw, buffer, buflen, 1))
                        rv = NS_SUCCESS;
                else {                          /* dodgy entry, try again */
                        hesiod_free_list(_dns_state.context, hp);
                        goto next_dns_entry;
                }
        }

        if (hp)
                hesiod_free_list(_dns_state.context, hp);
        if (rv == NS_SUCCESS)
                *result = pw;
        else
                *result = NULL;
        return rv;
}

static const char *_dns_uid_zones[] = {
        "uid",
        "passwd",
        NULL
};

/*ARGSUSED*/
static int
_dns_getpwuid(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        uid_t            uid    = va_arg(ap, uid_t);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _dns_start(&_dns_state);
        if (rv != NS_SUCCESS)
                return rv;
        snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf),
            "%u", (unsigned int)uid);
        rv = _dns_pwscan(&rerror, &_dns_passwd,
            _dns_passwdbuf, sizeof(_dns_passwdbuf),
            &_dns_state, _dns_uid_zones);
        if (!_dns_state.stayopen)
                _dns_end(&_dns_state);
        if (rv == NS_SUCCESS && uid == _dns_passwd.pw_uid)
                *retval = &_dns_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_dns_getpwuid_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        uid_t            uid    = va_arg(ap, uid_t);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct dns_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        snprintf(buffer, buflen, "%u", (unsigned int)uid);
        rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_uid_zones);
        _dns_end(&state);
        if (rv != NS_SUCCESS)
                return rv;
        if (uid == pw->pw_uid) {
                *result = pw;
                return NS_SUCCESS;
        } else
                return NS_NOTFOUND;
}

static const char *_dns_nam_zones[] = {
        "passwd",
        NULL
};

/*ARGSUSED*/
static int
_dns_getpwnam(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        const char      *name   = va_arg(ap, const char *);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _dns_start(&_dns_state);
        if (rv != NS_SUCCESS)
                return rv;
        snprintf(_dns_passwdbuf, sizeof(_dns_passwdbuf), "%s", name);
        rv = _dns_pwscan(&rerror, &_dns_passwd,
            _dns_passwdbuf, sizeof(_dns_passwdbuf),
            &_dns_state, _dns_nam_zones);
        if (!_dns_state.stayopen)
                _dns_end(&_dns_state);
        if (rv == NS_SUCCESS && strcmp(name, _dns_passwd.pw_name) == 0)
                *retval = &_dns_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_dns_getpwnam_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        const char      *name   = va_arg(ap, const char *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct dns_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        snprintf(buffer, buflen, "%s", name);
        rv = _dns_pwscan(retval, pw, buffer, buflen, &state, _dns_nam_zones);
        _dns_end(&state);
        if (rv != NS_SUCCESS)
                return rv;
        if (strcmp(name, pw->pw_name) == 0) {
                *result = pw;
                return NS_SUCCESS;
        } else
                return NS_NOTFOUND;
}

#endif /* HESIOD */


#ifdef YP
                /*
                 *      nis methods
                 */
        /* state shared between nis methods */
struct nis_state {
        int              stayopen;      /* see getpassent(3) */
        char            *domain;        /* NIS domain */
        int              done;          /* non-zero if search exhausted */
        char            *current;       /* current first/next match */
        int              currentlen;    /* length of _nis_current */
        enum {                          /* shadow map type */
                NISMAP_UNKNOWN = 0,     /*  unknown ... */
                NISMAP_NONE,            /*  none: use "passwd.by*" */
                NISMAP_ADJUNCT,         /*  pw_passwd from "passwd.adjunct.*" */
                NISMAP_MASTER           /*  all from "master.passwd.by*" */
        }                maptype;
};

static struct nis_state         _nis_state;
                                        /* storage for non _r functions */
static struct passwd            _nis_passwd;
static char                     _nis_passwdbuf[_GETPW_R_SIZE_MAX];

static const char __nis_pw_n_1[] = "master.passwd.byname";
static const char __nis_pw_n_2[] = "passwd.byname";
static const char __nis_pw_u_1[] = "master.passwd.byuid";
static const char __nis_pw_u_2[] = "passwd.byuid";

static const char * const __nis_pw_n_map[4] = { __nis_pw_n_2, __nis_pw_n_2, __nis_pw_n_2, __nis_pw_n_1 };
static const char * const __nis_pw_u_map[4] = { __nis_pw_u_2, __nis_pw_u_2, __nis_pw_u_2, __nis_pw_u_1 };

        /* macros for deciding which NIS maps to use. */
#define PASSWD_BYNAME(x)        ((x)->maptype == NISMAP_MASTER ? __nis_pw_n_1 : __nis_pw_n_2)
#define PASSWD_BYUID(x)         ((x)->maptype == NISMAP_MASTER ? __nis_pw_u_1 : __nis_pw_u_2)

static int
_nis_start(struct nis_state *state)
{

        _DIAGASSERT(state != NULL);

        state->done = 0;
        if (state->current) {
                free(state->current);
                state->current = NULL;
        }
        if (state->domain == NULL) {                    /* setup NIS */
                switch (yp_get_default_domain(&state->domain)) {
                case 0:
                        break;
                case YPERR_RESRC:
                        return NS_TRYAGAIN;
                default:
                        return NS_UNAVAIL;
                }
        }

                                /* determine where to get pw_passwd from */
        if (state->maptype == NISMAP_UNKNOWN) {
                int     r, order;

                state->maptype = NISMAP_NONE;   /* default to no adjunct */
                if (geteuid() != 0)             /* non-root can't use adjunct */
                        return NS_SUCCESS;

                                                /* look for "master.passwd.*" */
                r = yp_order(state->domain, "master.passwd.byname", &order);
                if (r == 0) {
                        state->maptype = NISMAP_MASTER;
                        return NS_SUCCESS;
                }

                        /* master.passwd doesn't exist, try passwd.adjunct */
                if (r == YPERR_MAP) {
                        r = yp_order(state->domain, "passwd.adjunct.byname",
                            &order);
                        if (r == 0)
                                state->maptype = NISMAP_ADJUNCT;
                }
        }
        return NS_SUCCESS;
}

static int
_nis_end(struct nis_state *state)
{

        _DIAGASSERT(state != NULL);

        if (state->domain)
                state->domain = NULL;
        state->done = 0;
        if (state->current)
                free(state->current);
        state->current = NULL;
        state->maptype = NISMAP_UNKNOWN;
        return NS_SUCCESS;
}

/*
 * nis_parse
 *      wrapper to _pw_parse that obtains the real password from the
 *      "passwd.adjunct.byname" NIS map if the maptype is NISMAP_ADJUNCT.
 */
static int
_nis_parse(const char *entry, struct passwd *pw, char *buf, size_t buflen,
        struct nis_state *state)
{
        size_t  elen;

        _DIAGASSERT(entry != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buf != NULL);
        _DIAGASSERT(state != NULL);

        elen = strlen(entry) + 1;
        if (elen >= buflen)
                return 0;
        if (! _pw_parse(entry, pw, buf, buflen,
            !(state->maptype == NISMAP_MASTER)))
                return 0;

        if ((state->maptype == NISMAP_ADJUNCT) &&
            (strstr(pw->pw_passwd, "##") != NULL)) {
                char    *data;
                int     datalen;

                if (yp_match(state->domain, "passwd.adjunct.byname",
                    pw->pw_name, (int)strlen(pw->pw_name),
                    &data, &datalen) == 0) {
                        char    *bp, *ep;
                                                /* skip name to get password */
                        ep = data;
                        if (strsep(&ep, ":") != NULL &&
                            (bp = strsep(&ep, ":")) != NULL) {
                                        /* store new pw_passwd after entry */
                                if (strlcpy(buf + elen, bp, buflen - elen) >=
                                    buflen - elen) {
                                        free(data);
                                        return 0;
                                }
                                pw->pw_passwd = &buf[elen];
                        }
                        free(data);
                }
        }

        return 1;
}


/*
 * _nis_pwscan
 *      Look for the yp key provided in buffer from map,
 *      and decode into pw/buffer/buflen.
 */
static int
_nis_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
        struct nis_state *state, const char * const *map_arr, size_t nmaps)
{
        char    *data;
        int     nisr, rv, datalen;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(state != NULL);
        _DIAGASSERT(map_arr != NULL);

        *retval = 0;

        if (state->domain == NULL) {    /* only start if NIS not setup */
                rv = _nis_start(state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

        data = NULL;
        rv = NS_NOTFOUND;
        _DIAGASSERT(state->maptype != NISMAP_UNKNOWN &&
                    (unsigned)state->maptype < nmaps);

                                                        /* search map */
        nisr = yp_match(state->domain, map_arr[state->maptype], buffer, (int)strlen(buffer),
            &data, &datalen);
        switch (nisr) {
        case 0:
                data[datalen] = '\0';                   /* clear trailing \n */
                if (_nis_parse(data, pw, buffer, buflen, state))
                        rv = NS_SUCCESS;                /* validate line */
                else
                        rv = NS_UNAVAIL;
                break;
        case YPERR_KEY:
                break;
        default:
                rv = NS_UNAVAIL;
                break;
        }

        if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
                *retval = errno;
        if (data)
                free(data);
        return rv;
}

/*ARGSUSED*/
static int
_nis_setpwent(void *nsrv, void *nscb, va_list ap)
{

        _nis_state.stayopen = 0;
        return _nis_start(&_nis_state);
}

/*ARGSUSED*/
static int
_nis_setpassent(void *nsrv, void *nscb, va_list ap)
{
        int     *retval         = va_arg(ap, int *);
        int      stayopen       = va_arg(ap, int);

        int     rv;

        _nis_state.stayopen = stayopen;
        rv = _nis_start(&_nis_state);
        *retval = (rv == NS_SUCCESS);
        return rv;
}

/*ARGSUSED*/
static int
_nis_endpwent(void *nsrv, void *nscb, va_list ap)
{

        return _nis_end(&_nis_state);
}


/*ARGSUSED*/
static int
_nis_getpwent(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);

        char    *key, *data;
        int     keylen, datalen, rv, nisr;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;

        if (_nis_state.done)                            /* exhausted search */
                return NS_NOTFOUND;
        if (_nis_state.domain == NULL) {
                                        /* only start if NIS not setup */
                rv = _nis_start(&_nis_state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

 next_nis_entry:
        key = NULL;
        data = NULL;
        rv = NS_NOTFOUND;

        if (_nis_state.current) {                       /* already searching */
                nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
                    _nis_state.current, _nis_state.currentlen,
                    &key, &keylen, &data, &datalen);
                free(_nis_state.current);
                _nis_state.current = NULL;
                switch (nisr) {
                case 0:
                        _nis_state.current = key;
                        _nis_state.currentlen = keylen;
                        key = NULL;
                        break;
                case YPERR_NOMORE:
                        _nis_state.done = 1;
                        goto nisent_out;
                default:
                        rv = NS_UNAVAIL;
                        goto nisent_out;
                }
        } else {                                        /* new search */
                if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
                    &_nis_state.current, &_nis_state.currentlen,
                    &data, &datalen)) {
                        rv = NS_UNAVAIL;
                        goto nisent_out;
                }
        }

        data[datalen] = '\0';                           /* clear trailing \n */
                                                        /* validate line */
        if (_nis_parse(data, &_nis_passwd,
            _nis_passwdbuf, sizeof(_nis_passwdbuf), &_nis_state))
                rv = NS_SUCCESS;
        else {                                  /* dodgy entry, try again */
                free(data);
                goto next_nis_entry;
        }

 nisent_out:
        if (key)
                free(key);
        if (data)
                free(data);
        if (rv == NS_SUCCESS)
                *retval = &_nis_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_nis_getpwent_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        char    *key, *data;
        int     keylen, datalen, rv, nisr;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *retval = 0;

        if (_nis_state.done)                            /* exhausted search */
                return NS_NOTFOUND;
        if (_nis_state.domain == NULL) {
                                        /* only start if NIS not setup */
                rv = _nis_start(&_nis_state);
                if (rv != NS_SUCCESS)
                        return rv;
        }

 next_nis_entry:
        key = NULL;
        data = NULL;
        rv = NS_NOTFOUND;

        if (_nis_state.current) {                       /* already searching */
                nisr = yp_next(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
                    _nis_state.current, _nis_state.currentlen,
                    &key, &keylen, &data, &datalen);
                free(_nis_state.current);
                _nis_state.current = NULL;
                switch (nisr) {
                case 0:
                        _nis_state.current = key;
                        _nis_state.currentlen = keylen;
                        key = NULL;
                        break;
                case YPERR_NOMORE:
                        _nis_state.done = 1;
                        goto nisent_out;
                default:
                        rv = NS_UNAVAIL;
                        goto nisent_out;
                }
        } else {                                        /* new search */
                if (yp_first(_nis_state.domain, PASSWD_BYNAME(&_nis_state),
                    &_nis_state.current, &_nis_state.currentlen,
                    &data, &datalen)) {
                        rv = NS_UNAVAIL;
                        goto nisent_out;
                }
        }

        data[datalen] = '\0';                           /* clear trailing \n */
                                                        /* validate line */
        if (_nis_parse(data, pw, buffer, buflen, &_nis_state))
                rv = NS_SUCCESS;
        else {                                  /* dodgy entry, try again */
                if (key)
                        free(key);
                free(data);
                goto next_nis_entry;
        }

 nisent_out:
        if (key)
                free(key);
        if (data)
                free(data);
        if (rv == NS_SUCCESS)
                *result = pw;
        else
                *result = NULL;
        return rv;
}

/*ARGSUSED*/
static int
_nis_getpwuid(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        uid_t            uid    = va_arg(ap, uid_t);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _nis_start(&_nis_state);
        if (rv != NS_SUCCESS)
                return rv;
        snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%u", (unsigned int)uid);
        rv = _nis_pwscan(&rerror, &_nis_passwd,
            _nis_passwdbuf, sizeof(_nis_passwdbuf),
            &_nis_state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
        if (!_nis_state.stayopen)
                _nis_end(&_nis_state);
        if (rv == NS_SUCCESS && uid == _nis_passwd.pw_uid)
                *retval = &_nis_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_nis_getpwuid_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        uid_t            uid    = va_arg(ap, uid_t);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct nis_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        snprintf(buffer, buflen, "%u", (unsigned int)uid);
/* remark: we run under a global mutex inside of this module ... */
        if (_nis_state.stayopen)
          { /* use global state only if stayopen is set - otherwise we would blow up getpwent_r() ... */
            rv = _nis_pwscan(retval, pw, buffer, buflen,
                &_nis_state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
          }
        else
          { /* keep old semantic if no stayopen set - no need to call _nis_start() here - _nis_pwscan() will do it for us ... */
            /* use same way as in getgrent.c ... */
            memset(&state, 0, sizeof(state));
            rv = _nis_pwscan(retval, pw, buffer, buflen,
                &state, __nis_pw_u_map, __arraycount(__nis_pw_u_map));
            _nis_end(&state);
          }
        if (rv != NS_SUCCESS)
                return rv;
        if (uid == pw->pw_uid) {
                *result = pw;
                return NS_SUCCESS;
        } else
                return NS_NOTFOUND;
}

/*ARGSUSED*/
static int
_nis_getpwnam(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        const char      *name   = va_arg(ap, const char *);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _nis_start(&_nis_state);
        if (rv != NS_SUCCESS)
                return rv;
        snprintf(_nis_passwdbuf, sizeof(_nis_passwdbuf), "%s", name);
        rv = _nis_pwscan(&rerror, &_nis_passwd,
            _nis_passwdbuf, sizeof(_nis_passwdbuf),
            &_nis_state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
        if (!_nis_state.stayopen)
                _nis_end(&_nis_state);
        if (rv == NS_SUCCESS && strcmp(name, _nis_passwd.pw_name) == 0)
                *retval = &_nis_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_nis_getpwnam_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        const char      *name   = va_arg(ap, const char *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct nis_state state;
        int     rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        snprintf(buffer, buflen, "%s", name);
/* remark: we run under a global mutex inside of this module ... */
        if (_nis_state.stayopen)
          { /* use global state only if stayopen is set - otherwise we would blow up getpwent_r() ... */
            rv = _nis_pwscan(retval, pw, buffer, buflen,
                &_nis_state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
          }
        else
          { /* keep old semantic if no stayopen set - no need to call _nis_start() here - _nis_pwscan() will do it for us ... */
            /* use same way as in getgrent.c ... */
            memset(&state, 0, sizeof(state));
            rv = _nis_pwscan(retval, pw, buffer, buflen,
                &state, __nis_pw_n_map, __arraycount(__nis_pw_n_map));
            _nis_end(&state);
          }
        if (rv != NS_SUCCESS)
                return rv;
        if (strcmp(name, pw->pw_name) == 0) {
                *result = pw;
                return NS_SUCCESS;
        } else
                return NS_NOTFOUND;
}

#endif /* YP */


#ifdef _PASSWD_COMPAT
                /*
                 *      compat methods
                 */

        /* state shared between compat methods */

struct compat_state {
        int              stayopen;      /* see getpassent(3) */
        DB              *db;            /* passwd DB */
        int              keynum;        /* key counter, -1 if no more */
        enum {                          /* current compat mode */
                COMPAT_NOTOKEN = 0,     /*  no compat token present */
                COMPAT_NONE,            /*  parsing normal pwd.db line */
                COMPAT_FULL,            /*  parsing `+' entries */
                COMPAT_USER,            /*  parsing `+name' entries */
                COMPAT_NETGROUP         /*  parsing `+@netgroup' entries */
        }                mode;
        char            *user;          /* COMPAT_USER "+name" */
        DB              *exclude;       /* compat exclude DB */
        struct passwd    proto;         /* proto passwd entry */
        char             protobuf[_GETPW_R_SIZE_MAX];
                                        /* buffer for proto ptrs */
        int              protoflags;    /* proto passwd flags */
        int              version;
};

static struct compat_state      _compat_state;
                                        /* storage for non _r functions */
static struct passwd            _compat_passwd;
static char                     _compat_passwdbuf[_GETPW_R_SIZE_MAX];

static int
_compat_start(struct compat_state *state)
{
        int     rv;

        _DIAGASSERT(state != NULL);

        state->keynum = 0;
        if (state->db == NULL) {                /* not open yet */
                DBT     key, data;
                DBT     pkey, pdata;
                char    bf[MAXLOGNAME];

                rv = _pw_opendb(&state->db, &state->version);
                if (rv != NS_SUCCESS)
                        return rv;

                state->mode = COMPAT_NOTOKEN;

                /*
                 *      Determine if the "compat" token is present in pwd.db;
                 *      either "__YP!" or PW_KEYBYNAME+"+".
                 *      Only works if pwd_mkdb installs the token.
                 */
                key.data = (u_char *)__UNCONST(__yp_token);
                key.size = strlen(__yp_token);

                bf[0] = _PW_KEYBYNAME;   /* Pre-token database support. */
                bf[1] = '+';
                pkey.data = (u_char *)bf;
                pkey.size = 2;

                if ((state->db->get)(state->db, &key, &data, 0) == 0
                    || (state->db->get)(state->db, &pkey, &pdata, 0) == 0)
                        state->mode = COMPAT_NONE;
        }
        return NS_SUCCESS;
}

static int
_compat_end(struct compat_state *state)
{

        _DIAGASSERT(state != NULL);

        state->keynum = 0;
        if (state->db) {
                (void)(state->db->close)(state->db);
                state->db = NULL;
        }
        state->mode = COMPAT_NOTOKEN;
        if (state->user)
                free(state->user);
        state->user = NULL;
        if (state->exclude != NULL)
                (void)(state->exclude->close)(state->exclude);
        state->exclude = NULL;
        state->proto.pw_name = NULL;
        state->protoflags = 0;
        return NS_SUCCESS;
}

/*
 * _compat_add_exclude
 *      add the name to the exclude list in state->exclude.
 */
static int
_compat_add_exclude(struct compat_state *state, const char *name)
{
        DBT     key, data;

        _DIAGASSERT(state != NULL);
        _DIAGASSERT(name != NULL);

                                /* initialize the exclusion table if needed */
        if (state->exclude == NULL) {
                state->exclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
                if (state->exclude == NULL)
                        return 0;
        }

        key.size = strlen(name);                        /* set up the key */
        key.data = (u_char *)__UNCONST(name);

        data.data = NULL;                               /* data is nothing */
        data.size = 0;

                                                        /* store it */
        if ((state->exclude->put)(state->exclude, &key, &data, 0) == -1)
                return 0;

        return 1;
}

/*
 * _compat_is_excluded
 *      test if a name is on the compat mode exclude list
 */
static int
_compat_is_excluded(struct compat_state *state, const char *name)
{
        DBT     key, data;

        _DIAGASSERT(state != NULL);
        _DIAGASSERT(name != NULL);

        if (state->exclude == NULL)
                return 0;       /* nothing excluded */

        key.size = strlen(name);                        /* set up the key */
        key.data = (u_char *)__UNCONST(name);

        if ((state->exclude->get)(state->exclude, &key, &data, 0) == 0)
                return 1;                               /* is excluded */

        return 0;
}


/*
 * _passwdcompat_bad
 *      log an error if "files" or "compat" is specified in
 *      passwd_compat database
 */
/*ARGSUSED*/
static int
_passwdcompat_bad(void *nsrv, void *nscb, va_list ap)
{
        static int warned;

        _DIAGASSERT(nsrv != NULL);
        _DIAGASSERT(nscb != NULL);

        if (!warned) {
                syslog(LOG_ERR,
                        "nsswitch.conf passwd_compat database can't use '%s'",
                        (char *)nscb);
        }
        warned = 1;
        return NS_UNAVAIL;
}

/*
 * _passwdcompat_setpassent
 *      Call setpassent for all passwd_compat sources.
 */
static int
_passwdcompat_setpassent(int stayopen)
{
        static const ns_dtab dtab[] = {
                NS_FILES_CB(_passwdcompat_bad, "files")
                NS_DNS_CB(_dns_setpassent, NULL)
                NS_NIS_CB(_nis_setpassent, NULL)
                NS_COMPAT_CB(_passwdcompat_bad, "compat")
                NS_NULL_CB
        };

        int     rv, result;

        rv = nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpassent",
            __nsdefaultnis_forceall, &result, stayopen);
        return rv;
}

/*
 * _passwdcompat_endpwent
 *      Call endpwent for all passwd_compat sources.
 */
static int
_passwdcompat_endpwent(void)
{
        static const ns_dtab dtab[] = {
                NS_FILES_CB(_passwdcompat_bad, "files")
                NS_DNS_CB(_dns_endpwent, NULL)
                NS_NIS_CB(_nis_endpwent, NULL)
                NS_COMPAT_CB(_passwdcompat_bad, "compat")
                NS_NULL_CB
        };

        return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
            __nsdefaultnis_forceall);
}

/*
 * _passwdcompat_pwscan
 *      When a name lookup in compat mode is required (e.g., `+name', or a
 *      name in `+@netgroup'), look it up in the 'passwd_compat' nsswitch
 *      database.
 *      Fail if passwd_compat contains files or compat.
 */
static int
_passwdcompat_pwscan(struct passwd *pw, char *buffer, size_t buflen,
        int search, const char *name, uid_t uid)
{
        static const ns_dtab compatentdtab[] = {
                NS_FILES_CB(_passwdcompat_bad, "files")
                NS_DNS_CB(_dns_getpwent_r, NULL)
                NS_NIS_CB(_nis_getpwent_r, NULL)
                NS_COMPAT_CB(_passwdcompat_bad, "compat")
                NS_NULL_CB
        };
        static const ns_dtab compatuiddtab[] = {
                NS_FILES_CB(_passwdcompat_bad, "files")
                NS_DNS_CB(_dns_getpwuid_r, NULL)
                NS_NIS_CB(_nis_getpwuid_r, NULL)
                NS_COMPAT_CB(_passwdcompat_bad, "compat")
                NS_NULL_CB
        };
        static const ns_dtab compatnamdtab[] = {
                NS_FILES_CB(_passwdcompat_bad, "files")
                NS_DNS_CB(_dns_getpwnam_r, NULL)
                NS_NIS_CB(_nis_getpwnam_r, NULL)
                NS_COMPAT_CB(_passwdcompat_bad, "compat")
                NS_NULL_CB
        };

        int             rv, crv;
        struct passwd   *cpw;

        switch (search) {
        case _PW_KEYBYNUM:
                rv = nsdispatch(NULL, compatentdtab,
                    NSDB_PASSWD_COMPAT, "getpwent_r", __nsdefaultnis,
                    &crv, pw, buffer, buflen, &cpw);
                break;
        case _PW_KEYBYNAME:
                _DIAGASSERT(name != NULL);
                rv = nsdispatch(NULL, compatnamdtab,
                    NSDB_PASSWD_COMPAT, "getpwnam_r", __nsdefaultnis,
                    &crv, name, pw, buffer, buflen, &cpw);
                break;
        case _PW_KEYBYUID:
                rv = nsdispatch(NULL, compatuiddtab,
                    NSDB_PASSWD_COMPAT, "getpwuid_r", __nsdefaultnis,
                    &crv, uid, pw, buffer, buflen, &cpw);
                break;
        default:
                abort();
                /*NOTREACHED*/
        }
        return rv;
}

/*
 * _compat_pwscan
 *      Search state->db for the next desired entry.
 *      If search is _PW_KEYBYNUM, look for state->keynum.
 *      If search is _PW_KEYBYNAME, look for name.
 *      If search is _PW_KEYBYUID, look for uid.
 *      Sets *retval to the errno if the result is not NS_SUCCESS
 *      or NS_NOTFOUND.
 */
static int
_compat_pwscan(int *retval, struct passwd *pw, char *buffer, size_t buflen,
        struct compat_state *state, int search, const char *name, uid_t uid)
{
        DBT              key;
        int              rv, r, pwflags;
        const char      *user, *host, *dom;
        const void      *from;
        size_t           fromlen;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(state != NULL);
        /* name may be NULL */

        *retval = 0;

        if (state->db == NULL) {
                rv = _compat_start(state);
                if (rv != NS_SUCCESS)
                        return rv;
        }
        if (buflen <= 1) {                      /* buffer too small */
                *retval = ERANGE;
                return NS_UNAVAIL;
        }

        for (;;) {                              /* loop over pwd.db */
                rv = NS_NOTFOUND;
                if (state->mode != COMPAT_NOTOKEN &&
                    state->mode != COMPAT_NONE) {
                                                /* doing a compat lookup */
                        struct passwd   cpw;
                        char            cbuf[_GETPW_R_SIZE_MAX];

                        switch (state->mode) {

                        case COMPAT_FULL:
                                        /* get next user or lookup by key */
                                rv = _passwdcompat_pwscan(&cpw,
                                    cbuf, sizeof(cbuf), search, name, uid);
                                if (rv != NS_SUCCESS)
                                        state->mode = COMPAT_NONE;
                                break;

                        case COMPAT_NETGROUP:
/* XXXREENTRANT: getnetgrent is not thread safe */
                                        /* get next user from netgroup */
                                r = getnetgrent(&host, &user, &dom);
                                if (r == 0) {   /* end of group */
                                        endnetgrent();
                                        state->mode = COMPAT_NONE;
                                        break;
                                }
                                if (!user || !*user)
                                        break;
                                rv = _passwdcompat_pwscan(&cpw,
                                    cbuf, sizeof(cbuf),
                                    _PW_KEYBYNAME, user, 0);
                                break;

                        case COMPAT_USER:
                                        /* get specific user */
                                if (state->user == NULL) {
                                        state->mode = COMPAT_NONE;
                                        break;
                                }
                                rv = _passwdcompat_pwscan(&cpw,
                                    cbuf, sizeof(cbuf),
                                    _PW_KEYBYNAME, state->user, 0);
                                free(state->user);
                                state->user = NULL;
                                state->mode = COMPAT_NONE;
                                break;

                        case COMPAT_NOTOKEN:
                        case COMPAT_NONE:
                                abort();

                        }
                        if (rv != NS_SUCCESS)   /* if not matched, next loop */
                                continue;

                                /* copy cpw to pw, applying prototype */
                        if (! _pw_copy(&cpw, pw, buffer, buflen,
                            &state->proto, state->protoflags)) {
                                rv = NS_UNAVAIL;
                                break;
                        }

                        if (_compat_is_excluded(state, pw->pw_name))
                                continue;       /* excluded; next loop */

                        if ((search == _PW_KEYBYNAME
                                        && strcmp(pw->pw_name, name) != 0)
                            || (search == _PW_KEYBYUID && pw->pw_uid != uid)) {
                                continue;       /* not specific; next loop */
                        }

                        break;                  /* exit loop if found */
                } else {                        /* not a compat line */
                        state->proto.pw_name = NULL;
                                                /* clear prototype */
                }

                if (state->mode == COMPAT_NOTOKEN) {
                                /* no compat token; do direct lookup */
                        switch (search) {
                        case _PW_KEYBYNUM:
                                if (state->keynum == -1)  /* no more records */
                                        return NS_NOTFOUND;
                                state->keynum++;
                                from = &state->keynum;
                                fromlen = sizeof(state->keynum);
                                break;
                        case _PW_KEYBYNAME:
                                from = name;
                                fromlen = strlen(name);
                                break;
                        case _PW_KEYBYUID:
                                from = &uid;
                                fromlen = sizeof(uid);
                                break;
                        default:
                                abort();
                        }
                        buffer[0] = search;
                } else {
                                /* compat token; do line by line */
                        if (state->keynum == -1)  /* no more records */
                                return NS_NOTFOUND;
                        state->keynum++;
                        from = &state->keynum;
                        fromlen = sizeof(state->keynum);
                        buffer[0] = _PW_KEYBYNUM;
                }

                if (buflen <= fromlen) {                /* buffer too small */
                        *retval = ERANGE;
                        return NS_UNAVAIL;
                }
                memmove(buffer + 1, from, fromlen);     /* setup key */
                key.size = fromlen + 1;
                key.data = (u_char *)buffer;

                rv = _pw_getkey(state->db, &key, pw, buffer, buflen, &pwflags,
                    state->version);
                if (rv != NS_SUCCESS)           /* stop on error */
                        break;

                if (state->mode == COMPAT_NOTOKEN)
                        break;                  /* stop if no compat token */

                if (pw->pw_name[0] == '+') {
                                                /* compat inclusion */
                        switch(pw->pw_name[1]) {
                        case '\0':              /* `+' */
                                state->mode = COMPAT_FULL;
                                                /* reset passwd_compat search */
/* XXXREENTRANT: setpassent is not thread safe ? */
                                (void) _passwdcompat_setpassent(_compat_state.stayopen);
                                break;
                        case '@':               /* `+@netgroup' */
                                state->mode = COMPAT_NETGROUP;
                                                /* reset netgroup search */
/* XXXREENTRANT: setnetgrent is not thread safe */
                                setnetgrent(pw->pw_name + 2);
                                break;
                        default:                /* `+name' */
                                state->mode = COMPAT_USER;
                                if (state->user)
                                        free(state->user);
                                state->user = strdup(pw->pw_name + 1);
                                break;
                        }
                                                /* save the prototype */
                        state->protoflags = pwflags;
                        if (! _pw_copy(pw, &state->proto, state->protobuf,
                            sizeof(state->protobuf), NULL, 0)) {
                                rv = NS_UNAVAIL;
                                break;
                        }
                        continue;               /* loop again after inclusion */
                } else if (pw->pw_name[0] == '-') {
                                                /* compat exclusion */
                        rv = NS_SUCCESS;
                        switch(pw->pw_name[1]) {
                        case '\0':              /* `-' */
                                break;
                        case '@':               /* `-@netgroup' */
/* XXXREENTRANT: {set,get,end}netgrent is not thread safe */
                                setnetgrent(pw->pw_name + 2);
                                while (getnetgrent(&host, &user, &dom)) {
                                        if (!user || !*user)
                                                continue;
                                        if (! _compat_add_exclude(state,user)) {
                                                rv = NS_UNAVAIL;
                                                break;
                                        }
                                }
                                endnetgrent();
                                break;
                        default:                /* `-name' */
                                if (! _compat_add_exclude(state,
                                    pw->pw_name + 1)) {
                                        rv = NS_UNAVAIL;
                                }
                                break;
                        }
                        if (rv != NS_SUCCESS)   /* exclusion failure */
                                break;
                        continue;               /* loop again after exclusion */
                }
                if (search == _PW_KEYBYNUM ||
                    (search == _PW_KEYBYUID && pw->pw_uid == uid) ||
                    (search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) == 0))
                        break;                  /* token mode match found */
        }

        if (rv == NS_NOTFOUND &&
            (search == _PW_KEYBYNUM || state->mode != COMPAT_NOTOKEN))
                state->keynum = -1;             /* flag `no more records' */

        if (rv == NS_SUCCESS) {
                if ((search == _PW_KEYBYNAME && strcmp(pw->pw_name, name) != 0)
                    || (search == _PW_KEYBYUID && pw->pw_uid != uid))
                        rv = NS_NOTFOUND;
        }

        if (rv != NS_SUCCESS && rv != NS_NOTFOUND)
                *retval = errno;
        return rv;
}

/*ARGSUSED*/
static int
_compat_setpwent(void *nsrv, void *nscb, va_list ap)
{

                                        /* force passwd_compat setpwent() */
        (void) _passwdcompat_setpassent(0);

                                        /* reset state, keep db open */
        _compat_state.stayopen = 0;
        return _compat_start(&_compat_state);
}

/*ARGSUSED*/
static int
_compat_setpassent(void *nsrv, void *nscb, va_list ap)
{
        int     *retval         = va_arg(ap, int *);
        int      stayopen       = va_arg(ap, int);

        int     rv;

                                        /* force passwd_compat setpassent() */
        (void) _passwdcompat_setpassent(stayopen);

        _compat_state.stayopen = stayopen;
        rv = _compat_start(&_compat_state);
        *retval = (rv == NS_SUCCESS);
        return rv;
}

/*ARGSUSED*/
static int
_compat_endpwent(void *nsrv, void *nscb, va_list ap)
{

                                        /* force passwd_compat endpwent() */
        (void) _passwdcompat_endpwent();

                                        /* reset state, close db */
        _compat_state.stayopen = 0;
        return _compat_end(&_compat_state);
}


/*ARGSUSED*/
static int
_compat_getpwent(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _compat_pwscan(&rerror, &_compat_passwd,
            _compat_passwdbuf, sizeof(_compat_passwdbuf),
            &_compat_state, _PW_KEYBYNUM, NULL, 0);
        if (rv == NS_SUCCESS)
                *retval = &_compat_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_compat_getpwent_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        int             rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        rv = _compat_pwscan(retval, pw, buffer, buflen, &_compat_state,
            _PW_KEYBYNUM, NULL, 0);
        if (rv == NS_SUCCESS)
                *result = pw;
        else
                *result = NULL;
        return rv;
}


/*ARGSUSED*/
static int
_compat_getpwnam(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        const char      *name   = va_arg(ap, const char *);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _compat_start(&_compat_state);
        if (rv != NS_SUCCESS)
                return rv;
        rv = _compat_pwscan(&rerror, &_compat_passwd,
            _compat_passwdbuf, sizeof(_compat_passwdbuf),
            &_compat_state, _PW_KEYBYNAME, name, 0);
        if (!_compat_state.stayopen)
                _compat_end(&_compat_state);
        if (rv == NS_SUCCESS)
                *retval = &_compat_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_compat_getpwnam_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        const char      *name   = va_arg(ap, const char *);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct compat_state     state;
        int             rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
            _PW_KEYBYNAME, name, 0);
        _compat_end(&state);
        if (rv == NS_SUCCESS)
                *result = pw;
        return rv;
}

/*ARGSUSED*/
static int
_compat_getpwuid(void *nsrv, void *nscb, va_list ap)
{
        struct passwd   **retval = va_arg(ap, struct passwd **);
        uid_t            uid    = va_arg(ap, uid_t);

        int     rv, rerror;

        _DIAGASSERT(retval != NULL);

        *retval = NULL;
        rv = _compat_start(&_compat_state);
        if (rv != NS_SUCCESS)
                return rv;
        rv = _compat_pwscan(&rerror, &_compat_passwd,
            _compat_passwdbuf, sizeof(_compat_passwdbuf),
            &_compat_state, _PW_KEYBYUID, NULL, uid);
        if (!_compat_state.stayopen)
                _compat_end(&_compat_state);
        if (rv == NS_SUCCESS)
                *retval = &_compat_passwd;
        return rv;
}

/*ARGSUSED*/
static int
_compat_getpwuid_r(void *nsrv, void *nscb, va_list ap)
{
        int             *retval = va_arg(ap, int *);
        uid_t            uid    = va_arg(ap, uid_t);
        struct passwd   *pw     = va_arg(ap, struct passwd *);
        char            *buffer = va_arg(ap, char *);
        size_t           buflen = va_arg(ap, size_t);
        struct passwd  **result = va_arg(ap, struct passwd **);

        struct compat_state     state;
        int             rv;

        _DIAGASSERT(retval != NULL);
        _DIAGASSERT(pw != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        memset(&state, 0, sizeof(state));
        rv = _compat_pwscan(retval, pw, buffer, buflen, &state,
            _PW_KEYBYUID, NULL, uid);
        _compat_end(&state);
        if (rv == NS_SUCCESS)
                *result = pw;
        return rv;
}

#endif /* _PASSWD_COMPAT */


                /*
                 *      public functions
                 */

struct passwd *
getpwent(void)
{
        int             r;
        struct passwd   *retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwent, NULL)
                NS_DNS_CB(_dns_getpwent, NULL)
                NS_NIS_CB(_nis_getpwent, NULL)
                NS_COMPAT_CB(_compat_getpwent, NULL)
                NS_NULL_CB
        };

        mutex_lock(&_pwmutex);
        r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", __nsdefaultcompat,
            &retval);
        mutex_unlock(&_pwmutex);
        return (r == NS_SUCCESS) ? retval : NULL;
}

int
getpwent_r(struct passwd *pwd, char *buffer, size_t buflen,
    struct passwd **result)
{
        int     r, retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwent_r, NULL)
                NS_DNS_CB(_dns_getpwent_r, NULL)
                NS_NIS_CB(_nis_getpwent_r, NULL)
                NS_COMPAT_CB(_compat_getpwent_r, NULL)
                NS_NULL_CB
        };

        _DIAGASSERT(pwd != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        retval = 0;
        mutex_lock(&_pwmutex);
        r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent_r", __nsdefaultcompat,
            &retval, pwd, buffer, buflen, result);
        mutex_unlock(&_pwmutex);
        switch (r) {
        case NS_SUCCESS:
        case NS_NOTFOUND:
                return 0;
        default:
                return retval;
        }
}


struct passwd *
getpwnam(const char *name)
{
        int             rv;
        struct passwd   *retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwnam, NULL)
                NS_DNS_CB(_dns_getpwnam, NULL)
                NS_NIS_CB(_nis_getpwnam, NULL)
                NS_COMPAT_CB(_compat_getpwnam, NULL)
                NS_NULL_CB
        };

        mutex_lock(&_pwmutex);
        rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", __nsdefaultcompat,
            &retval, name);
        mutex_unlock(&_pwmutex);
        return (rv == NS_SUCCESS) ? retval : NULL;
}

int
getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t buflen,
        struct passwd **result)
{
        int     r, retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwnam_r, NULL)
                NS_DNS_CB(_dns_getpwnam_r, NULL)
                NS_NIS_CB(_nis_getpwnam_r, NULL)
                NS_COMPAT_CB(_compat_getpwnam_r, NULL)
                NS_NULL_CB
        };

        _DIAGASSERT(name != NULL);
        _DIAGASSERT(pwd != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        retval = 0;
        mutex_lock(&_pwmutex);
        r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam_r", __nsdefaultcompat,
            &retval, name, pwd, buffer, buflen, result);
        mutex_unlock(&_pwmutex);
        switch (r) {
        case NS_SUCCESS:
        case NS_NOTFOUND:
                return 0;
        default:
                return retval;
        }
}

struct passwd *
getpwuid(uid_t uid)
{
        int             rv;
        struct passwd   *retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwuid, NULL)
                NS_DNS_CB(_dns_getpwuid, NULL)
                NS_NIS_CB(_nis_getpwuid, NULL)
                NS_COMPAT_CB(_compat_getpwuid, NULL)
                NS_NULL_CB
        };

        mutex_lock(&_pwmutex);
        rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", __nsdefaultcompat,
            &retval, uid);
        mutex_unlock(&_pwmutex);
        return (rv == NS_SUCCESS) ? retval : NULL;
}

int
getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen,
        struct passwd **result)
{
        int     r, retval;

        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_getpwuid_r, NULL)
                NS_DNS_CB(_dns_getpwuid_r, NULL)
                NS_NIS_CB(_nis_getpwuid_r, NULL)
                NS_COMPAT_CB(_compat_getpwuid_r, NULL)
                NS_NULL_CB
        };

        _DIAGASSERT(pwd != NULL);
        _DIAGASSERT(buffer != NULL);
        _DIAGASSERT(result != NULL);

        *result = NULL;
        retval = 0;
        mutex_lock(&_pwmutex);
        r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid_r", __nsdefaultcompat,
            &retval, uid, pwd, buffer, buflen, result);
        mutex_unlock(&_pwmutex);
        switch (r) {
        case NS_SUCCESS:
        case NS_NOTFOUND:
                return 0;
        default:
                return retval;
        }
}

void
endpwent(void)
{
        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_endpwent, NULL)
                NS_DNS_CB(_dns_endpwent, NULL)
                NS_NIS_CB(_nis_endpwent, NULL)
                NS_COMPAT_CB(_compat_endpwent, NULL)
                NS_NULL_CB
        };

        mutex_lock(&_pwmutex);
                                        /* force all endpwent() methods */
        (void) nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent",
            __nsdefaultcompat_forceall);
        mutex_unlock(&_pwmutex);
}

/*ARGSUSED*/
int
setpassent(int stayopen)
{
        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_setpassent, NULL)
                NS_DNS_CB(_dns_setpassent, NULL)
                NS_NIS_CB(_nis_setpassent, NULL)
                NS_COMPAT_CB(_compat_setpassent, NULL)
                NS_NULL_CB
        };
        int     rv, retval;

        mutex_lock(&_pwmutex);
                                        /* force all setpassent() methods */
        rv = nsdispatch(NULL, dtab, NSDB_PASSWD, "setpassent",
            __nsdefaultcompat_forceall, &retval, stayopen);
        mutex_unlock(&_pwmutex);
        return (rv == NS_SUCCESS) ? retval : 0;
}

void
setpwent(void)
{
        static const ns_dtab dtab[] = {
                NS_FILES_CB(_files_setpwent, NULL)
                NS_DNS_CB(_dns_setpwent, NULL)
                NS_NIS_CB(_nis_setpwent, NULL)
                NS_COMPAT_CB(_compat_setpwent, NULL)
                NS_NULL_CB
        };

        mutex_lock(&_pwmutex);
                                        /* force all setpwent() methods */
        (void) nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent",
            __nsdefaultcompat_forceall);
        mutex_unlock(&_pwmutex);
}