diff --git a/src/libraries/edit/edit.module b/src/libraries/edit/edit.module new file mode 100644 index 0000000..55e4543 --- /dev/null +++ b/src/libraries/edit/edit.module @@ -0,0 +1,11 @@ +# vim: ft=python + +module("edit", + kind = "lib", + deps = [ "j6", "libc" ], + sources = [ + "line.cpp", + ], + public_headers = [ + "edit/line.h", + ]) diff --git a/src/libraries/edit/include/edit/line.h b/src/libraries/edit/include/edit/line.h new file mode 100644 index 0000000..5ad9aba --- /dev/null +++ b/src/libraries/edit/include/edit/line.h @@ -0,0 +1,40 @@ +#pragma once +/// \file line.h +/// Declaration of line-based editing class + +#include +#include + +namespace edit { + +class API line +{ +public: + line(const char *prompt = nullptr, size_t propmt_len = 0); + ~line(); + + /// Feed input from the terminal keyboard in. + /// \arg data Data from the keyboard + /// \arg len Length of data in bytes + /// \retruns Number of bytes consumed + size_t input(char const *data, size_t len); + + /// Get output to be sent to the terminal screen. + /// \arg data [out] Data to be written to the screen + /// \returns Number of bytes from data to be written + size_t output(char const **data); + +private: + size_t parse_command(char const *data, size_t len); + + char *m_buf; + + size_t m_in; + size_t m_out; + size_t m_prefix; + + char const *m_tmp_out; + size_t m_tmp_out_len; +}; + +} // namespace edit diff --git a/src/libraries/edit/line.cpp b/src/libraries/edit/line.cpp new file mode 100644 index 0000000..2d44b25 --- /dev/null +++ b/src/libraries/edit/line.cpp @@ -0,0 +1,137 @@ +#include +#include +#include + +namespace edit { + +static constexpr char ESC = '\x1b'; +static constexpr char BACKSPACE = '\x08'; +static constexpr char DEL = '\x7f'; + +static const char init_line[] = "\x1b[999D\x1b[K"; +static const size_t init_line_len = sizeof(init_line) - 1; + +static const char get_pos[] = "\x1b[n"; +static const size_t get_pos_len = sizeof(get_pos) - 1; + +static const char backspace[] = "\x1b[D\x1b[K"; +static const size_t backspace_len = sizeof(backspace) - 1; + +line::line(const char *prompt, size_t prompt_len) : + m_out {0} +{ + m_buf = new char [1024]; + memcpy(m_buf, init_line, init_line_len); + m_in = init_line_len; + + if (prompt && prompt_len) { + memcpy(m_buf + m_in, prompt, prompt_len); + m_in += prompt_len; + } + + m_prefix = m_in; + + m_tmp_out = get_pos; + m_tmp_out_len = get_pos_len; +} + + +line::~line() +{ + delete [] m_buf; +} + + +size_t +line::input(char const *data, size_t len) +{ + j6::syslog(j6::logs::app, j6::log_level::spam, "Line edit got %d bytes input", len); + size_t i = 0; + size_t sub_len = 0; + while (i < len) { + switch (data[i]) { + case ESC: + sub_len = parse_command(data + i, len - i); + if (!sub_len) + return i; // Not yet enough data + i += sub_len; + break; + + case BACKSPACE: + case DEL: + if (m_in > m_prefix) { + --m_in; + if (m_out > m_in) + m_out = m_in; + m_tmp_out = backspace; + m_tmp_out_len = backspace_len; + } + ++i; + break; + + case '\r': + m_out = 0; + m_in = m_prefix; + m_tmp_out = "\n"; + m_tmp_out_len = 1; + ++i; + break; + + default: + m_buf[m_in++] = data[i++]; + break; + } + } + + return i; +} + + +size_t +line::output(char const **data) +{ + if (m_tmp_out) { + char const *out = m_tmp_out; + m_tmp_out = nullptr; + + size_t len = m_tmp_out_len; + m_tmp_out_len = 0; + + *data = out; + j6::syslog(j6::logs::app, j6::log_level::spam, "Line edit sending %d bytes alt output", len); + return len; + } + + *data = (m_buf + m_out); + size_t len = m_in - m_out; + m_out = m_in; + j6::syslog(j6::logs::app, j6::log_level::spam, "Line edit sending %d bytes output", len); + return len; +} + + +size_t +line::parse_command(char const *data, size_t len) +{ + size_t i = 2; + + char buf[60]; + char *p = buf; + + while (i < len) { + if ((data[i] >= '0' && data[i] <= '9') || data[i] == ';') { + *p++ = data[i]; + } else { + *p++ = data[i]; + *p = 0; + j6::syslog(j6::logs::app, j6::log_level::verbose, + "line edit got command: ESC[%s", buf); + return i; + } + + ++i; + } + return 0; +} + +} // namespace line diff --git a/src/libraries/j6/channel.cpp b/src/libraries/j6/channel.cpp index 17e1174..b4c22a7 100644 --- a/src/libraries/j6/channel.cpp +++ b/src/libraries/j6/channel.cpp @@ -152,12 +152,13 @@ channel::reserve(size_t size, uint8_t **area, bool block) void channel::commit(size_t size) { - j6::syslog(logs::ipc, log_level::spam, - "Sending %d bytes to channel on {%x}", size, m_def.tx); j6::scoped_lock lock {m_tx.mutex}; m_tx.buf.commit(size); - if (size) - m_tx.waiting.wake(); + if (!size) return; + + m_tx.waiting.wake(); + j6::syslog(logs::ipc, log_level::spam, + "Sending %d bytes to channel on {%x}", size, m_def.tx); } size_t @@ -178,11 +179,13 @@ channel::get_block(uint8_t const **area, bool block) const void channel::consume(size_t size) { + j6::scoped_lock lock {m_tx.mutex}; + m_rx.buf.consume(size); + if (!size) return; + + m_rx.waiting.wake(); j6::syslog(logs::ipc, log_level::spam, "Read %d bytes from channel on {%x}", size, m_def.rx); - m_rx.buf.consume(size); - if (size) - m_rx.waiting.wake(); } } // namespace j6 diff --git a/src/user/6s/6s.module b/src/user/6s/6s.module index 010a01b..f82a273 100644 --- a/src/user/6s/6s.module +++ b/src/user/6s/6s.module @@ -2,7 +2,7 @@ module("6s", targets = [ "user" ], - deps = [ "libc" ], + deps = [ "libc", "edit" ], description = "j6 shell", sources = [ "main.cpp", diff --git a/src/user/6s/main.cpp b/src/user/6s/main.cpp index 9d26d54..b52bbc1 100644 --- a/src/user/6s/main.cpp +++ b/src/user/6s/main.cpp @@ -1,11 +1,13 @@ #include #include #include +#include #include #include #include #include #include +#include extern "C" { int main(int, const char **); @@ -13,9 +15,12 @@ extern "C" { j6_handle_t g_handle_sys = j6_handle_invalid; -const char prompt[] = "\r\nj6> "; +const char prompt[] = "\x1b[1;32mj6> \x1b[0m"; static constexpr size_t prompt_len = sizeof(prompt) - 1; +const char query[] = "\x1b[6n"; +static constexpr size_t query_len = sizeof(prompt) - 1; + size_t gather_command(j6::channel &cout, j6::ring_buffer &buf) { @@ -34,9 +39,15 @@ gather_command(j6::channel &cout, j6::ring_buffer &buf) size_t i = 0; bool newline = false; + bool eof = false; while (i < n) { start[i] = input[i]; outp[i] = input[i]; + if (start[i] == 0x1b) + outp[i] = 'E'; + if (start[i] == 4) + eof = true; + if (start[i++] == '\r') { newline = true; break; @@ -46,6 +57,12 @@ gather_command(j6::channel &cout, j6::ring_buffer &buf) cout.commit(i); cout.consume(i); + if (eof) { + cout.reserve(query_len, &outp, true); + memcpy(outp, query, query_len); + cout.commit(query_len); + } + if (newline) return total_len + i; @@ -63,10 +80,12 @@ main(int argc, const char **argv) if (g_handle_sys == j6_handle_invalid) return 1; + /* static constexpr size_t buffer_pages = 1; j6::ring_buffer in_buffer {buffer_pages}; if (!in_buffer.valid()) return 2; + */ j6_handle_t slp = j6_find_init_handle(j6::proto::sl::id); if (slp == j6_handle_invalid) @@ -93,15 +112,25 @@ main(int argc, const char **argv) if (!cout) return 5; - while (true) { - uint8_t *outp = nullptr; - cout->reserve(prompt_len, &outp, true); - for (size_t i = 0; i < prompt_len; ++i) - outp[i] = prompt[i]; - cout->commit(prompt_len); + edit::line editor {prompt, prompt_len}; - const char *inp = in_buffer.read_ptr(); - size_t size = gather_command(*cout, in_buffer); - in_buffer.consume(size); + while (true) { + uint8_t *outb = nullptr; + char const *inc = nullptr; + size_t len = editor.output(&inc); + while (len) { + cout->reserve(len, &outb, true); + memcpy(outb, inc, len); + cout->commit(len); + len = editor.output(&inc); + } + + char *outc = nullptr; + uint8_t const *inb = nullptr; + len = cout->get_block(&inb); + if (len) { + len = editor.input(reinterpret_cast(inb), len); + cout->consume(len); + } } }