libpwdgrp: rewritten to use malloced implementation
authorTito Ragusa <farmatito@tiscali.it>
Fri, 2 Jan 2015 20:37:59 +0000 (21:37 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 2 Jan 2015 20:37:59 +0000 (21:37 +0100)
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@tiscali.it>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
include/grp_.h
include/pwd_.h
libbb/bb_pwd.c
libpwdgrp/pwd_grp.c
libpwdgrp/pwd_grp_internal.c [deleted file]

index e5075e5a0bde50bd03e6c4bee9737aaefa94c577..f7b8d836f57c28a829fa5661292399ca303e79d3 100644 (file)
@@ -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
index 625b6f5a2eb1abf2f5dc00cbbf4854954f343b9a..d086f86e3222b6f9585c2d127cd4adf6175de890 100644 (file)
@@ -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
index 8250cd446d84c76a777bdb74725ccad0ce0fb543..4829b723a0195163df5e937090d4c171ab9d8460 100644 (file)
@@ -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
index 2060d78116dab74e2bf05a72702d954dc08f9901..ed8370124a0fbcbf43306d683b7fe872e7530173 100644 (file)
 /* vi: set sw=4 ts=4: */
-/* Copyright (C) 2003     Manuel Novoa III
+/* Copyright (C) 2014   Tito Ragusa <farmatito@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 (file)
index d6483be..0000000
+++ /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