/* Generate an index file for an existing GNATS database.
   Copyright (C) 1993 Free Software Foundation, Inc.
   Contributed by Brendan Kehoe (brendan@cygnus.com).

This file is part of GNU GNATS.

GNU GNATS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU GNATS 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 General Public License for more details.

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

#include "config.h"

#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "gnats.h"
#include "globals.h"
#include "gnats-dirs.h"
#include "pathmax.h"

/* The name this program was run with.  */
char *program_name;

/* Where the results of query-pr should go.  */
FILE *outfile = stdout;

/* If TRUE, we should sort the index list by number.  */
int sort_numerical = FALSE;

/* Defined in version.c.  */
extern char *version_string;

struct option long_options[] =
{
  {"directory", 1, NULL, 'd'},
  {"outfile", 1, NULL, 'o'},
  {"numerical", 0, NULL, 'n'},
  {"help", 0, NULL, 'h'},
  {"version", 0, NULL, 'V'},
  {NULL, 0, NULL, 0}
};

typedef struct category_list
{
  Category *c;
  struct category_list *next;
} Categories;

typedef struct entry
{
  unsigned int number;
  char *string;
} Entry;

#define MAX_ENTRIES 1024
Entry *entries;
int max_entries = MAX_ENTRIES, num_entries = 0;

void usage (), version ();

unsigned int
entry_cmp (e1, e2)
     Entry *e1, *e2;
{
  return e1->number - e2->number;
}

/* For a given category C, go into its directory and either emit an
   index entry for each file in it, or swallow its index entry so we
   can sort it later.  */
void
do_category (c)
     char *c;
{
  register DIR *d;
  register struct dirent *next;
  FILE *fp;
  int len = strlen (gnats_root) + 1 + strlen (c) + 2;
  /* Allocate for a PR with 8 digits.  */
  char *path = (char *) xmalloc (len + 9);
  char *p;
  char line[STR_MAX];

  errno = 0;
  if (chdir (gnats_root) < 0)
    {
      fprintf (stderr, "%s: can't read the database directory %s: %s\n",
	       program_name, gnats_root, strerror (errno));
      exit (3);
    }

  if (c == NULL || *c == '\0')
    return;

  errno = 0;
  d = opendir (c);
  if (! d)
    {
      fprintf (stderr, "can't open the category directory %s/%s: %s\n",
	       gnats_root, c, strerror (errno));
      return;
    }

  sprintf (path, "%s/%s/", gnats_root, c);

  /* Process each file in the directory; ignore files that have periods
     in their names; either they're the . and .. dirs, or they're a
     lock file (1234.lock).  */
  while ((next = readdir (d)))
    if (strchr (next->d_name, '.') == NULL)
      {
	p = path + len - 1;
	strcat (p, next->d_name);

	fp = fopen (path, "r");
	if (fp == (FILE *) NULL)
	  {
	    fprintf (stderr, "%s: couldn't read pr %s: %s\n",
		     program_name, path, strerror (errno));

	    *p = '\0';
	    continue;
	  }

	*p = '\0';
	read_header (fp);
	read_pr (fp, 1);
	fclose (fp);

	create_index_entry (line);

	if (sort_numerical == TRUE)
	  {
	    if (num_entries == max_entries - 1)
	      {
		max_entries += MAX_ENTRIES;
		entries = (Entry *) xrealloc ((char *) entries,
					       max_entries * sizeof (Entry));
	      }

	    entries[num_entries].number = atoi (field_value (NUMBER));
	    entries[num_entries].string = (char *) strdup (line);
	    num_entries++;
	  }
	else
	  fprintf (outfile, "%s", line);
      }

  free (path);
}

/* Get the next entry in the categories file.  */
Categories *
next_category (fp)
     FILE *fp;
{
  char *start, *end, *b;
  char *buf = (char *) xmalloc (STR_MAX);
  Categories *p = (Categories *) xmalloc (sizeof (Categories));
  Category *c;

  c = p->c = (Category *) xmalloc (sizeof (Category));
  p->next = NULL;

  b = buf;
  do
    if (fgets (buf, STR_MAX, fp) == NULL)
      goto no_entry;
  while (buf[0] == '#' || buf[0] == '\n' || buf[0] == ' ');

  start = b;
  end = strchr (start, ':');
  if (end == NULL)
    goto no_entry;
  *end = '\0';
  c->key = start;

  start = end + 1;
  end = strchr (start, ':');
  if (end == NULL)
    goto no_entry;
  *end = '\0';
  c->fullname = start;

  start = end + 1;
  end = strchr (start, ':');
  if (end == NULL)
    goto no_entry;
  *end = '\0';
  c->person = start;

  start = end + 1;
  end = strchr (start, '\n');
  if (end == NULL)
    goto no_entry;
  *end = '\0';
  c->notify = start;

  return p;

no_entry:
  free (p->c);
  free (p);
  free (buf);
  return (Categories *) NULL;
}

/* Return a linked list of categories.  */
Categories *
get_categories ()
{
  char *path = (char *) xmalloc (PATH_MAX);
  FILE *fp;
  Categories *cat_list = (Categories *) NULL;
  Categories *cat_list_end = (Categories *) NULL;
  Categories *c;

  sprintf (path, "%s/gnats-adm/%s", gnats_root, CATEGORIES);

  errno = 0;
  fp = fopen (path, "r");
  if (fp == (FILE *) NULL)
    {
      fprintf (stderr, "%s: couldn't read the categories file %s: %s\n",
	       program_name, path, strerror (errno));
      exit (3);
    }

  while ((c = next_category (fp)))
    {
      if (cat_list == NULL)
	cat_list = cat_list_end = c;
      else
	{
	  cat_list_end->next = c;
	  cat_list_end = c;
	}

      c->next = NULL;
    }

  fclose (fp);

  return cat_list;
}
  
void
main (argc, argv)
     int argc;
     char **argv;
{
  int optc, i;
  Categories *clist, *c;

  program_name = (char *) basename (argv[0]);

  while ((optc = getopt_long (argc, argv, "o:hd:nV",
			      long_options, (int *) 0)) != EOF)
    {
      switch (optc)
	{
	case 'd':
	  gnats_root = optarg;
	  break;

	case 'o':
	  outfile = fopen (optarg, "w+");
	  if (outfile == (FILE *) NULL)
	    {
	      fprintf (stderr, "%s: can't write to %s: %s\n", program_name,
		       optarg, strerror (errno));
	      exit (3);
	    }
	  break;

	case 'n':
	  sort_numerical = TRUE;
	  break;

	case 'V':
	  version ();
	  exit (0);

	case 'h':
	  /* Fall through.  */

	default:
	  usage ();
	}
    }

  configure ();
  init_gnats ();
  umask (022);

  if (sort_numerical)
    entries = (Entry *) xmalloc (max_entries * sizeof (Entry));

  clist = get_categories ();

  for (c = clist ; c ; c = c->next)
    do_category (c->c->key);

  if (sort_numerical)
    {
      qsort (entries, num_entries, sizeof (Entry), entry_cmp);
      for (i = 0; i < num_entries; i++)
	fprintf (outfile, "%s", entries[i].string);
    }

  exit (0);
}

void
usage ()
{
  fprintf (stderr, "\
Usage: %s [-hnV] [-d directory] [-o outfile] [--directory=directory]\n\
          [--outfile=filename] [--numerical] [--version] [--help]",
	   program_name);
  exit (1);
}

void
version ()
{
  printf ("gen-index %s\n", version_string);
}
