/* changeset-report.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/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "file-utils/libfsutils/read-line.h"
#include "file-utils/libawk/relassoc.h"
#include "file-utils/libfsutils/find-utils.h"
#include "file-utils/libfsutils/ensure-dir.h"
#include "file-utils/libfsutils/copy-file.h"
#include "file-utils/libarch/changeset-utils.h"
#include "file-utils/libarch/changeset-report.h"


/* __STDC__ prototypes for static functions */
static void fix_mod_locs (assoc_table loc_of, rel_table table);
static void report_unique_files_and_symlinks (rel_table * files, rel_table * symlinks,
                                              rel_table index,
                                              t_uchar * archive_dir, t_uchar * archive_basename);
static void find_renames (rel_table * out, rel_table common, assoc_table orig_dir_tag_of, assoc_table mod_dir_tag_of);




int
arch_any_changes (struct arch_changeset_report * report)
{
  return (report->removed_dirs
          || report->added_dirs

          || report->removed_files
          || report->added_files

          || report->removed_symlinks
          || report->added_symlinks

          || report->renamed_files
          || report->renamed_dirs

          || report->patched_regular_files
          || report->patched_symlinks
          || report->patched_binaries
          || report->file_metadata_changed
          || report->dir_metadata_changed
          || report->symlink_to_file
          || report->file_to_symlink);
}

