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



void
arch_make_pristine (const t_uchar * tree_root,
                    const t_uchar * archive,
                    const t_uchar * revision)
{
  rel_table inventory = rel_table_nil;
  t_uchar * tmp_pristine = 0;
  const int full_meta = arch_tree_has_meta_flag (tree_root);

  inventory = arch_source_inventory (tree_root, 1, 0, 0);
  tmp_pristine = tmp_file_name (tree_root, ",,pristine");
  rmrf_file (tmp_pristine);
  safe_mkdir (tmp_pristine, 0777);
  copy_file_list (tmp_pristine, tree_root, inventory, full_meta);

  arch_install_pristine (tree_root, archive, revision, tmp_pristine);

  rel_free_table (inventory);
  lim_free (0, tmp_pristine);
}


void
arch_add_pristine (int chatter_fd,
                   const t_uchar * tree_root,
                   struct arch_archive * arch,
                   const t_uchar * archive,
                   const t_uchar * revision)
{
  t_uchar * tmp_path = 0;
  t_uchar * cache_dir = 0;

  tmp_path = tmp_file_name (tree_root, ",,new-pristine");
  safe_mkdir (tmp_path, 0777);

  cache_dir = file_name_directory_file (0, tree_root);
  arch_build_revision (chatter_fd, tmp_path, arch, archive, revision, cache_dir);
  arch_install_pristine (tree_root, archive, revision, tmp_path);

  lim_free (0, tmp_path);
  lim_free (0, cache_dir);
}


t_uchar *
arch_pristine_loc (const t_uchar * archive,
                   const t_uchar * revision,
                   int locked_p)
{
  t_uchar * category = 0;
  t_uchar * branch = 0;
  t_uchar * version = 0;
  t_uchar * answer = 0;

  invariant (arch_valid_package_name (revision, arch_no_archive, arch_req_patch_level, 0));
  invariant (arch_valid_archive_name (archive));

  category = arch_parse_package_name (arch_ret_category, 0, revision);
  branch = arch_parse_package_name (arch_ret_package, 0, revision);
  version = arch_parse_package_name (arch_ret_package_version, 0, revision);

  answer = str_alloc_cat_many (0, "{arch}/++pristine-trees/", (locked_p ? "locked" : "unlocked"), "/", category, "/", branch, "/", version, "/", archive, "/", revision, str_end);

  lim_free (0, category);
  lim_free (0, branch);
  lim_free (0, version);

  return answer;
}


t_uchar *
arch_pristine_path (const t_uchar * tree_root,
                    const t_uchar * archive,
                    const t_uchar * revision,
                    int locked_p)
{
  t_uchar * loc = 0;
  t_uchar * answer = 0;

  loc = arch_pristine_loc (archive, revision, locked_p);
  answer = file_name_in_vicinity (0, tree_root, loc);

  lim_free (0, loc);
  return answer;
}


void
arch_install_pristine (const t_uchar * tree_root,
                       const t_uchar * archive,
                       const t_uchar * revision,
                       const t_uchar * source)
{
  t_uchar * loc = 0;
  t_uchar * path = 0;

  arch_snap_inode_sig (source, archive, revision);

  loc = arch_pristine_loc (archive, revision, 0);
  path = file_name_in_vicinity (0, tree_root, loc);

  ensure_directory_exists (path);
  rmrf_file (path);
  safe_rename (source, path);

  lim_free (0, loc);
  lim_free (0, path);
}


void
arch_lock_pristine (const t_uchar * tree_root,
                    const t_uchar * archive,
                    const t_uchar * revision)
{
  t_uchar * locked_path = 0;
  t_uchar * unlocked_path = 0;

  locked_path = arch_pristine_path (tree_root, archive, revision, 1);
  unlocked_path = arch_pristine_path (tree_root, archive, revision, 0);

  if (safe_access (locked_path, F_OK))
    {
      t_uchar * locked_dir = 0;

      if (safe_access (unlocked_path, F_OK))
        {
          safe_printfmt (2, "arch_lock_pristine: unlocked pristine tree not found for %s/%s\n", archive, revision);
          exit (2);
        }

      locked_dir = file_name_directory_file (0, locked_path);
      ensure_directory_exists (locked_dir);
      safe_rename (unlocked_path, locked_path);

      lim_free (0, locked_dir);
    }

  lim_free (0, locked_path);
  lim_free (0, unlocked_path);
}


