/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:getpwent.c 12.0$ */
/* $ACIS:getpwent.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/libc/gen/RCS/getpwent.c,v $ */

#ifndef lint
static char *rcsid = "$Header:getpwent.c 12.0$";
#endif

#include <sys/nfs_defines.h>

/*
 * Copyright (c) 1984 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */
#ifdef VFS
/* 
 * Copyright (c) 1984 by Sun Microsystems, Inc.
 * @(#)getpwent.c	1.3 87/09/14 3.2/4.3NFSSRC
 */
#endif VFS

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getpwent.c	5.2 (Berkeley) 3/9/86";
#endif LIBC_SCCS and not lint

#include <stdio.h>
#include <pwd.h>
#include <ndbm.h>
#ifdef VFS
#include "../yp/ypclnt.h"
#include <sys/file.h>
#endif VFS

#ifdef VFS

#define MAXINT 0x7fffffff

extern void rewind();
extern long strtol();
extern int strcmp();
extern int strlen();
extern int fclose();
extern char *strcpy();
extern char *strncpy();
extern char *malloc();

void setpwent(), endpwent();

static char domain[256];
static struct passwd NULLPW = {NULL, NULL, 0, 0, 0, NULL, NULL, NULL, NULL};
#endif VFS
static char EMPTY[] = "";
static FILE *pwf = NULL;
#ifdef VFS
static char *yp;		/*pointer into yellow pages */
static int yplen;
#endif VFS
static char line[BUFSIZ+1];
static struct passwd passwd;
#ifdef VFS
static char *oldyp = NULL;	
static int oldyplen;
static int usingyellow;
static struct passwd *interpret();
static struct passwd *interpretwithsave();
static struct passwd *save();
static struct passwd *getnamefromyellow();
static struct passwd *getuidfromyellow();
#endif VFS

/*
 * The following are shared with getpwnamuid.c
 */
char	*_pw_file = "/etc/passwd";
DBM	*_pw_db;
int	_pw_stayopen;

#ifdef VFS

static struct list {
    char *name;
    struct list *nxt
} *minuslist;			/* list of - items */

static struct passwd *
fetchpw(key)
	datum key;
{
	register char *cp, *tp;

	if (key.dptr == 0)
		return ((struct passwd *)NULL);
	key = dbm_fetch(_pw_db, key);
	if (key.dptr == 0)
		return ((struct passwd *)NULL);
	cp = key.dptr;
	tp = line;

#define EXPAND(e)	passwd.pw_/**/e = tp; while (*tp++ = *cp++);
	EXPAND(name);
	EXPAND(passwd);
	bcopy(cp, (char *)&passwd.pw_uid, sizeof (int));
	cp += sizeof (int);
	bcopy(cp, (char *)&passwd.pw_gid, sizeof (int));
	cp += sizeof (int);
	bcopy(cp, (char *)&passwd.pw_quota, sizeof (int));
	cp += sizeof (int);
	EXPAND(comment);
	EXPAND(gecos);
	EXPAND(dir);
	EXPAND(shell);
	return (&passwd);
}

struct passwd *
getpwnam(nam)
	register char *nam;
{
	char line[BUFSIZ+1];
	datum key;
	struct passwd *pw;

	setpwent();
   if (usingyellow)
   {
	if (!pwf)
		return (NULL);
	while (fgets(line, BUFSIZ, pwf) != NULL) {
		if ((pw = interpret(line, strlen(line))) == NULL)
			continue;
		if (matchname(line, &pw, nam)) {
			endpwent();
			return (pw);
		}
	}
	endpwent();
	return (NULL);
   }
   /* otherwise, use the dbm method */
   if ((_pw_db == (DBM *)0) &&
		((_pw_db = dbm_open(_pw_file, O_RDONLY)) == (DBM *)0)) {
	oldcode:
		setpwent();
		while ((pw = getpwent()) && strcmp(nam, pw->pw_name))
			;
		if (!_pw_stayopen)
			endpwent();
		return (pw);
	}
	if (flock(dbm_dirfno(_pw_db), LOCK_SH) < 0) {
		dbm_close(_pw_db);
		_pw_db = (DBM *)0;
		goto oldcode;
	}
	key.dptr = nam;
	key.dsize = strlen(nam);
	pw = fetchpw(key);
	(void) flock(dbm_dirfno(_pw_db), LOCK_UN);
	if (!_pw_stayopen) {
		dbm_close(_pw_db);
		_pw_db = (DBM *)0;
	}
	return (pw);
}

