/* changeset-utils.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/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/mem/talloc.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "po/gettext.h"
#include "libfsutils/read-line.h"
#include "libfsutils/safety.h"
#include "libarch/diffs.h"
#include "libarch/invent.h"
#include "libarch/changeset-utils.h"


/* __STDC__ prototypes for static functions */
static void changeset_inv_callback (void * closure,
                                    invent_callback_data_t const * const data);
static void cset_free (void *data);



void
arch_changeset_inventory (struct arch_changeset_inventory * inv_out,
                          arch_project_tree_t *tree,
                          enum arch_id_tagging_method method,
                          enum arch_inventory_category untagged_source_category,
                          int escape_classes)
{
  int here_fd;
  struct arch_inventory_options options;

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

  mem_set0 ((t_uchar *)&options, sizeof (options));
  options.categories = arch_inventory_source;
  options.want_ids = 1;
  options.treat_unrecognized_source_as_source = 1;

  if (method != arch_unspecified_id_tagging)
    {
      options.method = method;
      options.untagged_source_category = untagged_source_category;
      options.override_method = 1;
    }
  options.nested = 0;
  options.include_excluded = 1;

  arch_get_inventory_naming_conventions (&options, tree);

  inv_out->method = options.method;
  inv_out->escape_classes = escape_classes;

  safe_chdir (tree->root);
  arch_project_tree_changeset_inventory_traversal (tree, &options, changeset_inv_callback, inv_out);
  arch_free_inventory_naming_conventions (&options);

  rel_sort_table_by_field (0, inv_out->dirs, 1);
  rel_sort_table_by_field (0, inv_out->files, 1);

  safe_fchdir (here_fd);

  safe_close (here_fd);
}


void
arch_free_changeset_inventory_data (struct arch_changeset_inventory * i)
{
  rel_free_table (i->dirs);     i->dirs = 0;
  rel_free_table (i->files);    i->files = 0;
  cset_map_free (i->entries); i->entries = NULL;
}



rel_table
arch_read_changeset_index (t_uchar * path)
{
  int in_fd;
  rel_table answer = 0;
  t_uchar * line;
  long len;

  in_fd = safe_open (path, O_RDONLY, 0);

  while (1)
    {
      t_uchar * loc;
      t_uchar * id;
      t_uchar * start;

      line = 0;
      len = 0;
      safe_next_line (&line, &len, in_fd);

      if (!len)
        break;

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      start = line;
      while (len && !char_is_space (*line))
        {
          ++line;
          --len;
        }

      if (line == start)
        {
        syntax_error:
          safe_printfmt (2, "illegally formed changeset index (%s)\n", path);
          exit (2);
        }

      loc = pika_save_unescape_iso8859_1_n (0, 0, start, line - start );

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      start = line;

      while (len && !char_is_space (*line))
        {
          ++line;
          --len;
        }

      if (line == start)
        goto syntax_error;

      id = pika_save_unescape_iso8859_1_n (0, 0, start, line - start );

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      if (len)
        goto syntax_error;

      if (!is_non_upwards_relative_path (loc))
        {
          safe_printfmt (2, "illegal path in changeset: %s\n", loc);
          exit (2);
        }

      rel_add_records (&answer, rel_make_record (loc, id, 0), 0);
      lim_free (0, loc);
      lim_free (0, id);
    }

  safe_close (in_fd);
  return answer;
}


