/* cmd-ancestry.c:
 *
 * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * Copyright (C) 2004 Canonical Ltd.
 * 	Authors: Robert Collins <robert.collins@canonical.com>
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */




#include "config-options.h"
#include "po/gettext.h"
#include "hackerlab/bugs/exception.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/rmrf.h"
#include "libarch/namespace.h"
#include "libarch/project-tree.h"
#include "libarch/my.h"
#include "libarch/ancestry.h"
#include "libarch/archive.h"
#include "libarch/archives.h"
#include "libarch/patch-logs.h"
#include "libarch/project-tree.h"
#include "commands/cmdutils.h"
#include "commands/ancestry.h"
#include "commands/version.h"



static t_uchar * usage = N_("[options] [revision]");

#define OPTS(OP) \
  OP (opt_help_msg, "h", "help", 0, \
      N_("Display a help message and exit.")) \
  OP (opt_long_help, "H", 0, 0, \
      N_("Display a verbose help message and exit.")) \
  OP (opt_version, "V", "version", 0, \
      N_("Display a release identifier string\n" \
      "and exit.")) \
  OP (opt_dir, "d", "dir DIR", 1, \
      N_("cd to DIR first")) \
  OP (opt_merges, "m", "merges", 0, \
      N_("show merges into this development line")) \
  OP (opt_reverse, "r", "reverse", 0, \
      N_("list oldest to newest")) \
  OP (opt_summary, "s", "summary", 0, \
      N_("print a summary of each patch")) \
  OP (opt_creator, "c", "creator", 0, \
      N_("print the creator of each patch")) \
  OP (opt_tree, "t", "tree", 0, \
      N_("generate or print the tree ancestry file")) \
  OP (opt_date, "D", "date", 0, \
      N_("print the date of each patch"))




t_uchar arch_cmd_ancestry_help[] = N_("display the ancestory of a revision\n"
                                    "Print the ancestry of a revision.\n");

enum options
{
  OPTS (OPT_ENUM)
};

static struct opt_desc opts[] =
{
  OPTS (OPT_DESC)
    {-1, 0, 0, 0, 0}
};



typedef struct program_type
{
    t_uchar const * program_name;
    t_uchar * default_archive;
} program_t;


