/* inv-tags.c: 
 *
 ****************************************************************
 * Copyright (C) 2002, 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/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/os/time.h"
#include "hackerlab/os/sys/types.h"
#include "hackerlab/os/unistd.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "file-utils/libfsutils/ensure-dir.h"
#include "file-utils/liblarch/ftag.h"
#include "file-utils/libarch/my.h"
#include "file-utils/libarch/project-tree.h"
#include "file-utils/libarch/patch-logs.h"
#include "file-utils/libarch/inv-tags.h"



t_uchar *
arch_log_file_tag (t_uchar * archive, t_uchar * revision)
{
  t_uchar * log_file_path = 0;
  t_uchar * answer = 0;

  log_file_path = arch_log_file (".", archive, revision);
  answer = str_alloc_cat (0, "A_", log_file_path);

  lim_free (0, log_file_path);
  return answer;
}

t_uchar *
arch_inventory_tag (enum arch_tagging_method method, t_uchar * path)
{
  int errn;
  t_uchar * answer;
  enum ftag_method m;

  if (method == arch_unspecified_tagging)
    {
      t_uchar * dir = 0;
      t_uchar * root = 0;

      dir = file_name_directory_file (0, path);
      root = arch_tree_root (0, dir, 0);

      if (!root)
        method = arch_names_tagging;
      else
        {
          method = arch_tree_tagging_method (root, 0);
        }

      lim_free (0, dir);
      lim_free (0, root);
    }

  switch (method)
    {
    default:
      panic ("unrecognized method in arch_inventory_tag");
      break;

    case arch_names_tagging:
      {
        m = ftag_names;
        break;
      }
    case arch_explicit_tagging:
      {
        m = ftag_explicit;
        break;
      }
    case arch_implicit_tagging:
      {
        m = ftag_implicit;
        break;
      }
    }



  errn = 0;
  answer = file_tag (&errn, 0, m, path);

  if (!answer && errn)
    {
      safe_printfmt (2, "error finding file tag (%d: %s)\n path: %s\n", errn, errno_to_string(errn), path);
      panic ("arch_inventory_tag");
    }

  return answer;
}

t_uchar *
arch_tagging_method_name (enum arch_tagging_method m)
{
  switch (m)
    {
    default:
      panic ("unknown tagging method (arch_tagging_method_name)");
      return 0;                 /* not reached */

    case arch_names_tagging:		return str_save (0, "names");
    case arch_implicit_tagging:		return str_save (0, "implicit");
    case arch_explicit_tagging:		return str_save (0, "explicit");
    }
}


enum arch_tagging_method
arch_tagging_method_from_name (t_uchar * name)
{
  if (!str_casecmp (name, "explicit"))
    return arch_explicit_tagging;
  else if (!str_casecmp (name, "implicit"))
    return arch_implicit_tagging;
  else if (!str_casecmp (name, "names"))
    return arch_names_tagging;
  else
    {
      safe_printfmt (2, "no such tagging method (%s)\n", name);
      exit (2);
      return arch_names_tagging; /* notreached */
    }
}


t_uchar * 
arch_tree_tagging_method_file (t_uchar * tree_root)
{
  t_uchar * ctl_dir;
  t_uchar * answer;

  ctl_dir = arch_tree_ctl_dir (tree_root);
  answer = file_name_in_vicinity (0, ctl_dir, "=tagging-method");
  lim_free (0, ctl_dir);
  return answer;
}


