/* libraries.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/exception.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/dir-listing.h"
#include "libfsutils/same.h"
#include "libarch/my.h"
#include "libarch/namespace.h"
#include "libarch/patch-logs.h"
#include "libarch/changeset-utils.h"
#include "libarch/libraries.h"
#include "libarch/project-tree.h"
#include "libarch/inode-sig.h"


/* __STDC__ prototypes for static functions */





rel_table
arch_library_archive_dirs (rel_table opt_lib_path,
                           t_uchar const * const archive,
                           int for_add)
{
  rel_table lib_path = 0;
  rel_table answer = 0;
  int x;

  invariant (arch_valid_archive_name (archive));

  if (opt_lib_path)
    {
      lib_path = rel_copy_table (opt_lib_path);
    }
  else
    {
      lib_path = arch_my_library_path (for_add ? arch_library_path_add_order : arch_library_path_search_order);
      if (!lib_path)
        return 0;
    }

  for (x = 0; x < rel_n_records (lib_path); ++x)
    {
      t_uchar * lib_dir;
      t_uchar * maybe_archive_dir = 0;

      lib_dir = lib_path[x][0];
      maybe_archive_dir = file_name_in_vicinity (0, lib_dir, archive);

      if (!safe_access (maybe_archive_dir, F_OK))
        {
          rel_add_records (&answer, rel_make_record (maybe_archive_dir, 0), 0);
        }

      lim_free (0, maybe_archive_dir);
    }

  if (for_add && !answer)
    {
      t_uchar * priority_lib_dir;
      t_uchar * archive_dir = 0;

      priority_lib_dir = lib_path[0][0];
      archive_dir = file_name_in_vicinity (0, priority_lib_dir, archive);
      rel_add_records (&answer, rel_make_record (archive_dir, 0), 0);

      lim_free (0, archive_dir);
    }

  rel_free_table (lib_path);
  return answer;
}


rel_table
arch_library_category_dirs (rel_table opt_lib_path,
                            t_uchar const * const archive,
                            t_uchar const * const category,
                            int for_add)
{
  rel_table answer = 0;
  rel_table archive_dirs = 0;
  int x;

  invariant (arch_valid_package_name (category, arch_no_archive, arch_req_category, 0));

  archive_dirs = arch_library_archive_dirs (opt_lib_path, archive, for_add);
  if (!archive_dirs)
    return 0;

  for (x = 0; x < rel_n_records (archive_dirs); ++x)
    {
      t_uchar * archive_dir;
      t_uchar * maybe_category_dir = 0;

      archive_dir = archive_dirs[x][0];
      maybe_category_dir = file_name_in_vicinity (0, archive_dir, category);

      if (!safe_access (maybe_category_dir, F_OK))
        {
          rel_add_records (&answer, rel_make_record (maybe_category_dir, 0), 0);
        }

      lim_free (0, maybe_category_dir);
    }

  if (for_add && !answer)
    {
      t_uchar * priority_archive_dir;
      t_uchar * category_dir = 0;

      priority_archive_dir = archive_dirs[0][0];
      category_dir = file_name_in_vicinity (0, priority_archive_dir, category);
      rel_add_records (&answer, rel_make_record (category_dir, 0), 0);

      lim_free (0, category_dir);
    }

  rel_free_table (archive_dirs);

  return answer;
}


rel_table
arch_library_branch_dirs (rel_table opt_lib_path,
                          t_uchar const * const archive,
                          t_uchar const * const branch,
                          int for_add)
{
  rel_table answer = 0;
  t_uchar * category = 0;
  rel_table category_dirs = 0;
  int x;

  invariant (arch_valid_package_name (branch, arch_no_archive, arch_req_package, 0));

  category = arch_parse_package_name (arch_ret_category, 0, branch);

  category_dirs = arch_library_category_dirs (opt_lib_path, archive, category, for_add);
  if (!category_dirs)
    return 0;

  for (x = 0; x < rel_n_records (category_dirs); ++x)
    {
      t_uchar * category_dir;
      t_uchar * maybe_branch_dir = 0;

      category_dir = category_dirs[x][0];
      maybe_branch_dir = file_name_in_vicinity (0, category_dir, branch);

      if (!safe_access (maybe_branch_dir, F_OK))
        {
          rel_add_records (&answer, rel_make_record (maybe_branch_dir, 0), 0);
        }

      lim_free (0, maybe_branch_dir);
    }

  if (for_add && !answer)
    {
      t_uchar * priority_category_dir;
      t_uchar * branch_dir = 0;

      priority_category_dir = category_dirs[0][0];
      branch_dir = file_name_in_vicinity (0, priority_category_dir, branch);
      rel_add_records (&answer, rel_make_record (branch_dir, 0), 0);

      lim_free (0, branch_dir);
    }

  rel_free_table (category_dirs);
  lim_free (0, category);

  return answer;
}


