This example implements search and replace in a file (specifically an Xresources
formatted file).
bundle agent __main__
{
methods:
"Test data"
usebundle => init_test_file( $(example.file) );
"example";
}
bundle agent example
{
vars:
"file" string => "/tmp/t.conf";
"a" data => '{
"*font": "DejaVu Sans Mono-11-REPLACED",
"*background": "#2e3436"
}';
files:
"$(file)"
edit_line => Xresources_replace_resource_value("*mode", "blank");
"$(file)"
edit_line => Xresources_replace_map( "@(a)" );
}
bundle agent init_test_file(file)
{
files:
"$(file)"
content => "*mode:$(const.t)$(const.t)$(const.t)random
*font:$(const.t)$(const.t)$(const.t)DejaVu Sans Mono-11
*background:$(const.t)$(const.t)$(const.t)#2e3436";
}
bundle edit_line Xresources_replace_resource_value( resource, value )
{
vars:
"e_resource"
string => escape( $(resource) ),
comment => "Xresources resources may contain asterisks for wildcard matching it must be escaped.";
replace_patterns:
# I think this is a bug. $(match.1) doesn't seem to faithfully reproduce the text from the first capture group.
# In the test file the 3 tabs in the first capture group matched by \s+ are replaced by a single tab.
"(^$(e_resource):\s+)((?!$(value)$).*$)"
replace_with => value( "$(match.1)$(value)" );
# Works if there are 3 tabs
#"(^$(e_resource):\t\t\t)((?!$(value)$).*$)"
# replace_with => value( "$(match.1)$(value)" );
}
bundle edit_line Xresources_replace_map( map )
{
vars:
"resources" slist => getindices( map );
replace_patterns:
# I think this is a bug. $(match.1) doesn't seem to faithfully reproduce the text from the first capture group.
# In the test file the 3 tabs in the first capture group matched by \s+ are replaced by a single tab.
"(^$(with):\s+)((?!$(map[$(resources)])$).*$)"
with => escape( $(resources) ),
replace_with => value( "$(match.1)$(map[$(resources)])" );
#"(^$(with):\s+)((?!$(map[$(resources)])$).*$)"
# with => escape( $(resources) ),
# replace_with => value( "$(match.1)$(map[$(resources)])" );
# In the test file the 3 tabs in the first capture group matched by \t\t\t are retained
# While this faithfully reproduces the whitespace, it would not be robust to any deviation in the ammount of whitespace
# But it works correctly if there are 3 tabs
# "(^$(with):\t\t\t)((?!$(map[$(resources)])$).*$)"
# with => escape( $(resources) ),
# replace_with => value( "$(match.1)$(map[$(resources)])" );
}
Here is the state of the /tmp/t.conf
file in the above policy before and after running the policy.
cat -T /tmp/t.conf
*mode:^I^I^Irandom *font:^I^I^IDejaVu Sans Mono-11 *background:^I^I^I#2e3436
After:
cat -T /tmp/t.conf
*mode:^Iblank *font:^IDejaVu Sans Mono-11-REPLACED *background:^I#2e3436
Note the current implementation resulted in the original three tabs being replaced with a single, which I think is a bug. Hopefully I or somoene else remembers to log a ticket.