/* repair.c */

/* Written by Stefan Parmark. */

#include <stdio.h>
#ifdef AMIGA
#include <stdlib.h>
#include <string.h>
#endif AMIGA

#include "btoa.h"

/* File names. */
BYTE *diagnosisname   = "btoa.dia";
BYTE *repairname      = "btoa.rep";
BYTE *repairedname    = "btoa.rdy";

/* File headers. */
BYTE *diagnosisheader = "xdiagnosis\n";
BYTE *repairheader    = "xrepair\n";


/* Produce diagnosis file from diagnoses records created by atob(). */
/* It contains the lines immediately before and after the error     */
/* sequence.                                                        */
void producediagnosis(diagnosislist, infile)
register struct Diagnosis *diagnosislist;
register FILE *infile;
{
  register FILE *diagnosisfile;
  LONG startpos, endpos;
  register LONG currentpos;
  extern BYTE *diagnosisname, *diagnosisheader, buffer[BUFSIZE];

  currentpos = ftell(infile);

  if ((diagnosisfile = fopen_write(diagnosisname)) != NULL)
  {
    fprintf(stderr, "btoa: Diagnosis output to '%s'.\n", diagnosisname);

    fputs(diagnosisheader, diagnosisfile);
    do
    {
      /* Extract startpos & endpos from diagnosislist. */
      outdiagnosislist(diagnosislist, &startpos, &endpos);

      if (startpos != -1)
      {
        /* Print line before error. */
        fseek(infile, startpos, 0);
        fgets(buffer, BUFSIZE, infile);
        fputs(buffer, diagnosisfile);

        /* Print line after error. */
        fseek(infile, endpos, 0);
        fgets(buffer, BUFSIZE, infile);
        fputs(buffer, diagnosisfile);
      }
    }
    while (startpos != -1);
    fputs(diagnosisheader, diagnosisfile);

    fclose(diagnosisfile);
  }

  /* Move file pointer to where it was when we entered. */
  fseek(infile, currentpos, 0);
}


/* Insert two file positions into diagnosislist. */
void intodiagnosislist(diagnosislist, startpos, endpos)
register struct Diagnosis *diagnosislist;
register LONG startpos, endpos;
{
  register struct Diagnosis *diagnosisitem, *lastitem;

  diagnosisitem = (struct Diagnosis *)malloc(sizeof(struct Diagnosis));
  diagnosisitem->startpos = startpos;
  diagnosisitem->endpos = endpos;
  diagnosisitem->next = NULL;

  if ((lastitem = diagnosislist->last) == NULL)  /* List is empty */
    diagnosislist->next = diagnosislist->last = diagnosisitem;
  else
  {
    if (lastitem->endpos >= startpos)
    {
      lastitem->endpos = endpos;
      free((BYTE *) diagnosisitem);
    }
    else
    {
      lastitem->next = diagnosisitem;
      diagnosislist->last = diagnosisitem;
    }
  }
}


/* Extract two file positions from diagnosislist. */
void outdiagnosislist(diagnosislist, startpos, endpos)
register struct Diagnosis *diagnosislist;
LONG *startpos, *endpos;
{
  register struct Diagnosis *diagnosisitem;

  if ((diagnosisitem = diagnosislist->next) == NULL)  /* List is empty */
    *startpos = *endpos = -1;
  else
  {
    *startpos = diagnosisitem->startpos;
    *endpos = diagnosisitem->endpos;

    diagnosislist->next = diagnosisitem->next;
    free((BYTE *)diagnosisitem);
    if (diagnosislist->next == NULL)
      diagnosislist->last = NULL;
  }
}


/* Copy infile to outfile until searchstring is found. If outfile */
/* is NULL nothing will be written.                               */
BYTE copyfile(infile, outfile, searchstring)
register FILE *infile, *outfile;
register BYTE *searchstring;
{
  register BYTE stop, error;
  static BYTE copybuffer[BUFSIZE];

  stop = error = FALSE;
  while (!(stop || error))
    if (readbuffer(copybuffer, "archive", infile))
      error = TRUE;
    else
    {
      if (outfile != NULL)
        fputs(copybuffer, outfile);
      if (strcmp(copybuffer, searchstring) == 0)
        stop = TRUE;
    }

  return(error);
}


/* Read a line from infile into buffer. Returns TRUE if */
/* end-of-file has been reached.                        */
BYTE readbuffer(buffer, errormsg, infile)
register BYTE *buffer, *errormsg;
register FILE *infile;
{
  register BYTE error;

  error = FALSE;
  if (fgets(buffer, BUFSIZE, infile) == NULL)
  {
    fprintf(stderr, "btoa: Unexpected end of %s file.\n", errormsg);
    error = TRUE;
  }

  return(error);
}


