/* patch-logs.c:
 *
 ****************************************************************
 * 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/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "tla/libfsutils/dir-listing.h"
#include "tla/libfsutils/string-files.h"
#include "tla/libfsutils/file-contents.h"
#include "tla/libfsutils/ensure-dir.h"
#include "tla/libfsutils/rmrf.h"
#include "tla/libfsutils/copy-file.h"
#include "tla/libawk/trim.h"
#include "tla/libarch/arch.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/merge-points.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/patch-logs.h"



t_uchar *
arch_make_log_file (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  t_uchar * log_name = 0;
  t_uchar * log_path = 0;

  invariant (arch_valid_archive_name (archive));
  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));

  /* The VI-user flame generator:
   */
  log_name = str_alloc_cat_many (0, "++log.", version, "--", archive, str_end);
  log_path = file_name_in_vicinity (0, tree_root, log_name);

  lim_free (0, log_name);
  return log_path;
}


t_uchar *
arch_make_log (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  int errn;
  t_uchar * log_path = 0;
  int out_fd;

  log_path = arch_make_log_file (tree_root, archive, version);

  out_fd = vu_open (&errn, log_path, O_WRONLY | O_CREAT | O_EXCL, 0666);

  if (out_fd >= 0)
    {
      safe_printfmt (out_fd, "Summary: \nKeywords: \n\n");
      safe_close (out_fd);
    }
  else if (errn != EEXIST)
    {
      safe_printfmt (2, "arch_make_log: I/O error (%s) creating %s\n", errno_to_string (errn), log_path);
      exit (2);
    }

  return log_path;
}

t_uchar *
arch_auto_log_message (t_uchar * tree_root, t_uchar * archive, t_uchar * version, t_uchar * summary, t_uchar * body)
{
  t_uchar * cooked_summary = 0;
  t_uchar * merge_summary = 0;
  t_uchar * answer = 0;

  cooked_summary = trim_surrounding_ws (str_save (0, summary));
  merge_summary = arch_log_for_merge (tree_root, archive, version, 0);

  answer = str_alloc_cat_many (0,
                               "Summary: ", summary, "\n",
                               "Keywords: \n",
                               "\n",
                               body, (body ? "\n" : 0),
                               (body ? "\n" : 0),
                               merge_summary,
                               str_end);

  lim_free (0, cooked_summary);
  lim_free (0, merge_summary);
  return answer;
}


t_uchar *
arch_log_for_merge (t_uchar * tree_root, t_uchar * archive, t_uchar * version, int reverse)
{
  rel_table new_logs = 0;
  int string_fd;

  new_logs = arch_new_in_version (tree_root, archive, version);

  if (reverse)
    arch_sort_table_by_name_field (1, new_logs, 0);

  string_fd = make_output_to_string_fd ();

  if (new_logs)
    {
      int x;

      safe_printfmt (string_fd, "Patches applied:\n\n");

      for (x = 0; x < rel_n_records (new_logs); ++x)
        {
          t_uchar * archive = 0;
          t_uchar * revision = 0;
          t_uchar * log_file = 0;
          t_uchar * log = 0;
          assoc_table headers = 0;
          t_uchar * summary;

          archive = arch_parse_package_name (arch_ret_archive, 0, new_logs[x][0]);
          revision = arch_parse_package_name (arch_ret_non_archive, 0, new_logs[x][0]);

          invariant (!!archive && !!revision);

          log_file = arch_log_file (tree_root, archive, revision);
          log = file_contents (log_file);

          arch_parse_log (0, &headers, 0, log);
          summary = assoc_ref (headers, "summary");

          safe_printfmt (string_fd, " * %s\n", new_logs[x][0]);
          safe_printfmt (string_fd, "   %s\n\n", (summary ? summary : (t_uchar *)"<no summary provided>"));

          lim_free (0, archive);
          lim_free (0, revision);
          lim_free (0, log_file);
          lim_free (0, log);
          free_assoc_table (headers);
        }
    }

  rel_free_table (new_logs);
  return string_fd_close (string_fd);
}