struct passwd *
getpwuid(uid)
	int uid;
{
	datum key;
	char line[BUFSIZ+1];
   struct passwd *pw;

	setpwent();
   if (usingyellow)
   {
	if (!pwf)
		return (NULL);
	while (fgets(line, BUFSIZ, pwf) != NULL) {
		if  ((pw = interpret(line, strlen(line))) == NULL)
			continue;
		if (matchuid(line, &pw, uid)) {
			endpwent();
			return (pw);
		}
	}
	endpwent();
	return(NULL);
   }
   /* otherwise, use the dbm method */
	if ((_pw_db == (DBM *)0) &&
		((_pw_db = dbm_open(_pw_file, O_RDONLY)) == (DBM *)0)) {
	oldcode:
		setpwent();
		while ((pw = getpwent()) && pw->pw_uid != uid);
		if (!_pw_stayopen)
			endpwent();
		return (pw);
	}
	if (flock(dbm_dirfno(_pw_db), LOCK_SH) < 0) {
		dbm_close(_pw_db);
		_pw_db = (DBM *)0;
		goto oldcode;
	}
	key.dptr = (char *) &uid;
	key.dsize = sizeof uid;
	pw = fetchpw(key);
	(void) flock(dbm_dirfno(_pw_db), LOCK_UN);
	if (!_pw_stayopen) {
		dbm_close(_pw_db);
		_pw_db = (DBM *)0;
	}
	return (pw);
}
#endif VFS

void
setpwent()
{
	if (pwf == NULL)
		pwf = fopen(_pw_file, "r");
	else
		rewind(pwf);
#ifdef VFS
	if (yp)
		free(yp);
	yp = NULL;
	freeminuslist();
	yellowup(1);		/* recheck whether yellow pages are up */
#endif VFS
}

void
endpwent()
{
	if (pwf != NULL) {
		fclose(pwf);
		pwf = NULL;
	}
	if (_pw_db != (DBM *)0) {
		dbm_close(_pw_db);
		_pw_db = (DBM *)0;
		_pw_stayopen = 0;
	}
#ifdef VFS
	if (yp)
		free(yp);
	yp = NULL;
	freeminuslist();
	endnetgrent();
#endif VFS
}

#ifdef VFS
struct passwd *
fgetpwent(f)
	FILE *f;
{
	char line1[BUFSIZ+1];
	
	if(fgets(line1, BUFSIZ, f) == NULL)
		return(NULL);
	return (interpret(line1, strlen(line1)));
}
#endif VFS

static char *
pwskip(p)
register char *p;
{
	while (*p && *p != ':' && *p != '\n')
		++p;
#ifndef VFS
	if (*p)
		*p++ = 0;
#else !VFS
	if (*p == '\n')
		*p = '\0';
	else if (*p != '\0')
		*p++ = '\0';
#endif !VFS
	return(p);
}