rel_table
arch_library_version_dirs (rel_table opt_lib_path,
                           t_uchar const * const archive,
                           t_uchar const * const version,
                           int for_add)
{
  rel_table answer = 0;
  t_uchar * branch = 0;
  rel_table branch_dirs = 0;
  int x;

  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));

  branch = arch_parse_package_name (arch_ret_package, 0, version);

  branch_dirs = arch_library_branch_dirs (opt_lib_path, archive, branch, for_add);
  if (!branch_dirs)
    return 0;

  for (x = 0; x < rel_n_records (branch_dirs); ++x)
    {
      t_uchar * branch_dir;
      t_uchar * maybe_version_dir = 0;

      branch_dir = branch_dirs[x][0];
      maybe_version_dir = file_name_in_vicinity (0, branch_dir, version);

      if (!safe_access (maybe_version_dir, F_OK))
        {
          rel_add_records (&answer, rel_make_record (maybe_version_dir, 0), 0);
        }

      lim_free (0, maybe_version_dir);
    }

  if (for_add && !answer)
    {
      t_uchar * priority_branch_dir;
      t_uchar * version_dir = 0;

      priority_branch_dir = branch_dirs[0][0];
      version_dir = file_name_in_vicinity (0, priority_branch_dir, version);
      rel_add_records (&answer, rel_make_record (version_dir, 0), 0);

      lim_free (0, version_dir);
    }

  rel_free_table (branch_dirs);
  lim_free (0, branch);

  return answer;
}


rel_table
arch_library_revision_dirs (rel_table opt_lib_path,
                            t_uchar * archive,
                            t_uchar * revision,
                            int for_add)
{
  rel_table answer = 0;
  t_uchar * version = 0;
  rel_table version_dirs = 0;
  int x;

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

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);

  version_dirs = arch_library_version_dirs (opt_lib_path, archive, version, for_add);
  if (!version_dirs)
    return 0;

  for (x = 0; x < rel_n_records (version_dirs); ++x)
    {
      t_uchar * version_dir;
      t_uchar * maybe_revision_dir = 0;

      version_dir = version_dirs[x][0];
      maybe_revision_dir = file_name_in_vicinity (0, version_dir, revision);

      if (!safe_access (maybe_revision_dir, F_OK))
        {
          rel_add_records (&answer, rel_make_record (maybe_revision_dir, 0), 0);
        }

      lim_free (0, maybe_revision_dir);
    }

  if (for_add && !answer)
    {
      t_uchar * priority_version_dir;
      t_uchar * revision_dir = 0;

      priority_version_dir = version_dirs[0][0];
      revision_dir = file_name_in_vicinity (0, priority_version_dir, revision);
      rel_add_records (&answer, rel_make_record (revision_dir, 0), 0);

      lim_free (0, revision_dir);
    }

  rel_free_table (version_dirs);
  lim_free (0, version);

  return answer;
}



arch_project_tree_t *
arch_library_find (rel_table opt_lib_path,
		   arch_patch_id *patch_id,
                   int check_inode_sigs)
{
  static assoc_table validated_trees = NULL;
  rel_table revision_dirs = 0;
  arch_project_tree_t * answer = NULL;

  revision_dirs = arch_library_revision_dirs (opt_lib_path, arch_patch_id_archive(patch_id), arch_patch_id_revision(patch_id), 0);
  if (rel_n_records(revision_dirs) == 0)
    return 0;

  invariant (!safe_access (revision_dirs[0][0], F_OK));
  
    {
      t_uchar * tree_version = arch_tree_version (revision_dirs[0][0]);
      if (str_cmp (tree_version, arch_patch_id_branch (patch_id)))
	  arch_set_tree_version (revision_dirs[0][0], patch_id);
      lim_free (0, tree_version);
    }

  answer = arch_project_tree_new_exact (talloc_context, revision_dirs[0][0]);
  if (check_inode_sigs && !assoc_ref (validated_trees, answer->root))
    {
      if (!arch_valid_inode_sig (answer, arch_patch_id_archive(patch_id), arch_patch_id_revision(patch_id)))
	{
	  safe_printfmt (2, "corrupt library (failed inode signature validation)\n    revision: %s\nYou should remove this revision from your library.\n",
			 arch_patch_id_patch_id(patch_id));
	  exit (2);
	}
      assoc_set (&validated_trees, answer->root, arch_patch_id_patch_id(patch_id));
    }

  rel_free_table (revision_dirs);

  return answer;
}