t_uchar *
arch_log_dir (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  t_uchar * category;
  t_uchar * package;
  t_uchar * package_version;

  t_uchar * t;
  t_uchar * log_dir;

  invariant (arch_valid_archive_name (archive));
  invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));

  category = arch_parse_package_name (arch_ret_category, 0, version);
  package = arch_parse_package_name (arch_ret_package, 0, version);
  package_version = arch_parse_package_name (arch_ret_package_version, 0, version);

  log_dir = arch_tree_ctl_dir (tree_root);
  t = file_name_in_vicinity (0, log_dir, category);
  lim_free (0, log_dir);
  log_dir = t;
  t = file_name_in_vicinity (0, log_dir, package);
  lim_free (0, log_dir);
  log_dir = t;
  t = file_name_in_vicinity (0, log_dir, package_version);
  lim_free (0, log_dir);
  log_dir = t;
  t = file_name_in_vicinity (0, log_dir, archive);
  lim_free (0, log_dir);
  log_dir = t;
  t = file_name_in_vicinity (0, log_dir, "patch-log");
  lim_free (0, log_dir);
  log_dir = t;

  lim_free (0, category);
  lim_free (0, package);
  lim_free (0, package_version);

  return log_dir;
}


t_uchar *
arch_log_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * version;
  t_uchar * dir;
  t_uchar * lvl;
  t_uchar * file;

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);

  dir = arch_log_dir (tree_root, archive, version);
  lvl = arch_parse_package_name (arch_ret_patch_level, 0, revision);
  file = file_name_in_vicinity (0, dir, lvl);

  lim_free (0, version);
  lim_free (0, dir);
  lim_free (0, lvl);
  return file;
}

t_uchar *
arch_log_contents (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * file = 0;
  t_uchar * answer = 0;

  if (!arch_tree_has_log (tree_root, archive, revision))
    return 0;

  file = arch_log_file (tree_root, archive, revision);
  answer = file_contents (file);
  lim_free (0, file);

  return answer;
}



int
arch_tree_has_log (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * file = 0;
  int answer;

  file = arch_log_file (tree_root, archive, revision);

  answer = !safe_access (file, F_OK);

  lim_free (0, file);

  return answer;
}

void
arch_add_log_version (t_uchar * tree_root, t_uchar * archive,
                      t_uchar * version)
{
  t_uchar * log_dir;

  log_dir = arch_log_dir (tree_root, archive, version);
  ensure_directory_exists (log_dir);

  lim_free (0, log_dir);
}


void
arch_remove_log_version (t_uchar * tree_root, t_uchar * archive,
                         t_uchar * version)
{
  t_uchar * log_dir;

  log_dir = arch_log_dir (tree_root, archive, version);
  rmrf_file (log_dir);

  lim_free (0, log_dir);
}

int
arch_has_patch_log (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  t_uchar * dir = 0;
  int answer = 0;

  dir = arch_log_dir (tree_root, archive, version);

  if (!safe_access (dir, F_OK))
    answer = 1;

  lim_free (0, dir);
  return answer;
}



