aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-18 19:45:40 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-18 19:45:40 -0700
commit425ef692da7e74112f88f0b368f3286dba84f846 (patch)
treed45729e90010e8d8c539031c3b72165f6884575d
parent0522b4bf4e125b7ceb67f7177db692aed3a0ebf9 (diff)
feat: working parser for rc shell language
-rw-r--r--bin/dvtm-editor.c180
-rwxr-xr-xbin/mkblddir17
-rw-r--r--compile_commands.json709
-rw-r--r--include/libn.h56
-rw-r--r--include/u.h5
-rw-r--r--sys/cmd/cat/cat.c2
-rw-r--r--sys/cmd/dvtm/buffer.c326
-rw-r--r--sys/cmd/dvtm/buffer.h0
-rw-r--r--sys/cmd/dvtm/dvtm-editor.c7
-rw-r--r--sys/cmd/dvtm/dvtm.c116
-rw-r--r--sys/cmd/dvtm/dvtm.c.old1810
-rw-r--r--sys/cmd/dvtm/dvtm.h9
-rw-r--r--sys/cmd/dvtm/events.c1690
-rw-r--r--sys/cmd/dvtm/rules.mk9
-rw-r--r--sys/cmd/dvtm/term.c395
-rw-r--r--sys/cmd/dvtm/term.h481
-rw-r--r--sys/cmd/dvtm/vt.c1282
-rw-r--r--sys/cmd/dvtm/vt.c.old2074
-rw-r--r--sys/cmd/dvtm/vt.h8
-rw-r--r--sys/cmd/dvtm/window.c44
-rw-r--r--sys/cmd/dwm/client.c8
-rw-r--r--sys/cmd/dwm/config.h8
-rw-r--r--sys/cmd/rc/code.c356
-rw-r--r--sys/cmd/rc/code.dep166
-rw-r--r--sys/cmd/rc/exec.c19
-rw-r--r--sys/cmd/rc/io.c435
-rw-r--r--sys/cmd/rc/lex.c415
-rw-r--r--sys/cmd/rc/main.c86
-rw-r--r--sys/cmd/rc/parse.c430
-rw-r--r--sys/cmd/rc/rc.h254
-rw-r--r--sys/cmd/rc/rules.mk21
-rw-r--r--sys/cmd/rc/simple.c13
-rw-r--r--sys/cmd/rc/tree.c144
-rw-r--r--sys/cmd/rc/util.c40
-rw-r--r--sys/cmd/rc/var.c108
-rw-r--r--sys/cmd/rc/word.c64
-rw-r--r--sys/cmd/rules.mk27
-rw-r--r--sys/cmd/term/term.c5
-rw-r--r--sys/cmd/term/term.info37
-rw-r--r--sys/libn/error.c10
-rw-r--r--sys/libn/memory.c5
-rw-r--r--sys/libn/rules.mk2
-rw-r--r--sys/libterm/buffer.c326
-rw-r--r--sys/libterm/escseq.c0
-rw-r--r--sys/libterm/events.c1692
-rw-r--r--sys/libterm/input.c108
-rw-r--r--sys/libterm/term.c256
-rw-r--r--sys/libterm/term.h365
-rw-r--r--sys/libterm/window.c426
-rw-r--r--sys/rules.mk6
50 files changed, 5004 insertions, 10048 deletions
diff --git a/bin/dvtm-editor.c b/bin/dvtm-editor.c
deleted file mode 100644
index 45a61b2..0000000
--- a/bin/dvtm-editor.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/* Invoke $EDITOR as a filter.
- *
- * Copyright (c) 2016 Dmitry Bogatov <KAction@gnu.org>
- * Copyright (c) 2017 Marc André Tanner <mat@brain-dump.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-static void error(const char *msg, ...) {
- va_list ap;
- va_start(ap, msg);
- vfprintf(stderr, msg, ap);
- va_end(ap);
- if (errno)
- fprintf(stderr, ": %s", strerror(errno));
- fprintf(stderr, "\n");
-}
-
-int main(int argc, char *argv[])
-{
- int exit_status = EXIT_FAILURE, tmp_write = -1;
-
- const char *editor = getenv("DVTM_EDITOR");
- if (!editor)
- editor = getenv("VISUAL");
- if (!editor)
- editor = getenv("EDITOR");
- if (!editor)
- editor = "vi";
-
- char tempname[] = "/tmp/dvtm-editor.XXXXXX";
- if ((tmp_write = mkstemp(tempname)) == -1) {
- error("failed to open temporary file `%s'", tempname);
- goto err;
- }
-
- /* POSIX does not mandates modes of temporary file. */
- if (fchmod(tmp_write, 0600) == -1) {
- error("failed to change mode of temporary file `%s'", tempname);
- goto err;
- }
-
- char buffer[2048];
- ssize_t bytes;
- while ((bytes = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) {
- do {
- ssize_t written = write(tmp_write, buffer, bytes);
- if (written == -1) {
- error("failed to write data to temporary file `%s'",
- tempname);
- goto err;
- }
- bytes -= written;
- } while (bytes > 0);
- }
-
- if (fsync(tmp_write) == -1) {
- error("failed to fsync temporary file `%s'", tempname);
- goto err;
- }
-
- struct stat stat_before;
- if (fstat(tmp_write, &stat_before) == -1) {
- error("failed to stat newly created temporary file `%s'", tempname);
- goto err;
- }
-
- if (close(tmp_write) == -1) {
- error("failed to close temporary file `%s'", tempname);
- goto err;
- }
-
- pid_t pid = fork();
- if (pid == -1) {
- error("failed to fork editor process");
- goto err;
- } else if (pid == 0) {
- int tty = open("/dev/tty", O_RDWR);
- if (tty == -1) {
- error("failed to open /dev/tty");
- _exit(1);
- }
-
- if (dup2(tty, STDIN_FILENO) == -1) {
- error("failed to set tty as stdin");
- _exit(1);
- }
-
- if (dup2(tty, STDOUT_FILENO) == -1) {
- error("failed to set tty as stdout");
- _exit(1);
- }
-
- if (dup2(tty, STDERR_FILENO) == -1) {
- error("failed to set tty as stderr");
- _exit(1);
- }
-
- close(tty);
-
- const char *editor_argv[argc+2];
- editor_argv[0] = editor;
- for (int i = 1; i < argc; i++)
- editor_argv[i] = argv[i];
- editor_argv[argc] = tempname;
- editor_argv[argc+1] = NULL;
-
- execvp(editor, (char* const*)editor_argv);
- error("failed to exec editor process `%s'", editor);
- _exit(127);
- }
-
- int status;
- if (waitpid(pid, &status, 0) == -1) {
- error("waitpid failed");
- goto err;
- }
- if (!WIFEXITED(status)) {
- error("editor invocation failed");
- goto err;
- }
- if ((status = WEXITSTATUS(status)) != 0) {
- error("editor terminated with exit status: %d", status);
- goto err;
- }
-
- int tmp_read = open(tempname, O_RDONLY);
- if (tmp_read == -1) {
- error("failed to open for reading of edited temporary file `%s'",
- tempname);
- goto err;
- }
-
- struct stat stat_after;
- if (fstat(tmp_read, &stat_after) == -1) {
- error("failed to stat edited temporary file `%s'", tempname);
- goto err;
- }
-
- if (stat_before.st_mtime == stat_after.st_mtime)
- goto ok; /* no modifications */
-
- while ((bytes = read(tmp_read, buffer, sizeof(buffer))) > 0) {
- do {
- ssize_t written = write(STDOUT_FILENO, buffer, bytes);
- if (written == -1) {
- error("failed to write data to stdout");
- goto err;
- }
- bytes -= written;
- } while (bytes > 0);
- }
-
-ok:
- exit_status = EXIT_SUCCESS;
-err:
- if (tmp_write != -1)
- unlink(tempname);
- return exit_status;
-}
diff --git a/bin/mkblddir b/bin/mkblddir
deleted file mode 100755
index bb646e0..0000000
--- a/bin/mkblddir
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/python
-
-import os
-
-ROOT = "/home/nolln/root"
-SRC = "sys"
-BUILD = "build"
-TEST = "test"
-IGNORED = ["build", "include", "lib", "bin", ".git", "vendor", "obj", "dep", ".generated"]
-
-if __name__ == "__main__":
- for root, dirs, _ in os.walk(f"{ROOT}/{SRC}"):
- dirs[:] = [d for d in dirs if d not in IGNORED]
- for newroot in [BUILD, TEST]:
- blddir = f"{ROOT}/{newroot}/{root[len(ROOT)+len(SRC)+2:]}"
- if not os.path.exists(blddir):
- os.mkdir(blddir)
diff --git a/compile_commands.json b/compile_commands.json
index a2985d6..02f4556 100644
--- a/compile_commands.json
+++ b/compile_commands.json
@@ -8,19 +8,16 @@
"-fno-strict-aliasing",
"-fwrapv",
"-fms-extensions",
- "-D_XOPEN_SOURCE=700",
- "-D_XOPEN_SOURCE_EXTENDED",
- "-DNDEBUG",
"-I",
"include",
"-isystem",
"include/vendor/libc",
"-o",
- "build/cmd/dvtm/hook.o",
- "sys/cmd/dvtm/hook.c"
+ "build/libbio/io/newick.o",
+ "sys/libbio/io/newick.c"
],
"directory": "/home/nolln/root",
- "file": "sys/cmd/dvtm/hook.c"
+ "file": "sys/libbio/io/newick.c"
},
{
"arguments": [
@@ -31,19 +28,16 @@
"-fno-strict-aliasing",
"-fwrapv",
"-fms-extensions",
- "-D_XOPEN_SOURCE=700",
- "-D_XOPEN_SOURCE_EXTENDED",
- "-DNDEBUG",
"-I",
"include",
"-isystem",
"include/vendor/libc",
"-o",
- "build/cmd/dvtm/vt.o",
- "sys/cmd/dvtm/vt.c"
+ "build/libbio/io/fasta.o",
+ "sys/libbio/io/fasta.c"
],
"directory": "/home/nolln/root",
- "file": "sys/cmd/dvtm/vt.c"
+ "file": "sys/libbio/io/fasta.c"
},
{
"arguments": [
@@ -54,18 +48,697 @@
"-fno-strict-aliasing",
"-fwrapv",
"-fms-extensions",
- "-D_XOPEN_SOURCE=700",
- "-D_XOPEN_SOURCE_EXTENDED",
- "-DNDEBUG",
"-I",
"include",
"-isystem",
"include/vendor/libc",
"-o",
- "build/cmd/dvtm/dvtm.o",
- "sys/cmd/dvtm/dvtm.c"
+ "build/cmd/echo/echo.o",
+ "sys/cmd/echo/echo.c"
],
"directory": "/home/nolln/root",
- "file": "sys/cmd/dvtm/dvtm.c"
+ "file": "sys/cmd/echo/echo.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/string.o",
+ "sys/libn/string.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/string.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/cmd/filter/filter.o",
+ "sys/cmd/filter/filter.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/filter/filter.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/sort.o",
+ "sys/libn/sort.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/sort.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/mmap.o",
+ "sys/libn/mmap.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/mmap.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/basic.o",
+ "sys/libmath/basic.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/basic.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libbio/phylo.o",
+ "sys/libbio/phylo.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libbio/phylo.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/random.o",
+ "sys/libn/random.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/random.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/blas1.o",
+ "sys/libmath/blas1.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/blas1.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-o",
+ "build/cmd/menu/menu.o",
+ "sys/cmd/menu/menu.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/menu/menu.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/blas2.o",
+ "sys/libmath/blas2.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/blas2.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/dwm/drw.o",
+ "sys/cmd/dwm/drw.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/dwm/drw.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-o",
+ "build/cmd/menu/drw.o",
+ "sys/cmd/menu/drw.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/menu/drw.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/dwm/util.o",
+ "sys/cmd/dwm/util.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/dwm/util.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/memory.o",
+ "sys/libn/memory.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/memory.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/dwm/hook.o",
+ "sys/cmd/dwm/hook.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/dwm/hook.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-D_GNU_SOURCE",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/blas.o",
+ "sys/libmath/blas.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/blas.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libbio/simulate.o",
+ "sys/libbio/simulate.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libbio/simulate.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/blas3.o",
+ "sys/libmath/blas3.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/blas3.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-o",
+ "build/cmd/menu/util.o",
+ "sys/cmd/menu/util.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/menu/util.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libbio/align.o",
+ "sys/libbio/align.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libbio/align.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libbio/test.o",
+ "sys/libbio/test.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libbio/test.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/dwm/client.o",
+ "sys/cmd/dwm/client.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/dwm/client.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-D_GNU_SOURCE",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libmath/test.o",
+ "sys/libmath/test.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libmath/test.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/test.o",
+ "sys/libn/test.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/test.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/term/x.o",
+ "sys/cmd/term/x.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/term/x.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/libn/fs.o",
+ "sys/libn/fs.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/libn/fs.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/dwm/dwm.o",
+ "sys/cmd/dwm/dwm.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/dwm/dwm.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/cmd/cat/cat.o",
+ "sys/cmd/cat/cat.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/cat/cat.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I/usr/include/freetype2",
+ "-I/usr/include/libpng16",
+ "-I/usr/include/harfbuzz",
+ "-I/usr/include/glib-2.0",
+ "-I/usr/lib/glib-2.0/include",
+ "-I",
+ "include",
+ "-o",
+ "build/cmd/term/term.o",
+ "sys/cmd/term/term.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/term/term.c"
+ },
+ {
+ "arguments": [
+ "gcc",
+ "-c",
+ "-g",
+ "-march=native",
+ "-fno-strict-aliasing",
+ "-fwrapv",
+ "-fms-extensions",
+ "-I",
+ "include",
+ "-isystem",
+ "include/vendor/libc",
+ "-o",
+ "build/cmd/ic/ic.o",
+ "sys/cmd/ic/ic.c"
+ ],
+ "directory": "/home/nolln/root",
+ "file": "sys/cmd/ic/ic.c"
}
] \ No newline at end of file
diff --git a/include/libn.h b/include/libn.h
index f30a761..81ddc9f 100644
--- a/include/libn.h
+++ b/include/libn.h
@@ -100,7 +100,7 @@ uintptr coro·yield(Coro *c, uintptr arg);
error coro·free(Coro *c);
// -----------------------------------------------------------------------------
-// Strings
+// strings
typedef byte* string;
@@ -161,7 +161,7 @@ int utf8·isspace(rune r);
int utf8·istitle(rune r);
// -----------------------------------------------------------------------------
-// I/O
+// i/o
typedef FILE Stream;
typedef struct stat io·Stat;
@@ -300,6 +300,37 @@ mmap·Reader mmap·open(byte *name);
error mmap·close(mmap·Reader rdr);
// -----------------------------------------------------------------------------
+// filesystem
+
+#define iota(x) 1 << (x)
+enum
+{
+ fs·preorder = iota(0),
+ fs·nolinks = iota(1),
+ fs·verbose = iota(2),
+};
+#undef iota
+
+typedef struct fs·Walker fs·Walker;
+typedef struct fs·History fs·History;
+
+struct fs·Walker
+{
+ int fd, lev, max, err;
+ uchar flags : 4;
+ fs·History *hist;
+ struct {
+ void *data;
+ void (*func)(void *data, char *relp, char *absp, io·Stat* info);
+ };
+ char *base, *end, path[4096];
+};
+
+int fs·init(fs·Walker *, char *path);
+void fs·fini(fs·Walker *);
+void fs·walk(fs·Walker *);
+
+// -----------------------------------------------------------------------------
// libflate
// NOTE: Experimental!
@@ -339,6 +370,7 @@ vlong gz·seek(gz·Stream *s, long off, enum SeekPos whence);
// -----------------------------------------------------------------------------
// error handling functions
+void exits(char *s);
void errorf(byte* fmt, ...);
void verrorf(byte* fmt, va_list args);
void panicf(byte *fmt, ...);
@@ -389,16 +421,16 @@ uint64 rng·randi(int max);
#endif
extern char *argv0;
-#define ARGBEGIN for((argv0?0:(argv0=*argv)),argv++,argc--;\
- argv[0] && argv[0][0]=='-' && argv[0][1];\
- argc--, argv++) {\
- byte *_args, *_argt;\
- rune _argc;\
- _args = &argv[0][1];\
- if(_args[0]=='-' && _args[1]==0){\
- argc--; argv++; break;\
- }\
- _argc = 0;\
+#define ARGBEGIN for((argv0?0:(argv0=*argv)),argv++,argc--; \
+ argv[0] && argv[0][0]=='-' && argv[0][1]; \
+ argc--, argv++) { \
+ byte *_args, *_argt; \
+ rune _argc; \
+ _args = &argv[0][1]; \
+ if(_args[0]=='-' && _args[1]==0){ \
+ argc--; argv++; break; \
+ } \
+ _argc = 0; \
while(*_args && (_args += utf8·bytetorune(&_argc, _args)))\
switch(_argc)
#define ARGEND SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
diff --git a/include/u.h b/include/u.h
index 617e41b..3cf3c7b 100644
--- a/include/u.h
+++ b/include/u.h
@@ -59,5 +59,6 @@ typedef int error;
#define arrlen(Array) (sizeof(Array) / sizeof((Array)[0]))
#define arrend(Array) ((Array) + arrlen(Array))
-#define MAX(x, y) ((x) >= (y) ? (x) : (y))
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#define CLAMP(x, lo, hi) (((x) < (lo)) ? (lo) : (((x) > (hi)) ? (hi) : (x)))
diff --git a/sys/cmd/cat/cat.c b/sys/cmd/cat/cat.c
index 847ec0c..a82cfe4 100644
--- a/sys/cmd/cat/cat.c
+++ b/sys/cmd/cat/cat.c
@@ -6,7 +6,7 @@ void
cat(Stream *f, byte *name)
{
long n;
- byte buf[8192];
+ static byte buf[8192];
while(n = io·read(f, 1, arrlen(buf), buf), n > 0) {
if (io·write(stdout, 1, n, buf) != n) {
diff --git a/sys/cmd/dvtm/buffer.c b/sys/cmd/dvtm/buffer.c
deleted file mode 100644
index b903e71..0000000
--- a/sys/cmd/dvtm/buffer.c
+++ /dev/null
@@ -1,326 +0,0 @@
-#include "term.h"
-
-/* row operations */
-void
-zero(Row *row, int start, int len)
-{
- int i;
- Cell cell = {
- .r = L'\0',
- .pen = {0},
- };
-
- for (i = start; i < len + start; i++)
- row->cells[i] = cell;
- row->dirty = true;
-}
-
-void
-roll(Row *start, Row *end, int count)
-{
- int n = end - start;
-
- count %= n;
- if (count < 0)
- count += n;
-
- if (count) {
- char buf[count * sizeof(Row)];
- memcpy(buf, start, count * sizeof(Row));
- memmove(start, start + count, (n - count) * sizeof(Row));
- memcpy(end - count, buf, count * sizeof(Row));
- for (Row *row = start; row < end; row++)
- row->dirty = true;
- }
-}
-
-/* buffer operations */
-void
-bclear(Buffer *b)
-{
- int i;
- Cell cell = {
- .r = L'\0',
- .pen = {
- .state = PenNormal,
- .col.fg = -1,
- .col.bg = -1,
- },
- };
-
- for (i = 0; i < b->nrow; i++) {
- Row *row = b->row + i;
- for (int j = 0; j < b->ncol; j++) {
- row->cells[j] = cell;
- row->dirty = true;
- }
- }
-}
-
-void
-bfree(Buffer *b)
-{
- int i;
-
- for (i = 0; i < b->nrow; i++)
- free(b->row[i].cells);
-
- free(b->row);
-
- for (i = 0; i < b->scroll.size; i++)
- free(b->scroll.buf[i].cells);
-
- free(b->scroll.buf);
- free(b->tabs);
-}
-
-void
-bscroll(Buffer *b, int s)
-{
- /* work in screenfuls */
- int ssz = b->scroll.bot - b->scroll.top;
-
- if (s > ssz) {
- bscroll(b, ssz);
- bscroll(b, s - ssz);
- return;
- }
- if (s < -ssz) {
- bscroll(b, -ssz);
- bscroll(b, s + ssz);
- return;
- }
-
- b->scroll.above += s;
- if (b->scroll.above >= b->scroll.size)
- b->scroll.above = b->scroll.size;
-
- if (s > 0 && b->scroll.size) {
- for (int i = 0; i < s; i++) {
- Row tmp = b->scroll.top[i];
- b->scroll.top[i] = b->scroll.buf[b->scroll.index];
- b->scroll.buf[b->scroll.index] = tmp;
-
- b->scroll.index++;
- if (b->scroll.index == b->scroll.size)
- b->scroll.index = 0;
- }
- }
- roll(b->scroll.top, b->scroll.bot, s);
- if (s < 0 && b->scroll.size) {
- for (int i = (-s) - 1; i >= 0; i--) {
- b->scroll.index--;
- if (b->scroll.index == -1)
- b->scroll.index = b->scroll.size - 1;
-
- Row tmp = b->scroll.top[i];
- b->scroll.top[i] = b->scroll.buf[b->scroll.index];
- b->scroll.buf[b->scroll.index] = tmp;
- b->scroll.top[i].dirty = true;
- }
- }
-}
-
-void
-bresize(Buffer *b, int nrow, int ncol)
-{
- Row *row = b->row;
-
- if (b->nrow != nrow) {
- if (b->crow >= row + nrow) {
- /* scroll up instead of simply chopping off bottom */
- bscroll(b, (b->crow - b->row) - nrow + 1);
- }
- while (b->nrow > nrow) {
- free(row[b->nrow - 1].cells);
- b->nrow--;
- }
-
- row = realloc(row, sizeof(Row) * nrow);
- }
-
- if (b->maxcols < ncol) {
- for (int r = 0; r < b->nrow; r++) {
- row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(row + r, b->ncol, ncol - b->ncol);
- row[r].dirty = true;
- }
- Row *sbuf = b->scroll.buf;
- for (int r = 0; r < b->scroll.size; r++) {
- sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(sbuf + r, b->ncol, ncol - b->ncol);
- }
- b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol);
- for (int c = b->ncol; c < ncol; c++)
- b->tabs[c] = !(c & 7);
- b->maxcols = ncol;
- b->ncol = ncol;
- } else if (b->ncol != ncol) {
- for (int r = 0; r < b->nrow; r++)
- row[r].dirty = true;
- b->ncol = ncol;
- }
-
- int deltarows = 0;
- if (b->nrow < nrow) {
- while (b->nrow < nrow) {
- row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell));
- zero(row + b->nrow, 0, b->maxcols);
- b->nrow++;
- }
-
- /* prepare for backfill */
- if (b->crow >= b->scroll.bot - 1) {
- deltarows = b->row + nrow - b->crow - 1;
- if (deltarows > b->scroll.above)
- deltarows = b->scroll.above;
- }
- }
-
- b->crow += row - b->row;
- b->scroll.top = row;
- b->scroll.bot = row + nrow;
- b->row = row;
-
- /* perform backfill */
- if (deltarows > 0) {
- bscroll(b, -deltarows);
- b->crow += deltarows;
- }
-}
-
-bool
-binit(Buffer *b, int rows, int cols, int size)
-{
- b->pen.state = PenNormal;
- b->pen.col.fg = b->pen.col.fg = -1;
-
- if (size < 0)
- size = 0;
- if (size && !(b->scroll.buf = calloc(size, sizeof(Row))))
- return false;
-
- b->scroll.size = size;
- bresize(b, rows, cols);
- return true;
-}
-
-void
-bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
-{
- if (bs)
- *bs = nil;
- if (be)
- *be = nil;
- if (as)
- *as = nil;
- if (ae)
- *ae = nil;
- if (!b->scroll.size)
- return;
-
- if (b->scroll.above) {
- if (bs)
- *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size];
- if (be)
- *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size];
- }
- if (b->scroll.below) {
- if (as)
- *as = &b->scroll.buf[b->scroll.index];
- if (ae)
- *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size];
- }
-}
-
-Row *
-browfirst(Buffer *b)
-{
- Row *bstart;
- if (!b->scroll.size || !b->scroll.above)
- return b->row;
- bboundary(b, &bstart, nil, nil, nil);
- return bstart;
-}
-
-Row *
-browlast(Buffer *b)
-{
- Row *aend;
- if (!b->scroll.size || !b->scroll.below)
- return b->row + b->nrow - 1;
- bboundary(b, nil, nil, nil, &aend);
- return aend;
-}
-
-Row *
-brownext(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->row, *last = b->row + b->nrow - 1;
-
- if (!row)
- return nil;
-
- bboundary(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row >= first && row < last)
- return ++row;
- if (row == last)
- return after_start;
- if (row == before_end)
- return first;
- if (row == after_end)
- return nil;
- if (row == &b->scroll.buf[b->scroll.size - 1])
- return b->scroll.buf;
- return ++row;
-}
-
-Row *
-bprevrow(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->row, *last = b->row + b->nrow - 1;
-
- if (!row)
- return nil;
-
- bboundary(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row > first && row <= last)
- return --row;
- if (row == first)
- return before_end;
- if (row == before_start)
- return nil;
- if (row == after_start)
- return last;
- if (row == b->scroll.buf)
- return &b->scroll.buf[b->scroll.size - 1];
- return --row;
-}
-
-void
-brender(Buffer *b, Term *t)
-{
- int r, c, n;
- char u[UTFmax+1];
- Row *row;
- Cell *cell;
-
- for (r = 0; r < b->nrow; r++) {
- row = b->row + r;
- if (!row->dirty)
- continue;
-
- for (c = 0; c < b->ncol; c++) {
- cell = row->cells + c;
- tsetpen(t, cell->pen);
- n = utf8·runetobyte(u, &cell->r);
- twrite(t, n, u);
- }
- }
-}
diff --git a/sys/cmd/dvtm/buffer.h b/sys/cmd/dvtm/buffer.h
deleted file mode 100644
index e69de29..0000000
--- a/sys/cmd/dvtm/buffer.h
+++ /dev/null
diff --git a/sys/cmd/dvtm/dvtm-editor.c b/sys/cmd/dvtm/dvtm-editor.c
index 45a61b2..e8685e9 100644
--- a/sys/cmd/dvtm/dvtm-editor.c
+++ b/sys/cmd/dvtm/dvtm-editor.c
@@ -26,7 +26,9 @@
#include <string.h>
#include <unistd.h>
-static void error(const char *msg, ...) {
+static
+void
+error(const char *msg, ...) {
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
@@ -36,7 +38,8 @@ static void error(const char *msg, ...) {
fprintf(stderr, "\n");
}
-int main(int argc, char *argv[])
+int
+main(int argc, char *argv[])
{
int exit_status = EXIT_FAILURE, tmp_write = -1;
diff --git a/sys/cmd/dvtm/dvtm.c b/sys/cmd/dvtm/dvtm.c
index 68f4b8a..3c0f52b 100644
--- a/sys/cmd/dvtm/dvtm.c
+++ b/sys/cmd/dvtm/dvtm.c
@@ -5,7 +5,6 @@ uint waw, wah, wax, way;
Client *clients = nil;
char *title;
const char *dvtm_name = "dvtm";
-
Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY };
static Client *stack = nil;
static Client *sel = nil;
@@ -17,15 +16,7 @@ static uint tagset[2] = { 1, 1 };
static bool mouse_events_enabled = ENABLE_MOUSE;
static Layout *layout = layouts;
-static StatusBar bar =
-{
- .fd = -1,
- .lastpos = BAR_POS,
- .pos = BAR_POS,
- .autohide = BAR_AUTOHIDE,
- .h = 1
-};
-
+static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
static CmdFifo cmdfifo = { .fd = -1 };
static const char *shell;
static Register copyreg;
@@ -33,6 +24,7 @@ static volatile sig_atomic_t running = true;
static bool runinall = false;
/* function implementations */
+
static
void
eprint(const char *errstr, ...)
@@ -195,8 +187,7 @@ show_border(void) {
return (bar.pos != BAR_OFF) || (clients && clients->next);
}
-static
-void
+static void
draw_border(Client *c) {
char t = '\0';
int x, y, maxlen, attrs = NORMAL_ATTR;
@@ -228,17 +219,13 @@ draw_border(Client *c) {
wmove(c->window, y, x);
}
-static
-void
-draw_content(Client *c)
-{
+static void
+draw_content(Client *c) {
vt_draw(c->term, c->window, c->has_title_line, 0);
}
-static
-void
-draw(Client *c)
-{
+static void
+draw(Client *c) {
if (is_content_visible(c)) {
redrawwin(c->window);
draw_content(c);
@@ -248,10 +235,8 @@ draw(Client *c)
wnoutrefresh(c->window);
}
-static
-void
-draw_all(void)
-{
+static void
+draw_all(void) {
if (!nextvisible(clients)) {
sel = nil;
curs_set(0);
@@ -275,10 +260,8 @@ draw_all(void)
draw(sel);
}
-static
-void
-arrange(void)
-{
+static void
+arrange(void) {
uint m = 0, n = 0;
for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
c->order = ++n;
@@ -313,13 +296,10 @@ arrange(void)
draw_all();
}
-static
-void
-attach(Client *c)
-{
+static void
+attach(Client *c) {
if (clients)
clients->prev = c;
-
c->next = clients;
c->prev = nil;
clients = c;
@@ -327,10 +307,8 @@ attach(Client *c)
c->order = o;
}
-static
-void
-attachafter(Client *c, Client *a)
-{ /* attach c after a */
+static void
+attachafter(Client *c, Client *a) { /* attach c after a */
if (c == a)
return;
if (!a)
@@ -347,18 +325,14 @@ attachafter(Client *c, Client *a)
}
}
-static
-void
-attachstack(Client *c)
-{
+static void
+attachstack(Client *c) {
c->snext = stack;
stack = c;
}
-static
-void
-detach(Client *c)
-{
+static void
+detach(Client *c) {
Client *d;
if (c->prev)
c->prev->next = c->next;
@@ -372,10 +346,8 @@ detach(Client *c)
c->next = c->prev = nil;
}
-static
-void
-settitle(Client *c)
-{
+static void
+settitle(Client *c) {
char *term, *t = title;
if (!t && sel == c && *c->title)
t = c->title;
@@ -385,19 +357,15 @@ settitle(Client *c)
}
}
-static
-void
-detachstack(Client *c)
-{
+static void
+detachstack(Client *c) {
Client **tc;
- for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext)
- ;
+ for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
*tc = c->snext;
}
void
-focus(Client *c)
-{
+focus(Client *c) {
if (!c)
for (c = stack; c && !isvisible(c); c = c->snext);
if (sel == c)
@@ -450,8 +418,7 @@ applycolorrules(Client *c)
static
void
-term_title_handler(Vt *term, const char *title)
-{
+term_title_handler(Vt *term, const char *title) {
Client *c = (Client *)vt_data_get(term);
if (title)
strncpy(c->title, title, sizeof(c->title) - 1);
@@ -1718,7 +1685,7 @@ main(int argc, char *argv[])
KeyCombo keys;
uint key_index = 0;
memset(keys, 0, sizeof(keys));
- sigset_t emptysigs, blockset;
+ sigset_t emptyset, blockset;
setenv("DVTM", VERSION, 1);
if (!parse_args(argc, argv)) {
@@ -1726,7 +1693,7 @@ main(int argc, char *argv[])
startup(nil);
}
- sigemptyset(&emptysigs);
+ sigemptyset(&emptyset);
sigemptyset(&blockset);
sigaddset(&blockset, SIGWINCH);
sigaddset(&blockset, SIGCHLD);
@@ -1734,23 +1701,23 @@ main(int argc, char *argv[])
while (running) {
int r, nfds = 0;
- fd_set rdrs; /* set of file descriptors we watch */
+ fd_set rd;
if (screen.need_resize) {
resize_screen();
screen.need_resize = false;
}
- FD_ZERO(&rdrs);
- FD_SET(STDIN_FILENO, &rdrs);
+ FD_ZERO(&rd);
+ FD_SET(STDIN_FILENO, &rd);
if (cmdfifo.fd != -1) {
- FD_SET(cmdfifo.fd, &rdrs);
+ FD_SET(cmdfifo.fd, &rd);
nfds = cmdfifo.fd;
}
if (bar.fd != -1) {
- FD_SET(bar.fd, &rdrs);
+ FD_SET(bar.fd, &rd);
nfds = MAX(nfds, bar.fd);
}
@@ -1764,13 +1731,13 @@ main(int argc, char *argv[])
continue;
}
int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
- FD_SET(pty, &rdrs);
+ FD_SET(pty, &rd);
nfds = MAX(nfds, pty);
- c = c->next;
+ c = c->next;
}
doupdate();
- r = pselect(nfds + 1, &rdrs, nil, nil, nil, &emptysigs);
+ r = pselect(nfds + 1, &rd, nil, nil, nil, &emptyset);
if (r < 0) {
if (errno == EINTR)
@@ -1779,7 +1746,7 @@ main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- if (FD_ISSET(STDIN_FILENO, &rdrs)) {
+ if (FD_ISSET(STDIN_FILENO, &rd)) {
/* NOTE: this is the input handling step */
int code = getch();
if (code >= 0) {
@@ -1804,19 +1771,18 @@ main(int argc, char *argv[])
keypress(code);
}
}
- /* no data available on pty's */
- if (r == 1)
+ if (r == 1) /* no data available on pty's */
continue;
}
- if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rdrs))
+ if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
handle_cmdfifo();
- if (bar.fd != -1 && FD_ISSET(bar.fd, &rdrs))
+ if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
handle_statusbar();
for (Client *c = clients; c; c = c->next) {
- if (FD_ISSET(vt_pty_get(c->term), &rdrs)) {
+ if (FD_ISSET(vt_pty_get(c->term), &rd)) {
if (vt_process(c->term) < 0 && errno == EIO) {
if (c->editor)
c->editor_died = true;
diff --git a/sys/cmd/dvtm/dvtm.c.old b/sys/cmd/dvtm/dvtm.c.old
deleted file mode 100644
index 749ca1d..0000000
--- a/sys/cmd/dvtm/dvtm.c.old
+++ /dev/null
@@ -1,1810 +0,0 @@
-#include "dvtm.h"
-
-/* global variables */
-uint waw, wah, wax, way;
-Client *clients = nil;
-char *title;
-const char *dvtm_name = "dvtm";
-
-Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY };
-static Client *stack = nil;
-static Client *sel = nil;
-static Client *lastsel = nil;
-static Client *msel = nil;
-
-static uint seltags;
-static uint tagset[2] = { 1, 1 };
-static bool mouse_events_enabled = ENABLE_MOUSE;
-static Layout *layout = layouts;
-
-static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 };
-static CmdFifo cmdfifo = { .fd = -1 };
-static const char *shell;
-static Register copyreg;
-static volatile sig_atomic_t running = true;
-static bool runinall = false;
-
-/* function implementations */
-static
-void
-eprint(const char *errstr, ...)
-{
- va_list ap;
- va_start(ap, errstr);
- vfprintf(stderr, errstr, ap);
- va_end(ap);
-}
-
-static
-void
-fatal(const char *errstr, ...)
-{
- va_list ap;
- va_start(ap, errstr);
- vfprintf(stderr, errstr, ap);
- va_end(ap);
- exit(EXIT_FAILURE);
-}
-
-static
-bool
-isarrange(void (*func)())
-{
- return func == layout->arrange;
-}
-
-static
-bool
-isvisible(Client *c)
-{
- return c->tags & tagset[seltags];
-}
-
-static
-bool
-is_content_visible(Client *c)
-{
- if (!c)
- return false;
- if (isarrange(fullscreen))
- return sel == c;
- return isvisible(c) && !c->minimized;
-}
-
-Client*
-nextvisible(Client *c)
-{
- for (; c && !isvisible(c); c = c->next);
- return c;
-}
-
-static
-void
-updatebarpos(void)
-{
- bar.y = 0;
- wax = 0;
- way = 0;
- wah = screen.h;
- waw = screen.w;
- if (bar.pos == BAR_TOP) {
- wah -= bar.h;
- way += bar.h;
- } else if (bar.pos == BAR_BOTTOM) {
- wah -= bar.h;
- bar.y = wah;
- }
-}
-
-static
-void
-hidebar(void)
-{
- if (bar.pos != BAR_OFF) {
- bar.lastpos = bar.pos;
- bar.pos = BAR_OFF;
- }
-}
-
-static
-void
-showbar(void)
-{
- if (bar.pos == BAR_OFF)
- bar.pos = bar.lastpos;
-}
-
-static
-void
-drawbar(void)
-{
- int sx, sy, x, y, width;
- uint occupied = 0, urgent = 0;
- if (bar.pos == BAR_OFF)
- return;
-
- for (Client *c = clients; c; c = c->next) {
- occupied |= c->tags;
- if (c->urgent)
- urgent |= c->tags;
- }
-
- getyx(stdscr, sy, sx);
- attrset(BAR_ATTR);
- move(bar.y, 0);
-
- for (uint i = 0; i < arrlen(tags); i++){
- if (tagset[seltags] & (1 << i))
- attrset(TAG_SEL);
- else if (urgent & (1 << i))
- attrset(TAG_URGENT);
- else if (occupied & (1 << i))
- attrset(TAG_OCCUPIED);
- else
- attrset(TAG_NORMAL);
- printw(TAG_SYMBOL, tags[i]);
- }
-
- attrset(runinall ? TAG_SEL : TAG_NORMAL);
- addstr(layout->symbol);
- attrset(TAG_NORMAL);
-
- getyx(stdscr, y, x);
- (void)y;
- int maxwidth = screen.w - x - 2;
-
- addch(BAR_BEGIN);
- attrset(BAR_ATTR);
-
- wchar_t wbuf[sizeof bar.text];
- size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text);
-
- if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) {
- int pos;
- for (pos = 0; pos + width < maxwidth; pos++)
- addch(' ');
-
- for (size_t i = 0; i < numchars; i++) {
- pos += wcwidth(wbuf[i]);
- if (pos > maxwidth)
- break;
- addnwstr(wbuf+i, 1);
- }
-
- clrtoeol();
- }
-
- attrset(TAG_NORMAL);
- mvaddch(bar.y, screen.w - 1, BAR_END);
- attrset(NORMAL_ATTR);
- move(sy, sx);
- wnoutrefresh(stdscr);
-}
-
-static
-int
-show_border(void) {
- return (bar.pos != BAR_OFF) || (clients && clients->next);
-}
-
-static void
-draw_border(Client *c) {
- char t = '\0';
- int x, y, maxlen, attrs = NORMAL_ATTR;
-
- if (!show_border())
- return;
- if (sel != c && c->urgent)
- attrs = URGENT_ATTR;
- if (sel == c || (runinall && !c->minimized))
- attrs = SELECTED_ATTR;
-
- wattrset(c->window, attrs);
- getyx(c->window, y, x);
- mvwhline(c->window, 0, 0, ACS_HLINE, c->w);
- maxlen = c->w - 10;
- if (maxlen < 0)
- maxlen = 0;
- if ((size_t)maxlen < sizeof(c->title)) {
- t = c->title[maxlen];
- c->title[maxlen] = '\0';
- }
-
- mvwprintw(c->window, 0, 2, "[%s%s#%d]",
- *c->title ? c->title : "",
- *c->title ? " | " : "",
- c->order);
- if (t)
- c->title[maxlen] = t;
- wmove(c->window, y, x);
-}
-
-static void
-draw_content(Client *c) {
- vt_draw(c->term, c->window, c->has_title_line, 0);
-}
-
-static void
-draw(Client *c) {
- if (is_content_visible(c)) {
- redrawwin(c->window);
- draw_content(c);
- }
- if (!isarrange(fullscreen) || sel == c)
- draw_border(c);
- wnoutrefresh(c->window);
-}
-
-static void
-draw_all(void) {
- if (!nextvisible(clients)) {
- sel = nil;
- curs_set(0);
- erase();
- drawbar();
- doupdate();
- return;
- }
-
- if (!isarrange(fullscreen)) {
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- if (c != sel)
- draw(c);
- }
- }
- /* as a last step the selected window is redrawn,
- * this has the effect that the cursor position is
- * accurate
- */
- if (sel)
- draw(sel);
-}
-
-static void
-arrange(void) {
- uint m = 0, n = 0;
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- c->order = ++n;
- if (c->minimized)
- m++;
- }
- erase();
- attrset(NORMAL_ATTR);
- if (bar.fd == -1 && bar.autohide) {
- if ((!clients || !clients->next) && n == 1)
- hidebar();
- else
- showbar();
- updatebarpos();
- }
- if (m && !isarrange(fullscreen))
- wah--;
- layout->arrange();
- if (m && !isarrange(fullscreen)) {
- uint i = 0, nw = waw / m, nx = wax;
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- if (c->minimized) {
- resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1);
- nx += nw;
- }
- }
- wah++;
- }
- focus(nil);
- wnoutrefresh(stdscr);
- drawbar();
- draw_all();
-}
-
-static void
-attach(Client *c) {
- if (clients)
- clients->prev = c;
- c->next = clients;
- c->prev = nil;
- clients = c;
- for (int o = 1; c; c = nextvisible(c->next), o++)
- c->order = o;
-}
-
-static void
-attachafter(Client *c, Client *a) { /* attach c after a */
- if (c == a)
- return;
- if (!a)
- for (a = clients; a && a->next; a = a->next);
-
- if (a) {
- if (a->next)
- a->next->prev = c;
- c->next = a->next;
- c->prev = a;
- a->next = c;
- for (int o = a->order; c; c = nextvisible(c->next))
- c->order = ++o;
- }
-}
-
-static void
-attachstack(Client *c) {
- c->snext = stack;
- stack = c;
-}
-
-static void
-detach(Client *c) {
- Client *d;
- if (c->prev)
- c->prev->next = c->next;
- if (c->next) {
- c->next->prev = c->prev;
- for (d = nextvisible(c->next); d; d = nextvisible(d->next))
- --d->order;
- }
- if (c == clients)
- clients = c->next;
- c->next = c->prev = nil;
-}
-
-static void
-settitle(Client *c) {
- char *term, *t = title;
- if (!t && sel == c && *c->title)
- t = c->title;
- if (t && (term = getenv("TERM")) && !strstr(term, "linux")) {
- printf("\033]0;%s\007", t);
- fflush(stdout);
- }
-}
-
-static void
-detachstack(Client *c) {
- Client **tc;
- for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext);
- *tc = c->snext;
-}
-
-void
-focus(Client *c) {
- if (!c)
- for (c = stack; c && !isvisible(c); c = c->snext);
- if (sel == c)
- return;
- lastsel = sel;
- sel = c;
- if (lastsel) {
- lastsel->urgent = false;
- if (!isarrange(fullscreen)) {
- draw_border(lastsel);
- wnoutrefresh(lastsel->window);
- }
- }
-
- if (c) {
- detachstack(c);
- attachstack(c);
- settitle(c);
- c->urgent = false;
- if (isarrange(fullscreen)) {
- draw(c);
- } else {
- draw_border(c);
- wnoutrefresh(c->window);
- }
- }
- curs_set(c && !c->minimized && vt_cursor_visible(c->term));
-}
-
-static
-void
-applycolorrules(Client *c)
-{
- const ColorRule *r = colorrules;
- int fg = r->color->fg, bg = r->color->bg;
- attr_t attrs = r->attrs;
-
- for (uint i = 1; i < arrlen(colorrules); i++) {
- r = &colorrules[i];
- if (strstr(c->title, r->title)) {
- attrs = r->attrs;
- fg = r->color->fg;
- bg = r->color->bg;
- break;
- }
- }
-
- vt_default_colors_set(c->term, attrs, fg, bg);
-}
-
-static
-void
-term_title_handler(Vt *term, const char *title) {
- Client *c = (Client *)vt_data_get(term);
- if (title)
- strncpy(c->title, title, sizeof(c->title) - 1);
- c->title[title ? sizeof(c->title) - 1 : 0] = '\0';
- settitle(c);
- if (!isarrange(fullscreen) || sel == c)
- draw_border(c);
- applycolorrules(c);
-}
-
-static
-void
-term_urgent_handler(Vt *term) {
- Client *c = (Client *)vt_data_get(term);
- c->urgent = true;
- printf("\a");
- fflush(stdout);
- drawbar();
- if (!isarrange(fullscreen) && sel != c && isvisible(c))
- draw_border(c);
-}
-
-static
-void
-move_client(Client *c, int x, int y)
-{
- if (c->x == x && c->y == y)
- return;
- debug("moving, x: %d y: %d\n", x, y);
- if (mvwin(c->window, y, x) == ERR) {
- eprint("error moving, x: %d y: %d\n", x, y);
- } else {
- c->x = x;
- c->y = y;
- }
-}
-
-static
-void
-resize_client(Client *c, int w, int h)
-{
- bool has_title_line = show_border();
- bool resize_window = c->w != w || c->h != h;
- if (resize_window) {
- debug("resizing, w: %d h: %d\n", w, h);
- if (wresize(c->window, h, w) == ERR) {
- eprint("error resizing, w: %d h: %d\n", w, h);
- } else {
- c->w = w;
- c->h = h;
- }
- }
- if (resize_window || c->has_title_line != has_title_line) {
- c->has_title_line = has_title_line;
- vt_resize(c->app, h - has_title_line, w);
- if (c->editor)
- vt_resize(c->editor, h - has_title_line, w);
- }
-}
-
-void
-resize(Client *c, int x, int y, int w, int h)
-{
- resize_client(c, w, h);
- move_client(c, x, y);
-}
-
-static
-Client*
-get_client_by_coord(uint x, unsigned int y) {
- if (y < way || y >= way+wah)
- return nil;
- if (isarrange(fullscreen))
- return sel;
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) {
- debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order);
- return c;
- }
- }
- return nil;
-}
-
-static
-void
-sigchld_handler(int sig) {
- int errsv = errno;
- int status;
- pid_t pid;
-
- while ((pid = waitpid(-1, &status, WNOHANG)) != 0) {
- if (pid == -1) {
- if (errno == ECHILD) {
- /* no more child processes */
- break;
- }
- eprint("waitpid: %s\n", strerror(errno));
- break;
- }
-
- debug("child with pid %d died\n", pid);
-
- for (Client *c = clients; c; c = c->next) {
- if (c->pid == pid) {
- c->died = true;
- break;
- }
- if (c->editor && vt_pid_get(c->editor) == pid) {
- c->editor_died = true;
- break;
- }
- }
- }
-
- errno = errsv;
-}
-
-static
-void
-sigwinch_handler(int sig) {
- screen.need_resize = true;
-}
-
-static
-void
-sigterm_handler(int sig) {
- running = false;
-}
-
-static
-void
-resize_screen(void)
-{
- struct winsize ws;
-
- if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
- getmaxyx(stdscr, screen.h, screen.w);
- } else {
- screen.w = ws.ws_col;
- screen.h = ws.ws_row;
- }
-
- debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h);
-
- resizeterm(screen.h, screen.w);
- wresize(stdscr, screen.h, screen.w);
- updatebarpos();
- clear();
- arrange();
-}
-
-static
-KeyBinding*
-keybinding(KeyCombo keys, uint keycount)
-{
- for (uint b = 0; b < arrlen(bindings); b++) {
- for (uint k = 0; k < keycount; k++) {
- if (keys[k] != bindings[b].keys[k])
- break;
- if (k == keycount - 1)
- return &bindings[b];
- }
- }
- return nil;
-}
-
-static
-uint
-bitoftag(const char *tag)
-{
- uint i;
- if (!tag)
- return ~0;
- for (i = 0; (i < arrlen(tags)) && strcmp(tags[i], tag); i++);
- return (i < arrlen(tags)) ? (1 << i) : 0;
-}
-
-static void
-tagschanged() {
- bool allminimized = true;
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- if (!c->minimized) {
- allminimized = false;
- break;
- }
- }
- if (allminimized && nextvisible(clients)) {
- focus(nil);
- toggleminimize(nil);
- }
- arrange();
-}
-
-void
-tag(const char *args[])
-{
- if (!sel)
- return;
- sel->tags = bitoftag(args[0]) & TAGMASK;
- tagschanged();
-}
-
-void
-tagid(const char *args[])
-{
- if (!args[0] || !args[1])
- return;
-
- const int win_id = atoi(args[0]);
- for (Client *c = clients; c; c = c->next) {
- if (c->id == win_id) {
- uint ntags = c->tags;
- for (uint i = 1; i < MAX_ARGS && args[i]; i++) {
- if (args[i][0] == '+')
- ntags |= bitoftag(args[i]+1);
- else if (args[i][0] == '-')
- ntags &= ~bitoftag(args[i]+1);
- else
- ntags = bitoftag(args[i]);
- }
- ntags &= TAGMASK;
- if (ntags) {
- c->tags = ntags;
- tagschanged();
- }
- return;
- }
- }
-}
-
-void
-toggletag(const char *args[])
-{
- if (!sel)
- return;
- uint newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK);
- if (newtags) {
- sel->tags = newtags;
- tagschanged();
- }
-}
-
-void
-toggleview(const char *args[])
-{
- uint newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK);
- if (newtagset) {
- tagset[seltags] = newtagset;
- tagschanged();
- }
-}
-
-void
-view(const char *args[])
-{
- uint newtagset = bitoftag(args[0]) & TAGMASK;
- if (tagset[seltags] != newtagset && newtagset) {
- seltags ^= 1; /* toggle sel tagset */
- tagset[seltags] = newtagset;
- tagschanged();
- }
-}
-
-void
-viewprevtag(const char *args[])
-{
- seltags ^= 1;
- tagschanged();
-}
-
-static
-void
-keypress(int code)
-{
- int key = -1;
- uint len = 1;
- char buf[8] = { '\e' };
-
- if (code == '\e') {
- /* pass characters following escape to the underlying app */
- nodelay(stdscr, TRUE);
- for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) {
- if (t > 255) {
- key = t;
- break;
- }
- buf[len] = t;
- }
- nodelay(stdscr, FALSE);
- }
-
- for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) {
- if (is_content_visible(c)) {
- c->urgent = false;
- if (code == '\e')
- vt_write(c->term, buf, len);
- else
- vt_keypress(c->term, code);
-
- if (key != -1)
- vt_keypress(c->term, key);
- }
- if (!runinall)
- break;
- }
-}
-
-static
-void
-mouse_setup(void)
-{
-#ifdef CONFIG_MOUSE
- mmask_t mask = 0;
-
- if (mouse_events_enabled) {
- mask = BUTTON1_CLICKED | BUTTON2_CLICKED;
- for (uint i = 0; i < arrlen(buttons); i++)
- mask |= buttons[i].mask;
- }
- mousemask(mask, nil);
-#endif /* CONFIG_MOUSE */
-}
-
-static bool
-checkshell(const char *shell) {
- if (shell == nil || *shell == '\0' || *shell != '/')
- return false;
- if (!strcmp(strrchr(shell, '/')+1, dvtm_name))
- return false;
- if (access(shell, X_OK))
- return false;
- return true;
-}
-
-static const char *
-getshell(void) {
- const char *shell = getenv("SHELL");
- struct passwd *pw;
-
- if (checkshell(shell))
- return shell;
- if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell))
- return pw->pw_shell;
- return "/bin/sh";
-}
-
-static
-void
-setup(void)
-{
- shell = getshell();
- setlocale(LC_CTYPE, "");
- initscr();
- start_color();
- noecho();
- nonl();
- keypad(stdscr, TRUE);
- mouse_setup();
- raw();
- vt_init();
- vt_keytable_set(keytable, arrlen(keytable));
- for (uint i = 0; i < arrlen(colors); i++) {
- if (COLORS == 256) {
- if (colors[i].fg256)
- colors[i].fg = colors[i].fg256;
- if (colors[i].bg256)
- colors[i].bg = colors[i].bg256;
- }
- colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg);
- }
- resize_screen();
-
- struct sigaction sa;
- memset(&sa, 0, sizeof sa);
- sa.sa_flags = 0;
- sigemptyset(&sa.sa_mask);
- sa.sa_handler = sigwinch_handler;
- sigaction(SIGWINCH, &sa, nil);
- sa.sa_handler = sigchld_handler;
- sigaction(SIGCHLD, &sa, nil);
- sa.sa_handler = sigterm_handler;
- sigaction(SIGTERM, &sa, nil);
- sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, nil);
-}
-
-static
-void
-destroy(Client *c) {
- if (sel == c)
- focusnextnm(nil);
- detach(c);
- detachstack(c);
- if (sel == c) {
- Client *next = nextvisible(clients);
- if (next) {
- focus(next);
- toggleminimize(nil);
- } else {
- sel = nil;
- }
- }
- if (lastsel == c)
- lastsel = nil;
- werase(c->window);
- wnoutrefresh(c->window);
- vt_destroy(c->term);
- delwin(c->window);
- if (!clients && arrlen(actions)) {
- if (!strcmp(c->cmd, shell))
- quit(nil);
- else
- create(nil);
- }
- free(c);
- arrange();
-}
-
-static
-void
-cleanup(void) {
- while (clients)
- destroy(clients);
- vt_shutdown();
- endwin();
- free(copyreg.data);
- if (bar.fd > 0)
- close(bar.fd);
- if (bar.file)
- unlink(bar.file);
- if (cmdfifo.fd > 0)
- close(cmdfifo.fd);
- if (cmdfifo.file)
- unlink(cmdfifo.file);
-}
-
-static
-char *getcwd_by_pid(Client *c) {
- if (!c)
- return nil;
- char buf[32];
- snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid);
- return realpath(buf, nil);
-}
-
-void
-create(const char *args[])
-{
- const char *pargs[4] = { shell, nil };
- char buf[8], *cwd = nil;
- const char *env[] = {
- "DVTM_WINDOW_ID", buf,
- nil
- };
-
- if (args && args[0]) {
- pargs[1] = "-c";
- pargs[2] = args[0];
- pargs[3] = nil;
- }
- Client *c = calloc(1, sizeof(Client));
- if (!c)
- return;
- c->tags = tagset[seltags];
- c->id = ++cmdfifo.id;
- snprintf(buf, sizeof buf, "%d", c->id);
-
- if (!(c->window = newwin(wah, waw, way, wax))) {
- free(c);
- return;
- }
-
- c->term = c->app = vt_create(screen.h, screen.w, screen.history);
- if (!c->term) {
- delwin(c->window);
- free(c);
- return;
- }
-
- if (args && args[0]) {
- c->cmd = args[0];
- char name[PATH_MAX];
- strncpy(name, args[0], sizeof(name));
- name[sizeof(name)-1] = '\0';
- strncpy(c->title, basename(name), sizeof(c->title));
- } else {
- c->cmd = shell;
- }
-
- if (args && args[1])
- strncpy(c->title, args[1], sizeof(c->title));
- c->title[sizeof(c->title)-1] = '\0';
-
- if (args && args[2])
- cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2];
- c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, nil, nil);
- if (args && args[2] && !strcmp(args[2], "$CWD"))
- free(cwd);
- vt_data_set(c->term, c);
- vt_title_handler_set(c->term, term_title_handler);
- vt_urgent_handler_set(c->term, term_urgent_handler);
- applycolorrules(c);
- c->x = wax;
- c->y = way;
- debug("client with pid %d forked\n", c->pid);
- attach(c);
- focus(c);
- arrange();
-}
-
-void
-copymode(const char *args[])
-{
- if (!args || !args[0] || !sel || sel->editor)
- return;
-
- bool colored = strstr(args[0], "pager") != nil;
-
- if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0)))
- return;
-
- int *to = &sel->editor_fds[0];
- int *from = strstr(args[0], "editor") ? &sel->editor_fds[1] : nil;
- sel->editor_fds[0] = sel->editor_fds[1] = -1;
-
- const char *argv[3] = { args[0], nil, nil };
- char argline[32];
- int line = vt_content_start(sel->app);
- snprintf(argline, sizeof(argline), "+%d", line);
- argv[1] = argline;
-
- if (vt_forkpty(sel->editor, args[0], argv, nil, nil, to, from) < 0) {
- vt_destroy(sel->editor);
- sel->editor = nil;
- return;
- }
-
- sel->term = sel->editor;
-
- if (sel->editor_fds[0] != -1) {
- char *buf = nil;
- size_t len = vt_content_get(sel->app, &buf, colored);
- char *cur = buf;
- while (len > 0) {
- ssize_t res = write(sel->editor_fds[0], cur, len);
- if (res < 0) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- break;
- }
- cur += res;
- len -= res;
- }
- free(buf);
- close(sel->editor_fds[0]);
- sel->editor_fds[0] = -1;
- }
-
- if (args[1])
- vt_write(sel->editor, args[1], strlen(args[1]));
-}
-
-void
-focusn(const char *args[])
-{
- for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) {
- if (c->order == atoi(args[0])) {
- focus(c);
- if (c->minimized)
- toggleminimize(nil);
- return;
- }
- }
-}
-
-void
-focusid(const char *args[])
-{
- if (!args[0])
- return;
-
- const int win_id = atoi(args[0]);
- for (Client *c = clients; c; c = c->next) {
- if (c->id == win_id) {
- focus(c);
- if (c->minimized)
- toggleminimize(nil);
- if (!isvisible(c)) {
- c->tags |= tagset[seltags];
- tagschanged();
- }
- return;
- }
- }
-}
-
-void
-focusnext(const char *args[])
-{
- Client *c;
- if (!sel)
- return;
- for (c = sel->next; c && !isvisible(c); c = c->next);
- if (!c)
- for (c = clients; c && !isvisible(c); c = c->next);
- if (c)
- focus(c);
-}
-
-void
-focusnextnm(const char *args[])
-{
- if (!sel)
- return;
- Client *c = sel;
- do {
- c = nextvisible(c->next);
- if (!c)
- c = nextvisible(clients);
- } while (c->minimized && c != sel);
- focus(c);
-}
-
-void
-focusprev(const char *args[])
-{
- Client *c;
- if (!sel)
- return;
- for (c = sel->prev; c && !isvisible(c); c = c->prev);
- if (!c) {
- for (c = clients; c && c->next; c = c->next);
- for (; c && !isvisible(c); c = c->prev);
- }
- if (c)
- focus(c);
-}
-
-void
-focusprevnm(const char *args[])
-{
- if (!sel)
- return;
- Client *c = sel;
- do {
- for (c = c->prev; c && !isvisible(c); c = c->prev);
- if (!c) {
- for (c = clients; c && c->next; c = c->next);
- for (; c && !isvisible(c); c = c->prev);
- }
- } while (c && c != sel && c->minimized);
- focus(c);
-}
-
-void
-focuslast(const char *args[])
-{
- if (lastsel)
- focus(lastsel);
-}
-
-void
-focusup(const char *args[])
-{
- if (!sel)
- return;
- /* avoid vertical separator, hence +1 in x direction */
- Client *c = get_client_by_coord(sel->x + 1, sel->y - 1);
- if (c)
- focus(c);
- else
- focusprev(args);
-}
-
-void
-focusdown(const char *args[])
-{
- if (!sel)
- return;
- Client *c = get_client_by_coord(sel->x, sel->y + sel->h);
- if (c)
- focus(c);
- else
- focusnext(args);
-}
-
-void
-focusleft(const char *args[])
-{
- if (!sel)
- return;
- Client *c = get_client_by_coord(sel->x - 2, sel->y);
- if (c)
- focus(c);
- else
- focusprev(args);
-}
-
-void
-focusright(const char *args[])
-{
- if (!sel)
- return;
- Client *c = get_client_by_coord(sel->x + sel->w + 1, sel->y);
- if (c)
- focus(c);
- else
- focusnext(args);
-}
-
-void
-killclient(const char *args[])
-{
- if (!sel)
- return;
- debug("killing client with pid: %d\n", sel->pid);
- kill(-sel->pid, SIGKILL);
-}
-
-void
-paste(const char *args[])
-{
- if (sel && copyreg.data)
- vt_write(sel->term, copyreg.data, copyreg.len);
-}
-
-void
-quit(const char *args[])
-{
- cleanup();
- exit(EXIT_SUCCESS);
-}
-
-void
-redraw(const char *args[])
-{
- for (Client *c = clients; c; c = c->next) {
- if (!c->minimized) {
- vt_dirty(c->term);
- wclear(c->window);
- wnoutrefresh(c->window);
- }
- }
- resize_screen();
-}
-
-void
-scrollback(const char *args[])
-{
- if (!is_content_visible(sel))
- return;
-
- if (!args[0] || atoi(args[0]) < 0)
- vt_scroll(sel->term, -sel->h/2);
- else
- vt_scroll(sel->term, sel->h/2);
-
- draw(sel);
- curs_set(vt_cursor_visible(sel->term));
-}
-
-void
-send(const char *args[])
-{
- if (sel && args && args[0])
- vt_write(sel->term, args[0], strlen(args[0]));
-}
-
-void
-setlayout(const char *args[])
-{
- uint i;
-
- if (!args || !args[0]) {
- if (++layout == &layouts[arrlen(layouts)])
- layout = &layouts[0];
- } else {
- for (i = 0; i < arrlen(layouts); i++)
- if (!strcmp(args[0], layouts[i].symbol))
- break;
- if (i == arrlen(layouts))
- return;
- layout = &layouts[i];
- }
- arrange();
-}
-
-void
-incnmaster(const char *args[])
-{
- int delta;
-
- if (isarrange(fullscreen) || isarrange(grid))
- return;
- /* arg handling, manipulate nmaster */
- if (args[0] == nil) {
- screen.nmaster = NMASTER;
- } else if (sscanf(args[0], "%d", &delta) == 1) {
- if (args[0][0] == '+' || args[0][0] == '-')
- screen.nmaster += delta;
- else
- screen.nmaster = delta;
- if (screen.nmaster < 1)
- screen.nmaster = 1;
- }
- arrange();
-}
-
-void
-setmfact(const char *args[])
-{
- float delta;
-
- if (isarrange(fullscreen) || isarrange(grid))
- return;
- /* arg handling, manipulate mfact */
- if (args[0] == nil) {
- screen.mfact = MFACT;
- } else if (sscanf(args[0], "%f", &delta) == 1) {
- if (args[0][0] == '+' || args[0][0] == '-')
- screen.mfact += delta;
- else
- screen.mfact = delta;
- if (screen.mfact < 0.1)
- screen.mfact = 0.1;
- else if (screen.mfact > 0.9)
- screen.mfact = 0.9;
- }
- arrange();
-}
-
-void
-startup(const char *args[])
-{
- for (uint i = 0; i < arrlen(actions); i++)
- actions[i].cmd(actions[i].args);
-}
-
-void
-togglebar(const char *args[])
-{
- if (bar.pos == BAR_OFF)
- showbar();
- else
- hidebar();
- bar.autohide = false;
- updatebarpos();
- redraw(nil);
-}
-
-void
-togglebarpos(const char *args[])
-{
- switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) {
- case BAR_TOP:
- bar.pos = BAR_BOTTOM;
- break;
- case BAR_BOTTOM:
- bar.pos = BAR_TOP;
- break;
- }
- updatebarpos();
- redraw(nil);
-}
-
-void
-toggleminimize(const char *args[])
-{
- Client *c, *m, *t;
- uint n;
- if (!sel)
- return;
- /* the last window can't be minimized */
- if (!sel->minimized) {
- for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next))
- if (!c->minimized)
- n++;
- if (n == 1)
- return;
- }
- sel->minimized = !sel->minimized;
- m = sel;
- /* check whether the master client was minimized */
- if (sel == nextvisible(clients) && sel->minimized) {
- c = nextvisible(sel->next);
- detach(c);
- attach(c);
- focus(c);
- detach(m);
- for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t);
- attachafter(m, c);
- } else if (m->minimized) {
- /* non master window got minimized move it above all other
- * minimized ones */
- focusnextnm(nil);
- detach(m);
- for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t);
- attachafter(m, c);
- } else { /* window is no longer minimized, move it to the master area */
- vt_dirty(m->term);
- detach(m);
- attach(m);
- }
- arrange();
-}
-
-void
-togglemouse(const char *args[])
-{
- mouse_events_enabled = !mouse_events_enabled;
- mouse_setup();
-}
-
-void
-togglerunall(const char *args[])
-{
- runinall = !runinall;
- drawbar();
- draw_all();
-}
-
-void
-zoom(const char *args[])
-{
- Client *c;
-
- if (!sel)
- return;
- if (args && args[0])
- focusn(args);
- if ((c = sel) == nextvisible(clients))
- if (!(c = nextvisible(c->next)))
- return;
- detach(c);
- attach(c);
- focus(c);
- if (c->minimized)
- toggleminimize(nil);
- arrange();
-}
-
-/* commands for use by mouse bindings */
-void
-mouse_focus(const char *args[])
-{
- focus(msel);
- if (msel->minimized)
- toggleminimize(nil);
-}
-
-void
-mouse_fullscreen(const char *args[])
-{
- mouse_focus(nil);
- setlayout(isarrange(fullscreen) ? nil : args);
-}
-
-void
-mouse_minimize(const char *args[])
-{
- focus(msel);
- toggleminimize(nil);
-}
-
-void
-mouse_zoom(const char *args[])
-{
- focus(msel);
- zoom(nil);
-}
-
-static
-Cmd *
-get_cmd_by_name(const char *name) {
- for (uint i = 0; i < arrlen(commands); i++) {
- if (!strcmp(name, commands[i].name))
- return &commands[i];
- }
- return nil;
-}
-
-static
-void
-handle_cmdfifo(void) {
- int r;
- char *p, *s, cmdbuf[512], c;
- Cmd *cmd;
-
- r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1);
- if (r <= 0) {
- cmdfifo.fd = -1;
- return;
- }
-
- cmdbuf[r] = '\0';
- p = cmdbuf;
- while (*p) {
- /* find the command name */
- for (; *p == ' ' || *p == '\n'; p++);
- for (s = p; *p && *p != ' ' && *p != '\n'; p++);
- if ((c = *p))
- *p++ = '\0';
- if (*s && (cmd = get_cmd_by_name(s)) != nil) {
- bool quote = false;
- int argc = 0;
- const char *args[MAX_ARGS], *arg;
- memset(args, 0, sizeof(args));
- /* if arguments were specified in config.h ignore the one given via
- * the named pipe and thus skip everything until we find a new line
- */
- if (cmd->action.args[0] || c == '\n') {
- debug("execute %s", s);
- cmd->action.cmd(cmd->action.args);
- while (*p && *p != '\n')
- p++;
- continue;
- }
- /* no arguments were given in config.h so we parse the command line */
- while (*p == ' ')
- p++;
- arg = p;
- for (; (c = *p); p++) {
- switch (*p) {
- case '\\':
- /* remove the escape character '\\' move every
- * following character to the left by one position
- */
- switch (p[1]) {
- case '\\':
- case '\'':
- case '\"': {
- char *t = p+1;
- do {
- t[-1] = *t;
- } while (*t++);
- }
- }
- break;
- case '\'':
- case '\"':
- quote = !quote;
- break;
- case ' ':
- if (!quote) {
- case '\n':
- /* remove trailing quote if there is one */
- if (*(p - 1) == '\'' || *(p - 1) == '\"')
- *(p - 1) = '\0';
- *p++ = '\0';
- /* remove leading quote if there is one */
- if (*arg == '\'' || *arg == '\"')
- arg++;
- if (argc < MAX_ARGS)
- args[argc++] = arg;
-
- while (*p == ' ')
- ++p;
- arg = p--;
- }
- break;
- }
-
- if (c == '\n' || *p == '\n') {
- if (!*p)
- p++;
- debug("execute %s", s);
- for(int i = 0; i < argc; i++)
- debug(" %s", args[i]);
- debug("\n");
- cmd->action.cmd(args);
- break;
- }
- }
- }
- }
-}
-
-static void
-handle_mouse(void) {
-#ifdef CONFIG_MOUSE
- MEVENT event;
- uint i;
- if (getmouse(&event) != OK)
- return;
- msel = get_client_by_coord(event.x, event.y);
-
- if (!msel)
- return;
-
- debug("mouse x:%d y:%d cx:%d cy:%d mask:%d\n", event.x, event.y, event.x - msel->x, event.y - msel->y, event.bstate);
-
- vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate);
-
- for (i = 0; i < arrlen(buttons); i++) {
- if (event.bstate & buttons[i].mask)
- buttons[i].action.cmd(buttons[i].action.args);
- }
-
- msel = nil;
-#endif /* CONFIG_MOUSE */
-}
-
-static void
-handle_statusbar(void) {
- char *p;
- int r;
- switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) {
- case -1:
- strncpy(bar.text, strerror(errno), sizeof bar.text - 1);
- bar.text[sizeof bar.text - 1] = '\0';
- bar.fd = -1;
- break;
- case 0:
- bar.fd = -1;
- break;
- default:
- bar.text[r] = '\0';
- p = bar.text + r - 1;
- for (; p >= bar.text && *p == '\n'; *p-- = '\0');
- for (; p >= bar.text && *p != '\n'; --p);
- if (p >= bar.text)
- memmove(bar.text, p + 1, strlen(p));
- drawbar();
- }
-}
-
-static void
-handle_editor(Client *c) {
- if (!copyreg.data && (copyreg.data = malloc(screen.history)))
- copyreg.size = screen.history;
- copyreg.len = 0;
- while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) {
- ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len);
- if (len == -1) {
- if (errno == EINTR)
- continue;
- break;
- }
- if (len == 0)
- break;
- copyreg.len += len;
- if (copyreg.len == copyreg.size) {
- copyreg.size *= 2;
- if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) {
- copyreg.size = 0;
- copyreg.len = 0;
- }
- }
- }
- c->editor_died = false;
- c->editor_fds[1] = -1;
- vt_destroy(c->editor);
- c->editor = nil;
- c->term = c->app;
- vt_dirty(c->term);
- draw_content(c);
- wnoutrefresh(c->window);
-}
-
-static int
-open_or_create_fifo(const char *name, const char **name_created) {
- struct stat info;
- int fd;
-
- do {
- if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) {
- if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) {
- *name_created = name;
- continue;
- }
- fatal("%s\n", strerror(errno));
- }
- } while (fd == -1);
-
- if (fstat(fd, &info) == -1)
- fatal("%s\n", strerror(errno));
- if (!S_ISFIFO(info.st_mode))
- fatal("%s is not a named pipe\n", name);
- return fd;
-}
-
-static
-void
-usage(void) {
- cleanup();
- eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
- "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
- exit(EXIT_FAILURE);
-}
-
-static
-bool
-parse_args(int argc, char *argv[]) {
- bool init = false;
- const char *name = argv[0];
-
- if (name && (name = strrchr(name, '/')))
- dvtm_name = name + 1;
- if (!getenv("ESCDELAY"))
- set_escdelay(100);
- for (int arg = 1; arg < argc; arg++) {
- if (argv[arg][0] != '-') {
- const char *args[] = { argv[arg], nil, nil };
- if (!init) {
- setup();
- init = true;
- }
- create(args);
- continue;
- }
- if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc)
- usage();
- switch (argv[arg][1]) {
- case 'v':
- puts("dvtm-"VERSION);
- exit(EXIT_SUCCESS);
- case 'M':
- mouse_events_enabled = !mouse_events_enabled;
- break;
- case 'm': {
- char *mod = argv[++arg];
- if (mod[0] == '^' && mod[1])
- *mod = CTRL(mod[1]);
- for (uint b = 0; b < arrlen(bindings); b++)
- if (bindings[b].keys[0] == MOD)
- bindings[b].keys[0] = *mod;
- break;
- }
- case 'd':
- set_escdelay(atoi(argv[++arg]));
- if (ESCDELAY < 50)
- set_escdelay(50);
- else if (ESCDELAY > 1000)
- set_escdelay(1000);
- break;
- case 'h':
- screen.history = atoi(argv[++arg]);
- break;
- case 't':
- title = argv[++arg];
- break;
- case 's':
- bar.fd = open_or_create_fifo(argv[++arg], &bar.file);
- updatebarpos();
- break;
- case 'c': {
- const char *fifo;
- cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file);
- if (!(fifo = realpath(argv[arg], nil)))
- fatal("%s\n", strerror(errno));
- setenv("DVTM_CMD_FIFO", fifo, 1);
- break;
- }
- default:
- usage();
- }
- }
- return init;
-}
-
-int
-main(int argc, char *argv[])
-{
- KeyCombo keys;
- uint key_index = 0;
- memset(keys, 0, sizeof(keys));
- sigset_t emptyset, blockset;
-
- setenv("DVTM", VERSION, 1);
- if (!parse_args(argc, argv)) {
- setup();
- startup(nil);
- }
-
- sigemptyset(&emptyset);
- sigemptyset(&blockset);
- sigaddset(&blockset, SIGWINCH);
- sigaddset(&blockset, SIGCHLD);
- sigprocmask(SIG_BLOCK, &blockset, nil);
-
- while (running) {
- int r, nfds = 0;
- fd_set rd;
-
- if (screen.need_resize) {
- resize_screen();
- screen.need_resize = false;
- }
-
- FD_ZERO(&rd);
- FD_SET(STDIN_FILENO, &rd);
-
- if (cmdfifo.fd != -1) {
- FD_SET(cmdfifo.fd, &rd);
- nfds = cmdfifo.fd;
- }
-
- if (bar.fd != -1) {
- FD_SET(bar.fd, &rd);
- nfds = MAX(nfds, bar.fd);
- }
-
- for (Client *c = clients; c;) {
- if (c->editor && c->editor_died)
- handle_editor(c);
- if (!c->editor && c->died) {
- Client *t = c->next;
- destroy(c);
- c = t;
- continue;
- }
- int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app);
- FD_SET(pty, &rd);
- nfds = MAX(nfds, pty);
- c = c->next;
- }
-
- doupdate();
- r = pselect(nfds + 1, &rd, nil, nil, nil, &emptyset);
-
- if (r < 0) {
- if (errno == EINTR)
- continue;
- perror("select()");
- exit(EXIT_FAILURE);
- }
-
- if (FD_ISSET(STDIN_FILENO, &rd)) {
- /* NOTE: this is the input handling step */
- int code = getch();
- if (code >= 0) {
- keys[key_index++] = code;
- KeyBinding *binding = nil;
-
- if (code == KEY_MOUSE) {
- key_index = 0;
- handle_mouse();
- } else if ((binding = keybinding(keys, key_index))) {
- uint key_length = MAX_KEYS;
- while (key_length > 1 && !binding->keys[key_length-1])
- key_length--;
- if (key_index == key_length) {
- binding->action.cmd(binding->action.args);
- key_index = 0;
- memset(keys, 0, sizeof(keys));
- }
- } else {
- key_index = 0;
- memset(keys, 0, sizeof(keys));
- keypress(code);
- }
- }
- if (r == 1) /* no data available on pty's */
- continue;
- }
-
- if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd))
- handle_cmdfifo();
-
- if (bar.fd != -1 && FD_ISSET(bar.fd, &rd))
- handle_statusbar();
-
- for (Client *c = clients; c; c = c->next) {
- if (FD_ISSET(vt_pty_get(c->term), &rd)) {
- if (vt_process(c->term) < 0 && errno == EIO) {
- if (c->editor)
- c->editor_died = true;
- else
- c->died = true;
- continue;
- }
- }
-
- if (c != sel && is_content_visible(c)) {
- draw_content(c);
- wnoutrefresh(c->window);
- }
- }
-
- if (is_content_visible(sel)) {
- draw_content(sel);
- curs_set(vt_cursor_visible(sel->term));
- wnoutrefresh(sel->window);
- }
- }
-
- cleanup();
- return 0;
-}
diff --git a/sys/cmd/dvtm/dvtm.h b/sys/cmd/dvtm/dvtm.h
index f89f517..a777e18 100644
--- a/sys/cmd/dvtm/dvtm.h
+++ b/sys/cmd/dvtm/dvtm.h
@@ -19,11 +19,9 @@
#include "vt.h"
/* types */
-typedef struct Term Term;
-typedef struct Screen Screen;
typedef struct Layout Layout;
-typedef struct Window Window;
typedef struct Client Client;
+typedef struct Screen Screen;
typedef struct Cmd Cmd;
typedef struct CmdFifo CmdFifo;
@@ -36,7 +34,6 @@ typedef struct StatusBar StatusBar;
struct Screen
{
- Term *backend;
float mfact;
uint nmaster;
int history;
@@ -52,7 +49,7 @@ struct Layout
struct Client
{
- Window *window;
+ WINDOW *window;
Vt *term;
Vt *editor, *app;
/* meta data */
@@ -72,7 +69,6 @@ struct Client
uint tags;
};
-#if 0
typedef struct {
int fg;
int bg;
@@ -86,7 +82,6 @@ typedef struct {
attr_t attrs;
Color *color;
} ColorRule;
-#endif
#define ALT(k) ((k) + (161 - 'a'))
diff --git a/sys/cmd/dvtm/events.c b/sys/cmd/dvtm/events.c
deleted file mode 100644
index c4f544b..0000000
--- a/sys/cmd/dvtm/events.c
+++ /dev/null
@@ -1,1690 +0,0 @@
-#include "term.h"
-
-#include <poll.h>
-
-#define bufcount(in) in->buf.c - in->buf.b
-
-enum {
- NodeKey,
- NodeArr,
-};
-
-struct Node
-{
- int type;
-};
-
-struct KeyNode
-{
- struct Node;
- struct KeyInfo key;
-};
-
-struct ArrNode
-{
- struct Node;
- uchar min, max;
- Node *arr[];
-};
-
-// -----------------------------------------------------------------------
-// loads data into trie
-
-static enum KeyEvent peekmousekey(Input *in, Key *key, ulong *nb);
-
-#define FuncNameMax 10
-static struct {
- char *name;
- int type;
- int sym;
- int mods;
-} funcs[] =
-{
- /* THIS LIST MUST REMAIN SORTED ALPHABETICALLY! */
- { "backspace", KeySym, SymBackspace, 0 },
- { "begin", KeySym, SymBegin, 0 },
- { "beg", KeySym, SymBegin, 0 },
- { "btab", KeySym, SymTab, ModShift},
- { "cancel", KeySym, SymCancel, 0 },
- { "clear", KeySym, SymClear, 0 },
- { "close", KeySym, SymClose, 0 },
- { "command", KeySym, SymCommand, 0 },
- { "copy", KeySym, SymCopy, 0 },
- { "dc", KeySym, SymDelete, 0 },
- { "down", KeySym, SymDown, 0 },
- { "end", KeySym, SymEnd, 0 },
- { "enter", KeySym, SymEnter, 0 },
- { "exit", KeySym, SymExit, 0 },
- { "find", KeySym, SymFind, 0 },
- { "help", KeySym, SymHelp, 0 },
- { "home", KeySym, SymHome, 0 },
- { "ic", KeySym, SymInsert, 0 },
- { "left", KeySym, SymLeft, 0 },
- { "mark", KeySym, SymMark, 0 },
- { "message", KeySym, SymMessage, 0 },
- { "move", KeySym, SymMove, 0 },
- { "next", KeySym, SymPagedown, 0 }, // Not quite, but it's the best we can do
- { "npage", KeySym, SymPagedown, 0 },
- { "open", KeySym, SymOpen, 0 },
- { "options", KeySym, SymOptions, 0 },
- { "ppage", KeySym, SymPageup, 0 },
- { "previous", KeySym, SymPageup, 0 }, // Not quite, but it's the best we can do
- { "print", KeySym, SymPrint, 0 },
- { "redo", KeySym, SymRedo, 0 },
- { "reference", KeySym, SymReference, 0 },
- { "refresh", KeySym, SymRefresh, 0 },
- { "replace", KeySym, SymReplace, 0 },
- { "restart", KeySym, SymRestart, 0 },
- { "resume", KeySym, SymResume, 0 },
- { "right", KeySym, SymRight, 0 },
- { "save", KeySym, SymSave, 0 },
- { "select", KeySym, SymSelect, 0 },
- { "suspend", KeySym, SymSuspend, 0 },
- { "undo", KeySym, SymUndo, 0 },
- { "up", KeySym, SymUp, 0 },
- { nil },
-};
-
-// -----------------------------------------------------------------------
-// utility functions
-
-static
-int
-stricmp(char *s, char *t)
-{
- if (s == nil)
- if (t == nil)
- return 0;
- else
- return -strlen(t);
- else
- if (t == nil)
- return +strlen(s);
-
- int d;
- for (; *s && *t; s++, t++) {
- d = tolower(*s) - tolower(*t);
- if (d < 0 || d > 0)
- return d;
- }
-
- /* XXX: not sure about passing in 0 here */
- return tolower(*s) - tolower(*t);
-}
-
-// -----------------------------------------------------------------------
-// bytes -> keysymbols
-
-static
-Node *
-keynode(int type, int sym, int mask, int set)
-{
- struct KeyNode *n;
- n = malloc(sizeof(*n));
- if (!n)
- panicf("out of memory");
-
- n->type = NodeKey;
- n->key.type = type;
- n->key.sym = sym;
- n->key.modmask = mask;
- n->key.modset = set;
-
- return (Node *)n;
-}
-
-static
-Node *
-arrnode(uchar min, uchar max)
-{
- int nb, i;
- struct ArrNode *n;
- nb = ((int)max-min+1)*sizeof(*n->arr);
- n = malloc(sizeof(*n) + nb);
- if (!n)
- panicf("out of memory");
-
- n->type = NodeArr;
- n->min = min, n->max = max;
- for (i = 0; i <= max-min; i++)
- n->arr[i] = nil;
-
- return (Node *)n;
-}
-
-
-static
-Node *
-nlookup(Node *n, uchar b)
-{
- struct ArrNode *arr;
- switch (n->type) {
- case NodeKey:
- panicf("attempting to subdivide a leaf key node");
- return nil;
- case NodeArr:
- arr = (struct ArrNode *)n;
- if (b < arr->min || b > arr->max)
- return nil;
- return arr->arr[b - arr->min];
- default:
- panicf("unrecognized key node type");
- return nil;
- }
-}
-
-static
-Node *
-compress(Node *root)
-{
- int i;
- uchar min, max;
- struct ArrNode *n, *new;
-
- if (!root)
- return root;
-
- switch (root->type) {
- case NodeKey:
- return root;
- case NodeArr:
- n = (struct ArrNode *)root;
- /* find the zeros */
- for (min = 0; !n->arr[min]; min++) {
- if (min == 255 && !n->arr[min]) {
- free(n);
- return arrnode(1, 0);
- }
- }
-
- for (max = 0xff; !n->arr[max]; max--)
- ;
-
- new = (struct ArrNode *)arrnode(min, max);
-
- for (i = min; i <= max; i++)
- new->arr[i-min] = compress(n->arr[i]);
-
- free(n);
- return (Node*)new;
- default:
- panicf("unrecognized key node type");
- return nil;
- }
-
- return root;
-}
-
-static
-void
-teardown(Node *n)
-{
- int i;
- struct ArrNode *arr;
-
- switch (n->type) {
- case NodeKey:
- break;
- case NodeArr:
- arr = (struct ArrNode *)n;
- for (i = arr->min; i <= arr->max; i++)
- if (arr->arr[i - arr->min])
- teardown(arr->arr[i - arr->min]);
- break;
- default:
- return;
- }
-
- free(n);
-}
-
-static
-int
-insert(Node *root, Node *n, char *seq)
-{
- int pos;
- uchar b;
- Node *nn;
- struct ArrNode *a;
-
- for (pos = 0; (b = seq[pos]); pos++) {
- nn = nlookup(root, b);
- if (!nn)
- break;
- root = nn;
- a = (struct ArrNode *)root;
- }
-
- for (; (b = seq[pos]); pos++) {
- nn = (seq[pos+1]) ? arrnode(0, 0xff) : n;
- if (!nn)
- return 0;
-
- if (root->type != NodeArr)
- panicf("inserted child node onto leaf 'key' node");
-
- a = (struct ArrNode*)root;
- if (b < a->min || b > a->max)
- panicf("out of bound trie insertion");
-
- a->arr[b-a->min] = root = nn;
- }
-
- return 1;
-}
-
-/* unibilium helpers */
-static
-enum unibi_string
-nametostr(char *name)
-{
- enum unibi_string ret;
- for (ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++)
- if (!strcmp(unibi_name_str(ret), name))
- return ret;
-
- return -1;
-}
-
-static
-char *
-unibi_lookup(unibi_term *info, char *name)
-{
- enum unibi_string idx;
- if ((idx = nametostr(name)) == -1)
- return nil;
-
- return (char*)unibi_get_str(info, idx);
-}
-
-static
-Node *
-tryloadkey(unibi_term *info, char *name, char **val, struct KeyInfo *key)
-{
- char *v;
-
- v = unibi_lookup(info, name);
- if (!v || v[0] == 0)
- return nil;
-
- *val = v;
- return keynode(key->type, key->sym, key->modmask, key->modset);
-}
-
-static
-Node *
-loadtermkeys(unibi_term *info)
-{
- int i;
- struct Node *root, *n;
- char *val, name[5 + FuncNameMax + 1];
-
- root = arrnode(0, 0xff);
- if (!root)
- panicf("out of memory");
-
- /* load key syms */
- for (i = 0; funcs[i].name; i++) {
- sprintf(name, "key_%s", funcs[i].name);
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = funcs[i].type,
- .sym = funcs[i].sym,
- .modmask = funcs[i].mods,
- .modset = funcs[i].mods,
- });
-
- /* try shift modified */
- if (!n)
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = funcs[i].type,
- .sym = funcs[i].sym,
- .modmask = funcs[i].mods | ModShift,
- .modset = funcs[i].mods | ModShift,
- });
-
- /* give up */
- if (n)
- insert(root, n, val);
- }
-
- /* load function keys */
- for (i = 1; i < 0xff; i++) {
- sprintf(name, "key_f%d", i);
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = KeyFunc,
- .sym = i,
- .modmask = 0,
- .modset = 0,
- });
- if (!n)
- break;
-
- insert(root, n, val);
- }
-
- /* load mouse keys */
- val = unibi_lookup(info, "key_mouse");
- if (val && !strcmp(val, "\x1b[M")) {
- n = keynode(KeyMouse, 0, 0, 0);
- insert(root, n, val);
- }
-
- return compress(root);
-}
-
-static
-enum KeyEvent
-peeksym(Input *in, Key *key, int force, ulong *nb)
-{
- int res;
- uchar *b, *o;
- Node *root;
- struct KeyNode *kn;
-
- root = in->keys;
- b = in->buf.b;
- while (b <= in->buf.c) {
- printf("checking '%s'\n", b);
- root = nlookup(root, *b);
- if (!root)
- break;
- b++;
-
- if (root->type != NodeKey)
- continue;
-
- kn = (struct KeyNode*)root;
- if (kn->key.type == KeyMouse) {
- o = in->buf.b, in->buf.b = b;
- res = peekmousekey(in, key, nb);
- in->buf.b = o;
-
- if (res == EvKey)
- *nb += b - in->buf.b;
-
- return res;
- }
- key->type = kn->key.type;
- key->mods = kn->key.modset;
- key->code.sym = kn->key.type;
- *nb = b - in->buf.b;
-
- return EvKey;
- }
-
- if (root && !force)
- return EvAgain;
-
- return EvNil;
-}
-
-// -----------------------------------------------------------------------
-// misc commands
-
-static
-inline
-void
-setpos(Key *key, int row, int col)
-{
- row = MIN(row, 0xfff);
- col = MIN(col, 0xfff);
-
- key->code.mouse[1] = (row & 0x0ff);
- key->code.mouse[2] = (col & 0x0ff);
- key->code.mouse[3] = (row & 0xf00) >> 8 | (col & 0x300) >> 4;
-}
-
-static
-inline
-void
-getpos(Key *key, int *row, int *col)
-{
- if (col)
- *col = ((uchar)key->code.mouse[1] | ((uchar)key->code.mouse[3] & 0x0f) << 8) - 1;
-
- if (row)
- *row = ((uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4) - 1;
-}
-
-// -----------------------------------------------------------------------
-// csi/ss3 commands
-
-static int csiinit;
-
-static struct KeyInfo ss3[64];
-static char ss3kpalts[64];
-
-static struct KeyInfo csiss3[64];
-static enum KeyEvent (*do_csi[64])(Input *in, Key *key, int cmd, int narg, long *arg);
-
-static struct KeyInfo csifuncs[35];
-
-/* csi/ss3 cmd keys */
-static
-enum KeyEvent
-do_csi_full(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- if(narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- key->type = csiss3[cmd - 0x40].type;
- key->code.sym = csiss3[cmd - 0x40].sym;
- key->mods &= ~(csiss3[cmd - 0x40].modmask);
- key->mods |= csiss3[cmd - 0x40].modset;
-
- if(key->code.sym == SymUnknown)
- return EvNil;
-
- return EvKey;
-}
-
-static
-void
-put_csi_full(int type, int sym, int modset, int modmask, uchar cmd)
-{
- if(cmd < 0x40 || cmd >= 0x80)
- return;
-
- csiss3[cmd - 0x40].type = type;
- csiss3[cmd - 0x40].sym = sym;
- csiss3[cmd - 0x40].modset = modset;
- csiss3[cmd - 0x40].modmask = modmask;
-
- do_csi[cmd - 0x40] = &do_csi_full;
-}
-
-/* ss3 kpad keys */
-static
-void
-put_ss3_kpalt(int type, int sym, uchar cmd, char kpalt)
-{
- if(cmd < 0x40 || cmd >= 0x80)
- return;
-
- ss3[cmd - 0x40].type = type;
- ss3[cmd - 0x40].sym = sym;
- ss3[cmd - 0x40].modset = 0;
- ss3[cmd - 0x40].modmask= 0;
- ss3kpalts[cmd - 0x40] = kpalt;
-}
-
-/* csi number ~func keys */
-static void emitcodepoint(Input *in, rune r, Key *key);
-
-static
-enum KeyEvent
-do_csi_func(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- if (narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- key->type = KeySym;
-
- if (arg[0] == 27) {
- int mod = key->mods;
- emitcodepoint(in, (rune)arg[2], key);
- key->mods |= mod;
- } else if (arg[0] >= 0 && arg[0] < arrlen(csifuncs)) {
- key->type = csifuncs[arg[0]].type;
- key->code.sym = csifuncs[arg[0]].sym;
- key->mods &= ~(csifuncs[arg[0]].modmask);
- key->mods |= csifuncs[arg[0]].modset;
- } else
- key->code.sym = SymUnknown;
-
- if (key->code.sym == SymUnknown)
- return EvNil;
-
- return EvKey;
-}
-
-static
-void
-put_csi_func(int type, int sym, int num)
-{
- if(num >= arrlen(csifuncs))
- return;
-
- csifuncs[num].type = type;
- csifuncs[num].sym = sym;
- csifuncs[num].modset = 0;
- csifuncs[num].modmask = 0;
-
- do_csi['~' - 0x40] = &do_csi_func;
-}
-
-/* CSI u extended unicode keys */
-static
-enum KeyEvent
-do_csi_u(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch(cmd) {
- case 'u': {
- if(narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- int mod = key->mods;
- key->type = KeySym;
- emitcodepoint(in, arg[0], key);
- key->mods |= mod;
-
- return EvKey;
- }
- default:
- return EvNil;
- }
-}
-
-/* csi m/M mouse events */
-
-static
-enum KeyEvent
-do_csi_m(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- int initial = cmd >> 8;
- cmd &= 0xff;
-
- switch(cmd) {
- case 'M':
- case 'm':
- break;
- default:
- return EvNil;
- }
-
- // rxvt protocol
- if(!initial && narg >= 3) {
- key->type = KeyMouse;
- key->code.mouse[0] = arg[0];
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- setpos(key, arg[1], arg[2]);
-
- return EvKey;
- }
-
- if(initial == '<' && narg >= 3) { // SGR protocol
- key->type = KeyMouse;
- key->code.mouse[0] = arg[0];
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- setpos(key, arg[1], arg[2]);
-
- if(cmd == 'm') // release
- key->code.mouse[3] |= 0x80;
-
- return EvKey;
- }
-
- return EvNil;
-}
-
-/* csi ? R position events */
-
-static
-enum KeyEvent
-do_csi_R(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch(cmd) {
- case 'R'|'?'<<8:
- if(narg < 2)
- return EvNil;
- key->type = KeyPosition;
- setpos(key, arg[1], arg[0]);
- return EvKey;
- default:
- return do_csi_full(in, key, cmd, narg, arg);
- }
-}
-
-/* csi $y mode status events */
-
-static
-enum KeyEvent
-do_csi_y(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch (cmd) {
- case 'y'|'$'<<16:
- case 'y'|'$'<<16 | '?'<<8:
- if (narg < 2)
- return EvNil;
-
- key->type = KeyModeReport;
- key->code.mouse[0] = (cmd >> 8);
- key->code.mouse[1] = arg[0] >> 8;
- key->code.mouse[2] = arg[0] & 0xff;
- key->code.mouse[3] = arg[1];
- return EvKey;
-
- default:
- return EvNil;
- }
-}
-
-/* parse csi events */
-static
-enum KeyEvent
-parse_csi(Input *in, ulong introlen, ulong *csi_len, ulong *nargs, long args[], unsigned long *commandp)
-{
- ulong csi_end = introlen;
-
- while (csi_end < bufcount(in)) {
- if (in->buf.b[csi_end] >= 0x40 && in->buf.b[csi_end] < 0x80)
- break;
- csi_end++;
- }
-
- if(csi_end >= bufcount(in))
- return EvAgain;
-
- uchar cmd = in->buf.b[csi_end];
- *commandp = cmd;
-
- char present = 0;
- int argi = 0;
-
- ulong p = introlen;
-
- // See if there is an initial byte
- if (in->buf.b[p] >= '<' && in->buf.b[p] <= '?') {
- *commandp |= (in->buf.b[p] << 8);
- p++;
- }
-
- // Now attempt to parse out up number;number;... separated values
- while (p < csi_end) {
- uchar c = in->buf.b[p];
-
- if (c >= '0' && c <= '9') {
- if (!present) {
- args[argi] = c - '0';
- present = 1;
- } else {
- args[argi] = (args[argi] * 10) + c - '0';
- }
- } else if(c == ';') {
- if (!present)
- args[argi] = -1;
- present = 0;
- argi++;
-
- if(argi > 16)
- break;
- } else if (c >= 0x20 && c <= 0x2f) {
- *commandp |= c << 16;
- break;
- }
- p++;
- }
-
- if(present)
- argi++;
-
- *nargs = argi;
- *csi_len = csi_end + 1;
-
- return EvKey;
-}
-
-static
-void
-loadctrlkeys(void)
-{
- int i;
- for(i = 0; i < 64; i++) {
- csiss3[i].sym = SymUnknown;
- ss3[i].sym = SymUnknown;
- ss3kpalts[i] = 0;
- }
-
- for(i = 0; i < arrlen(csifuncs); i++)
- csifuncs[i].sym = SymUnknown;
-
- put_csi_full(KeySym, SymUp, 0, 0, 'A');
- put_csi_full(KeySym, SymDown, 0, 0, 'B');
- put_csi_full(KeySym, SymRight,0, 0, 'C');
- put_csi_full(KeySym, SymLeft, 0, 0, 'D');
- put_csi_full(KeySym, SymBegin,0, 0, 'E');
- put_csi_full(KeySym, SymEnd, 0, 0, 'F');
- put_csi_full(KeySym, SymHome, 0, 0, 'H');
- put_csi_full(KeySym, 1, 0, 0, 'P');
- put_csi_full(KeySym, 2, 0, 0, 'Q');
- put_csi_full(KeySym, 3, 0, 0, 'R');
- put_csi_full(KeySym, 4, 0, 0, 'S');
-
- put_csi_full(KeySym, SymTab, ModShift, ModShift, 'Z');
-
- put_ss3_kpalt(KeySym, SymKpenter, 'M', 0);
- put_ss3_kpalt(KeySym, SymKpequals, 'X', '=');
- put_ss3_kpalt(KeySym, SymKpmult, 'j', '*');
- put_ss3_kpalt(KeySym, SymKpplus, 'k', '+');
- put_ss3_kpalt(KeySym, SymKpcomma, 'l', ',');
- put_ss3_kpalt(KeySym, SymKpminus, 'm', '-');
- put_ss3_kpalt(KeySym, SymKpperiod, 'n', '.');
- put_ss3_kpalt(KeySym, SymKpdiv, 'o', '/');
- put_ss3_kpalt(KeySym, SymKp0, 'p', '0');
- put_ss3_kpalt(KeySym, SymKp1, 'q', '1');
- put_ss3_kpalt(KeySym, SymKp2, 'r', '2');
- put_ss3_kpalt(KeySym, SymKp3, 's', '3');
- put_ss3_kpalt(KeySym, SymKp4, 't', '4');
- put_ss3_kpalt(KeySym, SymKp5, 'u', '5');
- put_ss3_kpalt(KeySym, SymKp6, 'v', '6');
- put_ss3_kpalt(KeySym, SymKp7, 'w', '7');
- put_ss3_kpalt(KeySym, SymKp8, 'x', '8');
- put_ss3_kpalt(KeySym, SymKp9, 'y', '9');
-
- put_csi_func(KeySym, SymFind, 1);
- put_csi_func(KeySym, SymInsert, 2);
- put_csi_func(KeySym, SymDelete, 3);
- put_csi_func(KeySym, SymSelect, 4);
- put_csi_func(KeySym, SymPageup, 5);
- put_csi_func(KeySym, SymPagedown, 6);
- put_csi_func(KeySym, SymHome, 7);
- put_csi_func(KeySym, SymEnd, 8);
-
- put_csi_func(KeyFunc, 1, 11);
- put_csi_func(KeyFunc, 2, 12);
- put_csi_func(KeyFunc, 3, 13);
- put_csi_func(KeyFunc, 4, 14);
- put_csi_func(KeyFunc, 5, 15);
- put_csi_func(KeyFunc, 6, 17);
- put_csi_func(KeyFunc, 7, 18);
- put_csi_func(KeyFunc, 8, 19);
- put_csi_func(KeyFunc, 9, 20);
- put_csi_func(KeyFunc, 10, 21);
- put_csi_func(KeyFunc, 11, 23);
- put_csi_func(KeyFunc, 12, 24);
- put_csi_func(KeyFunc, 13, 25);
- put_csi_func(KeyFunc, 14, 26);
- put_csi_func(KeyFunc, 15, 28);
- put_csi_func(KeyFunc, 16, 29);
- put_csi_func(KeyFunc, 17, 31);
- put_csi_func(KeyFunc, 18, 32);
- put_csi_func(KeyFunc, 19, 33);
- put_csi_func(KeyFunc, 20, 34);
-
- do_csi['u' - 0x40] = &do_csi_u;
- do_csi['M' - 0x40] = &do_csi_m;
- do_csi['m' - 0x40] = &do_csi_m;
- do_csi['R' - 0x40] = &do_csi_R;
- do_csi['y' - 0x40] = &do_csi_y;
-
- csiinit = 1;
-}
-
-static
-enum KeyEvent
-peekcsi(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- ulong csi_len;
- long arg[16];
- ulong nargs = arrlen(arg);
- ulong cmd;
-
- enum KeyEvent ev = parse_csi(in, introlen, &csi_len, &nargs, arg, &cmd);
-
- if (ev== EvAgain) {
- if(!force)
- return ev;
-
- emitcodepoint(in, '[', key);
- key->mods |= ModAlt;
- *nb = introlen;
- return EvKey;
- }
- // Mouse in X10 encoding consumes the next 3 bytes also
- if (cmd == 'M' && nargs < 3) {
- in->buf.b += csi_len;
- ev = peekmousekey(in, key, nb);
- in->buf.b -= csi_len;
-
- if (ev == EvKey)
- *nb += csi_len;
-
- return ev;
- }
-
- ev = EvNil;
-
- // We know from the logic above that cmd must be >= 0x40 and < 0x80
- if (do_csi[(cmd & 0xff) - 0x40])
- ev = (*do_csi[(cmd & 0xff) - 0x40])(in, key, cmd, nargs, arg);
-
- if (ev == EvNil) {
- key->type = KeyUnknownCSI;
- key->code.num = cmd;
- key->mods = 0;
-
- in->buf.off = csi_len - introlen;
- *nb = introlen; /* dont advance yet */
- return EvKey;
- }
-
- *nb = csi_len;
- return ev;
-}
-
-static
-enum KeyEvent
-peekss3(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- if(bufcount(in) < introlen + 1) {
- if(!force)
- return EvAgain;
-
- emitcodepoint(in, 'O', key);
- key->mods |= ModAlt;
- *nb= bufcount(in);
- return EvKey;
- }
- uchar cmd = in->buf.b[introlen];
-
- if(cmd < 0x40 || cmd >= 0x80)
- return EvNil;
-
- key->type = csiss3[cmd - 0x40].type;
- key->code.sym = csiss3[cmd - 0x40].sym;
- key->mods = csiss3[cmd - 0x40].modset;
-
- if (key->code.sym == SymUnknown) {
- if (in->flags & FlagConvertKP && ss3kpalts[cmd - 0x40]) {
- key->type = KeyUnicode;
- key->code.pt = ss3kpalts[cmd - 0x40];
- key->mods = 0;
-
- key->utf8[0] = key->code.pt;
- key->utf8[1] = 0;
- } else {
- key->type = ss3[cmd - 0x40].type;
- key->code.sym = ss3[cmd - 0x40].sym;
- key->mods = ss3[cmd - 0x40].modset;
- }
- }
-
- if(key->code.sym == SymUnknown)
- return EvNil;
-
- *nb = introlen + 1;
-
- return EvKey;
-}
-
-static
-enum KeyEvent
-peekctrl(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- ulong str_end = introlen;
-
- while(str_end < bufcount(in)) {
- if (in->buf.b[str_end] == 0x9c) // ST
- break;
- if (in->buf.b[str_end] == 0x1b &&
- (str_end + 1) < bufcount(in) &&
- in->buf.b[str_end+1] == 0x5c) // ESC-prefixed ST
- break;
-
- str_end++;
- }
-
- if (str_end >= bufcount(in))
- return EvAgain;
-
- *nb = str_end + 1;
- if(in->buf.b[str_end] == 0x1b)
- (*nb)++;
-
- // XXX: read carefully
- if(in->savedcsi)
- free(in->savedcsi);
-
- ulong len = str_end - introlen;
-
- in->nsavedcsi++;
- in->savedcsi = malloc(len + 1);
-
- strncpy(in->savedcsi, (char *)in->buf.b + introlen, len);
- in->savedcsi[len] = 0;
-
- key->type = (in->buf.b[introlen-1] & 0x1f) == 0x10 ? KeyDCS : KeyOSC;
- key->code.num = in->nsavedcsi;
- key->mods = 0;
-
- return EvKey;
-}
-
-static
-enum KeyEvent
-peekesc(Input *in, Key *key, int force, ulong *nb)
-{
- if (bufcount(in) == 0)
- return in->closed ? EvEOF : EvNil;
-
- switch (*in->buf.b) {
- case 0x1b:
- if(bufcount(in) < 2)
- return EvNil;
-
- switch(in->buf.b[1]) {
- case 0x4f: // ESC-prefixed SS3
- return peekss3(in, 2, key, force, nb);
-
- case 0x50: // ESC-prefixed DCS
- case 0x5d: // ESC-prefixed OSC
- return peekctrl(in, 2, key, force, nb);
-
- case 0x5b: // ESC-prefixed CSI
- return peekcsi(in, 2, key, force, nb);
- }
-
- return EvNil;
-
- case 0x8f: // SS3
- return peekss3(in, 1, key, force, nb);
-
- case 0x90: // DCS
- case 0x9d: // OSC
- return peekctrl(in, 1, key, force, nb);
-
- case 0x9b: // CSI
- return peekcsi(in, 1, key, force, nb);
- }
-
- return EvNil;
-}
-
-
-// -----------------------------------------------------------------------
-// internal functions
-
-static
-int
-registername(Input *in, int sym, char *name)
-{
- if (!sym)
- sym = in->nkeynm;
-
- if(sym >= in->nkeynm) {
- char **tmp = realloc(in->keynm, sizeof(*tmp) * (sym + 1));
- if(!tmp)
- return -1;
-
- in->keynm = tmp;
-
- // Fill in the hole
- for(int i = in->nkeynm; i < sym; i++)
- in->keynm[i] = nil;
-
- in->nkeynm = sym + 1;
- }
-
- in->keynm[sym] = name;
-
- return sym;
-}
-
-static
-int
-registerc0(Input *in, int sym, int modset, int modmask, uchar ctrl, char *name)
-{
- if(ctrl >= 0x20) {
- errno = EINVAL;
- return -1;
- }
-
- if (name)
- sym = registername(in, sym, name);
-
- in->c0[ctrl].sym = sym;
- in->c0[ctrl].modset = modset;
- in->c0[ctrl].modmask = modmask;
-
- return sym;
-}
-
-static
-void
-advance(Input *in, ulong nb)
-{
- if (in->buf.c < in->buf.b + nb) {
- in->buf.b = in->buf.c;
- return;
- }
-
- in->buf.b += nb;
-}
-
-static
-void
-slidebuffer(Input *in)
-{
- static const ulong halfway = arrlen(in->buf.bytes) / 2;
- if (in->buf.b - in->buf.bytes > halfway) {
- memmove(in->buf.bytes, in->buf.bytes + halfway, halfway);
- in->buf.b -= halfway;
- in->buf.c -= halfway;
- }
-}
-
-static
-void
-harmonize(Input *in, Key *key)
-{
- int flags = in->hflag;
-
- if (flags & HarmonizeSpace) {
- if (key->type == KeyUnicode && key->code.pt == 0x20) {
- key->type = KeySym;
- key->code.sym = SymSpace;
- }
- } else {
- if (key->type == KeySym && key->code.sym == SymSpace) {
- key->type = KeyUnicode;
- key->code.pt = 0x20;
- utf8·runetobyte((char*)key->utf8, &key->code.pt);
- }
- }
-
- if (flags & HarmonizeDelBS) {
- if (key->type == KeySym && key->code.sym == SymDel) {
- key->code.sym = SymBackspace;
- }
- }
-}
-
-static
-void
-emitcodepoint(Input *in, rune r, Key *key)
-{
- if (r == 0) {
- key->type = KeySym;
- key->code.sym = SymSpace;
- key->mods = ModCtrl;
- goto harmonize;
- }
- if (r < 0x20) {
- key->code.pt = 0;
- key->mods = 0;
- if (!(in->flags & FlagNoInterpret) && in->c0[r].sym != SymUnknown) {
- key->code.sym = in->c0[r].sym;
- key->mods |= in->c0[r].modset;
- }
- if (!key->code.sym) {
- key->type = KeyUnicode;
- if (r+0x40 >= 'A' && r+0x40 <= 'Z')
- // it's a letter - use lowercase instead
- key->code.pt = r + 0x60;
- else
- key->code.pt = r + 0x40;
- key->mods = ModCtrl;
- } else
- key->type = KeySym;
- goto harmonize;
- }
- if (r == 0x7f && !(in->flags & FlagNoInterpret)) {
- // ascii del
- key->type = KeySym;
- key->code.sym = SymDel;
- key->mods = 0;
- goto harmonize;
- }
- if (r >= 0x20 && r < 0x80) {
- // ascii lowbyte range
- key->type = KeyUnicode;
- key->code.pt = r;
- key->mods = 0;
- goto harmonize;
- }
- if (r >= 0x80 && r < 0xa0) {
- // UTF-8 never starts with a C1 byte. So we can be sure of these
- key->type = KeyUnicode;
- key->code.pt = r - 0x40;
- key->mods = ModCtrl|ModAlt;
- goto harmonize;
- }
- key->type = KeyUnicode;
- key->code.pt = r;
- key->mods = 0;
-
-harmonize:
- harmonize(in, key);
- utf8·runetobyte((char*)key->utf8, &key->code.pt);
-}
-
-static
-enum KeyEvent
-peekmousekey(Input *in, Key *key, ulong *nb)
-{
- if (in->buf.c - in->buf.b < 3)
- return EvAgain;
-
- key->type = KeyMouse;
- key->code.mouse[0] = in->buf.c[0] - 0x20;
- key->code.mouse[1] = in->buf.c[1] - 0x20;
- key->code.mouse[2] = in->buf.c[2] - 0x20;
- key->code.mouse[3] = 0;
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- *nb = 3;
- return EvKey;
-}
-
-enum KeyEvent peekkey(Input *in, Key *key, int force, ulong *nb);
-
-static
-enum KeyEvent
-peeksimplekey(Input *in, Key *key, int force, ulong *nb)
-{
- uchar c, *b;
- int n;
- rune r;
- enum KeyEvent ev;
-
- b = in->buf.b;
- c = *b;
-
- if (c == 0x1b) {
- if (bufcount(in) == 1) {
- if (!force)
- return EvAgain;
- goto ascii;
- }
- in->buf.b++;
- ev = peekkey(in, key, force, nb);
- in->buf.b--;
-
- switch (ev) {
- case EvKey:
- key->mods |= ModAlt;
- (*nb)++;
- /* fallthrough */
- case EvNil: case EvEOF:
- case EvAgain: case EvErr:
- return ev;
- }
- }
- if (c == 0xa0)
- goto ascii;
- if (in->flags & FlagUTF8) {
- n = utf8·bytetorune(&r, (char*)in->buf.b);
- *nb = n; /* store the number of bytes */
- if (n > bufcount(in)) {
- if (!force)
- return EvAgain;
- r = RuneErr;
- *nb = bufcount(in);
- }
- key->type = KeyUnicode;
- key->mods = 0;
- goto utf8;
- }
- /* if we are here just emit raw byte */
- key->type = KeyUnicode;
- key->code.pt = c;
- key->mods = 0;
- key->utf8[0] = c;
- key->utf8[1] = 0;
- *nb = 1;
- return EvKey;
-
-ascii:
- *nb = 1;
- r = c;
-utf8:
- emitcodepoint(in, r, key);
- return EvKey;
-}
-
-// -----------------------------------------------------------------------
-// exported functions
-
-Input *
-makeinput(int fd, int flags, unibi_term *info)
-{
- int i;
- Input *in;
- char *e;
-
- if (!(in = malloc(sizeof(in))))
- panicf("out of memory");
-
- in->fd = fd;
- if (!(flags & (FlagRaw|FlagUTF8))) {
- if (((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) &&
- (e = strchr(e, '.')) && e++ && (!stricmp(e, "UTF-8") || !stricmp(e, "UTF8")))
- flags |= FlagUTF8;
- else
- flags |= FlagRaw;
- }
- in->flags = flags;
- in->wait = 50; /* in msec */
- in->closed = 0;
- in->started = 0;
- in->hasold = 0;
- in->keys = loadtermkeys(info);
-
- /* initialize buffer */
- in->buf.c = in->buf.b = in->buf.bytes;
- in->buf.e = arrend(in->buf.bytes);
- in->buf.off = 0;
- memset(in->buf.bytes, 0, arrlen(in->buf.bytes));
-
- /* initialize names */
- for (i = 0; i < 32; i++)
- in->c0[i].sym = SymNone;
-
- registerc0(in, SymTab, 0x09, 0, 0, nil);
- registerc0(in, SymEnter, 0x0d, 0, 0, nil);
- registerc0(in, SymEscape, 0x1b, 0, 0, nil);
-
- /* load in csi */
- in->nsavedcsi = 0;
- in->savedcsi = nil;
- loadctrlkeys();
-
- return in;
-}
-
-void
-freeinput(Input *in)
-{
- // free(in);
-}
-
-int
-startrecord(Input *in)
-{
- struct termios new;
- if (in->started)
- return 1;
-
- if (in->fd != -1 && !(in->flags & FlagNoTermIOS)) {
- if (tcgetattr(in->fd, &new) == 0) {
- in->oldterm = new;
- in->hasold = 1;
-
- new.c_iflag &= ~(IXON|INLCR|ICRNL);
- new.c_lflag &= ~(ICANON|ECHO|IEXTEN);
-
- new.c_cc[VMIN] = 1;
- new.c_cc[VTIME] = 0;
-
- if (in->flags & FlagCtrlC)
- new.c_lflag &= ~ISIG;
- else {
- /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */
- new.c_cc[VQUIT] = _POSIX_VDISABLE;
- new.c_cc[VSUSP] = _POSIX_VDISABLE;
- /* Some OSes have Ctrl-Y==VDSUSP */
-# ifdef VDSUSP
- new.c_cc[VDSUSP] = _POSIX_VDISABLE;
-# endif
- }
- tcsetattr(in->fd, TCSANOW, &new);
- }
- }
-
- in->started = 1;
- return 1;
-}
-
-int
-stoprecord(Input *in)
-{
- if (!in->started)
- return 1;
-
- if (in->hasold)
- tcsetattr(in->fd, TCSANOW, &in->oldterm);
-
- in->started = 0;
- return 1;
-}
-
-enum KeyEvent
-peekkey(Input *in, Key *key, int force, ulong *nb)
-{
- int i, again = 0;
- enum KeyEvent ev;
- static enum KeyEvent (*peek[2])(Input *, Key *, int, ulong *) = { peeksym, peekesc };
-
- if (!in->started) {
- errno = EINVAL;
- return EvErr;
- }
-
- if (in->buf.off) {
- in->buf.b += in->buf.off;
- in->buf.off = 0;
- }
-
- for (i = 0; i < arrlen(peek); i++) {
- ev = peek[i](in, key, force, nb);
- switch (ev) {
- case EvKey:
- slidebuffer(in);
- /* fallthrough */
- case EvEOF:
- case EvErr:
- return ev;
- case EvAgain:
- if (!force)
- again = 1;
- /* fallthrough */
- case EvNil:
- break;
- }
- }
- if (again)
- return EvAgain;
-
- return peeksimplekey(in, key, force, nb);
-}
-
-enum KeyEvent
-getkey(Input *in, Key *key)
-{
- ulong nb;
- enum KeyEvent ev;
-
- ev = peekkey(in, key, 0, &nb);
- switch (ev) {
- case EvKey:
- advance(in, nb);
- break;
- case EvAgain:
- peekkey(in, key, 1, &nb);
- /* get nb but don't advance */
- break;
- default:
- ;
- }
- return ev;
-}
-
-enum KeyEvent
-demandkey(Input *in, Key *key)
-{
- ulong nb;
- enum KeyEvent ev;
-
- ev = peekkey(in, key, 1, &nb);
- if (ev == EvKey)
- advance(in, nb);
-
- return ev;
-}
-
-enum KeyEvent
-isreadablekey(Input *in)
-{
- int n;
- if (in->fd == -1) {
- errno = EBADF;
- return EvErr;
- }
-
- /* reset to beginning of buffer */
- if (in->buf.b > in->buf.bytes) {
- n = in->buf.b - in->buf.bytes;
- memmove(in->buf.bytes, in->buf.b, n);
- in->buf.b = in->buf.bytes;
- in->buf.c = in->buf.b + n;
- }
-
-read:
- n = read(in->fd, in->buf.c, in->buf.e-in->buf.c);
- if (n == -1) {
- if (errno == EAGAIN)
- return EvNil;
- if (errno == EINTR && !(in->flags & FlagEintr))
- goto read;
- else
- return EvErr;
- }
- if (n < 1) {
- in->closed = 1;
- return EvNil;
- }
- in->buf.c += n;
- return EvAgain;
-}
-
-enum KeyEvent
-waitkey(Input *in, Key *key)
-{
- enum KeyEvent ev;
- struct pollfd p;
-
- if (in->fd == -1) {
- errno = EBADF;
- return EvErr;
- }
-
- for (;;) {
- ev = getkey(in, key);
- switch (ev) {
- case EvKey: case EvEOF: case EvErr:
- return ev;
- case EvNil:
- ev = isreadablekey(in);
- if (ev == EvErr)
- return ev;
- break;
-
- case EvAgain:
- /* can't wait any longer */
- if (in->closed)
- return demandkey(in, key);
- poll:
- p.fd = in->fd;
- p.events = POLLIN;
-
- if (poll(&p, 1, in->wait) == -1) {
- if (errno == EINTR && !(in->flags & FlagEintr))
- goto poll;
- return EvErr;
- }
-
- if (p.revents & (POLLIN|POLLHUP|POLLERR))
- ev = isreadablekey(in);
- else
- ev = EvNil;
-
- if (ev == EvErr)
- return ev;
- if (ev == EvNil)
- return demandkey(in, key);
- break;
- }
- }
- /* unreachable */
-}
-
-enum KeyEvent
-decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col)
-{
- if(key->type != KeyMouse)
- return EvNil;
-
- if(button)
- *button = 0;
-
- getpos(key, row, col);
-
- if(!ev)
- return EvKey;
-
- int btn = 0;
- int code = key->code.mouse[0];
- int drag = code & 0x20;
- code &= ~0x3c;
-
- switch(code) {
- case 0:
- case 1:
- case 2:
- *ev = drag ? MouseDrag : MousePress;
- btn = code + 1;
- break;
-
- case 3:
- *ev = MouseRelease;
- // no button hint
- break;
-
- case 64:
- case 65:
- *ev = drag ? MouseDrag : MousePress;
- btn = code + 4 - 64;
- break;
-
- default:
- *ev = MouseNil;
- }
-
- if (button)
- *button = btn;
-
- if (key->code.mouse[3] & 0x80)
- *ev = MouseRelease;
-
- return EvKey;
-}
-
-enum KeyEvent
-decodepos(Input *in, Key *key, int *row, int *col)
-{
- if (key->type != KeyPosition)
- return EvNil;
-
- getpos(key, row, col);
-
- return EvKey;
-}
-
-enum KeyEvent
-decodemode(Input *in, Key *key, int *init, int *mode, int *val)
-{
- if (key->type != KeyModeReport)
- return EvNil;
-
- if (init)
- *init = key->code.mouse[0];
-
- if (mode)
- *mode = (key->code.mouse[1] << 8) | key->code.mouse[2];
-
- if (val)
- *val = key->code.mouse[3];
-
- return EvKey;
-}
-
-char *
-keyname(Input *in, int sym)
-{
- if (sym == SymUnknown || sym >= in->nkeynm)
- return "<unknown>";
-
- return in->keynm[sym];
-}
-
-// -----------------------------------------------------------------------
-// main point of entry
-
-static
-void
-printkey(Input *in, Key *key)
-{
- enum MouseEvent ev;
- int button, line, col;
- int init, mode, val;
-
- switch(key->type) {
- case KeyUnicode:
- fprintf(stderr, "Unicode codepoint=U+%04dx utf8='%s'", key->code.pt, key->utf8);
- break;
- case KeyFunc:
- fprintf(stderr, "Function F%d", key->code.num);
- break;
- case KeySym:
- fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, keyname(in, key->code.sym));
- break;
- case KeyMouse:
- decodemouse(in, key, &ev, &button, &line, &col);
- fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col);
- break;
- case KeyPosition:
- decodepos(in, key, &line, &col);
- fprintf(stderr, "Position report pos=(%d,%d)\n", line, col);
- break;
- case KeyModeReport:
- decodemode(in, key, &init, &mode, &val);
- fprintf(stderr, "Mode report mode=%s %d val=%d\n", init == '?' ? "DEC" : "ANSI", mode, val);
- break;
- case KeyDCS:
- fprintf(stderr, "Device Control String");
- break;
- case KeyOSC:
- fprintf(stderr, "Operating System Control");
- break;
- case KeyUnknownCSI:
- fprintf(stderr, "unknown CSI\n");
- break;
- }
-
- mode = key->mods;
- fprintf(stderr, " mod=%s%s%s+%02x",
- (mode & ModCtrl ? "<C>" : ""),
- (mode & ModAlt ? "<A>" : ""),
- (mode & ModShift? "<S>" : ""),
- mode & ~(ModCtrl|ModAlt|ModShift));
-}
-
-int
-main()
-{
- char *name;
- Input *in;
- unibi_term *info;
- Key key;
- enum KeyEvent ev;
-
- name = getenv("TERM");
- info = unibi_from_term(name);
- in = makeinput(0, FlagSpaceSymbol, info);
-
- int n = 0; /* for debugging purposes only */
- startrecord(in);
- while ((ev = waitkey(in, &key)) != EvEOF) {
- switch (ev) {
- case EvKey:
- printkey(in, &key);
- printf("\n");
- break;
- case EvNil:
- printf("<nil>\n");
- case EvAgain:
- printf("<again>\n");
- default:
- ;
- }
- n++;
- if (n > 200)
- break;
- }
- stoprecord(in);
- freeinput(in);
-}
diff --git a/sys/cmd/dvtm/rules.mk b/sys/cmd/dvtm/rules.mk
index 5b681af..f12aa17 100644
--- a/sys/cmd/dvtm/rules.mk
+++ b/sys/cmd/dvtm/rules.mk
@@ -2,10 +2,9 @@ include share/push.mk
# Local sources
SRCS_$(d) := \
-$(d)/term.c \
-# $(d)/events.c \
-# $(d)/vt.c \
-# $(d)/dvtm.c
+$(d)/hook.c \
+$(d)/vt.c \
+$(d)/dvtm.c
# needed for additional editor target
AUX := $(d)/dvtm-editor.o
@@ -22,7 +21,7 @@ BINS_$(d) := $(DVTM) $(DVTM-ED)
include share/paths.mk
$(DVTM): TCFLAGS = -D_XOPEN_SOURCE=700 -D_XOPEN_SOURCE_EXTENDED -DNDEBUG
-$(DVTM): TCLIBS = -lunibilium $(OBJ_DIR)/libn/libn.a
+$(DVTM): TCLIBS = -lncursesw $(OBJ_DIR)/libn/libn.a
$(DVTM): $(OBJS_$(d))
$(COMPLINK)
diff --git a/sys/cmd/dvtm/term.c b/sys/cmd/dvtm/term.c
deleted file mode 100644
index cda2bbe..0000000
--- a/sys/cmd/dvtm/term.c
+++ /dev/null
@@ -1,395 +0,0 @@
-#include "term.h"
-
-struct ExtraInfo
-{
- char *enteralt;
- char *exitalt;
-
- char *entermouse;
- char *exitmouse;
-};
-
-static
-struct ExtraInfo vt200 =
-{
- .enteralt = "\e[?1049h",
- .exitalt = "\e[?1049l",
-
- .entermouse = "\e[?1049h\e[?1006l",
- .exitmouse = "\e[?1002l\e[?1006l",
-};
-
-// -----------------------------------------------------------------------
-// database lookup
-
-static
-char*
-tryinfostr(Term *t, enum unibi_string s)
-{
- char *val = (char*)unibi_get_str(t->info, s);
- /* TODO: provide fallbacks */
- return val;
-}
-
-static
-char*
-guessinfostr(Term *t, enum unibi_string s, char *guess)
-{
- char *val = (char*)unibi_get_str(t->info, s);
- if (!val)
- return guess;
- return val;
-}
-
-static
-char*
-getinfostr(Term *t, enum unibi_string s)
-{
- char *val = tryinfostr(t, s);
- if (!val)
- panicf("required term info string '%s' missing", unibi_name_str(s));
-
- return val;
-}
-
-static
-char *
-tryextrastr(Term *t, char *name)
-{
- const char *nm;
- size_t max = unibi_count_ext_str(t->info);
- for (size_t i = 0; i < max; i++) {
- nm = unibi_get_ext_str_name(t->info, i);
- if (nm && !strcmp(nm, name)) {
- return (char *)nm;
- }
- }
- return nil;
-}
-
-static
-char *
-guessextrastr(Term *t, char *name, char *guess)
-{
- char *s;
- if ((s = tryextrastr(t, name)))
- return s;
-
- return guess;
-}
-
-static
-int
-tryextrabool(Term *t, char *name)
-{
- const char *nm;
- size_t max = unibi_count_ext_bool(t->info);
- for (size_t i = 0; i < max; i++) {
- nm = unibi_get_ext_bool_name(t->info, i);
- if (nm && !strcmp(nm, name)) {
- return (int)i;
- }
- }
- return -1;
-}
-
-/* formats escape strings and writes to output */
-static void tfmt(Term *t, char *esc, int n, ...);
-
-// -----------------------------------------------------------------------
-// exported pen methods
-
-// -----------------------------------------------------------------------
-// exported term methods
-
-static
-char *
-ttmpbuf(Term *t, int len)
-{
- if (t->tmp.len >= len)
- return t->tmp.b;
-
- /* TODO: error handling */
- return (t->tmp.b = realloc(t->tmp.b, len));
-}
-
-void
-tinit(Term *t)
-{
- t->name = getenv("TERM");
- t->info = unibi_from_term(t->name);
- if (!t->info)
- panicf("could not identify terminal");
-
- t->mode.mouse = 0;
- t->mode.cursorvis = 1;
- t->mode.altscreen = 0;
-
- t->cap.colors = unibi_get_num(t->info, unibi_max_colors);
- t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase);
-
- /* TODO */
- t->input = nil;
- t->root = nil;
- t->pen = (Pen){0};
-
- /* fill in buffers */
- t->buf.c = t->buf.b;
- t->tmp.b = nil;
- t->tmp.len = 0;
-
- /* get all term info format strings */
- t->esc.cup = getinfostr(t, unibi_cursor_address);
- t->esc.vpa = tryinfostr(t, unibi_row_address);
- t->esc.hpa = tryinfostr(t, unibi_column_address);
- t->esc.cuu = getinfostr(t, unibi_parm_up_cursor);
- t->esc.cuu1 = tryinfostr(t, unibi_cursor_up);
- t->esc.cud = getinfostr(t, unibi_parm_down_cursor);
- t->esc.cud1 = tryinfostr(t, unibi_cursor_down);
- t->esc.cuf = getinfostr(t, unibi_parm_right_cursor);
- t->esc.cuf1 = tryinfostr(t, unibi_cursor_right);
- t->esc.cub = getinfostr(t, unibi_parm_left_cursor);
- t->esc.cub1 = tryinfostr(t, unibi_cursor_left);
- t->esc.ich = getinfostr(t, unibi_parm_ich);
- t->esc.ich1 = tryinfostr(t, unibi_insert_character);
- t->esc.dch = getinfostr(t, unibi_parm_dch);
- t->esc.dch1 = tryinfostr(t, unibi_delete_character);
- t->esc.il = getinfostr(t, unibi_parm_insert_line);
- t->esc.il1 = tryinfostr(t, unibi_insert_line);
- t->esc.dl = getinfostr(t, unibi_parm_delete_line);
- t->esc.dl1 = tryinfostr(t, unibi_delete_line);
- t->esc.ech = getinfostr(t, unibi_erase_chars);
- t->esc.ed2 = getinfostr(t, unibi_clear_screen);
- t->esc.stbm = getinfostr(t, unibi_change_scroll_region);
- t->esc.sgr = getinfostr(t, unibi_set_attributes);
- t->esc.sgr0 = getinfostr(t, unibi_exit_attribute_mode);
- t->esc.sgr_i0 = tryinfostr(t, unibi_exit_italics_mode);
- t->esc.sgr_i1 = tryinfostr(t, unibi_enter_italics_mode);
- t->esc.sgr_fg = getinfostr(t, unibi_set_a_foreground);
- t->esc.sgr_bg = getinfostr(t, unibi_set_a_background);
- t->esc.sm_csr = getinfostr(t, unibi_cursor_normal);
- t->esc.rm_csr = getinfostr(t, unibi_cursor_invisible);
-
- /* extensions to terminfo */
- t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
- t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
-
- /* acs characters */
- t->acs.vline = tryinfostr(t, unibi_acs_vline);
- t->acs.hline = tryinfostr(t, unibi_acs_hline);
- t->acs.plus = tryinfostr(t, unibi_acs_plus);
- t->acs.ltee = tryinfostr(t, unibi_acs_ltee);
- t->acs.rtee = tryinfostr(t, unibi_acs_rtee);
- t->acs.ttee = tryinfostr(t, unibi_acs_ttee);
- t->acs.btee = tryinfostr(t, unibi_acs_btee);
- t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner);
- t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner);
- t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner);
- t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner);
-}
-
-void
-tfini(Term *t)
-{
- if (t->mode.mouse)
- twrite(t, 0, vt200.exitmouse);
- if (!t->mode.cursorvis)
- tfmt(t, t->esc.rm_csr, 0);
- if (t->mode.altscreen)
- twrite(t, 0, vt200.exitalt);
-
- tfmt(t, t->esc.sgr0, 0);
-}
-
-void
-tflush(Term *t)
-{
- if (t->fd != -1)
- write(t->fd, t->buf.b, t->buf.c - t->buf.b);
-
- t->buf.c = t->buf.b;
-}
-
-void
-twrite(Term *t, long len, char *s)
-{
- int n;
- if (!len)
- len = strlen(s);
-
- while (len > 0) {
- n = MIN(len, arrend(t->buf.b) - t->buf.c);
- memcpy(t->buf.c, s, n);
- t->buf.c += n;
- len -= n;
- tflush(t);
- }
-}
-
-void
-tsetpen(Term *t, Pen new)
-{
- int c;
- ushort ic, in;
- Pen cur = t->pen;
- if (!memcmp(&new, &cur, sizeof(new)))
- return;
-
- /* attributes */
- tfmt(t, t->esc.sgr, 9,
- 0, /* standout */
- new.state & PenUnderline,
- new.state & PenReverse,
- new.state & PenBlink,
- new.state & PenDim,
- new.state & PenBold,
- new.state & PenInvis,
- 0, /* protect */
- 0); /* alt */
-
- ic = cur.state & PenItalic;
- in = new.state & PenItalic;
- if (ic & ~in)
- tfmt(t, t->esc.sgr_i0, 0);
- else if (~ic & in)
- tfmt(t, t->esc.sgr_i1, 0);
-
- /* fg/bg color */
- /* TODO: add a check for if the terminal supports true color */
- /* TODO: add a check for negative indices */
- if (new.state & PenTrueClr) {
- tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b);
- tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b);
- } else {
- tfmt(t, t->esc.sgr_fg, 1, new.col.fg);
- tfmt(t, t->esc.sgr_bg, 1, new.col.bg);
- }
-
- t->pen = new;
-}
-
-static
-void
-tfmt(Term *t, char *esc, int n, ...)
-{
- int i;
- long len;
- va_list args;
- unibi_var_t param[9];
- char buf[64], *c = buf;
-
- if (!esc)
- panicf("no terminfo escape string given");
-
- va_start(args, n);
- for (i = 0; i < arrlen(param) && i < n; i++)
- param[i] = unibi_var_from_num(va_arg(args, int));
- va_end(args);
-
- len = unibi_run(esc, param, c, sizeof(buf));
- if (len < arrlen(buf)) {
- c = ttmpbuf(t, len);
- unibi_run(esc, param, c, len);
- }
-
- twrite(t, len, c);
-}
-
-/* absolute move */
-int
-tgoto(Term *t, int row, int col)
-{
- if (row != -1 && col != -1)
- tfmt(t, t->esc.cup, 2, row, col);
- else if (row != -1) {
- if (!t->esc.vpa)
- return 0;
- tfmt(t, t->esc.vpa, 1, row);
- } else if (col != -1) {
- if (col == 0) {
- twrite(t, 1, "\r");
- return 1;
- }
- if (t->esc.hpa)
- tfmt(t, t->esc.hpa, 1, col);
- else if (t->esc.cuf) {
- twrite(t, 1, "\r");
- tfmt(t, t->esc.cuf, 1, col);
- } else
- return 0;
- } else
- return 0; /* unreachable */
-
- return 1;
-}
-
-/* relative move */
-void
-tjump(Term *t, int down, int right)
-{
- if (down == 1 && t->esc.cud1)
- tfmt(t, t->esc.cud1, 0);
- else if (down == -1 && t->esc.cuu1)
- tfmt(t, t->esc.cuu1, 0);
- else if (down > 0)
- tfmt(t, t->esc.cud, 1, down);
- else if (down < 0)
- tfmt(t, t->esc.cuu, 1, -down);
-
- if (right == 1 && t->esc.cuf1)
- tfmt(t, t->esc.cuf1, 0);
- else if (right == -1 && t->esc.cub1)
- tfmt (t, t->esc.cub1, 0);
- else if (right > 0)
- tfmt(t, t->esc.cuf, 1, right);
- else if( right < 0)
- tfmt(t, t->esc.cub, 1, -right);
-}
-
-void
-tdel(Term *t, int num)
-{
- char *c, buf[64];
- if (num < 1)
- return;
-
- /* TODO: allow for not moving */
- if (t->cap.bce) {
- tfmt(t, t->esc.ech, 1, num);
- tjump(t, 0, num);
- } else {
- c = buf;
- memset(c, ' ', arrlen(buf));
- while(num > 64) {
- twrite(t, arrlen(buf), c);
- num -= arrlen(buf);
- }
- twrite(t, num, c);
- }
-}
-
-void
-tclear(Term *t)
-{
- tfmt(t, t->esc.ed2, 0);
-}
-
-// -----------------------------------------------------------------------
-// testing entry
-
-int
-main()
-{
- Term t;
- tinit(&t);
-
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg);
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg);
- tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100);
- tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100);
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, t.acs.hline);
-
- tfini(&t);
-}
diff --git a/sys/cmd/dvtm/term.h b/sys/cmd/dvtm/term.h
deleted file mode 100644
index 53c0850..0000000
--- a/sys/cmd/dvtm/term.h
+++ /dev/null
@@ -1,481 +0,0 @@
-#pragma once
-
-#include <u.h>
-#include <libn.h>
-
-#include <termios.h>
-#include <unibilium.h>
-
-#define iota(x) 1 << (x)
-/*
- * obtained from:
- * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html
- */
-#define ACSRUNES \
- /* name utf8 ascii acsc*/ \
- ACSRUNE("block", 0x25ae, '#', '0') \
- ACSRUNE("board", 0x2592, '#', 'h') \
- ACSRUNE("btee", 0x2534, '+', 'v') \
- ACSRUNE("bullet", 0x00b7, 'o', '~') \
- ACSRUNE("ckboard", 0x2592, ':', 'a') \
- ACSRUNE("darrow", 0x2193, 'v', '.') \
- ACSRUNE("degree", 0x00b0, '\'','f') \
- ACSRUNE("diamond", 0x25c6, '+', '`') \
- ACSRUNE("gequal", 0x2265, '>', '>') \
- ACSRUNE("hline", 0x2500, '-', 'q') \
- ACSRUNE("antern", 0x2603, '#', 'i') \
- ACSRUNE("larrow", 0x2190, '<', ',') \
- ACSRUNE("lequal", 0x2264, '<', 'y') \
- ACSRUNE("llcorner", 0x2514, '+', 'm') \
- ACSRUNE("lrcorner", 0x2518, '+', 'j') \
- ACSRUNE("ltee", 0x2524, '+', 't') \
- ACSRUNE("nequal", 0x2260, '!', '|') \
- ACSRUNE("pi", 0x03c0, '*', '{') \
- ACSRUNE("plminus", 0x00b1, '#', 'g') \
- ACSRUNE("plus", 0x253c, '+', 'n') \
- ACSRUNE("rarrow", 0x2192, '>', '+') \
- ACSRUNE("rtee", 0x251c, '+', 'u') \
- ACSRUNE("s1", 0x23ba, '-', 'o') \
- ACSRUNE("s3", 0x23bb, '-', 'p') \
- ACSRUNE("s7", 0x23bc, '-', 'r') \
- ACSRUNE("s9", 0x23bd, '_', 's') \
- ACSRUNE("sterling", 0x00a3, 'f', '}') \
- ACSRUNE("ttee", 0x252c, '+', 'w') \
- ACSRUNE("uarrow", 0x2191, '^', '-') \
- ACSRUNE("ulcorner", 0x250c, '+', 'l') \
- ACSRUNE("urcorner", 0x2510, '+', 'k') \
- ACSRUNE("vline", 0x2502, '|', 'x') \
- /* thick versions */ \
- ACSRUNE("t_btee", 0x253b, '+', 'V') \
- ACSRUNE("t_hline", 0x2501, '-', 'Q') \
- ACSRUNE("t_llcorner", 0x2517, '+', 'M') \
- ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \
- ACSRUNE("t_ltee", 0x252b, '+', 'T') \
- ACSRUNE("t_plus", 0x254b, '+', 'N') \
- ACSRUNE("t_rtee", 0x2523, '+', 'U') \
- ACSRUNE("t_ttee", 0x2533, '+', 'W') \
- ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \
- ACSRUNE("t_urcorner", 0x2513, '+', 'K') \
- ACSRUNE("t_vline", 0x2503, '|', 'X') \
- /* double version */ \
- ACSRUNE("d_btee", 0x2569, '+', 'H') \
- ACSRUNE("d_hline", 0x2550, '-', 'R') \
- ACSRUNE("d_llcorner", 0x255a, '+', 'D') \
- ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \
- ACSRUNE("d_ltee", 0x2560, '+', 'F') \
- ACSRUNE("d_plus", 0x256c, '+', 'E') \
- ACSRUNE("d_rtee", 0x2563, '+', 'G') \
- ACSRUNE("d_ttee", 0x2566, '+', 'I') \
- ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \
- ACSRUNE("d_urcorner", 0x2557, '+', 'B') \
- ACSRUNE("d_vline", 0x2551, '|', 'Y')
-
-/* enums */
-
-/* key symbols */
-enum
-{
- SymUnknown = -1,
- SymNone = 0,
-
- /* special names in c0 */
- SymBackspace, SymTab, SymEnter, SymEscape,
-
- /* special names in g0 */
- SymSpace, SymDel,
-
- /* special keys */
- SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert,
- SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd,
-
- /* special keys from terminfo */
- SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit,
- SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions,
- SymPrint, SymRedo, SymReference, SymRefresh, SymReplace,
- SymRestart, SymResume, SymSave, SymSuspend, SymUndo,
-
- /* numeric keypad special keys */
- SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8,
- SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma,
- SymKpperiod, SymKpequals,
-
- /* et cetera ad nauseum */
- NumSyms
-};
-
-/* key type */
-enum
-{
- KeyUnicode,
- KeyFunc,
- KeySym,
- KeyMouse,
- KeyPosition,
- KeyModeReport,
- KeyDCS,
- KeyOSC,
- /* add other recognised types here */
-
- KeyUnknownCSI = -1
-};
-
-/* key events */
-enum KeyEvent
-{
- EvNil,
- EvKey,
- EvEOF,
- EvAgain,
- EvErr,
-};
-
-enum MouseEvent
-{
- MouseNil,
- MousePress,
- MouseDrag,
- MouseRelease,
-};
-
-enum
-{
- ModShift = iota(0),
- ModAlt = iota(1),
- ModCtrl = iota(2),
-};
-
-enum
-{
- FlagNoInterpret = iota(0),
- FlagConvertKP = iota(1),
- FlagRaw = iota(2),
- FlagUTF8 = iota(3),
- FlagNoTermIOS = iota(4),
- FlagSpaceSymbol = iota(5),
- FlagCtrlC = iota(6),
- FlagEintr = iota(7),
-};
-
-enum
-{
- HarmonizeSpace = iota(0),
- HarmonizeDelBS = iota(1),
-};
-
-/* types */
-typedef struct RGB8 RGB8;
-typedef struct Pen Pen;
-
-typedef struct Cell Cell;
-typedef struct Row Row;
-typedef struct Buffer Buffer;
-typedef struct Window Window;
-
-typedef struct Node Node;
-typedef struct Key Key;
-typedef struct Input Input;
-
-typedef struct Term Term;
-
-struct RGB8
-{
- uint8 r, g, b;
-};
-
-enum
-{
- PenNormal = 0,
- PenBold = iota(0),
- PenDim = iota(1),
- PenInvis = iota(2),
- PenItalic = iota(3),
- PenReverse = iota(4),
- PenStrike = iota(5),
- PenUnderline = iota(6),
- PenBlink = iota(7),
- /* ... */
- PenTrueClr = iota(15),
-};
-
-struct Pen
-{
- ushort state;
- union {
- /* 256 color (legacy) */
- struct {
- sshort fg : 8, bg : 8; /* 0 - 255 or COLOUR_DEFAULT */
- } col;
- /* true color (modern) */
- struct {
- RGB8 fg, bg;
- } rgb;
- };
-};
-
-/* outputs */
-struct Cell {
- rune r;
- Pen pen;
-};
-
-struct Row {
- Cell *cells;
- uint dirty:1;
-};
-
-/* Buffer holding the current window content (as an array) as well
- * as the scroll back buffer content (as a circular/ring buffer).
- *
- * If new content is added to terminal the view port slides down and the
- * previously top most line is moved into the scroll back buffer at postion
- * scroll.index. This index will eventually wrap around and thus overwrite
- * the oldest lines.
- *
- * In the scenerio below a scroll up has been performed. That is 'scroll.above'
- * lines still lie above the current view port. Further scrolling up will show
- * them. Similarly 'scroll.below' is the amount of lines below the current
- * viewport.
- *
- * The function buffer_boundary sets the row pointers to the start/end range
- * of the section delimiting the region before/after the viewport. The functions
- * buffer_row_{first,last} return the first/last logical row. And
- * buffer_row_{next,prev} allows to iterate over the logical lines in either
- * direction.
- *
- * scroll back buffer
- *
- * scroll_buf->+----------------+-----+
- * | | | ^ \
- * | before | | | |
- * current terminal content | viewport | | | |
- * | | | |
- * +----------------+-----+\ | | | s > scroll.above
- * ^ | | i | \ | | i | c |
- * | | | n | \ | | n | r |
- * | | v | \ | | v | o |
- * r | | i | \ | | i | l /
- * o | viewport | s | >|<- scroll.index | s | l \
- * w | | i | / | | i | |
- * s | | b | / | after | b | s > scroll.below
- * | | l | / | viewport | l | i |
- * v | | e | / | | e | z /
- * +----------------+-----+/ | unused | | e
- * <- maxcols -> | scroll back | |
- * <- cols -> | buffer | | |
- * | | | |
- * | | | v
- * roll_buf + scroll.size->+----------------+-----+
- * <- maxcols ->
- * <- cols ->
- */
-
-struct Buffer {
- Row *row; /* array of Row pointers of size 'rows' */
- Row *crow; /* row on which the cursor currently resides */
- bool *tabs; /* a boolean flag for each column whether it is a tab */
- struct {
- Row *buf; /* a ring buffer holding the scroll back content */
- Row *top; /* row in lines where scrolling region starts */
- Row *bot; /* row in lines where scrolling region ends */
- int size; /* maximal capacity of scroll back buffer (in lines) */
- int index; /* current index into the ring buffer */
- int above; /* number of lines above current viewport */
- int below; /* number of lines below current viewport */
- } scroll;
- Pen pen, spen;
- int nrow, ncol; /* current dimension of buffer */
- int maxcols; /* allocated cells (maximal cols over time) */
- int srow, scol; /* saved cursor row/column (zero based) */
-};
-
-/* input */
-struct Key
-{
- int type;
- int mods;
- uchar utf8[UTFmax+1];
- union {
- rune pt;
- int num;
- int sym;
- char mouse[4];
- } code;
-};
-
-struct KeyInfo
-{
- int type;
- int sym;
- int modmask;
- int modset;
-};
-
-/* make opaque? */
-struct Input
-{
- int fd;
- int flags;
- int hflag;
- int wait; /* in ms */
-
- struct termios oldterm;
-
- /* buffer */
- struct {
- long off;
- uchar *b, *c, *e, bytes[256];
- } buf;
-
- /* modifiers */
- char closed : 1;
- char started : 1;
- char hasold : 1;
-
- /* key data */
- Node *keys;
- struct KeyInfo c0[32];
-
- char **keynm;
- int nkeynm;
-
- int nsavedcsi;
- char *savedcsi;
-};
-
-struct Term
-{
- /* meta data */
- char *name;
- unibi_term *info;
- struct {
- uint altscreen : 1;
- uint cursorvis : 1;
- uint mouse : 1;
- } mode;
- struct {
- uint bce : 1;
- int colors;
- } cap;
-
- /* input capture */
- Input *input;
-
- /* output display */
- Window *root;
- Pen pen;
-
- /* output raw text */
- int fd;
- struct {
- char *c, b[512];
- } buf;
-
- struct {
- int len;
- char *b;
- } tmp;
-
- /* info */
- struct {
- /* Positioning */
- char *cup; // cursor_address
- char *vpa; // row_address == vertical position absolute
- char *hpa; // column_address = horizontal position absolute
-
- /* Moving */
- char *cuu; char *cuu1; // Cursor Up
- char *cud; char *cud1; // Cursor Down
- char *cuf; char *cuf1; // Cursor Forward == Right
- char *cub; char *cub1; // Cursor Backward == Left
-
- /* Editing */
- char *ich; char *ich1; // Insert Character
- char *dch; char *dch1; // Delete Character
- char *il; char *il1; // Insert Line
- char *dl; char *dl1; // Delete Line
- char *ech; // Erase Character
- char *ed2; // Erase Data 2 == Clear screen
- char *stbm; // Set Top/Bottom Margins
-
- /* formatting */
- char *sgr; // Select Graphic Rendition
- char *sgr0; // Exit Attribute Mode
- char *sgr_i0, *sgr_i1; // SGR italic off/on
- char *sgr_fg; // SGR foreground colour
- char *sgr_bg; // SGR background colour
-
- /* Mode setting/clearing */
- char *sm_csr; char *rm_csr; // Set/reset mode: Cursor visible
-
- /* augmentations to terminfo */
- struct {
- char *rgbf; // rgb foreground
- char *rgbb; // rgb background
- char *smxx; // strikethrough
- char *smulx; // curly underline
- } ext;
- } esc;
-
- /* basic shapes */
- struct {
- rune block;
- rune board;
- rune hline;
- rune vline;
- rune plus;
- rune ltee;
- rune rtee;
- rune ttee;
- rune btee;
- rune ulcorner;
- rune urcorner;
- rune llcorner;
- rune lrcorner;
- } acs;
-};
-
-/* exported functions */
-
-/* buffer.c */
-void zero(Row *row, int start, int len);
-void roll(Row *start, Row *end, int count);
-
-void bclear(Buffer *b);
-void bfree(Buffer *b);
-void bscroll(Buffer *b, int s);
-void bresize(Buffer *b, int rows, int cols);
-bool binit(Buffer *b, int rows, int cols, int size);
-void brender(Buffer *b, Term *t);
-void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ;
-Row *browfirst(Buffer *b);
-Row *browlast(Buffer *b);
-Row *brownext(Buffer *b, Row *row);
-Row *bprevrow(Buffer *b, Row *row);
-
-/* input.c */
-Input *makeinput(int fd, int flags, unibi_term *info);
-void freeinput(Input *in);
-int startrecord(Input *in);
-int stoprecord(Input *in);
-char *keyname(Input *in, int sym);
-
-enum KeyEvent waitkey(Input *in, Key *key); /* block until next keypress */
-enum KeyEvent getkey(Input *in, Key *key); /* grab key if we can */
-enum KeyEvent demandkey(Input *in, Key *key); /* grab now and interpret as best we can */
-enum KeyEvent isreadablekey(Input *in); /* reads from input and reports the event w/o advance */
-
-/* unpack key event into useful data */
-enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col);
-enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col);
-enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val);
-
-/* term.c */
-void tinit(Term *t);
-void tfini(Term *t);
-void tflush(Term *t);
-void twrite(Term *t, long len, char *s);
-int tgoto(Term *t, int row, int col);
-void tjump(Term *t, int down, int right);
-void tdel(Term *t, int num);
-void tsetpen(Term *t, Pen pen);
-void tclear(Term *t);
diff --git a/sys/cmd/dvtm/vt.c b/sys/cmd/dvtm/vt.c
index 4e1f427..aff4ab0 100644
--- a/sys/cmd/dvtm/vt.c
+++ b/sys/cmd/dvtm/vt.c
@@ -9,8 +9,6 @@
#include <sys/ioctl.h>
#include <sys/types.h>
-#include "term.h"
-
#if defined(__linux__) || defined(__CYGWIN__)
# include <pty.h>
#elif defined(__FreeBSD__) || defined(__DragonFly__)
@@ -19,121 +17,609 @@
# include <util.h>
#endif
+#include "vt.h"
+
+#ifndef NCURSES_ATTR_SHIFT
+# define NCURSES_ATTR_SHIFT 8
+#endif
+
+#ifndef NCURSES_ACS
+# ifdef PDCURSES
+# define NCURSES_ACS(c) (acs_map[(uchar)(c)])
+# else /* BSD curses */
+# define NCURSES_ACS(c) (_acs_map[(uchar)(c)])
+# endif
+#endif
+
+#ifdef NCURSES_VERSION
+
+#ifndef NCURSES_EXT_COLORS
+# define NCURSES_EXT_COLORS 0
+#endif
+
+#if !NCURSES_EXT_COLORS
+# define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256)
+#endif
+
+#endif
+
+#ifndef MAX_COLOR_PAIRS
+# define MAX_COLOR_PAIRS COLOR_PAIRS
+#endif
+
+#if defined _AIX && defined CTRL
+# undef CTRL
+#endif
+#ifndef CTRL
+# define CTRL(k) ((k) & 0x1F)
+#endif
+
#define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
-typedef struct Vt Vt;
+static bool is_utf8, has_default_colors;
+static int color_pairs_reserved, color_pairs_max, color_pair_current;
+static short *color2palette, default_fg, default_bg;
+static char vt_term[32];
+
+typedef struct {
+ wchar_t text;
+ attr_t attr;
+ union {
+ struct {
+ short fg;
+ short bg;
+ };
+ struct {
+ uint8 r;
+ uint8 g;
+ uint8 b;
+ };
+ };
+} Cell;
+
+typedef struct {
+ Cell *cells;
+ uint dirty:1;
+} Row;
+
+/* Buffer holding the current terminal window content (as an array) as well
+ * as the scroll back buffer content (as a circular/ring buffer).
+ *
+ * If new content is added to terminal the view port slides down and the
+ * previously top most line is moved into the scroll back buffer at postion
+ * scroll_index. This index will eventually wrap around and thus overwrite
+ * the oldest lines.
+ *
+ * In the scenerio below a scroll up has been performed. That is 'scroll_above'
+ * lines still lie above the current view port. Further scrolling up will show
+ * them. Similarly 'scroll_below' is the amount of lines below the current
+ * viewport.
+ *
+ * The function buffer_boundary sets the row pointers to the start/end range
+ * of the section delimiting the region before/after the viewport. The functions
+ * buffer_row_{first,last} return the first/last logical row. And
+ * buffer_row_{next,prev} allows to iterate over the logical lines in either
+ * direction.
+ *
+ * scroll back buffer
+ *
+ * scroll_buf->+----------------+-----+
+ * | | | ^ \
+ * | before | | | |
+ * current terminal content | viewport | | | |
+ * | | | |
+ * +----------------+-----+\ | | | s > scroll_above
+ * ^ | | i | \ | | i | c |
+ * | | | n | \ | | n | r |
+ * | | v | \ | | v | o |
+ * r | | i | \ | | i | l /
+ * o | viewport | s | >|<- scroll_index | s | l \
+ * w | | i | / | | i | |
+ * s | | b | / | after | b | s > scroll_below
+ * | | l | / | viewport | l | i |
+ * v | | e | / | | e | z /
+ * +----------------+-----+/ | unused | | e
+ * <- maxcols -> | scroll back | |
+ * <- cols -> | buffer | | |
+ * | | | |
+ * | | | v
+ * scroll_buf + scroll_size->+----------------+-----+
+ * <- maxcols ->
+ * <- cols ->
+ */
+typedef struct {
+ Row *lines; /* array of Row pointers of size 'rows' */
+ Row *curs_row; /* row on which the cursor currently resides */
+ Row *scroll_buf; /* a ring buffer holding the scroll back content */
+ Row *scroll_top; /* row in lines where scrolling region starts */
+ Row *scroll_bot; /* row in lines where scrolling region ends */
+ bool *tabs; /* a boolean flag for each column whether it is a tab */
+ int scroll_size; /* maximal capacity of scroll back buffer (in lines) */
+ int scroll_index; /* current index into the ring buffer */
+ int scroll_above; /* number of lines above current viewport */
+ int scroll_below; /* number of lines below current viewport */
+ int rows, cols; /* current dimension of buffer */
+ int maxcols; /* allocated cells (maximal cols over time) */
+ attr_t curattrs, savattrs; /* current and saved attributes for cells */
+ int curs_col; /* current cursor column (zero based) */
+ int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */
+ short curfg, curbg; /* current fore and background colors */
+ short savfg, savbg; /* saved colors */
+} Buffer;
struct Vt {
- Buffer buf[2]; /* normal & alternative screen buffer */
- Buffer *buffer; /* currently active buffer (one of the above) */
- Pen pen; /* default pen */
- int pty; /* master side pty file descriptor */
- pid_t pid; /* process id of the process running in this vt */
+ Buffer buffer_normal; /* normal screen buffer */
+ Buffer buffer_alternate; /* alternate screen buffer */
+ Buffer *buffer; /* currently active buffer (one of the above) */
+ attr_t defattrs; /* attributes to use for normal/empty cells */
+ int deffg, defbg; /* colors to use for back normal/empty cells (white/black) */
+ int pty; /* master side pty file descriptor */
+ pid_t pid; /* process id of the process running in this vt */
/* flags */
- char title[256]; /* xterm style window title */
- void *data; /* user supplied data */
- uint seen_input : 1;
- uint insert : 1;
- uint escaped : 1;
- uint curshid : 1;
- uint curskeymode : 1;
- uint bell : 1;
- uint relposmode : 1;
- uint mousetrack : 1;
- uint graphmode : 1;
- uint savgraphmode : 1;
+ uint seen_input:1;
+ uint insert:1;
+ uint escaped:1;
+ uint curshid:1;
+ uint curskeymode:1;
+ uint bell:1;
+ uint relposmode:1;
+ uint mousetrack:1;
+ uint graphmode:1;
+ uint savgraphmode:1;
bool charsets[2];
/* buffers and parsing state */
char rbuf[BUFSIZ];
char ebuf[BUFSIZ];
uint rlen, elen;
- int srow, scol; /* last known offset to display start row, start column */
+ int srow, scol; /* last known offset to display start row, start column */
+ char title[256]; /* xterm style window title */
+ vt_title_handler_t title_handler; /* hook which is called when title changes */
+ vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */
+ void *data; /* user supplied data */
};
-/* forward declares */
-
-void vt·noscroll(Vt *t);
-void vt·dirty(Vt *t);
-void vt·scroll(Vt *t, int rows);
+static const char *keytable[KEY_MAX+1] = {
+ [KEY_ENTER] = "\r",
+ ['\n'] = "\n",
+ /* for the arrow keys the CSI / SS3 sequences are not stored here
+ * because they depend on the current cursor terminal mode
+ */
+ [KEY_UP] = "A",
+ [KEY_DOWN] = "B",
+ [KEY_RIGHT] = "C",
+ [KEY_LEFT] = "D",
+#ifdef KEY_SUP
+ [KEY_SUP] = "\e[1;2A",
+#endif
+#ifdef KEY_SDOWN
+ [KEY_SDOWN] = "\e[1;2B",
+#endif
+ [KEY_SRIGHT] = "\e[1;2C",
+ [KEY_SLEFT] = "\e[1;2D",
+ [KEY_BACKSPACE] = "\177",
+ [KEY_IC] = "\e[2~",
+ [KEY_DC] = "\e[3~",
+ [KEY_PPAGE] = "\e[5~",
+ [KEY_NPAGE] = "\e[6~",
+ [KEY_HOME] = "\e[7~",
+ [KEY_END] = "\e[8~",
+ [KEY_BTAB] = "\e[Z",
+ [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */
+ [KEY_F(1)] = "\e[11~",
+ [KEY_F(2)] = "\e[12~",
+ [KEY_F(3)] = "\e[13~",
+ [KEY_F(4)] = "\e[14~",
+ [KEY_F(5)] = "\e[15~",
+ [KEY_F(6)] = "\e[17~",
+ [KEY_F(7)] = "\e[18~",
+ [KEY_F(8)] = "\e[19~",
+ [KEY_F(9)] = "\e[20~",
+ [KEY_F(10)] = "\e[21~",
+ [KEY_F(11)] = "\e[23~",
+ [KEY_F(12)] = "\e[24~",
+ [KEY_F(13)] = "\e[23~",
+ [KEY_F(14)] = "\e[24~",
+ [KEY_F(15)] = "\e[25~",
+ [KEY_F(16)] = "\e[26~",
+ [KEY_F(17)] = "\e[28~",
+ [KEY_F(18)] = "\e[29~",
+ [KEY_F(19)] = "\e[31~",
+ [KEY_F(20)] = "\e[32~",
+ [KEY_F(21)] = "\e[33~",
+ [KEY_F(22)] = "\e[34~",
+ [KEY_RESIZE] = "",
+#ifdef KEY_EVENT
+ [KEY_EVENT] = "",
+#endif
+};
static void puttab(Vt *t, int count);
-static void process_nonprinting(Vt *t, rune r);
-static void sendcurs(Vt *t);
+static void process_nonprinting(Vt *t, wchar_t wc);
+static void send_curs(Vt *t);
-/* globals */
-static int isutf8;
-static char vtname[36];
+const static
+attr_t
+build_attrs(attr_t curattrs)
+{
+ return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) >> NCURSES_ATTR_SHIFT;
+}
static
void
-cclamp(Vt *t)
+row_set(Row *row, int start, int len, Buffer *t)
{
- Buffer *b = t->buffer;
- Row *lines = t->relposmode ? b->scroll.top : b->lines;
- int rows = t->relposmode ? b->scroll.bot - b->scroll.top : b->rows;
+ Cell cell = {
+ .text = L'\0',
+ .attr = t ? build_attrs(t->curattrs) : 0,
+ .fg = t ? t->curfg : -1,
+ .bg = t ? t->curbg : -1,
+ };
- if (b->crow < lines)
- b->crow = lines;
- if (b->crow >= lines + rows)
- b->crow = lines + rows - 1;
- if (b->ccol < 0)
- b->ccol = 0;
- if (b->ccol >= b->cols)
- b->ccol = b->cols - 1;
+ for (int i = start; i < len + start; i++)
+ row->cells[i] = cell;
+ row->dirty = true;
}
static
void
-clinedown(Vt *t)
+row_roll(Row *start, Row *end, int count)
{
- Buffer *b = t->buffer;
- zero(b->crow, b->cols, b->maxcols - b->cols);
- b->crow++;
- if (b->crow < b->scroll.bot)
+ int n = end - start;
+
+ count %= n;
+ if (count < 0)
+ count += n;
+
+ if (count) {
+ char buf[count * sizeof(Row)];
+ memcpy(buf, start, count * sizeof(Row));
+ memmove(start, start + count, (n - count) * sizeof(Row));
+ memcpy(end - count, buf, count * sizeof(Row));
+ for (Row *row = start; row < end; row++)
+ row->dirty = true;
+ }
+}
+
+static
+void
+buffer_clear(Buffer *b)
+{
+ Cell cell = {
+ .text = L'\0',
+ .attr = A_NORMAL,
+ .fg = -1,
+ .bg = -1,
+ };
+
+ for (int i = 0; i < b->rows; i++) {
+ Row *row = b->lines + i;
+ for (int j = 0; j < b->cols; j++) {
+ row->cells[j] = cell;
+ row->dirty = true;
+ }
+ }
+}
+
+static
+void
+buffer_free(Buffer *b)
+{
+ for (int i = 0; i < b->rows; i++)
+ free(b->lines[i].cells);
+ free(b->lines);
+ for (int i = 0; i < b->scroll_size; i++)
+ free(b->scroll_buf[i].cells);
+ free(b->scroll_buf);
+ free(b->tabs);
+}
+
+static
+void
+buffer_scroll(Buffer *b, int s)
+{
+ /* work in screenfuls */
+ int ssz = b->scroll_bot - b->scroll_top;
+ if (s > ssz) {
+ buffer_scroll(b, ssz);
+ buffer_scroll(b, s - ssz);
+ return;
+ }
+ if (s < -ssz) {
+ buffer_scroll(b, -ssz);
+ buffer_scroll(b, s + ssz);
return;
+ }
+
+ b->scroll_above += s;
+ if (b->scroll_above >= b->scroll_size)
+ b->scroll_above = b->scroll_size;
+
+ if (s > 0 && b->scroll_size) {
+ for (int i = 0; i < s; i++) {
+ Row tmp = b->scroll_top[i];
+ b->scroll_top[i] = b->scroll_buf[b->scroll_index];
+ b->scroll_buf[b->scroll_index] = tmp;
+
+ b->scroll_index++;
+ if (b->scroll_index == b->scroll_size)
+ b->scroll_index = 0;
+ }
+ }
+ row_roll(b->scroll_top, b->scroll_bot, s);
+ if (s < 0 && b->scroll_size) {
+ for (int i = (-s) - 1; i >= 0; i--) {
+ b->scroll_index--;
+ if (b->scroll_index == -1)
+ b->scroll_index = b->scroll_size - 1;
+
+ Row tmp = b->scroll_top[i];
+ b->scroll_top[i] = b->scroll_buf[b->scroll_index];
+ b->scroll_buf[b->scroll_index] = tmp;
+ b->scroll_top[i].dirty = true;
+ }
+ }
+}
+
+static
+void
+buffer_resize(Buffer *b, int rows, int cols)
+{
+ Row *lines = b->lines;
+
+ if (b->rows != rows) {
+ if (b->curs_row >= lines + rows) {
+ /* scroll up instead of simply chopping off bottom */
+ buffer_scroll(b, (b->curs_row - b->lines) - rows + 1);
+ }
+ while (b->rows > rows) {
+ free(lines[b->rows - 1].cells);
+ b->rows--;
+ }
+
+ lines = realloc(lines, sizeof(Row) * rows);
+ }
+
+ if (b->maxcols < cols) {
+ for (int row = 0; row < b->rows; row++) {
+ lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols);
+ if (b->cols < cols)
+ row_set(lines + row, b->cols, cols - b->cols, nil);
+ lines[row].dirty = true;
+ }
+ Row *sbuf = b->scroll_buf;
+ for (int row = 0; row < b->scroll_size; row++) {
+ sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols);
+ if (b->cols < cols)
+ row_set(sbuf + row, b->cols, cols - b->cols, nil);
+ }
+ b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols);
+ for (int col = b->cols; col < cols; col++)
+ b->tabs[col] = !(col & 7);
+ b->maxcols = cols;
+ b->cols = cols;
+ } else if (b->cols != cols) {
+ for (int row = 0; row < b->rows; row++)
+ lines[row].dirty = true;
+ b->cols = cols;
+ }
+
+ int deltarows = 0;
+ if (b->rows < rows) {
+ while (b->rows < rows) {
+ lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell));
+ row_set(lines + b->rows, 0, b->maxcols, b);
+ b->rows++;
+ }
+
+ /* prepare for backfill */
+ if (b->curs_row >= b->scroll_bot - 1) {
+ deltarows = b->lines + rows - b->curs_row - 1;
+ if (deltarows > b->scroll_above)
+ deltarows = b->scroll_above;
+ }
+ }
+
+ b->curs_row += lines - b->lines;
+ b->scroll_top = lines;
+ b->scroll_bot = lines + rows;
+ b->lines = lines;
+
+ /* perform backfill */
+ if (deltarows > 0) {
+ buffer_scroll(b, -deltarows);
+ b->curs_row += deltarows;
+ }
+}
+
+static
+bool
+buffer_init(Buffer *b, int rows, int cols, int scroll_size)
+{
+ b->curattrs = A_NORMAL; /* white text over black background */
+ b->curfg = b->curbg = -1;
+ if (scroll_size < 0)
+ scroll_size = 0;
+ if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row))))
+ return false;
+ b->scroll_size = scroll_size;
+ buffer_resize(b, rows, cols);
+ return true;
+}
+
+static
+void
+buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
+{
+ if (bs)
+ *bs = nil;
+ if (be)
+ *be = nil;
+ if (as)
+ *as = nil;
+ if (ae)
+ *ae = nil;
+ if (!b->scroll_size)
+ return;
+
+ if (b->scroll_above) {
+ if (bs)
+ *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size];
+ if (be)
+ *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size];
+ }
+ if (b->scroll_below) {
+ if (as)
+ *as = &b->scroll_buf[b->scroll_index];
+ if (ae)
+ *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size];
+ }
+}
- vt·noscroll(t);
+static
+Row *
+buffer_row_first(Buffer *b) {
+ Row *bstart;
+ if (!b->scroll_size || !b->scroll_above)
+ return b->lines;
+ buffer_boundry(b, &bstart, nil, nil, nil);
+ return bstart;
+}
- b->crow = b->scroll.bot - 1;
- bscroll(b, 1);
- zero(b->crow, 0, b->cols);
+static
+Row *
+buffer_row_last(Buffer *b) {
+ Row *aend;
+ if (!b->scroll_size || !b->scroll_below)
+ return b->lines + b->rows - 1;
+ buffer_boundry(b, nil, nil, nil, &aend);
+ return aend;
}
-
+
+static
+Row *
+buffer_row_next(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->lines, *last = b->lines + b->rows - 1;
+
+ if (!row)
+ return nil;
+
+ buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row >= first && row < last)
+ return ++row;
+ if (row == last)
+ return after_start;
+ if (row == before_end)
+ return first;
+ if (row == after_end)
+ return nil;
+ if (row == &b->scroll_buf[b->scroll_size - 1])
+ return b->scroll_buf;
+ return ++row;
+}
+
+static
+Row *
+buffer_row_prev(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->lines, *last = b->lines + b->rows - 1;
+
+ if (!row)
+ return nil;
+
+ buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row > first && row <= last)
+ return --row;
+ if (row == first)
+ return before_end;
+ if (row == before_start)
+ return nil;
+ if (row == after_start)
+ return last;
+ if (row == b->scroll_buf)
+ return &b->scroll_buf[b->scroll_size - 1];
+ return --row;
+}
+
static
void
-csave(Vt *t)
+cursor_clamp(Vt *t)
{
Buffer *b = t->buffer;
- b->scrow = b->crow - b->lines;
- b->sccol = b->ccol;
+ Row *lines = t->relposmode ? b->scroll_top : b->lines;
+ int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows;
+
+ if (b->curs_row < lines)
+ b->curs_row = lines;
+ if (b->curs_row >= lines + rows)
+ b->curs_row = lines + rows - 1;
+ if (b->curs_col < 0)
+ b->curs_col = 0;
+ if (b->curs_col >= b->cols)
+ b->curs_col = b->cols - 1;
}
static
void
-crestore(Vt *t)
+cursor_line_down(Vt *t)
{
Buffer *b = t->buffer;
- b->crow = b->lines + b->scrow;
- b->ccol = b->sccol;
- cclamp(t);
+ row_set(b->curs_row, b->cols, b->maxcols - b->cols, nil);
+ b->curs_row++;
+ if (b->curs_row < b->scroll_bot)
+ return;
+
+ vt_noscroll(t);
+
+ b->curs_row = b->scroll_bot - 1;
+ buffer_scroll(b, 1);
+ row_set(b->curs_row, 0, b->cols, b);
}
static
void
-savepen(Vt *t)
+cursor_save(Vt *t)
{
Buffer *b = t->buffer;
- b->spen = b->pen;
+ b->curs_srow = b->curs_row - b->lines;
+ b->curs_scol = b->curs_col;
}
static
void
-loadpen(Vt *t)
+cursor_restore(Vt *t)
{
Buffer *b = t->buffer;
- b->pen = b->spen;
+ b->curs_row = b->lines + b->curs_srow;
+ b->curs_col = b->curs_scol;
+ cursor_clamp(t);
}
+static
+void
+attributes_save(Vt *t)
+{
+ Buffer *b = t->buffer;
+ b->savattrs = b->curattrs;
+ b->savfg = b->curfg;
+ b->savbg = b->curbg;
+ t->savgraphmode = t->graphmode;
+}
+
+static
+void
+attributes_restore(Vt *t)
+{
+ Buffer *b = t->buffer;
+ b->curattrs = b->savattrs;
+ b->curfg = b->savfg;
+ b->curbg = b->savbg;
+ t->graphmode = t->savgraphmode;
+}
static
void
@@ -170,85 +656,89 @@ interpret_csi_sgr(Vt *t, int param[], int pcount)
Buffer *b = t->buffer;
if (pcount == 0) {
/* special case: reset attributes */
- b->pen.state = PenNormal;
- b->pen.col.fg = b->pen.col.bg = -1;
+ b->curattrs = A_NORMAL;
+ b->curfg = b->curbg = -1;
return;
}
for (int i = 0; i < pcount; i++) {
switch (param[i]) {
case 0:
- b->pen.state = PenNormal;
- b->pen.col.fg = b->pen.col.bg = -1;
+ b->curattrs = A_NORMAL;
+ b->curfg = b->curbg = -1;
break;
case 1:
- b->pen.state |= PenBold;
+ b->curattrs |= A_BOLD;
break;
case 2:
- b->pen.state |= PenDim;
+ b->curattrs |= A_DIM;
break;
+#ifdef A_ITALIC
case 3:
- b->pen.state |= PenItalic;
+ b->curattrs |= A_ITALIC;
break;
+#endif
case 4:
- b->pen.state |= PenUnderline;
+ b->curattrs |= A_UNDERLINE;
break;
case 5:
- b->pen.state |= PenBlink;
+ b->curattrs |= A_BLINK;
break;
case 7:
- b->pen.state |= PenReverse;
+ b->curattrs |= A_REVERSE;
break;
case 8:
- b->pen.state |= PenInvis;
+ b->curattrs |= A_INVIS;
break;
case 22:
- b->pen.state &= ~(PenBold | PenDim);
+ b->curattrs &= ~(A_BOLD | A_DIM);
break;
+#ifdef A_ITALIC
case 23:
- b->pen.state &= ~PenItalic;
+ b->curattrs &= ~A_ITALIC;
break;
+#endif
case 24:
- b->pen.state &= ~PenUnderline;
+ b->curattrs &= ~A_UNDERLINE;
break;
case 25:
- b->pen.state &= ~PenBlink;
+ b->curattrs &= ~A_BLINK;
break;
case 27:
- b->pen.state &= ~PenReverse;
+ b->curattrs &= ~A_REVERSE;
break;
case 28:
- b->pen.state &= ~PenInvis;
+ b->curattrs &= ~A_INVIS;
break;
case 30 ... 37: /* fg */
- b->pen.col.fg = param[i] - 30;
+ b->curfg = param[i] - 30;
break;
case 38:
if ((i + 2) < pcount && param[i + 1] == 5) {
- b->pen.col.fg = param[i + 2];
+ b->curfg = param[i + 2];
i += 2;
}
break;
case 39:
- b->pen.col.fg = -1;
+ b->curfg = -1;
break;
case 40 ... 47: /* bg */
- b->pen.col.bg = param[i] - 40;
+ b->curbg = param[i] - 40;
break;
case 48:
if ((i + 2) < pcount && param[i + 1] == 5) {
- b->pen.col.bg = param[i + 2];
+ b->curbg = param[i + 2];
i += 2;
}
break;
case 49:
- b->pen.col.bg = -1;
+ b->curbg = -1;
break;
- case 90 ... 97: /* hi fg */
- b->pen.col.fg = param[i] - 82;
+ case 90 ... 97: /* hi fg */
+ b->curfg = param[i] - 82;
break;
case 100 ... 107: /* hi bg */
- b->pen.col.bg = param[i] - 92;
+ b->curbg = param[i] - 92;
break;
default:
break;
@@ -264,27 +754,27 @@ interpret_csi_ed(Vt *t, int param[], int pcount)
Row *row, *start, *end;
Buffer *b = t->buffer;
- savepen(t);
- b->pen.state = PenNormal;
- b->pen.col.fg = b->pen.col.bg = -1;
+ attributes_save(t);
+ b->curattrs = A_NORMAL;
+ b->curfg = b->curbg = -1;
if (pcount && param[0] == 2) {
start = b->lines;
- end = b->lines + b->rows;
+ end = b->lines + b->rows;
} else if (pcount && param[0] == 1) {
start = b->lines;
- end = b->crow;
- zero(b->crow, 0, b->ccol + 1);
+ end = b->curs_row;
+ row_set(b->curs_row, 0, b->curs_col + 1, b);
} else {
- zero(b->crow, b->ccol, b->cols - b->ccol);
- start = b->crow + 1;
- end = b->lines + b->rows;
+ row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
+ start = b->curs_row + 1;
+ end = b->lines + b->rows;
}
for (row = start; row < end; row++)
- zero(row, 0, b->cols);
+ row_set(row, 0, b->cols, b);
- loadpen(t);
+ attributes_restore(t);
}
/* interprets a 'move cursor' (CUP) escape sequence */
@@ -292,21 +782,21 @@ static
void
interpret_csi_cup(Vt *t, int param[], int pcount)
{
- Buffer *b = t->buffer;
- Row *lines = t->relposmode ? b->scroll.top : b->lines;
+ Buffer *b = t->buffer;
+ Row *lines = t->relposmode ? b->scroll_top : b->lines;
if (pcount == 0) {
- b->crow = lines;
- b->ccol = 0;
+ b->curs_row = lines;
+ b->curs_col = 0;
} else if (pcount == 1) {
- b->crow = lines + param[0] - 1;
- b->ccol = 0;
+ b->curs_row = lines + param[0] - 1;
+ b->curs_col = 0;
} else {
- b->crow = lines + param[0] - 1;
- b->ccol = param[1] - 1;
+ b->curs_row = lines + param[0] - 1;
+ b->curs_col = param[1] - 1;
}
- cclamp(t);
+ cursor_clamp(t);
}
/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
@@ -320,37 +810,37 @@ interpret_csi_c(Vt *t, char verb, int param[], int pcount)
switch (verb) {
case 'A':
- b->crow -= n;
+ b->curs_row -= n;
break;
case 'B':
case 'e':
- b->crow += n;
+ b->curs_row += n;
break;
case 'C':
case 'a':
- b->ccol += n;
+ b->curs_col += n;
break;
case 'D':
- b->ccol -= n;
+ b->curs_col -= n;
break;
case 'E':
- b->crow += n;
- b->ccol = 0;
+ b->curs_row += n;
+ b->curs_col = 0;
break;
case 'F':
- b->crow -= n;
- b->ccol = 0;
+ b->curs_row -= n;
+ b->curs_col = 0;
break;
case 'G':
case '`':
- b->ccol = n - 1;
+ b->curs_col = n - 1;
break;
case 'd':
- b->crow = b->lines + n - 1;
+ b->curs_row = b->lines + n - 1;
break;
}
- cclamp(t);
+ cursor_clamp(t);
}
/* Interpret the 'erase line' escape sequence */
@@ -361,13 +851,13 @@ interpret_csi_el(Vt *t, int param[], int pcount)
Buffer *b = t->buffer;
switch (pcount ? param[0] : 0) {
case 1:
- zero(b->crow, 0, b->ccol + 1);
+ row_set(b->curs_row, 0, b->curs_col + 1, b);
break;
case 2:
- zero(b->crow, 0, b->cols);
+ row_set(b->curs_row, 0, b->cols, b);
break;
default:
- zero(b->crow, b->ccol, b->cols - b->ccol);
+ row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
break;
}
}
@@ -378,16 +868,16 @@ void
interpret_csi_ich(Vt *t, int param[], int pcount)
{
Buffer *b = t->buffer;
- Row *row = b->crow;
+ Row *row = b->curs_row;
int n = (pcount && param[0] > 0) ? param[0] : 1;
- if (b->ccol + n > b->cols)
- n = b->cols - b->ccol;
+ if (b->curs_col + n > b->cols)
+ n = b->cols - b->curs_col;
- for (int i = b->cols - 1; i >= b->ccol + n; i--)
+ for (int i = b->cols - 1; i >= b->curs_col + n; i--)
row->cells[i] = row->cells[i - n];
- zero(row, b->ccol, n);
+ row_set(row, b->curs_col, n, b);
}
/* Interpret the 'delete chars' sequence (DCH) */
@@ -396,16 +886,16 @@ void
interpret_csi_dch(Vt *t, int param[], int pcount)
{
Buffer *b = t->buffer;
- Row *row = b->crow;
+ Row *row = b->curs_row;
int n = (pcount && param[0] > 0) ? param[0] : 1;
- if (b->ccol + n > b->cols)
- n = b->cols - b->ccol;
+ if (b->curs_col + n > b->cols)
+ n = b->cols - b->curs_col;
- for (int i = b->ccol; i < b->cols - n; i++)
+ for (int i = b->curs_col; i < b->cols - n; i++)
row->cells[i] = row->cells[i + n];
- zero(row, b->cols - n, n);
+ row_set(row, b->cols - n, n, b);
}
/* Interpret an 'insert line' sequence (IL) */
@@ -416,13 +906,13 @@ interpret_csi_il(Vt *t, int param[], int pcount)
Buffer *b = t->buffer;
int n = (pcount && param[0] > 0) ? param[0] : 1;
- if (b->crow + n >= b->scroll.bot) {
- for (Row *row = b->crow; row < b->scroll.bot; row++)
- zero(row, 0, b->cols);
+ if (b->curs_row + n >= b->scroll_bot) {
+ for (Row *row = b->curs_row; row < b->scroll_bot; row++)
+ row_set(row, 0, b->cols, b);
} else {
- roll(b->crow, b->scroll.bot, -n);
- for (Row *row = b->crow; row < b->crow + n; row++)
- zero(row, 0, b->cols);
+ row_roll(b->curs_row, b->scroll_bot, -n);
+ for (Row *row = b->curs_row; row < b->curs_row + n; row++)
+ row_set(row, 0, b->cols, b);
}
}
@@ -434,13 +924,13 @@ interpret_csi_dl(Vt *t, int param[], int pcount)
Buffer *b = t->buffer;
int n = (pcount && param[0] > 0) ? param[0] : 1;
- if (b->crow + n >= b->scroll.bot) {
- for (Row *row = b->crow; row < b->scroll.bot; row++)
- zero(row, 0, b->cols);
+ if (b->curs_row + n >= b->scroll_bot) {
+ for (Row *row = b->curs_row; row < b->scroll_bot; row++)
+ row_set(row, 0, b->cols, b);
} else {
- roll(b->crow, b->scroll.bot, n);
- for (Row *row = b->scroll.bot - n; row < b->scroll.bot; row++)
- zero(row, 0, b->cols);
+ row_roll(b->curs_row, b->scroll_bot, n);
+ for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++)
+ row_set(row, 0, b->cols, b);
}
}
@@ -452,10 +942,10 @@ interpret_csi_ech(Vt *t, int param[], int pcount)
Buffer *b = t->buffer;
int n = (pcount && param[0] > 0) ? param[0] : 1;
- if (b->ccol + n > b->cols)
- n = b->cols - b->ccol;
+ if (b->curs_col + n > b->cols)
+ n = b->cols - b->curs_col;
- zero(b->crow, b->ccol, n);
+ row_set(b->curs_row, b->curs_col, n, b);
}
/* Interpret a 'set scrolling region' (DECSTBM) sequence */
@@ -468,8 +958,8 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount)
switch (pcount) {
case 0:
- b->scroll.top = b->lines;
- b->scroll.bot = b->lines + b->rows;
+ b->scroll_top = b->lines;
+ b->scroll_bot = b->lines + b->rows;
break;
case 2:
new_top = param[0] - 1;
@@ -487,15 +977,15 @@ interpret_csi_decstbm(Vt *t, int param[], int pcount)
/* check for range validity */
if (new_top < new_bot) {
- b->scroll.top = b->lines + new_top;
- b->scroll.bot = b->lines + new_bot;
+ b->scroll_top = b->lines + new_top;
+ b->scroll_bot = b->lines + new_bot;
}
break;
default:
return; /* malformed */
}
- b->crow = b->scroll.top;
- b->ccol = 0;
+ b->curs_row = b->scroll_top;
+ b->curs_col = 0;
}
static
@@ -530,17 +1020,17 @@ interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set)
case 47: /* use alternate/normal screen buffer */
case 1047:
if (!set)
- bclear(&t->buf[1]);
- t->buffer = set ? &t->buf[1] : &t->buf[0];
- vt·dirty(t);
+ buffer_clear(&t->buffer_alternate);
+ t->buffer = set ? &t->buffer_alternate : &t->buffer_normal;
+ vt_dirty(t);
if (param[i] != 1049)
break;
/* fall through */
case 1048: /* save/restore cursor */
if (set)
- csave(t);
+ cursor_save(t);
else
- crestore(t);
+ cursor_restore(t);
break;
case 1000: /* enable/disable normal mouse tracking */
t->mousetrack = set;
@@ -633,10 +1123,10 @@ interpret_csi(Vt *t)
interpret_csi_ech(t, csiparam, param_count);
break;
case 'S': /* SU: scroll up */
- vt·scroll(t, param_count ? -csiparam[0] : -1);
+ vt_scroll(t, param_count ? -csiparam[0] : -1);
break;
case 'T': /* SD: scroll down */
- vt·scroll(t, param_count ? csiparam[0] : 1);
+ vt_scroll(t, param_count ? csiparam[0] : 1);
break;
case 'Z': /* CBT: cursor backward tabulation */
puttab(t, param_count ? -csiparam[0] : -1);
@@ -644,7 +1134,7 @@ interpret_csi(Vt *t)
case 'g': /* TBC: tabulation clear */
switch (param_count ? csiparam[0] : 0) {
case 0:
- b->tabs[b->ccol] = false;
+ b->tabs[b->curs_col] = false;
break;
case 3:
memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols);
@@ -655,14 +1145,14 @@ interpret_csi(Vt *t)
interpret_csi_decstbm(t, csiparam, param_count);
break;
case 's': /* save cursor location */
- csave(t);
+ cursor_save(t);
break;
case 'u': /* restore cursor location */
- crestore(t);
+ cursor_restore(t);
break;
case 'n': /* query cursor location */
if (param_count == 1 && csiparam[0] == 6)
- sendcurs(t);
+ send_curs(t);
break;
default:
break;
@@ -675,8 +1165,8 @@ void
interpret_csi_ind(Vt *t)
{
Buffer *b = t->buffer;
- if (b->crow < b->lines + b->rows - 1)
- b->crow++;
+ if (b->curs_row < b->lines + b->rows - 1)
+ b->curs_row++;
}
/* Interpret a 'reverse index' (RI) sequence */
@@ -685,11 +1175,11 @@ void
interpret_csi_ri(Vt *t)
{
Buffer *b = t->buffer;
- if (b->crow > b->scroll.top)
- b->crow--;
+ if (b->curs_row > b->scroll_top)
+ b->curs_row--;
else {
- roll(b->scroll.top, b->scroll.bot, -1);
- zero(b->scroll.top, 0, b->cols);
+ row_roll(b->scroll_top, b->scroll_bot, -1);
+ row_set(b->scroll_top, 0, b->cols, b);
}
}
@@ -699,9 +1189,9 @@ void
interpret_csi_nel(Vt *t)
{
Buffer *b = t->buffer;
- if (b->crow < b->lines + b->rows - 1) {
- b->crow++;
- b->ccol = 0;
+ if (b->curs_row < b->lines + b->rows - 1) {
+ b->curs_row++;
+ b->curs_col = 0;
}
}
@@ -730,8 +1220,8 @@ interpret_osc(Vt *t)
switch (command) {
case 0: /* icon name and window title */
case 2: /* window title */
- // if (t->title_handler)
- // t->title_handler(t, data+1);
+ if (t->title_handler)
+ t->title_handler(t, data+1);
break;
case 1: /* icon name */
break;
@@ -786,12 +1276,12 @@ try_interpret_escape_seq(Vt *t)
}
break;
case '7': /* DECSC: save cursor and attributes */
- savepen(t);
- csave(t);
+ attributes_save(t);
+ cursor_save(t);
goto handled;
case '8': /* DECRC: restore cursor and attributes */
- loadpen(t);
- crestore(t);
+ attributes_restore(t);
+ cursor_restore(t);
goto handled;
case 'D': /* IND: index */
interpret_csi_ind(t);
@@ -803,7 +1293,7 @@ try_interpret_escape_seq(Vt *t)
interpret_csi_nel(t);
goto handled;
case 'H': /* HTS: horizontal tab set */
- t->buffer->tabs[t->buffer->ccol] = true;
+ t->buffer->tabs[t->buffer->curs_col] = true;
goto handled;
default:
goto cancel;
@@ -833,17 +1323,17 @@ puttab(Vt *t, int count)
{
Buffer *b = t->buffer;
int direction = count >= 0 ? 1 : -1;
- for (int col = b->ccol + direction; count; col += direction) {
+ for (int col = b->curs_col + direction; count; col += direction) {
if (col < 0) {
- b->ccol = 0;
+ b->curs_col = 0;
break;
}
if (col >= b->cols) {
- b->ccol = b->cols - 1;
+ b->curs_col = b->cols - 1;
break;
}
if (b->tabs[col]) {
- b->ccol = col;
+ b->curs_col = col;
count -= direction;
}
}
@@ -851,31 +1341,31 @@ puttab(Vt *t, int count)
static
void
-process_nonprinting(Vt *t, rune r)
+process_nonprinting(Vt *t, wchar_t wc)
{
Buffer *b = t->buffer;
- switch (r) {
+ switch (wc) {
case '\e': /* ESC */
new_escape_sequence(t);
break;
case '\a': /* BEL */
- // if (t->urgent_handler)
- // t->urgent_handler(t);
+ if (t->urgent_handler)
+ t->urgent_handler(t);
break;
case '\b': /* BS */
- if (b->ccol > 0)
- b->ccol--;
+ if (b->curs_col > 0)
+ b->curs_col--;
break;
case '\t': /* HT */
puttab(t, 1);
break;
case '\r': /* CR */
- b->ccol = 0;
+ b->curs_col = 0;
break;
case '\v': /* VT */
case '\f': /* FF */
case '\n': /* LF */
- clinedown(t);
+ cursor_line_down(t);
break;
case '\016': /* SO: shift out, invoke the G1 character set */
t->graphmode = t->charsets[1];
@@ -893,7 +1383,7 @@ is_utf8_locale(void)
const char *cset = nl_langinfo(CODESET);
if (!cset)
cset = "ANSI_X3.4-1968";
- isutf8 = !strcmp(cset, "UTF-8");
+ is_utf8 = !strcmp(cset, "UTF-8");
}
static
@@ -917,18 +1407,17 @@ get_vt100_graphic(char c)
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
};
- if (isutf8)
+ if (is_utf8)
return vt100_utf8[c - 0x41];
- // else if (strchr(vt100_acs, c))
- // return NCURSES_ACS(c);
+ else if (strchr(vt100_acs, c))
+ return NCURSES_ACS(c);
return '\0';
}
static
void
-putrune(Vt *t, rune r)
+put_wc(Vt *t, wchar_t wc)
{
- Cell blank;
int width = 0;
if (!t->seen_input) {
@@ -938,61 +1427,54 @@ putrune(Vt *t, rune r)
if (t->escaped) {
if (t->elen + 1 < sizeof(t->ebuf)) {
- t->ebuf[t->elen] = r;
+ t->ebuf[t->elen] = wc;
t->ebuf[++t->elen] = '\0';
try_interpret_escape_seq(t);
} else {
cancel_escape_sequence(t);
}
- } else if (IS_CONTROL(r)) {
- process_nonprinting(t, r);
+ } else if (IS_CONTROL(wc)) {
+ process_nonprinting(t, wc);
} else {
if (t->graphmode) {
- if (r >= 0x41 && r <= 0x7e) {
- wchar_t gc = get_vt100_graphic(r);
+ if (wc >= 0x41 && wc <= 0x7e) {
+ wchar_t gc = get_vt100_graphic(wc);
if (gc)
- r = gc;
+ wc = gc;
}
width = 1;
- } else if ((width = wcwidth(r)) < 1) {
+ } else if ((width = wcwidth(wc)) < 1) {
width = 1;
}
Buffer *b = t->buffer;
- blank = (Cell){
- .r = L'\0',
- .pen = (Pen){
- .state = b->pen.state,
- .col = b->pen.col
- },
- };
- if (width == 2 && b->ccol == b->cols - 1) {
- b->crow->cells[b->ccol++] = blank;
- b->crow->dirty = true;
+ Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg };
+ if (width == 2 && b->curs_col == b->cols - 1) {
+ b->curs_row->cells[b->curs_col++] = blank_cell;
+ b->curs_row->dirty = true;
}
- if (b->ccol >= b->cols) {
- b->ccol = 0;
- clinedown(t);
+ if (b->curs_col >= b->cols) {
+ b->curs_col = 0;
+ cursor_line_down(t);
}
if (t->insert) {
- Cell *src = b->crow->cells + b->ccol;
+ Cell *src = b->curs_row->cells + b->curs_col;
Cell *dest = src + width;
- size_t len = b->cols - b->ccol - width;
+ size_t len = b->cols - b->curs_col - width;
memmove(dest, src, len * sizeof *dest);
}
- b->crow->cells[b->ccol] = blank;
- b->crow->cells[b->ccol++].r = r;
- b->crow->dirty = true;
-
+ b->curs_row->cells[b->curs_col] = blank_cell;
+ b->curs_row->cells[b->curs_col++].text = wc;
+ b->curs_row->dirty = true;
if (width == 2)
- b->crow->cells[b->ccol++] = blank;
+ b->curs_row->cells[b->curs_col++] = blank_cell;
}
}
int
-vt·process(Vt *t)
+vt_process(Vt *t)
{
int res;
uint pos = 0;
@@ -1004,17 +1486,18 @@ vt·process(Vt *t)
return -1;
}
+ /* read from the pty */
res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen);
if (res < 0)
return -1;
+ /* process the bytes you read */
t->rlen += res;
while (pos < t->rlen) {
- rune r;
- size_t len;
+ wchar_t wc;
+ ssize_t len;
- // XXX: convert this to use utf8 functions
- len = (ssize_t)mbrtowc((wchar*)&r, t->rbuf + pos, t->rlen - pos, &ps);
+ len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps);
if (len == -2) {
t->rlen -= pos;
memmove(t->rbuf, t->rbuf + pos, t->rlen);
@@ -1023,11 +1506,11 @@ vt·process(Vt *t)
if (len == -1) {
len = 1;
- r = t->rbuf[pos];
+ wc = t->rbuf[pos];
}
pos += len ? len : 1;
- putrune(t, r);
+ put_wc(t, wc);
}
t->rlen -= pos;
@@ -1035,9 +1518,16 @@ vt·process(Vt *t)
return 0;
}
-/* size is the number of rows kept in the scrollback */
+void
+vt_default_colors_set(Vt *t, attr_t attrs, short fg, short bg)
+{
+ t->defattrs = attrs;
+ t->deffg = fg;
+ t->defbg = bg;
+}
+
Vt *
-vt·make(int rows, int cols, int size)
+vt_create(int rows, int cols, int scroll_size)
{
if (rows <= 0 || cols <= 0)
return nil;
@@ -1047,14 +1537,11 @@ vt·make(int rows, int cols, int size)
return nil;
t->pty = -1;
- t->pen = (Pen) {
- .state = PenNormal,
- .col = {-1, -1},
- };
- t->buffer = &t->buf[0];
+ t->deffg = t->defbg = -1;
+ t->buffer = &t->buffer_normal;
- if (!binit(&t->buf[0], rows, cols, size) ||
- !binit(&t->buf[1], rows, cols, 0)) {
+ if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) ||
+ !buffer_init(&t->buffer_alternate, rows, cols, 0)) {
free(t);
return nil;
}
@@ -1063,34 +1550,34 @@ vt·make(int rows, int cols, int size)
}
void
-vt·resize(Vt *t, int rows, int cols)
+vt_resize(Vt *t, int rows, int cols)
{
struct winsize ws = { .ws_row = rows, .ws_col = cols };
if (rows <= 0 || cols <= 0)
return;
- vt·noscroll(t);
- bresize(&t->buf[0], rows, cols);
- bresize(&t->buf[1], rows, cols);
- cclamp(t);
+ vt_noscroll(t);
+ buffer_resize(&t->buffer_normal, rows, cols);
+ buffer_resize(&t->buffer_alternate, rows, cols);
+ cursor_clamp(t);
ioctl(t->pty, TIOCSWINSZ, &ws);
kill(-t->pid, SIGWINCH);
}
void
-vt·free(Vt *t)
+vt_destroy(Vt *t)
{
if (!t)
return;
- bfree(&t->buf[0]);
- bfree(&t->buf[1]);
+ buffer_free(&t->buffer_normal);
+ buffer_free(&t->buffer_alternate);
close(t->pty);
free(t);
}
void
-vt·dirty(Vt *t)
+vt_dirty(Vt *t)
{
Buffer *b = t->buffer;
for (Row *row = b->lines, *end = row + b->rows; row < end; row++)
@@ -1098,49 +1585,51 @@ vt·dirty(Vt *t)
}
void
-vt·draw(Vt *t, Window *win, int srow, int scol)
+vt_draw(Vt *t, WINDOW *win, int srow, int scol)
{
- int i, j;
- Cell *cell, *prev;
Buffer *b = t->buffer;
if (srow != t->srow || scol != t->scol) {
- vt·dirty(t);
+ vt_dirty(t);
t->srow = srow;
t->scol = scol;
}
- for (i = 0; i < b->rows; i++) {
+ for (int i = 0; i < b->rows; i++) {
Row *row = b->lines + i;
if (!row->dirty)
continue;
wmove(win, srow + i, scol);
- for (j = 0; j < b->cols; j++) {
- prev = cell;
+ Cell *cell = nil;
+ for (int j = 0; j < b->cols; j++) {
+ Cell *prev_cell = cell;
cell = row->cells + j;
- if (!prev || !peneq(cell->pen, prev->pen)) {
- if (cell->pen.state == PenNormal)
- cell->pen.state = t->pen.state;
- if (cell->pen.col.fg == -1)
- cell->pen.col.fg = t->pen.col.fg;
- if (cell->pen.col.bg == -1)
- cell->pen.col.bg = t->pen.col.bg;
- // wattrset(win, cell->attr << NCURSES_ATTR_SHIFT);
- // wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil);
+ if (!prev_cell || cell->attr != prev_cell->attr
+ || cell->fg != prev_cell->fg
+ || cell->bg != prev_cell->bg) {
+ if (cell->attr == A_NORMAL)
+ cell->attr = t->defattrs;
+ if (cell->fg == -1)
+ cell->fg = t->deffg;
+ if (cell->bg == -1)
+ cell->bg = t->defbg;
+ wattrset(win, cell->attr << NCURSES_ATTR_SHIFT);
+ wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil);
}
- if (isutf8 && cell->r >= RuneSync) {
+ if (is_utf8 && cell->text >= 128) {
char buf[MB_CUR_MAX + 1];
- size_t len = wcrtomb(buf, cell->r, nil);
+ size_t len = wcrtomb(buf, cell->text, nil);
if (len > 0) {
waddnstr(win, buf, len);
- if (wcwidth(cell->r) > 1)
+ if (wcwidth(cell->text) > 1)
j++;
}
- } else
- waddch(win, cell->r > ' ' ? cell->r: ' ');
+ } else {
+ waddch(win, cell->text > ' ' ? cell->text : ' ');
+ }
}
int x, y;
@@ -1152,36 +1641,36 @@ vt·draw(Vt *t, Window *win, int srow, int scol)
row->dirty = false;
}
- wmove(win, srow + b->crow - b->lines, scol + b->ccol);
+ wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col);
}
void
-vt·scroll(Vt *t, int rows)
+vt_scroll(Vt *t, int rows)
{
Buffer *b = t->buffer;
- if (!b->scroll.size)
+ if (!b->scroll_size)
return;
if (rows < 0) { /* scroll back */
- if (rows < -b->scroll.above)
- rows = -b->scroll.above;
+ if (rows < -b->scroll_above)
+ rows = -b->scroll_above;
} else { /* scroll forward */
- if (rows > b->scroll.below)
- rows = b->scroll.below;
+ if (rows > b->scroll_below)
+ rows = b->scroll_below;
}
- bscroll(b, rows);
- b->scroll.below -= rows;
+ buffer_scroll(b, rows);
+ b->scroll_below -= rows;
}
void
-vt·noscroll(Vt *t)
+vt_noscroll(Vt *t)
{
- int below = t->buffer->scroll.below;
- if (below)
- vt·scroll(t, below);
+ int scroll_below = t->buffer->scroll_below;
+ if (scroll_below)
+ vt_scroll(t, scroll_below);
}
pid_t
-vt·forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from)
+vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from)
{
int vt2ed[2], ed2vt[2];
struct winsize ws;
@@ -1228,7 +1717,7 @@ vt·forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const cha
for (const char **envp = env; envp && envp[0]; envp += 2)
setenv(envp[0], envp[1], 1);
- setenv("TERM", vtname, 1);
+ setenv("TERM", vt_term, 1);
if (cwd)
chdir(cwd);
@@ -1257,13 +1746,13 @@ vt_pty_get(Vt *t)
return t->pty;
}
-uintptr
-vt·write(Vt *t, const char *buf, size_t len)
+ssize_t
+vt_write(Vt *t, const char *buf, size_t len)
{
- uintptr res, ret = len;
+ ssize_t ret = len;
while (len > 0) {
- res = write(t->pty, buf, len);
+ ssize_t res = write(t->pty, buf, len);
if (res < 0) {
if (errno != EAGAIN && errno != EINTR)
return -1;
@@ -1278,19 +1767,18 @@ vt·write(Vt *t, const char *buf, size_t len)
static
void
-sendcurs(Vt *t)
+send_curs(Vt *t)
{
Buffer *b = t->buffer;
char keyseq[16];
- snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->crow - b->lines), b->ccol);
- vt·write(t, keyseq, strlen(keyseq));
+ snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col);
+ vt_write(t, keyseq, strlen(keyseq));
}
-#if 0
void
-vt·keypress(Vt *t, int keycode)
+vt_keypress(Vt *t, int keycode)
{
- vt·noscroll(t);
+ vt_noscroll(t);
if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) {
switch (keycode) {
@@ -1299,23 +1787,26 @@ vt·keypress(Vt *t, int keycode)
case KEY_RIGHT:
case KEY_LEFT: {
char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] };
- vt·write(t, keyseq, sizeof keyseq);
+ vt_write(t, keyseq, sizeof keyseq);
break;
}
default:
- vt·write(t, keytable[keycode], strlen(keytable[keycode]));
+ vt_write(t, keytable[keycode], strlen(keytable[keycode]));
}
} else if (keycode <= UCHAR_MAX) {
char c = keycode;
- vt·write(t, &c, 1);
+ vt_write(t, &c, 1);
} else {
+#ifndef NDEBUG
fprintf(stderr, "unhandled key %#o\n", keycode);
+#endif
}
}
void
-vt·mouse(Vt *t, int x, int y, mmask_t mask)
+vt_mouse(Vt *t, int x, int y, mmask_t mask)
{
+#ifdef NCURSES_MOUSE_VERSION
char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0;
if (!t->mousetrack)
@@ -1341,16 +1832,16 @@ vt·mouse(Vt *t, int x, int y, mmask_t mask)
seq[4] = 32 + x;
seq[5] = 32 + y;
- vt·write(t, seq, sizeof seq);
+ vt_write(t, seq, sizeof seq);
if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) {
/* send a button release event */
button = 3;
seq[3] = 32 + button + state;
- vt·write(t, seq, sizeof seq);
+ vt_write(t, seq, sizeof seq);
}
+#endif /* NCURSES_MOUSE_VERSION */
}
-#endif
static
uint
@@ -1363,8 +1854,124 @@ color_hash(short fg, short bg)
return fg * (COLORS + 2) + bg;
}
+int
+vt_rgb_get(Vt *t, uint8 r, uint8 g, uint8 b)
+{
+ return 0;
+}
+
+
+int
+vt_color_get(Vt *t, short fg, short bg)
+{
+ if (fg >= COLORS)
+ fg = (t ? t->deffg : default_fg);
+ if (bg >= COLORS)
+ bg = (t ? t->defbg : default_bg);
+
+ if (!has_default_colors) {
+ if (fg == -1)
+ fg = (t && t->deffg != -1 ? t->deffg : default_fg);
+ if (bg == -1)
+ bg = (t && t->defbg != -1 ? t->defbg : default_bg);
+ }
+
+ if (!color2palette || (fg == -1 && bg == -1))
+ return 0;
+ uint index = color_hash(fg, bg);
+ if (color2palette[index] == 0) {
+ short oldfg, oldbg;
+ for (;;) {
+ if (++color_pair_current >= color_pairs_max)
+ color_pair_current = color_pairs_reserved + 1;
+ pair_content(color_pair_current, &oldfg, &oldbg);
+ uint old_index = color_hash(oldfg, oldbg);
+ if (color2palette[old_index] >= 0) {
+ if (init_pair(color_pair_current, fg, bg) == OK) {
+ color2palette[old_index] = 0;
+ color2palette[index] = color_pair_current;
+ }
+ break;
+ }
+ }
+ }
+
+ int color_pair = color2palette[index];
+ return color_pair >= 0 ? color_pair : -color_pair;
+}
+
+int
+vt_color_reserve(short fg, short bg)
+{
+ if (!color2palette || fg >= COLORS || bg >= COLORS)
+ return 0;
+
+ if (!has_default_colors && fg == -1)
+ fg = default_fg;
+ if (!has_default_colors && bg == -1)
+ bg = default_bg;
+ if (fg == -1 && bg == -1)
+ return 0;
+
+ uint index = color_hash(fg, bg);
+ if (color2palette[index] >= 0) {
+ if (init_pair(color_pairs_reserved + 1, fg, bg) == OK)
+ color2palette[index] = -(++color_pairs_reserved);
+ }
+ int color_pair = color2palette[index];
+ return color_pair >= 0 ? color_pair : -color_pair;
+}
+
+int
+vt_rgb_reserve(short fg, short bg)
+{
+ if (!color2palette || fg >= COLORS || bg >= COLORS)
+ return 0;
+
+ if (!has_default_colors && fg == -1)
+ fg = default_fg;
+ if (!has_default_colors && bg == -1)
+ bg = default_bg;
+ if (fg == -1 && bg == -1)
+ return 0;
+
+ uint index = color_hash(fg, bg);
+ if (color2palette[index] >= 0) {
+ if (init_pair(color_pairs_reserved + 1, fg, bg) == OK)
+ color2palette[index] = -(++color_pairs_reserved);
+ }
+ int color_pair = color2palette[index];
+ return color_pair >= 0 ? color_pair : -color_pair;
+}
+
+static
+void
+init_colors(void)
+{
+ pair_content(0, &default_fg, &default_bg);
+ if (default_fg == -1)
+ default_fg = COLOR_WHITE;
+ if (default_bg == -1)
+ default_bg = COLOR_BLACK;
+ has_default_colors = (use_default_colors() == OK);
+ color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX);
+
+ if (COLORS)
+ color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(int));
+ /*
+ * XXX: On undefined color-pairs NetBSD curses pair_content() set fg
+ * and bg to default colors while ncurses set them respectively to
+ * 0 and 0. Initialize all color-pairs in order to have consistent
+ * behaviour despite the implementation used.
+ */
+ for (short i = 1; i < color_pairs_max; i++)
+ init_extended_pair(i, 0, 0);
+
+ vt_color_reserve(COLOR_WHITE, COLOR_BLACK);
+}
+
void
-vt·init(void)
+vt_init(void)
{
init_colors();
is_utf8_locale();
@@ -1372,17 +1979,11 @@ vt·init(void)
if (!term)
term = "dvtm";
- snprintf(vtname, sizeof vtname, "%s%s", term, COLORS >= 256 ? "-256color" : "");
-}
-
-void
-vt·shutdown(void)
-{
+ snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : "");
}
-#if 0
void
-vt·setkeytable(const char * const keytable_overlay[], int count)
+vt_keytable_set(const char * const keytable_overlay[], int count)
{
for (int k = 0; k < count && k < KEY_MAX; k++) {
const char *keyseq = keytable_overlay[k];
@@ -1392,47 +1993,52 @@ vt·setkeytable(const char * const keytable_overlay[], int count)
}
void
-vt·settitlecb(Vt *t, vt_title_handler_t handler)
+vt_shutdown(void)
+{
+ free(color2palette);
+}
+
+void
+vt_title_handler_set(Vt *t, vt_title_handler_t handler)
{
t->title_handler = handler;
}
void
-vt·seturgentcb(Vt *t, vt_urgent_handler_t handler)
+vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler)
{
t->urgent_handler = handler;
}
-#endif
void
-vt·setdata(Vt *t, void *data)
+vt_data_set(Vt *t, void *data)
{
t->data = data;
}
void *
-vt·getdata(Vt *t)
+vt_data_get(Vt *t)
{
return t->data;
}
bool
-vt·cursorvisible(Vt *t)
+vt_cursor_visible(Vt *t)
{
- return t->buffer->scroll.below ? false : !t->curshid;
+ return t->buffer->scroll_below ? false : !t->curshid;
}
pid_t
-vt·pid(Vt *t)
+vt_pid_get(Vt *t)
{
return t->pid;
}
size_t
-vt·content(Vt *t, char **buf, bool colored)
+vt_content_get(Vt *t, char **buf, bool colored)
{
Buffer *b = t->buffer;
- int lines = b->scroll.above + b->scroll.below + b->rows + 1;
+ int lines = b->scroll_above + b->scroll_below + b->rows + 1;
size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX));
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
@@ -1441,28 +2047,28 @@ vt·content(Vt *t, char **buf, bool colored)
return 0;
char *s = *buf;
- Cell *prev = nil;
+ Cell *prev_cell = nil;
- for (Row *row = browfirst(b); row; row = brownext(b, row)) {
+ for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) {
size_t len = 0;
char *last_non_space = s;
for (int col = 0; col < b->cols; col++) {
Cell *cell = row->cells + col;
if (colored) {
int esclen = 0;
- if (!prev || cell->attr != prev->attr) {
+ if (!prev_cell || cell->attr != prev_cell->attr) {
attr_t attr = cell->attr << NCURSES_ATTR_SHIFT;
esclen = sprintf(s, "\033[0%s%s%s%s%s%sm",
- attr & PenBold ? ";1" : "",
- attr & PenDim ? ";2" : "",
- attr & PenUnderline ? ";4" : "",
- attr & PenBlink ? ";5" : "",
- attr & PenReverse ? ";7" : "",
- attr & PenInvis ? ";8" : "");
+ attr & A_BOLD ? ";1" : "",
+ attr & A_DIM ? ";2" : "",
+ attr & A_UNDERLINE ? ";4" : "",
+ attr & A_BLINK ? ";5" : "",
+ attr & A_REVERSE ? ";7" : "",
+ attr & A_INVIS ? ";8" : "");
if (esclen > 0)
s += esclen;
}
- if (!prev || cell->fg != prev->fg || cell->attr != prev->attr) {
+ if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) {
if (cell->fg == -1)
esclen = sprintf(s, "\033[39m");
else
@@ -1470,7 +2076,7 @@ vt·content(Vt *t, char **buf, bool colored)
if (esclen > 0)
s += esclen;
}
- if (!prev || cell->bg != prev->bg || cell->attr != prev->attr) {
+ if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) {
if (cell->bg == -1)
esclen = sprintf(s, "\033[49m");
else
@@ -1478,7 +2084,7 @@ vt·content(Vt *t, char **buf, bool colored)
if (esclen > 0)
s += esclen;
}
- prev = cell;
+ prev_cell = cell;
}
if (cell->text) {
len = wcrtomb(s, cell->text, &ps);
@@ -1500,7 +2106,7 @@ vt·content(Vt *t, char **buf, bool colored)
}
int
-vt·contentstart(Vt *t)
+vt_content_start(Vt *t)
{
- return t->buffer->scroll.above;
+ return t->buffer->scroll_above;
}
diff --git a/sys/cmd/dvtm/vt.c.old b/sys/cmd/dvtm/vt.c.old
deleted file mode 100644
index d858472..0000000
--- a/sys/cmd/dvtm/vt.c.old
+++ /dev/null
@@ -1,2074 +0,0 @@
-/* See license for details */
-#include <u.h>
-#include <libn.h>
-
-#include <langinfo.h>
-#include <signal.h>
-#include <termios.h>
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-#if defined(__linux__) || defined(__CYGWIN__)
-# include <pty.h>
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-# include <libutil.h>
-#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
-# include <util.h>
-#endif
-
-#include "vt.h"
-
-#ifndef NCURSES_ATTR_SHIFT
-# define NCURSES_ATTR_SHIFT 8
-#endif
-
-#ifndef NCURSES_ACS
-# ifdef PDCURSES
-# define NCURSES_ACS(c) (acs_map[(uchar)(c)])
-# else /* BSD curses */
-# define NCURSES_ACS(c) (_acs_map[(uchar)(c)])
-# endif
-#endif
-
-#ifdef NCURSES_VERSION
-
-#ifndef NCURSES_EXT_COLORS
-# define NCURSES_EXT_COLORS 0
-#endif
-
-#if !NCURSES_EXT_COLORS
-# define MAX_COLOR_PAIRS MIN(COLOR_PAIRS, 256)
-#endif
-
-#endif
-
-#ifndef MAX_COLOR_PAIRS
-# define MAX_COLOR_PAIRS COLOR_PAIRS
-#endif
-
-#if defined _AIX && defined CTRL
-# undef CTRL
-#endif
-#ifndef CTRL
-# define CTRL(k) ((k) & 0x1F)
-#endif
-
-#define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
-
-static bool is_utf8, has_default_colors;
-static int color_pairs_reserved, color_pairs_max, color_pair_current;
-static int *color2palette, default_fg, default_bg;
-static char vt_term[32];
-
-typedef struct {
- wchar_t text;
- attr_t attr;
- int fg;
- int bg;
-} Cell;
-
-typedef struct {
- Cell *cells;
- uint dirty:1;
-} Row;
-
-/* Buffer holding the current terminal window content (as an array) as well
- * as the scroll back buffer content (as a circular/ring buffer).
- *
- * If new content is added to terminal the view port slides down and the
- * previously top most line is moved into the scroll back buffer at postion
- * scroll_index. This index will eventually wrap around and thus overwrite
- * the oldest lines.
- *
- * In the scenerio below a scroll up has been performed. That is 'scroll_above'
- * lines still lie above the current view port. Further scrolling up will show
- * them. Similarly 'scroll_below' is the amount of lines below the current
- * viewport.
- *
- * The function buffer_boundary sets the row pointers to the start/end range
- * of the section delimiting the region before/after the viewport. The functions
- * buffer_row_{first,last} return the first/last logical row. And
- * buffer_row_{next,prev} allows to iterate over the logical lines in either
- * direction.
- *
- * scroll back buffer
- *
- * scroll_buf->+----------------+-----+
- * | | | ^ \
- * | before | | | |
- * current terminal content | viewport | | | |
- * | | | |
- * +----------------+-----+\ | | | s > scroll_above
- * ^ | | i | \ | | i | c |
- * | | | n | \ | | n | r |
- * | | v | \ | | v | o |
- * r | | i | \ | | i | l /
- * o | viewport | s | >|<- scroll_index | s | l \
- * w | | i | / | | i | |
- * s | | b | / | after | b | s > scroll_below
- * | | l | / | viewport | l | i |
- * v | | e | / | | e | z /
- * +----------------+-----+/ | unused | | e
- * <- maxcols -> | scroll back | |
- * <- cols -> | buffer | | |
- * | | | |
- * | | | v
- * roll_buf + scroll_size->+----------------+-----+
- * <- maxcols ->
- * <- cols ->
- */
-typedef struct {
- Row *lines; /* array of Row pointers of size 'rows' */
- Row *curs_row; /* row on which the cursor currently resides */
- Row *scroll_buf; /* a ring buffer holding the scroll back content */
- Row *scroll_top; /* row in lines where scrolling region starts */
- Row *scroll_bot; /* row in lines where scrolling region ends */
- bool *tabs; /* a boolean flag for each column whether it is a tab */
- int scroll_size; /* maximal capacity of scroll back buffer (in lines) */
- int scroll_index; /* current index into the ring buffer */
- int scroll_above; /* number of lines above current viewport */
- int scroll_below; /* number of lines below current viewport */
- int rows, cols; /* current dimension of buffer */
- int maxcols; /* allocated cells (maximal cols over time) */
- attr_t curattrs, savattrs; /* current and saved attributes for cells */
- int curs_col; /* current cursor column (zero based) */
- int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */
- short curfg, curbg; /* current fore and background colors */
- short savfg, savbg; /* saved colors */
-} Buffer;
-
-struct Vt {
- Buffer buffer_normal; /* normal screen buffer */
- Buffer buffer_alternate; /* alternate screen buffer */
- Buffer *buffer; /* currently active buffer (one of the above) */
- attr_t defattrs; /* attributes to use for normal/empty cells */
- int deffg, defbg; /* colors to use for back normal/empty cells (white/black) */
- int pty; /* master side pty file descriptor */
- pid_t pid; /* process id of the process running in this vt */
- /* flags */
- uint seen_input:1;
- uint insert:1;
- uint escaped:1;
- uint curshid:1;
- uint curskeymode:1;
- uint bell:1;
- uint relposmode:1;
- uint mousetrack:1;
- uint graphmode:1;
- uint savgraphmode:1;
- bool charsets[2];
- /* buffers and parsing state */
- char rbuf[BUFSIZ];
- char ebuf[BUFSIZ];
- uint rlen, elen;
- int srow, scol; /* last known offset to display start row, start column */
- char title[256]; /* xterm style window title */
- vt_title_handler_t title_handler; /* hook which is called when title changes */
- vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */
- void *data; /* user supplied data */
-};
-
-#if 0
-static const char *keytable[KEY_MAX+1] = {
- [KEY_ENTER] = "\r",
- ['\n'] = "\n",
- /* for the arrow keys the CSI / SS3 sequences are not stored here
- * because they depend on the current cursor terminal mode
- */
- [KEY_UP] = "A",
- [KEY_DOWN] = "B",
- [KEY_RIGHT] = "C",
- [KEY_LEFT] = "D",
-#ifdef KEY_SUP
- [KEY_SUP] = "\e[1;2A",
-#endif
-#ifdef KEY_SDOWN
- [KEY_SDOWN] = "\e[1;2B",
-#endif
- [KEY_SRIGHT] = "\e[1;2C",
- [KEY_SLEFT] = "\e[1;2D",
- [KEY_BACKSPACE] = "\177",
- [KEY_IC] = "\e[2~",
- [KEY_DC] = "\e[3~",
- [KEY_PPAGE] = "\e[5~",
- [KEY_NPAGE] = "\e[6~",
- [KEY_HOME] = "\e[7~",
- [KEY_END] = "\e[8~",
- [KEY_BTAB] = "\e[Z",
- [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */
- [KEY_F(1)] = "\e[11~",
- [KEY_F(2)] = "\e[12~",
- [KEY_F(3)] = "\e[13~",
- [KEY_F(4)] = "\e[14~",
- [KEY_F(5)] = "\e[15~",
- [KEY_F(6)] = "\e[17~",
- [KEY_F(7)] = "\e[18~",
- [KEY_F(8)] = "\e[19~",
- [KEY_F(9)] = "\e[20~",
- [KEY_F(10)] = "\e[21~",
- [KEY_F(11)] = "\e[23~",
- [KEY_F(12)] = "\e[24~",
- [KEY_F(13)] = "\e[23~",
- [KEY_F(14)] = "\e[24~",
- [KEY_F(15)] = "\e[25~",
- [KEY_F(16)] = "\e[26~",
- [KEY_F(17)] = "\e[28~",
- [KEY_F(18)] = "\e[29~",
- [KEY_F(19)] = "\e[31~",
- [KEY_F(20)] = "\e[32~",
- [KEY_F(21)] = "\e[33~",
- [KEY_F(22)] = "\e[34~",
- [KEY_RESIZE] = "",
-#ifdef KEY_EVENT
- [KEY_EVENT] = "",
-#endif
-};
-#endif
-
-static void puttab(Vt *t, int count);
-static void process_nonprinting(Vt *t, wchar_t wc);
-static void send_curs(Vt *t);
-
-const static
-attr_t
-build_attrs(attr_t curattrs)
-{
- return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff)) >> NCURSES_ATTR_SHIFT;
-}
-
-static
-void
-row_set(Row *row, int start, int len, Buffer *t)
-{
- Cell cell = {
- .text = L'\0',
- .attr = t ? build_attrs(t->curattrs) : 0,
- .fg = t ? t->curfg : -1,
- .bg = t ? t->curbg : -1,
- };
-
- for (int i = start; i < len + start; i++)
- row->cells[i] = cell;
- row->dirty = true;
-}
-
-static
-void
-row_roll(Row *start, Row *end, int count)
-{
- int n = end - start;
-
- count %= n;
- if (count < 0)
- count += n;
-
- if (count) {
- char buf[count * sizeof(Row)];
- memcpy(buf, start, count * sizeof(Row));
- memmove(start, start + count, (n - count) * sizeof(Row));
- memcpy(end - count, buf, count * sizeof(Row));
- for (Row *row = start; row < end; row++)
- row->dirty = true;
- }
-}
-
-static
-void
-buffer_clear(Buffer *b)
-{
- Cell cell = {
- .text = L'\0',
- .attr = A_NORMAL,
- .fg = -1,
- .bg = -1,
- };
-
- for (int i = 0; i < b->rows; i++) {
- Row *row = b->lines + i;
- for (int j = 0; j < b->cols; j++) {
- row->cells[j] = cell;
- row->dirty = true;
- }
- }
-}
-
-static
-void
-buffer_free(Buffer *b)
-{
- for (int i = 0; i < b->rows; i++)
- free(b->lines[i].cells);
- free(b->lines);
- for (int i = 0; i < b->scroll_size; i++)
- free(b->scroll_buf[i].cells);
- free(b->scroll_buf);
- free(b->tabs);
-}
-
-static
-void
-buffer_scroll(Buffer *b, int s)
-{
- /* work in screenfuls */
- int ssz = b->scroll_bot - b->scroll_top;
- if (s > ssz) {
- buffer_scroll(b, ssz);
- buffer_scroll(b, s - ssz);
- return;
- }
- if (s < -ssz) {
- buffer_scroll(b, -ssz);
- buffer_scroll(b, s + ssz);
- return;
- }
-
- b->scroll_above += s;
- if (b->scroll_above >= b->scroll_size)
- b->scroll_above = b->scroll_size;
-
- if (s > 0 && b->scroll_size) {
- for (int i = 0; i < s; i++) {
- Row tmp = b->scroll_top[i];
- b->scroll_top[i] = b->scroll_buf[b->scroll_index];
- b->scroll_buf[b->scroll_index] = tmp;
-
- b->scroll_index++;
- if (b->scroll_index == b->scroll_size)
- b->scroll_index = 0;
- }
- }
- row_roll(b->scroll_top, b->scroll_bot, s);
- if (s < 0 && b->scroll_size) {
- for (int i = (-s) - 1; i >= 0; i--) {
- b->scroll_index--;
- if (b->scroll_index == -1)
- b->scroll_index = b->scroll_size - 1;
-
- Row tmp = b->scroll_top[i];
- b->scroll_top[i] = b->scroll_buf[b->scroll_index];
- b->scroll_buf[b->scroll_index] = tmp;
- b->scroll_top[i].dirty = true;
- }
- }
-}
-
-static
-void
-buffer_resize(Buffer *b, int rows, int cols)
-{
- Row *lines = b->lines;
-
- if (b->rows != rows) {
- if (b->curs_row >= lines + rows) {
- /* scroll up instead of simply chopping off bottom */
- buffer_scroll(b, (b->curs_row - b->lines) - rows + 1);
- }
- while (b->rows > rows) {
- free(lines[b->rows - 1].cells);
- b->rows--;
- }
-
- lines = realloc(lines, sizeof(Row) * rows);
- }
-
- if (b->maxcols < cols) {
- for (int row = 0; row < b->rows; row++) {
- lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols);
- if (b->cols < cols)
- row_set(lines + row, b->cols, cols - b->cols, nil);
- lines[row].dirty = true;
- }
- Row *sbuf = b->scroll_buf;
- for (int row = 0; row < b->scroll_size; row++) {
- sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols);
- if (b->cols < cols)
- row_set(sbuf + row, b->cols, cols - b->cols, nil);
- }
- b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols);
- for (int col = b->cols; col < cols; col++)
- b->tabs[col] = !(col & 7);
- b->maxcols = cols;
- b->cols = cols;
- } else if (b->cols != cols) {
- for (int row = 0; row < b->rows; row++)
- lines[row].dirty = true;
- b->cols = cols;
- }
-
- int deltarows = 0;
- if (b->rows < rows) {
- while (b->rows < rows) {
- lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell));
- row_set(lines + b->rows, 0, b->maxcols, b);
- b->rows++;
- }
-
- /* prepare for backfill */
- if (b->curs_row >= b->scroll_bot - 1) {
- deltarows = b->lines + rows - b->curs_row - 1;
- if (deltarows > b->scroll_above)
- deltarows = b->scroll_above;
- }
- }
-
- b->curs_row += lines - b->lines;
- b->scroll_top = lines;
- b->scroll_bot = lines + rows;
- b->lines = lines;
-
- /* perform backfill */
- if (deltarows > 0) {
- buffer_scroll(b, -deltarows);
- b->curs_row += deltarows;
- }
-}
-
-static
-bool
-buffer_init(Buffer *b, int rows, int cols, int scroll_size)
-{
- b->curattrs = A_NORMAL; /* white text over black background */
- b->curfg = b->curbg = -1;
- if (scroll_size < 0)
- scroll_size = 0;
- if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row))))
- return false;
- b->scroll_size = scroll_size;
- buffer_resize(b, rows, cols);
- return true;
-}
-
-static
-void
-buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
-{
- if (bs)
- *bs = nil;
- if (be)
- *be = nil;
- if (as)
- *as = nil;
- if (ae)
- *ae = nil;
- if (!b->scroll_size)
- return;
-
- if (b->scroll_above) {
- if (bs)
- *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size];
- if (be)
- *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size];
- }
- if (b->scroll_below) {
- if (as)
- *as = &b->scroll_buf[b->scroll_index];
- if (ae)
- *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size];
- }
-}
-
-static
-Row *
-buffer_row_first(Buffer *b) {
- Row *bstart;
- if (!b->scroll_size || !b->scroll_above)
- return b->lines;
- buffer_boundry(b, &bstart, nil, nil, nil);
- return bstart;
-}
-
-static
-Row *
-buffer_row_last(Buffer *b) {
- Row *aend;
- if (!b->scroll_size || !b->scroll_below)
- return b->lines + b->rows - 1;
- buffer_boundry(b, nil, nil, nil, &aend);
- return aend;
-}
-
-static
-Row *
-buffer_row_next(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->lines, *last = b->lines + b->rows - 1;
-
- if (!row)
- return nil;
-
- buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row >= first && row < last)
- return ++row;
- if (row == last)
- return after_start;
- if (row == before_end)
- return first;
- if (row == after_end)
- return nil;
- if (row == &b->scroll_buf[b->scroll_size - 1])
- return b->scroll_buf;
- return ++row;
-}
-
-static
-Row *
-buffer_row_prev(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->lines, *last = b->lines + b->rows - 1;
-
- if (!row)
- return nil;
-
- buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row > first && row <= last)
- return --row;
- if (row == first)
- return before_end;
- if (row == before_start)
- return nil;
- if (row == after_start)
- return last;
- if (row == b->scroll_buf)
- return &b->scroll_buf[b->scroll_size - 1];
- return --row;
-}
-
-static
-void
-cursor_clamp(Vt *t)
-{
- Buffer *b = t->buffer;
- Row *lines = t->relposmode ? b->scroll_top : b->lines;
- int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows;
-
- if (b->curs_row < lines)
- b->curs_row = lines;
- if (b->curs_row >= lines + rows)
- b->curs_row = lines + rows - 1;
- if (b->curs_col < 0)
- b->curs_col = 0;
- if (b->curs_col >= b->cols)
- b->curs_col = b->cols - 1;
-}
-
-static
-void
-cursor_line_down(Vt *t)
-{
- Buffer *b = t->buffer;
- row_set(b->curs_row, b->cols, b->maxcols - b->cols, nil);
- b->curs_row++;
- if (b->curs_row < b->scroll_bot)
- return;
-
- vt_noscroll(t);
-
- b->curs_row = b->scroll_bot - 1;
- buffer_scroll(b, 1);
- row_set(b->curs_row, 0, b->cols, b);
-}
-
-static
-void
-cursor_save(Vt *t)
-{
- Buffer *b = t->buffer;
- b->curs_srow = b->curs_row - b->lines;
- b->curs_scol = b->curs_col;
-}
-
-static
-void
-cursor_restore(Vt *t)
-{
- Buffer *b = t->buffer;
- b->curs_row = b->lines + b->curs_srow;
- b->curs_col = b->curs_scol;
- cursor_clamp(t);
-}
-
-static
-void
-attributes_save(Vt *t)
-{
- Buffer *b = t->buffer;
- b->savattrs = b->curattrs;
- b->savfg = b->curfg;
- b->savbg = b->curbg;
- t->savgraphmode = t->graphmode;
-}
-
-static
-void
-attributes_restore(Vt *t)
-{
- Buffer *b = t->buffer;
- b->curattrs = b->savattrs;
- b->curfg = b->savfg;
- b->curbg = b->savbg;
- t->graphmode = t->savgraphmode;
-}
-
-static
-void
-new_escape_sequence(Vt *t)
-{
- t->escaped = true;
- t->elen = 0;
- t->ebuf[0] = '\0';
-}
-
-static
-void
-cancel_escape_sequence(Vt *t)
-{
- t->escaped = false;
- t->elen = 0;
- t->ebuf[0] = '\0';
-}
-
-static
-bool
-is_valid_csi_ender(int c)
-{
- return (c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c == '@' || c == '`');
-}
-
-/* interprets a 'set attribute' (SGR) CSI escape sequence */
-static
-void
-interpret_csi_sgr(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- if (pcount == 0) {
- /* special case: reset attributes */
- b->curattrs = A_NORMAL;
- b->curfg = b->curbg = -1;
- return;
- }
-
- for (int i = 0; i < pcount; i++) {
- switch (param[i]) {
- case 0:
- b->curattrs = A_NORMAL;
- b->curfg = b->curbg = -1;
- break;
- case 1:
- b->curattrs |= A_BOLD;
- break;
- case 2:
- b->curattrs |= A_DIM;
- break;
-#ifdef A_ITALIC
- case 3:
- b->curattrs |= A_ITALIC;
- break;
-#endif
- case 4:
- b->curattrs |= A_UNDERLINE;
- break;
- case 5:
- b->curattrs |= A_BLINK;
- break;
- case 7:
- b->curattrs |= A_REVERSE;
- break;
- case 8:
- b->curattrs |= A_INVIS;
- break;
- case 22:
- b->curattrs &= ~(A_BOLD | A_DIM);
- break;
-#ifdef A_ITALIC
- case 23:
- b->curattrs &= ~A_ITALIC;
- break;
-#endif
- case 24:
- b->curattrs &= ~A_UNDERLINE;
- break;
- case 25:
- b->curattrs &= ~A_BLINK;
- break;
- case 27:
- b->curattrs &= ~A_REVERSE;
- break;
- case 28:
- b->curattrs &= ~A_INVIS;
- break;
- case 30 ... 37: /* fg */
- b->curfg = param[i] - 30;
- break;
- case 38:
- if ((i + 2) < pcount && param[i + 1] == 5) {
- b->curfg = param[i + 2];
- i += 2;
- }
- break;
- case 39:
- b->curfg = -1;
- break;
- case 40 ... 47: /* bg */
- b->curbg = param[i] - 40;
- break;
- case 48:
- if ((i + 2) < pcount && param[i + 1] == 5) {
- b->curbg = param[i + 2];
- i += 2;
- }
- break;
- case 49:
- b->curbg = -1;
- break;
- case 90 ... 97: /* hi fg */
- b->curfg = param[i] - 82;
- break;
- case 100 ... 107: /* hi bg */
- b->curbg = param[i] - 92;
- break;
- default:
- break;
- }
- }
-}
-
-/* interprets an 'erase display' (ED) escape sequence */
-static
-void
-interpret_csi_ed(Vt *t, int param[], int pcount)
-{
- Row *row, *start, *end;
- Buffer *b = t->buffer;
-
- attributes_save(t);
- b->curattrs = A_NORMAL;
- b->curfg = b->curbg = -1;
-
- if (pcount && param[0] == 2) {
- start = b->lines;
- end = b->lines + b->rows;
- } else if (pcount && param[0] == 1) {
- start = b->lines;
- end = b->curs_row;
- row_set(b->curs_row, 0, b->curs_col + 1, b);
- } else {
- row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
- start = b->curs_row + 1;
- end = b->lines + b->rows;
- }
-
- for (row = start; row < end; row++)
- row_set(row, 0, b->cols, b);
-
- attributes_restore(t);
-}
-
-/* interprets a 'move cursor' (CUP) escape sequence */
-static
-void
-interpret_csi_cup(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- Row *lines = t->relposmode ? b->scroll_top : b->lines;
-
- if (pcount == 0) {
- b->curs_row = lines;
- b->curs_col = 0;
- } else if (pcount == 1) {
- b->curs_row = lines + param[0] - 1;
- b->curs_col = 0;
- } else {
- b->curs_row = lines + param[0] - 1;
- b->curs_col = param[1] - 1;
- }
-
- cursor_clamp(t);
-}
-
-/* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
- * CPL, CHA, HPR, VPA, VPR, HPA */
-static
-void
-interpret_csi_c(Vt *t, char verb, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- switch (verb) {
- case 'A':
- b->curs_row -= n;
- break;
- case 'B':
- case 'e':
- b->curs_row += n;
- break;
- case 'C':
- case 'a':
- b->curs_col += n;
- break;
- case 'D':
- b->curs_col -= n;
- break;
- case 'E':
- b->curs_row += n;
- b->curs_col = 0;
- break;
- case 'F':
- b->curs_row -= n;
- b->curs_col = 0;
- break;
- case 'G':
- case '`':
- b->curs_col = n - 1;
- break;
- case 'd':
- b->curs_row = b->lines + n - 1;
- break;
- }
-
- cursor_clamp(t);
-}
-
-/* Interpret the 'erase line' escape sequence */
-static
-void
-interpret_csi_el(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- switch (pcount ? param[0] : 0) {
- case 1:
- row_set(b->curs_row, 0, b->curs_col + 1, b);
- break;
- case 2:
- row_set(b->curs_row, 0, b->cols, b);
- break;
- default:
- row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
- break;
- }
-}
-
-/* Interpret the 'insert blanks' sequence (ICH) */
-static
-void
-interpret_csi_ich(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- Row *row = b->curs_row;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- if (b->curs_col + n > b->cols)
- n = b->cols - b->curs_col;
-
- for (int i = b->cols - 1; i >= b->curs_col + n; i--)
- row->cells[i] = row->cells[i - n];
-
- row_set(row, b->curs_col, n, b);
-}
-
-/* Interpret the 'delete chars' sequence (DCH) */
-static
-void
-interpret_csi_dch(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- Row *row = b->curs_row;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- if (b->curs_col + n > b->cols)
- n = b->cols - b->curs_col;
-
- for (int i = b->curs_col; i < b->cols - n; i++)
- row->cells[i] = row->cells[i + n];
-
- row_set(row, b->cols - n, n, b);
-}
-
-/* Interpret an 'insert line' sequence (IL) */
-static
-void
-interpret_csi_il(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- if (b->curs_row + n >= b->scroll_bot) {
- for (Row *row = b->curs_row; row < b->scroll_bot; row++)
- row_set(row, 0, b->cols, b);
- } else {
- row_roll(b->curs_row, b->scroll_bot, -n);
- for (Row *row = b->curs_row; row < b->curs_row + n; row++)
- row_set(row, 0, b->cols, b);
- }
-}
-
-/* Interpret a 'delete line' sequence (DL) */
-static
-void
-interpret_csi_dl(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- if (b->curs_row + n >= b->scroll_bot) {
- for (Row *row = b->curs_row; row < b->scroll_bot; row++)
- row_set(row, 0, b->cols, b);
- } else {
- row_roll(b->curs_row, b->scroll_bot, n);
- for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++)
- row_set(row, 0, b->cols, b);
- }
-}
-
-/* Interpret an 'erase characters' (ECH) sequence */
-static
-void
-interpret_csi_ech(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- int n = (pcount && param[0] > 0) ? param[0] : 1;
-
- if (b->curs_col + n > b->cols)
- n = b->cols - b->curs_col;
-
- row_set(b->curs_row, b->curs_col, n, b);
-}
-
-/* Interpret a 'set scrolling region' (DECSTBM) sequence */
-static
-void
-interpret_csi_decstbm(Vt *t, int param[], int pcount)
-{
- Buffer *b = t->buffer;
- int new_top, new_bot;
-
- switch (pcount) {
- case 0:
- b->scroll_top = b->lines;
- b->scroll_bot = b->lines + b->rows;
- break;
- case 2:
- new_top = param[0] - 1;
- new_bot = param[1];
-
- /* clamp to bounds */
- if (new_top < 0)
- new_top = 0;
- if (new_top >= b->rows)
- new_top = b->rows - 1;
- if (new_bot < 0)
- new_bot = 0;
- if (new_bot >= b->rows)
- new_bot = b->rows;
-
- /* check for range validity */
- if (new_top < new_bot) {
- b->scroll_top = b->lines + new_top;
- b->scroll_bot = b->lines + new_bot;
- }
- break;
- default:
- return; /* malformed */
- }
- b->curs_row = b->scroll_top;
- b->curs_col = 0;
-}
-
-static
-void
-interpret_csi_mode(Vt *t, int param[], int pcount, bool set)
-{
- for (int i = 0; i < pcount; i++) {
- switch (param[i]) {
- case 4: /* insert/replace mode */
- t->insert = set;
- break;
- }
- }
-}
-
-static
-void
-interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set)
-{
- for (int i = 0; i < pcount; i++) {
- switch (param[i]) {
- case 1: /* set application/normal cursor key mode (DECCKM) */
- t->curskeymode = set;
- break;
- case 6: /* set origin to relative/absolute (DECOM) */
- t->relposmode = set;
- break;
- case 25: /* make cursor visible/invisible (DECCM) */
- t->curshid = !set;
- break;
- case 1049: /* combine 1047 + 1048 */
- case 47: /* use alternate/normal screen buffer */
- case 1047:
- if (!set)
- buffer_clear(&t->buffer_alternate);
- t->buffer = set ? &t->buffer_alternate : &t->buffer_normal;
- vt_dirty(t);
- if (param[i] != 1049)
- break;
- /* fall through */
- case 1048: /* save/restore cursor */
- if (set)
- cursor_save(t);
- else
- cursor_restore(t);
- break;
- case 1000: /* enable/disable normal mouse tracking */
- t->mousetrack = set;
- break;
- }
- }
-}
-
-static
-void
-interpret_csi(Vt *t)
-{
- Buffer *b = t->buffer;
- int csiparam[16];
- uint param_count = 0;
- const char *p = t->ebuf + 1;
- char verb = t->ebuf[t->elen - 1];
-
- /* parse numeric parameters */
- for (p += (t->ebuf[1] == '?'); *p; p++) {
- if (IS_CONTROL(*p)) {
- process_nonprinting(t, *p);
- } else if (*p == ';') {
- if (param_count >= arrlen(csiparam))
- return; /* too long! */
- csiparam[param_count++] = 0;
- } else if (isdigit((uchar)*p)) {
- if (param_count == 0)
- csiparam[param_count++] = 0;
- csiparam[param_count - 1] *= 10;
- csiparam[param_count - 1] += *p - '0';
- }
- }
-
- if (t->ebuf[1] == '?') {
- switch (verb) {
- case 'h':
- case 'l': /* private set/reset mode */
- interpret_csi_priv_mode(t, csiparam, param_count, verb == 'h');
- break;
- }
- return;
- }
-
- /* delegate handling depending on command character (verb) */
- switch (verb) {
- case 'h':
- case 'l': /* set/reset mode */
- interpret_csi_mode(t, csiparam, param_count, verb == 'h');
- break;
- case 'm': /* set attribute */
- interpret_csi_sgr(t, csiparam, param_count);
- break;
- case 'J': /* erase display */
- interpret_csi_ed(t, csiparam, param_count);
- break;
- case 'H':
- case 'f': /* move cursor */
- interpret_csi_cup(t, csiparam, param_count);
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- case 'G':
- case 'e':
- case 'a':
- case 'd':
- case '`': /* relative move */
- interpret_csi_c(t, verb, csiparam, param_count);
- break;
- case 'K': /* erase line */
- interpret_csi_el(t, csiparam, param_count);
- break;
- case '@': /* insert characters */
- interpret_csi_ich(t, csiparam, param_count);
- break;
- case 'P': /* delete characters */
- interpret_csi_dch(t, csiparam, param_count);
- break;
- case 'L': /* insert lines */
- interpret_csi_il(t, csiparam, param_count);
- break;
- case 'M': /* delete lines */
- interpret_csi_dl(t, csiparam, param_count);
- break;
- case 'X': /* erase chars */
- interpret_csi_ech(t, csiparam, param_count);
- break;
- case 'S': /* SU: scroll up */
- vt_scroll(t, param_count ? -csiparam[0] : -1);
- break;
- case 'T': /* SD: scroll down */
- vt_scroll(t, param_count ? csiparam[0] : 1);
- break;
- case 'Z': /* CBT: cursor backward tabulation */
- puttab(t, param_count ? -csiparam[0] : -1);
- break;
- case 'g': /* TBC: tabulation clear */
- switch (param_count ? csiparam[0] : 0) {
- case 0:
- b->tabs[b->curs_col] = false;
- break;
- case 3:
- memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols);
- break;
- }
- break;
- case 'r': /* set scrolling region */
- interpret_csi_decstbm(t, csiparam, param_count);
- break;
- case 's': /* save cursor location */
- cursor_save(t);
- break;
- case 'u': /* restore cursor location */
- cursor_restore(t);
- break;
- case 'n': /* query cursor location */
- if (param_count == 1 && csiparam[0] == 6)
- send_curs(t);
- break;
- default:
- break;
- }
-}
-
-/* Interpret an 'index' (IND) sequence */
-static
-void
-interpret_csi_ind(Vt *t)
-{
- Buffer *b = t->buffer;
- if (b->curs_row < b->lines + b->rows - 1)
- b->curs_row++;
-}
-
-/* Interpret a 'reverse index' (RI) sequence */
-static
-void
-interpret_csi_ri(Vt *t)
-{
- Buffer *b = t->buffer;
- if (b->curs_row > b->scroll_top)
- b->curs_row--;
- else {
- row_roll(b->scroll_top, b->scroll_bot, -1);
- row_set(b->scroll_top, 0, b->cols, b);
- }
-}
-
-/* Interpret a 'next line' (NEL) sequence */
-static
-void
-interpret_csi_nel(Vt *t)
-{
- Buffer *b = t->buffer;
- if (b->curs_row < b->lines + b->rows - 1) {
- b->curs_row++;
- b->curs_col = 0;
- }
-}
-
-/* Interpret a 'select character set' (SCS) sequence */
-static
-void
-interpret_csi_scs(Vt *t)
-{
- /* ESC ( sets G0, ESC ) sets G1 */
- t->charsets[!!(t->ebuf[0] == ')')] = (t->ebuf[1] == '0');
- t->graphmode = t->charsets[0];
-}
-
-/* Interpret an 'operating system command' (OSC) sequence */
-static
-void
-interpret_osc(Vt *t)
-{
- /* ESC ] command ; data BEL
- * ESC ] command ; data ESC \\
- * Note that BEL or ESC \\ have already been replaced with NUL.
- */
- char *data = nil;
- int command = strtoul(t->ebuf + 1, &data, 10);
- if (data && *data == ';') {
- switch (command) {
- case 0: /* icon name and window title */
- case 2: /* window title */
- if (t->title_handler)
- t->title_handler(t, data+1);
- break;
- case 1: /* icon name */
- break;
- default:
-#ifndef NDEBUG
- fprintf(stderr, "unknown OSC command: %d\n", command);
-#endif
- break;
- }
- }
-}
-
-static
-void
-try_interpret_escape_seq(Vt *t)
-{
- char lastchar = t->ebuf[t->elen - 1];
-
- if (!*t->ebuf)
- return;
-
- switch (*t->ebuf) {
- case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
- if (t->elen == 2) {
- if (lastchar == '8') { /* DECALN */
- interpret_csi_ed(t, (int []){ 2 }, 1);
- goto handled;
- }
- goto cancel;
- }
- break;
- case '(':
- case ')':
- if (t->elen == 2) {
- interpret_csi_scs(t);
- goto handled;
- }
- break;
- case ']': /* OSC - operating system command */
- if (lastchar == '\a' ||
- (lastchar == '\\' && t->elen >= 2 && t->ebuf[t->elen - 2] == '\e')) {
- t->elen -= lastchar == '\a' ? 1 : 2;
- t->ebuf[t->elen] = '\0';
- interpret_osc(t);
- goto handled;
- }
- break;
- case '[': /* CSI - control sequence introducer */
- if (is_valid_csi_ender(lastchar)) {
- interpret_csi(t);
- goto handled;
- }
- break;
- case '7': /* DECSC: save cursor and attributes */
- attributes_save(t);
- cursor_save(t);
- goto handled;
- case '8': /* DECRC: restore cursor and attributes */
- attributes_restore(t);
- cursor_restore(t);
- goto handled;
- case 'D': /* IND: index */
- interpret_csi_ind(t);
- goto handled;
- case 'M': /* RI: reverse index */
- interpret_csi_ri(t);
- goto handled;
- case 'E': /* NEL: next line */
- interpret_csi_nel(t);
- goto handled;
- case 'H': /* HTS: horizontal tab set */
- t->buffer->tabs[t->buffer->curs_col] = true;
- goto handled;
- default:
- goto cancel;
- }
-
- if (t->elen + 1 >= sizeof(t->ebuf)) {
-cancel:
-#ifndef NDEBUG
- fprintf(stderr, "cancelled: \\033");
- for (uint i = 0; i < t->elen; i++) {
- if (isprint(t->ebuf[i])) {
- fputc(t->ebuf[i], stderr);
- } else {
- fprintf(stderr, "\\%03o", t->ebuf[i]);
- }
- }
- fputc('\n', stderr);
-#endif
-handled:
- cancel_escape_sequence(t);
- }
-}
-
-static
-void
-puttab(Vt *t, int count)
-{
- Buffer *b = t->buffer;
- int direction = count >= 0 ? 1 : -1;
- for (int col = b->curs_col + direction; count; col += direction) {
- if (col < 0) {
- b->curs_col = 0;
- break;
- }
- if (col >= b->cols) {
- b->curs_col = b->cols - 1;
- break;
- }
- if (b->tabs[col]) {
- b->curs_col = col;
- count -= direction;
- }
- }
-}
-
-static
-void
-process_nonprinting(Vt *t, wchar_t wc)
-{
- Buffer *b = t->buffer;
- switch (wc) {
- case '\e': /* ESC */
- new_escape_sequence(t);
- break;
- case '\a': /* BEL */
- if (t->urgent_handler)
- t->urgent_handler(t);
- break;
- case '\b': /* BS */
- if (b->curs_col > 0)
- b->curs_col--;
- break;
- case '\t': /* HT */
- puttab(t, 1);
- break;
- case '\r': /* CR */
- b->curs_col = 0;
- break;
- case '\v': /* VT */
- case '\f': /* FF */
- case '\n': /* LF */
- cursor_line_down(t);
- break;
- case '\016': /* SO: shift out, invoke the G1 character set */
- t->graphmode = t->charsets[1];
- break;
- case '\017': /* SI: shift in, invoke the G0 character set */
- t->graphmode = t->charsets[0];
- break;
- }
-}
-
-static
-void
-is_utf8_locale(void)
-{
- const char *cset = nl_langinfo(CODESET);
- if (!cset)
- cset = "ANSI_X3.4-1968";
- is_utf8 = !strcmp(cset, "UTF-8");
-}
-
-static
-wchar_t
-get_vt100_graphic(char c)
-{
- static char vt100_acs[] = "`afgjklmnopqrstuvwxyz{|}~";
-
- /*
- * 5f-7e standard vt100
- * 40-5e rxvt extension for extra curses acs chars
- */
- static uint16_t const vt100_utf8[62] = {
- 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
- 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
- 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
- 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
- 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
- 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
- 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
- 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
- };
-
- if (is_utf8)
- return vt100_utf8[c - 0x41];
- else if (strchr(vt100_acs, c))
- return NCURSES_ACS(c);
- return '\0';
-}
-
-static
-void
-put_wc(Vt *t, wchar_t wc)
-{
- int width = 0;
-
- if (!t->seen_input) {
- t->seen_input = 1;
- kill(-t->pid, SIGWINCH);
- }
-
- if (t->escaped) {
- if (t->elen + 1 < sizeof(t->ebuf)) {
- t->ebuf[t->elen] = wc;
- t->ebuf[++t->elen] = '\0';
- try_interpret_escape_seq(t);
- } else {
- cancel_escape_sequence(t);
- }
- } else if (IS_CONTROL(wc)) {
- process_nonprinting(t, wc);
- } else {
- if (t->graphmode) {
- if (wc >= 0x41 && wc <= 0x7e) {
- wchar_t gc = get_vt100_graphic(wc);
- if (gc)
- wc = gc;
- }
- width = 1;
- } else if ((width = wcwidth(wc)) < 1) {
- width = 1;
- }
- Buffer *b = t->buffer;
- Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg };
- if (width == 2 && b->curs_col == b->cols - 1) {
- b->curs_row->cells[b->curs_col++] = blank_cell;
- b->curs_row->dirty = true;
- }
-
- if (b->curs_col >= b->cols) {
- b->curs_col = 0;
- cursor_line_down(t);
- }
-
- if (t->insert) {
- Cell *src = b->curs_row->cells + b->curs_col;
- Cell *dest = src + width;
- size_t len = b->cols - b->curs_col - width;
- memmove(dest, src, len * sizeof *dest);
- }
-
- b->curs_row->cells[b->curs_col] = blank_cell;
- b->curs_row->cells[b->curs_col++].text = wc;
- b->curs_row->dirty = true;
- if (width == 2)
- b->curs_row->cells[b->curs_col++] = blank_cell;
- }
-}
-
-int
-vt_process(Vt *t)
-{
- int res;
- uint pos = 0;
- mbstate_t ps;
- memset(&ps, 0, sizeof(ps));
-
- if (t->pty < 0) {
- errno = EINVAL;
- return -1;
- }
-
- res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen);
- if (res < 0)
- return -1;
-
- t->rlen += res;
- while (pos < t->rlen) {
- wchar_t wc;
- ssize_t len;
-
- len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps);
- if (len == -2) {
- t->rlen -= pos;
- memmove(t->rbuf, t->rbuf + pos, t->rlen);
- return 0;
- }
-
- if (len == -1) {
- len = 1;
- wc = t->rbuf[pos];
- }
-
- pos += len ? len : 1;
- put_wc(t, wc);
- }
-
- t->rlen -= pos;
- memmove(t->rbuf, t->rbuf + pos, t->rlen);
- return 0;
-}
-
-void
-vt_default_colors_set(Vt *t, attr_t attrs, int fg, int bg)
-{
- t->defattrs = attrs;
- t->deffg = fg;
- t->defbg = bg;
-}
-
-Vt *
-vt_create(int rows, int cols, int scroll_size)
-{
- if (rows <= 0 || cols <= 0)
- return nil;
-
- Vt *t = calloc(1, sizeof(Vt));
- if (!t)
- return nil;
-
- t->pty = -1;
- t->deffg = t->defbg = -1;
- t->buffer = &t->buffer_normal;
-
- if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) ||
- !buffer_init(&t->buffer_alternate, rows, cols, 0)) {
- free(t);
- return nil;
- }
-
- return t;
-}
-
-void
-vt_resize(Vt *t, int rows, int cols)
-{
- struct winsize ws = { .ws_row = rows, .ws_col = cols };
-
- if (rows <= 0 || cols <= 0)
- return;
-
- vt_noscroll(t);
- buffer_resize(&t->buffer_normal, rows, cols);
- buffer_resize(&t->buffer_alternate, rows, cols);
- cursor_clamp(t);
- ioctl(t->pty, TIOCSWINSZ, &ws);
- kill(-t->pid, SIGWINCH);
-}
-
-void
-vt_destroy(Vt *t)
-{
- if (!t)
- return;
- buffer_free(&t->buffer_normal);
- buffer_free(&t->buffer_alternate);
- close(t->pty);
- free(t);
-}
-
-void
-vt_dirty(Vt *t)
-{
- Buffer *b = t->buffer;
- for (Row *row = b->lines, *end = row + b->rows; row < end; row++)
- row->dirty = true;
-}
-
-void
-vt_draw(Vt *t, WINDOW *win, int srow, int scol)
-{
- Buffer *b = t->buffer;
-
- if (srow != t->srow || scol != t->scol) {
- vt_dirty(t);
- t->srow = srow;
- t->scol = scol;
- }
-
- for (int i = 0; i < b->rows; i++) {
- Row *row = b->lines + i;
-
- if (!row->dirty)
- continue;
-
- wmove(win, srow + i, scol);
- Cell *cell = nil;
- for (int j = 0; j < b->cols; j++) {
- Cell *prev_cell = cell;
- cell = row->cells + j;
- if (!prev_cell || cell->attr != prev_cell->attr
- || cell->fg != prev_cell->fg
- || cell->bg != prev_cell->bg) {
- if (cell->attr == A_NORMAL)
- cell->attr = t->defattrs;
- if (cell->fg == -1)
- cell->fg = t->deffg;
- if (cell->bg == -1)
- cell->bg = t->defbg;
- wattrset(win, cell->attr << NCURSES_ATTR_SHIFT);
- wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), nil);
- }
-
- if (is_utf8 && cell->text >= 128) {
- char buf[MB_CUR_MAX + 1];
- size_t len = wcrtomb(buf, cell->text, nil);
- if (len > 0) {
- waddnstr(win, buf, len);
- if (wcwidth(cell->text) > 1)
- j++;
- }
- } else {
- waddch(win, cell->text > ' ' ? cell->text : ' ');
- }
- }
-
- int x, y;
- getyx(win, y, x);
- (void)y;
- if (x && x < b->cols - 1)
- whline(win, ' ', b->cols - x);
-
- row->dirty = false;
- }
-
- wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col);
-}
-
-void
-vt_scroll(Vt *t, int rows)
-{
- Buffer *b = t->buffer;
- if (!b->scroll_size)
- return;
- if (rows < 0) { /* scroll back */
- if (rows < -b->scroll_above)
- rows = -b->scroll_above;
- } else { /* scroll forward */
- if (rows > b->scroll_below)
- rows = b->scroll_below;
- }
- buffer_scroll(b, rows);
- b->scroll_below -= rows;
-}
-
-void
-vt_noscroll(Vt *t)
-{
- int scroll_below = t->buffer->scroll_below;
- if (scroll_below)
- vt_scroll(t, scroll_below);
-}
-
-pid_t
-vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from)
-{
- int vt2ed[2], ed2vt[2];
- struct winsize ws;
- ws.ws_row = t->buffer->rows;
- ws.ws_col = t->buffer->cols;
- ws.ws_xpixel = ws.ws_ypixel = 0;
-
- if (to && pipe(vt2ed)) {
- *to = -1;
- to = nil;
- }
- if (from && pipe(ed2vt)) {
- *from = -1;
- from = nil;
- }
-
- pid_t pid = forkpty(&t->pty, nil, nil, &ws);
- if (pid < 0)
- return -1;
-
- if (pid == 0) {
- setsid();
-
- sigset_t emptyset;
- sigemptyset(&emptyset);
- sigprocmask(SIG_SETMASK, &emptyset, nil);
-
- if (to) {
- close(vt2ed[1]);
- dup2(vt2ed[0], STDIN_FILENO);
- close(vt2ed[0]);
- }
-
- if (from) {
- close(ed2vt[0]);
- dup2(ed2vt[1], STDOUT_FILENO);
- close(ed2vt[1]);
- }
-
- int maxfd = sysconf(_SC_OPEN_MAX);
- for (int fd = 3; fd < maxfd; fd++)
- if (close(fd) == -1 && errno == EBADF)
- break;
-
- for (const char **envp = env; envp && envp[0]; envp += 2)
- setenv(envp[0], envp[1], 1);
- setenv("TERM", vt_term, 1);
-
- if (cwd)
- chdir(cwd);
-
- execvp(p, (char *const *)argv);
- fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]);
- exit(1);
- }
-
- if (to) {
- close(vt2ed[0]);
- *to = vt2ed[1];
- }
-
- if (from) {
- close(ed2vt[1]);
- *from = ed2vt[0];
- }
-
- return t->pid = pid;
-}
-
-int
-vt_pty_get(Vt *t)
-{
- return t->pty;
-}
-
-ssize_t
-vt_write(Vt *t, const char *buf, size_t len)
-{
- ssize_t ret = len;
-
- while (len > 0) {
- ssize_t res = write(t->pty, buf, len);
- if (res < 0) {
- if (errno != EAGAIN && errno != EINTR)
- return -1;
- continue;
- }
- buf += res;
- len -= res;
- }
-
- return ret;
-}
-
-static
-void
-send_curs(Vt *t)
-{
- Buffer *b = t->buffer;
- char keyseq[16];
- snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col);
- vt_write(t, keyseq, strlen(keyseq));
-}
-
-void
-vt_keypress(Vt *t, int keycode)
-{
- vt_noscroll(t);
-
- if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) {
- switch (keycode) {
- case KEY_UP:
- case KEY_DOWN:
- case KEY_RIGHT:
- case KEY_LEFT: {
- char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] };
- vt_write(t, keyseq, sizeof keyseq);
- break;
- }
- default:
- vt_write(t, keytable[keycode], strlen(keytable[keycode]));
- }
- } else if (keycode <= UCHAR_MAX) {
- char c = keycode;
- vt_write(t, &c, 1);
- } else {
-#ifndef NDEBUG
- fprintf(stderr, "unhandled key %#o\n", keycode);
-#endif
- }
-}
-
-void
-vt_mouse(Vt *t, int x, int y, mmask_t mask)
-{
-#ifdef NCURSES_MOUSE_VERSION
- char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0;
-
- if (!t->mousetrack)
- return;
-
- if (mask & (BUTTON1_PRESSED | BUTTON1_CLICKED))
- button = 0;
- else if (mask & (BUTTON2_PRESSED | BUTTON2_CLICKED))
- button = 1;
- else if (mask & (BUTTON3_PRESSED | BUTTON3_CLICKED))
- button = 2;
- else if (mask & (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED))
- button = 3;
-
- if (mask & BUTTON_SHIFT)
- state |= 4;
- if (mask & BUTTON_ALT)
- state |= 8;
- if (mask & BUTTON_CTRL)
- state |= 16;
-
- seq[3] = 32 + button + state;
- seq[4] = 32 + x;
- seq[5] = 32 + y;
-
- vt_write(t, seq, sizeof seq);
-
- if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) {
- /* send a button release event */
- button = 3;
- seq[3] = 32 + button + state;
- vt_write(t, seq, sizeof seq);
- }
-#endif /* NCURSES_MOUSE_VERSION */
-}
-
-static
-uint
-color_hash(short fg, short bg)
-{
- if (fg == -1)
- fg = COLORS;
- if (bg == -1)
- bg = COLORS + 1;
- return fg * (COLORS + 2) + bg;
-}
-
-int
-vt_color_get(Vt *t, int fg, int bg)
-{
- if (fg >= COLORS)
- fg = (t ? t->deffg : default_fg);
- if (bg >= COLORS)
- bg = (t ? t->defbg : default_bg);
-
- if (!has_default_colors) {
- if (fg == -1)
- fg = (t && t->deffg != -1 ? t->deffg : default_fg);
- if (bg == -1)
- bg = (t && t->defbg != -1 ? t->defbg : default_bg);
- }
-
- if (!color2palette || (fg == -1 && bg == -1))
- return 0;
- uint index = color_hash(fg, bg);
- if (color2palette[index] == 0) {
- int oldfg, oldbg;
- for (;;) {
- if (++color_pair_current >= color_pairs_max)
- color_pair_current = color_pairs_reserved + 1;
- extended_pair_content(color_pair_current, &oldfg, &oldbg);
- uint old_index = color_hash(oldfg, oldbg);
- if (color2palette[old_index] >= 0) {
- if (init_extended_pair(color_pair_current, fg, bg) == OK) {
- color2palette[old_index] = 0;
- color2palette[index] = color_pair_current;
- }
- break;
- }
- }
- }
-
- int color_pair = color2palette[index];
- return color_pair >= 0 ? color_pair : -color_pair;
-}
-
-int
-vt_color_reserve(int fg, int bg)
-{
- if (!color2palette || fg >= COLORS || bg >= COLORS)
- return 0;
-
- if (!has_default_colors && fg == -1)
- fg = default_fg;
- if (!has_default_colors && bg == -1)
- bg = default_bg;
- if (fg == -1 && bg == -1)
- return 0;
-
- uint index = color_hash(fg, bg);
- if (color2palette[index] >= 0) {
- if (init_extended_pair(color_pairs_reserved + 1, fg, bg) == OK)
- color2palette[index] = -(++color_pairs_reserved);
- }
- int color_pair = color2palette[index];
- return color_pair >= 0 ? color_pair : -color_pair;
-}
-
-static
-void
-init_colors(void)
-{
- extended_pair_content(0, &default_fg, &default_bg);
- if (default_fg == -1)
- default_fg = COLOR_WHITE;
- if (default_bg == -1)
- default_bg = COLOR_BLACK;
- has_default_colors = (use_default_colors() == OK);
- color_pairs_max = MIN(MAX_COLOR_PAIRS, SHRT_MAX);
-
- if (COLORS)
- color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(int));
- /*
- * XXX: On undefined color-pairs NetBSD curses pair_content() set fg
- * and bg to default colors while ncurses set them respectively to
- * 0 and 0. Initialize all color-pairs in order to have consistent
- * behaviour despite the implementation used.
- */
- for (short i = 1; i < color_pairs_max; i++)
- init_extended_pair(i, 0, 0);
-
- vt_color_reserve(COLOR_WHITE, COLOR_BLACK);
-}
-
-void
-vt_init(void)
-{
- init_colors();
- is_utf8_locale();
- char *term = getenv("DVTM_TERM");
- if (!term)
- term = "dvtm";
-
- snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : "");
-}
-
-void
-vt_keytable_set(const char * const keytable_overlay[], int count)
-{
- for (int k = 0; k < count && k < KEY_MAX; k++) {
- const char *keyseq = keytable_overlay[k];
- if (keyseq)
- keytable[k] = keyseq;
- }
-}
-
-void
-vt_shutdown(void)
-{
- free(color2palette);
-}
-
-void
-vt_title_handler_set(Vt *t, vt_title_handler_t handler)
-{
- t->title_handler = handler;
-}
-
-void
-vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler)
-{
- t->urgent_handler = handler;
-}
-
-void
-vt_data_set(Vt *t, void *data)
-{
- t->data = data;
-}
-
-void *
-vt_data_get(Vt *t)
-{
- return t->data;
-}
-
-bool
-vt_cursor_visible(Vt *t)
-{
- return t->buffer->scroll_below ? false : !t->curshid;
-}
-
-pid_t
-vt_pid_get(Vt *t)
-{
- return t->pid;
-}
-
-size_t
-vt_content_get(Vt *t, char **buf, bool colored)
-{
- Buffer *b = t->buffer;
- int lines = b->scroll_above + b->scroll_below + b->rows + 1;
- size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX));
- mbstate_t ps;
- memset(&ps, 0, sizeof(ps));
-
- if (!(*buf = malloc(size)))
- return 0;
-
- char *s = *buf;
- Cell *prev_cell = nil;
-
- for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) {
- size_t len = 0;
- char *last_non_space = s;
- for (int col = 0; col < b->cols; col++) {
- Cell *cell = row->cells + col;
- if (colored) {
- int esclen = 0;
- if (!prev_cell || cell->attr != prev_cell->attr) {
- attr_t attr = cell->attr << NCURSES_ATTR_SHIFT;
- esclen = sprintf(s, "\033[0%s%s%s%s%s%sm",
- attr & A_BOLD ? ";1" : "",
- attr & A_DIM ? ";2" : "",
- attr & A_UNDERLINE ? ";4" : "",
- attr & A_BLINK ? ";5" : "",
- attr & A_REVERSE ? ";7" : "",
- attr & A_INVIS ? ";8" : "");
- if (esclen > 0)
- s += esclen;
- }
- if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) {
- if (cell->fg == -1)
- esclen = sprintf(s, "\033[39m");
- else
- esclen = sprintf(s, "\033[38;5;%dm", cell->fg);
- if (esclen > 0)
- s += esclen;
- }
- if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) {
- if (cell->bg == -1)
- esclen = sprintf(s, "\033[49m");
- else
- esclen = sprintf(s, "\033[48;5;%dm", cell->bg);
- if (esclen > 0)
- s += esclen;
- }
- prev_cell = cell;
- }
- if (cell->text) {
- len = wcrtomb(s, cell->text, &ps);
- if (len > 0)
- s += len;
- last_non_space = s;
- } else if (len) {
- len = 0;
- } else {
- *s++ = ' ';
- }
- }
-
- s = last_non_space;
- *s++ = '\n';
- }
-
- return s - *buf;
-}
-
-int
-vt_content_start(Vt *t)
-{
- return t->buffer->scroll_above;
-}
diff --git a/sys/cmd/dvtm/vt.h b/sys/cmd/dvtm/vt.h
index 3d4d129..dfc94b3 100644
--- a/sys/cmd/dvtm/vt.h
+++ b/sys/cmd/dvtm/vt.h
@@ -2,7 +2,7 @@
#pragma once
#include <u.h>
-// #include <vendor/curses.h>
+#include <vendor/curses.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -18,7 +18,7 @@ void vt_init(void);
void vt_shutdown(void);
void vt_keytable_set(char const * const keytable_overlay[], int count);
-void vt_default_colors_set(Vt*, attr_t attrs, int fg, int bg);
+void vt_default_colors_set(Vt*, attr_t attrs, short fg, short bg);
void vt_title_handler_set(Vt*, vt_title_handler_t);
void vt_urgent_handler_set(Vt*, vt_urgent_handler_t);
void vt_data_set(Vt*, void *);
@@ -37,8 +37,8 @@ ssize_t vt_write(Vt*, const char *buf, size_t len);
void vt_mouse(Vt*, int x, int y, mmask_t mask);
void vt_dirty(Vt*);
void vt_draw(Vt*, WINDOW *win, int startrow, int startcol);
-int vt_color_get(Vt*, int fg, int bg);
-int vt_color_reserve(int fg, int bg);
+int vt_color_get(Vt*, short fg, short bg);
+int vt_color_reserve(short fg, short bg);
void vt_scroll(Vt*, int rows);
void vt_noscroll(Vt*);
diff --git a/sys/cmd/dvtm/window.c b/sys/cmd/dvtm/window.c
deleted file mode 100644
index fec3997..0000000
--- a/sys/cmd/dvtm/window.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include <u.h>
-#include <libn.h>
-
-#include "term.h"
-
-typedef struct Rect Rect;
-
-struct Rect
-{
- int top, left, rows, cols;
-};
-
-struct Window
-{
- Buffer buffer[2], *buf;
- Rect area; /* on screen */
- Pen pen, spen; /* current and saved pen */
- uint curvis : 1;
- uint damage : 1;
-};
-
-/* functions */
-
-Window *
-makewindow(Window *root, Rect area, int history)
-{
- Window *w;
- w = calloc(1, sizeof(*w));
- if (!w)
- panicf("out of memory");
-
- w->pen = (Pen) {
- .state = PenNormal,
- .col = {-1, -1},
- };
-
- if (!binit(w->buffer+0, area.rows, area.cols, history) ||
- !binit(w->buffer+1, area.rows, area.cols, 0)) {
- free(w);
- return nil;
- }
- w->buf = w->buffer;
- return w;
-}
diff --git a/sys/cmd/dwm/client.c b/sys/cmd/dwm/client.c
index 783190a..50d4da9 100644
--- a/sys/cmd/dwm/client.c
+++ b/sys/cmd/dwm/client.c
@@ -362,22 +362,18 @@ setfocus(Client *c)
void
setfullscreen(Client *c, int fullscreen)
{
-
+ static ulong opacity = 0xFFFFFFFFul;
if (fullscreen && !c->isfullscreen) {
- // ulong rect[4] = { 0, 0, c->w, c->h };
XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
PropModeReplace, (uchar*)&netatom[NetWMFullscreen], 1);
-
- static ulong opacity = 0xFFFFFFFFul;
XChangeProperty(dpy, c->win, netatom[NetWMWindowOpacity], XA_CARDINAL, 32, PropModeReplace, (uchar *)&opacity, 1L);
- // XChangeProperty(dpy, c->win, netatom[NetWMOpaqueRegion], XA_CARDINAL, 32,
- // PropModeReplace, (uchar*)&rect, arrlen(rect));
c->isfullscreen = 1;
c->oldstate = c->isfloating;
c->oldbw = c->bw;
c->bw = 0;
c->isfloating = 1;
+
resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
XRaiseWindow(dpy, c->win);
diff --git a/sys/cmd/dwm/config.h b/sys/cmd/dwm/config.h
index ce5b196..053b9f8 100644
--- a/sys/cmd/dwm/config.h
+++ b/sys/cmd/dwm/config.h
@@ -60,9 +60,9 @@ static Layout layouts[] = {
static char *menucmd[] = { "menu_run", nil };
static char *termcmd[] = { "term", nil };
static char *webscmd[] = { "qutebrowser", nil };
-static char *upvolcmd[] = { "pactl", "set-sink-volume", "0", "+5%", nil };
-static char *lovolcmd[] = { "pactl", "set-sink-volume", "0", "-5%", nil };
-static char *novolcmd[] = { "pactl", "set-sink-mute", "0", "toggle", nil };
+static char *upvolcmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", nil };
+static char *lovolcmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", nil };
+static char *novolcmd[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", nil };
#define XK_lovol XF86XK_AudioLowerVolume
#define XK_upvol XF86XK_AudioRaiseVolume
@@ -86,7 +86,7 @@ static Key keys[] = {
{ MODKEY, XK_o, incnmaster, {.i = -1 } },
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
- { MODKEY, XK_Return, zoom, {0} },
+ { MODKEY|ShiftMask, XK_Return, zoom, {0} },
{ MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_q, killclient, {0} },
{ MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} },
diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c
new file mode 100644
index 0000000..f38dd43
--- /dev/null
+++ b/sys/cmd/rc/code.c
@@ -0,0 +1,356 @@
+#include "rc.h"
+
+#define delcode 100
+#define c0 t->child[0]
+#define c1 t->child[1]
+#define c2 t->child[2]
+
+#define emitf(x) ((code.ip!=code.end || morecode()), code.ip++->f = (x))
+#define emiti(x) ((code.ip!=code.end || morecode()), code.ip++->i = (x))
+#define emits(x) ((code.ip!=code.end || morecode()), code.ip++->s = (x))
+
+static struct
+{
+ int cap;
+ Code *buf, *ip, *end;
+} code;
+
+static
+int
+morecode(void)
+{
+ code.cap += delcode;
+ code.buf = erealloc(code.buf, code.cap*sizeof(*code.buf));
+ code.end = code.ip + delcode;
+ memset(code.ip, 0, delcode*sizeof(*code.buf));
+
+ return 0;
+}
+
+static
+void
+stuffdot(Code *p)
+{
+ int a;
+
+ a = p - code.buf;
+ if (code.ip <= p || p < code.buf)
+ panic("bad address %d in stuffdot", a);
+ codebuf[a].i = code.ip-code.buf;
+}
+
+static
+void
+rcc(Tree *t, int eflag)
+{
+ Code *p, *q;
+ Tree *tt;
+
+ if (!t)
+ return;
+
+ switch(t->type) {
+ default:
+ pfmt(errio, "bad type %d in rc compiler\n", t->type);
+ break;
+ case '$':
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xdol);
+ break;
+ case '"':
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xqdol);
+ break;
+ case Asub:
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xmark);
+ rcc(c1, eflag);
+ emitf(Xsub);
+ break;
+ case '&':
+ emitf(Xasync);
+ if(havefork){
+ p = emiti(0);
+ rcc(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ } else
+ emits(fnstr(c0));
+ break;
+ case ';':
+ rcc(c0, eflag);
+ rcc(c1, eflag);
+ break;
+ case '^':
+ emitf(Xmark);
+ rcc(c1, eflag);
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xconc);
+ break;
+ case '`':
+ emitf(Xbackq);
+ if(havefork){
+ p = emiti(0);
+ rcc(c0, 0);
+ emitf(Xexit);
+ stuffdot(p);
+ } else
+ emits(fnstr(c0));
+ break;
+ case Aandand:
+ rcc(c0, 0);
+ emitf(Xtrue);
+ p = emiti(0);
+ rcc(c1, eflag);
+ stuffdot(p);
+ break;
+ case Aargs:
+ rcc(c1, eflag);
+ rcc(c0, eflag);
+ break;
+ case Kbang:
+ rcc(c0, eflag);
+ emitf(Xbang);
+ break;
+ case Aparen:
+ case Abrace:
+ rcc(c0, eflag);
+ break;
+ case Acount:
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xcount);
+ break;
+ case Kfunc:
+ emitf(Xmark);
+ rcc(c0, eflag);
+ if(c1){
+ emitf(Xfn);
+ p = emiti(0);
+ emits(fnstr(c1));
+ rcc(c1, eflag);
+ emitf(Xunlocal); /* get rid of $* */
+ emitf(Xreturn);
+ stuffdot(p);
+ }
+ else
+ emitf(Xdelfn);
+ break;
+ case Kif:
+ rcc(c0, 0);
+ emitf(Xif);
+ p = emiti(0);
+ rcc(c1, eflag);
+ emitf(Xwastrue);
+ stuffdot(p);
+ break;
+ case Kelse:
+ if(!runq->iflast)
+ error("`else' does not follow `if(...)'");
+ emitf(Xelse);
+ p = emiti(0);
+ rcc(c0, eflag);
+ stuffdot(p);
+ break;
+ case Aoror:
+ rcc(c0, 0);
+ emitf(Xfalse);
+ p = emiti(0);
+ rcc(c1, eflag);
+ stuffdot(p);
+ break;
+ case Aparen:
+ rcc(c0, eflag);
+ break;
+ case Asimple:
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xsimple);
+ if(eflag)
+ emitf(Xeflag);
+ break;
+ case Ksubsh:
+ emitf(Xsubshell);
+ if(havefork){
+ p = emiti(0);
+ rcc(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ } else
+ emits(fnstr(c0));
+ if(eflag)
+ emitf(Xeflag);
+ break;
+ case Kswitch:
+ codeswitch(t, eflag);
+ break;
+ case Ktwiddle:
+ emitf(Xmark);
+ rcc(c1, eflag);
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xmatch);
+ if(eflag)
+ emitf(Xeflag);
+ break;
+ case Kwhile:
+ q = code.ip;
+ rcc(c0, 0);
+ if(q==code.ip)
+ emitf(Xsettrue); /* empty condition == while(true) */
+ emitf(Xtrue);
+ p = emiti(0);
+ rcc(c1, eflag);
+ emitf(Xjump);
+ emiti(q);
+ stuffdot(p);
+ break;
+ case Awords:
+ rcc(c1, eflag);
+ rcc(c0, eflag);
+ break;
+ case Kfor:
+ emitf(Xmark);
+ if(c1){
+ rcc(c1, eflag);
+ emitf(Xglob);
+ }
+ else{
+ emitf(Xmark);
+ emitf(Xword);
+ emits(strdup("*"));
+ emitf(Xdol);
+ }
+ emitf(Xmark); /* dummy value for Xlocal */
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xlocal);
+ p = emitf(Xfor);
+ q = emiti(0);
+ rcc(c2, eflag);
+ emitf(Xjump);
+ emiti(p-code.buf);
+ stuffdot(q);
+ emitf(Xunlocal);
+ break;
+ case Aword:
+ emitf(Xword);
+ emits(strdup(t->str));
+ break;
+ case Adup:
+ if(t->rtype==Adupfd){
+ emitf(Xdup);
+ emiti(t->fd0);
+ emiti(t->fd1);
+ }
+ else{
+ emitf(Xclose);
+ emiti(t->fd0);
+ }
+ rcc(c1, eflag);
+ emitf(Xpopredir);
+ break;
+ case Apipefd:
+ emitf(Xpipefd);
+ emiti(t->rtype);
+ if(havefork){
+ p = emiti(0);
+ rcc(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ } else {
+ emits(fnstr(c0));
+ }
+ break;
+ case Aredir:
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xglob);
+ switch(t->rtype){
+ case Rappend:
+ emitf(Xappend);
+ break;
+ case Rwrite:
+ emitf(Xwrite);
+ break;
+ case Rread:
+ case Rhere:
+ emitf(Xread);
+ break;
+ case Rrdwr:
+ emitf(Xrdwr);
+ break;
+ }
+ emiti(t->fd0);
+ rcc(c1, eflag);
+ emitf(Xpopredir);
+ break;
+ case '=':
+ tt = t;
+ for(;t && t->type=='=';t = c2);
+ if(t){
+ for(t = tt;t->type=='=';t = c2){
+ emitf(Xmark);
+ rcc(c1, eflag);
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xlocal);
+ }
+ rcc(t, eflag);
+ for(t = tt; t->type=='='; t = c2)
+ emitf(Xunlocal);
+ }
+ else{
+ for(t = tt;t;t = c2){
+ emitf(Xmark);
+ rcc(c1, eflag);
+ emitf(Xmark);
+ rcc(c0, eflag);
+ emitf(Xassign);
+ }
+ }
+ t = tt; /* so tests below will work */
+ break;
+ case Apipe:
+ emitf(Xpipe);
+ emiti(t->fd0);
+ emiti(t->fd1);
+ if(havefork){
+ p = emiti(0);
+ q = emiti(0);
+ rcc(c0, eflag);
+ emitf(Xexit);
+ stuffdot(p);
+ } else {
+ emits(fnstr(c0));
+ q = emiti(0);
+ }
+ rcc(c1, eflag);
+ emitf(Xreturn);
+ stuffdot(q);
+ emitf(Xpipewait);
+ break;
+ }
+ if(t->type!=Kelse && t->type!=';')
+ runq->iflast = t->type==IF;
+ else if(c0) runq->iflast = c0->type==IF;
+}
+
+Code*
+compile(Tree *t)
+{
+ code.cap = delcode;
+ code.buf = code.ip = emalloc(code.cap*sizeof *code.buf);
+ code.end = code.ip + code.cap;
+
+ emiti(0);
+ rcc(t, 0);
+ emitf(Xreturn);
+ emitf(nil);
+
+ return code.buf;
+}
diff --git a/sys/cmd/rc/code.dep b/sys/cmd/rc/code.dep
new file mode 100644
index 0000000..7fdd4bc
--- /dev/null
+++ b/sys/cmd/rc/code.dep
@@ -0,0 +1,166 @@
+#if 0
+// simple example code
+error
+cd(Args args)
+{
+ switch (args.len) {
+ case 0:
+ errorf("reached cd with no arguments!");
+ return 1;
+ case 1:
+ one:
+ errorf("sh: expected argument to command 'cd'");
+ return 1;
+ case 2:
+ if (args.a[1] == nil)
+ goto one;
+ break;
+ default:
+ errorf("sh: too many arguments to command 'cd'");
+ return 1;
+ }
+ if (chdir(args.a[1]))
+ errorf("cd fail: %s", strerror(errno));
+
+ return 0;
+}
+
+error
+quit(Args args)
+{
+ exit(0);
+}
+
+Builtin builtins[] = {
+ { "cd", cd },
+ { "exit", quit },
+};
+
+void
+clear(Header *arr)
+{
+ arr->len = 0;
+}
+
+int
+readline(Code *code)
+{
+ int n, b;
+
+ n = code->len;
+getchar:
+ if (code->len >= code->cap) {
+ code->cap += 100;
+ code->s = realloc(code->s, code->cap);
+ }
+ /* TODO: unicode? */
+ switch ((b = getchar())) {
+ case EOF:
+ n = -1;
+ goto null;
+ case '\n':
+ n = code->len - n;
+ null:
+ code->s[code->len] = '\0';
+ break;
+ default:
+ code->s[code->len++] = b;
+ goto getchar;
+ }
+
+ return n;
+}
+
+/* TODO: unicode */
+int
+readargs(Code code, Args *args)
+{
+ if (args->a)
+ clear(&args->hdr);
+ else {
+ args->cap += 20;
+ args->a = realloc(args->a, args->cap);
+ }
+
+ args->a[args->len++] = code.s;
+ while (*code.s) {
+ if (!isspace(*code.s++))
+ continue;
+
+ code.s[-1] = '\0';
+ /* consume all remaining space */
+ while (isspace(*code.s))
+ code.s++;
+
+ if (args->len >= args->cap-1) {
+ args->cap += 20;
+ args->a = realloc(args->a, args->cap);
+ }
+ args->a[args->len++] = code.s;
+ }
+ /* nil acts as a sentinel value */
+ args->a[args->len] = nil;
+
+ return args->len;
+}
+
+error
+execute(Args args)
+{
+ int i, status;
+ pid_t cid, wid;
+
+ for (i = 0; i < arrlen(builtins); i++) {
+ if (strcmp(args.a[0], builtins[i].cmd) == 0)
+ return builtins[i].func(args);
+ }
+
+ if ((cid = fork()) == 0) {
+ if (execvp(args.a[0], args.a) == -1)
+ errorf("exec failed: %s", strerror(errno));
+ exit(1);
+ } else if (cid > 0)
+ do
+ wid = waitpid(cid, &status, WUNTRACED);
+ while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ else
+ errorf("fork failed: %s", strerror(errno));
+
+ return status;
+}
+
+static
+void
+flush(void)
+{
+ io·flush(stdout);
+}
+
+static
+void
+prompt(void)
+{
+ printf(";");
+ flush();
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, err, n;
+ Code code = {0};
+ Args args = {0};
+
+ ARGBEGIN {
+ } ARGEND;
+
+ do {
+ clear(&code.hdr);
+ prompt();
+
+ n = readline(&code);
+ readargs(code, &args);
+ err = execute(args);
+ } while (!err && n > 0);
+}
+#endif
diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c
new file mode 100644
index 0000000..2879678
--- /dev/null
+++ b/sys/cmd/rc/exec.c
@@ -0,0 +1,19 @@
+#include "rc.h"
+
+void
+Xerror(char *s)
+{
+
+}
+
+void
+Xmark(void)
+{
+ pushlist();
+}
+
+void
+Xword(void)
+{
+ pushword(shell->ip++->s);
+}
diff --git a/sys/cmd/rc/io.c b/sys/cmd/rc/io.c
new file mode 100644
index 0000000..8cc2f5b
--- /dev/null
+++ b/sys/cmd/rc/io.c
@@ -0,0 +1,435 @@
+#include "rc.h"
+
+#undef bufsize
+#define c0 t->child[0]
+#define c1 t->child[1]
+#define c2 t->child[2]
+
+#define bufsize 512
+#define strsize 100
+
+//------------------------------------------------------------------------
+// buffer operations
+
+/* open file */
+Io*
+openfd(int fd)
+{
+ Io *f;
+
+ f = emalloc(sizeof *f + bufsize);
+ f->fd = fd;
+ f->b = f->e = f->buf;
+ return f;
+}
+
+/* open string */
+Io*
+openstr(void)
+{
+ Io *f;
+
+ f = emalloc(sizeof *f + strsize + 1);
+ f->fd = -1;
+ f->b = f->buf;
+ f->e = f->buf+strsize;
+ memset(f->b, 0, strsize+1);
+
+ return f;
+}
+
+/* open core (not nil terminated) */
+Io*
+opencore(int len, char *s)
+{
+ Io *f;
+
+ f = emalloc(sizeof *f + len);
+ f->fd = -1;
+ f->b = f->buf;
+ f->e = f->buf+len;
+ memcpy(f->b, s, len);
+
+ return f;
+}
+
+void
+rewindio(Io *f)
+{
+ if (f->fd < 0)
+ f->b = f->buf;
+ else {
+ f->b = f->e = f->buf;
+ lseek(f->fd, 0, 0);
+ }
+}
+
+void
+closeio(Io *f)
+{
+ if (f->fd >= 0)
+ close(f->fd);
+
+ efree(f);
+}
+
+/* has the chance to realloc */
+void
+flush(Io **fp)
+{
+ int n;
+ char *s;
+ Io *f;
+
+ f = *fp;
+ if (f->fd < 0) {
+ n = f->e - f->b;
+ f = erealloc(f, sizeof *f + n + strsize + 1);
+ if (!f)
+ panic("can't realloc %d bytes in flush", n+strsize+1);
+ f->b = f->buf+n;
+ f->e = f->buf+n+strsize;
+ memset(f->b, 0, strsize+1);
+ } else {
+ n = f->b - f->buf;
+ if (n && write(f->fd, f->buf, n) < 0) {
+ write(3, "write error\n", 12);
+ if (ntrap)
+ dotrap();
+ }
+ f->b = f->buf;
+ f->e = f->buf + bufsize;
+ }
+
+ *fp = f;
+}
+
+//------------------------------------------------------------------------
+// read from io
+
+int
+rchr(Io *f)
+{
+ int n;
+ if (f->b == f->e) {
+ if (f->fd < 0 || (n = read(f->fd, f->buf, bufsize)) <= 0)
+ return EOF;
+
+ f->b = f->buf;
+ f->e = f->b + n;
+ }
+
+ return *f->b++&0xFF;
+}
+
+//------------------------------------------------------------------------
+// printf functionality
+
+/* character literal */
+int
+pchr(Io *f, int c)
+{
+ if (f->b == f->e)
+ flush(&f);
+
+ return *f->b++=c;
+}
+
+/* quote */
+void
+pquo(Io *f, char *s)
+{
+ pchr(f, '\'');
+ for (; *s; s++)
+ if (*s == '\'')
+ pfmt(f, "''");
+ else
+ pchr(f, *s);
+ pchr(f, '\'');
+}
+
+/* word */
+void
+pwrd(Io *f, char *s)
+{
+ char *t;
+ for (t = s; *t; t++)
+ if (!wordchr(*t))
+ break;
+ if (t == s || *t)
+ pquo(f, s);
+ else
+ pstr(f, s);
+}
+
+/* pointer */
+void
+pptr(Io *f, void *v)
+{
+ int n;
+ uintptr p;
+
+ p = (uintptr)v;
+ if ((sizeof(uintptr) == sizeof(uvlong)) && p >>32)
+ for (n = 60; n >= 32; n-=4)
+ pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+ for (n = 28; n >= 0; n-=4)
+ pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]);
+}
+
+/* string */
+void
+pstr(Io *f, char *s)
+{
+ if (!s)
+ s = "<null>";
+
+ while (*s)
+ pchr(f, *s++);
+}
+
+/* decimal */
+void
+pdec(Io *f, int n)
+{
+ if (n < 0) {
+ n = -n;
+ pchr(f, '-');
+ if (n >= 0) {
+ pdec(f, n);
+ return;
+ }
+ n = 1 - n;
+ pdec(f, n/10);
+ pchr(f, n%10+'1');
+ return;
+ }
+
+ if (n > 9)
+ pdec(f, n/10);
+ pchr(f, n%10+'0');
+}
+
+/* octal */
+void
+poct(Io *f, uint n)
+{
+ if (n > 7)
+ poct(f, n>>3);
+ pchr(f, (n&7)+'0');
+}
+
+/* value */
+void
+pval(Io *f, Word *a)
+{
+ if (a) {
+ while (a->link && a->link->word) {
+ pwrd(f, a->word);
+ pchr(f, ' ');
+ a = a->link;
+ }
+ pwrd(f, a->word);
+ }
+}
+
+/* tree */
+static
+void
+pdeglob(Io *f, char *s)
+{
+ while(*s){
+ if(*s==GLOB)
+ s++;
+ pchr(f, *s++);
+ }
+}
+
+void
+pcmd(Io *f, Tree *t)
+{
+ if(!t)
+ return;
+
+ switch(t->type){
+ default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2);
+ break;
+ case '$': pfmt(f, "$%t", c0);
+ break;
+ case '"': pfmt(f, "$\"%t", c0);
+ break;
+ case '&': pfmt(f, "%t&", c0);
+ break;
+ case '^': pfmt(f, "%t^%t", c0, c1);
+ break;
+ case '`': pfmt(f, "`%t", c0);
+ break;
+ case Aandand: pfmt(f, "%t && %t", c0, c1);
+ break;
+ case Kbang: pfmt(f, "! %t", c0);
+ break;
+ case Abrace: pfmt(f, "{%t}", c0);
+ break;
+ case Acount: pfmt(f, "$#%t", c0);
+ break;
+ case Kfunc: pfmt(f, "fn %t %t", c0, c1);
+ break;
+ case Kif: pfmt(f, "if%t%t", c0, c1);
+ break;
+ case Kelse: pfmt(f, "else %t", c0);
+ break;
+ case Aoror: pfmt(f, "%t || %t", c0, c1);
+ break;
+ case Aparen: pfmt(f, "(%t)", c0);
+ break;
+ case Asub: pfmt(f, "$%t(%t)", c0, c1);
+ break;
+ case Asimple: pfmt(f, "%t", c0);
+ break;
+ case Ksubsh: pfmt(f, "@ %t", c0);
+ break;
+ case Kswitch: pfmt(f, "switch %t %t", c0, c1);
+ break;
+ case Ktwiddle: pfmt(f, "~ %t %t", c0, c1);
+ break;
+ case Kwhile: pfmt(f, "while %t%t", c0, c1);
+ break;
+ case Aargs:
+ if(c0==0)
+ pfmt(f, "%t", c1);
+ else if(c1==0)
+ pfmt(f, "%t", c0);
+ else
+ pfmt(f, "%t %t", c0, c1);
+ break;
+ case ';':
+ if(c0){
+ if(c1)
+ pfmt(f, "%t%c%t", c0, '\n', c1);
+ else pfmt(f, "%t", c0);
+ }
+ else pfmt(f, "%t", c1);
+ break;
+ case Awords:
+ if(c0)
+ pfmt(f, "%t ", c0);
+ pfmt(f, "%t", c1);
+ break;
+ case Kfor:
+ pfmt(f, "for(%t", c0);
+ if(c1)
+ pfmt(f, " in %t", c1);
+ pfmt(f, ")%t", c2);
+ break;
+ case Aword:
+ if(t->quoted)
+ pfmt(f, "%Q", t->str);
+ else
+ pdeglob(f, t->str);
+ break;
+ case Adup:
+ if(t->redir.type==Rdupfd)
+ pfmt(f, ">[%d=%d]", t->redir.fd[1], t->redir.fd[0]); /* yes, fd1, then fd0; read lex.c */
+ else
+ pfmt(f, ">[%d=]", t->redir.fd[0]);
+ pfmt(f, "%t", c1);
+ break;
+ case Apipefd:
+ case Aredir:
+ switch(t->redir.type){
+ case Rhere:
+ pchr(f, '<');
+ case Rread:
+ case Rrdwr:
+ pchr(f, '<');
+ if(t->redir.type==Rrdwr)
+ pchr(f, '>');
+ if(t->redir.fd[0]!=0)
+ pfmt(f, "[%d]", t->redir.fd[0]);
+ break;
+ case Rappend:
+ pchr(f, '>');
+ case Rwrite:
+ pchr(f, '>');
+ if(t->redir.fd[0]!=1)
+ pfmt(f, "[%d]", t->redir.fd[0]);
+ break;
+ }
+ pfmt(f, "%t", c0);
+ if(c1)
+ pfmt(f, " %t", c1);
+ break;
+ case '=':
+ pfmt(f, "%t=%t", c0, c1);
+ if(c2)
+ pfmt(f, " %t", c2);
+ break;
+ case Apipe:
+ pfmt(f, "%t|", c0);
+ if(t->redir.fd[1]==0){
+ if(t->redir.fd[0]!=1)
+ pfmt(f, "[%d]", t->redir.fd[0]);
+ }
+ else pfmt(f, "[%d=%d]", t->redir.fd[0], t->redir.fd[1]);
+ pfmt(f, "%t", c1);
+ break;
+ }
+}
+
+/* rc specific printf */
+static int pfmtlev;
+
+void
+vpfmt(Io *f, char *fmt, va_list args)
+{
+ char err[124];
+
+ pfmtlev++;
+ for (; *fmt; fmt++)
+ if (*fmt!='%')
+ pchr(f, *fmt);
+ else switch(*++fmt) {
+ case '\0':
+ break;
+ case 'c':
+ pchr(f, va_arg(args, int));
+ break;
+ case 'd':
+ pdec(f, va_arg(args, int));
+ break;
+ case 'o':
+ poct(f, va_arg(args, uint));
+ break;
+ case 'p':
+ pptr(f, va_arg(args, void*));
+ break;
+ case 'Q':
+ pquo(f, va_arg(args, char*));
+ break;
+ case 'q':
+ pwrd(f, va_arg(args, char*));
+ break;
+ case 's':
+ pstr(f, va_arg(args, char*));
+ break;
+ case 't':
+ pcmd(f, va_arg(args, Tree*));
+ break;
+ case 'v':
+ pval(f, va_arg(args, Word*));
+ break;
+ }
+ if (--pfmtlev==0)
+ flush(&f);
+}
+
+void
+pfmt(Io *f, char *fmt, ...)
+{
+ va_list args;
+ char err[124];
+
+ va_start(args, fmt);
+ vpfmt(f, fmt, args);
+ va_end(args);
+}
diff --git a/sys/cmd/rc/lex.c b/sys/cmd/rc/lex.c
new file mode 100644
index 0000000..1415f5c
--- /dev/null
+++ b/sys/cmd/rc/lex.c
@@ -0,0 +1,415 @@
+#include "rc.h"
+
+#define onebyte(c) ((c&0x80)==0x00)
+#define twobyte(c) ((c&0xe0)==0xc0)
+#define threebyte(c) ((c&0xf0)==0xe0)
+#define fourbyte(c) ((c&0xf8)==0xf0)
+
+// -----------------------------------------------------------------------
+// globals
+
+static int lastc, nextc=EOF, lastdol, lastword, doprompt = 1;
+static char buf[8*1024];
+
+// -----------------------------------------------------------------------
+// utilities
+
+static uchar nwordc[256] =
+{
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int
+wordchr(char c)
+{
+ return !nwordc[c] && c!=EOF;
+}
+
+
+static uchar nquotec[256] =
+{
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int
+quotechr(char c)
+{
+ return !nquotec[c] && c!=EOF;
+}
+
+static uchar nvarc[256] =
+{
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+int
+varchr(char c)
+{
+ return !nvarc[c] && c!=EOF;
+}
+
+static
+void
+prompt(void)
+{
+ shell->cmd.line++;
+ doprompt = 0;
+}
+
+/* lookahead one byte */
+static
+int
+lookahead(void)
+{
+ int c;
+
+ if(nextc != EOF)
+ return nextc;
+
+ if(shell->cmd.eof)
+ return EOF;
+ if(doprompt)
+ prompt();
+
+ c = rchr(shell->cmd.io);
+ doprompt = c == '\n' || c == EOF;
+
+ if(c == EOF)
+ shell->cmd.eof++;
+
+ return nextc = c;
+}
+
+/* consumes the lookahead */
+static
+int
+advance(void)
+{
+ int c = lookahead();
+ lastc = nextc, nextc = EOF;
+
+ return c;
+}
+
+/*
+ * advance until we no longer hit horizontal space
+ * consumes all comments
+ */
+static
+void
+skipws(void)
+{
+ int c;
+ for(;;) {
+ c = lookahead();
+ if(c=='#'){
+ for(;;){
+ c = lookahead();
+ if(c=='\n' || c==EOF)
+ break;
+ advance();
+ }
+ }
+ if(c==' ' || c=='\t')
+ advance();
+ else
+ return;
+ }
+}
+
+/* advance until we no longer hit any space */
+void
+skipnl(void)
+{
+ int c;
+ for(;;) {
+ skipws();
+ if ((c = lookahead()) != '\n')
+ return;
+ advance();
+ }
+}
+
+/* advance if next char is equal to c */
+static
+int
+nextis(int c)
+{
+ if(lookahead()==c) {
+ advance();
+ return 1;
+ }
+ return 0;
+}
+
+/* functions to append to our write buffer */
+static
+char*
+putbyte(char *s, int c)
+{
+ if(!s)
+ return s;
+ if(s == arrend(buf)){
+ *s = 0;
+ rcerror("out of buffer space");
+ return nil;
+ }
+ *s++ = c;
+ return s;
+}
+
+static
+char*
+putrune(char *s, int c)
+{
+ s = putbyte(s, c);
+ if (onebyte(c))
+ return s;
+ if (twobyte(c))
+ return putbyte(s, advance());
+ if (threebyte(c)) {
+ putbyte(s, advance());
+ return putbyte(s, advance());
+ }
+ if (fourbyte(c)) {
+ putbyte(s, advance());
+ putbyte(s, advance());
+ return putbyte(s, advance());
+ }
+ rcerror("malformed utf8 stream");
+ return nil;
+}
+
+// -----------------------------------------------------------------------
+// main exports
+
+void
+rcerror(char *fmt, ...)
+{
+ va_list args;
+
+ pfmt(errio, "rc:");
+ if (shell->cmd.io)
+ pfmt(errio, "%s:%d ", shell->cmd.name, shell->cmd.line);
+
+ va_start(args, fmt);
+ vpfmt(errio, fmt, args);
+ va_end(args);
+
+ pfmt(errio, "\n");
+
+ flush(&errio);
+ lastword = lastdol = 0;
+ while (lastc != '\n' && lastc != EOF)
+ advance();
+}
+
+/* word is only modified in the event of a lexed word */
+int
+lex(Tree **node)
+{
+ int c;
+ char *w = buf;
+ /*
+ * NOTE:
+ * we inject tokens into the lexer based on context if last token = word:
+ * if we see a (, then we interpret that as a subscript
+ * otherwise, if the next character is the first char of a word, we return a ^ operator.
+ */
+ if(lastword){
+ lastword=0;
+ c = lookahead();
+ if(c=='('){
+ advance();
+ return Alparen;
+ }
+ if(quotechr(c))
+ return Acarot;
+ }
+
+ skipws();
+ switch(c=advance()) {
+ case EOF:
+ lastdol = 0;
+ return EOF;
+ case '$':
+ lastdol = 1;
+ if(nextis('#'))
+ return Acount;
+ if (nextis('"'))
+ return Aquote;
+ return Adol;
+ case '&':
+ lastdol = 0;
+ if(nextis('&'))
+ return Aandand;
+ return Aand;
+
+ case '!':
+ return Kbang;
+ case '@':
+ return Ksubsh;
+ case '~':
+ return Ktwiddle;
+
+ case '|':
+ lastdol = 0;
+ if(nextis('|')){
+ skipnl();
+ return Aoror;
+ }
+ (*node) = newtree();
+ (*node)->type = Apipe;
+ (*node)->redir.fd[0] = 0;
+ (*node)->redir.fd[1] = 1;
+ goto redir;
+ case '>':
+ (*node) = newtree();
+ (*node)->type = Aredir;
+ if (nextis(c))
+ (*node)->redir.type = Rappend;
+ else
+ (*node)->redir.type = Rwrite;
+ (*node)->redir.fd[0] = 1;
+ goto redir;
+ case '<':
+ (*node) = newtree();
+ (*node)->type = Aredir;
+ if(nextis(c))
+ (*node)->redir.type = Rhere;
+ else if(nextis('>'))
+ (*node)->redir.type = Rrdwr;
+ else
+ (*node)->redir.type = Rread;
+ (*node)->redir.fd[0] = 0;
+ /* fallthrough */
+ redir:
+ if(nextis('[')) {
+ c = advance();
+ if(c < '0' || '9' < c) {
+ redirerr:
+ rcerror("incorrect redirection syntax");
+ return EOF;
+ }
+ (*node)->redir.fd[0] = 0;
+ do {
+ (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0');
+ c = advance();
+ } while('0'<=c && c<='9');
+
+ if(c == '=') {
+ if((*node)->type == Aredir)
+ (*node)->type = Adup;
+ c = advance();
+ if('0'<=c && c<='9') {
+ (*node)->redir.type = Rdupfd;
+ (*node)->redir.fd[1] = (*node)->redir.fd[0];
+ (*node)->redir.fd[0] = 0;
+ do {
+ (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0');
+ c = advance();
+ } while('0'<=c && c<='9');
+ } else {
+ if((*node)->type == Apipe)
+ goto redirerr;
+ (*node)->redir.type = Rclose;
+ }
+ }
+ if (c != ']'
+ ||(*node)->type==Adup && ((*node)->redir.type==Rhere || (*node)->redir.type==Rappend))
+ goto redirerr;
+ }
+ if ((c = ((*node)->type)) == Apipe)
+ skipnl();
+ return c;
+
+ case '\'':
+ lastdol = 0;
+ lastword = 1;
+ for(;;){
+ c = advance();
+ if(c==EOF)
+ break;
+ if(c=='\''){
+ if(lookahead()!='\'')
+ break;
+ advance();
+ }
+ w = putrune(w, c);
+ }
+ *w = 0;
+ *node = wordnode(buf);
+ (*node)->quoted = 1;
+ return Aword;
+ }
+ if (!wordchr(c)) {
+ lastdol = 0;
+ return c;
+ }
+ for(;;){
+ if(c=='*'||c=='['||c=='?'||c==GLOB)
+ w = putbyte(w, GLOB);
+ w = putrune(w, c);
+ c = lookahead();
+ if(lastdol?!varchr(c):!wordchr(c))
+ break;
+ advance();
+ }
+ *w = 0;
+
+ if ((c = kwlookup(buf)) == -1) {
+ (*node) = wordnode(buf);
+ (*node)->type = c = Aword;
+ (*node)->quoted = 0;
+ lastword = 1;
+ }
+
+ lastdol = 0;
+ return c;
+}
diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c
new file mode 100644
index 0000000..baaf6bc
--- /dev/null
+++ b/sys/cmd/rc/main.c
@@ -0,0 +1,86 @@
+#include "rc.h"
+
+/* globals */
+Thread *shell = nil;
+int ntrap = 0;
+Io *errio;
+
+/* main execution */
+
+void
+dotrap(void)
+{
+ exit(1);
+}
+
+void
+bootup(Code *c, int off, Var *vars)
+{
+ Thread *sh;
+
+ alloc(sh);
+ sh->code = c, c->i++;
+ sh->ip = sh->code + off;
+ sh->local = vars;
+ sh->stack = nil;
+
+ sh->link = shell, shell = sh;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ Code *ip, sh[32];
+
+ ARGBEGIN {
+ } ARGEND;
+
+ errio = openfd(2);
+
+ initkw();
+
+ ip = sh;
+ memset(sh, 0, sizeof(sh));
+ /*
+ * NOTE: first element of code is a reference count
+ * bootup runs:
+ * 1. *=argv[1:]
+ * 2. . rcmain $*
+ */
+#if 0
+ ip++->i = 1;
+ ip++->f = Xmark;
+ ip++->f = Xword;
+ ip++->s = "*";
+ ip++->f = Xassign;
+ ip++->f = Xmark;
+ ip++->f = Xmark;
+ ip++->s = "*";
+ ip++->f = Xdol;
+ ip++->s = "rcmain";
+ ip++->f = Xword;
+ ip++->s = ".";
+ ip++->f = Xsimple;
+ ip++->f = Xexit;
+ ip++->i = 0;
+
+ bootup(sh, 1, nil);
+ pushlist();
+ for (i = argc-1; i != 0; i--)
+ pushword(argv[i]);
+
+ for (;;) {
+ shell->ip++->f();
+ if (ntrap)
+ dotrap();
+ }
+#else
+
+ bootup(sh, 1, nil);
+ shell->cmd.io = openfd(0);
+ parse();
+
+#endif
+ exit(0);
+}
diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c
new file mode 100644
index 0000000..d963c12
--- /dev/null
+++ b/sys/cmd/rc/parse.c
@@ -0,0 +1,430 @@
+#include "rc.h"
+
+// -----------------------------------------------------------------------
+// global data
+
+static int lasta, nexta=EOF;
+static Tree *node; /* if token was lexed as a tree node (redirs and words), its here */
+
+static uchar prectab[256] = {
+ [Kif] = 1, [Kfor] = 1, [Kswitch] = 1, [Kelse] = 1,
+ [Aandand] = 2, [Aoror] = 2,
+ [Kbang] = 3, [Ksubsh] = 3,
+ [Apipe] = 4,
+ [Acarot] = 5,
+ [Adol] = 6, [Acount] = 6, [Aquote] = 6,
+ [Asub] = 7,
+};
+
+// -----------------------------------------------------------------------
+// helpers
+
+static
+int
+lookahead(void)
+{
+ int tok;
+
+ if (nexta != EOF)
+ return nexta;
+
+ tok = lex(&node);
+ return nexta = tok;
+}
+
+static
+int
+advance(void)
+{
+ int tok = lookahead();
+ lasta = nexta, nexta = EOF;
+ node = nil;
+
+ return tok;
+}
+
+static
+int
+nextis(int tok)
+{
+ if (lookahead() == tok) {
+ advance();
+ return 1;
+ }
+ return 0;
+}
+
+// -----------------------------------------------------------------------
+// subparsers
+
+static Tree *word(void);
+static Tree *comword(void);
+static Tree *cmd(int prec);
+
+static
+Tree*
+body(void)
+{
+ int tok;
+ Tree *l, *r;
+ l = cmd(1);
+loop:
+ switch((tok=lookahead())){
+ case '&':
+ l = tree1('&', l);
+ /* fallthrough */
+ case ';': case '\n':
+ advance();
+ r = cmd(1);
+ l = tree2(';', l, r);
+ goto loop;
+ default:
+ ;
+ }
+
+ return l;
+}
+
+static
+Tree*
+brace(void)
+{
+ Tree *t;
+
+ if (!nextis('{'))
+ rcerror("not a brace");
+ t = tree1(Abrace, body());
+ if (!nextis('}'))
+ rcerror("unmatched brace");
+
+ return t;
+}
+
+static
+Tree*
+paren(void)
+{
+ Tree *t;
+
+ if (!nextis('('))
+ rcerror("not a paren");
+ t = tree1(Aparen, body());
+ if (!nextis(')'))
+ rcerror("unmatched paren");
+
+ return t;
+}
+
+/* TODO: fill in */
+static
+Tree*
+heredoc(Tree* t)
+{
+ return t;
+}
+
+static
+Tree*
+redir(void)
+{
+ int tok;
+ Tree *t;
+
+ switch (tok = lookahead()) {
+ case Adup:
+ t = node;
+ advance();
+ break;
+ case Aredir:
+ advance();
+ t = hang1(node, (node->redir.type == Rhere) ? heredoc(word()) : word());
+ break;
+ default:
+ t = nil;
+ }
+
+ return t;
+}
+
+static
+Tree*
+epilog(void)
+{
+ Tree *t, *tt;
+
+ t = redir();
+ while((tt = redir()))
+ t = hang2(t, t->child[0], tt);
+
+ return t;
+}
+
+static
+Tree*
+sword(void)
+{
+ int tok;
+ if (Kstart < (tok=lookahead()) && tok < Kend)
+ return node;
+
+ return comword();
+}
+
+static
+Tree*
+word(void)
+{
+ int tok;
+ Tree *t;
+
+ t = sword();
+ while(nextis('^'))
+ t = tree2('^', t, sword());
+
+ return t;
+}
+
+
+static
+Tree*
+words(void)
+{
+ Tree *t, *tt;
+ t = word();
+ while((tt=word()))
+ t = tree2(Awords, t, tt);
+
+ return t;
+}
+
+static
+Tree*
+comword(void)
+{
+ int tok;
+ Tree *t, *tt;
+
+ switch(tok=lookahead()){
+ case Adol:
+ advance();
+ t = word();
+ if(nextis('(')) {
+ t = tree2(Asub, t, words());
+ if (!nextis(')'))
+ rcerror("malformed index expression");
+ }
+ return tree1(Adol, t);
+ case Acount:
+ advance();
+ return tree1(Acount, word());
+ case Atick:
+ advance();
+ return tree1(Atick, brace());
+ case Alparen:
+ return paren();
+ case Aredir:
+ advance();
+ t = hang1(node, brace());
+ t->type = Apipefd;
+ return t;
+ case Aword:
+ t = node;
+ advance();
+ return t;
+ }
+ return nil;
+}
+
+static
+Tree*
+first(void)
+{
+ int tok;
+ Tree *t;
+
+ t = comword();
+ while(nextis('^')) {
+ t = tree2('^', t, word());
+ }
+
+ return t;
+}
+
+/* simple _or_ assignment */
+static
+Tree*
+simple_or_assign(void)
+{
+ int tok;
+ Tree *t, *tt;
+
+ /* can't continue */
+ if (!(t = first()))
+ return nil;
+
+ /* is an assignment */
+assign:
+ if(nextis('='))
+ return tree3(Aeq, t, word(), cmd(prectab[Kbang]));
+
+ /* is a 'simple' */
+simple:
+ switch ((tok=lookahead())) {
+ case Aredir:
+ case Adup:
+ t = tree2(Aargs, t, redir());
+ goto simple;
+ default:
+ if ((tt = word())) {
+ t = tree2(Aargs, t, tt);
+ goto simple;
+ }
+ /* fallthrough */
+ }
+
+ return simplehang(t);
+}
+
+static
+Tree*
+opand(void)
+{
+ int tok;
+ Tree *t, *tt;
+
+ switch(tok=lookahead()) {
+ case Kif:
+ advance();
+ t = paren();
+ skipnl();
+ tt = cmd(prectab[Kif]);
+ t = tree2(Kif, t, tt);
+ return t;
+
+ case Kelse:
+ advance();
+ skipnl();
+ t = tree1(Kelse, cmd(prectab[Kelse]));
+ return t;
+
+ case Kfor:
+ advance();
+ if (!nextis('('))
+ rcerror("malformed for statement");
+ t = word();
+ if (nextis(Kin)) {
+ advance();
+ tt = words();
+ t = tree3(Kin, t, tt, nil);
+ } else
+ t = tree3(Kin, t, nil, nil);
+ skipnl();
+ tt = cmd(prectab[Kfor]);
+ t->child[2] = tt;
+ return t;
+
+ case Kswitch:
+ advance();
+ t = word();
+ skipnl();
+ tt = brace();
+ t = tree2(Kswitch, t, tt);
+ return t;
+
+ case Kfunc:
+ advance();
+ t = words();
+ if ((tok=lookahead()) == '{') {
+ tt = brace();
+ t = tree2(Kfunc, t, tt);
+ } else
+ t = tree1(Kfunc, t);
+ return t;
+
+ case Ksubsh:
+ advance();
+ t = tree1(Ksubsh, cmd(prectab[Ksubsh]));
+ return t;
+
+ case Kbang:
+ advance();
+ t = tree1(Kbang, cmd(prectab[Kbang]));
+ return t;
+
+ case Ktwiddle:
+ advance();
+ tt = word();
+ t = tree2(Ktwiddle, tt, words());
+ return t;
+
+ case Albrace:
+ t = brace();
+ tt = epilog();
+ return epihang(t, tt);
+
+ case Aredir: /* fallthrough */
+ case Adup:
+ t = redir();
+ tt = cmd(prectab[Kbang]);
+ t = hang2(t, t->child[0], tt);
+ return t;
+ }
+
+ return simple_or_assign();
+}
+
+static
+Tree *
+cmd(int prec)
+{
+ int np, tok;
+ Tree *l, *r, *p;
+
+ if (!(l = opand()))
+ return nil;
+
+ for(;;) {
+ tok = lookahead();
+ np = prectab[tok];
+ if (np < prec)
+ break;
+ p = node;
+ advance();
+ r = cmd(np+1);
+ if (tok == Apipe)
+ l = hang2(p, l, r);
+ else
+ l = tree2(tok, l, r);
+ }
+
+ return l;
+}
+
+// -----------------------------------------------------------------------
+// main function
+
+int
+parse(void)
+{
+ int tok;
+ Tree *t, *tt;
+
+ t = cmd(1);
+loop:
+ switch(tok=lookahead()) {
+ case '&':
+ t = tree1('&', t);
+ /* fallthrough */
+ case ';':
+ advance();
+ tt = cmd(1);
+ t = tree2(';', t, tt);
+ goto loop;
+ case '\n': case EOF:
+ pfmt(errio, "%t", t);
+ break;
+ default:
+ rcerror("unrecognized token: %d", tok);
+ }
+ return 0;
+}
diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h
new file mode 100644
index 0000000..ad46a77
--- /dev/null
+++ b/sys/cmd/rc/rc.h
@@ -0,0 +1,254 @@
+#pragma once
+
+#include <u.h>
+#include <libn.h>
+
+#include <errno.h>
+#include <sys/wait.h>
+
+#define alloc(ptr) ptr = emalloc(sizeof(*ptr))
+
+// -----------------------------------------------------------------------
+// main enums
+
+#define GLOB 0x01
+
+/* TODO: make sure there are no collisions */
+enum
+{
+ /* keywords */
+ Kstart=11, Kfor, Kin, Kwhile, Kif, Kelse,
+ Kswitch, Kcase, Kfunc, Ktwiddle,
+ Kbang, Ksubsh, Kend,
+
+ /* tokens */
+ Aword='w', Aredir='r', Adup='d', Asimple='s',
+ Aargs='A', Awords='W', Abrace='b', Aparen='p', Asub='S',
+ Apcmd='c', Apipefd='-', Aandand='%', Aoror='@', Acount='#',
+
+ Atick='`', Apipe = '|', Adol='$', Aquote='"', Aand='&',
+ Alparen = '(', Arparen = ')', Albrace='{', Arbrace='}',
+ Asemi=';', Acarot='^', Aeq='=',
+};
+
+enum
+{
+ Rappend = 1,
+ Rwrite = 2,
+ Rread = 3,
+ Rhere = 4,
+ Rdupfd = 5,
+ Rclose = 6,
+ Rrdwr = 7,
+};
+
+// -----------------------------------------------------------------------
+// main types
+
+typedef union Code Code;
+typedef struct Word Word;
+typedef struct List List;
+typedef struct Var Var;
+typedef struct Tree Tree;
+typedef struct Builtin Builtin;
+typedef struct Thread Thread;
+typedef struct Io Io;
+
+union Code
+{
+ int i;
+ char *s;
+ void (*f)(void);
+};
+
+struct Word
+{
+ char *word;
+ Word *link;
+};
+
+struct List
+{
+ Word *words;
+ List *link;
+};
+
+struct Var
+{
+ string name;
+ Word *val;
+ Code *func;
+ Var *link;
+};
+
+struct Tree
+{
+ ushort type;
+ uchar quoted : 1;
+ union {
+ char *str;
+ struct {
+ ushort type;
+ int fd[2];
+ } redir;
+ };
+
+ Tree *child[3], *link;
+};
+
+struct Builtin
+{
+ char *cmd;
+ void (*func)(void);
+};
+
+struct Thread
+{
+ Code *code, *ip;
+ List *stack;
+ Var *local;
+ struct {
+ uchar eof : 1;
+ int line;
+ char *name;
+ Io *io;
+ } cmd;
+
+ int pid;
+ Tree *nodes;
+ Thread *link; /* continuation */
+};
+
+struct Io
+{
+ int fd;
+ uchar *b, *e, buf[];
+};
+
+// -----------------------------------------------------------------------
+// global interpreter variables
+
+extern Thread *shell;
+extern int ntrap;
+extern int status;
+
+extern Io *errio;
+
+extern Builtin builtins[];
+extern Var *globals[1021]; /* for now must be prime */
+
+// -----------------------------------------------------------------------
+// interpreter functions (defined in exec.c)
+
+/*
+ * notation:
+ * (var1, var2, ...) : items from stack
+ * [var1, var2, ...] : items from code stream
+ * -> moves value
+ */
+extern void Xmark(void); /* Xmark: delimit stack with new list */
+extern void Xword(void); /* Xword[val] -> (val) */
+extern void Xassign(void); /* Xassign(name, val): assign name to val */
+extern void Xdol(void); /* Xdol(name): get variable value */
+extern void Xsimple(void); /* Xsimple(args): run command */
+extern void Xexit(void); /* Xexit: exit with status */
+extern void Xerror(char *s); /* Xerror: report an error */
+extern void Xparse(void);
+
+// -----------------------------------------------------------------------
+// shell functions
+
+/*
+ * util.c
+ */
+void *emalloc(uintptr size);
+void *erealloc(void *ptr, uintptr size);
+void efree(void *);
+void panic(char *msg, int n);
+
+/*
+ * io.c
+ */
+Io *openfd(int fd);
+Io *openstr(void);
+Io *opencore(int len, char *s);
+void rewindio(Io *f);
+void closeio(Io *f);
+void flush(Io **fp);
+
+/* reads */
+int rchr(Io *f);
+
+/* writes */
+int pchr(Io *f, int c);
+void pquo(Io *f, char *s);
+void pwrd(Io *f, char *s);
+void pptr(Io *f, void *v);
+void pstr(Io *f, char *s);
+void pdec(Io *f, int n);
+void poct(Io *f, uint n);
+void pval(Io *f, Word *a);
+void pcmd(Io *f, Tree *t);
+void pfmt(Io *f, char *fmt, ...);
+void vpfmt(Io *f, char *fmt, va_list args);
+
+/*
+ * word.c
+ */
+void pushlist(void);
+void freelist(Word *w);
+void poplist(void);
+
+int count(Word *w);
+Word *newword(char *w, Word *link);
+void pushword(char *w);
+
+/*
+ * tree.c
+ */
+
+Tree *newtree(void);
+void freetree(Tree *t);
+Tree *tree3(int type, Tree *c0, Tree *c1, Tree *c2);
+Tree *tree2(int type, Tree *c0, Tree *c1);
+Tree *tree1(int type, Tree *c0);
+
+Tree *hang1(Tree *p, Tree *c0);
+Tree *hang2(Tree *p, Tree *c0, Tree *c1);
+Tree *hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2);
+Tree *epihang(Tree *c, Tree *epi);
+Tree *simplehang(Tree *t);
+Tree *wordnode(char *w);
+
+/*
+ * var.c
+ */
+
+Var *newvar(char *name, Var *link);
+Var *gvlookup(char *name);
+Var *vlookup(char *name);
+
+int kwlookup(char *name);
+void initkw(void);
+
+/*
+ * lex.c
+ */
+
+void skipnl(void);
+int wordchr(char c);
+
+void rcerror(char *msg, ...);
+int lex(Tree **node);
+
+/*
+ * parse.c
+ */
+
+int parse(void);
+
+/*
+ * main.c
+ */
+
+void dotrap(void);
diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk
new file mode 100644
index 0000000..33d6ba8
--- /dev/null
+++ b/sys/cmd/rc/rules.mk
@@ -0,0 +1,21 @@
+include share/push.mk
+
+# Local sources
+SRCS_$(d) := \
+ $(d)/io.c \
+ $(d)/util.c \
+ $(d)/var.c \
+ $(d)/word.c \
+ $(d)/tree.c \
+ $(d)/lex.c \
+ $(d)/parse.c \
+ $(d)/main.c
+BINS_$(d) := $(d)/rc
+
+include share/paths.mk
+
+# Local rules
+$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a
+ $(COMPLINK)
+
+include share/pop.mk
diff --git a/sys/cmd/rc/simple.c b/sys/cmd/rc/simple.c
new file mode 100644
index 0000000..f934aa1
--- /dev/null
+++ b/sys/cmd/rc/simple.c
@@ -0,0 +1,13 @@
+void
+Xsimple(void)
+{
+ Word *a;
+ Var *v;
+
+ a = shell->stack->words;
+ if (!a) {
+ Xerror("empty argument list");
+ return;
+ }
+ v = vlookup(a->word);
+}
diff --git a/sys/cmd/rc/tree.c b/sys/cmd/rc/tree.c
new file mode 100644
index 0000000..8dca67f
--- /dev/null
+++ b/sys/cmd/rc/tree.c
@@ -0,0 +1,144 @@
+#include "rc.h"
+
+// -----------------------------------------------------------------------
+// globals
+
+static Tree *nodes;
+
+// -----------------------------------------------------------------------
+// exported funcs
+
+Tree*
+newtree(void)
+{
+ Tree *t;
+
+ alloc(t);
+ t->str = nil;
+ t->child[0] = t->child[1] = t->child[2] = nil;
+ t->redir.fd[0] = t->redir.fd[1] = t->redir.type = 0;
+
+ t->link = nodes, nodes = t;
+ return t;
+}
+
+void
+freetree(Tree *t)
+{
+ if (!t)
+ return;
+
+ freetree(t->child[0]);
+ freetree(t->child[1]);
+ freetree(t->child[2]);
+
+ if (t->str)
+ efree(t->str);
+ efree(t);
+}
+
+void
+freenodes(void)
+{
+ Tree *t, *u;
+
+ for (t = nodes;t;t = u) {
+ u = t->link;
+ if (t->str)
+ efree(t->str);
+ efree(t);
+ }
+ nodes = nil;
+}
+
+/* tree creation */
+Tree*
+tree3(int type, Tree *c0, Tree *c1, Tree *c2)
+{
+ Tree *t;
+
+ t = newtree();
+ t->type = type;
+ t->child[0] = c0;
+ t->child[1] = c1;
+ t->child[2] = c2;
+
+ return t;
+}
+
+Tree*
+tree2(int type, Tree *c0, Tree *c1)
+{
+ return tree3(type, c0, c1, nil);
+}
+
+Tree*
+tree1(int type, Tree *c0)
+{
+ return tree3(type, c0, nil, nil);
+}
+
+/* tree hang */
+Tree*
+hang1(Tree *p, Tree *c0)
+{
+ p->child[0] = c0;
+ return p;
+}
+
+Tree*
+hang2(Tree *p, Tree *c0, Tree *c1)
+{
+ p->child[0] = c0;
+ p->child[1] = c1;
+ return p;
+}
+
+Tree*
+hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2)
+{
+ p->child[0] = c0;
+ p->child[1] = c1;
+ p->child[2] = c2;
+ return p;
+}
+
+/* hangs the cmd underneath the epilogue */
+Tree*
+epihang(Tree *c, Tree *epi)
+{
+ Tree *p;
+ if(!epi)
+ return c;
+ for(p=epi;p->child[1];p = p->child[1])
+ ;
+ p->child[1] = c;
+ return epi;
+}
+
+/* hangs tree t from a new simple node. percolates redirections to root */
+Tree*
+simplehang(Tree *t)
+{
+ Tree *u;
+ t = tree1(Asimple, t);
+ for(u = t->child[0];u->type==Aargs;u=u->child[0]) {
+ if (u->child[1]->type==Adup
+ || u->child[1]->type==Aredir){
+ u->child[1]->child[1] = t;
+ t = u->child[1];
+ u->child[1] = nil;
+ }
+ }
+ return t;
+}
+
+Tree*
+wordnode(char *w)
+{
+ Tree *t = newtree();
+ t->type = Aword;
+ t->str = strdup(w);
+
+ return t;
+}
diff --git a/sys/cmd/rc/util.c b/sys/cmd/rc/util.c
new file mode 100644
index 0000000..02b3611
--- /dev/null
+++ b/sys/cmd/rc/util.c
@@ -0,0 +1,40 @@
+#include "rc.h"
+
+void *
+emalloc(uintptr n)
+{
+ void *p = malloc(n);
+ if (!p)
+ panic("can't malloc %d bytes", n);
+
+ return p;
+}
+
+void *
+erealloc(void *p, uintptr n)
+{
+ void *new = realloc(p, n);
+ if (!new)
+ panic("can't realloc %d bytes", n);
+
+ return new;
+}
+
+void
+efree(void *p)
+{
+ if (p)
+ free(p);
+ else
+ pfmt(errio, "free <nil>\n");
+}
+
+void
+panic(char *s, int n)
+{
+ pfmt(errio, "rc: ");
+ pfmt(errio, s, n);
+ pchr(errio, '\n');
+ flush(&errio);
+ abort();
+}
diff --git a/sys/cmd/rc/var.c b/sys/cmd/rc/var.c
new file mode 100644
index 0000000..dbe7c14
--- /dev/null
+++ b/sys/cmd/rc/var.c
@@ -0,0 +1,108 @@
+#include "rc.h"
+
+Var *globals[1021] = { 0 };
+
+struct Keyword {
+ ushort type;
+ char *name;
+ struct Keyword *link;
+} *keywords[41];
+
+// -----------------------------------------------------------------------
+// utility
+
+static
+int
+hash(char *s, int n)
+{
+ int i = 1, h = 0;
+ while (*s)
+ h += *s++*i++;
+ h %= n;
+ return (h<0)?h+n:h;
+}
+
+// -----------------------------------------------------------------------
+// keywords
+
+static
+void
+putkw(int type, char *name)
+{
+ struct Keyword *kw;
+ int h = hash(name, arrlen(keywords));
+
+ alloc(kw);
+ kw->type = type;
+ kw->name = name;
+ kw->link = keywords[h];
+
+ keywords[h] = kw;
+}
+
+void
+initkw(void)
+{
+ putkw(Kfor, "for");
+ putkw(Kin, "in");
+ putkw(Kwhile, "while");
+ putkw(Kif, "if");
+ putkw(Kelse, "else");
+ putkw(Kswitch, "switch");
+ putkw(Kcase, "case");
+ putkw(Kfunc, "func");
+}
+
+int
+kwlookup(char *name)
+{
+ int t;
+ struct Keyword *it;
+ for(t=-1,it = keywords[hash(name, arrlen(keywords))];it;it = it->link)
+ if(!strcmp(it->name, name))
+ t = it->type;
+ return t;
+}
+
+// -----------------------------------------------------------------------
+// variables
+
+Var *
+newvar(char *name, Var *link)
+{
+ Var *v;
+
+ alloc(v);
+ v->name = name;
+ v->val = 0;
+ v->func = nil;
+ v->link = link;
+
+ return v;
+}
+
+/* only global lookup */
+
+Var *
+gvlookup(char *name)
+{
+ Var *v;
+ int h = hash(name, arrlen(globals));
+ for (v = globals[h]; v; v = v->link)
+ if (!strcmp(v->name, name))
+ return v;
+
+ return globals[h] = newvar(strdup(name), globals[h]);
+}
+
+/* local + global lookup */
+Var *
+vlookup(char *name)
+{
+ Var *v;
+ if (shell)
+ for (v = shell->local; v; v = v->link)
+ if (!strcmp(v->name, name))
+ return v;
+ return gvlookup(name);
+}
diff --git a/sys/cmd/rc/word.c b/sys/cmd/rc/word.c
new file mode 100644
index 0000000..84ff40c
--- /dev/null
+++ b/sys/cmd/rc/word.c
@@ -0,0 +1,64 @@
+#include "rc.h"
+
+void
+pushlist(void)
+{
+ List *ls;
+
+ alloc(ls);
+ ls->words = nil;
+ ls->link = shell->stack, shell->stack = ls;
+}
+
+void
+freelist(Word *w)
+{
+ Word *it;
+ while (w) {
+ it = w->link;
+ efree(w->word);
+ efree(w);
+ w = it;
+ }
+}
+
+void
+poplist(void)
+{
+ List *ls = shell->stack;
+ if (!ls)
+ panicf("shell stack underflow");
+
+ freelist(ls->words);
+ shell->stack = ls->link;
+ efree(ls);
+}
+
+int
+count(Word *w)
+{
+ int n;
+ for (n=0; w; n++)
+ w = w->link;
+ return n;
+}
+
+Word*
+newword(char *w, Word *link)
+{
+ Word *wd;
+
+ alloc(wd);
+ wd->word = strdup(w);
+ wd->link = link;
+
+ return wd;
+}
+
+void
+pushword(char *w)
+{
+ if (shell->stack == nil)
+ panicf("no active stack");
+ shell->stack->words = newword(w, shell->stack->words);
+}
diff --git a/sys/cmd/rules.mk b/sys/cmd/rules.mk
index 2364189..8e83e57 100644
--- a/sys/cmd/rules.mk
+++ b/sys/cmd/rules.mk
@@ -8,22 +8,43 @@ include $(DIR)/rules.mk
# DIR := $(d)/cc
# include $(DIR)/rules.mk
+# DIR := $(d)/cp
+# include $(DIR)/rules.mk
+
+# DIR := $(d)/dvtm
+# include $(DIR)/rules.mk
+
+DIR := $(d)/dwm
+include $(DIR)/rules.mk
+
+DIR := $(d)/echo
+include $(DIR)/rules.mk
+
# DIR := $(d)/edo
# include $(DIR)/rules.mk
-# DIR := $(d)/rc
+DIR := $(d)/filter
+include $(DIR)/rules.mk
+
+DIR := $(d)/ic
+include $(DIR)/rules.mk
+
+# DIR := $(d)/ls
# include $(DIR)/rules.mk
DIR := $(d)/menu
include $(DIR)/rules.mk
-DIR := $(d)/dwm
+DIR := $(d)/mv
+include $(DIR)/rules.mk
+
+DIR := $(d)/rc
include $(DIR)/rules.mk
DIR := $(d)/term
include $(DIR)/rules.mk
-DIR := $(d)/dvtm
+DIR := $(d)/walk
include $(DIR)/rules.mk
include share/pop.mk
diff --git a/sys/cmd/term/term.c b/sys/cmd/term/term.c
index 6a27a10..f7687b9 100644
--- a/sys/cmd/term/term.c
+++ b/sys/cmd/term/term.c
@@ -1111,13 +1111,12 @@ tdefcolor(int *attr, int *npar, int l)
break;
case 5: /* indexed color */
if (*npar + 2 >= l) {
- fprintf(stderr,
- "erresc(38): Incorrect number of parameters (%d)\n", *npar);
+ fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar);
break;
}
*npar += 2;
if (!BETWEEN(attr[*npar], 0, 255))
- fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);
+ fprintf(stderr, "erresc: bad color %d\n", attr[*npar]);
else
idx = attr[*npar];
break;
diff --git a/sys/cmd/term/term.info b/sys/cmd/term/term.info
index 2f40292..7b90344 100644
--- a/sys/cmd/term/term.info
+++ b/sys/cmd/term/term.info
@@ -1,4 +1,4 @@
-st-mono| simpleterm monocolor,
+term+mono| simpleterm monocolor,
acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
am,
bce,
@@ -193,8 +193,8 @@ st-mono| simpleterm monocolor,
Ss=\E[%p1%d q,
term| simpleterm,
- use=st-mono,
- colors#8,
+ use=term+mono,
+ colors#8, pairs#64,
setab=\E[4%p1%dm,
setaf=\E[3%p1%dm,
setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m,
@@ -202,38 +202,49 @@ term| simpleterm,
sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m,
term-256color| simpleterm with 256 colors,
- use=st,
+ use=term,
ccc,
- colors#256,
+ colors#256, pairs#32767,
oc=\E]104\007,
- pairs#32767,
# Nicked from xterm-256color
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
+term-direct| simpleterm with true color,
+ use=term,
+ RGB,
+# Nicked from xterm-direct
+ colors#0x1000000, pairs#0x7FFFF,
+ initc@, op=\E[39;49m,
+ setab=\E[%?%p1%{8}%<%t4%p1%d%e48;2;%p1%{65536}%/%d;%p1%{256}
+ %/%{255}%&%d;%p1%{255}%&%d%;m,
+ setaf=\E[%?%p1%{8}%<%t3%p1%d%e38;2;%p1%{65536}%/%d;%p1%{256}
+ %/%{255}%&%d;%p1%{255}%&%d%;m,
+ setb@, setf@,
+
term-meta| simpleterm with meta key,
- use=st,
+ use=term,
km,
rmm=\E[?1034l,
smm=\E[?1034h,
rs2=\E[4l\E>\E[?1034h,
is2=\E[4l\E>\E[?1034h,
-st-meta-256color| simpleterm with meta key and 256 colors,
- use=st-256color,
+term-meta-256color| simpleterm with meta key and 256 colors,
+ use=term-256color,
km,
rmm=\E[?1034l,
smm=\E[?1034h,
rs2=\E[4l\E>\E[?1034h,
is2=\E[4l\E>\E[?1034h,
-st-bs| simpleterm with backspace as backspace,
- use=st,
+term-bs| simpleterm with backspace as backspace,
+ use=term,
kbs=\010,
kdch1=\177,
-st-bs-256color| simpleterm with backspace as backspace and 256colors,
- use=st-256color,
+term-bs-256color| simpleterm with backspace as backspace and 256colors,
+ use=term-256color,
kbs=\010,
kdch1=\177,
diff --git a/sys/libn/error.c b/sys/libn/error.c
index 95a534a..a9d684c 100644
--- a/sys/libn/error.c
+++ b/sys/libn/error.c
@@ -1,6 +1,16 @@
#include <u.h>
#include <libn.h>
+void
+exits(char *s)
+{
+ if (s == nil || *s == 0)
+ exit(0);
+
+ fputs(s, stderr);
+ exit(1);
+}
+
void
errorf(byte* fmt, ...)
{
diff --git a/sys/libn/memory.c b/sys/libn/memory.c
index 31f910e..999819b 100644
--- a/sys/libn/memory.c
+++ b/sys/libn/memory.c
@@ -194,8 +194,3 @@ memset64(void *dst, uint64 val, uintptr size)
((byte*)dst)[i] = ((byte*)&val)[i&7];
}
}
-
-// -------------------------------------------------------------------------
-// First argument
-
-char *argv0;
diff --git a/sys/libn/rules.mk b/sys/libn/rules.mk
index 61a56fe..abcd60d 100644
--- a/sys/libn/rules.mk
+++ b/sys/libn/rules.mk
@@ -6,11 +6,13 @@ include share/push.mk
# Local sources
SRCS_$(d) := \
+ $(d)/arg.c \
$(d)/bufio.c \
$(d)/coro_unix_x64.s \
$(d)/coro.c \
$(d)/error.c \
$(d)/flate.c \
+ $(d)/fs.c \
$(d)/gz.c \
$(d)/io.c \
$(d)/os.c \
diff --git a/sys/libterm/buffer.c b/sys/libterm/buffer.c
deleted file mode 100644
index b903e71..0000000
--- a/sys/libterm/buffer.c
+++ /dev/null
@@ -1,326 +0,0 @@
-#include "term.h"
-
-/* row operations */
-void
-zero(Row *row, int start, int len)
-{
- int i;
- Cell cell = {
- .r = L'\0',
- .pen = {0},
- };
-
- for (i = start; i < len + start; i++)
- row->cells[i] = cell;
- row->dirty = true;
-}
-
-void
-roll(Row *start, Row *end, int count)
-{
- int n = end - start;
-
- count %= n;
- if (count < 0)
- count += n;
-
- if (count) {
- char buf[count * sizeof(Row)];
- memcpy(buf, start, count * sizeof(Row));
- memmove(start, start + count, (n - count) * sizeof(Row));
- memcpy(end - count, buf, count * sizeof(Row));
- for (Row *row = start; row < end; row++)
- row->dirty = true;
- }
-}
-
-/* buffer operations */
-void
-bclear(Buffer *b)
-{
- int i;
- Cell cell = {
- .r = L'\0',
- .pen = {
- .state = PenNormal,
- .col.fg = -1,
- .col.bg = -1,
- },
- };
-
- for (i = 0; i < b->nrow; i++) {
- Row *row = b->row + i;
- for (int j = 0; j < b->ncol; j++) {
- row->cells[j] = cell;
- row->dirty = true;
- }
- }
-}
-
-void
-bfree(Buffer *b)
-{
- int i;
-
- for (i = 0; i < b->nrow; i++)
- free(b->row[i].cells);
-
- free(b->row);
-
- for (i = 0; i < b->scroll.size; i++)
- free(b->scroll.buf[i].cells);
-
- free(b->scroll.buf);
- free(b->tabs);
-}
-
-void
-bscroll(Buffer *b, int s)
-{
- /* work in screenfuls */
- int ssz = b->scroll.bot - b->scroll.top;
-
- if (s > ssz) {
- bscroll(b, ssz);
- bscroll(b, s - ssz);
- return;
- }
- if (s < -ssz) {
- bscroll(b, -ssz);
- bscroll(b, s + ssz);
- return;
- }
-
- b->scroll.above += s;
- if (b->scroll.above >= b->scroll.size)
- b->scroll.above = b->scroll.size;
-
- if (s > 0 && b->scroll.size) {
- for (int i = 0; i < s; i++) {
- Row tmp = b->scroll.top[i];
- b->scroll.top[i] = b->scroll.buf[b->scroll.index];
- b->scroll.buf[b->scroll.index] = tmp;
-
- b->scroll.index++;
- if (b->scroll.index == b->scroll.size)
- b->scroll.index = 0;
- }
- }
- roll(b->scroll.top, b->scroll.bot, s);
- if (s < 0 && b->scroll.size) {
- for (int i = (-s) - 1; i >= 0; i--) {
- b->scroll.index--;
- if (b->scroll.index == -1)
- b->scroll.index = b->scroll.size - 1;
-
- Row tmp = b->scroll.top[i];
- b->scroll.top[i] = b->scroll.buf[b->scroll.index];
- b->scroll.buf[b->scroll.index] = tmp;
- b->scroll.top[i].dirty = true;
- }
- }
-}
-
-void
-bresize(Buffer *b, int nrow, int ncol)
-{
- Row *row = b->row;
-
- if (b->nrow != nrow) {
- if (b->crow >= row + nrow) {
- /* scroll up instead of simply chopping off bottom */
- bscroll(b, (b->crow - b->row) - nrow + 1);
- }
- while (b->nrow > nrow) {
- free(row[b->nrow - 1].cells);
- b->nrow--;
- }
-
- row = realloc(row, sizeof(Row) * nrow);
- }
-
- if (b->maxcols < ncol) {
- for (int r = 0; r < b->nrow; r++) {
- row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(row + r, b->ncol, ncol - b->ncol);
- row[r].dirty = true;
- }
- Row *sbuf = b->scroll.buf;
- for (int r = 0; r < b->scroll.size; r++) {
- sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
- if (b->ncol < ncol)
- zero(sbuf + r, b->ncol, ncol - b->ncol);
- }
- b->tabs = realloc(b->tabs, sizeof(*b->tabs) * ncol);
- for (int c = b->ncol; c < ncol; c++)
- b->tabs[c] = !(c & 7);
- b->maxcols = ncol;
- b->ncol = ncol;
- } else if (b->ncol != ncol) {
- for (int r = 0; r < b->nrow; r++)
- row[r].dirty = true;
- b->ncol = ncol;
- }
-
- int deltarows = 0;
- if (b->nrow < nrow) {
- while (b->nrow < nrow) {
- row[b->nrow].cells = calloc(b->maxcols, sizeof(Cell));
- zero(row + b->nrow, 0, b->maxcols);
- b->nrow++;
- }
-
- /* prepare for backfill */
- if (b->crow >= b->scroll.bot - 1) {
- deltarows = b->row + nrow - b->crow - 1;
- if (deltarows > b->scroll.above)
- deltarows = b->scroll.above;
- }
- }
-
- b->crow += row - b->row;
- b->scroll.top = row;
- b->scroll.bot = row + nrow;
- b->row = row;
-
- /* perform backfill */
- if (deltarows > 0) {
- bscroll(b, -deltarows);
- b->crow += deltarows;
- }
-}
-
-bool
-binit(Buffer *b, int rows, int cols, int size)
-{
- b->pen.state = PenNormal;
- b->pen.col.fg = b->pen.col.fg = -1;
-
- if (size < 0)
- size = 0;
- if (size && !(b->scroll.buf = calloc(size, sizeof(Row))))
- return false;
-
- b->scroll.size = size;
- bresize(b, rows, cols);
- return true;
-}
-
-void
-bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
-{
- if (bs)
- *bs = nil;
- if (be)
- *be = nil;
- if (as)
- *as = nil;
- if (ae)
- *ae = nil;
- if (!b->scroll.size)
- return;
-
- if (b->scroll.above) {
- if (bs)
- *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size];
- if (be)
- *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size];
- }
- if (b->scroll.below) {
- if (as)
- *as = &b->scroll.buf[b->scroll.index];
- if (ae)
- *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size];
- }
-}
-
-Row *
-browfirst(Buffer *b)
-{
- Row *bstart;
- if (!b->scroll.size || !b->scroll.above)
- return b->row;
- bboundary(b, &bstart, nil, nil, nil);
- return bstart;
-}
-
-Row *
-browlast(Buffer *b)
-{
- Row *aend;
- if (!b->scroll.size || !b->scroll.below)
- return b->row + b->nrow - 1;
- bboundary(b, nil, nil, nil, &aend);
- return aend;
-}
-
-Row *
-brownext(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->row, *last = b->row + b->nrow - 1;
-
- if (!row)
- return nil;
-
- bboundary(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row >= first && row < last)
- return ++row;
- if (row == last)
- return after_start;
- if (row == before_end)
- return first;
- if (row == after_end)
- return nil;
- if (row == &b->scroll.buf[b->scroll.size - 1])
- return b->scroll.buf;
- return ++row;
-}
-
-Row *
-bprevrow(Buffer *b, Row *row)
-{
- Row *before_start, *before_end, *after_start, *after_end;
- Row *first = b->row, *last = b->row + b->nrow - 1;
-
- if (!row)
- return nil;
-
- bboundary(b, &before_start, &before_end, &after_start, &after_end);
-
- if (row > first && row <= last)
- return --row;
- if (row == first)
- return before_end;
- if (row == before_start)
- return nil;
- if (row == after_start)
- return last;
- if (row == b->scroll.buf)
- return &b->scroll.buf[b->scroll.size - 1];
- return --row;
-}
-
-void
-brender(Buffer *b, Term *t)
-{
- int r, c, n;
- char u[UTFmax+1];
- Row *row;
- Cell *cell;
-
- for (r = 0; r < b->nrow; r++) {
- row = b->row + r;
- if (!row->dirty)
- continue;
-
- for (c = 0; c < b->ncol; c++) {
- cell = row->cells + c;
- tsetpen(t, cell->pen);
- n = utf8·runetobyte(u, &cell->r);
- twrite(t, n, u);
- }
- }
-}
diff --git a/sys/libterm/escseq.c b/sys/libterm/escseq.c
deleted file mode 100644
index e69de29..0000000
--- a/sys/libterm/escseq.c
+++ /dev/null
diff --git a/sys/libterm/events.c b/sys/libterm/events.c
deleted file mode 100644
index 80bc99a..0000000
--- a/sys/libterm/events.c
+++ /dev/null
@@ -1,1692 +0,0 @@
-#include "term.h"
-
-#include <poll.h>
-
-#define bufcount(in) in->buf.c - in->buf.b
-
-enum {
- NodeKey,
- NodeArr,
-};
-
-struct Node
-{
- int type;
-};
-
-struct KeyNode
-{
- struct Node;
- struct KeyInfo key;
-};
-
-struct ArrNode
-{
- struct Node;
- uchar min, max;
- Node *arr[];
-};
-
-// -----------------------------------------------------------------------
-// loads data into trie
-
-static enum KeyEvent peekmousekey(Input *in, Key *key, ulong *nb);
-
-#define FuncNameMax 10
-static struct {
- char *name;
- int type;
- int sym;
- int mods;
-} funcs[] =
-{
- /* THIS LIST MUST REMAIN SORTED ALPHABETICALLY! */
- { "backspace", KeySym, SymBackspace, 0 },
- { "begin", KeySym, SymBegin, 0 },
- { "beg", KeySym, SymBegin, 0 },
- { "btab", KeySym, SymTab, ModShift},
- { "cancel", KeySym, SymCancel, 0 },
- { "clear", KeySym, SymClear, 0 },
- { "close", KeySym, SymClose, 0 },
- { "command", KeySym, SymCommand, 0 },
- { "copy", KeySym, SymCopy, 0 },
- { "dc", KeySym, SymDelete, 0 },
- { "down", KeySym, SymDown, 0 },
- { "end", KeySym, SymEnd, 0 },
- { "enter", KeySym, SymEnter, 0 },
- { "exit", KeySym, SymExit, 0 },
- { "find", KeySym, SymFind, 0 },
- { "help", KeySym, SymHelp, 0 },
- { "home", KeySym, SymHome, 0 },
- { "ic", KeySym, SymInsert, 0 },
- { "left", KeySym, SymLeft, 0 },
- { "mark", KeySym, SymMark, 0 },
- { "message", KeySym, SymMessage, 0 },
- { "move", KeySym, SymMove, 0 },
- { "next", KeySym, SymPagedown, 0 }, // Not quite, but it's the best we can do
- { "npage", KeySym, SymPagedown, 0 },
- { "open", KeySym, SymOpen, 0 },
- { "options", KeySym, SymOptions, 0 },
- { "ppage", KeySym, SymPageup, 0 },
- { "previous", KeySym, SymPageup, 0 }, // Not quite, but it's the best we can do
- { "print", KeySym, SymPrint, 0 },
- { "redo", KeySym, SymRedo, 0 },
- { "reference", KeySym, SymReference, 0 },
- { "refresh", KeySym, SymRefresh, 0 },
- { "replace", KeySym, SymReplace, 0 },
- { "restart", KeySym, SymRestart, 0 },
- { "resume", KeySym, SymResume, 0 },
- { "right", KeySym, SymRight, 0 },
- { "save", KeySym, SymSave, 0 },
- { "select", KeySym, SymSelect, 0 },
- { "suspend", KeySym, SymSuspend, 0 },
- { "undo", KeySym, SymUndo, 0 },
- { "up", KeySym, SymUp, 0 },
- { nil },
-};
-
-// -----------------------------------------------------------------------
-// utility functions
-
-static
-int
-stricmp(char *s, char *t)
-{
- if (s == nil)
- if (t == nil)
- return 0;
- else
- return -strlen(t);
- else
- if (t == nil)
- return +strlen(s);
-
- int d;
- for (; *s && *t; s++, t++) {
- d = tolower(*s) - tolower(*t);
- if (d < 0 || d > 0)
- return d;
- }
-
- /* XXX: not sure about passing in 0 here */
- return tolower(*s) - tolower(*t);
-}
-
-// -----------------------------------------------------------------------
-// bytes -> keysymbols
-
-static
-Node *
-keynode(int type, int sym, int mask, int set)
-{
- struct KeyNode *n;
- n = malloc(sizeof(*n));
- if (!n)
- panicf("out of memory");
-
- n->type = NodeKey;
- n->key.type = type;
- n->key.sym = sym;
- n->key.modmask = mask;
- n->key.modset = set;
-
- return (Node *)n;
-}
-
-static
-Node *
-arrnode(uchar min, uchar max)
-{
- int nb, i;
- struct ArrNode *n;
- nb = ((int)max-min+1)*sizeof(*n->arr);
- n = malloc(sizeof(*n) + nb);
- if (!n)
- panicf("out of memory");
-
- n->type = NodeArr;
- n->min = min, n->max = max;
- for (i = 0; i <= max-min; i++)
- n->arr[i] = nil;
-
- return (Node *)n;
-}
-
-
-static
-Node *
-nlookup(Node *n, uchar b)
-{
- struct ArrNode *arr;
- switch (n->type) {
- case NodeKey:
- panicf("attempting to subdivide a leaf key node");
- return nil;
- case NodeArr:
- arr = (struct ArrNode *)n;
- if (b < arr->min || b > arr->max)
- return nil;
- return arr->arr[b - arr->min];
- default:
- panicf("unrecognized key node type");
- return nil;
- }
-}
-
-static
-Node *
-compress(Node *root)
-{
- int i;
- uchar min, max;
- struct ArrNode *n, *new;
-
- if (!root)
- return root;
-
- switch (root->type) {
- case NodeKey:
- return root;
- case NodeArr:
- n = (struct ArrNode *)root;
- /* find the zeros */
- for (min = 0; !n->arr[min]; min++) {
- if (min == 255 && !n->arr[min]) {
- free(n);
- return arrnode(1, 0);
- }
- }
-
- for (max = 0xff; !n->arr[max]; max--)
- ;
-
- new = (struct ArrNode *)arrnode(min, max);
-
- for (i = min; i <= max; i++)
- new->arr[i-min] = compress(n->arr[i]);
-
- free(n);
- return (Node*)new;
- default:
- panicf("unrecognized key node type");
- return nil;
- }
-
- return root;
-}
-
-static
-void
-teardown(Node *n)
-{
- int i;
- struct ArrNode *arr;
-
- switch (n->type) {
- case NodeKey:
- break;
- case NodeArr:
- arr = (struct ArrNode *)n;
- for (i = arr->min; i <= arr->max; i++)
- if (arr->arr[i - arr->min])
- teardown(arr->arr[i - arr->min]);
- break;
- default:
- return;
- }
-
- free(n);
-}
-
-static
-int
-insert(Node *root, Node *n, char *seq)
-{
- int pos;
- uchar b;
- Node *nn;
- struct ArrNode *a;
-
- for (pos = 0; (b = seq[pos]); pos++) {
- nn = nlookup(root, b);
- if (!nn)
- break;
- root = nn;
- a = (struct ArrNode *)root;
- }
-
- for (; (b = seq[pos]); pos++) {
- nn = (seq[pos+1]) ? arrnode(0, 0xff) : n;
- if (!nn)
- return 0;
-
- if (root->type != NodeArr)
- panicf("inserted child node onto leaf 'key' node");
-
- a = (struct ArrNode*)root;
- if (b < a->min || b > a->max)
- panicf("out of bound trie insertion");
-
- a->arr[b-a->min] = root = nn;
- }
-
- return 1;
-}
-
-/* unibilium helpers */
-static
-enum unibi_string
-nametostr(char *name)
-{
- enum unibi_string ret;
- for (ret = unibi_string_begin_+1; ret < unibi_string_end_; ret++)
- if (!strcmp(unibi_name_str(ret), name))
- return ret;
-
- return -1;
-}
-
-static
-char *
-unibi_lookup(unibi_term *info, char *name)
-{
- enum unibi_string idx;
- if ((idx = nametostr(name)) == -1)
- return nil;
-
- return (char*)unibi_get_str(info, idx);
-}
-
-static
-Node *
-tryloadkey(unibi_term *info, char *name, char **val, struct KeyInfo *key)
-{
- char *v;
-
- v = unibi_lookup(info, name);
- if (!v || v[0] == 0)
- return nil;
-
- *val = v;
- return keynode(key->type, key->sym, key->modmask, key->modset);
-}
-
-static
-Node *
-loadtermkeys(unibi_term *info)
-{
- int i;
- struct Node *root, *n;
- char *val, name[5 + FuncNameMax + 1];
-
- root = arrnode(0, 0xff);
- if (!root)
- panicf("out of memory");
-
- /* load key syms */
- for (i = 0; funcs[i].name; i++) {
- sprintf(name, "key_%s", funcs[i].name);
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = funcs[i].type,
- .sym = funcs[i].sym,
- .modmask = funcs[i].mods,
- .modset = funcs[i].mods,
- });
-
- /* try shift modified */
- if (!n)
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = funcs[i].type,
- .sym = funcs[i].sym,
- .modmask = funcs[i].mods | ModShift,
- .modset = funcs[i].mods | ModShift,
- });
-
- /* give up */
- if (n)
- insert(root, n, val);
- }
-
- /* load function keys */
- for (i = 1; i < 0xff; i++) {
- sprintf(name, "key_f%d", i);
- n = tryloadkey(info, name, &val, &(struct KeyInfo){
- .type = KeyFunc,
- .sym = i,
- .modmask = 0,
- .modset = 0,
- });
- if (!n)
- break;
-
- insert(root, n, val);
- }
-
- /* load mouse keys */
- val = unibi_lookup(info, "key_mouse");
- if (val && !strcmp(val, "\x1b[M")) {
- n = keynode(KeyMouse, 0, 0, 0);
- insert(root, n, val);
- }
-
- return compress(root);
-}
-
-static
-enum KeyEvent
-peeksym(Input *in, Key *key, int force, ulong *nb)
-{
- int res;
- uchar *b, *o;
- Node *root;
- struct KeyNode *kn;
-
- root = in->keys;
- b = in->buf.b;
- while (b <= in->buf.c) {
- printf("checking '%s'\n", b);
- root = nlookup(root, *b);
- if (!root)
- break;
- b++;
-
- if (root->type != NodeKey)
- continue;
-
- kn = (struct KeyNode*)root;
- if (kn->key.type == KeyMouse) {
- o = in->buf.b, in->buf.b = b;
- res = peekmousekey(in, key, nb);
- in->buf.b = o;
-
- if (res == EvKey)
- *nb += b - in->buf.b;
-
- return res;
- }
- key->type = kn->key.type;
- key->mods = kn->key.modset;
- key->code.sym = kn->key.type;
- *nb = b - in->buf.b;
-
- return EvKey;
- }
-
- if (root && !force)
- return EvAgain;
-
- return EvNil;
-}
-
-// -----------------------------------------------------------------------
-// misc commands
-
-static
-inline
-void
-setpos(Key *key, int row, int col)
-{
- row = MIN(row, 0xfff);
- col = MIN(col, 0xfff);
-
- key->code.mouse[1] = (row & 0x0ff);
- key->code.mouse[2] = (col & 0x0ff);
- key->code.mouse[3] = (row & 0xf00) >> 8 | (col & 0x300) >> 4;
-}
-
-static
-inline
-void
-getpos(Key *key, int *row, int *col)
-{
- if (col)
- *col = ((uchar)key->code.mouse[1] | ((uchar)key->code.mouse[3] & 0x0f) << 8) - 1;
-
- if (row)
- *row = ((uchar)key->code.mouse[2] | ((uchar)key->code.mouse[3] & 0x70) << 4) - 1;
-}
-
-// -----------------------------------------------------------------------
-// csi/ss3 commands
-
-static int csiinit;
-
-static struct KeyInfo ss3[64];
-static char ss3kpalts[64];
-
-static struct KeyInfo csiss3[64];
-static enum KeyEvent (*do_csi[64])(Input *in, Key *key, int cmd, int narg, long *arg);
-
-static struct KeyInfo csifuncs[35];
-
-/* csi/ss3 cmd keys */
-static
-enum KeyEvent
-do_csi_full(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- if(narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- key->type = csiss3[cmd - 0x40].type;
- key->code.sym = csiss3[cmd - 0x40].sym;
- key->mods &= ~(csiss3[cmd - 0x40].modmask);
- key->mods |= csiss3[cmd - 0x40].modset;
-
- if(key->code.sym == SymUnknown)
- return EvNil;
-
- return EvKey;
-}
-
-static
-void
-put_csi_full(int type, int sym, int modset, int modmask, uchar cmd)
-{
- if(cmd < 0x40 || cmd >= 0x80)
- return;
-
- csiss3[cmd - 0x40].type = type;
- csiss3[cmd - 0x40].sym = sym;
- csiss3[cmd - 0x40].modset = modset;
- csiss3[cmd - 0x40].modmask = modmask;
-
- do_csi[cmd - 0x40] = &do_csi_full;
-}
-
-/* ss3 kpad keys */
-static
-void
-put_ss3_kpalt(int type, int sym, uchar cmd, char kpalt)
-{
- if(cmd < 0x40 || cmd >= 0x80)
- return;
-
- ss3[cmd - 0x40].type = type;
- ss3[cmd - 0x40].sym = sym;
- ss3[cmd - 0x40].modset = 0;
- ss3[cmd - 0x40].modmask= 0;
- ss3kpalts[cmd - 0x40] = kpalt;
-}
-
-/* csi number ~func keys */
-static void emitcodepoint(Input *in, rune r, Key *key);
-
-static
-enum KeyEvent
-do_csi_func(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- if (narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- key->type = KeySym;
-
- if (arg[0] == 27) {
- int mod = key->mods;
- emitcodepoint(in, (rune)arg[2], key);
- key->mods |= mod;
- } else if (arg[0] >= 0 && arg[0] < arrlen(csifuncs)) {
- key->type = csifuncs[arg[0]].type;
- key->code.sym = csifuncs[arg[0]].sym;
- key->mods &= ~(csifuncs[arg[0]].modmask);
- key->mods |= csifuncs[arg[0]].modset;
- } else
- key->code.sym = SymUnknown;
-
- if (key->code.sym == SymUnknown)
- return EvNil;
-
- return EvKey;
-}
-
-static
-void
-put_csi_func(int type, int sym, int num)
-{
- if(num >= arrlen(csifuncs))
- return;
-
- csifuncs[num].type = type;
- csifuncs[num].sym = sym;
- csifuncs[num].modset = 0;
- csifuncs[num].modmask = 0;
-
- do_csi['~' - 0x40] = &do_csi_func;
-}
-
-/* CSI u extended unicode keys */
-static
-enum KeyEvent
-do_csi_u(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch(cmd) {
- case 'u': {
- if(narg > 1 && arg[1] != -1)
- key->mods = arg[1] - 1;
- else
- key->mods = 0;
-
- int mod = key->mods;
- key->type = KeySym;
- emitcodepoint(in, arg[0], key);
- key->mods |= mod;
-
- return EvKey;
- }
- default:
- return EvNil;
- }
-}
-
-/* csi m/M mouse events */
-
-static
-enum KeyEvent
-do_csi_m(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- int initial = cmd >> 8;
- cmd &= 0xff;
-
- switch(cmd) {
- case 'M':
- case 'm':
- break;
- default:
- return EvNil;
- }
-
- // rxvt protocol
- if(!initial && narg >= 3) {
- key->type = KeyMouse;
- key->code.mouse[0] = arg[0];
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- setpos(key, arg[1], arg[2]);
-
- return EvKey;
- }
-
- // SGR protocol
- if(initial == '<' && narg >= 3) {
- key->type = KeyMouse;
- key->code.mouse[0] = arg[0];
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- setpos(key, arg[1], arg[2]);
-
- if(cmd == 'm') // release
- key->code.mouse[3] |= 0x80;
-
- return EvKey;
- }
-
- return EvNil;
-}
-
-/* csi ? R position events */
-
-static
-enum KeyEvent
-do_csi_R(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch(cmd) {
- case 'R'|'?'<<8:
- if(narg < 2)
- return EvNil;
- key->type = KeyPosition;
- setpos(key, arg[1], arg[0]);
- return EvKey;
- default:
- return do_csi_full(in, key, cmd, narg, arg);
- }
-}
-
-/* csi $y mode status events */
-
-static
-enum KeyEvent
-do_csi_y(Input *in, Key *key, int cmd, int narg, long *arg)
-{
- switch (cmd) {
- case 'y'|'$'<<16:
- case 'y'|'$'<<16 | '?'<<8:
- if (narg < 2)
- return EvNil;
-
- key->type = KeyModeReport;
- key->code.mouse[0] = (cmd >> 8);
- key->code.mouse[1] = arg[0] >> 8;
- key->code.mouse[2] = arg[0] & 0xff;
- key->code.mouse[3] = arg[1];
- return EvKey;
-
- default:
- return EvNil;
- }
-}
-
-/* parse csi events */
-static
-enum KeyEvent
-parse_csi(Input *in, ulong introlen, ulong *csi_len, ulong *nargs, long args[], unsigned long *commandp)
-{
- ulong csi_end = introlen;
-
- while (csi_end < bufcount(in)) {
- if (in->buf.b[csi_end] >= 0x40 && in->buf.b[csi_end] < 0x80)
- break;
- csi_end++;
- }
-
- if(csi_end >= bufcount(in))
- return EvAgain;
-
- uchar cmd = in->buf.b[csi_end];
- *commandp = cmd;
-
- char present = 0;
- int argi = 0;
-
- ulong p = introlen;
-
- // See if there is an initial byte
- if (in->buf.b[p] >= '<' && in->buf.b[p] <= '?') {
- *commandp |= (in->buf.b[p] << 8);
- p++;
- }
-
- // Now attempt to parse out up number;number;... separated values
- while (p < csi_end) {
- uchar c = in->buf.b[p];
-
- if (c >= '0' && c <= '9') {
- if (!present) {
- args[argi] = c - '0';
- present = 1;
- } else {
- args[argi] = (args[argi] * 10) + c - '0';
- }
- } else if(c == ';') {
- if (!present)
- args[argi] = -1;
- present = 0;
- argi++;
-
- if(argi > 16)
- break;
- } else if (c >= 0x20 && c <= 0x2f) {
- *commandp |= c << 16;
- break;
- }
- p++;
- }
-
- if(present)
- argi++;
-
- *nargs = argi;
- *csi_len = csi_end + 1;
-
- return EvKey;
-}
-
-static
-void
-loadctrlkeys(void)
-{
- int i;
- for(i = 0; i < 64; i++) {
- csiss3[i].sym = SymUnknown;
- ss3[i].sym = SymUnknown;
- ss3kpalts[i] = 0;
- }
-
- for(i = 0; i < arrlen(csifuncs); i++)
- csifuncs[i].sym = SymUnknown;
-
- put_csi_full(KeySym, SymUp, 0, 0, 'A');
- put_csi_full(KeySym, SymDown, 0, 0, 'B');
- put_csi_full(KeySym, SymRight,0, 0, 'C');
- put_csi_full(KeySym, SymLeft, 0, 0, 'D');
- put_csi_full(KeySym, SymBegin,0, 0, 'E');
- put_csi_full(KeySym, SymEnd, 0, 0, 'F');
- put_csi_full(KeySym, SymHome, 0, 0, 'H');
- put_csi_full(KeySym, 1, 0, 0, 'P');
- put_csi_full(KeySym, 2, 0, 0, 'Q');
- put_csi_full(KeySym, 3, 0, 0, 'R');
- put_csi_full(KeySym, 4, 0, 0, 'S');
-
- put_csi_full(KeySym, SymTab, ModShift, ModShift, 'Z');
-
- put_ss3_kpalt(KeySym, SymKpenter, 'M', 0);
- put_ss3_kpalt(KeySym, SymKpequals, 'X', '=');
- put_ss3_kpalt(KeySym, SymKpmult, 'j', '*');
- put_ss3_kpalt(KeySym, SymKpplus, 'k', '+');
- put_ss3_kpalt(KeySym, SymKpcomma, 'l', ',');
- put_ss3_kpalt(KeySym, SymKpminus, 'm', '-');
- put_ss3_kpalt(KeySym, SymKpperiod, 'n', '.');
- put_ss3_kpalt(KeySym, SymKpdiv, 'o', '/');
- put_ss3_kpalt(KeySym, SymKp0, 'p', '0');
- put_ss3_kpalt(KeySym, SymKp1, 'q', '1');
- put_ss3_kpalt(KeySym, SymKp2, 'r', '2');
- put_ss3_kpalt(KeySym, SymKp3, 's', '3');
- put_ss3_kpalt(KeySym, SymKp4, 't', '4');
- put_ss3_kpalt(KeySym, SymKp5, 'u', '5');
- put_ss3_kpalt(KeySym, SymKp6, 'v', '6');
- put_ss3_kpalt(KeySym, SymKp7, 'w', '7');
- put_ss3_kpalt(KeySym, SymKp8, 'x', '8');
- put_ss3_kpalt(KeySym, SymKp9, 'y', '9');
-
- put_csi_func(KeySym, SymFind, 1);
- put_csi_func(KeySym, SymInsert, 2);
- put_csi_func(KeySym, SymDelete, 3);
- put_csi_func(KeySym, SymSelect, 4);
- put_csi_func(KeySym, SymPageup, 5);
- put_csi_func(KeySym, SymPagedown, 6);
- put_csi_func(KeySym, SymHome, 7);
- put_csi_func(KeySym, SymEnd, 8);
-
- put_csi_func(KeyFunc, 1, 11);
- put_csi_func(KeyFunc, 2, 12);
- put_csi_func(KeyFunc, 3, 13);
- put_csi_func(KeyFunc, 4, 14);
- put_csi_func(KeyFunc, 5, 15);
- put_csi_func(KeyFunc, 6, 17);
- put_csi_func(KeyFunc, 7, 18);
- put_csi_func(KeyFunc, 8, 19);
- put_csi_func(KeyFunc, 9, 20);
- put_csi_func(KeyFunc, 10, 21);
- put_csi_func(KeyFunc, 11, 23);
- put_csi_func(KeyFunc, 12, 24);
- put_csi_func(KeyFunc, 13, 25);
- put_csi_func(KeyFunc, 14, 26);
- put_csi_func(KeyFunc, 15, 28);
- put_csi_func(KeyFunc, 16, 29);
- put_csi_func(KeyFunc, 17, 31);
- put_csi_func(KeyFunc, 18, 32);
- put_csi_func(KeyFunc, 19, 33);
- put_csi_func(KeyFunc, 20, 34);
-
- do_csi['u' - 0x40] = &do_csi_u;
- do_csi['M' - 0x40] = &do_csi_m;
- do_csi['m' - 0x40] = &do_csi_m;
- do_csi['R' - 0x40] = &do_csi_R;
-
- do_csi['y' - 0x40] = &do_csi_y;
-
- csiinit = 1;
-}
-
-static
-enum KeyEvent
-peekcsi(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- ulong csi_len;
- long arg[16];
- ulong nargs = arrlen(arg);
- ulong cmd;
-
- enum KeyEvent ev = parse_csi(in, introlen, &csi_len, &nargs, arg, &cmd);
-
- if (ev== EvAgain) {
- if(!force)
- return ev;
-
- emitcodepoint(in, '[', key);
- key->mods |= ModAlt;
- *nb = introlen;
- return EvKey;
- }
- // Mouse in X10 encoding consumes the next 3 bytes also
- if (cmd == 'M' && nargs < 3) {
- in->buf.b += csi_len;
- ev = peekmousekey(in, key, nb);
- in->buf.b -= csi_len;
-
- if (ev == EvKey)
- *nb += csi_len;
-
- return ev;
- }
-
- ev = EvNil;
-
- // We know from the logic above that cmd must be >= 0x40 and < 0x80
- if (do_csi[(cmd & 0xff) - 0x40])
- ev = (*do_csi[(cmd & 0xff) - 0x40])(in, key, cmd, nargs, arg);
-
- if (ev == EvNil) {
- key->type = KeyUnknownCSI;
- key->code.num = cmd;
- key->mods = 0;
-
- in->buf.off = csi_len - introlen;
- *nb = introlen; /* dont advance yet */
- return EvKey;
- }
-
- *nb = csi_len;
- return ev;
-}
-
-static
-enum KeyEvent
-peekss3(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- if(bufcount(in) < introlen + 1) {
- if(!force)
- return EvAgain;
-
- emitcodepoint(in, 'O', key);
- key->mods |= ModAlt;
- *nb= bufcount(in);
- return EvKey;
- }
- uchar cmd = in->buf.b[introlen];
-
- if(cmd < 0x40 || cmd >= 0x80)
- return EvNil;
-
- key->type = csiss3[cmd - 0x40].type;
- key->code.sym = csiss3[cmd - 0x40].sym;
- key->mods = csiss3[cmd - 0x40].modset;
-
- if (key->code.sym == SymUnknown) {
- if (in->flags & FlagConvertKP && ss3kpalts[cmd - 0x40]) {
- key->type = KeyUnicode;
- key->code.pt = ss3kpalts[cmd - 0x40];
- key->mods = 0;
-
- key->utf8[0] = key->code.pt;
- key->utf8[1] = 0;
- } else {
- key->type = ss3[cmd - 0x40].type;
- key->code.sym = ss3[cmd - 0x40].sym;
- key->mods = ss3[cmd - 0x40].modset;
- }
- }
-
- if(key->code.sym == SymUnknown)
- return EvNil;
-
- *nb = introlen + 1;
-
- return EvKey;
-}
-
-static
-enum KeyEvent
-peekctrl(Input *in, ulong introlen, Key *key, int force, ulong *nb)
-{
- ulong str_end = introlen;
-
- while(str_end < bufcount(in)) {
- if (in->buf.b[str_end] == 0x9c) // ST
- break;
- if (in->buf.b[str_end] == 0x1b &&
- (str_end + 1) < bufcount(in) &&
- in->buf.b[str_end+1] == 0x5c) // ESC-prefixed ST
- break;
-
- str_end++;
- }
-
- if (str_end >= bufcount(in))
- return EvAgain;
-
- *nb = str_end + 1;
- if(in->buf.b[str_end] == 0x1b)
- (*nb)++;
-
- // XXX: read carefully
- if(in->savedcsi)
- free(in->savedcsi);
-
- ulong len = str_end - introlen;
-
- in->nsavedcsi++;
- in->savedcsi = malloc(len + 1);
-
- strncpy(in->savedcsi, (char *)in->buf.b + introlen, len);
- in->savedcsi[len] = 0;
-
- key->type = (in->buf.b[introlen-1] & 0x1f) == 0x10 ? KeyDCS : KeyOSC;
- key->code.num = in->nsavedcsi;
- key->mods = 0;
-
- return EvKey;
-}
-
-static
-enum KeyEvent
-peekesc(Input *in, Key *key, int force, ulong *nb)
-{
- if (bufcount(in) == 0)
- return in->closed ? EvEOF : EvNil;
-
- switch (*in->buf.b) {
- case 0x1b:
- if(bufcount(in) < 2)
- return EvNil;
-
- switch(in->buf.b[1]) {
- case 0x4f: // ESC-prefixed SS3
- return peekss3(in, 2, key, force, nb);
-
- case 0x50: // ESC-prefixed DCS
- case 0x5d: // ESC-prefixed OSC
- return peekctrl(in, 2, key, force, nb);
-
- case 0x5b: // ESC-prefixed CSI
- return peekcsi(in, 2, key, force, nb);
- }
-
- return EvNil;
-
- case 0x8f: // SS3
- return peekss3(in, 1, key, force, nb);
-
- case 0x90: // DCS
- case 0x9d: // OSC
- return peekctrl(in, 1, key, force, nb);
-
- case 0x9b: // CSI
- return peekcsi(in, 1, key, force, nb);
- }
-
- return EvNil;
-}
-
-
-// -----------------------------------------------------------------------
-// internal functions
-
-static
-int
-registername(Input *in, int sym, char *name)
-{
- if (!sym)
- sym = in->nkeynm;
-
- if(sym >= in->nkeynm) {
- char **tmp = realloc(in->keynm, sizeof(*tmp) * (sym + 1));
- if(!tmp)
- return -1;
-
- in->keynm = tmp;
-
- // Fill in the hole
- for(int i = in->nkeynm; i < sym; i++)
- in->keynm[i] = nil;
-
- in->nkeynm = sym + 1;
- }
-
- in->keynm[sym] = name;
-
- return sym;
-}
-
-static
-int
-registerc0(Input *in, int sym, int modset, int modmask, uchar ctrl, char *name)
-{
- if(ctrl >= 0x20) {
- errno = EINVAL;
- return -1;
- }
-
- if (name)
- sym = registername(in, sym, name);
-
- in->c0[ctrl].sym = sym;
- in->c0[ctrl].modset = modset;
- in->c0[ctrl].modmask = modmask;
-
- return sym;
-}
-
-static
-void
-advance(Input *in, ulong nb)
-{
- if (in->buf.c < in->buf.b + nb) {
- in->buf.b = in->buf.c;
- return;
- }
-
- in->buf.b += nb;
-}
-
-static
-void
-slidebuffer(Input *in)
-{
- static const ulong halfway = arrlen(in->buf.bytes) / 2;
- if (in->buf.b - in->buf.bytes > halfway) {
- memmove(in->buf.bytes, in->buf.bytes + halfway, halfway);
- in->buf.b -= halfway;
- in->buf.c -= halfway;
- }
-}
-
-static
-void
-harmonize(Input *in, Key *key)
-{
- int flags = in->hflag;
-
- if (flags & HarmonizeSpace) {
- if (key->type == KeyUnicode && key->code.pt == 0x20) {
- key->type = KeySym;
- key->code.sym = SymSpace;
- }
- } else {
- if (key->type == KeySym && key->code.sym == SymSpace) {
- key->type = KeyUnicode;
- key->code.pt = 0x20;
- utf8·runetobyte((char*)key->utf8, &key->code.pt);
- }
- }
-
- if (flags & HarmonizeDelBS) {
- if (key->type == KeySym && key->code.sym == SymDel) {
- key->code.sym = SymBackspace;
- }
- }
-}
-
-static
-void
-emitcodepoint(Input *in, rune r, Key *key)
-{
- if (r == 0) {
- key->type = KeySym;
- key->code.sym = SymSpace;
- key->mods = ModCtrl;
- goto harmonize;
- }
- if (r < 0x20) {
- key->code.pt = 0;
- key->mods = 0;
- if (!(in->flags & FlagNoInterpret) && in->c0[r].sym != SymUnknown) {
- key->code.sym = in->c0[r].sym;
- key->mods |= in->c0[r].modset;
- }
- if (!key->code.sym) {
- key->type = KeyUnicode;
- if (r+0x40 >= 'A' && r+0x40 <= 'Z')
- // it's a letter - use lowercase instead
- key->code.pt = r + 0x60;
- else
- key->code.pt = r + 0x40;
- key->mods = ModCtrl;
- } else
- key->type = KeySym;
- goto harmonize;
- }
- if (r == 0x7f && !(in->flags & FlagNoInterpret)) {
- // ascii del
- key->type = KeySym;
- key->code.sym = SymDel;
- key->mods = 0;
- goto harmonize;
- }
- if (r >= 0x20 && r < 0x80) {
- // ascii lowbyte range
- key->type = KeyUnicode;
- key->code.pt = r;
- key->mods = 0;
- goto harmonize;
- }
- if (r >= 0x80 && r < 0xa0) {
- // UTF-8 never starts with a C1 byte. So we can be sure of these
- key->type = KeyUnicode;
- key->code.pt = r - 0x40;
- key->mods = ModCtrl|ModAlt;
- goto harmonize;
- }
- key->type = KeyUnicode;
- key->code.pt = r;
- key->mods = 0;
-
-harmonize:
- harmonize(in, key);
- utf8·runetobyte((char*)key->utf8, &key->code.pt);
-}
-
-static
-enum KeyEvent
-peekmousekey(Input *in, Key *key, ulong *nb)
-{
- if (in->buf.c - in->buf.b < 3)
- return EvAgain;
-
- key->type = KeyMouse;
- key->code.mouse[0] = in->buf.c[0] - 0x20;
- key->code.mouse[1] = in->buf.c[1] - 0x20;
- key->code.mouse[2] = in->buf.c[2] - 0x20;
- key->code.mouse[3] = 0;
-
- key->mods = (key->code.mouse[0] & 0x1c) >> 2;
- key->code.mouse[0] &= ~0x1c;
-
- *nb = 3;
- return EvKey;
-}
-
-enum KeyEvent peekkey(Input *in, Key *key, int force, ulong *nb);
-
-static
-enum KeyEvent
-peeksimplekey(Input *in, Key *key, int force, ulong *nb)
-{
- uchar c, *b;
- int n;
- rune r;
- enum KeyEvent ev;
-
- b = in->buf.b;
- c = *b;
-
- if (c == 0x1b) {
- if (bufcount(in) == 1) {
- if (!force)
- return EvAgain;
- goto ascii;
- }
- in->buf.b++;
- ev = peekkey(in, key, force, nb);
- in->buf.b--;
-
- switch (ev) {
- case EvKey:
- key->mods |= ModAlt;
- (*nb)++;
- /* fallthrough */
- case EvNil: case EvEOF:
- case EvAgain: case EvErr:
- return ev;
- }
- }
- if (c == 0xa0)
- goto ascii;
- if (in->flags & FlagUTF8) {
- n = utf8·bytetorune(&r, (char*)in->buf.b);
- *nb = n; /* store the number of bytes */
- if (n > bufcount(in)) {
- if (!force)
- return EvAgain;
- r = RuneErr;
- *nb = bufcount(in);
- }
- key->type = KeyUnicode;
- key->mods = 0;
- goto utf8;
- }
- /* if we are here just emit raw byte */
- key->type = KeyUnicode;
- key->code.pt = c;
- key->mods = 0;
- key->utf8[0] = c;
- key->utf8[1] = 0;
- *nb = 1;
- return EvKey;
-
-ascii:
- *nb = 1;
- r = c;
-utf8:
- emitcodepoint(in, r, key);
- return EvKey;
-}
-
-// -----------------------------------------------------------------------
-// exported functions
-
-Input *
-makeinput(int fd, int flags, unibi_term *info)
-{
- int i;
- Input *in;
- char *e;
-
- if (!(in = malloc(sizeof(in))))
- panicf("out of memory");
-
- in->fd = fd;
- if (!(flags & (FlagRaw|FlagUTF8))) {
- if (((e = getenv("LANG")) || (e = getenv("LC_MESSAGES")) || (e = getenv("LC_ALL"))) &&
- (e = strchr(e, '.')) && e++ && (!stricmp(e, "UTF-8") || !stricmp(e, "UTF8")))
- flags |= FlagUTF8;
- else
- flags |= FlagRaw;
- }
- in->flags = flags;
- in->wait = 50; /* in msec */
- in->closed = 0;
- in->started = 0;
- in->hasold = 0;
- in->keys = loadtermkeys(info);
-
- /* initialize buffer */
- in->buf.c = in->buf.b = in->buf.bytes;
- in->buf.e = arrend(in->buf.bytes);
- in->buf.off = 0;
- memset(in->buf.bytes, 0, arrlen(in->buf.bytes));
-
- /* initialize names */
- for (i = 0; i < 32; i++)
- in->c0[i].sym = SymNone;
-
- registerc0(in, SymTab, 0x09, 0, 0, nil);
- registerc0(in, SymEnter, 0x0d, 0, 0, nil);
- registerc0(in, SymEscape, 0x1b, 0, 0, nil);
-
- /* load in csi */
- in->nsavedcsi = 0;
- in->savedcsi = nil;
- loadctrlkeys();
-
- return in;
-}
-
-void
-freeinput(Input *in)
-{
- // free(in);
-}
-
-int
-startrecord(Input *in)
-{
- struct termios new;
- if (in->started)
- return 1;
-
- if (in->fd != -1 && !(in->flags & FlagNoTermIOS)) {
- if (tcgetattr(in->fd, &new) == 0) {
- in->oldterm = new;
- in->hasold = 1;
-
- new.c_iflag &= ~(IXON|INLCR|ICRNL);
- new.c_lflag &= ~(ICANON|ECHO|IEXTEN);
-
- new.c_cc[VMIN] = 1;
- new.c_cc[VTIME] = 0;
-
- if (in->flags & FlagCtrlC)
- new.c_lflag &= ~ISIG;
- else {
- /* Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT */
- new.c_cc[VQUIT] = _POSIX_VDISABLE;
- new.c_cc[VSUSP] = _POSIX_VDISABLE;
- /* Some OSes have Ctrl-Y==VDSUSP */
-# ifdef VDSUSP
- new.c_cc[VDSUSP] = _POSIX_VDISABLE;
-# endif
- }
- tcsetattr(in->fd, TCSANOW, &new);
- }
- }
-
- in->started = 1;
- return 1;
-}
-
-int
-stoprecord(Input *in)
-{
- if (!in->started)
- return 1;
-
- if (in->hasold)
- tcsetattr(in->fd, TCSANOW, &in->oldterm);
-
- in->started = 0;
- return 1;
-}
-
-enum KeyEvent
-peekkey(Input *in, Key *key, int force, ulong *nb)
-{
- int i, again = 0;
- enum KeyEvent ev;
- static enum KeyEvent (*peek[2])(Input *, Key *, int, ulong *) = { peeksym, peekesc };
-
- if (!in->started) {
- errno = EINVAL;
- return EvErr;
- }
-
- if (in->buf.off) {
- in->buf.b += in->buf.off;
- in->buf.off = 0;
- }
-
- for (i = 0; i < arrlen(peek); i++) {
- ev = peek[i](in, key, force, nb);
- switch (ev) {
- case EvKey:
- slidebuffer(in);
- /* fallthrough */
- case EvEOF:
- case EvErr:
- return ev;
- case EvAgain:
- if (!force)
- again = 1;
- /* fallthrough */
- case EvNil:
- continue;
- }
- }
- if (again)
- return EvAgain;
-
- return peeksimplekey(in, key, force, nb);
-}
-
-enum KeyEvent
-getkey(Input *in, Key *key)
-{
- ulong nb;
- enum KeyEvent ev;
-
- ev = peekkey(in, key, 0, &nb);
- switch (ev) {
- case EvKey:
- advance(in, nb);
- break;
- case EvAgain:
- peekkey(in, key, 1, &nb);
- /* get nb but don't advance */
- break;
- default:
- ;
- }
- return ev;
-}
-
-enum KeyEvent
-demandkey(Input *in, Key *key)
-{
- ulong nb;
- enum KeyEvent ev;
-
- ev = peekkey(in, key, 1, &nb);
- if (ev == EvKey)
- advance(in, nb);
-
- return ev;
-}
-
-enum KeyEvent
-isreadablekey(Input *in)
-{
- int n;
- if (in->fd == -1) {
- errno = EBADF;
- return EvErr;
- }
-
- /* reset to beginning of buffer */
- if (in->buf.b > in->buf.bytes) {
- n = in->buf.b - in->buf.bytes;
- memmove(in->buf.bytes, in->buf.b, n);
- in->buf.b = in->buf.bytes;
- in->buf.c = in->buf.b + n;
- }
-
-read:
- n = read(in->fd, in->buf.c, in->buf.e-in->buf.c);
- if (n == -1) {
- if (errno == EAGAIN)
- return EvNil;
- if (errno == EINTR && !(in->flags & FlagEintr))
- goto read;
- else
- return EvErr;
- }
- if (n < 1) {
- in->closed = 1;
- return EvNil;
- }
- in->buf.c += n;
- return EvAgain;
-}
-
-enum KeyEvent
-waitkey(Input *in, Key *key)
-{
- enum KeyEvent ev;
- struct pollfd p;
-
- if (in->fd == -1) {
- errno = EBADF;
- return EvErr;
- }
-
- for (;;) {
- ev = getkey(in, key);
- switch (ev) {
- case EvKey: case EvEOF: case EvErr:
- return ev;
- case EvNil:
- ev = isreadablekey(in);
- if (ev == EvErr)
- return ev;
- break;
-
- case EvAgain:
- /* can't wait any longer */
- if (in->closed)
- return demandkey(in, key);
- poll:
- p.fd = in->fd;
- p.events = POLLIN;
-
- if (poll(&p, 1, in->wait) == -1) {
- if (errno == EINTR && !(in->flags & FlagEintr))
- goto poll;
- return EvErr;
- }
-
- if (p.revents & (POLLIN|POLLHUP|POLLERR))
- ev = isreadablekey(in);
- else
- ev = EvNil;
-
- if (ev == EvErr)
- return ev;
- if (ev == EvNil)
- return demandkey(in, key);
- break;
- }
- }
- /* unreachable */
-}
-
-enum KeyEvent
-decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col)
-{
- if(key->type != KeyMouse)
- return EvNil;
-
- if(button)
- *button = 0;
-
- getpos(key, row, col);
-
- if(!ev)
- return EvKey;
-
- int btn = 0;
- int code = key->code.mouse[0];
- int drag = code & 0x20;
- code &= ~0x3c;
-
- switch(code) {
- case 0:
- case 1:
- case 2:
- *ev = drag ? MouseDrag : MousePress;
- btn = code + 1;
- break;
-
- case 3:
- *ev = MouseRelease;
- // no button hint
- break;
-
- case 64:
- case 65:
- *ev = drag ? MouseDrag : MousePress;
- btn = code + 4 - 64;
- break;
-
- default:
- *ev = MouseNil;
- }
-
- if (button)
- *button = btn;
-
- if (key->code.mouse[3] & 0x80)
- *ev = MouseRelease;
-
- return EvKey;
-}
-
-enum KeyEvent
-decodepos(Input *in, Key *key, int *row, int *col)
-{
- if (key->type != KeyPosition)
- return EvNil;
-
- getpos(key, row, col);
-
- return EvKey;
-}
-
-enum KeyEvent
-decodemode(Input *in, Key *key, int *init, int *mode, int *val)
-{
- if (key->type != KeyModeReport)
- return EvNil;
-
- if (init)
- *init = key->code.mouse[0];
-
- if (mode)
- *mode = (key->code.mouse[1] << 8) | key->code.mouse[2];
-
- if (val)
- *val = key->code.mouse[3];
-
- return EvKey;
-}
-
-char *
-keyname(Input *in, int sym)
-{
- if (sym == SymUnknown || sym >= in->nkeynm)
- return "<unknown>";
-
- return in->keynm[sym];
-}
-
-// -----------------------------------------------------------------------
-// main point of entry
-
-static
-void
-printkey(Input *in, Key *key)
-{
- enum MouseEvent ev;
- int button, line, col;
- int init, mode, val;
-
- switch(key->type) {
- case KeyUnicode:
- fprintf(stderr, "Unicode codepoint=U+%04dx utf8='%s'", key->code.pt, key->utf8);
- break;
- case KeyFunc:
- fprintf(stderr, "Function F%d", key->code.num);
- break;
- case KeySym:
- fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, keyname(in, key->code.sym));
- break;
- case KeyMouse:
- decodemouse(in, key, &ev, &button, &line, &col);
- fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col);
- break;
- case KeyPosition:
- decodepos(in, key, &line, &col);
- fprintf(stderr, "Position report pos=(%d,%d)\n", line, col);
- break;
- case KeyModeReport:
- decodemode(in, key, &init, &mode, &val);
- fprintf(stderr, "Mode report mode=%s %d val=%d\n", init == '?' ? "DEC" : "ANSI", mode, val);
- break;
- case KeyDCS:
- fprintf(stderr, "Device Control String");
- break;
- case KeyOSC:
- fprintf(stderr, "Operating System Control");
- break;
- case KeyUnknownCSI:
- fprintf(stderr, "unknown CSI\n");
- break;
- }
-
- mode = key->mods;
- fprintf(stderr, " mod=%s%s%s+%02x",
- (mode & ModCtrl ? "<C>" : ""),
- (mode & ModAlt ? "<A>" : ""),
- (mode & ModShift? "<S>" : ""),
- mode & ~(ModCtrl|ModAlt|ModShift));
-}
-
-int
-main()
-{
- char *name;
- Input *in;
- unibi_term *info;
- Key key;
- enum KeyEvent ev;
-
- name = getenv("TERM");
- info = unibi_from_term(name);
- in = makeinput(0, FlagSpaceSymbol, info);
-
- int n = 0; /* for debugging purposes only */
- startrecord(in);
- while ((ev = waitkey(in, &key)) != EvEOF) {
- switch (ev) {
- case EvKey:
- printkey(in, &key);
- printf("\n");
- break;
- case EvNil:
- printf("<nil>\n");
- case EvAgain:
- printf("<again>\n");
- default:
- ;
- }
- n++;
- if (n > 200)
- break;
- }
- stoprecord(in);
- freeinput(in);
-}
diff --git a/sys/libterm/input.c b/sys/libterm/input.c
deleted file mode 100644
index 038e8df..0000000
--- a/sys/libterm/input.c
+++ /dev/null
@@ -1,108 +0,0 @@
-#include <u.h>
-#include <libn.h>
-#include <unibilium.h>
-
-#define iota(x) 1 << (x)
-
-// -----------------------------------------------------------------------
-// enums
-
-enum
-{
- KeyRune, KeyFunc, KeySym,
- KeyMouse, KeyPosition, KeyModeReport,
- KeyDCS, KeyOSC, /* add other recognised types here */
- KeyUnknownCSI = -1
-};
-
-enum KeyEvent
-{
- EvNil, EvKey, EvEOF, EvAgain, EvErr,
-};
-
-enum
-{
- SymUnknown = -1,
- SymNone = 0,
-
- /* special names in c0 */
- SymBackspace, SymTab, SymEnter, SymEscape,
-
- /* special names in g0 */
- SymSpace, SymDel,
-
- /* special keys */
- SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert,
- SymDelete, SymSelect, SymPageUp, SymPageDown, SymHome, SymEnd,
-
- /* special keys from terminfo */
- SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit,
- SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions,
- SymPrint, SymRedo, SymReference, SymRefresh, SymReplace,
- SymRestart, SymResume, SymSave, SymSuspend, SymUndo,
-
- /* numeric keypad special keys */
- SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8,
- SymKp9, SymKpEnter, SymKpPlus, SymKpMinus, SymKpMul, SymKpDiv, SymKpComma,
- SymKpDot, SymKpEq,
-
- /* et cetera ad nauseum */
- NumSyms
-};
-
-enum MouseEvent
-{
- MouseNil, MousePress, MouseDrag, MouseRelease,
-};
-
-enum
-{
- ModShift = iota(0),
- ModAlt = iota(1),
- ModCtrl = iota(2),
-};
-
-// -----------------------------------------------------------------------
-// types
-
-typedef struct Input Input;
-
-struct Input
-{
- int fd;
- int wait; /* in msec */
- struct {
- char *b, *c, bytes[1024];
- } rbuf; /* read buffer */
- struct {
- char *b, *c, bytes[1024];
- } ebuf; /* escape buffer */
-};
-
-// -----------------------------------------------------------------------
-// globals
-
-// -----------------------------------------------------------------------
-// utility functions
-
-// -----------------------------------------------------------------------
-// implementation
-
-Input *
-makeinput(int fd, int flags, unibi_term *info)
-{
- int i;
- Input *in;
- char *e;
-
- if (!(in = calloc(1, sizeof(in))))
- panicf("out of memory");
-
- in->fd = fd;
- in->wait = 50; /* in msec */
-
- /* initialize buffers */
- in->rbuf.c = in->rbuf.b = in->rbuf.bytes;
-
- return in;
-}
diff --git a/sys/libterm/term.c b/sys/libterm/term.c
index cda2bbe..11591fc 100644
--- a/sys/libterm/term.c
+++ b/sys/libterm/term.c
@@ -1,5 +1,8 @@
#include "term.h"
+#include <signal.h>
+#include <sys/ioctl.h>
+
struct ExtraInfo
{
char *enteralt;
@@ -19,6 +22,8 @@ struct ExtraInfo vt200 =
.exitmouse = "\e[?1002l\e[?1006l",
};
+static Term *sigwinchhead;
+
// -----------------------------------------------------------------------
// database lookup
@@ -78,26 +83,9 @@ guessextrastr(Term *t, char *name, char *guess)
return guess;
}
-static
-int
-tryextrabool(Term *t, char *name)
-{
- const char *nm;
- size_t max = unibi_count_ext_bool(t->info);
- for (size_t i = 0; i < max; i++) {
- nm = unibi_get_ext_bool_name(t->info, i);
- if (nm && !strcmp(nm, name)) {
- return (int)i;
- }
- }
- return -1;
-}
-
/* formats escape strings and writes to output */
static void tfmt(Term *t, char *esc, int n, ...);
-
-// -----------------------------------------------------------------------
-// exported pen methods
+static void tclear(Term *t);
// -----------------------------------------------------------------------
// exported term methods
@@ -113,14 +101,25 @@ ttmpbuf(Term *t, int len)
return (t->tmp.b = realloc(t->tmp.b, len));
}
-void
-tinit(Term *t)
+void twrite(Term *t, long len, char *s);
+void tlistensigwinch(Term *t);
+
+Term*
+tmake(void)
{
+ Term *t;
+
+ t = calloc(1, sizeof(*t));
+
+ /* meta data */
t->name = getenv("TERM");
t->info = unibi_from_term(t->name);
if (!t->info)
panicf("could not identify terminal");
+ t->fd = 1; // stdout
+ tlistensigwinch(t);
+
t->mode.mouse = 0;
t->mode.cursorvis = 1;
t->mode.altscreen = 0;
@@ -128,13 +127,23 @@ tinit(Term *t)
t->cap.colors = unibi_get_num(t->info, unibi_max_colors);
t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase);
- /* TODO */
- t->input = nil;
- t->root = nil;
- t->pen = (Pen){0};
+ /* initialize root window (get current size)*/
+ struct winsize ws = { 0 };
+ if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
+ goto bad;
- /* fill in buffers */
- t->buf.c = t->buf.b;
+ t->root = wmake(nil, 0, 0, ws.ws_col, ws.ws_row, 0);
+
+ t->root->curvis = 1;
+ t->root->blink = 0;
+
+ t->pen = (Pen){
+ .state = PenNormal,
+ .col = {.fg = -1, .bg = -1},
+ };
+
+ /* fill in output buffers */
+ t->buf.c = t->buf.b;
t->tmp.b = nil;
t->tmp.len = 0;
@@ -174,22 +183,16 @@ tinit(Term *t)
t->esc.ext.rgbf = guessextrastr(t, "setrgbf", "\x1b[38;2;%p1%d;%p2%d;%p3%dm");
t->esc.ext.rgbb = guessextrastr(t, "setrgbb", "\x1b[48;2;%p1%d;%p2%d;%p3%dm");
- /* acs characters */
- t->acs.vline = tryinfostr(t, unibi_acs_vline);
- t->acs.hline = tryinfostr(t, unibi_acs_hline);
- t->acs.plus = tryinfostr(t, unibi_acs_plus);
- t->acs.ltee = tryinfostr(t, unibi_acs_ltee);
- t->acs.rtee = tryinfostr(t, unibi_acs_rtee);
- t->acs.ttee = tryinfostr(t, unibi_acs_ttee);
- t->acs.btee = tryinfostr(t, unibi_acs_btee);
- t->acs.ulcorner = tryinfostr(t, unibi_acs_ulcorner);
- t->acs.urcorner = tryinfostr(t, unibi_acs_urcorner);
- t->acs.llcorner = tryinfostr(t, unibi_acs_llcorner);
- t->acs.lrcorner = tryinfostr(t, unibi_acs_lrcorner);
+ return t;
+
+bad:
+ panicf("failed to initialize terminal instance");
+ free(t);
+ return nil;
}
void
-tfini(Term *t)
+tfree(Term *t)
{
if (t->mode.mouse)
twrite(t, 0, vt200.exitmouse);
@@ -199,6 +202,56 @@ tfini(Term *t)
twrite(t, 0, vt200.exitalt);
tfmt(t, t->esc.sgr0, 0);
+ tclear(t);
+ free(t);
+}
+
+/* handle resize events */
+void
+tresize(Term *t)
+{
+ if (t->fd == -1)
+ return;
+
+ struct winsize ws = { 0 };
+ if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1)
+ return;
+
+ printf("[%d,%d]\n", ws.ws_col, ws.ws_row);
+ if (t->root->w != ws.ws_col || t->root->h != ws.ws_row)
+ wresize(t->root, ws.ws_col, ws.ws_row);
+}
+
+static
+void
+sigwinch(int num)
+{
+ Term *it;
+ for (it = sigwinchhead; it; it = it->link)
+ tresize(it);
+}
+
+void
+tlistensigwinch(Term *t)
+{
+ sigset_t new, old;
+ Term *it;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGWINCH);
+ sigprocmask(SIG_BLOCK, &new, &old);
+
+ if (!sigwinchhead) {
+ sigaction(SIGWINCH, &(struct sigaction){ .sa_handler = sigwinch }, nil);
+ sigwinchhead = t;
+ } else {
+ it = sigwinchhead;
+ while (it->link)
+ it = it->link;
+ it->link = t;
+ }
+
+ sigprocmask(SIG_SETMASK, &old, nil);
}
void
@@ -217,12 +270,14 @@ twrite(Term *t, long len, char *s)
if (!len)
len = strlen(s);
- while (len > 0) {
- n = MIN(len, arrend(t->buf.b) - t->buf.c);
- memcpy(t->buf.c, s, n);
- t->buf.c += n;
- len -= n;
+loop:
+ n = MIN(len, arrend(t->buf.b) - t->buf.c);
+ memcpy(t->buf.c, s, n);
+ t->buf.c += n;
+ len -= n;
+ if (len) {
tflush(t);
+ goto loop;
}
}
@@ -256,8 +311,8 @@ tsetpen(Term *t, Pen new)
/* fg/bg color */
/* TODO: add a check for if the terminal supports true color */
- /* TODO: add a check for negative indices */
- if (new.state & PenTrueClr) {
+ /* TODO: deal w/ negative indices properly */
+ if (new.state & PenRGB) {
tfmt(t, t->esc.ext.rgbf, 3, new.rgb.fg.r, new.rgb.fg.g, new.rgb.fg.b);
tfmt(t, t->esc.ext.rgbb, 3, new.rgb.bg.r, new.rgb.bg.g, new.rgb.bg.b);
} else {
@@ -282,12 +337,13 @@ tfmt(Term *t, char *esc, int n, ...)
panicf("no terminfo escape string given");
va_start(args, n);
- for (i = 0; i < arrlen(param) && i < n; i++)
+ for (i = 0; i < arrlen(param) && i < n; i++) {
param[i] = unibi_var_from_num(va_arg(args, int));
+ }
va_end(args);
len = unibi_run(esc, param, c, sizeof(buf));
- if (len < arrlen(buf)) {
+ if (len >= arrlen(buf)) {
c = ttmpbuf(t, len);
unibi_run(esc, param, c, len);
}
@@ -296,6 +352,7 @@ tfmt(Term *t, char *esc, int n, ...)
}
/* absolute move */
+static
int
tgoto(Term *t, int row, int col)
{
@@ -324,6 +381,7 @@ tgoto(Term *t, int row, int col)
}
/* relative move */
+static
void
tjump(Term *t, int down, int right)
{
@@ -346,50 +404,86 @@ tjump(Term *t, int down, int right)
tfmt(t, t->esc.cub, 1, -right);
}
+static
void
-tdel(Term *t, int num)
+tclear(Term *t)
{
- char *c, buf[64];
- if (num < 1)
- return;
-
- /* TODO: allow for not moving */
- if (t->cap.bce) {
- tfmt(t, t->esc.ech, 1, num);
- tjump(t, 0, num);
- } else {
- c = buf;
- memset(c, ' ', arrlen(buf));
- while(num > 64) {
- twrite(t, arrlen(buf), c);
- num -= arrlen(buf);
- }
- twrite(t, num, c);
- }
+ tfmt(t, t->esc.ed2, 0);
}
void
-tclear(Term *t)
+tblit(Term *t, Window *win)
{
- tfmt(t, t->esc.ed2, 0);
+ int r, c, n, j;
+ Row *row;
+ char u[UTFmax+1] = {0};
+
+ j = 0;
+ tgoto(t, win->top, win->left);
+ for (r = 0; r < win->h; r++) {
+ row = win->row + r;
+ if (!row->dirty) {
+ j++;
+ continue;
+ }
+
+ if (j) {
+ tjump(t, j, 0);
+ j = 0;
+ }
+
+ for (c = 0; c < win->w; c++) {
+ tsetpen(t, row->cells[c].pen);
+ n = utf8·runetobyte(u, &row->cells[c].txt);
+ twrite(t, n, u);
+ }
+
+ row->dirty = 0;
+ }
+
+ tflush(t);
}
// -----------------------------------------------------------------------
-// testing entry
+// testing
int
main()
{
- Term t;
- tinit(&t);
-
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_foreground), t.esc.sgr_fg);
- printf("%s: %s\n", unibi_short_name_str(unibi_set_a_background), t.esc.sgr_bg);
- tfmt(&t, t.esc.ext.rgbf, 3, 10, 10, 100);
- tfmt(&t, t.esc.ext.rgbb, 3, 100, 0, 100);
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, "hello world\n");
- twrite(&t, 0, t.acs.hline);
-
- tfini(&t);
+ int i;
+ Term *t;
+ Window *win;
+
+ t = tmake();
+ win = t->root;
+ tclear(t);
+
+ win->pen = (Pen){
+ .state = PenNormal,
+ .col = {.fg=-1, .bg=-1},
+ };
+ for (i = 0; i < 2000; i++)
+ wputrune(win, 'a');
+
+ tblit(t, win);
+
+ win->cur.row = 10;
+ win->cur.col = 0;
+
+ win->pen = (Pen){
+ .state=PenNormal|PenRGB,
+ .rgb={.fg={200, 100, 100}, .bg={0, 0, 0} },
+ };
+
+ for (i = 0; i < 500; i++)
+ wputrune(win, 'b');
+
+ tblit(t, win);
+
+ sleep(5);
+ wscroll(win, 10);
+ tblit(t, win);
+ sleep(5);
+
+ tfree(t);
}
diff --git a/sys/libterm/term.h b/sys/libterm/term.h
index acae95f..6bd2f6b 100644
--- a/sys/libterm/term.h
+++ b/sys/libterm/term.h
@@ -7,165 +7,11 @@
#include <unibilium.h>
#define iota(x) 1 << (x)
-/*
- * obtained from:
- * https://invisible-island.net/ncurses/man/curs_add_wch.3x.html
- */
-#define ACSRUNES \
- /* name utf8 ascii acsc*/ \
- ACSRUNE("block", 0x25ae, '#', '0') \
- ACSRUNE("board", 0x2592, '#', 'h') \
- ACSRUNE("btee", 0x2534, '+', 'v') \
- ACSRUNE("bullet", 0x00b7, 'o', '~') \
- ACSRUNE("ckboard", 0x2592, ':', 'a') \
- ACSRUNE("darrow", 0x2193, 'v', '.') \
- ACSRUNE("degree", 0x00b0, '\'','f') \
- ACSRUNE("diamond", 0x25c6, '+', '`') \
- ACSRUNE("gequal", 0x2265, '>', '>') \
- ACSRUNE("hline", 0x2500, '-', 'q') \
- ACSRUNE("antern", 0x2603, '#', 'i') \
- ACSRUNE("larrow", 0x2190, '<', ',') \
- ACSRUNE("lequal", 0x2264, '<', 'y') \
- ACSRUNE("llcorner", 0x2514, '+', 'm') \
- ACSRUNE("lrcorner", 0x2518, '+', 'j') \
- ACSRUNE("ltee", 0x2524, '+', 't') \
- ACSRUNE("nequal", 0x2260, '!', '|') \
- ACSRUNE("pi", 0x03c0, '*', '{') \
- ACSRUNE("plminus", 0x00b1, '#', 'g') \
- ACSRUNE("plus", 0x253c, '+', 'n') \
- ACSRUNE("rarrow", 0x2192, '>', '+') \
- ACSRUNE("rtee", 0x251c, '+', 'u') \
- ACSRUNE("s1", 0x23ba, '-', 'o') \
- ACSRUNE("s3", 0x23bb, '-', 'p') \
- ACSRUNE("s7", 0x23bc, '-', 'r') \
- ACSRUNE("s9", 0x23bd, '_', 's') \
- ACSRUNE("sterling", 0x00a3, 'f', '}') \
- ACSRUNE("ttee", 0x252c, '+', 'w') \
- ACSRUNE("uarrow", 0x2191, '^', '-') \
- ACSRUNE("ulcorner", 0x250c, '+', 'l') \
- ACSRUNE("urcorner", 0x2510, '+', 'k') \
- ACSRUNE("vline", 0x2502, '|', 'x') \
- /* thick versions */ \
- ACSRUNE("t_btee", 0x253b, '+', 'V') \
- ACSRUNE("t_hline", 0x2501, '-', 'Q') \
- ACSRUNE("t_llcorner", 0x2517, '+', 'M') \
- ACSRUNE("t_lrcorner", 0x251b, '+', 'J') \
- ACSRUNE("t_ltee", 0x252b, '+', 'T') \
- ACSRUNE("t_plus", 0x254b, '+', 'N') \
- ACSRUNE("t_rtee", 0x2523, '+', 'U') \
- ACSRUNE("t_ttee", 0x2533, '+', 'W') \
- ACSRUNE("t_ulcorner", 0x250f, '+', 'L') \
- ACSRUNE("t_urcorner", 0x2513, '+', 'K') \
- ACSRUNE("t_vline", 0x2503, '|', 'X') \
- /* double version */ \
- ACSRUNE("d_btee", 0x2569, '+', 'H') \
- ACSRUNE("d_hline", 0x2550, '-', 'R') \
- ACSRUNE("d_llcorner", 0x255a, '+', 'D') \
- ACSRUNE("d_lrcorner", 0x255d, '+', 'A') \
- ACSRUNE("d_ltee", 0x2560, '+', 'F') \
- ACSRUNE("d_plus", 0x256c, '+', 'E') \
- ACSRUNE("d_rtee", 0x2563, '+', 'G') \
- ACSRUNE("d_ttee", 0x2566, '+', 'I') \
- ACSRUNE("d_ulcorner", 0x2554, '+', 'C') \
- ACSRUNE("d_urcorner", 0x2557, '+', 'B') \
- ACSRUNE("d_vline", 0x2551, '|', 'Y')
-
-/* enums */
-
-/* key symbols */
-enum
-{
- SymUnknown = -1,
- SymNone = 0,
-
- /* special names in c0 */
- SymBackspace, SymTab, SymEnter, SymEscape,
-
- /* special names in g0 */
- SymSpace, SymDel,
-
- /* special keys */
- SymUp, SymDown, SymLeft, SymRight, SymBegin, SymFind, SymInsert,
- SymDelete, SymSelect, SymPageup, SymPagedown, SymHome, SymEnd,
-
- /* special keys from terminfo */
- SymCancel, SymClear, SymClose, SymCommand, SymCopy, SymExit,
- SymHelp, SymMark, SymMessage, SymMove, SymOpen, SymOptions,
- SymPrint, SymRedo, SymReference, SymRefresh, SymReplace,
- SymRestart, SymResume, SymSave, SymSuspend, SymUndo,
-
- /* numeric keypad special keys */
- SymKp0, SymKp1, SymKp2, SymKp3, SymKp4, SymKp5, SymKp6, SymKp7, SymKp8,
- SymKp9, SymKpenter, SymKpplus, SymKpminus, SymKpmult, SymKpdiv, SymKpcomma,
- SymKpperiod, SymKpequals,
-
- /* et cetera ad nauseum */
- NumSyms
-};
-
-/* key type */
-enum
-{
- KeyUnicode,
- KeyFunc,
- KeySym,
- KeyMouse,
- KeyPosition,
- KeyModeReport,
- KeyDCS,
- KeyOSC,
- /* add other recognised types here */
-
- KeyUnknownCSI = -1
-};
-
-/* key events */
-enum KeyEvent
-{
- EvNil,
- EvKey,
- EvEOF,
- EvAgain,
- EvErr,
-};
-
-enum MouseEvent
-{
- MouseNil,
- MousePress,
- MouseDrag,
- MouseRelease,
-};
-
-enum
-{
- ModShift = iota(0),
- ModAlt = iota(1),
- ModCtrl = iota(2),
-};
-
-enum
-{
- FlagNoInterpret = iota(0),
- FlagConvertKP = iota(1),
- FlagRaw = iota(2),
- FlagUTF8 = iota(3),
- FlagNoTermIOS = iota(4),
- FlagSpaceSymbol = iota(5),
- FlagCtrlC = iota(6),
- FlagEintr = iota(7),
-};
-
-enum
-{
- HarmonizeSpace = iota(0),
- HarmonizeDelBS = iota(1),
-};
-/* types */
typedef struct RGB8 RGB8;
typedef struct Pen Pen;
+typedef struct Dot Dot;
typedef struct Cell Cell;
typedef struct Row Row;
typedef struct Buffer Buffer;
@@ -194,7 +40,7 @@ enum
PenUnderline = iota(6),
PenBlink = iota(7),
/* ... */
- PenTrueClr = iota(15),
+ PenRGB = iota(15),
};
struct Pen
@@ -213,38 +59,29 @@ struct Pen
};
/* outputs */
-struct Cell {
- rune r;
+struct Cell
+{
+ rune txt;
Pen pen;
};
-struct Row {
+struct Row
+{
Cell *cells;
- uint dirty:1;
+ uint dirty : 1;
};
-/* Buffer holding the current window content (as an array) as well
- * as the scroll back buffer content (as a circular/ring buffer).
- *
- * If new content is added to terminal the view port slides down and the
- * previously top most line is moved into the scroll back buffer at postion
- * scroll.index. This index will eventually wrap around and thus overwrite
- * the oldest lines.
- *
- * In the scenerio below a scroll up has been performed. That is 'scroll.above'
- * lines still lie above the current view port. Further scrolling up will show
- * them. Similarly 'scroll.below' is the amount of lines below the current
- * viewport.
- *
- * The function buffer_boundary sets the row pointers to the start/end range
- * of the section delimiting the region before/after the viewport. The functions
- * buffer_row_{first,last} return the first/last logical row. And
- * buffer_row_{next,prev} allows to iterate over the logical lines in either
- * direction.
+struct Dot
+{
+ int row, col;
+};
+
+/*
+ * scroll.top & scroll.bot are pointers into the viewport.
*
* scroll back buffer
*
- * scroll_buf->+----------------+-----+
+ * scroll.buf->+----------------+-----+
* | | | ^ \
* | before | | | |
* current terminal content | viewport | | | |
@@ -253,39 +90,48 @@ struct Row {
* ^ | | i | \ | | i | c |
* | | | n | \ | | n | r |
* | | v | \ | | v | o |
- * r | | i | \ | | i | l /
- * o | viewport | s | >|<- scroll.index | s | l \
- * w | | i | / | | i | |
- * s | | b | / | after | b | s > scroll.below
+ * | | i | \ | | i | l /
+ * | buffer | s | >|<- scroll.index | s | l \
+ * h | | i | / | | i | |
+ * | | b | / | after | b | s > scroll.below
* | | l | / | viewport | l | i |
* v | | e | / | | e | z /
* +----------------+-----+/ | unused | | e
- * <- maxcols -> | scroll back | |
- * <- cols -> | buffer | | |
+ * <- maxw -> | scroll back | |
+ * <- w -> | buffer | | |
* | | | |
* | | | v
- * roll_buf + scroll.size->+----------------+-----+
- * <- maxcols ->
- * <- cols ->
+ * scroll.buf + scroll.size->+----------------+-----+
+ * <- maxw ->
+ * <- w ->
*/
-struct Buffer {
- Row *row; /* array of Row pointers of size 'rows' */
- Row *crow; /* row on which the cursor currently resides */
- bool *tabs; /* a boolean flag for each column whether it is a tab */
+struct Buffer
+{
+ int w, h; /* dimension of buffer */
+ Pen pen; /* default attributes */
+ int maxw; /* allocated cells (maximal cols over time) */
+ Row *row; /* array of row pointers of size 'h' */
struct {
- Row *buf; /* a ring buffer holding the scroll back content */
- Row *top; /* row in lines where scrolling region starts */
- Row *bot; /* row in lines where scrolling region ends */
- int size; /* maximal capacity of scroll back buffer (in lines) */
- int index; /* current index into the ring buffer */
- int above; /* number of lines above current viewport */
- int below; /* number of lines below current viewport */
+ Row *buf;
+ Row *top;
+ Row *bot;
+ int size;
+ int index;
+ int above;
+ int below;
} scroll;
- Pen pen, spen;
- int nrow, ncol; /* current dimension of buffer */
- int maxcols; /* allocated cells (maximal cols over time) */
- int srow, scol; /* saved cursor row/column (zero based) */
+ Dot cur, save; /* cursor position within buffer */
+};
+
+struct Window
+{
+ struct Buffer;
+ int top, left;
+ uchar curvis : 1;
+ uchar blink : 2;
+
+ Window *parent, *child, *link;
};
/* input */
@@ -310,61 +156,57 @@ struct KeyInfo
int modset;
};
-/* make opaque? */
struct Input
{
int fd;
int flags;
- int hflag;
int wait; /* in ms */
+ /* modifiers */
+ uchar closed : 1;
+ uchar started : 1;
+ uchar hasold : 1;
+
struct termios oldterm;
/* buffer */
struct {
long off;
uchar *b, *c, *e, bytes[256];
- } buf;
-
- /* modifiers */
- char closed : 1;
- char started : 1;
- char hasold : 1;
+ } rbuf;
+ struct {
+ uchar *s, bytes[256];
+ } ebuf;
/* key data */
Node *keys;
struct KeyInfo c0[32];
-
- char **keynm;
- int nkeynm;
-
- int nsavedcsi;
- char *savedcsi;
};
+
struct Term
{
/* meta data */
char *name;
unibi_term *info;
struct {
- uint altscreen : 1;
- uint cursorvis : 1;
- uint mouse : 1;
+ uchar altscreen : 1;
+ uchar cursorvis : 1;
+ uchar mouse : 1;
} mode;
struct {
- uint bce : 1;
- int colors;
+ uchar bce : 1;
+ int colors;
} cap;
/* input capture */
- Input *input;
+ Input input;
/* output display */
Window *root;
Pen pen;
- /* output raw text */
+ /* raw text to pty */
int fd;
struct {
char *c, b[512];
@@ -416,74 +258,13 @@ struct Term
} ext;
} esc;
- /* basic shapes */
- struct {
- rune block;
- rune board;
- rune hline;
- rune vline;
- rune plus;
- rune ltee;
- rune rtee;
- rune ttee;
- rune btee;
- rune ulcorner;
- rune urcorner;
- rune llcorner;
- rune lrcorner;
- } acs;
+ Term *link;
};
-/*
- * exported functions
- */
+/* functions */
+void tresize(Term *t);
-#if 0
-/* buffer.c */
-void zero(Row *row, int start, int len);
-void roll(Row *start, Row *end, int count);
-
-void bclear(Buffer *b);
-void bfree(Buffer *b);
-void bscroll(Buffer *b, int s);
-void bresize(Buffer *b, int rows, int cols);
-bool binit(Buffer *b, int rows, int cols, int size);
-void brender(Buffer *b, Term *t);
-void bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) ;
-Row *browfirst(Buffer *b);
-Row *browlast(Buffer *b);
-Row *brownext(Buffer *b, Row *row);
-Row *bprevrow(Buffer *b, Row *row);
-
-/* input.c */
-Input *makeinput(int fd, int flags, unibi_term *info);
-void freeinput(Input *in);
-int startrecord(Input *in);
-int stoprecord(Input *in);
-char *keyname(Input *in, int sym);
-
-enum KeyEvent term·waitkey(Input *in, Key *key); /* block until next keypress */
-enum KeyEvent term·getkey(Input *in, Key *key); /* grab key if we can */
-enum KeyEvent term·demandkey(Input *in, Key *key); /* grab now and interpret as best we can */
-
-/* unpack key event into useful data */
-enum KeyEvent decodemouse(Input *in, Key *key, enum MouseEvent *ev, int *button, int *row, int *col);
-enum KeyEvent decodepos(Input *in, Key *key, int *row, int *col);
-enum KeyEvent decodemode(Input *in, Key *key, int *init, int *mode, int *val);
-#endif
-
-/* term.c */
-void term·init(Term *t);
-void term·fini(Term *t);
-void term·flush(Term *t);
-void term·write(Term *t, long len, char *s);
-int term·goto(Term *t, int row, int col);
-void term·jump(Term *t, int down, int right);
-void term·del(Term *t, int num);
-void term·setpen(Term *t, Pen pen);
-void term·clear(Term *t);
-
-/* input.c */
-enum KeyEvent term·waitkey(Term *t, Key *key); /* block until next keypress */
-enum KeyEvent term·getkey(Term *t, Key *key); /* grab key if we can */
-enum KeyEvent term·demandkey(Term *t, Key *key); /* grab now and interpret as best we can */
+Window *wmake(Window *root, int top, int left, int w, int h, int scroll);
+void wresize(Window *root, int w, int h);
+void wputrune(Window *win, rune r);
+void wscroll(Window *win, int s);
diff --git a/sys/libterm/window.c b/sys/libterm/window.c
index fec3997..5d36c8b 100644
--- a/sys/libterm/window.c
+++ b/sys/libterm/window.c
@@ -1,44 +1,408 @@
-#include <u.h>
-#include <libn.h>
-
#include "term.h"
-typedef struct Rect Rect;
+// -----------------------------------------------------------------------
+// buffers
-struct Rect
+static
+void
+zero(Row *row, int start, int len)
{
- int top, left, rows, cols;
-};
+ int i;
+ Cell cell = {
+ .txt = L' ',
+ .pen = {
+ .state = PenNormal,
+ .col.fg = -1,
+ .col.bg = -1,
+ },
+ };
+
+ for (i = start; i < len + start; i++)
+ row->cells[i] = cell;
+ row->dirty = 1;
+}
-struct Window
+static
+void
+roll(Row *start, Row *end, int count)
{
- Buffer buffer[2], *buf;
- Rect area; /* on screen */
- Pen pen, spen; /* current and saved pen */
- uint curvis : 1;
- uint damage : 1;
-};
+ int n = end - start;
-/* functions */
+ /* enforce circularity */
+ count %= n;
+ if (count < 0)
+ count += n;
-Window *
-makewindow(Window *root, Rect area, int history)
+ if (count) {
+ char buf[count * sizeof(Row)]; /* XXX: remove VLA */
+ memcpy(buf, start, count * sizeof(Row));
+ memmove(start, start + count, (n - count) * sizeof(Row));
+ memcpy(end - count, buf, count * sizeof(Row));
+
+ for (Row *row = start; row < end; row++)
+ row->dirty = 1;
+ }
+}
+
+/* buffer operations */
+static
+void
+bclear(Buffer *b)
{
- Window *w;
- w = calloc(1, sizeof(*w));
- if (!w)
- panicf("out of memory");
+ int i;
+ Cell cell = {
+ .txt = L' ',
+ .pen = {
+ .state = PenNormal,
+ .col.fg = -1,
+ .col.bg = -1,
+ },
+ };
+
+ for (i = 0; i < b->h; i++) {
+ Row *row = b->row + i;
+ for (int j = 0; j < b->w; j++) {
+ row->cells[j] = cell;
+ row->dirty = 1;
+ }
+ }
+}
- w->pen = (Pen) {
- .state = PenNormal,
- .col = {-1, -1},
- };
+static
+void
+bfini(Buffer *b)
+{
+ int i;
- if (!binit(w->buffer+0, area.rows, area.cols, history) ||
- !binit(w->buffer+1, area.rows, area.cols, 0)) {
- free(w);
- return nil;
+ for (i = 0; i < b->h; i++)
+ free(b->row[i].cells);
+
+ free(b->row);
+
+ if (b->scroll.size) {
+ for (i = 0; i < b->scroll.size; i++)
+ free(b->scroll.buf[i].cells);
+
+ free(b->scroll.buf);
+ }
+}
+
+static
+void
+bscroll(Buffer *b, int s)
+{
+ Row tmp;
+ int i, ssz = b->scroll.bot - b->scroll.top;
+
+ /* work in quanta of screen size */
+ if (s > ssz) {
+ bscroll(b, ssz);
+ bscroll(b, s - ssz);
+ return;
+ }
+ if (s < -ssz) {
+ bscroll(b, -ssz);
+ bscroll(b, s + ssz);
+ return;
+ }
+
+ b->scroll.above += s;
+ b->scroll.above = CLAMP(b->scroll.above, 0, b->scroll.size);
+
+ if (s > 0) {
+ if (b->scroll.size) {
+ for (i = 0; i < s; i++) {
+ tmp = b->scroll.top[i];
+ b->scroll.top[i] = b->scroll.buf[b->scroll.index];
+ b->scroll.buf[b->scroll.index] = tmp;
+
+ b->scroll.index++;
+ if (b->scroll.index == b->scroll.size)
+ b->scroll.index = 0;
+ }
+ } else
+ for (i = 0; i < s; i++)
+ zero(b->scroll.top+i, 0, b->maxw);
+ }
+
+ roll(b->scroll.top, b->scroll.bot, s);
+
+ if (s < 0) {
+ if (b->scroll.size) {
+ for (i = (-s) - 1; i >= 0; i--) {
+ b->scroll.index--;
+ if (b->scroll.index == -1)
+ b->scroll.index = b->scroll.size - 1;
+
+ tmp = b->scroll.top[i];
+
+ b->scroll.top[i] = b->scroll.buf[b->scroll.index];
+ b->scroll.buf[b->scroll.index] = tmp;
+ b->scroll.top[i].dirty = 1;
+ }
+ } else
+ for (i = (-s) - 1; i >= 0; i--)
+ zero(b->scroll.top+i, 0, b->maxw);
+ }
+}
+
+static
+void
+bresize(Buffer *b, int nrow, int ncol)
+{
+ int r, d;
+ Row *row = b->row;
+ Row *cur = row + b->cur.row;
+
+ if (b->h != nrow) {
+ /* scroll if we can */
+ if (cur >= row + nrow)
+ bscroll(b, b->cur.row - nrow + 1);
+ while (b->h > nrow) {
+ free(row[b->h - 1].cells);
+ b->h--;
+ }
+
+ row = realloc(row, sizeof(Row) * nrow);
+ }
+
+ if (b->maxw < ncol) {
+ /* expand each row */
+ for (r = 0; r < b->h; r++) {
+ row[r].cells = realloc(row[r].cells, sizeof(Cell) * ncol);
+ if (b->h < ncol)
+ zero(row + r, b->w, ncol - b->w);
+ row[r].dirty = 1;
+ }
+ /* expand the scroll buffer */
+ Row *sbuf = b->scroll.buf;
+ for (r = 0; r < b->scroll.size; r++) {
+ sbuf[r].cells = realloc(sbuf[r].cells, sizeof(Cell) * ncol);
+ if (b->w < ncol)
+ zero(sbuf + r, b->w, ncol - b->w);
+ }
+ b->maxw = b->w = ncol;
+ } else if (b->w != ncol) {
+ for (r = 0; r < b->h; r++)
+ row[r].dirty = 1;
+ b->w = ncol;
+ }
+
+ d = 0;
+ if (b->h < nrow) {
+ while (b->h < nrow) {
+ row[b->h].cells = calloc(b->maxw, sizeof(Cell));
+ zero(row + b->h, 0, b->maxw);
+ b->h++;
+ }
+
+ /* prepare for backfill */
+ if (cur >= b->scroll.bot - 1) {
+ d = b->row + nrow - cur - 1;
+ if (d > b->scroll.above)
+ d = b->scroll.above;
+ }
}
- w->buf = w->buffer;
- return w;
+
+ b->cur.row += row - b->row;
+ b->scroll.top = row;
+ b->scroll.bot = row + nrow;
+ b->row = row;
+
+ /* perform backfill */
+ if (d > 0) {
+ bscroll(b, -d);
+ b->cur.row += d;
+ }
+}
+
+static
+bool
+binit(Buffer *b, int cols, int rows, int scroll)
+{
+ int size;
+
+ b->pen.state = PenNormal;
+ b->pen.col.fg = b->pen.col.bg = -1;
+
+ size = MAX(scroll, 0);
+ if (size && !(b->scroll.buf = calloc(size, sizeof(Row))))
+ return false;
+
+ b->scroll.size = size;
+ bresize(b, rows, cols);
+
+ b->cur = (Dot){0};
+ b->save = b->cur;
+
+ return true;
+}
+
+static
+void
+bboundary(Buffer *b, Row **bs, Row **be, Row **as, Row **ae)
+{
+ if (bs)
+ *bs = nil;
+ if (be)
+ *be = nil;
+ if (as)
+ *as = nil;
+ if (ae)
+ *ae = nil;
+ if (!b->scroll.size)
+ return;
+
+ if (b->scroll.above) {
+ if (bs)
+ *bs = &b->scroll.buf[(b->scroll.index - b->scroll.above + b->scroll.size) % b->scroll.size];
+ if (be)
+ *be = &b->scroll.buf[(b->scroll.index-1 + b->scroll.size) % b->scroll.size];
+ }
+ if (b->scroll.below) {
+ if (as)
+ *as = &b->scroll.buf[b->scroll.index];
+ if (ae)
+ *ae = &b->scroll.buf[(b->scroll.index + b->scroll.below-1) % b->scroll.size];
+ }
+}
+
+static
+Row *
+browfirst(Buffer *b)
+{
+ Row *bstart;
+ if (!b->scroll.size || !b->scroll.above)
+ return b->row;
+ bboundary(b, &bstart, nil, nil, nil);
+ return bstart;
+}
+
+static
+Row *
+browlast(Buffer *b)
+{
+ Row *aend;
+ if (!b->scroll.size || !b->scroll.below)
+ return b->row + b->h - 1;
+ bboundary(b, nil, nil, nil, &aend);
+ return aend;
+}
+
+static
+Row *
+brownext(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->h - 1;
+
+ if (!row)
+ return nil;
+
+ bboundary(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row >= first && row < last)
+ return ++row;
+ if (row == last)
+ return after_start;
+ if (row == before_end)
+ return first;
+ if (row == after_end)
+ return nil;
+ if (row == &b->scroll.buf[b->scroll.size - 1])
+ return b->scroll.buf;
+ return ++row;
+}
+
+static
+Row *
+bprevrow(Buffer *b, Row *row)
+{
+ Row *before_start, *before_end, *after_start, *after_end;
+ Row *first = b->row, *last = b->row + b->h - 1;
+
+ if (!row)
+ return nil;
+
+ bboundary(b, &before_start, &before_end, &after_start, &after_end);
+
+ if (row > first && row <= last)
+ return --row;
+ if (row == first)
+ return before_end;
+ if (row == before_start)
+ return nil;
+ if (row == after_start)
+ return last;
+ if (row == b->scroll.buf)
+ return &b->scroll.buf[b->scroll.size - 1];
+ return --row;
+}
+
+// -----------------------------------------------------------------------
+// windows
+
+Window *
+wmake(Window *root, int top, int left, int w, int h, int scroll)
+{
+ Window *child, *it;
+
+ child = calloc(1, sizeof(*child));
+ child->top = top;
+ child->left = left;
+ child->parent = root;
+ if (root) {
+ if (root->child) {
+ for (it = root->child; it->link != nil; it = it->link)
+ ;
+ it->link = child;
+ } else
+ root->child = child;
+
+ child->curvis = root->curvis;
+ child->blink = root->blink;
+ }
+
+ if (!binit((Buffer*)child, w, h, scroll)) {
+ free(child);
+ return nil;
+ }
+
+ return child;
+}
+
+void
+wfree(Window *win)
+{
+ free(win);
+}
+
+void
+wresize(Window *win, int w, int h)
+{
+ bresize((Buffer*)win, w, h);
+}
+
+/* TODO: more sophisticated damage tracking */
+void
+wputrune(Window *win, rune r)
+{
+ Row *row = win->row + win->cur.row;
+ Cell *cell = row->cells + win->cur.col;
+
+ cell->pen = win->pen;
+ cell->txt = r;
+
+ if (win->cur.col++ >= win->w) {
+ win->cur.col = 0;
+ if (win->cur.row++ >= win->h)
+ win->cur.row = win->h-1;
+ }
+ row->dirty = 1;
+}
+
+void
+wscroll(Window *win, int s)
+{
+ bscroll((Buffer*)win, s);
}
diff --git a/sys/rules.mk b/sys/rules.mk
index fa33346..a98e84c 100644
--- a/sys/rules.mk
+++ b/sys/rules.mk
@@ -11,13 +11,13 @@ include $(DIR)/rules.mk
DIR := $(d)/libn
include $(DIR)/rules.mk
+# DIR := $(d)/libterm
+# include $(DIR)/rules.mk
+
DIR := $(d)/libmath
include $(DIR)/rules.mk
DIR := $(d)/libbio
include $(DIR)/rules.mk
-DIR := $(d)/libgpu
-include $(DIR)/rules.mk
-
include share/pop.mk