#
# add_file "tests/t_add_executable.at"
#
# add_file "tests/t_attr_init.at"
#
# patch "ChangeLog"
# from [683d5c57faf919307e31781f4f27ef9fdaf70fed]
# to [9379fec8957439a7cfb2e2a78658886cefe29d3f]
#
# patch "lua.cc"
# from [4e29481967fff85b5399cc318e535b48ec4f553a]
# to [ee8e38e41928d17b8903309f71265e859443c43e]
#
# patch "lua.hh"
# from [7e62c232b0a1e71e509ac1834170507bd776507f]
# to [9e6604ebd9285f905820bd95e92fb68c402807ce]
#
# patch "monotone.texi"
# from [4dd98a43af45c817331acf63a1181ac501ca0527]
# to [66422aaa133c5bdab836f44a592cee17421004aa]
#
# patch "platform.hh"
# from [6a6fb903facbfdcce8d50db7a2b3ac8f84fa26d7]
# to [1f01b1dee6b2c96235160d63197f92f740790f8d]
#
# patch "std_hooks.lua"
# from [a09034cf15e000407c7fdbea620de03f79f8a73e]
# to [245cf5ff0a0858e6436cc1d44bbba982f0afbc6f]
#
# patch "tests/t_add_executable.at"
# from []
# to [070973ab2e1129c20c04c48679ca5cd9e4a417aa]
#
# patch "tests/t_attr_init.at"
# from []
# to [2808c8a9f6fb506e726f6fd7c6daab46946cca50]
#
# patch "testsuite.at"
# from [4574965e47126b91ab46a390639effd6d2917e92]
# to [9caba90d174e7b550907e836847f65dca5ee502e]
#
# patch "unix/process.cc"
# from [2db9de39676d033ebea840ba0ae8bc437b1789c4]
# to [3dfbca0b6549d1b91d19960ed704d6e4ed8ea042]
#
# patch "win32/process.cc"
# from [f20f385828115ec65d4565e80d58822335c80bc2]
# to [8290190317b7b74e281eaa749b50393c32928928]
#
# patch "work.cc"
# from [dd1b1800d78f63c9f5615622d7c638c01c2f7459]
# to [1b1687d4db3d2daf7748b642d52c5fa844c251a9]
#
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,27 @@
+2005-04-20 Nathaniel Smith
+
+ Most of this patch from Joel Reed, with only small tweaks myself.
+
+ * AUTHORS: Add Joel Reed.
+
+ * platform.hh (is_executable): New function.
+ * {unix,win32}/process.cc: Define it.
+
+ * lua.cc (monotone_is_executable_for_lua): New function.
+ (lua_hooks): Register it.
+ (Lua::push_nil): New method.
+ (lua_hooks::hook_init_attributes): New hook.
+ * lua.hh: Declare it.
+ * monotone.texi (Hook Reference): Document it.
+
+ * work.cc (addition_builder): Call new hook, collect attributes
+ for added files.
+ (build_additions): Set attributes on new files.
+
+ * tests/t_attr_init.at: New test.
+ * tests/t_add_executable.at: New test.
+ * testsuite.at: Add them.
+
2005-04-19 Nathaniel Smith
* file_io.cc (read_localized_data, write_localized_data): Remove
--- lua.cc
+++ lua.cc
@@ -89,6 +89,14 @@
}
static int
+ monotone_is_executable_for_lua(lua_State *L)
+ {
+ const char *path = lua_tostring(L, -1);
+ lua_pushboolean(L, is_executable(path));
+ return 1;
+ }
+
+ static int
monotone_make_executable_for_lua(lua_State *L)
{
const char *path = lua_tostring(L, -1);
@@ -168,6 +176,7 @@
// add monotone-specific functions
lua_register(st, "mkstemp", monotone_mkstemp_for_lua);
lua_register(st, "existsonpath", monotone_existsonpath_for_lua);
+ lua_register(st, "is_executable", monotone_is_executable_for_lua);
lua_register(st, "make_executable", monotone_make_executable_for_lua);
lua_register(st, "spawn", monotone_spawn_for_lua);
lua_register(st, "wait", monotone_wait_for_lua);
@@ -412,6 +421,14 @@
return *this;
}
+ Lua & push_nil()
+ {
+ if (failed) return *this;
+ I(lua_checkstack (st, 1));
+ lua_pushnil(st);
+ return *this;
+ }
+
Lua & push_table()
{
if (failed) return *this;
@@ -918,7 +935,39 @@
return exec_ok && permitted;
}
+bool
+lua_hooks::hook_init_attributes(file_path const & filename,
+ std::map & attrs)
+{
+ Lua ll(st);
+ ll
+ .push_str("attr_init_functions")
+ .get_tab()
+ .push_nil();
+
+ while (ll.next())
+ {
+ ll.push_str(filename());
+ ll.call(1, 1);
+
+ if (lua_isstring(st, -1))
+ {
+ string key, value;
+
+ ll.extract_str(value);
+ ll.pop();
+ ll.extract_str(key);
+
+ attrs[key] = value;
+ }
+ else
+ ll.pop();
+ }
+
+ return ll.pop().ok();
+}
+
bool
lua_hooks::hook_apply_attribute(string const & attr,
file_path const & filename,
--- lua.hh
+++ lua.hh
@@ -95,6 +95,8 @@
file_path & res);
// attribute hooks
+ bool hook_init_attributes(file_path const & filename,
+ std::map & attrs);
bool hook_apply_attribute(std::string const & attr,
file_path const & filename,
std::string const & value);
--- monotone.texi
+++ monotone.texi
@@ -5089,7 +5089,32 @@
attr_functions["execute"] =
function(filename, value)
if (value == "true") then
- os.execute(string.format("chmod +x %s", filename))
+ make_executable(filename)
+ end
+ end
address@hidden group
address@hidden smallexample
+
address@hidden attr_init_functions address@hidden (@var{filename})
+
+This is not a hook function, but a @emph{table} of hook
+functions. Each entry in the table @code{attr_init_functions}, at table
+entry @var{attribute}, is a function taking a file name
address@hidden Each function defines the attributes that should be
+stored in @file{.mt-attrs} for the given @var{filename}. This table of
+hook functions is called once for each file during an @dfn{add}.
+
+By default, there is only one entry in this table, for the @code{execute}
+attribute. Its definition is:
+
address@hidden
address@hidden
+attr_init_functions["execute"] =
+ function(filename)
+ if (is_executable(filename)) then
+ return "true"
+ else
+ return nil
end
end
@end group
--- platform.hh
+++ platform.hh
@@ -15,6 +15,7 @@
void read_password(std::string const & prompt, char * buf, size_t bufsz);
void get_system_flavour(std::string & ident);
+bool is_executable(const char *path);
// For LUA
int existsonpath(const char *exe);
--- std_hooks.lua
+++ std_hooks.lua
@@ -28,6 +28,19 @@
-- manifest). each (f,k,v) triple in an atribute file turns into a
-- call to attr_functions[k](f,v) in lua.
+if (attr_init_functions == nil) then
+ attr_init_functions = {}
+end
+
+attr_init_functions["execute"] =
+ function(filename)
+ if (is_executable(filename)) then
+ return "true"
+ else
+ return nil
+ end
+ end
+
if (attr_functions == nil) then
attr_functions = {}
end
--- tests/t_add_executable.at
+++ tests/t_add_executable.at
@@ -0,0 +1,17 @@
+AT_SETUP([add executable])
+MONOTONE_SETUP
+
+NOT_ON_WIN32
+
+AT_DATA(foo, [blah blah
+])
+AT_CHECK(chmod 755 foo)
+# Have to use RAW_MONOTONE, because we're testing the standard hooks...
+AT_CHECK(RAW_MONOTONE --rcfile=test_hooks.lua add foo, [], [ignore], [ignore])
+COMMIT(testbranch)
+
+# Have to use RAW_MONOTONE, because we're testing the standard hooks...
+AT_CHECK(RAW_MONOTONE --rcfile=test_hooks.lua checkout --branch=testbranch codir, [], [ignore], [ignore])
+AT_CHECK(test -x codir/foo)
+
+AT_CLEANUP
--- tests/t_attr_init.at
+++ tests/t_attr_init.at
@@ -0,0 +1,33 @@
+AT_SETUP([attr init functions])
+MONOTONE_SETUP
+
+AT_DATA(hook.lua, [
+if (attr_init_functions == nil) then
+ attr_init_functions = {}
+end
+attr_init_functions[["test_attr"]] =
+ function(filename)
+ if filename == "magic" then
+ return "bob"
+ else
+ return nil
+ end
+ end
+])
+
+AT_DATA(testfile, [foo
+])
+AT_CHECK(MONOTONE --rcfile=hook.lua add testfile, [], [ignore], [ignore])
+AT_DATA(magic, [stuff
+])
+AT_CHECK(MONOTONE --rcfile=hook.lua add magic, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE attr get testfile, [], [stdout], [ignore])
+AT_CHECK(grep -q test_attr stdout, [1])
+AT_CHECK(grep -q bob stdout, [1])
+
+AT_CHECK(MONOTONE attr get magic, [], [stdout], [ignore])
+AT_CHECK(grep -q test_attr stdout, [])
+AT_CHECK(grep -q bob stdout, [])
+
+AT_CLEANUP
--- testsuite.at
+++ testsuite.at
@@ -570,3 +570,5 @@
m4_include(tests/t_drop_attr.at)
m4_include(tests/t_attr_drop.at)
m4_include(tests/t_log_depth_single.at)
+m4_include(tests/t_attr_init.at)
+m4_include(tests/t_add_executable.at)
--- unix/process.cc
+++ unix/process.cc
@@ -29,6 +29,16 @@
return -1;
}
+bool is_executable(const char *path)
+{
+ struct stat s;
+
+ int rc = stat(path, &s);
+ N(rc != -1, F("stat() error on file %s)") % path);
+
+ return s.st_mode & S_IXUSR;
+}
+
int make_executable(const char *path)
{
mode_t mode;
--- win32/process.cc
+++ win32/process.cc
@@ -17,6 +17,11 @@
return 0;
}
+bool is_executable(const char *path)
+{
+ return false; /* Basically meaningless on win32 */
+}
+
int make_executable(const char *path)
{
return 0; /* Basically meaningless on win32 */
--- work.cc
+++ work.cc
@@ -29,11 +29,13 @@
app_state & app;
change_set::path_rearrangement & pr;
path_set ps;
+ attr_map & am_attrs;
public:
addition_builder(app_state & a,
change_set::path_rearrangement & pr,
- path_set & p)
- : app(a), pr(pr), ps(p)
+ path_set & p,
+ attr_map & am)
+ : app(a), pr(pr), ps(p), am_attrs(am)
{}
virtual void visit_file(file_path const & path);
};
@@ -56,6 +58,11 @@
P(F("adding %s to working copy add set\n") % path);
ps.insert(path);
pr.added_files.insert(path);
+
+ map attrs;
+ app.lua.hook_init_attributes(path, attrs);
+ if (attrs.size() > 0)
+ am_attrs[path] = attrs;
}
void
@@ -68,10 +75,11 @@
change_set cs_new;
path_set ps;
+ attr_map am_attrs;
extract_path_set(man, ps);
apply_path_rearrangement(pr, ps);
- addition_builder build(app, pr_new, ps);
+ addition_builder build(app, pr_new, ps, am_attrs);
for (vector::const_iterator i = paths.begin(); i != paths.end(); ++i)
{
@@ -82,6 +90,48 @@
walk_tree(*i, build);
}
+ if (am_attrs.size () > 0)
+ {
+ // add .mt-attrs to manifest if not already registered
+ file_path attr_path;
+ get_attr_path(attr_path);
+
+ if ((man.find(attr_path) == man.end ()) &&
+ (pr_new.added_files.find(attr_path) == pr_new.added_files.end()))
+ {
+ P(F("registering %s file in working copy\n") % attr_path);
+ pr.added_files.insert(attr_path);
+ }
+
+ // read attribute map if available
+ data attr_data;
+ attr_map attrs;
+
+ if (file_exists(attr_path))
+ {
+ read_data(attr_path, attr_data);
+ read_attr_map(attr_data, attrs);
+ }
+
+ // add new attribute entries
+ for (attr_map::const_iterator i = am_attrs.begin();
+ i != am_attrs.end(); ++i)
+ {
+ map m = i->second;
+
+ for (map::const_iterator j = m.begin();
+ j != m.end(); ++j)
+ {
+ P(F("adding attribute '%s' to file %s to .mt_attrs\n") % j->first % i->first);
+ attrs[i->first][j->first] = j->second;
+ }
+ }
+
+ // write out updated map
+ write_attr_map(attr_data, attrs);
+ write_data(attr_path, attr_data);
+ }
+
normalize_path_rearrangement(pr_new);
concatenate_rearrangements(pr, pr_new, pr_concatenated);
pr = pr_concatenated;