# # 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;