From: jef@netcom.com (Jef Poskanzer)
Subject: libsmpw - self-maintaining passwd indexer
Date: Sun, 16 Aug 92 07:52:11 GMT
Organization: Paratheo-Anametamystikhood Of Eris Esoteric
Followup-To: alt.sources.d
Reply-To: Jef Poskanzer <jef@netcom.com>

This is kind of weird/cool.  It's a library for indexed access to
/etc/passwd, but the unusual thing is that it's self-maintaining.
Why bother with something like this, when dbm/dbz works just fine?
Well, back in January the sysadmins at the WELL turned off yp,
which is good, but they didn't replace it with any other sort
of indexed access to /etc/passwd, which, considering that the WELL
has about 7000 users, is not so good.

After eight months of waiting for them to do something about it
(they've tried one CPU upgrade and have scheduled another, but
gosh, things are still slow), I had a brainflash that I could
do it myself.  Make the index files world-writable, and have
the library incrementally update with new entries whenever it
has to fall back on linear search.  Add lots of checking in case
someone trashes the files.  Then relink ls, finger, and whatever
else does getpwuid()/getpwnam().  I'll leave relinking login
for the sysadmins.
---
Jef

           Jef Poskanzer  jef@netcom.com  jef@well.sf.ca.us
"Republicans understand the importance of bondage between a mother and
                     child." -- J. Danforth Quayle

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	libsmpw.3
#	smpw.c
#	Makefile
# This archive created: Sun Aug 16 00:38:01 1992
# By:	Jef Poskanzer (Paratheo-Anametamystikhood Of Eris Esoteric)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1034 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
X         libsmpw - self-maintaining indexed access to /etc/passwd
X                        Version 1.0 of 16aug92
X                        Previous release NONE
X
Xlibsmpw is a drop-in replacement for getpwuid()/getpwnam().  Like many
Xversions of these routines it uses external index files for faster
Xaccess.  Unlike other versions, the index files are "self-maintained".
XThis means they are world-writable, and every time any user runs a
Xprogram that accesses these routines, the indexes get updated as
Xnecessary.
X
XThis is mainly to avoid the need for root privs to get fast /etc/passwd
Xaccess - although even sysadmins may wish to install these routines to
Xavoid maintenance headaches and delays in password changing.
X
X
XFiles in this distribution:
X    README		this
X    Makefile		guess
X    smpw.c		source code
X    libsmpw.3		man page
X
XTo install:
X    Unpack the files.
X    Edit the Makefile to change the configuration options if necessary.
X    Make.
X    Make install.
X
XComments to:
X    Jef Poskanzer  jef@netcom.com  jef@well.sf.ca.us
SHAR_EOF
if test 1034 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1034 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'libsmpw.3'" '(1179 characters)'
if test -f 'libsmpw.3'
then
	echo shar: will not over-write existing file "'libsmpw.3'"
else
sed 's/^X//' << \SHAR_EOF > 'libsmpw.3'
X.TH libsmpw 3 "13 August 1992"
X.SH NAME
Xlibsmpw - self-maintaining indexed access to /etc/passwd
X.SH SYNOPSIS
X-L/usr/public/lib -lsmpw
X.SH DESCRIPTION
X.B libsmpw
Xis a drop-in replacement for getpwuid()/getpwnam().
XLike many versions of these routines it uses external index files
Xfor faster access.
XUnlike other versions, the index files are "self-maintained".
XThis means they are world-writable, and every time any user runs
Xa program that accesses these routines, the indexes get updated
Xas necessary.
X.PP
XThis is mainly to avoid the need for root privs to get fast /etc/passwd
Xaccess - although even sysadmins may wish to install these
Xroutines to avoid maintenance headaches and delays in password changing.
X.SH "SEE ALSO"
Xgetpwent(3)
X.SH AUTHOR
XCopyright (C) 1992 by Jef Poskanzer.
X.\" Permission to use, copy, modify, and distribute this software and its
X.\" documentation for any purpose and without fee is hereby granted, provided
X.\" that the above copyright notice appear in all copies and that both that
X.\" copyright notice and this permission notice appear in supporting
X.\" documentation.  This software is provided "as is" without express or
X.\" implied warranty.
SHAR_EOF
if test 1179 -ne "`wc -c < 'libsmpw.3'`"
then
	echo shar: error transmitting "'libsmpw.3'" '(should have been 1179 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'smpw.c'" '(15518 characters)'
