/* 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/panic.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/same.h"
#include "tla/libarch/my.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/changeset-utils.h"
#include "tla/libarch/libraries.h"
#include "tla/libarch/inode-sig.h"


/* __STDC__ prototypes for static functions */





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

  invariant (arch_valid_archive_name (archive));

  if (rel_n_records (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 (!rel_n_records (lib_path))
        return rel_table_nil;
    }

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

      lib_dir = rel_peek_str (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_singleton_record_taking (rel_make_field_str (maybe_archive_dir)), rel_record_null);
        }

      lim_free (0, maybe_archive_dir);
    }

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

      priority_lib_dir = rel_peek_str (lib_path, 0, 0);
      archive_dir = file_name_in_vicinity (0, priority_lib_dir, archive);
      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (archive_dir)), rel_record_null);

      lim_free (0, archive_dir);
    }

  rel_free_table (lib_path);
  return answer;
}


rel_table
arch_library_category_dirs (rel_table opt_lib_path,
                            const t_uchar * archive,
                            const t_uchar * category,
                            int for_add)
{
  rel_table answer = rel_table_nil;
  rel_table archive_dirs = rel_table_nil;
  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 (!rel_n_records (archive_dirs))
    return rel_table_nil;

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

      archive_dir = rel_peek_str (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_singleton_record_taking (rel_make_field_str (maybe_category_dir)), rel_record_null);
        }

      lim_free (0, maybe_category_dir);
    }

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

      priority_archive_dir = rel_peek_str (archive_dirs, 0, 0);
      category_dir = file_name_in_vicinity (0, priority_archive_dir, category);
      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (category_dir)), rel_record_null);

      lim_free (0, category_dir);
    }

  rel_free_table (archive_dirs);

  return answer;
}


rel_table
arch_library_branch_dirs (rel_table opt_lib_path,
                          const t_uchar * archive,
                          const t_uchar * branch,
                          int for_add)
{
  rel_table answer = rel_table_nil;
  t_uchar * category = 0;
  rel_table category_dirs = rel_table_nil;
  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 (!rel_n_records (category_dirs))
    return rel_table_nil;

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

      category_dir = rel_peek_str (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_singleton_record_taking (rel_make_field_str (maybe_branch_dir)), rel_record_null);
        }

      lim_free (0, maybe_branch_dir);
    }

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

      priority_category_dir = rel_peek_str (category_dirs, 0, 0);
      branch_dir = file_name_in_vicinity (0, priority_category_dir, branch);
      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (branch_dir)), rel_record_null);

      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,
                           const t_uchar * archive,
                           const t_uchar * version,
                           int for_add)
{
  rel_table answer = rel_table_nil;
  t_uchar * branch = 0;
  rel_table branch_dirs = rel_table_nil;
  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 (!rel_n_records (branch_dirs))
    return rel_table_nil;

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

      branch_dir = rel_peek_str (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_singleton_record_taking (rel_make_field_str (maybe_version_dir)), rel_record_null);
        }

      lim_free (0, maybe_version_dir);
    }

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

      priority_branch_dir = rel_peek_str (branch_dirs, 0, 0);
      version_dir = file_name_in_vicinity (0, priority_branch_dir, version);
      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (version_dir)), rel_record_null);

      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,
                            const t_uchar * archive,
                            const t_uchar * revision,
                            int for_add)
{
  rel_table answer = rel_table_nil;
  t_uchar * version = 0;
  rel_table version_dirs = rel_table_nil;
  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 (!rel_n_records (version_dirs))
    return rel_table_nil;

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

      version_dir = rel_peek_str (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_singleton_record_taking (rel_make_field_str (maybe_revision_dir)), rel_record_null);
        }

      lim_free (0, maybe_revision_dir);
    }

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

      priority_version_dir = rel_peek_str (version_dirs, 0, 0);
      revision_dir = file_name_in_vicinity (0, priority_version_dir, revision);
      rel_add_records (&answer, rel_singleton_record_taking (rel_make_field_str (revision_dir)), rel_record_null);

      lim_free (0, revision_dir);
    }

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

  return answer;
}



t_uchar *
arch_library_find (rel_table opt_lib_path,
                   const t_uchar * archive,
                   const t_uchar * revision,
                   int check_inode_sigs)
{
  rel_table revision_dirs = rel_table_nil;
  t_uchar * answer = 0;
  int x;

  revision_dirs = arch_library_revision_dirs (opt_lib_path, archive, revision, 0);

  for (x = 0; x < rel_n_records (revision_dirs); ++x)
    {
      if (!safe_access (rel_peek_str (revision_dirs, x, 0), F_OK))
        {
          if (check_inode_sigs) {
            if (!arch_valid_inode_sig (rel_peek_str (revision_dirs, 0, 0), archive, revision))
              {
                safe_printfmt (2, "corrupt library (failed inode signature validation)\n    archive: %s\n    revision: %s\nYou should remove this revision from your library.\n",
                               archive, revision);
                exit (2);
              }
          }
          answer = str_save (0, rel_peek_str (revision_dirs, 0, 0));
          break;
        }
    }

  rel_free_table (revision_dirs);
  return answer;
}



