/* export.c:
*
****************************************************************
* Copyright (C) 2003 Tom Lord
* 2005 Canonical Ltd
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "config-options.h"
#include "po/gettext.h"
#include "hackerlab/bugs/exception.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/char/str.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/copy-file.h"
#include "libarch/namespace.h"
#include "libarch/invent.h"
#include "libarch/project-tree.h"
#include "libarch/my.h"
#include "libarch/archive.h"
#include "libarch/pristines.h"
#include "libarch/build-revision.h"
#include "libarch/debug.h"
#include "libarch/inode-sig.h"
#include "libarch/libraries.h"
#include "libarch/local-cache.h"
#include "libarch/library-txn.h"
#include "commands/cmd.h"
#include "commands/cmdutils.h"
#include "commands/export.h"
#include "commands/version.h"
static t_uchar * usage = N_("[options] [revision] [dir]");
#define OPTS(OP) \
OP (opt_help_msg, "h", "help", 0, \
N_("Display a help message and exit.")) \
OP (opt_long_help, "H", 0, 0, \
N_("Display a verbose help message and exit.")) \
OP (opt_version, "V", "version", 0, \
N_("Display a release identifier string\n" \
"and exit.")) \
OP (opt_silent, "s", "silent", 0, \
N_("no output"))
t_uchar arch_cmd_export_help[] = N_("export all or some of a tree revision\n"
"Extract REVISION from an archive, creating a new source tree with no control information.\n"
"\n"
);
enum options
{
OPTS (OPT_ENUM)
};
static struct opt_desc opts[] =
{
OPTS (OPT_DESC)
{-1, 0, 0, 0, 0}
};
static int
filename_matches (regex_t * pattern, char * filename)
{
int answer;
answer = regexec (pattern, filename, 0, 0, 0);
if (answer == REG_NOMATCH)
return 0;
if (answer == REG_NOERROR)
return 1;
panic ("unexpected regexec error in arch_inventory_traversal");
return -1;
}
/* FIXME: add doxygen docs
* return true if the path is in .arch-ids or {arch}, ie if it matches
* s/(\{arch\}|\.arch-ids)/.
*/
int
arch_is_in_control_dir(t_uchar * path)
{
static int compiled = 0;
static regex_t control_pattern = {0,};
if (!compiled)
{
int re_error;
re_error = regcomp (&control_pattern,
"\\.arch-ids|\\{arch\\}",
REG_EXTENDED);
invariant (!re_error);
compiled = 1;
}
return filename_matches (&control_pattern, path);
}
/*FIXME: add doxygen docs
* return a new rel_table which does not contain any control files
*/
rel_table
arch_filter_control_files (rel_table * index)
{
rel_table new_index = 0;
int l = 0;
for (l = 0; l < rel_n_records (*index); ++l)
{
if (!(
(arch_is_control_file ((*index)[l][0], NULL) && str_cmp_suffix_n((*index)[l][0], ".arch-inventory", 15)) ||
arch_is_in_control_dir ((*index)[l][0])))
{
rel_add_records (&new_index, rel_copy_record ((*index)[l]), 0);
}
}
safe_flush (1);
return new_index;
}
/*FIXME: add doxygen docs
* return a new rel_table which begins with prefix
*/
rel_table
arch_filter_to_subdir (rel_table * index, t_uchar * prefix)
{
rel_table new_index = 0;
int l = 0;
for (l = 0; l < rel_n_records (*index); ++l)
{
if (!str_cmp_prefix(prefix, (t_uchar *)(*index)[l][0]))
rel_add_records (&new_index, rel_copy_record ((*index)[l]), 0);
}
return new_index;
}
int
arch_cmd_export (t_uchar * program_name, int argc, char * argv[])
{
int o;
struct opt_parsed * option;
t_uchar * cache_dir_spec = 0;
t_uchar * selected_library = 0;
int silent = 0;
safe_buffer_fd (1, 0, O_WRONLY, 0);
option = 0;
while (1)
{
o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_cmd_export_help, opt_help_msg, opt_long_help, opt_version);
if (o == opt_none)
break;
switch (o)
{
default:
safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
panic ("internal error parsing arguments");
usage_error:
opt_usage (2, argv[0], program_name, usage, 1);
exit (1);
/* bogus_arg: */
safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string);
goto usage_error;
case opt_silent:
{
silent = 1;
break;
}
}
}
if ((argc < 2) || (argc > 3))
goto usage_error;
{
int here_fd;
t_uchar * revision_spec = 0;
t_uchar * revision = 0;
t_uchar * output_dir = 0;
t_uchar * tmp_dir = 0;
t_uchar * last_arg = 0;
t_uchar * subdir = 0;
struct arch_archive * arch = 0;
arch_project_tree_t * tree;
here_fd = safe_open (".", O_RDONLY, 0);
tree = arch_project_tree_new (talloc_context, ".");
last_arg = str_save (0, argv[1]);
/* three cases:
* no args - tree version, automatic dir.
* XXX not currently supported. (see the check up above)
* 1 arg - named revision or output dir if there is a tree, named revision otherwise.
* 2 args - named revision and output dir
*/
/* input canonicalisation */
{
/* disambiguate dir or revision */
if (tree)
{
arch_project_tree_check_name (tree, &arch, &revision_spec, (argc > 1) ? argv[1] : (char *)tree->fqrevision);
if (!arch && argc == 2)
{
/* ambigous, could be a dir spec */
arch_project_tree_check_name (tree, &arch, &revision_spec, tree->fqrevision);
output_dir = str_save (0, argv[1]);
}
else if (argc == 3)
output_dir = str_save (0, argv[2]);
}
else
{
if (argc < 2)
goto usage_error;
/* no tree, argv[1] must be a revision */
arch = arch_archive_connect_branch (argv[1], &revision_spec);
if (argc == 3)
output_dir = str_save (0, argv[2]);
}
if (!arch)
{
safe_printfmt (2, _("Cannot connect to archive for revision %s\n"), argv[1]);
exit (2);
}
}
invariant (!!arch);
revision_spec = str_replace (revision_spec, arch_determine_fqrevision(&arch, NULL, revision_spec, argv[0]));
revision = arch_parse_package_name (arch_ret_non_archive, NULL, revision_spec);
if (!output_dir)
{
/* name of revision part */
t_uchar * cwd = safe_current_working_directory ();
output_dir = file_name_in_vicinity (0, cwd, revision);
lim_free (0, cwd);
}
invariant (!!output_dir);
{
t_uchar * canonical_dir = file_name_from_directory (0, output_dir);
t_uchar * output_dirname = file_name_directory (0, canonical_dir);
int output_dirname_exists = safe_access (output_dirname, F_OK) == 0;
if (!output_dirname_exists)
{
safe_printfmt (2, _("Parent dir of output directory %s does not exist\n"), output_dirname);
exit (2);
}
tmp_dir = tmp_file_name_in_tmp ("export");
}
if (!safe_access (output_dir, F_OK))
{
safe_printfmt (2, "%s: output directory already exists (%s)\n",
argv[0], output_dir);
exit (1);
}
rmrf_file (tmp_dir);
safe_mkdir (tmp_dir, 0777);
{
t_uchar * revision_dir = 0;
rel_table index = 0;
rel_table index_stripped = 0;
rel_table index_subdird = 0;
arch_project_tree_t * tmp_tree;
int old_debug_level = arch_debug_levels[dbg_builder];
/* FIXME: inefficient, since arch_build_revision is already
* doing an inventory and throwing it away.
*/
if (silent)
arch_debug_levels[dbg_builder] = -1;
arch_build_revision (tmp_dir, arch, arch->official_name, revision, NULL);
arch_debug_levels[dbg_builder] = old_debug_level;
tmp_tree = arch_project_tree_new (talloc_context, tmp_dir);
index = arch_source_inventory (tmp_tree, 1, 0, 0);
safe_flush (1);
index_stripped = arch_filter_control_files (&index);
if (subdir)
{
safe_printfmt(2," limiting to %s", subdir);
index_subdird = arch_filter_to_subdir (&index_stripped, str_alloc_cat(0, "./", subdir));
}
else
{
index_subdird = index_stripped;
}
rel_sort_table_by_field (0, index_subdird, 0);
copy_file_list (output_dir, tmp_dir, index_subdird);
lim_free (0, revision_dir);
arch_project_tree_delete (tmp_tree);
}
arch_archive_close (arch);
safe_fchdir (here_fd);
safe_close (here_fd);
rmrf_file (tmp_dir);
lim_free (0, revision_spec);
lim_free (0, revision);
lim_free (0, output_dir);
lim_free (0, tmp_dir);
arch_project_tree_delete (tree);
}
lim_free (0, selected_library);
lim_free (0, cache_dir_spec);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1