/* 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