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