/*
 * syncfile: keeps one file up to date with another
 * 
 * Tom Coppeto
 * MIT Network Services
 * 20 May 1990
 *
 *    $Source: /afs/net.mit.edu/tools/src/syncfile/RCS/syncfile.c,v $
 *    $Author: tom $
 *    $Locker: tom $
 *    $Log:	syncfile.c,v $
 * Revision 1.2  90/07/21  00:30:39  tom
 * added a blcok argument to never exit even if errors occur
 * 
 * Revision 1.1  90/05/23  14:34:17  tom
 * Initial revision
 * 
 *
 */

#ifndef lint
static char *rcsid = "$Header: /afs/net.mit.edu/tools/src/syncfile/RCS/syncfile.c,v 1.2 90/07/21 00:30:39 tom Exp Locker: tom $";
#endif

#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUF_SIZE 10240

char buf[BUF_SIZE];
char *prgname;

char *source_file = (char *) NULL;
char *target_file = (char *) NULL;
int source;
int target;

/*
 * maintaining an open descriptor on the target doesn't work across afs, so
 * we have to close an reopen after each write
 */

int n_reuse = 0;
int block   = 0;

main(argc, argv)
     int argc;
     char *argv[];
{
  int i;

  prgname = argv[0];

  for(i = 1; i < argc; i++)
    {
      if(strcmp(argv[i], "-s") == 0)
	{
	  n_reuse = 1;
	  continue;
	}
      
      if(strcmp(argv[i], "-b") == 0)
	{
	  block = 1;
	  continue;
	}

      if(source_file == (char *) NULL)
	{
	  source_file = argv[i];
	  continue;
	}
      
      if(target_file == (char *) NULL)
	{
	  target_file = argv[i];
	  continue;
	}
      
      fprintf(stderr, 
	      "usage: %s [-s] [-b] <target file> <source file>\n", prgname);
      exit(1);
    }
	    
  for(;;)
    {
      if((source = open(source_file, O_RDONLY, 0)) < 0)
	{
	  fprintf(stderr, 
		  "%s: cannot open file \"%s\" for reading.\n", prgname,
		  source_file);
	  if(block)
	    continue;
	  exit(1);
	}
     
      create_target();
      if(copy_file(source, target) == 0)
	{
	  if(n_reuse)
	    {
	      if(close(target) < 0)
		perror(prgname);
	      open_target();
	    }

	  update_file(source, target);
	}
      (void) close(source);
      if(close(target) < 0)
	perror(prgname);
    }
}



copy_file(from, to)
     int from;
     int to;
{
  int cc;
  
  while((cc = read(from, buf, sizeof(buf))) > 0)
    {
      if(write(to, buf, cc) != cc)
	{
	  perror("prgname");
	  if(block)
	    {
	      sleep(5);
	      return(-1);
	    }
	  exit(1);
	}
    }
  return(0);
}



update_file(from, to)
     int from;
     int to;
{
  int cc; 

  for (;;)
    {      
      sleep(3);
      if(file_changed(from))
	return;
      while ((cc = read (from, buf, BUF_SIZE)) > 0)
	{
	  (void) write (to, buf, cc);      
	  fsync(to);
	  if(n_reuse)
	    {
	      if(close(to) < 0)
		perror(prgname);
	      to = open_target();
	    }
	}
    }
}


  
file_changed(fd)
     int fd;
{
  static struct stat s;
  static struct stat t;
  static struct stat *a;
  static struct stat *b;
  static int init = 0;
  int status = 0;

  if(init == 0)
    {
      fstat(fd, &s);
      b = &s;
      a = &t;
      init = 1;
      return(0);
    }

  fstat(fd, a);
  if((a->st_size < b->st_size) || (a->st_ino != b->st_ino))
    status = 1;
  
  if(a == &s)
    {
      a = &t;
      b = &s;
    }
  else
    {
      a = &s;
      b = &t;
    }

  return(status);
}



open_target()
{
  while(1)
    {
      if((target = open(target_file, O_WRONLY, 0644)) < 0)
	{
	  fprintf(stderr, 
		  "%s: cannot open file \"%s\" for writing.\n", prgname,
		  target_file);
	  if(block)
	    {
	      sleep(10);
	      continue;
	    }
	  exit(1);
	}
      lseek(target, 0, L_XTND);
      return(target);
    }
}



create_target()
{
  while(1)
    {
      if((target = open(target_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
	{
	  fprintf(stderr, 
		  "%s: cannot open file \"%s\" for writing.\n", prgname,
		  target_file);
	  if(block)
	    {
	      sleep(10);
	      continue;
	    }
	  exit(1);
	}      
      return(target);
    }
}
