/*
    ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
    Copyright (C) 1992  Thomas McWilliams
    Copyright (C) 1990  Rene Cougnenc

    This program 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 1, or (at your option)
    any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
chosetag.c
*/

#define TLMX 60			/* maximum length for tagline */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef unix
#include <unistd.h>
#ifdef __EMX__
#include <io.h>
#endif
#else
#include <io.h>
#ifdef WIN32
#include <direct.h>
#else
#include <dir.h>		/* turbo c */
#endif
#endif
#include "system.h"
#include "reader.h"
#include "qlib.h"

#define FN_TMP_TEMPLATE "chXXXXXX"

#ifdef BSD
#define rand() random()
#endif

extern int fido;
extern char CurTag[];
extern const char CONSPTR FidoTag;
extern const char CONSPTR TagLine;

int ChooseTag (void);
void TagSeek (const unsigned int tagchoice);
static void Bye (void);
static char *FnTmp;
static FILE *FpTmp;
static void InitRandom (void);
static int DupCount (FILE * from, FILE * to);

int
ChooseTag (void)
{
  char ttbuf[30];
  unsigned int NToChoose = 1;
  unsigned int NSample;
  unsigned int toffset;
  static int flag1st = TRUE;
  FILE *Fp = NULL;
  char ttagpath[MAXPATHS];

  strcpy (ttbuf, FN_TMP_TEMPLATE);
  FnTmp = mktemp (ttbuf);
  if ((FpTmp = fopen (FnTmp, "w+b")) == NULL)
    {				/* open tagline file */
      fprintf (stderr, "Can't open file %s mode %s\n", FnTmp, "write");
      return (1);
    }

  sprintf (ttagpath, "%s%s", HomePath, TAGFILE);

  if ((Fp = fopen (ttagpath, "rb")) == NULL)
    {				/* open tagline file */
      Bye ();
      fprintf (stderr, "Can't open file %s mode %s\n", ttagpath, "read only");
      return (1);
    }

  NSample = 0;
  NSample += DupCount (Fp, FpTmp);
  Bye ();
  fclose (Fp);

  if (NToChoose > NSample)
    {
      fprintf (stderr, "%s: Can't choose %d from %d.\n",
	       ttagpath, NToChoose, NSample);
      return (1);
    }

  if (flag1st)
    {
      InitRandom ();
      flag1st = FALSE;
    }

  toffset = (unsigned int) ((rand ()) % NSample);
  TagSeek (toffset);
  return (0);
}

static void
Bye (void)			/* get rid of temp file and exit */
{
  fclose (FpTmp);
  unlink (FnTmp);
}


/* duplicate file and count lines */
static int
DupCount (FILE * FpFrom, FILE * FpTo)
{
  signed char Ch;
  int NLn = 0;

  while ((Ch = getc (FpFrom)) != EOF)
    {
      if (Ch == '\n')
	NLn++;
      if (Ch == '\r')
	continue;
      putc (Ch, FpTo);
    }

  return NLn;
}

static void
InitRandom ()			/* initialize random sequence  */
{
#ifdef BSD
  struct timeval tv;
  struct timezone tz;
  gettimeofday (&tv, &tz);
  srandom ((int) tv.tv_sec);
#else
    srand ((unsigned int) time (NULL));
/*	srand(getpid()); <- use this as a last resort */
#endif
  return;
}

void
TagSeek (const unsigned int tagchoice)
{
  unsigned int charct = 0, NLn = 0, TagOff = 0;
  char ttagpath[MAXPATHS], *crtag, Ch = '\040';
  char tmptagbuf[TLMX + 50];	/* temporary work buffer */
  FILE *Fp = NULL;

  crtag = tmptagbuf;
  sprintf (ttagpath, "%s%s", HomePath, TAGFILE);

  if ((Fp = fopen (ttagpath, "rb")) == NULL)
    {				/* open tagline file */
      fprintf (stderr, "Can't open file %s mode %s\n", ttagpath, "read only");
      return;
    }
  Ch = (char) 0;
  while (NLn != tagchoice && Ch != EOF)
    {				/* seek to chosen tagline */
      Ch = getc (Fp);
      TagOff++;
      if (Ch == '\n')
	NLn++;
    }
  if (Ch == EOF)
    {
      printf ("unexpected end of file\n");
      fclose (Fp);
      return;
    }
  Ch = (char) 0;
  while (Ch != EOF && Ch != '\n' && charct < TLMX)
    {				/* build new tag line */
      Ch = getc (Fp);
      if (Ch == '\r')
	continue;
      *crtag = Ch;
      crtag++;
      charct++;
    }
  if (Ch == EOF)
    {
      printf ("unexpected end of file\n");
      fclose (Fp);
      return;
    }
  crtag--;
  if (*crtag != '\n')
    *crtag = '\n';
  crtag++;
  *crtag = '\0';
  if (fido)
    strcpy (CurTag, FidoTag);	/* setup the default TagLines */
  else
    strcpy (CurTag, TagLine);
  strcat (CurTag, tmptagbuf);
  fclose (Fp);
  return;
}