struct passwd *
getpwent()
{
#ifndef VFS
	register char *p;
#else !VFS
	char line1[BUFSIZ+1];
	static struct passwd *savepw;
	struct passwd *pw;
	char *user; 
	char *mach;
	char *dom;
#endif !VFS

#ifdef VFS
   yellowup(0);
#endif VFS
	if (pwf == NULL) {
		if ((pwf = fopen( _pw_file, "r" )) == NULL)
			return(0);
	}
#ifndef VFS
	p = fgets(line, BUFSIZ, pwf);
	if (p == NULL)
		return(0);
#else !VFS
	for (;;) {
		if (yp) {
			pw = interpretwithsave(yp, yplen, savepw); 
			free(yp);
			if (pw == NULL)
				return(NULL);
			getnextfromyellow();
			if (!onminuslist(pw)) {
				return(pw);
			}
		} else if (getnetgrent(&mach,&user,&dom)) {
			if (user) {
				pw = getnamefromyellow(user, savepw);
				if (pw != NULL && !onminuslist(pw)) {
					return(pw);
				}
			}
		} else {
			endnetgrent();
			if (fgets(line1, BUFSIZ, pwf) == NULL)  {
				return(NULL);
			}
			if ((pw = interpret(line1, strlen(line1))) == NULL)
				return(NULL);
			switch(line1[0]) {
			case '+':
				if (!usingyellow)
					continue;
				if (strcmp(pw->pw_name, "+") == 0) {
					getfirstfromyellow();
					savepw = save(pw);
				} else if (line1[1] == '@') {
					savepw = save(pw);
					if (innetgr(pw->pw_name+2,(char *) NULL,"*",domain)) {
						/* include the whole yp database */
						getfirstfromyellow();
					} else {
						setnetgrent(pw->pw_name+2);
					}
				} else {
					/* 
					 * else look up this entry in yellow pages
				 	 */
					savepw = save(pw);
					pw = getnamefromyellow(pw->pw_name+1, savepw);
					if (pw != NULL && !onminuslist(pw)) {
						return(pw);
					}
				}
				break;
			case '-':
				if (!usingyellow)
					continue;
				if (line1[1] == '@') {
					if (innetgr(pw->pw_name+2,(char *) NULL,"*",domain)) {
						/* everybody was subtracted */
						return(NULL);
					}
					setnetgrent(pw->pw_name+2);
					while (getnetgrent(&mach,&user,&dom)) {
						if (user) {
							addtominuslist(user);
						}
					}
					endnetgrent();
				} else {
					addtominuslist(pw->pw_name+1);
				}
				break;
			default:
				if (!onminuslist(pw)) {
					return(pw);
				}
				break;
			}
		}
	}
}

static
matchname(line1, pwp, name)
	char line1[];
	struct passwd **pwp;
	char *name;
{
	struct passwd *savepw;
	struct passwd *pw = *pwp;

	switch(line1[0]) {
		case '+':
			if (strcmp(pw->pw_name, "+") == 0) {
				savepw = save(pw);
				pw = getnamefromyellow(name, savepw);
				if (pw) {
					*pwp = pw;
					return (1);
				}
				else
					return (0);
			}
			if (line1[1] == '@') {
				if (innetgr(pw->pw_name+2,(char *) NULL,name,domain)) {
					savepw = save(pw);
					pw = getnamefromyellow(name,savepw);
					if (pw) {
						*pwp = pw;
						return (1);
					}
				}
				return(0);
			}
			if (strcmp(pw->pw_name+1, name) == 0) {
				savepw = save(pw);
				pw = getnamefromyellow(pw->pw_name+1, savepw);
				if (pw) {
					*pwp = pw;
					return(1);
				}
				else
					return(0);
			}
			break;
		case '-':
			if (line1[1] == '@') {
				if (innetgr(pw->pw_name+2,(char *) NULL,name,domain)) {
					*pwp = NULL;
					return(1);
				}
			}
			else if (strcmp(pw->pw_name+1, name) == 0) {
				*pwp = NULL;
				return(1);
			}
			break;
		default:
			if (strcmp(pw->pw_name, name) == 0)
				return(1);
	}
	return(0);
}

static
matchuid(line1, pwp, uid)
	char line1[];
	struct passwd **pwp;
{
	struct passwd *savepw;
	struct passwd *pw = *pwp;
	char group[256];

