/* configs.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/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/pika-escaping-utils.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/mem/talloc.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/arrays/ar.h"
#include "libfsutils/ensure-dir.h"
#include "libfsutils/safety.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/read-line.h"
#include "libfsutils/rmrf.h"
#include "libarch/arch.h"
#include "libarch/configs.h"
#include "libarch/namespace.h"
#include "libarch/pfs.h"
#include "libarch/project-tree.h"
#include "libarch/patch-logs.h"
#include "commands/cmd.h"
#include "commands/get.h"
#include "po/gettext.h"
/* __STDC__ prototypes for static functions */
static rel_table arch_read_config_simple (t_uchar const * const config_path);
static t_uchar * arch_config_output_path (t_uchar * tree_root, t_uchar * target_dir);
/**
* \brief determine the path for a config file or for a config line, taking a potential ree_root
* \return t_uchar * the fully qualified path of the file
*/
t_uchar *
arch_config_path (arch_project_tree_t * tree, t_uchar * config_name)
{
t_uchar * rel = 0;
t_uchar * answer = 0;
t_uchar * tree_rel = 0;
invariant (arch_valid_config_name (config_name));
tree_rel = file_name_in_vicinity (0, tree->root, "configs");
tree_rel = str_replace (tree_rel, file_name_in_vicinity (0, tree_rel, config_name));
rel = file_name_in_vicinity (0, tree->root, config_name);
if (! safe_access(tree_rel, F_OK))
/* tree/configs/<path> */
answer = str_save (0, tree_rel);
else if (! safe_access(rel, F_OK))
/* tree/<path> */
answer = str_save (0, rel);
else
/* <path? */
answer = str_save (0, config_name);
lim_free (0, rel);
lim_free (0, tree_rel);
return answer;
}
rel_table
arch_read_config (arch_project_tree_t * tree, t_uchar * config_name)
{
t_uchar * config_path = 0;
rel_table answer = 0;
invariant (arch_valid_config_name (config_name));
config_path = arch_config_path (tree, config_name);
answer = arch_read_config_simple (config_path);
lim_free (0, config_path);
return answer;
}
/**
* \brief read a config from a filepath
* \return rel_table descrbing the config
*/
rel_table
arch_read_config_simple (t_uchar const * const config_path)
{
int in_fd;
int errn;
int temp_file_created = 0;
t_uchar * tmp_file = tmp_file_name_in_tmp (",,config-tmp");
rel_table answer = NULL;
/* try literal path */
in_fd = vu_open (&errn, (t_uchar *)config_path, O_RDONLY, 0);
if (-1 == in_fd)
{
/* download via pfs */
int out_fd;
t_uchar * tmp_path;
t_uchar * head;
t_uchar * tail;
struct arch_pfs_session * session = NULL;
tmp_path = file_name_from_directory(0,(t_uchar *)config_path);
head = file_name_directory(0, tmp_path);
tail = file_name_tail(0, config_path);
lim_free(0,tmp_path);
if (!head)
panic (_("Could not find file for config"));
session = arch_pfs_connect (head, 0);
out_fd = safe_open (tmp_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (! out_fd)
panic (_("Could not create temporary config file"));
arch_pfs_get_file (session, out_fd, tail, 0);
temp_file_created = 1;
safe_close(out_fd);
in_fd = vu_open (&errn, (t_uchar *)tmp_file, O_RDONLY, 0);
arch_pfs_disconnect (&session);
lim_free(0, head);
lim_free(0, tail);
}
while (1)
{
t_uchar * line = 0;
t_uchar * pos;
line = read_line_from_fd (in_fd);
if (!line)
break;
for (pos = line; char_is_space (*pos); ++pos)
;
if (*pos && (*pos != '#'))
{
t_uchar * end;
t_uchar * loc = 0;
t_uchar * rev = 0;
for (pos = line; char_is_space (*pos); ++pos)
;
for (end = pos; *end && !char_is_space (*end); ++end)
;
if (end == pos)
{
safe_printfmt (2, "arch_read_config: illegal config file (%s)\n", config_path);
exit (2);
}
loc = pika_save_unescape_iso8859_1_n (0, 0, pos, end - pos );
for (pos = end; char_is_space (*pos); ++pos)
;
for (end = pos; *end && !char_is_space (*end); ++end)
;
if (end == pos)
{
safe_printfmt (2, "arch_read_config: illegal config file (%s)\n", config_path);
exit (2);
}
rev = pika_save_unescape_iso8859_1_n (0, 0, pos, end - pos );
if (!is_non_upwards_relative_path (loc))
{
safe_printfmt (2, "arch_read_config: illegal config file (%s)\n", config_path);
exit (2);
}
rel_add_records (&answer, rel_make_record (loc, rev, 0), 0);
lim_free (0, loc);
lim_free (0, rev);
}
lim_free (0, line);
}
if (temp_file_created)
rmrf_file(tmp_file);
lim_free (0, tmp_file);
return answer;
}
rel_table
arch_config_from_tree (arch_project_tree_t * tree, rel_table config_in)
{
int here_fd;
int x;
rel_table answer = 0;
here_fd = safe_open (".", O_RDONLY, 0);
for (x = 0; x < rel_n_records (config_in); ++x)
{
t_uchar * subtree_path_spec = 0;
t_uchar * subtree_path = 0;
t_uchar * archive = 0;
t_uchar * version = 0;
arch_project_tree_t * subtree;
t_uchar * level = 0;
t_uchar * revision = 0;
t_uchar * fqr = 0;
subtree_path_spec = file_name_in_vicinity (0, tree->root, config_in[x][0]);
safe_chdir (subtree_path_spec);
subtree_path = safe_current_working_directory ();
safe_fchdir (here_fd);
subtree = arch_project_tree_new (NULL, subtree_path);
invariant (subtree->root && !str_cmp (subtree->root, subtree_path));
archive = arch_parse_package_name (arch_ret_archive, 0, config_in[x][1]);
if (arch_valid_package_name (config_in[x][1], arch_maybe_archive, arch_req_version, 1))
version = arch_parse_package_name (arch_ret_package_version, 0, config_in[x][1]);
else
{
t_uchar * package = 0;
package = arch_parse_package_name (arch_ret_package, 0, config_in[x][1]);
version = arch_latest_logged_version (subtree->root, archive, package);
lim_free (0, package);
}
level = arch_highest_patch_level (subtree->root, archive, version);
if (level)
{
revision = str_alloc_cat_many (0, version, "--", level, str_end);
fqr = arch_fully_qualify (archive, revision);
}
else
fqr = arch_fully_qualify (archive, version);
rel_add_records (&answer, rel_make_record (config_in[x][0], fqr, 0), 0);
lim_free (0, subtree_path_spec);
lim_free (0, subtree_path);
lim_free (0, archive);
lim_free (0, version);
arch_project_tree_delete (subtree);
lim_free (0, level);
lim_free (0, revision);
lim_free (0, fqr);
}
safe_close (here_fd);
return answer;
}
int
arch_begin_new_config (arch_project_tree_t * tree, t_uchar * name, int force)
{
int ign;
t_uchar * config_file = 0;
t_uchar * config_dir = 0;
t_uchar * name_tail = 0;
t_uchar * tmp_tail = 0;
t_uchar * config_tmp = 0;
int answer;
invariant (arch_valid_config_name (name));
config_file = arch_config_path (tree, name);
config_dir = file_name_directory_file (0, config_file);
name_tail = file_name_tail (0, name);
tmp_tail = str_alloc_cat (0, ",,", name_tail);
config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail);
if (!force && !safe_access (config_file, F_OK))
{
safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name);
exit (2);
}
ensure_directory_exists (config_dir);
vu_unlink (&ign, config_tmp);
answer = safe_open (config_tmp, O_WRONLY | O_CREAT | O_EXCL, 0666);
lim_free (0, config_file);
lim_free (0, config_dir);
lim_free (0, name_tail);
lim_free (0, tmp_tail);
lim_free (0, config_tmp);
return answer;
}
void
arch_finish_new_config (int fd, arch_project_tree_t * tree, t_uchar * name, int force)
{
t_uchar * config_file = 0;
t_uchar * config_dir = 0;
t_uchar * name_tail = 0;
t_uchar * tmp_tail = 0;
t_uchar * config_tmp = 0;
invariant (arch_valid_config_name (name));
config_file = arch_config_path (tree, name);
config_dir = file_name_directory_file (0, config_file);
name_tail = file_name_tail (0, name);
tmp_tail = str_alloc_cat (0, ",,", name_tail);
config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail);
safe_close (fd);
if (!force && !safe_access (config_file, F_OK))
{
safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name);
exit (2);
}
safe_rename (config_tmp, config_file);
lim_free (0, config_file);
lim_free (0, config_dir);
lim_free (0, name_tail);
lim_free (0, tmp_tail);
lim_free (0, config_tmp);
}
/**
* \brief get a fully qualified target dir path for a config
*/
t_uchar *
arch_config_output_path (t_uchar * tree_root, t_uchar * target_dir)
{
/* FIXME: (SECURITY) This allows one to write arbitrary paths from
* configs that may be on *remote* filesystems.
*/
if (tree_root)
return file_name_in_vicinity (0, tree_root, target_dir);
else
return arch_abs_path (target_dir);
}
void
arch_build_config (arch_project_tree_t * tree,
t_uchar * config_name,
struct arch_build_config_params * params,
t_uchar * default_archive)
{
rel_table config = 0;
int x;
if (tree->root)
config = arch_read_config (tree, config_name);
else
config = arch_read_config_simple (config_name);
/* Ensure a shallowist to deepest sort
*/
rel_sort_table_by_field (0, config, 0);
/* move conflicting dirs and files
*/
for (x = 0; x < rel_n_records (config); ++x)
{
int errn;
t_uchar * path = 0;
t_uchar * path_dir = 0;
t_uchar * path_tail = 0;
t_uchar * saved_tail = 0;
t_uchar * saved_path = 0;
path = arch_config_output_path (tree->root, config[x][0]);
path_dir = file_name_directory_file (0, path);
path_tail = file_name_tail (0, path);
saved_tail = str_alloc_cat (0, "++saved.", path_tail);
saved_path = talloc_tmp_file_name (talloc_context, path_dir, saved_tail);
if (vu_rename (&errn, path, saved_path) && (errn != ENOENT))
{
safe_printfmt (2, "build-config: unable to set aside conflicting directory %s\n", path);
exit (2);
}
lim_free (0, path);
lim_free (0, path_dir);
lim_free (0, path_tail);
lim_free (0, saved_tail);
talloc_free (saved_path);
}
/* build desired trees.
*/
for (x = 0; x < rel_n_records (config); ++x)
{
t_uchar * path_to_subtree = 0;
t_uchar * path_to_subtree_dir = 0;
t_uchar * revspec;
int status;
path_to_subtree = arch_config_output_path (tree->root, config[x][0]);
path_to_subtree_dir = file_name_directory_file (0, path_to_subtree);
ensure_directory_exists (path_to_subtree_dir);
revspec = config[x][1];
{
char ** argv = 0;
/* call `get' -- build an argv for it
*/
ar_push_char_star (&argv, "get");
if (default_archive)
{
ar_push_char_star (&argv, "-A");
ar_push_char_star (&argv, default_archive);
}
if (params->no_pristines)
{
ar_push_char_star (&argv, "--no-pristine");
}
if (params->hardlinks)
{
ar_push_char_star (&argv, "--link");
}
if (params->library)
{
ar_push_char_star (&argv, "--library");
}
if (params->sparse)
{
ar_push_char_star (&argv, "--sparse");
}
if (params->no_greedy_add)
{
ar_push_char_star (&argv, "--no-greedy-add");
}
ar_push_char_star (&argv, revspec);
ar_push_char_star (&argv, path_to_subtree);
ar_push_char_star (&argv, 0);
status = arch_cmd_get ("get", (ar_size_char_star (argv) - 1), argv);
ar_free_char_star (&argv);
}
if (status)
{
safe_printfmt (2, "unable to build %s at %s\n", revspec, path_to_subtree);
exit (status);
}
lim_free (0, path_to_subtree);
lim_free (0, path_to_subtree_dir);
}
if (tree->root && params->release_id)
{
int errn;
t_uchar * tree_version = 0;
t_uchar * tree_revision = 0;
t_uchar * release_id_file = 0;
rel_table snapped_config = 0;
int out_fd;
tree_version = arch_tree_version (tree->root);
if (tree_version)
{
t_uchar * archive = 0;
t_uchar * version = 0;
t_uchar * level = 0;
archive = arch_parse_package_name (arch_ret_archive, 0, tree_version);
version = arch_parse_package_name (arch_ret_non_archive, 0, tree_version);
level = arch_highest_patch_level (tree->root, archive, version);
tree_revision = str_alloc_cat_many (0, tree_version, "--", level, str_end);
lim_free (0, archive);
lim_free (0, version);
lim_free (0, level);
}
snapped_config = arch_config_from_tree (tree, config);
release_id_file = file_name_in_vicinity (0, tree->root, "=RELEASE-ID");
invariant (!vu_unlink (&errn, release_id_file) || (errn == ENOENT));
out_fd = safe_open (release_id_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
safe_printfmt (out_fd, "# automatically generated release id (by baz build-config)\n");
safe_printfmt (out_fd, "#\n");
safe_printfmt (out_fd, "\n");
safe_printfmt (out_fd, "%s(%s)\n", (tree_revision ? tree_revision : (t_uchar *)"<no tree version set>"), config_name);
safe_printfmt (out_fd, "\n");
rel_print_pika_escape_iso8859_1_table (out_fd, arch_escape_classes, snapped_config);
safe_close (out_fd);
lim_free (0, tree_version);
lim_free (0, tree_revision);
lim_free (0, release_id_file);
rel_free_table (snapped_config);
}
rel_free_table (config);
}
/* tag: Tom Lord Fri May 30 00:05:24 2003 (configs.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1