static void
do_ancestry(t_uchar * revision_spec, program_t const * program, int const wanted_headers, int silent, rel_table *output, t_uchar const *dir)
{
  t_uchar * archive;
  t_uchar * version;
  t_uchar * revision = 0;
  struct arch_archive * arch = 0;
  t_uchar * this_archive = 0;
  t_uchar * this_version = 0;
  arch_project_tree_t * tree;
  int position;
  int partial;
  rel_table revisions;

  revision = arch_determine_revision (&arch, NULL, revision_spec, program->program_name);
  archive = str_save (0, arch->official_name);
  version = arch_parse_package_name (arch_ret_package_version, 0, revision);

  tree = arch_project_tree_new (talloc_context, dir);
    {
      arch_project_tree_t *aTree;
      arch_patch_id * patch_id;
      if (tree->root)
	  aTree = tree;
      else
	  aTree = NULL;
      if (aTree && !str_cmp (revision_spec, tree->fqversion))
          patch_id = talloc_steal (tree, arch_patch_id_new (tree->fqrevision));
      else
          patch_id = talloc_steal (tree, arch_patch_id_new_archive (arch->official_name, revision));
      *output = patch_ancestry (talloc_context, tree, patch_id, -1);
    }
  arch_project_tree_delete(tree);
  /* we might be finished now ..*/
  if (silent)
      return;

  revisions = *output; 
  position = 0;
  partial = 0;
  while (position < rel_n_records (revisions) && !partial)
    {
      t_uchar * version_wanted = 0;
      t_uchar * level = 0;

      /* pre-condition:
       *
       * revisions[position][0] tells us the next revision to print.
       *
       * if arch is 0, we can not reach that archive.
       * otherwise, arch is connected to archive.
       *
       * this_archive is 0 or the name of the last archive name printed
       * this_version is 0 or the name of the last package-version printed
       */
      if (str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, revisions[position][0]))
	{
	  archive = arch_parse_package_name (arch_ret_archive, 0, revisions[position][0]);
	  revision = arch_parse_package_name (arch_ret_non_archive, 0, revisions[position][0]);
	}
      else 
	  partial = -1;

      if (str_cmp (this_archive, archive))
        {
	  if (!silent) 
	      safe_printfmt (1, "%s\n", archive);
          lim_free (0, this_archive);
          this_archive = str_save (0, archive);
          lim_free (0, this_version);
          this_version = 0;
        }

      version_wanted = arch_parse_package_name (arch_ret_package_version, 0, revision);

      if (str_cmp (this_version, version_wanted))
        {
	  if (!silent)
	      safe_printfmt (1, "  %s\n", version_wanted);
          lim_free (0, this_version);
          this_version = str_save (0, version_wanted);
        }

      level = arch_parse_package_name (arch_ret_patch_level, 0, revision);

      if (!silent)
	  safe_printfmt (1, "    %s", level);

      if (partial)
        {
	  if (!silent)
	      safe_printfmt (1, " (\?\?\?)\n(\?\?\?)\n");
          break;
        }
      else
        {
          t_uchar * fq_next_revision = 0;
          t_uchar * next_archive = 0;
          t_uchar * next_version = 0;
          t_uchar * next_revision = 0;

	  if (position + 1 < rel_n_records(revisions))
	      fq_next_revision = str_save(0, revisions[position + 1][0]);
	  else
	      fq_next_revision = NULL;
          if (fq_next_revision)
	    {
	      if (str_cmp(ARCH_ANCESTRY_PATCH_PARTIAL, fq_next_revision))
		{
		  next_archive = arch_parse_package_name (arch_ret_archive, 0, fq_next_revision);
		  next_version = arch_parse_package_name (arch_ret_package_version, 0, fq_next_revision);
		  next_revision = arch_parse_package_name (arch_ret_non_archive, 0, fq_next_revision);
		}
	      else
		{
		  next_archive = NULL;
		  next_version = NULL;
		  next_revision = NULL;
		  partial = -1;
		  if (!silent)
		      safe_printfmt (1, " (\?\?\?)\n(\?\?\?)\n");
		  break;
		}
	    }

          if (wanted_headers)
            {
              t_uchar * log = 0;
              assoc_table headers = 0;

              log = arch_archive_log (arch, revision);
              arch_parse_log (0, &headers, 0, log);

              safe_printfmt (1, "\n");
              arch_print_headers_summary (1, 6, headers, wanted_headers);

              lim_free (0, log);
              free_assoc_table (headers);
            }
          else if (fq_next_revision)
            {
              /* scan backwards in the same archive version and print " ... level"
               */

              while (fq_next_revision && !str_cmp (next_archive, archive) && !str_cmp (next_version, version))
                {
                  lim_free (0, revision);
                  revision = str_save (0, next_revision);
		  ++position;
		  

                  lim_free (0, level);
                  level = arch_parse_package_name (arch_ret_patch_level, 0, revision);

                  lim_free (0, fq_next_revision);
                  lim_free (0, next_archive);
                  lim_free (0, next_version);
                  lim_free (0, next_revision);

		  if (position + 1 < rel_n_records(revisions))
		      fq_next_revision = str_save(0, revisions[position + 1][0]);
		  else
		      fq_next_revision = NULL;
                  if (fq_next_revision)
		    {
		      if (str_cmp(ARCH_ANCESTRY_PATCH_PARTIAL, fq_next_revision))
			{
			  next_archive = arch_parse_package_name (arch_ret_archive, 0, fq_next_revision);
			  next_version = arch_parse_package_name (arch_ret_package_version, 0, fq_next_revision);
			  next_revision = arch_parse_package_name (arch_ret_non_archive, 0, fq_next_revision);
			}
		      else
			{
			  next_archive = NULL;
			  next_version = NULL;
			  next_revision = NULL;
			  partial = -1;
			}
		    }
		  
                }

              safe_printfmt (1, " ... %s\n", level);
            }
          else
            safe_printfmt (1, "\n");

          safe_flush (1);

          if (!fq_next_revision)
            {
              break;
            }
          else
            {
              /* update the invariant variables */

              lim_free (0, version);
              version = next_version;
              next_version = 0;

              revision = next_revision;
              next_revision = 0;

              lim_free (0, fq_next_revision);
              fq_next_revision = 0;
	      ++position;

              if (!str_cmp (next_archive, archive))
                {
                  lim_free (0, next_archive);
                  next_archive = 0;
                }
              else
                {
                  lim_free (0, archive);
                  archive = next_archive;
                  next_archive = 0;

                  arch_archive_close (arch);
                  arch = 0;
		  if (!partial && wanted_headers)
		    {
                      arch = arch_archive_connect_branch (archive, NULL);
                      if (!arch)
                        {
                          safe_printfmt (2, "failed to connect to archive %s\n", archive);
                          partial = -1;
                        }
		    }
		}
            }
        }

      lim_free (0, version_wanted);
      lim_free (0, level);
    }

  arch_archive_close (arch);
  lim_free (0, archive);
  lim_free (0, version);
  lim_free (0, revision);
  lim_free (0, this_archive);
  lim_free (0, this_version);
}

