/* library-txn.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/char/str.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/link-tree.h"
#include "libfsutils/ensure-dir.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/copy-file.h"
#include "libfsutils/same.h"
#include "libarch/arch.h"
#include "libarch/chatter.h"
#include "libarch/namespace.h"
#include "libarch/patch-logs.h"
#include "libarch/build-revision.h"
#include "libarch/invent.h"
#include "libarch/libraries.h"
#include "libarch/project-tree.h"
#include "libarch/apply-changeset.h"
#include "libarch/inode-sig.h"
#include "libarch/replay.h"
#include "libarch/my.h"
#include "libarch/library-txn.h"
/* __STDC__ prototypes for static functions */
static void library_txn_callback (void * vfp, char * fmt, va_list ap);
/*(c arch_library_add)
* t_uchar * arch_library_add (int chatter_fd,
* struct arch_archive * arch,
* t_uchar * revision,
* t_uchar * opt_library_spec,
* t_uchar * opt_same_dev_path)
*
* Return the path to the _library_ where REVISION from ARCH
* should be added by library-add.
*
* OPT_LIBRARY_SPEC is the (optional) path to a library to which
* the user explicitly requested addition. If not 0, we essentially
* validate that parameter.
*
* OPT_SAME_DEV_PATH is the (optional) path to a file or directory
* on the device to which we want to library-add. This is for
* things like --link: we need to make sure that the revision
* is added to a library on the same device.
*
* Subject to the constraints of the OPT_ parameters, the users
* library path is searched (in add order):
*
* If the revision exists in some library, the first such library
* is returned.
*
* Otherwise, if the revision's version exists in some library, the
* first such library is returned.
*
* Otherwise, if the revision's branch exists in some library, the
* first such library is returned.
*
* Otherwise, if the revision's category exists in some library, the
* first such library is returned.
*
* Otherwise, if the revision's archive exists in some library, the
* first such library is returned.
*
* Otherwise, the first library in the (qualifying part of) the path
* is returned.
*
* This function will not return 0. If no place can be found to add
* the revision subject to the constraints of the OPT_* parameters,
* an error message is printed and the process exitted with status 2.
*/
t_uchar *
arch_library_add_choice (arch_patch_id * revision,
t_uchar const * const opt_library,
t_uchar const * const opt_same_dev_path)
{
rel_table full_user_path = 0;
rel_table search_path = 0;
t_uchar * target_library = 0;
if (opt_library && opt_same_dev_path)
{
if (!on_same_device (opt_library, opt_same_dev_path))
{
safe_printfmt (2, "baz: indicated library is not on the right device for dir\n lib %s\n dir %s\n", opt_library, opt_same_dev_path);
exit (2);
}
}
/* For the library path on which to search for
* a place to add this revision.
*
* Note that the path is in general a subsequence of
* the user's full path:
*/
full_user_path = arch_my_library_path (arch_library_path_add_order);
if (opt_library)
{
int x;
/* The user wants a specific library.
*
* Make sure it is really on the user's library path,
* then make that one library the entire search path.
*/
for (x = 0; x < rel_n_records (full_user_path); ++x)
{
if (names_same_inode (opt_library, full_user_path[x][0]))
break;
}
if (x == rel_n_records (full_user_path))
{
safe_printfmt (2, "baz: indicated library is not on library path\n dir %s\n", opt_library);
exit (2);
}
rel_add_records (&search_path, rel_make_record (full_user_path[x][0], 0), 0);
}
else
search_path=make_search_path(full_user_path, opt_same_dev_path, 0);
if (!search_path)
{
if (opt_same_dev_path)
safe_printfmt (2, "baz: no library on the same device as dir\n dir %s\n", opt_same_dev_path);
else
safe_printfmt (2, "baz: no library to add to\n");
exit (2);
}
/* Now the search path is set.
*
* If the requested revision is already somewhere on that path,
* we're done.
*/
{
arch_project_tree_t * already_there = 0;
already_there = arch_library_find (search_path, revision, 1);
if (already_there)
{
t_uchar * there_vsn = 0;
t_uchar * there_branch = 0;
t_uchar * there_category = 0;
t_uchar * there_archive = 0;
there_vsn = file_name_directory_file (0, already_there->root);
there_branch = file_name_directory_file (0, there_vsn);
there_category = file_name_directory_file (0, there_branch);
there_archive = file_name_directory_file (0, there_category);
target_library = file_name_directory_file (0, there_archive);
lim_free (0, there_vsn);
lim_free (0, there_branch);
lim_free (0, there_category);
lim_free (0, there_archive);
arch_project_tree_delete (already_there);
}
/* Not there -- we must decide where to add it */
else
{
t_uchar *best_guess = arch_find_best_library(arch_patch_id_archive (revision), search_path, arch_patch_id_revision (revision));
target_library = str_save (0, best_guess);
}
}
rel_free_table (full_user_path);
rel_free_table (search_path);
return target_library;
}
/**
* This function is similar to the above, but it may return NULL
*
* It finds the best greedy library to add a given revision to
* If opt_same_dev_path has a value, matches are restricted to libraries on the
* device containing this path
*/
extern t_uchar * arch_library_greedy_add_choice(arch_patch_id * revision,
t_uchar const * const opt_same_dev_path,
int require_greedy)
{
rel_table full_user_path = 0;
rel_table search_path = 0;
t_uchar * target_library = 0;
t_uchar * best_guess = 0;
full_user_path = arch_my_library_path (arch_library_path_add_order);
search_path = make_search_path (full_user_path, opt_same_dev_path, require_greedy);
best_guess = arch_find_best_library (arch_patch_id_archive (revision), search_path, arch_patch_id_revision (revision));
if (best_guess)
target_library=str_save(0, best_guess);
rel_free_table (full_user_path);
rel_free_table (search_path);
return target_library;
}
/* Produces a search path containing only libraries that matches the given
* criteria:
* if opt_same_dev_path has a value, canditates must be on the same device as
* the provided path
* if require_greedy is true, candidates must be greedy
* returns 0 if there are no matching candidates
*/
rel_table make_search_path(rel_table full_user_path, t_uchar const *const opt_same_dev_path, int require_greedy)
{
rel_table search_path = 0;
int x;
for (x = 0; x < rel_n_records (full_user_path); ++x)
{
if (opt_same_dev_path && !on_same_device (opt_same_dev_path, full_user_path[x][0]))
continue;
if (require_greedy && !arch_library_is_greedy(full_user_path[x][0]))
continue;
rel_add_records (&search_path, rel_make_record (full_user_path[x][0], 0), 0);
}
return search_path;
}
t_uchar * arch_library_find_picky (t_uchar * archive, t_uchar * revision, int check_inode_sigs, t_uchar * opt_same_dev_path, int require_greedy)
{
arch_project_tree_t * answer_tree;
t_uchar * answer = 0;
rel_table search_path = 0;
rel_table full_user_path = arch_my_library_path (arch_library_path_add_order);
arch_patch_id patch_id;
search_path = make_search_path (full_user_path, opt_same_dev_path, require_greedy);
rel_free_table (full_user_path);
if (!search_path)
return answer;
arch_patch_id_init_archive (&patch_id, archive, revision);
answer_tree = arch_library_find (search_path, &patch_id, check_inode_sigs);
answer = str_save (0, answer_tree->root);
arch_project_tree_delete (answer_tree);
arch_patch_id_finalise (&patch_id);
rel_free_table (search_path);
return answer;
}
/* We search along the path and pick the first revision
* with the most specific already-present stuff (same version,
* branch, category, or archive) -- defaulting to the first in the
* path.
*/
t_uchar * arch_find_best_library(t_uchar const * const archive,
rel_table search_path,
t_uchar const * const revision)
{
int guess_priority;
int x;
t_uchar * best_guess = 0;
t_uchar * category = 0;
t_uchar * branch = 0;
t_uchar * version = 0;
if (rel_n_records (search_path) == 0)
return 0;
if (rel_n_records (search_path) == 1)
return search_path[0][0];
category = arch_parse_package_name (arch_ret_category, 0, revision);
branch = arch_parse_package_name (arch_ret_package, 0, revision);
version = arch_parse_package_name (arch_ret_package_version, 0, revision);
best_guess = search_path[0][0];
guess_priority = 0;
for (x = 0; x < rel_n_records (search_path); ++x)
{
if (arch_library_has_version (search_path[x][0], archive, version))
{
best_guess = search_path[x][0];
break;
}
else if (arch_library_has_branch (search_path[x][0], archive, branch))
{
best_guess = search_path[x][0];
guess_priority = 3;
}
else if ((guess_priority < 2) && arch_library_has_category (search_path[x][0], archive, category))
{
best_guess = search_path[x][0];
guess_priority = 2;
}
else if ((guess_priority < 1) && arch_library_has_archive (search_path[x][0], archive))
{
best_guess = search_path[x][0];
guess_priority = 1;
}
}
lim_free (0, category);
lim_free (0, branch);
lim_free (0, version);
return best_guess;
}
void
arch_library_add (int chatter_fd,
int no_patch_noise,
struct arch_archive * arch,
t_uchar const * const revision,
t_uchar const * const opt_library,
t_uchar const * const opt_same_dev_path,
int sparse,
int escape_classes)
{
arch_patch_id * patch_id = arch_patch_id_new_archive (arch->official_name, revision);
t_uchar * dest_library = arch_library_add_choice (patch_id, opt_library, opt_same_dev_path);
t_uchar * dest_directory = arch_library_revision_dir_in_lib (dest_library, patch_id);
if (!safe_access (dest_directory, F_OK))
{
lim_free (0, dest_library);
lim_free (0, dest_directory);
talloc_free (patch_id);
return;
}
if (sparse < 0)
sparse = arch_library_is_sparse (dest_library);
arch_build_library_revision(no_patch_noise, dest_directory, arch, patch_id, opt_library, opt_same_dev_path, sparse, library_txn_callback, escape_classes);
lim_free (0, dest_library);
lim_free (0, dest_directory);
talloc_free (patch_id);
}
static void
library_txn_callback (void * vfp, char * fmt, va_list ap)
{
int fd;
fd = (int)(t_ulong)vfp;
if (fd >= 0)
{
safe_printfmt_va_list (fd, fmt, ap);
safe_flush (1);
}
}
/* tag: Tom Lord Fri May 30 13:50:27 2003 (library-txn.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1