/* make-changeset-files.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/mem/mem.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "libarch/diffs.h"
#include "libawk/relassoc.h"
#include "libarch/invent.h"
#include "libarch/make-changeset-files.h"


/* __STDC__ prototypes for static functions */
static int find_id_in_inventory (rel_table invent, t_uchar * id);
static void double_check_file_identity (t_uchar * from_here_path, t_uchar * tree_root, t_uchar * loc);


#if !defined(__GNUC__)
#  undef __attribute__
#  define __attribute__(X)
#endif

static void __attribute__((format (printf, 2, 3)))
invoke_report_callback (struct arch_make_changeset_report * report, char * fmt, ...)
{
  va_list ap;

  if (report->callback)
    {
      va_start (ap, fmt);
      report->callback (report->thunk, fmt, ap);
      va_end (ap);
    }
}

/**
 * \brief make a changeset with limits
 *
 * fixing up the method and unttaged source here
 * requires fixing up inventory.
 * \param file_list a list of paths in the orig tree
 */
void
arch_make_files_changeset (struct arch_make_changeset_report * report,
                           t_uchar * dest,
                           rel_table file_list,
                           arch_project_tree_t * orig, arch_project_tree_t * mod,
                           enum arch_id_tagging_method method,
                           enum arch_inventory_category untagged_source_category,
                           int escape_classes)
{
  int x;
  struct arch_changeset_report csr = {0,};
  int overide_untagged_source = 0;
  int overide_method = 0;
  int prior_untagged_source = 0;
  enum arch_id_tagging_method prior_method = arch_unspecified_id_tagging;

  if (method == arch_unspecified_id_tagging)
    {
      /* sets untagged source in mod */
      method = arch_tree_id_tagging_method (mod, &untagged_source_category, 0);
    }
  else
    {
      overide_untagged_source = 1;
      overide_method = 1;
    }
  /* sets untagged source in orig */
  arch_tree_id_tagging_method (orig, &untagged_source_category, 0);

  mem_set0 ((t_uchar *)&report->orig_index, sizeof (report->orig_index));
  mem_set0 ((t_uchar *)&report->mod_index, sizeof (report->mod_index));

  arch_changeset_inventory (&report->orig_index, orig, method,
                            untagged_source_category, escape_classes);
  report->mod_index.dirs = rel_copy_table (report->orig_index.dirs);
  report->mod_index.files = rel_copy_table (report->orig_index.files);


  /****************************************************************
   * build associative tables too and from ids and locs
   */
  report->orig_dir_id_of = rel_to_assoc (report->orig_index.dirs, 0, 1);
  report->orig_dir_loc_of = rel_to_assoc (report->orig_index.dirs, 1, 0);

  report->orig_file_id_of = rel_to_assoc (report->orig_index.files, 0, 1);
  report->orig_file_loc_of = rel_to_assoc (report->orig_index.files, 1, 0);

  report->mod_dir_id_of = rel_to_assoc (report->mod_index.dirs, 0, 1);
  report->mod_dir_loc_of = rel_to_assoc (report->mod_index.dirs, 1, 0);

  report->mod_file_id_of = rel_to_assoc (report->mod_index.files, 0, 1);
  report->mod_file_loc_of = rel_to_assoc (report->mod_index.files, 1, 0);

  assoc_set (&report->orig_dir_id_of, ".", "?_.");
  assoc_set (&report->mod_dir_id_of, ".", "?_.");
  assoc_set (&report->orig_dir_loc_of, "?_.", ".");
  assoc_set (&report->mod_dir_loc_of, "?_.", ".");

  arch_make_changeset_compute_container_map (&report->orig_container_dir_id_of_dir_id, report->orig_dir_id_of, report->orig_index.dirs);
  arch_make_changeset_compute_container_map (&report->orig_container_dir_id_of_file_id, report->orig_dir_id_of, report->orig_index.files);
  arch_make_changeset_compute_container_map (&report->mod_container_dir_id_of_dir_id, report->mod_dir_id_of, report->mod_index.dirs);
  arch_make_changeset_compute_container_map (&report->mod_container_dir_id_of_file_id, report->mod_dir_id_of, report->mod_index.files);

  arch_make_empty_changeset (report, &csr, dest);

  for (x = 0; x < rel_n_records (file_list); ++x)
    {
      t_uchar * id = 0;
      int orig_index;
      int mod_index;
      t_uchar * orig_path = 0;
      t_uchar * mod_path = 0;
      int diff_fd;

      if (overide_untagged_source)
        {
          prior_untagged_source = orig->untagged_is_source;
          orig->untagged_is_source = untagged_source_category == arch_inventory_source;
        }
      if (overide_method)
        {
          prior_method = orig->tag_method;
          orig->tag_method = method;
        }
      id = arch_inventory_id (orig, file_list[x][0], 0);
      if (overide_untagged_source)
          orig->untagged_is_source = prior_untagged_source;
      if (overide_method)
          orig->tag_method = prior_method;

      orig_index = find_id_in_inventory (report->orig_index.files, id);
      mod_index = find_id_in_inventory (report->mod_index.files, id);

      if (orig_index < 0)
        {
          safe_printfmt (2, "make-changeset-files: file missing from ORIG tree (%s)\n", file_list[x][0]);
          exit (1);
        }

      if (mod_index < 0)
        {
          safe_printfmt (2, "make-changeset-files: file missing from MOD tree (%s)\n", file_list[x][0]);
          exit (1);
        }

      double_check_file_identity (file_list[x][0], mod->root, report->mod_index.files[mod_index][0]);

      orig_path = file_name_in_vicinity (0, orig->root, report->orig_index.files[orig_index][0]);
      mod_path = file_name_in_vicinity (0, mod->root, report->mod_index.files[mod_index][0]);

      if (arch_binary_files_differ (orig_path, mod_path, 0, 0))
        {
          int diff_stat; 
          t_uchar * escaped_tmp;

          escaped_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes, file_list[x][0]);
          invoke_report_callback (report, "M  %s\n", escaped_tmp);
          diff_fd = arch_changeset_add_diffs (&csr, report, dest, report->orig_index.files[orig_index][0], report->mod_index.files[mod_index][0], id);
          diff_stat = arch_invoke_diff (diff_fd, orig_path, report->orig_index.files[orig_index][0], mod_path, report->orig_index.files[orig_index][0], 0, 0);
          safe_close (diff_fd);

          lim_free (0, escaped_tmp);

          if (diff_stat == 1)
            {
              t_uchar diff_pseudo_magic[sizeof ("binary files ")];
              t_uchar * patches_path = 0;
              t_uchar * patch_path = 0;
              int diff_in_fd;
              long amt;

              patches_path = file_name_in_vicinity (0, dest, "patches");
              patch_path = file_name_in_vicinity (0, patches_path, report->mod_index.files[mod_index][0]);
              patch_path = str_realloc_cat (0, patch_path, ".patch");

              diff_in_fd = safe_open (patch_path, O_RDONLY, 0);

              /* diff might have just said "binary files differ" or something
               * similar.
               */
              amt = safe_read (diff_in_fd, diff_pseudo_magic, sizeof (diff_pseudo_magic) - 1);

              if (amt > 0)
                {
                  diff_pseudo_magic[amt] = 0;
                  if (!str_casecmp (diff_pseudo_magic, "binary files "))
                    diff_stat = 2;
                }
              safe_close (diff_in_fd);
              lim_free (0, patch_path);
              lim_free (0, patches_path);
            }

          if (diff_stat == 2)
            {
              safe_printfmt (2, "binary files not yet supported with --files or --file-list\n");
              exit (2);
            }
          
        }

      lim_free (0, id);
      lim_free (0, orig_path);
      lim_free (0, mod_path);
    }

  arch_changeset_rewrite_indexes (dest, &csr);
  arch_free_changeset_report_data (&csr);
}




static int
find_id_in_inventory (rel_table invent, t_uchar * id)
{
  int x;

  for (x = 0; x < rel_n_records (invent); ++x)
    {
      if (!str_cmp (id, invent[x][1]))
        return x;
    }

  return -1;
}


static void
double_check_file_identity (t_uchar * from_here_path, t_uchar * tree_root, t_uchar * loc)
{
  struct stat users_stat;
  t_uchar * computed_path = 0;
  struct stat computed_stat;

  safe_stat (from_here_path, &users_stat);
  computed_path = file_name_in_vicinity (0, tree_root, loc);
  safe_stat (computed_path, &computed_stat);

  lim_free (0, computed_path);
  if ((users_stat.st_dev != computed_stat.st_dev) || (users_stat.st_ino != computed_stat.st_ino))
    {
      safe_printfmt (2, "make-changeset --files: the file specified as %s\n  is not the same as the one in the project tree (%s)\n", from_here_path, computed_path);
      exit (1);
    }
}




/* tag: Tom Lord Tue Jun 17 21:39:34 2003 (make-changeset-files.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1