xml_editor: VFS API update, 'toggle' action
Add an action at the xml_editor to toggle the presence of an XML node. Edit clients have no explicit knowledge of what nodes are present in the file being edited so a 'toggle' feature is best implemented server side. Ref #86
This commit is contained in:
committed by
Norman Feske
parent
6263202681
commit
09964463e9
@@ -2,11 +2,11 @@ This component edits a single XML file as instructed by clients via
|
||||
Report sessions. It is designed to allow insertion and removal of XML
|
||||
nodes without revealing the content the file being edited.
|
||||
|
||||
The two edit actions are _add_ and _remove_, both operate over XML
|
||||
The three edit actions are _add_, _remove_, and toggle, all operate over XML
|
||||
nodes under the parent node in the file being edited. The content of
|
||||
edits are verified for corrent syntax and XML node type and _name_
|
||||
attribute are used to prevent the same node from being inserted
|
||||
twice or to find nodes to remove.
|
||||
attribute is used to prevent the same node from being inserted
|
||||
twice and to find nodes to remove or toggle.
|
||||
|
||||
Add action
|
||||
----------
|
||||
@@ -19,7 +19,13 @@ Add action
|
||||
Remove action
|
||||
-------------
|
||||
! <remove>
|
||||
! <... name="..."/>
|
||||
! </remove>
|
||||
|
||||
Toggle action
|
||||
-------------
|
||||
! <toggle>
|
||||
! <... name="...">
|
||||
! ...
|
||||
! </...>
|
||||
! </remove>
|
||||
! </toggle>
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace Xml_editor {
|
||||
|
||||
#define REVISION_ATTR_NAME "edit_rev"
|
||||
|
||||
Genode::Env *_env;
|
||||
|
||||
struct Xml_editor::Xml_file
|
||||
{
|
||||
Genode::Allocator &alloc;
|
||||
@@ -95,8 +97,19 @@ struct Xml_editor::Xml_file
|
||||
/**
|
||||
* Flush file changes
|
||||
*/
|
||||
void sync() {
|
||||
vfs_handle.ds().sync(path.base()); }
|
||||
void sync()
|
||||
{
|
||||
while (true) {
|
||||
if (vfs_handle.fs().queue_sync(&vfs_handle))
|
||||
break;
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
while (true) {
|
||||
if (vfs_handle.fs().complete_sync(&vfs_handle) != Vfs::File_io_service::SYNC_QUEUED)
|
||||
break;
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void read_file()
|
||||
{
|
||||
@@ -107,25 +120,34 @@ struct Xml_editor::Xml_file
|
||||
file_size const total = sb.size;
|
||||
|
||||
Buffer &next = next_buffer(total ? total : 4096);
|
||||
buffer = &next;
|
||||
|
||||
if (total == 0) {
|
||||
strncpy(next.ptr, "<config/>", next.size);
|
||||
} else {
|
||||
/*
|
||||
* Read in one pass, reading in multiple passes is
|
||||
* too complicated and error prone
|
||||
*/
|
||||
file_size out = 0;
|
||||
while (!vfs_handle.fs().queue_read(&vfs_handle, total))
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
|
||||
file_size offset = 0;
|
||||
while (offset < total) {
|
||||
vfs_handle.seek(offset);
|
||||
file_size out = 0;
|
||||
vfs_handle.fs().read(
|
||||
&vfs_handle,
|
||||
next.ptr + offset,
|
||||
total - offset,
|
||||
out);
|
||||
offset += out;
|
||||
for (;;) {
|
||||
auto r = vfs_handle.fs().complete_read(
|
||||
&vfs_handle, next.ptr, total, out);
|
||||
switch (r) {
|
||||
case Vfs::File_io_service::Read_result::READ_QUEUED:
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
break;
|
||||
case Vfs::File_io_service::Read_result::READ_OK:
|
||||
return;
|
||||
default:
|
||||
Genode::error("failed to read XML file");
|
||||
throw r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer = &next;
|
||||
}
|
||||
|
||||
void write_file(Vfs::file_size length)
|
||||
@@ -273,6 +295,24 @@ struct Xml_editor::Xml_file
|
||||
|
||||
return gen.used();
|
||||
}
|
||||
|
||||
size_t toggle(Xml_node const &node)
|
||||
{
|
||||
Name const toggle_name = node.attribute_value("name", Name());
|
||||
Xml_node const current = xml();
|
||||
bool name_present = false;
|
||||
|
||||
current.for_each_sub_node([&] (Xml_node const &other) {
|
||||
if (!name_present &&
|
||||
other.type() == node.type() &&
|
||||
toggle_name == other.attribute_value("name", Name()))
|
||||
{
|
||||
name_present = true;
|
||||
}
|
||||
});
|
||||
|
||||
return name_present ? remove(node) : add(node);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -323,9 +363,18 @@ struct Xml_editor::Report_session_component : Genode::Rpc_object<Report::Session
|
||||
});
|
||||
};
|
||||
|
||||
auto toggle_fn = [&] (Xml_node const &action) {
|
||||
action.for_each_sub_node([&] (Xml_node const &subnode) {
|
||||
if (verbose)
|
||||
log("'", label, "' toggles '", subnode.attribute_value("name", Name()), "'");
|
||||
content_size = xml_file.toggle(subnode);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
Xml_node edit_node(ram_ds.local_addr<char const>(), length);
|
||||
|
||||
edit_node.for_each_sub_node("toggle", toggle_fn);
|
||||
edit_node.for_each_sub_node("remove", remove_fn);
|
||||
edit_node.for_each_sub_node("add", add_fn);
|
||||
if (content_size) {
|
||||
@@ -515,6 +564,10 @@ struct Xml_editor::Main
|
||||
die("OPEN_ERR_NAME_TOO_LONG"); break;
|
||||
case Open_result::OPEN_ERR_NO_SPACE:
|
||||
die("OPEN_ERR_NO_SPACE"); break;
|
||||
case Open_result::OPEN_ERR_OUT_OF_RAM:
|
||||
die("OPEN_ERR_OUT_OF_RAM"); break;
|
||||
case Open_result::OPEN_ERR_OUT_OF_CAPS:
|
||||
die("OPEN_ERR_OUT_OF_CAPS"); break;
|
||||
}
|
||||
throw ~0;
|
||||
}
|
||||
@@ -532,5 +585,8 @@ struct Xml_editor::Main
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) {
|
||||
static Xml_editor::Main inst(env); }
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
_env = &env;
|
||||
static Xml_editor::Main inst(env);
|
||||
}
|
||||
|
||||
@@ -36,12 +36,14 @@ namespace Xml_term_edit {
|
||||
struct Main;
|
||||
}
|
||||
|
||||
Genode::Env *_env;
|
||||
|
||||
struct Xml_term_edit::Command : Cli_monitor::Command
|
||||
{
|
||||
Vfs::Dir_file_system &vfs;
|
||||
Genode::Allocator &alloc;
|
||||
Reporter &report;
|
||||
Vfs::Vfs_handle *root_handle = nullptr;
|
||||
|
||||
Command(char const *name,
|
||||
char const *desc,
|
||||
@@ -53,23 +55,65 @@ struct Xml_term_edit::Command : Cli_monitor::Command
|
||||
Cli_monitor::Command(name, desc),
|
||||
vfs(vfs), alloc(alloc), report(report)
|
||||
{
|
||||
auto r = vfs.open(
|
||||
"/", Vfs::Directory_service::OPEN_MODE_RDONLY, &root_handle, alloc);
|
||||
if (r != Vfs::Directory_service::Open_result::OPEN_OK) {
|
||||
Genode::error("failed to open VFS root directory");
|
||||
throw r;
|
||||
}
|
||||
|
||||
cmds.insert(this);
|
||||
}
|
||||
|
||||
void _for_each_argument(Argument_fn const &fn) const override
|
||||
{
|
||||
Vfs::Directory_service::Dirent de;
|
||||
for (Vfs::file_offset i = 0; ; ++i) {
|
||||
if (vfs.dirent("/", i, de) != Vfs::Directory_service::DIRENT_OK)
|
||||
break;
|
||||
switch (de.type) {
|
||||
case Vfs::Directory_service::DIRENT_TYPE_FILE:
|
||||
fn(Argument(de.name, ""));
|
||||
break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_END:
|
||||
typedef Vfs::File_io_service::Read_result Result;
|
||||
|
||||
enum { DIRENT_COUNT = 4096 / sizeof(Vfs::Directory_service::Dirent) };
|
||||
Vfs::Directory_service::Dirent dirents[DIRENT_COUNT];
|
||||
memset(dirents, 0x00, sizeof(dirents));
|
||||
|
||||
root_handle->seek(0);
|
||||
|
||||
for (;;) {
|
||||
while (!vfs.queue_read(root_handle, sizeof(dirents))) {
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
Vfs::file_size read_count = 0;
|
||||
Result r;
|
||||
for (;;) {
|
||||
r = vfs.complete_read(
|
||||
root_handle, (char*)&dirents, sizeof(dirents), read_count);
|
||||
if (r == Result::READ_QUEUED) {
|
||||
_env->ep().wait_and_dispatch_one_io_signal();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (r != Result::READ_OK) {
|
||||
Genode::error("failed to read subsystems");
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_count == 0) return;
|
||||
root_handle->advance_seek(read_count);
|
||||
read_count = read_count / DIRENT_COUNT;
|
||||
|
||||
for (unsigned i = 0; i < read_count; i++) {
|
||||
Vfs::Directory_service::Dirent const &e = dirents[i];
|
||||
|
||||
switch (e.type) {
|
||||
case Vfs::Directory_service::DIRENT_TYPE_FILE:
|
||||
/* check if the VFS returned junk */
|
||||
if (e.name[0] != '\0')
|
||||
fn(Argument(e.name, ""));
|
||||
break;
|
||||
case Vfs::Directory_service::DIRENT_TYPE_END:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +161,7 @@ struct Xml_term_edit::Command : Cli_monitor::Command
|
||||
while (offset < sb.size) {
|
||||
file_size n = 0;
|
||||
file_size count = min(sizeof(buf), sb.size-offset);
|
||||
handle->fs().read(handle, buf, count, n);
|
||||
handle->fs().complete_read(handle, buf, count, n);
|
||||
if (!n)
|
||||
return;
|
||||
gen.append(buf, n);
|
||||
@@ -262,5 +306,8 @@ void Xml_term_edit::Main::handle_term()
|
||||
}
|
||||
}
|
||||
|
||||
void Component::construct(Genode::Env &env) {
|
||||
static Xml_term_edit::Main inst(env); }
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
_env = &env;
|
||||
static Xml_term_edit::Main inst(env);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user