/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                      epcopy (error prone copy)                          *
 *        a little tool to copy files skipping erroneous bytes             *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright (C) 2005 Benjamin <bookeldor> Canou                           *
 *                                                                         *
 * 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 2 of the License, 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., 51 Franklin St, Fifth Floor,            *
 * Boston, MA  02110-1301, USA.                                            *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define BLOCK_SIZE 1000

int epcopy (char *source, char *destination) {
  FILE *fsrc, *fdest, *flog;
  long size, pos;
  long nberr = 0;
  char *logname;

  /* open source */
  if ((fsrc = fopen (source, "rb")) == NULL)
    {
      perror ("fopen (source)");
      return -1;
    }

  /* open destination */
  if ((fdest = fopen (destination, "wb")) == NULL)
    {
      perror ("fopen (destination)");
      if (fclose (fsrc) != 0)
	perror ("fclose (source)");
      return -1;
    }

  /* open log */
  logname = (char *) malloc (strlen (destination) + 7 + 1);
  sprintf (logname, "%s.errlog", destination);
  if ((flog = fopen (logname, "wb")) == NULL)
    {
      perror ("fopen (log)");
      if (fclose (fsrc) != 0)
	perror ("fclose (source)");
      if (fclose (fdest) != 0)
	perror ("fclose (destination)");
      return -1;
    }

  /* size */
  fseek (fsrc, 0, SEEK_END);
  size = ftell (fsrc);
  fseek (fsrc, 0, SEEK_SET);

  /* pre stats */
  printf ("file size:%d B = %d MiB \n", size, size / 1024 / 1024);

  /* perform copy */
  for (pos = 0; pos < size; pos += BLOCK_SIZE)
    {
      uint8_t b, t[BLOCK_SIZE];
      long spos, rsize;

      /* try a big block */
      clearerr (fsrc);
      fseek (fsrc, pos, SEEK_SET);
      rsize = fread (t, 1, BLOCK_SIZE, fsrc);
      if (ferror (fsrc) == 0)
	{
	  clearerr (fdest);
	  fseek (fdest, pos, SEEK_SET);
	  fwrite (t, 1, rsize, fdest);
	  if (ferror (fdest) != 0)
	    {
	      printf ("error in destination, exiting \n");
	      fclose (fsrc);
	      fclose (fdest);
	      fclose (flog);
	      printf ("unable to perform copy \n", nberr);
	      return -1;
	    }
	  printf ("%d %%, %d B = %d MiB, %d errors, quick copy \033[1000D",
		  (pos +
		   BLOCK_SIZE) /
		  (size / 100),
		  pos + BLOCK_SIZE, (pos + BLOCK_SIZE) / 1024 / 1024, nberr);
	  fflush (stdout);
	}
      else
	{
	  for (spos = pos; (spos < size) && (spos < pos + BLOCK_SIZE); spos++)
	    {
	      clearerr (fsrc);
	      fseek (fsrc, spos, SEEK_SET);
	      rsize = fread (&b, 1, 1, fsrc);
	      if ((ferror (fsrc) != 0) || (rsize == 0))
		{
		  nberr++;
		  fprintf (flog, "%lu \n", spos);
		  printf
		    ("%d %%, %d B = %d MiB, %d errors, SLOW copy \033[1000D",
		     (spos) / (size / 100), spos, (spos) / 1024 / 1024,
		     nberr);
		}
	      else
		{
		  clearerr (fdest);
		  fseek (fdest, spos, SEEK_SET);
		  fwrite (&b, 1, 1, fdest);
		  if (ferror (fdest) != 0)
		    {
		      printf ("error in destination, exiting \n");
		      fclose (fsrc);
		      fclose (fdest);
		      fclose (flog);
		      printf ("unable to perform copy \n", nberr);
		      return -1;
		    }
		  printf
		    ("%d %%, %d B =%d MiB, %d errors, SLOW copy \033[1000D",
		     (spos) / (size / 100), spos, (spos) / 1024 / 1024,
		     nberr);
		  fflush (stdout);
		}
	    }
	  pos = spos;
	}
    }

  /* post stats */
  printf ("copy successfully performed                    \n"
	  "%d bad bytes on %lu (%02 d %%) \n", nberr, size,
	  nberr * 100 / size);
  /* close both */
  if (fclose (fsrc) != 0)
    {
      perror ("fclose (source)");
      if (fclose (flog) != 0)
	perror ("fclose (log)");
      if (fclose (fdest) != 0)
	perror ("fclose (destination)");
      return -1;
    }
  if (fclose (flog) != 0)
    {
      perror ("fclose (log)");
      if (fclose (fdest) != 0)
	perror ("fclose (destination)");
      return -1;
    }
  if (fclose (fdest) != 0)
    {
      perror ("fclose (destination)");
      return -1;
    }
}

int main (int argc, char *argv[]) {
  if (argc != 3) {
      if (argc > 0)
	printf ("usage:\n\t %s source destination \n", argv[0]);
      else
	printf ("usage:\n\tepcopy source destination \n");
      return 1;
    }
  if (epcopy (argv[1], argv[2]) != -1)
    return 0;
  else
    return 1;
}
