/* status.c * **************************************************************** * Copyright (C) 2003 Tom Lord * Copyright (C) 2004, 2005 Canonical Ltd. * * 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 "libarch/chatter.h" #include "libarch/conflict-handling.h" #include "libarch/inode-sig.h" #include "libarch/local-cache.h" #include "libarch/make-changeset.h" #include "libarch/project-tree.h" #include "libarch/proj-tree-lint.h" #include "libfsutils/rmrf.h" #include "commands/cmd.h" #include "commands/diff.h" #include "commands/status.h" #include "commands/version.h" static t_uchar * usage = N_("[options] [dir]"); static void lint (struct arch_tree_lint_result * lint_result, int categories_filter, int escape_classes); static int lint_status (struct arch_tree_lint_result * lint_result, int categories_filter); static int changes (arch_project_tree_t *tree, t_uchar * program_name); #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 and exit")) \ OP (opt_broken_symlink, "s", "broken-symlinks", 0, \ N_("Just list broken symlinks")) \ OP (opt_unrecognized_files, "u", "unrecognized-files", 0, \ N_("Just list files violating naming conventions")) \ OP (opt_untagged_files, "t", "untagged-files", 0, \ N_("Just list files lacking inventory ids")) \ OP (opt_missing_files, "m", "missing-files", 0, \ N_("Just list inventory ids lacking corresponding files")) \ OP (opt_duplicate_ids, "d", "duplicate-ids", 0, \ N_("Just list duplicated ids")) \ OP (opt_conflicted, "c", "conflicted", 0, \ N_("Just list conflicted files")) \ OP (opt_lint, 0, "lint", 0,\ N_("Only lint the project tree")) \ OP (opt_diffs, 0, "diffs", 0, \ N_("deprecated. use baz diff instead")) \ OP (opt_strict, 0, "strict", 0, \ N_("exit with non-0 status on _any_ oddity")) \ OP (opt_unescaped, 0, "unescaped", 0, \ N_("show filenames in unescaped form")) t_uchar arch_cmd_status_help[] = N_("scan a project tree and show the tree status.\n" "\n" "Audit a source tree for various changes, the characters in the first column mean:\n" "C - conflicts\n" "R - renames\n" "A - adds\n" "D - deletes\n" "P - permissions\n" "? - unexpected files\n" "By default, changes will report the status \n" "for everything within a tree. Specific changes\n" "may be ignored by passing options to the\n" "changes command\n\n" "Note: If conflicts have occurred, when you have\n" "resolved them, notify baz by run baz resolved --all\n"); enum options { OPTS (OPT_ENUM) }; static struct opt_desc opts[] = { OPTS (OPT_DESC) {-1, 0, 0, 0, 0} }; int arch_cmd_status (t_uchar * program_name, int argc, char * argv[]) { int o; struct opt_parsed * option; char * dir; int strict = 0; int only_lint = 0; int result = 0; t_uint categories_filter = 0; int escape_classes = arch_escape_classes; int conflicted_only = 0; safe_buffer_fd (1, 0, O_WRONLY, 0); option = 0; while (1) { o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_cmd_status_help, opt_help_msg, opt_long_help, opt_version); if (o == opt_none) break; switch (o) { /* Begin Option error handling------------------------------------- */ 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; /* Begin General options ------------------------------------- */ case opt_strict: { strict = 1; break; } case opt_lint : { only_lint = 1; break; } /* BEGIN Lint options ------------------------------------- */ case opt_broken_symlink: { categories_filter = categories_filter | symlinks_sans_targets; break; } case opt_unrecognized_files: { categories_filter = categories_filter | unrecognized_files; break; } case opt_untagged_files: { categories_filter = categories_filter | untagged_files; break; } case opt_missing_files: { categories_filter = categories_filter | ids_sans_files; break; } case opt_duplicate_ids: { categories_filter = categories_filter | duplicate_id_groups; break; } case opt_conflicted: { conflicted_only = 1; break; } case opt_unescaped: { escape_classes = 0; break; } case opt_diffs: { safe_printfmt(2, "This option is deprecated. Please baz diff instead\n"); exit(2); break; } } } if (argc > 2) goto usage_error; { int status; int outstanding_rejects = 0; arch_project_tree_t * tree; struct arch_tree_lint_result * lint_result; /* The first thing we do is determine the tree root, which * is used by several of the command suboptions */ if (argc == 1) dir = "."; else dir = argv[1]; tree = arch_project_tree_new (talloc_context, dir); if (!tree->root) { safe_printfmt (2, _("%s: directory is not in a project tree.\n"), argv[0]); exit (1); } if (!tree->version) { safe_printfmt (2, _("%s: tree-version is not set.\n"), argv[0]); exit (1); } if (!tree->fqrevision) { safe_printfmt (2, _("%s: %s is not imported yet. Try baz lint if you need to check\n" "the tree inventory\n"), argv[0], tree->version); exit (1); } /* Now, we know what directory we're working with. Now, we need to * run our problem finding commands. Each of the following commands * runs a specific set of checks for us, telling the user of changes, * returning to us whether or not there was success or failure. If there * is any failure, then we'll return a pass/fail error code back to the * shell * FIXME: Would it be useful to return a an enumerated set of bools * back to the shell, so that things like pybaz and fai can tell what went * wrong? */ /* perhaps the output from lint and changes should be combined for the UI ? * if so, perhaps print_compact can be given a lint report as an option * * Note: We need to lint first, so that if things bomb, the user has a * better understanding of why */ lint_result = arch_tree_lint (tree); status = lint_status (lint_result, categories_filter); if (strict && status != 0) status = 1; else if (status > 0) status = 0; if (! only_lint && ! categories_filter && ! conflicted_only) { /* -1 is hard errors, 1 is warnings */ if (status < 0) { lint (lint_result, categories_filter, escape_classes); safe_printfmt(2, _("Tree is not lint clean. Unable to continue past this point\n")); exit(1); } status |= changes (tree, argv[0]); } if (! conflicted_only) lint (lint_result, categories_filter, escape_classes); outstanding_rejects = arch_tree_conflicts_exist (tree); if ( outstanding_rejects && ! categories_filter && ! only_lint) { rel_table conflicts = 0; int loop; int conflicts_error; /* maybe this should be part of lint ? */ conflicts_error = arch_tree_show_conflicts (argv[0], tree, &conflicts); status |= conflicts_error; if (conflicts_error) panic(_("Unknown error with arch_tree_show_rejects\n")); /* MM: Useless (?) safe_printfmt(1, _("* The following %d files are conflicted:\n\n"), rel_n_records(conflicts)); */ for (loop = 0; loop < rel_n_records(conflicts); loop++) { safe_printfmt(1, " C %s\n", conflicts[loop][0]); } } result = status; lim_free (0, lint_result); arch_project_tree_delete (tree); } return result; } /* Supporting functions are below this point */ /* This function prints the lints from tree */ void lint (struct arch_tree_lint_result * lint_result, int categories_filter, int escape_classes) { if (categories_filter) { arch_print_filtered_tree_lint_report (1, lint_result, categories_filter, escape_classes); return; } else { arch_print_tree_lint_report (1, lint_result, escape_classes); return; } } /* get the status for a lint result */ int lint_status (struct arch_tree_lint_result * lint_result, int categories_filter) { if (categories_filter) { return arch_tree_lint_report_filtered_status (lint_result, categories_filter); } else { return arch_tree_lint_report_status (lint_result); } } /* this function generates a summary of the non lint related changes */ int changes (arch_project_tree_t *tree, t_uchar * program_name) { t_uchar * output = 0; rel_table limits = 0; int result = 0; output = arch_diff_default_output_dir (talloc_context, tree->root, 0 ); /**************************************************************** * The heart of the matter. */ { struct arch_make_changeset_report make_report = {0, }; struct arch_changeset_report report = {0, }; assoc_table inode_shortcut = 0; arch_project_tree_t * orig; arch_chatter (1, "* looking for %s/%s to compare with\n", tree->archive, tree->revision); /* should be less noisy - RBC 20041209 */ orig = arch_find_or_make_local_tree_copy (1, tree, 0, 0, tree->archive, tree->revision); if (!orig) { safe_printfmt (2, "%s: no local copies to compare to (%s/%s)\n consider `add-pristine --help'\n", program_name, tree->archive, tree->revision); exit (2); } arch_chatter (1, "* comparing to %s/%s\n", tree->archive, tree->revision); arch_read_inode_sig_ids (0, &inode_shortcut, tree->root, tree->archive, tree->revision); arch_make_changeset (&make_report, orig, tree, output, arch_unspecified_id_tagging, arch_inventory_unrecognized, limits, inode_shortcut, 0, arch_escape_classes); arch_evaluate_changeset (&report, output); result = arch_any_changes (&report); if (result) { safe_printfmt (1, "\n"); arch_changeset_report_print_compact (1, &report, arch_escape_classes); } arch_free_make_changeset_report_data (&make_report); arch_free_changeset_report_data (&report); rmrf_file (output); free_assoc_table (inode_shortcut); arch_project_tree_delete (orig); } talloc_free (output); return result; } /* tag: Tom Lord Mon May 12 12:25:44 2003 (tree-lint.c) */