[git commit] libpwdgrp: rewritten to use malloced implementation

Denys Vlasenko vda.linux at googlemail.com
Fri Jan 2 20:37:59 UTC 2015


commit: http://git.busybox.net/busybox/commit/?id=1da09cfacf1c4789cc74322857a098c2ddb06e31
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

This removed buffer size limitations.

function                                             old     new   delta
convert_to_struct                                      -     269    +269
getXXnam_r                                             -     204    +204
parse_common                                           -     185    +185
getXXnam                                               -     164    +164
tokenize                                               -     126    +126
bb_internal_getpwent_r                               102     167     +65
get_S                                                 30      88     +58
getgrouplist_internal                                195     240     +45
const_sp_db                                            -      20     +20
const_pw_db                                            -      20     +20
const_gr_db                                            -      20     +20
bb_internal_endpwent                                  27      36      +9
bb_internal_endgrent                                  27      36      +9
decode_one_format                                    726     734      +8
bb_internal_setpwent                                  17      24      +7
volume_id_probe_iso9660                              319     322      +3
scriptreplay_main                                    204     207      +3
mkfs_minix_main                                     2684    2687      +3
id_main                                              478     480      +2
hash_find                                            233     235      +2
pstree_main                                          321     322      +1
gr_off                                                 3       4      +1
expand_one_var                                      1579    1578      -1
pwf                                                    4       -      -4
grf                                                    4       -      -4
pack_gzip                                           1787    1780      -7
addattr32                                             67      56     -11
buffer_fill_and_print                                191     178     -13
dpkg_main                                           2944    2927     -17
bb_internal_setgrent                                  17       -     -17
bb_internal_getpwuid                                  38      19     -19
bb_internal_getgrgid                                  44      19     -25
bb_internal_getpwnam                                  38      11     -27
bb_internal_getgrnam                                  44      14     -30
bb_internal_fgetpwent_r                               51       -     -51
bb_internal_fgetgrent_r                               51       -     -51
bb_internal_getspnam_r                               121      42     -79
bb_internal_getpwnam_r                               121      39     -82
bb_internal_getgrent_r                               102       -    -102
bb__parsepwent                                       110       -    -110
bb_internal_getpwuid_r                               113       -    -113
bb_internal_getgrgid_r                               113       -    -113
bb__parsespent                                       120       -    -120
bb_internal_getgrnam_r                               121       -    -121
bb__pgsreader                                        213       -    -213
bb__parsegrent                                       226       -    -226
------------------------------------------------------------------------------
(add/remove: 8/13 grow/shrink: 14/11 up/down: 1224/-1556)    Total: -332 bytes
   text	   data	    bss	    dec	    hex	filename
 923471	    928	  17684	 942083	  e6003	busybox_old
 923167	    928	  17676	 941771	  e5ecb	busybox_unstripped

Signed-off-by: Tito Ragusa <farmatito at tiscali.it>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 include/grp_.h               |   54 +--
 include/pwd_.h               |   25 -
 libbb/bb_pwd.c               |   48 --
 libpwdgrp/pwd_grp.c          | 1238 ++++++++++++------------------------------
 libpwdgrp/pwd_grp_internal.c |   61 --
 5 files changed, 359 insertions(+), 1067 deletions(-)

diff --git a/include/grp_.h b/include/grp_.h
index e5075e5..f7b8d83 100644
--- a/include/grp_.h
+++ b/include/grp_.h
@@ -30,17 +30,9 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
  * so that function calls are directed to bb_internal_XXX replacements
  */
 #undef endgrent
-#define setgrent     bb_internal_setgrent
 #define endgrent     bb_internal_endgrent
-#define getgrent     bb_internal_getgrent
-#define fgetgrent    bb_internal_fgetgrent
-#define putgrent     bb_internal_putgrent
 #define getgrgid     bb_internal_getgrgid
 #define getgrnam     bb_internal_getgrnam
-#define getgrent_r   bb_internal_getgrent_r
-#define getgrgid_r   bb_internal_getgrgid_r
-#define getgrnam_r   bb_internal_getgrnam_r
-#define fgetgrent_r  bb_internal_fgetgrent_r
 #define getgrouplist bb_internal_getgrouplist
 #define initgroups   bb_internal_initgroups
 
@@ -48,60 +40,16 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 /* All function names below should be remapped by #defines above
  * in order to not collide with libc names. */
 
-
-/* Rewind the group-file stream.  */
-extern void setgrent(void);
-
 /* Close the group-file stream.  */
 extern void endgrent(void);
 
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-/* Read an entry from the group-file stream, opening it if necessary.  */
-extern struct group *getgrent(void);
-
-/* Read a group entry from STREAM.  */
-extern struct group *fgetgrent(FILE *__stream);
-
-/* Write the given entry onto the given stream.  */
-extern int putgrent(const struct group *__restrict __p,
-		FILE *__restrict __f);
-#endif
-
 /* Search for an entry with a matching group ID.  */
 extern struct group *getgrgid(gid_t __gid);
 
 /* Search for an entry with a matching group name.  */
 extern struct group *getgrnam(const char *__name);
 
-/* Reentrant versions of some of the functions above.
-
-   PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
-   The interface may change in later versions of this library.  But
-   the interface is designed following the principals used for the
-   other reentrant functions so the chances are good this is what the
-   POSIX people would choose.  */
-
-extern int getgrent_r(struct group *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct group **__restrict __result);
-
-/* Search for an entry with a matching group ID.  */
-extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct group **__restrict __result);
-
-/* Search for an entry with a matching group name.  */
-extern int getgrnam_r(const char *__restrict __name,
-		struct group *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct group **__restrict __result);
-
-/* Read a group entry from STREAM.  This function is not standardized
-   an probably never will.  */
-extern int fgetgrent_r(FILE *__restrict __stream,
-		struct group *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct group **__restrict __result);
+/* Reentrant versions of some of the functions above. */
 
 /* Store at most *NGROUPS members of the group set for USER into
    *GROUPS.  Also include GROUP.  The actual number of groups found is
diff --git a/include/pwd_.h b/include/pwd_.h
index 625b6f5..d086f86 100644
--- a/include/pwd_.h
+++ b/include/pwd_.h
@@ -34,20 +34,14 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 #define setpwent    bb_internal_setpwent
 #define endpwent    bb_internal_endpwent
 #define getpwent    bb_internal_getpwent
-#define fgetpwent   bb_internal_fgetpwent
-#define putpwent    bb_internal_putpwent
 #define getpwuid    bb_internal_getpwuid
 #define getpwnam    bb_internal_getpwnam
 #define getpwent_r  bb_internal_getpwent_r
-#define getpwuid_r  bb_internal_getpwuid_r
 #define getpwnam_r  bb_internal_getpwnam_r
-#define fgetpwent_r bb_internal_fgetpwent_r
-
 
 /* All function names below should be remapped by #defines above
  * in order to not collide with libc names. */
 
