/* changelogs.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/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/rx-posix/regex.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/inv-ids.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/changelogs.h"



#define arch_changelog_id_re "^([ix]_automatic-ChangeLog--)(" arch_archive_re ")/(" arch_base_re ")(--" arch_base_re ")?(--" arch_vsn_re ")$"


/* __STDC__ prototypes for static functions */
static regex_t * changelog_id_pattern (void);
static void generate_changelog_entry (int out_fd, const t_uchar * archive, const t_uchar * version, const t_uchar * patch_lvl, const t_uchar * file, int no_files);
static void changelog_file_list (int out_fd, assoc_table headers, const t_uchar * header, const t_uchar * exclude);
static void changelog_file_pair_list (int out_fd, assoc_table headers, const t_uchar * header);



int
arch_id_indicates_changelog (const t_uchar * id)
{
  regex_t * pattern;

  pattern = changelog_id_pattern ();

  if (!regexec (pattern, id, 0, 0, 0))
    return 1;
  else
    return 0;
}

void
arch_parse_changelog_id (t_uchar ** archive, t_uchar ** version, const t_uchar * id)
{
  regex_t * pattern;
  regmatch_t pmatch[6];

  pattern = changelog_id_pattern ();

  invariant (!regexec (pattern, id, 6, pmatch, 0));
  *archive = str_save_n (0, id + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
  *version = str_save_n (0, id + pmatch[3].rm_so, pmatch[5].rm_eo - pmatch[3].rm_so);
}


void
arch_generate_changelog (int out_fd,
                         const t_uchar * tree_root,
                         int no_files,
                         int untagged,
                         const t_uchar * new_entry_patch_lvl,
                         const t_uchar * new_entry_file,
                         const t_uchar * archive,
                         const t_uchar * version)
{
  rel_table log_ls;
  int lim;
  int x;

  if (!arch_valid_package_name (version, arch_no_archive, arch_req_version, 0))
    {
      safe_printfmt (2, "changelog: invalid version name (%s)\n", version);
      exit (1);
    }

  log_ls = arch_logs (tree_root, archive, version, 0);
  arch_sort_table_by_patch_level_field (1, log_ls, 0);

  {
    t_uchar * tag_name;

    if (untagged)
      tag_name = "non-id";
    else if (arch_implicit_id_tagging == arch_tree_id_tagging_method (0, tree_root, 0))
      tag_name = "tag";
    else
      tag_name = "arch-tag";

    safe_printfmt (out_fd, "# do not edit -- automatically generated by arch changelog\n# %s: automatic-ChangeLog--%s/%s\n#\n\n", tag_name, archive, version);
  }

  if (new_entry_patch_lvl)
    {
      generate_changelog_entry (out_fd, archive, version, new_entry_patch_lvl, new_entry_file, no_files);
    }

  lim = rel_n_records (log_ls);
  for (x = 0; x < lim; ++x)
    {
      generate_changelog_entry (out_fd, archive, version, rel_peek_str (log_ls, x, 0), rel_peek_str (log_ls, x, 1), no_files);
    }

  rel_free_table (log_ls);
}





static regex_t *
changelog_id_pattern (void)
{
  static int compiled = 0;
  static regex_t pattern;

  if (!compiled)
    {
      if (regcomp (&pattern, arch_changelog_id_re, REG_EXTENDED))
        panic ("unable to compile arch_changelog_id_re");
      compiled = 1;
    }

  return &pattern;
}



static void
generate_changelog_entry (int out_fd, const t_uchar * archive, const t_uchar * version, const t_uchar * patch_lvl, const t_uchar * file, int no_files)
{
  int in_fd;
  t_uchar * log = 0;
  size_t len;
  assoc_table headers = 0;
  t_uchar * body = 0;

  in_fd = safe_open (file, O_RDONLY, 0);
  safe_file_to_string (&log, &len, in_fd);
  safe_close (in_fd);
  log = lim_realloc (0, log, len + 1);
  log[len] = 0;
  headers = 0;
  body = 0;
  arch_parse_log (0, &headers, (const t_uchar **)&body, log);
  lim_free (0, log);

  /* print the first line (date, creator, patch level)
   */

  {
    const t_uchar * raw_date;
    t_uchar * cooked_date = 0;
    const t_uchar * creator;

    raw_date = assoc_get_str_taking (headers, rel_make_field_str ("standard-date"));
    if (raw_date)
      {
        const t_uchar * from;
        const t_uchar * to;

        from = raw_date;
        while (*from && char_is_space (*from))
          ++from;

        to = from;
        while (*to && !char_is_space (*to))
          ++to;
        while (*to && char_is_space (*to))
          ++to;
        while (*to && !char_is_space (*to))
          ++to;

        cooked_date = str_save_n (0, from, to - from);
        cooked_date = str_realloc_cat (0, cooked_date, " GMT");
      }
    else
      {
        cooked_date = str_save (0, "\?\?\?\?-\?\?-\?\? GMT");
      }

    creator = assoc_get_str_taking (headers, rel_make_field_str ("creator"));
    if (!creator)
      creator = "????";

    safe_printfmt (out_fd, "%s\t%s\t%s\n", cooked_date, creator, patch_lvl);
    lim_free (0, cooked_date);
  }

  safe_printfmt (out_fd, "\n");

  /* Print the summary: */

  {
    const t_uchar * summary;

    summary = assoc_get_str_taking (headers, rel_make_field_str ("summary"));
    if (!summary)
      summary = "";

    safe_printfmt (out_fd, "    Summary:\n      %s\n", summary);
  }

  /* Print the revision name: */
  {
    const t_uchar * revision;

    revision = assoc_get_str_taking (headers, rel_make_field_str ("revision"));
    if (!revision)
      revision = "";

    safe_printfmt (out_fd, "    Revision:\n      %s\n", revision);
  }

  safe_printfmt (out_fd, "\n");

  /* Print the revision body: */
  {
    t_uchar * eol;
    t_uchar * pos;

    pos = body;
    while (*pos)
      {
        eol = str_chr_index (pos, '\n');

        if (eol)
          {
            safe_printfmt (out_fd, "    %.*s\n", (int)(eol - pos), pos);
            pos = eol + 1;
          }
        else
          {
            safe_printfmt (out_fd, "    %s\n", pos);
            break;
          }
      }
  }

  safe_printfmt (out_fd, "\n");

  /* file lists */

  if (!no_files)
    {
      t_uchar * revision = 0;
      t_uchar * fqrev = 0;
      t_uchar * excluded_patch_file = 0;

      revision = str_alloc_cat_many (0, version, "--", patch_lvl, str_end);
      fqrev = arch_fully_qualify (archive, revision);

      excluded_patch_file = arch_log_file (".", archive, revision);

      changelog_file_list (out_fd, headers, "new-files", excluded_patch_file + 2);
      changelog_file_list (out_fd, headers, "removed-files", 0);
      changelog_file_list (out_fd, headers, "modified-files", 0);
      changelog_file_pair_list (out_fd, headers, "renamed-files");
      changelog_file_list (out_fd, headers, "new-directories", 0);
      changelog_file_list (out_fd, headers, "removed-directories", 0);
      changelog_file_list (out_fd, headers, "modified-directories", 0);
      changelog_file_pair_list (out_fd, headers, "renamed-directories");
      changelog_file_list (out_fd, headers, "new-patches", fqrev);

      lim_free (0, revision);
      lim_free (0, fqrev);
      lim_free (0, excluded_patch_file);
    }

  safe_printfmt (out_fd, "\n");

  lim_free (0, body);
  free_assoc_table (headers);
}

static void
changelog_file_list (int out_fd, assoc_table headers, const t_uchar * header, const t_uchar * exclude)
{
  const t_uchar * value;
  int width;
  int items_on_line;
  int printed_header;


  value = assoc_get_str_taking (headers, rel_make_field_str (header));
  if (!value)
    return;

  printed_header = 0;
  width = 0;
  items_on_line = 0;

  while (1)
    {
      const t_uchar * end;

      while (char_is_space (*value))
        ++value;

      if (!*value)
        break;

      end = value;
      while (*end && !char_is_space (*end))
        ++end;

      if (!exclude || str_cmp_n (exclude, str_length (exclude), value, end - value))
        {
          if (!printed_header)
            {
              t_uchar * fixed_header = 0;

              fixed_header = str_save (0, header);
              {
                t_uchar * h;
                for (h = fixed_header; *h; ++h)
                  if (*h == '-')
                    *h = ' ';
                safe_printfmt (out_fd, "    %s:\n    ", fixed_header);
              }
              lim_free (0, fixed_header);
              printed_header = 1;
            }


          if ((items_on_line == 0) || ((width + (1 + end - value)) < 64))
            {
              width += (end - value) + 1;
              ++items_on_line;
              safe_printfmt (out_fd, " %.*s", (int)(end - value), value);
            }
          else
            {
              safe_printfmt (out_fd, "\n     %.*s", (int)(end - value), value);
              width = (end - value) + 1;
              items_on_line = 1;
            }
        }

      value = end;
    }

  if (printed_header)
    safe_printfmt (out_fd, "\n\n");
}


static void
changelog_file_pair_list (int out_fd, assoc_table headers, const t_uchar * header)
{
  const t_uchar * value;
  int printed_header = 0;

  value = assoc_get_str_taking (headers, rel_make_field_str (header));
  if (!value)
    return;

  while (1)
    {
      const t_uchar * end_1;
      const t_uchar * start_2;
      const t_uchar * end_2;

      while (char_is_space (*value))
        ++value;

      if (!*value)
        break;

      end_1 = value;
      while (*end_1 && !char_is_space (*end_1))
        ++end_1;

      if (!*end_1)
        panic ("corrupt log file");

      start_2 = end_1;

      while (char_is_space (*start_2))
        ++start_2;

      if (!*start_2)
        panic ("corrupt log file");

      end_2 = start_2;
      while (*end_2 && !char_is_space (*end_2))
        ++end_2;

      if (!printed_header)
        {
          t_uchar * fixed_header = 0;

          fixed_header = str_save (0, header);
          {
            t_uchar * h;
            for (h = fixed_header; *h; ++h)
              if (*h == '-')
                *h = ' ';
            safe_printfmt (out_fd, "    %s:\n", fixed_header);
          }
          lim_free (0, fixed_header);
          printed_header = 1;
        }

      safe_printfmt (out_fd, "     %.*s\n       ==> %.*s\n", (end_1 - value), value, (end_2 - start_2), start_2);
      value = end_2;
    }

  if (printed_header)
    safe_printfmt (out_fd, "\n");
}





/* tag: Tom Lord Wed May 14 12:14:01 2003 (changelogs.c)
 */
