/* Tools for editing a PR.
   Copyright (C) 1994, 1995 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, 59 Temple Place - Suite 330, Boston, MA 02111, USA.  */

#include "config.h"
#include "gnats.h"
#include "gnatsd.h"

char *get_path			PARAMS((char *));

extern int force;

int
modify_pr (fp)
    FILE *fp;
{
  FILE *prfile;
  struct stat buf;
  time_t t;
  char *date;

  /* Where to keep the static index if necessary.  */
  Index *current_index = (Index *)NULL;

  char *path = (char *)NULL, *old_path = (char *)NULL,
    *lock_path = (char *)NULL;
  char *p;

  Index *new_index = (Index *) xmalloc (sizeof (Index));
  char *s;
  Index *i, *prev_index = NULL, *old_index = NULL;

  /* this will print the bad values out. then return and exit.  */
  if (!check_pr (fp))
    return 0;

  /* build the new index entry.  */
  new_index->buf = (char *)NULL;
  new_index->category = field_value (CATEGORY);
  new_index->number = field_value (NUMBER);
  new_index->submitter = field_value (SUBMITTER);
  new_index->responsible = field_value (RESPONSIBLE);
  if (*new_index->responsible)
    {
      p = (char *) strchr (new_index->responsible, ' ');
      if (p != NULL)
	{
	  int x = (int) (p - new_index->responsible);

	  /* Copy it instead of chopping off the full name in
	     the responsible field's value itself, so the PR
	     gets written out with what was read in.  */
	  p = (char *) strdup (new_index->responsible);
	  new_index->responsible = p;
	  p += x;
	  *p = '\0';
	}
    }
  new_index->state = field_value (STATE);
  new_index->confidential = field_value (CONFIDENTIAL);
  new_index->severity = field_value (SEVERITY);
  new_index->priority = field_value (PRIORITY);
#ifdef GNATS_RELEASE_BASED
  date = field_value (DATE_REQUIRED);
  if (date[0] == '\0')
    new_index->date_required = "0";
  else
    new_index->date_required = date;
#endif
  s = field_value (NUMBER);

  if (! force)
    {
      current_index = get_index ();
 
      /* Let's see if we can find what we are looking for.  */
      for (i = current_index; i ; i = i->next)
	{
	  if (strcmp (i->number, s) == 0)
	    {
	      /* replace the current index with the new. */ 
	      old_index = i;
	      if (prev_index)
		prev_index->next = new_index;
	      else
		prev_index = current_index = new_index;
	      new_index->next = i->next;
	      break; 
	    }
	  prev_index = i;
	}

      if (old_index == NULL)
	{
	  free_index (current_index);
	  if (is_daemon)
	    {
	      printf ("%d Cannot find index entry for %s\r\n",
		      CODE_NO_INDEX, s);
	      return 0;
	    }
	  else
	    punt (1, "%s: can't find index entry for %s\n", program_name, s);
	}

      lock_path = (char *) xmalloc (PATH_MAX);
      sprintf (lock_path, "%s/%s/%s.lock", gnats_root, old_index->category,
	       old_index->number);

      if (stat (lock_path, &buf) < 0)
	{
	  free_index (current_index);
	  free_index_entry (old_index);
	  if (is_daemon)
	      {
		printf ("%d Cannot stat lock file %s: %s\r\n",
			CODE_FILE_ERROR, lock_path, strerror (errno));
		xfree (lock_path);
		return 0;
	      }
	    else
	      /* We don't free lock_path when we go in here...  */
	      punt (1, "%s: can't stat lock file %s: %s",
		    program_name, lock_path, strerror (errno));
	}
      old_path = (char *) xmalloc (PATH_MAX);
      sprintf (old_path, "%s/%s/%s",  gnats_root, old_index->category, 
	       old_index->number);

      /* set this to be the file to be saved. now called .old. */
      sprintf (lock_path, "%s/%s/%s.old", gnats_root, old_index->category,
	       old_index->number);
    }
  else
    add_to_index ();

  path = (char *) xmalloc (PATH_MAX);

  /* check to see if the category changes, and if so, write the 
     new file out, and unlink the old one.  */
  if (! force)
    {
      if (strcmp (old_index->category, new_index->category) != 0)
	sprintf (path, "%s/%s/%s",  gnats_root, new_index->category, 
		 new_index->number);
      else
	sprintf (path, "%s/%s/%s", gnats_root, old_index->category,
		 old_index->number);
    }
  else
    sprintf (path, "%s/%s/%s", gnats_root, new_index->category,
	     new_index->number);

  if (! force && rename (old_path, lock_path) < 0)
    {
      if (errno != EXDEV)
	{
	  free_index (current_index);
	  free_index_entry (old_index);
	  xfree (lock_path);
	  xfree (path);
	  if (is_daemon)
	    {
	      printf ("%d Could not rename %s: %s\r\n",
		      CODE_FILE_ERROR, old_path, strerror (errno));
	      xfree (old_path);
	      return 0;
	    }
	  else
	    punt (1, "%s: could not rename %s: %s\n", program_name,
		  old_path, strerror (errno));
	}
      if (copy_file (old_path, lock_path))
	{
	  if (is_daemon)
	    {
	      free_index (current_index);
	      free_index_entry (old_index);
	      xfree (path);
	      printf ("%d Could not copy %s to %s: %s\r\n",
		      CODE_FILE_ERROR, old_path, lock_path, strerror (errno));
	      xfree (lock_path);
	      xfree (old_path);
	      return 0;
	    }
	  else
	    punt (1, "%s: could not copy %s to %s: %s\n", program_name,
		  old_path, lock_path, strerror (errno));
	}

      /* Don't complain if this fails, since trying to write to it will give
	 us the diagnostic if it's really serious.  */
      unlink (old_path);
    }

  /* Now build the file.  */
  prfile = fopen (path, "w+");
  if (prfile == (FILE *) NULL)
    {
      free_index (current_index);
      free_index_entry (old_index);
      xfree (lock_path);
      xfree (old_path);
      if (is_daemon)
	{
	  printf ("%d Cannot write the PR to %s: %s\r\n",
		  CODE_FILE_ERROR, path, strerror (errno));
	  xfree (path);
	  return 0;
	}
      else
	punt (1,  "%s: can't write the PR to %s: %s", program_name, path,
	      strerror (errno));
    }

  t = time (0);
  date = (char *) xmalloc (33);
  strftime (date, 33, "%a %b %e %T %Z %Y", localtime (&t));
  xfree (pr[LAST_MODIFIED].value);
  pr[LAST_MODIFIED].value = date;

  write_header (prfile, NUM_HEADER_ITEMS);
  fprintf (prfile, "\n");
  write_pr (prfile, NUM_PR_ITEMS);

  if (fclose (prfile) == EOF)
    {
      free_index (current_index);
      free_index_entry (old_index);
      xfree (lock_path);
      xfree (old_path);
      if (is_daemon)
	{
	  printf ("%d Error writing out PR %s: %s\r\n",
		  CODE_FILE_ERROR, path, strerror (errno));
	  xfree (path);
	  return 0;
	}
      else
	punt (1, "%s: error writing out PR %s: %s", program_name, path,
	      strerror (errno));
    }

  if (! force)
    unlink (lock_path);
  
  /* unlink the old file, if it is in another category dir. */
  if (! force)
    {
      if (strcmp (old_index->category, new_index->category) != 0)
	unlink (old_path);

      /* write out the new index.  */
      write_index (current_index);
    }

  free_index (current_index);
  free_index_entry (old_index);
  xfree (lock_path);
  xfree (path);
  xfree (old_path);

  return 1;
}