rel_table
arch_log_versions (t_uchar * tree_root,
                   t_uchar * only_archive,
                   t_uchar * only_category,
                   t_uchar * only_branch,
                   t_uchar * only_version)
{
  t_uchar * logs_root;
  int lim;
  int x;
  rel_table dir_listing;
  rel_table categories;
  int c_lim;
  int c;
  rel_table branches;           /* [0] == cat--branch,  [1] ==  path of branch's log dir */
  int b_lim;
  int b;
  rel_table versions;           /* [0] == cat--branch--vsn,  [1] ==  path of vsn's log dir */
  int v_lim;
  int v;
  rel_table log_versions;               /* [0] == fqversion, [1] == patch-log dir for fqversion  */

  logs_root = arch_tree_ctl_dir (tree_root);


  if (only_version)
    only_version = str_save (0, only_version);

  if (only_version && !only_branch)
    {
      only_branch = arch_parse_package_name (arch_ret_package, 0, only_version);
    }
  else if (only_branch)
    only_branch = str_save (0, only_branch);

  if (only_branch && !only_category)
    {
      only_category = arch_parse_package_name (arch_ret_category, 0, only_branch);
    }
  else if (only_category)
    only_category = str_save (0, only_category);



  /****************************************************************
   * Get a list of all categories in the log
   */

  dir_listing = directory_files (logs_root);

  categories = 0;
  lim = rel_n_records (dir_listing);
  for (x = 0; x < lim; ++x)
    {
      if (only_category && str_cmp (dir_listing[x][0], only_category))
        continue;

      if (arch_valid_package_name (dir_listing[x][0], arch_no_archive, arch_req_category, 0))
        rel_add_records (&categories, rel_copy_record (dir_listing[x]), 0);
    }
  rel_free_table (dir_listing);

  /****************************************************************
   * For each category, get a list of each branch for it in the log
   */

  branches = 0;
  c_lim = rel_n_records (categories);
  for (c = 0; c < c_lim; ++c)
    {
      t_uchar * category;
      t_uchar * cat_dir;

      category = categories[c][0];
      cat_dir = file_name_in_vicinity (0, logs_root, category);

      dir_listing = directory_files (cat_dir);
      lim = rel_n_records (dir_listing);
      for (x = 0; x < lim; ++x)
        {
          t_uchar * maybe_branch;
          t_uchar * branch_cat;

          maybe_branch = dir_listing[x][0];

          if (only_branch && str_cmp (only_branch, maybe_branch))
            continue;

          if (!arch_valid_package_name (maybe_branch, arch_no_archive, arch_req_package, 0))
            continue;

          branch_cat = arch_parse_package_name (arch_ret_category, 0, maybe_branch);
          if (!str_cmp (category, branch_cat))
            {
              t_uchar * branch_path;

              branch_path = file_name_in_vicinity (0, cat_dir, maybe_branch);
              rel_add_records (&branches, rel_make_record (maybe_branch, branch_path, 0), 0);
              lim_free (0, branch_path);
            }
          lim_free (0, branch_cat);
        }

      rel_free_table (dir_listing);
      lim_free (0, cat_dir);
    }

  rel_free_table (categories);

  /****************************************************************
   * For each branch, get a list of each version for it in the log
   */


  versions = 0;
  b_lim = rel_n_records (branches);
  for (b = 0; b < b_lim; ++b)
    {
      t_uchar * branch;
      t_uchar * branch_dir;

      branch = branches[b][0];
      branch_dir = branches[b][1];

      dir_listing = directory_files (branch_dir);
      lim = rel_n_records (dir_listing);
      for (x = 0; x < lim; ++x)
        {
          t_uchar * maybe_version;
          t_uchar * version_pkg;

          maybe_version = dir_listing[x][0];

          if (only_version && str_cmp (only_version, maybe_version))
            continue;

          if (!arch_valid_package_name (maybe_version, arch_no_archive, arch_req_version, 0))
            continue;

          version_pkg = arch_parse_package_name (arch_ret_package, 0, maybe_version);
          if (!str_cmp (branch, version_pkg))
            {
              t_uchar * version_path;

              version_path = file_name_in_vicinity (0, branch_dir, maybe_version);
              rel_add_records (&versions, rel_make_record (maybe_version, version_path, 0), 0);
              lim_free (0, version_path);
            }
          lim_free (0, version_pkg);
        }

      rel_free_table (dir_listing);
    }

  rel_free_table (branches);


  /****************************************************************
   * For each version, get a list of each archive for it in the log
   */

  log_versions = 0;
  v_lim = rel_n_records (versions);
  for (v = 0; v < v_lim; ++v)
    {
      t_uchar * version;
      t_uchar * vsn_dir;

      version = versions[v][0];
      vsn_dir = versions[v][1];

      dir_listing = directory_files (vsn_dir);
      lim = rel_n_records (dir_listing);
      for (x = 0; x < lim; ++x)
        {
          t_uchar * maybe_archive;
          t_uchar * archive_path;
          t_uchar * patch_log_path;

          maybe_archive = dir_listing[x][0];

          if (only_archive && str_cmp (only_archive, maybe_archive))
            continue;

          if (!arch_valid_archive_name (maybe_archive))
            continue;

          archive_path = file_name_in_vicinity (0, vsn_dir, maybe_archive);
          patch_log_path = file_name_in_vicinity (0, archive_path, "patch-log");

          if (!safe_access (patch_log_path, F_OK))
            {
              t_uchar * fqversion;

              fqversion = arch_fully_qualify (maybe_archive, version);
              rel_add_records (&log_versions, rel_make_record (fqversion, patch_log_path, 0), 0);
              lim_free (0, fqversion);
            }

          lim_free (0, archive_path);
          lim_free (0, patch_log_path);
        }

      rel_free_table (dir_listing);
    }

  rel_free_table (versions);

  if (only_category)
    lim_free (0, only_category);
  if (only_branch)
    lim_free (0, only_branch);
  if (only_version)
    lim_free (0, only_version);

  return log_versions;
}


t_uchar *
arch_highest_patch_level (t_uchar * tree_root, t_uchar * archive, t_uchar * version)
{
  rel_table logs = 0;
  t_uchar * answer = 0;

  logs = arch_logs (tree_root, archive, version, 0);

  if (logs)
    answer = str_save (0, logs[rel_n_records(logs) - 1][0]);

  rel_free_table (logs);
  return answer;
}


