/* changeset-utils.c: 
 *
 ****************************************************************
 * 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/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/safe.h"
#include "file-utils/libfsutils/read-line.h"
#include "file-utils/libarch/invent.h"
#include "file-utils/libarch/changeset-utils.h"


/* __STDC__ prototypes for static functions */
static void changeset_inv_callback (t_uchar * path,
                                    struct stat * stat_buf,
                                    enum arch_inventory_category category,
                                    t_uchar * tag,
                                    void * closure);



void
arch_changeset_inventory (struct arch_changeset_inventory * inv_out,
                          t_uchar * tree_root, t_uchar * path,
                          enum arch_tagging_method method)
{
  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_tags = 1;
  options.include_untagged = 1;
  if (method == arch_unspecified_tagging)
    options.method = arch_tree_tagging_method (tree_root, 0);
  else
    options.method = method;
  options.nested = 0;
  options.include_excluded = 1;

  arch_get_inventory_naming_conventions (&options, tree_root);

  safe_chdir (path);
  arch_inventory_traversal (&options, ".", changeset_inv_callback, (void *)inv_out);

  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;
}



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 * tag;
      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 = str_save_n (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;

      tag = str_save_n (0, start, line - start);

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

      if (len)
        goto syntax_error;

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

  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 += 12;
          
      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 = str_save_n (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 = str_save_n (0, start, line - start);

      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;
}





static void
changeset_inv_callback (t_uchar * path,
                        struct stat * stat_buf,
                        enum arch_inventory_category category,
                        t_uchar * tag,
                        void * closure)
{
  struct arch_changeset_inventory * index;

  if (!tag)
    {
      safe_printfmt (2, "missing explicit tag for file (try tree-lint)\n   file:%s\n", path);
      exit (2);
    }


  index = (struct arch_changeset_inventory *)closure;

  if (S_ISDIR (stat_buf->st_mode))
    rel_add_records (&index->dirs, rel_make_record (path, tag, 0), 0);
  else
    rel_add_records (&index->files, rel_make_record (path, tag, 0), 0);
}


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