#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#include <libintl.h>

#include "nisaddent.h"

#ifndef _
#define _(String) gettext (String)
#endif

static char *
spcdel (char *buf)
{
  char *p;

  while (*buf == ' ' || *p == '\t')
     buf++;

  p = buf + strlen (buf) - 1;
  while (*p == ' ' || *p == '\t' || *p == '\n')
    p--;
  *(++p) = '\0';

  return buf;
}

static char *
sletter (char *str)
{
  char *p = str;

  while (*p != '\0')
    *p++ = tolower (*p);

  return str;
}

void
print_parsed (parsed_t *parsed, addinfo_t *addinfo)
{
  int i;
  parsed_t *p = parsed;

  while (p)
    {
      for (i = 0; i < INFO_MAXCOL (addinfo); i++)
	if (p->pf[i])
	  printf (" [%s] ", p->pf[i]);
      putchar ('\n');
      p = p->next;
    }
}

static int 
check_parsed (parsed_t *parsed, addinfo_t *addinfo)
{
  int i, cmp;
  parsed_t *pi = parsed, *pd, *prev;

  while (pi)
    {
      for (i = 0; i < INFO_MAXCOL (addinfo); i++)
	{
	  if ((INFO_FLAGS (addinfo, i) & TA_SEARCHABLE) &&
	      pi->pf[i] == NULL)
	    return 0;

	  /* maybe Sun's specification */
	  /* Why is passwd entry not TA_CASE? */
	  if (pi->pf[i] && 
	      INFO_FLAGS (addinfo, i) & ~TA_CASE & ~TA_BINARY && 
	      strncmp ("passwd", INFO_COLNAME (addinfo, i), 6) != 0)
	    pi->pf[i] = sletter (pi->pf[i]);

	  /* 
	   * if (pi->pf[i] != NULL)
	   *   pi->pf[i] = spcdel (pi->pf[i]); 
	   */
	}

      /* check duplication, but is this needed? */
      for (pd = pi -> next, prev = pi; pd != NULL; prev = pd, pd = pd->next)
	{
	  for (i = 0, cmp = 0; i < INFO_MAXCOL (addinfo); i++)
	    if (pd->pf[i] != NULL && pi->pf[i] != NULL)
	      cmp += strcmp (pd->pf[i], pi->pf[i]) != 0;
	  
	  if (cmp == 0) /* && prev != NULL) */
	    {
	      prev->next = pd->next;
	      free (pd->pf);
	      free (pd);
	      pd = prev;
	    }
	}
      pi = pi->next;
    }

  return 1;
}

int
parsed_free (parsed_t *ptr)
{
  parsed_t *cur, *next;

  cur = ptr;
  while (cur)
    {
      next = cur->next;
      free (cur->pf);
      free (cur);
      cur = next;
    }
  return 1;
}

parsed_t *
parsed_malloc (int columns)
{
  parsed_t *pres;

  pres = (parsed_t *) malloc (sizeof (parsed_t));
  if (pres == NULL)
    return NULL;
  pres->pf = (char **) malloc (sizeof (char *) * columns);
  if (pres->pf == NULL)
    {
      free (pres);
      return NULL;
    }
  memset (pres->pf, 0, sizeof (char *) * columns);

  return pres;
}

parsed_t *
parse_serv (char *buf, struct addinfo_t *addinfo)
{
  static parsed_t *serv, *prev = NULL, *cur = NULL;
  char *ncname;
  char *after_proto;
  
  serv = parsed_malloc (INFO_MAXCOL (addinfo));
  if (serv == NULL)
    {
      fprintf (stderr, _("Could not alloc memory.\n"));
      return NULL;
    }

  /* comment */
  serv->pf[addinfo->order[4]] = strchr (buf, '#'); 
  if (serv->pf[addinfo->order[4]])
    {
      *serv->pf[addinfo->order[4]]++ = '\0';
      /* serv->pf[addinfo->order[4]] = spcdel (serv->pf[addinfo->order[4]]); */
    }
  
  after_proto = strchr (buf, '/');
  if (after_proto == NULL)
    {
      parsed_free (serv);
      return NULL;
    }
  *after_proto++ = '\0';
  
  serv->pf[addinfo->order[0]] = strtok (buf, " \t");         /* name */ 
  serv->pf[addinfo->order[1]] = strtok (NULL, " \t");        /* port */
  serv->pf[addinfo->order[2]] = strtok (after_proto, " \t"); /* protocol */

  serv->pf[addinfo->order[3]] = serv->pf[addinfo->order[0]];
  /* for multiple cname */
  cur = serv;
  while ((ncname = strtok (NULL, " \t")) != NULL)
    {
      prev = cur;
      cur = parsed_malloc (INFO_MAXCOL (addinfo));
      if (cur == NULL)
	{
	  fprintf (stderr, _("Could not alloc memory.\n"));
	  parsed_free (serv);
	  return NULL;
	}
      memcpy (cur->pf, prev->pf, sizeof (char *) * INFO_MAXCOL (addinfo));
      cur->pf[addinfo->order[0]] = ncname;
      prev->next = cur;
    }
  cur->next = NULL;

  if (check_parsed (serv, addinfo))
    return serv;
  else
    return NULL;
}