t_uchar *
arch_latest_logged_version (t_uchar * tree_root, t_uchar * archive, t_uchar * package)
{
  rel_table log_versions = 0;
  t_uchar * answer = 0;

  log_versions = arch_log_versions (tree_root, archive, 0, package, 0);
  arch_sort_table_by_name_field (1, log_versions, 0);

  if (log_versions)
    {
      answer = arch_parse_package_name (arch_ret_non_archive, 0, log_versions[0][0]);
    }

  rel_free_table (log_versions);
  return answer;
}


rel_table
arch_logs (t_uchar * tree_root, t_uchar * archive, t_uchar * version, int full)
{
  rel_table answer;             /* [0] == lvl, [2] == path */
  t_uchar * log_dir;
  rel_table dir_listing;
  int lim;
  int x;

  answer = 0;

  log_dir = arch_log_dir (tree_root, archive, version);

  if (safe_access (log_dir, F_OK))
    goto leave;

  dir_listing = directory_files (log_dir);

  lim = rel_n_records (dir_listing);
  for (x = 0; x < lim; ++x)
    {
      if (arch_valid_patch_level_name (dir_listing[x][0]))
        {
          t_uchar * path = 0;

          path = file_name_in_vicinity (0, log_dir, dir_listing[x][0]);

          if (!full)
            {
              rel_add_records (&answer, rel_make_record (dir_listing[x][0], path, 0), 0);
            }
          else
            {
              t_uchar * full_name = 0;

              full_name = str_alloc_cat_many (0, archive, "/", version, "--", dir_listing[x][0], str_end);
              rel_add_records (&answer, rel_make_record (full_name, path, 0), 0);

              lim_free (0, full_name);
            }

          lim_free (0, path);
        }
    }

 leave:
  lim_free (0, log_dir);

  if (full)
    arch_sort_table_by_name_field (0, answer, 0);
  else
    arch_sort_table_by_patch_level_field (0, answer, 0);
  return answer;
}

int
arch_valid_log_file (t_uchar * log)
{
  size_t len;
  t_uchar * pos;

  len = str_length (log);
  pos = log;

  /* Parse the headers.
   */

  while (1)
    {
      t_uchar * eol;
      t_uchar * colon;

      eol = str_chr_index (pos, '\n');
      if (!eol)
        eol = pos + len;

      if (eol == pos)
        break;

      colon = str_chr_index_n (pos, eol - pos, ':');
      if (!colon)
        return 0;

      len -= (1 + (colon - pos));
      pos = colon + 1;

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

      while (*eol && char_is_blank (eol[1]))
        {
          eol = str_chr_index (eol + 1, '\n');
          if (!eol)
            eol = pos + len;
        }

      if (*eol)
        ++eol;

      len -= (eol - pos);
      pos = eol;
    }

  /* skip the blank line */

  if (!*pos)
    return 0;

  return 1;
}


void
arch_parse_log (rel_table * headers_list, assoc_table * headers, t_uchar ** body, t_uchar * log)
{
  size_t len;
  t_uchar * pos;

  len = str_length (log);
  pos = log;

  /* Parse and assemble the headers.
   */

  while (1)
    {
      t_uchar * eol;
      t_uchar * colon;
      t_uchar * field_name;
      t_uchar * field_value;

      eol = str_chr_index (pos, '\n');
      if (!eol)
        eol = pos + len;

      if (eol == pos)
        break;

      colon = str_chr_index_n (pos, eol - pos, ':');
      if (!colon)
        panic ("bogusly formatted log message (missing field name in headers)");

      field_name = str_save_n (0, pos, colon - pos);
      {
        size_t x;
        size_t l;

        l = str_length (field_name);
        for (x = 0; x < l; ++x)
          field_name[x] = char_to_lower (field_name[x]);
      }


      len -= (1 + (colon - pos));
      pos = colon + 1;

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

      while (*eol && char_is_blank (eol[1]))
        {
          eol = str_chr_index (eol + 1, '\n');
          if (!eol)
            eol = pos + len;
        }

      field_value = str_save_n (0, pos, eol - pos);

      if (headers)
        assoc_set (headers, field_name, field_value);
      if (headers_list)
        rel_add_records (headers_list, rel_make_record (field_name, field_value, 0), 0);

      lim_free (0, field_name);
      lim_free (0, field_value);

      if (*eol)
        ++eol;

      len -= (eol - pos);
      pos = eol;
    }

  /* skip the blank line */

  if (*pos)
    ++pos;

  /* save the body */
  if (body)
    *body = str_save (0, pos);
}


