//
// aegis - project change supervisor
// Copyright (C) 2004-2009, 2011, 2012, 2014 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see
// .
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int
name_has_numeric_suffix(string_ty *name, string_ty **left, long *right)
{
char *ep;
ep = name->str_text + name->str_length;
while (ep > name->str_text && isdigit((unsigned char)ep[-1]))
--ep;
if (!*ep)
// still pointing to end of string
return 0;
if (ep < name->str_text + 2 || !ispunct((unsigned char)ep[-1]))
return 0;
*left = str_n_from_c(name->str_text, ep - 1 - name->str_text);
*right = magic_zero_encode(atol(ep));
return 1;
}
void
project::bind_existing(void)
{
string_ty *s;
string_ty *parent_name;
int alias_retry_count;
//
// make sure project exists
//
trace(("project::bind_existing(this = %p)\n{\n", this));
assert(!home_path);
alias_retry_count = 0;
alias_retry:
s = gonzo_project_home_path_from_name(name);
//
// Look to see if there is an alias.
//
if (!s)
{
s = gonzo_alias_to_actual(name);
if (s)
{
str_free(name);
name = str_copy(s);
s = gonzo_project_home_path_from_name(name);
}
}
//
// If the named project was not found, and it has a numeric suffix,
// then assume that it is a project.branch combination,
// and try to find the deeper project.
//
if (!s && name_has_numeric_suffix(name, &parent_name, &parent_bn))
{
trace(("mark\n"));
project *ppp = project_alloc(parent_name);
ppp->bind_existing();
int err = project_is_readable(ppp);
if (err != 0)
{
off_limits = true;
ppp->free();
ppp = 0;
}
else
{
parent = ppp;
ppp = 0;
pcp = change::create(parent, parent_bn);
pcp->bind_existing();
if (!pcp->was_a_branch())
change_fatal(pcp, 0, i18n("not a branch"));
//
// rebuild the project name
// ...eventually, use the remembered punctuation
//
str_free(name);
name =
str_format
(
"%s.%ld",
project_name_get(parent).c_str(),
magic_zero_decode(parent_bn)
);
changes_path =
str_format
(
"%s.branch",
parent->change_path_get(parent_bn).c_str()
);
//
// This project's user will be the same as the parent's user.
//
up = project_user(parent);
uid = up->get_uid();
gid = up->get_gid();
return;
}
}
//
// If the name was not found, try to find an alias match for it.
// Loop if an alias is found.
//
if (!s)
{
string_ty *other;
other = gonzo_alias_to_actual(name);
if (other)
{
if (++alias_retry_count > 5)
{
project_fatal(this, 0, i18n("alias loop detected"));
}
str_free(name);
name = str_copy(other);
goto alias_retry;
}
}
//
// If the name was not found, try to find a fuzzy match for it.
// In general, this results in more informative error messages.
//
if (!s)
{
string_list_ty wl;
string_ty *best;
double best_weight;
size_t j;
gonzo_project_list(&wl);
best = 0;
best_weight = 0.6;
for (j = 0; j < wl.size(); ++j)
{
double w;
s = wl[j];
w = fstrcmp(name->str_text, s->str_text);
if (w > best_weight)
{
best = s;
best_weight = w;
}
}
if (best)
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_string(scp, "Name", name);
sub_var_set_string(scp, "Guess", best);
fatal_intl(scp, i18n("no $name project, closest is $guess"));
}
else
{
sub_context_ty *scp;
scp = sub_context_new();
sub_var_set_string(scp, "Name", name);
fatal_intl(scp, i18n("no $name project"));
// NOTREACHED
sub_context_delete(scp);
}
}
//
// To cope with automounters, directories are stored as given,
// or are derived from the home directory in the passwd file.
// Within aegis, pathnames have their symbolic links resolved,
// and any comparison of paths is done on this "system idea"
// of the pathname.
//
home_path = str_copy(s);
//
// Create the project user from details of the project path.
//
get_the_owner();
up = user_ty::create(uid, gid);
//
// set the umask from the project state data.
//
up->umask_set(umask_get());
trace(("}\n"));
}
bool
project::bind_existing_errok(void)
{
string_ty *s;
string_ty *parent_name;
int alias_retry_count;
//
// make sure project exists
//
trace(("project::bind_existing_errok(this = %p)\n{\n", this));
assert(!home_path);
alias_retry_count = 0;
alias_retry:
s = gonzo_project_home_path_from_name(name);
if (s)
{
nstring root(s);
nstring info_state_path = root + "/info/state";
os_become_orig();
int err = os_readable(info_state_path);
os_become_undo();
if (err != 0)
return false;
}
//
// If the named project was not found, and it has a numeric suffix,
// then assume that it is a project.branch combination,
// and try to find the deeper project.
//
if (!s && name_has_numeric_suffix(name, &parent_name, &parent_bn))
{
parent = project_alloc(parent_name);
if (!parent->bind_existing_errok())
{
parent->free();
trace(("return false;\n"));
trace(("}\n"));
return false;
}
pcp = change::create(parent, parent_bn);
if (!pcp->bind_existing_errok())
{
pcp.reset();
parent->free();
trace(("return false;\n"));
trace(("}\n"));
return false;
}
if (!pcp->was_a_branch())
{
pcp.reset();
parent->free();
trace(("return false;\n"));
trace(("}\n"));
return false;
}
//
// rebuild the project name
// ...eventually, use the remembered punctuation
//
str_free(name);
name =
str_format
(
"%s.%ld",
project_name_get(parent).c_str(),
magic_zero_decode(parent_bn)
);
changes_path =
str_format
(
"%s.branch",
parent->change_path_get(parent_bn).c_str()
);
//
// This project's user will be the same as the parent's user.
//
up = project_user(parent);
uid = up->get_uid();
gid = up->get_gid();
trace(("return true;\n"));
trace(("}\n"));
return true;
}
//
// If the name was not found, try to find an alias match for it.
// Loop if an alias is found.
//
if (!s)
{
string_ty *other;
other = gonzo_alias_to_actual(name);
if (other)
{
if (++alias_retry_count > 5)
{
trace(("return false;\n"));
trace(("}\n"));
return false;
}
str_free(name);
name = str_copy(other);
goto alias_retry;
}
}
//
// If the name was not found, try to find a fuzzy match for it.
// In general, this results in more informative error messages.
//
if (!s)
{
trace(("return false;\n"));
trace(("}\n"));
return false;
}
//
// To cope with automounters, directories are stored as given,
// or are derived from the home directory in the passwd file.
// Within aegis, pathnames have their symbolic links resolved,
// and any comparison of paths is done on this "system idea"
// of the pathname.
//
home_path = str_copy(s);
//
// Create the project user from the details of the project directory.
//
get_the_owner();
up = user_ty::create(uid, gid);
//
// set the umask from the project state data.
//
up->umask_set(umask_get());
trace(("return true\n"));
trace(("}\n"));
return true;
}
// vim: set ts=8 sw=4 et :