rel_table
arch_read_changeset_dir_metadata (t_uchar * path)
{
  int errn;
  int in_fd;
  rel_table answer = 0;

  in_fd = vu_open (&errn, path, O_RDONLY, 0);
  if (in_fd < 0)
    {
      if (errn == ENOENT)
        return 0;
      else
        {
          safe_printfmt (2, "arch_read_changeset_dir_metadata: unable to open file (%s)\n", path);
          safe_printfmt (2, "  %s\n", errno_to_string (errn));
          exit (2);
        }
    }

  while (1)
    {
      t_uchar * line;
      long len;
      t_uchar * start;
      t_uchar * perms = 0;
      t_uchar * loc = 0;

      line = 0;
      len = 0;
      safe_next_line (&line, &len, in_fd);
      if (!len)
        break;

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      if ((len < 13) || str_cmp_prefix ("--permissions", line))
        {
        syntax_error:
          safe_printfmt (2, "illegal dir metadata file: %s\n", path);
          exit (2);
        }
      len -= 13;
      line += 13;

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      start = line;
      while (len && !char_is_space (*line))
        {
          ++line;
          --len;
        }

      if (start == line)
        goto syntax_error;

      perms = pika_save_unescape_iso8859_1_n (0, 0, start, line - start );

      while (len && char_is_space (*line))
        {
          ++line;
          --len;
        }

      start = line;
      while (len && !char_is_space (*line))
        {
          ++line;
          --len;
        }

      if (start == line)
        goto syntax_error;

      loc = pika_save_unescape_iso8859_1_n (0, 0, start, line - start );

      if (!is_non_upwards_relative_path (loc))
        {
          safe_printfmt (2, "illegal path in changeset: %s\n", loc);
          exit (2);
        }

      rel_add_records (&answer, rel_make_record (loc, perms, 0), 0);

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

  safe_close (in_fd);

  rel_sort_table_by_field (0, answer, 0);

  return answer;
}


mode_t
arch_read_permissions_patch (t_uchar * file)
{
  int errn;
  t_uchar * line = 0;
  t_uchar * s;
  t_uchar * e;
  t_ulong answer;

  line = read_line_from_file (file);

  s = line;

  while (char_is_space (*s))
    ++s;
  if (str_cmp_prefix ("--permissions", s))
    {
    syntax_error:
      safe_printfmt (2, "illegal metadata patch file: %s\n", file);
      exit (2);
    }
  s += sizeof ("--permissions") - 1;
  while (char_is_space (*s))
    ++s;

  for (e = s; char_is_odigit (*e); ++e)
    ;

  if (e == s)
    goto syntax_error;

  if (cvt_octal_to_ulong (&errn, &answer, s, e - s))
    goto syntax_error;

  lim_free (0, line);
  return (mode_t)answer;
}





void
changeset_inv_callback (void * closure, invent_callback_data_t const * const data)
{
  struct arch_changeset_inventory * index;

  index = (struct arch_changeset_inventory *)closure;

  if (!data->id)
    {
      t_uchar * dir = 0;

      dir = file_name_directory_file (0, data->path);
      if (!arch_is_dont_care_explicit_dflt_dir (dir))
        {
          t_uchar * e_path = 0;

          e_path = pika_save_escape_iso8859_1 (0, 0, index->escape_classes, data->path);
          safe_printfmt (2, _("missing explicit id for file (try status --lint)\n   file:%s\n"), e_path);
          exit (2);
        }
      lim_free (0, dir);

      return;
    }


  if (S_ISDIR (data->stat_buf.st_mode))
    rel_add_records (&index->dirs, rel_make_record (data->path, data->id, 0), 0);
  else
    rel_add_records (&index->files, rel_make_record (data->path, data->id, 0), 0);

  cset_map_insert (&index->entries, data->id, arch_changeset_inventory_entry_new (&data->stat_buf));
}

char *
no_dot (char *name)
{
  if (name[0] == '.' && name[1] == '/')
    return name + 2;
  else
    return name;
}

arch_changeset_inventory_entry_t *
arch_changeset_inventory_entry_new (struct stat const * const stat_buf)
{
    arch_changeset_inventory_entry_t * result = talloc (NULL, arch_changeset_inventory_entry_t);
    result->stat_buf = *stat_buf;
    return result;
}

void
cset_free (void *data)
{
  talloc_free (data);
}

void
cset_map_insert (cset_map * vtable,
           t_uchar const * key,
           arch_changeset_inventory_entry_t *entry)
{
  pointer_map_insert ((pointer_map *)vtable, key, entry, cset_free);
}

void
cset_map_free (cset_map vtable)
{
  pointer_map_free ((pointer_map)vtable, cset_free);
}

arch_changeset_inventory_entry_t *
cset_map_find (cset_map vtable, t_uchar const * key)
{
  return pointer_map_find ((pointer_map) vtable, key);
}

/* tag: Tom Lord Thu May 15 13:00:33 2003 (changeset-utils.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1