	switch(line1[0]) {
		case '+':
			if (strcmp(pw->pw_name, "+") == 0) {
				savepw = save(pw);
				pw = getuidfromyellow(uid, savepw);
				if (pw) {
					*pwp = pw;
					return(1);
				} else {
					return(0);
				}
			}
			if (line1[1] == '@') {
				(void) strcpy(group,pw->pw_name+2);
				savepw = save(pw);
				pw = getuidfromyellow(uid,savepw);
				if (pw && innetgr(group,(char *) NULL,pw->pw_name,domain)) {
					*pwp = pw;
					return(1);
				} else {
					return(0);
				}
			}
			savepw = save(pw);
			pw = getnamefromyellow(pw->pw_name+1, savepw);
			if (pw && pw->pw_uid == uid) {
				*pwp = pw;
				return(1);
			} else
				return(0);
			break;
		case '-':
			if (line1[1] == '@') {
				(void) strcpy(group,pw->pw_name+2);
				pw = getuidfromyellow(uid,&NULLPW);
				if (pw && innetgr(group,(char *) NULL,pw->pw_name,domain)) {
					*pwp = NULL;
					return(1);
				}
			} else if (uid == uidof(pw->pw_name+1)) {
				*pwp = NULL;
				return(1);
			}
			break;
		default:
			if (pw->pw_uid == uid)
				return(1);
	}
	return(0);
}

static
uidof(name)
	char *name;
{
	struct passwd *pw;
	
	pw = getnamefromyellow(name, &NULLPW);
	if (pw)
		return (pw->pw_uid);
	else
		return(MAXINT);
}

static
getnextfromyellow()
{
	int reason;
	char *key;
	int keylen;

	reason = yp_next(domain, "passwd.byname",oldyp, oldyplen, &key
	    ,&keylen,&yp,&yplen);
	if (reason) {
#ifdef DEBUG
fprintf(stderr, "reason yp_next failed is %d\n", reason);
#endif
		yp = NULL;
	}
	if (oldyp)
		free(oldyp);
	oldyp = key;
	oldyplen = keylen;
}

static
getfirstfromyellow()
{
	int reason;
	char *key;
	int keylen;
	
	reason =  yp_first(domain, "passwd.byname", &key, &keylen, &yp, &yplen);
	if (reason) {
#ifdef DEBUG
fprintf(stderr, "reason yp_first failed is %d\n", reason);
#endif
		yp = NULL;
	}
	if (oldyp)
		free(oldyp);
	oldyp = key;
	oldyplen = keylen;
}

static struct passwd *
getnamefromyellow(name, savepw)
	char *name;
	struct passwd *savepw;
{
	struct passwd *pw;
	int reason;
	char *val;
	int vallen;
	
	reason = yp_match(domain, "passwd.byname", name, strlen(name)
		, &val, &vallen);
	if (reason) {
#ifdef DEBUG
fprintf(stderr, "reason yp_next failed is %d\n", reason);
#endif
		return(NULL);
	} else {
		pw = interpret(val, vallen);
		if (pw == NULL)
			return(NULL);
		free(val);
		if (savepw->pw_passwd && *savepw->pw_passwd)
			pw->pw_passwd =  savepw->pw_passwd;
		if (savepw->pw_gecos && *savepw->pw_gecos)
			pw->pw_gecos = savepw->pw_gecos;
		if (savepw->pw_dir && *savepw->pw_dir)
			pw->pw_dir = savepw->pw_dir;
		if (savepw->pw_shell && *savepw->pw_shell)
			pw->pw_shell = savepw->pw_shell;
		return(pw);
	}
}

static struct passwd *
getuidfromyellow(uid, savepw)
	int uid;
	struct passwd *savepw;
{
	struct passwd *pw;
	int reason;
	char *val;
	int vallen;
	char uidstr[20];
	