-
 /* Rewind the password-file stream.  */
 extern void setpwent(void);
 
@@ -57,13 +51,6 @@ extern void endpwent(void);
 #ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
 /* Read an entry from the password-file stream, opening it if necessary.  */
 extern struct passwd *getpwent(void);
-
-/* Read an entry from STREAM.  */
-extern struct passwd *fgetpwent(FILE *__stream);
-
-/* Write the given entry onto the given stream.  */
-extern int putpwent(const struct passwd *__restrict __p,
-		FILE *__restrict __f);
 #endif
 
 /* Search for an entry with a matching user ID.  */
@@ -84,23 +71,11 @@ extern int getpwent_r(struct passwd *__restrict __resultbuf,
 		char *__restrict __buffer, size_t __buflen,
 		struct passwd **__restrict __result);
 
-extern int getpwuid_r(uid_t __uid,
-		struct passwd *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct passwd **__restrict __result);
-
 extern int getpwnam_r(const char *__restrict __name,
 		struct passwd *__restrict __resultbuf,
 		char *__restrict __buffer, size_t __buflen,
 		struct passwd **__restrict __result);
 
-/* Read an entry from STREAM.  This function is not standardized and
-   probably never will.  */
-extern int fgetpwent_r(FILE *__restrict __stream,
-		struct passwd *__restrict __resultbuf,
-		char *__restrict __buffer, size_t __buflen,
-		struct passwd **__restrict __result);
-
 POP_SAVED_FUNCTION_VISIBILITY
 
 #endif
diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c
index 8250cd4..4829b72 100644
--- a/libbb/bb_pwd.c
+++ b/libbb/bb_pwd.c
@@ -110,51 +110,3 @@ unsigned long FAST_FUNC get_ug_id(const char *s,
 		return xname2id(s);
 	return r;
 }
-
-/* Experimental "mallocing" API.
- * The goal is nice: "we want to support a case when "guests" group is very large"
- * but the code is butt-ugly.
- */
-#if 0
-static char *find_latest(char last, char *cp)
-{
-	if (!cp)
-		return last;
-	cp += strlen(cp) + 1;
-	if (last < cp)
-		last = cp;
-	return last;
-}
-
-struct group* FAST_FUNC xmalloc_getgrnam(const char *name)
-{
-	struct {
-		struct group gr;
-		// May still be not enough!
-		char buf[64*1024 - sizeof(struct group) - 16];
-	} *s;
-	struct group *grp;
-	int r;
-	char *last;
-	char **gr_mem;
-
-	s = xmalloc(sizeof(*s));
-	r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp);
-	if (!grp) {
-		free(s);
-		return grp;
-	}
-	last = find_latest(s->buf, grp->gr_name);
-	last = find_latest(last, grp->gr_passwd);
-	gr_mem = grp->gr_mem;
-	while (*gr_mem)
-		last = find_latest(last, *gr_mem++);
-	gr_mem++; /* points past NULL */
-	if (last < (char*)gr_mem)
-		last = (char*)gr_mem;
-//FIXME: what if we get not only truncated, but also moved here?
-// grp->gr_name pointer and friends are invalid now!!!
-	s = xrealloc(s, last - (char*)s);
-	return grp;
-}
-#endif
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
index 2060d78..ed83701 100644
--- a/libpwdgrp/pwd_grp.c
+++ b/libpwdgrp/pwd_grp.c
@@ -1,598 +1,480 @@
 /* vi: set sw=4 ts=4: */
-/* Copyright (C) 2003     Manuel Novoa III
+/* Copyright (C) 2014   Tito Ragusa <farmatito at tiscali.it>
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
-/* Nov 6, 2003  Initial version.
+/* This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY!!
  *
- * NOTE: This implementation is quite strict about requiring all
- *    field seperators.  It also does not allow leading whitespace
- *    except when processing the numeric fields.  glibc is more
- *    lenient.  See the various glibc difference comments below.
+ * Rewrite of some parts. Main differences are:
  *
- * TODO:
- *    Move to dynamic allocation of (currently statically allocated)
- *      buffers; especially for the group-related functions since
- *      large group member lists will cause error returns.
+ * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically
+ *    allocated and reused by later calls. if ERANGE error pops up it is
+ *    reallocated to the size of the longest line found so far in the
+ *    passwd/group files and reused for later calls.
+ *    If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program
+ *    exit using the atexit function to make valgrind happy.
+ * 2) the passwd/group files:
+ *      a) must contain the expected number of fields (as per count of field
+ *         delimeters ":") or we will complain with a error message.
+ *      b) leading or trailing whitespace in fields is allowed and handled.
+ *      c) some fields are not allowed to be empty (e.g. username, uid/gid,
+ *         homedir, shell) and in this case NULL is returned and errno is
+ *         set to EINVAL. This behaviour could be easily changed by
+ *         modifying PW_DEF, GR_DEF, SP_DEF strings (uppercase
+ *         makes a field mandatory).
+ *      d) the string representing uid/gid must be convertible by strtoXX
+ *         functions or NULL is returned and errno is set to EINVAL.
+ *      e) leading or trailing whitespaces in member names and empty members
+ *         are allowed and handled.
+ * 3) the internal function for getgrouplist uses a dynamically allocated
+ *    buffer and retries with a bigger one in case it is too small;
+ * 4) the _r functions use the user supplied buffers that are never reallocated
+ *    but use mostly the same common code as the other functions.
+ * 5) at the moment only the functions really used by busybox code are
+ *    implemented, if you need a particular missing function it should be
+ *    easy to write it by using the internal common code.
  */
 
 #include "libbb.h"
-#include <assert.h>
-
-/**********************************************************************/
-/* Sizes for statically allocated buffers. */
 
