/*  LAST EDIT: Wed Mar 31 07:39:35 1993 by Swen Thümmler (swen)  */
/* Copyright (C) 1991, 1992 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 Library General Public License as
published by the Free Software Foundation; either version 2 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.  */

#include <ansidecl.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <grp.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>

extern int __yp_check __P((char **)) ;
void __yp_setgrflag __P((void)) ;
void __yp_cleargrflag __P((void)) ;
int __yp_getgrmode __P((void)) ;
int __ypparsegrp __P((struct group *, char *)) ;
struct group * DEFUN(__grpread_noyp, (stream, p), FILE *stream AND PTR CONST g) ;
static char *__ypcurrent = NULL ;
static char *__ypdomain = NULL ;
static int __ypcurrentlen = 0 ;
static int __ypmode = 0 ;
static int __noyp = 0 ;
static char line[1024] ;

void
__yp_setgrflag(void)
{
  __ypmode = 1 ;
}

void
__yp_cleargrflag(void)
{
  __ypmode = 0 ;
  __ypcurrent = NULL ;
}

int
__yp_getgrmode(void)
{
  return(__ypmode) ;
}

int
__ypparsegrp(struct group *gr, char *s)
{
  char *start, *end ;
  char **members ;
  int i, max_members = 5 ;

  start = s;
  end = strchr(start, ':');
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  gr->gr_name = start ;
  start = end + 1 ; 
  end = strchr(start, ':');
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  gr->gr_passwd = start ;
  start = end + 1 ; 
  end = strchr(start, ':');
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  gr->gr_gid = atoi(start);
  start = end + 1 ;
  members = (char **) malloc (max_members * sizeof(char *)) ;
  if (members == NULL)
    return (-1) ;
  i = 0 ;
  do
    {
      start = end + 1;
      end = strchr (start, ',');
      if (end == NULL)
	{
	  end = strchr (start, '\n');
	  if (end == start)
	    break;
	  if (NULL != end)
	  {
	    *end = '\0';
	    end = NULL;
	  }
	}
      else
	*end = '\0';
      
      if (i == max_members - 2)
	{
	  max_members += 5;
	  members = (char **)
	    realloc ((PTR) members, max_members * sizeof (char *));
	  if (members == NULL)
	    return (-1);
	}
      
      members[i++] = start;
    } while (end != NULL);
  members[i] = NULL;
  gr->gr_mem = members;
  
  return 0;
  
}

static int
DEFUN(__yp_getline, (buf, buflen), char **buf AND size_t *buflen)
{
  char *key, *data;
  int keylen, datalen;
  int r;
  
  if(!__ypdomain)
    {
      if( __yp_check(&__ypdomain) == 0)
	return(-1);
    }
  if(__ypcurrent)
    {
      r = yp_next(__ypdomain, "group.byname",
		  __ypcurrent, __ypcurrentlen,
		  &key, &keylen, &data, &datalen);
      free(__ypcurrent);
      __ypcurrent = NULL;
      /*printf("yp_next %d\n", r);*/
      switch(r) 
	{
	case 0:
	  break;
	default:
	  __ypcurrent = NULL;
	  __ypmode = 0;
	  free(data);
	  data = NULL;
	  return(-1);
	}
      __ypcurrent = key;
      __ypcurrentlen = keylen;
      bcopy(data, line, datalen);
      free(data);
      data = NULL;
    }
  else
    {
      r = yp_first(__ypdomain, "group.byname",
		   &__ypcurrent, &__ypcurrentlen,
		   &data, &datalen);
      /*printf("yp_first %d\n", r);*/
      switch(r)
	{
	case 0:
	  break;
	default:
	  __ypmode = 0;
	  free(data);
	  return(-1);
	}
      bcopy(data, line, datalen);
      free(data);
      data = NULL;
    }
  line[datalen] = '\0';
  /*printf("line = %s\n", line);*/
  *buf = line;
  *buflen = datalen;
  return(0);
}
#endif /* YP */

/* This is the function that all the others are based on.
   The format of the group file is known only here.  */

/* Structure containing info kept by each __grpread caller.  */
typedef struct
  {
    char *buf;
    size_t buflen;
    size_t max_members;
    char **members;
    struct group g;
  } grpread_info;


/* Return a chunk of memory containing a pre-initialized `grpread_info'.  */
PTR
DEFUN_VOID(__grpalloc)
{
  grpread_info *info = (PTR) malloc (sizeof(grpread_info));
  if (info == NULL)
    return NULL;

  info->buf = NULL;
  info->buflen = 0;

  info->max_members = 5;
  info->members = (char **) malloc (5 * sizeof(char *));
  if (info->members == NULL)
    {
      free ((PTR) info);
      return NULL;
    }

  return info;
}

/* Read a group entry from STREAM, filling in G.  */
struct group *
DEFUN(__grpread, (stream, g), FILE *stream AND PTR CONST g)
{
  register grpread_info *CONST info = (grpread_info *) g;
  char *start, *end;
  register size_t i;

  /* Idiocy checks.  */
  if (stream == NULL)
    {
      errno = EINVAL;
      return NULL;
    }

 again:
  do
#ifdef YP
  if (__ypmode)
    {
      if (__yp_getline (&info->buf, &info->buflen) == -1)
	return NULL;
    }
  else
#endif /* YP */
    if (__getline (&info->buf, &info->buflen, stream) == -1)
      return NULL;
  while (info->buf[0] == '#');

  start = info->buf;
  end = strchr (start, ':');
  if (end == NULL)
    return NULL;
  *end = '\0';
  info->g.gr_name = start;

  start = end + 1;
  end = strchr (start, ':');
  if (end == NULL)
    return NULL;
  *end = '\0';
  info->g.gr_passwd = start;

  info->g.gr_gid = (gid_t) strtol (end + 1, &end, 10);
  if (*end != ':')
    return NULL;
#ifdef YP
  if ((__noyp != 1) && (strcmp(info->g.gr_name,"+") == 0))
    {
      __ypmode = 1;
      goto again;
    }
#endif /* YP */

  i = 0;
  do
    {
      start = end + 1;
      end = strchr (start, ',');
      if (end == NULL)
	{
#ifdef YP
	  if(! __ypmode)
	    {
	      end = strchr (start, '\n');
	      if (end == start)
		break;
	      if (end == NULL)
		return NULL;
	      *end = '\0';
	      end = NULL;
	    }
#else
	  end = strchr (start, '\n');
	  if (end == start)
	    break;
	  if (end == NULL)
	    return NULL;
	  *end = '\0';
	  end = NULL;
#endif /* YP */
	}
      else
	*end = '\0';

      if (i == info->max_members - 2)
	{
	  info->max_members += 5;
	  info->members = (char **)
	    realloc ((PTR) info->members, info->max_members * sizeof (char *));
	  if (info->members == NULL)
	    return NULL;
	}

      info->members[i++] = start;
    } while (end != NULL);
  info->members[i] = NULL;
  info->g.gr_mem = info->members;

  return &info->g;
}

#ifdef YP
struct group *
DEFUN(__grpread_noyp, (stream, p), FILE *stream AND PTR CONST g)
{
  struct group *grptr ;
  __noyp = 1 ;
  __ypmode = 0 ;
  grptr = __grpread(stream, g);
  __noyp = 0 ;
  return(grptr) ;
}
#endif
