/*  LAST EDIT: Wed Mar 31 06:28:54 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 <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <sys/types.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>

extern int __yp_check(char **) ;
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_setpwflag(void)
{
  __ypmode = 1 ;
}

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

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

char *
__yp_getdomain(void)
{
  if(!__ypdomain)
    {
      if(__yp_check(&__ypdomain) == 0)
	{
	  return NULL ;
	}
    }
  return(__ypdomain);
}

int
__ypparsepwd(struct passwd *pw, char *s)
{
  char *start, *end;
  
  start = s ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_name = start ;
  start = end + 1 ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_passwd = start ;
  start = end + 1 ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_uid = atoi(start);
  start = end + 1 ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_gid = atoi(start);
  start = end + 1 ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_gecos = start ;
  start = end + 1 ;
  end = strchr(start, ':') ;
  if (end == NULL)
    return (-1) ;
  *end = 0 ;
  pw->pw_dir = start ;
  start = end + 1 ;
  end = strchr(start, '\n') ;
  if (end != NULL)
    *end = 0 ;
  pw->pw_shell = start ;
  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, "passwd.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, "passwd.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 password file is known only here.  */

/* Structure containing info kept by each __pwdread caller.  */
typedef struct
  {
    char *buf;
    size_t buflen;
    struct passwd p;
  } pwdread_info;


/* Return a chunk of memory containing a pre-initialized `pwdread_info'.  */
PTR
DEFUN_VOID(__pwdalloc)
{
  pwdread_info *info = (PTR) malloc (sizeof(pwdread_info));
  if (info == NULL)
    return NULL;
  info->buf = NULL;
  info->buflen = 0;
  return info;
}

/* Read a password entry from STREAM, filling in P.  */
struct passwd *
DEFUN(__pwdread, (stream, p), FILE *stream AND PTR CONST p)
{
  register pwdread_info *CONST info = (pwdread_info *) p;
  char *start, *end;

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

 again:
  do
#if 0	/* Linux has __getline () in 4.5. */
    {
#ifndef MAX_CANON
#define MAX_CANON       256
#endif
#ifdef YP
      if (__ypmode)
	{
	  if (__yp_getline (&info->buf, &info->buflen) == -1)
	    return NULL;
	}
      else 
	{
#endif /* YP */
	  if ((info->buf == NULL) &&
	      ((info->buf = malloc (MAX_CANON)) == NULL))
	    {
	      errno = ENOMEM;
	      return NULL;
	    }
	  if (fgets (info->buf, MAX_CANON, stream) == NULL)
	    return NULL;
	  info->buflen = strlen (info->buf);
#ifdef YP
	}
#endif /* YP */
    }
#else
#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;
#endif /* linux */
  while (info->buf[0] == '#');

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

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

  info->p.pw_uid = (uid_t) strtol (end + 1, &end, 10);
  if (*end != ':')
    return NULL;
  info->p.pw_gid = (gid_t) strtol (end + 1, &end, 10);
  if (*end != ':')
    return NULL;

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

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

  start = end + 1;
#ifdef YP
  if (__ypmode)
    info->p.pw_shell = start;
  else
    {
      end = strchr (start, '\n');
      if (end == NULL)
	return NULL;
      *end = '\0';
      info->p.pw_shell = start;
    }
  if ((__noyp != 1) && (strcmp(info->p.pw_name,"+") == 0))
    {
      __ypmode = 1;
      goto again;
    }
#else
  end = strchr (start, '\n');
  if (end == NULL)
    return NULL;
  *end = '\0';
  info->p.pw_shell = start;
#endif /* YP */  

  return &info->p;
}

#ifdef YP
struct passwd *
DEFUN(__pwdread_noyp, (stream, p), FILE *stream AND PTR CONST p)
{
  struct passwd *pwptr ;
  __noyp = 1 ;
  __ypmode = 0 ;
  pwptr = __pwdread(stream, p);
  __noyp = 0 ;
  return(pwptr) ;
}


#endif
