about summary refs log tree commit diff
path: root/REORG.TODO/grp
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/grp')
-rw-r--r--REORG.TODO/grp/Makefile66
-rw-r--r--REORG.TODO/grp/Versions34
-rw-r--r--REORG.TODO/grp/compat-initgroups.c116
-rw-r--r--REORG.TODO/grp/fgetgrent.c84
-rw-r--r--REORG.TODO/grp/fgetgrent_r.c107
-rw-r--r--REORG.TODO/grp/getgrent.c29
-rw-r--r--REORG.TODO/grp/getgrent_r.c29
-rw-r--r--REORG.TODO/grp/getgrgid.c29
-rw-r--r--REORG.TODO/grp/getgrgid_r.c32
-rw-r--r--REORG.TODO/grp/getgrnam.c29
-rw-r--r--REORG.TODO/grp/getgrnam_r.c32
-rw-r--r--REORG.TODO/grp/grp-merge.c186
-rw-r--r--REORG.TODO/grp/grp-merge.h37
-rw-r--r--REORG.TODO/grp/grp.h203
-rw-r--r--REORG.TODO/grp/initgroups.c232
-rw-r--r--REORG.TODO/grp/putgrent.c76
-rw-r--r--REORG.TODO/grp/setgroups.c31
-rw-r--r--REORG.TODO/grp/testgrp.c41
-rw-r--r--REORG.TODO/grp/tst-putgrent.c167
-rw-r--r--REORG.TODO/grp/tst_fgetgrent.c119
-rw-r--r--REORG.TODO/grp/tst_fgetgrent.sh41
21 files changed, 1720 insertions, 0 deletions
diff --git a/REORG.TODO/grp/Makefile b/REORG.TODO/grp/Makefile
new file mode 100644
index 0000000000..7828e778d9
--- /dev/null
+++ b/REORG.TODO/grp/Makefile
@@ -0,0 +1,66 @@
+# Copyright (C) 1991-2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+#
+#	Sub-makefile for grp portion of the library.
+#
+subdir	:= grp
+
+include ../Makeconfig
+
+headers := grp.h
+
+routines := fgetgrent initgroups setgroups \
+	    getgrent getgrgid getgrnam putgrent \
+	    getgrent_r getgrgid_r getgrnam_r fgetgrent_r \
+	    grp-merge
+
+tests := testgrp tst-putgrent
+
+ifeq (yes,$(build-shared))
+test-srcs :=  tst_fgetgrent
+ifeq ($(run-built-tests),yes)
+tests-special += $(objpfx)tst_fgetgrent.out
+endif
+endif
+
+
+include ../Rules
+
+ifeq ($(have-thread-library),yes)
+
+CFLAGS-getgrgid_r.c = -fexceptions
+CFLAGS-getgrnam_r.c = -fexceptions
+CFLAGS-getgrent_r.c = -fexceptions
+CFLAGS-getgrent.c = -fexceptions
+CFLAGS-fgetgrent.c = -fexceptions
+CFLAGS-fgetgrent_r.c = -fexceptions $(libio-mtsafe)
+CFLAGS-putgrent.c = -fexceptions $(libio-mtsafe)
+CFLAGS-initgroups.c = -fexceptions
+CFLAGS-getgrgid.c = -fexceptions
+
+endif
+
+ifeq ($(run-built-tests),yes)
+# tst_fgetgrent currently only works with shared libraries
+ifeq (yes,$(build-shared))
+$(objpfx)tst_fgetgrent.out: tst_fgetgrent.sh $(objpfx)tst_fgetgrent
+	$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
+	$(evaluate-test)
+
+endif
+endif
diff --git a/REORG.TODO/grp/Versions b/REORG.TODO/grp/Versions
new file mode 100644
index 0000000000..096caa47c5
--- /dev/null
+++ b/REORG.TODO/grp/Versions
@@ -0,0 +1,34 @@
+libc {
+  GLIBC_2.0 {
+    # e*
+    endgrent;
+
+    # f*
+    fgetgrent; fgetgrent_r;
+
+    # g*
+    getgrent; getgrent_r; getgrgid; getgrgid_r; getgrnam; getgrnam_r;
+    getgroups;
+
+    # i*
+    initgroups;
+
+    # s*
+    setgrent; setgroups;
+  }
+  GLIBC_2.1 {
+    # p*
+    putgrent;
+  }
+  GLIBC_2.1.2 {
+    # g*
+    getgrent_r; getgrgid_r; getgrnam_r;
+  }
+  GLIBC_2.2.4 {
+    # g*
+    getgrouplist;
+  }
+  GLIBC_PRIVATE {
+    __merge_grp; __copy_grp;
+  }
+}
diff --git a/REORG.TODO/grp/compat-initgroups.c b/REORG.TODO/grp/compat-initgroups.c
new file mode 100644
index 0000000000..3dd50d2306
--- /dev/null
+++ b/REORG.TODO/grp/compat-initgroups.c
@@ -0,0 +1,116 @@
+/* Prototype for the setgrent functions we use here.  */
+typedef enum nss_status (*set_function) (void);
+
+/* Prototype for the endgrent functions we use here.  */
+typedef enum nss_status (*end_function) (void);
+
+/* Prototype for the setgrent functions we use here.  */
+typedef enum nss_status (*get_function) (struct group *, char *,
+					 size_t, int *);
+
+
+static enum nss_status
+compat_call (service_user *nip, const char *user, gid_t group, long int *start,
+	     long int *size, gid_t **groupsp, long int limit, int *errnop)
+{
+  struct group grpbuf;
+  enum nss_status status;
+  set_function setgrent_fct;
+  get_function getgrent_fct;
+  end_function endgrent_fct;
+  gid_t *groups = *groupsp;
+
+  getgrent_fct = __nss_lookup_function (nip, "getgrent_r");
+  if (getgrent_fct == NULL)
+    return NSS_STATUS_UNAVAIL;
+
+  setgrent_fct = __nss_lookup_function (nip, "setgrent");
+  if (setgrent_fct)
+    {
+      status = DL_CALL_FCT (setgrent_fct, ());
+      if (status != NSS_STATUS_SUCCESS)
+	return status;
+    }
+
+  endgrent_fct = __nss_lookup_function (nip, "endgrent");
+
+  struct scratch_buffer tmpbuf;
+  scratch_buffer_init (&tmpbuf);
+  enum nss_status result = NSS_STATUS_SUCCESS;
+
+  do
+    {
+      while ((status = DL_CALL_FCT (getgrent_fct,
+				     (&grpbuf, tmpbuf.data, tmpbuf.length,
+				      errnop)),
+	      status == NSS_STATUS_TRYAGAIN)
+	     && *errnop == ERANGE)
+        {
+	  if (!scratch_buffer_grow (&tmpbuf))
+	    {
+	      result = NSS_STATUS_TRYAGAIN;
+	      goto done;
+	    }
+        }
+
+      if (status != NSS_STATUS_SUCCESS)
+        goto done;
+
+      if (grpbuf.gr_gid != group)
+        {
+          char **m;
+
+          for (m = grpbuf.gr_mem; *m != NULL; ++m)
+            if (strcmp (*m, user) == 0)
+              {
+		/* Check whether the group is already on the list.  */
+		long int cnt;
+		for (cnt = 0; cnt < *start; ++cnt)
+		  if (groups[cnt] == grpbuf.gr_gid)
+		    break;
+
+		if (cnt == *start)
+		  {
+		    /* Matches user and not yet on the list.  Insert
+		       this group.  */
+		    if (__glibc_unlikely (*start == *size))
+		      {
+			/* Need a bigger buffer.  */
+			gid_t *newgroups;
+			long int newsize;
+
+			if (limit > 0 && *size == limit)
+			  /* We reached the maximum.  */
+			  goto done;
+
+			if (limit <= 0)
+			  newsize = 2 * *size;
+			else
+			  newsize = MIN (limit, 2 * *size);
+
+			newgroups = realloc (groups,
+					     newsize * sizeof (*groups));
+			if (newgroups == NULL)
+			  goto done;
+			*groupsp = groups = newgroups;
+			*size = newsize;
+		      }
+
+		    groups[*start] = grpbuf.gr_gid;
+		    *start += 1;
+		  }
+
+                break;
+              }
+        }
+    }
+  while (status == NSS_STATUS_SUCCESS);
+
+ done:
+  scratch_buffer_free (&tmpbuf);
+
+  if (endgrent_fct)
+    DL_CALL_FCT (endgrent_fct, ());
+
+  return result;
+}
diff --git a/REORG.TODO/grp/fgetgrent.c b/REORG.TODO/grp/fgetgrent.c
new file mode 100644
index 0000000000..302f8e7183
--- /dev/null
+++ b/REORG.TODO/grp/fgetgrent.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <grp.h>
+#include <libc-lock.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/* We need to protect the dynamic buffer handling.  */
+__libc_lock_define_initialized (static, lock);
+
+libc_freeres_ptr (static char *buffer);
+
+/* Read one entry from the given stream.  */
+struct group *
+fgetgrent (FILE *stream)
+{
+  static size_t buffer_size;
+  static struct group resbuf;
+  fpos_t pos;
+  struct group *result;
+  int save;
+
+  if (__builtin_expect (fgetpos (stream, &pos), 0) != 0)
+    return NULL;
+
+  /* Get lock.  */
+  __libc_lock_lock (lock);
+
+  /* Allocate buffer if not yet available.  */
+  if (buffer == NULL)
+    {
+      buffer_size = NSS_BUFLEN_GROUP;
+      buffer = malloc (buffer_size);
+    }
+
+  while (buffer != NULL
+	 && (__fgetgrent_r (stream, &resbuf, buffer, buffer_size, &result)
+	     == ERANGE))
+    {
+      char *new_buf;
+      buffer_size += NSS_BUFLEN_GROUP;
+      new_buf = realloc (buffer, buffer_size);
+      if (__glibc_unlikely (new_buf == NULL))
+	{
+	  /* We are out of memory.  Free the current buffer so that the
+	     process gets a chance for a normal termination.  */
+	  save = errno;
+	  free (buffer);
+	  __set_errno (save);
+	}
+      buffer = new_buf;
+
+      /* Reset the stream.  */
+      if (fsetpos (stream, &pos) != 0)
+	buffer = NULL;
+    }
+
+  if (buffer == NULL)
+    result = NULL;
+
+  /* Release lock.  Preserve error value.  */
+  save = errno;
+  __libc_lock_unlock (lock);
+  __set_errno (save);
+
+  return result;
+}
diff --git a/REORG.TODO/grp/fgetgrent_r.c b/REORG.TODO/grp/fgetgrent_r.c
new file mode 100644
index 0000000000..5a4107ba9c
--- /dev/null
+++ b/REORG.TODO/grp/fgetgrent_r.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+
+#include <libio/iolibio.h>
+#define flockfile(s) _IO_flockfile (s)
+#define funlockfile(s) _IO_funlockfile (s)
+
+/* Define a line parsing function using the common code
+   used in the nss_files module.  */
+
+#define STRUCTURE	group
+#define ENTNAME		grent
+struct grent_data {};
+
+#define TRAILING_LIST_MEMBER		gr_mem
+#define TRAILING_LIST_SEPARATOR_P(c)	((c) == ',')
+#include <nss/nss_files/files-parse.c>
+LINE_PARSER
+(,
+ STRING_FIELD (result->gr_name, ISCOLON, 0);
+ if (line[0] == '\0'
+     && (result->gr_name[0] == '+' || result->gr_name[0] == '-'))
+   {
+     result->gr_passwd = NULL;
+     result->gr_gid = 0;
+   }
+ else
+   {
+     STRING_FIELD (result->gr_passwd, ISCOLON, 0);
+     if (result->gr_name[0] == '+' || result->gr_name[0] == '-')
+       INT_FIELD_MAYBE_NULL (result->gr_gid, ISCOLON, 0, 10, , 0)
+     else
+       INT_FIELD (result->gr_gid, ISCOLON, 0, 10,)
+   }
+ )
+
+
+/* Read one entry from the given stream.  */
+int
+__fgetgrent_r (FILE *stream, struct group *resbuf, char *buffer, size_t buflen,
+	       struct group **result)
+{
+  char *p;
+  int parse_result;
+
+  flockfile (stream);
+  do
+    {
+      buffer[buflen - 1] = '\xff';
+      p = fgets_unlocked (buffer, buflen, stream);
+      if (__builtin_expect (p == NULL, 0) && feof_unlocked (stream))
+	{
+	  funlockfile (stream);
+	  *result = NULL;
+	  __set_errno (ENOENT);
+	  return errno;
+	}
+      if (__builtin_expect (p == NULL, 0) || buffer[buflen - 1] != '\xff')
+	{
+	  funlockfile (stream);
+	  *result = NULL;
+	  __set_errno (ERANGE);
+	  return errno;
+	}
+
+      /* Skip leading blanks.  */
+      while (isspace (*p))
+	++p;
+    } while (*p == '\0' || *p == '#'	/* Ignore empty and comment lines.  */
+	     /* Parse the line.  If it is invalid, loop to
+		get the next line of the file to parse.  */
+	     || ! (parse_result = parse_line (p, resbuf,
+					      (void *) buffer, buflen,
+					      &errno)));
+
+  funlockfile (stream);
+
+  if (__builtin_expect (parse_result, 0) == -1)
+    {
+      /* The parser ran out of space.  */
+      *result = NULL;
+      return errno;
+    }
+
+  *result = resbuf;
+  return 0;
+}
+weak_alias (__fgetgrent_r, fgetgrent_r)
diff --git a/REORG.TODO/grp/getgrent.c b/REORG.TODO/grp/getgrent.c
new file mode 100644
index 0000000000..9ba6655980
--- /dev/null
+++ b/REORG.TODO/grp/getgrent.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+
+#define LOOKUP_TYPE	struct group
+#define SETFUNC_NAME	setgrent
+#define	GETFUNC_NAME	getgrent
+#define	ENDFUNC_NAME	endgrent
+#define DATABASE_NAME	group
+#define BUFLEN		NSS_BUFLEN_GROUP
+
+#include "../nss/getXXent.c"
diff --git a/REORG.TODO/grp/getgrent_r.c b/REORG.TODO/grp/getgrent_r.c
new file mode 100644
index 0000000000..51eaf6f103
--- /dev/null
+++ b/REORG.TODO/grp/getgrent_r.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+
+#define LOOKUP_TYPE	struct group
+#define SETFUNC_NAME	setgrent
+#define	GETFUNC_NAME	getgrent
+#define	ENDFUNC_NAME	endgrent
+#define DATABASE_NAME	group
+#define BUFLEN		NSS_BUFLEN_GROUP
+
+#include "../nss/getXXent_r.c"
diff --git a/REORG.TODO/grp/getgrgid.c b/REORG.TODO/grp/getgrgid.c
new file mode 100644
index 0000000000..d29ff7fdc4
--- /dev/null
+++ b/REORG.TODO/grp/getgrgid.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrgid
+#define DATABASE_NAME	group
+#define ADD_PARAMS	gid_t gid
+#define ADD_VARIABLES	gid
+#define BUFLEN		NSS_BUFLEN_GROUP
+
+#include "../nss/getXXbyYY.c"
diff --git a/REORG.TODO/grp/getgrgid_r.c b/REORG.TODO/grp/getgrgid_r.c
new file mode 100644
index 0000000000..36443b8a04
--- /dev/null
+++ b/REORG.TODO/grp/getgrgid_r.c
@@ -0,0 +1,32 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+#include <grp-merge.h>
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrgid
+#define DATABASE_NAME	group
+#define ADD_PARAMS	gid_t gid
+#define ADD_VARIABLES	gid
+#define BUFLEN		NSS_BUFLEN_GROUP
+#define DEEPCOPY_FN	__copy_grp
+#define MERGE_FN	__merge_grp
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/grp/getgrnam.c b/REORG.TODO/grp/getgrnam.c
new file mode 100644
index 0000000000..73ed3a5c38
--- /dev/null
+++ b/REORG.TODO/grp/getgrnam.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrnam
+#define DATABASE_NAME	group
+#define ADD_PARAMS	const char *name
+#define ADD_VARIABLES	name
+#define BUFLEN		NSS_BUFLEN_GROUP
+
+#include "../nss/getXXbyYY.c"
diff --git a/REORG.TODO/grp/getgrnam_r.c b/REORG.TODO/grp/getgrnam_r.c
new file mode 100644
index 0000000000..d1dde3ffd7
--- /dev/null
+++ b/REORG.TODO/grp/getgrnam_r.c
@@ -0,0 +1,32 @@
+/* Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+
+#include <grp-merge.h>
+
+#define LOOKUP_TYPE	struct group
+#define FUNCTION_NAME	getgrnam
+#define DATABASE_NAME	group
+#define ADD_PARAMS	const char *name
+#define ADD_VARIABLES	name
+
+#define DEEPCOPY_FN	__copy_grp
+#define MERGE_FN	__merge_grp
+
+#include <nss/getXXbyYY_r.c>
diff --git a/REORG.TODO/grp/grp-merge.c b/REORG.TODO/grp/grp-merge.c
new file mode 100644
index 0000000000..77c494d159
--- /dev/null
+++ b/REORG.TODO/grp/grp-merge.c
@@ -0,0 +1,186 @@
+/* Group merging implementation.
+   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grp.h>
+#include <grp-merge.h>
+
+#define BUFCHECK(size)			\
+  ({					\
+    do					\
+      {					\
+	if (c + (size) > buflen)	\
+          {				\
+	    free (members);		\
+	    return ERANGE;		\
+	  }				\
+      }					\
+    while (0);				\
+  })
+
+int
+internal_function
+__copy_grp (const struct group srcgrp, const size_t buflen,
+	    struct group *destgrp, char *destbuf, char **endptr)
+{
+  size_t i;
+  size_t c = 0;
+  size_t len;
+  size_t memcount;
+  char **members = NULL;
+
+  /* Copy the GID.  */
+  destgrp->gr_gid = srcgrp.gr_gid;
+
+  /* Copy the name.  */
+  len = strlen (srcgrp.gr_name) + 1;
+  BUFCHECK (len);
+  memcpy (&destbuf[c], srcgrp.gr_name, len);
+  destgrp->gr_name = &destbuf[c];
+  c += len;
+
+  /* Copy the password.  */
+  len = strlen (srcgrp.gr_passwd) + 1;
+  BUFCHECK (len);
+  memcpy (&destbuf[c], srcgrp.gr_passwd, len);
+  destgrp->gr_passwd = &destbuf[c];
+  c += len;
+
+  /* Count all of the members.  */
+  for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++)
+    ;
+
+  /* Allocate a temporary holding area for the pointers to the member
+     contents, including space for a NULL-terminator.  */
+  members = malloc (sizeof (char *) * (memcount + 1));
+  if (members == NULL)
+    return ENOMEM;
+
+  /* Copy all of the group members to destbuf and add a pointer to each of
+     them into the 'members' array.  */
+  for (i = 0; srcgrp.gr_mem[i]; i++)
+    {
+      len = strlen (srcgrp.gr_mem[i]) + 1;
+      BUFCHECK (len);
+      memcpy (&destbuf[c], srcgrp.gr_mem[i], len);
+      members[i] = &destbuf[c];
+      c += len;
+    }
+  members[i] = NULL;
+
+  /* Copy the pointers from the members array into the buffer and assign them
+     to the gr_mem member of destgrp.  */
+  destgrp->gr_mem = (char **) &destbuf[c];
+  len = sizeof (char *) * (memcount + 1);
+  BUFCHECK (len);
+  memcpy (&destbuf[c], members, len);
+  c += len;
+  free (members);
+  members = NULL;
+
+  /* Save the count of members at the end.  */
+  BUFCHECK (sizeof (size_t));
+  memcpy (&destbuf[c], &memcount, sizeof (size_t));
+  c += sizeof (size_t);
+
+  if (endptr)
+    *endptr = destbuf + c;
+  return 0;
+}
+libc_hidden_def (__copy_grp)
+
+/* Check that the name, GID and passwd fields match, then
+   copy in the gr_mem array.  */
+int
+internal_function
+__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
+	     size_t buflen, struct group *mergegrp, char *mergebuf)
+{
+  size_t c, i, len;
+  size_t savedmemcount;
+  size_t memcount;
+  size_t membersize;
+  char **members = NULL;
+
+  /* We only support merging members of groups with identical names and
+     GID values. If we hit this case, we need to overwrite the current
+     buffer with the saved one (which is functionally equivalent to
+     treating the new lookup as NSS_STATUS_NOTFOUND).  */
+  if (mergegrp->gr_gid != savedgrp->gr_gid
+      || strcmp (mergegrp->gr_name, savedgrp->gr_name))
+    return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
+
+  /* Get the count of group members from the last sizeof (size_t) bytes in the
+     mergegrp buffer.  */
+  savedmemcount = (size_t) *(savedend - sizeof (size_t));
+
+  /* Get the count of new members to add.  */
+  for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++)
+    ;
+
+  /* Create a temporary array to hold the pointers to the member values from
+     both the saved and merge groups.  */
+  membersize = savedmemcount + memcount + 1;
+  members = malloc (sizeof (char *) * membersize);
+  if (members == NULL)
+    return ENOMEM;
+
+  /* Copy in the existing member pointers from the saved group
+     Note: this is not NULL-terminated yet.  */
+  memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount);
+
+  /* Back up into the savedbuf until we get back to the NULL-terminator of the
+     group member list. (This means walking back savedmemcount + 1 (char *) pointers
+     and the member count value.
+     The value of c is going to be the used length of the buffer backed up by
+     the member count and further backed up by the size of the pointers.  */
+  c = savedend - savedbuf
+      - sizeof (size_t)
+      - sizeof (char *) * (savedmemcount + 1);
+
+  /* Add all the new group members, overwriting the old NULL-terminator while
+     adding the new pointers to the temporary array.  */
+  for (i = 0; mergegrp->gr_mem[i]; i++)
+    {
+      len = strlen (mergegrp->gr_mem[i]) + 1;
+      BUFCHECK (len);
+      memcpy (&savedbuf[c], mergegrp->gr_mem[i], len);
+      members[savedmemcount + i] = &savedbuf[c];
+      c += len;
+    }
+  /* Add the NULL-terminator.  */
+  members[savedmemcount + memcount] = NULL;
+
+  /* Copy the member array back into the buffer after the member list and free
+     the member array.  */
+  savedgrp->gr_mem = (char **) &savedbuf[c];
+  len = sizeof (char *) * membersize;
+  BUFCHECK (len);
+  memcpy (&savedbuf[c], members, len);
+  c += len;
+
+  free (members);
+  members = NULL;
+
+  /* Finally, copy the results back into mergebuf, since that's the buffer
+     that we were provided by the caller.  */
+  return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
+}
+libc_hidden_def (__merge_grp)
diff --git a/REORG.TODO/grp/grp-merge.h b/REORG.TODO/grp/grp-merge.h
new file mode 100644
index 0000000000..1ad9b9a539
--- /dev/null
+++ b/REORG.TODO/grp/grp-merge.h
@@ -0,0 +1,37 @@
+/* Group merging implementation.
+   Copyright (C) 2016-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GRP_MERGE_H
+#define _GRP_MERGE_H 1
+
+#include <grp.h>
+
+/* Duplicate a grp struct (and its members). When no longer needed, the
+   calling function must free(newbuf).  */
+int
+__copy_grp (const struct group srcgrp, const size_t buflen,
+	    struct group *destgrp, char *destbuf, char **endptr)
+	    internal_function;
+
+/* Merge the member lists of two grp structs together.  */
+int
+__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
+	     size_t buflen, struct group *mergegrp, char *mergebuf)
+	     internal_function;
+
+#endif /* _GRP_MERGE_H */
diff --git a/REORG.TODO/grp/grp.h b/REORG.TODO/grp/grp.h
new file mode 100644
index 0000000000..0f833fff89
--- /dev/null
+++ b/REORG.TODO/grp/grp.h
@@ -0,0 +1,203 @@
+/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/*
+ *	POSIX Standard: 9.2.1 Group Database Access	<grp.h>
+ */
+
+#ifndef	_GRP_H
+#define	_GRP_H	1
+
+#include <features.h>
+
+__BEGIN_DECLS
+
+#include <bits/types.h>
+
+#define __need_size_t
+#include <stddef.h>
+
+
+/* For the Single Unix specification we must define this type here.  */
+#if (defined __USE_XOPEN || defined __USE_XOPEN2K) && !defined __gid_t_defined
+typedef __gid_t gid_t;
+# define __gid_t_defined
+#endif
+
+/* The group structure.	 */
+struct group
+  {
+    char *gr_name;		/* Group name.	*/
+    char *gr_passwd;		/* Password.	*/
+    __gid_t gr_gid;		/* Group ID.	*/
+    char **gr_mem;		/* Member list.	*/
+  };
+
+
+#ifdef __USE_MISC
+# include <bits/types/FILE.h>
+#endif
+
+
+#if defined __USE_MISC || defined __USE_XOPEN_EXTENDED
+/* Rewind the group-file stream.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern void setgrent (void);
+
+/* Close the group-file stream.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern void endgrent (void);
+
+/* Read an entry from the group-file stream, opening it if necessary.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern struct group *getgrent (void);
+#endif
+
+#ifdef	__USE_MISC
+/* Read a group entry from STREAM.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern struct group *fgetgrent (FILE *__stream);
+#endif
+
+#ifdef __USE_GNU
+/* Write the given entry onto the given stream.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern int putgrent (const struct group *__restrict __p,
+		     FILE *__restrict __f);
+#endif
+
+/* Search for an entry with a matching group ID.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern struct group *getgrgid (__gid_t __gid);
+
+/* Search for an entry with a matching group name.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern struct group *getgrnam (const char *__name);
+
+#ifdef __USE_POSIX
+
+# ifdef __USE_MISC
+/* Reasonable value for the buffer sized used in the reentrant
+   functions below.  But better use `sysconf'.  */
+#  define NSS_BUFLEN_GROUP	1024
+# endif
+
+/* 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.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+
+# ifdef __USE_GNU
+extern int getgrent_r (struct group *__restrict __resultbuf,
+		       char *__restrict __buffer, size_t __buflen,
+		       struct group **__restrict __result);
+# endif
+
+/* Search for an entry with a matching group ID.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+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.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern int getgrnam_r (const char *__restrict __name,
+		       struct group *__restrict __resultbuf,
+		       char *__restrict __buffer, size_t __buflen,
+		       struct group **__restrict __result);
+
+# ifdef	__USE_MISC
+/* Read a group entry from STREAM.  This function is not standardized
+   an probably never will.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern int fgetgrent_r (FILE *__restrict __stream,
+			struct group *__restrict __resultbuf,
+			char *__restrict __buffer, size_t __buflen,
+			struct group **__restrict __result);
+# endif
+
+#endif	/* POSIX or reentrant */
+
+
+#ifdef	__USE_MISC
+
+# define __need_size_t
+# include <stddef.h>
+
+/* Set the group set for the current user to GROUPS (N of them).  */
+extern int setgroups (size_t __n, const __gid_t *__groups) __THROW;
+
+/* Store at most *NGROUPS members of the group set for USER into
+   *GROUPS.  Also include GROUP.  The actual number of groups found is
+   returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern int getgrouplist (const char *__user, __gid_t __group,
+			 __gid_t *__groups, int *__ngroups);
+
+/* Initialize the group set for the current user
+   by reading the group database and using all groups
+   of which USER is a member.  Also include GROUP.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern int initgroups (const char *__user, __gid_t __group);
+
+#endif /* Use misc.  */
+
+__END_DECLS
+
+#endif /* grp.h  */
diff --git a/REORG.TODO/grp/initgroups.c b/REORG.TODO/grp/initgroups.c
new file mode 100644
index 0000000000..7a40813d5e
--- /dev/null
+++ b/REORG.TODO/grp/initgroups.c
@@ -0,0 +1,232 @@
+/* Copyright (C) 1989, 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <nsswitch.h>
+#include <scratch_buffer.h>
+
+#include "../nscd/nscd-client.h"
+#include "../nscd/nscd_proto.h"
+
+
+/* Type of the lookup function.  */
+typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
+						    long int *, long int *,
+						    gid_t **, long int, int *);
+
+/* The lookup function for the first entry of this service.  */
+extern int __nss_group_lookup (service_user **nip, const char *name,
+				   void **fctp);
+extern void *__nss_lookup_function (service_user *ni, const char *fct_name);
+
+extern service_user *__nss_group_database attribute_hidden;
+service_user *__nss_initgroups_database;
+static bool use_initgroups_entry;
+
+
+#include "compat-initgroups.c"
+
+
+static int
+internal_getgrouplist (const char *user, gid_t group, long int *size,
+		       gid_t **groupsp, long int limit)
+{
+#ifdef USE_NSCD
+  if (__nss_not_use_nscd_group > 0
+      && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
+    __nss_not_use_nscd_group = 0;
+  if (!__nss_not_use_nscd_group
+      && !__nss_database_custom[NSS_DBSIDX_group])
+    {
+      int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
+      if (n >= 0)
+	return n;
+
+      /* nscd is not usable.  */
+      __nss_not_use_nscd_group = 1;
+    }
+#endif
+
+  enum nss_status status = NSS_STATUS_UNAVAIL;
+  int no_more = 0;
+
+  /* Never store more than the starting *SIZE number of elements.  */
+  assert (*size > 0);
+  (*groupsp)[0] = group;
+  /* Start is one, because we have the first group as parameter.  */
+  long int start = 1;
+
+  if (__nss_initgroups_database == NULL)
+    {
+      if (__nss_database_lookup ("initgroups", NULL, "",
+				 &__nss_initgroups_database) < 0)
+	{
+	  if (__nss_group_database == NULL)
+	    no_more = __nss_database_lookup ("group", NULL, "compat files",
+					     &__nss_group_database);
+
+	  __nss_initgroups_database = __nss_group_database;
+	}
+      else
+	use_initgroups_entry = true;
+    }
+  else
+    /* __nss_initgroups_database might have been set through
+       __nss_configure_lookup in which case use_initgroups_entry was
+       not set here.  */
+    use_initgroups_entry = __nss_initgroups_database != __nss_group_database;
+
+  service_user *nip = __nss_initgroups_database;
+  while (! no_more)
+    {
+      long int prev_start = start;
+
+      initgroups_dyn_function fct = __nss_lookup_function (nip,
+							   "initgroups_dyn");
+      if (fct == NULL)
+	status = compat_call (nip, user, group, &start, size, groupsp,
+			      limit, &errno);
+      else
+	status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp,
+				    limit, &errno));
+
+      /* Remove duplicates.  */
+      long int cnt = prev_start;
+      while (cnt < start)
+	{
+	  long int inner;
+	  for (inner = 0; inner < prev_start; ++inner)
+	    if ((*groupsp)[inner] == (*groupsp)[cnt])
+	      break;
+
+	  if (inner < prev_start)
+	    (*groupsp)[cnt] = (*groupsp)[--start];
+	  else
+	    ++cnt;
+	}
+
+      /* This is really only for debugging.  */
+      if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
+	__libc_fatal ("illegal status in internal_getgrouplist");
+
+      /* For compatibility reason we will continue to look for more
+	 entries using the next service even though data has already
+	 been found if the nsswitch.conf file contained only a 'groups'
+	 line and no 'initgroups' line.  If the latter is available
+	 we always respect the status.  This means that the default
+	 for successful lookups is to return.  */
+      if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS)
+	  && nss_next_action (nip, status) == NSS_ACTION_RETURN)
+	 break;
+
+      if (nip->next == NULL)
+	no_more = -1;
+      else
+	nip = nip->next;
+    }
+
+  return start;
+}
+
+/* Store at most *NGROUPS members of the group set for USER into
+   *GROUPS.  Also include GROUP.  The actual number of groups found is
+   returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.  */
+int
+getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups)
+{
+  long int size = MAX (1, *ngroups);
+
+  gid_t *newgroups = (gid_t *) malloc (size * sizeof (gid_t));
+  if (__glibc_unlikely (newgroups == NULL))
+    /* No more memory.  */
+    // XXX This is wrong.  The user provided memory, we have to use
+    // XXX it.  The internal functions must be called with the user
+    // XXX provided buffer and not try to increase the size if it is
+    // XXX too small.  For initgroups a flag could say: increase size.
+    return -1;
+
+  int total = internal_getgrouplist (user, group, &size, &newgroups, -1);
+
+  memcpy (groups, newgroups, MIN (*ngroups, total) * sizeof (gid_t));
+
+  free (newgroups);
+
+  int retval = total > *ngroups ? -1 : total;
+  *ngroups = total;
+
+  return retval;
+}
+
+nss_interface_function (getgrouplist)
+
+/* Initialize the group set for the current user
+   by reading the group database and using all groups
+   of which USER is a member.  Also include GROUP.  */
+int
+initgroups (const char *user, gid_t group)
+{
+#if defined NGROUPS_MAX && NGROUPS_MAX == 0
+
+  /* No extra groups allowed.  */
+  return 0;
+
+#else
+
+  long int size;
+  gid_t *groups;
+  int ngroups;
+  int result;
+
+ /* We always use sysconf even if NGROUPS_MAX is defined.  That way, the
+     limit can be raised in the kernel configuration without having to
+     recompile libc.  */
+  long int limit = __sysconf (_SC_NGROUPS_MAX);
+
+  if (limit > 0)
+    /* We limit the size of the intially allocated array.  */
+    size = MIN (limit, 64);
+  else
+    /* No fixed limit on groups.  Pick a starting buffer size.  */
+    size = 16;
+
+  groups = (gid_t *) malloc (size * sizeof (gid_t));
+  if (__glibc_unlikely (groups == NULL))
+    /* No more memory.  */
+    return -1;
+
+  ngroups = internal_getgrouplist (user, group, &size, &groups, limit);
+
+  /* Try to set the maximum number of groups the kernel can handle.  */
+  do
+    result = setgroups (ngroups, groups);
+  while (result == -1 && errno == EINVAL && --ngroups > 0);
+
+  free (groups);
+
+  return result;
+#endif
+}
+
+nss_interface_function (initgroups)
diff --git a/REORG.TODO/grp/putgrent.c b/REORG.TODO/grp/putgrent.c
new file mode 100644
index 0000000000..5a12c70557
--- /dev/null
+++ b/REORG.TODO/grp/putgrent.c
@@ -0,0 +1,76 @@
+/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <nss.h>
+#include <stdio.h>
+#include <string.h>
+#include <grp.h>
+
+#define flockfile(s) _IO_flockfile (s)
+#define funlockfile(s) _IO_funlockfile (s)
+
+#define _S(x)	x ? x : ""
+
+/* Write an entry to the given stream.
+   This must know the format of the group file.  */
+int
+putgrent (const struct group *gr, FILE *stream)
+{
+  int retval;
+
+  if (__glibc_unlikely (gr == NULL) || __glibc_unlikely (stream == NULL)
+      || gr->gr_name == NULL || !__nss_valid_field (gr->gr_name)
+      || !__nss_valid_field (gr->gr_passwd)
+      || !__nss_valid_list_field (gr->gr_mem))
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  flockfile (stream);
+
+  if (gr->gr_name[0] == '+' || gr->gr_name[0] == '-')
+    retval = fprintf (stream, "%s:%s::",
+		      gr->gr_name, _S (gr->gr_passwd));
+  else
+    retval = fprintf (stream, "%s:%s:%lu:",
+		      gr->gr_name, _S (gr->gr_passwd),
+		      (unsigned long int) gr->gr_gid);
+  if (__builtin_expect (retval, 0) < 0)
+    {
+      funlockfile (stream);
+      return -1;
+    }
+
+  if (gr->gr_mem != NULL)
+    {
+      for (size_t i = 0; gr->gr_mem[i] != NULL; i++)
+	if (fprintf (stream, i == 0 ? "%s" : ",%s", gr->gr_mem[i]) < 0)
+	  {
+	    /* What else can we do?  */
+	    funlockfile (stream);
+	    return -1;
+	  }
+    }
+
+  retval = fputc_unlocked ('\n', stream);
+
+  funlockfile (stream);
+
+  return retval < 0 ? -1 : 0;
+}
diff --git a/REORG.TODO/grp/setgroups.c b/REORG.TODO/grp/setgroups.c
new file mode 100644
index 0000000000..c00c030e14
--- /dev/null
+++ b/REORG.TODO/grp/setgroups.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <grp.h>
+
+/* Set the group set for the current user to GROUPS (N of them).  */
+int
+setgroups (size_t n, const gid_t *groups)
+{
+  __set_errno (ENOSYS);
+  return -1;
+}
+libc_hidden_def (setgroups)
+
+stub_warning (setgroups)
diff --git a/REORG.TODO/grp/testgrp.c b/REORG.TODO/grp/testgrp.c
new file mode 100644
index 0000000000..892cfaaa21
--- /dev/null
+++ b/REORG.TODO/grp/testgrp.c
@@ -0,0 +1,41 @@
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main (int argc, char *argv[])
+{
+  uid_t me;
+  struct passwd *my_passwd;
+  struct group *my_group = NULL;
+  char **members;
+
+  me = getuid ();
+  my_passwd = getpwuid (me);
+  if (my_passwd == NULL)
+    printf ("Cannot find user entry for UID %d\n", me);
+  else
+    {
+      printf ("My login name is %s.\n", my_passwd->pw_name);
+      printf ("My uid is %d.\n", (int)(my_passwd->pw_uid));
+      printf ("My home directory is %s.\n", my_passwd->pw_dir);
+      printf ("My default shell is %s.\n", my_passwd->pw_shell);
+
+      my_group = getgrgid (my_passwd->pw_gid);
+      if (my_group == NULL)
+	printf ("No data for group %d found\n", my_passwd->pw_gid);
+      else
+	{
+	  printf ("My default group is %s (%d).\n",
+		  my_group->gr_name, (int)(my_passwd->pw_gid));
+	  printf ("The members of this group are:\n");
+	  for (members = my_group->gr_mem; *members != NULL; ++members)
+	    printf ("  %s\n", *members);
+	}
+    }
+
+  return my_passwd && my_group ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/REORG.TODO/grp/tst-putgrent.c b/REORG.TODO/grp/tst-putgrent.c
new file mode 100644
index 0000000000..0175ec0d1c
--- /dev/null
+++ b/REORG.TODO/grp/tst-putgrent.c
@@ -0,0 +1,167 @@
+/* Test for processing of invalid group entries.  [BZ #18724]
+   Copyright (C) 2015-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <grp.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool errors;
+
+static void
+check (struct group e, const char *expected)
+{
+  char *buf;
+  size_t buf_size;
+  FILE *f = open_memstream (&buf, &buf_size);
+
+  if (f == NULL)
+    {
+      printf ("open_memstream: %m\n");
+      errors = true;
+      return;
+    }
+
+  int ret = putgrent (&e, f);
+
+  if (expected == NULL)
+    {
+      if (ret == -1)
+	{
+	  if (errno != EINVAL)
+	    {
+	      printf ("putgrent: unexpected error code: %m\n");
+	      errors = true;
+	    }
+	}
+      else
+	{
+	  printf ("putgrent: unexpected success (\"%s\", \"%s\")\n",
+		  e.gr_name, e.gr_passwd);
+	  errors = true;
+	}
+    }
+  else
+    {
+      /* Expect success.  */
+      size_t expected_length = strlen (expected);
+      if (ret == 0)
+	{
+	  long written = ftell (f);
+
+	  if (written <= 0 || fflush (f) < 0)
+	    {
+	      printf ("stream error: %m\n");
+	      errors = true;
+	    }
+	  else if (buf[written - 1] != '\n')
+	    {
+	      printf ("FAILED: \"%s\" without newline\n", expected);
+	      errors = true;
+	    }
+	  else if (strncmp (buf, expected, written - 1) != 0
+		   || written - 1 != expected_length)
+	    {
+	      buf[written - 1] = '\0';
+	      printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
+		      buf, written - 1, expected, expected_length);
+	      errors = true;
+	    }
+	}
+      else
+	{
+	  printf ("FAILED: putgrent (expected \"%s\"): %m\n", expected);
+	  errors = true;
+	}
+    }
+
+  fclose (f);
+  free (buf);
+}
+
+static int
+do_test (void)
+{
+  check ((struct group) {
+      .gr_name = (char *) "root",
+    },
+    "root::0:");
+  check ((struct group) {
+      .gr_name = (char *) "root",
+      .gr_passwd = (char *) "password",
+      .gr_gid = 1234,
+      .gr_mem = (char *[2]) {(char *) "member1", NULL}
+    },
+    "root:password:1234:member1");
+  check ((struct group) {
+      .gr_name = (char *) "root",
+      .gr_passwd = (char *) "password",
+      .gr_gid = 1234,
+      .gr_mem = (char *[3]) {(char *) "member1", (char *) "member2", NULL}
+    },
+    "root:password:1234:member1,member2");
+
+  /* Bad values.  */
+  {
+    static const char *const bad_strings[] = {
+      ":",
+      "\n",
+      ":bad",
+      "\nbad",
+      "b:ad",
+      "b\nad",
+      "bad:",
+      "bad\n",
+      "b:a\nd"
+      ",",
+      "\n,",
+      ":,",
+      ",bad",
+      "b,ad",
+      "bad,",
+      NULL
+    };
+    for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
+      {
+	char *members[]
+	  = {(char *) "first", (char *) *bad, (char *) "last", NULL};
+	if (strpbrk (*bad, ":\n") != NULL)
+	  {
+	    check ((struct group) {
+		.gr_name = (char *) *bad,
+	      }, NULL);
+	    check ((struct group) {
+		.gr_name = (char *) "root",
+		.gr_passwd = (char *) *bad,
+	      }, NULL);
+	  }
+	check ((struct group) {
+	    .gr_name = (char *) "root",
+	    .gr_passwd = (char *) "password",
+	    .gr_mem = members,
+	  }, NULL);
+      }
+  }
+
+  return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/REORG.TODO/grp/tst_fgetgrent.c b/REORG.TODO/grp/tst_fgetgrent.c
new file mode 100644
index 0000000000..9045a655a3
--- /dev/null
+++ b/REORG.TODO/grp/tst_fgetgrent.c
@@ -0,0 +1,119 @@
+/* Copyright (C) 1999-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@arthur.rhein-neckar.de>, 1999.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+static int errors;
+
+static void
+write_users (FILE *f, int large_pos, int pos)
+{
+  int i;
+
+  if (pos == large_pos)
+    {
+      if (large_pos == 3)
+	fprintf (f, ":three");
+
+      /* we need more than 2048 bytes for proper testing.  */
+      for (i = 0; i < 500; i++)
+	fprintf (f, ",user%03d", i);
+    }
+  fprintf (f, "\n");
+
+}
+
+static void
+write_group (const char *filename, int pos)
+{
+  FILE *f;
+
+  f = fopen (filename, "w");
+  fprintf (f, "one:x:1:one");
+  write_users (f, pos, 1);
+  fprintf (f, "two:x:2:two");
+  write_users (f, pos, 2);
+  fprintf (f, "three:x:3");
+  write_users (f, pos, 3);
+  fclose (f);
+}
+
+static void
+test_entry (const char *name, gid_t gid, struct group *g)
+{
+  if (!g)
+    {
+      printf ("Error: Entry is empty\n");
+      errors++;
+      return;
+    }
+
+  if ((g->gr_gid == gid) && (strcmp (g->gr_name, name) == 0))
+    printf ("Ok: %s: %d\n", g->gr_name, g->gr_gid);
+  else
+    {
+      printf ("Error: %s: %d should be: %s: %d\n", g->gr_name, g->gr_gid,
+	      name, gid);
+      errors++;
+    }
+}
+
+
+static void
+test_fgetgrent (const char *filename)
+{
+  struct group *g;
+  FILE *f;
+
+  f = fopen (filename,"r");
+
+  g = fgetgrent (f);
+  test_entry ("one", 1, g);
+  g = fgetgrent (f);
+  test_entry ("two", 2, g);
+  g = fgetgrent (f);
+  test_entry ("three", 3, g);
+  fclose (f);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  char *file = tmpnam (NULL);
+  int i = 0;
+
+  if (argc > 1)
+    i = atoi (argv[1]);
+  if (i > 3)
+    i = 3;
+  if (i)
+    printf ("Large group is group: %d\n", i);
+  else
+    printf ("Not using a large group\n");
+  write_group (file, i);
+  test_fgetgrent (file);
+
+  remove (file);
+
+  return (errors != 0);
+}
diff --git a/REORG.TODO/grp/tst_fgetgrent.sh b/REORG.TODO/grp/tst_fgetgrent.sh
new file mode 100644
index 0000000000..4355a060b0
--- /dev/null
+++ b/REORG.TODO/grp/tst_fgetgrent.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+# Contributed by Andreas Jaeger <aj@arthur.rhein-neckar.de>, 1999.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+set -e
+
+common_objpfx=$1; shift
+test_program_prefix=$1; shift
+
+testout=${common_objpfx}/grp/tst_fgetgrent.out
+
+result=0
+
+${test_program_prefix} \
+${common_objpfx}grp/tst_fgetgrent 0 > ${testout} || result=1
+
+${test_program_prefix} \
+${common_objpfx}grp/tst_fgetgrent 1 >> ${testout} || result=1
+
+${test_program_prefix} \
+${common_objpfx}grp/tst_fgetgrent 2 >> ${testout} || result=1
+
+${test_program_prefix} \
+${common_objpfx}grp/tst_fgetgrent 3 >> ${testout} || result=1
+
+exit $result