/* diffs.c: 
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/os/signal.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/file-names.h"
#include "file-utils/libfsutils/diffs.h"



int
binary_files_differ (t_uchar * a, t_uchar * b)
{
  struct stat a_stat;
  struct stat b_stat;
  static t_uchar a_buf[65536];
  static t_uchar b_buf[sizeof (a_buf)];
  int a_fd;
  int b_fd;
  long a_amt;
  long b_amt;
  int answer;

  safe_stat (a, &a_stat);
  safe_stat (a, &b_stat);

  if (a_stat.st_size != b_stat.st_size)
    return 1;
    
  a_fd = safe_open (a, O_RDONLY, 0);
  b_fd = safe_open (b, O_RDONLY, 0);
  
  answer = 0;

  while (1)
    {
      a_amt = safe_read_retry (a_fd, a_buf, sizeof (a_buf));
      b_amt = safe_read_retry (b_fd, b_buf, sizeof (b_buf));

      if ((a_amt != b_amt) || mem_cmp (a_buf, b_buf, a_amt))
        {
          answer = 1;
          break;
        }

      if (!a_amt)
        break;
    }

  safe_close (a_fd);
  safe_close (b_fd);

  return answer;
}



int
invoke_diff (int output_fd, char * orig_path, char * orig_loc, char * mod_path, char * mod_loc)
{
  char * orig_label;
  char * mod_label;
  int pid;

  if (!binary_files_differ (orig_path, mod_path))
    return 0;

  orig_label = file_name_in_vicinity (0, "orig", orig_loc + 2);
  mod_label = file_name_in_vicinity (0, "mod", orig_loc + 2);

  pid = fork ();
  if (pid == -1)
    panic ("unable to fork for diff");

  if (pid)
    {
      int status;
      int wait_pid;

      wait_pid = waitpid (pid, &status, 0);
      if (wait_pid < 0)
	{
	  panic_msg ("error waiting for subprocess");
	  kill (0, SIGKILL);
	  panic ("error waiting for subprocess");
	}
      if (WIFSIGNALED (status))
	{
	  safe_printfmt (2, "\n");
	  safe_printfmt (2, "diff subprocess killed by signal %d\n", WTERMSIG (status));
	  safe_printfmt (2, "\n");
	  exit (2);
	}
      else if (!WIFEXITED (status))
	{
	  panic_msg ("waitpid returned for a non-exited process");
	  kill (0, SIGKILL);
	  panic ("waitpid returned for a non-exited process");
	}
      else
	{
	  int exit_status;

	  exit_status = WEXITSTATUS (status);

	  if (exit_status && (exit_status != 1) && (exit_status != 2))
	    {
	      safe_printfmt (2, "\n");
	      safe_printfmt (2, "encountered error diffing files\n");
	      safe_printfmt (2, "  orig: %s\n", orig_path);
	      safe_printfmt (2, "  mod: %s\n", mod_path);
	      safe_printfmt (2, "  diff exit status: %d\n", exit_status);
	      safe_printfmt (2, "\n");
	      exit (2);
	    }
	  lim_free (0, orig_label);
	  lim_free (0, mod_label);
	  return exit_status;
	}
    }
  else
    {
      int errn;
      if (0 > vu_move_fd (&errn, output_fd, 1))
	panic ("unable to redirect stdout for diff");
      execlp ("diff", "diff", "-u", "-L", orig_label, "-L", mod_label, orig_path, mod_path, 0);
      panic ("execlp for diff returned to caller");
      exit (2);
    }
  panic ("invoke_diff: not reached");
  return 1;
}



/* tag: Tom Lord Mon May 19 18:00:23 2003 (diffs.c)
 */
