/* cmdutils.c:
*
* vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
****************************************************************
* Copyright (C) 2004 Tom Lord, Canonical Ltd.
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
/**
* @file cmdutils.c
* @brief Utility functions for sub-commands
*/
/**
* \defgroup cmdutils Utility functions for sub-commands
* @{
*/
#include "hackerlab/bugs/panic.h"
#include "hackerlab/bugs/exception.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/vu/vu.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/str.h"
#include "po/gettext.h"
#include "commands/cmdutils.h"
#include "commands/diff.h"
#include "libarch/libraries.h"
#include "libarch/project-tree.h"
#include "libarch/patch-logs.h"
#include "libarch/pfs.h"
#include "libarch/local-cache.h"
#include "libfsutils/dir-as-cwd.h"
#include "libfsutils/rmrf.h"
#include "libarch/local-cache.h"
#include "libarch/changeset-report.h"
#include "libarch/make-changeset.h"
#include "libarch/inode-sig.h"
#include "libarch/arch.h"
/* __STDC__ prototypes for static functions */
static t_uchar * missing_string (enum arch_valid_package_name_types type);
static t_uchar * name_string (enum arch_valid_package_name_types type);
static enum arch_parse_package_name_type valid_to_parse (enum arch_valid_package_name_types type);
/* Gather a fully-qualified revision name from the current tree
* and input (e.g. patch-10)
*/
t_uchar *
arch_fqrvsn_from_tree_and_input (void * context,
t_uchar *cmd,
t_uchar * string,
t_uchar * dir)
{
t_uchar * ret = 0;
arch_project_tree_t * tree;
if (arch_valid_package_name (string, arch_req_archive, arch_req_patch_level, 0))
return talloc_strdup (context, string);
tree = arch_project_tree_new (talloc_context, dir);
if (!tree->root)
{
safe_printfmt (2, "%s: not in a project tree\n dir: %s\n", cmd, directory_as_cwd (dir));
exit (2);
}
if (!tree->fqversion)
{
safe_printfmt (2, "%s: no tree-version set\n tree: %s\n", cmd, tree->root);
exit (2);
}
if (arch_valid_package_name (string, arch_maybe_archive, arch_req_patch_level, 0))
{
ret = arch_fully_qualify (tree->archive, string);
ret = str_replace (ret, talloc_strdup (context, ret));
}
else if (arch_valid_package_name (string, arch_maybe_archive, arch_req_version, 0))
{
t_uchar * archive = arch_parse_package_name (arch_ret_archive, tree->archive, string);
t_uchar * version = arch_parse_package_name (arch_ret_package_version, 0, string);
t_uchar * patch_level = arch_highest_patch_level (tree->root, archive, version);
t_uchar * revision = str_alloc_cat_many (0, version, "--", patch_level, str_end);
ret = arch_fully_qualify (archive, revision);
ret = str_replace (ret, talloc_strdup (talloc_context, ret));
lim_free (0, archive);
lim_free (0, version);
lim_free (0, patch_level);
lim_free (0, revision);
}
else
{
ret = str_alloc_cat_many (0, tree->fqversion, "--", string, str_end);
if (!arch_valid_package_name (ret, arch_req_archive, arch_req_patch_level, 0))
{
/* TODO let this archive connection be cached open - propogate it back up */
struct arch_archive *temp_arch = NULL;
lim_free (0, ret);
ret = NULL;
arch_project_tree_check_name (tree, &temp_arch, &ret, string);
if (!temp_arch)
{
safe_printfmt (2, "%s: invalid revision or patch name (%s)\n", cmd, string);
exit (2);
}
arch_archive_close (temp_arch);
/* FUGLY. FIXME - overhaul the entire api in the style of arch_project_tree_check_name ..
* perhaps arch_project_tree_validate_input which /may/ connect if needed
*/
ret = str_replace (ret, arch_fqrvsn_from_tree_and_input (talloc_context, cmd, ret, dir));
}
else
ret = str_replace (ret, talloc_strdup (talloc_context, ret));
}
talloc_free (tree);
return talloc_steal (context, ret);
}
int
arch_category_exists (struct arch_archive *arch, t_uchar * package)
{
t_uchar * category = 0;
rel_table categories = 0;
int x;
category = arch_parse_package_name (arch_ret_category, 0, package);
categories = arch_archive_categories (arch);
for (x = 0; x < rel_n_records (categories); ++x)
{
if (!str_cmp (category, categories[x][0]))
{
lim_free (0, category);
rel_free_table (categories);
return 1;
}
}
return 0;
}
int
arch_package_exists (struct arch_archive * arch, t_uchar * package)
{
t_uchar * category = 0;
t_uchar * branch = 0;
rel_table categories = 0;
rel_table branches = 0;
int x;
int found_it = 0;
category = arch_parse_package_name (arch_ret_category, 0, package);
branch = arch_parse_package_name (arch_ret_package, 0, package);
categories = arch_archive_categories (arch);
for (x = 0; x < rel_n_records (categories); ++x)
{
if (!str_cmp (category, categories[x][0]))
break;
}
if (x < rel_n_records (categories))
{
branches = arch_archive_branches (arch, category);
for (x = 0; x < rel_n_records (branches); ++x)
{
if (!str_cmp (branch, branches[x][0]))
{
found_it = 1;
break;
}
}
}
lim_free (0, category);
lim_free (0, branch);
rel_free_table (categories);
rel_free_table (branches);
return found_it;
}
int
arch_version_exists (struct arch_archive * arch, t_uchar * version_spec)
{
t_uchar * branch = 0;
t_uchar * version = 0;
rel_table versions = 0;
int x;
int found_it = 0;
branch = arch_parse_package_name (arch_ret_package, 0, version_spec);
version = arch_parse_package_name (arch_ret_package_version, 0, version_spec);
versions = arch_archive_versions (arch, branch);
for (x = 0; x < rel_n_records (versions); ++x)
{
if (!str_cmp (version, versions[x][0]))
{
found_it = 1;
break;
}
}
lim_free (0, branch);
lim_free (0, version);
rel_free_table (versions);
return found_it;
}
/**
* @brief Checks whether the specified item (and all its components) exist
* @param arch The archive to look in
* @param type The type of name being queried
* @param package The name being queried
*/
void
arch_check_for (struct arch_archive * arch,
enum arch_valid_package_name_types type,
t_uchar const * const package)
{
t_uchar * spec = arch_parse_package_name (arch_ret_non_archive, "", package);
int missing = -1;
/* note fall-through behavior */
switch (type)
{
case arch_req_patch_level:
if (arch_revision_exists (arch, spec))
break;
else
missing = arch_req_patch_level;
default:
if (!arch_category_exists (arch, spec))
{
missing=arch_req_category;
break;
}
if (type == arch_req_category)
break;
if (!arch_package_exists (arch, spec))
{
missing = arch_req_package;
break;
}
if (type == arch_req_package)
break;
if (!arch_version_exists (arch, spec))
missing = arch_req_version;
};
if (missing != -1)
{
arch_print_missing (arch, missing, type, spec);
exit (2);
}
lim_free (0, spec);
}
void
arch_print_missing (struct arch_archive * arch,
enum arch_valid_package_name_types type,
enum arch_valid_package_name_types supplied_type,
t_uchar * spec)
{
t_uchar * name = missing_string (type);
t_uchar * supplied_name = name_string (supplied_type);
t_uchar * portion = arch_parse_package_name (valid_to_parse(type), arch->official_name, spec);
t_uchar * nonarch = arch_parse_package_name (arch_ret_non_archive, "", spec);
safe_printfmt (2, "No such %s (%s)\n", name, portion);
safe_printfmt (2, " name: %s\n location: %s\n %s: %s\n", arch->official_name, arch->location, supplied_name, nonarch);
lim_free (0, portion);
lim_free (0, nonarch);
}
static t_uchar *
missing_string (enum arch_valid_package_name_types type)
{
switch (type)
{
case arch_req_category:
return "category";
break;
case arch_req_package:
return "package";
break;
case arch_req_version:
return "version";
break;
case arch_req_patch_level:
return "patchlevel";
break;
default:
panic ("missing_string: bad argument.");
return 0;
};
}
static t_uchar *
name_string (enum arch_valid_package_name_types type)
{
switch (type)
{
case arch_req_category:
return "category";
break;
case arch_req_package:
return "package";
break;
case arch_req_version:
return "package-version";
break;
case arch_req_patch_level:
return "revision";
break;
default:
panic ("name_string: bad argument.");
return 0;
};
}
static enum arch_parse_package_name_type
valid_to_parse (enum arch_valid_package_name_types type)
{
switch (type)
{
case arch_req_category:
return arch_ret_category;
break;
case arch_req_package:
return arch_ret_package;
break;
case arch_req_version:
return arch_ret_version;
break;
case arch_req_patch_level:
return arch_ret_patch_level;
break;
default:
panic ("valid_to_parse: bad argument.");
return 0;
};
}
void
arch_check_library_for_revision (t_uchar * archive,
t_uchar * revision)
{
arch_patch_id patch_id;
arch_project_tree_t * loc;
arch_patch_id_init_archive (&patch_id, archive,revision);
loc = arch_library_find (0, &patch_id, 0);
if (loc)
{
arch_project_tree_delete (loc);
}
else
{
safe_printfmt (2, "Could not find revision in any library:\n%s/%s\n", archive, revision);
exit (2);
}
arch_patch_id_finalise (&patch_id);
}
t_uchar *
safe_tree_version (t_uchar const * const cmd_name)
{
arch_project_tree_t * tree = arch_project_tree_new (talloc_context, ".");
t_uchar * tree_version = 0;
if (!tree->root)
{
safe_printfmt (2, "%s: not in a project tree\n dir: %s\n", cmd_name, directory_as_cwd ("."));
exit (2);
}
if (!tree->fqversion)
{
safe_printfmt (2, "%s: no tree-version set\n tree: %s\n", cmd_name, tree->root);
exit (2);
}
tree_version = tree->fqversion;
tree->fqversion = NULL;
arch_project_tree_delete (tree);
return tree_version;
}
static t_uchar *
arch_determine_fqrevision_safe_latest_revision (struct arch_archive *arch, t_uchar *version_spec, t_uchar const *cmd_name)
{
t_uchar * version;
t_uchar * revision;
arch_check_for (arch, arch_req_version, version_spec);
version = arch_parse_package_name (arch_ret_package_version, 0, version_spec);
version_spec = arch_archive_latest_revision (arch, version, 2);
if (!version_spec)
{
safe_printfmt (1, "%s: version has no revisions (%s/%s)\n", cmd_name, arch->official_name, version);
exit (1);
}
/* FIXME: why deliberately not?
* RBC 20050312 - becayse lock-revision connects by name so you can unlock revisions in -MIRROR etc
* thus lock-revision needs to accept URL based input as an option, when then will
* propogate down to here.
*/
revision = arch_fully_qualify ((t_uchar *)arch->official_name /*deliberately not official_name */, version_spec);
lim_free (0, version);
lim_free (0, version_spec);
return revision;
}
static t_uchar *
arch_determine_fqrevision_common (struct arch_archive ** arch,
t_uchar * default_archive,
t_uchar * revision_spec,
t_uchar const * const cmd_name)
{
t_uchar * new_revision_spec = 0;
if (!arch_valid_package_name (revision_spec, arch_maybe_archive, arch_req_version, 1))
{
/* url or patch-level */
if (!arch_valid_patch_level_name (revision_spec))
{
/* url ? */
if (!*arch)
*arch = arch_archive_connect_branch (revision_spec, &new_revision_spec);
if (!*arch || !arch_valid_package_name (new_revision_spec, arch_maybe_archive, arch_req_version, 1))
{
safe_printfmt (2, "%s: illegal revision spec (%s)\n", cmd_name, revision_spec);
exit (1);
}
}
else
{
/* given a patch-level */
t_uchar * tree_version = safe_tree_version (cmd_name);
new_revision_spec = str_alloc_cat_many (0, tree_version, "--", revision_spec, str_end);
}
}
else if (!arch_valid_package_name (revision_spec, arch_req_archive, arch_req_version, 1))
{
new_revision_spec = arch_fully_qualify (default_archive, revision_spec);
}
else
{
new_revision_spec = str_save (0, revision_spec);
}
if (!*arch)
{
*arch = arch_archive_connect_branch (new_revision_spec, NULL);
if (!*arch)
{
safe_printfmt (2, "Can't connect to %s.\n", new_revision_spec);
exit(1);
}
}
return new_revision_spec;
}
t_uchar *
arch_determine_fqrevision (struct arch_archive ** arch,
t_uchar * default_archive,
t_uchar * revision_spec,
t_uchar const * const cmd_name)
{
t_uchar * revision = 0;
t_uchar * new_revision_spec = arch_determine_fqrevision_common (arch, default_archive, revision_spec, cmd_name);
if (arch_valid_package_name (new_revision_spec, arch_maybe_archive, arch_req_patch_level, 0))
{
arch_check_for (*arch, arch_req_patch_level, new_revision_spec);
revision = str_save (0, new_revision_spec);
}
else
{
revision = arch_determine_fqrevision_safe_latest_revision (*arch, new_revision_spec, cmd_name);
}
lim_free (0, new_revision_spec);
return revision;
}
/**
* \brief this function determines the next revision in a namespace
*
* note that its API is broken - it needs to operate in such a way that
* it can disambiguate between mirrors.
* i.e. it needs to accept a location as an option, and look for
* revision spec in that location, falling back to heuristics as appropriate
*/
t_uchar *
arch_determine_fqrevision_next (struct arch_archive ** arch,
t_uchar * default_archive,
t_uchar * revision_spec,
t_uchar const * const cmd_name)
{
t_uchar * fqrevision_next;
t_uchar * current;
t_uchar * new_revision_spec = arch_determine_fqrevision_common (arch, default_archive, revision_spec, cmd_name);
if (arch_valid_package_name (new_revision_spec, arch_maybe_archive, arch_req_patch_level, 0))
/* FIXME-REMOVENAME: this must stay as -name until lock-revision accepts url input,
* when the archive to qualify from can be probed from the url.
* RBC 20050312
*/
current = arch_fully_qualify ((*arch)->registered_name, new_revision_spec);
else
current = arch_determine_fqrevision_safe_latest_revision (*arch, new_revision_spec, cmd_name);
{
t_uchar * revision = arch_parse_package_name (arch_ret_non_archive, "", current);
t_uchar * spec = arch_parse_package_name (arch_ret_package_version, "", current);
t_uchar * patchlevel = arch_parse_package_name (arch_ret_patch_level, "", current);
if (arch_revision_exists (*arch, revision))
{
t_uchar * revision_next = arch_next_revision (spec,patchlevel,0,0,cmd_name);
/* FIXME-REMOVENAME rbc 20050312 url awareness allows us to change this */
fqrevision_next = arch_fully_qualify ((*arch)->registered_name, revision_next);
lim_free (0, revision);
lim_free (0, revision_next);
}
else
fqrevision_next = str_save(0, current);
lim_free (0, spec);
lim_free (0, patchlevel);
}
lim_free (0, current);
lim_free (0, new_revision_spec);
return fqrevision_next;
}
/*
* @brief Determine the specified revision using the local tree->
*
* Uses the local tree if any, the default archive, and the revision spec.
* Leaves arch connected to the archive within which this revision should be
* found
* @return a newly-allocated fully-qualified revision name
*/
t_uchar *
arch_determine_revision (struct arch_archive ** arch,
t_uchar * default_archive,
t_uchar * revision_spec,
t_uchar const * const cmd_name)
{
t_uchar * fqrevision = arch_determine_fqrevision (arch, default_archive, revision_spec, cmd_name);
t_uchar * revision = arch_parse_package_name (arch_ret_non_archive, 0, fqrevision);
lim_free (0, fqrevision);
return revision;
}
/**
* @brief as arch_determine_revision, but return the patch level above the one
* requested.
*
* If the one requested doesn't exist, it will be returned.
* in a present branch, permit it
*/
t_uchar *
arch_determine_revision_next (struct arch_archive ** arch,
t_uchar * default_archive,
t_uchar * revision_spec,
t_uchar const * const cmd_name)
{
t_uchar * fqrevision = arch_determine_fqrevision_next (arch, default_archive, revision_spec, cmd_name);
t_uchar * revision = arch_parse_package_name (arch_ret_non_archive, 0, fqrevision);
lim_free (0, fqrevision);
return revision;
}
/**
* \brief check that a directory is accessible and optionally writable
* \param path The path to check
* \param check_write If true, check for writability
* \param soft_errors if true, don't report or exit on errors
* \return 0 on success
* */
int
arch_check_directory (t_uchar const * const path, int check_write, int soft_errors)
{
int errn;
int mode = R_OK+X_OK;
struct stat statb;
if (check_write) mode+=W_OK;
if (vu_stat (&errn, (t_uchar *)path, &statb) == -1)
{
if (soft_errors)
return -1;
if (errn == ENOENT)
safe_printfmt (2, "Specified directory does not exist\nPath: %s\n", path);
else
safe_printfmt (2, "Error encountered accessing directory (%s)\nPath: %s\n", errno_to_string (errn), path);
exit(2);
}
if (!S_ISDIR(statb.st_mode))
{
if (soft_errors)
return -1;
safe_printfmt (2, "Specified path is not a directory\nPath: %s\n", path);
exit (2);
}
if (access (path, mode) == -1)
{
if (soft_errors)
return -1;
safe_printfmt (2, "Error accessing specified directory (%s)\nDirectory: %s\n", errno_to_string (errno), path);
exit (2);
}
return 0;
}
void
arch_check_uri (t_uchar * uri)
{
/* hackish, I know - should be a any_of search RBC20041202 */
if (!str_chr_index (uri, '\n') &&
!str_chr_index (uri, '\r') &&
arch_valid_uri (uri))
return;
safe_printfmt (2, "URL invalid or unsupported: %s\n", uri);
exit (2);
}
/**
* @brief Check whether the revision exists, using local data first, before
* trying the archive
* @param archive the archive containg the revision
* @param revision the unqualified revision
*/
extern void
arch_check_revision_local (t_uchar * archive,
t_uchar * revision)
{
struct arch_archive * arch = 0;
arch_patch_id patch_id;
arch_project_tree_t * loc;
arch_patch_id_init_archive (&patch_id, archive,revision);
loc = arch_library_find (0, &patch_id, 0);
arch_patch_id_finalise (&patch_id);
if (loc)
{
arch_project_tree_delete (loc);
return;
}
arch = arch_archive_connect_branch (archive, NULL);
if (!arch)
{
safe_printfmt (2, _("cannot connect to archive '%s' to verify revision '%s'\n"), archive, revision);
exit (2);
}
arch_check_for (arch, arch_req_patch_level, revision);
arch_archive_close (arch);
}
/**
* @brief Check the archive to make sure it's suitable for normal use, and not
* just for mirroring.
* @param arch The archive to check
*/
extern void arch_check_arch (struct arch_archive *arch)
{
/* FIXME-REMOVENAMES: this goes in baz 1.4 with registered names */
if (str_cmp (arch->registered_name, arch->official_name) != 0)
{
safe_printfmt (2, _("This archive can only be used as a mirror source or target, because it is \nregistered with the wrong name. To use it for other purposes, you should\nregister it using the official name.\n"));
safe_printfmt (2, _("name: %s\nofficial name: %s\nlocation: %s\n"), arch->official_name, arch->official_name, arch->location);
exit (2);
}
}
t_uchar *
arch_project_tree_revision (t_uchar const * name, t_uchar const * tree_root)
{
t_uchar * result;
arch_project_tree_t * tree = arch_project_tree_new (talloc_context, tree_root);
if (!tree->fqversion)
{
safe_printfmt (2, "%s: no tree-version set\n tree: %s\n",
name, tree_root);
exit (2);
}
result = tree->fqrevision;
tree->fqrevision=NULL;
arch_project_tree_delete (tree);
return result;
}
void
arch_cmd_fail (t_uchar const *name, char const * format, ...)
{
va_list ap;
safe_printfmt (2, "%s :", name);
va_start (ap, format);
safe_printfmt_va_list (2, format, ap);
va_end (ap);
safe_flush (2);
exit (2);
}
t_uchar *
arch_archive_last_level (struct arch_archive * arch, t_uchar *version)
{
t_uchar * last_level = 0;
rel_table revisions = arch_archive_revisions (arch, version, 0);
if (revisions)
{
last_level = str_save (0, revisions[rel_n_records (revisions) - 1][0]);
}
rel_free_table (revisions);
return last_level;
}
t_uchar *
arch_next_revision (t_uchar * version, t_uchar * last_level, int seal, int fix, t_uchar const * const argv0)
{
enum arch_patch_level_type last_level_type;
enum arch_patch_level_type desired_level_type;
t_ulong last_n;
t_ulong desired_n;
t_uchar * desired_level = 0;
t_uchar * revision = 0;
last_level_type = arch_analyze_patch_level (&last_n, last_level);
switch (last_level_type)
{
default:
panic ("NOT IMPLEMENTED YET");
panic ("internal error");
break;
case arch_is_base0_level:
{
if (seal)
{
desired_level_type = arch_is_version_level;
desired_n = 0;
}
else if (fix)
{
safe_printfmt (2, "%s: can not --fix before --seal\n", argv0);
exit (2);
}
else
{
desired_level_type = arch_is_patch_level;
desired_n = 1;
}
break;
}
case arch_is_patch_level:
{
if (seal)
{
desired_level_type = arch_is_version_level;
desired_n = 0;
}
else if (fix)
{
safe_printfmt (2, "%s: can not --fix before --seal\n", argv0);
exit (2);
}
else
{
desired_level_type = arch_is_patch_level;
desired_n = last_n + 1;
}
break;
}
case arch_is_version_level:
{
if (seal)
{
safe_printfmt (2, "%s: version already sealed\n", argv0);
exit (2);
}
else if (fix)
{
desired_level_type = arch_is_versionfix_level;
desired_n = 1;
}
else
{
safe_printfmt (2, "%s: cannot commit to sealed version without --fix\n",
argv0);
exit (2);
}
break;
}
case arch_is_versionfix_level:
{
if (seal)
{
safe_printfmt (2, "%s: version already sealed\n", argv0);
exit (2);
}
else if (fix)
{
desired_level_type = arch_is_versionfix_level;
desired_n = last_n + 1;
}
else
{
safe_printfmt (2, "%s: cannot commit to sealed version without --fix\n",
argv0);
exit (2);
}
break;
}
}
desired_level = arch_form_patch_level (desired_level_type, desired_n);
revision = str_alloc_cat_many (0, version, "--", desired_level, str_end);
lim_free (0, desired_level);
return revision;
}
t_uchar *
arch_version_spec_to_fq_version(t_uchar * version_spec)
{
t_uchar * archive;
t_uchar * version;
t_uchar * fqversion;
archive = arch_parse_package_name (arch_ret_archive, NULL, version_spec);
version = arch_parse_package_name (arch_ret_package_version, 0, version_spec);
fqversion = arch_fully_qualify (archive, version);
return fqversion;
}
void
arch_assert_in_tree(t_uchar * program_name, t_uchar * dir)
{
t_uchar * tree_root;
tree_root = arch_tree_root (0, dir, 0);
if (!tree_root)
{
safe_printfmt (2, "%s: not in project tree (%s)\n", program_name, dir);
exit (1);
}
}
/**
* @brief Does arch_tree_root(dir) have any uncommited changes?
* @param program_name The program name to emit in errors
* @param tree The path to the directory to examine
* @return patch to a changeset containing the changes if there are any
*/
t_uchar *
arch_any_local_changes (t_uchar * program_name, arch_project_tree_t * tree)
{
arch_project_tree_t *orig;
struct arch_make_changeset_report make_report = {0, };
struct arch_changeset_report report = {0, };
assoc_table inode_shortcut = 0;
rel_table limits = 0;
t_uchar * result;
{
t_uchar * tmp = arch_diff_default_output_dir (talloc_context, tree->root, NULL);
result = str_save (0, tmp);
talloc_free (tmp);
}
orig = arch_find_or_make_local_tree_copy (1, tree, 0, 0, tree->archive, tree->revision);
/* Is this neccessary? -rjw 20050109 */
if (!orig)
{
safe_printfmt (2, "%s: no local copies to compare to (%s/%s)\n consider `add-pristine --help'\n",
program_name, tree->archive, tree->revision);
exit (2);
}
arch_read_inode_sig_ids (0, &inode_shortcut, tree->root, tree->archive, tree->revision);
arch_make_changeset (&make_report, orig, tree, result, arch_unspecified_id_tagging, arch_inventory_unrecognized, limits, inode_shortcut, 0, arch_escape_classes);
arch_evaluate_changeset (&report, result);
if (!arch_any_changes (&report))
{
rmrf_file (result);
lim_free (0, result);
result = NULL;
}
arch_project_tree_delete (orig);
arch_free_make_changeset_report_data (&make_report);
return result;
}
/**
* \brief factoring out from interpret_delta_path
*/
static t_uchar *
arch_interpret_delta_make (t_uchar **arch_out, t_uchar **rev_out,
arch_project_tree_t * tree, arch_project_tree_t * cache,
struct arch_archive * arch, t_uchar const * const archive, t_uchar const * const revision,
t_uchar const * const scratch_if_no_root)
{
t_uchar * answer;
safe_printfmt (1, "* finding or making %s/%s\n", archive, revision);
safe_flush (1);
answer = arch_find_or_make_tmp_local_copy (1, tree->root ? tree->root: scratch_if_no_root, tree, cache, arch, archive, revision);
if (arch_out)
*arch_out = str_save (0, archive);
if (rev_out)
*rev_out = str_save (0, revision);
return answer;
}
/**
* \brief return a tree for use in delta and related commands
*/
arch_project_tree_t *
arch_interpret_delta_path (t_uchar ** arch, t_uchar ** rev, t_uchar * scratch_dir, t_uchar * spec, arch_project_tree_t * cache)
{
/* give local paths priority */
if (!arch_check_directory (spec, 0, 1))
return arch_project_tree_new_ext (talloc_context, directory_as_cwd (spec), 1, 0);
/* patch level or RL-or-official-name */
else
{
arch_project_tree_t * answer;
t_uchar * archive = 0;
t_uchar * revision = 0;
t_uchar * maybe_revision = 0;
t_uchar * fqrn = 0;
struct arch_archive *arch_struct = 0;
t_uchar * official_namespace = NULL;
/* try tree relative validation */
arch_project_tree_t * tree = arch_project_tree_new (talloc_context, ".");
arch_project_tree_check_name (tree, &arch_struct, &official_namespace, spec);
if (!arch_struct)
/* if we haven't errored and have reached here it
* must be a local path, so error with diagnostics
* if we can't use it.
*/
arch_check_directory (spec, 0, 0);
archive = arch_parse_package_name (arch_ret_archive, NULL, official_namespace);
maybe_revision = arch_parse_package_name (arch_ret_non_archive,
NULL,
official_namespace);
/* Here, we check to see if a revision is specified. If so, then
* we use that. If not, then lets hunt for the lastest one, because
* we may have been givine just a package or a version
*/
if (arch_valid_package_name (maybe_revision,
arch_no_archive,
arch_req_patch_level,
0))
{
fqrn = str_save (0, maybe_revision);
}
else
{
fqrn = arch_archive_latest_revision(arch_struct, maybe_revision, 1);
}
revision = arch_parse_package_name(arch_ret_non_archive,
NULL,
fqrn);
answer = arch_project_tree_new (talloc_context, arch_interpret_delta_make (arch, rev, tree, cache, arch_struct, archive, revision, scratch_dir));
arch_archive_close (arch_struct);
lim_free (0, archive);
lim_free (0, revision);
lim_free (0, maybe_revision);
lim_free (0, fqrn);
arch_project_tree_delete (tree);
return answer;
}
}
/**
* \brief generate a list of file paths and ids from a user input list of names
* \throw EINVAL on missing or invalid paths.
* \param context the talloc context to allocate against
* \param program_name for errors
* \param tree the tree to look in
* \param count the number of strings
* \param first a pointer to a pointer to the first string
* \return a rel_table
*/
rel_table
arch_paths_from_user (void * context, t_uchar const * program_name, arch_project_tree_t * tree, int count, char **first)
{
rel_table result = NULL;
rel_table tmp;
rel_table full_source_files;
rel_table filter_paths = NULL;
int index;
if (count) {
tmp = rel_unflatten (count, 1, first);
rel_for_each (tmp, index)
{
t_uchar * relpath = arch_project_tree_rel_path_from_user_input (tree,tmp[index][0]);
rel_add_records (&filter_paths, rel_make_record (relpath, NULL), NULL);
lim_free (0, relpath);
}
rel_free_table (tmp);
}
full_source_files = arch_source_files_inventory (tree, 0, 0);
result = arch_inventory_included (full_source_files, filter_paths);
rel_free_table (full_source_files);
if ((rel_n_records(filter_paths) != 0) && (rel_n_records(result) != rel_n_records(filter_paths)))
{
rel_free_table (filter_paths);
rel_free_table (result);
Throw (exception (EINVAL, _("Some files specified do not exist or are not source files.")));
}
rel_free_table (filter_paths);
talloc_steal (context, ar_base (result));
return result;
}
/**
* }@
*/
/* tag: Aaron Bentley Tue Dec 23 10:46:00 2003 (cmdutils.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1