t_uchar *
arch_library_find_file (const t_uchar * archive,
                        const t_uchar * revision,
                        const t_uchar * loc)
{
  t_uchar * revision_dir = 0;
  t_uchar * file_path = 0;

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

  if (revision_dir)
    {
      file_path = file_name_in_vicinity (0, revision_dir, loc);

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

  lim_free (0, revision_dir);

  return file_path;
}


t_uchar *
arch_library_find_file_by_id (const t_uchar * archive,
                              const t_uchar * revision,
                              const t_uchar * id)
{
  t_uchar * revision_dir = 0;
  t_uchar * file_path = 0;

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

  if (revision_dir)
    {
      t_uchar * index_file = 0;
      rel_table index = rel_table_nil;
      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, rel_peek_str (index, x, 1)))
            {
              file_path = file_name_in_vicinity (0, revision_dir, rel_peek_str (index, x, 0));
              break;
            }
        }

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

  lim_free (0, revision_dir);

  return file_path;
}



rel_table
arch_library_archives (void)
{
  rel_table lib_path = rel_table_nil;
  rel_table answer = rel_table_nil;
  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 = rel_table_nil;
      rel_table files = rel_table_nil;
          
      files = directory_files (rel_peek_str (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 (const t_uchar * archive)
{
  rel_table answer = rel_table_nil;
  rel_table archive_dirs = rel_table_nil;
  int x;

  archive_dirs = arch_library_archive_dirs (rel_table_nil, archive, 0);

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

      files = directory_files (rel_peek_str (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 (const t_uchar * archive,
                       const t_uchar * category)
{
  rel_table answer = rel_table_nil;
  rel_table category_dirs = rel_table_nil;
  int x;

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

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

      files = directory_files (rel_peek_str (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 (const t_uchar * archive,
                       const t_uchar * branch)
{
  rel_table answer = rel_table_nil;
  rel_table branch_dirs = rel_table_nil;
  int x;

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

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

      files = directory_files (rel_peek_str (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 (const t_uchar * archive,
                        const t_uchar * version,
                        int full)
{
  rel_table answer = rel_table_nil;
  rel_table version_dirs = rel_table_nil;
  int x;

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

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

      files = directory_files (rel_peek_str (version_dirs, x, 0));
      tmp = arch_pick_revisions_by_field (files, 0);
      
      for (y = 0; y < rel_n_records (tmp); ++y)
        {
          t_uchar * t = 0;

          if (full)
            {
              t = arch_fully_qualify (archive, rel_peek_str (tmp, y, 0));
              rel_set_taking (tmp, y, 0, rel_make_field_str (t));
            }
          else
            {
              t = arch_parse_package_name (arch_ret_patch_level, 0, rel_peek_str (tmp, y, 0));
              rel_set_taking (tmp, y, 0, rel_make_field_str (t));
            }

          lim_free (0, t);
        }

      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 (const t_uchar * archive,
                  const t_uchar * revision)
{
  t_uchar * rev_dir = 0;
  t_uchar * log_path = 0;
  int in_fd;
  t_uchar * answer = 0;
  size_t len;

  rev_dir = arch_library_find (rel_table_nil, archive, revision, 1);
  log_path = arch_log_file (rev_dir, 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);

  lim_free (0, rev_dir);
  lim_free (0, log_path);
  return answer;
}


t_uchar *
arch_library_index_file (const t_uchar * archive,
                         const t_uchar * revision)
{
  t_uchar * revision_dir = 0;
  t_uchar * answer = 0;

  revision_dir = arch_library_find (rel_table_nil, archive, revision, 1);
  invariant (!!revision_dir);

  answer = file_name_in_vicinity (0, revision_dir, ",,index");

  lim_free (0, revision_dir);
  return answer;
}


rel_table
arch_library_index (const t_uchar * archive,
                    const t_uchar * revision)
{
  t_uchar * index_path = 0;
  rel_table answer = rel_table_nil;

  index_path = arch_library_index_file (archive, revision);
  answer = arch_read_changeset_index (index_path);

  lim_free (0, index_path);
  return answer;
}



int
arch_library_has_archive (const t_uchar * lib,
                          const t_uchar * 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 (const t_uchar * lib,
                           const t_uchar * archive,
                           const t_uchar * 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 (const t_uchar * lib,
                         const t_uchar * archive,
                         const t_uchar * 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 (const t_uchar * lib,
                          const t_uchar * archive,
                          const t_uchar * 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 (const t_uchar * lib,
                                  const t_uchar * archive,
                                  const t_uchar * revision)
{
  t_uchar * category = arch_parse_package_name (arch_ret_category, 0, revision);
  t_uchar * branch = arch_parse_package_name (arch_ret_package, 0, revision);
  t_uchar * version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  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);
  t_uchar * revision_path = file_name_in_vicinity (0, version_path, revision);

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

  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 (const 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 (const 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 (const 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 (const 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 (const t_uchar * lib)
{
  rel_table lib_path = rel_table_nil;
  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, rel_peek_str (lib_path, x, 0)))
        return;
    }

  safe_printfmt (2, "tla: 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)
 */
