1 /* proj-tree-lint.c:
2 *
3 ****************************************************************
4 * Copyright (C) 2003 Tom Lord
5 *
6 * See the file "COPYING" for further information about
7 * the copyright and warranty status of this work.
8 */
9
10
11 #include "hackerlab/bugs/panic.h"
12 #include "hackerlab/os/errno.h"
13 #include "hackerlab/os/errno-to-string.h"
14 #include "hackerlab/char/str.h"
15 #include "hackerlab/mem/mem.h"
16 #include "hackerlab/arrays/ar.h"
17 #include "hackerlab/fs/file-names.h"
18 #include "hackerlab/fs/cwd.h"
19 #include "hackerlab/hash/hashtree.h"
20 #include "hackerlab/hash/hash-utils.h"
21 #include "hackerlab/vu/safe.h"
22 #include "tla/libarch/invent.h"
23 #include "tla/libarch/project-tree.h"
24 #include "tla/libarch/proj-tree-lint.h"
25
26
27 /* __STDC__ prototypes for static functions */
28 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);
29 static void tree_lint_callback (const t_uchar * path,
30 struct stat * stat_buf,
31 enum arch_inventory_category category,
32 const t_uchar * id,
33 int has_source_name,
34 void * vthunk,
35 int escape_classes);
36 static int str_key_eq (void * va, void * vb, struct hashtree_rules * r);
37 static void tree_lint_hashtree_freefn (struct hashtree_item * it, struct hashtree_rules * rules);
38 static t_ulong hash_key (const t_uchar * key);
39
40
41
42 struct lint_traversal_thunk
43 {
44 struct arch_tree_lint_result * answer;
45 struct hashtree * duplicated_ids_index;
46 struct arch_inventory_options * options;
47 rel_table explicit_ids;
48 };
49
50 struct lint_id_mapping
51 {
52 t_uchar * first_occurence;
53 int index;
54 };
55
56 static struct hashtree_rules tree_lint_hashtree_rules = { str_key_eq, 0, 0, 0, 0, 0 };
57
58
59
60 int
arch_print_tree_lint_report(int fd,struct arch_tree_lint_result * lint,int escape_classes)61 arch_print_tree_lint_report (int fd, struct arch_tree_lint_result * lint, int escape_classes)
62 {
63 int status;
64
65 status = 0;
66
67 if (rel_n_records (lint->warning_files))
68 {
69 safe_printfmt (fd, "These files would be source but lack inventory ids (`tla add' or a tagline perhaps?):\n\n");
70 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->warning_files);
71 safe_printfmt (fd, "\n\n");
72 if (!status)
73 status = 1;
74 }
75
76 if (rel_n_records (lint->unrecognized_files))
77 {
78 safe_printfmt (fd, "These files violate naming conventions:\n\n");
79 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->unrecognized_files);
80 safe_printfmt (fd, "\n\n");
81 status = -1;
82 }
83
84 if (rel_n_records (lint->symlinks_sans_targets))
85 {
86 safe_printfmt (fd, "These symlinks point to nonexistent files:\n\n");
87 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->symlinks_sans_targets);
88 safe_printfmt (fd, "\n\n");
89 if (!status)
90 status = 1;
91 }
92
93 if (rel_n_records (lint->untagged_files))
94 {
95 safe_printfmt (fd, "These apparent source files lack inventory ids:\n\n");
96 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->untagged_files);
97 safe_printfmt (fd, "\n\n");
98
99 if ((lint->id_tagging_method == arch_implicit_id_tagging) || (lint->id_tagging_method == arch_tagline_id_tagging))
100 {
101 if (!status)
102 status = 1;
103 }
104 else
105 status = -1;
106 }
107
108 if (rel_n_records (lint->ids_sans_files))
109 {
110 safe_printfmt (fd, "These explicit ids have no corresponding file:\n\n");
111 rel_print_table (fd, lint->ids_sans_files);
112 safe_printfmt (fd, "\n\n");
113 status = -1;
114 }
115
116 if (lint->duplicate_id_groups)
117 {
118 int lim;
119 int x;
120
121 safe_printfmt (fd, "Duplicated ids among each group of files listed here:\n\n");
122
123 lim = ar_size ((void *)lint->duplicate_id_groups, 0, sizeof (rel_table));
124
125 for (x = 0; x < lim; ++x)
126 {
127 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->duplicate_id_groups[x]);
128 safe_printfmt (fd, "\n");
129 }
130 safe_printfmt (fd, "\n\n");
131 status = -1;
132 }
133 return status;
134 }
135
136 int
arch_print_filtered_tree_lint_report(int fd,struct arch_tree_lint_result * lint,t_uint categories,int escape_classes)137 arch_print_filtered_tree_lint_report (int fd,
138 struct arch_tree_lint_result * lint,
139 t_uint categories,
140 int escape_classes)
141 {
142 int status;
143
144 status = 0;
145
146 if (rel_n_records (lint->unrecognized_files) && (categories & unrecognized_files))
147 {
148 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->unrecognized_files);
149 status = -1;
150 }
151 if (rel_n_records (lint->symlinks_sans_targets) && (categories & symlinks_sans_targets))
152 {
153 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->symlinks_sans_targets);
154 if (!status)
155 status = 1;
156 }
157 if ((rel_n_records (lint->warning_files) || rel_n_records (lint->untagged_files)) && (categories & untagged_files))
158 {
159 rel_table combined;
160
161 combined = rel_copy_table (lint->warning_files);
162 rel_append_x (&combined, lint->untagged_files);
163 rel_sort_table_by_field (0, combined, 0);
164
165 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, combined);
166
167 if ((lint->id_tagging_method == arch_implicit_id_tagging) || (lint->id_tagging_method == arch_tagline_id_tagging))
168 {
169 if (!status)
170 status = 1;
171 }
172 else
173 status = -1;
174
175 rel_free_table (combined);
176 }
177
178 if (rel_n_records (lint->ids_sans_files) && (categories & ids_sans_files))
179 {
180 rel_print_table (fd, lint->ids_sans_files);
181 status = -1;
182 }
183
184 if (lint->duplicate_id_groups && (categories & duplicate_id_groups))
185 {
186 int lim;
187 int x;
188
189 lim = ar_size ((void *)lint->duplicate_id_groups, 0, sizeof (rel_table));
190
191 for (x = 0; x < lim; ++x)
192 {
193 rel_print_pika_escape_iso8859_1_table (fd, escape_classes, lint->duplicate_id_groups[x]);
194 safe_printfmt (fd, "\n");
195 }
196 status = -1;
197 }
198
199 return status;
200 }
201
202 struct arch_tree_lint_result *
arch_tree_lint(t_uchar * dirspec)203 arch_tree_lint (t_uchar * dirspec)
204 {
205 int here_fd;
206 t_uchar * dir = 0;
207 t_uchar * tree_root = 0;
208 struct arch_tree_lint_result * answer;
209 struct arch_inventory_options options;
210 struct lint_traversal_thunk thunk;
211
212 here_fd = safe_open (".", O_RDONLY, 0);
213
214 safe_chdir (dirspec);
215 dir = safe_current_working_directory ();
216
217 tree_root = arch_tree_root (0, dir, 0);
218 invariant (!tree_root || !str_cmp (tree_root, dir));
219
220 if (tree_root)
221 safe_chdir (tree_root);
222
223 answer = lim_malloc (0, sizeof (*answer));
224 mem_set0 ((t_uchar *)answer, sizeof (*answer));
225
226 mem_set0 ((t_uchar *)&options, sizeof (options));
227 options.categories = (arch_inventory_source
228 | arch_inventory_precious
229 | arch_inventory_backup
230 | arch_inventory_junk
231 | arch_inventory_unrecognized); /* not arch_inventory_{tree,excludes} */
232 options.want_ids = 1;
233 options.treat_unrecognized_source_as_source = 1;
234 /* options.method = (!tree_root ? arch_names_id_tagging : arch_tree_id_tagging_method (tree_root, 0)); */
235 options.nested = 0;
236 options.include_excluded = 1;
237 arch_get_inventory_naming_conventions (&options, tree_root);
238
239 answer->id_tagging_method = options.method;
240
241 mem_set0 ((t_uchar *)&thunk, sizeof (thunk));
242 thunk.answer = answer;
243 thunk.duplicated_ids_index = hashtree_alloc (&tree_lint_hashtree_rules);
244 thunk.options = &options;
245 thunk.explicit_ids = rel_table_nil;
246
247 arch_inventory_traversal (&options, ".", tree_lint_callback, &thunk, 0);
248 find_unused_ids (&(answer->ids_sans_files), thunk.explicit_ids, thunk.duplicated_ids_index, &tree_lint_hashtree_rules);
249
250 safe_fchdir (here_fd);
251 safe_close (here_fd);
252
253 rel_free_table (thunk.explicit_ids);
254 hashtree_free (thunk.duplicated_ids_index, tree_lint_hashtree_freefn, &tree_lint_hashtree_rules);
255 arch_free_inventory_naming_conventions (&options);
256 lim_free (0, dir);
257 lim_free (0, tree_root);
258
259 return answer;
260 }
261
262 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)263 find_unused_ids (rel_table * ids_sans_files, rel_table explicit_ids, struct hashtree * id_map, struct hashtree_rules * tree_lint_hashtree_rules)
264 {
265 int n_records=rel_n_records (explicit_ids);
266 int i;
267 for (i = 0; i < n_records; ++i)
268 {
269 if (hashtree_find (id_map, hash_key (rel_peek_str (explicit_ids, i, 0)), (void *)rel_peek_str (explicit_ids, i, 0), tree_lint_hashtree_rules) == 0)
270 rel_add_records (ids_sans_files, rel_singleton_record_taking (rel_get_field (explicit_ids, i, 1)), rel_record_null);
271 }
272 }
273
274
275 void
arch_free_lint_result(struct arch_tree_lint_result * result)276 arch_free_lint_result (struct arch_tree_lint_result * result)
277 {
278 int x;
279 rel_free_table (result->unrecognized_files);
280 rel_free_table (result->symlinks_sans_targets);
281 rel_free_table (result->untagged_files);
282 rel_free_table (result->ids_sans_files);
283 for (x = 0; x < ar_size ((void *)result->duplicate_id_groups, 0, sizeof (rel_table)); ++x)
284 rel_free_table (result->duplicate_id_groups[x]);
285 ar_free ((void **)&result->duplicate_id_groups, 0);
286 lim_free (0, result);
287 }
288
289
290 static void
tree_lint_callback(const t_uchar * path,struct stat * stat_buf,enum arch_inventory_category category,const t_uchar * id,int has_source_name,void * vthunk,int escape_classes)291 tree_lint_callback (const t_uchar * path,
292 struct stat * stat_buf,
293 enum arch_inventory_category category,
294 const t_uchar * id,
295 int has_source_name,
296 void * vthunk,
297 int escape_classes)
298 {
299 struct lint_traversal_thunk * thunk;
300
301 thunk = (struct lint_traversal_thunk *)vthunk;
302
303 /* Remove leading ./ */
304 if (path[0] == '.' && path[1] == '/')
305 path += 2;
306
307 /* violations of naming conventions
308 */
309 if (category == arch_inventory_unrecognized)
310 {
311 rel_add_records (&thunk->answer->unrecognized_files, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
312 }
313
314 /* files classified as non-source because they lack an inventory id
315 */
316 if ((category != arch_inventory_source) && has_source_name)
317 {
318 rel_add_records (&thunk->answer->warning_files, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
319 }
320
321
322 /* symlink but no target
323 */
324 if (S_ISLNK (stat_buf->st_mode))
325 {
326 if (safe_access (path, F_OK))
327 {
328 rel_add_records (&thunk->answer->symlinks_sans_targets, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
329 }
330 }
331
332
333 /* untagged file that passes naming conventions
334 */
335 if (((thunk->options->method == arch_implicit_id_tagging) || (thunk->options->method == arch_tagline_id_tagging))
336 && (category == arch_inventory_source)
337 && (!id || (id[0] == '?')))
338 {
339 rel_add_records (&thunk->answer->untagged_files, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
340 }
341 else if ((thunk->options->method == arch_explicit_id_tagging) && (category == arch_inventory_source) && !id)
342 {
343 t_uchar * dir = 0;
344
345 dir = file_name_directory_file (0, path);
346 if (!arch_is_dont_care_explicit_dflt_dir (dir))
347 rel_add_records (&thunk->answer->untagged_files, rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
348 lim_free (0, dir);
349 }
350
351 /* explicit id but no file
352 */
353 if (category == arch_inventory_source)
354 {
355 size_t len;
356
357 len = str_length (path);
358 if ((len > 3) && !str_cmp (path + len - 3, ".id"))
359 {
360 t_uchar * path_dir;
361 t_uchar * path_dir_basename;
362 t_uchar * path_dir_dir;
363 t_uchar * basename;
364 t_uchar * file;
365
366 path_dir = file_name_directory_file (0, path);
367 if (path_dir != 0)
368 {
369 path_dir_basename = file_name_tail (0, path_dir);
370 path_dir_dir = file_name_directory_file (0, path_dir);
371 basename = file_name_tail (0, path);
372 basename[str_length(basename) - 3] = 0;
373 file = file_name_in_vicinity (0, path_dir_dir, basename);
374 if (!str_cmp (path_dir_basename, ".arch-ids"))
375 {
376 int errn = 0;
377 t_uchar * id = arch_id_from_explicit_file (&errn, path);
378 if (id == 0)
379 {
380 safe_printfmt (2, "i/o error during inventory traversal (%s) for %s\n", errno_to_string (errn), file);
381 exit (2);
382 }
383 rel_add_records (&thunk->explicit_ids, rel_make_record_2_taking (rel_make_field_str (id), rel_make_field_str (path)), rel_record_null);
384 lim_free (0, id);
385 }
386 lim_free (0, path_dir);
387 lim_free (0, path_dir_basename);
388 lim_free (0, path_dir_dir);
389 lim_free (0, basename);
390 lim_free (0, file);
391 }
392 }
393 }
394
395 /* duplicate ids
396 */
397 if (id)
398 {
399 struct hashtree_item * item;
400
401 item = hashtree_store (thunk->duplicated_ids_index, hash_key (id), (void *)id, &tree_lint_hashtree_rules);
402
403 if (item->key == id)
404 {
405 struct lint_id_mapping * binding;
406
407 item->key = str_save (0, id);
408 binding = lim_malloc (0, sizeof (*binding));
409 binding->first_occurence = str_save (0, path);
410 binding->index = -1;
411 item->binding = (void *)binding;
412 }
413 else
414 {
415 struct lint_id_mapping * binding;
416
417 binding = (struct lint_id_mapping *)item->binding;
418 if (binding->index < 0)
419 {
420 *(rel_table *)ar_push ((void **)&thunk->answer->duplicate_id_groups, 0, sizeof (rel_table)) = rel_table_nil;
421 binding->index = ar_size ((void *)thunk->answer->duplicate_id_groups, 0, sizeof (rel_table)) - 1;
422 rel_add_records (&thunk->answer->duplicate_id_groups[binding->index], rel_make_record_2_taking (rel_make_field_str (binding->first_occurence), rel_make_field_str (id)), rel_record_null);
423 }
424
425 rel_add_records (&thunk->answer->duplicate_id_groups[binding->index], rel_singleton_record_taking (rel_make_field_str (path)), rel_record_null);
426 }
427
428
429 }
430 }
431
432 static int
str_key_eq(void * va,void * vb,struct hashtree_rules * r)433 str_key_eq (void * va, void * vb, struct hashtree_rules * r)
434 {
435 t_uchar * a;
436 t_uchar * b;
437
438 a = (t_uchar *)va;
439 b = (t_uchar *)vb;
440
441 return !str_cmp (a, b);
442 }
443
444 static void
tree_lint_hashtree_freefn(struct hashtree_item * it,struct hashtree_rules * rules)445 tree_lint_hashtree_freefn (struct hashtree_item * it, struct hashtree_rules * rules)
446 {
447 struct lint_id_mapping * binding;
448 lim_free (0, it->key);
449 binding = (struct lint_id_mapping *)it->binding;
450 lim_free (0, binding->first_occurence);
451 }
452
453 static t_ulong
hash_key(const t_uchar * key)454 hash_key (const t_uchar * key)
455 {
456 return hash_mem (key, str_length (key));
457 }
458
459
460
461
462 /* tag: Tom Lord Wed May 14 14:30:47 2003 (proj-tree-lint.c)
463 */
464