int
arch_cmd_ancestry (t_uchar * program_name, int argc, char * argv[])
{
  int o;
  struct opt_parsed * option;
  t_uchar * dir = 0;
  int merges = 0;
  int reverse = 0;
  int wanted_headers = 0;
  int fortree = 0;
  t_uchar * revision_spec = 0;
  program_t program;
  rel_table revisions;

  program.program_name = program_name;

  safe_buffer_fd (1, 0, O_WRONLY, 0);

  dir = str_save (0, ".");

  option = 0;

  while (1)
    {
      o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_cmd_ancestry_help, opt_help_msg, opt_long_help, opt_version);
      if (o == opt_none)
        break;
      switch (o)
        {
        default:
          safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
          panic ("internal error parsing arguments");

        usage_error:
          opt_usage (2, argv[0], program_name, usage, 1);
          exit (1);

          /* bogus_arg: */
          safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string);
          goto usage_error;

        case opt_dir:
          {
            lim_free (0, dir);
            dir = str_save (0, option->arg_string);
            break;
          }

        case opt_merges:
          {
            merges = 1;
            break;
          }

        case opt_reverse:
          {
            reverse = 1;
            break;
          }

        case opt_summary:
          {
            wanted_headers |= arch_include_summary;
            break;
          }

        case opt_creator:
          {
            wanted_headers |= arch_include_creator;
            break;
          }

	case opt_tree:
	  {
	    fortree = 1;
	    break;
	  }
	
        case opt_date:
          {
            wanted_headers |= arch_include_date;
            break;
          }
        }
    }

  if (argc > 2)
    goto usage_error;

  program.default_archive = arch_my_default_archive (NULL);

  if (argc == 2)
    revision_spec = str_save (0, argv[1]);
  else
    {
      arch_project_tree_t * tree = arch_project_tree_new (talloc_context, dir);
      revision_spec = arch_try_tree_version_dir (program_name, tree);
      arch_project_tree_delete (tree);
    }

  revisions = NULL;
  do_ancestry(revision_spec, &program, wanted_headers, fortree, &revisions, dir);
  if (fortree)
    {
      int position;
      safe_printfmt (1, "baz ancestry version 1\n");
      for (position = 0; position < rel_n_records (revisions); ++position)
	{
	  if (str_cmp (ARCH_ANCESTRY_PATCH_PARTIAL, revisions[position][0]))
	      safe_printfmt (1, ARCH_ANCESTRY_PATCH_PREFIX"%s\n", revisions[position][0]);
	  else
	      safe_printfmt (1, "%s\n", revisions[position][0]);
	}
    }
  rel_free_table (revisions);

  lim_free (0, program.default_archive);
  lim_free (0, revision_spec);

  return 0;
}



/* tag: Tom Lord Wed Jul 16 13:58:59 2003 (cmd-ancestry.c)
 */


syntax highlighted by Code2HTML, v. 0.9.1