/* tla-project-tree.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2005 Canonical Limited
 *    Authors: Robert Collins <robert.collins@canonical.com>
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/bugs/exception.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/vu.h"
#include "hackerlab/os/errno-to-string.h"
#include "libfsutils/file-contents.h"
#include "libawk/trim.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/invent.h"
#include "libarch/project-tree.h"
#include "libarch/null-project-tree.h"

typedef arch_project_tree_t null_project_tree_t;

static int null_project_tree_destructor (void * data);
/* methods */
static void null_init (arch_project_tree_t * tree);
static enum arch_id_tagging_method null_id_tagging_method (arch_project_tree_t * tree, enum arch_inventory_category * cat_var, int strict);
static inventory_entry_t * null_path_id (arch_project_tree_t * tree, int * errn, t_uchar const * const path, struct stat * known_lstat);
static void null_changeset_inventory_traveral (arch_project_tree_t * tree, struct arch_inventory_options * options, inv_callback callback, void * closure);

arch_project_tree_vtable null_project_tree_vtable = 
{
    null_init,
    null_id_tagging_method,
    null_path_id,
    null_changeset_inventory_traveral,
    NULL,
    arch_project_tree_vtable_end
};


/**
 * \brief is dir the root of a null project tree? 
 * \return non zero on true.
 */
int 
null_project_tree_dir_is_root (void * context, t_uchar const * dir, arch_project_tree_t **out)
{
  if (!safe_access ((t_uchar *)dir, F_OK))
    {
      if (out)
        {
          *out = talloc (context, null_project_tree_t);
          talloc_set_destructor (*out, null_project_tree_destructor);
          (*out)->vtable = &null_project_tree_vtable;
        }
      return -1;
    }
  /* incompatible tree */
  safe_printfmt (2, "null_project_tree_dir_is_root: inaccessible path: '%s'\n", dir);
  exit (2);
}

/**
 * \brief free a null project tree
 */
int
null_project_tree_destructor (void * data)
{
    arch_project_tree_t * tree = talloc_get_type (data, null_project_tree_t);
    if (!tree)
        Throw (exception (EINVAL, "invalid tree in null_project_tree_destructor"));
    arch_project_tree_finalise (tree);
    return 0;
}

/**
 * \brief find the id tagging method for a tree
 */
enum arch_id_tagging_method
null_id_tagging_method (arch_project_tree_t * tree, enum arch_inventory_category * cat_var, int strict)
{
  if (cat_var)
    {
      *cat_var = arch_inventory_source;
    }
  return arch_names_id_tagging;
}

/**
 * \brief see arch_project_tree_init
 */
void
null_init (arch_project_tree_t * tree)
{
    tree->untagged_is_source = 1;
    tree->tag_method = arch_names_id_tagging; /* no ids in real fs's */
}

/**
 * \brief see arch_inventory_id
 */
inventory_entry_t * 
null_path_id (arch_project_tree_t * tree, int * errn, t_uchar const * const path, struct stat * known_lstat)
{
  t_uchar * as_file = file_name_from_directory (0, (t_uchar *)path);
  t_uchar * answer_id = NULL;
  t_uchar * as_file_tail = NULL; 
  inventory_entry_t * answer = NULL;
  t_uchar * fq_path = NULL;
  if (!as_file)
      goto enomem;
  
  as_file_tail = file_name_tail (0, as_file);
  if (!as_file_tail)
      goto enomem;
  
  fq_path = file_name_in_vicinity (0, tree->root, as_file);
  if (!fq_path)
      goto enomem;

  answer_id = str_alloc_cat (0, "?", as_file);
  if (!answer_id)
    {
enomem:
      *errn = ENOMEM;
error:
      lim_free (0, fq_path);
      lim_free (0, as_file);
      lim_free (0, as_file_tail);
      lim_free (0, answer_id);
      return 0;
    }
  /* FIXME RBC 20050331 make this cleaner and only-called once */
    {
      struct stat stat_buf;
      struct stat *thestat;

      if (known_lstat)
          thestat = known_lstat;
      else
        {
          thestat = &stat_buf;

          if (vu_lstat (errn, fq_path, thestat))
            {
              goto error;
            }
        }
      if (S_ISDIR (thestat->st_mode))
          answer = inventory_dir_new (NULL, as_file_tail, answer_id);
      else if (S_ISLNK (thestat->st_mode))
          answer = inventory_link_new (NULL, as_file_tail, answer_id);
      else
          answer = inventory_file_new (NULL, as_file_tail, answer_id);
    }
  lim_free (0, fq_path);
  lim_free (0, answer_id);
  lim_free (0, as_file_tail);
  return answer;
}

/**
 * \brief see arch_project_tree_changeset_inventory_traversal
 */
void
null_changeset_inventory_traveral (arch_project_tree_t * tree, struct arch_inventory_options * options, inv_callback callback, void * closure)
{
  arch_inventory_traversal (options, tree, callback, closure);
}


syntax highlighted by Code2HTML, v. 0.9.1