void
arch_print_headers_summary (int out_fd, int indent_level, assoc_table headers, int summarized_headers)
{
  if (arch_include_date & summarized_headers)
    {
      t_uchar * d;
      t_uchar * separator;

      if (arch_include_creator & summarized_headers)
        {
          separator = "";
        }
      else
        {
          separator = "\n";
        }

      d = assoc_ref (headers, "standard-date");

      if (!d)
        safe_printfmt (out_fd, "%*s<<<no date: header>>>%s", indent_level, "", separator);
      else
        safe_printfmt (out_fd, "%*s%s%s", indent_level, "", d, separator);
    }

  if (arch_include_creator & summarized_headers)
    {
      t_uchar * c;
      int creator_indent;

      if (arch_include_date & summarized_headers)
        {
          creator_indent = 6;
        }
      else
        {
          creator_indent = indent_level;
        }

      c = assoc_ref (headers, "creator");
      if (!c)
        safe_printfmt (out_fd, "%*s<<<no creator: header>>>\n", creator_indent, "");
      else
        safe_printfmt (out_fd, "%*s%s\n", creator_indent, "", c);
    }

  if (arch_include_summary & summarized_headers)
    {
      t_uchar * s;

      s = assoc_ref (headers, "summary");
      if (!s)
        safe_printfmt (out_fd, "%*s<<<no summary: header>>>\n", indent_level, "");
      else
        {
          rel_table s_lines = 0;
          int x;

          s_lines = rel_nl_split (s);

          for (x = 0; x < rel_n_records (s_lines); ++x)
            {
              s_lines[x][0] = trim_surrounding_ws (s_lines[x][0]);
              safe_printfmt (out_fd, "%*s%s\n", indent_level, "", s_lines[x][0]);
            }

          rel_free_table (s_lines);
        }
    }

  if (summarized_headers & (arch_include_foreign_merges | arch_include_local_merges))
    {
      t_uchar * this_archive = 0;
      t_uchar * this_revision = 0;
      t_uchar * this_fqrevision = 0;
      t_uchar * raw_merges = 0;
      rel_table new_patches = 0;
      int any = 0;
      int x;

      this_archive = trim_surrounding_ws (str_save (0, assoc_ref (headers, "archive")));
      this_revision = trim_surrounding_ws (str_save (0, assoc_ref (headers, "revision")));
      this_fqrevision = arch_fully_qualify (this_archive, this_revision);

      raw_merges = trim_surrounding_ws (str_save (0, assoc_ref (headers, "new-patches")));

      new_patches = rel_ws_split (raw_merges);
      arch_sort_table_by_name_field (0, new_patches, 0);

      for (x = 0; x < rel_n_records (new_patches); ++x)
        {
          if (arch_valid_package_name (new_patches[x][0], arch_req_archive, arch_req_patch_level, 0)
              && str_cmp (new_patches[x][0], this_fqrevision))
            {
              t_uchar * patch_archive = 0;

              patch_archive = arch_parse_package_name (arch_ret_archive, 0, new_patches[x][0]);

              if (str_cmp (patch_archive, this_archive)
                  ? (summarized_headers & arch_include_foreign_merges)
                  : (summarized_headers & arch_include_local_merges))
                {
                  if (!any)
                    {
                      if (arch_include_summary & summarized_headers)
                        safe_printfmt (out_fd, "\n");

                      safe_printfmt (out_fd, "%*smerges in:\n", indent_level, "");
                      any = 1;
                    }
                  safe_printfmt (out_fd, "%*s%s\n", indent_level + 2, "", new_patches[x][0]);
                }

              lim_free (0, patch_archive);
            }
        }

      lim_free (0, this_archive);
      lim_free (0, this_revision);
      lim_free (0, this_fqrevision);
      lim_free (0, raw_merges);
      rel_free_table (new_patches);
    }
}


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

