/* make-changeset.c:
*
* vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
****************************************************************
* 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/mem/mem.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/sort/qsort.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "libfsutils/ensure-dir.h"
#include "libfsutils/dir-as-cwd.h"
#include "libfsutils/copy-file.h"
#include "libfsutils/link-target.h"
#include "libawk/associative.h"
#include "po/gettext.h"
#include "libarch/arch.h"
#include "libarch/debug.h"
#include "libarch/diffs.h"
#include "libarch/invent.h"
#include "libarch/project-tree.h"
#include "libarch/make-changeset.h"
typedef struct changeset_maker_
{
rel_table * orig_index_names_output;
rel_table * mod_index_names_output;
struct arch_make_changeset_report * report;
int escape_classes;
t_uchar * patches;
} changeset_maker;
/* This struct isn't strictly used by write_changeset, but contains lots of auxiliary data
* that might be of use elsewhere
*/
struct changeset_vars_ids
{
rel_table orig_dir_ids;
rel_table mod_dir_ids;
rel_table original_only_dir_ids;
rel_table modified_only_dir_ids;
rel_table dir_ids_in_both;
rel_table orig_file_ids;
rel_table mod_file_ids;
rel_table original_only_file_ids;
rel_table modified_only_file_ids;
rel_table file_ids_in_both;
};
struct changeset_vars
{
/* Original and modified trees */
arch_project_tree_t * orig_tree;
arch_project_tree_t * mod_tree;
/* Lists of names of items changed in some way, or common between both */
rel_table original_only_dirs;
rel_table modified_only_dirs;
rel_table original_only_files;
rel_table modified_only_files;
rel_table files_in_both;
rel_table dirs_in_both;
rel_table renamed_dirs;
rel_table renamed_files;
rel_table orig_dirs_full_index_by_name;
rel_table orig_files_full_index_by_name;
rel_table mod_dirs_full_index_by_name;
rel_table mod_files_full_index_by_name;
/* Extra data */
rel_table orig_full_index;
rel_table mod_full_index;
rel_table mod_full_index_by_name;
rel_table orig_full_index_by_name;
assoc_table orig_ids_seen;
assoc_table mod_ids_seen;
struct changeset_vars_ids ids;
};
#if !defined(__GNUC__)
# undef __attribute__
# define __attribute__(X)
#endif
static void __attribute__((format (printf, 2, 3))) invoke_report_callback (struct arch_make_changeset_report * report, char * fmt, ...);
static void compute_renames (rel_table * out,
assoc_table loc_of_orig_X_id,
assoc_table mod_dir_id_of,
assoc_table orig_container_dir_id_of_X_id,
rel_table X_index);
static void write_dir_metadata (t_uchar * file, t_uchar * tree_root, rel_table dirs);
static void copy_files_and_symlinks (t_uchar * dest, t_uchar * tree_root, rel_table loc_list);
static void emit_dir_patches (rel_table * orig_index_names_output,
rel_table * mod_index_names_output,
struct arch_make_changeset_report * report,
t_uchar * patches,
t_uchar * orig,
t_uchar * orig_loc,
t_uchar * mod,
t_uchar * mod_loc,
int escape_classes);
static void emit_file_or_symlink_patches (rel_table * orig_index_names_output,
rel_table * mod_index_names_output,
struct arch_make_changeset_report * report,
t_uchar * patches,
t_uchar * orig,
t_uchar * orig_loc,
t_uchar * mod,
t_uchar * mod_loc,
t_uchar * id,
assoc_table inode_sig_shortcuts_of_mod,
int link_same,
int escape_classes);
static void compute_parent_dir_closure (rel_table * orig_newname_dirs, rel_table * mod_newname_dirs,
rel_table orig_newname_files, rel_table mod_newname_files,
assoc_table orig_dir_loc_of, assoc_table orig_dir_id_of,
assoc_table orig_container_dir_id_of_dir_id, assoc_table orig_container_dir_id_of_file_id,
assoc_table mod_dir_loc_of, assoc_table mod_dir_id_of,
assoc_table mod_container_dir_id_of_dir_id, assoc_table mod_container_dir_id_of_file_id,
assoc_table orig_file_id_of, assoc_table mod_file_id_of);
static void schedule_missing_containers (rel_table * added_orig_container_ids, assoc_table * orig_has_dir_id,
rel_table * added_mod_container_ids, assoc_table * mod_has_dir_id,
rel_table locs_in, assoc_table id_of_dir_loc,
assoc_table first_container_id_of_id, assoc_table container_id_of_dir_id,
assoc_table orig_dir_loc_of, assoc_table mod_dir_loc_of);
static void container_closure (rel_table * locs_out,
assoc_table * has_ids,
t_uchar * deep_id,
assoc_table dir_loc_of,
assoc_table container_dir_id_of_dir_id);
static void link_force (t_uchar *old_path, t_uchar *new_path);
static void emit_symlink_patch (changeset_maker *self,
t_uchar * orig_loc,
t_uchar * orig_target,
t_uchar * mod_loc,
t_uchar * mod_target);
static void note_change (changeset_maker *self,
t_uchar * orig_loc,
t_uchar * mod_loc);
static void rel_merge_to_assoc_unique (rel_table from,
assoc_table *to,
t_uchar const *error_message);
static rel_table canonicalise_limits (rel_table limits_spec, t_uchar const * mod_root);
static void make_changeset_inventory (struct arch_changeset_inventory * index, arch_project_tree_t *tree,
enum arch_id_tagging_method method,
enum arch_inventory_category untagged_source_category,
int escape_classes,
rel_table *orig_full_index,
rel_table *orig_full_index_by_name);
void
arch_free_make_changeset_report_data (struct arch_make_changeset_report * report)
{
arch_free_changeset_inventory_data (&report->orig_index);
arch_free_changeset_inventory_data (&report->mod_index);
free_assoc_table (report->orig_dir_id_of);
free_assoc_table (report->orig_dir_loc_of);
free_assoc_table (report->orig_file_id_of);
free_assoc_table (report->orig_file_loc_of);
free_assoc_table (report->mod_dir_id_of);
free_assoc_table (report->mod_dir_loc_of);
free_assoc_table (report->mod_file_id_of);
free_assoc_table (report->mod_file_loc_of);
free_assoc_table (report->orig_container_dir_id_of_dir_id);
free_assoc_table (report->orig_container_dir_id_of_file_id);
free_assoc_table (report->mod_container_dir_id_of_dir_id);
free_assoc_table (report->mod_container_dir_id_of_file_id);
rel_free_table (report->renamed_dirs);
rel_free_table (report->renamed_files);
rel_free_table (report->modified_files);
rel_free_table (report->perms_changed_files);
rel_free_table (report->perms_changed_dirs);
rel_free_table (report->added_files);
rel_free_table (report->added_dirs);
rel_free_table (report->removed_files);
rel_free_table (report->removed_dirs);
}
void
invoke_report_callback (struct arch_make_changeset_report * report, char * fmt, ...)
{
va_list ap;
va_start (ap, fmt);
report->callback (report->thunk, fmt, ap);
va_end (ap);
}
void
rel_merge_to_assoc_unique (rel_table from, assoc_table *to, t_uchar const *error_message)
{
int lim = rel_n_records (from);
int position;
for (position = 0; position < lim; ++position)
{
if (assoc_ref (*to, from[position][1]))
panic (error_message);
assoc_set (to, from[position][1], from[position][0]);
}
}
/**
* \brief convert user supplied limit paths into in-tree paths, eliminating limits outside the tree or in sub-projects.
*/
rel_table
canonicalise_limits (rel_table limits_spec,
t_uchar const * mod_root)
{
rel_table limits = NULL;
rel_table lim_spec_locs = 0;
int mod_root_length;
int l;
if (!limits_spec)
return NULL;
mod_root_length = str_length (mod_root);
for (l = 0; l < rel_n_records (limits_spec); ++l)
{
t_uchar * spec = 0;
t_uchar * spec_dir = 0;
t_uchar * spec_tail = 0;
t_uchar * spec_dir_abs = 0;
t_uchar * spec_loc = 0;
spec = limits_spec[l][0];
spec_dir = file_name_directory_file (0, spec);
if (!spec_dir)
spec_dir = str_save (0, ".");
spec_tail = file_name_tail (0, spec);
spec_dir_abs = directory_as_cwd (spec_dir);
if (str_cmp_prefix (mod_root, spec_dir_abs) || (spec_dir_abs[mod_root_length] && (spec_dir_abs[mod_root_length] != '/')))
{
safe_printfmt (2, "make-changeset: limit %s is not in tree %s\n", spec, mod_root);
exit (2);
}
spec_loc = str_alloc_cat_many (0, ".", spec_dir_abs + mod_root_length, "/", spec_tail, str_end);
rel_add_records (&lim_spec_locs, rel_make_record (spec_loc, 0), 0);
lim_free (0, spec);
lim_free (0, spec_dir);
lim_free (0, spec_tail);
lim_free (0, spec_dir_abs);
lim_free (0, spec_loc);
}
for (l = 0; l < rel_n_records (lim_spec_locs); ++l)
{
int m;
int dont_want = 0;
for (m = 0; (!dont_want && (m < rel_n_records (limits))); ++m)
{
if (file_name_is_path_prefix (limits[m][0], lim_spec_locs[l][0]))
{
dont_want = 1;
}
else if (file_name_is_path_prefix (lim_spec_locs[l][0], limits[m][0]))
{
rel_replace_record (limits, m, rel_make_record
(lim_spec_locs[l][0], NULL));
dont_want = 1;
}
}
if (!dont_want)
rel_add_records (&limits, rel_make_record (lim_spec_locs[l][0], 0), 0);
}
rel_sort_table_by_field (0, limits, 0);
rel_free_table (lim_spec_locs);
return limits;
}
void
make_changeset_inventory (struct arch_changeset_inventory * index, arch_project_tree_t * tree,
enum arch_id_tagging_method method,
enum arch_inventory_category untagged_source_category,
int escape_classes,
rel_table *full_index,
rel_table *full_index_by_name)
{
mem_set0 ((t_uchar *)index, sizeof (*index));
arch_changeset_inventory (index, tree, method,
untagged_source_category, escape_classes);
*full_index = rel_copy_table (index->dirs);
rel_append_x (full_index, index->files);
rel_add_records (full_index, rel_make_record (".", "?_.", 0), 0);
rel_sort_table_by_field (0, *full_index, 1);
*full_index_by_name = rel_copy_table (*full_index);
rel_sort_table_by_field (0, *full_index_by_name, 0);
}
static void
arch_free_changeset_vars_ids(struct changeset_vars_ids * csi)
{
rel_free_table (csi->orig_dir_ids);
rel_free_table (csi->mod_dir_ids);
rel_free_table (csi->original_only_dir_ids);
rel_free_table (csi->modified_only_dir_ids);
rel_free_table (csi->dir_ids_in_both);
rel_free_table (csi->orig_file_ids);
rel_free_table (csi->mod_file_ids);
rel_free_table (csi->original_only_file_ids);
rel_free_table (csi->modified_only_file_ids);
rel_free_table (csi->file_ids_in_both);
}
static void
arch_free_changeset_vars(struct changeset_vars * cs)
{
rel_free_table (cs->original_only_dirs);
rel_free_table (cs->modified_only_dirs);
rel_free_table (cs->original_only_files);
rel_free_table (cs->modified_only_files);
rel_free_table (cs->files_in_both);
rel_free_table (cs->dirs_in_both);
rel_free_table (cs->renamed_dirs);
rel_free_table (cs->renamed_files);
rel_free_table (cs->orig_dirs_full_index_by_name);
rel_free_table (cs->orig_files_full_index_by_name);
rel_free_table (cs->mod_dirs_full_index_by_name);
rel_free_table (cs->mod_files_full_index_by_name);
rel_free_table (cs->orig_full_index);
rel_free_table (cs->mod_full_index);
rel_free_table (cs->mod_full_index_by_name);
rel_free_table (cs->orig_full_index_by_name);
free_assoc_table (cs->orig_ids_seen);
free_assoc_table (cs->mod_ids_seen);
arch_free_changeset_vars_ids(&cs->ids);
}
static void
arch_detect_changeset (struct arch_make_changeset_report * report,
struct changeset_vars * cs,
arch_project_tree_t * orig_tree, arch_project_tree_t * mod_tree,
enum arch_id_tagging_method method,
enum arch_inventory_category untagged_source_category,
rel_table limits_spec,
int escape_classes)
{
int here_fd;
rel_table limits;
/* For array iteration in for() loops */
int lim;
int x;
here_fd = safe_open (".", O_RDONLY, 0);
limits = canonicalise_limits (limits_spec, mod_tree->root);
/* inventory orig and mod
*/
make_changeset_inventory (&report->orig_index, orig_tree, method, untagged_source_category, escape_classes, &cs->orig_full_index, &cs->orig_full_index_by_name);
make_changeset_inventory (&report->mod_index, mod_tree, method, untagged_source_category, escape_classes, &cs->mod_full_index, &cs->mod_full_index_by_name);
/* if limits were specified, smudge the MOD index and load-up the translation table
*/
if (limits)
{
/* The goal here is to create a fake mod index -- the index of the
* mod tree as it would be after we ignore changes outside the limits.
*
* If there were apparently renames across the limit boundaries, we have to
* fail.
*
* Additionally, we need a "mod-path-translation" -- a mapping from fake-inventory
* locs to actual mod locs to use when diffing. If we want to pretend/presume
* that some item hasn't been modified, it's left out of this translation.
*/
/* Three cases of items to deal with in the fake inventory:
*
* 1) Items common to both ORIG and MOD
* 2) Items unique to ORIG
* 3) Items unique to MOD
*
*
* Little lemma: for each (canonicalized) limit $PREFIX/$TAIL,
* the item in MOD with loc $PREFIX must exist in ORIG. Because,
* otherwise, the limit wants to add files to dirs that have not been
* added to ORIG (a user error).
*
*/
/* currently, limits is just [0] == mod_path */
safe_printfmt (2, "limits in make_changeset is current unimplemented\n");
exit (2);
}
cs->orig_dirs_full_index_by_name = rel_copy_table (report->orig_index.dirs);
rel_sort_table_by_field (0, cs->orig_dirs_full_index_by_name, 0);
cs->mod_dirs_full_index_by_name = rel_copy_table (report->mod_index.dirs);
rel_sort_table_by_field (0, cs->mod_dirs_full_index_by_name, 0);
{
rel_cut_spec spec = rel_cut_list (1, -1);
cs->ids.orig_dir_ids = rel_cut (spec, report->orig_index.dirs);
cs->ids.mod_dir_ids = rel_cut (spec, report->mod_index.dirs);
rel_cut_spec_finalise (&spec);
}
cs->ids.original_only_dir_ids = rel_join (1, rel_join_output (1,0, -1), 0, 0, cs->ids.orig_dir_ids, cs->ids.mod_dir_ids);
cs->ids.modified_only_dir_ids = rel_join (2, rel_join_output (2,0, -1), 0, 0, cs->ids.orig_dir_ids, cs->ids.mod_dir_ids);
cs->ids.dir_ids_in_both = rel_join (-1, rel_join_output (0,0, -1), 0, 0, cs->ids.orig_dir_ids, cs->ids.mod_dir_ids);
cs->original_only_dirs = rel_join (-1, rel_join_output (2,0, -1), 0, 1, cs->ids.original_only_dir_ids, report->orig_index.dirs);
rel_sort_table_by_field (0, cs->original_only_dirs, 0);
cs->modified_only_dirs = rel_join (-1, rel_join_output (2,0, -1), 0, 1, cs->ids.modified_only_dir_ids, report->mod_index.dirs);
rel_sort_table_by_field (0, cs->modified_only_dirs, 0);
/****************************************************************
* build associative tables to and from ids and locs
*/
lim = rel_n_records (report->orig_index.dirs);
for (x = 0; x < lim; ++x)
{
assoc_set (&report->orig_dir_id_of, report->orig_index.dirs[x][0], report->orig_index.dirs[x][1]);
assoc_set (&report->orig_dir_loc_of, report->orig_index.dirs[x][1], report->orig_index.dirs[x][0]);
}
lim = rel_n_records (report->orig_index.files);
for (x = 0; x < lim; ++x)
{
assoc_set (&report->orig_file_id_of, report->orig_index.files[x][0], report->orig_index.files[x][1]);
assoc_set (&report->orig_file_loc_of, report->orig_index.files[x][1], report->orig_index.files[x][0]);
}
lim = rel_n_records (report->mod_index.dirs);
for (x = 0; x < lim; ++x)
{
assoc_set (&report->mod_dir_id_of, report->mod_index.dirs[x][0], report->mod_index.dirs[x][1]);
assoc_set (&report->mod_dir_loc_of, report->mod_index.dirs[x][1], report->mod_index.dirs[x][0]);
}
lim = rel_n_records (report->mod_index.files);
for (x = 0; x < lim; ++x)
{
assoc_set (&report->mod_file_id_of, report->mod_index.files[x][0], report->mod_index.files[x][1]);
assoc_set (&report->mod_file_loc_of, report->mod_index.files[x][1], report->mod_index.files[x][0]);
}
assoc_set (&report->orig_dir_id_of, ".", "?_.");
assoc_set (&report->mod_dir_id_of, ".", "?_.");
assoc_set (&report->orig_dir_loc_of, "?_.", ".");
assoc_set (&report->mod_dir_loc_of, "?_.", ".");
arch_make_changeset_compute_container_map (&report->orig_container_dir_id_of_dir_id, report->orig_dir_id_of, report->orig_index.dirs);
arch_make_changeset_compute_container_map (&report->orig_container_dir_id_of_file_id, report->orig_dir_id_of, report->orig_index.files);
arch_make_changeset_compute_container_map (&report->mod_container_dir_id_of_dir_id, report->mod_dir_id_of, report->mod_index.dirs);
arch_make_changeset_compute_container_map (&report->mod_container_dir_id_of_file_id, report->mod_dir_id_of, report->mod_index.files);
/****************************************************************
* figure out which directories have been renamed.
*/
compute_renames (&cs->renamed_dirs, report->orig_dir_loc_of, report->mod_dir_id_of, report->orig_container_dir_id_of_dir_id, report->mod_index.dirs);
/****************************************************************
* file lists
*/
cs->orig_files_full_index_by_name = rel_copy_table (report->orig_index.files);
rel_sort_table_by_field (0, cs->orig_files_full_index_by_name, 0);
cs->mod_files_full_index_by_name = rel_copy_table (report->mod_index.files);
rel_sort_table_by_field (0, cs->mod_files_full_index_by_name, 0);
{
rel_cut_spec spec = rel_cut_list (1, -1);
cs->ids.orig_file_ids = rel_cut (spec, report->orig_index.files);
cs->ids.mod_file_ids = rel_cut (spec, report->mod_index.files);
rel_cut_spec_finalise (&spec);
}
cs->ids.original_only_file_ids = rel_join (1, rel_join_output (1,0, -1), 0, 0, cs->ids.orig_file_ids, cs->ids.mod_file_ids);
cs->ids.modified_only_file_ids = rel_join (2, rel_join_output (2,0, -1), 0, 0, cs->ids.orig_file_ids, cs->ids.mod_file_ids);
cs->ids.file_ids_in_both = rel_join (-1, rel_join_output (1,0, -1), 0, 0, cs->ids.orig_file_ids, cs->ids.mod_file_ids);
cs->original_only_files = rel_join (-1, rel_join_output (2,0, -1), 0, 1, cs->ids.original_only_file_ids, report->orig_index.files);
rel_sort_table_by_field (0, cs->original_only_files, 0);
cs->modified_only_files = rel_join (-1, rel_join_output (2,0, -1), 0, 1, cs->ids.modified_only_file_ids, report->mod_index.files);
rel_sort_table_by_field (0, cs->modified_only_files, 0);
compute_renames (&cs->renamed_files, report->orig_file_loc_of, report->mod_dir_id_of, report->orig_container_dir_id_of_file_id, report->mod_index.files);
/***************************************************************
* {files,dir}_in_both [0] orig_loc [1] mod_loc
*/
{
rel_table tmp_table;
tmp_table = rel_join (-1, rel_join_output (1,0, 2,0, -1), 0, 1, cs->ids.file_ids_in_both, report->mod_index.files);
cs->files_in_both = rel_join (-1, rel_join_output (2,0, 1,1, 1,0, -1), 0, 1, tmp_table, report->orig_index.files);
rel_free_table (tmp_table);
}
{
rel_table tmp_table;
tmp_table = rel_join (-1, rel_join_output (1,0, 2,0, -1), 0, 1, cs->ids.dir_ids_in_both, report->mod_index.dirs);
cs->dirs_in_both = rel_join (-1, rel_join_output (2,0, 1,1, -1), 0, 1, tmp_table, report->orig_index.dirs);
rel_free_table (tmp_table);
}
/****************************************************************
* Check for Duplicate Inventory Ids
*/
rel_merge_to_assoc_unique (report->orig_index.dirs, &cs->orig_ids_seen, _("duplicate ids in ORIG tree, try status --lint"));
rel_merge_to_assoc_unique (report->orig_index.files, &cs->orig_ids_seen, _("duplicate ids in ORIG tree, try status --lint"));
rel_merge_to_assoc_unique (report->mod_index.dirs, &cs->mod_ids_seen, _("duplicate ids in MOD tree, try status --lint"));
rel_merge_to_assoc_unique (report->mod_index.files, &cs->mod_ids_seen, _("duplicate ids in MOD tree, try status --lint"));
safe_fchdir (here_fd);
safe_close (here_fd);
rel_free_table (limits);
}
static void
arch_write_changeset(struct arch_make_changeset_report * report,
struct changeset_vars * cs,
t_uchar * dest_spec,
assoc_table inode_sig_shortcuts_of_mod,
int link_same,
int escape_classes)
{
int here_fd;
t_uchar * dest;
t_uchar * original_only_dir_metadata_file = 0;
t_uchar * modified_only_dir_metadata_file = 0;
t_uchar * removed_files_archive_dir = 0;
t_uchar * new_files_archive_dir = 0;
t_uchar * patches_dir = 0;
rel_table orig_files_index_names_output = 0;
rel_table mod_files_index_names_output = 0;
rel_table orig_dirs_index_names_output = 0;
rel_table mod_dirs_index_names_output = 0;
t_uchar * orig_files_index_file = 0;
t_uchar * orig_dirs_index_file = 0;
t_uchar * mod_files_index_file = 0;
t_uchar * mod_dirs_index_file = 0;
/* Useful for iterating over arrays in for() loops */
int lim;
int x;
here_fd = safe_open (".", O_RDONLY, 0);
/* create the output directory
*/
safe_mkdir (dest_spec, 0777);
safe_chdir (dest_spec);
dest = safe_current_working_directory ();
safe_fchdir (here_fd);
/****************************************************************
* Capture the Permissions of Removed and New Directories
*/
original_only_dir_metadata_file = file_name_in_vicinity (0, dest, "original-only-dir-metadata");
modified_only_dir_metadata_file = file_name_in_vicinity (0, dest, "modified-only-dir-metadata");
write_dir_metadata (original_only_dir_metadata_file, cs->orig_tree->root, cs->original_only_dirs);
write_dir_metadata (modified_only_dir_metadata_file, cs->mod_tree->root, cs->modified_only_dirs);
if (report)
{
report->removed_dirs = rel_copy_table (cs->original_only_dirs);
report->added_dirs = rel_copy_table (cs->modified_only_dirs);
if (report->callback)
{
lim = rel_n_records (report->removed_dirs);
for (x = 0; x < lim; ++x)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->removed_dirs[x][0]);
invoke_report_callback (report, "D/ %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
lim = rel_n_records (report->added_dirs);
for (x = 0; x < lim; ++x)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->added_dirs[x][0]);
invoke_report_callback (report, "A/ %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
}
/****************************************************************
* Save Copies of new and removed files.
*/
removed_files_archive_dir = file_name_in_vicinity (0, dest, "removed-files-archive");
new_files_archive_dir = file_name_in_vicinity (0, dest, "new-files-archive");
safe_mkdir (removed_files_archive_dir, 0777);
safe_mkdir (new_files_archive_dir, 0777);
copy_files_and_symlinks (removed_files_archive_dir, cs->orig_tree->root, cs->original_only_files);
copy_files_and_symlinks (new_files_archive_dir, cs->mod_tree->root, cs->modified_only_files);
if (report)
{
report->removed_files = rel_copy_table (cs->original_only_files);
report->added_files = rel_copy_table (cs->modified_only_files);
if (report->callback)
{
lim = rel_n_records (report->removed_files);
for (x = 0; x < lim; ++x)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->removed_files[x][0]);
invoke_report_callback (report, "D %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
lim = rel_n_records (report->added_files);
for (x = 0; x < lim; ++x)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->added_files[x][0]);
invoke_report_callback (report, "A %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
}
/****************************************************************
* Save patches for common files and directories.
*/
patches_dir = file_name_in_vicinity (0, dest, "patches");
safe_mkdir (patches_dir, 0777);
lim = rel_n_records (cs->files_in_both);
for (x = 0; x < lim; ++x)
{
emit_file_or_symlink_patches (&orig_files_index_names_output, &mod_files_index_names_output, report,
patches_dir, cs->orig_tree->root, cs->files_in_both[x][0],
cs->mod_tree->root, cs->files_in_both[x][1],
cs->files_in_both[x][2],
inode_sig_shortcuts_of_mod, link_same, escape_classes);
}
lim = rel_n_records (cs->dirs_in_both);
for (x = 0; x < lim; ++x)
{
emit_dir_patches (&orig_dirs_index_names_output, &mod_dirs_index_names_output, report,
patches_dir, cs->orig_tree->root, cs->dirs_in_both[x][0], cs->mod_tree->root, cs->dirs_in_both[x][1], escape_classes);
}
/****************************************************************
* Write the file indexes.
*/
orig_files_index_file = file_name_in_vicinity (0, dest, "orig-files-index");
orig_dirs_index_file = file_name_in_vicinity (0, dest, "orig-dirs-index");
mod_files_index_file = file_name_in_vicinity (0, dest, "mod-files-index");
mod_dirs_index_file = file_name_in_vicinity (0, dest, "mod-dirs-index");
{
rel_table orig_newname_dir_names = 0;
rel_table orig_newname_file_names = 0;
rel_table mod_newname_dir_names = 0;
rel_table mod_newname_file_names = 0;
rel_table orig_dirs_index_final = 0;
rel_table orig_files_index_final = 0;
rel_table mod_dirs_index_final = 0;
rel_table mod_files_index_final = 0;
int orig_dirs_index_fd;
int orig_files_index_fd;
int mod_dirs_index_fd;
int mod_files_index_fd;
{
rel_cut_spec spec = rel_cut_list (0, -1);
orig_newname_dir_names = rel_cut (spec, cs->renamed_dirs);
orig_newname_file_names = rel_cut (spec, cs->renamed_files);
rel_cut_spec_finalise (&spec);
spec = rel_cut_list (1, -1);
mod_newname_dir_names = rel_cut (spec, cs->renamed_dirs);
mod_newname_file_names = rel_cut (spec, cs->renamed_files);
rel_cut_spec_finalise (&spec);
}
rel_append_x (&orig_newname_dir_names, cs->original_only_dirs);
rel_append_x (&orig_newname_file_names, cs->original_only_files);
rel_append_x (&mod_newname_dir_names, cs->modified_only_dirs);
rel_append_x (&mod_newname_file_names, cs->modified_only_files);
compute_parent_dir_closure (&orig_newname_dir_names, &mod_newname_dir_names,
orig_newname_file_names, mod_newname_file_names,
report->orig_dir_loc_of, report->orig_dir_id_of,
report->orig_container_dir_id_of_dir_id, report->orig_container_dir_id_of_file_id,
report->mod_dir_loc_of, report->mod_dir_id_of,
report->mod_container_dir_id_of_dir_id, report->mod_container_dir_id_of_file_id,
report->orig_file_id_of, report->mod_file_id_of);
rel_append_x (&orig_dirs_index_names_output, orig_newname_dir_names);
rel_append_x (&orig_files_index_names_output, orig_newname_file_names);
rel_append_x (&mod_dirs_index_names_output, mod_newname_dir_names);
rel_append_x (&mod_files_index_names_output, mod_newname_file_names);
rel_sort_table_by_field (0, orig_dirs_index_names_output, 0);
rel_sort_table_by_field (0, orig_files_index_names_output, 0);
rel_sort_table_by_field (0, mod_dirs_index_names_output, 0);
rel_sort_table_by_field (0, mod_files_index_names_output, 0);
rel_uniq_by_field (&orig_dirs_index_names_output, 0);
rel_uniq_by_field (&orig_files_index_names_output, 0);
rel_uniq_by_field (&mod_dirs_index_names_output, 0);
rel_uniq_by_field (&mod_files_index_names_output, 0);
orig_dirs_index_final = rel_join (-1, rel_join_output (2,0, 2,1, -1), 0, 0, orig_dirs_index_names_output, cs->orig_dirs_full_index_by_name);
orig_files_index_final = rel_join (-1, rel_join_output (2,0, 2,1, -1), 0, 0, orig_files_index_names_output, cs->orig_files_full_index_by_name);
mod_dirs_index_final = rel_join (-1, rel_join_output (2,0, 2,1, -1), 0, 0, mod_dirs_index_names_output, cs->mod_dirs_full_index_by_name);
mod_files_index_final = rel_join (-1, rel_join_output (2,0, 2,1, -1), 0, 0, mod_files_index_names_output, cs->mod_files_full_index_by_name);
rel_sort_table_by_field (0, orig_dirs_index_final, 1);
rel_sort_table_by_field (0, orig_files_index_final, 1);
rel_sort_table_by_field (0, mod_dirs_index_final, 1);
rel_sort_table_by_field (0, mod_files_index_final, 1);
orig_dirs_index_fd = safe_open (orig_dirs_index_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
orig_files_index_fd = safe_open (orig_files_index_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
mod_dirs_index_fd = safe_open (mod_dirs_index_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
mod_files_index_fd = safe_open (mod_files_index_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
rel_print_pika_escape_iso8859_1_table_sp (orig_dirs_index_fd, arch_escape_classes, orig_dirs_index_final);
rel_print_pika_escape_iso8859_1_table_sp (orig_files_index_fd, arch_escape_classes, orig_files_index_final);
rel_print_pika_escape_iso8859_1_table_sp (mod_dirs_index_fd, arch_escape_classes, mod_dirs_index_final);
rel_print_pika_escape_iso8859_1_table_sp (mod_files_index_fd, arch_escape_classes, mod_files_index_final);
rel_free_table (orig_dirs_index_final);
rel_free_table (orig_files_index_final);
rel_free_table (mod_dirs_index_final);
rel_free_table (mod_files_index_final);
safe_close (orig_dirs_index_fd);
safe_close (orig_files_index_fd);
safe_close (mod_dirs_index_fd);
safe_close (mod_files_index_fd);
rel_free_table (orig_newname_dir_names);
rel_free_table (orig_newname_file_names);
rel_free_table (mod_newname_dir_names);
rel_free_table (mod_newname_file_names);
if (report)
{
lim = rel_n_records (cs->renamed_dirs);
for (x = 0; x < lim; ++x)
{
rel_add_records (&report->renamed_dirs, rel_copy_record (cs->renamed_dirs[x]), 0);
if (report->callback)
{
t_uchar * escape_tmp0;
t_uchar * escape_tmp1;
escape_tmp0 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->renamed_dirs[x][0]);
escape_tmp1 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->renamed_dirs[x][1]);
invoke_report_callback (report, "/> %s\t%s\n", no_dot (escape_tmp0), no_dot (escape_tmp1));
lim_free (0, escape_tmp1);
lim_free (0, escape_tmp0);
}
}
lim = rel_n_records (cs->renamed_files);
for (x = 0; x < lim; ++x)
{
rel_add_records (&report->renamed_files, rel_copy_record (cs->renamed_files[x]), 0);
if (report->callback)
{
t_uchar * escape_tmp0;
t_uchar * escape_tmp1;
escape_tmp0 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->renamed_files[x][0]);
escape_tmp1 = pika_save_escape_iso8859_1 (0, 0, escape_classes,
report->renamed_files[x][1]);
invoke_report_callback (report, "=> %s\t%s\n", no_dot (escape_tmp0), no_dot (escape_tmp1));
lim_free (0, escape_tmp1);
lim_free (0, escape_tmp0);
}
}
}
}
safe_fchdir (here_fd);
safe_close (here_fd);
lim_free (0, dest);
lim_free (0, original_only_dir_metadata_file);
lim_free (0, modified_only_dir_metadata_file);
lim_free (0, removed_files_archive_dir);
lim_free (0, new_files_archive_dir);
lim_free (0, patches_dir);
lim_free (0, orig_files_index_file);
lim_free (0, orig_dirs_index_file);
lim_free (0, mod_files_index_file);
lim_free (0, mod_dirs_index_file);
rel_free_table (orig_files_index_names_output);
rel_free_table (orig_dirs_index_names_output);
rel_free_table (mod_files_index_names_output);
rel_free_table (mod_dirs_index_names_output);
}
/* both orig and dest_spec must point to arch trees,
*/
void
arch_make_changeset (struct arch_make_changeset_report * report,
arch_project_tree_t * orig_tree, arch_project_tree_t * mod_tree,
t_uchar * dest_spec,
enum arch_id_tagging_method method,
enum arch_inventory_category untagged_source_category,
rel_table limits_spec,
assoc_table inode_sig_shortcuts_of_mod,
int link_same,
int escape_classes)
{
/* The changeset variables themselves */
struct changeset_vars cs = { 0 }; /* Initialise all pointers to NULL */
debug(dbg_diff, 6, "Detecting changeset...\n");
cs.orig_tree = orig_tree;
cs.mod_tree = mod_tree;
arch_detect_changeset (report, &cs, orig_tree, mod_tree, method, untagged_source_category, limits_spec, escape_classes);
debug(dbg_diff, 6, "Writing changeset...\n");
arch_write_changeset (report, &cs, dest_spec, inode_sig_shortcuts_of_mod, link_same, escape_classes);
arch_free_changeset_vars (&cs);
}
void
arch_make_empty_changeset (struct arch_make_changeset_report * make_report,
struct arch_changeset_report * report,
t_uchar * changeset_path)
{
t_uchar * new_files_archive = 0;
t_uchar * removed_files_archive = 0;
t_uchar * patches = 0;
t_uchar * original_only_dir_metadata = 0;
t_uchar * modified_only_dir_metadata = 0;
t_uchar * orig_files_index = 0;
t_uchar * orig_dirs_index = 0;
t_uchar * mod_files_index = 0;
t_uchar * mod_dirs_index = 0;
int fd;
new_files_archive = file_name_in_vicinity (0, changeset_path, "new-files-archive");
removed_files_archive = file_name_in_vicinity (0, changeset_path, "removed-files-archive");
patches = file_name_in_vicinity (0, changeset_path, "patches");
original_only_dir_metadata = file_name_in_vicinity (0, changeset_path, "original-only-dir-metadata");
modified_only_dir_metadata = file_name_in_vicinity (0, changeset_path, "modified-only-dir-metadata");
orig_files_index = file_name_in_vicinity (0, changeset_path, "orig-files-index");
orig_dirs_index = file_name_in_vicinity (0, changeset_path, "orig-dirs-index");
mod_files_index = file_name_in_vicinity (0, changeset_path, "mod-files-index");
mod_dirs_index = file_name_in_vicinity (0, changeset_path, "mod-dirs-index");
safe_mkdir (changeset_path, 0777);
safe_mkdir (patches, 0777);
safe_mkdir (new_files_archive, 0777);
safe_mkdir (removed_files_archive, 0777);
fd = safe_open (original_only_dir_metadata, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
fd = safe_open (modified_only_dir_metadata, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
fd = safe_open (orig_files_index, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
fd = safe_open (orig_dirs_index, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
fd = safe_open (mod_files_index, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
fd = safe_open (mod_dirs_index, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_close (fd);
assoc_set (&make_report->orig_dir_id_of, ".", "?_.");
assoc_set (&make_report->mod_dir_id_of, ".", "?_.");
assoc_set (&make_report->orig_dir_loc_of, "?_.", ".");
assoc_set (&make_report->mod_dir_loc_of, "?_.", ".");
lim_free (0, new_files_archive);
lim_free (0, removed_files_archive);
lim_free (0, patches);
lim_free (0, original_only_dir_metadata);
lim_free (0, modified_only_dir_metadata);
lim_free (0, orig_files_index);
lim_free (0, orig_dirs_index);
lim_free (0, mod_files_index);
lim_free (0, mod_dirs_index);
}
void
arch_changeset_rewrite_indexes (t_uchar * changeset_path, struct arch_changeset_report * report)
{
int ign;
t_uchar * orig_dirs_index_path = 0;
t_uchar * orig_files_index_path = 0;
t_uchar * mod_dirs_index_path = 0;
t_uchar * mod_files_index_path = 0;
int orig_dirs_index_fd = -1;
int orig_files_index_fd = -1;
int mod_dirs_index_fd = -1;
int mod_files_index_fd = -1;
orig_dirs_index_path = file_name_in_vicinity (0, changeset_path, "orig-dirs-index");
orig_files_index_path = file_name_in_vicinity (0, changeset_path, "orig-files-index");
mod_dirs_index_path = file_name_in_vicinity (0, changeset_path, "mod-dirs-index");
mod_files_index_path = file_name_in_vicinity (0, changeset_path, "mod-files-index");
vu_unlink (&ign, orig_dirs_index_path);
orig_dirs_index_fd = safe_open (orig_dirs_index_path, O_WRONLY | O_CREAT | O_EXCL, 0444);
vu_unlink (&ign, orig_files_index_path);
orig_files_index_fd = safe_open (orig_files_index_path, O_WRONLY | O_CREAT | O_EXCL, 0444);
vu_unlink (&ign, mod_dirs_index_path);
mod_dirs_index_fd = safe_open (mod_dirs_index_path, O_WRONLY | O_CREAT | O_EXCL, 0444);
vu_unlink (&ign, mod_files_index_path);
mod_files_index_fd = safe_open (mod_files_index_path, O_WRONLY | O_CREAT | O_EXCL, 0444);
rel_sort_table_by_field (0, report->orig_dirs_index, 1);
rel_sort_table_by_field (0, report->orig_files_index, 1);
rel_sort_table_by_field (0, report->mod_dirs_index, 1);
rel_sort_table_by_field (0, report->mod_files_index, 1);
rel_print_pika_escape_iso8859_1_table_sp (orig_dirs_index_fd, arch_escape_classes, report->orig_dirs_index);
rel_print_pika_escape_iso8859_1_table_sp (orig_files_index_fd, arch_escape_classes, report->orig_files_index);
rel_print_pika_escape_iso8859_1_table_sp (mod_dirs_index_fd, arch_escape_classes, report->mod_dirs_index);
rel_print_pika_escape_iso8859_1_table_sp (mod_files_index_fd, arch_escape_classes, report->mod_files_index);
safe_close (orig_dirs_index_fd);
safe_close (orig_files_index_fd);
safe_close (mod_dirs_index_fd);
safe_close (mod_files_index_fd);
lim_free (0, orig_dirs_index_path);
lim_free (0, orig_files_index_path);
lim_free (0, mod_dirs_index_path);
lim_free (0, mod_files_index_path);
}
int
arch_changeset_add_file (t_uchar ** path_ret,
struct arch_changeset_report * report,
struct arch_make_changeset_report * make_report,
t_uchar * changeset_path,
t_uchar * mod_loc,
t_uchar * id)
{
int ign;
t_uchar * new_file_archive_path = 0;
t_uchar * new_file_path = 0;
t_uchar * new_file_dir_path = 0;
t_uchar * loc_dir = 0;
t_uchar * loc_dir_id;
int dest_fd = -1;
new_file_archive_path = file_name_in_vicinity (0, changeset_path, "new-files-archive");
new_file_path = file_name_in_vicinity (0, new_file_archive_path, mod_loc);
if (path_ret)
*path_ret = str_save (0, new_file_path);
new_file_dir_path = file_name_directory_file (0, new_file_path);
ensure_directory_exists (new_file_dir_path);
vu_unlink (&ign, new_file_path);
dest_fd = safe_open (new_file_path, O_WRONLY | O_CREAT | O_EXCL, 0666);
/* update the changeset report.
*/
rel_add_records (&report->mod_files_index, rel_make_record (mod_loc, id, 0), 0);
rel_sort_table_by_field (0, report->mod_files_index, 0);
rel_uniq_by_field (&report->mod_files_index, 0);
rel_add_records (&report->added_files, rel_make_record (mod_loc, id, new_file_path, 0), 0);
rel_sort_table_by_field (0, report->added_files, 0);
rel_uniq_by_field (&report->added_files, 0);
/* update the make-changeset report.
*/
assoc_set (&make_report->mod_file_id_of, mod_loc, id);
assoc_set (&make_report->mod_file_loc_of, id, mod_loc);
rel_add_records (&make_report->mod_index.files, rel_make_record (mod_loc, id, 0), 0);
rel_sort_table_by_field (0, make_report->mod_index.files, 1);
rel_uniq_by_field (&make_report->mod_index.files, 1);
rel_add_records (&make_report->added_files, rel_make_record (mod_loc, 0), 0);
rel_sort_table_by_field (0, make_report->added_files, 0);
rel_uniq_by_field (&make_report->added_files, 0);
loc_dir = file_name_directory_file (0, mod_loc);
loc_dir_id = assoc_ref (make_report->mod_dir_id_of, loc_dir);
if (loc_dir_id)
assoc_set (&make_report->mod_container_dir_id_of_file_id, id, loc_dir_id);
lim_free (0, new_file_archive_path);
lim_free (0, new_file_path);
lim_free (0, new_file_dir_path);
lim_free (0, loc_dir);
return dest_fd;
}
int
arch_changeset_add_diffs (struct arch_changeset_report * report,
struct arch_make_changeset_report * make_report,
t_uchar * changeset_path,
t_uchar * orig_loc,
t_uchar * mod_loc,
t_uchar * id)
{
int ign;
t_uchar * patches_path = 0;
t_uchar * patch_path = 0;
t_uchar * patch_dir_path = 0;
t_uchar * orig_loc_dir = 0;
t_uchar * orig_loc_dir_id;
t_uchar * mod_loc_dir = 0;
t_uchar * mod_loc_dir_id;
int dest_fd = -1;
patches_path = file_name_in_vicinity (0, changeset_path, "patches");
patch_path = file_name_in_vicinity (0, patches_path, mod_loc);
patch_path = str_realloc_cat (0, patch_path, ".patch");
patch_dir_path = file_name_directory_file (0, patch_path);
ensure_directory_exists (patch_dir_path);
vu_unlink (&ign, patch_path);
dest_fd = safe_open (patch_path, O_WRONLY | O_CREAT | O_EXCL, 0666);
/* update the changeset report.
*/
if (report)
{
rel_add_records (&report->mod_files_index, rel_make_record (mod_loc, id, 0), 0);
rel_sort_table_by_field (0, report->mod_files_index, 0);
rel_uniq_by_field (&report->mod_files_index, 0);
rel_add_records (&report->orig_files_index, rel_make_record (orig_loc, id, 0), 0);
rel_sort_table_by_field (0, report->orig_files_index, 0);
rel_uniq_by_field (&report->orig_files_index, 0);
rel_add_records (&report->patched_regular_files, rel_make_record (mod_loc, id, patch_path, 0), 0);
rel_sort_table_by_field (0, report->patched_regular_files, 0);
rel_uniq_by_field (&report->patched_regular_files, 0);
}
/* update the make-changeset report.
*/
assoc_set (&make_report->mod_file_id_of, mod_loc, id);
assoc_set (&make_report->mod_file_loc_of, id, mod_loc);
assoc_set (&make_report->orig_file_id_of, orig_loc, id);
assoc_set (&make_report->orig_file_loc_of, id, orig_loc);
rel_add_records (&make_report->mod_index.files, rel_make_record (mod_loc, id, 0), 0);
rel_sort_table_by_field (0, make_report->mod_index.files, 1);
rel_uniq_by_field (&make_report->mod_index.files, 1);
rel_add_records (&make_report->orig_index.files, rel_make_record (orig_loc, id, 0), 0);
rel_sort_table_by_field (0, make_report->orig_index.files, 1);
rel_uniq_by_field (&make_report->orig_index.files, 1);
rel_add_records (&make_report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
rel_sort_table_by_field (0, make_report->modified_files, 0);
rel_uniq_by_field (&make_report->modified_files, 0);
mod_loc_dir = file_name_directory_file (0, mod_loc);
mod_loc_dir_id = assoc_ref (make_report->mod_dir_id_of, mod_loc_dir);
if (mod_loc_dir_id)
assoc_set (&make_report->mod_container_dir_id_of_file_id, id, mod_loc_dir_id);
orig_loc_dir = file_name_directory_file (0, orig_loc);
orig_loc_dir_id = assoc_ref (make_report->orig_dir_id_of, orig_loc_dir);
if (orig_loc_dir_id)
assoc_set (&make_report->orig_container_dir_id_of_file_id, id, orig_loc_dir_id);
lim_free (0, patches_path);
lim_free (0, patch_path);
lim_free (0, patch_dir_path);
lim_free (0, orig_loc_dir);
lim_free (0, mod_loc_dir);
return dest_fd;
}
void
arch_make_changeset_compute_container_map (assoc_table * out, assoc_table dir_id_of, rel_table index)
{
int lim;
int x;
lim = rel_n_records (index);
for (x = 0; x < lim; ++x)
{
t_uchar * item;
t_uchar * id;
t_uchar * dir_as_file;
t_uchar * container_id;
item = index[x][0];
id = index[x][1];
dir_as_file = file_name_directory_file (0, item);
container_id = assoc_ref (dir_id_of, dir_as_file);
invariant (!!container_id);
assoc_set (out, id, container_id);
lim_free (0, dir_as_file);
}
}
static void
compute_renames (rel_table * out,
assoc_table loc_of_orig_X_id,
assoc_table mod_dir_id_of,
assoc_table orig_container_dir_id_of_X_id,
rel_table X_index)
{
int lim;
int x;
lim = rel_n_records (X_index);
for (x = 0; x < lim; ++x)
{
t_uchar * container;
t_uchar * tail;
t_uchar * id;
t_uchar * orig;
t_uchar * orig_tail;
t_uchar * mod_container_id;
t_uchar * orig_container_id;
container = file_name_directory_file (0, X_index[x][0]);
tail = file_name_tail (0, X_index[x][0]);
id = X_index[x][1];
orig = assoc_ref (loc_of_orig_X_id, id);
orig_tail = (orig ? file_name_tail (0, orig) : 0);
mod_container_id = assoc_ref (mod_dir_id_of, container);
orig_container_id = assoc_ref (orig_container_dir_id_of_X_id, id);
if (orig && (str_cmp (orig_tail, tail)
|| (!mod_container_id && str_cmp (X_index[x][0], orig))
|| str_cmp (mod_container_id, orig_container_id)))
{
rel_add_records (out,
rel_make_record (orig, X_index[x][0], 0),
0);
}
lim_free (0, container);
lim_free (0, tail);
lim_free (0, orig_tail);
}
rel_sort_table_by_field (0, *out, 1);
}
static void
write_dir_metadata (t_uchar * file, t_uchar * tree_root, rel_table dirs)
{
int out_fd;
int lim;
int x;
out_fd = safe_open (file, O_WRONLY | O_CREAT | O_EXCL, 0666);
lim = rel_n_records (dirs);
for (x = 0; x < lim; ++x)
{
t_uchar * path;
t_uchar * escape_tmp;
struct stat stat_buf;
path = file_name_in_vicinity (0, tree_root, dirs[x][0]);
safe_stat (path, &stat_buf);
escape_tmp = pika_save_escape_iso8859_1 (0, 0, arch_escape_classes, dirs[x][0]);
safe_printfmt (out_fd, "--permissions %lo\t%s\n",
(unsigned long)(stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)),
escape_tmp);
lim_free (0, escape_tmp);
lim_free (0, path);
}
safe_close (out_fd);
}
static void
copy_files_and_symlinks (t_uchar * dest, t_uchar * tree_root, rel_table loc_list)
{
int lim;
int x;
lim = rel_n_records (loc_list);
for (x = 0; x < lim; ++x)
{
t_uchar * loc;
t_uchar * source_name;
t_uchar * dest_name;
t_uchar * dest_dir;
loc = loc_list[x][0];
source_name = file_name_in_vicinity (0, tree_root, loc);
dest_name = file_name_in_vicinity (0, dest, loc);
dest_dir = file_name_directory_file (0, dest_name);
ensure_directory_exists (dest_dir);
copy_file_or_symlink (source_name, dest_name);
copy_permissions (source_name, dest_name);
lim_free (0, source_name);
lim_free (0, dest_name);
lim_free (0, dest_dir);
}
}
static void
emit_dir_patches (rel_table * orig_index_names_output,
rel_table * mod_index_names_output,
struct arch_make_changeset_report * report,
t_uchar * patches,
t_uchar * orig,
t_uchar * orig_loc,
t_uchar * mod,
t_uchar * mod_loc,
int escape_classes)
{
t_uchar * orig_path;
t_uchar * mod_path;
struct stat orig_stat;
struct stat mod_stat;
t_ulong orig_mode;
t_ulong mod_mode;
orig_path = file_name_in_vicinity (0, orig, orig_loc);
mod_path = file_name_in_vicinity (0, mod, mod_loc);
safe_stat (orig_path, &orig_stat);
safe_stat (mod_path, &mod_stat);
orig_mode = (t_ulong)(orig_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
mod_mode = (t_ulong)(mod_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
if (orig_mode != mod_mode)
{
t_uchar * patches_dir;
t_uchar * orig_patch;
t_uchar * mod_patch;
int orig_patch_fd;
int mod_patch_fd;
rel_add_records (orig_index_names_output, rel_make_record (orig_loc, 0), 0);
rel_add_records (mod_index_names_output, rel_make_record (mod_loc, 0), 0);
if (report)
{
rel_add_records (&report->perms_changed_dirs, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "-/ %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
patches_dir = file_name_in_vicinity (0, patches, mod_loc);
orig_patch = file_name_in_vicinity (0, patches_dir, "=dir-meta-orig");
mod_patch = file_name_in_vicinity (0, patches_dir, "=dir-meta-mod");
ensure_directory_exists (patches_dir);
orig_patch_fd = safe_open (orig_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
mod_patch_fd = safe_open (mod_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_printfmt (orig_patch_fd, "--permissions %lo\n", orig_mode);
safe_printfmt (mod_patch_fd, "--permissions %lo\n", mod_mode);
safe_close (orig_patch_fd);
safe_close (mod_patch_fd);
lim_free (0, patches_dir);
lim_free (0, orig_patch);
lim_free (0, mod_patch);
}
lim_free (0, orig_path);
lim_free (0, mod_path);
}
void
emit_symlink_patch (changeset_maker * self,
t_uchar * orig_loc,
t_uchar * orig_target,
t_uchar * mod_loc,
t_uchar * mod_target)
{
t_uchar * patch_basename_path;
t_uchar * orig_patch;
t_uchar * mod_patch;
t_uchar * patch_dir;
int orig_out_fd;
int mod_out_fd;
note_change (self, orig_loc, mod_loc);
if (self->report)
{
rel_add_records (&self->report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (self->report->callback)
{
/* report the symlink itself renamed */
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, self->escape_classes,
mod_loc);
invoke_report_callback (self->report, "-> %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
patch_basename_path = file_name_in_vicinity (0, self->patches, mod_loc);
orig_patch = str_alloc_cat (0, patch_basename_path, ".link-orig");
mod_patch = str_alloc_cat (0, patch_basename_path, ".link-mod");
patch_dir = file_name_directory_file (0, patch_basename_path);
ensure_directory_exists (patch_dir);
orig_out_fd = safe_open (orig_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
mod_out_fd = safe_open (mod_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_printfmt (orig_out_fd, "%s\n", orig_target);
safe_printfmt (mod_out_fd, "%s\n", mod_target);
lim_free (0, patch_basename_path);
lim_free (0, orig_patch);
lim_free (0, mod_patch);
lim_free (0, patch_dir);
}
void
note_change (changeset_maker *self,
t_uchar * orig_loc,
t_uchar * mod_loc)
{
rel_add_records (self->orig_index_names_output, rel_make_record (orig_loc, 0), 0);
rel_add_records (self->mod_index_names_output, rel_make_record (mod_loc, 0), 0);
}
/**
* \brief Check for changes for a file
*
* \param orig_index_names_output FIXME docstring
* \param mod_index_names_output FIXME docstring
* \param report return a description of the changeset in this
* parameter
* \param patches the directory where patches will be stored
* \param orig root of the ORIG tree
* \param orig_loc path to the file in ORIG tree (relative from orig)
* \param mod root of the MOD tree
* \param mod_loc path to the file in MOD tree (relative from mod)
* \param id the id of the file considered
* \param inode_sig_shortcuts_of_mod inode signature table for mod.
* If non NULL, a file matching its inode signature will be
* considered unmodified. If NULL, the inode signature will not
* be checked.
* \param link_same Whether unmodified files should be hardlinked to
* their reference.
* \param escape_classes how file names should be escaped.
*/
void
emit_file_or_symlink_patches (rel_table * orig_index_names_output,
rel_table * mod_index_names_output,
struct arch_make_changeset_report * report,
t_uchar * patches,
t_uchar * orig,
t_uchar * orig_loc,
t_uchar * mod,
t_uchar * mod_loc,
t_uchar * id,
assoc_table inode_sig_shortcuts_of_mod,
int link_same,
int escape_classes)
{
t_uchar * orig_path;
t_uchar * mod_path;
int orig_is_symlink;
int mod_is_symlink;
changeset_maker self = {orig_index_names_output, mod_index_names_output, report, escape_classes, patches};
struct stat *orig_stat = &(cset_map_find (report->orig_index.entries, id)->stat_buf);
struct stat *mod_stat = &(cset_map_find (report->mod_index.entries, id)->stat_buf);
orig_path = file_name_in_vicinity (0, orig, orig_loc);
mod_path = file_name_in_vicinity (0, mod, mod_loc);
orig_is_symlink = S_ISLNK (orig_stat->st_mode);
mod_is_symlink = S_ISLNK (mod_stat->st_mode);
if (orig_is_symlink && mod_is_symlink)
{
t_uchar * orig_target = link_target (orig_path);
t_uchar * mod_target = link_target (mod_path);
if (arch_symlinks_differ (orig_path, mod_path))
emit_symlink_patch (&self, orig_loc, orig_target, mod_loc, mod_target);
lim_free (0, orig_target);
lim_free (0, mod_target);
}
else if (orig_is_symlink)
{
t_uchar * orig_target;
t_uchar * patch_basename_path;
t_uchar * orig_patch;
t_uchar * mod_patch;
t_uchar * patch_dir;
int orig_out_fd;
note_change (&self, orig_loc, mod_loc);
if (report)
{
rel_add_records (&report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "lf %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
orig_target = link_target (orig_path);
patch_basename_path = file_name_in_vicinity (0, patches, mod_loc);
orig_patch = str_alloc_cat (0, patch_basename_path, ".link-orig");
mod_patch = str_alloc_cat (0, patch_basename_path, ".modified");
patch_dir = file_name_directory_file (0, patch_basename_path);
ensure_directory_exists (patch_dir);
orig_out_fd = safe_open (orig_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_printfmt (orig_out_fd, "%s\n", orig_target);
copy_file (mod_path, mod_patch);
copy_permissions (mod_path, mod_patch);
lim_free (0, orig_target);
lim_free (0, patch_basename_path);
lim_free (0, orig_patch);
lim_free (0, mod_patch);
lim_free (0, patch_dir);
}
else if (mod_is_symlink)
{
t_uchar * mod_target;
t_uchar * patch_basename_path;
t_uchar * orig_patch;
t_uchar * mod_patch;
t_uchar * patch_dir;
int mod_out_fd;
note_change (&self, orig_loc, mod_loc);
if (report)
{
rel_add_records (&report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "fl %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
mod_target = link_target (mod_path);
patch_basename_path = file_name_in_vicinity (0, patches, mod_loc);
orig_patch = str_alloc_cat (0, patch_basename_path, ".original");
mod_patch = str_alloc_cat (0, patch_basename_path, ".link-mod");
patch_dir = file_name_directory_file (0, patch_basename_path);
ensure_directory_exists (patch_dir);
mod_out_fd = safe_open (mod_patch, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_printfmt (mod_out_fd, "%s\n", mod_target);
copy_file (orig_path, orig_patch);
copy_permissions (orig_path, orig_patch);
lim_free (0, mod_target);
lim_free (0, patch_basename_path);
lim_free (0, orig_patch);
lim_free (0, mod_patch);
lim_free (0, patch_dir);
}
else
{
int ign;
t_uchar * patch_base_file;
t_uchar * patch_file_dir;
mode_t orig_mode;
mode_t mod_mode;
t_uchar * diff_file;
int diff_fd;
int diff_stat;
int is_changed;
is_changed = 0;
if (arch_file_stats_differ (orig_stat, mod_stat) == 0)
goto path_free;
patch_base_file = file_name_in_vicinity (0, patches, mod_loc);
patch_file_dir = file_name_directory_file (0, patch_base_file);
orig_mode = orig_stat->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
mod_mode = mod_stat->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
if (orig_mode != mod_mode)
{
t_uchar * orig_patch_meta_file;
t_uchar * mod_patch_meta_file;
int orig_meta_fd;
int mod_meta_fd;
note_change (&self, orig_loc, mod_loc);
if (report)
{
rel_add_records (&report->perms_changed_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "-- %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
orig_patch_meta_file = str_alloc_cat (0, patch_base_file, ".meta-orig");
mod_patch_meta_file = str_alloc_cat (0, patch_base_file, ".meta-mod");
ensure_directory_exists (patch_file_dir);
orig_meta_fd = safe_open (orig_patch_meta_file, O_WRONLY | O_EXCL | O_CREAT, 0666);
mod_meta_fd = safe_open (mod_patch_meta_file, O_WRONLY | O_EXCL | O_CREAT, 0666);
safe_printfmt (orig_meta_fd, "--permissions %lo\n", (t_ulong)orig_mode);
safe_printfmt (mod_meta_fd, "--permissions %lo\n", (t_ulong)mod_mode);
safe_close (orig_meta_fd);
safe_close (mod_meta_fd);
lim_free (0, orig_patch_meta_file);
lim_free (0, mod_patch_meta_file);
is_changed = 1;
}
/* FIXME: performance tuning:
* arch_filename_contents_differ involves reading the entire files,
* more efficient to just spawn patch and use the result.
*/
if ((arch_inode_sigs_differ (mod_stat, id, inode_sig_shortcuts_of_mod) == 0)
|| (arch_filename_contents_differ (orig_path, mod_path) == 0))
{
if (orig_stat->st_dev == mod_stat->st_dev && link_same)
link_force (orig_path, mod_path);
goto file_path_free;
}
ensure_directory_exists (patch_file_dir);
diff_file = str_alloc_cat (0, patch_base_file, ".patch");
vu_unlink (&ign, diff_file);
diff_fd = safe_open (diff_file, O_RDWR | O_EXCL | O_CREAT, 0666);
diff_stat = arch_really_invoke_diff (diff_fd, orig_path, orig_loc, mod_path, mod_loc, NULL);
invariant (diff_stat!=0);
if (diff_stat == 1)
{
t_uchar diff_pseudo_magic[sizeof ("binary files ")];
long amt;
/* diff might have just said "binary files differ" or something
* similar.
*/
safe_lseek (diff_fd, (off_t)0, SEEK_SET);
amt = safe_read (diff_fd, diff_pseudo_magic, sizeof (diff_pseudo_magic) - 1);
if (amt > 0)
{
diff_pseudo_magic[amt] = 0;
if (!str_casecmp (diff_pseudo_magic, "binary files "))
diff_stat = 2;
}
}
if (diff_stat == 2)
safe_unlink (diff_file);
/* diff_stat: 0 == same, 1 == patch in diff_fd, 2 == differing binary files
*/
switch (diff_stat)
{
case 1:
{
note_change (&self, orig_loc, mod_loc);
if (report)
{
rel_add_records (&report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "M %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
is_changed = 1;
lim_free (0, diff_file);
break;
}
case 2:
{
t_uchar * orig_copy;
t_uchar * mod_copy;
note_change (&self, orig_loc, mod_loc);
if (report)
{
rel_add_records (&report->modified_files, rel_make_record (orig_loc, mod_loc, 0), 0);
if (report->callback)
{
t_uchar * escape_tmp;
escape_tmp = pika_save_escape_iso8859_1 (0, 0, escape_classes,
mod_loc);
invoke_report_callback (report, "Mb %s\n", no_dot (escape_tmp));
lim_free (0, escape_tmp);
}
}
orig_copy = str_alloc_cat (0, patch_base_file, ".original");
mod_copy = str_alloc_cat (0, patch_base_file, ".modified");
copy_file (orig_path, orig_copy);
copy_permissions (orig_path, orig_copy);
copy_file (mod_path, mod_copy);
copy_permissions (mod_path, mod_copy);
lim_free (0, orig_copy);
lim_free (0, mod_copy);
is_changed = 1;
break;
}
default:
{
break;
}
}
#if 0
if (is_changed)
{
*(int *)ar_push ((void **)&map.changed_files[0], 0, sizeof (int)) = map.common_files[0][x];
*(int *)ar_push ((void **)&map.changed_files[1], 0, sizeof (int)) = map.common_files[1][x];
}
#endif
safe_close (diff_fd);
file_path_free:
lim_free (0, patch_base_file);
lim_free (0, patch_file_dir);
}
path_free:
lim_free (0, orig_path);
lim_free (0, mod_path);
}
static void
compute_parent_dir_closure (rel_table * orig_newname_dirs, rel_table * mod_newname_dirs,
rel_table orig_newname_files, rel_table mod_newname_files,
assoc_table orig_dir_loc_of, assoc_table orig_dir_id_of,
assoc_table orig_container_dir_id_of_dir_id, assoc_table orig_container_dir_id_of_file_id,
assoc_table mod_dir_loc_of, assoc_table mod_dir_id_of,
assoc_table mod_container_dir_id_of_dir_id, assoc_table mod_container_dir_id_of_file_id,
assoc_table orig_file_id_of, assoc_table mod_file_id_of)
{
assoc_table orig_has_dir_id = 0;
assoc_table mod_has_dir_id = 0;
rel_table added_orig_container_ids = 0; /* just the deepest new id */
rel_table added_mod_container_ids = 0; /* just the deepest new id */
int lim;
int x;
/* Note what dir ids are already in the
* final indexes.
*/
lim = rel_n_records (*orig_newname_dirs);
for (x = 0; x < lim; ++x)
{
t_uchar * loc;
t_uchar * id;
loc = (*orig_newname_dirs)[x][0];
id = assoc_ref (orig_dir_id_of, loc);
assoc_set (&orig_has_dir_id, id, loc);
}
lim = rel_n_records (*mod_newname_dirs);
for (x = 0; x < lim; ++x)
{
t_uchar * loc;
t_uchar * id;
loc = (*mod_newname_dirs)[x][0];
id = assoc_ref (mod_dir_id_of, loc);
assoc_set (&mod_has_dir_id, id, loc);
}
/* For each newname dir in one tree,
* find the deepest container in that
* tree that is present in each tree
* but not in that tree's index,
* and slate it for addition to the index.
*/
schedule_missing_containers (&added_orig_container_ids, &orig_has_dir_id,
&added_mod_container_ids, &mod_has_dir_id,
*orig_newname_dirs, orig_dir_id_of,
orig_container_dir_id_of_dir_id, orig_container_dir_id_of_dir_id,
orig_dir_loc_of, mod_dir_loc_of);
schedule_missing_containers (&added_orig_container_ids, &orig_has_dir_id,
&added_mod_container_ids, &mod_has_dir_id,
orig_newname_files, orig_dir_id_of,
orig_container_dir_id_of_file_id, orig_container_dir_id_of_dir_id,
orig_dir_loc_of, mod_dir_loc_of);
schedule_missing_containers (&added_orig_container_ids, &orig_has_dir_id,
&added_mod_container_ids, &mod_has_dir_id,
*mod_newname_dirs, mod_dir_id_of,
mod_container_dir_id_of_dir_id, mod_container_dir_id_of_dir_id,
orig_dir_loc_of, mod_dir_loc_of);
schedule_missing_containers (&added_orig_container_ids, &orig_has_dir_id,
&added_mod_container_ids, &mod_has_dir_id,
mod_newname_files, mod_dir_id_of,
mod_container_dir_id_of_file_id, mod_container_dir_id_of_dir_id,
orig_dir_loc_of, mod_dir_loc_of);
/* Add the closure of container paths of added containers
* to the output lists.
*/
lim = rel_n_records (added_orig_container_ids);
for (x = 0; x < lim; ++x)
{
container_closure (orig_newname_dirs,
&orig_has_dir_id,
added_orig_container_ids[x][0],
orig_dir_loc_of,
orig_container_dir_id_of_dir_id);
}
lim = rel_n_records (added_mod_container_ids);
for (x = 0; x < lim; ++x)
{
container_closure (mod_newname_dirs,
&mod_has_dir_id,
added_mod_container_ids[x][0],
mod_dir_loc_of,
mod_container_dir_id_of_dir_id);
}
free_assoc_table (orig_has_dir_id);
free_assoc_table (mod_has_dir_id);
rel_free_table (added_orig_container_ids);
rel_free_table (added_mod_container_ids);
}
static void
schedule_missing_containers (rel_table * added_orig_container_ids, assoc_table * orig_has_dir_id,
rel_table * added_mod_container_ids, assoc_table * mod_has_dir_id,
rel_table locs_in, assoc_table id_of_dir_loc,
assoc_table first_container_id_of_id, assoc_table container_id_of_dir_id,
assoc_table orig_dir_loc_of, assoc_table mod_dir_loc_of)
{
int lim;
int x;
lim = rel_n_records (locs_in);
for (x = 0; x < lim; ++x)
{
t_uchar * loc = 0;
t_uchar * id;
t_uchar * container_id;
int added_orig;
int added_mod;
loc = str_save (0, locs_in[x][0]);
id = assoc_ref (id_of_dir_loc, loc);
container_id = assoc_ref (first_container_id_of_id, id);
added_orig = 0;
added_mod = 0;
while ((!added_orig || !added_mod) && container_id && str_cmp (id, "?_."))
{
t_uchar * t = 0;
if (!added_orig && !assoc_ref (*orig_has_dir_id, container_id) && assoc_ref (orig_dir_loc_of, container_id))
{
rel_add_records (added_orig_container_ids, rel_make_record (container_id, 0), 0);
/* assoc_set (orig_has_dir_id, container_id, assoc_ref (orig_dir_loc_of, container_id)); */
added_orig = 1;
}
if (!added_mod && !assoc_ref (*mod_has_dir_id, container_id) && assoc_ref (mod_dir_loc_of, container_id))
{
rel_add_records (added_mod_container_ids, rel_make_record (container_id, 0), 0);
/* assoc_set (mod_has_dir_id, container_id, assoc_ref (mod_dir_loc_of, container_id)); */
added_mod = 1;
}
t = loc;
loc = file_name_directory_file (0, loc);
id = assoc_ref (id_of_dir_loc, loc);
container_id = assoc_ref (container_id_of_dir_id, id);
lim_free (0, t);
}
lim_free (0, loc);
}
}
static void
container_closure (rel_table * locs_out,
assoc_table * has_ids,
t_uchar * deep_id,
assoc_table dir_loc_of,
assoc_table container_dir_id_of_dir_id)
{
while (deep_id && str_cmp (deep_id, "?_."))
{
if (!assoc_ref (*has_ids, deep_id))
{
t_uchar * deep_loc;
deep_loc = assoc_ref (dir_loc_of, deep_id);
rel_add_records (locs_out, rel_make_record (deep_loc, 0), 0);
assoc_set (has_ids, deep_id, deep_loc);
}
deep_id = assoc_ref (container_dir_id_of_dir_id, deep_id);
}
}
static void
link_force (t_uchar *old_path, t_uchar *new_path)
{
t_uchar *dir;
t_uchar *tmp_nam;
dir = file_name_directory (0, new_path);
tmp_nam = file_name_in_vicinity (0, dir, ",,tmp");
safe_link (old_path, tmp_nam);
safe_rename (tmp_nam, new_path);
lim_free (0, dir);
lim_free (0, tmp_nam);
}
/* tag: Tom Lord Wed May 14 18:53:05 2003 (make-changeset.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1