/*  
 *  clip: a function to truncate ATP databases to n messages 
 *  returns OK on success, and ERROR on failure. Called by
 *  function Purge() 
 */
int
clip (char *bbspath, char Tflg, long trunc_num)
{
/* clipmain (char *bbspath, int confnum, int trunc_num) */

  long file_len;
  long num_messages = 0L ;
  long msg_start = 0L ;
  long idx_offset = 0L ;
  long last_read = 0L ;
  long old_num = 0L ;
  long old_size = 0L;
  long old_offset = 0L ;
  size_t nrecs, msg_size;
  unsigned char kill_it = 0;
  char *tmptr, *tmptr2;
  char tmpbuf[MAXPATHS];
  char tmpbuf2[MAXPATHS];
  int verbose = TRUE;
  int purge_mode = FALSE;
  int check_mode = FALSE;
  int nrml_exit = TRUE;
  int wrote_one = FALSE;

  struct MyIndex msg_idx;
  FILE *idxptr, *ocnfptr;
  FILE *ndxptr, *ncnfptr;

  if (trunc_num < 1L )
    return (OK);			/*  nothing to do */

  purge_mode = (Tflg == 'C' ? TRUE : FALSE);

/* GET LAST-READ MESSAGE and TRUNCATE that way ... */

  {
/* 	parse index file names, and open files */
    wrote_one = FALSE;
    tmptr = tmpbuf;
    strcpy (tmpbuf, bbspath);

    if (strlen (tmpbuf) < (size_t) 5 )
      {
	printf ("Invalid file name %s\n", tmpbuf);
	return (ERROR);
      }
    while (*tmptr++);		/* seek to end of buffer */
    tmptr -= 4;
    strcpy (tmptr, "idx");
    if (access (tmpbuf, R_OK))
      {
	printf ("Can't access %s\n", tmpbuf);
	return (ERROR);
      }
    if ((idxptr = fopen (tmpbuf, "rb")) == NULL)	/* old index */
      {
		return (ERROR);
      }
	else
	  {
/*
use idx file to determine how many messages are in database.
if number of messages is greater then trunc_num then contiue,
otherwise exit with a message
*/
    	fseek (idxptr, 0L, SEEK_END);
    	file_len = ftell (idxptr);
    	if ( file_len < sizeof(struct MyIndex) )
      	{
			fprintf (stderr, "ERROR: Index file empty\n");
			fclose( idxptr);
			return (ERROR);
	  	}
        num_messages = file_len / sizeof (struct MyIndex);
	  }

/* now we continue opening files ... */	  
    strcpy (tmptr, "tmi");
    if ((ndxptr = fopen (tmpbuf, "wb")) == NULL)	/* new index */
      {
	fclose (idxptr);
	return (ERROR);
      }
    strcpy (tmptr, "tmc");
    if ((ncnfptr = fopen (tmpbuf, "wb")) == NULL)	/* new conf */
      {
	fclose (idxptr); fclose (ndxptr);
	strcpy (tmptr, "tmi"); unlink (tmpbuf);
	return (ERROR);
      }
    strcpy (tmptr, "cnf");
    if ((ocnfptr = fopen (tmpbuf, "rb")) == NULL)	/* old conf */
      {
	fclose (idxptr); fclose (ndxptr); fclose (ncnfptr);
	strcpy (tmptr, "tmi"); unlink (tmpbuf);
	strcpy (tmptr, "tmc"); unlink (tmpbuf);
	return (ERROR);
      }

/*
We now know the number of messages in the file. Next we must
calculate the current message number of the message that will
start the new message base: msg_start = num_messages - trunc_num
*/
    if (!check_mode && !purge_mode)
      {
	if (trunc_num >= num_messages)
	  {
	    check_mode = TRUE;	/* don't unlink source */
	    nrml_exit = FALSE;
	    goto fini;
	  }
	msg_start = num_messages - trunc_num;
	idx_offset = msg_start * sizeof (struct MyIndex);
      }
    else
      idx_offset = msg_start = 0;

/* calculate new last_read pointer */
    fseek (idxptr, 0L, SEEK_SET);
    if (fread (&msg_idx, sizeof (struct MyIndex), 1, idxptr) != (size_t) 1)
        return (ERROR) ;
    last_read = msg_idx.LastRead;
    if (last_read >= num_messages)
      last_read = num_messages - 1;	/* repair an error by resetting message pointer */
    if (last_read > msg_start)
      last_read -= msg_start;
    else
      last_read = 0;

#ifdef ATPDBG3
    printf ("LAST: %ld   START: %ld\n", last_read, msg_start);
    sleep (5);
#endif

/* 	Point to the index record for new first message.
Get its offset in the message data base and its size */

    fseek (idxptr, idx_offset, SEEK_SET);
    old_num = 0;
    old_size = 0;
    old_offset = 0;

/* LOOP through message base */
    while (fread (&msg_idx, sizeof (struct MyIndex), 1, idxptr) == (size_t) 1)
      {

#ifdef ATPDBG3
	printf ("Offset = %ld\n", msg_idx.Offset);
	printf ("MsgNum = %ld\n", msg_idx.MsgNum);
	printf ("MsgSize = %ld\n", msg_idx.Size);
#endif

/* read in the new message to the buffer */
	nrecs = (msg_idx.Size / 128) + 1;
	fseek (ocnfptr, msg_idx.Offset, SEEK_SET);
	if (nrecs > RbufRecs && !reup ((nrecs + 1) * 128L))
	  {
	    check_mode = TRUE;	/* don't unlink source message base */
	    nrml_exit = FALSE;
	    goto fini;		/* can't reallocte buffer */
	  }
	fread (rbuf, 128, nrecs, ocnfptr);

/*  verify that message size in index and and header match */
	msg_size = atoi ((char *) &rbuf[115]);
	if (msg_size != nrecs)
	  {
	    fprintf (stderr, "Warning: index and header size mismatch: #%ld             \n", old_num + 1);
	    fprintf (stderr, "header size says %d blocks\n", msg_size);
	    fprintf (stderr, " index size says %d blocks\n", nrecs);
	  }
#ifdef ATPDBG3
	fprintf (stderr, "msg_size = %d  nrecs = %d \n", msg_size, nrecs);
#endif
/* check if status byte is marked as killed */
	kill_it = rbuf[0] & 0x80;

/* Now  we update the new index and conference files */
	if (!kill_it)
	  {
	    if ( fwrite (rbuf, 128, nrecs, ncnfptr) != nrecs ){
			printf("\n (ncnfptr) Write error in clip()%c (disk full?)\n", BELL ) ;
	    	check_mode = TRUE;	/* don't unlink source */
	    	nrml_exit = FALSE;
			goto fini ;
		}
	    msg_idx.Offset = old_size + old_offset;
	    msg_idx.MsgNum = old_num;
	    old_num++;
	    old_size = msg_idx.Size;
	    old_offset = msg_idx.Offset + 128;
	    if( fwrite (&msg_idx, sizeof (struct MyIndex), 1, ndxptr) != (size_t) 1 ){
			printf("\n (ndxptr) Write error in clip()%c (disk full?)\n", BELL ) ;
	    	check_mode = TRUE;	/* don't unlink source */
	    	nrml_exit = FALSE;
			goto fini ;
	  	}
	    wrote_one = TRUE;
	  }
	else if ((old_num <= last_read) && last_read)
	  {
	    last_read--;	/* adjust last message read */
	  }

	if (verbose)
	  {
	    if (!kill_it)
	      printf ("%s : processing message %ld        \r", tmpbuf, msg_idx.MsgNum + 1);
	    else
	      printf ("%s :    KILLING message %ld        \r", tmpbuf, msg_idx.MsgNum + 1);
	  }
	fflush (stdout);
      }
    if (verbose){ 
      printf ("\n\n");
	  printf("wait ... \r");
	  fflush(stdout);
	}
/* fix-up last_read pointer */
    fseek (ndxptr, 0L, SEEK_SET);
    fwrite (&last_read, sizeof (long), 1, ndxptr);
/* close open files */
  fini:
    fclose (idxptr);
    fclose (ndxptr);
    fclose (ocnfptr);
    fclose (ncnfptr);

/* build path to temp files */
	strcpy (tmpbuf2, tmpbuf);
	tmptr2 = tmpbuf2;
	while (*tmptr2++);	/* seek to end of buffer */
	tmptr2 -= 4;
	strcpy (tmptr2, "tmc");

    if (nrml_exit)		/* rename and unlink temp files */
      {
	if (!check_mode)	/* move temporary file to conference file */
	  {
	    unlink (tmpbuf);	/* unlink xxx.cnf */
	    if (wrote_one)
	      rename (tmpbuf2, tmpbuf);	/* link xxx.tmc to xxx.cnf */
	    else
	      unlink (tmpbuf2);	/* empty conference file */
	  }
	else
	  unlink (tmpbuf2);

	strcpy (tmptr, "idx");
	strcpy (tmptr2, "tmi");
	if (!check_mode)	/* move temporary file to conferce file */
	  {
	    unlink (tmpbuf);	/* unlink xxx.idx */
	    if (wrote_one)
	      rename (tmpbuf2, tmpbuf);	/* link xxx.tmi to xxx.idx */
	    else
	      unlink (tmpbuf2);
	  }
	else
	  unlink (tmpbuf2);
  	if (verbose)
    	printf("done.     \n");
      } else {  /* not normal exit */
		/* unlink temporary files on error */
	      unlink (tmpbuf2);	/* unlink xxx.tmc (temp conference file) */
		  strcpy (tmptr2, "tmi");
	      unlink (tmpbuf2);	/* unlink xxx.tmi (temp index file) */
		  printf("\n");
	  }
  }
  return (OK);
}
