/* cmd-grab.c
*
****************************************************************
* Copyright (C) 2003 Tom Lord, Mark Thomas
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "config-options.h"
#include "libawk/trim.h"
#include "po/gettext.h"
#include "hackerlab/bugs/exception.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/char/str.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/vu/safe.h"
#include "libarch/archive.h"
#include "libarch/archives.h"
#include "libarch/configs.h"
#include "libarch/build-revision.h"
#include "libarch/namespace.h"
#include "libarch/pfs-dav.h"
#include "libarch/project-tree.h"
#include "libfsutils/file-contents.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/tmp-files.h"
#include "commands/cmd.h"
#include "commands/build-config.h"
#include "commands/grab.h"
#include "commands/version.h"
/* __STDC__ prototypes for static functions */
static void arch_grab (int chatter_fd, t_uchar *location, struct arch_build_config_params*);
static t_uchar * find_latest_revision (struct arch_archive * archive, t_uchar *revision);
static size_t remote_path_off (t_uchar * location);
static t_uchar * usage = N_("[options] location");
#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_no_pristines, 0, "no-pristines", 0, \
N_("don't create pristine copies")) \
OP (opt_hardlinks, 0, "link", 0, \
N_("hardlink files to revision library instead of copying")) \
OP (opt_library, 0, "library", 0, \
N_("ensure revisions are in the revision library")) \
OP (opt_sparse, 0, "sparse", 0, \
N_("add library entries sparsely (--link, --library)")) \
OP (opt_release_id, "r", "release-id", 0, \
N_("overwrite ./=RELEASE-ID for this config"))
t_uchar arch_cmd_grab_help[] = N_("grab a published revision\n"
"Grabs a published revision from LOCATION.\n\n"
"A grab file has the following syntax:\n\n"
"Archive-Name: [the name of the archive]\n"
"Archive-Location: [the location of the archive]\n"
"Target-Revision: [PACKAGE|VERSION|REVISION to get]\n"
"Target-Directory: [optional][The suggested directory to get into]\n"
"Target-Config: [optional][A configuration to build upon grabbing]\n"
);
enum options
{
OPTS (OPT_ENUM)
};
static struct opt_desc opts[] =
{
OPTS (OPT_DESC)
{-1, 0, 0, 0, 0}
};
int
arch_cmd_grab (t_uchar * program_name, int argc, char * argv[])
{
int o;
struct opt_parsed * option;
t_uchar * errname;
t_uchar * location;
struct arch_build_config_params config_params = {0, };
errname = "grab";
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_grab_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_no_pristines:
{
config_params.no_pristines = 1;
break;
}
case opt_hardlinks:
{
config_params.hardlinks = 1;
break;
}
case opt_library:
{
config_params.library = 1;
break;
}
case opt_sparse:
{
config_params.sparse = 1;
break;
}
case opt_release_id:
{
config_params.release_id = 1;
break;
}
}
}
if (argc != 2)
goto usage_error;
location = str_save (0, argv[1]);
arch_grab (1, location, &config_params);
lim_free (0, location);
return 0;
}
void
arch_grab (int chatter_fd, t_uchar * location, struct arch_build_config_params* config_params)
{
t_uchar * publication = NULL;
t_uchar * archive_name = NULL;
t_uchar * archive_location = NULL;
t_uchar * target_revision = NULL;
t_uchar * target_directory = NULL;
t_uchar * target_config = NULL;
t_uchar * current_directory = NULL;
t_uchar * target_full_directory = NULL;
t_uchar * tmp_tail;
t_uchar * tmp_directory = NULL;
t_uchar * line = NULL;
t_uchar * colon = NULL;
t_uchar * eol = NULL;
t_uchar * name = NULL;
t_uchar * value = NULL;
t_uchar * final_revision;
t_uchar * uri = NULL;
t_uchar * path = NULL;
struct arch_pfs_session * session;
path = location + remote_path_off (location);
if (path)
{
/************************************************************
* grab from http location
*/
path = str_chr_index(path, '/');
if (path)
{
uri = str_save_n (0, location, path - location);
path = str_save (0, path);
}
else
{
path = str_save (0, "/");
uri = str_save (0, location);
}
session = arch_pfs_connect ( uri, 0);
publication = arch_pfs_file_contents(session, path, 0);
}
else
{
/************************************************************
* try local file system
*/
publication = file_contents (location);
}
if (publication == NULL)
{
safe_printfmt (2, "could not obtain publication data from %s.\n", location);
return;
}
/****************************************************************
* parse the publication
*/
line = publication;
while (*line)
{
while (*line == '\n')
++line;
/************************************************************
* split the line up
*/
colon = str_chr_index (line, ':');
eol = str_chr_index (line, '\n');
if (!eol)
eol = line + str_length (line);
if (!colon || colon > eol)
{
line = eol;
continue;
}
name = str_save_n (0, line, colon - line);
value = str_save_n (0, colon + 1, eol - colon - 1);
name = trim_surrounding_ws (name);
value = trim_surrounding_ws (value);
/************************************************************
* save any useful values
*/
if (str_cmp (name, "Archive-Name") == 0 && !archive_name)
archive_name = value;
else if (str_cmp (name, "Archive-Location") == 0 && !archive_location)
archive_location = value;
else if (str_cmp (name, "Target-Revision") == 0 && !target_revision)
target_revision = value;
else if (str_cmp (name, "Target-Directory") == 0 && !target_directory)
target_directory = value;
else if (str_cmp (name, "Target-Config") == 0 && !target_config)
target_config = value;
else
lim_free (0, value);
lim_free (0, name);
line = eol;
}
lim_free (0, publication);
if (!archive_name || !archive_location || !target_revision)
{
safe_printfmt (2, "Invalid publication file at %s.\n", location);
return;
}
else
{
struct arch_archive *archive;
if (target_directory)
{
/********************************************************
* perform sanity check on target dir
* we don't want to allow any old target dir (to avoid
* "/etc" or "../../../etc" exploits), so we only take
* the tail of the specified target directory.
*/
t_uchar * target_directory_file = file_name_from_directory (0, target_directory);
t_uchar * target_directory_tail = file_name_tail (0, target_directory_file);
lim_free (0, target_directory);
lim_free (0, target_directory_file);
if (str_length (target_directory_tail) == 0)
{
lim_free (0, target_directory_tail);
target_directory_tail = NULL;
}
target_directory = target_directory_tail;
}
if (target_directory == NULL)
{
target_directory = str_save (0, target_revision);
}
current_directory = safe_current_working_directory ();
tmp_tail = str_alloc_cat_many (0, ",,grab.", target_directory, str_end);
target_full_directory = file_name_in_vicinity (0, current_directory, target_directory);
tmp_directory = talloc_tmp_file_name (talloc_context, current_directory, tmp_tail);
rmrf_file (tmp_directory);
safe_mkdir (tmp_directory, 0777);
safe_printfmt(1, "Grabbing: %s/%s\n", archive_name, archive_location);
safe_printfmt(1, "Source: %s, Dest: %s\n", archive_location, target_directory);
if (target_config)
safe_printfmt(1, "Config: %s\n", target_config);
/********************
* Time to grab the archive
*/
archive = arch_archive_connect_branch (archive_location, NULL);
final_revision = find_latest_revision (archive, target_revision);
arch_build_revision (tmp_directory, archive, archive->official_name, final_revision, NULL);
arch_archive_close (archive);
safe_rename (tmp_directory, target_full_directory);
/****************
* Time to config, if we should
*/
if (target_config)
{
arch_project_tree_t * tree;
t_uchar *tree_version;
t_uchar *def_archive = NULL;
if (!arch_valid_config_name (target_config))
{
safe_printfmt(2, "grab: given invalid config name '%s'\n", target_config);
exit(1);
}
tree = arch_project_tree_new (talloc_context, target_full_directory);
if (! tree->root)
panic("grab: Not in a valid tree root");
tree_version = arch_tree_version (tree->root);
if (tree_version)
{
def_archive = arch_parse_package_name (arch_ret_archive, 0, tree_version);
lim_free(0, tree_version);
}
arch_build_config (tree, target_config, config_params , def_archive);
arch_project_tree_delete (tree);
lim_free (0, def_archive);
}
}
lim_free (0, archive_name);
lim_free (0, archive_location);
lim_free (0, target_revision);
lim_free (0, target_directory);
lim_free (0, current_directory);
lim_free (0, target_full_directory);
talloc_free (tmp_directory);
lim_free (0, tmp_tail);
}
static t_uchar *
find_latest_revision (struct arch_archive *archive, t_uchar *revision)
{
t_uchar *oldrevision;
t_uchar *workrevision;
if (! arch_valid_package_name (revision, arch_no_archive, arch_req_package, 1))
{
panic("Invalid Package");
}
workrevision = str_save(0, revision);
if (! arch_valid_package_name (workrevision, arch_no_archive, arch_req_version, 1))
{
rel_table versions = 0;
versions = arch_archive_versions (archive, revision);
if (!versions)
{
panic("No versions for that package exist in the archive");
}
arch_sort_table_by_name_field (1, versions, 0);
oldrevision = workrevision;
workrevision = str_save(0, versions[0][0]);
lim_free(0, oldrevision);
rel_free_table (versions);
}
if (! arch_valid_package_name (workrevision, arch_no_archive, arch_req_patch_level, 1))
{
oldrevision = workrevision;
workrevision = str_alloc_cat_many (0, workrevision, "--",
arch_archive_latest_revision(archive, workrevision, 0),
str_end);
lim_free(0, oldrevision);
}
return workrevision;
}
static size_t
remote_path_off (t_uchar * location)
{
if (!str_casecmp_prefix ("http:/", location))
return sizeof ("http:/");
return 0;
}
/* tag: Mark Thomas Tue Jul 29 22:32:52 BST 2003 (cmd-grab.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1