void
arch_unlock_pristine (const t_uchar * tree_root,
                      const t_uchar * archive,
                      const t_uchar * revision)
{
  t_uchar * locked_path = 0;
  t_uchar * unlocked_path = 0;

  locked_path = arch_pristine_path (tree_root, archive, revision, 1);
  unlocked_path = arch_pristine_path (tree_root, archive, revision, 0);

  if (safe_access (unlocked_path, F_OK))
    {
      t_uchar * unlocked_dir = 0;

      if (safe_access (locked_path, F_OK))
        {
          safe_printfmt (2, "arch_unlock_pristine: locked pristine tree not found for %s/%s\n", archive, revision);
          exit (2);
        }

      unlocked_dir = file_name_directory_file (0, unlocked_path);
      ensure_directory_exists (unlocked_dir);
      safe_rename (locked_path, unlocked_path);

      lim_free (0, unlocked_dir);
    }

  lim_free (0, locked_path);
  lim_free (0, unlocked_path);
}


rel_table
arch_pristines (const t_uchar * tree_root,
                const t_uchar * archive_limit,
                const t_uchar * limit,
                int pristine_types)
{
  int here_fd;
  int errn;
  struct stat statb;
  t_uchar * category = 0;
  t_uchar * branch = 0;
  t_uchar * version = 0;
  const t_uchar * revision = 0;
  t_uchar * locked_in_this_tree_path = 0;
  t_uchar * unlocked_in_this_tree_path = 0;
  rel_table answer = rel_table_nil;
  int x;

  here_fd = safe_open (".", O_RDONLY, 0);

  if (archive_limit)
    invariant (arch_valid_archive_name (archive_limit));

  if (limit)
    {
      invariant (arch_valid_package_name (limit, arch_no_archive, arch_req_category, 1));

      category = arch_parse_package_name (arch_ret_category, 0, limit);

      if (arch_valid_package_name (limit, arch_no_archive, arch_req_package, 1))
        {
          branch = arch_parse_package_name (arch_ret_package, 0, limit);

          if (arch_valid_package_name (limit, arch_no_archive, arch_req_version, 1))
            {
              version = arch_parse_package_name (arch_ret_package_version, 0, limit);

              if (arch_valid_package_name (limit, arch_no_archive, arch_req_patch_level, 0))
                {
                  revision = limit;
                }
            }
        }
    }

  unlocked_in_this_tree_path = file_name_in_vicinity (0, tree_root, "{arch}/++pristine-trees/unlocked");
  locked_in_this_tree_path = file_name_in_vicinity (0, tree_root, "{arch}/++pristine-trees/locked");

  for (x = arch_unlocked_pristine; x != 0; (x = ((x == arch_unlocked_pristine) ? arch_locked_pristine : 0)))
    {
      if (x & pristine_types)
        {
          switch (x)
            {
            case arch_unlocked_pristine:
              {
                if (vu_chdir (&errn, unlocked_in_this_tree_path))
                  continue;
                break;
              }
            case arch_locked_pristine:
              {
                if (vu_chdir (&errn, locked_in_this_tree_path))
                  continue;
                break;
              }
            default:
              {
                panic ("not reached in arch_pristines");
              }
            }

          {
            rel_table maybe_categories = rel_table_nil;
            int c;

            if (category)
              {
                rel_add_records (&maybe_categories, rel_singleton_record_taking (rel_make_field_str (category)), rel_record_null);
              }
            else
              {
                maybe_categories = directory_files (".");
              }


            for (c = 0; c < rel_n_records (maybe_categories); ++c)
              {

                if (arch_valid_package_name (rel_peek_str (maybe_categories, c, 0), arch_no_archive, arch_req_category, 0)
                    && (!vu_lstat (&errn, rel_peek_str (maybe_categories, c, 0), &statb) && S_ISDIR (statb.st_mode)))
                  {
                    rel_table maybe_branches = rel_table_nil;
                    int b;

                    safe_chdir (rel_peek_str (maybe_categories, c, 0));

                    if (branch)
                      {
                        rel_add_records (&maybe_branches, rel_singleton_record_taking (rel_make_field_str (branch)), rel_record_null);
                      }
                    else
                      {
                        maybe_branches = directory_files (".");
                      }

                    for (b = 0; b < rel_n_records (maybe_branches); ++b)
                      {

                        if (arch_valid_package_name (rel_peek_str (maybe_branches, b, 0), arch_no_archive, arch_req_package, 0)
                            && (!vu_lstat (&errn, rel_peek_str (maybe_branches, b, 0), &statb) && S_ISDIR (statb.st_mode)))
                          {
                            rel_table maybe_versions = rel_table_nil;
                            int v;

                            safe_chdir (rel_peek_str (maybe_branches, b, 0));

                            if (version)
                              {
                                rel_add_records (&maybe_versions, rel_singleton_record_taking (rel_make_field_str (version)), rel_record_null);
                              }
                            else
                              {
                                maybe_versions = directory_files (".");
                              }

                            for (v = 0; v < rel_n_records (maybe_versions); ++v)
                              {
                                if (arch_valid_package_name (rel_peek_str (maybe_versions, v, 0), arch_no_archive, arch_req_version, 0)
                                    && (!vu_lstat (&errn, rel_peek_str (maybe_versions, v, 0), &statb) && S_ISDIR (statb.st_mode)))
                                  {
                                    rel_table maybe_archives = rel_table_nil;
                                    int a;

                                    safe_chdir (rel_peek_str (maybe_versions, v, 0));

                                    if (archive_limit)
                                      {
                                        rel_add_records (&maybe_archives, rel_singleton_record_taking (rel_make_field_str (archive_limit)), rel_record_null);
                                      }
                                    else
                                      {
                                        maybe_archives = directory_files (".");
                                      }

                                    for (a = 0; a < rel_n_records (maybe_archives); ++a)
                                      {
                                        if (arch_valid_archive_name (rel_peek_str (maybe_archives, a, 0)) && (!vu_lstat (&errn, rel_peek_str (maybe_archives, a, 0), &statb) && S_ISDIR (statb.st_mode)))
                                          {
                                            rel_table maybe_revisions = rel_table_nil;
                                            int r;

                                            safe_chdir (rel_peek_str (maybe_archives, a, 0));

                                            if (revision)
                                              {
                                                rel_add_records (&maybe_revisions, rel_singleton_record_taking (rel_make_field_str (revision)), rel_record_null);
                                              }
                                            else
                                              {
                                                maybe_revisions = directory_files (".");
                                              }

                                            for (r = 0; r < rel_n_records (maybe_revisions); ++r)
                                              {
                                                if (arch_valid_package_name (rel_peek_str (maybe_revisions, r, 0), arch_no_archive, arch_req_patch_level, 0)
                                                    && (!vu_lstat (&errn, rel_peek_str (maybe_revisions, r, 0), &statb) && S_ISDIR (statb.st_mode)))
                                                  {
                                                    t_uchar * fqr = 0;

                                                    fqr = arch_fully_qualify (rel_peek_str (maybe_archives, a, 0), rel_peek_str (maybe_revisions, r, 0));
                                                    rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (fqr)), rel_record_null);
                                                    lim_free (0, fqr);
                                                  }
                                              }

                                            safe_chdir ("..");
                                            rel_free_table (maybe_revisions);
                                          }
                                      }

                                    safe_chdir ("..");
                                    rel_free_table (maybe_archives);
                                  }
                              }

                            safe_chdir ("..");
                            rel_free_table (maybe_versions);
                          }
                      }

                    safe_chdir ("..");
                    rel_free_table (maybe_branches);
                  }
              }

            safe_fchdir (here_fd);
            rel_free_table (maybe_categories);
          }
        }
    }

  arch_sort_table_by_name_field (0, answer, 0);

  safe_close (here_fd);

  lim_free (0, category);
  lim_free (0, branch);
  lim_free (0, version);
  lim_free (0, locked_in_this_tree_path);
  lim_free (0, unlocked_in_this_tree_path);

  return answer;
}