parsed_t *
parse_colon (char *buf, struct addinfo_t *addinfo)
{
  char *s;
  parsed_t *grp;
  int i = 0;

  grp = parsed_malloc (INFO_MAXCOL (addinfo));
  if (grp == NULL)
    {
      fprintf (stderr, _("Could not alloc memory.\n"));
      return NULL;
    }

  if (*buf == '+') /* YP entry */
    return NULL;

  s = buf;
  for (i = 0; i < INFO_MAXCOL (addinfo); i++)
    {
      grp->pf[addinfo->order[i]] = s;
      s = strchr (grp->pf[addinfo->order[i]], ':');
      if (s == NULL)
	{
	  grp->pf[addinfo->order[i]][strlen (grp->pf[addinfo->order[i]]) + 1] 
	    = '\0';
	  break;
	}
      if (s == grp->pf[addinfo->order[i]])
	grp->pf[addinfo->order[i]] = NULL;
      else
	*s = '\0';
      s++;
    }

  grp->next = NULL;

  if (check_parsed (grp, addinfo))
    return grp;
  else
    return NULL;
}

parsed_t *
parse_generic (char *str, struct addinfo_t *addinfo)
{
  parsed_t *res;
  int i;
  char *comment;

  res = parsed_malloc (INFO_MAXCOL (addinfo));
  if (res == NULL)
    {
      fprintf (stderr, _("Could not alloc memory.\n"));
      return NULL;
    }

  /* comment is almost the last */
  comment = strchr (str, '#');
  if (comment)
    {
      *comment++ = '\0';
      /* comment = spcdel (comment); */
      res->pf[INFO_MAXCOL(addinfo) - 1] = comment;
    }

  res->pf[addinfo->order[0]] = strtok (str, " \t");
  for (i = 1; i < INFO_MAXCOL (addinfo) - 1; i++)
    res->pf[addinfo->order[i]] = strtok (NULL, " \t");

  res->next = NULL;

  if (check_parsed (res, addinfo))
    return res;
  else 
    return NULL;
}

parsed_t *
parse_mltcn (char *buf, struct addinfo_t *addinfo)
{
  static parsed_t *pres, *prev = NULL, *cur = NULL;
  char *ncname, *comment;
  int i, name_pos;

  pres = parsed_malloc (INFO_MAXCOL (addinfo));
  if (pres == NULL)
    {
      fprintf (stderr, _("Could not alloc memory.\n"));
      return NULL;
    }
  
  /* 
   * assume comment is last, previous is cname.
   * If not, to set those correct, use ent_pos (char *, addinfo_t *)
   */
  name_pos = ent_pos ("name", addinfo);

  /* comment */
  comment = strchr (buf, '#');
  if (comment)
    {
      *comment++ = '\0';
      pres->pf[INFO_MAXCOL (addinfo) - 1] = comment;
    }
  
  pres->pf[addinfo->order[0]] = strtok (buf, " \t");
  for (i = 1; i < INFO_MAXCOL (addinfo) - 2; i++)
    pres->pf[addinfo->order[i]] = strtok (NULL, " \t");

  pres->pf[addinfo->order[i]] = pres->pf[addinfo->order[name_pos]];
  /* for multiple cname */
  cur = pres;
  while ((ncname = strtok (NULL, " \t")) != NULL)
    {
      prev = cur;
      cur = parsed_malloc (INFO_MAXCOL (addinfo));
      if (cur == NULL)
	{
	  fprintf (stderr, _("Could not alloc memory.\n"));
	  parsed_free (pres);
	  return NULL;
	}
      memcpy (cur->pf, prev->pf, sizeof (char *) * INFO_MAXCOL (addinfo));
      cur->pf[addinfo->order[name_pos]] = ncname;

      prev->next = cur;
    }
  cur->next = NULL;

  if (check_parsed (pres, addinfo))
    {
      return pres;
    }
  else
    return NULL;
}

parsed_t *
parse_shadow (char *buf, struct addinfo_t *addinfo)
{
  parsed_t *shdw;
  int i;
  char *s;

  shdw = parsed_malloc (INFO_MAXCOL (addinfo));
  if (shdw == NULL)
    {
      fprintf (stderr, _("Could not alloc memory.\n"));
      return NULL;
    }

  s = buf;
  for (i = 0; i < 3; i++)
    {
      shdw->pf[addinfo->order[i]] = s;
      s = strchr (shdw->pf[addinfo->order[i]], ':');
      if (s == NULL)
	{
	  shdw->pf[addinfo->order[i]][strlen (shdw->pf[addinfo->order[i]]) + 1] 
	    = '\0';
	  break;
	}
      if (s == shdw->pf[addinfo->order[i]])
	shdw->pf[addinfo->order[i]] = NULL;
      else
	*s = '\0';
      s++;
    }

  shdw->next = NULL;

  if (check_parsed (shdw, addinfo))
    return shdw;
  else
    return NULL;
}