void
arch_print_log_list_header (int out_fd, t_uchar * header, rel_table list, int field)
{
  int num_recs = rel_n_records (list);
  int escape_classes = arch_escape_classes;

  if (num_recs > 0)
    {
      int x;
      size_t col;
      int any_on_this_line;

      safe_printfmt (out_fd, "%s: ", header);
      col = str_length (header) + 2;
      any_on_this_line = 0;

      for (x = 0; x < num_recs; ++x)
        {
          t_uchar * escape_tmp = 0;
          size_t len_this;

          escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes, no_dot (list[x][field]));
          len_this = str_length (escape_tmp);

          if (!any_on_this_line)
            {
              safe_printfmt (out_fd, "%s", escape_tmp);
              col += len_this;
              any_on_this_line = 1;
            }
          else
            {
              if ((col + 1 + len_this) >= 60)
                {
                  safe_printfmt (out_fd, "\n    %s", escape_tmp);
                  col = 4 + len_this;
                  any_on_this_line = 1;
                }
              else
                {
                  safe_printfmt (out_fd, " %s", escape_tmp);
                  col += len_this;
                  any_on_this_line = 1;
                }
            }

          lim_free (0, escape_tmp);
        }
      safe_printfmt (out_fd, "\n");
    }
}


void
arch_print_log_pairs_header (int out_fd, t_uchar * header, rel_table list, int field_a, int field_b)
{
  int num_recs = rel_n_records (list);
  int escape_classes = arch_escape_classes;

  if (num_recs > 0)
    {
      int x;
      int once;

      safe_printfmt (out_fd, "%s: ", header);

      once = 0;

      for (x = 0; x < num_recs; ++x)
        {
          t_uchar * escape_tmp_a;
          t_uchar * escape_tmp_b;
          escape_tmp_a = pika_save_escape_iso8859_1 (0, 0, escape_classes, list[x][field_a]);
          escape_tmp_b = pika_save_escape_iso8859_1 (0, 0, escape_classes, list[x][field_b]);
          safe_printfmt (out_fd, "%s%s %s", (once ? "\n    " : ""),
                         no_dot (escape_tmp_a), no_dot (escape_tmp_b));
          once = 1;
          lim_free (0, escape_tmp_b);
          lim_free (0, escape_tmp_a);
        }
      safe_printfmt (out_fd, "\n");
    }
}


void
arch_copy_to_patch_log (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar * log_file)
{
  int ign;
  t_uchar * version = 0;
  t_uchar * lvl = 0;
  t_uchar * log_dir = 0;
  t_uchar * dest_path = 0;
  t_uchar * dest_dir = 0;
  t_uchar * tmp_path = 0;

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

  log_dir = arch_log_dir (tree_root, archive, version);
  dest_path = file_name_in_vicinity (0, log_dir, lvl);
  dest_dir = file_name_directory_file (0, dest_path);
  tmp_path = file_name_in_vicinity (0, dest_dir, ",,new-log");

  ensure_directory_exists (log_dir);

  vu_unlink (&ign, tmp_path);

  copy_file (log_file, tmp_path);

  safe_rename (tmp_path, dest_path);

  lim_free (0, version);
  lim_free (0, lvl);
  lim_free (0, log_dir);
  lim_free (0, dest_path);
  lim_free (0, dest_dir);
  lim_free (0, tmp_path);
}

void
arch_rename_to_patch_log (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar * log_file)
{
  t_uchar * version = 0;
  t_uchar * lvl = 0;
  t_uchar * log_dir = 0;
  t_uchar * dest_path = 0;

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

  log_dir = arch_log_dir (tree_root, archive, version);
  dest_path = file_name_in_vicinity (0, log_dir, lvl);

  ensure_directory_exists (log_dir);
  safe_rename (log_file, dest_path);

  lim_free (0, version);
  lim_free (0, lvl);
  lim_free (0, log_dir);
  lim_free (0, dest_path);
}

rel_table
arch_all_logs (t_uchar * tree_root)
{
  rel_table log_versions = 0;
  rel_table answer = 0;
  int x;

  log_versions = arch_log_versions (tree_root, 0, 0, 0, 0);

  for (x = 0; x < rel_n_records (log_versions); ++x)
    {
      t_uchar * archive = 0;
      t_uchar * version = 0;
      rel_table in_this_log = 0;

      archive = arch_parse_package_name (arch_ret_archive, 0,
                                         log_versions[x][0]);
      version = arch_parse_package_name (arch_ret_non_archive, 0,
                                         log_versions[x][0]);

      in_this_log = arch_logs (tree_root, archive, version, 1);
      rel_append_x (&answer, in_this_log);

      lim_free (0, archive);
      lim_free (0, version);
      rel_free_table (in_this_log);
    }

  arch_sort_table_by_name_field (0, answer, 0);

  rel_free_table (log_versions);
  return answer;
}




/* tag: Tom Lord Mon May 12 19:20:16 2003 (patch-logs.c)
 */