/* check_pr - reads the PR in, searches for bogus enum types, lack of
   a PR number, and prints out the bogus info.  */

int
check_pr (fp)
     FILE* fp;
{
  struct bad_enum *enums, *e;
#ifdef GNATS_RELEASE_BASED
  char *d;
  time_t t;
#endif
  char *p;
  Category category;
  Submitter submitter;
  Responsible *responsible;

  /* Read the message header in.  */
  if (read_header (fp) < 0)
    {
      if (! is_daemon)
	fprintf (stderr, "%s: end-of-file reading PR\n", program_name);
      return 0;
    }

  read_pr (fp, 0);

  if (find_category (&category, pr[CATEGORY].value) == -1)
    {
      if (is_daemon)
	printf ("%d No such category as %s.\r\n",
		CODE_INVALID_CATEGORY, pr[CATEGORY].value);
      else
	fprintf (stderr, "%s: no such category as `%s'\n",
		 program_name, pr[CATEGORY].value);
      return 0;
    }
  free_category (&category);

  if (find_submitter (&submitter, pr[SUBMITTER].value) == -1)
    {
      if (is_daemon)
	printf ("%d No such submitter as %s.\r\n",
		CODE_INVALID_SUBMITTER, pr[SUBMITTER].value);
      else
	fprintf (stderr, "%s: no such submitter as %s\n",
		 program_name, pr[SUBMITTER].value);
      return 0;
    }
  free_submitter (&submitter);

  p = (char *) strchr (pr[RESPONSIBLE].value, ' ');
  if (p != NULL)
    *p = '\0';
  responsible = get_responsible_address (pr[RESPONSIBLE].value);
  if (responsible == (Responsible *)NULL)
    {
      if (is_daemon)
	printf ("%d No such responsible as %s.\r\n",
		CODE_INVALID_RESPONSIBLE, pr[RESPONSIBLE].value);
      else
	fprintf (stderr, "%s: no such responsible as %s\n",
		 program_name, p);
      return 0;
    }

  free_responsible (responsible);
  xfree ((char *) responsible);

#ifdef GNATS_RELEASE_BASED
  d = field_value (DATE_REQUIRED);
  if (d)
    {
      t = get_date (d, NULL);
      if (! t)
	{
	  fprintf (stderr,
		   "%s: Couldn't parse the date: %s.\r\n", program_name, d);
	}
    }
#endif

  enums = check_enum_types (2);
  p = field_value (NUMBER);

  if (enums || *p == '-')
    {
      if (! is_daemon)
	{
	  printf ("pr-edit: invalid fields");
	  if (enums)
	    for (e = enums; e; e = e->next)
	      printf ("%s", e->msg);
	  /* It was missing its number! */
	  if (*p == '-')
	    printf ("Number: %s\n", p);
	}
      return 0;
    }

  return 1;
}