	(void) sprintf(uidstr, "%d", uid);
	reason = yp_match(domain, "passwd.byuid", uidstr, strlen(uidstr)
		, &val, &vallen);
	if (reason) {
#ifdef DEBUG
fprintf(stderr, "reason yp_next failed is %d\n", reason);
#endif
		return(NULL);
	} else {
		pw = interpret(val, vallen);
		free(val);
		if (pw == NULL)
			return(NULL);
		if (savepw->pw_passwd && *savepw->pw_passwd)
			pw->pw_passwd =  savepw->pw_passwd;
		if (savepw->pw_gecos && *savepw->pw_gecos)
			pw->pw_gecos = savepw->pw_gecos;
		if (savepw->pw_dir && *savepw->pw_dir)
			pw->pw_dir = savepw->pw_dir;
		if (savepw->pw_shell && *savepw->pw_shell)
			pw->pw_shell = savepw->pw_shell;
		return(pw);
	}
}

static struct passwd *
interpretwithsave(val, len, savepw)
	char *val;
	struct passwd *savepw;
{
	struct passwd *pw;
	
	if ((pw = interpret(val, len)) == NULL)
		return(NULL);
	if (savepw->pw_passwd && *savepw->pw_passwd)
		pw->pw_passwd =  savepw->pw_passwd;
	if (savepw->pw_gecos && *savepw->pw_gecos)
		pw->pw_gecos = savepw->pw_gecos;
	if (savepw->pw_dir && *savepw->pw_dir)
		pw->pw_dir = savepw->pw_dir;
	if (savepw->pw_shell && *savepw->pw_shell)
		pw->pw_shell = savepw->pw_shell;
	return(pw);
}

static struct passwd *
interpret(val, len)
	char *val;
{
	register char *p;
	char *end;
	long x;
	static struct passwd passwd;
	static char line[BUFSIZ+1];
	register int ypentry;

	(void) strncpy(line, val, len);
	p = line;
	line[len] = '\n';
	line[len+1] = 0;

	/*
	 * Set "ypentry" if this entry references the Yellow Pages;
	 * if so, null UIDs and GIDs are allowed (because they will
	 * be filled in from the matching Yellow Pages entry).
	 */
	ypentry = (*p == '+');

#endif !VFS
	passwd.pw_name = p;
	p = pwskip(p);
	passwd.pw_passwd = p;
	p = pwskip(p);
#ifndef VFS
	passwd.pw_uid = atoi(p);
	p = pwskip(p);
	passwd.pw_gid = atoi(p);
#else !VFS
	if (*p == ':' && !ypentry)
		/* check for non-null uid */
		return (NULL);
	x = strtol(p, &end, 10);
	p = end;
	if (*p++ != ':' && !ypentry)
		/* check for numeric value - must have stopped on the colon */
		return (NULL);
	passwd.pw_uid = x;
	if (*p == ':' && !ypentry)
		/* check for non-null gid */
		return (NULL);
	x = strtol(p, &end, 10);
	p = end;
	if (*p++ != ':' && !ypentry)
		/* check for numeric value - must have stopped on the colon */
		return (NULL);
	passwd.pw_gid = x;
#endif !VFS
	passwd.pw_quota = 0;
	passwd.pw_comment = EMPTY;
#ifndef VFS
	p = pwskip(p);
#endif !VFS
	passwd.pw_gecos = p;
	p = pwskip(p);
	passwd.pw_dir = p;
	p = pwskip(p);
	passwd.pw_shell = p;
	while (*p && *p != '\n')
		p++;
	*p = '\0';
	return(&passwd);
}

setpwfile(file)
	char *file;
{
	_pw_file = file;
}

#ifdef VFS
static
freeminuslist() {
	struct list *ls;
	
	for (ls = minuslist; ls != NULL; ls = ls->nxt) {
		free(ls->name);
		free((char *) ls);
	}
	minuslist = NULL;
}

static
addtominuslist(name)
	char *name;
{
	struct list *ls;
	char *buf;
	
	ls = (struct list *) malloc(sizeof(struct list));
	buf = malloc((unsigned) strlen(name) + 1);
	(void) strcpy(buf, name);
	ls->name = buf;
	ls->nxt = minuslist;
	minuslist = ls;
}