t_uchar *
arch_library_find_file (t_uchar * archive, t_uchar * revision, t_uchar * loc)
{
  arch_project_tree_t * revision_tree;
  t_uchar * file_path = 0;
  arch_patch_id patch_id;

  arch_patch_id_init_archive (&patch_id, archive, revision);
  revision_tree = arch_library_find (0, &patch_id, 1);
  arch_patch_id_finalise (&patch_id);

  if (revision_tree)
    {
      file_path = file_name_in_vicinity (0, revision_tree->root, loc);

      if (safe_access (file_path, F_OK))
        {
          lim_free (0, file_path);
          file_path = 0;
        }
    }

  arch_project_tree_delete (revision_tree);

  return file_path;
}


t_uchar *
arch_library_find_file_by_id (t_uchar * archive, t_uchar * revision, t_uchar * id)
{
  arch_project_tree_t * revision_tree;
  t_uchar * file_path = 0;
  arch_patch_id patch_id;

  arch_patch_id_init_archive (&patch_id, archive, revision);
  revision_tree = arch_library_find (0, &patch_id, 1);
  arch_patch_id_finalise (&patch_id);

  if (revision_tree)
    {
      t_uchar * index_file = 0;
      rel_table index = 0;
      int x;

      index_file = arch_library_index_file (archive, revision);
      index = arch_read_changeset_index (index_file);

      for (x = 0; x < rel_n_records (index); ++x)
        {
          if (!str_cmp (id, index[x][1]))
            {
              file_path = file_name_in_vicinity (0, revision_tree->root, index[x][0]);
              break;
            }
        }

      lim_free (0, index_file);
      rel_free_table (index);
    }

  arch_project_tree_delete (revision_tree);

  return file_path;
}



rel_table
arch_library_archives (void)
{
  rel_table lib_path = 0;
  rel_table answer = 0;
  int x;

  lib_path = arch_my_library_path (arch_library_path_search_order);

  for (x = 0; x < rel_n_records (lib_path); ++x)
    {
      rel_table tmp = 0;
      rel_table files = 0;
          
      files = directory_files (lib_path[x][0]);
      tmp = arch_pick_archives_by_field (files, 0);

      rel_append_x (&answer, tmp);

      rel_free_table (tmp);
      rel_free_table (files);
    }

  rel_sort_table_by_field (0, answer, 0);
  rel_uniq_by_field (&answer, 0);

  rel_free_table (lib_path);
  return answer;
}


rel_table
arch_library_categories (t_uchar * archive)
{
  rel_table answer = 0;
  rel_table archive_dirs = 0;
  int x;

  archive_dirs = arch_library_archive_dirs (0, archive, 0);

  for (x = 0; x < rel_n_records (archive_dirs); ++x)
    {
      rel_table files = 0;
      rel_table tmp = 0;

      files = directory_files (archive_dirs[x][0]);
      tmp = arch_pick_categories_by_field (files, 0);
      rel_append_x (&answer, tmp);

      rel_free_table (files);
      rel_free_table (tmp);
    }

  rel_sort_table_by_field (0, answer, 0);
  rel_uniq_by_field (&answer, 0);

  rel_free_table (archive_dirs);

  return answer;
}


rel_table
arch_library_branches (t_uchar * archive, t_uchar * category)
{
  rel_table answer = 0;
  rel_table category_dirs = 0;
  int x;

  category_dirs = arch_library_category_dirs (0, archive, category, 0);

  for (x = 0; x < rel_n_records (category_dirs); ++x)
    {
      rel_table files = 0;
      rel_table tmp = 0;

      files = directory_files (category_dirs[x][0]);
      tmp = arch_pick_branches_by_field (files, 0);
      rel_append_x (&answer, tmp);

      rel_free_table (files);
      rel_free_table (tmp);
    }

  rel_sort_table_by_field (0, answer, 0);
  rel_uniq_by_field (&answer, 0);

  rel_free_table (category_dirs);

  return answer;
}