enum arch_tagging_method
arch_tree_tagging_method (t_uchar * tree_root, int strict)
{
  t_uchar * file;
  int in_fd;
  int errn;
  t_uchar * contents;
  size_t len;
  t_uchar * pos;

  file = arch_tree_tagging_method_file (tree_root);

  in_fd = vu_open (&errn, file, O_RDONLY, 0);
  if (in_fd < 0)
    {
    no_method_set:
      if (strict)
        {
          safe_printfmt (2, "no tagging method set for tree\n  tree: %s\n", tree_root);
          exit (2);
        }
      else
        return arch_names_tagging;
    }

  contents = 0;
  len = 0;
  safe_file_to_string (&contents, &len, in_fd);
  safe_close (in_fd);

  pos = contents;
  while (1)
    {
      enum arch_tagging_method m;

      if (!len)
        goto no_method_set;

      while (len && char_is_blank (*pos))
        {
          ++pos;
          --len;
        }

      if ((len >= (sizeof ("explicit") - 1)) && !str_casecmp_n ("explicit", sizeof ("explicit") - 1, pos, sizeof ("explicit") - 1))
        {
          pos += sizeof ("explicit") - 1;
          len -= sizeof ("explicit") - 1;
          m = arch_explicit_tagging;

        maybe_found_it:

          while (len && char_is_blank (*pos))
            {
              ++pos;
              --len;
            }

          if (!len || (*pos == '\n'))
            return m;
          else
            {
              t_uchar * nxt;

              nxt = str_chr_index_n (pos, len, '\n');

              len -= (nxt + 1) - pos;
              pos = (nxt + 1);
            }
        }
      else if ((len >= (sizeof ("implicit") - 1)) && !str_casecmp_n ("implicit", sizeof ("implicit") - 1, pos, sizeof ("implicit") - 1))
        {
          pos += sizeof ("implicit") - 1;
          len -= sizeof ("implicit") - 1;
          m = arch_implicit_tagging;
          goto maybe_found_it;
        }
      else if ((len >= (sizeof ("names") - 1)) && !str_casecmp_n ("names", sizeof ("names") - 1, pos, sizeof ("names") - 1))
        {
          pos += sizeof ("names") - 1;
          len -= sizeof ("names") - 1;
          m = arch_names_tagging;
          goto maybe_found_it;
        }
      else
        {
          t_uchar * nxt;

          nxt = str_chr_index_n (pos, len, '\n');

          len -= (nxt + 1) - pos;
          pos = (nxt + 1);
        }
    }
}


void
arch_set_tree_tagging_method (t_uchar * tree_root,
                              enum arch_tagging_method method)
{
  int errn;
  t_uchar * method_name;
  t_uchar * method_file;
  t_uchar * method_dir;
  t_uchar * method_tmp;
  int out_fd;

  method_name = arch_tagging_method_name (method);
  method_file = arch_tree_tagging_method_file (tree_root);
  method_dir = file_name_directory (0, method_file);
  method_tmp = file_name_in_vicinity (0, method_dir, ",,tagging-method");

  vu_unlink (&errn, method_tmp);
  out_fd = safe_open (method_tmp, O_WRONLY | O_CREAT | O_EXCL, 0777);

  if (safe_access (method_file, F_OK))
    {
      safe_printfmt (out_fd, "%s\n", method_name);
    }
  else
    {
      int in_fd;
      t_uchar * line;
      long len;
      int emitted_method;

      in_fd = safe_open (method_file, O_RDONLY, 0);

      emitted_method = 0;

      while (1)
        {
          t_uchar * pos;
          t_uchar * lim;
          int line_maybe_specifies_method;
          enum arch_tagging_method maybe_method;
          int replace_with_method_name;

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

          lim = line + len;
          pos = line;

          line_maybe_specifies_method = 0;
          replace_with_method_name = 0;

          while ((pos < lim) && char_is_blank (*pos))
            ++pos;

          if (((lim - pos) >= (sizeof ("names") - 1)) && !str_casecmp_n ("names", sizeof ("names") - 1, pos, sizeof ("names") - 1))
            {
              line_maybe_specifies_method = 1;
              maybe_method = arch_names_tagging;
              pos += sizeof ("names") - 1;
            }
          else if (((lim - pos) >= (sizeof ("explicit") - 1)) && !str_casecmp_n ("explicit", sizeof ("explicit") - 1, pos, sizeof ("explicit") - 1))
            {
              line_maybe_specifies_method = 1;
              maybe_method = arch_explicit_tagging;
              pos += sizeof ("explicit") - 1;
            }
          else if (((lim - pos) >= (sizeof ("implicit") - 1)) && !str_casecmp_n ("implicit", sizeof ("implicit") - 1, pos, sizeof ("implicit") - 1))
            {
              line_maybe_specifies_method = 1;
              maybe_method = arch_implicit_tagging;
              pos += sizeof ("implicit") - 1;
            }

          if (line_maybe_specifies_method)
            {
              while ((pos < lim) && char_is_space (*pos))
                ++pos;
              if (pos == lim)
                replace_with_method_name = 1;
            }

          if (replace_with_method_name)
            {
              safe_printfmt (out_fd, "%s\n", method_name);
              emitted_method = 1;
            }
          else
            {
              safe_printfmt (out_fd, "%.*s", (int)len, line);
              if (len && (line[len - 1] != '\n'))
                safe_printfmt (out_fd, "\n");
            }
        }

      if (!emitted_method)
        safe_printfmt (out_fd, "%s\n", method_name);
    }

