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