rel_table
arch_library_versions (t_uchar * archive, t_uchar * branch)
{
  rel_table answer = 0;
  rel_table branch_dirs = 0;
  int x;

  branch_dirs = arch_library_branch_dirs (0, archive, branch, 0);

  for (x = 0; x < rel_n_records (branch_dirs); ++x)
    {
      rel_table files = 0;
      rel_table tmp = 0;

      files = directory_files (branch_dirs[x][0]);
      tmp = arch_pick_versions_by_field (files, 0);
      rel_append_x (&answer, tmp);

      rel_free_table (files);
      rel_free_table (tmp);
    }

  rel_sort_table_by_field (0, answer, 0);
  rel_uniq_by_field (&answer, 0);

  rel_free_table (branch_dirs);

  return answer;
}


rel_table
arch_library_revisions (t_uchar const * const archive,
                        t_uchar const * const version,
                        int full)
{
  rel_table answer = 0;
  rel_table version_dirs = 0;
  int x;

  version_dirs = arch_library_version_dirs (0, archive, version, 0);

  for (x = 0; x < rel_n_records (version_dirs); ++x)
    {
      rel_table files = 0;
      rel_table tmp = 0;
      int y;

      files = directory_files (version_dirs[x][0]);
      tmp = arch_pick_revisions_by_field (files, 0);
      
      for (y = 0; y < rel_n_records (tmp); ++y)
        {
          if (full)
            {
	      rel_replace_record (tmp, y, rel_make_record 
				  (arch_fully_qualify (archive, tmp[y][0]),
				   NULL));
            }
          else
            {
	      rel_replace_record (tmp, y, rel_make_record 
				  (arch_parse_package_name (arch_ret_patch_level, 0, tmp[y][0]),
				   NULL));
            }
        }

      rel_append_x (&answer, tmp);
      
      rel_free_table (files);
      rel_free_table (tmp);
    }

  if (full)
    {
      arch_sort_table_by_name_field (0, answer, 0);
    }
  else
    {
      arch_sort_table_by_patch_level_field (0, answer, 0);
    }
  rel_uniq_by_field (&answer, 0);

  rel_free_table (version_dirs);

  return answer;
}



t_uchar *
arch_library_log (t_uchar * archive, t_uchar * revision)
{
  arch_project_tree_t * rev_tree = 0;
  t_uchar * log_path = 0;
  int in_fd;
  t_uchar * answer = 0;
  size_t len;
  arch_patch_id patch_id;

  arch_patch_id_init_archive (&patch_id, archive, revision);
  rev_tree = arch_library_find (0, &patch_id, 1);
  arch_patch_id_finalise (&patch_id);

  log_path = arch_log_file (rev_tree->root, archive, revision);
  in_fd = safe_open (log_path, O_RDONLY, 0);
  safe_file_to_string (&answer, &len, in_fd);
  answer = lim_realloc (0, answer, len + 1);
  answer[len] = 0;
  safe_close (in_fd);

  arch_project_tree_delete (rev_tree);
  lim_free (0, log_path);
  return answer;
}

/** 
 * FIXME give this a directory hint for functions that
 * already have found the library dir 
 */
t_uchar *
arch_library_index_file (t_uchar const * const archive, t_uchar const * const revision)
{
  arch_project_tree_t * rev_tree;
  t_uchar * answer = 0;
  arch_patch_id patch_id;

  arch_patch_id_init_archive (&patch_id, archive, revision);
  rev_tree = arch_library_find (0, &patch_id, 1);
  arch_patch_id_finalise (&patch_id);

  invariant (!!rev_tree);

  answer = file_name_in_vicinity (0, rev_tree->root, ",,index");

  arch_project_tree_delete (rev_tree);
  return answer;
}


rel_table
arch_library_index (arch_patch_id * revision)
{
  t_uchar * index_path = 0;
  rel_table answer = 0;

  index_path = arch_library_index_file (arch_patch_id_archive (revision), arch_patch_id_revision (revision));
  answer = arch_read_changeset_index (index_path);

  lim_free (0, index_path);
  return answer;
}



int
arch_library_has_archive (t_uchar const * const lib, t_uchar const * const archive)
{
  t_uchar * path = file_name_in_vicinity (0, lib, archive);
  int answer;

  answer = !safe_access (path, F_OK);

  lim_free (0, path);
  return answer;
}


int
arch_library_has_category (t_uchar const * const lib, t_uchar const * const archive, t_uchar const * const category)
{
  t_uchar * archive_path = file_name_in_vicinity (0, lib, archive);
  t_uchar * category_path = file_name_in_vicinity (0, archive_path, category);
  int answer;

  answer = !safe_access (category_path, F_OK);

  lim_free (0, archive_path);
  lim_free (0, category_path);
  return answer;
}


