Remove XML editor

This commit is contained in:
Emery Hemingway
2019-02-22 16:00:45 +01:00
committed by Norman Feske
parent 9fe7643285
commit 1de91edfe8
15 changed files with 0 additions and 1165 deletions

View File

@@ -1,2 +0,0 @@
SRC_DIR := src/app/xml_editor
include $(GENODE_DIR)/repos/base/recipes/src/content.inc

View File

@@ -1 +0,0 @@
2017-06-08 29beffb8c1589ab3398db5b17f432e32ade2c0f2

View File

@@ -1,8 +0,0 @@
base
block_session
file_system_session
os
report_session
rtc_session
terminal_session
vfs

View File

@@ -1,14 +0,0 @@
SRC_DIR := src/app/xml_term_edit
content: $(SRC_DIR) LICENSE
$(SRC_DIR):
mkdir -p $@
cp -r $(REP_DIR)/$@/* $@/
cp \
$(GENODE_DIR)/repos/os/src/app/cli_monitor/command_line.h \
$(GENODE_DIR)/repos/os/src/app/cli_monitor/line_editor.h \
$@/
LICENSE:
cp $(GENODE_DIR)/LICENSE $@

View File

@@ -1 +0,0 @@
2017-06-08 1c40e2580a6a1abf3c72a9272cb3d9d7a1eab81d

View File

@@ -1,8 +0,0 @@
base
block_session
file_system_session
os
report_session
rtc_session
terminal_session
vfs

View File

@@ -1,163 +0,0 @@
create_boot_directory
import_from_depot \
genodelabs/src/[base_src] \
genodelabs/pkg/[drivers_interactive_pkg] \
ehmry/src/xml_editor \
ehmry/src/xml_term_edit \
set build_components {
core init
timer
server/fs_rom
server/terminal
server/ram_fs
test/log
test/timer
}
build $build_components
append config {
<config>
<default caps="256"/>
<parent-provides>
<service name="CPU"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="IRQ"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RM"/>
<service name="ROM"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="drivers" caps="4096">
<resource name="RAM" quantum="32M"/>
<binary name="init"/>
<route>
<service name="ROM" label="config"> <parent label="drivers.config"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<any-service> <parent/> </any-service>
</route>
<provides>
<service name="Input"/> <service name="Framebuffer"/>
</provides>
</start>
<start name="ram_fs">
<resource name="RAM" quantum="4M"/>
<provides><service name="File_system"/></provides>
<config>
<policy label_prefix="xml_editor" root="/" writeable="yes"/>
<policy label_prefix="fs_rom" root="/" writeable="no"/>
<content>
<inline name="init.config">
<config>
<default caps="128"/>
<parent-provides>
<service name="CPU"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RM"/>
<service name="ROM"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> </any-service>
</default-route>
</config>
</inline>
</content>
</config>
</start>
<start name="fs_rom">
<resource name="RAM" quantum="2M"/>
<provides><service name="ROM"/></provides>
</start>
<start name="xml_editor">
<resource name="RAM" quantum="4M"/>
<provides> <service name="Report"/> </provides>
<config output="init.config">
<vfs> <fs/> </vfs>
</config>
<route>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="init">
<resource name="RAM" quantum="4M"/>
<configfile name="init.config"/>
<route>
<service name="ROM" label="init.config"> <child name="fs_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="terminal">
<resource name="RAM" quantum="2M"/>
<provides><service name="Terminal"/></provides>
<config>
<keyboard layout="none"/>
</config>
<route>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="xml_term_edit">
<resource name="RAM" quantum="4M"/>
<config>
<vfs>
<inline name="test-log.subsystem">
<start name="test-log">
<resource name="RAM" quantum="2M"/>
</start>
</inline>
<inline name="test-timer.subsystem">
<start name="test-timer">
<resource name="RAM" quantum="2M"/>
</start>
</inline>
<inline name="junk">
<start name="junk"/>
</inline>
</vfs>
</config>
<route>
<service name="Report"> <child name="xml_editor"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
</config>
}
install_config $config
#
# Boot modules
#
# generic modules
append boot_modules {
core ld.lib.so init
fs_rom
libc.lib.so vfs.lib.so
libm.lib.so
ram_fs
terminal
test-log
test-timer
timer
}
build_boot_image $boot_modules
run_genode_until forever

View File

@@ -1,67 +0,0 @@
#
# \brief Hotkey XML editor
# \author Emery Hemingway
# \date 2017-09-28
#
#
# Copyright (C) 2017 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU General Public License version 2.
#
import streams, tables, strtabs, xmlparser, xmltree,
genode, reportclient, romclient, nitpickerclient, inputclient
var
keyActions = initTable[KeyCode, XmlNode]()
let
configRom = newRomClient("config")
editReport = newReportClient("xml_editor")
nitClient = newNitpickerClient("input")
let
input = nitClient.input
inputDispatch = newSignalDispatcher()
input.sigh inputDispatch.cap
proc submit(action: XmlNode) =
editReport.stream.setPosition 0
editReport.stream.writeLine "<edit>", action, "</edit>", 0.char
submit editReport
inputDispatch.handler = proc() =
for ev in input.events:
if ev.typ == RELEASE and keyActions.contains ev.code:
submit keyActions[ev.code]
proc xml(rom: RomClient): XmlNode =
## Parse ROM content as XML.
rom.stream.setPosition 0
try: result = parseXml rom.stream
except XmlError: result = newElement "empty"
proc configHandler() =
update configRom
clear keyActions
let config = configRom.xml
for node in config.findAll("key").items:
try:
let
keyName = node.attrs["name"]
keyCode = lookupKey keyName
if keyActions.contains keyCode:
echo "discarding duplicate action for key ", keyName
else:
keyActions[keyCode] = node[0]
except:
discard
configRom.handler = configHandler
# set the ROM callback
configHandler()
# parse the config
echo "--- hotkey_edit returning to entrypoint ---"

View File

@@ -1,8 +0,0 @@
TARGET = hotkey_edit
LIBS = base libc
SRC_NIM = main.nim
# Peek inside Input::Client
CC_OPT += -Dprivate=public
CC_CXX_WARN_STRICT =

View File

@@ -1,31 +0,0 @@
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 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 is used to prevent the same node from being inserted
twice and to find nodes to remove or toggle.
Add action
----------
! <add>
! <... name="...">
! ...
! </...>
! </add>
Remove action
-------------
! <remove>
! <... name="..."/>
! </remove>
Toggle action
-------------
! <toggle>
! <... name="...">
! ...
! </...>
! </toggle>

View File

@@ -1,593 +0,0 @@
/*
* \brief Transactional XML editor
* \author Emery Hemingway
* \date 2017-04-29
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <vfs/file_system_factory.h>
#include <vfs/dir_file_system.h>
#include <report_session/report_session.h>
#include <root/component.h>
#include <base/attached_ram_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <base/sleep.h>
#include <base/log.h>
#include <base/component.h>
#include <util/xml_generator.h>
namespace Xml_editor {
using namespace Genode;
typedef Path<Vfs::MAX_PATH_LEN/2> Path;
typedef Genode::String<64> Name;
struct Xml_file;
struct Report_session_component;
class Report_root_component;
struct Main;
}
#define REVISION_ATTR_NAME "edit_rev"
Genode::Env *_env;
struct Xml_editor::Xml_file
{
Genode::Allocator &alloc;
Vfs::Vfs_handle &vfs_handle;
Path const path;
unsigned revision = 0;
/*****************************
** Current and next buffer **
*****************************/
struct Buffer {
char *ptr = nullptr;
size_t size = 0;
Buffer *next;
};
Buffer yin;
Buffer yang;
Buffer *buffer = &yin;
Buffer &next_buffer() { return *buffer->next; }
/**
* Return the next buffer, zeroed, and reallocated
* if necessary
*/
Buffer &next_buffer(size_t min_size)
{
Buffer &next = *buffer->next;
if (next.size < min_size) {
if (next.ptr)
alloc.free(next.ptr, next.size);
next.ptr = (char *)alloc.alloc(min_size);
next.size = min_size;
}
memset(next.ptr, 0x00, next.size);
return next;
}
template<typename FUNC>
Xml_generator generate(char const *type, size_t min_size, FUNC const &fn)
{
Buffer &next = next_buffer(min_size);
return Xml_generator(next.ptr, next.size, type, fn);
}
Xml_node xml() const {
return Xml_node(buffer->ptr, buffer->size); }
/**
* Flush file changes
*/
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()
{
using namespace Vfs;
Directory_service::Stat sb;
vfs_handle.ds().stat(path.base(), sb);
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();
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;
}
}
}
}
void write_file(Vfs::file_size length)
{
using namespace Vfs;
Buffer &next = next_buffer();
vfs_handle.fs().ftruncate(&vfs_handle, length);
file_size offset = 0;
while (offset < length) {
vfs_handle.seek(offset);
file_size n = 0;
vfs_handle.fs().write(
&vfs_handle,
next.ptr + offset,
length - offset,
n);
offset += n;
}
buffer = &next;
}
Xml_file(Genode::Allocator &alloc,
Vfs::Vfs_handle &handle,
Path const &path)
:
alloc(alloc), vfs_handle(handle), path(path)
{
yin.next = &yang;
yang.next = &yin;
read_file();
try {
Xml_node const editor_node = xml().sub_node("xml_editor");
revision = editor_node.attribute_value("rev", 0U);
}
catch (Xml_node::Nonexistent_sub_node) { }
catch (Xml_node::Invalid_syntax) {
Genode::error("invalid XML at '", path, "'");
}
}
~Xml_file()
{
if (yin.ptr)
alloc.free(yin.ptr, yin.size);
if (yang.ptr)
alloc.free(yang.ptr, yang.size);
}
size_t add(Xml_node const &new_node)
{
Xml_node const current = xml();
Name const new_name =
new_node.attribute_value("name", Name());
current.for_each_sub_node(new_node.type().string(),
[&] (Xml_node const &existing_start) {
if (existing_start.attribute_value("name", Name()) == new_name) {
error(new_name, " start node already present in config");
}
});
Xml_generator gen = generate(current.type().string(),
current.size()+new_node.size()*2,
[&] () {
try {
Xml_attribute attr = current.attribute(0U);
while (true) {
auto attr_name = attr.name();
if (attr_name != REVISION_ATTR_NAME) {
Genode::String<256> data;
attr.value(&data);
gen.attribute(attr_name.string(), data.string());
}
attr = attr.next();
}
} catch (Xml_node::Nonexistent_attribute) { }
/* set revision info as a top-level attribute */
gen.attribute("edit_rev", ++revision);
/* add new content node */
gen.append(new_node.addr(), new_node.size());
try {
/* add existing nodes */
Xml_node node = current.sub_node();
while (true) {
gen.append(node.addr(), node.size());
node = node.next();
}
} catch (Xml_node::Nonexistent_sub_node) {
}
gen.append("\n");
});
return gen.used();
}
size_t remove(Xml_node const &node)
{
Name const remove_name =
node.attribute_value("name", Name());
Xml_node const current = xml();
Xml_generator gen = generate(current.type().string(),
current.size()*4,
[&] () {
try {
Xml_attribute attr = current.attribute(0U);
while (true) {
auto attr_name = attr.name();
if (attr_name != REVISION_ATTR_NAME) {
Genode::String<256> data;
attr.value(&data);
gen.attribute(attr_name.string(), data.string());
}
attr = attr.next();
}
} catch (Xml_node::Nonexistent_attribute) { }
/* set revision info as a top-level attribute */
gen.attribute("edit_rev", ++revision);
current.for_each_sub_node([&] (Xml_node const &node) {
/* skip the node we are removing */
auto name = node.attribute_value("name", Name());
if (name != remove_name)
{
gen.append(node.addr(), node.size());
gen.append("\n");
}
});
gen.append("\n");
});
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);
}
};
struct Xml_editor::Report_session_component : Genode::Rpc_object<Report::Session>
{
Session_label const label;
Attached_ram_dataspace ram_ds;
Xml_file &xml_file;
bool const verbose = true;
Report_session_component(Genode::Env &env, size_t buffer_size,
Xml_file &file,
Session_label const &session_label)
:
label(session_label),
ram_ds(env.ram(), env.rm(), buffer_size),
xml_file(file)
{ }
/******************************
** Report session interface **
******************************/
Dataspace_capability dataspace() override {
return ram_ds.cap(); }
void submit(size_t length) override
{
size_t content_size = 0;
auto add_fn = [&] (Xml_node const &action) {
action.for_each_sub_node([&] (Xml_node const &subnode) {
if (verbose)
log("'", label, "' adds '", subnode.attribute_value("name", Name()), "'");
content_size = xml_file.add(subnode);
});
};
auto remove_fn = [&] (Xml_node const &action) {
action.for_each_sub_node([&] (Xml_node const &subnode) {
if (verbose)
log("'", label, "' removes '", subnode.attribute_value("name", Name()), "'");
content_size = xml_file.remove(subnode);
});
};
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) {
xml_file.write_file(content_size);
xml_file.sync();
}
} catch (Xml_node::Invalid_syntax) {
error("invalid XML received from '", label, "'");
} catch (Genode::Xml_generator::Buffer_exceeded) {
error("Genode::Xml_generator::Buffer_exceeded");
} catch (...) {
error("failed to process action from '", label, "'");
throw;
}
}
void response_sigh(Signal_context_capability) override
{
warning(__func__, " not implemented");
}
size_t obtain_response()
{
warning(__func__, " not implemented");
return 0;
}
};
class Xml_editor::Report_root_component :
public Genode::Root_component<Report_session_component>
{
private:
Genode::Env &_env;
Attached_rom_dataspace &_config;
Xml_file &_xml_file;
protected:
Report_session_component *_create_session(char const *args) override
{
using namespace Genode;
size_t const ram_quota =
Arg_string::find_arg(args, "ram_quota").aligned_size();
/* read report buffer size from session arguments */
size_t const buffer_size =
Arg_string::find_arg(args, "buffer_size").aligned_size();
size_t const session_size =
max(sizeof(Report_session_component), 4096U) + buffer_size;
Session_label const label = label_from_args(args);
if (ram_quota < session_size) {
Genode::error("insufficient ram donation from ", label);
throw Insufficient_ram_quota();
}
if (buffer_size == 0) {
Genode::error("zero-length report requested by ", label);
throw Service_denied();
}
try {
return new (md_alloc())
Xml_editor::Report_session_component(
_env, buffer_size, _xml_file, label);
}
catch (Out_of_ram) { error("Out_of_ram"); }
catch (Out_of_caps) { error("Out_of_caps"); }
catch (Service_denied) { error("Service_denied"); }
catch (Insufficient_cap_quota) { error("Insufficient_cap_quota"); }
catch (Insufficient_ram_quota) { error("Insufficient_ram_quota"); }
throw ~0;
}
public:
Report_root_component(Genode::Env &env,
Genode::Allocator &md_alloc,
Attached_rom_dataspace &config,
Xml_file &file)
:
Root_component<Report_session_component>(env.ep(), md_alloc),
_env(env), _config(config), _xml_file(file)
{ }
};
struct Xml_editor::Main
{
Genode::Env &env;
Attached_rom_dataspace config { env, "config" };
Heap heap { env.ram(), env.rm() };
void die(char const *msg)
{
error(msg);
env.parent().exit(~0);
sleep_forever();
}
/*********
** VFS **
*********/
Xml_node vfs_config()
{
try {
return config.xml().sub_node("vfs");
} catch (Xml_node::Nonexistent_sub_node) {
/* XXX: spin for config update? */
die("no VFS configuration defined");
throw;
}
}
struct Io_response_handler : Vfs::Io_response_handler
{
void handle_io_response(Vfs::Vfs_handle::Context *) override { }
} io_response_handler;
Vfs::Global_file_system_factory vfs_factory { heap };
Vfs::Dir_file_system vfs_root {
env, heap, vfs_config(), io_response_handler,
vfs_factory, Vfs::Dir_file_system::Root() };
/* Handle on the output file */
Vfs::Vfs_handle *vfs_handle;
/************************
** Init configuration **
************************/
Path parse_xml_file_path()
{
try {
/* get user string */
Genode::String<Path::capacity()> raw;
Xml_attribute attr = config.xml().attribute("output");
attr.value(&raw);
/* return canonicalized path */
return Path(raw.string());
} catch (Xml_node::Nonexistent_attribute) {
/* XXX: spin for config update? */
die("output file must be defined with <config output=\"...\"/>");
throw;
}
}
Path const xml_file_path = parse_xml_file_path();
Vfs::Vfs_handle &open_output_handle()
{
using namespace Vfs;
typedef Directory_service::Open_result Open_result;
unsigned mode = Directory_service::OPEN_MODE_RDWR;
Vfs_handle *handle;
Open_result res = vfs_root.open(xml_file_path.base(), mode, &handle, heap);
if (res == Open_result::OPEN_ERR_UNACCESSIBLE) {
mode |= Directory_service::OPEN_MODE_CREATE;
res = vfs_root.open(xml_file_path.base(), mode, &handle, heap);
}
switch (res) {
case Open_result::OPEN_OK:
return *handle;
case Open_result::OPEN_ERR_UNACCESSIBLE:
die("OPEN_ERR_UNACCESSIBLE"); break;
case Open_result::OPEN_ERR_NO_PERM:
die("OPEN_ERR_NO_PERM"); break;
case Open_result::OPEN_ERR_EXISTS:
die("OPEN_ERR_EXISTS"); break;
case Open_result::OPEN_ERR_NAME_TOO_LONG:
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;
}
Xml_file xml_file { heap, open_output_handle(), xml_file_path };
Sliced_heap report_heap { env.ram(), env.rm() };
Report_root_component report_root { env, report_heap, config, xml_file };
Main(Genode::Env &env) : env(env)
{
env.parent().announce(env.ep().manage(report_root));
}
};
void Component::construct(Genode::Env &env)
{
_env = &env;
static Xml_editor::Main inst(env);
}

View File

@@ -1,5 +0,0 @@
TARGET = xml_editor
SRC_CC = component.cc
LIBS = base vfs
CC_CXX_WARN_STRICT =

View File

@@ -1,11 +0,0 @@
This component is a simple terminal frontend to _xml_editor_.
The _add_ command opens and reads a file path passed as an agument
and submits it to _xml_editor_ for inclusion into the file being
edited.
The _del_ command does the same, but submits a _remove_ edit action
and _xml_editor_ will remove nodes from the file being edited using
the XML node type and _name_ attribute.
See _run/xml_term_edit.run_ for an example.

View File

@@ -1,246 +0,0 @@
/*
* \brief Transactional XML editor terminal frontend
* \author Emery Hemingway
* \date 2017-04-29
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/reporter.h>
#include <terminal_session/connection.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <libc/component.h>
/* Cli_monitor includes */
#include <command_line.h>
#include <line_editor.h>
/* Libc includes */
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
namespace Xml_term_edit {
using namespace Genode;
using namespace Cli_monitor;
struct Command;
struct Add_command;
struct Del_command;
struct Exit_command;
struct Main;
}
struct Xml_term_edit::Command : Cli_monitor::Command
{
Genode::Allocator &alloc;
Reporter &report;
Command(char const *name,
char const *desc,
Command_registry &cmds,
Genode::Allocator &alloc,
Reporter &report)
:
Cli_monitor::Command(name, desc),
alloc(alloc), report(report)
{
cmds.insert(this);
}
/**
* TODO: this is too slow, cache it
*/
void _for_each_argument(Argument_fn const &fn) const override
{
DIR *dirp = opendir("/");
if (dirp == NULL) {
Genode::error("failed to read root directory");
return;
}
dirent *dp;
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_type == DT_REG) {
fn(Argument(dp->d_name, ""));
}
}
closedir(dirp);
}
void insert_file_content(Command_line &cmd, Xml_generator gen)
{
Path<128> path;
{
char name[128] = { '\0' };
if (cmd.argument(0, name, sizeof(name)) == false) {
error("Error: no configuration name specified\n");
return;
}
path.import(name);
}
int fd = open(path.base(), O_RDONLY);
if (fd == -1) {
Genode::error("failed to open '", path, "'");
return;
}
char buf[1024];
for (;;) {
auto n = read(fd, buf, sizeof(buf));
if (n > 0) {
gen.append(buf, n);
} else {
if (n < 0)
Genode::error("failed to read '", path, "'");
break;
}
}
close(fd);
}
};
struct Xml_term_edit::Add_command : Xml_term_edit::Command
{
Add_command(Command_registry &cmds,
Genode::Allocator &alloc,
Reporter &report)
: Command("add", "add a new subsystem to init", cmds, alloc, report)
{ }
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
Reporter::Xml_generator gen(report, [&] () {
gen.node("add", [&] () {
insert_file_content(cmd, gen); }); });
}
};
struct Xml_term_edit::Del_command : Xml_term_edit::Command
{
Del_command(Command_registry &cmds,
Genode::Allocator &alloc,
Reporter &report)
: Command("del", "delete a subsystem from init", cmds, alloc, report)
{ }
void execute(Command_line &cmd, Terminal::Session &terminal) override
{
Reporter::Xml_generator gen(report, [&] () {
gen.node("remove", [&] () {
insert_file_content(cmd, gen); }); });
}
};
struct Xml_term_edit::Exit_command : Cli_monitor::Command
{
Genode::Parent &parent;
Exit_command(Command_registry &cmds, Genode::Parent &parent)
: Cli_monitor::Command("exit", ""), parent(parent) {
cmds.insert(this); }
void execute(Command_line &cmd, Terminal::Session &terminal) override {
parent.exit(0); }
};
struct Xml_term_edit::Main
{
Genode::Env &env;
Attached_rom_dataspace config { env, "config" };
Xml_node vfs_config()
{
try {
return config.xml().sub_node("vfs");
} catch (Xml_node::Nonexistent_sub_node) {
warning("no VFS configuration defined");
return Xml_node("<vfs><fs writeable=\"0\"/></vfs>");
}
}
Heap heap { env.ram(), env.rm() };
Terminal::Connection term { env, "edit" };
Reporter reporter { env, "xml_editor", "edit", env.ram().avail_ram().value / 2 };
Command_registry cmds;
Cli_monitor::Command *lookup_command(char const *buf)
{
Cli_monitor::Token token(buf);
for (Cli_monitor::Command *curr = cmds.first(); curr; curr = curr->next())
if (strcmp(token.start(), curr->name().string(), token.len()) == 0
&& strlen(curr->name().string()) == token.len())
return curr;
return nullptr;
}
Add_command add_command { cmds, heap, reporter };
Del_command del_command { cmds, heap, reporter };
Exit_command exit_command { cmds, env.parent() };
enum { COMMAND_MAX_LEN = 1024 };
char cmd_buf[COMMAND_MAX_LEN];
Line_editor editor {
"> ", cmd_buf, sizeof(cmd_buf), term, cmds };
void handle_term();
Signal_handler<Main> term_handler {
env.ep(), *this, &Main::handle_term };
Main(Genode::Env &env) : env(env)
{
reporter.enabled(true);
term.read_avail_sigh(term_handler);
}
};
void Xml_term_edit::Main::handle_term()
{
Libc::with_libc([&] () {
while (term.avail() && !editor.completed()) {
char c = 0;
term.read(&c, 1);
editor.submit_input(c);
}
if (editor.completed()) {
auto *cmd = lookup_command(cmd_buf);
if (cmd) {
Cli_monitor::Command_line cmd_line(cmd_buf, *cmd);
cmd->execute(cmd_line, term);
}
editor.reset();
}
});
}
void Libc::Component::construct(Libc::Env &env)
{
Libc::with_libc([&] () {
static Xml_term_edit::Main inst(env);
});
}

View File

@@ -1,7 +0,0 @@
TARGET = xml_term_edit
SRC_CC = component.cc
LIBS = base libc
INC_DIR += $(PRG_DIR) $(call select_from_repositories,src/app/cli_monitor)
CC_CXX_WARN_STRICT =