if test -f 'smpw.c'
then
	echo shar: will not over-write existing file "'smpw.c'"
else
sed 's/^X//' << \SHAR_EOF > 'smpw.c'
X/* smpw.c - self-maintaining indexed access to /etc/passwd
X**
X** Copyright (C) 1992 by Jef Poskanzer.
X**
X** Permission to use, copy, modify, and distribute this software and its
X** documentation for any purpose and without fee is hereby granted, provided
X** that the above copyright notice appear in all copies and that both that
X** copyright notice and this permission notice appear in supporting
X** documentation.  This software is provided "as is" without express or
X** implied warranty.
X**
X**
X** Parts of this file are:
X** Copyright 1989, 1990, 1991 John F. Haugh II
X** All rights reserved.
X**
X** Permission is granted to copy and create derivative works for any
X** non-commercial purpose, provided this copyright notice is preserved
X** in all copies of source code, or included in human readable form
X** and conspicuously displayed on all copies of object code or
X** distribution media.
X*/
X
X#include <stdio.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X
X#ifdef SYSV
X#include <string.h>
X#define	index strchr
X#else /*SYSV*/
X#include <strings.h>
X#endif /*SYSV*/
X
Xextern off_t lseek();
X
X#ifndef PWDFILE
X#define PWDFILE "/etc/passwd"
X#endif
X#ifndef PWDUFILE
X#define PWDUFILE "/etc/passwd.u"
X#endif
X#ifndef PWDNFILE
X#define PWDNFILE "/etc/passwd.n"
X#endif
X#ifndef PWDSFILE
X#define PWDSFILE "/etc/passwd.s"
X#endif
X
X#define NAMEHASHMULT 50021
X
X#define NO_OFF 2147483647
X#define NO_UID 65535
X
X#define NEARBY_ENTRIES 30
X#define BYTES_PER_ENTRY 74	/* approximately */
X
X#define NFIELDS 7
X
Xstatic uid_t maxuid;
Xstatic off_t namehashsize;
X
Xstatic FILE* pwdfp = (FILE*) 0;
Xstatic int pwufd = -1;
Xstatic int pwnfd = -1;
X
Xstatic char pwdbuf[BUFSIZ];
Xstatic char* pwdfields[NFIELDS];
Xstatic struct passwd pwent;
X
X/* smpw_init - initialize the library */
Xstatic void
Xsmpw_init()
X    {
X    static int firstTime = 1;
X    FILE* fp;
X    int i1, i2;
X
X    if ( firstTime )
X	{
X	firstTime = 0;
X
X	/* Read maxuid and namehashsize from the file.  The reason we do
X	** this instead of just compiling them in is so that if you ever
X	** need to change them, you don't have to recompile every program
X	** in the world.
X	*/
X	maxuid = 0;
X	fp = fopen( PWDSFILE, "r" );
X	if ( fp != (FILE*) 0 )
X	    {
X	    if ( fscanf( fp, "%d %d", &i1, &i2 ) == 2 )
X		{
X		maxuid = i1;
X		namehashsize = i2;
X		}
X	    (void) fclose( fp );
X	    }
X	}
X    }
X
X/* sgetpwent - convert a string to a (struct passwd)
X**
X** sgetpwent() parses pwdbuf into the parts required for a password
X** structure.  Strict checking is made for the uid and gid fields and
X** presence of the correct number of colons.  Any failing tests result
X** in a NULL pointer being returned.
X**/
Xstatic struct passwd*
Xsgetpwent()
X    {
X    int i;
X    char* cp;
X
X    /* Save a pointer to the start of each colon separated
X    ** field.  The fields are converted into NUL terminated strings.
X    */
X    for ( cp = pwdbuf, i = 0; i < NFIELDS && cp; ++i )
X	{
X	pwdfields[i] = cp;
X	cp = index( cp, ':' );
X	if ( cp )
X	    *cp++ = 0;
X	}
X
X    /* There must be exactly NFIELDS colon separated fields or
X    ** the entry is invalid.  Also, the uid and gid must be non-blank.
X    */
X    if ( i != NFIELDS || *pwdfields[2] == '\0' || *pwdfields[3] == '\0' )
X	return (struct passwd*) 0;
X
X    /* Each of the fields is converted the appropriate data type
X    ** and the result assigned to the password structure.  If the
X    ** uid or gid does not convert to an integer value, a NULL
X    ** pointer is returned.
X    */
X    pwent.pw_name = pwdfields[0];
X    pwent.pw_passwd = pwdfields[1];
X    if ( sscanf( pwdfields[2], "%d", &i ) != 1 )
X	return (struct passwd*) 0;
X    pwent.pw_uid = i;
X    if ( sscanf( pwdfields[3], "%d", &i ) != 1 )
X	return (struct passwd*) 0;
X    pwent.pw_gid = i;
X    pwent.pw_gecos = pwdfields[4];
X    pwent.pw_dir = pwdfields[5];
X    pwent.pw_shell = pwdfields[6];
X
X    return &pwent;
X    }
X
X/* fgetpwent - get a password file entry from a stream
X**
X** fgetpwent() reads the next line from a password file formatted stream
X** and returns a pointer to the password structure for that line.
X*/
Xstruct passwd*
Xfgetpwent( fp )
X    FILE* fp;
X    {
X    smpw_init();
X
X    if ( fgets( pwdbuf, BUFSIZ, fp ) == (char*) 0 )
X	return (struct passwd*) 0;
X    pwdbuf[strlen( pwdbuf ) - 1] = '\0';
X    return sgetpwent();
X    }
X
X/* getpwent - get a password entry from the password file
X**
X** getpwent() opens the password file, if not already opened, and reads
X** a single entry.  NULL is returned if any errors are encountered reading
X** the password file.
X*/
Xstruct passwd*
Xgetpwent()
X    {
X    smpw_init();
X
X    if ( pwdfp == (FILE*) 0 && setpwent() < 0 )
X	return (struct passwd*) 0;
X    return fgetpwent( pwdfp );
X    }
X
X/* igetpwuid - locate a password entry for a given uid using the index */
Xstatic struct passwd*
Xigetpwuid( uid, offP )
X    uid_t uid;
X    off_t *offP;
X    {
X    struct passwd* pwd;
X
X    *offP = NO_OFF;
X
X    /* Check that the uid index file was opened successfully. */
X    if ( pwufd == -1 )
X	return (struct passwd*) 0;
X    
X    /* Use the uid to index into the uid index file. */
X    if ( lseek( pwufd, (off_t) uid * sizeof(off_t), L_SET ) < 0 )
X	return (struct passwd*) 0;
X
X    /* Read out a seek address. */
X    if ( read( pwufd, (char*) offP, sizeof(off_t) ) != sizeof(off_t) )
X	return (struct passwd*) 0;
X
X    /* Seek to the address, making sure it's the beginning of a line. */
X    if ( *offP == 0 )
X	rewind( pwdfp );
X    else
X	{
X	if ( fseek( pwdfp, *offP - 1, 0 ) < 0 )
X	    return (struct passwd*) 0;
X	if ( getc( pwdfp ) != '\n' )
X	    return (struct passwd*) 0;
X	}
X
X    /* Read the entry. */
X    pwd = fgetpwent( pwdfp );
X    if ( pwd == (struct passwd*) 0 )
X	return (struct passwd*) 0;
X
X    /* Check that it's the right one. */
X    if ( pwd->pw_uid != uid )
X	return (struct passwd*) 0;
X
X    /* Ok! */
X    return pwd;
X    }
X
X/* isetpwuid - set the index entry for a given uid
X**
X** isetpwuid() takes a uid and a seek address and writes the address
X** to the uid index file.
X*/
Xstatic void
Xisetpwuid( uid, off )
X    uid_t uid;
X    off_t off;
X    {
X    /* Check that the uid index file was opened successfully. */
X    if ( pwufd == -1 )
X	return;
X    
X    /* Use the uid to index into the uid index file. */
X    if ( lseek( pwufd, (off_t) uid * sizeof(off_t), L_SET ) < 0 )
X	return;
X
X    /* Write the seek address. */
X    (void) write( pwufd, (char*) &off, sizeof(off_t) );
X    }
X
X/* getpwuid - locate a password entry for a given uid
X**
X** getpwuid() locates a password file entry for the given uid,
X** by use of the index if possible, then by nearby search, and
X** lastly by linear search.
X*/
Xstruct passwd*
Xgetpwuid( uid )
X    uid_t uid;
X    {
X    struct passwd* pwd;
X    off_t off;
X    char buf[5000];
X    int i;
X
X    smpw_init();
X
X    /* Open if necessary. */
X    if ( setpwent() < 0 )
X	return (struct passwd*) 0;
X
X    /* First try indexed access. */
X    pwd = igetpwuid( uid, &off );
X    if ( pwd != (struct passwd*) 0 )
X	return pwd;
X
X    if ( off != NO_OFF )
X	{
X	/* Fall back on nearby search.  Seek backwards a few entries. */
X	if ( off < NEARBY_ENTRIES * BYTES_PER_ENTRY / 2 )
X	    off = 0;
X	else
X	    off -= NEARBY_ENTRIES * BYTES_PER_ENTRY / 2;
X	if ( fseek( pwdfp, off, 0 ) >= 0 )
X	    {
X	    if ( off != 0 )
X		/* Start at next newline. */
X		(void) fgets( buf, sizeof(buf), pwdfp);
X	    for ( i = 0; i < NEARBY_ENTRIES; ++i )
X		{
X		off = ftell( pwdfp );
X		pwd = fgetpwent( pwdfp );
X		if ( pwd == (struct passwd*) 0 )
X		    break;
X		if ( pwd->pw_uid == uid )
X		    {
X		    isetpwuid( uid, off );
X		    return pwd;
X		    }
X		}
X	    }
X	}
X
X    /* Fall back on linear search. */
X    rewind( pwdfp );
X    for (;;)
X	{
X	off = ftell( pwdfp );
X	pwd = fgetpwent( pwdfp );
X	if ( pwd == (struct passwd*) 0 )
X	    break;
X	if ( pwd->pw_uid == uid )
X	    {
X	    /* Got it.  Save the seek address for next time. */
X	    isetpwuid( uid, off );
X	    return pwd;
X	    }
X	}
X
X    /* Couldn't find it anywhere. */
X    isetpwuid( uid, NO_OFF );
X    return (struct passwd*) 0;
X    }
X
X/* namehash - hash a name */
Xstatic unsigned long
Xnamehash( name )
X    char* name;
X    {
X    char* cp;
X    unsigned long hash;
X
X    for ( cp = name, hash = 0; *cp != '\0'; ++cp )
X	hash = ( hash * NAMEHASHMULT + *cp ) % namehashsize;
X
X    return hash;
X    }
X
X/* isetuident - set the index entry for a given name hash
X**
X** isetuident() takes a hash entry and a uid and writes the uid
X** to the name index file.
X*/
Xstatic void
Xisetuident( ent, uid )
X    unsigned long ent;
X    uid_t uid;
X    {
X    /* Check that the name index file was opened successfully. */
X    if ( pwnfd == -1 )
X	return;
X    
X    /* Use the hash entry to index into the name index file. */
X    if ( lseek( pwnfd, (off_t) ent * sizeof(uid_t), L_SET ) < 0 )
X	return;
X
X    /* Write the uid. */
X    (void) write( pwnfd, (char*) &uid, sizeof(uid_t) );
X    }
X
X/* igetpwnam - locate a passwd entry for a given name by use of the index */
Xstatic struct passwd*
Xigetpwnam( name )
X    char* name;
X    {
X    uid_t uid;
X    unsigned long hash, h;
X    int first;
X    struct passwd* pwd;
X
X    /* Check that the name index file was opened successfully. */
X    if ( pwnfd == -1 )
X	return (struct passwd*) 0;
X    
X    /* Hash the name. */
X    hash = namehash( name ); 
X
X    for ( h = hash, first = 1;
X	  h != hash || first;
X	  h = ( h + 1 ) % namehashsize, first = 0 )
X	{
X	/* Use the hash to index into the name index file. */
X	if ( lseek( pwnfd, (off_t) h * sizeof(uid_t), L_SET ) < 0 )
X	    return (struct passwd*) 0;
X
X	/* Read out a uid. */
X	if ( read( pwnfd, (char*) &uid, sizeof(uid_t) ) != sizeof(uid_t) )
X	    return (struct passwd*) 0;
X
X	/* Has the hash chain run out? */
X	if ( uid == NO_UID )
X	    return (struct passwd*) 0;
X
X	/* Fetch the passwd entry for this uid. */
X	pwd = getpwuid( uid );
X	if ( pwd == (struct passwd*) 0 )
X	    {
X	    /* No uid?  This must be an obsolete entry.  Zero it and
X	    ** continue searching.
X	    */
X	    isetuident( h, NO_UID );
X	    continue;
X	    }
X
X	/* Check that it's the right one. */
X	if ( strcmp( pwd->pw_name, name ) == 0 )
X	    return pwd;
X	}
X
X    /* The whole name index was searched without a hit!  It must be
X    ** trashed.  Zero it.
X    */
X    (void) ftruncate( pwnfd, 0 );
X    (void) endpwent();
X    (void) setpwent();
X    return (struct passwd*) 0;
X    }
X
X/* isetuidnam - store a uid for a given name into the index */
Xstatic void
Xisetuidnam( name, uid )
X    char* name;
X    uid_t uid;
X    {
X    unsigned long hash, h;
X    int first;
X    uid_t juid;
X
X
X    /* Check that the name index file was opened successfully. */
X    if ( pwnfd == -1 )
X	return;
X    
X    /* Hash the name. */
X    hash = namehash( name ); 
X
X    for ( h = hash, first = 1;
X	  h != hash || first;
X	  h = ( h + 1 ) % namehashsize, first = 0 )
X	{
X	/* Use the hash to index into the name index file. */
X	if ( lseek( pwnfd, (off_t) h * sizeof(uid_t), L_SET ) < 0 )
X	    return;
X
X	/* Read out a uid. */
X	if ( read( pwnfd, (char*) &juid, sizeof(uid_t) ) != sizeof(uid_t) )
X	    return;
X
X	/* Is it free? */
X	if ( juid == NO_UID )
X	    {
X	    /* Fill it in.  Doesn't matter if someone else just filled
X	    ** in this hole, whoever loses the race this time will just
X	    ** try again next time. */
X	    isetuident( h, uid );
X	    return;
X	    }
X	}
X
X    /* The whole name index was searched without a hole!  It must be
X    ** trashed.  Zero it.
X    */
X    (void) ftruncate( pwnfd, 0 );
X    (void) endpwent();
X    (void) setpwent();
X    }
X
X/* getpwnam - locate a password entry for a given name
X**
X** getpwnam() locates a password file entry for the given name,
X** by use of the index if possible and by linear search otherwise.
X*/
Xstruct passwd*
Xgetpwnam( name )
X    char* name;
X    {
X    struct passwd* pwd;
X    off_t off;
X
X    smpw_init();
X
X    /* Open if necessary. */
X    if ( setpwent() < 0 )
X	return (struct passwd*) 0;
X
X    /* First try indexed access. */
X    pwd = igetpwnam( name );
X    if ( pwd != (struct passwd*) 0 )
X	return pwd;
X
X    /* Fall back on linear search. */
X    rewind( pwdfp );
X    for (;;)
X	{
X	off = ftell( pwdfp );
X	pwd = fgetpwent( pwdfp );
X	if ( pwd == (struct passwd*) 0 )
X	    break;
X	if ( strcmp( pwd->pw_name, name ) == 0 )
X	    {
X	    /* Got it.  Save the seek address and uid for next time. */
X	    isetpwuid( pwd->pw_uid, off );
X	    isetuidnam( name, pwd->pw_uid );
X	    return pwd;
X	    }
X	}
X
X    /* Couldn't find it anywhere. */
X    return (struct passwd*) 0;
X    }
X
X/* checkpwu - check and adjust the uid index file
X**
X** Checks that the uid index file is the right size.  If it's too
X** big, truncates it, if it's too small, adds NO_OFF entries.
X*/
Xstatic void
Xcheckpwu()
X    {
X    struct stat sb;
X    off_t goodsize;
X    off_t no_off_buf[1000];
X    int i;
X
X    if ( fstat( pwufd, &sb ) < 0 )
X	{ pwufd = -1; return; }
X    goodsize = ( maxuid + 1 ) * sizeof(off_t);
X    if ( sb.st_size > goodsize )
X	{
X	if ( ftruncate( pwufd, goodsize ) < 0 )
X	    { pwufd = -1; return; }
X	}
X    else if ( sb.st_size < goodsize )
X	{
X	for ( i = sizeof(no_off_buf) / sizeof(off_t) - 1; i >= 0; --i )
X	    no_off_buf[i] = NO_OFF;
X	if ( lseek( pwufd, sb.st_size, L_SET ) < 0 )
X	    { pwufd = -1; return; }
X	while ( sb.st_size + sizeof(no_off_buf) < goodsize )
X	    {
X	    if ( write( pwufd, (char*) no_off_buf, sizeof(no_off_buf) ) !=
X		 sizeof(no_off_buf) )
X		{ pwufd = -1; return; }
X	    sb.st_size += sizeof(no_off_buf);
X	    }
X	if ( sb.st_size < goodsize )
X	    if ( write( pwufd, (char*) no_off_buf, goodsize - sb.st_size ) !=
X		 goodsize - sb.st_size )
X		{ pwufd = -1; return; }
X	}
X    }
X
X/* checkpwn - check and adjust the name index file
X**
X** Checks that the name index file is the right size.  If it's too
X** big, truncates it, and if it's either too big or too small, writes
X** it full of NO_UID entries.
X*/
Xstatic void
Xcheckpwn()
X    {
X    struct stat sb;
X    off_t goodsize;
X    uid_t no_uid_buf[1000];
X    int i;
X
X    if ( fstat( pwnfd, &sb ) < 0 )
X	{ pwnfd = -1; return; }
X    goodsize = namehashsize * sizeof(uid_t);
X    if ( sb.st_size == goodsize )
X	return;
X    if ( sb.st_size > goodsize )
X	{
X	if ( ftruncate( pwnfd, goodsize ) < 0 )
X	    { pwnfd = -1; return; }
X	}
X    for ( i = sizeof(no_uid_buf) / sizeof(uid_t) - 1; i >= 0; --i )
X	no_uid_buf[i] = NO_UID;
X    if ( lseek( pwnfd, (off_t) 0, L_SET ) < 0 )
X	{ pwnfd = -1; return; }
X    i = 0;
X    while ( i + sizeof(no_uid_buf) < goodsize )
X	{
X	if ( write( pwnfd, (char*) no_uid_buf, sizeof(no_uid_buf) ) !=
X	     sizeof(no_uid_buf) )
X	    { pwnfd = -1; return; }
X	i += sizeof(no_uid_buf);
X	}
X    if ( i < goodsize )
X	if ( write( pwnfd, (char*) no_uid_buf, goodsize - i ) != goodsize - i )
X	    { pwnfd = -1; return; }
X    }
X
X/* setpwent - open the password file
X**
X** setpwent() opens the system password file, or rewinds it if it was
X** open already.
X*/
Xint
Xsetpwent()
X    {
X    smpw_init();
X
X    if ( pwdfp == (FILE*) 0 )
X	{
X	pwdfp = fopen( PWDFILE, "r" );
X	if ( pwdfp == (FILE*) 0 )
X	    return -1;
X	if ( maxuid == 0 )
X	    pwufd = pwnfd = -1;
X	else
X	{
X	    pwufd = open( PWDUFILE, O_RDWR );
X	    if ( pwufd != -1 )
X		checkpwu();
X	    pwnfd = open( PWDNFILE, O_RDWR );
X	    if ( pwnfd != -1 )
X		checkpwn();
X	    }
X	}
X    else
X	rewind( pwdfp );
X
X    return 0;
X    }
X
X/* endpwent - close a password file
X**
X** endpwent() closes the password file if open.
X*/
Xint
Xendpwent()
X    {
X    int ret;
X
X    smpw_init();
X
X    ret = 0;
X
X    if ( pwdfp != (FILE*) 0 )
X	if ( fclose( pwdfp ) != 0 )
X	    ret = -1;
X    pwdfp = (FILE*) 0;
X
X    if ( pwufd != -1 )
X	if ( close( pwufd ) < 0 )
X	    ret = -1;
X    pwufd = -1;
X
X    if ( pwnfd != -1 )
X	if ( close( pwnfd ) < 0 )
X	    ret = -1;
X    pwnfd = -1;
X
X    return ret;
X    }
SHAR_EOF
if test 15518 -ne "`wc -c < 'smpw.c'`"
then
	echo shar: error transmitting "'smpw.c'" '(should have been 15518 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(3501 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# Makefile for libsmpw
