/* build-revision.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/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "tla/libfsutils/copy-file.h"
#include "tla/libfsutils/dir-listing.h"
#include "tla/libfsutils/tmp-files.h"
#include "tla/libfsutils/rmrf.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/libraries.h"
#include "tla/libarch/archive.h"
#include "tla/libarch/apply-changeset.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/pristines.h"
#include "tla/libarch/invent.h"
#include "tla/libarch/inode-sig.h"
#include "tla/libarch/chatter.h"
#include "tla/libarch/build-revision.h"




/* return non-0 if it came from a library.
 */
int
arch_build_revision (int chatter_fd,
                     const t_uchar * dest_dir,
                     struct arch_archive * arch,
                     const t_uchar * archive,
                     const t_uchar * revision,
                     const t_uchar * cache_dir)
{
  struct arch_archive * my_arch = 0;
  t_uchar * library_rev_dir = 0;
  int is_built = 0;
  int from_library = 0;

  library_rev_dir = arch_library_find (rel_table_nil, archive, revision, 1);

  /****************************************************************
   * Take it from a revision library, if we can.
   */
  if (!is_built && library_rev_dir)
    {
      rel_table index_by_name = rel_table_nil;
      const int full_meta = arch_tree_has_meta_flag (library_rev_dir);

      arch_chatter (chatter_fd, "* from revision library: %s/%s\n", archive, revision);

      index_by_name = arch_library_index (archive, revision);
      rel_sort_table_by_field (0, index_by_name, 0);
      copy_file_list (dest_dir, library_rev_dir, index_by_name, full_meta);

      rel_free_table (index_by_name);

      is_built = 1;
      from_library = 1;
    }

  /****************************************************************
   * Next, try the pristine cache.
   */
  if (!is_built && cache_dir)
    {
      t_uchar * pristine_copy = 0;

      pristine_copy = arch_find_pristine (0, cache_dir, archive, revision, arch_any_pristine, arch_cache_dir_pristine_search);

      if (pristine_copy)
        {
          rel_table file_list = rel_table_nil;
          const int full_meta = arch_tree_has_meta_flag (pristine_copy);

          arch_chatter (chatter_fd, "* from pristine cache: %s/%s\n", archive, revision);

          file_list = arch_source_inventory (pristine_copy, 1, 0, 0);
          copy_file_list (dest_dir, pristine_copy, file_list, full_meta);
          is_built = 1;

          rel_free_table (file_list);
        }

      lim_free (0, pristine_copy);
    }

  /****************************************************************
   * Ok, have to hit up the archive.
   */
  if (!is_built)
    {
      enum arch_revision_type type;
      int is_cached;

      if (!arch)
        {
          my_arch = arch_archive_connect (archive, 0);
          arch = my_arch;
        }

      arch_revision_type (&type, &is_cached, arch, revision);

      /********************************
       * Archive Cached Revision?
       */
      if (is_cached)
        {
          t_uchar * tmpdir = 0;
          rel_table files = rel_table_nil;
          int x;

          arch_chatter (chatter_fd, "* from archive cached: %s/%s\n", archive, revision);

          tmpdir = tmp_file_name (dest_dir, ",,cached-revision");
          arch_get_cached_revision (arch, revision, tmpdir);
          files = directory_files (tmpdir);

          for (x = 0; x < rel_n_records (files); ++x)
            {
              if (str_cmp (".", rel_peek_str (files, x, 0)) && str_cmp ("..", rel_peek_str (files, x, 0)))
                {
                  t_uchar * frm = 0;
                  t_uchar * to = 0;

                  frm = file_name_in_vicinity (0, tmpdir, rel_peek_str (files, x, 0));
                  to = file_name_in_vicinity (0, dest_dir, rel_peek_str (files, x, 0));

                  safe_rename (frm, to);

                  lim_free (0, frm);
                  lim_free (0, to);
                }
            }

          safe_rmdir (tmpdir);

          lim_free (0, tmpdir);
          rel_free_table (files);
        }

      /********************************
       * Import Revision?
       */
      else if (type == arch_import_revision)
        {
          t_uchar * tmpdir = 0;
          rel_table files = rel_table_nil;
          int x;

          arch_chatter (chatter_fd, "* from import revision: %s/%s\n", archive, revision);

          tmpdir = tmp_file_name (dest_dir, ",,import-revision");
          arch_get_import_revision (arch, revision, tmpdir);
          files = directory_files (tmpdir);

          for (x = 0; x < rel_n_records (files); ++x)
            {
              if (str_cmp (".", rel_peek_str (files, x, 0)) && str_cmp ("..", rel_peek_str (files, x, 0)))
                {
                  t_uchar * frm = 0;
                  t_uchar * to = 0;

                  frm = file_name_in_vicinity (0, tmpdir, rel_peek_str (files, x, 0));
                  to = file_name_in_vicinity (0, dest_dir, rel_peek_str (files, x, 0));

                  safe_rename (frm, to);

                  lim_free (0, frm);
                  lim_free (0, to);
                }
            }

          safe_rmdir (tmpdir);

          lim_free (0, tmpdir);
          rel_free_table (files);
        }
      else
        {
          /********************************
           * Recurse and Build by Patching
           */

          t_uchar * prev_revision = 0;
          t_uchar * prev_archive = 0;
          struct arch_archive * prev_arch;

          if (type == arch_simple_revision)
            {
              prev_revision = arch_previous_revision (arch, revision);
              invariant (!!prev_revision);
              prev_archive = str_save (0, archive);
              prev_arch = arch;
            }
          else
            {
              t_uchar * fq_prev_revision = 0;

              fq_prev_revision = arch_get_continuation (arch, revision);

              if (!arch_valid_package_name (fq_prev_revision, arch_req_archive, arch_req_patch_level, 0))
                {
                  safe_printfmt (2, "arch_build_revision: archive contains bogus CONTINUATION file\n  archive: %s\n  revision: %s\n",
                                 archive, revision);
                  exit (2);
                }

              prev_archive = arch_parse_package_name (arch_ret_archive, 0, fq_prev_revision);
              prev_revision = arch_parse_package_name (arch_ret_non_archive, 0, fq_prev_revision);

              if (!str_cmp (archive, prev_revision))
                {
                  prev_arch = arch;
                }
              else
                {
                  prev_arch = arch_archive_connect (prev_archive, 0);
                }

              lim_free (0, fq_prev_revision);
            }


          arch_build_revision (chatter_fd, dest_dir, prev_arch, prev_archive, prev_revision, cache_dir);

          arch_chatter (chatter_fd, "* patching for revision: %s/%s\n", archive, revision);

          {
            t_uchar * tmppatch = 0;
            struct arch_apply_changeset_report r;

            tmppatch = tmp_file_name (dest_dir, ",,next-patch");
            arch_get_patch (arch, revision, tmppatch);

            mem_set0 ((t_uchar *)&r, sizeof (r));

            arch_apply_changeset (&r, tmppatch, dest_dir, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, 0, 0, 0, 0, 0);

            if (arch_conflicts_occured (&r))
              panic ("conflict applying patch in arch_build_revision");

            arch_free_apply_changeset_report_data (&r);

            rmrf_file (tmppatch);
            lim_free (0, tmppatch);
          }

          if (arch != prev_arch)
            arch_archive_close (prev_arch);

          lim_free (0, prev_archive);
          lim_free (0, prev_revision);
        }
    }

  arch_snap_inode_sig (dest_dir, archive, revision);

  lim_free (0, library_rev_dir);
  arch_archive_close (my_arch);
  return from_library;
}





/* tag: Tom Lord Wed May 21 15:59:22 2003 (build-revision.c)
 */
