From 09964463e999b6ca5f09162ddf4c18cf70cdf21b Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Thu, 28 Sep 2017 13:49:09 -0500 Subject: [PATCH] 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 --- src/app/xml_editor/README | 14 +++-- src/app/xml_editor/component.cc | 88 ++++++++++++++++++++++++------ src/app/xml_term_edit/component.cc | 75 ++++++++++++++++++++----- 3 files changed, 143 insertions(+), 34 deletions(-) diff --git a/src/app/xml_editor/README b/src/app/xml_editor/README index 873de6e..ee09d99 100644 --- a/src/app/xml_editor/README +++ b/src/app/xml_editor/README @@ -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 ------------- ! +! <... name="..."/> +! + +Toggle action +------------- +! ! <... name="..."> ! ... ! -! +! diff --git a/src/app/xml_editor/component.cc b/src/app/xml_editor/component.cc index cfad04c..93d8977 100644 --- a/src/app/xml_editor/component.cc +++ b/src/app/xml_editor/component.cc @@ -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, "", 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(), 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); +} diff --git a/src/app/xml_term_edit/component.cc b/src/app/xml_term_edit/component.cc index a019cd5..0d8a8e0 100644 --- a/src/app/xml_term_edit/component.cc +++ b/src/app/xml_term_edit/component.cc @@ -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); +}