  safe_close (out_fd);
  safe_rename (method_tmp, method_file);

  lim_free (0, method_name);
  lim_free (0, method_file);
  lim_free (0, method_dir);
  lim_free (0, method_tmp);
}


t_uchar *
arch_explicit_tag_file_for (t_uchar * path)
{
  int errn;
  struct stat stat_buf;
  int is_file;
  t_uchar * parent_dir;
  t_uchar * dot_arch_dir;
  t_uchar * tag_file_basename;
  t_uchar * tag_file_path;

  if (vu_lstat (&errn, path, &stat_buf))
    {
      if (errn != ENOENT)
        {
          safe_printfmt (2, "i/o error (%d: %s) for vu_lstat of  %s\n", errn, errno_to_string (errn), path);
          exit (2);
        }
      is_file = 1;
    }
  else
    {
      is_file = !S_ISDIR (stat_buf.st_mode);
    }

  if (is_file)
    parent_dir = file_name_directory (0, path);
  else
    parent_dir = str_save (0, path);

  dot_arch_dir = file_name_in_vicinity (0, parent_dir, ".arch-ids");

  if (is_file)
    {
      tag_file_basename = file_name_tail (0, path);
      tag_file_basename = str_realloc_cat (0, tag_file_basename, ".id");
    }
  else
    tag_file_basename = str_save (0, "=id");


  tag_file_path = file_name_in_vicinity (0, dot_arch_dir, tag_file_basename);

  lim_free (0, parent_dir);
  lim_free (0, dot_arch_dir);
  lim_free (0, tag_file_basename);

  return tag_file_path;
}


t_uchar * 
arch_generate_tag (void)
{
  static unsigned long seq = 0;

  time_t now;
  char * now_str;
  t_uchar * nl;
  t_uchar * my_id;
  pid_t my_pid;
  t_uchar my_pid_str[128];
  t_uchar seq_str[128];
  t_uchar * tag;

  if (0 > time (&now))
    panic ("unable to get time of day in arch_generate_tag");

  now_str = ctime (&now);
  nl = str_chr_index (now_str, '\n');
  if (nl)
    *nl = 0;
  my_id = arch_my_id ();
  my_pid = getpid ();
  cvt_ulong_to_decimal (my_pid_str, (unsigned long)my_pid);
  cvt_ulong_to_decimal (seq_str, (unsigned long)seq);
  ++seq;

  tag = str_alloc_cat_many (0, my_id, " ", now_str, " ", my_pid_str, ".", seq_str, str_end);

  lim_free (0, my_id);
  return tag;
}


void
arch_add_explicit_tag (t_uchar * path, t_uchar * tag)
{
  t_uchar * tag_file;
  t_uchar * tag_dir;
  int out_fd;

  tag_file = arch_explicit_tag_file_for (path);

  if (!safe_access (tag_file, F_OK))
    {
      safe_printfmt (2, "attempt to tag already tagged file: %s\n", path);
      exit (2);
    }

  tag_dir = file_name_directory (0, tag_file);

  ensure_directory_exists (tag_dir);
  out_fd = safe_open (tag_file, O_WRONLY | O_CREAT | O_EXCL, 0777);
  safe_printfmt (out_fd, "%s\n", tag);
  safe_close (out_fd);

  lim_free (0, tag_file);
  lim_free (0, tag_dir);
}


void
arch_delete_explicit_tag (t_uchar * path)
{
  t_uchar * tag_file;

  tag_file = arch_explicit_tag_file_for (path);

  if (!safe_access (tag_file, F_OK))
    safe_unlink (tag_file);
  else
    {
      safe_printfmt (2, "attempt to remove non-existent tag for %s\n", path);
      exit (2);
    }

  lim_free (0, tag_file);
}


void
arch_move_explicit_tag (t_uchar * from, t_uchar * to)
{
  t_uchar * old_tag_file;
  t_uchar * new_tag_file;
  t_uchar * new_tag_dir;

  old_tag_file = arch_explicit_tag_file_for (from);
  new_tag_file = arch_explicit_tag_file_for (to);
  new_tag_dir = file_name_directory (0, new_tag_file);

  ensure_directory_exists (new_tag_dir);
  safe_link (old_tag_file, new_tag_file);
  safe_unlink (old_tag_file);

  lim_free (0, old_tag_file);
  lim_free (0, new_tag_file);
  lim_free (0, new_tag_dir);
}


