Logo Search packages:      
Sourcecode: bazaar version File versions

project-tree.c

/* project-tree.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/bugs/panic.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/char/str.h"
#include "libfsutils/file-contents.h"
#include "libarch/ancestry.h"
#include "libarch/namespace.h"
#include "libarch/patch-id.h"
#include "libarch/patch-logs.h"
#include "libarch/changelogs.h"
#include "libarch/inv-ids.h"
#include "libarch/project-tree.h"



/* These must agree about the format version number.
 */
static const int arch_tree_format_vsn = 1;
static const char arch_tree_format_vsn_id[] = "1";
static const char arch_tree_format_str[] = "Hackerlab arch project directory, format version 1.";

static void arch_ensure_compatable_tree(t_uchar * arch_version_file);



/* FIXME: jb- I think this should return a str_save, so that we can
 * remain consistant throughout the code of lim_freeing every string we
 * 'create'
 */
t_uchar *
arch_tree_format_string (void)
{
  return (t_uchar *)arch_tree_format_str;
}

void
arch_init_tree (t_uchar * tree_root)
{
  t_uchar * arch_dir = 0;
  t_uchar * arch_vsn_file = 0;
  t_uchar * id_tagging_method_file = 0;
  t_uchar * id_tagging_method_defaults = 0;
  int out_fd;

  arch_dir = file_name_in_vicinity (0, tree_root, "{arch}");
  arch_vsn_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");
  id_tagging_method_file = file_name_in_vicinity (0, arch_dir, "=tagging-method");

  safe_mkdir (arch_dir, 0777);
  out_fd = safe_open (arch_vsn_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
  safe_printfmt (out_fd, "%s\n", arch_tree_format_string ());
  safe_close (out_fd);

  id_tagging_method_defaults = arch_default_id_tagging_method_contents (arch_unspecified_id_tagging);
  out_fd = safe_open (id_tagging_method_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
  safe_printfmt (out_fd, "%s", id_tagging_method_defaults);
  safe_close (out_fd);

  lim_free (0, arch_dir);
  lim_free (0, arch_vsn_file);
  lim_free (0, id_tagging_method_defaults);
  lim_free (0, id_tagging_method_file);
}

/* Move to fsutils or hackerlab */
static int
is_dir (t_uchar const *path)
{
    int errn;
    int answer;
    struct stat stat_buf;

    answer = vu_lstat (&errn, (char  *)path, &stat_buf);
    if (answer)
      {
      /* couldn't stat, pretend its not a dir */
      return 0;
      }
    return S_ISDIR(stat_buf.st_mode);
}

/* Note: Right now, because there's only one archive format, its a simple
 * string to string copy. Some day, it may be an array of supported 
 * project trees, and we'll have to loop an array. Or we may have partial
 * compatibility, etc. For now though, this'll do
 */
void
arch_ensure_compatable_tree(t_uchar * arch_version_file)
{
  int in_fd;
  size_t file_contents_len;
  t_uchar * tree_version_name = 0;
  t_uchar * supported_versions = 0;

  in_fd = safe_open (arch_version_file, O_RDONLY, 0666);

  safe_file_to_string (&tree_version_name, &file_contents_len, in_fd);
  safe_close (in_fd);

  supported_versions = arch_tree_format_string();

  if ( 0 == str_cmp(tree_version_name, supported_versions) )
    {
      safe_printfmt(2, "Incompatible working copy:\n");
      safe_printfmt(2, "   %s\n", tree_version_name);
      exit(1);
    }

  lim_free(0, tree_version_name);


}

t_uchar *
arch_tree_root (enum arch_tree_state * state, t_uchar * input_dir, int accurate)
{
  int here_fd = -1;  /* shutup gcc, here_fd is used properly */
  int errn;
  t_uchar * dir;
  t_uchar * answer;
  t_uchar *new_dir = NULL;

  answer = 0;

  if (input_dir)
    {
      if (!is_dir (input_dir))
      {
        new_dir = file_name_directory_file (0, input_dir);
        input_dir = new_dir;
        if (! input_dir)
            input_dir = ".";
      }
      here_fd = safe_open (".", O_RDONLY, 0);
      safe_chdir (input_dir);
    }

  dir = (t_uchar *)current_working_directory (&errn, 0);
  if (!dir)
    panic ("unable to compute current working directory");

  if (input_dir)
    {
      safe_fchdir (here_fd);
      safe_close (here_fd);
    }

  while (1)
    {
      t_uchar * arch_dir;
      t_uchar * arch_version_file;
      t_uchar * next_dir;

      arch_dir = file_name_in_vicinity (0, dir, "{arch}");
      arch_version_file = file_name_in_vicinity (0, arch_dir, ".arch-project-tree");

      if (!safe_access (arch_dir, F_OK) && !safe_access (arch_version_file, F_OK))
        {
          arch_ensure_compatable_tree(arch_version_file);
          answer = str_save (0, dir);
          break;
        }

      lim_free (0, arch_dir);
      lim_free (0, arch_version_file);

      if (!str_cmp (dir, "/"))
        break;


      next_dir = file_name_directory_file (0, dir);
      lim_free (0, dir);
      dir = next_dir;
      next_dir = 0;
    }

  if (answer && accurate)
    {
      t_uchar * rc_name;
      t_uchar * cd_name;
      t_uchar * mc_name;

      rc_name = file_name_in_vicinity (0, answer, "{arch}/++resolve-conflicts");
      cd_name = file_name_in_vicinity (0, answer, "{arch}/++commit-definite");
      mc_name = file_name_in_vicinity (0, answer, "{arch}/++mid-commit");

      if (state)
        {
          if (!safe_access (rc_name, F_OK))
            *state = arch_tree_in_resolve_conflicts;
          else if (!safe_access (cd_name, F_OK))
            *state = arch_tree_in_commit_definite;
          else if (!safe_access (mc_name, F_OK))
            *state = arch_tree_in_mid_commit;
          else
            *state = arch_tree_in_ok_state;
        }
    }

  lim_free (0, dir);
  if (new_dir)
      lim_free (0, new_dir);
  return answer;
}

t_uchar *
arch_tree_ctl_dir (t_uchar const * tree_root)
{
  return file_name_in_vicinity (0, tree_root, "{arch}");
}


void
arch_set_tree_version (t_uchar const * tree_root, t_uchar const * fqversion)
{
  t_uchar * default_version_file = 0;
  t_uchar * tmp_file = 0;
  int out_fd;

  invariant (arch_valid_package_name (fqversion, arch_req_archive, arch_req_version, 0));

  tmp_file = file_name_in_vicinity (0, tree_root, "{arch}/,,set-tree-version");

  default_version_file = file_name_in_vicinity (0, tree_root, "{arch}/++default-version");

  out_fd = safe_open (tmp_file, O_WRONLY | O_CREAT, 0666);

  safe_ftruncate (out_fd, (long)0);
  safe_printfmt (out_fd, "%s\n", fqversion);

  safe_close (out_fd);
  safe_rename (tmp_file, default_version_file);

  lim_free (0, tmp_file);
  lim_free (0, default_version_file);
}

t_uchar *
arch_tree_version (t_uchar const * tree_root)
{
  t_uchar * default_version_file;
  int in_fd;
  t_uchar * file_contents;
  size_t file_contents_len;
  t_uchar * nl;

  default_version_file = file_name_in_vicinity (0, tree_root, "{arch}/++default-version");

  if (safe_access (default_version_file, F_OK))
    return 0;

  in_fd = safe_open (default_version_file, O_RDONLY, 0666);

  file_contents = 0;
  file_contents_len = 0;
  safe_file_to_string (&file_contents, &file_contents_len, in_fd);

  safe_close (in_fd);

  nl = str_chr_index_n (file_contents, file_contents_len, '\n');
  if (nl)
    file_contents_len = nl - file_contents;

  file_contents = lim_realloc (0, file_contents, file_contents_len + 1);
  file_contents[file_contents_len] = 0;

  invariant (arch_valid_package_name (file_contents, arch_req_archive, arch_req_version, 0));

  return file_contents;
}


t_uchar *
arch_try_tree_version_dir (t_uchar * cmd, t_uchar * dir)
{
  t_uchar * version_spec = 0;
  t_uchar * tree_root = 0;

  tree_root = arch_tree_root (0, dir, 0);

  if (!tree_root)
    {
      safe_printfmt (2, "%s: not in a project tree\n", cmd);
      exit (2);
    }

  version_spec = arch_tree_version (tree_root);

  if (!version_spec)
    {
      safe_printfmt (2, "%s: tree has no default version set\n    tree: %s\n",
                     cmd, tree_root);
      exit (2);
    }

  lim_free (0, tree_root);

  return version_spec;
}

t_uchar *
arch_try_tree_version (t_uchar * cmd)
{
  t_uchar * answer;
  answer = arch_try_tree_version_dir (cmd, ".");
  return answer;
}



void
arch_start_tree_commit (t_uchar * tree_root, t_uchar * log)
{
  int ign;
  t_uchar * mid_commit_file = 0;
  t_uchar * mid_commit_tmp = 0;
  int out_fd;

  mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");
  mid_commit_tmp = file_name_in_vicinity (0, tree_root, "{arch}/,,mid-commit");

  vu_unlink (&ign, mid_commit_tmp);
  vu_unlink (&ign, mid_commit_file);

  out_fd = safe_open (mid_commit_tmp, O_WRONLY | O_CREAT | O_EXCL, 0666);
  safe_printfmt (out_fd, "%s", log);
  safe_close (out_fd);

  safe_rename (mid_commit_tmp, mid_commit_file);

  lim_free (0, mid_commit_file);
}


void
arch_finish_tree_commit (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar * changelog_loc)
{
  t_uchar * mid_commit_file = 0;
  t_uchar * commit_definite_file = 0;

  mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");
  commit_definite_file = file_name_in_vicinity (0, tree_root, "{arch}/++commit-definite");

  safe_rename (mid_commit_file, commit_definite_file);

  if (changelog_loc)
    {
      struct stat statb;
      t_uchar * level = 0;
      t_uchar * version = 0;
      t_uchar * changelog_path = 0;
      t_uchar * changelog_dir = 0;
      t_uchar * changelog_tmp = 0;
      mode_t mode;
      int fd;

      level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
      version = arch_parse_package_name (arch_ret_package_version, 0, revision);

      changelog_path = file_name_in_vicinity (0, tree_root, changelog_loc);
      changelog_dir = file_name_directory_file (0, changelog_path);
      changelog_tmp = file_name_in_vicinity (0, changelog_dir, ",,new-changelog");

      safe_stat (changelog_path, &statb);
      mode = statb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
      fd = safe_open (changelog_tmp, O_WRONLY | O_CREAT | O_TRUNC, mode);
      safe_fchmod (fd, mode);

      arch_generate_changelog (fd, tree_root, 0, 0, level, commit_definite_file, archive, version);

      safe_close (fd);

      safe_rename (changelog_tmp, changelog_path);

      lim_free (0, level);
      lim_free (0, version);
      lim_free (0, changelog_path);
      lim_free (0, changelog_dir);
      lim_free (0, changelog_tmp);
    }

  arch_copy_to_patch_log (tree_root, archive, revision, commit_definite_file);

  safe_unlink (commit_definite_file);

  lim_free (0, mid_commit_file);
  lim_free (0, commit_definite_file);
}


void
arch_abort_tree_commit (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * mid_commit_file = 0;

  mid_commit_file = file_name_in_vicinity (0, tree_root, "{arch}/++mid-commit");

  safe_unlink (mid_commit_file);

  lim_free (0, mid_commit_file);
}

void 
arch_project_tree_init (arch_project_tree_t *tree, t_uchar const * tree_root)
{
  t_uchar * latest_log;
    
  tree->archive = NULL;
  tree->fqversion = NULL;
  tree->fqrevision = NULL;
  tree->version = NULL;
  tree->revision = NULL;
  tree->ancestry = NULL;
  tree->root = arch_tree_root (0, (t_uchar *)tree_root, 0); /* Bah. need to make stuff const correct everywhere */
  if (!tree->root)
      return;
  tree->fqversion = arch_tree_version (tree->root);
  if (!tree->fqversion)
      return;
  tree->archive = arch_parse_package_name (arch_ret_archive, 0, tree->fqversion);
  tree->version = arch_parse_package_name (arch_ret_non_archive, 0, tree->fqversion);
  /* tree_root should have done this right.. but lets be sure */
  invariant (!!tree->archive);
  invariant (!!tree->version);
  
  latest_log = arch_highest_patch_level (tree->root, tree->archive, tree->version);
  tree->fqrevision = str_alloc_cat_many (0, tree->archive, "/", tree->version, "--", latest_log, str_end);
  tree->revision = str_alloc_cat_many (0, tree->version, "--", latest_log, str_end);
  
  lim_free (0, latest_log);
}

void
arch_project_tree_finalise (arch_project_tree_t *tree)
{
  lim_free (0, tree->root);
  lim_free (0, tree->archive);
  lim_free (0, tree->fqversion);
  lim_free (0, tree->fqrevision);
  lim_free (0, tree->version);
  lim_free (0, tree->revision);
  rel_free_table (tree->ancestry);
}

int
arch_project_tree_file_exists(struct arch_project_tree *tree, t_uchar const *rel_path)
{
  t_uchar *path = file_name_in_vicinity (0, tree->root, rel_path);
  struct stat stat_buf;
  int result = stat (path, &stat_buf);
  lim_free (0, path);
  return result == 0;
}

t_uchar *
arch_project_tree_file_contents(struct arch_project_tree *tree, t_uchar const *rel_path)
{
  t_uchar *path = file_name_in_vicinity (0, tree->root, rel_path);
  t_uchar *answer = file_contents (path);
  lim_free (0, path);
  return answer;
}

/* 
 * get as much of the trees ancestry as we can. Uses the archive
 * to retrieve the ancestry. 
 * Ownership of the table is retained by the project tree struct
 */
rel_table /* const */
arch_project_tree_ancestry (struct arch_project_tree *tree)
{
    if (tree->ancestry)
      return tree->ancestry;
    tree->ancestry = patch_ancestry (tree, tree->fqrevision, -1, 1);
    return tree->ancestry;
}

int
arch_project_tree_has_patch (arch_project_tree_t *tree, t_uchar const *patch_id)
{
  int answer = 0;
  t_uchar * log_dir, *log_file;
  arch_patch_id thePatch;

  arch_patch_id_init (&thePatch, patch_id);

  answer = 0;

  log_dir = arch_log_dir (tree->root, arch_patch_id_archive(&thePatch), arch_patch_id_version(&thePatch));
  log_file = file_name_in_vicinity (0, log_dir, arch_patch_id_patchlevel(&thePatch));

  /* log files are never symlinks etc */
  if (!safe_access (log_file, F_OK))
      answer = -1;

  lim_free (0, log_file);
  lim_free (0, log_dir);
  arch_patch_id_finalise (&thePatch);
  return answer;
}

/* tag: Tom Lord Mon May 12 10:12:38 2003 (project-tree.c)
 */

Generated by  Doxygen 1.6.0   Back to index