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