int
arch_library_has_branch (t_uchar const * const lib, t_uchar const * const archive, t_uchar const * const branch)
{
  t_uchar * category = arch_parse_package_name (arch_ret_category, 0, branch);
  t_uchar * archive_path = file_name_in_vicinity (0, lib, archive);
  t_uchar * category_path = file_name_in_vicinity (0, archive_path, category);
  t_uchar * branch_path = file_name_in_vicinity (0, category_path, branch);
  int answer;

  answer = !safe_access (branch_path, F_OK);

  lim_free (0, category);

  lim_free (0, archive_path);
  lim_free (0, category_path);
  lim_free (0, branch_path);
  return answer;
}


int
arch_library_has_version (t_uchar const * const lib, t_uchar const * const archive, t_uchar const * const version)
{
  t_uchar * category = arch_parse_package_name (arch_ret_category, 0, version);
  t_uchar * branch = arch_parse_package_name (arch_ret_package, 0, version);
  t_uchar * archive_path = file_name_in_vicinity (0, lib, archive);
  t_uchar * category_path = file_name_in_vicinity (0, archive_path, category);
  t_uchar * branch_path = file_name_in_vicinity (0, category_path, branch);
  t_uchar * version_path = file_name_in_vicinity (0, branch_path, version);
  int answer;

  answer = !safe_access (version_path, F_OK);

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

  lim_free (0, archive_path);
  lim_free (0, category_path);
  lim_free (0, branch_path);
  lim_free (0, version_path);
  return answer;
}


t_uchar *
arch_library_revision_dir_in_lib (t_uchar const * const lib, arch_patch_id * patch_id)
{
  t_uchar * branch = arch_parse_package_name (arch_ret_package, 0, arch_patch_id_revision (patch_id));
  t_uchar * category = arch_parse_package_name (arch_ret_category, 0, arch_patch_id_revision (patch_id));
  t_uchar * archive_path = file_name_in_vicinity (0, lib, arch_patch_id_archive (patch_id));
  t_uchar * category_path = file_name_in_vicinity (0, archive_path, category);
  t_uchar * branch_path = file_name_in_vicinity (0, category_path, branch);
  t_uchar * version_path = file_name_in_vicinity (0, branch_path, arch_patch_id_version (patch_id));
  t_uchar * revision_path = file_name_in_vicinity (0, version_path, arch_patch_id_revision (patch_id));

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

  lim_free (0, archive_path);
  lim_free (0, category_path);
  lim_free (0, branch_path);
  lim_free (0, version_path);

  return revision_path;
}



int
arch_library_is_greedy (t_uchar * lib)
{
  t_uchar * greed_path = file_name_in_vicinity (0, lib, "=greedy");
  int answer = 0;

  answer = !safe_access (greed_path, F_OK);

  lim_free (0, greed_path);

  return answer;
}


void
arch_set_library_greediness (t_uchar * lib, int setting)
{
  t_uchar * greed_path = file_name_in_vicinity (0, lib, "=greedy");

  if (setting)
    {
      int fd;

      fd = safe_open (greed_path, O_RDONLY | O_CREAT, 0666);
      safe_close (fd);
    }
  else
    {
      if (!safe_access (greed_path, F_OK))
        safe_unlink (greed_path);
    }

  lim_free (0, greed_path);
}


int
arch_library_is_sparse (t_uchar * lib)
{
  t_uchar * sparse_path = file_name_in_vicinity (0, lib, "=sparse");
  int answer = 0;

  answer = !safe_access (sparse_path, F_OK);

  lim_free (0, sparse_path);

  return answer;
}


void
arch_set_library_sparseness (t_uchar * lib, int setting)
{
  t_uchar * sparse_path = file_name_in_vicinity (0, lib, "=sparse");

  if (setting)
    {
      int fd;

      fd = safe_open (sparse_path, O_RDONLY | O_CREAT, 0666);
      safe_close (fd);
    }
  else
    {
      if (!safe_access (sparse_path, F_OK))
        safe_unlink (sparse_path);
    }

  lim_free (0, sparse_path);
}




void
arch_verify_is_library (t_uchar * lib)
{
  rel_table lib_path = 0;
  int x;

  lib_path = arch_my_library_path (arch_library_path_search_order);

  for (x = 0; x < rel_n_records (lib_path); ++x)
    {
      if (names_same_inode (lib, lib_path[x][0]))
        return;
    }

  safe_printfmt (2, "baz: indicated library is not on library path\n  dir %s\n", lib);
  exit (2);
}



/* tag: Tom Lord Wed May 21 13:56:29 2003 (libraries.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1