t_uchar *
arch_find_pristine (int * is_locked,
                    const t_uchar * tree_root_or_root_dir,
                    const t_uchar * archive,
                    const t_uchar * revision,
                    int pristine_types,
                    enum arch_pristine_search_scope scope)
{
  const t_uchar * locked_pristine_stem = "{arch}/++pristine-trees/locked";
  const t_uchar * unlocked_pristine_stem = "{arch}/++pristine-trees/unlocked";
  t_uchar * category = 0;
  t_uchar * branch = 0;
  t_uchar * version = 0;
  t_uchar * locked_relpath = 0;
  t_uchar * unlocked_relpath = 0;
  t_uchar * locked_in_this_tree_path = 0;
  t_uchar * unlocked_in_this_tree_path = 0;
  t_uchar * answer = 0;

  invariant (arch_valid_archive_name (archive));
  invariant (arch_valid_package_name (revision, arch_no_archive, arch_req_patch_level, 0));

  category = arch_parse_package_name (arch_ret_category, 0, revision);
  branch = arch_parse_package_name (arch_ret_package, 0, revision);
  version = arch_parse_package_name (arch_ret_package_version, 0, revision);

  locked_relpath = str_alloc_cat_many (0, locked_pristine_stem, "/", category, "/", branch, "/", version, "/", archive, "/", revision, str_end);
  unlocked_relpath = str_alloc_cat_many (0, unlocked_pristine_stem, "/", category, "/", branch, "/", version, "/", archive, "/", revision, str_end);