void
arch_evaluate_changeset (struct arch_changeset_report * report, t_uchar * path)
{
  t_uchar * orig_files_index_path = 0;
  t_uchar * orig_dirs_index_path = 0;
  t_uchar * mod_files_index_path = 0;
  t_uchar * mod_dirs_index_path = 0;

  rel_table removed_dirs_index = 0;
  rel_table added_dirs_index = 0;

  rel_table removed_files_and_symlinks_index = 0;
  rel_table added_files_and_symlinks_index = 0;

  rel_table common_files_index = 0; /* [0] origloc [1] modloc [2] tag */
  rel_table common_dirs_index = 0; /* [0] origloc [1] modloc [2] tag */

  assoc_table orig_dir_loc_of = 0;
  assoc_table orig_dir_tag_of = 0;
  assoc_table orig_file_loc_of = 0;
  assoc_table orig_file_tag_of = 0;
  assoc_table mod_dir_loc_of = 0;
  assoc_table mod_dir_tag_of = 0;
  assoc_table mod_file_loc_of = 0;
  assoc_table mod_file_tag_of = 0;



  orig_files_index_path = file_name_in_vicinity (0, path, "orig-files-index");
  orig_dirs_index_path = file_name_in_vicinity (0, path, "orig-dirs-index");
  mod_files_index_path = file_name_in_vicinity (0, path, "mod-files-index");
  mod_dirs_index_path = file_name_in_vicinity (0, path, "mod-dirs-index");

  report->orig_files_index = arch_read_changeset_index (orig_files_index_path);
  report->orig_dirs_index = arch_read_changeset_index (orig_dirs_index_path);
  report->mod_files_index = arch_read_changeset_index (mod_files_index_path);
  report->mod_dirs_index = arch_read_changeset_index (mod_dirs_index_path);

  /* just to be sure:
   */
  rel_sort_table_by_field (0, report->orig_files_index, 1);
  rel_sort_table_by_field (0, report->orig_dirs_index, 1);
  rel_sort_table_by_field (0, report->mod_files_index, 1);
  rel_sort_table_by_field (0, report->mod_dirs_index, 1);

  orig_dir_tag_of = rel_to_assoc (report->orig_dirs_index, 0, 1);
  orig_dir_loc_of = rel_to_assoc (report->orig_dirs_index, 1, 0);
  orig_file_tag_of = rel_to_assoc (report->orig_files_index, 0, 1);
  orig_file_loc_of = rel_to_assoc (report->orig_files_index, 1, 0);
  mod_dir_tag_of = rel_to_assoc (report->mod_dirs_index, 0, 1);
  mod_dir_loc_of = rel_to_assoc (report->mod_dirs_index, 1, 0);
  mod_file_tag_of = rel_to_assoc (report->mod_files_index, 0, 1);
  mod_file_loc_of = rel_to_assoc (report->mod_files_index, 1, 0);


  /****************************************************************
   * Removed and added directories
   */

  {
    t_uchar * original_only_dir_metadata_file = 0;
    t_uchar * modified_only_dir_metadata_file = 0;
    rel_table original_only_dir_metadata = 0;
    rel_table modified_only_dir_metadata = 0;
    rel_table orig_dirs_by_name = 0;
    rel_table mod_dirs_by_name = 0;
    rel_table t;	/* [0] name [1] tag  -- sort 0*/
    rel_table t2;	/* [0] name [1] tag [2] name -- sort 0 -- of dirs sans perms */

    original_only_dir_metadata_file = file_name_in_vicinity (0, path, "original-only-dir-metadata");
    modified_only_dir_metadata_file = file_name_in_vicinity (0, path, "original-only-dir-metadata");

    original_only_dir_metadata = arch_read_changeset_dir_metadata (original_only_dir_metadata_file);
    modified_only_dir_metadata = arch_read_changeset_dir_metadata (modified_only_dir_metadata_file);

    orig_dirs_by_name = rel_copy_table (report->orig_dirs_index);
    rel_sort_table_by_field (0, orig_dirs_by_name, 0);

    mod_dirs_by_name = rel_copy_table (report->mod_dirs_index);
    rel_sort_table_by_field (0, mod_dirs_by_name, 0);

    removed_dirs_index = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);
    added_dirs_index = rel_join (2, rel_join_output (2,0, 2,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);

    t = rel_copy_table (removed_dirs_index);
    rel_sort_table_by_field (0, t, 0);
    report->removed_dirs = rel_join (-1, rel_join_output (1,0, 1,1, 2,1, -1), 0, 0, t, original_only_dir_metadata);
    t2 = rel_join (1, rel_join_output (1,0, 1,1, 1,0, -1), 0, 0, t, original_only_dir_metadata);
    rel_append_x (&report->removed_dirs, t2);
    rel_sort_table_by_field (0, report->removed_dirs, 0);
    rel_free_table (t);
    rel_free_table (t2);
    invariant (rel_n_records (report->removed_dirs) == rel_n_records (removed_dirs_index));

    t = rel_copy_table (added_dirs_index);
    rel_sort_table_by_field (0, t, 0);
    report->added_dirs = rel_join (-1, rel_join_output (1,0, 1,1, 2,1, -1), 0, 0, t, modified_only_dir_metadata);
    t2 = rel_join (1, rel_join_output (1,0, 1,1, 1,0, -1), 0, 0, t, modified_only_dir_metadata);
    rel_append_x (&report->added_dirs, t2);
    rel_sort_table_by_field (0, report->added_dirs, 0);
    rel_free_table (t);
    rel_free_table (t2);
    invariant (rel_n_records (report->added_dirs) == rel_n_records (added_dirs_index));

    lim_free (0, original_only_dir_metadata_file);
    lim_free (0, modified_only_dir_metadata_file);
    rel_free_table (original_only_dir_metadata);
    rel_free_table (modified_only_dir_metadata);
    rel_free_table (orig_dirs_by_name);
    rel_free_table (mod_dirs_by_name);
  }
  
  /****************************************************************
   * Removed and added filenames and symlinks
   */

  removed_files_and_symlinks_index = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);
  added_files_and_symlinks_index = rel_join (2, rel_join_output (2,0, 2,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);

  report_unique_files_and_symlinks (&report->removed_files, &report->removed_symlinks,
                                    removed_files_and_symlinks_index,
                                    path, "removed-files-archive");
  
  report_unique_files_and_symlinks (&report->added_files, &report->added_symlinks,
                                    added_files_and_symlinks_index,
                                    path, "new-files-archive");

  /****************************************************************
   * renamed files and dirs
   */

  common_dirs_index = rel_join (-1, rel_join_output (1,0, 2,0, 1,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);
  common_files_index = rel_join (-1, rel_join_output (1,0, 2,0, 1,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);

  find_renames (&report->renamed_dirs, common_dirs_index, orig_dir_tag_of, mod_dir_tag_of);
  find_renames (&report->renamed_files, common_files_index, orig_dir_tag_of, mod_dir_tag_of);

  /****************************************************************
   * Identify patches
   */
  {
    int lim;
    int x;
    int here_fd;
    t_uchar * patches_root;
    rel_table patches_files = 0;
    assoc_table saw_patches_file = 0;

    patches_root = file_name_in_vicinity (0, path, "patches");

    here_fd = safe_open (".", O_RDONLY, 0);
    safe_chdir (patches_root);
    find_files (&patches_files, ".");
    safe_fchdir (here_fd);
    safe_close (here_fd);

    saw_patches_file = rel_to_assoc (patches_files, 0, 0);

    lim = rel_n_records (patches_files);
    for (x = 0; x < lim; ++x)
      {
        t_uchar * patch_file = 0;
        t_uchar * patch_file_tail = 0;
        t_uchar * patch_file_suffix = 0;

        t_uchar * patch_file_loc = 0;
        rel_table * dest;
        t_uchar * complement_patch = 0;

        t_uchar * patch_file_path = 0;
        t_uchar * tag = 0;      /* not allocated */
        

        patch_file = patches_files[x][0];
        patch_file_suffix = str_chr_rindex (patch_file, '.');
        patch_file_tail = file_name_tail (0, patch_file);

        /* Compute the loc and the appropriate table for this
         * this patch.  (patch_file_loc and dest)
         */
        if (!str_cmp (patch_file_tail, "=dir-meta-orig"))
          {
            t_uchar * t;

            t = file_name_directory (0, patch_file);
            patch_file_loc = file_name_from_directory (0, t);
            lim_free (0, t);

            dest = &report->dir_metadata_changed;
            tag = assoc_ref (mod_dir_tag_of, patch_file_loc);
          }
        else if (!str_cmp (patch_file_suffix, ".link-orig"))
          {
            patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
            complement_patch = str_alloc_cat (0, patch_file_loc, ".link-mod");
            if (assoc_ref (saw_patches_file, complement_patch))
              dest = &report->patched_symlinks;
            else
              dest = &report->symlink_to_file;
            tag = assoc_ref (mod_file_tag_of, patch_file_loc);
          }
        else if (!str_cmp (patch_file_suffix, ".original"))
          {
            patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
            complement_patch = str_alloc_cat (0, patch_file_loc, ".modified");
            if (assoc_ref (saw_patches_file, complement_patch))
              dest = &report->patched_binaries;
            else
              dest = &report->file_to_symlink;
            tag = assoc_ref (mod_file_tag_of, patch_file_loc);
          }
        else if (!str_cmp (patch_file_suffix, ".patch"))
          {
            patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
            dest = &report->patched_regular_files;
            tag = assoc_ref (mod_file_tag_of, patch_file_loc);
          }
        else if (!str_cmp (patch_file_suffix, ".meta-orig"))
          {
            patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
            dest = &report->file_metadata_changed;
            tag = assoc_ref (mod_file_tag_of, patch_file_loc);
          }

        if (patch_file_loc)
          {
            patch_file_path = file_name_in_vicinity (0, patches_root, patch_file_loc);
            rel_add_records (dest, rel_make_record (patch_file_loc, tag, patch_file_path, 0), 0);
          }

        lim_free (0, patch_file_tail);
        lim_free (0, patch_file_loc);
        lim_free (0, complement_patch);
        lim_free (0, patch_file_path);
      }


    lim_free (0, patches_root);
    rel_free_table (patches_files);
    free_assoc_table (saw_patches_file);
  }

  rel_sort_table_by_field (0, report->removed_dirs, 0);
  rel_sort_table_by_field (0, report->added_dirs, 0);

  rel_sort_table_by_field (0, report->removed_files, 0);
  rel_sort_table_by_field (0, report->added_files, 0);

  rel_sort_table_by_field (0, report->removed_symlinks, 0);
  rel_sort_table_by_field (0, report->added_symlinks, 0);

  rel_sort_table_by_field (0, report->renamed_files, 0);
  rel_sort_table_by_field (0, report->renamed_dirs, 0);

  rel_sort_table_by_field (0, report->patched_regular_files, 0);
  rel_sort_table_by_field (0, report->patched_symlinks, 0);
  rel_sort_table_by_field (0, report->patched_binaries, 0);
  rel_sort_table_by_field (0, report->file_metadata_changed, 0);
  rel_sort_table_by_field (0, report->dir_metadata_changed, 0);
  rel_sort_table_by_field (0, report->symlink_to_file, 0);
  rel_sort_table_by_field (0, report->file_to_symlink, 0);

  lim_free (0, orig_files_index_path);
  lim_free (0, orig_dirs_index_path);
  lim_free (0, mod_files_index_path);
  lim_free (0, mod_dirs_index_path);
  rel_free_table (removed_dirs_index);
  rel_free_table (added_dirs_index);
  rel_free_table (removed_files_and_symlinks_index);
  rel_free_table (added_files_and_symlinks_index);
  free_assoc_table (orig_dir_loc_of);
  free_assoc_table (orig_dir_tag_of);
  free_assoc_table (orig_file_loc_of);
  free_assoc_table (orig_file_tag_of);
  free_assoc_table (mod_dir_loc_of);
  free_assoc_table (mod_dir_tag_of);
  free_assoc_table (mod_file_loc_of);
  free_assoc_table (mod_file_tag_of);
  rel_free_table (common_files_index);
  rel_free_table (common_dirs_index);
  
}


void
arch_reverse_changeset (struct arch_changeset_report * report)
{
  rel_table t;
  int x;
  assoc_table new_mod_file_loc_of = 0;
  assoc_table new_mod_dir_loc_of = 0;

  t = report->orig_files_index;
  report->orig_files_index = report->mod_files_index;
  report->mod_files_index = t;

  t = report->orig_dirs_index;
  report->orig_dirs_index = report->mod_dirs_index;
  report->mod_dirs_index = t;

  t = report->removed_dirs;
  report->removed_dirs = report->added_dirs;
  report->added_dirs = t;

  t = report->removed_files;
  report->removed_files = report->added_files;
  report->added_files = t;

  t = report->removed_symlinks;
  report->removed_symlinks = report->added_symlinks;
  report->added_symlinks = t;

  for (x = 0; x < rel_n_records (report->renamed_files); ++x)
    {
      t_uchar * s;

      s = report->renamed_files[x][0];
      report->renamed_files[x][0] = report->renamed_files[x][1];
      report->renamed_files[x][1] = s;
    }
  for (x = 0; x < rel_n_records (report->renamed_dirs); ++x)
    {
      t_uchar * s;

      s = report->renamed_dirs[x][0];
      report->renamed_dirs[x][0] = report->renamed_dirs[x][1];
      report->renamed_dirs[x][1] = s;
    }

  new_mod_file_loc_of = rel_to_assoc (report->mod_files_index, 1, 0);
  new_mod_dir_loc_of = rel_to_assoc (report->mod_dirs_index, 1, 0);

  fix_mod_locs (new_mod_file_loc_of, report->patched_regular_files);
  fix_mod_locs (new_mod_file_loc_of, report->patched_symlinks);
  fix_mod_locs (new_mod_file_loc_of, report->patched_binaries);
  fix_mod_locs (new_mod_file_loc_of, report->file_metadata_changed);
  fix_mod_locs (new_mod_dir_loc_of, report->dir_metadata_changed);
  fix_mod_locs (new_mod_file_loc_of, report->symlink_to_file);
  fix_mod_locs (new_mod_file_loc_of, report->file_to_symlink);
}

static void
fix_mod_locs (assoc_table loc_of, rel_table table)
{
  int x;

  for (x = 0; x < rel_n_records (table); ++x)
    {
      t_uchar * new_loc = 0;
      t_uchar * old_loc = 0;
      t_uchar * tag;

      tag = table[x][1];
      old_loc = table[x][0];
      new_loc = assoc_ref (loc_of, tag);
      if (new_loc)
        new_loc = str_save (0, new_loc);
      table[x][0] = new_loc;
      lim_free (0, old_loc);
    }
}

void
arch_print_changeset  (int out_fd, struct arch_changeset_report * report, int diffs)
{
  int x;

  if (report->removed_dirs)
    {
      safe_printfmt (out_fd, "* removed directories\n\n");
      for (x = 0; x < rel_n_records (report->removed_dirs); ++x)
        safe_printfmt (out_fd, "    %s\n", report->removed_dirs[x][0]);
      safe_printfmt (out_fd, "\n");
    }
  if (report->added_dirs)
    {
      safe_printfmt (out_fd, "* added directories\n\n");
      for (x = 0; x < rel_n_records (report->added_dirs); ++x)
        safe_printfmt (out_fd, "    %s\n", report->added_dirs[x][0]);
      safe_printfmt (out_fd, "\n");
    }

  if (report->removed_files)
    {
      safe_printfmt (out_fd, "* removed files\n\n");
      for (x = 0; x < rel_n_records (report->removed_files); ++x)
        safe_printfmt (out_fd, "    %s\n", report->removed_files[x][0]);
      safe_printfmt (out_fd, "\n");
    }
  if (report->added_files)
    {
      safe_printfmt (out_fd, "* added files\n\n");
      for (x = 0; x < rel_n_records (report->added_files); ++x)
        safe_printfmt (out_fd, "    %s\n", report->added_files[x][0]);
      safe_printfmt (out_fd, "\n");
    }

  if (report->removed_symlinks)
    {
      safe_printfmt (out_fd, "* removed symlinks\n\n");
      for (x = 0; x < rel_n_records (report->removed_symlinks); ++x)
        safe_printfmt (out_fd, "    %s\n", report->removed_symlinks[x][0]);
      safe_printfmt (out_fd, "\n");
    }
  if (report->added_symlinks)
    {
      safe_printfmt (out_fd, "* added symlinks\n\n");
      for (x = 0; x < rel_n_records (report->added_symlinks); ++x)
        safe_printfmt (out_fd, "    %s\n", report->added_symlinks[x][0]);
      safe_printfmt (out_fd, "\n");
    }

  if (report->renamed_files)
    {
      safe_printfmt (out_fd, "* renamed files and symlinks\n\n");
      for (x = 0; x < rel_n_records (report->renamed_files); ++x)
        {
          safe_printfmt (out_fd, "    %s\n     => %s\n\n",
                         report->renamed_files[x][0], 
                         report->renamed_files[x][1]);
        }
    }
  if (report->renamed_dirs)
    {
      safe_printfmt (out_fd, "* renamed directories\n\n");
      for (x = 0; x < rel_n_records (report->renamed_dirs); ++x)
        {
          safe_printfmt (out_fd, "    %s\n     => %s\n\n",
                         report->renamed_dirs[x][0], 
                         report->renamed_dirs[x][1]);
        }
    }

  if (report->file_to_symlink)
    {
      safe_printfmt (out_fd, "* files replaced by symlinks \n\n");
      for (x = 0; x < rel_n_records (report->file_to_symlink); ++x)
        {
          safe_printfmt (out_fd, "    %s\n", report->file_to_symlink[x][0]);
        }
      safe_printfmt (out_fd, "\n");
    }
  if (report->symlink_to_file)
    {
      safe_printfmt (out_fd, "* symlinks replaced by files\n\n");
      for (x = 0; x < rel_n_records (report->symlink_to_file); ++x)
        {
          safe_printfmt (out_fd, "    %s\n", report->symlink_to_file[x][0]);
        }
      safe_printfmt (out_fd, "\n");
    }

  if (report->file_metadata_changed)
    {
      safe_printfmt (out_fd, "* file metadata changed\n\n");
      for (x = 0; x < rel_n_records (report->file_metadata_changed); ++x)
        {
          safe_printfmt (out_fd, "    %s\n", report->file_metadata_changed[x][0]);
          if (diffs)
            {
              t_uchar * orig_meta_path = 0;
              t_uchar * mod_meta_path = 0;
              t_uchar * orig_meta = 0;
              t_uchar * mod_meta = 0;

              orig_meta_path = str_alloc_cat (0, report->file_metadata_changed[x][2], ".meta-orig");
              mod_meta_path = str_alloc_cat (0, report->file_metadata_changed[x][2], ".meta-mod");
              orig_meta = read_line_from_file (orig_meta_path);
              mod_meta = read_line_from_file (mod_meta_path);

              safe_printfmt (out_fd, "        %s\n        => %s\n", orig_meta, mod_meta);

              lim_free (0, orig_meta_path);
              lim_free (0, mod_meta_path);
              lim_free (0, orig_meta);
              lim_free (0, mod_meta);
            }
        }
      safe_printfmt (out_fd, "\n");
    }

  if (report->dir_metadata_changed)
    {
      safe_printfmt (out_fd, "* dir metadata changed\n\n");
      for (x = 0; x < rel_n_records (report->dir_metadata_changed); ++x)
        {
          safe_printfmt (out_fd, "    %s\n", report->dir_metadata_changed[x][0]);
          if (diffs)
            {
              t_uchar * orig_meta_path = 0;
              t_uchar * mod_meta_path = 0;
              t_uchar * orig_meta = 0;
              t_uchar * mod_meta = 0;

              orig_meta_path = file_name_in_vicinity (0, report->dir_metadata_changed[x][2], "=dir-meta-orig");
              mod_meta_path = file_name_in_vicinity (0, report->dir_metadata_changed[x][2], "=dir-meta-mod");
              orig_meta = read_line_from_file (orig_meta_path);
              mod_meta = read_line_from_file (mod_meta_path);

              safe_printfmt (out_fd, "        %s\n        => %s\n", orig_meta, mod_meta);

              lim_free (0, orig_meta_path);
              lim_free (0, mod_meta_path);
              lim_free (0, orig_meta);
              lim_free (0, mod_meta);
            }
        }
      safe_printfmt (out_fd, "\n");
    }

  if (report->patched_symlinks)
    {
      safe_printfmt (out_fd, "* retargeted symlinks\n\n");
      for (x = 0; x < rel_n_records (report->patched_symlinks); ++x)
        safe_printfmt (out_fd, "    %s\n", report->patched_symlinks[x][0]);
      safe_printfmt (out_fd, "\n");
    }
  if (report->patched_binaries)
    {
      safe_printfmt (out_fd, "* modified binary files\n\n");
      for (x = 0; x < rel_n_records (report->patched_binaries); ++x)
        safe_printfmt (out_fd, "    %s\n", report->patched_binaries[x][0]);
      safe_printfmt (out_fd, "\n");
    }
  if (report->patched_regular_files)
    {
      safe_printfmt (out_fd, "* modified files\n\n");
      for (x = 0; x < rel_n_records (report->patched_regular_files); ++x)
        safe_printfmt (out_fd, "    %s\n", report->patched_regular_files[x][0]);
      safe_printfmt (out_fd, "\n");

      if (diffs)
        {
          safe_printfmt (out_fd, "* file diffs\n\n");
          for (x = 0; x < rel_n_records (report->patched_regular_files); ++x)
            {
              t_uchar * patch_file;
              int in_fd;

              patch_file = str_alloc_cat (0, report->patched_regular_files[x][2], ".patch");
              in_fd = safe_open (patch_file, O_RDONLY, 0);
              copy_fd (in_fd, out_fd);
              safe_printfmt (out_fd, "\n\n");
              safe_close (in_fd);
              lim_free (0, patch_file);
            }
        }
    }
}


void
arch_free_changeset_report_data (struct arch_changeset_report * r)
{
  rel_free_table (r->orig_files_index);                 r->orig_files_index = 0;
  rel_free_table (r->orig_dirs_index);                  r->orig_dirs_index = 0;
  rel_free_table (r->mod_files_index);                  r->mod_files_index = 0;
  rel_free_table (r->mod_dirs_index);                   r->mod_dirs_index = 0;

  rel_free_table (r->removed_dirs);                     r->removed_dirs = 0;
  rel_free_table (r->added_dirs);                       r->added_dirs = 0;

  rel_free_table (r->removed_files);                    r->removed_files = 0;
  rel_free_table (r->added_files);                      r->added_files = 0;

  rel_free_table (r->removed_symlinks);                 r->removed_symlinks = 0;
  rel_free_table (r->added_symlinks);                   r->added_symlinks = 0;

  rel_free_table (r->renamed_files);                    r->renamed_files = 0;
  rel_free_table (r->renamed_dirs);                     r->renamed_dirs = 0;

  rel_free_table (r->patched_regular_files);            r->patched_regular_files = 0;
  rel_free_table (r->patched_symlinks);                 r->patched_symlinks = 0;
  rel_free_table (r->patched_binaries);                 r->patched_binaries = 0;
  rel_free_table (r->file_metadata_changed);            r->file_metadata_changed = 0;
  rel_free_table (r->dir_metadata_changed);             r->dir_metadata_changed = 0;
  rel_free_table (r->symlink_to_file);                  r->symlink_to_file = 0;
  rel_free_table (r->file_to_symlink);                  r->file_to_symlink = 0;
}




static void
report_unique_files_and_symlinks (rel_table * files, rel_table * symlinks,
                                  rel_table index,
                                  t_uchar * archive_dir, t_uchar * archive_basename)
{
  t_uchar * archive = 0;
  int lim;
  int x;

  archive = file_name_in_vicinity (0, archive_dir, archive_basename);

  lim = rel_n_records (index);
  for (x = 0; x < lim; ++x)
    {
      t_uchar * archive_path;
      struct stat stat_buf;

      archive_path = file_name_in_vicinity (0, archive, index[x][0]);

      safe_lstat (archive_path, &stat_buf);

      if (S_ISLNK (stat_buf.st_mode))
        {
          rel_add_records (symlinks, rel_make_record (index[x][0], index[x][1], archive_path, 0), 0);
        }
      else
        {
          rel_add_records (files, rel_make_record (index[x][0], index[x][1], archive_path, 0), 0);
        }

      lim_free (0, archive_path);
    }

  rel_sort_table_by_field (0, *files, 0);
  rel_sort_table_by_field (0, *symlinks, 0);

  lim_free (0, archive);
}

static void
find_renames (rel_table * out, rel_table common, assoc_table orig_dir_tag_of, assoc_table mod_dir_tag_of)
{
  int lim;
  int x;

  lim = rel_n_records (common);
  for (x = 0; x < lim; ++x)
    {
      t_uchar * orig_loc = 0;
      t_uchar * mod_loc = 0;
      t_uchar * tag = 0;
      t_uchar * orig_basename = 0;
      t_uchar * mod_basename = 0;
      t_uchar * orig_dir = 0;
      t_uchar * mod_dir = 0;
      t_uchar * orig_dir_tag = 0;
      t_uchar * mod_dir_tag = 0;

      orig_loc = common[x][0];
      mod_loc = common[x][1];
      tag = common[x][2];

      orig_basename = file_name_tail (0, orig_loc);
      mod_basename = file_name_tail (0, mod_loc);

      orig_dir = file_name_directory_file (0, orig_loc);
      mod_dir = file_name_directory_file (0, mod_loc);

      orig_dir_tag = assoc_ref (orig_dir_tag_of, orig_dir);
      mod_dir_tag = assoc_ref (mod_dir_tag_of, mod_dir);

      /* The conditional subexpression here attempts to identify renames
       * even when the the changeset is missing locs and tags for
       * one or both of the containing dirs of the object under consideration.
       *
       */

      if (str_cmp (orig_basename, mod_basename)
          || ((orig_dir_tag && mod_dir_tag)
              ? str_cmp (orig_dir_tag, mod_dir_tag)
              : str_cmp (orig_dir, mod_dir)))
        {
          rel_add_records (out, rel_make_record (orig_loc, mod_loc, tag, 0), 0);
        }
      

      lim_free (0, orig_basename);
      lim_free (0, mod_basename);
      lim_free (0, orig_dir);
      lim_free (0, mod_dir);
    }
}





/* tag: Tom Lord Thu May 15 13:07:24 2003 (changeset-report.c)
 */
