/* proj-tree-lint.c:
*
****************************************************************
* Copyright (C) 2003 Tom Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/hash/hashtree.h"
#include "hackerlab/hash/hash-utils.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/rmrf.h"
#include "libarch/invent.h"
#include "libarch/project-tree.h"
#include "libarch/proj-tree-lint.h"
/* __STDC__ prototypes for static functions */
static void tree_lint_callback (void * closure, invent_callback_data_t const * const data);
static int str_key_eq (void * va, void * vb, struct hashtree_rules * r);
static void tree_lint_hashtree_freefn (struct hashtree_item * it, struct hashtree_rules * rules);
static void
find_unused_ids (rel_table * ids_sans_files,
rel_table explicit_ids,
struct hashtree * id_map,
struct hashtree_rules * tree_lint_hashtree_rules);
static t_ulong hash_key (t_uchar * key);
struct lint_traversal_thunk
{
struct arch_tree_lint_result * answer;
struct hashtree * duplicated_ids_index;
struct arch_inventory_options * options;
rel_table explicit_ids;
};
struct lint_id_mapping
{
t_uchar * first_occurence;
int index;
};
static struct hashtree_rules tree_lint_hashtree_rules = { str_key_eq, 0, 0, 0, 0, 0 };
int
arch_print_tree_lint_report (int fd, struct arch_tree_lint_result * lint, int escape_classes)
{
if (lint->warning_files)
{
safe_printfmt (fd, "These files would be source but lack inventory ids (`baz add' perhaps?):\n\n");
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->warning_files);
safe_printfmt (fd, "\n\n");
}
if (lint->unrecognized_files)
{
safe_printfmt (fd, "These files violate naming conventions:\n\n");
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->unrecognized_files);
safe_printfmt (fd, "\n\n");
}
if (lint->symlinks_sans_targets)
{
safe_printfmt (fd, "These symlinks point to nonexistent files:\n\n");
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->symlinks_sans_targets);
safe_printfmt (fd, "\n\n");
}
if (lint->untagged_files)
{
safe_printfmt (fd, "These apparent source files lack inventory ids:\n\n");
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->untagged_files);
safe_printfmt (fd, "\n\n");
}
if (lint->ids_sans_files)
{
safe_printfmt (fd, "These explicit ids have no corresponding file:\n\n");
rel_print_table (fd, lint->ids_sans_files);
safe_printfmt (fd, "\n\n");
}
if (lint->duplicate_id_groups)
{
int lim;
int x;
safe_printfmt (fd, "Duplicated ids among each group of files listed here:\n\n");
lim = ar_size_rel_tables (lint->duplicate_id_groups);
for (x = 0; x < lim; ++x)
{
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->duplicate_id_groups[x]);
safe_printfmt (fd, "\n");
}
safe_printfmt (fd, "\n\n");
}
return arch_tree_lint_report_status (lint);
}
int
arch_print_filtered_tree_lint_report (int fd,
struct arch_tree_lint_result * lint,
t_uint categories,
int escape_classes)
{
if (lint->unrecognized_files && (categories & unrecognized_files))
{
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->unrecognized_files);
}
if (lint->symlinks_sans_targets && (categories & symlinks_sans_targets))
{
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->symlinks_sans_targets);
}
if ((lint->warning_files || lint->untagged_files) && (categories & untagged_files))
{
rel_table combined;
combined = rel_copy_table (lint->warning_files);
rel_append_x (&combined, lint->untagged_files);
rel_sort_table_by_field (0, combined, 0);
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, combined);
rel_free_table (combined);
}
if (lint->ids_sans_files && (categories & ids_sans_files))
{
rel_print_table (fd, lint->ids_sans_files);
}
if (lint->duplicate_id_groups && (categories & duplicate_id_groups))
{
int lim;
int x;
lim = ar_size_rel_tables (lint->duplicate_id_groups);
for (x = 0; x < lim; ++x)
{
rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->duplicate_id_groups[x]);
safe_printfmt (fd, "\n");
}
}
return arch_tree_lint_report_filtered_status (lint, categories);
}
/* calculate the tree status:
* -1 - hard error
* 0 - clean
* 1 - soft error
*/
int
arch_tree_lint_report_status (struct arch_tree_lint_result * lint)
{
int status = 0;
if (lint->warning_files)
{
if (lint->untagged_source_category == arch_inventory_unrecognized)
status = -1;
else
status = 1;
}
if (lint->unrecognized_files)
{
status = -1;
}
if (lint->symlinks_sans_targets)
{
if (!status)
status = 1;
}
if (lint->untagged_files)
{
if (lint->untagged_source_category == arch_inventory_unrecognized)
status = -1;
else if ((lint->id_tagging_method == arch_implicit_id_tagging) || (lint->id_tagging_method == arch_tagline_id_tagging))
{
if (!status)
status = 1;
}
else
status = -1;
}
if (lint->ids_sans_files)
{
status = -1;
}
if (lint->duplicate_id_groups)
{
status = -1;
}
return status;
}
/* calculate the tree status:
* -1 - hard error
* 0 - clean
* 1 - soft error
*/
int
arch_tree_lint_report_filtered_status (struct arch_tree_lint_result * lint, t_uint categories)
{
int status = 0;
if (lint->unrecognized_files && (categories & unrecognized_files))
{
status = -1;
}
if (lint->symlinks_sans_targets && (categories & symlinks_sans_targets))
{
if (!status)
status = 1;
}
if ((lint->warning_files || lint->untagged_files) && (categories & untagged_files))
{
if ((lint->id_tagging_method == arch_implicit_id_tagging) || (lint->id_tagging_method == arch_tagline_id_tagging))
{
if (!status)
status = 1;
}
else
status = -1;
}
if (lint->ids_sans_files && (categories & ids_sans_files))
{
status = -1;
}
if (lint->duplicate_id_groups && (categories & duplicate_id_groups))
{
status = -1;
}
return status;
}
struct arch_tree_lint_result *
arch_tree_lint (arch_project_tree_t * tree)
{
int here_fd;
struct arch_tree_lint_result * answer;
struct arch_inventory_options options;
struct lint_traversal_thunk thunk;
here_fd = safe_open (".", O_RDONLY, 0);
/*
safe_chdir (dirspec);
dir = safe_current_working_directory ();
tree_root = arch_tree_root (0, dir, 0);
invariant (!tree_root || !str_cmp (tree_root, dir));
*/
safe_chdir (tree->root);
answer = lim_malloc (0, sizeof (*answer));
mem_set0 ((t_uchar *)answer, sizeof (*answer));
mem_set0 ((t_uchar *)&options, sizeof (options));
options.categories = (arch_inventory_source
| arch_inventory_precious
| arch_inventory_backup
| arch_inventory_junk
| arch_inventory_unrecognized); /* not arch_inventory_{tree,excludes} */
options.want_ids = 1;
options.treat_unrecognized_source_as_source = 1;
/* options.method = (!tree_root ? arch_names_id_tagging : arch_tree_id_tagging_method (tree_root, 0)); */
options.nested = 0;
options.include_excluded = 1;
arch_get_inventory_naming_conventions (&options, tree);
answer->id_tagging_method = options.method;
answer->untagged_source_category = options.untagged_source_category;
mem_set0 ((t_uchar *)&thunk, sizeof (thunk));
thunk.answer = answer;
thunk.duplicated_ids_index = hashtree_alloc (&tree_lint_hashtree_rules);
thunk.options = &options;
thunk.explicit_ids = 0;
arch_inventory_traversal (&options, tree, tree_lint_callback, &thunk);
find_unused_ids (&(answer->ids_sans_files), thunk.explicit_ids, thunk.duplicated_ids_index, &tree_lint_hashtree_rules);
safe_fchdir (here_fd);
safe_close (here_fd);
rel_free_table (thunk.explicit_ids);
hashtree_free (thunk.duplicated_ids_index, tree_lint_hashtree_freefn, &tree_lint_hashtree_rules);
arch_free_inventory_naming_conventions (&options);
return answer;
}
static void
find_unused_ids (rel_table * ids_sans_files, rel_table explicit_ids, struct hashtree * id_map, struct hashtree_rules * tree_lint_hashtree_rules)
{
int n_records=rel_n_records (explicit_ids);
int i;
for (i = 0; i < n_records; ++i)
{
if ( hashtree_find (id_map, hash_key (explicit_ids[i][0]), explicit_ids[i][0], tree_lint_hashtree_rules) == 0)
rel_add_records (ids_sans_files, rel_make_record (explicit_ids[i][1], 0), 0);
}
}
void
arch_free_lint_result (struct arch_tree_lint_result * result)
{
int x;
rel_free_table (result->unrecognized_files);
rel_free_table (result->symlinks_sans_targets);
rel_free_table (result->untagged_files);
rel_free_table (result->ids_sans_files);
for (x = 0; x < ar_size_rel_tables (result->duplicate_id_groups); ++x)
rel_free_table (result->duplicate_id_groups[x]);
ar_free_rel_tables (&result->duplicate_id_groups);
lim_free (0, result);
}
void
tree_lint_callback (void * vthunk, invent_callback_data_t const * const data)
{
struct lint_traversal_thunk * thunk;
t_uchar * path = data->path;
thunk = (struct lint_traversal_thunk *)vthunk;
/* Remove leading ./ */
if (path[0] == '.' && path[1] == '/')
path += 2;
/* violations of naming conventions
*/
if (data->category == arch_inventory_unrecognized)
{
rel_add_records (&thunk->answer->unrecognized_files, rel_make_record (path, 0), 0);
}
/* files classified as non-source because they lack an inventory id
*/
if ((data->category != arch_inventory_source) && data->has_source_name)
{
rel_add_records (&thunk->answer->warning_files, rel_make_record (path, 0), 0);
}
/* symlink but no target
*/
if (S_ISLNK (data->stat_buf.st_mode))
{
if (safe_access (path, F_OK))
{
rel_add_records (&thunk->answer->symlinks_sans_targets, rel_make_record (path, 0), 0);
}
}
/* untagged file that passes naming conventions
*/
if (((thunk->options->method == arch_implicit_id_tagging) || (thunk->options->method == arch_tagline_id_tagging))
&& (data->category == arch_inventory_source)
&& (!data->id || (data->id[0] == '?')))
{
rel_add_records (&thunk->answer->untagged_files, rel_make_record (path, 0), 0);
}
else if ((thunk->options->method == arch_explicit_id_tagging) && (data->category == arch_inventory_source) && !data->id)
{
t_uchar * dir = 0;
dir = file_name_directory_file (0, path);
if (!arch_is_dont_care_explicit_dflt_dir (dir))
rel_add_records (&thunk->answer->untagged_files, rel_make_record (path, 0), 0);
lim_free (0, dir);
}
/* explicit id but no file
*/
if (data->category == arch_inventory_source)
{
size_t len;
len = str_length (path);
if ((len > 3) && !str_cmp (path + len - 3, ".id"))
{
t_uchar * path_dir;
t_uchar * path_dir_basename;
t_uchar * path_dir_dir;
t_uchar * basename;
t_uchar * file;
path_dir = file_name_directory_file (0, path);
if (path_dir != 0)
{
path_dir_basename = file_name_tail (0, path_dir);
path_dir_dir = file_name_directory_file (0, path_dir);
basename = file_name_tail (0, path);
basename[str_length(basename) - 3] = 0;
file = file_name_in_vicinity (0, path_dir_dir, basename);
if (!str_cmp (path_dir_basename, ".arch-ids"))
{
int errn = 0;
t_uchar * id = arch_id_from_explicit_file (&errn, path);
if (id == 0)
{
safe_printfmt (2, "i/o error during inventory traversal (%s) for %s\n", errno_to_string (errn), file);
exit (2);
}
rel_add_records (&thunk->explicit_ids, rel_make_record (id, path, 0), 0);
lim_free (0, id);
}
lim_free (0, path_dir);
lim_free (0, path_dir_basename);
lim_free (0, path_dir_dir);
lim_free (0, basename);
lim_free (0, file);
}
}
}
/* duplicate ids
*/
if (data->id)
{
struct hashtree_item * item;
item = hashtree_store (thunk->duplicated_ids_index, hash_key (data->id), data->id, &tree_lint_hashtree_rules);
if (item->key == data->id)
{
struct lint_id_mapping * binding;
item->key = str_save (0, data->id);
binding = lim_malloc (0, sizeof (*binding));
binding->first_occurence = str_save (0, path);
binding->index = -1;
item->binding = (void *)binding;
}
else
{
struct lint_id_mapping * binding;
binding = (struct lint_id_mapping *)item->binding;
if (binding->index < 0)
{
ar_push_rel_tables (&thunk->answer->duplicate_id_groups, NULL);
binding->index = ar_size_rel_tables (thunk->answer->duplicate_id_groups) - 1;
rel_add_records (&thunk->answer->duplicate_id_groups[binding->index], rel_make_record (binding->first_occurence, data->id, 0), 0);
}
rel_add_records (&thunk->answer->duplicate_id_groups[binding->index], rel_make_record (path, 0), 0);
}
}
}
static int
str_key_eq (void * va, void * vb, struct hashtree_rules * r)
{
t_uchar * a;
t_uchar * b;
a = (t_uchar *)va;
b = (t_uchar *)vb;
return !str_cmp (a, b);
}
static void
tree_lint_hashtree_freefn (struct hashtree_item * it, struct hashtree_rules * rules)
{
struct lint_id_mapping * binding;
lim_free (0, it->key);
binding = (struct lint_id_mapping *)it->binding;
lim_free (0, binding->first_occurence);
}
static t_ulong
hash_key (t_uchar * key)
{
return hash_mem (key, str_length (key));
}
/* tag: Tom Lord Wed May 14 14:30:47 2003 (proj-tree-lint.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1