X#
X# Copyright (C) 1992 by Jef Poskanzer.
X#
X# Permission to use, copy, modify, and distribute this software and its
X# documentation for any purpose and without fee is hereby granted, provided
X# that the above copyright notice appear in all copies and that both that
X# copyright notice and this permission notice appear in supporting
X# documentation.  This software is provided "as is" without express or
X# implied warranty.
X
X# CONFIGURE: The maximum uid that the package should handle.  Not the
X# maximum number of uids, the maximum uid.  For instance, you might
X# have 8000 uids but the maximum uid might be 20000.  In that case
X# you might set MAXUID to 40000, to leave some room for expansion.
X#
X# If you want to change this number later, you only have to re-install,
X# no recompilation is necessary.  The index file size will be automatically
X# adjusted the next time anyone runs a program linked with this library.
X#MAXUID =	5000
XMAXUID =	65535
X
X# CONFIGURE: The number of name hash entries to use.  This should be
X# a prime number somewhat larger than the number of uids you expect
X# to handle.  Not the maximum uid, the number of uids.  Four times
X# larger is probably ok.
X#
X# Again, if you change this number later the index file gets automatically
X# adjusted, but in this case it also gets zeroed.  You should only do this
X# when no one is using the package, although it will clean everything up
X# eventually no matter what you do.
X#NAMEHASHSIZE =	10007
XNAMEHASHSIZE =	40009
X
X# CONFIGURE: The directory for the uid and name index files.
X#INDEXDIR =	/etc
XINDEXDIR =	/usr/public/lib/libsmpw
X
X# CONFIGURE: The names of the uid and name index files, and the size file.
XPWDUFILE =	$(INDEXDIR)/passwd.u
XPWDNFILE =	$(INDEXDIR)/passwd.n
XPWDSFILE =	$(INDEXDIR)/passwd.s
X
X# CONFIGURE: The directory you want the library installed into.
XLIBDIR =	/usr/public/lib
X
X# CONFIGURE: The directory tree you want the man pages installed into.
XMANDIR =	/usr/public/man
X
X# CONFIGURE: The manual section.
XMANSEC =	3
X
X# CONFIGURE: Your favorite C compiler.
XCC =		cc
X#CC =		gcc -ansi -pedantic -fpcc-struct-return -fcombine-regs -fstrength-reduce -finline-functions
X#CC =		gcc -ansi -pedantic -fpcc-struct-return
X
X# CONFIGURE: Your favorite C flags.
X#CFLAGS =	-g
XCFLAGS =	-O
X
X# Uncomment if you're compiling on a System V machine.
X#DSYSV =	-DSYSV
X
X# End of configurable definitions.
X
XDPWDUFILE =	-DPWDUFILE=\"$(PWDUFILE)\"
XDPWDNFILE =	-DPWDNFILE=\"$(PWDNFILE)\"
XDPWDSFILE =	-DPWDSFILE=\"$(PWDSFILE)\"
XDEFINES =	$(DPWDUFILE) $(DPWDNFILE) $(DPWDSFILE) $(DSYSV)
XLINTFLAGS =	-hxz
X
XSRCS =		smpw.c
XOBJS =		smpw.o
XLIBS =		libsmpw.a
XMANS =		libsmpw
X
XINCLUDES =	
XALLCFLAGS =	$(CFLAGS) $(DEFINES) $(INCLUDES)
XALLLINTFLAGS =	$(LINTFLAGS) $(DEFINES) $(INCLUDES)
X
Xall:		$(LIBS)
X
Xlibsmpw.a:	$(OBJS)
X	rm -f libsmpw.a
X	ar rc libsmpw.a $(OBJS)
X	-ranlib libsmpw.a
X
Xsmpw.o:		smpw.c
X	$(CC) $(ALLCFLAGS) -c smpw.c
X
Xlint:
X	for i in $(SRCS) ; do \
X	    echo $$i ; \
X	    lint $(ALLLINTFLAGS) $$i | sed -e '/but never used or defined/d' -e '/but not defined/d' -e '/but never used/d' ; \
X	done
X
Xinstall:	install.lib install.man
X
Xinstall.lib:	$(LIBS)
X	for i in $(LIBS) ; do \
X	    install -c $$i $(LIBDIR) ; \
X	    ranlib $(LIBDIR)/$$i ; \
X	done
X	-mkdir $(INDEXDIR)
X	touch $(PWDUFILE) $(PWDNFILE)
X	chmod 666 $(PWDUFILE) $(PWDNFILE)
X	echo $(MAXUID) $(NAMEHASHSIZE) > $(PWDSFILE)
X
Xinstall.man:
X	for i in $(MANS) ; do \
X	    install -c $$i.3 $(MANDIR)/man$(MANSEC)/$$i.$(MANSEC) ; \
X	done
X
Xclean:
X	rm -f *.a *.o a.out core
SHAR_EOF
if test 3501 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 3501 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0