t_uchar *
arch_strong_explicit_dflt_file (t_uchar * dir)
{
  t_uchar * dot_arch_dir;
  t_uchar * answer;

  dot_arch_dir = file_name_in_vicinity (0, dir, ".arch-ids");
  answer = file_name_in_vicinity (0, dot_arch_dir, "=all");

  lim_free (0, dot_arch_dir);
  return answer;
}

t_uchar *
arch_weak_explicit_dflt_file (t_uchar * dir)
{
  t_uchar * dot_arch_dir;
  t_uchar * answer;

  dot_arch_dir = file_name_in_vicinity (0, dir, ".arch-ids");
  answer = file_name_in_vicinity (0, dot_arch_dir, "=default");

  lim_free (0, dot_arch_dir);
  return answer;
}

t_uchar *
arch_dont_care_explicit_dflt_file (t_uchar * dir)
{
  t_uchar * dot_arch_dir;
  t_uchar * answer;

  dot_arch_dir = file_name_in_vicinity (0, dir, ".arch-ids");
  answer = file_name_in_vicinity (0, dot_arch_dir, "=dont-care");

  lim_free (0, dot_arch_dir);
  return answer;
}

void
arch_delete_strong_explicit_default (t_uchar * dir)
{
  t_uchar * file;

  file = arch_strong_explicit_dflt_file (dir);

  if (safe_access (file, F_OK))
    {
      safe_printfmt (2, "attempt to delete non-existing strong explicit default in %s\n", dir);
      exit (2);
    }

  safe_unlink (dir);
  lim_free (0, file);
}


void
arch_delete_weak_explicit_default (t_uchar * dir)
{
  t_uchar * file;

  file = arch_weak_explicit_dflt_file (dir);

  if (safe_access (file, F_OK))
    {
      safe_printfmt (2, "attempt to delete non-existing weak explicit default in %s\n", dir);
      exit (2);
    }

  safe_unlink (dir);
  lim_free (0, file);
}

void
arch_delete_dont_care_explicit_default (t_uchar * dir)
{
  t_uchar * file;

  file = arch_dont_care_explicit_dflt_file (dir);

  if (safe_access (file, F_OK))
    {
      safe_printfmt (2, "attempt to delete non-existing dont-care explicit default in %s\n", dir);
      exit (2);
    }

  safe_unlink (dir);
  lim_free (0, file);
}


void
arch_set_strong_explicit_default (t_uchar * dir, t_uchar * tag)
{
  t_uchar * file;
  t_uchar * file_dir;
  int out_fd;

  file = arch_strong_explicit_dflt_file (dir);
  file_dir = file_name_directory (0, file);

  if (!safe_access (file, F_OK))
    {
      safe_printfmt (2, "attempt to overwrite strong explicit default in %s\n", dir);
      exit (2);
    }

  ensure_directory_exists (file_dir);
  out_fd = safe_open (file, O_WRONLY | O_CREAT | O_EXCL, 0777);
  safe_printfmt (out_fd, "%s\n", tag);
  safe_close (out_fd);

  lim_free (0, file);
  lim_free (0, file_dir);
}

void
arch_set_weak_explicit_default (t_uchar * dir, t_uchar * tag)
{
  t_uchar * file;
  t_uchar * file_dir;
  int out_fd;

  file = arch_weak_explicit_dflt_file (dir);
  file_dir = file_name_directory (0, file);

  if (!safe_access (file, F_OK))
    {
      safe_printfmt (2, "attempt to overwrite weak explicit default in %s\n", dir);
      exit (2);
    }

  ensure_directory_exists (file_dir);
  out_fd = safe_open (file, O_WRONLY | O_CREAT | O_EXCL, 0777);
  safe_printfmt (out_fd, "%s\n", tag);
  safe_close (out_fd);

  lim_free (0, file);
  lim_free (0, file_dir);
}

void
arch_set_dont_care_explicit_default (t_uchar * dir)
{
  t_uchar * file;
  t_uchar * file_dir;
  int out_fd;

  file = arch_dont_care_explicit_dflt_file (dir);
  file_dir = file_name_directory (0, file);

  if (safe_access (file, F_OK))
    {
      ensure_directory_exists (file_dir);
      out_fd = safe_open (file, O_WRONLY | O_CREAT | O_EXCL, 0777);
      safe_close (out_fd);
    }

  lim_free (0, file);
  lim_free (0, file_dir);
}





/* tag: Tom Lord Wed May 14 07:20:26 2003 (inv-tags.c)
 */
