RCS_ID_C "$Id: getgroupent.c,v 1.3 1993/10/15 01:16:22 ppessi Exp $";
/*
 * getgroupent.c - getgrp*() for network library
 *
 * This file is part of the AmiTCP/IP Network Library.
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 * Copyright © 1993 AmiTCP/IP Group, <AmiTCP-Group@hut.fi>
 *                  Helsinki University of Technology, Finland.
 *
 * Created      : Sun Jun 20 17:52:37 1993 ppessi
 * Last modified: Thu Oct 14 19:25:25 1993 ppessi
 */

#include <exec/memory.h>
#include <dos/dos.h>

#include <string.h>
#include <sys/errno.h>

#include "grp.h"

#if !NETLIB
#error 
#else
#include <proto/dos.h>

#include <errno.h>
#define SetNetErr(x) (errno=(x))
static BPTR grpfh = NULL;

#define INTERNAL static

#endif

/*
 * Parsing function
 */
extern int do_parse(UBYTE *str, int n, UBYTE **array);

/* End reading group entities */
INTERNAL void EndGroupEnt(void)
{
  if (grpfh) Close(grpfh), grpfh = NULL;
}

/* Restart reading group entities */
INTERNAL void SetGroupEnt(void)
{
  if (grpfh) EndGroupEnt();
  
  grpfh = Open(_PATH_GROUP, MODE_OLDFILE);
#if TESTING
  atexit(EndGroupEnt);
#endif
}

/* Read the next entry from the group-file database */
INTERNAL struct group *GetGroupEnt(struct group *g, ULONG size)
{
  UBYTE *p, *buffer = (UBYTE*)g;
  UBYTE **members;
  ULONG s;
  int err = 0;

  if (size - sizeof(struct group) < 0) {
    err = ENOBUFS;
    goto bad;
  }

  memset(buffer, sizeof(struct group), 0);

  size   -= sizeof(struct group) + 1; 
  buffer += sizeof(struct group);
  /* fix a bug in the FGetS routine */
  buffer[size] = '\0';

  if (!grpfh) 
    SetGroupEnt();

  if (!grpfh || !FGets(grpfh, buffer, size)) {
    err = ENOENT;
    goto bad;
  }

  /* Make space for the member list */
  for (s = 2, p = buffer; *p; p++) {
    if (*p == ',') 
      s++;
  }
  if (*--p != '\n' || p - buffer + s * sizeof(*members) > size) {
    /* Too long! */
    err = ENOBUFS;
    goto bad;
  }

  {
    UBYTE *from = p + 1, *to = p + s * sizeof(*members) + 1;
    while (from >= buffer) {
      *to-- = *from--;
    }
    buffer = to + 1;
  }

  if (err = do_parse(buffer, 4, (UBYTE **)g))
    goto bad;

  /* Parse the group ID field */
  if (StrToLong((UBYTE*)g->gr_gid, &g->gr_gid) <= 0)
  if ((s = StrToLong(p, &g->gr_gid)) <= 0 || p[s] != '|') {
    err = EFTYPE;
    goto bad;
  }

  /* Parse the memberlist */
  p = (UBYTE*)g->gr_mem;
  for (g->gr_mem = members = (UBYTE **)(g + 1); *p;) {
    *members++ = p;
    while (*p && *p++ != ',')
      ;
  }
  *members = NULL;

  return g;

 bad:
  SetNetErr(err);
  EndGroupEnt();
  return (struct group*) NULL;
}

/* Search for an entry with a matching group ID */
INTERNAL struct group *GetGroupGID(ULONG gid, 
			  struct group *g, 
			  ULONG size)
{
  while ((g = GetGroupEnt(g, size)) && (g->gr_gid != gid))
    ;
  EndGroupEnt();
  return g;
}

/* Search for an entry with a matching group name */
INTERNAL struct group *GetGroupName(const UBYTE *name, 
			   struct group *g, 
			   ULONG size)
{
  while ((g = GetGroupEnt(g, size)) && strcmp(g->gr_name, name))
    ;
  EndGroupEnt();
  return g;
}

/*
 * A global structure for Unix compatible functions
 */
#define GSTORE_SIZE 1024
static struct group * gstore = NULL;

#include <stdlib.h>

static struct group * alloc_gstore(void)
{
  return gstore = (struct group *)malloc(GSTORE_SIZE);
}

/* 
 * POSIX compatible getgrgid()
 */
struct group *getgrgid(gid_t gid)
{
  if (!gstore && !alloc_gstore()) {
    return NULL;
  }

  return GetGroupGID((ULONG) gid, gstore, GSTORE_SIZE);
}

/*
 * POSIX compatible getgrnam
 */
struct group *getgrnam(const char *name)
{
  if (!gstore && !alloc_gstore()) {
    return NULL;
  }

  return GetGroupName((UBYTE *) name, gstore, GSTORE_SIZE);
}