  locked_in_this_tree_path = file_name_in_vicinity (0, tree_root_or_root_dir, locked_relpath);
  unlocked_in_this_tree_path = file_name_in_vicinity (0, tree_root_or_root_dir, unlocked_relpath);

  if (((scope == arch_tree_pristine_search) || (scope == arch_tree_and_sibling_pristine_search))
      && (pristine_types & arch_unlocked_pristine) && !safe_access (unlocked_in_this_tree_path, F_OK))
    {
      answer = str_save (0, unlocked_in_this_tree_path);
      if (is_locked)
        *is_locked = 0;
    }
  else
    {
      t_uchar * tree_root_dir = 0;
      rel_table sibling_files = rel_table_nil;
      rel_table sibling_paths = rel_table_nil;
      int x;

      if (scope == arch_cache_dir_pristine_search)
        tree_root_dir = str_save (0, tree_root_or_root_dir);
      else
        {
          tree_root_dir = file_name_directory_file (0, tree_root_or_root_dir);
          if (!tree_root_dir)
            tree_root_dir = str_save (0, ".");
        }
      sibling_files = directory_files (tree_root_dir);

      if (scope != arch_tree_pristine_search)
        {
          for (x = 0; x < rel_n_records (sibling_files); ++x)
            {
              if (char_is_alnum (rel_peek_str (sibling_files, x, 0)[0]))
                {
                  int ign;
                  struct stat stat_buf;
                  t_uchar * path = 0;

                  path = file_name_in_vicinity (0, tree_root_dir, rel_peek_str (sibling_files, x, 0));

                  if (!vu_lstat (&ign, path, &stat_buf) && S_ISDIR (stat_buf.st_mode))
                    {
                      rel_add_records (&sibling_paths, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
                    }
                  lim_free (0, path);
                }
            }

          if (pristine_types & arch_unlocked_pristine)
            {
              for (x = 0; !answer && (x < rel_n_records (sibling_paths)); ++x)
                {
                  t_uchar * unlocked_sibling_path = 0;
                  int errno = 0;

                  unlocked_sibling_path = file_name_in_vicinity (0, rel_peek_str (sibling_paths, x, 0), unlocked_relpath);

                  if (!vu_access (&errno, unlocked_sibling_path, F_OK))
                    {
                      answer = str_save (0, unlocked_sibling_path);
                      if (is_locked)
                        *is_locked = 0;
                    }

                  lim_free (0, unlocked_sibling_path);
                }
            }
        }

      if (pristine_types & arch_locked_pristine)
        {
          if ((scope != arch_cache_dir_pristine_search) && !answer && (!safe_access (locked_in_this_tree_path, F_OK)))
            {
              answer = str_save (0, locked_in_this_tree_path);
              if (is_locked)
                *is_locked = 1;
            }

          if (scope != arch_tree_pristine_search)
            {
              for (x = 0; !answer && (x < rel_n_records (sibling_paths)); ++x)
                {
                  t_uchar * locked_sibling_path = 0;
                  int errno = 0;

                  locked_sibling_path = file_name_in_vicinity (0, rel_peek_str (sibling_paths, x, 0), locked_relpath);

                  if (!vu_access (&errno, locked_sibling_path, F_OK))
                    {
                      answer = str_save (0, locked_sibling_path);
                      if (is_locked)
                        *is_locked = 1;
                    }

                  lim_free (0, locked_sibling_path);
                }
            }
        }

      lim_free (0, tree_root_dir);
      rel_free_table (sibling_files);
      rel_free_table (sibling_paths);
    }

  lim_free (0, category);
  lim_free (0, branch);
  lim_free (0, version);
  lim_free (0, locked_relpath);
  lim_free (0, unlocked_relpath);
  lim_free (0, locked_in_this_tree_path);
  lim_free (0, unlocked_in_this_tree_path);


  if (answer && !arch_valid_inode_sig (answer, archive, revision))
    {
      safe_printfmt (2, "corrupt pristine (failed inode signature validation)\n    archive: %s\n    revision: %s\n    directory %s\nYou should remove this pristine from your tree.\n",
                     archive, revision, answer);
      exit (2);
    }

  return answer;
}




/* tag: Tom Lord Wed May 21 18:53:22 2003 (pristines.c)
 */
