1 /* changeset-report.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/char/str.h"
13 #include "hackerlab/fs/file-names.h"
14 #include "hackerlab/vu/safe.h"
15 #include "hackerlab/arrays/ar.h"
16 #include "hackerlab/char/pika-escaping-utils.h"
17 #include "tla/libfsutils/read-line.h"
18 #include "tla/libawk/relassoc.h"
19 #include "tla/libfsutils/find-utils.h"
20 #include "tla/libfsutils/ensure-dir.h"
21 #include "tla/libfsutils/copy-file.h"
22 #include "tla/libarch/diffs.h"
23 #include "tla/libarch/changeset-utils.h"
24 #include "tla/libarch/changeset-report.h"
25
26
27 /* __STDC__ prototypes for static functions */
28 static void fix_mod_locs (assoc_table loc_of, rel_table table);
29 static void arch_print_file_metadata_diffs (int out_fd,
30 struct arch_changeset_report * report,
31 int escape_classes);
32 static void arch_print_dir_metadata_diffs (int out_fd,
33 struct arch_changeset_report * report,
34 int escape_classes);
35 static void print_custom_diffs(int fd, struct arch_changeset_report * report,
36 t_uchar * orig_dir, t_uchar * mod_dir,
37 t_uchar ** opts);
38
39 static void arch_print_file_diffs (int out_fd,
40 struct arch_changeset_report * report,
41 int escape_classes);
42 static void arch_print_added_file_diffs (int out_fd,
43 struct arch_changeset_report * report,
44 int escape_classes);
45 static void
46 print_removed_file_diffs (int fd, struct arch_changeset_report * report,
47 t_uchar ** opts);
48 static void print_file_metadata (int out_fd,
49 struct arch_changeset_report * report,
50 int escape_classes);
51 static void print_dir_metadata (int out_fd,
52 struct arch_changeset_report * report,
53 int escape_classes);
54 static void print_diffs_new(int fd, struct arch_changeset_report * report);
55
56 static char * no_dot (char *name);
57 static void report_unique_files_and_symlinks (rel_table * files, rel_table * symlinks,
58 rel_table index,
59 t_uchar * archive_dir, t_uchar * archive_basename);
60 static void find_renames (rel_table * out, rel_table common, assoc_table orig_dir_id_of, assoc_table mod_dir_id_of);
61
62
63
64
65 int
arch_any_changes(struct arch_changeset_report * report)66 arch_any_changes (struct arch_changeset_report * report)
67 {
68 return (rel_n_records (report->removed_dirs)
69 || rel_n_records (report->added_dirs)
70
71 || rel_n_records (report->removed_files)
72 || rel_n_records (report->added_files)
73
74 || rel_n_records (report->removed_symlinks)
75 || rel_n_records (report->added_symlinks)
76
77 || rel_n_records (report->renamed_files)
78 || rel_n_records (report->renamed_dirs)
79
80 || rel_n_records (report->patched_regular_files)
81 || rel_n_records (report->patched_symlinks)
82 || rel_n_records (report->patched_binaries)
83 || rel_n_records (report->file_metadata_changed)
84 || rel_n_records (report->dir_metadata_changed)
85 || rel_n_records (report->symlink_to_file)
86 || rel_n_records (report->file_to_symlink));
87 }
88
89 void
arch_evaluate_changeset(struct arch_changeset_report * report,t_uchar * path)90 arch_evaluate_changeset (struct arch_changeset_report * report, t_uchar * path)
91 {
92 t_uchar * orig_files_index_path = 0;
93 t_uchar * orig_dirs_index_path = 0;
94 t_uchar * mod_files_index_path = 0;
95 t_uchar * mod_dirs_index_path = 0;
96
97 rel_table removed_dirs_index = rel_table_nil;
98 rel_table added_dirs_index = rel_table_nil;
99
100 rel_table removed_files_and_symlinks_index = rel_table_nil;
101 rel_table added_files_and_symlinks_index = rel_table_nil;
102
103 rel_table common_files_index = rel_table_nil; /* [0] origloc [1] modloc [2] id */
104 rel_table common_dirs_index = rel_table_nil; /* [0] origloc [1] modloc [2] id */
105
106 assoc_table orig_dir_loc_of = 0;
107 assoc_table orig_dir_id_of = 0;
108 assoc_table orig_file_loc_of = 0;
109 assoc_table orig_file_id_of = 0;
110 assoc_table mod_dir_loc_of = 0;
111 assoc_table mod_dir_id_of = 0;
112 assoc_table mod_file_loc_of = 0;
113 assoc_table mod_file_id_of = 0;
114
115
116 orig_files_index_path = file_name_in_vicinity (0, path, "orig-files-index");
117 orig_dirs_index_path = file_name_in_vicinity (0, path, "orig-dirs-index");
118 mod_files_index_path = file_name_in_vicinity (0, path, "mod-files-index");
119 mod_dirs_index_path = file_name_in_vicinity (0, path, "mod-dirs-index");
120
121 report->orig_files_index = arch_read_changeset_index (orig_files_index_path);
122 report->orig_dirs_index = arch_read_changeset_index (orig_dirs_index_path);
123 report->mod_files_index = arch_read_changeset_index (mod_files_index_path);
124 report->mod_dirs_index = arch_read_changeset_index (mod_dirs_index_path);
125
126 /* just to be sure:
127 */
128 rel_sort_table_by_field (0, report->orig_files_index, 1);
129 rel_sort_table_by_field (0, report->orig_dirs_index, 1);
130 rel_sort_table_by_field (0, report->mod_files_index, 1);
131 rel_sort_table_by_field (0, report->mod_dirs_index, 1);
132
133 orig_dir_id_of = rel_to_assoc (report->orig_dirs_index, 0, 1);
134 orig_dir_loc_of = rel_to_assoc (report->orig_dirs_index, 1, 0);
135 orig_file_id_of = rel_to_assoc (report->orig_files_index, 0, 1);
136 orig_file_loc_of = rel_to_assoc (report->orig_files_index, 1, 0);
137 mod_dir_id_of = rel_to_assoc (report->mod_dirs_index, 0, 1);
138 mod_dir_loc_of = rel_to_assoc (report->mod_dirs_index, 1, 0);
139 mod_file_id_of = rel_to_assoc (report->mod_files_index, 0, 1);
140 mod_file_loc_of = rel_to_assoc (report->mod_files_index, 1, 0);
141
142
143 /****************************************************************
144 * Removed and added directories
145 */
146
147 {
148 t_uchar * original_only_dir_metadata_file = 0;
149 t_uchar * modified_only_dir_metadata_file = 0;
150 rel_table original_only_dir_metadata = rel_table_nil;
151 rel_table modified_only_dir_metadata = rel_table_nil;
152 rel_table orig_dirs_by_name = rel_table_nil;
153 rel_table mod_dirs_by_name = rel_table_nil;
154 rel_table t; /* [0] name [1] id -- sort 0*/
155 rel_table t2; /* [0] name [1] id [2] name -- sort 0 -- of dirs sans perms */
156
157 original_only_dir_metadata_file = file_name_in_vicinity (0, path, "original-only-dir-metadata");
158 modified_only_dir_metadata_file = file_name_in_vicinity (0, path, "modified-only-dir-metadata");
159
160 original_only_dir_metadata = arch_read_changeset_dir_metadata (original_only_dir_metadata_file);
161 modified_only_dir_metadata = arch_read_changeset_dir_metadata (modified_only_dir_metadata_file);
162
163 orig_dirs_by_name = rel_copy_table (report->orig_dirs_index);
164 rel_sort_table_by_field (0, orig_dirs_by_name, 0);
165
166 mod_dirs_by_name = rel_copy_table (report->mod_dirs_index);
167 rel_sort_table_by_field (0, mod_dirs_by_name, 0);
168
169 removed_dirs_index = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);
170 added_dirs_index = rel_join (2, rel_join_output (2,0, 2,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);
171
172 t = rel_copy_table (removed_dirs_index);
173 rel_sort_table_by_field (0, t, 0);
174 report->removed_dirs = rel_join (-1, rel_join_output (1,0, 1,1, 2,1, -1), 0, 0, t, original_only_dir_metadata);
175 t2 = rel_join (1, rel_join_output (1,0, 1,1, 1,0, -1), 0, 0, t, original_only_dir_metadata);
176 rel_append_x (&report->removed_dirs, t2);
177 rel_sort_table_by_field (0, report->removed_dirs, 0);
178 rel_free_table (t);
179 rel_free_table (t2);
180 invariant (rel_n_records (report->removed_dirs) == rel_n_records (removed_dirs_index));
181
182 t = rel_copy_table (added_dirs_index);
183 rel_sort_table_by_field (0, t, 0);
184 report->added_dirs = rel_join (-1, rel_join_output (1,0, 1,1, 2,1, -1), 0, 0, t, modified_only_dir_metadata);
185 t2 = rel_join (1, rel_join_output (1,0, 1,1, 1,0, -1), 0, 0, t, modified_only_dir_metadata);
186 rel_append_x (&report->added_dirs, t2);
187 rel_sort_table_by_field (0, report->added_dirs, 0);
188 rel_free_table (t);
189 rel_free_table (t2);
190 invariant (rel_n_records (report->added_dirs) == rel_n_records (added_dirs_index));
191
192 lim_free (0, original_only_dir_metadata_file);
193 lim_free (0, modified_only_dir_metadata_file);
194 rel_free_table (original_only_dir_metadata);
195 rel_free_table (modified_only_dir_metadata);
196 rel_free_table (orig_dirs_by_name);
197 rel_free_table (mod_dirs_by_name);
198 }
199
200 /****************************************************************
201 * Removed and added filenames and symlinks
202 */
203
204 removed_files_and_symlinks_index = rel_join (1, rel_join_output (1,0, 1,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);
205 added_files_and_symlinks_index = rel_join (2, rel_join_output (2,0, 2,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);
206
207 report_unique_files_and_symlinks (&report->removed_files, &report->removed_symlinks,
208 removed_files_and_symlinks_index,
209 path, "removed-files-archive");
210
211 report_unique_files_and_symlinks (&report->added_files, &report->added_symlinks,
212 added_files_and_symlinks_index,
213 path, "new-files-archive");
214
215 /****************************************************************
216 * renamed files and dirs
217 */
218 common_dirs_index = rel_join (-1, rel_join_output (1,0, 2,0, 1,1, -1), 1, 1, report->orig_dirs_index, report->mod_dirs_index);
219 common_files_index = rel_join (-1, rel_join_output (1,0, 2,0, 1,1, -1), 1, 1, report->orig_files_index, report->mod_files_index);
220
221 find_renames (&report->renamed_dirs, common_dirs_index, orig_dir_id_of, mod_dir_id_of);
222 find_renames (&report->renamed_files, common_files_index, orig_dir_id_of, mod_dir_id_of);
223
224 /****************************************************************
225 * Identify patches
226 */
227 {
228 int lim;
229 int x;
230 int here_fd;
231 t_uchar * patches_root;
232 rel_table patches_files = rel_table_nil;
233 assoc_table saw_patches_file = 0;
234
235 patches_root = file_name_in_vicinity (0, path, "patches");
236
237 here_fd = safe_open (".", O_RDONLY, 0);
238 safe_chdir (patches_root);
239 find_files (&patches_files, ".");
240 safe_fchdir (here_fd);
241 safe_close (here_fd);
242
243 saw_patches_file = rel_to_assoc (patches_files, 0, 0);
244
245 lim = rel_n_records (patches_files);
246 for (x = 0; x < lim; ++x)
247 {
248 const t_uchar * patch_file;
249 t_uchar * patch_file_tail = 0;
250 t_uchar * patch_file_suffix = 0;
251
252 t_uchar * patch_file_loc = 0;
253 rel_table * dest = 0;
254 t_uchar * complement_patch = 0;
255
256 t_uchar * patch_file_path = 0;
257
258 rel_field id_field = rel_field_nil;
259
260 patch_file = rel_peek_str (patches_files, x, 0);
261 patch_file_suffix = str_chr_rindex (patch_file, '.');
262 patch_file_tail = file_name_tail (0, patch_file);
263
264 /* Compute the loc and the appropriate table for this
265 * this patch. (patch_file_loc and dest)
266 */
267 if (!str_cmp (patch_file_tail, "=dir-meta-orig"))
268 {
269 patch_file_loc = file_name_directory_file (0, patch_file);
270 dest = &report->dir_metadata_changed;
271 id_field = assoc_get_taking (mod_dir_id_of, rel_make_field_str (patch_file_loc));
272 }
273 else if (!str_cmp (patch_file_suffix, ".link-orig"))
274 {
275 patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
276 complement_patch = str_alloc_cat (0, patch_file_loc, ".link-mod");
277 if (assoc_get_str_taking (saw_patches_file, rel_make_field_str (complement_patch)))
278 dest = &report->patched_symlinks;
279 else
280 dest = &report->symlink_to_file;
281 id_field = assoc_get_taking (mod_file_id_of, rel_make_field_str (patch_file_loc));
282 }
283 else if (!str_cmp (patch_file_suffix, ".original"))
284 {
285 patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
286 complement_patch = str_alloc_cat (0, patch_file_loc, ".modified");
287 if (assoc_get_str_taking (saw_patches_file, rel_make_field_str (complement_patch)))
288 dest = &report->patched_binaries;
289 else
290 dest = &report->file_to_symlink;
291 id_field = assoc_get_taking (mod_file_id_of, rel_make_field_str (patch_file_loc));
292 }
293 else if (!str_cmp (patch_file_suffix, ".patch"))
294 {
295 patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
296 dest = &report->patched_regular_files;
297 id_field = assoc_get_taking (mod_file_id_of, rel_make_field_str (patch_file_loc));
298 }
299 else if (!str_cmp (patch_file_suffix, ".meta-orig"))
300 {
301 patch_file_loc = str_save_n (0, patch_file, patch_file_suffix - patch_file);
302 dest = &report->file_metadata_changed;
303 id_field = assoc_get_taking (mod_file_id_of, rel_make_field_str (patch_file_loc));
304 }
305 else
306 {
307 id_field = rel_field_nil;
308 }
309
310 if (dest && patch_file_loc)
311 {
312 patch_file_path = file_name_in_vicinity (0, patches_root, patch_file_loc);
313 rel_add_records (dest,
314 rel_make_record_3_taking (rel_make_field_str (patch_file_loc),
315 rel_field_ref (id_field),
316 rel_make_field_str (patch_file_path)),
317 rel_record_null);
318 }
319
320 rel_field_unref (id_field);
321 lim_free (0, patch_file_tail);
322 lim_free (0, patch_file_loc);
323 lim_free (0, complement_patch);
324 lim_free (0, patch_file_path);
325 }
326
327
328 lim_free (0, patches_root);
329 rel_free_table (patches_files);
330 free_assoc_table (saw_patches_file);
331 }
332
333 rel_sort_table_by_field (0, report->removed_dirs, 0);
334 rel_sort_table_by_field (0, report->added_dirs, 0);
335
336 rel_sort_table_by_field (0, report->removed_files, 0);
337 rel_sort_table_by_field (0, report->added_files, 0);
338
339 rel_sort_table_by_field (0, report->removed_symlinks, 0);
340 rel_sort_table_by_field (0, report->added_symlinks, 0);
341
342 rel_sort_table_by_field (0, report->renamed_files, 0);
343 rel_sort_table_by_field (0, report->renamed_dirs, 0);
344
345 rel_sort_table_by_field (0, report->patched_regular_files, 0);
346 rel_sort_table_by_field (0, report->patched_symlinks, 0);
347 rel_sort_table_by_field (0, report->patched_binaries, 0);
348 rel_sort_table_by_field (0, report->file_metadata_changed, 0);
349 rel_sort_table_by_field (0, report->dir_metadata_changed, 0);
350 rel_sort_table_by_field (0, report->symlink_to_file, 0);
351 rel_sort_table_by_field (0, report->file_to_symlink, 0);
352
353 lim_free (0, orig_files_index_path);
354 lim_free (0, orig_dirs_index_path);
355 lim_free (0, mod_files_index_path);
356 lim_free (0, mod_dirs_index_path);
357 rel_free_table (removed_dirs_index);
358 rel_free_table (added_dirs_index);
359 rel_free_table (removed_files_and_symlinks_index);
360 rel_free_table (added_files_and_symlinks_index);
361 free_assoc_table (orig_dir_loc_of);
362 free_assoc_table (orig_dir_id_of);
363 free_assoc_table (orig_file_loc_of);
364 free_assoc_table (orig_file_id_of);
365 free_assoc_table (mod_dir_loc_of);
366 free_assoc_table (mod_dir_id_of);
367 free_assoc_table (mod_file_loc_of);
368 free_assoc_table (mod_file_id_of);
369 rel_free_table (common_files_index);
370 rel_free_table (common_dirs_index);
371
372 }
373
374
375 void
arch_reverse_changeset(struct arch_changeset_report * report)376 arch_reverse_changeset (struct arch_changeset_report * report)
377 {
378 rel_table t;
379 int x;
380 assoc_table new_mod_file_loc_of = 0;
381 assoc_table new_mod_dir_loc_of = 0;
382
383 t = report->orig_files_index;
384 report->orig_files_index = report->mod_files_index;
385 report->mod_files_index = t;
386
387 t = report->orig_dirs_index;
388 report->orig_dirs_index = report->mod_dirs_index;
389 report->mod_dirs_index = t;
390
391 t = report->removed_dirs;
392 report->removed_dirs = report->added_dirs;
393 report->added_dirs = t;
394
395 t = report->removed_files;
396 report->removed_files = report->added_files;
397 report->added_files = t;
398
399 t = report->removed_symlinks;
400 report->removed_symlinks = report->added_symlinks;
401 report->added_symlinks = t;
402
403 for (x = 0; x < rel_n_records (report->renamed_files); ++x)
404 {
405 t_uchar * s;
406
407 s = str_save (0, rel_peek_str (report->renamed_files, x, 0));
408 rel_set_taking (report->renamed_files, x, 0, rel_get_field (report->renamed_files, x, 1));
409 rel_set_taking (report->renamed_files, x, 1, rel_make_field_str (s));
410 lim_free (0, s);
411 }
412 for (x = 0; x < rel_n_records (report->renamed_dirs); ++x)
413 {
414 t_uchar * s;
415
416 s = str_save (0, rel_peek_str (report->renamed_dirs, x, 0));
417 rel_set_taking (report->renamed_dirs, x, 0, rel_get_field (report->renamed_dirs, x, 1));
418 rel_set_taking (report->renamed_dirs, x, 1, rel_make_field_str (s));
419 lim_free (0, s);
420 }
421
422 new_mod_file_loc_of = rel_to_assoc (report->mod_files_index, 1, 0);
423 new_mod_dir_loc_of = rel_to_assoc (report->mod_dirs_index, 1, 0);
424
425 fix_mod_locs (new_mod_file_loc_of, report->patched_regular_files);
426 fix_mod_locs (new_mod_file_loc_of, report->patched_symlinks);
427 fix_mod_locs (new_mod_file_loc_of, report->patched_binaries);
428 fix_mod_locs (new_mod_file_loc_of, report->file_metadata_changed);
429 fix_mod_locs (new_mod_dir_loc_of, report->dir_metadata_changed);
430 fix_mod_locs (new_mod_file_loc_of, report->symlink_to_file);
431 fix_mod_locs (new_mod_file_loc_of, report->file_to_symlink);
432 }
433
434 static void
fix_mod_locs(assoc_table loc_of,rel_table table)435 fix_mod_locs (assoc_table loc_of, rel_table table)
436 {
437 int x;
438
439 for (x = 0; x < rel_n_records (table); ++x)
440 {
441 rel_field id_field;
442 rel_field new_loc_field;
443
444 id_field = rel_get_field (table, x, 1);
445 new_loc_field = assoc_get_taking (loc_of, id_field);
446 rel_set_taking (table, x, 0, new_loc_field);
447 }
448 }
449
450 static void
arch_print_file_metadata_diffs(int out_fd,struct arch_changeset_report * report,int escape_classes)451 arch_print_file_metadata_diffs (int out_fd,
452 struct arch_changeset_report * report,
453 int escape_classes)
454 {
455 int x;
456
457 for (x = 0; x < rel_n_records (report->file_metadata_changed); ++x)
458 {
459 t_uchar * orig_meta_path = 0;
460 t_uchar * mod_meta_path = 0;
461 t_uchar * orig_meta = 0;
462 t_uchar * mod_meta = 0;
463 t_uchar * file_metadata_changed = 0;
464
465 file_metadata_changed = pika_save_escape_iso8859_1 (0, 0, escape_classes,
466 rel_peek_str (report->file_metadata_changed, x, 0));
467 safe_printfmt (out_fd, " %s\n", file_metadata_changed);
468
469 orig_meta_path = str_alloc_cat (0, rel_peek_str (report->file_metadata_changed, x, 2), ".meta-orig");
470 mod_meta_path = str_alloc_cat (0, rel_peek_str (report->file_metadata_changed, x, 2), ".meta-mod");
471 orig_meta = read_line_from_file (orig_meta_path);
472 mod_meta = read_line_from_file (mod_meta_path);
473
474 safe_printfmt (out_fd, " %s\n => %s\n", orig_meta, mod_meta);
475
476 lim_free (0, file_metadata_changed);
477 lim_free (0, orig_meta_path);
478 lim_free (0, mod_meta_path);
479 lim_free (0, orig_meta);
480 lim_free (0, mod_meta);
481 }
482 }
483
484 static void
arch_print_dir_metadata_diffs(int out_fd,struct arch_changeset_report * report,int escape_classes)485 arch_print_dir_metadata_diffs (int out_fd,
486 struct arch_changeset_report * report,
487 int escape_classes)
488 {
489 int x;
490
491 for (x = 0; x < rel_n_records (report->dir_metadata_changed); ++x)
492 {
493 t_uchar * orig_meta_path = 0;
494 t_uchar * mod_meta_path = 0;
495 t_uchar * orig_meta = 0;
496 t_uchar * mod_meta = 0;
497 t_uchar * dir_metadata_changed = 0;
498
499 dir_metadata_changed = pika_save_escape_iso8859_1 (0, 0, escape_classes,
500 rel_peek_str (report->dir_metadata_changed, x, 0));
501 safe_printfmt (out_fd, " %s\n", dir_metadata_changed);
502
503 orig_meta_path = file_name_in_vicinity (0, rel_peek_str (report->dir_metadata_changed, x, 2), "=dir-meta-orig");
504 mod_meta_path = file_name_in_vicinity (0, rel_peek_str (report->dir_metadata_changed, x, 2), "=dir-meta-mod");
505 orig_meta = read_line_from_file (orig_meta_path);
506 mod_meta = read_line_from_file (mod_meta_path);
507
508 safe_printfmt (out_fd, " %s\n => %s\n", orig_meta, mod_meta);
509
510 lim_free (0, dir_metadata_changed);
511 lim_free (0, orig_meta_path);
512 lim_free (0, mod_meta_path);
513 lim_free (0, orig_meta);
514 lim_free (0, mod_meta);
515 }
516 }
517
518 static void
arch_print_file_diffs(int out_fd,struct arch_changeset_report * report,int escape_classes)519 arch_print_file_diffs (int out_fd,
520 struct arch_changeset_report * report,
521 int escape_classes)
522 {
523 int x;
524
525 for (x = 0; x < rel_n_records (report->patched_regular_files); ++x)
526 {
527 t_uchar * patch_file;
528 int in_fd;
529
530 patch_file = str_alloc_cat (0, rel_peek_str (report->patched_regular_files, x, 2), ".patch");
531 in_fd = safe_open (patch_file, O_RDONLY, 0);
532 copy_fd (in_fd, out_fd);
533 safe_printfmt (out_fd, "\n\n");
534 safe_close (in_fd);
535 lim_free (0, patch_file);
536 }
537 }
538
539 static void
arch_print_added_file_diffs(int out_fd,struct arch_changeset_report * report,int escape_classes)540 arch_print_added_file_diffs (int out_fd,
541 struct arch_changeset_report * report,
542 int escape_classes)
543 {
544 int x;
545
546 for (x = 0; x < rel_n_records (report->added_files); ++x)
547 {
548 arch_really_invoke_diff (1, "/dev/null", NULL, rel_peek_str (report->added_files, x, 2), NULL, NULL);
549 }
550 }
551
552
print_file_metadata(int out_fd,struct arch_changeset_report * report,int escape_classes)553 static void print_file_metadata (int out_fd, struct arch_changeset_report *
554 report, int escape_classes)
555 {
556 if (rel_n_records(report->file_metadata_changed) > 0)
557 {
558 safe_printfmt (out_fd, "* file metadata changed\n\n");
559 arch_print_file_metadata_diffs (out_fd, report, escape_classes);
560 safe_printfmt (out_fd, "\n");
561 }
562 }
563
564
print_dir_metadata(int out_fd,struct arch_changeset_report * report,int escape_classes)565 static void print_dir_metadata (int out_fd,
566 struct arch_changeset_report * report,
567 int escape_classes)
568 {
569 if (rel_n_records(report->dir_metadata_changed) > 0)
570 {
571 safe_printfmt (out_fd, "* dir metadata changed\n\n");
572 arch_print_dir_metadata_diffs (out_fd, report, escape_classes);
573 safe_printfmt (out_fd, "\n");
574 }
575 }
576
print_diffs_new(int out_fd,struct arch_changeset_report * report)577 static void print_diffs_new(int out_fd, struct arch_changeset_report * report)
578 {
579 if (rel_n_records(report->patched_regular_files))
580 {
581 safe_printfmt (out_fd, "* modified files\n\n");
582 arch_print_file_diffs (out_fd, report, 0);
583 safe_printfmt (out_fd, "\n");
584 }
585 if (rel_n_records(report->added_files))
586 {
587 safe_printfmt (out_fd, "* added files\n\n");
588 arch_print_added_file_diffs (out_fd, report, 0);
589 safe_printfmt (out_fd, "\n");
590 }
591 print_removed_file_diffs(out_fd, report, NULL);
592 }
593
594
595 /**
596 * \brief Write custom diffs for to a file descriptor
597 *
598 * The changeset report is used to determine which files to diff, and the
599 * partial file paths. The orig_dir and mod_dir are used to produce full
600 * paths. diff is invoked to produce the output.
601 *
602 * \param report The report to use for output
603 * \param orig_dir The path to a copy of the ORIG tree
604 * \param mod_dir The path to a copy of the MOD tree
605 */
606 static void
print_custom_diffs(int out_fd,struct arch_changeset_report * report,t_uchar * orig_dir,t_uchar * mod_dir,t_uchar ** opts)607 print_custom_diffs (int out_fd, struct arch_changeset_report * report, t_uchar * orig_dir, t_uchar * mod_dir, t_uchar ** opts)
608 {
609 if (rel_n_records(report->patched_regular_files))
610 {
611 int x;
612 rel_field key;
613 assoc_table orig_paths = rel_to_assoc (report->orig_files_index, out_fd, 0);
614
615 safe_printfmt ( out_fd, "* modified files\n\n");
616 for (x = 0; x < rel_n_records (report->patched_regular_files); ++x)
617 {
618 t_uchar *id = str_save(0, rel_peek_str(report->patched_regular_files, x, 1)) /*report->patched_regular_files[x][1]*/;
619 t_uchar *id2 = str_save(0, rel_peek_str(report->patched_regular_files, x, 0)) /*report->patched_regular_files[x][1]*/;
620
621 t_uchar *orig_part_path;
622 t_uchar *orig_path;
623 t_uchar *mod_path;
624
625 key = rel_make_field_str(id);
626
627 rel_field_ref(key);
628
629 orig_part_path = assoc_get_str_taking(orig_paths, key) /*assoc_ref(orig_paths, id)*/;
630 orig_path = file_name_in_vicinity(0, orig_dir, orig_part_path);
631 mod_path = file_name_in_vicinity(0, mod_dir, id2 /*report->patched_regular_files[x][0]*/);
632
633 arch_really_invoke_diff ( out_fd, orig_path, orig_part_path, mod_path, id2 /*report->patched_regular_files[x][0]*/, (char **)opts);
634
635 rel_field_unref(key);
636 lim_free(0, mod_path);
637 lim_free(0, orig_path);
638 lim_free(0, id);
639 lim_free(0, id2);
640 }
641 safe_printfmt ( out_fd, "\n");
642 free_assoc_table (orig_paths);
643 }
644 if (rel_n_records(report->added_files))
645 {
646 int x;
647 safe_printfmt (1, "* added files\n\n");
648
649 for (x = 0; x < rel_n_records (report->added_files); ++x)
650 {
651 t_uchar *id = str_save(0, rel_peek_str(report->added_files, x, 2));
652 t_uchar *id2 = str_save(0, rel_peek_str(report->added_files, x, 0));
653 arch_really_invoke_diff ( out_fd, "/dev/null", NULL, id /*report->added_files[x][2]*/, id2 /*report->added_files[x][0]*/, (char**)opts);
654 lim_free(0, id);
655 lim_free(0, id2);
656 }
657 safe_printfmt ( out_fd, "\n");
658 }
659 print_removed_file_diffs ( out_fd, report, opts);
660 }
661
662
663
664 /**
665 * \brief Print the diffs for any removed files to output.
666 *
667 * If no files were removed, no output is produced
668 * \param report The changeset report to generate results for
669 * \param opts Additional parameters to pass to diff, or NULL
670 */
671 static void
print_removed_file_diffs(int out_fd,struct arch_changeset_report * report,t_uchar ** opts)672 print_removed_file_diffs (int out_fd, struct arch_changeset_report * report, t_uchar ** opts)
673 {
674 if (rel_n_records(report->removed_files))
675 {
676 int x;
677 safe_printfmt ( out_fd, "* removed files\n\n");
678
679 for (x = 0; x < rel_n_records (report->removed_files); ++x)
680 {
681 t_uchar *id = str_save(0, rel_peek_str(report->removed_files, x, 2));
682 t_uchar *id2 = str_save(0, rel_peek_str(report->removed_files, x, 0));
683 arch_really_invoke_diff ( out_fd, id /*report->removed_files[x][2]*/, id2 /*report->removed_files[x][0]*/, "/dev/null", NULL, (char**)opts);
684 lim_free(0, id);
685 lim_free(0, id2);
686 }
687 safe_printfmt ( out_fd, "\n");
688 }
689 }
690
691
692 /**
693 * \brief Write a changeset report with custom diffs to a file descriptor
694 *
695 * \param report The report to use for output
696 * \param orig_dir The path to a copy of the ORIG tree
697 * \param mod_dir The path to a copy of the MOD tree
698 * \param escape_classes The types of characters that should be escaped
699 * \todo Consider reimplementing so that a custom changeset is generated, not
700 * custom diffs.
701 */
702 void
arch_print_changeset_custom_diffs(int out_fd,struct arch_changeset_report * report,t_uchar * orig_dir,t_uchar * mod_dir,t_uchar ** opts,int escape_classes)703 arch_print_changeset_custom_diffs (int out_fd, struct arch_changeset_report * report, t_uchar * orig_dir, t_uchar * mod_dir, t_uchar ** opts, int escape_classes)
704 {
705 print_file_metadata ( out_fd, report, escape_classes);
706 print_dir_metadata ( out_fd, report, escape_classes);
707 if (opts == NULL)
708 print_diffs_new ( out_fd, report);
709 else
710 print_custom_diffs ( out_fd, report, orig_dir, mod_dir, opts);
711 }
712
713
714
715
716
717 void
arch_print_changeset_diffs(int out_fd,struct arch_changeset_report * report,int escape_classes)718 arch_print_changeset_diffs (int out_fd, struct arch_changeset_report * report, int escape_classes)
719 {
720 if (rel_n_records (report->file_metadata_changed))
721 {
722 safe_printfmt (out_fd, "* file metadata changed\n\n");
723 arch_print_file_metadata_diffs (out_fd, report, escape_classes);
724 safe_printfmt (out_fd, "\n");
725 }
726
727 if (rel_n_records (report->dir_metadata_changed))
728 {
729 safe_printfmt (out_fd, "* dir metadata changed\n\n");
730 arch_print_dir_metadata_diffs (out_fd, report, escape_classes);
731 safe_printfmt (out_fd, "\n");
732 }
733
734 if (rel_n_records (report->patched_regular_files))
735 {
736 safe_printfmt (out_fd, "* modified files\n\n");
737 arch_print_file_diffs (out_fd, report, escape_classes);
738 safe_printfmt (out_fd, "\n");
739 }
740 if (rel_n_records (report->added_files))
741 {
742 safe_printfmt (out_fd, "* added files\n\n");
743 arch_print_added_file_diffs (out_fd, report, escape_classes);
744 safe_printfmt (out_fd, "\n");
745 }
746 }
747
748
749 static char *
no_dot(char * name)750 no_dot (char *name)
751 {
752 if (name[0] == '.' && name[1] == '/')
753 return name + 2;
754 else
755 return name;
756 }
757
758 void
arch_print_changeset(int out_fd,struct arch_changeset_report * report,int diffs,int escape_classes)759 arch_print_changeset (int out_fd, struct arch_changeset_report * report, int diffs, int escape_classes)
760 {
761 int x;
762
763 if (rel_n_records (report->removed_dirs))
764 {
765 safe_printfmt (out_fd, "* removed directories\n\n");
766 for (x = 0; x < rel_n_records (report->removed_dirs); ++x)
767 {
768 t_uchar * item;
769 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
770 rel_peek_str (report->removed_dirs, x, 0));
771 safe_printfmt (out_fd, " %s\n", no_dot (item));
772 lim_free (0, item);
773 }
774 safe_printfmt (out_fd, "\n");
775 }
776 if (rel_n_records (report->added_dirs))
777 {
778 safe_printfmt (out_fd, "* added directories\n\n");
779 for (x = 0; x < rel_n_records (report->added_dirs); ++x)
780 {
781 t_uchar * item;
782 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
783 rel_peek_str (report->added_dirs, x, 0));
784 safe_printfmt (out_fd, " %s\n", no_dot (item));
785 lim_free (0, item);
786 }
787 safe_printfmt (out_fd, "\n");
788 }
789
790 if (rel_n_records (report->removed_files))
791 {
792 safe_printfmt (out_fd, "* removed files\n\n");
793 for (x = 0; x < rel_n_records (report->removed_files); ++x)
794 {
795 t_uchar * item;
796 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
797 rel_peek_str (report->removed_files, x, 0));
798 safe_printfmt (out_fd, " %s\n", no_dot (item));
799 lim_free (0, item);
800 }
801 safe_printfmt (out_fd, "\n");
802 }
803
804 if (rel_n_records (report->added_files))
805 {
806 safe_printfmt (out_fd, "* added files\n\n");
807 for (x = 0; x < rel_n_records (report->added_files); ++x)
808 {
809 t_uchar * item;
810 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
811 rel_peek_str (report->added_files, x, 0));
812 safe_printfmt (out_fd, " %s\n", no_dot (item));
813 lim_free (0, item);
814 }
815 safe_printfmt (out_fd, "\n");
816 }
817
818 if (rel_n_records (report->removed_symlinks))
819 {
820 safe_printfmt (out_fd, "* removed symlinks\n\n");
821 for (x = 0; x < rel_n_records (report->removed_symlinks); ++x)
822 {
823 t_uchar * item;
824 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
825 rel_peek_str (report->removed_symlinks, x, 0));
826 safe_printfmt (out_fd, " %s\n", no_dot (item));
827 lim_free (0, item);
828 }
829 safe_printfmt (out_fd, "\n");
830 }
831
832 if (rel_n_records (report->added_symlinks))
833 {
834 safe_printfmt (out_fd, "* added symlinks\n\n");
835 for (x = 0; x < rel_n_records (report->added_symlinks); ++x)
836 {
837 t_uchar * item;
838 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
839 rel_peek_str (report->added_symlinks, x, 0));
840 safe_printfmt (out_fd, " %s\n", no_dot (item));
841 lim_free (0, item);
842 }
843 safe_printfmt (out_fd, "\n");
844 }
845
846 if (rel_n_records (report->renamed_files))
847 {
848 safe_printfmt (out_fd, "* renamed files and symlinks\n\n");
849 for (x = 0; x < rel_n_records (report->renamed_files); ++x)
850 {
851 t_uchar * item1;
852 t_uchar * item2;
853 item1 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
854 rel_peek_str (report->renamed_files, x, 0));
855 item2 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
856 rel_peek_str (report->renamed_files, x, 1));
857 safe_printfmt (out_fd, " %s\n => %s\n\n", no_dot (item1), no_dot (item2));
858 lim_free (0, item2);
859 lim_free (0, item1);
860 }
861 }
862
863 if (rel_n_records (report->renamed_dirs))
864 {
865 safe_printfmt (out_fd, "* renamed directories\n\n");
866 for (x = 0; x < rel_n_records (report->renamed_dirs); ++x)
867 {
868 t_uchar * item1;
869 t_uchar * item2;
870 item1 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
871 rel_peek_str (report->renamed_dirs, x, 0));
872 item2 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
873 rel_peek_str (report->renamed_dirs, x, 1));
874 safe_printfmt (out_fd, " %s\n => %s\n\n", no_dot (item1), no_dot (item2));
875 lim_free (0, item2);
876 lim_free (0, item1);
877 }
878 }
879
880 if (rel_n_records (report->file_to_symlink))
881 {
882 safe_printfmt (out_fd, "* files replaced by symlinks \n\n");
883 for (x = 0; x < rel_n_records (report->file_to_symlink); ++x)
884 {
885 t_uchar * item;
886 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
887 rel_peek_str (report->file_to_symlink, x, 0));
888 safe_printfmt (out_fd, " %s\n", no_dot (item));
889 lim_free (0, item);
890 }
891 safe_printfmt (out_fd, "\n");
892 }
893
894 if (rel_n_records (report->symlink_to_file))
895 {
896 safe_printfmt (out_fd, "* symlinks replaced by files\n\n");
897 for (x = 0; x < rel_n_records (report->symlink_to_file); ++x)
898 {
899 t_uchar * item;
900 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
901 rel_peek_str (report->symlink_to_file, x, 0));
902 safe_printfmt (out_fd, " %s\n", no_dot (item));
903 lim_free (0, item);
904 }
905 safe_printfmt (out_fd, "\n");
906 }
907
908 if (rel_n_records (report->file_metadata_changed))
909 {
910 safe_printfmt (out_fd, "* file metadata changed\n\n");
911
912 if (diffs)
913 {
914 arch_print_file_metadata_diffs (out_fd, report, escape_classes);
915 }
916 else
917 {
918 for (x = 0; x < rel_n_records (report->file_metadata_changed); ++x)
919 {
920 t_uchar * item;
921 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
922 rel_peek_str (report->file_metadata_changed, x, 0));
923 safe_printfmt (out_fd, " %s\n", no_dot (item));
924 lim_free (0, item);
925 }
926 }
927 safe_printfmt (out_fd, "\n");
928 }
929
930 if (rel_n_records (report->dir_metadata_changed))
931 {
932 safe_printfmt (out_fd, "* dir metadata changed\n\n");
933
934 if (diffs)
935 {
936 arch_print_dir_metadata_diffs (out_fd, report, escape_classes);
937 }
938 else
939 {
940 for (x = 0; x < rel_n_records (report->dir_metadata_changed); ++x)
941 {
942 t_uchar * item;
943 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
944 rel_peek_str (report->dir_metadata_changed, x, 0));
945 safe_printfmt (out_fd, " %s\n", no_dot (item));
946 lim_free (0, item);
947 }
948 }
949 safe_printfmt (out_fd, "\n");
950 }
951
952 if (rel_n_records (report->patched_symlinks))
953 {
954 safe_printfmt (out_fd, "* retargeted symlinks\n\n");
955 for (x = 0; x < rel_n_records (report->patched_symlinks); ++x)
956 {
957 t_uchar * item;
958 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
959 rel_peek_str (report->patched_symlinks, x, 0));
960 safe_printfmt (out_fd, " %s\n", no_dot (item));
961 lim_free (0, item);
962 }
963 safe_printfmt (out_fd, "\n");
964 }
965
966 if (rel_n_records (report->patched_binaries))
967 {
968 safe_printfmt (out_fd, "* modified binary files\n\n");
969 for (x = 0; x < rel_n_records (report->patched_binaries); ++x)
970 {
971 t_uchar * item;
972 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
973 rel_peek_str (report->patched_binaries, x, 0));
974 safe_printfmt (out_fd, " %s\n", no_dot (item));
975 lim_free (0, item);
976 }
977 safe_printfmt (out_fd, "\n");
978 }
979
980 if (rel_n_records (report->patched_regular_files))
981 {
982 safe_printfmt (out_fd, "* modified files\n\n");
983
984 if (diffs)
985 {
986 arch_print_file_diffs (out_fd, report, escape_classes);
987 }
988 else
989 {
990 for (x = 0; x < rel_n_records (report->patched_regular_files); ++x)
991 {
992 t_uchar * item;
993 item = pika_save_escape_iso8859_1 (0, 0, escape_classes,
994 rel_peek_str (report->patched_regular_files, x, 0));
995 safe_printfmt (out_fd, " %s\n", no_dot (item));
996 lim_free (0, item);
997 }
998 }
999 safe_printfmt (out_fd, "\n");
1000 }
1001 }
1002
1003 void
arch_free_changeset_report_data(struct arch_changeset_report * r)1004 arch_free_changeset_report_data (struct arch_changeset_report * r)
1005 {
1006 rel_free_table (r->orig_files_index); r->orig_files_index = rel_table_nil;
1007 rel_free_table (r->orig_dirs_index); r->orig_dirs_index = rel_table_nil;
1008 rel_free_table (r->mod_files_index); r->mod_files_index = rel_table_nil;
1009 rel_free_table (r->mod_dirs_index); r->mod_dirs_index = rel_table_nil;
1010
1011 rel_free_table (r->removed_dirs); r->removed_dirs = rel_table_nil;
1012 rel_free_table (r->added_dirs); r->added_dirs = rel_table_nil;
1013
1014 rel_free_table (r->removed_files); r->removed_files = rel_table_nil;
1015 rel_free_table (r->added_files); r->added_files = rel_table_nil;
1016
1017 rel_free_table (r->removed_symlinks); r->removed_symlinks = rel_table_nil;
1018 rel_free_table (r->added_symlinks); r->added_symlinks = rel_table_nil;
1019
1020 rel_free_table (r->renamed_files); r->renamed_files = rel_table_nil;
1021 rel_free_table (r->renamed_dirs); r->renamed_dirs = rel_table_nil;
1022
1023 rel_free_table (r->patched_regular_files); r->patched_regular_files = rel_table_nil;
1024 rel_free_table (r->patched_symlinks); r->patched_symlinks = rel_table_nil;
1025 rel_free_table (r->patched_binaries); r->patched_binaries = rel_table_nil;
1026 rel_free_table (r->file_metadata_changed); r->file_metadata_changed = rel_table_nil;
1027 rel_free_table (r->dir_metadata_changed); r->dir_metadata_changed = rel_table_nil;
1028 rel_free_table (r->symlink_to_file); r->symlink_to_file = rel_table_nil;
1029 rel_free_table (r->file_to_symlink); r->file_to_symlink = rel_table_nil;
1030 }
1031
1032
1033
1034
1035 static void
report_unique_files_and_symlinks(rel_table * files,rel_table * symlinks,rel_table index,t_uchar * archive_dir,t_uchar * archive_basename)1036 report_unique_files_and_symlinks (rel_table * files, rel_table * symlinks,
1037 rel_table index,
1038 t_uchar * archive_dir, t_uchar * archive_basename)
1039 {
1040 t_uchar * archive = 0;
1041 int lim;
1042 int x;
1043
1044 archive = file_name_in_vicinity (0, archive_dir, archive_basename);
1045
1046 lim = rel_n_records (index);
1047 for (x = 0; x < lim; ++x)
1048 {
1049 t_uchar * archive_path;
1050 struct stat stat_buf;
1051
1052 archive_path = file_name_in_vicinity (0, archive, rel_peek_str (index, x, 0));
1053
1054 safe_lstat (archive_path, &stat_buf);
1055
1056 if (S_ISLNK (stat_buf.st_mode))
1057 {
1058 rel_add_records (symlinks,
1059 rel_make_record_3_taking (rel_get_field (index, x, 0),
1060 rel_get_field (index, x, 1),
1061 rel_make_field_str (archive_path)),
1062 rel_record_null);
1063 }
1064 else
1065 {
1066 rel_add_records (files,
1067 rel_make_record_3_taking (rel_get_field (index, x, 0),
1068 rel_get_field (index, x, 1),
1069 rel_make_field_str (archive_path)),
1070 rel_record_null);
1071 }
1072
1073 lim_free (0, archive_path);
1074 }
1075
1076 rel_sort_table_by_field (0, *files, 0);
1077 rel_sort_table_by_field (0, *symlinks, 0);
1078
1079 lim_free (0, archive);
1080 }
1081
1082 static void
find_renames(rel_table * out,rel_table common,assoc_table orig_dir_id_of,assoc_table mod_dir_id_of)1083 find_renames (rel_table * out, rel_table common, assoc_table orig_dir_id_of, assoc_table mod_dir_id_of)
1084 {
1085 int lim;
1086 int x;
1087
1088 lim = rel_n_records (common);
1089 for (x = 0; x < lim; ++x)
1090 {
1091 rel_field orig_loc_field;
1092 rel_field mod_loc_field;
1093 rel_field id_field;
1094
1095 const t_uchar * orig_loc;
1096 const t_uchar * mod_loc;
1097 const t_uchar * id;
1098
1099 t_uchar * orig_basename = 0;
1100 t_uchar * mod_basename = 0;
1101 t_uchar * orig_dir = 0;
1102 t_uchar * mod_dir = 0;
1103 const t_uchar * orig_dir_id;
1104 const t_uchar * mod_dir_id;
1105
1106 orig_loc_field = rel_get_field (common, x, 0);
1107 orig_loc = rel_field_str (orig_loc_field);
1108
1109 mod_loc_field = rel_get_field (common, x, 1);
1110 mod_loc = rel_field_str (mod_loc_field);
1111
1112 id_field = rel_get_field (common, x, 2);
1113 id = rel_field_str (id_field);
1114
1115 orig_basename = file_name_tail (0, orig_loc);
1116 mod_basename = file_name_tail (0, mod_loc);
1117
1118 orig_dir = file_name_directory_file (0, orig_loc);
1119 mod_dir = file_name_directory_file (0, mod_loc);
1120
1121 orig_dir_id = assoc_get_str_taking (orig_dir_id_of, rel_make_field_str (orig_dir));
1122 mod_dir_id = assoc_get_str_taking (mod_dir_id_of, rel_make_field_str (mod_dir));
1123
1124 /* The conditional subexpression here attempts to identify renames
1125 * even when the the changeset is missing locs and ids for
1126 * one or both of the containing dirs of the object under consideration.
1127 *
1128 */
1129
1130 if (str_cmp (orig_basename, mod_basename)
1131 || ((orig_dir_id && mod_dir_id)
1132 ? str_cmp (orig_dir_id, mod_dir_id)
1133 : str_cmp (orig_dir, mod_dir)))
1134 {
1135 rel_field_ref (orig_loc_field);
1136 rel_field_ref (mod_loc_field);
1137 rel_field_ref (id_field);
1138 rel_add_records (out, rel_make_record_3_taking (orig_loc_field,
1139 mod_loc_field,
1140 id_field),
1141 rel_record_null);
1142 }
1143
1144
1145 rel_field_unref (orig_loc_field);
1146 rel_field_unref (mod_loc_field);
1147 rel_field_unref (id_field);
1148
1149 lim_free (0, orig_basename);
1150 lim_free (0, mod_basename);
1151 lim_free (0, orig_dir);
1152 lim_free (0, mod_dir);
1153 }
1154 }
1155
1156
1157
1158
1159
1160 /* tag: Tom Lord Thu May 15 13:07:24 2003 (changeset-report.c)
1161 */
1162