-#define PWD_BUFFER_SIZE 256
-#define GRP_BUFFER_SIZE 256
-
-/**********************************************************************/
-/* Prototypes for internal functions. */
+/* S = string not empty, s = string maybe empty,  */
+/* I = uid,gid, l = long maybe empty, m = members,*/
+/* r = reserved */
+#define PW_DEF "SsIIsSS"
+#define GR_DEF "SsIm"
+#define SP_DEF "Ssllllllr"
+
+static const uint8_t pw_off[] ALIGN1 = {
+	offsetof(struct passwd, pw_name),       /* 0 S */
+	offsetof(struct passwd, pw_passwd),     /* 1 s */
+	offsetof(struct passwd, pw_uid),        /* 2 I */
+	offsetof(struct passwd, pw_gid),        /* 3 I */
+	offsetof(struct passwd, pw_gecos),      /* 4 s */
+	offsetof(struct passwd, pw_dir),        /* 5 S */
+	offsetof(struct passwd, pw_shell)       /* 6 S */
+};
+static const uint8_t gr_off[] ALIGN1 = {
+	offsetof(struct group, gr_name),        /* 0 S */
+	offsetof(struct group, gr_passwd),      /* 1 s */
+	offsetof(struct group, gr_gid),         /* 2 I */
+	offsetof(struct group, gr_mem)          /* 3 m (char **) */
+};
+#if ENABLE_USE_BB_SHADOW
+static const uint8_t sp_off[] ALIGN1 = {
+	offsetof(struct spwd, sp_namp),         /* 0 S Login name */
+	offsetof(struct spwd, sp_pwdp),         /* 1 s Encrypted password */
+	offsetof(struct spwd, sp_lstchg),       /* 2 l */
+	offsetof(struct spwd, sp_min),          /* 3 l */
+	offsetof(struct spwd, sp_max),          /* 4 l */
+	offsetof(struct spwd, sp_warn),         /* 5 l */
+	offsetof(struct spwd, sp_inact),        /* 6 l */
+	offsetof(struct spwd, sp_expire),       /* 7 l */
+	offsetof(struct spwd, sp_flag)          /* 8 r Reserved */
+};
+#endif
 
-static int bb__pgsreader(
-		int FAST_FUNC (*parserfunc)(void *d, char *line),
-		void *data,
-		char *__restrict line_buff,
-		size_t buflen,
-		FILE *f);
+struct const_passdb {
+	const char *filename;
+	const uint8_t *off;
+	const char def[10];
+	uint8_t numfields;
+	uint8_t size_of;
+};
+struct passdb {
+	const char *filename;
+	const uint8_t *off;
+	const char def[10];
+	uint8_t numfields;
+	uint8_t size_of;
+	FILE *fp;
+	void *malloced;
+};
 
-static int FAST_FUNC bb__parsepwent(void *pw, char *line);
-static int FAST_FUNC bb__parsegrent(void *gr, char *line);
+static const struct const_passdb const_pw_db = { _PATH_PASSWD, pw_off, PW_DEF, sizeof(PW_DEF)-1, sizeof(struct passwd) };
+static const struct const_passdb const_gr_db = { _PATH_GROUP , gr_off, GR_DEF, sizeof(GR_DEF)-1, sizeof(struct group) };
 #if ENABLE_USE_BB_SHADOW
-static int FAST_FUNC bb__parsespent(void *sp, char *line);
+static const struct const_passdb const_sp_db = { _PATH_SHADOW, sp_off, SP_DEF, sizeof(SP_DEF)-1, sizeof(struct spwd) };
 #endif
 
-/**********************************************************************/
 /* We avoid having big global data. */
-
 struct statics {
-	/* Smaller things first */
 	/* It's ok to use one buffer for getpwuid and getpwnam. Manpage says:
 	 * "The return value may point to a static area, and may be overwritten
 	 * by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
 	 */
-	struct passwd getpw_resultbuf;
-	struct group getgr_resultbuf;
-
-	char getpw_buffer[PWD_BUFFER_SIZE];
-	char getgr_buffer[GRP_BUFFER_SIZE];
-#if 0 //ENABLE_USE_BB_SHADOW
-	struct spwd getsp_resultbuf;
-	char getsp_buffer[PWD_BUFFER_SIZE];
-#endif
-// Not converted - too small to bother
-//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
-//FILE *pwf /*= NULL*/;
-//FILE *grf /*= NULL*/;
-//FILE *spf /*= NULL*/;
+	struct passdb db[2 + ENABLE_USE_BB_SHADOW];
+	char *tokenize_end;
 };
 
 static struct statics *ptr_to_statics;
+#define S     (*ptr_to_statics)
+#define has_S (ptr_to_statics)
 
 static struct statics *get_S(void)
 {
-	if (!ptr_to_statics)
-		ptr_to_statics = xzalloc(sizeof(*ptr_to_statics));
+	if (!ptr_to_statics) {
+		ptr_to_statics = xzalloc(sizeof(S));
+		memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db));
+		memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db));
+#if ENABLE_USE_BB_SHADOW
+		memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db));
+#endif
+	}
 	return ptr_to_statics;
 }
 
-/* Always use in this order, get_S() must be called first */
-#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
-#define BUFFER(name)    (S->name##_buffer)
-
 /**********************************************************************/
-/* For the various fget??ent_r funcs, return
- *
- *  0: success
- *  ENOENT: end-of-file encountered
- *  ERANGE: buflen too small
- *  other error values possible. See bb__pgsreader.
- *
- * Also, *result == resultbuf on success and NULL on failure.
- *
- * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
- *   We do not, as it really isn't an error if we reach the end-of-file.
- *   Doing so is analogous to having fgetc() set errno on EOF.
- */
+/* Internal functions                                                 */
 /**********************************************************************/
 
