/* 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)
*/
syntax highlighted by Code2HTML, v. 0.9.1