/* import.c:
*
****************************************************************
* Copyright (C) 2003 Tom Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/bugs/exception.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/time.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "libdate/date-string.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/string-files.h"
#include "libfsutils/copy-file.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/rmrf.h"
#include "libarch/ancestry.h"
#include "libarch/patch-logs.h"
#include "libarch/invent.h"
#include "libarch/my.h"
#include "libarch/hooks.h"
#include "libarch/namespace.h"
#include "libarch/pristines.h"
#include "libarch/project-tree.h"
#include "libarch/changelogs.h"
#include "libarch/local-cache.h"
#include "libarch/import.h"
/* __STDC__ prototypes for static functions */
static t_uchar * arch_prepare_import_pristine (t_uchar ** changelog_loc_ret,
t_uchar ** cooked_log_ret,
arch_project_tree_t * tree,
t_uchar * raw_log,
t_uchar * archive,
t_uchar * version);
static void arch_finish_import (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine, t_uchar * changelog_loc);
static void arch_import_failed (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine);
static rel_table all_patches (t_uchar * tree_root);
void
arch_import (struct arch_archive * arch,
t_uchar * version,
arch_project_tree_t * tree,
t_uchar * raw_log)
{
t_uchar * changelog_loc = 0;
t_uchar * errstr;
t_uchar * pristine_path = 0;
t_uchar * cooked_log = 0;
t_uchar * my_uid = 0;
t_uchar * txn_id = 0;
t_uchar * revision;
revision = str_alloc_cat (0, version, "--base-0");
pristine_path = arch_prepare_import_pristine (&changelog_loc, &cooked_log, tree, raw_log, arch->official_name, version);
my_uid = arch_my_id_uid ();
txn_id = arch_generate_txn_id ();
if (arch_revision_exists (arch, revision))
{
safe_printfmt (2, "arch_import: the revision already exists\n revision: %s/%s\n", arch->official_name, revision);
exit (2);
}
if (arch_archive_lock_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
{
safe_printfmt (2, "arch_import: unable to acquire revision lock (%s)\n tree: %s\n revision: %s/%s--base-0\n url: %s\n",
errstr, tree->root, arch->official_name, version, arch->location);
exit (2);
}
if (arch_archive_put_log (&errstr, arch, version, 0, my_uid, txn_id, cooked_log))
{
safe_printfmt (2, "arch_import: unable to send log message to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n",
errstr, tree->root, arch->official_name, version, arch->location);
exit (2);
}
if (arch_archive_put_import (&errstr, arch, version, 0, my_uid, txn_id, "base-0", pristine_path))
{
safe_printfmt (2, "arch_import: unable to send import tree to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n",
errstr, tree->root, arch->official_name, version, arch->location);
exit (2);
}
if (arch_revision_ready (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
{
safe_printfmt (2, "arch_import: error sending tree to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n",
errstr, tree->root, arch->official_name, version, arch->location);
exit (2);
}
arch_start_tree_commit (tree, cooked_log);
if (arch_archive_finish_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0"))
{
arch_import_failed (tree, arch->official_name, version, pristine_path);
safe_printfmt (2, "arch_import: unable to complete import transaction (%s)\n tree: %s\n revision: %s/%s\n url: %s\n",
errstr, tree->root, arch->official_name, version, arch->location);
exit (2);
}
{
arch_patch_id * patch_id = arch_patch_id_new_archive (arch->official_name, revision);
/* FIXME upload limited history and update a current dir in the archive */
ancestry_upload_patch (arch, patch_id, -1);
talloc_free (patch_id);
}
arch_finish_import (tree, arch->official_name, version, pristine_path, changelog_loc);
arch_run_hook ("import", "ARCH_ARCHIVE", arch->official_name, "ARCH_REVISION", revision, "ARCH_TREE_ROOT", tree->root, 0);
lim_free (0, revision);
lim_free (0, changelog_loc);
lim_free (0, pristine_path);
lim_free (0, cooked_log);
lim_free (0, my_uid);
lim_free (0, txn_id);
}
static t_uchar *
arch_prepare_import_pristine (t_uchar ** changelog_loc_ret,
t_uchar ** cooked_log_ret,
arch_project_tree_t * tree,
t_uchar * raw_log,
t_uchar * archive,
t_uchar * version)
{
t_uchar * tmp_stem = 0;
t_uchar * tmp_path = 0;
t_uchar * revision = 0;
t_uchar * pristine_path = 0;
rel_table inventory = 0;
rel_table new_files_list = 0;
t_uchar * log_message = 0;
t_uchar * pristine_log_path = 0;
arch_project_tree_t * pristine;
int pristine_log_fd;
/****************************************************************
* double check that we were handed a valid log message, if any
*/
if (raw_log)
invariant (arch_valid_log_file (raw_log));
/****************************************************************
* make a temp dir for the pristine copy of the import
*/
tmp_stem = str_alloc_cat_many (0, ",,import.", version, "--base-0--", archive, str_end);
tmp_path = talloc_tmp_file_name (talloc_context, tree->root, tmp_stem);
rmrf_file (tmp_path);
safe_mkdir (tmp_path, 0777);
revision = str_alloc_cat (0, version, "--base-0");
pristine_path = file_name_in_vicinity (0, tmp_path, revision);
inventory = arch_source_inventory (tree, 1, 0, 0);
safe_mkdir (pristine_path, 0777);
copy_file_list (pristine_path, tree->root, inventory);
pristine = arch_project_tree_new (tmp_path, pristine_path);
/****************************************************************
* As long as the cache is hot with directories and inodes, compute
* a list of files for the New-files: header in the log message.
* FIXME: this is wasteful, post process the inventory instead.
*/
{
rel_table temp_list = arch_source_files_inventory (tree, 1, 0);
new_files_list = pick_non_control (temp_list);
rel_free_table (temp_list);
}
/****************************************************************
* Create a log message for the newly committed base-0.
*
* Although the message is empty at this point, it's important for
* it to appear in directory listings of the patch-log, so that it
* shows up in the New-patches: header.
*/
pristine_log_path = arch_log_file (pristine_path, archive, revision);
pristine_log_fd = safe_open (pristine_log_path, O_WRONLY | O_CREAT | O_EXCL, 0666);
/****************************************************************
* Generate the actual log message, adding automatically generated
* headers either to the supplied raw-log or to a default raw-log.
*/
{
t_uchar * my_id = 0;
time_t now;
t_uchar * std_date = 0;
t_uchar * human_date = 0;
int log_fd;
rel_table patches = 0;
t_uchar * eoh = NULL; /* gcc doesn't see the link from raw_log to eoh */
my_id = arch_my_id ();
now = time(0);
std_date = standard_date (now);
human_date = pretty_date (now);
log_fd = make_output_to_string_fd ();
safe_printfmt (log_fd, "Revision: %s--base-0\n", version);
safe_printfmt (log_fd, "Archive: %s\n", archive);
safe_printfmt (log_fd, "Creator: %s\n", my_id);
safe_printfmt (log_fd, "Date: %s\n", human_date);
safe_printfmt (log_fd, "Standard-date: %s\n", std_date);
/********************************
* Copy headers from the raw log
*/
if (raw_log)
{
eoh = raw_log;
while (1)
{
eoh = str_chr_index (eoh, '\n');
if (!eoh || (eoh[1] == '\n') || (!eoh[1]))
break;
++eoh;
}
if (eoh)
{
eoh = eoh + 1;
safe_printfmt (log_fd, "%.*s", (int)(eoh - raw_log), raw_log);
}
}
else
{
safe_printfmt (log_fd, "Summary: initial import\n");
}
/********************************
* automatic headers for New-files: and New-patches:
*/
arch_print_log_list_header (log_fd, "New-files", new_files_list, 0);
patches = all_patches (pristine_path);
arch_print_log_list_header (log_fd, "New-patches", patches, 0);
/********************************
* copy or generate the log body
*/
if (!raw_log)
safe_printfmt (log_fd, "\n\n(automatically generated log message)\n");
else
{
if (*eoh)
{
safe_printfmt (log_fd, "%s", eoh);
}
else
{
safe_printfmt (log_fd, "\n\n");
}
}
/********************************
* oh... did i mention we were writing
* the log to a string?
*/
log_message = string_fd_close (log_fd);
lim_free (0, my_id);
lim_free (0, std_date);
lim_free (0, human_date);
rel_free_table (patches);
}
/****************************************************************
* Write the log into the patch-log of the pristine tree.
*/
safe_printfmt (pristine_log_fd, "%s", log_message);
safe_close (pristine_log_fd);
/****************************************************************
* If the import tree has a ChangeLog for this version,
* update it in the pristine tree.
*/
{
int x;
t_uchar * changelog_id_suffix = 0;
t_uchar * changelog_x_id = 0;
t_uchar * changelog_i_id = 0;
changelog_id_suffix = str_alloc_cat_many (0, "_automatic-ChangeLog--", archive, "/", version, str_end);
changelog_x_id = str_alloc_cat (0, "x", changelog_id_suffix);
changelog_i_id = str_alloc_cat (0, "i", changelog_id_suffix);
for (x = 0; x < rel_n_records (inventory); ++x)
{
if (!str_cmp (changelog_x_id, inventory[x][1]) || !str_cmp (changelog_i_id, inventory[x][1]))
{
struct stat clstatb;
t_uchar * changelog_path = 0;
mode_t clmode;
int out_fd = -1;
if (changelog_loc_ret)
*changelog_loc_ret = str_save (0, inventory[x][0]);
changelog_path = file_name_in_vicinity (0, pristine_path, inventory[x][0]);
safe_stat (changelog_path, &clstatb);
clmode = clstatb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
out_fd = safe_open (changelog_path, O_WRONLY | O_CREAT | O_TRUNC, clmode);
safe_fchmod (out_fd, clmode);
arch_generate_changelog (out_fd, pristine, 0, 0, 0, 0, archive, version);
safe_close (out_fd);
lim_free (0, changelog_path);
break;
}
}
lim_free (0, changelog_id_suffix);
lim_free (0, changelog_x_id);
lim_free (0, changelog_i_id);
}
/****************************************************************
* Give a copy of the log to the caller.
*/
if (cooked_log_ret)
*cooked_log_ret = str_save (0, log_message);
lim_free (0, tmp_stem);
talloc_free (tmp_path);
lim_free (0, revision);
rel_free_table (inventory);
rel_free_table (new_files_list);
lim_free (0, log_message);
lim_free (0, pristine_log_path);
/****************************************************************
* Give the user the path to the pristine tree for base-0.
* It's up tot he caller to stash this in the archive.
*/
return pristine_path;
}
static void
arch_finish_import (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine, t_uchar * changelog_loc)
{
t_uchar * revision = 0;
t_uchar * pristine_dir = 0;
t_uchar * pristine_dir_tail = 0;
arch_patch_id * patch_id;
revision = str_alloc_cat (0, version, "--base-0");
patch_id = arch_patch_id_new_archive (archive, revision);
/****************************************************************
* txnally install the log file in the patch log
*/
arch_finish_tree_commit (tree, archive, revision, changelog_loc);
/****************************************************************
* Install the pristine tree unless we have a greedy library
* that is eager to slurp it up.
*
* If we don't snarf the library here, it'll be deleted
* during "clean up", below.
*/
if (!arch_greedy_library_wants_revision (patch_id))
arch_install_pristine (tree, archive, revision, pristine);
/****************************************************************
* clean up
*/
pristine_dir = file_name_directory_file (0, pristine);
invariant (!!pristine_dir);
pristine_dir_tail = file_name_tail (0, pristine_dir);
invariant (!str_cmp_prefix (",,import.", pristine_dir_tail));
rmrf_file (pristine_dir);
lim_free (0, revision);
lim_free (0, pristine_dir);
lim_free (0, pristine_dir_tail);
talloc_free (patch_id);
}
static void
arch_import_failed (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine)
{
t_uchar * revision = 0;
t_uchar * pristine_dir = 0;
t_uchar * pristine_dir_tail = 0;
revision = str_alloc_cat (0, version, "--base-0");
/****************************************************************
* get out of mid-commit state in the project tree
*/
arch_abort_tree_commit (tree, archive, revision);
/****************************************************************
* clean up
*/
pristine_dir = file_name_directory_file (0, pristine);
invariant (!!pristine_dir);
pristine_dir_tail = file_name_tail (0, pristine_dir);
invariant (str_cmp_prefix (",,import.", pristine_dir_tail));
rmrf_file (pristine_dir);
lim_free (0, revision);
lim_free (0, pristine_dir);
lim_free (0, pristine_dir_tail);
}
static rel_table
all_patches (t_uchar * tree_root)
{
rel_table log_versions = 0;
rel_table answer = 0;
int x;
log_versions = arch_log_versions (tree_root, 0, 0, 0, 0);
for (x = 0; x < rel_n_records (log_versions); ++x)
{
t_uchar * archive = 0;
t_uchar * version = 0;
rel_table patch_list = 0;
archive = arch_parse_package_name (arch_ret_archive, 0, log_versions[x][0]);
version = arch_parse_package_name (arch_ret_non_archive, 0, log_versions[x][0]);
patch_list = arch_logs (tree_root, archive, version, 1);
rel_append_x (&answer, patch_list);
lim_free (0, archive);
lim_free (0, version);
rel_free_table (patch_list);
}
rel_free_table (log_versions);
return answer;
}
/* tag: Tom Lord Sat May 24 22:40:45 2003 (import.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1