/* 
 * save away psswd, gecos, dir and shell fields, which are the only
 * ones which can be specified in a local + entry to override the
 * value in the yellow pages
 */
static struct passwd *
save(pw)
	struct passwd *pw;
{
	static struct passwd *sv;

	/* free up stuff from last call */
	if (sv) {
		free(sv->pw_passwd);
		free(sv->pw_gecos);
		free(sv->pw_dir);
		free(sv->pw_shell);
		free((char *) sv);
	}
	sv = (struct passwd *) malloc(sizeof(struct passwd));

	sv->pw_passwd = malloc((unsigned) strlen(pw->pw_passwd) + 1);
	(void) strcpy(sv->pw_passwd, pw->pw_passwd);

	sv->pw_gecos = malloc((unsigned) strlen(pw->pw_gecos) + 1);
	(void) strcpy(sv->pw_gecos, pw->pw_gecos);

	sv->pw_dir = malloc((unsigned) strlen(pw->pw_dir) + 1);
	(void) strcpy(sv->pw_dir, pw->pw_dir);

	sv->pw_shell = malloc((unsigned) strlen(pw->pw_shell) + 1);
	(void) strcpy(sv->pw_shell, pw->pw_shell);

	return(sv);
}

static
onminuslist(pw)
	struct passwd *pw;
{
	struct list *ls;
	register char *nm;

	nm = pw->pw_name;
	for (ls = minuslist; ls != NULL; ls = ls->nxt) {
		if (strcmp(ls->name,nm) == 0) {
			return(1);
		}
	}
	return(0);
}

#include <ctype.h>
#define DIGIT(x)	(isdigit(x) ? (x) - '0' : \
			islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
#define MBASE	('z' - 'a' + 1 + 10)

/*
 * strtol used by getpwent and getgrent
 */
long
strtol(str, ptr, base)
register char *str;
char **ptr;
register int base;
{
	register long val;
	register int c;
	int xx, neg = 0;
	
	if (ptr != (char **)0)
		*ptr = str; /* in case no number is formed */
	if (base < 0 || base > MBASE)
		return (0); /*base is invalid -- should be a fatal error */
	if (!isalnum(c = *str)) {
		while (isspace(c))
			c = *++str;
		switch (c) {
		case '-':
			neg++;
		case '+': /* fall - through */
			c = *++str;
		}
	}
	if (base == 0)
		if (c != '0')
			base = 10;
		else if (str[1] == 'x' || str[1] == 'X')
			base = 16;
		else
			base = 8;
	/*
	 * for any base > 10, the digits incrementally following
	 *	9 are assumed to be "abc..z" or "ABC..Z"
	 */
	if (!isalnum(c) || (xx = DIGIT(c)) >= base)
		return (0); /* no number formed */
	if (base == 16 && c == '0' && isxdigit(str[2]) &&
		(str[1] == 'x' || str[1] == 'X'))
		c = *(str +=2); /* skip over leading "0x" or "0X" */
	for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < base; )
		/* accumulate neg avoids surprises near MAXLONG */
		val = base * val - xx;
	if (ptr != (char **)0)
		*ptr = str;
	return (neg ? val : -val);
}

/*
 * check to see if yellow pages are up, and store that fact in usingyellow.
 * The check is performed once at startup and thereafter if flag is set
 */
static
yellowup(flag)
{
	static int firsttime = 1;
	char *key, *val;
	int keylen, vallen;
	
	if (firsttime || flag) {
	    firsttime = 0;
	    if (domain[0] == 0) {
		if (getdomainname(domain, sizeof(domain)) < 0) {
		    domain[0] = '\0';	/* missing system call implies that */
		    usingyellow = 0;	/*  we're not usingyellow */
		    return;
		}
	    }
	    usingyellow = !yp_bind(domain);
	}
}
#endif VFS