-int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
-				char *__restrict buffer, size_t buflen,
-				struct passwd **__restrict result)
+/* Divide the passwd/group/shadow record in fields
+ * by substituting the given delimeter
+ * e.g. ':' or ',' with '\0'.
+ * Returns the  number of fields found.
+ * Strips leading and trailing whitespace in fields.
+ */
+static int tokenize(char *buffer, int ch)
 {
-	int rv;
+	char *p = buffer;
+	char *s = p;
+	int num_fields = 0;
 
-	*result = NULL;
-
-	rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
-	if (!rv) {
-		*result = resultbuf;
+	for (;;) {
+		if (isblank(*s)) {
+			overlapping_strcpy(s, skip_whitespace(s));
+		}
+		if (*p == ch || *p == '\0') {
+			char *end = p;
+			while (p != s && isblank(p[-1]))
+				p--;
+			if (p != end)
+				overlapping_strcpy(p, end);
+			num_fields++;
+			if (*end == '\0') {
+				S.tokenize_end = p + 1;
+				return num_fields;
+			}
+			*p = '\0';
+			s = p + 1;
+		}
+		p++;
 	}
-
-	return rv;
 }
 
-int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
-				char *__restrict buffer, size_t buflen,
-				struct group **__restrict result)
-{
-	int rv;
+/* Returns !NULL on success and matching line broken up in fields by '\0' in buf.
+ * We require the expected number of fields to be found.
+ */
+static char *parse_common(FILE *fp, const char *filename,
+		int n_fields,
+		const char *key, int field_pos)
+{
+	int count = 0;
+	char *buf;
+
+	while ((buf = xmalloc_fgetline(fp)) != NULL) {
+		count++;
+		/* Skip empty lines, comment lines */
+		if (buf[0] == '\0' || buf[0] == '#')
+			goto free_and_next;
+		if (tokenize(buf, ':') != n_fields) {
+			/* number of fields is wrong */
+			bb_error_msg("bad record at %s:%u", filename, count);
+			goto free_and_next;
+		}
 
-	*result = NULL;
+/* Ugly hack: group db requires aqdditional buffer space
+ * for members[] array. If there is only one group, we need space
+ * for 3 pointers: alignment padding, group name, NULL.
+ * +1 for every additional group.
+ */
+		if (n_fields == sizeof(GR_DEF)-1) { /* if we read group file */
+			int resize = 3;
+			char *p = buf;
+			while (*p)
+				if (*p++ == ',')
+					resize++;
+			resize *= sizeof(char**);
+			resize += S.tokenize_end - buf;
+			buf = xrealloc(buf, resize);
+		}
 
-	rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
-	if (!rv) {
-		*result = resultbuf;
+		if (!key) {
+			/* no key specified: sequential read, return a record */
+			break;
+		}
+		if (strcmp(key, nth_string(buf, field_pos)) == 0) {
+			/* record found */
+			break;
+		}
+ free_and_next:
+		free(buf);
 	}
 
-	return rv;
+	return buf;
 }
 
-#if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_FOR_NOW
-int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
-				char *__restrict buffer, size_t buflen,
-				struct spwd **__restrict result)
+static char *parse_file(const char *filename,
+		int n_fields,
+		const char *key, int field_pos)
 {
-	int rv;
+	char *buf = NULL;
+	FILE *fp = fopen_for_read(filename);
 
-	*result = NULL;
-
-	rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
-	if (!rv) {
-		*result = resultbuf;
+	if (fp) {
+		buf = parse_common(fp, filename, n_fields, key, field_pos);
+		fclose(fp);
 	}
-
-	return rv;
-}
-#endif
-#endif
-
-/**********************************************************************/
-/* For the various fget??ent funcs, return NULL on failure and a
- * pointer to the appropriate struct (statically allocated) on success.
- * TODO: audit & stop using these in bbox, they pull in static buffers */
-/**********************************************************************/
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct passwd *fgetpwent(FILE *stream)
-{
-	struct statics *S;
-	struct passwd *resultbuf = RESULTBUF(getpw);
-	char *buffer = BUFFER(getpw);
-	struct passwd *result;
-
-	fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
-	return result;
+	return buf;
 }
 
-struct group *fgetgrent(FILE *stream)
+/* Convert passwd/group/shadow file record in buffer to a struct */
+static void *convert_to_struct(const char *def,	const unsigned char *off,
+		char *buffer, void *result)
 {
-	struct statics *S;
-	struct group *resultbuf = RESULTBUF(getgr);
-	char *buffer = BUFFER(getgr);
-	struct group *result;
-
-	fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
-	return result;
-}
-#endif
+	for (;;) {
+		void *member = (char*)result + (*off++);
 
+		if ((*def | 0x20) == 's') { /* s or S */
+			*(char **)member = (char*)buffer;
+			if (!buffer[0] && (*def == 'S')) {
+				errno = EINVAL;
+			}
+		}
+		if (*def == 'I') {
+			*(int *)member = bb_strtou(buffer, NULL, 10);
+		}
 #if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct spwd *fgetspent(FILE *stream)
-{
-	struct statics *S;
-	struct spwd *resultbuf = RESULTBUF(getsp);
-	char *buffer = BUFFER(getsp);
-	struct spwd *result;
-
-	fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
-	return result;
-}
+		if (*def == 'l') {
+			long n = -1;
+			if (buffer[0])
+				n = bb_strtol(buffer, NULL, 10);
+			*(long *)member = n;
+		}
 #endif
-
-#ifdef UNUSED_FOR_NOW
-int sgetspent_r(const char *string, struct spwd *result_buf,
-				char *buffer, size_t buflen, struct spwd **result)
-{
-	int rv = ERANGE;
-
-	*result = NULL;
-
-	if (buflen < PWD_BUFFER_SIZE) {
- DO_ERANGE:
-		errno = rv;
-		goto DONE;
-	}
-
-	if (string != buffer) {
-		if (strlen(string) >= buflen) {
-			goto DO_ERANGE;
+		if (*def == 'm') {
+			char **members;
+			int i = tokenize(buffer, ',');
+
+			/* Store members[] after buffer's end.
+			 * This is safe ONLY because there is a hack
+			 * in parse_common() which allocates additional space
+			 * at the end of malloced buffer!
+			 */
+			members = (char **)
+				( ((intptr_t)S.tokenize_end + sizeof(char**))
+				& -(intptr_t)sizeof(char**)
+				);
+
+			((struct group *)result)->gr_mem = members;
+			while (--i >= 0) {
+				*members++ = buffer;
+				buffer += strlen(buffer) + 1;
+			}
+			*members = NULL;
 		}
-		strcpy(buffer, string);
-	}
+		/* def "r" does nothing */
 
-	rv = bb__parsespent(result_buf, buffer);
-	if (!rv) {
-		*result = result_buf;
+		def++;
+		if (*def == '\0')
+			break;
+		buffer += strlen(buffer) + 1;
 	}
 
- DONE:
-	return rv;
-}
-#endif
-#endif /* ENABLE_USE_BB_SHADOW */
-
-/**********************************************************************/
-
-#define GETXXKEY_R_FUNC         getpwnam_r
-#define GETXXKEY_R_PARSER       bb__parsepwent
-#define GETXXKEY_R_ENTTYPE      struct passwd
-#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->pw_name, key))
-#define GETXXKEY_R_KEYTYPE      const char *__restrict
-#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
-#include "pwd_grp_internal.c"
-
-#define GETXXKEY_R_FUNC         getgrnam_r
-#define GETXXKEY_R_PARSER       bb__parsegrent
-#define GETXXKEY_R_ENTTYPE      struct group
-#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->gr_name, key))
-#define GETXXKEY_R_KEYTYPE      const char *__restrict
-#define GETXXKEY_R_PATHNAME     _PATH_GROUP
-#include "pwd_grp_internal.c"
-
-#if ENABLE_USE_BB_SHADOW
-#define GETXXKEY_R_FUNC         getspnam_r
-#define GETXXKEY_R_PARSER       bb__parsespent
-#define GETXXKEY_R_ENTTYPE      struct spwd
-#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->sp_namp, key))
-#define GETXXKEY_R_KEYTYPE      const char *__restrict
-#define GETXXKEY_R_PATHNAME     _PATH_SHADOW
-#include "pwd_grp_internal.c"
-#endif
-
-#define GETXXKEY_R_FUNC         getpwuid_r
-#define GETXXKEY_R_PARSER       bb__parsepwent
-#define GETXXKEY_R_ENTTYPE      struct passwd
-#define GETXXKEY_R_TEST(ENT)    ((ENT)->pw_uid == key)
-#define GETXXKEY_R_KEYTYPE      uid_t
-#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
-#include "pwd_grp_internal.c"
-
-#define GETXXKEY_R_FUNC         getgrgid_r
-#define GETXXKEY_R_PARSER       bb__parsegrent
-#define GETXXKEY_R_ENTTYPE      struct group
-#define GETXXKEY_R_TEST(ENT)    ((ENT)->gr_gid == key)
-#define GETXXKEY_R_KEYTYPE      gid_t
-#define GETXXKEY_R_PATHNAME     _PATH_GROUP
-#include "pwd_grp_internal.c"
-
-/**********************************************************************/
-/* TODO: audit & stop using these in bbox, they pull in static buffers */
-
-/* This one has many users */
-struct passwd *getpwuid(uid_t uid)
-{
-	struct statics *S;
-	struct passwd *resultbuf = RESULTBUF(getpw);
-	char *buffer = BUFFER(getpw);
-	struct passwd *result;
-
-	getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
+	if (errno)
+		result = NULL;
 	return result;
 }
 
-/* This one has many users */
-struct group *getgrgid(gid_t gid)
-{
-	struct statics *S;
-	struct group *resultbuf = RESULTBUF(getgr);
-	char *buffer = BUFFER(getgr);
-	struct group *result;
+/****** getXXnam/id_r */
 
-	getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
-	return result;
-}
-
-#if 0 //ENABLE_USE_BB_SHADOW
-/* This function is non-standard and is currently not built.  It seems
- * to have been created as a reentrant version of the non-standard
- * functions getspuid.  Why getspuid was added, I do not know. */
-int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
-			char *__restrict buffer, size_t buflen,
-			struct spwd **__restrict result)
+static int getXXnam_r(const char *name, uintptr_t db_and_field_pos, char *buffer, size_t buflen,
+		void *result)
 {
-	int rv;
-	struct passwd *pp;
-	struct passwd password;
-	char pwd_buff[PWD_BUFFER_SIZE];
+	void *struct_buf = *(void**)result;
+	char *buf;
+	struct passdb *db;
+	get_S();
+	db = &S.db[db_and_field_pos >> 2];
 
-	*result = NULL;
-	rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
-	if (!rv) {
-		rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
+	*(void**)result = NULL;
+	buf = parse_file(db->filename, db->numfields, name, db_and_field_pos & 3);
+	if (buf) {
+		size_t size = S.tokenize_end - buf;
+		if (size > buflen) {
+			errno = ERANGE;
+		} else {
+			memcpy(buffer, buf, size);
+			*(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
+		}
+		free(buf);
 	}
-
-	return rv;
-}
-
-/* This function is non-standard and is currently not built.
- * Why it was added, I do not know. */
-struct spwd *getspuid(uid_t uid)
-{
-	struct statics *S;
-	struct spwd *resultbuf = RESULTBUF(getsp);
-	char *buffer = BUFFER(getsp);
-	struct spwd *result;
-
-	getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
-	return result;
-}
-#endif
-
-/* This one has many users */
-struct passwd *getpwnam(const char *name)
-{
-	struct statics *S;
-	struct passwd *resultbuf = RESULTBUF(getpw);
-	char *buffer = BUFFER(getpw);
-	struct passwd *result;
-
-	getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
-	return result;
+	/* "The reentrant functions return zero on success.
+	 * In case of error, an error number is returned."
+	 * NB: not finding the record is also a "success" here:
+	 */
+	return errno;
 }
 
-/* This one has many users */
-struct group *getgrnam(const char *name)
+int getpwnam_r(const char *name, struct passwd *struct_buf, char *buffer, size_t buflen,
+				struct passwd **result)
 {
-	struct statics *S;
-	struct group *resultbuf = RESULTBUF(getgr);
-	char *buffer = BUFFER(getgr);
-	struct group *result;
-
-	getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
-	return result;
+	/* Why the "store buffer address in result" trick?
+	 * This way, getXXnam_r has the same ABI signature as getpwnam_r,
+	 * hopefully compiler can optimize tall call better in this case.
+	 */
+	*result = struct_buf;
+	return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result);
 }
-
-#if 0 //ENABLE_USE_BB_SHADOW
-struct spwd *getspnam(const char *name)
+#if ENABLE_USE_BB_SHADOW
+int getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen,
+			   struct spwd **result)
 {
-	struct statics *S;
-	struct spwd *resultbuf = RESULTBUF(getsp);
-	char *buffer = BUFFER(getsp);
-	struct spwd *result;
-
-	getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
-	return result;
+	*result = struct_buf;
+	return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result);
 }
 #endif
 
-/**********************************************************************/
-
-/* FIXME: we don't have such CONFIG_xx - ?! */
-
-#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
-static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
-# define LOCK		pthread_mutex_lock(&mylock)
-# define UNLOCK		pthread_mutex_unlock(&mylock);
-#else
-# define LOCK		((void) 0)
-# define UNLOCK		((void) 0)
-#endif
-
-static FILE *pwf /*= NULL*/;
-void setpwent(void)
-{
-	LOCK;
-	if (pwf) {
-		rewind(pwf);
-	}
-	UNLOCK;
-}
-
-void endpwent(void)
-{
-	LOCK;
-	if (pwf) {
-		fclose(pwf);
-		pwf = NULL;
-	}
-	UNLOCK;
-}
-
+/****** getXXent_r */
 
-int getpwent_r(struct passwd *__restrict resultbuf,
-			char *__restrict buffer, size_t buflen,
-			struct passwd **__restrict result)
+static int getXXent_r(void *struct_buf, char *buffer, size_t buflen,
+		void *result,
+		unsigned db_idx)
 {
-	int rv;
+	char *buf;
+	struct passdb *db;
+	get_S();
+	db = &S.db[db_idx];
 
-	LOCK;
-	*result = NULL;				/* In case of error... */
+	*(void**)result = NULL;
 
-	if (!pwf) {
-		pwf = fopen_for_read(_PATH_PASSWD);
-		if (!pwf) {
-			rv = errno;
-			goto ERR;
+	if (!db->fp) {
+		db->fp = fopen_for_read(db->filename);
+		if (!db->fp) {
+			return errno;
 		}
-		close_on_exec_on(fileno(pwf));
+		close_on_exec_on(fileno(db->fp));
 	}
 
-	rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
-	if (!rv) {
-		*result = resultbuf;
+	buf = parse_common(db->fp, db->filename, db->numfields, /*no search key:*/ NULL, 0);
+	if (buf) {
+		size_t size = S.tokenize_end - buf;
+		if (size > buflen) {
+			errno = ERANGE;
+		} else {
+			memcpy(buffer, buf, size);
+			*(void**)result = convert_to_struct(db->def, db->off, buffer, struct_buf);
+		}
+		free(buf);
 	}
-
- ERR:
-	UNLOCK;
-	return rv;
+	/* "The reentrant functions return zero on success.
+	 * In case of error, an error number is returned."
+	 * NB: not finding the record is also a "success" here:
+	 */
+	return errno;
 }
 
-static FILE *grf /*= NULL*/;
-void setgrent(void)
+int getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen, struct passwd **result)
 {
-	LOCK;
-	if (grf) {
-		rewind(grf);
-	}
-	UNLOCK;
+	return getXXent_r(struct_buf, buffer, buflen, result, 0);
 }
 
-void endgrent(void)
-{
-	LOCK;
-	if (grf) {
-		fclose(grf);
-		grf = NULL;
-	}
-	UNLOCK;
-}
+/****** getXXnam/id */
 
-int getgrent_r(struct group *__restrict resultbuf,
-			char *__restrict buffer, size_t buflen,
-			struct group **__restrict result)
+static void *getXXnam(const char *name, unsigned db_and_field_pos)
 {
-	int rv;
+	char *buf;
+	void *result;
+	struct passdb *db;
+	get_S();
+	db = &S.db[db_and_field_pos >> 2];
 
-	LOCK;
-	*result = NULL;				/* In case of error... */
+	result = NULL;
 
-	if (!grf) {
-		grf = fopen_for_read(_PATH_GROUP);
-		if (!grf) {
-			rv = errno;
-			goto ERR;
+	if (!db->fp) {
+		db->fp = fopen_for_read(db->filename);
+		if (!db->fp) {
+			return NULL;
 		}
-		close_on_exec_on(fileno(grf));
+		close_on_exec_on(fileno(db->fp));
 	}
 
-	rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
-	if (!rv) {
-		*result = resultbuf;
+	free(db->malloced);
+	db->malloced = NULL;
+	buf = parse_common(db->fp, db->filename, db->numfields, name, db_and_field_pos & 3);
+	if (buf) {
+		db->malloced = xzalloc(db->size_of);
+		result = convert_to_struct(db->def, db->off, buf, db->malloced);
 	}
-
- ERR:
-	UNLOCK;
-	return rv;
+	return result;
 }
 
-#ifdef UNUSED_FOR_NOW
-#if ENABLE_USE_BB_SHADOW
-static FILE *spf /*= NULL*/;
-void setspent(void)
+struct passwd *getpwnam(const char *name)
 {
-	LOCK;
-	if (spf) {
-		rewind(spf);
-	}
-	UNLOCK;
+	return getXXnam(name, (0 << 2) + 0);
 }
-
-void endspent(void)
+struct group *getgrnam(const char *name)
 {
-	LOCK;
-	if (spf) {
-		fclose(spf);
-		spf = NULL;
-	}
-	UNLOCK;
+	return getXXnam(name, (1 << 2) + 0);
 }
-
-int getspent_r(struct spwd *resultbuf, char *buffer,
-			size_t buflen, struct spwd **result)
+struct passwd *getpwuid(uid_t id)
 {
-	int rv;
-
-	LOCK;
-	*result = NULL;				/* In case of error... */
-
-	if (!spf) {
-		spf = fopen_for_read(_PATH_SHADOW);
-		if (!spf) {
-			rv = errno;
-			goto ERR;
-		}
-		close_on_exec_on(fileno(spf));
-	}
-
-	rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
-	if (!rv) {
-		*result = resultbuf;
-	}
-
- ERR:
-	UNLOCK;
-	return rv;
+	return getXXnam(utoa(id), (0 << 2) + 2);
 }
-#endif
-#endif /* UNUSED_FOR_NOW */
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct passwd *getpwent(void)
+struct group *getgrgid(gid_t id)
 {
-	static char line_buff[PWD_BUFFER_SIZE];
-	static struct passwd pwd;
-	struct passwd *result;
-
-	getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
-	return result;
+	return getXXnam(utoa(id), (1 << 2) + 2);
 }
 
-struct group *getgrent(void)
-{
-	static char line_buff[GRP_BUFFER_SIZE];
-	static struct group gr;
-	struct group *result;
+/****** end/setXXend */
 
-	getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
-	return result;
+void endpwent(void)
+{
+	if (has_S && S.db[0].fp) {
+		fclose(S.db[0].fp);
+		S.db[0].fp = NULL;
+	}
 }
-
-#if ENABLE_USE_BB_SHADOW
-struct spwd *getspent(void)
+void setpwent(void)
 {
-	static char line_buff[PWD_BUFFER_SIZE];
-	static struct spwd spwd;
-	struct spwd *result;
-
-	getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
-	return result;
+	if (has_S && S.db[0].fp) {
+		rewind(S.db[0].fp);
+	}
 }
-
-struct spwd *sgetspent(const char *string)
+void endgrent(void)
 {
-	static char line_buff[PWD_BUFFER_SIZE];
-	static struct spwd spwd;
-	struct spwd *result;
-
-	sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
-	return result;
+	if (has_S && S.db[1].fp) {
+		fclose(S.db[1].fp);
+		S.db[1].fp = NULL;
+	}
 }
-#endif
-#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
 
-static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
+/****** initgroups and getgrouplist */
+
+static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr,
+		const char *user, gid_t gid)
 {
-	FILE *grfile;
+	FILE *fp;
 	gid_t *group_list;
 	int ngroups;
-	struct group group;
-	char buff[PWD_BUFFER_SIZE];
+
+	get_S();
 
 	/* We alloc space for 8 gids at a time. */
 	group_list = xmalloc(8 * sizeof(group_list[0]));
 	group_list[0] = gid;
 	ngroups = 1;
 
-	grfile = fopen_for_read(_PATH_GROUP);
-	if (grfile) {
-		while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
+	fp = fopen_for_read(_PATH_GROUP);
+	if (fp) {
+		char *buf;
+		while ((buf = parse_common(fp, _PATH_GROUP, sizeof(GR_DEF)-1, NULL, 0)) != NULL) {
 			char **m;
-			assert(group.gr_mem); /* Must have at least a NULL terminator. */
+			struct group group;
+			if (!convert_to_struct(GR_DEF, gr_off, buf, &group))
+				goto next;
 			if (group.gr_gid == gid)
-				continue;
+				goto next;
 			for (m = group.gr_mem; *m; m++) {
 				if (strcmp(*m, user) != 0)
 					continue;
@@ -600,8 +482,10 @@ static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gi
 				group_list[ngroups++] = group.gr_gid;
 				break;
 			}
+ next:
+			free(buf);
 		}
-		fclose(grfile);
+		fclose(fp);
 	}
 	*ngroups_ptr = ngroups;
 	return group_list;
@@ -631,409 +515,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
 	free(group_list);
 	return ngroups_old;
 }
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
-{
-	int rv = -1;
-
-#if 0
-	/* glibc does this check */
-	if (!p || !f) {
-		errno = EINVAL;
-		return rv;
-	}
-#endif
-
-	/* No extra thread locking is needed above what fprintf does. */
-	if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
-				p->pw_name, p->pw_passwd,
-				(unsigned long)(p->pw_uid),
-				(unsigned long)(p->pw_gid),
-				p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
-		) {
-		rv = 0;
-	}
-
-	return rv;
-}
-
-int putgrent(const struct group *__restrict p, FILE *__restrict f)
-{
-	int rv = -1;
-
-#if 0
-	/* glibc does this check */
-	if (!p || !f) {
-		errno = EINVAL;
-		return rv;
-	}
-#endif
-
-	if (fprintf(f, "%s:%s:%lu:",
-				p->gr_name, p->gr_passwd,
-				(unsigned long)(p->gr_gid)) >= 0
-	) {
-		static const char format[] ALIGN1 = ",%s";
-
-		char **m;
-		const char *fmt;
-
-		fmt = format + 1;
-
-		assert(p->gr_mem);
-		m = p->gr_mem;
-
-		while (1) {
-			if (!*m) {
-				if (fputc('\n', f) >= 0) {
-					rv = 0;
-				}
-				break;
-			}
-			if (fprintf(f, fmt, *m) < 0) {
-				break;
-			}
-			m++;
-			fmt = format;
-		}
-	}
-
-	return rv;
-}
-#endif
-
-#if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_FOR_NOW
-static const unsigned char put_sp_off[] ALIGN1 = {
-	offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
-	offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
-	offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
-	offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
-	offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
-	offsetof(struct spwd, sp_expire)        /* 7 - not a char ptr */
-};
-
-int putspent(const struct spwd *p, FILE *stream)
-{
-	const char *fmt;
-	long x;
-	int i;
-	int rv = -1;
-
-	/* Unlike putpwent and putgrent, glibc does not check the args. */
-	if (fprintf(stream, "%s:%s:", p->sp_namp,
-				(p->sp_pwdp ? p->sp_pwdp : "")) < 0
-	) {
-		goto DO_UNLOCK;
-	}
-
-	for (i = 0; i < sizeof(put_sp_off); i++) {
-		fmt = "%ld:";
-		x = *(long *)((char *)p + put_sp_off[i]);
-		if (x == -1) {
-			fmt += 3;
-		}
-		if (fprintf(stream, fmt, x) < 0) {
-			goto DO_UNLOCK;
-		}
-	}
-
-	if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
-		goto DO_UNLOCK;
-	}
-
-	if (fputc('\n', stream) > 0) {
-		rv = 0;
-	}
-
- DO_UNLOCK:
-	return rv;
-}
-#endif
-#endif /* USE_BB_SHADOW */
-
-/**********************************************************************/
-/* Internal functions                                                 */
-/**********************************************************************/
-
-static const unsigned char pw_off[] ALIGN1 = {
-	offsetof(struct passwd, pw_name),       /* 0 */
-	offsetof(struct passwd, pw_passwd),     /* 1 */
-	offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
-	offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
-	offsetof(struct passwd, pw_gecos),      /* 4 */
-	offsetof(struct passwd, pw_dir),        /* 5 */
-	offsetof(struct passwd, pw_shell)       /* 6 */
-};
-
-static int FAST_FUNC bb__parsepwent(void *data, char *line)
-{
-	char *endptr;
-	char *p;
-	int i;
-
-	i = 0;
-	while (1) {
-		p = (char *) data + pw_off[i];
-
-		if (i < 2 || i > 3) {
-			*((char **) p) = line;
-			if (i == 6) {
-				return 0;
-			}
-			/* NOTE: glibc difference - glibc allows omission of
-			 * ':' seperators after the gid field if all remaining
-			 * entries are empty.  We require all separators. */
-			line = strchr(line, ':');
-			if (!line) {
-				break;
-			}
-		} else {
-			unsigned long t = strtoul(line, &endptr, 10);
-			/* Make sure we had at least one digit, and that the
-			 * failing char is the next field seperator ':'.  See
-			 * glibc difference note above. */
-			/* TODO: Also check for leading whitespace? */
-			if ((endptr == line) || (*endptr != ':')) {
-				break;
-			}
-			line = endptr;
-			if (i & 1) {		/* i == 3 -- gid */
-				*((gid_t *) p) = t;
-			} else {			/* i == 2 -- uid */
-				*((uid_t *) p) = t;
-			}
-		}
-
-		*line++ = '\0';
-		i++;
-	} /* while (1) */
-
-	return -1;
-}
-
-/**********************************************************************/
-
-static const unsigned char gr_off[] ALIGN1 = {
-	offsetof(struct group, gr_name),        /* 0 */
-	offsetof(struct group, gr_passwd),      /* 1 */
-	offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
-};
-
-static int FAST_FUNC bb__parsegrent(void *data, char *line)
-{
-	char *endptr;
-	char *p;
-	int i;
-	char **members;
-	char *end_of_buf;
-
-	end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
-	i = 0;
-	while (1) {
-		p = (char *) data + gr_off[i];
-
-		if (i < 2) {
-			*((char **) p) = line;
-			line = strchr(line, ':');
-			if (!line) {
-				break;
-			}
-			*line++ = '\0';
-			i++;
-		} else {
-			*((gid_t *) p) = strtoul(line, &endptr, 10);
-
-			/* NOTE: glibc difference - glibc allows omission of the
-			 * trailing colon when there is no member list.  We treat
-			 * this as an error. */
-
-			/* Make sure we had at least one digit, and that the
-			 * failing char is the next field seperator ':'.  See
-			 * glibc difference note above. */
-			if ((endptr == line) || (*endptr != ':')) {
-				break;
-			}
-
-			i = 1;				/* Count terminating NULL ptr. */
-			p = endptr;
-
-			if (p[1]) { /* We have a member list to process. */
-				/* Overwrite the last ':' with a ',' before counting.
-				 * This allows us to (1) test for initial ','
-				 * and (2) adds one ',' so that the number of commas
-				 * equals the member count. */
-				*p = ',';
-				do {
-					/* NOTE: glibc difference - glibc allows and trims leading
-					 * (but not trailing) space.  We treat this as an error. */
-					/* NOTE: glibc difference - glibc allows consecutive and
-					 * trailing commas, and ignores "empty string" users.  We
-					 * treat this as an error. */
-					if (*p == ',') {
-						++i;
-						*p = 0;	/* nul-terminate each member string. */
-						if (!*++p || (*p == ',') || isspace(*p)) {
-							goto ERR;
-						}
-					}
-				} while (*++p);
-			}
-
-			/* Now align (p+1), rounding up. */
-			/* Assumes sizeof(char **) is a power of 2. */
-			members = (char **)( (((intptr_t) p) + sizeof(char **))
-								 & ~((intptr_t)(sizeof(char **) - 1)) );
-
-			if (((char *)(members + i)) > end_of_buf) {	/* No space. */
-				break;
-			}
-
-			((struct group *) data)->gr_mem = members;
-
-			if (--i) {
-				p = endptr;	/* Pointing to char prior to first member. */
-				while (1) {
-					*members++ = ++p;
-					if (!--i)
-						break;
-					while (*++p)
-						continue;
-				}
-			}
-			*members = NULL;
-
-			return 0;
-		}
-	} /* while (1) */
-
- ERR:
-	return -1;
-}
-
-/**********************************************************************/
-
-#if ENABLE_USE_BB_SHADOW
-static const unsigned char sp_off[] ALIGN1 = {
-	offsetof(struct spwd, sp_namp),         /* 0: char* */
-	offsetof(struct spwd, sp_pwdp),         /* 1: char* */
-	offsetof(struct spwd, sp_lstchg),       /* 2: long */
-	offsetof(struct spwd, sp_min),          /* 3: long */
-	offsetof(struct spwd, sp_max),          /* 4: long */
-	offsetof(struct spwd, sp_warn),         /* 5: long */
-	offsetof(struct spwd, sp_inact),        /* 6: long */
-	offsetof(struct spwd, sp_expire),       /* 7: long */
-	offsetof(struct spwd, sp_flag)          /* 8: unsigned long */
-};
-
-static int FAST_FUNC bb__parsespent(void *data, char *line)
-{
-	char *endptr;
-	char *p;
-	int i;
-
-	i = 0;
-	while (1) {
-		p = (char *) data + sp_off[i];
-		if (i < 2) {
-			*((char **) p) = line;
-			line = strchr(line, ':');
-			if (!line) {
-				break; /* error */
-			}
-		} else {
-			*((long *) p) = strtoul(line, &endptr, 10);
-			if (endptr == line) {
-				*((long *) p) = -1L;
-			}
-			line = endptr;
-			if (i == 8) {
-				if (*line != '\0') {
-					break; /* error */
-				}
-				return 0; /* all ok */
-			}
-			if (*line != ':') {
-				break; /* error */
-			}
-		}
-		*line++ = '\0';
-		i++;
-	}
-
-	return EINVAL;
-}
-#endif
-
-/**********************************************************************/
-
-/* Reads until EOF, or until it finds a line which fits in the buffer
- * and for which the parser function succeeds.
- *
- * Returns 0 on success and ENOENT for end-of-file (glibc convention).
- */
-static int bb__pgsreader(
-		int FAST_FUNC (*parserfunc)(void *d, char *line),
-		void *data,
-		char *__restrict line_buff,
-		size_t buflen,
-		FILE *f)
-{
-	int skip;
-	int rv = ERANGE;
-
-	if (buflen < PWD_BUFFER_SIZE) {
-		errno = rv;
-		return rv;
-	}
-
-	skip = 0;
-	while (1) {
-		if (!fgets(line_buff, buflen, f)) {
-			if (feof(f)) {
-				rv = ENOENT;
-			}
-			break;
-		}
-
-		{
-			int line_len = strlen(line_buff) - 1;
-			if (line_len >= 0 && line_buff[line_len] == '\n') {
-				line_buff[line_len] = '\0';
-			} else
-			if (line_len + 2 == buflen) {
-				/* A start (or continuation) of overlong line */
-				skip = 1;
-				continue;
-			} /* else: a last line in the file, and it has no '\n' */
-		}
-
-		if (skip) {
-			/* This "line" is a remainder of overlong line, ignore */
-			skip = 0;
-			continue;
-		}
-
-		/* NOTE: glibc difference - glibc strips leading whitespace from
-		 * records.  We do not allow leading whitespace. */
-
-		/* Skip empty lines, comment lines, and lines with leading
-		 * whitespace. */
-		if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
-			if (parserfunc == bb__parsegrent) {
-				/* Do evil group hack:
-				 * The group entry parsing function needs to know where
-				 * the end of the buffer is so that it can construct the
-				 * group member ptr table. */
-				((struct group *) data)->gr_name = line_buff + buflen;
-			}
-			if (parserfunc(data, line_buff) == 0) {
-				rv = 0;
-				break;
-			}
-		}
-	} /* while (1) */
-
-	return rv;
-}
diff --git a/libpwdgrp/pwd_grp_internal.c b/libpwdgrp/pwd_grp_internal.c
deleted file mode 100644
index d6483be..0000000
--- a/libpwdgrp/pwd_grp_internal.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/* Copyright (C) 2003     Manuel Novoa III
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
-
-/* Nov 6, 2003  Initial version.
- *
- * NOTE: This implementation is quite strict about requiring all
- *    field seperators.  It also does not allow leading whitespace
- *    except when processing the numeric fields.  glibc is more
- *    lenient.  See the various glibc difference comments below.
- *
- * TODO:
- *    Move to dynamic allocation of (currently statically allocated)
- *      buffers; especially for the group-related functions since
- *      large group member lists will cause error returns.
- */
-
-#ifndef GETXXKEY_R_FUNC
-#error GETXXKEY_R_FUNC is not defined!
-#endif
-
-int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
-				GETXXKEY_R_ENTTYPE *__restrict resultbuf,
-				char *__restrict buffer, size_t buflen,
-				GETXXKEY_R_ENTTYPE **__restrict result)
-{
-	FILE *stream;
-	int rv;
-
-	*result = NULL;
-
-	stream = fopen_for_read(GETXXKEY_R_PATHNAME);
-	if (!stream)
-		return errno;
-	while (1) {
-		rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
-		if (!rv) {
-			if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
-				*result = resultbuf;
-				break;
-			}
-		} else {
-			if (rv == ENOENT) {  /* EOF encountered */
-				rv = 0;
-			}
-			break;
-		}
-	}
-	fclose(stream);
-
-	return rv;
-}
-
-#undef GETXXKEY_R_FUNC
-#undef GETXXKEY_R_PARSER
-#undef GETXXKEY_R_ENTTYPE
-#undef GETXXKEY_R_TEST
-#undef GETXXKEY_R_KEYTYPE
-#undef GETXXKEY_R_PATHNAME


More information about the busybox-cvs mailing list