FILE *fopen_read(filename)
register BYTE *filename;
{
  register FILE *infile;

  if ((infile = fopen(filename, "r")) == NULL)
    fprintf(stderr, "btoa: Can't open '%s' for input.\n", filename);

  return(infile);
}


FILE *fopen_write(filename)
register BYTE *filename;
{
  register FILE *outfile;

  if ((outfile = fopen(filename, "w")) == NULL)
    fprintf(stderr, "btoa: Can't open '%s' for output.\n", filename);

  return(outfile);
}


/* Extract lines from original archive to fix the damaged one. */
BYTE producerepair(infile)
register FILE *infile;
{
  register FILE *repairfile, *diagnosisfile;
  register BYTE error, stop;
  static BYTE *errormsg = "diagnosis";
  extern BYTE *diagnosisname, *diagnosisheader, *repairname, *repairheader,
              buffer[BUFSIZE];

  error = FALSE;
  diagnosisfile = repairfile = NULL;

  fprintf(stderr, "btoa: Repair output to '%s'.\n", repairname);
  if ((diagnosisfile = fopen_read(diagnosisname)) == NULL)
    error = TRUE;
  else if ((repairfile = fopen_write(repairname)) == NULL)
  {
    fclose(diagnosisfile);
    diagnosisfile = NULL;
    error = TRUE;
  }
  else
  {
    /* Read until header is found. This makes it possible to   */
    /* have junk before the header, such as an article header. */
    do
    {
      if (readbuffer(buffer, errormsg, diagnosisfile))
        error = TRUE;
    }
    while (!error && strcmp(buffer, diagnosisheader) != 0);
    fputs(repairheader, repairfile);
  }

  stop = FALSE;
  while (!(error || stop))
  {
    /* Loop until header is found again. */

    if (readbuffer(buffer, errormsg, diagnosisfile))
      error = TRUE;
    else if (strcmp(buffer, diagnosisheader) == 0)
      stop = TRUE;
    else
    {
      /* Read until line before error is found. */
      error = copyfile(infile, NULL, buffer);
      if (!error)
      {
        /* Print line before error. */
        fputs(buffer, repairfile);

        if (readbuffer(buffer, errormsg, diagnosisfile))
          error = TRUE;
        else
        {
          /* Print line after error */
          fputs(buffer, repairfile);
          /* Copy infile to repairfile until line after error */
          error = copyfile(infile, repairfile, buffer);
        }
      }
    }
  }

  if (!error)
    fputs(repairheader, repairfile);

  if (repairfile != NULL)
    fclose(repairfile);
  if (diagnosisfile != NULL)
    fclose(diagnosisfile);

  return(error);
}


/* Repair damaged archive from repair file. */
BYTE performrepair(infile)
register FILE *infile;
{
  register FILE *repairfile, *outfile;
  register BYTE error, stop;
  static BYTE *errormsg = "repair";
  extern BYTE *repairname, *repairedname, *repairheader, buffer[BUFSIZE];

  error = FALSE;
  repairfile = outfile = NULL;

  if ((repairfile = fopen_read(repairname)) == NULL)
    error = TRUE;
  else if ((outfile = fopen_write(repairedname)) == NULL)
  {
    fclose(repairfile);
    repairfile = NULL;
    error = TRUE;
  }
  else
  {
    fprintf(stderr, "btoa: Repaired archive written to '%s'.\n", repairedname);

    /* Read until header is found. */
    do
    {
      if (readbuffer(buffer, errormsg, repairfile))
        error = TRUE;
    }
    while (!error && strcmp(buffer, repairheader) != 0);
  }

  stop = FALSE;
  while (!(error || stop))
  {
    /* Loop until header is found. */

    if (readbuffer(buffer, errormsg, repairfile))
      error = TRUE;
    else if (strcmp(buffer, repairheader) == 0)
      stop = TRUE;
    else
    {
      /* Read and write until line before error. */
      error = copyfile(infile, outfile, buffer);
      if (!error)
        if (readbuffer(buffer, errormsg, repairfile))
          error = TRUE;
        else
        {
          /* Read and write until line after error. */
          error = copyfile(repairfile, outfile, buffer);
          /* Skip until line after error */
          copyfile(infile, NULL, buffer);
        }
    }
  }

  if (!error)  /* Write rest of archive. */
    while (fgets(buffer, BUFSIZE, infile) != NULL)
      fputs(buffer, outfile);

  if (outfile != NULL)
    fclose(outfile);
  if (repairfile != NULL)
    fclose(repairfile);

  return(error);
}