int
lock_pr (fname, user, processid)
     char *fname, *user, *processid;
{
  char *lock_path;
  struct stat sbuf;
  FILE *ifp;

  if (fname == NULL)
    return 0;

  if (! is_daemon)
    lock_path = get_path (fname);
  else
    {
      lock_path = (char *) xmalloc (strlen (fname) + 6);
      strcpy (lock_path, fname);
      strcat (lock_path, ".lock");
    }

  if (stat (lock_path, &sbuf) == 0)
    {
      FILE *fp = fopen (lock_path, "r");
      char buf[1024], *s;

      /* If we couldn't open it for reading, something else is hosed,
	 so just bail now.  */
      if (fp == (FILE *) NULL)
	{
	  xfree (lock_path);
	  return 0;
	}

      fgets (buf, 1023, fp);
      s = strchr (buf, '\n');
      if (s)
	s[0] = '\0';
      if (is_daemon)
	printf ("%d PR %s locked by %s\r\n", CODE_LOCKED_PR, fname, buf);
      else
	fprintf (stderr, "%s: PR %s locked by %s\n", program_name,
		 fname, buf);
      fclose (fp);
      xfree (lock_path);
      return 0;
    }

  ifp = fopen (lock_path, "w");
  if (ifp == NULL)
    {
      if (is_daemon)
	{
	  printf ("%d Cannot create lock file %s\r\n",
		  CODE_FILE_ERROR, lock_path);
	  xfree (lock_path);
	  return 0;
	}
      else
	punt (1, "%s: can't create lock file %s", program_name, lock_path);
    }

  if (processid)
    fprintf (ifp, "%s %s\n", user, processid);
  else
    fprintf (ifp, "%s\n", user);
  fclose (ifp);

  xfree (lock_path);
  return 1;
}

int
unlock_pr (fname)
    char *fname;
{
  char *lock_path;
  struct stat buf;

  if (fname == NULL)
    return 0;

  if (! is_daemon)
    lock_path = get_path (fname);
  else
    {
      lock_path = (char *) xmalloc (strlen (fname) + 6);
      strcpy (lock_path, fname);
      strcat (lock_path, ".lock");
    }

  if (lock_path == NULL)
    {
      if (is_daemon)
	printf ("%d No such PR %s\r\n", CODE_INVALID_PR, fname);
      else
	fprintf (stderr, "%s: no such PR `%s'\n", program_name, fname);
      xfree (lock_path);
      return 0;
    }

  if (stat (lock_path, &buf) != 0)
    {
      if (is_daemon)
	printf ("%d %s is not locked.\r\n", CODE_PR_NOT_LOCKED, lock_path);
      else
	fprintf (stderr, "%s: lock file %s does not exist\n",
		 program_name, lock_path);
      xfree (lock_path);
      return 0;
    }

  if (unlink (lock_path) == -1)
    {
      if (is_daemon)
	printf ("%d Cannot delete lock file %s\r\n",
		CODE_FILE_ERROR, lock_path);
      else
	fprintf (stderr, "%s: can't delete lock file %s\n",
		 program_name, lock_path);
      xfree (lock_path);
      return 0;
    }

  xfree (lock_path);
  return 1;
}

/* returns actually the path of the lock file.  */
char*
get_path (fname)
    char *fname;
{
  char *p;
  FILE *ifp;
  char *category = NULL; 
  char *path;

  if (fname == NULL)
    return NULL;

  p = (char *) strchr (fname, '/');
  if (p == NULL)
    {
      ifp = open_index ();
      if (ifp == NULL)
	return NULL;

      category = find_pr_category (ifp, fname);
      close_index (ifp);

      if (category == NULL)
        return NULL;
    }

  path = (char *) xmalloc (PATH_MAX);
  if (category)
    sprintf (path, "%s/%s/%s.lock", gnats_root, category, fname);
  else
    sprintf (path, "%s/%s.lock", gnats_root, fname);

  return path;
}
