From ce05175372a9ddca1a225db0765ace1127a39293 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Fri, 12 Nov 2021 09:22:01 -0800 Subject: chore: simplified organizational structure --- .gitignore | 17 +- Makefile | 4 +- bin/gentags | 18 - bin/initmk | 49 - bin/updatedirs | 21 +- rules.mk | 21 +- share/paths.mk | 19 +- src/base/arg.c | 71 + src/base/bufio/dump.c | 66 + src/base/bufio/get.c | 17 + src/base/bufio/internal.h | 4 + src/base/bufio/read.c | 36 + src/base/bufio/reader.c | 28 + src/base/bufio/refill.h | 28 + src/base/bufio/rules.mk | 5 + src/base/bufio/unget.c | 18 + src/base/coro/coro.c | 43 + src/base/coro/internal.h | 15 + src/base/coro/rules.mk | 3 + src/base/coro/unix_x64.s | 113 + src/base/error/errorf.c | 13 + src/base/error/exits.c | 11 + src/base/error/internal.h | 3 + src/base/error/panicf.c | 16 + src/base/error/rules.mk | 6 + src/base/error/verrorf.c | 9 + src/base/error/vpanicf.c | 11 + src/base/flate/internal.h | 39 + src/base/flate/read.c | 41 + src/base/flate/reader.c | 59 + src/base/flate/rules.mk | 6 + src/base/flate/write.c | 48 + src/base/flate/writer.c | 57 + src/base/fs/internal.h | 18 + src/base/fs/rules.mk | 3 + src/base/fs/walk.c | 119 + src/base/fs/walker.c | 39 + src/base/gz/flush.c | 7 + src/base/gz/get.c | 17 + src/base/gz/interface.c | 12 + src/base/gz/internal.h | 6 + src/base/gz/open.c | 13 + src/base/gz/printf.c | 15 + src/base/gz/put.c | 7 + src/base/gz/putstring.c | 8 + src/base/gz/read.c | 16 + src/base/gz/rules.mk | 11 + src/base/gz/seek.c | 7 + src/base/gz/write.c | 7 + src/base/io/fd.c | 7 + src/base/io/flush.c | 7 + src/base/io/get.c | 7 + src/base/io/interface.c | 70 + src/base/io/internal.h | 4 + src/base/io/open.c | 13 + src/base/io/putbyte.c | 7 + src/base/io/putstring.c | 7 + src/base/io/read.c | 7 + src/base/io/readln.c | 12 + src/base/io/rules.mk | 14 + src/base/io/seek.c | 7 + src/base/io/stat.c | 7 + src/base/io/tell.c | 7 + src/base/io/unget.c | 7 + src/base/io/write.c | 7 + src/base/mem/arena.c | 119 + src/base/mem/buffer.c | 45 + src/base/mem/interface.c | 36 + src/base/mem/internal.h | 4 + src/base/mem/rules.mk | 5 + src/base/mem/set64.c | 13 + src/base/mmap/internal.h | 5 + src/base/mmap/mmap.c | 39 + src/base/mmap/rules.mk | 2 + src/base/os/basename.c | 10 + src/base/os/exists.c | 7 + src/base/os/internal.h | 4 + src/base/os/rules.mk | 4 + src/base/os/sep.c | 14 + src/base/rng/base.c | 24 + src/base/rng/bernoulli.c | 7 + src/base/rng/exponential.c | 11 + src/base/rng/internal.h | 19 + src/base/rng/normal.c | 77 + src/base/rng/poisson.c | 126 + src/base/rng/random.c | 33 + src/base/rng/rules.mk | 7 + src/base/rules.mk | 37 + src/base/sort/double.c | 12 + src/base/sort/float.c | 12 + src/base/sort/int.c | 12 + src/base/sort/int16.c | 12 + src/base/sort/int32.c | 12 + src/base/sort/int64.c | 12 + src/base/sort/int8.c | 12 + src/base/sort/internal.h | 5 + src/base/sort/rules.mk | 14 + src/base/sort/string.c | 12 + src/base/sort/uint.c | 12 + src/base/sort/uint16.c | 12 + src/base/sort/uint32.c | 12 + src/base/sort/uint64.c | 12 + src/base/sort/uint8.c | 12 + src/base/string/append.c | 53 + src/base/string/appendf.c | 31 + src/base/string/clear.c | 9 + src/base/string/copyn.c | 11 + src/base/string/equals.c | 12 + src/base/string/find.c | 11 + src/base/string/fit.c | 20 + src/base/string/free.c | 8 + src/base/string/grow.c | 33 + src/base/string/internal.h | 12 + src/base/string/join.c | 16 + src/base/string/len.c | 17 + src/base/string/lower.c | 12 + src/base/string/make.c | 53 + src/base/string/makef.c | 25 + src/base/string/read.c | 12 + src/base/string/replace.c | 26 + src/base/string/rules.mk | 19 + src/base/string/split.c | 39 + src/base/string/upper.c | 12 + src/base/test.c | 170 ++ src/cmd/cc/ast.c | 2139 +++++++++++++++++ src/cmd/cc/bits.c | 114 + src/cmd/cc/cc.c | 409 ++++ src/cmd/cc/cc.h | 806 +++++++ src/cmd/cc/lex.c | 873 +++++++ src/cmd/cc/pp.c | 1125 +++++++++ src/cmd/cc/rules.mk | 21 + src/cmd/cc/scratch.c | 7 + src/cmd/cc/util.c | 21 + src/cmd/dwm/LICENSE | 37 + src/cmd/dwm/client.c | 657 ++++++ src/cmd/dwm/config.h | 141 ++ src/cmd/dwm/drw.c | 376 +++ src/cmd/dwm/dwm.c | 1185 ++++++++++ src/cmd/dwm/dwm.h | 384 ++++ src/cmd/dwm/hook.c | 489 ++++ src/cmd/dwm/rules.mk | 29 + src/cmd/dwm/util.c | 66 + src/cmd/filter/filter.c | 104 + src/cmd/filter/rules.mk | 14 + src/cmd/ic/LICENSE | 23 + src/cmd/ic/ic.1 | 100 + src/cmd/ic/ic.c | 878 +++++++ src/cmd/ic/rules.mk | 14 + src/cmd/ic/strlcpy.c | 32 + src/cmd/menu/LICENSE | 30 + src/cmd/menu/config.h | 25 + src/cmd/menu/drw.c | 428 ++++ src/cmd/menu/drw.h | 57 + src/cmd/menu/menu.c | 765 +++++++ src/cmd/menu/menu.h | 40 + src/cmd/menu/rules.mk | 27 + src/cmd/menu/util.c | 30 + src/cmd/rc/code.c | 277 +++ src/cmd/rc/exec.c | 1267 ++++++++++ src/cmd/rc/exec.h | 47 + src/cmd/rc/input.c | 1679 ++++++++++++++ src/cmd/rc/io.c | 437 ++++ src/cmd/rc/job.c | 91 + src/cmd/rc/lex.c | 394 ++++ src/cmd/rc/main.c | 66 + src/cmd/rc/parse.c | 2059 +++++++++++++++++ src/cmd/rc/parse.h | 141 ++ src/cmd/rc/prompt.c | 36 + src/cmd/rc/rc.h | 263 +++ src/cmd/rc/rules.mk | 32 + src/cmd/rc/syntax.y | 147 ++ src/cmd/rc/sys.c | 137 ++ src/cmd/rc/tree.c | 111 + src/cmd/rc/util.c | 65 + src/cmd/rc/var.c | 336 +++ src/cmd/rc/wait.c | 247 ++ src/cmd/rules.mk | 32 + src/cmd/term/LICENSE | 34 + src/cmd/term/config.h | 474 ++++ src/cmd/term/hb.c | 147 ++ src/cmd/term/nonspacing.h | 89 + src/cmd/term/rules.mk | 26 + src/cmd/term/term.c | 2417 ++++++++++++++++++++ src/cmd/term/term.h | 316 +++ src/cmd/term/term.info | 250 ++ src/cmd/term/util.c | 30 + src/cmd/term/wide.h | 65 + src/cmd/term/x.c | 2070 +++++++++++++++++ src/cmd/walk/rules.mk | 15 + src/cmd/walk/walk.c | 84 + src/cmd/wm/arg.c | 0 src/cmd/wm/client.c | 274 +++ src/cmd/wm/config.h | 70 + src/cmd/wm/input.c | 316 +++ src/cmd/wm/layer.c | 107 + src/cmd/wm/main.c | 177 ++ src/cmd/wm/monitor.c | 386 ++++ src/cmd/wm/protocol/sync | 6 + .../wm/protocol/wlr-layer-shell-unstable-v1.xml | 390 ++++ src/cmd/wm/render.c | 160 ++ src/cmd/wm/rules.mk | 62 + src/cmd/wm/util.c | 99 + src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.c | 93 + src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.h | 564 +++++ src/cmd/wm/wm.h | 350 +++ src/cmd/wm/xdg-shell-protocol.c | 181 ++ src/cmd/wm/xdg-shell-protocol.h | 1676 ++++++++++++++ src/cmd/wm/xdg.c | 118 + src/libbio/align.c | 178 ++ src/libbio/fasta.c | 393 ++++ src/libbio/newick.c | 414 ++++ src/libbio/phylo.c | 427 ++++ src/libbio/rules.mk | 24 + src/libbio/simulate.c | 120 + src/libbio/test.c | 283 +++ src/libc/rules.mk | 20 + src/libc/stdio.c | 59 + src/libc/string.c | 80 + src/libfmt/buffer.c | 60 + src/libfmt/do.c | 730 ++++++ src/libfmt/esprint.c | 14 + src/libfmt/float.c | 1077 +++++++++ src/libfmt/fprint.c | 14 + src/libfmt/internal.h | 17 + src/libfmt/locale.c | 16 + src/libfmt/nsprint.c | 14 + src/libfmt/open.c | 34 + src/libfmt/print.c | 13 + src/libfmt/rules.mk | 35 + src/libfmt/sprint.c | 19 + src/libfmt/test.c | 72 + src/libfmt/vesprint.c | 26 + src/libfmt/vfprint.c | 19 + src/libfmt/vnsprint.c | 26 + src/libfmt/vprint.c | 19 + src/libfmt/vwrite.c | 26 + src/libfmt/write.c | 22 + src/libmath/basic.c | 531 +++++ src/libmath/blas.c | 63 + src/libmath/blas1.c | 58 + src/libmath/blas1body | 215 ++ src/libmath/blas2.c | 222 ++ src/libmath/blas2body | 256 +++ src/libmath/blas3.c | 279 +++ src/libmath/lapack.c | 0 src/libmath/linalg.c | 63 + src/libmath/loop.h | 114 + src/libmath/matrix.c | 176 ++ src/libmath/rules.mk | 27 + src/libmath/test.c | 471 ++++ src/libsre/lex.c | 246 ++ src/libsre/sre.h | 93 + src/libterm/term.c | 489 ++++ src/libterm/term.h | 270 +++ src/libterm/window.c | 408 ++++ src/libutf/canfit.c | 23 + src/libutf/decode.c | 98 + src/libutf/decodeprev.c | 60 + src/libutf/encode.c | 69 + src/libutf/find.c | 31 + src/libutf/findlast.c | 32 + src/libutf/internal.h | 38 + src/libutf/len.c | 21 + src/libutf/rules.mk | 76 + src/libutf/runelen.c | 8 + src/libutf/vendor/common.c | 220 ++ src/libutf/vendor/common.h | 46 + src/libutf/vendor/mkgraphemedata.c | 24 + src/libutf/vendor/mkrunetype.c | 388 ++++ src/libutf/vendor/mkrunewidth.c | 325 +++ src/nixos/rules.mk | 0 src/rules.mk | 23 + sys/base/arg.c | 71 - sys/base/bufio/dump.c | 66 - sys/base/bufio/get.c | 17 - sys/base/bufio/internal.h | 4 - sys/base/bufio/read.c | 36 - sys/base/bufio/reader.c | 28 - sys/base/bufio/refill.h | 28 - sys/base/bufio/rules.mk | 5 - sys/base/bufio/unget.c | 18 - sys/base/coro/coro.c | 43 - sys/base/coro/internal.h | 15 - sys/base/coro/rules.mk | 3 - sys/base/coro/unix_x64.s | 113 - sys/base/error/errorf.c | 13 - sys/base/error/exits.c | 11 - sys/base/error/internal.h | 3 - sys/base/error/panicf.c | 16 - sys/base/error/rules.mk | 6 - sys/base/error/verrorf.c | 9 - sys/base/error/vpanicf.c | 11 - sys/base/flate/internal.h | 39 - sys/base/flate/read.c | 41 - sys/base/flate/reader.c | 59 - sys/base/flate/rules.mk | 6 - sys/base/flate/write.c | 48 - sys/base/flate/writer.c | 57 - sys/base/fs/internal.h | 18 - sys/base/fs/rules.mk | 3 - sys/base/fs/walk.c | 119 - sys/base/fs/walker.c | 39 - sys/base/gz/flush.c | 7 - sys/base/gz/get.c | 17 - sys/base/gz/interface.c | 12 - sys/base/gz/internal.h | 6 - sys/base/gz/open.c | 13 - sys/base/gz/printf.c | 15 - sys/base/gz/put.c | 7 - sys/base/gz/putstring.c | 8 - sys/base/gz/read.c | 16 - sys/base/gz/rules.mk | 11 - sys/base/gz/seek.c | 7 - sys/base/gz/write.c | 7 - sys/base/io/fd.c | 7 - sys/base/io/flush.c | 7 - sys/base/io/get.c | 7 - sys/base/io/interface.c | 70 - sys/base/io/internal.h | 4 - sys/base/io/open.c | 13 - sys/base/io/putbyte.c | 7 - sys/base/io/putstring.c | 7 - sys/base/io/read.c | 7 - sys/base/io/readln.c | 12 - sys/base/io/rules.mk | 14 - sys/base/io/seek.c | 7 - sys/base/io/stat.c | 7 - sys/base/io/tell.c | 7 - sys/base/io/unget.c | 7 - sys/base/io/write.c | 7 - sys/base/mem/arena.c | 119 - sys/base/mem/buffer.c | 45 - sys/base/mem/interface.c | 36 - sys/base/mem/internal.h | 4 - sys/base/mem/rules.mk | 5 - sys/base/mem/set64.c | 13 - sys/base/mmap/internal.h | 5 - sys/base/mmap/mmap.c | 39 - sys/base/mmap/rules.mk | 2 - sys/base/os/basename.c | 10 - sys/base/os/exists.c | 7 - sys/base/os/internal.h | 4 - sys/base/os/rules.mk | 4 - sys/base/os/sep.c | 14 - sys/base/rng/base.c | 24 - sys/base/rng/bernoulli.c | 7 - sys/base/rng/exponential.c | 11 - sys/base/rng/internal.h | 19 - sys/base/rng/normal.c | 77 - sys/base/rng/poisson.c | 126 - sys/base/rng/random.c | 33 - sys/base/rng/rules.mk | 7 - sys/base/rules.mk | 36 - sys/base/sort/double.c | 12 - sys/base/sort/float.c | 12 - sys/base/sort/int.c | 12 - sys/base/sort/int16.c | 12 - sys/base/sort/int32.c | 12 - sys/base/sort/int64.c | 12 - sys/base/sort/int8.c | 12 - sys/base/sort/internal.h | 5 - sys/base/sort/rules.mk | 14 - sys/base/sort/string.c | 12 - sys/base/sort/uint.c | 12 - sys/base/sort/uint16.c | 12 - sys/base/sort/uint32.c | 12 - sys/base/sort/uint64.c | 12 - sys/base/sort/uint8.c | 12 - sys/base/string/append.c | 53 - sys/base/string/appendf.c | 31 - sys/base/string/clear.c | 9 - sys/base/string/copyn.c | 11 - sys/base/string/equals.c | 12 - sys/base/string/find.c | 11 - sys/base/string/fit.c | 20 - sys/base/string/free.c | 8 - sys/base/string/grow.c | 33 - sys/base/string/internal.h | 12 - sys/base/string/join.c | 16 - sys/base/string/len.c | 17 - sys/base/string/lower.c | 12 - sys/base/string/make.c | 53 - sys/base/string/makef.c | 25 - sys/base/string/read.c | 12 - sys/base/string/replace.c | 26 - sys/base/string/rules.mk | 19 - sys/base/string/split.c | 39 - sys/base/string/upper.c | 12 - sys/base/test.c | 170 -- sys/cmd/cc/ast.c | 2139 ----------------- sys/cmd/cc/bits.c | 114 - sys/cmd/cc/cc.c | 409 ---- sys/cmd/cc/cc.h | 806 ------- sys/cmd/cc/lex.c | 873 ------- sys/cmd/cc/pp.c | 1125 --------- sys/cmd/cc/rules.mk | 23 - sys/cmd/cc/scratch.c | 7 - sys/cmd/cc/util.c | 21 - sys/cmd/dwm/LICENSE | 37 - sys/cmd/dwm/client.c | 657 ------ sys/cmd/dwm/config.h | 141 -- sys/cmd/dwm/drw.c | 376 --- sys/cmd/dwm/dwm.c | 1185 ---------- sys/cmd/dwm/dwm.h | 384 ---- sys/cmd/dwm/hook.c | 489 ---- sys/cmd/dwm/rules.mk | 28 - sys/cmd/dwm/util.c | 66 - sys/cmd/filter/filter.c | 104 - sys/cmd/filter/rules.mk | 13 - sys/cmd/ic/LICENSE | 23 - sys/cmd/ic/ic.1 | 100 - sys/cmd/ic/ic.c | 878 ------- sys/cmd/ic/rules.mk | 14 - sys/cmd/ic/strlcpy.c | 32 - sys/cmd/menu/LICENSE | 30 - sys/cmd/menu/config.h | 25 - sys/cmd/menu/drw.c | 428 ---- sys/cmd/menu/drw.h | 57 - sys/cmd/menu/menu.c | 765 ------- sys/cmd/menu/menu.h | 40 - sys/cmd/menu/rules.mk | 25 - sys/cmd/menu/util.c | 30 - sys/cmd/rc/code.c | 277 --- sys/cmd/rc/exec.c | 1267 ---------- sys/cmd/rc/exec.h | 47 - sys/cmd/rc/input.c | 1679 -------------- sys/cmd/rc/io.c | 437 ---- sys/cmd/rc/job.c | 91 - sys/cmd/rc/lex.c | 394 ---- sys/cmd/rc/main.c | 66 - sys/cmd/rc/parse.c | 2059 ----------------- sys/cmd/rc/parse.h | 141 -- sys/cmd/rc/prompt.c | 36 - sys/cmd/rc/rc.h | 263 --- sys/cmd/rc/rules.mk | 31 - sys/cmd/rc/syntax.y | 147 -- sys/cmd/rc/sys.c | 137 -- sys/cmd/rc/tree.c | 111 - sys/cmd/rc/util.c | 65 - sys/cmd/rc/var.c | 336 --- sys/cmd/rc/wait.c | 247 -- sys/cmd/rules.mk | 38 - sys/cmd/term/LICENSE | 34 - sys/cmd/term/config.h | 474 ---- sys/cmd/term/hb.c | 147 -- sys/cmd/term/nonspacing.h | 89 - sys/cmd/term/rules.mk | 24 - sys/cmd/term/term.c | 2417 -------------------- sys/cmd/term/term.h | 316 --- sys/cmd/term/term.info | 250 -- sys/cmd/term/util.c | 30 - sys/cmd/term/wide.h | 65 - sys/cmd/term/x.c | 2070 ----------------- sys/cmd/walk/rules.mk | 13 - sys/cmd/walk/walk.c | 84 - sys/cmd/wm/arg.c | 0 sys/cmd/wm/client.c | 274 --- sys/cmd/wm/config.h | 70 - sys/cmd/wm/input.c | 316 --- sys/cmd/wm/layer.c | 107 - sys/cmd/wm/main.c | 177 -- sys/cmd/wm/monitor.c | 386 ---- sys/cmd/wm/protocol/sync | 6 - sys/cmd/wm/render.c | 160 -- sys/cmd/wm/rules.mk | 61 - sys/cmd/wm/util.c | 99 - sys/cmd/wm/wm.h | 350 --- sys/cmd/wm/xdg.c | 118 - sys/libbio/align.c | 178 -- sys/libbio/fasta.c | 393 ---- sys/libbio/newick.c | 414 ---- sys/libbio/phylo.c | 427 ---- sys/libbio/rules.mk | 28 - sys/libbio/simulate.c | 120 - sys/libbio/test.c | 283 --- sys/libc/rules.mk | 23 - sys/libc/stdio.c | 59 - sys/libc/string.c | 80 - sys/libfmt/buffer.c | 60 - sys/libfmt/do.c | 730 ------ sys/libfmt/esprint.c | 14 - sys/libfmt/float.c | 1077 --------- sys/libfmt/fprint.c | 14 - sys/libfmt/internal.h | 17 - sys/libfmt/locale.c | 16 - sys/libfmt/nsprint.c | 14 - sys/libfmt/open.c | 34 - sys/libfmt/print.c | 13 - sys/libfmt/rules.mk | 36 - sys/libfmt/sprint.c | 19 - sys/libfmt/test.c | 72 - sys/libfmt/vesprint.c | 26 - sys/libfmt/vfprint.c | 19 - sys/libfmt/vnsprint.c | 26 - sys/libfmt/vprint.c | 19 - sys/libfmt/vwrite.c | 26 - sys/libfmt/write.c | 22 - sys/libmath/basic.c | 531 ----- sys/libmath/blas.c | 63 - sys/libmath/blas1.c | 58 - sys/libmath/blas1body | 215 -- sys/libmath/blas2.c | 222 -- sys/libmath/blas2body | 256 --- sys/libmath/blas3.c | 279 --- sys/libmath/lapack.c | 0 sys/libmath/linalg.c | 63 - sys/libmath/loop.h | 114 - sys/libmath/matrix.c | 176 -- sys/libmath/rules.mk | 24 - sys/libmath/test.c | 471 ---- sys/libsre/lex.c | 246 -- sys/libsre/sre.h | 93 - sys/libterm/term.c | 489 ---- sys/libterm/term.h | 270 --- sys/libterm/window.c | 408 ---- sys/libutf/canfit.c | 23 - sys/libutf/decode.c | 98 - sys/libutf/decodeprev.c | 60 - sys/libutf/encode.c | 69 - sys/libutf/find.c | 31 - sys/libutf/findlast.c | 32 - sys/libutf/internal.h | 38 - sys/libutf/len.c | 21 - sys/libutf/rules.mk | 76 - sys/libutf/runelen.c | 8 - sys/libutf/vendor/common.c | 220 -- sys/libutf/vendor/common.h | 46 - sys/libutf/vendor/mkgraphemedata.c | 24 - sys/libutf/vendor/mkrunetype.c | 388 ---- sys/libutf/vendor/mkrunewidth.c | 325 --- sys/nixos/rules.mk | 0 sys/rules.mk | 41 - 532 files changed, 42791 insertions(+), 39984 deletions(-) delete mode 100755 bin/gentags delete mode 100755 bin/initmk create mode 100644 src/base/arg.c create mode 100644 src/base/bufio/dump.c create mode 100644 src/base/bufio/get.c create mode 100644 src/base/bufio/internal.h create mode 100644 src/base/bufio/read.c create mode 100644 src/base/bufio/reader.c create mode 100644 src/base/bufio/refill.h create mode 100644 src/base/bufio/rules.mk create mode 100644 src/base/bufio/unget.c create mode 100644 src/base/coro/coro.c create mode 100644 src/base/coro/internal.h create mode 100644 src/base/coro/rules.mk create mode 100644 src/base/coro/unix_x64.s create mode 100644 src/base/error/errorf.c create mode 100644 src/base/error/exits.c create mode 100644 src/base/error/internal.h create mode 100644 src/base/error/panicf.c create mode 100644 src/base/error/rules.mk create mode 100644 src/base/error/verrorf.c create mode 100644 src/base/error/vpanicf.c create mode 100644 src/base/flate/internal.h create mode 100644 src/base/flate/read.c create mode 100644 src/base/flate/reader.c create mode 100644 src/base/flate/rules.mk create mode 100644 src/base/flate/write.c create mode 100644 src/base/flate/writer.c create mode 100644 src/base/fs/internal.h create mode 100644 src/base/fs/rules.mk create mode 100644 src/base/fs/walk.c create mode 100644 src/base/fs/walker.c create mode 100644 src/base/gz/flush.c create mode 100644 src/base/gz/get.c create mode 100644 src/base/gz/interface.c create mode 100644 src/base/gz/internal.h create mode 100644 src/base/gz/open.c create mode 100644 src/base/gz/printf.c create mode 100644 src/base/gz/put.c create mode 100644 src/base/gz/putstring.c create mode 100644 src/base/gz/read.c create mode 100644 src/base/gz/rules.mk create mode 100644 src/base/gz/seek.c create mode 100644 src/base/gz/write.c create mode 100644 src/base/io/fd.c create mode 100644 src/base/io/flush.c create mode 100644 src/base/io/get.c create mode 100644 src/base/io/interface.c create mode 100644 src/base/io/internal.h create mode 100644 src/base/io/open.c create mode 100644 src/base/io/putbyte.c create mode 100644 src/base/io/putstring.c create mode 100644 src/base/io/read.c create mode 100644 src/base/io/readln.c create mode 100644 src/base/io/rules.mk create mode 100644 src/base/io/seek.c create mode 100644 src/base/io/stat.c create mode 100644 src/base/io/tell.c create mode 100644 src/base/io/unget.c create mode 100644 src/base/io/write.c create mode 100644 src/base/mem/arena.c create mode 100644 src/base/mem/buffer.c create mode 100644 src/base/mem/interface.c create mode 100644 src/base/mem/internal.h create mode 100644 src/base/mem/rules.mk create mode 100644 src/base/mem/set64.c create mode 100644 src/base/mmap/internal.h create mode 100644 src/base/mmap/mmap.c create mode 100644 src/base/mmap/rules.mk create mode 100644 src/base/os/basename.c create mode 100644 src/base/os/exists.c create mode 100644 src/base/os/internal.h create mode 100644 src/base/os/rules.mk create mode 100644 src/base/os/sep.c create mode 100644 src/base/rng/base.c create mode 100644 src/base/rng/bernoulli.c create mode 100644 src/base/rng/exponential.c create mode 100644 src/base/rng/internal.h create mode 100644 src/base/rng/normal.c create mode 100644 src/base/rng/poisson.c create mode 100644 src/base/rng/random.c create mode 100644 src/base/rng/rules.mk create mode 100644 src/base/rules.mk create mode 100644 src/base/sort/double.c create mode 100644 src/base/sort/float.c create mode 100644 src/base/sort/int.c create mode 100644 src/base/sort/int16.c create mode 100644 src/base/sort/int32.c create mode 100644 src/base/sort/int64.c create mode 100644 src/base/sort/int8.c create mode 100644 src/base/sort/internal.h create mode 100644 src/base/sort/rules.mk create mode 100644 src/base/sort/string.c create mode 100644 src/base/sort/uint.c create mode 100644 src/base/sort/uint16.c create mode 100644 src/base/sort/uint32.c create mode 100644 src/base/sort/uint64.c create mode 100644 src/base/sort/uint8.c create mode 100644 src/base/string/append.c create mode 100644 src/base/string/appendf.c create mode 100644 src/base/string/clear.c create mode 100644 src/base/string/copyn.c create mode 100644 src/base/string/equals.c create mode 100644 src/base/string/find.c create mode 100644 src/base/string/fit.c create mode 100644 src/base/string/free.c create mode 100644 src/base/string/grow.c create mode 100644 src/base/string/internal.h create mode 100644 src/base/string/join.c create mode 100644 src/base/string/len.c create mode 100644 src/base/string/lower.c create mode 100644 src/base/string/make.c create mode 100644 src/base/string/makef.c create mode 100644 src/base/string/read.c create mode 100644 src/base/string/replace.c create mode 100644 src/base/string/rules.mk create mode 100644 src/base/string/split.c create mode 100644 src/base/string/upper.c create mode 100644 src/base/test.c create mode 100644 src/cmd/cc/ast.c create mode 100644 src/cmd/cc/bits.c create mode 100644 src/cmd/cc/cc.c create mode 100644 src/cmd/cc/cc.h create mode 100644 src/cmd/cc/lex.c create mode 100644 src/cmd/cc/pp.c create mode 100644 src/cmd/cc/rules.mk create mode 100644 src/cmd/cc/scratch.c create mode 100644 src/cmd/cc/util.c create mode 100644 src/cmd/dwm/LICENSE create mode 100644 src/cmd/dwm/client.c create mode 100644 src/cmd/dwm/config.h create mode 100644 src/cmd/dwm/drw.c create mode 100644 src/cmd/dwm/dwm.c create mode 100644 src/cmd/dwm/dwm.h create mode 100644 src/cmd/dwm/hook.c create mode 100644 src/cmd/dwm/rules.mk create mode 100644 src/cmd/dwm/util.c create mode 100644 src/cmd/filter/filter.c create mode 100644 src/cmd/filter/rules.mk create mode 100644 src/cmd/ic/LICENSE create mode 100644 src/cmd/ic/ic.1 create mode 100644 src/cmd/ic/ic.c create mode 100644 src/cmd/ic/rules.mk create mode 100644 src/cmd/ic/strlcpy.c create mode 100644 src/cmd/menu/LICENSE create mode 100644 src/cmd/menu/config.h create mode 100644 src/cmd/menu/drw.c create mode 100644 src/cmd/menu/drw.h create mode 100644 src/cmd/menu/menu.c create mode 100644 src/cmd/menu/menu.h create mode 100644 src/cmd/menu/rules.mk create mode 100644 src/cmd/menu/util.c create mode 100644 src/cmd/rc/code.c create mode 100644 src/cmd/rc/exec.c create mode 100644 src/cmd/rc/exec.h create mode 100644 src/cmd/rc/input.c create mode 100644 src/cmd/rc/io.c create mode 100644 src/cmd/rc/job.c create mode 100644 src/cmd/rc/lex.c create mode 100644 src/cmd/rc/main.c create mode 100644 src/cmd/rc/parse.c create mode 100644 src/cmd/rc/parse.h create mode 100644 src/cmd/rc/prompt.c create mode 100644 src/cmd/rc/rc.h create mode 100644 src/cmd/rc/rules.mk create mode 100644 src/cmd/rc/syntax.y create mode 100644 src/cmd/rc/sys.c create mode 100644 src/cmd/rc/tree.c create mode 100644 src/cmd/rc/util.c create mode 100644 src/cmd/rc/var.c create mode 100644 src/cmd/rc/wait.c create mode 100644 src/cmd/rules.mk create mode 100644 src/cmd/term/LICENSE create mode 100644 src/cmd/term/config.h create mode 100644 src/cmd/term/hb.c create mode 100644 src/cmd/term/nonspacing.h create mode 100644 src/cmd/term/rules.mk create mode 100644 src/cmd/term/term.c create mode 100644 src/cmd/term/term.h create mode 100644 src/cmd/term/term.info create mode 100644 src/cmd/term/util.c create mode 100644 src/cmd/term/wide.h create mode 100644 src/cmd/term/x.c create mode 100644 src/cmd/walk/rules.mk create mode 100644 src/cmd/walk/walk.c create mode 100644 src/cmd/wm/arg.c create mode 100644 src/cmd/wm/client.c create mode 100644 src/cmd/wm/config.h create mode 100644 src/cmd/wm/input.c create mode 100644 src/cmd/wm/layer.c create mode 100644 src/cmd/wm/main.c create mode 100644 src/cmd/wm/monitor.c create mode 100755 src/cmd/wm/protocol/sync create mode 100644 src/cmd/wm/protocol/wlr-layer-shell-unstable-v1.xml create mode 100644 src/cmd/wm/render.c create mode 100644 src/cmd/wm/rules.mk create mode 100644 src/cmd/wm/util.c create mode 100644 src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.c create mode 100644 src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.h create mode 100644 src/cmd/wm/wm.h create mode 100644 src/cmd/wm/xdg-shell-protocol.c create mode 100644 src/cmd/wm/xdg-shell-protocol.h create mode 100644 src/cmd/wm/xdg.c create mode 100644 src/libbio/align.c create mode 100644 src/libbio/fasta.c create mode 100644 src/libbio/newick.c create mode 100644 src/libbio/phylo.c create mode 100644 src/libbio/rules.mk create mode 100644 src/libbio/simulate.c create mode 100644 src/libbio/test.c create mode 100644 src/libc/rules.mk create mode 100644 src/libc/stdio.c create mode 100644 src/libc/string.c create mode 100644 src/libfmt/buffer.c create mode 100644 src/libfmt/do.c create mode 100644 src/libfmt/esprint.c create mode 100644 src/libfmt/float.c create mode 100644 src/libfmt/fprint.c create mode 100644 src/libfmt/internal.h create mode 100644 src/libfmt/locale.c create mode 100644 src/libfmt/nsprint.c create mode 100644 src/libfmt/open.c create mode 100644 src/libfmt/print.c create mode 100644 src/libfmt/rules.mk create mode 100644 src/libfmt/sprint.c create mode 100644 src/libfmt/test.c create mode 100644 src/libfmt/vesprint.c create mode 100644 src/libfmt/vfprint.c create mode 100644 src/libfmt/vnsprint.c create mode 100644 src/libfmt/vprint.c create mode 100644 src/libfmt/vwrite.c create mode 100644 src/libfmt/write.c create mode 100644 src/libmath/basic.c create mode 100644 src/libmath/blas.c create mode 100644 src/libmath/blas1.c create mode 100644 src/libmath/blas1body create mode 100644 src/libmath/blas2.c create mode 100644 src/libmath/blas2body create mode 100644 src/libmath/blas3.c create mode 100644 src/libmath/lapack.c create mode 100644 src/libmath/linalg.c create mode 100644 src/libmath/loop.h create mode 100644 src/libmath/matrix.c create mode 100644 src/libmath/rules.mk create mode 100644 src/libmath/test.c create mode 100644 src/libsre/lex.c create mode 100644 src/libsre/sre.h create mode 100644 src/libterm/term.c create mode 100644 src/libterm/term.h create mode 100644 src/libterm/window.c create mode 100644 src/libutf/canfit.c create mode 100644 src/libutf/decode.c create mode 100644 src/libutf/decodeprev.c create mode 100644 src/libutf/encode.c create mode 100644 src/libutf/find.c create mode 100644 src/libutf/findlast.c create mode 100644 src/libutf/internal.h create mode 100644 src/libutf/len.c create mode 100644 src/libutf/rules.mk create mode 100644 src/libutf/runelen.c create mode 100644 src/libutf/vendor/common.c create mode 100644 src/libutf/vendor/common.h create mode 100644 src/libutf/vendor/mkgraphemedata.c create mode 100644 src/libutf/vendor/mkrunetype.c create mode 100644 src/libutf/vendor/mkrunewidth.c create mode 100644 src/nixos/rules.mk create mode 100644 src/rules.mk delete mode 100644 sys/base/arg.c delete mode 100644 sys/base/bufio/dump.c delete mode 100644 sys/base/bufio/get.c delete mode 100644 sys/base/bufio/internal.h delete mode 100644 sys/base/bufio/read.c delete mode 100644 sys/base/bufio/reader.c delete mode 100644 sys/base/bufio/refill.h delete mode 100644 sys/base/bufio/rules.mk delete mode 100644 sys/base/bufio/unget.c delete mode 100644 sys/base/coro/coro.c delete mode 100644 sys/base/coro/internal.h delete mode 100644 sys/base/coro/rules.mk delete mode 100644 sys/base/coro/unix_x64.s delete mode 100644 sys/base/error/errorf.c delete mode 100644 sys/base/error/exits.c delete mode 100644 sys/base/error/internal.h delete mode 100644 sys/base/error/panicf.c delete mode 100644 sys/base/error/rules.mk delete mode 100644 sys/base/error/verrorf.c delete mode 100644 sys/base/error/vpanicf.c delete mode 100644 sys/base/flate/internal.h delete mode 100644 sys/base/flate/read.c delete mode 100644 sys/base/flate/reader.c delete mode 100644 sys/base/flate/rules.mk delete mode 100644 sys/base/flate/write.c delete mode 100644 sys/base/flate/writer.c delete mode 100644 sys/base/fs/internal.h delete mode 100644 sys/base/fs/rules.mk delete mode 100644 sys/base/fs/walk.c delete mode 100644 sys/base/fs/walker.c delete mode 100644 sys/base/gz/flush.c delete mode 100644 sys/base/gz/get.c delete mode 100644 sys/base/gz/interface.c delete mode 100644 sys/base/gz/internal.h delete mode 100644 sys/base/gz/open.c delete mode 100644 sys/base/gz/printf.c delete mode 100644 sys/base/gz/put.c delete mode 100644 sys/base/gz/putstring.c delete mode 100644 sys/base/gz/read.c delete mode 100644 sys/base/gz/rules.mk delete mode 100644 sys/base/gz/seek.c delete mode 100644 sys/base/gz/write.c delete mode 100644 sys/base/io/fd.c delete mode 100644 sys/base/io/flush.c delete mode 100644 sys/base/io/get.c delete mode 100644 sys/base/io/interface.c delete mode 100644 sys/base/io/internal.h delete mode 100644 sys/base/io/open.c delete mode 100644 sys/base/io/putbyte.c delete mode 100644 sys/base/io/putstring.c delete mode 100644 sys/base/io/read.c delete mode 100644 sys/base/io/readln.c delete mode 100644 sys/base/io/rules.mk delete mode 100644 sys/base/io/seek.c delete mode 100644 sys/base/io/stat.c delete mode 100644 sys/base/io/tell.c delete mode 100644 sys/base/io/unget.c delete mode 100644 sys/base/io/write.c delete mode 100644 sys/base/mem/arena.c delete mode 100644 sys/base/mem/buffer.c delete mode 100644 sys/base/mem/interface.c delete mode 100644 sys/base/mem/internal.h delete mode 100644 sys/base/mem/rules.mk delete mode 100644 sys/base/mem/set64.c delete mode 100644 sys/base/mmap/internal.h delete mode 100644 sys/base/mmap/mmap.c delete mode 100644 sys/base/mmap/rules.mk delete mode 100644 sys/base/os/basename.c delete mode 100644 sys/base/os/exists.c delete mode 100644 sys/base/os/internal.h delete mode 100644 sys/base/os/rules.mk delete mode 100644 sys/base/os/sep.c delete mode 100644 sys/base/rng/base.c delete mode 100644 sys/base/rng/bernoulli.c delete mode 100644 sys/base/rng/exponential.c delete mode 100644 sys/base/rng/internal.h delete mode 100644 sys/base/rng/normal.c delete mode 100644 sys/base/rng/poisson.c delete mode 100644 sys/base/rng/random.c delete mode 100644 sys/base/rng/rules.mk delete mode 100644 sys/base/rules.mk delete mode 100644 sys/base/sort/double.c delete mode 100644 sys/base/sort/float.c delete mode 100644 sys/base/sort/int.c delete mode 100644 sys/base/sort/int16.c delete mode 100644 sys/base/sort/int32.c delete mode 100644 sys/base/sort/int64.c delete mode 100644 sys/base/sort/int8.c delete mode 100644 sys/base/sort/internal.h delete mode 100644 sys/base/sort/rules.mk delete mode 100644 sys/base/sort/string.c delete mode 100644 sys/base/sort/uint.c delete mode 100644 sys/base/sort/uint16.c delete mode 100644 sys/base/sort/uint32.c delete mode 100644 sys/base/sort/uint64.c delete mode 100644 sys/base/sort/uint8.c delete mode 100644 sys/base/string/append.c delete mode 100644 sys/base/string/appendf.c delete mode 100644 sys/base/string/clear.c delete mode 100644 sys/base/string/copyn.c delete mode 100644 sys/base/string/equals.c delete mode 100644 sys/base/string/find.c delete mode 100644 sys/base/string/fit.c delete mode 100644 sys/base/string/free.c delete mode 100644 sys/base/string/grow.c delete mode 100644 sys/base/string/internal.h delete mode 100644 sys/base/string/join.c delete mode 100644 sys/base/string/len.c delete mode 100644 sys/base/string/lower.c delete mode 100644 sys/base/string/make.c delete mode 100644 sys/base/string/makef.c delete mode 100644 sys/base/string/read.c delete mode 100644 sys/base/string/replace.c delete mode 100644 sys/base/string/rules.mk delete mode 100644 sys/base/string/split.c delete mode 100644 sys/base/string/upper.c delete mode 100644 sys/base/test.c delete mode 100644 sys/cmd/cc/ast.c delete mode 100644 sys/cmd/cc/bits.c delete mode 100644 sys/cmd/cc/cc.c delete mode 100644 sys/cmd/cc/cc.h delete mode 100644 sys/cmd/cc/lex.c delete mode 100644 sys/cmd/cc/pp.c delete mode 100644 sys/cmd/cc/rules.mk delete mode 100644 sys/cmd/cc/scratch.c delete mode 100644 sys/cmd/cc/util.c delete mode 100644 sys/cmd/dwm/LICENSE delete mode 100644 sys/cmd/dwm/client.c delete mode 100644 sys/cmd/dwm/config.h delete mode 100644 sys/cmd/dwm/drw.c delete mode 100644 sys/cmd/dwm/dwm.c delete mode 100644 sys/cmd/dwm/dwm.h delete mode 100644 sys/cmd/dwm/hook.c delete mode 100644 sys/cmd/dwm/rules.mk delete mode 100644 sys/cmd/dwm/util.c delete mode 100644 sys/cmd/filter/filter.c delete mode 100644 sys/cmd/filter/rules.mk delete mode 100644 sys/cmd/ic/LICENSE delete mode 100644 sys/cmd/ic/ic.1 delete mode 100644 sys/cmd/ic/ic.c delete mode 100644 sys/cmd/ic/rules.mk delete mode 100644 sys/cmd/ic/strlcpy.c delete mode 100644 sys/cmd/menu/LICENSE delete mode 100644 sys/cmd/menu/config.h delete mode 100644 sys/cmd/menu/drw.c delete mode 100644 sys/cmd/menu/drw.h delete mode 100644 sys/cmd/menu/menu.c delete mode 100644 sys/cmd/menu/menu.h delete mode 100644 sys/cmd/menu/rules.mk delete mode 100644 sys/cmd/menu/util.c delete mode 100644 sys/cmd/rc/code.c delete mode 100644 sys/cmd/rc/exec.c delete mode 100644 sys/cmd/rc/exec.h delete mode 100644 sys/cmd/rc/input.c delete mode 100644 sys/cmd/rc/io.c delete mode 100644 sys/cmd/rc/job.c delete mode 100644 sys/cmd/rc/lex.c delete mode 100644 sys/cmd/rc/main.c delete mode 100644 sys/cmd/rc/parse.c delete mode 100644 sys/cmd/rc/parse.h delete mode 100644 sys/cmd/rc/prompt.c delete mode 100644 sys/cmd/rc/rc.h delete mode 100644 sys/cmd/rc/rules.mk delete mode 100644 sys/cmd/rc/syntax.y delete mode 100644 sys/cmd/rc/sys.c delete mode 100644 sys/cmd/rc/tree.c delete mode 100644 sys/cmd/rc/util.c delete mode 100644 sys/cmd/rc/var.c delete mode 100644 sys/cmd/rc/wait.c delete mode 100644 sys/cmd/rules.mk delete mode 100644 sys/cmd/term/LICENSE delete mode 100644 sys/cmd/term/config.h delete mode 100644 sys/cmd/term/hb.c delete mode 100644 sys/cmd/term/nonspacing.h delete mode 100644 sys/cmd/term/rules.mk delete mode 100644 sys/cmd/term/term.c delete mode 100644 sys/cmd/term/term.h delete mode 100644 sys/cmd/term/term.info delete mode 100644 sys/cmd/term/util.c delete mode 100644 sys/cmd/term/wide.h delete mode 100644 sys/cmd/term/x.c delete mode 100644 sys/cmd/walk/rules.mk delete mode 100644 sys/cmd/walk/walk.c delete mode 100644 sys/cmd/wm/arg.c delete mode 100644 sys/cmd/wm/client.c delete mode 100644 sys/cmd/wm/config.h delete mode 100644 sys/cmd/wm/input.c delete mode 100644 sys/cmd/wm/layer.c delete mode 100644 sys/cmd/wm/main.c delete mode 100644 sys/cmd/wm/monitor.c delete mode 100755 sys/cmd/wm/protocol/sync delete mode 100644 sys/cmd/wm/render.c delete mode 100644 sys/cmd/wm/rules.mk delete mode 100644 sys/cmd/wm/util.c delete mode 100644 sys/cmd/wm/wm.h delete mode 100644 sys/cmd/wm/xdg.c delete mode 100644 sys/libbio/align.c delete mode 100644 sys/libbio/fasta.c delete mode 100644 sys/libbio/newick.c delete mode 100644 sys/libbio/phylo.c delete mode 100644 sys/libbio/rules.mk delete mode 100644 sys/libbio/simulate.c delete mode 100644 sys/libbio/test.c delete mode 100644 sys/libc/rules.mk delete mode 100644 sys/libc/stdio.c delete mode 100644 sys/libc/string.c delete mode 100644 sys/libfmt/buffer.c delete mode 100644 sys/libfmt/do.c delete mode 100644 sys/libfmt/esprint.c delete mode 100644 sys/libfmt/float.c delete mode 100644 sys/libfmt/fprint.c delete mode 100644 sys/libfmt/internal.h delete mode 100644 sys/libfmt/locale.c delete mode 100644 sys/libfmt/nsprint.c delete mode 100644 sys/libfmt/open.c delete mode 100644 sys/libfmt/print.c delete mode 100644 sys/libfmt/rules.mk delete mode 100644 sys/libfmt/sprint.c delete mode 100644 sys/libfmt/test.c delete mode 100644 sys/libfmt/vesprint.c delete mode 100644 sys/libfmt/vfprint.c delete mode 100644 sys/libfmt/vnsprint.c delete mode 100644 sys/libfmt/vprint.c delete mode 100644 sys/libfmt/vwrite.c delete mode 100644 sys/libfmt/write.c delete mode 100644 sys/libmath/basic.c delete mode 100644 sys/libmath/blas.c delete mode 100644 sys/libmath/blas1.c delete mode 100644 sys/libmath/blas1body delete mode 100644 sys/libmath/blas2.c delete mode 100644 sys/libmath/blas2body delete mode 100644 sys/libmath/blas3.c delete mode 100644 sys/libmath/lapack.c delete mode 100644 sys/libmath/linalg.c delete mode 100644 sys/libmath/loop.h delete mode 100644 sys/libmath/matrix.c delete mode 100644 sys/libmath/rules.mk delete mode 100644 sys/libmath/test.c delete mode 100644 sys/libsre/lex.c delete mode 100644 sys/libsre/sre.h delete mode 100644 sys/libterm/term.c delete mode 100644 sys/libterm/term.h delete mode 100644 sys/libterm/window.c delete mode 100644 sys/libutf/canfit.c delete mode 100644 sys/libutf/decode.c delete mode 100644 sys/libutf/decodeprev.c delete mode 100644 sys/libutf/encode.c delete mode 100644 sys/libutf/find.c delete mode 100644 sys/libutf/findlast.c delete mode 100644 sys/libutf/internal.h delete mode 100644 sys/libutf/len.c delete mode 100644 sys/libutf/rules.mk delete mode 100644 sys/libutf/runelen.c delete mode 100644 sys/libutf/vendor/common.c delete mode 100644 sys/libutf/vendor/common.h delete mode 100644 sys/libutf/vendor/mkgraphemedata.c delete mode 100644 sys/libutf/vendor/mkrunetype.c delete mode 100644 sys/libutf/vendor/mkrunewidth.c delete mode 100644 sys/nixos/rules.mk delete mode 100644 sys/rules.mk diff --git a/.gitignore b/.gitignore index 8fb80bc..daccce7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,14 @@ bin/ lib/ -src/ data/ share/ vendor/ test/ -.build/ -.test/ +obj/ .cache/ include/libc include/vendor -include/libdraw.h -include/libimage.h sys/cc sys/nixos @@ -20,14 +16,9 @@ sys/libdraw sys/libimage sys/libterm -sys/libutf/*-14.0.0.c - -sys/cmd/muc -sys/cmd/wm -sys/cmd/term2 - -bin/fasttree -bin/mafft +src/libutf/*-14.0.0.c .dep/ .clangd + +compile_commands.json diff --git a/Makefile b/Makefile index 668ccda..fbf1905 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,8 @@ BIN_DIR := bin SYS_DIR := sys LIB_DIR := lib SRC_DIR := src -OBJ_DIR := .build -TST_DIR := .test +OBJ_DIR := obj +TST_DIR := test # C runtime library CINIT := $(LIB_DIR)/crt/crt1.o $(LIB_DIR)/crt/x86_64/crti.o `gcc --print-file-name=crtbeginS.o` diff --git a/bin/gentags b/bin/gentags deleted file mode 100755 index d9019c9..0000000 --- a/bin/gentags +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# TAGS=".tag-files" - -# echo "finding files ..." -# ROOT=/home/nolln/root -# find $ROOT \ -# -path "$ROOT/sys/*.[chs]" -prune -o \ -# -path "$ROOT/vendor/musl/src/*.[chs]" -prune -o \ -# -path "$ROOT/include/*.h" \ -# -path "$ROOT/include/vendor/*.h" > "$TAGS" - -ctags -R -f .tags . - -# cscope -b -k -I include/vendor/libc -I include/ -i "$CSCOPE_DIR/files" - -# CSCOPE_DB="$ROOT/cscope.out" -# echo "exported CSCOPE_DB to: '$CSCOPE_DB'" diff --git a/bin/initmk b/bin/initmk deleted file mode 100755 index 2ea018f..0000000 --- a/bin/initmk +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/python - -import os -import sys - -NAME = "rules.mk" -TEMPLATE = """include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := -LIBS_$(d) := -BINS_$(d) := -TSTS_$(d) := - -include share/paths.mk - -# Local rules -# $(LIBS_$(d)) = TCFLAGS := -# $(LIBS_$(d)) = TCINCS := -# $(LIBS_$(d)) = TCLIBS := - -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -$(BINS_$(d)): $(OBJS_$(d)) - $(LINK) - -$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) - $(LINK) - -include share/pop.mk""" - -if __name__ == "__main__": - if len(sys.argv) == 2: - dir = sys.argv[1] - if not os.path.exists(dir): - raise ValueError(f"path '{dir}' does not exist") - path = f"{dir}/{NAME}" - elif len(sys.argv) > 2: - raise ValueError("only one argument is accepted") - else: - path = NAME - try: - with open(path, 'x') as makefile: - makefile.write(f"{TEMPLATE}\n") - except: - print("rules.mk already present", file=sys.stderr) - exit(1) diff --git a/bin/updatedirs b/bin/updatedirs index cde7a6b..b304251 100755 --- a/bin/updatedirs +++ b/bin/updatedirs @@ -2,10 +2,10 @@ import os ROOT = "/home/nolln/u" -SRCS = ["src", "sys"] -BUILD = ".build" -TEST = ".test" -IGNORED = ["build", "include", "lib", "bin", ".git", "vendor", "obj", "dep", ".generated"] +SRC = "src" +BUILD = "obj" +TEST = "test" +IGNORED = ["include", "lib", "bin", ".git", "vendor", "obj", ".dep"] if __name__ == "__main__": if not os.path.exists(BUILD): @@ -14,10 +14,9 @@ if __name__ == "__main__": if not os.path.exists(TEST): os.mkdir(TEST) - for SRC in SRCS: - 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}/{SRC}/{root[len(ROOT)+len(SRC)+2:]}" - if not os.path.exists(blddir): - os.mkdir(blddir) + 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/rules.mk b/rules.mk index d4c7bf9..f01c791 100644 --- a/rules.mk +++ b/rules.mk @@ -13,7 +13,7 @@ debug: CINIT := debug: CFINI := debug: targets -release: CFLAGS += -O3 -mtune=native -flto -ffast-math #-DNDEBUG +release: CFLAGS += -O2 -mtune=native -flto -ffast-math #-DNDEBUG release: targets # Targets & array of sources & intermediates @@ -23,17 +23,14 @@ DEPS := LIBS := BINS := -UNTS := +TEST := GENS := # Iterate through directory tree -DIR := sys +DIR := src include $(DIR)/rules.mk -# DIR := src -# include $(DIR)/rules.mk - # Generic rules %.a: %.o $(ARCHIVE) @@ -41,16 +38,16 @@ include $(DIR)/rules.mk %: %.o $(LINK) -$(OBJ_DIR)/%.o: %.c +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(COMPILE) -$(OBJ_DIR)/%.o: %.s +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.s $(ASSEMBLE) -$(OBJ_DIR)/%: %.c +$(OBJ_DIR)/%: $(SRC_DIR)%.c $(COMPLNK) -targets: $(LIBS) $(BINS) $(UNTS) +targets: $(LIBS) $(BINS) $(TEST) clean: @echo removing object files @@ -64,7 +61,7 @@ clean: @echo removing binaries @rm -f $(BINS) @echo removing unit tests - @rm -f $(UNTS) + @rm -f $(TEST) install: targets @echo installing executables @@ -76,7 +73,7 @@ install: targets install $(LIBS) $(LIB_DIR); \ fi @echo installing terminfo - @tic -sx sys/cmd/term/term.info + @tic -sx cmd/term/term.info database: compiledb make -j4 diff --git a/share/paths.mk b/share/paths.mk index 91a101f..371ab7a 100644 --- a/share/paths.mk +++ b/share/paths.mk @@ -1,22 +1,25 @@ OBJS_$(d) := $(filter %.o, $(SRCS_$(d):.c=.o)) OBJS_$(d) += $(filter %.o, $(SRCS_$(d):.s=.o)) -OBJS_$(d) := $(addprefix $(OBJ_DIR)/, $(OBJS_$(d))) +OBJS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(OBJS_$(d))) DEPS_$(d) := $(OBJS_$(d):.o=.d) -OBJS := $(OBJS) $(OBJS_$(d)) $(TOBJS_$(d)) +# Binary building + +OBJS += $(OBJS_$(d)) DEPS += $(DEPS_$(d)) -LIBS_$(d) := $(addprefix $(OBJ_DIR)/, $(LIBS_$(d))) +LIBS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(LIBS_$(d))) LIBS += $(LIBS_$(d)) -BINS_$(d) := $(addprefix $(OBJ_DIR)/, $(BINS_$(d))) +BINS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(BINS_$(d))) BINS += $(BINS_$(d)) # Testing infrastructure -TOBJS_$(d) := $(TSTS_$(d):.c=.o) -TOBJS_$(d) := $(addprefix $(OBJ_DIR)/, $(TOBJS_$(d))) +UNIT_$(d) := $(CHECK_$(d):.c=.o) +UNIT_$(d) := $(patsubst %(SRC_DIR)/%, $(OBJ_DIR)/%, $(UNIT_$(d))) +OBJS += $(UNIT_$(d)) -UNTS_$(d) := $(addprefix $(TST_DIR)/, $(TSTS_$(d):.c=)) -UNTS += $(UNTS_$(d)) +TEST_$(d) := $(patsubst $(SRC_DIR)/%, $(TEST_DIR)/%, $(TEST_$(d):.c=)) +TEST += $(TEST_$(d)) diff --git a/src/base/arg.c b/src/base/arg.c new file mode 100644 index 0000000..269043e --- /dev/null +++ b/src/base/arg.c @@ -0,0 +1,71 @@ +#include +#include + +// NOTE: this utf8 bit is copied from libunicode to remove the hard dependency just for ARG_BEGIN. + +#define UTFmax 4 +#define RuneSync 0x80u +#define RuneSelf 0x80u +#define RuneErr 0xFFFDu +#define RuneMax 0x10FFFFu +#define RuneMask 0x1FFFFFu + +#define Bit(i) (7-(i)) +/* N 0's preceded by i 1's e.g. T(Bit(2)) is 1100 0000 */ +#define Tbyte(i) (((1 << (Bit(i)+1))-1) ^ 0xFF) +/* 0000 0000 0000 0111 1111 1111 */ +#define RuneX(i) ((1 << (Bit(i) + ((i)-1)*Bitx))-1) +enum +{ + Bitx = Bit(1), + Tx = Tbyte(1), + Rune1 = (1 << (Bit(0)+0*Bitx)) - 1, + + Maskx = (1 << Bitx) - 1, /* 0011 1111 */ + Testx = Maskx ^ 0xff, /* 1100 0000 */ + + SurrogateMin = 0xD800, + SurrogateMax = 0xDFFF, + Bad = RuneErr, +}; + + +int +arg·bytetorune(uint32* r, byte* s) +{ + int c[4], i; + uint32 l; + + c[0] = *(ubyte*)(s); + if(c[0] < Tx) { + *r = c[0]; + return 1; + } + + l = c[0]; + for(i = 1; i < UTFmax; i++) { + c[i] = *(ubyte*)(s+i); + c[i] ^= Tx; + if (c[i] & Testx) goto bad; + + l = (l << Bitx) | c[i]; + if(c[0] < Tbyte(i + 2)) { + l &= RuneX(i + 1); + if (i == 1) { + if (c[0] < Tbyte(2) || l <= Rune1) + goto bad; + } else if (l <= RuneX(i) || l > RuneMax) + goto bad; + if (i == 2 && SurrogateMin <= l && l <= SurrogateMax) + goto bad; + + *r = l; + return i + 1; + } + } +bad: + *r = RuneErr; + return 1; +} + +char *argv0; diff --git a/src/base/bufio/dump.c b/src/base/bufio/dump.c new file mode 100644 index 0000000..0b527e2 --- /dev/null +++ b/src/base/bufio/dump.c @@ -0,0 +1,66 @@ +// ----------------------------------------------------------------------- +// reader + +#if 0 +rune +bufio·getrune(io·Buffer *buf) +{ + ubyte b; + int i; + byte str[UTFmax+1]; + rune r; + + // NOTE: I'm worried about the sign here... + b = bufio·getbyte(buf); + if (b < RuneSelf) { + buf->runesize = 1; + return b; + } + + i = 0; + str[i++] = b; + +nextbyte: + b = bufio·getbyte(buf); + if (b < 0) return b; + if (i >= arrlen(str)) return RuneErr; + str[i++] = b; + if (!utf8·fullrune(str, i)) + goto nextbyte; + + buf->runesize = utf8·bytetorune(&r, str); + if (r == RuneErr && b == 1) { + errorf("illegal UTF-8 sequence"); + for (; i >= 0; i--) + errorf("%s%.2x", i > 0 ? " " : "", *(ubyte*)(str+i)); + errorf("\n"); + + buf->runesize = 0; + } else + for (; i > buf->runesize; i--) + bufio·ungetbyte(buf, str[i]); + + return r; +} + +// TODO: Check that we are given the correct rune! +error +bufio·ungetrune(io·Buffer *buf, rune r) +{ + if (buf->state & bufio·rdr) { + errorf("attempted to unget on non-active reader"); + return bufio·err; + } + + if (buf->pos == buf->buf) { + errorf("attempted to unget past end of buffer"); + return bufio·err; + } + + buf->pos -= buf->runesize; + return 0; +} +#endif + +// ----------------------------------------------------------------------- +// writer diff --git a/src/base/bufio/get.c b/src/base/bufio/get.c new file mode 100644 index 0000000..9f10c88 --- /dev/null +++ b/src/base/bufio/get.c @@ -0,0 +1,17 @@ +#include "internal.h" +#include "refill.h" + +int +bufio·getbyte(io·Buffer *buf) +{ +getbyte: + if(buf->pos < buf->end) + return *buf->pos++; + + memmove(buf->buf, buf->end - bufio·ungets, bufio·ungets); + + if(refill(buf) <= 0) + return bufio·eof; + + goto getbyte; +} diff --git a/src/base/bufio/internal.h b/src/base/bufio/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/src/base/bufio/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/src/base/bufio/read.c b/src/base/bufio/read.c new file mode 100644 index 0000000..09a9f83 --- /dev/null +++ b/src/base/bufio/read.c @@ -0,0 +1,36 @@ +#include "internal.h" +#include "refill.h" + +int +bufio·read(io·Buffer *buf, int sz, int n, void *out) +{ + byte *wtr; + int nr, rem, diff; + + if(n == 0 || buf->state & bufio·end) + return bufio·err; + + assert(buf->state & bufio·rdr); + + wtr = out; + rem = n*sz; + + while(rem > 0){ + diff = buf->end - buf->pos; + nr = MIN(diff, rem); + if(!nr){ + if(buf->state & bufio·end) + break; + if(refill(buf) <= 0) + break; + + continue; + } + memmove(wtr, buf->pos, nr); + wtr += nr; + buf->pos += nr; + rem -= nr; + } + + return n - rem/sz; +} diff --git a/src/base/bufio/reader.c b/src/base/bufio/reader.c new file mode 100644 index 0000000..afdaf60 --- /dev/null +++ b/src/base/bufio/reader.c @@ -0,0 +1,28 @@ +#include "internal.h" + +error +bufio·initreader(io·Buffer *buf, io·Reader rdr, void *h) +{ + if (buf->state) { + errorf("attemped to initialize an active buffer, state is '%d'", buf->state); + return bufio·err; + } + buf->state = bufio·rdr; + buf->runesize = 0; + buf->h = h; + buf->rdr = rdr; + buf->beg = buf->buf + bufio·ungets; + buf->pos = buf->beg; + buf->end = buf->pos; + buf->size = bufio·size - bufio·ungets; + + return 0; +} + +void +bufio·finireader(io·Buffer *buf) +{ + buf->state = bufio·nil; + buf->runesize = 0; + buf->rdr = (io·Reader){ .read = nil }; +} diff --git a/src/base/bufio/refill.h b/src/base/bufio/refill.h new file mode 100644 index 0000000..41e357e --- /dev/null +++ b/src/base/bufio/refill.h @@ -0,0 +1,28 @@ +int +refill(io·Buffer *buf) +{ + int n; + + if(buf->state & bufio·end) + return bufio·err; + + memcpy(buf->buf, buf->pos - bufio·ungets, bufio·ungets); + + n = buf->rdr.read(buf->h, 1, buf->size, buf->beg); + if(n < 0) + return bufio·err; + if(n == 0){ + buf->state |= bufio·end; + return 0; + } + + buf->pos = buf->beg; + buf->end = buf->pos + n; + + // TEST: put a physical EOF byte at the end + // this would allow for an unget operation + if(n < buf->size) + *buf->end++ = EOF; + + return n; +} diff --git a/src/base/bufio/rules.mk b/src/base/bufio/rules.mk new file mode 100644 index 0000000..84f283f --- /dev/null +++ b/src/base/bufio/rules.mk @@ -0,0 +1,5 @@ +SRCS_$(d)+=\ + $(d)/bufio/get.c\ + $(d)/bufio/read.c\ + $(d)/bufio/reader.c\ + $(d)/bufio/unget.c\ diff --git a/src/base/bufio/unget.c b/src/base/bufio/unget.c new file mode 100644 index 0000000..3fd16de --- /dev/null +++ b/src/base/bufio/unget.c @@ -0,0 +1,18 @@ +#include "internal.h" + +error +bufio·ungetbyte(io·Buffer *buf, byte c) +{ + if(!(buf->state & bufio·rdr)) { + errorf("attempted to unget on non-active reader"); + return bufio·err; + } + + if(buf->pos == buf->buf) { + errorf("attempted to unget past end of buffer"); + return bufio·err; + } + + buf->pos--; + return 0; +} diff --git a/src/base/coro/coro.c b/src/base/coro/coro.c new file mode 100644 index 0000000..2255c99 --- /dev/null +++ b/src/base/coro/coro.c @@ -0,0 +1,43 @@ +#include "internal.h" + +/* Co-routine context */ +Coro* +coro·make(uintptr stk, uintptr (*func)(Coro*, uintptr)) +{ + if (!func) return nil; + if (stk == 0) stk = 8192; + + byte *block = malloc(stk); + Coro *co = (Coro*)&block[stk - sizeof(Coro)]; + co->bp = block; + co->size = stk; + + _newcoro(co, func, co); + return co; +} + +error +coro·free(Coro *co) +{ + enum + { + NIL, + GOOD, + EMPTY, + LOST, + }; + + if (!co) return NIL; + if (!co->bp) return LOST; + if (co->size == 0) return EMPTY; + + free(co->bp); + + return GOOD; +} + +uintptr +coro·yield(Coro *c, uintptr arg) +{ + return _coroyield(c, arg); +} diff --git a/src/base/coro/internal.h b/src/base/coro/internal.h new file mode 100644 index 0000000..f57d27b --- /dev/null +++ b/src/base/coro/internal.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +extern void _newcoro(Coro *co, uintptr (*func)(Coro*, uintptr), void *stk); +extern uintptr _coroyield(Coro *co, uintptr arg); + +struct Coro +{ + void *sp; + void *bp; + uintptr size; + void *user; +}; diff --git a/src/base/coro/rules.mk b/src/base/coro/rules.mk new file mode 100644 index 0000000..c2ee89f --- /dev/null +++ b/src/base/coro/rules.mk @@ -0,0 +1,3 @@ +SRCS_$(d)+=\ + $(d)/coro/coro.c\ + $(d)/coro/unix_x64.s\ diff --git a/src/base/coro/unix_x64.s b/src/base/coro/unix_x64.s new file mode 100644 index 0000000..d7de2a2 --- /dev/null +++ b/src/base/coro/unix_x64.s @@ -0,0 +1,113 @@ +; Nicholas Noll 2019 +; +; =================================================================== +%use altreg + + bits 64 + default rel + global _newcoro + global _coroyield + +; =================================================================== + section .text +; ------------------------------------------------------------------- + +%assign L.coro -8 +%assign L.func -16 + +coroinit: + mov R7, [RBP + L.coro] + mov R6, R0 + call [RBP + L.func] + +rerun: + mov R7, [RBP + L.coro] + mov R6, R0 + call _coroyield + jmp rerun + +; ------------------------------------------------------------------- +; # Register Mapping +; +; R0 R1 R2 R3 R4 R5 R6 R7 R8 ... +; RAX RCX RDX RBX RSP RBP RSI RDI R8 ... +; +; # Sys V calling convention +; func(R7, R6, R2, R1, R8, R9, Z0-7): R0 +; +; # Stack layout of an in-flight coro +; *coro +; *func +; *bp (base pointer of stack) +; ....... STACK ......... +; Saved Clobbers +; +; ### +; Stack layout of an init coro +; Stores the func pointer to init +; Stores the clobber registers. +; +; L.coro [8] +; L.func [7] +; coroinit [6] +; RBP [5] +; R3 [4] +; R12 [3] +; R13 [2] +; R14 [1] +; R15 [0] + +%define WORDSZ 8 +%define NSAVES 9 + +; coro *coro·new(co *coro, fn func, bp *stack) +_newcoro: + lea R0, [coroinit] ; Store address of init function + lea R1, [R2 - NSAVES*WORDSZ] ; Store offset address of stack + + mov [R1 + 8*WORDSZ], R7 ; Store context pointer + mov [R1 + 7*WORDSZ], R6 ; Store function pointer + mov [R1 + 6*WORDSZ], R0 ; Store initializer pointer + mov [R1 + 5*WORDSZ], R2 ; Store stack base pointer + + xor R0, R0 + + ; Start of mutable stack + ; Blank out the clobbers + mov [R1 + 4*WORDSZ], R0 ; R3 + mov [R1 + 3*WORDSZ], R0 ; R12 + mov [R1 + 2*WORDSZ], R0 ; R13 + mov [R1 + 1*WORDSZ], R0 ; R14 + mov [R1 + 0*WORDSZ], R0 ; R15 + + mov [R7], R1 + ret + +; Saves register state +%macro pushclobs 0 + push RBP + push R3 + push R12 + push R13 + push R14 + push R15 +%endmacro + +; Restores register state +%macro popclobs 0 + pop R15 + pop R14 + pop R13 + pop R12 + pop R3 + pop RBP +%endmacro + +; uintptr coro.yield(co *coro, data uintptr) +_coroyield: + pushclobs + mov R0, R6 ; Move return value into return register. + xchg RSP, [R7] ; Atomically swap the stack pointer with the yieldee. + popclobs + + ret diff --git a/src/base/error/errorf.c b/src/base/error/errorf.c new file mode 100644 index 0000000..193dd9d --- /dev/null +++ b/src/base/error/errorf.c @@ -0,0 +1,13 @@ +#include "internal.h" + +void +errorf(byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, args); + + va_end(args); +} diff --git a/src/base/error/exits.c b/src/base/error/exits.c new file mode 100644 index 0000000..6be7d3b --- /dev/null +++ b/src/base/error/exits.c @@ -0,0 +1,11 @@ +#include "internal.h" + +void +exits(char *s) +{ + if(s == nil || *s == 0) + exit(0); + + fputs(s, stderr); + exit(1); +} diff --git a/src/base/error/internal.h b/src/base/error/internal.h new file mode 100644 index 0000000..88a8895 --- /dev/null +++ b/src/base/error/internal.h @@ -0,0 +1,3 @@ +#include +#include + diff --git a/src/base/error/panicf.c b/src/base/error/panicf.c new file mode 100644 index 0000000..d698576 --- /dev/null +++ b/src/base/error/panicf.c @@ -0,0 +1,16 @@ +#include "internal.h" + +void +panicf(byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + printf("panic: "); + vprintf(fmt, args); + printf("\n"); + + va_end(args); + + exit(1); +} diff --git a/src/base/error/rules.mk b/src/base/error/rules.mk new file mode 100644 index 0000000..e3a9ce0 --- /dev/null +++ b/src/base/error/rules.mk @@ -0,0 +1,6 @@ +SRCS_$(d)+=\ + $(d)/error/exits.c \ + $(d)/error/errorf.c \ + $(d)/error/panicf.c \ + $(d)/error/verrorf.c \ + $(d)/error/vpanicf.c \ diff --git a/src/base/error/verrorf.c b/src/base/error/verrorf.c new file mode 100644 index 0000000..15af064 --- /dev/null +++ b/src/base/error/verrorf.c @@ -0,0 +1,9 @@ +#include "internal.h" + +void +verrorf(byte* fmt, va_list args) +{ + printf("error: "); + vprintf(fmt, args); + printf("\n"); +} diff --git a/src/base/error/vpanicf.c b/src/base/error/vpanicf.c new file mode 100644 index 0000000..bea97ac --- /dev/null +++ b/src/base/error/vpanicf.c @@ -0,0 +1,11 @@ +#include "internal.h" + +void +vpanicf(byte* fmt, va_list args) +{ + printf("panic: "); + vprintf(fmt, args); + printf("\n"); + + exit(1); +} diff --git a/src/base/flate/internal.h b/src/base/flate/internal.h new file mode 100644 index 0000000..794c7c2 --- /dev/null +++ b/src/base/flate/internal.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include + +typedef struct buffer +{ + union { + struct z_stream_s; + z_stream z; + }; + + ubyte buf[4098]; +} buffer; + +typedef struct flate·Reader +{ + io·Reader rdr; + void* impl; + + union { + struct buffer; + buffer b; + }; +} flate·Reader; + +typedef struct flate·Writer +{ + io·Writer wtr; + void* impl; + + union { + struct buffer; + buffer b; + }; +} flate·Writer; + diff --git a/src/base/flate/read.c b/src/base/flate/read.c new file mode 100644 index 0000000..9a42070 --- /dev/null +++ b/src/base/flate/read.c @@ -0,0 +1,41 @@ +#include "internal.h" + +int +flate·read(flate·Reader *rdr, int sz, int n, void *buf) +{ + int r; + int err; + flate·Reader zrdr; + + zrdr = *rdr; + zrdr.next_out = buf; + zrdr.avail_out = n*sz; + +READ: + err = inflate(&zrdr.b.z, Z_STREAM_END); + switch (err) { + case Z_OK: + return n; + + case Z_STREAM_END: + r = zrdr.next_out - (ubyte*)buf; + n -= r; + zrdr.avail_in = zrdr.rdr.read(zrdr.impl, 1, arrlen(zrdr.buf), zrdr.buf); + if (!zrdr.avail_in) { + return r; + } + zrdr.next_in = zrdr.buf; + goto READ; + + case Z_NEED_DICT: + errorf("zlib: need input dictionary"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: inconsistent stream structure"); + goto ERROR; + } +ERROR: + flate·closereader(rdr); + return -1; +} diff --git a/src/base/flate/reader.c b/src/base/flate/reader.c new file mode 100644 index 0000000..84f0d80 --- /dev/null +++ b/src/base/flate/reader.c @@ -0,0 +1,59 @@ +#include "internal.h" + +flate·Reader* +flate·openreader(io·Reader rdr, void* r, mem·Allocator mem, void* m) +{ + error err; + flate·Reader *zrdr; + + zrdr = mem.alloc(m, 1, sizeof(*zrdr)); + + zrdr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; + zrdr->zfree = mem.free; + zrdr->opaque = m; + zrdr->avail_in = rdr.read(r, 1, arrlen(zrdr->buf), zrdr->buf); + zrdr->next_in = zrdr->buf; + + err = inflateInit(&zrdr->b.z); + + switch (err) { + case Z_OK: + return zrdr; + + case Z_MEM_ERROR: + errorf("zlib: not enough memory"); + goto ERROR; + + case Z_VERSION_ERROR: + errorf("zlib: incompatible version"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: incorrect input parameters"); + goto ERROR; + + default: + errorf("zlib: unrecognized error code"); + } +ERROR: + errorf("zlib: msg: %s", zrdr->msg); + mem.free(m, zrdr); + return nil; +} + +error +flate·closereader(flate·Reader *rdr) +{ + int err; + flate·Reader zrdr; + + zrdr = *rdr; + err = inflateEnd(&zrdr.b.z); + if (err != Z_OK) { + errorf("zlib: failed to cleanup"); + return err; + } + rdr->zfree(rdr->opaque, rdr); + + return 0; +} diff --git a/src/base/flate/rules.mk b/src/base/flate/rules.mk new file mode 100644 index 0000000..54d8c14 --- /dev/null +++ b/src/base/flate/rules.mk @@ -0,0 +1,6 @@ +SRCS_$(d)+=\ + $(d)/flate/read.c\ + $(d)/flate/reader.c\ + $(d)/flate/write.c\ + $(d)/flate/writer.c\ + $(d)/flate/writer.c\ diff --git a/src/base/flate/write.c b/src/base/flate/write.c new file mode 100644 index 0000000..3f07b94 --- /dev/null +++ b/src/base/flate/write.c @@ -0,0 +1,48 @@ +#include "internal.h" + +int +flate·write(flate·Writer *wtr, int sz, int n, void *buf) +{ + int r; + int err; + flate·Writer zwtr; + + zwtr = *wtr; + zwtr.next_out = buf; +DEFLATE: + zwtr.avail_out = n*sz; + err = deflate(&zwtr.z, Z_NO_FLUSH); + + switch (err) { + case Z_STREAM_END: + return n; + + case Z_OK: + r = (zwtr.next_out - (ubyte*)buf)/sz; + n -= r; + if (!n) { + return r; + } + buf += n; + goto DEFLATE; + + case Z_STREAM_ERROR: + errorf("zlib: bad input"); + goto ERROR; + + case Z_BUF_ERROR: + if (!zwtr.avail_in) { + zwtr.avail_in += zwtr.wtr.write(zwtr.impl, 1, arrlen(zwtr.buf), buf); + if (!zwtr.avail_in) { + errorf("reader: failed read"); + goto ERROR; + } + goto DEFLATE; + } + } + + return 0; +ERROR: + errorf("zlib: %s", zwtr.msg); + return -1; +} diff --git a/src/base/flate/writer.c b/src/base/flate/writer.c new file mode 100644 index 0000000..f339ae0 --- /dev/null +++ b/src/base/flate/writer.c @@ -0,0 +1,57 @@ +#include "internal.h" + +flate·Writer* +flate·openwriter(io·Writer wtr, void* w, mem·Allocator mem, void* m) +{ + error err; + flate·Writer *zwtr; + + zwtr = mem.alloc(m, 1, sizeof(*zwtr)); + zwtr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; + zwtr->zfree = mem.free; + zwtr->opaque = m; + zwtr->avail_in = 0; + + err = deflateInit(&zwtr->b.z, Z_DEFAULT_COMPRESSION); + + switch (err) { + case Z_OK: + return zwtr; + + case Z_MEM_ERROR: + errorf("zlib: not enough memory"); + goto ERROR; + + case Z_VERSION_ERROR: + errorf("zlib: incompatible version"); + goto ERROR; + + case Z_STREAM_ERROR: + errorf("zlib: incorrect compression level"); + goto ERROR; + + default: + errorf("zlib: unrecognized error code"); + } +ERROR: + errorf("zlib: msg: %s", zwtr->msg); + mem.free(m, zwtr); + return nil; +} + +error +flate·closewriter(flate·Writer *wtr) +{ + int err; + flate·Writer zwtr; + + zwtr = *wtr; + err = deflateEnd(&zwtr.b.z); + if (err != Z_OK) { + errorf("zlib: failed to cleanup"); + return err; + } + zwtr.zfree(zwtr.opaque, wtr); + + return 0; +} diff --git a/src/base/fs/internal.h b/src/base/fs/internal.h new file mode 100644 index 0000000..7fde093 --- /dev/null +++ b/src/base/fs/internal.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +/* + * path history + */ +struct Key +{ + ino_t ino; + dev_t dev; +}; + +struct fs·History +{ + SET_STRUCT_BODY(struct Key); +}; diff --git a/src/base/fs/rules.mk b/src/base/fs/rules.mk new file mode 100644 index 0000000..3927ae3 --- /dev/null +++ b/src/base/fs/rules.mk @@ -0,0 +1,3 @@ +SRCS_$(d)+=\ + $(d)/fs/walk.c\ + $(d)/fs/walker.c\ diff --git a/src/base/fs/walk.c b/src/base/fs/walk.c new file mode 100644 index 0000000..d528896 --- /dev/null +++ b/src/base/fs/walk.c @@ -0,0 +1,119 @@ +#include "internal.h" + +#define hash(k) ((int32)k.ino ^ (int32)k.dev) +#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev) + +static +int +morehistory(fs·History *h, int n) +{ + SET_GROW(h, struct Key, n, hash, sys·Memory, nil); +} + +static +int +addentry(fs·History *h, struct Key key, int *err) +{ + SET_PUT(h, key, hash, equal, morehistory, err); +} + +static +void +forget(fs·History *h) +{ + if (!h) + return; + + SET_RESET(h); +} + +void +fs·walk(fs·Walker *fs) +{ + char *e, *b; + DIR *dir; + int new, fd, ofd, flags; + fs·History *h; + struct dirent *d; + io·Stat cwd; + struct fs·Entry *it; + + flags = 0; + if(fs->flags & fs·nolinks) + flags |= AT_SYMLINK_NOFOLLOW; + + /* get info for base relative to current fd */ + if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){ + if(fs->flags & fs·verbose) + errorf("stat: %s", fs->path); + return; + } + + /* if we hit a file, finish! */ + if(!S_ISDIR(cwd.st_mode)) { + fs->func(fs->data, fs->base, fs->path, &cwd); + return; + } + + /* have we been here before? (cycle detection) */ + /* if not, add to our path history */ + if (!(fs->flags & fs·nolinks)) { + addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new); + if (!new) + return; + } + + /* + * operate on directory first if preorder traversal + * truncate recursion if callback returns an error code + */ + if (fs->flags & fs·preorder) { + if (fs->func(fs->data, fs->base, fs->path, &cwd)) + return; + } + + /* open directory */ + if(!fs->max || fs->lev + 1 < fs->max) { + fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (fd < 0) + errorf("open %s:", fs->path); + + if (!(dir=fdopendir(fd))) { + if(fs->flags & fs·verbose) + errorf("fdopendir: %s", fs->path); + return; + } + + ofd = fs->fd, fs->fd = fd; + + /* traverse children */ + e = fs->end, b = fs->base; + if (fs->end[-1] != '/') + *fs->end++ = '/'; + + fs->base = fs->end; + while((d = readdir(dir))) { + if(*d->d_name == '.') + if(d->d_name[1] == 0 || /* . */ + (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */ + continue; + + fs->end = str·copyn(fs->base, d->d_name, arrend(fs->path) - fs->base); + + fs->lev++; + fs·walk(fs); + fs->lev--; + } + *e = 0; + fs->fd = ofd; + fs->end = e, fs->base = b; + closedir(dir); + } + + /* operate on directory if postorder (default) traversal */ + if (!(fs->flags & fs·preorder)) + fs->func(fs->data, fs->base, fs->path, &cwd); + + if (!fs->lev) + forget(fs->hist); +} diff --git a/src/base/fs/walker.c b/src/base/fs/walker.c new file mode 100644 index 0000000..65ff391 --- /dev/null +++ b/src/base/fs/walker.c @@ -0,0 +1,39 @@ +#include "internal.h" + +static +void +delete(fs·History *h) +{ + SET_FREE(h, sys·Memory, nil); +} + +int +fs·init(fs·Walker *fs, char *path) +{ + fs->base = fs->end = fs->path; + + if(!path || !path[0]){ + path = getcwd(fs->path, arrlen(fs->path)); + if (!path) + return 1; + fs->end += strlen(path); + }else + fs->end = str·copyn(fs->base, path, arrlen(fs->path)); + + if(fs->path[0] != '/') + fs->fd = AT_FDCWD; + + if(!fs->hist && !(fs->flags & fs·nolinks)) + fs->hist = calloc(1, sizeof(*fs->hist)); + + return 0; +} + +void +fs·fini(fs·Walker *fs) +{ + if(fs->hist){ + delete(fs->hist); + free(fs->hist); + } +} diff --git a/src/base/gz/flush.c b/src/base/gz/flush.c new file mode 100644 index 0000000..011a3ab --- /dev/null +++ b/src/base/gz/flush.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +gz·flush(gz·Stream *s) +{ + return gzflush(s, Z_FINISH); +} diff --git a/src/base/gz/get.c b/src/base/gz/get.c new file mode 100644 index 0000000..24ba23a --- /dev/null +++ b/src/base/gz/get.c @@ -0,0 +1,17 @@ +#include "internal.h" + +byte +gz·getbyte(gz·Stream *s) +{ + // NOTE: Can't call macro + byte b[2]; + gzread(s, b, 1); + + return b[0]; +} + +error +gz·ungetbyte(gz·Stream *s, byte c) +{ + return gzungetc(c, s); +} diff --git a/src/base/gz/interface.c b/src/base/gz/interface.c new file mode 100644 index 0000000..15b8f10 --- /dev/null +++ b/src/base/gz/interface.c @@ -0,0 +1,12 @@ +#include "internal.h" + +io·Reader gz·Reader = (io·Reader){ gz·read }; +io·Peeker gz·Peeker = (io·Peeker){ gz·getbyte, gz·ungetbyte }; +io·Seeker gz·Seeker = (io·Seeker){ gz·seek, gz·tell }; +io·PeekReader gz·Peekreader = (io·PeekReader){ gz·read, gz·getbyte, gz·ungetbyte }; + +io·Writer gz·Writer = (io·Writer){ gz·write }; +io·Putter gz·Putter = (io·Putter){ gz·putbyte, gz·putstring }; +io·PutWriter gz·PutWriter = (io·PutWriter){ gz·write, gz·putbyte, gz·putstring }; + +io·ReadWriter gz·ReadWriter = (io·ReadWriter){ gz·read, gz·write }; diff --git a/src/base/gz/internal.h b/src/base/gz/internal.h new file mode 100644 index 0000000..6a268c4 --- /dev/null +++ b/src/base/gz/internal.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +#include diff --git a/src/base/gz/open.c b/src/base/gz/open.c new file mode 100644 index 0000000..c84ce5e --- /dev/null +++ b/src/base/gz/open.c @@ -0,0 +1,13 @@ +#include "internal.h" + +gz·Stream* +gz·open(byte *path, byte *mode) +{ + return gzopen(path, mode); +} + +error +gz·close(gz·Stream* s) +{ + return gzclose(s); +} diff --git a/src/base/gz/printf.c b/src/base/gz/printf.c new file mode 100644 index 0000000..d7f75cf --- /dev/null +++ b/src/base/gz/printf.c @@ -0,0 +1,15 @@ +#include "internal.h" + +int +gz·printf(gz·Stream *s, byte *fmt, ...) +{ + error err; + + va_list args; + va_start(args, fmt); + err = gzprintf(s, fmt, args); + va_end(args); + + return err; +} + diff --git a/src/base/gz/put.c b/src/base/gz/put.c new file mode 100644 index 0000000..fa9807d --- /dev/null +++ b/src/base/gz/put.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +gz·putbyte(gz·Stream *s, byte c) +{ + return gzputc(s, c); +} diff --git a/src/base/gz/putstring.c b/src/base/gz/putstring.c new file mode 100644 index 0000000..64ff470 --- /dev/null +++ b/src/base/gz/putstring.c @@ -0,0 +1,8 @@ +#include "internal.h" + +error +gz·putstring(gz·Stream *s, byte *str) +{ + return gzputs(s, str); +} + diff --git a/src/base/gz/read.c b/src/base/gz/read.c new file mode 100644 index 0000000..112fe4d --- /dev/null +++ b/src/base/gz/read.c @@ -0,0 +1,16 @@ +#include "internal.h" + +int +gz·read(gz·Stream *s, int sz, int n, void* buf) +{ + return gzread(s, buf, n*sz); +} + +int +gz·readln(gz·Stream *s, int n, byte *buf) +{ + byte* b; + b = gzgets(s, buf, n); + + return strlen(b); +} diff --git a/src/base/gz/rules.mk b/src/base/gz/rules.mk new file mode 100644 index 0000000..a933291 --- /dev/null +++ b/src/base/gz/rules.mk @@ -0,0 +1,11 @@ +SRCS_$(d)+=\ + $(d)/gz/flush.c\ + $(d)/gz/get.c\ + $(d)/gz/interface.c\ + $(d)/gz/open.c\ + $(d)/gz/printf.c\ + $(d)/gz/put.c\ + $(d)/gz/putstring.c\ + $(d)/gz/read.c\ + $(d)/gz/seek.c\ + $(d)/gz/write.c\ diff --git a/src/base/gz/seek.c b/src/base/gz/seek.c new file mode 100644 index 0000000..328886d --- /dev/null +++ b/src/base/gz/seek.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +gz·seek(gz·Stream *s, long off, enum SeekPos whence) +{ + return gzseek(s, off, whence); +} diff --git a/src/base/gz/write.c b/src/base/gz/write.c new file mode 100644 index 0000000..862d833 --- /dev/null +++ b/src/base/gz/write.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +gz·write(gz·Stream *s, int sz, int n, void* buf) +{ + return gzwrite(s, buf, n*sz); +} diff --git a/src/base/io/fd.c b/src/base/io/fd.c new file mode 100644 index 0000000..ded1b02 --- /dev/null +++ b/src/base/io/fd.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·fd(io·Stream *s) +{ + return fileno(s); +} diff --git a/src/base/io/flush.c b/src/base/io/flush.c new file mode 100644 index 0000000..0f1217a --- /dev/null +++ b/src/base/io/flush.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·flush(io·Stream *s) +{ + return fflush(s); +} diff --git a/src/base/io/get.c b/src/base/io/get.c new file mode 100644 index 0000000..d4e52f8 --- /dev/null +++ b/src/base/io/get.c @@ -0,0 +1,7 @@ +#include "internal.h" + +byte +io·getbyte(io·Stream *s) +{ + return fgetc(s); +} diff --git a/src/base/io/interface.c b/src/base/io/interface.c new file mode 100644 index 0000000..bead9e1 --- /dev/null +++ b/src/base/io/interface.c @@ -0,0 +1,70 @@ +#include "internal.h" + +static +int +·read(void *rdr, int size, int n, void *buf) +{ + return io·read((io·Stream *)rdr, size, n, buf); +} + +static +byte +·get(void *rdr) +{ + return io·getbyte((io·Stream *)rdr); +} + +static +error +·unget(void *rdr, byte c) +{ + return io·ungetbyte((io·Stream *)rdr, c); +} + +static +int +·write(void *wtr, int sz, int n, void *buf) +{ + return io·write((io·Stream *)wtr, sz, n, buf); +} + +static +error +·put(void *wtr, byte c) +{ + return io·putbyte((io·Stream *)wtr, c); +} + +static +int +·puts(void *wtr, string s) +{ + return io·putstring((io·Stream *)wtr, s); +} + +static +int +·seek(void *skr, long off, enum SeekPos whence) +{ + return io·seek((io·Stream *)skr, off, whence); +} + +static +long +·tell(void *skr) +{ + return io·tell((io·Stream *)skr); +} + +/* actual interfaces */ +io·Reader sys·Reader = (io·Reader){ ·read }; +io·Seeker sys·Seeker = (io·Seeker){ ·seek, ·tell }; +io·Peeker sys·Peeker = (io·Peeker){ ·get, ·unget }; +io·SeekReader sys·SeekReader = (io·SeekReader){ ·seek, ·tell, ·read }; +io·PeekReader sys·PeekReader = (io·PeekReader){ ·read, ·get, ·unget }; + +io·Writer sys·Writer = (io·Writer){ ·write }; +io·Putter sys·Putter = (io·Putter){ ·put, ·puts }; +io·PutWriter sys·PutWriter = (io·PutWriter){ ·write, ·put, ·puts }; + +io·ReadWriter sys·ReadWriter = (io·ReadWriter){ ·read, ·write }; diff --git a/src/base/io/internal.h b/src/base/io/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/src/base/io/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/src/base/io/open.c b/src/base/io/open.c new file mode 100644 index 0000000..e50e334 --- /dev/null +++ b/src/base/io/open.c @@ -0,0 +1,13 @@ +#include "internal.h" + +io·Stream* +io·open(byte *name, byte *mode) +{ + return fopen(name, mode); +} + +error +io·close(io·Stream *s) +{ + return fclose(s); +} diff --git a/src/base/io/putbyte.c b/src/base/io/putbyte.c new file mode 100644 index 0000000..2350a8d --- /dev/null +++ b/src/base/io/putbyte.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·putbyte(io·Stream *s, byte c) +{ + return fputc(c, s); +} diff --git a/src/base/io/putstring.c b/src/base/io/putstring.c new file mode 100644 index 0000000..53fa993 --- /dev/null +++ b/src/base/io/putstring.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·putstring(io·Stream *s, string str) +{ + return fputs(str, s); +} diff --git a/src/base/io/read.c b/src/base/io/read.c new file mode 100644 index 0000000..b0ed3d2 --- /dev/null +++ b/src/base/io/read.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·read(io·Stream *s, int sz, int n, void *buf) +{ + return fread(buf, sz, n, s); +} diff --git a/src/base/io/readln.c b/src/base/io/readln.c new file mode 100644 index 0000000..283472d --- /dev/null +++ b/src/base/io/readln.c @@ -0,0 +1,12 @@ +#include "internal.h" + +int +io·readln(io·Stream *s, int n, byte* buf) +{ + byte* b; + b = fgets(buf, n+1, s); + if(b == nil) + return -1; + + return strlen(buf); +} diff --git a/src/base/io/rules.mk b/src/base/io/rules.mk new file mode 100644 index 0000000..2e03ca5 --- /dev/null +++ b/src/base/io/rules.mk @@ -0,0 +1,14 @@ +SRCS_$(d)+=\ + $(d)/io/fd.c\ + $(d)/io/flush.c\ + $(d)/io/interface.c\ + $(d)/io/open.c\ + $(d)/io/putbyte.c\ + $(d)/io/putstring.c\ + $(d)/io/read.c\ + $(d)/io/readln.c\ + $(d)/io/seek.c\ + $(d)/io/stat.c\ + $(d)/io/tell.c\ + $(d)/io/unget.c\ + $(d)/io/write.c\ diff --git a/src/base/io/seek.c b/src/base/io/seek.c new file mode 100644 index 0000000..d0e7488 --- /dev/null +++ b/src/base/io/seek.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·seek(io·Stream *s, long off, enum SeekPos origin) +{ + return fseek(s, off, origin); +} diff --git a/src/base/io/stat.c b/src/base/io/stat.c new file mode 100644 index 0000000..d86f1ee --- /dev/null +++ b/src/base/io/stat.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +io·stat(io·Stream *s, io·Stat *buf) +{ + return fstat(fileno(s), buf); +} diff --git a/src/base/io/tell.c b/src/base/io/tell.c new file mode 100644 index 0000000..1c50439 --- /dev/null +++ b/src/base/io/tell.c @@ -0,0 +1,7 @@ +#include "internal.h" + +long +io·tell(io·Stream *s) +{ + return ftell(s); +} diff --git a/src/base/io/unget.c b/src/base/io/unget.c new file mode 100644 index 0000000..5ec3536 --- /dev/null +++ b/src/base/io/unget.c @@ -0,0 +1,7 @@ +#include "internal.h" + +error +io·ungetbyte(io·Stream *s, byte c) +{ + return ungetc(c, s); +} diff --git a/src/base/io/write.c b/src/base/io/write.c new file mode 100644 index 0000000..63df664 --- /dev/null +++ b/src/base/io/write.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +io·write(io·Stream *s, int sz, int n, void *buf) +{ + return fwrite(buf, sz, n, s); +} diff --git a/src/base/mem/arena.c b/src/base/mem/arena.c new file mode 100644 index 0000000..b2ce044 --- /dev/null +++ b/src/base/mem/arena.c @@ -0,0 +1,119 @@ +#include "internal.h" + +#define ARENA_ALIGN 8 +#define ARENA_BLOCK_SIZE 1024 * 1024 + +#define ALIGN_DOWN(n, a) ((n) & ~((a)-1)) +#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a)) +#define ALIGN_DOWN_PTR(p, a) ((void*)ALIGN_DOWN((uintptr)(p), (a))) +#define ALIGN_UP_PTR(p, a) ((void*)ALIGN_UP((uintptr)(p), (a))) + +struct Block +{ + struct Block *next; + byte buf[]; +}; + +struct mem·Arena +{ + void *heap; + mem·Allocator mem; + + byte *off; + byte *end; + struct Block *curr; + struct Block first; +}; + +static +void* +·arenaalloc(void *heap, uint n, ulong size) +{ + return mem·arenaalloc(heap, n, size); +} + +static +void +·arenafree(void *heap, void *ptr) +{ + /* no-op */ +} + +mem·Allocator mem·ArenaAllocator = { + .alloc = ·arenaalloc, + .free = ·arenafree, +}; + + +static +void +grow(mem·Arena *a, vlong min) +{ + uintptr size; + struct Block *blk; + + size = ALIGN_UP(MAX(min, ARENA_BLOCK_SIZE), ARENA_ALIGN); + blk = a->mem.alloc(a->heap, 1, sizeof(*blk) + size); + a->off = blk->buf; + a->end = a->off + size; + + assert(a->curr->next == nil); + assert(a->off == ALIGN_DOWN_PTR(a->off, ARENA_ALIGN)); + + a->curr->next = blk; + a->curr = blk; +} + +mem·Arena* +mem·makearena(mem·Allocator from, void *impl) +{ + mem·Arena *a = from.alloc(impl, 1, sizeof(*a) + ARENA_BLOCK_SIZE); + a->mem = from; + a->heap = impl; + a->off = a->first.buf; + a->end = a->first.buf + ARENA_BLOCK_SIZE; + a->curr = &a->first; + a->first.next = nil; + + return a; +} + +void +mem·freearena(mem·Arena *a) +{ + struct Block *it, *next; + + it = a->first.next; + while (it != nil) { + next = it->next; + a->mem.free(a->heap, it); + it = next; + } + + a->mem.free(a->heap, a); +} + +void* +mem·arenaalloc(mem·Arena *a, uint n, ulong size) +{ + if(!n) { + return nil; + } + + void *ptr; + // TODO(nnoll): check for overflow + size = n * size; + + if (size > (ulong)(a->end - a->off)) { + grow(a, size); + assert(size <= (uintptr)(a->end - a->off)); + } + + ptr = a->off; + a->off = ALIGN_UP_PTR(a->off + size, ARENA_ALIGN); + + assert(a->off <= a->end); + assert(ptr == ALIGN_DOWN_PTR(ptr, ARENA_ALIGN)); + + return ptr; +} diff --git a/src/base/mem/buffer.c b/src/base/mem/buffer.c new file mode 100644 index 0000000..b684d35 --- /dev/null +++ b/src/base/mem/buffer.c @@ -0,0 +1,45 @@ +#include "internal.h" + +/* Grow to particular size */ +void* +·bufgrow(void* buf, vlong newLen, vlong eltsize) +{ + assert(bufcap(buf) <= (SIZE_MAX - 1) / 2); + + vlong newCap = MAX(16, MAX(1 + 2 * bufcap(buf), newLen)); + + assert(newLen <= newCap); + assert(newCap <= (SIZE_MAX - offsetof(BufHdr, buf)) / eltsize); + + vlong newSize = offsetof(BufHdr, buf) + newCap * eltsize; + + BufHdr* newHdr; + if (buf) { + newHdr = bufhdr(buf); + newHdr = (BufHdr*)realloc((void*)newHdr, newSize); + } else { + newHdr = (BufHdr*)malloc(newSize); + newHdr->len = 0; + } + + newHdr->cap = newCap; + return (void*)newHdr->buf; +} + +/* Pop out a value */ +void +·bufdel(void *buf, int i, vlong eltsize) +{ + int n; + byte *b; + byte stk[1024]; + assert(eltsize < sizeof(stk)); + + b = (byte*)buf; + if(n = buflen(buf), i < n) { + memcpy(stk, b+eltsize*i, eltsize); + memcpy(b+eltsize*i, b+eltsize*(i+1), eltsize*(n-i-1)); + memcpy(b+eltsize*(n-1), stk, eltsize); + } + bufhdr(buf)->len--; +} diff --git a/src/base/mem/interface.c b/src/base/mem/interface.c new file mode 100644 index 0000000..4d7d1ce --- /dev/null +++ b/src/base/mem/interface.c @@ -0,0 +1,36 @@ +#include "internal.h" + +static +void +·free(void *_, void *ptr) { + return free(ptr); +} + +static +void * +·alloc(void *_, uint n, ulong size) { + return malloc(n*size); +} + +static +void * +·calloc(void *_, uint n, ulong size) { + return calloc(n, size); +} + +static +void * +·realloc(void *_, void *ptr, uint n, ulong size) { + return realloc(ptr, n*size); +} + +mem·Allocator sys·Memory = { + .alloc = ·calloc, + .free = ·free +}; + +mem·Reallocator sys·FullMemory = { + .alloc = ·calloc, + .realloc = ·realloc, + .free = ·free +}; diff --git a/src/base/mem/internal.h b/src/base/mem/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/src/base/mem/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/src/base/mem/rules.mk b/src/base/mem/rules.mk new file mode 100644 index 0000000..b912d0c --- /dev/null +++ b/src/base/mem/rules.mk @@ -0,0 +1,5 @@ +SRCS_$(d)+=\ + $(d)/mem/arena.c\ + $(d)/mem/buffer.c\ + $(d)/mem/interface.c\ + $(d)/mem/set64.c\ diff --git a/src/base/mem/set64.c b/src/base/mem/set64.c new file mode 100644 index 0000000..464b3ad --- /dev/null +++ b/src/base/mem/set64.c @@ -0,0 +1,13 @@ +#include "internal.h" + +void +mem·set64(void *dst, uint64 val, uintptr size) +{ + intptr i; + + for(i = 0; i < (size & (~7)); i += 8) + memcpy((byte*)dst + i, &val, 8); + + for(; i < size; i++) + ((byte*)dst)[i] = ((byte*)&val)[i&7]; +} diff --git a/src/base/mmap/internal.h b/src/base/mmap/internal.h new file mode 100644 index 0000000..7606c7e --- /dev/null +++ b/src/base/mmap/internal.h @@ -0,0 +1,5 @@ +#pragma once + +#include +#include +#include diff --git a/src/base/mmap/mmap.c b/src/base/mmap/mmap.c new file mode 100644 index 0000000..ce3011c --- /dev/null +++ b/src/base/mmap/mmap.c @@ -0,0 +1,39 @@ +#include "internal.h" + +mmap·Reader +mmap·open(byte *filename) +{ + int fd; + int err; + void *buf; + io·Stream *s; + io·Stat st; + + s = io·open(filename, "r"); + fd = io·fd(s); + err = io·stat(s, &st); + if(err){ + errorf("file stat: error code %d", err); + goto ERROR; + } + + buf = mmap(nil, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if(!buf){ + errorf("mmap: failed"); + goto ERROR; + } + // NOTE: posix systems require that reference kept to mmap file after fd is closed + io·close(s); + return (mmap·Reader){.len=st.st_size, .b=buf}; + +ERROR: + io·close(s); + return (mmap·Reader){ 0 }; +} + +error +mmap·close(mmap·Reader rdr) +{ + munmap(rdr.b, rdr.len); + return 0; +} diff --git a/src/base/mmap/rules.mk b/src/base/mmap/rules.mk new file mode 100644 index 0000000..fb3cab5 --- /dev/null +++ b/src/base/mmap/rules.mk @@ -0,0 +1,2 @@ +SRCS_$(d)+=\ + $(d)/mmap/mmap.c\ diff --git a/src/base/os/basename.c b/src/base/os/basename.c new file mode 100644 index 0000000..b5bb343 --- /dev/null +++ b/src/base/os/basename.c @@ -0,0 +1,10 @@ +#include "internal.h" + +char* +os·basename(char *path) +{ + char *sep; + + sep = strrchr(path, os·sep()); + return (sep == nil) ? path : sep+1; +} diff --git a/src/base/os/exists.c b/src/base/os/exists.c new file mode 100644 index 0000000..a3c8935 --- /dev/null +++ b/src/base/os/exists.c @@ -0,0 +1,7 @@ +#include "internal.h" + +int +os·exists(byte *path, int flag) +{ + return access(path, flag) == 0; +} diff --git a/src/base/os/internal.h b/src/base/os/internal.h new file mode 100644 index 0000000..302c035 --- /dev/null +++ b/src/base/os/internal.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/src/base/os/rules.mk b/src/base/os/rules.mk new file mode 100644 index 0000000..bf1e71d --- /dev/null +++ b/src/base/os/rules.mk @@ -0,0 +1,4 @@ +SRCS_$(d)+=\ + $(d)/os/basename.c\ + $(d)/os/exists.c\ + $(d)/os/sep.c\ diff --git a/src/base/os/sep.c b/src/base/os/sep.c new file mode 100644 index 0000000..750e627 --- /dev/null +++ b/src/base/os/sep.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +os·sep(void) +{ +#if defined(UNIX) || defined(__linux__) + return '/'; +#elif defined(WIN32) + return '\\'; +#else + panicf("unrecognized operating system"); + return '\0'; +#endif +} diff --git a/src/base/rng/base.c b/src/base/rng/base.c new file mode 100644 index 0000000..9ec496e --- /dev/null +++ b/src/base/rng/base.c @@ -0,0 +1,24 @@ +#include "internal.h" + +static uint64 +splitmix64(struct Mix *state) +{ + uint64 result = state->s; + + state->s = result + 0x9E3779B97f4A7C15; + result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; + result = (result ^ (result >> 27)) * 0x94D049BB133111EB; + return result ^ (result >> 31); +} + +int +rng·init(uint64 seed) +{ + int i; + Mix smstate = {seed}; + + for(i=0; i < 4; i++) + rng·RNG.s[i] = splitmix64(&smstate); + + return 0; +} diff --git a/src/base/rng/bernoulli.c b/src/base/rng/bernoulli.c new file mode 100644 index 0000000..02f531e --- /dev/null +++ b/src/base/rng/bernoulli.c @@ -0,0 +1,7 @@ +#include "internal.h" + +bool +rng·bernoulli(double f) +{ + return rng·random() < f; +} diff --git a/src/base/rng/exponential.c b/src/base/rng/exponential.c new file mode 100644 index 0000000..c07e007 --- /dev/null +++ b/src/base/rng/exponential.c @@ -0,0 +1,11 @@ +#include "internal.h" + +/* Returns a random float64 between 0 and 1 */ +double +rng·exponential(double lambda) +{ + double f; + + f = rng·random(); + return -log(1 - f)/lambda; +} diff --git a/src/base/rng/internal.h b/src/base/rng/internal.h new file mode 100644 index 0000000..9cf5f41 --- /dev/null +++ b/src/base/rng/internal.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#define rol64(x, k) ((x) << (k) | ((x) >> (64-(k)))) + +typedef struct Rng +{ + uint64 s[4]; +} Rng; + +typedef struct Mix +{ + uint64 s; +} Mix; + + +extern Rng rng·RNG; diff --git a/src/base/rng/normal.c b/src/base/rng/normal.c new file mode 100644 index 0000000..aab5731 --- /dev/null +++ b/src/base/rng/normal.c @@ -0,0 +1,77 @@ +#include "internal.h" + +static inline double +erfinv(double x) +{ + /* useful constants */ + static double + a0 = 1.1975323115670912564578e0, a1 = 4.7072688112383978012285e1, + a2 = 6.9706266534389598238465e2, a3 = 4.8548868893843886794648e3, + a4 = 1.6235862515167575384252e4, a5 = 2.3782041382114385731252e4, + a6 = 1.1819493347062294404278e4, a7 = 8.8709406962545514830200e2, + + b0 = 1.0000000000000000000e0, b1 = 4.2313330701600911252e1, + b2 = 6.8718700749205790830e2, b3 = 5.3941960214247511077e3, + b4 = 2.1213794301586595867e4, b5 = 3.9307895800092710610e4, + b6 = 2.8729085735721942674e4, b7 = 5.2264952788528545610e3, + + c0 = 1.42343711074968357734e0, c1 = 4.63033784615654529590e0, + c2 = 5.76949722146069140550e0, c3 = 3.64784832476320460504e0, + c4 = 1.27045825245236838258e0, c5 = 2.41780725177450611770e-1, + c6 = 2.27238449892691845833e-2, c7 = 7.74545014278341407640e-4, + + d0 = 1.4142135623730950488016887e0, d1 = 2.9036514445419946173133295e0, + d2 = 2.3707661626024532365971225e0, d3 = 9.7547832001787427186894837e-1, + d4 = 2.0945065210512749128288442e-1, d5 = 2.1494160384252876777097297e-2, + d6 = 7.7441459065157709165577218e-4, d7 = 1.4859850019840355905497876e-9, + + e0 = 6.65790464350110377720e0, e1 = 5.46378491116411436990e0, + e2 = 1.78482653991729133580e0, e3 = 2.96560571828504891230e-1, + e4 = 2.65321895265761230930e-2, e5 = 1.24266094738807843860e-3, + e6 = 2.71155556874348757815e-5, e7 = 2.01033439929228813265e-7, + + f0 = 1.414213562373095048801689e0, f1 = 8.482908416595164588112026e-1, + f2 = 1.936480946950659106176712e-1, f3 = 2.103693768272068968719679e-2, + f4 = 1.112800997078859844711555e-3, f5 = 2.611088405080593625138020e-5, + f6 = 2.010321207683943062279931e-7, f7 = 2.891024605872965461538222e-15, + + Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009; + + int s; + double r, z1, z2; + + if(x < 0) { + s = -1; + x = -x; + } else { + s = +1; + } + + if(x <= 0.85) { + r = 0.180625 - 0.25*x*x; + z1 = ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0; + z2 = ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0; + return s*(x*z1) / z2; + } + r = sqrt(Ln2 - log(1.0-x)); + if(r <= 5.0) { + r -= 1.6; + z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0; + z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0; + } else { + r -= 5.0; + z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0; + z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0; + } + + return s*z1/z2; +} + +double +rng·normal(void) +{ + double f; + f = rng·random(); + + return sqrt(2)*erfinv(2*f-1); +} diff --git a/src/base/rng/poisson.c b/src/base/rng/poisson.c new file mode 100644 index 0000000..3ec15c9 --- /dev/null +++ b/src/base/rng/poisson.c @@ -0,0 +1,126 @@ +#include "internal.h" + +/* + * Ahrens, J. H., & Dieter, U. (1982). + * Computer Generation of Poisson Deviates from Modified Normal Distributions. + */ +static double factorial[10] = {1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.}; +static double coeffs[9] = { + -.500000000, +.333333333, -.249999856, + +.200011780, -.166684875, +.142187833, + -.124196313, +.125005956, -.114265030, +}; + +static inline +double +log1pmx(double x, double off) +{ + int i; + double r, t; + + if(-0.25 < x && x < 0.25) { + r = 0; + t = 1; + for(i=0;i=L) + return K; +stepS: + U = rng·random(); + if(d*U >= (mu-K)*(mu-K)*(mu-K)) + return K; +stepP: + if(G < 0) + goto stepE; +stepQ: + c = procf(mu, s, K, &px, &py, &fx, &fy); +stepE: + E = rng·exponential(1.0); + U = rng·random(); + U = U + U - 1; + T = 1.8 + copysign(E,U); + if(T < 0.6744) + goto stepE; + K = floor(mu + s*T); + c = procf(mu, s, K, &px, &py, &fx, &fy); +stepH: + if(c*fabs(U) > (py*exp(px + E) - fy*exp(fx + E))) + goto stepE; + return K; +} + +uint64 +rng·poisson(double mean) +{ + int64 n; + double z; + + if(mean<10.0) { + for(n=0, z=rng·exponential(1.0); zs; + uint64 result = rol64(s[1] * 5, 7) * 9; + uint64 t = s[1] << 17; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + + s[2] ^= t; + s[3] = rol64(s[3], 45); + + return result; +} + +double +rng·random(void) +{ + uint64 r = xoshiro256ss(&rng·RNG); + return (double)r / (double)UINT64_MAX; +} + +uint64 +rng·randi(int max) +{ + uint64 r = xoshiro256ss(&rng·RNG); + return r % max; +} diff --git a/src/base/rng/rules.mk b/src/base/rng/rules.mk new file mode 100644 index 0000000..407b1bf --- /dev/null +++ b/src/base/rng/rules.mk @@ -0,0 +1,7 @@ +SRCS_$(d)+=\ + $(d)/rng/base.c\ + $(d)/rng/bernoulli.c\ + $(d)/rng/exponential.c\ + $(d)/rng/normal.c\ + $(d)/rng/poisson.c\ + $(d)/rng/random.c\ diff --git a/src/base/rules.mk b/src/base/rules.mk new file mode 100644 index 0000000..847e4d8 --- /dev/null +++ b/src/base/rules.mk @@ -0,0 +1,37 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# local sources +SRCS_$(d):=\ + $(d)/arg.c +include $(d)/bufio/rules.mk +include $(d)/coro/rules.mk +include $(d)/error/rules.mk +include $(d)/flate/rules.mk +include $(d)/fs/rules.mk +include $(d)/gz/rules.mk +include $(d)/io/rules.mk +include $(d)/mem/rules.mk +include $(d)/mmap/rules.mk +include $(d)/os/rules.mk +include $(d)/rng/rules.mk +include $(d)/sort/rules.mk +include $(d)/string/rules.mk +CHECK_$(d):=\ + $(d)/test.c + +# outputs +LIBS_$(d) := $(d)/base.a +BINS_$(d) := + +include share/paths.mk + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(TEST_$(d)): TCLIBS := $(LIBS_$(d)) +$(TEST_$(d)): $(UNIT_$(d)) $(LIBS_$(d)) + $(LINK) + +include share/pop.mk diff --git a/src/base/sort/double.c b/src/base/sort/double.c new file mode 100644 index 0000000..c3feac2 --- /dev/null +++ b/src/base/sort/double.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·double(uintptr sz, double arr[]) +{ + double tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/float.c b/src/base/sort/float.c new file mode 100644 index 0000000..57bd482 --- /dev/null +++ b/src/base/sort/float.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·float(uintptr sz, float arr[]) +{ + float tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/int.c b/src/base/sort/int.c new file mode 100644 index 0000000..33e1def --- /dev/null +++ b/src/base/sort/int.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int(uintptr sz, int arr[]) +{ + int tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/int16.c b/src/base/sort/int16.c new file mode 100644 index 0000000..072a3eb --- /dev/null +++ b/src/base/sort/int16.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int16(uintptr sz, int16 arr[]) +{ + int16 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/int32.c b/src/base/sort/int32.c new file mode 100644 index 0000000..27b3b7b --- /dev/null +++ b/src/base/sort/int32.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int32(uintptr sz, int32 arr[]) +{ + int32 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/int64.c b/src/base/sort/int64.c new file mode 100644 index 0000000..b3fa5d4 --- /dev/null +++ b/src/base/sort/int64.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int64(uintptr sz, int64 arr[]) +{ + int64 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/int8.c b/src/base/sort/int8.c new file mode 100644 index 0000000..5848e6e --- /dev/null +++ b/src/base/sort/int8.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·int8(uintptr sz, int8 arr[]) +{ + int8 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/internal.h b/src/base/sort/internal.h new file mode 100644 index 0000000..ac569de --- /dev/null +++ b/src/base/sort/internal.h @@ -0,0 +1,5 @@ +#pragma once + +#include +#include +#include diff --git a/src/base/sort/rules.mk b/src/base/sort/rules.mk new file mode 100644 index 0000000..780d6ea --- /dev/null +++ b/src/base/sort/rules.mk @@ -0,0 +1,14 @@ +SRCS_$(d)+=\ + $(d)/sort/double.c\ + $(d)/sort/float.c\ + $(d)/sort/int.c\ + $(d)/sort/int16.c\ + $(d)/sort/int32.c\ + $(d)/sort/int64.c\ + $(d)/sort/int8.c\ + $(d)/sort/string.c\ + $(d)/sort/uint.c\ + $(d)/sort/uint16.c\ + $(d)/sort/uint32.c\ + $(d)/sort/uint64.c\ + $(d)/sort/uint8.c\ diff --git a/src/base/sort/string.c b/src/base/sort/string.c new file mode 100644 index 0000000..b511efa --- /dev/null +++ b/src/base/sort/string.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·string(uintptr sz, byte* arr[]) +{ + byte *tmp; +#define LESS(i, j) (strcmp(arr[i], arr[j]) < 0) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/uint.c b/src/base/sort/uint.c new file mode 100644 index 0000000..5b27330 --- /dev/null +++ b/src/base/sort/uint.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint(uintptr sz, uint arr[]) +{ + uint tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/uint16.c b/src/base/sort/uint16.c new file mode 100644 index 0000000..2b635b4 --- /dev/null +++ b/src/base/sort/uint16.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint16(uintptr sz, uint16 arr[]) +{ + uint16 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/uint32.c b/src/base/sort/uint32.c new file mode 100644 index 0000000..99a58cf --- /dev/null +++ b/src/base/sort/uint32.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint32(uintptr sz, uint32 arr[]) +{ + uint32 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/uint64.c b/src/base/sort/uint64.c new file mode 100644 index 0000000..2769825 --- /dev/null +++ b/src/base/sort/uint64.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint64(uintptr sz, uint64 arr[]) +{ + uint64 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/sort/uint8.c b/src/base/sort/uint8.c new file mode 100644 index 0000000..ff02b3c --- /dev/null +++ b/src/base/sort/uint8.c @@ -0,0 +1,12 @@ +#include "internal.h" + +void +sort·uint8(uintptr sz, uint8 arr[]) +{ + uint8 tmp; +#define LESS(i, j) (arr[i] < arr[j]) +#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) + QSORT(sz, LESS, SWAP); +#undef SWAP +#undef LESS +} diff --git a/src/base/string/append.c b/src/base/string/append.c new file mode 100644 index 0000000..d4d0396 --- /dev/null +++ b/src/base/string/append.c @@ -0,0 +1,53 @@ +#include "internal.h" + +// append will append the given null terminated c string to the string data +// structure. this variant can append a substring of length len of the given +// string to our buffer. the result is reallocated if not enough room is present +// in the buffer. +int +str·appendlen(string *s, vlong n, const byte* b) +{ + /* + bl = strlen(b); + if (n > bl) panicf("attempted to make a substring longer than string"); + */ + + str·grow(s, n); + if(*s == nil) + return 0; + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + + memcpy(*s + str·len(*s), b, n); + h->len += n; + (*s)[h->len] = '\0'; + + return n; +} + +// append will append the given null terminated c string to the string data +// structure. this variant will append the entire string. +int +str·append(string *s, const byte* b) +{ + return str·appendlen(s, strlen(b), b); +} + +// appendbyte will append the given byte to our string. +// NOTE: as the byte is on the stack, it is not null-terminated. +// can not pass to the above functions. +int +str·appendbyte(string *s, const byte b) +{ + str·grow(s, 1); + if(*s == nil) + return 0; + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + + *(*s + str·len(*s)) = b; + h->len++; + (*s)[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..? + + return 1; +} diff --git a/src/base/string/appendf.c b/src/base/string/appendf.c new file mode 100644 index 0000000..4b8d76c --- /dev/null +++ b/src/base/string/appendf.c @@ -0,0 +1,31 @@ +#include "internal.h" + +/* + * appendf will append the given formatted string to our buffer. + * returns the newly minted string + */ + +int +str·appendf(string *s, const byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int remain = str·cap(*s) - str·len(*s); + int n = vsnprintf(*s + str·len(*s), remain + 1, fmt, args); + va_end(args); + + if(n > remain){ + // If the first write was incomplete, we overwite the data again. + str·grow(s, n); + va_list args; + va_start(args, fmt); + n = vsnprintf(*s + str·len(*s), n + 1, fmt, args); + assert(n - remain <= str·cap(*s)); + va_end(args); + } + + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + h->len += n; + + return n; +} diff --git a/src/base/string/clear.c b/src/base/string/clear.c new file mode 100644 index 0000000..986f809 --- /dev/null +++ b/src/base/string/clear.c @@ -0,0 +1,9 @@ +#include "internal.h" + +void +str·clear(string *s) +{ + Hdr* h = (Hdr*)(*s - sizeof(Hdr)); + h->len = 0; + *s[0] = '\0'; +} diff --git a/src/base/string/copyn.c b/src/base/string/copyn.c new file mode 100644 index 0000000..09c2879 --- /dev/null +++ b/src/base/string/copyn.c @@ -0,0 +1,11 @@ +#include "internal.h" + +char * +str·copyn(char *dst, char *src, int n) +{ + while(*src && n-- > 0) + *dst++ = *src++; + + *dst = 0; + return dst; +} diff --git a/src/base/string/equals.c b/src/base/string/equals.c new file mode 100644 index 0000000..a975cf5 --- /dev/null +++ b/src/base/string/equals.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// Equals returns true if string s and t are equivalent. +bool +str·equals(const string s, const string t) +{ + vlong sL = str·len(s); + vlong tL = str·len(t); + if (sL != tL) return false; + + return memcmp(s, t, sL) == 0; +} diff --git a/src/base/string/find.c b/src/base/string/find.c new file mode 100644 index 0000000..20f990e --- /dev/null +++ b/src/base/string/find.c @@ -0,0 +1,11 @@ +#include "internal.h" + +// find will find the first occurence of +// substr in the string returns -1 if nothing was found. +int +str·find(string s, const byte* substr) +{ + byte* loc = strstr(s, substr); + if (loc == nil) return -1; + return (int)(loc - s); +} diff --git a/src/base/string/fit.c b/src/base/string/fit.c new file mode 100644 index 0000000..56ab041 --- /dev/null +++ b/src/base/string/fit.c @@ -0,0 +1,20 @@ +#include "internal.h" + +// fit reallocates the string such that the buffer is exactly sized for the +// buffer. if the capacity equals the length, then the function is a noop. the +// byte array is unchanged. +void +str·fit(string *s) +{ + Hdr* h; + vlong cap = str·cap(*s); + vlong len = str·len(*s); + + if (cap == len) return; + + h = (Hdr*)(s - sizeof(Hdr)); + h = realloc(h, sizeof(*h) + len + 1); + h->cap = len; + + *s = h->buf; +} diff --git a/src/base/string/free.c b/src/base/string/free.c new file mode 100644 index 0000000..7b5ee98 --- /dev/null +++ b/src/base/string/free.c @@ -0,0 +1,8 @@ +#include "internal.h" + +// free returns memory associated to the buffer. +void +str·free(string s) +{ + free(s - sizeof(Hdr)); +} diff --git a/src/base/string/grow.c b/src/base/string/grow.c new file mode 100644 index 0000000..39a9d2f --- /dev/null +++ b/src/base/string/grow.c @@ -0,0 +1,33 @@ +#include "internal.h" + +// grow ensures that the string can encompass at least delta bytes. +// if it already can, this is a no op. +// if it can't, the string will be reallocated. +void +str·grow(string *s, vlong delta) +{ + Hdr *h, *newh; + vlong cap = str·cap(*s); + vlong len = str·len(*s); + assert(cap >= len); // To prevent unsigned behavior + + if (cap - len >= delta) return; + + h = (Hdr*)(*s - sizeof(Hdr)); + + vlong newCap = cap + delta; + assert(newCap >= cap); // To prevent unsigned behavior + if (newCap < MAX_STRING_ALLOC) { + newCap *= 2; + } else + newCap += MAX_STRING_ALLOC; + + newh = (Hdr*)realloc(h, sizeof(*h) + newCap + 1); + if (newh == nil) return; + + memset(newh->buf + len, '\0', newCap - len); + newh->cap = newCap; + newh->len = len; + + *s = newh->buf; +} diff --git a/src/base/string/internal.h b/src/base/string/internal.h new file mode 100644 index 0000000..8c16c64 --- /dev/null +++ b/src/base/string/internal.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +#define MAX_STRING_ALLOC 1024 * 1024 + +typedef struct Hdr +{ + vlong len; + vlong cap; + byte buf[]; +} Hdr; diff --git a/src/base/string/join.c b/src/base/string/join.c new file mode 100644 index 0000000..fb97b6c --- /dev/null +++ b/src/base/string/join.c @@ -0,0 +1,16 @@ +#include "internal.h" + +string +str·join(vlong len, byte** fields, const byte* sep) +{ + string s = str·makecap("", 0, 10); + int j = 0; + + for (j = 0; j < len; j++) { + str·append(&s, fields[j]); + if (j < len - 1) + str·appendlen(&s, 1, sep); + } + + return s; +} diff --git a/src/base/string/len.c b/src/base/string/len.c new file mode 100644 index 0000000..5e42919 --- /dev/null +++ b/src/base/string/len.c @@ -0,0 +1,17 @@ +#include "internal.h" + +// len returns the length of the string. +int +str·len(const string s) +{ + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + return h->len; +} + +// cap returns the capacity of the string buffer. +int +str·cap(const string s) +{ + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + return h->cap; +} diff --git a/src/base/string/lower.c b/src/base/string/lower.c new file mode 100644 index 0000000..c6935f8 --- /dev/null +++ b/src/base/string/lower.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// lower will force all runes in the string to be lowercase +void +str·lower(string s) +{ + byte *b, *e; + b = s; + e = b + str·len(s); + while (b++ != e) + *b = tolower(*b); +} diff --git a/src/base/string/make.c b/src/base/string/make.c new file mode 100644 index 0000000..eb71543 --- /dev/null +++ b/src/base/string/make.c @@ -0,0 +1,53 @@ +#include "internal.h" + +// new returns a new dynamic string object, initialized from the given c string. +// len defines the length of the c substring that we will copy into our buffer. +// the backing buffer will have capacity cap. +string +str·makecap(const byte *s, vlong len, vlong cap) +{ + struct Hdr* h; + + h = malloc(sizeof(*h) + cap + 1); + if (s == nil) memset(h, 0, sizeof(*h)); + + if (h == nil) return nil; // Allocation failed. + + h->len = (s == nil) ? 0 : len; + h->cap = cap; + + if (cap < h->len) goto cleanup; + + if (s != nil && cap > 0) { + memcpy(h->buf, s, h->len); + memset(h->buf + h->len, '\0', h->cap - h->len + 1); + } + + return h->buf; + +cleanup: + free(h); + panicf("Attempted to create a string with less capacity than length"); + return nil; +} + +// new returns a new dynamic string object, initialized from the given c string. +// the backing buffer capacity is equivalent to the string length. +string +str·makelen(const byte *s, vlong len) +{ + vlong sl = (!s) ? 0 : strlen(s); + if (sl < len) panicf("attempted to take a bigger substring than string length"); + + vlong cap = (len == 0) ? 1 : len; + return str·makecap(s, len, cap); +} + +// new returns a new dynamic string object, initialized from the given c string. +// the backing buffer capacity is equivalent to the string length. +string +str·make(const byte *s) +{ + vlong len = (!s) ? 0 : strlen(s); + return str·makelen(s, len); +} diff --git a/src/base/string/makef.c b/src/base/string/makef.c new file mode 100644 index 0000000..8fb9c38 --- /dev/null +++ b/src/base/string/makef.c @@ -0,0 +1,25 @@ +#include "internal.h" + +// Newf returns a new dynamic string object +string +str·makef(const byte *fmt, ...) +{ + vlong n; + string s; + va_list args; + + va_start(args, fmt); + n = vsnprintf(nil, 0, fmt, args); + va_end(args); + + s = str·makecap(nil, 0, n); + + va_start(args, fmt); + vsnprintf(s, n + 1, fmt, args); + va_end(args); + + Hdr* h = (Hdr*)(s - sizeof(Hdr)); + h->len = n; + + return s; +} diff --git a/src/base/string/read.c b/src/base/string/read.c new file mode 100644 index 0000000..df2028f --- /dev/null +++ b/src/base/string/read.c @@ -0,0 +1,12 @@ +#include "internal.h" + +int +str·read(string s, int size, int n, void *buf) +{ + int len; + + len = MIN(n * size, str·len(s)); + memcpy(buf, s, len); + + return len; +} diff --git a/src/base/string/replace.c b/src/base/string/replace.c new file mode 100644 index 0000000..127daed --- /dev/null +++ b/src/base/string/replace.c @@ -0,0 +1,26 @@ +#include "internal.h" + +// replace will replace all occurences of the given bytes 'from' to bytes 'to' +// edits are done in place and modify the string. +// NOTE: as of now strings from and to must be the same size. +void +str·replace(string s, const byte* from, const byte* to) +{ + vlong fromL = strlen(from); + vlong toL = strlen(to); + if (toL != fromL) { panicf("different sized replacement string not supported"); } + + vlong l = str·len(s); + vlong i = l; + vlong j = l; + + for (i = 0; i < l; i++) { + for (j = 0; j < toL; j++) { + if (s[i] == from[j]) { + s[i] = to[j]; + break; + } + } + } +} + diff --git a/src/base/string/rules.mk b/src/base/string/rules.mk new file mode 100644 index 0000000..e517ca5 --- /dev/null +++ b/src/base/string/rules.mk @@ -0,0 +1,19 @@ +SRCS_$(d)+=\ + $(d)/string/append.c\ + $(d)/string/appendf.c\ + $(d)/string/clear.c\ + $(d)/string/copyn.c\ + $(d)/string/equals.c\ + $(d)/string/find.c\ + $(d)/string/fit.c\ + $(d)/string/free.c\ + $(d)/string/grow.c\ + $(d)/string/join.c\ + $(d)/string/len.c\ + $(d)/string/lower.c\ + $(d)/string/make.c\ + $(d)/string/makef.c\ + $(d)/string/read.c\ + $(d)/string/replace.c\ + $(d)/string/split.c\ + $(d)/string/upper.c\ diff --git a/src/base/string/split.c b/src/base/string/split.c new file mode 100644 index 0000000..2aa68b4 --- /dev/null +++ b/src/base/string/split.c @@ -0,0 +1,39 @@ +#include "internal.h" + +// split will split the string by the given token. +// returns a stretchy buffer of strings that result from the partition. +// it is the caller's responsibility to clean the memory. +string* +str·split(string s, const byte* tok) +{ + string* fields = nil; + vlong start = 0; + + vlong sL = str·len(s); + vlong tokL = strlen(tok); + if (sL == 0 || tokL == 0) return nil; + + buffit(fields, 5); + + for (vlong i = 0; i < sL - tokL; i++) { + if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) { + bufpush(fields, str·makelen(s + start, i - start)); + if (fields[buflen(fields) - 1] == nil) goto cleanup; + + start = i + tokL; + i += tokL - 1; + } + } + + bufpush(fields, str·makelen(s + start, sL - start)); + + return fields; + +cleanup: + for (vlong i = 0; i < buflen(fields); i++) { + str·free(fields[i]); + } + buffree(fields); + return nil; +} + diff --git a/src/base/string/upper.c b/src/base/string/upper.c new file mode 100644 index 0000000..ab692c1 --- /dev/null +++ b/src/base/string/upper.c @@ -0,0 +1,12 @@ +#include "internal.h" + +// Upper will force all runes in the string to be uppercase. +void +str·upper(string s) +{ + byte *b, *e; + b = s; + e = b + str·len(s); + while (b++ != e) + *b = toupper(*b); +} diff --git a/src/base/test.c b/src/base/test.c new file mode 100644 index 0000000..a29be1d --- /dev/null +++ b/src/base/test.c @@ -0,0 +1,170 @@ +#include +#include +#include + +#include + +uintptr +printtest(Coro *c, uintptr d) +{ + printf("--> Recieved %lu\n", d); + d = coro·yield(c, d+10); + printf("--> Now %lu\n", d); + + return d; +} + +uintptr +sequence(Coro *c, uintptr start) +{ + int d = start; + for (;;) { + coro·yield(c, d++); + } + + return d; +} + +struct PrimeMsg +{ + Coro *seq; + int p; +}; + +uintptr +filter(Coro *c, uintptr data) +{ + int x, p; + Coro *seq; + struct PrimeMsg *msg; + + // Need to copy relevant variables onto the local stack + // Data is volatile. + msg = (struct PrimeMsg*)data; + seq = msg->seq; + p = msg->p; + + for (;;) { + x = coro·yield(seq, x); + if (x % p != 0) { + x = coro·yield(c, x); + } + } + + return 0; +} + +error +test·coro() +{ + int i; + Coro *c[4]; + uintptr d; + + printf("Starting singleton test\n"); + + for (i = 0; i < arrlen(c); i++) { + c[i] = coro·make(0, &printtest); + } + + /* Singleton test */ + d = 0; + for (i = 0; i < 10; i++) { + d = coro·yield(c[0], d); + } + + printf("Starting triplet test\n"); + + /* Triplet test */ + for (i = 0; i < 10; i++) { + d = coro·yield(c[1], d); + d = coro·yield(c[2], d+100); + d = coro·yield(c[3], d+200); + } + + for (i = 0; i < arrlen(c); i++) { + coro·free(c[i]); + } + + /* Prime sieve */ + printf("Starting prime test\n"); + uintptr num; + Coro *cur, *seq[50]; + + num = 2; + seq[0] = coro·make(4096, &sequence); + cur = *seq; + + num = coro·yield(cur, num); + for (i = 1; i < arrlen(seq); i++) { + seq[i] = coro·make(4096, &filter); + struct PrimeMsg msg = { + .seq = cur, + .p = num, + }; + cur = seq[i]; + num = coro·yield(cur, (uintptr)&msg); + printf("--> prime number %lu\n", num); + } + return 0; +} + +int +less(void* a, void* b) +{ + int ai, bi; + ai = *(int*)a; + bi = *(int*)b; + + return ai - bi; +} + +error +test·sort() +{ + clock_t t; + int i, test[10000]; + for (i = 0; i < arrlen(test); i++) { + test[i] = rand(); + } + + t = clock(); + sort·int(arrlen(test), test); + t = clock() - t; + printf("inlined code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); + + for (i = 0; i < arrlen(test); i++) { + test[i] = rand(); + } + + t = clock(); + qsort(test, arrlen(test), sizeof(int), (int (*)(const void *, const void *))less); + t = clock() - t; + printf("std qsort code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); + + /* + for (i = 1; i < arrlen(test); i++) { + if (test[i] >= test[i-1]) { + printf("%d is less that %d\n", test[i], test[i-1]); + } else { + printf("ERROR: %d is NOT less that %d\n", test[i], test[i-1]); + } + } + */ + + return 0; +} + +error +main() +{ + error err; +#if 0 + if (err = test·coro(), err) { + errorf("test fail: coroutine"); + } +#endif + if (err = test·sort(), err) { + errorf("test fail: coroutine"); + } +} diff --git a/src/cmd/cc/ast.c b/src/cmd/cc/ast.c new file mode 100644 index 0000000..4330bcc --- /dev/null +++ b/src/cmd/cc/ast.c @@ -0,0 +1,2139 @@ +#include "cc.h" + +// ----------------------------------------------------------------------- +// helper macros + +#define alloc(ptr) (ptr) = mem·arenaalloc(C.heap, 1, sizeof *(ptr)) +#define copyarray(dst, arr, n) (dst) = mem·arenaalloc(C.heap, (n), sizeof *(arr)), memcpy((dst), (arr), n * sizeof *(arr)) +#define movearray(dst, arr, n) copyarray(dst,arr,n), free(arr) + +#define attop(prs) ((uintptr)prs->sp == (uintptr)prs->spstk) +#define peek(p, i) (p->tok[i]) +#define iskw(t, k) (((t).kind == Akeywd) && (t).val.i == (k)) +#define advance(p, l) (p->tok[0] = p->tok[1], p->tok[1] = lex(l), p->tok[0]) + +#define Bit(i) (1 << (i)) + +// ----------------------------------------------------------------------- +// helper functions + +static +string +nameof(Name *n) +{ + switch (n->kind) { + /* 0 corresponds to no state - i.e. an abstract name */ + case Nnil: + return nil; + case Nident: + return n->ident; + case Nparen: + return nameof(n->paren->name); + case Nindex: + case Ncall: + return nameof(n->sfx.name); + } + panicf("unreachable"); + return nil; +} + +static +void +openscope(Parser *p) +{ + if (++p->sp >= arrend(p->spstk)) + panicf("scope stack overflow"); +} + +/* + * TODO: save the symbol table with the ast node + * write a "copy(move)scope" + */ + +static +void +closescope(Parser *p) +{ + if (p->sp <= p->spstk) + panicf("scope stack underflow"); + + forgetall(&p->sp->objs); + forgetall(&p->sp->tags); + p->sp--; +} + +/* temporary stack helpers */ +static +Name* +getname(Parser *p) +{ + if (p->nm >= arrend(p->nmstk)) + panicf("name stack overflow"); + return p->nm++; +} + +static void putdtor(Parser *p, Dtor *dt); + +static +void +putname(Parser *p, Name *n) +{ + if (p->nm <= p->nmstk) + panicf("name stack underflow"); + + switch (n->kind) { + case Nnil: + case Nident: + break; + case Nparen: + putdtor(p, n->paren); + break; + case Nindex: + case Ncall: + putname(p, n->sfx.name); + break; + default: + panicf("unrecognized name kind"); + } + *p->nm-- = (Name){0}; +} + +static +Ptr* +getptr(Parser *p) +{ + if (p->pt >= arrend(p->ptstk)) + panicf("pointer stack overflow"); + + return p->pt++; +} + +static +void +putptr(Parser *p, Ptr *ptr) +{ + if (p->pt <= p->ptstk) + panicf("pointer stack underflow"); + + while ((ptr = ptr->link)) + putptr(p, ptr); + + *p->pt-- = (Ptr){0}; +} + + +static +Dtor* +getdtor(Parser *p) +{ + if (p->dt >= arrend(p->dtstk)) + panicf("dtor stack overflow"); + + p->dt->name = getname(p); + return p->dt++; +} + +static +void +putdtor(Parser *p, Dtor *dt) +{ + if (p->dt <= p->dtstk) + panicf("dtor stack underflow"); + + /* release the pointer overflow if we had to use it */ + if (p->dt->ptr.link) + putptr(p, p->dt->ptr.link); + + /* the dtor could encompass multiple names hierarchically */ + putname(p, dt->name); + *p->dt-- = (Dtor){0}; +} + +/* TODO: This will fail for forward declarations */ +static +void +declareobj(Parser *p, Decl *d) +{ + Sym *sym; + string ident; + uint32 kind; + struct Decls *link; + + switch (d->kind) { + case Dfunc: + case Dvar: + kind = Svar; + goto one; + case Dtype: + kind = Stype; + one: + ident = d->name; + break; + + case Dvars: + kind = Svar; + goto many; + case Dtypes: + kind = Stype; + many: + while (link = &d->list, link != nil) { + ident = link->name; + sym = lookup(&p->sp->objs, ident); + if (sym) { + errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident); + return; + } + sym = define(&p->sp->objs, ident, kind); + if (kind == Svar) + sym->obj = d; + else + sym->type = d->type; + } + break; + + default: + panicf("unrecognized node kind %d. expected declaration", d->kind); + } + sym = lookup(&p->sp->objs, ident); + if (sym) { + errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident); + return; + } + sym = define(&p->sp->objs, ident, kind); + if (kind == Svar) + sym->obj = d; + else + sym->type = d->type; +} + +/* enters the object identifier space */ +static +void +declareenum(Parser *p, int n, string *elts, Expr *vals) +{ + int i; + Sym *s; + + for (i = 0; i < n; i++) { + s = lookup(&p->sp->objs, elts[i]); + if (s) { + errorat(peek(p, 0).pos, "redeclaration of name %s in object space", elts[i]); + continue; + } + s = define(&p->sp->objs, elts[i], Senum); + s->val = vals + i; + } +} + +static +void +declaretag(Parser *p, uint32 t, string name) +{ + Sym *sym; + sym = lookup(&p->sp->tags, name); + if (sym) { + errorat(peek(p, 0).pos, "redeclaration of name '%s' in tag space", name); + return; + } + + sym = define(&p->sp->tags, name, Stype); + sym->type = t; +} + +static +Sym * +lookupobj(Parser *p, string ident) +{ + Sym *sym; + Scope *it; + + it = p->sp; + do { + sym = lookup(&it->objs, ident); + } while (sym == nil && --it >= p->spstk); + + return sym; +} + +static +Sym * +lookuptag(Parser *p, string ident) +{ + Sym *sym; + Scope *it; + + it = p->sp; + do { + sym = lookup(&it->tags, ident); + } while (sym == nil && --it >= p->spstk); + + return sym; +} + +static +int +nomatch(Token t, vlong kind) +{ + if (t.kind == kind) + return 0; + + if (t.kind == Akeywd) + errorat(t.pos, "expected token '%s', instead found keyword '%s'", tokens[kind], keywords[t.val.i]); + else + errorat(t.pos, "expected token '%s', instead found '%s'", tokens[kind], tokens[t.kind]); + return 1; +} + +// ----------------------------------------------------------------------- +// needed forward declarations + +static error spec(Parser *, Lexer *, uint64 *); +static uint32 basetype(Parser *, Lexer *, uint64 *s); +static string namedecl(Parser *, Lexer *, uint32 *, int); +static uint32 typename(Parser *, Lexer *, uint32 *); + +static error dtor(Parser *p, Lexer *lx, Dtor *d, int ab); +static uint32 typeofdtor(Dtor *, uint32); + + +static Decl *decl(Parser *, Lexer *); + +static Expr *ternary(Parser *, Lexer *); +static Expr *expr(Parser *, Lexer *); + +static error blkstmt(Parser *, Lexer *, Stmt **); + + +// ----------------------------------------------------------------------- +// expressions + +#define MAKEX(x, state) alloc((x)), (x)->kind = X##state + +static +Expr* +primary(Parser *p, Lexer *lx) +{ + int k; + Expr *x; + Token t; + Pos b; + + t = peek(p, 0); + b = t.pos; + switch (k = (t.kind & Vmask)) { + case Aident: + MAKEX(x, ident); + x->pos.beg = b; + x->pos.end = lx->pos; + x->name = t.val.s; + break; + + case Alit: + MAKEX(x, lit); + x->pos.beg = b; + x->pos.end = lx->pos; + x->val.kind = t.kind & ~Vmask; + x->val.v = t.val; + break; + + case Alparen: + advance(p, lx); + x = expr(p, lx); + t = peek(p, 0); + if (nomatch(t, Arparen)) { + errorat(lx->pos, "unterminated paren expression"); + goto Bad; + } + break; + + default: + panicf("unreachable"); + } + + advance(p, lx); + return x; +Bad: + errorat(lx->pos, "unable to parse operand expression"); + return nil; +} + +static +int +istypename(Parser *p, Token t) +{ + Sym *sym; + + if (t.kind == Akeywd && (Kconst <= t.val.i && t.val.i <= Kenum)) + return 1; + if (t.kind == Aident) { + sym = lookupobj(p, t.val.s); + return (sym != nil) && sym->kind == Stype; + } + + return 0; +} + +static Expr* initx(Parser *p, Lexer *lx); + +static +Expr* +initlist(Parser *p, Lexer *lx) +{ + Token t; + int c, n; + Expr *x, **a; + struct Key *k; + + MAKEX(x, initlist); + x->pos.beg = lx->pos; + x->init.n = 0; + if (t.kind == Arbrace) { + x->init.k = nil; + x->init.v = nil; + return x; + } + + c = 0; + n = 0; + a = nil; + k = nil; +Key0: + if (n >= c) { + c += 20; + k = realloc(k, c * sizeof(*k)); + a = realloc(a, c * sizeof(*a)); + } +Key1: + switch (t.kind) { + case Adot: + t = advance(p, lx); + if (t.kind != Aident) { + errorat(t.pos, "dot designator must be followed by identifier"); + goto Bad; + } + k[n++] = (struct Key) { + .kind = (uint32)x->init.n, + .s = t.val.s, + }; + t = advance(p, lx); + goto Key0; + + case Albrakt: + t = advance(p, lx); + k[n++] = (struct Key) { + .kind = (uint32)x->init.n | (1ULL << 32), + .x = expr(p, lx), + }; + t = peek(p, 0); + goto Key0; + + case Aeq: + t = advance(p, lx); + /* fallthrough */ + default: + a[x->init.n++] = initx(p, lx); + + t = peek(p, 0); + switch (t.kind) { + case Arbrace: + break; + case Acomma: + advance(p, lx); + /* fallthrough */ + default: + goto Key0; + } + break; + + case Acomma: + t = advance(p, lx); + break; + } + movearray(x->init.k, k, n); + movearray(x->init.v, a, x->init.n); + return x; +Bad: + errorat(t.pos, "could not parse initializer list"); + return nil; +} + +static +Expr* +initx(Parser *p, Lexer *lx) +{ + Expr *x; + Token t; + + t = peek(p, 0); + if (t.kind != Albrace) + return ternary(p, lx); + + advance(p, lx); + x = initlist(p, lx); + t = peek(p, 0); + if (nomatch(t, Arbrace)) { + errorat(t.pos, "unmatched brace in initializer list, found %s instead", tokens[t.kind]); + advance(p, lx); + } + + return x; +} + +static +Expr* +postfix(Parser *p, Lexer *lx) +{ + Pos b; + Token t; + int c, n; + uint32 type, qual; + Expr *x, *y, **a; + + t = peek(p, 0); + if (t.kind == Alparen) + if (istypename(p, peek(p, 1))) { + t = advance(p, lx); + type = typename(p, lx, &qual); + t = peek(p, 0); + if (nomatch(t, Arparen)) { + errorat(lx->pos, "unmatched paren: found %s instead", tokens[t.kind]); + goto Bad; + } + t = advance(p, lx); + if (nomatch(t, Albrace)) { + errorat(lx->pos, "bad initializer list: found %s", tokens[t.kind]); + goto Bad; + } + + x = initlist(p, lx); + + t = peek(p, 0); + if (nomatch(t, Arbrace)) { + errorat(lx->pos, "unmatched brace: found %s instead", tokens[t.kind]); + goto Bad; + } + + x->type = type; + x->qual = qual; + return x; + } + + x = primary(p, lx); + t = peek(p, 0); + for (;;) { + b = x->pos.beg; + switch (t.kind) { + case Ainc: + MAKEX(y, postinc); + goto Postfix; + case Adec: + MAKEX(y, postdec); + Postfix: + y->pos.beg = b; + y->pos.end = lx->pos; + y->unary.post = x; + x = y, y = nil; + break; + + case Adot: + MAKEX(y, self); + goto Select; + case Aarrow: + MAKEX(y, selp); + Select: + t = advance(p, lx); + if (t.kind != Aident) { + errorat(t.pos, "invalid operand of selector expression"); + goto Bad; + } + y->pos.beg = b; + y->pos.end = lx->pos; + + y->idx.f = t.val.s; + y->idx.x = x; + x = y, y = nil; + break; + + case Albrakt: + t = advance(p, lx); + if (t.kind == Arbrakt) { + errorat(t.pos, "empty index expression"); + goto Bad; + } + MAKEX(y, index); + y->idx.x = x; + y->idx.i = expr(p, lx); + + t = peek(p, 0); + if (t.kind != Albrakt) { + errorat(t.pos, "malformed index expression"); + goto Bad; + } + + x = y, y = nil; + break; + + case Alparen: + t = advance(p, lx); + MAKEX(y, call); + y->call.fn = x; + y->pos.beg = b; + y->call.n = 0; + if (t.kind == Arparen) { + y->call.arg = nil; + goto Endfunc; + } + c = 0; + a = nil; + Arg: + if (y->call.n >= c) { + c += 20; + a = realloc(a, c * sizeof(*a)); + } + a[y->call.n++] = expr(p, lx); + t = peek(p, 0); + if (t.kind == Acomma) { + advance(p, lx); + goto Arg; + } + if (t.kind != Arparen) { + errorat(t.pos, "invalid token '%s' found in call argument"); + goto Bad; + } + movearray(y->call.arg, a, y->call.n); + Endfunc: + y->pos.end = lx->pos; + x = y, y = nil; + break; + + default: + return x; + } + t = advance(p, lx); + } + return x; +Bad: + errorat(lx->pos, "failed to parse primary expression"); + return nil; +} + +static +uint32 +typename(Parser *p, Lexer *lx, uint32 *spec) +{ + uint32 base; + uint64 s; + + base = basetype(p, lx, &s); + if (!base) { + errorat(lx->pos, "failed to parse type name specifiers"); + return 0; + } + *spec = (uint32)s; + namedecl(p, lx, &base, 1); + + return base; +} + +static Expr* cast(Parser *p, Lexer *lx); + +static +Expr* +unary(Parser *p, Lexer *lx) +{ + Expr *x; + Token t; + + t = peek(p, 0); + switch (t.kind) { + case Ainc: MAKEX(x, preinc); goto Prefix; + case Adec: MAKEX(x, predec); /* fallthrough */ + Prefix: + advance(p, lx); + x->pos.beg = t.pos; + x->unary.pre = unary(p, lx); + x->pos.end = x->unary.pre->pos.end; + return x; + + case Aneg: MAKEX(x, neg); goto Unary; + case Aand: MAKEX(x, ref); goto Unary; + case Anot: MAKEX(x, not); goto Unary; + case Astar: MAKEX(x, star); goto Unary; + case Aadd: MAKEX(x, plus); goto Unary; + case Asub: MAKEX(x, minus); /* fallthrough */ + Unary: + advance(p, lx); + x->pos.beg = t.pos; + x->unary.pre = cast(p, lx); + x->pos.end = x->unary.pre->pos.end; + return x; + + case Akeywd: + switch (t.val.i) { + case Ksizeof: + MAKEX(x, sizeof); + goto Key; + case Kalignof: + MAKEX(x, alignof); + /* fallthrough */ + Key: + t = advance(p, lx); + if (t.kind == Alparen) + if (istypename(p, peek(p, 1))) { + t = advance(p, lx); + x->info.type = 0; + x->info.of.type = typename(p, lx, &x->info.of.qual); + + t = peek(p, 0); + if (nomatch(t, Arparen)) { + errorat(t.pos, "missing paren for size/alignof statement"); + goto Bad; + } + advance(p, lx); + return x; + } + + x->info.type = 1; + x->info.x = unary(p, lx); + return x; + + default: + ; + } + /* fallthrough */ + default: + return postfix(p, lx); + } +Bad: + return nil; +} + +static +Expr* +cast(Parser *p, Lexer *lx) +{ + Expr *x; + Token t; + + t = peek(p, 0); + if (t.kind == Alparen && istypename(p, peek(p,1))) { + t = advance(p, lx); + MAKEX(x, cast); + + x->pos.beg = t.pos; + x->cast.to.type = typename(p, lx, &x->cast.to.qual); + if (!x->cast.to.type) { + errorat(lx->pos, "invalid type operand of cast"); + goto Bad; + } + + t = peek(p, 0); + if (nomatch(t, Arparen)) { + errorat(lx->pos, "missing closing paren after cast expression"); + goto Bad; + } + advance(p, lx); + + x->cast.x = cast(p, lx); + x->pos.beg = lx->pos; + return x; + } + return unary(p, lx); + +Bad: + errorat(lx->pos, "failed to parse cast expression"); + return nil; +} + +/* static data for binary operators */ +#define OPERATORS \ + OPERATOR(Astar, 10, Xmul) \ + OPERATOR(Adiv, 10, Xdiv) \ + OPERATOR(Amod, 10, Xmod) \ + OPERATOR(Aadd, 9, Xadd) \ + OPERATOR(Asub, 9, Xsub) \ + OPERATOR(Alsft, 8, Xlsft) \ + OPERATOR(Arsft, 8, Xrsft) \ + OPERATOR(Agteq, 7, Xgteq) \ + OPERATOR(Alteq, 7, Xlteq) \ + OPERATOR(Alt, 7, Xlt) \ + OPERATOR(Agt, 7, Xgt) \ + OPERATOR(Aeq, 6, Xeql) \ + OPERATOR(Aneq, 6, Xneq) \ + OPERATOR(Aand, 5, Xand) \ + OPERATOR(Axor, 4, Xxor) \ + OPERATOR(Aor, 3, Xor) \ + OPERATOR(Aandand, 2, Xandand) \ + OPERATOR(Aoror, 1, Xoror) + +static int prectab[NUM_TOKENS] = +{ +#define OPERATOR(a, b, c) [a] = b, + OPERATORS +#undef OPERATOR +}; + +static int optab[NUM_TOKENS] = +{ +#define OPERATOR(a, b, c) [a] = c, + OPERATORS +#undef OPERATOR +}; +#undef OPERATORS + +static +Expr* +binary(Parser *p, Lexer *lx, int prec) +{ + Token t; + int k, np; + Expr *l, *x; + + l = cast(p, lx); + for (;;) { + t = peek(p, 0); + k = t.kind; + np = prectab[k]; + if (np < prec) + return l; + + alloc(x); + t = advance(p, lx); + + x->pos.beg = l->pos.beg; + x->kind = optab[k]; + x->binary.l = l; + x->binary.r = binary(p, lx, np + 1); + x->pos.end = x->binary.r->pos.end; + + l = x; + } + return l; +Bad: + errorat(t.pos, "failed to parse expression"); + return nil; +} + +static +Expr* +ternary(Parser *p, Lexer *lx) +{ + Pos b; + Token t; + Expr *x, *y; + + x = binary(p, lx, 1); + t = peek(p, 0); + b = t.pos; + + switch (t.kind) { + case Aqmark: + t = advance(p, lx); + y = x; + MAKEX(x, ternary); + x->pos.beg = b; + x->kind = Xternary; + x->cond.c = y; + x->cond.t = expr(p, lx); + + t = peek(p, 0); + if (nomatch(t, Acolon)) { + errorat(t.pos, "ternary expression missing ':'"); + goto Bad; + } + t = advance(p, lx); + x->cond.e = expr(p, lx); + x->pos.end = lx->pos; + break; + + case Aasn: MAKEX(y, asn); goto Assign; + case Aorasn: MAKEX(y, orasn); goto Assign; + case Axorasn: MAKEX(y, xorasn); goto Assign; + case Aandasn: MAKEX(y, andasn); goto Assign; + case Asubasn: MAKEX(y, subasn); goto Assign; + case Amulasn: MAKEX(y, mulasn); goto Assign; + case Adivasn: MAKEX(y, divasn); goto Assign; + case Amodasn: MAKEX(y, modasn); goto Assign; + case Alsftasn: MAKEX(y, lsftasn); goto Assign; + case Arsftasn: MAKEX(y, rsftasn); goto Assign; + Assign: + advance(p, lx); + + y->asn.l = x; + y->asn.r = ternary(p, lx); + x = y; + x->pos.beg = b; + x->pos.end = lx->pos; + break; + default: + ; + } + + return x; +Bad: + errorat(lx->pos, "failing expression parse"); + return nil; +} + +static +Expr* +expr(Parser *p, Lexer *lx) +{ + Pos b; + Token t; + Expr *x, *y; + + x = ternary(p, lx); + while (t = peek(p, 0), t.kind == Acomma) { + advance(p, lx); + y = x; + MAKEX(x, comma); + x->pos.beg = y->pos.beg; + x->comma.x[0] = y; + x->comma.x[1] = ternary(p, lx); + x->pos.end = lx->pos; + y = nil; + } + + return x; +} + +// ----------------------------------------------------------------------- +// statements + +static +struct Node* +stmt(Parser *p, Lexer *lx) +{ + int k; + Stmt *s; + Sym *sym; + Token t; + + t = peek(p, 0); + k = t.kind; + + /* intercept decl before allocating a statement */ + if (k == Aident) { + if (peek(p, 1).kind == Acolon) + goto Tlabel; + sym = lookupobj(p, t.val.s); + if (!sym) { + errorat(lx->pos, "unrecognized type identifier '%s'", t.val.s); + goto Bad; + } + + if (sym->kind == Stype) + goto Tdecl; + if (sym->kind == Svar) { + alloc(s); + s->pos.beg = lx->pos; + goto Texpr; + } + + errorat(lx->pos, "bad symbol type used as type identifier"); + goto Bad; + } + + if (k == Akeywd) { + if ((Kauto <= t.val.i && t.val.i <= Ktypedef) || (Kconst <= t.val.i && t.val.i <= Kenum)) { + Tdecl: + return (Node *)decl(p, lx); + } + } + + alloc(s); + s->pos.beg = lx->pos; + + switch (k) { + case Akeywd: + switch (k = t.val.i) { + case Kif: + t = advance(p, lx); + s->kind = Sif; + + if (nomatch(t, Alparen)) { + errorat(lx->pos, "missing opening paren before if conditional"); + goto Bad; + } + s->br.cond = expr(p, lx); + if (nomatch(t, Arparen)) { + errorat(lx->pos, "missing closing paren after if conditional"); + goto Bad; + } + s->br.body = stmt(p, lx); + + t = peek(p, 0); + if (iskw(t, Kelse)) + s->br.orelse = stmt(p, lx); + else + s->br.orelse = nil; + + break; + + case Kswitch: + t = advance(p, lx); + s->kind = Sswitch; + + if (nomatch(t, Alparen)) { + errorat(lx->pos, "missing opening paren before switch conditional"); + goto Bad; + } + s->br.cond = expr(p, lx); + if (nomatch(t, Arparen)) { + errorat(lx->pos, "missing closing paren after switch conditional"); + goto Bad; + } + s->br.body = stmt(p, lx); + s->br.orelse = nil; + + break; + + case Kfor: + t = advance(p, lx); + s->kind = Sfor; + + if (nomatch(t, Alparen)) { + errorat(lx->pos, "missing opening paren before for loop preamble"); + goto Bad; + } + + if (t.kind == Asemi) + s->loop.init = nil; + else { + // TODO: test for declaration + s->loop.init = (Node *)expr(p, lx); + } + + if (nomatch(t, Asemi)) { + errorat(lx->pos, "missing semicolon"); + goto Bad; + } + + if (t.kind == Asemi) + s->loop.cond = nil; + else + s->loop.cond = expr(p, lx); + + if (nomatch(t, Asemi)) { + errorat(lx->pos, "missing semicolon"); + goto Bad; + } + + if (t.kind == Asemi) + s->loop.step = nil; + else + s->loop.step = expr(p, lx); + + if (nomatch(t, Alparen)) { + errorat(lx->pos, "missing closing paren after for loop preamble"); + goto Bad; + } + s->loop.body = stmt(p, lx); + break; + + case Kwhile: + t = advance(p, lx); + s->kind = Swhile; + if (nomatch(t, Alparen)) { + errorat(lx->pos, "missing opening paren before while loop conditional"); + goto Bad; + } + s->loop.cond = expr(p, lx); + if (nomatch(t, Arparen)) { + errorat(t.pos, "missing closing paren after while loop conditional"); + goto Bad; + } + + s->loop.init = nil; + s->loop.step = nil; + s->loop.body = stmt(p, lx); + break; + + case Kdo: + t = advance(p, lx); + s->kind = Sdo; + s->loop.body = stmt(p, lx); + + if (!iskw(t, Kwhile)) { + errorat(t.pos, "missing while statement conditional after do body"); + goto Bad; + } + t = advance(p, lx); + if (nomatch(t, Alparen)) { + errorat(t.pos, "missing open paren after while conditional"); + goto Bad; + } + + s->loop.init = nil; + s->loop.step = nil; + s->loop.cond = expr(p, lx); + break; + + case Kgoto: + t = advance(p, lx); + s->kind = Sgoto; + if (t.kind != Aident) { + errorat(t.pos, "invalid argument to goto"); + goto Bad; + } + s->jmp.lbl = t.val.s; + t = advance(p, lx); + if (nomatch(t, Asemi)) { + errorat(t.pos, "missing semicolon after goto"); + goto Bad; + } + advance(p, lx); + break; + + case Kcontinue: + t = advance(p, lx); + s->kind = Scontin; + s->jmp.lbl = nil; + s->jmp.x = nil; + if (nomatch(t, Asemi)) { + errorat(t.pos, "missing semicolon after continue"); + goto Bad; + } + advance(p, lx); + break; + + case Kbreak: + t = advance(p, lx); + s->kind = Sbreak; + s->jmp.lbl = nil; + s->jmp.x = nil; + if (nomatch(t, Asemi)) { + errorat(t.pos, "missing semicolon after break"); + goto Bad; + } + advance(p, lx); + break; + + case Kreturn: + t = advance(p, lx); + s->kind = Sreturn; + + s->jmp.lbl = nil; + s->jmp.x = (t.kind == Asemi) ? nil : expr(p, lx); + + t = peek(p, 0); + if (nomatch(t, Asemi)) { + errorat(t.pos, "missing semicolon after return statement"); + goto Bad; + } + advance(p, lx); + break; + + case Kcase: + t = advance(p, lx); + s->kind = Scase; + s->lbl.x = expr(p, lx); + if (nomatch(t, Acolon)) { + errorat(t.pos, "missing colon after default label"); + goto Bad; + } + t = advance(p, lx); + s->lbl.stmt = stmt(p, lx); + break; + + case Kdefault: + t = advance(p, lx); + s->kind = Scase; + s->lbl.x = nil; + if (nomatch(t, Acolon)) { + errorat(t.pos, "missing colon after default label"); + goto Bad; + } + t = advance(p, lx); + s->lbl.stmt = stmt(p, lx); + break; + + default: + panicf("unexpected statement keyword %s", keywords[k]); + } + break; + case Albrace: + s->kind = Sblock; + openscope(p); + if (blkstmt(p, lx, &s)) { + errorat(lx->pos, "failed to parse block statement"); + goto Bad; + } + closescope(p); + break; + + case Asemi: + t = advance(p, lx); + s->kind = Sempty; + break; + + case Aident: + Tlabel: + t = advance(p, lx); + s->kind = Slabel; + if (nomatch(t, Acolon)) { + errorat(t.pos, "missing colon after labelled block"); + goto Bad; + } + t = advance(p, lx); + s->lbl.stmt = stmt(p, lx); + break; + + default: + Texpr: + s->kind = Sexpr; + s->x = expr(p, lx); + + t = peek(p, 0); + if (nomatch(t, Asemi)) { + errorat(t.pos, "missing semicolon after statement expression"); + goto Bad; + } + advance(p, lx); + } + + s->pos.end = lx->pos; + return (Node *)s; +Bad: + errorat(lx->pos, "failed to parse statement"); + return nil; +} + +static +error +blkstmt(Parser *p, Lexer *lx, Stmt **s) +{ + Token t; + int len; + int cap; + Node **ns; + + alloc(*s); + (*s)->kind = Sblock; + (*s)->pos.beg = lx->pos; + + t = peek(p, 0); + if (nomatch(t, Albrace)) + goto Bad; + t = advance(p, lx); + + len = 0, cap = 20; + ns = malloc(cap*sizeof(*ns)); + while (t.kind != Arbrace) { + if (cap == len) { + cap += 20; + ns = realloc(ns, cap*sizeof(*ns)); + } + ns[len++] = stmt(p, lx); + t = peek(p, 0); + } + advance(p, lx); + + (*s)->pos.end = lx->pos; + (*s)->blk.n = len; + movearray((*s)->blk.item, ns, len); + return 0; +Bad: + errorat(lx->pos, "failed to parse block statement"); + free(ns); + return 1; +} + +// ----------------------------------------------------------------------- +// types + +uint32 +ptrtype(uint32 base, uint32 qual) +{ + uint32 i; + Type *t; + + i = type(); + t = C.type.info + i; + t->kind = Tptr; + t->ptr.base = base; + t->ptr.qual = qual; + t->size = pointer.size; + t->align = pointer.align; + t->sign = pointer.sign; + + return i; +} + +uint32 +arraytype(uint32 base, uint32 qual, Expr *ix) +{ + int i, n; + Type *t; + + /* TODO: evaluate the length */ + n = 10; + i = type(); + t = C.type.info + i; + t->kind = Tarray; + t->ptr.base = base; + t->size = n * C.type.info[base].size; + t->align = C.type.info[base].align; + t->sign = 0; + + return i; +} + +uint32 +functype(uint32 ret, int n, Field *args, int dots) +{ + uint32 i; + Type *t; + + i = type(); + t = C.type.info + i; + t->kind = Tfunc; + t->size = pointer.size; + t->align = pointer.align; + t->sign = pointer.sign; + + t->func.ret = ret; + t->func.n = n; + t->func.arg = args; + t->func.dots = dots; + + return i; +} + +#define ALIGN_DOWN(n, a) ((n) & ~((a)-1)) +#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a)) +uint32 +structtype(int n, Field *field, Expr *bits) +{ + uint32 i; + Type *t; + Field *f, *e; + + i = type(); + t = C.type.info + i; + t->kind = Tstruct; + t->size = 0; + t->align = 0; + for (f = field, e = field+n; f != e; ++f) { + t->size += C.type.info[f->type].size + ALIGN_UP(t->size, C.type.info[f->type].align); + t->align = MAX(t->align, C.type.info[f->type].align); + } + t->aggr.len = n; + t->aggr.f = field; + t->aggr.x = bits; + + return i; +} + +uint32 +uniontype(int n, Field *field, Expr *bits) +{ + uint32 i; + Type *t; + Field *f, *e; + + i = type(); + t = C.type.info + i; + t->kind = Tstruct; + t->size = 0; + t->align = 0; + for (f = field, e = field+n; f != e; ++f) { + t->size = MAX(t->size, C.type.info[f->type].size); + t->align = MAX(t->align, C.type.info[f->type].align); + } + t->aggr.len = n; + t->aggr.f = field; + t->aggr.x = bits; + + return i; +} + +uint32 +enumtype(int n, string *elts, Expr *vals) +{ + uint32 i; + Type *t; + Field *f, *e; + + i = type(); + t = C.type.info + i; + t->kind = Tenum; + /* TODO: dont hardcode int32 */ + t->size = 4; + t->align = 4; + t->enm.len = n; + t->enm.elt = elts; + t->enm.val = vals; + + return i; +} +#undef ALIGN_UP +#undef ALIGN_DOWN + +/* unpacking C declarations into sensible types */ +static +uint32 +typeofname(Name *name, uint32 base) +{ + switch (name->kind) { + /* Nnil corresponds to an abstract declarator (i.e. no identifier) */ + case Nnil: + case Nident: + return base; + case Nparen: + return typeofdtor(name->paren, base); + case Nindex: + return typeofname(name->sfx.name, arraytype(base, name->sfx.idx.q, name->sfx.idx.x)); + case Ncall: + return typeofname(name->sfx.name, functype(base, name->sfx.call.n, name->sfx.call.arg, name->sfx.call.dots)); + default: + panicf("unreachable"); + } + return 0; +} + +static +uint32 +typeofdtor(Dtor *decl, uint32 base) +{ + int n; + Ptr *p; + uint64 b, tmp; + + n = 0; + p = &decl->ptr; + b = p->kind; + while (b & 1) { + base = ptrtype(base, b >> 1); + if (++n >= 8) { + p = p->link; + b = p->kind; + } else { + b >>= 6; + } + } + + return typeofname(decl->name, base); +} + +static +uint32 +basetype(Parser *p, Lexer *lx, uint64 *s) +{ + int n; + uint64 m; + + if (spec(p, lx, s)) { + errorat(lx->pos, "failed to parse type specifier"); + return 0; + } + + m = (((*s<<32)>>32) & ~(MaskQul|MaskMem|MaskFcn)); + for (n = 0; n < arrlen(validtypespec); n++) { + if (validtypespec[n] == m) { + if (indextypespec[n] < 0) { + m = *s >> 32; + if (!m) { + errorat(lx->pos, "not a valid type identifier"); + return 0; + } + return m; + } + return indextypespec[n]; + } + } + + errorat(lx->pos, "invalid type specifier"); + return 0; +} + +static +string +namedecl(Parser *p, Lexer *lx, uint32 *base, int noname) +{ + Dtor *dt; + string name; + Type *t; + + dt = getdtor(p); + name = nil; + if (dtor(p, lx, dt, noname)) { + errorat(lx->pos, "invalid declarator"); + goto End; + } + if (!noname || noname == 2 && dt->name->kind) + name = nameof(dt->name); + + *base = typeofdtor(dt, *base); + putdtor(p, dt); + return name; +End: + putdtor(p, dt); + return nil; +} + +// ----------------------------------------------------------------------- +// declarations + +static +uint32 +enumerate(Parser *p, Lexer *lx, string name, int kind) +{ + int i, n; + uint64 s; + uint32 t; + Token tk; + /* TODO: think of a better soln */ + string nm[1024], *elts; + Expr *cx[1024], *vals; + + for (n = 0; tk.kind != Arbrace && n < arrlen(nm); n++) { + if (tk.kind != Aident) { + errorat(tk.pos, "invalid token %s in enum declaration", tokens[tk.kind]); + goto Bad; + } + nm[n] = tk.val.s; + cx[n] = nil; + + tk = advance(p, lx); + switch(tk.kind) { + case Aeq: + advance(p, lx); + cx[n] = expr(p, lx); + tk = peek(p, 0); + if (tk.kind != Acomma) + continue; + /* fallthrough */ + case Acomma: + tk = advance(p, lx); + } + } + copyarray(elts, nm, n); + copyarray(vals, cx, n); + + t = enumtype(n, elts, vals); + declareenum(p, n, elts, vals); + return t; +Bad: + errorat(tk.pos, "failed to parse enum declaration"); + return 0; +} + +static +uint32 +aggregate(Parser *p, Lexer *lx, string name, int kind) +{ + int n; + uint64 s; + Token tk; + /* TODO: think of a better soln */ + static Field fs[1024]; + Field *f; + static Expr *cx[1024]; + Expr *x; + + for (n = 0, tk = peek(p, 0); tk.kind != Arbrace && n < arrlen(fs); n++) { + fs[n].type = basetype(p, lx, &s); + fs[n].qual = (uint32)(s & ~(MaskTyp|MaskInt|MaskFlt)); + Field: + fs[n].name = namedecl(p, lx, &fs[n].type, 0); + tk = peek(p, 0); + switch (tk.kind) { + case Acolon: + advance(p, lx); + cx[n] = expr(p, lx); + tk = peek(p, 0); + if (tk.kind == Asemi) { + tk = advance(p, lx); + continue; + } + if (tk.kind != Acomma) { + errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]); + goto Bad; + } + /* fallthrough */ + case Acomma: + advance(p, lx); + n++; + goto Field; + + case Asemi: + tk = advance(p, lx); + continue; + + default: + errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]); + goto Bad; + } + } + copyarray(f, fs, n); + copyarray(x, cx, n); + return (kind == Tstruct) ? structtype(n, f, x) : uniontype(n, f, x); +Bad: + errorat(tk.pos, "failed to parse aggregate declaration"); + return 0; +} + +static +error +spec(Parser *p, Lexer *lx, uint64 *spec) +{ + Token t; + int n, i; + Sym *typ; + string name; + uint32 tag; + uint64 s, sm; + static uint32 (*aggrfunc[2])(Parser *, Lexer *, string , int) = {aggregate, enumerate}; + + s = 0; + while (t = peek(p, 0), t.kind >= Aident) { + /* typename */ + if (t.kind == Aident) { + typ = lookupobj(p, t.val.s); + if (!typ || (typ && typ->kind != Stype)) + break; + + sm = typ->type; + s |= (sm << 32 | Tname); + advance(p, lx); + continue; + } + + /* keyword */ + switch (n = t.val.i) { + case Kauto: case Kregister: case Kstatic: case Kextern: case Ktypedef: case Ktls: + if (s & MaskMem) { + errorat(lx->pos, "multiple storage class specifiers: second was %s", keywords[n]); + goto Bad; + } + break; + + case Kinline: case Knoret: + if (s & Bit(n)) + warnat(lx->pos, "duplicate %s function specifier", keywords[n]); + break; + + case Kconst: case Kvolatile: + if (s & Bit(n)) + warnat(lx->pos, "duplicate %s specifier found in declaration", keywords[n]); + break; + + case Ksigned: case Kunsigned: + if (s & MaskSgn) { + if (s & Bit(n)) { + warnat(lx->pos, "duplicated storage class specifier: second was %s", keywords[n]); + break; + } + errorat(lx->pos, "multiple storage class specifiers"); + goto Bad; + } + break; + + case Kshort: + if (s & Tshort) { + warnat(lx->pos, "duplicated short specifier"); + break; + } + break; + + case Klong: + if ((s >> Klong) & 2) { + errorat(lx->pos, "cannot chain three or more long specifiers"); + goto Bad; + } + s += Bit(n); + t = advance(p, lx); + continue; + + case Kvoid: case Kchar: case Kint: case Kfloat: case Kdouble: + if (s & MaskTyp) { + errorat(lx->pos, "more than one base type specified"); + goto Bad; + } + break; + + case Kstruct: case Kunion: + i = 0; + goto Aggr; + case Kenum: + i = 1; + Aggr: + if (s & (Tstruct | Tunion | Tenum)) { + errorat(lx->pos, "more than one aggregate/enum type specified"); + goto Bad; + } + t = advance(p, lx); + if (t.kind != Aident && t.kind != Albrace) { + errorat(t.pos, "enum specifier missing valid declaration"); + goto Bad; + } + + /* NOTE: This offset is needed to correctly obtain Tstruct */ + n++; + name = nil; + tag = 0; + if (t.kind == Aident) { + name = t.val.s; + t = advance(p, lx); + } + if (t.kind == Albrace) { + /* TODO: we need check if the name exists. */ + t = advance(p, lx); + /* NOTE: This depends on the enum order. KEEP IN SYNC */ + tag = aggrfunc[i](p, lx, name, Bit(n)); + if (t = peek(p, 0), nomatch(t, Arbrace)) { + errorat(t.pos, "invalid token %s in aggregate/enum declaration", tokens[t.kind]); + goto Bad; + } + /* high bits encode the type index */ + s |= (uint64)tag << 32; + } + /* TODO: if name does not exist, enter in an incomplete type! */ + if (name) + declaretag(p, tag, name); + + break; + + default: + errorat(t.pos, "invalid keyword '%s' found in declaration specifier", keywords[n]); + } + + s |= Bit(n); + advance(p, lx); + } + + *spec = s; + return 0; + +Bad: + /* TODO: serialize bitflags to string for nice error message */ + errorat(lx->pos, "ignoring specifier"); + *spec = Sbad; + return 1; +} + +/* + * name declaration + * see dtor for valid values of ab + */ +static +error +name(Parser *p, Lexer *lx, Name **nmp, int ab) +{ + Token t; + int n, k; + uint64 s; + Sym *sym; + Name *nm, *tmp; + + /* max args = 100 */ + struct Field args[100]; + + nm = *nmp; + t = peek(p, 0); + switch (k = t.kind) { + case Aident: + if (ab == 1) { + errorat(t.pos, "identifier not allowed in abstract declarator"); + goto Bad; + } + nm->kind = Nident; + nm->ident = t.val.s; + break; + + case Alparen: + advance(p, lx); + nm->kind = Nparen; + nm->paren = getdtor(p); + if (dtor(p, lx, nm->paren, ab)) { + putdtor(p, nm->paren); + nm->paren = nil; + errorat(lx->pos, "invalid declarator in parenthesis"); + goto Bad; + } + + t = peek(p, 0); + if (nomatch(t, Arparen)) { + putdtor(p, nm->paren); + nm->paren = nil; + errorat(lx->pos, "missing closing paren in declarator"); + goto Bad; + } + break; + + case Albrakt: + if (ab) + goto Sfx; + errorat(lx->pos, "missing identifier in non-abstract declarator"); + /* fallthrough */ + default: + if (ab) + goto Sfx; + errorat(lx->pos, "invalid token '%s' in name declaration", tokens[k]); + goto Bad; + } + + t = advance(p, lx); +Sfx: + for (;;) { + switch (k = t.kind) { + case Albrakt: + tmp = getname(p); + tmp->kind = Nindex; + tmp->sfx.name = nm; + + nm = tmp, tmp = nil; + + t = advance(p, lx); + if (t.kind == Arbrakt) { + nm->sfx.idx.q = 0; + Iend: + nm->sfx.idx.x = nil; + t = advance(p, lx); + break; + } + if (t.kind == Astar) { + nm->sfx.idx.q = -1; + IStar: + nm->sfx.idx.x = nil; + t = advance(p, lx); + if (t.kind != Arbrakt) { + errorat(t.pos, "invalid '*' syntax in index expression"); + goto Bad; + } + t = advance(p, lx); + break; + } + + if (spec(p, lx, &s)) { + errorat(lx->pos, "invalid type qualifier list in index expression"); + goto Bad; + } + + nm->sfx.idx.q = (uint32)s; + t = peek(p, 0); + + if (t.kind == Astar) + goto IStar; + + if (t.kind == Arbrakt) + goto Iend; + + nm->sfx.idx.x = expr(p, lx); + + t = peek(p, 0); + if (nomatch(t, Arbrakt)) { + errorat(t.pos, "unterminated index expression"); + goto Bad; + } + + t = advance(p, lx); + continue; + + case Alparen: + tmp = getname(p); + tmp->kind = Ncall; + tmp->sfx.name = nm; + + nm = tmp, tmp = nil; + + t = advance(p, lx); + nm->sfx.call.n = 0; + switch (t.kind) { + case Arparen: + nm->sfx.call.arg = nil; + break; + + case Aident: + sym = lookupobj(p, t.val.s); + if (!sym || (sym && sym->kind != Stype)) { + while (t.kind == Aident) { + if (nm->sfx.call.n >= arrlen(args)) + panicf("ident stack overflow"); + args[nm->sfx.call.n++] = (struct Field) { + .qual = 0, + .type = 0, + .name = t.val.s, + }; + t = advance(p, lx); + } + if (nomatch(t, Arparen)) { + errorat(t.pos, "token '%s' found in function parameter identifier list"); + goto Bad; + } + copyarray(nm->sfx.call.arg, args, nm->sfx.call.n); + break; + } + goto ParamLoop; + + case Akeywd: + if (t.val.i < Kconst || t.val.i > Kenum) { + errorat(t.pos, "invalid keyword %s inside function signature"); + goto Bad; + } + + ParamLoop: + if (nm->sfx.call.n >= arrlen(args)-1) + panicf("out of argument buffer"); + + args[nm->sfx.call.n].type = basetype(p, lx, &s); + if (!args[nm->sfx.call.n].type) { + errorat(lx->pos, "could not parse base type in function call"); + goto Bad; + } + + args[nm->sfx.call.n].qual = (uint32)s & ~(MaskTyp|MaskInt|MaskFlt); + args[nm->sfx.call.n].name = namedecl(p, lx, &args[nm->sfx.call.n].type, 2); + + nm->sfx.call.n++; + if ((t = peek(p, 0)).kind == Acomma) { + advance(p, lx); + goto ParamLoop; + } + + if (t.kind == Aellip) { + nm->sfx.call.dots = 1; + t = advance(p, lx); + } + + if (nomatch(t, Arparen)) { + errorat(t.pos, "token '%s' found in function parameter list"); + goto Bad; + } + copyarray(nm->sfx.call.arg, args, nm->sfx.call.n); + break; + + default: + errorat(t.pos, "invalid token %s inside function call signature", tokens[t.kind]); + goto Bad; + } + + t = advance(p, lx); + continue; + + default: + break; + } + break; + } + + *nmp = nm; + return 0; +Bad: + return 1; +} + +/* pointer kind is partitioned into 8x6 regions + * ab => abstract + * @ 0: must have identifier + * @ 1: must not have identifier + * @ 2: don't care + * else: undefined + */ +static +error +dtor(Parser *p, Lexer *lx, Dtor *d, int ab) +{ + int n, k; + error err; + Token t; + Dtor *link; + Ptr *ptr, *x; + + err = 1; + + ptr = &d->ptr; + ptr->kind = 0; + ptr->link = nil; + + t = peek(p, 0); + if (t.kind != Astar) { + if (ab || t.kind == Aident || t.kind == Arparen) + goto Name; + goto Bad; + } + n = 0; +Ptr: + ptr->kind |= Bit(n); + advance(p, lx); +Key: + t = peek(p, 0); + switch (k = t.kind) { + case Akeywd: + if (Kconst <= t.val.i && t.val.i <= Katomic) + ptr->kind |= Bit(6*n + (t.val.i - Kconst + 1)); + else { + errorat(lx->pos, "invalid keyword '%s' modifies pointer", keywords[t.val.i]); + goto Bad; + } + advance(p, lx); + goto Key; + + case Astar: + if (++n >= 8) { + x = getptr(p); + x->kind = 0; + x->link = nil; + ptr->link = x; + ptr = x; + n = 0; + } + goto Ptr; + + case Aident: + case Alparen: + goto Name; + + default: + if (ab) + goto Name; + errorat(lx->pos, "invalid token '%s' modifies pointer specification", tokens[t.kind]); + goto Bad; + } +Name: + return name(p, lx, &d->name, ab); +Bad: + return err; +} + +static +Decl * +decl(Parser *p, Lexer *lx) +{ + uint64 s; + Token t; + Decl *d; + Expr *x; + string name; + struct Decls *ds; + uint32 base, type; + + alloc(d); + + d->kind = 0; + d->pos.beg = lx->pos; + + base = basetype(p, lx, &s); + if (!base) { + errorat(lx->pos, "could not parse type declaration"); + goto Bad; + } + + x = nil; + d->spec = (uint32)s & ~(MaskInt|MaskFlt|MaskTyp); + d->type = base; + d->name = namedecl(p, lx, &d->type, 0); + /* TODO: think about functions (both decls and defs) */ + d->kind = (s & Mtype) ? Dtype : Dvar; + + switch (t = peek(p, 0), t.kind) { + case Aeq: + if (s & Mtype) { + errorat(d->pos.beg, "initialization of type not allowed"); + goto Bad; + } + t = advance(p, lx); + x = initx(p, lx); + d->kind = Dvar; + if (t.kind != Acomma) { + d->init = x; + goto Semi; + } + /* fallthrough */ + case Acomma: + d->kind |= Dlist; + d->list.init = x; + /* move singleton data over */ + name = d->name; + type = d->type; + d->list.name = name; + d->list.type = type; + ds = &d->list; + /* iterate until we hit end of list */ + while (t.kind == Acomma) { + t = advance(p, lx); + + alloc(ds->link); + ds = ds->link; + ds->type = base; + ds->name = namedecl(p, lx, &ds->type, 0); + + t = peek(p, 0); + if (t.kind == Aeq) { + t = advance(p, lx); + ds->init = initx(p, lx); + } else + ds->init = nil; + } + goto Semi; + + case Albrace: + d->kind = Dfunc; + alloc(d->body); + + if (!attop(p)) { + errorat(lx->pos, "nested function declarations are illegal"); + goto Bad; + } + + if (C.type.info[d->type].kind != Tfunc) { + errorat(lx->pos, "attempted to define function body for non function type"); + goto Bad; + } + + openscope(p); + if (blkstmt(p, lx, &d->body)) { + errorat(lx->pos, "failed to parse function body"); + goto Bad; + } + closescope(p); + break; + + default: + Semi: + if (nomatch(t, Asemi)) { + errorat(t.pos, "no semicolon after declaration"); + goto Bad; + } + t = advance(p, lx); + } + + d->pos.end = lx->pos; + declareobj(p, d); + return d; +Bad: + errorat(lx->pos, "failed to parse top level declaration"); + return nil; +} + +// ----------------------------------------------------------------------- +// top level api + +void +setup(Parser *p, Lexer *lx) +{ + advance(p,lx); + advance(p,lx); + + /* define all builtin typedefs */ + declareobj(p, &C.builtin.vargs); +} + +error +parse(Parser *p, Lexer *lx) +{ + Token tok; + + setup(p, lx); + while ((tok = peek(p, 0)), tok.kind > Aeof) { + if (p->ast.len >= p->ast.cap) { + p->ast.cap += 20; + p->ast.decls = realloc(p->ast.decls, p->ast.cap*sizeof(*p->ast.decls)); + } + p->ast.decls[p->ast.len++] = decl(p, lx); + } + + return 0; +} diff --git a/src/cmd/cc/bits.c b/src/cmd/cc/bits.c new file mode 100644 index 0000000..4b405dc --- /dev/null +++ b/src/cmd/cc/bits.c @@ -0,0 +1,114 @@ +#include "cc.h" + +// ----------------------------------------------------------------------- +// Architecture + +enum +{ + archx64, + numarch, +}; + +// ----------------------------------------------------------------------- +// Types + +/* + * enumerated type specifers + * see https://en.wikipedia.org/wiki/C_data_types + */ +#define VOID X(Tvoid, 2) + +#define BOOL X(Tbool, 3) +#define CHAR X(Tchar, 4) +#define SCHAR X(Tsign|Tchar, 5) +#define UCHAR X(Tunsign|Tchar, 6) + +#define SHORT X(Tshort, 7), X(Tshort|Tint, 7) +#define SSHORT X(Tsign|Tshort, 8), X(Tsign|Tshort|Tint, 8) +#define USHORT X(Tunsign|Tshort, 9), X(Tunsign|Tshort|Tint, 9) + +#define INT X(0, 10), X(Tint, 10) +#define SINT X(Tsign, 11), X(Tsign|Tint, 11) +#define UINT X(Tunsign, 12), X(Tunsign|Tint, 12) + +#define LONG X(Tlong, 13), X(Tlong|Tint, 13) +#define SLONG X(Tsign|Tlong, 14), X(Tsign|Tlong|Tint, 14) +#define ULONG X(Tunsign|Tlong, 15), X(Tunsign|Tlong|Tint, 15) + +#define VLONG X(Tvlong, 16), X(Tvlong|Tint, 16) +#define SVLONG X(Tsign|Tvlong, 17), X(Tsign|Tvlong|Tint, 17) +#define UVLONG X(Tunsign|Tvlong, 18), X(Tunsign|Tvlong|Tint, 18) + +#define FLOAT X(Tfloat, 19) +#define DOUBLE X(Tdouble, 20) +#define LONGDB X(Tlong|Tdouble, 21) +#define COMPLEX X(Tcmplx, 22) +#define IMAGINARY X(Timag, 23) + +/* fixed width definitions */ +#define DEF(sz, aln, mx, sgn) {.size=sz, .align=aln, .max=mx, .sign=sgn } + +#define INT8 DEF(1, 1, 0x7fff, 0) +#define UINT8 DEF(1, 1, 0xffff, 1) + +#define INT16 DEF(2, 2, 0x7fff, 0) +#define UINT16 DEF(2, 2, 0xffff, 1) + +#define INT32 DEF(4, 4, 0x7fffffff, 0) +#define UINT32 DEF(4, 4, 0xffffffff, 1) + +#define INT64 DEF(8, 8, 0x7fffffffffffffff, 0) +#define UINT64 DEF(8, 8, 0xffffffffffffffff, 1) + +/* architecture specific definitions */ +// TODO: max value should be able to take floats +#define TYPES \ + TYPE(DEF(0, 0, 0, 0), VOID) \ + TYPE(INT8, BOOL) \ + TYPE(UINT8, CHAR) \ + TYPE(INT8, SCHAR) \ + TYPE(UINT8, UCHAR) \ + TYPE(INT16, SHORT) \ + TYPE(INT16, SSHORT) \ + TYPE(UINT16, USHORT) \ + TYPE(INT32, INT) \ + TYPE(INT32, SINT) \ + TYPE(UINT32, UINT) \ + TYPE(INT64, LONG) \ + TYPE(INT64, SLONG) \ + TYPE(UINT64, ULONG) \ + TYPE(INT64, VLONG) \ + TYPE(INT64, SVLONG) \ + TYPE(UINT64, UVLONG) \ + TYPE(DEF(4, 4, 0, 0), FLOAT) \ + TYPE(DEF(8, 8, 0, 0), DOUBLE) \ + TYPE(DEF(16, 16, 0, 0), LONGDB) \ + TYPE(DEF(8, 8, 0, 0), COMPLEX) \ + TYPE(DEF(4, 4, 0, 0), IMAGINARY) \ + +Type pointer = {.size=8, .align=8, .max=0xffffffffffffffff, .sign=0}; + +/* pack architecture specific definitions into exported arrays */ +#define TYPE(a, ...) a, +Type basetypes[] = { + { 0 }, /* sentinel value for bad types */ + { 0 }, /* sentinel value for variadic args */ + TYPES +}; +#undef TYPE + +#define TYPE(a, ...) __VA_ARGS__, +#define X(a, b) a +uint64 validtypespec[38] = { + TYPES + Tstruct, Tunion, Tenum, Tname, +}; +#undef X + +#define X(a, b) b +int indextypespec[38] = { + TYPES + -1, -1, -1, -1, +}; +#undef X +#undef TYPE diff --git a/src/cmd/cc/cc.c b/src/cmd/cc/cc.c new file mode 100644 index 0000000..8ad0022 --- /dev/null +++ b/src/cmd/cc/cc.c @@ -0,0 +1,409 @@ +#include "cc.h" +#include + +// ----------------------------------------------------------------------- +// string interning + +/* jenkins' one at a time hash */ +static +int32 +hash_string(byte* s) +{ + int32 h; + + h = 0; + if (s != nil) { + for (; *s; ++s) { + h += *s; + h = (h << 10); + h = (h >> 6); + } + } + + h += (h << 3); + h ^= (h >> 11); + h += (h >> 11); + + return h; +} + +static +int +streq(byte *s, byte *t) +{ + if (s == nil) { + if (t == nil) + return 1; + else + return 0; + } + + return (t == nil) ? 0 : strcmp(s, t) == 0; +} + +#define HASH(s) hash_string(s) +#define EQUAL(s, t) (streq(s, t)) +static +int +getstr(string key, int *ok) +{ + int idx; + MAP_GET(idx, (&C.strs), key, HASH, EQUAL); + + *ok = idx < C.strs.n_buckets; + return idx; +} + +static +void +·free(void* _, void* ptr) { + return free(ptr); +} + +static +void * +·alloc(void* _, uint n, ulong size) { + return malloc(n*size); +} + +static +void * +·calloc(void* _, uint n, ulong size) { + return calloc(n, size); +} + +static +int +morestrtab(StrTab *tab, int n) +{ + MAP_GROW(tab, string, int32, n, HASH, ·calloc, ·free, nil); +} + +static +int +putstr(byte *s, error *err) +{ + int sz; + sz = C.strs.size; + MAP_PUT((&C.strs), s, sz, HASH, EQUAL, morestrtab, err); +} +#undef HASH +#undef EQUAL + +int32 +intern(byte **s) +{ + int i, ok; + + i = getstr(*s, &ok); + if (ok) { + *s = C.strs.keys[i]; + goto END; + } + + *s = str·make(*s); + i = putstr(*s, &ok); + C.strs.vals[i] = C.strs.size - 1; + +END: + return C.strs.vals[i]; +} + +// ----------------------------------------------------------------------- +// type interning + +/* TODO: intern types for memory savings */ +int +type() +{ + if (C.type.len >= C.type.cap) { + C.type.cap += 100; + C.type.info = realloc(C.type.info, C.type.cap * sizeof(*C.type.info)); + } + + return C.type.len++; +} + +// ----------------------------------------------------------------------- +// universal compiler builtins + +#define KEYWORD(a, b) b, +byte *keywords[NUM_KEYWORDS] = { KEYWORDS }; +#undef KEYWORD + +#define DIRECTIVE(a, b, c) b, +byte *directives[NUM_DIRECTIVES] = { DIRECTIVES }; +#undef DIRECTIVE + +struct Compiler C = { 0 }; + +// ----------------------------------------------------------------------- +// cli flag handlers + +void +pushinclude(byte *dirs) +{ + string d, s, *it, *end; + + while (*dirs != '\0') { + d = strchr(dirs, ' '); + if (d != nil) + *d = '\0'; + + s = dirs; + intern(&s); + for (it = C.inc.dir, end = it + C.inc.len; it != end; ++it) { + if ((uintptr)s == (uintptr)(*it)) + goto Nextdir; + } + + if (C.inc.len == C.inc.cap) { + C.inc.cap += 20; + C.inc.dir = realloc(C.inc.dir, C.inc.cap*sizeof(*C.inc.dir)); + } + C.inc.dir[C.inc.len++] = s; +Nextdir: + if (d == nil) + break; + dirs = d + 1; + } +} + +// ----------------------------------------------------------------------- +// error reporting + +void +errorat(Pos x, byte *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + printf("error:%s:%d:%d: ", os·basename(x.path), x.line, x.col); + vprintf(fmt, args); + printf("\n"); + + va_end(args); + assert(0); +} + +void +warnat(Pos x, byte *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + printf("warning:%s:%d:%d: ", os·basename(x.path), x.line, x.col); + vprintf(fmt, args); + printf("\n"); + + va_end(args); +} + +// ----------------------------------------------------------------------- +// main point of entry + +void +init(void) +{ + int i; + + for (i = 0; i < arrlen(keywords); i++) + intern(&keywords[i]); + + for (i = 0; i < arrlen(directives); i++) + intern(&directives[i]); + + C.heap = mem·makearena(mem·sys, nil); + + /* compiler definitions */ + C.def.len = 0; + C.def.cap = 100; + C.def.val = calloc(C.def.cap, sizeof(*C.def.val)); + + /* compiler include paths */ + C.inc.len = 0; + C.inc.cap = 100; + C.inc.dir = calloc(C.inc.cap, sizeof(*C.inc.dir)); + C.inc.dir[C.inc.len++] = "."; + + C.outfile = nil; + + /* type info */ + C.type.len = arrlen(basetypes); + C.type.cap = 100 + arrlen(basetypes); + C.type.info = calloc(C.type.cap, sizeof(*C.type.info)); + + memcpy(C.type.info, basetypes, C.type.len * sizeof(*C.type.info)); + + /* builtins */ + C.builtin.vargs = (Decl) { + .pos = (Range) { + .beg = { + .col = 0, + .line = 0, + .path = "", + }, + .end = { + .col = 0, + .line = 0, + .path = "", + }, + }, + .kind = Dtype, + .spec = Mtype, + .type = 1, + .name = "__builtin_va_list", + }; + + intern(&C.builtin.vargs.name); +} + +void +initlx(Lexer *lx) +{ + int i; + + memset(lx, 0, sizeof(*lx)); + lx->b = lx->buf; + + /* predefine macros */ + dodefine(lx, "__LINE__"); + dodefine(lx, "__FILE__"); + lx->macline = (uintptr)lookup(&lx->sym, "__LINE__"); + lx->macfile = (uintptr)lookup(&lx->sym, "__FILE__"); + + for (i = 0; i < C.def.len; i++) + dodefine(lx, C.def.val[i]); + + lx->omit.len = 0; + lx->omit.cap = 100; + lx->omit.path = calloc(lx->omit.cap, sizeof(*C.inc.dir)); + + lx->new = lx->iostk; + lx->new->link = nil; + memset(lx->iostk, 0, sizeof(lx->iostk)); + + lx->sym = (SymTab){ 0 }; +} + +void +freelx(Lexer *lx) +{ + free(lx->omit.path); +} + +void +initp(Parser *p) +{ + /* initialize temporary buffers */ + memset(p->spstk, 0, sizeof(p->spstk)); + memset(p->nmstk, 0, sizeof(p->nmstk)); + memset(p->dtstk, 0, sizeof(p->dtstk)); + memset(p->ptstk, 0, sizeof(p->ptstk)); + + p->sp = p->spstk; + p->nm = p->nmstk; + p->dt = p->dtstk; + p->pt = p->ptstk; + + /* initialize ast */ + p->ast.cap = 0; + p->ast.len = 0; + p->ast.decls = nil; +} + +error +compile(byte *path) +{ + Lexer lx; + Parser p; + error err; + byte *sep, out[400]; + + intern(&path); + strcpy(out, path); + + sep = utf8·findrrune(out, '/'); + if (sep) + *sep++ = '\0'; + else + sep = out; + + if (!C.outfile) { + C.outfile = sep; + if (C.outfile) { + if ((sep = utf8·findrrune(C.outfile, '.'))) { + sep[0] = '.'; + sep[1] = 'o'; + sep[2] = '\0'; + } + } else { + C.outfile = "/dev/null"; + } + } + + initlx(&lx); + initp(&p); + + lx.io = openio(&lx, path); + lx.pos = (Pos){ + .path = path, + .line = 1, + .col = 1, + }; + + err = parse(&p, &lx); + freelx(&lx); + return err; +} + +error +main(int argc, byte *argv[]) +{ + byte *a, *src; + int err; + + init(); + + ARGBEGIN { + case 'o': + C.outfile = ARGF(); + break; + + case 'D': + a = ARGF(); + if (a) { + intern(&a); + if (C.def.len >= C.def.cap) { + C.def.cap += 20; + C.def.val = realloc(C.def.val, C.def.cap * sizeof(*C.def.val)); + } + C.def.val[C.def.len++] = a; + } + break; + + case 'I': + a = ARGF(); + if (a) + pushinclude(a); + break; + } ARGEND + + if (argc < 1 && C.outfile == nil) { + printf("usage: cc [-options] files\n"); + exit(1); + } + + // NOTE: This is just for my comfort during debugging. + pushinclude("/home/nolln/root/include"); + pushinclude("/home/nolln/root/include/vendor/libc"); + + src = (argc == 0) ? "" : argv[0]; + intern(&src); + + if ((err = compile(src)), err) { + exit(2); + } + + exit(0); +} diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h new file mode 100644 index 0000000..8fc5f73 --- /dev/null +++ b/src/cmd/cc/cc.h @@ -0,0 +1,806 @@ +#pragma once + +#include +#include + +#define iota(x) 1 << (x) + +/* core types */ +typedef struct Io Io; +typedef struct Pos Pos; +typedef struct Range Range; +typedef struct Token Token; + +typedef struct Lexer Lexer; + +typedef struct Sym Sym; +typedef struct Type Type; +typedef struct Scope Scope; + +typedef struct Parser Parser; + +typedef struct Ptr Ptr; +typedef struct Name Name; +typedef struct Dtor Dtor; +typedef struct Field Field; + +typedef struct Node Node; +typedef struct Decl Decl; +typedef struct Stmt Stmt; +typedef struct Expr Expr; + +typedef struct SymTab SymTab; +typedef struct StrTab StrTab; + +typedef struct Compiler Compiler; + +/* keywords of language */ +#define KEYWORDS \ + KEYWORD(Kauto,"auto") \ + KEYWORD(Kregister,"register") \ + KEYWORD(Kstatic,"static") \ + KEYWORD(Kextern,"extern") \ + KEYWORD(Ktls,"thread_local") \ + KEYWORD(Ktypedef,"typedef") \ + KEYWORD(Kinline,"inline") \ + KEYWORD(Knoret,"_Noreturn") \ + KEYWORD(Kconst,"const") \ + KEYWORD(Kvolatile,"volatile") \ + KEYWORD(Krestrict,"restrict") \ + KEYWORD(Katomic,"_Atomic") \ + KEYWORD(Ksigned,"signed") \ + KEYWORD(Kunsigned,"unsigned") \ + KEYWORD(Kvoid,"void") \ + KEYWORD(Kbool,"_Bool") \ + KEYWORD(Kchar,"char") \ + KEYWORD(Kfloat,"float") \ + KEYWORD(Kdouble,"double") \ + KEYWORD(Kcomplex,"complex") \ + KEYWORD(Kimaginary,"imaginary") \ + KEYWORD(Kint,"int") \ + KEYWORD(Kshort,"short") \ + KEYWORD(Klong,"long") \ + KEYWORD(Kstruct,"struct") \ + KEYWORD(Kunion,"union") \ + KEYWORD(Kenum,"enum") \ + KEYWORD(Kfor,"for") \ + KEYWORD(Kdo,"do") \ + KEYWORD(Kwhile,"while") \ + KEYWORD(Kcontinue,"continue") \ + KEYWORD(Kif,"if") \ + KEYWORD(Kelse,"else") \ + KEYWORD(Kswitch,"switch") \ + KEYWORD(Kcase,"case") \ + KEYWORD(Kdefault,"default") \ + KEYWORD(Kbreak,"break") \ + KEYWORD(Kgoto,"goto") \ + KEYWORD(Kreturn,"return") \ + KEYWORD(Ksizeof,"sizeof") \ + KEYWORD(Kalignof,"alignof") \ + KEYWORD(Kalignas,"alignas") + +#define KEYWORD(a, b) a, +enum { KEYWORDS NUM_KEYWORDS }; +#undef KEYWORD + +extern byte *keywords[NUM_KEYWORDS]; + +// ----------------------------------------------------------------------- +// lexing: byte stream -> tokens +// pre-processor built in + +/* source position: error reporting */ +struct Pos +{ + int col; + int line; + string path; +}; + + +struct Range +{ + Pos beg; + Pos end; +}; + +void errorat(Pos x, byte *fmt, ...); +void warnat(Pos x, byte *fmt, ...); + +/* pre-processor */ +#define DIRECTIVES \ + DIRECTIVE(Dpragma,"pragma", ppprag) \ + DIRECTIVE(Dinclude,"include", ppinc) \ + DIRECTIVE(Ddefine,"define", ppdef) \ + DIRECTIVE(Dundef,"undef", ppund) \ + DIRECTIVE(Dif,"if", ppif0) \ + DIRECTIVE(Delif,"elif", ppif1) \ + DIRECTIVE(Delse, "else", ppif1) \ + DIRECTIVE(Difdef,"ifdef", ppif2) \ + DIRECTIVE(Difndef,"ifndef", ppif3) \ + DIRECTIVE(Dendif,"endif", ppend) + +#define DIRECTIVE(a, b, c) a, +enum { DIRECTIVES NUM_DIRECTIVES }; +#undef DIRECTIVE + +extern byte *directives[NUM_DIRECTIVES]; + +error domacro(Lexer*); +error dodefine(Lexer *lx, string s); +int expandmacro(Lexer *lx, Sym *s, byte *dst); + +extern error (*macros[NUM_DIRECTIVES])(Lexer*); + +/* tokenization of byte stream */ +#define TOKENS \ + TOK(Anil,"nil") \ + TOK(Aeof,"eof") \ + TOK(Aeq, "==") \ + TOK(Aneq, "!=") \ + TOK(Anot, "!") \ + TOK(Aneg, "~") \ + TOK(Axor, "^") \ + TOK(Aor, "|") \ + TOK(Aand, "&") \ + TOK(Aoror, "||") \ + TOK(Aandand, "&&") \ + TOK(Aadd,"+") \ + TOK(Asub,"-") \ + TOK(Astar,"*") \ + TOK(Adiv,"/") \ + TOK(Amod,"%") \ + TOK(Agt,">") \ + TOK(Alt,"<") \ + TOK(Agteq,">=") \ + TOK(Alteq,"<=") \ + TOK(Alsft,"<<") \ + TOK(Arsft,">>") \ + TOK(Ainc,"++") \ + TOK(Adec,"--") \ + TOK(Aasn,"=") \ + TOK(Aorasn,"|=") \ + TOK(Axorasn,"^=") \ + TOK(Aandasn,"&=") \ + TOK(Aaddasn,"+=") \ + TOK(Asubasn,"-=") \ + TOK(Amulasn,"*=") \ + TOK(Adivasn,"/=") \ + TOK(Amodasn,"%=") \ + TOK(Alsftasn,"<<=") \ + TOK(Arsftasn,">>=") \ + TOK(Acomma,",") \ + TOK(Acolon,":") \ + TOK(Asemi,";") \ + TOK(Alparen,"(") \ + TOK(Arparen,")") \ + TOK(Albrace,"{") \ + TOK(Arbrace,"}") \ + TOK(Albrakt,"[") \ + TOK(Arbrakt,"]") \ + TOK(Adot,".") \ + TOK(Aarrow,"->") \ + TOK(Aqmark,"?") \ + TOK(Aellip,"...") \ + TOK(Alit,"") \ + TOK(Aident,"") \ + TOK(Akeywd,"") \ + +#define TOK(a, b) a, +enum +{ + TOKENS + NUM_TOKENS, + + Vchar = iota(8), + Vrune = iota(9), + Vint = iota(10), + Vlong = iota(11), + Vvlong = iota(12), + Vun = iota(13), + Vfloat = iota(14), + Vstr = iota(15), + Vwstr = iota(16), + + Vmask = Vchar - 1, +}; +#undef TOK + +extern byte *tokens[NUM_TOKENS]; + +/* TODO: store literals in a big val */ +union Val +{ + byte *s; + double f; + vlong i; + uvlong ui; + int32 c; + uint32 uc; + rune r; +}; + +struct Token +{ + uint32 kind; + Pos pos; + union Val val; +}; + +enum +{ + Svar = iota(1), + Sfunc = iota(2), + Stype = iota(3), + Stag = iota(4), + Senum = iota(5), + Slabl = iota(6), + Smacro = iota(7), +}; + +struct Sym +{ + uint32 kind; + string name; + union { + string macro; + Decl *obj; + int32 type; + Stmt *blk; + Expr *val; + }; +}; + +struct SymTab +{ + int32 n_buckets; + int32 size; + int32 n_occupied; + int32 upper_bound; + int32 *flags; + string *keys; + Sym **vals; +}; + +Sym *define(SymTab *tab, string ident, uint32 kind); +Sym *lookup(SymTab *tab, string ident); +error forget(SymTab *tab, string ident); +void forgetall(SymTab *tab); + +enum +{ + IOnil = iota(0), + IOfile = iota(1), + IObuff = iota(2), +}; + +struct Io +{ + io·Buffer rdr; + string path; + uint32 kind; + union { + Stream *f; + byte *b; + }; + + Pos store; + struct Io *link; +}; + +struct Lexer +{ + Pos pos; + SymTab sym; + byte *b; + byte buf[2*1024]; + + /* predefined dynamic macros */ + uintptr macfile; + uintptr macline; + + /* i/o data */ + Io *io, *new; + Io iostk[100]; + struct { + int cap; + int len; + string *path; + } omit; +}; + +/* lex.c functions */ +Token lex(Lexer *); + +int getbyte(Lexer *); +int getnsbyte(Lexer *l); +rune getrune(Lexer *); +byte ungetbyte(Lexer *); +rune ungetrune(Lexer *, rune r); + +Io* openio(Lexer *lx, byte *path); +void pushio(Lexer *lx, Io *new); +void popio(Lexer *lx); + +void puttok(Token); + +// ----------------------------------------------------------------------- +// parsing & type resolution +// tokens -> ast + +/* parent data */ +struct Node +{ + Range pos; + uint32 kind; +}; + +/* ast types */ +enum +{ + Nbad, + /* labels */ + Sempty, Slabel, Scase, + Sblock, + Sexpr, Sdecl, + Sselect, + /* loops */ + Sfor, Swhile, Sdo, + /* jumps */ + Sgoto, Scontin, Sbreak, Sreturn, + /* forks */ + Sif, Sswitch, + + + /* assignments */ + Xasn, Xmulasn, Xdivasn, Xmodasn, Xsubasn, Xaddasn, + Xlsftasn, Xrsftasn, Xandasn, Xxorasn, Xorasn, + /* conditional */ + Xternary, + /* unary prefix ops */ + Xref, Xstar, Xplus, Xminus, Xneg, Xnot, Xsizeof, Xalignof, Xpreinc, Xpredec, + Xcast, + /* unary postfix ops */ + Xpostinc, Xpostdec, Xindex, Xcall, Xselp, Xself, Xinitlist, + /* binary ops */ + Xoror, Xandand, Xor, Xxor, Xand, Xneq, Xeql, Xgt, Xlt, Xgteq, Xlteq, Xlsft, Xrsft, + Xadd, Xsub, Xmul, Xdiv, Xmod, + /* primary */ + Xparen, Xident, Xlit, + /* lists */ + Xcomma, + + + Dvar, + Dfunc, + Dtype, + Dlist = iota(20), + Dvars = Dvar | Dlist, + Dtypes = Dtype | Dlist, + + /* names (don't interact w/ final AST) */ + Nnil = 0, + Nident, + Nparen, + Nindex, + Ncall, +}; + +/* expressions */ +enum +{ + Keynil, + Keyidx, + Keysel, +}; + +struct Key +{ + uint kind : 2; + union { + Expr *x; + string s; + }; +}; + +struct Expr +{ + struct Node; + uint32 qual; + uint32 type; + union { + string name; + struct { + uint64 kind; + union { + union Val; + union Val v; + }; + } val; + struct { + int n; + struct Key *k; + Expr *v; + } init; + Expr *x; + struct { + Expr *l; + Expr *r; + } asn; + struct { + Expr *c; + Expr *t; + Expr *e; + } cond; + struct { + Expr *x; + union { + Expr *i; + string f; + }; + } idx; + struct { + Expr *fn; + int n; + Expr **arg; + } call; + union { + Expr *pre; + Expr *post; + } unary; + struct { + int type : 1; + union { + struct { + uint32 qual; + uint32 type; + } of; + Expr *x; + }; + } info; + struct { + struct { + uint32 qual; + uint32 type; + } to; + Expr *x; + } cast; + struct { + Expr *l; + Expr *r; + } binary; + struct { + Expr *x[2]; + } comma; + }; +}; + + +/* statements */ +struct Stmt +{ + struct Node; + union { + struct { + union { + string ident; + Expr *x; + }; + Node *stmt; + } lbl; + struct { + long n; + struct Node **item; + } blk; + Expr *x; + struct { + Node *init; + Expr *cond; + Expr *step; + Node *body; + } loop; + union{ + string lbl; + Expr *x; + } jmp; + struct { + Expr *cond; + Node *body; + Node *orelse; + } br; + }; +}; + +/* declarations */ + +/* + * specifiers + * the design is the following: + * type info is held w/in a 64 bit integer. + * the bottom 32 bits are associated to specializations + * the top 32 bits index into a type-info array held by the compiler. + */ +enum +{ + /* memory */ + Mauto = iota(Kauto), + Mstatic = iota(Kstatic), + Mreg = iota(Kregister), + Mtls = iota(Ktls), + Mtype = iota(Ktypedef), + Mextern = iota(Kextern), + + MaskMem = Mauto | Mstatic | Mreg | Mtls | Mtype | Mextern, + + /* qualifiers */ + Qconst = iota(Kconst), + Qrestr = iota(Krestrict), + Qvoltl = iota(Kvolatile), + Qatom = iota(Katomic), + + MaskQul = Qconst | Qrestr | Qvoltl | Qatom, + + Finlne = iota(Kinline), + Fnoret = iota(Knoret), + + MaskFcn = Finlne | Fnoret, + + /* types */ + Tsign = iota(Ksigned), + Tunsign = iota(Kunsigned), + + MaskSgn = Tsign | Tunsign, + + Tvoid = iota(Kvoid), + Tfloat = iota(Kfloat), + Tdouble = iota(Kdouble), + Tcmplx = iota(Kcomplex), + Timag = iota(Kimaginary), + + MaskFlt = Tfloat | Tdouble | Tcmplx | Timag, + + Tchar = iota(Kchar), + Tbool = iota(Kbool), + + Tshort = iota(Kshort), + Tint = iota(Kint), + Tlong = iota(Klong), + Tvlong = iota(Klong+1), + + MaskInt = Tshort | Tint | Tlong | Tvlong, + MaskTyp = Tvoid | Tbool | Tchar | Tint | Tfloat | Timag | Tcmplx, + /* + * NOTE IMPORTANT: vlong takes over the struct bit place + * DON'T MOVE KEYWORDS WITHOUT REORGANIZING + */ + Tstruct = iota(Kstruct+1), + Tunion = iota(Kunion+1), + Tenum = iota(Kenum+1), + Tname = iota(Kenum+2), + + Sbad = -1, +}; + +/* intermediate nodes */ +struct Ptr +{ + uint64 kind; + Ptr *link; +}; + +struct Name +{ + uint32 kind; + union { + string ident; + struct Dtor *paren; + struct { + Name *name; + union { + struct { + uint32 q; + Expr *x; + } idx; + struct { + int n; + int dots : 1; + Field *arg; + } call; + }; + } sfx; + }; +}; + +struct Dtor +{ + Ptr ptr; + Name *name; +}; + +/* final ast node */ + +struct Field +{ + uint32 qual; + uint32 type; + string name; +}; + +struct Decls +{ + string name; + uint32 type; + Expr *init; + struct Decls *link; +}; + + +struct Decl +{ + struct Node; + uint32 spec; + union { + struct { + string name; + uint32 type; + union { + Stmt *body; + Expr *init; + }; + }; + struct Decls list; + }; +}; + +enum +{ + Tbad, + Tbase, + Tdef, + Tptr, + Tarray, + Tfunc, +}; + +/* types */ +struct Type +{ + uint32 kind; + Sym *sym; + uintptr size; + uintptr max; + uint16 align : 8; + uint8 sign : 2; + union { + struct { + uint32 qual; + uint32 base; + } ptr; + struct { + int len; + uint32 qual; + uint32 *elt; + } arr; + struct { + int len; + Field *f; + Expr *x; + } aggr; + struct { + int len; + string *elt; + Expr *val; + } enm; + struct { + uint32 ret; + int n; + int dots : 1; + Field *arg; + } func; + }; +}; + +/* platform specific */ +extern Type pointer; +extern Type basetypes[24]; +/* mandated by C standard */ +extern uint64 validtypespec[38]; +extern int indextypespec[38]; + +struct Scope +{ + SymTab tags; + SymTab objs; +}; + +struct Parser +{ + Token tok[2]; + struct { + int cap; + int len; + Decl **decls; + } ast; + + /* static buffers/stacks */ + Scope *sp; + Scope spstk[40]; + + Name *nm; + Name nmstk[40]; + + Ptr *pt; + Ptr ptstk[10]; + + Dtor *dt; + Dtor dtstk[40]; +}; + +/* ast.c functions */ +error parse(Parser *, Lexer *); + +// ----------------------------------------------------------------------- +// global compiler data + +struct StrTab +{ + int32 n_buckets; + int32 size; + int32 n_occupied; + int32 upper_bound; + int32 *flags; + string *keys; + int32 *vals; +}; + +#if 0 +struct TypeSet +{ + int32 n_buckets; + int32 size; + int32 n_occupied; + int32 upper_bound; + int32 *flags; + Type **keys; +}; +#endif + +/* main data */ +struct Compiler +{ + mem·Arena *heap; + StrTab strs; + string outfile; + + struct { + int cap; + int len; + string *val; + } def; + + struct { + int cap; + int len; + string *dir; + } inc; + + struct { + int cap; + int len; + Type *info; + } type; + + /* TODO: make array */ + struct { + Decl vargs; + } builtin; +}; + +extern Compiler C; + +/* cc.c functions */ +void init(); +int32 intern(byte **str); +int32 type(); + +#undef iota diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c new file mode 100644 index 0000000..33fc5d0 --- /dev/null +++ b/src/cmd/cc/lex.c @@ -0,0 +1,873 @@ +#include "cc.h" +#include + +// ----------------------------------------------------------------------- +// printing functions + +void +puttok(Token tok) +{ + if (tok.kind < Alit) + printf("%s", tokens[tok.kind]); + else if (tok.kind & Alit) { + if (tok.kind & Vchar) + if (tok.kind & Vint) + if (tok.kind & Vlong) + if (tok.kind & Vvlong) + printf("literal <%lld>", tok.val.i); + if (tok.kind & Vfloat) + printf("literal <%f>", tok.val.f); + printf("literal <%s>", tok.val.s); + } else + printf("ident <%s>", tok.val.s); +} + +// ----------------------------------------------------------------------- +// io buffer management + +#define asrdr(x) (io·Reader){(int (*)(void *, int, int, void *))x} + +// path should be absolute +Io* +openio(Lexer *lx, byte *path) +{ + string *it, *end; + + intern(&path); + + // See if we have already opened file; + // If so, and it hasn't been flagged return it + for (it = lx->omit.path, end = it + lx->omit.len; it < end; ++it) { + if ((uintptr)(*it) == (uintptr)(path)) + return nil; + } + + // TODO: See if we have already loaded the file + + if ((lx->new - lx->iostk) >= arrlen(lx->iostk)-1) + panicf("out of I/O space!"); + + lx->new->f = io·open(path, "r"); + if (!lx->new->f) + panicf("file %s not found", path); + + lx->new->kind = IOfile; + lx->new->path = path; + bufio·initreader(&lx->new->rdr, asrdr(io·read), lx->new->f); + + return lx->new++; +} + +static +Io* +makeio(Lexer *lx, byte *name) +{ + if ((lx->new - lx->iostk) >= arrlen(lx->iostk)-1) + panicf("out of I/O space!"); + + lx->new->path = name; + lx->new->rdr = (io·Buffer) { + .state = bufio·rdr | bufio·end, + .runesize = 0, + .h = nil, + .size = bufio·size, + .beg = lx->new->rdr.buf + bufio·ungets, + .pos = lx->new->rdr.buf + bufio·ungets, + .end = lx->new->rdr.buf + bufio·ungets, + }; + lx->new->b = lx->new->rdr.beg; + + return lx->new++; +} +#undef asrdr + +static +void +freeio(Lexer *lx, Io *io) +{ + if (io->kind & IOfile) { + io·close(io->f); + } + + io->rdr.state = 0; + io->kind = 0; + io->link = nil; + io->path = nil; + io->store = (Pos){ 0 }; + io->path = ""; +} + +void +pushio(Lexer *lx, Io *new) +{ + new->link = lx->io; + lx->io->store = lx->pos; + lx->io = new; + + lx->pos = (Pos){ + .line = 1, + .col = 1, + .path = new->path, + }; +} + +void +popio(Lexer *lx) +{ + Io *prev; + + assert(lx->io == lx->new-1); + --lx->new; + + prev = lx->io->link; + freeio(lx, lx->io); + + lx->io = prev; + if (!prev) { + return; + } + + lx->pos = prev->store; +} + +// ----------------------------------------------------------------------- +// simple wrappers + +int +getbyte(Lexer *lx) +{ + return bufio·getbyte(&lx->io->rdr); +} + +int +getnsbyte(Lexer *lx) +{ + int b; + b = getbyte(lx); + for (;;) { + if (b == EOF) { + if (lx->io->link) { + popio(lx); + assert(lx->io); + b = getbyte(lx); + continue; + } else + return b; + } + if (b >= RuneSelf || !isspace(b)) + return b; + if (b == '\n') + return b; + b = getbyte(lx); + } + return b; +} + +rune +getrune(Lexer *lx) +{ + return bufio·getrune(&lx->io->rdr); +} + +byte +ungetbyte(Lexer *lx) +{ + byte b; + return bufio·ungetbyte(&lx->io->rdr, b); +} + +rune +ungetrune(Lexer *l, rune r) +{ + return bufio·ungetrune(&l->io->rdr, r); +} + +// ----------------------------------------------------------------------- +// main lexer + +#define TOK(a, b) b, +byte *tokens[NUM_TOKENS] = { TOKENS }; +#undef TOK + +static uint8 Atoi[256] = +{ + ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, ['5'] = 5, + ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9, ['a'] = 10, ['A'] = 10, + ['b'] = 11, ['B'] = 11, ['c'] = 12, ['C'] = 12, ['d'] = 13, ['D'] = 13, + ['e'] = 14, ['E'] = 14, ['f'] = 15, ['F'] = 15, +}; + +static +error +escapechar(Lexer *lx, int x, int islong, int esc, vlong *val) +{ + int i, u, c; + vlong l; + + c = getrune(lx); + + switch (c) { + case '\\': + break; + case EOF: + errorat(lx->pos, "EOF in string"); + return 1; + case '\n': + errorat(lx->pos, "newline in string"); + return 1; + default: + if (c == x) + return 1; + *val = c; + return 0; + } + + u = 0; + c = getrune(lx); + + switch(c) { + case 'x': + i = islong ? 4 : 2; + goto hex; + + case 'u': + i = islong ? 8 : 4; + u = 1; + goto hex; + + case 'U': + i = 8; + u = 1; + goto hex; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + i = islong ? 4 : 2; + goto oct; + + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\':c = '\\'; break; + + default: + if(c != x) errorat(lx->pos, "unknown escape sequence: %c", c); + } + *val = c; + return 0; + +hex: + l = 0; + for(; i > 0; i--) { + c = getbyte(lx); + if (c >= '0' && c <= '9') { + l = l*16 + c-'0'; + continue; + } + if (c >= 'a' && c <= 'f') { + l = l*16 + c-'a' + 10; + continue; + } + if (c >= 'A' && c <= 'F') { + l = l*16 + c-'A' + 10; + continue; + } + ungetbyte(lx); + break; + } + if (u && (l > RuneMax || (0xd800 <= l && l < 0xe000))) { + errorat(lx->pos, "invalid unicode code point in escape sequence: %#llx", l); + l = RuneErr; + } + *val = l; + if (esc) + *val |= RuneMask + 1; + return 0; + +oct: + l = c - '0'; + for (; i > 0; i--) { + c = getbyte(lx); + if (c >= '0' && c <= '7') { + l = l*8 + c-'0'; + continue; + } + ungetbyte(lx); + break; + } + if (l > 255) errorat(lx->pos, "octal escape value > 255: %d", l); + + *val = l; + if (esc) + *val |= RuneMask + 1; + return 0; +} + +#define CASE1(stmt1, kind1) \ + case stmt1: \ + tok.kind = kind1; \ + goto Return + +#define CASE2(stmt1, kind1, b1, kind2) \ + case stmt1: \ + tok.kind = kind1; \ + b = getbyte(lx); \ + if (b == b1) \ + tok.kind = kind2; \ + else \ + ungetbyte(lx); \ + goto Return + +#define CASE3(stmt1, kind1, b1, kind2, b2, kind3) \ + case stmt1: \ + tok.kind = kind1; \ + b = getbyte(lx); \ + if (b == b1) \ + tok.kind = kind2; \ + else if (b == b2) \ + tok.kind = kind3; \ + else \ + ungetbyte(lx); \ + goto Return + +#define CASE4(stmt1, kind1, b1, kind2, b2, kind3, b3, type4) \ + case stmt1: \ + tok.kind = kind1; \ + b = getbyte(lx); \ + if (b == b1) \ + tok.kind = kind2; \ + else if (b == b2) \ + tok.kind = kind3; \ + else if (b == b3) \ + tok.kind = type4; \ + else \ + ungetbyte(lx); \ + goto Return + + +Token +lex(Lexer *lx) +{ + int b, n, f; + vlong v, _; + rune r; + string s; + double d; + byte *e; + Token tok; + Sym *sym; + Io *io; + +GetByte: + b = getbyte(lx); +Dispatch: + tok.pos = lx->pos; + + if ((b != EOF && b >= RuneSelf) || b == '_') + goto Talpha; + if (isalpha(b)) { + if (b != 'L') + goto Talpha; + + n = b; + b = getbyte(lx); + if (b == '\'') { + if (escapechar(lx, '\'', 1, 0, &v)) + b = '\''; + if (!escapechar(lx, '\'', 1, 0, &_)) { + errorat(lx->pos, "missing ' at end of character constant"); + } + tok.kind = Alit | Vrune; + tok.val.r = v; + goto Return; + } + if (b == '"') + goto TLstr; + ungetbyte(lx); + b = n; + + goto Talpha; + } + if (isdigit(b)) + goto Tnum; + + switch (b) { + case '\n': + lx->pos.line++; + case ' ': case '\r': case '\t': case '\v': case '\f': + while (b = getbyte(lx), isspace(b)) + if (b == '\n') + lx->pos.line++; + goto Dispatch; + + case '\\': + b = getbyte(lx); + if (b != '\n') + errorat(lx->pos, "'\\' without a trailing newline"); + goto GetByte; + + Tchar: + case '\'': + if (escapechar(lx, '\'', 0, 0, &v)) { + errorat(lx->pos, "empty literal or escaped ' in char literal"); + v = '\''; + } + if (!escapechar(lx, '\'', 0, 0, &_)) { + errorat(lx->pos, "missing '"); + ungetbyte(lx); + } + + if (v > 0xff) { + errorat(lx->pos, "overflowed character literal"); + v = 0; + } + tok.kind = Alit | Vchar; + tok.val.c = v; + goto Return; + + case '"': + s = str·makecap("", 0, 8); + for (;;) { + if (escapechar(lx, '"', 0, 1, &v)) + break; + + if (v & (RuneMask + 1)) + str·appendbyte(&s, v); + else { + r = v; + b = utf8·runelen(r); + utf8·runetobyte(lx->buf, &r); + str·appendlen(&s, b, lx->buf); + } + } + tok.kind = Alit | Vstr; + tok.val.s = s; + intern(&tok.val.s); + + str·free(s); + goto Return; + + TLstr: + s = str·makecap("", 0, 8); + // NOTE: this violates strict aliasing + for (;;) { + if (escapechar(lx, '"', 1, 0, &v)) + break; + str·appendlen(&s, sizeof(wchar_t), (byte*)&v); + } + tok.kind = Alit | Vwstr; + tok.val.s = s; + intern(&tok.val.s); + + str·free(s); + goto Return; + + case '.': + tok.kind = Adot; + b = getbyte(lx); + + if (isdigit(b)) { + // *lx->b++ = b; + goto Tflt; + } else if (b == '.') { + b = getbyte(lx); + if (b != '.') { + errorat(lx->pos, "invalid token '..'"); + tok.kind = Aellip; + break; + } + } + ungetbyte(lx); + goto Return; + + case '<': + tok.kind = Alt; + b = getbyte(lx); + + if (b == '<') { + tok.kind = Alsft; + b = getbyte(lx); + if (b == '=') + tok.kind = Alsftasn; + else + ungetbyte(lx); + } else if (b == '=') + tok.kind = Alteq; + else + ungetbyte(lx); + goto Return; + + case '>': + tok.kind = Agt; + b = getbyte(lx); + + if (b == '>') { + tok.kind = Arsft; + b = getbyte(lx); + if (b == '=') + tok.kind = Arsftasn; + else + ungetbyte(lx); + } else if (b == '=') + tok.kind = Agteq; + else + ungetbyte(lx); + goto Return; + + case '/': + tok.kind = Adiv; + b = getbyte(lx); + + if (b == '=') + tok.kind = Adivasn; + else if (b == '/') { + while (b != EOF && b != '\n') + b = getbyte(lx); + goto Dispatch; + } else if (b == '*') { + int level = 1; + b = getbyte(lx); + while (b != EOF && level > 0) { + if (b == '/') { + b = getbyte(lx); + if (b == '*') + level++; + } else if (b == '*') { + b = getbyte(lx); + if (b == '/') + level--; + } + if (b == '\n') + lx->pos.line++; + b = getbyte(lx); + } + goto Dispatch; + } else + ungetbyte(lx); + goto Return; + + case '#': + if (domacro(lx)) { + tok.kind = Anil; + errorat(lx->pos, "failed to perform preprocessor directive"); + return tok; + } + goto GetByte; + + case EOF: + popio(lx); + if (lx->io) + goto GetByte; + tok.kind = Aeof; + goto Return; + + CASE1('(', Alparen); + CASE1(')', Arparen); + CASE1('{', Albrace); + CASE1('}', Arbrace); + CASE1('[', Albrakt); + CASE1(']', Arbrakt); + CASE1(',', Acomma); + CASE1('?', Aqmark); + CASE1(';', Asemi); + CASE1('~', Aneg); + CASE1(':', Acolon); + CASE2('^', Axor, '=', Axorasn); + CASE2('!', Anot, '=', Aneq); + CASE2('*', Astar,'=', Amulasn); + CASE2('=', Aasn, '=', Aeq); + CASE2('%', Amod, '=', Amodasn); + CASE3('+', Aadd, '=', Aaddasn, '+', Ainc); + CASE3('&', Aand, '=', Aandasn, '&', Aandand); + CASE3('|', Aor, '=', Aorasn, '|', Aoror); + CASE4('-', Asub, '=', Asubasn, '-', Adec, '>', Aarrow); + + Tnum: + e = lx->buf + arrlen(lx->buf); + do { + if (lx->b >= e) { + errorat(lx->pos, "number overflows lexer buffer"); + goto Nospace; + } + *lx->b++ = b; + } while (b = getbyte(lx), isdigit(b) || b == '_'); + + if (b == '.' || tolower(b) == 'e') + goto Tflt; + Tint: + n = 10; + s = lx->buf; + if (*s == '0') { + switch (b) { + case 'x': n = 16; break; + case 'b': n = 2; break; + case 'o': n = 8; break; + default: goto Rint; + } + lx->b = s; + /* reparse number, now with base info */ + while (b = getbyte(lx), (isdigit(b) || + ('a' <= b && b <= 'f') || + ('A' <= b && b <= 'F') || + b == '_')) + *lx->b++ = b; + } + Rint: + v = 0; + r = b; + for (; s != lx->b ; s++) { + b = *s; + if (b == '_') continue; + + f = Atoi[b]; + if (f == 0 && b != '0') + break; + + if (f >= n) { + errorat(lx->pos, "digit '%c' out of range for base %d", b, n); + f = 0; + } + + if (v > (UINT64_MAX - f) / n) { + errorat(lx->pos, "integer literal overflow"); + v = 0; + break; + } + + v = v * n + f; + } + + b = r; + tok.kind = Alit; + tok.val.i = v; + + if (b == 'u' || b == 'U') { + tok.kind |= Vun; + b = getbyte(lx); + } + if (b == 'l' || b == 'L') { + r = getbyte(lx); + if (r == 'l' || r == 'L') { + if (r != b) + errorat(lx->pos, "mismatched case on long long integer suffix"); + tok.kind |= Vvlong; + r = getbyte(lx); + } else + tok.kind |= Vlong; + + if (r == 'u' || r == 'U') { + if (tok.kind & Vun) + errorat(lx->pos, "multiple unsigned designators on integer suffix"); + tok.kind |= Vun; + goto Return; + } + + ungetbyte(lx); + goto Return; + } + + tok.kind |= Vint; + ungetbyte(lx); + goto Return; + + Tflt: + if (b == '.') { + *lx->b++ = b; + b = getbyte(lx); + } + + while (isdigit(b)) { + *lx->b++ = b; + + if (lx->b >= e) { + errorat(lx->pos, "number overflows lexer buffer"); + goto Nospace; + } + } + + if (tolower(b) == 'e') { + b = getbyte(lx); + if (b == '-' || b == '+') + b = getbyte(lx); + + if (!isdigit(b)) + errorat(lx->pos, "expected number after exponent, found %c", b); + + do { + *lx->b++ = b; + } while (b = getbyte(lx), isdigit(b)); + } + *lx->b = '\0'; + d = strtod(lx->buf, nil); + ungetbyte(lx); + + tok.kind = Alit | Vfloat; + tok.val.f = d; + + goto Return; + + Talpha: + s = lx->buf; + e = lx->buf + arrlen(lx->buf); + for (;;) { + if (s >= e) { + errorat(lx->pos, "identifier too long for buffer: %s", s); + goto Nospace; + } + if (b != EOF && b >= RuneSelf) { + ungetbyte(lx); + r = getrune(lx); + if (!utf8·isletter(r) && !utf8·isdigit(r) && r != 0xb7) { + errorat(lx->pos, "invalid identifier character %d", r); + } + s += utf8·runetobyte(s, &r); + } else if (!isalnum(b) && b != '_') + break; + else + *s++ = b; + b = getbyte(lx); + } + *s = '\0'; + ungetbyte(lx); + + tok.kind = Aident; + tok.val.s = lx->buf; + + n = intern(&tok.val.s); + if (n < arrlen(keywords)) { + tok.kind = Akeywd; + tok.val.i = n; + goto Return; + } + + sym = lookup(&lx->sym, tok.val.s); + if (sym && ((uintptr)sym->name != (uintptr)lx->io->path)) { + if ((uintptr)sym == lx->macline) { + tok.kind = Alit | Vint; + tok.val.i = lx->pos.line; + goto Return; + } + if ((uintptr)sym == lx->macfile) { + tok.kind = Alit | Vstr; + tok.val.s = lx->pos.path; + goto Return; + } + io = makeio(lx, sym->name); + io->rdr.end += expandmacro(lx, sym, io->b); + printf("EXPANDED %s: %s\n", sym->name, io->rdr.beg); + *io->rdr.end++ = EOF; + pushio(lx, io); + goto GetByte; + } + goto Return; + + default: + tok.kind = Anil; + errorat(lx->pos, "invalid token, crashing"); + abort(); + } + +Return: + lx->b = lx->buf; + return tok; + +Nospace: + panicf("aborting compilation"); + exit(1); +} + +#undef CASE4 +#undef CASE3 +#undef CASE2 +#undef CASE1 + +// ----------------------------------------------------------------------- +// symbol tables + +#define PTR_HASH(p) (uintptr)(p) +#define PTR_EQUAL(p1, p2) ((uintptr)(p1) == (uintptr)(p2)) + +static +void +·free(void* _, void* ptr) { + return free(ptr); +} + +static +void * +·alloc(void* _, uint n, ulong size) { + return malloc(n*size); +} + +static +void * +·calloc(void* _, uint n, ulong size) { + return calloc(n, size); +} + +static +int +moresymtab(SymTab *tab, int n) +{ + MAP_GROW(tab, string, Sym*, n, PTR_HASH, sys·Memory, nil); +} + +static +int +putsym(SymTab *tab, Sym *sym, error *err) +{ + MAP_PUT(tab, sym->name, sym, PTR_HASH, PTR_EQUAL, moresymtab, err); +} + +Sym* +define(SymTab *tab, string name, uint32 kind) +{ + int i; + Sym *sym; + error err; + + sym = mem·arenaalloc(C.heap, 1, sizeof(*sym)); + sym->name = name; + sym->kind = kind; + + i = putsym(tab, sym, &err); + tab->vals[i] = sym; + + return sym; +} + +Sym* +lookup(SymTab *tab, string ident) +{ + int idx; + MAP_GET(idx, tab, ident, PTR_HASH, PTR_EQUAL); + + if (idx < tab->n_buckets) + return tab->vals[idx]; + + return nil; +} + + +error +forget(SymTab *tab, string ident) +{ + int idx; + MAP_GET(idx, tab, ident, PTR_HASH, PTR_EQUAL); + + if (idx < tab->n_buckets) { + MAP_DEL(tab, idx); + return 0; + } + return 1; +} + +void +forgetall(SymTab *tab) +{ + MAP_RESET(tab); +} diff --git a/src/cmd/cc/pp.c b/src/cmd/cc/pp.c new file mode 100644 index 0000000..57c3501 --- /dev/null +++ b/src/cmd/cc/pp.c @@ -0,0 +1,1125 @@ +#include "cc.h" + +// ----------------------------------------------------------------------- +// helper functions + +static +void +pushomit(Lexer *lx, string omit) +{ + if (lx->omit.len == lx->omit.cap) { + lx->omit.cap += 20; + lx->omit.path = realloc(lx->omit.path, lx->omit.cap*sizeof(*lx->omit.path)); + } + lx->omit.path[lx->omit.len++] = omit; +} + +// NOTE: The iterator of lexer lx->b IS NOT reset. +// Its the caller's responsibility. +static +string +ident(Lexer *lx) +{ + int b; + byte *s; + + b = getnsbyte(lx); + if (!isalpha(b) && b != '_' && b < RuneSelf) { + ungetbyte(lx); + return ""; + } + + s = lx->b; + for (;;) { + *lx->b++ = b; + b = getbyte(lx); + if (isalnum(b) || b == '_' || b >= RuneSelf) + continue; + ungetbyte(lx); + break; + } + *lx->b++ = '\0'; + + return s; +} + +static +string +identdots(Lexer *lx, int *dots) +{ + int c; + byte *s; + + s = ident(lx); + if (*s != '\0') + return s; + + c = getnsbyte(lx); + if (c != '.') { + ungetbyte(lx); + return s; + } + + if (getbyte(lx) != '.' || getbyte(lx) != '.') + errorat(lx->pos, "incorrect '...' token in macro"); + + *dots = 1; + // TODO: should only run intern once... + s = "__VA_ARGS__"; + intern(&s); + return s; +} + +static +Sym* +defmacro(Lexer *lx, string name, string macro) +{ + Sym *mac; + + // printf("DEFINING MACRO %s ON LINE %d, file %s\n", name, lx->pos.line, os·basename(lx->pos.path)); + mac = define(&lx->sym, name, Smacro); + mac->macro = macro; + + return mac; +} + +static vlong evalmacro(Lexer *lx, byte prec); + +static +vlong +opand(Lexer *lx) +{ + int b; + vlong v; + string s; + Token tok; + Sym *sym; + + b = getnsbyte(lx); + if (b == '\n') { + errorat(lx->pos, "new line in macro expression"); + return 0; + } + ungetbyte(lx); + + tok = lex(lx); + + switch (tok.kind & Vmask) { + case Aneg: + return ~opand(lx); + + case Anot: + return !opand(lx); + + case Alparen: + v = evalmacro(lx, 1); + tok = lex(lx); + if (!(tok.kind & Arparen)) { + errorat(lx->pos, "unbalanced parenthesis in macro expression"); + return 0; + } + return v; + + case Alit: + switch (tok.kind & ~Vmask) { + case Vint: case Vlong: case Vvlong: + return tok.val.i; + case Vun|Vint : case Vun|Vlong : case Vun|Vvlong: + return tok.val.ui; + case Vrune: + return tok.val.r; + case Vchar: + return tok.val.c; + default: + errorat(lx->pos, "invalid literal of type '%s' in conditional macro", tokens[tok.kind & ~Vmask]); + return 0; + } + + case Aident: + sym = lookup(&lx->sym, tok.val.s); + if (!sym) { + /* calling lex directly would expand the operand here + * manually lex the result + */ + if (strcmp(tok.val.s, "defined") == 0) { + b = getnsbyte(lx); + if (b == '\n') { + errorat(lx->pos, "new line in defined operand"); + return 0; + } + s = lx->buf; + if (b == '(') { + b = getnsbyte(lx); + while (b != ')') { + if (b == '\n') { + errorat(lx->pos, "new line inside defined operand"); + return 0; + } + if (b == '(') { + errorat(lx->pos, "nested parens not allowed inside defined operator"); + return 0; + } + if (!isspace(b)) + *s++ = b; + b = getbyte(lx); + } + } else { + while (!isspace(b)) { + *s++ = b; + b = getbyte(lx); + + if (b == '\n') { + errorat(lx->pos, "new line inside defined operand"); + return 0; + } + } + } + *s = '\0'; + s = lx->buf; + intern(&s); + return lookup(&lx->sym, s) != nil; + } + return 0; + } + panicf("unreachable"); + return 1; + + default: + errorat(lx->pos, "opand: invalid token found in macro conditional: '%s'", tokens[tok.kind & Vmask]); + return 0; + } +} + +// recursively evaluates a macro +// reduced set of operators allowed here +static +vlong +evalmacro(Lexer *lx, byte prec) +{ + int b; + vlong l, r; + Token tok; + + l = opand(lx); + for (;;) { + b = getnsbyte(lx); + // NOTE: Either this or we pass in what are stopping byte is + // New line should always stop us... + // Is there any case where we SHOULDN'T STOP ON ')'? + if (b == '\n' || b == ')') { + ungetbyte(lx); + break; + } + ungetbyte(lx); + + tok = lex(lx); + // simplified jump table of precedence + // unpacked to evaluate inline + switch (tok.kind & Vmask) { + case Astar: + if (prec > 10) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 10 + 1); + l = l * r; + continue; + + case Adiv: + if (prec > 10) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 10 + 1); + l = l / r; + continue; + + case Amod: + if (prec > 10) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 10 + 1); + l = l % r; + continue; + + case Aadd: + if (prec > 9) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 9 + 1); + l = l + r; + continue; + + case Asub: + if (prec > 9) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 9 + 1); + l = l - r; + continue; + + case Alsft: + if (prec > 8) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 8 + 1); + l = l << r; + continue; + + case Arsft: + if (prec > 8) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 8 + 1); + l = l >> r; + continue; + + case Alt: + if (prec > 7) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 7 + 1); + l = l < r; + continue; + + case Agt: + if (prec > 7) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 7 + 1); + l = l > r; + continue; + + case Agteq: + if (prec > 7) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 7 + 1); + l = l >= r; + continue; + + case Alteq: + if (prec > 7) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 7 + 1); + l = l >= r; + continue; + + case Aeq: + if (prec > 6) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 6 + 1); + l = l == r; + continue; + + case Aneq: + if (prec > 6) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 6 + 1); + l = l != r; + continue; + + case Aand: + if (prec > 5) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 5 + 1); + l = l & r; + continue; + + case Axor: + if (prec > 4) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 4 + 1); + l = l ^ r; + continue; + + case Aor: + if (prec > 3) { + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 3 + 1); + l = l | r; + continue; + + case Aandand: + if (prec > 2) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 2 + 1); + l = l && r; + continue; + + case Aoror: + if (prec > 1) { + ungetbyte(lx); + ungetbyte(lx); + return l; + } + r = evalmacro(lx, 1 + 1); + l = l || r; + continue; + + default: + errorat(lx->pos, "eval: invalid token found in macro conditional '%s'", tokens[tok.kind & Vmask]); + abort(); + return 0; + } + } + + return l; +} + +// ----------------------------------------------------------------------- +// preprocessor magic numbers + +enum +{ + PPbeg = 0x02, + PParg = 0x03, + PPcat = 0x04, + PPstr = 0x05, + + PPnarg = 30, +}; + +#define PPvar 0x80u + +// ----------------------------------------------------------------------- +// preprocessor functions + +/* #endif */ +static +error +ppend(Lexer *lx) +{ + int b; + do { + b = getnsbyte(lx); + } while (b > 0 && b != '\n'); + + if (b == '\n') + lx->pos.line++; + + return 0; +} + + +/* #undef */ +static +error +ppund(Lexer *lx) +{ + string s; + error err; + + s = ident(lx); + intern(&s); + lx->b = lx->buf; + + err = forget(&lx->sym, s); + if (err) + warnat(lx->pos, "attempting to undefine unrecognized symbol '%s'", s); + + ppend(lx); + return 0; +} + +/* #define */ +static +error +ppdef(Lexer *lx) +{ + int b; + Sym *sym; + int i, j, n, dot; + string s, a, base, end, buf, args[PPnarg]; + + s = ident(lx); + if (!s) { + errorat(lx->pos, "failed to parse defined identifer"); + goto Bad; + } + intern(&s); + printf("DEFINING %s\n", s); + lx->b = lx->buf; + + sym = lookup(&lx->sym, s); + if (sym) + warnat(lx->pos, "macro redefined: '%s'", sym->name); + + n = 0; + dot = 0; + b = getbyte(lx); + if (b == '(') { + b = getnsbyte(lx); + if (b != ')') { + ungetbyte(lx); + for (;;) { + // NOTE: This is a pointer into the lx->buffer. + // Can't reset lx->b while we hold the args! + a = identdots(lx, &dot); + if (a == nil) { + errorat(lx->pos, "macro syntax error: improper argument"); + goto Bad; + } + if (n >= PPnarg) { + errorat(lx->pos, "macro syntax error: too many arguments: %d > %d", n, PPnarg); + goto Bad; + } + + args[n++] = a; + b = getnsbyte(lx); + + if (b == ')') + break; + if (b != ',') { + errorat(lx->pos, "macro syntax error: bad token in argument '%b'", b); + goto Bad; + } + } + } + b = getbyte(lx); + } + + if (isspace(b)) + if (b != '\n') + b = getnsbyte(lx); + + base = lx->b; + end = lx->buf + arrlen(lx->buf); + if (base >= end) { + errorat(lx->pos, "out of macro buffer space!"); + goto Bad; + } + buf = str·makef("%c%c", n, PPbeg); + for (;;) { + if (isalpha(b) || b == '_') { + lx->b = base; + *lx->b++ = b; + + b = getbyte(lx); + while (isalnum(b) || b == '_') { + *lx->b++ = b; + if (lx->b >= end) { + errorat(lx->pos, "out of macro buffer space!"); + goto Bad; + } + b = getbyte(lx); + } + *lx->b++ = '\0'; + + for (i = 0; i < n; i++) { + if (strcmp(base, args[i]) == 0) { + goto Arg; + } + } + str·appendlen(&buf, (lx->b - base - 1), base); + continue; + Arg: + str·appendbyte(&buf, PParg); + str·appendbyte(&buf, 'a' + i); + continue; + } + + if (b == '/') { + b = getbyte(lx); + if (b == '/') { + while (b = getbyte(lx), b != '\n'); + continue; + } + if (b == '*') { + b = getbyte(lx); + for (;;) { + if (b == '*') { + b = getbyte(lx); + if (b != '/') + continue; + b = getbyte(lx); + break; + } + if (b == '\n') { + errorat(lx->pos, "comment and newline found in define statement of %s", s); + break; + } + b = getbyte(lx); + } + continue; + } + str·appendbyte(&buf, '/'); + continue; + } + + if (b == '\\') { + b = getbyte(lx); + /* unix */ + if (b == '\n') { + lx->pos.line++; + b = getbyte(lx); + continue; + } + /* windows */ + if (b == '\r') { + b = getbyte(lx); + if (b == '\n') { + lx->pos.line++; + b = getbyte(lx); + continue; + } + } + str·appendbyte(&buf, '\\'); + } + if (b == '\n') { + lx->pos.line++; + break; + } + + if (b == '#') { + b = getnsbyte(lx); + if (b == '#') { + str·appendbyte(&buf, PPcat); + b = getbyte(lx); + continue; + } + + lx->b = base; + while (isalnum(b) || b == '_') { + *lx->b++ = b; + b = getbyte(lx); + } + *lx->b = '\0'; + + for (i = 0; i < n; i++) { + if (strcmp(base, args[i]) == 0) + goto Str; + } + errorat(lx->pos, "macro operator '#' must be followed by a valid variable identifier"); + goto Bad; + Str: + str·appendbyte(&buf, PPstr); + str·appendbyte(&buf, 'a' + i); + continue; + } + + str·appendbyte(&buf, b); + b = getbyte(lx); + if (b == EOF) { + errorat(lx->pos, "eof found in macro '%s'", s); + goto Bad; + } + } + if (dot) + *buf |= PPvar; + + lx->b = lx->buf; + sym = defmacro(lx, s, buf); + return 0; +Bad: + errorat(lx->pos, "failed parse of #define macro '%s'", s); + lx->b = lx->buf; + ppend(lx); + return 1; +} + +/* macro expansion */ +int +expandmacro(Lexer *lx, Sym *s, byte *dst) +{ + int n, lv, nargs, dots; + byte b, *it, *e, *arg[PPnarg]; + + /* not a function macro */ + if (s->macro[0] == '\0') { + if (s->macro[1] != PPbeg) { + errorat(lx->pos, "malformed macro"); + goto Bad; + } + strcpy(dst, s->macro + 2); + return str·len(s->macro)-2; + } + dots = (ubyte)s->macro[0] & PPvar; + nargs = (ubyte)s->macro[0] & (~PPvar); + + b = getnsbyte(lx); + if (b != '(') { + errorat(lx->pos, "macro function not given arguments"); + goto Bad; + } + + n = 0; + b = getbyte(lx); + if (b != ')') { + ungetbyte(lx); + lv = 0; + lx->b = lx->buf; + e = lx->buf + arrlen(lx->buf) - 4; + arg[n++] = lx->buf; + for (;;) { + if (lx->b >= e) + goto Nospace; + b = getbyte(lx); + if (b == '"') + for (;;) { + if (lx->b >= e) + goto Nospace; + *lx->b++ = b; + b = getbyte(lx); + if (b == '\\') { + *lx->b++ = b; + b = getbyte(lx); + continue; + } + if (b == '\n') { + errorat(lx->pos, "newline found in arguments: macro '%s'", s->name); + goto Bad; + } + if (b == '"') + break; + } + if (b == '\'') + for (;;) { + if (lx->b >= e) + goto Nospace; + *lx->b++ = b; + b = getbyte(lx); + if (b == '\\') { + *lx->b++ = b; + b = getbyte(lx); + continue; + } + if (b == '\n') { + errorat(lx->pos, "newline found in arguments: macro '%s'", s->name); + goto Bad; + } + if (b == '"') + break; + } + if (b == '/') { + b = getbyte(lx); + switch(b) { + case '*': + for (;;) { + b = getbyte(lx); + if (b == '*') { + b = getbyte(lx); + if (b == '/') + break; + } + } + *lx->b++ = ' '; + continue; + case '/': + while ((b = getbyte(lx)) != '\n') + ; + break; + + default: + ungetbyte(lx); + b = '/'; + } + } + if (lv == 0) { + if (b == ',') { + if (n == nargs && dots) { + *lx->b++ = ','; + continue; + } + *lx->b++ = '\0'; + arg[n++] = lx->b; + if (n > nargs) + break; + continue; + } + if (b == ')') + break; + } + if (b == '\n') + b = ' '; + *lx->b++ = b; + if (b == '(') + lv++; + if (b == ')') + lv--; + } + *lx->b = '\0'; + } + + if (n != nargs) { + errorat(lx->pos, "number of arguments don't match macro definition: %s", s->name); + *dst = '\0'; + goto Bad; + } + + if (s->macro[1] != PPbeg) { + errorat(lx->pos, "corrupted macro buffer: %s", s->name); + *dst = '\0'; + goto Bad; + } + + it = s->macro+2; + e = dst; + for (;;) { + b = *it++; + if (b == '\n') + b = ' '; + switch (b) { + case PParg: + b = *it++; + b -= 'a'; + if (b < 0 && b > n) { + errorat(lx->pos, "malformed macro index: %s", s->name); + goto Bad; + } + strcpy(dst, arg[b]); + dst += strlen(arg[b]); + + break; + + case PPstr: + b = *it++; + b -= 'a'; + if (b < 0 && b > n) { + errorat(lx->pos, "malformed macro index: %s", s->name); + goto Bad; + } + *dst++ = '"'; + strcpy(dst, arg[b]); + *dst++ = '"'; + + break; + + case PPcat: + continue; + + case '\0': + goto End; + + default: + *dst++ = b; + continue; + } + } +End: + *dst = '\0'; + return dst - e; +Nospace: + errorat(lx->pos, "out of memory during macro expansion %s", s->name); +Bad: + ppend(lx); + lx->b = lx->buf; + errorat(lx->pos, "failed to expand macro %s", s->name); + return -1; +} + +/* #include */ +static +error +ppinc(Lexer *lx) +{ + int i; + byte b, end; + string s; + + Stream *f; + Io *io; + + b = getnsbyte(lx); + if (b != '"') { + end = b; + if (b != '<') { + errorat(lx->pos, "unrecognized token '%c' in include directive", b); + goto Bad; + } + end = '>'; + } else + end = '"'; + + lx->b = lx->buf; + for (;;) { + b = getbyte(lx); + if (b == end) + break; + if (b == '\n') { + errorat(lx->pos, "hit end of line before include directive completed"); + goto Bad; + } + *lx->b++ = b; + } + *lx->b = '\0'; + s = lx->buf; + intern(&s); // NOTE: we could use this to see if we already have the file + + lx->b = lx->buf; + for (i = 0; i < C.inc.len; i++) { + if (i == 0 && end == '>') + continue; + + strcpy(lx->buf, C.inc.dir[i]); + strcat(lx->buf, "/"); + + if (strcmp(lx->buf, "./") == 0) + lx->buf[0] = '\0'; + strcat(lx->buf, s); + + if (os·exists(lx->buf, ReadOK)) { + break; + } + } + if (i == C.inc.len) { + errorat(lx->pos, "could not find file '%s' on standard include search path", s); + goto Bad; + } + + io = openio(lx, lx->buf); + if (io != nil) { + pushio(lx, io); + } + + return 0; + +Bad: + ungetbyte(lx); + lx->b = lx->buf; + errorat(lx->pos, "failed include"); + ppend(lx); + return 1; +} + +/* #pragma */ +static +error +ppprag(Lexer *lx) +{ + string s; + + s = ident(lx); + if (s == nil) { + errorat(lx->pos, "failed to parse pragma identifier"); + goto Bad; + } + lx->b = lx->buf; + if (strcmp(s, "once") == 0) { + pushomit(lx, lx->io->path); + return 0; + } +Bad: + lx->b = lx->buf; + errorat(lx->pos, "unrecognized pragma '%s'", s); + ppend(lx); + return 1; +} + +/* all #if statements */ +static +error +ppif(Lexer *lx, int f) +{ + Sym *sym; + string s; + int c, l, b; + +Eval: + if (f == 0) { + b = evalmacro(lx, 1); + if (b) { + ppend(lx); + return 0; + } + goto Skip; + } + + if (f == 1) + goto Skip; + + s = ident(lx); + if (s == nil) { + errorat(lx->pos, "failed to parse preprocessor identifier"); + goto Bad; + } + intern(&s); + lx->b = lx->buf; + + sym = lookup(&lx->sym, s); + if ((!sym && (f == 3)) || (sym && (f == 2))) + return 0; + +Skip: + b = 1; + l = 0; + for (;;) { + c = getbyte(lx); + if (c != '#') { + if (!isspace(c)) + b = 0; + if (c == '\n') { + lx->pos.line++; + b = 1; + } + if (c == EOF) { + errorat(lx->pos, "EOF hit while skipping if block. Missing endif"); + goto Bad; + } + continue; + } + if (!b) + continue; + s = ident(lx); + lx->b = lx->buf; + if (!s) + continue; + + if (l == 0 && (strcmp(s, "elif") == 0)) { + f = 0; + goto Eval; + } + + if (strcmp(s, "endif") == 0) { + if (l) { + l--; + continue; + } + ppend(lx); + return 0; + } + if (strcmp(s, "if") == 0 || + strcmp(s, "ifdef") == 0 || + strcmp(s, "ifndef") == 0) { + l++; + continue; + } + + if (l == 0 && f != 1 && strcmp(s, "else") == 0) { + return 0; + } + } + +Bad: + lx->b = lx->buf; + errorat(lx->pos, "bad syntax in preprocessor conditional directive"); + ppend(lx); + return 1; +} + +/* #if */ +static +error +ppif0(Lexer *lx) +{ + return ppif(lx, 0); +} + +/* #else */ +static +error +ppif1(Lexer *lx) +{ + return ppif(lx, 1); +} + +/* #ifdef */ +static +error +ppif2(Lexer *lx) +{ + return ppif(lx, 2); +} + +/* #ifndef */ +static +error +ppif3(Lexer *lx) +{ + return ppif(lx, 3); +} + +// ----------------------------------------------------------------------- +// dispatch function + +#define DIRECTIVE(a, b, c) c, +error (*macros[NUM_DIRECTIVES])(Lexer*) = { DIRECTIVES }; +#undef DIRECTIVE + +/* reads an identifier into the lexer's buffer */ +/* caller must intern */ + +error +domacro(Lexer *lx) +{ + int n; + error err; + string s; + + s = ident(lx); + intern(&s); + lx->b = lx->buf; + for (n = 0; n < NUM_DIRECTIVES; n++) { + if ((uintptr)s == (uintptr)directives[n]) { + goto Do; + } + } + errorat(lx->pos, "unrecognized directive name '%s'", s); + return 1; +Do: + err = macros[n](lx); + return err; +} + +error +dodefine(Lexer *lx, string s) +{ + int n; + byte *c, *def; + Sym *sym; + + strcpy(lx->buf, s); + c = strchr(lx->buf, '='); + if (c) { + *c++ = '\0'; + sym = lookup(&lx->sym, lx->buf); + if (sym) { + errorf("redefinition of symbol '%s'", sym->name); + return 1; + } + sym = define(&lx->sym, lx->buf, Smacro); + n = strlen(c) + 2; + sym->macro = str·makelen("", n); + str·appendbyte(&sym->macro, '\0'); + str·append(&sym->macro, c); + } else { + sym = lookup(&lx->sym, lx->buf); + if (sym) { + errorf("redefinition of symbol '%s'", sym->name); + return 1; + } + sym = define(&lx->sym, s, Smacro); + sym->macro = "\00\02"; + } + + return 0; +} diff --git a/src/cmd/cc/rules.mk b/src/cmd/cc/rules.mk new file mode 100644 index 0000000..b7b4688 --- /dev/null +++ b/src/cmd/cc/rules.mk @@ -0,0 +1,21 @@ +include share/push.mk + +# local sources +SRCS_$(d):=\ + $(d)/pp.c\ + $(d)/lex.c\ + $(d)/ast.c\ + $(d)/bits.c\ + $(d)/cc.c +TEST_$(d) := + +# outputs +BINS_$(d) := $(d)/cc + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/cc/scratch.c b/src/cmd/cc/scratch.c new file mode 100644 index 0000000..b37d9a5 --- /dev/null +++ b/src/cmd/cc/scratch.c @@ -0,0 +1,7 @@ +#define XXX(a, b, c) a ## b ## c + +int +main() +{ + XXX(d, e, f); +} diff --git a/src/cmd/cc/util.c b/src/cmd/cc/util.c new file mode 100644 index 0000000..cca16f2 --- /dev/null +++ b/src/cmd/cc/util.c @@ -0,0 +1,21 @@ +#include "cc.h" + +void +·free(void* _, void* ptr) { + return free(ptr); +} + +void * +·alloc(void* _, uint n, ulong size) { + return malloc(n*size); +} + +void * +·calloc(void* _, uint n, ulong size) { + return calloc(n, size); +} + +void * +·realloc(void* _, void *ptr, uint n, ulong size) { + return realloc(ptr, n*size); +} diff --git a/src/cmd/dwm/LICENSE b/src/cmd/dwm/LICENSE new file mode 100644 index 0000000..d221f09 --- /dev/null +++ b/src/cmd/dwm/LICENSE @@ -0,0 +1,37 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/cmd/dwm/client.c b/src/cmd/dwm/client.c new file mode 100644 index 0000000..fa04f5f --- /dev/null +++ b/src/cmd/dwm/client.c @@ -0,0 +1,657 @@ +#include "dwm.h" + +void +applyrules(Client *c) +{ + char *class, *instance; + uint i; + Rule *r; + Monitor *m; + XClassHint ch = { nil, nil }; + + c->isfloating = 0; + c->tags = 0; + c->noswallow = -1; + + /* rule matching */ + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + for (i = 0; i < arrlen(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isterm = r->isterm; + c->noswallow = r->noswallow; + c->isfloating = r->isfloating; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next) + ; + if (m) + c->mon = m; + } + } + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +enqueue(Client *c) +{ + Client *l; + + for (l = c->mon->clients; l && l->next; l = l->next) + ; + + if (l) { + l->next = c; + c->next = nil; + } +} + +void +attachbottom(Client *c) +{ + Client **tc; + c->next = nil; + for (tc = &c->mon->clients; *tc; tc = &(*tc)->next) + ; + *tc = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +enqueuestack(Client *c) +{ + Client *l; + for (l = c->mon->clients; l && l->next; l = l->next) + ; + + if (l) { + l->snext = c; + c->snext = nil; + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next) + ; + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext) + ; + + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext) + ; + c->mon->sel = t; + } +} + +void +focus(Client *c) +{ + if (!c || !ISVISIBLE(c)) + for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext) + ; + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + if (c) { + if (c->mon != selmon) + selmon = c->mon; + if (c->isurgent) + seturgent(c, 0); + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + uchar *p = nil; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + uint i, j; + uint modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < arrlen(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < arrlen(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next) + ; + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +sendtomon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + /* attach(c); */ + attachbottom(c); + attachstack(c); + focus(nil); + arrange(nil); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (uchar *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (uchar *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + static uint32 opacity = 0xFFFFFFFFul; + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (uchar*)&netatom[NetWMFullscreen], 1); + XChangeProperty(dpy, c->win, netatom[NetWMWindowOpacity], XA_CARDINAL, 32, PropModeReplace, (uchar *)&opacity, 1L); + + 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); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (uchar*)nil, 0); + XDeleteProperty(dpy, c->win, netatom[NetWMWindowOpacity]); + + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + return; + if (ISVISIBLE(c)) { + /* show clients top down */ + XMoveWindow(dpy, c->win, c->x, c->y); + if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) + resize(c, c->x, c->y, c->w, c->h, 0); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +swallow(Client *p, Client *c) +{ + Client *s; + + + if (c->noswallow > 0 || c->isterm) + return; + if (c->noswallow < 0 && !swallowfloating && c->isfloating) + return; + + detach(c); + detachstack(c); + + setclientstate(c, WithdrawnState); + XUnmapWindow(dpy, p->win); + + p->swallowing = c; + c->mon = p->mon; + + Window w = p->win; + p->win = c->win; + c->win = w; + + XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(p->win), 1); + + updatetitle(p); + s = scanner ? c : p; + XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); + arrange(p->mon); + configure(p); + updateclientlist(); +} + +Client * +termof(Client *w) +{ + Client *c; + Monitor *m; + + if (!w->pid || w->isterm) + return NULL; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->isterm && !c->swallowing && c->pid && isdescendent(c->pid, w->pid)) + return c; + } + } + + return NULL; +} + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +void +unmanage(Client *c, int destroyed) +{ + Client *s; + Monitor *m = c->mon; + XWindowChanges wc; + + if (c->swallowing) { + unswallow(c); + return; + } + + s = swallowing(c->win); + if (s) { + free(s->swallowing); + s->swallowing = nil; + arrange(m); + focus(nil); + return; + } + + + detach(c); + detachstack(c); + + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + free(c); + focus(nil); + updateclientlist(); + arrange(m); + + if (!s) { + // arrange(m); + focus(nil); + updateclientlist(); + } +} + +void +unswallow(Client *c) +{ + c->win = c->swallowing->win; + + free(c->swallowing); + c->swallowing = nil; + + XDeleteProperty(dpy, c->win, netatom[NetClientList]); + + /* unfullscreen the client */ + setfullscreen(c, 0); + updatetitle(c); + arrange(c->mon); + XMapWindow(dpy, c->win); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + setclientstate(c, NormalState); + focus(nil); + arrange(c->mon); +} + + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); +} + +void +updatetitle(Client *c) +{ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if (c->name[0] == '\0') /* hack to mark broken clients */ + strcpy(c->name, broken); +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} diff --git a/src/cmd/dwm/config.h b/src/cmd/dwm/config.h new file mode 100644 index 0000000..1f82b1f --- /dev/null +++ b/src/cmd/dwm/config.h @@ -0,0 +1,141 @@ +/* See LICENSE file for copyright and license details. */ +#define VERSION "1" + +/* appearance */ +static uint borderpx = 2; /* border pixel of windows */ +static uint gapx = 4; /* gaps between windows */ +static uint snap = 32; /* snap pixel */ +static int swallowfloating = 0; /* 1 will swallow floating by default */ +static int showbar = 1; /* 0 means no bar */ +static int topbar = 1; /* 0 means bottom bar */ +static char *fonts[] = { "consolas:size=16" }; +static char col_gray1[] = "#504945"; +static char col_gray2[] = "#282828"; +static char col_gray3[] = "#fbf1c7"; +static char col_gray4[] = "#504945"; +static char col_cyan[] = "#83a598"; +static char *colors[][3] = +{ + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +}; + +/* tagging */ +static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + +static Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { "Gimp", nil, nil, 0, 1, 0, 0, -1 }, + { "Inkscape", nil, nil, 0, 1, 0, 0, -1 }, + { "zoom", nil, nil, 0, 1, 0, 0, -1 }, + { "qutebrowser", nil, nil, 0, 0, 0, 0, -1 }, + { "term-256color", nil, nil, 0, 0, 1, -1, -1 }, +}; + +/* layout(s) */ +static float mfact = 0.55; /* factor of master area size [0.05..0.95] */ +static int nmaster = 1; /* number of clients in master area */ +static int resizehints = 1; /* 1 means respect size hints in tiled resizals */ + +static Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", nil }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod4Mask +#define TAGKEYS(KEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + +/* commands */ +static char *menucmd[] = { "menu_run", nil }; +static char *termcmd[] = { "term", nil }; +static char *webscmd[] = { "qutebrowser", nil }; +static char scratchname[] = "scratchpad"; +static char *scratchcmd[] = { "term", "-t", scratchname, "-g", "120x34", nil }; +static char *upvolcmd[] = { "vol", "+5%", nil }; +static char *lovolcmd[] = { "vol", "-5%", nil }; +static char *novolcmd[] = { "vol", "mute", nil }; + +#define XK_lovol XF86XK_AudioLowerVolume +#define XK_upvol XF86XK_AudioRaiseVolume +#define XK_novol XF86XK_AudioMute + +static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_d, spawn, {.v = menucmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_q, spawn, {.v = webscmd } }, + { 0, XK_upvol, spawn, {.v = upvolcmd} }, + { 0, XK_lovol, spawn, {.v = lovolcmd} }, + { 0, XK_novol, spawn, {.v = novolcmd} }, + { MODKEY, XK_s, togglescratch, {.v = scratchcmd} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_f, togglefocus, {0} }, + { MODKEY, XK_Up, focusstack, {.i = +1 } }, + { MODKEY, XK_Down, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_Up, rotatestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_Down, rotatestack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_o, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, focusdirection, {.i = 'l'} }, + { MODKEY, XK_l, focusdirection, {.i = 'r'} }, + { MODKEY, XK_k, focusdirection, {.i = 'u'} }, + { MODKEY, XK_j, focusdirection, {.i = 'd'} }, + { MODKEY|ShiftMask, XK_h, setmfact, {.f = -0.05} }, + { MODKEY|ShiftMask, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } }, + { 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]} }, + { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + { MODKEY, XK_comma, focusmon, {.i = -1 } }, + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) + TAGKEYS( XK_4, 3) + TAGKEYS( XK_5, 4) + TAGKEYS( XK_6, 5) + TAGKEYS( XK_7, 6) + TAGKEYS( XK_8, 7) + TAGKEYS( XK_9, 8) + { MODKEY|ShiftMask, XK_e, quit, {0} }, +}; + +/* button definitions */ +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ +static Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/src/cmd/dwm/drw.c b/src/cmd/dwm/drw.c new file mode 100644 index 0000000..a6d6902 --- /dev/null +++ b/src/cmd/dwm/drw.c @@ -0,0 +1,376 @@ +/* See LICENSE file for copyright and license details. */ +#include "dwm.h" + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + fatal("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + fatal("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + rune utf8codepoint = 0; + char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8·decode(text, &utf8codepoint); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + fatal("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/src/cmd/dwm/dwm.c b/src/cmd/dwm/dwm.c new file mode 100644 index 0000000..0567650 --- /dev/null +++ b/src/cmd/dwm/dwm.c @@ -0,0 +1,1185 @@ +#include "dwm.h" + +/* global variables */ +char broken[] = ""; +char stext[256]; +int scanner; +int screen; +int sw, sh; /* X display screen geometry width, height */ +int bh, blw = 0; /* bar geometry */ +int lrpad; /* sum of left and right padding for text */ +int (*xerrorxlib)(Display *, XErrorEvent *); +uint numlockmask = 0; +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; + +xcb_connection_t *xcon; + +Atom wmatom[WMLast] = {0}, netatom[NetLast] = {0}; +int running = 1; +Cur *cursor[MouseLast] = {0}; +Clr **scheme = nil; +Display *dpy = nil; +Drw *drw = nil; +Monitor *mons = nil, *selmon = nil; +Window root = {0}, wmcheckwin = {0}; + +/* compile-time check if all tags fit into an uint bit array. */ +struct NumTags { char limitexceeded[arrlen(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else for (m = mons; m; m = m->next) + showhide(m->stack); + if (m) { + arrangemon(m); + restack(m); + } else for (m = mons; m; m = m->next) + arrangemon(m); +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +buttonpress(XEvent *e) +{ + uint i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(nil); + } + if (ev->window == selmon->barwin) { + i = x = 0; + do + x += TEXTW(tags[i]); + while (ev->x >= x && ++i < arrlen(tags)); + if (i < arrlen(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + blw) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < arrlen(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", nil }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < MouseLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < arrlen(colors); i++) + free(scheme[i]); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || cme->data.l[2] == netatom[NetWMFullscreen]) + setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ + || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) + if (c->isfullscreen) + resizeclient(c, m->mx, m->my, m->mw, m->mh); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); + } + focus(nil); + arrange(nil); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +Monitor * +createmon(void) +{ + Monitor *m; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % arrlen(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); + else if ((c = swallowing(ev->window))) + unmanage(c->swallowing, 1); +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = nil; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + uint i, occ = 0, urg = 0; + Client *c; + + /* draw status first so it can be overdrawn by tags later */ + if(m == selmon) { /* status is only drawn on selected monitor */ + drw_setscheme(drw, scheme[SchemeNorm]); + tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); + } + + for(c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + for(i = 0; i < arrlen(tags); i++) { + w = TEXTW(tags[i]); + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = blw = TEXTW(m->ltsymbol); + drw_setscheme(drw, scheme[SchemeNorm]); + x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + + if((w = m->ww - tw - x) > bh) { + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); + if (m->sel->isfloating) + drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } + drw_map(drw, m->barwin, 0, 0, m->ww, bh); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +int +getrootptr(int *x, int *y) +{ + int di; + uint dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = nil; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, uint size) +{ + char **list = nil; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) + strncpy(text, (char *)name.value, size - 1); + else { + if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + uint i, j; + uint modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < arrlen(keys); i++) + if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) + for (j = 0; j < arrlen(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, + True, GrabModeAsync, GrabModeAsync); + } +} + +static +int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} + +void +keypress(XEvent *e) +{ + uint i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); + for (i = 0; i < arrlen(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = nil, *term = nil; + Window trans = None; + XWindowChanges wc; + + c = ecalloc(1, sizeof(Client)); + c->win = w; + c->pid = winpid(w); + /* geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + term = termof(c); + } + + if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) + c->x = c->mon->mx + c->mon->mw - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) + c->y = c->mon->my + c->mon->mh - HEIGHT(c); + c->x = MAX(c->x, c->mon->mx); + /* only fix client y-offset, if the client center might cover the bar */ + c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) + && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + c->bw = borderpx; + + selmon->tagset[selmon->seltags] &= ~scratchtag; + if(!strcmp(c->name, scratchname)) { + c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag; + c->isfloating = 1; + c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); + c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); + } + + wc.border_width = c->bw; + XConfigureWindow(dpy, w, CWBorderWidth, &wc); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + + if (!c->isfloating) + c->isfloating = c->oldstate = trans != None || c->isfixed; + if (c->isfloating) + XRaiseWindow(dpy, c->win); + + /* attach(c); */ + attachbottom(c); + attachstack(c); + + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ + setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); + XMapWindow(dpy, c->win); + if (term) + swallow(term, c); + focus(nil); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if (wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +void +monocle(Monitor *m) +{ + uint n = 0; + Client *c; + + for (c = m->clients; c; c = c->next) + if (ISVISIBLE(c)) + n++; + if (n > 0) /* override layout symbol */ + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) + resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = nil; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(nil); + } + mon = m; +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != nil)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + uint i, num; + Window d1, d2, *wins = nil; + XWindowAttributes wa; + char swin[256]; + + scanner = 1; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } + + scanner = 0; +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + + /* clean up any zombies immediately */ + sigchld(0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, arrlen(fonts))) + fatal("no fonts could be loaded."); + + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); + netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); + netatom[NetWMWindowOpacity] = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); + netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + + /* init cursors */ + cursor[MouseNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[MouseResize] = drw_cur_create(drw, XC_sizing); + cursor[MouseMove] = drw_cur_create(drw, XC_fleur); + + /* init appearance */ + scheme = ecalloc(arrlen(colors), sizeof(Clr *)); + for (i = 0; i < arrlen(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + + /* init bars */ + updatebars(); + updatestatus(); + + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (uchar *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (uchar *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (uchar *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (uchar *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[MouseNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(nil); +} + + +void +sigchld(int unused) +{ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + fatal("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, nil, WNOHANG)); +} + +Client * +swallowing(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) { + for (c = m->clients; c; c = c->next) { + if (c->swallowing && c->swallowing->win == w) + return c; + } + } + + return nil; +} + +void +tile(Monitor *m) +{ + uint i, n, h, r, mw, my, ty; + Client *c; + + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) + ; + + if (n == 0) + return; + + if (n > m->nmaster) + mw = m->nmaster ? (m->ww+gapx) * m->mfact : 0; + else + mw = m->ww - gapx; + + for (i = 0, my = ty = gapx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) + if (i < m->nmaster) { + r = MIN(n, m->nmaster) - i; + h = (m->wh - my)/r - gapx; + resize(c, m->wx + gapx, m->wy + my, mw - (2*c->bw) - gapx, h - (2*c->bw), 0); + if (my + HEIGHT(c) + gapx < m->wh) + my += HEIGHT(c) + gapx; + } else { + r = (n-i); + h = (m->wh - ty)/r - gapx; + resize(c, m->wx + mw + gapx, m->wy + ty, m->ww - mw - (2*c->bw) - (2*gapx), h - (2*c->bw), 0); + if (ty + HEIGHT(c) + gapx < m->wh) + ty += HEIGHT(c) + gapx; + } +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + for (m = mons; m; m = m->next) { + if (m->barwin) + continue; + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[MouseNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist() +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = nil; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + if (n <= nn) { /* new monitors available */ + for (i = 0; i < (nn - n); i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + } else { /* less monitors available nn < n */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + /* attach(c); */ + attachbottom(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + } + free(unique); + } else + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + uint i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +pid_t +winpid(Window w) +{ + pid_t result = 0; + + xcb_res_client_id_spec_t spec = {0}; + spec.client = w; + spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; + + xcb_generic_error_t *e = NULL; + xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); + xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); + + if (!r) + return (pid_t)0; + + xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); + for (; i.rem; xcb_res_client_id_value_next(&i)) { + spec = i.data->spec; + if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { + uint32_t *t = xcb_res_client_id_value_value(i.data); + result = *t; + break; + } + } + + free(r); + + if (result == (pid_t)-1) + result = 0; + + return result; +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return nil; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + fatal("dwm: another window manager is already running"); + return -1; +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + fatal("dwm-"VERSION); + else if (argc != 1) + fatal("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(nil))) + fatal("dwm: cannot open display"); + if (!(xcon = XGetXCBConnection(dpy))) + fatal("dwm: cannot get xcb connection"); + + checkotherwm(); + setup(); + +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", nil) == -1) + fatal("pledge"); +#endif /* __OpenBSD__ */ + + scan(); + run(); + cleanup(); + + XCloseDisplay(dpy); + return 0; +} diff --git a/src/cmd/dwm/dwm.h b/src/cmd/dwm/dwm.h new file mode 100644 index 0000000..afec1f2 --- /dev/null +++ b/src/cmd/dwm/dwm.h @@ -0,0 +1,384 @@ +/* See LICENSE file for copyright and license details. */ +#pragma once +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << arrlen(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + + +/* enums */ +enum +{ + MouseNormal, + MouseResize, + MouseMove, + MouseLast, +}; /* mouse states */ + +enum +{ + SchemeNorm, + SchemeSel +}; /* color schemes */ + +enum +{ + NetSupported, + NetWMName, + NetWMState, + NetWMCheck, + NetWMFullscreen, + NetActiveWindow, + NetWMWindowType, + NetWMWindowTypeDialog, + NetWMWindowOpacity, + NetClientList, + NetLast +}; /* EWMH atoms */ + +enum +{ + WMProtocols, + WMDelete, + WMState, + WMTakeFocus, + WMLast +}; /* default atoms */ + +enum +{ + ClkTagBar, + ClkLtSymbol, + ClkStatusText, + ClkWinTitle, + ClkClientWin, + ClkRootWin, + ClkLast +}; /* clicks */ + +enum +{ + ColFg, + ColBg, + ColBorder +}; /* color scheme index */ + +typedef struct Monitor Monitor; +typedef struct Layout Layout; +typedef struct Client Client; +typedef struct Keyboard Keyboard; +typedef struct Button Button; +typedef struct Key Key; +typedef struct Rule Rule; +typedef union Arg Arg; + +union Arg { + int i; + uint ui; + float f; + void *v; +}; + +struct Button { + uint click; + uint mask; + uint button; + void (*func)(Arg *arg); + Arg arg; +}; + +struct Client { + char name[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int bw, oldbw; + uint tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterm, noswallow; + pid_t pid; + Client *next; + Client *snext; + Client *swallowing; + Monitor *mon; + Window win; +}; + +struct Key { + uint mod; + KeySym keysym; + void (*func)(Arg *); + Arg arg; +}; + +struct Layout { + char *symbol; + void (*arrange)(Monitor *); +}; + +struct Monitor { + char ltsymbol[16]; + float mfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + uint seltags; + uint sellt; + uint tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + Layout *lt[2]; +}; + +struct Rule { + char *class; + char *instance; + char *title; + uint tags; + int isfloating; + int isterm; + int noswallow; + int monitor; +}; + +/* draw.c */ +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + uint h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +typedef XftColor Clr; + +typedef struct { + uint w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* global state */ + +extern char broken[]; +extern char stext[256]; +extern int scanner; +extern int screen; +extern int sw, sh; +extern int bh, blw; +extern int lrpad; +extern int (*xerrorxlib)(Display *, XErrorEvent *); +extern uint numlockmask; +extern void (*handler[LASTEvent]) (XEvent *); +extern int scratchtag; + +extern xcb_connection_t *xcon; + +extern Atom wmatom[WMLast], netatom[NetLast]; +extern int running; +extern Cur *cursor[MouseLast]; +extern Clr **scheme; +extern Display *dpy; +extern Drw *drw; +extern Monitor *mons, *selmon; +extern Window root, wmcheckwin; + +// ----------------------------------------------------------------------- +// function declarations + +// TODO: remove declarations that don't require global existence... +void applyrules(Client *c); +int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +void arrange(Monitor *m); +void arrangemon(Monitor *m); +void attach(Client *c); +void enqueue(Client *c); +void attachbottom(Client *c); +void attachstack(Client *c); +void enqueuestack(Client *c); +void buttonpress(XEvent *e); +void checkotherwm(void); +void cleanup(void); +void cleanupmon(Monitor *mon); +void clientmessage(XEvent *e); +void configure(Client *c); +void configurenotify(XEvent *e); +void configurerequest(XEvent *e); +Monitor *createmon(void); +void destroynotify(XEvent *e); +void detach(Client *c); +void detachstack(Client *c); +Monitor *dirtomon(int dir); +void drawbar(Monitor *m); +void drawbars(void); +void enternotify(XEvent *e); +void expose(XEvent *e); +void focus(Client *c); +void focusin(XEvent *e); +void focusmon(Arg *arg); +void focusstack(Arg *arg); +void focusdirection(Arg *arg); +void rotatestack(Arg *arg); +Atom getatomprop(Client *c, Atom prop); +int getrootptr(int *x, int *y); +long getstate(Window w); +int gettextprop(Window w, Atom atom, char *text, uint size); +void grabbuttons(Client *c, int focused); +void grabkeys(void); +void incnmaster(Arg *arg); +void keypress(XEvent *e); +void killclient(Arg *arg); +void manage(Window w, XWindowAttributes *wa); +void mappingnotify(XEvent *e); +void maprequest(XEvent *e); +void monocle(Monitor *m); +void motionnotify(XEvent *e); +void movemouse(Arg *arg); +Client *nexttiled(Client *c); +void pop(Client *); +void propertynotify(XEvent *e); +void quit(Arg *arg); +Monitor *recttomon(int x, int y, int w, int h); +void resize(Client *c, int x, int y, int w, int h, int interact); +void resizeclient(Client *c, int x, int y, int w, int h); +void resizemouse(Arg *arg); +void restack(Monitor *m); +void run(void); +void scan(void); +int sendevent(Client *c, Atom proto); +void sendtomon(Client *c, Monitor *m); +void setclientstate(Client *c, long state); +void setfocus(Client *c); +void setfullscreen(Client *c, int fullscreen); +void setlayout(Arg *arg); +void setmfact(Arg *arg); +void setup(void); +void seturgent(Client *c, int urg); +void showhide(Client *c); +void sigchld(int unused); +void swallow(Client *p, Client *c); +Client *swallowing(Window w); +void spawn(Arg *arg); +void tag(Arg *arg); +void tagmon(Arg *arg); +Client *termof(Client *c); +void tile(Monitor *); +void togglebar(Arg *arg); +void togglefocus(Arg *arg); +void togglefloating(Arg *arg); +void togglescratch(Arg *arg); +void toggletag(Arg *arg); +void toggleview(Arg *arg); +void unfocus(Client *c, int setfocus); +void unmanage(Client *c, int destroyed); +void unmapnotify(XEvent *e); +void unswallow(Client *c); +void updatebarpos(Monitor *m); +void updatebars(void); +void updateclientlist(void); +int updategeom(void); +void updatenumlockmask(void); +void updatesizehints(Client *c); +void updatestatus(void); +void updatetitle(Client *c); +void updatewindowtype(Client *c); +void updatewmhints(Client *c); +void view(Arg *arg); +pid_t winpid(Window w); +Client *wintoclient(Window w); +Monitor *wintomon(Window w); +int xerror(Display *dpy, XErrorEvent *ee); +int xerrordummy(Display *dpy, XErrorEvent *ee); +int xerrorstart(Display *dpy, XErrorEvent *ee); +void zoom(Arg *arg); + +#include "config.h" + +/* draw.c */ + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, uint w, uint h); +void drw_resize(Drw *drw, uint w, uint h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +uint drw_fontset_getwidth(Drw *drw, char *text); +void drw_font_getexts(Fnt *font, char *text, uint len, uint *w, uint *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, char *clrname); +Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, uint w, uint h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, uint w, uint h, uint lpad, char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, uint w, uint h); + +/* util.c */ +void fatal(char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); +pid_t getparentproc(pid_t p); +pid_t isdescendent(pid_t p, pid_t c); diff --git a/src/cmd/dwm/hook.c b/src/cmd/dwm/hook.c new file mode 100644 index 0000000..9758965 --- /dev/null +++ b/src/cmd/dwm/hook.c @@ -0,0 +1,489 @@ +#include "dwm.h" + +int scratchtag = 1 << arrlen(tags); + +void +focusmon(Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(nil); +} + +void +focusstack(Arg *arg) +{ + Client *c = nil, *i; + + if (!selmon->sel) + return; + if (arg->i > 0) { + for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if(!c) + for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + } else { + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i)) + c = i; + if(!c) + for(; i; i = i->next) + if (ISVISIBLE(i)) + c = i; + } + if(c) { + focus(c); + restack(selmon); + } +} + +void +focusdirection(Arg *arg) +{ + Monitor *m; + Client *it, *c; + int x, y, cx, cy; + + if(!selmon || !selmon->sel) + return; + + c = selmon->sel; + x = c->x, y = c->y; + + c = nil; + switch(arg->i) { + case 'l': + cx = INT_MIN; + cy = y; + for(m=mons; m; m=m->next) { + for(it=m->clients; it; it = it->next) { + if(ISVISIBLE(it) && (it->x < x)) { + if((it->x > cx) || ((it->x == cx) && abs(y-it->y) < abs(y-cy))) { + c = it; + cx = it->x; + cy = it->y; + } + } + } + } + break; + + case 'r': + cx = INT_MAX; + cy = y; + for(m=mons; m; m=m->next) { + for(it=m->clients; it; it = it->next) { + if(ISVISIBLE(it) && (it->x > x)) { + if((it->x < cx) || ((it->x == cx) && abs(y-it->y) < abs(y-cy))) { + c = it; + cx = it->x; + cy = it->y; + } + } + } + } + break; + + case 'u': + cx = x; + cy = INT_MIN; + for(m=mons; m; m=m->next) { + for(it=m->clients; it; it = it->next) { + if(ISVISIBLE(it) && (it->y < y)) { + if((it->y > cy) || ((it->y == cy) && abs(x-it->x) < abs(x-cx))) { + c = it; + cx = it->x; + cy = it->y; + } + } + } + } + break; + + case 'd': + cx = x; + cy = INT_MAX; + for(m=mons; m; m=m->next) { + for(it=m->clients; it; it = it->next) { + if(ISVISIBLE(it) && (it->y > y)) { + if((it->y < cy) || ((it->y == cy) && abs(x-it->x) < abs(x-cx))) { + c = it; + cx = it->x; + cy = it->y; + } + } + } + } + break; + + default: + ; + } + + if(c) { + focus(c); + restack(selmon); + if(c->mon != selmon) + restack(c->mon); + } +} + +void +rotatestack(Arg *arg) +{ + Client *c = nil, *f; + + if (!selmon->sel) + return; + + f = selmon->sel; + if (arg->i > 0) { + for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)) + ; + + if (c) { + detach(c); + attach(c); + detachstack(c); + attachstack(c); + } + } else { + if ((c = nexttiled(selmon->clients))) { + detach(c); + enqueue(c); + detachstack(c); + enqueuestack(c); + } + } + + if (c) { + arrange(selmon); + focus(f); + restack(selmon); + } +} + + +void +incnmaster(Arg *arg) +{ + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +void +killclient(Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +void +movemouse(Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[MouseMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(nil); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendtomon(c, m); + selmon = m; + focus(nil); + } +} + +void +quit(Arg *arg) +{ + running = 0; +} + +void +resizemouse(Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[MouseResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(nil); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendtomon(c, m); + selmon = m; + focus(nil); + } +} + +void +setlayout(Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); + if (selmon->sel) + arrange(selmon); + else + drawbar(selmon); +} + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(Arg *arg) +{ + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.05 || f > 0.95) + return; + selmon->mfact = f; + arrange(selmon); +} + +void +spawn(Arg *arg) +{ + selmon->tagset[selmon->seltags] &= ~scratchtag; + + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); + perror(" failed"); + exit(EXIT_SUCCESS); + } +} + +void +tag(Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(nil); + arrange(selmon); + } +} + +void +tagmon(Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendtomon(selmon->sel, dirtomon(arg->i)); +} + +void +togglebar(Arg *arg) +{ + selmon->showbar = !selmon->showbar; + updatebarpos(selmon); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + arrange(selmon); +} + +void +togglefloating(Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +togglescratch(Arg *arg) +{ + Client *c; + uint f = 0; + + for(c = selmon->clients; c && !(f = (c->tags & scratchtag)); c = c->next) + ; + + if(f) { + f = selmon->tagset[selmon->seltags] ^ scratchtag; + if(f) { + selmon->tagset[selmon->seltags] = f; + focus(nil); + arrange(selmon); + } + if(ISVISIBLE(c)) { + focus(c); + restack(selmon); + } + } else + spawn(arg); + +} + +void +toggletag(Arg *arg) +{ + uint newtags; + if (!selmon->sel) + return; + + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(nil); + arrange(selmon); + } +} + +void +togglefocus(Arg *arg) +{ + if (selmon->sel) + setfullscreen(selmon->sel, !selmon->sel->isfullscreen); + + togglebar(arg); +} + +void +toggleview(Arg *arg) +{ + uint newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focus(nil); + arrange(selmon); + } +} + +void +view(Arg *arg) +{ + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focus(nil); + arrange(selmon); +} + +void +zoom(Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange + || (selmon->sel && selmon->sel->isfloating)) + return; + if (c == nexttiled(selmon->clients)) + if (!c || !(c = nexttiled(c->next))) + return; + pop(c); +} diff --git a/src/cmd/dwm/rules.mk b/src/cmd/dwm/rules.mk new file mode 100644 index 0000000..a840217 --- /dev/null +++ b/src/cmd/dwm/rules.mk @@ -0,0 +1,29 @@ +include share/push.mk + +# local sources +SRCS_$(d):=\ + $(d)/drw.c\ + $(d)/hook.c\ + $(d)/client.c\ + $(d)/util.c\ + $(d)/dwm.c + +# outputs +BINS_$(d) := $(d)/dwm + +include share/paths.mk + +# Local rules +include share/dynamic.mk +$(BINS_$(d)): TCFLAGS=\ + `$(PKG) --cflags fontconfig`\ + `$(PKG) --cflags freetype2` +$(BINS_$(d)): TCLIBS=\ + `$(PKG) --libs fontconfig`\ + `$(PKG) --libs freetype2`\ + -lX11 -lXinerama -lXft -lX11-xcb -lxcb -lxcb-res + +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libutf/libutf.a $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/dwm/util.c b/src/cmd/dwm/util.c new file mode 100644 index 0000000..0db71cc --- /dev/null +++ b/src/cmd/dwm/util.c @@ -0,0 +1,66 @@ +/* See LICENSE file for copyright and license details. */ +#include "dwm.h" + +void +fatal(char *fmt, ...) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + if(fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + fatal("calloc:"); + return p; +} + +pid_t +getparentprocess(pid_t p) +{ + uint v = 0; + +#if defined(__linux__) + io·Stream *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return (pid_t)0; + + if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) + v = (pid_t)0; + fclose(f); +#elif defined(__FreeBSD__) + struct kinfo_proc *proc = kinfo_getproc(p); + if (!proc) + return (pid_t)0; + + v = proc->ki_ppid; + free(proc); +#endif + return (pid_t)v; +} + +int +isdescendent(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} diff --git a/src/cmd/filter/filter.c b/src/cmd/filter/filter.c new file mode 100644 index 0000000..abc9a88 --- /dev/null +++ b/src/cmd/filter/filter.c @@ -0,0 +1,104 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include + +#include +#include + +#define FLAG(x) (flag[(x)-'a']) + +static void filter(const char *, const char *); +static void usage(void); + +static int match = 0; +static int flag[26]; +static struct stat old, new; + +static +void +filter(const char *path, const char *name) +{ + struct stat st, ln; + + if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ + && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ + && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ + && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ + && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ + && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ + && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ + && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ + && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ + && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ + && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ + && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ + && (!FLAG('s') || st.st_size > 0) /* not empty */ + && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ + && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ + && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ + if (FLAG('q')) + exit(0); + match = 1; + puts(name); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " + "[-n file] [-o file] [file...]\n", argv0); + exit(2); /* like test(1) return > 1 on error */ +} + +int +main(int argc, char *argv[]) +{ + struct dirent *d; + char path[PATH_MAX], *line = NULL, *file; + size_t linesiz = 0; + ssize_t n; + DIR *dir; + int r; + + ARGBEGIN { + case 'n': /* newer than file */ + case 'o': /* older than file */ + file = EARGF(usage()); + if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) + perror(file); + break; + default: + /* miscellaneous operators */ + if (strchr("abcdefghlpqrsuvwx", ARGC())) + FLAG(ARGC()) = 1; + else + usage(); /* unknown flag */ + } ARGEND; + + if (!argc) { + /* read list from stdin */ + while ((n = getline(&line, &linesiz, stdin)) > 0) { + if (n && line[n - 1] == '\n') + line[n - 1] = '\0'; + filter(line, line); + } + free(line); + } else { + for (; argc; argc--, argv++) { + if (FLAG('l') && (dir = opendir(*argv))) { + /* filter directory contents */ + while ((d = readdir(dir))) { + r = snprintf(path, sizeof path, "%s/%s", + *argv, d->d_name); + if (r >= 0 && (size_t)r < sizeof path) + filter(path, d->d_name); + } + closedir(dir); + } else { + filter(*argv, *argv); + } + } + } + return match ? 0 : 1; +} diff --git a/src/cmd/filter/rules.mk b/src/cmd/filter/rules.mk new file mode 100644 index 0000000..13ddd56 --- /dev/null +++ b/src/cmd/filter/rules.mk @@ -0,0 +1,14 @@ +include share/push.mk + +# local sources +SRCS_$(d):=$(d)/filter.c +# outputs +BINS_$(d):=$(d)/filter + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/ic/LICENSE b/src/cmd/ic/LICENSE new file mode 100644 index 0000000..a5816a8 --- /dev/null +++ b/src/cmd/ic/LICENSE @@ -0,0 +1,23 @@ +MIT/X Consortium License + +(C)opyright 2014-2018 Hiltjo Posthuma +(C)opyright 2005-2006 Anselm R. Garbe +(C)opyright 2005-2011 Nico Golde + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/cmd/ic/ic.1 b/src/cmd/ic/ic.1 new file mode 100644 index 0000000..3302dad --- /dev/null +++ b/src/cmd/ic/ic.1 @@ -0,0 +1,100 @@ +.TH II 1 ic\-VERSION +.SH NAME +ic \- irc it or irc improved +.SH DESCRIPTION +.B ic +is a minimalistic FIFO and filesystem based IRC client. +It creates an irc directory tree with server, channel and +nick name directories. +In every directory a FIFO file (in) and normal file (out) +is placed. This will be for example ~/irc/irc.freenode.net/. +The in file is used to communicate with the servers and the out +files includes the server messages. For every channel and every nick +name there will be new in and out files. +The basic idea of this is to be able to communicate with an IRC +server with basic command line tools. +For example if you will join a channel just do echo "/j #channel" > in +and ic creates a new channel directory with in and out file. +.SH SYNOPSIS +.B ic +.RB < \-s +.IR servername > +.RB [ \-p +.IR port ] +.RB [ \-k +.IR "environment variable" ] +.RB [ \-i +.IR prefix ] +.RB [ \-n +.IR nickname ] +.RB [ \-f +.IR realname ] +.RB < \-u +.IR sockname > +.SH OPTIONS +.TP +.BI \-s " servername" +server to connect to, for example: irc.freenode.net +.TP +.BI \-u " sockname" +connect to a UNIX domain socket instead of directly to a server. +.TP +.BI \-p " port" +lets you override the default port (6667) +.TP +.BI \-k " environment variable" +lets you specify an environment variable that contains your IRC password, e.g. IIPASS="foobar" ic -k IIPASS. +This is done in order to prevent other users from eavesdropping the server password via the process list. +.TP +.BI \-i " prefix" +lets you override the default irc path (~/irc) +.TP +.BI \-n " nickname" +lets you override the default nick ($USER) +.TP +.BI \-f " realname" +lets you specify your real name associated with your nick +.SH DIRECTORIES +.TP +.B ~/irc +In this directory the irc tree will be created. In this directory you +will find a directory for your server (default: irc.freenode.net) in +which the FIFO and the output file will be stored. +If you join a channel a new directory with the name of the channel +will be created in the ~/irc/$servername/ directory. +.SH COMMANDS +.TP +.BI /a " []" +mark yourself as away +.TP +.BI /j " #channel/nickname []" +join a channel or open private conversation with user +.TP +.BI /l " [reason]" +leave a channel or query +.TP +.BI /n " nick" +change the nick name +.TP +.BI /q " [reason]" +quit ic +.TP +.BI /t " topic" +set the topic of a channel +.SH RAW COMMANDS +.LP +Everything which is not a command will be posted into the channel or to the server. +So if you need /who just write /WHO as described in RFC#1459 to the server in FIFO. +.SH SSL PROTOCOL SUPPORT +.LP +For TLS/SSL protocol support you can connect to a local tunnel, for example with stunnel or socat. +.SH CONTACT +.LP +Subscribe to the mailinglist and write to dev (at) suckless (dot) org for suggestions, fixes, etc. +.SH AUTHORS +ic engineers, see LICENSE file +.SH SEE ALSO +.BR echo (1), +.BR tail (1) +.SH BUGS +Please report them! diff --git a/src/cmd/ic/ic.c b/src/cmd/ic/ic.c new file mode 100644 index 0000000..7fc37d8 --- /dev/null +++ b/src/cmd/ic/ic.c @@ -0,0 +1,878 @@ +/* See LICENSE file for license details. */ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +size_t strlcpy(char *, const char *, size_t); + +#define IRC_CHANNEL_MAX 200 +#define IRC_MSG_MAX 512 /* guaranteed to be <= than PIPE_BUF */ +#define PING_TIMEOUT 300 + +enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST }; + +typedef struct Channel Channel; +struct Channel { + int fdin; + char name[IRC_CHANNEL_MAX]; /* channel name (normalized) */ + char inpath[PATH_MAX]; /* input path */ + char outpath[PATH_MAX]; /* output path */ + Channel *next; +}; + +static Channel * channel_add(const char *); +static Channel * channel_find(const char *); +static Channel * channel_join(const char *); +static void channel_leave(Channel *); +static Channel * channel_new(const char *); +static void channel_normalize_name(char *); +static void channel_normalize_path(char *); +static int channel_open(Channel *); +static void channel_print(Channel *, const char *); +static int channel_reopen(Channel *); +static void channel_rm(Channel *); + +static void create_dirtree(const char *); +static void create_filepath(char *, size_t, const char *, const char *, const char *); +static void ewritestr(int, const char *); +static void handle_channels_input(int, Channel *); +static void handle_server_output(int); +static int isnumeric(const char *); +static void loginkey(int, const char *); +static void loginuser(int, const char *, const char *); +static void proc_channels_input(int, Channel *, char *); +static void proc_channels_privmsg(int, Channel *, char *); +static void proc_server_cmd(int, char *); +static int read_line(int, char *, size_t); +static void run(int, const char *); +static void setup(void); +static void sighandler(int); +static int tcpopen(const char *, const char *); +static size_t tokenize(char **, size_t, char *, int); +static int udsopen(const char *); +static void usage(void); + +static int isrunning = 1; +static time_t last_response = 0; +static Channel *channels = nil; +static Channel *channelmaster = nil; +static char nick[32], _nick[arrlen(nick)]; /* active nickname at runtime */ +static char ircpath[PATH_MAX]; /* irc dir (-i) */ +static char msg[IRC_MSG_MAX]; /* message buf used for communication */ + +static +void +usage(void) +{ + fprintf(stderr, "usage: %s <-s host> [-i ] [-p ] " + "[-u ] [-n ] [-k ] " + "[-f ]\n", argv0); + exit(1); +} + +static +void +ewritestr(int fd, const char *s) +{ + size_t len, off = 0; + int w = -1; + + len = strlen(s); + for (off = 0; off < len; off += w) { + if ((w = write(fd, s + off, len - off)) == -1) + break; + off += w; + } + if (w == -1) { + fprintf(stderr, "%s: write: %s\n", argv0, strerror(errno)); + exit(1); + } +} + +/* creates directories bottom-up, if necessary */ +static +void +create_dirtree(const char *dir) +{ + char tmp[PATH_MAX], *p; + struct stat st; + size_t len; + + strlcpy(tmp, dir, sizeof(tmp)); + len = strlen(tmp); + if (len > 0 && tmp[len - 1] == '/') + tmp[len - 1] = '\0'; + + if ((stat(tmp, &st) != -1) && S_ISDIR(st.st_mode)) + return; /* dir exists */ + + for (p = tmp + 1; *p; p++) { + if (*p != '/') + continue; + *p = '\0'; + mkdir(tmp, S_IRWXU); + *p = '/'; + } + mkdir(tmp, S_IRWXU); +} + +static +void +channel_normalize_path(char *s) +{ + for (; *s; s++) { + if (isalpha((unsigned char)*s)) + *s = tolower((unsigned char)*s); + else if (!isdigit((unsigned char)*s) && !strchr(".#&+!-", *s)) + *s = '_'; + } +} + +static +void +channel_normalize_name(char *s) +{ + char *p; + + while (*s == '&' || *s == '#') + s++; + for (p = s; *s; s++) { + if (!strchr(" ,&#\x07", *s)) { + *p = *s; + p++; + } + } + *p = '\0'; +} + +static +void +create_filepath(char *filepath, size_t len, const char *path, + const char *channel, const char *suffix) +{ + int r; + + if (channel[0]) { + r = snprintf(filepath, len, "%s/%s", path, channel); + if (r < 0 || (size_t)r >= len) + goto error; + create_dirtree(filepath); + r = snprintf(filepath, len, "%s/%s/%s", path, channel, suffix); + if (r < 0 || (size_t)r >= len) + goto error; + } else { + r = snprintf(filepath, len, "%s/%s", path, suffix); + if (r < 0 || (size_t)r >= len) + goto error; + } + return; + +error: + fprintf(stderr, "%s: path to irc directory too long\n", argv0); + exit(1); +} + +static +int +channel_open(Channel *c) +{ + int fd; + struct stat st; + + /* make "in" fifo if it doesn't exist already. */ + if (lstat(c->inpath, &st) != -1) { + if (!(st.st_mode & S_IFIFO)) + return -1; + } else if (mkfifo(c->inpath, S_IRWXU)) { + return -1; + } + c->fdin = -1; + fd = open(c->inpath, O_RDONLY | O_NONBLOCK, 0); + if (fd == -1) + return -1; + c->fdin = fd; + + return 0; +} + +static +int +channel_reopen(Channel *c) +{ + if (c->fdin > 2) { + close(c->fdin); + c->fdin = -1; + } + return channel_open(c); +} + +static +Channel * +channel_new(const char *name) +{ + Channel *c; + char channelpath[PATH_MAX]; + + strlcpy(channelpath, name, sizeof(channelpath)); + channel_normalize_path(channelpath); + + if (!(c = calloc(1, sizeof(Channel)))) { + fprintf(stderr, "%s: calloc: %s\n", argv0, strerror(errno)); + exit(1); + } + + strlcpy(c->name, name, sizeof(c->name)); + channel_normalize_name(c->name); + + create_filepath(c->inpath, sizeof(c->inpath), ircpath, + channelpath, "in"); + create_filepath(c->outpath, sizeof(c->outpath), ircpath, + channelpath, "out"); + return c; +} + +static +Channel * +channel_find(const char *name) +{ + Channel *c; + char chan[IRC_CHANNEL_MAX]; + + strlcpy(chan, name, sizeof(chan)); + channel_normalize_name(chan); + for (c = channels; c; c = c->next) { + if (!strcmp(chan, c->name)) + return c; /* already handled */ + } + return nil; +} + +static +Channel * +channel_add(const char *name) +{ + Channel *c; + + c = channel_new(name); + if (channel_open(c) == -1) { + fprintf(stderr, "%s: cannot create channel: %s: %s\n", + argv0, name, strerror(errno)); + free(c); + return nil; + } + if (!channels) { + channels = c; + } else { + c->next = channels; + channels = c; + } + return c; +} + +static +Channel * +channel_join(const char *name) +{ + Channel *c; + + if (!(c = channel_find(name))) + c = channel_add(name); + return c; +} + +static +void +channel_rm(Channel *c) +{ + Channel *p; + + if (channels == c) { + channels = channels->next; + } else { + for (p = channels; p && p->next != c; p = p->next) + ; + if (p && p->next == c) + p->next = c->next; + } + free(c); +} + +static +void +channel_leave(Channel *c) +{ + if (c->fdin > 2) { + close(c->fdin); + c->fdin = -1; + } + /* remove "in" file on leaving the channel */ + unlink(c->inpath); + channel_rm(c); +} + +static +void +loginkey(int ircfd, const char *key) +{ + snprintf(msg, sizeof(msg), "PASS %s\r\n", key); + ewritestr(ircfd, msg); +} + +static +void +loginuser(int ircfd, const char *host, const char *fullname) +{ + snprintf(msg, sizeof(msg), "NICK %s\r\nUSER %s localhost %s :%s\r\n", + nick, nick, host, fullname); + puts(msg); + ewritestr(ircfd, msg); +} + +static +int +udsopen(const char *uds) +{ + struct sockaddr_un sun; + size_t len; + int fd; + + if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "%s: socket: %s\n", argv0, strerror(errno)); + exit(1); + } + + sun.sun_family = AF_UNIX; + if(strlcpy(sun.sun_path, uds, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { + fprintf(stderr, "%s: UNIX domain socket path truncation\n", argv0); + exit(1); + } + len = strlen(sun.sun_path) + 1 + sizeof(sun.sun_family); + if (connect(fd, (struct sockaddr *)&sun, len) == -1) { + fprintf(stderr, "%s: connect: %s\n", argv0, strerror(errno)); + exit(1); + } + return fd; +} + +static +int +tcpopen(const char *host, const char *service) +{ + struct addrinfo hints, *res = nil, *rp; + int fd = -1, e; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICSERV; /* avoid name lookup for port */ + hints.ai_socktype = SOCK_STREAM; + + if ((e = getaddrinfo(host, service, &hints, &res))) { + fprintf(stderr, "%s: getaddrinfo: %s\n", argv0, gai_strerror(e)); + exit(1); + } + + for (rp = res; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd == -1) + continue; + if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) { + close(fd); + fd = -1; + continue; + } + break; /* success */ + } + if (fd == -1) { + fprintf(stderr, "%s: could not connect to %s:%s: %s\n", + argv0, host, service, strerror(errno)); + exit(1); + } + + freeaddrinfo(res); + return fd; +} + +static +int +isnumeric(const char *s) +{ + errno = 0; + strtol(s, nil, 10); + return errno == 0; +} + +static +size_t +tokenize(char **result, size_t reslen, char *str, int delim) +{ + char *p = nil, *n = nil; + size_t i = 0; + + for (n = str; *n == ' '; n++) + ; + p = n; + while (*n != '\0') { + if (i >= reslen) + return 0; + if (i > TOK_CHAN - TOK_CMD && result[0] && isnumeric(result[0])) + delim = ':'; /* workaround non-RFC compliant messages */ + if (*n == delim) { + *n = '\0'; + result[i++] = p; + p = ++n; + } else { + n++; + } + } + /* add last entry */ + if (i < reslen && p < n && p && *p) + result[i++] = p; + return i; /* number of tokens */ +} + +static +void +channel_print(Channel *c, const char *buf) +{ + FILE *fp = nil; + time_t t = time(nil); + + if (!(fp = fopen(c->outpath, "a"))) + return; + fprintf(fp, "%lu %s\n", (unsigned long)t, buf); + fclose(fp); +} + +static +void +proc_channels_privmsg(int ircfd, Channel *c, char *buf) +{ + snprintf(msg, sizeof(msg), "<%s> %s", nick, buf); + channel_print(c, msg); + snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", c->name, buf); + ewritestr(ircfd, msg); +} + +static +void +proc_channels_input(int ircfd, Channel *c, char *buf) +{ + char *p = nil; + size_t buflen; + + if (buf[0] == '\0') + return; + if (buf[0] != '/') { + proc_channels_privmsg(ircfd, c, buf); + return; + } + + msg[0] = '\0'; + if ((buflen = strlen(buf)) < 2) + return; + if (buf[2] == ' ' || buf[2] == '\0') { + switch (buf[1]) { + case 'j': /* join */ + if (buflen < 3) + return; + if ((p = strchr(&buf[3], ' '))) /* password parameter */ + *p = '\0'; + if ((buf[3] == '#') || (buf[3] == '&') || (buf[3] == '+') || + (buf[3] == '!')) + { + /* password protected channel */ + if (p) + snprintf(msg, sizeof(msg), "JOIN %s %s\r\n", &buf[3], p + 1); + else + snprintf(msg, sizeof(msg), "JOIN %s\r\n", &buf[3]); + channel_join(&buf[3]); + } else if (p) { + if ((c = channel_join(&buf[3]))) + proc_channels_privmsg(ircfd, c, p + 1); + return; + } + break; + case 't': /* topic */ + if (buflen >= 3) + snprintf(msg, sizeof(msg), "TOPIC %s :%s\r\n", c->name, &buf[3]); + break; + case 'a': /* away */ + if (buflen >= 3) { + snprintf(msg, sizeof(msg), "-!- %s is away \"%s\"", nick, &buf[3]); + channel_print(c, msg); + } + if (buflen >= 3) + snprintf(msg, sizeof(msg), "AWAY :%s\r\n", &buf[3]); + else + snprintf(msg, sizeof(msg), "AWAY\r\n"); + break; + case 'n': /* change nick */ + if (buflen >= 3) { + strlcpy(_nick, &buf[3], sizeof(_nick)); + snprintf(msg, sizeof(msg), "NICK %s\r\n", &buf[3]); + } + break; + case 'l': /* leave */ + if (c == channelmaster) + return; + if (buflen >= 3) + snprintf(msg, sizeof(msg), "PART %s :%s\r\n", c->name, &buf[3]); + else + snprintf(msg, sizeof(msg), + "PART %s :leaving\r\n", c->name); + ewritestr(ircfd, msg); + channel_leave(c); + return; + break; + case 'q': /* quit */ + if (buflen >= 3) + snprintf(msg, sizeof(msg), "QUIT :%s\r\n", &buf[3]); + else + snprintf(msg, sizeof(msg), + "QUIT %s\r\n", "bye"); + ewritestr(ircfd, msg); + isrunning = 0; + return; + break; + default: /* raw IRC command */ + snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]); + break; + } + } else { + /* raw IRC command */ + snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]); + } + if (msg[0] != '\0') + ewritestr(ircfd, msg); +} + +static +void +proc_server_cmd(int fd, char *buf) +{ + Channel *c; + const char *channel; + char *argv[TOK_LAST], *cmd = nil, *p = nil; + unsigned int i; + + if (!buf || buf[0] == '\0') + return; + + /* clear tokens */ + for (i = 0; i < TOK_LAST; i++) + argv[i] = nil; + + /* check prefix */ + if (buf[0] == ':') { + if (!(p = strchr(buf, ' '))) + return; + *p = '\0'; + for (++p; *p == ' '; p++) + ; + cmd = p; + argv[TOK_NICKSRV] = &buf[1]; + if ((p = strchr(buf, '!'))) { + *p = '\0'; + argv[TOK_USER] = ++p; + } + } else { + cmd = buf; + } + + /* remove CRLFs */ + for (p = cmd; p && *p != '\0'; p++) { + if (*p == '\r' || *p == '\n') + *p = '\0'; + } + + if ((p = strchr(cmd, ':'))) { + *p = '\0'; + argv[TOK_TEXT] = ++p; + } + + tokenize(&argv[TOK_CMD], TOK_LAST - TOK_CMD, cmd, ' '); + + if (!argv[TOK_CMD] || !strcmp("PONG", argv[TOK_CMD])) { + return; + } else if (!strcmp("PING", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "PONG %s\r\n", argv[TOK_TEXT]); + ewritestr(fd, msg); + return; + } else if (!argv[TOK_NICKSRV] || !argv[TOK_USER]) { + /* server command */ + snprintf(msg, sizeof(msg), "%s%s", + argv[TOK_ARG] ? argv[TOK_ARG] : "", + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + channel_print(channelmaster, msg); + return; /* don't process further */ + } else if (!strcmp("ERROR", argv[TOK_CMD])) + snprintf(msg, sizeof(msg), "-!- error %s", + argv[TOK_TEXT] ? argv[TOK_TEXT] : "unknown"); + else if (!strcmp("JOIN", argv[TOK_CMD]) && (argv[TOK_CHAN] || argv[TOK_TEXT])) { + if (argv[TOK_TEXT]) + argv[TOK_CHAN] = argv[TOK_TEXT]; + snprintf(msg, sizeof(msg), "-!- %s(%s) has joined %s", + argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]); + } else if (!strcmp("PART", argv[TOK_CMD]) && argv[TOK_CHAN]) { + snprintf(msg, sizeof(msg), "-!- %s(%s) has left %s", + argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]); + /* if user itself leaves, don't write to channel (don't reopen channel). */ + if (!strcmp(argv[TOK_NICKSRV], nick)) + return; + } else if (!strcmp("MODE", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "-!- %s changed mode/%s -> %s %s", + argv[TOK_NICKSRV], + argv[TOK_CHAN] ? argv[TOK_CHAN] : "", + argv[TOK_ARG] ? argv[TOK_ARG] : "", + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else if (!strcmp("QUIT", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "-!- %s(%s) has quit \"%s\"", + argv[TOK_NICKSRV], argv[TOK_USER], + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else if (!strncmp("NICK", argv[TOK_CMD], 5) && argv[TOK_TEXT] && + !strcmp(_nick, argv[TOK_TEXT])) { + strlcpy(nick, _nick, sizeof(nick)); + snprintf(msg, sizeof(msg), "-!- changed nick to \"%s\"", nick); + channel_print(channelmaster, msg); + } else if (!strcmp("NICK", argv[TOK_CMD]) && argv[TOK_TEXT]) { + snprintf(msg, sizeof(msg), "-!- %s changed nick to %s", + argv[TOK_NICKSRV], argv[TOK_TEXT]); + } else if (!strcmp("TOPIC", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "-!- %s changed topic to \"%s\"", + argv[TOK_NICKSRV], + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else if (!strcmp("KICK", argv[TOK_CMD]) && argv[TOK_ARG]) { + snprintf(msg, sizeof(msg), "-!- %s kicked %s (\"%s\")", + argv[TOK_NICKSRV], argv[TOK_ARG], + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else if (!strcmp("NOTICE", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "-!- \"%s\"", + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else if (!strcmp("PRIVMSG", argv[TOK_CMD])) { + snprintf(msg, sizeof(msg), "<%s> %s", argv[TOK_NICKSRV], + argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); + } else { + return; /* can't read this message */ + } + if (argv[TOK_CHAN] && !strcmp(argv[TOK_CHAN], nick)) + channel = argv[TOK_NICKSRV]; + else + channel = argv[TOK_CHAN]; + + if (!channel || channel[0] == '\0') + c = channelmaster; + else + c = channel_join(channel); + if (c) + channel_print(c, msg); +} + +static +int +read_line(int fd, char *buf, size_t bufsiz) +{ + size_t i = 0; + char c = '\0'; + + do { + if (read(fd, &c, sizeof(char)) != sizeof(char)) + return -1; + buf[i++] = c; + } while (c != '\n' && i < bufsiz); + buf[i - 1] = '\0'; /* eliminates '\n' */ + return 0; +} + +static +void +handle_channels_input(int ircfd, Channel *c) +{ + char buf[IRC_MSG_MAX]; + + if(read_line(c->fdin, buf, sizeof(buf)) == -1) { + if(channel_reopen(c) == -1) + channel_rm(c); + return; + } + proc_channels_input(ircfd, c, buf); +} + +static +void +handle_server_output(int ircfd) +{ + char buf[IRC_MSG_MAX]; + + if (read_line(ircfd, buf, sizeof(buf)) == -1) { + fprintf(stderr, "%s: remote host closed connection: %s\n", + argv0, strerror(errno)); + exit(1); + } + fprintf(stdout, "%lu %s\n", (unsigned long)time(nil), buf); + fflush(stdout); + proc_server_cmd(ircfd, buf); +} + +static +void +sighandler(int sig) +{ + if (sig == SIGTERM || sig == SIGINT) + isrunning = 0; +} + +static +void +setup(void) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sighandler; + sigaction(SIGTERM, &sa, nil); + sigaction(SIGINT, &sa, nil); +} + +static +void +run(int ircfd, const char *host) +{ + Channel *c, *tmp; + fd_set rdset; + struct timeval tv; + char ping_msg[IRC_MSG_MAX]; + int r, maxfd; + + snprintf(ping_msg, sizeof(ping_msg), "PING %s\r\n", host); + while(isrunning) { + maxfd = ircfd; + FD_ZERO(&rdset); + FD_SET(ircfd, &rdset); + for (c = channels; c; c = c->next) { + if (c->fdin > maxfd) + maxfd = c->fdin; + FD_SET(c->fdin, &rdset); + } + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 120; + r = select(maxfd + 1, &rdset, 0, 0, &tv); + if(r < 0){ + if (errno == EINTR) + continue; + fprintf(stderr, "%s: select: %s\n", argv0, strerror(errno)); + exit(1); + }else if(r == 0){ + if (time(nil) - last_response >= PING_TIMEOUT) { + channel_print(channelmaster, "-!- ii shutting down: ping timeout"); + exit(2); /* status code 2 for timeout */ + } + ewritestr(ircfd, ping_msg); + continue; + } + if(FD_ISSET(ircfd, &rdset)) { + handle_server_output(ircfd); + last_response = time(nil); + } + for(c = channels; c; c = tmp) { + tmp = c->next; + if (FD_ISSET(c->fdin, &rdset)) + handle_channels_input(ircfd, c); + } + } +} + +int +main(int argc, char *argv[]) +{ + Channel *c, *tmp; + struct passwd *spw; + const char *key = nil, *fullname = nil, *host = ""; + const char *uds = nil, *service = "6667"; + char prefix[PATH_MAX]; + int ircfd, r; + + /* use nickname and home dir of user by default */ + if(!(spw = getpwuid(getuid()))) { + fprintf(stderr, "%s: getpwuid: %s\n", argv0, strerror(errno)); + exit(1); + } + strlcpy(nick, spw->pw_name, sizeof(nick)); + snprintf(prefix, sizeof(prefix), "%s/irc", spw->pw_dir); + + ARGBEGIN { + case 'f': + fullname = EARGF(usage()); + break; + case 'i': + strlcpy(prefix, EARGF(usage()), sizeof(prefix)); + break; + case 'k': + key = getenv(EARGF(usage())); + break; + case 'n': + strlcpy(nick, EARGF(usage()), sizeof(nick)); + break; + case 'p': + service = EARGF(usage()); + break; + case 's': + host = EARGF(usage()); + break; + case 'u': + uds = EARGF(usage()); + break; + default: + usage(); + break; + } ARGEND + + if(!*host) + usage(); + + if(uds) + ircfd = udsopen(uds); + else + ircfd = tcpopen(host, service); + +#ifdef __OpenBSD__ + /* OpenBSD pledge(2) support */ + if (pledge("stdio rpath wpath cpath dpath", nil) == -1) { + fprintf(stderr, "%s: pledge: %s\n", argv0, strerror(errno)); + exit(1); + } +#endif + + r = snprintf(ircpath, sizeof(ircpath), "%s/%s", prefix, host); + if (r < 0 || (size_t)r >= sizeof(ircpath)) { + fprintf(stderr, "%s: path to irc directory too long\n", argv0); + exit(1); + } + create_dirtree(ircpath); + + channelmaster = channel_add(""); /* master channel */ + if(key) + loginkey(ircfd, key); + loginuser(ircfd, host, fullname && *fullname ? fullname : nick); + setup(); + run(ircfd, host); + if(channelmaster) + channel_leave(channelmaster); + + for(c = channels; c; c = tmp) { + tmp = c->next; + channel_leave(c); + } + + return 0; +} diff --git a/src/cmd/ic/rules.mk b/src/cmd/ic/rules.mk new file mode 100644 index 0000000..d001c01 --- /dev/null +++ b/src/cmd/ic/rules.mk @@ -0,0 +1,14 @@ +include share/push.mk +# Iterate through subdirectory tree + +# Local sources +SRCS_$(d):=$(d)/strlcpy.c $(d)/ic.c +BINS_$(d):=$(d)/ic + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/ic/strlcpy.c b/src/cmd/ic/strlcpy.c new file mode 100644 index 0000000..5af7906 --- /dev/null +++ b/src/cmd/ic/strlcpy.c @@ -0,0 +1,32 @@ +/* Taken from OpenBSD */ +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if(n != 0) { + while(--n != 0) { + if((*d++ = *s++) == '\0') + break; + } + } + /* Not enough room in dst, add NUL and traverse rest of src */ + if(n == 0) { + if(siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while(*s++) + ; + } + return s - src - 1; /* count does not include NUL */ +} diff --git a/src/cmd/menu/LICENSE b/src/cmd/menu/LICENSE new file mode 100644 index 0000000..9762166 --- /dev/null +++ b/src/cmd/menu/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2019 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/cmd/menu/config.h b/src/cmd/menu/config.h new file mode 100644 index 0000000..9bfd5b3 --- /dev/null +++ b/src/cmd/menu/config.h @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ +#define VERSION "1.0" + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "consolas:size=16" +}; + +static const char *prompt = "cmds"; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#fbf1c7", "#504945" }, + [SchemeSel] = { "#504945", "#83a598" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * e.g. " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/src/cmd/menu/drw.c b/src/cmd/menu/drw.c new file mode 100644 index 0000000..162fe40 --- /dev/null +++ b/src/cmd/menu/drw.c @@ -0,0 +1,428 @@ +/* See LICENSE file for copyright and license details. */ +#include "menu.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = RuneErr; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = RuneErr; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + fatal("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + fatal("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + fatal("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/src/cmd/menu/drw.h b/src/cmd/menu/drw.h new file mode 100644 index 0000000..4c67419 --- /dev/null +++ b/src/cmd/menu/drw.h @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/src/cmd/menu/menu.c b/src/cmd/menu/menu.c new file mode 100644 index 0000000..e6e4bb2 --- /dev/null +++ b/src/cmd/menu/menu.c @@ -0,0 +1,765 @@ +#include "menu.h" + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw, passwd = 0; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = nil; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static +void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = nil; + *last = item; +} + +static +void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static +void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static +char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return nil; +} + +static +int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static +void +drawmenu(void) +{ + uint curpos; + struct item *item; + int x = 0, y = 0, w; + char *censort; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + if(passwd){ + censort = ecalloc(1, sizeof(text)); + memset(censort, '.', strlen(text)); + drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); + free(censort); + } + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static +void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, nil); + } + fatal("cannot grab focus"); +} + +static +void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, nil); + } + fatal("cannot grab keyboard"); +} + +static +void +match(void) +{ + static char **tokv = nil; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(nil, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + fatal("cannot realloc %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = nil; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static +void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static +size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static +void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static +void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(nil, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(nil, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(nil, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + movewordedge(-1); + goto draw; + case XK_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl(*buf)) + insert(buf, len); + break; + case XK_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(nil, nextrune(-1) - cursor); + break; + case XK_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + match(); + break; + } + +draw: + drawmenu(); +} + +static +void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static +void +readstdin(void) +{ + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + uint tmpmax = 0; + if(passwd){ + inputw = lines = 0; + return; + } + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + fatal("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + fatal("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, nil); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = nil; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static +void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static +void +setup(void) +{ + int x, y, i, j; + uint du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"menu", "menu"}; + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; + + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + XFree(info); + } else + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + fatal("could not get embedding window attributes: 0x%lx", + parentwin); + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = MIN(inputw, mw/3); + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, nil, nil, nil)) == nil) + fatal("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, nil); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static +void +usage(void) +{ + fputs("usage: menu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("menu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (!strcmp(argv[i], "-P")) { + passwd = 1; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(nil))) + fatal("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, nil, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + fatal("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, arrlen(fonts))) + fatal("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", nil) == -1) + fatal("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/src/cmd/menu/menu.h b/src/cmd/menu/menu.h new file mode 100644 index 0000000..f4345bb --- /dev/null +++ b/src/cmd/menu/menu.h @@ -0,0 +1,40 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "drw.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + + +/* enums */ +enum { + SchemeNorm, + SchemeSel, + SchemeOut, + SchemeLast +}; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +/* util.c */ +void fatal(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/src/cmd/menu/rules.mk b/src/cmd/menu/rules.mk new file mode 100644 index 0000000..f9b59aa --- /dev/null +++ b/src/cmd/menu/rules.mk @@ -0,0 +1,27 @@ +include share/push.mk +# Iterate through subdirectory tree + +# Local sources +SRCS_$(d):=\ + $(d)/menu.c\ + $(d)/drw.c\ + $(d)/util.c + +#outputs +BINS_$(d):=$(d)/menu + +include share/paths.mk + +# Local rules +include share/dynamic.mk + +$(BINS_$(d)): TCLIBS=\ + -lfontconfig -lXft -lXinerama -lX11 +$(BINS_$(d)): TCINCS=\ + `$(PKG) --cflags fontconfig`\ + `$(PKG) --cflags freetype2` + +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/menu/util.c b/src/cmd/menu/util.c new file mode 100644 index 0000000..14bfe1c --- /dev/null +++ b/src/cmd/menu/util.c @@ -0,0 +1,30 @@ +/* See LICENSE file for copyright and license details. */ +#include "menu.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + fatal("calloc:"); + return p; +} + +void +fatal(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} diff --git a/src/cmd/rc/code.c b/src/cmd/rc/code.c new file mode 100644 index 0000000..786f284 --- /dev/null +++ b/src/cmd/rc/code.c @@ -0,0 +1,277 @@ +#include "rc.h" +#include "parse.h" +#include "exec.h" + +// ----------------------------------------------------------------------- +// types + +struct Interpreter +{ + int i, cap; + Code *code; +}; + +Code *compiled = nil; +static struct Interpreter interpreter; +#define emiti(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].i = (x), interpreter.i++) +#define emitf(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].f = (x), interpreter.i++) +#define emits(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].s = (x), interpreter.i++) + +static +int +grow(void) +{ + interpreter.cap += 100; + interpreter.code = erealloc(interpreter.code, sizeof(*interpreter.code)*interpreter.cap); + memset(interpreter.code+interpreter.cap-100, 0, 100*sizeof(*interpreter.code)); + + return 0; +} + +static +void +storepc(int a) +{ + if(interpreter.i <= a || a < 0) + fatal("bad address %d in interpreter", a); + + interpreter.code[a].i = interpreter.i; +} + +static +void +walk(Tree *node) +{ + Tree *n; + int addr1, addr2; + + if(!node) + return; + + switch(node->type){ + default: + print(shell.err, "bad type %d in interpreter walk\n", node->type); + fatal("crashing\n"); + break; + + case '$': + emitf(Xmark); + walk(node->child[0]); + emitf(Xdollar); + break; + + case Tcount: + emitf(Xmark); + walk(node->child[0]); + emitf(Xcount); + break; + + case Tjoin: + emitf(Xmark); + walk(node->child[0]); + emitf(Xjoin); + break; + + case Tindex: + emitf(Xmark); + walk(node->child[1]); + emitf(Xmark); + walk(node->child[0]); + emitf(Xindex); + break; + + case ';': + walk(node->child[0]); + walk(node->child[1]); + break; + + case '^': + emitf(Xmark); + walk(node->child[1]); + emitf(Xmark); + walk(node->child[0]); + emitf(Xconcatenate); + break; + + case Tandand: + walk(node->child[0]); + emitf(Xtrue); + addr1 = emiti(0); + walk(node->child[1]); + storepc(addr1); + break; + + case Toror: + walk(node->child[0]); + emitf(Xfalse); + addr1 = emiti(0); + walk(node->child[1]); + storepc(addr1); + break; + + case Targs: + walk(node->child[1]); + walk(node->child[0]); + break; + + case Tparen: case Tblock: + walk(node->child[0]); + break; + + case Tbasic: + emitf(Xmark); + walk(node->child[0]); + emitf(Xbasic); + break; + + case Tbang: + walk(node->child[0]); + emitf(Xbang); + + case Tword: + emitf(Xword); + emits(strdup(node->str)); + break; + + case Twords: + walk(node->child[1]); + walk(node->child[0]); + break; + + case '=': + for(n=node; node && node->type == '='; node = node->child[2]) + ; + if(node){ + for(node=n; node->type=='='; node = node->child[2]){ + emitf(Xmark); + walk(node->child[1]); + emitf(Xmark); + walk(node->child[0]); + emitf(Xlocal); + } + walk(node); + for(node=n; node->type=='='; node = node->child[2]) + emitf(Xunlocal); + }else{ + for(node=n; node; node=node->child[2]){ + emitf(Xmark); + walk(node->child[1]); + emitf(Xmark); + walk(node->child[0]); + emitf(Xassign); + } + } + node = n; + break; + /* control structures */ + case Twhile: + addr1 = interpreter.i; // head of loop + walk(node->child[0]); + if(addr1 == interpreter.i) + fatal("TODO"); + emitf(Xtrue); + addr2 = emiti(0); // goto end of loop + + walk(node->child[1]); + emitf(Xgoto); + emiti(addr1); // goto top of loop + storepc(addr2); + break; + + case Tfor: + emitf(Xmark); + if(node->child[1]){ // for( x in X ) + walk(node->child[1]); + // emitf(Xglob) + }else{ // for(X) + fatal("TODO"); + } + emitf(Xmark); // null initial value for Xlocal + emitf(Xmark); + walk(node->child[0]); + emitf(Xlocal); + + addr1 = emitf(Xfor); + addr2 = emiti(0); + + walk(node->child[2]); + emitf(Xgoto); + emiti(addr1); + storepc(addr2); + emitf(Xunlocal); + break; + + /* forks */ + case '&': + emitf(Xasync); + addr1 = emiti(0); + walk(node->child[0]); + emitf(Xexit); + storepc(addr1); + break; + + case Tsubshell: + emitf(Xsubshell); + addr1 = emiti(0); + walk(node->child[0]); + emitf(Xexit); + storepc(addr1); + break; + + case Tpipe: + emitf(Xpipe); + + emiti(node->redir.fd[0]); + emiti(node->redir.fd[1]); + addr1 = emiti(0); + addr2 = emiti(0); + + walk(node->child[0]); + emitf(Xexit); + storepc(addr1); + + walk(node->child[1]); + emitf(Xreturn); + storepc(addr2); + + emitf(Xpipewait); + + break; + } +} + +// ----------------------------------------------------------------------- +// main exports + +void +freecode(Code *c) +{ + if(--c[0].i!=0) + return; + efree(c); +} + +Code * +copycode(Code *c) +{ + c[0].i++; + return c; +} + +int +compile(Tree *node) +{ + flush(shell.err); + + interpreter.i = 0; + interpreter.code = emalloc(100*sizeof(*interpreter.code)); + emiti(0); // reference count: no thread owns code yet + + walk(node); + + emitf(Xreturn); + emitf(nil); + + compiled = interpreter.code; + return 0; +} diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c new file mode 100644 index 0000000..5baaf1a --- /dev/null +++ b/src/cmd/rc/exec.c @@ -0,0 +1,1267 @@ +#include "rc.h" +#include "exec.h" + +#include + +int yyparse(void); + +struct Builtin{ + char *name; + void (*func)(void); +}; + +struct State { + int async; +}; + +static struct State state; + +// ----------------------------------------------------------------------- +// globals + +static Word nullpath = { .str="", .link=nil }; + +struct Builtin builtin[]={ + {"cd", xcd}, + {".", xdot}, + {"echo", xecho}, + {"exit", xexit}, + {"fg", xfg}, + {"jobs", xjob}, + 0, +}; + +// ----------------------------------------------------------------------- +// internal + +/* words and lists */ + +static +void +pushword(char *str) +{ + if(!runner->args) + fatal("attempt to push on empty argument stack\n"); + + runner->args->word = makeword(str, runner->args->word); +} + +static +void +popword(void) +{ + Word *w; + if(!runner->args) + fatal("tried to pop word on empty argument stack\n"); + + w = runner->args->word; + if(!w) + fatal("tried to pop word but nothing there\n"); + + runner->args->word = w->link; + efree(w->str); + efree(w); +} + +static +Word* +copywords(Word *a, Word *tail) +{ + Word *v = nil, **end; + + for(end=&v; a; a = a->link,end=&(*end)->link) + *end = makeword(a->str, nil); + *end = tail; + + return v; +} + +static +void +freewords(Word *w) +{ + Word *n; + while(w){ + efree(w->str); + n = w->link; + efree(w); + w = n; + } +} + +static +void +freelist(Word *w) +{ + Word *n; + while(w){ + n = w->link; + efree(w->str); + efree(w); + w = n; + } + +} + +static +void +pushlist(void) +{ + List *stack = emalloc(sizeof(*stack)); + + stack->word = nil; + stack->link = runner->args; + + runner->args = stack; +} + +static +void +poplist(void) +{ + List *stack = runner->args; + if(!stack) + fatal("attempted to pop an empty argument stack\n"); + + freelist(stack->word); + runner->args = stack->link; + efree(stack); +} + +/* system interop */ +static +Word* +path(char *w) +{ + Word *path; + + if(strncmp(w, "/", 1)==0 + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path = var("path")->val)==0) + path=&nullpath; + + return path; +} + +static inline +void +undoredirs(void) +{ + while(runner->redir.end != runner->redir.start) + Xpopredir(); +} + +static inline +int +exitsnext(void) +{ + Code *c = &runner->code.exe[runner->code.i]; + while(c->f == Xpopredir) + c++; + + return c->f == Xexit; +} + +static inline +void +defaultsignal(void) +{ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_DFL); + signal(SIGTTIN, SIG_DFL); + signal(SIGTTOU, SIG_DFL); + signal(SIGCHLD, SIG_DFL); +} + +static inline +void +setpid(Thread *job, int pid) +{ + job->pid = pid; + if(job->pgid <= 0){ + job->pgid = pid; + addjob(job); + } + + setpgid(pid, job->pgid); +} + +/* fork/execute helpers */ + +static inline +void +initchild(Thread *job, int fg) +{ + int pid = getpid(); + setpid(job, pid); + + if(job->flag.user){ + if(fg) + tcsetpgrp(0, job->pgid); + else + job->flag.user = 0; + defaultsignal(); + } + + clearwait(job); +} + +static inline +void +initparent(Thread *job, int pid, int fg) +{ + setpid(job, pid); + + if(job->flag.user){ + if(!fg){ + tcsetpgrp(0, job->pgid); + job->flag.user = 0; + } + } + addwait(job, pid); +} + +static +void +xx(void) +{ + popword(); // "exec" + if(!runner->args->word){ + Xerror("empty argument list"); + return; + } + + redirect(runner->redir.end); + execute(runner->args->word, path(runner->args->word->str)); + poplist(); +} + +static +int +xforkx(void) +{ + int n, pid; + + switch(pid=fork()){ + case -1: + Xerror("try again\n"); + return -1; + case 0: // child + initchild(runner, 1); + + pushword("exec"); + xx(); + + exit(2); // NOTE: unreachable: xx does not return + default: // parent + initparent(runner, pid, 0); + + return pid; + } +} + +/* redirections */ +void +pushredir(int type, int from, int to) +{ + Redir *r = emalloc(sizeof(*r)); + + r->type = type; + r->from = from; + r->to = to; + + r->link = runner->redir.end, runner->redir.end = r; +} + +/* byte code */ +static +void +run(Code *c, int pc, Var *local, int inherit) +{ + Thread *new = emalloc(sizeof(*new)); + + new->code.i = pc; + new->code.exe = copycode(c); + + new->cmd.path = nil; + new->cmd.io = nil; + + new->args = nil; + new->local = local; + + new->flag.eof = 0; + if(runner){ + new->pid = runner->pid; + new->flag.user = runner->flag.user; + new->redir.end = new->redir.start = runner->redir.end; + }else{ + new->pid = shell.pid; + new->flag.user = shell.interactive; + new->redir.end = new->redir.start = nil; + } + + new->wait.status = 0; + new->wait.len = 0; + new->wait.cap = 0; + new->wait.on = nil; + + new->status = 0; + if(inherit) + new->pgid = runner->pgid; + else + new->pgid = -1; + + new->line = 0; + new->caller = runner; + new->link = nil; + + runner = new; +} + +// ----------------------------------------------------------------------- +// exported builtins + +// XXX: find a better place for these +Word* +makeword(char *str, Word *link) +{ + Word *w = emalloc(sizeof(*w)); + + w->str = strdup(str); + w->link = link; + + return w; +} + +void +freeword(Word *word) +{ + Word *n; + + while(word){ + efree(word->str); + n = word->link; + efree(word); + word = n; + } +} + +int +count(Word *w) +{ + int n; + for(n = 0; w; n++) + w = w->link; + return n; +} + +// ----------------------------------------------------------------------- +// builtins + +void +xecho(void) +{ + int fd; + Word *arg; + char *b, *s, buf[128]; + + fd = mapfd(1); + b = buf; + + popword(); // echo + + // TODO: controllable flags here + arg = runner->args->word; +printword: + s = arg->str; + while(*s){ + *b++ = *s++; + if(b == arrend(buf)-2) // always have 2 bytes available + write(fd, buf, arrlen(buf)-2), b = buf; + } + + arg = arg->link; + if(arg){ + *b++ = ' '; + goto printword; + }else{ + *b++ = '\n'; + *b++ = 0; + /* fallthrough */ + } + write(fd, buf, b-buf); + + poplist(); +} + +void +xexit(void) +{ + Word *arg; + + popword(); // exit + arg = runner->args->word; + switch(count(arg)){ + default: + print(shell.err, "invalid number of arguments to exit, exiting anyways\n"); + case 0: + Xexit(); + } + /* unreachable */ +} + +void +xcd(void) +{ + Word *arg; + Word *cdpath; + char dir[512]; + + popword(); // cd + + arg = runner->args->word; + switch(count(arg)){ + default: + print(shell.err, "usage: cd [directory]\n"); + break; + case 0: + arg = var("home")->val; + if(count(arg) >= 1){ + if(chdir(arg->str) < 0) + print(shell.err, "failed cd: %s\n", strerror(errno)); + }else{ + print(shell.err, "ambiguous cd: $home empty\n"); + } + break; + + case 1: + // TODO: add cdpath + cdpath = &nullpath; + for(; cdpath; cdpath = cdpath->link){ + strcpy(dir, cdpath->str); + if(dir[0]) + strcat(dir,"/"); + strcat(dir, arg->str); + if(chdir(dir) < 0){ + print(shell.err, "failed cd %s: %s\n", dir, strerror(errno)); + } + break; + } + break; + } + + poplist(); +} + +static Code dotcmd[14] = +{ + [0] = {.i = 0}, + [1] = {.f = Xmark}, + [2] = {.f = Xword}, + [3] = {.s = "0"}, + [4] = {.f = Xlocal}, + [5] = {.f = Xmark}, + [6] = {.f = Xword}, + [7] = {.s = "*"}, + [8] = {.f = Xlocal}, + [9] = {.f = Xreadcmd}, + [10] = {.f = Xunlocal}, + [11] = {.f = Xunlocal}, + [12] = {.f = Xreturn}, +}; + +void +xdot(void) +{ + Word *p; + List *argv; + char *base; + int fd, iflag = 0; + Thread *old; + char file[512]; + + popword(); // "." +#if 0 + if(proc->args->word && strcmp(proc->args->word->str, "-i")==0){ + iflag = 1; + popword(); + } +#endif + /* get input file */ + if(!runner->args->word){ + Xerror("usage: . [-i] file [arg ...]\n"); + return; + } + + base = strdup(runner->args->word->str); + popword(); + for(fd=-1, p=path(base); p; p = p->link){ + strcpy(file, p->str); + + if(file[0]) + strcat(file, "/"); + strcat(file, base); + + if((fd = open(file, 0))>=0) + break; + } + + if(fd<0){ + print(shell.err, "failed open: %s: ", base); + return; + } + /* set up for a new command loop */ + old = runner; // store pointer to old code + run(dotcmd, 1, nil, 0); + + /* operations on new command stack */ + pushredir(Rclose, fd, 0); + runner->cmd.path = base; + runner->cmd.io = openfd(fd); + + /* push $* value */ + pushlist(); + runner->args->word = old->args->word; + + /* free caller's copy of $* */ + argv = old->args; + old->args = argv->link; + efree(argv); + + /* push $0 value */ + pushlist(); + pushword(base); + //ndot++; +} + +void +xjob(void) +{ + int i; + Thread *job; + + for(i=0, job = shell.jobs; job; job = job->link, i++) + report(job,i); + + poplist(); +} + +void +xfg(void) +{ + int i; + Thread *job, *old; + + popword(); // fg + + /* get input job id */ + if(!runner->args->word){ + print(shell.err, "usage: fg [pid|\%num]\n"); + poplist(); + return; + } + + i = atoi(runner->args->word->str); + popword(); // [pid|num] + + for(job=shell.jobs; i > 0; job=job->link, --i) + ; + + poplist(); // this goes here? + + wakeup(job); + job->caller = runner, runner = job; // XXX: can this leave zombies? + foreground(job, 1); +} + +void +xboot(int argc, char *argv[]) +{ + int i; + Code bootstrap[32]; + char num[12]; + + i = 0; + bootstrap[i++].i = 1; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xassign; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xdollar; + bootstrap[i++].f = Xword; + bootstrap[i++].s = "/dev/stdin"; + bootstrap[i++].f = Xword; + bootstrap[i++].s="."; + bootstrap[i++].f = Xbasic; + bootstrap[i++].f = Xexit; + bootstrap[i].i = 0; + + run(bootstrap, 1, nil, 0); + runner->pid = runner->pgid = shell.pid; + pushlist(); // prime bootstrap argv + + argv0 = strdup(argv[0]); + for(i = argc-1; i > 0; --i) + pushword(argv[i]); + + /* main interpreter loop */ + for(;;){ + runner->code.i++; + (*runner->code.exe[runner->code.i-1].f)(); + } +} + +// ----------------------------------------------------------------------- +// exported interpreter bytecode + +void +Xmark(void) +{ + pushlist(); +} + +void +Xword(void) +{ + pushword(runner->code.exe[runner->code.i++].s); +} + +void +Xtrue(void) +{ + if(!runner->status){ + assert(runner->wait.status == Pdone); + runner->code.i++; + deljob(runner); + runner->pgid = -1; + }else + runner->code.i = runner->code.exe[runner->code.i].i; +} + +void +Xfalse(void) +{ + if(runner->status){ + assert(runner->wait.status == Pdone); + runner->code.i++; + deljob(runner); + runner->pgid = -1; + } else + runner->code.i = runner->code.exe[runner->code.i].i; +} + +void +Xgoto(void) +{ + runner->code.i = runner->code.exe[runner->code.i].i; +} + +void +Xfor(void) +{ + if(!runner->args->word){ + poplist(); + runner->code.i = runner->code.exe[runner->code.i].i; + }else{ + freelist(runner->local->val); + + runner->local->val = runner->args->word; + runner->local->new = 1; + runner->args->word = runner->args->word->link; + + runner->local->val->link = nil; + runner->code.i++; + } + +} + +static +Word* +catlist(Word *l, Word *r, Word *tail) +{ + Word *w; + char *buf; + + if(l->link || r->link) + tail = catlist( (!l->link)?l:l->link, (!r->link)?r:r->link, tail); + + buf = emalloc(strlen(l->str)+strlen(r->str)+1); + strcpy(buf, l->str); + strcat(buf, r->str); + + w = makeword(buf, tail); + efree(buf); + + return w; +} + +void +Xconcatenate(void) +{ + int rn, ln; + Word *l = runner->args->word; + Word *r = runner->args->link->word; + Word *w = runner->args->link->link->word; + + ln = count(l), rn = count(r); + if(ln != 0 || rn != 0) { + if(ln == 0 || rn == 0){ + Xerror("null list in concatenation\n"); + return; + } + if(ln != 1 && rn != 1 && ln != rn) { + Xerror("mismatched list lengths in concatenation\n"); + return; + } + w = catlist(l, r, w); + } + + poplist(); + poplist(); + runner->args->word = w; +} + +void +Xdollar(void) +{ + int n; + char *s, *t; + Word *a, *star; + + if(count(runner->args->word)!=1){ + Xerror("variable name not singleton!\n"); + return; + } + s = runner->args->word->str; + // deglob(s); + n = 0; + + for(t = s;'0'<=*t && *t<='9';t++) + n = n*10+*t-'0'; + + a = runner->args->link->word; + + if(n==0 || *t) + a = copywords(var(s)->val, a); + else{ + star = var("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) + star = star->link; + + a = makeword(star->str, a); + } + } + + poplist(); + runner->args->word = a; +} + +static +Word* +cpwords(Word *array, Word *tail, int n) +{ + Word *cp, **end; + + cp = nil, end = &cp; + while(n-- > 0){ + *end = makeword(array->str, nil); + end = &(*end)->link; + array = array->link; + } + *end = tail; + + return cp; +} + + +static +Word* +getindex(Word *array, int len, Word *index, Word *tail) +{ + char *s; + int n, m; + if(!index) + return tail; + + tail = getindex(array, len, index->link, tail); + + s = index->str; + //deglob(s) + + m = 0, n = 0; + while('0' <= *s && *s <= '9') + n = 10*n + (*s++ - '0'); + if(*s == '-'){ + if(*++s == 0) + m = len - n; + else{ + while('0' <= *s && *s <= '9') + m = 10*m + (*s++ - '0'); + m -= n; + } + } + + if(n<1 || n > len || m < 0) + return tail; + if(n+m > len) + m = len-n; + while(--n > 0) + array = array->link; + return cpwords(array, tail, m+1); +} + +void +Xindex(void) +{ + char *s; + Word *val, *ret; + + if(count(runner->args->word) != 1){ + Xerror("variable name not a singleton"); + return; + } + s = runner->args->word->str; + //deglob(s) + val = var(s)->val; + poplist(); + + ret = runner->args->link->word; // pointer to next stack frame + ret = getindex(val, count(val), runner->args->word, ret); + poplist(); + + // push result back on stack + runner->args->word = ret; +} + +void +Xjoin(void) +{ + int n; + char *s; + Word *arg, *elt; + + if(count(runner->args->word) != 1){ + Xerror("variable name is not singleton\n"); + return; + } + + s = runner->args->word->str; + // deglob(s) + + arg = var(s)->val; + poplist(); + + n = count(arg); + if(n==0){ + pushword(""); + return; + } + + for(elt = arg; elt; elt=elt->link) + n += strlen(elt->str); + + s = emalloc(n); + if(arg){ + strcpy(s, arg->str); + for(elt = arg->link; elt; elt = elt->link){ + strcat(s, " "); + strcat(s, elt->str); + } + }else + s[0] = 0; + + pushword(s); + efree(s); +} + +void +Xassign(void) +{ + Var *v; + + if(count(runner->args->word)!=1){ + Xerror("variable name not singleton!\n"); + return; + } + //deglob(runq->argv->words->word); + v = var(runner->args->word->str); + poplist(); + + //globlist(); + freewords(v->val); + v->val = runner->args->word; + v->new = 1; + if(v->update) + v->update(v); + + runner->args->word = nil; + poplist(); +} + +void +Xreadcmd(void) +{ + Thread *root; + Word *prompt; + + flush(shell.err); + root = runner; + + resetprompt(); + + if(yyparse()){ + // resource cleanup? + if(runner->flag.eof) + Xreturn(); + else + --root->code.i; + }else{ + --root->code.i; /* re-execute Xreadcmd after codebuf runs */ + run(compiled, 1, root->local, 0); + } + + killzombies(); + freeparsetree(); +} + +void +Xlocal(void) +{ + if(count(runner->args->word)!=1){ + Xerror("variable name must be singleton\n"); + return; + } + //deglob(shell->args->word->str); + + runner->local = makevar(strdup(runner->args->word->str), runner->local); + runner->local->val = copywords(runner->args->link->word, nil); + runner->local->new = 1; + + poplist(); + poplist(); +} + +void +Xunlocal(void) +{ + Var *v = runner->local, *hide; + if(!v) + fatal("Xunlocal: no locals!\n", 0); + + runner->local = v->link; + hide = var(v->name); + hide->new = 1; + + efree(v->name); + freewords(v->val); + efree(v); +} + +void +Xasync(void) +{ + int pid; + /* + int null = open("/dev/null", 0); + if(!null){ + Xerror("can not open /dev/null\n"); + return; + } + */ + + switch(pid=fork()){ + case -1: + // close(null); + Xerror("fork failed: try again"); + break; + + case 0: // child in background + initchild(runner,0); + /* pushredir(Ropen, null, 0); */ + + run(runner->code.exe, runner->code.i+1, runner->local, 0); + runner->caller = nil; + runner->flag.user = 0; + break; + + default: // parent in foreground + initparent(runner,pid,1); + // close(null); + + runner->code.i = runner->code.exe[runner->code.i].i; /* jump to end of async command */ + /* don't wait: continue running */ + } +} + +void +Xsubshell(void) +{ + int pid, user; + + user = runner->flag.user; + switch(pid=fork()){ + case -1: + Xerror("fork failed: try again"); + break; + + case 0: // child + initchild(runner, 1); + run(runner->code.exe, runner->code.i+1, runner->local, 1); + runner->caller = nil; + break; + + default: // parent + initparent(runner, pid, 0); // relinquish control + waitfor(runner, pid); // wait until child finishes + if(user){ + tcsetpgrp(0, shell.pid); + runner->flag.user = 1; // take control + } + + runner->code.i = runner->code.exe[runner->code.i].i; // jump to end of subshell command and continue execution + } +} + +void +Xpipewait(void) +{ + foreground(runner, 0); +} + +void +Xpipe(void) +{ + Thread *orig; + int pc, pid, lfd, rfd, pfd[2]; + + orig = runner; + pc = orig->code.i; + lfd = orig->code.exe[pc++].i; + rfd = orig->code.exe[pc++].i; + + if(pipe(pfd)<0){ + Xerror("can't get pipe\n"); + return; + } + + switch(pid=fork()){ + case -1: + Xerror("try again"); + break; + case 0: // child + initchild(runner,1); + + /* child 0 (writer) forked process */ + run(runner->code.exe, pc+2, runner->local, 1); + runner->caller = nil; + + close(pfd[0]); + pushredir(Ropen, pfd[1], lfd); + break; + + default: // parent + initparent(runner,pid,0); + + /* child 1 (reader) subprocess*/ + run(runner->code.exe, runner->code.exe[pc].i, runner->local, 1); + + close(pfd[1]); + pushredir(Ropen, pfd[0], rfd); + + orig->code.i = orig->code.exe[pc+1].i; + break; + } +} + +void +Xbasic(void) +{ + Var *v; + Word *arg; + int pid, status; + struct Builtin *b; + + arg = runner->args->word; + if(!arg){ + Xerror("empty argument list\n"); + return; + } + + v = var(arg->str); + if(v->func){ + return; + } + + // see if it matches a builtin + for(b = builtin; b->name; b++){ + if(strcmp(b->name, arg->str)==0){ + b->func(); + return; + } + } + + /* if we are here then it's an external command */ + if(exitsnext()){ // if we exit immediately, no need to fork + pushword("exec"); + xx(); + Xexit(); + } + + // run the external command + if((pid = xforkx()) < 0) { + Xerror("try again"); + return; + } + + poplist(); + foreground(runner, 0); // waits for child +} + +void +Xcount(void) +{ + Word *arg; + char *str, num[12]; + + if(count(runner->args->word) != 1){ + Xerror("variable name not a singleton\n"); + return; + } + + str = runner->args->word->str; + arg = var(str)->val; + poplist(); + + itoa(num, count(arg)); + pushword(num); +} + +void +Xflat(void) +{ + int len; + char *str; + Word *arg, *a; + + if(count(runner->args->word)!=1){ + Xerror("variable name is not a singleton\n"); + return; + } + + str = runner->args->word->str; + arg = var(str)->val; + poplist(); + + len = count(arg); + if(!len){ + pushword(""); + return; + } + + for(a=arg; a; a=a->link) + len += strlen(a->str); + + str = emalloc(len); + if(arg){ + strcpy(str, arg->str); + for(a = arg->link; a; a = a->link){ + strcat(str," "); + strcat(str,a->str); + } + }else + str[0] = 0; + + pushword(str); + efree(str); +} + +void +Xbang(void) +{ + if(runner->status) + runner->status = 0; + else + runner->status = 1; +} + +void +Xpopredir(void) +{ + Redir *r = runner->redir.end; + if(!r) + fatal("attempted to pop a nil redir\n"); + + runner->redir.end = runner->redir.end->link; + if(r->type==Ropen) + close(r->from); + + efree(r); +} + +void +Xreturn(void) +{ + Thread *curr = runner; + + switch(curr->wait.status){ + /* + * If our job is still running or suspended we must: + * 1. move program one step back to rerun Xreturn upon recall + * 2. return to our calling thread + * 3. don't free! + */ + case Prun: + report(curr, 0); + curr->flag.user = 0; + case Pstop: + curr->code.i--; + runner = curr->caller; + curr->caller = nil; // detach job + return; + /* + * If our job has finished: + * 1. remove from our list + * 2. continue to clean up its memory + */ + case Pdone: + deljob(curr); + /* fallthrough */ + default: + ; + } + + undoredirs(); + + while(curr->args) + poplist(); + freecode(curr->code.exe); + efree(curr->wait.on); + + runner = curr->caller; + efree(curr); + if(!runner) + exit(0); +} + +void +Xexit(void) +{ + exit(runner->status); +} + +void +Xerror(char *msg) +{ + print(shell.err, "rc: %s", msg); + flush(shell.err); + while(!runner->flag.user) + Xreturn(); +} + diff --git a/src/cmd/rc/exec.h b/src/cmd/rc/exec.h new file mode 100644 index 0000000..a3a6ae9 --- /dev/null +++ b/src/cmd/rc/exec.h @@ -0,0 +1,47 @@ +#pragma once + +/* + * opcode routines + * arguments on stack (...) + * arguments in line [...] + * code in line with jump around {...} + */ + +void Xmark(void); // Xmark marks stack location for word +void Xindex(void); // Xindex +void Xlocal(void); // Xlocal(name,val) create local variable, assign value +void Xunlocal(void); // Xunlocal delete local variable +void Xdollar(void); // Xdollar(name) get value of name +void Xtrue(void); // Xtrue{...} execute {} if true +void Xfalse(void); // Xfalse{...} execute {} if false +void Xgoto(void); // Xgoto[addr] goto address +void Xfor(void); // Xfor(var, list){... Xreturn} +void Xreadcmd(void); // +void Xassign(void); +void Xbang(void); +void Xasync(void); +void Xbasic(void); // Xbasic(args) run command and wait for result +void Xsubshell(void); +void Xword(void); +void Xjoin(void); +void Xconcatenate(void); +void Xcount(void); +void Xflat(void); +void Xpipe(void); +void Xpipewait(void); +void Xpopredir(void); + +void Xreturn(void); +void Xexit(void); + +void Xerror(char*); + +/* builtin commands */ +void xcd(void); +void xdot(void); +void xecho(void); +void xexit(void); +void xfg(void); +void xjob(void); + +void xboot(int argc, char *argv[]); diff --git a/src/cmd/rc/input.c b/src/cmd/rc/input.c new file mode 100644 index 0000000..cc2383d --- /dev/null +++ b/src/cmd/rc/input.c @@ -0,0 +1,1679 @@ +#include "rc.h" + +#include +#include + +/* don't change order of these without modifying matrix */ +enum +{ + NonPrintable, + Alnum, + Punctuation, + Space +}; + +static int ascii[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 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, 2, 2, 2, 2, 1, + 2, 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, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +struct Mode +{ + ushort raw : 1; + ushort multiline : 1; + ushort mask : 1; + ushort defer : 1; + struct { + ushort on : 1; + ushort insert : 1; + } vi ; +}; + +/* + * the structure represents the state during line editing. + * we pass this state to functions implementing specific editing functionalities + */ +struct TerminalState +{ + int ifd; /* terminal stdin file descriptor. */ + int ofd; /* terminal stdout file descriptor. */ + + struct{ + char *s; /* raw UTF-8 bytes */ + int len; /* number of bytes in prompt */ + int size; /* number of (printed) runes in prompt */ + } prompt; + + struct{ + intptr cap; /* capacity of edit buffer */ + intptr len; /* current number of bytes stored */ + intptr pos; /* position within edit buffer */ + char *buf; + } edit; /* edit buffer */ + + struct{ + intptr cap; /* number of columns in terminal */ + intptr len; /* current edited line length (in runes) */ + intptr pos; /* current cursor position (in runes) */ + intptr old; /* previous refresh cursor position (in runes) */ + } cursor; + + struct{ + intptr cap; + intptr len; + char *buf; + } yank; /* yank buffer */ + + intptr maxrows; /* maximum num of rows used so far (multiline mode) */ + intptr history; /* index of history we are currently editing */ +}; + +/* + * line history (circular buffer) + */ +struct History +{ + char **bot, **top, *entry[1024]; +}; + +/* globals */ +static struct Mode mode; +static struct History history; +static struct termios originalterm; + +enum +{ + KeyNil = 0, /* nil */ + KeyCtrlA = 1, /* Ctrl+a */ + KeyCtrlB = 2, /* Ctrl-b */ + KeyCtrlC = 3, /* Ctrl-c */ + KeyCtrlD = 4, /* Ctrl-d */ + KeyCtrlE = 5, /* Ctrl-e */ + KeyCtrlF = 6, /* Ctrl-f */ + KeyCtrlH = 8, /* Ctrl-h */ + KeyTab = 9, /* Tab */ + KeyCtrlK = 11, /* Ctrl+k */ + KeyCtrlL = 12, /* Ctrl+l */ + KeyEnter = 13, /* Enter */ + KeyCtrlN = 14, /* Ctrl-n */ + KeyCtrlP = 16, /* Ctrl-p */ + KeyCtrlT = 20, /* Ctrl-t */ + KeyCtrlU = 21, /* Ctrl+u */ + KeyCtrlW = 23, /* Ctrl+w */ + KeyEsc = 27, /* Escape */ + KeyBackspace = 127 /* Backspace */ +}; + +static void doatexit(void); + +/* vi operations */ +typedef struct +{ + intptr buffer; + intptr cursor; +} Position; + +typedef Position (*Noun)(struct TerminalState*, int); +typedef void (*Verb)(struct TerminalState*, Position); + +static +int +runetype(rune r) +{ + if(r<128) + return ascii[r]; + if(utf8·isspace(r)) + return Space; + if(utf8·isdigit(r) || utf8·isalpha(r)) + return Alnum; + if(utf8·ispunct(r)) + return Punctuation; + + return NonPrintable; +} + +static +void +normalcursor(int fd) +{ + write(fd,"\e[2 q",5); +} + +static +void +insertcursor(int fd) +{ + write(fd,"\e[6 q",5); +} + +/* raw mode: 1960 magic shit. */ +static +int +enterraw(int fd) +{ + struct termios raw; + + if(!shell.interactive) + goto fatal; + + if(!mode.defer){ + atexit(doatexit); + mode.defer = 1; + } + if(tcgetattr(fd,&originalterm) == -1) + goto fatal; + + raw = originalterm; /* modify the original mode */ + + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if(tcsetattr(fd,TCSAFLUSH,&raw) < 0) + goto fatal; + + mode.raw = 1; + return 1; + +fatal: + errno = ENOTTY; + return 0; +} + +static +void +exitraw(int fd) +{ + /* don't even check the return value as it's too late. */ + if(mode.raw && tcsetattr(fd,TCSAFLUSH,&originalterm) != -1) + mode.raw = 0; +} + +/* use the esc [6n escape sequence to query the horizontal cursor position + * and return it. on error -1 is returned, on success the position of the + * cursor. */ +static +int +cursorposition(int ifd, int ofd) +{ + char buf[32]; + int cols, rows; + unsigned int i = 0; + + /* Report cursor location */ + if(write(ofd, "\x1b[6n", 4) != 4) + return -1; + + /* Read the response: ESC [ rows ; cols R */ + while(i < sizeof(buf)-1) { + if(read(ifd,buf+i,1) != 1) + break; + if(buf[i] == 'R') + break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if(buf[0] != KeyEsc || buf[1] != '[') + return -1; + if(sscanf(buf+2,"%d;%d",&rows,&cols) != 2) + return -1; + + return cols; +} + +/* try to get the number of columns in the current terminal, or assume 80 if it fails. */ +static +int +columns(int ifd, int ofd) +{ + struct winsize ws; + + if(ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0){ + /* ioctl() failed. Try to query the terminal itself. */ + int start, cols; + + /* Get the initial position so we can restore it later. */ + start = cursorposition(ifd,ofd); + if(start == -1) + goto failed; + + /* Go to right margin and get position. */ + if(write(ofd,"\x1b[999C",6) != 6) + goto failed; + cols = cursorposition(ifd,ofd); + if(cols == -1) + goto failed; + + /* Restore position. */ + if(cols > start){ + char esc[32]; + snprintf(esc,32,"\x1b[%dD",cols-start); + if(write(ofd,esc,strlen(esc)) == -1) + ; + } + return cols; + }else + return ws.ws_col; + +failed: + return 80; +} + +static +void +clear(void) +{ + if(write(1,"\x1b[H\x1b[2J",7) <= 0) + ; +} + +/* beep: used for completion when there is nothing to complete or when all + * the choices were already shown. */ +static +void +beep(void) +{ + fprintf(stderr, "\x7"); + fflush(stderr); +} + +// ----------------------------------------------------------------------- +// command history + +void +inithistory(void) +{ + history.bot = history.top = history.entry; +} + +int +addhistory(char *line) +{ + char *copy; + + copy = strdup(line); + if(!copy) + return 0; + + *history.top++ = copy; + if(history.top == arrend(history.entry)) + history.top = history.entry; + + if(history.top == history.bot){ + efree(history.bot); + history.bot++; + } + + return 1; +} + +static +void +pophistory(void) +{ + if(--history.top < history.entry) + history.top = arrend(history.entry)-1; + efree(*history.top); +} + +static void refreshline(struct TerminalState *); + +static +char ** +currenthistory(struct TerminalState *term, intptr *size) +{ + char **entry; + intptr len, head; + + if(history.top > history.bot){ + len = history.top - history.bot; + entry = history.top - term->history - 1; + }else if(history.top < history.bot){ + len = (arrend(history.entry) - history.bot) + (history.top - history.entry); + if((head=history.top - history.entry) < term->history) + entry = arrend(history.entry) - head; + else + entry = history.top - term->history - 1; + }else + return nil; + + *size = len; + return entry; +} + +static +void +usehistory(struct TerminalState *term, int d) +{ + rune r; + intptr w, len; + char *b, *e, **entry; + + if(!(entry = currenthistory(term, &len))) + return; + + efree(*entry); + *entry = strdup(term->edit.buf); + + term->history += d; + if(term->history < 0){ + term->history = 0; + return; + }else if(term->history >= len){ + term->history = len - 1; + return; + } + entry = currenthistory(term, &len); + + strncpy(term->edit.buf, *entry, term->edit.cap); + term->edit.buf[term->edit.cap-1] = 0; + + /* update cursor/buffer positions */ + term->edit.len = term->edit.pos = strlen(term->edit.buf); + for(w=0, b=term->edit.buf, e=term->edit.buf+term->edit.len; b < e; ){ + b += utf8·decode(b, &r); + w += utf8·runewidth(r); + } + term->cursor.len = term->cursor.pos = w; + + refreshline(term); +} + +// ----------------------------------------------------------------------- +// line editing + +/* + * we define a very simple "append buffer" structure, that is an heap + * allocated string where we can append to. this is useful in order to + * write all the escape sequences in a buffer and flush them to the standard + * output in a single call, to avoid flickering effects. + */ + +struct Buffer +{ + int len; + char *b; +}; + +static +void +initbuffer(struct Buffer *ab) +{ + ab->b = nil; + ab->len = 0; +} + +static +void +append(struct Buffer *ab, const char *s, int len) +{ + char *new = realloc(ab->b,ab->len+len); + + if (new == nil) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; +} + +static +void +freebuffer(struct Buffer *ab) +{ + free(ab->b); +} + +/* single line low level line refresh. + * + * rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static +void +refreshsingleline(struct TerminalState *term) +{ + char esc[64]; + struct Buffer ab; + + int n, w; + rune r; + int fd = term->ofd; + intptr off = term->prompt.size; + char *buf = term->edit.buf; + intptr len = term->edit.len; + intptr pos = term->cursor.pos; + intptr col = term->cursor.len; + + while((off+pos) >= term->cursor.cap){ + n = utf8·decode(buf, &r); + w = utf8·runewidth(r); + + buf+=n, len-=n; + pos-=w, col-=w; + } + + assert(buf <= term->edit.buf + len); + + while(off+col > term->cursor.cap){ + n = utf8·decodeprev(buf+len-1, &r); + w = utf8·runewidth(r); + + len-=n, col-=w; + } + assert(len >= 0); + + initbuffer(&ab); // TODO: do we need so much malloc pressure? + + /* move cursor to left edge */ + snprintf(esc,64,"\r"); + append(&ab,"\r",1); + + /* write the prompt and the current buffer content */ + append(&ab, term->prompt.s, term->prompt.len); + + if(mode.mask == 1) + while(len--) + append(&ab,"*",1); + else + append(&ab,buf,len); + + snprintf(esc,64,"\x1b[0K"); // erase to right + append(&ab,esc,strlen(esc)); + + snprintf(esc,64,"\r\x1b[%dC", (int)(off+pos)); // move cursor to original position + append(&ab,esc,strlen(esc)); + + if(write(fd,ab.b,ab.len) == -1) /* can't recover from write error. */ + ; + + freebuffer(&ab); +} + +/* multi line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. */ +static +void +refreshmultilines(struct TerminalState *term) +{ +#if 0 + char esc[64]; + int plen = term->plen; + int rows = (plen+term->len+term->cols-1)/term->cols; /* rows used by current buf. */ + int rpos = (plen+term->oldpos+term->cols)/term->cols; /* cursor relative row. */ + int rpos2; /* rpos after refresh. */ + int col; /* colum position, zero-based. */ + int i; + int old_rows = term->maxrows; + int fd = term->ofd, j; + struct Buffer ab; + + /* Update maxrows if needed. */ + if(rows > (int)term->maxrows) + term->maxrows = rows; + + /* First step: clear all the lines used before. To do so start by + * going to the last row. */ + initbuffer(&ab); + if(old_rows-rpos > 0){ + snprintf(esc,64,"\x1b[%dB", old_rows-rpos); + append(&ab,esc,strlen(esc)); + } + + /* Now for every row clear it, go up. */ + for(j = 0; j < old_rows-1; j++){ + snprintf(esc,64,"\r\x1b[0K\x1b[1A"); + append(&ab,esc,strlen(esc)); + } + + /* clean the top line. */ + snprintf(esc,64,"\r\x1b[0K"); + append(&ab,esc,strlen(esc)); + + /* Write the prompt and the current buffer content */ + append(&ab,term->prompt,strlen(term->prompt)); + if(mode.mask == 1){ + for(i = 0; i < term->len; i++) append(&ab,"*",1); + }else + append(&ab,term->buf,term->len); + + /* If we are at the very end of the screen with our prompt, we need to + * emit a newline and move the prompt to the first column. */ + if(term->pos && term->pos == term->len && (term->pos+plen) % term->cols == 0) { + append(&ab,"\n",1); + snprintf(esc,64,"\r"); + append(&ab,esc,strlen(esc)); + rows++; + if(rows > (int)term->maxrows) + term->maxrows = rows; + } + + /* Move cursor to right position. */ + rpos2 = (plen+term->pos+term->cols)/term->cols; /* current cursor relative row. */ + + /* Go up till we reach the expected positon. */ + if(rows-rpos2 > 0){ + snprintf(esc,64,"\x1b[%dA", rows-rpos2); + append(&ab,esc,strlen(esc)); + } + + /* Set column. */ + col = (plen+(int)term->pos) % (int)term->cols; + if(col) + snprintf(esc,64,"\r\x1b[%dC", col); + else + snprintf(esc,64,"\r"); + append(&ab,esc,strlen(esc)); + + term->oldpos = term->pos; + + if(write(fd,ab.b,ab.len) == -1) /* Can't recover from write error. */ + ; + + freebuffer(&ab); +#endif +} + +/* Calls the two low level functions refreshSingleLine() or + * refreshMultiLine() according to the selected mode. */ +static +void +refreshline(struct TerminalState *term) +{ + if(mode.multiline) + refreshmultilines(term); + else + refreshsingleline(term); +} + +/* insert the rune 'c' at cursor current position. + * on error writing to the terminal -1 is returned, otherwise 0. */ +int +insertrune(struct TerminalState *term, int n, char *c) +{ + int w; + rune r; + + utf8·decode(c, &r); + w = utf8·runewidth(r); + + if(term->edit.len + n <= term->edit.cap){ + if(term->edit.pos == term->edit.len){ + memcpy(term->edit.buf+term->edit.pos, c, n); + + term->edit.pos += n, term->edit.len += n; + term->cursor.pos += w, term->cursor.len += w; + + term->edit.buf[term->edit.len] = '\0'; + + if(!mode.multiline && ((term->prompt.size+term->cursor.pos+n) <= term->cursor.cap)){ + if(mode.mask){ + c = "*"; + n = 1; + } + if(write(term->ofd, c, n) == -1) + return 0; + } + refreshline(term); + }else{ + memmove(term->edit.buf+term->edit.pos+n, term->edit.buf+term->edit.pos, term->edit.len-term->edit.pos); + memcpy(term->edit.buf+term->edit.pos, c, n); + + term->edit.pos += n, term->edit.len += n; + term->cursor.pos += w, term->cursor.len += w; + + term->edit.buf[term->edit.len] = '\0'; + refreshline(term); + } + } + + return 1; +} + +int +insertbytes(struct TerminalState *term, int len, char *buf) +{ + int nr; + if(term->edit.len + len > term->edit.cap){ + len = term->edit.cap - term->edit.len; + buf[len] = 0; + } + nr = utf8·len(buf); + + if(term->edit.pos == term->cursor.len){ + memcpy(term->edit.buf+term->edit.len, buf, len); + + term->edit.pos += len, term->edit.len += len; + term->cursor.pos += nr, term->cursor.len += nr; + + // XXX: transfer the modeline here? + term->edit.buf[term->edit.len] = '\0'; + refreshline(term); + }else{ + memmove(term->edit.buf+term->edit.pos+len,term->edit.buf+term->edit.pos,term->edit.len-term->edit.pos); + memcpy(term->edit.buf+term->edit.pos, buf, len); + + term->edit.pos += len, term->edit.len += len; + term->cursor.pos += nr, term->cursor.len += nr; + + term->edit.buf[term->edit.len] = '\0'; + refreshline(term); + } + + return 1; +} + +// ----------------------------------------------------------------------- +// vi functionality + +/* modes */ + +static +void +normalmode(int fd) +{ + mode.vi.insert = 0; + normalcursor(fd); +} + +static +void +insertmode(int fd) +{ + mode.vi.insert = 1; + insertcursor(fd); +} + +/* actions */ + +static +void +move(struct TerminalState *term, Position to) +{ + if(to.buffer != term->edit.pos){ + term->edit.pos = to.buffer; + term->cursor.pos = to.cursor; + refreshline(term); + } +} + +static +void +yank(struct TerminalState *term, Position to) +{ + intptr len, off; + + if(to.buffer == term->edit.pos) + return; // noop + + if(to.buffer > term->edit.pos){ + len = to.buffer - term->edit.pos; + off = term->edit.pos; + }else{ + len = term->edit.pos - to.buffer; + off = to.buffer; + } + + if(term->yank.cap < len+1){ + efree(term->yank.buf); + term->yank.cap = len+1; + term->yank.buf = emalloc(len+1); + } + term->yank.len = len; + memcpy(term->yank.buf, term->edit.buf+off, len); + term->yank.buf[len] = 0; +} + +static +void +delete(struct TerminalState *term, Position to) +{ + intptr diff; + + // delete characters in front of us (exclusive) + if(to.buffer > term->edit.pos){ + diff = to.buffer - term->edit.pos; + memmove(term->edit.buf+term->edit.pos, term->edit.buf+to.buffer, term->edit.len-to.buffer+1); + term->edit.len -= diff; + + diff = to.cursor - term->cursor.pos; + goto refresh; + } + + // delete characters behind us + if(to.buffer < term->edit.pos){ + diff = term->edit.pos - to.buffer; + memmove(term->edit.buf+to.buffer, term->edit.buf+term->edit.pos, term->edit.len-term->edit.pos+1); + term->edit.pos = to.buffer; + term->edit.len -= diff; + + diff = term->cursor.pos - to.cursor; + term->cursor.pos = to.cursor; + goto refresh; + } + // do nothing + return; + +refresh: + term->cursor.len -= diff; + refreshline(term); +} +/* movements */ + +#define CURRENT(term) (Position){ .buffer=(term)->edit.pos, .cursor=(term)->cursor.pos }; + +// move cursor to the left n boxes +static +Position +left(struct TerminalState *term, int n) +{ + rune r; + int w, d; + Position pos = CURRENT(term); + char *buf = term->edit.buf + term->edit.pos; + + d = 0; + while(n > 0 && buf > term->edit.buf){ + buf -= utf8·decodeprev(buf-1, &r); + + w = utf8·runewidth(r); + n -= w; + d += w; + } + + pos.cursor = MAX(pos.cursor-d, 0); + pos.buffer = MAX(buf-term->edit.buf, 0); + return pos; +} + +// move cursor to the right n boxes +static +Position +right(struct TerminalState *term, int n) +{ + rune r; + int w, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + char *end = term->edit.buf + term->edit.len; + + d = 0; + while(n > 0 && buf < end){ + buf += utf8·decode(buf, &r); + + w = utf8·runewidth(r); + n -= w; + d += w; + } + + pos.cursor = MIN(pos.cursor+d, term->cursor.len); + pos.buffer = MIN(buf-term->edit.buf, term->edit.len); + return pos; +} + +static +Position +prevword(struct TerminalState *term, int n) +{ + rune r; + int c, w, b, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + + d = 0; + while(n-- > 0 && buf > term->edit.buf){ + eatspace: + b = utf8·decodeprev(buf-1, &r); + w = utf8·runewidth(r); + if((c=runetype(r)) == Space){ + buf -= b; + d += w; + + if(buf <= term->edit.buf) + break; + + goto eatspace; + } + + eatword: + if(runetype(r) == c){ + buf -= b; + d += w; + + if(buf <= term->edit.buf) + break; + + b = utf8·decodeprev(buf-1, &r); + w = utf8·runewidth(r); + + goto eatword; + } + } + + pos.cursor = MAX(pos.cursor-d, 0); + pos.buffer = MAX(buf-term->edit.buf, 0); + return pos; +} + +static +Position +nextword(struct TerminalState *term, int n) +{ + rune r; + int c, b, w, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + char *end = term->edit.buf + term->edit.len; + + d = 0; + while(n-- > 0 && buf < end){ + b = utf8·decode(buf, &r); + w = utf8·runewidth(r); + c = runetype(r); + eatword: + if(runetype(r) == c){ + buf += b; + d += w; + + if(buf >= end) + break; + + b = utf8·decode(buf, &r); + w = utf8·runewidth(r); + goto eatword; + } + eatspace: + while((c=runetype(r)) == Space){ + buf += b; + d += w; + + if(buf >= end) + break; + + b = utf8·decode(buf, &r); + w = utf8·runewidth(r); + goto eatspace; + } + } + + pos.cursor = MIN(pos.cursor+d, term->cursor.len); + pos.buffer = MIN(buf-term->edit.buf, term->edit.len); + return pos; +} + + +static +Position +prevWord(struct TerminalState *term, int n) +{ + rune r; + int c, w, b, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + + d = 0; + while(n-- > 0 && buf > term->edit.buf){ + eatspace: + b = utf8·decodeprev(buf-1, &r); + w = utf8·runewidth(r); + if((c=runetype(r)) == Space){ + buf -= b; + d += w; + + if(buf <= term->edit.buf) + break; + + goto eatspace; + } + + eatword: + if((c=runetype(r)) != Space){ + buf -= b; + d += w; + + if(buf <= term->edit.buf) + break; + + b = utf8·decodeprev(buf-1, &r); + w = utf8·runewidth(r); + + goto eatword; + } + } + + pos.cursor = MAX(pos.cursor-d, 0); + pos.buffer = MAX(buf-term->edit.buf, 0); + return pos; +} + +static +Position +nextWord(struct TerminalState *term, int n) +{ + rune r; + int b, w, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + char *end = term->edit.buf + term->edit.len; + + d = 0; + while(n-- > 0 && buf < end){ + eatword: + b = utf8·decode(buf, &r); + w = utf8·runewidth(r); + if(runetype(r) != Space){ + buf += b; + d += w; + + if(buf > end) + break; + + goto eatword; + } + + eatspace: + if(runetype(r) == Space){ + buf += b; + d += w; + + if(buf > end) + break; + + b = utf8·decode(buf, &r); + w = utf8·runewidth(r); + + goto eatspace; + } + } + + pos.cursor = MIN(pos.cursor+d, term->cursor.len); + pos.buffer = MIN(buf-term->edit.buf, term->edit.len); + return pos; +} + +static +Position +nextend(struct TerminalState *term, int n) +{ + rune r; + int c, b, w, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + char *end = term->edit.buf + term->edit.len; + + d = 0; + while(n-- > 0 && buf+1 < end){ + eatspace: + b = utf8·decode(buf+1, &r); + w = utf8·runewidth(r); + while((c=runetype(r)) == Space){ + buf += b; + d += w; + + if(buf+1 >= end) + break; + + goto eatspace; + } + eatword: + if(runetype(r) == c){ + buf += b; + d += w; + + if(buf+1 >= end) + break; + + b = utf8·decode(buf+1, &r); + w = utf8·runewidth(r); + goto eatword; + } + } + + pos.cursor = MIN(pos.cursor+d, term->cursor.len); + pos.buffer = MIN(buf-term->edit.buf, term->edit.len); + return pos; +} + +static +Position +nextEnd(struct TerminalState *term, int n) +{ + rune r; + int b, w, d; + Position pos = CURRENT(term); + + char *buf = term->edit.buf + term->edit.pos; + char *end = term->edit.buf + term->edit.len; + + d = 0; + while(n-- > 0 && buf+1 < end){ + eatspace: + b = utf8·decode(buf+1, &r); + w = utf8·runewidth(r); + if(runetype(r) == Space){ + buf += b; + d += w; + + if(buf+1 > end) + break; + + goto eatspace; + } + + eatword: + if(runetype(r) != Space){ + buf += b; + d += w; + + if(buf+1 > end) + break; + + b = utf8·decode(buf+1, &r); + w = utf8·runewidth(r); + + goto eatword; + } + } + + pos.cursor = MIN(pos.cursor+d, term->cursor.len); + pos.buffer = MIN(buf-term->edit.buf, term->edit.len); + return pos; +} + +#define HOME(term) (Position){0} +#define END(term) (Position){(term)->edit.len, (term)->cursor.len} + +static +int +vi(struct TerminalState *term, char c) +{ + int n = 1; + Verb verb = move; + +action: + switch(c){ + /* # of repeats */ + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + n = 0; + while('0' <= c && c <= '9'){ + n = 10*n + (c-'0'); + if(read(term->ifd, &c, 1)<1) + return -1; + } + goto action; + + /* composable actions */ + case 'l': verb(term, right(term, n)); break; + case 'h': verb(term, left(term, n)); break; + case '0': verb(term, HOME(term)); break; + case '$': verb(term, END(term)); break; + case 'b': verb(term, prevword(term,n)); break; + case 'B': verb(term, prevWord(term,n)); break; + case 'w': verb(term, nextword(term,n)); break; + case 'W': verb(term, nextWord(term,n)); break; + case 'e': verb(term, nextend(term,n)); break; + case 'E': verb(term, nextEnd(term,n)); break; + + /* verb switches */ + case 'd': // delete + verb = delete; + if(read(term->ifd, &c, 1)<1) + return -1; + /* special cases */ + switch(c){ + case 'd': + move(term, HOME(term)); + delete(term, END(term)); + return 0; + default: + goto action; + } + case 'y': // yank + verb = yank; + if(read(term->ifd, &c, 1)<1) + return -1; + /* special cases */ + switch(c){ + case 'y': + if(term->yank.cap < term->edit.len+1){ + efree(term->yank.buf); + term->yank.len = term->edit.len; + term->yank.cap = term->edit.len+1; + term->yank.buf = emalloc(term->yank.cap); + } + memcpy(term->yank.buf, term->edit.buf, term->edit.len+1); + break; + default: + goto action; + } + break; + + case 'p': // put + insertbytes(term, term->yank.len, term->yank.buf); + refreshline(term); + return 0; + + /* special cases + * sadly I don't know a better way than to have these checks for move + * the vi language doesn't fully compose + */ + case 'i': insertmode: + if(verb != move) goto unrecognized; + insertmode(term->ofd); + break; + + case 'I': + if(verb != move) goto unrecognized; + move(term, HOME(term)); + goto insertmode; + + case 'a': + if(verb != move) goto unrecognized; + if(term->edit.pos < term->edit.len){ + term->edit.pos++; + refreshline(term); + } + goto insertmode; + + case 'A': + if(verb != move) goto unrecognized; + move(term, END(term)); + goto insertmode; + + case 'x': + if(verb != move) goto unrecognized; + delete(term, right(term, 1)); + break; + + case 'X': + if(verb != move) goto unrecognized; + delete(term, left(term, 1)); + break; + + case 'r': + if(verb != move) goto unrecognized; + if(read(term->ifd, &c, 1)<1) + return -1; + if(c < ' ') + break; + term->edit.buf[term->edit.pos] = c; + refreshline(term); + break; + + // TODO: replace mode? + + case 'c': + if(verb != move) goto unrecognized; + insertmode(term->ofd); + verb = delete; + if(read(term->ifd, &c, 1)<1) + return -1; + goto action; + + case 'C': + if(verb != move) goto unrecognized; + insertmode(term->ofd); + goto deleteln; + + case 'D': + if(verb != move) goto unrecognized; + deleteln: + term->edit.len = term->edit.pos; + term->edit.buf[term->edit.pos] = 0; + refreshline(term); + break; + + default: unrecognized: + beep(); + break; + } + + return 0; +} +#undef END + +#define END(term) (Position){(term).edit.len, (term).cursor.len} + +static +int +size(char *s) +{ + rune c; + int n, len = 0;; + while((c=*s)){ + if(c == '\033'){ + n = 1; + esccode: + c = s[n]; + if(!c) // we hit end of string in the middle of parsing an escape code! + return len; + if(c == 'm'){ + s += n + 1; + continue; // outer loop + } + n++; + goto esccode; + } + n = utf8·decode(s, &c); + s += n; + len += utf8·runewidth(c); + } + return len; +} + +/* this function is the core of the line editing capability of linenoise. + * it expects 'fd' to be already in "raw mode" so that every key pressed + * will be returned asap to read(). + * + * the resulting string is put into 'buf' when the user type enter, or + * when ctrl+d is typed. + * + * the function returns the length of the current buffer. */ +static +int +interact(int ifd, int ofd, char *buf, intptr len, char *prompt) +{ + int n, aux; + char esc[3]; + char c[UTFmax+1] = { 0 }; + rune r; + + struct TerminalState term; + /* + * populate the state that we pass to functions implementing + * specific editing functionalities + */ + term.ifd = ifd; + term.ofd = ofd; + + term.edit.buf = buf; + term.edit.cap = len; + term.edit.len = 0; + term.edit.pos = 0; + + term.prompt.s = prompt; + term.prompt.len = strlen(prompt); + term.prompt.size = size(prompt); + + term.cursor.pos = 0; + term.cursor.len = 0; + term.cursor.cap = columns(ifd, ofd); + + term.maxrows = 0; + term.history = 0; + + term.yank.buf = nil; + term.yank.cap = term.yank.len = 0; + + /* buffer starts empty. */ + term.edit.buf[0] = '\0'; + term.edit.cap--; /* make sure there is always space for the nulterm */ + + /* push current (empty) command onto history stack */ + addhistory(""); + + if(write(term.ofd,prompt,term.prompt.len) == -1) + return -1; + + for(;;){ + n = read(term.ifd,c,1); + if(n <= 0) + goto finish; + + /* partition input by rune */ + if(utf8·onebyte(c[0])){ + r = c[0]; + }else if(utf8·twobyte(c[0])){ + n = read(term.ifd,c+1,1); + if(n < 1 || (n=utf8·decode(c, &r)) != 2) + goto finish; + }else if(utf8·threebyte(c[0])){ + n = read(term.ifd,c+1,2); + if(n < 2 || (n=utf8·decode(c, &r)) != 3) + goto finish; + }else if(utf8·fourbyte(c[0])){ + n = read(term.ifd,c+1,3); + if(n < 3 || (n=utf8·decode(c, &r)) != 4) + goto finish; + }else + goto finish; + + switch(r){ + case KeyEnter: + pophistory(); + if(mode.multiline) + move(&term, END(term)); + goto finish; + + case KeyCtrlC: + errno = EAGAIN; + return -1; + + case KeyBackspace: + case KeyCtrlH: + delete(&term, left(&term, 1)); + break; + + case KeyCtrlD: + if(term.edit.len > 0) + delete(&term, right(&term, 1)); + break; + + case KeyCtrlT: + if(term.edit.pos > 0 && term.edit.pos < term.edit.len){ + aux = buf[term.edit.pos-1]; + + buf[term.edit.pos-1] = buf[term.edit.pos]; + buf[term.edit.pos] = aux; + + if(term.edit.pos != term.edit.len-1) + term.edit.pos++; + + refreshline(&term); + } + break; + + case KeyCtrlB: + move(&term, left(&term, 1)); + break; + + case KeyCtrlF: /* ctrl-f */ + move(&term, right(&term, 1)); + break; + + case KeyCtrlP: /* ctrl-p */ + usehistory(&term, +1); + break; + + case KeyCtrlN: /* ctrl-n */ + usehistory(&term, -1); + break; + + case KeyEsc: /* escape sequence */ + /* + * try to read two bytes representing the escape sequence. + * if we read less than 2 and we are in vi mode, interpret as command + * + * NOTE: we could do a timed read here + */ + switch(read(term.ifd,esc,2)){ + case 0: + if(mode.vi.on){ + if(mode.vi.insert){ + normalmode(term.ofd); + if(term.edit.pos > 0){ + --term.edit.pos; + refreshline(&term); + } + continue; + } + } + case 1: + if(mode.vi.on){ + if(mode.vi.insert){ + normalmode(term.ofd); + if(vi(&term,esc[0]) < 0){ + term.edit.len = -1; + goto finish; + } + continue; + } + } + default: // 2 + ; + } + + /* ESC [ sequences. */ + if(esc[0] == '['){ + if(0 <= esc[1] && esc[1] <= '9'){ + /* extended escape, read additional byte. */ + if(read(term.ifd,esc+2,1) == -1) + break; + + if(esc[2] == '~'){ + switch(esc[1]){ + case '3': /* delete key. */ + delete(&term, left(&term,1)); + break; + } + } + }else{ + switch(esc[1]) { + case 'A': /* up */ + usehistory(&term, +1); + break; + case 'B': /* down */ + usehistory(&term, -1); + break; + case 'C': /* right */ + move(&term, right(&term, 1)); + break; + case 'D': /* left */ + move(&term, left(&term, 1)); + break; + case 'H': /* home */ + move(&term, HOME(term)); + break; + case 'F': /* end*/ + move(&term, END(term)); + break; + } + } + } + /* ESC O sequences. */ + else if(esc[0] == 'O'){ + switch(esc[1]) { + case 'H': /* home */ + move(&term, HOME(term)); + break; + case 'F': /* end*/ + move(&term, END(term)); + break; + } + } + break; + + default: + if(mode.vi.on && !mode.vi.insert && n == 1){ + if(vi(&term,c[0]) < 0){ + term.edit.len = -1; + goto finish; + } + }else if(!insertrune(&term,n,c)){ + term.edit.len = -1; + goto finish; + } + + break; + + case KeyCtrlU: /* Ctrl+u, delete the whole line. */ + buf[0] = '\0'; + term.edit.pos = term.edit.len = 0; + term.cursor.pos = term.cursor.len = 0; + refreshline(&term); + break; + + case KeyCtrlK: /* Ctrl+k, delete from current to end of line. */ + buf[term.edit.pos] = '\0'; + term.edit.len = term.edit.pos; + term.cursor.len = term.cursor.pos; + refreshline(&term); + break; + + case KeyCtrlA: /* Ctrl+a, go to the start of the line */ + move(&term, HOME(term)); + break; + + case KeyCtrlE: /* ctrl+e, go to the end of the line */ + move(&term, END(term)); + break; + + case KeyCtrlL: /* ctrl+term, clear screen */ + clear(); + refreshline(&term); + break; + + case KeyCtrlW: /* ctrl+w, delete previous word */ + delete(&term, prevword(&term,1)); + break; + } + } +finish: + efree(term.yank.buf); + return term.edit.len; +} + +/* + * this special mode is used by linenoise in order to print scan codes + * on screen for debugging / development purposes. It is implemented + * by the linenoise_example program using the --keycodes option. + */ +void +printkeycode(void) +{ + int n; + char c, quit[4]; + + printf("entering debugging mode. printing key codes.\n" + "press keys to see scan codes. type 'quit' at any time to exit.\n"); + + if(!enterraw(0)) + return; + + memset(quit,' ',4); + + for(;;){ + n = read(0,&c,1); + if(n <= 0) + continue; + memmove(quit,quit+1,sizeof(quit)-1); // shift string to left + quit[arrlen(quit)-1] = c; /* Insert current char on the right. */ + + if(memcmp(quit,"quit",sizeof(quit)) == 0) + break; + + printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); + printf("\r"); /* go to left edge manually, we are in raw mode. */ + fflush(stdout); + } + exitraw(0); +} + +/* + * this function calls the line editing function edit() using the stdin set in raw mode + */ +static +int +raw(char *buf, intptr len, char *prompt) +{ + int n; + + if(!len){ + errno = EINVAL; + return -1; + } + + // XXX: should we not hardcode stdin and stdout fd? + if(!enterraw(0)) return -1; + n = interact(0, 1, buf, len, prompt); + exitraw(0); + + return n; +} + +/* + * called when readline() is called with the standard + * input file descriptor not attached to a TTY. For example when the + * program is called in pipe or with a file redirected to its standard input + * in this case, we want to be able to return the line regardless of its length + */ +static +int +notty(void) +{ + int c; + + for(;;){ + c = fgetc(stdin); + put(&runner->cmd.io, c); + } +} + +void +enablevi(void) +{ + mode.vi.on = 1; + insertmode(1); +} + +/* + * The high level function that is the main API. + * This function checks if the terminal has basic capabilities and later + * either calls the line editing function or uses dummy fgets() so that + * you will be able to type something even in the most desperate of the + * conditions. + */ +int +readline(char *prompt) +{ + int n; + + // reset the command buffer + runner->cmd.io->e = runner->cmd.io->b = runner->cmd.io->buf; + + if(!shell.interactive) + return notty(); + + if((n = raw(runner->cmd.io->e, runner->cmd.io->cap-1, prompt)) == -1) + return 0; + runner->cmd.io->e += n; + + /* insert a newline character at the end */ + put(&runner->cmd.io, '\n'); + + return 1; +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static +void +doatexit(void) +{ + exitraw(0); + normalcursor(1); +} diff --git a/src/cmd/rc/io.c b/src/cmd/rc/io.c new file mode 100644 index 0000000..dc81c2e --- /dev/null +++ b/src/cmd/rc/io.c @@ -0,0 +1,437 @@ +#include "rc.h" +#include "parse.h" + +#define CAP0 512 + +Io* +openfd(int fd) +{ + Io *io = emalloc(sizeof(*io) + CAP0); + + io->fd = fd; + io->cap = CAP0; + io->b = io->e = io->buf; + io->s = nil; + + return io; +} + +Io* +openstr(void) +{ + char *s; + Io *io = emalloc(sizeof(*io) + CAP0); + + io->fd = -1; + io->cap = CAP0; + io->b = io->s = emalloc(101); + io->e = io->b+100; + + for(s = io->b; s<=io->e; s++) + *s=0; + + return io; +} + +#if 0 +/* + * open a corebuffer to read. EOF occurs after reading len characters from buf + */ + +Io* +opencore(char *s, int len) +{ + Io *io = emalloc(sizeof(*io)); + char *buf = emalloc(len); + io->fd = -1 /*open("/dev/null", 0)*/; + io->b = io->s = buf; + io->e = buf+len; + memcpy(buf, s, len); + + return io; +} +#endif + +void +iorewind(Io *io) +{ + if(io->fd==-1) + io->b = io->s; + else{ + io->b = io->e = io->buf; + lseek(io->fd, 0L, 0); + } +} + +void +terminate(Io *io) +{ + if(io->fd>=0) + close(io->fd); + if(io->s) + efree(io->s); + + efree((char *)io); +} + +static +int +refill(Io *io) +{ + int n; + + if(io->fd==-1 || (n = read(io->fd, io->buf, io->cap))<=0) + return EOF; + + io->b = io->buf; + io->e = io->buf+n; + + return *io->b++&0xff; +} + + +void +flush(Io *io) +{ + int n; + char *s; + + if(io->s){ + n = io->e-io->s; + io->s = realloc(io->s, n+101); + if(io->s==0) + panicf("Can't realloc %d bytes in flush!", n+101); + io->b = io->s+n; + io->e = io->b+100; + for(s = io->b;s<=io->e;s++) *s='\0'; + }else{ + n = io->b-io->buf; + if(n && write(io->fd, io->buf, n) < 0) + write(3, "write error\n", 12); + io->b = io->buf; + io->e = io->buf + io->cap; + } +} + + +static +void +printchar(Io *io, int c) +{ + if(io->b==io->e) + flush(io); + + *io->b++=c; +} + +void +printquote(Io *io, char *s) +{ + printchar(io, '\''); + for(;*s;s++) + if(*s=='\'') + print(io, "''"); + else printchar(io, *s); + printchar(io, '\''); +} + +void +printstr(Io *io, char *s) +{ + if(s==0) + s="(null)"; + while(*s) printchar(io, *s++); +} + +void +printword(Io *io, char *s) +{ + char *t; + + for(t = s;*t;t++) + if(!iswordchar(*t)) + break; + + if(t==s || *t) + printquote(io, s); + else + printstr(io, s); +} + +void +printptr(Io *io, void *v) +{ + int n; + uintptr p; + + p = (uintptr)v; + if(sizeof(uintptr) == sizeof(uvlong) && p>>32) + for(n = 60;n>=32;n-=4) printchar(io, "0123456789ABCDEF"[(p>>n)&0xF]); + + for(n = 28;n>=0;n-=4) printchar(io, "0123456789ABCDEF"[(p>>n)&0xF]); +} + +static +void +printint(Io *io, int n) +{ + if(n<0){ + if(n!=INT_MIN){ + printchar(io, '-'); + printint(io, -n); + return; + } + /* n is two's complement minimum integer */ + n = -(INT_MIN+1); + printchar(io, '-'); + printint(io, n/10); + printchar(io, n%10+'1'); + return; + } + if(n>9) + printint(io, n/10); + printchar(io, n%10+'0'); +} + +static +void +printoct(Io *io, unsigned n) +{ + if(n>7) + printoct(io, n>>3); + printchar(io, (n&7)+'0'); +} + +static +void +printval(Io *io, Word *a) +{ + if(a){ + while(a->link && a->link->str){ + printword(io, a->str); + printchar(io, ' '); + a = a->link; + } + printword(io, a->str); + } +} + +#define C0 t->child[0] +#define C1 t->child[1] +#define C2 t->child[2] + +static +void +printtree(Io *io, Tree *t) +{ + if(!t) + return; + + switch(t->type){ + default: print(io, "bad(%d)[%p %p %p]", t->type, C0, C1, C2); break; + case '$': print(io,"$%t",C0); break; + case '&': print(io,"%t&",C0); break; + case '^': print(io,"%t^%t",C0,C1); break; + case '`': print(io,"`%t",C0); break; + + case Tbasic: print(io, "%t", C0); break; + case Tbang: print(io, "!%t", C0); break; + case Tblock: print(io, "{%t}", C0); break; + case Tcount: print(io, "$#%t", C0); break; + case Tparen: print(io, "(%t)", C0); break; + case Tjoin: print(io,"$\"%t",C0); break; + case Tindex: print(io, "%t(%t)",C0); break; + case Tsubshell: print(io, "@ %t",C0); break; + //case Ttwiddle: print(io, "~ %t %t", C0, C1); break; + + case Toror: + case Tandand: + + case Targs: + if(!C0) + print(io, "%t", C1); + else if(!C1) + print(io, "%t", C0); + else + print(io, "%t %t", C0, C1); + break; + + case ';': + if(C0){ + if(C1) + print(io, "%t;%t", C0, C1); + else + print(io, "%t", C0); + }else + print(io, "%t", C1); + break; + + case Twords: + if(C0) + print(io, "%t", C0); + print(io, "%t", C1); + + case Tword: + if(t->quoted) + print(io, "%Q", t->str); + print(io, "%q", t->str); + break; + + case '=': + print(io, "%t=%t", C0, C1); + if(C2) + print(io, " %t", C2); + break; + + case Tdup: + if(t->redir.type == Rdupfd) + print(io, ">[%d=%d]", t->redir.fd[1], t->redir.fd[0]); + else + print(io, ">[%d=]", t->redir.fd[0]); + print(io, "%t", C1); + break; + + case Tredir: + switch(t->redir.type){ + case Rhere: + printchar(io, '<'); + case Rread: + printchar(io, '<'); + goto readfd; + case Rrdwr: + printchar(io, '<'); + printchar(io, '>'); + readfd: + if(t->redir.fd[0]!=0) + print(io, "[%d]", t->redir.fd[0]); + break; + case Rappend: + printchar(io, '>'); + goto writefd; + case Rwrite: + printchar(io, '>'); + printchar(io, '>'); + writefd: + if(t->redir.fd[0]!=1) + print(io, "[%d]", t->redir.fd[0]); + break; + } + print(io, "%t", C0); + if(C1) + print(io, " %t", C1); + break; + + case Tpipe: + print(io, "%t|", C0); + if(t->redir.fd[1]==0){ + if(t->redir.fd[0]!=1) + print(io, "[%d]", t->redir.fd[0]); + } + else + print(io, "[%d=%d]", t->redir.fd[0], t->redir.fd[1]); + print(io, "%t", C1); + break; + } +} + +#undef C0 +#undef C1 +#undef C2 + +// ----------------------------------------------------------------------- +// exports + +/* readers */ +int +get(Io *io) +{ + if(io->b==io->e) + return refill(io); + + return *io->b++ & 0xFF; +} + +/* writers */ +int +put(Io **iop, char c) +{ + int nb, ne, nc; + Io *io = *iop; + char *e = io->b + io->cap; + + if(io->e == e){ + nb = io->b - io->buf; + ne = io->e - io->buf; + nc = 2*io->cap; + + if(!(io = erealloc(io, sizeof(*io)+nc))) + return 0; + + io->b = io->buf + nb; + io->e = io->buf + ne; + io->cap = nc; + + *iop = io; + } + + *io->e++ = c; + return 1; +} + +/* printers */ +static int pfmtnest; + +void +print(Io *io, char *fmt, ...) +{ + va_list args; + char err[ERRMAX]; + + va_start(args, fmt); + pfmtnest++; + + for(;*fmt;fmt++) + if(*fmt!='%') + printchar(io, *fmt); + else + switch(*++fmt){ + case '\0': + va_end(args); + return; + case 'c': + printchar(io, va_arg(args, int)); + break; + case 'd': + printint(io, va_arg(args, int)); + break; + case 'o': + printoct(io, va_arg(args, unsigned)); + break; + case 'p': + printptr(io, va_arg(args, void*)); + break; + case 'Q': + printquote(io, va_arg(args, char *)); + break; + case 'q': + printword(io, va_arg(args, char *)); + break; + case 's': + printstr(io, va_arg(args, char *)); + break; + case 't': + printtree(io, va_arg(args, struct Tree *)); + break; + case 'v': + printval(io, va_arg(args, struct Word *)); + break; + default: + printchar(io, *fmt); + break; + } + + va_end(args); + + if(--pfmtnest==0) + flush(io); +} diff --git a/src/cmd/rc/job.c b/src/cmd/rc/job.c new file mode 100644 index 0000000..1587951 --- /dev/null +++ b/src/cmd/rc/job.c @@ -0,0 +1,91 @@ +#include "rc.h" + +#include +#include + +// ----------------------------------------------------------------------- +// exports + +Thread * +getjob(int pid, int *index) +{ + int i; + Thread *job; + for(i=0,job=shell.jobs; job && job->pid != pid; i++, job=job->link) + ; + + return job; +} + +void +report(Thread *job, int index) +{ + switch(job->wait.status){ + case Pdone: + print(shell.err, "job %d [%d]: done\n", index, job->pid); + break; + case Pstop: + print(shell.err, "job %d [%d]: suspended\n", index, job->pid); + break; + case Pagain: + print(shell.err, "job %d [%d]: continued\n", index, job->pid); + break; + case Prun: + print(shell.err, "job %d [%d]: running\n", index, job->pid); + break; + default: + fatal("bad wait status: %d\n", job->wait.status); + } +} + +void +wakeup(Thread *job) +{ + int i; + job->wait.status = Prun; + for(i=0; i < job->wait.len; i++){ + if(job->wait.on[i].status == Pstop) + job->wait.on[i].status = Prun; + } + + tcsetpgrp(0, job->pgid); +} + +void +foreground(Thread *job, int now) +{ + Thread *caller = job->caller; + if(now){ + if(kill(-job->pgid, SIGCONT) < 0) + perror("kill[SIGCONT]"); + } + + waitall(job); + /* + * reset state if we have a caller + * otherwise we will exit anyways + */ + if(caller && caller->flag.user){ + tcsetpgrp(0, caller->pid); + job->flag.user = 1; + } +} + +void +addjob(Thread *job) +{ + job->link = shell.jobs; + shell.jobs = job; + job->wait.status = Prun; +} + +void +deljob(Thread *job) +{ + Thread **jp; + + for(jp = &shell.jobs; *jp && *jp != job; jp = &(*jp)->link) + ; + + *jp = job->link; +} diff --git a/src/cmd/rc/lex.c b/src/cmd/rc/lex.c new file mode 100644 index 0000000..9ca2453 --- /dev/null +++ b/src/cmd/rc/lex.c @@ -0,0 +1,394 @@ +#include "rc.h" +#include "parse.h" + +static int advance(void); + +// ----------------------------------------------------------------------- +// lexer + +struct Lexer +{ + int c[2]; + ushort doprompt; + ushort hadword; + ushort haddollar; + ushort inquote; + char buf[BUFSIZ]; +}; + +static struct Lexer lexer = { .c={0, EOF}, .doprompt=1 }; + +#define put1(b) lexer.buf[0] = (b), lexer.buf[1] = 0; +#define put2(b0,b1) lexer.buf[0] = (b0), lexer.buf[1] = (b1), lexer.buf[2] = 0; +#define put3(b0,b1,b2) lexer.buf[0] = (b0), lexer.buf[1] = (b1), lexer.buf[2] = b2, lexer.buf[3] = 0; + +void +yyerror(const char *msg) +{ + print(shell.err, "rc:%d: ", runner->line); + + if(lexer.buf[0] && lexer.buf[0]!='\n') + print(shell.err, "%q: ", lexer.buf); + + print(shell.err, "%s\n", msg); + flush(shell.err); + + lexer.hadword = 0; + lexer.haddollar = 0; + + /* consume remaining tokens */ + while(lexer.c[0] !='\n' && lexer.c[0] != EOF) + advance(); +} + +int +readc(void) +{ + int c; + static int peek = EOF; + + if(peek!=EOF){ + c = peek; + peek = EOF; + return c; + } + + if(runner->flag.eof) + return EOF; + + if(!prompt(&lexer.doprompt)) + exit(1); // XXX: hack for signal handling right now... + + c = get(runner->cmd.io); + lexer.doprompt = lexer.doprompt || c=='\n' || c==EOF; + + if(c==EOF) + runner->flag.eof = 1; + + return c; +} + +static +int +peekc(void) +{ + if(lexer.c[1] == EOF) + lexer.c[1] = readc(); + + return lexer.c[1]; +} + +static +int +advance(void) +{ + int c = peekc(); + lexer.c[0] = lexer.c[1], lexer.c[1] = EOF; + + return c; +} + +static +void +skipws(void) +{ + int c; + for(;;){ + c = peekc(); + if(c== ' ' || c == '\t') + advance(); + else + return; + } +} + +static +void +skipnl(void) +{ + int c; + for(;;){ + c = peekc(); + if(c== ' ' || c == '\t' || c == '\n') + advance(); + else + return; + } +} + +static +int +nextis(int c) +{ + if(peekc()==c){ + advance(); + return 1; + } + return 0; +} + +static +char * +putbyte(char *buf, int c) +{ + if(!buf) + return buf; + + if(buf == arrend(lexer.buf)){ + fatal("lexer: out of buffer space"); + return nil; + } + *buf++ = c; + return buf; +} + +static +char * +putrune(char *buf, int c) +{ + buf = putbyte(buf, c); + if(utf8·onebyte(c)) + return buf; + if(utf8·twobyte(c)) + return putbyte(buf,advance()); + if(utf8·threebyte(c)){ + buf = putbyte(buf,advance()); + return putbyte(buf,advance()); + } + if(utf8·fourbyte(c)){ + buf = putbyte(buf,advance()); + buf = putbyte(buf,advance()); + return putbyte(buf,advance()); + } + fatal("malformed utf8 stream"); + + return nil; +} + +// ----------------------------------------------------------------------- +// exported functions + +// TODO: turn into static tables +int +iswordchar(int c) +{ + return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; +} + +int +isidentchar(int c) +{ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); +} + +int +yylex(void) +{ + int c, d = peekc(); + Tree *node; + char *w = lexer.buf; + + yylval.tree = nil; + + /* inject tokens */ + if(lexer.hadword){ + lexer.hadword = 0; + if(d=='('){ + advance(); + strcpy(lexer.buf, "( [Tindex]"); + return Tindex; + } + if(iswordchar(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(lexer.buf, "^"); + return '^'; + } + } + + lexer.inquote = 0; + + skipws(); + switch(c=advance()){ + case EOF: + lexer.haddollar = 0; + put3('E','O','F'); + return EOF; + + case '$': + lexer.haddollar = 1; + if(nextis('#')){ + put2('$','#'); + return Tcount; + } + if(nextis('^')){ + put2('$','^'); + return Tjoin; + } + put1('$'); + return '$'; + + case '@': + lexer.haddollar = 0; + put1('@'); + return Tsubshell; + + case '!': + lexer.haddollar = 0; + put1('!'); + return Tbang; + + case '&': + lexer.haddollar = 0; + if(nextis('&')){ + put2('&','&'); + return Tandand; + } + put1('&'); + return '&'; + + case '|': + lexer.haddollar = 0; + if(nextis('|')){ + put2('|','|'); + return Toror; + } + node = maketree(); + *w++ = '|'; + + node->type = Tpipe; + node->redir.fd[0] = 1; + node->redir.fd[1] = 0; + goto redir; + + case '>': + lexer.haddollar = 0; + node = maketree(); + *w++ = '>'; + node->type = Tredir; + + if(nextis('>')){ + node->redir.type = Rappend; + *w++ = '>'; + }else + node->redir.type = Rwrite; + node->redir.fd[0] = 1; + goto redir; + + case '<': + lexer.haddollar = 0; + node = maketree(); + *w++ = '<'; + node->type = Tredir; + + if(nextis('<')){ + node->redir.type = Rhere; + *w++ = '<'; + }else if(nextis('>')){ + node->redir.type = Rrdwr; + *w++ = '>'; + }else{ + node->redir.type = Rread; + } + node->redir.fd[0] = 0; + /* fallthrough */ + redir: + if(nextis('[')){ + *w++='['; + c = advance(); + *w++ = c; + if(c < '0' || '9' < c){ + badredir: + *w = 0; + yyerror(node->type == Tpipe ? "pipe syntax" : "redirection syntax"); + return EOF; + } + node->redir.fd[0] = 0; + do{ + node->redir.fd[0] = 10*node->redir.fd[0]+(c-'0'); + *w++ = c; + c = advance(); + }while('0'<=c && c<='9'); + + if(c == '='){ + *w++ = '='; + if(node->type==Tredir) + node->type = Tdup; + c = advance(); + } + if(c < '0' || '9' < c){ + if(node->type == Tpipe) + goto badredir; + node->redir.type = Rclose; + }else{ + 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'); + *w++ = c; + c = advance(); + }while('0'<=c && c<='9'); + } + if(c != ']' || (node->type == Tdup && (node->redir.type = Rhere || node->redir.type == Rappend))) + goto badredir; + *w++ = ']'; + } + *w++ = 0; + yylval.tree = node; + + return node->type; + + case '\'': + lexer.hadword = 1; + lexer.inquote = 1; + lexer.haddollar = 0; + for(;;){ + c = advance(); + if(c==EOF) + break; + + if(c=='\''){ + if(peekc()!='\'') + break; + advance(); + } + w = putrune(w, c); + } + if(w) + *w = 0; + node = token(Tword, lexer.buf); + node->quoted = 1; + return node->type; + + default: + ; + } + if(!iswordchar(c)){ + put1(c); + lexer.haddollar = 0; + return c; + } + + for(;;){ + w = putrune(w, c); + c = peekc(); + if(lexer.haddollar ? !isidentchar(c) : !iswordchar(c)) + break; + advance(); + } + + lexer.hadword = 1; + lexer.haddollar = 0; + if(w) + *w = 0; + + node = token(Tword, lexer.buf); + if((c=iskeyword(lexer.buf))){ + node->type = c; + lexer.hadword = 0; + } + + node->quoted = 0; + + yylval.tree = node; + return node->type; +} diff --git a/src/cmd/rc/main.c b/src/cmd/rc/main.c new file mode 100644 index 0000000..2c0aa42 --- /dev/null +++ b/src/cmd/rc/main.c @@ -0,0 +1,66 @@ +#include "rc.h" +#include "parse.h" +#include "exec.h" + +#include +#include + +// ----------------------------------------------------------------------- +// globals + +Thread *runner = nil; +Shell shell = { 0 }; + +// ----------------------------------------------------------------------- +// functions + +void +initshell(void) +{ + if((shell.interactive=isatty(0))){ + while(tcgetpgrp(0) != (shell.pid = getpgrp())) + kill(-shell.pid, SIGTTIN); + + /* ignore job control signals */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + /* + * NOTE: if SIGCHLD is set to SIG_IGN then + * 1. children that terminate do not become zombies + * 2. call a to wait() will block until all children have terminated + * 3. the call to wait will fail with errno == ECHILD + * see for discussion: + * https://stackoverflow.com/questions/1608017/no-child-process-error-from-waitpid-when-waiting-for-process-group + */ + // signal(SIGCHLD, SIG_IGN); + + /* take control */ + shell.pid = getpid(); + if(setpgid(shell.pid, shell.pid)<0) + fatal("could not put shell in its own process group"); + + tcsetpgrp(shell.pid, shell.pid); + } +} + +// ----------------------------------------------------------------------- +// main point of entry + +int +main(int argc, char *argv[]) +{ + shell.err = openfd(2); + + initenv(); + initpath(); + initkeywords(); + initshell(); + inithistory(); + + enablevi(); + xboot(argc, argv); + /* unreachable */ +} diff --git a/src/cmd/rc/parse.c b/src/cmd/rc/parse.c new file mode 100644 index 0000000..1b29d41 --- /dev/null +++ b/src/cmd/rc/parse.c @@ -0,0 +1,2059 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 7 "sys/cmd/rc/syntax.y" + + #include "rc.h" + + int yylex(void); + void yyerror(const char *); + +#line 78 "sys/cmd/rc/parse.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "parse.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_Tfor = 3, /* Tfor */ + YYSYMBOL_Tin = 4, /* Tin */ + YYSYMBOL_Twhile = 5, /* Twhile */ + YYSYMBOL_Tif = 6, /* Tif */ + YYSYMBOL_Telse = 7, /* Telse */ + YYSYMBOL_Tswitch = 8, /* Tswitch */ + YYSYMBOL_Tcase = 9, /* Tcase */ + YYSYMBOL_Tcasebody = 10, /* Tcasebody */ + YYSYMBOL_Ttwiddle = 11, /* Ttwiddle */ + YYSYMBOL_Tbang = 12, /* Tbang */ + YYSYMBOL_Tsubshell = 13, /* Tsubshell */ + YYSYMBOL_Tfunc = 14, /* Tfunc */ + YYSYMBOL_Tredir = 15, /* Tredir */ + YYSYMBOL_Tdup = 16, /* Tdup */ + YYSYMBOL_Tpipe = 17, /* Tpipe */ + YYSYMBOL_Tindex = 18, /* Tindex */ + YYSYMBOL_Tbasic = 19, /* Tbasic */ + YYSYMBOL_Targs = 20, /* Targs */ + YYSYMBOL_Tword = 21, /* Tword */ + YYSYMBOL_Twords = 22, /* Twords */ + YYSYMBOL_Tparen = 23, /* Tparen */ + YYSYMBOL_Tblock = 24, /* Tblock */ + YYSYMBOL_25_ = 25, /* ')' */ + YYSYMBOL_Tandand = 26, /* Tandand */ + YYSYMBOL_Toror = 27, /* Toror */ + YYSYMBOL_28_n_ = 28, /* '\n' */ + YYSYMBOL_29_ = 29, /* '^' */ + YYSYMBOL_30_ = 30, /* '$' */ + YYSYMBOL_Tcount = 31, /* Tcount */ + YYSYMBOL_Tjoin = 32, /* Tjoin */ + YYSYMBOL_33_ = 33, /* '(' */ + YYSYMBOL_34_ = 34, /* '{' */ + YYSYMBOL_35_ = 35, /* '}' */ + YYSYMBOL_36_ = 36, /* ';' */ + YYSYMBOL_37_ = 37, /* '&' */ + YYSYMBOL_38_ = 38, /* '=' */ + YYSYMBOL_39_ = 39, /* '`' */ + YYSYMBOL_YYACCEPT = 40, /* $accept */ + YYSYMBOL_rc = 41, /* rc */ + YYSYMBOL_line = 42, /* line */ + YYSYMBOL_body = 43, /* body */ + YYSYMBOL_paren = 44, /* paren */ + YYSYMBOL_block = 45, /* block */ + YYSYMBOL_cmds = 46, /* cmds */ + YYSYMBOL_cmdsln = 47, /* cmdsln */ + YYSYMBOL_ifbody = 48, /* ifbody */ + YYSYMBOL_case = 49, /* case */ + YYSYMBOL_casebody = 50, /* casebody */ + YYSYMBOL_assign = 51, /* assign */ + YYSYMBOL_redir = 52, /* redir */ + YYSYMBOL_epilog = 53, /* epilog */ + YYSYMBOL_cmd = 54, /* cmd */ + YYSYMBOL_basic = 55, /* basic */ + YYSYMBOL_atom = 56, /* atom */ + YYSYMBOL_word = 57, /* word */ + YYSYMBOL_executable = 58, /* executable */ + YYSYMBOL_nonkeyword = 59, /* nonkeyword */ + YYSYMBOL_keyword = 60, /* keyword */ + YYSYMBOL_words = 61, /* words */ + YYSYMBOL_wordsnl = 62, /* wordsnl */ + YYSYMBOL_nl = 63 /* nl */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_uint8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 56 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 478 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 40 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 24 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 73 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 129 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 283 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 30, 2, 37, 2, + 33, 25, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, + 2, 38, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 29, 2, 39, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 34, 2, 35, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 26, 27, 31, 32 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 38, 38, 39, 42, 43, 46, 47, 50, 53, + 56, 57, 60, 61, 64, 65, 68, 69, 72, 73, + 74, 77, 80, 81, 84, 85, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 105, 106, 107, 110, 111, 114, 115, 118, 119, + 122, 123, 124, 125, 126, 127, 128, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 135, 136, 139, + 140, 141, 143, 145 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "Tfor", "Tin", + "Twhile", "Tif", "Telse", "Tswitch", "Tcase", "Tcasebody", "Ttwiddle", + "Tbang", "Tsubshell", "Tfunc", "Tredir", "Tdup", "Tpipe", "Tindex", + "Tbasic", "Targs", "Tword", "Twords", "Tparen", "Tblock", "')'", + "Tandand", "Toror", "'\\n'", "'^'", "'$'", "Tcount", "Tjoin", "'('", + "'{'", "'}'", "';'", "'&'", "'='", "'`'", "$accept", "rc", "line", + "body", "paren", "block", "cmds", "cmdsln", "ifbody", "case", "casebody", + "assign", "redir", "epilog", "cmd", "basic", "atom", "word", + "executable", "nonkeyword", "keyword", "words", "wordsnl", "nl", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-82) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-3) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 121, -17, -2, -2, 5, 439, 439, 343, -82, -82, + 343, 343, 343, -82, 439, -23, 45, 32, 11, 439, + 439, 439, 13, 158, -14, -82, 343, 439, -82, -82, + 343, 30, 30, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, 34, -82, -82, 47, -82, -82, + 195, 41, -82, 439, 54, -82, -82, -82, 11, -82, + -82, 30, 30, -82, -82, -82, -82, -82, -82, 34, + 343, 343, 19, 44, 375, 375, 4, 343, -82, -82, + -82, 34, -82, -82, -82, -82, 375, 375, 375, -82, + 34, -82, -82, -82, -82, 29, 77, -82, 29, -82, + -82, 269, -82, 30, 30, 306, 375, -82, 25, -82, + 34, -82, 29, 375, 407, 375, 29, -82, 407, 407, + 48, 54, 29, 232, -82, -82, -82, -82, -82 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 26, 0, 0, 0, 0, 26, 26, 0, 22, 50, + 0, 0, 0, 69, 26, 0, 0, 0, 24, 26, + 26, 26, 4, 27, 41, 48, 0, 26, 72, 72, + 0, 34, 35, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 46, 23, 44, 45, 51, 54, 55, + 0, 0, 12, 26, 6, 56, 1, 3, 24, 28, + 5, 33, 32, 72, 72, 72, 10, 11, 43, 42, + 0, 0, 0, 0, 26, 26, 0, 0, 67, 53, + 70, 71, 9, 7, 13, 25, 26, 26, 26, 49, + 21, 67, 72, 8, 73, 38, 24, 39, 14, 72, + 47, 0, 29, 30, 31, 0, 26, 72, 0, 52, + 68, 72, 36, 26, 26, 26, 15, 67, 26, 26, + 0, 18, 37, 0, 20, 19, 40, 17, 16 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -82, -82, 75, -19, 93, -11, 18, -52, -82, -82, + -21, -82, -1, 42, 0, -82, -9, 28, -82, 2, + -82, -81, -82, -22 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 16, 17, 51, 28, 18, 52, 53, 97, 119, + 120, 20, 21, 59, 54, 23, 43, 110, 24, 25, + 46, 101, 50, 74 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 22, 47, 48, 49, 55, 31, 32, 75, 73, 45, + 105, 14, 45, 45, 45, 70, 26, 58, 19, 22, + 61, 62, 68, 91, 71, 45, 7, 8, 45, 99, + 63, 27, 45, 77, 83, 44, 123, 19, 30, 64, + 65, 86, 87, 88, 92, 56, 63, 63, 77, 66, + 67, 69, 45, 94, 72, 64, 65, 58, 76, 114, + 57, 89, 118, 77, 96, 78, 118, 118, 100, 93, + 106, 63, 45, 45, 95, 98, 82, 108, 81, 45, + 64, 65, 84, 126, 107, 113, 102, 103, 104, 115, + 66, 67, 7, 8, 60, 58, 29, 124, 125, 90, + 85, 0, 0, 45, 0, 0, 112, 45, 0, 0, + 0, 0, 0, 116, 121, 122, 0, 0, 121, 121, + 0, -2, 0, 0, 1, 45, 2, 3, 0, 4, + 0, 0, 0, 5, 6, 0, 7, 8, 0, 0, + 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 0, 0, 0, 0, + 15, 33, 34, 35, 36, 37, 38, 39, 0, 0, + 40, 41, 42, 7, 8, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, + 12, 13, 0, 0, 0, 0, 0, 15, 33, 34, + 35, 36, 37, 38, 39, 0, 0, 40, 41, 42, + 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 79, 0, 0, 80, 0, 10, 11, 12, 13, 0, + 0, 0, 0, 0, 15, 33, 34, 35, 36, 37, + 38, 39, 0, 0, 40, 41, 42, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, + 127, 0, 10, 11, 12, 13, 0, 0, 128, 0, + 0, 15, 33, 34, 35, 36, 37, 38, 39, 0, + 0, 40, 41, 42, 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 109, 0, 0, 0, 0, 10, + 11, 12, 13, 0, 0, 0, 0, 0, 15, 33, + 34, 35, 36, 37, 38, 39, 0, 0, 40, 41, + 42, 0, 0, 0, 0, 0, 0, 9, 0, 0, + 0, 111, 0, 0, 0, 0, 10, 11, 12, 13, + 0, 0, 0, 0, 0, 15, 33, 34, 35, 36, + 37, 38, 39, 0, 0, 40, 41, 42, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 11, 12, 13, 0, 1, 0, + 2, 3, 15, 4, 0, 0, 0, 5, 6, 0, + 7, 8, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0, 0, 94, 0, 10, 11, 12, 13, 14, + 1, 0, 2, 3, 15, 4, 117, 0, 0, 5, + 6, 0, 7, 8, 0, 0, 0, 0, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 14, 1, 0, 2, 3, 15, 4, 0, 0, + 0, 5, 6, 0, 7, 8, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 11, 12, 13, 14, 0, 0, 0, 0, 15 +}; + +static const yytype_int8 yycheck[] = +{ + 0, 10, 11, 12, 15, 5, 6, 29, 27, 7, + 91, 34, 10, 11, 12, 29, 33, 18, 0, 19, + 20, 21, 23, 4, 38, 23, 15, 16, 26, 25, + 17, 33, 30, 29, 53, 7, 117, 19, 33, 26, + 27, 63, 64, 65, 25, 0, 17, 17, 29, 36, + 37, 23, 50, 28, 26, 26, 27, 58, 30, 34, + 28, 70, 114, 29, 75, 18, 118, 119, 77, 25, + 92, 17, 70, 71, 74, 75, 35, 99, 50, 77, + 26, 27, 28, 35, 7, 107, 86, 87, 88, 111, + 36, 37, 15, 16, 19, 96, 3, 118, 119, 71, + 58, -1, -1, 101, -1, -1, 106, 105, -1, -1, + -1, -1, -1, 113, 114, 115, -1, -1, 118, 119, + -1, 0, -1, -1, 3, 123, 5, 6, -1, 8, + -1, -1, -1, 12, 13, -1, 15, 16, -1, -1, + -1, -1, 21, -1, -1, -1, -1, -1, -1, -1, + -1, 30, 31, 32, 33, 34, -1, -1, -1, -1, + 39, 3, 4, 5, 6, 7, 8, 9, -1, -1, + 12, 13, 14, 15, 16, -1, -1, -1, -1, 21, + -1, -1, -1, -1, -1, -1, -1, -1, 30, 31, + 32, 33, -1, -1, -1, -1, -1, 39, 3, 4, + 5, 6, 7, 8, 9, -1, -1, 12, 13, 14, + -1, -1, -1, -1, -1, -1, 21, -1, -1, -1, + 25, -1, -1, 28, -1, 30, 31, 32, 33, -1, + -1, -1, -1, -1, 39, 3, 4, 5, 6, 7, + 8, 9, -1, -1, 12, 13, 14, -1, -1, -1, + -1, -1, -1, 21, -1, -1, -1, -1, -1, -1, + 28, -1, 30, 31, 32, 33, -1, -1, 36, -1, + -1, 39, 3, 4, 5, 6, 7, 8, 9, -1, + -1, 12, 13, 14, -1, -1, -1, -1, -1, -1, + 21, -1, -1, -1, 25, -1, -1, -1, -1, 30, + 31, 32, 33, -1, -1, -1, -1, -1, 39, 3, + 4, 5, 6, 7, 8, 9, -1, -1, 12, 13, + 14, -1, -1, -1, -1, -1, -1, 21, -1, -1, + -1, 25, -1, -1, -1, -1, 30, 31, 32, 33, + -1, -1, -1, -1, -1, 39, 3, 4, 5, 6, + 7, 8, 9, -1, -1, 12, 13, 14, -1, -1, + -1, -1, -1, -1, 21, -1, -1, -1, -1, -1, + -1, -1, -1, 30, 31, 32, 33, -1, 3, -1, + 5, 6, 39, 8, -1, -1, -1, 12, 13, -1, + 15, 16, -1, -1, -1, -1, 21, -1, -1, -1, + -1, -1, -1, 28, -1, 30, 31, 32, 33, 34, + 3, -1, 5, 6, 39, 8, 9, -1, -1, 12, + 13, -1, 15, 16, -1, -1, -1, -1, 21, -1, + -1, -1, -1, -1, -1, -1, -1, 30, 31, 32, + 33, 34, 3, -1, 5, 6, 39, 8, -1, -1, + -1, 12, 13, -1, 15, 16, -1, -1, -1, -1, + 21, -1, -1, -1, -1, -1, -1, -1, -1, 30, + 31, 32, 33, 34, -1, -1, -1, -1, 39 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 5, 6, 8, 12, 13, 15, 16, 21, + 30, 31, 32, 33, 34, 39, 41, 42, 45, 46, + 51, 52, 54, 55, 58, 59, 33, 33, 44, 44, + 33, 54, 54, 3, 4, 5, 6, 7, 8, 9, + 12, 13, 14, 56, 57, 59, 60, 56, 56, 56, + 62, 43, 46, 47, 54, 45, 0, 28, 52, 53, + 42, 54, 54, 17, 26, 27, 36, 37, 52, 57, + 29, 38, 57, 43, 63, 63, 57, 29, 18, 25, + 28, 57, 35, 43, 28, 53, 63, 63, 63, 56, + 57, 4, 25, 25, 28, 54, 45, 48, 54, 25, + 56, 61, 54, 54, 54, 61, 63, 7, 63, 25, + 57, 25, 54, 63, 34, 63, 54, 9, 47, 49, + 50, 54, 54, 61, 50, 50, 35, 28, 36 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 40, 41, 41, 42, 42, 43, 43, 44, 45, + 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, + 50, 51, 52, 52, 53, 53, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 55, 55, 55, 56, 56, 57, 57, 58, 58, + 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, + 62, 62, 63, 63 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 1, 2, 1, 2, 3, 3, + 2, 2, 1, 2, 1, 4, 3, 3, 1, 2, + 2, 3, 1, 2, 0, 2, 0, 1, 2, 4, + 4, 4, 2, 2, 2, 2, 6, 8, 4, 4, + 8, 1, 2, 2, 1, 1, 1, 3, 1, 3, + 1, 2, 5, 3, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 2, 0, + 2, 2, 0, 2 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* rc: %empty */ +#line 38 "sys/cmd/rc/syntax.y" + { return 0; } +#line 1549 "sys/cmd/rc/parse.c" + break; + + case 3: /* rc: line '\n' */ +#line 39 "sys/cmd/rc/syntax.y" + { return compile((yyvsp[-1].tree)); } +#line 1555 "sys/cmd/rc/parse.c" + break; + + case 5: /* line: cmds line */ +#line 43 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(';', (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1561 "sys/cmd/rc/parse.c" + break; + + case 7: /* body: cmdsln body */ +#line 47 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(';', (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1567 "sys/cmd/rc/parse.c" + break; + + case 8: /* paren: '(' body ')' */ +#line 50 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tparen, (yyvsp[-1].tree)); } +#line 1573 "sys/cmd/rc/parse.c" + break; + + case 9: /* block: '{' body '}' */ +#line 53 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tblock, (yyvsp[-1].tree)); } +#line 1579 "sys/cmd/rc/parse.c" + break; + + case 11: /* cmds: cmd '&' */ +#line 57 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1('&', (yyvsp[-1].tree)); } +#line 1585 "sys/cmd/rc/parse.c" + break; + + case 14: /* ifbody: cmd */ +#line 64 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tif, nil, (yyvsp[0].tree)); } +#line 1591 "sys/cmd/rc/parse.c" + break; + + case 15: /* ifbody: block Telse nl cmd */ +#line 65 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree3(Tif, nil, (yyvsp[-3].tree), (yyvsp[-2].tree)); } +#line 1597 "sys/cmd/rc/parse.c" + break; + + case 16: /* case: Tcase words ';' */ +#line 68 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-1].tree), 0); } +#line 1603 "sys/cmd/rc/parse.c" + break; + + case 17: /* case: Tcase words '\n' */ +#line 69 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-1].tree), 0); } +#line 1609 "sys/cmd/rc/parse.c" + break; + + case 18: /* casebody: cmd */ +#line 72 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tcasebody, (yyvsp[0].tree), nil); } +#line 1615 "sys/cmd/rc/parse.c" + break; + + case 19: /* casebody: case casebody */ +#line 73 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tcasebody, (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1621 "sys/cmd/rc/parse.c" + break; + + case 20: /* casebody: cmdsln casebody */ +#line 74 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tcasebody, (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1627 "sys/cmd/rc/parse.c" + break; + + case 21: /* assign: executable '=' word */ +#line 77 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2('=', (yyvsp[-2].tree), (yyvsp[0].tree)); } +#line 1633 "sys/cmd/rc/parse.c" + break; + + case 23: /* redir: Tredir word */ +#line 81 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 0); } +#line 1639 "sys/cmd/rc/parse.c" + break; + + case 24: /* epilog: %empty */ +#line 84 "sys/cmd/rc/syntax.y" + { (yyval.tree) = nil; } +#line 1645 "sys/cmd/rc/parse.c" + break; + + case 25: /* epilog: redir epilog */ +#line 85 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 1); } +#line 1651 "sys/cmd/rc/parse.c" + break; + + case 26: /* cmd: %empty */ +#line 88 "sys/cmd/rc/syntax.y" + { (yyval.tree) = nil; } +#line 1657 "sys/cmd/rc/parse.c" + break; + + case 27: /* cmd: basic */ +#line 89 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tbasic, (yyvsp[0].tree)); } +#line 1663 "sys/cmd/rc/parse.c" + break; + + case 28: /* cmd: block epilog */ +#line 90 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangepilog((yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1669 "sys/cmd/rc/parse.c" + break; + + case 29: /* cmd: cmd Tpipe nl cmd */ +#line 91 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild2((yyvsp[-2].tree), (yyvsp[-3].tree), 0, (yyvsp[0].tree), 1); } +#line 1675 "sys/cmd/rc/parse.c" + break; + + case 30: /* cmd: cmd Tandand nl cmd */ +#line 92 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tandand, (yyvsp[-3].tree), (yyvsp[0].tree)); } +#line 1681 "sys/cmd/rc/parse.c" + break; + + case 31: /* cmd: cmd Toror nl cmd */ +#line 93 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Toror, (yyvsp[-3].tree), (yyvsp[0].tree)); } +#line 1687 "sys/cmd/rc/parse.c" + break; + + case 32: /* cmd: redir cmd */ +#line 94 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 1); } +#line 1693 "sys/cmd/rc/parse.c" + break; + + case 33: /* cmd: assign cmd */ +#line 95 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 2); } +#line 1699 "sys/cmd/rc/parse.c" + break; + + case 34: /* cmd: Tbang cmd */ +#line 96 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tbang, (yyvsp[0].tree)); } +#line 1705 "sys/cmd/rc/parse.c" + break; + + case 35: /* cmd: Tsubshell cmd */ +#line 97 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tsubshell, (yyvsp[0].tree)); } +#line 1711 "sys/cmd/rc/parse.c" + break; + + case 36: /* cmd: Tfor '(' word ')' nl cmd */ +#line 98 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild3((yyvsp[-5].tree), (yyvsp[-3].tree), nil, (yyvsp[0].tree)); } +#line 1717 "sys/cmd/rc/parse.c" + break; + + case 37: /* cmd: Tfor '(' word Tin words ')' nl cmd */ +#line 99 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild3((yyvsp[-7].tree), (yyvsp[-5].tree), (yyvsp[-3].tree), (yyvsp[0].tree)); } +#line 1723 "sys/cmd/rc/parse.c" + break; + + case 38: /* cmd: Twhile paren nl cmd */ +#line 100 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild2((yyvsp[-3].tree), (yyvsp[-2].tree), 0, (yyvsp[0].tree), 1); } +#line 1729 "sys/cmd/rc/parse.c" + break; + + case 39: /* cmd: Tif paren nl ifbody */ +#line 101 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-3].tree), 0); } +#line 1735 "sys/cmd/rc/parse.c" + break; + + case 40: /* cmd: Tswitch '(' word ')' nl '{' casebody '}' */ +#line 102 "sys/cmd/rc/syntax.y" + { (yyval.tree) = hangchild2((yyvsp[-7].tree), (yyvsp[-5].tree), 0, (yyvsp[-1].tree), 1); } +#line 1741 "sys/cmd/rc/parse.c" + break; + + case 42: /* basic: basic word */ +#line 106 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Targs, (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1747 "sys/cmd/rc/parse.c" + break; + + case 43: /* basic: basic redir */ +#line 107 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Targs, (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1753 "sys/cmd/rc/parse.c" + break; + + case 45: /* atom: keyword */ +#line 111 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tword, (yyvsp[0].tree)); } +#line 1759 "sys/cmd/rc/parse.c" + break; + + case 47: /* word: word '^' atom */ +#line 115 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2('^', (yyvsp[-2].tree), (yyvsp[0].tree)); } +#line 1765 "sys/cmd/rc/parse.c" + break; + + case 49: /* executable: executable '^' atom */ +#line 119 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2('^', (yyvsp[-2].tree), (yyvsp[0].tree)); } +#line 1771 "sys/cmd/rc/parse.c" + break; + + case 51: /* nonkeyword: '$' atom */ +#line 123 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1('$', (yyvsp[0].tree)); } +#line 1777 "sys/cmd/rc/parse.c" + break; + + case 52: /* nonkeyword: '$' atom Tindex words ')' */ +#line 124 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Tindex, (yyvsp[-3].tree), (yyvsp[-1].tree)); } +#line 1783 "sys/cmd/rc/parse.c" + break; + + case 53: /* nonkeyword: '(' wordsnl ')' */ +#line 125 "sys/cmd/rc/syntax.y" + { (yyval.tree) = (yyvsp[-1].tree); } +#line 1789 "sys/cmd/rc/parse.c" + break; + + case 54: /* nonkeyword: Tcount atom */ +#line 126 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tcount, (yyvsp[0].tree)); } +#line 1795 "sys/cmd/rc/parse.c" + break; + + case 55: /* nonkeyword: Tjoin atom */ +#line 127 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1(Tjoin, (yyvsp[0].tree)); } +#line 1801 "sys/cmd/rc/parse.c" + break; + + case 56: /* nonkeyword: '`' block */ +#line 128 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree1('`', (yyvsp[0].tree)); } +#line 1807 "sys/cmd/rc/parse.c" + break; + + case 67: /* words: %empty */ +#line 135 "sys/cmd/rc/syntax.y" + { (yyval.tree) = nil; } +#line 1813 "sys/cmd/rc/parse.c" + break; + + case 68: /* words: words word */ +#line 136 "sys/cmd/rc/syntax.y" + { (yyval.tree) = maketree2(Twords, (yyvsp[-1].tree), (yyvsp[0].tree)); } +#line 1819 "sys/cmd/rc/parse.c" + break; + + case 69: /* wordsnl: %empty */ +#line 139 "sys/cmd/rc/syntax.y" + { (yyval.tree) = nil; } +#line 1825 "sys/cmd/rc/parse.c" + break; + + case 71: /* wordsnl: wordsnl word */ +#line 141 "sys/cmd/rc/syntax.y" + {(yyval.tree) = (!(yyvsp[-1].tree)) ? ((!(yyvsp[0].tree)) ? nil : (yyvsp[0].tree)) : ((!(yyvsp[0].tree)) ? (yyvsp[-1].tree) : maketree2(Twords, (yyvsp[-1].tree), (yyvsp[0].tree))); } +#line 1831 "sys/cmd/rc/parse.c" + break; + + +#line 1835 "sys/cmd/rc/parse.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == YYENOMEM) + YYNOMEM; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 147 "sys/cmd/rc/syntax.y" + diff --git a/src/cmd/rc/parse.h b/src/cmd/rc/parse.h new file mode 100644 index 0000000..64ee07b --- /dev/null +++ b/src/cmd/rc/parse.h @@ -0,0 +1,141 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED +# define YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + Tfor = 258, /* Tfor */ + Tin = 259, /* Tin */ + Twhile = 260, /* Twhile */ + Tif = 261, /* Tif */ + Telse = 262, /* Telse */ + Tswitch = 263, /* Tswitch */ + Tcase = 264, /* Tcase */ + Tcasebody = 265, /* Tcasebody */ + Ttwiddle = 266, /* Ttwiddle */ + Tbang = 267, /* Tbang */ + Tsubshell = 268, /* Tsubshell */ + Tfunc = 269, /* Tfunc */ + Tredir = 270, /* Tredir */ + Tdup = 271, /* Tdup */ + Tpipe = 272, /* Tpipe */ + Tindex = 273, /* Tindex */ + Tbasic = 274, /* Tbasic */ + Targs = 275, /* Targs */ + Tword = 276, /* Tword */ + Twords = 277, /* Twords */ + Tparen = 278, /* Tparen */ + Tblock = 279, /* Tblock */ + Tandand = 280, /* Tandand */ + Toror = 281, /* Toror */ + Tcount = 282, /* Tcount */ + Tjoin = 283 /* Tjoin */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define Tfor 258 +#define Tin 259 +#define Twhile 260 +#define Tif 261 +#define Telse 262 +#define Tswitch 263 +#define Tcase 264 +#define Tcasebody 265 +#define Ttwiddle 266 +#define Tbang 267 +#define Tsubshell 268 +#define Tfunc 269 +#define Tredir 270 +#define Tdup 271 +#define Tpipe 272 +#define Tindex 273 +#define Tbasic 274 +#define Targs 275 +#define Tword 276 +#define Twords 277 +#define Tparen 278 +#define Tblock 279 +#define Tandand 280 +#define Toror 281 +#define Tcount 282 +#define Tjoin 283 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 24 "sys/cmd/rc/syntax.y" + + struct Tree *tree; + +#line 127 "sys/cmd/rc/parse.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED */ diff --git a/src/cmd/rc/prompt.c b/src/cmd/rc/prompt.c new file mode 100644 index 0000000..1122d54 --- /dev/null +++ b/src/cmd/rc/prompt.c @@ -0,0 +1,36 @@ +#include "rc.h" + +/* static char promptbuf[7] = {'>', ' ', 0, ' ' , ' ', ' ', 0}; */ +static char *base= "\x1b[1;31m" ">" "\x1b[0;0m" " ", *promptstr; + +void +resetprompt(void) +{ + promptstr = base; +} + +int +prompt(ushort *flag) +{ + int f = *flag; + + if(f){ + if(!readline(promptstr)){ + runner->flag.eof = 1; + return 0; + } + if(runner->cmd.io->e[-1] == '\n'){ + runner->cmd.io->e[-1] = 0; + addhistory(runner->cmd.io->b); + runner->cmd.io->e[-1] = '\n'; + } + + write(mapfd(0), "\n\r", 2); + promptstr = " "; + + runner->line++; + *flag = 0; + } + + return 1; +} diff --git a/src/cmd/rc/rc.h b/src/cmd/rc/rc.h new file mode 100644 index 0000000..9b415fc --- /dev/null +++ b/src/cmd/rc/rc.h @@ -0,0 +1,263 @@ + +#include +#include +#include + +// ----------------------------------------------------------------------- +// types + +typedef struct Io Io; +typedef struct Var Var; +typedef struct Word Word; +typedef struct List List; +typedef struct Tree Tree; +typedef struct Redir Redir; +typedef union Code Code; +typedef struct Thread Thread; +typedef struct Shell Shell; + +struct Io +{ + int fd, cap; + char *s; + char *b, *e, buf[]; +}; + +enum +{ + Rappend, + Rwrite, + Rread, + Rhere, + Rdupfd, + Ropen, + Rclose, + Rrdwr +}; + +struct Tree +{ + int type; + union{ + struct { + ushort quoted; + char *str; // Tword + }; + struct { + ushort type; // Tpipe, Tredir, Tdup + int fd[2]; + } redir; + }; + Tree *child[3]; + Tree *link; +}; + +struct Word +{ + char *str; + Word *link; +}; + +struct List +{ + Word *word; + List *link; +}; + +/* + * the first word of any code vector is a reference count + * always create a new reference to a code vector by calling copycode() + * always call freecode() when deleting a reference + */ +union Code +{ + int i; + void (*f)(void); + char *s; +}; + +struct Var +{ + char *name; + Word *val; + short new : 1; + short newfunc : 1; + Code *func; + void (*update)(Var *); + Var *link; +}; + +struct Redir +{ + char type; /* what to do */ + short from, to; /* what to do it to */ + struct Redir *link; /* what else to do (reverse order) */ +}; + +enum +{ + Pnil, + Prun, + Pstop, + Psig, + Pagain, + Pdone, +}; + +struct WaitItem +{ + int pid; + ushort status; +}; + +struct Thread +{ + struct { + int i; + Code *exe; + } code; // execution stack + struct { + Io *io; + char *path; + } cmd; // command input + + List *args; // argument stack + Var *local; // local variables + struct { + Redir *start; + Redir *end; + } redir; // list of redirections + + struct { + ushort user : 1; + ushort eof : 1; + } flag; + + struct { + ushort status; + int len, cap; + struct WaitItem *on; + } wait; + + int pid, pgid, status; + long line; + + Thread *caller; // process we return to + Thread *link; // next job +}; + +struct Shell +{ + int pid; + Io *err; + int status; + int interactive; + Thread *jobs; +}; + +// ----------------------------------------------------------------------- +// globals + +extern Shell shell; +extern Thread *runner; +extern Code *compiled; + +// ----------------------------------------------------------------------- +// functions + +/* util.c */ +void itoa(char*, long i); +void fatal(char *, ...); +void *emalloc(uintptr); +void *erealloc(void*, uintptr); +void efree(void*); + +/* input.c */ +int readline(char *); +void enablevi(void); + +void inithistory(void); +int addhistory(char *); + +/* prompt.c */ +void resetprompt(void); +int prompt(ushort *); + +/* io.c */ +Io *openfd(int fd); +Io *openstr(void); +void terminate(Io *io); + +int get(Io *); +int put(Io **, char); + +void flush(Io *io); +void print(Io *, char *, ...); + +/* lex.c */ +int iswordchar(int c); +int yylex(void); + +/* tree.c */ +Tree *maketree(void); +Tree *maketree1(int, Tree*); +Tree *maketree2(int, Tree*, Tree*); +Tree *maketree3(int, Tree*, Tree*, Tree*); + +Tree *token(int, char *); +Tree *hangchild1(Tree *, Tree *, int); +Tree *hangchild2(Tree *, Tree *, int, Tree *, int); +Tree *hangchild3(Tree *, Tree *, Tree *, Tree *); +Tree *hangepilog(Tree *, Tree*); + +void freeparsetree(void); + +/* sys.c */ +void initenv(void); +void redirect(struct Redir *); +void execute(Word *, Word*); +int mapfd(int fd); + +/* wait.c */ +void addwait(Thread *, int); +void delwait(Thread *, int); +void clearwait(Thread*); + +int waitall(Thread *); +int waitfor(Thread *, int); + +void killzombies(void); + +/* job.c */ +Thread *getjob(int, int*); +void addjob(Thread *); +void deljob(Thread *); +void wakeup(Thread *); +void report(Thread *, int); + +void foreground(Thread *, int); +void background(Thread *, int); + +/* exec.c */ +// XXX: odd place for this +int count(Word *); +Word *makeword(char *str, Word *link); +void freeword(Word *w); + +/* var.c */ +Var *var(char*); +Var *definevar(char*, Var *); +Var *globalvar(char*); +Var *makevar(char *name, Var *link); +void setvar(char *, Word *); +int iskeyword(char *); + +void initpath(void); +void initkeywords(void); + +char **mkenv(void); + +/* code.c */ +int compile(Tree *); +Code *copycode(Code *c); +void freecode(Code *c); diff --git a/src/cmd/rc/rules.mk b/src/cmd/rc/rules.mk new file mode 100644 index 0000000..76837fc --- /dev/null +++ b/src/cmd/rc/rules.mk @@ -0,0 +1,32 @@ +include share/push.mk +# Iterate through subdirectory tree + +# local sources +SRCS_$(d):=\ + $(d)/util.c\ + $(d)/input.c\ + $(d)/prompt.c\ + $(d)/io.c\ + $(d)/lex.c\ + $(d)/parse.c\ + $(d)/tree.c\ + $(d)/code.c\ + $(d)/var.c\ + $(d)/sys.c\ + $(d)/wait.c\ + $(d)/job.c\ + $(d)/exec.c\ + $(d)/main.c + +# local targets +BINS_$(d) := $(d)/rc + +include share/paths.mk +$(d)/parse.h $(d)/parse.c: $(d)/syntax.y + yacc --header=$( line cmds cmdsln body paren block ifbody casebody case assign epilog redir; +%type cmd basic executable nonkeyword keyword word words wordsnl atom; +%type Tfor Tin Twhile Tif Telse Tswitch Tcase Ttwiddle Tbang Tsubshell Tfunc; +%type Tword Tredir Tpipe Tdup; + +/* grammar */ + +%start rc + +%% +rc: +/*empty*/ { return 0; } +| line '\n' { return compile($1); } + +line: + cmd +| cmds line { $$ = maketree2(';', $1, $2); } + +body: + cmd +| cmdsln body { $$ = maketree2(';', $1, $2); } + +paren: + '(' body ')' { $$ = maketree1(Tparen, $2); } + +block: + '{' body '}' { $$ = maketree1(Tblock, $2); } + +cmds: + cmd ';' +| cmd '&' { $$ = maketree1('&', $1); } + +cmdsln: + cmds +| cmd '\n' + +ifbody: + cmd %prec Telse { $$ = maketree2(Tif, nil, $1); } +| block Telse nl cmd { $$ = maketree3(Tif, nil, $1, $2); } + +case: + Tcase words ';' { $$ = hangchild1($1, $2, 0); } +| Tcase words '\n' { $$ = hangchild1($1, $2, 0); } + +casebody: + cmd { $$ = maketree2(Tcasebody, $1, nil); } +| case casebody { $$ = maketree2(Tcasebody, $1, $2); } +| cmdsln casebody { $$ = maketree2(Tcasebody, $1, $2); } + +assign: + executable '=' word { $$ = maketree2('=', $1, $3); } + +redir: + Tdup +| Tredir word { $$ = hangchild1($1, $2, 0); } + +epilog: +/* empty */ { $$ = nil; } +| redir epilog { $$ = hangchild1($1, $2, 1); } + +cmd: +/* empty */ %prec Twhile { $$ = nil; } +| basic { $$ = maketree1(Tbasic, $1); } +| block epilog { $$ = hangepilog($1, $2); } +| cmd Tpipe nl cmd { $$ = hangchild2($2, $1, 0, $4, 1); } +| cmd Tandand nl cmd { $$ = maketree2(Tandand, $1, $4); } +| cmd Toror nl cmd { $$ = maketree2(Toror, $1, $4); } +| redir cmd %prec Tbang { $$ = hangchild1($1, $2, 1); } +| assign cmd %prec Tbang { $$ = hangchild1($1, $2, 2); } +| Tbang cmd { $$ = maketree1(Tbang, $2); } +| Tsubshell cmd { $$ = maketree1(Tsubshell, $2); } +| Tfor '(' word ')' nl cmd { $$ = hangchild3($1, $3, nil, $6); } +| Tfor '(' word Tin words ')' nl cmd { $$ = hangchild3($1, $3, $5, $8); } +| Twhile paren nl cmd { $$ = hangchild2($1, $2, 0, $4, 1); } +| Tif paren nl ifbody { $$ = hangchild1($2, $1, 0); } +| Tswitch '(' word ')' nl '{' casebody '}' { $$ = hangchild2($1, $3, 0, $7, 1); } + +basic: + executable +| basic word { $$ = maketree2(Targs, $1, $2); } +| basic redir { $$ = maketree2(Targs, $1, $2); } + +atom: + nonkeyword +| keyword { $$ = maketree1(Tword, $1); } + +word: + atom +| word '^' atom { $$ = maketree2('^', $1, $3); } + +executable: + nonkeyword +| executable '^' atom { $$ = maketree2('^', $1, $3); } + +nonkeyword: + Tword +| '$' atom { $$ = maketree1('$', $2); } +| '$' atom Tindex words ')' { $$ = maketree2(Tindex, $2, $4); } +| '(' wordsnl ')' { $$ = $2; } +| Tcount atom { $$ = maketree1(Tcount, $2); } +| Tjoin atom { $$ = maketree1(Tjoin, $2); } +| '`' block { $$ = maketree1('`', $2); } +//| Tredir block { $$ = hangchild1($1, $2, 0); $$->type = Tpipefd; } + +keyword: + Tfor|Tin|Twhile|Tif|Telse|Tswitch|Tcase|Tbang|Tsubshell|Tfunc + +words: +/* empty */ { $$ = nil; } +| words word { $$ = maketree2(Twords, $1, $2); } + +wordsnl: +/* empty */ { $$ = nil; } +| wordsnl '\n' /* empty */ +| wordsnl word {$$ = (!$1) ? ((!$2) ? nil : $2) : ((!$2) ? $1 : maketree2(Twords, $1, $2)); } + +nl: +/*empty*/ +| nl '\n' + +%% diff --git a/src/cmd/rc/sys.c b/src/cmd/rc/sys.c new file mode 100644 index 0000000..807359d --- /dev/null +++ b/src/cmd/rc/sys.c @@ -0,0 +1,137 @@ +#include "rc.h" + +// ----------------------------------------------------------------------- +// internal + +static +char** +mkargv(Word *args) +{ + char **argv=emalloc((count(args)+2)*sizeof(char *)); + char **argp=argv+1; /* leave one at front for runcoms */ + + for(;args;args=args->link) + *argp++=args->str; + *argp=nil; + + return argv; +} + +static +Word* +envval(char *s) +{ + Word *v; + char *t, c; + + for(t=s; *t && *t!='\1'; t++) + ; + + c = *t; + *t = '\0'; + + v = makeword(s, (c=='\0') ? nil : envval(t+1)); + *t=c; + + return v; +} + +// ----------------------------------------------------------------------- +// exported + +void +initenv(void) +{ + extern char **environ; + + char *s; + char **env; + + for(env=environ; *env; env++) { + for(s=*env; *s && *s != '(' && *s != '='; s++) + ; + switch(*s){ + case '\0': + break; + case '(': /* ignore functions */ + break; + case '=': + *s = '\0'; + setvar(*env, envval(s+1)); + *s = '='; + break; + } + } +} + +void +execute(Word *cmd, Word *path) +{ + int nc; + char **argv = mkargv(cmd); + char **env = mkenv(); + char file[1024]; + + for(; path; path=path->link){ + nc = strlen(path->str); + if(nc < arrlen(file)){ + strcpy(file, path->str); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1]) < arrlen(file)){ + strcat(file, argv[1]); + execve(file, argv+1, env); + }else + fatal("command name too long"); + } + } + print(shell.err, "could not execute command: %s\n", argv[1]); + efree(argv); +} + +void +redirect(Redir *r) +{ + if(r){ + redirect(r->link); + switch(r->type){ + case Ropen: + if(r->from != r->to){ + dup2(r->from, r->to); + close(r->from); + } + break; + case Rdupfd: + dup2(r->from, r->to); // TODO: error checking + break; + case Rclose: + close(r->from); + break; + default: + fatal("unrecognized redirection type %d\n", r->type); + } + } +} + +int +mapfd(int fd) +{ + Redir *r; + for(r = runner->redir.end; r; r = r->link){ + switch(r->type){ + case Rclose: + if(r->from == fd) + fd = -1; + break; + case Rdupfd: + case Ropen: + if(r->to == fd) + fd = r->from; + break; + } + } + + return fd; +} diff --git a/src/cmd/rc/tree.c b/src/cmd/rc/tree.c new file mode 100644 index 0000000..2c65041 --- /dev/null +++ b/src/cmd/rc/tree.c @@ -0,0 +1,111 @@ +#include "rc.h" +#include "parse.h" + +static Tree *nodes; + +Tree* +maketree(void) +{ + Tree *node = emalloc(sizeof(*node)); + + node->link = nodes; + nodes = node; + return node; +} + +void +freeparsetree(void) +{ + Tree *t, *nt; + for(t = nodes; t; t = nt) { + nt = t->link; + if(t->type == Tword && t->str) + efree(t->str); + efree(t); + } + nodes = nil; +} + +Tree* +maketree1(int type, Tree *c0) +{ + return maketree2(type, c0, nil); +} + +Tree* +maketree2(int type, Tree *c0, Tree *c1) +{ + return maketree3(type, c0, c1, nil); +} + +Tree* +maketree3(int type, Tree *c0, Tree *c1, Tree *c2) +{ + Tree *node = maketree(); + + node->type = type; + node->child[0] = c0; + node->child[1] = c1; + node->child[2] = c2; + + return node; +} + +Tree* +hangchild1(Tree *node, Tree *c, int i) +{ + node->child[i] = c; + + return node; +} + +Tree* +hangchild2(Tree *node, Tree *c1, int i1, Tree *c2, int i2) +{ + node->child[i1] = c1; + node->child[i2] = c2; + + return node; +} + +Tree* +hangchild3(Tree *node, Tree *c0, Tree *c1, Tree *c2) +{ + node->child[0] = c0; + node->child[1] = c1; + node->child[2] = c2; + + return node; +} + +Tree* +hangepilog(Tree *cmd, Tree *epi) +{ + Tree *p; + if(!epi) + return cmd; + for(p = epi; p->child[1]; p = p->child[1]) + ; + + p->child[1] = cmd; + return epi; +} + +Tree* +token(int type, char *s) +{ + Tree *node = maketree(); + + node->type = type; + node->str = strdup(s); + + return node; +} + +/* +Tree* +basic(Tree *node) +{ + return maketree1(Tbasic, node); +} +*/ diff --git a/src/cmd/rc/util.c b/src/cmd/rc/util.c new file mode 100644 index 0000000..b0be788 --- /dev/null +++ b/src/cmd/rc/util.c @@ -0,0 +1,65 @@ +#include "rc.h" + +void +fatal(char *msg, ...) +{ + va_list args; + vfprintf(stderr, msg, args); + va_end(args); + + abort(); +} + +void* +emalloc(uintptr n) +{ + void *p; + if(!(p = malloc(n))) + fatal("out of memory: can't allocate %d bytes", n); + + memset(p, 0, n); + return p; +} + +void* +erealloc(void *p, uintptr n) +{ + void *r; + if(!(r = realloc(p,n))) + fatal("out of memory: can't reallocate %d bytes", n); + + return r; +} + +void +efree(void *p) +{ + if(p) + free(p); + // TODO: log the double free +} + + +char *bp; + +static +void +iacvt(int n) +{ + if(n<0){ + *bp++='-'; + n=-n; /* doesn't work for n==-inf */ + } + if(n/10) + iacvt(n/10); + + *bp++=n%10+'0'; +} + +void +itoa(char *s, long n) +{ + bp = s; + iacvt(n); + *bp='\0'; +} diff --git a/src/cmd/rc/var.c b/src/cmd/rc/var.c new file mode 100644 index 0000000..3e9635f --- /dev/null +++ b/src/cmd/rc/var.c @@ -0,0 +1,336 @@ +#include "rc.h" +#include "parse.h" + +// TODO: string interning + +// ----------------------------------------------------------------------- +// globals + +struct Keyword +{ + char *name; + int type; +}; + +static Var *globals[512]; +static struct Keyword keywords[100]; // sparse map means less hits + +// ----------------------------------------------------------------------- +// internals + +static +int +hash(char *s, int len) +{ + int h =0, i = 1; + while(*s) + h += *s++*i++; + + h %= len; + return h < 0 ? h+len : h; +} + +static +void +·setvar(char *name, Word *val, int call) +{ + Var *v = var(name); + freeword(v->val); + + v->val = val; + v->new = 1; // this never turns off? + + if(call && v->update) + v->update(v); +} + +static +char* +list2strcolon(Word *words) +{ + char *value, *s, *t; + int len = 0; + Word *ap; + for(ap = words;ap;ap = ap->link) + len+=1+strlen(ap->str); + + value = emalloc(len+1); + + s = value; + for(ap = words; ap; ap = ap->link){ + for(t = ap->str;*t;) *s++=*t++; + *s++=':'; + } + + if(s==value) + *s='\0'; + else + s[-1]='\0'; + + return value; +} + +static +void +littlepath(Var *v) +{ + /* convert $path to $PATH */ + char *p; + Word *w; + + p = list2strcolon(v->val); + w = emalloc(sizeof(*w)); + w->str = p; + w->link = nil; + + ·setvar("PATH", w, 1); +} + +static +void +bigpath(Var *v) +{ + /* convert $PATH to $path */ + char *p, *q; + Word **l, *w; + + if(v->val == nil){ + ·setvar("path", nil, 0); + return; + } + + p = v->val->str; + w = nil; + l = &w; + + /* Doesn't handle escaped colon nonsense. */ + if(p[0] == 0) + p = nil; + + while(p){ + q = strchr(p, ':'); + if(q) + *q = 0; + + *l = makeword(p[0] ? p : ".", nil); + l = &(*l)->link; + + if(q){ + *q = ':'; + p = q+1; + }else + p = nil; + } + ·setvar("path", w, 0); +} + +// ----------------------------------------------------------------------- +// exports + +Var* +makevar(char *name, Var *link) +{ + Var *v = emalloc(sizeof(*v)); + + v->name = name; + v->val = 0; + v->new = 0; + v->newfunc = 0; + v->link = link; + v->func = nil; + v->update = nil; + + return v; +} + +void +setvar(char *name, Word *val) +{ + ·setvar(name, val, 1); +} + +Var* +definevar(char *name, Var *link) +{ + Var *v = emalloc(sizeof(*v)); + + v->name = name; + v->val = 0; + v->link = link; + + return v; +} + +Var* +globalvar(char *name) +{ + int h; + Var *v; + + h = hash(name, arrlen(globals)); + + if(strcmp(name,"PATH")==0){ + flush(shell.err); + } + + for(v = globals[h]; v; v = v->link){ + if(strcmp(v->name, name) == 0){ + return v; + } + } + + return globals[h] = definevar(strdup(name), globals[h]); +} + +Var* +var(char *name) +{ + Var *v; + if(runner){ + for(v = runner->local; v; v=v->link) + if(strcmp(v->name, name) == 0) + return v; + } + return globalvar(name); +} + +static +int +cmpenv(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} + +char** +mkenv(void) +{ + char **env, **ep, *p, *q; + Var **h, *v; + Word *a; + int nvar=0, nchr=0, sep; + +#define BODY \ + if((v==var(v->name)) && v->val){ \ + nvar++; \ + nchr+=strlen(v->name)+1; \ + for(a=v->val;a;a=a->link) \ + nchr+=strlen(a->str)+1; \ + } + + for(v= runner->local; v; v=v->link){ + BODY + } + for(h=globals; h!=arrend(globals); h++){ + for(v = *h; v; v=v->link){ + BODY + } + } + +#undef BODY + + env=emalloc((nvar+1)*sizeof(*env)+nchr); + ep=env; + p=(char *)&env[nvar+1]; + +#define BODY \ + if((v==var(v->name)) && v->val){ \ + *ep++=p; \ + q=v->name; \ + while(*q) \ + *p++=*q++; \ + sep='='; \ + for(a=v->val;a;a=a->link){ \ + *p++=sep; \ + sep='\1'; \ + q=a->str; \ + while(*q) \ + *p++=*q++; \ + } \ + *p++='\0'; \ + } + + for(v=runner->local; v; v=v->link){ + BODY + } + for(h=globals; h!=arrend(globals); h++){ + for(v = *h; v; v=v->link){ + BODY + } + } +#undef BODY + + *ep=0; + + qsort((char *)env, nvar, sizeof ep[0], cmpenv); + return env; +} + +void +initpath(void) +{ + Var *v; + + v = globalvar("path"); + v->update = littlepath; + + v = globalvar("PATH"); + v->update = bigpath; + + flush(shell.err); + bigpath(v); +} + +#define KEYWORDS \ + KEYWORD("for", Tfor) \ + KEYWORD("in", Tin) \ + KEYWORD("while", Twhile) \ + KEYWORD("if", Tif) \ + KEYWORD("else", Telse) \ + KEYWORD("switch", Tswitch) \ + KEYWORD("case", Tcase) \ + KEYWORD("!", Tbang) \ + KEYWORD("@", Tsubshell) \ + KEYWORD("func", Tfunc) + +void +initkeywords(void) +{ + int i, s, j, h; +#define KEYWORD(a, b) a, + static char *name[] = { KEYWORDS }; +#undef KEYWORD +#define KEYWORD(a, b) b, + static int type[] = { KEYWORDS }; +#undef KEYWORD + + for(i = 0; i < arrlen(type); i++){ + h = hash(name[i], arrlen(keywords)); + for(s=0; s < arrlen(keywords); s++){ + j = (h + s) % arrlen(keywords); + if(!keywords[j].type || strcmp(keywords[j].name, name[i]) == 0){ + keywords[j].name = name[i]; + keywords[j].type = type[i]; + goto nextkeyword; + } + } + nextkeyword:; + } +} + +int +iskeyword(char *word) +{ + int i, s, h; + + h = hash(word, arrlen(keywords)); + for(s = 0; s < arrlen(keywords); s++){ + i = (h + s) % arrlen(keywords); + if(!keywords[i].type) + return 0; + if(strcmp(keywords[i].name, word) == 0) + return keywords[i].type; + } + return 0; +} + +#undef KEYWORDS diff --git a/src/cmd/rc/wait.c b/src/cmd/rc/wait.c new file mode 100644 index 0000000..911601c --- /dev/null +++ b/src/cmd/rc/wait.c @@ -0,0 +1,247 @@ +#include "rc.h" + +#include + +// ----------------------------------------------------------------------- +// globals + +struct WaitMsg +{ + int pid; + int type; + ulong time[3]; + int status; +}; + +// ----------------------------------------------------------------------- +// internal + +static +int +await(int pid4, int opt, struct WaitMsg *msg) +{ + int pid, status, core; + struct rusage ru; + ulong u, s; + + /* event loop */ + for(;;){ + if((pid = wait4(pid4, &status, opt, &ru)) <= 0){ + if(errno == ECHILD){ + msg->pid = -1; + return 1; + } + msg->pid = 0; + perror("failed wait4"); + return 0; + } + + u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000); + s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000); + + if(WIFEXITED(status)){ + msg->pid = pid; + msg->time[0] = u; + msg->time[1] = s; + msg->time[2] = u+s; + msg->status = WEXITSTATUS(status); + msg->type = Pdone; + + return 1; + } + + if(WIFSIGNALED(status)){ + msg->pid = pid; + msg->time[0] = u; + msg->time[1] = s; + msg->time[2] = u+s; + msg->status = WTERMSIG(status); + msg->type = Psig; + + return 1; + } + + if(WIFSTOPPED(status)){ + msg->pid = pid; + msg->time[0] = u; + msg->time[1] = s; + msg->time[2] = u+s; + msg->status = WSTOPSIG(status); + msg->type = Pstop; + + return 1; + } + } +} + +static +int +shouldwait(Thread *job) +{ + int i; + + for(i=0; iwait.len; i++){ + if(job->wait.on[i].status == Prun) + return 1; + } + + return 0; +} + +static inline +void +notify(Thread *job, struct WaitMsg msg) +{ + int i; + for(i=0; i < job->wait.len; i++){ + if(job->wait.on[i].pid == msg.pid){ + job->status = msg.status; + switch(msg.type){ + case Pstop: + print(shell.err, "%d: suspended\n", msg.pid); + job->wait.status = Pstop; + job->wait.on[i].status = Pstop; + break; + + case Psig: + print(shell.err, "%d: terminated by signal %d\n", msg.pid, msg.status); + /* fallthrough */ + case Pdone: + job->wait.on[i].status = Pdone; + delwait(job, msg.pid); + if(!job->wait.len) + job->wait.status = Pdone; + break; + + default: + fatal("%d: unrecognized message type %d\n", msg.pid, msg.type); + } + break; + } + } +} + +// ----------------------------------------------------------------------- +// exported + +void +clearwait(Thread *job) +{ + job->wait.len = 0; +} + +int +havewait(Thread *job, int pid) +{ + int i; + + for(i=0; iwait.len; i++) + if(job->wait.on[i].pid == pid) + return 1; + return 0; +} + +void +addwait(Thread *job, int pid) +{ + if(job->wait.len == job->wait.cap){ + job->wait.cap = job->wait.cap + 2; + job->wait.on = erealloc(job->wait.on, job->wait.cap*sizeof(*job->wait.on)); + } + + job->wait.on[job->wait.len++] = (struct WaitItem){.pid=pid, .status=Prun}; +} + +void +delwait(Thread *job, int pid) +{ + int r, w; + + for(r=w=0; r < job->wait.len; r++){ + if(job->wait.on[r].pid != pid) + job->wait.on[w++].pid = job->wait.on[r].pid; + } + job->wait.len = w; +} + +int +waitall(Thread *job) +{ + int i; + Thread *t; + struct WaitMsg msg; + + while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ + switch(msg.pid){ + case 0: // error + perror("wait job"); + return 0; + case -1: // no children: assume they have exited + job->wait.status = Pdone; + clearwait(job); + return 1; + default: + ; + } + + notify(job, msg); + } + return 1; +} + +int +waitfor(Thread *job, int pid) +{ + int i; + Thread *t; + struct WaitMsg msg; + + while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ + switch(msg.pid){ + case 0: // error + perror("wait for"); + return 0; + case -1: // no children: assume they have exited + job->wait.status = Pdone; + clearwait(job); + return 1; + default: + ; + } + + notify(job, msg); + /* allow for an early exit */ + if(msg.pid == pid) + return 1; + } + return 1; + +} + +void +killzombies(void) +{ + Thread *job; + int index, status, pid; + + while((pid=waitpid(-1, &status, WNOHANG))>0){ + print(shell.err, "found zombie pid %d\n", pid); + flush(shell.err); + + job = getjob(pid, &index); + if(!job) + perror("invalid pid"); + + if(WIFEXITED(status)) + job->wait.status = Pdone; + if(WIFSTOPPED(status)) + job->wait.status = Pstop; + if(WIFCONTINUED(status)) + job->wait.status = Pagain; + + if(job->wait.status == Pdone){ + report(job,index); + deljob(job); + } + } +} diff --git a/src/cmd/rules.mk b/src/cmd/rules.mk new file mode 100644 index 0000000..72cd0ce --- /dev/null +++ b/src/cmd/rules.mk @@ -0,0 +1,32 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# DIR := $(d)/cc +# include $(DIR)/rules.mk + +DIR := $(d)/rc +include $(DIR)/rules.mk + +DIR := $(d)/walk +include $(DIR)/rules.mk + +DIR := $(d)/filter +include $(DIR)/rules.mk + +DIR := $(d)/ic +include $(DIR)/rules.mk + +DIR := $(d)/dwm +include $(DIR)/rules.mk + +DIR := $(d)/menu +include $(DIR)/rules.mk + +DIR := $(d)/term +include $(DIR)/rules.mk + +# DIR := $(d)/wm +# include $(DIR)/rules.mk + +include share/pop.mk diff --git a/src/cmd/term/LICENSE b/src/cmd/term/LICENSE new file mode 100644 index 0000000..c356c39 --- /dev/null +++ b/src/cmd/term/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2018 Hiltjo Posthuma +© 2018 Devin J. Pohly +© 2014-2017 Quentin Rameau +© 2009-2012 Aurélien APTEL +© 2008-2017 Anselm R Garbe +© 2012-2017 Roberto E. Vargas Caballero +© 2012-2016 Christoph Lohmann <20h at r-36 dot net> +© 2013 Eon S. Jeon +© 2013 Alexander Sedov +© 2013 Mark Edgar +© 2013-2014 Eric Pruitt +© 2013 Michael Forney +© 2013-2014 Markus Teich +© 2014-2015 Laslo Hunhold + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/cmd/term/config.h b/src/cmd/term/config.h new file mode 100644 index 0000000..a740ecf --- /dev/null +++ b/src/cmd/term/config.h @@ -0,0 +1,474 @@ +/* See LICENSE file for copyright and license details. */ +#define VERSION "1" + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "consolas:size=16"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/mksh"; +char *utmp = nil; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = nil; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static uint doubleclicktimeout = 300; +static uint tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static uint blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static uint cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "term-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +uint tabspaces = 4; + +/* bg opacity */ +float alpha = 0.98; + +/* Terminal colors (16 first used in escape sequence) */ +static char *colorname[] = { + "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ + "#ea6962", /* red */ + "#a9b665", /* green */ + "#d8a657", /* yellow */ + "#7daea3", /* blue */ + "#d3869b", /* magenta */ + "#89b482", /* cyan */ + "#d4be98", /* white */ + + "#928374", /* black */ + "#ef938e", /* red */ + "#bbc585", /* green */ + "#e1bb7e", /* yellow */ + "#9dc2ba", /* blue */ + "#e1acbb", /* magenta */ + "#a7c7a2", /* cyan */ + "#e2d3ba", /* white */ + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#fbf1c7", + "#3c3836", + "#555555", +}; + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +uint defaultfg = 256; +uint defaultbg = 257; +static uint defaultcs = 15; +static uint defaultrcs = 258; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static uint cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static uint cols = 80; +static uint rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static uint mouseshape = XC_left_ptr; +static uint mousefg = 0; +static uint mousebg = 7; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static uint defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_plus, zoom, {.f = +1} }, + { ControlMask, XK_minus, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SelRectangular] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/src/cmd/term/hb.c b/src/cmd/term/hb.c new file mode 100644 index 0000000..4b6b42d --- /dev/null +++ b/src/cmd/term/hb.c @@ -0,0 +1,147 @@ +#include "term.h" + +#include +#include + +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } + +hb_font_t *hbfindfont(XftFont *match); +void hbtransformsegment(XftFont *xfont, const Letter *glyph, rune *codepoints, int start, int end); + +typedef struct +{ + XftFont *match; + hb_font_t *font; +} HbFontMatch; + +static int hbfontslen = 0; +static HbFontMatch *hbfontcache = nil; + +/* + * Replace 0 with a list of font features, wrapped in FEATURE macro, e.g. + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') + */ +hb_feature_t features[] = { 0 }; + +void +hbunloadfonts() +{ + int i; + for(i = 0; i < hbfontslen; i++) { + hb_font_destroy(hbfontcache[i].font); + XftUnlockFace(hbfontcache[i].match); + } + + if(hbfontcache != nil) { + free(hbfontcache); + hbfontcache = nil; + } + + hbfontslen = 0; +} + +hb_font_t * +hbfindfont(XftFont *match) +{ + int i; + for (i = 0; i < hbfontslen; i++) { + if (hbfontcache[i].match == match) + return hbfontcache[i].font; + } + + /* Font not found in cache, caching it now. */ + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); + FT_Face face = XftLockFace(match); + hb_font_t *font = hb_ft_font_create(face, NULL); + if(!font) + fatal("failed to load Harfbuzz font."); + + hbfontcache[hbfontslen].match = match; + hbfontcache[hbfontslen].font = font; + hbfontslen += 1; + + return font; +} + +void +hbtransform(XftGlyphFontSpec *specs, const Letter *glyphs, size_t len, int x, int y) +{ + int idx, specidx, start = 0, length = 1, gstart = 0; + rune *runes = calloc((unsigned int)len, sizeof(hb_codepoint_t)); + + for(idx = 1, specidx = 1; idx < len; idx++) { + if(glyphs[idx].mode & Gwdummy) { + length += 1; + continue; + } + + if(specs[specidx].font != specs[start].font + || GLYPHCMP(glyphs[gstart], glyphs[idx]) + || selected(x + idx, y) != selected(x + gstart, y) + ) { + hbtransformsegment(specs[start].font, glyphs, runes, gstart, length); + /* reset the sequence. */ + length = 1; + start = specidx; + gstart = idx; + } else { + length += 1; + } + + specidx++; + } + + /* eol */ + hbtransformsegment(specs[start].font, glyphs, runes, gstart, length); + + /* apply the transformation to glyph specs. */ + for(idx = 0, specidx = 0; idx < len; idx++) { + if(glyphs[idx].mode & Gwdummy) + continue; + + if(runes[idx] != specs[specidx].glyph) + ((Letter *)glyphs)[idx].mode |= Gliga; + + specs[specidx++].glyph = runes[idx]; + } + + free(runes); +} + +void +hbtransformsegment(XftFont *xfont, const Letter *glyph, rune *codepoints, int start, int len) +{ + hb_font_t *font = hbfindfont(xfont); + if(!font) + return; + + int i; + rune r; + ushort mode = USHRT_MAX; + hb_buffer_t *buffer = hb_buffer_create(); + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); + + /* Fill buffer with codepoints. */ + for(i=start; i < (start+len); i++) { + r = glyph[i].u; + mode = glyph[i].mode; + if(mode & Gwdummy) + r = 0x0020; + hb_buffer_add_codepoints(buffer, &r, 1, 0, 1); + } + + /* Shape the segment. */ + hb_shape(font, buffer, features, sizeof(features)); + + /* Get new glyph info. */ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); + + /* Write new codepoints. */ + for(i = 0; i < len; i++) { + r = info[i].codepoint; + codepoints[start+i] = r; + } + + /* Cleanup. */ + hb_buffer_destroy(buffer); +} diff --git a/src/cmd/term/nonspacing.h b/src/cmd/term/nonspacing.h new file mode 100644 index 0000000..5d05a3d --- /dev/null +++ b/src/cmd/term/nonspacing.h @@ -0,0 +1,89 @@ +16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35, +36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50, +51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66, +67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83, +84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,248,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,254,255,255,255,255,191,182,0,0,0,0,0,0,0,63,0,255,23,0,0,0,0,0,248,255, +255,0,0,1,0,0,0,0,0,0,0,0,0,0,0,192,191,159,61,0,0,0,128,2,0,0,0,255,255,255, +7,0,0,0,0,0,0,0,0,0,0,192,255,1,0,0,0,0,0,0,248,15,32,0,0,192,251,239,62,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255, +255,7,0,0,0,0,0,0,20,254,33,254,0,12,0,0,0,2,0,0,0,0,0,0,16,30,32,0,0,12,0,0, +64,6,0,0,0,0,0,0,16,134,57,2,0,0,0,35,0,6,0,0,0,0,0,0,16,190,33,0,0,12,0,0, +252,2,0,0,0,0,0,0,144,30,32,64,0,12,0,0,0,4,0,0,0,0,0,0,0,1,32,0,0,0,0,0,0,17, +0,0,0,0,0,0,192,193,61,96,0,12,0,0,0,2,0,0,0,0,0,0,144,64,48,0,0,12,0,0,0,3,0, +0,0,0,0,0,24,30,32,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,4,92,0,0,0,0,0,0,0,0,0,0,0, +242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160, +2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0, +0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0, +0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127, +229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0, +0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, +251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, +255,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,128,3,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0, +0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0, +0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0, +0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,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,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0, +0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255, +127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0, +0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192, +31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0, +0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,60,176,1,0,0,48,0,0,0, +0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0, +0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0, +126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0, +0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0, +0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,24, +0,0,0,0,0,0,0,255,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,31,0,0,0,0,0,0,0,127,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0, +0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248, +254,255,0,0,0,0,0,0,0,0,0, +0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/src/cmd/term/rules.mk b/src/cmd/term/rules.mk new file mode 100644 index 0000000..76701ec --- /dev/null +++ b/src/cmd/term/rules.mk @@ -0,0 +1,26 @@ +include share/push.mk + +# local sources +SRCS_$(d) := $(d)/term.c $(d)/x.c #$(d)/hb.c + +# local outputs +BINS_$(d) := $(d)/term + +include share/paths.mk + +# Local rules +include share/dynamic.mk + +$(BINS_$(d)): TCFLAGS=\ + `$(PKG) --cflags fontconfig`\ + `$(PKG) --cflags freetype2` + +$(BINS_$(d)): TCLIBS=\ + `$(PKG) --libs fontconfig`\ + `$(PKG) --libs freetype2`\ + -lm -lrt -lX11 -lutil -lXft -lXrender #-lharfbuzz + +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libutf/libutf.a $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/term/term.c b/src/cmd/term/term.c new file mode 100644 index 0000000..50ab29c --- /dev/null +++ b/src/cmd/term/term.c @@ -0,0 +1,2417 @@ +/* See LICENSE for license details. */ +#include "term.h" + +#include +#include +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +/* forward declare functions */ +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(int *, int); +static void tsetchar(rune, Letter *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, int *, int); +static int twrite(char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32 tdefcolor(int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static char *base64dec(char *); +static char base64dec_getc(char **); + +static uintptr xwrite(int, char *, size_t); +extern int wcwidth(wchar_t wc); + +/* globals */ +static Terminal term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +/* functions */ +uintptr +xwrite(int fd, char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + fatal("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == nil) + fatal("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(char *s) +{ + if ((s = strdup(s)) == nil) + fatal("strdup: %s\n", strerror(errno)); + + return s; +} + +static char base64_digits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +char +base64dec_getc(char **src) +{ + while (**src && !isprint(**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SelIdle; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (term.line[y][i - 1].mode & Gwrap) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SelEmpty; + sel.type = SelRegular; + sel.alt = IS_SET(Taltscreen); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SelReady; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SelIdle) + return; + if (done && sel.mode == SelEmpty) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SelEmpty) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SelIdle : SelReady; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SelRegular && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SelRectangular) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if(sel.mode == SelEmpty || sel.ob.x == -1 || + sel.alt != IS_SET(Taltscreen)) + return 0; + + if(sel.type == SelRectangular) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + Letter *gp, *prevgp; + + switch (sel.snap) { + case SnapWord: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &term.line[*y][*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(term.line[yt][xt].mode & Gwrap)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[newy][newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & Gwdummy) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SnapLine: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & Gwrap)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & Gwrap)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + Letter *gp, *last; + + if (sel.ob.x == -1) + return nil; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTFmax; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for(y = sel.nb.y; y <= sel.ne.y; y++) { + if((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if(sel.type == SelRectangular) { + gp = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + }else{ + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & Gwdummy) + continue; + + ptr += utf8·encode(&gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & Gwrap) || sel.type == SelRectangular)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SelIdle; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +fatal(char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == nil) { + if (errno) + fatal("getpwuid: %s\n", strerror(errno)); + else + fatal("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == nil) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = nil; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = nil; + } else { + prog = sh; + arg = nil; + } + DEFAULT(args, ((char *[]) {prog, arg, nil})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + fatal("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + fatal("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + fatal("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + fatal("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + fatal("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(char *line, char *cmd, char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= Tprint; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + fatal("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, nil, nil, nil) < 0) + fatal("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + fatal("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, nil) < 0) + fatal("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + close(s); + close(m); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", nil) == -1) + fatal("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", nil) == -1) + fatal("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, arrlen(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + fatal("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if(buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(char *s, size_t n, int may_echo) +{ + char *next; + + if (may_echo && IS_SET(Techo)) + twrite(s, n, 1); + + if (!IS_SET(Tcrlf)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, nil, nil, nil) < 0) { + if (errno == EINTR) + continue; + fatal("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + fatal("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup() +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static Dot c[2]; + int alt = IS_SET(Taltscreen); + + if (mode == CursorSave) { + c[alt] = term.c; + } else if (mode == CursorLoad) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (Dot){{ + .mode = Gnil, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CursorDefault}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = Twrap|Tutf8; + memset(term.trantbl, CSusa, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CursorSave); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Terminal){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Letter **tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= Taltscreen; + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Letter *temp; + + LIMIT(n, 0, term.bot-orig+1); + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Letter *temp; + + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = nil; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CursorOrigin) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CursorOrigin) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CursorWrap; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(rune u, Letter *attr, int x, int y) +{ + static char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CSgfx0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8·decode(vt100_0[u - 0x41], &u); + + if (term.line[y][x].mode & Gwide) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~Gwdummy; + } + } else if (term.line[y][x].mode & Gwdummy) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~Gwide; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Letter *gp; + + if(x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if(y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for(y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for(x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if(selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Letter *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Letter)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Letter *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Letter)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc(38): bad rgb color (%u,%u,%u)\n", r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad color %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + Gbold | + Gfaint | + Gitalic | + Gunline | + Gblink | + Greverse | + Ginvisible | + Gstruck ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= Gbold; + break; + case 2: + term.c.attr.mode |= Gfaint; + break; + case 3: + term.c.attr.mode |= Gitalic; + break; + case 4: + term.c.attr.mode |= Gunline; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= Gblink; + break; + case 7: + term.c.attr.mode |= Greverse; + break; + case 8: + term.c.attr.mode |= Ginvisible; + break; + case 9: + term.c.attr.mode |= Gstruck; + break; + case 22: + term.c.attr.mode &= ~(Gbold | Gfaint); + break; + case 23: + term.c.attr.mode &= ~Gitalic; + break; + case 24: + term.c.attr.mode &= ~Gunline; + break; + case 25: + term.c.attr.mode &= ~Gblink; + break; + case 27: + term.c.attr.mode &= ~Greverse; + break; + case 28: + term.c.attr.mode &= ~Ginvisible; + break; + case 29: + term.c.attr.mode &= ~Gstruck; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, int *args, int narg) +{ + int alt, *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, Wappcursor); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, Wreverse); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CursorOrigin); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, Twrap); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, Whide); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, Wmouse); + xsetmode(set, Wmousex10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, Wmouse); + xsetmode(set, Wmousebtn); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, Wmouse); + xsetmode(set, Wmousemotion); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, Wmouse); + xsetmode(set, Wmousemany); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, Wfocus); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, Wmousesgr); + break; + case 1034: + xsetmode(set, W8bit); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CursorSave : CursorLoad); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(Taltscreen); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CursorSave : CursorLoad); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, Wbrcktpaste); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, Wkbdblock); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, Tinsert); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, Techo); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, Tcrlf); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* fatal(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~Tprint; + break; + case 5: + term.mode |= Tprint; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR – Device Status Report (cursor position) */ + if (csiescseq.arg[0] == 6) { + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CursorSave); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CursorLoad); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +strhandle(void) +{ + char *p = nil, *dec; + int j, narg, par; + + term.esc &= ~(Xstrend|Xstr); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + case 1: + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset, here p = nil */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) + return; /* color reset without parameter */ + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + redraw(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + term.mode |= Xdcs; + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(Arg *arg) +{ + term.mode ^= Tprint; +} + +void +printscreen(Arg *arg) +{ + tdump(); +} + +void +printsel(Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTFmax]; + Letter *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8·encode(&bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= Tutf8; + else if (ascii == '@') + term.mode &= ~Tutf8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CSgfx0, CSusa}; + char *p; + + if ((p = strchr(cs, ascii)) == nil) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + strreset(); + + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + term.esc |= Xdcs; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strescseq.type = c; + term.esc |= Xstr; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(Tcrlf)); + return; + case '\a': /* BEL */ + if (term.esc & Xstrend) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(Xcsi|Xaltcs|Xtest); + term.esc |= Xstart; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(Xstrend|Xstr); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= Xcsi; + return 0; + case '#': + term.esc |= Xtest; + return 0; + case '%': + term.esc |= Xutf8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= Xaltcs; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, Wappkeypad); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, Wappkeypad); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CursorSave); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CursorLoad); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & Xstrend) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(rune u) +{ + char c[UTFmax]; + int control; + int width, len; + rune nu; + Letter *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(Tutf8 | Tsixel)) { + c[0] = u; + width = len = 1; + } else { + len = utf8·encode(&u, c); + if(!control && (width = wcwidth(u)) == -1) + width = 1; + } + + /* combining characters */ + if(!width){ + if(term.c.x > 0) + gp = &term.line[term.c.y][term.c.x-1]; + else if(term.c.y > 0) + gp = &term.line[term.c.y-1][term.col-1]; + else + return; + +#if 0 + if(!hb_unicode_compose(hb_unicode_funcs_get_default(),gp->u, u, &nu)) { + return; + } +#endif + + gp->u = nu; + return; + } + + if (IS_SET(Tprint)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if(term.esc & Xstr) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(Xstart|Xstr|Xdcs); + if (IS_SET(Tsixel)) { + /* TODO: render sixel */; + term.mode &= ~Tsixel; + return; + } + term.esc |= Xstrend; + goto check_control_code; + } + + if(IS_SET(Tsixel)) { + /* TODO: implement sixel mode */ + return; + } + if (term.esc&Xdcs && strescseq.len == 0 && u == 'q') + term.mode |= Tsixel; + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if(strescseq.siz > (SIZE_MAX - UTFmax) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if(control) { + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if(term.esc & Xstart) { + if (term.esc & Xcsi) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & Xutf8) { + tdefutf8(u); + } else if (term.esc & Xaltcs) { + tdeftran(u); + } else if (term.esc & Xtest) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + + if(selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if(IS_SET(Twrap) && (term.c.state & CursorWrap)) { + gp->mode |= Gwrap; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if(IS_SET(Tinsert) && term.c.x+width < term.col) + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Letter)); + + if(term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if(width == 2) { + gp->mode |= Gwrap; + if (term.c.x+1 < term.col) { + gp[1].u = '\0'; + gp[1].mode = Gwdummy; + } + } + if(term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + }else{ + term.c.state |= CursorWrap; + } +} + +int +twrite(char *buf, int buflen, int show_ctrl) +{ + int charsize; + rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if(IS_SET(Tutf8) && !IS_SET(Tsixel)) { + /* process a complete utf8 char */ + charsize = utf8·decode(buf + n, &u); + if(charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if(show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + Dot c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not nil */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Letter*)); + memmove(term.alt, term.alt + i, row * sizeof(Letter*)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Letter*)); + term.alt = xrealloc(term.alt, row * sizeof(Letter*)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Letter)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Letter)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Letter)); + term.alt[i] = xmalloc(col * sizeof(Letter)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CursorLoad); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(nil); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & Gwdummy) + term.ocx--; + if (term.line[term.c.y][cx].mode & Gwdummy) + cx--; + + drawregion(0, 0, term.col, term.row); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx], + term.line[term.ocy], term.col + ); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/src/cmd/term/term.h b/src/cmd/term/term.h new file mode 100644 index 0000000..6784974 --- /dev/null +++ b/src/cmd/term/term.h @@ -0,0 +1,316 @@ +/* See LICENSE for license details. */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +// ----------------------------------------------------------------------- +// macros + +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define GLYPHCMP(a, b) (((a).mode & (~Gwrap) & (~Gliga)) != ((b).mode & (~Gwrap) & (~Gliga)) || \ + (a).fg != (b).fg || (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) + +#define iota(x) 1 << (x) + +/* arbitrary sizes */ +#define ESC_BUF_SIZ (128*UTFmax) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +// ----------------------------------------------------------------------- +// constants + +enum { + Gnil, + Gbold = iota(0), + Gfaint = iota(1), + Gitalic = iota(2), + Gunline = iota(3), + Gblink = iota(4), + Greverse = iota(5), + Ginvisible = iota(6), + Gstruck = iota(7), + Gwrap = iota(8), + Gwide = iota(9), + Gwdummy = iota(10), + Gliga = iota(11), + Gboldfaint = Gbold | Gfaint, +}; + +enum { + SelIdle = 0, + SelEmpty = 1, + SelReady = 2 +}; + +enum { + SelRegular = 1, + SelRectangular = 2 +}; + +enum { + SnapWord = 1, + SnapLine = 2 +}; + +/* cursor state */ +enum { + CursorSave, + CursorLoad +}; + +/* cursor mode */ +enum { + CursorDefault = 0, + CursorWrap = 1, + CursorOrigin = 2 +}; + +/* character set */ +enum { + CSgfx0, + CSgfx1, + CSuk, + CSusa, + CSmulti, + CSger, + CSfin, +}; + +/* escape sequences */ +enum { + Xstart = 1, + Xcsi = 2, + Xstr = 4, /* OSC, PM, APC */ + Xaltcs = 8, + Xstrend = 16, /* a final string was encountered */ + Xtest = 32, /* Enter in test mode */ + Xutf8 = 64, + Xdcs =128, +}; + +/* terminal mode */ +enum { + Twrap = iota(0), + Tinsert = iota(1), + Taltscreen = iota(2), + Tcrlf = iota(3), + Techo = iota(4), + Tprint = iota(5), + Tutf8 = iota(6), + Tsixel = iota(7), +}; + +/* window mode */ +enum { + Wvisible = iota(0), + Wfocused = iota(1), + Wappkeypad = iota(2), + Wmousebtn = iota(3), + Wmousemotion = iota(4), + Wreverse = iota(5), + Wkbdblock = iota(6), + Whide = iota(7), + Wappcursor = iota(8), + Wmousesgr = iota(9), + W8bit = iota(10), + Wblink = iota(11), + Wbflink = iota(12), + Wfocus = iota(13), + Wmousex10 = iota(14), + Wmousemany = iota(15), + Wbrcktpaste = iota(16), + Wnumlock = iota(17), + Wmouse = Wmousebtn|Wmousemotion|Wmousex10|Wmousemany, +}; + + +// ----------------------------------------------------------------------- +// types + +/* term.c */ +typedef struct Letter Letter; +typedef struct Dot Dot; +typedef struct Selection Selection; +typedef struct Terminal Terminal; + +typedef union Arg Arg; + +struct Letter { + rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32 fg; /* foreground */ + uint32 bg; /* background */ +}; + +struct Dot { + Letter attr; /* current char attributes */ + int x; + int y; + char state; +}; + +struct Selection { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +}; + +/* Internal representation of the screen */ +struct Terminal { + int row; /* nb row */ + int col; /* nb col */ + Letter **line; /* screen */ + Letter **alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + Dot c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4];/* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + rune lastc; /* last printed char outside of sequence, 0 if control */ +}; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + ulong len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +/* x.c */ +typedef struct TermWindow TermWindow; + +struct TermWindow { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int hb, vb; /* horizontal and vertical border (in pix) */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +}; + +/* used for user hooks */ +union Arg { + int i; + uint ui; + float f; + void *v; + char *s; +}; + +// ----------------------------------------------------------------------- +// x.c (backend functions) + +void xbell(void); +void xclipcopy(void); +void xdrawcursor(int, int, Letter, int, int, Letter, Letter*, int); +void xdrawline(Letter*, int, int, int); +void xfinishdraw(void); +void xloadcols(void); +int xsetcolorname(int, char *); +void xsettitle(char *); +int xsetcursor(int); +void xsetmode(int, uint); +void xsetpointermotion(int); +void xsetsel(char *); +int xstartdraw(void); +void xximspot(int, int); + +void fatal( char *, ...); +void redraw(void); +void draw(void); + +void printscreen(Arg *); +void printsel(Arg *); +void sendbreak(Arg *); +void toggleprinter(Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(char *, char *, char *, char **); +ulong ttyread(void); +void ttyresize(int, int); +void ttywrite( char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(char *); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern uint tabspaces; +extern uint defaultfg; +extern uint defaultbg; +extern float alpha; diff --git a/src/cmd/term/term.info b/src/cmd/term/term.info new file mode 100644 index 0000000..7b90344 --- /dev/null +++ b/src/cmd/term/term.info @@ -0,0 +1,250 @@ +term+mono| simpleterm monocolor, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, + bel=^G, + blink=\E[5m, + bold=\E[1m, + cbt=\E[Z, + cvvis=\E[?25h, + civis=\E[?25l, + clear=\E[H\E[2J, + cnorm=\E[?12l\E[?25h, + colors#2, + cols#80, + cr=^M, + csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, + cub1=^H, + cud1=^J, + cud=\E[%p1%dB, + cuf1=\E[C, + cuf=\E[%p1%dC, + cup=\E[%i%p1%d;%p2%dH, + cuu1=\E[A, + cuu=\E[%p1%dA, + dch=\E[%p1%dP, + dch1=\E[P, + dim=\E[2m, + dl=\E[%p1%dM, + dl1=\E[M, + ech=\E[%p1%dX, + ed=\E[J, + el=\E[K, + el1=\E[1K, + enacs=\E)0, + flash=\E[?5h$<80/>\E[?5l, + fsl=^G, + home=\E[H, + hpa=\E[%i%p1%dG, + hs, + ht=^I, + hts=\EH, + ich=\E[%p1%d@, + il1=\E[L, + il=\E[%p1%dL, + ind=^J, + indn=\E[%p1%dS, + invis=\E[8m, + is2=\E[4l\E>\E[?1034l, + it#4, + kel=\E[1;2F, + ked=\E[1;5F, + ka1=\E[1~, + ka3=\E[5~, + kc1=\E[4~, + kc3=\E[6~, + kbs=\177, + kcbt=\E[Z, + kb2=\EOu, + kcub1=\EOD, + kcud1=\EOB, + kcuf1=\EOC, + kcuu1=\EOA, + kDC=\E[3;2~, + kent=\EOM, + kEND=\E[1;2F, + kIC=\E[2;2~, + kNXT=\E[6;2~, + kPRV=\E[5;2~, + kHOM=\E[1;2H, + kLFT=\E[1;2D, + kRIT=\E[1;2C, + kind=\E[1;2B, + kri=\E[1;2A, + kclr=\E[3;5~, + kdl1=\E[3;2~, + kdch1=\E[3~, + kich1=\E[2~, + kend=\E[4~, + kf1=\EOP, + kf2=\EOQ, + kf3=\EOR, + kf4=\EOS, + kf5=\E[15~, + kf6=\E[17~, + kf7=\E[18~, + kf8=\E[19~, + kf9=\E[20~, + kf10=\E[21~, + kf11=\E[23~, + kf12=\E[24~, + kf13=\E[1;2P, + kf14=\E[1;2Q, + kf15=\E[1;2R, + kf16=\E[1;2S, + kf17=\E[15;2~, + kf18=\E[17;2~, + kf19=\E[18;2~, + kf20=\E[19;2~, + kf21=\E[20;2~, + kf22=\E[21;2~, + kf23=\E[23;2~, + kf24=\E[24;2~, + kf25=\E[1;5P, + kf26=\E[1;5Q, + kf27=\E[1;5R, + kf28=\E[1;5S, + kf29=\E[15;5~, + kf30=\E[17;5~, + kf31=\E[18;5~, + kf32=\E[19;5~, + kf33=\E[20;5~, + kf34=\E[21;5~, + kf35=\E[23;5~, + kf36=\E[24;5~, + kf37=\E[1;6P, + kf38=\E[1;6Q, + kf39=\E[1;6R, + kf40=\E[1;6S, + kf41=\E[15;6~, + kf42=\E[17;6~, + kf43=\E[18;6~, + kf44=\E[19;6~, + kf45=\E[20;6~, + kf46=\E[21;6~, + kf47=\E[23;6~, + kf48=\E[24;6~, + kf49=\E[1;3P, + kf50=\E[1;3Q, + kf51=\E[1;3R, + kf52=\E[1;3S, + kf53=\E[15;3~, + kf54=\E[17;3~, + kf55=\E[18;3~, + kf56=\E[19;3~, + kf57=\E[20;3~, + kf58=\E[21;3~, + kf59=\E[23;3~, + kf60=\E[24;3~, + kf61=\E[1;4P, + kf62=\E[1;4Q, + kf63=\E[1;4R, + khome=\E[1~, + kil1=\E[2;5~, + krmir=\E[2;2~, + knp=\E[6~, + kmous=\E[M, + kpp=\E[5~, + lines#24, + mir, + msgr, + npc, + op=\E[39;49m, + pairs#64, + mc0=\E[i, + mc4=\E[4i, + mc5=\E[5i, + rc=\E8, + rev=\E[7m, + ri=\EM, + rin=\E[%p1%dT, + ritm=\E[23m, + rmacs=\E(B, + rmcup=\E[?1049l, + rmir=\E[4l, + rmkx=\E[?1l\E>, + rmso=\E[27m, + rmul=\E[24m, + rs1=\Ec, + rs2=\E[4l\E>\E[?1034l, + sc=\E7, + sitm=\E[3m, + sgr0=\E[0m, + smacs=\E(0, + smcup=\E[?1049h, + smir=\E[4h, + smkx=\E[?1h\E=, + smso=\E[7m, + smul=\E[4m, + tbc=\E[3g, + tsl=\E]0;, + xenl, + vpa=\E[%i%p1%dd, +# XTerm extensions + rmxx=\E[29m, + smxx=\E[9m, +# disabled rep for now: causes some issues with older ncurses versions. +# rep=%p1%c\E[%p2%{1}%-%db, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) + Tc, + Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, + +term| simpleterm, + 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, + setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + 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=term, + ccc, + colors#256, pairs#32767, + oc=\E]104\007, +# 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=term, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +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, + +term-bs| simpleterm with backspace as backspace, + use=term, + kbs=\010, + kdch1=\177, + +term-bs-256color| simpleterm with backspace as backspace and 256colors, + use=term-256color, + kbs=\010, + kdch1=\177, diff --git a/src/cmd/term/util.c b/src/cmd/term/util.c new file mode 100644 index 0000000..3e7d81b --- /dev/null +++ b/src/cmd/term/util.c @@ -0,0 +1,30 @@ +#include + +static const uchar table[] = { +#include "nonspacing.h" +}; + +static const uchar wtable[] = { +#include "wide.h" +}; + +int +wcwidth(wchar_t wc) +{ + if (wc < 0xffU) + return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0; + if ((wc & 0xfffeffffU) < 0xfffe) { + if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) + return 0; + if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) + return 2; + return 1; + } + if ((wc & 0xfffe) == 0xfffe) + return -1; + if (wc-0x20000U < 0x20000) + return 2; + if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100U < 0xef) + return 0; + return 1; +} diff --git a/src/cmd/term/wide.h b/src/cmd/term/wide.h new file mode 100644 index 0000000..e403c9a --- /dev/null +++ b/src/cmd/term/wide.h @@ -0,0 +1,65 @@ +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,19,16,20,21,22,16,16,16,23,16,16,24,25,26,27,28,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,29, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,30,16,16,16,16,31,16,16,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,17,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,16,16,16,33, +34,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,35,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +17,17,17,17,17,17,36,17,17,37,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,38,39,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,40,41,42,43,44,45,46,47,16,48,49,16,16,16,16, +16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,48,0,0,0,0,0,0,255,15,0,0,0,0,128,0,0,8, +0,2,12,0,96,48,64,16,0,0,4,44,36,32,12,0,0,0,1,0,0,0,80,184,0,0,0,0,0,0,0,224, +0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,251,255,255,255,255,255,255,255, +255,255,255,15,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,63,0,0,0,255,15,255,255,255,255, +255,255,255,127,254,255,255,255,255,255,255,255,255,255,127,254,255,255,255, +255,255,255,255,255,255,255,255,255,224,255,255,255,255,255,254,255,255,255, +255,255,255,255,255,255,255,127,255,255,255,255,255,7,255,255,255,255,15,0, +255,255,255,255,255,127,255,255,255,255,255,0,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0, +0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,31,255,255,255,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, +255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,255,3,0,0,255,255,255,255,247,255,127,15,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,255,255,255,255,255,255,255, +255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,7,0,255,255,255,127,0,0,0,0,0, +0,7,0,240,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255, +15,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,64,254,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,255,255,255, +255,255,15,255,1,3,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255, +1,224,191,255,255,255,255,255,255,255,255,223,255,255,15,0,255,255,255,255, +255,135,15,0,255,255,17,255,255,255,255,255,255,255,255,127,253,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +159,255,255,255,255,255,255,255,63,0,120,255,255,255,0,0,4,0,0,96,0,16,0,0,0, +0,0,0,0,0,0,0,248,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255, +255,255,255,255,255,255,63,16,39,0,0,24,240,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,255,15,0, +0,0,224,255,255,255,255,255,255,255,255,255,255,255,255,123,252,255,255,255, +255,231,199,255,255,255,231,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,15,7,7,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/src/cmd/term/x.c b/src/cmd/term/x.c new file mode 100644 index 0000000..27a2e7a --- /dev/null +++ b/src/cmd/term/x.c @@ -0,0 +1,2070 @@ +/* See LICENSE for license details. */ +#include "term.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* harfbuzz additions */ +#if 0 +void hbunloadfonts(); +void hbtransform(XftGlyphFontSpec *, const Letter*, size_t, int, int); +#endif + +/* types used in config.h */ +typedef struct Shortcut Shortcut; +typedef struct MouseShortcut MouseShortcut; +typedef struct Key Key; + +struct Shortcut{ + uint mod; + KeySym keysym; + void (*func)(Arg *); + Arg arg; +}; + +struct MouseShortcut { + uint mod; + uint button; + void (*func)(Arg *); + Arg arg; + uint release; +}; + +struct Key { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + schar appkey; /* application keypad */ + schar appcursor; /* application cursor */ +}; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13) + +/* function definitions used in config.h */ +static void clipcopy(Arg *); +static void clippaste(Arg *); +static void numlock(Arg *); +static void selpaste(Arg *); +static void zoom(Arg *); +static void zoomabs(Arg *); +static void zoomreset(Arg *); +static void ttysend(Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int depth; /* color depth */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, Letter *, int, int, int); +static void xdrawglyphfontspecs(XftGlyphFontSpec *, Letter, int, int, int); +static void xdrawglyph(Letter, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, + [SelectionClear] = selclear_, + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = nil; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = nil; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_alpha = nil; +static char *opt_class = nil; +static char **opt_cmd = nil; +static char *opt_embed = nil; +static char *opt_font = nil; +static char *opt_io = nil; +static char *opt_line = nil; +static char *opt_name = nil; +static char *opt_title = nil; + +static int oldbutton = 3; /* button event on startup: 3 = release */ + +void +clipcopy(Arg *_) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = nil; + + if (xsel.primary != nil) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(Arg *_) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(Arg *_) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(Arg *_) +{ + win.mode ^= Wnumlock; +} + +void +zoom(Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - win.hb; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - win.vb; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SelRegular; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < arrlen(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, x = evcol(e), y = evrow(e), + button = e->xbutton.button, state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + /* from urxvt */ + if (e->xbutton.type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(Wmousemotion) && !IS_SET(Wmousemany)) + return; + /* MOUSE_MOTION: no reporting if no button is pressed */ + if (IS_SET(Wmousemotion) && oldbutton == 3) + return; + + button = oldbutton + 32; + ox = x; + oy = y; + } else { + if (!IS_SET(Wmousesgr) && e->xbutton.type == ButtonRelease) { + button = 3; + } else { + button -= Button1; + if (button >= 3) + button += 64 - 3; + } + if (e->xbutton.type == ButtonPress) { + oldbutton = button; + ox = x; + oy = y; + } else if (e->xbutton.type == ButtonRelease) { + oldbutton = 3; + /* Wmousex10: no button release reporting */ + if (IS_SET(Wmousex10)) + return; + if (button == 64 || button == 65) + return; + } + } + + if (!IS_SET(Wmousex10)) { + button += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod4Mask ) ? 8 : 0) + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(Wmousesgr)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + button, x+1, y+1, + e->xbutton.type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+button, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + arrlen(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + struct timespec now; + int snap; + + if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (e->xbutton.button == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SnapLine; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SnapWord; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(Wbrcktpaste) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(Wbrcktpaste) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(nil); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != nil) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (e->xbutton.button == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + win.hb = (win.w - col*win.cw)/2; + win.vb = (win.h - row*win.ch)/2; + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(arrlen(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, nil, &dc.col[i])) { + if (colorname[i]) + fatal("could not allocate color '%s'\n", colorname[i]); + else + fatal("could not allocate color %d\n", i); + } + + if (opt_alpha) + alpha = strtof(opt_alpha, nil); + + dc.col[defaultbg].color.alpha = (ushort)(0xffff * alpha); + dc.col[defaultbg].pixel &= 0x00ffffff; + dc.col[defaultbg].pixel |= (uchar)(0xff*alpha) << 24; + loaded = 1; +} + +int +xsetcolorname(int x, char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, &dc.col[IS_SET(Wreverse)? defaultfg : defaultbg], x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h, sizeh->width = win.w; + sizeh->height_inc = 1; + sizeh->width_inc = 1; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, nil, nil, nil, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(nil, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(nil, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = nil; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((FcChar8 *)fontstr); + + if (!pattern) + fatal("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + fatal("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + fatal("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + fatal("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + fatal("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ +#if 0 + hbunloadfonts(); +#endif + + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = nil, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = nil, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, nil, nil, nil); + if (xw.ime.xim == nil) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, nil)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + nil); + + if (xw.ime.xic == nil) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + nil); + } + if (xw.ime.xic == nil) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, nil, nil, nil, + ximinstantiate, nil); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = nil; + XRegisterIMInstantiateCallback(xw.dpy, nil, nil, nil, + ximinstantiate, nil); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = nil; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + XColor xmousefg, xmousebg; + XWindowAttributes attr; + XVisualInfo vis; + pid_t thispid = getpid(); + + if (!(xw.dpy = XOpenDisplay(nil))) + fatal("can't open display\n"); + + if (!(opt_embed && (parent == strtol(opt_embed, nil, 0)))) { + parent = XRootWindow(xw.dpy, xw.scr); + xw.depth = 32; + } else { + XGetWindowAttributes(xw.dpy, parent, &attr); + xw.depth = attr.depth; + } + + XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); + xw.vis = vis.visual; + xw.scr = XDefaultScreen(xw.dpy); + + /* font */ + if (!FcInit()) + fatal("could not init fontconfig.\n"); + + usedfont = (opt_font == nil)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * win.hb + cols*win.cw; + win.h = 2 * win.vb + rows*win.ch; + + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, xw.depth, InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); + dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); + + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) + XRegisterIMInstantiateCallback(xw.dpy, nil, nil, nil, ximinstantiate, nil); + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = Wnumlock; + resettitle(); + xhints(); + + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = nil; + xsel.clipboard = nil; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, Letter *glyphs, int len, int x, int y) +{ + float winx = win.hb + x * win.cw, winy = win.vb + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + rune r; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { nil }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for(i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + r = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ +#if 0 + if(mode & Gwdummy) +#endif + if(mode == Gwdummy) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if(prevmode != mode){ + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & Gwide) ? 2.0f : 1.0f); + if ((mode & Gitalic) && (mode & Gbold)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & Gitalic) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & Gbold) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, r); + if(glyphidx){ + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for(f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, r); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == r) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if(f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, r); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + fatal("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = r; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, r); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + +#if 0 + hbtransform(specs, glyphs, len, x, y); +#endif + + return numspecs; +} + +void +xdrawglyphfontspecs(XftGlyphFontSpec *specs, Letter base, int len, int x, int y) +{ + int charlen = len * ((base.mode & Gwide) ? 2 : 1); + int winx = win.hb + x * win.cw, winy = win.vb + y * win.ch, width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & Gitalic && base.mode & Gbold) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & Gitalic && dc.ifont.badslant) || + (base.mode & Gbold && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & Gfaint) == Gbold && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(Wreverse)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &revbg); + bg = &revbg; + } + } + + if ((base.mode & Gboldfaint) == Gfaint) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & Greverse) + temp = fg, fg = bg, bg = temp; + + if (base.mode & Gblink && win.mode & Wblink) + fg = bg; + + if (base.mode & Ginvisible) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, win.vb, + winy + win.ch + + ((winy + win.ch >= win.vb + win.th)? win.h : 0)); + } + if (winx + width >= win.hb + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= win.vb + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, win.hb); + if (winy + win.ch >= win.hb + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0, r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & Gunline) + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); + + if (base.mode & Gstruck) + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, width, 1); + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Letter g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Letter g, int ox, int oy, Letter og, Letter *line, int len) +{ + Color drawcol; + + /* remove the old cursor */ + if(selected(ox, oy)) + og.mode ^= Greverse; + xdrawglyph(og, ox, oy); +#if 0 + xdrawline(line, 0, oy, len); +#endif + + if(IS_SET(Whide)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= Gbold|Gitalic|Gunline|Gstruck|Gwide; + + if(IS_SET(Wreverse)) { + g.mode |= Greverse; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if(selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = og.bg; //defaultbg; + g.bg = og.fg; //defaultcs; + if (IS_TRUECOL(og.fg)) { + drawcol.color.alpha = 0xffff; + drawcol.color.red = TRUERED(og.fg); + drawcol.color.green = TRUEGREEN(og.fg); + drawcol.color.blue = TRUEBLUE(og.fg); + goto drawnew; + } + } + drawcol = dc.col[g.bg]; + } + +drawnew: + if(IS_SET(Wfocused)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* fallthrough */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + win.hb + cx * win.cw, + win.vb + (cy + 1) * win.ch - cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + win.hb + cx * win.cw, + win.vb + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + win.hb + cx * win.cw, + win.vb + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + win.hb + cx * win.cw, + win.vb + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + win.hb + (cx + 1) * win.cw - 1, + win.vb + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + win.hb + cx * win.cw, + win.vb + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop); + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(Wvisible); +} + +void +xdrawline(Letter *line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Letter base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == Gwdummy) + continue; + if (selected(x, y1)) + new.mode ^= Greverse; + if (i > 0 && GLYPHCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(Wreverse)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == nil) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, nil); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, Wvisible); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~Wvisible; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, uint flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & Wreverse) != (mode & Wreverse)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(Wfocused))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)nil); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= Wfocused; + xseturgency(0); + if (IS_SET(Wfocus)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~Wfocused; + if (IS_SET(Wfocus)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < arrlen(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == arrlen(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return nil; + } + + for (kp = key; kp < key + arrlen(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(Wappkeypad) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(Wnumlock) && kp->appkey == 2) + continue; + + if (IS_SET(Wappcursor) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return nil; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym; + char buf[64], *customkey; + int len; + rune c; + Status status; + Shortcut *bp; + + if (IS_SET(Wkbdblock)) + return; + + if (xw.ime.xic) + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + else + len = XLookupString(e, buf, sizeof buf, &ksym, nil); + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + arrlen(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(W8bit)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8·encode(&c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= Wfocused; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~Wfocused; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : nil; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, nil, nil, tv, nil) < 0) { + if (errno == EINTR) + continue; + fatal("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(Gblink)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= Wblink; + win.mode ^= Wblink; + tsetdirtattr(Gblink); + lastblink = now; + timeout = blinktimeout; + } + } + + draw(); + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + fatal("usage: %s [-aiv] [-c class] [-f font] [-A alpha] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'A': + opt_alpha = EARGF(usage()); + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + fatal("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "term" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + + cols = MAX(cols, 1); + rows = MAX(rows, 1); + + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + + run(); + + return 0; +} diff --git a/src/cmd/walk/rules.mk b/src/cmd/walk/rules.mk new file mode 100644 index 0000000..cb9768c --- /dev/null +++ b/src/cmd/walk/rules.mk @@ -0,0 +1,15 @@ +include share/push.mk + +# local sources +SRCS_$(d):=$(d)/walk.c + +# local outputs +BINS_$(d):=$(d)/walk + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/walk/walk.c b/src/cmd/walk/walk.c new file mode 100644 index 0000000..c68d6e0 --- /dev/null +++ b/src/cmd/walk/walk.c @@ -0,0 +1,84 @@ +#include +#include + +static char buf[4*1024], *c = buf; /* should be greater or equal to PATH_MAX */ + +static +void +flush(void) +{ + *c = 0; + puts(buf); + c = buf; +} + +static +int +print(void *data, char *rel, char *abs, io·Stat *info) +{ +copy: + while (*abs && c < (arrend(buf)-2)) + *c++ = *abs++; + + if (*abs) { + flush(); + goto copy; + } + *c++ = '\n'; + + return 0; +} + +static +void +usage(void) +{ + fprintf(stderr, "usage: walk [-dlpv] file ...\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i, f = fs·nolinks, err, max = 0; + char *p; + static fs·Walker walker; + + ARGBEGIN{ + case 'd': + max = atoi(ARGF()); + break; + case 'l': + f ^= fs·nolinks; + break; + case 'p': + f |= fs·preorder; + break; + case 'v': + f |= fs·verbose; + break; + default: + usage(); + }ARGEND; + + walker.flags = f; + walker.func = print; + walker.data = nil; + walker.max = max; + + if (argc == 0) { + fs·init(&walker, ""); + fs·walk(&walker); + return(err = walker.err); + } else { + err = 0; + for (i=0; ix, server.cursor.dot->y))) + return; + + floating(server.grab.client, 1); +} + +void +move_client(Arg *arg) +{ + grab_client(); + server.cursor.mode = CursorMove; + + server.grab.x = server.cursor.dot->x - server.grab.client->geometry.x; + server.grab.y = server.cursor.dot->y - server.grab.client->geometry.y; + wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "fleur", server.cursor.dot); +} + +void +float_client(Arg *arg) +{ + Client *client = selected_client(); + wlr_log(WLR_DEBUG, "client selected = %lx", (uintptr)client); + if(!client) + return; + + floating(client, client->isfloating ? 0 : 1); +} + +void +resize_client(Arg *arg) +{ + double x, y; + struct wlr_box geometry; + + grab_client(); + server.cursor.mode = CursorResize; + + wlr_xdg_surface_get_geometry(server.grab.client->xdg, &geometry); + + x = server.grab.client->geometry.x + geometry.x + geometry.width; + y = server.grab.client->geometry.y + geometry.y + geometry.height; + + server.grab.x = server.cursor.dot->x - x; + server.grab.y = server.cursor.dot->y - y; + + server.grab.box = geometry; + server.grab.box.x += server.grab.client->geometry.x; + server.grab.box.y += server.grab.client->geometry.y; +} + +// ----------------------------------------------------------------------- +// core + +static inline +void +activate(struct wlr_surface *surface, int state) +{ +} + +void +focus(Client *client, int lift) +{ + struct wlr_xdg_surface *xdg; + struct wlr_surface *old, *new; + struct wlr_keyboard *keyboard; + + if(!client) { + wlr_seat_keyboard_notify_clear_focus(server.input.seat); + return; + } + + old = server.input.seat->keyboard_state.focused_surface; + + if(lift) { + wl_list_remove(&client->stack); + wl_list_insert(&server.client.stack, &client->stack); + } + + new = client->xdg->surface; + if(old==new) + return; + + wl_list_remove(&client->focus); + wl_list_insert(&server.client.focus, &client->focus); + server.monitor.selected = client->monitor; + client->isurgent = 0; + + if(old) { + if(wlr_surface_is_xdg_surface(old)) { + xdg = wlr_xdg_surface_from_wlr_surface(old); + wlr_xdg_toplevel_set_activated(xdg, false); + } + } + + keyboard = wlr_seat_get_keyboard(server.input.seat); + + wlr_seat_keyboard_notify_enter(server.input.seat, new, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers + ); + + wlr_xdg_toplevel_set_activated(client->xdg, true); +} + +Client* +client_at(double x, double y) +{ + Client *client; + wl_list_for_each(client, &server.client.stack, stack) + if(VISIBLE_ON(client, client->monitor) && wlr_box_contains_point(&client->geometry, x, y)) + return client; + return nil; +} + +static +int +has(Client *client, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) +{ + double x, y, vsx = lx - client->geometry.x, vsy = ly - client->geometry.y; + struct wlr_surface *find = nil; + + find = wlr_xdg_surface_surface_at(client->xdg, vsx, vsy, &x, &y); + if(find) { + *sx = x; + *sy = y; + *surface = find; + return true; + } + + return false; +} + +struct wlr_surface * +client_surface_at(Client *client, double cx, double cy, double *sx, double *sy) +{ + return wlr_xdg_surface_surface_at(client->xdg, cx, cy, sx, sy); +} + + +static +void +constrain(Client *client, struct wlr_box *box) +{ + client->geometry.width = MAX(1, client->geometry.width); + client->geometry.height = MAX(1, client->geometry.height); + + if(client->geometry.x >= box->x + box->width) + client->geometry.x = box->x + box->width - client->geometry.width; + if(client->geometry.y >= box->y + box->height) + client->geometry.y = box->y + box->height - client->geometry.height; + if(client->geometry.x + client->geometry.width + 2*client->border <= box->x) + client->geometry.x = box->x; + if(client->geometry.y + client->geometry.height + 2*client->border <= box->y) + client->geometry.y = box->y; +} + +void +resize(Client *client, int x, int y, int w, int h, int interact) +{ + struct wlr_box *box = interact ? &server.monitor.geometry : &client->monitor->window; + + client->geometry.x = x; + client->geometry.y = y; + client->geometry.width = w; + client->geometry.height = h; + + constrain(client, box); + + client->resize = wlr_xdg_toplevel_set_size(client->xdg, + client->geometry.width - 2*client->border, + client->geometry.height - 2*client->border + ); +} + +void +attach(Client *client, Monitor *monitor, uint tags) +{ + Monitor *old = client->monitor; + if(old == monitor) + return; + + client->monitor = monitor; + + if(old) { + wlr_surface_send_leave(client->xdg->surface, old->output); + arrange(old); + } + + if(monitor) { + /* make sure window actually overlaps with the monitor */ + constrain(client, &monitor->geometry); + wlr_surface_send_enter(client->xdg->surface, monitor->output); + client->tags = tags ? tags : monitor->tag.set[monitor->tag.selected]; + arrange(monitor); + } + + focus(focused_client(server.monitor.selected), 1); +} + +void +rules(Client *client) +{ + /* rule matching */ + Rule *rule; + uint i, tags; + char *id, *title; + Monitor *monitor, *it; + + monitor = server.monitor.selected; + + if (!(id=client->xdg->toplevel->app_id)) + id = broken; + if (!(title=client->xdg->toplevel->title)) + title = broken; + + for(tags=0, rule=cfg·rule; rule != cfg·endrule; ++rule) { + if ((!rule->title || strstr(title, rule->title)) + && (!rule->id || strstr(id, rule->id))) { + client->isfloating = rule->isfloating; + tags |= rule->tags; + i = 0; + wl_list_for_each(it, &server.monitor.list, link) + if(rule->monitor == i++) + monitor = it; + } + } + + attach(client, monitor, tags); +} + +void +floating(Client *client, int state) +{ + wlr_log(WLR_DEBUG, "client %lx, floating = %d", (uintptr)client, state); + client->isfloating = state; + arrange(client->monitor); +} + +Client * +selected_client(void) +{ + Client *client = wl_container_of(server.client.focus.next, client, focus); + if(wl_list_empty(&server.client.focus) || !VISIBLE_ON(client, server.monitor.selected)) + return nil; + return client; +} + +void +request_activate(struct wl_listener *l, void *data) +{ + struct wlr_xdg_activation_v1_request_activate_event *event = data; + Client *client; + + if (!wlr_surface_is_xdg_surface(event->surface)) + return; + + client = wlr_xdg_surface_from_wlr_surface(event->surface)->data; + if(client != selected_client()) + client->isurgent = 1; +} diff --git a/src/cmd/wm/config.h b/src/cmd/wm/config.h new file mode 100644 index 0000000..1f5ba85 --- /dev/null +++ b/src/cmd/wm/config.h @@ -0,0 +1,70 @@ +/* appearance */ +CONFIG(int, sloppyfocus, 1); +CONFIG(int, borderpixel, 1); +CONFIG(float, rootcolor[], {0.3, 0.3, 0.3, 1.0}); +CONFIG(float, bordercolor[], {0.5, 0.5, 0.5, 1.0}); +CONFIG(float, focuscolor[], {1.0, 0.0, 0.0, 1.0}); + +/* sampling */ +CONFIG(int, repeat_rate, 25); +CONFIG(int, repeat_delay, 600); + +/* tags */ +CONFIG(char*, tags[], { "1", "2", "3", "4", "5", "6", "7", "8", "9" }); + +/* application specific rules */ +CONFIG(Rule, rule[], { + /* app_id title tags mask isfloating monitor */ + /* examples: + { "Gimp", nil, 0, 1, -1 }, + { "firefox", nil, 1 << 8, 0, -1 }, + */ +}); +CONFIG(Rule*, endrule, arrend(cfg·rule)); + +/* commands */ +CONFIG(char*, termcommand[], { "alacritty", nil }); +CONFIG(char*, menucommand[], { "dmenu-wl_run", nil }); + +/* layouts */ +CONFIG(Layout, layouts[], { + /* symbol arrange */ + { "[]=", tile }, + { "><>", nil }, /* no layout function means floating behavior */ +}); +CONFIG(Layout*, endlayout, arrend(cfg·layouts)); + +/* monitors + * The order in which monitors are defined determines their position. + * non-configured monitors are always added to the left. */ +CONFIG(MonitorRule, monitorrule[], { + /* name layout, x, y, scale, transform master */ + { nil, &cfg·layouts[0], 0, 0, 1, WL_OUTPUT_TRANSFORM_NORMAL, {0.55, 1} }, +}); +CONFIG(MonitorRule*, endmonitorrule, arrend(cfg·monitorrule)); + +/* keybindings */ +#define MODKEY WLR_MODIFIER_ALT +#define MOD(a) WLR_MODIFIER_##a +#define KEY(a) XKB_KEY_##a + +CONFIG(Key, binding[], { + /* modifier key function argument */ + { MODKEY, KEY(Return), spawn, {.v = cfg·termcommand} }, + { MODKEY, KEY(d), spawn, {.v = cfg·menucommand} }, + { MODKEY|MOD(SHIFT), KEY(Q), quit, {.v = nil} }, +}); +CONFIG(Key*, endbinding, arrend(cfg·binding)); + +#undef MOD +#undef KEY + +/* mouse buttons */ +CONFIG(Button, button[], { + { MODKEY, BTN_LEFT, move_client, {0} }, + { MODKEY, BTN_MIDDLE, float_client, {0} }, + { MODKEY, BTN_RIGHT, resize_client, {0} }, +}); +CONFIG(Button*, endbutton, arrend(cfg·button)); + +#undef MODKEY diff --git a/src/cmd/wm/input.c b/src/cmd/wm/input.c new file mode 100644 index 0000000..4c6bfd4 --- /dev/null +++ b/src/cmd/wm/input.c @@ -0,0 +1,316 @@ +#include "wm.h" + +// ----------------------------------------------------------------------- +// keyboard + +static +void +keymodifier(struct wl_listener *l, void *data) +{ + Keyboard *keyboard = wl_container_of(l, keyboard, event.modify); + + wlr_seat_set_keyboard(server.input.seat, keyboard->device); + wlr_seat_keyboard_notify_modifiers(server.input.seat, &keyboard->device->keyboard->modifiers); +} + +static +int +keybinding(uint32 modifier, xkb_keysym_t sym) +{ + Key *key; + + for(key=cfg·binding; key!=cfg·endbinding; ++key) { + if(modifier == key->modifier && sym == key->sym && key->action){ + key->action(&key->arg); + return 1; + } + } + return 0; +} + +static +void +keypress(struct wl_listener *l, void *data) +{ + int i,h,n; + uint32 keycode, modifier; + const xkb_keysym_t *syms; + struct Keyboard *keyboard = wl_container_of(l, keyboard, event.press); + struct wlr_event_keyboard_key *event = data; + + keycode = event->keycode + 8; + + h = 0; + n = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); + + modifier = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + if(event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + for(i=0; idevice); + wlr_seat_keyboard_notify_key(server.input.seat, event->time_msec, event->keycode, event->state); + } +} + +static +void +free_keyboard(struct wl_listener *l, void *data) +{ + struct wlr_input_device *device = data; + Keyboard *keyboard = device->data; + + /* XXX: debug + wl_list_remove(&keyboard->link); + wl_list_remove(&keyboard->event.modify.link); + wl_list_remove(&keyboard->event.press.link); + wl_list_remove(&keyboard->event.destroy.link); + + free(keyboard); + */ +} + +static +void +make_keyboard(struct wlr_input_device *device) +{ + Keyboard *keyboard; + struct xkb_context *context; + struct xkb_keymap *keymap; + + keyboard = device->data = calloc(1, sizeof(*keyboard)); + keyboard->device = device; + + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + keymap = xkb_keymap_new_from_names(context, nil, XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(device->keyboard, keymap); + + xkb_keymap_unref(keymap); + xkb_context_unref(context); + + wlr_keyboard_set_repeat_info(device->keyboard, cfg·repeat_rate, cfg·repeat_delay); + + keyboard->event.modify.notify = keymodifier; + wl_signal_add(&device->keyboard->events.modifiers, &keyboard->event.modify); + + keyboard->event.press.notify = keypress; + wl_signal_add(&device->keyboard->events.key, &keyboard->event.press); + + keyboard->event.destroy.notify = free_keyboard; + wl_signal_add(&device->keyboard->events.destroy, &keyboard->event.destroy); + + wlr_seat_set_keyboard(server.input.seat, device); + + wl_list_insert(&server.input.keyboards, &keyboard->link); +} + +// ----------------------------------------------------------------------- +// cursor + +static +void +focus_surface(Client *client, struct wlr_surface *surface, double sx, double sy, uint32 time) +{ + struct timespec now; + int lift = time; + + if(client && !surface) + surface = client->xdg->surface; + + if(!surface){ + wlr_seat_pointer_notify_clear_focus(server.input.seat); + return; + } + + if(!time) { + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } + + if(surface == server.input.seat->pointer_state.focused_surface) { + wlr_seat_pointer_notify_motion(server.input.seat, time, sx, sy); + return; + } + + wlr_seat_pointer_notify_enter(server.input.seat, surface, sx, sy); + + if(cfg·sloppyfocus && lift) + focus(client, 0); +} + +void +notify_move(uint32 time) +{ + double sx, sy; + Client *client; + struct wlr_box box; + struct wlr_surface *surface; + + if(time) { + wlr_idle_notify_activity(server.input.idle, server.input.seat); + if(cfg·sloppyfocus) + server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); + } + + if(server.cursor.mode == CursorMove) { + resize(server.grab.client, + server.cursor.dot->x - server.grab.x, + server.cursor.dot->y - server.grab.y, + server.grab.client->geometry.width, + server.grab.client->geometry.height, + 1 + ); + return; + } + + if(server.cursor.mode == CursorResize) { + wlr_xdg_surface_get_geometry(server.grab.client->xdg, &box); + resize(server.grab.client, + server.grab.box.x - box.x, + server.grab.box.y - box.y, + server.cursor.dot->x - server.grab.x - server.grab.box.x, + server.cursor.dot->y - server.grab.y - server.grab.box.y, + 1 + ); + return; + } + + /* otherwise, find the client under the pointer and send the event along. */ + client = client_at(server.cursor.dot->x, server.cursor.dot->y); + if(!client) { + wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); + return; + } + + surface = client_surface_at( + client, + server.cursor.dot->x - client->geometry.x - client->border, + server.cursor.dot->y - client->geometry.y - client->border, + &sx, &sy + ); + + focus_surface(client, surface, sx, sy, time); +} + +void +cursor_move(struct wl_listener *l, void *data) +{ + struct wlr_event_pointer_motion *event = data; + wlr_cursor_move(server.cursor.dot, event->device, event->delta_x, event->delta_y); + notify_move(event->time_msec); +} + +void +cursor_move_abs(struct wl_listener *l, void *data) +{ + struct wlr_event_pointer_motion_absolute *event = data; + wlr_cursor_warp_absolute(server.cursor.dot, event->device, event->x, event->y); + notify_move(event->time_msec); +} + +void +cursor_button(struct wl_listener *l, void *data) +{ + Client *client; + uint32 modifier; + Button *button; + struct wlr_keyboard *keyboard; + struct wlr_event_pointer_button *event = data; + + wlr_idle_notify_activity(server.input.idle, server.input.seat); + + switch(event->state) { + case WLR_BUTTON_PRESSED: + if((client=client_at(server.cursor.dot->x, server.cursor.dot->y))) + focus(client,1); + + keyboard = wlr_seat_get_keyboard(server.input.seat); + modifier = wlr_keyboard_get_modifiers(keyboard); + for(button=cfg·button; button != cfg·endbutton; ++button) { + if(modifier == button->modifier && event->button == button->code && button->function) { + button->function(&button->arg); + return; + } + } + break; + case WLR_BUTTON_RELEASED: + if(server.cursor.mode != CursorNormal) { + wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); + server.cursor.mode = CursorNormal; + /* Drop the window off on its new monitor */ + server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); + attach(server.grab.client, server.monitor.selected, 0); + return; + } + } + + wlr_seat_pointer_notify_button(server.input.seat, event->time_msec, event->button, event->state); +} + +void +cursor_axis(struct wl_listener *l, void *data) +{ + struct wlr_event_pointer_axis *event = data; + /* Notify the client with pointer focus of the axis event. */ + wlr_seat_pointer_notify_axis(server.input.seat, + event->time_msec, event->orientation, event->delta, + event->delta_discrete, event->source); +} + +void +cursor_frame(struct wl_listener *l, void *data) +{ + wlr_seat_pointer_notify_frame(server.input.seat); +} + +void +request_cursor(struct wl_listener *l, void *data) +{ + struct wlr_seat_pointer_request_set_cursor_event *event = data; + struct wlr_seat_client *focused = server.input.seat->pointer_state.focused_client; + if(focused == event->seat_client) + wlr_cursor_set_surface(server.cursor.dot, event->surface, event->hotspot_x, event->hotspot_y); +} + +void +request_set_selection(struct wl_listener *l, void *data) +{ + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(server.input.seat, event->source, event->serial); +} + +static +void +make_pointer(struct wlr_input_device *device) +{ + wlr_cursor_attach_input_device(server.cursor.dot, device); +} + +// ----------------------------------------------------------------------- +// generic input + +void +make_input(struct wl_listener *l, void *data) +{ + uint32 capability; + struct wlr_input_device *device = data; + + switch(device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + make_keyboard(device); + break; + case WLR_INPUT_DEVICE_POINTER: + make_pointer(device); + /* fallthrough */ + default: + break; + } + + capability = WL_SEAT_CAPABILITY_POINTER; + if(!wl_list_empty(&server.input.keyboards)) + capability |= WL_SEAT_CAPABILITY_KEYBOARD; + wlr_seat_set_capabilities(server.input.seat, capability); +} diff --git a/src/cmd/wm/layer.c b/src/cmd/wm/layer.c new file mode 100644 index 0000000..bfac744 --- /dev/null +++ b/src/cmd/wm/layer.c @@ -0,0 +1,107 @@ +#include "wm.h" + +static +void +map(struct wl_listener *l, void *data) +{ + Layer *layer = wl_container_of(l, layer, event.map); + wlr_surface_send_enter(layer->surface->surface, layer->surface->output); + notify_move(0); +} + +static +void +finalize(Layer *layer) +{ + layer->surface->mapped = 0; + if (layer->surface->surface == server.input.seat->keyboard_state.focused_surface) + focus(selected_client(), 1); + notify_move(0); +} + +static +void +unmap(struct wl_listener *l, void *data) +{ + Layer *layer = wl_container_of(l, layer, event.unmap); + finalize(layer); +} + +static +void +destroy(struct wl_listener *l, void *data) +{ + Monitor *monitor; + Layer *layer = wl_container_of(l, layer, event.destroy); + + if (layer->surface->mapped) + finalize(layer); + + wl_list_remove(&layer->link); + wl_list_remove(&layer->event.destroy.link); + wl_list_remove(&layer->event.map.link); + wl_list_remove(&layer->event.unmap.link); + wl_list_remove(&layer->event.commit.link); + + if(layer->surface->output) { + monitor = layer->surface->output->data; + if(monitor) + stratify(monitor); + layer->surface->output = nil; + } + free(layer); +} + +static +void +commit(struct wl_listener *l, void *data) +{ + Monitor *monitor; + Layer *layer = wl_container_of(l, layer, event.commit); + struct wlr_layer_surface_v1 *surface = layer->surface; + struct wlr_output *output = surface->output; + + if(!output) + return; + + monitor = output->data; + stratify(monitor); + + if (layer->type != surface->current.layer) { + wl_list_remove(&layer->link); + wl_list_insert(&monitor->layer[surface->current.layer], &layer->link); + layer->type = surface->current.layer; + } +} + +void +make_layer_surface(struct wl_listener *l, void *data) +{ + Layer *layer; + Monitor *monitor; + struct wlr_layer_surface_v1_state state; + struct wlr_layer_surface_v1 *surface = data; + + if(!surface->output) + surface->output = server.monitor.selected->output; + + layer = surface->data = calloc(1, sizeof(*layer)); + layer->surface = surface; + + layer->event.map.notify = map; + wl_signal_add(&surface->events.map, &layer->event.map); + layer->event.unmap.notify = unmap; + wl_signal_add(&surface->events.unmap, &layer->event.unmap); + layer->event.destroy.notify = destroy; + wl_signal_add(&surface->events.destroy, &layer->event.destroy); + layer->event.commit.notify = commit; + wl_signal_add(&surface->surface->events.commit, &layer->event.commit); + + monitor = surface->output->data; + wl_list_insert(&monitor->layer[surface->client_pending.layer], &layer->link); + + state = surface->current; + surface->current = surface->client_pending; + stratify(monitor); + surface->current = state; +} diff --git a/src/cmd/wm/main.c b/src/cmd/wm/main.c new file mode 100644 index 0000000..2607801 --- /dev/null +++ b/src/cmd/wm/main.c @@ -0,0 +1,177 @@ +#include "wm.h" + +Server server = { + .event = { + .make_input = { .notify = make_input }, + .make_monitor = { .notify = make_monitor }, + .make_xdg_surface = { .notify = make_xdg_surface }, + .make_layer_surface = { .notify = make_layer_surface }, + + .monitor_change = { .notify = monitor_change }, + .monitor_test = { .notify = monitor_test }, + .monitor_apply = { .notify = monitor_apply }, + + .cursor_move = { .notify = cursor_move }, + .cursor_move_abs = { .notify = cursor_move_abs }, + .cursor_button = { .notify = cursor_button }, + .cursor_axis = { .notify = cursor_axis }, + .cursor_frame = { .notify = cursor_frame }, + + .request_activate = { .notify = request_activate }, + .request_cursor = { .notify = request_cursor }, + .request_set_selection = { .notify = request_set_selection }, + }, +}; + +// ----------------------------------------------------------------------- +// helper functions + +static inline +void +init(void) +{ + /* compositor initialization */ + server.display = wl_display_create(); + server.backend = wlr_backend_autocreate(server.display); + server.renderer = wlr_backend_get_renderer(server.backend); + server.present = wlr_presentation_create(server.display, server.backend); + + wlr_renderer_init_wl_display(server.renderer, server.display); + + wlr_compositor_create(server.display, server.renderer); + wlr_export_dmabuf_manager_v1_create(server.display); + wlr_screencopy_manager_v1_create(server.display); + wlr_data_control_manager_v1_create(server.display); + wlr_data_device_manager_create(server.display); + wlr_gamma_control_manager_v1_create(server.display); + wlr_primary_selection_v1_device_manager_create(server.display); + wlr_viewporter_create(server.display); + + server.activate = wlr_xdg_activation_v1_create(server.display); + wl_signal_add(&server.activate->events.request_activate, &server.event.request_activate); + + wlr_data_device_manager_create(server.display); + + server.monitor.layout = wlr_output_layout_create(); + wl_signal_add(&server.monitor.layout->events.change, &server.event.monitor_change); + wlr_xdg_output_manager_v1_create(server.display, server.monitor.layout); + + wl_list_init(&server.monitor.list); + wl_signal_add(&server.backend->events.new_output, &server.event.make_monitor); + + server.monitor.manager = wlr_output_manager_v1_create(server.display); + wl_signal_add(&server.monitor.manager->events.test, &server.event.monitor_test); + wl_signal_add(&server.monitor.manager->events.apply, &server.event.monitor_apply); + + /* shell initialization */ + wl_list_init(&server.client.list); + wl_list_init(&server.client.stack); + wl_list_init(&server.client.focus); + + server.shell.xdg = wlr_xdg_shell_create(server.display); + wl_signal_add(&server.shell.xdg->events.new_surface, &server.event.make_xdg_surface); + + server.shell.layer = wlr_layer_shell_v1_create(server.display); + wl_signal_add(&server.shell.layer->events.new_surface, &server.event.make_layer_surface); + + wlr_server_decoration_manager_set_default_mode( + wlr_server_decoration_manager_create(server.display), + WLR_SERVER_DECORATION_MANAGER_MODE_SERVER + ); + wlr_xdg_decoration_manager_v1_create(server.display); + + /* input initialization */ + server.cursor.dot = wlr_cursor_create(); + wlr_cursor_attach_output_layout(server.cursor.dot, server.monitor.layout); + + server.cursor.manager = wlr_xcursor_manager_create(nil, 24); + wlr_xcursor_manager_load(server.cursor.manager, 1); + + wl_signal_add(&server.cursor.dot->events.motion, &server.event.cursor_move); + wl_signal_add(&server.cursor.dot->events.motion_absolute, &server.event.cursor_move_abs); + wl_signal_add(&server.cursor.dot->events.button, &server.event.cursor_button); + wl_signal_add(&server.cursor.dot->events.axis, &server.event.cursor_axis); + wl_signal_add(&server.cursor.dot->events.frame, &server.event.cursor_frame); + + wl_list_init(&server.input.keyboards); + wl_signal_add(&server.backend->events.new_input, &server.event.make_input); + + server.input.idle = wlr_idle_create(server.display); + server.input.seat = wlr_seat_create(server.display, "seat0"); + + wl_signal_add(&server.input.seat->events.request_set_cursor, &server.event.request_cursor); + wl_signal_add(&server.input.seat->events.request_set_selection, &server.event.request_set_selection); +} + +static inline +void +fini(void) +{ + wl_display_destroy_clients(server.display); + + wlr_backend_destroy(server.backend); + wlr_xcursor_manager_destroy(server.cursor.manager); + wlr_output_layout_destroy(server.monitor.layout); + wlr_seat_destroy(server.input.seat); + + wl_display_destroy(server.display); +} + +// ----------------------------------------------------------------------- +// main point of entry + +int +usage(void) +{ + fprintf(stderr, "usage: %s [-s startup command]\n", argv0); + return 1; +} + + +int +main(int argc, char *argv[]) +{ + char *socket, *cmd=nil; + + ARGBEGIN{ + case 's': + cmd = ARGF(); + break; + default: + return usage(); + } ARGEND + + if(argc != 0) + return usage(); + + wlr_log_init(WLR_DEBUG, nil); + + init(); + + if(!(socket=(char*)wl_display_add_socket_auto(server.display))) { + wlr_backend_destroy(server.backend); + return 1; + } + + if(!(wlr_backend_start(server.backend))) { + wlr_backend_destroy(server.backend); + wl_display_destroy(server.display); + return 1; + } + + setenv("WAYLAND_DISPLAY", socket, true); + if(cmd) { + if(fork()==0) + execl("/bin/sh", "/bin/sh", "-c", cmd, nil); + } + wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); + + server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); + wlr_cursor_warp_closest(server.cursor.dot, nil, server.cursor.dot->x, server.cursor.dot->y); + wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); + + wl_display_run(server.display); /* event loop */ + + fini(); + return 0; +} diff --git a/src/cmd/wm/monitor.c b/src/cmd/wm/monitor.c new file mode 100644 index 0000000..93073f3 --- /dev/null +++ b/src/cmd/wm/monitor.c @@ -0,0 +1,386 @@ +#include "wm.h" + +/* callbacks */ +void +monitor_change(struct wl_listener *l, void *data) +{ + Monitor *monitor; + struct wlr_output_configuration_v1 *config; + + config = wlr_output_configuration_v1_create(); + server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); + + wl_list_for_each(monitor, &server.monitor.list, link) { + struct wlr_output_configuration_head_v1 *head = + wlr_output_configuration_head_v1_create(config, monitor->output); + + monitor->geometry = monitor->window = *wlr_output_layout_get_box(server.monitor.layout, monitor->output); + + stratify(monitor); + arrange(monitor); + + head->state.enabled = monitor->output->enabled; + head->state.mode = monitor->output->current_mode; + head->state.x = monitor->geometry.x; + head->state.y = monitor->geometry.y; + } + + wlr_output_manager_v1_set_configuration(server.monitor.manager, config); +} + +static +void +trylayout(struct wlr_output_configuration_v1 *config, int force) +{ + int ok; + struct wlr_output_configuration_head_v1 *head; + + ok = 1; + wl_list_for_each(head, &config->heads, link) { + struct wlr_output *output= head->state.output; + wlr_output_enable(output, head->state.enabled); + if (head->state.enabled) { + if (head->state.mode) + wlr_output_set_mode(output, head->state.mode); + else + wlr_output_set_custom_mode( + output, + head->state.custom_mode.width, + head->state.custom_mode.height, + head->state.custom_mode.refresh + ); + + wlr_output_layout_move(server.monitor.layout, output, + head->state.x, head->state.y); + wlr_output_set_transform(output, head->state.transform); + } + + if(!(ok=wlr_output_test(output))) + break; + } + + wl_list_for_each(head, &config->heads, link) { + if(ok && force) + wlr_output_commit(head->state.output); + else + wlr_output_rollback(head->state.output); + } + + if(ok) + wlr_output_configuration_v1_send_succeeded(config); + else + wlr_output_configuration_v1_send_failed(config); + + wlr_output_configuration_v1_destroy(config); +} + +void +monitor_apply(struct wl_listener *l, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + trylayout(config, 1); +} + +void +monitor_test(struct wl_listener *l, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + trylayout(config, 0); +} + +void +make_monitor(struct wl_listener *l, void *data) +{ + int i; + Client *client; + Monitor *monitor; + MonitorRule *rule; + struct wlr_output_mode *mode; + struct wlr_output *output = data; + + /* + * XXX: needed? + if (wl_list_empty(&output->modes)) + return; + */ + + monitor = output->data = calloc(1, sizeof(*monitor)); + monitor->output = output; + + for(i=0; i < arrlen(monitor->layer); i++) + wl_list_init(&monitor->layer[i]); + monitor->tag.set[0] = monitor->tag.set[1] = 1; + + for(rule=cfg·monitorrule; rule != cfg·endmonitorrule; ++rule) { + if(!rule->name || strstr(output->name, rule->name)) { + monitor->master.len = rule->master.len; + monitor->master.frac = rule->master.frac; + + wlr_output_set_scale(output, rule->scale); + wlr_xcursor_manager_load(server.cursor.manager, rule->scale); + monitor->layouts[0] = monitor->layouts[1] = monitor->layout = rule->layout; + + wlr_output_set_transform(output, rule->transform); + break; + } + } + + mode = wlr_output_preferred_mode(output); + wlr_output_set_mode(output, mode); + wlr_output_enable_adaptive_sync(output, true); + + monitor->event.render.notify = render_monitor; + wl_signal_add(&output->events.frame, &monitor->event.render); + monitor->event.destroy.notify = free_monitor; + wl_signal_add(&output->events.destroy, &monitor->event.destroy); + + wlr_output_enable(output, true); + if(!wlr_output_commit(output)) + return; + + wl_list_insert(&server.monitor.list, &monitor->link); + + wlr_output_layout_add(server.monitor.layout, output, rule->x, rule->y); + server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); + + /* update the geometries of all monitors */ + wl_list_for_each(monitor, &server.monitor.list, link) { + /* first monitor in the list = most recently added */ + wl_list_for_each(client, &server.client.list, link) { + if(client->isfloating) + resize(client, client->geometry.x+monitor->window.width, client->geometry.y, + client->geometry.width, client->geometry.height, 0); + } + return; + } +} + +void +free_monitor(struct wl_listener *l, void *data) +{ + int i, len; + Client *client; + struct wlr_output *output = data; + Monitor *monitor = output->data; + + wl_list_remove(&monitor->event.destroy.link); + wl_list_remove(&monitor->event.render.link); + wl_list_remove(&monitor->link); + + wlr_output_layout_remove(server.monitor.layout, monitor->output); + + for(i=0, len=wl_list_length(&server.monitor.list); i < len; i++) { + server.monitor.selected = wl_container_of(server.monitor.list.prev, server.monitor.selected, link); + if(server.monitor.selected->output->enabled) + break; + } + + focus(focused_client(server.monitor.selected), 1); + + /* move closed monitor's clients to newly selected one */ + wl_list_for_each(client, &server.client.list, link) { + if(client->isfloating && client->geometry.x > monitor->geometry.width) + resize(client, + client->geometry.x - monitor->window.width, + client->geometry.y, + client->geometry.width, + client->geometry.height, + 0 + ); + if(client->monitor == monitor) + attach(client, monitor, client->tags); + } + + free(monitor); +} + +/* methods */ +void +arrange(Monitor *monitor) +{ + if(monitor->layout->arrange) + monitor->layout->arrange(monitor); +} + +void +stratum(Monitor *monitor, struct wl_list *list, struct wlr_box *area, int exclusive) +{ + Layer *layer; + struct wlr_box full = monitor->geometry; + + wl_list_for_each(layer, list, link) { + struct wlr_layer_surface_v1 *surface = layer->surface; + struct wlr_layer_surface_v1_state *state = &surface->current; + struct wlr_box bounds; + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height + }; + const uint32 horizontal = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + const uint32 vertical = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + if (exclusive != (state->exclusive_zone > 0)) + continue; + + bounds = state->exclusive_zone == -1 ? full : *area; + + // horizontal axis + if((state->anchor & horizontal) && box.width == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); + } + + // vertical axis + if((state->anchor & vertical) && box.height == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + + // margin + if((state->anchor & horizontal) == horizontal) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + + if((state->anchor & vertical) == vertical) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if(box.width < 0 || box.height < 0) { + wlr_layer_surface_v1_close(surface); + continue; + } + layer->geometry = box; + + if (state->exclusive_zone > 0) + exclude(area, + state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_v1_configure(surface, box.width, box.height); + } +} + +void +stratify(Monitor *monitor) +{ + int i; + Layer *layer; + struct wlr_box area = monitor->geometry; + uint32_t overlays[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server.input.seat); + + // arrange exclusive surfaces from top->bottom + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 1); + + if(memcmp(&area, &monitor->window, sizeof(area))) { + monitor->window = area; + arrange(monitor); + } + + // arrange non-exlusive surfaces from top->bottom + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 0); + + // find topmost keyboard interactive layer, if such a layer exists + for(i = 0; i < arrlen(overlays); i++) { + wl_list_for_each_reverse(layer, &monitor->layer[overlays[i]], link) { + if (layer->surface->current.keyboard_interactive && layer->surface->mapped) { + // Deactivate the focused client. + focus(nil, 0); + wlr_seat_keyboard_notify_enter( + server.input.seat, + layer->surface->surface, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers + ); + return; + } + } + } +} + +Client * +focused_client(Monitor *monitor) +{ + Client *client; + wl_list_for_each(client, &server.client.focus, focus) { + if(VISIBLE_ON(client, monitor)) + return client; + } + + return nil; +} + +void +tile(Monitor *monitor) +{ + Client *client; + uint i, n, h, mw, my, ty; + + n = 0; + wl_list_for_each(client, &server.client.list, link) { + if(VISIBLE_ON(client, monitor) && !client->isfloating) + n++; + } + if(!n) return; + + if(n > monitor->master.len) + mw = monitor->master.len ? monitor->window.width * monitor->master.frac : 0; + else + mw = monitor->window.width; + + i = my = ty = 0; + wl_list_for_each(client, &server.client.list, link) { + if(!VISIBLE_ON(client,monitor) || client->isfloating || client->isfullscreen) + continue; + if(i < monitor->master.len) { + h = (monitor->window.height - my) / (MIN(n, monitor->master.len) - i); + resize(client, monitor->window.x, monitor->window.y + my, mw, h, 0); + my += client->geometry.height; + } else { + h = (monitor->window.height - ty) / (n - i); + resize(client, monitor->window.x + mw, monitor->window.y + ty, monitor->window.width - mw, h, 0); + ty += client->geometry.height; + } + i++; + } +} + +Monitor * +monitor_at(double x, double y) +{ + struct wlr_output *output = wlr_output_layout_output_at(server.monitor.layout, x, y); + return output ? output->data : nil; +} diff --git a/src/cmd/wm/protocol/sync b/src/cmd/wm/protocol/sync new file mode 100755 index 0000000..19a728a --- /dev/null +++ b/src/cmd/wm/protocol/sync @@ -0,0 +1,6 @@ +#!/bin/sh + +for base in wlr-layer-shell-unstable-v1.xml +do + curl https://raw.githubusercontent.com/swaywm/wlroots/master/protocol/$base --output $base +done diff --git a/src/cmd/wm/protocol/wlr-layer-shell-unstable-v1.xml b/src/cmd/wm/protocol/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/src/cmd/wm/protocol/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, 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. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/src/cmd/wm/render.c b/src/cmd/wm/render.c new file mode 100644 index 0000000..1f51804 --- /dev/null +++ b/src/cmd/wm/render.c @@ -0,0 +1,160 @@ +#include "wm.h" + +struct Payload +{ + Client *client; + struct wlr_output *output; + struct timespec *when; + int x, y; +}; + +static +void +render(struct wlr_surface *surface, int sx, int sy, void *data) +{ + float matrix[9]; + double x, y; + struct Payload *payload; + + struct wlr_box box; + struct wlr_output *output; + struct wlr_texture *texture; + + enum wl_output_transform transform; + + payload = data; + output = payload->output; + + texture = wlr_surface_get_texture(surface); + if(!texture) + return; + + x = 0, y = 0; + wlr_output_layout_output_coords(server.monitor.layout, output, &x, &y); + + box = (struct wlr_box) { + .x = x + payload->x + sx, + .y = y + payload->y + sy, + .width = surface->current.width, + .height = surface->current.height, + }; + scale_box(&box, output->scale); + + transform = wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); + + wlr_render_texture_with_matrix(server.renderer, texture, matrix, 1); + wlr_surface_send_frame_done(surface, payload->when); + wlr_presentation_surface_sampled_on_output(server.present, surface, output); +} + +static +void +render_layer(struct wl_list *list, struct timespec *now) +{ + Layer *layer; + wl_list_for_each(layer, list, link) { + struct Payload payload= { + .output = layer->surface->output, + .x = layer->geometry.x, + .y = layer->geometry.y, + .when = now, + }; + + wlr_surface_for_each_surface(layer->surface->surface, render, &payload); + } +} + +static +void +render_clients(Monitor *monitor, struct timespec *now) +{ + double x, y; + int i, w, h, bw; + float *color; + + Client *client; + struct wlr_output *output; + struct wlr_box *borders; + struct wlr_surface *surface; + + output = monitor->output; + wl_list_for_each_reverse(client, &server.client.stack, stack) { + if(!VISIBLE_ON(client, client->monitor)) + continue; + if(!wlr_output_layout_intersects(server.monitor.layout, monitor->output, &client->geometry)) + continue; + + surface = client->xdg->surface; + + x = client->geometry.x, y = client->geometry.y; + wlr_output_layout_output_coords(server.monitor.layout, output, &x, &y); + + if((bw=client->border)) { + w = surface->current.width; + h = surface->current.height; + borders = (struct wlr_box[4]) { + {x, y, w+2*bw, bw}, /* top */ + {x, y+bw, bw, h}, /* left */ + {x+bw+w, y+bw, bw, h}, /* right */ + {x, y+bw+h, w+2*bw, bw}, /* bottom */ + }; + + color = (client == server.selected) ? cfg·focuscolor : cfg·bordercolor; + for(i=0; i<4; i++) { + scale_box(&borders[i], output->scale); + wlr_render_rect(server.renderer, &borders[i], color, output->transform_matrix); + } + } + + struct Payload payload = { + .output = output, + .when = now, + + .x = client->geometry.x + client->border, + .y = client->geometry.y + client->border, + }; + + wlr_xdg_surface_for_each_surface(client->xdg, render, &payload); + } +} + +void +render_monitor(struct wl_listener *l, void *data) +{ + int w, h; + Client *client; + Monitor *monitor; + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + monitor = wl_container_of(l, monitor, event.render); + + wl_list_for_each(client, &server.client.list, link) { + if(client->resize) { + wlr_surface_send_frame_done(client->xdg->surface, &now); + } + } + + if(!wlr_output_attach_render(monitor->output, nil)) + return; + + wlr_output_effective_resolution(monitor->output, &w, &h); + + /* start of rendering kernel */ + wlr_renderer_begin(server.renderer, w, h); + wlr_renderer_clear(server.renderer, cfg·rootcolor); + + render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); + render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); + + render_clients(monitor, &now); + + render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now); + render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now); + + wlr_output_render_software_cursors(monitor->output, nil); + + wlr_renderer_end(server.renderer); + wlr_output_commit(monitor->output); +} diff --git a/src/cmd/wm/rules.mk b/src/cmd/wm/rules.mk new file mode 100644 index 0000000..30d786d --- /dev/null +++ b/src/cmd/wm/rules.mk @@ -0,0 +1,62 @@ +include share/push.mk +# Iterate through subdirectory tree + +# local sources +SRCS_$(d):=\ + $(d)/xdg-shell-protocol.c\ + $(d)/wlr-layer-shell-unstable-v1-protocol.c\ + $(d)/util.c\ + $(d)/input.c\ + $(d)/render.c\ + $(d)/layer.c\ + $(d)/xdg.c\ + $(d)/client.c\ + $(d)/monitor.c\ + $(d)/main.c + +# local outputs +BINS_$(d) := $(d)/wm + +include share/paths.mk + +# Local rules +include share/dynamic.mk + +$(d)/xdg-shell-protocol.h: + @echo "MK $(notdir $@)";\ + $(WL_SCAN) server-header $(WL_PROTO)/stable/xdg-shell/xdg-shell.xml $@ + +$(d)/xdg-shell-protocol.c: $(d)/xdg-shell-protocol.h + @echo "MK $(notdir $@)";\ + $(WL_SCAN) private-code $(WL_PROTO)/stable/xdg-shell/xdg-shell.xml $@ + +$(d)/wlr-layer-shell-unstable-v1-protocol.h: + @echo "MK $(notdir $@)";\ + $(WL_SCAN) server-header $(dir $@)protocol/wlr-layer-shell-unstable-v1.xml $@ + +$(d)/wlr-layer-shell-unstable-v1-protocol.c: $(d)/wlr-layer-shell-unstable-v1-protocol.h + @echo "MK $(notdir $@)";\ + $(WL_SCAN) private-code $(dir $@)protocol/wlr-layer-shell-unstable-v1.xml $@ + +GENS+=\ + $(d)/xdg-shell-protocol.h\ + $(d)/xdg-shell-protocol.c\ + $(d)/wlr-layer-shell-unstable-v1-protocol.h\ + $(d)/wlr-layer-shell-unstable-v1-protocol.c + +$(BINS_$(d)): TCINCS=-I cmd/wm + +$(BINS_$(d)): TCFLAGS=\ + `$(PKG) --cflags wlroots`\ + `$(PKG) --cflags wayland-server`\ + `$(PKG) --cflags xkbcommon` + +$(BINS_$(d)): TCLIBS=\ + `$(PKG) --libs wlroots`\ + `$(PKG) --libs wayland-server`\ + `$(PKG) --libs xkbcommon`\ + +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/cmd/wm/util.c b/src/cmd/wm/util.c new file mode 100644 index 0000000..7871d15 --- /dev/null +++ b/src/cmd/wm/util.c @@ -0,0 +1,99 @@ +#include "wm.h" + +typedef struct { + uint32 singular_anchor; + uint32 anchor_triplet; + int *positive_axis; + int *negative_axis; + int margin; +} Edge; + +// ----------------------------------------------------------------------- +// general purpose function on rectangles + +void +scale_box(struct wlr_box *box, float scale) +{ + box->width = ROUND((box->x + box->width) * scale) - ROUND(box->x * scale); + box->height = ROUND((box->y + box->height) * scale) - ROUND(box->y * scale); + box->x = ROUND(box->x * scale); + box->y = ROUND(box->y * scale); +} + +void +exclude(struct wlr_box *usable_area, uint32 anchor, int32 exclusive, + int32 margin_top, int32 margin_right, int32 margin_bottom, int32 margin_left) +{ + Edge edges[] = { + { // Top + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + { // Bottom + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + { // Left + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + { // Right + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, + .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + } + }; + for(size_t i = 0; i < arrlen(edges); i++) { + if((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) + && exclusive + edges[i].margin > 0) { + if(edges[i].positive_axis) + *edges[i].positive_axis += exclusive + edges[i].margin; + if(edges[i].negative_axis) + *edges[i].negative_axis -= exclusive + edges[i].margin; + break; + } + } +} + +// ----------------------------------------------------------------------- +// user facing functions + +void +spawn(Arg *arg) +{ + wlr_log(WLR_DEBUG, "spawning %s", ((char **)arg->v)[0]); + if(!fork()) { + dup2(2, 1); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + } +} + +void +quit(Arg *arg) +{ + wl_display_terminate(server.display); +} + +#define CONFIG(a,b,...) a cfg·##b = __VA_ARGS__ +#include "config.h" +#undef CONFIG diff --git a/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.c b/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.c new file mode 100644 index 0000000..95ff317 --- /dev/null +++ b/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.c @@ -0,0 +1,93 @@ +/* Generated by wayland-scanner 1.19.0 */ + +/* + * Copyright © 2017 Drew DeVault + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, 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 +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface zwlr_layer_surface_v1_interface; + +static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + &zwlr_layer_surface_v1_interface, + &wl_surface_interface, + &wl_output_interface, + NULL, + NULL, + &xdg_popup_interface, +}; + +static const struct wl_message zwlr_layer_shell_v1_requests[] = { + { "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 }, + { "destroy", "3", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = { + "zwlr_layer_shell_v1", 4, + 2, zwlr_layer_shell_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwlr_layer_surface_v1_requests[] = { + { "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 }, + { "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 }, + { "destroy", "", wlr_layer_shell_unstable_v1_types + 0 }, + { "set_layer", "2u", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_layer_surface_v1_events[] = { + { "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 }, + { "closed", "", wlr_layer_shell_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = { + "zwlr_layer_surface_v1", 4, + 9, zwlr_layer_surface_v1_requests, + 2, zwlr_layer_surface_v1_events, +}; + diff --git a/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.h b/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.h new file mode 100644 index 0000000..ea2fa9b --- /dev/null +++ b/src/cmd/wm/wlr-layer-shell-unstable-v1-protocol.h @@ -0,0 +1,564 @@ +/* Generated by wayland-scanner 1.19.0 */ + +#ifndef WLR_LAYER_SHELL_UNSTABLE_V1_SERVER_PROTOCOL_H +#define WLR_LAYER_SHELL_UNSTABLE_V1_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1 protocol + * @section page_ifaces_wlr_layer_shell_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers of the desktop + * - @subpage page_iface_zwlr_layer_surface_v1 - layer metadata interface + * @section page_copyright_wlr_layer_shell_unstable_v1 Copyright + *
+ *
+ * Copyright © 2017 Drew DeVault
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * the copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, 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.
+ * 
+ */ +struct wl_output; +struct wl_surface; +struct xdg_popup; +struct zwlr_layer_shell_v1; +struct zwlr_layer_surface_v1; + +#ifndef ZWLR_LAYER_SHELL_V1_INTERFACE +#define ZWLR_LAYER_SHELL_V1_INTERFACE +/** + * @page page_iface_zwlr_layer_shell_v1 zwlr_layer_shell_v1 + * @section page_iface_zwlr_layer_shell_v1_desc Description + * + * Clients can use this interface to assign the surface_layer role to + * wl_surfaces. Such surfaces are assigned to a "layer" of the output and + * rendered with a defined z-depth respective to each other. They may also be + * anchored to the edges and corners of a screen and specify input handling + * semantics. This interface should be suitable for the implementation of + * many desktop shell components, and a broad number of other applications + * that interact with the desktop. + * @section page_iface_zwlr_layer_shell_v1_api API + * See @ref iface_zwlr_layer_shell_v1. + */ +/** + * @defgroup iface_zwlr_layer_shell_v1 The zwlr_layer_shell_v1 interface + * + * Clients can use this interface to assign the surface_layer role to + * wl_surfaces. Such surfaces are assigned to a "layer" of the output and + * rendered with a defined z-depth respective to each other. They may also be + * anchored to the edges and corners of a screen and specify input handling + * semantics. This interface should be suitable for the implementation of + * many desktop shell components, and a broad number of other applications + * that interact with the desktop. + */ +extern const struct wl_interface zwlr_layer_shell_v1_interface; +#endif +#ifndef ZWLR_LAYER_SURFACE_V1_INTERFACE +#define ZWLR_LAYER_SURFACE_V1_INTERFACE +/** + * @page page_iface_zwlr_layer_surface_v1 zwlr_layer_surface_v1 + * @section page_iface_zwlr_layer_surface_v1_desc Description + * + * An interface that may be implemented by a wl_surface, for surfaces that + * are designed to be rendered as a layer of a stacked desktop-like + * environment. + * + * Layer surface state (layer, size, anchor, exclusive zone, + * margin, interactivity) is double-buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * Attaching a null buffer to a layer surface unmaps it. + * + * Unmapping a layer_surface means that the surface cannot be shown by the + * compositor until it is explicitly mapped again. The layer_surface + * returns to the state it had right after layer_shell.get_layer_surface. + * The client can re-map the surface by performing a commit without any + * buffer attached, waiting for a configure event and handling it as usual. + * @section page_iface_zwlr_layer_surface_v1_api API + * See @ref iface_zwlr_layer_surface_v1. + */ +/** + * @defgroup iface_zwlr_layer_surface_v1 The zwlr_layer_surface_v1 interface + * + * An interface that may be implemented by a wl_surface, for surfaces that + * are designed to be rendered as a layer of a stacked desktop-like + * environment. + * + * Layer surface state (layer, size, anchor, exclusive zone, + * margin, interactivity) is double-buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * Attaching a null buffer to a layer surface unmaps it. + * + * Unmapping a layer_surface means that the surface cannot be shown by the + * compositor until it is explicitly mapped again. The layer_surface + * returns to the state it had right after layer_shell.get_layer_surface. + * The client can re-map the surface by performing a commit without any + * buffer attached, waiting for a configure event and handling it as usual. + */ +extern const struct wl_interface zwlr_layer_surface_v1_interface; +#endif + +#ifndef ZWLR_LAYER_SHELL_V1_ERROR_ENUM +#define ZWLR_LAYER_SHELL_V1_ERROR_ENUM +enum zwlr_layer_shell_v1_error { + /** + * wl_surface has another role + */ + ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0, + /** + * layer value is invalid + */ + ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1, + /** + * wl_surface has a buffer attached or committed + */ + ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2, +}; +#endif /* ZWLR_LAYER_SHELL_V1_ERROR_ENUM */ + +#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM +#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM +/** + * @ingroup iface_zwlr_layer_shell_v1 + * available layers for surfaces + * + * These values indicate which layers a surface can be rendered in. They + * are ordered by z depth, bottom-most first. Traditional shell surfaces + * will typically be rendered between the bottom and top layers. + * Fullscreen shell surfaces are typically rendered at the top layer. + * Multiple surfaces can share a single layer, and ordering within a + * single layer is undefined. + */ +enum zwlr_layer_shell_v1_layer { + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1, + ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2, + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3, +}; +#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */ + +/** + * @ingroup iface_zwlr_layer_shell_v1 + * @struct zwlr_layer_shell_v1_interface + */ +struct zwlr_layer_shell_v1_interface { + /** + * create a layer_surface from a surface + * + * Create a layer surface for an existing surface. This assigns + * the role of layer_surface, or raises a protocol error if another + * role is already assigned. + * + * Creating a layer surface from a wl_surface which has a buffer + * attached or committed is a client error, and any attempts by a + * client to attach or manipulate a buffer prior to the first + * layer_surface.configure call must also be treated as errors. + * + * After creating a layer_surface object and setting it up, the + * client must perform an initial commit without any buffer + * attached. The compositor will reply with a + * layer_surface.configure event. The client must acknowledge it + * and is then allowed to attach a buffer to map the surface. + * + * You may pass NULL for output to allow the compositor to decide + * which output to use. Generally this will be the one that the + * user most recently interacted with. + * + * Clients can specify a namespace that defines the purpose of the + * layer surface. + * @param layer layer to add this surface to + * @param namespace namespace for the layer surface + */ + void (*get_layer_surface)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface, + struct wl_resource *output, + uint32_t layer, + const char *namespace); + /** + * destroy the layer_shell object + * + * This request indicates that the client will not use the + * layer_shell object any more. Objects that have been created + * through this instance are not affected. + * @since 3 + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); +}; + + +/** + * @ingroup iface_zwlr_layer_shell_v1 + */ +#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_shell_v1 + */ +#define ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION 3 + +#ifndef ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM +#define ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM +/** + * @ingroup iface_zwlr_layer_surface_v1 + * request regular keyboard focus semantics + * + * This requests the compositor to allow this surface to be focused and + * unfocused by the user in an implementation-defined manner. The user + * should be able to unfocus this surface even regardless of the layer + * it is on. + * + * Typically, the compositor will want to use its normal mechanism to + * manage keyboard focus between layer shell surfaces with this setting + * and regular toplevels on the desktop layer (e.g. click to focus). + * Nevertheless, it is possible for a compositor to require a special + * interaction to focus or unfocus layer shell surfaces (e.g. requiring + * a click even if focus follows the mouse normally, or providing a + * keybinding to switch focus between layers). + * + * This setting is mainly intended for desktop shell components (e.g. + * panels) that allow keyboard interaction. Using this option can allow + * implementing a desktop shell that can be fully usable without the + * mouse. + */ +enum zwlr_layer_surface_v1_keyboard_interactivity { + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE = 0, + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE = 1, + /** + * @since 4 + */ + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND = 2, +}; +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND_SINCE_VERSION 4 +#endif /* ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ENUM */ + +#ifndef ZWLR_LAYER_SURFACE_V1_ERROR_ENUM +#define ZWLR_LAYER_SURFACE_V1_ERROR_ENUM +enum zwlr_layer_surface_v1_error { + /** + * provided surface state is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0, + /** + * size is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1, + /** + * anchor bitfield is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2, + /** + * keyboard interactivity is invalid + */ + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY = 3, +}; +#endif /* ZWLR_LAYER_SURFACE_V1_ERROR_ENUM */ + +#ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM +#define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM +enum zwlr_layer_surface_v1_anchor { + /** + * the top edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1, + /** + * the bottom edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2, + /** + * the left edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4, + /** + * the right edge of the anchor rectangle + */ + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8, +}; +#endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */ + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * @struct zwlr_layer_surface_v1_interface + */ +struct zwlr_layer_surface_v1_interface { + /** + * sets the size of the surface + * + * Sets the size of the surface in surface-local coordinates. The + * compositor will display the surface centered with respect to its + * anchors. + * + * If you pass 0 for either value, the compositor will assign it + * and inform you of the assignment in the configure event. You + * must set your anchor to opposite edges in the dimensions you + * omit; not doing so is a protocol error. Both values are 0 by + * default. + * + * Size is double-buffered, see wl_surface.commit. + */ + void (*set_size)(struct wl_client *client, + struct wl_resource *resource, + uint32_t width, + uint32_t height); + /** + * configures the anchor point of the surface + * + * Requests that the compositor anchor the surface to the + * specified edges and corners. If two orthogonal edges are + * specified (e.g. 'top' and 'left'), then the anchor point will be + * the intersection of the edges (e.g. the top left corner of the + * output); otherwise the anchor point will be centered on that + * edge, or in the center if none is specified. + * + * Anchor is double-buffered, see wl_surface.commit. + */ + void (*set_anchor)(struct wl_client *client, + struct wl_resource *resource, + uint32_t anchor); + /** + * configures the exclusive geometry of this surface + * + * Requests that the compositor avoids occluding an area with + * other surfaces. The compositor's use of this information is + * implementation-dependent - do not assume that this region will + * not actually be occluded. + * + * A positive value is only meaningful if the surface is anchored + * to one edge or an edge and both perpendicular edges. If the + * surface is not anchored, anchored to only two perpendicular + * edges (a corner), anchored to only two parallel edges or + * anchored to all edges, a positive value will be treated the same + * as zero. + * + * A positive zone is the distance from the edge in surface-local + * coordinates to consider exclusive. + * + * Surfaces that do not wish to have an exclusive zone may instead + * specify how they should interact with surfaces that do. If set + * to zero, the surface indicates that it would like to be moved to + * avoid occluding surfaces with a positive exclusive zone. If set + * to -1, the surface indicates that it would not like to be moved + * to accommodate for other surfaces, and the compositor should + * extend it all the way to the edges it is anchored to. + * + * For example, a panel might set its exclusive zone to 10, so that + * maximized shell surfaces are not shown on top of it. A + * notification might set its exclusive zone to 0, so that it is + * moved to avoid occluding the panel, but shell surfaces are shown + * underneath it. A wallpaper or lock screen might set their + * exclusive zone to -1, so that they stretch below or over the + * panel. + * + * The default value is 0. + * + * Exclusive zone is double-buffered, see wl_surface.commit. + */ + void (*set_exclusive_zone)(struct wl_client *client, + struct wl_resource *resource, + int32_t zone); + /** + * sets a margin from the anchor point + * + * Requests that the surface be placed some distance away from + * the anchor point on the output, in surface-local coordinates. + * Setting this value for edges you are not anchored to has no + * effect. + * + * The exclusive zone includes the margin. + * + * Margin is double-buffered, see wl_surface.commit. + */ + void (*set_margin)(struct wl_client *client, + struct wl_resource *resource, + int32_t top, + int32_t right, + int32_t bottom, + int32_t left); + /** + * requests keyboard events + * + * Set how keyboard events are delivered to this surface. By + * default, layer shell surfaces do not receive keyboard events; + * this request can be used to change this. + * + * This setting is inherited by child surfaces set by the get_popup + * request. + * + * Layer surfaces receive pointer, touch, and tablet events + * normally. If you do not want to receive them, set the input + * region on your surface to an empty region. + * + * Keyboard interactivity is double-buffered, see + * wl_surface.commit. + */ + void (*set_keyboard_interactivity)(struct wl_client *client, + struct wl_resource *resource, + uint32_t keyboard_interactivity); + /** + * assign this layer_surface as an xdg_popup parent + * + * This assigns an xdg_popup's parent to this layer_surface. This + * popup should have been created via xdg_surface::get_popup with + * the parent set to NULL, and this request must be invoked before + * committing the popup's initial state. + * + * See the documentation of xdg_popup for more details about what + * an xdg_popup is and how it is used. + */ + void (*get_popup)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *popup); + /** + * ack a configure event + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client must + * make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * If the client receives multiple configure events before it can + * respond to one, it only has to ack the last configure event. + * + * A client is not required to commit immediately after sending an + * ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before + * committing, but only the last request sent before a commit + * indicates which configure event the client really is responding + * to. + * @param serial the serial from the configure event + */ + void (*ack_configure)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); + /** + * destroy the layer_surface + * + * This request destroys the layer surface. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * change the layer of the surface + * + * Change the layer that the surface is rendered on. + * + * Layer is double-buffered, see wl_surface.commit. + * @param layer layer to move this surface to + * @since 2 + */ + void (*set_layer)(struct wl_client *client, + struct wl_resource *resource, + uint32_t layer); +}; + +#define ZWLR_LAYER_SURFACE_V1_CONFIGURE 0 +#define ZWLR_LAYER_SURFACE_V1_CLOSED 1 + +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_CLOSED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_layer_surface_v1 + */ +#define ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION 2 + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +zwlr_layer_surface_v1_send_configure(struct wl_resource *resource_, uint32_t serial, uint32_t width, uint32_t height) +{ + wl_resource_post_event(resource_, ZWLR_LAYER_SURFACE_V1_CONFIGURE, serial, width, height); +} + +/** + * @ingroup iface_zwlr_layer_surface_v1 + * Sends an closed event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +zwlr_layer_surface_v1_send_closed(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, ZWLR_LAYER_SURFACE_V1_CLOSED); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cmd/wm/wm.h b/src/cmd/wm/wm.h new file mode 100644 index 0000000..a263804 --- /dev/null +++ b/src/cmd/wm/wm.h @@ -0,0 +1,350 @@ +#pragma once + +#include +#include +#include +#include + +#define WLR_USE_UNSTABLE +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// ----------------------------------------------------------------------- +// macros + +#define ROUND(x) ((int)((x)+0.5)) +#define VISIBLE_ON(C,M) ((C)->monitor == (M) && ((C)->tags & (M)->tag.set[(M)->tag.selected])) + +// ----------------------------------------------------------------------- +// types + +enum +{ + CursorNormal, + CursorMove, + CursorResize, +}; + +typedef union Arg Arg; +typedef struct Button Button; +typedef struct Key Key; +typedef struct Keyboard Keyboard; +typedef struct Layer Layer; +typedef struct Client Client; +typedef struct Layout Layout; +typedef struct Monitor Monitor; +typedef struct Server Server; + +typedef struct Rule Rule; +typedef struct MonitorRule MonitorRule; + +struct Rectangle +{ + int x, y; + int w, h; +}; + +union Arg +{ + int i; + uint ui; + float f; + void *v; +}; + +struct Key +{ + uint modifier; + xkb_keysym_t sym; + void (*action)(Arg *); + Arg arg; +}; + +struct Button +{ + uint modifier; + uint code; + void (*function)(Arg *); + Arg arg; +}; + +struct Keyboard +{ + struct wl_list link; + struct wlr_input_device *device; + struct { + struct wl_listener press; + struct wl_listener modify; + struct wl_listener destroy; + } event; +}; + +struct Layer +{ + struct wl_list link; + struct wlr_layer_surface_v1 *surface; + enum zwlr_layer_shell_v1_layer type; + + struct wlr_box geometry; + + struct { + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener commit; + struct wl_listener destroy; + } event; +}; + +struct Client +{ + struct wl_list link; + struct wl_list stack; + struct wl_list focus; + + struct wlr_xdg_surface *xdg; + + struct { + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener commit; + struct wl_listener destroy; + struct wl_listener request_move; + struct wl_listener request_title; + struct wl_listener request_resize; + struct wl_listener request_fullscreen; + } event; + + struct wlr_box geometry, oldgeometry; + + Monitor *monitor; + + uint tags; + int border : 4; + int ismapped : 1; + int isfloating : 1; + int isurgent : 1; + int isfullscreen : 1; + + uint32 resize; +}; + +struct Layout +{ + char *symbol; + void (*arrange)(Monitor *); +}; + +struct Monitor +{ + struct wl_list link; + struct wlr_output *output; + struct { + struct wl_listener render; + struct wl_listener destroy; + } event; + + struct wlr_box geometry; + struct wlr_box window; + struct wl_list layer[4]; + + Layout *layout, *layouts[2]; + struct { + uint set[2]; + uint selected; + } tag; + struct { + double frac; + int len; + } master; +}; + +struct MonitorRule +{ + char *name; + Layout *layout; + int x, y; + float scale; + enum wl_output_transform transform; + struct { + double frac; + int len; + } master; +}; + +struct Rule +{ + char *id; + char *title; + uint tags; + int isfloating; + int monitor; +}; + +struct Server +{ + struct wl_display *display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_presentation *present; + struct wlr_xdg_activation_v1 *activate; + + struct { + struct wlr_xdg_shell *xdg; + struct wlr_layer_shell_v1 *layer; + } shell; + + struct { + struct wl_list list; + struct wl_list stack; + struct wl_list focus; + } client; + Client *selected; + + struct { + Client *client; + double x, y; + struct wlr_box box; + } grab; + uint32 resize; + + struct { + struct wlr_output_layout *layout; + struct wl_list list; + struct wlr_box geometry; + struct wlr_output_manager_v1 *manager; + Monitor *selected; + } monitor; + + struct { + struct wlr_cursor *dot; + struct wlr_xcursor_manager *manager; + int mode; + } cursor; + + struct { + struct wlr_seat *seat; + struct wl_list keyboards; + struct wlr_idle *idle; + } input; + + struct { + struct wl_listener make_input; + struct wl_listener make_monitor; + struct wl_listener make_xdg_surface; + struct wl_listener make_layer_surface; + + struct wl_listener monitor_test; + struct wl_listener monitor_apply; + struct wl_listener monitor_change; + + struct wl_listener cursor_move; + struct wl_listener cursor_move_abs; + struct wl_listener cursor_button; + struct wl_listener cursor_axis; + struct wl_listener cursor_frame; + + struct wl_listener request_cursor; + struct wl_listener request_activate; + struct wl_listener request_set_selection; + } event; +}; + +extern struct Server server; + +// ----------------------------------------------------------------------- +// functions + +/* util.c */ +void scale_box(struct wlr_box *, float); +void exclude(struct wlr_box *, uint32, int32, int32, int32, int32, int32 ); + +/* render.c */ +void render_monitor(struct wl_listener *, void *); + +/* xdg.c */ +void make_xdg_surface(struct wl_listener *, void *); + +/* layer.c */ +void make_layer_surface(struct wl_listener *, void *); + +/* input.c */ +void make_input(struct wl_listener *, void *); +void notify_move(uint32 time); + +void cursor_axis(struct wl_listener *, void *); +void cursor_frame(struct wl_listener *, void *); +void cursor_button(struct wl_listener *, void *); +void cursor_move(struct wl_listener *, void *); +void cursor_move_abs(struct wl_listener *, void *); + +void request_cursor(struct wl_listener *, void *); +void request_set_selection(struct wl_listener *, void *); + +/* client.c */ +void rules(Client *); +void focus(Client *, int lift); +void resize(Client *, int x, int y, int w, int h, int interact); +void attach(Client *, Monitor *, uint tags); +void floating(Client *, int); + +void move_client(Arg *arg); +void float_client(Arg *arg); +void resize_client(Arg *arg); + +void request_activate(struct wl_listener *, void *); + +Client *selected_client(void); +Client *client_at(double x, double y); +struct wlr_surface *client_surface_at(Client *, double cx, double cy, double *sx, double *sy); +struct wlr_surface *top_surface(Client *); + +/* monitor.c */ +void tile(Monitor *); +void arrange(Monitor *); +void stratify(Monitor *); +Client *focused_client(Monitor *); +Monitor *monitor_at(double x, double y); + +void monitor_test(struct wl_listener *, void *); +void monitor_apply(struct wl_listener *, void *); +void monitor_change(struct wl_listener *, void *); + +void free_monitor(struct wl_listener *, void *); +void make_monitor(struct wl_listener *, void *); + +#define CONFIG(a,b,...) extern a cfg·##b +#include "config.h" +#undef CONFIG diff --git a/src/cmd/wm/xdg-shell-protocol.c b/src/cmd/wm/xdg-shell-protocol.c new file mode 100644 index 0000000..62a2612 --- /dev/null +++ b/src/cmd/wm/xdg-shell-protocol.c @@ -0,0 +1,181 @@ +/* Generated by wayland-scanner 1.19.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 3, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 3, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 3, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 3, + 14, xdg_toplevel_requests, + 2, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 3, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/src/cmd/wm/xdg-shell-protocol.h b/src/cmd/wm/xdg-shell-protocol.h new file mode 100644 index 0000000..312e5b9 --- /dev/null +++ b/src/cmd/wm/xdg-shell-protocol.h @@ -0,0 +1,1676 @@ +/* Generated by wayland-scanner 1.19.0 */ + +#ifndef XDG_SHELL_SERVER_PROTOCOL_H +#define XDG_SHELL_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_interface + */ +struct xdg_wm_base_interface { + /** + * destroy xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is + * illegal and will result in a protocol error. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * create a positioner object + * + * Create a positioner object. A positioner object is used to + * position surfaces relative to some parent surface. See the + * interface description and xdg_surface.get_popup for details. + */ + void (*create_positioner)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); + /** + * create a shell surface from a surface + * + * This creates an xdg_surface for the given surface. While + * xdg_surface itself is not a role, the corresponding surface may + * only be assigned a role extending xdg_surface, such as + * xdg_toplevel or xdg_popup. It is illegal to create an + * xdg_surface for a wl_surface which already has an assigned role + * and this will result in a protocol error. + * + * This creates an xdg_surface for the given surface. An + * xdg_surface is used as basis to define a role to a given + * surface, such as xdg_toplevel or xdg_popup. It also manages + * functionality shared between xdg_surface based surface roles. + * + * See the documentation of xdg_surface for more details about what + * an xdg_surface is and how it is used. + */ + void (*get_xdg_surface)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface); + /** + * respond to a ping event + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping. + * @param serial serial of the ping event + */ + void (*pong)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + +#define XDG_WM_BASE_PING 0 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + * Sends an ping event to the client owning the resource. + * @param resource_ The client's resource + * @param serial pass this to the pong request + */ +static inline void +xdg_wm_base_send_ping(struct wl_resource *resource_, uint32_t serial) +{ + wl_resource_post_event(resource_, XDG_WM_BASE_PING, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * vertically resize the surface + * + * Resize the surface vertically so that it is completely unconstrained. + */ +enum xdg_positioner_constraint_adjustment { + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +/** + * @ingroup iface_xdg_positioner + * @struct xdg_positioner_interface + */ +struct xdg_positioner_interface { + /** + * destroy the xdg_positioner object + * + * Notify the compositor that the xdg_positioner will no longer + * be used. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the size of the to-be positioned rectangle + * + * Set the size of the surface that is to be positioned with the + * positioner object. The size is in surface-local coordinates and + * corresponds to the window geometry. See + * xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is + * raised. + * @param width width of positioned rectangle + * @param height height of positioned rectangle + */ + void (*set_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * set the anchor rectangle within the parent surface + * + * Specify the anchor rectangle within the parent surface that + * the child surface will be placed relative to. The rectangle is + * relative to the window geometry as defined by + * xdg_surface.set_window_geometry of the parent surface. + * + * When the xdg_positioner object is used to position a child + * surface, the anchor rectangle may not extend outside the window + * geometry of the positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + * @param x x position of anchor rectangle + * @param y y position of anchor rectangle + * @param width width of anchor rectangle + * @param height height of anchor rectangle + */ + void (*set_anchor_rect)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * set anchor rectangle anchor + * + * Defines the anchor point for the anchor rectangle. The + * specified anchor is used derive an anchor point that the child + * surface will be positioned relative to. If a corner anchor is + * set (e.g. 'top_left' or 'bottom_right'), the anchor point will + * be at the specified corner; otherwise, the derived anchor point + * will be centered on the specified edge, or in the center of the + * anchor rectangle if no edge is specified. + * @param anchor anchor + */ + void (*set_anchor)(struct wl_client *client, + struct wl_resource *resource, + uint32_t anchor); + /** + * set child surface gravity + * + * Defines in what direction a surface should be positioned, + * relative to the anchor point of the parent surface. If a corner + * gravity is specified (e.g. 'bottom_right' or 'top_left'), then + * the child surface will be placed towards the specified gravity; + * otherwise, the child surface will be centered over the anchor + * point on any axis that had no gravity specified. + * @param gravity gravity direction + */ + void (*set_gravity)(struct wl_client *client, + struct wl_resource *resource, + uint32_t gravity); + /** + * set the adjustment to be done when constrained + * + * Specify how the window should be positioned if the originally + * intended position caused the surface to be constrained, meaning + * at least partially outside positioning boundaries set by the + * compositor. The adjustment is set by constructing a bitmask + * describing the adjustment to be made when the surface is + * constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that + * the child surface should not change its position on that axis + * when constrained. + * + * If more than one bit for one axis is set, the order of how + * adjustments are applied is specified in the corresponding + * adjustment descriptions. + * + * The default adjustment is none. + * @param constraint_adjustment bit mask of constraint adjustments + */ + void (*set_constraint_adjustment)(struct wl_client *client, + struct wl_resource *resource, + uint32_t constraint_adjustment); + /** + * set surface position offset + * + * Specify the surface position offset relative to the position + * of the anchor on the anchor rectangle and the anchor on the + * surface. For example if the anchor of the anchor rectangle is at + * (x, y), the surface has the gravity bottom|right, and the offset + * is (ox, oy), the calculated surface position will be (x + ox, y + * + oy). The offset position of the surface is the one used for + * constraint testing. See set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user + * interface element, while aligning the user interface element of + * the parent surface with some user interface element placed + * somewhere in the popup surface. + * @param x surface position x offset + * @param y surface position y offset + */ + void (*set_offset)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y); + /** + * continuously reconstrain the surface + * + * When set reactive, the surface is reconstrained if the + * conditions used for constraining changed, e.g. the parent window + * moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, + * followed by an xdg_surface.configure event. + * @since 3 + */ + void (*set_reactive)(struct wl_client *client, + struct wl_resource *resource); + /** + * + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information + * to determine the future state the popup should be constrained + * using. If this doesn't match the dimension of the parent the + * popup is eventually positioned against, the behavior is + * undefined. + * + * The arguments are given in the surface-local coordinate space. + * @param parent_width future window geometry width of parent + * @param parent_height future window geometry height of parent + * @since 3 + */ + void (*set_parent_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t parent_width, + int32_t parent_height); + /** + * set parent configure this is a response to + * + * Set the serial of an xdg_surface.configure event this + * positioner will be used in response to. The compositor may use + * this information together with set_parent_size to determine what + * future state the popup should be constrained using. + * @param serial serial of parent configure event + * @since 3 + */ + void (*set_parent_configure)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_interface + */ +struct xdg_surface_interface { + /** + * destroy the xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be + * destroyed after its role object has been destroyed. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * assign the xdg_toplevel surface role + * + * This creates an xdg_toplevel object for the given xdg_surface + * and gives the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about + * what an xdg_toplevel is and how it is used. + */ + void (*get_toplevel)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); + /** + * assign the xdg_popup surface role + * + * This creates an xdg_popup object for the given xdg_surface and + * gives the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be + * specified using some other protocol, before committing the + * initial state. + * + * See the documentation of xdg_popup for more details about what + * an xdg_popup is and how it is used. + */ + void (*get_popup)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *parent, + struct wl_resource *positioner); + /** + * set the new window geometry + * + * The window geometry of a surface is its "visible bounds" from + * the user's perspective. Client-side decorations often have + * invisible portions like drop-shadows which should be ignored for + * the purposes of aligning, placing and constraining windows. + * + * The window geometry is double buffered, and will be applied at + * the time wl_surface.commit of the corresponding wl_surface is + * called. + * + * When maintaining a position, the compositor should treat the (x, + * y) coordinate of the window geometry as the top left corner of + * the window. A client changing the (x, y) window geometry + * coordinate should in general not alter the position of the + * window. + * + * Once the window geometry of the surface is set, it is not + * possible to unset it, and it will remain the same until + * set_window_geometry is called again, even if a new subsurface or + * buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface. + * + * The width and height must be greater than zero. Setting an + * invalid size will raise an error. When applied, the effective + * window geometry will be the set window geometry clamped to the + * bounding rectangle of the combined geometry of the surface of + * the xdg_surface and the associated subsurfaces. + */ + void (*set_window_geometry)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * ack a configure event + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client must + * make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use + * this information to move a surface to the top left only when the + * client has drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it can + * respond to one, it only has to ack the last configure event. + * + * A client is not required to commit immediately after sending an + * ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before + * committing, but only the last request sent before a commit + * indicates which configure event the client really is responding + * to. + * @param serial the serial from the configure event + */ + void (*ack_configure)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + +#define XDG_SURFACE_CONFIGURE 0 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + * @param serial serial of the configure event + */ +static inline void +xdg_surface_send_configure(struct wl_resource *resource_, uint32_t serial) +{ + wl_resource_post_event(resource_, XDG_SURFACE_CONFIGURE, serial); +} + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * the surface is tiled + * + * The window is currently in a tiled layout and the bottom edge is + * considered to be adjacent to another part of the tiling grid. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_interface + */ +struct xdg_toplevel_interface { + /** + * destroy the xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the parent of this surface + * + * Set the "parent" of this surface. This surface should be + * stacked above the parent surface and all other ancestor + * surfaces. + * + * Parent windows should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the + * dialog is raised. + * + * Setting a null parent for a child window removes any + * parent-child relationship for the child. Setting a null parent + * for a window which currently has no parent is a no-op. + * + * If the parent is unmapped then its children are managed as + * though the parent of the now-unmapped parent has become the + * parent of this surface. If no parent exists for the now-unmapped + * parent then the children are managed as though they have no + * parent surface. + */ + void (*set_parent)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent); + /** + * set surface title + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ + void (*set_title)(struct wl_client *client, + struct wl_resource *resource, + const char *title); + /** + * set application ID + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group + * multiple surfaces together, or to determine how to launch a new + * application. + * + * For D-Bus activatable applications, the app ID is used as the + * D-Bus service name. + * + * The compositor shell will try to group application surfaces + * together by their app ID. As a best practice, it is suggested to + * select app ID's that match the basename of the application's + * .desktop file. For example, "org.freedesktop.FooViewer" where + * the .desktop file is "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after + * the xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] http://standards.freedesktop.org/desktop-entry-spec/ + */ + void (*set_app_id)(struct wl_client *client, + struct wl_resource *resource, + const char *app_id); + /** + * show the window menu + * + * Clients implementing client-side decorations might want to + * show a context menu when right-clicking on the decorations, + * giving the user a menu that they can use to maximize or minimize + * the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu + * items the window menu contains. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + * @param x the x position to pop up the window menu at + * @param y the y position to pop up the window menu at + */ + void (*show_window_menu)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + int32_t x, + int32_t y); + /** + * start an interactive move + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * passed serial is used to determine the type of interactive move + * (touch, pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed + * serial is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, + * such as updating a pointer cursor, during the move. There is no + * guarantee that the device focus will return when the move is + * completed. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + */ + void (*move)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial); + /** + * start an interactive resize + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * passed serial is used to determine the type of interactive + * resize (touch, pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the + * "resize" enum value for more details about what is required. The + * client must also acknowledge configure events using + * "ack_configure". After the resize is completed, the client will + * receive another "configure" event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is + * no guarantee that the device focus will return when the resize + * is completed. + * + * The edges parameter specifies how the surface should be resized, + * and is one of the values of the resize_edge enum. The compositor + * may use this information to update the surface position for + * example when dragging the top left corner. The compositor may + * also use this information to adapt its behavior, e.g. choose an + * appropriate cursor image. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + * @param edges which edge or corner is being dragged + */ + void (*resize)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + uint32_t edges); + /** + * set the maximum size + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor + * does not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry + * coordinates. See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get + * applied on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. As a + * result, a client wishing to reset the maximum size to an + * unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in a protocol error. + * + * The width and height must be greater than or equal to zero. + * Using strictly negative values for width and height will result + * in a protocol error. + */ + void (*set_max_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * set the minimum size + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor + * does not try to configure the window below this size. + * + * The width and height arguments are in window geometry + * coordinates. See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get + * applied on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. As a + * result, a client wishing to reset the minimum size to an + * unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in a protocol error. + * + * The width and height must be greater than or equal to zero. + * Using strictly negative values for width and height will result + * in a protocol error. + */ + void (*set_min_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * maximize the window + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the + * compositor will respond by emitting a configure event. Whether + * this configure actually sets the window maximized is subject to + * compositor policies. The client must then update its content, + * drawing in the configured state. The client must also + * acknowledge the configure when committing the new content (see + * ack_configure). + * + * It is up to the compositor to decide how and where to maximize + * the surface, for example which output and what region of the + * screen should be used. + * + * If the surface was already maximized, the compositor will still + * emit a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no + * direct effect. It may alter the state the surface is returned to + * when unmaximized unless overridden by the compositor. + */ + void (*set_maximized)(struct wl_client *client, + struct wl_resource *resource); + /** + * unmaximize the window + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the + * compositor will respond by emitting a configure event. Whether + * this actually un-maximizes the window is subject to compositor + * policies. If available and applicable, the compositor will + * include the window geometry dimensions the window had prior to + * being maximized in the configure event. The client must then + * update its content, drawing it in the configured state. The + * client must also acknowledge the configure when committing the + * new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before + * maximizing, if applicable. + * + * If the surface was already not maximized, the compositor will + * still emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no + * direct effect. It may alter the state the surface is returned to + * when unmaximized unless overridden by the compositor. + */ + void (*unset_maximized)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the window as fullscreen on an output + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether + * the client is actually put into a fullscreen state is subject to + * compositor policies. The client must also acknowledge the + * configure when committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's + * preference as to which display it should be set fullscreen on. + * If this value is NULL, it's up to the compositor to choose which + * display will be used to map this surface. + * + * If the surface doesn't cover the whole output, the compositor + * will position the surface in the center of the output and + * compensate with with border fill covering the rest of the + * output. The content of the border fill is undefined, but should + * be assumed to be in some way that attempts to blend into the + * surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must + * make sure that other screen content not part of the same surface + * tree (made up of subsurfaces, popups or similarly coupled + * surfaces) are not visible below the fullscreened surface. + */ + void (*set_fullscreen)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output); + /** + * unset the window as fullscreen + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. Whether + * this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based + * on the following: * the state(s) it may have had before becoming + * fullscreen * any state(s) decided by the compositor * any + * state(s) requested by the client while the surface was + * fullscreen + * + * The compositor may include the previous window geometry + * dimensions in the configure event, if applicable. + * + * The client must also acknowledge the configure when committing + * the new content (see ack_configure). + */ + void (*unset_fullscreen)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the window as minimized + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ + void (*set_minimized)(struct wl_client *client, + struct wl_resource *resource); +}; + +#define XDG_TOPLEVEL_CONFIGURE 0 +#define XDG_TOPLEVEL_CLOSE 1 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_toplevel + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_toplevel_send_configure(struct wl_resource *resource_, int32_t width, int32_t height, struct wl_array *states) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_CONFIGURE, width, height, states); +} + +/** + * @ingroup iface_xdg_toplevel + * Sends an close event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_toplevel_send_close(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_CLOSE); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_interface + */ +struct xdg_popup_interface { + /** + * remove xdg_popup interface + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, a protocol error + * will be sent. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * make the popup take an explicit grab + * + * This request makes the created popup take an explicit grab. An + * explicit grab will be dismissed when the user dismisses the + * popup, or when the client destroys the xdg_popup. This can be + * done by the user clicking outside the surface, using the + * keyboard, or even locking the screen through closing the lid or + * a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * serial number of the event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel + * surface or another xdg_popup with an explicit grab. If the + * parent is another xdg_popup it means that the popups are nested, + * with this popup now being the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were + * created in, e.g. the only popup you are allowed to destroy at + * all times is the topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss + * every nested grabbing popup as well. When a compositor dismisses + * popups, it will follow the same dismissing order as required + * from the client. + * + * The parent of a grabbing popup must either be another xdg_popup + * with an active explicit grab, or an xdg_popup or xdg_toplevel, + * if there are no explicit grabs already taken. + * + * If the topmost grabbing popup is destroyed, the grab will be + * returned to the parent of the popup, if that parent previously + * had an explicit grab. + * + * If the parent is a grabbing popup which has already been + * dismissed, this popup will be immediately dismissed. If the + * parent is a popup that did not take an explicit grab, an error + * will be raised. + * + * During a popup grab, the client owning the grab will receive + * pointer and touch events for all their surfaces as normal + * (similar to an "owner-events" grab in X11 parlance), while the + * top most grabbing popup will always have keyboard focus. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + */ + void (*grab)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial); + /** + * recalculate the popup's location + * + * Reposition an already-mapped popup. The popup will be placed + * given the details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any + * parameters set by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not + * take effect until the corresponding configure event is + * acknowledged by the client. See xdg_popup.repositioned for + * details. The token itself is opaque, and has no other special + * meaning. + * + * If multiple reposition requests are sent, the compositor may + * skip all but the last one. + * + * If the popup is repositioned in response to a configure event + * for its parent, the client should send an + * xdg_positioner.set_parent_configure and possibly an + * xdg_positioner.set_parent_size request to allow the compositor + * to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is + * being resized, but not in response to a configure event, the + * client should send an xdg_positioner.set_parent_size request. + * @param token reposition request token + * @since 3 + */ + void (*reposition)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *positioner, + uint32_t token); +}; + +#define XDG_POPUP_CONFIGURE 0 +#define XDG_POPUP_POPUP_DONE 1 +#define XDG_POPUP_REPOSITIONED 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ +static inline void +xdg_popup_send_configure(struct wl_resource *resource_, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_resource_post_event(resource_, XDG_POPUP_CONFIGURE, x, y, width, height); +} + +/** + * @ingroup iface_xdg_popup + * Sends an popup_done event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_popup_send_popup_done(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, XDG_POPUP_POPUP_DONE); +} + +/** + * @ingroup iface_xdg_popup + * Sends an repositioned event to the client owning the resource. + * @param resource_ The client's resource + * @param token reposition request token + */ +static inline void +xdg_popup_send_repositioned(struct wl_resource *resource_, uint32_t token) +{ + wl_resource_post_event(resource_, XDG_POPUP_REPOSITIONED, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cmd/wm/xdg.c b/src/cmd/wm/xdg.c new file mode 100644 index 0000000..6a0c2c8 --- /dev/null +++ b/src/cmd/wm/xdg.c @@ -0,0 +1,118 @@ +#include "wm.h" + +static +void +map(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.map); + + wl_list_insert(&server.client.list, &client->link); + wl_list_insert(&server.client.stack, &client->stack); + wl_list_insert(&server.client.focus, &client->focus); + + wlr_xdg_surface_get_geometry(client->xdg, &client->geometry); + client->geometry.width += 2 * client->border; + client->geometry.height += 2 * client->border; + + wlr_xdg_toplevel_set_tiled(client->xdg, + WLR_EDGE_TOP|WLR_EDGE_BOTTOM|WLR_EDGE_LEFT|WLR_EDGE_RIGHT + ); + + rules(client); +} + +static +void +unmap(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.unmap); + + wl_list_remove(&client->link); + attach(client, nil, 0); + + wl_list_remove(&client->stack); + wl_list_remove(&client->focus); +} + +static +void +commit(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.commit); + if(client->resize && client->resize <= client->xdg->configure_serial) + client->resize = 0; +} + +static +void +destroy(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.destroy); + free(client); +} + +static +void +request_move(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.request_move); +} + +static +void +request_resize(struct wl_listener *l, void *data) +{ + struct wlr_xdg_toplevel_resize_event *event = data; + Client *client = wl_container_of(l, client, event.request_resize); +} + + +static +void +request_title(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.request_title); +} + +static +void +request_fullscreen(struct wl_listener *l, void *data) +{ + Client *client = wl_container_of(l, client, event.request_fullscreen); + client->isfullscreen = 1; +} + +void +make_xdg_surface(struct wl_listener *l, void *data) +{ + Client *client; + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_surface *xdg = data; + + if(xdg->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) + return; + + client = xdg->surface->data = calloc(1, sizeof(*client)); + client->xdg = xdg; + client->border = cfg·borderpixel; + + client->event.map.notify = map; + wl_signal_add(&xdg->events.map, &client->event.map); + client->event.unmap.notify = unmap; + wl_signal_add(&xdg->events.unmap, &client->event.unmap); + client->event.destroy.notify = destroy; + wl_signal_add(&xdg->events.destroy, &client->event.destroy); + + client->event.commit.notify = commit; + wl_signal_add(&xdg->surface->events.commit, &client->event.commit); + + toplevel = xdg->toplevel; + client->event.request_move.notify = request_move; + wl_signal_add(&toplevel->events.request_move, &client->event.request_move); + client->event.request_title.notify = request_title; + wl_signal_add(&toplevel->events.set_title, &client->event.request_title); + client->event.request_resize.notify = request_resize; + wl_signal_add(&toplevel->events.request_resize, &client->event.request_resize); + client->event.request_fullscreen.notify = request_fullscreen; + wl_signal_add(&toplevel->events.request_fullscreen, &client->event.request_fullscreen); +} diff --git a/src/libbio/align.c b/src/libbio/align.c new file mode 100644 index 0000000..20a57df --- /dev/null +++ b/src/libbio/align.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include + +// ----------------------------------------------------------------------- +// globals + +uint64 aln·shft = (2ULL * (aln·K - 1ULL)); +uint64 aln·mask = (1ULL << (2*aln·K)) - 1ULL; + +// ----------------------------------------------------------------------- +// static data + +static uint64 nuctab[256] = { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +}; + +// ----------------------------------------------------------------------- +// hash functions + +enum +{ + MURM641 = 0xff51afd7ed558ccd, + MURM642 = 0xc4ceb9fe1a85ec53 +}; + +static +uint64 +minihash(uint64 x, uint64 mask) +{ + x = (~x + (x << 21)) & mask; + x = x ^ (x >> 24); + x = (x + (x << 3) + (x << 8)) & mask; + x = x ^ (x >> 14); + x = (x + (x << 2) + (x << 4)) & mask; + x = x ^ (x >> 28); + x = (x + (x << 31)); + + return x; +} + +static +uint64 +murmurhash(uint64 x, uint64 mask) +{ + x = x ^ (x >> 33); + x = (x * MURM641); + x = x ^ (x >> 33); + x = (x * MURM642); + x = x ^ (x >> 33); + + return x; +} + +// ----------------------------------------------------------------------- +// locality sensitive hashing (with spatial extent) + +static +void +sortpos(uintptr len, uint64 vals[], int locs[]) +{ + int tmpi; + uint64 tmpu64; + +#define LESS(i, j) (locs[i] < locs[j]) +#define SWAP(i, j) (tmpu64 = vals[i], tmpi = locs[i], \ + vals[i] = vals[j], locs[i] = locs[j], \ + vals[j] = tmpu64 , locs[j] = tmpi) + QSORT(len, LESS, SWAP); +#undef LESS +#undef SWAP +} + +/* + * sketch + * @param seq: '0' terminated string + * @param len: number of sequential sketches to keep + * @param vals: buffer to store hashes of sketch. + * @param locs: buffer to store location of sketch hashes + */ +error +aln·sketch(byte *seq, int len, uint64 *vals[aln·N], int *locs[aln·N]) +{ + int i, n, l, *loc; + uint64 kmer, h[3], *val; + int tmpi[2]; + uint64 tmpu[2]; + + for(n = 0; n < aln·N; n++) { + for(l = 0; l < len; l++) { + vals[n][l] = UINT64_MAX; + } + } + + kmer = UINT64_MAX; + for(l = 0; *seq != '\0'; seq++, l++) { + kmer = ((kmer << 2) | nuctab[*seq]) & aln·mask; + + h[0] = minihash(kmer, aln·mask); + h[1] = murmurhash(kmer, aln·mask); + + for(n = 0; n < aln·N; n++) { + val = vals[n]; + loc = locs[n]; + + h[2] = (h[0] + n * h[1]) & aln·mask; + for (i = 0; i < len && h[2] < val[i]; i++) { + ; + } + + tmpu[1] = h[2]; + tmpi[1] = l; + for(i -= 1; i >= 0; i--) { + tmpu[0] = tmpu[1], tmpu[1] = val[i], val[i] = tmpu[0]; + tmpi[0] = tmpi[1], tmpi[1] = loc[i], loc[i] = tmpi[0]; + } + } + } + + for(n = 0; n < aln·N; n++) { + sortpos(len, vals[n], locs[n]); + } + + return 0; +} + +static +int +lessarrs(int len, uint64 a[], uint64 b[]) +{ + int l; + + for(l = 0; l < len; l++) { + if (a[l] < b[l]) return 1; + if (a[l] > b[l]) return 0; + } + + return 0; +} + +static +void +swaparrs(int len, uint64 a[], uint64 b[]) +{ + int l; + uint64 tmp; + + for(l = 0; l < len; l++) { + tmp = a[l], a[l] = b[l], b[l] = tmp; + } +} + +error +aln·sort(uintptr n, int len, uint64 *vals) +{ +#define LESS(i, j) (lessarrs(len, vals+((i)*len), vals+((j)*len))) +#define SWAP(i, j) (swaparrs(len, vals+((i)*len), vals+((j)*len))) + QSORT(n, LESS, SWAP); +#undef LESS +#undef SWAP + return 0; +} diff --git a/src/libbio/fasta.c b/src/libbio/fasta.c new file mode 100644 index 0000000..3788544 --- /dev/null +++ b/src/libbio/fasta.c @@ -0,0 +1,393 @@ +#include +#include +#include + +#define INIT_NM_SIZE 128 +#define INIT_SQ_SIZE 4096 + +struct SeqBuf +{ + mem·Allocator mem; + void *heap; + + int cap, off; + byte *it, b[]; +}; + +static +void +reset(struct SeqBuf *sb) +{ + sb->off = 0; + sb->it = sb->b; +} + +static +error +grow(struct SeqBuf **sb, int min) +{ + void* heap; + mem·Allocator mem; + + vlong newcap; + struct SeqBuf *old, *new; + + old = *sb; + mem = old->mem; + heap = old->heap; + + assert((*sb)->cap <= (SIZE_MAX - 1) / 2); + newcap = MAX(16, MAX(1 + 2*(*sb)->cap, (*sb)->cap+min)); + assert(newcap >= (*sb)->cap+min); + + if(new = mem.alloc(heap, 1, sizeof(*new)+newcap), !new) { + errorf("memory: could not allocate new buffer\n"); + return 1; + } + + memcpy(new, old, sizeof(*new) + (*sb)->cap); + + new->cap = newcap; + new->it = new->b + (old->it - old->b); + mem.free(heap, old); + + *sb = new; + return 0; +} + +static +error +put(struct SeqBuf **sb, byte c) +{ + int err; + struct SeqBuf *sq; + + sq = *sb; + if(sq->it < (sq->b + sq->cap)) { + *sq->it++ = c; + return 0; + } + + if(err = grow(sb, 1), err) { + errorf("memory fail: could not allocate more buffer\n"); + sq->mem.free(sq->heap, sq); + return 1; + } + + *((*sb)->it++) = c; + return 0; +} + +static +error +push(struct SeqBuf **sb, int n, void *buf) +{ + int d, err; + struct SeqBuf *seq; + + seq = *sb; + if(d = (seq->cap - (seq->it - seq->b)), d < n) { + assert(d >= 0); + if (err = grow(sb, n-d), err) { + errorf("memory fail: could not allocate more buffer\n"); + seq->mem.free(seq->heap, seq); + return 1; + } + } + seq = *sb; + + memcpy(seq->it, buf, n); + seq->it += n; + + return 0; +} + +// ----------------------------------------------------------------------- +// sequence data + +struct bio·SeqReader { + byte eof; + io·Reader rdr; + void *io; + + struct SeqBuf *seq; + + /* read buffer */ + byte *b, *bend, buf[4*4098]; +}; + +static +error +fill(bio·SeqReader *rdr) +{ + int n; + // NOTE: This could lead to an infinite loop. + if(rdr->eof) + return 0; + + n = rdr->rdr.read(rdr->io, 1, arrlen(rdr->buf), rdr->buf); + if(n < 0) { + errorf("read: no data obtained from reader\n"); + return 1; + } + rdr->b = rdr->buf; + rdr->bend = rdr->b + n; + if(rdr->eof = (n < arrlen(rdr->buf)), rdr->eof) + *rdr->bend++ = '\0'; + + return 0; +} + +bio·SeqReader* +bio·openseq(io·Reader rdr, void *io, mem·Allocator mem, void *heap) +{ + error err; + bio·SeqReader *r; + + r = mem.alloc(heap, 1, sizeof(bio·SeqReader)); + r->rdr = rdr; + r->io = io; + r->eof = 0; + + r->seq = mem.alloc(heap, 1, sizeof(*r->seq) + INIT_NM_SIZE + INIT_SQ_SIZE); + r->seq->mem = mem; + r->seq->heap = heap; + r->seq->it = r->seq->b; + r->seq->cap = INIT_NM_SIZE + INIT_SQ_SIZE; + + if (err=fill(r), err) { + errorf("fill: could not populate buffer\n"); + goto ERROR; + } + + return r; + +ERROR: + mem.free(heap, r->seq); + mem.free(heap, r); + return nil; +} + +error +bio·closeseq(bio·SeqReader *rdr) +{ + mem·Allocator mem; + void *heap; + + mem = rdr->seq->mem; + heap = rdr->seq->heap; + + mem.free(heap, rdr->seq); + mem.free(heap, rdr); + + return 0; +} + + +static +error +readfasta(bio·SeqReader *rdr, bio·Seq *seq, byte hdr, byte stop) +{ + error err; + byte *beg; + + if(rdr->eof && rdr->b == rdr->bend-1) + return EOF; + + reset(rdr->seq); + + // NOTE: Can this case happen? + assert(rdr->b != rdr->bend); + if(*rdr->b++ != hdr) { + errorf("fasta/q format: expected '%c', found '%c'\n", hdr, *rdr->b--); + return 1; + } + +NAME: + beg = rdr->b; + while(rdr->b != rdr->bend) { + if(*rdr->b++ == '\n') { + push(&rdr->seq, (rdr->b - 1) - beg, beg); + goto SEQ; + } + } + push(&rdr->seq, rdr->b - beg, beg); + + if(err=fill(rdr), err) { + errorf("read: could not populate buffer\n"); + return 1; + } + goto NAME; + +SEQ: + put(&rdr->seq, '\0'); + rdr->seq->off = rdr->seq->it - rdr->seq->b; + +SEQLOOP: + beg = rdr->b; + while(rdr->b != rdr->bend) { + if(*rdr->b == '\n') { + push(&rdr->seq, rdr->b - beg, beg); + beg = rdr->b + 1; + } + + if(*rdr->b == stop || *rdr->b == '\0') + goto SUCCESS; + + rdr->b++; + } + + push(&rdr->seq, rdr->b - beg, beg); + + if(err=fill(rdr), err) { + errorf("read: could not populate buffer\n"); + return 1; + } + goto SEQLOOP; + +SUCCESS: + push(&rdr->seq, rdr->b - beg, beg); + put(&rdr->seq, '\0'); + + return 0; +} + +/* + * fasta files + */ + +error +bio·readfasta(bio·SeqReader *rdr, bio·Seq *seq) +{ + error err; + + err = readfasta(rdr, seq, '>', '>'); + if(err && err != EOF) { + errorf("parse fail: could not read sequence of record\n"); + return err; + } + + seq->name = rdr->seq->b; + seq->s = rdr->seq->b + rdr->seq->off; + seq->len = rdr->seq->it - seq->s - 1; // shift by 1 as we pushed a '0' to end + seq->q = nil; + + return err; +} + +/* + * fastq files + */ + +error +bio·readfastq(bio·SeqReader *rdr, bio·Seq *seq) +{ + int n; + byte *beg; + error err; + + err = readfasta(rdr, seq, '@', '+'); + if(err) { + errorf("parse fail: could not read sequence of record\n"); + return err; + } + + seq->len = rdr->seq->it - (rdr->seq->b + rdr->seq->off); + + if(*rdr->b++ != '+') { + errorf("format error: no '+' character seperator found\n"); + return -1; + } + +EATLN: + while(rdr->b != rdr->bend) { + if (*rdr->b++ == '\n') { + n = 0; + goto QUAL; + } + } + + if(err = fill((bio·SeqReader*)rdr), err) { + errorf("read: could not populate buffer\n"); + return 1; + } + goto EATLN; + +QUAL: + beg = rdr->b; + while(rdr->b != rdr->bend) { + if(*rdr->b == '\n') { + push(&rdr->seq, rdr->b - beg, beg); + beg = rdr->b + 1; + } + + if(n++ == seq->len || *rdr->b == '\0') { + err = *rdr->b == '\0' ? EOF : 0; + goto SUCCESS; + } + + rdr->b++; + } + + push(&rdr->seq, rdr->b - beg, beg); + + if(err = fill((bio·SeqReader*)rdr), err) { + errorf("read: could not populate buffer\n"); + return 1; + } + goto QUAL; + + +SUCCESS: + push(&rdr->seq, rdr->b - beg, beg); + put(&rdr->seq, '\0'); + + seq->name = rdr->seq->b; + seq->s = rdr->seq->b + rdr->seq->off - 1; + seq->q = seq->s + seq->len + 1; + + return err; +} + +// ----------------------------------------------------------------------- +// sequence writing + +error +bio·writefasta(io·Writer io, void *wtr, bio·Seq seq) +{ + int i, j, d; + char buf[2048], *b = buf, *e = arrend(buf); + + *b++ = '>'; + while(*seq.name) { + *b++ = *seq.name++; + if(b == e) { + io.write(wtr, 1, arrlen(buf), buf); + b = buf; + } + } + + for(i=0; i +#include +#include + +// ----------------------------------------------------------------------- +// Tokens + +enum TokenKind +{ + tok·nil, + tok·eof, + tok·space, + tok·ident, + tok·number, + tok·lparen, + tok·rparen, + tok·lbrak, + tok·rbrak, + tok·comma, + tok·semi, + tok·colon, + + NUM_TOKENS, +}; + + +struct Token { + enum TokenKind kind; + union + { + byte *s; + double x; + } lit; +}; + +static +byte* +tokstr(struct Token tok) +{ + static byte b[50]; + switch (tok.kind) { + case tok·nil: return ""; + case tok·eof: return nil; + case tok·space: return " "; + case tok·ident: return tok.lit.s; + case tok·lparen: return "("; + case tok·rparen: return ")"; + case tok·lbrak: return "["; + case tok·rbrak: return "]"; + case tok·comma: return ","; + case tok·semi: return ";"; + case tok·colon: return ":"; + case tok·number: + snprintf(b, arrlen(b), "%f", tok.lit.x); + return b; + default: + return nil; + } +} + + +// ----------------------------------------------------------------------- +// Read + +// TODO: Bounds checking on buffer +static +struct Token +lex(io·Peeker s, void* fp) +{ +#define isvalidchar(C) ((C) == '!') || \ + ('\"' < (C) && (C) < '\'') || \ + (')' < (C) && (C) < '+') || \ + (',' < (C) && (C) < ':') || \ + (':' < (C) && (C) < '[') || \ + ((C) == '\\') || \ + (']' < (C) && (C) <= '~') + byte *c; + struct Token tok; + static byte b[1024]; + c = b; + *c = s.get(fp); + + if (isspace(*c)) { + while (isspace(*c)) { + *(++c) = s.get(fp); + } + + s.unget(fp, *c); + assert(c - b < 1024); + + *c = 0; + tok.kind = tok·space; + tok.lit.s = b; + return tok; + } + + switch (*c) { + case EOF: tok.kind = tok·eof; return tok; + case '(': tok.kind = tok·lparen; return tok; + case ')': tok.kind = tok·rparen; return tok; + case '[': tok.kind = tok·lbrak; return tok; + case ']': tok.kind = tok·rbrak; return tok; + case ',': tok.kind = tok·comma; return tok; + case ';': tok.kind = tok·semi; return tok; + case ':': tok.kind = tok·colon; return tok; + + case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + while (isdigit(*c)) { + NUM: *(++c) = s.get(fp); + } + if (*c == '.') goto NUM; + if (isvalidchar(*c)) goto IDENT; + + s.unget(fp, *c); + assert(c - b < 1024); + + *c = 0; + tok.kind = tok·number; + tok.lit.x = atof(b); + return tok; + + case '\"': + while ((*c) != '\"') { + *(++c) = s.get(fp); + } + assert(c - b < 1024); + + *c = '\0'; + tok.kind = tok·ident; + tok.lit.s = b + 1; + return tok; + + default: + IDENT: + while (isvalidchar(*c)) { + *(++c) = s.get(fp); + } + s.unget(fp, *c); + assert(c - b < 1024); + + *c = '\0'; + tok.kind = tok·ident; + tok.lit.s = b; + return tok; + } +#undef isvalidchar +} + +static +struct Token +lex_nospace(io·Peeker s, void *impl) +{ + struct Token tok; + tok = lex(s, impl); + if (tok.kind == tok·space) { + tok = lex_nospace(s, impl); + } + + return tok; +} + +struct Parser +{ + int lev; + bio·Node *root; + struct Token tok; + + void *io; + io·Peeker file; + void *heap; + mem·Allocator mem; +}; + +static +error +parse(struct Parser *p) +{ + error err; + bio·Node *node; + bio·Node *root; + struct Token tok; + + node = p->root; + for (;;) { + tok = lex_nospace(p->file, p->io); + + switch (tok.kind) { + case tok·lparen: + if (!p->root && p->lev > 0) { + errorf("parse format: attempted to make root at non-zero level"); + goto ERROR; + } + + node = p->mem.alloc(p->heap, 1, sizeof(*node)); + memset(node, 0, sizeof(*node)); + + if (p->root) { + phylo·addchild(p->root, node); + root = p->root; + } else { + root = node; + } + + p->lev++; + p->root = node; + p->tok = tok; + err = parse(p); + if (err) { + goto ERROR; + } + if (p->tok.kind != tok·rparen) { + errorf("incorrect format: closing parentheses expected to proceed opening"); + goto ERROR; + } + p->root = root; + // NOTE(nnoll): We don't want to override the state of p->tok here! + // Jump straight to grabbing next token. + continue; + + case tok·rparen: + p->lev--; + goto DONE; + + /* Comments */ + case tok·lbrak: + if (!node) { + errorf("incorrect format: comment found in disallowed region"); + goto ERROR; + } + node->comment = str·make(""); + while (tok.kind != tok·rbrak) { + tok = lex_nospace(p->file, p->io); + if (tok.kind == tok·eof || tok.kind == tok·nil) { + errorf("incorrect format: unmatched comment bracket '['"); + goto ERROR; + } + str·append(&node->comment, tokstr(tok)); + } + break; + + case tok·rbrak: + errorf("incorrect format: end comment token found in disallowed region"); + goto ERROR; + break; + + case tok·colon: + tok = lex_nospace(p->file, p->io); + if (tok.kind != tok·number) { + errorf("incorrect format: expected number after colon"); + goto ERROR; + } + if (node == nil) { + errorf("parse error: attempting to set distance of nil node"); + goto ERROR; + } + node->dist = tok.lit.x; + break; + + case tok·comma: + node = nil; + break; + + case tok·ident: + if (p->tok.kind == tok·rparen) { + if (!node) { + errorf("parse error: attempting to set name of nil node"); + goto ERROR; + } + node->name = str·make(tok.lit.s); + } else { + if (p->tok.kind != tok·lparen && p->tok.kind != tok·comma) { + errorf("format error: misplaced identifier for leaf found"); + goto ERROR; + } + + if (!p->root) { + errorf("parse error: attempting to create child for no parent"); + goto ERROR; + } + + node = p->mem.alloc(p->heap, 1, sizeof(*node)); + memset(node, 0, sizeof(*node)); + + node->name = str·make(tok.lit.s); + + phylo·addchild(p->root, node); + } + break; + + case tok·number: + if (p->tok.kind == tok·rparen) { + if (p->lev == 0) { + errorf("format error: support value on root not supported"); + goto ERROR; + } + node->support = tok.lit.x; + } else { + errorf("format error: found number in unexpected location"); + goto ERROR; + } + break; + + case tok·semi: + p->file.unget(p->io, ';'); + if (p->lev) { + errorf("format error: uneven number of parentheses found at ';'"); + goto ERROR; + } + goto DONE; + + case tok·eof: + goto DONE; + + default: + errorf("parse error: unrecognized token"); + goto ERROR; + } + + p->tok = tok; + } + +DONE: + p->tok = tok; + return 0; +ERROR: + // TODO(nnoll): cleanup + return 1; +} + +int +bio·readnewick(io·Peeker stream, void *s, bio·Tree *tree) +{ + error err; + struct Parser p; + + if (!tree) { + errorf("tree pointer nil"); + return 0; + } + + p = (struct Parser){ + .lev = 0, + .root = nil, + .tok = (struct Token){ 0 }, + .io = s, + .file = stream, + .mem = tree->mem, + .heap = tree->heap, + }; + err = parse(&p); + if (err) { + errorf("parsing failed\n"); + return 0; + } + + tree->root = p.root; + tree->nleaf = 0; + tree->root->nnode = 0; + + phylo·countleafs(tree->root, &tree->nleaf); + phylo·countnodes(tree->root, &tree->root->nnode); + + return 1; +} + +// ----------------------------------------------------------------------- +// Write + +static +error +dump(bio·Node *node, void *impl, io·Putter out) +{ + byte b[24]; + + if (!node) { + return 1; + } + + bio·Node *child; + if (node->nchild) { + out.put(impl, '('); + + dump(node->child, impl, out); + for (child = node->child->sibling; child != nil; child = child->sibling) { + out.put(impl, ','); + dump(child, impl, out); + } + + out.put(impl, ')'); + } + if (node->name) { + out.puts(impl, node->name); + } + + if (node->parent) { + out.put(impl, ':'); + snprintf(b, arrlen(b), "%f", node->dist); + out.puts(impl, b); + } + + return 0; +} + +error +bio·writenewick(bio·Tree tree, io·Putter out, void* impl) +{ + dump(tree.root, impl, out); + out.put(impl, ';'); + out.put(impl, '\n'); + + return 0; +} diff --git a/src/libbio/phylo.c b/src/libbio/phylo.c new file mode 100644 index 0000000..d50934f --- /dev/null +++ b/src/libbio/phylo.c @@ -0,0 +1,427 @@ +#include +#include +#include +#include + +// ----------------------------------------------------------------------- +// subtree manipulation methods +// NOTE: As of now these don't update nnode & nleaf stats. +// It is the caller's responsibility to refresh counts. + +error +phylo·addchild(bio·Node* parent, bio·Node* child) +{ + bio·Node *it, *sibling; + if (!parent->nchild) { + parent->child = child; + goto SUCCESS; + } + + for (it = parent->child, sibling = it; it != nil; it = it->sibling) { + sibling = it; + } + sibling->sibling = child; + +SUCCESS: + child->parent = parent; + parent->nchild++; + return 0; +} + +error +phylo·rmchild(bio·Node *parent, bio·Node *child) +{ + bio·Node *it, *prev; + enum { + error·nil, + error·notfound, + error·nochildren, + }; + + prev = nil; + for (it = parent->child; it != nil; it = it->sibling) { + if (it == child) goto FOUND; + prev = it; + } + return error·notfound; + +FOUND: + if (prev == nil) { + parent->child = child->sibling; + } else { + prev->sibling = child->sibling; + } + + parent->nchild--; + return error·nil; +} + +// ----------------------------------------------------------------------- +// subtree statistics + +error +phylo·countnodes(bio·Node *node, int *n) +{ + int m; + error err; + bio·Node *child; + + m = *n; + for (child = node->child; child != nil; child = child->sibling) { + if (err = phylo·countnodes(child, n), err) { + errorf("node count: failure at '%s'", child->name); + return 1; + } + } + node->nnode = *n - m; + *n += 1; + + return 0; +} + +error +phylo·countleafs(bio·Node *node, int *n) +{ + error err; + bio·Node *child; + + if (!node->nchild) { + *n += 1; + } + + for (child = node->child; child != nil; child = child->sibling) { + if (err = phylo·countleafs(child, n), err) { + errorf("leaf count: failure at '%s'", child->name); + return 1; + } + } + + return 0; +} + +// ----------------------------------------------------------------------- +// generic operations on tree + +void* +phylo·postorder(bio·Node *clade, void *(*op)(bio·Node*, void*), void *ctx) +{ + bio·Node *it; + + for(it = clade->child; it != nil; it = it->sibling) { + ctx = phylo·postorder(it, op, ctx); + } + + return op(clade, ctx); +} + +void* +phylo·preorder(bio·Node *clade, void *(*op)(bio·Node*, void*), void *ctx) +{ + bio·Node *it; + + ctx = op(clade, ctx); + for(it = clade->child; it != nil; it = it->sibling) { + ctx = phylo·preorder(it, op, ctx); + } + + return ctx; +} + +int +phylo·collectpostorder(bio·Node *clade, bio·Node **list) +{ + bio·Node *it; + int n; + + for(n = 0, it = clade->child; it != nil; it = it->sibling) { + n += phylo·collectpostorder(it, list+n); + } + + return n; +} + +static +inline +void* +appendleaf(bio·Node *node, void* list) +{ + bio·Node **leafs; + + leafs = list; + if (!node->nchild) { + *leafs++ = node; + } + + return leafs; +} + +void +phylo·getleafs(bio·Tree tree, bio·Node **leafs) +{ + phylo·postorder(tree.root, &appendleaf, leafs); +} + +// ----------------------------------------------------------------------- +// tree editing + +static +void +sortnodelist(bio·Node **head, bio·Node *next) +{ + bio·Node tmp, *it; + + it = &tmp; + tmp.sibling = *head; + + while (it->sibling != nil && it->sibling->nnode < next->nnode) { + it = it->sibling; + } + + next->sibling = it->sibling; + it->sibling = next; + *head = tmp.sibling; +} + +error +phylo·ladderize(bio·Node *root) +{ + int i; + error err; + bio·Node *child, *sorted, *sibling; + + if (!root->nchild) return 0; + + // ladderize below + for (child = root->child; child != nil; child = child->sibling) { + if (err = phylo·ladderize(child), err) { + errorf("ladderize: failure at '%s'", child->name); + return 1; + } + } + + // ladderize yourself + sorted = nil; + child = root->child; + while (child != nil) { + sibling = child->sibling; + sortnodelist(&sorted, child); + child = sibling; + } + root->child = sorted; + + return 0; +} + +/* + * compute all distances from a given node + * must provide a working buffer + */ + +struct Tuple +{ + double *d; + bio·Node **n; +}; + +static +struct Tuple +getdistsfrom(bio·Node *node, bio·Node *prev, double curr, double *dist, bio·Node **list) +{ + bio·Node *it; + struct Tuple ret; + + *dist++ = curr; + *list++ = node; + + ret.d = dist; + ret.n = list; + + if (node->parent && node->parent != prev) { + ret = getdistsfrom(node->parent, node, curr + node->dist, dist, list); + + dist = ret.d; + list = ret.n; + } + + for (it = node->child; it != nil; it = it->sibling) { + if (it != prev) { + ret = getdistsfrom(it, node, curr + it->dist, dist, list); + + dist = ret.d; + list = ret.n; + } + } + + return ret; +} + +int +phylo·getdistsfrom(bio·Node *node, int len, double *dist, bio·Node **list) +{ + struct Tuple ret; + // TODO: Better bounds checking. + + ret = getdistsfrom(node, nil, 0.0, dist, list); + + assert(ret.n - list == len); + assert(ret.d - dist == len); + + return len; +} + +/* +static +void +disttoroot(bio·Node *clade, double anc, double *dists) +{ + double d; + bio·Node *it; + + *dists++ = anc + clade->dist; + d = dists[-1]; + for (it = clade->child; it != nil; it = it->sibling) { + disttoroot(it, d, ++dists); + } +} + +void +phylo·disttoroot(bio·Tree tree, double *dists) +{ + disttoroot(tree.root, 0.0, dists); +} +*/ + +/* + * compute the path constituting the tree diameter + * returns the number of edges in the path + */ + +static +void +sort·nodedists(uintptr len, double fs[], bio·Node* ns[]) +{ + double f; + bio·Node *n; +#define LESS(i, j) (fs[i] < fs[j]) +#define SWAP(i, j) (n = ns[i], f = fs[i], \ + fs[i] = fs[j], ns[i] = ns[j], \ + fs[j] = f, ns[j] = n) + QSORT(len, LESS, SWAP); +#undef LESS +#undef SWAP +} + +#define BUFLEN 4096 +double +phylo·diameter(bio·Tree tree, int *len, bio·Node **path) +{ + // TODO: deal with large tree > BUFLEN gracefully + int n; + double fbuf[BUFLEN]; + bio·Node *nbuf[BUFLEN]; + + n = tree.root->nnode; + + assert(n < BUFLEN); + + n = phylo·getdistsfrom(tree.root, tree.root->nnode, fbuf, nbuf); + sort·nodedists(n, fbuf, nbuf); + + path[0] = nbuf[n-1]; + printf("first end '%s'\n", path[0]->name); + + n = phylo·getdistsfrom(path[0], n, fbuf, nbuf); + sort·nodedists(n, fbuf, nbuf); + printf("second end '%s'\n", nbuf[n-1]->name); + + *len = 0; + + // TODO: Traverse up the tree from each node + // Find MRCA by intersection of nodes hit + + return 0.0; +} +#undef BUFLEN + +/* + * reroot a tree on a new node + */ +static +error +rotateparent(bio·Node *node, bio·Node *to) +{ + error err; + + // NOTE: will this ever be taken? + if (node->parent == to) { + return 0; + } + + if (!node->parent) { + goto RMCHILD; + } + + err = rotateparent(node->parent, node); + if (err) { + errorf("failure: broken tree"); + return err; + } + + err = phylo·addchild(node, node->parent); + if (err) { + errorf("inconsistent topology: could not add parent '%s' as child of '%s'", node->parent->name, node->name); + return err; + } + +RMCHILD: + err = phylo·rmchild(node, to); + if (err) { + errorf("inconsistent topology: could not remove child '%s' from '%s'", to->name, node->name); + return err; + } + + node->parent = to; + return 0; +} + +#define PREC .00000001 +error +phylo·reroot(bio·Tree *tree, bio·Node *node, double d) +{ + bio·Node *new; + + // TODO: should check that node is part of this tree? + // TODO: should we check if node->parent != nil? + + if (fabs(d) < PREC) { + new = node; + rotateparent(node->parent, node); + } else if (fabs(d-node->dist) < PREC) { + new = node->parent; + if (new->parent->parent) { + rotateparent(new->parent->parent, new->parent); + } + } else { + new = tree->mem.alloc(tree->heap, 1, sizeof(*new)); + memset(new, 0, sizeof(*new)); + + phylo·addchild(new, node); + node->parent = new; + + phylo·addchild(new, node->parent); + if (node->parent->parent) { + rotateparent(node->parent->parent, node->parent); + } + node->parent->parent = new; + } + + printf("number of children on old root: %d\n", tree->root->nchild); + tree->root = new; + tree->nleaf = 0; + + phylo·countleafs(new, &tree->nleaf); + phylo·countnodes(new, &new->nnode); + + return 0; +} +#undef PREC diff --git a/src/libbio/rules.mk b/src/libbio/rules.mk new file mode 100644 index 0000000..07ce97e --- /dev/null +++ b/src/libbio/rules.mk @@ -0,0 +1,24 @@ +include share/push.mk + +# Local sources +SRCS_$(d) := \ + $(d)/fasta.c \ + $(d)/newick.c \ + $(d)/phylo.c +LIBS_$(d) := $(d)/libbio.a +BINS_$(d) := +# CHECK_$(d) := \ +# $(d)/test.c \ +# $(d)/simulate.c + +include share/paths.mk + +# Local rules +$(LIBS_$(d)): $(OBJS_$(d)) $(OBJS_$(d)/io) + $(ARCHIVE) + +$(TEST_$(d)): TCLIBS = $(LIBS_$(d)) $(OBJ_DIR)/libn/libn.a +$(TEST_$(d)): $(UNIT_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/libn/libn.a + $(LINK) + +include share/pop.mk diff --git a/src/libbio/simulate.c b/src/libbio/simulate.c new file mode 100644 index 0000000..0f5a97e --- /dev/null +++ b/src/libbio/simulate.c @@ -0,0 +1,120 @@ +#include +#include +#include + +#define SEQLEN 2560 +static byte *SEQ = +"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAGGTAC" +"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" +"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCAACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" +"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" +"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCTCTAAAGTCA" +"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" +"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" +"CGTGAAGTCAGTGCTGGAGTTGATTGAAAAGGTCGATGTGCATGCCATTGCGCACCTGACCGGCGGCGGCTTCTGGGAAA" +"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" +"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCGCCCTGCC" +"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" +"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" +"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGTTCGGCCTT" +"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" +"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" +"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" +"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGTGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" +"ACAGGCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" +"ATCCACTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" +"CCGCCGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" +"CTGTAAATCGCAAGCTCCAGCAGTTTTTTTCCCCCTTTTCTGGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" +"TCCAGGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATCGAAAAAGAGCTCAGT" +"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" +"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATCATTATTAGCGAAGAAC" +"AAGGCTCCAACTCTCATTCCCGCCATTTACTGGGCAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" +"CTCTACAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" +"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" +"TGCAGTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" +"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" +"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" +"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" +"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCAGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT"; + +byte* +modify(byte *seq, int *len, double p) +{ + byte *head, *new; + + head = calloc(SEQLEN+1, sizeof(byte)); + new = head; + for (; *seq != '\0'; seq++) { + if (rng·bernoulli(p)) { + switch (rng·randi(5)) { + case 0: *new++ = 'A'; break; + case 1: *new++ = 'C'; break; + case 2: *new++ = 'G'; break; + case 3: *new++ = 'T'; break; + case 4: continue; + } + } else { + *new++ = *seq; + } + } + *new = '\0'; + *len = new - head; + return head; +} + +#define NSEQS 20 +int +main() +{ + int n, i, l, lens[NSEQS]; + byte *seqs[NSEQS]; + + int locs[aln·N][NSEQS][aln·L]; + int *loc[aln·N]; + uint64 vals[aln·N][NSEQS][aln·L]; + uint64 *val[aln·N]; + + rng·init(0); + + seqs[0] = SEQ; + lens[0] = SEQLEN; + + for (n = 0; n < aln·N; n++) { + for (i = 0; i < NSEQS; i++) { + for (l = 0; l < aln·L; l++) { + vals[n][i][l] = 0; + } + } + } + + for (i = 1; i < NSEQS; i++) { + seqs[i] = modify(SEQ, lens + i, .01*i); + } + + for (i = 0; i < NSEQS; i++) { + for (n = 0; n < aln·N; n++) { + val[n] = vals[n][i]; + loc[n] = locs[n][i]; + } + aln·sketch(seqs[i], aln·L, val, loc); + } + + // for (n = 0; n < aln·N; n++) { + // printf("iteration %d\n", n); + // printf("[\n"); + // for (i = 0; i < NSEQS; i++) { + // printf(" ["); + // for (l = 0; l < aln·L; l++) { + // printf("%lu,", vals[n][i][l]); + // } + // printf("],\n"); + // } + // printf("]\n"); + // } + + for (n = 0; n < aln·N; n++) { + aln·sort(NSEQS, aln·L, (uint64*)vals[n]); + } + + return 0; +} diff --git a/src/libbio/test.c b/src/libbio/test.c new file mode 100644 index 0000000..9926764 --- /dev/null +++ b/src/libbio/test.c @@ -0,0 +1,283 @@ +#include +#include +#include + +#include + +// ----------------------------------------------------------------------- +// Global data + +static byte *SEQ[] = { +"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAGGTAC" +"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" +"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCAACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" +"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" +"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCTCTAAAGTCA" +"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" +"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" +"CGTGAAGTCAGTGCTGGAGTTGATTGAAAAGGTCGATGTGCATGCCATTGCGCACCTGACCGGCGGCGGCTTCTGGGAAA" +"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" +"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCGCCCTGCC" +"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" +"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" +"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGTTCGGCCTT" +"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" +"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" +"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" +"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGTGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" +"ACAGGCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" +"ATCCACTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" +"CCGCCGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" +"CTGTAAATCGCAAGCTCCAGCAGTTTTTTTCCCCCTTTTCTGGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" +"TCCAGGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATCGAAAAAGAGCTCAGT" +"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" +"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATCATTATTAGCGAAGAAC" +"AAGGCTCCAACTCTCATTCCCGCCATTTACTGGGCAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" +"CTCTACAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" +"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" +"TGCAGTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" +"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" +"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" +"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" +"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCAGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT", + +"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAAATAC" +"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" +"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" +"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" +"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCAAAGTCA" +"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" +"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" +"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" +"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCCCCTGCC" +"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" +"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" +"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGCGGCCTT" +"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" +"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" +"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" +"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" +"ACAGTCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" +"ATCCTCTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" +"CCGCTGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" +"CTGTTAATCGCAAGCTCCAGCAGCCCCCCCCCCCCTTTTCTGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" +"TCCATGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATAAAAAGAGCTCAGT" +"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" +"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATTATTAGCGAAGAAC" +"AAGGTTCCAACTCTCATTCCCGCCATTTACTGGGAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" +"CTCTTCAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" +"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" +"TGCATTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" +"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" +"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" +"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" +"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT", +}; + + +static +int +my_read(Stream *s, void *buf, int n) +{ + return io·read(s, 1, n, buf); +} + +// ----------------------------------------------------------------------- +// Point of entry for testing + +error +test·newick() +{ + error err; + bio·Tree t; + mem·Arena *heap; + Stream *fd[2]; + + io·Peeker rdr; + io·Putter wtr; + + bio·Node **end, **it, **list; + + heap = mem·makearena(mem·sys, nil); + rdr = (io·Peeker){.get = (byte (*)(void *))io·getbyte, .unget = (error (*)(void *, byte))io·ungetbyte}; + wtr = (io·Putter){.put = (error (*)(void *, byte))io·putbyte, .putstr = (int (*)(void *, string))io·putstring}; + + fd[0] = io·open("/home/nolln/root/data/test/zika.nwk", "r"); + fd[1] = io·open("/home/nolln/root/data/test/zika.proc.nwk", "w"); + + t.h = heap; + t.heap = (mem·Allocator){ .alloc = (void *(*)(void *, uint, ulong))mem·arenaalloc, .free = nil, }; + + if (err = bio·readnewick(rdr, fd[0], &t), err) { + errorf("failed to read newick"); + return 1; + } + printf("number of children: %d\n", t.root->nchild); + + phylo·ladderize(t.root); + + list = mem·arenaalloc(heap, t.nleaf, sizeof(**list)); + phylo·getleafs(t, list); + for (it = list, end = list + t.nleaf; it != end; ++it) { + printf("Leaf '%s'\n", (*it)->name); + } + + bio·Node *path[100]; + // phylo·diameter(t, path); + + printf("Loaded tree with %d leafs and %d nodes\n", t.nleaf, t.root->nnode); + err = bio·writenewick(t, wtr, fd[1]); + + io·flush(fd[1]); + + io·close(fd[0]); + io·close(fd[1]); + + mem·freearena(heap); + return 0; +} + +error +test·fasta() +{ + error err; + Stream *fd; + + bio·Seq seq; + bio·FastaReader *rdr; + + clock_t t; + + fd = io·open("/home/nolln/root/data/test/zika.fa", "r"); + + /* Benchmark against Heng */ +#if 0 + int n, slen; + kseq_t *kseq; + + t = clock(); + kseq = kseq_init(fd); + while (kseq_read(kseq) >= 0) { + ++n, slen += kseq->seq.l; + } + t = clock() - t; + printf("heng's code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); + + kseq_destroy(kseq); + + io·seek(fd, 0, seek·set); +#endif + + rdr = bio·openfasta((io·Reader){.read = (int (*)(void *, int, int, void *))io·read}, fd, mem·sys, nil); + + t = clock(); + err = 0; + while (!err) { + err = bio·readfasta(rdr, &seq); + } + t = clock() - t; + printf("nick's code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); + bio·closefasta(rdr); + + + io·close(fd); + return err <= 0 ? 0 : 1; +} + +#define asrdr(x) (int (*)(void *, int, int, void *))(x) +error +test·fastq() +{ + error err; + Stream *fd; + + bio·Seq seq; + bio·FastqReader *rdr; + + clock_t t; + + fd = io·open("/home/nolln/root/data/test/eg.fq", "r"); + + rdr = bio·openfastq((io·Reader){.read = asrdr(io·read)}, fd, mem·sys, nil); + + t = clock(); + err = 0; + while (!err) { + err = bio·readfastq(rdr, &seq); + } + t = clock() - t; + printf("nick's fastq code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); + bio·closefastq(rdr); + + + io·close(fd); + return err <= 0 ? 0 : 1; +} + +error +test·align() +{ + double f; + error err; + int i, l, n; + + uint64 mem[aln·N][arrlen(SEQ)][aln·L]; + uint64 *phi[aln·N]; + int loc[aln·N][arrlen(SEQ)][aln·L]; + int *pos[aln·N]; + + for (i = 0; i < arrlen(SEQ); i++) { + for (n = 0; n < aln·N; n++) { + phi[n] = mem[n][i]; + pos[n] = loc[n][i]; + } + + err = aln·sketch(SEQ[i], aln·L, phi, pos); + } + + f = 0; + for (n = 0; n < aln·N; n++) { + aln·sort(arrlen(SEQ), aln·L, (uint64*)mem[n]); + + if (!memcmp(mem[n][0], mem[n][1], sizeof(uint64)*aln·L)) { + f += 1.; + printf("True : "); + } else { + printf("False: "); + } + for (i = 0; i < arrlen(SEQ); i++) { + printf("["); + for (l = 0; l < aln·L; l++) { + printf("%lu,", mem[n][i][l]); + } + printf("]"); + if (i == 0) printf(" ~ "); + } + printf("\n"); + } + + printf("Fraction hits %f\n", f/aln·N); + return err; + +} + +error +main() +{ + error err; + + if (err = test·newick(), err) { + errorf("test fail: newick"); + } + +#if 0 + if (err = test·fasta(), err) { + errorf("test fail: fasta"); + } + + if (err = test·fastq(), err) { + errorf("test fail: fastq"); + } +#endif +} + diff --git a/src/libc/rules.mk b/src/libc/rules.mk new file mode 100644 index 0000000..34e0912 --- /dev/null +++ b/src/libc/rules.mk @@ -0,0 +1,20 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# Local sources +SRCS_$(d) := $(wildcard $(d)/*.c) +LIBS_$(d) := $(d)/libc_n.a +BINS_$(d) := + +include share/paths.mk + +# Local rules +$(LIBS_$(d)): TCFLAGS = -ffreestanding -fno-builtin -nostdlib +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(BINS_$(d)): $(OBJ_DIR)/libn/test.o + $(LINK) + +include share/pop.mk diff --git a/src/libc/stdio.c b/src/libc/stdio.c new file mode 100644 index 0000000..8bbbe9a --- /dev/null +++ b/src/libc/stdio.c @@ -0,0 +1,59 @@ +#include +#include + +int +printf(byte* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + int nw, rem, peek, len; + byte *str, c; + + while (*fmt) { + rem = INT_MAX - nw; + + if (fmt[0] != '%' || fmt[1] == '%') { + if (fmt[0] == '%') fmt++; + + for (peek = 1; fmt[peek] && fmt[peek] != '%'; peek++) { + ; + } + if (rem < peek) return -1; + // TODO: Print here. + fmt += peek; + nw += peek; + continue; + } + + str = fmt++; + + switch (*fmt++) { + case 'c': + c = va_arg(args, int); + if (rem < 0) return -1; + // TODO: Print here + nw++; + break; + + case 's': + str = va_arg(args, byte*); + len = strlen(str); + if (rem < len) return -1; + // TODO: Print here + nw += len; + break; + default: + fmt = str; + len = strlen(fmt); + if (rem < len) return -1; + // TODO: Print here + nw += len; + fmt += len; + break; + } + } + + va_end(args); + return nw; +} diff --git a/src/libc/string.c b/src/libc/string.c new file mode 100644 index 0000000..0e41efa --- /dev/null +++ b/src/libc/string.c @@ -0,0 +1,80 @@ +#include +#include + +void* +memcopy(void *dst, void *src, intptr n) +{ + byte *e, *s, *d; + + d = dst; + e = d + n; + for (s = src ; d != e; ++s, ++d) { + *d = *s; + } + + return dst; +} + +void* +memmove(void *dst, void *src, intptr n) +{ + byte *e, *s, *d; + s = src; + d = dst; + + if (d < s) { + e = d + n; + for (; d != e; ++s, ++d) + *d = *s; + + } else { + e = d; + d += n; + s += n; + for (; d != e; --s, --d) + d[-1] = s[-1]; + } + + return dst; +} + +void* +memset(void *buf, int val, intptr n) +{ + byte *b, *e; + b = buf; + e = b + n; + for (; b != e; b++) { + *b = (byte)val; + } + + return buf; +} + +int +memcmp(void *lhs, void *rhs, intptr n) +{ + byte *bl, *br, *e; + + br = rhs; + e = br + n; + for (bl = lhs; br != e; ++bl, ++br) { + if (*bl < *br) + return -1; + else if (*bl > *br) + return 1; + } + + return 0; +} + +int +strlen(byte* s) +{ + byte* b; + for (b = s; *b; b++) { + ; + } + + return b - s; +} diff --git a/src/libfmt/buffer.c b/src/libfmt/buffer.c new file mode 100644 index 0000000..0099e72 --- /dev/null +++ b/src/libfmt/buffer.c @@ -0,0 +1,60 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n; + char *s; + + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + if(!io->buffer.beg) + return 0; + + n = 2*(uintptr)io->file; + s = io->buffer.beg; + + io->buffer.beg = mem.realloc(heap, io->buffer.beg, n, 1); + if(!io->buffer.beg){ + io->file = io->buffer.cur = io->buffer.end = nil; + mem.free(heap, s); + return 0; + } + io->file = (void*)(uintptr)n; + io->buffer.cur = io->buffer.beg + (io->buffer.cur - s); + io->buffer.end = io->buffer.beg + n - 1; + + return 1; +} + +int +fmt·make(mem·Reallocator mem, void *heap, fmt·State *io) +{ + int n; + + memset(io, 0, sizeof(*io)); + + n = 32; + io->buffer.beg = io->buffer.cur = mem.alloc(heap, n, 1); + if(!io->buffer.beg) + return -1; + io->buffer.end = io->buffer.beg + n - 1; + + io->flush = flush; + io->file = (void*)(uintptr)n; + io->n = 0; + + fmt·setlocale(io, nil, nil, nil); + return 0; +} + +void +fmt·free(fmt·State *io) +{ + void *heap = io->heap; + mem·Reallocator mem = io->mem; + + mem.free(heap, io->buffer.beg); + io->buffer.beg = io->buffer.cur = io->buffer.end = nil; +} diff --git a/src/libfmt/do.c b/src/libfmt/do.c new file mode 100644 index 0000000..eaac0a3 --- /dev/null +++ b/src/libfmt/do.c @@ -0,0 +1,730 @@ +#include "internal.h" +#include + +#define atomic _Atomic +#define MaxFmt 128 +#define atomic·load atomic_load +#define atomic·store atomic_store + +// ----------------------------------------------------------------------- +// globals + +/* built in verbs */ +static int fmtflag(fmt·State *); +static int fmtpercent(fmt·State *); +static int fmtrune(fmt·State *); +static int fmtfloat(fmt·State *); +static int fmtutf8(fmt·State *); +static int fmtint(fmt·State *); +static int fmtchar(fmt·State *); +static int fmtcount(fmt·State *); +static int fmtstring(fmt·State *); +static int fmterror(fmt·State *); + +static int badfmt(fmt·State *); + +static struct +{ + atomic int len; + Verb verb[MaxFmt]; +} formatter = +{ + ATOMIC_VAR_INIT(30), + { + {' ', fmtflag}, + {'#', fmtflag}, + {'%', fmtpercent}, + {'\'',fmtflag}, + {'+', fmtflag}, + {',', fmtflag}, + {'-', fmtflag}, + {'C', fmtrune}, + {'E', fmtfloat}, + {'F', fmtfloat}, + {'G', fmtfloat}, + {'L', fmtflag}, + {'S', fmtutf8}, + {'X', fmtint}, + {'b', fmtint}, + {'c', fmtchar}, + {'d', fmtint}, + {'e', fmtfloat}, + {'f', fmtfloat}, + {'g', fmtfloat}, + {'h', fmtflag}, + {'i', fmtint}, + {'l', fmtflag}, + {'n', fmtcount}, + {'o', fmtint}, + {'p', fmtint}, + {'r', fmterror}, + {'s', fmtstring}, + {'U', fmtflag}, + {'u', fmtint}, + {'x', fmtint}, + } +}; + +// ----------------------------------------------------------------------- +// internal functions + +static Formatter +format(int c) +{ + Verb *v, *e; + e = &formatter.verb[atomic·load(&formatter.len)]; + for(v=e; v > formatter.verb; --v){ + if(v->c == c) + return v->fmt; + } + + return badfmt; +} + +static char * +dispatch(fmt·State *io, char *fmt) +{ + rune r; + int i, n; + + io->flag = 0; + io->width = io->prec = 0; + + /* + * the form of each print verb: + * % [flags] verb + * + the verb is a single character + * + each flag is either + * - a single character + * - a decimal numeric string + * - up to 2 decimal strings can be used + * - [width|*].[prec|*] + * - if missing, set to 0 + * - if *, grab from varargs + */ + for(;;){ + fmt += utf8·decode(fmt, &r); + io->verb = r; + switch(r){ + case 0: + return nil; + case '.': + io->flag |= fmt·Width|fmt·Prec; + continue; + case '0': + if(!(io->flag & fmt·Width)){ + io->flag |= fmt·Zero; + continue; + } + /* fallthrough */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = 0; + while('0' <= r && r <= '9'){ + i = 10*i + (r-'0'); + r = *fmt++; + } + fmt--; + number: + if(io->flag & fmt·Width){ + io->flag |= fmt·Prec; + io->prec = i; + }else{ + io->flag |= fmt·Width; + io->width = i; + } + continue; + case '*': + i = va_arg(io->args, int); + if(i < 0){ + if(io->flag&fmt·Prec){ + io->flag &= ~fmt·Prec; + io->prec = 0; + continue; + } + i = -i; + io->flag |= fmt·Left; + } + goto number; + } + n = format(r)(io); + if(n < 0) + return nil; + if(!n) + return fmt; + } +} + +static char * +flush(fmt·State *io, char *b, int len) +{ + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!io->flush || !(*io->flush)(io) || io->buffer.cur + len >= io->buffer.end) { + io->buffer.end = io->buffer.cur; + return nil; + } + return io->buffer.cur; +} + +static int +pad(fmt·State *io, int n) +{ + int i; + char *b=io->buffer.cur, *e=io->buffer.end; + + for(i=0; i=e){ + if(!(b=flush(io, b, 1))) + return -1; + e = io->buffer.end; + } + *b++ = ' '; + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + return 0; +} + +static int +copy(fmt·State *io, char *m, int sz, int n) +{ + ulong f; + rune r; + int nc, w, nb; + char *b, *e, *me; + + w = 0; + f = io->flag; + me = m + sz; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(nc=n; nc>0; nc--){ + r = *(uchar *)m; + if(utf8·onebyte(r)){ + nb=1; + m++; + }else if((me-m) >= UTFmax || utf8·canfit(m, me-m)){ + nb=utf8·decode(m, &r); + m+=n; + }else + break; + + if(b+n>e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copyrune(fmt·State *io, rune *m, int n) +{ + ulong f; + rune r, *me; + int w, nb; + char *b, *e; + + w = 0; + f = io->flag; + + if(f&fmt·Width) + w = io->width; + if(f&fmt·Prec && n > io->prec) + n = io->prec; + + if(!(f&fmt·Left) && pad(io, w-n)<0) + return -1; + + b = io->buffer.cur; + e = io->buffer.end; + + for(me=m+n; m < me; m++){ + r = *m; + nb = utf8·runelen(r); + if(b + nb > e){ + if(!(b=flush(io, b, nb))) + return -1; + e = io->buffer.end; + } + b += utf8·encode(&r, b); + } + + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(f&fmt·Left && pad(io, w-n)<0) + return -1; + + return 0; +} + +static int +copystring(fmt·State *io, char *s) +{ + rune r; + int i,j; + + if(!s) + return copy(io, "", 5, 5); + + if(io->flag&fmt·Prec){ + i = 0; + for(j=0; j < io->prec && s[i]; j++) + i += utf8·decode(s+i, &r); + + return copy(io, s, i, j); + } + return copy(io, s, strlen(s), utf8·len(s)); +} + +static int +copyutf8(fmt·State *io, rune *s) +{ + rune *e; + int n,p; + + if(!s) + return copy(io, "", 5, 5); + + if(io->flag & fmt·Prec){ + p = io->prec; + for(n=0; n group){ + if((*groups)[1] != 0) + (*groups)++; + *digits = 1; + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------- +// formatters + +static int +fmtchar(fmt·State *io) +{ + char x[1]; + x[0] = va_arg(io->args, int); + io->prec = 1; + + return copy(io, x, 1, 1); +} + +static int +fmtstring(fmt·State *io) +{ + char *s; + s = va_arg(io->args, char *); + return copystring(io, s); +} + +static int +fmterror(fmt·State *io) +{ + char *s; + s = strerror(errno); + return copystring(io, s); +} + +static int +fmtrune(fmt·State *io) +{ + rune x[1]; + + x[0] = va_arg(io->args, int); + return copyrune(io, x, 1); +} + +static int +fmtutf8(fmt·State *io) +{ + rune *s; + + s = va_arg(io->args, rune *); + return copyutf8(io, s); +} + +static int +fmtpercent(fmt·State *io) +{ + rune x[1]; + + x[0] = io->verb; + io->prec = 1; + return copyrune(io, x, 1); +} + +static int +fmtint(fmt·State *io) +{ + union{ + ulong u; + uvlong v; + } val; + int neg, base, i, n, f, w, isv; + int digits, bytes, runes, excess; + char *groups, *thousands; + char *p, *conv, buf[140]; + + f = io->flag; + neg = 0; + isv = 0; + val.u = 0; + + switch(io->verb){ + case 'o': case 'p': case 'u': case 'x': case 'X': + f |= fmt·Unsigned; + f &= ~(fmt·Sign|fmt·Space); + } + + /* set flags */ + if(io->verb=='p'){ + val.u = (ulong)va_arg(io->args, void*); + io->verb = 'x'; + f |= fmt·Unsigned; + }else if(f&fmt·Vlong){ + isv=1; + if(f&fmt·Unsigned) + val.v = va_arg(io->args, uvlong); + else + val.v = va_arg(io->args, vlong); + }else if(f&fmt·Long){ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, ulong); + else + val.u = va_arg(io->args, long); + }else if(f&fmt·Byte){ + if(f&fmt·Unsigned) + val.u = (uchar)va_arg(io->args, int); + else + val.u = (char)va_arg(io->args, int); + }else if(f&fmt·Short){ + if(f&fmt·Unsigned) + val.u = (ushort)va_arg(io->args, int); + else + val.u = (short)va_arg(io->args, int); + }else{ + if(f&fmt·Unsigned) + val.u = va_arg(io->args, uint); + else + val.u = va_arg(io->args, int); + } + + conv = "0123456789abcdef"; + groups = "\4"; + thousands = io->thousands; + /* get base */ + switch(io->verb){ + case 'd': case 'i': case 'u': + base = 10; + groups = io->groups; + break; + case 'X': + conv = "0123456789ABCDEF"; + /*fallthrough*/ + case 'x': + base = 16; + thousands = ":"; + break; + case 'b': + base = 2; + thousands = ":"; + break; + case 'o': + base = 8; + break; + default: + return -1; + } + + /* check for negativity */ + if(!(f&fmt·Unsigned)){ + if(isv && (vlong)val.v < 0){ + val.v = -(vlong)val.v; + neg = 1; + }else if(!isv && (long)val.u < 0){ + val.u = -(long)val.u; + neg = 1; + } + } + + p = buf + sizeof(buf) - 1; + n = 0; + digits = 0; + excess = 0; + runes = utf8·len(thousands); + bytes = strlen(thousands); + +#define PARSE(VALUE) \ + while((VALUE)){ \ + i = (VALUE) % base; \ + (VALUE) /= base; \ + if((f&fmt·Comma) && n%4 == 3){ \ + *p-- = ','; \ + n++; \ + } \ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ \ + n += runes; \ + excess += bytes - runes; \ + p -= bytes; \ + memmove(p+1, thousands, bytes); \ + } \ + *p-- = conv[i]; \ + n++; \ + } + if(isv) + PARSE(val.v) + else + PARSE(val.u) +#undef PARSE + + if(!n){ + if(!(f&fmt·Prec) || io->prec != 0 || (io->verb == 'o' && (f&fmt·Sharp))){ + *p-- = '0'; + n = 1; + if(f&fmt·Apost) + needseperate(&digits,&groups); + } + + if(io->verb == 'x' || io->verb == 'X') + f &= ~fmt·Sharp; + } + + for(w = io->prec; n < w && p > buf+3; n++){ + if((f&fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + + if(neg || (f&(fmt·Sign|fmt·Space))) + n++; + + if(f&fmt·Sharp){ + if(base==16) + n += 2; + else if(base == 8){ + if(p[1] == '0') + f &= ~fmt·Sharp; + else + n++; + } + } + + if(f&fmt·Zero && !(f & (fmt·Left|fmt·Prec))){ + w = 0; + if(f & fmt·Width) + w = io->width; + for(; n < w && p > buf+3; n++){ + if((f & fmt·Apost) && needseperate(&digits, &groups)){ + n += runes; + excess += bytes - runes; + p -= bytes; + memmove(p+1, thousands, bytes); + } + *p-- = '0'; + } + io->flag &= ~fmt·Width; + } + + if(f&fmt·Sharp){ + if(base==16) + *p-- = io->verb; + if(base==16 || base == 8) + *p-- = '0'; + } + + if(neg) + *p-- = '-'; + else if(f & fmt·Sign) + *p-- = '+'; + else if (f & fmt·Space) + *p-- = ' '; + + io->flag &= ~fmt·Prec; + return copy(io, p+1, n+excess, n); +} + +static int +fmtcount(fmt·State *io) +{ + void *p; + ulong f; + + f = io->flag; + p = va_arg(io->args, void*); + + if(f&fmt·Vlong) + *(vlong*)p = io->n; + else if(f&fmt·Long) + *(long*)p = io->n; + else if(f&fmt·Byte) + *(char*)p = io->n; + else if(f&fmt·Short) + *(short*)p = io->n; + else + *(int*)p = io->n; + + return 0; +} + +static int +fmtflag(fmt·State *io) +{ + switch(io->verb){ + case ',': io->flag |= fmt·Comma; break; + case '-': io->flag |= fmt·Left; break; + case '+': io->flag |= fmt·Sign; break; + case '#': io->flag |= fmt·Sharp; break; + case '\'': io->flag |= fmt·Apost; break; + case ' ': io->flag |= fmt·Space; break; + case 'u': io->flag |= fmt·Unsigned; break; + case 'L': io->flag |= fmt·Ldouble; break; + case 'h': + if(io->flag&fmt·Short) + io->flag |= fmt·Byte; + io->flag |= fmt·Short; + break; + case 'l': + if(io->flag&fmt·Long) + io->flag |= fmt·Vlong; + io->flag |= fmt·Long; + break; + } + return 1; +} + +static int +badfmt(fmt·State *io) +{ + int n; + char x[UTFmax+2]; + + x[0] = '%'; + n = 1 + utf8·encode(&io->verb, x+1); + x[n++] = '%'; + io->prec = n; + copy(io, x, n, n); + + return 0; +} + +#include "float.c" + +// ----------------------------------------------------------------------- +// exports + +int +fmt·do(fmt·State *io, char *fmt) +{ + rune r; + int c, n; + char *b, *e; + + for(;;){ + b = io->buffer.cur; + e = io->buffer.end; + while((c = *(uchar *)fmt) && c != '%'){ + if(utf8·onebyte(c)){ + if(b >= e){ + if(!(b=flush(io, b, 1))) + return -1; + e = io->buffer.end; + } + *b++ = *fmt++; + }else{ + n = utf8·decode(fmt, &r); + if(b + n > e){ + if(!(b=flush(io, b, n))) + return -1; + e = io->buffer.end; + } + while(n--) + *b++ = *fmt++; + } + } + fmt++; + io->n += b - io->buffer.cur; + io->buffer.cur = b; + if(!c) /* we hit our nul terminator */ + return io->n - n; + io->buffer.end = e; + + if(!(fmt=dispatch(io, fmt))) + return -1; + } +} + +int +fmt·install(int verb, Formatter func) +{ + Verb *v; + int i, ret; + +lock: + if(verb <= 0 || verb >= 65536){ + ret = -1; + goto unlock; + } + if(!func) + func = badfmt; + + if((i = atomic·load(&formatter.len))==MaxFmt) + return -1; + + v = &formatter.verb[i]; + v->c = verb; + v->fmt = func; + + atomic·store(&formatter.len, i+1); + ret = 0; +unlock: + return ret; +} diff --git a/src/libfmt/esprint.c b/src/libfmt/esprint.c new file mode 100644 index 0000000..6d97340 --- /dev/null +++ b/src/libfmt/esprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +char * +fmt·esprint(char *buf, char *end, char *fmt, ...) +{ + char *p; + va_list args; + + va_start(args, fmt); + p = fmt·vesprint(buf, end, fmt, args); + va_end(args); + + return p; +} diff --git a/src/libfmt/float.c b/src/libfmt/float.c new file mode 100644 index 0000000..63ea80f --- /dev/null +++ b/src/libfmt/float.c @@ -0,0 +1,1077 @@ +#define FDIGIT 30 +#define FDEFLT 6 +#define NSIGNIF 17 + +static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; +static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; +static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; + +static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; + +static int +isNaN(double val) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + return (x.i&uvinf) == uvinf && (x.i&~uvneginf) != 0; +} + +static double +NaN(void) +{ + union{ + uvlong i; + double f; + }x; + x.i = uvnan; + return x.f; +} + +static int +isInf(double val, int sign) +{ + union{ + uvlong i; + double f; + }x; + + x.f = val; + if(sign == 0) + return x.i == uvinf || x.i == uvneginf; + else if(sign == 1) + return x.i == uvinf; + else + return x.i == uvneginf; +} + +static double pows10[] = +{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, + 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, + 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, + 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, + 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, + 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, + 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, + 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, + 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, + 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, + 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, +}; + +static double +fpow10(int n) +{ + double d; + int neg; + + neg = 0; + if(n < 0){ + neg = 1; + n = -n; + } + + if(n NSIGNIF) + return 0; + + for(b = a+n-1; b >= a; b--){ + c = *b + 1; + if(c <= '9'){ + *b = c; + return 0; + } + *b = '0'; + } + /* + * need to overflow adding digit. + * shift number down and insert 1 at beginning. + * decimal is known to be 0s or we wouldn't + * have gotten this far. (e.g., 99999+1 => 00000) + */ + a[0] = '1'; + return 1; +} + +static int +sub1(char *a, int n) +{ + int c; + char *b; + + if(n < 0 || n > NSIGNIF) + return 0; + for(b = a+n-1; b >= a; b--){ + c = *b - 1; + if(c >= '0'){ + if(c == '0' && b == a){ + /* + * just zeroed the top digit; shift everyone up. + * decimal is known to be 9s or we wouldn't + * have gotten this far. (e.g., 10000-1 => 09999) + */ + *b = '9'; + return 1; + } + *b = c; + return 0; + } + *b = '9'; + } + /* + * can't get here. the number a is always normalized + * so that it has a nonzero first digit. + */ + abort(); +} + +// ----------------------------------------------------------------------- +// strtod + +#define Nbits 28 +#define Nmant 53 +#define Prec ((Nmant+Nbits+1)/Nbits) + +#define Sigbit (1<<(Prec*Nbits-Nmant)) /* first significant bit of Prec-th word */ +#define Ndig 1500 +#define One (ulong)(1<>1) +#define Maxe 310 + +#define Fsign (1<<0) /* found - */ +#define Fesign (1<<1) /* found e- */ +#define Fdpoint (1<<2) /* found . */ + +#define S0 0 /* _ _S0 +S1 #S2 .S3 */ +#define S1 1 /* _+ #S2 .S3 */ +#define S2 2 /* _+# #S2 .S4 eS5 */ +#define S3 3 /* _+. #S4 */ +#define S4 4 /* _+#.# #S4 eS5 */ +#define S5 5 /* _+#.#e +S6 #S7 */ +#define S6 6 /* _+#.#e+ #S7 */ +#define S7 7 /* _+#.#e+# #S7 */ + +typedef struct Tab Tab; +struct Tab +{ + int bp; + int siz; + char *cmp; +}; + +static ulong +umuldiv(ulong a, ulong b, ulong c) +{ + double d; + + d = ((double)a * (double)b) / (double)c; + if(d >= 4294967295.) + d = 4294967295.; + return (ulong)d; +} + +static void +frnorm(ulong *f) +{ + int i, c; + + c = 0; + for(i=Prec-1; i>0; i--) { + f[i] += c; + c = f[i] >> Nbits; + f[i] &= One-1; + } + f[0] += c; +} + +static int +fpcmp(char *a, ulong* f) +{ + ulong tf[Prec]; + int i, d, c; + + for(i=0; i> Nbits) + '0'; + tf[0] &= One-1; + + /* compare next digit */ + c = *a; + if(c == 0) { + if('0' < d) + return -1; + if(tf[0] != 0) + goto cont; + for(i=1; i d) + return +1; + if(c < d) + return -1; + a++; + cont:; +} +} + +static void +divby(char *a, int *na, int b) +{ + int n, c; + char *p; + + p = a; + n = 0; + while(n>>b == 0){ + c = *a++; + if(c == 0) { + while(n) { + c = n*10; + if(c>>b) + break; + n = c; + } + goto xx; + } + n = n*10 + c-'0'; + (*na)--; + } + for(;;){ + c = n>>b; + n -= c<>b; + n -= c<= (int)(arrlen(tab1))) + d = (int)(arrlen(tab1))-1; + t = tab1 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) > 0) + d--; + *dp -= d; + *bp += b; + divby(a, na, b); +} + +static void +mulby(char *a, char *p, char *q, int b) +{ + int n, c; + + n = 0; + *p = 0; + for(;;) { + q--; + if(q < a) + break; + c = *q - '0'; + c = (c<= (int)(arrlen(tab2))) + d = (int)(arrlen(tab2))-1; + t = tab2 + d; + b = t->bp; + if(memcmp(a, t->cmp, t->siz) < 0) + d--; + p = a + *na; + *bp -= b; + *dp += d; + *na += d; + mulby(a, p+d, p, b); +} + +static int +cmp(char *a, char *b) +{ + int c1, c2; + + while((c1 = *b++) != '\0') { + c2 = *a++; + if(isupper(c2)) + c2 = tolower(c2); + if(c1 != c2) + return 1; + } + return 0; +} + +double +fmtstrtod(char *as, char **aas) +{ + int na, ex, dp, bp, c, i, flag, state; + ulong low[Prec], hig[Prec], mid[Prec]; + double d; + char *s, a[Ndig]; + + flag = 0; /* Fsign, Fesign, Fdpoint */ + na = 0; /* number of digits of a[] */ + dp = 0; /* na of decimal point */ + ex = 0; /* exonent */ + + state = S0; + for(s=as;;s++){ + c = *s; + if('0' <= c && c <= '9'){ + switch(state){ + case S0: case S1: case S2: + state = S2; + break; + case S3: case S4: + state = S4; + break; + case S5: case S6: case S7: + state = S7; + ex = ex*10 + (c-'0'); + continue; + } + + if(na == 0 && c == '0'){ + dp--; + continue; + } + if(na < Ndig-50) + a[na++] = c; + continue; + } + switch(c){ + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + if(state == S0) + continue; + break; + case '-': + if(state == S0) + flag |= Fsign; + else + flag |= Fesign; + case '+': + if(state == S0) + state = S1; + else + if(state == S5) + state = S6; + else + break; /* syntax */ + continue; + case '.': + flag |= Fdpoint; + dp = na; + if(state == S0 || state == S1){ + state = S3; + continue; + } + if(state == S2){ + state = S4; + continue; + } + break; + case 'e': case 'E': + if(state == S2 || state == S4){ + state = S5; + continue; + } + break; + } + break; + } + + /* clean up return char-pointer */ + switch(state) { + case S0: + if(cmp(s, "nan") == 0){ + if(aas != nil) + *aas = s+3; + goto retnan; + } + case S1: + if(cmp(s, "infinity") == 0){ + if(aas != nil) + *aas = s+8; + goto retinf; + } + if(cmp(s, "inf") == 0){ + if(aas != nil) + *aas = s+3; + goto retinf; + } + case S3: + if(aas != nil) + *aas = as; + goto ret0; /* no digits found */ + case S6: + s--; /* back over +- */ + case S5: + s--; /* back over e */ + break; + } + if(aas != nil) + *aas = s; + + if(flag & Fdpoint) + while(na > 0 && a[na-1] == '0') + na--; + if(na == 0) + goto ret0; /* zero */ + a[na] = 0; + if(!(flag & Fdpoint)) + dp = na; + if(flag & Fesign) + ex = -ex; + dp += ex; + if(dp < -Maxe){ + errno = ERANGE; + goto ret0; /* underflow by exp */ + } else + if(dp > +Maxe) + goto retinf; /* overflow by exp */ + + /* + * normalize the decimal ascii number + * to range .[5-9][0-9]* e0 + */ + bp = 0; /* binary exponent */ + while(dp > 0) + divascii(a, &na, &dp, &bp); + while(dp < 0 || a[0] < '5') + mulascii(a, &na, &dp, &bp); + + /* close approx by naive conversion */ + mid[0] = 0; + mid[1] = 1; + for(i=0; (c=a[i]) != '\0'; i++) { + mid[0] = mid[0]*10 + (c-'0'); + mid[1] = mid[1]*10; + if(i >= 8) + break; + } + low[0] = umuldiv(mid[0], One, mid[1]); + hig[0] = umuldiv(mid[0]+1, One, mid[1]); + for(i=1; i>= 1; + } + frnorm(mid); + + /* compare */ + c = fpcmp(a, mid); + if(c > 0) { + c = 1; + for(i=0; i= Sigbit/2) { + mid[Prec-1] += Sigbit; + frnorm(mid); + } + goto out; + +ret0: + return 0; + +retnan: + return NaN(); + +retinf: + /* Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ + errno = ERANGE; + if(flag & Fsign) + return -HUGE_VAL; + return HUGE_VAL; + +out: + d = 0; + for(i=0; i 0) + *p++ = se[--i]; + + *p++ = '\0'; +} + +/* + * compute decimal integer m, exp such that: + * f = m*10^exp + * m is as short as possible with losing exactness + * assumes special cases (NaN, +Inf, -Inf) have been handled. + */ +static void +dtoa(double f, char *s, int *exp, int *neg, int *len) +{ + int c, d, e2, e, ee, i, ndigit, oerrno; + char buf[NSIGNIF+10]; + double g; + + oerrno = errno; + + *neg = 0; + if(f < 0){ + f = -f; + *neg = 1; + } + + if(f == 0){ + *exp = 0; + s[0] = '0'; + s[1] = 0; + *len = 1; + return; + } + + frexp(f, &e2); + e = (int)(e2 * .301029995664); + g = f * fpow10(-e); + while(g < 1) { + e--; + g = f * fpow10(-e); + } + while(g >= 10){ + e++; + g = f * fpow10(-e); + } + + /* convert nsignif digits as a first approximation */ + for(i=0; i g) { + if(add1(s, NSIGNIF)){ + /* gained a digit */ + e--; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + if(f < g){ + if(sub1(s, NSIGNIF)){ + /* lost a digit */ + e++; + fmtexp(s+NSIGNIF, e, 0); + } + continue; + } + break; + } + + /* + * bump last few digits down to 0 as we can. + */ + for(i=NSIGNIF-1; i>=NSIGNIF-3; i--){ + c = s[i]; + if(c != '0'){ + s[i] = '0'; + g=fmtstrtod(s, nil); + if(g != f){ + s[i] = c; + break; + } + } + } + + /* + * remove trailing zeros. + */ + ndigit = NSIGNIF; + while(ndigit > 1 && s[ndigit-1] == '0'){ + e++; + --ndigit; + } + s[ndigit] = 0; + *exp = e; + *len = ndigit; + + errno = oerrno; +} + + +static int +fmtfloat(fmt·State *io) +{ + char buf[NSIGNIF+10], *dot, *digits, *p, *end, suf[10], *cur; + double val; + int c, verb, ndot, e, exp, f, ndigits, neg, newndigits; + int npad, pt, prec, realverb, sign, nsuf, ucase, n, z1, z2; + + if(io->flag&fmt·Long) + val = va_arg(io->args, long double); + else + val = va_arg(io->args, double); + + /* extract formatting flags */ + f = io->flag; + io->flag = 0; + prec = FDEFLT; + if(f & fmt·Prec) + prec = io->prec; + + verb = io->verb; + ucase = 0; + switch(verb) { + case 'A': + case 'E': + case 'F': + case 'G': + verb += 'a'-'A'; + ucase = 1; + break; + } + + /* pick off special numbers. */ + if(isNaN(val)) { + end = special[0+ucase]; + special: + io->flag = f & (fmt·Width|fmt·Left); + return copy(io, end, strlen(end), strlen(end)); + } + if(isInf(val, 1)) { + end = special[2+ucase]; + goto special; + } + if(isInf(val, -1)) { + end = special[4+ucase]; + goto special; + } + + /* get exact representation. */ + digits = buf; + dtoa(val, digits, &exp, &neg, &ndigits); + + /* get locale's decimal point. */ + dot = io->decimal; + if(dot == nil) + dot = "."; + ndot = utf8·len(dot); + + /* + * now the formatting fun begins. + * compute parameters for actual fmt: + * + * pad: number of spaces to insert before/after field. + * z1: number of zeros to insert before digits + * z2: number of zeros to insert after digits + * point: number of digits to print before decimal point + * ndigits: number of digits to use from digits[] + * suf: trailing suffix, like "e-5" + */ + realverb = verb; + switch(verb){ + case 'g': + /* convert to at most prec significant digits. (prec=0 means 1) */ + if(prec == 0) + prec = 1; + if(ndigits > prec) { + if(digits[prec] >= '5' && add1(digits, prec)) + exp++; + exp += ndigits-prec; + ndigits = prec; + } + + /* + * extra rules for %g (implemented below): + * trailing zeros removed after decimal unless FmtSharp. + * decimal point only if digit follows. + */ + + /* fall through to %e */ + default: + case 'e': + /* one significant digit before decimal, no leading zeros. */ + pt = 1; + z1 = 0; + + /* + * decimal point is after ndigits digits right now. + * slide to be after first. + */ + e = exp + (ndigits-1); + + /* if this is %g, check exponent and convert prec */ + if(realverb == 'g') { + if(-4 <= e && e < prec) + goto casef; + prec--; /* one digit before decimal; rest after */ + } + + /* compute trailing zero padding or truncate digits. */ + if(1+prec >= ndigits) + z2 = 1+prec - ndigits; + else { + /* truncate digits */ + assert(realverb != 'g'); + newndigits = 1+prec; + if(digits[newndigits] >= '5' && add1(digits, newndigits)) { + /* had 999e4, now have 100e5 */ + e++; + } + ndigits = newndigits; + z2 = 0; + } + fmtexp(suf, e, ucase); + nsuf = strlen(suf); + break; + + casef: + case 'f': + /* determine where digits go with respect to decimal point */ + if(ndigits+exp > 0) { + pt = ndigits+exp; + z1 = 0; + } else { + pt = 1; + z1 = 1 + -(ndigits+exp); + } + + /* + * %g specifies prec = number of significant digits + * convert to number of digits after decimal point + */ + if(realverb == 'g') + prec += z1 - pt; + + /* compute trailing zero padding or truncate digits. */ + if(pt+prec >= z1+ndigits) + z2 = pt+prec - (z1+ndigits); + else{ + /* truncate digits */ + assert(realverb != 'g'); + newndigits = pt+prec - z1; + if(newndigits < 0){ + z1 += newndigits; + newndigits = 0; + }else if(newndigits == 0){ + /* perhaps round up */ + if(digits[0] >= '5'){ + digits[0] = '1'; + newndigits = 1; + goto newdigit; + } + }else if(digits[newndigits] >= '5' && add1(digits, newndigits)){ + /* digits was 999, is now 100; make it 1000 */ + digits[newndigits++] = '0'; + newdigit: + /* account for new digit */ + if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ + z1--; + else /* 9.99 => 10.00 */ + pt++; + } + z2 = 0; + ndigits = newndigits; + } + nsuf = 0; + break; + } + + /* + * if %g is given without FmtSharp, remove trailing zeros. + * must do after truncation, so that e.g. print %.3g 1.001 + * produces 1, not 1.00. sorry, but them's the rules. + */ + if(realverb == 'g' && !(f & fmt·Sharp)) { + if(z1+ndigits+z2 >= pt) { + if(z1+ndigits < pt) + z2 = pt - (z1+ndigits); + else{ + z2 = 0; + while(z1+ndigits > pt && digits[ndigits-1] == '0') + ndigits--; + } + } + } + + /* + * compute width of all digits and decimal point and suffix if any + */ + n = z1+ndigits+z2; + if(n > pt) + n += ndot; + else if(n == pt){ + if(f & fmt·Sharp) + n += ndot; + else + pt++; /* do not print any decimal point */ + } + n += nsuf; + + /* + * determine sign + */ + sign = 0; + if(neg) + sign = '-'; + else if(f & fmt·Sign) + sign = '+'; + else if(f & fmt·Space) + sign = ' '; + if(sign) + n++; + + /* compute padding */ + npad = 0; + if((f & fmt·Width) && io->width > n) + npad = io->width - n; + if(npad && !(f & fmt·Left) && (f & fmt·Zero)){ + z1 += npad; + pt += npad; + npad = 0; + } + + /* format the actual field. too bad about doing this twice. */ + if(npad && !(f & fmt·Left) && pad(io, npad < 0)) + return -1; + + cur = io->buffer.cur; + end = io->buffer.end; + + if(sign){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = sign; + } + + while(z1>0 || ndigits>0 || z2>0){ + if(z1 > 0){ + z1--; + c = '0'; + }else if(ndigits > 0){ + ndigits--; + c = *digits++; + }else{ + z2--; + c = '0'; + } + + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = c; + + if(--pt == 0) + for(p=dot; *p; p++){ + if(cur+1 > end){ + if(!(cur=flush(io,cur,1))) + return -1; + end = io->buffer.end; + } + *cur++ = *p; + } + } + io->n += cur - (char*)io->buffer.cur; + io->buffer.cur = cur; + if(nsuf && copy(io, suf, nsuf, nsuf) < 0) + return -1; + if(npad && (f & fmt·Left) && pad(io, npad < 0)) + return -1; + + return 0; +} diff --git a/src/libfmt/fprint.c b/src/libfmt/fprint.c new file mode 100644 index 0000000..26343f7 --- /dev/null +++ b/src/libfmt/fprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fprint(int fd, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(fd, fmt, args); + va_end(args); + + return n; +} diff --git a/src/libfmt/internal.h b/src/libfmt/internal.h new file mode 100644 index 0000000..725cfff --- /dev/null +++ b/src/libfmt/internal.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +typedef int (*Formatter)(fmt·State *io); +typedef struct Verb Verb; + +struct Verb +{ + int c; + Formatter fmt; +}; + +void fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups); diff --git a/src/libfmt/locale.c b/src/libfmt/locale.c new file mode 100644 index 0000000..437c61e --- /dev/null +++ b/src/libfmt/locale.c @@ -0,0 +1,16 @@ +#include "internal.h" + +void +fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups) +{ + if(decimal == nil || decimal[0] == '\0') + decimal = "."; + if(thousands == nil) + thousands = ","; + if(groups == nil) + groups = "\3"; + + io->groups = groups; + io->decimal = decimal; + io->thousands = thousands; +} diff --git a/src/libfmt/nsprint.c b/src/libfmt/nsprint.c new file mode 100644 index 0000000..90489e0 --- /dev/null +++ b/src/libfmt/nsprint.c @@ -0,0 +1,14 @@ +#include "internal.h" + +int +fmt·nsprint(int len, char *buf, char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/src/libfmt/open.c b/src/libfmt/open.c new file mode 100644 index 0000000..8aadef5 --- /dev/null +++ b/src/libfmt/open.c @@ -0,0 +1,34 @@ +#include "internal.h" + +static int +flush(fmt·State *io) +{ + int n, fd; + + fd = (uintptr)io->file; + n = io->buffer.cur - io->buffer.beg; + if(n && write(fd, io->buffer.beg, n) != n) + return -1; + + io->buffer.cur = io->buffer.beg; + return io->n; +} + +int +fmt·open(int fd, int len, char *buf, fmt·State *io) +{ + io->buffer.beg = buf; + io->buffer.cur = buf; + io->buffer.end = buf+len; + io->flush = flush; + io->file = (void*)(uintptr)fd; + io->flag = 0; + io->n = 0; + /* no heap needed */ + io->heap = nil; + io->mem = (mem·Reallocator){ 0 }; + + fmt·setlocale(io, nil, nil, nil); + + return 0; +} diff --git a/src/libfmt/print.c b/src/libfmt/print.c new file mode 100644 index 0000000..20b8e00 --- /dev/null +++ b/src/libfmt/print.c @@ -0,0 +1,13 @@ +#include "internal.h" + +int +fmt·print(char *fmt, ...) +{ + int n; + va_list args; + + va_start(args, fmt); + n = fmt·vfprint(1, fmt, args); + va_end(args); + return n; +} diff --git a/src/libfmt/rules.mk b/src/libfmt/rules.mk new file mode 100644 index 0000000..9080bba --- /dev/null +++ b/src/libfmt/rules.mk @@ -0,0 +1,35 @@ +include share/push.mk + +# Local sources +SRCS_$(d):=\ + $(d)/buffer.c\ + $(d)/do.c\ + $(d)/esprint.c\ + $(d)/fprint.c\ + $(d)/locale.c\ + $(d)/nsprint.c\ + $(d)/open.c\ + $(d)/print.c\ + $(d)/sprint.c\ + $(d)/vesprint.c\ + $(d)/vfprint.c\ + $(d)/vnsprint.c\ + $(d)/vprint.c\ + $(d)/vwrite.c\ + $(d)/write.c + +LIBS_$(d):=\ + $(d)/libfmt.a + +CHECK_$(d):=\ + $(d)/test.c + +include share/paths.mk + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(TEST_$(d)): $(UNIT_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/libutf/libutf.a $(OBJ_DIR)/base/base.a + $(COMPLINK) + +include share/pop.mk diff --git a/src/libfmt/sprint.c b/src/libfmt/sprint.c new file mode 100644 index 0000000..f1be6dd --- /dev/null +++ b/src/libfmt/sprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·sprint(char *buf, char *fmt, ...) +{ + int n; + uint len; + va_list args; + + len = 1 << 30; + if(buf+len < buf) + len = -(uintptr)buf-1; + + va_start(args, fmt); + n = fmt·vnsprint(len, buf, fmt, args); + va_end(args); + + return n; +} diff --git a/src/libfmt/test.c b/src/libfmt/test.c new file mode 100644 index 0000000..d81a62e --- /dev/null +++ b/src/libfmt/test.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +typedef struct Complex +{ + double r, i; +} Complex; + +int +Xfmt(fmt·State *io) +{ + Complex c; + c = va_arg(io->args, Complex); + + return fmt·write(io, "(real=%g,imag=%g)", c.r, c.i); +} + +int +main(int argc, char *argv[]) +{ + fmt·print("basic tests\n"); + fmt·print("\tx: %x\n", 0x87654321); + fmt·print("\tu: %u\n", 0x87654321); + fmt·print("\td: %d\n", 0x87654321); + fmt·print("\ts: %s\n", "hi there"); + fmt·print("\tc: %c\n", '!'); + fmt·print("\tg: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\te: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tf: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); + fmt·print("\tsmiley: %C\n", (rune)0x263a); + fmt·print("\t%g %.18g\n", 2e25, 2e25); + fmt·print("\t%2.18g\n", 1.0); + fmt·print("\t%2.18f\n", 1.0); + fmt·print("\t%f\n", 3.1415927/4); + fmt·print("\t%d\n", 23); + fmt·print("\t%i\n", 23); + fmt·print("\t%0.10d\n", 12345); + + fmt·print("%%4%%d tests\n"); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); + fmt·print("\t%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20); + fmt·print("\t%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20); + fmt·print("\t%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20); + + /* test %'d formats */ + fmt·print("%%'%%d tests\n"); + fmt·print("\t%'d %'d %'d\n", 1, 2222, 33333333); + fmt·print("\t%'019d\n", 0); + fmt·print("\t%08d %08d %08d\n", 1, 2222, 33333333); + fmt·print("\t%'08d %'08d %'08d\n", 1, 2222, 33333333); + fmt·print("\t%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345); + fmt·print("\t%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL); + fmt·print("\t%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL); + + /* test precision */ + fmt·print("precision tests\n"); + fmt·print("%020.10d\n", 100); + + /* test install */ + fmt·install('X', Xfmt); + Complex c = { 1.5, -2.3 }; + fmt·print("x = %X\n", c); + + return 0; + +} diff --git a/src/libfmt/vesprint.c b/src/libfmt/vesprint.c new file mode 100644 index 0000000..18f4dd2 --- /dev/null +++ b/src/libfmt/vesprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +char* +fmt·vesprint(char *buf, char *end, char *fmt, va_list args) +{ + fmt·State io; + + if(end <= buf) + return nil; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = end-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur; +} diff --git a/src/libfmt/vfprint.c b/src/libfmt/vfprint.c new file mode 100644 index 0000000..4306ea7 --- /dev/null +++ b/src/libfmt/vfprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vfprint(int fd, char *fmt, va_list args) +{ + int n; + fmt·State io; + char buf[256]; + + fmt·open(fd, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/src/libfmt/vnsprint.c b/src/libfmt/vnsprint.c new file mode 100644 index 0000000..7ded908 --- /dev/null +++ b/src/libfmt/vnsprint.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vnsprint(int len, char *buf, char *fmt, va_list args) +{ + fmt·State io; + + if(len <= 0) + return -1; + + io.n = 0; + io.buffer.beg = io.buffer.cur = buf; + io.buffer.end = buf+len-1; + io.flush = nil; + io.file = nil; + + va_copy(io.args, args); + + fmt·setlocale(&io, nil, nil, nil); + fmt·do(&io, fmt); + + va_end(io.args); + + *(io.buffer.cur) = 0; + return io.buffer.cur - io.buffer.beg; +} diff --git a/src/libfmt/vprint.c b/src/libfmt/vprint.c new file mode 100644 index 0000000..bb3076b --- /dev/null +++ b/src/libfmt/vprint.c @@ -0,0 +1,19 @@ +#include "internal.h" + +int +fmt·vprint(char *fmt, va_list args) +{ + fmt·State io; + int n; + char buf[256]; + + fmt·open(1, sizeof(buf), buf, &io); + + va_copy(io.args, args); + n = fmt·do(&io, fmt); + va_end(io.args); + + if(n > 0 && io.flush(&io) < 0) + return -1; + return n; +} diff --git a/src/libfmt/vwrite.c b/src/libfmt/vwrite.c new file mode 100644 index 0000000..cacdef2 --- /dev/null +++ b/src/libfmt/vwrite.c @@ -0,0 +1,26 @@ +#include "internal.h" + +int +fmt·vwrite(fmt·State *io, char *fmt, va_list args) +{ + int n; + va_list tmp; + + io->flag = io->width = io->prec = 0; + + va_copy(tmp, io->args); + va_end(io->args); + + va_copy(io->args,args); + n = fmt·do(io, fmt); + va_end(io->args); + + va_copy(io->args, tmp); + va_end(tmp); + + io->flag = io->width = io->prec = 0; + + if(n >= 0) + return 0; + return n; +} diff --git a/src/libfmt/write.c b/src/libfmt/write.c new file mode 100644 index 0000000..9a77223 --- /dev/null +++ b/src/libfmt/write.c @@ -0,0 +1,22 @@ +#include "internal.h" + +int +fmt·write(fmt·State *io, char *fmt, ...) +{ + int n; + va_list args; + + io->flag = io->width = io->prec = 0; + + va_copy(args, io->args); + va_end(io->args); + + va_start(io->args, fmt); + n = fmt·do(io, fmt); + va_end(io->args); + + io->flag = io->width = io->prec = 0; + if(n >= 0) + return 0; + return n; +} diff --git a/src/libmath/basic.c b/src/libmath/basic.c new file mode 100644 index 0000000..1341f7b --- /dev/null +++ b/src/libmath/basic.c @@ -0,0 +1,531 @@ +#include +#include +#include + +#include + +// TODO(nnoll): Replace implementations with your own. + +double +math·acos(double x) +{ + return acos(x); +} + +float +math·acosf(float x) +{ + return acosf(x); +} + + +double +math·acosh(double x) +{ + return acosh(x); +} + +float +math·acoshf(float x) +{ + return acoshf(x); +} + + +double +math·asin(double x) +{ + return asin(x); +} + +float +math·asinf(float x) +{ + return asinf(x); +} + + +double +math·asinh(double x) +{ + return asinh(x); +} + +float +math·asinhf(float x) +{ + return asinhf(x); +} + + +double +math·atan(double x) +{ + return atan(x); +} + +float +math·atanf(float x) +{ + return atanf(x); +} + + +double +math·atan2(double x, double y) +{ + return atan2(x, y); +} + +float +math·atan2f(float x, float y) +{ + return atan2f(x, y); +} + +double +math·atanh(double x) +{ + return atanh(x); +} + +float +math·atanhf(float x) +{ + return atanhf(x); +} + + +double +math·cbrt(double x) +{ + return cbrt(x); +} + +float +math·cbrtf(float x) +{ + return cbrtf(x); +} + + +double +math·ceil(double x) +{ + return ceil(x); +} + +float +math·ceilf(float x) +{ + return ceilf(x); +} + +double +math·cos(double x) +{ + return cos(x); +} + +float +math·cosf(float x) +{ + return cosf(x); +} + + +double +math·cosh(double x) +{ + return cosh(x); +} + +float +math·coshf(float x) +{ + return coshf(x); +} + + +double +math·erf(double x) +{ + return erf(x); +} + +float +math·erff(float x) +{ + return erff(x); +} + + +double +math·erfc(double x) +{ + return erfc(x); +} + +float +math·erfcf(float x) +{ + return erfcf(x); +} + + +double +math·exp(double x) +{ + return exp(x); +} + +float +math·expf(float x) +{ + return expf(x); +} + + +double +math·exp2(double x) +{ + return exp2(x); +} + +float +math·exp2f(float x) +{ + return exp2f(x); +} + + +double +math·expm1(double x) +{ + return expm1(x); +} + +float +math·expm1f(float x) +{ + return expm1f(x); +} + + +double +math·floor(double x) +{ + return floor(x); +} + +float +math·floorf(float x) +{ + return floorf(x); +} + + +int +math·ilogb(double x) +{ + return ilogb(x); +} + +int +math·ilogbf(float x) +{ + return ilogbf(x); +} + +double +math·lgamma(double x) +{ + return lgamma(x); +} + +float +math·lgammaf(float x) +{ + return lgammaf(x); +} + + +vlong +math·llrint(double x) +{ + return math·llrint(x); +} + +vlong +math·llrintf(float x) +{ + return math·llrintf(x); +} + + +vlong +math·llround(double x) +{ + return llround(x); +} + +vlong +math·llroundf(float x) +{ + return llroundf(x); +} + + +double +math·log(double x) +{ + return log(x); +} + +float +math·logf(float x) +{ + return logf(x); +} + + +double +math·log10(double x) +{ + return log10(x); +} + +float +math·log10f(float x) +{ + return log10f(x); +} + + +double +math·log1p(double x) +{ + return log1p(x); +} + +float +math·log1pf(float x) +{ + return log1pf(x); +} + + +double +math·log2(double x) +{ + return log2(x); +} + +float +math·log2f(float x) +{ + return log2f(x); +} + + +double +math·logb(double x) +{ + return logb(x); +} + +float +math·logbf(float x) +{ + return logbf(x); +} + + +long +math·lrint(double x) +{ + return lrint(x); +} + +long +math·lrintf(float x) +{ + return lrintf(x); +} + + +long +math·lround(double x) +{ + return lround(x); +} + +long +math·lroundf(float x) +{ + return lroundf(x); +} + + +double math·modf(double, double *); +float math·modff(float, float *); + +double +math·nan(const char * x) +{ + return nan(x); +} + +float +math·nanf(const char * x) +{ + return nanf(x); +} + + +double +math·nearbyint(double x) +{ + return nearbyint(x); +} + +float +math·nearbyintf(float x) +{ + return nearbyintf(x); +} + + +double +math·pow(double x, double exp) +{ + return pow(x, exp); +} + +float +math·powf(float x, float exp) +{ + return powf(x, exp); +} + +double +math·rint(double x) +{ + return rint(x); +} + +float +math·rintf(float x) +{ + return rintf(x); +} + + +double +math·round(double x) +{ + return round(x); +} + +float +math·roundf(float x) +{ + return roundf(x); +} + + +double math·scalbln(double, long); +float math·scalblnf(float, long); + +double math·scalbn(double, int); +float math·scalbnf(float, int); + +double +math·sin(double x) +{ + return sin(x); +} + +float +math·sinf(float x) +{ + return sinf(x); +} + + +double +math·sinh(double x) +{ + return sinh(x); +} + +float +math·sinhf(float x) +{ + return sinhf(x); +} + + +double +math·sqrt(double x) +{ + return sqrt(x); +} + +float +math·sqrtf(float x) +{ + return sqrtf(x); +} + + +double +math·tan(double x) +{ + return tan(x); +} + +float +math·tanf(float x) +{ + return tanf(x); +} + + +double +math·tanh(double x) +{ + return tanh(x); +} + +float +math·tanhf(float x) +{ + return tanhf(x); +} + + +double +math·tgamma(double x) +{ + return tgamma(x); +} + +float +math·tgammaf(float x) +{ + return tgammaf(x); +} + + +double +math·trunc(double x) +{ + return trunc(x); +} + +float +math·truncf(float x) +{ + return truncf(x); +} diff --git a/src/libmath/blas.c b/src/libmath/blas.c new file mode 100644 index 0000000..18f9760 --- /dev/null +++ b/src/libmath/blas.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +/* #include */ + +#define NCOL 2*512 +#define NROW 2*512 +#define NSUM 2*512 +#define NIT 10 +#define INC 1 +error +main() +{ + int i, j, nit; + double *x, *y, *z, *w, res[2]; + + clock_t t; + double tprof[2] = { 0 }; + + rng·init(0); + + x = malloc(sizeof(*x)*NROW*NCOL); + y = malloc(sizeof(*x)*NROW*NCOL); + z = malloc(sizeof(*x)*NROW*NCOL); + w = malloc(sizeof(*x)*NROW*NCOL); + +#define DO_0 t = clock(); \ + blas·dgemm(0,0,NROW,NCOL,NSUM,10.1,x,NROW,y,NROW,1.2,z,NROW);\ + t = clock() - t; \ + res[0] += blas·dasum(NROW*NCOL,z,INC); \ + tprof[0] += 1000.*t/CLOCKS_PER_SEC; \ + +#define DO_1 t = clock(); \ + cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,NROW,NCOL,NSUM,10.1,x,NROW,y,NROW,1.2,w,NROW);\ + t = clock() - t; \ + res[1] += cblas_dasum(NROW*NCOL,w,INC); \ + tprof[1] += 1000.*t/CLOCKS_PER_SEC; + + for (nit = 0; nit < NIT; nit++) { + for (i = 0; i < NROW; i++) { + for (j = 0; j < NCOL; j++) { + x[j + NROW*i] = rng·random(); + y[j + NROW*i] = rng·random(); + z[j + NROW*i] = rng·random(); + w[j + NROW*i] = z[j + NROW*i]; + } + } + + switch (nit % 2) { + case 0: DO_0; DO_1; break; + case 1: DO_1; DO_0; break; + } + } + printf("mean time/iteration (mine): %fms\n", tprof[0]/NIT); + printf("--> result (mine): %f\n", res[0]); + printf("mean time/iteration (openblas): %fms\n", tprof[1]/NIT); + printf("--> result (openblas): %f\n", res[1]); + + return 0; +} diff --git a/src/libmath/blas1.c b/src/libmath/blas1.c new file mode 100644 index 0000000..a8ca085 --- /dev/null +++ b/src/libmath/blas1.c @@ -0,0 +1,58 @@ +#include +#include + +// ----------------------------------------------------------------------- +// Templates + +#include "loop.h" +#define BODY_XY() \ + LOOP(UNROLL, 0, INIT); \ + n = ROUNDBY(len, UNROLL); \ + if (incx == 1 && incy == 1) { \ + for (i = 0; i < n; i+=UNROLL) { \ + LOOP(UNROLL,0,KERNEL,1,1); \ + } \ + } else { \ + for (i = 0; i < n; i+=UNROLL) { \ + LOOP(UNROLL,0,KERNEL,incx,incy);\ + } \ + } \ + \ + for (; i < len; i++) { \ + LOOP(1,0,KERNEL,incx,incy); \ + } + +#define BODY_X() \ + LOOP(UNROLL, 0, INIT); \ + n = ROUNDBY(len, UNROLL); \ + if (incx == 1) { \ + for (i = 0; i < n; i+=UNROLL) { \ + LOOP(UNROLL,0,KERNEL,1); \ + } \ + } else { \ + for (i = 0; i < n; i+=UNROLL) { \ + LOOP(UNROLL,0,KERNEL,incx); \ + } \ + } \ + \ + for (; i < len; i++) { \ + LOOP(1,0,KERNEL,incx); \ + } + +// ----------------------------------------------------------------------- +// Implementation + +#define UNROLL 8 +#define INT int + +#define FLOAT double +#define func(name) blas·d##name +#include "blas1body" + +#undef FLOAT +#undef func + +#define FLOAT float +#define func(name) blas·f##name +#include "blas1body" +#undef FLOAT diff --git a/src/libmath/blas1body b/src/libmath/blas1body new file mode 100644 index 0000000..de4b637 --- /dev/null +++ b/src/libmath/blas1body @@ -0,0 +1,215 @@ +/* vim: set ft=c */ +// ----------------------------------------------------------------------- +// Function implementations + +FLOAT +func(dot)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) +{ +#define INIT(I,...) reg[I] = 0; +#define KERNEL(I, INCX, INCY) reg[I] += x[(INCX)*(i + I)] * y[(INCY)*(i + I)]; + INT i, n; + FLOAT reg[UNROLL]; + + BODY_XY() + + for (i = 1; i < UNROLL; i++) + reg[0] += reg[i]; + + return reg[0]; +#undef INIT +#undef KERNEL +} + +void +func(copy)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) +{ +#define INIT(I,...) +#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] = x[(INCX)*(i + I)]; + INT i, n; + + BODY_XY(); + +#undef INIT +#undef KERNEL +} + +void +func(swap)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) +{ +#define INIT(I,...) +#define KERNEL(I, INCX, INCY) tmp[I] = x[(INCX)*(i + I)], x[(INCX)*(i + I)] = y[(INCY)*(i + I)], y[(INCY)*(i + I)] = tmp[I]; + INT i, n; + FLOAT tmp[UNROLL]; + + BODY_XY(); + +#undef INIT +#undef KERNEL +} + +void +func(axpy)(INT len, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy) +{ +#define INIT(I,...) +#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] += a*x[(INCX)*(i + I)]; + INT i, n; + + BODY_XY(); + +#undef INIT +#undef KERNEL +} + +void +func(axpby)(INT len, FLOAT a, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) +{ +#define INIT(I,...) +#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] = a*x[(INCX)*(i + I)] + b*y[(INCY)*(i + I)]; + INT i, n; + + BODY_XY(); + +#undef INIT +#undef KERNEL +} + +INT +func(argmax)(INT len, FLOAT *x, INT incx) +{ +#define INIT(I,...) max[I] = x[I], idx[I] = I; +#define KERNEL(I, INCX) if (x[(INCX)*(i+I)] > max[I]) {max[i] = x[INCX*(i+I)]; idx[I] = i+I;} + INT i, n; + FLOAT max[UNROLL]; + INT idx[UNROLL]; + + BODY_X(); + + for (i = 1; i < UNROLL; i++) + if (max[i] > max[0]) + idx[0] = idx[i]; + + return idx[0]; +#undef INIT +#undef KERNEL +} + +INT +func(argmin)(INT len, FLOAT *x, INT incx) +{ +#define INIT(I,...) min[I] = x[I], idx[I] = I; +#define KERNEL(I, INCX) if (x[INCX*(i+I)] < min[I]) {min[i] = x[INCX*(i+I)]; idx[I] = i+I;} + INT i, n; + FLOAT min[UNROLL]; + INT idx[UNROLL]; + + BODY_X(); + + for (i = 1; i < UNROLL; i++) + if (min[i] < min[0]) + idx[0] = idx[i]; + + return idx[0]; +#undef INIT +#undef KERNEL +} + +FLOAT +func(asum)(INT len, FLOAT *x, INT incx) +{ +#define INIT(I,...) sum[I] = 0; +#define KERNEL(I, INCX) sum[I] += x[INCX*(i+I)] > 0 ? x[INCX*(i+I)] : -x[INCX*(i+I)]; + INT i, n; + FLOAT sum[UNROLL]; + + BODY_X(); + + for (i = 1; i < UNROLL; i++) + sum[0] += sum[i]; + + return sum[0]; + +#undef INIT +#undef KERNEL +} + +void +func(scale)(INT len, FLOAT a, FLOAT *x, INT incx) +{ +#define INIT(I, ...) +#define KERNEL(I, INCX) x[INCX*(i+I)] *= a; + INT i, n; + + BODY_X(); + +#undef INIT +#undef KERNEL +} + +FLOAT +func(norm)(INT len, FLOAT *x, INT incx) +{ +#define INIT(I, ...) +#define KERNEL(I, INCX) norm[I] += x[INCX*(i+I)] * x[INCX*(i+I)]; + INT i, n; + FLOAT norm[UNROLL]; + + BODY_X(); + + for (i = 1; i < UNROLL; i++) + norm[0] += norm[i]; + + return math·sqrt(norm[0]); + +#undef INIT +#undef KERNEL +} + +void +func(drot)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT cos, FLOAT sin) +{ +#define INIT(I, ...) +#define KERNEL(I, INCX, INCY) tmp[I] = x[INCX*(i+I)], x[INCX*(i+I)] = cos*x[INCX*(i+I)] + sin*y[INCY*(i+I)], y[INCY*(i+I)] = cos*y[INCY*(i+I)] - sin*tmp[I]; + INT i, n; + FLOAT tmp[UNROLL]; + + BODY_XY(); + +#undef INIT +#undef KERNEL +} + +void +func(rotm)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT H[5]) +{ +#define INIT(I, ...) +#define KERNEL(I, INCX, INCY) tmp[I] = x[INCX*(i+I)], x[INCX*(i+I)] = H[1]*x[INCX*(i+I)] + H[2]*y[INCY*(i+I)], y[INCY*(i+I)] = H[3]*tmp[I] + H[4]*y[INCY*(i+I)]; + INT i, n, f; + FLOAT tmp[UNROLL]; + + f = (INT)H[0]; + switch (f) { + case -2: + H[1] = +1; + H[2] = +0; + H[3] = +0; + H[4] = +1; + break; + case -1: + break; + case +0: + H[1] = +1; + H[4] = +1; + break; + case +1: + H[2] = +1; + H[3] = -1; + break; + default: + return; + } + + BODY_XY(); + +#undef INIT +#undef KERNEL +} diff --git a/src/libmath/blas2.c b/src/libmath/blas2.c new file mode 100644 index 0000000..7e4b08e --- /dev/null +++ b/src/libmath/blas2.c @@ -0,0 +1,222 @@ +#include +#include +#include "loop.h" + +// ----------------------------------------------------------------------- +// Templates + +#define BODY_RECT() \ + nr = ROUNDBY(nrow, UNROW); \ + nc = ROUNDBY(ncol, UNCOL); \ + if (incx == 1 && incy == 1) { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,1,1); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,1,1,UNCOL); \ + } \ + for (; c < ncol; c++) { \ + LOOP(UNROW,0,KERN,1,1,1); \ + } \ + LOOP(UNROW,0,FINI,1,1); \ + } \ + } else { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,incx,incy); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c < ncol; c++) { \ + LOOP(UNROW,0,KERN,incx,incy,1); \ + } \ + LOOP(UNROW,0,FINI,incx,incy); \ + } \ + } \ + \ + for (; r < nrow; r++) { \ + LOOP(1,0,INIT,incx,incy); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(1,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c < ncol; c++) { \ + LOOP(1,0,KERN,incx,incy,1); \ + } \ + LOOP(1,0,FINI,incx,incy); \ + } + +#define BODY_LOTRI() \ + nr = ROUNDBY(n, UNROW); \ + if (incx == 1) { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,1); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,1,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(UNROW,0,KERN,1,1); \ + } \ + LOOP(UNROW,0,FINI,1); \ + } \ + } else { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,incx); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,incx,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(UNROW,0,KERN,incx,1); \ + } \ + LOOP(UNROW,0,FINI,incx); \ + } \ + } \ + \ + for (; r < n; r++) { \ + LOOP(1,0,INIT,incx); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(1,0,KERN,incx,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(1,0,KERN,incx,1); \ + } \ + LOOP(1,0,FINI,incx); \ + } + +#define BODY_UPTRI() \ + nr = n - ROUNDBY(n, UNROW); \ + if (incx == 1) { \ + for (r = n-1; r >= nr; r -= UNROW) { \ + LOOP(UNROW,0,INIT,1); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(UNROW,0,KERN,1,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(UNROW,0,KERN,1,1); \ + } \ + LOOP(UNROW,0,FINI,1); \ + } \ + } else { \ + for (r = n-1; r >= nr; r -= UNROW) { \ + LOOP(UNROW,0,INIT,incx); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(UNROW,0,KERN,incx,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(UNROW,0,KERN,incx,1); \ + } \ + LOOP(UNROW,0,FINI,incx); \ + } \ + } \ + \ + for (; r >= 0; r--) { \ + LOOP(1,0,INIT,incx); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(1,0,KERN,incx,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(1,0,KERN,incx,1); \ + } \ + LOOP(1,0,FINI,incx); \ + } + +#define BODY_LOTRI_XY() \ + nr = ROUNDBY(n, UNROW); \ + if (incx == 1 && incy == 1) { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,1,1); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,1,1,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(UNROW,0,KERN,1,1,1); \ + } \ + LOOP(UNROW,0,FINI,1,1); \ + } \ + } else { \ + for (r = 0; r < nr; r += UNROW) { \ + LOOP(UNROW,0,INIT,incx,incy); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(UNROW,0,KERN,incx,incy,1); \ + } \ + LOOP(UNROW,0,FINI,incx, incy); \ + } \ + } \ + \ + for (; r < n; r++) { \ + LOOP(1,0,INIT,incx,incy); \ + nc = ROUNDBY(r, UNCOL); \ + for (c = 0; c < nc; c += UNCOL) { \ + LOOP(1,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c < r; c++) { \ + LOOP(1,0,KERN,incx,incy,1); \ + } \ + LOOP(1,0,FINI,incx,incy); \ + } + +#define BODY_UPTRI_XY() \ + nr = n - ROUNDBY(n, UNROW); \ + if (incx == 1 && incy == 1) { \ + for (r = n-1; r >= nr; r -= UNROW) { \ + LOOP(UNROW,0,INIT,1,1); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(UNROW,0,KERN,1,1,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(UNROW,0,KERN,1,1,1); \ + } \ + LOOP(UNROW,0,FINI,1,1); \ + } \ + } else { \ + for (r = n-1; r >= nr; r -= UNROW) { \ + LOOP(UNROW,0,INIT,incx,incy); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(UNROW,0,KERN,incx,incy,1); \ + } \ + LOOP(UNROW,0,FINI,incx,incy); \ + } \ + } \ + \ + for (; r >= 0; r--) { \ + LOOP(1,0,INIT,incx,incy); \ + nc = n - ROUNDBY(r, UNCOL); \ + for (c = n-1; c >= nc; c -= UNCOL) { \ + LOOP(1,0,KERN,incx,incy,UNCOL); \ + } \ + for (; c > r; c--) { \ + LOOP(1,0,KERN,incx,incy,1); \ + } \ + LOOP(1,0,FINI,incx,incy); \ + } + +// ----------------------------------------------------------------------- +// implementation + +#define UNROW 4 +#define UNCOL 4 + +#define INT int +#define FLOAT double +#define func(name) blas·d##name +#include "blas2body" + +#undef FLOAT +#undef func + +#define FLOAT float +#define func(name) blas·f##name +#include "blas2body" diff --git a/src/libmath/blas2body b/src/libmath/blas2body new file mode 100644 index 0000000..45baf67 --- /dev/null +++ b/src/libmath/blas2body @@ -0,0 +1,256 @@ +/* general matrix multiply */ +error +func(gemv)(uint flag, INT nrow, INT ncol, FLOAT a, FLOAT *m, INT incm, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) +{ + INT r, c, nr, nc; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = 0; +#define TERM(J, I, INCX, INCY) row[I][c+J] * x[INCX*(c+J)] +#define KERN(I, INCX, INCY, LENGTH) reg[I] += EXPAND(LENGTH,0,TERM,+,I,INCX,INCY); +#define FINI(I, INCX, INCY) y[INCY*(r+I)] = b*y[INCY*(r+I)] + a*reg[I]; + + if (!flag) { + BODY_RECT(); + } else { + func(scale)(ncol, b, y, incy); +#undef KERN +#undef FINI +#undef INIT +#undef TERM +#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = a*x[INCX*(r+I)]; +#define TERM(J, I, INCX, INCY) row[I][c+J] * reg[I] +#define KERN(I, INCX, INCY, LENGTH) y[INCY*(c+I)] += EXPAND(LENGTH,0,TERM,+,I,INCX,INCY); +#define FINI(I, INCX, INCY) + BODY_RECT(); + } + + return 0; +#undef INIT +#undef TERM +#undef KERN +#undef FINI +} + +/* symmetric matrix vector multiply (different layouts) */ +void +func(symv)(uint upper, INT n, FLOAT a, FLOAT *m, INT incm, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg1[UNROW], reg2[UNROW]; + +#define INIT(I, INCX, INCY) row[I] = m + (r XX I)*incm, reg1[I] = 0, reg2[I] = 0; +#define TERM1(J, I, INCX, INCY) row[I][c XX J]*x[INCX*(c XX J)] +#define TERM2(J, I, INCX, INCY) row[I][c XX J]*x[INCX*((n-c-1) XX J)] +#define KERN(I, INCX, INCY, REPEAT) reg1[I] += EXPAND(REPEAT,0,TERM1,+,I,INCX,INCY), \ + reg2[I] += EXPAND(REPEAT,0,TERM2,+,I,INCX,INCY); +#define FINI(I, INCX, INCY) y[INCY*(r+I)] += a*(reg1[I] + row[I][r]*x[INCX*r]), \ + y[INCY*(n-r-1+I)] += a*reg2[I]; + + func(scale)(n, b, y, incy); +#define XX + + if (!upper) { + BODY_LOTRI_XY(); + } else { +#undef XX +#define XX - + BODY_UPTRI_XY(); + } +#undef XX + +#undef INIT +} + +void +func(spmv)(uint upper, INT n, FLOAT a, FLOAT *m, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg1[UNROW], reg2[UNROW]; + +#define INIT(I, INCX, INCY) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg1[I] = 0, reg2[I] = 0; + + func(scale)(n, b, y, incy); +#define XX + + if (!upper) { + BODY_LOTRI_XY(); + } else { +#undef XX +#undef INIT +#define INIT(I, INCX, INCY) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg1[I] = 0, reg2[I] = 0; +#define XX - + BODY_UPTRI_XY(); + } +#undef XX + +#undef TERM +#undef INIT +#undef KERN +#undef FINI +} + +/* triangular multiply (different layouts) */ +void +func(trmv)(uint upper, INT n, FLOAT *m, INT incm, FLOAT *x, INT incx) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = 0; +#define TERM(J, I, INCX) row[I][c XX J]*x[INCX*(c XX J)] +#define KERN(I, INCX, REPEAT) reg[I] += EXPAND(REPEAT,0,TERM,+,I,INCX); +#define FINI(I, INCX) x[INCX*(r XX I)] = row[I][r XX I]*x[INCX*(r XX I)] + reg[I]; + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +} + +void +func(tpmv)(uint upper, INT n, FLOAT *m, FLOAT *x, INT incx) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#undef INIT +#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +#undef TERM +#undef KERN +#undef FINI +} + +/* triangular solve (different layouts) */ +void +func(trsv)(uint upper, INT n, FLOAT *m, INT incm, FLOAT *x, INT incx) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = 0; +#define TERM(J, I, INCX) row[I][c XX J]*x[c XX J] +#define KERN(I, INCX, REPEAT) reg[I] += EXPAND(REPEAT,0,TERM,+,I,INCX); +#define SOLN(J, I, INCX) reg[J] += row[I][r XX I]*x[INCX*(r XX I)] +#define FINI(I, INCX) x[INCX*(r XX I)] = reg[I] / row[I][r XX I]; EXPAND_TRI(UNROW,INC(I),SOLN,;,I,INCX); + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +} + +void +func(tpsv)(uint upper, INT n, FLOAT *m, FLOAT *x, INT incx) +{ + INT r, c, nr, nc, i; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#undef INIT +#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +#undef TERM +#undef KERN +#undef SOLN +#undef FINI +} + +/* rank 1 update */ +void +func(ger)(INT nrow, INT ncol, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT *m, INT incm) +{ + INT r, c, nr, nc; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = a*x[INCX*(r+I)]; +#define TERM(J, I, INCX, INCY) row[I][c+J] += reg[I] * y[INCY*(c+J)] +#define KERN(I, INCX, INCY, LENGTH) EXPAND(LENGTH,0,TERM,;,I,INCX, INCY); +#define FINI(I, ...) + + BODY_RECT(); + +#undef INIT +#undef TERM +#undef KERN +#undef FINI +} + +/* symmetric rank 1 update (different memory layouts) */ +void +func(syr)(uint upper, INT n, FLOAT a, FLOAT *x, INT incx, FLOAT *m, INT incm) +{ + INT r, c, nr, nc; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = a*x[INCX*(r XX I)]; +#define TERM(J, I, INCX) row[I][c XX J] += reg[I] * x[INCX*(c XX J)] +#define KERN(I, INCX, LENGTH) EXPAND(LENGTH,0,TERM,;,I,INCX); +#define FINI(I, ...) + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +} + +void +func(spr)(uint upper, INT n, FLOAT a, FLOAT *x, INT incx, FLOAT *m) +{ + INT r, c, nr, nc; + FLOAT *row[UNROW], reg[UNROW]; +#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; + +#define XX + + if (!upper) { + BODY_LOTRI(); + } else { +#undef XX +#undef INIT +#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; +#define XX - + BODY_UPTRI(); + } +#undef XX + +#undef INIT +#undef TERM +#undef KERN +#undef FINI +} diff --git a/src/libmath/blas3.c b/src/libmath/blas3.c new file mode 100644 index 0000000..b048c95 --- /dev/null +++ b/src/libmath/blas3.c @@ -0,0 +1,279 @@ +#include +#include +#include + +#define INT int +#define FLOAT double +#define func(name) blas·d##name + +#define X(i, j) x[j + incx*(i)] +#define Y(i, j) y[j + incy*(i)] +#define Z(i, j) z[j + incz*(i)] + +void +func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) +{ + INT jj, jb, kk, kb, dk, i, j, k, end; + FLOAT r0[8], r1[8], r2[8], r3[8], pf; + + for (i = 0; i < ni; i++) { + for (j = 0; j < nj; j++) { + Z(i,j) *= b; + } + } + + jb = MIN(256, nj); + kb = MIN(48, nk); + for (jj = 0; jj < nj; jj += jb) { + for (kk = 0; kk < nk; kk += kb) { + for (i = 0; i < ni; i += 4) { + for (j = jj; j < jj + jb; j += 8) { + r0[0] = Z(i+0, j+0); r0[1] = Z(i+0, j+1); r0[2] = Z(i+0, j+2); r0[3] = Z(i+0, j+3); + r1[0] = Z(i+1, j+0); r1[1] = Z(i+1, j+1); r1[2] = Z(i+1, j+2); r1[3] = Z(i+1, j+3); + r2[0] = Z(i+2, j+0); r2[1] = Z(i+2, j+1); r2[2] = Z(i+2, j+2); r2[3] = Z(i+2, j+3); + r3[0] = Z(i+3, j+0); r3[1] = Z(i+3, j+1); r3[2] = Z(i+3, j+2); r3[3] = Z(i+3, j+3); + end = MIN(nk, kk+kb); + for (k = kk; k < end; k++) { + pf = a * X(i, k); + r0[0] += pf * Y(k, j+0); r0[1] += pf * Y(k, j+1); r0[2] += pf * Y(k, j+2); r0[3] += pf * Y(k, j+3); + + pf = a * X(i+1, k); + r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); + + pf = a * X(i+2, k); + r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); + + pf = a * X(i+3, k); + r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); + } + Z(i+0, j+0) = r0[0]; Z(i+0, j+1) = r0[1]; Z(i+0, j+2) = r0[2]; Z(i+0, j+3) = r0[3]; + Z(i+1, j+0) = r1[0]; Z(i+1, j+1) = r1[1]; Z(i+1, j+2) = r1[2]; Z(i+1, j+3) = r1[3]; + Z(i+2, j+0) = r2[0]; Z(i+2, j+1) = r2[1]; Z(i+2, j+2) = r2[2]; Z(i+2, j+3) = r2[3]; + Z(i+3, j+0) = r3[0]; Z(i+3, j+1) = r3[1]; Z(i+3, j+2) = r3[2]; Z(i+3, j+3) = r3[3]; + } + } + } + } +} + +#if 0 +void +func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) +{ + int i, j, k; + FLOAT w[nj*nk], acc[4][4]; + + for (i = 0; i < ni; i++) { + for (j = 0; j < nj; j++) { + Z(i,j) *= b; + W(i,j) = Y(j,i); + } + } + + for (i = 0; i < ni; i+=4) { + for (j = 0; j < nj; j+=4) { + memset(acc, 0, sizeof(acc)); + for (k = 0; k < nk; k+=4) { + acc[0][0] += X(i+0,k)*W(j+0,k) + X(i+0,k+1)*W(j+0,k+1) + X(i+0,k+2)*W(j+0,k+2) + X(i+0,k+3)*W(j+0,k+3); + acc[0][1] += X(i+0,k)*W(j+1,k) + X(i+0,k+1)*W(j+1,k+1) + X(i+0,k+2)*W(j+1,k+2) + X(i+0,k+3)*W(j+1,k+3); + acc[0][2] += X(i+0,k)*W(j+2,k) + X(i+0,k+1)*W(j+2,k+1) + X(i+0,k+2)*W(j+2,k+2) + X(i+0,k+3)*W(j+2,k+3); + acc[0][3] += X(i+0,k)*W(j+3,k) + X(i+0,k+1)*W(j+3,k+1) + X(i+0,k+2)*W(j+3,k+2) + X(i+0,k+3)*W(j+3,k+3); + + acc[1][0] += X(i+1,k)*W(j+0,k) + X(i+1,k+1)*W(j+0,k+1) + X(i+1,k+2)*W(j+0,k+2) + X(i+1,k+3)*W(j+0,k+3); + acc[1][1] += X(i+1,k)*W(j+1,k) + X(i+1,k+1)*W(j+1,k+1) + X(i+1,k+2)*W(j+1,k+2) + X(i+1,k+3)*W(j+1,k+3); + acc[1][2] += X(i+1,k)*W(j+2,k) + X(i+1,k+1)*W(j+2,k+1) + X(i+1,k+2)*W(j+2,k+2) + X(i+1,k+3)*W(j+2,k+3); + acc[1][3] += X(i+1,k)*W(j+3,k) + X(i+1,k+1)*W(j+3,k+1) + X(i+1,k+2)*W(j+3,k+2) + X(i+1,k+3)*W(j+3,k+3); + + acc[2][0] += X(i+2,k)*W(j+0,k) + X(i+2,k+1)*W(j+0,k+1) + X(i+2,k+2)*W(j+0,k+2) + X(i+2,k+3)*W(j+0,k+3); + acc[2][1] += X(i+2,k)*W(j+1,k) + X(i+2,k+1)*W(j+1,k+1) + X(i+2,k+2)*W(j+1,k+2) + X(i+2,k+3)*W(j+1,k+3); + acc[2][2] += X(i+2,k)*W(j+2,k) + X(i+2,k+1)*W(j+2,k+1) + X(i+2,k+2)*W(j+2,k+2) + X(i+2,k+3)*W(j+2,k+3); + acc[2][3] += X(i+2,k)*W(j+3,k) + X(i+2,k+1)*W(j+3,k+1) + X(i+2,k+2)*W(j+3,k+2) + X(i+2,k+3)*W(j+3,k+3); + + acc[2][0] += X(i+3,k)*W(j+0,k) + X(i+3,k+1)*W(j+0,k+1) + X(i+3,k+2)*W(j+0,k+2) + X(i+3,k+3)*W(j+0,k+3); + acc[2][1] += X(i+3,k)*W(j+1,k) + X(i+3,k+1)*W(j+1,k+1) + X(i+3,k+2)*W(j+1,k+2) + X(i+3,k+3)*W(j+1,k+3); + acc[2][2] += X(i+3,k)*W(j+2,k) + X(i+3,k+1)*W(j+2,k+1) + X(i+3,k+2)*W(j+2,k+2) + X(i+3,k+3)*W(j+2,k+3); + acc[2][3] += X(i+3,k)*W(j+3,k) + X(i+3,k+1)*W(j+3,k+1) + X(i+3,k+2)*W(j+3,k+2) + X(i+3,k+3)*W(j+3,k+3); + // Z(i,j) += X(i,k)*Y(k,j); + } + Z(i+0,j+1) = a*acc[0][0]; + Z(i+0,j+2) = a*acc[0][1]; + Z(i+0,j+3) = a*acc[0][2]; + Z(i+0,j+4) = a*acc[0][3]; + + Z(i+1,j+1) = a*acc[1][0]; + Z(i+1,j+2) = a*acc[1][1]; + Z(i+1,j+3) = a*acc[1][2]; + Z(i+1,j+4) = a*acc[1][3]; + + Z(i+2,j+1) = a*acc[2][0]; + Z(i+2,j+2) = a*acc[2][1]; + Z(i+2,j+3) = a*acc[2][2]; + Z(i+2,j+4) = a*acc[2][3]; + + Z(i+3,j+1) = a*acc[3][0]; + Z(i+3,j+2) = a*acc[3][1]; + Z(i+3,j+3) = a*acc[3][2]; + Z(i+3,j+4) = a*acc[3][3]; + } + } +} +#endif + +#if 0 +void +func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) +{ + int i, j, k, ri, rj, rk; + FLOAT reg[4][4], *xrow[4], *yrow[4]; + + for (i = 0; i < ni; i++) { + for (j = 0; j < nj; j++) { + z[j + incz*i] *= b; + } + } + + for (i = 0; i < ni; i += 4) { + xrow[0] = x + incx*(i+0); + xrow[1] = x + incx*(i+1); + xrow[2] = x + incx*(i+2); + xrow[3] = x + incx*(i+3); + for (k = 0; k < nk; k+=4) { + yrow[0] = y + incy*(k+0); + yrow[1] = y + incy*(k+1); + yrow[2] = y + incy*(k+2); + yrow[3] = y + incy*(k+3); + reg[0][0] = a * xrow[0][k+0]; reg[0][1] = a * xrow[0][k+1]; reg[0][2] = a * xrow[0][k+2]; reg[0][3] = a * xrow[0][k+3]; + reg[1][0] = a * xrow[1][k+0]; reg[1][1] = a * xrow[1][k+1]; reg[1][2] = a * xrow[1][k+2]; reg[1][3] = a * xrow[1][k+3]; + reg[2][0] = a * xrow[2][k+0]; reg[2][1] = a * xrow[2][k+1]; reg[2][2] = a * xrow[2][k+2]; reg[2][3] = a * xrow[2][k+3]; + reg[3][0] = a * xrow[3][k+0]; reg[3][1] = a * xrow[3][k+1]; reg[3][2] = a * xrow[3][k+2]; reg[3][3] = a * xrow[3][k+3]; + for (j = 0; j < nj; j += 1) { + z[j + incz*(i+0)] += (reg[0][0]*yrow[0][j]+reg[0][1]*yrow[1][j]+reg[0][2]*yrow[2][j]+reg[0][3]*yrow[3][j]); + z[j + incz*(i+1)] += (reg[1][0]*yrow[0][j]+reg[1][1]*yrow[1][j]+reg[1][2]*yrow[2][j]+reg[1][3]*yrow[3][j]); + z[j + incz*(i+2)] += (reg[2][0]*yrow[0][j]+reg[2][1]*yrow[1][j]+reg[2][2]*yrow[2][j]+reg[2][3]*yrow[3][j]); + z[j + incz*(i+3)] += (reg[3][0]*yrow[0][j]+reg[3][1]*yrow[1][j]+reg[3][2]*yrow[2][j]+reg[3][3]*yrow[3][j]); + } + } + } +} +#endif + +#if 0 +void +func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) +{ + int i, j, k, ri, rj, rk; + FLOAT r[4][4], *row[4]; + + for (i = 0; i < ni; i++) { + for (j = 0; j < nj; j++) { + Z(i, j) *= b; + } + } + + for (i = 0; i < ni; i+=4) { + for (j = 0; j < nj; j+=4) { + r[0][0] = 0; r[0][1] = 0; r[0][2] = 0; r[0][3] = 0; + r[1][0] = 0; r[1][1] = 0; r[1][2] = 0; r[1][3] = 0; + r[2][0] = 0; r[2][1] = 0; r[2][2] = 0; r[2][3] = 0; + r[3][0] = 0; r[3][1] = 0; r[3][2] = 0; r[3][3] = 0; + row[0] = &X(i+0, 0); + row[1] = &X(i+1, 0); + row[2] = &X(i+2, 0); + row[3] = &X(i+3, 0); + for (k = 0; k < nk; k++) { + r[0][0] += row[0][k]*Y(k,0); r[0][1] += row[0][k]*Y(k,1); r[0][2] += row[0][k]*Y(k,2); r[0][3] += row[0][k]*Y(k,3); + r[1][0] += row[1][k]*Y(k,0); r[1][1] += row[1][k]*Y(k,1); r[1][2] += row[1][k]*Y(k,2); r[1][3] += row[1][k]*Y(k,3); + r[2][0] += row[2][k]*Y(k,0); r[2][1] += row[2][k]*Y(k,1); r[2][2] += row[2][k]*Y(k,2); r[2][3] += row[2][k]*Y(k,3); + r[3][0] += row[3][k]*Y(k,0); r[3][1] += row[3][k]*Y(k,1); r[3][2] += row[3][k]*Y(k,2); r[3][3] += row[3][k]*Y(k,3); + } + Z(i+0, j+0) += r[0][0]; Z(i+0, j+1) += r[0][1]; Z(i+0, j+2) += r[0][2]; Z(i+0, j+3) += r[0][3]; + Z(i+1, j+0) += r[1][0]; Z(i+1, j+1) += r[1][1]; Z(i+1, j+2) += r[1][2]; Z(i+1, j+3) += r[1][3]; + Z(i+2, j+0) += r[2][0]; Z(i+2, j+1) += r[2][1]; Z(i+2, j+2) += r[2][2]; Z(i+2, j+3) += r[2][3]; + Z(i+3, j+0) += r[3][0]; Z(i+3, j+1) += r[3][1]; Z(i+3, j+2) += r[3][2]; Z(i+3, j+3) += r[3][3]; + } + } +} +#endif + +#if 0 +void +func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) +{ + int i, j, k, ri, rj, rk; + FLOAT *xrow[8], *yrow[8], reg; + + for (i = 0; i < ni; i++) { + for (j = 0; j < nj; j++) { + z[j + incz*i] *= b; + } + } + + ri = ni & ~7; + rj = nj & ~7; + for (i = 0; i < ri; i += 8) { + xrow[0] = x + incx*(i+0); + xrow[1] = x + incx*(i+1); + xrow[2] = x + incx*(i+2); + xrow[3] = x + incx*(i+3); + xrow[4] = x + incx*(i+4); + xrow[5] = x + incx*(i+5); + xrow[6] = x + incx*(i+6); + xrow[7] = x + incx*(i+7); + for (j = 0; j < rj; j += 8) { + yrow[0] = y + incy*(j+0); + yrow[1] = y + incy*(j+1); + yrow[2] = y + incy*(j+2); + yrow[3] = y + incy*(j+3); + yrow[4] = y + incy*(j+4); + yrow[5] = y + incy*(j+5); + yrow[6] = y + incy*(j+6); + yrow[7] = y + incy*(j+7); + for (k = 0; k < nk; k++) { + reg = a*(yrow[0][k] + yrow[1][k] + yrow[2][k] + yrow[3][k] + yrow[4][k] + yrow[5][k] + yrow[6][k] + yrow[7][k]); + z[k + incz*(i+0)] += xrow[0][k]*reg; + z[k + incz*(i+1)] += xrow[1][k]*reg; + z[k + incz*(i+2)] += xrow[2][k]*reg; + z[k + incz*(i+3)] += xrow[3][k]*reg; + z[k + incz*(i+4)] += xrow[4][k]*reg; + z[k + incz*(i+5)] += xrow[5][k]*reg; + z[k + incz*(i+6)] += xrow[6][k]*reg; + z[k + incz*(i+7)] += xrow[7][k]*reg; + } + } + for (; j < nj; j++) { + for (k = 0; k < nk; k++) { + reg = a*y[k+incy*j]; + z[k + incz*(i+0)] += xrow[0][k]*reg; + z[k + incz*(i+1)] += xrow[1][k]*reg; + z[k + incz*(i+2)] += xrow[2][k]*reg; + z[k + incz*(i+3)] += xrow[3][k]*reg; + z[k + incz*(i+4)] += xrow[4][k]*reg; + z[k + incz*(i+5)] += xrow[5][k]*reg; + z[k + incz*(i+6)] += xrow[6][k]*reg; + z[k + incz*(i+7)] += xrow[7][k]*reg; + } + } + } + + for (; i < ni; i++) { + for (j = 0; j < rj; j += 8) { + yrow[0] = y + incy*(j+0); + yrow[1] = y + incy*(j+1); + yrow[2] = y + incy*(j+2); + yrow[3] = y + incy*(j+3); + yrow[4] = y + incy*(j+4); + yrow[5] = y + incy*(j+5); + yrow[6] = y + incy*(j+6); + yrow[7] = y + incy*(j+7); + for (k = 0; k < nk; k++) { + z[k + incz*(i)] += a*x[k + incx*i]*(yrow[0][k] + yrow[1][k] + yrow[2][k] + yrow[3][k] + yrow[4][k] + yrow[5][k] + yrow[6][k] + yrow[7][k]); + } + } + for (; j < nj; j++) { + for (k = 0; k < nk; k++) { + z[k + incz*i] += a*x[k + incx*i]*y[k + incy*j]; + } + } + } +} +#endif diff --git a/src/libmath/lapack.c b/src/libmath/lapack.c new file mode 100644 index 0000000..e69de29 diff --git a/src/libmath/linalg.c b/src/libmath/linalg.c new file mode 100644 index 0000000..8551ff1 --- /dev/null +++ b/src/libmath/linalg.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include + +// ----------------------------------------------------------------------- +// Vector + +void +linalg·normalize(math·Vector vec) +{ + double norm; + + norm = blas·normd(vec.len, vec.data, 1); + blas·scaled(vec.len, 1/norm, vec.data, 1); +} +// TODO: Write blas wrappers that eat vectors for convenience + +// ----------------------------------------------------------------------- +// Matrix +// +// NOTE: all matrices are row major oriented + +/* + * linalg·lq + * computes the LQ decomposition of matrix M: M = LQ + * L is lower triangular + * Q is orthogonal -> transp(Q) * Q = I + * + * m: matrix to factorize. changes in place + * + lower triangle -> L + * + upper triangle -> all reflection vectors stored in rows + * w: working buffer: len = ncols! + */ +error +linalg·lq(math·Matrix m, math·Vector w) +{ + int i, j, len; + double *row, mag; + enum { + err·nil, + err·baddims, + }; + + if (m.dim[0] > m.dim[1]) { + return err·baddims; + } + + for (i = 0; i < m.dim[0]; i++, m.data += m.dim[1]) { + row = m.data + i; + len = m.dim[0] - i; + + // TODO: Don't want to compute norm twice!! + w.data[0] = math·sgn(row[0]) * blas·normd(len, row, 1); + blas·axpyd(len, 1.0, row, 1, w.data, 1); + mag = blas·normd(len, w.data, 1); + blas·scaled(len, 1/mag, w.data, 1); + + blas·copyd(len - m.dim[0], w.data, 1, m.data + i, 1); + } + + return err·nil; +} diff --git a/src/libmath/loop.h b/src/libmath/loop.h new file mode 100644 index 0000000..a877d84 --- /dev/null +++ b/src/libmath/loop.h @@ -0,0 +1,114 @@ +#pragma once + +/* increment operator */ +#define INC2(x) INC_##x +#define INC1(x) INC2(x) +#define INC(x) INC1(x) + +#define INC_0 1 +#define INC_1 2 +#define INC_2 3 +#define INC_3 4 +#define INC_4 5 +#define INC_5 6 +#define INC_6 7 +#define INC_7 8 +#define INC_8 9 +#define INC_9 10 +#define INC_10 11 +#define INC_11 12 +#define INC_12 13 +#define INC_13 14 +#define INC_14 15 +#define INC_15 16 + +#define ROUNDBY(x, n) ((x) & ~((n)-1)) + +/* subtraction tables */ +#define SUB2(x, y) SUB_##x##_##y +#define SUB1(x, y) SUB2(x, y) +#define SUB(x, y) SUB1(x, y) +#define SUB_8_0 8 +#define SUB_8_1 7 +#define SUB_8_2 6 +#define SUB_8_3 5 +#define SUB_8_4 4 +#define SUB_8_5 3 +#define SUB_8_6 2 +#define SUB_8_7 1 +#define SUB_8_8 0 +#define SUB_7_0 7 +#define SUB_7_1 6 +#define SUB_7_2 5 +#define SUB_7_3 4 +#define SUB_7_4 3 +#define SUB_7_5 2 +#define SUB_7_6 1 +#define SUB_7_7 0 +#define SUB_6_0 6 +#define SUB_6_1 5 +#define SUB_6_2 4 +#define SUB_6_3 3 +#define SUB_6_4 2 +#define SUB_6_5 1 +#define SUB_6_6 0 +#define SUB_5_0 5 +#define SUB_5_1 4 +#define SUB_5_2 3 +#define SUB_5_3 2 +#define SUB_5_4 1 +#define SUB_5_5 0 +#define SUB_4_0 4 +#define SUB_4_1 3 +#define SUB_4_2 2 +#define SUB_4_3 1 +#define SUB_4_4 0 +#define SUB_3_0 3 +#define SUB_3_1 2 +#define SUB_3_2 1 +#define SUB_3_3 0 +#define SUB_2_0 2 +#define SUB_2_1 1 +#define SUB_2_2 0 +#define SUB_1_0 1 +#define SUB_1_1 0 + +/* rounding operator */ +#define ROUNDBY(x, n) ((x) & ~((n)-1)) + +/* loop unrolling (vertical) */ +#define LOOP1(I,STMT,...) STMT(I,__VA_ARGS__) +#define LOOP2(I,STMT,...) STMT(I,__VA_ARGS__) LOOP1(INC(I),STMT,__VA_ARGS__) +#define LOOP3(I,STMT,...) STMT(I,__VA_ARGS__) LOOP2(INC(I),STMT,__VA_ARGS__) +#define LOOP4(I,STMT,...) STMT(I,__VA_ARGS__) LOOP3(INC(I),STMT,__VA_ARGS__) +#define LOOP5(I,STMT,...) STMT(I,__VA_ARGS__) LOOP4(INC(I),STMT,__VA_ARGS__) +#define LOOP6(I,STMT,...) STMT(I,__VA_ARGS__) LOOP5(INC(I),STMT,__VA_ARGS__) +#define LOOP7(I,STMT,...) STMT(I,__VA_ARGS__) LOOP6(INC(I),STMT,__VA_ARGS__) +#define LOOP8(I,STMT,...) STMT(I,__VA_ARGS__) LOOP7(INC(I),STMT,__VA_ARGS__) +#define LOOP9(I,STMT,...) STMT(I,__VA_ARGS__) LOOP8(INC(I),STMT,__VA_ARGS__) +#define LOOP10(I,STMT,...) STMT(I,__VA_ARGS__) LOOP9(INC(I),STMT,__VA_ARGS__) +#define LOOP11(I,STMT,...) STMT(I,__VA_ARGS__) LOOP10(INC(I),STMT,__VA_ARGS__) +#define LOOP12(I,STMT,...) STMT(I,__VA_ARGS__) LOOP11(INC(I),STMT,__VA_ARGS__) +#define LOOP13(I,STMT,...) STMT(I,__VA_ARGS__) LOOP12(INC(I),STMT,__VA_ARGS__) +#define LOOP14(I,STMT,...) STMT(I,__VA_ARGS__) LOOP13(INC(I),STMT,__VA_ARGS__) +#define LOOP15(I,STMT,...) STMT(I,__VA_ARGS__) LOOP14(INC(I),STMT,__VA_ARGS__) +#define LOOP16(I,STMT,...) STMT(I,__VA_ARGS__) LOOP15(INC(I),STMT,__VA_ARGS__) + +#define _LOOP_(n,I,STMT,...) LOOP##n(I,STMT,__VA_ARGS__) +#define LOOP(n,I,STMT,...) _LOOP_(n,I,STMT,__VA_ARGS__) + +/* loop expansion (horizontal) */ +#define EXPAND0(I,TERM,OP,...) +#define EXPAND1(I,TERM,OP,...) TERM(I,__VA_ARGS__) +#define EXPAND2(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND1(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND3(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND2(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND4(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND3(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND5(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND4(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND6(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND5(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND7(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND6(INC(I),TERM,OP,__VA_ARGS__) +#define EXPAND8(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND7(INC(I),TERM,OP,__VA_ARGS__) + +#define _EXPAND_(n,I,TERM,OP,...) EXPAND##n(I,TERM,OP,__VA_ARGS__) +#define EXPAND(n,I,TERM,OP,...) _EXPAND_(n,I,TERM,OP,__VA_ARGS__) +#define EXPAND_TRI1(n,I,TERM,OP,...) EXPAND(n,I,TERM,OP,__VA_ARGS__) +#define EXPAND_TRI(n,I,TERM,OP,...) EXPAND_TRI1(SUB(n,I),I,TERM,OP,__VA_ARGS__) diff --git a/src/libmath/matrix.c b/src/libmath/matrix.c new file mode 100644 index 0000000..e8bca0b --- /dev/null +++ b/src/libmath/matrix.c @@ -0,0 +1,176 @@ +#include +#include +#include + +/* TODO: replace (incrementally) with native C version! */ +#include +#include + +// ----------------------------------------------------------------------- +// level 1 + +error +la·vecslice(math·Vector *x, int min, int max, int inc) +{ + if (max > x->len || min < 0) { + errorf("out of bounds: attempted to access vector past length"); + return 1; + } + x->len = (max - min) / inc; + x->d += x->inc * min; + x->inc *= inc; + + return 0; +} + +/* simple blas wrappers */ +void +la·veccopy(math·Vector *dst, math·Vector *src) +{ + return cblas_dcopy(src->len, src->d, src->inc, dst->d, dst->inc); +} + +double +la·vecnorm(math·Vector *x) +{ + return cblas_dnrm2(x->len, x->d, x->inc); +} + +void +la·vecscale(math·Vector *x, double a) +{ + return cblas_dscal(x->len, a, x->d, x->inc); +} + +double +la·vecdot(math·Vector *x, math·Vector *y) +{ + return cblas_ddot(x->len, x->d, x->inc, y->d, y->inc); +} + +// ----------------------------------------------------------------------- +// level 2 + +error +la·vecmat(math·Vector *x, math·Matrix *M) +{ + if (M->dim[1] != x->len) { + errorf("incompatible matrix dimensions"); + return 1; + } + if (M->state & ~mat·trans) + cblas_dgemv(CblasRowMajor,CblasNoTrans,M->dim[0],M->dim[1],1.,M->d,M->inc,x->d,x->inc,0.,x->d,x->inc); + else + cblas_dgemv(CblasRowMajor,CblasTrans,M->dim[0],M->dim[1],1.,M->d,M->inc,x->d,x->inc,0.,x->d,x->inc); + + return 0; +} + +// ----------------------------------------------------------------------- +// level 3 + +void +la·transpose(math·Matrix *X) +{ + int tmp; + X->state ^= mat·trans; + tmp = X->dim[0], X->dim[0] = X->dim[1], X->dim[1] = tmp; +} + +error +la·matrow(math·Matrix *X, int r, math·Vector *row) +{ + if (r < 0 || r >= X->dim[0]) { + errorf("out of bounds"); + return 1; + } + + row->len = X->dim[1]; + row->inc = 1; + row->d = X->d + X->dim[1] * r; + + return 0; +} + +error +la·matcol(math·Matrix *X, int c, math·Vector *col) +{ + if (c < 0 || c >= X->dim[1]) { + errorf("out of bounds"); + return 1; + } + + col->len = X->dim[0]; + col->inc = X->dim[1]; + col->d = X->d + c; + + return 0; +} + +error +la·matslice(math·Matrix *X, int r[3], int c[3]) +{ + /* TODO */ + return 0; +} + +error +la·eig(math·Matrix *X) +{ + +} + +/* X = A*B */ +error +la·matmul(math·Matrix *X, math·Matrix *A, math·Matrix *B) +{ + if (A->dim[1] != B->dim[0]) { + errorf("number of interior dimensions of A '%d' not equal to that of B '%d'", A->dim[1], B->dim[0]); + return 1; + } + if (X->dim[0] != A->dim[0]) { + errorf("number of exterior dimensions of X '%d' not equal to that of A '%d'", X->dim[0], A->dim[0]); + return 1; + } + if (X->dim[1] != B->dim[1]) { + errorf("number of exterior dimensions of X '%d' not equal to that of B '%d'", X->dim[1], B->dim[1]); + return 1; + } + + if (X->state & ~mat·trans) + if (A->state & ~mat·trans) + cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); + else + cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); + else + if (A->state & ~mat·trans) + cblas_dgemm(CblasRowMajor,CblasTrans,CblasNoTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); + else + cblas_dgemm(CblasRowMajor,CblasTrans,CblasTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); + + return 0; +} + +/* + * solves A*X=B + * pass in B via X + */ +error +la·solve(math·Matrix *X, math·Matrix *A) +{ + error err; + int n, *ipv; + static int buf[512]; + if (n = A->dim[0], n < arrlen(buf)) { + ipv = buf; + n = 0; + } else + ipv = malloc(n*sizeof(*ipv)); + + /* TODO: utilize more specific regimes if applicable */ + err = LAPACKE_dgesv(LAPACK_ROW_MAJOR,A->dim[0],X->dim[1],A->d,A->inc,ipv,X->d,X->inc); + + if (n) + free(ipv); + return err; +} diff --git a/src/libmath/rules.mk b/src/libmath/rules.mk new file mode 100644 index 0000000..577aba4 --- /dev/null +++ b/src/libmath/rules.mk @@ -0,0 +1,27 @@ +include share/push.mk + +# Iterate through subdirectory tree + +# local sources +SRCS_$(d):=\ + $(d)/basic.c\ + $(d)/blas1.c\ + $(d)/blas2.c\ + $(d)/blas3.c +CHECK_$(d):=\ + +# outputs +LIBS_$(d):=\ + $(d)/libmath.a + +include share/paths.mk + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +$(TEST_$(d)): TCFLAGS = -D_GNU_SOURCE +$(TEST_$(d)): TCLIBS = -lpthread -lm $(LIB_DIR)/libblas.a +$(TEST_$(d)): $(UNIT_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/base/base.a + $(LINK) + +include share/pop.mk diff --git a/src/libmath/test.c b/src/libmath/test.c new file mode 100644 index 0000000..66700f8 --- /dev/null +++ b/src/libmath/test.c @@ -0,0 +1,471 @@ +#include +#include +/* #include */ + +#include + +#include + +// ----------------------------------------------------------------------- +// Vectors + +/* + * NOTE: I'm not sure I like stashing the header in _all_ vectors + * The only way to fix is to have a library based allocator... + */ +typedef struct math·Vec +{ + struct { + void *h; + mem·Allocator heap; + }; + int len; + double *d; +} math·Vec; + +math·Vec +math·makevec(int len, mem·Allocator heap, void *h) +{ + math·Vec v; + v.len = len; + v.heap = heap; + v.h = h; + v.d = heap.alloc(h, 1, len*sizeof(double)); + + // memset(v.d, 0, len*sizeof(double)); + + return v; +} + +error +math·freevec(math·Vec *v) +{ + if (v->h == nil && v->heap.alloc == nil && v->heap.free == nil) { + errorf("attempting to free a vector that doesn't own its data"); + return 1; + } + v->heap.free(v->h, v->d); + v->d = nil; + v->len = 0; + + return 0; +} + +math·Vec +math·copyvec(math·Vec v) +{ + math·Vec cpy; + cpy.heap = v.heap; + cpy.h = v.h; + cpy.len = v.len; + cpy.d = cpy.heap.alloc(cpy.h, 1, v.len); + + memcpy(cpy.d, v.d, sizeof(double)*v.len); + return cpy; +} + +/* + * Scale vector + */ + +static +void +scale_kernel8_avx2(int n, double *x, double a) +{ + __m128d a128; + __m256d a256; + register int i; + + a128 = _mm_load_sd(&a); + a256 = _mm256_broadcastsd_pd(a128); + for (i = 0; i < n; i += 8) { + _mm256_storeu_pd(x+i+0, a256 * _mm256_loadu_pd(x+i+0)); + _mm256_storeu_pd(x+i+4, a256 * _mm256_loadu_pd(x+i+4)); + } +} + +static +void +scale_kernel8(int n, double *x, double a) +{ + register int i; + for (i = 0; i < n; i += 8) { + x[i+0] *= a; + x[i+1] *= a; + x[i+2] *= a; + x[i+3] *= a; + x[i+4] *= a; + x[i+5] *= a; + x[i+6] *= a; + x[i+7] *= a; + } +} + +void +math·scalevec(math·Vec u, double a) +{ + int n; + + n = u.len & ~7; + scale_kernel8_avx2(n, u.d, a); + + for (; n < u.len; n++) { + u.d[n] *= a; + } +} + +/* + * Add scaled vector + */ + +static +void +daxpy_kernel8_avx2(int n, double *x, double *y, double a) +{ + __m128d a128; + __m256d a256; + register int i; + + a128 = _mm_load_sd(&a); + a256 = _mm256_broadcastsd_pd(a128); + for (i = 0; i < n; i += 8) { + _mm256_storeu_pd(x+i+0, _mm256_loadu_pd(x+i+0) + a256 * _mm256_loadu_pd(y+i+0)); + _mm256_storeu_pd(x+i+4, _mm256_loadu_pd(x+i+4) + a256 * _mm256_loadu_pd(y+i+4)); + } +} + +static +void +daxpy_kernel8(int n, double *x, double *y, double a) +{ + register int i; + for (i = 0; i < n; i += 8) { + x[i+0] += a*y[i+0]; + x[i+1] += a*y[i+1]; + x[i+2] += a*y[i+2]; + x[i+3] += a*y[i+3]; + x[i+4] += a*y[i+4]; + x[i+5] += a*y[i+5]; + x[i+6] += a*y[i+6]; + x[i+7] += a*y[i+7]; + } +} + +/* performs u = u + a*v */ +void +math·addvec(math·Vec u, math·Vec v, double a) +{ + int n; + + n = u.len & ~7; + daxpy_kernel8_avx2(n, u.d, v.d, a); + + for (; n < u.len; n++) { + u.d[n] += a*v.d[n]; + } +} + +/* + * Dot product + */ + +static +double +dot_kernel8_avx2(int len, double *x, double *y) +{ + register int i; + __m256d sum[4]; + __m128d res; + + for (i = 0; i < arrlen(sum); i++) { + sum[i] = _mm256_setzero_pd(); + } + + for (i = 0; i < len; i += 16) { + sum[0] += _mm256_loadu_pd(x+i+0) * _mm256_loadu_pd(y+i+0); + sum[1] += _mm256_loadu_pd(x+i+4) * _mm256_loadu_pd(y+i+4); + sum[2] += _mm256_loadu_pd(x+i+8) * _mm256_loadu_pd(y+i+8); + sum[3] += _mm256_loadu_pd(x+i+12) * _mm256_loadu_pd(y+i+12); + } + + sum[0] += sum[1] + sum[2] + sum[3]; + + res = _mm_add_pd(_mm256_extractf128_pd(sum[0], 0), _mm256_extractf128_pd(sum[0], 1)); + res = _mm_hadd_pd(res, res); + + return res[0]; +} + +static +double +dot_kernel8_fma3(int len, double *x, double *y) +{ + register int i; + __m256d sum[4]; + __m128d res; + + for (i = 0; i < arrlen(sum); i++) { + sum[i] = _mm256_setzero_pd(); + } + + for (i = 0; i < len; i += 16) { + sum[0] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+0), _mm256_loadu_pd(y+i+0), sum[0]); + sum[1] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+4), _mm256_loadu_pd(y+i+4), sum[1]); + sum[2] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+8), _mm256_loadu_pd(y+i+8), sum[2]); + sum[3] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+12), _mm256_loadu_pd(y+i+12), sum[3]); + } + + sum[0] += sum[1] + sum[2] + sum[3]; + + res = _mm_add_pd(_mm256_extractf128_pd(sum[0], 0), _mm256_extractf128_pd(sum[0], 1)); + res = _mm_hadd_pd(res, res); + + return res[0]; +} + +static +double +dot_kernel8(int len, double *x, double *y) +{ + double res; + register int i; + + for (i = 0; i < len; i += 8) { + res += x[i] * y[i] + + x[i+1] * y[i+1] + + x[i+2] * y[i+2] + + x[i+3] * y[i+3] + + x[i+4] * y[i+4] + + x[i+5] * y[i+5] + + x[i+6] * y[i+6] + + x[i+7] * y[i+7]; + } + + return res; +} + +double +math·dot(math·Vec u, math·Vec v) +{ + int i, len; + double res; + + len = u.len & ~15; // neat trick + res = dot_kernel8_fma3(len, u.d, v.d); + + for (i = len; i < u.len; i++) { + res += u.d[i] * v.d[i]; + } + + return res; +} + +// ----------------------------------------------------------------------- +// Matrix + +typedef struct math·Mtx +{ + struct { + void *h; + mem·Allocator heap; + }; + int dim[2]; + double *d; +} math·Mtx; + +math·Mtx +math·makemtx(int n, int m, mem·Allocator heap, void *h) +{ + math·Mtx a; + a.dim[0] = n; + a.dim[1] = m; + a.heap = heap; + a.h = h; + a.d = heap.alloc(h, 1, n*m*sizeof(double)); + + // memset(a.d, 0, n*m*sizeof(double)); + + return a; +} + +error +math·freemtx(math·Vec *m) +{ + if (m->h == nil && m->heap.alloc == nil && m->heap.free == nil) { + errorf("attempting to free a matrix that doesn't own its data"); + return 1; + } + m->heap.free(m->h, m->d); + m->d = nil; + m->len = 0; + + return 0; +} + +/************************************************ + * multiply matrix to vector + ***********************************************/ + +/* + * Notation: (number of rows) x (number of columns) _ unroll factor + * N => variable we sum over + */ +static +void +mtxvec_kernel4xN_4_avx2(int ncol, double **row, double *x, double *y) +{ + int c; + __m128d hr; + __m256d x256, r256[4]; + + for (c = 0; c < 4; c++) { + r256[c] = _mm256_setzero_pd(); + } + + for (c = 0; c < ncol; c += 4) { + x256 = _mm256_loadu_pd(x+c); + r256[0] += x256 * _mm256_loadu_pd(row[0] + c); + r256[1] += x256 * _mm256_loadu_pd(row[1] + c); + r256[2] += x256 * _mm256_loadu_pd(row[2] + c); + r256[3] += x256 * _mm256_loadu_pd(row[3] + c); + } + + for (c = 0; c < 4; c++) { + hr = _mm_add_pd(_mm256_extractf128_pd(r256[c], 0), _mm256_extractf128_pd(r256[c], 1)); + hr = _mm_hadd_pd(hr, hr); + y[c] = hr[0]; + } +} + +static +void +mtxvec_kernel4xN_4(int ncol, double **row, double *x, double *y) +{ + int c; + double res[4]; + + res[0] = 0.; + res[1] = 0.; + res[2] = 0.; + res[3] = 0.; + + for (c = 0; c < ncol; c += 4) { + res[0] += row[0][c+0]*x[c+0] + row[0][c+1]*x[c+1] + row[0][c+2]*x[c+2] + row[0][c+3]*x[c+3]; + res[1] += row[1][c+0]*x[c+0] + row[1][c+1]*x[c+1] + row[1][c+2]*x[c+2] + row[1][c+3]*x[c+3]; + res[2] += row[2][c+0]*x[c+0] + row[2][c+1]*x[c+1] + row[2][c+2]*x[c+2] + row[2][c+3]*x[c+3]; + res[3] += row[3][c+0]*x[c+0] + row[3][c+1]*x[c+1] + row[3][c+2]*x[c+2] + row[3][c+3]*x[c+3]; + } + + y[0] = res[0]; + y[1] = res[1]; + y[2] = res[2]; + y[3] = res[3]; +} + +static +void +mtxvec_kernel1xN_4(int ncol, double *row, double *x, double *y) +{ + int c; + double res; + + res = 0.; + for (c = 0; c < ncol; c += 4) { + res += row[c+0]*x[c+0] + row[c+1]*x[c+1] + row[c+2]*x[c+2] + row[c+3]*x[c+3]; + } + + y[0] = res; +} + +// y = a*mx + b*y +error +math·mtxvec(math·Mtx m, double a, math·Vec x, double b, math·Vec y) +{ + int c, r, nrow, ncol; + double *row[4], res[4]; + + nrow = m.dim[0] & ~3; + ncol = m.dim[1] & ~3; + for (r = 0; r < nrow; r += 4) { + row[0] = m.d + (r * (m.dim[1]+0)); + row[1] = m.d + (r * (m.dim[1]+1)); + row[2] = m.d + (r * (m.dim[1]+2)); + row[3] = m.d + (r * (m.dim[1]+3)); + + mtxvec_kernel4xN_4_avx2(ncol, row, x.d + r, res); + + for (c = ncol; c < m.dim[1]; c++) { + res[0] += row[0][c]; + res[1] += row[1][c]; + res[2] += row[2][c]; + res[3] += row[3][c]; + } + + y.d[r+0] = res[0] + b*y.d[r+0]; + y.d[r+1] = res[1] + b*y.d[r+1]; + y.d[r+2] = res[2] + b*y.d[r+2]; + y.d[r+3] = res[3] + b*y.d[r+3]; + } + + for (; r < m.dim[0]; r++) { + mtxvec_kernel1xN_4(m.dim[0], m.d + (r * m.dim[1]), x.d + r, res); + y.d[r] = res[0] + b*y.d[r]; + } + + return 0; +} + +/************************************************ + * add matrix to vector outerproduct + ***********************************************/ + +#define NITER 50 + +#if 0 +error +main() +{ + int i; + clock_t t; + double res; + + math·Mtx m; + math·Vec x, y; + + openblas_set_num_threads(1); + + x = math·makevec(1000, mem·sys, nil); + y = math·makevec(1000, mem·sys, nil); + m = math·makemtx(1000, 1000, mem·sys, nil); + + for (i = 0; i < x.len; i++) { + y.d[i] = i; + } + + t = clock(); + for (i = 0; i < NITER; i++) { + cblas_dgemv(CblasRowMajor, CblasNoTrans, m.dim[0], m.dim[1], 1.5, m.d, m.dim[1], x.d, 1, 2.5, y.d, 1); + } + t = clock() - t; + res = math·dot(y, y); + printf("the result is %f\n", res); + printf("time elapsed (blas): %fms\n", 1000.*t/CLOCKS_PER_SEC); + + for (i = 0; i < x.len; i++) { + y.d[i] = i; + } + + t = clock(); + for (i = 0; i < NITER; i++) { + math·mtxvec(m, 1.5, x, 2.5, y); + } + t = clock() - t; + res = math·dot(y, y); + + printf("the dot product is %f\n", res); + printf("time elapsed (naive): %fms\n", 1000.*t/CLOCKS_PER_SEC); + + + return 0; +} +#endif diff --git a/src/libsre/lex.c b/src/libsre/lex.c new file mode 100644 index 0000000..f4c6ac2 --- /dev/null +++ b/src/libsre/lex.c @@ -0,0 +1,246 @@ +#include "sre.h" + +static +State * +state(Machine *m, int t) +{ + if (m->state >= m->statestk + arrlen(m->statestk)) + panicf("regexp vm: out of state space"); + + m->state->type = t; + m->state->l = nil; + m->state->r = nil; + + return m->state++; +} + +static +int +poptor(Parser *p) +{ + if (p->optor <= p->optorstk) + panicf("regexp parser: opand stack underflow"); + + return *--p->optor; +} + +static +void +pushtor(Parser *p, int t) +{ + if (p->optor >= arrend(p->optorstk)) + panicf("regexp parser: opand stack overflow"); + + *p->optor++ = t; +} + +static +void +pushand(Parser *p, State *beg, State *end) +{ + if (p->node >= arrend(p->nodestk)) + panicf("regexp parser: opand stack overflow"); + + p->node->beg = beg; + p->node->end = end; + + p->node++; +} + +static +Node * +popand(Parser *p) +{ + if (p->node <= p->nodestk) + panicf("regexp parser: opand stack underflow"); + + return --p->node; +} + +static +void +operateuntil(Parser *p, int prec) +{ + Node *o1, *o2, *t; + State *s1, *s2; + + while (prec == Trparen || p->optor[-1] >= prec) { + switch (poptor(p)) { + case Tor: + o1 = popand(p); + o2 = popand(p); + + s1 = state(p->mach, Tor); + s2 = state(p->mach, Tnop); + s1->l = o1->beg; + s1->r = o2->beg; + + o1->end->out = s2; + o2->end->out = s2; + + pushand(p, s1, s2); + break; + + case Tcat: + o1 = popand(p); + o2 = popand(p); + + o1->end->out = o2->beg; + pushand(p, o1->beg, o2->end); + break; + + case Tstar: + o1 = popand(p); + s1 = state(p->mach, Tor); + o1->end->out = s1; + s1->l = o1->beg; + pushand(p, s1, s1); + break; + + case Tplus: + o1 = popand(p); + s1 = state(p->mach, Tor); + o1->end->out = s1; + s1->l = o1->beg; + pushand(p, o1->beg, s1); + break; + + case Tqmark: + o1 = popand(p); + s1 = state(p->mach, Tor); + s2 = state(p->mach, Tnop); + s1->l = o1->beg; + s1->r = s2; + o1->end->out = s2; + pushand(p, s1, s2); + break; + + default: + panicf("unsupported regexp operator"); + } + } +} + +static +void +operator(Parser *p, int t) +{ + operateuntil(p, t); + pushtor(p, t); + p->wasopand = (t != Tstar && t != Tqmark && t != Tplus && t != Trparen); +} + +static +void +operand(Parser *p, int t) +{ + State *new; + if (p->wasopand) + operator(p, Tcat); + + new = state(p->mach, t); + pushand(p, new, new); + p->wasopand = true; +} + +#define cinc 20 +int +lex(Parser *p) +{ + int c, t; + byte *class; + long n, cap; + + c = *p->re++; + switch (c) { + case '\\': + if (*p->re) + if ((c = *p->re++) == '\n') + c = '\n'; + break; + case '\0': + c = Tend, + --p->re; + break; + case '*': + c = Tstar; + break; + case '?': + c = Tqmark; + break; + case '+': + c = Tplus; + break; + case '|': + c = Tor; + break; + case '.': + c = Tany; + break; + case '(': + c = Tlparen; + break; + case ')': + c = Trparen; + break; + case '^': + c = Tbol; + break; + case '$': + c = Teol; + break; + case '[': + goto charclass; + default: + ; + } + return c; + +charclass: + panicf("to implement"); +} + +#undef cinc + +static +State* +optimize(State *entry) +{ + State *curr, *next; + for (curr=entry; curr->type != Tend; curr++) { + next = curr->out; + while (next->type == Tnop) + next = next->out; + curr->out = next; + } + + return entry; +} + +void +sre·compile(Machine *mach, byte *regexp) +{ + int tok; + Parser p; + Node *prog; + + p = (Parser) { + .re = regexp, + .mach = mach, + .node = p.nodestk, + }; + + pushtor(&p, Tstart - 1); + while ((tok = lex(&p)) != Tend) { + if ((tok & isoptor) == Toperator) + operator(&p, tok); + else + operand(&p, tok); + } + operateuntil(&p, Tstart); + operand(&p, Tend); + operateuntil(&p, Tstart); + + prog = popand(&p); + mach->entry = optimize(prog->beg); +} diff --git a/src/libsre/sre.h b/src/libsre/sre.h new file mode 100644 index 0000000..a7ace1a --- /dev/null +++ b/src/libsre/sre.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include + +enum +{ + Toperator = RuneMask + 1, + Tstart = Toperator, + Trparen, + Tlparen, + Tor, + Tcat, + Tstar, + Tplus, + Tqmark, + + Tany = Toperator << 1, + Tnop, + Tbol, + Teol, + Tcclass, + Tnclass, + Tend, + + isoptor = Toperator, + isopand = Toperator << 1, +}; + +typedef struct Class Class; +typedef struct State State; +typedef struct Patch Patch; +typedef struct Node Node; + +typedef struct Parser Parser; +typedef struct Machine Machine; + +struct Class +{ + rune *end; + rune span[64]; +}; + +struct State +{ + int type; + union { + State *l; + }; + union { + State *r; + State *out; + }; +}; + +struct Patch +{ + State *s; + Patch *link; +}; + +struct Node +{ + State *beg; + State *end; +}; + +struct Parser +{ + Machine *mach; + byte *re; + int wasopand : 1; + int *optor, optorstk[1000]; + Node *node, nodestk[1000]; +}; + +struct Machine +{ + /* memory buffers */ + struct { + void *heap; + mem·Reallocator; + }; + State *state, statestk[1000]; + + struct { + int cap; + int len; + Class *c; + } class; + + State *entry; +}; diff --git a/src/libterm/term.c b/src/libterm/term.c new file mode 100644 index 0000000..11591fc --- /dev/null +++ b/src/libterm/term.c @@ -0,0 +1,489 @@ +#include "term.h" + +#include +#include + +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", +}; + +static Term *sigwinchhead; + +// ----------------------------------------------------------------------- +// 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; +} + +/* formats escape strings and writes to output */ +static void tfmt(Term *t, char *esc, int n, ...); +static void tclear(Term *t); + +// ----------------------------------------------------------------------- +// 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 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; + + t->cap.colors = unibi_get_num(t->info, unibi_max_colors); + t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase); + + /* initialize root window (get current size)*/ + struct winsize ws = { 0 }; + if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1) + goto bad; + + 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; + + /* 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"); + + return t; + +bad: + panicf("failed to initialize terminal instance"); + free(t); + return nil; +} + +void +tfree(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); + 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 +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); + +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; + } +} + +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: 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 { + 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 */ +static +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 */ +static +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); +} + +static +void +tclear(Term *t) +{ + tfmt(t, t->esc.ed2, 0); +} + +void +tblit(Term *t, Window *win) +{ + 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 + +int +main() +{ + 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/src/libterm/term.h b/src/libterm/term.h new file mode 100644 index 0000000..6bd2f6b --- /dev/null +++ b/src/libterm/term.h @@ -0,0 +1,270 @@ +#pragma once + +#include +#include + +#include +#include + +#define iota(x) 1 << (x) + +typedef struct RGB8 RGB8; +typedef struct Pen Pen; + +typedef struct Dot Dot; +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), + /* ... */ + PenRGB = 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 txt; + Pen pen; +}; + +struct Row +{ + Cell *cells; + uint dirty : 1; +}; + +struct Dot +{ + int row, col; +}; + +/* + * scroll.top & scroll.bot are pointers into the viewport. + * + * scroll back buffer + * + * scroll.buf->+----------------+-----+ + * | | | ^ \ + * | before | | | | + * current terminal content | viewport | | | | + * | | | | + * +----------------+-----+\ | | | s > scroll.above + * ^ | | i | \ | | i | c | + * | | | n | \ | | n | r | + * | | v | \ | | v | o | + * | | 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 + * <- maxw -> | scroll back | | + * <- w -> | buffer | | | + * | | | | + * | | | v + * scroll.buf + scroll.size->+----------------+-----+ + * <- maxw -> + * <- w -> + */ + +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; + Row *top; + Row *bot; + int size; + int index; + int above; + int below; + } scroll; + 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 */ +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; +}; + +struct Input +{ + int fd; + int flags; + 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]; + } rbuf; + struct { + uchar *s, bytes[256]; + } ebuf; + + /* key data */ + Node *keys; + struct KeyInfo c0[32]; +}; + + +struct Term +{ + /* meta data */ + char *name; + unibi_term *info; + struct { + uchar altscreen : 1; + uchar cursorvis : 1; + uchar mouse : 1; + } mode; + struct { + uchar bce : 1; + int colors; + } cap; + + /* input capture */ + Input input; + + /* output display */ + Window *root; + Pen pen; + + /* raw text to pty */ + 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; + + Term *link; +}; + +/* functions */ +void tresize(Term *t); + +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/src/libterm/window.c b/src/libterm/window.c new file mode 100644 index 0000000..5d36c8b --- /dev/null +++ b/src/libterm/window.c @@ -0,0 +1,408 @@ +#include "term.h" + +// ----------------------------------------------------------------------- +// buffers + +static +void +zero(Row *row, int start, int len) +{ + 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; +} + +static +void +roll(Row *start, Row *end, int count) +{ + int n = end - start; + + /* enforce circularity */ + count %= n; + if (count < 0) + count += n; + + 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) +{ + 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; + } + } +} + +static +void +bfini(Buffer *b) +{ + int i; + + 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; + } + } + + 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/src/libutf/canfit.c b/src/libutf/canfit.c new file mode 100644 index 0000000..4579ab3 --- /dev/null +++ b/src/libutf/canfit.c @@ -0,0 +1,23 @@ +#include "internal.h" + +/* returns 1 if string of length n is long enough to be decoded */ +int +utf8·canfit(byte* s, int n) +{ + int i; + rune c; + + if(n <= 0) + return 0; + + c = *(ubyte*)s; + if(c < TByte1) + return 1; + + if(c < TByte3) + return n >= 2; + if(c < TByte4) + return n >= 3; + + return n >= UTFmax; +} diff --git a/src/libutf/decode.c b/src/libutf/decode.c new file mode 100644 index 0000000..01797f1 --- /dev/null +++ b/src/libutf/decode.c @@ -0,0 +1,98 @@ +#include "internal.h" + +#define ACCEPT 0 +#define REJECT 12 + +static uint8 decode[] = { + /* + * the first part of the table maps bytes to character classes that + * to reduce the size of the transition table and create bitmasks + */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + + /* + * the second part is a transition table that maps a combination + * of a state of the automaton and a character class to a state + */ + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, +}; + +int +utf8·decode(char *s, rune *r) +{ + int n; + rune v; + uint8 b, t, x=ACCEPT; + + b = ((uint8 *)s)[0]; + t = decode[b]; + v = (0xFF >> t) & b; + x = decode[256+x+t]; + + for(n=1; x > REJECT && n < UTFmax; n++){ + b = ((uint8 *)s)[n]; + t = decode[b]; + v = (v << 6) | (b & TMask); + x = decode[256+x+t]; + } + + if(x != ACCEPT){ + *r = RuneErr; + return 1; + } + + *r = v; + return n; +} + +#if 0 +int +utf8·decode(byte *s, rune *r) +{ + int c[UTFmax], i; + rune l; + + c[0] = *(ubyte*)(s); + if(c[0] < Tx){ + *r = c[0]; + return 1; + } + + l = c[0]; + for(i = 1; i < UTFmax; i++){ + c[i] = *(ubyte*)(s+i); + c[i] ^= Tx; + if(c[i] & Testx) goto bad; + + l = (l << Bitx) | c[i]; + if(c[0] < Tbyte(i + 2)){ + l &= RuneX(i + 1); + if(i == 1){ + if(c[0] < Tbyte(2) || l <= Rune1) + goto bad; + }else if(l <= RuneX(i) || l > RuneMax) + goto bad; + + if(i == 2 && SurrogateMin <= l && l <= SurrogateMax) + goto bad; + + *r = l; + return i + 1; + } + } +bad: + *r = RuneErr; + return 1; +} +#endif diff --git a/src/libutf/decodeprev.c b/src/libutf/decodeprev.c new file mode 100644 index 0000000..27dced6 --- /dev/null +++ b/src/libutf/decodeprev.c @@ -0,0 +1,60 @@ +#include "internal.h" + +#define ACCEPT 0 +#define REJECT 12 + +static uint8 decode[] = { + /* + * the first part of the table maps bytes to character classes that + * to reduce the size of the transition table and create bitmasks. + */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + /* + * The second part is a transition table that maps a combination + * of a state of the automaton and a character class to a state. + */ + // 0 1 2 3 4 5 6 7 8 9 10 11 + 0,24,12,12,12,12,12,24,12,24,12,12, + 0,24,12,12,12,12,12,24,12,24,12,12, + 12,36, 0,12,12,12,12,48,12,36,12,12, + 12,60,12, 0, 0,12,12,72,12,72,12,12, + 12,60,12, 0,12,12,12,72,12,72, 0,12, + 12,12,12,12,12, 0, 0,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12, 0 +}; + +int +utf8·decodeprev(byte *s, rune *r) +{ + int n; + rune v; + uint8 b, t, d, x=ACCEPT; + + v=0, n=0, d=0; +nextbyte: + b = ((uint8 *)s)[-n++]; + t = decode[b]; + x = decode[256+x+t]; + + if(x > REJECT && n < UTFmax){ + v = v | ((b & TMask) << d); + d += 6; + goto nextbyte; + } + + if(x != ACCEPT) + *r = RuneErr; + else{ + v |= (((0xFFu >> t) & b) << d); + *r = v; + } + + return n; +} diff --git a/src/libutf/encode.c b/src/libutf/encode.c new file mode 100644 index 0000000..fa7c93e --- /dev/null +++ b/src/libutf/encode.c @@ -0,0 +1,69 @@ +#include "internal.h" + +int +utf8·encode(rune *r, byte *s) +{ + rune c; + + c = *r; + if(c < Rune1Byte){ // 7 bits + s[0] = (uint8)c; + return 1; + } + + if(c < Rune2Byte){ // 11 bits + s[0] = TByte1 | (c >> 6); + s[1] = Tx | (c & TMask); + return 2; + } + + if(c < Rune3Byte){ // 16 bits + s[0] = TByte2 | ((c >> 12)); + s[1] = Tx | ((c >> 6) & TMask); + s[2] = Tx | ((c) & TMask); + return 3; + } + + // 22 bits + if(c > RuneMax || (RuneSurrogateMin <= c && c <= RuneSurrogateMax)) + c = RuneErr; + + s[0] = TByte3 | ((c >> 18)); + s[1] = Tx | ((c >> 12) & TMask); + s[2] = Tx | ((c >> 6) & TMask); + s[3] = Tx | ((c) & TMask); + + return 4; +} + +#if 0 +int +utf8·encode(rune* r, byte* s) +{ + int i, j; + rune c; + + c = *r; + if(c <= Rune1) { + s[0] = c; + return 1; + } + + for(i = 2; i < UTFmax + 1; i++){ + if(i == 3){ + if(c > RuneMax) + c = RuneErr; + if(SurrogateMin <= c && c <= SurrogateMax) + c = RuneErr; + } + if(c <= RuneX(i) || i == UTFmax) { + s[0] = Tbyte(i) | (c >> (i - 1)*Bitx); + for(j = 1; j < i; j++) + s[j] = Tx | ((c >> (i - j - 1)*Bitx) & Maskx); + return i; + } + } + + return UTFmax; +} +#endif diff --git a/src/libutf/find.c b/src/libutf/find.c new file mode 100644 index 0000000..d75feb8 --- /dev/null +++ b/src/libutf/find.c @@ -0,0 +1,31 @@ +#include "internal.h" + +byte* +utf8·find(byte* s, rune c) +{ + long c1; + rune r; + int n; + + if(c < Tx) + return strchr(s, c); + + for(;;){ + c1 = *(ubyte*)s; + if(c1 < Tx){ + if(c1 == 0) return nil; + if(c1 == c) return s; + s++; + continue; + } + + n = utf8·decode(s, &r); + + if(r == c) + return s; + + s += n; + } + + return nil; +} diff --git a/src/libutf/findlast.c b/src/libutf/findlast.c new file mode 100644 index 0000000..ab25ab2 --- /dev/null +++ b/src/libutf/findlast.c @@ -0,0 +1,32 @@ +#include "internal.h" + +byte* +utf8·findlast(byte* s, rune c) +{ + long c1; + rune r; + byte *l; + + if(c < Tx) + return strrchr(s, c); + + l = nil; + for(;;){ + c1 = *(ubyte*)s; + if(c1 < Tx){ + if(c1 == 0) return l; + if(c1 == c) l = s; + s++; + continue; + } + + c1 = utf8·decode(s, &r); + + if(r == c) + l = s; + + s += c1; + } + + return nil; +} diff --git a/src/libutf/internal.h b/src/libutf/internal.h new file mode 100644 index 0000000..9719977 --- /dev/null +++ b/src/libutf/internal.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +/* + * NOTE: we use the preprocessor to ensure we have unsigned constants. + * UTF-8 code: + * 1 byte: + * 0xxxxxxx + * 2 byte: + * 110xxxxx 10xxxxxx + * 3 byte: + * 1110xxxx 10xxxxxx 10xxxxxx + * 4 byte: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + +#define Tx 0x80u // 0b10000000 transfer header +#define TMask 0x3Fu // 0b00111111 transfer mask + +#define TByte1 0xC0u // 0b11000000 +#define TByte2 0xE0u // 0b11100000 +#define TByte3 0xF0u // 0b11110000 +#define TByte4 0xF8u // 0b11111000 + +#define RuneMask 0x1FFFFFu + +#define Rune1Byte 0x000080u // 1 << 8 (1 byte) +#define Rune2Byte 0x001000u // 1 << 12 (2 bytes) +#define Rune3Byte 0x020000u // 1 << 17 (3 bytes) +#define Rune4Byte 0x400000u // 1 << 22 (4 bytes) + + +/* UTF-16 nonsense */ +#define RuneSurrogateMin 0x0D8000 +#define RuneSurrogateMax 0x0D8FFF diff --git a/src/libutf/len.c b/src/libutf/len.c new file mode 100644 index 0000000..8fbd679 --- /dev/null +++ b/src/libutf/len.c @@ -0,0 +1,21 @@ +#include "internal.h" + +int +utf8·len(char *s) +{ + int c; + long n; + rune r; + + n = 0; + for(;;){ + c = *(uchar*)s; + if(c < Tx){ + if(c == 0) + return n; + s++; + }else + s += utf8·decode(s, &r); + n++; + } +} diff --git a/src/libutf/rules.mk b/src/libutf/rules.mk new file mode 100644 index 0000000..aeb86b2 --- /dev/null +++ b/src/libutf/rules.mk @@ -0,0 +1,76 @@ +include share/push.mk + +UNICODE=14.0.0 + +SRCS_$(d):=\ + $(d)/encode.c\ + $(d)/decode.c\ + $(d)/decodeprev.c\ + $(d)/find.c\ + $(d)/findlast.c\ + $(d)/canfit.c\ + $(d)/runelen.c\ + $(d)/len.c\ + $(d)/runetype-$(UNICODE).c\ + $(d)/runewidth-$(UNICODE).c + +LIBS_$(d):=$(d)/libutf.a + +include share/paths.mk + +# ======================================================================== +# table generation + +$(d)/vendor/common.o: $(d)/vendor/common.c + $(COMPILE) + +# rune categories +$(d)/vendor/UnicodeData-$(UNICODE).txt: + @echo "GET UnicodeData.txt";\ + curl https://www.unicode.org/Public/$(UNICODE)/ucd/UnicodeData.txt > $@ + +$(d)/vendor/mkrunetype: $(d)/vendor/mkrunetype.c $(d)/vendor/common.o $(OBJ_DIR)/base/base.a + $(COMPLINK) + +GENS += $(d)/vendor/mkrunetype + +$(d)/runetype-$(UNICODE).c: $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/mkrunetype + @$(dir $@)vendor/mkrunetype $< > $@ + +# rune widths +$(d)/vendor/EastAsianWidth-$(UNICODE).txt: + @echo "GET EastAsianWidth.txt";\ + curl https://www.unicode.org/Public/$(UNICODE)/ucd/EastAsianWidth.txt > $@ + +$(d)/vendor/EmojiData-$(UNICODE).txt: + @echo "GET EmojiData.txt";\ + curl https://www.unicode.org/Public/$(UNICODE)/ucd/emoji/emoji-data.txt > $@ + +$(d)/vendor/mkrunewidth: $(d)/vendor/mkrunewidth.c $(d)/vendor/common.o $(OBJ_DIR)/base/base.a + $(COMPLINK) + +GENS += $(d)/vendor/mkrunewidth + +$(d)/runewidth-$(UNICODE).c: $(d)/vendor/mkrunewidth $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/EastAsianWidth-$(UNICODE).txt $(d)/vendor/EmojiData-$(UNICODE).txt + @$(dir $@)vendor/mkrunewidth $(filter-out $<, $^) > $@ + +# grapheme boundaries +$(d)/vendor/GraphemeBreakProperty-$(UNICODE).txt: + @echo "GET GraphemeBreakProperty.txt";\ + curl https://www.unicode.org/Public/$(UNICODE)/ucd/auxiliary/GraphemeBreakProperty.txt > $@ + +$(d)/vendor/mkgraphemedata: $(d)/vendor/mkgraphemedata.c $(d)/vendor/common.o $(OBJ_DIR)/base/base.a + $(COMPLINK) + +$(d)/graphemedata-$(UNICODE).c: $(d)/vendor/mkgraphemedata $(d)/vendor/GraphemeBreakProperty-$(UNICODE).txt + $^ > $@ + +GENS += $(d)/vendor/mkgraphemedata + +# ======================================================================== +# normal operations + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +include share/pop.mk diff --git a/src/libutf/runelen.c b/src/libutf/runelen.c new file mode 100644 index 0000000..dac7f15 --- /dev/null +++ b/src/libutf/runelen.c @@ -0,0 +1,8 @@ +#include "internal.h" + +int +utf8·runelen(rune r) +{ + byte s[10]; + return utf8·encode(&r, s); +} diff --git a/src/libutf/vendor/common.c b/src/libutf/vendor/common.c new file mode 100644 index 0000000..5a03a50 --- /dev/null +++ b/src/libutf/vendor/common.c @@ -0,0 +1,220 @@ +#include "common.h" + +// ----------------------------------------------------------------------- +// input functions + +int +parse(io·Stream *io, int nfield, char **field, int len, char *line) +{ + int n; + if((n=io·readln(io, len, line)) <= 0) + return ParseEOF; + + if(n == len) + panicf("line too long"); + + if(line[n-1] != '\n') + panicf("invalid line: expected '\n', found '%c'", line[n]); + + line[n-1] = 0; + + if(line[0] == '#' || line[0] == 0) + return ParseSkip; + + /* tokenize line into fields */ + n = 0; + field[n] = line; + while(*line){ + if(*line == ';'){ + *line = 0; + field[++n] = line+1; + } + line++; + } + + if(n != nfield-1) + panicf("expected %d number of fields, got %d: %s", nfield, n, line); + + return ParseOK; +} + +int +codepoint(char *s) +{ + int c, b; + + c = 0; + while((b=*s++)){ + c <<= 4; + if(b >= '0' && b <= '9') + c += b - '0'; + else if(b >= 'A' && b <= 'F') + c += b - 'A' + 10; + else + panicf("bad codepoint char '%c'", b); + } + + return c; +} + +void +codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop) +{ + int e, c; + char *other[NumFields], line[1024]; + + // XXX: the stop variable passes in the previous stopping character + e = *stop; + c = codepoint(field[Fcode]); + + if(c >= NumRunes) + panicf("unexpected large codepoint %x", c); + if(c <= e) + panicf("bad code sequence: %x then %x", e, c); + e = c; + + if(strstr(field[Fname], ", First>") != nil){ + if(!parse(utf8, arrlen(other), other, arrlen(line), line)) + panicf("range start at end of file"); + if(strstr(other[Fname], ", Last>") == nil) + panicf("range start not followed by range end"); + + e = codepoint(other[Fcode]); + + if(e <= c) + panicf("bad code sequence: %x then %x", c, e); + if(strcmp(field[Fcategory], other[Fcategory]) != 0) + panicf("range with mismatched category"); + } + + *start = c; + *stop = e; +} + +// ----------------------------------------------------------------------- +// output functions + +void +putsearch(void) +{ + puts( + "#include \n" + "#include \n" + "\n" + "static\n" + "rune*\n" + "rangesearch(rune c, rune *t, int n, int ne)\n" + "{\n" + " rune *p;\n" + " int m;\n" + " while(n > 1) {\n" + " m = n >> 1;\n" + " p = t + m*ne;\n" + " if(c >= p[0]){\n" + " t = p;\n" + " n = n-m;\n" + " }else\n" + " n = m;\n" + " }\n" + " if(n && c >= t[0])\n" + " return t;\n" + " return 0;\n" + "}\n" + ); + +} + +int +putrange(char *ident, char *prop, int force) +{ + int l, r, start; + + start = 0; + for(l = 0; l < NumRunes;) { + if(!prop[l]){ + l++; + continue; + } + + for(r = l+1; r < NumRunes; r++){ + if(!prop[r]) + break; + prop[r] = 0; + } + + if(force || r > l + 1){ + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[l] = 0; + printf("\t0x%.4x, 0x%.4x,\n", l, r-1); + } + + l = r; + } + + if(start) + printf("};\n\n"); + + return start; +} + +int +putpair(char *ident, char *prop) +{ + int l, r, start; + + start = 0; + for(l=0; l+2 < NumRunes; ){ + if(!prop[l]){ + l++; + continue; + } + + for(r = l + 2; r < NumRunes; r += 2){ + if(!prop[r]) + break; + prop[r] = 0; + } + + if(r != l + 2){ + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[l] = 0; + printf("\t0x%.4x, 0x%.4x,\n", l, r - 2); + } + + l = r; + } + + if(start) + printf("};\n\n"); + return start; +} + +int +putsingle(char *ident, char *prop) +{ + int i, start; + + start = 0; + for(i = 0; i < NumRunes; i++) { + if(!prop[i]) + continue; + + if(!start){ + printf("static rune %s[] = {\n", ident); + start = 1; + } + prop[i] = 0; + printf("\t0x%.4x,\n", i); + } + + if(start) + printf("};\n\n"); + + return start; +} diff --git a/src/libutf/vendor/common.h b/src/libutf/vendor/common.h new file mode 100644 index 0000000..62f6c5b --- /dev/null +++ b/src/libutf/vendor/common.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +enum +{ + // Fields inside UnicodeData.txt + Fcode, + Fname, + Fcategory, + Fcombine, + Fbidir, + Fdecomp, + Fdecimal, + Fdigit, + Fnumeric, + Fmirror, + Foldname, + Fcomment, + Fupper, + Flower, + Ftitle, + + NumFields, + NumRunes = 1 << 21, +}; + +/* input functions */ +enum +{ + ParseEOF, + ParseOK, + ParseSkip, +}; + +int parse(io·Stream *io, int nfield, char **field, int len, char *line); +int codepoint(char *s); +void codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop); + +/* output functions */ +void putsearch(void); +int putrange(char *ident, char *prop, int force); +int putpair(char *ident, char *prop); +int putsingle(char *ident, char *prop); diff --git a/src/libutf/vendor/mkgraphemedata.c b/src/libutf/vendor/mkgraphemedata.c new file mode 100644 index 0000000..ce5a952 --- /dev/null +++ b/src/libutf/vendor/mkgraphemedata.c @@ -0,0 +1,24 @@ +#include +#include +#include + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkgraphemedata \n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + io·Stream *utf8; + char line[1024]; + + ARGBEGIN{ + }ARGEND; +} diff --git a/src/libutf/vendor/mkrunetype.c b/src/libutf/vendor/mkrunetype.c new file mode 100644 index 0000000..9f939f4 --- /dev/null +++ b/src/libutf/vendor/mkrunetype.c @@ -0,0 +1,388 @@ +#include "common.h" + +// ----------------------------------------------------------------------- +// globals + +#define OFFSET (1 << 20) +#define DELTA(mapx, x) ((1 << 20) + (mapx) - (x)) + +// TODO: use bitarrays. will reduce executable size 8x +struct Table +{ + /* properties */ + char isspace[NumRunes]; + char isalpha[NumRunes]; + char ismark[NumRunes]; + char isdigit[NumRunes]; + char isupper[NumRunes]; + char islower[NumRunes]; + char istitle[NumRunes]; + char ispunct[NumRunes]; + char issymbl[NumRunes]; + char iscntrl[NumRunes]; + + char combine[NumRunes]; + + /* transformations */ + int toupper[NumRunes]; + int tolower[NumRunes]; + int totitle[NumRunes]; +}; + +static struct Table table; + +// ----------------------------------------------------------------------- +// internal functions + +static +int +isrange(char *label, char *prop, int force) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_range", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putrange(ident, prop, force); +} + +static +int +ispair(char *label, char *prop) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_pair", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putpair(ident, prop); +} + +static +int +issingle(char *label, char *prop) +{ + char ident[128]; + if(snprintf(ident, arrlen(ident), "is%s_single", label) == arrlen(ident)) + panicf("out of identifier space\n"); + + return putsingle(ident, prop); +} + +static +void +makeis(char *label, char *table, int pairs, int onlyranges) +{ + int hasr, hasp=0, hass=0; + + hasr = isrange(label, table, onlyranges); + if(!onlyranges && pairs) + hasp = ispair(label, table); + if(!onlyranges) + hass = issingle(label, table); + + printf( + "int\n" + "utf8·is%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(hasr){ + printf( + " p = rangesearch(c, is%s_range, arrlen(is%s_range)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return 1;\n", + label, label); + } + + if(hasp){ + printf( + " p = rangesearch(c, is%s_pair, arrlen(is%s_pair)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return 1;\n", + label, label); + } + + if(hass) + printf( + " p = rangesearch(c, is%s_single, arrlen(is%s_single), 1);\n" + " if(p && c == p[0])\n" + " return 1;\n", + label, label); + + printf( + " return 0;\n" + "}\n" + "\n"); +} + +static +int +torange(char *label, int *index, int force) +{ + int l, r, d, start = 0; + + for(l = 0; l < NumRunes; ){ + if(index[l] == l){ + l++; + continue; + } + + d = DELTA(index[l], l); + if(d != (rune)d) + panicf("bad map delta %d", d); + + for(r = l+1; r < NumRunes; r++){ + if(DELTA(index[r], r) != d) + break; + index[r] = r; + } + + if(force || r != l + 1){ + if(!start){ + printf("static rune to%s_range[] = {\n", label); + start = 1; + } + index[l] = l; + printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-1, d); + } + l = r; + } + if(start) + printf("};\n\n"); + + return start; +} + +static +int +topair(char *label, int *index) +{ + int l, r, d, start = 0; + + for(l = 0; l + 2 < NumRunes; ){ + if(index[l] == l){ + l++; + continue; + } + + d = DELTA(index[l], l); + if(d != (rune)d) + panicf("bad delta %d", d); + + for(r = l+2; r < NumRunes; r += 2){ + if(DELTA(index[r], r) != d) + break; + index[r] = r; + } + + if(r > l+2){ + if(!start){ + printf("static rune to%s_pair[] = {\n", label); + start = 1; + } + index[l] = l; + printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-2, d); + } + + l = r; + } + if(start) + printf("};\n\n"); + + return start; +} + +static +int +tosingle(char *label, int *index) +{ + int i, d, start = 0; + + for(i=0; i < NumRunes; i++) { + if(index[i] == i) + continue; + + d = DELTA(index[i], i); + if(d != (rune)d) + panicf("bad map delta %d", d); + + if(!start){ + printf("static rune to%s_single[] = {\n", label); + start = 1; + } + index[i] = i; + printf("\t0x%.4x, %d,\n", i, d); + } + if(start) + printf("};\n\n"); + + return start; +} + +static +void +mkto(char *label, int *index, int pairs, int onlyrange) +{ + int hasr, hasp=0, hass=0; + + hasr = torange(label, index, !onlyrange); + if(!onlyrange && pairs) + hasp = topair(label, index); + if(!onlyrange) + hass = tosingle(label, index); + + printf( + "rune\n" + "utf8·to%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(hasr) + printf( + " p = rangesearch(c, to%s_range, arrlen(to%s_range)/3, 3);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return c + p[2] - %d;\n", + label, label, OFFSET); + + if(hasp) + printf( + " p = rangesearch(c, to%s_pair, arrlen(to%s_pair)/3, 3);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return c + p[2] - %d;\n", + label, label, OFFSET); + + if(hass) + printf( + " p = rangesearch(c, to%s_single, arrlen(to%s_single)/2, 2);\n" + " if(p && c == p[0])\n" + " return c + p[1] - %d;\n", + label, label, OFFSET); + + + printf( + " return c;\n" + "}\n" + "\n" + ); +} + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkrunetype \n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i, sc, c, ec; + io·Stream *utf8; + char *prop, *field[NumFields], line[1024]; + + ARGBEGIN{ + }ARGEND; + + if(argc != 1) + usage(); + + if(!(utf8 = io·open(argv[0], "r"))) + panicf("can't open %s\n", argv[0]); + + /* by default each character maps to itself */ + for(i = 0; i < NumRunes; i++) { + table.toupper[i] = i; + table.tolower[i] = i; + table.totitle[i] = i; + } + + /* ensure all C local white space characters pass */ + table.isspace['\t'] = 1; + table.isspace['\n'] = 1; + table.isspace['\r'] = 1; + table.isspace['\f'] = 1; + table.isspace['\v'] = 1; + table.isspace[0x85] = 1; + + ec = -1; + // NOTE: we don't check for comments here: assume UnicodeData.txt doesn't have any + while(parse(utf8, arrlen(field), field, arrlen(line), line)){ + /* parse unicode range */ + codepointrange(utf8, field, &sc, &ec); + prop = field[Fcategory]; + + for(c = sc; c <= ec; c++){ + /* grab properties */ + switch(prop[0]){ + case 'L': + table.isalpha[c] = 1; + switch(prop[1]){ + case 'u': table.isupper[c] = 1; break; + case 'l': table.islower[c] = 1; break; + case 't': table.istitle[c] = 1; break; + case 'm': break; // modifier letters + case 'o': break; // ideograph letters + default: + goto badproperty; + } + break; + + case 'Z': + table.isspace[c] = 1; + break; + + case 'M': + table.ismark[c] = 1; + break; + + case 'N': + table.isdigit[c] = 1; + break; + + case 'P': + table.ispunct[c] = 1; + break; + + case 'S': + table.issymbl[c] = 1; + break; + + case 'C': + table.iscntrl[c] = 1; + break; + + default: badproperty: + panicf("unrecognized category '%s'", prop); + } + /* grab transformations */ + if(*field[Fupper]) + table.toupper[c] = codepoint(field[Fupper]); + if(*field[Flower]) + table.tolower[c] = codepoint(field[Flower]); + if(*field[Ftitle]) + table.totitle[c] = codepoint(field[Ftitle]); + } + } + io·close(utf8); + + putsearch(); + + makeis("space", table.isspace, 0, 1); + makeis("digit", table.isdigit, 0, 1); + makeis("alpha", table.isalpha, 0, 0); + makeis("upper", table.isupper, 1, 0); + makeis("lower", table.islower, 1, 0); + makeis("title", table.istitle, 1, 0); + makeis("punct", table.ispunct, 1, 0); + + mkto("upper", table.toupper, 1, 0); + mkto("lower", table.tolower, 1, 0); + mkto("title", table.totitle, 1, 0); +} diff --git a/src/libutf/vendor/mkrunewidth.c b/src/libutf/vendor/mkrunewidth.c new file mode 100644 index 0000000..14e6973 --- /dev/null +++ b/src/libutf/vendor/mkrunewidth.c @@ -0,0 +1,325 @@ +#include "common.h" + +/* + * inspired by design choices in utf8proc/charwidths.jl + * all widths default to 1 unless they fall within the categories: + * 1. Mn 2. Mc 3. Me 4. Zl + * 5. Zp 6. Cc 7. Cf 8. Cs + * these default to zero width + */ +enum +{ + /* width ? */ + WidthNeutral, /* (N) practially treated like narrow but unclear ... */ + WidthAmbiguous, /* (A) sometimes wide and sometimes not... */ + /* width 1 */ + WidthHalf, /* (H) = to narrow (compatability equivalent) */ + WidthNarrow, /* (Na) ASCII width */ + /* width 2 */ + WidthWide, /* (W) 2x width */ + WidthFull, /* (F) = to wide (compatability equivalent) */ +}; + +struct Table +{ + char width[3][NumRunes]; +}; + +static struct Table table; + +// ----------------------------------------------------------------------- +// internal functions + +static +void +parse_category(char *path) +{ + int sc, c, ec, w; + io·Stream *utf8; + char *prop, *field[NumFields], line[1024]; + + if(!(utf8 = io·open(path, "r"))) + panicf("can't open %s\n", path); + + // NOTE: we don't check for comments here + ec = -1; + while(parse(utf8, arrlen(field), field, arrlen(line), line)){ + codepointrange(utf8, field, &sc, &ec); + + prop = field[Fcategory]; + + switch(prop[0]){ + case 'M': + switch(prop[1]){ + case 'n': case 'c': case 'e': + w = 0; + break; + default: + w = 1; + break; + } + break; + case 'Z': + switch(prop[1]){ + case 'l': case 'p': + w = 0; + break; + default: + w = 1; + break; + } + break; + case 'C': + switch(prop[1]){ + case 'c': case 'f': case 's': + w = 0; + break; + default: + w = 1; + break; + } + default: + w = 1; + } + + for(c = sc; c <= ec; c++) + table.width[w][c] = 1; + } + + io·close(utf8); +} + +static +void +coderange(char *field, int *l, int *r) +{ + char *s; + + if(!(s = strstr(field, ".."))) + *l=*r=codepoint(field); + else{ + *s++ = 0, *s++ = 0; + *l=codepoint(field); + *r=codepoint(s); + } +} + +static +void +parse_eawidths(char *path) +{ + int at, w; + int l, c, r; + io·Stream *utf8; + char *field[2], line[1024]; + + utf8 = io·open(path, "r"); + while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ + if(at == ParseSkip) + continue; + + switch(field[1][0]){ + case 'A': continue; + case 'N': + if(field[1][1] != 'a') + continue; + /* fallthrough */ + case 'H': w = 1; break; + + case 'W': /* fallthrough */ + case 'F': w = 2; break; + + default: + panicf("malformed east asian width class: %s\n", field[1]); + } + + coderange(field[0], &l, &r); + + for(c=l; c <= r; c++){ + /* ensure it only exists in one table */ + table.width[w][c] = 1; + table.width[(w+1)%3][c] = 0; + table.width[(w+2)%3][c] = 0; + } + } + io·close(utf8); +} + +static +void +parse_emoji(char *path) +{ + int at, w; + int l, c, r; + io·Stream *utf8; + char *s, *field[2], line[1024]; + + utf8 = io·open(path, "r"); + while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ + if(at == ParseSkip) + continue; + + /* only override emoji presentation */ + if(!strstr(field[1], "Emoji_Presentation")) + continue; + + /* trim trailing space */ + for(s=field[0]; *s; s++){ + if(*s == ' ') + *s = 0; + } + + coderange(field[0], &l, &r); + + for(c=l; c <= r; c++){ + table.width[0][c] = 0; + table.width[1][c] = 0; + table.width[2][c] = 1; + } + } + + io·close(utf8); +} + +/* output functions */ +static +void +maketable(char *label, char *table, int pairs, int onlyranges) +{ + int r, p=0, s=0; + char ident[3][128]; + + enum + { + Irange, + Ipair, + Isingle, + }; + + /* ranges */ + if(snprintf(ident[Irange], arrlen(ident[Irange]), "%s_range", label) == arrlen(ident[Irange])) + panicf("out of identifier space\n"); + r = putrange(ident[Irange], table, onlyranges); + + if(!onlyranges && pairs){ + if(snprintf(ident[Ipair], arrlen(ident[Ipair]), "%s_pair", label) == arrlen(ident[Ipair])) + panicf("out of identifier space\n"); + p = putpair(ident[Ipair], table); + } + if(!onlyranges){ + if(snprintf(ident[Isingle], arrlen(ident[Isingle]), "%s_single", label) == arrlen(ident[Isingle])) + panicf("out of identifier space\n"); + + s = putsingle(ident[Isingle], table); + } + + printf( + "static int\n" + "is%s(rune c)\n" + "{\n" + " rune *p;\n" + "\n", + label); + + if(r){ + printf( + " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1])\n" + " return 1;\n", + ident[Irange], ident[Irange]); + } + + if(p){ + printf( + " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" + " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" + " return 1;\n", + ident[Ipair], ident[Ipair]); + } + + if(s) + printf( + " p = rangesearch(c, %s, arrlen(%s), 1);\n" + " if(p && c == p[0])\n" + " return 1;\n", + ident[Isingle], ident[Isingle]); + + printf( + " return 0;\n" + "}\n" + "\n"); +} + +// ----------------------------------------------------------------------- +// main point of entry + +static +void +usage(void) +{ + fprintf(stderr, "usage: mkrunewidth \n"); + exit(1); +} + +#define SETW0(c) \ + table.width[0][(c)] = 1, \ + table.width[1][(c)] = 0, \ + table.width[2][(c)] = 0; + +#define SETW1(c) \ + table.width[0][(c)] = 0, \ + table.width[1][(c)] = 1, \ + table.width[2][(c)] = 0; + +#define SETW2(c) \ + table.width[0][(c)] = 0, \ + table.width[1][(c)] = 0, \ + table.width[2][(c)] = 1; + + +int +main(int argc, char *argv[]) +{ + int c; + + ARGBEGIN{ + }ARGEND; + + if(argc != 3) + usage(); + + parse_category(*argv++); + parse_eawidths(*argv++); + parse_emoji(*argv); + + /* overrides */ + SETW0(0x2028); + SETW0(0x2029); + + SETW1(0x00AD); + + /* simple checking */ + for(c=0; c 1) + panicf("improper table state"); + } + + putsearch(); + + maketable("width0", table.width[0], 1, 0); + maketable("width1", table.width[1], 1, 0); + maketable("width2", table.width[2], 1, 0); + + puts( + "\n" + "int\n" + "utf8·runewidth(rune c)\n" + "{\n" + " if(iswidth1(c))\n" + " return 1;\n" + " if(iswidth2(c))\n" + " return 2;\n" + " return 0;\n" + "}" + ); +} diff --git a/src/nixos/rules.mk b/src/nixos/rules.mk new file mode 100644 index 0000000..e69de29 diff --git a/src/rules.mk b/src/rules.mk new file mode 100644 index 0000000..9bb61ae --- /dev/null +++ b/src/rules.mk @@ -0,0 +1,23 @@ +include share/push.mk + +# Iterate through subdirectory tree + +DIR := $(d)/cmd +include $(DIR)/rules.mk + +DIR := $(d)/base +include $(DIR)/rules.mk + +DIR := $(d)/libutf +include $(DIR)/rules.mk + +DIR := $(d)/libfmt +include $(DIR)/rules.mk + +DIR := $(d)/libmath +include $(DIR)/rules.mk + +DIR := $(d)/libbio +include $(DIR)/rules.mk + +include share/pop.mk diff --git a/sys/base/arg.c b/sys/base/arg.c deleted file mode 100644 index 269043e..0000000 --- a/sys/base/arg.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include - -// NOTE: this utf8 bit is copied from libunicode to remove the hard dependency just for ARG_BEGIN. - -#define UTFmax 4 -#define RuneSync 0x80u -#define RuneSelf 0x80u -#define RuneErr 0xFFFDu -#define RuneMax 0x10FFFFu -#define RuneMask 0x1FFFFFu - -#define Bit(i) (7-(i)) -/* N 0's preceded by i 1's e.g. T(Bit(2)) is 1100 0000 */ -#define Tbyte(i) (((1 << (Bit(i)+1))-1) ^ 0xFF) -/* 0000 0000 0000 0111 1111 1111 */ -#define RuneX(i) ((1 << (Bit(i) + ((i)-1)*Bitx))-1) -enum -{ - Bitx = Bit(1), - Tx = Tbyte(1), - Rune1 = (1 << (Bit(0)+0*Bitx)) - 1, - - Maskx = (1 << Bitx) - 1, /* 0011 1111 */ - Testx = Maskx ^ 0xff, /* 1100 0000 */ - - SurrogateMin = 0xD800, - SurrogateMax = 0xDFFF, - Bad = RuneErr, -}; - - -int -arg·bytetorune(uint32* r, byte* s) -{ - int c[4], i; - uint32 l; - - c[0] = *(ubyte*)(s); - if(c[0] < Tx) { - *r = c[0]; - return 1; - } - - l = c[0]; - for(i = 1; i < UTFmax; i++) { - c[i] = *(ubyte*)(s+i); - c[i] ^= Tx; - if (c[i] & Testx) goto bad; - - l = (l << Bitx) | c[i]; - if(c[0] < Tbyte(i + 2)) { - l &= RuneX(i + 1); - if (i == 1) { - if (c[0] < Tbyte(2) || l <= Rune1) - goto bad; - } else if (l <= RuneX(i) || l > RuneMax) - goto bad; - if (i == 2 && SurrogateMin <= l && l <= SurrogateMax) - goto bad; - - *r = l; - return i + 1; - } - } -bad: - *r = RuneErr; - return 1; -} - -char *argv0; diff --git a/sys/base/bufio/dump.c b/sys/base/bufio/dump.c deleted file mode 100644 index 0b527e2..0000000 --- a/sys/base/bufio/dump.c +++ /dev/null @@ -1,66 +0,0 @@ -// ----------------------------------------------------------------------- -// reader - -#if 0 -rune -bufio·getrune(io·Buffer *buf) -{ - ubyte b; - int i; - byte str[UTFmax+1]; - rune r; - - // NOTE: I'm worried about the sign here... - b = bufio·getbyte(buf); - if (b < RuneSelf) { - buf->runesize = 1; - return b; - } - - i = 0; - str[i++] = b; - -nextbyte: - b = bufio·getbyte(buf); - if (b < 0) return b; - if (i >= arrlen(str)) return RuneErr; - str[i++] = b; - if (!utf8·fullrune(str, i)) - goto nextbyte; - - buf->runesize = utf8·bytetorune(&r, str); - if (r == RuneErr && b == 1) { - errorf("illegal UTF-8 sequence"); - for (; i >= 0; i--) - errorf("%s%.2x", i > 0 ? " " : "", *(ubyte*)(str+i)); - errorf("\n"); - - buf->runesize = 0; - } else - for (; i > buf->runesize; i--) - bufio·ungetbyte(buf, str[i]); - - return r; -} - -// TODO: Check that we are given the correct rune! -error -bufio·ungetrune(io·Buffer *buf, rune r) -{ - if (buf->state & bufio·rdr) { - errorf("attempted to unget on non-active reader"); - return bufio·err; - } - - if (buf->pos == buf->buf) { - errorf("attempted to unget past end of buffer"); - return bufio·err; - } - - buf->pos -= buf->runesize; - return 0; -} -#endif - -// ----------------------------------------------------------------------- -// writer diff --git a/sys/base/bufio/get.c b/sys/base/bufio/get.c deleted file mode 100644 index 9f10c88..0000000 --- a/sys/base/bufio/get.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "internal.h" -#include "refill.h" - -int -bufio·getbyte(io·Buffer *buf) -{ -getbyte: - if(buf->pos < buf->end) - return *buf->pos++; - - memmove(buf->buf, buf->end - bufio·ungets, bufio·ungets); - - if(refill(buf) <= 0) - return bufio·eof; - - goto getbyte; -} diff --git a/sys/base/bufio/internal.h b/sys/base/bufio/internal.h deleted file mode 100644 index 302c035..0000000 --- a/sys/base/bufio/internal.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include -#include diff --git a/sys/base/bufio/read.c b/sys/base/bufio/read.c deleted file mode 100644 index 09a9f83..0000000 --- a/sys/base/bufio/read.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "internal.h" -#include "refill.h" - -int -bufio·read(io·Buffer *buf, int sz, int n, void *out) -{ - byte *wtr; - int nr, rem, diff; - - if(n == 0 || buf->state & bufio·end) - return bufio·err; - - assert(buf->state & bufio·rdr); - - wtr = out; - rem = n*sz; - - while(rem > 0){ - diff = buf->end - buf->pos; - nr = MIN(diff, rem); - if(!nr){ - if(buf->state & bufio·end) - break; - if(refill(buf) <= 0) - break; - - continue; - } - memmove(wtr, buf->pos, nr); - wtr += nr; - buf->pos += nr; - rem -= nr; - } - - return n - rem/sz; -} diff --git a/sys/base/bufio/reader.c b/sys/base/bufio/reader.c deleted file mode 100644 index afdaf60..0000000 --- a/sys/base/bufio/reader.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "internal.h" - -error -bufio·initreader(io·Buffer *buf, io·Reader rdr, void *h) -{ - if (buf->state) { - errorf("attemped to initialize an active buffer, state is '%d'", buf->state); - return bufio·err; - } - buf->state = bufio·rdr; - buf->runesize = 0; - buf->h = h; - buf->rdr = rdr; - buf->beg = buf->buf + bufio·ungets; - buf->pos = buf->beg; - buf->end = buf->pos; - buf->size = bufio·size - bufio·ungets; - - return 0; -} - -void -bufio·finireader(io·Buffer *buf) -{ - buf->state = bufio·nil; - buf->runesize = 0; - buf->rdr = (io·Reader){ .read = nil }; -} diff --git a/sys/base/bufio/refill.h b/sys/base/bufio/refill.h deleted file mode 100644 index 41e357e..0000000 --- a/sys/base/bufio/refill.h +++ /dev/null @@ -1,28 +0,0 @@ -int -refill(io·Buffer *buf) -{ - int n; - - if(buf->state & bufio·end) - return bufio·err; - - memcpy(buf->buf, buf->pos - bufio·ungets, bufio·ungets); - - n = buf->rdr.read(buf->h, 1, buf->size, buf->beg); - if(n < 0) - return bufio·err; - if(n == 0){ - buf->state |= bufio·end; - return 0; - } - - buf->pos = buf->beg; - buf->end = buf->pos + n; - - // TEST: put a physical EOF byte at the end - // this would allow for an unget operation - if(n < buf->size) - *buf->end++ = EOF; - - return n; -} diff --git a/sys/base/bufio/rules.mk b/sys/base/bufio/rules.mk deleted file mode 100644 index 84f283f..0000000 --- a/sys/base/bufio/rules.mk +++ /dev/null @@ -1,5 +0,0 @@ -SRCS_$(d)+=\ - $(d)/bufio/get.c\ - $(d)/bufio/read.c\ - $(d)/bufio/reader.c\ - $(d)/bufio/unget.c\ diff --git a/sys/base/bufio/unget.c b/sys/base/bufio/unget.c deleted file mode 100644 index 3fd16de..0000000 --- a/sys/base/bufio/unget.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "internal.h" - -error -bufio·ungetbyte(io·Buffer *buf, byte c) -{ - if(!(buf->state & bufio·rdr)) { - errorf("attempted to unget on non-active reader"); - return bufio·err; - } - - if(buf->pos == buf->buf) { - errorf("attempted to unget past end of buffer"); - return bufio·err; - } - - buf->pos--; - return 0; -} diff --git a/sys/base/coro/coro.c b/sys/base/coro/coro.c deleted file mode 100644 index 2255c99..0000000 --- a/sys/base/coro/coro.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "internal.h" - -/* Co-routine context */ -Coro* -coro·make(uintptr stk, uintptr (*func)(Coro*, uintptr)) -{ - if (!func) return nil; - if (stk == 0) stk = 8192; - - byte *block = malloc(stk); - Coro *co = (Coro*)&block[stk - sizeof(Coro)]; - co->bp = block; - co->size = stk; - - _newcoro(co, func, co); - return co; -} - -error -coro·free(Coro *co) -{ - enum - { - NIL, - GOOD, - EMPTY, - LOST, - }; - - if (!co) return NIL; - if (!co->bp) return LOST; - if (co->size == 0) return EMPTY; - - free(co->bp); - - return GOOD; -} - -uintptr -coro·yield(Coro *c, uintptr arg) -{ - return _coroyield(c, arg); -} diff --git a/sys/base/coro/internal.h b/sys/base/coro/internal.h deleted file mode 100644 index f57d27b..0000000 --- a/sys/base/coro/internal.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -extern void _newcoro(Coro *co, uintptr (*func)(Coro*, uintptr), void *stk); -extern uintptr _coroyield(Coro *co, uintptr arg); - -struct Coro -{ - void *sp; - void *bp; - uintptr size; - void *user; -}; diff --git a/sys/base/coro/rules.mk b/sys/base/coro/rules.mk deleted file mode 100644 index c2ee89f..0000000 --- a/sys/base/coro/rules.mk +++ /dev/null @@ -1,3 +0,0 @@ -SRCS_$(d)+=\ - $(d)/coro/coro.c\ - $(d)/coro/unix_x64.s\ diff --git a/sys/base/coro/unix_x64.s b/sys/base/coro/unix_x64.s deleted file mode 100644 index d7de2a2..0000000 --- a/sys/base/coro/unix_x64.s +++ /dev/null @@ -1,113 +0,0 @@ -; Nicholas Noll 2019 -; -; =================================================================== -%use altreg - - bits 64 - default rel - global _newcoro - global _coroyield - -; =================================================================== - section .text -; ------------------------------------------------------------------- - -%assign L.coro -8 -%assign L.func -16 - -coroinit: - mov R7, [RBP + L.coro] - mov R6, R0 - call [RBP + L.func] - -rerun: - mov R7, [RBP + L.coro] - mov R6, R0 - call _coroyield - jmp rerun - -; ------------------------------------------------------------------- -; # Register Mapping -; -; R0 R1 R2 R3 R4 R5 R6 R7 R8 ... -; RAX RCX RDX RBX RSP RBP RSI RDI R8 ... -; -; # Sys V calling convention -; func(R7, R6, R2, R1, R8, R9, Z0-7): R0 -; -; # Stack layout of an in-flight coro -; *coro -; *func -; *bp (base pointer of stack) -; ....... STACK ......... -; Saved Clobbers -; -; ### -; Stack layout of an init coro -; Stores the func pointer to init -; Stores the clobber registers. -; -; L.coro [8] -; L.func [7] -; coroinit [6] -; RBP [5] -; R3 [4] -; R12 [3] -; R13 [2] -; R14 [1] -; R15 [0] - -%define WORDSZ 8 -%define NSAVES 9 - -; coro *coro·new(co *coro, fn func, bp *stack) -_newcoro: - lea R0, [coroinit] ; Store address of init function - lea R1, [R2 - NSAVES*WORDSZ] ; Store offset address of stack - - mov [R1 + 8*WORDSZ], R7 ; Store context pointer - mov [R1 + 7*WORDSZ], R6 ; Store function pointer - mov [R1 + 6*WORDSZ], R0 ; Store initializer pointer - mov [R1 + 5*WORDSZ], R2 ; Store stack base pointer - - xor R0, R0 - - ; Start of mutable stack - ; Blank out the clobbers - mov [R1 + 4*WORDSZ], R0 ; R3 - mov [R1 + 3*WORDSZ], R0 ; R12 - mov [R1 + 2*WORDSZ], R0 ; R13 - mov [R1 + 1*WORDSZ], R0 ; R14 - mov [R1 + 0*WORDSZ], R0 ; R15 - - mov [R7], R1 - ret - -; Saves register state -%macro pushclobs 0 - push RBP - push R3 - push R12 - push R13 - push R14 - push R15 -%endmacro - -; Restores register state -%macro popclobs 0 - pop R15 - pop R14 - pop R13 - pop R12 - pop R3 - pop RBP -%endmacro - -; uintptr coro.yield(co *coro, data uintptr) -_coroyield: - pushclobs - mov R0, R6 ; Move return value into return register. - xchg RSP, [R7] ; Atomically swap the stack pointer with the yieldee. - popclobs - - ret diff --git a/sys/base/error/errorf.c b/sys/base/error/errorf.c deleted file mode 100644 index 193dd9d..0000000 --- a/sys/base/error/errorf.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "internal.h" - -void -errorf(byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, args); - - va_end(args); -} diff --git a/sys/base/error/exits.c b/sys/base/error/exits.c deleted file mode 100644 index 6be7d3b..0000000 --- a/sys/base/error/exits.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "internal.h" - -void -exits(char *s) -{ - if(s == nil || *s == 0) - exit(0); - - fputs(s, stderr); - exit(1); -} diff --git a/sys/base/error/internal.h b/sys/base/error/internal.h deleted file mode 100644 index 88a8895..0000000 --- a/sys/base/error/internal.h +++ /dev/null @@ -1,3 +0,0 @@ -#include -#include - diff --git a/sys/base/error/panicf.c b/sys/base/error/panicf.c deleted file mode 100644 index d698576..0000000 --- a/sys/base/error/panicf.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "internal.h" - -void -panicf(byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - printf("panic: "); - vprintf(fmt, args); - printf("\n"); - - va_end(args); - - exit(1); -} diff --git a/sys/base/error/rules.mk b/sys/base/error/rules.mk deleted file mode 100644 index e3a9ce0..0000000 --- a/sys/base/error/rules.mk +++ /dev/null @@ -1,6 +0,0 @@ -SRCS_$(d)+=\ - $(d)/error/exits.c \ - $(d)/error/errorf.c \ - $(d)/error/panicf.c \ - $(d)/error/verrorf.c \ - $(d)/error/vpanicf.c \ diff --git a/sys/base/error/verrorf.c b/sys/base/error/verrorf.c deleted file mode 100644 index 15af064..0000000 --- a/sys/base/error/verrorf.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "internal.h" - -void -verrorf(byte* fmt, va_list args) -{ - printf("error: "); - vprintf(fmt, args); - printf("\n"); -} diff --git a/sys/base/error/vpanicf.c b/sys/base/error/vpanicf.c deleted file mode 100644 index bea97ac..0000000 --- a/sys/base/error/vpanicf.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "internal.h" - -void -vpanicf(byte* fmt, va_list args) -{ - printf("panic: "); - vprintf(fmt, args); - printf("\n"); - - exit(1); -} diff --git a/sys/base/flate/internal.h b/sys/base/flate/internal.h deleted file mode 100644 index 794c7c2..0000000 --- a/sys/base/flate/internal.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#include - -typedef struct buffer -{ - union { - struct z_stream_s; - z_stream z; - }; - - ubyte buf[4098]; -} buffer; - -typedef struct flate·Reader -{ - io·Reader rdr; - void* impl; - - union { - struct buffer; - buffer b; - }; -} flate·Reader; - -typedef struct flate·Writer -{ - io·Writer wtr; - void* impl; - - union { - struct buffer; - buffer b; - }; -} flate·Writer; - diff --git a/sys/base/flate/read.c b/sys/base/flate/read.c deleted file mode 100644 index 9a42070..0000000 --- a/sys/base/flate/read.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "internal.h" - -int -flate·read(flate·Reader *rdr, int sz, int n, void *buf) -{ - int r; - int err; - flate·Reader zrdr; - - zrdr = *rdr; - zrdr.next_out = buf; - zrdr.avail_out = n*sz; - -READ: - err = inflate(&zrdr.b.z, Z_STREAM_END); - switch (err) { - case Z_OK: - return n; - - case Z_STREAM_END: - r = zrdr.next_out - (ubyte*)buf; - n -= r; - zrdr.avail_in = zrdr.rdr.read(zrdr.impl, 1, arrlen(zrdr.buf), zrdr.buf); - if (!zrdr.avail_in) { - return r; - } - zrdr.next_in = zrdr.buf; - goto READ; - - case Z_NEED_DICT: - errorf("zlib: need input dictionary"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: inconsistent stream structure"); - goto ERROR; - } -ERROR: - flate·closereader(rdr); - return -1; -} diff --git a/sys/base/flate/reader.c b/sys/base/flate/reader.c deleted file mode 100644 index 84f0d80..0000000 --- a/sys/base/flate/reader.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "internal.h" - -flate·Reader* -flate·openreader(io·Reader rdr, void* r, mem·Allocator mem, void* m) -{ - error err; - flate·Reader *zrdr; - - zrdr = mem.alloc(m, 1, sizeof(*zrdr)); - - zrdr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; - zrdr->zfree = mem.free; - zrdr->opaque = m; - zrdr->avail_in = rdr.read(r, 1, arrlen(zrdr->buf), zrdr->buf); - zrdr->next_in = zrdr->buf; - - err = inflateInit(&zrdr->b.z); - - switch (err) { - case Z_OK: - return zrdr; - - case Z_MEM_ERROR: - errorf("zlib: not enough memory"); - goto ERROR; - - case Z_VERSION_ERROR: - errorf("zlib: incompatible version"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: incorrect input parameters"); - goto ERROR; - - default: - errorf("zlib: unrecognized error code"); - } -ERROR: - errorf("zlib: msg: %s", zrdr->msg); - mem.free(m, zrdr); - return nil; -} - -error -flate·closereader(flate·Reader *rdr) -{ - int err; - flate·Reader zrdr; - - zrdr = *rdr; - err = inflateEnd(&zrdr.b.z); - if (err != Z_OK) { - errorf("zlib: failed to cleanup"); - return err; - } - rdr->zfree(rdr->opaque, rdr); - - return 0; -} diff --git a/sys/base/flate/rules.mk b/sys/base/flate/rules.mk deleted file mode 100644 index 54d8c14..0000000 --- a/sys/base/flate/rules.mk +++ /dev/null @@ -1,6 +0,0 @@ -SRCS_$(d)+=\ - $(d)/flate/read.c\ - $(d)/flate/reader.c\ - $(d)/flate/write.c\ - $(d)/flate/writer.c\ - $(d)/flate/writer.c\ diff --git a/sys/base/flate/write.c b/sys/base/flate/write.c deleted file mode 100644 index 3f07b94..0000000 --- a/sys/base/flate/write.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "internal.h" - -int -flate·write(flate·Writer *wtr, int sz, int n, void *buf) -{ - int r; - int err; - flate·Writer zwtr; - - zwtr = *wtr; - zwtr.next_out = buf; -DEFLATE: - zwtr.avail_out = n*sz; - err = deflate(&zwtr.z, Z_NO_FLUSH); - - switch (err) { - case Z_STREAM_END: - return n; - - case Z_OK: - r = (zwtr.next_out - (ubyte*)buf)/sz; - n -= r; - if (!n) { - return r; - } - buf += n; - goto DEFLATE; - - case Z_STREAM_ERROR: - errorf("zlib: bad input"); - goto ERROR; - - case Z_BUF_ERROR: - if (!zwtr.avail_in) { - zwtr.avail_in += zwtr.wtr.write(zwtr.impl, 1, arrlen(zwtr.buf), buf); - if (!zwtr.avail_in) { - errorf("reader: failed read"); - goto ERROR; - } - goto DEFLATE; - } - } - - return 0; -ERROR: - errorf("zlib: %s", zwtr.msg); - return -1; -} diff --git a/sys/base/flate/writer.c b/sys/base/flate/writer.c deleted file mode 100644 index f339ae0..0000000 --- a/sys/base/flate/writer.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "internal.h" - -flate·Writer* -flate·openwriter(io·Writer wtr, void* w, mem·Allocator mem, void* m) -{ - error err; - flate·Writer *zwtr; - - zwtr = mem.alloc(m, 1, sizeof(*zwtr)); - zwtr->zalloc = (void *(*)(void *, unsigned int, unsigned int))mem.alloc; - zwtr->zfree = mem.free; - zwtr->opaque = m; - zwtr->avail_in = 0; - - err = deflateInit(&zwtr->b.z, Z_DEFAULT_COMPRESSION); - - switch (err) { - case Z_OK: - return zwtr; - - case Z_MEM_ERROR: - errorf("zlib: not enough memory"); - goto ERROR; - - case Z_VERSION_ERROR: - errorf("zlib: incompatible version"); - goto ERROR; - - case Z_STREAM_ERROR: - errorf("zlib: incorrect compression level"); - goto ERROR; - - default: - errorf("zlib: unrecognized error code"); - } -ERROR: - errorf("zlib: msg: %s", zwtr->msg); - mem.free(m, zwtr); - return nil; -} - -error -flate·closewriter(flate·Writer *wtr) -{ - int err; - flate·Writer zwtr; - - zwtr = *wtr; - err = deflateEnd(&zwtr.b.z); - if (err != Z_OK) { - errorf("zlib: failed to cleanup"); - return err; - } - zwtr.zfree(zwtr.opaque, wtr); - - return 0; -} diff --git a/sys/base/fs/internal.h b/sys/base/fs/internal.h deleted file mode 100644 index 7fde093..0000000 --- a/sys/base/fs/internal.h +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include -#include - -/* - * path history - */ -struct Key -{ - ino_t ino; - dev_t dev; -}; - -struct fs·History -{ - SET_STRUCT_BODY(struct Key); -}; diff --git a/sys/base/fs/rules.mk b/sys/base/fs/rules.mk deleted file mode 100644 index 3927ae3..0000000 --- a/sys/base/fs/rules.mk +++ /dev/null @@ -1,3 +0,0 @@ -SRCS_$(d)+=\ - $(d)/fs/walk.c\ - $(d)/fs/walker.c\ diff --git a/sys/base/fs/walk.c b/sys/base/fs/walk.c deleted file mode 100644 index d528896..0000000 --- a/sys/base/fs/walk.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "internal.h" - -#define hash(k) ((int32)k.ino ^ (int32)k.dev) -#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev) - -static -int -morehistory(fs·History *h, int n) -{ - SET_GROW(h, struct Key, n, hash, sys·Memory, nil); -} - -static -int -addentry(fs·History *h, struct Key key, int *err) -{ - SET_PUT(h, key, hash, equal, morehistory, err); -} - -static -void -forget(fs·History *h) -{ - if (!h) - return; - - SET_RESET(h); -} - -void -fs·walk(fs·Walker *fs) -{ - char *e, *b; - DIR *dir; - int new, fd, ofd, flags; - fs·History *h; - struct dirent *d; - io·Stat cwd; - struct fs·Entry *it; - - flags = 0; - if(fs->flags & fs·nolinks) - flags |= AT_SYMLINK_NOFOLLOW; - - /* get info for base relative to current fd */ - if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){ - if(fs->flags & fs·verbose) - errorf("stat: %s", fs->path); - return; - } - - /* if we hit a file, finish! */ - if(!S_ISDIR(cwd.st_mode)) { - fs->func(fs->data, fs->base, fs->path, &cwd); - return; - } - - /* have we been here before? (cycle detection) */ - /* if not, add to our path history */ - if (!(fs->flags & fs·nolinks)) { - addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new); - if (!new) - return; - } - - /* - * operate on directory first if preorder traversal - * truncate recursion if callback returns an error code - */ - if (fs->flags & fs·preorder) { - if (fs->func(fs->data, fs->base, fs->path, &cwd)) - return; - } - - /* open directory */ - if(!fs->max || fs->lev + 1 < fs->max) { - fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY); - if (fd < 0) - errorf("open %s:", fs->path); - - if (!(dir=fdopendir(fd))) { - if(fs->flags & fs·verbose) - errorf("fdopendir: %s", fs->path); - return; - } - - ofd = fs->fd, fs->fd = fd; - - /* traverse children */ - e = fs->end, b = fs->base; - if (fs->end[-1] != '/') - *fs->end++ = '/'; - - fs->base = fs->end; - while((d = readdir(dir))) { - if(*d->d_name == '.') - if(d->d_name[1] == 0 || /* . */ - (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */ - continue; - - fs->end = str·copyn(fs->base, d->d_name, arrend(fs->path) - fs->base); - - fs->lev++; - fs·walk(fs); - fs->lev--; - } - *e = 0; - fs->fd = ofd; - fs->end = e, fs->base = b; - closedir(dir); - } - - /* operate on directory if postorder (default) traversal */ - if (!(fs->flags & fs·preorder)) - fs->func(fs->data, fs->base, fs->path, &cwd); - - if (!fs->lev) - forget(fs->hist); -} diff --git a/sys/base/fs/walker.c b/sys/base/fs/walker.c deleted file mode 100644 index 65ff391..0000000 --- a/sys/base/fs/walker.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "internal.h" - -static -void -delete(fs·History *h) -{ - SET_FREE(h, sys·Memory, nil); -} - -int -fs·init(fs·Walker *fs, char *path) -{ - fs->base = fs->end = fs->path; - - if(!path || !path[0]){ - path = getcwd(fs->path, arrlen(fs->path)); - if (!path) - return 1; - fs->end += strlen(path); - }else - fs->end = str·copyn(fs->base, path, arrlen(fs->path)); - - if(fs->path[0] != '/') - fs->fd = AT_FDCWD; - - if(!fs->hist && !(fs->flags & fs·nolinks)) - fs->hist = calloc(1, sizeof(*fs->hist)); - - return 0; -} - -void -fs·fini(fs·Walker *fs) -{ - if(fs->hist){ - delete(fs->hist); - free(fs->hist); - } -} diff --git a/sys/base/gz/flush.c b/sys/base/gz/flush.c deleted file mode 100644 index 011a3ab..0000000 --- a/sys/base/gz/flush.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -error -gz·flush(gz·Stream *s) -{ - return gzflush(s, Z_FINISH); -} diff --git a/sys/base/gz/get.c b/sys/base/gz/get.c deleted file mode 100644 index 24ba23a..0000000 --- a/sys/base/gz/get.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "internal.h" - -byte -gz·getbyte(gz·Stream *s) -{ - // NOTE: Can't call macro - byte b[2]; - gzread(s, b, 1); - - return b[0]; -} - -error -gz·ungetbyte(gz·Stream *s, byte c) -{ - return gzungetc(c, s); -} diff --git a/sys/base/gz/interface.c b/sys/base/gz/interface.c deleted file mode 100644 index 15b8f10..0000000 --- a/sys/base/gz/interface.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -io·Reader gz·Reader = (io·Reader){ gz·read }; -io·Peeker gz·Peeker = (io·Peeker){ gz·getbyte, gz·ungetbyte }; -io·Seeker gz·Seeker = (io·Seeker){ gz·seek, gz·tell }; -io·PeekReader gz·Peekreader = (io·PeekReader){ gz·read, gz·getbyte, gz·ungetbyte }; - -io·Writer gz·Writer = (io·Writer){ gz·write }; -io·Putter gz·Putter = (io·Putter){ gz·putbyte, gz·putstring }; -io·PutWriter gz·PutWriter = (io·PutWriter){ gz·write, gz·putbyte, gz·putstring }; - -io·ReadWriter gz·ReadWriter = (io·ReadWriter){ gz·read, gz·write }; diff --git a/sys/base/gz/internal.h b/sys/base/gz/internal.h deleted file mode 100644 index 6a268c4..0000000 --- a/sys/base/gz/internal.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -#include diff --git a/sys/base/gz/open.c b/sys/base/gz/open.c deleted file mode 100644 index c84ce5e..0000000 --- a/sys/base/gz/open.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "internal.h" - -gz·Stream* -gz·open(byte *path, byte *mode) -{ - return gzopen(path, mode); -} - -error -gz·close(gz·Stream* s) -{ - return gzclose(s); -} diff --git a/sys/base/gz/printf.c b/sys/base/gz/printf.c deleted file mode 100644 index d7f75cf..0000000 --- a/sys/base/gz/printf.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "internal.h" - -int -gz·printf(gz·Stream *s, byte *fmt, ...) -{ - error err; - - va_list args; - va_start(args, fmt); - err = gzprintf(s, fmt, args); - va_end(args); - - return err; -} - diff --git a/sys/base/gz/put.c b/sys/base/gz/put.c deleted file mode 100644 index fa9807d..0000000 --- a/sys/base/gz/put.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -error -gz·putbyte(gz·Stream *s, byte c) -{ - return gzputc(s, c); -} diff --git a/sys/base/gz/putstring.c b/sys/base/gz/putstring.c deleted file mode 100644 index 64ff470..0000000 --- a/sys/base/gz/putstring.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "internal.h" - -error -gz·putstring(gz·Stream *s, byte *str) -{ - return gzputs(s, str); -} - diff --git a/sys/base/gz/read.c b/sys/base/gz/read.c deleted file mode 100644 index 112fe4d..0000000 --- a/sys/base/gz/read.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "internal.h" - -int -gz·read(gz·Stream *s, int sz, int n, void* buf) -{ - return gzread(s, buf, n*sz); -} - -int -gz·readln(gz·Stream *s, int n, byte *buf) -{ - byte* b; - b = gzgets(s, buf, n); - - return strlen(b); -} diff --git a/sys/base/gz/rules.mk b/sys/base/gz/rules.mk deleted file mode 100644 index a933291..0000000 --- a/sys/base/gz/rules.mk +++ /dev/null @@ -1,11 +0,0 @@ -SRCS_$(d)+=\ - $(d)/gz/flush.c\ - $(d)/gz/get.c\ - $(d)/gz/interface.c\ - $(d)/gz/open.c\ - $(d)/gz/printf.c\ - $(d)/gz/put.c\ - $(d)/gz/putstring.c\ - $(d)/gz/read.c\ - $(d)/gz/seek.c\ - $(d)/gz/write.c\ diff --git a/sys/base/gz/seek.c b/sys/base/gz/seek.c deleted file mode 100644 index 328886d..0000000 --- a/sys/base/gz/seek.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -gz·seek(gz·Stream *s, long off, enum SeekPos whence) -{ - return gzseek(s, off, whence); -} diff --git a/sys/base/gz/write.c b/sys/base/gz/write.c deleted file mode 100644 index 862d833..0000000 --- a/sys/base/gz/write.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -gz·write(gz·Stream *s, int sz, int n, void* buf) -{ - return gzwrite(s, buf, n*sz); -} diff --git a/sys/base/io/fd.c b/sys/base/io/fd.c deleted file mode 100644 index ded1b02..0000000 --- a/sys/base/io/fd.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·fd(io·Stream *s) -{ - return fileno(s); -} diff --git a/sys/base/io/flush.c b/sys/base/io/flush.c deleted file mode 100644 index 0f1217a..0000000 --- a/sys/base/io/flush.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·flush(io·Stream *s) -{ - return fflush(s); -} diff --git a/sys/base/io/get.c b/sys/base/io/get.c deleted file mode 100644 index d4e52f8..0000000 --- a/sys/base/io/get.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -byte -io·getbyte(io·Stream *s) -{ - return fgetc(s); -} diff --git a/sys/base/io/interface.c b/sys/base/io/interface.c deleted file mode 100644 index bead9e1..0000000 --- a/sys/base/io/interface.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "internal.h" - -static -int -·read(void *rdr, int size, int n, void *buf) -{ - return io·read((io·Stream *)rdr, size, n, buf); -} - -static -byte -·get(void *rdr) -{ - return io·getbyte((io·Stream *)rdr); -} - -static -error -·unget(void *rdr, byte c) -{ - return io·ungetbyte((io·Stream *)rdr, c); -} - -static -int -·write(void *wtr, int sz, int n, void *buf) -{ - return io·write((io·Stream *)wtr, sz, n, buf); -} - -static -error -·put(void *wtr, byte c) -{ - return io·putbyte((io·Stream *)wtr, c); -} - -static -int -·puts(void *wtr, string s) -{ - return io·putstring((io·Stream *)wtr, s); -} - -static -int -·seek(void *skr, long off, enum SeekPos whence) -{ - return io·seek((io·Stream *)skr, off, whence); -} - -static -long -·tell(void *skr) -{ - return io·tell((io·Stream *)skr); -} - -/* actual interfaces */ -io·Reader sys·Reader = (io·Reader){ ·read }; -io·Seeker sys·Seeker = (io·Seeker){ ·seek, ·tell }; -io·Peeker sys·Peeker = (io·Peeker){ ·get, ·unget }; -io·SeekReader sys·SeekReader = (io·SeekReader){ ·seek, ·tell, ·read }; -io·PeekReader sys·PeekReader = (io·PeekReader){ ·read, ·get, ·unget }; - -io·Writer sys·Writer = (io·Writer){ ·write }; -io·Putter sys·Putter = (io·Putter){ ·put, ·puts }; -io·PutWriter sys·PutWriter = (io·PutWriter){ ·write, ·put, ·puts }; - -io·ReadWriter sys·ReadWriter = (io·ReadWriter){ ·read, ·write }; diff --git a/sys/base/io/internal.h b/sys/base/io/internal.h deleted file mode 100644 index 302c035..0000000 --- a/sys/base/io/internal.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include -#include diff --git a/sys/base/io/open.c b/sys/base/io/open.c deleted file mode 100644 index e50e334..0000000 --- a/sys/base/io/open.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "internal.h" - -io·Stream* -io·open(byte *name, byte *mode) -{ - return fopen(name, mode); -} - -error -io·close(io·Stream *s) -{ - return fclose(s); -} diff --git a/sys/base/io/putbyte.c b/sys/base/io/putbyte.c deleted file mode 100644 index 2350a8d..0000000 --- a/sys/base/io/putbyte.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·putbyte(io·Stream *s, byte c) -{ - return fputc(c, s); -} diff --git a/sys/base/io/putstring.c b/sys/base/io/putstring.c deleted file mode 100644 index 53fa993..0000000 --- a/sys/base/io/putstring.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·putstring(io·Stream *s, string str) -{ - return fputs(str, s); -} diff --git a/sys/base/io/read.c b/sys/base/io/read.c deleted file mode 100644 index b0ed3d2..0000000 --- a/sys/base/io/read.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·read(io·Stream *s, int sz, int n, void *buf) -{ - return fread(buf, sz, n, s); -} diff --git a/sys/base/io/readln.c b/sys/base/io/readln.c deleted file mode 100644 index 283472d..0000000 --- a/sys/base/io/readln.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -int -io·readln(io·Stream *s, int n, byte* buf) -{ - byte* b; - b = fgets(buf, n+1, s); - if(b == nil) - return -1; - - return strlen(buf); -} diff --git a/sys/base/io/rules.mk b/sys/base/io/rules.mk deleted file mode 100644 index 2e03ca5..0000000 --- a/sys/base/io/rules.mk +++ /dev/null @@ -1,14 +0,0 @@ -SRCS_$(d)+=\ - $(d)/io/fd.c\ - $(d)/io/flush.c\ - $(d)/io/interface.c\ - $(d)/io/open.c\ - $(d)/io/putbyte.c\ - $(d)/io/putstring.c\ - $(d)/io/read.c\ - $(d)/io/readln.c\ - $(d)/io/seek.c\ - $(d)/io/stat.c\ - $(d)/io/tell.c\ - $(d)/io/unget.c\ - $(d)/io/write.c\ diff --git a/sys/base/io/seek.c b/sys/base/io/seek.c deleted file mode 100644 index d0e7488..0000000 --- a/sys/base/io/seek.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·seek(io·Stream *s, long off, enum SeekPos origin) -{ - return fseek(s, off, origin); -} diff --git a/sys/base/io/stat.c b/sys/base/io/stat.c deleted file mode 100644 index d86f1ee..0000000 --- a/sys/base/io/stat.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -error -io·stat(io·Stream *s, io·Stat *buf) -{ - return fstat(fileno(s), buf); -} diff --git a/sys/base/io/tell.c b/sys/base/io/tell.c deleted file mode 100644 index 1c50439..0000000 --- a/sys/base/io/tell.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -long -io·tell(io·Stream *s) -{ - return ftell(s); -} diff --git a/sys/base/io/unget.c b/sys/base/io/unget.c deleted file mode 100644 index 5ec3536..0000000 --- a/sys/base/io/unget.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -error -io·ungetbyte(io·Stream *s, byte c) -{ - return ungetc(c, s); -} diff --git a/sys/base/io/write.c b/sys/base/io/write.c deleted file mode 100644 index 63df664..0000000 --- a/sys/base/io/write.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -io·write(io·Stream *s, int sz, int n, void *buf) -{ - return fwrite(buf, sz, n, s); -} diff --git a/sys/base/mem/arena.c b/sys/base/mem/arena.c deleted file mode 100644 index b2ce044..0000000 --- a/sys/base/mem/arena.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "internal.h" - -#define ARENA_ALIGN 8 -#define ARENA_BLOCK_SIZE 1024 * 1024 - -#define ALIGN_DOWN(n, a) ((n) & ~((a)-1)) -#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a)) -#define ALIGN_DOWN_PTR(p, a) ((void*)ALIGN_DOWN((uintptr)(p), (a))) -#define ALIGN_UP_PTR(p, a) ((void*)ALIGN_UP((uintptr)(p), (a))) - -struct Block -{ - struct Block *next; - byte buf[]; -}; - -struct mem·Arena -{ - void *heap; - mem·Allocator mem; - - byte *off; - byte *end; - struct Block *curr; - struct Block first; -}; - -static -void* -·arenaalloc(void *heap, uint n, ulong size) -{ - return mem·arenaalloc(heap, n, size); -} - -static -void -·arenafree(void *heap, void *ptr) -{ - /* no-op */ -} - -mem·Allocator mem·ArenaAllocator = { - .alloc = ·arenaalloc, - .free = ·arenafree, -}; - - -static -void -grow(mem·Arena *a, vlong min) -{ - uintptr size; - struct Block *blk; - - size = ALIGN_UP(MAX(min, ARENA_BLOCK_SIZE), ARENA_ALIGN); - blk = a->mem.alloc(a->heap, 1, sizeof(*blk) + size); - a->off = blk->buf; - a->end = a->off + size; - - assert(a->curr->next == nil); - assert(a->off == ALIGN_DOWN_PTR(a->off, ARENA_ALIGN)); - - a->curr->next = blk; - a->curr = blk; -} - -mem·Arena* -mem·makearena(mem·Allocator from, void *impl) -{ - mem·Arena *a = from.alloc(impl, 1, sizeof(*a) + ARENA_BLOCK_SIZE); - a->mem = from; - a->heap = impl; - a->off = a->first.buf; - a->end = a->first.buf + ARENA_BLOCK_SIZE; - a->curr = &a->first; - a->first.next = nil; - - return a; -} - -void -mem·freearena(mem·Arena *a) -{ - struct Block *it, *next; - - it = a->first.next; - while (it != nil) { - next = it->next; - a->mem.free(a->heap, it); - it = next; - } - - a->mem.free(a->heap, a); -} - -void* -mem·arenaalloc(mem·Arena *a, uint n, ulong size) -{ - if(!n) { - return nil; - } - - void *ptr; - // TODO(nnoll): check for overflow - size = n * size; - - if (size > (ulong)(a->end - a->off)) { - grow(a, size); - assert(size <= (uintptr)(a->end - a->off)); - } - - ptr = a->off; - a->off = ALIGN_UP_PTR(a->off + size, ARENA_ALIGN); - - assert(a->off <= a->end); - assert(ptr == ALIGN_DOWN_PTR(ptr, ARENA_ALIGN)); - - return ptr; -} diff --git a/sys/base/mem/buffer.c b/sys/base/mem/buffer.c deleted file mode 100644 index b684d35..0000000 --- a/sys/base/mem/buffer.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "internal.h" - -/* Grow to particular size */ -void* -·bufgrow(void* buf, vlong newLen, vlong eltsize) -{ - assert(bufcap(buf) <= (SIZE_MAX - 1) / 2); - - vlong newCap = MAX(16, MAX(1 + 2 * bufcap(buf), newLen)); - - assert(newLen <= newCap); - assert(newCap <= (SIZE_MAX - offsetof(BufHdr, buf)) / eltsize); - - vlong newSize = offsetof(BufHdr, buf) + newCap * eltsize; - - BufHdr* newHdr; - if (buf) { - newHdr = bufhdr(buf); - newHdr = (BufHdr*)realloc((void*)newHdr, newSize); - } else { - newHdr = (BufHdr*)malloc(newSize); - newHdr->len = 0; - } - - newHdr->cap = newCap; - return (void*)newHdr->buf; -} - -/* Pop out a value */ -void -·bufdel(void *buf, int i, vlong eltsize) -{ - int n; - byte *b; - byte stk[1024]; - assert(eltsize < sizeof(stk)); - - b = (byte*)buf; - if(n = buflen(buf), i < n) { - memcpy(stk, b+eltsize*i, eltsize); - memcpy(b+eltsize*i, b+eltsize*(i+1), eltsize*(n-i-1)); - memcpy(b+eltsize*(n-1), stk, eltsize); - } - bufhdr(buf)->len--; -} diff --git a/sys/base/mem/interface.c b/sys/base/mem/interface.c deleted file mode 100644 index 4d7d1ce..0000000 --- a/sys/base/mem/interface.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "internal.h" - -static -void -·free(void *_, void *ptr) { - return free(ptr); -} - -static -void * -·alloc(void *_, uint n, ulong size) { - return malloc(n*size); -} - -static -void * -·calloc(void *_, uint n, ulong size) { - return calloc(n, size); -} - -static -void * -·realloc(void *_, void *ptr, uint n, ulong size) { - return realloc(ptr, n*size); -} - -mem·Allocator sys·Memory = { - .alloc = ·calloc, - .free = ·free -}; - -mem·Reallocator sys·FullMemory = { - .alloc = ·calloc, - .realloc = ·realloc, - .free = ·free -}; diff --git a/sys/base/mem/internal.h b/sys/base/mem/internal.h deleted file mode 100644 index 302c035..0000000 --- a/sys/base/mem/internal.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include -#include diff --git a/sys/base/mem/rules.mk b/sys/base/mem/rules.mk deleted file mode 100644 index b912d0c..0000000 --- a/sys/base/mem/rules.mk +++ /dev/null @@ -1,5 +0,0 @@ -SRCS_$(d)+=\ - $(d)/mem/arena.c\ - $(d)/mem/buffer.c\ - $(d)/mem/interface.c\ - $(d)/mem/set64.c\ diff --git a/sys/base/mem/set64.c b/sys/base/mem/set64.c deleted file mode 100644 index 464b3ad..0000000 --- a/sys/base/mem/set64.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "internal.h" - -void -mem·set64(void *dst, uint64 val, uintptr size) -{ - intptr i; - - for(i = 0; i < (size & (~7)); i += 8) - memcpy((byte*)dst + i, &val, 8); - - for(; i < size; i++) - ((byte*)dst)[i] = ((byte*)&val)[i&7]; -} diff --git a/sys/base/mmap/internal.h b/sys/base/mmap/internal.h deleted file mode 100644 index 7606c7e..0000000 --- a/sys/base/mmap/internal.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include -#include -#include diff --git a/sys/base/mmap/mmap.c b/sys/base/mmap/mmap.c deleted file mode 100644 index ce3011c..0000000 --- a/sys/base/mmap/mmap.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "internal.h" - -mmap·Reader -mmap·open(byte *filename) -{ - int fd; - int err; - void *buf; - io·Stream *s; - io·Stat st; - - s = io·open(filename, "r"); - fd = io·fd(s); - err = io·stat(s, &st); - if(err){ - errorf("file stat: error code %d", err); - goto ERROR; - } - - buf = mmap(nil, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if(!buf){ - errorf("mmap: failed"); - goto ERROR; - } - // NOTE: posix systems require that reference kept to mmap file after fd is closed - io·close(s); - return (mmap·Reader){.len=st.st_size, .b=buf}; - -ERROR: - io·close(s); - return (mmap·Reader){ 0 }; -} - -error -mmap·close(mmap·Reader rdr) -{ - munmap(rdr.b, rdr.len); - return 0; -} diff --git a/sys/base/mmap/rules.mk b/sys/base/mmap/rules.mk deleted file mode 100644 index fb3cab5..0000000 --- a/sys/base/mmap/rules.mk +++ /dev/null @@ -1,2 +0,0 @@ -SRCS_$(d)+=\ - $(d)/mmap/mmap.c\ diff --git a/sys/base/os/basename.c b/sys/base/os/basename.c deleted file mode 100644 index b5bb343..0000000 --- a/sys/base/os/basename.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "internal.h" - -char* -os·basename(char *path) -{ - char *sep; - - sep = strrchr(path, os·sep()); - return (sep == nil) ? path : sep+1; -} diff --git a/sys/base/os/exists.c b/sys/base/os/exists.c deleted file mode 100644 index a3c8935..0000000 --- a/sys/base/os/exists.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -int -os·exists(byte *path, int flag) -{ - return access(path, flag) == 0; -} diff --git a/sys/base/os/internal.h b/sys/base/os/internal.h deleted file mode 100644 index 302c035..0000000 --- a/sys/base/os/internal.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include -#include diff --git a/sys/base/os/rules.mk b/sys/base/os/rules.mk deleted file mode 100644 index bf1e71d..0000000 --- a/sys/base/os/rules.mk +++ /dev/null @@ -1,4 +0,0 @@ -SRCS_$(d)+=\ - $(d)/os/basename.c\ - $(d)/os/exists.c\ - $(d)/os/sep.c\ diff --git a/sys/base/os/sep.c b/sys/base/os/sep.c deleted file mode 100644 index 750e627..0000000 --- a/sys/base/os/sep.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "internal.h" - -int -os·sep(void) -{ -#if defined(UNIX) || defined(__linux__) - return '/'; -#elif defined(WIN32) - return '\\'; -#else - panicf("unrecognized operating system"); - return '\0'; -#endif -} diff --git a/sys/base/rng/base.c b/sys/base/rng/base.c deleted file mode 100644 index 9ec496e..0000000 --- a/sys/base/rng/base.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "internal.h" - -static uint64 -splitmix64(struct Mix *state) -{ - uint64 result = state->s; - - state->s = result + 0x9E3779B97f4A7C15; - result = (result ^ (result >> 30)) * 0xBF58476D1CE4E5B9; - result = (result ^ (result >> 27)) * 0x94D049BB133111EB; - return result ^ (result >> 31); -} - -int -rng·init(uint64 seed) -{ - int i; - Mix smstate = {seed}; - - for(i=0; i < 4; i++) - rng·RNG.s[i] = splitmix64(&smstate); - - return 0; -} diff --git a/sys/base/rng/bernoulli.c b/sys/base/rng/bernoulli.c deleted file mode 100644 index 02f531e..0000000 --- a/sys/base/rng/bernoulli.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "internal.h" - -bool -rng·bernoulli(double f) -{ - return rng·random() < f; -} diff --git a/sys/base/rng/exponential.c b/sys/base/rng/exponential.c deleted file mode 100644 index c07e007..0000000 --- a/sys/base/rng/exponential.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "internal.h" - -/* Returns a random float64 between 0 and 1 */ -double -rng·exponential(double lambda) -{ - double f; - - f = rng·random(); - return -log(1 - f)/lambda; -} diff --git a/sys/base/rng/internal.h b/sys/base/rng/internal.h deleted file mode 100644 index 9cf5f41..0000000 --- a/sys/base/rng/internal.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -#define rol64(x, k) ((x) << (k) | ((x) >> (64-(k)))) - -typedef struct Rng -{ - uint64 s[4]; -} Rng; - -typedef struct Mix -{ - uint64 s; -} Mix; - - -extern Rng rng·RNG; diff --git a/sys/base/rng/normal.c b/sys/base/rng/normal.c deleted file mode 100644 index aab5731..0000000 --- a/sys/base/rng/normal.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "internal.h" - -static inline double -erfinv(double x) -{ - /* useful constants */ - static double - a0 = 1.1975323115670912564578e0, a1 = 4.7072688112383978012285e1, - a2 = 6.9706266534389598238465e2, a3 = 4.8548868893843886794648e3, - a4 = 1.6235862515167575384252e4, a5 = 2.3782041382114385731252e4, - a6 = 1.1819493347062294404278e4, a7 = 8.8709406962545514830200e2, - - b0 = 1.0000000000000000000e0, b1 = 4.2313330701600911252e1, - b2 = 6.8718700749205790830e2, b3 = 5.3941960214247511077e3, - b4 = 2.1213794301586595867e4, b5 = 3.9307895800092710610e4, - b6 = 2.8729085735721942674e4, b7 = 5.2264952788528545610e3, - - c0 = 1.42343711074968357734e0, c1 = 4.63033784615654529590e0, - c2 = 5.76949722146069140550e0, c3 = 3.64784832476320460504e0, - c4 = 1.27045825245236838258e0, c5 = 2.41780725177450611770e-1, - c6 = 2.27238449892691845833e-2, c7 = 7.74545014278341407640e-4, - - d0 = 1.4142135623730950488016887e0, d1 = 2.9036514445419946173133295e0, - d2 = 2.3707661626024532365971225e0, d3 = 9.7547832001787427186894837e-1, - d4 = 2.0945065210512749128288442e-1, d5 = 2.1494160384252876777097297e-2, - d6 = 7.7441459065157709165577218e-4, d7 = 1.4859850019840355905497876e-9, - - e0 = 6.65790464350110377720e0, e1 = 5.46378491116411436990e0, - e2 = 1.78482653991729133580e0, e3 = 2.96560571828504891230e-1, - e4 = 2.65321895265761230930e-2, e5 = 1.24266094738807843860e-3, - e6 = 2.71155556874348757815e-5, e7 = 2.01033439929228813265e-7, - - f0 = 1.414213562373095048801689e0, f1 = 8.482908416595164588112026e-1, - f2 = 1.936480946950659106176712e-1, f3 = 2.103693768272068968719679e-2, - f4 = 1.112800997078859844711555e-3, f5 = 2.611088405080593625138020e-5, - f6 = 2.010321207683943062279931e-7, f7 = 2.891024605872965461538222e-15, - - Ln2 = 0.693147180559945309417232121458176568075500134360255254120680009; - - int s; - double r, z1, z2; - - if(x < 0) { - s = -1; - x = -x; - } else { - s = +1; - } - - if(x <= 0.85) { - r = 0.180625 - 0.25*x*x; - z1 = ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0; - z2 = ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0; - return s*(x*z1) / z2; - } - r = sqrt(Ln2 - log(1.0-x)); - if(r <= 5.0) { - r -= 1.6; - z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0; - z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0; - } else { - r -= 5.0; - z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0; - z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0; - } - - return s*z1/z2; -} - -double -rng·normal(void) -{ - double f; - f = rng·random(); - - return sqrt(2)*erfinv(2*f-1); -} diff --git a/sys/base/rng/poisson.c b/sys/base/rng/poisson.c deleted file mode 100644 index 3ec15c9..0000000 --- a/sys/base/rng/poisson.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "internal.h" - -/* - * Ahrens, J. H., & Dieter, U. (1982). - * Computer Generation of Poisson Deviates from Modified Normal Distributions. - */ -static double factorial[10] = {1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.}; -static double coeffs[9] = { - -.500000000, +.333333333, -.249999856, - +.200011780, -.166684875, +.142187833, - -.124196313, +.125005956, -.114265030, -}; - -static inline -double -log1pmx(double x, double off) -{ - int i; - double r, t; - - if(-0.25 < x && x < 0.25) { - r = 0; - t = 1; - for(i=0;i=L) - return K; -stepS: - U = rng·random(); - if(d*U >= (mu-K)*(mu-K)*(mu-K)) - return K; -stepP: - if(G < 0) - goto stepE; -stepQ: - c = procf(mu, s, K, &px, &py, &fx, &fy); -stepE: - E = rng·exponential(1.0); - U = rng·random(); - U = U + U - 1; - T = 1.8 + copysign(E,U); - if(T < 0.6744) - goto stepE; - K = floor(mu + s*T); - c = procf(mu, s, K, &px, &py, &fx, &fy); -stepH: - if(c*fabs(U) > (py*exp(px + E) - fy*exp(fx + E))) - goto stepE; - return K; -} - -uint64 -rng·poisson(double mean) -{ - int64 n; - double z; - - if(mean<10.0) { - for(n=0, z=rng·exponential(1.0); zs; - uint64 result = rol64(s[1] * 5, 7) * 9; - uint64 t = s[1] << 17; - - s[2] ^= s[0]; - s[3] ^= s[1]; - s[1] ^= s[2]; - s[0] ^= s[3]; - - s[2] ^= t; - s[3] = rol64(s[3], 45); - - return result; -} - -double -rng·random(void) -{ - uint64 r = xoshiro256ss(&rng·RNG); - return (double)r / (double)UINT64_MAX; -} - -uint64 -rng·randi(int max) -{ - uint64 r = xoshiro256ss(&rng·RNG); - return r % max; -} diff --git a/sys/base/rng/rules.mk b/sys/base/rng/rules.mk deleted file mode 100644 index 407b1bf..0000000 --- a/sys/base/rng/rules.mk +++ /dev/null @@ -1,7 +0,0 @@ -SRCS_$(d)+=\ - $(d)/rng/base.c\ - $(d)/rng/bernoulli.c\ - $(d)/rng/exponential.c\ - $(d)/rng/normal.c\ - $(d)/rng/poisson.c\ - $(d)/rng/random.c\ diff --git a/sys/base/rules.mk b/sys/base/rules.mk deleted file mode 100644 index 1726aa3..0000000 --- a/sys/base/rules.mk +++ /dev/null @@ -1,36 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := $(d)/arg.c -include $(d)/bufio/rules.mk -include $(d)/coro/rules.mk -include $(d)/error/rules.mk -include $(d)/flate/rules.mk -include $(d)/fs/rules.mk -include $(d)/gz/rules.mk -include $(d)/io/rules.mk -include $(d)/mem/rules.mk -include $(d)/mmap/rules.mk -include $(d)/os/rules.mk -include $(d)/rng/rules.mk -include $(d)/sort/rules.mk -include $(d)/string/rules.mk - - -TSTS_$(d) := $(d)/test.c - -LIBS_$(d) := $(d)/base.a -BINS_$(d) := - -include share/paths.mk - -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -$(UNTS_$(d)): TCLIBS := $(LIBS_$(d)) -$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) - $(LINK) - -include share/pop.mk diff --git a/sys/base/sort/double.c b/sys/base/sort/double.c deleted file mode 100644 index c3feac2..0000000 --- a/sys/base/sort/double.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·double(uintptr sz, double arr[]) -{ - double tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/float.c b/sys/base/sort/float.c deleted file mode 100644 index 57bd482..0000000 --- a/sys/base/sort/float.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·float(uintptr sz, float arr[]) -{ - float tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/int.c b/sys/base/sort/int.c deleted file mode 100644 index 33e1def..0000000 --- a/sys/base/sort/int.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·int(uintptr sz, int arr[]) -{ - int tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/int16.c b/sys/base/sort/int16.c deleted file mode 100644 index 072a3eb..0000000 --- a/sys/base/sort/int16.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·int16(uintptr sz, int16 arr[]) -{ - int16 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/int32.c b/sys/base/sort/int32.c deleted file mode 100644 index 27b3b7b..0000000 --- a/sys/base/sort/int32.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·int32(uintptr sz, int32 arr[]) -{ - int32 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/int64.c b/sys/base/sort/int64.c deleted file mode 100644 index b3fa5d4..0000000 --- a/sys/base/sort/int64.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·int64(uintptr sz, int64 arr[]) -{ - int64 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/int8.c b/sys/base/sort/int8.c deleted file mode 100644 index 5848e6e..0000000 --- a/sys/base/sort/int8.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·int8(uintptr sz, int8 arr[]) -{ - int8 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/internal.h b/sys/base/sort/internal.h deleted file mode 100644 index ac569de..0000000 --- a/sys/base/sort/internal.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include -#include -#include diff --git a/sys/base/sort/rules.mk b/sys/base/sort/rules.mk deleted file mode 100644 index 780d6ea..0000000 --- a/sys/base/sort/rules.mk +++ /dev/null @@ -1,14 +0,0 @@ -SRCS_$(d)+=\ - $(d)/sort/double.c\ - $(d)/sort/float.c\ - $(d)/sort/int.c\ - $(d)/sort/int16.c\ - $(d)/sort/int32.c\ - $(d)/sort/int64.c\ - $(d)/sort/int8.c\ - $(d)/sort/string.c\ - $(d)/sort/uint.c\ - $(d)/sort/uint16.c\ - $(d)/sort/uint32.c\ - $(d)/sort/uint64.c\ - $(d)/sort/uint8.c\ diff --git a/sys/base/sort/string.c b/sys/base/sort/string.c deleted file mode 100644 index b511efa..0000000 --- a/sys/base/sort/string.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·string(uintptr sz, byte* arr[]) -{ - byte *tmp; -#define LESS(i, j) (strcmp(arr[i], arr[j]) < 0) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/uint.c b/sys/base/sort/uint.c deleted file mode 100644 index 5b27330..0000000 --- a/sys/base/sort/uint.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·uint(uintptr sz, uint arr[]) -{ - uint tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/uint16.c b/sys/base/sort/uint16.c deleted file mode 100644 index 2b635b4..0000000 --- a/sys/base/sort/uint16.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·uint16(uintptr sz, uint16 arr[]) -{ - uint16 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/uint32.c b/sys/base/sort/uint32.c deleted file mode 100644 index 99a58cf..0000000 --- a/sys/base/sort/uint32.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·uint32(uintptr sz, uint32 arr[]) -{ - uint32 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/uint64.c b/sys/base/sort/uint64.c deleted file mode 100644 index 2769825..0000000 --- a/sys/base/sort/uint64.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·uint64(uintptr sz, uint64 arr[]) -{ - uint64 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/sort/uint8.c b/sys/base/sort/uint8.c deleted file mode 100644 index ff02b3c..0000000 --- a/sys/base/sort/uint8.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -void -sort·uint8(uintptr sz, uint8 arr[]) -{ - uint8 tmp; -#define LESS(i, j) (arr[i] < arr[j]) -#define SWAP(i, j) (tmp = arr[i], arr[i] = arr[j], arr[j] = tmp) - QSORT(sz, LESS, SWAP); -#undef SWAP -#undef LESS -} diff --git a/sys/base/string/append.c b/sys/base/string/append.c deleted file mode 100644 index d4d0396..0000000 --- a/sys/base/string/append.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "internal.h" - -// append will append the given null terminated c string to the string data -// structure. this variant can append a substring of length len of the given -// string to our buffer. the result is reallocated if not enough room is present -// in the buffer. -int -str·appendlen(string *s, vlong n, const byte* b) -{ - /* - bl = strlen(b); - if (n > bl) panicf("attempted to make a substring longer than string"); - */ - - str·grow(s, n); - if(*s == nil) - return 0; - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - - memcpy(*s + str·len(*s), b, n); - h->len += n; - (*s)[h->len] = '\0'; - - return n; -} - -// append will append the given null terminated c string to the string data -// structure. this variant will append the entire string. -int -str·append(string *s, const byte* b) -{ - return str·appendlen(s, strlen(b), b); -} - -// appendbyte will append the given byte to our string. -// NOTE: as the byte is on the stack, it is not null-terminated. -// can not pass to the above functions. -int -str·appendbyte(string *s, const byte b) -{ - str·grow(s, 1); - if(*s == nil) - return 0; - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - - *(*s + str·len(*s)) = b; - h->len++; - (*s)[h->len] = '\0'; // NOTE: I don't think an explicit zero is required..? - - return 1; -} diff --git a/sys/base/string/appendf.c b/sys/base/string/appendf.c deleted file mode 100644 index 4b8d76c..0000000 --- a/sys/base/string/appendf.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "internal.h" - -/* - * appendf will append the given formatted string to our buffer. - * returns the newly minted string - */ - -int -str·appendf(string *s, const byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int remain = str·cap(*s) - str·len(*s); - int n = vsnprintf(*s + str·len(*s), remain + 1, fmt, args); - va_end(args); - - if(n > remain){ - // If the first write was incomplete, we overwite the data again. - str·grow(s, n); - va_list args; - va_start(args, fmt); - n = vsnprintf(*s + str·len(*s), n + 1, fmt, args); - assert(n - remain <= str·cap(*s)); - va_end(args); - } - - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - h->len += n; - - return n; -} diff --git a/sys/base/string/clear.c b/sys/base/string/clear.c deleted file mode 100644 index 986f809..0000000 --- a/sys/base/string/clear.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "internal.h" - -void -str·clear(string *s) -{ - Hdr* h = (Hdr*)(*s - sizeof(Hdr)); - h->len = 0; - *s[0] = '\0'; -} diff --git a/sys/base/string/copyn.c b/sys/base/string/copyn.c deleted file mode 100644 index 09c2879..0000000 --- a/sys/base/string/copyn.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "internal.h" - -char * -str·copyn(char *dst, char *src, int n) -{ - while(*src && n-- > 0) - *dst++ = *src++; - - *dst = 0; - return dst; -} diff --git a/sys/base/string/equals.c b/sys/base/string/equals.c deleted file mode 100644 index a975cf5..0000000 --- a/sys/base/string/equals.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -// Equals returns true if string s and t are equivalent. -bool -str·equals(const string s, const string t) -{ - vlong sL = str·len(s); - vlong tL = str·len(t); - if (sL != tL) return false; - - return memcmp(s, t, sL) == 0; -} diff --git a/sys/base/string/find.c b/sys/base/string/find.c deleted file mode 100644 index 20f990e..0000000 --- a/sys/base/string/find.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "internal.h" - -// find will find the first occurence of -// substr in the string returns -1 if nothing was found. -int -str·find(string s, const byte* substr) -{ - byte* loc = strstr(s, substr); - if (loc == nil) return -1; - return (int)(loc - s); -} diff --git a/sys/base/string/fit.c b/sys/base/string/fit.c deleted file mode 100644 index 56ab041..0000000 --- a/sys/base/string/fit.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "internal.h" - -// fit reallocates the string such that the buffer is exactly sized for the -// buffer. if the capacity equals the length, then the function is a noop. the -// byte array is unchanged. -void -str·fit(string *s) -{ - Hdr* h; - vlong cap = str·cap(*s); - vlong len = str·len(*s); - - if (cap == len) return; - - h = (Hdr*)(s - sizeof(Hdr)); - h = realloc(h, sizeof(*h) + len + 1); - h->cap = len; - - *s = h->buf; -} diff --git a/sys/base/string/free.c b/sys/base/string/free.c deleted file mode 100644 index 7b5ee98..0000000 --- a/sys/base/string/free.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "internal.h" - -// free returns memory associated to the buffer. -void -str·free(string s) -{ - free(s - sizeof(Hdr)); -} diff --git a/sys/base/string/grow.c b/sys/base/string/grow.c deleted file mode 100644 index 39a9d2f..0000000 --- a/sys/base/string/grow.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "internal.h" - -// grow ensures that the string can encompass at least delta bytes. -// if it already can, this is a no op. -// if it can't, the string will be reallocated. -void -str·grow(string *s, vlong delta) -{ - Hdr *h, *newh; - vlong cap = str·cap(*s); - vlong len = str·len(*s); - assert(cap >= len); // To prevent unsigned behavior - - if (cap - len >= delta) return; - - h = (Hdr*)(*s - sizeof(Hdr)); - - vlong newCap = cap + delta; - assert(newCap >= cap); // To prevent unsigned behavior - if (newCap < MAX_STRING_ALLOC) { - newCap *= 2; - } else - newCap += MAX_STRING_ALLOC; - - newh = (Hdr*)realloc(h, sizeof(*h) + newCap + 1); - if (newh == nil) return; - - memset(newh->buf + len, '\0', newCap - len); - newh->cap = newCap; - newh->len = len; - - *s = newh->buf; -} diff --git a/sys/base/string/internal.h b/sys/base/string/internal.h deleted file mode 100644 index 8c16c64..0000000 --- a/sys/base/string/internal.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include - -#define MAX_STRING_ALLOC 1024 * 1024 - -typedef struct Hdr -{ - vlong len; - vlong cap; - byte buf[]; -} Hdr; diff --git a/sys/base/string/join.c b/sys/base/string/join.c deleted file mode 100644 index fb97b6c..0000000 --- a/sys/base/string/join.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "internal.h" - -string -str·join(vlong len, byte** fields, const byte* sep) -{ - string s = str·makecap("", 0, 10); - int j = 0; - - for (j = 0; j < len; j++) { - str·append(&s, fields[j]); - if (j < len - 1) - str·appendlen(&s, 1, sep); - } - - return s; -} diff --git a/sys/base/string/len.c b/sys/base/string/len.c deleted file mode 100644 index 5e42919..0000000 --- a/sys/base/string/len.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "internal.h" - -// len returns the length of the string. -int -str·len(const string s) -{ - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - return h->len; -} - -// cap returns the capacity of the string buffer. -int -str·cap(const string s) -{ - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - return h->cap; -} diff --git a/sys/base/string/lower.c b/sys/base/string/lower.c deleted file mode 100644 index c6935f8..0000000 --- a/sys/base/string/lower.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -// lower will force all runes in the string to be lowercase -void -str·lower(string s) -{ - byte *b, *e; - b = s; - e = b + str·len(s); - while (b++ != e) - *b = tolower(*b); -} diff --git a/sys/base/string/make.c b/sys/base/string/make.c deleted file mode 100644 index eb71543..0000000 --- a/sys/base/string/make.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "internal.h" - -// new returns a new dynamic string object, initialized from the given c string. -// len defines the length of the c substring that we will copy into our buffer. -// the backing buffer will have capacity cap. -string -str·makecap(const byte *s, vlong len, vlong cap) -{ - struct Hdr* h; - - h = malloc(sizeof(*h) + cap + 1); - if (s == nil) memset(h, 0, sizeof(*h)); - - if (h == nil) return nil; // Allocation failed. - - h->len = (s == nil) ? 0 : len; - h->cap = cap; - - if (cap < h->len) goto cleanup; - - if (s != nil && cap > 0) { - memcpy(h->buf, s, h->len); - memset(h->buf + h->len, '\0', h->cap - h->len + 1); - } - - return h->buf; - -cleanup: - free(h); - panicf("Attempted to create a string with less capacity than length"); - return nil; -} - -// new returns a new dynamic string object, initialized from the given c string. -// the backing buffer capacity is equivalent to the string length. -string -str·makelen(const byte *s, vlong len) -{ - vlong sl = (!s) ? 0 : strlen(s); - if (sl < len) panicf("attempted to take a bigger substring than string length"); - - vlong cap = (len == 0) ? 1 : len; - return str·makecap(s, len, cap); -} - -// new returns a new dynamic string object, initialized from the given c string. -// the backing buffer capacity is equivalent to the string length. -string -str·make(const byte *s) -{ - vlong len = (!s) ? 0 : strlen(s); - return str·makelen(s, len); -} diff --git a/sys/base/string/makef.c b/sys/base/string/makef.c deleted file mode 100644 index 8fb9c38..0000000 --- a/sys/base/string/makef.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "internal.h" - -// Newf returns a new dynamic string object -string -str·makef(const byte *fmt, ...) -{ - vlong n; - string s; - va_list args; - - va_start(args, fmt); - n = vsnprintf(nil, 0, fmt, args); - va_end(args); - - s = str·makecap(nil, 0, n); - - va_start(args, fmt); - vsnprintf(s, n + 1, fmt, args); - va_end(args); - - Hdr* h = (Hdr*)(s - sizeof(Hdr)); - h->len = n; - - return s; -} diff --git a/sys/base/string/read.c b/sys/base/string/read.c deleted file mode 100644 index df2028f..0000000 --- a/sys/base/string/read.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -int -str·read(string s, int size, int n, void *buf) -{ - int len; - - len = MIN(n * size, str·len(s)); - memcpy(buf, s, len); - - return len; -} diff --git a/sys/base/string/replace.c b/sys/base/string/replace.c deleted file mode 100644 index 127daed..0000000 --- a/sys/base/string/replace.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "internal.h" - -// replace will replace all occurences of the given bytes 'from' to bytes 'to' -// edits are done in place and modify the string. -// NOTE: as of now strings from and to must be the same size. -void -str·replace(string s, const byte* from, const byte* to) -{ - vlong fromL = strlen(from); - vlong toL = strlen(to); - if (toL != fromL) { panicf("different sized replacement string not supported"); } - - vlong l = str·len(s); - vlong i = l; - vlong j = l; - - for (i = 0; i < l; i++) { - for (j = 0; j < toL; j++) { - if (s[i] == from[j]) { - s[i] = to[j]; - break; - } - } - } -} - diff --git a/sys/base/string/rules.mk b/sys/base/string/rules.mk deleted file mode 100644 index e517ca5..0000000 --- a/sys/base/string/rules.mk +++ /dev/null @@ -1,19 +0,0 @@ -SRCS_$(d)+=\ - $(d)/string/append.c\ - $(d)/string/appendf.c\ - $(d)/string/clear.c\ - $(d)/string/copyn.c\ - $(d)/string/equals.c\ - $(d)/string/find.c\ - $(d)/string/fit.c\ - $(d)/string/free.c\ - $(d)/string/grow.c\ - $(d)/string/join.c\ - $(d)/string/len.c\ - $(d)/string/lower.c\ - $(d)/string/make.c\ - $(d)/string/makef.c\ - $(d)/string/read.c\ - $(d)/string/replace.c\ - $(d)/string/split.c\ - $(d)/string/upper.c\ diff --git a/sys/base/string/split.c b/sys/base/string/split.c deleted file mode 100644 index 2aa68b4..0000000 --- a/sys/base/string/split.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "internal.h" - -// split will split the string by the given token. -// returns a stretchy buffer of strings that result from the partition. -// it is the caller's responsibility to clean the memory. -string* -str·split(string s, const byte* tok) -{ - string* fields = nil; - vlong start = 0; - - vlong sL = str·len(s); - vlong tokL = strlen(tok); - if (sL == 0 || tokL == 0) return nil; - - buffit(fields, 5); - - for (vlong i = 0; i < sL - tokL; i++) { - if ((tokL == 1 && s[i] == tokL) || !memcmp(s + i, tok, tokL)) { - bufpush(fields, str·makelen(s + start, i - start)); - if (fields[buflen(fields) - 1] == nil) goto cleanup; - - start = i + tokL; - i += tokL - 1; - } - } - - bufpush(fields, str·makelen(s + start, sL - start)); - - return fields; - -cleanup: - for (vlong i = 0; i < buflen(fields); i++) { - str·free(fields[i]); - } - buffree(fields); - return nil; -} - diff --git a/sys/base/string/upper.c b/sys/base/string/upper.c deleted file mode 100644 index ab692c1..0000000 --- a/sys/base/string/upper.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "internal.h" - -// Upper will force all runes in the string to be uppercase. -void -str·upper(string s) -{ - byte *b, *e; - b = s; - e = b + str·len(s); - while (b++ != e) - *b = toupper(*b); -} diff --git a/sys/base/test.c b/sys/base/test.c deleted file mode 100644 index a29be1d..0000000 --- a/sys/base/test.c +++ /dev/null @@ -1,170 +0,0 @@ -#include -#include -#include - -#include - -uintptr -printtest(Coro *c, uintptr d) -{ - printf("--> Recieved %lu\n", d); - d = coro·yield(c, d+10); - printf("--> Now %lu\n", d); - - return d; -} - -uintptr -sequence(Coro *c, uintptr start) -{ - int d = start; - for (;;) { - coro·yield(c, d++); - } - - return d; -} - -struct PrimeMsg -{ - Coro *seq; - int p; -}; - -uintptr -filter(Coro *c, uintptr data) -{ - int x, p; - Coro *seq; - struct PrimeMsg *msg; - - // Need to copy relevant variables onto the local stack - // Data is volatile. - msg = (struct PrimeMsg*)data; - seq = msg->seq; - p = msg->p; - - for (;;) { - x = coro·yield(seq, x); - if (x % p != 0) { - x = coro·yield(c, x); - } - } - - return 0; -} - -error -test·coro() -{ - int i; - Coro *c[4]; - uintptr d; - - printf("Starting singleton test\n"); - - for (i = 0; i < arrlen(c); i++) { - c[i] = coro·make(0, &printtest); - } - - /* Singleton test */ - d = 0; - for (i = 0; i < 10; i++) { - d = coro·yield(c[0], d); - } - - printf("Starting triplet test\n"); - - /* Triplet test */ - for (i = 0; i < 10; i++) { - d = coro·yield(c[1], d); - d = coro·yield(c[2], d+100); - d = coro·yield(c[3], d+200); - } - - for (i = 0; i < arrlen(c); i++) { - coro·free(c[i]); - } - - /* Prime sieve */ - printf("Starting prime test\n"); - uintptr num; - Coro *cur, *seq[50]; - - num = 2; - seq[0] = coro·make(4096, &sequence); - cur = *seq; - - num = coro·yield(cur, num); - for (i = 1; i < arrlen(seq); i++) { - seq[i] = coro·make(4096, &filter); - struct PrimeMsg msg = { - .seq = cur, - .p = num, - }; - cur = seq[i]; - num = coro·yield(cur, (uintptr)&msg); - printf("--> prime number %lu\n", num); - } - return 0; -} - -int -less(void* a, void* b) -{ - int ai, bi; - ai = *(int*)a; - bi = *(int*)b; - - return ai - bi; -} - -error -test·sort() -{ - clock_t t; - int i, test[10000]; - for (i = 0; i < arrlen(test); i++) { - test[i] = rand(); - } - - t = clock(); - sort·int(arrlen(test), test); - t = clock() - t; - printf("inlined code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); - - for (i = 0; i < arrlen(test); i++) { - test[i] = rand(); - } - - t = clock(); - qsort(test, arrlen(test), sizeof(int), (int (*)(const void *, const void *))less); - t = clock() - t; - printf("std qsort code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); - - /* - for (i = 1; i < arrlen(test); i++) { - if (test[i] >= test[i-1]) { - printf("%d is less that %d\n", test[i], test[i-1]); - } else { - printf("ERROR: %d is NOT less that %d\n", test[i], test[i-1]); - } - } - */ - - return 0; -} - -error -main() -{ - error err; -#if 0 - if (err = test·coro(), err) { - errorf("test fail: coroutine"); - } -#endif - if (err = test·sort(), err) { - errorf("test fail: coroutine"); - } -} diff --git a/sys/cmd/cc/ast.c b/sys/cmd/cc/ast.c deleted file mode 100644 index 4330bcc..0000000 --- a/sys/cmd/cc/ast.c +++ /dev/null @@ -1,2139 +0,0 @@ -#include "cc.h" - -// ----------------------------------------------------------------------- -// helper macros - -#define alloc(ptr) (ptr) = mem·arenaalloc(C.heap, 1, sizeof *(ptr)) -#define copyarray(dst, arr, n) (dst) = mem·arenaalloc(C.heap, (n), sizeof *(arr)), memcpy((dst), (arr), n * sizeof *(arr)) -#define movearray(dst, arr, n) copyarray(dst,arr,n), free(arr) - -#define attop(prs) ((uintptr)prs->sp == (uintptr)prs->spstk) -#define peek(p, i) (p->tok[i]) -#define iskw(t, k) (((t).kind == Akeywd) && (t).val.i == (k)) -#define advance(p, l) (p->tok[0] = p->tok[1], p->tok[1] = lex(l), p->tok[0]) - -#define Bit(i) (1 << (i)) - -// ----------------------------------------------------------------------- -// helper functions - -static -string -nameof(Name *n) -{ - switch (n->kind) { - /* 0 corresponds to no state - i.e. an abstract name */ - case Nnil: - return nil; - case Nident: - return n->ident; - case Nparen: - return nameof(n->paren->name); - case Nindex: - case Ncall: - return nameof(n->sfx.name); - } - panicf("unreachable"); - return nil; -} - -static -void -openscope(Parser *p) -{ - if (++p->sp >= arrend(p->spstk)) - panicf("scope stack overflow"); -} - -/* - * TODO: save the symbol table with the ast node - * write a "copy(move)scope" - */ - -static -void -closescope(Parser *p) -{ - if (p->sp <= p->spstk) - panicf("scope stack underflow"); - - forgetall(&p->sp->objs); - forgetall(&p->sp->tags); - p->sp--; -} - -/* temporary stack helpers */ -static -Name* -getname(Parser *p) -{ - if (p->nm >= arrend(p->nmstk)) - panicf("name stack overflow"); - return p->nm++; -} - -static void putdtor(Parser *p, Dtor *dt); - -static -void -putname(Parser *p, Name *n) -{ - if (p->nm <= p->nmstk) - panicf("name stack underflow"); - - switch (n->kind) { - case Nnil: - case Nident: - break; - case Nparen: - putdtor(p, n->paren); - break; - case Nindex: - case Ncall: - putname(p, n->sfx.name); - break; - default: - panicf("unrecognized name kind"); - } - *p->nm-- = (Name){0}; -} - -static -Ptr* -getptr(Parser *p) -{ - if (p->pt >= arrend(p->ptstk)) - panicf("pointer stack overflow"); - - return p->pt++; -} - -static -void -putptr(Parser *p, Ptr *ptr) -{ - if (p->pt <= p->ptstk) - panicf("pointer stack underflow"); - - while ((ptr = ptr->link)) - putptr(p, ptr); - - *p->pt-- = (Ptr){0}; -} - - -static -Dtor* -getdtor(Parser *p) -{ - if (p->dt >= arrend(p->dtstk)) - panicf("dtor stack overflow"); - - p->dt->name = getname(p); - return p->dt++; -} - -static -void -putdtor(Parser *p, Dtor *dt) -{ - if (p->dt <= p->dtstk) - panicf("dtor stack underflow"); - - /* release the pointer overflow if we had to use it */ - if (p->dt->ptr.link) - putptr(p, p->dt->ptr.link); - - /* the dtor could encompass multiple names hierarchically */ - putname(p, dt->name); - *p->dt-- = (Dtor){0}; -} - -/* TODO: This will fail for forward declarations */ -static -void -declareobj(Parser *p, Decl *d) -{ - Sym *sym; - string ident; - uint32 kind; - struct Decls *link; - - switch (d->kind) { - case Dfunc: - case Dvar: - kind = Svar; - goto one; - case Dtype: - kind = Stype; - one: - ident = d->name; - break; - - case Dvars: - kind = Svar; - goto many; - case Dtypes: - kind = Stype; - many: - while (link = &d->list, link != nil) { - ident = link->name; - sym = lookup(&p->sp->objs, ident); - if (sym) { - errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident); - return; - } - sym = define(&p->sp->objs, ident, kind); - if (kind == Svar) - sym->obj = d; - else - sym->type = d->type; - } - break; - - default: - panicf("unrecognized node kind %d. expected declaration", d->kind); - } - sym = lookup(&p->sp->objs, ident); - if (sym) { - errorat(peek(p, 0).pos, "redeclaration of name '%s' in object space", ident); - return; - } - sym = define(&p->sp->objs, ident, kind); - if (kind == Svar) - sym->obj = d; - else - sym->type = d->type; -} - -/* enters the object identifier space */ -static -void -declareenum(Parser *p, int n, string *elts, Expr *vals) -{ - int i; - Sym *s; - - for (i = 0; i < n; i++) { - s = lookup(&p->sp->objs, elts[i]); - if (s) { - errorat(peek(p, 0).pos, "redeclaration of name %s in object space", elts[i]); - continue; - } - s = define(&p->sp->objs, elts[i], Senum); - s->val = vals + i; - } -} - -static -void -declaretag(Parser *p, uint32 t, string name) -{ - Sym *sym; - sym = lookup(&p->sp->tags, name); - if (sym) { - errorat(peek(p, 0).pos, "redeclaration of name '%s' in tag space", name); - return; - } - - sym = define(&p->sp->tags, name, Stype); - sym->type = t; -} - -static -Sym * -lookupobj(Parser *p, string ident) -{ - Sym *sym; - Scope *it; - - it = p->sp; - do { - sym = lookup(&it->objs, ident); - } while (sym == nil && --it >= p->spstk); - - return sym; -} - -static -Sym * -lookuptag(Parser *p, string ident) -{ - Sym *sym; - Scope *it; - - it = p->sp; - do { - sym = lookup(&it->tags, ident); - } while (sym == nil && --it >= p->spstk); - - return sym; -} - -static -int -nomatch(Token t, vlong kind) -{ - if (t.kind == kind) - return 0; - - if (t.kind == Akeywd) - errorat(t.pos, "expected token '%s', instead found keyword '%s'", tokens[kind], keywords[t.val.i]); - else - errorat(t.pos, "expected token '%s', instead found '%s'", tokens[kind], tokens[t.kind]); - return 1; -} - -// ----------------------------------------------------------------------- -// needed forward declarations - -static error spec(Parser *, Lexer *, uint64 *); -static uint32 basetype(Parser *, Lexer *, uint64 *s); -static string namedecl(Parser *, Lexer *, uint32 *, int); -static uint32 typename(Parser *, Lexer *, uint32 *); - -static error dtor(Parser *p, Lexer *lx, Dtor *d, int ab); -static uint32 typeofdtor(Dtor *, uint32); - - -static Decl *decl(Parser *, Lexer *); - -static Expr *ternary(Parser *, Lexer *); -static Expr *expr(Parser *, Lexer *); - -static error blkstmt(Parser *, Lexer *, Stmt **); - - -// ----------------------------------------------------------------------- -// expressions - -#define MAKEX(x, state) alloc((x)), (x)->kind = X##state - -static -Expr* -primary(Parser *p, Lexer *lx) -{ - int k; - Expr *x; - Token t; - Pos b; - - t = peek(p, 0); - b = t.pos; - switch (k = (t.kind & Vmask)) { - case Aident: - MAKEX(x, ident); - x->pos.beg = b; - x->pos.end = lx->pos; - x->name = t.val.s; - break; - - case Alit: - MAKEX(x, lit); - x->pos.beg = b; - x->pos.end = lx->pos; - x->val.kind = t.kind & ~Vmask; - x->val.v = t.val; - break; - - case Alparen: - advance(p, lx); - x = expr(p, lx); - t = peek(p, 0); - if (nomatch(t, Arparen)) { - errorat(lx->pos, "unterminated paren expression"); - goto Bad; - } - break; - - default: - panicf("unreachable"); - } - - advance(p, lx); - return x; -Bad: - errorat(lx->pos, "unable to parse operand expression"); - return nil; -} - -static -int -istypename(Parser *p, Token t) -{ - Sym *sym; - - if (t.kind == Akeywd && (Kconst <= t.val.i && t.val.i <= Kenum)) - return 1; - if (t.kind == Aident) { - sym = lookupobj(p, t.val.s); - return (sym != nil) && sym->kind == Stype; - } - - return 0; -} - -static Expr* initx(Parser *p, Lexer *lx); - -static -Expr* -initlist(Parser *p, Lexer *lx) -{ - Token t; - int c, n; - Expr *x, **a; - struct Key *k; - - MAKEX(x, initlist); - x->pos.beg = lx->pos; - x->init.n = 0; - if (t.kind == Arbrace) { - x->init.k = nil; - x->init.v = nil; - return x; - } - - c = 0; - n = 0; - a = nil; - k = nil; -Key0: - if (n >= c) { - c += 20; - k = realloc(k, c * sizeof(*k)); - a = realloc(a, c * sizeof(*a)); - } -Key1: - switch (t.kind) { - case Adot: - t = advance(p, lx); - if (t.kind != Aident) { - errorat(t.pos, "dot designator must be followed by identifier"); - goto Bad; - } - k[n++] = (struct Key) { - .kind = (uint32)x->init.n, - .s = t.val.s, - }; - t = advance(p, lx); - goto Key0; - - case Albrakt: - t = advance(p, lx); - k[n++] = (struct Key) { - .kind = (uint32)x->init.n | (1ULL << 32), - .x = expr(p, lx), - }; - t = peek(p, 0); - goto Key0; - - case Aeq: - t = advance(p, lx); - /* fallthrough */ - default: - a[x->init.n++] = initx(p, lx); - - t = peek(p, 0); - switch (t.kind) { - case Arbrace: - break; - case Acomma: - advance(p, lx); - /* fallthrough */ - default: - goto Key0; - } - break; - - case Acomma: - t = advance(p, lx); - break; - } - movearray(x->init.k, k, n); - movearray(x->init.v, a, x->init.n); - return x; -Bad: - errorat(t.pos, "could not parse initializer list"); - return nil; -} - -static -Expr* -initx(Parser *p, Lexer *lx) -{ - Expr *x; - Token t; - - t = peek(p, 0); - if (t.kind != Albrace) - return ternary(p, lx); - - advance(p, lx); - x = initlist(p, lx); - t = peek(p, 0); - if (nomatch(t, Arbrace)) { - errorat(t.pos, "unmatched brace in initializer list, found %s instead", tokens[t.kind]); - advance(p, lx); - } - - return x; -} - -static -Expr* -postfix(Parser *p, Lexer *lx) -{ - Pos b; - Token t; - int c, n; - uint32 type, qual; - Expr *x, *y, **a; - - t = peek(p, 0); - if (t.kind == Alparen) - if (istypename(p, peek(p, 1))) { - t = advance(p, lx); - type = typename(p, lx, &qual); - t = peek(p, 0); - if (nomatch(t, Arparen)) { - errorat(lx->pos, "unmatched paren: found %s instead", tokens[t.kind]); - goto Bad; - } - t = advance(p, lx); - if (nomatch(t, Albrace)) { - errorat(lx->pos, "bad initializer list: found %s", tokens[t.kind]); - goto Bad; - } - - x = initlist(p, lx); - - t = peek(p, 0); - if (nomatch(t, Arbrace)) { - errorat(lx->pos, "unmatched brace: found %s instead", tokens[t.kind]); - goto Bad; - } - - x->type = type; - x->qual = qual; - return x; - } - - x = primary(p, lx); - t = peek(p, 0); - for (;;) { - b = x->pos.beg; - switch (t.kind) { - case Ainc: - MAKEX(y, postinc); - goto Postfix; - case Adec: - MAKEX(y, postdec); - Postfix: - y->pos.beg = b; - y->pos.end = lx->pos; - y->unary.post = x; - x = y, y = nil; - break; - - case Adot: - MAKEX(y, self); - goto Select; - case Aarrow: - MAKEX(y, selp); - Select: - t = advance(p, lx); - if (t.kind != Aident) { - errorat(t.pos, "invalid operand of selector expression"); - goto Bad; - } - y->pos.beg = b; - y->pos.end = lx->pos; - - y->idx.f = t.val.s; - y->idx.x = x; - x = y, y = nil; - break; - - case Albrakt: - t = advance(p, lx); - if (t.kind == Arbrakt) { - errorat(t.pos, "empty index expression"); - goto Bad; - } - MAKEX(y, index); - y->idx.x = x; - y->idx.i = expr(p, lx); - - t = peek(p, 0); - if (t.kind != Albrakt) { - errorat(t.pos, "malformed index expression"); - goto Bad; - } - - x = y, y = nil; - break; - - case Alparen: - t = advance(p, lx); - MAKEX(y, call); - y->call.fn = x; - y->pos.beg = b; - y->call.n = 0; - if (t.kind == Arparen) { - y->call.arg = nil; - goto Endfunc; - } - c = 0; - a = nil; - Arg: - if (y->call.n >= c) { - c += 20; - a = realloc(a, c * sizeof(*a)); - } - a[y->call.n++] = expr(p, lx); - t = peek(p, 0); - if (t.kind == Acomma) { - advance(p, lx); - goto Arg; - } - if (t.kind != Arparen) { - errorat(t.pos, "invalid token '%s' found in call argument"); - goto Bad; - } - movearray(y->call.arg, a, y->call.n); - Endfunc: - y->pos.end = lx->pos; - x = y, y = nil; - break; - - default: - return x; - } - t = advance(p, lx); - } - return x; -Bad: - errorat(lx->pos, "failed to parse primary expression"); - return nil; -} - -static -uint32 -typename(Parser *p, Lexer *lx, uint32 *spec) -{ - uint32 base; - uint64 s; - - base = basetype(p, lx, &s); - if (!base) { - errorat(lx->pos, "failed to parse type name specifiers"); - return 0; - } - *spec = (uint32)s; - namedecl(p, lx, &base, 1); - - return base; -} - -static Expr* cast(Parser *p, Lexer *lx); - -static -Expr* -unary(Parser *p, Lexer *lx) -{ - Expr *x; - Token t; - - t = peek(p, 0); - switch (t.kind) { - case Ainc: MAKEX(x, preinc); goto Prefix; - case Adec: MAKEX(x, predec); /* fallthrough */ - Prefix: - advance(p, lx); - x->pos.beg = t.pos; - x->unary.pre = unary(p, lx); - x->pos.end = x->unary.pre->pos.end; - return x; - - case Aneg: MAKEX(x, neg); goto Unary; - case Aand: MAKEX(x, ref); goto Unary; - case Anot: MAKEX(x, not); goto Unary; - case Astar: MAKEX(x, star); goto Unary; - case Aadd: MAKEX(x, plus); goto Unary; - case Asub: MAKEX(x, minus); /* fallthrough */ - Unary: - advance(p, lx); - x->pos.beg = t.pos; - x->unary.pre = cast(p, lx); - x->pos.end = x->unary.pre->pos.end; - return x; - - case Akeywd: - switch (t.val.i) { - case Ksizeof: - MAKEX(x, sizeof); - goto Key; - case Kalignof: - MAKEX(x, alignof); - /* fallthrough */ - Key: - t = advance(p, lx); - if (t.kind == Alparen) - if (istypename(p, peek(p, 1))) { - t = advance(p, lx); - x->info.type = 0; - x->info.of.type = typename(p, lx, &x->info.of.qual); - - t = peek(p, 0); - if (nomatch(t, Arparen)) { - errorat(t.pos, "missing paren for size/alignof statement"); - goto Bad; - } - advance(p, lx); - return x; - } - - x->info.type = 1; - x->info.x = unary(p, lx); - return x; - - default: - ; - } - /* fallthrough */ - default: - return postfix(p, lx); - } -Bad: - return nil; -} - -static -Expr* -cast(Parser *p, Lexer *lx) -{ - Expr *x; - Token t; - - t = peek(p, 0); - if (t.kind == Alparen && istypename(p, peek(p,1))) { - t = advance(p, lx); - MAKEX(x, cast); - - x->pos.beg = t.pos; - x->cast.to.type = typename(p, lx, &x->cast.to.qual); - if (!x->cast.to.type) { - errorat(lx->pos, "invalid type operand of cast"); - goto Bad; - } - - t = peek(p, 0); - if (nomatch(t, Arparen)) { - errorat(lx->pos, "missing closing paren after cast expression"); - goto Bad; - } - advance(p, lx); - - x->cast.x = cast(p, lx); - x->pos.beg = lx->pos; - return x; - } - return unary(p, lx); - -Bad: - errorat(lx->pos, "failed to parse cast expression"); - return nil; -} - -/* static data for binary operators */ -#define OPERATORS \ - OPERATOR(Astar, 10, Xmul) \ - OPERATOR(Adiv, 10, Xdiv) \ - OPERATOR(Amod, 10, Xmod) \ - OPERATOR(Aadd, 9, Xadd) \ - OPERATOR(Asub, 9, Xsub) \ - OPERATOR(Alsft, 8, Xlsft) \ - OPERATOR(Arsft, 8, Xrsft) \ - OPERATOR(Agteq, 7, Xgteq) \ - OPERATOR(Alteq, 7, Xlteq) \ - OPERATOR(Alt, 7, Xlt) \ - OPERATOR(Agt, 7, Xgt) \ - OPERATOR(Aeq, 6, Xeql) \ - OPERATOR(Aneq, 6, Xneq) \ - OPERATOR(Aand, 5, Xand) \ - OPERATOR(Axor, 4, Xxor) \ - OPERATOR(Aor, 3, Xor) \ - OPERATOR(Aandand, 2, Xandand) \ - OPERATOR(Aoror, 1, Xoror) - -static int prectab[NUM_TOKENS] = -{ -#define OPERATOR(a, b, c) [a] = b, - OPERATORS -#undef OPERATOR -}; - -static int optab[NUM_TOKENS] = -{ -#define OPERATOR(a, b, c) [a] = c, - OPERATORS -#undef OPERATOR -}; -#undef OPERATORS - -static -Expr* -binary(Parser *p, Lexer *lx, int prec) -{ - Token t; - int k, np; - Expr *l, *x; - - l = cast(p, lx); - for (;;) { - t = peek(p, 0); - k = t.kind; - np = prectab[k]; - if (np < prec) - return l; - - alloc(x); - t = advance(p, lx); - - x->pos.beg = l->pos.beg; - x->kind = optab[k]; - x->binary.l = l; - x->binary.r = binary(p, lx, np + 1); - x->pos.end = x->binary.r->pos.end; - - l = x; - } - return l; -Bad: - errorat(t.pos, "failed to parse expression"); - return nil; -} - -static -Expr* -ternary(Parser *p, Lexer *lx) -{ - Pos b; - Token t; - Expr *x, *y; - - x = binary(p, lx, 1); - t = peek(p, 0); - b = t.pos; - - switch (t.kind) { - case Aqmark: - t = advance(p, lx); - y = x; - MAKEX(x, ternary); - x->pos.beg = b; - x->kind = Xternary; - x->cond.c = y; - x->cond.t = expr(p, lx); - - t = peek(p, 0); - if (nomatch(t, Acolon)) { - errorat(t.pos, "ternary expression missing ':'"); - goto Bad; - } - t = advance(p, lx); - x->cond.e = expr(p, lx); - x->pos.end = lx->pos; - break; - - case Aasn: MAKEX(y, asn); goto Assign; - case Aorasn: MAKEX(y, orasn); goto Assign; - case Axorasn: MAKEX(y, xorasn); goto Assign; - case Aandasn: MAKEX(y, andasn); goto Assign; - case Asubasn: MAKEX(y, subasn); goto Assign; - case Amulasn: MAKEX(y, mulasn); goto Assign; - case Adivasn: MAKEX(y, divasn); goto Assign; - case Amodasn: MAKEX(y, modasn); goto Assign; - case Alsftasn: MAKEX(y, lsftasn); goto Assign; - case Arsftasn: MAKEX(y, rsftasn); goto Assign; - Assign: - advance(p, lx); - - y->asn.l = x; - y->asn.r = ternary(p, lx); - x = y; - x->pos.beg = b; - x->pos.end = lx->pos; - break; - default: - ; - } - - return x; -Bad: - errorat(lx->pos, "failing expression parse"); - return nil; -} - -static -Expr* -expr(Parser *p, Lexer *lx) -{ - Pos b; - Token t; - Expr *x, *y; - - x = ternary(p, lx); - while (t = peek(p, 0), t.kind == Acomma) { - advance(p, lx); - y = x; - MAKEX(x, comma); - x->pos.beg = y->pos.beg; - x->comma.x[0] = y; - x->comma.x[1] = ternary(p, lx); - x->pos.end = lx->pos; - y = nil; - } - - return x; -} - -// ----------------------------------------------------------------------- -// statements - -static -struct Node* -stmt(Parser *p, Lexer *lx) -{ - int k; - Stmt *s; - Sym *sym; - Token t; - - t = peek(p, 0); - k = t.kind; - - /* intercept decl before allocating a statement */ - if (k == Aident) { - if (peek(p, 1).kind == Acolon) - goto Tlabel; - sym = lookupobj(p, t.val.s); - if (!sym) { - errorat(lx->pos, "unrecognized type identifier '%s'", t.val.s); - goto Bad; - } - - if (sym->kind == Stype) - goto Tdecl; - if (sym->kind == Svar) { - alloc(s); - s->pos.beg = lx->pos; - goto Texpr; - } - - errorat(lx->pos, "bad symbol type used as type identifier"); - goto Bad; - } - - if (k == Akeywd) { - if ((Kauto <= t.val.i && t.val.i <= Ktypedef) || (Kconst <= t.val.i && t.val.i <= Kenum)) { - Tdecl: - return (Node *)decl(p, lx); - } - } - - alloc(s); - s->pos.beg = lx->pos; - - switch (k) { - case Akeywd: - switch (k = t.val.i) { - case Kif: - t = advance(p, lx); - s->kind = Sif; - - if (nomatch(t, Alparen)) { - errorat(lx->pos, "missing opening paren before if conditional"); - goto Bad; - } - s->br.cond = expr(p, lx); - if (nomatch(t, Arparen)) { - errorat(lx->pos, "missing closing paren after if conditional"); - goto Bad; - } - s->br.body = stmt(p, lx); - - t = peek(p, 0); - if (iskw(t, Kelse)) - s->br.orelse = stmt(p, lx); - else - s->br.orelse = nil; - - break; - - case Kswitch: - t = advance(p, lx); - s->kind = Sswitch; - - if (nomatch(t, Alparen)) { - errorat(lx->pos, "missing opening paren before switch conditional"); - goto Bad; - } - s->br.cond = expr(p, lx); - if (nomatch(t, Arparen)) { - errorat(lx->pos, "missing closing paren after switch conditional"); - goto Bad; - } - s->br.body = stmt(p, lx); - s->br.orelse = nil; - - break; - - case Kfor: - t = advance(p, lx); - s->kind = Sfor; - - if (nomatch(t, Alparen)) { - errorat(lx->pos, "missing opening paren before for loop preamble"); - goto Bad; - } - - if (t.kind == Asemi) - s->loop.init = nil; - else { - // TODO: test for declaration - s->loop.init = (Node *)expr(p, lx); - } - - if (nomatch(t, Asemi)) { - errorat(lx->pos, "missing semicolon"); - goto Bad; - } - - if (t.kind == Asemi) - s->loop.cond = nil; - else - s->loop.cond = expr(p, lx); - - if (nomatch(t, Asemi)) { - errorat(lx->pos, "missing semicolon"); - goto Bad; - } - - if (t.kind == Asemi) - s->loop.step = nil; - else - s->loop.step = expr(p, lx); - - if (nomatch(t, Alparen)) { - errorat(lx->pos, "missing closing paren after for loop preamble"); - goto Bad; - } - s->loop.body = stmt(p, lx); - break; - - case Kwhile: - t = advance(p, lx); - s->kind = Swhile; - if (nomatch(t, Alparen)) { - errorat(lx->pos, "missing opening paren before while loop conditional"); - goto Bad; - } - s->loop.cond = expr(p, lx); - if (nomatch(t, Arparen)) { - errorat(t.pos, "missing closing paren after while loop conditional"); - goto Bad; - } - - s->loop.init = nil; - s->loop.step = nil; - s->loop.body = stmt(p, lx); - break; - - case Kdo: - t = advance(p, lx); - s->kind = Sdo; - s->loop.body = stmt(p, lx); - - if (!iskw(t, Kwhile)) { - errorat(t.pos, "missing while statement conditional after do body"); - goto Bad; - } - t = advance(p, lx); - if (nomatch(t, Alparen)) { - errorat(t.pos, "missing open paren after while conditional"); - goto Bad; - } - - s->loop.init = nil; - s->loop.step = nil; - s->loop.cond = expr(p, lx); - break; - - case Kgoto: - t = advance(p, lx); - s->kind = Sgoto; - if (t.kind != Aident) { - errorat(t.pos, "invalid argument to goto"); - goto Bad; - } - s->jmp.lbl = t.val.s; - t = advance(p, lx); - if (nomatch(t, Asemi)) { - errorat(t.pos, "missing semicolon after goto"); - goto Bad; - } - advance(p, lx); - break; - - case Kcontinue: - t = advance(p, lx); - s->kind = Scontin; - s->jmp.lbl = nil; - s->jmp.x = nil; - if (nomatch(t, Asemi)) { - errorat(t.pos, "missing semicolon after continue"); - goto Bad; - } - advance(p, lx); - break; - - case Kbreak: - t = advance(p, lx); - s->kind = Sbreak; - s->jmp.lbl = nil; - s->jmp.x = nil; - if (nomatch(t, Asemi)) { - errorat(t.pos, "missing semicolon after break"); - goto Bad; - } - advance(p, lx); - break; - - case Kreturn: - t = advance(p, lx); - s->kind = Sreturn; - - s->jmp.lbl = nil; - s->jmp.x = (t.kind == Asemi) ? nil : expr(p, lx); - - t = peek(p, 0); - if (nomatch(t, Asemi)) { - errorat(t.pos, "missing semicolon after return statement"); - goto Bad; - } - advance(p, lx); - break; - - case Kcase: - t = advance(p, lx); - s->kind = Scase; - s->lbl.x = expr(p, lx); - if (nomatch(t, Acolon)) { - errorat(t.pos, "missing colon after default label"); - goto Bad; - } - t = advance(p, lx); - s->lbl.stmt = stmt(p, lx); - break; - - case Kdefault: - t = advance(p, lx); - s->kind = Scase; - s->lbl.x = nil; - if (nomatch(t, Acolon)) { - errorat(t.pos, "missing colon after default label"); - goto Bad; - } - t = advance(p, lx); - s->lbl.stmt = stmt(p, lx); - break; - - default: - panicf("unexpected statement keyword %s", keywords[k]); - } - break; - case Albrace: - s->kind = Sblock; - openscope(p); - if (blkstmt(p, lx, &s)) { - errorat(lx->pos, "failed to parse block statement"); - goto Bad; - } - closescope(p); - break; - - case Asemi: - t = advance(p, lx); - s->kind = Sempty; - break; - - case Aident: - Tlabel: - t = advance(p, lx); - s->kind = Slabel; - if (nomatch(t, Acolon)) { - errorat(t.pos, "missing colon after labelled block"); - goto Bad; - } - t = advance(p, lx); - s->lbl.stmt = stmt(p, lx); - break; - - default: - Texpr: - s->kind = Sexpr; - s->x = expr(p, lx); - - t = peek(p, 0); - if (nomatch(t, Asemi)) { - errorat(t.pos, "missing semicolon after statement expression"); - goto Bad; - } - advance(p, lx); - } - - s->pos.end = lx->pos; - return (Node *)s; -Bad: - errorat(lx->pos, "failed to parse statement"); - return nil; -} - -static -error -blkstmt(Parser *p, Lexer *lx, Stmt **s) -{ - Token t; - int len; - int cap; - Node **ns; - - alloc(*s); - (*s)->kind = Sblock; - (*s)->pos.beg = lx->pos; - - t = peek(p, 0); - if (nomatch(t, Albrace)) - goto Bad; - t = advance(p, lx); - - len = 0, cap = 20; - ns = malloc(cap*sizeof(*ns)); - while (t.kind != Arbrace) { - if (cap == len) { - cap += 20; - ns = realloc(ns, cap*sizeof(*ns)); - } - ns[len++] = stmt(p, lx); - t = peek(p, 0); - } - advance(p, lx); - - (*s)->pos.end = lx->pos; - (*s)->blk.n = len; - movearray((*s)->blk.item, ns, len); - return 0; -Bad: - errorat(lx->pos, "failed to parse block statement"); - free(ns); - return 1; -} - -// ----------------------------------------------------------------------- -// types - -uint32 -ptrtype(uint32 base, uint32 qual) -{ - uint32 i; - Type *t; - - i = type(); - t = C.type.info + i; - t->kind = Tptr; - t->ptr.base = base; - t->ptr.qual = qual; - t->size = pointer.size; - t->align = pointer.align; - t->sign = pointer.sign; - - return i; -} - -uint32 -arraytype(uint32 base, uint32 qual, Expr *ix) -{ - int i, n; - Type *t; - - /* TODO: evaluate the length */ - n = 10; - i = type(); - t = C.type.info + i; - t->kind = Tarray; - t->ptr.base = base; - t->size = n * C.type.info[base].size; - t->align = C.type.info[base].align; - t->sign = 0; - - return i; -} - -uint32 -functype(uint32 ret, int n, Field *args, int dots) -{ - uint32 i; - Type *t; - - i = type(); - t = C.type.info + i; - t->kind = Tfunc; - t->size = pointer.size; - t->align = pointer.align; - t->sign = pointer.sign; - - t->func.ret = ret; - t->func.n = n; - t->func.arg = args; - t->func.dots = dots; - - return i; -} - -#define ALIGN_DOWN(n, a) ((n) & ~((a)-1)) -#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a)) -uint32 -structtype(int n, Field *field, Expr *bits) -{ - uint32 i; - Type *t; - Field *f, *e; - - i = type(); - t = C.type.info + i; - t->kind = Tstruct; - t->size = 0; - t->align = 0; - for (f = field, e = field+n; f != e; ++f) { - t->size += C.type.info[f->type].size + ALIGN_UP(t->size, C.type.info[f->type].align); - t->align = MAX(t->align, C.type.info[f->type].align); - } - t->aggr.len = n; - t->aggr.f = field; - t->aggr.x = bits; - - return i; -} - -uint32 -uniontype(int n, Field *field, Expr *bits) -{ - uint32 i; - Type *t; - Field *f, *e; - - i = type(); - t = C.type.info + i; - t->kind = Tstruct; - t->size = 0; - t->align = 0; - for (f = field, e = field+n; f != e; ++f) { - t->size = MAX(t->size, C.type.info[f->type].size); - t->align = MAX(t->align, C.type.info[f->type].align); - } - t->aggr.len = n; - t->aggr.f = field; - t->aggr.x = bits; - - return i; -} - -uint32 -enumtype(int n, string *elts, Expr *vals) -{ - uint32 i; - Type *t; - Field *f, *e; - - i = type(); - t = C.type.info + i; - t->kind = Tenum; - /* TODO: dont hardcode int32 */ - t->size = 4; - t->align = 4; - t->enm.len = n; - t->enm.elt = elts; - t->enm.val = vals; - - return i; -} -#undef ALIGN_UP -#undef ALIGN_DOWN - -/* unpacking C declarations into sensible types */ -static -uint32 -typeofname(Name *name, uint32 base) -{ - switch (name->kind) { - /* Nnil corresponds to an abstract declarator (i.e. no identifier) */ - case Nnil: - case Nident: - return base; - case Nparen: - return typeofdtor(name->paren, base); - case Nindex: - return typeofname(name->sfx.name, arraytype(base, name->sfx.idx.q, name->sfx.idx.x)); - case Ncall: - return typeofname(name->sfx.name, functype(base, name->sfx.call.n, name->sfx.call.arg, name->sfx.call.dots)); - default: - panicf("unreachable"); - } - return 0; -} - -static -uint32 -typeofdtor(Dtor *decl, uint32 base) -{ - int n; - Ptr *p; - uint64 b, tmp; - - n = 0; - p = &decl->ptr; - b = p->kind; - while (b & 1) { - base = ptrtype(base, b >> 1); - if (++n >= 8) { - p = p->link; - b = p->kind; - } else { - b >>= 6; - } - } - - return typeofname(decl->name, base); -} - -static -uint32 -basetype(Parser *p, Lexer *lx, uint64 *s) -{ - int n; - uint64 m; - - if (spec(p, lx, s)) { - errorat(lx->pos, "failed to parse type specifier"); - return 0; - } - - m = (((*s<<32)>>32) & ~(MaskQul|MaskMem|MaskFcn)); - for (n = 0; n < arrlen(validtypespec); n++) { - if (validtypespec[n] == m) { - if (indextypespec[n] < 0) { - m = *s >> 32; - if (!m) { - errorat(lx->pos, "not a valid type identifier"); - return 0; - } - return m; - } - return indextypespec[n]; - } - } - - errorat(lx->pos, "invalid type specifier"); - return 0; -} - -static -string -namedecl(Parser *p, Lexer *lx, uint32 *base, int noname) -{ - Dtor *dt; - string name; - Type *t; - - dt = getdtor(p); - name = nil; - if (dtor(p, lx, dt, noname)) { - errorat(lx->pos, "invalid declarator"); - goto End; - } - if (!noname || noname == 2 && dt->name->kind) - name = nameof(dt->name); - - *base = typeofdtor(dt, *base); - putdtor(p, dt); - return name; -End: - putdtor(p, dt); - return nil; -} - -// ----------------------------------------------------------------------- -// declarations - -static -uint32 -enumerate(Parser *p, Lexer *lx, string name, int kind) -{ - int i, n; - uint64 s; - uint32 t; - Token tk; - /* TODO: think of a better soln */ - string nm[1024], *elts; - Expr *cx[1024], *vals; - - for (n = 0; tk.kind != Arbrace && n < arrlen(nm); n++) { - if (tk.kind != Aident) { - errorat(tk.pos, "invalid token %s in enum declaration", tokens[tk.kind]); - goto Bad; - } - nm[n] = tk.val.s; - cx[n] = nil; - - tk = advance(p, lx); - switch(tk.kind) { - case Aeq: - advance(p, lx); - cx[n] = expr(p, lx); - tk = peek(p, 0); - if (tk.kind != Acomma) - continue; - /* fallthrough */ - case Acomma: - tk = advance(p, lx); - } - } - copyarray(elts, nm, n); - copyarray(vals, cx, n); - - t = enumtype(n, elts, vals); - declareenum(p, n, elts, vals); - return t; -Bad: - errorat(tk.pos, "failed to parse enum declaration"); - return 0; -} - -static -uint32 -aggregate(Parser *p, Lexer *lx, string name, int kind) -{ - int n; - uint64 s; - Token tk; - /* TODO: think of a better soln */ - static Field fs[1024]; - Field *f; - static Expr *cx[1024]; - Expr *x; - - for (n = 0, tk = peek(p, 0); tk.kind != Arbrace && n < arrlen(fs); n++) { - fs[n].type = basetype(p, lx, &s); - fs[n].qual = (uint32)(s & ~(MaskTyp|MaskInt|MaskFlt)); - Field: - fs[n].name = namedecl(p, lx, &fs[n].type, 0); - tk = peek(p, 0); - switch (tk.kind) { - case Acolon: - advance(p, lx); - cx[n] = expr(p, lx); - tk = peek(p, 0); - if (tk.kind == Asemi) { - tk = advance(p, lx); - continue; - } - if (tk.kind != Acomma) { - errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]); - goto Bad; - } - /* fallthrough */ - case Acomma: - advance(p, lx); - n++; - goto Field; - - case Asemi: - tk = advance(p, lx); - continue; - - default: - errorat(tk.pos, "unrecognized token %s in struct field declaration", tokens[tk.kind]); - goto Bad; - } - } - copyarray(f, fs, n); - copyarray(x, cx, n); - return (kind == Tstruct) ? structtype(n, f, x) : uniontype(n, f, x); -Bad: - errorat(tk.pos, "failed to parse aggregate declaration"); - return 0; -} - -static -error -spec(Parser *p, Lexer *lx, uint64 *spec) -{ - Token t; - int n, i; - Sym *typ; - string name; - uint32 tag; - uint64 s, sm; - static uint32 (*aggrfunc[2])(Parser *, Lexer *, string , int) = {aggregate, enumerate}; - - s = 0; - while (t = peek(p, 0), t.kind >= Aident) { - /* typename */ - if (t.kind == Aident) { - typ = lookupobj(p, t.val.s); - if (!typ || (typ && typ->kind != Stype)) - break; - - sm = typ->type; - s |= (sm << 32 | Tname); - advance(p, lx); - continue; - } - - /* keyword */ - switch (n = t.val.i) { - case Kauto: case Kregister: case Kstatic: case Kextern: case Ktypedef: case Ktls: - if (s & MaskMem) { - errorat(lx->pos, "multiple storage class specifiers: second was %s", keywords[n]); - goto Bad; - } - break; - - case Kinline: case Knoret: - if (s & Bit(n)) - warnat(lx->pos, "duplicate %s function specifier", keywords[n]); - break; - - case Kconst: case Kvolatile: - if (s & Bit(n)) - warnat(lx->pos, "duplicate %s specifier found in declaration", keywords[n]); - break; - - case Ksigned: case Kunsigned: - if (s & MaskSgn) { - if (s & Bit(n)) { - warnat(lx->pos, "duplicated storage class specifier: second was %s", keywords[n]); - break; - } - errorat(lx->pos, "multiple storage class specifiers"); - goto Bad; - } - break; - - case Kshort: - if (s & Tshort) { - warnat(lx->pos, "duplicated short specifier"); - break; - } - break; - - case Klong: - if ((s >> Klong) & 2) { - errorat(lx->pos, "cannot chain three or more long specifiers"); - goto Bad; - } - s += Bit(n); - t = advance(p, lx); - continue; - - case Kvoid: case Kchar: case Kint: case Kfloat: case Kdouble: - if (s & MaskTyp) { - errorat(lx->pos, "more than one base type specified"); - goto Bad; - } - break; - - case Kstruct: case Kunion: - i = 0; - goto Aggr; - case Kenum: - i = 1; - Aggr: - if (s & (Tstruct | Tunion | Tenum)) { - errorat(lx->pos, "more than one aggregate/enum type specified"); - goto Bad; - } - t = advance(p, lx); - if (t.kind != Aident && t.kind != Albrace) { - errorat(t.pos, "enum specifier missing valid declaration"); - goto Bad; - } - - /* NOTE: This offset is needed to correctly obtain Tstruct */ - n++; - name = nil; - tag = 0; - if (t.kind == Aident) { - name = t.val.s; - t = advance(p, lx); - } - if (t.kind == Albrace) { - /* TODO: we need check if the name exists. */ - t = advance(p, lx); - /* NOTE: This depends on the enum order. KEEP IN SYNC */ - tag = aggrfunc[i](p, lx, name, Bit(n)); - if (t = peek(p, 0), nomatch(t, Arbrace)) { - errorat(t.pos, "invalid token %s in aggregate/enum declaration", tokens[t.kind]); - goto Bad; - } - /* high bits encode the type index */ - s |= (uint64)tag << 32; - } - /* TODO: if name does not exist, enter in an incomplete type! */ - if (name) - declaretag(p, tag, name); - - break; - - default: - errorat(t.pos, "invalid keyword '%s' found in declaration specifier", keywords[n]); - } - - s |= Bit(n); - advance(p, lx); - } - - *spec = s; - return 0; - -Bad: - /* TODO: serialize bitflags to string for nice error message */ - errorat(lx->pos, "ignoring specifier"); - *spec = Sbad; - return 1; -} - -/* - * name declaration - * see dtor for valid values of ab - */ -static -error -name(Parser *p, Lexer *lx, Name **nmp, int ab) -{ - Token t; - int n, k; - uint64 s; - Sym *sym; - Name *nm, *tmp; - - /* max args = 100 */ - struct Field args[100]; - - nm = *nmp; - t = peek(p, 0); - switch (k = t.kind) { - case Aident: - if (ab == 1) { - errorat(t.pos, "identifier not allowed in abstract declarator"); - goto Bad; - } - nm->kind = Nident; - nm->ident = t.val.s; - break; - - case Alparen: - advance(p, lx); - nm->kind = Nparen; - nm->paren = getdtor(p); - if (dtor(p, lx, nm->paren, ab)) { - putdtor(p, nm->paren); - nm->paren = nil; - errorat(lx->pos, "invalid declarator in parenthesis"); - goto Bad; - } - - t = peek(p, 0); - if (nomatch(t, Arparen)) { - putdtor(p, nm->paren); - nm->paren = nil; - errorat(lx->pos, "missing closing paren in declarator"); - goto Bad; - } - break; - - case Albrakt: - if (ab) - goto Sfx; - errorat(lx->pos, "missing identifier in non-abstract declarator"); - /* fallthrough */ - default: - if (ab) - goto Sfx; - errorat(lx->pos, "invalid token '%s' in name declaration", tokens[k]); - goto Bad; - } - - t = advance(p, lx); -Sfx: - for (;;) { - switch (k = t.kind) { - case Albrakt: - tmp = getname(p); - tmp->kind = Nindex; - tmp->sfx.name = nm; - - nm = tmp, tmp = nil; - - t = advance(p, lx); - if (t.kind == Arbrakt) { - nm->sfx.idx.q = 0; - Iend: - nm->sfx.idx.x = nil; - t = advance(p, lx); - break; - } - if (t.kind == Astar) { - nm->sfx.idx.q = -1; - IStar: - nm->sfx.idx.x = nil; - t = advance(p, lx); - if (t.kind != Arbrakt) { - errorat(t.pos, "invalid '*' syntax in index expression"); - goto Bad; - } - t = advance(p, lx); - break; - } - - if (spec(p, lx, &s)) { - errorat(lx->pos, "invalid type qualifier list in index expression"); - goto Bad; - } - - nm->sfx.idx.q = (uint32)s; - t = peek(p, 0); - - if (t.kind == Astar) - goto IStar; - - if (t.kind == Arbrakt) - goto Iend; - - nm->sfx.idx.x = expr(p, lx); - - t = peek(p, 0); - if (nomatch(t, Arbrakt)) { - errorat(t.pos, "unterminated index expression"); - goto Bad; - } - - t = advance(p, lx); - continue; - - case Alparen: - tmp = getname(p); - tmp->kind = Ncall; - tmp->sfx.name = nm; - - nm = tmp, tmp = nil; - - t = advance(p, lx); - nm->sfx.call.n = 0; - switch (t.kind) { - case Arparen: - nm->sfx.call.arg = nil; - break; - - case Aident: - sym = lookupobj(p, t.val.s); - if (!sym || (sym && sym->kind != Stype)) { - while (t.kind == Aident) { - if (nm->sfx.call.n >= arrlen(args)) - panicf("ident stack overflow"); - args[nm->sfx.call.n++] = (struct Field) { - .qual = 0, - .type = 0, - .name = t.val.s, - }; - t = advance(p, lx); - } - if (nomatch(t, Arparen)) { - errorat(t.pos, "token '%s' found in function parameter identifier list"); - goto Bad; - } - copyarray(nm->sfx.call.arg, args, nm->sfx.call.n); - break; - } - goto ParamLoop; - - case Akeywd: - if (t.val.i < Kconst || t.val.i > Kenum) { - errorat(t.pos, "invalid keyword %s inside function signature"); - goto Bad; - } - - ParamLoop: - if (nm->sfx.call.n >= arrlen(args)-1) - panicf("out of argument buffer"); - - args[nm->sfx.call.n].type = basetype(p, lx, &s); - if (!args[nm->sfx.call.n].type) { - errorat(lx->pos, "could not parse base type in function call"); - goto Bad; - } - - args[nm->sfx.call.n].qual = (uint32)s & ~(MaskTyp|MaskInt|MaskFlt); - args[nm->sfx.call.n].name = namedecl(p, lx, &args[nm->sfx.call.n].type, 2); - - nm->sfx.call.n++; - if ((t = peek(p, 0)).kind == Acomma) { - advance(p, lx); - goto ParamLoop; - } - - if (t.kind == Aellip) { - nm->sfx.call.dots = 1; - t = advance(p, lx); - } - - if (nomatch(t, Arparen)) { - errorat(t.pos, "token '%s' found in function parameter list"); - goto Bad; - } - copyarray(nm->sfx.call.arg, args, nm->sfx.call.n); - break; - - default: - errorat(t.pos, "invalid token %s inside function call signature", tokens[t.kind]); - goto Bad; - } - - t = advance(p, lx); - continue; - - default: - break; - } - break; - } - - *nmp = nm; - return 0; -Bad: - return 1; -} - -/* pointer kind is partitioned into 8x6 regions - * ab => abstract - * @ 0: must have identifier - * @ 1: must not have identifier - * @ 2: don't care - * else: undefined - */ -static -error -dtor(Parser *p, Lexer *lx, Dtor *d, int ab) -{ - int n, k; - error err; - Token t; - Dtor *link; - Ptr *ptr, *x; - - err = 1; - - ptr = &d->ptr; - ptr->kind = 0; - ptr->link = nil; - - t = peek(p, 0); - if (t.kind != Astar) { - if (ab || t.kind == Aident || t.kind == Arparen) - goto Name; - goto Bad; - } - n = 0; -Ptr: - ptr->kind |= Bit(n); - advance(p, lx); -Key: - t = peek(p, 0); - switch (k = t.kind) { - case Akeywd: - if (Kconst <= t.val.i && t.val.i <= Katomic) - ptr->kind |= Bit(6*n + (t.val.i - Kconst + 1)); - else { - errorat(lx->pos, "invalid keyword '%s' modifies pointer", keywords[t.val.i]); - goto Bad; - } - advance(p, lx); - goto Key; - - case Astar: - if (++n >= 8) { - x = getptr(p); - x->kind = 0; - x->link = nil; - ptr->link = x; - ptr = x; - n = 0; - } - goto Ptr; - - case Aident: - case Alparen: - goto Name; - - default: - if (ab) - goto Name; - errorat(lx->pos, "invalid token '%s' modifies pointer specification", tokens[t.kind]); - goto Bad; - } -Name: - return name(p, lx, &d->name, ab); -Bad: - return err; -} - -static -Decl * -decl(Parser *p, Lexer *lx) -{ - uint64 s; - Token t; - Decl *d; - Expr *x; - string name; - struct Decls *ds; - uint32 base, type; - - alloc(d); - - d->kind = 0; - d->pos.beg = lx->pos; - - base = basetype(p, lx, &s); - if (!base) { - errorat(lx->pos, "could not parse type declaration"); - goto Bad; - } - - x = nil; - d->spec = (uint32)s & ~(MaskInt|MaskFlt|MaskTyp); - d->type = base; - d->name = namedecl(p, lx, &d->type, 0); - /* TODO: think about functions (both decls and defs) */ - d->kind = (s & Mtype) ? Dtype : Dvar; - - switch (t = peek(p, 0), t.kind) { - case Aeq: - if (s & Mtype) { - errorat(d->pos.beg, "initialization of type not allowed"); - goto Bad; - } - t = advance(p, lx); - x = initx(p, lx); - d->kind = Dvar; - if (t.kind != Acomma) { - d->init = x; - goto Semi; - } - /* fallthrough */ - case Acomma: - d->kind |= Dlist; - d->list.init = x; - /* move singleton data over */ - name = d->name; - type = d->type; - d->list.name = name; - d->list.type = type; - ds = &d->list; - /* iterate until we hit end of list */ - while (t.kind == Acomma) { - t = advance(p, lx); - - alloc(ds->link); - ds = ds->link; - ds->type = base; - ds->name = namedecl(p, lx, &ds->type, 0); - - t = peek(p, 0); - if (t.kind == Aeq) { - t = advance(p, lx); - ds->init = initx(p, lx); - } else - ds->init = nil; - } - goto Semi; - - case Albrace: - d->kind = Dfunc; - alloc(d->body); - - if (!attop(p)) { - errorat(lx->pos, "nested function declarations are illegal"); - goto Bad; - } - - if (C.type.info[d->type].kind != Tfunc) { - errorat(lx->pos, "attempted to define function body for non function type"); - goto Bad; - } - - openscope(p); - if (blkstmt(p, lx, &d->body)) { - errorat(lx->pos, "failed to parse function body"); - goto Bad; - } - closescope(p); - break; - - default: - Semi: - if (nomatch(t, Asemi)) { - errorat(t.pos, "no semicolon after declaration"); - goto Bad; - } - t = advance(p, lx); - } - - d->pos.end = lx->pos; - declareobj(p, d); - return d; -Bad: - errorat(lx->pos, "failed to parse top level declaration"); - return nil; -} - -// ----------------------------------------------------------------------- -// top level api - -void -setup(Parser *p, Lexer *lx) -{ - advance(p,lx); - advance(p,lx); - - /* define all builtin typedefs */ - declareobj(p, &C.builtin.vargs); -} - -error -parse(Parser *p, Lexer *lx) -{ - Token tok; - - setup(p, lx); - while ((tok = peek(p, 0)), tok.kind > Aeof) { - if (p->ast.len >= p->ast.cap) { - p->ast.cap += 20; - p->ast.decls = realloc(p->ast.decls, p->ast.cap*sizeof(*p->ast.decls)); - } - p->ast.decls[p->ast.len++] = decl(p, lx); - } - - return 0; -} diff --git a/sys/cmd/cc/bits.c b/sys/cmd/cc/bits.c deleted file mode 100644 index 4b405dc..0000000 --- a/sys/cmd/cc/bits.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "cc.h" - -// ----------------------------------------------------------------------- -// Architecture - -enum -{ - archx64, - numarch, -}; - -// ----------------------------------------------------------------------- -// Types - -/* - * enumerated type specifers - * see https://en.wikipedia.org/wiki/C_data_types - */ -#define VOID X(Tvoid, 2) - -#define BOOL X(Tbool, 3) -#define CHAR X(Tchar, 4) -#define SCHAR X(Tsign|Tchar, 5) -#define UCHAR X(Tunsign|Tchar, 6) - -#define SHORT X(Tshort, 7), X(Tshort|Tint, 7) -#define SSHORT X(Tsign|Tshort, 8), X(Tsign|Tshort|Tint, 8) -#define USHORT X(Tunsign|Tshort, 9), X(Tunsign|Tshort|Tint, 9) - -#define INT X(0, 10), X(Tint, 10) -#define SINT X(Tsign, 11), X(Tsign|Tint, 11) -#define UINT X(Tunsign, 12), X(Tunsign|Tint, 12) - -#define LONG X(Tlong, 13), X(Tlong|Tint, 13) -#define SLONG X(Tsign|Tlong, 14), X(Tsign|Tlong|Tint, 14) -#define ULONG X(Tunsign|Tlong, 15), X(Tunsign|Tlong|Tint, 15) - -#define VLONG X(Tvlong, 16), X(Tvlong|Tint, 16) -#define SVLONG X(Tsign|Tvlong, 17), X(Tsign|Tvlong|Tint, 17) -#define UVLONG X(Tunsign|Tvlong, 18), X(Tunsign|Tvlong|Tint, 18) - -#define FLOAT X(Tfloat, 19) -#define DOUBLE X(Tdouble, 20) -#define LONGDB X(Tlong|Tdouble, 21) -#define COMPLEX X(Tcmplx, 22) -#define IMAGINARY X(Timag, 23) - -/* fixed width definitions */ -#define DEF(sz, aln, mx, sgn) {.size=sz, .align=aln, .max=mx, .sign=sgn } - -#define INT8 DEF(1, 1, 0x7fff, 0) -#define UINT8 DEF(1, 1, 0xffff, 1) - -#define INT16 DEF(2, 2, 0x7fff, 0) -#define UINT16 DEF(2, 2, 0xffff, 1) - -#define INT32 DEF(4, 4, 0x7fffffff, 0) -#define UINT32 DEF(4, 4, 0xffffffff, 1) - -#define INT64 DEF(8, 8, 0x7fffffffffffffff, 0) -#define UINT64 DEF(8, 8, 0xffffffffffffffff, 1) - -/* architecture specific definitions */ -// TODO: max value should be able to take floats -#define TYPES \ - TYPE(DEF(0, 0, 0, 0), VOID) \ - TYPE(INT8, BOOL) \ - TYPE(UINT8, CHAR) \ - TYPE(INT8, SCHAR) \ - TYPE(UINT8, UCHAR) \ - TYPE(INT16, SHORT) \ - TYPE(INT16, SSHORT) \ - TYPE(UINT16, USHORT) \ - TYPE(INT32, INT) \ - TYPE(INT32, SINT) \ - TYPE(UINT32, UINT) \ - TYPE(INT64, LONG) \ - TYPE(INT64, SLONG) \ - TYPE(UINT64, ULONG) \ - TYPE(INT64, VLONG) \ - TYPE(INT64, SVLONG) \ - TYPE(UINT64, UVLONG) \ - TYPE(DEF(4, 4, 0, 0), FLOAT) \ - TYPE(DEF(8, 8, 0, 0), DOUBLE) \ - TYPE(DEF(16, 16, 0, 0), LONGDB) \ - TYPE(DEF(8, 8, 0, 0), COMPLEX) \ - TYPE(DEF(4, 4, 0, 0), IMAGINARY) \ - -Type pointer = {.size=8, .align=8, .max=0xffffffffffffffff, .sign=0}; - -/* pack architecture specific definitions into exported arrays */ -#define TYPE(a, ...) a, -Type basetypes[] = { - { 0 }, /* sentinel value for bad types */ - { 0 }, /* sentinel value for variadic args */ - TYPES -}; -#undef TYPE - -#define TYPE(a, ...) __VA_ARGS__, -#define X(a, b) a -uint64 validtypespec[38] = { - TYPES - Tstruct, Tunion, Tenum, Tname, -}; -#undef X - -#define X(a, b) b -int indextypespec[38] = { - TYPES - -1, -1, -1, -1, -}; -#undef X -#undef TYPE diff --git a/sys/cmd/cc/cc.c b/sys/cmd/cc/cc.c deleted file mode 100644 index 8ad0022..0000000 --- a/sys/cmd/cc/cc.c +++ /dev/null @@ -1,409 +0,0 @@ -#include "cc.h" -#include - -// ----------------------------------------------------------------------- -// string interning - -/* jenkins' one at a time hash */ -static -int32 -hash_string(byte* s) -{ - int32 h; - - h = 0; - if (s != nil) { - for (; *s; ++s) { - h += *s; - h = (h << 10); - h = (h >> 6); - } - } - - h += (h << 3); - h ^= (h >> 11); - h += (h >> 11); - - return h; -} - -static -int -streq(byte *s, byte *t) -{ - if (s == nil) { - if (t == nil) - return 1; - else - return 0; - } - - return (t == nil) ? 0 : strcmp(s, t) == 0; -} - -#define HASH(s) hash_string(s) -#define EQUAL(s, t) (streq(s, t)) -static -int -getstr(string key, int *ok) -{ - int idx; - MAP_GET(idx, (&C.strs), key, HASH, EQUAL); - - *ok = idx < C.strs.n_buckets; - return idx; -} - -static -void -·free(void* _, void* ptr) { - return free(ptr); -} - -static -void * -·alloc(void* _, uint n, ulong size) { - return malloc(n*size); -} - -static -void * -·calloc(void* _, uint n, ulong size) { - return calloc(n, size); -} - -static -int -morestrtab(StrTab *tab, int n) -{ - MAP_GROW(tab, string, int32, n, HASH, ·calloc, ·free, nil); -} - -static -int -putstr(byte *s, error *err) -{ - int sz; - sz = C.strs.size; - MAP_PUT((&C.strs), s, sz, HASH, EQUAL, morestrtab, err); -} -#undef HASH -#undef EQUAL - -int32 -intern(byte **s) -{ - int i, ok; - - i = getstr(*s, &ok); - if (ok) { - *s = C.strs.keys[i]; - goto END; - } - - *s = str·make(*s); - i = putstr(*s, &ok); - C.strs.vals[i] = C.strs.size - 1; - -END: - return C.strs.vals[i]; -} - -// ----------------------------------------------------------------------- -// type interning - -/* TODO: intern types for memory savings */ -int -type() -{ - if (C.type.len >= C.type.cap) { - C.type.cap += 100; - C.type.info = realloc(C.type.info, C.type.cap * sizeof(*C.type.info)); - } - - return C.type.len++; -} - -// ----------------------------------------------------------------------- -// universal compiler builtins - -#define KEYWORD(a, b) b, -byte *keywords[NUM_KEYWORDS] = { KEYWORDS }; -#undef KEYWORD - -#define DIRECTIVE(a, b, c) b, -byte *directives[NUM_DIRECTIVES] = { DIRECTIVES }; -#undef DIRECTIVE - -struct Compiler C = { 0 }; - -// ----------------------------------------------------------------------- -// cli flag handlers - -void -pushinclude(byte *dirs) -{ - string d, s, *it, *end; - - while (*dirs != '\0') { - d = strchr(dirs, ' '); - if (d != nil) - *d = '\0'; - - s = dirs; - intern(&s); - for (it = C.inc.dir, end = it + C.inc.len; it != end; ++it) { - if ((uintptr)s == (uintptr)(*it)) - goto Nextdir; - } - - if (C.inc.len == C.inc.cap) { - C.inc.cap += 20; - C.inc.dir = realloc(C.inc.dir, C.inc.cap*sizeof(*C.inc.dir)); - } - C.inc.dir[C.inc.len++] = s; -Nextdir: - if (d == nil) - break; - dirs = d + 1; - } -} - -// ----------------------------------------------------------------------- -// error reporting - -void -errorat(Pos x, byte *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - printf("error:%s:%d:%d: ", os·basename(x.path), x.line, x.col); - vprintf(fmt, args); - printf("\n"); - - va_end(args); - assert(0); -} - -void -warnat(Pos x, byte *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - printf("warning:%s:%d:%d: ", os·basename(x.path), x.line, x.col); - vprintf(fmt, args); - printf("\n"); - - va_end(args); -} - -// ----------------------------------------------------------------------- -// main point of entry - -void -init(void) -{ - int i; - - for (i = 0; i < arrlen(keywords); i++) - intern(&keywords[i]); - - for (i = 0; i < arrlen(directives); i++) - intern(&directives[i]); - - C.heap = mem·makearena(mem·sys, nil); - - /* compiler definitions */ - C.def.len = 0; - C.def.cap = 100; - C.def.val = calloc(C.def.cap, sizeof(*C.def.val)); - - /* compiler include paths */ - C.inc.len = 0; - C.inc.cap = 100; - C.inc.dir = calloc(C.inc.cap, sizeof(*C.inc.dir)); - C.inc.dir[C.inc.len++] = "."; - - C.outfile = nil; - - /* type info */ - C.type.len = arrlen(basetypes); - C.type.cap = 100 + arrlen(basetypes); - C.type.info = calloc(C.type.cap, sizeof(*C.type.info)); - - memcpy(C.type.info, basetypes, C.type.len * sizeof(*C.type.info)); - - /* builtins */ - C.builtin.vargs = (Decl) { - .pos = (Range) { - .beg = { - .col = 0, - .line = 0, - .path = "", - }, - .end = { - .col = 0, - .line = 0, - .path = "", - }, - }, - .kind = Dtype, - .spec = Mtype, - .type = 1, - .name = "__builtin_va_list", - }; - - intern(&C.builtin.vargs.name); -} - -void -initlx(Lexer *lx) -{ - int i; - - memset(lx, 0, sizeof(*lx)); - lx->b = lx->buf; - - /* predefine macros */ - dodefine(lx, "__LINE__"); - dodefine(lx, "__FILE__"); - lx->macline = (uintptr)lookup(&lx->sym, "__LINE__"); - lx->macfile = (uintptr)lookup(&lx->sym, "__FILE__"); - - for (i = 0; i < C.def.len; i++) - dodefine(lx, C.def.val[i]); - - lx->omit.len = 0; - lx->omit.cap = 100; - lx->omit.path = calloc(lx->omit.cap, sizeof(*C.inc.dir)); - - lx->new = lx->iostk; - lx->new->link = nil; - memset(lx->iostk, 0, sizeof(lx->iostk)); - - lx->sym = (SymTab){ 0 }; -} - -void -freelx(Lexer *lx) -{ - free(lx->omit.path); -} - -void -initp(Parser *p) -{ - /* initialize temporary buffers */ - memset(p->spstk, 0, sizeof(p->spstk)); - memset(p->nmstk, 0, sizeof(p->nmstk)); - memset(p->dtstk, 0, sizeof(p->dtstk)); - memset(p->ptstk, 0, sizeof(p->ptstk)); - - p->sp = p->spstk; - p->nm = p->nmstk; - p->dt = p->dtstk; - p->pt = p->ptstk; - - /* initialize ast */ - p->ast.cap = 0; - p->ast.len = 0; - p->ast.decls = nil; -} - -error -compile(byte *path) -{ - Lexer lx; - Parser p; - error err; - byte *sep, out[400]; - - intern(&path); - strcpy(out, path); - - sep = utf8·findrrune(out, '/'); - if (sep) - *sep++ = '\0'; - else - sep = out; - - if (!C.outfile) { - C.outfile = sep; - if (C.outfile) { - if ((sep = utf8·findrrune(C.outfile, '.'))) { - sep[0] = '.'; - sep[1] = 'o'; - sep[2] = '\0'; - } - } else { - C.outfile = "/dev/null"; - } - } - - initlx(&lx); - initp(&p); - - lx.io = openio(&lx, path); - lx.pos = (Pos){ - .path = path, - .line = 1, - .col = 1, - }; - - err = parse(&p, &lx); - freelx(&lx); - return err; -} - -error -main(int argc, byte *argv[]) -{ - byte *a, *src; - int err; - - init(); - - ARGBEGIN { - case 'o': - C.outfile = ARGF(); - break; - - case 'D': - a = ARGF(); - if (a) { - intern(&a); - if (C.def.len >= C.def.cap) { - C.def.cap += 20; - C.def.val = realloc(C.def.val, C.def.cap * sizeof(*C.def.val)); - } - C.def.val[C.def.len++] = a; - } - break; - - case 'I': - a = ARGF(); - if (a) - pushinclude(a); - break; - } ARGEND - - if (argc < 1 && C.outfile == nil) { - printf("usage: cc [-options] files\n"); - exit(1); - } - - // NOTE: This is just for my comfort during debugging. - pushinclude("/home/nolln/root/include"); - pushinclude("/home/nolln/root/include/vendor/libc"); - - src = (argc == 0) ? "" : argv[0]; - intern(&src); - - if ((err = compile(src)), err) { - exit(2); - } - - exit(0); -} diff --git a/sys/cmd/cc/cc.h b/sys/cmd/cc/cc.h deleted file mode 100644 index 8fc5f73..0000000 --- a/sys/cmd/cc/cc.h +++ /dev/null @@ -1,806 +0,0 @@ -#pragma once - -#include -#include - -#define iota(x) 1 << (x) - -/* core types */ -typedef struct Io Io; -typedef struct Pos Pos; -typedef struct Range Range; -typedef struct Token Token; - -typedef struct Lexer Lexer; - -typedef struct Sym Sym; -typedef struct Type Type; -typedef struct Scope Scope; - -typedef struct Parser Parser; - -typedef struct Ptr Ptr; -typedef struct Name Name; -typedef struct Dtor Dtor; -typedef struct Field Field; - -typedef struct Node Node; -typedef struct Decl Decl; -typedef struct Stmt Stmt; -typedef struct Expr Expr; - -typedef struct SymTab SymTab; -typedef struct StrTab StrTab; - -typedef struct Compiler Compiler; - -/* keywords of language */ -#define KEYWORDS \ - KEYWORD(Kauto,"auto") \ - KEYWORD(Kregister,"register") \ - KEYWORD(Kstatic,"static") \ - KEYWORD(Kextern,"extern") \ - KEYWORD(Ktls,"thread_local") \ - KEYWORD(Ktypedef,"typedef") \ - KEYWORD(Kinline,"inline") \ - KEYWORD(Knoret,"_Noreturn") \ - KEYWORD(Kconst,"const") \ - KEYWORD(Kvolatile,"volatile") \ - KEYWORD(Krestrict,"restrict") \ - KEYWORD(Katomic,"_Atomic") \ - KEYWORD(Ksigned,"signed") \ - KEYWORD(Kunsigned,"unsigned") \ - KEYWORD(Kvoid,"void") \ - KEYWORD(Kbool,"_Bool") \ - KEYWORD(Kchar,"char") \ - KEYWORD(Kfloat,"float") \ - KEYWORD(Kdouble,"double") \ - KEYWORD(Kcomplex,"complex") \ - KEYWORD(Kimaginary,"imaginary") \ - KEYWORD(Kint,"int") \ - KEYWORD(Kshort,"short") \ - KEYWORD(Klong,"long") \ - KEYWORD(Kstruct,"struct") \ - KEYWORD(Kunion,"union") \ - KEYWORD(Kenum,"enum") \ - KEYWORD(Kfor,"for") \ - KEYWORD(Kdo,"do") \ - KEYWORD(Kwhile,"while") \ - KEYWORD(Kcontinue,"continue") \ - KEYWORD(Kif,"if") \ - KEYWORD(Kelse,"else") \ - KEYWORD(Kswitch,"switch") \ - KEYWORD(Kcase,"case") \ - KEYWORD(Kdefault,"default") \ - KEYWORD(Kbreak,"break") \ - KEYWORD(Kgoto,"goto") \ - KEYWORD(Kreturn,"return") \ - KEYWORD(Ksizeof,"sizeof") \ - KEYWORD(Kalignof,"alignof") \ - KEYWORD(Kalignas,"alignas") - -#define KEYWORD(a, b) a, -enum { KEYWORDS NUM_KEYWORDS }; -#undef KEYWORD - -extern byte *keywords[NUM_KEYWORDS]; - -// ----------------------------------------------------------------------- -// lexing: byte stream -> tokens -// pre-processor built in - -/* source position: error reporting */ -struct Pos -{ - int col; - int line; - string path; -}; - - -struct Range -{ - Pos beg; - Pos end; -}; - -void errorat(Pos x, byte *fmt, ...); -void warnat(Pos x, byte *fmt, ...); - -/* pre-processor */ -#define DIRECTIVES \ - DIRECTIVE(Dpragma,"pragma", ppprag) \ - DIRECTIVE(Dinclude,"include", ppinc) \ - DIRECTIVE(Ddefine,"define", ppdef) \ - DIRECTIVE(Dundef,"undef", ppund) \ - DIRECTIVE(Dif,"if", ppif0) \ - DIRECTIVE(Delif,"elif", ppif1) \ - DIRECTIVE(Delse, "else", ppif1) \ - DIRECTIVE(Difdef,"ifdef", ppif2) \ - DIRECTIVE(Difndef,"ifndef", ppif3) \ - DIRECTIVE(Dendif,"endif", ppend) - -#define DIRECTIVE(a, b, c) a, -enum { DIRECTIVES NUM_DIRECTIVES }; -#undef DIRECTIVE - -extern byte *directives[NUM_DIRECTIVES]; - -error domacro(Lexer*); -error dodefine(Lexer *lx, string s); -int expandmacro(Lexer *lx, Sym *s, byte *dst); - -extern error (*macros[NUM_DIRECTIVES])(Lexer*); - -/* tokenization of byte stream */ -#define TOKENS \ - TOK(Anil,"nil") \ - TOK(Aeof,"eof") \ - TOK(Aeq, "==") \ - TOK(Aneq, "!=") \ - TOK(Anot, "!") \ - TOK(Aneg, "~") \ - TOK(Axor, "^") \ - TOK(Aor, "|") \ - TOK(Aand, "&") \ - TOK(Aoror, "||") \ - TOK(Aandand, "&&") \ - TOK(Aadd,"+") \ - TOK(Asub,"-") \ - TOK(Astar,"*") \ - TOK(Adiv,"/") \ - TOK(Amod,"%") \ - TOK(Agt,">") \ - TOK(Alt,"<") \ - TOK(Agteq,">=") \ - TOK(Alteq,"<=") \ - TOK(Alsft,"<<") \ - TOK(Arsft,">>") \ - TOK(Ainc,"++") \ - TOK(Adec,"--") \ - TOK(Aasn,"=") \ - TOK(Aorasn,"|=") \ - TOK(Axorasn,"^=") \ - TOK(Aandasn,"&=") \ - TOK(Aaddasn,"+=") \ - TOK(Asubasn,"-=") \ - TOK(Amulasn,"*=") \ - TOK(Adivasn,"/=") \ - TOK(Amodasn,"%=") \ - TOK(Alsftasn,"<<=") \ - TOK(Arsftasn,">>=") \ - TOK(Acomma,",") \ - TOK(Acolon,":") \ - TOK(Asemi,";") \ - TOK(Alparen,"(") \ - TOK(Arparen,")") \ - TOK(Albrace,"{") \ - TOK(Arbrace,"}") \ - TOK(Albrakt,"[") \ - TOK(Arbrakt,"]") \ - TOK(Adot,".") \ - TOK(Aarrow,"->") \ - TOK(Aqmark,"?") \ - TOK(Aellip,"...") \ - TOK(Alit,"") \ - TOK(Aident,"") \ - TOK(Akeywd,"") \ - -#define TOK(a, b) a, -enum -{ - TOKENS - NUM_TOKENS, - - Vchar = iota(8), - Vrune = iota(9), - Vint = iota(10), - Vlong = iota(11), - Vvlong = iota(12), - Vun = iota(13), - Vfloat = iota(14), - Vstr = iota(15), - Vwstr = iota(16), - - Vmask = Vchar - 1, -}; -#undef TOK - -extern byte *tokens[NUM_TOKENS]; - -/* TODO: store literals in a big val */ -union Val -{ - byte *s; - double f; - vlong i; - uvlong ui; - int32 c; - uint32 uc; - rune r; -}; - -struct Token -{ - uint32 kind; - Pos pos; - union Val val; -}; - -enum -{ - Svar = iota(1), - Sfunc = iota(2), - Stype = iota(3), - Stag = iota(4), - Senum = iota(5), - Slabl = iota(6), - Smacro = iota(7), -}; - -struct Sym -{ - uint32 kind; - string name; - union { - string macro; - Decl *obj; - int32 type; - Stmt *blk; - Expr *val; - }; -}; - -struct SymTab -{ - int32 n_buckets; - int32 size; - int32 n_occupied; - int32 upper_bound; - int32 *flags; - string *keys; - Sym **vals; -}; - -Sym *define(SymTab *tab, string ident, uint32 kind); -Sym *lookup(SymTab *tab, string ident); -error forget(SymTab *tab, string ident); -void forgetall(SymTab *tab); - -enum -{ - IOnil = iota(0), - IOfile = iota(1), - IObuff = iota(2), -}; - -struct Io -{ - io·Buffer rdr; - string path; - uint32 kind; - union { - Stream *f; - byte *b; - }; - - Pos store; - struct Io *link; -}; - -struct Lexer -{ - Pos pos; - SymTab sym; - byte *b; - byte buf[2*1024]; - - /* predefined dynamic macros */ - uintptr macfile; - uintptr macline; - - /* i/o data */ - Io *io, *new; - Io iostk[100]; - struct { - int cap; - int len; - string *path; - } omit; -}; - -/* lex.c functions */ -Token lex(Lexer *); - -int getbyte(Lexer *); -int getnsbyte(Lexer *l); -rune getrune(Lexer *); -byte ungetbyte(Lexer *); -rune ungetrune(Lexer *, rune r); - -Io* openio(Lexer *lx, byte *path); -void pushio(Lexer *lx, Io *new); -void popio(Lexer *lx); - -void puttok(Token); - -// ----------------------------------------------------------------------- -// parsing & type resolution -// tokens -> ast - -/* parent data */ -struct Node -{ - Range pos; - uint32 kind; -}; - -/* ast types */ -enum -{ - Nbad, - /* labels */ - Sempty, Slabel, Scase, - Sblock, - Sexpr, Sdecl, - Sselect, - /* loops */ - Sfor, Swhile, Sdo, - /* jumps */ - Sgoto, Scontin, Sbreak, Sreturn, - /* forks */ - Sif, Sswitch, - - - /* assignments */ - Xasn, Xmulasn, Xdivasn, Xmodasn, Xsubasn, Xaddasn, - Xlsftasn, Xrsftasn, Xandasn, Xxorasn, Xorasn, - /* conditional */ - Xternary, - /* unary prefix ops */ - Xref, Xstar, Xplus, Xminus, Xneg, Xnot, Xsizeof, Xalignof, Xpreinc, Xpredec, - Xcast, - /* unary postfix ops */ - Xpostinc, Xpostdec, Xindex, Xcall, Xselp, Xself, Xinitlist, - /* binary ops */ - Xoror, Xandand, Xor, Xxor, Xand, Xneq, Xeql, Xgt, Xlt, Xgteq, Xlteq, Xlsft, Xrsft, - Xadd, Xsub, Xmul, Xdiv, Xmod, - /* primary */ - Xparen, Xident, Xlit, - /* lists */ - Xcomma, - - - Dvar, - Dfunc, - Dtype, - Dlist = iota(20), - Dvars = Dvar | Dlist, - Dtypes = Dtype | Dlist, - - /* names (don't interact w/ final AST) */ - Nnil = 0, - Nident, - Nparen, - Nindex, - Ncall, -}; - -/* expressions */ -enum -{ - Keynil, - Keyidx, - Keysel, -}; - -struct Key -{ - uint kind : 2; - union { - Expr *x; - string s; - }; -}; - -struct Expr -{ - struct Node; - uint32 qual; - uint32 type; - union { - string name; - struct { - uint64 kind; - union { - union Val; - union Val v; - }; - } val; - struct { - int n; - struct Key *k; - Expr *v; - } init; - Expr *x; - struct { - Expr *l; - Expr *r; - } asn; - struct { - Expr *c; - Expr *t; - Expr *e; - } cond; - struct { - Expr *x; - union { - Expr *i; - string f; - }; - } idx; - struct { - Expr *fn; - int n; - Expr **arg; - } call; - union { - Expr *pre; - Expr *post; - } unary; - struct { - int type : 1; - union { - struct { - uint32 qual; - uint32 type; - } of; - Expr *x; - }; - } info; - struct { - struct { - uint32 qual; - uint32 type; - } to; - Expr *x; - } cast; - struct { - Expr *l; - Expr *r; - } binary; - struct { - Expr *x[2]; - } comma; - }; -}; - - -/* statements */ -struct Stmt -{ - struct Node; - union { - struct { - union { - string ident; - Expr *x; - }; - Node *stmt; - } lbl; - struct { - long n; - struct Node **item; - } blk; - Expr *x; - struct { - Node *init; - Expr *cond; - Expr *step; - Node *body; - } loop; - union{ - string lbl; - Expr *x; - } jmp; - struct { - Expr *cond; - Node *body; - Node *orelse; - } br; - }; -}; - -/* declarations */ - -/* - * specifiers - * the design is the following: - * type info is held w/in a 64 bit integer. - * the bottom 32 bits are associated to specializations - * the top 32 bits index into a type-info array held by the compiler. - */ -enum -{ - /* memory */ - Mauto = iota(Kauto), - Mstatic = iota(Kstatic), - Mreg = iota(Kregister), - Mtls = iota(Ktls), - Mtype = iota(Ktypedef), - Mextern = iota(Kextern), - - MaskMem = Mauto | Mstatic | Mreg | Mtls | Mtype | Mextern, - - /* qualifiers */ - Qconst = iota(Kconst), - Qrestr = iota(Krestrict), - Qvoltl = iota(Kvolatile), - Qatom = iota(Katomic), - - MaskQul = Qconst | Qrestr | Qvoltl | Qatom, - - Finlne = iota(Kinline), - Fnoret = iota(Knoret), - - MaskFcn = Finlne | Fnoret, - - /* types */ - Tsign = iota(Ksigned), - Tunsign = iota(Kunsigned), - - MaskSgn = Tsign | Tunsign, - - Tvoid = iota(Kvoid), - Tfloat = iota(Kfloat), - Tdouble = iota(Kdouble), - Tcmplx = iota(Kcomplex), - Timag = iota(Kimaginary), - - MaskFlt = Tfloat | Tdouble | Tcmplx | Timag, - - Tchar = iota(Kchar), - Tbool = iota(Kbool), - - Tshort = iota(Kshort), - Tint = iota(Kint), - Tlong = iota(Klong), - Tvlong = iota(Klong+1), - - MaskInt = Tshort | Tint | Tlong | Tvlong, - MaskTyp = Tvoid | Tbool | Tchar | Tint | Tfloat | Timag | Tcmplx, - /* - * NOTE IMPORTANT: vlong takes over the struct bit place - * DON'T MOVE KEYWORDS WITHOUT REORGANIZING - */ - Tstruct = iota(Kstruct+1), - Tunion = iota(Kunion+1), - Tenum = iota(Kenum+1), - Tname = iota(Kenum+2), - - Sbad = -1, -}; - -/* intermediate nodes */ -struct Ptr -{ - uint64 kind; - Ptr *link; -}; - -struct Name -{ - uint32 kind; - union { - string ident; - struct Dtor *paren; - struct { - Name *name; - union { - struct { - uint32 q; - Expr *x; - } idx; - struct { - int n; - int dots : 1; - Field *arg; - } call; - }; - } sfx; - }; -}; - -struct Dtor -{ - Ptr ptr; - Name *name; -}; - -/* final ast node */ - -struct Field -{ - uint32 qual; - uint32 type; - string name; -}; - -struct Decls -{ - string name; - uint32 type; - Expr *init; - struct Decls *link; -}; - - -struct Decl -{ - struct Node; - uint32 spec; - union { - struct { - string name; - uint32 type; - union { - Stmt *body; - Expr *init; - }; - }; - struct Decls list; - }; -}; - -enum -{ - Tbad, - Tbase, - Tdef, - Tptr, - Tarray, - Tfunc, -}; - -/* types */ -struct Type -{ - uint32 kind; - Sym *sym; - uintptr size; - uintptr max; - uint16 align : 8; - uint8 sign : 2; - union { - struct { - uint32 qual; - uint32 base; - } ptr; - struct { - int len; - uint32 qual; - uint32 *elt; - } arr; - struct { - int len; - Field *f; - Expr *x; - } aggr; - struct { - int len; - string *elt; - Expr *val; - } enm; - struct { - uint32 ret; - int n; - int dots : 1; - Field *arg; - } func; - }; -}; - -/* platform specific */ -extern Type pointer; -extern Type basetypes[24]; -/* mandated by C standard */ -extern uint64 validtypespec[38]; -extern int indextypespec[38]; - -struct Scope -{ - SymTab tags; - SymTab objs; -}; - -struct Parser -{ - Token tok[2]; - struct { - int cap; - int len; - Decl **decls; - } ast; - - /* static buffers/stacks */ - Scope *sp; - Scope spstk[40]; - - Name *nm; - Name nmstk[40]; - - Ptr *pt; - Ptr ptstk[10]; - - Dtor *dt; - Dtor dtstk[40]; -}; - -/* ast.c functions */ -error parse(Parser *, Lexer *); - -// ----------------------------------------------------------------------- -// global compiler data - -struct StrTab -{ - int32 n_buckets; - int32 size; - int32 n_occupied; - int32 upper_bound; - int32 *flags; - string *keys; - int32 *vals; -}; - -#if 0 -struct TypeSet -{ - int32 n_buckets; - int32 size; - int32 n_occupied; - int32 upper_bound; - int32 *flags; - Type **keys; -}; -#endif - -/* main data */ -struct Compiler -{ - mem·Arena *heap; - StrTab strs; - string outfile; - - struct { - int cap; - int len; - string *val; - } def; - - struct { - int cap; - int len; - string *dir; - } inc; - - struct { - int cap; - int len; - Type *info; - } type; - - /* TODO: make array */ - struct { - Decl vargs; - } builtin; -}; - -extern Compiler C; - -/* cc.c functions */ -void init(); -int32 intern(byte **str); -int32 type(); - -#undef iota diff --git a/sys/cmd/cc/lex.c b/sys/cmd/cc/lex.c deleted file mode 100644 index 33fc5d0..0000000 --- a/sys/cmd/cc/lex.c +++ /dev/null @@ -1,873 +0,0 @@ -#include "cc.h" -#include - -// ----------------------------------------------------------------------- -// printing functions - -void -puttok(Token tok) -{ - if (tok.kind < Alit) - printf("%s", tokens[tok.kind]); - else if (tok.kind & Alit) { - if (tok.kind & Vchar) - if (tok.kind & Vint) - if (tok.kind & Vlong) - if (tok.kind & Vvlong) - printf("literal <%lld>", tok.val.i); - if (tok.kind & Vfloat) - printf("literal <%f>", tok.val.f); - printf("literal <%s>", tok.val.s); - } else - printf("ident <%s>", tok.val.s); -} - -// ----------------------------------------------------------------------- -// io buffer management - -#define asrdr(x) (io·Reader){(int (*)(void *, int, int, void *))x} - -// path should be absolute -Io* -openio(Lexer *lx, byte *path) -{ - string *it, *end; - - intern(&path); - - // See if we have already opened file; - // If so, and it hasn't been flagged return it - for (it = lx->omit.path, end = it + lx->omit.len; it < end; ++it) { - if ((uintptr)(*it) == (uintptr)(path)) - return nil; - } - - // TODO: See if we have already loaded the file - - if ((lx->new - lx->iostk) >= arrlen(lx->iostk)-1) - panicf("out of I/O space!"); - - lx->new->f = io·open(path, "r"); - if (!lx->new->f) - panicf("file %s not found", path); - - lx->new->kind = IOfile; - lx->new->path = path; - bufio·initreader(&lx->new->rdr, asrdr(io·read), lx->new->f); - - return lx->new++; -} - -static -Io* -makeio(Lexer *lx, byte *name) -{ - if ((lx->new - lx->iostk) >= arrlen(lx->iostk)-1) - panicf("out of I/O space!"); - - lx->new->path = name; - lx->new->rdr = (io·Buffer) { - .state = bufio·rdr | bufio·end, - .runesize = 0, - .h = nil, - .size = bufio·size, - .beg = lx->new->rdr.buf + bufio·ungets, - .pos = lx->new->rdr.buf + bufio·ungets, - .end = lx->new->rdr.buf + bufio·ungets, - }; - lx->new->b = lx->new->rdr.beg; - - return lx->new++; -} -#undef asrdr - -static -void -freeio(Lexer *lx, Io *io) -{ - if (io->kind & IOfile) { - io·close(io->f); - } - - io->rdr.state = 0; - io->kind = 0; - io->link = nil; - io->path = nil; - io->store = (Pos){ 0 }; - io->path = ""; -} - -void -pushio(Lexer *lx, Io *new) -{ - new->link = lx->io; - lx->io->store = lx->pos; - lx->io = new; - - lx->pos = (Pos){ - .line = 1, - .col = 1, - .path = new->path, - }; -} - -void -popio(Lexer *lx) -{ - Io *prev; - - assert(lx->io == lx->new-1); - --lx->new; - - prev = lx->io->link; - freeio(lx, lx->io); - - lx->io = prev; - if (!prev) { - return; - } - - lx->pos = prev->store; -} - -// ----------------------------------------------------------------------- -// simple wrappers - -int -getbyte(Lexer *lx) -{ - return bufio·getbyte(&lx->io->rdr); -} - -int -getnsbyte(Lexer *lx) -{ - int b; - b = getbyte(lx); - for (;;) { - if (b == EOF) { - if (lx->io->link) { - popio(lx); - assert(lx->io); - b = getbyte(lx); - continue; - } else - return b; - } - if (b >= RuneSelf || !isspace(b)) - return b; - if (b == '\n') - return b; - b = getbyte(lx); - } - return b; -} - -rune -getrune(Lexer *lx) -{ - return bufio·getrune(&lx->io->rdr); -} - -byte -ungetbyte(Lexer *lx) -{ - byte b; - return bufio·ungetbyte(&lx->io->rdr, b); -} - -rune -ungetrune(Lexer *l, rune r) -{ - return bufio·ungetrune(&l->io->rdr, r); -} - -// ----------------------------------------------------------------------- -// main lexer - -#define TOK(a, b) b, -byte *tokens[NUM_TOKENS] = { TOKENS }; -#undef TOK - -static uint8 Atoi[256] = -{ - ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, ['5'] = 5, - ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9, ['a'] = 10, ['A'] = 10, - ['b'] = 11, ['B'] = 11, ['c'] = 12, ['C'] = 12, ['d'] = 13, ['D'] = 13, - ['e'] = 14, ['E'] = 14, ['f'] = 15, ['F'] = 15, -}; - -static -error -escapechar(Lexer *lx, int x, int islong, int esc, vlong *val) -{ - int i, u, c; - vlong l; - - c = getrune(lx); - - switch (c) { - case '\\': - break; - case EOF: - errorat(lx->pos, "EOF in string"); - return 1; - case '\n': - errorat(lx->pos, "newline in string"); - return 1; - default: - if (c == x) - return 1; - *val = c; - return 0; - } - - u = 0; - c = getrune(lx); - - switch(c) { - case 'x': - i = islong ? 4 : 2; - goto hex; - - case 'u': - i = islong ? 8 : 4; - u = 1; - goto hex; - - case 'U': - i = 8; - u = 1; - goto hex; - - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - i = islong ? 4 : 2; - goto oct; - - case 'a': c = '\a'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case '\\':c = '\\'; break; - - default: - if(c != x) errorat(lx->pos, "unknown escape sequence: %c", c); - } - *val = c; - return 0; - -hex: - l = 0; - for(; i > 0; i--) { - c = getbyte(lx); - if (c >= '0' && c <= '9') { - l = l*16 + c-'0'; - continue; - } - if (c >= 'a' && c <= 'f') { - l = l*16 + c-'a' + 10; - continue; - } - if (c >= 'A' && c <= 'F') { - l = l*16 + c-'A' + 10; - continue; - } - ungetbyte(lx); - break; - } - if (u && (l > RuneMax || (0xd800 <= l && l < 0xe000))) { - errorat(lx->pos, "invalid unicode code point in escape sequence: %#llx", l); - l = RuneErr; - } - *val = l; - if (esc) - *val |= RuneMask + 1; - return 0; - -oct: - l = c - '0'; - for (; i > 0; i--) { - c = getbyte(lx); - if (c >= '0' && c <= '7') { - l = l*8 + c-'0'; - continue; - } - ungetbyte(lx); - break; - } - if (l > 255) errorat(lx->pos, "octal escape value > 255: %d", l); - - *val = l; - if (esc) - *val |= RuneMask + 1; - return 0; -} - -#define CASE1(stmt1, kind1) \ - case stmt1: \ - tok.kind = kind1; \ - goto Return - -#define CASE2(stmt1, kind1, b1, kind2) \ - case stmt1: \ - tok.kind = kind1; \ - b = getbyte(lx); \ - if (b == b1) \ - tok.kind = kind2; \ - else \ - ungetbyte(lx); \ - goto Return - -#define CASE3(stmt1, kind1, b1, kind2, b2, kind3) \ - case stmt1: \ - tok.kind = kind1; \ - b = getbyte(lx); \ - if (b == b1) \ - tok.kind = kind2; \ - else if (b == b2) \ - tok.kind = kind3; \ - else \ - ungetbyte(lx); \ - goto Return - -#define CASE4(stmt1, kind1, b1, kind2, b2, kind3, b3, type4) \ - case stmt1: \ - tok.kind = kind1; \ - b = getbyte(lx); \ - if (b == b1) \ - tok.kind = kind2; \ - else if (b == b2) \ - tok.kind = kind3; \ - else if (b == b3) \ - tok.kind = type4; \ - else \ - ungetbyte(lx); \ - goto Return - - -Token -lex(Lexer *lx) -{ - int b, n, f; - vlong v, _; - rune r; - string s; - double d; - byte *e; - Token tok; - Sym *sym; - Io *io; - -GetByte: - b = getbyte(lx); -Dispatch: - tok.pos = lx->pos; - - if ((b != EOF && b >= RuneSelf) || b == '_') - goto Talpha; - if (isalpha(b)) { - if (b != 'L') - goto Talpha; - - n = b; - b = getbyte(lx); - if (b == '\'') { - if (escapechar(lx, '\'', 1, 0, &v)) - b = '\''; - if (!escapechar(lx, '\'', 1, 0, &_)) { - errorat(lx->pos, "missing ' at end of character constant"); - } - tok.kind = Alit | Vrune; - tok.val.r = v; - goto Return; - } - if (b == '"') - goto TLstr; - ungetbyte(lx); - b = n; - - goto Talpha; - } - if (isdigit(b)) - goto Tnum; - - switch (b) { - case '\n': - lx->pos.line++; - case ' ': case '\r': case '\t': case '\v': case '\f': - while (b = getbyte(lx), isspace(b)) - if (b == '\n') - lx->pos.line++; - goto Dispatch; - - case '\\': - b = getbyte(lx); - if (b != '\n') - errorat(lx->pos, "'\\' without a trailing newline"); - goto GetByte; - - Tchar: - case '\'': - if (escapechar(lx, '\'', 0, 0, &v)) { - errorat(lx->pos, "empty literal or escaped ' in char literal"); - v = '\''; - } - if (!escapechar(lx, '\'', 0, 0, &_)) { - errorat(lx->pos, "missing '"); - ungetbyte(lx); - } - - if (v > 0xff) { - errorat(lx->pos, "overflowed character literal"); - v = 0; - } - tok.kind = Alit | Vchar; - tok.val.c = v; - goto Return; - - case '"': - s = str·makecap("", 0, 8); - for (;;) { - if (escapechar(lx, '"', 0, 1, &v)) - break; - - if (v & (RuneMask + 1)) - str·appendbyte(&s, v); - else { - r = v; - b = utf8·runelen(r); - utf8·runetobyte(lx->buf, &r); - str·appendlen(&s, b, lx->buf); - } - } - tok.kind = Alit | Vstr; - tok.val.s = s; - intern(&tok.val.s); - - str·free(s); - goto Return; - - TLstr: - s = str·makecap("", 0, 8); - // NOTE: this violates strict aliasing - for (;;) { - if (escapechar(lx, '"', 1, 0, &v)) - break; - str·appendlen(&s, sizeof(wchar_t), (byte*)&v); - } - tok.kind = Alit | Vwstr; - tok.val.s = s; - intern(&tok.val.s); - - str·free(s); - goto Return; - - case '.': - tok.kind = Adot; - b = getbyte(lx); - - if (isdigit(b)) { - // *lx->b++ = b; - goto Tflt; - } else if (b == '.') { - b = getbyte(lx); - if (b != '.') { - errorat(lx->pos, "invalid token '..'"); - tok.kind = Aellip; - break; - } - } - ungetbyte(lx); - goto Return; - - case '<': - tok.kind = Alt; - b = getbyte(lx); - - if (b == '<') { - tok.kind = Alsft; - b = getbyte(lx); - if (b == '=') - tok.kind = Alsftasn; - else - ungetbyte(lx); - } else if (b == '=') - tok.kind = Alteq; - else - ungetbyte(lx); - goto Return; - - case '>': - tok.kind = Agt; - b = getbyte(lx); - - if (b == '>') { - tok.kind = Arsft; - b = getbyte(lx); - if (b == '=') - tok.kind = Arsftasn; - else - ungetbyte(lx); - } else if (b == '=') - tok.kind = Agteq; - else - ungetbyte(lx); - goto Return; - - case '/': - tok.kind = Adiv; - b = getbyte(lx); - - if (b == '=') - tok.kind = Adivasn; - else if (b == '/') { - while (b != EOF && b != '\n') - b = getbyte(lx); - goto Dispatch; - } else if (b == '*') { - int level = 1; - b = getbyte(lx); - while (b != EOF && level > 0) { - if (b == '/') { - b = getbyte(lx); - if (b == '*') - level++; - } else if (b == '*') { - b = getbyte(lx); - if (b == '/') - level--; - } - if (b == '\n') - lx->pos.line++; - b = getbyte(lx); - } - goto Dispatch; - } else - ungetbyte(lx); - goto Return; - - case '#': - if (domacro(lx)) { - tok.kind = Anil; - errorat(lx->pos, "failed to perform preprocessor directive"); - return tok; - } - goto GetByte; - - case EOF: - popio(lx); - if (lx->io) - goto GetByte; - tok.kind = Aeof; - goto Return; - - CASE1('(', Alparen); - CASE1(')', Arparen); - CASE1('{', Albrace); - CASE1('}', Arbrace); - CASE1('[', Albrakt); - CASE1(']', Arbrakt); - CASE1(',', Acomma); - CASE1('?', Aqmark); - CASE1(';', Asemi); - CASE1('~', Aneg); - CASE1(':', Acolon); - CASE2('^', Axor, '=', Axorasn); - CASE2('!', Anot, '=', Aneq); - CASE2('*', Astar,'=', Amulasn); - CASE2('=', Aasn, '=', Aeq); - CASE2('%', Amod, '=', Amodasn); - CASE3('+', Aadd, '=', Aaddasn, '+', Ainc); - CASE3('&', Aand, '=', Aandasn, '&', Aandand); - CASE3('|', Aor, '=', Aorasn, '|', Aoror); - CASE4('-', Asub, '=', Asubasn, '-', Adec, '>', Aarrow); - - Tnum: - e = lx->buf + arrlen(lx->buf); - do { - if (lx->b >= e) { - errorat(lx->pos, "number overflows lexer buffer"); - goto Nospace; - } - *lx->b++ = b; - } while (b = getbyte(lx), isdigit(b) || b == '_'); - - if (b == '.' || tolower(b) == 'e') - goto Tflt; - Tint: - n = 10; - s = lx->buf; - if (*s == '0') { - switch (b) { - case 'x': n = 16; break; - case 'b': n = 2; break; - case 'o': n = 8; break; - default: goto Rint; - } - lx->b = s; - /* reparse number, now with base info */ - while (b = getbyte(lx), (isdigit(b) || - ('a' <= b && b <= 'f') || - ('A' <= b && b <= 'F') || - b == '_')) - *lx->b++ = b; - } - Rint: - v = 0; - r = b; - for (; s != lx->b ; s++) { - b = *s; - if (b == '_') continue; - - f = Atoi[b]; - if (f == 0 && b != '0') - break; - - if (f >= n) { - errorat(lx->pos, "digit '%c' out of range for base %d", b, n); - f = 0; - } - - if (v > (UINT64_MAX - f) / n) { - errorat(lx->pos, "integer literal overflow"); - v = 0; - break; - } - - v = v * n + f; - } - - b = r; - tok.kind = Alit; - tok.val.i = v; - - if (b == 'u' || b == 'U') { - tok.kind |= Vun; - b = getbyte(lx); - } - if (b == 'l' || b == 'L') { - r = getbyte(lx); - if (r == 'l' || r == 'L') { - if (r != b) - errorat(lx->pos, "mismatched case on long long integer suffix"); - tok.kind |= Vvlong; - r = getbyte(lx); - } else - tok.kind |= Vlong; - - if (r == 'u' || r == 'U') { - if (tok.kind & Vun) - errorat(lx->pos, "multiple unsigned designators on integer suffix"); - tok.kind |= Vun; - goto Return; - } - - ungetbyte(lx); - goto Return; - } - - tok.kind |= Vint; - ungetbyte(lx); - goto Return; - - Tflt: - if (b == '.') { - *lx->b++ = b; - b = getbyte(lx); - } - - while (isdigit(b)) { - *lx->b++ = b; - - if (lx->b >= e) { - errorat(lx->pos, "number overflows lexer buffer"); - goto Nospace; - } - } - - if (tolower(b) == 'e') { - b = getbyte(lx); - if (b == '-' || b == '+') - b = getbyte(lx); - - if (!isdigit(b)) - errorat(lx->pos, "expected number after exponent, found %c", b); - - do { - *lx->b++ = b; - } while (b = getbyte(lx), isdigit(b)); - } - *lx->b = '\0'; - d = strtod(lx->buf, nil); - ungetbyte(lx); - - tok.kind = Alit | Vfloat; - tok.val.f = d; - - goto Return; - - Talpha: - s = lx->buf; - e = lx->buf + arrlen(lx->buf); - for (;;) { - if (s >= e) { - errorat(lx->pos, "identifier too long for buffer: %s", s); - goto Nospace; - } - if (b != EOF && b >= RuneSelf) { - ungetbyte(lx); - r = getrune(lx); - if (!utf8·isletter(r) && !utf8·isdigit(r) && r != 0xb7) { - errorat(lx->pos, "invalid identifier character %d", r); - } - s += utf8·runetobyte(s, &r); - } else if (!isalnum(b) && b != '_') - break; - else - *s++ = b; - b = getbyte(lx); - } - *s = '\0'; - ungetbyte(lx); - - tok.kind = Aident; - tok.val.s = lx->buf; - - n = intern(&tok.val.s); - if (n < arrlen(keywords)) { - tok.kind = Akeywd; - tok.val.i = n; - goto Return; - } - - sym = lookup(&lx->sym, tok.val.s); - if (sym && ((uintptr)sym->name != (uintptr)lx->io->path)) { - if ((uintptr)sym == lx->macline) { - tok.kind = Alit | Vint; - tok.val.i = lx->pos.line; - goto Return; - } - if ((uintptr)sym == lx->macfile) { - tok.kind = Alit | Vstr; - tok.val.s = lx->pos.path; - goto Return; - } - io = makeio(lx, sym->name); - io->rdr.end += expandmacro(lx, sym, io->b); - printf("EXPANDED %s: %s\n", sym->name, io->rdr.beg); - *io->rdr.end++ = EOF; - pushio(lx, io); - goto GetByte; - } - goto Return; - - default: - tok.kind = Anil; - errorat(lx->pos, "invalid token, crashing"); - abort(); - } - -Return: - lx->b = lx->buf; - return tok; - -Nospace: - panicf("aborting compilation"); - exit(1); -} - -#undef CASE4 -#undef CASE3 -#undef CASE2 -#undef CASE1 - -// ----------------------------------------------------------------------- -// symbol tables - -#define PTR_HASH(p) (uintptr)(p) -#define PTR_EQUAL(p1, p2) ((uintptr)(p1) == (uintptr)(p2)) - -static -void -·free(void* _, void* ptr) { - return free(ptr); -} - -static -void * -·alloc(void* _, uint n, ulong size) { - return malloc(n*size); -} - -static -void * -·calloc(void* _, uint n, ulong size) { - return calloc(n, size); -} - -static -int -moresymtab(SymTab *tab, int n) -{ - MAP_GROW(tab, string, Sym*, n, PTR_HASH, sys·Memory, nil); -} - -static -int -putsym(SymTab *tab, Sym *sym, error *err) -{ - MAP_PUT(tab, sym->name, sym, PTR_HASH, PTR_EQUAL, moresymtab, err); -} - -Sym* -define(SymTab *tab, string name, uint32 kind) -{ - int i; - Sym *sym; - error err; - - sym = mem·arenaalloc(C.heap, 1, sizeof(*sym)); - sym->name = name; - sym->kind = kind; - - i = putsym(tab, sym, &err); - tab->vals[i] = sym; - - return sym; -} - -Sym* -lookup(SymTab *tab, string ident) -{ - int idx; - MAP_GET(idx, tab, ident, PTR_HASH, PTR_EQUAL); - - if (idx < tab->n_buckets) - return tab->vals[idx]; - - return nil; -} - - -error -forget(SymTab *tab, string ident) -{ - int idx; - MAP_GET(idx, tab, ident, PTR_HASH, PTR_EQUAL); - - if (idx < tab->n_buckets) { - MAP_DEL(tab, idx); - return 0; - } - return 1; -} - -void -forgetall(SymTab *tab) -{ - MAP_RESET(tab); -} diff --git a/sys/cmd/cc/pp.c b/sys/cmd/cc/pp.c deleted file mode 100644 index 57c3501..0000000 --- a/sys/cmd/cc/pp.c +++ /dev/null @@ -1,1125 +0,0 @@ -#include "cc.h" - -// ----------------------------------------------------------------------- -// helper functions - -static -void -pushomit(Lexer *lx, string omit) -{ - if (lx->omit.len == lx->omit.cap) { - lx->omit.cap += 20; - lx->omit.path = realloc(lx->omit.path, lx->omit.cap*sizeof(*lx->omit.path)); - } - lx->omit.path[lx->omit.len++] = omit; -} - -// NOTE: The iterator of lexer lx->b IS NOT reset. -// Its the caller's responsibility. -static -string -ident(Lexer *lx) -{ - int b; - byte *s; - - b = getnsbyte(lx); - if (!isalpha(b) && b != '_' && b < RuneSelf) { - ungetbyte(lx); - return ""; - } - - s = lx->b; - for (;;) { - *lx->b++ = b; - b = getbyte(lx); - if (isalnum(b) || b == '_' || b >= RuneSelf) - continue; - ungetbyte(lx); - break; - } - *lx->b++ = '\0'; - - return s; -} - -static -string -identdots(Lexer *lx, int *dots) -{ - int c; - byte *s; - - s = ident(lx); - if (*s != '\0') - return s; - - c = getnsbyte(lx); - if (c != '.') { - ungetbyte(lx); - return s; - } - - if (getbyte(lx) != '.' || getbyte(lx) != '.') - errorat(lx->pos, "incorrect '...' token in macro"); - - *dots = 1; - // TODO: should only run intern once... - s = "__VA_ARGS__"; - intern(&s); - return s; -} - -static -Sym* -defmacro(Lexer *lx, string name, string macro) -{ - Sym *mac; - - // printf("DEFINING MACRO %s ON LINE %d, file %s\n", name, lx->pos.line, os·basename(lx->pos.path)); - mac = define(&lx->sym, name, Smacro); - mac->macro = macro; - - return mac; -} - -static vlong evalmacro(Lexer *lx, byte prec); - -static -vlong -opand(Lexer *lx) -{ - int b; - vlong v; - string s; - Token tok; - Sym *sym; - - b = getnsbyte(lx); - if (b == '\n') { - errorat(lx->pos, "new line in macro expression"); - return 0; - } - ungetbyte(lx); - - tok = lex(lx); - - switch (tok.kind & Vmask) { - case Aneg: - return ~opand(lx); - - case Anot: - return !opand(lx); - - case Alparen: - v = evalmacro(lx, 1); - tok = lex(lx); - if (!(tok.kind & Arparen)) { - errorat(lx->pos, "unbalanced parenthesis in macro expression"); - return 0; - } - return v; - - case Alit: - switch (tok.kind & ~Vmask) { - case Vint: case Vlong: case Vvlong: - return tok.val.i; - case Vun|Vint : case Vun|Vlong : case Vun|Vvlong: - return tok.val.ui; - case Vrune: - return tok.val.r; - case Vchar: - return tok.val.c; - default: - errorat(lx->pos, "invalid literal of type '%s' in conditional macro", tokens[tok.kind & ~Vmask]); - return 0; - } - - case Aident: - sym = lookup(&lx->sym, tok.val.s); - if (!sym) { - /* calling lex directly would expand the operand here - * manually lex the result - */ - if (strcmp(tok.val.s, "defined") == 0) { - b = getnsbyte(lx); - if (b == '\n') { - errorat(lx->pos, "new line in defined operand"); - return 0; - } - s = lx->buf; - if (b == '(') { - b = getnsbyte(lx); - while (b != ')') { - if (b == '\n') { - errorat(lx->pos, "new line inside defined operand"); - return 0; - } - if (b == '(') { - errorat(lx->pos, "nested parens not allowed inside defined operator"); - return 0; - } - if (!isspace(b)) - *s++ = b; - b = getbyte(lx); - } - } else { - while (!isspace(b)) { - *s++ = b; - b = getbyte(lx); - - if (b == '\n') { - errorat(lx->pos, "new line inside defined operand"); - return 0; - } - } - } - *s = '\0'; - s = lx->buf; - intern(&s); - return lookup(&lx->sym, s) != nil; - } - return 0; - } - panicf("unreachable"); - return 1; - - default: - errorat(lx->pos, "opand: invalid token found in macro conditional: '%s'", tokens[tok.kind & Vmask]); - return 0; - } -} - -// recursively evaluates a macro -// reduced set of operators allowed here -static -vlong -evalmacro(Lexer *lx, byte prec) -{ - int b; - vlong l, r; - Token tok; - - l = opand(lx); - for (;;) { - b = getnsbyte(lx); - // NOTE: Either this or we pass in what are stopping byte is - // New line should always stop us... - // Is there any case where we SHOULDN'T STOP ON ')'? - if (b == '\n' || b == ')') { - ungetbyte(lx); - break; - } - ungetbyte(lx); - - tok = lex(lx); - // simplified jump table of precedence - // unpacked to evaluate inline - switch (tok.kind & Vmask) { - case Astar: - if (prec > 10) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 10 + 1); - l = l * r; - continue; - - case Adiv: - if (prec > 10) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 10 + 1); - l = l / r; - continue; - - case Amod: - if (prec > 10) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 10 + 1); - l = l % r; - continue; - - case Aadd: - if (prec > 9) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 9 + 1); - l = l + r; - continue; - - case Asub: - if (prec > 9) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 9 + 1); - l = l - r; - continue; - - case Alsft: - if (prec > 8) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 8 + 1); - l = l << r; - continue; - - case Arsft: - if (prec > 8) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 8 + 1); - l = l >> r; - continue; - - case Alt: - if (prec > 7) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 7 + 1); - l = l < r; - continue; - - case Agt: - if (prec > 7) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 7 + 1); - l = l > r; - continue; - - case Agteq: - if (prec > 7) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 7 + 1); - l = l >= r; - continue; - - case Alteq: - if (prec > 7) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 7 + 1); - l = l >= r; - continue; - - case Aeq: - if (prec > 6) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 6 + 1); - l = l == r; - continue; - - case Aneq: - if (prec > 6) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 6 + 1); - l = l != r; - continue; - - case Aand: - if (prec > 5) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 5 + 1); - l = l & r; - continue; - - case Axor: - if (prec > 4) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 4 + 1); - l = l ^ r; - continue; - - case Aor: - if (prec > 3) { - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 3 + 1); - l = l | r; - continue; - - case Aandand: - if (prec > 2) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 2 + 1); - l = l && r; - continue; - - case Aoror: - if (prec > 1) { - ungetbyte(lx); - ungetbyte(lx); - return l; - } - r = evalmacro(lx, 1 + 1); - l = l || r; - continue; - - default: - errorat(lx->pos, "eval: invalid token found in macro conditional '%s'", tokens[tok.kind & Vmask]); - abort(); - return 0; - } - } - - return l; -} - -// ----------------------------------------------------------------------- -// preprocessor magic numbers - -enum -{ - PPbeg = 0x02, - PParg = 0x03, - PPcat = 0x04, - PPstr = 0x05, - - PPnarg = 30, -}; - -#define PPvar 0x80u - -// ----------------------------------------------------------------------- -// preprocessor functions - -/* #endif */ -static -error -ppend(Lexer *lx) -{ - int b; - do { - b = getnsbyte(lx); - } while (b > 0 && b != '\n'); - - if (b == '\n') - lx->pos.line++; - - return 0; -} - - -/* #undef */ -static -error -ppund(Lexer *lx) -{ - string s; - error err; - - s = ident(lx); - intern(&s); - lx->b = lx->buf; - - err = forget(&lx->sym, s); - if (err) - warnat(lx->pos, "attempting to undefine unrecognized symbol '%s'", s); - - ppend(lx); - return 0; -} - -/* #define */ -static -error -ppdef(Lexer *lx) -{ - int b; - Sym *sym; - int i, j, n, dot; - string s, a, base, end, buf, args[PPnarg]; - - s = ident(lx); - if (!s) { - errorat(lx->pos, "failed to parse defined identifer"); - goto Bad; - } - intern(&s); - printf("DEFINING %s\n", s); - lx->b = lx->buf; - - sym = lookup(&lx->sym, s); - if (sym) - warnat(lx->pos, "macro redefined: '%s'", sym->name); - - n = 0; - dot = 0; - b = getbyte(lx); - if (b == '(') { - b = getnsbyte(lx); - if (b != ')') { - ungetbyte(lx); - for (;;) { - // NOTE: This is a pointer into the lx->buffer. - // Can't reset lx->b while we hold the args! - a = identdots(lx, &dot); - if (a == nil) { - errorat(lx->pos, "macro syntax error: improper argument"); - goto Bad; - } - if (n >= PPnarg) { - errorat(lx->pos, "macro syntax error: too many arguments: %d > %d", n, PPnarg); - goto Bad; - } - - args[n++] = a; - b = getnsbyte(lx); - - if (b == ')') - break; - if (b != ',') { - errorat(lx->pos, "macro syntax error: bad token in argument '%b'", b); - goto Bad; - } - } - } - b = getbyte(lx); - } - - if (isspace(b)) - if (b != '\n') - b = getnsbyte(lx); - - base = lx->b; - end = lx->buf + arrlen(lx->buf); - if (base >= end) { - errorat(lx->pos, "out of macro buffer space!"); - goto Bad; - } - buf = str·makef("%c%c", n, PPbeg); - for (;;) { - if (isalpha(b) || b == '_') { - lx->b = base; - *lx->b++ = b; - - b = getbyte(lx); - while (isalnum(b) || b == '_') { - *lx->b++ = b; - if (lx->b >= end) { - errorat(lx->pos, "out of macro buffer space!"); - goto Bad; - } - b = getbyte(lx); - } - *lx->b++ = '\0'; - - for (i = 0; i < n; i++) { - if (strcmp(base, args[i]) == 0) { - goto Arg; - } - } - str·appendlen(&buf, (lx->b - base - 1), base); - continue; - Arg: - str·appendbyte(&buf, PParg); - str·appendbyte(&buf, 'a' + i); - continue; - } - - if (b == '/') { - b = getbyte(lx); - if (b == '/') { - while (b = getbyte(lx), b != '\n'); - continue; - } - if (b == '*') { - b = getbyte(lx); - for (;;) { - if (b == '*') { - b = getbyte(lx); - if (b != '/') - continue; - b = getbyte(lx); - break; - } - if (b == '\n') { - errorat(lx->pos, "comment and newline found in define statement of %s", s); - break; - } - b = getbyte(lx); - } - continue; - } - str·appendbyte(&buf, '/'); - continue; - } - - if (b == '\\') { - b = getbyte(lx); - /* unix */ - if (b == '\n') { - lx->pos.line++; - b = getbyte(lx); - continue; - } - /* windows */ - if (b == '\r') { - b = getbyte(lx); - if (b == '\n') { - lx->pos.line++; - b = getbyte(lx); - continue; - } - } - str·appendbyte(&buf, '\\'); - } - if (b == '\n') { - lx->pos.line++; - break; - } - - if (b == '#') { - b = getnsbyte(lx); - if (b == '#') { - str·appendbyte(&buf, PPcat); - b = getbyte(lx); - continue; - } - - lx->b = base; - while (isalnum(b) || b == '_') { - *lx->b++ = b; - b = getbyte(lx); - } - *lx->b = '\0'; - - for (i = 0; i < n; i++) { - if (strcmp(base, args[i]) == 0) - goto Str; - } - errorat(lx->pos, "macro operator '#' must be followed by a valid variable identifier"); - goto Bad; - Str: - str·appendbyte(&buf, PPstr); - str·appendbyte(&buf, 'a' + i); - continue; - } - - str·appendbyte(&buf, b); - b = getbyte(lx); - if (b == EOF) { - errorat(lx->pos, "eof found in macro '%s'", s); - goto Bad; - } - } - if (dot) - *buf |= PPvar; - - lx->b = lx->buf; - sym = defmacro(lx, s, buf); - return 0; -Bad: - errorat(lx->pos, "failed parse of #define macro '%s'", s); - lx->b = lx->buf; - ppend(lx); - return 1; -} - -/* macro expansion */ -int -expandmacro(Lexer *lx, Sym *s, byte *dst) -{ - int n, lv, nargs, dots; - byte b, *it, *e, *arg[PPnarg]; - - /* not a function macro */ - if (s->macro[0] == '\0') { - if (s->macro[1] != PPbeg) { - errorat(lx->pos, "malformed macro"); - goto Bad; - } - strcpy(dst, s->macro + 2); - return str·len(s->macro)-2; - } - dots = (ubyte)s->macro[0] & PPvar; - nargs = (ubyte)s->macro[0] & (~PPvar); - - b = getnsbyte(lx); - if (b != '(') { - errorat(lx->pos, "macro function not given arguments"); - goto Bad; - } - - n = 0; - b = getbyte(lx); - if (b != ')') { - ungetbyte(lx); - lv = 0; - lx->b = lx->buf; - e = lx->buf + arrlen(lx->buf) - 4; - arg[n++] = lx->buf; - for (;;) { - if (lx->b >= e) - goto Nospace; - b = getbyte(lx); - if (b == '"') - for (;;) { - if (lx->b >= e) - goto Nospace; - *lx->b++ = b; - b = getbyte(lx); - if (b == '\\') { - *lx->b++ = b; - b = getbyte(lx); - continue; - } - if (b == '\n') { - errorat(lx->pos, "newline found in arguments: macro '%s'", s->name); - goto Bad; - } - if (b == '"') - break; - } - if (b == '\'') - for (;;) { - if (lx->b >= e) - goto Nospace; - *lx->b++ = b; - b = getbyte(lx); - if (b == '\\') { - *lx->b++ = b; - b = getbyte(lx); - continue; - } - if (b == '\n') { - errorat(lx->pos, "newline found in arguments: macro '%s'", s->name); - goto Bad; - } - if (b == '"') - break; - } - if (b == '/') { - b = getbyte(lx); - switch(b) { - case '*': - for (;;) { - b = getbyte(lx); - if (b == '*') { - b = getbyte(lx); - if (b == '/') - break; - } - } - *lx->b++ = ' '; - continue; - case '/': - while ((b = getbyte(lx)) != '\n') - ; - break; - - default: - ungetbyte(lx); - b = '/'; - } - } - if (lv == 0) { - if (b == ',') { - if (n == nargs && dots) { - *lx->b++ = ','; - continue; - } - *lx->b++ = '\0'; - arg[n++] = lx->b; - if (n > nargs) - break; - continue; - } - if (b == ')') - break; - } - if (b == '\n') - b = ' '; - *lx->b++ = b; - if (b == '(') - lv++; - if (b == ')') - lv--; - } - *lx->b = '\0'; - } - - if (n != nargs) { - errorat(lx->pos, "number of arguments don't match macro definition: %s", s->name); - *dst = '\0'; - goto Bad; - } - - if (s->macro[1] != PPbeg) { - errorat(lx->pos, "corrupted macro buffer: %s", s->name); - *dst = '\0'; - goto Bad; - } - - it = s->macro+2; - e = dst; - for (;;) { - b = *it++; - if (b == '\n') - b = ' '; - switch (b) { - case PParg: - b = *it++; - b -= 'a'; - if (b < 0 && b > n) { - errorat(lx->pos, "malformed macro index: %s", s->name); - goto Bad; - } - strcpy(dst, arg[b]); - dst += strlen(arg[b]); - - break; - - case PPstr: - b = *it++; - b -= 'a'; - if (b < 0 && b > n) { - errorat(lx->pos, "malformed macro index: %s", s->name); - goto Bad; - } - *dst++ = '"'; - strcpy(dst, arg[b]); - *dst++ = '"'; - - break; - - case PPcat: - continue; - - case '\0': - goto End; - - default: - *dst++ = b; - continue; - } - } -End: - *dst = '\0'; - return dst - e; -Nospace: - errorat(lx->pos, "out of memory during macro expansion %s", s->name); -Bad: - ppend(lx); - lx->b = lx->buf; - errorat(lx->pos, "failed to expand macro %s", s->name); - return -1; -} - -/* #include */ -static -error -ppinc(Lexer *lx) -{ - int i; - byte b, end; - string s; - - Stream *f; - Io *io; - - b = getnsbyte(lx); - if (b != '"') { - end = b; - if (b != '<') { - errorat(lx->pos, "unrecognized token '%c' in include directive", b); - goto Bad; - } - end = '>'; - } else - end = '"'; - - lx->b = lx->buf; - for (;;) { - b = getbyte(lx); - if (b == end) - break; - if (b == '\n') { - errorat(lx->pos, "hit end of line before include directive completed"); - goto Bad; - } - *lx->b++ = b; - } - *lx->b = '\0'; - s = lx->buf; - intern(&s); // NOTE: we could use this to see if we already have the file - - lx->b = lx->buf; - for (i = 0; i < C.inc.len; i++) { - if (i == 0 && end == '>') - continue; - - strcpy(lx->buf, C.inc.dir[i]); - strcat(lx->buf, "/"); - - if (strcmp(lx->buf, "./") == 0) - lx->buf[0] = '\0'; - strcat(lx->buf, s); - - if (os·exists(lx->buf, ReadOK)) { - break; - } - } - if (i == C.inc.len) { - errorat(lx->pos, "could not find file '%s' on standard include search path", s); - goto Bad; - } - - io = openio(lx, lx->buf); - if (io != nil) { - pushio(lx, io); - } - - return 0; - -Bad: - ungetbyte(lx); - lx->b = lx->buf; - errorat(lx->pos, "failed include"); - ppend(lx); - return 1; -} - -/* #pragma */ -static -error -ppprag(Lexer *lx) -{ - string s; - - s = ident(lx); - if (s == nil) { - errorat(lx->pos, "failed to parse pragma identifier"); - goto Bad; - } - lx->b = lx->buf; - if (strcmp(s, "once") == 0) { - pushomit(lx, lx->io->path); - return 0; - } -Bad: - lx->b = lx->buf; - errorat(lx->pos, "unrecognized pragma '%s'", s); - ppend(lx); - return 1; -} - -/* all #if statements */ -static -error -ppif(Lexer *lx, int f) -{ - Sym *sym; - string s; - int c, l, b; - -Eval: - if (f == 0) { - b = evalmacro(lx, 1); - if (b) { - ppend(lx); - return 0; - } - goto Skip; - } - - if (f == 1) - goto Skip; - - s = ident(lx); - if (s == nil) { - errorat(lx->pos, "failed to parse preprocessor identifier"); - goto Bad; - } - intern(&s); - lx->b = lx->buf; - - sym = lookup(&lx->sym, s); - if ((!sym && (f == 3)) || (sym && (f == 2))) - return 0; - -Skip: - b = 1; - l = 0; - for (;;) { - c = getbyte(lx); - if (c != '#') { - if (!isspace(c)) - b = 0; - if (c == '\n') { - lx->pos.line++; - b = 1; - } - if (c == EOF) { - errorat(lx->pos, "EOF hit while skipping if block. Missing endif"); - goto Bad; - } - continue; - } - if (!b) - continue; - s = ident(lx); - lx->b = lx->buf; - if (!s) - continue; - - if (l == 0 && (strcmp(s, "elif") == 0)) { - f = 0; - goto Eval; - } - - if (strcmp(s, "endif") == 0) { - if (l) { - l--; - continue; - } - ppend(lx); - return 0; - } - if (strcmp(s, "if") == 0 || - strcmp(s, "ifdef") == 0 || - strcmp(s, "ifndef") == 0) { - l++; - continue; - } - - if (l == 0 && f != 1 && strcmp(s, "else") == 0) { - return 0; - } - } - -Bad: - lx->b = lx->buf; - errorat(lx->pos, "bad syntax in preprocessor conditional directive"); - ppend(lx); - return 1; -} - -/* #if */ -static -error -ppif0(Lexer *lx) -{ - return ppif(lx, 0); -} - -/* #else */ -static -error -ppif1(Lexer *lx) -{ - return ppif(lx, 1); -} - -/* #ifdef */ -static -error -ppif2(Lexer *lx) -{ - return ppif(lx, 2); -} - -/* #ifndef */ -static -error -ppif3(Lexer *lx) -{ - return ppif(lx, 3); -} - -// ----------------------------------------------------------------------- -// dispatch function - -#define DIRECTIVE(a, b, c) c, -error (*macros[NUM_DIRECTIVES])(Lexer*) = { DIRECTIVES }; -#undef DIRECTIVE - -/* reads an identifier into the lexer's buffer */ -/* caller must intern */ - -error -domacro(Lexer *lx) -{ - int n; - error err; - string s; - - s = ident(lx); - intern(&s); - lx->b = lx->buf; - for (n = 0; n < NUM_DIRECTIVES; n++) { - if ((uintptr)s == (uintptr)directives[n]) { - goto Do; - } - } - errorat(lx->pos, "unrecognized directive name '%s'", s); - return 1; -Do: - err = macros[n](lx); - return err; -} - -error -dodefine(Lexer *lx, string s) -{ - int n; - byte *c, *def; - Sym *sym; - - strcpy(lx->buf, s); - c = strchr(lx->buf, '='); - if (c) { - *c++ = '\0'; - sym = lookup(&lx->sym, lx->buf); - if (sym) { - errorf("redefinition of symbol '%s'", sym->name); - return 1; - } - sym = define(&lx->sym, lx->buf, Smacro); - n = strlen(c) + 2; - sym->macro = str·makelen("", n); - str·appendbyte(&sym->macro, '\0'); - str·append(&sym->macro, c); - } else { - sym = lookup(&lx->sym, lx->buf); - if (sym) { - errorf("redefinition of symbol '%s'", sym->name); - return 1; - } - sym = define(&lx->sym, s, Smacro); - sym->macro = "\00\02"; - } - - return 0; -} diff --git a/sys/cmd/cc/rules.mk b/sys/cmd/cc/rules.mk deleted file mode 100644 index 34df34d..0000000 --- a/sys/cmd/cc/rules.mk +++ /dev/null @@ -1,23 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := \ - $(d)/pp.c \ - $(d)/lex.c \ - $(d)/ast.c \ - $(d)/bits.c \ - $(d)/cc.c - - -LIBS_$(d) := -BINS_$(d) := $(d)/cc -UNTS_$(d) := - -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/cc/scratch.c b/sys/cmd/cc/scratch.c deleted file mode 100644 index b37d9a5..0000000 --- a/sys/cmd/cc/scratch.c +++ /dev/null @@ -1,7 +0,0 @@ -#define XXX(a, b, c) a ## b ## c - -int -main() -{ - XXX(d, e, f); -} diff --git a/sys/cmd/cc/util.c b/sys/cmd/cc/util.c deleted file mode 100644 index cca16f2..0000000 --- a/sys/cmd/cc/util.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "cc.h" - -void -·free(void* _, void* ptr) { - return free(ptr); -} - -void * -·alloc(void* _, uint n, ulong size) { - return malloc(n*size); -} - -void * -·calloc(void* _, uint n, ulong size) { - return calloc(n, size); -} - -void * -·realloc(void* _, void *ptr, uint n, ulong size) { - return realloc(ptr, n*size); -} diff --git a/sys/cmd/dwm/LICENSE b/sys/cmd/dwm/LICENSE deleted file mode 100644 index d221f09..0000000 --- a/sys/cmd/dwm/LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -MIT/X Consortium License - -© 2006-2019 Anselm R Garbe -© 2006-2009 Jukka Salmi -© 2006-2007 Sander van Dijk
-© 2007-2011 Peter Hartlich -© 2007-2009 Szabolcs Nagy -© 2007-2009 Christof Musik -© 2007-2009 Premysl Hruby -© 2007-2008 Enno Gottox Boland -© 2008 Martin Hurton -© 2008 Neale Pickett -© 2009 Mate Nagy -© 2010-2016 Hiltjo Posthuma -© 2010-2012 Connor Lane Smith -© 2011 Christoph Lohmann <20h@r-36.net> -© 2015-2016 Quentin Rameau -© 2015-2016 Eric Pruitt -© 2016-2017 Markus Teich - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/sys/cmd/dwm/client.c b/sys/cmd/dwm/client.c deleted file mode 100644 index fa04f5f..0000000 --- a/sys/cmd/dwm/client.c +++ /dev/null @@ -1,657 +0,0 @@ -#include "dwm.h" - -void -applyrules(Client *c) -{ - char *class, *instance; - uint i; - Rule *r; - Monitor *m; - XClassHint ch = { nil, nil }; - - c->isfloating = 0; - c->tags = 0; - c->noswallow = -1; - - /* rule matching */ - XGetClassHint(dpy, c->win, &ch); - class = ch.res_class ? ch.res_class : broken; - instance = ch.res_name ? ch.res_name : broken; - - for (i = 0; i < arrlen(rules); i++) { - r = &rules[i]; - if ((!r->title || strstr(c->name, r->title)) - && (!r->class || strstr(class, r->class)) - && (!r->instance || strstr(instance, r->instance))) - { - c->isterm = r->isterm; - c->noswallow = r->noswallow; - c->isfloating = r->isfloating; - c->tags |= r->tags; - for (m = mons; m && m->num != r->monitor; m = m->next) - ; - if (m) - c->mon = m; - } - } - if (ch.res_class) - XFree(ch.res_class); - if (ch.res_name) - XFree(ch.res_name); - c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; -} - -int -applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) -{ - int baseismin; - Monitor *m = c->mon; - - /* set minimum possible */ - *w = MAX(1, *w); - *h = MAX(1, *h); - if (interact) { - if (*x > sw) - *x = sw - WIDTH(c); - if (*y > sh) - *y = sh - HEIGHT(c); - if (*x + *w + 2 * c->bw < 0) - *x = 0; - if (*y + *h + 2 * c->bw < 0) - *y = 0; - } else { - if (*x >= m->wx + m->ww) - *x = m->wx + m->ww - WIDTH(c); - if (*y >= m->wy + m->wh) - *y = m->wy + m->wh - HEIGHT(c); - if (*x + *w + 2 * c->bw <= m->wx) - *x = m->wx; - if (*y + *h + 2 * c->bw <= m->wy) - *y = m->wy; - } - if (*h < bh) - *h = bh; - if (*w < bh) - *w = bh; - if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { - /* see last two sentences in ICCCM 4.1.2.3 */ - baseismin = c->basew == c->minw && c->baseh == c->minh; - if (!baseismin) { /* temporarily remove base dimensions */ - *w -= c->basew; - *h -= c->baseh; - } - /* adjust for aspect limits */ - if (c->mina > 0 && c->maxa > 0) { - if (c->maxa < (float)*w / *h) - *w = *h * c->maxa + 0.5; - else if (c->mina < (float)*h / *w) - *h = *w * c->mina + 0.5; - } - if (baseismin) { /* increment calculation requires this */ - *w -= c->basew; - *h -= c->baseh; - } - /* adjust for increment value */ - if (c->incw) - *w -= *w % c->incw; - if (c->inch) - *h -= *h % c->inch; - /* restore base dimensions */ - *w = MAX(*w + c->basew, c->minw); - *h = MAX(*h + c->baseh, c->minh); - if (c->maxw) - *w = MIN(*w, c->maxw); - if (c->maxh) - *h = MIN(*h, c->maxh); - } - return *x != c->x || *y != c->y || *w != c->w || *h != c->h; -} - -void -attach(Client *c) -{ - c->next = c->mon->clients; - c->mon->clients = c; -} - -void -enqueue(Client *c) -{ - Client *l; - - for (l = c->mon->clients; l && l->next; l = l->next) - ; - - if (l) { - l->next = c; - c->next = nil; - } -} - -void -attachbottom(Client *c) -{ - Client **tc; - c->next = nil; - for (tc = &c->mon->clients; *tc; tc = &(*tc)->next) - ; - *tc = c; -} - -void -attachstack(Client *c) -{ - c->snext = c->mon->stack; - c->mon->stack = c; -} - -void -enqueuestack(Client *c) -{ - Client *l; - for (l = c->mon->clients; l && l->next; l = l->next) - ; - - if (l) { - l->snext = c; - c->snext = nil; - } -} - -void -configure(Client *c) -{ - XConfigureEvent ce; - - ce.type = ConfigureNotify; - ce.display = dpy; - ce.event = c->win; - ce.window = c->win; - ce.x = c->x; - ce.y = c->y; - ce.width = c->w; - ce.height = c->h; - ce.border_width = c->bw; - ce.above = None; - ce.override_redirect = False; - XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); -} - -void -detach(Client *c) -{ - Client **tc; - - for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next) - ; - *tc = c->next; -} - -void -detachstack(Client *c) -{ - Client **tc, *t; - - for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext) - ; - - *tc = c->snext; - - if (c == c->mon->sel) { - for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext) - ; - c->mon->sel = t; - } -} - -void -focus(Client *c) -{ - if (!c || !ISVISIBLE(c)) - for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext) - ; - if (selmon->sel && selmon->sel != c) - unfocus(selmon->sel, 0); - if (c) { - if (c->mon != selmon) - selmon = c->mon; - if (c->isurgent) - seturgent(c, 0); - detachstack(c); - attachstack(c); - grabbuttons(c, 1); - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); - setfocus(c); - } else { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); - } - selmon->sel = c; - drawbars(); -} - -Atom -getatomprop(Client *c, Atom prop) -{ - int di; - unsigned long dl; - uchar *p = nil; - Atom da, atom = None; - - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, - &da, &di, &dl, &dl, &p) == Success && p) { - atom = *(Atom *)p; - XFree(p); - } - return atom; -} - -void -grabbuttons(Client *c, int focused) -{ - updatenumlockmask(); - { - uint i, j; - uint modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - if (!focused) - XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, - BUTTONMASK, GrabModeSync, GrabModeSync, None, None); - for (i = 0; i < arrlen(buttons); i++) - if (buttons[i].click == ClkClientWin) - for (j = 0; j < arrlen(modifiers); j++) - XGrabButton(dpy, buttons[i].button, - buttons[i].mask | modifiers[j], - c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } -} - -Client * -nexttiled(Client *c) -{ - for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next) - ; - return c; -} - -void -pop(Client *c) -{ - detach(c); - attach(c); - focus(c); - arrange(c->mon); -} - -void -resize(Client *c, int x, int y, int w, int h, int interact) -{ - if (applysizehints(c, &x, &y, &w, &h, interact)) - resizeclient(c, x, y, w, h); -} - - -void -resizeclient(Client *c, int x, int y, int w, int h) -{ - XWindowChanges wc; - - c->oldx = c->x; c->x = wc.x = x; - c->oldy = c->y; c->y = wc.y = y; - c->oldw = c->w; c->w = wc.width = w; - c->oldh = c->h; c->h = wc.height = h; - wc.border_width = c->bw; - XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); - configure(c); - XSync(dpy, False); -} - -void -sendtomon(Client *c, Monitor *m) -{ - if (c->mon == m) - return; - unfocus(c, 1); - detach(c); - detachstack(c); - c->mon = m; - c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - /* attach(c); */ - attachbottom(c); - attachstack(c); - focus(nil); - arrange(nil); -} - -void -setclientstate(Client *c, long state) -{ - long data[] = { state, None }; - - XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, - PropModeReplace, (uchar *)data, 2); -} - -int -sendevent(Client *c, Atom proto) -{ - int n; - Atom *protocols; - int exists = 0; - XEvent ev; - - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { - while (!exists && n--) - exists = protocols[n] == proto; - XFree(protocols); - } - if (exists) { - ev.type = ClientMessage; - ev.xclient.window = c->win; - ev.xclient.message_type = wmatom[WMProtocols]; - ev.xclient.format = 32; - ev.xclient.data.l[0] = proto; - ev.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, c->win, False, NoEventMask, &ev); - } - return exists; -} - -void -setfocus(Client *c) -{ - if (!c->neverfocus) { - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - XChangeProperty(dpy, root, netatom[NetActiveWindow], - XA_WINDOW, 32, PropModeReplace, - (uchar *) &(c->win), 1); - } - sendevent(c, wmatom[WMTakeFocus]); -} - -void -setfullscreen(Client *c, int fullscreen) -{ - static uint32 opacity = 0xFFFFFFFFul; - if (fullscreen && !c->isfullscreen) { - XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, - PropModeReplace, (uchar*)&netatom[NetWMFullscreen], 1); - XChangeProperty(dpy, c->win, netatom[NetWMWindowOpacity], XA_CARDINAL, 32, PropModeReplace, (uchar *)&opacity, 1L); - - 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); - } else if (!fullscreen && c->isfullscreen){ - XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, - PropModeReplace, (uchar*)nil, 0); - XDeleteProperty(dpy, c->win, netatom[NetWMWindowOpacity]); - - c->isfullscreen = 0; - c->isfloating = c->oldstate; - c->bw = c->oldbw; - c->x = c->oldx; - c->y = c->oldy; - c->w = c->oldw; - c->h = c->oldh; - resizeclient(c, c->x, c->y, c->w, c->h); - arrange(c->mon); - } -} - -void -seturgent(Client *c, int urg) -{ - XWMHints *wmh; - - c->isurgent = urg; - if (!(wmh = XGetWMHints(dpy, c->win))) - return; - wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); - XSetWMHints(dpy, c->win, wmh); - XFree(wmh); -} - -void -showhide(Client *c) -{ - if (!c) - return; - if (ISVISIBLE(c)) { - /* show clients top down */ - XMoveWindow(dpy, c->win, c->x, c->y); - if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) - resize(c, c->x, c->y, c->w, c->h, 0); - showhide(c->snext); - } else { - /* hide clients bottom up */ - showhide(c->snext); - XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); - } -} - -void -swallow(Client *p, Client *c) -{ - Client *s; - - - if (c->noswallow > 0 || c->isterm) - return; - if (c->noswallow < 0 && !swallowfloating && c->isfloating) - return; - - detach(c); - detachstack(c); - - setclientstate(c, WithdrawnState); - XUnmapWindow(dpy, p->win); - - p->swallowing = c; - c->mon = p->mon; - - Window w = p->win; - p->win = c->win; - c->win = w; - - XChangeProperty(dpy, c->win, netatom[NetClientList], XA_WINDOW, 32, PropModeReplace, - (unsigned char *) &(p->win), 1); - - updatetitle(p); - s = scanner ? c : p; - XMoveResizeWindow(dpy, p->win, s->x, s->y, s->w, s->h); - arrange(p->mon); - configure(p); - updateclientlist(); -} - -Client * -termof(Client *w) -{ - Client *c; - Monitor *m; - - if (!w->pid || w->isterm) - return NULL; - - for (m = mons; m; m = m->next) { - for (c = m->clients; c; c = c->next) { - if (c->isterm && !c->swallowing && c->pid && isdescendent(c->pid, w->pid)) - return c; - } - } - - return NULL; -} - -void -unfocus(Client *c, int setfocus) -{ - if (!c) - return; - grabbuttons(c, 0); - XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); - if (setfocus) { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); - } -} - -void -unmanage(Client *c, int destroyed) -{ - Client *s; - Monitor *m = c->mon; - XWindowChanges wc; - - if (c->swallowing) { - unswallow(c); - return; - } - - s = swallowing(c->win); - if (s) { - free(s->swallowing); - s->swallowing = nil; - arrange(m); - focus(nil); - return; - } - - - detach(c); - detachstack(c); - - if (!destroyed) { - wc.border_width = c->oldbw; - XGrabServer(dpy); /* avoid race conditions */ - XSetErrorHandler(xerrordummy); - XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - setclientstate(c, WithdrawnState); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - } - free(c); - focus(nil); - updateclientlist(); - arrange(m); - - if (!s) { - // arrange(m); - focus(nil); - updateclientlist(); - } -} - -void -unswallow(Client *c) -{ - c->win = c->swallowing->win; - - free(c->swallowing); - c->swallowing = nil; - - XDeleteProperty(dpy, c->win, netatom[NetClientList]); - - /* unfullscreen the client */ - setfullscreen(c, 0); - updatetitle(c); - arrange(c->mon); - XMapWindow(dpy, c->win); - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); - setclientstate(c, NormalState); - focus(nil); - arrange(c->mon); -} - - -void -updatesizehints(Client *c) -{ - long msize; - XSizeHints size; - - if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) - /* size is uninitialized, ensure that size.flags aren't used */ - size.flags = PSize; - if (size.flags & PBaseSize) { - c->basew = size.base_width; - c->baseh = size.base_height; - } else if (size.flags & PMinSize) { - c->basew = size.min_width; - c->baseh = size.min_height; - } else - c->basew = c->baseh = 0; - if (size.flags & PResizeInc) { - c->incw = size.width_inc; - c->inch = size.height_inc; - } else - c->incw = c->inch = 0; - if (size.flags & PMaxSize) { - c->maxw = size.max_width; - c->maxh = size.max_height; - } else - c->maxw = c->maxh = 0; - if (size.flags & PMinSize) { - c->minw = size.min_width; - c->minh = size.min_height; - } else if (size.flags & PBaseSize) { - c->minw = size.base_width; - c->minh = size.base_height; - } else - c->minw = c->minh = 0; - if (size.flags & PAspect) { - c->mina = (float)size.min_aspect.y / size.min_aspect.x; - c->maxa = (float)size.max_aspect.x / size.max_aspect.y; - } else - c->maxa = c->mina = 0.0; - c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); -} - -void -updatetitle(Client *c) -{ - if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) - gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); - if (c->name[0] == '\0') /* hack to mark broken clients */ - strcpy(c->name, broken); -} - -void -updatewindowtype(Client *c) -{ - Atom state = getatomprop(c, netatom[NetWMState]); - Atom wtype = getatomprop(c, netatom[NetWMWindowType]); - - if (state == netatom[NetWMFullscreen]) - setfullscreen(c, 1); - if (wtype == netatom[NetWMWindowTypeDialog]) - c->isfloating = 1; -} - -void -updatewmhints(Client *c) -{ - XWMHints *wmh; - - if ((wmh = XGetWMHints(dpy, c->win))) { - if (c == selmon->sel && wmh->flags & XUrgencyHint) { - wmh->flags &= ~XUrgencyHint; - XSetWMHints(dpy, c->win, wmh); - } else - c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; - if (wmh->flags & InputHint) - c->neverfocus = !wmh->input; - else - c->neverfocus = 0; - XFree(wmh); - } -} diff --git a/sys/cmd/dwm/config.h b/sys/cmd/dwm/config.h deleted file mode 100644 index 1f82b1f..0000000 --- a/sys/cmd/dwm/config.h +++ /dev/null @@ -1,141 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#define VERSION "1" - -/* appearance */ -static uint borderpx = 2; /* border pixel of windows */ -static uint gapx = 4; /* gaps between windows */ -static uint snap = 32; /* snap pixel */ -static int swallowfloating = 0; /* 1 will swallow floating by default */ -static int showbar = 1; /* 0 means no bar */ -static int topbar = 1; /* 0 means bottom bar */ -static char *fonts[] = { "consolas:size=16" }; -static char col_gray1[] = "#504945"; -static char col_gray2[] = "#282828"; -static char col_gray3[] = "#fbf1c7"; -static char col_gray4[] = "#504945"; -static char col_cyan[] = "#83a598"; -static char *colors[][3] = -{ - /* fg bg border */ - [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, - [SchemeSel] = { col_gray4, col_cyan, col_cyan }, -}; - -/* tagging */ -static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; - -static Rule rules[] = { - /* xprop(1): - * WM_CLASS(STRING) = instance, class - * WM_NAME(STRING) = title - */ - /* class instance title tags mask isfloating isterminal noswallow monitor */ - { "Gimp", nil, nil, 0, 1, 0, 0, -1 }, - { "Inkscape", nil, nil, 0, 1, 0, 0, -1 }, - { "zoom", nil, nil, 0, 1, 0, 0, -1 }, - { "qutebrowser", nil, nil, 0, 0, 0, 0, -1 }, - { "term-256color", nil, nil, 0, 0, 1, -1, -1 }, -}; - -/* layout(s) */ -static float mfact = 0.55; /* factor of master area size [0.05..0.95] */ -static int nmaster = 1; /* number of clients in master area */ -static int resizehints = 1; /* 1 means respect size hints in tiled resizals */ - -static Layout layouts[] = { - /* symbol arrange function */ - { "[]=", tile }, /* first entry is default */ - { "><>", nil }, /* no layout function means floating behavior */ - { "[M]", monocle }, -}; - -/* key definitions */ -#define MODKEY Mod4Mask -#define TAGKEYS(KEY,TAG) \ - { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ - { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, - -/* commands */ -static char *menucmd[] = { "menu_run", nil }; -static char *termcmd[] = { "term", nil }; -static char *webscmd[] = { "qutebrowser", nil }; -static char scratchname[] = "scratchpad"; -static char *scratchcmd[] = { "term", "-t", scratchname, "-g", "120x34", nil }; -static char *upvolcmd[] = { "vol", "+5%", nil }; -static char *lovolcmd[] = { "vol", "-5%", nil }; -static char *novolcmd[] = { "vol", "mute", nil }; - -#define XK_lovol XF86XK_AudioLowerVolume -#define XK_upvol XF86XK_AudioRaiseVolume -#define XK_novol XF86XK_AudioMute - -static Key keys[] = { - /* modifier key function argument */ - { MODKEY, XK_d, spawn, {.v = menucmd } }, - { MODKEY, XK_Return, spawn, {.v = termcmd } }, - { MODKEY, XK_q, spawn, {.v = webscmd } }, - { 0, XK_upvol, spawn, {.v = upvolcmd} }, - { 0, XK_lovol, spawn, {.v = lovolcmd} }, - { 0, XK_novol, spawn, {.v = novolcmd} }, - { MODKEY, XK_s, togglescratch, {.v = scratchcmd} }, - { MODKEY, XK_b, togglebar, {0} }, - { MODKEY, XK_f, togglefocus, {0} }, - { MODKEY, XK_Up, focusstack, {.i = +1 } }, - { MODKEY, XK_Down, focusstack, {.i = -1 } }, - { MODKEY|ShiftMask, XK_Up, rotatestack, {.i = +1 } }, - { MODKEY|ShiftMask, XK_Down, rotatestack, {.i = -1 } }, - { MODKEY, XK_i, incnmaster, {.i = +1 } }, - { MODKEY, XK_o, incnmaster, {.i = -1 } }, - { MODKEY, XK_h, focusdirection, {.i = 'l'} }, - { MODKEY, XK_l, focusdirection, {.i = 'r'} }, - { MODKEY, XK_k, focusdirection, {.i = 'u'} }, - { MODKEY, XK_j, focusdirection, {.i = 'd'} }, - { MODKEY|ShiftMask, XK_h, setmfact, {.f = -0.05} }, - { MODKEY|ShiftMask, XK_l, setmfact, {.f = +0.05} }, - { MODKEY|ShiftMask, XK_k, rotatestack, {.i = -1 } }, - { MODKEY|ShiftMask, XK_j, rotatestack, {.i = +1 } }, - { 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]} }, - { MODKEY|ShiftMask, XK_f, setlayout, {.v = &layouts[1]} }, - { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[2]} }, - { MODKEY, XK_space, setlayout, {0} }, - { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, - { MODKEY, XK_0, view, {.ui = ~0 } }, - { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, - { MODKEY, XK_comma, focusmon, {.i = -1 } }, - { MODKEY, XK_period, focusmon, {.i = +1 } }, - { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, - { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, - TAGKEYS( XK_1, 0) - TAGKEYS( XK_2, 1) - TAGKEYS( XK_3, 2) - TAGKEYS( XK_4, 3) - TAGKEYS( XK_5, 4) - TAGKEYS( XK_6, 5) - TAGKEYS( XK_7, 6) - TAGKEYS( XK_8, 7) - TAGKEYS( XK_9, 8) - { MODKEY|ShiftMask, XK_e, quit, {0} }, -}; - -/* button definitions */ -/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */ -static Button buttons[] = { - /* click event mask button function argument */ - { ClkLtSymbol, 0, Button1, setlayout, {0} }, - { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, - { ClkWinTitle, 0, Button2, zoom, {0} }, - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, - { ClkClientWin, MODKEY, Button1, movemouse, {0} }, - { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, - { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, - { ClkTagBar, 0, Button1, view, {0} }, - { ClkTagBar, 0, Button3, toggleview, {0} }, - { ClkTagBar, MODKEY, Button1, tag, {0} }, - { ClkTagBar, MODKEY, Button3, toggletag, {0} }, -}; - diff --git a/sys/cmd/dwm/drw.c b/sys/cmd/dwm/drw.c deleted file mode 100644 index a6d6902..0000000 --- a/sys/cmd/dwm/drw.c +++ /dev/null @@ -1,376 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" - -Drw * -drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) -{ - Drw *drw = ecalloc(1, sizeof(Drw)); - - drw->dpy = dpy; - drw->screen = screen; - drw->root = root; - drw->w = w; - drw->h = h; - drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); - drw->gc = XCreateGC(dpy, root, 0, NULL); - XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); - - return drw; -} - -void -drw_resize(Drw *drw, unsigned int w, unsigned int h) -{ - if (!drw) - return; - - drw->w = w; - drw->h = h; - if (drw->drawable) - XFreePixmap(drw->dpy, drw->drawable); - drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); -} - -void -drw_free(Drw *drw) -{ - XFreePixmap(drw->dpy, drw->drawable); - XFreeGC(drw->dpy, drw->gc); - free(drw); -} - -/* This function is an implementation detail. Library users should use - * drw_fontset_create instead. - */ -static Fnt * -xfont_create(Drw *drw, char *fontname, FcPattern *fontpattern) -{ - Fnt *font; - XftFont *xfont = NULL; - FcPattern *pattern = NULL; - - if (fontname) { - /* Using the pattern found at font->xfont->pattern does not yield the - * same substitution results as using the pattern returned by - * FcNameParse; using the latter results in the desired fallback - * behaviour whereas the former just results in missing-character - * rectangles being drawn, at least with some fonts. */ - if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { - fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); - return NULL; - } - if (!(pattern = FcNameParse((FcChar8 *) fontname))) { - fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); - XftFontClose(drw->dpy, xfont); - return NULL; - } - } else if (fontpattern) { - if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { - fprintf(stderr, "error, cannot load font from pattern.\n"); - return NULL; - } - } else { - fatal("no font specified."); - } - - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - - font = ecalloc(1, sizeof(Fnt)); - font->xfont = xfont; - font->pattern = pattern; - font->h = xfont->ascent + xfont->descent; - font->dpy = drw->dpy; - - return font; -} - -static void -xfont_free(Fnt *font) -{ - if (!font) - return; - if (font->pattern) - FcPatternDestroy(font->pattern); - XftFontClose(font->dpy, font->xfont); - free(font); -} - -Fnt* -drw_fontset_create(Drw* drw, char *fonts[], size_t fontcount) -{ - Fnt *cur, *ret = NULL; - size_t i; - - if (!drw || !fonts) - return NULL; - - for (i = 1; i <= fontcount; i++) { - if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { - cur->next = ret; - ret = cur; - } - } - return (drw->fonts = ret); -} - -void -drw_fontset_free(Fnt *font) -{ - if (font) { - drw_fontset_free(font->next); - xfont_free(font); - } -} - -void -drw_clr_create(Drw *drw, Clr *dest, char *clrname) -{ - if (!drw || !dest || !clrname) - return; - - if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), - DefaultColormap(drw->dpy, drw->screen), - clrname, dest)) - fatal("error, cannot allocate color '%s'", clrname); -} - -/* Wrapper to create color schemes. The caller has to call free(3) on the - * returned color scheme when done using it. */ -Clr * -drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount) -{ - size_t i; - Clr *ret; - - /* need at least two colors for a scheme */ - if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) - return NULL; - - for (i = 0; i < clrcount; i++) - drw_clr_create(drw, &ret[i], clrnames[i]); - return ret; -} - -void -drw_setfontset(Drw *drw, Fnt *set) -{ - if (drw) - drw->fonts = set; -} - -void -drw_setscheme(Drw *drw, Clr *scm) -{ - if (drw) - drw->scheme = scm; -} - -void -drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) -{ - if (!drw || !drw->scheme) - return; - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); - if (filled) - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); - else - XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); -} - -int -drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, char *text, int invert) -{ - char buf[1024]; - int ty; - unsigned int ew; - XftDraw *d = NULL; - Fnt *usedfont, *curfont, *nextfont; - size_t i, len; - int utf8strlen, utf8charlen, render = x || y || w || h; - rune utf8codepoint = 0; - char *utf8str; - FcCharSet *fccharset; - FcPattern *fcpattern; - FcPattern *match; - XftResult result; - int charexists = 0; - - if (!drw || (render && !drw->scheme) || !text || !drw->fonts) - return 0; - - if (!render) { - w = ~w; - } else { - XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); - d = XftDrawCreate(drw->dpy, drw->drawable, - DefaultVisual(drw->dpy, drw->screen), - DefaultColormap(drw->dpy, drw->screen)); - x += lpad; - w -= lpad; - } - - usedfont = drw->fonts; - while (1) { - utf8strlen = 0; - utf8str = text; - nextfont = NULL; - while (*text) { - utf8charlen = utf8·decode(text, &utf8codepoint); - for (curfont = drw->fonts; curfont; curfont = curfont->next) { - charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); - if (charexists) { - if (curfont == usedfont) { - utf8strlen += utf8charlen; - text += utf8charlen; - } else { - nextfont = curfont; - } - break; - } - } - - if (!charexists || nextfont) - break; - else - charexists = 0; - } - - if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) - drw_font_getexts(usedfont, utf8str, len, &ew, NULL); - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; - } - } - - if (!*text) { - break; - } else if (nextfont) { - charexists = 0; - usedfont = nextfont; - } else { - /* Regardless of whether or not a fallback font is found, the - * character must be drawn. */ - charexists = 1; - - fccharset = FcCharSetCreate(); - FcCharSetAddChar(fccharset, utf8codepoint); - - if (!drw->fonts->pattern) { - /* Refer to the comment in xfont_create for more information. */ - fatal("the first font in the cache must be loaded from a font string."); - } - - fcpattern = FcPatternDuplicate(drw->fonts->pattern); - FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); - - FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); - FcDefaultSubstitute(fcpattern); - match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); - - FcCharSetDestroy(fccharset); - FcPatternDestroy(fcpattern); - - if (match) { - usedfont = xfont_create(drw, NULL, match); - if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { - for (curfont = drw->fonts; curfont->next; curfont = curfont->next) - ; /* NOP */ - curfont->next = usedfont; - } else { - xfont_free(usedfont); - usedfont = drw->fonts; - } - } - } - } - if (d) - XftDrawDestroy(d); - - return x + (render ? w : 0); -} - -void -drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) -{ - if (!drw) - return; - - XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); - XSync(drw->dpy, False); -} - -unsigned int -drw_fontset_getwidth(Drw *drw, char *text) -{ - if (!drw || !drw->fonts || !text) - return 0; - return drw_text(drw, 0, 0, 0, 0, 0, text, 0); -} - -void -drw_font_getexts(Fnt *font, char *text, unsigned int len, unsigned int *w, unsigned int *h) -{ - XGlyphInfo ext; - - if (!font || !text) - return; - - XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); - if (w) - *w = ext.xOff; - if (h) - *h = font->h; -} - -Cur * -drw_cur_create(Drw *drw, int shape) -{ - Cur *cur; - - if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) - return NULL; - - cur->cursor = XCreateFontCursor(drw->dpy, shape); - - return cur; -} - -void -drw_cur_free(Drw *drw, Cur *cursor) -{ - if (!cursor) - return; - - XFreeCursor(drw->dpy, cursor->cursor); - free(cursor); -} diff --git a/sys/cmd/dwm/dwm.c b/sys/cmd/dwm/dwm.c deleted file mode 100644 index 0567650..0000000 --- a/sys/cmd/dwm/dwm.c +++ /dev/null @@ -1,1185 +0,0 @@ -#include "dwm.h" - -/* global variables */ -char broken[] = ""; -char stext[256]; -int scanner; -int screen; -int sw, sh; /* X display screen geometry width, height */ -int bh, blw = 0; /* bar geometry */ -int lrpad; /* sum of left and right padding for text */ -int (*xerrorxlib)(Display *, XErrorEvent *); -uint numlockmask = 0; -void (*handler[LASTEvent]) (XEvent *) = { - [ButtonPress] = buttonpress, - [ClientMessage] = clientmessage, - [ConfigureRequest] = configurerequest, - [ConfigureNotify] = configurenotify, - [DestroyNotify] = destroynotify, - [EnterNotify] = enternotify, - [Expose] = expose, - [FocusIn] = focusin, - [KeyPress] = keypress, - [MappingNotify] = mappingnotify, - [MapRequest] = maprequest, - [MotionNotify] = motionnotify, - [PropertyNotify] = propertynotify, - [UnmapNotify] = unmapnotify -}; - -xcb_connection_t *xcon; - -Atom wmatom[WMLast] = {0}, netatom[NetLast] = {0}; -int running = 1; -Cur *cursor[MouseLast] = {0}; -Clr **scheme = nil; -Display *dpy = nil; -Drw *drw = nil; -Monitor *mons = nil, *selmon = nil; -Window root = {0}, wmcheckwin = {0}; - -/* compile-time check if all tags fit into an uint bit array. */ -struct NumTags { char limitexceeded[arrlen(tags) > 31 ? -1 : 1]; }; - -/* function implementations */ -void -arrange(Monitor *m) -{ - if (m) - showhide(m->stack); - else for (m = mons; m; m = m->next) - showhide(m->stack); - if (m) { - arrangemon(m); - restack(m); - } else for (m = mons; m; m = m->next) - arrangemon(m); -} - -void -arrangemon(Monitor *m) -{ - strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); - if (m->lt[m->sellt]->arrange) - m->lt[m->sellt]->arrange(m); -} - -void -buttonpress(XEvent *e) -{ - uint i, x, click; - Arg arg = {0}; - Client *c; - Monitor *m; - XButtonPressedEvent *ev = &e->xbutton; - - click = ClkRootWin; - /* focus monitor if necessary */ - if ((m = wintomon(ev->window)) && m != selmon) { - unfocus(selmon->sel, 1); - selmon = m; - focus(nil); - } - if (ev->window == selmon->barwin) { - i = x = 0; - do - x += TEXTW(tags[i]); - while (ev->x >= x && ++i < arrlen(tags)); - if (i < arrlen(tags)) { - click = ClkTagBar; - arg.ui = 1 << i; - } else if (ev->x < x + blw) - click = ClkLtSymbol; - else if (ev->x > selmon->ww - TEXTW(stext)) - click = ClkStatusText; - else - click = ClkWinTitle; - } else if ((c = wintoclient(ev->window))) { - focus(c); - restack(selmon); - XAllowEvents(dpy, ReplayPointer, CurrentTime); - click = ClkClientWin; - } - for (i = 0; i < arrlen(buttons); i++) - if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); -} - -void -checkotherwm(void) -{ - xerrorxlib = XSetErrorHandler(xerrorstart); - /* this causes an error if some other window manager is running */ - XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); - XSync(dpy, False); - XSetErrorHandler(xerror); - XSync(dpy, False); -} - -void -cleanup(void) -{ - Arg a = {.ui = ~0}; - Layout foo = { "", nil }; - Monitor *m; - size_t i; - - view(&a); - selmon->lt[selmon->sellt] = &foo; - for (m = mons; m; m = m->next) - while (m->stack) - unmanage(m->stack, 0); - XUngrabKey(dpy, AnyKey, AnyModifier, root); - while (mons) - cleanupmon(mons); - for (i = 0; i < MouseLast; i++) - drw_cur_free(drw, cursor[i]); - for (i = 0; i < arrlen(colors); i++) - free(scheme[i]); - XDestroyWindow(dpy, wmcheckwin); - drw_free(drw); - XSync(dpy, False); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XDeleteProperty(dpy, root, netatom[NetActiveWindow]); -} - -void -cleanupmon(Monitor *mon) -{ - Monitor *m; - - if (mon == mons) - mons = mons->next; - else { - for (m = mons; m && m->next != mon; m = m->next); - m->next = mon->next; - } - XUnmapWindow(dpy, mon->barwin); - XDestroyWindow(dpy, mon->barwin); - free(mon); -} - -void -clientmessage(XEvent *e) -{ - XClientMessageEvent *cme = &e->xclient; - Client *c = wintoclient(cme->window); - - if (!c) - return; - if (cme->message_type == netatom[NetWMState]) { - if (cme->data.l[1] == netatom[NetWMFullscreen] - || cme->data.l[2] == netatom[NetWMFullscreen]) - setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ - || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); - } else if (cme->message_type == netatom[NetActiveWindow]) { - if (c != selmon->sel && !c->isurgent) - seturgent(c, 1); - } -} - -void -configurenotify(XEvent *e) -{ - Monitor *m; - Client *c; - XConfigureEvent *ev = &e->xconfigure; - int dirty; - - /* TODO: updategeom handling sucks, needs to be simplified */ - if (ev->window == root) { - dirty = (sw != ev->width || sh != ev->height); - sw = ev->width; - sh = ev->height; - if (updategeom() || dirty) { - drw_resize(drw, sw, bh); - updatebars(); - for (m = mons; m; m = m->next) { - for (c = m->clients; c; c = c->next) - if (c->isfullscreen) - resizeclient(c, m->mx, m->my, m->mw, m->mh); - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); - } - focus(nil); - arrange(nil); - } - } -} - -void -configurerequest(XEvent *e) -{ - Client *c; - Monitor *m; - XConfigureRequestEvent *ev = &e->xconfigurerequest; - XWindowChanges wc; - - if ((c = wintoclient(ev->window))) { - if (ev->value_mask & CWBorderWidth) - c->bw = ev->border_width; - else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { - m = c->mon; - if (ev->value_mask & CWX) { - c->oldx = c->x; - c->x = m->mx + ev->x; - } - if (ev->value_mask & CWY) { - c->oldy = c->y; - c->y = m->my + ev->y; - } - if (ev->value_mask & CWWidth) { - c->oldw = c->w; - c->w = ev->width; - } - if (ev->value_mask & CWHeight) { - c->oldh = c->h; - c->h = ev->height; - } - if ((c->x + c->w) > m->mx + m->mw && c->isfloating) - c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ - if ((c->y + c->h) > m->my + m->mh && c->isfloating) - c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ - if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) - configure(c); - if (ISVISIBLE(c)) - XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); - } else - configure(c); - } else { - wc.x = ev->x; - wc.y = ev->y; - wc.width = ev->width; - wc.height = ev->height; - wc.border_width = ev->border_width; - wc.sibling = ev->above; - wc.stack_mode = ev->detail; - XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); - } - XSync(dpy, False); -} - -Monitor * -createmon(void) -{ - Monitor *m; - - m = ecalloc(1, sizeof(Monitor)); - m->tagset[0] = m->tagset[1] = 1; - m->mfact = mfact; - m->nmaster = nmaster; - m->showbar = showbar; - m->topbar = topbar; - m->lt[0] = &layouts[0]; - m->lt[1] = &layouts[1 % arrlen(layouts)]; - strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); - return m; -} - -void -destroynotify(XEvent *e) -{ - Client *c; - XDestroyWindowEvent *ev = &e->xdestroywindow; - - if ((c = wintoclient(ev->window))) - unmanage(c, 1); - else if ((c = swallowing(ev->window))) - unmanage(c->swallowing, 1); -} - -Monitor * -dirtomon(int dir) -{ - Monitor *m = nil; - - if (dir > 0) { - if (!(m = selmon->next)) - m = mons; - } else if (selmon == mons) - for (m = mons; m->next; m = m->next); - else - for (m = mons; m->next != selmon; m = m->next); - return m; -} - -void -drawbar(Monitor *m) -{ - int x, w, tw = 0; - int boxs = drw->fonts->h / 9; - int boxw = drw->fonts->h / 6 + 2; - uint i, occ = 0, urg = 0; - Client *c; - - /* draw status first so it can be overdrawn by tags later */ - if(m == selmon) { /* status is only drawn on selected monitor */ - drw_setscheme(drw, scheme[SchemeNorm]); - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); - } - - for(c = m->clients; c; c = c->next) { - occ |= c->tags; - if (c->isurgent) - urg |= c->tags; - } - x = 0; - for(i = 0; i < arrlen(tags); i++) { - w = TEXTW(tags[i]); - drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); - if (occ & 1 << i) - drw_rect(drw, x + boxs, boxs, boxw, boxw, - m == selmon && selmon->sel && selmon->sel->tags & 1 << i, - urg & 1 << i); - x += w; - } - w = blw = TEXTW(m->ltsymbol); - drw_setscheme(drw, scheme[SchemeNorm]); - x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); - - if((w = m->ww - tw - x) > bh) { - if (m->sel) { - drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); - if (m->sel->isfloating) - drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); - } else { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, x, 0, w, bh, 1, 1); - } - } - drw_map(drw, m->barwin, 0, 0, m->ww, bh); -} - -void -drawbars(void) -{ - Monitor *m; - - for (m = mons; m; m = m->next) - drawbar(m); -} - -void -enternotify(XEvent *e) -{ - Client *c; - Monitor *m; - XCrossingEvent *ev = &e->xcrossing; - - if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) - return; - c = wintoclient(ev->window); - m = c ? c->mon : wintomon(ev->window); - if (m != selmon) { - unfocus(selmon->sel, 1); - selmon = m; - } else if (!c || c == selmon->sel) - return; - focus(c); -} - -void -expose(XEvent *e) -{ - Monitor *m; - XExposeEvent *ev = &e->xexpose; - - if (ev->count == 0 && (m = wintomon(ev->window))) - drawbar(m); -} - -/* there are some broken focus acquiring clients needing extra handling */ -void -focusin(XEvent *e) -{ - XFocusChangeEvent *ev = &e->xfocus; - - if (selmon->sel && ev->window != selmon->sel->win) - setfocus(selmon->sel); -} - -int -getrootptr(int *x, int *y) -{ - int di; - uint dui; - Window dummy; - - return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); -} - -long -getstate(Window w) -{ - int format; - long result = -1; - unsigned char *p = nil; - unsigned long n, extra; - Atom real; - - if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], - &real, &format, &n, &extra, (unsigned char **)&p) != Success) - return -1; - if (n != 0) - result = *p; - XFree(p); - return result; -} - -int -gettextprop(Window w, Atom atom, char *text, uint size) -{ - char **list = nil; - int n; - XTextProperty name; - - if (!text || size == 0) - return 0; - text[0] = '\0'; - if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) - return 0; - if (name.encoding == XA_STRING) - strncpy(text, (char *)name.value, size - 1); - else { - if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { - strncpy(text, *list, size - 1); - XFreeStringList(list); - } - } - text[size - 1] = '\0'; - XFree(name.value); - return 1; -} - -void -grabkeys(void) -{ - updatenumlockmask(); - { - uint i, j; - uint modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - KeyCode code; - - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for (i = 0; i < arrlen(keys); i++) - if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) - for (j = 0; j < arrlen(modifiers); j++) - XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, - True, GrabModeAsync, GrabModeAsync); - } -} - -static -int -isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) -{ - while (n--) - if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org - && unique[n].width == info->width && unique[n].height == info->height) - return 0; - return 1; -} - -void -keypress(XEvent *e) -{ - uint i; - KeySym keysym; - XKeyEvent *ev; - - ev = &e->xkey; - keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); - for (i = 0; i < arrlen(keys); i++) - if (keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) - keys[i].func(&(keys[i].arg)); -} - -void -manage(Window w, XWindowAttributes *wa) -{ - Client *c, *t = nil, *term = nil; - Window trans = None; - XWindowChanges wc; - - c = ecalloc(1, sizeof(Client)); - c->win = w; - c->pid = winpid(w); - /* geometry */ - c->x = c->oldx = wa->x; - c->y = c->oldy = wa->y; - c->w = c->oldw = wa->width; - c->h = c->oldh = wa->height; - c->oldbw = wa->border_width; - - updatetitle(c); - if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { - c->mon = t->mon; - c->tags = t->tags; - } else { - c->mon = selmon; - applyrules(c); - term = termof(c); - } - - if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw) - c->x = c->mon->mx + c->mon->mw - WIDTH(c); - if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh) - c->y = c->mon->my + c->mon->mh - HEIGHT(c); - c->x = MAX(c->x, c->mon->mx); - /* only fix client y-offset, if the client center might cover the bar */ - c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) - && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); - c->bw = borderpx; - - selmon->tagset[selmon->seltags] &= ~scratchtag; - if(!strcmp(c->name, scratchname)) { - c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag; - c->isfloating = 1; - c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); - c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); - } - - wc.border_width = c->bw; - XConfigureWindow(dpy, w, CWBorderWidth, &wc); - XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); - configure(c); /* propagates border_width, if size doesn't change */ - updatewindowtype(c); - updatesizehints(c); - updatewmhints(c); - XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); - grabbuttons(c, 0); - - if (!c->isfloating) - c->isfloating = c->oldstate = trans != None || c->isfixed; - if (c->isfloating) - XRaiseWindow(dpy, c->win); - - /* attach(c); */ - attachbottom(c); - attachstack(c); - - XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, - (unsigned char *) &(c->win), 1); - XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ - setclientstate(c, NormalState); - if (c->mon == selmon) - unfocus(selmon->sel, 0); - c->mon->sel = c; - arrange(c->mon); - XMapWindow(dpy, c->win); - if (term) - swallow(term, c); - focus(nil); -} - -void -mappingnotify(XEvent *e) -{ - XMappingEvent *ev = &e->xmapping; - - XRefreshKeyboardMapping(ev); - if (ev->request == MappingKeyboard) - grabkeys(); -} - -void -maprequest(XEvent *e) -{ - static XWindowAttributes wa; - XMapRequestEvent *ev = &e->xmaprequest; - - if (!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if (wa.override_redirect) - return; - if (!wintoclient(ev->window)) - manage(ev->window, &wa); -} - -void -monocle(Monitor *m) -{ - uint n = 0; - Client *c; - - for (c = m->clients; c; c = c->next) - if (ISVISIBLE(c)) - n++; - if (n > 0) /* override layout symbol */ - snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); - for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) - resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); -} - -void -motionnotify(XEvent *e) -{ - static Monitor *mon = nil; - Monitor *m; - XMotionEvent *ev = &e->xmotion; - - if (ev->window != root) - return; - if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { - unfocus(selmon->sel, 1); - selmon = m; - focus(nil); - } - mon = m; -} - -void -propertynotify(XEvent *e) -{ - Client *c; - Window trans; - XPropertyEvent *ev = &e->xproperty; - - if ((ev->window == root) && (ev->atom == XA_WM_NAME)) - updatestatus(); - else if (ev->state == PropertyDelete) - return; /* ignore */ - else if ((c = wintoclient(ev->window))) { - switch(ev->atom) { - default: break; - case XA_WM_TRANSIENT_FOR: - if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && - (c->isfloating = (wintoclient(trans)) != nil)) - arrange(c->mon); - break; - case XA_WM_NORMAL_HINTS: - updatesizehints(c); - break; - case XA_WM_HINTS: - updatewmhints(c); - drawbars(); - break; - } - if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if (c == c->mon->sel) - drawbar(c->mon); - } - if (ev->atom == netatom[NetWMWindowType]) - updatewindowtype(c); - } -} - -Monitor * -recttomon(int x, int y, int w, int h) -{ - Monitor *m, *r = selmon; - int a, area = 0; - - for (m = mons; m; m = m->next) - if ((a = INTERSECT(x, y, w, h, m)) > area) { - area = a; - r = m; - } - return r; -} - -void -restack(Monitor *m) -{ - Client *c; - XEvent ev; - XWindowChanges wc; - - drawbar(m); - if (!m->sel) - return; - if (m->sel->isfloating || !m->lt[m->sellt]->arrange) - XRaiseWindow(dpy, m->sel->win); - if (m->lt[m->sellt]->arrange) { - wc.stack_mode = Below; - wc.sibling = m->barwin; - for (c = m->stack; c; c = c->snext) - if (!c->isfloating && ISVISIBLE(c)) { - XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); - wc.sibling = c->win; - } - } - XSync(dpy, False); - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -run(void) -{ - XEvent ev; - /* main event loop */ - XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) - if (handler[ev.type]) - handler[ev.type](&ev); /* call handler */ -} - -void -scan(void) -{ - uint i, num; - Window d1, d2, *wins = nil; - XWindowAttributes wa; - char swin[256]; - - scanner = 1; - - if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { - for (i = 0; i < num; i++) { - if (!XGetWindowAttributes(dpy, wins[i], &wa) - || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) - continue; - if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) - manage(wins[i], &wa); - else if (gettextprop(wins[i], netatom[NetClientList], swin, sizeof swin)) - manage(wins[i], &wa); - } - for (i = 0; i < num; i++) { /* now the transients */ - if (!XGetWindowAttributes(dpy, wins[i], &wa)) - continue; - if (XGetTransientForHint(dpy, wins[i], &d1) - && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) - manage(wins[i], &wa); - } - if (wins) - XFree(wins); - } - - scanner = 0; -} - -void -setup(void) -{ - int i; - XSetWindowAttributes wa; - Atom utf8string; - - /* clean up any zombies immediately */ - sigchld(0); - - /* init screen */ - screen = DefaultScreen(dpy); - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - root = RootWindow(dpy, screen); - drw = drw_create(dpy, screen, root, sw, sh); - if (!drw_fontset_create(drw, fonts, arrlen(fonts))) - fatal("no fonts could be loaded."); - - lrpad = drw->fonts->h; - bh = drw->fonts->h + 2; - updategeom(); - - /* init atoms */ - utf8string = XInternAtom(dpy, "UTF8_STRING", False); - wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); - wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); - wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); - - netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); - netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); - netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); - netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); - netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); - netatom[NetWMWindowOpacity] = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); - netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); - netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); - - /* init cursors */ - cursor[MouseNormal] = drw_cur_create(drw, XC_left_ptr); - cursor[MouseResize] = drw_cur_create(drw, XC_sizing); - cursor[MouseMove] = drw_cur_create(drw, XC_fleur); - - /* init appearance */ - scheme = ecalloc(arrlen(colors), sizeof(Clr *)); - for (i = 0; i < arrlen(colors); i++) - scheme[i] = drw_scm_create(drw, colors[i], 3); - - /* init bars */ - updatebars(); - updatestatus(); - - /* supporting window for NetWMCheck */ - wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); - XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, - PropModeReplace, (uchar *) &wmcheckwin, 1); - XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, - PropModeReplace, (uchar *) "dwm", 3); - XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, - PropModeReplace, (uchar *) &wmcheckwin, 1); - /* EWMH support per view */ - XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, - PropModeReplace, (uchar *) netatom, NetLast); - XDeleteProperty(dpy, root, netatom[NetClientList]); - /* select events */ - wa.cursor = cursor[MouseNormal]->cursor; - wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask - |ButtonPressMask|PointerMotionMask|EnterWindowMask - |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; - XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); - XSelectInput(dpy, root, wa.event_mask); - grabkeys(); - focus(nil); -} - - -void -sigchld(int unused) -{ - if (signal(SIGCHLD, sigchld) == SIG_ERR) - fatal("can't install SIGCHLD handler:"); - while (0 < waitpid(-1, nil, WNOHANG)); -} - -Client * -swallowing(Window w) -{ - Client *c; - Monitor *m; - - for (m = mons; m; m = m->next) { - for (c = m->clients; c; c = c->next) { - if (c->swallowing && c->swallowing->win == w) - return c; - } - } - - return nil; -} - -void -tile(Monitor *m) -{ - uint i, n, h, r, mw, my, ty; - Client *c; - - for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) - ; - - if (n == 0) - return; - - if (n > m->nmaster) - mw = m->nmaster ? (m->ww+gapx) * m->mfact : 0; - else - mw = m->ww - gapx; - - for (i = 0, my = ty = gapx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) - if (i < m->nmaster) { - r = MIN(n, m->nmaster) - i; - h = (m->wh - my)/r - gapx; - resize(c, m->wx + gapx, m->wy + my, mw - (2*c->bw) - gapx, h - (2*c->bw), 0); - if (my + HEIGHT(c) + gapx < m->wh) - my += HEIGHT(c) + gapx; - } else { - r = (n-i); - h = (m->wh - ty)/r - gapx; - resize(c, m->wx + mw + gapx, m->wy + ty, m->ww - mw - (2*c->bw) - (2*gapx), h - (2*c->bw), 0); - if (ty + HEIGHT(c) + gapx < m->wh) - ty += HEIGHT(c) + gapx; - } -} - -void -unmapnotify(XEvent *e) -{ - Client *c; - XUnmapEvent *ev = &e->xunmap; - - if ((c = wintoclient(ev->window))) { - if (ev->send_event) - setclientstate(c, WithdrawnState); - else - unmanage(c, 0); - } -} - -void -updatebars(void) -{ - Monitor *m; - XSetWindowAttributes wa = { - .override_redirect = True, - .background_pixmap = ParentRelative, - .event_mask = ButtonPressMask|ExposureMask - }; - XClassHint ch = {"dwm", "dwm"}; - for (m = mons; m; m = m->next) { - if (m->barwin) - continue; - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), - CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); - XDefineCursor(dpy, m->barwin, cursor[MouseNormal]->cursor); - XMapRaised(dpy, m->barwin); - XSetClassHint(dpy, m->barwin, &ch); - } -} - -void -updatebarpos(Monitor *m) -{ - m->wy = m->my; - m->wh = m->mh; - if (m->showbar) { - m->wh -= bh; - m->by = m->topbar ? m->wy : m->wy + m->wh; - m->wy = m->topbar ? m->wy + bh : m->wy; - } else - m->by = -bh; -} - -void -updateclientlist() -{ - Client *c; - Monitor *m; - - XDeleteProperty(dpy, root, netatom[NetClientList]); - for (m = mons; m; m = m->next) - for (c = m->clients; c; c = c->next) - XChangeProperty(dpy, root, netatom[NetClientList], - XA_WINDOW, 32, PropModeAppend, - (unsigned char *) &(c->win), 1); -} - -int -updategeom(void) -{ - int dirty = 0; - - if (XineramaIsActive(dpy)) { - int i, j, n, nn; - Client *c; - Monitor *m; - XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); - XineramaScreenInfo *unique = nil; - - for (n = 0, m = mons; m; m = m->next, n++); - /* only consider unique geometries as separate screens */ - unique = ecalloc(nn, sizeof(XineramaScreenInfo)); - for (i = 0, j = 0; i < nn; i++) - if (isuniquegeom(unique, j, &info[i])) - memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); - XFree(info); - nn = j; - if (n <= nn) { /* new monitors available */ - for (i = 0; i < (nn - n); i++) { - for (m = mons; m && m->next; m = m->next); - if (m) - m->next = createmon(); - else - mons = createmon(); - } - for (i = 0, m = mons; i < nn && m; m = m->next, i++) - if (i >= n - || unique[i].x_org != m->mx || unique[i].y_org != m->my - || unique[i].width != m->mw || unique[i].height != m->mh) - { - dirty = 1; - m->num = i; - m->mx = m->wx = unique[i].x_org; - m->my = m->wy = unique[i].y_org; - m->mw = m->ww = unique[i].width; - m->mh = m->wh = unique[i].height; - updatebarpos(m); - } - } else { /* less monitors available nn < n */ - for (i = nn; i < n; i++) { - for (m = mons; m && m->next; m = m->next); - while ((c = m->clients)) { - dirty = 1; - m->clients = c->next; - detachstack(c); - c->mon = mons; - /* attach(c); */ - attachbottom(c); - attachstack(c); - } - if (m == selmon) - selmon = mons; - cleanupmon(m); - } - } - free(unique); - } else - { /* default monitor setup */ - if (!mons) - mons = createmon(); - if (mons->mw != sw || mons->mh != sh) { - dirty = 1; - mons->mw = mons->ww = sw; - mons->mh = mons->wh = sh; - updatebarpos(mons); - } - } - if (dirty) { - selmon = mons; - selmon = wintomon(root); - } - return dirty; -} - -void -updatenumlockmask(void) -{ - uint i, j; - XModifierKeymap *modmap; - - numlockmask = 0; - modmap = XGetModifierMapping(dpy); - for (i = 0; i < 8; i++) - for (j = 0; j < modmap->max_keypermod; j++) - if (modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(dpy, XK_Num_Lock)) - numlockmask = (1 << i); - XFreeModifiermap(modmap); -} - -void -updatestatus(void) -{ - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) - strcpy(stext, "dwm-"VERSION); - drawbar(selmon); -} - -pid_t -winpid(Window w) -{ - pid_t result = 0; - - xcb_res_client_id_spec_t spec = {0}; - spec.client = w; - spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; - - xcb_generic_error_t *e = NULL; - xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec); - xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e); - - if (!r) - return (pid_t)0; - - xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r); - for (; i.rem; xcb_res_client_id_value_next(&i)) { - spec = i.data->spec; - if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { - uint32_t *t = xcb_res_client_id_value_value(i.data); - result = *t; - break; - } - } - - free(r); - - if (result == (pid_t)-1) - result = 0; - - return result; -} - -Client * -wintoclient(Window w) -{ - Client *c; - Monitor *m; - - for (m = mons; m; m = m->next) - for (c = m->clients; c; c = c->next) - if (c->win == w) - return c; - return nil; -} - -Monitor * -wintomon(Window w) -{ - int x, y; - Client *c; - Monitor *m; - - if (w == root && getrootptr(&x, &y)) - return recttomon(x, y, 1, 1); - for (m = mons; m; m = m->next) - if (w == m->barwin) - return m; - if ((c = wintoclient(w))) - return c->mon; - return selmon; -} - -/* There's no way to check accesses to destroyed windows, thus those cases are - * ignored (especially on UnmapNotify's). Other types of errors call Xlibs - * default error handler, which may call exit. */ -int -xerror(Display *dpy, XErrorEvent *ee) -{ - if (ee->error_code == BadWindow - || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) - || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) - || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) - || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) - || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) - || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) - return 0; - fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", - ee->request_code, ee->error_code); - return xerrorxlib(dpy, ee); /* may call exit */ -} - -int -xerrordummy(Display *dpy, XErrorEvent *ee) -{ - return 0; -} - -/* Startup Error handler to check if another window manager - * is already running. */ -int -xerrorstart(Display *dpy, XErrorEvent *ee) -{ - fatal("dwm: another window manager is already running"); - return -1; -} - -int -main(int argc, char *argv[]) -{ - if (argc == 2 && !strcmp("-v", argv[1])) - fatal("dwm-"VERSION); - else if (argc != 1) - fatal("usage: dwm [-v]"); - if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) - fputs("warning: no locale support\n", stderr); - if (!(dpy = XOpenDisplay(nil))) - fatal("dwm: cannot open display"); - if (!(xcon = XGetXCBConnection(dpy))) - fatal("dwm: cannot get xcb connection"); - - checkotherwm(); - setup(); - -#ifdef __OpenBSD__ - if (pledge("stdio rpath proc exec", nil) == -1) - fatal("pledge"); -#endif /* __OpenBSD__ */ - - scan(); - run(); - cleanup(); - - XCloseDisplay(dpy); - return 0; -} diff --git a/sys/cmd/dwm/dwm.h b/sys/cmd/dwm/dwm.h deleted file mode 100644 index afec1f2..0000000 --- a/sys/cmd/dwm/dwm.h +++ /dev/null @@ -1,384 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#pragma once -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* macros */ -#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) -#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) -#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ - * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) -#define MOUSEMASK (BUTTONMASK|PointerMotionMask) -#define WIDTH(X) ((X)->w + 2 * (X)->bw) -#define HEIGHT(X) ((X)->h + 2 * (X)->bw) -#define TAGMASK ((1 << arrlen(tags)) - 1) -#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) -#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) - - -/* enums */ -enum -{ - MouseNormal, - MouseResize, - MouseMove, - MouseLast, -}; /* mouse states */ - -enum -{ - SchemeNorm, - SchemeSel -}; /* color schemes */ - -enum -{ - NetSupported, - NetWMName, - NetWMState, - NetWMCheck, - NetWMFullscreen, - NetActiveWindow, - NetWMWindowType, - NetWMWindowTypeDialog, - NetWMWindowOpacity, - NetClientList, - NetLast -}; /* EWMH atoms */ - -enum -{ - WMProtocols, - WMDelete, - WMState, - WMTakeFocus, - WMLast -}; /* default atoms */ - -enum -{ - ClkTagBar, - ClkLtSymbol, - ClkStatusText, - ClkWinTitle, - ClkClientWin, - ClkRootWin, - ClkLast -}; /* clicks */ - -enum -{ - ColFg, - ColBg, - ColBorder -}; /* color scheme index */ - -typedef struct Monitor Monitor; -typedef struct Layout Layout; -typedef struct Client Client; -typedef struct Keyboard Keyboard; -typedef struct Button Button; -typedef struct Key Key; -typedef struct Rule Rule; -typedef union Arg Arg; - -union Arg { - int i; - uint ui; - float f; - void *v; -}; - -struct Button { - uint click; - uint mask; - uint button; - void (*func)(Arg *arg); - Arg arg; -}; - -struct Client { - char name[256]; - float mina, maxa; - int x, y, w, h; - int oldx, oldy, oldw, oldh; - int basew, baseh, incw, inch, maxw, maxh, minw, minh; - int bw, oldbw; - uint tags; - int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterm, noswallow; - pid_t pid; - Client *next; - Client *snext; - Client *swallowing; - Monitor *mon; - Window win; -}; - -struct Key { - uint mod; - KeySym keysym; - void (*func)(Arg *); - Arg arg; -}; - -struct Layout { - char *symbol; - void (*arrange)(Monitor *); -}; - -struct Monitor { - char ltsymbol[16]; - float mfact; - int nmaster; - int num; - int by; /* bar geometry */ - int mx, my, mw, mh; /* screen size */ - int wx, wy, ww, wh; /* window area */ - uint seltags; - uint sellt; - uint tagset[2]; - int showbar; - int topbar; - Client *clients; - Client *sel; - Client *stack; - Monitor *next; - Window barwin; - Layout *lt[2]; -}; - -struct Rule { - char *class; - char *instance; - char *title; - uint tags; - int isfloating; - int isterm; - int noswallow; - int monitor; -}; - -/* draw.c */ -typedef struct { - Cursor cursor; -} Cur; - -typedef struct Fnt { - Display *dpy; - uint h; - XftFont *xfont; - FcPattern *pattern; - struct Fnt *next; -} Fnt; - -typedef XftColor Clr; - -typedef struct { - uint w, h; - Display *dpy; - int screen; - Window root; - Drawable drawable; - GC gc; - Clr *scheme; - Fnt *fonts; -} Drw; - -/* global state */ - -extern char broken[]; -extern char stext[256]; -extern int scanner; -extern int screen; -extern int sw, sh; -extern int bh, blw; -extern int lrpad; -extern int (*xerrorxlib)(Display *, XErrorEvent *); -extern uint numlockmask; -extern void (*handler[LASTEvent]) (XEvent *); -extern int scratchtag; - -extern xcb_connection_t *xcon; - -extern Atom wmatom[WMLast], netatom[NetLast]; -extern int running; -extern Cur *cursor[MouseLast]; -extern Clr **scheme; -extern Display *dpy; -extern Drw *drw; -extern Monitor *mons, *selmon; -extern Window root, wmcheckwin; - -// ----------------------------------------------------------------------- -// function declarations - -// TODO: remove declarations that don't require global existence... -void applyrules(Client *c); -int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); -void arrange(Monitor *m); -void arrangemon(Monitor *m); -void attach(Client *c); -void enqueue(Client *c); -void attachbottom(Client *c); -void attachstack(Client *c); -void enqueuestack(Client *c); -void buttonpress(XEvent *e); -void checkotherwm(void); -void cleanup(void); -void cleanupmon(Monitor *mon); -void clientmessage(XEvent *e); -void configure(Client *c); -void configurenotify(XEvent *e); -void configurerequest(XEvent *e); -Monitor *createmon(void); -void destroynotify(XEvent *e); -void detach(Client *c); -void detachstack(Client *c); -Monitor *dirtomon(int dir); -void drawbar(Monitor *m); -void drawbars(void); -void enternotify(XEvent *e); -void expose(XEvent *e); -void focus(Client *c); -void focusin(XEvent *e); -void focusmon(Arg *arg); -void focusstack(Arg *arg); -void focusdirection(Arg *arg); -void rotatestack(Arg *arg); -Atom getatomprop(Client *c, Atom prop); -int getrootptr(int *x, int *y); -long getstate(Window w); -int gettextprop(Window w, Atom atom, char *text, uint size); -void grabbuttons(Client *c, int focused); -void grabkeys(void); -void incnmaster(Arg *arg); -void keypress(XEvent *e); -void killclient(Arg *arg); -void manage(Window w, XWindowAttributes *wa); -void mappingnotify(XEvent *e); -void maprequest(XEvent *e); -void monocle(Monitor *m); -void motionnotify(XEvent *e); -void movemouse(Arg *arg); -Client *nexttiled(Client *c); -void pop(Client *); -void propertynotify(XEvent *e); -void quit(Arg *arg); -Monitor *recttomon(int x, int y, int w, int h); -void resize(Client *c, int x, int y, int w, int h, int interact); -void resizeclient(Client *c, int x, int y, int w, int h); -void resizemouse(Arg *arg); -void restack(Monitor *m); -void run(void); -void scan(void); -int sendevent(Client *c, Atom proto); -void sendtomon(Client *c, Monitor *m); -void setclientstate(Client *c, long state); -void setfocus(Client *c); -void setfullscreen(Client *c, int fullscreen); -void setlayout(Arg *arg); -void setmfact(Arg *arg); -void setup(void); -void seturgent(Client *c, int urg); -void showhide(Client *c); -void sigchld(int unused); -void swallow(Client *p, Client *c); -Client *swallowing(Window w); -void spawn(Arg *arg); -void tag(Arg *arg); -void tagmon(Arg *arg); -Client *termof(Client *c); -void tile(Monitor *); -void togglebar(Arg *arg); -void togglefocus(Arg *arg); -void togglefloating(Arg *arg); -void togglescratch(Arg *arg); -void toggletag(Arg *arg); -void toggleview(Arg *arg); -void unfocus(Client *c, int setfocus); -void unmanage(Client *c, int destroyed); -void unmapnotify(XEvent *e); -void unswallow(Client *c); -void updatebarpos(Monitor *m); -void updatebars(void); -void updateclientlist(void); -int updategeom(void); -void updatenumlockmask(void); -void updatesizehints(Client *c); -void updatestatus(void); -void updatetitle(Client *c); -void updatewindowtype(Client *c); -void updatewmhints(Client *c); -void view(Arg *arg); -pid_t winpid(Window w); -Client *wintoclient(Window w); -Monitor *wintomon(Window w); -int xerror(Display *dpy, XErrorEvent *ee); -int xerrordummy(Display *dpy, XErrorEvent *ee); -int xerrorstart(Display *dpy, XErrorEvent *ee); -void zoom(Arg *arg); - -#include "config.h" - -/* draw.c */ - -/* Drawable abstraction */ -Drw *drw_create(Display *dpy, int screen, Window win, uint w, uint h); -void drw_resize(Drw *drw, uint w, uint h); -void drw_free(Drw *drw); - -/* Fnt abstraction */ -Fnt *drw_fontset_create(Drw* drw, char *fonts[], size_t fontcount); -void drw_fontset_free(Fnt* set); -uint drw_fontset_getwidth(Drw *drw, char *text); -void drw_font_getexts(Fnt *font, char *text, uint len, uint *w, uint *h); - -/* Colorscheme abstraction */ -void drw_clr_create(Drw *drw, Clr *dest, char *clrname); -Clr *drw_scm_create(Drw *drw, char *clrnames[], size_t clrcount); - -/* Cursor abstraction */ -Cur *drw_cur_create(Drw *drw, int shape); -void drw_cur_free(Drw *drw, Cur *cursor); - -/* Drawing context manipulation */ -void drw_setfontset(Drw *drw, Fnt *set); -void drw_setscheme(Drw *drw, Clr *scm); - -/* Drawing functions */ -void drw_rect(Drw *drw, int x, int y, uint w, uint h, int filled, int invert); -int drw_text(Drw *drw, int x, int y, uint w, uint h, uint lpad, char *text, int invert); - -/* Map functions */ -void drw_map(Drw *drw, Window win, int x, int y, uint w, uint h); - -/* util.c */ -void fatal(char *fmt, ...); -void *ecalloc(size_t nmemb, size_t size); -pid_t getparentproc(pid_t p); -pid_t isdescendent(pid_t p, pid_t c); diff --git a/sys/cmd/dwm/hook.c b/sys/cmd/dwm/hook.c deleted file mode 100644 index 9758965..0000000 --- a/sys/cmd/dwm/hook.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "dwm.h" - -int scratchtag = 1 << arrlen(tags); - -void -focusmon(Arg *arg) -{ - Monitor *m; - - if (!mons->next) - return; - if ((m = dirtomon(arg->i)) == selmon) - return; - unfocus(selmon->sel, 0); - selmon = m; - focus(nil); -} - -void -focusstack(Arg *arg) -{ - Client *c = nil, *i; - - if (!selmon->sel) - return; - if (arg->i > 0) { - for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); - if(!c) - for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next); - } else { - for(i = selmon->clients; i != selmon->sel; i = i->next) - if(ISVISIBLE(i)) - c = i; - if(!c) - for(; i; i = i->next) - if (ISVISIBLE(i)) - c = i; - } - if(c) { - focus(c); - restack(selmon); - } -} - -void -focusdirection(Arg *arg) -{ - Monitor *m; - Client *it, *c; - int x, y, cx, cy; - - if(!selmon || !selmon->sel) - return; - - c = selmon->sel; - x = c->x, y = c->y; - - c = nil; - switch(arg->i) { - case 'l': - cx = INT_MIN; - cy = y; - for(m=mons; m; m=m->next) { - for(it=m->clients; it; it = it->next) { - if(ISVISIBLE(it) && (it->x < x)) { - if((it->x > cx) || ((it->x == cx) && abs(y-it->y) < abs(y-cy))) { - c = it; - cx = it->x; - cy = it->y; - } - } - } - } - break; - - case 'r': - cx = INT_MAX; - cy = y; - for(m=mons; m; m=m->next) { - for(it=m->clients; it; it = it->next) { - if(ISVISIBLE(it) && (it->x > x)) { - if((it->x < cx) || ((it->x == cx) && abs(y-it->y) < abs(y-cy))) { - c = it; - cx = it->x; - cy = it->y; - } - } - } - } - break; - - case 'u': - cx = x; - cy = INT_MIN; - for(m=mons; m; m=m->next) { - for(it=m->clients; it; it = it->next) { - if(ISVISIBLE(it) && (it->y < y)) { - if((it->y > cy) || ((it->y == cy) && abs(x-it->x) < abs(x-cx))) { - c = it; - cx = it->x; - cy = it->y; - } - } - } - } - break; - - case 'd': - cx = x; - cy = INT_MAX; - for(m=mons; m; m=m->next) { - for(it=m->clients; it; it = it->next) { - if(ISVISIBLE(it) && (it->y > y)) { - if((it->y < cy) || ((it->y == cy) && abs(x-it->x) < abs(x-cx))) { - c = it; - cx = it->x; - cy = it->y; - } - } - } - } - break; - - default: - ; - } - - if(c) { - focus(c); - restack(selmon); - if(c->mon != selmon) - restack(c->mon); - } -} - -void -rotatestack(Arg *arg) -{ - Client *c = nil, *f; - - if (!selmon->sel) - return; - - f = selmon->sel; - if (arg->i > 0) { - for (c = nexttiled(selmon->clients); c && nexttiled(c->next); c = nexttiled(c->next)) - ; - - if (c) { - detach(c); - attach(c); - detachstack(c); - attachstack(c); - } - } else { - if ((c = nexttiled(selmon->clients))) { - detach(c); - enqueue(c); - detachstack(c); - enqueuestack(c); - } - } - - if (c) { - arrange(selmon); - focus(f); - restack(selmon); - } -} - - -void -incnmaster(Arg *arg) -{ - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); - arrange(selmon); -} - -void -killclient(Arg *arg) -{ - if (!selmon->sel) - return; - if (!sendevent(selmon->sel, wmatom[WMDelete])) { - XGrabServer(dpy); - XSetErrorHandler(xerrordummy); - XSetCloseDownMode(dpy, DestroyAll); - XKillClient(dpy, selmon->sel->win); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - } -} - -void -movemouse(Arg *arg) -{ - int x, y, ocx, ocy, nx, ny; - Client *c; - Monitor *m; - XEvent ev; - Time lasttime = 0; - - if (!(c = selmon->sel)) - return; - if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ - return; - restack(selmon); - ocx = c->x; - ocy = c->y; - if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[MouseMove]->cursor, CurrentTime) != GrabSuccess) - return; - if (!getrootptr(&x, &y)) - return; - do { - XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); - switch(ev.type) { - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - if ((ev.xmotion.time - lasttime) <= (1000 / 60)) - continue; - lasttime = ev.xmotion.time; - - nx = ocx + (ev.xmotion.x - x); - ny = ocy + (ev.xmotion.y - y); - if (abs(selmon->wx - nx) < snap) - nx = selmon->wx; - else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) - nx = selmon->wx + selmon->ww - WIDTH(c); - if (abs(selmon->wy - ny) < snap) - ny = selmon->wy; - else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) - ny = selmon->wy + selmon->wh - HEIGHT(c); - if (!c->isfloating && selmon->lt[selmon->sellt]->arrange - && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) - togglefloating(nil); - if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) - resize(c, nx, ny, c->w, c->h, 1); - break; - } - } while (ev.type != ButtonRelease); - XUngrabPointer(dpy, CurrentTime); - if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { - sendtomon(c, m); - selmon = m; - focus(nil); - } -} - -void -quit(Arg *arg) -{ - running = 0; -} - -void -resizemouse(Arg *arg) -{ - int ocx, ocy, nw, nh; - Client *c; - Monitor *m; - XEvent ev; - Time lasttime = 0; - - if (!(c = selmon->sel)) - return; - if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ - return; - restack(selmon); - ocx = c->x; - ocy = c->y; - if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[MouseResize]->cursor, CurrentTime) != GrabSuccess) - return; - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); - do { - XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); - switch(ev.type) { - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - if ((ev.xmotion.time - lasttime) <= (1000 / 60)) - continue; - lasttime = ev.xmotion.time; - - nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); - nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); - if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww - && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) - { - if (!c->isfloating && selmon->lt[selmon->sellt]->arrange - && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) - togglefloating(nil); - } - if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) - resize(c, c->x, c->y, nw, nh, 1); - break; - } - } while (ev.type != ButtonRelease); - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); - XUngrabPointer(dpy, CurrentTime); - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); - if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { - sendtomon(c, m); - selmon = m; - focus(nil); - } -} - -void -setlayout(Arg *arg) -{ - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; - if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; - strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); - if (selmon->sel) - arrange(selmon); - else - drawbar(selmon); -} - -/* arg > 1.0 will set mfact absolutely */ -void -setmfact(Arg *arg) -{ - float f; - - if (!arg || !selmon->lt[selmon->sellt]->arrange) - return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; - if (f < 0.05 || f > 0.95) - return; - selmon->mfact = f; - arrange(selmon); -} - -void -spawn(Arg *arg) -{ - selmon->tagset[selmon->seltags] &= ~scratchtag; - - if (fork() == 0) { - if (dpy) - close(ConnectionNumber(dpy)); - setsid(); - execvp(((char **)arg->v)[0], (char **)arg->v); - fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]); - perror(" failed"); - exit(EXIT_SUCCESS); - } -} - -void -tag(Arg *arg) -{ - if (selmon->sel && arg->ui & TAGMASK) { - selmon->sel->tags = arg->ui & TAGMASK; - focus(nil); - arrange(selmon); - } -} - -void -tagmon(Arg *arg) -{ - if (!selmon->sel || !mons->next) - return; - sendtomon(selmon->sel, dirtomon(arg->i)); -} - -void -togglebar(Arg *arg) -{ - selmon->showbar = !selmon->showbar; - updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); - arrange(selmon); -} - -void -togglefloating(Arg *arg) -{ - if (!selmon->sel) - return; - if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ - return; - selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; - if (selmon->sel->isfloating) - resize(selmon->sel, selmon->sel->x, selmon->sel->y, - selmon->sel->w, selmon->sel->h, 0); - arrange(selmon); -} - -void -togglescratch(Arg *arg) -{ - Client *c; - uint f = 0; - - for(c = selmon->clients; c && !(f = (c->tags & scratchtag)); c = c->next) - ; - - if(f) { - f = selmon->tagset[selmon->seltags] ^ scratchtag; - if(f) { - selmon->tagset[selmon->seltags] = f; - focus(nil); - arrange(selmon); - } - if(ISVISIBLE(c)) { - focus(c); - restack(selmon); - } - } else - spawn(arg); - -} - -void -toggletag(Arg *arg) -{ - uint newtags; - if (!selmon->sel) - return; - - newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); - if (newtags) { - selmon->sel->tags = newtags; - focus(nil); - arrange(selmon); - } -} - -void -togglefocus(Arg *arg) -{ - if (selmon->sel) - setfullscreen(selmon->sel, !selmon->sel->isfullscreen); - - togglebar(arg); -} - -void -toggleview(Arg *arg) -{ - uint newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); - - if (newtagset) { - selmon->tagset[selmon->seltags] = newtagset; - focus(nil); - arrange(selmon); - } -} - -void -view(Arg *arg) -{ - if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) - return; - selmon->seltags ^= 1; /* toggle sel tagset */ - if (arg->ui & TAGMASK) - selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focus(nil); - arrange(selmon); -} - -void -zoom(Arg *arg) -{ - Client *c = selmon->sel; - - if (!selmon->lt[selmon->sellt]->arrange - || (selmon->sel && selmon->sel->isfloating)) - return; - if (c == nexttiled(selmon->clients)) - if (!c || !(c = nexttiled(c->next))) - return; - pop(c); -} diff --git a/sys/cmd/dwm/rules.mk b/sys/cmd/dwm/rules.mk deleted file mode 100644 index 79c4548..0000000 --- a/sys/cmd/dwm/rules.mk +++ /dev/null @@ -1,28 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := \ - $(d)/drw.c \ - $(d)/hook.c \ - $(d)/client.c \ - $(d)/util.c \ - $(d)/dwm.c -BINS_$(d) := $(d)/dwm - -include share/paths.mk - -# Local rules -include share/dynamic.mk -$(BINS_$(d)): TCFLAGS = \ - `$(PKG) --cflags fontconfig` \ - `$(PKG) --cflags freetype2` -$(BINS_$(d)): TCLIBS = \ - `$(PKG) --libs fontconfig` \ - `$(PKG) --libs freetype2` \ - -lX11 -lXinerama -lXft -lX11-xcb -lxcb -lxcb-res - -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/libutf/libutf.a $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/dwm/util.c b/sys/cmd/dwm/util.c deleted file mode 100644 index 0db71cc..0000000 --- a/sys/cmd/dwm/util.c +++ /dev/null @@ -1,66 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "dwm.h" - -void -fatal(char *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - - if(fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - - exit(1); -} - -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - fatal("calloc:"); - return p; -} - -pid_t -getparentprocess(pid_t p) -{ - uint v = 0; - -#if defined(__linux__) - io·Stream *f; - char buf[256]; - snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); - - if (!(f = fopen(buf, "r"))) - return (pid_t)0; - - if (fscanf(f, "%*u %*s %*c %u", (unsigned *)&v) != 1) - v = (pid_t)0; - fclose(f); -#elif defined(__FreeBSD__) - struct kinfo_proc *proc = kinfo_getproc(p); - if (!proc) - return (pid_t)0; - - v = proc->ki_ppid; - free(proc); -#endif - return (pid_t)v; -} - -int -isdescendent(pid_t p, pid_t c) -{ - while (p != c && c != 0) - c = getparentprocess(c); - - return (int)c; -} diff --git a/sys/cmd/filter/filter.c b/sys/cmd/filter/filter.c deleted file mode 100644 index abc9a88..0000000 --- a/sys/cmd/filter/filter.c +++ /dev/null @@ -1,104 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include -#include - -#include -#include - -#define FLAG(x) (flag[(x)-'a']) - -static void filter(const char *, const char *); -static void usage(void); - -static int match = 0; -static int flag[26]; -static struct stat old, new; - -static -void -filter(const char *path, const char *name) -{ - struct stat st, ln; - - if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ - && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ - && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ - && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ - && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ - && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ - && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ - && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ - && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ - && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ - && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ - && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ - && (!FLAG('s') || st.st_size > 0) /* not empty */ - && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ - && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ - && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ - if (FLAG('q')) - exit(0); - match = 1; - puts(name); - } -} - -static void -usage(void) -{ - fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " - "[-n file] [-o file] [file...]\n", argv0); - exit(2); /* like test(1) return > 1 on error */ -} - -int -main(int argc, char *argv[]) -{ - struct dirent *d; - char path[PATH_MAX], *line = NULL, *file; - size_t linesiz = 0; - ssize_t n; - DIR *dir; - int r; - - ARGBEGIN { - case 'n': /* newer than file */ - case 'o': /* older than file */ - file = EARGF(usage()); - if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) - perror(file); - break; - default: - /* miscellaneous operators */ - if (strchr("abcdefghlpqrsuvwx", ARGC())) - FLAG(ARGC()) = 1; - else - usage(); /* unknown flag */ - } ARGEND; - - if (!argc) { - /* read list from stdin */ - while ((n = getline(&line, &linesiz, stdin)) > 0) { - if (n && line[n - 1] == '\n') - line[n - 1] = '\0'; - filter(line, line); - } - free(line); - } else { - for (; argc; argc--, argv++) { - if (FLAG('l') && (dir = opendir(*argv))) { - /* filter directory contents */ - while ((d = readdir(dir))) { - r = snprintf(path, sizeof path, "%s/%s", - *argv, d->d_name); - if (r >= 0 && (size_t)r < sizeof path) - filter(path, d->d_name); - } - closedir(dir); - } else { - filter(*argv, *argv); - } - } - } - return match ? 0 : 1; -} diff --git a/sys/cmd/filter/rules.mk b/sys/cmd/filter/rules.mk deleted file mode 100644 index 31bb257..0000000 --- a/sys/cmd/filter/rules.mk +++ /dev/null @@ -1,13 +0,0 @@ -include share/push.mk - -# Local sources -SRCS_$(d) := $(d)/filter.c -BINS_$(d) := $(d)/filter - -include share/paths.mk - -# Local rules -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/ic/LICENSE b/sys/cmd/ic/LICENSE deleted file mode 100644 index a5816a8..0000000 --- a/sys/cmd/ic/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -MIT/X Consortium License - -(C)opyright 2014-2018 Hiltjo Posthuma -(C)opyright 2005-2006 Anselm R. Garbe -(C)opyright 2005-2011 Nico Golde - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/sys/cmd/ic/ic.1 b/sys/cmd/ic/ic.1 deleted file mode 100644 index 3302dad..0000000 --- a/sys/cmd/ic/ic.1 +++ /dev/null @@ -1,100 +0,0 @@ -.TH II 1 ic\-VERSION -.SH NAME -ic \- irc it or irc improved -.SH DESCRIPTION -.B ic -is a minimalistic FIFO and filesystem based IRC client. -It creates an irc directory tree with server, channel and -nick name directories. -In every directory a FIFO file (in) and normal file (out) -is placed. This will be for example ~/irc/irc.freenode.net/. -The in file is used to communicate with the servers and the out -files includes the server messages. For every channel and every nick -name there will be new in and out files. -The basic idea of this is to be able to communicate with an IRC -server with basic command line tools. -For example if you will join a channel just do echo "/j #channel" > in -and ic creates a new channel directory with in and out file. -.SH SYNOPSIS -.B ic -.RB < \-s -.IR servername > -.RB [ \-p -.IR port ] -.RB [ \-k -.IR "environment variable" ] -.RB [ \-i -.IR prefix ] -.RB [ \-n -.IR nickname ] -.RB [ \-f -.IR realname ] -.RB < \-u -.IR sockname > -.SH OPTIONS -.TP -.BI \-s " servername" -server to connect to, for example: irc.freenode.net -.TP -.BI \-u " sockname" -connect to a UNIX domain socket instead of directly to a server. -.TP -.BI \-p " port" -lets you override the default port (6667) -.TP -.BI \-k " environment variable" -lets you specify an environment variable that contains your IRC password, e.g. IIPASS="foobar" ic -k IIPASS. -This is done in order to prevent other users from eavesdropping the server password via the process list. -.TP -.BI \-i " prefix" -lets you override the default irc path (~/irc) -.TP -.BI \-n " nickname" -lets you override the default nick ($USER) -.TP -.BI \-f " realname" -lets you specify your real name associated with your nick -.SH DIRECTORIES -.TP -.B ~/irc -In this directory the irc tree will be created. In this directory you -will find a directory for your server (default: irc.freenode.net) in -which the FIFO and the output file will be stored. -If you join a channel a new directory with the name of the channel -will be created in the ~/irc/$servername/ directory. -.SH COMMANDS -.TP -.BI /a " []" -mark yourself as away -.TP -.BI /j " #channel/nickname []" -join a channel or open private conversation with user -.TP -.BI /l " [reason]" -leave a channel or query -.TP -.BI /n " nick" -change the nick name -.TP -.BI /q " [reason]" -quit ic -.TP -.BI /t " topic" -set the topic of a channel -.SH RAW COMMANDS -.LP -Everything which is not a command will be posted into the channel or to the server. -So if you need /who just write /WHO as described in RFC#1459 to the server in FIFO. -.SH SSL PROTOCOL SUPPORT -.LP -For TLS/SSL protocol support you can connect to a local tunnel, for example with stunnel or socat. -.SH CONTACT -.LP -Subscribe to the mailinglist and write to dev (at) suckless (dot) org for suggestions, fixes, etc. -.SH AUTHORS -ic engineers, see LICENSE file -.SH SEE ALSO -.BR echo (1), -.BR tail (1) -.SH BUGS -Please report them! diff --git a/sys/cmd/ic/ic.c b/sys/cmd/ic/ic.c deleted file mode 100644 index 7fc37d8..0000000 --- a/sys/cmd/ic/ic.c +++ /dev/null @@ -1,878 +0,0 @@ -/* See LICENSE file for license details. */ -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -size_t strlcpy(char *, const char *, size_t); - -#define IRC_CHANNEL_MAX 200 -#define IRC_MSG_MAX 512 /* guaranteed to be <= than PIPE_BUF */ -#define PING_TIMEOUT 300 - -enum { TOK_NICKSRV = 0, TOK_USER, TOK_CMD, TOK_CHAN, TOK_ARG, TOK_TEXT, TOK_LAST }; - -typedef struct Channel Channel; -struct Channel { - int fdin; - char name[IRC_CHANNEL_MAX]; /* channel name (normalized) */ - char inpath[PATH_MAX]; /* input path */ - char outpath[PATH_MAX]; /* output path */ - Channel *next; -}; - -static Channel * channel_add(const char *); -static Channel * channel_find(const char *); -static Channel * channel_join(const char *); -static void channel_leave(Channel *); -static Channel * channel_new(const char *); -static void channel_normalize_name(char *); -static void channel_normalize_path(char *); -static int channel_open(Channel *); -static void channel_print(Channel *, const char *); -static int channel_reopen(Channel *); -static void channel_rm(Channel *); - -static void create_dirtree(const char *); -static void create_filepath(char *, size_t, const char *, const char *, const char *); -static void ewritestr(int, const char *); -static void handle_channels_input(int, Channel *); -static void handle_server_output(int); -static int isnumeric(const char *); -static void loginkey(int, const char *); -static void loginuser(int, const char *, const char *); -static void proc_channels_input(int, Channel *, char *); -static void proc_channels_privmsg(int, Channel *, char *); -static void proc_server_cmd(int, char *); -static int read_line(int, char *, size_t); -static void run(int, const char *); -static void setup(void); -static void sighandler(int); -static int tcpopen(const char *, const char *); -static size_t tokenize(char **, size_t, char *, int); -static int udsopen(const char *); -static void usage(void); - -static int isrunning = 1; -static time_t last_response = 0; -static Channel *channels = nil; -static Channel *channelmaster = nil; -static char nick[32], _nick[arrlen(nick)]; /* active nickname at runtime */ -static char ircpath[PATH_MAX]; /* irc dir (-i) */ -static char msg[IRC_MSG_MAX]; /* message buf used for communication */ - -static -void -usage(void) -{ - fprintf(stderr, "usage: %s <-s host> [-i ] [-p ] " - "[-u ] [-n ] [-k ] " - "[-f ]\n", argv0); - exit(1); -} - -static -void -ewritestr(int fd, const char *s) -{ - size_t len, off = 0; - int w = -1; - - len = strlen(s); - for (off = 0; off < len; off += w) { - if ((w = write(fd, s + off, len - off)) == -1) - break; - off += w; - } - if (w == -1) { - fprintf(stderr, "%s: write: %s\n", argv0, strerror(errno)); - exit(1); - } -} - -/* creates directories bottom-up, if necessary */ -static -void -create_dirtree(const char *dir) -{ - char tmp[PATH_MAX], *p; - struct stat st; - size_t len; - - strlcpy(tmp, dir, sizeof(tmp)); - len = strlen(tmp); - if (len > 0 && tmp[len - 1] == '/') - tmp[len - 1] = '\0'; - - if ((stat(tmp, &st) != -1) && S_ISDIR(st.st_mode)) - return; /* dir exists */ - - for (p = tmp + 1; *p; p++) { - if (*p != '/') - continue; - *p = '\0'; - mkdir(tmp, S_IRWXU); - *p = '/'; - } - mkdir(tmp, S_IRWXU); -} - -static -void -channel_normalize_path(char *s) -{ - for (; *s; s++) { - if (isalpha((unsigned char)*s)) - *s = tolower((unsigned char)*s); - else if (!isdigit((unsigned char)*s) && !strchr(".#&+!-", *s)) - *s = '_'; - } -} - -static -void -channel_normalize_name(char *s) -{ - char *p; - - while (*s == '&' || *s == '#') - s++; - for (p = s; *s; s++) { - if (!strchr(" ,&#\x07", *s)) { - *p = *s; - p++; - } - } - *p = '\0'; -} - -static -void -create_filepath(char *filepath, size_t len, const char *path, - const char *channel, const char *suffix) -{ - int r; - - if (channel[0]) { - r = snprintf(filepath, len, "%s/%s", path, channel); - if (r < 0 || (size_t)r >= len) - goto error; - create_dirtree(filepath); - r = snprintf(filepath, len, "%s/%s/%s", path, channel, suffix); - if (r < 0 || (size_t)r >= len) - goto error; - } else { - r = snprintf(filepath, len, "%s/%s", path, suffix); - if (r < 0 || (size_t)r >= len) - goto error; - } - return; - -error: - fprintf(stderr, "%s: path to irc directory too long\n", argv0); - exit(1); -} - -static -int -channel_open(Channel *c) -{ - int fd; - struct stat st; - - /* make "in" fifo if it doesn't exist already. */ - if (lstat(c->inpath, &st) != -1) { - if (!(st.st_mode & S_IFIFO)) - return -1; - } else if (mkfifo(c->inpath, S_IRWXU)) { - return -1; - } - c->fdin = -1; - fd = open(c->inpath, O_RDONLY | O_NONBLOCK, 0); - if (fd == -1) - return -1; - c->fdin = fd; - - return 0; -} - -static -int -channel_reopen(Channel *c) -{ - if (c->fdin > 2) { - close(c->fdin); - c->fdin = -1; - } - return channel_open(c); -} - -static -Channel * -channel_new(const char *name) -{ - Channel *c; - char channelpath[PATH_MAX]; - - strlcpy(channelpath, name, sizeof(channelpath)); - channel_normalize_path(channelpath); - - if (!(c = calloc(1, sizeof(Channel)))) { - fprintf(stderr, "%s: calloc: %s\n", argv0, strerror(errno)); - exit(1); - } - - strlcpy(c->name, name, sizeof(c->name)); - channel_normalize_name(c->name); - - create_filepath(c->inpath, sizeof(c->inpath), ircpath, - channelpath, "in"); - create_filepath(c->outpath, sizeof(c->outpath), ircpath, - channelpath, "out"); - return c; -} - -static -Channel * -channel_find(const char *name) -{ - Channel *c; - char chan[IRC_CHANNEL_MAX]; - - strlcpy(chan, name, sizeof(chan)); - channel_normalize_name(chan); - for (c = channels; c; c = c->next) { - if (!strcmp(chan, c->name)) - return c; /* already handled */ - } - return nil; -} - -static -Channel * -channel_add(const char *name) -{ - Channel *c; - - c = channel_new(name); - if (channel_open(c) == -1) { - fprintf(stderr, "%s: cannot create channel: %s: %s\n", - argv0, name, strerror(errno)); - free(c); - return nil; - } - if (!channels) { - channels = c; - } else { - c->next = channels; - channels = c; - } - return c; -} - -static -Channel * -channel_join(const char *name) -{ - Channel *c; - - if (!(c = channel_find(name))) - c = channel_add(name); - return c; -} - -static -void -channel_rm(Channel *c) -{ - Channel *p; - - if (channels == c) { - channels = channels->next; - } else { - for (p = channels; p && p->next != c; p = p->next) - ; - if (p && p->next == c) - p->next = c->next; - } - free(c); -} - -static -void -channel_leave(Channel *c) -{ - if (c->fdin > 2) { - close(c->fdin); - c->fdin = -1; - } - /* remove "in" file on leaving the channel */ - unlink(c->inpath); - channel_rm(c); -} - -static -void -loginkey(int ircfd, const char *key) -{ - snprintf(msg, sizeof(msg), "PASS %s\r\n", key); - ewritestr(ircfd, msg); -} - -static -void -loginuser(int ircfd, const char *host, const char *fullname) -{ - snprintf(msg, sizeof(msg), "NICK %s\r\nUSER %s localhost %s :%s\r\n", - nick, nick, host, fullname); - puts(msg); - ewritestr(ircfd, msg); -} - -static -int -udsopen(const char *uds) -{ - struct sockaddr_un sun; - size_t len; - int fd; - - if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - fprintf(stderr, "%s: socket: %s\n", argv0, strerror(errno)); - exit(1); - } - - sun.sun_family = AF_UNIX; - if(strlcpy(sun.sun_path, uds, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { - fprintf(stderr, "%s: UNIX domain socket path truncation\n", argv0); - exit(1); - } - len = strlen(sun.sun_path) + 1 + sizeof(sun.sun_family); - if (connect(fd, (struct sockaddr *)&sun, len) == -1) { - fprintf(stderr, "%s: connect: %s\n", argv0, strerror(errno)); - exit(1); - } - return fd; -} - -static -int -tcpopen(const char *host, const char *service) -{ - struct addrinfo hints, *res = nil, *rp; - int fd = -1, e; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ - hints.ai_flags = AI_NUMERICSERV; /* avoid name lookup for port */ - hints.ai_socktype = SOCK_STREAM; - - if ((e = getaddrinfo(host, service, &hints, &res))) { - fprintf(stderr, "%s: getaddrinfo: %s\n", argv0, gai_strerror(e)); - exit(1); - } - - for (rp = res; rp; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd == -1) - continue; - if (connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) { - close(fd); - fd = -1; - continue; - } - break; /* success */ - } - if (fd == -1) { - fprintf(stderr, "%s: could not connect to %s:%s: %s\n", - argv0, host, service, strerror(errno)); - exit(1); - } - - freeaddrinfo(res); - return fd; -} - -static -int -isnumeric(const char *s) -{ - errno = 0; - strtol(s, nil, 10); - return errno == 0; -} - -static -size_t -tokenize(char **result, size_t reslen, char *str, int delim) -{ - char *p = nil, *n = nil; - size_t i = 0; - - for (n = str; *n == ' '; n++) - ; - p = n; - while (*n != '\0') { - if (i >= reslen) - return 0; - if (i > TOK_CHAN - TOK_CMD && result[0] && isnumeric(result[0])) - delim = ':'; /* workaround non-RFC compliant messages */ - if (*n == delim) { - *n = '\0'; - result[i++] = p; - p = ++n; - } else { - n++; - } - } - /* add last entry */ - if (i < reslen && p < n && p && *p) - result[i++] = p; - return i; /* number of tokens */ -} - -static -void -channel_print(Channel *c, const char *buf) -{ - FILE *fp = nil; - time_t t = time(nil); - - if (!(fp = fopen(c->outpath, "a"))) - return; - fprintf(fp, "%lu %s\n", (unsigned long)t, buf); - fclose(fp); -} - -static -void -proc_channels_privmsg(int ircfd, Channel *c, char *buf) -{ - snprintf(msg, sizeof(msg), "<%s> %s", nick, buf); - channel_print(c, msg); - snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", c->name, buf); - ewritestr(ircfd, msg); -} - -static -void -proc_channels_input(int ircfd, Channel *c, char *buf) -{ - char *p = nil; - size_t buflen; - - if (buf[0] == '\0') - return; - if (buf[0] != '/') { - proc_channels_privmsg(ircfd, c, buf); - return; - } - - msg[0] = '\0'; - if ((buflen = strlen(buf)) < 2) - return; - if (buf[2] == ' ' || buf[2] == '\0') { - switch (buf[1]) { - case 'j': /* join */ - if (buflen < 3) - return; - if ((p = strchr(&buf[3], ' '))) /* password parameter */ - *p = '\0'; - if ((buf[3] == '#') || (buf[3] == '&') || (buf[3] == '+') || - (buf[3] == '!')) - { - /* password protected channel */ - if (p) - snprintf(msg, sizeof(msg), "JOIN %s %s\r\n", &buf[3], p + 1); - else - snprintf(msg, sizeof(msg), "JOIN %s\r\n", &buf[3]); - channel_join(&buf[3]); - } else if (p) { - if ((c = channel_join(&buf[3]))) - proc_channels_privmsg(ircfd, c, p + 1); - return; - } - break; - case 't': /* topic */ - if (buflen >= 3) - snprintf(msg, sizeof(msg), "TOPIC %s :%s\r\n", c->name, &buf[3]); - break; - case 'a': /* away */ - if (buflen >= 3) { - snprintf(msg, sizeof(msg), "-!- %s is away \"%s\"", nick, &buf[3]); - channel_print(c, msg); - } - if (buflen >= 3) - snprintf(msg, sizeof(msg), "AWAY :%s\r\n", &buf[3]); - else - snprintf(msg, sizeof(msg), "AWAY\r\n"); - break; - case 'n': /* change nick */ - if (buflen >= 3) { - strlcpy(_nick, &buf[3], sizeof(_nick)); - snprintf(msg, sizeof(msg), "NICK %s\r\n", &buf[3]); - } - break; - case 'l': /* leave */ - if (c == channelmaster) - return; - if (buflen >= 3) - snprintf(msg, sizeof(msg), "PART %s :%s\r\n", c->name, &buf[3]); - else - snprintf(msg, sizeof(msg), - "PART %s :leaving\r\n", c->name); - ewritestr(ircfd, msg); - channel_leave(c); - return; - break; - case 'q': /* quit */ - if (buflen >= 3) - snprintf(msg, sizeof(msg), "QUIT :%s\r\n", &buf[3]); - else - snprintf(msg, sizeof(msg), - "QUIT %s\r\n", "bye"); - ewritestr(ircfd, msg); - isrunning = 0; - return; - break; - default: /* raw IRC command */ - snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]); - break; - } - } else { - /* raw IRC command */ - snprintf(msg, sizeof(msg), "%s\r\n", &buf[1]); - } - if (msg[0] != '\0') - ewritestr(ircfd, msg); -} - -static -void -proc_server_cmd(int fd, char *buf) -{ - Channel *c; - const char *channel; - char *argv[TOK_LAST], *cmd = nil, *p = nil; - unsigned int i; - - if (!buf || buf[0] == '\0') - return; - - /* clear tokens */ - for (i = 0; i < TOK_LAST; i++) - argv[i] = nil; - - /* check prefix */ - if (buf[0] == ':') { - if (!(p = strchr(buf, ' '))) - return; - *p = '\0'; - for (++p; *p == ' '; p++) - ; - cmd = p; - argv[TOK_NICKSRV] = &buf[1]; - if ((p = strchr(buf, '!'))) { - *p = '\0'; - argv[TOK_USER] = ++p; - } - } else { - cmd = buf; - } - - /* remove CRLFs */ - for (p = cmd; p && *p != '\0'; p++) { - if (*p == '\r' || *p == '\n') - *p = '\0'; - } - - if ((p = strchr(cmd, ':'))) { - *p = '\0'; - argv[TOK_TEXT] = ++p; - } - - tokenize(&argv[TOK_CMD], TOK_LAST - TOK_CMD, cmd, ' '); - - if (!argv[TOK_CMD] || !strcmp("PONG", argv[TOK_CMD])) { - return; - } else if (!strcmp("PING", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "PONG %s\r\n", argv[TOK_TEXT]); - ewritestr(fd, msg); - return; - } else if (!argv[TOK_NICKSRV] || !argv[TOK_USER]) { - /* server command */ - snprintf(msg, sizeof(msg), "%s%s", - argv[TOK_ARG] ? argv[TOK_ARG] : "", - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - channel_print(channelmaster, msg); - return; /* don't process further */ - } else if (!strcmp("ERROR", argv[TOK_CMD])) - snprintf(msg, sizeof(msg), "-!- error %s", - argv[TOK_TEXT] ? argv[TOK_TEXT] : "unknown"); - else if (!strcmp("JOIN", argv[TOK_CMD]) && (argv[TOK_CHAN] || argv[TOK_TEXT])) { - if (argv[TOK_TEXT]) - argv[TOK_CHAN] = argv[TOK_TEXT]; - snprintf(msg, sizeof(msg), "-!- %s(%s) has joined %s", - argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]); - } else if (!strcmp("PART", argv[TOK_CMD]) && argv[TOK_CHAN]) { - snprintf(msg, sizeof(msg), "-!- %s(%s) has left %s", - argv[TOK_NICKSRV], argv[TOK_USER], argv[TOK_CHAN]); - /* if user itself leaves, don't write to channel (don't reopen channel). */ - if (!strcmp(argv[TOK_NICKSRV], nick)) - return; - } else if (!strcmp("MODE", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "-!- %s changed mode/%s -> %s %s", - argv[TOK_NICKSRV], - argv[TOK_CHAN] ? argv[TOK_CHAN] : "", - argv[TOK_ARG] ? argv[TOK_ARG] : "", - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else if (!strcmp("QUIT", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "-!- %s(%s) has quit \"%s\"", - argv[TOK_NICKSRV], argv[TOK_USER], - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else if (!strncmp("NICK", argv[TOK_CMD], 5) && argv[TOK_TEXT] && - !strcmp(_nick, argv[TOK_TEXT])) { - strlcpy(nick, _nick, sizeof(nick)); - snprintf(msg, sizeof(msg), "-!- changed nick to \"%s\"", nick); - channel_print(channelmaster, msg); - } else if (!strcmp("NICK", argv[TOK_CMD]) && argv[TOK_TEXT]) { - snprintf(msg, sizeof(msg), "-!- %s changed nick to %s", - argv[TOK_NICKSRV], argv[TOK_TEXT]); - } else if (!strcmp("TOPIC", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "-!- %s changed topic to \"%s\"", - argv[TOK_NICKSRV], - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else if (!strcmp("KICK", argv[TOK_CMD]) && argv[TOK_ARG]) { - snprintf(msg, sizeof(msg), "-!- %s kicked %s (\"%s\")", - argv[TOK_NICKSRV], argv[TOK_ARG], - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else if (!strcmp("NOTICE", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "-!- \"%s\"", - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else if (!strcmp("PRIVMSG", argv[TOK_CMD])) { - snprintf(msg, sizeof(msg), "<%s> %s", argv[TOK_NICKSRV], - argv[TOK_TEXT] ? argv[TOK_TEXT] : ""); - } else { - return; /* can't read this message */ - } - if (argv[TOK_CHAN] && !strcmp(argv[TOK_CHAN], nick)) - channel = argv[TOK_NICKSRV]; - else - channel = argv[TOK_CHAN]; - - if (!channel || channel[0] == '\0') - c = channelmaster; - else - c = channel_join(channel); - if (c) - channel_print(c, msg); -} - -static -int -read_line(int fd, char *buf, size_t bufsiz) -{ - size_t i = 0; - char c = '\0'; - - do { - if (read(fd, &c, sizeof(char)) != sizeof(char)) - return -1; - buf[i++] = c; - } while (c != '\n' && i < bufsiz); - buf[i - 1] = '\0'; /* eliminates '\n' */ - return 0; -} - -static -void -handle_channels_input(int ircfd, Channel *c) -{ - char buf[IRC_MSG_MAX]; - - if(read_line(c->fdin, buf, sizeof(buf)) == -1) { - if(channel_reopen(c) == -1) - channel_rm(c); - return; - } - proc_channels_input(ircfd, c, buf); -} - -static -void -handle_server_output(int ircfd) -{ - char buf[IRC_MSG_MAX]; - - if (read_line(ircfd, buf, sizeof(buf)) == -1) { - fprintf(stderr, "%s: remote host closed connection: %s\n", - argv0, strerror(errno)); - exit(1); - } - fprintf(stdout, "%lu %s\n", (unsigned long)time(nil), buf); - fflush(stdout); - proc_server_cmd(ircfd, buf); -} - -static -void -sighandler(int sig) -{ - if (sig == SIGTERM || sig == SIGINT) - isrunning = 0; -} - -static -void -setup(void) -{ - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sighandler; - sigaction(SIGTERM, &sa, nil); - sigaction(SIGINT, &sa, nil); -} - -static -void -run(int ircfd, const char *host) -{ - Channel *c, *tmp; - fd_set rdset; - struct timeval tv; - char ping_msg[IRC_MSG_MAX]; - int r, maxfd; - - snprintf(ping_msg, sizeof(ping_msg), "PING %s\r\n", host); - while(isrunning) { - maxfd = ircfd; - FD_ZERO(&rdset); - FD_SET(ircfd, &rdset); - for (c = channels; c; c = c->next) { - if (c->fdin > maxfd) - maxfd = c->fdin; - FD_SET(c->fdin, &rdset); - } - memset(&tv, 0, sizeof(tv)); - tv.tv_sec = 120; - r = select(maxfd + 1, &rdset, 0, 0, &tv); - if(r < 0){ - if (errno == EINTR) - continue; - fprintf(stderr, "%s: select: %s\n", argv0, strerror(errno)); - exit(1); - }else if(r == 0){ - if (time(nil) - last_response >= PING_TIMEOUT) { - channel_print(channelmaster, "-!- ii shutting down: ping timeout"); - exit(2); /* status code 2 for timeout */ - } - ewritestr(ircfd, ping_msg); - continue; - } - if(FD_ISSET(ircfd, &rdset)) { - handle_server_output(ircfd); - last_response = time(nil); - } - for(c = channels; c; c = tmp) { - tmp = c->next; - if (FD_ISSET(c->fdin, &rdset)) - handle_channels_input(ircfd, c); - } - } -} - -int -main(int argc, char *argv[]) -{ - Channel *c, *tmp; - struct passwd *spw; - const char *key = nil, *fullname = nil, *host = ""; - const char *uds = nil, *service = "6667"; - char prefix[PATH_MAX]; - int ircfd, r; - - /* use nickname and home dir of user by default */ - if(!(spw = getpwuid(getuid()))) { - fprintf(stderr, "%s: getpwuid: %s\n", argv0, strerror(errno)); - exit(1); - } - strlcpy(nick, spw->pw_name, sizeof(nick)); - snprintf(prefix, sizeof(prefix), "%s/irc", spw->pw_dir); - - ARGBEGIN { - case 'f': - fullname = EARGF(usage()); - break; - case 'i': - strlcpy(prefix, EARGF(usage()), sizeof(prefix)); - break; - case 'k': - key = getenv(EARGF(usage())); - break; - case 'n': - strlcpy(nick, EARGF(usage()), sizeof(nick)); - break; - case 'p': - service = EARGF(usage()); - break; - case 's': - host = EARGF(usage()); - break; - case 'u': - uds = EARGF(usage()); - break; - default: - usage(); - break; - } ARGEND - - if(!*host) - usage(); - - if(uds) - ircfd = udsopen(uds); - else - ircfd = tcpopen(host, service); - -#ifdef __OpenBSD__ - /* OpenBSD pledge(2) support */ - if (pledge("stdio rpath wpath cpath dpath", nil) == -1) { - fprintf(stderr, "%s: pledge: %s\n", argv0, strerror(errno)); - exit(1); - } -#endif - - r = snprintf(ircpath, sizeof(ircpath), "%s/%s", prefix, host); - if (r < 0 || (size_t)r >= sizeof(ircpath)) { - fprintf(stderr, "%s: path to irc directory too long\n", argv0); - exit(1); - } - create_dirtree(ircpath); - - channelmaster = channel_add(""); /* master channel */ - if(key) - loginkey(ircfd, key); - loginuser(ircfd, host, fullname && *fullname ? fullname : nick); - setup(); - run(ircfd, host); - if(channelmaster) - channel_leave(channelmaster); - - for(c = channels; c; c = tmp) { - tmp = c->next; - channel_leave(c); - } - - return 0; -} diff --git a/sys/cmd/ic/rules.mk b/sys/cmd/ic/rules.mk deleted file mode 100644 index 649c9ac..0000000 --- a/sys/cmd/ic/rules.mk +++ /dev/null @@ -1,14 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := $(d)/strlcpy.c $(d)/ic.c -BINS_$(d) := $(d)/ic - -include share/paths.mk - -# Local rules -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/ic/strlcpy.c b/sys/cmd/ic/strlcpy.c deleted file mode 100644 index 5af7906..0000000 --- a/sys/cmd/ic/strlcpy.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Taken from OpenBSD */ -#include -#include - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if(n != 0) { - while(--n != 0) { - if((*d++ = *s++) == '\0') - break; - } - } - /* Not enough room in dst, add NUL and traverse rest of src */ - if(n == 0) { - if(siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while(*s++) - ; - } - return s - src - 1; /* count does not include NUL */ -} diff --git a/sys/cmd/menu/LICENSE b/sys/cmd/menu/LICENSE deleted file mode 100644 index 9762166..0000000 --- a/sys/cmd/menu/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -MIT/X Consortium License - -© 2006-2019 Anselm R Garbe -© 2006-2008 Sander van Dijk -© 2006-2007 Michał Janeczek -© 2007 Kris Maglione -© 2009 Gottox -© 2009 Markus Schnalke -© 2009 Evan Gates -© 2010-2012 Connor Lane Smith -© 2014-2019 Hiltjo Posthuma -© 2015-2019 Quentin Rameau - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/sys/cmd/menu/config.h b/sys/cmd/menu/config.h deleted file mode 100644 index 9bfd5b3..0000000 --- a/sys/cmd/menu/config.h +++ /dev/null @@ -1,25 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -/* Default settings; can be overriden by command line. */ -#define VERSION "1.0" - -static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ -/* -fn option overrides fonts[0]; default X11 font or font set */ -static const char *fonts[] = { - "consolas:size=16" -}; - -static const char *prompt = "cmds"; /* -p option; prompt to the left of input field */ -static const char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#fbf1c7", "#504945" }, - [SchemeSel] = { "#504945", "#83a598" }, - [SchemeOut] = { "#000000", "#00ffff" }, -}; -/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ -static unsigned int lines = 0; - -/* - * Characters not considered part of a word while deleting words - * e.g. " /?\"&[]" - */ -static const char worddelimiters[] = " "; diff --git a/sys/cmd/menu/drw.c b/sys/cmd/menu/drw.c deleted file mode 100644 index 162fe40..0000000 --- a/sys/cmd/menu/drw.c +++ /dev/null @@ -1,428 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "menu.h" - -#define UTF_INVALID 0xFFFD -#define UTF_SIZ 4 - -static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; -static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; -static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; -static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; - -static long -utf8decodebyte(const char c, size_t *i) -{ - for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) - if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) - return (unsigned char)c & ~utfmask[*i]; - return 0; -} - -static size_t -utf8validate(long *u, size_t i) -{ - if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) - *u = RuneErr; - for (i = 1; *u > utfmax[i]; ++i) - ; - return i; -} - -static size_t -utf8decode(const char *c, long *u, size_t clen) -{ - size_t i, j, len, type; - long udecoded; - - *u = RuneErr; - if (!clen) - return 0; - udecoded = utf8decodebyte(c[0], &len); - if (!BETWEEN(len, 1, UTF_SIZ)) - return 1; - for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { - udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); - if (type) - return j; - } - if (j < len) - return 0; - *u = udecoded; - utf8validate(u, len); - - return len; -} - -Drw * -drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) -{ - Drw *drw = ecalloc(1, sizeof(Drw)); - - drw->dpy = dpy; - drw->screen = screen; - drw->root = root; - drw->w = w; - drw->h = h; - drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); - drw->gc = XCreateGC(dpy, root, 0, NULL); - XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); - - return drw; -} - -void -drw_resize(Drw *drw, unsigned int w, unsigned int h) -{ - if (!drw) - return; - - drw->w = w; - drw->h = h; - if (drw->drawable) - XFreePixmap(drw->dpy, drw->drawable); - drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); -} - -void -drw_free(Drw *drw) -{ - XFreePixmap(drw->dpy, drw->drawable); - XFreeGC(drw->dpy, drw->gc); - free(drw); -} - -/* This function is an implementation detail. Library users should use - * drw_fontset_create instead. - */ -static Fnt * -xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) -{ - Fnt *font; - XftFont *xfont = NULL; - FcPattern *pattern = NULL; - - if (fontname) { - /* Using the pattern found at font->xfont->pattern does not yield the - * same substitution results as using the pattern returned by - * FcNameParse; using the latter results in the desired fallback - * behaviour whereas the former just results in missing-character - * rectangles being drawn, at least with some fonts. */ - if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { - fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); - return NULL; - } - if (!(pattern = FcNameParse((FcChar8 *) fontname))) { - fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); - XftFontClose(drw->dpy, xfont); - return NULL; - } - } else if (fontpattern) { - if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { - fprintf(stderr, "error, cannot load font from pattern.\n"); - return NULL; - } - } else { - fatal("no font specified."); - } - - /* Do not allow using color fonts. This is a workaround for a BadLength - * error from Xft with color glyphs. Modelled on the Xterm workaround. See - * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 - * https://lists.suckless.org/dev/1701/30932.html - * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 - * and lots more all over the internet. - */ - FcBool iscol; - if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { - XftFontClose(drw->dpy, xfont); - return NULL; - } - - font = ecalloc(1, sizeof(Fnt)); - font->xfont = xfont; - font->pattern = pattern; - font->h = xfont->ascent + xfont->descent; - font->dpy = drw->dpy; - - return font; -} - -static void -xfont_free(Fnt *font) -{ - if (!font) - return; - if (font->pattern) - FcPatternDestroy(font->pattern); - XftFontClose(font->dpy, font->xfont); - free(font); -} - -Fnt* -drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) -{ - Fnt *cur, *ret = NULL; - size_t i; - - if (!drw || !fonts) - return NULL; - - for (i = 1; i <= fontcount; i++) { - if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { - cur->next = ret; - ret = cur; - } - } - return (drw->fonts = ret); -} - -void -drw_fontset_free(Fnt *font) -{ - if (font) { - drw_fontset_free(font->next); - xfont_free(font); - } -} - -void -drw_clr_create(Drw *drw, Clr *dest, const char *clrname) -{ - if (!drw || !dest || !clrname) - return; - - if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), - DefaultColormap(drw->dpy, drw->screen), - clrname, dest)) - fatal("error, cannot allocate color '%s'", clrname); -} - -/* Wrapper to create color schemes. The caller has to call free(3) on the - * returned color scheme when done using it. */ -Clr * -drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) -{ - size_t i; - Clr *ret; - - /* need at least two colors for a scheme */ - if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) - return NULL; - - for (i = 0; i < clrcount; i++) - drw_clr_create(drw, &ret[i], clrnames[i]); - return ret; -} - -void -drw_setfontset(Drw *drw, Fnt *set) -{ - if (drw) - drw->fonts = set; -} - -void -drw_setscheme(Drw *drw, Clr *scm) -{ - if (drw) - drw->scheme = scm; -} - -void -drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) -{ - if (!drw || !drw->scheme) - return; - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); - if (filled) - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); - else - XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); -} - -int -drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) -{ - char buf[1024]; - int ty; - unsigned int ew; - XftDraw *d = NULL; - Fnt *usedfont, *curfont, *nextfont; - size_t i, len; - int utf8strlen, utf8charlen, render = x || y || w || h; - long utf8codepoint = 0; - const char *utf8str; - FcCharSet *fccharset; - FcPattern *fcpattern; - FcPattern *match; - XftResult result; - int charexists = 0; - - if (!drw || (render && !drw->scheme) || !text || !drw->fonts) - return 0; - - if (!render) { - w = ~w; - } else { - XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); - d = XftDrawCreate(drw->dpy, drw->drawable, - DefaultVisual(drw->dpy, drw->screen), - DefaultColormap(drw->dpy, drw->screen)); - x += lpad; - w -= lpad; - } - - usedfont = drw->fonts; - while (1) { - utf8strlen = 0; - utf8str = text; - nextfont = NULL; - while (*text) { - utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); - for (curfont = drw->fonts; curfont; curfont = curfont->next) { - charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); - if (charexists) { - if (curfont == usedfont) { - utf8strlen += utf8charlen; - text += utf8charlen; - } else { - nextfont = curfont; - } - break; - } - } - - if (!charexists || nextfont) - break; - else - charexists = 0; - } - - if (utf8strlen) { - drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); - /* shorten text if necessary */ - for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) - drw_font_getexts(usedfont, utf8str, len, &ew, NULL); - - if (len) { - memcpy(buf, utf8str, len); - buf[len] = '\0'; - if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.') - ; /* NOP */ - - if (render) { - ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; - XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], - usedfont->xfont, x, ty, (XftChar8 *)buf, len); - } - x += ew; - w -= ew; - } - } - - if (!*text) { - break; - } else if (nextfont) { - charexists = 0; - usedfont = nextfont; - } else { - /* Regardless of whether or not a fallback font is found, the - * character must be drawn. */ - charexists = 1; - - fccharset = FcCharSetCreate(); - FcCharSetAddChar(fccharset, utf8codepoint); - - if (!drw->fonts->pattern) { - /* Refer to the comment in xfont_create for more information. */ - fatal("the first font in the cache must be loaded from a font string."); - } - - fcpattern = FcPatternDuplicate(drw->fonts->pattern); - FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); - FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); - - FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); - FcDefaultSubstitute(fcpattern); - match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); - - FcCharSetDestroy(fccharset); - FcPatternDestroy(fcpattern); - - if (match) { - usedfont = xfont_create(drw, NULL, match); - if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { - for (curfont = drw->fonts; curfont->next; curfont = curfont->next) - ; /* NOP */ - curfont->next = usedfont; - } else { - xfont_free(usedfont); - usedfont = drw->fonts; - } - } - } - } - if (d) - XftDrawDestroy(d); - - return x + (render ? w : 0); -} - -void -drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) -{ - if (!drw) - return; - - XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); - XSync(drw->dpy, False); -} - -unsigned int -drw_fontset_getwidth(Drw *drw, const char *text) -{ - if (!drw || !drw->fonts || !text) - return 0; - return drw_text(drw, 0, 0, 0, 0, 0, text, 0); -} - -void -drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) -{ - XGlyphInfo ext; - - if (!font || !text) - return; - - XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); - if (w) - *w = ext.xOff; - if (h) - *h = font->h; -} - -Cur * -drw_cur_create(Drw *drw, int shape) -{ - Cur *cur; - - if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) - return NULL; - - cur->cursor = XCreateFontCursor(drw->dpy, shape); - - return cur; -} - -void -drw_cur_free(Drw *drw, Cur *cursor) -{ - if (!cursor) - return; - - XFreeCursor(drw->dpy, cursor->cursor); - free(cursor); -} diff --git a/sys/cmd/menu/drw.h b/sys/cmd/menu/drw.h deleted file mode 100644 index 4c67419..0000000 --- a/sys/cmd/menu/drw.h +++ /dev/null @@ -1,57 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -typedef struct { - Cursor cursor; -} Cur; - -typedef struct Fnt { - Display *dpy; - unsigned int h; - XftFont *xfont; - FcPattern *pattern; - struct Fnt *next; -} Fnt; - -enum { ColFg, ColBg }; /* Clr scheme index */ -typedef XftColor Clr; - -typedef struct { - unsigned int w, h; - Display *dpy; - int screen; - Window root; - Drawable drawable; - GC gc; - Clr *scheme; - Fnt *fonts; -} Drw; - -/* Drawable abstraction */ -Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); -void drw_resize(Drw *drw, unsigned int w, unsigned int h); -void drw_free(Drw *drw); - -/* Fnt abstraction */ -Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); -void drw_fontset_free(Fnt* set); -unsigned int drw_fontset_getwidth(Drw *drw, const char *text); -void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); - -/* Colorscheme abstraction */ -void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); -Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); - -/* Cursor abstraction */ -Cur *drw_cur_create(Drw *drw, int shape); -void drw_cur_free(Drw *drw, Cur *cursor); - -/* Drawing context manipulation */ -void drw_setfontset(Drw *drw, Fnt *set); -void drw_setscheme(Drw *drw, Clr *scm); - -/* Drawing functions */ -void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); -int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); - -/* Map functions */ -void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/sys/cmd/menu/menu.c b/sys/cmd/menu/menu.c deleted file mode 100644 index e6e4bb2..0000000 --- a/sys/cmd/menu/menu.c +++ /dev/null @@ -1,765 +0,0 @@ -#include "menu.h" - -static char text[BUFSIZ] = ""; -static char *embed; -static int bh, mw, mh; -static int inputw = 0, promptw, passwd = 0; -static int lrpad; /* sum of left and right padding */ -static size_t cursor; -static struct item *items = nil; -static struct item *matches, *matchend; -static struct item *prev, *curr, *next, *sel; -static int mon = -1, screen; - -static Atom clip, utf8; -static Display *dpy; -static Window root, parentwin, win; -static XIC xic; - -static Drw *drw; -static Clr *scheme[SchemeLast]; - -#include "config.h" - -static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -static char *(*fstrstr)(const char *, const char *) = strstr; - -static -void -appenditem(struct item *item, struct item **list, struct item **last) -{ - if (*last) - (*last)->right = item; - else - *list = item; - - item->left = *last; - item->right = nil; - *last = item; -} - -static -void -calcoffsets(void) -{ - int i, n; - - if (lines > 0) - n = lines * bh; - else - n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); - /* calculate which items will begin the next page and previous page */ - for (i = 0, next = curr; next; next = next->right) - if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) - break; - for (i = 0, prev = curr; prev && prev->left; prev = prev->left) - if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) - break; -} - -static -void -cleanup(void) -{ - size_t i; - - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for (i = 0; i < SchemeLast; i++) - free(scheme[i]); - drw_free(drw); - XSync(dpy, False); - XCloseDisplay(dpy); -} - -static -char * -cistrstr(const char *s, const char *sub) -{ - size_t len; - - for (len = strlen(sub); *s; s++) - if (!strncasecmp(s, sub, len)) - return (char *)s; - return nil; -} - -static -int -drawitem(struct item *item, int x, int y, int w) -{ - if (item == sel) - drw_setscheme(drw, scheme[SchemeSel]); - else if (item->out) - drw_setscheme(drw, scheme[SchemeOut]); - else - drw_setscheme(drw, scheme[SchemeNorm]); - - return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); -} - -static -void -drawmenu(void) -{ - uint curpos; - struct item *item; - int x = 0, y = 0, w; - char *censort; - - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1); - - if (prompt && *prompt) { - drw_setscheme(drw, scheme[SchemeSel]); - x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); - } - /* draw input field */ - w = (lines > 0 || !matches) ? mw - x : inputw; - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); - if(passwd){ - censort = ecalloc(1, sizeof(text)); - memset(censort, '.', strlen(text)); - drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); - free(censort); - } - - curpos = TEXTW(text) - TEXTW(&text[cursor]); - if ((curpos += lrpad / 2 - 1) < w) { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); - } - - if (lines > 0) { - /* draw vertical list */ - for (item = curr; item != next; item = item->right) - drawitem(item, x, y += bh, mw - x); - } else if (matches) { - /* draw horizontal list */ - x += inputw; - w = TEXTW("<"); - if (curr->left) { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); - } - x += w; - for (item = curr; item != next; item = item->right) - x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); - if (next) { - w = TEXTW(">"); - drw_setscheme(drw, scheme[SchemeNorm]); - drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); - } - } - drw_map(drw, win, 0, 0, mw, mh); -} - -static -void -grabfocus(void) -{ - struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; - Window focuswin; - int i, revertwin; - - for (i = 0; i < 100; ++i) { - XGetInputFocus(dpy, &focuswin, &revertwin); - if (focuswin == win) - return; - XSetInputFocus(dpy, win, RevertToParent, CurrentTime); - nanosleep(&ts, nil); - } - fatal("cannot grab focus"); -} - -static -void -grabkeyboard(void) -{ - struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; - int i; - - if (embed) - return; - /* try to grab keyboard, we may have to wait for another process to ungrab */ - for (i = 0; i < 1000; i++) { - if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, - GrabModeAsync, CurrentTime) == GrabSuccess) - return; - nanosleep(&ts, nil); - } - fatal("cannot grab keyboard"); -} - -static -void -match(void) -{ - static char **tokv = nil; - static int tokn = 0; - - char buf[sizeof text], *s; - int i, tokc = 0; - size_t len, textsize; - struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; - - strcpy(buf, text); - /* separate input text into tokens to be matched individually */ - for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(nil, " ")) - if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) - fatal("cannot realloc %u bytes:", tokn * sizeof *tokv); - len = tokc ? strlen(tokv[0]) : 0; - - matches = lprefix = lsubstr = matchend = prefixend = substrend = nil; - textsize = strlen(text) + 1; - for (item = items; item && item->text; item++) { - for (i = 0; i < tokc; i++) - if (!fstrstr(item->text, tokv[i])) - break; - if (i != tokc) /* not all tokens match */ - continue; - /* exact matches go first, then prefixes, then substrings */ - if (!tokc || !fstrncmp(text, item->text, textsize)) - appenditem(item, &matches, &matchend); - else if (!fstrncmp(tokv[0], item->text, len)) - appenditem(item, &lprefix, &prefixend); - else - appenditem(item, &lsubstr, &substrend); - } - if (lprefix) { - if (matches) { - matchend->right = lprefix; - lprefix->left = matchend; - } else - matches = lprefix; - matchend = prefixend; - } - if (lsubstr) { - if (matches) { - matchend->right = lsubstr; - lsubstr->left = matchend; - } else - matches = lsubstr; - matchend = substrend; - } - curr = sel = matches; - calcoffsets(); -} - -static -void -insert(const char *str, ssize_t n) -{ - if (strlen(text) + n > sizeof text - 1) - return; - /* move existing text out of the way, insert new text, and update cursor */ - memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); - if (n > 0) - memcpy(&text[cursor], str, n); - cursor += n; - match(); -} - -static -size_t -nextrune(int inc) -{ - ssize_t n; - - /* return location of next utf8 rune in the given direction (+1 or -1) */ - for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) - ; - return n; -} - -static -void -movewordedge(int dir) -{ - if (dir < 0) { /* move cursor to the start of the word*/ - while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) - cursor = nextrune(-1); - while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) - cursor = nextrune(-1); - } else { /* move cursor to the end of the word */ - while (text[cursor] && strchr(worddelimiters, text[cursor])) - cursor = nextrune(+1); - while (text[cursor] && !strchr(worddelimiters, text[cursor])) - cursor = nextrune(+1); - } -} - -static -void -keypress(XKeyEvent *ev) -{ - char buf[32]; - int len; - KeySym ksym; - Status status; - - len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); - switch (status) { - default: /* XLookupNone, XBufferOverflow */ - return; - case XLookupChars: - goto insert; - case XLookupKeySym: - case XLookupBoth: - break; - } - - if (ev->state & ControlMask) { - switch(ksym) { - case XK_a: ksym = XK_Home; break; - case XK_b: ksym = XK_Left; break; - case XK_c: ksym = XK_Escape; break; - case XK_d: ksym = XK_Delete; break; - case XK_e: ksym = XK_End; break; - case XK_f: ksym = XK_Right; break; - case XK_g: ksym = XK_Escape; break; - case XK_h: ksym = XK_BackSpace; break; - case XK_i: ksym = XK_Tab; break; - case XK_j: /* fallthrough */ - case XK_J: /* fallthrough */ - case XK_m: /* fallthrough */ - case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; - case XK_n: ksym = XK_Down; break; - case XK_p: ksym = XK_Up; break; - - case XK_k: /* delete right */ - text[cursor] = '\0'; - match(); - break; - case XK_u: /* delete left */ - insert(nil, 0 - cursor); - break; - case XK_w: /* delete word */ - while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) - insert(nil, nextrune(-1) - cursor); - while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) - insert(nil, nextrune(-1) - cursor); - break; - case XK_y: /* paste selection */ - case XK_Y: - XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, - utf8, utf8, win, CurrentTime); - return; - case XK_Left: - movewordedge(-1); - goto draw; - case XK_Right: - movewordedge(+1); - goto draw; - case XK_Return: - case XK_KP_Enter: - break; - case XK_bracketleft: - cleanup(); - exit(1); - default: - return; - } - } else if (ev->state & Mod1Mask) { - switch(ksym) { - case XK_b: - movewordedge(-1); - goto draw; - case XK_f: - movewordedge(+1); - goto draw; - case XK_g: ksym = XK_Home; break; - case XK_G: ksym = XK_End; break; - case XK_h: ksym = XK_Up; break; - case XK_j: ksym = XK_Next; break; - case XK_k: ksym = XK_Prior; break; - case XK_l: ksym = XK_Down; break; - default: - return; - } - } - - switch(ksym) { - default: -insert: - if (!iscntrl(*buf)) - insert(buf, len); - break; - case XK_Delete: - if (text[cursor] == '\0') - return; - cursor = nextrune(+1); - /* fallthrough */ - case XK_BackSpace: - if (cursor == 0) - return; - insert(nil, nextrune(-1) - cursor); - break; - case XK_End: - if (text[cursor] != '\0') { - cursor = strlen(text); - break; - } - if (next) { - /* jump to end of list and position items in reverse */ - curr = matchend; - calcoffsets(); - curr = prev; - calcoffsets(); - while (next && (curr = curr->right)) - calcoffsets(); - } - sel = matchend; - break; - case XK_Escape: - cleanup(); - exit(1); - case XK_Home: - if (sel == matches) { - cursor = 0; - break; - } - sel = curr = matches; - calcoffsets(); - break; - case XK_Left: - if (cursor > 0 && (!sel || !sel->left || lines > 0)) { - cursor = nextrune(-1); - break; - } - if (lines > 0) - return; - /* fallthrough */ - case XK_Up: - if (sel && sel->left && (sel = sel->left)->right == curr) { - curr = prev; - calcoffsets(); - } - break; - case XK_Next: - if (!next) - return; - sel = curr = next; - calcoffsets(); - break; - case XK_Prior: - if (!prev) - return; - sel = curr = prev; - calcoffsets(); - break; - case XK_Return: - case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); - if (!(ev->state & ControlMask)) { - cleanup(); - exit(0); - } - if (sel) - sel->out = 1; - break; - case XK_Right: - if (text[cursor] != '\0') { - cursor = nextrune(+1); - break; - } - if (lines > 0) - return; - /* fallthrough */ - case XK_Down: - if (sel && sel->right && (sel = sel->right) == next) { - curr = next; - calcoffsets(); - } - break; - case XK_Tab: - if (!sel) - return; - strncpy(text, sel->text, sizeof text - 1); - text[sizeof text - 1] = '\0'; - cursor = strlen(text); - match(); - break; - } - -draw: - drawmenu(); -} - -static -void -paste(void) -{ - char *p, *q; - int di; - unsigned long dl; - Atom da; - - /* we have been given the current selection, now insert it into input */ - if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, - utf8, &da, &di, &dl, &dl, (unsigned char **)&p) - == Success && p) { - insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); - XFree(p); - } - drawmenu(); -} - -static -void -readstdin(void) -{ - char buf[sizeof text], *p; - size_t i, imax = 0, size = 0; - uint tmpmax = 0; - if(passwd){ - inputw = lines = 0; - return; - } - - /* read each line from stdin and add it to the item list */ - for (i = 0; fgets(buf, sizeof buf, stdin); i++) { - if (i + 1 >= size / sizeof *items) - if (!(items = realloc(items, (size += BUFSIZ)))) - fatal("cannot realloc %u bytes:", size); - if ((p = strchr(buf, '\n'))) - *p = '\0'; - if (!(items[i].text = strdup(buf))) - fatal("cannot strdup %u bytes:", strlen(buf) + 1); - items[i].out = 0; - drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, nil); - if (tmpmax > inputw) { - inputw = tmpmax; - imax = i; - } - } - if (items) - items[i].text = nil; - inputw = items ? TEXTW(items[imax].text) : 0; - lines = MIN(lines, i); -} - -static -void -run(void) -{ - XEvent ev; - - while (!XNextEvent(dpy, &ev)) { - if (XFilterEvent(&ev, win)) - continue; - switch(ev.type) { - case DestroyNotify: - if (ev.xdestroywindow.window != win) - break; - cleanup(); - exit(1); - case Expose: - if (ev.xexpose.count == 0) - drw_map(drw, win, 0, 0, mw, mh); - break; - case FocusIn: - /* regrab focus from parent window */ - if (ev.xfocus.window != win) - grabfocus(); - break; - case KeyPress: - keypress(&ev.xkey); - break; - case SelectionNotify: - if (ev.xselection.property == utf8) - paste(); - break; - case VisibilityNotify: - if (ev.xvisibility.state != VisibilityUnobscured) - XRaiseWindow(dpy, win); - break; - } - } -} - -static -void -setup(void) -{ - int x, y, i, j; - uint du; - XSetWindowAttributes swa; - XIM xim; - Window w, dw, *dws; - XWindowAttributes wa; - XClassHint ch = {"menu", "menu"}; - XineramaScreenInfo *info; - Window pw; - int a, di, n, area = 0; - - /* init appearance */ - for (j = 0; j < SchemeLast; j++) - scheme[j] = drw_scm_create(drw, colors[j], 2); - - clip = XInternAtom(dpy, "CLIPBOARD", False); - utf8 = XInternAtom(dpy, "UTF8_STRING", False); - - /* calculate menu geometry */ - bh = drw->fonts->h + 2; - lines = MAX(lines, 0); - mh = (lines + 1) * bh; - i = 0; - if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { - XGetInputFocus(dpy, &w, &di); - if (mon >= 0 && mon < n) - i = mon; - else if (w != root && w != PointerRoot && w != None) { - /* find top-level window containing current input focus */ - do { - if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) - XFree(dws); - } while (w != root && w != pw); - /* find xinerama screen with which the window intersects most */ - if (XGetWindowAttributes(dpy, pw, &wa)) - for (j = 0; j < n; j++) - if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { - area = a; - i = j; - } - } - /* no focused window is on screen, so use pointer location instead */ - if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) - for (i = 0; i < n; i++) - if (INTERSECT(x, y, 1, 1, info[i])) - break; - - x = info[i].x_org; - y = info[i].y_org + (topbar ? 0 : info[i].height - mh); - mw = info[i].width; - XFree(info); - } else - { - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - fatal("could not get embedding window attributes: 0x%lx", - parentwin); - x = 0; - y = topbar ? 0 : wa.height - mh; - mw = wa.width; - } - promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; - inputw = MIN(inputw, mw/3); - match(); - - /* create menu window */ - swa.override_redirect = True; - swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; - swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; - win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, - CopyFromParent, CopyFromParent, CopyFromParent, - CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); - XSetClassHint(dpy, win, &ch); - - - /* input methods */ - if ((xim = XOpenIM(dpy, nil, nil, nil)) == nil) - fatal("XOpenIM failed: could not open input device"); - - xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, win, XNFocusWindow, win, nil); - - XMapRaised(dpy, win); - if (embed) { - XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); - if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { - for (i = 0; i < du && dws[i] != win; ++i) - XSelectInput(dpy, dws[i], FocusChangeMask); - XFree(dws); - } - grabfocus(); - } - drw_resize(drw, mw, mh); - drawmenu(); -} - -static -void -usage(void) -{ - fputs("usage: menu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); - exit(1); -} - -int -main(int argc, char *argv[]) -{ - XWindowAttributes wa; - int i, fast = 0; - - for (i = 1; i < argc; i++) - /* these options take no arguments */ - if (!strcmp(argv[i], "-v")) { /* prints version information */ - puts("menu-"VERSION); - exit(0); - } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ - topbar = 0; - else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ - fast = 1; - else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ - fstrncmp = strncasecmp; - fstrstr = cistrstr; - } else if (!strcmp(argv[i], "-P")) { - passwd = 1; - } else if (i + 1 == argc) - usage(); - /* these options take one argument */ - else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ - lines = atoi(argv[++i]); - else if (!strcmp(argv[i], "-m")) - mon = atoi(argv[++i]); - else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ - prompt = argv[++i]; - else if (!strcmp(argv[i], "-fn")) /* font or font set */ - fonts[0] = argv[++i]; - else if (!strcmp(argv[i], "-nb")) /* normal background color */ - colors[SchemeNorm][ColBg] = argv[++i]; - else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ - colors[SchemeNorm][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-sb")) /* selected background color */ - colors[SchemeSel][ColBg] = argv[++i]; - else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ - colors[SchemeSel][ColFg] = argv[++i]; - else if (!strcmp(argv[i], "-w")) /* embedding window id */ - embed = argv[++i]; - else - usage(); - - if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) - fputs("warning: no locale support\n", stderr); - if (!(dpy = XOpenDisplay(nil))) - fatal("cannot open display"); - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - if (!embed || !(parentwin = strtol(embed, nil, 0))) - parentwin = root; - if (!XGetWindowAttributes(dpy, parentwin, &wa)) - fatal("could not get embedding window attributes: 0x%lx", - parentwin); - drw = drw_create(dpy, screen, root, wa.width, wa.height); - if (!drw_fontset_create(drw, fonts, arrlen(fonts))) - fatal("no fonts could be loaded."); - lrpad = drw->fonts->h; - -#ifdef __OpenBSD__ - if (pledge("stdio rpath", nil) == -1) - fatal("pledge"); -#endif - - if (fast && !isatty(0)) { - grabkeyboard(); - readstdin(); - } else { - readstdin(); - grabkeyboard(); - } - setup(); - run(); - - return 1; /* unreachable */ -} diff --git a/sys/cmd/menu/menu.h b/sys/cmd/menu/menu.h deleted file mode 100644 index f4345bb..0000000 --- a/sys/cmd/menu/menu.h +++ /dev/null @@ -1,40 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include "drw.h" - -/* macros */ -#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ - * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) -#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) -#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) - - -/* enums */ -enum { - SchemeNorm, - SchemeSel, - SchemeOut, - SchemeLast -}; /* color schemes */ - -struct item { - char *text; - struct item *left, *right; - int out; -}; - -/* util.c */ -void fatal(const char *fmt, ...); -void *ecalloc(size_t nmemb, size_t size); diff --git a/sys/cmd/menu/rules.mk b/sys/cmd/menu/rules.mk deleted file mode 100644 index 1ee3ab0..0000000 --- a/sys/cmd/menu/rules.mk +++ /dev/null @@ -1,25 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := \ - $(d)/menu.c \ - $(d)/drw.c \ - $(d)/util.c - -BINS_$(d) := $(d)/menu - -include share/paths.mk - -# Local rules -include share/dynamic.mk -$(BINS_$(d)): TCLIBS = \ - -lfontconfig -lXft -lXinerama -lX11 -$(BINS_$(d)): TCINCS = \ - `$(PKG) --cflags fontconfig` \ - `$(PKG) --cflags freetype2` - -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/menu/util.c b/sys/cmd/menu/util.c deleted file mode 100644 index 14bfe1c..0000000 --- a/sys/cmd/menu/util.c +++ /dev/null @@ -1,30 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include "menu.h" - -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - - if (!(p = calloc(nmemb, size))) - fatal("calloc:"); - return p; -} - -void -fatal(const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - - exit(1); -} diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c deleted file mode 100644 index 786f284..0000000 --- a/sys/cmd/rc/code.c +++ /dev/null @@ -1,277 +0,0 @@ -#include "rc.h" -#include "parse.h" -#include "exec.h" - -// ----------------------------------------------------------------------- -// types - -struct Interpreter -{ - int i, cap; - Code *code; -}; - -Code *compiled = nil; -static struct Interpreter interpreter; -#define emiti(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].i = (x), interpreter.i++) -#define emitf(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].f = (x), interpreter.i++) -#define emits(x) ((void)(interpreter.i != interpreter.cap || grow()), interpreter.code[interpreter.i].s = (x), interpreter.i++) - -static -int -grow(void) -{ - interpreter.cap += 100; - interpreter.code = erealloc(interpreter.code, sizeof(*interpreter.code)*interpreter.cap); - memset(interpreter.code+interpreter.cap-100, 0, 100*sizeof(*interpreter.code)); - - return 0; -} - -static -void -storepc(int a) -{ - if(interpreter.i <= a || a < 0) - fatal("bad address %d in interpreter", a); - - interpreter.code[a].i = interpreter.i; -} - -static -void -walk(Tree *node) -{ - Tree *n; - int addr1, addr2; - - if(!node) - return; - - switch(node->type){ - default: - print(shell.err, "bad type %d in interpreter walk\n", node->type); - fatal("crashing\n"); - break; - - case '$': - emitf(Xmark); - walk(node->child[0]); - emitf(Xdollar); - break; - - case Tcount: - emitf(Xmark); - walk(node->child[0]); - emitf(Xcount); - break; - - case Tjoin: - emitf(Xmark); - walk(node->child[0]); - emitf(Xjoin); - break; - - case Tindex: - emitf(Xmark); - walk(node->child[1]); - emitf(Xmark); - walk(node->child[0]); - emitf(Xindex); - break; - - case ';': - walk(node->child[0]); - walk(node->child[1]); - break; - - case '^': - emitf(Xmark); - walk(node->child[1]); - emitf(Xmark); - walk(node->child[0]); - emitf(Xconcatenate); - break; - - case Tandand: - walk(node->child[0]); - emitf(Xtrue); - addr1 = emiti(0); - walk(node->child[1]); - storepc(addr1); - break; - - case Toror: - walk(node->child[0]); - emitf(Xfalse); - addr1 = emiti(0); - walk(node->child[1]); - storepc(addr1); - break; - - case Targs: - walk(node->child[1]); - walk(node->child[0]); - break; - - case Tparen: case Tblock: - walk(node->child[0]); - break; - - case Tbasic: - emitf(Xmark); - walk(node->child[0]); - emitf(Xbasic); - break; - - case Tbang: - walk(node->child[0]); - emitf(Xbang); - - case Tword: - emitf(Xword); - emits(strdup(node->str)); - break; - - case Twords: - walk(node->child[1]); - walk(node->child[0]); - break; - - case '=': - for(n=node; node && node->type == '='; node = node->child[2]) - ; - if(node){ - for(node=n; node->type=='='; node = node->child[2]){ - emitf(Xmark); - walk(node->child[1]); - emitf(Xmark); - walk(node->child[0]); - emitf(Xlocal); - } - walk(node); - for(node=n; node->type=='='; node = node->child[2]) - emitf(Xunlocal); - }else{ - for(node=n; node; node=node->child[2]){ - emitf(Xmark); - walk(node->child[1]); - emitf(Xmark); - walk(node->child[0]); - emitf(Xassign); - } - } - node = n; - break; - /* control structures */ - case Twhile: - addr1 = interpreter.i; // head of loop - walk(node->child[0]); - if(addr1 == interpreter.i) - fatal("TODO"); - emitf(Xtrue); - addr2 = emiti(0); // goto end of loop - - walk(node->child[1]); - emitf(Xgoto); - emiti(addr1); // goto top of loop - storepc(addr2); - break; - - case Tfor: - emitf(Xmark); - if(node->child[1]){ // for( x in X ) - walk(node->child[1]); - // emitf(Xglob) - }else{ // for(X) - fatal("TODO"); - } - emitf(Xmark); // null initial value for Xlocal - emitf(Xmark); - walk(node->child[0]); - emitf(Xlocal); - - addr1 = emitf(Xfor); - addr2 = emiti(0); - - walk(node->child[2]); - emitf(Xgoto); - emiti(addr1); - storepc(addr2); - emitf(Xunlocal); - break; - - /* forks */ - case '&': - emitf(Xasync); - addr1 = emiti(0); - walk(node->child[0]); - emitf(Xexit); - storepc(addr1); - break; - - case Tsubshell: - emitf(Xsubshell); - addr1 = emiti(0); - walk(node->child[0]); - emitf(Xexit); - storepc(addr1); - break; - - case Tpipe: - emitf(Xpipe); - - emiti(node->redir.fd[0]); - emiti(node->redir.fd[1]); - addr1 = emiti(0); - addr2 = emiti(0); - - walk(node->child[0]); - emitf(Xexit); - storepc(addr1); - - walk(node->child[1]); - emitf(Xreturn); - storepc(addr2); - - emitf(Xpipewait); - - break; - } -} - -// ----------------------------------------------------------------------- -// main exports - -void -freecode(Code *c) -{ - if(--c[0].i!=0) - return; - efree(c); -} - -Code * -copycode(Code *c) -{ - c[0].i++; - return c; -} - -int -compile(Tree *node) -{ - flush(shell.err); - - interpreter.i = 0; - interpreter.code = emalloc(100*sizeof(*interpreter.code)); - emiti(0); // reference count: no thread owns code yet - - walk(node); - - emitf(Xreturn); - emitf(nil); - - compiled = interpreter.code; - return 0; -} diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c deleted file mode 100644 index 5baaf1a..0000000 --- a/sys/cmd/rc/exec.c +++ /dev/null @@ -1,1267 +0,0 @@ -#include "rc.h" -#include "exec.h" - -#include - -int yyparse(void); - -struct Builtin{ - char *name; - void (*func)(void); -}; - -struct State { - int async; -}; - -static struct State state; - -// ----------------------------------------------------------------------- -// globals - -static Word nullpath = { .str="", .link=nil }; - -struct Builtin builtin[]={ - {"cd", xcd}, - {".", xdot}, - {"echo", xecho}, - {"exit", xexit}, - {"fg", xfg}, - {"jobs", xjob}, - 0, -}; - -// ----------------------------------------------------------------------- -// internal - -/* words and lists */ - -static -void -pushword(char *str) -{ - if(!runner->args) - fatal("attempt to push on empty argument stack\n"); - - runner->args->word = makeword(str, runner->args->word); -} - -static -void -popword(void) -{ - Word *w; - if(!runner->args) - fatal("tried to pop word on empty argument stack\n"); - - w = runner->args->word; - if(!w) - fatal("tried to pop word but nothing there\n"); - - runner->args->word = w->link; - efree(w->str); - efree(w); -} - -static -Word* -copywords(Word *a, Word *tail) -{ - Word *v = nil, **end; - - for(end=&v; a; a = a->link,end=&(*end)->link) - *end = makeword(a->str, nil); - *end = tail; - - return v; -} - -static -void -freewords(Word *w) -{ - Word *n; - while(w){ - efree(w->str); - n = w->link; - efree(w); - w = n; - } -} - -static -void -freelist(Word *w) -{ - Word *n; - while(w){ - n = w->link; - efree(w->str); - efree(w); - w = n; - } - -} - -static -void -pushlist(void) -{ - List *stack = emalloc(sizeof(*stack)); - - stack->word = nil; - stack->link = runner->args; - - runner->args = stack; -} - -static -void -poplist(void) -{ - List *stack = runner->args; - if(!stack) - fatal("attempted to pop an empty argument stack\n"); - - freelist(stack->word); - runner->args = stack->link; - efree(stack); -} - -/* system interop */ -static -Word* -path(char *w) -{ - Word *path; - - if(strncmp(w, "/", 1)==0 - || strncmp(w, "./", 2)==0 - || strncmp(w, "../", 3)==0 - || (path = var("path")->val)==0) - path=&nullpath; - - return path; -} - -static inline -void -undoredirs(void) -{ - while(runner->redir.end != runner->redir.start) - Xpopredir(); -} - -static inline -int -exitsnext(void) -{ - Code *c = &runner->code.exe[runner->code.i]; - while(c->f == Xpopredir) - c++; - - return c->f == Xexit; -} - -static inline -void -defaultsignal(void) -{ - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTSTP, SIG_DFL); - signal(SIGTTIN, SIG_DFL); - signal(SIGTTOU, SIG_DFL); - signal(SIGCHLD, SIG_DFL); -} - -static inline -void -setpid(Thread *job, int pid) -{ - job->pid = pid; - if(job->pgid <= 0){ - job->pgid = pid; - addjob(job); - } - - setpgid(pid, job->pgid); -} - -/* fork/execute helpers */ - -static inline -void -initchild(Thread *job, int fg) -{ - int pid = getpid(); - setpid(job, pid); - - if(job->flag.user){ - if(fg) - tcsetpgrp(0, job->pgid); - else - job->flag.user = 0; - defaultsignal(); - } - - clearwait(job); -} - -static inline -void -initparent(Thread *job, int pid, int fg) -{ - setpid(job, pid); - - if(job->flag.user){ - if(!fg){ - tcsetpgrp(0, job->pgid); - job->flag.user = 0; - } - } - addwait(job, pid); -} - -static -void -xx(void) -{ - popword(); // "exec" - if(!runner->args->word){ - Xerror("empty argument list"); - return; - } - - redirect(runner->redir.end); - execute(runner->args->word, path(runner->args->word->str)); - poplist(); -} - -static -int -xforkx(void) -{ - int n, pid; - - switch(pid=fork()){ - case -1: - Xerror("try again\n"); - return -1; - case 0: // child - initchild(runner, 1); - - pushword("exec"); - xx(); - - exit(2); // NOTE: unreachable: xx does not return - default: // parent - initparent(runner, pid, 0); - - return pid; - } -} - -/* redirections */ -void -pushredir(int type, int from, int to) -{ - Redir *r = emalloc(sizeof(*r)); - - r->type = type; - r->from = from; - r->to = to; - - r->link = runner->redir.end, runner->redir.end = r; -} - -/* byte code */ -static -void -run(Code *c, int pc, Var *local, int inherit) -{ - Thread *new = emalloc(sizeof(*new)); - - new->code.i = pc; - new->code.exe = copycode(c); - - new->cmd.path = nil; - new->cmd.io = nil; - - new->args = nil; - new->local = local; - - new->flag.eof = 0; - if(runner){ - new->pid = runner->pid; - new->flag.user = runner->flag.user; - new->redir.end = new->redir.start = runner->redir.end; - }else{ - new->pid = shell.pid; - new->flag.user = shell.interactive; - new->redir.end = new->redir.start = nil; - } - - new->wait.status = 0; - new->wait.len = 0; - new->wait.cap = 0; - new->wait.on = nil; - - new->status = 0; - if(inherit) - new->pgid = runner->pgid; - else - new->pgid = -1; - - new->line = 0; - new->caller = runner; - new->link = nil; - - runner = new; -} - -// ----------------------------------------------------------------------- -// exported builtins - -// XXX: find a better place for these -Word* -makeword(char *str, Word *link) -{ - Word *w = emalloc(sizeof(*w)); - - w->str = strdup(str); - w->link = link; - - return w; -} - -void -freeword(Word *word) -{ - Word *n; - - while(word){ - efree(word->str); - n = word->link; - efree(word); - word = n; - } -} - -int -count(Word *w) -{ - int n; - for(n = 0; w; n++) - w = w->link; - return n; -} - -// ----------------------------------------------------------------------- -// builtins - -void -xecho(void) -{ - int fd; - Word *arg; - char *b, *s, buf[128]; - - fd = mapfd(1); - b = buf; - - popword(); // echo - - // TODO: controllable flags here - arg = runner->args->word; -printword: - s = arg->str; - while(*s){ - *b++ = *s++; - if(b == arrend(buf)-2) // always have 2 bytes available - write(fd, buf, arrlen(buf)-2), b = buf; - } - - arg = arg->link; - if(arg){ - *b++ = ' '; - goto printword; - }else{ - *b++ = '\n'; - *b++ = 0; - /* fallthrough */ - } - write(fd, buf, b-buf); - - poplist(); -} - -void -xexit(void) -{ - Word *arg; - - popword(); // exit - arg = runner->args->word; - switch(count(arg)){ - default: - print(shell.err, "invalid number of arguments to exit, exiting anyways\n"); - case 0: - Xexit(); - } - /* unreachable */ -} - -void -xcd(void) -{ - Word *arg; - Word *cdpath; - char dir[512]; - - popword(); // cd - - arg = runner->args->word; - switch(count(arg)){ - default: - print(shell.err, "usage: cd [directory]\n"); - break; - case 0: - arg = var("home")->val; - if(count(arg) >= 1){ - if(chdir(arg->str) < 0) - print(shell.err, "failed cd: %s\n", strerror(errno)); - }else{ - print(shell.err, "ambiguous cd: $home empty\n"); - } - break; - - case 1: - // TODO: add cdpath - cdpath = &nullpath; - for(; cdpath; cdpath = cdpath->link){ - strcpy(dir, cdpath->str); - if(dir[0]) - strcat(dir,"/"); - strcat(dir, arg->str); - if(chdir(dir) < 0){ - print(shell.err, "failed cd %s: %s\n", dir, strerror(errno)); - } - break; - } - break; - } - - poplist(); -} - -static Code dotcmd[14] = -{ - [0] = {.i = 0}, - [1] = {.f = Xmark}, - [2] = {.f = Xword}, - [3] = {.s = "0"}, - [4] = {.f = Xlocal}, - [5] = {.f = Xmark}, - [6] = {.f = Xword}, - [7] = {.s = "*"}, - [8] = {.f = Xlocal}, - [9] = {.f = Xreadcmd}, - [10] = {.f = Xunlocal}, - [11] = {.f = Xunlocal}, - [12] = {.f = Xreturn}, -}; - -void -xdot(void) -{ - Word *p; - List *argv; - char *base; - int fd, iflag = 0; - Thread *old; - char file[512]; - - popword(); // "." -#if 0 - if(proc->args->word && strcmp(proc->args->word->str, "-i")==0){ - iflag = 1; - popword(); - } -#endif - /* get input file */ - if(!runner->args->word){ - Xerror("usage: . [-i] file [arg ...]\n"); - return; - } - - base = strdup(runner->args->word->str); - popword(); - for(fd=-1, p=path(base); p; p = p->link){ - strcpy(file, p->str); - - if(file[0]) - strcat(file, "/"); - strcat(file, base); - - if((fd = open(file, 0))>=0) - break; - } - - if(fd<0){ - print(shell.err, "failed open: %s: ", base); - return; - } - /* set up for a new command loop */ - old = runner; // store pointer to old code - run(dotcmd, 1, nil, 0); - - /* operations on new command stack */ - pushredir(Rclose, fd, 0); - runner->cmd.path = base; - runner->cmd.io = openfd(fd); - - /* push $* value */ - pushlist(); - runner->args->word = old->args->word; - - /* free caller's copy of $* */ - argv = old->args; - old->args = argv->link; - efree(argv); - - /* push $0 value */ - pushlist(); - pushword(base); - //ndot++; -} - -void -xjob(void) -{ - int i; - Thread *job; - - for(i=0, job = shell.jobs; job; job = job->link, i++) - report(job,i); - - poplist(); -} - -void -xfg(void) -{ - int i; - Thread *job, *old; - - popword(); // fg - - /* get input job id */ - if(!runner->args->word){ - print(shell.err, "usage: fg [pid|\%num]\n"); - poplist(); - return; - } - - i = atoi(runner->args->word->str); - popword(); // [pid|num] - - for(job=shell.jobs; i > 0; job=job->link, --i) - ; - - poplist(); // this goes here? - - wakeup(job); - job->caller = runner, runner = job; // XXX: can this leave zombies? - foreground(job, 1); -} - -void -xboot(int argc, char *argv[]) -{ - int i; - Code bootstrap[32]; - char num[12]; - - i = 0; - bootstrap[i++].i = 1; - bootstrap[i++].f = Xmark; - bootstrap[i++].f = Xword; - bootstrap[i++].s="*"; - bootstrap[i++].f = Xassign; - bootstrap[i++].f = Xmark; - bootstrap[i++].f = Xmark; - bootstrap[i++].f = Xword; - bootstrap[i++].s="*"; - bootstrap[i++].f = Xdollar; - bootstrap[i++].f = Xword; - bootstrap[i++].s = "/dev/stdin"; - bootstrap[i++].f = Xword; - bootstrap[i++].s="."; - bootstrap[i++].f = Xbasic; - bootstrap[i++].f = Xexit; - bootstrap[i].i = 0; - - run(bootstrap, 1, nil, 0); - runner->pid = runner->pgid = shell.pid; - pushlist(); // prime bootstrap argv - - argv0 = strdup(argv[0]); - for(i = argc-1; i > 0; --i) - pushword(argv[i]); - - /* main interpreter loop */ - for(;;){ - runner->code.i++; - (*runner->code.exe[runner->code.i-1].f)(); - } -} - -// ----------------------------------------------------------------------- -// exported interpreter bytecode - -void -Xmark(void) -{ - pushlist(); -} - -void -Xword(void) -{ - pushword(runner->code.exe[runner->code.i++].s); -} - -void -Xtrue(void) -{ - if(!runner->status){ - assert(runner->wait.status == Pdone); - runner->code.i++; - deljob(runner); - runner->pgid = -1; - }else - runner->code.i = runner->code.exe[runner->code.i].i; -} - -void -Xfalse(void) -{ - if(runner->status){ - assert(runner->wait.status == Pdone); - runner->code.i++; - deljob(runner); - runner->pgid = -1; - } else - runner->code.i = runner->code.exe[runner->code.i].i; -} - -void -Xgoto(void) -{ - runner->code.i = runner->code.exe[runner->code.i].i; -} - -void -Xfor(void) -{ - if(!runner->args->word){ - poplist(); - runner->code.i = runner->code.exe[runner->code.i].i; - }else{ - freelist(runner->local->val); - - runner->local->val = runner->args->word; - runner->local->new = 1; - runner->args->word = runner->args->word->link; - - runner->local->val->link = nil; - runner->code.i++; - } - -} - -static -Word* -catlist(Word *l, Word *r, Word *tail) -{ - Word *w; - char *buf; - - if(l->link || r->link) - tail = catlist( (!l->link)?l:l->link, (!r->link)?r:r->link, tail); - - buf = emalloc(strlen(l->str)+strlen(r->str)+1); - strcpy(buf, l->str); - strcat(buf, r->str); - - w = makeword(buf, tail); - efree(buf); - - return w; -} - -void -Xconcatenate(void) -{ - int rn, ln; - Word *l = runner->args->word; - Word *r = runner->args->link->word; - Word *w = runner->args->link->link->word; - - ln = count(l), rn = count(r); - if(ln != 0 || rn != 0) { - if(ln == 0 || rn == 0){ - Xerror("null list in concatenation\n"); - return; - } - if(ln != 1 && rn != 1 && ln != rn) { - Xerror("mismatched list lengths in concatenation\n"); - return; - } - w = catlist(l, r, w); - } - - poplist(); - poplist(); - runner->args->word = w; -} - -void -Xdollar(void) -{ - int n; - char *s, *t; - Word *a, *star; - - if(count(runner->args->word)!=1){ - Xerror("variable name not singleton!\n"); - return; - } - s = runner->args->word->str; - // deglob(s); - n = 0; - - for(t = s;'0'<=*t && *t<='9';t++) - n = n*10+*t-'0'; - - a = runner->args->link->word; - - if(n==0 || *t) - a = copywords(var(s)->val, a); - else{ - star = var("*")->val; - if(star && 1<=n && n<=count(star)){ - while(--n) - star = star->link; - - a = makeword(star->str, a); - } - } - - poplist(); - runner->args->word = a; -} - -static -Word* -cpwords(Word *array, Word *tail, int n) -{ - Word *cp, **end; - - cp = nil, end = &cp; - while(n-- > 0){ - *end = makeword(array->str, nil); - end = &(*end)->link; - array = array->link; - } - *end = tail; - - return cp; -} - - -static -Word* -getindex(Word *array, int len, Word *index, Word *tail) -{ - char *s; - int n, m; - if(!index) - return tail; - - tail = getindex(array, len, index->link, tail); - - s = index->str; - //deglob(s) - - m = 0, n = 0; - while('0' <= *s && *s <= '9') - n = 10*n + (*s++ - '0'); - if(*s == '-'){ - if(*++s == 0) - m = len - n; - else{ - while('0' <= *s && *s <= '9') - m = 10*m + (*s++ - '0'); - m -= n; - } - } - - if(n<1 || n > len || m < 0) - return tail; - if(n+m > len) - m = len-n; - while(--n > 0) - array = array->link; - return cpwords(array, tail, m+1); -} - -void -Xindex(void) -{ - char *s; - Word *val, *ret; - - if(count(runner->args->word) != 1){ - Xerror("variable name not a singleton"); - return; - } - s = runner->args->word->str; - //deglob(s) - val = var(s)->val; - poplist(); - - ret = runner->args->link->word; // pointer to next stack frame - ret = getindex(val, count(val), runner->args->word, ret); - poplist(); - - // push result back on stack - runner->args->word = ret; -} - -void -Xjoin(void) -{ - int n; - char *s; - Word *arg, *elt; - - if(count(runner->args->word) != 1){ - Xerror("variable name is not singleton\n"); - return; - } - - s = runner->args->word->str; - // deglob(s) - - arg = var(s)->val; - poplist(); - - n = count(arg); - if(n==0){ - pushword(""); - return; - } - - for(elt = arg; elt; elt=elt->link) - n += strlen(elt->str); - - s = emalloc(n); - if(arg){ - strcpy(s, arg->str); - for(elt = arg->link; elt; elt = elt->link){ - strcat(s, " "); - strcat(s, elt->str); - } - }else - s[0] = 0; - - pushword(s); - efree(s); -} - -void -Xassign(void) -{ - Var *v; - - if(count(runner->args->word)!=1){ - Xerror("variable name not singleton!\n"); - return; - } - //deglob(runq->argv->words->word); - v = var(runner->args->word->str); - poplist(); - - //globlist(); - freewords(v->val); - v->val = runner->args->word; - v->new = 1; - if(v->update) - v->update(v); - - runner->args->word = nil; - poplist(); -} - -void -Xreadcmd(void) -{ - Thread *root; - Word *prompt; - - flush(shell.err); - root = runner; - - resetprompt(); - - if(yyparse()){ - // resource cleanup? - if(runner->flag.eof) - Xreturn(); - else - --root->code.i; - }else{ - --root->code.i; /* re-execute Xreadcmd after codebuf runs */ - run(compiled, 1, root->local, 0); - } - - killzombies(); - freeparsetree(); -} - -void -Xlocal(void) -{ - if(count(runner->args->word)!=1){ - Xerror("variable name must be singleton\n"); - return; - } - //deglob(shell->args->word->str); - - runner->local = makevar(strdup(runner->args->word->str), runner->local); - runner->local->val = copywords(runner->args->link->word, nil); - runner->local->new = 1; - - poplist(); - poplist(); -} - -void -Xunlocal(void) -{ - Var *v = runner->local, *hide; - if(!v) - fatal("Xunlocal: no locals!\n", 0); - - runner->local = v->link; - hide = var(v->name); - hide->new = 1; - - efree(v->name); - freewords(v->val); - efree(v); -} - -void -Xasync(void) -{ - int pid; - /* - int null = open("/dev/null", 0); - if(!null){ - Xerror("can not open /dev/null\n"); - return; - } - */ - - switch(pid=fork()){ - case -1: - // close(null); - Xerror("fork failed: try again"); - break; - - case 0: // child in background - initchild(runner,0); - /* pushredir(Ropen, null, 0); */ - - run(runner->code.exe, runner->code.i+1, runner->local, 0); - runner->caller = nil; - runner->flag.user = 0; - break; - - default: // parent in foreground - initparent(runner,pid,1); - // close(null); - - runner->code.i = runner->code.exe[runner->code.i].i; /* jump to end of async command */ - /* don't wait: continue running */ - } -} - -void -Xsubshell(void) -{ - int pid, user; - - user = runner->flag.user; - switch(pid=fork()){ - case -1: - Xerror("fork failed: try again"); - break; - - case 0: // child - initchild(runner, 1); - run(runner->code.exe, runner->code.i+1, runner->local, 1); - runner->caller = nil; - break; - - default: // parent - initparent(runner, pid, 0); // relinquish control - waitfor(runner, pid); // wait until child finishes - if(user){ - tcsetpgrp(0, shell.pid); - runner->flag.user = 1; // take control - } - - runner->code.i = runner->code.exe[runner->code.i].i; // jump to end of subshell command and continue execution - } -} - -void -Xpipewait(void) -{ - foreground(runner, 0); -} - -void -Xpipe(void) -{ - Thread *orig; - int pc, pid, lfd, rfd, pfd[2]; - - orig = runner; - pc = orig->code.i; - lfd = orig->code.exe[pc++].i; - rfd = orig->code.exe[pc++].i; - - if(pipe(pfd)<0){ - Xerror("can't get pipe\n"); - return; - } - - switch(pid=fork()){ - case -1: - Xerror("try again"); - break; - case 0: // child - initchild(runner,1); - - /* child 0 (writer) forked process */ - run(runner->code.exe, pc+2, runner->local, 1); - runner->caller = nil; - - close(pfd[0]); - pushredir(Ropen, pfd[1], lfd); - break; - - default: // parent - initparent(runner,pid,0); - - /* child 1 (reader) subprocess*/ - run(runner->code.exe, runner->code.exe[pc].i, runner->local, 1); - - close(pfd[1]); - pushredir(Ropen, pfd[0], rfd); - - orig->code.i = orig->code.exe[pc+1].i; - break; - } -} - -void -Xbasic(void) -{ - Var *v; - Word *arg; - int pid, status; - struct Builtin *b; - - arg = runner->args->word; - if(!arg){ - Xerror("empty argument list\n"); - return; - } - - v = var(arg->str); - if(v->func){ - return; - } - - // see if it matches a builtin - for(b = builtin; b->name; b++){ - if(strcmp(b->name, arg->str)==0){ - b->func(); - return; - } - } - - /* if we are here then it's an external command */ - if(exitsnext()){ // if we exit immediately, no need to fork - pushword("exec"); - xx(); - Xexit(); - } - - // run the external command - if((pid = xforkx()) < 0) { - Xerror("try again"); - return; - } - - poplist(); - foreground(runner, 0); // waits for child -} - -void -Xcount(void) -{ - Word *arg; - char *str, num[12]; - - if(count(runner->args->word) != 1){ - Xerror("variable name not a singleton\n"); - return; - } - - str = runner->args->word->str; - arg = var(str)->val; - poplist(); - - itoa(num, count(arg)); - pushword(num); -} - -void -Xflat(void) -{ - int len; - char *str; - Word *arg, *a; - - if(count(runner->args->word)!=1){ - Xerror("variable name is not a singleton\n"); - return; - } - - str = runner->args->word->str; - arg = var(str)->val; - poplist(); - - len = count(arg); - if(!len){ - pushword(""); - return; - } - - for(a=arg; a; a=a->link) - len += strlen(a->str); - - str = emalloc(len); - if(arg){ - strcpy(str, arg->str); - for(a = arg->link; a; a = a->link){ - strcat(str," "); - strcat(str,a->str); - } - }else - str[0] = 0; - - pushword(str); - efree(str); -} - -void -Xbang(void) -{ - if(runner->status) - runner->status = 0; - else - runner->status = 1; -} - -void -Xpopredir(void) -{ - Redir *r = runner->redir.end; - if(!r) - fatal("attempted to pop a nil redir\n"); - - runner->redir.end = runner->redir.end->link; - if(r->type==Ropen) - close(r->from); - - efree(r); -} - -void -Xreturn(void) -{ - Thread *curr = runner; - - switch(curr->wait.status){ - /* - * If our job is still running or suspended we must: - * 1. move program one step back to rerun Xreturn upon recall - * 2. return to our calling thread - * 3. don't free! - */ - case Prun: - report(curr, 0); - curr->flag.user = 0; - case Pstop: - curr->code.i--; - runner = curr->caller; - curr->caller = nil; // detach job - return; - /* - * If our job has finished: - * 1. remove from our list - * 2. continue to clean up its memory - */ - case Pdone: - deljob(curr); - /* fallthrough */ - default: - ; - } - - undoredirs(); - - while(curr->args) - poplist(); - freecode(curr->code.exe); - efree(curr->wait.on); - - runner = curr->caller; - efree(curr); - if(!runner) - exit(0); -} - -void -Xexit(void) -{ - exit(runner->status); -} - -void -Xerror(char *msg) -{ - print(shell.err, "rc: %s", msg); - flush(shell.err); - while(!runner->flag.user) - Xreturn(); -} - diff --git a/sys/cmd/rc/exec.h b/sys/cmd/rc/exec.h deleted file mode 100644 index a3a6ae9..0000000 --- a/sys/cmd/rc/exec.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/* - * opcode routines - * arguments on stack (...) - * arguments in line [...] - * code in line with jump around {...} - */ - -void Xmark(void); // Xmark marks stack location for word -void Xindex(void); // Xindex -void Xlocal(void); // Xlocal(name,val) create local variable, assign value -void Xunlocal(void); // Xunlocal delete local variable -void Xdollar(void); // Xdollar(name) get value of name -void Xtrue(void); // Xtrue{...} execute {} if true -void Xfalse(void); // Xfalse{...} execute {} if false -void Xgoto(void); // Xgoto[addr] goto address -void Xfor(void); // Xfor(var, list){... Xreturn} -void Xreadcmd(void); // -void Xassign(void); -void Xbang(void); -void Xasync(void); -void Xbasic(void); // Xbasic(args) run command and wait for result -void Xsubshell(void); -void Xword(void); -void Xjoin(void); -void Xconcatenate(void); -void Xcount(void); -void Xflat(void); -void Xpipe(void); -void Xpipewait(void); -void Xpopredir(void); - -void Xreturn(void); -void Xexit(void); - -void Xerror(char*); - -/* builtin commands */ -void xcd(void); -void xdot(void); -void xecho(void); -void xexit(void); -void xfg(void); -void xjob(void); - -void xboot(int argc, char *argv[]); diff --git a/sys/cmd/rc/input.c b/sys/cmd/rc/input.c deleted file mode 100644 index cc2383d..0000000 --- a/sys/cmd/rc/input.c +++ /dev/null @@ -1,1679 +0,0 @@ -#include "rc.h" - -#include -#include - -/* don't change order of these without modifying matrix */ -enum -{ - NonPrintable, - Alnum, - Punctuation, - Space -}; - -static int ascii[256] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, - 2, 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, 2, 2, 2, 2, 1, - 2, 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, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -struct Mode -{ - ushort raw : 1; - ushort multiline : 1; - ushort mask : 1; - ushort defer : 1; - struct { - ushort on : 1; - ushort insert : 1; - } vi ; -}; - -/* - * the structure represents the state during line editing. - * we pass this state to functions implementing specific editing functionalities - */ -struct TerminalState -{ - int ifd; /* terminal stdin file descriptor. */ - int ofd; /* terminal stdout file descriptor. */ - - struct{ - char *s; /* raw UTF-8 bytes */ - int len; /* number of bytes in prompt */ - int size; /* number of (printed) runes in prompt */ - } prompt; - - struct{ - intptr cap; /* capacity of edit buffer */ - intptr len; /* current number of bytes stored */ - intptr pos; /* position within edit buffer */ - char *buf; - } edit; /* edit buffer */ - - struct{ - intptr cap; /* number of columns in terminal */ - intptr len; /* current edited line length (in runes) */ - intptr pos; /* current cursor position (in runes) */ - intptr old; /* previous refresh cursor position (in runes) */ - } cursor; - - struct{ - intptr cap; - intptr len; - char *buf; - } yank; /* yank buffer */ - - intptr maxrows; /* maximum num of rows used so far (multiline mode) */ - intptr history; /* index of history we are currently editing */ -}; - -/* - * line history (circular buffer) - */ -struct History -{ - char **bot, **top, *entry[1024]; -}; - -/* globals */ -static struct Mode mode; -static struct History history; -static struct termios originalterm; - -enum -{ - KeyNil = 0, /* nil */ - KeyCtrlA = 1, /* Ctrl+a */ - KeyCtrlB = 2, /* Ctrl-b */ - KeyCtrlC = 3, /* Ctrl-c */ - KeyCtrlD = 4, /* Ctrl-d */ - KeyCtrlE = 5, /* Ctrl-e */ - KeyCtrlF = 6, /* Ctrl-f */ - KeyCtrlH = 8, /* Ctrl-h */ - KeyTab = 9, /* Tab */ - KeyCtrlK = 11, /* Ctrl+k */ - KeyCtrlL = 12, /* Ctrl+l */ - KeyEnter = 13, /* Enter */ - KeyCtrlN = 14, /* Ctrl-n */ - KeyCtrlP = 16, /* Ctrl-p */ - KeyCtrlT = 20, /* Ctrl-t */ - KeyCtrlU = 21, /* Ctrl+u */ - KeyCtrlW = 23, /* Ctrl+w */ - KeyEsc = 27, /* Escape */ - KeyBackspace = 127 /* Backspace */ -}; - -static void doatexit(void); - -/* vi operations */ -typedef struct -{ - intptr buffer; - intptr cursor; -} Position; - -typedef Position (*Noun)(struct TerminalState*, int); -typedef void (*Verb)(struct TerminalState*, Position); - -static -int -runetype(rune r) -{ - if(r<128) - return ascii[r]; - if(utf8·isspace(r)) - return Space; - if(utf8·isdigit(r) || utf8·isalpha(r)) - return Alnum; - if(utf8·ispunct(r)) - return Punctuation; - - return NonPrintable; -} - -static -void -normalcursor(int fd) -{ - write(fd,"\e[2 q",5); -} - -static -void -insertcursor(int fd) -{ - write(fd,"\e[6 q",5); -} - -/* raw mode: 1960 magic shit. */ -static -int -enterraw(int fd) -{ - struct termios raw; - - if(!shell.interactive) - goto fatal; - - if(!mode.defer){ - atexit(doatexit); - mode.defer = 1; - } - if(tcgetattr(fd,&originalterm) == -1) - goto fatal; - - raw = originalterm; /* modify the original mode */ - - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ - raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - - /* put terminal in raw mode after flushing */ - if(tcsetattr(fd,TCSAFLUSH,&raw) < 0) - goto fatal; - - mode.raw = 1; - return 1; - -fatal: - errno = ENOTTY; - return 0; -} - -static -void -exitraw(int fd) -{ - /* don't even check the return value as it's too late. */ - if(mode.raw && tcsetattr(fd,TCSAFLUSH,&originalterm) != -1) - mode.raw = 0; -} - -/* use the esc [6n escape sequence to query the horizontal cursor position - * and return it. on error -1 is returned, on success the position of the - * cursor. */ -static -int -cursorposition(int ifd, int ofd) -{ - char buf[32]; - int cols, rows; - unsigned int i = 0; - - /* Report cursor location */ - if(write(ofd, "\x1b[6n", 4) != 4) - return -1; - - /* Read the response: ESC [ rows ; cols R */ - while(i < sizeof(buf)-1) { - if(read(ifd,buf+i,1) != 1) - break; - if(buf[i] == 'R') - break; - i++; - } - buf[i] = '\0'; - - /* Parse it. */ - if(buf[0] != KeyEsc || buf[1] != '[') - return -1; - if(sscanf(buf+2,"%d;%d",&rows,&cols) != 2) - return -1; - - return cols; -} - -/* try to get the number of columns in the current terminal, or assume 80 if it fails. */ -static -int -columns(int ifd, int ofd) -{ - struct winsize ws; - - if(ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0){ - /* ioctl() failed. Try to query the terminal itself. */ - int start, cols; - - /* Get the initial position so we can restore it later. */ - start = cursorposition(ifd,ofd); - if(start == -1) - goto failed; - - /* Go to right margin and get position. */ - if(write(ofd,"\x1b[999C",6) != 6) - goto failed; - cols = cursorposition(ifd,ofd); - if(cols == -1) - goto failed; - - /* Restore position. */ - if(cols > start){ - char esc[32]; - snprintf(esc,32,"\x1b[%dD",cols-start); - if(write(ofd,esc,strlen(esc)) == -1) - ; - } - return cols; - }else - return ws.ws_col; - -failed: - return 80; -} - -static -void -clear(void) -{ - if(write(1,"\x1b[H\x1b[2J",7) <= 0) - ; -} - -/* beep: used for completion when there is nothing to complete or when all - * the choices were already shown. */ -static -void -beep(void) -{ - fprintf(stderr, "\x7"); - fflush(stderr); -} - -// ----------------------------------------------------------------------- -// command history - -void -inithistory(void) -{ - history.bot = history.top = history.entry; -} - -int -addhistory(char *line) -{ - char *copy; - - copy = strdup(line); - if(!copy) - return 0; - - *history.top++ = copy; - if(history.top == arrend(history.entry)) - history.top = history.entry; - - if(history.top == history.bot){ - efree(history.bot); - history.bot++; - } - - return 1; -} - -static -void -pophistory(void) -{ - if(--history.top < history.entry) - history.top = arrend(history.entry)-1; - efree(*history.top); -} - -static void refreshline(struct TerminalState *); - -static -char ** -currenthistory(struct TerminalState *term, intptr *size) -{ - char **entry; - intptr len, head; - - if(history.top > history.bot){ - len = history.top - history.bot; - entry = history.top - term->history - 1; - }else if(history.top < history.bot){ - len = (arrend(history.entry) - history.bot) + (history.top - history.entry); - if((head=history.top - history.entry) < term->history) - entry = arrend(history.entry) - head; - else - entry = history.top - term->history - 1; - }else - return nil; - - *size = len; - return entry; -} - -static -void -usehistory(struct TerminalState *term, int d) -{ - rune r; - intptr w, len; - char *b, *e, **entry; - - if(!(entry = currenthistory(term, &len))) - return; - - efree(*entry); - *entry = strdup(term->edit.buf); - - term->history += d; - if(term->history < 0){ - term->history = 0; - return; - }else if(term->history >= len){ - term->history = len - 1; - return; - } - entry = currenthistory(term, &len); - - strncpy(term->edit.buf, *entry, term->edit.cap); - term->edit.buf[term->edit.cap-1] = 0; - - /* update cursor/buffer positions */ - term->edit.len = term->edit.pos = strlen(term->edit.buf); - for(w=0, b=term->edit.buf, e=term->edit.buf+term->edit.len; b < e; ){ - b += utf8·decode(b, &r); - w += utf8·runewidth(r); - } - term->cursor.len = term->cursor.pos = w; - - refreshline(term); -} - -// ----------------------------------------------------------------------- -// line editing - -/* - * we define a very simple "append buffer" structure, that is an heap - * allocated string where we can append to. this is useful in order to - * write all the escape sequences in a buffer and flush them to the standard - * output in a single call, to avoid flickering effects. - */ - -struct Buffer -{ - int len; - char *b; -}; - -static -void -initbuffer(struct Buffer *ab) -{ - ab->b = nil; - ab->len = 0; -} - -static -void -append(struct Buffer *ab, const char *s, int len) -{ - char *new = realloc(ab->b,ab->len+len); - - if (new == nil) return; - memcpy(new+ab->len,s,len); - ab->b = new; - ab->len += len; -} - -static -void -freebuffer(struct Buffer *ab) -{ - free(ab->b); -} - -/* single line low level line refresh. - * - * rewrite the currently edited line accordingly to the buffer content, - * cursor position, and number of columns of the terminal. */ -static -void -refreshsingleline(struct TerminalState *term) -{ - char esc[64]; - struct Buffer ab; - - int n, w; - rune r; - int fd = term->ofd; - intptr off = term->prompt.size; - char *buf = term->edit.buf; - intptr len = term->edit.len; - intptr pos = term->cursor.pos; - intptr col = term->cursor.len; - - while((off+pos) >= term->cursor.cap){ - n = utf8·decode(buf, &r); - w = utf8·runewidth(r); - - buf+=n, len-=n; - pos-=w, col-=w; - } - - assert(buf <= term->edit.buf + len); - - while(off+col > term->cursor.cap){ - n = utf8·decodeprev(buf+len-1, &r); - w = utf8·runewidth(r); - - len-=n, col-=w; - } - assert(len >= 0); - - initbuffer(&ab); // TODO: do we need so much malloc pressure? - - /* move cursor to left edge */ - snprintf(esc,64,"\r"); - append(&ab,"\r",1); - - /* write the prompt and the current buffer content */ - append(&ab, term->prompt.s, term->prompt.len); - - if(mode.mask == 1) - while(len--) - append(&ab,"*",1); - else - append(&ab,buf,len); - - snprintf(esc,64,"\x1b[0K"); // erase to right - append(&ab,esc,strlen(esc)); - - snprintf(esc,64,"\r\x1b[%dC", (int)(off+pos)); // move cursor to original position - append(&ab,esc,strlen(esc)); - - if(write(fd,ab.b,ab.len) == -1) /* can't recover from write error. */ - ; - - freebuffer(&ab); -} - -/* multi line low level line refresh. - * - * Rewrite the currently edited line accordingly to the buffer content, - * cursor position, and number of columns of the terminal. */ -static -void -refreshmultilines(struct TerminalState *term) -{ -#if 0 - char esc[64]; - int plen = term->plen; - int rows = (plen+term->len+term->cols-1)/term->cols; /* rows used by current buf. */ - int rpos = (plen+term->oldpos+term->cols)/term->cols; /* cursor relative row. */ - int rpos2; /* rpos after refresh. */ - int col; /* colum position, zero-based. */ - int i; - int old_rows = term->maxrows; - int fd = term->ofd, j; - struct Buffer ab; - - /* Update maxrows if needed. */ - if(rows > (int)term->maxrows) - term->maxrows = rows; - - /* First step: clear all the lines used before. To do so start by - * going to the last row. */ - initbuffer(&ab); - if(old_rows-rpos > 0){ - snprintf(esc,64,"\x1b[%dB", old_rows-rpos); - append(&ab,esc,strlen(esc)); - } - - /* Now for every row clear it, go up. */ - for(j = 0; j < old_rows-1; j++){ - snprintf(esc,64,"\r\x1b[0K\x1b[1A"); - append(&ab,esc,strlen(esc)); - } - - /* clean the top line. */ - snprintf(esc,64,"\r\x1b[0K"); - append(&ab,esc,strlen(esc)); - - /* Write the prompt and the current buffer content */ - append(&ab,term->prompt,strlen(term->prompt)); - if(mode.mask == 1){ - for(i = 0; i < term->len; i++) append(&ab,"*",1); - }else - append(&ab,term->buf,term->len); - - /* If we are at the very end of the screen with our prompt, we need to - * emit a newline and move the prompt to the first column. */ - if(term->pos && term->pos == term->len && (term->pos+plen) % term->cols == 0) { - append(&ab,"\n",1); - snprintf(esc,64,"\r"); - append(&ab,esc,strlen(esc)); - rows++; - if(rows > (int)term->maxrows) - term->maxrows = rows; - } - - /* Move cursor to right position. */ - rpos2 = (plen+term->pos+term->cols)/term->cols; /* current cursor relative row. */ - - /* Go up till we reach the expected positon. */ - if(rows-rpos2 > 0){ - snprintf(esc,64,"\x1b[%dA", rows-rpos2); - append(&ab,esc,strlen(esc)); - } - - /* Set column. */ - col = (plen+(int)term->pos) % (int)term->cols; - if(col) - snprintf(esc,64,"\r\x1b[%dC", col); - else - snprintf(esc,64,"\r"); - append(&ab,esc,strlen(esc)); - - term->oldpos = term->pos; - - if(write(fd,ab.b,ab.len) == -1) /* Can't recover from write error. */ - ; - - freebuffer(&ab); -#endif -} - -/* Calls the two low level functions refreshSingleLine() or - * refreshMultiLine() according to the selected mode. */ -static -void -refreshline(struct TerminalState *term) -{ - if(mode.multiline) - refreshmultilines(term); - else - refreshsingleline(term); -} - -/* insert the rune 'c' at cursor current position. - * on error writing to the terminal -1 is returned, otherwise 0. */ -int -insertrune(struct TerminalState *term, int n, char *c) -{ - int w; - rune r; - - utf8·decode(c, &r); - w = utf8·runewidth(r); - - if(term->edit.len + n <= term->edit.cap){ - if(term->edit.pos == term->edit.len){ - memcpy(term->edit.buf+term->edit.pos, c, n); - - term->edit.pos += n, term->edit.len += n; - term->cursor.pos += w, term->cursor.len += w; - - term->edit.buf[term->edit.len] = '\0'; - - if(!mode.multiline && ((term->prompt.size+term->cursor.pos+n) <= term->cursor.cap)){ - if(mode.mask){ - c = "*"; - n = 1; - } - if(write(term->ofd, c, n) == -1) - return 0; - } - refreshline(term); - }else{ - memmove(term->edit.buf+term->edit.pos+n, term->edit.buf+term->edit.pos, term->edit.len-term->edit.pos); - memcpy(term->edit.buf+term->edit.pos, c, n); - - term->edit.pos += n, term->edit.len += n; - term->cursor.pos += w, term->cursor.len += w; - - term->edit.buf[term->edit.len] = '\0'; - refreshline(term); - } - } - - return 1; -} - -int -insertbytes(struct TerminalState *term, int len, char *buf) -{ - int nr; - if(term->edit.len + len > term->edit.cap){ - len = term->edit.cap - term->edit.len; - buf[len] = 0; - } - nr = utf8·len(buf); - - if(term->edit.pos == term->cursor.len){ - memcpy(term->edit.buf+term->edit.len, buf, len); - - term->edit.pos += len, term->edit.len += len; - term->cursor.pos += nr, term->cursor.len += nr; - - // XXX: transfer the modeline here? - term->edit.buf[term->edit.len] = '\0'; - refreshline(term); - }else{ - memmove(term->edit.buf+term->edit.pos+len,term->edit.buf+term->edit.pos,term->edit.len-term->edit.pos); - memcpy(term->edit.buf+term->edit.pos, buf, len); - - term->edit.pos += len, term->edit.len += len; - term->cursor.pos += nr, term->cursor.len += nr; - - term->edit.buf[term->edit.len] = '\0'; - refreshline(term); - } - - return 1; -} - -// ----------------------------------------------------------------------- -// vi functionality - -/* modes */ - -static -void -normalmode(int fd) -{ - mode.vi.insert = 0; - normalcursor(fd); -} - -static -void -insertmode(int fd) -{ - mode.vi.insert = 1; - insertcursor(fd); -} - -/* actions */ - -static -void -move(struct TerminalState *term, Position to) -{ - if(to.buffer != term->edit.pos){ - term->edit.pos = to.buffer; - term->cursor.pos = to.cursor; - refreshline(term); - } -} - -static -void -yank(struct TerminalState *term, Position to) -{ - intptr len, off; - - if(to.buffer == term->edit.pos) - return; // noop - - if(to.buffer > term->edit.pos){ - len = to.buffer - term->edit.pos; - off = term->edit.pos; - }else{ - len = term->edit.pos - to.buffer; - off = to.buffer; - } - - if(term->yank.cap < len+1){ - efree(term->yank.buf); - term->yank.cap = len+1; - term->yank.buf = emalloc(len+1); - } - term->yank.len = len; - memcpy(term->yank.buf, term->edit.buf+off, len); - term->yank.buf[len] = 0; -} - -static -void -delete(struct TerminalState *term, Position to) -{ - intptr diff; - - // delete characters in front of us (exclusive) - if(to.buffer > term->edit.pos){ - diff = to.buffer - term->edit.pos; - memmove(term->edit.buf+term->edit.pos, term->edit.buf+to.buffer, term->edit.len-to.buffer+1); - term->edit.len -= diff; - - diff = to.cursor - term->cursor.pos; - goto refresh; - } - - // delete characters behind us - if(to.buffer < term->edit.pos){ - diff = term->edit.pos - to.buffer; - memmove(term->edit.buf+to.buffer, term->edit.buf+term->edit.pos, term->edit.len-term->edit.pos+1); - term->edit.pos = to.buffer; - term->edit.len -= diff; - - diff = term->cursor.pos - to.cursor; - term->cursor.pos = to.cursor; - goto refresh; - } - // do nothing - return; - -refresh: - term->cursor.len -= diff; - refreshline(term); -} -/* movements */ - -#define CURRENT(term) (Position){ .buffer=(term)->edit.pos, .cursor=(term)->cursor.pos }; - -// move cursor to the left n boxes -static -Position -left(struct TerminalState *term, int n) -{ - rune r; - int w, d; - Position pos = CURRENT(term); - char *buf = term->edit.buf + term->edit.pos; - - d = 0; - while(n > 0 && buf > term->edit.buf){ - buf -= utf8·decodeprev(buf-1, &r); - - w = utf8·runewidth(r); - n -= w; - d += w; - } - - pos.cursor = MAX(pos.cursor-d, 0); - pos.buffer = MAX(buf-term->edit.buf, 0); - return pos; -} - -// move cursor to the right n boxes -static -Position -right(struct TerminalState *term, int n) -{ - rune r; - int w, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - char *end = term->edit.buf + term->edit.len; - - d = 0; - while(n > 0 && buf < end){ - buf += utf8·decode(buf, &r); - - w = utf8·runewidth(r); - n -= w; - d += w; - } - - pos.cursor = MIN(pos.cursor+d, term->cursor.len); - pos.buffer = MIN(buf-term->edit.buf, term->edit.len); - return pos; -} - -static -Position -prevword(struct TerminalState *term, int n) -{ - rune r; - int c, w, b, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - - d = 0; - while(n-- > 0 && buf > term->edit.buf){ - eatspace: - b = utf8·decodeprev(buf-1, &r); - w = utf8·runewidth(r); - if((c=runetype(r)) == Space){ - buf -= b; - d += w; - - if(buf <= term->edit.buf) - break; - - goto eatspace; - } - - eatword: - if(runetype(r) == c){ - buf -= b; - d += w; - - if(buf <= term->edit.buf) - break; - - b = utf8·decodeprev(buf-1, &r); - w = utf8·runewidth(r); - - goto eatword; - } - } - - pos.cursor = MAX(pos.cursor-d, 0); - pos.buffer = MAX(buf-term->edit.buf, 0); - return pos; -} - -static -Position -nextword(struct TerminalState *term, int n) -{ - rune r; - int c, b, w, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - char *end = term->edit.buf + term->edit.len; - - d = 0; - while(n-- > 0 && buf < end){ - b = utf8·decode(buf, &r); - w = utf8·runewidth(r); - c = runetype(r); - eatword: - if(runetype(r) == c){ - buf += b; - d += w; - - if(buf >= end) - break; - - b = utf8·decode(buf, &r); - w = utf8·runewidth(r); - goto eatword; - } - eatspace: - while((c=runetype(r)) == Space){ - buf += b; - d += w; - - if(buf >= end) - break; - - b = utf8·decode(buf, &r); - w = utf8·runewidth(r); - goto eatspace; - } - } - - pos.cursor = MIN(pos.cursor+d, term->cursor.len); - pos.buffer = MIN(buf-term->edit.buf, term->edit.len); - return pos; -} - - -static -Position -prevWord(struct TerminalState *term, int n) -{ - rune r; - int c, w, b, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - - d = 0; - while(n-- > 0 && buf > term->edit.buf){ - eatspace: - b = utf8·decodeprev(buf-1, &r); - w = utf8·runewidth(r); - if((c=runetype(r)) == Space){ - buf -= b; - d += w; - - if(buf <= term->edit.buf) - break; - - goto eatspace; - } - - eatword: - if((c=runetype(r)) != Space){ - buf -= b; - d += w; - - if(buf <= term->edit.buf) - break; - - b = utf8·decodeprev(buf-1, &r); - w = utf8·runewidth(r); - - goto eatword; - } - } - - pos.cursor = MAX(pos.cursor-d, 0); - pos.buffer = MAX(buf-term->edit.buf, 0); - return pos; -} - -static -Position -nextWord(struct TerminalState *term, int n) -{ - rune r; - int b, w, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - char *end = term->edit.buf + term->edit.len; - - d = 0; - while(n-- > 0 && buf < end){ - eatword: - b = utf8·decode(buf, &r); - w = utf8·runewidth(r); - if(runetype(r) != Space){ - buf += b; - d += w; - - if(buf > end) - break; - - goto eatword; - } - - eatspace: - if(runetype(r) == Space){ - buf += b; - d += w; - - if(buf > end) - break; - - b = utf8·decode(buf, &r); - w = utf8·runewidth(r); - - goto eatspace; - } - } - - pos.cursor = MIN(pos.cursor+d, term->cursor.len); - pos.buffer = MIN(buf-term->edit.buf, term->edit.len); - return pos; -} - -static -Position -nextend(struct TerminalState *term, int n) -{ - rune r; - int c, b, w, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - char *end = term->edit.buf + term->edit.len; - - d = 0; - while(n-- > 0 && buf+1 < end){ - eatspace: - b = utf8·decode(buf+1, &r); - w = utf8·runewidth(r); - while((c=runetype(r)) == Space){ - buf += b; - d += w; - - if(buf+1 >= end) - break; - - goto eatspace; - } - eatword: - if(runetype(r) == c){ - buf += b; - d += w; - - if(buf+1 >= end) - break; - - b = utf8·decode(buf+1, &r); - w = utf8·runewidth(r); - goto eatword; - } - } - - pos.cursor = MIN(pos.cursor+d, term->cursor.len); - pos.buffer = MIN(buf-term->edit.buf, term->edit.len); - return pos; -} - -static -Position -nextEnd(struct TerminalState *term, int n) -{ - rune r; - int b, w, d; - Position pos = CURRENT(term); - - char *buf = term->edit.buf + term->edit.pos; - char *end = term->edit.buf + term->edit.len; - - d = 0; - while(n-- > 0 && buf+1 < end){ - eatspace: - b = utf8·decode(buf+1, &r); - w = utf8·runewidth(r); - if(runetype(r) == Space){ - buf += b; - d += w; - - if(buf+1 > end) - break; - - goto eatspace; - } - - eatword: - if(runetype(r) != Space){ - buf += b; - d += w; - - if(buf+1 > end) - break; - - b = utf8·decode(buf+1, &r); - w = utf8·runewidth(r); - - goto eatword; - } - } - - pos.cursor = MIN(pos.cursor+d, term->cursor.len); - pos.buffer = MIN(buf-term->edit.buf, term->edit.len); - return pos; -} - -#define HOME(term) (Position){0} -#define END(term) (Position){(term)->edit.len, (term)->cursor.len} - -static -int -vi(struct TerminalState *term, char c) -{ - int n = 1; - Verb verb = move; - -action: - switch(c){ - /* # of repeats */ - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - n = 0; - while('0' <= c && c <= '9'){ - n = 10*n + (c-'0'); - if(read(term->ifd, &c, 1)<1) - return -1; - } - goto action; - - /* composable actions */ - case 'l': verb(term, right(term, n)); break; - case 'h': verb(term, left(term, n)); break; - case '0': verb(term, HOME(term)); break; - case '$': verb(term, END(term)); break; - case 'b': verb(term, prevword(term,n)); break; - case 'B': verb(term, prevWord(term,n)); break; - case 'w': verb(term, nextword(term,n)); break; - case 'W': verb(term, nextWord(term,n)); break; - case 'e': verb(term, nextend(term,n)); break; - case 'E': verb(term, nextEnd(term,n)); break; - - /* verb switches */ - case 'd': // delete - verb = delete; - if(read(term->ifd, &c, 1)<1) - return -1; - /* special cases */ - switch(c){ - case 'd': - move(term, HOME(term)); - delete(term, END(term)); - return 0; - default: - goto action; - } - case 'y': // yank - verb = yank; - if(read(term->ifd, &c, 1)<1) - return -1; - /* special cases */ - switch(c){ - case 'y': - if(term->yank.cap < term->edit.len+1){ - efree(term->yank.buf); - term->yank.len = term->edit.len; - term->yank.cap = term->edit.len+1; - term->yank.buf = emalloc(term->yank.cap); - } - memcpy(term->yank.buf, term->edit.buf, term->edit.len+1); - break; - default: - goto action; - } - break; - - case 'p': // put - insertbytes(term, term->yank.len, term->yank.buf); - refreshline(term); - return 0; - - /* special cases - * sadly I don't know a better way than to have these checks for move - * the vi language doesn't fully compose - */ - case 'i': insertmode: - if(verb != move) goto unrecognized; - insertmode(term->ofd); - break; - - case 'I': - if(verb != move) goto unrecognized; - move(term, HOME(term)); - goto insertmode; - - case 'a': - if(verb != move) goto unrecognized; - if(term->edit.pos < term->edit.len){ - term->edit.pos++; - refreshline(term); - } - goto insertmode; - - case 'A': - if(verb != move) goto unrecognized; - move(term, END(term)); - goto insertmode; - - case 'x': - if(verb != move) goto unrecognized; - delete(term, right(term, 1)); - break; - - case 'X': - if(verb != move) goto unrecognized; - delete(term, left(term, 1)); - break; - - case 'r': - if(verb != move) goto unrecognized; - if(read(term->ifd, &c, 1)<1) - return -1; - if(c < ' ') - break; - term->edit.buf[term->edit.pos] = c; - refreshline(term); - break; - - // TODO: replace mode? - - case 'c': - if(verb != move) goto unrecognized; - insertmode(term->ofd); - verb = delete; - if(read(term->ifd, &c, 1)<1) - return -1; - goto action; - - case 'C': - if(verb != move) goto unrecognized; - insertmode(term->ofd); - goto deleteln; - - case 'D': - if(verb != move) goto unrecognized; - deleteln: - term->edit.len = term->edit.pos; - term->edit.buf[term->edit.pos] = 0; - refreshline(term); - break; - - default: unrecognized: - beep(); - break; - } - - return 0; -} -#undef END - -#define END(term) (Position){(term).edit.len, (term).cursor.len} - -static -int -size(char *s) -{ - rune c; - int n, len = 0;; - while((c=*s)){ - if(c == '\033'){ - n = 1; - esccode: - c = s[n]; - if(!c) // we hit end of string in the middle of parsing an escape code! - return len; - if(c == 'm'){ - s += n + 1; - continue; // outer loop - } - n++; - goto esccode; - } - n = utf8·decode(s, &c); - s += n; - len += utf8·runewidth(c); - } - return len; -} - -/* this function is the core of the line editing capability of linenoise. - * it expects 'fd' to be already in "raw mode" so that every key pressed - * will be returned asap to read(). - * - * the resulting string is put into 'buf' when the user type enter, or - * when ctrl+d is typed. - * - * the function returns the length of the current buffer. */ -static -int -interact(int ifd, int ofd, char *buf, intptr len, char *prompt) -{ - int n, aux; - char esc[3]; - char c[UTFmax+1] = { 0 }; - rune r; - - struct TerminalState term; - /* - * populate the state that we pass to functions implementing - * specific editing functionalities - */ - term.ifd = ifd; - term.ofd = ofd; - - term.edit.buf = buf; - term.edit.cap = len; - term.edit.len = 0; - term.edit.pos = 0; - - term.prompt.s = prompt; - term.prompt.len = strlen(prompt); - term.prompt.size = size(prompt); - - term.cursor.pos = 0; - term.cursor.len = 0; - term.cursor.cap = columns(ifd, ofd); - - term.maxrows = 0; - term.history = 0; - - term.yank.buf = nil; - term.yank.cap = term.yank.len = 0; - - /* buffer starts empty. */ - term.edit.buf[0] = '\0'; - term.edit.cap--; /* make sure there is always space for the nulterm */ - - /* push current (empty) command onto history stack */ - addhistory(""); - - if(write(term.ofd,prompt,term.prompt.len) == -1) - return -1; - - for(;;){ - n = read(term.ifd,c,1); - if(n <= 0) - goto finish; - - /* partition input by rune */ - if(utf8·onebyte(c[0])){ - r = c[0]; - }else if(utf8·twobyte(c[0])){ - n = read(term.ifd,c+1,1); - if(n < 1 || (n=utf8·decode(c, &r)) != 2) - goto finish; - }else if(utf8·threebyte(c[0])){ - n = read(term.ifd,c+1,2); - if(n < 2 || (n=utf8·decode(c, &r)) != 3) - goto finish; - }else if(utf8·fourbyte(c[0])){ - n = read(term.ifd,c+1,3); - if(n < 3 || (n=utf8·decode(c, &r)) != 4) - goto finish; - }else - goto finish; - - switch(r){ - case KeyEnter: - pophistory(); - if(mode.multiline) - move(&term, END(term)); - goto finish; - - case KeyCtrlC: - errno = EAGAIN; - return -1; - - case KeyBackspace: - case KeyCtrlH: - delete(&term, left(&term, 1)); - break; - - case KeyCtrlD: - if(term.edit.len > 0) - delete(&term, right(&term, 1)); - break; - - case KeyCtrlT: - if(term.edit.pos > 0 && term.edit.pos < term.edit.len){ - aux = buf[term.edit.pos-1]; - - buf[term.edit.pos-1] = buf[term.edit.pos]; - buf[term.edit.pos] = aux; - - if(term.edit.pos != term.edit.len-1) - term.edit.pos++; - - refreshline(&term); - } - break; - - case KeyCtrlB: - move(&term, left(&term, 1)); - break; - - case KeyCtrlF: /* ctrl-f */ - move(&term, right(&term, 1)); - break; - - case KeyCtrlP: /* ctrl-p */ - usehistory(&term, +1); - break; - - case KeyCtrlN: /* ctrl-n */ - usehistory(&term, -1); - break; - - case KeyEsc: /* escape sequence */ - /* - * try to read two bytes representing the escape sequence. - * if we read less than 2 and we are in vi mode, interpret as command - * - * NOTE: we could do a timed read here - */ - switch(read(term.ifd,esc,2)){ - case 0: - if(mode.vi.on){ - if(mode.vi.insert){ - normalmode(term.ofd); - if(term.edit.pos > 0){ - --term.edit.pos; - refreshline(&term); - } - continue; - } - } - case 1: - if(mode.vi.on){ - if(mode.vi.insert){ - normalmode(term.ofd); - if(vi(&term,esc[0]) < 0){ - term.edit.len = -1; - goto finish; - } - continue; - } - } - default: // 2 - ; - } - - /* ESC [ sequences. */ - if(esc[0] == '['){ - if(0 <= esc[1] && esc[1] <= '9'){ - /* extended escape, read additional byte. */ - if(read(term.ifd,esc+2,1) == -1) - break; - - if(esc[2] == '~'){ - switch(esc[1]){ - case '3': /* delete key. */ - delete(&term, left(&term,1)); - break; - } - } - }else{ - switch(esc[1]) { - case 'A': /* up */ - usehistory(&term, +1); - break; - case 'B': /* down */ - usehistory(&term, -1); - break; - case 'C': /* right */ - move(&term, right(&term, 1)); - break; - case 'D': /* left */ - move(&term, left(&term, 1)); - break; - case 'H': /* home */ - move(&term, HOME(term)); - break; - case 'F': /* end*/ - move(&term, END(term)); - break; - } - } - } - /* ESC O sequences. */ - else if(esc[0] == 'O'){ - switch(esc[1]) { - case 'H': /* home */ - move(&term, HOME(term)); - break; - case 'F': /* end*/ - move(&term, END(term)); - break; - } - } - break; - - default: - if(mode.vi.on && !mode.vi.insert && n == 1){ - if(vi(&term,c[0]) < 0){ - term.edit.len = -1; - goto finish; - } - }else if(!insertrune(&term,n,c)){ - term.edit.len = -1; - goto finish; - } - - break; - - case KeyCtrlU: /* Ctrl+u, delete the whole line. */ - buf[0] = '\0'; - term.edit.pos = term.edit.len = 0; - term.cursor.pos = term.cursor.len = 0; - refreshline(&term); - break; - - case KeyCtrlK: /* Ctrl+k, delete from current to end of line. */ - buf[term.edit.pos] = '\0'; - term.edit.len = term.edit.pos; - term.cursor.len = term.cursor.pos; - refreshline(&term); - break; - - case KeyCtrlA: /* Ctrl+a, go to the start of the line */ - move(&term, HOME(term)); - break; - - case KeyCtrlE: /* ctrl+e, go to the end of the line */ - move(&term, END(term)); - break; - - case KeyCtrlL: /* ctrl+term, clear screen */ - clear(); - refreshline(&term); - break; - - case KeyCtrlW: /* ctrl+w, delete previous word */ - delete(&term, prevword(&term,1)); - break; - } - } -finish: - efree(term.yank.buf); - return term.edit.len; -} - -/* - * this special mode is used by linenoise in order to print scan codes - * on screen for debugging / development purposes. It is implemented - * by the linenoise_example program using the --keycodes option. - */ -void -printkeycode(void) -{ - int n; - char c, quit[4]; - - printf("entering debugging mode. printing key codes.\n" - "press keys to see scan codes. type 'quit' at any time to exit.\n"); - - if(!enterraw(0)) - return; - - memset(quit,' ',4); - - for(;;){ - n = read(0,&c,1); - if(n <= 0) - continue; - memmove(quit,quit+1,sizeof(quit)-1); // shift string to left - quit[arrlen(quit)-1] = c; /* Insert current char on the right. */ - - if(memcmp(quit,"quit",sizeof(quit)) == 0) - break; - - printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); - printf("\r"); /* go to left edge manually, we are in raw mode. */ - fflush(stdout); - } - exitraw(0); -} - -/* - * this function calls the line editing function edit() using the stdin set in raw mode - */ -static -int -raw(char *buf, intptr len, char *prompt) -{ - int n; - - if(!len){ - errno = EINVAL; - return -1; - } - - // XXX: should we not hardcode stdin and stdout fd? - if(!enterraw(0)) return -1; - n = interact(0, 1, buf, len, prompt); - exitraw(0); - - return n; -} - -/* - * called when readline() is called with the standard - * input file descriptor not attached to a TTY. For example when the - * program is called in pipe or with a file redirected to its standard input - * in this case, we want to be able to return the line regardless of its length - */ -static -int -notty(void) -{ - int c; - - for(;;){ - c = fgetc(stdin); - put(&runner->cmd.io, c); - } -} - -void -enablevi(void) -{ - mode.vi.on = 1; - insertmode(1); -} - -/* - * The high level function that is the main API. - * This function checks if the terminal has basic capabilities and later - * either calls the line editing function or uses dummy fgets() so that - * you will be able to type something even in the most desperate of the - * conditions. - */ -int -readline(char *prompt) -{ - int n; - - // reset the command buffer - runner->cmd.io->e = runner->cmd.io->b = runner->cmd.io->buf; - - if(!shell.interactive) - return notty(); - - if((n = raw(runner->cmd.io->e, runner->cmd.io->cap-1, prompt)) == -1) - return 0; - runner->cmd.io->e += n; - - /* insert a newline character at the end */ - put(&runner->cmd.io, '\n'); - - return 1; -} - -/* At exit we'll try to fix the terminal to the initial conditions. */ -static -void -doatexit(void) -{ - exitraw(0); - normalcursor(1); -} diff --git a/sys/cmd/rc/io.c b/sys/cmd/rc/io.c deleted file mode 100644 index dc81c2e..0000000 --- a/sys/cmd/rc/io.c +++ /dev/null @@ -1,437 +0,0 @@ -#include "rc.h" -#include "parse.h" - -#define CAP0 512 - -Io* -openfd(int fd) -{ - Io *io = emalloc(sizeof(*io) + CAP0); - - io->fd = fd; - io->cap = CAP0; - io->b = io->e = io->buf; - io->s = nil; - - return io; -} - -Io* -openstr(void) -{ - char *s; - Io *io = emalloc(sizeof(*io) + CAP0); - - io->fd = -1; - io->cap = CAP0; - io->b = io->s = emalloc(101); - io->e = io->b+100; - - for(s = io->b; s<=io->e; s++) - *s=0; - - return io; -} - -#if 0 -/* - * open a corebuffer to read. EOF occurs after reading len characters from buf - */ - -Io* -opencore(char *s, int len) -{ - Io *io = emalloc(sizeof(*io)); - char *buf = emalloc(len); - io->fd = -1 /*open("/dev/null", 0)*/; - io->b = io->s = buf; - io->e = buf+len; - memcpy(buf, s, len); - - return io; -} -#endif - -void -iorewind(Io *io) -{ - if(io->fd==-1) - io->b = io->s; - else{ - io->b = io->e = io->buf; - lseek(io->fd, 0L, 0); - } -} - -void -terminate(Io *io) -{ - if(io->fd>=0) - close(io->fd); - if(io->s) - efree(io->s); - - efree((char *)io); -} - -static -int -refill(Io *io) -{ - int n; - - if(io->fd==-1 || (n = read(io->fd, io->buf, io->cap))<=0) - return EOF; - - io->b = io->buf; - io->e = io->buf+n; - - return *io->b++&0xff; -} - - -void -flush(Io *io) -{ - int n; - char *s; - - if(io->s){ - n = io->e-io->s; - io->s = realloc(io->s, n+101); - if(io->s==0) - panicf("Can't realloc %d bytes in flush!", n+101); - io->b = io->s+n; - io->e = io->b+100; - for(s = io->b;s<=io->e;s++) *s='\0'; - }else{ - n = io->b-io->buf; - if(n && write(io->fd, io->buf, n) < 0) - write(3, "write error\n", 12); - io->b = io->buf; - io->e = io->buf + io->cap; - } -} - - -static -void -printchar(Io *io, int c) -{ - if(io->b==io->e) - flush(io); - - *io->b++=c; -} - -void -printquote(Io *io, char *s) -{ - printchar(io, '\''); - for(;*s;s++) - if(*s=='\'') - print(io, "''"); - else printchar(io, *s); - printchar(io, '\''); -} - -void -printstr(Io *io, char *s) -{ - if(s==0) - s="(null)"; - while(*s) printchar(io, *s++); -} - -void -printword(Io *io, char *s) -{ - char *t; - - for(t = s;*t;t++) - if(!iswordchar(*t)) - break; - - if(t==s || *t) - printquote(io, s); - else - printstr(io, s); -} - -void -printptr(Io *io, void *v) -{ - int n; - uintptr p; - - p = (uintptr)v; - if(sizeof(uintptr) == sizeof(uvlong) && p>>32) - for(n = 60;n>=32;n-=4) printchar(io, "0123456789ABCDEF"[(p>>n)&0xF]); - - for(n = 28;n>=0;n-=4) printchar(io, "0123456789ABCDEF"[(p>>n)&0xF]); -} - -static -void -printint(Io *io, int n) -{ - if(n<0){ - if(n!=INT_MIN){ - printchar(io, '-'); - printint(io, -n); - return; - } - /* n is two's complement minimum integer */ - n = -(INT_MIN+1); - printchar(io, '-'); - printint(io, n/10); - printchar(io, n%10+'1'); - return; - } - if(n>9) - printint(io, n/10); - printchar(io, n%10+'0'); -} - -static -void -printoct(Io *io, unsigned n) -{ - if(n>7) - printoct(io, n>>3); - printchar(io, (n&7)+'0'); -} - -static -void -printval(Io *io, Word *a) -{ - if(a){ - while(a->link && a->link->str){ - printword(io, a->str); - printchar(io, ' '); - a = a->link; - } - printword(io, a->str); - } -} - -#define C0 t->child[0] -#define C1 t->child[1] -#define C2 t->child[2] - -static -void -printtree(Io *io, Tree *t) -{ - if(!t) - return; - - switch(t->type){ - default: print(io, "bad(%d)[%p %p %p]", t->type, C0, C1, C2); break; - case '$': print(io,"$%t",C0); break; - case '&': print(io,"%t&",C0); break; - case '^': print(io,"%t^%t",C0,C1); break; - case '`': print(io,"`%t",C0); break; - - case Tbasic: print(io, "%t", C0); break; - case Tbang: print(io, "!%t", C0); break; - case Tblock: print(io, "{%t}", C0); break; - case Tcount: print(io, "$#%t", C0); break; - case Tparen: print(io, "(%t)", C0); break; - case Tjoin: print(io,"$\"%t",C0); break; - case Tindex: print(io, "%t(%t)",C0); break; - case Tsubshell: print(io, "@ %t",C0); break; - //case Ttwiddle: print(io, "~ %t %t", C0, C1); break; - - case Toror: - case Tandand: - - case Targs: - if(!C0) - print(io, "%t", C1); - else if(!C1) - print(io, "%t", C0); - else - print(io, "%t %t", C0, C1); - break; - - case ';': - if(C0){ - if(C1) - print(io, "%t;%t", C0, C1); - else - print(io, "%t", C0); - }else - print(io, "%t", C1); - break; - - case Twords: - if(C0) - print(io, "%t", C0); - print(io, "%t", C1); - - case Tword: - if(t->quoted) - print(io, "%Q", t->str); - print(io, "%q", t->str); - break; - - case '=': - print(io, "%t=%t", C0, C1); - if(C2) - print(io, " %t", C2); - break; - - case Tdup: - if(t->redir.type == Rdupfd) - print(io, ">[%d=%d]", t->redir.fd[1], t->redir.fd[0]); - else - print(io, ">[%d=]", t->redir.fd[0]); - print(io, "%t", C1); - break; - - case Tredir: - switch(t->redir.type){ - case Rhere: - printchar(io, '<'); - case Rread: - printchar(io, '<'); - goto readfd; - case Rrdwr: - printchar(io, '<'); - printchar(io, '>'); - readfd: - if(t->redir.fd[0]!=0) - print(io, "[%d]", t->redir.fd[0]); - break; - case Rappend: - printchar(io, '>'); - goto writefd; - case Rwrite: - printchar(io, '>'); - printchar(io, '>'); - writefd: - if(t->redir.fd[0]!=1) - print(io, "[%d]", t->redir.fd[0]); - break; - } - print(io, "%t", C0); - if(C1) - print(io, " %t", C1); - break; - - case Tpipe: - print(io, "%t|", C0); - if(t->redir.fd[1]==0){ - if(t->redir.fd[0]!=1) - print(io, "[%d]", t->redir.fd[0]); - } - else - print(io, "[%d=%d]", t->redir.fd[0], t->redir.fd[1]); - print(io, "%t", C1); - break; - } -} - -#undef C0 -#undef C1 -#undef C2 - -// ----------------------------------------------------------------------- -// exports - -/* readers */ -int -get(Io *io) -{ - if(io->b==io->e) - return refill(io); - - return *io->b++ & 0xFF; -} - -/* writers */ -int -put(Io **iop, char c) -{ - int nb, ne, nc; - Io *io = *iop; - char *e = io->b + io->cap; - - if(io->e == e){ - nb = io->b - io->buf; - ne = io->e - io->buf; - nc = 2*io->cap; - - if(!(io = erealloc(io, sizeof(*io)+nc))) - return 0; - - io->b = io->buf + nb; - io->e = io->buf + ne; - io->cap = nc; - - *iop = io; - } - - *io->e++ = c; - return 1; -} - -/* printers */ -static int pfmtnest; - -void -print(Io *io, char *fmt, ...) -{ - va_list args; - char err[ERRMAX]; - - va_start(args, fmt); - pfmtnest++; - - for(;*fmt;fmt++) - if(*fmt!='%') - printchar(io, *fmt); - else - switch(*++fmt){ - case '\0': - va_end(args); - return; - case 'c': - printchar(io, va_arg(args, int)); - break; - case 'd': - printint(io, va_arg(args, int)); - break; - case 'o': - printoct(io, va_arg(args, unsigned)); - break; - case 'p': - printptr(io, va_arg(args, void*)); - break; - case 'Q': - printquote(io, va_arg(args, char *)); - break; - case 'q': - printword(io, va_arg(args, char *)); - break; - case 's': - printstr(io, va_arg(args, char *)); - break; - case 't': - printtree(io, va_arg(args, struct Tree *)); - break; - case 'v': - printval(io, va_arg(args, struct Word *)); - break; - default: - printchar(io, *fmt); - break; - } - - va_end(args); - - if(--pfmtnest==0) - flush(io); -} diff --git a/sys/cmd/rc/job.c b/sys/cmd/rc/job.c deleted file mode 100644 index 1587951..0000000 --- a/sys/cmd/rc/job.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "rc.h" - -#include -#include - -// ----------------------------------------------------------------------- -// exports - -Thread * -getjob(int pid, int *index) -{ - int i; - Thread *job; - for(i=0,job=shell.jobs; job && job->pid != pid; i++, job=job->link) - ; - - return job; -} - -void -report(Thread *job, int index) -{ - switch(job->wait.status){ - case Pdone: - print(shell.err, "job %d [%d]: done\n", index, job->pid); - break; - case Pstop: - print(shell.err, "job %d [%d]: suspended\n", index, job->pid); - break; - case Pagain: - print(shell.err, "job %d [%d]: continued\n", index, job->pid); - break; - case Prun: - print(shell.err, "job %d [%d]: running\n", index, job->pid); - break; - default: - fatal("bad wait status: %d\n", job->wait.status); - } -} - -void -wakeup(Thread *job) -{ - int i; - job->wait.status = Prun; - for(i=0; i < job->wait.len; i++){ - if(job->wait.on[i].status == Pstop) - job->wait.on[i].status = Prun; - } - - tcsetpgrp(0, job->pgid); -} - -void -foreground(Thread *job, int now) -{ - Thread *caller = job->caller; - if(now){ - if(kill(-job->pgid, SIGCONT) < 0) - perror("kill[SIGCONT]"); - } - - waitall(job); - /* - * reset state if we have a caller - * otherwise we will exit anyways - */ - if(caller && caller->flag.user){ - tcsetpgrp(0, caller->pid); - job->flag.user = 1; - } -} - -void -addjob(Thread *job) -{ - job->link = shell.jobs; - shell.jobs = job; - job->wait.status = Prun; -} - -void -deljob(Thread *job) -{ - Thread **jp; - - for(jp = &shell.jobs; *jp && *jp != job; jp = &(*jp)->link) - ; - - *jp = job->link; -} diff --git a/sys/cmd/rc/lex.c b/sys/cmd/rc/lex.c deleted file mode 100644 index 9ca2453..0000000 --- a/sys/cmd/rc/lex.c +++ /dev/null @@ -1,394 +0,0 @@ -#include "rc.h" -#include "parse.h" - -static int advance(void); - -// ----------------------------------------------------------------------- -// lexer - -struct Lexer -{ - int c[2]; - ushort doprompt; - ushort hadword; - ushort haddollar; - ushort inquote; - char buf[BUFSIZ]; -}; - -static struct Lexer lexer = { .c={0, EOF}, .doprompt=1 }; - -#define put1(b) lexer.buf[0] = (b), lexer.buf[1] = 0; -#define put2(b0,b1) lexer.buf[0] = (b0), lexer.buf[1] = (b1), lexer.buf[2] = 0; -#define put3(b0,b1,b2) lexer.buf[0] = (b0), lexer.buf[1] = (b1), lexer.buf[2] = b2, lexer.buf[3] = 0; - -void -yyerror(const char *msg) -{ - print(shell.err, "rc:%d: ", runner->line); - - if(lexer.buf[0] && lexer.buf[0]!='\n') - print(shell.err, "%q: ", lexer.buf); - - print(shell.err, "%s\n", msg); - flush(shell.err); - - lexer.hadword = 0; - lexer.haddollar = 0; - - /* consume remaining tokens */ - while(lexer.c[0] !='\n' && lexer.c[0] != EOF) - advance(); -} - -int -readc(void) -{ - int c; - static int peek = EOF; - - if(peek!=EOF){ - c = peek; - peek = EOF; - return c; - } - - if(runner->flag.eof) - return EOF; - - if(!prompt(&lexer.doprompt)) - exit(1); // XXX: hack for signal handling right now... - - c = get(runner->cmd.io); - lexer.doprompt = lexer.doprompt || c=='\n' || c==EOF; - - if(c==EOF) - runner->flag.eof = 1; - - return c; -} - -static -int -peekc(void) -{ - if(lexer.c[1] == EOF) - lexer.c[1] = readc(); - - return lexer.c[1]; -} - -static -int -advance(void) -{ - int c = peekc(); - lexer.c[0] = lexer.c[1], lexer.c[1] = EOF; - - return c; -} - -static -void -skipws(void) -{ - int c; - for(;;){ - c = peekc(); - if(c== ' ' || c == '\t') - advance(); - else - return; - } -} - -static -void -skipnl(void) -{ - int c; - for(;;){ - c = peekc(); - if(c== ' ' || c == '\t' || c == '\n') - advance(); - else - return; - } -} - -static -int -nextis(int c) -{ - if(peekc()==c){ - advance(); - return 1; - } - return 0; -} - -static -char * -putbyte(char *buf, int c) -{ - if(!buf) - return buf; - - if(buf == arrend(lexer.buf)){ - fatal("lexer: out of buffer space"); - return nil; - } - *buf++ = c; - return buf; -} - -static -char * -putrune(char *buf, int c) -{ - buf = putbyte(buf, c); - if(utf8·onebyte(c)) - return buf; - if(utf8·twobyte(c)) - return putbyte(buf,advance()); - if(utf8·threebyte(c)){ - buf = putbyte(buf,advance()); - return putbyte(buf,advance()); - } - if(utf8·fourbyte(c)){ - buf = putbyte(buf,advance()); - buf = putbyte(buf,advance()); - return putbyte(buf,advance()); - } - fatal("malformed utf8 stream"); - - return nil; -} - -// ----------------------------------------------------------------------- -// exported functions - -// TODO: turn into static tables -int -iswordchar(int c) -{ - return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; -} - -int -isidentchar(int c) -{ - return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); -} - -int -yylex(void) -{ - int c, d = peekc(); - Tree *node; - char *w = lexer.buf; - - yylval.tree = nil; - - /* inject tokens */ - if(lexer.hadword){ - lexer.hadword = 0; - if(d=='('){ - advance(); - strcpy(lexer.buf, "( [Tindex]"); - return Tindex; - } - if(iswordchar(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ - strcpy(lexer.buf, "^"); - return '^'; - } - } - - lexer.inquote = 0; - - skipws(); - switch(c=advance()){ - case EOF: - lexer.haddollar = 0; - put3('E','O','F'); - return EOF; - - case '$': - lexer.haddollar = 1; - if(nextis('#')){ - put2('$','#'); - return Tcount; - } - if(nextis('^')){ - put2('$','^'); - return Tjoin; - } - put1('$'); - return '$'; - - case '@': - lexer.haddollar = 0; - put1('@'); - return Tsubshell; - - case '!': - lexer.haddollar = 0; - put1('!'); - return Tbang; - - case '&': - lexer.haddollar = 0; - if(nextis('&')){ - put2('&','&'); - return Tandand; - } - put1('&'); - return '&'; - - case '|': - lexer.haddollar = 0; - if(nextis('|')){ - put2('|','|'); - return Toror; - } - node = maketree(); - *w++ = '|'; - - node->type = Tpipe; - node->redir.fd[0] = 1; - node->redir.fd[1] = 0; - goto redir; - - case '>': - lexer.haddollar = 0; - node = maketree(); - *w++ = '>'; - node->type = Tredir; - - if(nextis('>')){ - node->redir.type = Rappend; - *w++ = '>'; - }else - node->redir.type = Rwrite; - node->redir.fd[0] = 1; - goto redir; - - case '<': - lexer.haddollar = 0; - node = maketree(); - *w++ = '<'; - node->type = Tredir; - - if(nextis('<')){ - node->redir.type = Rhere; - *w++ = '<'; - }else if(nextis('>')){ - node->redir.type = Rrdwr; - *w++ = '>'; - }else{ - node->redir.type = Rread; - } - node->redir.fd[0] = 0; - /* fallthrough */ - redir: - if(nextis('[')){ - *w++='['; - c = advance(); - *w++ = c; - if(c < '0' || '9' < c){ - badredir: - *w = 0; - yyerror(node->type == Tpipe ? "pipe syntax" : "redirection syntax"); - return EOF; - } - node->redir.fd[0] = 0; - do{ - node->redir.fd[0] = 10*node->redir.fd[0]+(c-'0'); - *w++ = c; - c = advance(); - }while('0'<=c && c<='9'); - - if(c == '='){ - *w++ = '='; - if(node->type==Tredir) - node->type = Tdup; - c = advance(); - } - if(c < '0' || '9' < c){ - if(node->type == Tpipe) - goto badredir; - node->redir.type = Rclose; - }else{ - 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'); - *w++ = c; - c = advance(); - }while('0'<=c && c<='9'); - } - if(c != ']' || (node->type == Tdup && (node->redir.type = Rhere || node->redir.type == Rappend))) - goto badredir; - *w++ = ']'; - } - *w++ = 0; - yylval.tree = node; - - return node->type; - - case '\'': - lexer.hadword = 1; - lexer.inquote = 1; - lexer.haddollar = 0; - for(;;){ - c = advance(); - if(c==EOF) - break; - - if(c=='\''){ - if(peekc()!='\'') - break; - advance(); - } - w = putrune(w, c); - } - if(w) - *w = 0; - node = token(Tword, lexer.buf); - node->quoted = 1; - return node->type; - - default: - ; - } - if(!iswordchar(c)){ - put1(c); - lexer.haddollar = 0; - return c; - } - - for(;;){ - w = putrune(w, c); - c = peekc(); - if(lexer.haddollar ? !isidentchar(c) : !iswordchar(c)) - break; - advance(); - } - - lexer.hadword = 1; - lexer.haddollar = 0; - if(w) - *w = 0; - - node = token(Tword, lexer.buf); - if((c=iskeyword(lexer.buf))){ - node->type = c; - lexer.hadword = 0; - } - - node->quoted = 0; - - yylval.tree = node; - return node->type; -} diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c deleted file mode 100644 index 2c0aa42..0000000 --- a/sys/cmd/rc/main.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "rc.h" -#include "parse.h" -#include "exec.h" - -#include -#include - -// ----------------------------------------------------------------------- -// globals - -Thread *runner = nil; -Shell shell = { 0 }; - -// ----------------------------------------------------------------------- -// functions - -void -initshell(void) -{ - if((shell.interactive=isatty(0))){ - while(tcgetpgrp(0) != (shell.pid = getpgrp())) - kill(-shell.pid, SIGTTIN); - - /* ignore job control signals */ - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - signal(SIGTTIN, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - /* - * NOTE: if SIGCHLD is set to SIG_IGN then - * 1. children that terminate do not become zombies - * 2. call a to wait() will block until all children have terminated - * 3. the call to wait will fail with errno == ECHILD - * see for discussion: - * https://stackoverflow.com/questions/1608017/no-child-process-error-from-waitpid-when-waiting-for-process-group - */ - // signal(SIGCHLD, SIG_IGN); - - /* take control */ - shell.pid = getpid(); - if(setpgid(shell.pid, shell.pid)<0) - fatal("could not put shell in its own process group"); - - tcsetpgrp(shell.pid, shell.pid); - } -} - -// ----------------------------------------------------------------------- -// main point of entry - -int -main(int argc, char *argv[]) -{ - shell.err = openfd(2); - - initenv(); - initpath(); - initkeywords(); - initshell(); - inithistory(); - - enablevi(); - xboot(argc, argv); - /* unreachable */ -} diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c deleted file mode 100644 index 1b29d41..0000000 --- a/sys/cmd/rc/parse.c +++ /dev/null @@ -1,2059 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output, and Bison version. */ -#define YYBISON 30802 - -/* Bison version string. */ -#define YYBISON_VERSION "3.8.2" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - - - - -/* First part of user prologue. */ -#line 7 "sys/cmd/rc/syntax.y" - - #include "rc.h" - - int yylex(void); - void yyerror(const char *); - -#line 78 "sys/cmd/rc/parse.c" - -# ifndef YY_CAST -# ifdef __cplusplus -# define YY_CAST(Type, Val) static_cast (Val) -# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) -# else -# define YY_CAST(Type, Val) ((Type) (Val)) -# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) -# endif -# endif -# ifndef YY_NULLPTR -# if defined __cplusplus -# if 201103L <= __cplusplus -# define YY_NULLPTR nullptr -# else -# define YY_NULLPTR 0 -# endif -# else -# define YY_NULLPTR ((void*)0) -# endif -# endif - -#include "parse.h" -/* Symbol kind. */ -enum yysymbol_kind_t -{ - YYSYMBOL_YYEMPTY = -2, - YYSYMBOL_YYEOF = 0, /* "end of file" */ - YYSYMBOL_YYerror = 1, /* error */ - YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ - YYSYMBOL_Tfor = 3, /* Tfor */ - YYSYMBOL_Tin = 4, /* Tin */ - YYSYMBOL_Twhile = 5, /* Twhile */ - YYSYMBOL_Tif = 6, /* Tif */ - YYSYMBOL_Telse = 7, /* Telse */ - YYSYMBOL_Tswitch = 8, /* Tswitch */ - YYSYMBOL_Tcase = 9, /* Tcase */ - YYSYMBOL_Tcasebody = 10, /* Tcasebody */ - YYSYMBOL_Ttwiddle = 11, /* Ttwiddle */ - YYSYMBOL_Tbang = 12, /* Tbang */ - YYSYMBOL_Tsubshell = 13, /* Tsubshell */ - YYSYMBOL_Tfunc = 14, /* Tfunc */ - YYSYMBOL_Tredir = 15, /* Tredir */ - YYSYMBOL_Tdup = 16, /* Tdup */ - YYSYMBOL_Tpipe = 17, /* Tpipe */ - YYSYMBOL_Tindex = 18, /* Tindex */ - YYSYMBOL_Tbasic = 19, /* Tbasic */ - YYSYMBOL_Targs = 20, /* Targs */ - YYSYMBOL_Tword = 21, /* Tword */ - YYSYMBOL_Twords = 22, /* Twords */ - YYSYMBOL_Tparen = 23, /* Tparen */ - YYSYMBOL_Tblock = 24, /* Tblock */ - YYSYMBOL_25_ = 25, /* ')' */ - YYSYMBOL_Tandand = 26, /* Tandand */ - YYSYMBOL_Toror = 27, /* Toror */ - YYSYMBOL_28_n_ = 28, /* '\n' */ - YYSYMBOL_29_ = 29, /* '^' */ - YYSYMBOL_30_ = 30, /* '$' */ - YYSYMBOL_Tcount = 31, /* Tcount */ - YYSYMBOL_Tjoin = 32, /* Tjoin */ - YYSYMBOL_33_ = 33, /* '(' */ - YYSYMBOL_34_ = 34, /* '{' */ - YYSYMBOL_35_ = 35, /* '}' */ - YYSYMBOL_36_ = 36, /* ';' */ - YYSYMBOL_37_ = 37, /* '&' */ - YYSYMBOL_38_ = 38, /* '=' */ - YYSYMBOL_39_ = 39, /* '`' */ - YYSYMBOL_YYACCEPT = 40, /* $accept */ - YYSYMBOL_rc = 41, /* rc */ - YYSYMBOL_line = 42, /* line */ - YYSYMBOL_body = 43, /* body */ - YYSYMBOL_paren = 44, /* paren */ - YYSYMBOL_block = 45, /* block */ - YYSYMBOL_cmds = 46, /* cmds */ - YYSYMBOL_cmdsln = 47, /* cmdsln */ - YYSYMBOL_ifbody = 48, /* ifbody */ - YYSYMBOL_case = 49, /* case */ - YYSYMBOL_casebody = 50, /* casebody */ - YYSYMBOL_assign = 51, /* assign */ - YYSYMBOL_redir = 52, /* redir */ - YYSYMBOL_epilog = 53, /* epilog */ - YYSYMBOL_cmd = 54, /* cmd */ - YYSYMBOL_basic = 55, /* basic */ - YYSYMBOL_atom = 56, /* atom */ - YYSYMBOL_word = 57, /* word */ - YYSYMBOL_executable = 58, /* executable */ - YYSYMBOL_nonkeyword = 59, /* nonkeyword */ - YYSYMBOL_keyword = 60, /* keyword */ - YYSYMBOL_words = 61, /* words */ - YYSYMBOL_wordsnl = 62, /* wordsnl */ - YYSYMBOL_nl = 63 /* nl */ -}; -typedef enum yysymbol_kind_t yysymbol_kind_t; - - - - -#ifdef short -# undef short -#endif - -/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure - and (if available) are included - so that the code can choose integer types of a good width. */ - -#ifndef __PTRDIFF_MAX__ -# include /* INFRINGES ON USER NAME SPACE */ -# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_STDINT_H -# endif -#endif - -/* Narrow types that promote to a signed type and that can represent a - signed or unsigned integer of at least N bits. In tables they can - save space and decrease cache pressure. Promoting to a signed type - helps avoid bugs in integer arithmetic. */ - -#ifdef __INT_LEAST8_MAX__ -typedef __INT_LEAST8_TYPE__ yytype_int8; -#elif defined YY_STDINT_H -typedef int_least8_t yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef __INT_LEAST16_MAX__ -typedef __INT_LEAST16_TYPE__ yytype_int16; -#elif defined YY_STDINT_H -typedef int_least16_t yytype_int16; -#else -typedef short yytype_int16; -#endif - -/* Work around bug in HP-UX 11.23, which defines these macros - incorrectly for preprocessor constants. This workaround can likely - be removed in 2023, as HPE has promised support for HP-UX 11.23 - (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of - . */ -#ifdef __hpux -# undef UINT_LEAST8_MAX -# undef UINT_LEAST16_MAX -# define UINT_LEAST8_MAX 255 -# define UINT_LEAST16_MAX 65535 -#endif - -#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST8_TYPE__ yytype_uint8; -#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ - && UINT_LEAST8_MAX <= INT_MAX) -typedef uint_least8_t yytype_uint8; -#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX -typedef unsigned char yytype_uint8; -#else -typedef short yytype_uint8; -#endif - -#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST16_TYPE__ yytype_uint16; -#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ - && UINT_LEAST16_MAX <= INT_MAX) -typedef uint_least16_t yytype_uint16; -#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX -typedef unsigned short yytype_uint16; -#else -typedef int yytype_uint16; -#endif - -#ifndef YYPTRDIFF_T -# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ -# define YYPTRDIFF_T __PTRDIFF_TYPE__ -# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ -# elif defined PTRDIFF_MAX -# ifndef ptrdiff_t -# include /* INFRINGES ON USER NAME SPACE */ -# endif -# define YYPTRDIFF_T ptrdiff_t -# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX -# else -# define YYPTRDIFF_T long -# define YYPTRDIFF_MAXIMUM LONG_MAX -# endif -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned -# endif -#endif - -#define YYSIZE_MAXIMUM \ - YY_CAST (YYPTRDIFF_T, \ - (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ - ? YYPTRDIFF_MAXIMUM \ - : YY_CAST (YYSIZE_T, -1))) - -#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) - - -/* Stored state numbers (used for stacks). */ -typedef yytype_uint8 yy_state_t; - -/* State numbers in computations. */ -typedef int yy_state_fast_t; - -#ifndef YY_ -# if defined YYENABLE_NLS && YYENABLE_NLS -# if ENABLE_NLS -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_(Msgid) dgettext ("bison-runtime", Msgid) -# endif -# endif -# ifndef YY_ -# define YY_(Msgid) Msgid -# endif -#endif - - -#ifndef YY_ATTRIBUTE_PURE -# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) -# else -# define YY_ATTRIBUTE_PURE -# endif -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) -# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -# else -# define YY_ATTRIBUTE_UNUSED -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YY_USE(E) ((void) (E)) -#else -# define YY_USE(E) /* empty */ -#endif - -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ -# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") -# else -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ - _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -# endif -# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ - _Pragma ("GCC diagnostic pop") -#else -# define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -# define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - -#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ -# define YY_IGNORE_USELESS_CAST_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") -# define YY_IGNORE_USELESS_CAST_END \ - _Pragma ("GCC diagnostic pop") -#endif -#ifndef YY_IGNORE_USELESS_CAST_BEGIN -# define YY_IGNORE_USELESS_CAST_BEGIN -# define YY_IGNORE_USELESS_CAST_END -#endif - - -#define YY_ASSERT(E) ((void) (0 && (E))) - -#if 1 - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS -# include /* INFRINGES ON USER NAME SPACE */ - /* Use EXIT_SUCCESS as a witness for stdlib.h. */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's 'empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* 1 */ - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yy_state_t yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYPTRDIFF_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / YYSIZEOF (*yyptr); \ - } \ - while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(Dst, Src, Count) \ - __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) -# else -# define YYCOPY(Dst, Src, Count) \ - do \ - { \ - YYPTRDIFF_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } \ - while (0) -# endif -# endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 56 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 478 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 40 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 24 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 73 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 129 - -/* YYMAXUTOK -- Last valid token kind. */ -#define YYMAXUTOK 283 - - -/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, with out-of-bounds checking. */ -#define YYTRANSLATE(YYX) \ - (0 <= (YYX) && (YYX) <= YYMAXUTOK \ - ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ - : YYSYMBOL_YYUNDEF) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex. */ -static const yytype_int8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 30, 2, 37, 2, - 33, 25, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, - 2, 38, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 29, 2, 39, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 34, 2, 35, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 26, 27, 31, 32 -}; - -#if YYDEBUG -/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint8 yyrline[] = -{ - 0, 38, 38, 39, 42, 43, 46, 47, 50, 53, - 56, 57, 60, 61, 64, 65, 68, 69, 72, 73, - 74, 77, 80, 81, 84, 85, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - 102, 105, 106, 107, 110, 111, 114, 115, 118, 119, - 122, 123, 124, 125, 126, 127, 128, 132, 132, 132, - 132, 132, 132, 132, 132, 132, 132, 135, 136, 139, - 140, 141, 143, 145 -}; -#endif - -/** Accessing symbol of state STATE. */ -#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) - -#if 1 -/* The user-facing name of the symbol whose (internal) number is - YYSYMBOL. No bounds checking. */ -static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; - -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "\"end of file\"", "error", "\"invalid token\"", "Tfor", "Tin", - "Twhile", "Tif", "Telse", "Tswitch", "Tcase", "Tcasebody", "Ttwiddle", - "Tbang", "Tsubshell", "Tfunc", "Tredir", "Tdup", "Tpipe", "Tindex", - "Tbasic", "Targs", "Tword", "Twords", "Tparen", "Tblock", "')'", - "Tandand", "Toror", "'\\n'", "'^'", "'$'", "Tcount", "Tjoin", "'('", - "'{'", "'}'", "';'", "'&'", "'='", "'`'", "$accept", "rc", "line", - "body", "paren", "block", "cmds", "cmdsln", "ifbody", "case", "casebody", - "assign", "redir", "epilog", "cmd", "basic", "atom", "word", - "executable", "nonkeyword", "keyword", "words", "wordsnl", "nl", YY_NULLPTR -}; - -static const char * -yysymbol_name (yysymbol_kind_t yysymbol) -{ - return yytname[yysymbol]; -} -#endif - -#define YYPACT_NINF (-82) - -#define yypact_value_is_default(Yyn) \ - ((Yyn) == YYPACT_NINF) - -#define YYTABLE_NINF (-3) - -#define yytable_value_is_error(Yyn) \ - 0 - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int16 yypact[] = -{ - 121, -17, -2, -2, 5, 439, 439, 343, -82, -82, - 343, 343, 343, -82, 439, -23, 45, 32, 11, 439, - 439, 439, 13, 158, -14, -82, 343, 439, -82, -82, - 343, 30, 30, -82, -82, -82, -82, -82, -82, -82, - -82, -82, -82, -82, 34, -82, -82, 47, -82, -82, - 195, 41, -82, 439, 54, -82, -82, -82, 11, -82, - -82, 30, 30, -82, -82, -82, -82, -82, -82, 34, - 343, 343, 19, 44, 375, 375, 4, 343, -82, -82, - -82, 34, -82, -82, -82, -82, 375, 375, 375, -82, - 34, -82, -82, -82, -82, 29, 77, -82, 29, -82, - -82, 269, -82, 30, 30, 306, 375, -82, 25, -82, - 34, -82, 29, 375, 407, 375, 29, -82, 407, 407, - 48, 54, 29, 232, -82, -82, -82, -82, -82 -}; - -/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_int8 yydefact[] = -{ - 26, 0, 0, 0, 0, 26, 26, 0, 22, 50, - 0, 0, 0, 69, 26, 0, 0, 0, 24, 26, - 26, 26, 4, 27, 41, 48, 0, 26, 72, 72, - 0, 34, 35, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 46, 23, 44, 45, 51, 54, 55, - 0, 0, 12, 26, 6, 56, 1, 3, 24, 28, - 5, 33, 32, 72, 72, 72, 10, 11, 43, 42, - 0, 0, 0, 0, 26, 26, 0, 0, 67, 53, - 70, 71, 9, 7, 13, 25, 26, 26, 26, 49, - 21, 67, 72, 8, 73, 38, 24, 39, 14, 72, - 47, 0, 29, 30, 31, 0, 26, 72, 0, 52, - 68, 72, 36, 26, 26, 26, 15, 67, 26, 26, - 0, 18, 37, 0, 20, 19, 40, 17, 16 -}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int8 yypgoto[] = -{ - -82, -82, 75, -19, 93, -11, 18, -52, -82, -82, - -21, -82, -1, 42, 0, -82, -9, 28, -82, 2, - -82, -81, -82, -22 -}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = -{ - 0, 16, 17, 51, 28, 18, 52, 53, 97, 119, - 120, 20, 21, 59, 54, 23, 43, 110, 24, 25, - 46, 101, 50, 74 -}; - -/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_int16 yytable[] = -{ - 22, 47, 48, 49, 55, 31, 32, 75, 73, 45, - 105, 14, 45, 45, 45, 70, 26, 58, 19, 22, - 61, 62, 68, 91, 71, 45, 7, 8, 45, 99, - 63, 27, 45, 77, 83, 44, 123, 19, 30, 64, - 65, 86, 87, 88, 92, 56, 63, 63, 77, 66, - 67, 69, 45, 94, 72, 64, 65, 58, 76, 114, - 57, 89, 118, 77, 96, 78, 118, 118, 100, 93, - 106, 63, 45, 45, 95, 98, 82, 108, 81, 45, - 64, 65, 84, 126, 107, 113, 102, 103, 104, 115, - 66, 67, 7, 8, 60, 58, 29, 124, 125, 90, - 85, 0, 0, 45, 0, 0, 112, 45, 0, 0, - 0, 0, 0, 116, 121, 122, 0, 0, 121, 121, - 0, -2, 0, 0, 1, 45, 2, 3, 0, 4, - 0, 0, 0, 5, 6, 0, 7, 8, 0, 0, - 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, - 0, 10, 11, 12, 13, 14, 0, 0, 0, 0, - 15, 33, 34, 35, 36, 37, 38, 39, 0, 0, - 40, 41, 42, 7, 8, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, - 12, 13, 0, 0, 0, 0, 0, 15, 33, 34, - 35, 36, 37, 38, 39, 0, 0, 40, 41, 42, - 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 79, 0, 0, 80, 0, 10, 11, 12, 13, 0, - 0, 0, 0, 0, 15, 33, 34, 35, 36, 37, - 38, 39, 0, 0, 40, 41, 42, 0, 0, 0, - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, - 127, 0, 10, 11, 12, 13, 0, 0, 128, 0, - 0, 15, 33, 34, 35, 36, 37, 38, 39, 0, - 0, 40, 41, 42, 0, 0, 0, 0, 0, 0, - 9, 0, 0, 0, 109, 0, 0, 0, 0, 10, - 11, 12, 13, 0, 0, 0, 0, 0, 15, 33, - 34, 35, 36, 37, 38, 39, 0, 0, 40, 41, - 42, 0, 0, 0, 0, 0, 0, 9, 0, 0, - 0, 111, 0, 0, 0, 0, 10, 11, 12, 13, - 0, 0, 0, 0, 0, 15, 33, 34, 35, 36, - 37, 38, 39, 0, 0, 40, 41, 42, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 10, 11, 12, 13, 0, 1, 0, - 2, 3, 15, 4, 0, 0, 0, 5, 6, 0, - 7, 8, 0, 0, 0, 0, 9, 0, 0, 0, - 0, 0, 0, 94, 0, 10, 11, 12, 13, 14, - 1, 0, 2, 3, 15, 4, 117, 0, 0, 5, - 6, 0, 7, 8, 0, 0, 0, 0, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, - 13, 14, 1, 0, 2, 3, 15, 4, 0, 0, - 0, 5, 6, 0, 7, 8, 0, 0, 0, 0, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 10, - 11, 12, 13, 14, 0, 0, 0, 0, 15 -}; - -static const yytype_int8 yycheck[] = -{ - 0, 10, 11, 12, 15, 5, 6, 29, 27, 7, - 91, 34, 10, 11, 12, 29, 33, 18, 0, 19, - 20, 21, 23, 4, 38, 23, 15, 16, 26, 25, - 17, 33, 30, 29, 53, 7, 117, 19, 33, 26, - 27, 63, 64, 65, 25, 0, 17, 17, 29, 36, - 37, 23, 50, 28, 26, 26, 27, 58, 30, 34, - 28, 70, 114, 29, 75, 18, 118, 119, 77, 25, - 92, 17, 70, 71, 74, 75, 35, 99, 50, 77, - 26, 27, 28, 35, 7, 107, 86, 87, 88, 111, - 36, 37, 15, 16, 19, 96, 3, 118, 119, 71, - 58, -1, -1, 101, -1, -1, 106, 105, -1, -1, - -1, -1, -1, 113, 114, 115, -1, -1, 118, 119, - -1, 0, -1, -1, 3, 123, 5, 6, -1, 8, - -1, -1, -1, 12, 13, -1, 15, 16, -1, -1, - -1, -1, 21, -1, -1, -1, -1, -1, -1, -1, - -1, 30, 31, 32, 33, 34, -1, -1, -1, -1, - 39, 3, 4, 5, 6, 7, 8, 9, -1, -1, - 12, 13, 14, 15, 16, -1, -1, -1, -1, 21, - -1, -1, -1, -1, -1, -1, -1, -1, 30, 31, - 32, 33, -1, -1, -1, -1, -1, 39, 3, 4, - 5, 6, 7, 8, 9, -1, -1, 12, 13, 14, - -1, -1, -1, -1, -1, -1, 21, -1, -1, -1, - 25, -1, -1, 28, -1, 30, 31, 32, 33, -1, - -1, -1, -1, -1, 39, 3, 4, 5, 6, 7, - 8, 9, -1, -1, 12, 13, 14, -1, -1, -1, - -1, -1, -1, 21, -1, -1, -1, -1, -1, -1, - 28, -1, 30, 31, 32, 33, -1, -1, 36, -1, - -1, 39, 3, 4, 5, 6, 7, 8, 9, -1, - -1, 12, 13, 14, -1, -1, -1, -1, -1, -1, - 21, -1, -1, -1, 25, -1, -1, -1, -1, 30, - 31, 32, 33, -1, -1, -1, -1, -1, 39, 3, - 4, 5, 6, 7, 8, 9, -1, -1, 12, 13, - 14, -1, -1, -1, -1, -1, -1, 21, -1, -1, - -1, 25, -1, -1, -1, -1, 30, 31, 32, 33, - -1, -1, -1, -1, -1, 39, 3, 4, 5, 6, - 7, 8, 9, -1, -1, 12, 13, 14, -1, -1, - -1, -1, -1, -1, 21, -1, -1, -1, -1, -1, - -1, -1, -1, 30, 31, 32, 33, -1, 3, -1, - 5, 6, 39, 8, -1, -1, -1, 12, 13, -1, - 15, 16, -1, -1, -1, -1, 21, -1, -1, -1, - -1, -1, -1, 28, -1, 30, 31, 32, 33, 34, - 3, -1, 5, 6, 39, 8, 9, -1, -1, 12, - 13, -1, 15, 16, -1, -1, -1, -1, 21, -1, - -1, -1, -1, -1, -1, -1, -1, 30, 31, 32, - 33, 34, 3, -1, 5, 6, 39, 8, -1, -1, - -1, 12, 13, -1, 15, 16, -1, -1, -1, -1, - 21, -1, -1, -1, -1, -1, -1, -1, -1, 30, - 31, 32, 33, 34, -1, -1, -1, -1, 39 -}; - -/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of - state STATE-NUM. */ -static const yytype_int8 yystos[] = -{ - 0, 3, 5, 6, 8, 12, 13, 15, 16, 21, - 30, 31, 32, 33, 34, 39, 41, 42, 45, 46, - 51, 52, 54, 55, 58, 59, 33, 33, 44, 44, - 33, 54, 54, 3, 4, 5, 6, 7, 8, 9, - 12, 13, 14, 56, 57, 59, 60, 56, 56, 56, - 62, 43, 46, 47, 54, 45, 0, 28, 52, 53, - 42, 54, 54, 17, 26, 27, 36, 37, 52, 57, - 29, 38, 57, 43, 63, 63, 57, 29, 18, 25, - 28, 57, 35, 43, 28, 53, 63, 63, 63, 56, - 57, 4, 25, 25, 28, 54, 45, 48, 54, 25, - 56, 61, 54, 54, 54, 61, 63, 7, 63, 25, - 57, 25, 54, 63, 34, 63, 54, 9, 47, 49, - 50, 54, 54, 61, 50, 50, 35, 28, 36 -}; - -/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr1[] = -{ - 0, 40, 41, 41, 42, 42, 43, 43, 44, 45, - 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, - 50, 51, 52, 52, 53, 53, 54, 54, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 55, 55, 55, 56, 56, 57, 57, 58, 58, - 59, 59, 59, 59, 59, 59, 59, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, - 62, 62, 63, 63 -}; - -/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr2[] = -{ - 0, 2, 0, 2, 1, 2, 1, 2, 3, 3, - 2, 2, 1, 2, 1, 4, 3, 3, 1, 2, - 2, 3, 1, 2, 0, 2, 0, 1, 2, 4, - 4, 4, 2, 2, 2, 2, 6, 8, 4, 4, - 8, 1, 2, 2, 1, 1, 1, 3, 1, 3, - 1, 2, 5, 3, 2, 2, 2, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 2, 0, - 2, 2, 0, 2 -}; - - -enum { YYENOMEM = -2 }; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab -#define YYNOMEM goto yyexhaustedlab - - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ - do \ - if (yychar == YYEMPTY) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ - while (0) - -/* Backward compatibility with an undocumented macro. - Use YYerror or YYUNDEF. */ -#define YYERRCODE YYUNDEF - - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - - - - -# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Kind, Value); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - - -/*-----------------------------------. -| Print this symbol's value on YYO. | -`-----------------------------------*/ - -static void -yy_symbol_value_print (FILE *yyo, - yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) -{ - FILE *yyoutput = yyo; - YY_USE (yyoutput); - if (!yyvaluep) - return; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE (yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - -/*---------------------------. -| Print this symbol on YYO. | -`---------------------------*/ - -static void -yy_symbol_print (FILE *yyo, - yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) -{ - YYFPRINTF (yyo, "%s %s (", - yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); - - yy_symbol_value_print (yyo, yykind, yyvaluep); - YYFPRINTF (yyo, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void -yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) -{ - YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void -yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, - int yyrule) -{ - int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, - YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), - &yyvsp[(yyi + 1) - (yynrhs)]); - YYFPRINTF (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyssp, yyvsp, Rule); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) ((void) 0) -# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - -/* Context of a parse error. */ -typedef struct -{ - yy_state_t *yyssp; - yysymbol_kind_t yytoken; -} yypcontext_t; - -/* Put in YYARG at most YYARGN of the expected tokens given the - current YYCTX, and return the number of tokens stored in YYARG. If - YYARG is null, return the number of expected tokens (guaranteed to - be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. - Return 0 if there are more than YYARGN expected tokens, yet fill - YYARG up to YYARGN. */ -static int -yypcontext_expected_tokens (const yypcontext_t *yyctx, - yysymbol_kind_t yyarg[], int yyargn) -{ - /* Actual size of YYARG. */ - int yycount = 0; - int yyn = yypact[+*yyctx->yyssp]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (!yyarg) - ++yycount; - else if (yycount == yyargn) - return 0; - else - yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); - } - } - if (yyarg && yycount == 0 && 0 < yyargn) - yyarg[0] = YYSYMBOL_YYEMPTY; - return yycount; -} - - - - -#ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) -# else -/* Return the length of YYSTR. */ -static YYPTRDIFF_T -yystrlen (const char *yystr) -{ - YYPTRDIFF_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; -} -# endif -#endif - -#ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -yystpcpy (char *yydest, const char *yysrc) -{ - char *yyd = yydest; - const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -#endif - -#ifndef yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYPTRDIFF_T -yytnamerr (char *yyres, const char *yystr) -{ - if (*yystr == '"') - { - YYPTRDIFF_T yyn = 0; - char const *yyp = yystr; - for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - else - goto append; - - append: - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; - - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } - do_not_strip_quotes: ; - } - - if (yyres) - return yystpcpy (yyres, yystr) - yyres; - else - return yystrlen (yystr); -} -#endif - - -static int -yy_syntax_error_arguments (const yypcontext_t *yyctx, - yysymbol_kind_t yyarg[], int yyargn) -{ - /* Actual size of YYARG. */ - int yycount = 0; - /* There are many possibilities here to consider: - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yyctx->yytoken != YYSYMBOL_YYEMPTY) - { - int yyn; - if (yyarg) - yyarg[yycount] = yyctx->yytoken; - ++yycount; - yyn = yypcontext_expected_tokens (yyctx, - yyarg ? yyarg + 1 : yyarg, yyargn - 1); - if (yyn == YYENOMEM) - return YYENOMEM; - else - yycount += yyn; - } - return yycount; -} - -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, - const yypcontext_t *yyctx) -{ - enum { YYARGS_MAX = 5 }; - /* Internationalized format string. */ - const char *yyformat = YY_NULLPTR; - /* Arguments of yyformat: reported tokens (one for the "unexpected", - one per "expected"). */ - yysymbol_kind_t yyarg[YYARGS_MAX]; - /* Cumulated lengths of YYARG. */ - YYPTRDIFF_T yysize = 0; - - /* Actual size of YYARG. */ - int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); - if (yycount == YYENOMEM) - return YYENOMEM; - - switch (yycount) - { -#define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - default: /* Avoid compiler warnings. */ - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -#undef YYCASE_ - } - - /* Compute error message size. Don't count the "%s"s, but reserve - room for the terminator. */ - yysize = yystrlen (yyformat) - 2 * yycount + 1; - { - int yyi; - for (yyi = 0; yyi < yycount; ++yyi) - { - YYPTRDIFF_T yysize1 - = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); - if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) - yysize = yysize1; - else - return YYENOMEM; - } - } - - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return -1; - } - - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); - yyformat += 2; - } - else - { - ++yyp; - ++yyformat; - } - } - return 0; -} - - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void -yydestruct (const char *yymsg, - yysymbol_kind_t yykind, YYSTYPE *yyvaluep) -{ - YY_USE (yyvaluep); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE (yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - -/* Lookahead token kind. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; -/* Number of syntax errors so far. */ -int yynerrs; - - - - -/*----------. -| yyparse. | -`----------*/ - -int -yyparse (void) -{ - yy_state_fast_t yystate = 0; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus = 0; - - /* Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* Their size. */ - YYPTRDIFF_T yystacksize = YYINITDEPTH; - - /* The state stack: array, bottom, top. */ - yy_state_t yyssa[YYINITDEPTH]; - yy_state_t *yyss = yyssa; - yy_state_t *yyssp = yyss; - - /* The semantic value stack: array, bottom, top. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp = yyvs; - - int yyn; - /* The return value of yyparse. */ - int yyresult; - /* Lookahead symbol kind. */ - yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yychar = YYEMPTY; /* Cause a token to be read. */ - - goto yysetstate; - - -/*------------------------------------------------------------. -| yynewstate -- push a new state, which is found in yystate. | -`------------------------------------------------------------*/ -yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - -/*--------------------------------------------------------------------. -| yysetstate -- set current state (the top of the stack) to yystate. | -`--------------------------------------------------------------------*/ -yysetstate: - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - YY_ASSERT (0 <= yystate && yystate < YYNSTATES); - YY_IGNORE_USELESS_CAST_BEGIN - *yyssp = YY_CAST (yy_state_t, yystate); - YY_IGNORE_USELESS_CAST_END - YY_STACK_PRINT (yyss, yyssp); - - if (yyss + yystacksize - 1 <= yyssp) -#if !defined yyoverflow && !defined YYSTACK_RELOCATE - YYNOMEM; -#else - { - /* Get the current used size of the three stacks, in elements. */ - YYPTRDIFF_T yysize = yyssp - yyss + 1; - -# if defined yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - yy_state_t *yyss1 = yyss; - YYSTYPE *yyvs1 = yyvs; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * YYSIZEOF (*yyssp), - &yyvs1, yysize * YYSIZEOF (*yyvsp), - &yystacksize); - yyss = yyss1; - yyvs = yyvs1; - } -# else /* defined YYSTACK_RELOCATE */ - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - YYNOMEM; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yy_state_t *yyss1 = yyss; - union yyalloc *yyptr = - YY_CAST (union yyalloc *, - YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); - if (! yyptr) - YYNOMEM; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YY_IGNORE_USELESS_CAST_BEGIN - YYDPRINTF ((stderr, "Stack size increased to %ld\n", - YY_CAST (long, yystacksize))); - YY_IGNORE_USELESS_CAST_END - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } -#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ - - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token\n")); - yychar = yylex (); - } - - if (yychar <= YYEOF) - { - yychar = YYEOF; - yytoken = YYSYMBOL_YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else if (yychar == YYerror) - { - /* The scanner already issued an error message, process directly - to error recovery. But do not keep the error token as - lookahead, it is too special and may lead us to an endless - loop in error recovery. */ - yychar = YYUNDEF; - yytoken = YYSYMBOL_YYerror; - goto yyerrlab1; - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yytable_value_is_error (yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Discard the shifted token. */ - yychar = YYEMPTY; - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 2: /* rc: %empty */ -#line 38 "sys/cmd/rc/syntax.y" - { return 0; } -#line 1549 "sys/cmd/rc/parse.c" - break; - - case 3: /* rc: line '\n' */ -#line 39 "sys/cmd/rc/syntax.y" - { return compile((yyvsp[-1].tree)); } -#line 1555 "sys/cmd/rc/parse.c" - break; - - case 5: /* line: cmds line */ -#line 43 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(';', (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1561 "sys/cmd/rc/parse.c" - break; - - case 7: /* body: cmdsln body */ -#line 47 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(';', (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1567 "sys/cmd/rc/parse.c" - break; - - case 8: /* paren: '(' body ')' */ -#line 50 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tparen, (yyvsp[-1].tree)); } -#line 1573 "sys/cmd/rc/parse.c" - break; - - case 9: /* block: '{' body '}' */ -#line 53 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tblock, (yyvsp[-1].tree)); } -#line 1579 "sys/cmd/rc/parse.c" - break; - - case 11: /* cmds: cmd '&' */ -#line 57 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1('&', (yyvsp[-1].tree)); } -#line 1585 "sys/cmd/rc/parse.c" - break; - - case 14: /* ifbody: cmd */ -#line 64 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tif, nil, (yyvsp[0].tree)); } -#line 1591 "sys/cmd/rc/parse.c" - break; - - case 15: /* ifbody: block Telse nl cmd */ -#line 65 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree3(Tif, nil, (yyvsp[-3].tree), (yyvsp[-2].tree)); } -#line 1597 "sys/cmd/rc/parse.c" - break; - - case 16: /* case: Tcase words ';' */ -#line 68 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-1].tree), 0); } -#line 1603 "sys/cmd/rc/parse.c" - break; - - case 17: /* case: Tcase words '\n' */ -#line 69 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-1].tree), 0); } -#line 1609 "sys/cmd/rc/parse.c" - break; - - case 18: /* casebody: cmd */ -#line 72 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tcasebody, (yyvsp[0].tree), nil); } -#line 1615 "sys/cmd/rc/parse.c" - break; - - case 19: /* casebody: case casebody */ -#line 73 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tcasebody, (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1621 "sys/cmd/rc/parse.c" - break; - - case 20: /* casebody: cmdsln casebody */ -#line 74 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tcasebody, (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1627 "sys/cmd/rc/parse.c" - break; - - case 21: /* assign: executable '=' word */ -#line 77 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2('=', (yyvsp[-2].tree), (yyvsp[0].tree)); } -#line 1633 "sys/cmd/rc/parse.c" - break; - - case 23: /* redir: Tredir word */ -#line 81 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 0); } -#line 1639 "sys/cmd/rc/parse.c" - break; - - case 24: /* epilog: %empty */ -#line 84 "sys/cmd/rc/syntax.y" - { (yyval.tree) = nil; } -#line 1645 "sys/cmd/rc/parse.c" - break; - - case 25: /* epilog: redir epilog */ -#line 85 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 1); } -#line 1651 "sys/cmd/rc/parse.c" - break; - - case 26: /* cmd: %empty */ -#line 88 "sys/cmd/rc/syntax.y" - { (yyval.tree) = nil; } -#line 1657 "sys/cmd/rc/parse.c" - break; - - case 27: /* cmd: basic */ -#line 89 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tbasic, (yyvsp[0].tree)); } -#line 1663 "sys/cmd/rc/parse.c" - break; - - case 28: /* cmd: block epilog */ -#line 90 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangepilog((yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1669 "sys/cmd/rc/parse.c" - break; - - case 29: /* cmd: cmd Tpipe nl cmd */ -#line 91 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild2((yyvsp[-2].tree), (yyvsp[-3].tree), 0, (yyvsp[0].tree), 1); } -#line 1675 "sys/cmd/rc/parse.c" - break; - - case 30: /* cmd: cmd Tandand nl cmd */ -#line 92 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tandand, (yyvsp[-3].tree), (yyvsp[0].tree)); } -#line 1681 "sys/cmd/rc/parse.c" - break; - - case 31: /* cmd: cmd Toror nl cmd */ -#line 93 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Toror, (yyvsp[-3].tree), (yyvsp[0].tree)); } -#line 1687 "sys/cmd/rc/parse.c" - break; - - case 32: /* cmd: redir cmd */ -#line 94 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 1); } -#line 1693 "sys/cmd/rc/parse.c" - break; - - case 33: /* cmd: assign cmd */ -#line 95 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-1].tree), (yyvsp[0].tree), 2); } -#line 1699 "sys/cmd/rc/parse.c" - break; - - case 34: /* cmd: Tbang cmd */ -#line 96 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tbang, (yyvsp[0].tree)); } -#line 1705 "sys/cmd/rc/parse.c" - break; - - case 35: /* cmd: Tsubshell cmd */ -#line 97 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tsubshell, (yyvsp[0].tree)); } -#line 1711 "sys/cmd/rc/parse.c" - break; - - case 36: /* cmd: Tfor '(' word ')' nl cmd */ -#line 98 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild3((yyvsp[-5].tree), (yyvsp[-3].tree), nil, (yyvsp[0].tree)); } -#line 1717 "sys/cmd/rc/parse.c" - break; - - case 37: /* cmd: Tfor '(' word Tin words ')' nl cmd */ -#line 99 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild3((yyvsp[-7].tree), (yyvsp[-5].tree), (yyvsp[-3].tree), (yyvsp[0].tree)); } -#line 1723 "sys/cmd/rc/parse.c" - break; - - case 38: /* cmd: Twhile paren nl cmd */ -#line 100 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild2((yyvsp[-3].tree), (yyvsp[-2].tree), 0, (yyvsp[0].tree), 1); } -#line 1729 "sys/cmd/rc/parse.c" - break; - - case 39: /* cmd: Tif paren nl ifbody */ -#line 101 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild1((yyvsp[-2].tree), (yyvsp[-3].tree), 0); } -#line 1735 "sys/cmd/rc/parse.c" - break; - - case 40: /* cmd: Tswitch '(' word ')' nl '{' casebody '}' */ -#line 102 "sys/cmd/rc/syntax.y" - { (yyval.tree) = hangchild2((yyvsp[-7].tree), (yyvsp[-5].tree), 0, (yyvsp[-1].tree), 1); } -#line 1741 "sys/cmd/rc/parse.c" - break; - - case 42: /* basic: basic word */ -#line 106 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Targs, (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1747 "sys/cmd/rc/parse.c" - break; - - case 43: /* basic: basic redir */ -#line 107 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Targs, (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1753 "sys/cmd/rc/parse.c" - break; - - case 45: /* atom: keyword */ -#line 111 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tword, (yyvsp[0].tree)); } -#line 1759 "sys/cmd/rc/parse.c" - break; - - case 47: /* word: word '^' atom */ -#line 115 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2('^', (yyvsp[-2].tree), (yyvsp[0].tree)); } -#line 1765 "sys/cmd/rc/parse.c" - break; - - case 49: /* executable: executable '^' atom */ -#line 119 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2('^', (yyvsp[-2].tree), (yyvsp[0].tree)); } -#line 1771 "sys/cmd/rc/parse.c" - break; - - case 51: /* nonkeyword: '$' atom */ -#line 123 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1('$', (yyvsp[0].tree)); } -#line 1777 "sys/cmd/rc/parse.c" - break; - - case 52: /* nonkeyword: '$' atom Tindex words ')' */ -#line 124 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Tindex, (yyvsp[-3].tree), (yyvsp[-1].tree)); } -#line 1783 "sys/cmd/rc/parse.c" - break; - - case 53: /* nonkeyword: '(' wordsnl ')' */ -#line 125 "sys/cmd/rc/syntax.y" - { (yyval.tree) = (yyvsp[-1].tree); } -#line 1789 "sys/cmd/rc/parse.c" - break; - - case 54: /* nonkeyword: Tcount atom */ -#line 126 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tcount, (yyvsp[0].tree)); } -#line 1795 "sys/cmd/rc/parse.c" - break; - - case 55: /* nonkeyword: Tjoin atom */ -#line 127 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1(Tjoin, (yyvsp[0].tree)); } -#line 1801 "sys/cmd/rc/parse.c" - break; - - case 56: /* nonkeyword: '`' block */ -#line 128 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree1('`', (yyvsp[0].tree)); } -#line 1807 "sys/cmd/rc/parse.c" - break; - - case 67: /* words: %empty */ -#line 135 "sys/cmd/rc/syntax.y" - { (yyval.tree) = nil; } -#line 1813 "sys/cmd/rc/parse.c" - break; - - case 68: /* words: words word */ -#line 136 "sys/cmd/rc/syntax.y" - { (yyval.tree) = maketree2(Twords, (yyvsp[-1].tree), (yyvsp[0].tree)); } -#line 1819 "sys/cmd/rc/parse.c" - break; - - case 69: /* wordsnl: %empty */ -#line 139 "sys/cmd/rc/syntax.y" - { (yyval.tree) = nil; } -#line 1825 "sys/cmd/rc/parse.c" - break; - - case 71: /* wordsnl: wordsnl word */ -#line 141 "sys/cmd/rc/syntax.y" - {(yyval.tree) = (!(yyvsp[-1].tree)) ? ((!(yyvsp[0].tree)) ? nil : (yyvsp[0].tree)) : ((!(yyvsp[0].tree)) ? (yyvsp[-1].tree) : maketree2(Twords, (yyvsp[-1].tree), (yyvsp[0].tree))); } -#line 1831 "sys/cmd/rc/parse.c" - break; - - -#line 1835 "sys/cmd/rc/parse.c" - - default: break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); - - YYPOPSTACK (yylen); - yylen = 0; - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - { - const int yylhs = yyr1[yyn] - YYNTOKENS; - const int yyi = yypgoto[yylhs] + *yyssp; - yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp - ? yytable[yyi] - : yydefgoto[yylhs]); - } - - goto yynewstate; - - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; - { - yypcontext_t yyctx - = {yyssp, yytoken}; - char const *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == -1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = YY_CAST (char *, - YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); - if (yymsg) - { - yysyntax_error_status - = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); - yymsgp = yymsg; - } - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = YYENOMEM; - } - } - yyerror (yymsgp); - if (yysyntax_error_status == YYENOMEM) - YYNOMEM; - } - } - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - /* Pacify compilers when the user code never invokes YYERROR and the - label yyerrorlab therefore never appears in user code. */ - if (0) - YYERROR; - ++yynerrs; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - /* Pop stack until we find a state that shifts the error token. */ - for (;;) - { - yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) - { - yyn += YYSYMBOL_YYerror; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - - yydestruct ("Error: popping", - YY_ACCESSING_SYMBOL (yystate), yyvsp); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturnlab; - - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturnlab; - - -/*-----------------------------------------------------------. -| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | -`-----------------------------------------------------------*/ -yyexhaustedlab: - yyerror (YY_("memory exhausted")); - yyresult = 2; - goto yyreturnlab; - - -/*----------------------------------------------------------. -| yyreturnlab -- parsing is finished, clean up and return. | -`----------------------------------------------------------*/ -yyreturnlab: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - return yyresult; -} - -#line 147 "sys/cmd/rc/syntax.y" - diff --git a/sys/cmd/rc/parse.h b/sys/cmd/rc/parse.h deleted file mode 100644 index 64ee07b..0000000 --- a/sys/cmd/rc/parse.h +++ /dev/null @@ -1,141 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -#ifndef YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED -# define YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int yydebug; -#endif - -/* Token kinds. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - YYEMPTY = -2, - YYEOF = 0, /* "end of file" */ - YYerror = 256, /* error */ - YYUNDEF = 257, /* "invalid token" */ - Tfor = 258, /* Tfor */ - Tin = 259, /* Tin */ - Twhile = 260, /* Twhile */ - Tif = 261, /* Tif */ - Telse = 262, /* Telse */ - Tswitch = 263, /* Tswitch */ - Tcase = 264, /* Tcase */ - Tcasebody = 265, /* Tcasebody */ - Ttwiddle = 266, /* Ttwiddle */ - Tbang = 267, /* Tbang */ - Tsubshell = 268, /* Tsubshell */ - Tfunc = 269, /* Tfunc */ - Tredir = 270, /* Tredir */ - Tdup = 271, /* Tdup */ - Tpipe = 272, /* Tpipe */ - Tindex = 273, /* Tindex */ - Tbasic = 274, /* Tbasic */ - Targs = 275, /* Targs */ - Tword = 276, /* Tword */ - Twords = 277, /* Twords */ - Tparen = 278, /* Tparen */ - Tblock = 279, /* Tblock */ - Tandand = 280, /* Tandand */ - Toror = 281, /* Toror */ - Tcount = 282, /* Tcount */ - Tjoin = 283 /* Tjoin */ - }; - typedef enum yytokentype yytoken_kind_t; -#endif -/* Token kinds. */ -#define YYEMPTY -2 -#define YYEOF 0 -#define YYerror 256 -#define YYUNDEF 257 -#define Tfor 258 -#define Tin 259 -#define Twhile 260 -#define Tif 261 -#define Telse 262 -#define Tswitch 263 -#define Tcase 264 -#define Tcasebody 265 -#define Ttwiddle 266 -#define Tbang 267 -#define Tsubshell 268 -#define Tfunc 269 -#define Tredir 270 -#define Tdup 271 -#define Tpipe 272 -#define Tindex 273 -#define Tbasic 274 -#define Targs 275 -#define Tword 276 -#define Twords 277 -#define Tparen 278 -#define Tblock 279 -#define Tandand 280 -#define Toror 281 -#define Tcount 282 -#define Tjoin 283 - -/* Value type. */ -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -union YYSTYPE -{ -#line 24 "sys/cmd/rc/syntax.y" - - struct Tree *tree; - -#line 127 "sys/cmd/rc/parse.h" - -}; -typedef union YYSTYPE YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 -# define YYSTYPE_IS_DECLARED 1 -#endif - - -extern YYSTYPE yylval; - - -int yyparse (void); - - -#endif /* !YY_YY_SYS_CMD_RC_PARSE_H_INCLUDED */ diff --git a/sys/cmd/rc/prompt.c b/sys/cmd/rc/prompt.c deleted file mode 100644 index 1122d54..0000000 --- a/sys/cmd/rc/prompt.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "rc.h" - -/* static char promptbuf[7] = {'>', ' ', 0, ' ' , ' ', ' ', 0}; */ -static char *base= "\x1b[1;31m" ">" "\x1b[0;0m" " ", *promptstr; - -void -resetprompt(void) -{ - promptstr = base; -} - -int -prompt(ushort *flag) -{ - int f = *flag; - - if(f){ - if(!readline(promptstr)){ - runner->flag.eof = 1; - return 0; - } - if(runner->cmd.io->e[-1] == '\n'){ - runner->cmd.io->e[-1] = 0; - addhistory(runner->cmd.io->b); - runner->cmd.io->e[-1] = '\n'; - } - - write(mapfd(0), "\n\r", 2); - promptstr = " "; - - runner->line++; - *flag = 0; - } - - return 1; -} diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h deleted file mode 100644 index 9b415fc..0000000 --- a/sys/cmd/rc/rc.h +++ /dev/null @@ -1,263 +0,0 @@ - -#include -#include -#include - -// ----------------------------------------------------------------------- -// types - -typedef struct Io Io; -typedef struct Var Var; -typedef struct Word Word; -typedef struct List List; -typedef struct Tree Tree; -typedef struct Redir Redir; -typedef union Code Code; -typedef struct Thread Thread; -typedef struct Shell Shell; - -struct Io -{ - int fd, cap; - char *s; - char *b, *e, buf[]; -}; - -enum -{ - Rappend, - Rwrite, - Rread, - Rhere, - Rdupfd, - Ropen, - Rclose, - Rrdwr -}; - -struct Tree -{ - int type; - union{ - struct { - ushort quoted; - char *str; // Tword - }; - struct { - ushort type; // Tpipe, Tredir, Tdup - int fd[2]; - } redir; - }; - Tree *child[3]; - Tree *link; -}; - -struct Word -{ - char *str; - Word *link; -}; - -struct List -{ - Word *word; - List *link; -}; - -/* - * the first word of any code vector is a reference count - * always create a new reference to a code vector by calling copycode() - * always call freecode() when deleting a reference - */ -union Code -{ - int i; - void (*f)(void); - char *s; -}; - -struct Var -{ - char *name; - Word *val; - short new : 1; - short newfunc : 1; - Code *func; - void (*update)(Var *); - Var *link; -}; - -struct Redir -{ - char type; /* what to do */ - short from, to; /* what to do it to */ - struct Redir *link; /* what else to do (reverse order) */ -}; - -enum -{ - Pnil, - Prun, - Pstop, - Psig, - Pagain, - Pdone, -}; - -struct WaitItem -{ - int pid; - ushort status; -}; - -struct Thread -{ - struct { - int i; - Code *exe; - } code; // execution stack - struct { - Io *io; - char *path; - } cmd; // command input - - List *args; // argument stack - Var *local; // local variables - struct { - Redir *start; - Redir *end; - } redir; // list of redirections - - struct { - ushort user : 1; - ushort eof : 1; - } flag; - - struct { - ushort status; - int len, cap; - struct WaitItem *on; - } wait; - - int pid, pgid, status; - long line; - - Thread *caller; // process we return to - Thread *link; // next job -}; - -struct Shell -{ - int pid; - Io *err; - int status; - int interactive; - Thread *jobs; -}; - -// ----------------------------------------------------------------------- -// globals - -extern Shell shell; -extern Thread *runner; -extern Code *compiled; - -// ----------------------------------------------------------------------- -// functions - -/* util.c */ -void itoa(char*, long i); -void fatal(char *, ...); -void *emalloc(uintptr); -void *erealloc(void*, uintptr); -void efree(void*); - -/* input.c */ -int readline(char *); -void enablevi(void); - -void inithistory(void); -int addhistory(char *); - -/* prompt.c */ -void resetprompt(void); -int prompt(ushort *); - -/* io.c */ -Io *openfd(int fd); -Io *openstr(void); -void terminate(Io *io); - -int get(Io *); -int put(Io **, char); - -void flush(Io *io); -void print(Io *, char *, ...); - -/* lex.c */ -int iswordchar(int c); -int yylex(void); - -/* tree.c */ -Tree *maketree(void); -Tree *maketree1(int, Tree*); -Tree *maketree2(int, Tree*, Tree*); -Tree *maketree3(int, Tree*, Tree*, Tree*); - -Tree *token(int, char *); -Tree *hangchild1(Tree *, Tree *, int); -Tree *hangchild2(Tree *, Tree *, int, Tree *, int); -Tree *hangchild3(Tree *, Tree *, Tree *, Tree *); -Tree *hangepilog(Tree *, Tree*); - -void freeparsetree(void); - -/* sys.c */ -void initenv(void); -void redirect(struct Redir *); -void execute(Word *, Word*); -int mapfd(int fd); - -/* wait.c */ -void addwait(Thread *, int); -void delwait(Thread *, int); -void clearwait(Thread*); - -int waitall(Thread *); -int waitfor(Thread *, int); - -void killzombies(void); - -/* job.c */ -Thread *getjob(int, int*); -void addjob(Thread *); -void deljob(Thread *); -void wakeup(Thread *); -void report(Thread *, int); - -void foreground(Thread *, int); -void background(Thread *, int); - -/* exec.c */ -// XXX: odd place for this -int count(Word *); -Word *makeword(char *str, Word *link); -void freeword(Word *w); - -/* var.c */ -Var *var(char*); -Var *definevar(char*, Var *); -Var *globalvar(char*); -Var *makevar(char *name, Var *link); -void setvar(char *, Word *); -int iskeyword(char *); - -void initpath(void); -void initkeywords(void); - -char **mkenv(void); - -/* code.c */ -int compile(Tree *); -Code *copycode(Code *c); -void freecode(Code *c); diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk deleted file mode 100644 index a2fd058..0000000 --- a/sys/cmd/rc/rules.mk +++ /dev/null @@ -1,31 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) :=\ - $(d)/util.c\ - $(d)/input.c\ - $(d)/prompt.c\ - $(d)/io.c\ - $(d)/lex.c\ - $(d)/parse.c\ - $(d)/tree.c\ - $(d)/code.c\ - $(d)/var.c\ - $(d)/sys.c\ - $(d)/wait.c\ - $(d)/job.c\ - $(d)/exec.c\ - $(d)/main.c - -BINS_$(d) := $(d)/rc - -include share/paths.mk -$(d)/parse.h $(d)/parse.c: $(d)/syntax.y - yacc --header=$( line cmds cmdsln body paren block ifbody casebody case assign epilog redir; -%type cmd basic executable nonkeyword keyword word words wordsnl atom; -%type Tfor Tin Twhile Tif Telse Tswitch Tcase Ttwiddle Tbang Tsubshell Tfunc; -%type Tword Tredir Tpipe Tdup; - -/* grammar */ - -%start rc - -%% -rc: -/*empty*/ { return 0; } -| line '\n' { return compile($1); } - -line: - cmd -| cmds line { $$ = maketree2(';', $1, $2); } - -body: - cmd -| cmdsln body { $$ = maketree2(';', $1, $2); } - -paren: - '(' body ')' { $$ = maketree1(Tparen, $2); } - -block: - '{' body '}' { $$ = maketree1(Tblock, $2); } - -cmds: - cmd ';' -| cmd '&' { $$ = maketree1('&', $1); } - -cmdsln: - cmds -| cmd '\n' - -ifbody: - cmd %prec Telse { $$ = maketree2(Tif, nil, $1); } -| block Telse nl cmd { $$ = maketree3(Tif, nil, $1, $2); } - -case: - Tcase words ';' { $$ = hangchild1($1, $2, 0); } -| Tcase words '\n' { $$ = hangchild1($1, $2, 0); } - -casebody: - cmd { $$ = maketree2(Tcasebody, $1, nil); } -| case casebody { $$ = maketree2(Tcasebody, $1, $2); } -| cmdsln casebody { $$ = maketree2(Tcasebody, $1, $2); } - -assign: - executable '=' word { $$ = maketree2('=', $1, $3); } - -redir: - Tdup -| Tredir word { $$ = hangchild1($1, $2, 0); } - -epilog: -/* empty */ { $$ = nil; } -| redir epilog { $$ = hangchild1($1, $2, 1); } - -cmd: -/* empty */ %prec Twhile { $$ = nil; } -| basic { $$ = maketree1(Tbasic, $1); } -| block epilog { $$ = hangepilog($1, $2); } -| cmd Tpipe nl cmd { $$ = hangchild2($2, $1, 0, $4, 1); } -| cmd Tandand nl cmd { $$ = maketree2(Tandand, $1, $4); } -| cmd Toror nl cmd { $$ = maketree2(Toror, $1, $4); } -| redir cmd %prec Tbang { $$ = hangchild1($1, $2, 1); } -| assign cmd %prec Tbang { $$ = hangchild1($1, $2, 2); } -| Tbang cmd { $$ = maketree1(Tbang, $2); } -| Tsubshell cmd { $$ = maketree1(Tsubshell, $2); } -| Tfor '(' word ')' nl cmd { $$ = hangchild3($1, $3, nil, $6); } -| Tfor '(' word Tin words ')' nl cmd { $$ = hangchild3($1, $3, $5, $8); } -| Twhile paren nl cmd { $$ = hangchild2($1, $2, 0, $4, 1); } -| Tif paren nl ifbody { $$ = hangchild1($2, $1, 0); } -| Tswitch '(' word ')' nl '{' casebody '}' { $$ = hangchild2($1, $3, 0, $7, 1); } - -basic: - executable -| basic word { $$ = maketree2(Targs, $1, $2); } -| basic redir { $$ = maketree2(Targs, $1, $2); } - -atom: - nonkeyword -| keyword { $$ = maketree1(Tword, $1); } - -word: - atom -| word '^' atom { $$ = maketree2('^', $1, $3); } - -executable: - nonkeyword -| executable '^' atom { $$ = maketree2('^', $1, $3); } - -nonkeyword: - Tword -| '$' atom { $$ = maketree1('$', $2); } -| '$' atom Tindex words ')' { $$ = maketree2(Tindex, $2, $4); } -| '(' wordsnl ')' { $$ = $2; } -| Tcount atom { $$ = maketree1(Tcount, $2); } -| Tjoin atom { $$ = maketree1(Tjoin, $2); } -| '`' block { $$ = maketree1('`', $2); } -//| Tredir block { $$ = hangchild1($1, $2, 0); $$->type = Tpipefd; } - -keyword: - Tfor|Tin|Twhile|Tif|Telse|Tswitch|Tcase|Tbang|Tsubshell|Tfunc - -words: -/* empty */ { $$ = nil; } -| words word { $$ = maketree2(Twords, $1, $2); } - -wordsnl: -/* empty */ { $$ = nil; } -| wordsnl '\n' /* empty */ -| wordsnl word {$$ = (!$1) ? ((!$2) ? nil : $2) : ((!$2) ? $1 : maketree2(Twords, $1, $2)); } - -nl: -/*empty*/ -| nl '\n' - -%% diff --git a/sys/cmd/rc/sys.c b/sys/cmd/rc/sys.c deleted file mode 100644 index 807359d..0000000 --- a/sys/cmd/rc/sys.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "rc.h" - -// ----------------------------------------------------------------------- -// internal - -static -char** -mkargv(Word *args) -{ - char **argv=emalloc((count(args)+2)*sizeof(char *)); - char **argp=argv+1; /* leave one at front for runcoms */ - - for(;args;args=args->link) - *argp++=args->str; - *argp=nil; - - return argv; -} - -static -Word* -envval(char *s) -{ - Word *v; - char *t, c; - - for(t=s; *t && *t!='\1'; t++) - ; - - c = *t; - *t = '\0'; - - v = makeword(s, (c=='\0') ? nil : envval(t+1)); - *t=c; - - return v; -} - -// ----------------------------------------------------------------------- -// exported - -void -initenv(void) -{ - extern char **environ; - - char *s; - char **env; - - for(env=environ; *env; env++) { - for(s=*env; *s && *s != '(' && *s != '='; s++) - ; - switch(*s){ - case '\0': - break; - case '(': /* ignore functions */ - break; - case '=': - *s = '\0'; - setvar(*env, envval(s+1)); - *s = '='; - break; - } - } -} - -void -execute(Word *cmd, Word *path) -{ - int nc; - char **argv = mkargv(cmd); - char **env = mkenv(); - char file[1024]; - - for(; path; path=path->link){ - nc = strlen(path->str); - if(nc < arrlen(file)){ - strcpy(file, path->str); - if(file[0]){ - strcat(file, "/"); - nc++; - } - if(nc+strlen(argv[1]) < arrlen(file)){ - strcat(file, argv[1]); - execve(file, argv+1, env); - }else - fatal("command name too long"); - } - } - print(shell.err, "could not execute command: %s\n", argv[1]); - efree(argv); -} - -void -redirect(Redir *r) -{ - if(r){ - redirect(r->link); - switch(r->type){ - case Ropen: - if(r->from != r->to){ - dup2(r->from, r->to); - close(r->from); - } - break; - case Rdupfd: - dup2(r->from, r->to); // TODO: error checking - break; - case Rclose: - close(r->from); - break; - default: - fatal("unrecognized redirection type %d\n", r->type); - } - } -} - -int -mapfd(int fd) -{ - Redir *r; - for(r = runner->redir.end; r; r = r->link){ - switch(r->type){ - case Rclose: - if(r->from == fd) - fd = -1; - break; - case Rdupfd: - case Ropen: - if(r->to == fd) - fd = r->from; - break; - } - } - - return fd; -} diff --git a/sys/cmd/rc/tree.c b/sys/cmd/rc/tree.c deleted file mode 100644 index 2c65041..0000000 --- a/sys/cmd/rc/tree.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "rc.h" -#include "parse.h" - -static Tree *nodes; - -Tree* -maketree(void) -{ - Tree *node = emalloc(sizeof(*node)); - - node->link = nodes; - nodes = node; - return node; -} - -void -freeparsetree(void) -{ - Tree *t, *nt; - for(t = nodes; t; t = nt) { - nt = t->link; - if(t->type == Tword && t->str) - efree(t->str); - efree(t); - } - nodes = nil; -} - -Tree* -maketree1(int type, Tree *c0) -{ - return maketree2(type, c0, nil); -} - -Tree* -maketree2(int type, Tree *c0, Tree *c1) -{ - return maketree3(type, c0, c1, nil); -} - -Tree* -maketree3(int type, Tree *c0, Tree *c1, Tree *c2) -{ - Tree *node = maketree(); - - node->type = type; - node->child[0] = c0; - node->child[1] = c1; - node->child[2] = c2; - - return node; -} - -Tree* -hangchild1(Tree *node, Tree *c, int i) -{ - node->child[i] = c; - - return node; -} - -Tree* -hangchild2(Tree *node, Tree *c1, int i1, Tree *c2, int i2) -{ - node->child[i1] = c1; - node->child[i2] = c2; - - return node; -} - -Tree* -hangchild3(Tree *node, Tree *c0, Tree *c1, Tree *c2) -{ - node->child[0] = c0; - node->child[1] = c1; - node->child[2] = c2; - - return node; -} - -Tree* -hangepilog(Tree *cmd, Tree *epi) -{ - Tree *p; - if(!epi) - return cmd; - for(p = epi; p->child[1]; p = p->child[1]) - ; - - p->child[1] = cmd; - return epi; -} - -Tree* -token(int type, char *s) -{ - Tree *node = maketree(); - - node->type = type; - node->str = strdup(s); - - return node; -} - -/* -Tree* -basic(Tree *node) -{ - return maketree1(Tbasic, node); -} -*/ diff --git a/sys/cmd/rc/util.c b/sys/cmd/rc/util.c deleted file mode 100644 index b0be788..0000000 --- a/sys/cmd/rc/util.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "rc.h" - -void -fatal(char *msg, ...) -{ - va_list args; - vfprintf(stderr, msg, args); - va_end(args); - - abort(); -} - -void* -emalloc(uintptr n) -{ - void *p; - if(!(p = malloc(n))) - fatal("out of memory: can't allocate %d bytes", n); - - memset(p, 0, n); - return p; -} - -void* -erealloc(void *p, uintptr n) -{ - void *r; - if(!(r = realloc(p,n))) - fatal("out of memory: can't reallocate %d bytes", n); - - return r; -} - -void -efree(void *p) -{ - if(p) - free(p); - // TODO: log the double free -} - - -char *bp; - -static -void -iacvt(int n) -{ - if(n<0){ - *bp++='-'; - n=-n; /* doesn't work for n==-inf */ - } - if(n/10) - iacvt(n/10); - - *bp++=n%10+'0'; -} - -void -itoa(char *s, long n) -{ - bp = s; - iacvt(n); - *bp='\0'; -} diff --git a/sys/cmd/rc/var.c b/sys/cmd/rc/var.c deleted file mode 100644 index 3e9635f..0000000 --- a/sys/cmd/rc/var.c +++ /dev/null @@ -1,336 +0,0 @@ -#include "rc.h" -#include "parse.h" - -// TODO: string interning - -// ----------------------------------------------------------------------- -// globals - -struct Keyword -{ - char *name; - int type; -}; - -static Var *globals[512]; -static struct Keyword keywords[100]; // sparse map means less hits - -// ----------------------------------------------------------------------- -// internals - -static -int -hash(char *s, int len) -{ - int h =0, i = 1; - while(*s) - h += *s++*i++; - - h %= len; - return h < 0 ? h+len : h; -} - -static -void -·setvar(char *name, Word *val, int call) -{ - Var *v = var(name); - freeword(v->val); - - v->val = val; - v->new = 1; // this never turns off? - - if(call && v->update) - v->update(v); -} - -static -char* -list2strcolon(Word *words) -{ - char *value, *s, *t; - int len = 0; - Word *ap; - for(ap = words;ap;ap = ap->link) - len+=1+strlen(ap->str); - - value = emalloc(len+1); - - s = value; - for(ap = words; ap; ap = ap->link){ - for(t = ap->str;*t;) *s++=*t++; - *s++=':'; - } - - if(s==value) - *s='\0'; - else - s[-1]='\0'; - - return value; -} - -static -void -littlepath(Var *v) -{ - /* convert $path to $PATH */ - char *p; - Word *w; - - p = list2strcolon(v->val); - w = emalloc(sizeof(*w)); - w->str = p; - w->link = nil; - - ·setvar("PATH", w, 1); -} - -static -void -bigpath(Var *v) -{ - /* convert $PATH to $path */ - char *p, *q; - Word **l, *w; - - if(v->val == nil){ - ·setvar("path", nil, 0); - return; - } - - p = v->val->str; - w = nil; - l = &w; - - /* Doesn't handle escaped colon nonsense. */ - if(p[0] == 0) - p = nil; - - while(p){ - q = strchr(p, ':'); - if(q) - *q = 0; - - *l = makeword(p[0] ? p : ".", nil); - l = &(*l)->link; - - if(q){ - *q = ':'; - p = q+1; - }else - p = nil; - } - ·setvar("path", w, 0); -} - -// ----------------------------------------------------------------------- -// exports - -Var* -makevar(char *name, Var *link) -{ - Var *v = emalloc(sizeof(*v)); - - v->name = name; - v->val = 0; - v->new = 0; - v->newfunc = 0; - v->link = link; - v->func = nil; - v->update = nil; - - return v; -} - -void -setvar(char *name, Word *val) -{ - ·setvar(name, val, 1); -} - -Var* -definevar(char *name, Var *link) -{ - Var *v = emalloc(sizeof(*v)); - - v->name = name; - v->val = 0; - v->link = link; - - return v; -} - -Var* -globalvar(char *name) -{ - int h; - Var *v; - - h = hash(name, arrlen(globals)); - - if(strcmp(name,"PATH")==0){ - flush(shell.err); - } - - for(v = globals[h]; v; v = v->link){ - if(strcmp(v->name, name) == 0){ - return v; - } - } - - return globals[h] = definevar(strdup(name), globals[h]); -} - -Var* -var(char *name) -{ - Var *v; - if(runner){ - for(v = runner->local; v; v=v->link) - if(strcmp(v->name, name) == 0) - return v; - } - return globalvar(name); -} - -static -int -cmpenv(const void *a, const void *b) -{ - return strcmp(*(char**)a, *(char**)b); -} - -char** -mkenv(void) -{ - char **env, **ep, *p, *q; - Var **h, *v; - Word *a; - int nvar=0, nchr=0, sep; - -#define BODY \ - if((v==var(v->name)) && v->val){ \ - nvar++; \ - nchr+=strlen(v->name)+1; \ - for(a=v->val;a;a=a->link) \ - nchr+=strlen(a->str)+1; \ - } - - for(v= runner->local; v; v=v->link){ - BODY - } - for(h=globals; h!=arrend(globals); h++){ - for(v = *h; v; v=v->link){ - BODY - } - } - -#undef BODY - - env=emalloc((nvar+1)*sizeof(*env)+nchr); - ep=env; - p=(char *)&env[nvar+1]; - -#define BODY \ - if((v==var(v->name)) && v->val){ \ - *ep++=p; \ - q=v->name; \ - while(*q) \ - *p++=*q++; \ - sep='='; \ - for(a=v->val;a;a=a->link){ \ - *p++=sep; \ - sep='\1'; \ - q=a->str; \ - while(*q) \ - *p++=*q++; \ - } \ - *p++='\0'; \ - } - - for(v=runner->local; v; v=v->link){ - BODY - } - for(h=globals; h!=arrend(globals); h++){ - for(v = *h; v; v=v->link){ - BODY - } - } -#undef BODY - - *ep=0; - - qsort((char *)env, nvar, sizeof ep[0], cmpenv); - return env; -} - -void -initpath(void) -{ - Var *v; - - v = globalvar("path"); - v->update = littlepath; - - v = globalvar("PATH"); - v->update = bigpath; - - flush(shell.err); - bigpath(v); -} - -#define KEYWORDS \ - KEYWORD("for", Tfor) \ - KEYWORD("in", Tin) \ - KEYWORD("while", Twhile) \ - KEYWORD("if", Tif) \ - KEYWORD("else", Telse) \ - KEYWORD("switch", Tswitch) \ - KEYWORD("case", Tcase) \ - KEYWORD("!", Tbang) \ - KEYWORD("@", Tsubshell) \ - KEYWORD("func", Tfunc) - -void -initkeywords(void) -{ - int i, s, j, h; -#define KEYWORD(a, b) a, - static char *name[] = { KEYWORDS }; -#undef KEYWORD -#define KEYWORD(a, b) b, - static int type[] = { KEYWORDS }; -#undef KEYWORD - - for(i = 0; i < arrlen(type); i++){ - h = hash(name[i], arrlen(keywords)); - for(s=0; s < arrlen(keywords); s++){ - j = (h + s) % arrlen(keywords); - if(!keywords[j].type || strcmp(keywords[j].name, name[i]) == 0){ - keywords[j].name = name[i]; - keywords[j].type = type[i]; - goto nextkeyword; - } - } - nextkeyword:; - } -} - -int -iskeyword(char *word) -{ - int i, s, h; - - h = hash(word, arrlen(keywords)); - for(s = 0; s < arrlen(keywords); s++){ - i = (h + s) % arrlen(keywords); - if(!keywords[i].type) - return 0; - if(strcmp(keywords[i].name, word) == 0) - return keywords[i].type; - } - return 0; -} - -#undef KEYWORDS diff --git a/sys/cmd/rc/wait.c b/sys/cmd/rc/wait.c deleted file mode 100644 index 911601c..0000000 --- a/sys/cmd/rc/wait.c +++ /dev/null @@ -1,247 +0,0 @@ -#include "rc.h" - -#include - -// ----------------------------------------------------------------------- -// globals - -struct WaitMsg -{ - int pid; - int type; - ulong time[3]; - int status; -}; - -// ----------------------------------------------------------------------- -// internal - -static -int -await(int pid4, int opt, struct WaitMsg *msg) -{ - int pid, status, core; - struct rusage ru; - ulong u, s; - - /* event loop */ - for(;;){ - if((pid = wait4(pid4, &status, opt, &ru)) <= 0){ - if(errno == ECHILD){ - msg->pid = -1; - return 1; - } - msg->pid = 0; - perror("failed wait4"); - return 0; - } - - u = ru.ru_utime.tv_sec*1000+((ru.ru_utime.tv_usec+500)/1000); - s = ru.ru_stime.tv_sec*1000+((ru.ru_stime.tv_usec+500)/1000); - - if(WIFEXITED(status)){ - msg->pid = pid; - msg->time[0] = u; - msg->time[1] = s; - msg->time[2] = u+s; - msg->status = WEXITSTATUS(status); - msg->type = Pdone; - - return 1; - } - - if(WIFSIGNALED(status)){ - msg->pid = pid; - msg->time[0] = u; - msg->time[1] = s; - msg->time[2] = u+s; - msg->status = WTERMSIG(status); - msg->type = Psig; - - return 1; - } - - if(WIFSTOPPED(status)){ - msg->pid = pid; - msg->time[0] = u; - msg->time[1] = s; - msg->time[2] = u+s; - msg->status = WSTOPSIG(status); - msg->type = Pstop; - - return 1; - } - } -} - -static -int -shouldwait(Thread *job) -{ - int i; - - for(i=0; iwait.len; i++){ - if(job->wait.on[i].status == Prun) - return 1; - } - - return 0; -} - -static inline -void -notify(Thread *job, struct WaitMsg msg) -{ - int i; - for(i=0; i < job->wait.len; i++){ - if(job->wait.on[i].pid == msg.pid){ - job->status = msg.status; - switch(msg.type){ - case Pstop: - print(shell.err, "%d: suspended\n", msg.pid); - job->wait.status = Pstop; - job->wait.on[i].status = Pstop; - break; - - case Psig: - print(shell.err, "%d: terminated by signal %d\n", msg.pid, msg.status); - /* fallthrough */ - case Pdone: - job->wait.on[i].status = Pdone; - delwait(job, msg.pid); - if(!job->wait.len) - job->wait.status = Pdone; - break; - - default: - fatal("%d: unrecognized message type %d\n", msg.pid, msg.type); - } - break; - } - } -} - -// ----------------------------------------------------------------------- -// exported - -void -clearwait(Thread *job) -{ - job->wait.len = 0; -} - -int -havewait(Thread *job, int pid) -{ - int i; - - for(i=0; iwait.len; i++) - if(job->wait.on[i].pid == pid) - return 1; - return 0; -} - -void -addwait(Thread *job, int pid) -{ - if(job->wait.len == job->wait.cap){ - job->wait.cap = job->wait.cap + 2; - job->wait.on = erealloc(job->wait.on, job->wait.cap*sizeof(*job->wait.on)); - } - - job->wait.on[job->wait.len++] = (struct WaitItem){.pid=pid, .status=Prun}; -} - -void -delwait(Thread *job, int pid) -{ - int r, w; - - for(r=w=0; r < job->wait.len; r++){ - if(job->wait.on[r].pid != pid) - job->wait.on[w++].pid = job->wait.on[r].pid; - } - job->wait.len = w; -} - -int -waitall(Thread *job) -{ - int i; - Thread *t; - struct WaitMsg msg; - - while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ - switch(msg.pid){ - case 0: // error - perror("wait job"); - return 0; - case -1: // no children: assume they have exited - job->wait.status = Pdone; - clearwait(job); - return 1; - default: - ; - } - - notify(job, msg); - } - return 1; -} - -int -waitfor(Thread *job, int pid) -{ - int i; - Thread *t; - struct WaitMsg msg; - - while(shouldwait(job) && await(-job->pgid, WUNTRACED, &msg)){ - switch(msg.pid){ - case 0: // error - perror("wait for"); - return 0; - case -1: // no children: assume they have exited - job->wait.status = Pdone; - clearwait(job); - return 1; - default: - ; - } - - notify(job, msg); - /* allow for an early exit */ - if(msg.pid == pid) - return 1; - } - return 1; - -} - -void -killzombies(void) -{ - Thread *job; - int index, status, pid; - - while((pid=waitpid(-1, &status, WNOHANG))>0){ - print(shell.err, "found zombie pid %d\n", pid); - flush(shell.err); - - job = getjob(pid, &index); - if(!job) - perror("invalid pid"); - - if(WIFEXITED(status)) - job->wait.status = Pdone; - if(WIFSTOPPED(status)) - job->wait.status = Pstop; - if(WIFCONTINUED(status)) - job->wait.status = Pagain; - - if(job->wait.status == Pdone){ - report(job,index); - deljob(job); - } - } -} diff --git a/sys/cmd/rules.mk b/sys/cmd/rules.mk deleted file mode 100644 index 52a059b..0000000 --- a/sys/cmd/rules.mk +++ /dev/null @@ -1,38 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -# DIR := $(d)/cc -# include $(DIR)/rules.mk - -DIR := $(d)/rc -include $(DIR)/rules.mk - -DIR := $(d)/walk -include $(DIR)/rules.mk - -DIR := $(d)/filter -include $(DIR)/rules.mk - -# DIR := $(d)/test -# include $(DIR)/rules.mk - -DIR := $(d)/ic -include $(DIR)/rules.mk - -DIR := $(d)/dwm -include $(DIR)/rules.mk - -DIR := $(d)/wm -include $(DIR)/rules.mk - -DIR := $(d)/menu -include $(DIR)/rules.mk - -DIR := $(d)/term -include $(DIR)/rules.mk - -# DIR := $(d)/term2 -# include $(DIR)/rules.mk - -include share/pop.mk diff --git a/sys/cmd/term/LICENSE b/sys/cmd/term/LICENSE deleted file mode 100644 index c356c39..0000000 --- a/sys/cmd/term/LICENSE +++ /dev/null @@ -1,34 +0,0 @@ -MIT/X Consortium License - -© 2014-2018 Hiltjo Posthuma -© 2018 Devin J. Pohly -© 2014-2017 Quentin Rameau -© 2009-2012 Aurélien APTEL -© 2008-2017 Anselm R Garbe -© 2012-2017 Roberto E. Vargas Caballero -© 2012-2016 Christoph Lohmann <20h at r-36 dot net> -© 2013 Eon S. Jeon -© 2013 Alexander Sedov -© 2013 Mark Edgar -© 2013-2014 Eric Pruitt -© 2013 Michael Forney -© 2013-2014 Markus Teich -© 2014-2015 Laslo Hunhold - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/sys/cmd/term/config.h b/sys/cmd/term/config.h deleted file mode 100644 index a740ecf..0000000 --- a/sys/cmd/term/config.h +++ /dev/null @@ -1,474 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#define VERSION "1" - -/* - * appearance - * - * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html - */ -static char *font = "consolas:size=16"; -static int borderpx = 2; - -/* - * What program is execed by st depends of these precedence rules: - * 1: program passed with -e - * 2: scroll and/or utmp - * 3: SHELL environment variable - * 4: value of shell in /etc/passwd - * 5: value of shell in config.h - */ -static char *shell = "/bin/mksh"; -char *utmp = nil; -/* scroll program: to enable use a string like "scroll" */ -char *scroll = nil; -char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; - -/* identification sequence returned in DA and DECID */ -char *vtiden = "\033[?6c"; - -/* Kerning / character bounding-box multipliers */ -static float cwscale = 1.0; -static float chscale = 1.0; - -/* - * word delimiter string - * - * More advanced example: L" `'\"()[]{}" - */ -wchar_t *worddelimiters = L" "; - -/* selection timeouts (in milliseconds) */ -static uint doubleclicktimeout = 300; -static uint tripleclicktimeout = 600; - -/* alt screens */ -int allowaltscreen = 1; - -/* allow certain non-interactive (insecure) window operations such as: - setting the clipboard text */ -int allowwindowops = 0; - -/* - * draw latency range in ms - from new content/keypress/etc until drawing. - * within this range, st draws when content stops arriving (idle). mostly it's - * near minlatency, but it waits longer for slow updates to avoid partial draw. - * low minlatency will tear/flicker more, as it can "detect" idle too early. - */ -static double minlatency = 8; -static double maxlatency = 33; - -/* - * blinking timeout (set to 0 to disable blinking) for the terminal blinking - * attribute. - */ -static uint blinktimeout = 800; - -/* - * thickness of underline and bar cursors - */ -static uint cursorthickness = 2; - -/* - * bell volume. It must be a value between -100 and 100. Use 0 for disabling - * it - */ -static int bellvolume = 0; - -/* default TERM value */ -char *termname = "term-256color"; - -/* - * spaces per tab - * - * When you are changing this value, don't forget to adapt the »it« value in - * the st.info and appropriately install the st.info in the environment where - * you use this st version. - * - * it#$tabspaces, - * - * Secondly make sure your kernel is not expanding tabs. When running `stty - * -a` »tab0« should appear. You can tell the terminal to not expand tabs by - * running following command: - * - * stty tabs - */ -uint tabspaces = 4; - -/* bg opacity */ -float alpha = 0.98; - -/* Terminal colors (16 first used in escape sequence) */ -static char *colorname[] = { - "#282828", /* hard contrast: #1d2021 / soft contrast: #32302f */ - "#ea6962", /* red */ - "#a9b665", /* green */ - "#d8a657", /* yellow */ - "#7daea3", /* blue */ - "#d3869b", /* magenta */ - "#89b482", /* cyan */ - "#d4be98", /* white */ - - "#928374", /* black */ - "#ef938e", /* red */ - "#bbc585", /* green */ - "#e1bb7e", /* yellow */ - "#9dc2ba", /* blue */ - "#e1acbb", /* magenta */ - "#a7c7a2", /* cyan */ - "#e2d3ba", /* white */ - - [255] = 0, - - /* more colors can be added after 255 to use with DefaultXX */ - "#fbf1c7", - "#3c3836", - "#555555", -}; - -/* - * Default colors (colorname index) - * foreground, background, cursor, reverse cursor - */ -uint defaultfg = 256; -uint defaultbg = 257; -static uint defaultcs = 15; -static uint defaultrcs = 258; - -/* - * Default shape of cursor - * 2: Block ("█") - * 4: Underline ("_") - * 6: Bar ("|") - * 7: Snowman ("☃") - */ -static uint cursorshape = 2; - -/* - * Default columns and rows numbers - */ - -static uint cols = 80; -static uint rows = 24; - -/* - * Default colour and shape of the mouse cursor - */ -static uint mouseshape = XC_left_ptr; -static uint mousefg = 0; -static uint mousebg = 7; - -/* - * Color used to display font attributes when fontconfig selected a font which - * doesn't match the ones requested. - */ -static uint defaultattr = 11; - -/* - * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). - * Note that if you want to use ShiftMask with selmasks, set this to an other - * modifier, set to 0 to not use it. - */ -static uint forcemousemod = ShiftMask; - -/* - * Internal mouse shortcuts. - * Beware that overloading Button1 will disable the selection. - */ -static MouseShortcut mshortcuts[] = { - /* mask button function argument release */ - { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, - { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, - { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, - { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, - { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, -}; - -/* Internal keyboard shortcuts. */ -#define MODKEY Mod1Mask -#define TERMMOD (ControlMask|ShiftMask) - -static Shortcut shortcuts[] = { - /* mask keysym function argument */ - { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, - { ControlMask, XK_Print, toggleprinter, {.i = 0} }, - { ShiftMask, XK_Print, printscreen, {.i = 0} }, - { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, - { TERMMOD, XK_plus, zoom, {.f = +1} }, - { ControlMask, XK_minus, zoom, {.f = -1} }, - { TERMMOD, XK_Home, zoomreset, {.f = 0} }, - { TERMMOD, XK_C, clipcopy, {.i = 0} }, - { TERMMOD, XK_V, clippaste, {.i = 0} }, - { TERMMOD, XK_Y, selpaste, {.i = 0} }, - { ShiftMask, XK_Insert, selpaste, {.i = 0} }, - { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, -}; - -/* - * Special keys (change & recompile st.info accordingly) - * - * Mask value: - * * Use XK_ANY_MOD to match the key no matter modifiers state - * * Use XK_NO_MOD to match the key alone (no modifiers) - * appkey value: - * * 0: no value - * * > 0: keypad application mode enabled - * * = 2: term.numlock = 1 - * * < 0: keypad application mode disabled - * appcursor value: - * * 0: no value - * * > 0: cursor application mode enabled - * * < 0: cursor application mode disabled - * - * Be careful with the order of the definitions because st searches in - * this table sequentially, so any XK_ANY_MOD must be in the last - * position for a key. - */ - -/* - * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) - * to be mapped below, add them to this array. - */ -static KeySym mappedkeys[] = { -1 }; - -/* - * State bits to ignore when matching key or button events. By default, - * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. - */ -static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; - -/* - * This is the huge key array which defines all compatibility to the Linux - * world. Please decide about changes wisely. - */ -static Key key[] = { - /* keysym mask string appkey appcursor */ - { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, - { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, - { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, - { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, - { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, - { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, - { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, - { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, - { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, - { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, - { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, - { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, - { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, - { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, - { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, - { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, - { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, - { XK_KP_End, ControlMask, "\033[J", -1, 0}, - { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, - { XK_KP_End, ShiftMask, "\033[K", -1, 0}, - { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, - { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, - { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, - { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, - { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, - { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, - { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, - { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, - { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, - { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, - { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, - { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, - { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, - { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, - { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, - { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, - { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, - { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, - { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, - { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, - { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, - { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, - { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, - { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, - { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, - { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, - { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, - { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, - { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, - { XK_Up, ControlMask, "\033[1;5A", 0, 0}, - { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, - { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, - { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, - { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, - { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, - { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, - { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, - { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, - { XK_Down, ControlMask, "\033[1;5B", 0, 0}, - { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, - { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, - { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, - { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, - { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, - { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, - { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, - { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, - { XK_Left, ControlMask, "\033[1;5D", 0, 0}, - { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, - { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, - { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, - { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, - { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, - { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, - { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, - { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, - { XK_Right, ControlMask, "\033[1;5C", 0, 0}, - { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, - { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, - { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, - { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, - { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, - { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, - { XK_Return, Mod1Mask, "\033\r", 0, 0}, - { XK_Return, XK_ANY_MOD, "\r", 0, 0}, - { XK_Insert, ShiftMask, "\033[4l", -1, 0}, - { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, - { XK_Insert, ControlMask, "\033[L", -1, 0}, - { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, - { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, - { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, - { XK_Delete, ControlMask, "\033[M", -1, 0}, - { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, - { XK_Delete, ShiftMask, "\033[2K", -1, 0}, - { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, - { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, - { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, - { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, - { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, - { XK_Home, ShiftMask, "\033[2J", 0, -1}, - { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, - { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, - { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, - { XK_End, ControlMask, "\033[J", -1, 0}, - { XK_End, ControlMask, "\033[1;5F", +1, 0}, - { XK_End, ShiftMask, "\033[K", -1, 0}, - { XK_End, ShiftMask, "\033[1;2F", +1, 0}, - { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, - { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, - { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, - { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, - { XK_Next, ControlMask, "\033[6;5~", 0, 0}, - { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, - { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, - { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, - { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, - { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, - { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, - { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, - { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, - { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, - { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, - { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, - { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, - { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, - { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, - { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, - { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, - { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, - { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, - { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, - { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, - { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, - { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, - { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, - { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, - { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, - { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, - { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, - { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, - { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, - { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, - { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, - { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, - { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, - { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, - { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, - { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, - { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, - { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, - { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, - { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, - { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, - { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, - { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, - { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, - { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, - { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, - { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, - { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, - { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, - { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, - { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, - { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, - { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, - { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, - { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, - { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, - { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, - { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, - { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, - { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, - { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, - { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, - { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, - { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, - { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, - { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, - { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, - { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, - { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, - { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, - { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, - { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, - { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, - { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, - { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, - { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, - { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, - { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, - { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, - { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, - { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, - { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, - { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, - { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, - { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, - { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, - { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, - { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, -}; - -/* - * Selection types' masks. - * Use the same masks as usual. - * Button1Mask is always unset, to make masks match between ButtonPress. - * ButtonRelease and MotionNotify. - * If no match is found, regular selection is used. - */ -static uint selmasks[] = { - [SelRectangular] = Mod1Mask, -}; - -/* - * Printable characters in ASCII, used to estimate the advance width - * of single wide characters. - */ -static char ascii_printable[] = - " !\"#$%&'()*+,-./0123456789:;<=>?" - "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/sys/cmd/term/hb.c b/sys/cmd/term/hb.c deleted file mode 100644 index 4b6b42d..0000000 --- a/sys/cmd/term/hb.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "term.h" - -#include -#include - -#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } - -hb_font_t *hbfindfont(XftFont *match); -void hbtransformsegment(XftFont *xfont, const Letter *glyph, rune *codepoints, int start, int end); - -typedef struct -{ - XftFont *match; - hb_font_t *font; -} HbFontMatch; - -static int hbfontslen = 0; -static HbFontMatch *hbfontcache = nil; - -/* - * Replace 0 with a list of font features, wrapped in FEATURE macro, e.g. - * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') - */ -hb_feature_t features[] = { 0 }; - -void -hbunloadfonts() -{ - int i; - for(i = 0; i < hbfontslen; i++) { - hb_font_destroy(hbfontcache[i].font); - XftUnlockFace(hbfontcache[i].match); - } - - if(hbfontcache != nil) { - free(hbfontcache); - hbfontcache = nil; - } - - hbfontslen = 0; -} - -hb_font_t * -hbfindfont(XftFont *match) -{ - int i; - for (i = 0; i < hbfontslen; i++) { - if (hbfontcache[i].match == match) - return hbfontcache[i].font; - } - - /* Font not found in cache, caching it now. */ - hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); - FT_Face face = XftLockFace(match); - hb_font_t *font = hb_ft_font_create(face, NULL); - if(!font) - fatal("failed to load Harfbuzz font."); - - hbfontcache[hbfontslen].match = match; - hbfontcache[hbfontslen].font = font; - hbfontslen += 1; - - return font; -} - -void -hbtransform(XftGlyphFontSpec *specs, const Letter *glyphs, size_t len, int x, int y) -{ - int idx, specidx, start = 0, length = 1, gstart = 0; - rune *runes = calloc((unsigned int)len, sizeof(hb_codepoint_t)); - - for(idx = 1, specidx = 1; idx < len; idx++) { - if(glyphs[idx].mode & Gwdummy) { - length += 1; - continue; - } - - if(specs[specidx].font != specs[start].font - || GLYPHCMP(glyphs[gstart], glyphs[idx]) - || selected(x + idx, y) != selected(x + gstart, y) - ) { - hbtransformsegment(specs[start].font, glyphs, runes, gstart, length); - /* reset the sequence. */ - length = 1; - start = specidx; - gstart = idx; - } else { - length += 1; - } - - specidx++; - } - - /* eol */ - hbtransformsegment(specs[start].font, glyphs, runes, gstart, length); - - /* apply the transformation to glyph specs. */ - for(idx = 0, specidx = 0; idx < len; idx++) { - if(glyphs[idx].mode & Gwdummy) - continue; - - if(runes[idx] != specs[specidx].glyph) - ((Letter *)glyphs)[idx].mode |= Gliga; - - specs[specidx++].glyph = runes[idx]; - } - - free(runes); -} - -void -hbtransformsegment(XftFont *xfont, const Letter *glyph, rune *codepoints, int start, int len) -{ - hb_font_t *font = hbfindfont(xfont); - if(!font) - return; - - int i; - rune r; - ushort mode = USHRT_MAX; - hb_buffer_t *buffer = hb_buffer_create(); - hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); - - /* Fill buffer with codepoints. */ - for(i=start; i < (start+len); i++) { - r = glyph[i].u; - mode = glyph[i].mode; - if(mode & Gwdummy) - r = 0x0020; - hb_buffer_add_codepoints(buffer, &r, 1, 0, 1); - } - - /* Shape the segment. */ - hb_shape(font, buffer, features, sizeof(features)); - - /* Get new glyph info. */ - hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); - - /* Write new codepoints. */ - for(i = 0; i < len; i++) { - r = info[i].codepoint; - codepoints[start+i] = r; - } - - /* Cleanup. */ - hb_buffer_destroy(buffer); -} diff --git a/sys/cmd/term/nonspacing.h b/sys/cmd/term/nonspacing.h deleted file mode 100644 index 5d05a3d..0000000 --- a/sys/cmd/term/nonspacing.h +++ /dev/null @@ -1,89 +0,0 @@ -16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35, -36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50, -51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66, -67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83, -84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,248,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,254,255,255,255,255,191,182,0,0,0,0,0,0,0,63,0,255,23,0,0,0,0,0,248,255, -255,0,0,1,0,0,0,0,0,0,0,0,0,0,0,192,191,159,61,0,0,0,128,2,0,0,0,255,255,255, -7,0,0,0,0,0,0,0,0,0,0,192,255,1,0,0,0,0,0,0,248,15,32,0,0,192,251,239,62,0,0, -0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255, -255,7,0,0,0,0,0,0,20,254,33,254,0,12,0,0,0,2,0,0,0,0,0,0,16,30,32,0,0,12,0,0, -64,6,0,0,0,0,0,0,16,134,57,2,0,0,0,35,0,6,0,0,0,0,0,0,16,190,33,0,0,12,0,0, -252,2,0,0,0,0,0,0,144,30,32,64,0,12,0,0,0,4,0,0,0,0,0,0,0,1,32,0,0,0,0,0,0,17, -0,0,0,0,0,0,192,193,61,96,0,12,0,0,0,2,0,0,0,0,0,0,144,64,48,0,0,12,0,0,0,3,0, -0,0,0,0,0,24,30,32,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,4,92,0,0,0,0,0,0,0,0,0,0,0, -242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160, -2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0, -0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0, -0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127, -229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0, -0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, -251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, -255,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,128,3,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0, -0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0, -0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0, -0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,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,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0, -0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255, -127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0, -0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192, -31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0, -0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,60,176,1,0,0,48,0,0,0, -0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0, -0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0, -126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0, -0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0, -0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,24, -0,0,0,0,0,0,0,255,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,31,0,0,0,0,0,0,0,127,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0, -0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248, -254,255,0,0,0,0,0,0,0,0,0, -0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/sys/cmd/term/rules.mk b/sys/cmd/term/rules.mk deleted file mode 100644 index 45c9eb2..0000000 --- a/sys/cmd/term/rules.mk +++ /dev/null @@ -1,24 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := $(d)/term.c $(d)/x.c #$(d)/hb.c -BINS_$(d) := $(d)/term - -include share/paths.mk - -# Local rules -include share/dynamic.mk -$(BINS_$(d)): TCFLAGS = \ - `$(PKG) --cflags fontconfig` \ - `$(PKG) --cflags freetype2` - -$(BINS_$(d)): TCLIBS = \ - `$(PKG) --libs fontconfig` \ - `$(PKG) --libs freetype2` \ - -lm -lrt -lX11 -lutil -lXft -lXrender #-lharfbuzz - -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/libutf/libutf.a $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/term/term.c b/sys/cmd/term/term.c deleted file mode 100644 index 50ab29c..0000000 --- a/sys/cmd/term/term.c +++ /dev/null @@ -1,2417 +0,0 @@ -/* See LICENSE for license details. */ -#include "term.h" - -#include -#include -#if defined(__linux) - #include -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) - #include -#elif defined(__FreeBSD__) || defined(__DragonFly__) - #include -#endif - -/* macros */ -#define IS_SET(flag) ((term.mode & (flag)) != 0) -#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) -#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) -#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) -#define ISDELIM(u) (u && wcschr(worddelimiters, u)) - -/* forward declare functions */ -static void execsh(char *, char **); -static void stty(char **); -static void sigchld(int); -static void ttywriteraw(char *, size_t); - -static void csidump(void); -static void csihandle(void); -static void csiparse(void); -static void csireset(void); -static int eschandle(uchar); -static void strdump(void); -static void strhandle(void); -static void strparse(void); -static void strreset(void); - -static void tprinter(char *, size_t); -static void tdumpsel(void); -static void tdumpline(int); -static void tdump(void); -static void tclearregion(int, int, int, int); -static void tcursor(int); -static void tdeletechar(int); -static void tdeleteline(int); -static void tinsertblank(int); -static void tinsertblankline(int); -static int tlinelen(int); -static void tmoveto(int, int); -static void tmoveato(int, int); -static void tnewline(int); -static void tputtab(int); -static void tputc(rune); -static void treset(void); -static void tscrollup(int, int); -static void tscrolldown(int, int); -static void tsetattr(int *, int); -static void tsetchar(rune, Letter *, int, int); -static void tsetdirt(int, int); -static void tsetscroll(int, int); -static void tswapscreen(void); -static void tsetmode(int, int, int *, int); -static int twrite(char *, int, int); -static void tfulldirt(void); -static void tcontrolcode(uchar ); -static void tdectest(char ); -static void tdefutf8(char); -static int32 tdefcolor(int *, int *, int); -static void tdeftran(char); -static void tstrsequence(uchar); - -static void drawregion(int, int, int, int); - -static void selnormalize(void); -static void selscroll(int, int); -static void selsnap(int *, int *, int); - -static char *base64dec(char *); -static char base64dec_getc(char **); - -static uintptr xwrite(int, char *, size_t); -extern int wcwidth(wchar_t wc); - -/* globals */ -static Terminal term; -static Selection sel; -static CSIEscape csiescseq; -static STREscape strescseq; -static int iofd = 1; -static int cmdfd; -static pid_t pid; - -/* functions */ -uintptr -xwrite(int fd, char *s, size_t len) -{ - size_t aux = len; - ssize_t r; - - while (len > 0) { - r = write(fd, s, len); - if (r < 0) - return r; - len -= r; - s += r; - } - - return aux; -} - -void * -xmalloc(size_t len) -{ - void *p; - - if (!(p = malloc(len))) - fatal("malloc: %s\n", strerror(errno)); - - return p; -} - -void * -xrealloc(void *p, size_t len) -{ - if ((p = realloc(p, len)) == nil) - fatal("realloc: %s\n", strerror(errno)); - - return p; -} - -char * -xstrdup(char *s) -{ - if ((s = strdup(s)) == nil) - fatal("strdup: %s\n", strerror(errno)); - - return s; -} - -static char base64_digits[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, -1, 0, 0, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -char -base64dec_getc(char **src) -{ - while (**src && !isprint(**src)) - (*src)++; - return **src ? *((*src)++) : '='; /* emulate padding if string ends */ -} - -char * -base64dec(char *src) -{ - size_t in_len = strlen(src); - char *result, *dst; - - if (in_len % 4) - in_len += 4 - (in_len % 4); - result = dst = xmalloc(in_len / 4 * 3 + 1); - while (*src) { - int a = base64_digits[(unsigned char) base64dec_getc(&src)]; - int b = base64_digits[(unsigned char) base64dec_getc(&src)]; - int c = base64_digits[(unsigned char) base64dec_getc(&src)]; - int d = base64_digits[(unsigned char) base64dec_getc(&src)]; - - /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ - if (a == -1 || b == -1) - break; - - *dst++ = (a << 2) | ((b & 0x30) >> 4); - if (c == -1) - break; - *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); - if (d == -1) - break; - *dst++ = ((c & 0x03) << 6) | d; - } - *dst = '\0'; - return result; -} - -void -selinit(void) -{ - sel.mode = SelIdle; - sel.snap = 0; - sel.ob.x = -1; -} - -int -tlinelen(int y) -{ - int i = term.col; - - if (term.line[y][i - 1].mode & Gwrap) - return i; - - while (i > 0 && term.line[y][i - 1].u == ' ') - --i; - - return i; -} - -void -selstart(int col, int row, int snap) -{ - selclear(); - sel.mode = SelEmpty; - sel.type = SelRegular; - sel.alt = IS_SET(Taltscreen); - sel.snap = snap; - sel.oe.x = sel.ob.x = col; - sel.oe.y = sel.ob.y = row; - selnormalize(); - - if (sel.snap != 0) - sel.mode = SelReady; - tsetdirt(sel.nb.y, sel.ne.y); -} - -void -selextend(int col, int row, int type, int done) -{ - int oldey, oldex, oldsby, oldsey, oldtype; - - if (sel.mode == SelIdle) - return; - if (done && sel.mode == SelEmpty) { - selclear(); - return; - } - - oldey = sel.oe.y; - oldex = sel.oe.x; - oldsby = sel.nb.y; - oldsey = sel.ne.y; - oldtype = sel.type; - - sel.oe.x = col; - sel.oe.y = row; - selnormalize(); - sel.type = type; - - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SelEmpty) - tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); - - sel.mode = done ? SelIdle : SelReady; -} - -void -selnormalize(void) -{ - int i; - - if (sel.type == SelRegular && sel.ob.y != sel.oe.y) { - sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; - sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; - } else { - sel.nb.x = MIN(sel.ob.x, sel.oe.x); - sel.ne.x = MAX(sel.ob.x, sel.oe.x); - } - sel.nb.y = MIN(sel.ob.y, sel.oe.y); - sel.ne.y = MAX(sel.ob.y, sel.oe.y); - - selsnap(&sel.nb.x, &sel.nb.y, -1); - selsnap(&sel.ne.x, &sel.ne.y, +1); - - /* expand selection over line breaks */ - if (sel.type == SelRectangular) - return; - i = tlinelen(sel.nb.y); - if (i < sel.nb.x) - sel.nb.x = i; - if (tlinelen(sel.ne.y) <= sel.ne.x) - sel.ne.x = term.col - 1; -} - -int -selected(int x, int y) -{ - if(sel.mode == SelEmpty || sel.ob.x == -1 || - sel.alt != IS_SET(Taltscreen)) - return 0; - - if(sel.type == SelRectangular) - return BETWEEN(y, sel.nb.y, sel.ne.y) - && BETWEEN(x, sel.nb.x, sel.ne.x); - - return BETWEEN(y, sel.nb.y, sel.ne.y) - && (y != sel.nb.y || x >= sel.nb.x) - && (y != sel.ne.y || x <= sel.ne.x); -} - -void -selsnap(int *x, int *y, int direction) -{ - int newx, newy, xt, yt; - int delim, prevdelim; - Letter *gp, *prevgp; - - switch (sel.snap) { - case SnapWord: - /* - * Snap around if the word wraps around at the end or - * beginning of a line. - */ - prevgp = &term.line[*y][*x]; - prevdelim = ISDELIM(prevgp->u); - for (;;) { - newx = *x + direction; - newy = *y; - if (!BETWEEN(newx, 0, term.col - 1)) { - newy += direction; - newx = (newx + term.col) % term.col; - if (!BETWEEN(newy, 0, term.row - 1)) - break; - - if (direction > 0) - yt = *y, xt = *x; - else - yt = newy, xt = newx; - if (!(term.line[yt][xt].mode & Gwrap)) - break; - } - - if (newx >= tlinelen(newy)) - break; - - gp = &term.line[newy][newx]; - delim = ISDELIM(gp->u); - if (!(gp->mode & Gwdummy) && (delim != prevdelim - || (delim && gp->u != prevgp->u))) - break; - - *x = newx; - *y = newy; - prevgp = gp; - prevdelim = delim; - } - break; - case SnapLine: - /* - * Snap around if the the previous line or the current one - * has set ATTR_WRAP at its end. Then the whole next or - * previous line will be selected. - */ - *x = (direction < 0) ? 0 : term.col - 1; - if (direction < 0) { - for (; *y > 0; *y += direction) { - if (!(term.line[*y-1][term.col-1].mode - & Gwrap)) { - break; - } - } - } else if (direction > 0) { - for (; *y < term.row-1; *y += direction) { - if (!(term.line[*y][term.col-1].mode - & Gwrap)) { - break; - } - } - } - break; - } -} - -char * -getsel(void) -{ - char *str, *ptr; - int y, bufsize, lastx, linelen; - Letter *gp, *last; - - if (sel.ob.x == -1) - return nil; - - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTFmax; - ptr = str = xmalloc(bufsize); - - /* append every set & selected glyph to the selection */ - for(y = sel.nb.y; y <= sel.ne.y; y++) { - if((linelen = tlinelen(y)) == 0) { - *ptr++ = '\n'; - continue; - } - - if(sel.type == SelRectangular) { - gp = &term.line[y][sel.nb.x]; - lastx = sel.ne.x; - }else{ - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; - lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; - } - last = &term.line[y][MIN(lastx, linelen-1)]; - while (last >= gp && last->u == ' ') - --last; - - for ( ; gp <= last; ++gp) { - if (gp->mode & Gwdummy) - continue; - - ptr += utf8·encode(&gp->u, ptr); - } - - /* - * Copy and pasting of line endings is inconsistent - * in the inconsistent terminal and GUI world. - * The best solution seems like to produce '\n' when - * something is copied from st and convert '\n' to - * '\r', when something to be pasted is received by - * st. - * FIXME: Fix the computer world. - */ - if ((y < sel.ne.y || lastx >= linelen) && - (!(last->mode & Gwrap) || sel.type == SelRectangular)) - *ptr++ = '\n'; - } - *ptr = 0; - return str; -} - -void -selclear(void) -{ - if (sel.ob.x == -1) - return; - sel.mode = SelIdle; - sel.ob.x = -1; - tsetdirt(sel.nb.y, sel.ne.y); -} - -void -fatal(char *errstr, ...) -{ - va_list ap; - - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(1); -} - -void -execsh(char *cmd, char **args) -{ - char *sh, *prog, *arg; - struct passwd *pw; - - errno = 0; - if ((pw = getpwuid(getuid())) == nil) { - if (errno) - fatal("getpwuid: %s\n", strerror(errno)); - else - fatal("who are you?\n"); - } - - if ((sh = getenv("SHELL")) == nil) - sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; - - if (args) { - prog = args[0]; - arg = nil; - } else if (scroll) { - prog = scroll; - arg = utmp ? utmp : sh; - } else if (utmp) { - prog = utmp; - arg = nil; - } else { - prog = sh; - arg = nil; - } - DEFAULT(args, ((char *[]) {prog, arg, nil})); - - unsetenv("COLUMNS"); - unsetenv("LINES"); - unsetenv("TERMCAP"); - setenv("LOGNAME", pw->pw_name, 1); - setenv("USER", pw->pw_name, 1); - setenv("SHELL", sh, 1); - setenv("HOME", pw->pw_dir, 1); - setenv("TERM", termname, 1); - - signal(SIGCHLD, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGALRM, SIG_DFL); - - execvp(prog, args); - _exit(1); -} - -void -sigchld(int a) -{ - int stat; - pid_t p; - - if ((p = waitpid(pid, &stat, WNOHANG)) < 0) - fatal("waiting for pid %hd failed: %s\n", pid, strerror(errno)); - - if (pid != p) - return; - - if (WIFEXITED(stat) && WEXITSTATUS(stat)) - fatal("child exited with status %d\n", WEXITSTATUS(stat)); - else if (WIFSIGNALED(stat)) - fatal("child terminated due to signal %d\n", WTERMSIG(stat)); - _exit(0); -} - -void -stty(char **args) -{ - char cmd[_POSIX_ARG_MAX], **p, *q, *s; - size_t n, siz; - - if ((n = strlen(stty_args)) > sizeof(cmd)-1) - fatal("incorrect stty parameters\n"); - memcpy(cmd, stty_args, n); - q = cmd + n; - siz = sizeof(cmd) - n; - for (p = args; p && (s = *p); ++p) { - if ((n = strlen(s)) > siz-1) - fatal("stty parameter length too long\n"); - *q++ = ' '; - memcpy(q, s, n); - q += n; - siz -= n + 1; - } - *q = '\0'; - if (system(cmd) != 0) - perror("Couldn't call stty"); -} - -int -ttynew(char *line, char *cmd, char *out, char **args) -{ - int m, s; - - if (out) { - term.mode |= Tprint; - iofd = (!strcmp(out, "-")) ? - 1 : open(out, O_WRONLY | O_CREAT, 0666); - if (iofd < 0) { - fprintf(stderr, "Error opening %s:%s\n", - out, strerror(errno)); - } - } - - if (line) { - if ((cmdfd = open(line, O_RDWR)) < 0) - fatal("open line '%s' failed: %s\n", - line, strerror(errno)); - dup2(cmdfd, 0); - stty(args); - return cmdfd; - } - - /* seems to work fine on linux, openbsd and freebsd */ - if (openpty(&m, &s, nil, nil, nil) < 0) - fatal("openpty failed: %s\n", strerror(errno)); - - switch (pid = fork()) { - case -1: - fatal("fork failed: %s\n", strerror(errno)); - break; - case 0: - close(iofd); - setsid(); /* create a new process group */ - dup2(s, 0); - dup2(s, 1); - dup2(s, 2); - if (ioctl(s, TIOCSCTTY, nil) < 0) - fatal("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); - close(s); - close(m); -#ifdef __OpenBSD__ - if (pledge("stdio getpw proc exec", nil) == -1) - fatal("pledge\n"); -#endif - execsh(cmd, args); - break; - default: -#ifdef __OpenBSD__ - if (pledge("stdio rpath tty proc", nil) == -1) - fatal("pledge\n"); -#endif - close(s); - cmdfd = m; - signal(SIGCHLD, sigchld); - break; - } - return cmdfd; -} - -size_t -ttyread(void) -{ - static char buf[BUFSIZ]; - static int buflen = 0; - int ret, written; - - /* append read bytes to unprocessed bytes */ - ret = read(cmdfd, buf+buflen, arrlen(buf)-buflen); - - switch (ret) { - case 0: - exit(0); - case -1: - fatal("couldn't read from shell: %s\n", strerror(errno)); - default: - buflen += ret; - written = twrite(buf, buflen, 0); - buflen -= written; - /* keep any incomplete UTF-8 byte sequence for the next call */ - if(buflen > 0) - memmove(buf, buf + written, buflen); - return ret; - } -} - -void -ttywrite(char *s, size_t n, int may_echo) -{ - char *next; - - if (may_echo && IS_SET(Techo)) - twrite(s, n, 1); - - if (!IS_SET(Tcrlf)) { - ttywriteraw(s, n); - return; - } - - /* This is similar to how the kernel handles ONLCR for ttys */ - while (n > 0) { - if (*s == '\r') { - next = s + 1; - ttywriteraw("\r\n", 2); - } else { - next = memchr(s, '\r', n); - DEFAULT(next, s + n); - ttywriteraw(s, next - s); - } - n -= next - s; - s = next; - } -} - -void -ttywriteraw(char *s, size_t n) -{ - fd_set wfd, rfd; - ssize_t r; - size_t lim = 256; - - /* - * Remember that we are using a pty, which might be a modem line. - * Writing too much will clog the line. That's why we are doing this - * dance. - * FIXME: Migrate the world to Plan 9. - */ - while (n > 0) { - FD_ZERO(&wfd); - FD_ZERO(&rfd); - FD_SET(cmdfd, &wfd); - FD_SET(cmdfd, &rfd); - - /* Check if we can write. */ - if (pselect(cmdfd+1, &rfd, &wfd, nil, nil, nil) < 0) { - if (errno == EINTR) - continue; - fatal("select failed: %s\n", strerror(errno)); - } - if (FD_ISSET(cmdfd, &wfd)) { - /* - * Only write the bytes written by ttywrite() or the - * default of 256. This seems to be a reasonable value - * for a serial line. Bigger values might clog the I/O. - */ - if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) - goto write_error; - if (r < n) { - /* - * We weren't able to write out everything. - * This means the buffer is getting full - * again. Empty it. - */ - if (n < lim) - lim = ttyread(); - n -= r; - s += r; - } else { - /* All bytes have been written. */ - break; - } - } - if (FD_ISSET(cmdfd, &rfd)) - lim = ttyread(); - } - return; - -write_error: - fatal("write error on tty: %s\n", strerror(errno)); -} - -void -ttyresize(int tw, int th) -{ - struct winsize w; - - w.ws_row = term.row; - w.ws_col = term.col; - w.ws_xpixel = tw; - w.ws_ypixel = th; - if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) - fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); -} - -void -ttyhangup() -{ - /* Send SIGHUP to shell */ - kill(pid, SIGHUP); -} - -int -tattrset(int attr) -{ - int i, j; - - for (i = 0; i < term.row-1; i++) { - for (j = 0; j < term.col-1; j++) { - if (term.line[i][j].mode & attr) - return 1; - } - } - - return 0; -} - -void -tsetdirt(int top, int bot) -{ - int i; - - LIMIT(top, 0, term.row-1); - LIMIT(bot, 0, term.row-1); - - for (i = top; i <= bot; i++) - term.dirty[i] = 1; -} - -void -tsetdirtattr(int attr) -{ - int i, j; - - for (i = 0; i < term.row-1; i++) { - for (j = 0; j < term.col-1; j++) { - if (term.line[i][j].mode & attr) { - tsetdirt(i, i); - break; - } - } - } -} - -void -tfulldirt(void) -{ - tsetdirt(0, term.row-1); -} - -void -tcursor(int mode) -{ - static Dot c[2]; - int alt = IS_SET(Taltscreen); - - if (mode == CursorSave) { - c[alt] = term.c; - } else if (mode == CursorLoad) { - term.c = c[alt]; - tmoveto(c[alt].x, c[alt].y); - } -} - -void -treset(void) -{ - uint i; - - term.c = (Dot){{ - .mode = Gnil, - .fg = defaultfg, - .bg = defaultbg - }, .x = 0, .y = 0, .state = CursorDefault}; - - memset(term.tabs, 0, term.col * sizeof(*term.tabs)); - for (i = tabspaces; i < term.col; i += tabspaces) - term.tabs[i] = 1; - term.top = 0; - term.bot = term.row - 1; - term.mode = Twrap|Tutf8; - memset(term.trantbl, CSusa, sizeof(term.trantbl)); - term.charset = 0; - - for (i = 0; i < 2; i++) { - tmoveto(0, 0); - tcursor(CursorSave); - tclearregion(0, 0, term.col-1, term.row-1); - tswapscreen(); - } -} - -void -tnew(int col, int row) -{ - term = (Terminal){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; - tresize(col, row); - treset(); -} - -void -tswapscreen(void) -{ - Letter **tmp = term.line; - - term.line = term.alt; - term.alt = tmp; - term.mode ^= Taltscreen; - tfulldirt(); -} - -void -tscrolldown(int orig, int n) -{ - int i; - Letter *temp; - - LIMIT(n, 0, term.bot-orig+1); - - tsetdirt(orig, term.bot-n); - tclearregion(0, term.bot-n+1, term.col-1, term.bot); - - for (i = term.bot; i >= orig+n; i--) { - temp = term.line[i]; - term.line[i] = term.line[i-n]; - term.line[i-n] = temp; - } - - selscroll(orig, n); -} - -void -tscrollup(int orig, int n) -{ - int i; - Letter *temp; - - LIMIT(n, 0, term.bot-orig+1); - - tclearregion(0, orig, term.col-1, orig+n-1); - tsetdirt(orig+n, term.bot); - - for (i = orig; i <= term.bot-n; i++) { - temp = term.line[i]; - term.line[i] = term.line[i+n]; - term.line[i+n] = temp; - } - - selscroll(orig, -n); -} - -void -selscroll(int orig, int n) -{ - if (sel.ob.x == -1) - return; - - if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { - selclear(); - } else if (BETWEEN(sel.nb.y, orig, term.bot)) { - sel.ob.y += n; - sel.oe.y += n; - if (sel.ob.y < term.top || sel.ob.y > term.bot || - sel.oe.y < term.top || sel.oe.y > term.bot) { - selclear(); - } else { - selnormalize(); - } - } -} - -void -tnewline(int first_col) -{ - int y = term.c.y; - - if (y == term.bot) { - tscrollup(term.top, 1); - } else { - y++; - } - tmoveto(first_col ? 0 : term.c.x, y); -} - -void -csiparse(void) -{ - char *p = csiescseq.buf, *np; - long int v; - - csiescseq.narg = 0; - if (*p == '?') { - csiescseq.priv = 1; - p++; - } - - csiescseq.buf[csiescseq.len] = '\0'; - while (p < csiescseq.buf+csiescseq.len) { - np = nil; - v = strtol(p, &np, 10); - if (np == p) - v = 0; - if (v == LONG_MAX || v == LONG_MIN) - v = -1; - csiescseq.arg[csiescseq.narg++] = v; - p = np; - if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) - break; - p++; - } - csiescseq.mode[0] = *p++; - csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; -} - -/* for absolute user moves, when decom is set */ -void -tmoveato(int x, int y) -{ - tmoveto(x, y + ((term.c.state & CursorOrigin) ? term.top: 0)); -} - -void -tmoveto(int x, int y) -{ - int miny, maxy; - - if (term.c.state & CursorOrigin) { - miny = term.top; - maxy = term.bot; - } else { - miny = 0; - maxy = term.row - 1; - } - term.c.state &= ~CursorWrap; - term.c.x = LIMIT(x, 0, term.col-1); - term.c.y = LIMIT(y, miny, maxy); -} - -void -tsetchar(rune u, Letter *attr, int x, int y) -{ - static char *vt100_0[62] = { /* 0x41 - 0x7e */ - "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ - 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ - 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ - 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ - "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ - "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ - "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ - "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ - }; - - /* - * table is proudly stolen from rxvt. - */ - if (term.trantbl[term.charset] == CSgfx0 && - BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) - utf8·decode(vt100_0[u - 0x41], &u); - - if (term.line[y][x].mode & Gwide) { - if (x+1 < term.col) { - term.line[y][x+1].u = ' '; - term.line[y][x+1].mode &= ~Gwdummy; - } - } else if (term.line[y][x].mode & Gwdummy) { - term.line[y][x-1].u = ' '; - term.line[y][x-1].mode &= ~Gwide; - } - - term.dirty[y] = 1; - term.line[y][x] = *attr; - term.line[y][x].u = u; -} - -void -tclearregion(int x1, int y1, int x2, int y2) -{ - int x, y, temp; - Letter *gp; - - if(x1 > x2) - temp = x1, x1 = x2, x2 = temp; - if(y1 > y2) - temp = y1, y1 = y2, y2 = temp; - - LIMIT(x1, 0, term.col-1); - LIMIT(x2, 0, term.col-1); - LIMIT(y1, 0, term.row-1); - LIMIT(y2, 0, term.row-1); - - for(y = y1; y <= y2; y++) { - term.dirty[y] = 1; - for(x = x1; x <= x2; x++) { - gp = &term.line[y][x]; - if(selected(x, y)) - selclear(); - gp->fg = term.c.attr.fg; - gp->bg = term.c.attr.bg; - gp->mode = 0; - gp->u = ' '; - } - } -} - -void -tdeletechar(int n) -{ - int dst, src, size; - Letter *line; - - LIMIT(n, 0, term.col - term.c.x); - - dst = term.c.x; - src = term.c.x + n; - size = term.col - src; - line = term.line[term.c.y]; - - memmove(&line[dst], &line[src], size * sizeof(Letter)); - tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); -} - -void -tinsertblank(int n) -{ - int dst, src, size; - Letter *line; - - LIMIT(n, 0, term.col - term.c.x); - - dst = term.c.x + n; - src = term.c.x; - size = term.col - dst; - line = term.line[term.c.y]; - - memmove(&line[dst], &line[src], size * sizeof(Letter)); - tclearregion(src, term.c.y, dst - 1, term.c.y); -} - -void -tinsertblankline(int n) -{ - if (BETWEEN(term.c.y, term.top, term.bot)) - tscrolldown(term.c.y, n); -} - -void -tdeleteline(int n) -{ - if (BETWEEN(term.c.y, term.top, term.bot)) - tscrollup(term.c.y, n); -} - -int32_t -tdefcolor(int *attr, int *npar, int l) -{ - int32_t idx = -1; - uint r, g, b; - - switch (attr[*npar + 1]) { - case 2: /* direct color in RGB space */ - if (*npar + 4 >= l) { - fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar); - break; - } - r = attr[*npar + 2]; - g = attr[*npar + 3]; - b = attr[*npar + 4]; - *npar += 4; - if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) - fprintf(stderr, "erresc(38): bad rgb color (%u,%u,%u)\n", r, g, b); - else - idx = TRUECOLOR(r, g, b); - break; - case 5: /* indexed color */ - if (*npar + 2 >= l) { - fprintf(stderr, "erresc(38): Incorrect number of parameters (%d)\n", *npar); - break; - } - *npar += 2; - if (!BETWEEN(attr[*npar], 0, 255)) - fprintf(stderr, "erresc: bad color %d\n", attr[*npar]); - else - idx = attr[*npar]; - break; - case 0: /* implemented defined (only foreground) */ - case 1: /* transparent */ - case 3: /* direct color in CMY space */ - case 4: /* direct color in CMYK space */ - default: - fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[*npar]); - break; - } - - return idx; -} - -void -tsetattr(int *attr, int l) -{ - int i; - int32_t idx; - - for (i = 0; i < l; i++) { - switch (attr[i]) { - case 0: - term.c.attr.mode &= ~( - Gbold | - Gfaint | - Gitalic | - Gunline | - Gblink | - Greverse | - Ginvisible | - Gstruck ); - term.c.attr.fg = defaultfg; - term.c.attr.bg = defaultbg; - break; - case 1: - term.c.attr.mode |= Gbold; - break; - case 2: - term.c.attr.mode |= Gfaint; - break; - case 3: - term.c.attr.mode |= Gitalic; - break; - case 4: - term.c.attr.mode |= Gunline; - break; - case 5: /* slow blink */ - /* FALLTHROUGH */ - case 6: /* rapid blink */ - term.c.attr.mode |= Gblink; - break; - case 7: - term.c.attr.mode |= Greverse; - break; - case 8: - term.c.attr.mode |= Ginvisible; - break; - case 9: - term.c.attr.mode |= Gstruck; - break; - case 22: - term.c.attr.mode &= ~(Gbold | Gfaint); - break; - case 23: - term.c.attr.mode &= ~Gitalic; - break; - case 24: - term.c.attr.mode &= ~Gunline; - break; - case 25: - term.c.attr.mode &= ~Gblink; - break; - case 27: - term.c.attr.mode &= ~Greverse; - break; - case 28: - term.c.attr.mode &= ~Ginvisible; - break; - case 29: - term.c.attr.mode &= ~Gstruck; - break; - case 38: - if ((idx = tdefcolor(attr, &i, l)) >= 0) - term.c.attr.fg = idx; - break; - case 39: - term.c.attr.fg = defaultfg; - break; - case 48: - if ((idx = tdefcolor(attr, &i, l)) >= 0) - term.c.attr.bg = idx; - break; - case 49: - term.c.attr.bg = defaultbg; - break; - default: - if (BETWEEN(attr[i], 30, 37)) { - term.c.attr.fg = attr[i] - 30; - } else if (BETWEEN(attr[i], 40, 47)) { - term.c.attr.bg = attr[i] - 40; - } else if (BETWEEN(attr[i], 90, 97)) { - term.c.attr.fg = attr[i] - 90 + 8; - } else if (BETWEEN(attr[i], 100, 107)) { - term.c.attr.bg = attr[i] - 100 + 8; - } else { - fprintf(stderr, - "erresc(default): gfx attr %d unknown\n", - attr[i]); - csidump(); - } - break; - } - } -} - -void -tsetscroll(int t, int b) -{ - int temp; - - LIMIT(t, 0, term.row-1); - LIMIT(b, 0, term.row-1); - if (t > b) { - temp = t; - t = b; - b = temp; - } - term.top = t; - term.bot = b; -} - -void -tsetmode(int priv, int set, int *args, int narg) -{ - int alt, *lim; - - for (lim = args + narg; args < lim; ++args) { - if (priv) { - switch (*args) { - case 1: /* DECCKM -- Cursor key */ - xsetmode(set, Wappcursor); - break; - case 5: /* DECSCNM -- Reverse video */ - xsetmode(set, Wreverse); - break; - case 6: /* DECOM -- Origin */ - MODBIT(term.c.state, set, CursorOrigin); - tmoveato(0, 0); - break; - case 7: /* DECAWM -- Auto wrap */ - MODBIT(term.mode, set, Twrap); - break; - case 0: /* Error (IGNORED) */ - case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ - case 3: /* DECCOLM -- Column (IGNORED) */ - case 4: /* DECSCLM -- Scroll (IGNORED) */ - case 8: /* DECARM -- Auto repeat (IGNORED) */ - case 18: /* DECPFF -- Printer feed (IGNORED) */ - case 19: /* DECPEX -- Printer extent (IGNORED) */ - case 42: /* DECNRCM -- National characters (IGNORED) */ - case 12: /* att610 -- Start blinking cursor (IGNORED) */ - break; - case 25: /* DECTCEM -- Text Cursor Enable Mode */ - xsetmode(!set, Whide); - break; - case 9: /* X10 mouse compatibility mode */ - xsetpointermotion(0); - xsetmode(0, Wmouse); - xsetmode(set, Wmousex10); - break; - case 1000: /* 1000: report button press */ - xsetpointermotion(0); - xsetmode(0, Wmouse); - xsetmode(set, Wmousebtn); - break; - case 1002: /* 1002: report motion on button press */ - xsetpointermotion(0); - xsetmode(0, Wmouse); - xsetmode(set, Wmousemotion); - break; - case 1003: /* 1003: enable all mouse motions */ - xsetpointermotion(set); - xsetmode(0, Wmouse); - xsetmode(set, Wmousemany); - break; - case 1004: /* 1004: send focus events to tty */ - xsetmode(set, Wfocus); - break; - case 1006: /* 1006: extended reporting mode */ - xsetmode(set, Wmousesgr); - break; - case 1034: - xsetmode(set, W8bit); - break; - case 1049: /* swap screen & set/restore cursor as xterm */ - if (!allowaltscreen) - break; - tcursor((set) ? CursorSave : CursorLoad); - /* FALLTHROUGH */ - case 47: /* swap screen */ - case 1047: - if (!allowaltscreen) - break; - alt = IS_SET(Taltscreen); - if (alt) { - tclearregion(0, 0, term.col-1, - term.row-1); - } - if (set ^ alt) /* set is always 1 or 0 */ - tswapscreen(); - if (*args != 1049) - break; - /* FALLTHROUGH */ - case 1048: - tcursor((set) ? CursorSave : CursorLoad); - break; - case 2004: /* 2004: bracketed paste mode */ - xsetmode(set, Wbrcktpaste); - break; - /* Not implemented mouse modes. See comments there. */ - case 1001: /* mouse highlight mode; can hang the - terminal by design when implemented. */ - case 1005: /* UTF-8 mouse mode; will confuse - applications not supporting UTF-8 - and luit. */ - case 1015: /* urxvt mangled mouse mode; incompatible - and can be mistaken for other control - codes. */ - break; - default: - fprintf(stderr, - "erresc: unknown private set/reset mode %d\n", - *args); - break; - } - } else { - switch (*args) { - case 0: /* Error (IGNORED) */ - break; - case 2: - xsetmode(set, Wkbdblock); - break; - case 4: /* IRM -- Insertion-replacement */ - MODBIT(term.mode, set, Tinsert); - break; - case 12: /* SRM -- Send/Receive */ - MODBIT(term.mode, !set, Techo); - break; - case 20: /* LNM -- Linefeed/new line */ - MODBIT(term.mode, set, Tcrlf); - break; - default: - fprintf(stderr, - "erresc: unknown set/reset mode %d\n", - *args); - break; - } - } - } -} - -void -csihandle(void) -{ - char buf[40]; - int len; - - switch (csiescseq.mode[0]) { - default: - unknown: - fprintf(stderr, "erresc: unknown csi "); - csidump(); - /* fatal(""); */ - break; - case '@': /* ICH -- Insert blank char */ - DEFAULT(csiescseq.arg[0], 1); - tinsertblank(csiescseq.arg[0]); - break; - case 'A': /* CUU -- Cursor Up */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); - break; - case 'B': /* CUD -- Cursor Down */ - case 'e': /* VPR --Cursor Down */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); - break; - case 'i': /* MC -- Media Copy */ - switch (csiescseq.arg[0]) { - case 0: - tdump(); - break; - case 1: - tdumpline(term.c.y); - break; - case 2: - tdumpsel(); - break; - case 4: - term.mode &= ~Tprint; - break; - case 5: - term.mode |= Tprint; - break; - } - break; - case 'c': /* DA -- Device Attributes */ - if (csiescseq.arg[0] == 0) - ttywrite(vtiden, strlen(vtiden), 0); - break; - case 'b': /* REP -- if last char is printable print it more times */ - DEFAULT(csiescseq.arg[0], 1); - if (term.lastc) - while (csiescseq.arg[0]-- > 0) - tputc(term.lastc); - break; - case 'C': /* CUF -- Cursor Forward */ - case 'a': /* HPR -- Cursor Forward */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(term.c.x+csiescseq.arg[0], term.c.y); - break; - case 'D': /* CUB -- Cursor Backward */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(term.c.x-csiescseq.arg[0], term.c.y); - break; - case 'E': /* CNL -- Cursor Down and first col */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(0, term.c.y+csiescseq.arg[0]); - break; - case 'F': /* CPL -- Cursor Up and first col */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(0, term.c.y-csiescseq.arg[0]); - break; - case 'g': /* TBC -- Tabulation clear */ - switch (csiescseq.arg[0]) { - case 0: /* clear current tab stop */ - term.tabs[term.c.x] = 0; - break; - case 3: /* clear all the tabs */ - memset(term.tabs, 0, term.col * sizeof(*term.tabs)); - break; - default: - goto unknown; - } - break; - case 'G': /* CHA -- Move to */ - case '`': /* HPA */ - DEFAULT(csiescseq.arg[0], 1); - tmoveto(csiescseq.arg[0]-1, term.c.y); - break; - case 'H': /* CUP -- Move to */ - case 'f': /* HVP */ - DEFAULT(csiescseq.arg[0], 1); - DEFAULT(csiescseq.arg[1], 1); - tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); - break; - case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ - DEFAULT(csiescseq.arg[0], 1); - tputtab(csiescseq.arg[0]); - break; - case 'J': /* ED -- Clear screen */ - switch (csiescseq.arg[0]) { - case 0: /* below */ - tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); - if (term.c.y < term.row-1) { - tclearregion(0, term.c.y+1, term.col-1, - term.row-1); - } - break; - case 1: /* above */ - if (term.c.y > 1) - tclearregion(0, 0, term.col-1, term.c.y-1); - tclearregion(0, term.c.y, term.c.x, term.c.y); - break; - case 2: /* all */ - tclearregion(0, 0, term.col-1, term.row-1); - break; - default: - goto unknown; - } - break; - case 'K': /* EL -- Clear line */ - switch (csiescseq.arg[0]) { - case 0: /* right */ - tclearregion(term.c.x, term.c.y, term.col-1, - term.c.y); - break; - case 1: /* left */ - tclearregion(0, term.c.y, term.c.x, term.c.y); - break; - case 2: /* all */ - tclearregion(0, term.c.y, term.col-1, term.c.y); - break; - } - break; - case 'S': /* SU -- Scroll line up */ - DEFAULT(csiescseq.arg[0], 1); - tscrollup(term.top, csiescseq.arg[0]); - break; - case 'T': /* SD -- Scroll line down */ - DEFAULT(csiescseq.arg[0], 1); - tscrolldown(term.top, csiescseq.arg[0]); - break; - case 'L': /* IL -- Insert blank lines */ - DEFAULT(csiescseq.arg[0], 1); - tinsertblankline(csiescseq.arg[0]); - break; - case 'l': /* RM -- Reset Mode */ - tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); - break; - case 'M': /* DL -- Delete lines */ - DEFAULT(csiescseq.arg[0], 1); - tdeleteline(csiescseq.arg[0]); - break; - case 'X': /* ECH -- Erase char */ - DEFAULT(csiescseq.arg[0], 1); - tclearregion(term.c.x, term.c.y, - term.c.x + csiescseq.arg[0] - 1, term.c.y); - break; - case 'P': /* DCH -- Delete char */ - DEFAULT(csiescseq.arg[0], 1); - tdeletechar(csiescseq.arg[0]); - break; - case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ - DEFAULT(csiescseq.arg[0], 1); - tputtab(-csiescseq.arg[0]); - break; - case 'd': /* VPA -- Move to */ - DEFAULT(csiescseq.arg[0], 1); - tmoveato(term.c.x, csiescseq.arg[0]-1); - break; - case 'h': /* SM -- Set terminal mode */ - tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); - break; - case 'm': /* SGR -- Terminal attribute (color) */ - tsetattr(csiescseq.arg, csiescseq.narg); - break; - case 'n': /* DSR – Device Status Report (cursor position) */ - if (csiescseq.arg[0] == 6) { - len = snprintf(buf, sizeof(buf), "\033[%i;%iR", term.c.y+1, term.c.x+1); - ttywrite(buf, len, 0); - } - break; - case 'r': /* DECSTBM -- Set Scrolling Region */ - if (csiescseq.priv) { - goto unknown; - } else { - DEFAULT(csiescseq.arg[0], 1); - DEFAULT(csiescseq.arg[1], term.row); - tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); - tmoveato(0, 0); - } - break; - case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ - tcursor(CursorSave); - break; - case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ - tcursor(CursorLoad); - break; - case ' ': - switch (csiescseq.mode[1]) { - case 'q': /* DECSCUSR -- Set Cursor Style */ - if (xsetcursor(csiescseq.arg[0])) - goto unknown; - break; - default: - goto unknown; - } - break; - } -} - -void -csidump(void) -{ - size_t i; - uint c; - - fprintf(stderr, "ESC["); - for (i = 0; i < csiescseq.len; i++) { - c = csiescseq.buf[i] & 0xff; - if (isprint(c)) { - putc(c, stderr); - } else if (c == '\n') { - fprintf(stderr, "(\\n)"); - } else if (c == '\r') { - fprintf(stderr, "(\\r)"); - } else if (c == 0x1b) { - fprintf(stderr, "(\\e)"); - } else { - fprintf(stderr, "(%02x)", c); - } - } - putc('\n', stderr); -} - -void -csireset(void) -{ - memset(&csiescseq, 0, sizeof(csiescseq)); -} - -void -strhandle(void) -{ - char *p = nil, *dec; - int j, narg, par; - - term.esc &= ~(Xstrend|Xstr); - strparse(); - par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; - - switch (strescseq.type) { - case ']': /* OSC -- Operating System Command */ - switch (par) { - case 0: - case 1: - case 2: - if (narg > 1) - xsettitle(strescseq.args[1]); - return; - case 52: - if (narg > 2 && allowwindowops) { - dec = base64dec(strescseq.args[2]); - if (dec) { - xsetsel(dec); - xclipcopy(); - } else { - fprintf(stderr, "erresc: invalid base64\n"); - } - } - return; - case 4: /* color set */ - if (narg < 3) - break; - p = strescseq.args[2]; - /* FALLTHROUGH */ - case 104: /* color reset, here p = nil */ - j = (narg > 1) ? atoi(strescseq.args[1]) : -1; - if (xsetcolorname(j, p)) { - if (par == 104 && narg <= 1) - return; /* color reset without parameter */ - fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", - j, p ? p : "(null)"); - } else { - /* - * TODO if defaultbg color is changed, borders - * are dirty - */ - redraw(); - } - return; - } - break; - case 'k': /* old title set compatibility */ - xsettitle(strescseq.args[0]); - return; - case 'P': /* DCS -- Device Control String */ - term.mode |= Xdcs; - case '_': /* APC -- Application Program Command */ - case '^': /* PM -- Privacy Message */ - return; - } - - fprintf(stderr, "erresc: unknown str "); - strdump(); -} - -void -strparse(void) -{ - int c; - char *p = strescseq.buf; - - strescseq.narg = 0; - strescseq.buf[strescseq.len] = '\0'; - - if (*p == '\0') - return; - - while (strescseq.narg < STR_ARG_SIZ) { - strescseq.args[strescseq.narg++] = p; - while ((c = *p) != ';' && c != '\0') - ++p; - if (c == '\0') - return; - *p++ = '\0'; - } -} - -void -strdump(void) -{ - size_t i; - uint c; - - fprintf(stderr, "ESC%c", strescseq.type); - for (i = 0; i < strescseq.len; i++) { - c = strescseq.buf[i] & 0xff; - if (c == '\0') { - putc('\n', stderr); - return; - } else if (isprint(c)) { - putc(c, stderr); - } else if (c == '\n') { - fprintf(stderr, "(\\n)"); - } else if (c == '\r') { - fprintf(stderr, "(\\r)"); - } else if (c == 0x1b) { - fprintf(stderr, "(\\e)"); - } else { - fprintf(stderr, "(%02x)", c); - } - } - fprintf(stderr, "ESC\\\n"); -} - -void -strreset(void) -{ - strescseq = (STREscape){ - .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), - .siz = STR_BUF_SIZ, - }; -} - -void -sendbreak(Arg *arg) -{ - if (tcsendbreak(cmdfd, 0)) - perror("Error sending break"); -} - -void -tprinter(char *s, size_t len) -{ - if (iofd != -1 && xwrite(iofd, s, len) < 0) { - perror("Error writing to output file"); - close(iofd); - iofd = -1; - } -} - -void -toggleprinter(Arg *arg) -{ - term.mode ^= Tprint; -} - -void -printscreen(Arg *arg) -{ - tdump(); -} - -void -printsel(Arg *arg) -{ - tdumpsel(); -} - -void -tdumpsel(void) -{ - char *ptr; - - if ((ptr = getsel())) { - tprinter(ptr, strlen(ptr)); - free(ptr); - } -} - -void -tdumpline(int n) -{ - char buf[UTFmax]; - Letter *bp, *end; - - bp = &term.line[n][0]; - end = &bp[MIN(tlinelen(n), term.col) - 1]; - if (bp != end || bp->u != ' ') { - for ( ; bp <= end; ++bp) - tprinter(buf, utf8·encode(&bp->u, buf)); - } - tprinter("\n", 1); -} - -void -tdump(void) -{ - int i; - - for (i = 0; i < term.row; ++i) - tdumpline(i); -} - -void -tputtab(int n) -{ - uint x = term.c.x; - - if (n > 0) { - while (x < term.col && n--) - for (++x; x < term.col && !term.tabs[x]; ++x) - /* nothing */ ; - } else if (n < 0) { - while (x > 0 && n++) - for (--x; x > 0 && !term.tabs[x]; --x) - /* nothing */ ; - } - term.c.x = LIMIT(x, 0, term.col-1); -} - -void -tdefutf8(char ascii) -{ - if (ascii == 'G') - term.mode |= Tutf8; - else if (ascii == '@') - term.mode &= ~Tutf8; -} - -void -tdeftran(char ascii) -{ - static char cs[] = "0B"; - static int vcs[] = {CSgfx0, CSusa}; - char *p; - - if ((p = strchr(cs, ascii)) == nil) { - fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); - } else { - term.trantbl[term.icharset] = vcs[p - cs]; - } -} - -void -tdectest(char c) -{ - int x, y; - - if (c == '8') { /* DEC screen alignment test. */ - for (x = 0; x < term.col; ++x) { - for (y = 0; y < term.row; ++y) - tsetchar('E', &term.c.attr, x, y); - } - } -} - -void -tstrsequence(uchar c) -{ - strreset(); - - switch (c) { - case 0x90: /* DCS -- Device Control String */ - c = 'P'; - term.esc |= Xdcs; - break; - case 0x9f: /* APC -- Application Program Command */ - c = '_'; - break; - case 0x9e: /* PM -- Privacy Message */ - c = '^'; - break; - case 0x9d: /* OSC -- Operating System Command */ - c = ']'; - break; - } - strescseq.type = c; - term.esc |= Xstr; -} - -void -tcontrolcode(uchar ascii) -{ - switch (ascii) { - case '\t': /* HT */ - tputtab(1); - return; - case '\b': /* BS */ - tmoveto(term.c.x-1, term.c.y); - return; - case '\r': /* CR */ - tmoveto(0, term.c.y); - return; - case '\f': /* LF */ - case '\v': /* VT */ - case '\n': /* LF */ - /* go to first col if the mode is set */ - tnewline(IS_SET(Tcrlf)); - return; - case '\a': /* BEL */ - if (term.esc & Xstrend) { - /* backwards compatibility to xterm */ - strhandle(); - } else { - xbell(); - } - break; - case '\033': /* ESC */ - csireset(); - term.esc &= ~(Xcsi|Xaltcs|Xtest); - term.esc |= Xstart; - return; - case '\016': /* SO (LS1 -- Locking shift 1) */ - case '\017': /* SI (LS0 -- Locking shift 0) */ - term.charset = 1 - (ascii - '\016'); - return; - case '\032': /* SUB */ - tsetchar('?', &term.c.attr, term.c.x, term.c.y); - /* FALLTHROUGH */ - case '\030': /* CAN */ - csireset(); - break; - case '\005': /* ENQ (IGNORED) */ - case '\000': /* NUL (IGNORED) */ - case '\021': /* XON (IGNORED) */ - case '\023': /* XOFF (IGNORED) */ - case 0177: /* DEL (IGNORED) */ - return; - case 0x80: /* TODO: PAD */ - case 0x81: /* TODO: HOP */ - case 0x82: /* TODO: BPH */ - case 0x83: /* TODO: NBH */ - case 0x84: /* TODO: IND */ - break; - case 0x85: /* NEL -- Next line */ - tnewline(1); /* always go to first col */ - break; - case 0x86: /* TODO: SSA */ - case 0x87: /* TODO: ESA */ - break; - case 0x88: /* HTS -- Horizontal tab stop */ - term.tabs[term.c.x] = 1; - break; - case 0x89: /* TODO: HTJ */ - case 0x8a: /* TODO: VTS */ - case 0x8b: /* TODO: PLD */ - case 0x8c: /* TODO: PLU */ - case 0x8d: /* TODO: RI */ - case 0x8e: /* TODO: SS2 */ - case 0x8f: /* TODO: SS3 */ - case 0x91: /* TODO: PU1 */ - case 0x92: /* TODO: PU2 */ - case 0x93: /* TODO: STS */ - case 0x94: /* TODO: CCH */ - case 0x95: /* TODO: MW */ - case 0x96: /* TODO: SPA */ - case 0x97: /* TODO: EPA */ - case 0x98: /* TODO: SOS */ - case 0x99: /* TODO: SGCI */ - break; - case 0x9a: /* DECID -- Identify Terminal */ - ttywrite(vtiden, strlen(vtiden), 0); - break; - case 0x9b: /* TODO: CSI */ - case 0x9c: /* TODO: ST */ - break; - case 0x90: /* DCS -- Device Control String */ - case 0x9d: /* OSC -- Operating System Command */ - case 0x9e: /* PM -- Privacy Message */ - case 0x9f: /* APC -- Application Program Command */ - tstrsequence(ascii); - return; - } - /* only CAN, SUB, \a and C1 chars interrupt a sequence */ - term.esc &= ~(Xstrend|Xstr); -} - -/* - * returns 1 when the sequence is finished and it hasn't to read - * more characters for this sequence, otherwise 0 - */ -int -eschandle(uchar ascii) -{ - switch (ascii) { - case '[': - term.esc |= Xcsi; - return 0; - case '#': - term.esc |= Xtest; - return 0; - case '%': - term.esc |= Xutf8; - return 0; - case 'P': /* DCS -- Device Control String */ - case '_': /* APC -- Application Program Command */ - case '^': /* PM -- Privacy Message */ - case ']': /* OSC -- Operating System Command */ - case 'k': /* old title set compatibility */ - tstrsequence(ascii); - return 0; - case 'n': /* LS2 -- Locking shift 2 */ - case 'o': /* LS3 -- Locking shift 3 */ - term.charset = 2 + (ascii - 'n'); - break; - case '(': /* GZD4 -- set primary charset G0 */ - case ')': /* G1D4 -- set secondary charset G1 */ - case '*': /* G2D4 -- set tertiary charset G2 */ - case '+': /* G3D4 -- set quaternary charset G3 */ - term.icharset = ascii - '('; - term.esc |= Xaltcs; - return 0; - case 'D': /* IND -- Linefeed */ - if (term.c.y == term.bot) { - tscrollup(term.top, 1); - } else { - tmoveto(term.c.x, term.c.y+1); - } - break; - case 'E': /* NEL -- Next line */ - tnewline(1); /* always go to first col */ - break; - case 'H': /* HTS -- Horizontal tab stop */ - term.tabs[term.c.x] = 1; - break; - case 'M': /* RI -- Reverse index */ - if (term.c.y == term.top) { - tscrolldown(term.top, 1); - } else { - tmoveto(term.c.x, term.c.y-1); - } - break; - case 'Z': /* DECID -- Identify Terminal */ - ttywrite(vtiden, strlen(vtiden), 0); - break; - case 'c': /* RIS -- Reset to initial state */ - treset(); - resettitle(); - xloadcols(); - break; - case '=': /* DECPAM -- Application keypad */ - xsetmode(1, Wappkeypad); - break; - case '>': /* DECPNM -- Normal keypad */ - xsetmode(0, Wappkeypad); - break; - case '7': /* DECSC -- Save Cursor */ - tcursor(CursorSave); - break; - case '8': /* DECRC -- Restore Cursor */ - tcursor(CursorLoad); - break; - case '\\': /* ST -- String Terminator */ - if (term.esc & Xstrend) - strhandle(); - break; - default: - fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", - (uchar) ascii, isprint(ascii)? ascii:'.'); - break; - } - return 1; -} - -void -tputc(rune u) -{ - char c[UTFmax]; - int control; - int width, len; - rune nu; - Letter *gp; - - control = ISCONTROL(u); - if (u < 127 || !IS_SET(Tutf8 | Tsixel)) { - c[0] = u; - width = len = 1; - } else { - len = utf8·encode(&u, c); - if(!control && (width = wcwidth(u)) == -1) - width = 1; - } - - /* combining characters */ - if(!width){ - if(term.c.x > 0) - gp = &term.line[term.c.y][term.c.x-1]; - else if(term.c.y > 0) - gp = &term.line[term.c.y-1][term.col-1]; - else - return; - -#if 0 - if(!hb_unicode_compose(hb_unicode_funcs_get_default(),gp->u, u, &nu)) { - return; - } -#endif - - gp->u = nu; - return; - } - - if (IS_SET(Tprint)) - tprinter(c, len); - - /* - * STR sequence must be checked before anything else - * because it uses all following characters until it - * receives a ESC, a SUB, a ST or any other C1 control - * character. - */ - if(term.esc & Xstr) { - if (u == '\a' || u == 030 || u == 032 || u == 033 || - ISCONTROLC1(u)) { - term.esc &= ~(Xstart|Xstr|Xdcs); - if (IS_SET(Tsixel)) { - /* TODO: render sixel */; - term.mode &= ~Tsixel; - return; - } - term.esc |= Xstrend; - goto check_control_code; - } - - if(IS_SET(Tsixel)) { - /* TODO: implement sixel mode */ - return; - } - if (term.esc&Xdcs && strescseq.len == 0 && u == 'q') - term.mode |= Tsixel; - - if (strescseq.len+len >= strescseq.siz) { - /* - * Here is a bug in terminals. If the user never sends - * some code to stop the str or esc command, then st - * will stop responding. But this is better than - * silently failing with unknown characters. At least - * then users will report back. - * - * In the case users ever get fixed, here is the code: - */ - /* - * term.esc = 0; - * strhandle(); - */ - if(strescseq.siz > (SIZE_MAX - UTFmax) / 2) - return; - strescseq.siz *= 2; - strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); - } - - memmove(&strescseq.buf[strescseq.len], c, len); - strescseq.len += len; - return; - } - -check_control_code: - /* - * Actions of control codes must be performed as soon they arrive - * because they can be embedded inside a control sequence, and - * they must not cause conflicts with sequences. - */ - if(control) { - tcontrolcode(u); - /* - * control codes are not shown ever - */ - if (!term.esc) - term.lastc = 0; - return; - } else if(term.esc & Xstart) { - if (term.esc & Xcsi) { - csiescseq.buf[csiescseq.len++] = u; - if (BETWEEN(u, 0x40, 0x7E) - || csiescseq.len >= \ - sizeof(csiescseq.buf)-1) { - term.esc = 0; - csiparse(); - csihandle(); - } - return; - } else if (term.esc & Xutf8) { - tdefutf8(u); - } else if (term.esc & Xaltcs) { - tdeftran(u); - } else if (term.esc & Xtest) { - tdectest(u); - } else { - if (!eschandle(u)) - return; - /* sequence already finished */ - } - term.esc = 0; - /* - * All characters which form part of a sequence are not - * printed - */ - return; - } - - if(selected(term.c.x, term.c.y)) - selclear(); - - gp = &term.line[term.c.y][term.c.x]; - if(IS_SET(Twrap) && (term.c.state & CursorWrap)) { - gp->mode |= Gwrap; - tnewline(1); - gp = &term.line[term.c.y][term.c.x]; - } - - if(IS_SET(Tinsert) && term.c.x+width < term.col) - memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Letter)); - - if(term.c.x+width > term.col) { - tnewline(1); - gp = &term.line[term.c.y][term.c.x]; - } - - tsetchar(u, &term.c.attr, term.c.x, term.c.y); - term.lastc = u; - - if(width == 2) { - gp->mode |= Gwrap; - if (term.c.x+1 < term.col) { - gp[1].u = '\0'; - gp[1].mode = Gwdummy; - } - } - if(term.c.x+width < term.col) { - tmoveto(term.c.x+width, term.c.y); - }else{ - term.c.state |= CursorWrap; - } -} - -int -twrite(char *buf, int buflen, int show_ctrl) -{ - int charsize; - rune u; - int n; - - for (n = 0; n < buflen; n += charsize) { - if(IS_SET(Tutf8) && !IS_SET(Tsixel)) { - /* process a complete utf8 char */ - charsize = utf8·decode(buf + n, &u); - if(charsize == 0) - break; - } else { - u = buf[n] & 0xFF; - charsize = 1; - } - if(show_ctrl && ISCONTROL(u)) { - if (u & 0x80) { - u &= 0x7f; - tputc('^'); - tputc('['); - } else if (u != '\n' && u != '\r' && u != '\t') { - u ^= 0x40; - tputc('^'); - } - } - tputc(u); - } - return n; -} - -void -tresize(int col, int row) -{ - int i; - int minrow = MIN(row, term.row); - int mincol = MIN(col, term.col); - int *bp; - Dot c; - - if (col < 1 || row < 1) { - fprintf(stderr, - "tresize: error resizing to %dx%d\n", col, row); - return; - } - - /* - * slide screen to keep cursor where we expect it - - * tscrollup would work here, but we can optimize to - * memmove because we're freeing the earlier lines - */ - for (i = 0; i <= term.c.y - row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - /* ensure that both src and dst are not nil */ - if (i > 0) { - memmove(term.line, term.line + i, row * sizeof(Letter*)); - memmove(term.alt, term.alt + i, row * sizeof(Letter*)); - } - for (i += row; i < term.row; i++) { - free(term.line[i]); - free(term.alt[i]); - } - - /* resize to new height */ - term.line = xrealloc(term.line, row * sizeof(Letter*)); - term.alt = xrealloc(term.alt, row * sizeof(Letter*)); - term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); - term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); - - /* resize each row to new width, zero-pad if needed */ - for (i = 0; i < minrow; i++) { - term.line[i] = xrealloc(term.line[i], col * sizeof(Letter)); - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Letter)); - } - - /* allocate any new rows */ - for (/* i = minrow */; i < row; i++) { - term.line[i] = xmalloc(col * sizeof(Letter)); - term.alt[i] = xmalloc(col * sizeof(Letter)); - } - if (col > term.col) { - bp = term.tabs + term.col; - - memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); - while (--bp > term.tabs && !*bp) - /* nothing */ ; - for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) - *bp = 1; - } - /* update terminal size */ - term.col = col; - term.row = row; - /* reset scrolling region */ - tsetscroll(0, row-1); - /* make use of the LIMIT in tmoveto */ - tmoveto(term.c.x, term.c.y); - /* Clearing both screens (it makes dirty all lines) */ - c = term.c; - for (i = 0; i < 2; i++) { - if (mincol < col && 0 < minrow) { - tclearregion(mincol, 0, col - 1, minrow - 1); - } - if (0 < col && minrow < row) { - tclearregion(0, minrow, col - 1, row - 1); - } - tswapscreen(); - tcursor(CursorLoad); - } - term.c = c; -} - -void -resettitle(void) -{ - xsettitle(nil); -} - -void -drawregion(int x1, int y1, int x2, int y2) -{ - int y; - - for (y = y1; y < y2; y++) { - if (!term.dirty[y]) - continue; - - term.dirty[y] = 0; - xdrawline(term.line[y], x1, y, x2); - } -} - -void -draw(void) -{ - int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; - - if (!xstartdraw()) - return; - - /* adjust cursor position */ - LIMIT(term.ocx, 0, term.col-1); - LIMIT(term.ocy, 0, term.row-1); - if (term.line[term.ocy][term.ocx].mode & Gwdummy) - term.ocx--; - if (term.line[term.c.y][cx].mode & Gwdummy) - cx--; - - drawregion(0, 0, term.col, term.row); - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], - term.ocx, term.ocy, term.line[term.ocy][term.ocx], - term.line[term.ocy], term.col - ); - term.ocx = cx; - term.ocy = term.c.y; - xfinishdraw(); - if (ocx != term.ocx || ocy != term.ocy) - xximspot(term.ocx, term.ocy); -} - -void -redraw(void) -{ - tfulldirt(); - draw(); -} diff --git a/sys/cmd/term/term.h b/sys/cmd/term/term.h deleted file mode 100644 index 6784974..0000000 --- a/sys/cmd/term/term.h +++ /dev/null @@ -1,316 +0,0 @@ -/* See LICENSE for license details. */ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -// ----------------------------------------------------------------------- -// macros - -#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) -#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) -#define DEFAULT(a, b) (a) = (a) ? (a) : (b) -#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) -#define GLYPHCMP(a, b) (((a).mode & (~Gwrap) & (~Gliga)) != ((b).mode & (~Gwrap) & (~Gliga)) || \ - (a).fg != (b).fg || (a).bg != (b).bg) -#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6) -#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) -#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) -#define IS_TRUECOL(x) (1 << 24 & (x)) - -#define iota(x) 1 << (x) - -/* arbitrary sizes */ -#define ESC_BUF_SIZ (128*UTFmax) -#define ESC_ARG_SIZ 16 -#define STR_BUF_SIZ ESC_BUF_SIZ -#define STR_ARG_SIZ ESC_ARG_SIZ - -// ----------------------------------------------------------------------- -// constants - -enum { - Gnil, - Gbold = iota(0), - Gfaint = iota(1), - Gitalic = iota(2), - Gunline = iota(3), - Gblink = iota(4), - Greverse = iota(5), - Ginvisible = iota(6), - Gstruck = iota(7), - Gwrap = iota(8), - Gwide = iota(9), - Gwdummy = iota(10), - Gliga = iota(11), - Gboldfaint = Gbold | Gfaint, -}; - -enum { - SelIdle = 0, - SelEmpty = 1, - SelReady = 2 -}; - -enum { - SelRegular = 1, - SelRectangular = 2 -}; - -enum { - SnapWord = 1, - SnapLine = 2 -}; - -/* cursor state */ -enum { - CursorSave, - CursorLoad -}; - -/* cursor mode */ -enum { - CursorDefault = 0, - CursorWrap = 1, - CursorOrigin = 2 -}; - -/* character set */ -enum { - CSgfx0, - CSgfx1, - CSuk, - CSusa, - CSmulti, - CSger, - CSfin, -}; - -/* escape sequences */ -enum { - Xstart = 1, - Xcsi = 2, - Xstr = 4, /* OSC, PM, APC */ - Xaltcs = 8, - Xstrend = 16, /* a final string was encountered */ - Xtest = 32, /* Enter in test mode */ - Xutf8 = 64, - Xdcs =128, -}; - -/* terminal mode */ -enum { - Twrap = iota(0), - Tinsert = iota(1), - Taltscreen = iota(2), - Tcrlf = iota(3), - Techo = iota(4), - Tprint = iota(5), - Tutf8 = iota(6), - Tsixel = iota(7), -}; - -/* window mode */ -enum { - Wvisible = iota(0), - Wfocused = iota(1), - Wappkeypad = iota(2), - Wmousebtn = iota(3), - Wmousemotion = iota(4), - Wreverse = iota(5), - Wkbdblock = iota(6), - Whide = iota(7), - Wappcursor = iota(8), - Wmousesgr = iota(9), - W8bit = iota(10), - Wblink = iota(11), - Wbflink = iota(12), - Wfocus = iota(13), - Wmousex10 = iota(14), - Wmousemany = iota(15), - Wbrcktpaste = iota(16), - Wnumlock = iota(17), - Wmouse = Wmousebtn|Wmousemotion|Wmousex10|Wmousemany, -}; - - -// ----------------------------------------------------------------------- -// types - -/* term.c */ -typedef struct Letter Letter; -typedef struct Dot Dot; -typedef struct Selection Selection; -typedef struct Terminal Terminal; - -typedef union Arg Arg; - -struct Letter { - rune u; /* character code */ - ushort mode; /* attribute flags */ - uint32 fg; /* foreground */ - uint32 bg; /* background */ -}; - -struct Dot { - Letter attr; /* current char attributes */ - int x; - int y; - char state; -}; - -struct Selection { - int mode; - int type; - int snap; - /* - * Selection variables: - * nb – normalized coordinates of the beginning of the selection - * ne – normalized coordinates of the end of the selection - * ob – original coordinates of the beginning of the selection - * oe – original coordinates of the end of the selection - */ - struct { - int x, y; - } nb, ne, ob, oe; - - int alt; -}; - -/* Internal representation of the screen */ -struct Terminal { - int row; /* nb row */ - int col; /* nb col */ - Letter **line; /* screen */ - Letter **alt; /* alternate screen */ - int *dirty; /* dirtyness of lines */ - Dot c; /* cursor */ - int ocx; /* old cursor col */ - int ocy; /* old cursor row */ - int top; /* top scroll limit */ - int bot; /* bottom scroll limit */ - int mode; /* terminal mode flags */ - int esc; /* escape state flags */ - char trantbl[4];/* charset table translation */ - int charset; /* current charset */ - int icharset; /* selected charset for sequence */ - int *tabs; - rune lastc; /* last printed char outside of sequence, 0 if control */ -}; - -/* CSI Escape sequence structs */ -/* ESC '[' [[ [] [;]] []] */ -typedef struct { - char buf[ESC_BUF_SIZ]; /* raw string */ - ulong len; /* raw string length */ - char priv; - int arg[ESC_ARG_SIZ]; - int narg; /* nb of args */ - char mode[2]; -} CSIEscape; - -/* STR Escape sequence structs */ -/* ESC type [[ [] [;]] ] ESC '\' */ -typedef struct { - char type; /* ESC type ... */ - char *buf; /* allocated raw string */ - size_t siz; /* allocation size */ - size_t len; /* raw string length */ - char *args[STR_ARG_SIZ]; - int narg; /* nb of args */ -} STREscape; - -/* x.c */ -typedef struct TermWindow TermWindow; - -struct TermWindow { - int tw, th; /* tty width and height */ - int w, h; /* window width and height */ - int hb, vb; /* horizontal and vertical border (in pix) */ - int ch; /* char height */ - int cw; /* char width */ - int mode; /* window state/mode flags */ - int cursor; /* cursor style */ -}; - -/* used for user hooks */ -union Arg { - int i; - uint ui; - float f; - void *v; - char *s; -}; - -// ----------------------------------------------------------------------- -// x.c (backend functions) - -void xbell(void); -void xclipcopy(void); -void xdrawcursor(int, int, Letter, int, int, Letter, Letter*, int); -void xdrawline(Letter*, int, int, int); -void xfinishdraw(void); -void xloadcols(void); -int xsetcolorname(int, char *); -void xsettitle(char *); -int xsetcursor(int); -void xsetmode(int, uint); -void xsetpointermotion(int); -void xsetsel(char *); -int xstartdraw(void); -void xximspot(int, int); - -void fatal( char *, ...); -void redraw(void); -void draw(void); - -void printscreen(Arg *); -void printsel(Arg *); -void sendbreak(Arg *); -void toggleprinter(Arg *); - -int tattrset(int); -void tnew(int, int); -void tresize(int, int); -void tsetdirtattr(int); -void ttyhangup(void); -int ttynew(char *, char *, char *, char **); -ulong ttyread(void); -void ttyresize(int, int); -void ttywrite( char *, size_t, int); - -void resettitle(void); - -void selclear(void); -void selinit(void); -void selstart(int, int, int); -void selextend(int, int, int, int); -int selected(int, int); -char *getsel(void); - -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -char *xstrdup(char *); - -/* config.h globals */ -extern char *utmp; -extern char *scroll; -extern char *stty_args; -extern char *vtiden; -extern wchar *worddelimiters; -extern int allowaltscreen; -extern int allowwindowops; -extern char *termname; -extern uint tabspaces; -extern uint defaultfg; -extern uint defaultbg; -extern float alpha; diff --git a/sys/cmd/term/term.info b/sys/cmd/term/term.info deleted file mode 100644 index 7b90344..0000000 --- a/sys/cmd/term/term.info +++ /dev/null @@ -1,250 +0,0 @@ -term+mono| simpleterm monocolor, - acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, - am, - bce, - bel=^G, - blink=\E[5m, - bold=\E[1m, - cbt=\E[Z, - cvvis=\E[?25h, - civis=\E[?25l, - clear=\E[H\E[2J, - cnorm=\E[?12l\E[?25h, - colors#2, - cols#80, - cr=^M, - csr=\E[%i%p1%d;%p2%dr, - cub=\E[%p1%dD, - cub1=^H, - cud1=^J, - cud=\E[%p1%dB, - cuf1=\E[C, - cuf=\E[%p1%dC, - cup=\E[%i%p1%d;%p2%dH, - cuu1=\E[A, - cuu=\E[%p1%dA, - dch=\E[%p1%dP, - dch1=\E[P, - dim=\E[2m, - dl=\E[%p1%dM, - dl1=\E[M, - ech=\E[%p1%dX, - ed=\E[J, - el=\E[K, - el1=\E[1K, - enacs=\E)0, - flash=\E[?5h$<80/>\E[?5l, - fsl=^G, - home=\E[H, - hpa=\E[%i%p1%dG, - hs, - ht=^I, - hts=\EH, - ich=\E[%p1%d@, - il1=\E[L, - il=\E[%p1%dL, - ind=^J, - indn=\E[%p1%dS, - invis=\E[8m, - is2=\E[4l\E>\E[?1034l, - it#4, - kel=\E[1;2F, - ked=\E[1;5F, - ka1=\E[1~, - ka3=\E[5~, - kc1=\E[4~, - kc3=\E[6~, - kbs=\177, - kcbt=\E[Z, - kb2=\EOu, - kcub1=\EOD, - kcud1=\EOB, - kcuf1=\EOC, - kcuu1=\EOA, - kDC=\E[3;2~, - kent=\EOM, - kEND=\E[1;2F, - kIC=\E[2;2~, - kNXT=\E[6;2~, - kPRV=\E[5;2~, - kHOM=\E[1;2H, - kLFT=\E[1;2D, - kRIT=\E[1;2C, - kind=\E[1;2B, - kri=\E[1;2A, - kclr=\E[3;5~, - kdl1=\E[3;2~, - kdch1=\E[3~, - kich1=\E[2~, - kend=\E[4~, - kf1=\EOP, - kf2=\EOQ, - kf3=\EOR, - kf4=\EOS, - kf5=\E[15~, - kf6=\E[17~, - kf7=\E[18~, - kf8=\E[19~, - kf9=\E[20~, - kf10=\E[21~, - kf11=\E[23~, - kf12=\E[24~, - kf13=\E[1;2P, - kf14=\E[1;2Q, - kf15=\E[1;2R, - kf16=\E[1;2S, - kf17=\E[15;2~, - kf18=\E[17;2~, - kf19=\E[18;2~, - kf20=\E[19;2~, - kf21=\E[20;2~, - kf22=\E[21;2~, - kf23=\E[23;2~, - kf24=\E[24;2~, - kf25=\E[1;5P, - kf26=\E[1;5Q, - kf27=\E[1;5R, - kf28=\E[1;5S, - kf29=\E[15;5~, - kf30=\E[17;5~, - kf31=\E[18;5~, - kf32=\E[19;5~, - kf33=\E[20;5~, - kf34=\E[21;5~, - kf35=\E[23;5~, - kf36=\E[24;5~, - kf37=\E[1;6P, - kf38=\E[1;6Q, - kf39=\E[1;6R, - kf40=\E[1;6S, - kf41=\E[15;6~, - kf42=\E[17;6~, - kf43=\E[18;6~, - kf44=\E[19;6~, - kf45=\E[20;6~, - kf46=\E[21;6~, - kf47=\E[23;6~, - kf48=\E[24;6~, - kf49=\E[1;3P, - kf50=\E[1;3Q, - kf51=\E[1;3R, - kf52=\E[1;3S, - kf53=\E[15;3~, - kf54=\E[17;3~, - kf55=\E[18;3~, - kf56=\E[19;3~, - kf57=\E[20;3~, - kf58=\E[21;3~, - kf59=\E[23;3~, - kf60=\E[24;3~, - kf61=\E[1;4P, - kf62=\E[1;4Q, - kf63=\E[1;4R, - khome=\E[1~, - kil1=\E[2;5~, - krmir=\E[2;2~, - knp=\E[6~, - kmous=\E[M, - kpp=\E[5~, - lines#24, - mir, - msgr, - npc, - op=\E[39;49m, - pairs#64, - mc0=\E[i, - mc4=\E[4i, - mc5=\E[5i, - rc=\E8, - rev=\E[7m, - ri=\EM, - rin=\E[%p1%dT, - ritm=\E[23m, - rmacs=\E(B, - rmcup=\E[?1049l, - rmir=\E[4l, - rmkx=\E[?1l\E>, - rmso=\E[27m, - rmul=\E[24m, - rs1=\Ec, - rs2=\E[4l\E>\E[?1034l, - sc=\E7, - sitm=\E[3m, - sgr0=\E[0m, - smacs=\E(0, - smcup=\E[?1049h, - smir=\E[4h, - smkx=\E[?1h\E=, - smso=\E[7m, - smul=\E[4m, - tbc=\E[3g, - tsl=\E]0;, - xenl, - vpa=\E[%i%p1%dd, -# XTerm extensions - rmxx=\E[29m, - smxx=\E[9m, -# disabled rep for now: causes some issues with older ncurses versions. -# rep=%p1%c\E[%p2%{1}%-%db, -# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) - Tc, - Ms=\E]52;%p1%s;%p2%s\007, - Se=\E[2 q, - Ss=\E[%p1%d q, - -term| simpleterm, - 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, - setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, - 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=term, - ccc, - colors#256, pairs#32767, - oc=\E]104\007, -# 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=term, - km, - rmm=\E[?1034l, - smm=\E[?1034h, - rs2=\E[4l\E>\E[?1034h, - is2=\E[4l\E>\E[?1034h, - -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, - -term-bs| simpleterm with backspace as backspace, - use=term, - kbs=\010, - kdch1=\177, - -term-bs-256color| simpleterm with backspace as backspace and 256colors, - use=term-256color, - kbs=\010, - kdch1=\177, diff --git a/sys/cmd/term/util.c b/sys/cmd/term/util.c deleted file mode 100644 index 3e7d81b..0000000 --- a/sys/cmd/term/util.c +++ /dev/null @@ -1,30 +0,0 @@ -#include - -static const uchar table[] = { -#include "nonspacing.h" -}; - -static const uchar wtable[] = { -#include "wide.h" -}; - -int -wcwidth(wchar_t wc) -{ - if (wc < 0xffU) - return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0; - if ((wc & 0xfffeffffU) < 0xfffe) { - if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) - return 0; - if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1) - return 2; - return 1; - } - if ((wc & 0xfffe) == 0xfffe) - return -1; - if (wc-0x20000U < 0x20000) - return 2; - if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100U < 0xef) - return 0; - return 1; -} diff --git a/sys/cmd/term/wide.h b/sys/cmd/term/wide.h deleted file mode 100644 index e403c9a..0000000 --- a/sys/cmd/term/wide.h +++ /dev/null @@ -1,65 +0,0 @@ -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,19,16,20,21,22,16,16,16,23,16,16,24,25,26,27,28,17, -17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,29, -17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,17,17,30,16,16,16,16,31,16,16,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,17,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,16,16,16,33, -34,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,35,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, -17,17,17,17,17,17,36,17,17,37,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,38,39,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,40,41,42,43,44,45,46,47,16,48,49,16,16,16,16, -16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,6,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,48,0,0,0,0,0,0,255,15,0,0,0,0,128,0,0,8, -0,2,12,0,96,48,64,16,0,0,4,44,36,32,12,0,0,0,1,0,0,0,80,184,0,0,0,0,0,0,0,224, -0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,33,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,251,255,255,255,255,255,255,255, -255,255,255,15,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,63,0,0,0,255,15,255,255,255,255, -255,255,255,127,254,255,255,255,255,255,255,255,255,255,127,254,255,255,255, -255,255,255,255,255,255,255,255,255,224,255,255,255,255,255,254,255,255,255, -255,255,255,255,255,255,255,127,255,255,255,255,255,7,255,255,255,255,15,0, -255,255,255,255,255,127,255,255,255,255,255,0,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0, -0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,31,255,255,255,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255, -255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,255,3,0,0,255,255,255,255,247,255,127,15,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,255,255,255,255,255,255,255, -255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,7,0,255,255,255,127,0,0,0,0,0, -0,7,0,240,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255, -15,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,64,254,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,255,255,255, -255,255,15,255,1,3,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255, -1,224,191,255,255,255,255,255,255,255,255,223,255,255,15,0,255,255,255,255, -255,135,15,0,255,255,17,255,255,255,255,255,255,255,255,127,253,255,255,255, -255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, -159,255,255,255,255,255,255,255,63,0,120,255,255,255,0,0,4,0,0,96,0,16,0,0,0, -0,0,0,0,0,0,0,248,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255, -255,255,255,255,255,255,63,16,39,0,0,24,240,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,255,15,0, -0,0,224,255,255,255,255,255,255,255,255,255,255,255,255,123,252,255,255,255, -255,231,199,255,255,255,231,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,15,7,7,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/sys/cmd/term/x.c b/sys/cmd/term/x.c deleted file mode 100644 index 27a2e7a..0000000 --- a/sys/cmd/term/x.c +++ /dev/null @@ -1,2070 +0,0 @@ -/* See LICENSE for license details. */ -#include "term.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* harfbuzz additions */ -#if 0 -void hbunloadfonts(); -void hbtransform(XftGlyphFontSpec *, const Letter*, size_t, int, int); -#endif - -/* types used in config.h */ -typedef struct Shortcut Shortcut; -typedef struct MouseShortcut MouseShortcut; -typedef struct Key Key; - -struct Shortcut{ - uint mod; - KeySym keysym; - void (*func)(Arg *); - Arg arg; -}; - -struct MouseShortcut { - uint mod; - uint button; - void (*func)(Arg *); - Arg arg; - uint release; -}; - -struct Key { - KeySym k; - uint mask; - char *s; - /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ - schar appkey; /* application keypad */ - schar appcursor; /* application cursor */ -}; - -/* X modifiers */ -#define XK_ANY_MOD UINT_MAX -#define XK_NO_MOD 0 -#define XK_SWITCH_MOD (1<<13) - -/* function definitions used in config.h */ -static void clipcopy(Arg *); -static void clippaste(Arg *); -static void numlock(Arg *); -static void selpaste(Arg *); -static void zoom(Arg *); -static void zoomabs(Arg *); -static void zoomreset(Arg *); -static void ttysend(Arg *); - -/* config.h for applying patches and the configuration. */ -#include "config.h" - -/* XEMBED messages */ -#define XEMBED_FOCUS_IN 4 -#define XEMBED_FOCUS_OUT 5 - -/* macros */ -#define IS_SET(flag) ((win.mode & (flag)) != 0) -#define TRUERED(x) (((x) & 0xff0000) >> 8) -#define TRUEGREEN(x) (((x) & 0xff00)) -#define TRUEBLUE(x) (((x) & 0xff) << 8) - -typedef XftDraw *Draw; -typedef XftColor Color; -typedef XftGlyphFontSpec GlyphFontSpec; - -/* Purely graphic info */ -typedef struct { - Display *dpy; - Colormap cmap; - Window win; - Drawable buf; - GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ - Atom xembed, wmdeletewin, netwmname, netwmpid; - struct { - XIM xim; - XIC xic; - XPoint spot; - XVaNestedList spotlist; - } ime; - Draw draw; - Visual *vis; - XSetWindowAttributes attrs; - int scr; - int isfixed; /* is fixed geometry? */ - int depth; /* color depth */ - int l, t; /* left and top offset */ - int gm; /* geometry mask */ -} XWindow; - -typedef struct { - Atom xtarget; - char *primary, *clipboard; - struct timespec tclick1; - struct timespec tclick2; -} XSelection; - -/* Font structure */ -#define Font Font_ -typedef struct { - int height; - int width; - int ascent; - int descent; - int badslant; - int badweight; - short lbearing; - short rbearing; - XftFont *match; - FcFontSet *set; - FcPattern *pattern; -} Font; - -/* Drawing Context */ -typedef struct { - Color *col; - size_t collen; - Font font, bfont, ifont, ibfont; - GC gc; -} DC; - -static inline ushort sixd_to_16bit(int); -static int xmakeglyphfontspecs(XftGlyphFontSpec *, Letter *, int, int, int); -static void xdrawglyphfontspecs(XftGlyphFontSpec *, Letter, int, int, int); -static void xdrawglyph(Letter, int, int); -static void xclear(int, int, int, int); -static int xgeommasktogravity(int); -static int ximopen(Display *); -static void ximinstantiate(Display *, XPointer, XPointer); -static void ximdestroy(XIM, XPointer, XPointer); -static int xicdestroy(XIC, XPointer, XPointer); -static void xinit(int, int); -static void cresize(int, int); -static void xresize(int, int); -static void xhints(void); -static int xloadcolor(int, char *, Color *); -static int xloadfont(Font *, FcPattern *); -static void xloadfonts(char *, double); -static void xunloadfont(Font *); -static void xunloadfonts(void); -static void xsetenv(void); -static void xseturgency(int); -static int evcol(XEvent *); -static int evrow(XEvent *); - -static void expose(XEvent *); -static void visibility(XEvent *); -static void unmap(XEvent *); -static void kpress(XEvent *); -static void cmessage(XEvent *); -static void resize(XEvent *); -static void focus(XEvent *); -static uint buttonmask(uint); -static int mouseaction(XEvent *, uint); -static void brelease(XEvent *); -static void bpress(XEvent *); -static void bmotion(XEvent *); -static void propnotify(XEvent *); -static void selnotify(XEvent *); -static void selclear_(XEvent *); -static void selrequest(XEvent *); -static void setsel(char *, Time); -static void mousesel(XEvent *, int); -static void mousereport(XEvent *); -static char *kmap(KeySym, uint); -static int match(uint, uint); - -static void run(void); -static void usage(void); - -static void (*handler[LASTEvent])(XEvent *) = { - [KeyPress] = kpress, - [ClientMessage] = cmessage, - [ConfigureNotify] = resize, - [VisibilityNotify] = visibility, - [UnmapNotify] = unmap, - [Expose] = expose, - [FocusIn] = focus, - [FocusOut] = focus, - [MotionNotify] = bmotion, - [ButtonPress] = bpress, - [ButtonRelease] = brelease, - [SelectionClear] = selclear_, - [SelectionNotify] = selnotify, -/* - * PropertyNotify is only turned on when there is some INCR transfer happening - * for the selection retrieval. - */ - [PropertyNotify] = propnotify, - [SelectionRequest] = selrequest, -}; - -/* Globals */ -static DC dc; -static XWindow xw; -static XSelection xsel; -static TermWindow win; - -/* Font Ring Cache */ -enum { - FRC_NORMAL, - FRC_ITALIC, - FRC_BOLD, - FRC_ITALICBOLD -}; - -typedef struct { - XftFont *font; - int flags; - rune unicodep; -} Fontcache; - -/* Fontcache is an array now. A new font will be appended to the array. */ -static Fontcache *frc = nil; -static int frclen = 0; -static int frccap = 0; -static char *usedfont = nil; -static double usedfontsize = 0; -static double defaultfontsize = 0; - -static char *opt_alpha = nil; -static char *opt_class = nil; -static char **opt_cmd = nil; -static char *opt_embed = nil; -static char *opt_font = nil; -static char *opt_io = nil; -static char *opt_line = nil; -static char *opt_name = nil; -static char *opt_title = nil; - -static int oldbutton = 3; /* button event on startup: 3 = release */ - -void -clipcopy(Arg *_) -{ - Atom clipboard; - - free(xsel.clipboard); - xsel.clipboard = nil; - - if (xsel.primary != nil) { - xsel.clipboard = xstrdup(xsel.primary); - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); - } -} - -void -clippaste(Arg *_) -{ - Atom clipboard; - - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, - xw.win, CurrentTime); -} - -void -selpaste(Arg *_) -{ - XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, - xw.win, CurrentTime); -} - -void -numlock(Arg *_) -{ - win.mode ^= Wnumlock; -} - -void -zoom(Arg *arg) -{ - Arg larg; - - larg.f = usedfontsize + arg->f; - zoomabs(&larg); -} - -void -zoomabs(Arg *arg) -{ - xunloadfonts(); - xloadfonts(usedfont, arg->f); - cresize(0, 0); - redraw(); - xhints(); -} - -void -zoomreset(Arg *arg) -{ - Arg larg; - - if (defaultfontsize > 0) { - larg.f = defaultfontsize; - zoomabs(&larg); - } -} - -void -ttysend(Arg *arg) -{ - ttywrite(arg->s, strlen(arg->s), 1); -} - -int -evcol(XEvent *e) -{ - int x = e->xbutton.x - win.hb; - LIMIT(x, 0, win.tw - 1); - return x / win.cw; -} - -int -evrow(XEvent *e) -{ - int y = e->xbutton.y - win.vb; - LIMIT(y, 0, win.th - 1); - return y / win.ch; -} - -void -mousesel(XEvent *e, int done) -{ - int type, seltype = SelRegular; - uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); - - for (type = 1; type < arrlen(selmasks); ++type) { - if (match(selmasks[type], state)) { - seltype = type; - break; - } - } - selextend(evcol(e), evrow(e), seltype, done); - if (done) - setsel(getsel(), e->xbutton.time); -} - -void -mousereport(XEvent *e) -{ - int len, x = evcol(e), y = evrow(e), - button = e->xbutton.button, state = e->xbutton.state; - char buf[40]; - static int ox, oy; - - /* from urxvt */ - if (e->xbutton.type == MotionNotify) { - if (x == ox && y == oy) - return; - if (!IS_SET(Wmousemotion) && !IS_SET(Wmousemany)) - return; - /* MOUSE_MOTION: no reporting if no button is pressed */ - if (IS_SET(Wmousemotion) && oldbutton == 3) - return; - - button = oldbutton + 32; - ox = x; - oy = y; - } else { - if (!IS_SET(Wmousesgr) && e->xbutton.type == ButtonRelease) { - button = 3; - } else { - button -= Button1; - if (button >= 3) - button += 64 - 3; - } - if (e->xbutton.type == ButtonPress) { - oldbutton = button; - ox = x; - oy = y; - } else if (e->xbutton.type == ButtonRelease) { - oldbutton = 3; - /* Wmousex10: no button release reporting */ - if (IS_SET(Wmousex10)) - return; - if (button == 64 || button == 65) - return; - } - } - - if (!IS_SET(Wmousex10)) { - button += ((state & ShiftMask ) ? 4 : 0) - + ((state & Mod4Mask ) ? 8 : 0) - + ((state & ControlMask) ? 16 : 0); - } - - if (IS_SET(Wmousesgr)) { - len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", - button, x+1, y+1, - e->xbutton.type == ButtonRelease ? 'm' : 'M'); - } else if (x < 223 && y < 223) { - len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", - 32+button, 32+x+1, 32+y+1); - } else { - return; - } - - ttywrite(buf, len, 0); -} - -uint -buttonmask(uint button) -{ - return button == Button1 ? Button1Mask - : button == Button2 ? Button2Mask - : button == Button3 ? Button3Mask - : button == Button4 ? Button4Mask - : button == Button5 ? Button5Mask - : 0; -} - -int -mouseaction(XEvent *e, uint release) -{ - MouseShortcut *ms; - - /* ignore Buttonmask for Button - it's set on release */ - uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); - - for (ms = mshortcuts; ms < mshortcuts + arrlen(mshortcuts); ms++) { - if (ms->release == release && - ms->button == e->xbutton.button && - (match(ms->mod, state) || /* exact or forced */ - match(ms->mod, state & ~forcemousemod))) { - ms->func(&(ms->arg)); - return 1; - } - } - - return 0; -} - -void -bpress(XEvent *e) -{ - struct timespec now; - int snap; - - if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - if (mouseaction(e, 0)) - return; - - if (e->xbutton.button == Button1) { - /* - * If the user clicks below predefined timeouts specific - * snapping behaviour is exposed. - */ - clock_gettime(CLOCK_MONOTONIC, &now); - if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { - snap = SnapLine; - } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { - snap = SnapWord; - } else { - snap = 0; - } - xsel.tclick2 = xsel.tclick1; - xsel.tclick1 = now; - - selstart(evcol(e), evrow(e), snap); - } -} - -void -propnotify(XEvent *e) -{ - XPropertyEvent *xpev; - Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - - xpev = &e->xproperty; - if (xpev->state == PropertyNewValue && - (xpev->atom == XA_PRIMARY || - xpev->atom == clipboard)) { - selnotify(e); - } -} - -void -selnotify(XEvent *e) -{ - ulong nitems, ofs, rem; - int format; - uchar *data, *last, *repl; - Atom type, incratom, property = None; - - incratom = XInternAtom(xw.dpy, "INCR", 0); - - ofs = 0; - if (e->type == SelectionNotify) - property = e->xselection.property; - else if (e->type == PropertyNotify) - property = e->xproperty.atom; - - if (property == None) - return; - - do { - if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, - BUFSIZ/4, False, AnyPropertyType, - &type, &format, &nitems, &rem, - &data)) { - fprintf(stderr, "Clipboard allocation failed\n"); - return; - } - - if (e->type == PropertyNotify && nitems == 0 && rem == 0) { - /* - * If there is some PropertyNotify with no data, then - * this is the signal of the selection owner that all - * data has been transferred. We won't need to receive - * PropertyNotify events anymore. - */ - MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, - &xw.attrs); - } - - if (type == incratom) { - /* - * Activate the PropertyNotify events so we receive - * when the selection owner does send us the next - * chunk of data. - */ - MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, - &xw.attrs); - - /* - * Deleting the property is the transfer start signal. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); - continue; - } - - /* - * As seen in getsel: - * Line endings are inconsistent in the terminal and GUI world - * copy and pasting. When receiving some selection data, - * replace all '\n' with '\r'. - * FIXME: Fix the computer world. - */ - repl = data; - last = data + nitems * format / 8; - while ((repl = memchr(repl, '\n', last - repl))) { - *repl++ = '\r'; - } - - if (IS_SET(Wbrcktpaste) && ofs == 0) - ttywrite("\033[200~", 6, 0); - ttywrite((char *)data, nitems * format / 8, 1); - if (IS_SET(Wbrcktpaste) && rem == 0) - ttywrite("\033[201~", 6, 0); - XFree(data); - /* number of 32-bit chunks returned */ - ofs += nitems * format / 32; - } while (rem > 0); - - /* - * Deleting the property again tells the selection owner to send the - * next data chunk in the property. - */ - XDeleteProperty(xw.dpy, xw.win, (int)property); -} - -void -xclipcopy(void) -{ - clipcopy(nil); -} - -void -selclear_(XEvent *e) -{ - selclear(); -} - -void -selrequest(XEvent *e) -{ - XSelectionRequestEvent *xsre; - XSelectionEvent xev; - Atom xa_targets, string, clipboard; - char *seltext; - - xsre = (XSelectionRequestEvent *) e; - xev.type = SelectionNotify; - xev.requestor = xsre->requestor; - xev.selection = xsre->selection; - xev.target = xsre->target; - xev.time = xsre->time; - if (xsre->property == None) - xsre->property = xsre->target; - - /* reject */ - xev.property = None; - - xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); - if (xsre->target == xa_targets) { - /* respond with the supported type */ - string = xsel.xtarget; - XChangeProperty(xsre->display, xsre->requestor, xsre->property, - XA_ATOM, 32, PropModeReplace, - (uchar *) &string, 1); - xev.property = xsre->property; - } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { - /* - * xith XA_STRING non ascii characters may be incorrect in the - * requestor. It is not our problem, use utf8. - */ - clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); - if (xsre->selection == XA_PRIMARY) { - seltext = xsel.primary; - } else if (xsre->selection == clipboard) { - seltext = xsel.clipboard; - } else { - fprintf(stderr, - "Unhandled clipboard selection 0x%lx\n", - xsre->selection); - return; - } - if (seltext != nil) { - XChangeProperty(xsre->display, xsre->requestor, - xsre->property, xsre->target, - 8, PropModeReplace, - (uchar *)seltext, strlen(seltext)); - xev.property = xsre->property; - } - } - - /* all done, send a notification to the listener */ - if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) - fprintf(stderr, "Error sending SelectionNotify event\n"); -} - -void -setsel(char *str, Time t) -{ - if (!str) - return; - - free(xsel.primary); - xsel.primary = str; - - XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); - if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) - selclear(); -} - -void -xsetsel(char *str) -{ - setsel(str, CurrentTime); -} - -void -brelease(XEvent *e) -{ - if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - if (mouseaction(e, 1)) - return; - if (e->xbutton.button == Button1) - mousesel(e, 1); -} - -void -bmotion(XEvent *e) -{ - if (IS_SET(Wmouse) && !(e->xbutton.state & forcemousemod)) { - mousereport(e); - return; - } - - mousesel(e, 0); -} - -void -cresize(int width, int height) -{ - int col, row; - - if (width != 0) - win.w = width; - if (height != 0) - win.h = height; - - col = (win.w - 2 * borderpx) / win.cw; - row = (win.h - 2 * borderpx) / win.ch; - col = MAX(1, col); - row = MAX(1, row); - - win.hb = (win.w - col*win.cw)/2; - win.vb = (win.h - row*win.ch)/2; - - tresize(col, row); - xresize(col, row); - ttyresize(win.tw, win.th); -} - -void -xresize(int col, int row) -{ - win.tw = col * win.cw; - win.th = row * win.ch; - - XFreePixmap(xw.dpy, xw.buf); - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - - XftDrawChange(xw.draw, xw.buf); - xclear(0, 0, win.w, win.h); - - /* resize to new width */ - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); -} - -ushort -sixd_to_16bit(int x) -{ - return x == 0 ? 0 : 0x3737 + 0x2828 * x; -} - -int -xloadcolor(int i, char *name, Color *ncolor) -{ - XRenderColor color = { .alpha = 0xffff }; - - if (!name) { - if (BETWEEN(i, 16, 255)) { /* 256 color */ - if (i < 6*6*6+16) { /* same colors as xterm */ - color.red = sixd_to_16bit( ((i-16)/36)%6 ); - color.green = sixd_to_16bit( ((i-16)/6) %6 ); - color.blue = sixd_to_16bit( ((i-16)/1) %6 ); - } else { /* greyscale */ - color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); - color.green = color.blue = color.red; - } - return XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &color, ncolor); - } else - name = colorname[i]; - } - - return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); -} - -void -xloadcols(void) -{ - int i; - static int loaded; - Color *cp; - - if (loaded) { - for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) - XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); - } else { - dc.collen = MAX(arrlen(colorname), 256); - dc.col = xmalloc(dc.collen * sizeof(Color)); - } - - for (i = 0; i < dc.collen; i++) - if (!xloadcolor(i, nil, &dc.col[i])) { - if (colorname[i]) - fatal("could not allocate color '%s'\n", colorname[i]); - else - fatal("could not allocate color %d\n", i); - } - - if (opt_alpha) - alpha = strtof(opt_alpha, nil); - - dc.col[defaultbg].color.alpha = (ushort)(0xffff * alpha); - dc.col[defaultbg].pixel &= 0x00ffffff; - dc.col[defaultbg].pixel |= (uchar)(0xff*alpha) << 24; - loaded = 1; -} - -int -xsetcolorname(int x, char *name) -{ - Color ncolor; - - if (!BETWEEN(x, 0, dc.collen)) - return 1; - - if (!xloadcolor(x, name, &ncolor)) - return 1; - - XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); - dc.col[x] = ncolor; - - return 0; -} - -/* - * Absolute coordinates. - */ -void -xclear(int x1, int y1, int x2, int y2) -{ - XftDrawRect(xw.draw, &dc.col[IS_SET(Wreverse)? defaultfg : defaultbg], x1, y1, x2-x1, y2-y1); -} - -void -xhints(void) -{ - XClassHint class = {opt_name ? opt_name : termname, - opt_class ? opt_class : termname}; - XWMHints wm = {.flags = InputHint, .input = 1}; - XSizeHints *sizeh; - - sizeh = XAllocSizeHints(); - - sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; - sizeh->height = win.h, sizeh->width = win.w; - sizeh->height_inc = 1; - sizeh->width_inc = 1; - sizeh->base_height = 2 * borderpx; - sizeh->base_width = 2 * borderpx; - sizeh->min_height = win.ch + 2 * borderpx; - sizeh->min_width = win.cw + 2 * borderpx; - if (xw.isfixed) { - sizeh->flags |= PMaxSize; - sizeh->min_width = sizeh->max_width = win.w; - sizeh->min_height = sizeh->max_height = win.h; - } - if (xw.gm & (XValue|YValue)) { - sizeh->flags |= USPosition | PWinGravity; - sizeh->x = xw.l; - sizeh->y = xw.t; - sizeh->win_gravity = xgeommasktogravity(xw.gm); - } - - XSetWMProperties(xw.dpy, xw.win, nil, nil, nil, 0, sizeh, &wm, - &class); - XFree(sizeh); -} - -int -xgeommasktogravity(int mask) -{ - switch (mask & (XNegative|YNegative)) { - case 0: - return NorthWestGravity; - case XNegative: - return NorthEastGravity; - case YNegative: - return SouthWestGravity; - } - - return SouthEastGravity; -} - -int -xloadfont(Font *f, FcPattern *pattern) -{ - FcPattern *configured; - FcPattern *match; - FcResult result; - XGlyphInfo extents; - int wantattr, haveattr; - - /* - * Manually configure instead of calling XftMatchFont - * so that we can use the configured pattern for - * "missing glyph" lookups. - */ - configured = FcPatternDuplicate(pattern); - if (!configured) - return 1; - - FcConfigSubstitute(nil, configured, FcMatchPattern); - XftDefaultSubstitute(xw.dpy, xw.scr, configured); - - match = FcFontMatch(nil, configured, &result); - if (!match) { - FcPatternDestroy(configured); - return 1; - } - - if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { - FcPatternDestroy(configured); - FcPatternDestroy(match); - return 1; - } - - if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == - XftResultMatch)) { - /* - * Check if xft was unable to find a font with the appropriate - * slant but gave us one anyway. Try to mitigate. - */ - if ((XftPatternGetInteger(f->match->pattern, "slant", 0, - &haveattr) != XftResultMatch) || haveattr < wantattr) { - f->badslant = 1; - fputs("font slant does not match\n", stderr); - } - } - - if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == - XftResultMatch)) { - if ((XftPatternGetInteger(f->match->pattern, "weight", 0, - &haveattr) != XftResultMatch) || haveattr != wantattr) { - f->badweight = 1; - fputs("font weight does not match\n", stderr); - } - } - - XftTextExtentsUtf8(xw.dpy, f->match, - (const FcChar8 *) ascii_printable, - strlen(ascii_printable), &extents); - - f->set = nil; - f->pattern = configured; - - f->ascent = f->match->ascent; - f->descent = f->match->descent; - f->lbearing = 0; - f->rbearing = f->match->max_advance_width; - - f->height = f->ascent + f->descent; - f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); - - return 0; -} - -void -xloadfonts(char *fontstr, double fontsize) -{ - FcPattern *pattern; - double fontval; - - if (fontstr[0] == '-') - pattern = XftXlfdParse(fontstr, False, False); - else - pattern = FcNameParse((FcChar8 *)fontstr); - - if (!pattern) - fatal("can't open font %s\n", fontstr); - - if (fontsize > 1) { - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternDel(pattern, FC_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); - usedfontsize = fontsize; - } else { - if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = fontval; - } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == - FcResultMatch) { - usedfontsize = -1; - } else { - /* - * Default font size is 12, if none given. This is to - * have a known usedfontsize value. - */ - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); - usedfontsize = 12; - } - defaultfontsize = usedfontsize; - } - - if (xloadfont(&dc.font, pattern)) - fatal("can't open font %s\n", fontstr); - - if (usedfontsize < 0) { - FcPatternGetDouble(dc.font.match->pattern, - FC_PIXEL_SIZE, 0, &fontval); - usedfontsize = fontval; - if (fontsize == 0) - defaultfontsize = fontval; - } - - /* Setting character width and height. */ - win.cw = ceilf(dc.font.width * cwscale); - win.ch = ceilf(dc.font.height * chscale); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); - if (xloadfont(&dc.ifont, pattern)) - fatal("can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_WEIGHT); - FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); - if (xloadfont(&dc.ibfont, pattern)) - fatal("can't open font %s\n", fontstr); - - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); - if (xloadfont(&dc.bfont, pattern)) - fatal("can't open font %s\n", fontstr); - - FcPatternDestroy(pattern); -} - -void -xunloadfont(Font *f) -{ - XftFontClose(xw.dpy, f->match); - FcPatternDestroy(f->pattern); - if (f->set) - FcFontSetDestroy(f->set); -} - -void -xunloadfonts(void) -{ -#if 0 - hbunloadfonts(); -#endif - - /* Free the loaded fonts in the font cache. */ - while (frclen > 0) - XftFontClose(xw.dpy, frc[--frclen].font); - - xunloadfont(&dc.font); - xunloadfont(&dc.bfont); - xunloadfont(&dc.ifont); - xunloadfont(&dc.ibfont); -} - -int -ximopen(Display *dpy) -{ - XIMCallback imdestroy = { .client_data = nil, .callback = ximdestroy }; - XICCallback icdestroy = { .client_data = nil, .callback = xicdestroy }; - - xw.ime.xim = XOpenIM(xw.dpy, nil, nil, nil); - if (xw.ime.xim == nil) - return 0; - - if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, nil)) - fprintf(stderr, "XSetIMValues: " - "Could not set XNDestroyCallback.\n"); - - xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, - nil); - - if (xw.ime.xic == nil) { - xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, - XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, xw.win, - XNDestroyCallback, &icdestroy, - nil); - } - if (xw.ime.xic == nil) - fprintf(stderr, "XCreateIC: Could not create input context.\n"); - - return 1; -} - -void -ximinstantiate(Display *dpy, XPointer client, XPointer call) -{ - if (ximopen(dpy)) - XUnregisterIMInstantiateCallback(xw.dpy, nil, nil, nil, - ximinstantiate, nil); -} - -void -ximdestroy(XIM xim, XPointer client, XPointer call) -{ - xw.ime.xim = nil; - XRegisterIMInstantiateCallback(xw.dpy, nil, nil, nil, - ximinstantiate, nil); - XFree(xw.ime.spotlist); -} - -int -xicdestroy(XIC xim, XPointer client, XPointer call) -{ - xw.ime.xic = nil; - return 1; -} - -void -xinit(int cols, int rows) -{ - XGCValues gcvalues; - Cursor cursor; - Window parent; - XColor xmousefg, xmousebg; - XWindowAttributes attr; - XVisualInfo vis; - pid_t thispid = getpid(); - - if (!(xw.dpy = XOpenDisplay(nil))) - fatal("can't open display\n"); - - if (!(opt_embed && (parent == strtol(opt_embed, nil, 0)))) { - parent = XRootWindow(xw.dpy, xw.scr); - xw.depth = 32; - } else { - XGetWindowAttributes(xw.dpy, parent, &attr); - xw.depth = attr.depth; - } - - XMatchVisualInfo(xw.dpy, xw.scr, xw.depth, TrueColor, &vis); - xw.vis = vis.visual; - xw.scr = XDefaultScreen(xw.dpy); - - /* font */ - if (!FcInit()) - fatal("could not init fontconfig.\n"); - - usedfont = (opt_font == nil)? font : opt_font; - xloadfonts(usedfont, 0); - - /* colors */ - xw.cmap = XCreateColormap(xw.dpy, parent, xw.vis, None); - xloadcols(); - - /* adjust fixed window geometry */ - win.w = 2 * win.hb + cols*win.cw; - win.h = 2 * win.vb + rows*win.ch; - - if (xw.gm & XNegative) - xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; - if (xw.gm & YNegative) - xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; - - /* Events */ - xw.attrs.background_pixel = dc.col[defaultbg].pixel; - xw.attrs.border_pixel = dc.col[defaultbg].pixel; - xw.attrs.bit_gravity = NorthWestGravity; - xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask - | ExposureMask | VisibilityChangeMask | StructureNotifyMask - | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; - xw.attrs.colormap = xw.cmap; - - xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, - win.w, win.h, 0, xw.depth, InputOutput, - xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity - | CWEventMask | CWColormap, &xw.attrs); - - memset(&gcvalues, 0, sizeof(gcvalues)); - gcvalues.graphics_exposures = False; - - xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, xw.depth); - dc.gc = XCreateGC(xw.dpy, xw.buf, GCGraphicsExposures, &gcvalues); - - XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); - XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); - - /* font spec buffer */ - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); - - /* Xft rendering context */ - xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); - - /* input methods */ - if (!ximopen(xw.dpy)) - XRegisterIMInstantiateCallback(xw.dpy, nil, nil, nil, ximinstantiate, nil); - - /* white cursor, black outline */ - cursor = XCreateFontCursor(xw.dpy, mouseshape); - XDefineCursor(xw.dpy, xw.win, cursor); - - if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { - xmousefg.red = 0xffff; - xmousefg.green = 0xffff; - xmousefg.blue = 0xffff; - } - - if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { - xmousebg.red = 0x0000; - xmousebg.green = 0x0000; - xmousebg.blue = 0x0000; - } - - XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); - - xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); - xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); - xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); - XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); - - xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); - XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, - PropModeReplace, (uchar *)&thispid, 1); - - win.mode = Wnumlock; - resettitle(); - xhints(); - - XMapWindow(xw.dpy, xw.win); - XSync(xw.dpy, False); - - clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); - clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); - xsel.primary = nil; - xsel.clipboard = nil; - xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); - if (xsel.xtarget == None) - xsel.xtarget = XA_STRING; -} - -int -xmakeglyphfontspecs(XftGlyphFontSpec *specs, Letter *glyphs, int len, int x, int y) -{ - float winx = win.hb + x * win.cw, winy = win.vb + y * win.ch, xp, yp; - ushort mode, prevmode = USHRT_MAX; - Font *font = &dc.font; - int frcflags = FRC_NORMAL; - float runewidth = win.cw; - rune r; - FT_UInt glyphidx; - FcResult fcres; - FcPattern *fcpattern, *fontpattern; - FcFontSet *fcsets[] = { nil }; - FcCharSet *fccharset; - int i, f, numspecs = 0; - - for(i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { - /* Fetch rune and mode for current glyph. */ - r = glyphs[i].u; - mode = glyphs[i].mode; - - /* Skip dummy wide-character spacing. */ -#if 0 - if(mode & Gwdummy) -#endif - if(mode == Gwdummy) - continue; - - /* Determine font for glyph if different from previous glyph. */ - if(prevmode != mode){ - prevmode = mode; - font = &dc.font; - frcflags = FRC_NORMAL; - runewidth = win.cw * ((mode & Gwide) ? 2.0f : 1.0f); - if ((mode & Gitalic) && (mode & Gbold)) { - font = &dc.ibfont; - frcflags = FRC_ITALICBOLD; - } else if (mode & Gitalic) { - font = &dc.ifont; - frcflags = FRC_ITALIC; - } else if (mode & Gbold) { - font = &dc.bfont; - frcflags = FRC_BOLD; - } - yp = winy + font->ascent; - } - - /* lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, r); - if(glyphidx){ - specs[numspecs].font = font->match; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - continue; - } - - /* Fallback on font cache, search the font cache for match. */ - for(f = 0; f < frclen; f++) { - glyphidx = XftCharIndex(xw.dpy, frc[f].font, r); - /* Everything correct. */ - if (glyphidx && frc[f].flags == frcflags) - break; - /* We got a default font for a not found glyph. */ - if (!glyphidx && frc[f].flags == frcflags - && frc[f].unicodep == r) { - break; - } - } - - /* Nothing was found. Use fontconfig to find matching font. */ - if(f >= frclen) { - if (!font->set) - font->set = FcFontSort(0, font->pattern, - 1, 0, &fcres); - fcsets[0] = font->set; - - /* - * Nothing was found in the cache. Now use - * some dozen of Fontconfig calls to get the - * font for one single character. - * - * Xft and fontconfig are design failures. - */ - fcpattern = FcPatternDuplicate(font->pattern); - fccharset = FcCharSetCreate(); - - FcCharSetAddChar(fccharset, r); - FcPatternAddCharSet(fcpattern, FC_CHARSET, - fccharset); - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); - - FcConfigSubstitute(0, fcpattern, - FcMatchPattern); - FcDefaultSubstitute(fcpattern); - - fontpattern = FcFontSetMatch(0, fcsets, 1, - fcpattern, &fcres); - - /* Allocate memory for the new cache entry. */ - if (frclen >= frccap) { - frccap += 16; - frc = xrealloc(frc, frccap * sizeof(Fontcache)); - } - - frc[frclen].font = XftFontOpenPattern(xw.dpy, - fontpattern); - if (!frc[frclen].font) - fatal("XftFontOpenPattern failed seeking fallback font: %s\n", - strerror(errno)); - frc[frclen].flags = frcflags; - frc[frclen].unicodep = r; - - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, r); - - f = frclen; - frclen++; - - FcPatternDestroy(fcpattern); - FcCharSetDestroy(fccharset); - } - - specs[numspecs].font = frc[f].font; - specs[numspecs].glyph = glyphidx; - specs[numspecs].x = (short)xp; - specs[numspecs].y = (short)yp; - xp += runewidth; - numspecs++; - } - -#if 0 - hbtransform(specs, glyphs, len, x, y); -#endif - - return numspecs; -} - -void -xdrawglyphfontspecs(XftGlyphFontSpec *specs, Letter base, int len, int x, int y) -{ - int charlen = len * ((base.mode & Gwide) ? 2 : 1); - int winx = win.hb + x * win.cw, winy = win.vb + y * win.ch, width = charlen * win.cw; - Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; - XRenderColor colfg, colbg; - XRectangle r; - - /* Fallback on color display for attributes not supported by the font */ - if (base.mode & Gitalic && base.mode & Gbold) { - if (dc.ibfont.badslant || dc.ibfont.badweight) - base.fg = defaultattr; - } else if ((base.mode & Gitalic && dc.ifont.badslant) || - (base.mode & Gbold && dc.bfont.badweight)) { - base.fg = defaultattr; - } - - if (IS_TRUECOL(base.fg)) { - colfg.alpha = 0xffff; - colfg.red = TRUERED(base.fg); - colfg.green = TRUEGREEN(base.fg); - colfg.blue = TRUEBLUE(base.fg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); - fg = &truefg; - } else { - fg = &dc.col[base.fg]; - } - - if (IS_TRUECOL(base.bg)) { - colbg.alpha = 0xffff; - colbg.green = TRUEGREEN(base.bg); - colbg.red = TRUERED(base.bg); - colbg.blue = TRUEBLUE(base.bg); - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); - bg = &truebg; - } else { - bg = &dc.col[base.bg]; - } - - /* Change basic system colors [0-7] to bright system colors [8-15] */ - if ((base.mode & Gfaint) == Gbold && BETWEEN(base.fg, 0, 7)) - fg = &dc.col[base.fg + 8]; - - if (IS_SET(Wreverse)) { - if (fg == &dc.col[defaultfg]) { - fg = &dc.col[defaultbg]; - } else { - colfg.red = ~fg->color.red; - colfg.green = ~fg->color.green; - colfg.blue = ~fg->color.blue; - colfg.alpha = fg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); - fg = &revfg; - } - - if (bg == &dc.col[defaultbg]) { - bg = &dc.col[defaultfg]; - } else { - colbg.red = ~bg->color.red; - colbg.green = ~bg->color.green; - colbg.blue = ~bg->color.blue; - colbg.alpha = bg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &revbg); - bg = &revbg; - } - } - - if ((base.mode & Gboldfaint) == Gfaint) { - colfg.red = fg->color.red / 2; - colfg.green = fg->color.green / 2; - colfg.blue = fg->color.blue / 2; - colfg.alpha = fg->color.alpha; - XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); - fg = &revfg; - } - - if (base.mode & Greverse) - temp = fg, fg = bg, bg = temp; - - if (base.mode & Gblink && win.mode & Wblink) - fg = bg; - - if (base.mode & Ginvisible) - fg = bg; - - /* Intelligent cleaning up of the borders. */ - if (x == 0) { - xclear(0, (y == 0)? 0 : winy, win.vb, - winy + win.ch + - ((winy + win.ch >= win.vb + win.th)? win.h : 0)); - } - if (winx + width >= win.hb + win.tw) { - xclear(winx + width, (y == 0)? 0 : winy, win.w, - ((winy + win.ch >= win.vb + win.th)? win.h : (winy + win.ch))); - } - if (y == 0) - xclear(winx, 0, winx + width, win.hb); - if (winy + win.ch >= win.hb + win.th) - xclear(winx, winy + win.ch, winx + width, win.h); - - /* Clean up the region we want to draw to. */ - XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); - - /* Set the clip region because Xft is sometimes dirty. */ - r.x = 0, r.y = 0; - r.height = win.ch; - r.width = width; - XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); - - /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); - - /* Render underline and strikethrough. */ - if (base.mode & Gunline) - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); - - if (base.mode & Gstruck) - XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3, width, 1); - - /* Reset clip to none. */ - XftDrawSetClip(xw.draw, 0); -} - -void -xdrawglyph(Letter g, int x, int y) -{ - int numspecs; - XftGlyphFontSpec spec; - - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); - xdrawglyphfontspecs(&spec, g, numspecs, x, y); -} - -void -xdrawcursor(int cx, int cy, Letter g, int ox, int oy, Letter og, Letter *line, int len) -{ - Color drawcol; - - /* remove the old cursor */ - if(selected(ox, oy)) - og.mode ^= Greverse; - xdrawglyph(og, ox, oy); -#if 0 - xdrawline(line, 0, oy, len); -#endif - - if(IS_SET(Whide)) - return; - - /* - * Select the right color for the right mode. - */ - g.mode &= Gbold|Gitalic|Gunline|Gstruck|Gwide; - - if(IS_SET(Wreverse)) { - g.mode |= Greverse; - g.bg = defaultfg; - if (selected(cx, cy)) { - drawcol = dc.col[defaultcs]; - g.fg = defaultrcs; - } else { - drawcol = dc.col[defaultrcs]; - g.fg = defaultcs; - } - } else { - if(selected(cx, cy)) { - g.fg = defaultfg; - g.bg = defaultrcs; - } else { - g.fg = og.bg; //defaultbg; - g.bg = og.fg; //defaultcs; - if (IS_TRUECOL(og.fg)) { - drawcol.color.alpha = 0xffff; - drawcol.color.red = TRUERED(og.fg); - drawcol.color.green = TRUEGREEN(og.fg); - drawcol.color.blue = TRUEBLUE(og.fg); - goto drawnew; - } - } - drawcol = dc.col[g.bg]; - } - -drawnew: - if(IS_SET(Wfocused)) { - switch (win.cursor) { - case 7: /* st extension */ - g.u = 0x2603; /* snowman (U+2603) */ - /* fallthrough */ - case 0: /* Blinking Block */ - case 1: /* Blinking Block (Default) */ - case 2: /* Steady Block */ - xdrawglyph(g, cx, cy); - break; - case 3: /* Blinking Underline */ - case 4: /* Steady Underline */ - XftDrawRect(xw.draw, &drawcol, - win.hb + cx * win.cw, - win.vb + (cy + 1) * win.ch - cursorthickness, - win.cw, cursorthickness); - break; - case 5: /* Blinking bar */ - case 6: /* Steady bar */ - XftDrawRect(xw.draw, &drawcol, - win.hb + cx * win.cw, - win.vb + cy * win.ch, - cursorthickness, win.ch); - break; - } - } else { - XftDrawRect(xw.draw, &drawcol, - win.hb + cx * win.cw, - win.vb + cy * win.ch, - win.cw - 1, 1); - XftDrawRect(xw.draw, &drawcol, - win.hb + cx * win.cw, - win.vb + cy * win.ch, - 1, win.ch - 1); - XftDrawRect(xw.draw, &drawcol, - win.hb + (cx + 1) * win.cw - 1, - win.vb + cy * win.ch, - 1, win.ch - 1); - XftDrawRect(xw.draw, &drawcol, - win.hb + cx * win.cw, - win.vb + (cy + 1) * win.ch - 1, - win.cw, 1); - } -} - -void -xsetenv(void) -{ - char buf[sizeof(long) * 8 + 1]; - - snprintf(buf, sizeof(buf), "%lu", xw.win); - setenv("WINDOWID", buf, 1); -} - -void -xsettitle(char *p) -{ - XTextProperty prop; - DEFAULT(p, opt_title); - - Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, - &prop); - XSetWMName(xw.dpy, xw.win, &prop); - XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); - XFree(prop.value); -} - -int -xstartdraw(void) -{ - return IS_SET(Wvisible); -} - -void -xdrawline(Letter *line, int x1, int y1, int x2) -{ - int i, x, ox, numspecs; - Letter base, new; - XftGlyphFontSpec *specs = xw.specbuf; - - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); - i = ox = 0; - for (x = x1; x < x2 && i < numspecs; x++) { - new = line[x]; - if (new.mode == Gwdummy) - continue; - if (selected(x, y1)) - new.mode ^= Greverse; - if (i > 0 && GLYPHCMP(base, new)) { - xdrawglyphfontspecs(specs, base, i, ox, y1); - specs += i; - numspecs -= i; - i = 0; - } - if (i == 0) { - ox = x; - base = new; - } - i++; - } - if (i > 0) - xdrawglyphfontspecs(specs, base, i, ox, y1); -} - -void -xfinishdraw(void) -{ - XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, - win.h, 0, 0); - XSetForeground(xw.dpy, dc.gc, - dc.col[IS_SET(Wreverse)? - defaultfg : defaultbg].pixel); -} - -void -xximspot(int x, int y) -{ - if (xw.ime.xic == nil) - return; - - xw.ime.spot.x = borderpx + x * win.cw; - xw.ime.spot.y = borderpx + (y + 1) * win.ch; - - XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, nil); -} - -void -expose(XEvent *ev) -{ - redraw(); -} - -void -visibility(XEvent *ev) -{ - XVisibilityEvent *e = &ev->xvisibility; - - MODBIT(win.mode, e->state != VisibilityFullyObscured, Wvisible); -} - -void -unmap(XEvent *ev) -{ - win.mode &= ~Wvisible; -} - -void -xsetpointermotion(int set) -{ - MODBIT(xw.attrs.event_mask, set, PointerMotionMask); - XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); -} - -void -xsetmode(int set, uint flags) -{ - int mode = win.mode; - MODBIT(win.mode, set, flags); - if ((win.mode & Wreverse) != (mode & Wreverse)) - redraw(); -} - -int -xsetcursor(int cursor) -{ - if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ - return 1; - win.cursor = cursor; - return 0; -} - -void -xseturgency(int add) -{ - XWMHints *h = XGetWMHints(xw.dpy, xw.win); - - MODBIT(h->flags, add, XUrgencyHint); - XSetWMHints(xw.dpy, xw.win, h); - XFree(h); -} - -void -xbell(void) -{ - if (!(IS_SET(Wfocused))) - xseturgency(1); - if (bellvolume) - XkbBell(xw.dpy, xw.win, bellvolume, (Atom)nil); -} - -void -focus(XEvent *ev) -{ - XFocusChangeEvent *e = &ev->xfocus; - - if (e->mode == NotifyGrab) - return; - - if (ev->type == FocusIn) { - if (xw.ime.xic) - XSetICFocus(xw.ime.xic); - win.mode |= Wfocused; - xseturgency(0); - if (IS_SET(Wfocus)) - ttywrite("\033[I", 3, 0); - } else { - if (xw.ime.xic) - XUnsetICFocus(xw.ime.xic); - win.mode &= ~Wfocused; - if (IS_SET(Wfocus)) - ttywrite("\033[O", 3, 0); - } -} - -int -match(uint mask, uint state) -{ - return mask == XK_ANY_MOD || mask == (state & ~ignoremod); -} - -char* -kmap(KeySym k, uint state) -{ - Key *kp; - int i; - - /* Check for mapped keys out of X11 function keys. */ - for (i = 0; i < arrlen(mappedkeys); i++) { - if (mappedkeys[i] == k) - break; - } - if (i == arrlen(mappedkeys)) { - if ((k & 0xFFFF) < 0xFD00) - return nil; - } - - for (kp = key; kp < key + arrlen(key); kp++) { - if (kp->k != k) - continue; - - if (!match(kp->mask, state)) - continue; - - if (IS_SET(Wappkeypad) ? kp->appkey < 0 : kp->appkey > 0) - continue; - if (IS_SET(Wnumlock) && kp->appkey == 2) - continue; - - if (IS_SET(Wappcursor) ? kp->appcursor < 0 : kp->appcursor > 0) - continue; - - return kp->s; - } - - return nil; -} - -void -kpress(XEvent *ev) -{ - XKeyEvent *e = &ev->xkey; - KeySym ksym; - char buf[64], *customkey; - int len; - rune c; - Status status; - Shortcut *bp; - - if (IS_SET(Wkbdblock)) - return; - - if (xw.ime.xic) - len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); - else - len = XLookupString(e, buf, sizeof buf, &ksym, nil); - /* 1. shortcuts */ - for (bp = shortcuts; bp < shortcuts + arrlen(shortcuts); bp++) { - if (ksym == bp->keysym && match(bp->mod, e->state)) { - bp->func(&(bp->arg)); - return; - } - } - - /* 2. custom keys from config.h */ - if ((customkey = kmap(ksym, e->state))) { - ttywrite(customkey, strlen(customkey), 1); - return; - } - - /* 3. composed string from input method */ - if (len == 0) - return; - if (len == 1 && e->state & Mod1Mask) { - if (IS_SET(W8bit)) { - if (*buf < 0177) { - c = *buf | 0x80; - len = utf8·encode(&c, buf); - } - } else { - buf[1] = buf[0]; - buf[0] = '\033'; - len = 2; - } - } - ttywrite(buf, len, 1); -} - -void -cmessage(XEvent *e) -{ - /* - * See xembed specs - * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html - */ - if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { - if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { - win.mode |= Wfocused; - xseturgency(0); - } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { - win.mode &= ~Wfocused; - } - } else if (e->xclient.data.l[0] == xw.wmdeletewin) { - ttyhangup(); - exit(0); - } -} - -void -resize(XEvent *e) -{ - if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) - return; - - cresize(e->xconfigure.width, e->xconfigure.height); -} - -void -run(void) -{ - XEvent ev; - int w = win.w, h = win.h; - fd_set rfd; - int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; - struct timespec seltv, *tv, now, lastblink, trigger; - double timeout; - - /* Waiting for window mapping */ - do { - XNextEvent(xw.dpy, &ev); - /* - * This XFilterEvent call is required because of XOpenIM. It - * does filter out the key event and some client message for - * the input method too. - */ - if (XFilterEvent(&ev, None)) - continue; - if (ev.type == ConfigureNotify) { - w = ev.xconfigure.width; - h = ev.xconfigure.height; - } - } while (ev.type != MapNotify); - - ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); - cresize(w, h); - - for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { - FD_ZERO(&rfd); - FD_SET(ttyfd, &rfd); - FD_SET(xfd, &rfd); - - if (XPending(xw.dpy)) - timeout = 0; /* existing events might not set xfd */ - - seltv.tv_sec = timeout / 1E3; - seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); - tv = timeout >= 0 ? &seltv : nil; - - if (pselect(MAX(xfd, ttyfd)+1, &rfd, nil, nil, tv, nil) < 0) { - if (errno == EINTR) - continue; - fatal("select failed: %s\n", strerror(errno)); - } - clock_gettime(CLOCK_MONOTONIC, &now); - - if (FD_ISSET(ttyfd, &rfd)) - ttyread(); - - xev = 0; - while (XPending(xw.dpy)) { - xev = 1; - XNextEvent(xw.dpy, &ev); - if (XFilterEvent(&ev, None)) - continue; - if (handler[ev.type]) - (handler[ev.type])(&ev); - } - - /* - * To reduce flicker and tearing, when new content or event - * triggers drawing, we first wait a bit to ensure we got - * everything, and if nothing new arrives - we draw. - * We start with trying to wait minlatency ms. If more content - * arrives sooner, we retry with shorter and shorter periods, - * and eventually draw even without idle after maxlatency ms. - * Typically this results in low latency while interacting, - * maximum latency intervals during `cat huge.txt`, and perfect - * sync with periodic updates from animations/key-repeats/etc. - */ - if (FD_ISSET(ttyfd, &rfd) || xev) { - if (!drawing) { - trigger = now; - drawing = 1; - } - timeout = (maxlatency - TIMEDIFF(now, trigger)) \ - / maxlatency * minlatency; - if (timeout > 0) - continue; /* we have time, try to find idle */ - } - - /* idle detected or maxlatency exhausted -> draw */ - timeout = -1; - if (blinktimeout && tattrset(Gblink)) { - timeout = blinktimeout - TIMEDIFF(now, lastblink); - if (timeout <= 0) { - if (-timeout > blinktimeout) /* start visible */ - win.mode |= Wblink; - win.mode ^= Wblink; - tsetdirtattr(Gblink); - lastblink = now; - timeout = blinktimeout; - } - } - - draw(); - XFlush(xw.dpy); - drawing = 0; - } -} - -void -usage(void) -{ - fatal("usage: %s [-aiv] [-c class] [-f font] [-A alpha] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid]" - " [[-e] command [args ...]]\n" - " %s [-aiv] [-c class] [-f font] [-g geometry]" - " [-n name] [-o file]\n" - " [-T title] [-t title] [-w windowid] -l line" - " [stty_args ...]\n", argv0, argv0); -} - -int -main(int argc, char *argv[]) -{ - xw.l = xw.t = 0; - xw.isfixed = False; - xsetcursor(cursorshape); - - ARGBEGIN { - case 'a': - allowaltscreen = 0; - break; - case 'A': - opt_alpha = EARGF(usage()); - break; - case 'c': - opt_class = EARGF(usage()); - break; - case 'e': - if (argc > 0) - --argc, ++argv; - goto run; - case 'f': - opt_font = EARGF(usage()); - break; - case 'g': - xw.gm = XParseGeometry(EARGF(usage()), - &xw.l, &xw.t, &cols, &rows); - break; - case 'i': - xw.isfixed = 1; - break; - case 'o': - opt_io = EARGF(usage()); - break; - case 'l': - opt_line = EARGF(usage()); - break; - case 'n': - opt_name = EARGF(usage()); - break; - case 't': - case 'T': - opt_title = EARGF(usage()); - break; - case 'w': - opt_embed = EARGF(usage()); - break; - case 'v': - fatal("%s " VERSION "\n", argv0); - break; - default: - usage(); - } ARGEND; - -run: - if (argc > 0) /* eat all remaining arguments */ - opt_cmd = argv; - - if (!opt_title) - opt_title = (opt_line || !opt_cmd) ? "term" : opt_cmd[0]; - - setlocale(LC_CTYPE, ""); - XSetLocaleModifiers(""); - - cols = MAX(cols, 1); - rows = MAX(rows, 1); - - tnew(cols, rows); - xinit(cols, rows); - xsetenv(); - selinit(); - - run(); - - return 0; -} diff --git a/sys/cmd/walk/rules.mk b/sys/cmd/walk/rules.mk deleted file mode 100644 index 5b9192d..0000000 --- a/sys/cmd/walk/rules.mk +++ /dev/null @@ -1,13 +0,0 @@ -include share/push.mk - -# Local sources -SRCS_$(d) := $(d)/walk.c -BINS_$(d) := $(d)/walk - -include share/paths.mk - -# Local rules -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/walk/walk.c b/sys/cmd/walk/walk.c deleted file mode 100644 index c68d6e0..0000000 --- a/sys/cmd/walk/walk.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include - -static char buf[4*1024], *c = buf; /* should be greater or equal to PATH_MAX */ - -static -void -flush(void) -{ - *c = 0; - puts(buf); - c = buf; -} - -static -int -print(void *data, char *rel, char *abs, io·Stat *info) -{ -copy: - while (*abs && c < (arrend(buf)-2)) - *c++ = *abs++; - - if (*abs) { - flush(); - goto copy; - } - *c++ = '\n'; - - return 0; -} - -static -void -usage(void) -{ - fprintf(stderr, "usage: walk [-dlpv] file ...\n"); - exit(1); -} - -int -main(int argc, char *argv[]) -{ - int i, f = fs·nolinks, err, max = 0; - char *p; - static fs·Walker walker; - - ARGBEGIN{ - case 'd': - max = atoi(ARGF()); - break; - case 'l': - f ^= fs·nolinks; - break; - case 'p': - f |= fs·preorder; - break; - case 'v': - f |= fs·verbose; - break; - default: - usage(); - }ARGEND; - - walker.flags = f; - walker.func = print; - walker.data = nil; - walker.max = max; - - if (argc == 0) { - fs·init(&walker, ""); - fs·walk(&walker); - return(err = walker.err); - } else { - err = 0; - for (i=0; ix, server.cursor.dot->y))) - return; - - floating(server.grab.client, 1); -} - -void -move_client(Arg *arg) -{ - grab_client(); - server.cursor.mode = CursorMove; - - server.grab.x = server.cursor.dot->x - server.grab.client->geometry.x; - server.grab.y = server.cursor.dot->y - server.grab.client->geometry.y; - wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "fleur", server.cursor.dot); -} - -void -float_client(Arg *arg) -{ - Client *client = selected_client(); - wlr_log(WLR_DEBUG, "client selected = %lx", (uintptr)client); - if(!client) - return; - - floating(client, client->isfloating ? 0 : 1); -} - -void -resize_client(Arg *arg) -{ - double x, y; - struct wlr_box geometry; - - grab_client(); - server.cursor.mode = CursorResize; - - wlr_xdg_surface_get_geometry(server.grab.client->xdg, &geometry); - - x = server.grab.client->geometry.x + geometry.x + geometry.width; - y = server.grab.client->geometry.y + geometry.y + geometry.height; - - server.grab.x = server.cursor.dot->x - x; - server.grab.y = server.cursor.dot->y - y; - - server.grab.box = geometry; - server.grab.box.x += server.grab.client->geometry.x; - server.grab.box.y += server.grab.client->geometry.y; -} - -// ----------------------------------------------------------------------- -// core - -static inline -void -activate(struct wlr_surface *surface, int state) -{ -} - -void -focus(Client *client, int lift) -{ - struct wlr_xdg_surface *xdg; - struct wlr_surface *old, *new; - struct wlr_keyboard *keyboard; - - if(!client) { - wlr_seat_keyboard_notify_clear_focus(server.input.seat); - return; - } - - old = server.input.seat->keyboard_state.focused_surface; - - if(lift) { - wl_list_remove(&client->stack); - wl_list_insert(&server.client.stack, &client->stack); - } - - new = client->xdg->surface; - if(old==new) - return; - - wl_list_remove(&client->focus); - wl_list_insert(&server.client.focus, &client->focus); - server.monitor.selected = client->monitor; - client->isurgent = 0; - - if(old) { - if(wlr_surface_is_xdg_surface(old)) { - xdg = wlr_xdg_surface_from_wlr_surface(old); - wlr_xdg_toplevel_set_activated(xdg, false); - } - } - - keyboard = wlr_seat_get_keyboard(server.input.seat); - - wlr_seat_keyboard_notify_enter(server.input.seat, new, - keyboard->keycodes, - keyboard->num_keycodes, - &keyboard->modifiers - ); - - wlr_xdg_toplevel_set_activated(client->xdg, true); -} - -Client* -client_at(double x, double y) -{ - Client *client; - wl_list_for_each(client, &server.client.stack, stack) - if(VISIBLE_ON(client, client->monitor) && wlr_box_contains_point(&client->geometry, x, y)) - return client; - return nil; -} - -static -int -has(Client *client, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) -{ - double x, y, vsx = lx - client->geometry.x, vsy = ly - client->geometry.y; - struct wlr_surface *find = nil; - - find = wlr_xdg_surface_surface_at(client->xdg, vsx, vsy, &x, &y); - if(find) { - *sx = x; - *sy = y; - *surface = find; - return true; - } - - return false; -} - -struct wlr_surface * -client_surface_at(Client *client, double cx, double cy, double *sx, double *sy) -{ - return wlr_xdg_surface_surface_at(client->xdg, cx, cy, sx, sy); -} - - -static -void -constrain(Client *client, struct wlr_box *box) -{ - client->geometry.width = MAX(1, client->geometry.width); - client->geometry.height = MAX(1, client->geometry.height); - - if(client->geometry.x >= box->x + box->width) - client->geometry.x = box->x + box->width - client->geometry.width; - if(client->geometry.y >= box->y + box->height) - client->geometry.y = box->y + box->height - client->geometry.height; - if(client->geometry.x + client->geometry.width + 2*client->border <= box->x) - client->geometry.x = box->x; - if(client->geometry.y + client->geometry.height + 2*client->border <= box->y) - client->geometry.y = box->y; -} - -void -resize(Client *client, int x, int y, int w, int h, int interact) -{ - struct wlr_box *box = interact ? &server.monitor.geometry : &client->monitor->window; - - client->geometry.x = x; - client->geometry.y = y; - client->geometry.width = w; - client->geometry.height = h; - - constrain(client, box); - - client->resize = wlr_xdg_toplevel_set_size(client->xdg, - client->geometry.width - 2*client->border, - client->geometry.height - 2*client->border - ); -} - -void -attach(Client *client, Monitor *monitor, uint tags) -{ - Monitor *old = client->monitor; - if(old == monitor) - return; - - client->monitor = monitor; - - if(old) { - wlr_surface_send_leave(client->xdg->surface, old->output); - arrange(old); - } - - if(monitor) { - /* make sure window actually overlaps with the monitor */ - constrain(client, &monitor->geometry); - wlr_surface_send_enter(client->xdg->surface, monitor->output); - client->tags = tags ? tags : monitor->tag.set[monitor->tag.selected]; - arrange(monitor); - } - - focus(focused_client(server.monitor.selected), 1); -} - -void -rules(Client *client) -{ - /* rule matching */ - Rule *rule; - uint i, tags; - char *id, *title; - Monitor *monitor, *it; - - monitor = server.monitor.selected; - - if (!(id=client->xdg->toplevel->app_id)) - id = broken; - if (!(title=client->xdg->toplevel->title)) - title = broken; - - for(tags=0, rule=cfg·rule; rule != cfg·endrule; ++rule) { - if ((!rule->title || strstr(title, rule->title)) - && (!rule->id || strstr(id, rule->id))) { - client->isfloating = rule->isfloating; - tags |= rule->tags; - i = 0; - wl_list_for_each(it, &server.monitor.list, link) - if(rule->monitor == i++) - monitor = it; - } - } - - attach(client, monitor, tags); -} - -void -floating(Client *client, int state) -{ - wlr_log(WLR_DEBUG, "client %lx, floating = %d", (uintptr)client, state); - client->isfloating = state; - arrange(client->monitor); -} - -Client * -selected_client(void) -{ - Client *client = wl_container_of(server.client.focus.next, client, focus); - if(wl_list_empty(&server.client.focus) || !VISIBLE_ON(client, server.monitor.selected)) - return nil; - return client; -} - -void -request_activate(struct wl_listener *l, void *data) -{ - struct wlr_xdg_activation_v1_request_activate_event *event = data; - Client *client; - - if (!wlr_surface_is_xdg_surface(event->surface)) - return; - - client = wlr_xdg_surface_from_wlr_surface(event->surface)->data; - if(client != selected_client()) - client->isurgent = 1; -} diff --git a/sys/cmd/wm/config.h b/sys/cmd/wm/config.h deleted file mode 100644 index 1f5ba85..0000000 --- a/sys/cmd/wm/config.h +++ /dev/null @@ -1,70 +0,0 @@ -/* appearance */ -CONFIG(int, sloppyfocus, 1); -CONFIG(int, borderpixel, 1); -CONFIG(float, rootcolor[], {0.3, 0.3, 0.3, 1.0}); -CONFIG(float, bordercolor[], {0.5, 0.5, 0.5, 1.0}); -CONFIG(float, focuscolor[], {1.0, 0.0, 0.0, 1.0}); - -/* sampling */ -CONFIG(int, repeat_rate, 25); -CONFIG(int, repeat_delay, 600); - -/* tags */ -CONFIG(char*, tags[], { "1", "2", "3", "4", "5", "6", "7", "8", "9" }); - -/* application specific rules */ -CONFIG(Rule, rule[], { - /* app_id title tags mask isfloating monitor */ - /* examples: - { "Gimp", nil, 0, 1, -1 }, - { "firefox", nil, 1 << 8, 0, -1 }, - */ -}); -CONFIG(Rule*, endrule, arrend(cfg·rule)); - -/* commands */ -CONFIG(char*, termcommand[], { "alacritty", nil }); -CONFIG(char*, menucommand[], { "dmenu-wl_run", nil }); - -/* layouts */ -CONFIG(Layout, layouts[], { - /* symbol arrange */ - { "[]=", tile }, - { "><>", nil }, /* no layout function means floating behavior */ -}); -CONFIG(Layout*, endlayout, arrend(cfg·layouts)); - -/* monitors - * The order in which monitors are defined determines their position. - * non-configured monitors are always added to the left. */ -CONFIG(MonitorRule, monitorrule[], { - /* name layout, x, y, scale, transform master */ - { nil, &cfg·layouts[0], 0, 0, 1, WL_OUTPUT_TRANSFORM_NORMAL, {0.55, 1} }, -}); -CONFIG(MonitorRule*, endmonitorrule, arrend(cfg·monitorrule)); - -/* keybindings */ -#define MODKEY WLR_MODIFIER_ALT -#define MOD(a) WLR_MODIFIER_##a -#define KEY(a) XKB_KEY_##a - -CONFIG(Key, binding[], { - /* modifier key function argument */ - { MODKEY, KEY(Return), spawn, {.v = cfg·termcommand} }, - { MODKEY, KEY(d), spawn, {.v = cfg·menucommand} }, - { MODKEY|MOD(SHIFT), KEY(Q), quit, {.v = nil} }, -}); -CONFIG(Key*, endbinding, arrend(cfg·binding)); - -#undef MOD -#undef KEY - -/* mouse buttons */ -CONFIG(Button, button[], { - { MODKEY, BTN_LEFT, move_client, {0} }, - { MODKEY, BTN_MIDDLE, float_client, {0} }, - { MODKEY, BTN_RIGHT, resize_client, {0} }, -}); -CONFIG(Button*, endbutton, arrend(cfg·button)); - -#undef MODKEY diff --git a/sys/cmd/wm/input.c b/sys/cmd/wm/input.c deleted file mode 100644 index 4c6bfd4..0000000 --- a/sys/cmd/wm/input.c +++ /dev/null @@ -1,316 +0,0 @@ -#include "wm.h" - -// ----------------------------------------------------------------------- -// keyboard - -static -void -keymodifier(struct wl_listener *l, void *data) -{ - Keyboard *keyboard = wl_container_of(l, keyboard, event.modify); - - wlr_seat_set_keyboard(server.input.seat, keyboard->device); - wlr_seat_keyboard_notify_modifiers(server.input.seat, &keyboard->device->keyboard->modifiers); -} - -static -int -keybinding(uint32 modifier, xkb_keysym_t sym) -{ - Key *key; - - for(key=cfg·binding; key!=cfg·endbinding; ++key) { - if(modifier == key->modifier && sym == key->sym && key->action){ - key->action(&key->arg); - return 1; - } - } - return 0; -} - -static -void -keypress(struct wl_listener *l, void *data) -{ - int i,h,n; - uint32 keycode, modifier; - const xkb_keysym_t *syms; - struct Keyboard *keyboard = wl_container_of(l, keyboard, event.press); - struct wlr_event_keyboard_key *event = data; - - keycode = event->keycode + 8; - - h = 0; - n = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); - - modifier = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if(event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - for(i=0; idevice); - wlr_seat_keyboard_notify_key(server.input.seat, event->time_msec, event->keycode, event->state); - } -} - -static -void -free_keyboard(struct wl_listener *l, void *data) -{ - struct wlr_input_device *device = data; - Keyboard *keyboard = device->data; - - /* XXX: debug - wl_list_remove(&keyboard->link); - wl_list_remove(&keyboard->event.modify.link); - wl_list_remove(&keyboard->event.press.link); - wl_list_remove(&keyboard->event.destroy.link); - - free(keyboard); - */ -} - -static -void -make_keyboard(struct wlr_input_device *device) -{ - Keyboard *keyboard; - struct xkb_context *context; - struct xkb_keymap *keymap; - - keyboard = device->data = calloc(1, sizeof(*keyboard)); - keyboard->device = device; - - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - keymap = xkb_keymap_new_from_names(context, nil, XKB_KEYMAP_COMPILE_NO_FLAGS); - - wlr_keyboard_set_keymap(device->keyboard, keymap); - - xkb_keymap_unref(keymap); - xkb_context_unref(context); - - wlr_keyboard_set_repeat_info(device->keyboard, cfg·repeat_rate, cfg·repeat_delay); - - keyboard->event.modify.notify = keymodifier; - wl_signal_add(&device->keyboard->events.modifiers, &keyboard->event.modify); - - keyboard->event.press.notify = keypress; - wl_signal_add(&device->keyboard->events.key, &keyboard->event.press); - - keyboard->event.destroy.notify = free_keyboard; - wl_signal_add(&device->keyboard->events.destroy, &keyboard->event.destroy); - - wlr_seat_set_keyboard(server.input.seat, device); - - wl_list_insert(&server.input.keyboards, &keyboard->link); -} - -// ----------------------------------------------------------------------- -// cursor - -static -void -focus_surface(Client *client, struct wlr_surface *surface, double sx, double sy, uint32 time) -{ - struct timespec now; - int lift = time; - - if(client && !surface) - surface = client->xdg->surface; - - if(!surface){ - wlr_seat_pointer_notify_clear_focus(server.input.seat); - return; - } - - if(!time) { - clock_gettime(CLOCK_MONOTONIC, &now); - time = now.tv_sec * 1000 + now.tv_nsec / 1000000; - } - - if(surface == server.input.seat->pointer_state.focused_surface) { - wlr_seat_pointer_notify_motion(server.input.seat, time, sx, sy); - return; - } - - wlr_seat_pointer_notify_enter(server.input.seat, surface, sx, sy); - - if(cfg·sloppyfocus && lift) - focus(client, 0); -} - -void -notify_move(uint32 time) -{ - double sx, sy; - Client *client; - struct wlr_box box; - struct wlr_surface *surface; - - if(time) { - wlr_idle_notify_activity(server.input.idle, server.input.seat); - if(cfg·sloppyfocus) - server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); - } - - if(server.cursor.mode == CursorMove) { - resize(server.grab.client, - server.cursor.dot->x - server.grab.x, - server.cursor.dot->y - server.grab.y, - server.grab.client->geometry.width, - server.grab.client->geometry.height, - 1 - ); - return; - } - - if(server.cursor.mode == CursorResize) { - wlr_xdg_surface_get_geometry(server.grab.client->xdg, &box); - resize(server.grab.client, - server.grab.box.x - box.x, - server.grab.box.y - box.y, - server.cursor.dot->x - server.grab.x - server.grab.box.x, - server.cursor.dot->y - server.grab.y - server.grab.box.y, - 1 - ); - return; - } - - /* otherwise, find the client under the pointer and send the event along. */ - client = client_at(server.cursor.dot->x, server.cursor.dot->y); - if(!client) { - wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); - return; - } - - surface = client_surface_at( - client, - server.cursor.dot->x - client->geometry.x - client->border, - server.cursor.dot->y - client->geometry.y - client->border, - &sx, &sy - ); - - focus_surface(client, surface, sx, sy, time); -} - -void -cursor_move(struct wl_listener *l, void *data) -{ - struct wlr_event_pointer_motion *event = data; - wlr_cursor_move(server.cursor.dot, event->device, event->delta_x, event->delta_y); - notify_move(event->time_msec); -} - -void -cursor_move_abs(struct wl_listener *l, void *data) -{ - struct wlr_event_pointer_motion_absolute *event = data; - wlr_cursor_warp_absolute(server.cursor.dot, event->device, event->x, event->y); - notify_move(event->time_msec); -} - -void -cursor_button(struct wl_listener *l, void *data) -{ - Client *client; - uint32 modifier; - Button *button; - struct wlr_keyboard *keyboard; - struct wlr_event_pointer_button *event = data; - - wlr_idle_notify_activity(server.input.idle, server.input.seat); - - switch(event->state) { - case WLR_BUTTON_PRESSED: - if((client=client_at(server.cursor.dot->x, server.cursor.dot->y))) - focus(client,1); - - keyboard = wlr_seat_get_keyboard(server.input.seat); - modifier = wlr_keyboard_get_modifiers(keyboard); - for(button=cfg·button; button != cfg·endbutton; ++button) { - if(modifier == button->modifier && event->button == button->code && button->function) { - button->function(&button->arg); - return; - } - } - break; - case WLR_BUTTON_RELEASED: - if(server.cursor.mode != CursorNormal) { - wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); - server.cursor.mode = CursorNormal; - /* Drop the window off on its new monitor */ - server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); - attach(server.grab.client, server.monitor.selected, 0); - return; - } - } - - wlr_seat_pointer_notify_button(server.input.seat, event->time_msec, event->button, event->state); -} - -void -cursor_axis(struct wl_listener *l, void *data) -{ - struct wlr_event_pointer_axis *event = data; - /* Notify the client with pointer focus of the axis event. */ - wlr_seat_pointer_notify_axis(server.input.seat, - event->time_msec, event->orientation, event->delta, - event->delta_discrete, event->source); -} - -void -cursor_frame(struct wl_listener *l, void *data) -{ - wlr_seat_pointer_notify_frame(server.input.seat); -} - -void -request_cursor(struct wl_listener *l, void *data) -{ - struct wlr_seat_pointer_request_set_cursor_event *event = data; - struct wlr_seat_client *focused = server.input.seat->pointer_state.focused_client; - if(focused == event->seat_client) - wlr_cursor_set_surface(server.cursor.dot, event->surface, event->hotspot_x, event->hotspot_y); -} - -void -request_set_selection(struct wl_listener *l, void *data) -{ - struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(server.input.seat, event->source, event->serial); -} - -static -void -make_pointer(struct wlr_input_device *device) -{ - wlr_cursor_attach_input_device(server.cursor.dot, device); -} - -// ----------------------------------------------------------------------- -// generic input - -void -make_input(struct wl_listener *l, void *data) -{ - uint32 capability; - struct wlr_input_device *device = data; - - switch(device->type) { - case WLR_INPUT_DEVICE_KEYBOARD: - make_keyboard(device); - break; - case WLR_INPUT_DEVICE_POINTER: - make_pointer(device); - /* fallthrough */ - default: - break; - } - - capability = WL_SEAT_CAPABILITY_POINTER; - if(!wl_list_empty(&server.input.keyboards)) - capability |= WL_SEAT_CAPABILITY_KEYBOARD; - wlr_seat_set_capabilities(server.input.seat, capability); -} diff --git a/sys/cmd/wm/layer.c b/sys/cmd/wm/layer.c deleted file mode 100644 index bfac744..0000000 --- a/sys/cmd/wm/layer.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "wm.h" - -static -void -map(struct wl_listener *l, void *data) -{ - Layer *layer = wl_container_of(l, layer, event.map); - wlr_surface_send_enter(layer->surface->surface, layer->surface->output); - notify_move(0); -} - -static -void -finalize(Layer *layer) -{ - layer->surface->mapped = 0; - if (layer->surface->surface == server.input.seat->keyboard_state.focused_surface) - focus(selected_client(), 1); - notify_move(0); -} - -static -void -unmap(struct wl_listener *l, void *data) -{ - Layer *layer = wl_container_of(l, layer, event.unmap); - finalize(layer); -} - -static -void -destroy(struct wl_listener *l, void *data) -{ - Monitor *monitor; - Layer *layer = wl_container_of(l, layer, event.destroy); - - if (layer->surface->mapped) - finalize(layer); - - wl_list_remove(&layer->link); - wl_list_remove(&layer->event.destroy.link); - wl_list_remove(&layer->event.map.link); - wl_list_remove(&layer->event.unmap.link); - wl_list_remove(&layer->event.commit.link); - - if(layer->surface->output) { - monitor = layer->surface->output->data; - if(monitor) - stratify(monitor); - layer->surface->output = nil; - } - free(layer); -} - -static -void -commit(struct wl_listener *l, void *data) -{ - Monitor *monitor; - Layer *layer = wl_container_of(l, layer, event.commit); - struct wlr_layer_surface_v1 *surface = layer->surface; - struct wlr_output *output = surface->output; - - if(!output) - return; - - monitor = output->data; - stratify(monitor); - - if (layer->type != surface->current.layer) { - wl_list_remove(&layer->link); - wl_list_insert(&monitor->layer[surface->current.layer], &layer->link); - layer->type = surface->current.layer; - } -} - -void -make_layer_surface(struct wl_listener *l, void *data) -{ - Layer *layer; - Monitor *monitor; - struct wlr_layer_surface_v1_state state; - struct wlr_layer_surface_v1 *surface = data; - - if(!surface->output) - surface->output = server.monitor.selected->output; - - layer = surface->data = calloc(1, sizeof(*layer)); - layer->surface = surface; - - layer->event.map.notify = map; - wl_signal_add(&surface->events.map, &layer->event.map); - layer->event.unmap.notify = unmap; - wl_signal_add(&surface->events.unmap, &layer->event.unmap); - layer->event.destroy.notify = destroy; - wl_signal_add(&surface->events.destroy, &layer->event.destroy); - layer->event.commit.notify = commit; - wl_signal_add(&surface->surface->events.commit, &layer->event.commit); - - monitor = surface->output->data; - wl_list_insert(&monitor->layer[surface->client_pending.layer], &layer->link); - - state = surface->current; - surface->current = surface->client_pending; - stratify(monitor); - surface->current = state; -} diff --git a/sys/cmd/wm/main.c b/sys/cmd/wm/main.c deleted file mode 100644 index 2607801..0000000 --- a/sys/cmd/wm/main.c +++ /dev/null @@ -1,177 +0,0 @@ -#include "wm.h" - -Server server = { - .event = { - .make_input = { .notify = make_input }, - .make_monitor = { .notify = make_monitor }, - .make_xdg_surface = { .notify = make_xdg_surface }, - .make_layer_surface = { .notify = make_layer_surface }, - - .monitor_change = { .notify = monitor_change }, - .monitor_test = { .notify = monitor_test }, - .monitor_apply = { .notify = monitor_apply }, - - .cursor_move = { .notify = cursor_move }, - .cursor_move_abs = { .notify = cursor_move_abs }, - .cursor_button = { .notify = cursor_button }, - .cursor_axis = { .notify = cursor_axis }, - .cursor_frame = { .notify = cursor_frame }, - - .request_activate = { .notify = request_activate }, - .request_cursor = { .notify = request_cursor }, - .request_set_selection = { .notify = request_set_selection }, - }, -}; - -// ----------------------------------------------------------------------- -// helper functions - -static inline -void -init(void) -{ - /* compositor initialization */ - server.display = wl_display_create(); - server.backend = wlr_backend_autocreate(server.display); - server.renderer = wlr_backend_get_renderer(server.backend); - server.present = wlr_presentation_create(server.display, server.backend); - - wlr_renderer_init_wl_display(server.renderer, server.display); - - wlr_compositor_create(server.display, server.renderer); - wlr_export_dmabuf_manager_v1_create(server.display); - wlr_screencopy_manager_v1_create(server.display); - wlr_data_control_manager_v1_create(server.display); - wlr_data_device_manager_create(server.display); - wlr_gamma_control_manager_v1_create(server.display); - wlr_primary_selection_v1_device_manager_create(server.display); - wlr_viewporter_create(server.display); - - server.activate = wlr_xdg_activation_v1_create(server.display); - wl_signal_add(&server.activate->events.request_activate, &server.event.request_activate); - - wlr_data_device_manager_create(server.display); - - server.monitor.layout = wlr_output_layout_create(); - wl_signal_add(&server.monitor.layout->events.change, &server.event.monitor_change); - wlr_xdg_output_manager_v1_create(server.display, server.monitor.layout); - - wl_list_init(&server.monitor.list); - wl_signal_add(&server.backend->events.new_output, &server.event.make_monitor); - - server.monitor.manager = wlr_output_manager_v1_create(server.display); - wl_signal_add(&server.monitor.manager->events.test, &server.event.monitor_test); - wl_signal_add(&server.monitor.manager->events.apply, &server.event.monitor_apply); - - /* shell initialization */ - wl_list_init(&server.client.list); - wl_list_init(&server.client.stack); - wl_list_init(&server.client.focus); - - server.shell.xdg = wlr_xdg_shell_create(server.display); - wl_signal_add(&server.shell.xdg->events.new_surface, &server.event.make_xdg_surface); - - server.shell.layer = wlr_layer_shell_v1_create(server.display); - wl_signal_add(&server.shell.layer->events.new_surface, &server.event.make_layer_surface); - - wlr_server_decoration_manager_set_default_mode( - wlr_server_decoration_manager_create(server.display), - WLR_SERVER_DECORATION_MANAGER_MODE_SERVER - ); - wlr_xdg_decoration_manager_v1_create(server.display); - - /* input initialization */ - server.cursor.dot = wlr_cursor_create(); - wlr_cursor_attach_output_layout(server.cursor.dot, server.monitor.layout); - - server.cursor.manager = wlr_xcursor_manager_create(nil, 24); - wlr_xcursor_manager_load(server.cursor.manager, 1); - - wl_signal_add(&server.cursor.dot->events.motion, &server.event.cursor_move); - wl_signal_add(&server.cursor.dot->events.motion_absolute, &server.event.cursor_move_abs); - wl_signal_add(&server.cursor.dot->events.button, &server.event.cursor_button); - wl_signal_add(&server.cursor.dot->events.axis, &server.event.cursor_axis); - wl_signal_add(&server.cursor.dot->events.frame, &server.event.cursor_frame); - - wl_list_init(&server.input.keyboards); - wl_signal_add(&server.backend->events.new_input, &server.event.make_input); - - server.input.idle = wlr_idle_create(server.display); - server.input.seat = wlr_seat_create(server.display, "seat0"); - - wl_signal_add(&server.input.seat->events.request_set_cursor, &server.event.request_cursor); - wl_signal_add(&server.input.seat->events.request_set_selection, &server.event.request_set_selection); -} - -static inline -void -fini(void) -{ - wl_display_destroy_clients(server.display); - - wlr_backend_destroy(server.backend); - wlr_xcursor_manager_destroy(server.cursor.manager); - wlr_output_layout_destroy(server.monitor.layout); - wlr_seat_destroy(server.input.seat); - - wl_display_destroy(server.display); -} - -// ----------------------------------------------------------------------- -// main point of entry - -int -usage(void) -{ - fprintf(stderr, "usage: %s [-s startup command]\n", argv0); - return 1; -} - - -int -main(int argc, char *argv[]) -{ - char *socket, *cmd=nil; - - ARGBEGIN{ - case 's': - cmd = ARGF(); - break; - default: - return usage(); - } ARGEND - - if(argc != 0) - return usage(); - - wlr_log_init(WLR_DEBUG, nil); - - init(); - - if(!(socket=(char*)wl_display_add_socket_auto(server.display))) { - wlr_backend_destroy(server.backend); - return 1; - } - - if(!(wlr_backend_start(server.backend))) { - wlr_backend_destroy(server.backend); - wl_display_destroy(server.display); - return 1; - } - - setenv("WAYLAND_DISPLAY", socket, true); - if(cmd) { - if(fork()==0) - execl("/bin/sh", "/bin/sh", "-c", cmd, nil); - } - wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); - - server.monitor.selected = monitor_at(server.cursor.dot->x, server.cursor.dot->y); - wlr_cursor_warp_closest(server.cursor.dot, nil, server.cursor.dot->x, server.cursor.dot->y); - wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); - - wl_display_run(server.display); /* event loop */ - - fini(); - return 0; -} diff --git a/sys/cmd/wm/monitor.c b/sys/cmd/wm/monitor.c deleted file mode 100644 index 93073f3..0000000 --- a/sys/cmd/wm/monitor.c +++ /dev/null @@ -1,386 +0,0 @@ -#include "wm.h" - -/* callbacks */ -void -monitor_change(struct wl_listener *l, void *data) -{ - Monitor *monitor; - struct wlr_output_configuration_v1 *config; - - config = wlr_output_configuration_v1_create(); - server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); - - wl_list_for_each(monitor, &server.monitor.list, link) { - struct wlr_output_configuration_head_v1 *head = - wlr_output_configuration_head_v1_create(config, monitor->output); - - monitor->geometry = monitor->window = *wlr_output_layout_get_box(server.monitor.layout, monitor->output); - - stratify(monitor); - arrange(monitor); - - head->state.enabled = monitor->output->enabled; - head->state.mode = monitor->output->current_mode; - head->state.x = monitor->geometry.x; - head->state.y = monitor->geometry.y; - } - - wlr_output_manager_v1_set_configuration(server.monitor.manager, config); -} - -static -void -trylayout(struct wlr_output_configuration_v1 *config, int force) -{ - int ok; - struct wlr_output_configuration_head_v1 *head; - - ok = 1; - wl_list_for_each(head, &config->heads, link) { - struct wlr_output *output= head->state.output; - wlr_output_enable(output, head->state.enabled); - if (head->state.enabled) { - if (head->state.mode) - wlr_output_set_mode(output, head->state.mode); - else - wlr_output_set_custom_mode( - output, - head->state.custom_mode.width, - head->state.custom_mode.height, - head->state.custom_mode.refresh - ); - - wlr_output_layout_move(server.monitor.layout, output, - head->state.x, head->state.y); - wlr_output_set_transform(output, head->state.transform); - } - - if(!(ok=wlr_output_test(output))) - break; - } - - wl_list_for_each(head, &config->heads, link) { - if(ok && force) - wlr_output_commit(head->state.output); - else - wlr_output_rollback(head->state.output); - } - - if(ok) - wlr_output_configuration_v1_send_succeeded(config); - else - wlr_output_configuration_v1_send_failed(config); - - wlr_output_configuration_v1_destroy(config); -} - -void -monitor_apply(struct wl_listener *l, void *data) -{ - struct wlr_output_configuration_v1 *config = data; - trylayout(config, 1); -} - -void -monitor_test(struct wl_listener *l, void *data) -{ - struct wlr_output_configuration_v1 *config = data; - trylayout(config, 0); -} - -void -make_monitor(struct wl_listener *l, void *data) -{ - int i; - Client *client; - Monitor *monitor; - MonitorRule *rule; - struct wlr_output_mode *mode; - struct wlr_output *output = data; - - /* - * XXX: needed? - if (wl_list_empty(&output->modes)) - return; - */ - - monitor = output->data = calloc(1, sizeof(*monitor)); - monitor->output = output; - - for(i=0; i < arrlen(monitor->layer); i++) - wl_list_init(&monitor->layer[i]); - monitor->tag.set[0] = monitor->tag.set[1] = 1; - - for(rule=cfg·monitorrule; rule != cfg·endmonitorrule; ++rule) { - if(!rule->name || strstr(output->name, rule->name)) { - monitor->master.len = rule->master.len; - monitor->master.frac = rule->master.frac; - - wlr_output_set_scale(output, rule->scale); - wlr_xcursor_manager_load(server.cursor.manager, rule->scale); - monitor->layouts[0] = monitor->layouts[1] = monitor->layout = rule->layout; - - wlr_output_set_transform(output, rule->transform); - break; - } - } - - mode = wlr_output_preferred_mode(output); - wlr_output_set_mode(output, mode); - wlr_output_enable_adaptive_sync(output, true); - - monitor->event.render.notify = render_monitor; - wl_signal_add(&output->events.frame, &monitor->event.render); - monitor->event.destroy.notify = free_monitor; - wl_signal_add(&output->events.destroy, &monitor->event.destroy); - - wlr_output_enable(output, true); - if(!wlr_output_commit(output)) - return; - - wl_list_insert(&server.monitor.list, &monitor->link); - - wlr_output_layout_add(server.monitor.layout, output, rule->x, rule->y); - server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); - - /* update the geometries of all monitors */ - wl_list_for_each(monitor, &server.monitor.list, link) { - /* first monitor in the list = most recently added */ - wl_list_for_each(client, &server.client.list, link) { - if(client->isfloating) - resize(client, client->geometry.x+monitor->window.width, client->geometry.y, - client->geometry.width, client->geometry.height, 0); - } - return; - } -} - -void -free_monitor(struct wl_listener *l, void *data) -{ - int i, len; - Client *client; - struct wlr_output *output = data; - Monitor *monitor = output->data; - - wl_list_remove(&monitor->event.destroy.link); - wl_list_remove(&monitor->event.render.link); - wl_list_remove(&monitor->link); - - wlr_output_layout_remove(server.monitor.layout, monitor->output); - - for(i=0, len=wl_list_length(&server.monitor.list); i < len; i++) { - server.monitor.selected = wl_container_of(server.monitor.list.prev, server.monitor.selected, link); - if(server.monitor.selected->output->enabled) - break; - } - - focus(focused_client(server.monitor.selected), 1); - - /* move closed monitor's clients to newly selected one */ - wl_list_for_each(client, &server.client.list, link) { - if(client->isfloating && client->geometry.x > monitor->geometry.width) - resize(client, - client->geometry.x - monitor->window.width, - client->geometry.y, - client->geometry.width, - client->geometry.height, - 0 - ); - if(client->monitor == monitor) - attach(client, monitor, client->tags); - } - - free(monitor); -} - -/* methods */ -void -arrange(Monitor *monitor) -{ - if(monitor->layout->arrange) - monitor->layout->arrange(monitor); -} - -void -stratum(Monitor *monitor, struct wl_list *list, struct wlr_box *area, int exclusive) -{ - Layer *layer; - struct wlr_box full = monitor->geometry; - - wl_list_for_each(layer, list, link) { - struct wlr_layer_surface_v1 *surface = layer->surface; - struct wlr_layer_surface_v1_state *state = &surface->current; - struct wlr_box bounds; - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height - }; - const uint32 horizontal = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - const uint32 vertical = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - - if (exclusive != (state->exclusive_zone > 0)) - continue; - - bounds = state->exclusive_zone == -1 ? full : *area; - - // horizontal axis - if((state->anchor & horizontal) && box.width == 0) { - box.x = bounds.x; - box.width = bounds.width; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - - // vertical axis - if((state->anchor & vertical) && box.height == 0) { - box.y = bounds.y; - box.height = bounds.height; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - - // margin - if((state->anchor & horizontal) == horizontal) { - box.x += state->margin.left; - box.width -= state->margin.left + state->margin.right; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - - if((state->anchor & vertical) == vertical) { - box.y += state->margin.top; - box.height -= state->margin.top + state->margin.bottom; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if(box.width < 0 || box.height < 0) { - wlr_layer_surface_v1_close(surface); - continue; - } - layer->geometry = box; - - if (state->exclusive_zone > 0) - exclude(area, - state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); - wlr_layer_surface_v1_configure(surface, box.width, box.height); - } -} - -void -stratify(Monitor *monitor) -{ - int i; - Layer *layer; - struct wlr_box area = monitor->geometry; - uint32_t overlays[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server.input.seat); - - // arrange exclusive surfaces from top->bottom - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 1); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 1); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 1); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 1); - - if(memcmp(&area, &monitor->window, sizeof(area))) { - monitor->window = area; - arrange(monitor); - } - - // arrange non-exlusive surfaces from top->bottom - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 0); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 0); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 0); - stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 0); - - // find topmost keyboard interactive layer, if such a layer exists - for(i = 0; i < arrlen(overlays); i++) { - wl_list_for_each_reverse(layer, &monitor->layer[overlays[i]], link) { - if (layer->surface->current.keyboard_interactive && layer->surface->mapped) { - // Deactivate the focused client. - focus(nil, 0); - wlr_seat_keyboard_notify_enter( - server.input.seat, - layer->surface->surface, - keyboard->keycodes, - keyboard->num_keycodes, - &keyboard->modifiers - ); - return; - } - } - } -} - -Client * -focused_client(Monitor *monitor) -{ - Client *client; - wl_list_for_each(client, &server.client.focus, focus) { - if(VISIBLE_ON(client, monitor)) - return client; - } - - return nil; -} - -void -tile(Monitor *monitor) -{ - Client *client; - uint i, n, h, mw, my, ty; - - n = 0; - wl_list_for_each(client, &server.client.list, link) { - if(VISIBLE_ON(client, monitor) && !client->isfloating) - n++; - } - if(!n) return; - - if(n > monitor->master.len) - mw = monitor->master.len ? monitor->window.width * monitor->master.frac : 0; - else - mw = monitor->window.width; - - i = my = ty = 0; - wl_list_for_each(client, &server.client.list, link) { - if(!VISIBLE_ON(client,monitor) || client->isfloating || client->isfullscreen) - continue; - if(i < monitor->master.len) { - h = (monitor->window.height - my) / (MIN(n, monitor->master.len) - i); - resize(client, monitor->window.x, monitor->window.y + my, mw, h, 0); - my += client->geometry.height; - } else { - h = (monitor->window.height - ty) / (n - i); - resize(client, monitor->window.x + mw, monitor->window.y + ty, monitor->window.width - mw, h, 0); - ty += client->geometry.height; - } - i++; - } -} - -Monitor * -monitor_at(double x, double y) -{ - struct wlr_output *output = wlr_output_layout_output_at(server.monitor.layout, x, y); - return output ? output->data : nil; -} diff --git a/sys/cmd/wm/protocol/sync b/sys/cmd/wm/protocol/sync deleted file mode 100755 index 19a728a..0000000 --- a/sys/cmd/wm/protocol/sync +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -for base in wlr-layer-shell-unstable-v1.xml -do - curl https://raw.githubusercontent.com/swaywm/wlroots/master/protocol/$base --output $base -done diff --git a/sys/cmd/wm/render.c b/sys/cmd/wm/render.c deleted file mode 100644 index 1f51804..0000000 --- a/sys/cmd/wm/render.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "wm.h" - -struct Payload -{ - Client *client; - struct wlr_output *output; - struct timespec *when; - int x, y; -}; - -static -void -render(struct wlr_surface *surface, int sx, int sy, void *data) -{ - float matrix[9]; - double x, y; - struct Payload *payload; - - struct wlr_box box; - struct wlr_output *output; - struct wlr_texture *texture; - - enum wl_output_transform transform; - - payload = data; - output = payload->output; - - texture = wlr_surface_get_texture(surface); - if(!texture) - return; - - x = 0, y = 0; - wlr_output_layout_output_coords(server.monitor.layout, output, &x, &y); - - box = (struct wlr_box) { - .x = x + payload->x + sx, - .y = y + payload->y + sy, - .width = surface->current.width, - .height = surface->current.height, - }; - scale_box(&box, output->scale); - - transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); - - wlr_render_texture_with_matrix(server.renderer, texture, matrix, 1); - wlr_surface_send_frame_done(surface, payload->when); - wlr_presentation_surface_sampled_on_output(server.present, surface, output); -} - -static -void -render_layer(struct wl_list *list, struct timespec *now) -{ - Layer *layer; - wl_list_for_each(layer, list, link) { - struct Payload payload= { - .output = layer->surface->output, - .x = layer->geometry.x, - .y = layer->geometry.y, - .when = now, - }; - - wlr_surface_for_each_surface(layer->surface->surface, render, &payload); - } -} - -static -void -render_clients(Monitor *monitor, struct timespec *now) -{ - double x, y; - int i, w, h, bw; - float *color; - - Client *client; - struct wlr_output *output; - struct wlr_box *borders; - struct wlr_surface *surface; - - output = monitor->output; - wl_list_for_each_reverse(client, &server.client.stack, stack) { - if(!VISIBLE_ON(client, client->monitor)) - continue; - if(!wlr_output_layout_intersects(server.monitor.layout, monitor->output, &client->geometry)) - continue; - - surface = client->xdg->surface; - - x = client->geometry.x, y = client->geometry.y; - wlr_output_layout_output_coords(server.monitor.layout, output, &x, &y); - - if((bw=client->border)) { - w = surface->current.width; - h = surface->current.height; - borders = (struct wlr_box[4]) { - {x, y, w+2*bw, bw}, /* top */ - {x, y+bw, bw, h}, /* left */ - {x+bw+w, y+bw, bw, h}, /* right */ - {x, y+bw+h, w+2*bw, bw}, /* bottom */ - }; - - color = (client == server.selected) ? cfg·focuscolor : cfg·bordercolor; - for(i=0; i<4; i++) { - scale_box(&borders[i], output->scale); - wlr_render_rect(server.renderer, &borders[i], color, output->transform_matrix); - } - } - - struct Payload payload = { - .output = output, - .when = now, - - .x = client->geometry.x + client->border, - .y = client->geometry.y + client->border, - }; - - wlr_xdg_surface_for_each_surface(client->xdg, render, &payload); - } -} - -void -render_monitor(struct wl_listener *l, void *data) -{ - int w, h; - Client *client; - Monitor *monitor; - struct timespec now; - - clock_gettime(CLOCK_MONOTONIC, &now); - monitor = wl_container_of(l, monitor, event.render); - - wl_list_for_each(client, &server.client.list, link) { - if(client->resize) { - wlr_surface_send_frame_done(client->xdg->surface, &now); - } - } - - if(!wlr_output_attach_render(monitor->output, nil)) - return; - - wlr_output_effective_resolution(monitor->output, &w, &h); - - /* start of rendering kernel */ - wlr_renderer_begin(server.renderer, w, h); - wlr_renderer_clear(server.renderer, cfg·rootcolor); - - render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); - render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); - - render_clients(monitor, &now); - - render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now); - render_layer(&monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now); - - wlr_output_render_software_cursors(monitor->output, nil); - - wlr_renderer_end(server.renderer); - wlr_output_commit(monitor->output); -} diff --git a/sys/cmd/wm/rules.mk b/sys/cmd/wm/rules.mk deleted file mode 100644 index 5a36b6f..0000000 --- a/sys/cmd/wm/rules.mk +++ /dev/null @@ -1,61 +0,0 @@ -include share/push.mk -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := \ - $(d)/xdg-shell-protocol.c \ - $(d)/wlr-layer-shell-unstable-v1-protocol.c \ - $(d)/util.c \ - $(d)/input.c \ - $(d)/render.c \ - $(d)/layer.c \ - $(d)/xdg.c \ - $(d)/client.c \ - $(d)/monitor.c \ - $(d)/main.c -BINS_$(d) := $(d)/wm - -include share/paths.mk - -# Local rules -include share/dynamic.mk - -$(d)/xdg-shell-protocol.h: - @echo "MK $(notdir $@)";\ - $(WL_SCAN) server-header $(WL_PROTO)/stable/xdg-shell/xdg-shell.xml $@ - -$(d)/xdg-shell-protocol.c: $(d)/xdg-shell-protocol.h - @echo "MK $(notdir $@)";\ - $(WL_SCAN) private-code $(WL_PROTO)/stable/xdg-shell/xdg-shell.xml $@ - -$(d)/wlr-layer-shell-unstable-v1-protocol.h: - @echo "MK $(notdir $@)";\ - $(WL_SCAN) server-header $(dir $@)protocol/wlr-layer-shell-unstable-v1.xml $@ - -$(d)/wlr-layer-shell-unstable-v1-protocol.c: $(d)/wlr-layer-shell-unstable-v1-protocol.h - @echo "MK $(notdir $@)";\ - $(WL_SCAN) private-code $(dir $@)protocol/wlr-layer-shell-unstable-v1.xml $@ - -GENS += \ -$(d)/xdg-shell-protocol.h \ -$(d)/xdg-shell-protocol.c \ -$(d)/wlr-layer-shell-unstable-v1-protocol.h \ -$(d)/wlr-layer-shell-unstable-v1-protocol.c - -$(BINS_$(d)): TCINCS = \ - -I sys/cmd/wm - -$(BINS_$(d)): TCFLAGS = \ - `$(PKG) --cflags wlroots` \ - `$(PKG) --cflags wayland-server` \ - `$(PKG) --cflags xkbcommon` - -$(BINS_$(d)): TCLIBS = \ - `$(PKG) --libs wlroots` \ - `$(PKG) --libs wayland-server` \ - `$(PKG) --libs xkbcommon` \ - -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/cmd/wm/util.c b/sys/cmd/wm/util.c deleted file mode 100644 index 7871d15..0000000 --- a/sys/cmd/wm/util.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "wm.h" - -typedef struct { - uint32 singular_anchor; - uint32 anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; -} Edge; - -// ----------------------------------------------------------------------- -// general purpose function on rectangles - -void -scale_box(struct wlr_box *box, float scale) -{ - box->width = ROUND((box->x + box->width) * scale) - ROUND(box->x * scale); - box->height = ROUND((box->y + box->height) * scale) - ROUND(box->y * scale); - box->x = ROUND(box->x * scale); - box->y = ROUND(box->y * scale); -} - -void -exclude(struct wlr_box *usable_area, uint32 anchor, int32 exclusive, - int32 margin_top, int32 margin_right, int32 margin_bottom, int32 margin_left) -{ - Edge edges[] = { - { // Top - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .positive_axis = &usable_area->y, - .negative_axis = &usable_area->height, - .margin = margin_top, - }, - { // Bottom - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->height, - .margin = margin_bottom, - }, - { // Left - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, - .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = &usable_area->x, - .negative_axis = &usable_area->width, - .margin = margin_left, - }, - { // Right - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, - .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->width, - .margin = margin_right, - } - }; - for(size_t i = 0; i < arrlen(edges); i++) { - if((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) - && exclusive + edges[i].margin > 0) { - if(edges[i].positive_axis) - *edges[i].positive_axis += exclusive + edges[i].margin; - if(edges[i].negative_axis) - *edges[i].negative_axis -= exclusive + edges[i].margin; - break; - } - } -} - -// ----------------------------------------------------------------------- -// user facing functions - -void -spawn(Arg *arg) -{ - wlr_log(WLR_DEBUG, "spawning %s", ((char **)arg->v)[0]); - if(!fork()) { - dup2(2, 1); - setsid(); - execvp(((char **)arg->v)[0], (char **)arg->v); - } -} - -void -quit(Arg *arg) -{ - wl_display_terminate(server.display); -} - -#define CONFIG(a,b,...) a cfg·##b = __VA_ARGS__ -#include "config.h" -#undef CONFIG diff --git a/sys/cmd/wm/wm.h b/sys/cmd/wm/wm.h deleted file mode 100644 index a263804..0000000 --- a/sys/cmd/wm/wm.h +++ /dev/null @@ -1,350 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#define WLR_USE_UNSTABLE -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -// ----------------------------------------------------------------------- -// macros - -#define ROUND(x) ((int)((x)+0.5)) -#define VISIBLE_ON(C,M) ((C)->monitor == (M) && ((C)->tags & (M)->tag.set[(M)->tag.selected])) - -// ----------------------------------------------------------------------- -// types - -enum -{ - CursorNormal, - CursorMove, - CursorResize, -}; - -typedef union Arg Arg; -typedef struct Button Button; -typedef struct Key Key; -typedef struct Keyboard Keyboard; -typedef struct Layer Layer; -typedef struct Client Client; -typedef struct Layout Layout; -typedef struct Monitor Monitor; -typedef struct Server Server; - -typedef struct Rule Rule; -typedef struct MonitorRule MonitorRule; - -struct Rectangle -{ - int x, y; - int w, h; -}; - -union Arg -{ - int i; - uint ui; - float f; - void *v; -}; - -struct Key -{ - uint modifier; - xkb_keysym_t sym; - void (*action)(Arg *); - Arg arg; -}; - -struct Button -{ - uint modifier; - uint code; - void (*function)(Arg *); - Arg arg; -}; - -struct Keyboard -{ - struct wl_list link; - struct wlr_input_device *device; - struct { - struct wl_listener press; - struct wl_listener modify; - struct wl_listener destroy; - } event; -}; - -struct Layer -{ - struct wl_list link; - struct wlr_layer_surface_v1 *surface; - enum zwlr_layer_shell_v1_layer type; - - struct wlr_box geometry; - - struct { - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener commit; - struct wl_listener destroy; - } event; -}; - -struct Client -{ - struct wl_list link; - struct wl_list stack; - struct wl_list focus; - - struct wlr_xdg_surface *xdg; - - struct { - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener commit; - struct wl_listener destroy; - struct wl_listener request_move; - struct wl_listener request_title; - struct wl_listener request_resize; - struct wl_listener request_fullscreen; - } event; - - struct wlr_box geometry, oldgeometry; - - Monitor *monitor; - - uint tags; - int border : 4; - int ismapped : 1; - int isfloating : 1; - int isurgent : 1; - int isfullscreen : 1; - - uint32 resize; -}; - -struct Layout -{ - char *symbol; - void (*arrange)(Monitor *); -}; - -struct Monitor -{ - struct wl_list link; - struct wlr_output *output; - struct { - struct wl_listener render; - struct wl_listener destroy; - } event; - - struct wlr_box geometry; - struct wlr_box window; - struct wl_list layer[4]; - - Layout *layout, *layouts[2]; - struct { - uint set[2]; - uint selected; - } tag; - struct { - double frac; - int len; - } master; -}; - -struct MonitorRule -{ - char *name; - Layout *layout; - int x, y; - float scale; - enum wl_output_transform transform; - struct { - double frac; - int len; - } master; -}; - -struct Rule -{ - char *id; - char *title; - uint tags; - int isfloating; - int monitor; -}; - -struct Server -{ - struct wl_display *display; - struct wlr_backend *backend; - struct wlr_renderer *renderer; - struct wlr_presentation *present; - struct wlr_xdg_activation_v1 *activate; - - struct { - struct wlr_xdg_shell *xdg; - struct wlr_layer_shell_v1 *layer; - } shell; - - struct { - struct wl_list list; - struct wl_list stack; - struct wl_list focus; - } client; - Client *selected; - - struct { - Client *client; - double x, y; - struct wlr_box box; - } grab; - uint32 resize; - - struct { - struct wlr_output_layout *layout; - struct wl_list list; - struct wlr_box geometry; - struct wlr_output_manager_v1 *manager; - Monitor *selected; - } monitor; - - struct { - struct wlr_cursor *dot; - struct wlr_xcursor_manager *manager; - int mode; - } cursor; - - struct { - struct wlr_seat *seat; - struct wl_list keyboards; - struct wlr_idle *idle; - } input; - - struct { - struct wl_listener make_input; - struct wl_listener make_monitor; - struct wl_listener make_xdg_surface; - struct wl_listener make_layer_surface; - - struct wl_listener monitor_test; - struct wl_listener monitor_apply; - struct wl_listener monitor_change; - - struct wl_listener cursor_move; - struct wl_listener cursor_move_abs; - struct wl_listener cursor_button; - struct wl_listener cursor_axis; - struct wl_listener cursor_frame; - - struct wl_listener request_cursor; - struct wl_listener request_activate; - struct wl_listener request_set_selection; - } event; -}; - -extern struct Server server; - -// ----------------------------------------------------------------------- -// functions - -/* util.c */ -void scale_box(struct wlr_box *, float); -void exclude(struct wlr_box *, uint32, int32, int32, int32, int32, int32 ); - -/* render.c */ -void render_monitor(struct wl_listener *, void *); - -/* xdg.c */ -void make_xdg_surface(struct wl_listener *, void *); - -/* layer.c */ -void make_layer_surface(struct wl_listener *, void *); - -/* input.c */ -void make_input(struct wl_listener *, void *); -void notify_move(uint32 time); - -void cursor_axis(struct wl_listener *, void *); -void cursor_frame(struct wl_listener *, void *); -void cursor_button(struct wl_listener *, void *); -void cursor_move(struct wl_listener *, void *); -void cursor_move_abs(struct wl_listener *, void *); - -void request_cursor(struct wl_listener *, void *); -void request_set_selection(struct wl_listener *, void *); - -/* client.c */ -void rules(Client *); -void focus(Client *, int lift); -void resize(Client *, int x, int y, int w, int h, int interact); -void attach(Client *, Monitor *, uint tags); -void floating(Client *, int); - -void move_client(Arg *arg); -void float_client(Arg *arg); -void resize_client(Arg *arg); - -void request_activate(struct wl_listener *, void *); - -Client *selected_client(void); -Client *client_at(double x, double y); -struct wlr_surface *client_surface_at(Client *, double cx, double cy, double *sx, double *sy); -struct wlr_surface *top_surface(Client *); - -/* monitor.c */ -void tile(Monitor *); -void arrange(Monitor *); -void stratify(Monitor *); -Client *focused_client(Monitor *); -Monitor *monitor_at(double x, double y); - -void monitor_test(struct wl_listener *, void *); -void monitor_apply(struct wl_listener *, void *); -void monitor_change(struct wl_listener *, void *); - -void free_monitor(struct wl_listener *, void *); -void make_monitor(struct wl_listener *, void *); - -#define CONFIG(a,b,...) extern a cfg·##b -#include "config.h" -#undef CONFIG diff --git a/sys/cmd/wm/xdg.c b/sys/cmd/wm/xdg.c deleted file mode 100644 index 6a0c2c8..0000000 --- a/sys/cmd/wm/xdg.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "wm.h" - -static -void -map(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.map); - - wl_list_insert(&server.client.list, &client->link); - wl_list_insert(&server.client.stack, &client->stack); - wl_list_insert(&server.client.focus, &client->focus); - - wlr_xdg_surface_get_geometry(client->xdg, &client->geometry); - client->geometry.width += 2 * client->border; - client->geometry.height += 2 * client->border; - - wlr_xdg_toplevel_set_tiled(client->xdg, - WLR_EDGE_TOP|WLR_EDGE_BOTTOM|WLR_EDGE_LEFT|WLR_EDGE_RIGHT - ); - - rules(client); -} - -static -void -unmap(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.unmap); - - wl_list_remove(&client->link); - attach(client, nil, 0); - - wl_list_remove(&client->stack); - wl_list_remove(&client->focus); -} - -static -void -commit(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.commit); - if(client->resize && client->resize <= client->xdg->configure_serial) - client->resize = 0; -} - -static -void -destroy(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.destroy); - free(client); -} - -static -void -request_move(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.request_move); -} - -static -void -request_resize(struct wl_listener *l, void *data) -{ - struct wlr_xdg_toplevel_resize_event *event = data; - Client *client = wl_container_of(l, client, event.request_resize); -} - - -static -void -request_title(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.request_title); -} - -static -void -request_fullscreen(struct wl_listener *l, void *data) -{ - Client *client = wl_container_of(l, client, event.request_fullscreen); - client->isfullscreen = 1; -} - -void -make_xdg_surface(struct wl_listener *l, void *data) -{ - Client *client; - struct wlr_xdg_toplevel *toplevel; - struct wlr_xdg_surface *xdg = data; - - if(xdg->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) - return; - - client = xdg->surface->data = calloc(1, sizeof(*client)); - client->xdg = xdg; - client->border = cfg·borderpixel; - - client->event.map.notify = map; - wl_signal_add(&xdg->events.map, &client->event.map); - client->event.unmap.notify = unmap; - wl_signal_add(&xdg->events.unmap, &client->event.unmap); - client->event.destroy.notify = destroy; - wl_signal_add(&xdg->events.destroy, &client->event.destroy); - - client->event.commit.notify = commit; - wl_signal_add(&xdg->surface->events.commit, &client->event.commit); - - toplevel = xdg->toplevel; - client->event.request_move.notify = request_move; - wl_signal_add(&toplevel->events.request_move, &client->event.request_move); - client->event.request_title.notify = request_title; - wl_signal_add(&toplevel->events.set_title, &client->event.request_title); - client->event.request_resize.notify = request_resize; - wl_signal_add(&toplevel->events.request_resize, &client->event.request_resize); - client->event.request_fullscreen.notify = request_fullscreen; - wl_signal_add(&toplevel->events.request_fullscreen, &client->event.request_fullscreen); -} diff --git a/sys/libbio/align.c b/sys/libbio/align.c deleted file mode 100644 index 20a57df..0000000 --- a/sys/libbio/align.c +++ /dev/null @@ -1,178 +0,0 @@ -#include -#include -#include -#include - -// ----------------------------------------------------------------------- -// globals - -uint64 aln·shft = (2ULL * (aln·K - 1ULL)); -uint64 aln·mask = (1ULL << (2*aln·K)) - 1ULL; - -// ----------------------------------------------------------------------- -// static data - -static uint64 nuctab[256] = { - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -}; - -// ----------------------------------------------------------------------- -// hash functions - -enum -{ - MURM641 = 0xff51afd7ed558ccd, - MURM642 = 0xc4ceb9fe1a85ec53 -}; - -static -uint64 -minihash(uint64 x, uint64 mask) -{ - x = (~x + (x << 21)) & mask; - x = x ^ (x >> 24); - x = (x + (x << 3) + (x << 8)) & mask; - x = x ^ (x >> 14); - x = (x + (x << 2) + (x << 4)) & mask; - x = x ^ (x >> 28); - x = (x + (x << 31)); - - return x; -} - -static -uint64 -murmurhash(uint64 x, uint64 mask) -{ - x = x ^ (x >> 33); - x = (x * MURM641); - x = x ^ (x >> 33); - x = (x * MURM642); - x = x ^ (x >> 33); - - return x; -} - -// ----------------------------------------------------------------------- -// locality sensitive hashing (with spatial extent) - -static -void -sortpos(uintptr len, uint64 vals[], int locs[]) -{ - int tmpi; - uint64 tmpu64; - -#define LESS(i, j) (locs[i] < locs[j]) -#define SWAP(i, j) (tmpu64 = vals[i], tmpi = locs[i], \ - vals[i] = vals[j], locs[i] = locs[j], \ - vals[j] = tmpu64 , locs[j] = tmpi) - QSORT(len, LESS, SWAP); -#undef LESS -#undef SWAP -} - -/* - * sketch - * @param seq: '0' terminated string - * @param len: number of sequential sketches to keep - * @param vals: buffer to store hashes of sketch. - * @param locs: buffer to store location of sketch hashes - */ -error -aln·sketch(byte *seq, int len, uint64 *vals[aln·N], int *locs[aln·N]) -{ - int i, n, l, *loc; - uint64 kmer, h[3], *val; - int tmpi[2]; - uint64 tmpu[2]; - - for(n = 0; n < aln·N; n++) { - for(l = 0; l < len; l++) { - vals[n][l] = UINT64_MAX; - } - } - - kmer = UINT64_MAX; - for(l = 0; *seq != '\0'; seq++, l++) { - kmer = ((kmer << 2) | nuctab[*seq]) & aln·mask; - - h[0] = minihash(kmer, aln·mask); - h[1] = murmurhash(kmer, aln·mask); - - for(n = 0; n < aln·N; n++) { - val = vals[n]; - loc = locs[n]; - - h[2] = (h[0] + n * h[1]) & aln·mask; - for (i = 0; i < len && h[2] < val[i]; i++) { - ; - } - - tmpu[1] = h[2]; - tmpi[1] = l; - for(i -= 1; i >= 0; i--) { - tmpu[0] = tmpu[1], tmpu[1] = val[i], val[i] = tmpu[0]; - tmpi[0] = tmpi[1], tmpi[1] = loc[i], loc[i] = tmpi[0]; - } - } - } - - for(n = 0; n < aln·N; n++) { - sortpos(len, vals[n], locs[n]); - } - - return 0; -} - -static -int -lessarrs(int len, uint64 a[], uint64 b[]) -{ - int l; - - for(l = 0; l < len; l++) { - if (a[l] < b[l]) return 1; - if (a[l] > b[l]) return 0; - } - - return 0; -} - -static -void -swaparrs(int len, uint64 a[], uint64 b[]) -{ - int l; - uint64 tmp; - - for(l = 0; l < len; l++) { - tmp = a[l], a[l] = b[l], b[l] = tmp; - } -} - -error -aln·sort(uintptr n, int len, uint64 *vals) -{ -#define LESS(i, j) (lessarrs(len, vals+((i)*len), vals+((j)*len))) -#define SWAP(i, j) (swaparrs(len, vals+((i)*len), vals+((j)*len))) - QSORT(n, LESS, SWAP); -#undef LESS -#undef SWAP - return 0; -} diff --git a/sys/libbio/fasta.c b/sys/libbio/fasta.c deleted file mode 100644 index 3788544..0000000 --- a/sys/libbio/fasta.c +++ /dev/null @@ -1,393 +0,0 @@ -#include -#include -#include - -#define INIT_NM_SIZE 128 -#define INIT_SQ_SIZE 4096 - -struct SeqBuf -{ - mem·Allocator mem; - void *heap; - - int cap, off; - byte *it, b[]; -}; - -static -void -reset(struct SeqBuf *sb) -{ - sb->off = 0; - sb->it = sb->b; -} - -static -error -grow(struct SeqBuf **sb, int min) -{ - void* heap; - mem·Allocator mem; - - vlong newcap; - struct SeqBuf *old, *new; - - old = *sb; - mem = old->mem; - heap = old->heap; - - assert((*sb)->cap <= (SIZE_MAX - 1) / 2); - newcap = MAX(16, MAX(1 + 2*(*sb)->cap, (*sb)->cap+min)); - assert(newcap >= (*sb)->cap+min); - - if(new = mem.alloc(heap, 1, sizeof(*new)+newcap), !new) { - errorf("memory: could not allocate new buffer\n"); - return 1; - } - - memcpy(new, old, sizeof(*new) + (*sb)->cap); - - new->cap = newcap; - new->it = new->b + (old->it - old->b); - mem.free(heap, old); - - *sb = new; - return 0; -} - -static -error -put(struct SeqBuf **sb, byte c) -{ - int err; - struct SeqBuf *sq; - - sq = *sb; - if(sq->it < (sq->b + sq->cap)) { - *sq->it++ = c; - return 0; - } - - if(err = grow(sb, 1), err) { - errorf("memory fail: could not allocate more buffer\n"); - sq->mem.free(sq->heap, sq); - return 1; - } - - *((*sb)->it++) = c; - return 0; -} - -static -error -push(struct SeqBuf **sb, int n, void *buf) -{ - int d, err; - struct SeqBuf *seq; - - seq = *sb; - if(d = (seq->cap - (seq->it - seq->b)), d < n) { - assert(d >= 0); - if (err = grow(sb, n-d), err) { - errorf("memory fail: could not allocate more buffer\n"); - seq->mem.free(seq->heap, seq); - return 1; - } - } - seq = *sb; - - memcpy(seq->it, buf, n); - seq->it += n; - - return 0; -} - -// ----------------------------------------------------------------------- -// sequence data - -struct bio·SeqReader { - byte eof; - io·Reader rdr; - void *io; - - struct SeqBuf *seq; - - /* read buffer */ - byte *b, *bend, buf[4*4098]; -}; - -static -error -fill(bio·SeqReader *rdr) -{ - int n; - // NOTE: This could lead to an infinite loop. - if(rdr->eof) - return 0; - - n = rdr->rdr.read(rdr->io, 1, arrlen(rdr->buf), rdr->buf); - if(n < 0) { - errorf("read: no data obtained from reader\n"); - return 1; - } - rdr->b = rdr->buf; - rdr->bend = rdr->b + n; - if(rdr->eof = (n < arrlen(rdr->buf)), rdr->eof) - *rdr->bend++ = '\0'; - - return 0; -} - -bio·SeqReader* -bio·openseq(io·Reader rdr, void *io, mem·Allocator mem, void *heap) -{ - error err; - bio·SeqReader *r; - - r = mem.alloc(heap, 1, sizeof(bio·SeqReader)); - r->rdr = rdr; - r->io = io; - r->eof = 0; - - r->seq = mem.alloc(heap, 1, sizeof(*r->seq) + INIT_NM_SIZE + INIT_SQ_SIZE); - r->seq->mem = mem; - r->seq->heap = heap; - r->seq->it = r->seq->b; - r->seq->cap = INIT_NM_SIZE + INIT_SQ_SIZE; - - if (err=fill(r), err) { - errorf("fill: could not populate buffer\n"); - goto ERROR; - } - - return r; - -ERROR: - mem.free(heap, r->seq); - mem.free(heap, r); - return nil; -} - -error -bio·closeseq(bio·SeqReader *rdr) -{ - mem·Allocator mem; - void *heap; - - mem = rdr->seq->mem; - heap = rdr->seq->heap; - - mem.free(heap, rdr->seq); - mem.free(heap, rdr); - - return 0; -} - - -static -error -readfasta(bio·SeqReader *rdr, bio·Seq *seq, byte hdr, byte stop) -{ - error err; - byte *beg; - - if(rdr->eof && rdr->b == rdr->bend-1) - return EOF; - - reset(rdr->seq); - - // NOTE: Can this case happen? - assert(rdr->b != rdr->bend); - if(*rdr->b++ != hdr) { - errorf("fasta/q format: expected '%c', found '%c'\n", hdr, *rdr->b--); - return 1; - } - -NAME: - beg = rdr->b; - while(rdr->b != rdr->bend) { - if(*rdr->b++ == '\n') { - push(&rdr->seq, (rdr->b - 1) - beg, beg); - goto SEQ; - } - } - push(&rdr->seq, rdr->b - beg, beg); - - if(err=fill(rdr), err) { - errorf("read: could not populate buffer\n"); - return 1; - } - goto NAME; - -SEQ: - put(&rdr->seq, '\0'); - rdr->seq->off = rdr->seq->it - rdr->seq->b; - -SEQLOOP: - beg = rdr->b; - while(rdr->b != rdr->bend) { - if(*rdr->b == '\n') { - push(&rdr->seq, rdr->b - beg, beg); - beg = rdr->b + 1; - } - - if(*rdr->b == stop || *rdr->b == '\0') - goto SUCCESS; - - rdr->b++; - } - - push(&rdr->seq, rdr->b - beg, beg); - - if(err=fill(rdr), err) { - errorf("read: could not populate buffer\n"); - return 1; - } - goto SEQLOOP; - -SUCCESS: - push(&rdr->seq, rdr->b - beg, beg); - put(&rdr->seq, '\0'); - - return 0; -} - -/* - * fasta files - */ - -error -bio·readfasta(bio·SeqReader *rdr, bio·Seq *seq) -{ - error err; - - err = readfasta(rdr, seq, '>', '>'); - if(err && err != EOF) { - errorf("parse fail: could not read sequence of record\n"); - return err; - } - - seq->name = rdr->seq->b; - seq->s = rdr->seq->b + rdr->seq->off; - seq->len = rdr->seq->it - seq->s - 1; // shift by 1 as we pushed a '0' to end - seq->q = nil; - - return err; -} - -/* - * fastq files - */ - -error -bio·readfastq(bio·SeqReader *rdr, bio·Seq *seq) -{ - int n; - byte *beg; - error err; - - err = readfasta(rdr, seq, '@', '+'); - if(err) { - errorf("parse fail: could not read sequence of record\n"); - return err; - } - - seq->len = rdr->seq->it - (rdr->seq->b + rdr->seq->off); - - if(*rdr->b++ != '+') { - errorf("format error: no '+' character seperator found\n"); - return -1; - } - -EATLN: - while(rdr->b != rdr->bend) { - if (*rdr->b++ == '\n') { - n = 0; - goto QUAL; - } - } - - if(err = fill((bio·SeqReader*)rdr), err) { - errorf("read: could not populate buffer\n"); - return 1; - } - goto EATLN; - -QUAL: - beg = rdr->b; - while(rdr->b != rdr->bend) { - if(*rdr->b == '\n') { - push(&rdr->seq, rdr->b - beg, beg); - beg = rdr->b + 1; - } - - if(n++ == seq->len || *rdr->b == '\0') { - err = *rdr->b == '\0' ? EOF : 0; - goto SUCCESS; - } - - rdr->b++; - } - - push(&rdr->seq, rdr->b - beg, beg); - - if(err = fill((bio·SeqReader*)rdr), err) { - errorf("read: could not populate buffer\n"); - return 1; - } - goto QUAL; - - -SUCCESS: - push(&rdr->seq, rdr->b - beg, beg); - put(&rdr->seq, '\0'); - - seq->name = rdr->seq->b; - seq->s = rdr->seq->b + rdr->seq->off - 1; - seq->q = seq->s + seq->len + 1; - - return err; -} - -// ----------------------------------------------------------------------- -// sequence writing - -error -bio·writefasta(io·Writer io, void *wtr, bio·Seq seq) -{ - int i, j, d; - char buf[2048], *b = buf, *e = arrend(buf); - - *b++ = '>'; - while(*seq.name) { - *b++ = *seq.name++; - if(b == e) { - io.write(wtr, 1, arrlen(buf), buf); - b = buf; - } - } - - for(i=0; i -#include -#include - -// ----------------------------------------------------------------------- -// Tokens - -enum TokenKind -{ - tok·nil, - tok·eof, - tok·space, - tok·ident, - tok·number, - tok·lparen, - tok·rparen, - tok·lbrak, - tok·rbrak, - tok·comma, - tok·semi, - tok·colon, - - NUM_TOKENS, -}; - - -struct Token { - enum TokenKind kind; - union - { - byte *s; - double x; - } lit; -}; - -static -byte* -tokstr(struct Token tok) -{ - static byte b[50]; - switch (tok.kind) { - case tok·nil: return ""; - case tok·eof: return nil; - case tok·space: return " "; - case tok·ident: return tok.lit.s; - case tok·lparen: return "("; - case tok·rparen: return ")"; - case tok·lbrak: return "["; - case tok·rbrak: return "]"; - case tok·comma: return ","; - case tok·semi: return ";"; - case tok·colon: return ":"; - case tok·number: - snprintf(b, arrlen(b), "%f", tok.lit.x); - return b; - default: - return nil; - } -} - - -// ----------------------------------------------------------------------- -// Read - -// TODO: Bounds checking on buffer -static -struct Token -lex(io·Peeker s, void* fp) -{ -#define isvalidchar(C) ((C) == '!') || \ - ('\"' < (C) && (C) < '\'') || \ - (')' < (C) && (C) < '+') || \ - (',' < (C) && (C) < ':') || \ - (':' < (C) && (C) < '[') || \ - ((C) == '\\') || \ - (']' < (C) && (C) <= '~') - byte *c; - struct Token tok; - static byte b[1024]; - c = b; - *c = s.get(fp); - - if (isspace(*c)) { - while (isspace(*c)) { - *(++c) = s.get(fp); - } - - s.unget(fp, *c); - assert(c - b < 1024); - - *c = 0; - tok.kind = tok·space; - tok.lit.s = b; - return tok; - } - - switch (*c) { - case EOF: tok.kind = tok·eof; return tok; - case '(': tok.kind = tok·lparen; return tok; - case ')': tok.kind = tok·rparen; return tok; - case '[': tok.kind = tok·lbrak; return tok; - case ']': tok.kind = tok·rbrak; return tok; - case ',': tok.kind = tok·comma; return tok; - case ';': tok.kind = tok·semi; return tok; - case ':': tok.kind = tok·colon; return tok; - - case '.': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - while (isdigit(*c)) { - NUM: *(++c) = s.get(fp); - } - if (*c == '.') goto NUM; - if (isvalidchar(*c)) goto IDENT; - - s.unget(fp, *c); - assert(c - b < 1024); - - *c = 0; - tok.kind = tok·number; - tok.lit.x = atof(b); - return tok; - - case '\"': - while ((*c) != '\"') { - *(++c) = s.get(fp); - } - assert(c - b < 1024); - - *c = '\0'; - tok.kind = tok·ident; - tok.lit.s = b + 1; - return tok; - - default: - IDENT: - while (isvalidchar(*c)) { - *(++c) = s.get(fp); - } - s.unget(fp, *c); - assert(c - b < 1024); - - *c = '\0'; - tok.kind = tok·ident; - tok.lit.s = b; - return tok; - } -#undef isvalidchar -} - -static -struct Token -lex_nospace(io·Peeker s, void *impl) -{ - struct Token tok; - tok = lex(s, impl); - if (tok.kind == tok·space) { - tok = lex_nospace(s, impl); - } - - return tok; -} - -struct Parser -{ - int lev; - bio·Node *root; - struct Token tok; - - void *io; - io·Peeker file; - void *heap; - mem·Allocator mem; -}; - -static -error -parse(struct Parser *p) -{ - error err; - bio·Node *node; - bio·Node *root; - struct Token tok; - - node = p->root; - for (;;) { - tok = lex_nospace(p->file, p->io); - - switch (tok.kind) { - case tok·lparen: - if (!p->root && p->lev > 0) { - errorf("parse format: attempted to make root at non-zero level"); - goto ERROR; - } - - node = p->mem.alloc(p->heap, 1, sizeof(*node)); - memset(node, 0, sizeof(*node)); - - if (p->root) { - phylo·addchild(p->root, node); - root = p->root; - } else { - root = node; - } - - p->lev++; - p->root = node; - p->tok = tok; - err = parse(p); - if (err) { - goto ERROR; - } - if (p->tok.kind != tok·rparen) { - errorf("incorrect format: closing parentheses expected to proceed opening"); - goto ERROR; - } - p->root = root; - // NOTE(nnoll): We don't want to override the state of p->tok here! - // Jump straight to grabbing next token. - continue; - - case tok·rparen: - p->lev--; - goto DONE; - - /* Comments */ - case tok·lbrak: - if (!node) { - errorf("incorrect format: comment found in disallowed region"); - goto ERROR; - } - node->comment = str·make(""); - while (tok.kind != tok·rbrak) { - tok = lex_nospace(p->file, p->io); - if (tok.kind == tok·eof || tok.kind == tok·nil) { - errorf("incorrect format: unmatched comment bracket '['"); - goto ERROR; - } - str·append(&node->comment, tokstr(tok)); - } - break; - - case tok·rbrak: - errorf("incorrect format: end comment token found in disallowed region"); - goto ERROR; - break; - - case tok·colon: - tok = lex_nospace(p->file, p->io); - if (tok.kind != tok·number) { - errorf("incorrect format: expected number after colon"); - goto ERROR; - } - if (node == nil) { - errorf("parse error: attempting to set distance of nil node"); - goto ERROR; - } - node->dist = tok.lit.x; - break; - - case tok·comma: - node = nil; - break; - - case tok·ident: - if (p->tok.kind == tok·rparen) { - if (!node) { - errorf("parse error: attempting to set name of nil node"); - goto ERROR; - } - node->name = str·make(tok.lit.s); - } else { - if (p->tok.kind != tok·lparen && p->tok.kind != tok·comma) { - errorf("format error: misplaced identifier for leaf found"); - goto ERROR; - } - - if (!p->root) { - errorf("parse error: attempting to create child for no parent"); - goto ERROR; - } - - node = p->mem.alloc(p->heap, 1, sizeof(*node)); - memset(node, 0, sizeof(*node)); - - node->name = str·make(tok.lit.s); - - phylo·addchild(p->root, node); - } - break; - - case tok·number: - if (p->tok.kind == tok·rparen) { - if (p->lev == 0) { - errorf("format error: support value on root not supported"); - goto ERROR; - } - node->support = tok.lit.x; - } else { - errorf("format error: found number in unexpected location"); - goto ERROR; - } - break; - - case tok·semi: - p->file.unget(p->io, ';'); - if (p->lev) { - errorf("format error: uneven number of parentheses found at ';'"); - goto ERROR; - } - goto DONE; - - case tok·eof: - goto DONE; - - default: - errorf("parse error: unrecognized token"); - goto ERROR; - } - - p->tok = tok; - } - -DONE: - p->tok = tok; - return 0; -ERROR: - // TODO(nnoll): cleanup - return 1; -} - -int -bio·readnewick(io·Peeker stream, void *s, bio·Tree *tree) -{ - error err; - struct Parser p; - - if (!tree) { - errorf("tree pointer nil"); - return 0; - } - - p = (struct Parser){ - .lev = 0, - .root = nil, - .tok = (struct Token){ 0 }, - .io = s, - .file = stream, - .mem = tree->mem, - .heap = tree->heap, - }; - err = parse(&p); - if (err) { - errorf("parsing failed\n"); - return 0; - } - - tree->root = p.root; - tree->nleaf = 0; - tree->root->nnode = 0; - - phylo·countleafs(tree->root, &tree->nleaf); - phylo·countnodes(tree->root, &tree->root->nnode); - - return 1; -} - -// ----------------------------------------------------------------------- -// Write - -static -error -dump(bio·Node *node, void *impl, io·Putter out) -{ - byte b[24]; - - if (!node) { - return 1; - } - - bio·Node *child; - if (node->nchild) { - out.put(impl, '('); - - dump(node->child, impl, out); - for (child = node->child->sibling; child != nil; child = child->sibling) { - out.put(impl, ','); - dump(child, impl, out); - } - - out.put(impl, ')'); - } - if (node->name) { - out.puts(impl, node->name); - } - - if (node->parent) { - out.put(impl, ':'); - snprintf(b, arrlen(b), "%f", node->dist); - out.puts(impl, b); - } - - return 0; -} - -error -bio·writenewick(bio·Tree tree, io·Putter out, void* impl) -{ - dump(tree.root, impl, out); - out.put(impl, ';'); - out.put(impl, '\n'); - - return 0; -} diff --git a/sys/libbio/phylo.c b/sys/libbio/phylo.c deleted file mode 100644 index d50934f..0000000 --- a/sys/libbio/phylo.c +++ /dev/null @@ -1,427 +0,0 @@ -#include -#include -#include -#include - -// ----------------------------------------------------------------------- -// subtree manipulation methods -// NOTE: As of now these don't update nnode & nleaf stats. -// It is the caller's responsibility to refresh counts. - -error -phylo·addchild(bio·Node* parent, bio·Node* child) -{ - bio·Node *it, *sibling; - if (!parent->nchild) { - parent->child = child; - goto SUCCESS; - } - - for (it = parent->child, sibling = it; it != nil; it = it->sibling) { - sibling = it; - } - sibling->sibling = child; - -SUCCESS: - child->parent = parent; - parent->nchild++; - return 0; -} - -error -phylo·rmchild(bio·Node *parent, bio·Node *child) -{ - bio·Node *it, *prev; - enum { - error·nil, - error·notfound, - error·nochildren, - }; - - prev = nil; - for (it = parent->child; it != nil; it = it->sibling) { - if (it == child) goto FOUND; - prev = it; - } - return error·notfound; - -FOUND: - if (prev == nil) { - parent->child = child->sibling; - } else { - prev->sibling = child->sibling; - } - - parent->nchild--; - return error·nil; -} - -// ----------------------------------------------------------------------- -// subtree statistics - -error -phylo·countnodes(bio·Node *node, int *n) -{ - int m; - error err; - bio·Node *child; - - m = *n; - for (child = node->child; child != nil; child = child->sibling) { - if (err = phylo·countnodes(child, n), err) { - errorf("node count: failure at '%s'", child->name); - return 1; - } - } - node->nnode = *n - m; - *n += 1; - - return 0; -} - -error -phylo·countleafs(bio·Node *node, int *n) -{ - error err; - bio·Node *child; - - if (!node->nchild) { - *n += 1; - } - - for (child = node->child; child != nil; child = child->sibling) { - if (err = phylo·countleafs(child, n), err) { - errorf("leaf count: failure at '%s'", child->name); - return 1; - } - } - - return 0; -} - -// ----------------------------------------------------------------------- -// generic operations on tree - -void* -phylo·postorder(bio·Node *clade, void *(*op)(bio·Node*, void*), void *ctx) -{ - bio·Node *it; - - for(it = clade->child; it != nil; it = it->sibling) { - ctx = phylo·postorder(it, op, ctx); - } - - return op(clade, ctx); -} - -void* -phylo·preorder(bio·Node *clade, void *(*op)(bio·Node*, void*), void *ctx) -{ - bio·Node *it; - - ctx = op(clade, ctx); - for(it = clade->child; it != nil; it = it->sibling) { - ctx = phylo·preorder(it, op, ctx); - } - - return ctx; -} - -int -phylo·collectpostorder(bio·Node *clade, bio·Node **list) -{ - bio·Node *it; - int n; - - for(n = 0, it = clade->child; it != nil; it = it->sibling) { - n += phylo·collectpostorder(it, list+n); - } - - return n; -} - -static -inline -void* -appendleaf(bio·Node *node, void* list) -{ - bio·Node **leafs; - - leafs = list; - if (!node->nchild) { - *leafs++ = node; - } - - return leafs; -} - -void -phylo·getleafs(bio·Tree tree, bio·Node **leafs) -{ - phylo·postorder(tree.root, &appendleaf, leafs); -} - -// ----------------------------------------------------------------------- -// tree editing - -static -void -sortnodelist(bio·Node **head, bio·Node *next) -{ - bio·Node tmp, *it; - - it = &tmp; - tmp.sibling = *head; - - while (it->sibling != nil && it->sibling->nnode < next->nnode) { - it = it->sibling; - } - - next->sibling = it->sibling; - it->sibling = next; - *head = tmp.sibling; -} - -error -phylo·ladderize(bio·Node *root) -{ - int i; - error err; - bio·Node *child, *sorted, *sibling; - - if (!root->nchild) return 0; - - // ladderize below - for (child = root->child; child != nil; child = child->sibling) { - if (err = phylo·ladderize(child), err) { - errorf("ladderize: failure at '%s'", child->name); - return 1; - } - } - - // ladderize yourself - sorted = nil; - child = root->child; - while (child != nil) { - sibling = child->sibling; - sortnodelist(&sorted, child); - child = sibling; - } - root->child = sorted; - - return 0; -} - -/* - * compute all distances from a given node - * must provide a working buffer - */ - -struct Tuple -{ - double *d; - bio·Node **n; -}; - -static -struct Tuple -getdistsfrom(bio·Node *node, bio·Node *prev, double curr, double *dist, bio·Node **list) -{ - bio·Node *it; - struct Tuple ret; - - *dist++ = curr; - *list++ = node; - - ret.d = dist; - ret.n = list; - - if (node->parent && node->parent != prev) { - ret = getdistsfrom(node->parent, node, curr + node->dist, dist, list); - - dist = ret.d; - list = ret.n; - } - - for (it = node->child; it != nil; it = it->sibling) { - if (it != prev) { - ret = getdistsfrom(it, node, curr + it->dist, dist, list); - - dist = ret.d; - list = ret.n; - } - } - - return ret; -} - -int -phylo·getdistsfrom(bio·Node *node, int len, double *dist, bio·Node **list) -{ - struct Tuple ret; - // TODO: Better bounds checking. - - ret = getdistsfrom(node, nil, 0.0, dist, list); - - assert(ret.n - list == len); - assert(ret.d - dist == len); - - return len; -} - -/* -static -void -disttoroot(bio·Node *clade, double anc, double *dists) -{ - double d; - bio·Node *it; - - *dists++ = anc + clade->dist; - d = dists[-1]; - for (it = clade->child; it != nil; it = it->sibling) { - disttoroot(it, d, ++dists); - } -} - -void -phylo·disttoroot(bio·Tree tree, double *dists) -{ - disttoroot(tree.root, 0.0, dists); -} -*/ - -/* - * compute the path constituting the tree diameter - * returns the number of edges in the path - */ - -static -void -sort·nodedists(uintptr len, double fs[], bio·Node* ns[]) -{ - double f; - bio·Node *n; -#define LESS(i, j) (fs[i] < fs[j]) -#define SWAP(i, j) (n = ns[i], f = fs[i], \ - fs[i] = fs[j], ns[i] = ns[j], \ - fs[j] = f, ns[j] = n) - QSORT(len, LESS, SWAP); -#undef LESS -#undef SWAP -} - -#define BUFLEN 4096 -double -phylo·diameter(bio·Tree tree, int *len, bio·Node **path) -{ - // TODO: deal with large tree > BUFLEN gracefully - int n; - double fbuf[BUFLEN]; - bio·Node *nbuf[BUFLEN]; - - n = tree.root->nnode; - - assert(n < BUFLEN); - - n = phylo·getdistsfrom(tree.root, tree.root->nnode, fbuf, nbuf); - sort·nodedists(n, fbuf, nbuf); - - path[0] = nbuf[n-1]; - printf("first end '%s'\n", path[0]->name); - - n = phylo·getdistsfrom(path[0], n, fbuf, nbuf); - sort·nodedists(n, fbuf, nbuf); - printf("second end '%s'\n", nbuf[n-1]->name); - - *len = 0; - - // TODO: Traverse up the tree from each node - // Find MRCA by intersection of nodes hit - - return 0.0; -} -#undef BUFLEN - -/* - * reroot a tree on a new node - */ -static -error -rotateparent(bio·Node *node, bio·Node *to) -{ - error err; - - // NOTE: will this ever be taken? - if (node->parent == to) { - return 0; - } - - if (!node->parent) { - goto RMCHILD; - } - - err = rotateparent(node->parent, node); - if (err) { - errorf("failure: broken tree"); - return err; - } - - err = phylo·addchild(node, node->parent); - if (err) { - errorf("inconsistent topology: could not add parent '%s' as child of '%s'", node->parent->name, node->name); - return err; - } - -RMCHILD: - err = phylo·rmchild(node, to); - if (err) { - errorf("inconsistent topology: could not remove child '%s' from '%s'", to->name, node->name); - return err; - } - - node->parent = to; - return 0; -} - -#define PREC .00000001 -error -phylo·reroot(bio·Tree *tree, bio·Node *node, double d) -{ - bio·Node *new; - - // TODO: should check that node is part of this tree? - // TODO: should we check if node->parent != nil? - - if (fabs(d) < PREC) { - new = node; - rotateparent(node->parent, node); - } else if (fabs(d-node->dist) < PREC) { - new = node->parent; - if (new->parent->parent) { - rotateparent(new->parent->parent, new->parent); - } - } else { - new = tree->mem.alloc(tree->heap, 1, sizeof(*new)); - memset(new, 0, sizeof(*new)); - - phylo·addchild(new, node); - node->parent = new; - - phylo·addchild(new, node->parent); - if (node->parent->parent) { - rotateparent(node->parent->parent, node->parent); - } - node->parent->parent = new; - } - - printf("number of children on old root: %d\n", tree->root->nchild); - tree->root = new; - tree->nleaf = 0; - - phylo·countleafs(new, &tree->nleaf); - phylo·countnodes(new, &new->nnode); - - return 0; -} -#undef PREC diff --git a/sys/libbio/rules.mk b/sys/libbio/rules.mk deleted file mode 100644 index cbc6887..0000000 --- a/sys/libbio/rules.mk +++ /dev/null @@ -1,28 +0,0 @@ -include share/push.mk - -# Local sources -SRCS_$(d) := \ - $(d)/fasta.c \ - $(d)/newick.c \ - $(d)/phylo.c -LIBS_$(d) := $(d)/libbio.a -BINS_$(d) := -# TSTS_$(d) := \ -# $(d)/test.c \ -# $(d)/simulate.c - -include share/paths.mk - -# Local rules -# $(LIBS_$(d)) = TCFLAGS := -# $(LIBS_$(d)) = TCINCS := -# $(LIBS_$(d)) = TCLIBS := - -$(LIBS_$(d)): $(OBJS_$(d)) $(OBJS_$(d)/io) - $(ARCHIVE) - -$(UNTS_$(d)): TCLIBS := $(LIBS_$(d)) $(OBJ_DIR)/libn/libn.a -$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/libn/libn.a - $(LINK) - -include share/pop.mk diff --git a/sys/libbio/simulate.c b/sys/libbio/simulate.c deleted file mode 100644 index 0f5a97e..0000000 --- a/sys/libbio/simulate.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include - -#define SEQLEN 2560 -static byte *SEQ = -"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAGGTAC" -"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" -"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCAACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" -"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" -"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCTCTAAAGTCA" -"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" -"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" -"CGTGAAGTCAGTGCTGGAGTTGATTGAAAAGGTCGATGTGCATGCCATTGCGCACCTGACCGGCGGCGGCTTCTGGGAAA" -"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" -"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCGCCCTGCC" -"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" -"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" -"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGTTCGGCCTT" -"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" -"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" -"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" -"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGTGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" -"ACAGGCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" -"ATCCACTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" -"CCGCCGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" -"CTGTAAATCGCAAGCTCCAGCAGTTTTTTTCCCCCTTTTCTGGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" -"TCCAGGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATCGAAAAAGAGCTCAGT" -"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" -"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATCATTATTAGCGAAGAAC" -"AAGGCTCCAACTCTCATTCCCGCCATTTACTGGGCAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" -"CTCTACAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" -"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" -"TGCAGTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" -"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" -"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" -"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" -"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCAGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT"; - -byte* -modify(byte *seq, int *len, double p) -{ - byte *head, *new; - - head = calloc(SEQLEN+1, sizeof(byte)); - new = head; - for (; *seq != '\0'; seq++) { - if (rng·bernoulli(p)) { - switch (rng·randi(5)) { - case 0: *new++ = 'A'; break; - case 1: *new++ = 'C'; break; - case 2: *new++ = 'G'; break; - case 3: *new++ = 'T'; break; - case 4: continue; - } - } else { - *new++ = *seq; - } - } - *new = '\0'; - *len = new - head; - return head; -} - -#define NSEQS 20 -int -main() -{ - int n, i, l, lens[NSEQS]; - byte *seqs[NSEQS]; - - int locs[aln·N][NSEQS][aln·L]; - int *loc[aln·N]; - uint64 vals[aln·N][NSEQS][aln·L]; - uint64 *val[aln·N]; - - rng·init(0); - - seqs[0] = SEQ; - lens[0] = SEQLEN; - - for (n = 0; n < aln·N; n++) { - for (i = 0; i < NSEQS; i++) { - for (l = 0; l < aln·L; l++) { - vals[n][i][l] = 0; - } - } - } - - for (i = 1; i < NSEQS; i++) { - seqs[i] = modify(SEQ, lens + i, .01*i); - } - - for (i = 0; i < NSEQS; i++) { - for (n = 0; n < aln·N; n++) { - val[n] = vals[n][i]; - loc[n] = locs[n][i]; - } - aln·sketch(seqs[i], aln·L, val, loc); - } - - // for (n = 0; n < aln·N; n++) { - // printf("iteration %d\n", n); - // printf("[\n"); - // for (i = 0; i < NSEQS; i++) { - // printf(" ["); - // for (l = 0; l < aln·L; l++) { - // printf("%lu,", vals[n][i][l]); - // } - // printf("],\n"); - // } - // printf("]\n"); - // } - - for (n = 0; n < aln·N; n++) { - aln·sort(NSEQS, aln·L, (uint64*)vals[n]); - } - - return 0; -} diff --git a/sys/libbio/test.c b/sys/libbio/test.c deleted file mode 100644 index 9926764..0000000 --- a/sys/libbio/test.c +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include -#include - -#include - -// ----------------------------------------------------------------------- -// Global data - -static byte *SEQ[] = { -"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAGGTAC" -"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" -"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCAACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" -"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" -"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCTCTAAAGTCA" -"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" -"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" -"CGTGAAGTCAGTGCTGGAGTTGATTGAAAAGGTCGATGTGCATGCCATTGCGCACCTGACCGGCGGCGGCTTCTGGGAAA" -"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" -"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCGCCCTGCC" -"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" -"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" -"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGTTCGGCCTT" -"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" -"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" -"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" -"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGTGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" -"ACAGGCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" -"ATCCACTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" -"CCGCCGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" -"CTGTAAATCGCAAGCTCCAGCAGTTTTTTTCCCCCTTTTCTGGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" -"TCCAGGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATCGAAAAAGAGCTCAGT" -"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" -"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATCATTATTAGCGAAGAAC" -"AAGGCTCCAACTCTCATTCCCGCCATTTACTGGGCAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" -"CTCTACAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" -"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" -"TGCAGTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" -"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" -"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" -"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" -"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCAGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT", - -"GGCGGCTTCGGTGCGCTGTGTGCATTGCCGCAAAAATATCGTGAACCCGTGCTGGTTTCCGGCACTGACGGCGTAAATAC" -"CAAGCTGCGTCTGGCAATGGACTTAAAACGTCACGACACCATTGGTATTGATCTGGTCGCCATGTGCGTTAATGACCTGG" -"TGGTGCAAGGTGCGGAACCGCTGTTTTTCCTCGACTATTACGCACCGGAAAACTGGATGTTGATACCGCTTCAGCGGTG" -"ATCAGCGGCATTGCGGAAGGTTGTCTGCAATCGGGCTGTTCTCTGGTGGGTGGCGAAACGGCAGAAATGCCGGGGATGTA" -"TCACGGTGAAGATTACGATGTCGCGGGTTTCTGCGTGGGCGTGGTAGAAAAATCAGAAATCATCGACGGCAAAGTCA" -"GCGACGGCGATGTGCTGATTGCACTCGGTTCCAGCGGTCCGCACTCGAACGGTTATTCGCTGGTGCGCAAAATTCTTGAA" -"GTCAGCGGTTGTGATCCGCAAACCACCGAACTTGATGGTAAGCCATTAGCCGATCATCTGCTGGCACCGACCCGCATTTA" -"ACATTCCGCGCGTATTGCCAGATAATACCCAGGCAGTGATTGATGAATCTTCCTGGCAGTGGCCGGAAGTGTTCAACTGG" -"CTGCAAACGGCAGGTAACGTTGAGCGCCATGAAATGTATCGCACCTTCAACTGCGGCGTCGGGATGATTATCCCCTGCC" -"TGCTCCGGAAGTGGACAAAGCCCTCGCCCTGCTCAATGCCAACGGTGAAAACGCGTGGAAAATCGGTATCATCAAAGCCT" -"CTGATTCCGAACAACGCGTGGTTATCGAATAATGAATATTGTGTGCTTATTTCCGGCAACGGAAGTAATTTACAGGCAA" -"TTATTGACGCCTGTAAAACCAACAAAATTAAAGGCACCGTACGGGCAGTTTTCAGCAATAAGGCCGACGCGCGGCCTT" -"GAACGCGCCCGCCAGGCGGGTATTGCAACGCATACGCTCATCGCCAGCGCGTTTGACAGTCGTGAAGCCTATGACCGGGA" -"GTTGATTCATGAAATCGACATGTACGCACCCGATGTGGTCGTGCTGGCTGGTTTTATGCGCATTCTCAGCCCGGCGTTTG" -"TCTCCCACTATGCCGGGCGTTTGCTGAACATTCACCCTTCTCTGCTGCCGAAATATCCCGGATTACACACCCATCGTCAA" -"GCGCTGGAAAATGGCGATGAAGAGCACGGTACATCGGGCATTTCGTCACCGATGAACTGGACGGTGGCCCGGTTATTTT" -"ACAGTCGAAAGTCCCGGTATTTGCTGGTGATACGGAAGATGACGTCACCGCCCGCGTGCAAACCCAGGAACACGCCATTT" -"ATCCTCTGGTGATTAGCTGGTTTGCCGATGGTCGTCTGAAAATGCACGAAAACGCCGCGTGGCTGGATGGTCAACGTCTG" -"CCGCTGCAGGGCTACGCTGCCGACGAGTAATGCCCCCGTAGTTAAAGCGCCAGCTCTGCCGCTGGCGTTTTTCAATTCAC" -"CTGTTAATCGCAAGCTCCAGCAGCCCCCCCCCCCCTTTTCTGCATAGTTGGACATCTGCCAATATTGCTCGCCATAATA" -"TCCATGCAGTGTCCCGTGAATAAAACGGAGTAAAAGTGGTAATGGGTCAGGAAAAGCTATACATAAAAAGAGCTCAGT" -"TGGTTATCGTTCAATGAACGCGTGCTTCAGGAAGCGGCGGACAAATCTAACCCGCTGATTGAAAGGATGCGTTTCCTGGG" -"GATCTATTCCAATAACCTTGATGAGTTCTATAAAGTCCGCTTCGCTGAACTGAAGCGACGCATTATTAGCGAAGAAC" -"AAGGTTCCAACTCTCATTCCCGCCATTTACTGGGAAAATTCAGTCCCGGGTGCTGAAAGCCGATCAGGAATTCGACGGC" -"CTCTTCAACGAGCTATTGCTGGAGATGGCGCGCAACCAGATCTTCCTGATTAATGAACGCCAGCTCTCCGTCAATCAACA" -"AAACTGGCTGCGTCATTATTTTAAGCAGTATCTGCGTCAGCACATTACGCCGATTTTAATCAATCCTGACACTGACTTAG" -"TGCATTTCCTGAAAGATGATTACACCTATCTGGCGGTGGAAATTATCCGTGGCGATACCATCCGTTACGCGCTTCTGGAG" -"ATCCCATCAGATAAAGTGCCGCGCTTTGTGAATTTACCGCAGAAGCGCCGCGTCGACGCAAGCCGATGATTCTTCTGGA" -"TAACATTCTGCGTTACTGCCTTGATGATATTTTCAAAGGCTTCTTTGATTATGACGCGCTGAATGCCTATTCAATGAAGA" -"TGACCCGCGATGCCGAATACGATTTAGTGCATGAGATGGAAGCCAGCCTGATGGAGTTGATGTCTTCCAGTCTCAAGCAG" -"CGTTTAACTGCTGAGCCGGTGCGTTTTGTTTATCGCGCGATATGCCCAATGCGCTGGTTGAAGTTTTACGCGAAAAACT", -}; - - -static -int -my_read(Stream *s, void *buf, int n) -{ - return io·read(s, 1, n, buf); -} - -// ----------------------------------------------------------------------- -// Point of entry for testing - -error -test·newick() -{ - error err; - bio·Tree t; - mem·Arena *heap; - Stream *fd[2]; - - io·Peeker rdr; - io·Putter wtr; - - bio·Node **end, **it, **list; - - heap = mem·makearena(mem·sys, nil); - rdr = (io·Peeker){.get = (byte (*)(void *))io·getbyte, .unget = (error (*)(void *, byte))io·ungetbyte}; - wtr = (io·Putter){.put = (error (*)(void *, byte))io·putbyte, .putstr = (int (*)(void *, string))io·putstring}; - - fd[0] = io·open("/home/nolln/root/data/test/zika.nwk", "r"); - fd[1] = io·open("/home/nolln/root/data/test/zika.proc.nwk", "w"); - - t.h = heap; - t.heap = (mem·Allocator){ .alloc = (void *(*)(void *, uint, ulong))mem·arenaalloc, .free = nil, }; - - if (err = bio·readnewick(rdr, fd[0], &t), err) { - errorf("failed to read newick"); - return 1; - } - printf("number of children: %d\n", t.root->nchild); - - phylo·ladderize(t.root); - - list = mem·arenaalloc(heap, t.nleaf, sizeof(**list)); - phylo·getleafs(t, list); - for (it = list, end = list + t.nleaf; it != end; ++it) { - printf("Leaf '%s'\n", (*it)->name); - } - - bio·Node *path[100]; - // phylo·diameter(t, path); - - printf("Loaded tree with %d leafs and %d nodes\n", t.nleaf, t.root->nnode); - err = bio·writenewick(t, wtr, fd[1]); - - io·flush(fd[1]); - - io·close(fd[0]); - io·close(fd[1]); - - mem·freearena(heap); - return 0; -} - -error -test·fasta() -{ - error err; - Stream *fd; - - bio·Seq seq; - bio·FastaReader *rdr; - - clock_t t; - - fd = io·open("/home/nolln/root/data/test/zika.fa", "r"); - - /* Benchmark against Heng */ -#if 0 - int n, slen; - kseq_t *kseq; - - t = clock(); - kseq = kseq_init(fd); - while (kseq_read(kseq) >= 0) { - ++n, slen += kseq->seq.l; - } - t = clock() - t; - printf("heng's code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); - - kseq_destroy(kseq); - - io·seek(fd, 0, seek·set); -#endif - - rdr = bio·openfasta((io·Reader){.read = (int (*)(void *, int, int, void *))io·read}, fd, mem·sys, nil); - - t = clock(); - err = 0; - while (!err) { - err = bio·readfasta(rdr, &seq); - } - t = clock() - t; - printf("nick's code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); - bio·closefasta(rdr); - - - io·close(fd); - return err <= 0 ? 0 : 1; -} - -#define asrdr(x) (int (*)(void *, int, int, void *))(x) -error -test·fastq() -{ - error err; - Stream *fd; - - bio·Seq seq; - bio·FastqReader *rdr; - - clock_t t; - - fd = io·open("/home/nolln/root/data/test/eg.fq", "r"); - - rdr = bio·openfastq((io·Reader){.read = asrdr(io·read)}, fd, mem·sys, nil); - - t = clock(); - err = 0; - while (!err) { - err = bio·readfastq(rdr, &seq); - } - t = clock() - t; - printf("nick's fastq code took %f ms to execute\n", 1000.*t/CLOCKS_PER_SEC); - bio·closefastq(rdr); - - - io·close(fd); - return err <= 0 ? 0 : 1; -} - -error -test·align() -{ - double f; - error err; - int i, l, n; - - uint64 mem[aln·N][arrlen(SEQ)][aln·L]; - uint64 *phi[aln·N]; - int loc[aln·N][arrlen(SEQ)][aln·L]; - int *pos[aln·N]; - - for (i = 0; i < arrlen(SEQ); i++) { - for (n = 0; n < aln·N; n++) { - phi[n] = mem[n][i]; - pos[n] = loc[n][i]; - } - - err = aln·sketch(SEQ[i], aln·L, phi, pos); - } - - f = 0; - for (n = 0; n < aln·N; n++) { - aln·sort(arrlen(SEQ), aln·L, (uint64*)mem[n]); - - if (!memcmp(mem[n][0], mem[n][1], sizeof(uint64)*aln·L)) { - f += 1.; - printf("True : "); - } else { - printf("False: "); - } - for (i = 0; i < arrlen(SEQ); i++) { - printf("["); - for (l = 0; l < aln·L; l++) { - printf("%lu,", mem[n][i][l]); - } - printf("]"); - if (i == 0) printf(" ~ "); - } - printf("\n"); - } - - printf("Fraction hits %f\n", f/aln·N); - return err; - -} - -error -main() -{ - error err; - - if (err = test·newick(), err) { - errorf("test fail: newick"); - } - -#if 0 - if (err = test·fasta(), err) { - errorf("test fail: fasta"); - } - - if (err = test·fastq(), err) { - errorf("test fail: fastq"); - } -#endif -} - diff --git a/sys/libc/rules.mk b/sys/libc/rules.mk deleted file mode 100644 index 96d4202..0000000 --- a/sys/libc/rules.mk +++ /dev/null @@ -1,23 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := $(wildcard $(d)/*.c) -LIBS_$(d) := $(d)/libc_n.a -BINS_$(d) := - -include share/paths.mk - -# Local rules -# $(LIBS_$(d)) = TGTINCS := -# $(LIBS_$(d)) = TGTLIBS := - -$(LIBS_$(d)): TCFLAGS := -ffreestanding -fno-builtin -nostdlib -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -$(BINS_$(d)): $(OBJ_DIR)/libn/test.o - $(LINK) - -include share/pop.mk diff --git a/sys/libc/stdio.c b/sys/libc/stdio.c deleted file mode 100644 index 8bbbe9a..0000000 --- a/sys/libc/stdio.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include - -int -printf(byte* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - int nw, rem, peek, len; - byte *str, c; - - while (*fmt) { - rem = INT_MAX - nw; - - if (fmt[0] != '%' || fmt[1] == '%') { - if (fmt[0] == '%') fmt++; - - for (peek = 1; fmt[peek] && fmt[peek] != '%'; peek++) { - ; - } - if (rem < peek) return -1; - // TODO: Print here. - fmt += peek; - nw += peek; - continue; - } - - str = fmt++; - - switch (*fmt++) { - case 'c': - c = va_arg(args, int); - if (rem < 0) return -1; - // TODO: Print here - nw++; - break; - - case 's': - str = va_arg(args, byte*); - len = strlen(str); - if (rem < len) return -1; - // TODO: Print here - nw += len; - break; - default: - fmt = str; - len = strlen(fmt); - if (rem < len) return -1; - // TODO: Print here - nw += len; - fmt += len; - break; - } - } - - va_end(args); - return nw; -} diff --git a/sys/libc/string.c b/sys/libc/string.c deleted file mode 100644 index 0e41efa..0000000 --- a/sys/libc/string.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include - -void* -memcopy(void *dst, void *src, intptr n) -{ - byte *e, *s, *d; - - d = dst; - e = d + n; - for (s = src ; d != e; ++s, ++d) { - *d = *s; - } - - return dst; -} - -void* -memmove(void *dst, void *src, intptr n) -{ - byte *e, *s, *d; - s = src; - d = dst; - - if (d < s) { - e = d + n; - for (; d != e; ++s, ++d) - *d = *s; - - } else { - e = d; - d += n; - s += n; - for (; d != e; --s, --d) - d[-1] = s[-1]; - } - - return dst; -} - -void* -memset(void *buf, int val, intptr n) -{ - byte *b, *e; - b = buf; - e = b + n; - for (; b != e; b++) { - *b = (byte)val; - } - - return buf; -} - -int -memcmp(void *lhs, void *rhs, intptr n) -{ - byte *bl, *br, *e; - - br = rhs; - e = br + n; - for (bl = lhs; br != e; ++bl, ++br) { - if (*bl < *br) - return -1; - else if (*bl > *br) - return 1; - } - - return 0; -} - -int -strlen(byte* s) -{ - byte* b; - for (b = s; *b; b++) { - ; - } - - return b - s; -} diff --git a/sys/libfmt/buffer.c b/sys/libfmt/buffer.c deleted file mode 100644 index 0099e72..0000000 --- a/sys/libfmt/buffer.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "internal.h" - -static int -flush(fmt·State *io) -{ - int n; - char *s; - - void *heap = io->heap; - mem·Reallocator mem = io->mem; - - if(!io->buffer.beg) - return 0; - - n = 2*(uintptr)io->file; - s = io->buffer.beg; - - io->buffer.beg = mem.realloc(heap, io->buffer.beg, n, 1); - if(!io->buffer.beg){ - io->file = io->buffer.cur = io->buffer.end = nil; - mem.free(heap, s); - return 0; - } - io->file = (void*)(uintptr)n; - io->buffer.cur = io->buffer.beg + (io->buffer.cur - s); - io->buffer.end = io->buffer.beg + n - 1; - - return 1; -} - -int -fmt·make(mem·Reallocator mem, void *heap, fmt·State *io) -{ - int n; - - memset(io, 0, sizeof(*io)); - - n = 32; - io->buffer.beg = io->buffer.cur = mem.alloc(heap, n, 1); - if(!io->buffer.beg) - return -1; - io->buffer.end = io->buffer.beg + n - 1; - - io->flush = flush; - io->file = (void*)(uintptr)n; - io->n = 0; - - fmt·setlocale(io, nil, nil, nil); - return 0; -} - -void -fmt·free(fmt·State *io) -{ - void *heap = io->heap; - mem·Reallocator mem = io->mem; - - mem.free(heap, io->buffer.beg); - io->buffer.beg = io->buffer.cur = io->buffer.end = nil; -} diff --git a/sys/libfmt/do.c b/sys/libfmt/do.c deleted file mode 100644 index eaac0a3..0000000 --- a/sys/libfmt/do.c +++ /dev/null @@ -1,730 +0,0 @@ -#include "internal.h" -#include - -#define atomic _Atomic -#define MaxFmt 128 -#define atomic·load atomic_load -#define atomic·store atomic_store - -// ----------------------------------------------------------------------- -// globals - -/* built in verbs */ -static int fmtflag(fmt·State *); -static int fmtpercent(fmt·State *); -static int fmtrune(fmt·State *); -static int fmtfloat(fmt·State *); -static int fmtutf8(fmt·State *); -static int fmtint(fmt·State *); -static int fmtchar(fmt·State *); -static int fmtcount(fmt·State *); -static int fmtstring(fmt·State *); -static int fmterror(fmt·State *); - -static int badfmt(fmt·State *); - -static struct -{ - atomic int len; - Verb verb[MaxFmt]; -} formatter = -{ - ATOMIC_VAR_INIT(30), - { - {' ', fmtflag}, - {'#', fmtflag}, - {'%', fmtpercent}, - {'\'',fmtflag}, - {'+', fmtflag}, - {',', fmtflag}, - {'-', fmtflag}, - {'C', fmtrune}, - {'E', fmtfloat}, - {'F', fmtfloat}, - {'G', fmtfloat}, - {'L', fmtflag}, - {'S', fmtutf8}, - {'X', fmtint}, - {'b', fmtint}, - {'c', fmtchar}, - {'d', fmtint}, - {'e', fmtfloat}, - {'f', fmtfloat}, - {'g', fmtfloat}, - {'h', fmtflag}, - {'i', fmtint}, - {'l', fmtflag}, - {'n', fmtcount}, - {'o', fmtint}, - {'p', fmtint}, - {'r', fmterror}, - {'s', fmtstring}, - {'U', fmtflag}, - {'u', fmtint}, - {'x', fmtint}, - } -}; - -// ----------------------------------------------------------------------- -// internal functions - -static Formatter -format(int c) -{ - Verb *v, *e; - e = &formatter.verb[atomic·load(&formatter.len)]; - for(v=e; v > formatter.verb; --v){ - if(v->c == c) - return v->fmt; - } - - return badfmt; -} - -static char * -dispatch(fmt·State *io, char *fmt) -{ - rune r; - int i, n; - - io->flag = 0; - io->width = io->prec = 0; - - /* - * the form of each print verb: - * % [flags] verb - * + the verb is a single character - * + each flag is either - * - a single character - * - a decimal numeric string - * - up to 2 decimal strings can be used - * - [width|*].[prec|*] - * - if missing, set to 0 - * - if *, grab from varargs - */ - for(;;){ - fmt += utf8·decode(fmt, &r); - io->verb = r; - switch(r){ - case 0: - return nil; - case '.': - io->flag |= fmt·Width|fmt·Prec; - continue; - case '0': - if(!(io->flag & fmt·Width)){ - io->flag |= fmt·Zero; - continue; - } - /* fallthrough */ - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - i = 0; - while('0' <= r && r <= '9'){ - i = 10*i + (r-'0'); - r = *fmt++; - } - fmt--; - number: - if(io->flag & fmt·Width){ - io->flag |= fmt·Prec; - io->prec = i; - }else{ - io->flag |= fmt·Width; - io->width = i; - } - continue; - case '*': - i = va_arg(io->args, int); - if(i < 0){ - if(io->flag&fmt·Prec){ - io->flag &= ~fmt·Prec; - io->prec = 0; - continue; - } - i = -i; - io->flag |= fmt·Left; - } - goto number; - } - n = format(r)(io); - if(n < 0) - return nil; - if(!n) - return fmt; - } -} - -static char * -flush(fmt·State *io, char *b, int len) -{ - io->n += b - io->buffer.cur; - io->buffer.cur = b; - if(!io->flush || !(*io->flush)(io) || io->buffer.cur + len >= io->buffer.end) { - io->buffer.end = io->buffer.cur; - return nil; - } - return io->buffer.cur; -} - -static int -pad(fmt·State *io, int n) -{ - int i; - char *b=io->buffer.cur, *e=io->buffer.end; - - for(i=0; i=e){ - if(!(b=flush(io, b, 1))) - return -1; - e = io->buffer.end; - } - *b++ = ' '; - } - - io->n += b - io->buffer.cur; - io->buffer.cur = b; - return 0; -} - -static int -copy(fmt·State *io, char *m, int sz, int n) -{ - ulong f; - rune r; - int nc, w, nb; - char *b, *e, *me; - - w = 0; - f = io->flag; - me = m + sz; - - if(f&fmt·Width) - w = io->width; - if(f&fmt·Prec && n > io->prec) - n = io->prec; - if(!(f&fmt·Left) && pad(io, w-n)<0) - return -1; - - b = io->buffer.cur; - e = io->buffer.end; - - for(nc=n; nc>0; nc--){ - r = *(uchar *)m; - if(utf8·onebyte(r)){ - nb=1; - m++; - }else if((me-m) >= UTFmax || utf8·canfit(m, me-m)){ - nb=utf8·decode(m, &r); - m+=n; - }else - break; - - if(b+n>e){ - if(!(b=flush(io, b, nb))) - return -1; - e = io->buffer.end; - } - b += utf8·encode(&r, b); - } - - io->n += b - io->buffer.cur; - io->buffer.cur = b; - if(f&fmt·Left && pad(io, w-n)<0) - return -1; - - return 0; -} - -static int -copyrune(fmt·State *io, rune *m, int n) -{ - ulong f; - rune r, *me; - int w, nb; - char *b, *e; - - w = 0; - f = io->flag; - - if(f&fmt·Width) - w = io->width; - if(f&fmt·Prec && n > io->prec) - n = io->prec; - - if(!(f&fmt·Left) && pad(io, w-n)<0) - return -1; - - b = io->buffer.cur; - e = io->buffer.end; - - for(me=m+n; m < me; m++){ - r = *m; - nb = utf8·runelen(r); - if(b + nb > e){ - if(!(b=flush(io, b, nb))) - return -1; - e = io->buffer.end; - } - b += utf8·encode(&r, b); - } - - io->n += b - io->buffer.cur; - io->buffer.cur = b; - if(f&fmt·Left && pad(io, w-n)<0) - return -1; - - return 0; -} - -static int -copystring(fmt·State *io, char *s) -{ - rune r; - int i,j; - - if(!s) - return copy(io, "", 5, 5); - - if(io->flag&fmt·Prec){ - i = 0; - for(j=0; j < io->prec && s[i]; j++) - i += utf8·decode(s+i, &r); - - return copy(io, s, i, j); - } - return copy(io, s, strlen(s), utf8·len(s)); -} - -static int -copyutf8(fmt·State *io, rune *s) -{ - rune *e; - int n,p; - - if(!s) - return copy(io, "", 5, 5); - - if(io->flag & fmt·Prec){ - p = io->prec; - for(n=0; n group){ - if((*groups)[1] != 0) - (*groups)++; - *digits = 1; - return 1; - } - return 0; -} - -// ----------------------------------------------------------------------- -// formatters - -static int -fmtchar(fmt·State *io) -{ - char x[1]; - x[0] = va_arg(io->args, int); - io->prec = 1; - - return copy(io, x, 1, 1); -} - -static int -fmtstring(fmt·State *io) -{ - char *s; - s = va_arg(io->args, char *); - return copystring(io, s); -} - -static int -fmterror(fmt·State *io) -{ - char *s; - s = strerror(errno); - return copystring(io, s); -} - -static int -fmtrune(fmt·State *io) -{ - rune x[1]; - - x[0] = va_arg(io->args, int); - return copyrune(io, x, 1); -} - -static int -fmtutf8(fmt·State *io) -{ - rune *s; - - s = va_arg(io->args, rune *); - return copyutf8(io, s); -} - -static int -fmtpercent(fmt·State *io) -{ - rune x[1]; - - x[0] = io->verb; - io->prec = 1; - return copyrune(io, x, 1); -} - -static int -fmtint(fmt·State *io) -{ - union{ - ulong u; - uvlong v; - } val; - int neg, base, i, n, f, w, isv; - int digits, bytes, runes, excess; - char *groups, *thousands; - char *p, *conv, buf[140]; - - f = io->flag; - neg = 0; - isv = 0; - val.u = 0; - - switch(io->verb){ - case 'o': case 'p': case 'u': case 'x': case 'X': - f |= fmt·Unsigned; - f &= ~(fmt·Sign|fmt·Space); - } - - /* set flags */ - if(io->verb=='p'){ - val.u = (ulong)va_arg(io->args, void*); - io->verb = 'x'; - f |= fmt·Unsigned; - }else if(f&fmt·Vlong){ - isv=1; - if(f&fmt·Unsigned) - val.v = va_arg(io->args, uvlong); - else - val.v = va_arg(io->args, vlong); - }else if(f&fmt·Long){ - if(f&fmt·Unsigned) - val.u = va_arg(io->args, ulong); - else - val.u = va_arg(io->args, long); - }else if(f&fmt·Byte){ - if(f&fmt·Unsigned) - val.u = (uchar)va_arg(io->args, int); - else - val.u = (char)va_arg(io->args, int); - }else if(f&fmt·Short){ - if(f&fmt·Unsigned) - val.u = (ushort)va_arg(io->args, int); - else - val.u = (short)va_arg(io->args, int); - }else{ - if(f&fmt·Unsigned) - val.u = va_arg(io->args, uint); - else - val.u = va_arg(io->args, int); - } - - conv = "0123456789abcdef"; - groups = "\4"; - thousands = io->thousands; - /* get base */ - switch(io->verb){ - case 'd': case 'i': case 'u': - base = 10; - groups = io->groups; - break; - case 'X': - conv = "0123456789ABCDEF"; - /*fallthrough*/ - case 'x': - base = 16; - thousands = ":"; - break; - case 'b': - base = 2; - thousands = ":"; - break; - case 'o': - base = 8; - break; - default: - return -1; - } - - /* check for negativity */ - if(!(f&fmt·Unsigned)){ - if(isv && (vlong)val.v < 0){ - val.v = -(vlong)val.v; - neg = 1; - }else if(!isv && (long)val.u < 0){ - val.u = -(long)val.u; - neg = 1; - } - } - - p = buf + sizeof(buf) - 1; - n = 0; - digits = 0; - excess = 0; - runes = utf8·len(thousands); - bytes = strlen(thousands); - -#define PARSE(VALUE) \ - while((VALUE)){ \ - i = (VALUE) % base; \ - (VALUE) /= base; \ - if((f&fmt·Comma) && n%4 == 3){ \ - *p-- = ','; \ - n++; \ - } \ - if((f&fmt·Apost) && needseperate(&digits, &groups)){ \ - n += runes; \ - excess += bytes - runes; \ - p -= bytes; \ - memmove(p+1, thousands, bytes); \ - } \ - *p-- = conv[i]; \ - n++; \ - } - if(isv) - PARSE(val.v) - else - PARSE(val.u) -#undef PARSE - - if(!n){ - if(!(f&fmt·Prec) || io->prec != 0 || (io->verb == 'o' && (f&fmt·Sharp))){ - *p-- = '0'; - n = 1; - if(f&fmt·Apost) - needseperate(&digits,&groups); - } - - if(io->verb == 'x' || io->verb == 'X') - f &= ~fmt·Sharp; - } - - for(w = io->prec; n < w && p > buf+3; n++){ - if((f&fmt·Apost) && needseperate(&digits, &groups)){ - n += runes; - excess += bytes - runes; - p -= bytes; - memmove(p+1, thousands, bytes); - } - *p-- = '0'; - } - - if(neg || (f&(fmt·Sign|fmt·Space))) - n++; - - if(f&fmt·Sharp){ - if(base==16) - n += 2; - else if(base == 8){ - if(p[1] == '0') - f &= ~fmt·Sharp; - else - n++; - } - } - - if(f&fmt·Zero && !(f & (fmt·Left|fmt·Prec))){ - w = 0; - if(f & fmt·Width) - w = io->width; - for(; n < w && p > buf+3; n++){ - if((f & fmt·Apost) && needseperate(&digits, &groups)){ - n += runes; - excess += bytes - runes; - p -= bytes; - memmove(p+1, thousands, bytes); - } - *p-- = '0'; - } - io->flag &= ~fmt·Width; - } - - if(f&fmt·Sharp){ - if(base==16) - *p-- = io->verb; - if(base==16 || base == 8) - *p-- = '0'; - } - - if(neg) - *p-- = '-'; - else if(f & fmt·Sign) - *p-- = '+'; - else if (f & fmt·Space) - *p-- = ' '; - - io->flag &= ~fmt·Prec; - return copy(io, p+1, n+excess, n); -} - -static int -fmtcount(fmt·State *io) -{ - void *p; - ulong f; - - f = io->flag; - p = va_arg(io->args, void*); - - if(f&fmt·Vlong) - *(vlong*)p = io->n; - else if(f&fmt·Long) - *(long*)p = io->n; - else if(f&fmt·Byte) - *(char*)p = io->n; - else if(f&fmt·Short) - *(short*)p = io->n; - else - *(int*)p = io->n; - - return 0; -} - -static int -fmtflag(fmt·State *io) -{ - switch(io->verb){ - case ',': io->flag |= fmt·Comma; break; - case '-': io->flag |= fmt·Left; break; - case '+': io->flag |= fmt·Sign; break; - case '#': io->flag |= fmt·Sharp; break; - case '\'': io->flag |= fmt·Apost; break; - case ' ': io->flag |= fmt·Space; break; - case 'u': io->flag |= fmt·Unsigned; break; - case 'L': io->flag |= fmt·Ldouble; break; - case 'h': - if(io->flag&fmt·Short) - io->flag |= fmt·Byte; - io->flag |= fmt·Short; - break; - case 'l': - if(io->flag&fmt·Long) - io->flag |= fmt·Vlong; - io->flag |= fmt·Long; - break; - } - return 1; -} - -static int -badfmt(fmt·State *io) -{ - int n; - char x[UTFmax+2]; - - x[0] = '%'; - n = 1 + utf8·encode(&io->verb, x+1); - x[n++] = '%'; - io->prec = n; - copy(io, x, n, n); - - return 0; -} - -#include "float.c" - -// ----------------------------------------------------------------------- -// exports - -int -fmt·do(fmt·State *io, char *fmt) -{ - rune r; - int c, n; - char *b, *e; - - for(;;){ - b = io->buffer.cur; - e = io->buffer.end; - while((c = *(uchar *)fmt) && c != '%'){ - if(utf8·onebyte(c)){ - if(b >= e){ - if(!(b=flush(io, b, 1))) - return -1; - e = io->buffer.end; - } - *b++ = *fmt++; - }else{ - n = utf8·decode(fmt, &r); - if(b + n > e){ - if(!(b=flush(io, b, n))) - return -1; - e = io->buffer.end; - } - while(n--) - *b++ = *fmt++; - } - } - fmt++; - io->n += b - io->buffer.cur; - io->buffer.cur = b; - if(!c) /* we hit our nul terminator */ - return io->n - n; - io->buffer.end = e; - - if(!(fmt=dispatch(io, fmt))) - return -1; - } -} - -int -fmt·install(int verb, Formatter func) -{ - Verb *v; - int i, ret; - -lock: - if(verb <= 0 || verb >= 65536){ - ret = -1; - goto unlock; - } - if(!func) - func = badfmt; - - if((i = atomic·load(&formatter.len))==MaxFmt) - return -1; - - v = &formatter.verb[i]; - v->c = verb; - v->fmt = func; - - atomic·store(&formatter.len, i+1); - ret = 0; -unlock: - return ret; -} diff --git a/sys/libfmt/esprint.c b/sys/libfmt/esprint.c deleted file mode 100644 index 6d97340..0000000 --- a/sys/libfmt/esprint.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "internal.h" - -char * -fmt·esprint(char *buf, char *end, char *fmt, ...) -{ - char *p; - va_list args; - - va_start(args, fmt); - p = fmt·vesprint(buf, end, fmt, args); - va_end(args); - - return p; -} diff --git a/sys/libfmt/float.c b/sys/libfmt/float.c deleted file mode 100644 index 63ea80f..0000000 --- a/sys/libfmt/float.c +++ /dev/null @@ -1,1077 +0,0 @@ -#define FDIGIT 30 -#define FDEFLT 6 -#define NSIGNIF 17 - -static uvlong uvnan = ((uvlong)0x7FF00000<<32)|0x00000001; -static uvlong uvinf = ((uvlong)0x7FF00000<<32)|0x00000000; -static uvlong uvneginf = ((uvlong)0xFFF00000<<32)|0x00000000; - -static char *special[] = { "NaN", "NaN", "+Inf", "+Inf", "-Inf", "-Inf" }; - -static int -isNaN(double val) -{ - union{ - uvlong i; - double f; - }x; - - x.f = val; - return (x.i&uvinf) == uvinf && (x.i&~uvneginf) != 0; -} - -static double -NaN(void) -{ - union{ - uvlong i; - double f; - }x; - x.i = uvnan; - return x.f; -} - -static int -isInf(double val, int sign) -{ - union{ - uvlong i; - double f; - }x; - - x.f = val; - if(sign == 0) - return x.i == uvinf || x.i == uvneginf; - else if(sign == 1) - return x.i == uvinf; - else - return x.i == uvneginf; -} - -static double pows10[] = -{ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, - 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, - 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, - 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, - 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, - 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, - 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, - 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, - 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, - 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, - 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, - 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, - 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, - 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, -}; - -static double -fpow10(int n) -{ - double d; - int neg; - - neg = 0; - if(n < 0){ - neg = 1; - n = -n; - } - - if(n NSIGNIF) - return 0; - - for(b = a+n-1; b >= a; b--){ - c = *b + 1; - if(c <= '9'){ - *b = c; - return 0; - } - *b = '0'; - } - /* - * need to overflow adding digit. - * shift number down and insert 1 at beginning. - * decimal is known to be 0s or we wouldn't - * have gotten this far. (e.g., 99999+1 => 00000) - */ - a[0] = '1'; - return 1; -} - -static int -sub1(char *a, int n) -{ - int c; - char *b; - - if(n < 0 || n > NSIGNIF) - return 0; - for(b = a+n-1; b >= a; b--){ - c = *b - 1; - if(c >= '0'){ - if(c == '0' && b == a){ - /* - * just zeroed the top digit; shift everyone up. - * decimal is known to be 9s or we wouldn't - * have gotten this far. (e.g., 10000-1 => 09999) - */ - *b = '9'; - return 1; - } - *b = c; - return 0; - } - *b = '9'; - } - /* - * can't get here. the number a is always normalized - * so that it has a nonzero first digit. - */ - abort(); -} - -// ----------------------------------------------------------------------- -// strtod - -#define Nbits 28 -#define Nmant 53 -#define Prec ((Nmant+Nbits+1)/Nbits) - -#define Sigbit (1<<(Prec*Nbits-Nmant)) /* first significant bit of Prec-th word */ -#define Ndig 1500 -#define One (ulong)(1<>1) -#define Maxe 310 - -#define Fsign (1<<0) /* found - */ -#define Fesign (1<<1) /* found e- */ -#define Fdpoint (1<<2) /* found . */ - -#define S0 0 /* _ _S0 +S1 #S2 .S3 */ -#define S1 1 /* _+ #S2 .S3 */ -#define S2 2 /* _+# #S2 .S4 eS5 */ -#define S3 3 /* _+. #S4 */ -#define S4 4 /* _+#.# #S4 eS5 */ -#define S5 5 /* _+#.#e +S6 #S7 */ -#define S6 6 /* _+#.#e+ #S7 */ -#define S7 7 /* _+#.#e+# #S7 */ - -typedef struct Tab Tab; -struct Tab -{ - int bp; - int siz; - char *cmp; -}; - -static ulong -umuldiv(ulong a, ulong b, ulong c) -{ - double d; - - d = ((double)a * (double)b) / (double)c; - if(d >= 4294967295.) - d = 4294967295.; - return (ulong)d; -} - -static void -frnorm(ulong *f) -{ - int i, c; - - c = 0; - for(i=Prec-1; i>0; i--) { - f[i] += c; - c = f[i] >> Nbits; - f[i] &= One-1; - } - f[0] += c; -} - -static int -fpcmp(char *a, ulong* f) -{ - ulong tf[Prec]; - int i, d, c; - - for(i=0; i> Nbits) + '0'; - tf[0] &= One-1; - - /* compare next digit */ - c = *a; - if(c == 0) { - if('0' < d) - return -1; - if(tf[0] != 0) - goto cont; - for(i=1; i d) - return +1; - if(c < d) - return -1; - a++; - cont:; -} -} - -static void -divby(char *a, int *na, int b) -{ - int n, c; - char *p; - - p = a; - n = 0; - while(n>>b == 0){ - c = *a++; - if(c == 0) { - while(n) { - c = n*10; - if(c>>b) - break; - n = c; - } - goto xx; - } - n = n*10 + c-'0'; - (*na)--; - } - for(;;){ - c = n>>b; - n -= c<>b; - n -= c<= (int)(arrlen(tab1))) - d = (int)(arrlen(tab1))-1; - t = tab1 + d; - b = t->bp; - if(memcmp(a, t->cmp, t->siz) > 0) - d--; - *dp -= d; - *bp += b; - divby(a, na, b); -} - -static void -mulby(char *a, char *p, char *q, int b) -{ - int n, c; - - n = 0; - *p = 0; - for(;;) { - q--; - if(q < a) - break; - c = *q - '0'; - c = (c<= (int)(arrlen(tab2))) - d = (int)(arrlen(tab2))-1; - t = tab2 + d; - b = t->bp; - if(memcmp(a, t->cmp, t->siz) < 0) - d--; - p = a + *na; - *bp -= b; - *dp += d; - *na += d; - mulby(a, p+d, p, b); -} - -static int -cmp(char *a, char *b) -{ - int c1, c2; - - while((c1 = *b++) != '\0') { - c2 = *a++; - if(isupper(c2)) - c2 = tolower(c2); - if(c1 != c2) - return 1; - } - return 0; -} - -double -fmtstrtod(char *as, char **aas) -{ - int na, ex, dp, bp, c, i, flag, state; - ulong low[Prec], hig[Prec], mid[Prec]; - double d; - char *s, a[Ndig]; - - flag = 0; /* Fsign, Fesign, Fdpoint */ - na = 0; /* number of digits of a[] */ - dp = 0; /* na of decimal point */ - ex = 0; /* exonent */ - - state = S0; - for(s=as;;s++){ - c = *s; - if('0' <= c && c <= '9'){ - switch(state){ - case S0: case S1: case S2: - state = S2; - break; - case S3: case S4: - state = S4; - break; - case S5: case S6: case S7: - state = S7; - ex = ex*10 + (c-'0'); - continue; - } - - if(na == 0 && c == '0'){ - dp--; - continue; - } - if(na < Ndig-50) - a[na++] = c; - continue; - } - switch(c){ - case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': - if(state == S0) - continue; - break; - case '-': - if(state == S0) - flag |= Fsign; - else - flag |= Fesign; - case '+': - if(state == S0) - state = S1; - else - if(state == S5) - state = S6; - else - break; /* syntax */ - continue; - case '.': - flag |= Fdpoint; - dp = na; - if(state == S0 || state == S1){ - state = S3; - continue; - } - if(state == S2){ - state = S4; - continue; - } - break; - case 'e': case 'E': - if(state == S2 || state == S4){ - state = S5; - continue; - } - break; - } - break; - } - - /* clean up return char-pointer */ - switch(state) { - case S0: - if(cmp(s, "nan") == 0){ - if(aas != nil) - *aas = s+3; - goto retnan; - } - case S1: - if(cmp(s, "infinity") == 0){ - if(aas != nil) - *aas = s+8; - goto retinf; - } - if(cmp(s, "inf") == 0){ - if(aas != nil) - *aas = s+3; - goto retinf; - } - case S3: - if(aas != nil) - *aas = as; - goto ret0; /* no digits found */ - case S6: - s--; /* back over +- */ - case S5: - s--; /* back over e */ - break; - } - if(aas != nil) - *aas = s; - - if(flag & Fdpoint) - while(na > 0 && a[na-1] == '0') - na--; - if(na == 0) - goto ret0; /* zero */ - a[na] = 0; - if(!(flag & Fdpoint)) - dp = na; - if(flag & Fesign) - ex = -ex; - dp += ex; - if(dp < -Maxe){ - errno = ERANGE; - goto ret0; /* underflow by exp */ - } else - if(dp > +Maxe) - goto retinf; /* overflow by exp */ - - /* - * normalize the decimal ascii number - * to range .[5-9][0-9]* e0 - */ - bp = 0; /* binary exponent */ - while(dp > 0) - divascii(a, &na, &dp, &bp); - while(dp < 0 || a[0] < '5') - mulascii(a, &na, &dp, &bp); - - /* close approx by naive conversion */ - mid[0] = 0; - mid[1] = 1; - for(i=0; (c=a[i]) != '\0'; i++) { - mid[0] = mid[0]*10 + (c-'0'); - mid[1] = mid[1]*10; - if(i >= 8) - break; - } - low[0] = umuldiv(mid[0], One, mid[1]); - hig[0] = umuldiv(mid[0]+1, One, mid[1]); - for(i=1; i>= 1; - } - frnorm(mid); - - /* compare */ - c = fpcmp(a, mid); - if(c > 0) { - c = 1; - for(i=0; i= Sigbit/2) { - mid[Prec-1] += Sigbit; - frnorm(mid); - } - goto out; - -ret0: - return 0; - -retnan: - return NaN(); - -retinf: - /* Unix strtod requires these. Plan 9 would return Inf(0) or Inf(-1). */ - errno = ERANGE; - if(flag & Fsign) - return -HUGE_VAL; - return HUGE_VAL; - -out: - d = 0; - for(i=0; i 0) - *p++ = se[--i]; - - *p++ = '\0'; -} - -/* - * compute decimal integer m, exp such that: - * f = m*10^exp - * m is as short as possible with losing exactness - * assumes special cases (NaN, +Inf, -Inf) have been handled. - */ -static void -dtoa(double f, char *s, int *exp, int *neg, int *len) -{ - int c, d, e2, e, ee, i, ndigit, oerrno; - char buf[NSIGNIF+10]; - double g; - - oerrno = errno; - - *neg = 0; - if(f < 0){ - f = -f; - *neg = 1; - } - - if(f == 0){ - *exp = 0; - s[0] = '0'; - s[1] = 0; - *len = 1; - return; - } - - frexp(f, &e2); - e = (int)(e2 * .301029995664); - g = f * fpow10(-e); - while(g < 1) { - e--; - g = f * fpow10(-e); - } - while(g >= 10){ - e++; - g = f * fpow10(-e); - } - - /* convert nsignif digits as a first approximation */ - for(i=0; i g) { - if(add1(s, NSIGNIF)){ - /* gained a digit */ - e--; - fmtexp(s+NSIGNIF, e, 0); - } - continue; - } - if(f < g){ - if(sub1(s, NSIGNIF)){ - /* lost a digit */ - e++; - fmtexp(s+NSIGNIF, e, 0); - } - continue; - } - break; - } - - /* - * bump last few digits down to 0 as we can. - */ - for(i=NSIGNIF-1; i>=NSIGNIF-3; i--){ - c = s[i]; - if(c != '0'){ - s[i] = '0'; - g=fmtstrtod(s, nil); - if(g != f){ - s[i] = c; - break; - } - } - } - - /* - * remove trailing zeros. - */ - ndigit = NSIGNIF; - while(ndigit > 1 && s[ndigit-1] == '0'){ - e++; - --ndigit; - } - s[ndigit] = 0; - *exp = e; - *len = ndigit; - - errno = oerrno; -} - - -static int -fmtfloat(fmt·State *io) -{ - char buf[NSIGNIF+10], *dot, *digits, *p, *end, suf[10], *cur; - double val; - int c, verb, ndot, e, exp, f, ndigits, neg, newndigits; - int npad, pt, prec, realverb, sign, nsuf, ucase, n, z1, z2; - - if(io->flag&fmt·Long) - val = va_arg(io->args, long double); - else - val = va_arg(io->args, double); - - /* extract formatting flags */ - f = io->flag; - io->flag = 0; - prec = FDEFLT; - if(f & fmt·Prec) - prec = io->prec; - - verb = io->verb; - ucase = 0; - switch(verb) { - case 'A': - case 'E': - case 'F': - case 'G': - verb += 'a'-'A'; - ucase = 1; - break; - } - - /* pick off special numbers. */ - if(isNaN(val)) { - end = special[0+ucase]; - special: - io->flag = f & (fmt·Width|fmt·Left); - return copy(io, end, strlen(end), strlen(end)); - } - if(isInf(val, 1)) { - end = special[2+ucase]; - goto special; - } - if(isInf(val, -1)) { - end = special[4+ucase]; - goto special; - } - - /* get exact representation. */ - digits = buf; - dtoa(val, digits, &exp, &neg, &ndigits); - - /* get locale's decimal point. */ - dot = io->decimal; - if(dot == nil) - dot = "."; - ndot = utf8·len(dot); - - /* - * now the formatting fun begins. - * compute parameters for actual fmt: - * - * pad: number of spaces to insert before/after field. - * z1: number of zeros to insert before digits - * z2: number of zeros to insert after digits - * point: number of digits to print before decimal point - * ndigits: number of digits to use from digits[] - * suf: trailing suffix, like "e-5" - */ - realverb = verb; - switch(verb){ - case 'g': - /* convert to at most prec significant digits. (prec=0 means 1) */ - if(prec == 0) - prec = 1; - if(ndigits > prec) { - if(digits[prec] >= '5' && add1(digits, prec)) - exp++; - exp += ndigits-prec; - ndigits = prec; - } - - /* - * extra rules for %g (implemented below): - * trailing zeros removed after decimal unless FmtSharp. - * decimal point only if digit follows. - */ - - /* fall through to %e */ - default: - case 'e': - /* one significant digit before decimal, no leading zeros. */ - pt = 1; - z1 = 0; - - /* - * decimal point is after ndigits digits right now. - * slide to be after first. - */ - e = exp + (ndigits-1); - - /* if this is %g, check exponent and convert prec */ - if(realverb == 'g') { - if(-4 <= e && e < prec) - goto casef; - prec--; /* one digit before decimal; rest after */ - } - - /* compute trailing zero padding or truncate digits. */ - if(1+prec >= ndigits) - z2 = 1+prec - ndigits; - else { - /* truncate digits */ - assert(realverb != 'g'); - newndigits = 1+prec; - if(digits[newndigits] >= '5' && add1(digits, newndigits)) { - /* had 999e4, now have 100e5 */ - e++; - } - ndigits = newndigits; - z2 = 0; - } - fmtexp(suf, e, ucase); - nsuf = strlen(suf); - break; - - casef: - case 'f': - /* determine where digits go with respect to decimal point */ - if(ndigits+exp > 0) { - pt = ndigits+exp; - z1 = 0; - } else { - pt = 1; - z1 = 1 + -(ndigits+exp); - } - - /* - * %g specifies prec = number of significant digits - * convert to number of digits after decimal point - */ - if(realverb == 'g') - prec += z1 - pt; - - /* compute trailing zero padding or truncate digits. */ - if(pt+prec >= z1+ndigits) - z2 = pt+prec - (z1+ndigits); - else{ - /* truncate digits */ - assert(realverb != 'g'); - newndigits = pt+prec - z1; - if(newndigits < 0){ - z1 += newndigits; - newndigits = 0; - }else if(newndigits == 0){ - /* perhaps round up */ - if(digits[0] >= '5'){ - digits[0] = '1'; - newndigits = 1; - goto newdigit; - } - }else if(digits[newndigits] >= '5' && add1(digits, newndigits)){ - /* digits was 999, is now 100; make it 1000 */ - digits[newndigits++] = '0'; - newdigit: - /* account for new digit */ - if(z1) /* 0.099 => 0.100 or 0.99 => 1.00*/ - z1--; - else /* 9.99 => 10.00 */ - pt++; - } - z2 = 0; - ndigits = newndigits; - } - nsuf = 0; - break; - } - - /* - * if %g is given without FmtSharp, remove trailing zeros. - * must do after truncation, so that e.g. print %.3g 1.001 - * produces 1, not 1.00. sorry, but them's the rules. - */ - if(realverb == 'g' && !(f & fmt·Sharp)) { - if(z1+ndigits+z2 >= pt) { - if(z1+ndigits < pt) - z2 = pt - (z1+ndigits); - else{ - z2 = 0; - while(z1+ndigits > pt && digits[ndigits-1] == '0') - ndigits--; - } - } - } - - /* - * compute width of all digits and decimal point and suffix if any - */ - n = z1+ndigits+z2; - if(n > pt) - n += ndot; - else if(n == pt){ - if(f & fmt·Sharp) - n += ndot; - else - pt++; /* do not print any decimal point */ - } - n += nsuf; - - /* - * determine sign - */ - sign = 0; - if(neg) - sign = '-'; - else if(f & fmt·Sign) - sign = '+'; - else if(f & fmt·Space) - sign = ' '; - if(sign) - n++; - - /* compute padding */ - npad = 0; - if((f & fmt·Width) && io->width > n) - npad = io->width - n; - if(npad && !(f & fmt·Left) && (f & fmt·Zero)){ - z1 += npad; - pt += npad; - npad = 0; - } - - /* format the actual field. too bad about doing this twice. */ - if(npad && !(f & fmt·Left) && pad(io, npad < 0)) - return -1; - - cur = io->buffer.cur; - end = io->buffer.end; - - if(sign){ - if(cur+1 > end){ - if(!(cur=flush(io,cur,1))) - return -1; - end = io->buffer.end; - } - *cur++ = sign; - } - - while(z1>0 || ndigits>0 || z2>0){ - if(z1 > 0){ - z1--; - c = '0'; - }else if(ndigits > 0){ - ndigits--; - c = *digits++; - }else{ - z2--; - c = '0'; - } - - if(cur+1 > end){ - if(!(cur=flush(io,cur,1))) - return -1; - end = io->buffer.end; - } - *cur++ = c; - - if(--pt == 0) - for(p=dot; *p; p++){ - if(cur+1 > end){ - if(!(cur=flush(io,cur,1))) - return -1; - end = io->buffer.end; - } - *cur++ = *p; - } - } - io->n += cur - (char*)io->buffer.cur; - io->buffer.cur = cur; - if(nsuf && copy(io, suf, nsuf, nsuf) < 0) - return -1; - if(npad && (f & fmt·Left) && pad(io, npad < 0)) - return -1; - - return 0; -} diff --git a/sys/libfmt/fprint.c b/sys/libfmt/fprint.c deleted file mode 100644 index 26343f7..0000000 --- a/sys/libfmt/fprint.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "internal.h" - -int -fprint(int fd, char *fmt, ...) -{ - int n; - va_list args; - - va_start(args, fmt); - n = fmt·vfprint(fd, fmt, args); - va_end(args); - - return n; -} diff --git a/sys/libfmt/internal.h b/sys/libfmt/internal.h deleted file mode 100644 index 725cfff..0000000 --- a/sys/libfmt/internal.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -typedef int (*Formatter)(fmt·State *io); -typedef struct Verb Verb; - -struct Verb -{ - int c; - Formatter fmt; -}; - -void fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups); diff --git a/sys/libfmt/locale.c b/sys/libfmt/locale.c deleted file mode 100644 index 437c61e..0000000 --- a/sys/libfmt/locale.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "internal.h" - -void -fmt·setlocale(fmt·State *io, char *decimal, char *thousands, char *groups) -{ - if(decimal == nil || decimal[0] == '\0') - decimal = "."; - if(thousands == nil) - thousands = ","; - if(groups == nil) - groups = "\3"; - - io->groups = groups; - io->decimal = decimal; - io->thousands = thousands; -} diff --git a/sys/libfmt/nsprint.c b/sys/libfmt/nsprint.c deleted file mode 100644 index 90489e0..0000000 --- a/sys/libfmt/nsprint.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "internal.h" - -int -fmt·nsprint(int len, char *buf, char *fmt, ...) -{ - int n; - va_list args; - - va_start(args, fmt); - n = fmt·vnsprint(len, buf, fmt, args); - va_end(args); - - return n; -} diff --git a/sys/libfmt/open.c b/sys/libfmt/open.c deleted file mode 100644 index 8aadef5..0000000 --- a/sys/libfmt/open.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "internal.h" - -static int -flush(fmt·State *io) -{ - int n, fd; - - fd = (uintptr)io->file; - n = io->buffer.cur - io->buffer.beg; - if(n && write(fd, io->buffer.beg, n) != n) - return -1; - - io->buffer.cur = io->buffer.beg; - return io->n; -} - -int -fmt·open(int fd, int len, char *buf, fmt·State *io) -{ - io->buffer.beg = buf; - io->buffer.cur = buf; - io->buffer.end = buf+len; - io->flush = flush; - io->file = (void*)(uintptr)fd; - io->flag = 0; - io->n = 0; - /* no heap needed */ - io->heap = nil; - io->mem = (mem·Reallocator){ 0 }; - - fmt·setlocale(io, nil, nil, nil); - - return 0; -} diff --git a/sys/libfmt/print.c b/sys/libfmt/print.c deleted file mode 100644 index 20b8e00..0000000 --- a/sys/libfmt/print.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "internal.h" - -int -fmt·print(char *fmt, ...) -{ - int n; - va_list args; - - va_start(args, fmt); - n = fmt·vfprint(1, fmt, args); - va_end(args); - return n; -} diff --git a/sys/libfmt/rules.mk b/sys/libfmt/rules.mk deleted file mode 100644 index 2b1b431..0000000 --- a/sys/libfmt/rules.mk +++ /dev/null @@ -1,36 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) :=\ - $(d)/buffer.c\ - $(d)/do.c\ - $(d)/esprint.c\ - $(d)/fprint.c\ - $(d)/locale.c\ - $(d)/nsprint.c\ - $(d)/open.c\ - $(d)/print.c\ - $(d)/sprint.c\ - $(d)/vesprint.c\ - $(d)/vfprint.c\ - $(d)/vnsprint.c\ - $(d)/vprint.c\ - $(d)/vwrite.c\ - $(d)/write.c - -LIBS_$(d) := $(d)/libfmt.a - -TSTS_$(d) := \ - $(d)/test.c - -include share/paths.mk - -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/sys/libutf/libutf.a $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -include share/pop.mk diff --git a/sys/libfmt/sprint.c b/sys/libfmt/sprint.c deleted file mode 100644 index f1be6dd..0000000 --- a/sys/libfmt/sprint.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "internal.h" - -int -fmt·sprint(char *buf, char *fmt, ...) -{ - int n; - uint len; - va_list args; - - len = 1 << 30; - if(buf+len < buf) - len = -(uintptr)buf-1; - - va_start(args, fmt); - n = fmt·vnsprint(len, buf, fmt, args); - va_end(args); - - return n; -} diff --git a/sys/libfmt/test.c b/sys/libfmt/test.c deleted file mode 100644 index d81a62e..0000000 --- a/sys/libfmt/test.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include - -typedef struct Complex -{ - double r, i; -} Complex; - -int -Xfmt(fmt·State *io) -{ - Complex c; - c = va_arg(io->args, Complex); - - return fmt·write(io, "(real=%g,imag=%g)", c.r, c.i); -} - -int -main(int argc, char *argv[]) -{ - fmt·print("basic tests\n"); - fmt·print("\tx: %x\n", 0x87654321); - fmt·print("\tu: %u\n", 0x87654321); - fmt·print("\td: %d\n", 0x87654321); - fmt·print("\ts: %s\n", "hi there"); - fmt·print("\tc: %c\n", '!'); - fmt·print("\tg: %g %g %g\n", 3.14159, 3.14159e10, 3.14159e-10); - fmt·print("\te: %e %e %e\n", 3.14159, 3.14159e10, 3.14159e-10); - fmt·print("\tf: %f %f %f\n", 3.14159, 3.14159e10, 3.14159e-10); - fmt·print("\tsmiley: %C\n", (rune)0x263a); - fmt·print("\t%g %.18g\n", 2e25, 2e25); - fmt·print("\t%2.18g\n", 1.0); - fmt·print("\t%2.18f\n", 1.0); - fmt·print("\t%f\n", 3.1415927/4); - fmt·print("\t%d\n", 23); - fmt·print("\t%i\n", 23); - fmt·print("\t%0.10d\n", 12345); - - fmt·print("%%4%%d tests\n"); - fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); - fmt·print("\t%3$d %4$06d %2$d %1$d\n", 444, 333, 111, 222); - fmt·print("\t%3$d %4$*5$06d %2$d %1$d\n", 444, 333, 111, 222, 20); - fmt·print("\t%3$hd %4$*5$06d %2$d %1$d\n", 444, 333, (short)111, 222, 20); - fmt·print("\t%3$lld %4$*5$06d %2$d %1$d\n", 444, 333, 111LL, 222, 20); - - /* test %'d formats */ - fmt·print("%%'%%d tests\n"); - fmt·print("\t%'d %'d %'d\n", 1, 2222, 33333333); - fmt·print("\t%'019d\n", 0); - fmt·print("\t%08d %08d %08d\n", 1, 2222, 33333333); - fmt·print("\t%'08d %'08d %'08d\n", 1, 2222, 33333333); - fmt·print("\t%'x %'X %'b\n", 0x11111111, 0xabcd1234, 12345); - fmt·print("\t%'lld %'lld %'lld\n", 1LL, 222222222LL, 3333333333333LL); - fmt·print("\t%019lld %019lld %019lld\n", 1LL, 222222222LL, 3333333333333LL); - fmt·print("\t%'019lld %'019lld %'019lld\n", 1LL, 222222222LL, 3333333333333LL); - fmt·print("\t%'020lld %'020lld %'020lld\n", 1LL, 222222222LL, 3333333333333LL); - fmt·print("\t%'llx %'llX %'llb\n", 0x111111111111LL, 0xabcd12345678LL, 112342345LL); - - /* test precision */ - fmt·print("precision tests\n"); - fmt·print("%020.10d\n", 100); - - /* test install */ - fmt·install('X', Xfmt); - Complex c = { 1.5, -2.3 }; - fmt·print("x = %X\n", c); - - return 0; - -} diff --git a/sys/libfmt/vesprint.c b/sys/libfmt/vesprint.c deleted file mode 100644 index 18f4dd2..0000000 --- a/sys/libfmt/vesprint.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "internal.h" - -char* -fmt·vesprint(char *buf, char *end, char *fmt, va_list args) -{ - fmt·State io; - - if(end <= buf) - return nil; - - io.n = 0; - io.buffer.beg = io.buffer.cur = buf; - io.buffer.end = end-1; - io.flush = nil; - io.file = nil; - - va_copy(io.args, args); - - fmt·setlocale(&io, nil, nil, nil); - fmt·do(&io, fmt); - - va_end(io.args); - - *(io.buffer.cur) = 0; - return io.buffer.cur; -} diff --git a/sys/libfmt/vfprint.c b/sys/libfmt/vfprint.c deleted file mode 100644 index 4306ea7..0000000 --- a/sys/libfmt/vfprint.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "internal.h" - -int -fmt·vfprint(int fd, char *fmt, va_list args) -{ - int n; - fmt·State io; - char buf[256]; - - fmt·open(fd, sizeof(buf), buf, &io); - - va_copy(io.args, args); - n = fmt·do(&io, fmt); - va_end(io.args); - - if(n > 0 && io.flush(&io) < 0) - return -1; - return n; -} diff --git a/sys/libfmt/vnsprint.c b/sys/libfmt/vnsprint.c deleted file mode 100644 index 7ded908..0000000 --- a/sys/libfmt/vnsprint.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "internal.h" - -int -fmt·vnsprint(int len, char *buf, char *fmt, va_list args) -{ - fmt·State io; - - if(len <= 0) - return -1; - - io.n = 0; - io.buffer.beg = io.buffer.cur = buf; - io.buffer.end = buf+len-1; - io.flush = nil; - io.file = nil; - - va_copy(io.args, args); - - fmt·setlocale(&io, nil, nil, nil); - fmt·do(&io, fmt); - - va_end(io.args); - - *(io.buffer.cur) = 0; - return io.buffer.cur - io.buffer.beg; -} diff --git a/sys/libfmt/vprint.c b/sys/libfmt/vprint.c deleted file mode 100644 index bb3076b..0000000 --- a/sys/libfmt/vprint.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "internal.h" - -int -fmt·vprint(char *fmt, va_list args) -{ - fmt·State io; - int n; - char buf[256]; - - fmt·open(1, sizeof(buf), buf, &io); - - va_copy(io.args, args); - n = fmt·do(&io, fmt); - va_end(io.args); - - if(n > 0 && io.flush(&io) < 0) - return -1; - return n; -} diff --git a/sys/libfmt/vwrite.c b/sys/libfmt/vwrite.c deleted file mode 100644 index cacdef2..0000000 --- a/sys/libfmt/vwrite.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "internal.h" - -int -fmt·vwrite(fmt·State *io, char *fmt, va_list args) -{ - int n; - va_list tmp; - - io->flag = io->width = io->prec = 0; - - va_copy(tmp, io->args); - va_end(io->args); - - va_copy(io->args,args); - n = fmt·do(io, fmt); - va_end(io->args); - - va_copy(io->args, tmp); - va_end(tmp); - - io->flag = io->width = io->prec = 0; - - if(n >= 0) - return 0; - return n; -} diff --git a/sys/libfmt/write.c b/sys/libfmt/write.c deleted file mode 100644 index 9a77223..0000000 --- a/sys/libfmt/write.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "internal.h" - -int -fmt·write(fmt·State *io, char *fmt, ...) -{ - int n; - va_list args; - - io->flag = io->width = io->prec = 0; - - va_copy(args, io->args); - va_end(io->args); - - va_start(io->args, fmt); - n = fmt·do(io, fmt); - va_end(io->args); - - io->flag = io->width = io->prec = 0; - if(n >= 0) - return 0; - return n; -} diff --git a/sys/libmath/basic.c b/sys/libmath/basic.c deleted file mode 100644 index 1341f7b..0000000 --- a/sys/libmath/basic.c +++ /dev/null @@ -1,531 +0,0 @@ -#include -#include -#include - -#include - -// TODO(nnoll): Replace implementations with your own. - -double -math·acos(double x) -{ - return acos(x); -} - -float -math·acosf(float x) -{ - return acosf(x); -} - - -double -math·acosh(double x) -{ - return acosh(x); -} - -float -math·acoshf(float x) -{ - return acoshf(x); -} - - -double -math·asin(double x) -{ - return asin(x); -} - -float -math·asinf(float x) -{ - return asinf(x); -} - - -double -math·asinh(double x) -{ - return asinh(x); -} - -float -math·asinhf(float x) -{ - return asinhf(x); -} - - -double -math·atan(double x) -{ - return atan(x); -} - -float -math·atanf(float x) -{ - return atanf(x); -} - - -double -math·atan2(double x, double y) -{ - return atan2(x, y); -} - -float -math·atan2f(float x, float y) -{ - return atan2f(x, y); -} - -double -math·atanh(double x) -{ - return atanh(x); -} - -float -math·atanhf(float x) -{ - return atanhf(x); -} - - -double -math·cbrt(double x) -{ - return cbrt(x); -} - -float -math·cbrtf(float x) -{ - return cbrtf(x); -} - - -double -math·ceil(double x) -{ - return ceil(x); -} - -float -math·ceilf(float x) -{ - return ceilf(x); -} - -double -math·cos(double x) -{ - return cos(x); -} - -float -math·cosf(float x) -{ - return cosf(x); -} - - -double -math·cosh(double x) -{ - return cosh(x); -} - -float -math·coshf(float x) -{ - return coshf(x); -} - - -double -math·erf(double x) -{ - return erf(x); -} - -float -math·erff(float x) -{ - return erff(x); -} - - -double -math·erfc(double x) -{ - return erfc(x); -} - -float -math·erfcf(float x) -{ - return erfcf(x); -} - - -double -math·exp(double x) -{ - return exp(x); -} - -float -math·expf(float x) -{ - return expf(x); -} - - -double -math·exp2(double x) -{ - return exp2(x); -} - -float -math·exp2f(float x) -{ - return exp2f(x); -} - - -double -math·expm1(double x) -{ - return expm1(x); -} - -float -math·expm1f(float x) -{ - return expm1f(x); -} - - -double -math·floor(double x) -{ - return floor(x); -} - -float -math·floorf(float x) -{ - return floorf(x); -} - - -int -math·ilogb(double x) -{ - return ilogb(x); -} - -int -math·ilogbf(float x) -{ - return ilogbf(x); -} - -double -math·lgamma(double x) -{ - return lgamma(x); -} - -float -math·lgammaf(float x) -{ - return lgammaf(x); -} - - -vlong -math·llrint(double x) -{ - return math·llrint(x); -} - -vlong -math·llrintf(float x) -{ - return math·llrintf(x); -} - - -vlong -math·llround(double x) -{ - return llround(x); -} - -vlong -math·llroundf(float x) -{ - return llroundf(x); -} - - -double -math·log(double x) -{ - return log(x); -} - -float -math·logf(float x) -{ - return logf(x); -} - - -double -math·log10(double x) -{ - return log10(x); -} - -float -math·log10f(float x) -{ - return log10f(x); -} - - -double -math·log1p(double x) -{ - return log1p(x); -} - -float -math·log1pf(float x) -{ - return log1pf(x); -} - - -double -math·log2(double x) -{ - return log2(x); -} - -float -math·log2f(float x) -{ - return log2f(x); -} - - -double -math·logb(double x) -{ - return logb(x); -} - -float -math·logbf(float x) -{ - return logbf(x); -} - - -long -math·lrint(double x) -{ - return lrint(x); -} - -long -math·lrintf(float x) -{ - return lrintf(x); -} - - -long -math·lround(double x) -{ - return lround(x); -} - -long -math·lroundf(float x) -{ - return lroundf(x); -} - - -double math·modf(double, double *); -float math·modff(float, float *); - -double -math·nan(const char * x) -{ - return nan(x); -} - -float -math·nanf(const char * x) -{ - return nanf(x); -} - - -double -math·nearbyint(double x) -{ - return nearbyint(x); -} - -float -math·nearbyintf(float x) -{ - return nearbyintf(x); -} - - -double -math·pow(double x, double exp) -{ - return pow(x, exp); -} - -float -math·powf(float x, float exp) -{ - return powf(x, exp); -} - -double -math·rint(double x) -{ - return rint(x); -} - -float -math·rintf(float x) -{ - return rintf(x); -} - - -double -math·round(double x) -{ - return round(x); -} - -float -math·roundf(float x) -{ - return roundf(x); -} - - -double math·scalbln(double, long); -float math·scalblnf(float, long); - -double math·scalbn(double, int); -float math·scalbnf(float, int); - -double -math·sin(double x) -{ - return sin(x); -} - -float -math·sinf(float x) -{ - return sinf(x); -} - - -double -math·sinh(double x) -{ - return sinh(x); -} - -float -math·sinhf(float x) -{ - return sinhf(x); -} - - -double -math·sqrt(double x) -{ - return sqrt(x); -} - -float -math·sqrtf(float x) -{ - return sqrtf(x); -} - - -double -math·tan(double x) -{ - return tan(x); -} - -float -math·tanf(float x) -{ - return tanf(x); -} - - -double -math·tanh(double x) -{ - return tanh(x); -} - -float -math·tanhf(float x) -{ - return tanhf(x); -} - - -double -math·tgamma(double x) -{ - return tgamma(x); -} - -float -math·tgammaf(float x) -{ - return tgammaf(x); -} - - -double -math·trunc(double x) -{ - return trunc(x); -} - -float -math·truncf(float x) -{ - return truncf(x); -} diff --git a/sys/libmath/blas.c b/sys/libmath/blas.c deleted file mode 100644 index 18f9760..0000000 --- a/sys/libmath/blas.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include -#include -#include - -/* #include */ - -#define NCOL 2*512 -#define NROW 2*512 -#define NSUM 2*512 -#define NIT 10 -#define INC 1 -error -main() -{ - int i, j, nit; - double *x, *y, *z, *w, res[2]; - - clock_t t; - double tprof[2] = { 0 }; - - rng·init(0); - - x = malloc(sizeof(*x)*NROW*NCOL); - y = malloc(sizeof(*x)*NROW*NCOL); - z = malloc(sizeof(*x)*NROW*NCOL); - w = malloc(sizeof(*x)*NROW*NCOL); - -#define DO_0 t = clock(); \ - blas·dgemm(0,0,NROW,NCOL,NSUM,10.1,x,NROW,y,NROW,1.2,z,NROW);\ - t = clock() - t; \ - res[0] += blas·dasum(NROW*NCOL,z,INC); \ - tprof[0] += 1000.*t/CLOCKS_PER_SEC; \ - -#define DO_1 t = clock(); \ - cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,NROW,NCOL,NSUM,10.1,x,NROW,y,NROW,1.2,w,NROW);\ - t = clock() - t; \ - res[1] += cblas_dasum(NROW*NCOL,w,INC); \ - tprof[1] += 1000.*t/CLOCKS_PER_SEC; - - for (nit = 0; nit < NIT; nit++) { - for (i = 0; i < NROW; i++) { - for (j = 0; j < NCOL; j++) { - x[j + NROW*i] = rng·random(); - y[j + NROW*i] = rng·random(); - z[j + NROW*i] = rng·random(); - w[j + NROW*i] = z[j + NROW*i]; - } - } - - switch (nit % 2) { - case 0: DO_0; DO_1; break; - case 1: DO_1; DO_0; break; - } - } - printf("mean time/iteration (mine): %fms\n", tprof[0]/NIT); - printf("--> result (mine): %f\n", res[0]); - printf("mean time/iteration (openblas): %fms\n", tprof[1]/NIT); - printf("--> result (openblas): %f\n", res[1]); - - return 0; -} diff --git a/sys/libmath/blas1.c b/sys/libmath/blas1.c deleted file mode 100644 index a8ca085..0000000 --- a/sys/libmath/blas1.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include - -// ----------------------------------------------------------------------- -// Templates - -#include "loop.h" -#define BODY_XY() \ - LOOP(UNROLL, 0, INIT); \ - n = ROUNDBY(len, UNROLL); \ - if (incx == 1 && incy == 1) { \ - for (i = 0; i < n; i+=UNROLL) { \ - LOOP(UNROLL,0,KERNEL,1,1); \ - } \ - } else { \ - for (i = 0; i < n; i+=UNROLL) { \ - LOOP(UNROLL,0,KERNEL,incx,incy);\ - } \ - } \ - \ - for (; i < len; i++) { \ - LOOP(1,0,KERNEL,incx,incy); \ - } - -#define BODY_X() \ - LOOP(UNROLL, 0, INIT); \ - n = ROUNDBY(len, UNROLL); \ - if (incx == 1) { \ - for (i = 0; i < n; i+=UNROLL) { \ - LOOP(UNROLL,0,KERNEL,1); \ - } \ - } else { \ - for (i = 0; i < n; i+=UNROLL) { \ - LOOP(UNROLL,0,KERNEL,incx); \ - } \ - } \ - \ - for (; i < len; i++) { \ - LOOP(1,0,KERNEL,incx); \ - } - -// ----------------------------------------------------------------------- -// Implementation - -#define UNROLL 8 -#define INT int - -#define FLOAT double -#define func(name) blas·d##name -#include "blas1body" - -#undef FLOAT -#undef func - -#define FLOAT float -#define func(name) blas·f##name -#include "blas1body" -#undef FLOAT diff --git a/sys/libmath/blas1body b/sys/libmath/blas1body deleted file mode 100644 index de4b637..0000000 --- a/sys/libmath/blas1body +++ /dev/null @@ -1,215 +0,0 @@ -/* vim: set ft=c */ -// ----------------------------------------------------------------------- -// Function implementations - -FLOAT -func(dot)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) -{ -#define INIT(I,...) reg[I] = 0; -#define KERNEL(I, INCX, INCY) reg[I] += x[(INCX)*(i + I)] * y[(INCY)*(i + I)]; - INT i, n; - FLOAT reg[UNROLL]; - - BODY_XY() - - for (i = 1; i < UNROLL; i++) - reg[0] += reg[i]; - - return reg[0]; -#undef INIT -#undef KERNEL -} - -void -func(copy)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) -{ -#define INIT(I,...) -#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] = x[(INCX)*(i + I)]; - INT i, n; - - BODY_XY(); - -#undef INIT -#undef KERNEL -} - -void -func(swap)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy) -{ -#define INIT(I,...) -#define KERNEL(I, INCX, INCY) tmp[I] = x[(INCX)*(i + I)], x[(INCX)*(i + I)] = y[(INCY)*(i + I)], y[(INCY)*(i + I)] = tmp[I]; - INT i, n; - FLOAT tmp[UNROLL]; - - BODY_XY(); - -#undef INIT -#undef KERNEL -} - -void -func(axpy)(INT len, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy) -{ -#define INIT(I,...) -#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] += a*x[(INCX)*(i + I)]; - INT i, n; - - BODY_XY(); - -#undef INIT -#undef KERNEL -} - -void -func(axpby)(INT len, FLOAT a, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) -{ -#define INIT(I,...) -#define KERNEL(I, INCX, INCY) y[(INCY)*(i + I)] = a*x[(INCX)*(i + I)] + b*y[(INCY)*(i + I)]; - INT i, n; - - BODY_XY(); - -#undef INIT -#undef KERNEL -} - -INT -func(argmax)(INT len, FLOAT *x, INT incx) -{ -#define INIT(I,...) max[I] = x[I], idx[I] = I; -#define KERNEL(I, INCX) if (x[(INCX)*(i+I)] > max[I]) {max[i] = x[INCX*(i+I)]; idx[I] = i+I;} - INT i, n; - FLOAT max[UNROLL]; - INT idx[UNROLL]; - - BODY_X(); - - for (i = 1; i < UNROLL; i++) - if (max[i] > max[0]) - idx[0] = idx[i]; - - return idx[0]; -#undef INIT -#undef KERNEL -} - -INT -func(argmin)(INT len, FLOAT *x, INT incx) -{ -#define INIT(I,...) min[I] = x[I], idx[I] = I; -#define KERNEL(I, INCX) if (x[INCX*(i+I)] < min[I]) {min[i] = x[INCX*(i+I)]; idx[I] = i+I;} - INT i, n; - FLOAT min[UNROLL]; - INT idx[UNROLL]; - - BODY_X(); - - for (i = 1; i < UNROLL; i++) - if (min[i] < min[0]) - idx[0] = idx[i]; - - return idx[0]; -#undef INIT -#undef KERNEL -} - -FLOAT -func(asum)(INT len, FLOAT *x, INT incx) -{ -#define INIT(I,...) sum[I] = 0; -#define KERNEL(I, INCX) sum[I] += x[INCX*(i+I)] > 0 ? x[INCX*(i+I)] : -x[INCX*(i+I)]; - INT i, n; - FLOAT sum[UNROLL]; - - BODY_X(); - - for (i = 1; i < UNROLL; i++) - sum[0] += sum[i]; - - return sum[0]; - -#undef INIT -#undef KERNEL -} - -void -func(scale)(INT len, FLOAT a, FLOAT *x, INT incx) -{ -#define INIT(I, ...) -#define KERNEL(I, INCX) x[INCX*(i+I)] *= a; - INT i, n; - - BODY_X(); - -#undef INIT -#undef KERNEL -} - -FLOAT -func(norm)(INT len, FLOAT *x, INT incx) -{ -#define INIT(I, ...) -#define KERNEL(I, INCX) norm[I] += x[INCX*(i+I)] * x[INCX*(i+I)]; - INT i, n; - FLOAT norm[UNROLL]; - - BODY_X(); - - for (i = 1; i < UNROLL; i++) - norm[0] += norm[i]; - - return math·sqrt(norm[0]); - -#undef INIT -#undef KERNEL -} - -void -func(drot)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT cos, FLOAT sin) -{ -#define INIT(I, ...) -#define KERNEL(I, INCX, INCY) tmp[I] = x[INCX*(i+I)], x[INCX*(i+I)] = cos*x[INCX*(i+I)] + sin*y[INCY*(i+I)], y[INCY*(i+I)] = cos*y[INCY*(i+I)] - sin*tmp[I]; - INT i, n; - FLOAT tmp[UNROLL]; - - BODY_XY(); - -#undef INIT -#undef KERNEL -} - -void -func(rotm)(INT len, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT H[5]) -{ -#define INIT(I, ...) -#define KERNEL(I, INCX, INCY) tmp[I] = x[INCX*(i+I)], x[INCX*(i+I)] = H[1]*x[INCX*(i+I)] + H[2]*y[INCY*(i+I)], y[INCY*(i+I)] = H[3]*tmp[I] + H[4]*y[INCY*(i+I)]; - INT i, n, f; - FLOAT tmp[UNROLL]; - - f = (INT)H[0]; - switch (f) { - case -2: - H[1] = +1; - H[2] = +0; - H[3] = +0; - H[4] = +1; - break; - case -1: - break; - case +0: - H[1] = +1; - H[4] = +1; - break; - case +1: - H[2] = +1; - H[3] = -1; - break; - default: - return; - } - - BODY_XY(); - -#undef INIT -#undef KERNEL -} diff --git a/sys/libmath/blas2.c b/sys/libmath/blas2.c deleted file mode 100644 index 7e4b08e..0000000 --- a/sys/libmath/blas2.c +++ /dev/null @@ -1,222 +0,0 @@ -#include -#include -#include "loop.h" - -// ----------------------------------------------------------------------- -// Templates - -#define BODY_RECT() \ - nr = ROUNDBY(nrow, UNROW); \ - nc = ROUNDBY(ncol, UNCOL); \ - if (incx == 1 && incy == 1) { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,1,1); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,1,1,UNCOL); \ - } \ - for (; c < ncol; c++) { \ - LOOP(UNROW,0,KERN,1,1,1); \ - } \ - LOOP(UNROW,0,FINI,1,1); \ - } \ - } else { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,incx,incy); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c < ncol; c++) { \ - LOOP(UNROW,0,KERN,incx,incy,1); \ - } \ - LOOP(UNROW,0,FINI,incx,incy); \ - } \ - } \ - \ - for (; r < nrow; r++) { \ - LOOP(1,0,INIT,incx,incy); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(1,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c < ncol; c++) { \ - LOOP(1,0,KERN,incx,incy,1); \ - } \ - LOOP(1,0,FINI,incx,incy); \ - } - -#define BODY_LOTRI() \ - nr = ROUNDBY(n, UNROW); \ - if (incx == 1) { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,1); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,1,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(UNROW,0,KERN,1,1); \ - } \ - LOOP(UNROW,0,FINI,1); \ - } \ - } else { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,incx); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,incx,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(UNROW,0,KERN,incx,1); \ - } \ - LOOP(UNROW,0,FINI,incx); \ - } \ - } \ - \ - for (; r < n; r++) { \ - LOOP(1,0,INIT,incx); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(1,0,KERN,incx,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(1,0,KERN,incx,1); \ - } \ - LOOP(1,0,FINI,incx); \ - } - -#define BODY_UPTRI() \ - nr = n - ROUNDBY(n, UNROW); \ - if (incx == 1) { \ - for (r = n-1; r >= nr; r -= UNROW) { \ - LOOP(UNROW,0,INIT,1); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(UNROW,0,KERN,1,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(UNROW,0,KERN,1,1); \ - } \ - LOOP(UNROW,0,FINI,1); \ - } \ - } else { \ - for (r = n-1; r >= nr; r -= UNROW) { \ - LOOP(UNROW,0,INIT,incx); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(UNROW,0,KERN,incx,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(UNROW,0,KERN,incx,1); \ - } \ - LOOP(UNROW,0,FINI,incx); \ - } \ - } \ - \ - for (; r >= 0; r--) { \ - LOOP(1,0,INIT,incx); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(1,0,KERN,incx,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(1,0,KERN,incx,1); \ - } \ - LOOP(1,0,FINI,incx); \ - } - -#define BODY_LOTRI_XY() \ - nr = ROUNDBY(n, UNROW); \ - if (incx == 1 && incy == 1) { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,1,1); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,1,1,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(UNROW,0,KERN,1,1,1); \ - } \ - LOOP(UNROW,0,FINI,1,1); \ - } \ - } else { \ - for (r = 0; r < nr; r += UNROW) { \ - LOOP(UNROW,0,INIT,incx,incy); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(UNROW,0,KERN,incx,incy,1); \ - } \ - LOOP(UNROW,0,FINI,incx, incy); \ - } \ - } \ - \ - for (; r < n; r++) { \ - LOOP(1,0,INIT,incx,incy); \ - nc = ROUNDBY(r, UNCOL); \ - for (c = 0; c < nc; c += UNCOL) { \ - LOOP(1,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c < r; c++) { \ - LOOP(1,0,KERN,incx,incy,1); \ - } \ - LOOP(1,0,FINI,incx,incy); \ - } - -#define BODY_UPTRI_XY() \ - nr = n - ROUNDBY(n, UNROW); \ - if (incx == 1 && incy == 1) { \ - for (r = n-1; r >= nr; r -= UNROW) { \ - LOOP(UNROW,0,INIT,1,1); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(UNROW,0,KERN,1,1,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(UNROW,0,KERN,1,1,1); \ - } \ - LOOP(UNROW,0,FINI,1,1); \ - } \ - } else { \ - for (r = n-1; r >= nr; r -= UNROW) { \ - LOOP(UNROW,0,INIT,incx,incy); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(UNROW,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(UNROW,0,KERN,incx,incy,1); \ - } \ - LOOP(UNROW,0,FINI,incx,incy); \ - } \ - } \ - \ - for (; r >= 0; r--) { \ - LOOP(1,0,INIT,incx,incy); \ - nc = n - ROUNDBY(r, UNCOL); \ - for (c = n-1; c >= nc; c -= UNCOL) { \ - LOOP(1,0,KERN,incx,incy,UNCOL); \ - } \ - for (; c > r; c--) { \ - LOOP(1,0,KERN,incx,incy,1); \ - } \ - LOOP(1,0,FINI,incx,incy); \ - } - -// ----------------------------------------------------------------------- -// implementation - -#define UNROW 4 -#define UNCOL 4 - -#define INT int -#define FLOAT double -#define func(name) blas·d##name -#include "blas2body" - -#undef FLOAT -#undef func - -#define FLOAT float -#define func(name) blas·f##name -#include "blas2body" diff --git a/sys/libmath/blas2body b/sys/libmath/blas2body deleted file mode 100644 index 45baf67..0000000 --- a/sys/libmath/blas2body +++ /dev/null @@ -1,256 +0,0 @@ -/* general matrix multiply */ -error -func(gemv)(uint flag, INT nrow, INT ncol, FLOAT a, FLOAT *m, INT incm, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) -{ - INT r, c, nr, nc; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = 0; -#define TERM(J, I, INCX, INCY) row[I][c+J] * x[INCX*(c+J)] -#define KERN(I, INCX, INCY, LENGTH) reg[I] += EXPAND(LENGTH,0,TERM,+,I,INCX,INCY); -#define FINI(I, INCX, INCY) y[INCY*(r+I)] = b*y[INCY*(r+I)] + a*reg[I]; - - if (!flag) { - BODY_RECT(); - } else { - func(scale)(ncol, b, y, incy); -#undef KERN -#undef FINI -#undef INIT -#undef TERM -#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = a*x[INCX*(r+I)]; -#define TERM(J, I, INCX, INCY) row[I][c+J] * reg[I] -#define KERN(I, INCX, INCY, LENGTH) y[INCY*(c+I)] += EXPAND(LENGTH,0,TERM,+,I,INCX,INCY); -#define FINI(I, INCX, INCY) - BODY_RECT(); - } - - return 0; -#undef INIT -#undef TERM -#undef KERN -#undef FINI -} - -/* symmetric matrix vector multiply (different layouts) */ -void -func(symv)(uint upper, INT n, FLOAT a, FLOAT *m, INT incm, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg1[UNROW], reg2[UNROW]; - -#define INIT(I, INCX, INCY) row[I] = m + (r XX I)*incm, reg1[I] = 0, reg2[I] = 0; -#define TERM1(J, I, INCX, INCY) row[I][c XX J]*x[INCX*(c XX J)] -#define TERM2(J, I, INCX, INCY) row[I][c XX J]*x[INCX*((n-c-1) XX J)] -#define KERN(I, INCX, INCY, REPEAT) reg1[I] += EXPAND(REPEAT,0,TERM1,+,I,INCX,INCY), \ - reg2[I] += EXPAND(REPEAT,0,TERM2,+,I,INCX,INCY); -#define FINI(I, INCX, INCY) y[INCY*(r+I)] += a*(reg1[I] + row[I][r]*x[INCX*r]), \ - y[INCY*(n-r-1+I)] += a*reg2[I]; - - func(scale)(n, b, y, incy); -#define XX + - if (!upper) { - BODY_LOTRI_XY(); - } else { -#undef XX -#define XX - - BODY_UPTRI_XY(); - } -#undef XX - -#undef INIT -} - -void -func(spmv)(uint upper, INT n, FLOAT a, FLOAT *m, FLOAT *x, INT incx, FLOAT b, FLOAT *y, INT incy) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg1[UNROW], reg2[UNROW]; - -#define INIT(I, INCX, INCY) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg1[I] = 0, reg2[I] = 0; - - func(scale)(n, b, y, incy); -#define XX + - if (!upper) { - BODY_LOTRI_XY(); - } else { -#undef XX -#undef INIT -#define INIT(I, INCX, INCY) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg1[I] = 0, reg2[I] = 0; -#define XX - - BODY_UPTRI_XY(); - } -#undef XX - -#undef TERM -#undef INIT -#undef KERN -#undef FINI -} - -/* triangular multiply (different layouts) */ -void -func(trmv)(uint upper, INT n, FLOAT *m, INT incm, FLOAT *x, INT incx) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = 0; -#define TERM(J, I, INCX) row[I][c XX J]*x[INCX*(c XX J)] -#define KERN(I, INCX, REPEAT) reg[I] += EXPAND(REPEAT,0,TERM,+,I,INCX); -#define FINI(I, INCX) x[INCX*(r XX I)] = row[I][r XX I]*x[INCX*(r XX I)] + reg[I]; - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -} - -void -func(tpmv)(uint upper, INT n, FLOAT *m, FLOAT *x, INT incx) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#undef INIT -#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -#undef TERM -#undef KERN -#undef FINI -} - -/* triangular solve (different layouts) */ -void -func(trsv)(uint upper, INT n, FLOAT *m, INT incm, FLOAT *x, INT incx) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = 0; -#define TERM(J, I, INCX) row[I][c XX J]*x[c XX J] -#define KERN(I, INCX, REPEAT) reg[I] += EXPAND(REPEAT,0,TERM,+,I,INCX); -#define SOLN(J, I, INCX) reg[J] += row[I][r XX I]*x[INCX*(r XX I)] -#define FINI(I, INCX) x[INCX*(r XX I)] = reg[I] / row[I][r XX I]; EXPAND_TRI(UNROW,INC(I),SOLN,;,I,INCX); - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -} - -void -func(tpsv)(uint upper, INT n, FLOAT *m, FLOAT *x, INT incx) -{ - INT r, c, nr, nc, i; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#undef INIT -#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -#undef TERM -#undef KERN -#undef SOLN -#undef FINI -} - -/* rank 1 update */ -void -func(ger)(INT nrow, INT ncol, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT *m, INT incm) -{ - INT r, c, nr, nc; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX, INCY) row[I] = m + (r+I)*incm, reg[I] = a*x[INCX*(r+I)]; -#define TERM(J, I, INCX, INCY) row[I][c+J] += reg[I] * y[INCY*(c+J)] -#define KERN(I, INCX, INCY, LENGTH) EXPAND(LENGTH,0,TERM,;,I,INCX, INCY); -#define FINI(I, ...) - - BODY_RECT(); - -#undef INIT -#undef TERM -#undef KERN -#undef FINI -} - -/* symmetric rank 1 update (different memory layouts) */ -void -func(syr)(uint upper, INT n, FLOAT a, FLOAT *x, INT incx, FLOAT *m, INT incm) -{ - INT r, c, nr, nc; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + (r XX I)*incm, reg[I] = a*x[INCX*(r XX I)]; -#define TERM(J, I, INCX) row[I][c XX J] += reg[I] * x[INCX*(c XX J)] -#define KERN(I, INCX, LENGTH) EXPAND(LENGTH,0,TERM,;,I,INCX); -#define FINI(I, ...) - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -} - -void -func(spr)(uint upper, INT n, FLOAT a, FLOAT *x, INT incx, FLOAT *m) -{ - INT r, c, nr, nc; - FLOAT *row[UNROW], reg[UNROW]; -#define INIT(I, INCX) row[I] = m + ((r+I)*(r+I+1))*(1/2), reg[I] = 0; - -#define XX + - if (!upper) { - BODY_LOTRI(); - } else { -#undef XX -#undef INIT -#define INIT(I, INCX) row[I] = m + ((2*n-r-I)*(r+I+1))*(1/2), reg[I] = 0; -#define XX - - BODY_UPTRI(); - } -#undef XX - -#undef INIT -#undef TERM -#undef KERN -#undef FINI -} diff --git a/sys/libmath/blas3.c b/sys/libmath/blas3.c deleted file mode 100644 index b048c95..0000000 --- a/sys/libmath/blas3.c +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include -#include - -#define INT int -#define FLOAT double -#define func(name) blas·d##name - -#define X(i, j) x[j + incx*(i)] -#define Y(i, j) y[j + incy*(i)] -#define Z(i, j) z[j + incz*(i)] - -void -func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) -{ - INT jj, jb, kk, kb, dk, i, j, k, end; - FLOAT r0[8], r1[8], r2[8], r3[8], pf; - - for (i = 0; i < ni; i++) { - for (j = 0; j < nj; j++) { - Z(i,j) *= b; - } - } - - jb = MIN(256, nj); - kb = MIN(48, nk); - for (jj = 0; jj < nj; jj += jb) { - for (kk = 0; kk < nk; kk += kb) { - for (i = 0; i < ni; i += 4) { - for (j = jj; j < jj + jb; j += 8) { - r0[0] = Z(i+0, j+0); r0[1] = Z(i+0, j+1); r0[2] = Z(i+0, j+2); r0[3] = Z(i+0, j+3); - r1[0] = Z(i+1, j+0); r1[1] = Z(i+1, j+1); r1[2] = Z(i+1, j+2); r1[3] = Z(i+1, j+3); - r2[0] = Z(i+2, j+0); r2[1] = Z(i+2, j+1); r2[2] = Z(i+2, j+2); r2[3] = Z(i+2, j+3); - r3[0] = Z(i+3, j+0); r3[1] = Z(i+3, j+1); r3[2] = Z(i+3, j+2); r3[3] = Z(i+3, j+3); - end = MIN(nk, kk+kb); - for (k = kk; k < end; k++) { - pf = a * X(i, k); - r0[0] += pf * Y(k, j+0); r0[1] += pf * Y(k, j+1); r0[2] += pf * Y(k, j+2); r0[3] += pf * Y(k, j+3); - - pf = a * X(i+1, k); - r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); - - pf = a * X(i+2, k); - r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); - - pf = a * X(i+3, k); - r1[0] += pf * Y(k, j+0); r1[1] += pf * Y(k, j+1); r1[2] += pf * Y(k, j+2); r1[3] += pf * Y(k, j+3); - } - Z(i+0, j+0) = r0[0]; Z(i+0, j+1) = r0[1]; Z(i+0, j+2) = r0[2]; Z(i+0, j+3) = r0[3]; - Z(i+1, j+0) = r1[0]; Z(i+1, j+1) = r1[1]; Z(i+1, j+2) = r1[2]; Z(i+1, j+3) = r1[3]; - Z(i+2, j+0) = r2[0]; Z(i+2, j+1) = r2[1]; Z(i+2, j+2) = r2[2]; Z(i+2, j+3) = r2[3]; - Z(i+3, j+0) = r3[0]; Z(i+3, j+1) = r3[1]; Z(i+3, j+2) = r3[2]; Z(i+3, j+3) = r3[3]; - } - } - } - } -} - -#if 0 -void -func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) -{ - int i, j, k; - FLOAT w[nj*nk], acc[4][4]; - - for (i = 0; i < ni; i++) { - for (j = 0; j < nj; j++) { - Z(i,j) *= b; - W(i,j) = Y(j,i); - } - } - - for (i = 0; i < ni; i+=4) { - for (j = 0; j < nj; j+=4) { - memset(acc, 0, sizeof(acc)); - for (k = 0; k < nk; k+=4) { - acc[0][0] += X(i+0,k)*W(j+0,k) + X(i+0,k+1)*W(j+0,k+1) + X(i+0,k+2)*W(j+0,k+2) + X(i+0,k+3)*W(j+0,k+3); - acc[0][1] += X(i+0,k)*W(j+1,k) + X(i+0,k+1)*W(j+1,k+1) + X(i+0,k+2)*W(j+1,k+2) + X(i+0,k+3)*W(j+1,k+3); - acc[0][2] += X(i+0,k)*W(j+2,k) + X(i+0,k+1)*W(j+2,k+1) + X(i+0,k+2)*W(j+2,k+2) + X(i+0,k+3)*W(j+2,k+3); - acc[0][3] += X(i+0,k)*W(j+3,k) + X(i+0,k+1)*W(j+3,k+1) + X(i+0,k+2)*W(j+3,k+2) + X(i+0,k+3)*W(j+3,k+3); - - acc[1][0] += X(i+1,k)*W(j+0,k) + X(i+1,k+1)*W(j+0,k+1) + X(i+1,k+2)*W(j+0,k+2) + X(i+1,k+3)*W(j+0,k+3); - acc[1][1] += X(i+1,k)*W(j+1,k) + X(i+1,k+1)*W(j+1,k+1) + X(i+1,k+2)*W(j+1,k+2) + X(i+1,k+3)*W(j+1,k+3); - acc[1][2] += X(i+1,k)*W(j+2,k) + X(i+1,k+1)*W(j+2,k+1) + X(i+1,k+2)*W(j+2,k+2) + X(i+1,k+3)*W(j+2,k+3); - acc[1][3] += X(i+1,k)*W(j+3,k) + X(i+1,k+1)*W(j+3,k+1) + X(i+1,k+2)*W(j+3,k+2) + X(i+1,k+3)*W(j+3,k+3); - - acc[2][0] += X(i+2,k)*W(j+0,k) + X(i+2,k+1)*W(j+0,k+1) + X(i+2,k+2)*W(j+0,k+2) + X(i+2,k+3)*W(j+0,k+3); - acc[2][1] += X(i+2,k)*W(j+1,k) + X(i+2,k+1)*W(j+1,k+1) + X(i+2,k+2)*W(j+1,k+2) + X(i+2,k+3)*W(j+1,k+3); - acc[2][2] += X(i+2,k)*W(j+2,k) + X(i+2,k+1)*W(j+2,k+1) + X(i+2,k+2)*W(j+2,k+2) + X(i+2,k+3)*W(j+2,k+3); - acc[2][3] += X(i+2,k)*W(j+3,k) + X(i+2,k+1)*W(j+3,k+1) + X(i+2,k+2)*W(j+3,k+2) + X(i+2,k+3)*W(j+3,k+3); - - acc[2][0] += X(i+3,k)*W(j+0,k) + X(i+3,k+1)*W(j+0,k+1) + X(i+3,k+2)*W(j+0,k+2) + X(i+3,k+3)*W(j+0,k+3); - acc[2][1] += X(i+3,k)*W(j+1,k) + X(i+3,k+1)*W(j+1,k+1) + X(i+3,k+2)*W(j+1,k+2) + X(i+3,k+3)*W(j+1,k+3); - acc[2][2] += X(i+3,k)*W(j+2,k) + X(i+3,k+1)*W(j+2,k+1) + X(i+3,k+2)*W(j+2,k+2) + X(i+3,k+3)*W(j+2,k+3); - acc[2][3] += X(i+3,k)*W(j+3,k) + X(i+3,k+1)*W(j+3,k+1) + X(i+3,k+2)*W(j+3,k+2) + X(i+3,k+3)*W(j+3,k+3); - // Z(i,j) += X(i,k)*Y(k,j); - } - Z(i+0,j+1) = a*acc[0][0]; - Z(i+0,j+2) = a*acc[0][1]; - Z(i+0,j+3) = a*acc[0][2]; - Z(i+0,j+4) = a*acc[0][3]; - - Z(i+1,j+1) = a*acc[1][0]; - Z(i+1,j+2) = a*acc[1][1]; - Z(i+1,j+3) = a*acc[1][2]; - Z(i+1,j+4) = a*acc[1][3]; - - Z(i+2,j+1) = a*acc[2][0]; - Z(i+2,j+2) = a*acc[2][1]; - Z(i+2,j+3) = a*acc[2][2]; - Z(i+2,j+4) = a*acc[2][3]; - - Z(i+3,j+1) = a*acc[3][0]; - Z(i+3,j+2) = a*acc[3][1]; - Z(i+3,j+3) = a*acc[3][2]; - Z(i+3,j+4) = a*acc[3][3]; - } - } -} -#endif - -#if 0 -void -func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) -{ - int i, j, k, ri, rj, rk; - FLOAT reg[4][4], *xrow[4], *yrow[4]; - - for (i = 0; i < ni; i++) { - for (j = 0; j < nj; j++) { - z[j + incz*i] *= b; - } - } - - for (i = 0; i < ni; i += 4) { - xrow[0] = x + incx*(i+0); - xrow[1] = x + incx*(i+1); - xrow[2] = x + incx*(i+2); - xrow[3] = x + incx*(i+3); - for (k = 0; k < nk; k+=4) { - yrow[0] = y + incy*(k+0); - yrow[1] = y + incy*(k+1); - yrow[2] = y + incy*(k+2); - yrow[3] = y + incy*(k+3); - reg[0][0] = a * xrow[0][k+0]; reg[0][1] = a * xrow[0][k+1]; reg[0][2] = a * xrow[0][k+2]; reg[0][3] = a * xrow[0][k+3]; - reg[1][0] = a * xrow[1][k+0]; reg[1][1] = a * xrow[1][k+1]; reg[1][2] = a * xrow[1][k+2]; reg[1][3] = a * xrow[1][k+3]; - reg[2][0] = a * xrow[2][k+0]; reg[2][1] = a * xrow[2][k+1]; reg[2][2] = a * xrow[2][k+2]; reg[2][3] = a * xrow[2][k+3]; - reg[3][0] = a * xrow[3][k+0]; reg[3][1] = a * xrow[3][k+1]; reg[3][2] = a * xrow[3][k+2]; reg[3][3] = a * xrow[3][k+3]; - for (j = 0; j < nj; j += 1) { - z[j + incz*(i+0)] += (reg[0][0]*yrow[0][j]+reg[0][1]*yrow[1][j]+reg[0][2]*yrow[2][j]+reg[0][3]*yrow[3][j]); - z[j + incz*(i+1)] += (reg[1][0]*yrow[0][j]+reg[1][1]*yrow[1][j]+reg[1][2]*yrow[2][j]+reg[1][3]*yrow[3][j]); - z[j + incz*(i+2)] += (reg[2][0]*yrow[0][j]+reg[2][1]*yrow[1][j]+reg[2][2]*yrow[2][j]+reg[2][3]*yrow[3][j]); - z[j + incz*(i+3)] += (reg[3][0]*yrow[0][j]+reg[3][1]*yrow[1][j]+reg[3][2]*yrow[2][j]+reg[3][3]*yrow[3][j]); - } - } - } -} -#endif - -#if 0 -void -func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) -{ - int i, j, k, ri, rj, rk; - FLOAT r[4][4], *row[4]; - - for (i = 0; i < ni; i++) { - for (j = 0; j < nj; j++) { - Z(i, j) *= b; - } - } - - for (i = 0; i < ni; i+=4) { - for (j = 0; j < nj; j+=4) { - r[0][0] = 0; r[0][1] = 0; r[0][2] = 0; r[0][3] = 0; - r[1][0] = 0; r[1][1] = 0; r[1][2] = 0; r[1][3] = 0; - r[2][0] = 0; r[2][1] = 0; r[2][2] = 0; r[2][3] = 0; - r[3][0] = 0; r[3][1] = 0; r[3][2] = 0; r[3][3] = 0; - row[0] = &X(i+0, 0); - row[1] = &X(i+1, 0); - row[2] = &X(i+2, 0); - row[3] = &X(i+3, 0); - for (k = 0; k < nk; k++) { - r[0][0] += row[0][k]*Y(k,0); r[0][1] += row[0][k]*Y(k,1); r[0][2] += row[0][k]*Y(k,2); r[0][3] += row[0][k]*Y(k,3); - r[1][0] += row[1][k]*Y(k,0); r[1][1] += row[1][k]*Y(k,1); r[1][2] += row[1][k]*Y(k,2); r[1][3] += row[1][k]*Y(k,3); - r[2][0] += row[2][k]*Y(k,0); r[2][1] += row[2][k]*Y(k,1); r[2][2] += row[2][k]*Y(k,2); r[2][3] += row[2][k]*Y(k,3); - r[3][0] += row[3][k]*Y(k,0); r[3][1] += row[3][k]*Y(k,1); r[3][2] += row[3][k]*Y(k,2); r[3][3] += row[3][k]*Y(k,3); - } - Z(i+0, j+0) += r[0][0]; Z(i+0, j+1) += r[0][1]; Z(i+0, j+2) += r[0][2]; Z(i+0, j+3) += r[0][3]; - Z(i+1, j+0) += r[1][0]; Z(i+1, j+1) += r[1][1]; Z(i+1, j+2) += r[1][2]; Z(i+1, j+3) += r[1][3]; - Z(i+2, j+0) += r[2][0]; Z(i+2, j+1) += r[2][1]; Z(i+2, j+2) += r[2][2]; Z(i+2, j+3) += r[2][3]; - Z(i+3, j+0) += r[3][0]; Z(i+3, j+1) += r[3][1]; Z(i+3, j+2) += r[3][2]; Z(i+3, j+3) += r[3][3]; - } - } -} -#endif - -#if 0 -void -func(gemm)(uint trm, uint trn, INT ni, INT nj, INT nk, FLOAT a, FLOAT *x, INT incx, FLOAT *y, INT incy, FLOAT b, FLOAT *z, INT incz) -{ - int i, j, k, ri, rj, rk; - FLOAT *xrow[8], *yrow[8], reg; - - for (i = 0; i < ni; i++) { - for (j = 0; j < nj; j++) { - z[j + incz*i] *= b; - } - } - - ri = ni & ~7; - rj = nj & ~7; - for (i = 0; i < ri; i += 8) { - xrow[0] = x + incx*(i+0); - xrow[1] = x + incx*(i+1); - xrow[2] = x + incx*(i+2); - xrow[3] = x + incx*(i+3); - xrow[4] = x + incx*(i+4); - xrow[5] = x + incx*(i+5); - xrow[6] = x + incx*(i+6); - xrow[7] = x + incx*(i+7); - for (j = 0; j < rj; j += 8) { - yrow[0] = y + incy*(j+0); - yrow[1] = y + incy*(j+1); - yrow[2] = y + incy*(j+2); - yrow[3] = y + incy*(j+3); - yrow[4] = y + incy*(j+4); - yrow[5] = y + incy*(j+5); - yrow[6] = y + incy*(j+6); - yrow[7] = y + incy*(j+7); - for (k = 0; k < nk; k++) { - reg = a*(yrow[0][k] + yrow[1][k] + yrow[2][k] + yrow[3][k] + yrow[4][k] + yrow[5][k] + yrow[6][k] + yrow[7][k]); - z[k + incz*(i+0)] += xrow[0][k]*reg; - z[k + incz*(i+1)] += xrow[1][k]*reg; - z[k + incz*(i+2)] += xrow[2][k]*reg; - z[k + incz*(i+3)] += xrow[3][k]*reg; - z[k + incz*(i+4)] += xrow[4][k]*reg; - z[k + incz*(i+5)] += xrow[5][k]*reg; - z[k + incz*(i+6)] += xrow[6][k]*reg; - z[k + incz*(i+7)] += xrow[7][k]*reg; - } - } - for (; j < nj; j++) { - for (k = 0; k < nk; k++) { - reg = a*y[k+incy*j]; - z[k + incz*(i+0)] += xrow[0][k]*reg; - z[k + incz*(i+1)] += xrow[1][k]*reg; - z[k + incz*(i+2)] += xrow[2][k]*reg; - z[k + incz*(i+3)] += xrow[3][k]*reg; - z[k + incz*(i+4)] += xrow[4][k]*reg; - z[k + incz*(i+5)] += xrow[5][k]*reg; - z[k + incz*(i+6)] += xrow[6][k]*reg; - z[k + incz*(i+7)] += xrow[7][k]*reg; - } - } - } - - for (; i < ni; i++) { - for (j = 0; j < rj; j += 8) { - yrow[0] = y + incy*(j+0); - yrow[1] = y + incy*(j+1); - yrow[2] = y + incy*(j+2); - yrow[3] = y + incy*(j+3); - yrow[4] = y + incy*(j+4); - yrow[5] = y + incy*(j+5); - yrow[6] = y + incy*(j+6); - yrow[7] = y + incy*(j+7); - for (k = 0; k < nk; k++) { - z[k + incz*(i)] += a*x[k + incx*i]*(yrow[0][k] + yrow[1][k] + yrow[2][k] + yrow[3][k] + yrow[4][k] + yrow[5][k] + yrow[6][k] + yrow[7][k]); - } - } - for (; j < nj; j++) { - for (k = 0; k < nk; k++) { - z[k + incz*i] += a*x[k + incx*i]*y[k + incy*j]; - } - } - } -} -#endif diff --git a/sys/libmath/lapack.c b/sys/libmath/lapack.c deleted file mode 100644 index e69de29..0000000 diff --git a/sys/libmath/linalg.c b/sys/libmath/linalg.c deleted file mode 100644 index 8551ff1..0000000 --- a/sys/libmath/linalg.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include -#include - -// ----------------------------------------------------------------------- -// Vector - -void -linalg·normalize(math·Vector vec) -{ - double norm; - - norm = blas·normd(vec.len, vec.data, 1); - blas·scaled(vec.len, 1/norm, vec.data, 1); -} -// TODO: Write blas wrappers that eat vectors for convenience - -// ----------------------------------------------------------------------- -// Matrix -// -// NOTE: all matrices are row major oriented - -/* - * linalg·lq - * computes the LQ decomposition of matrix M: M = LQ - * L is lower triangular - * Q is orthogonal -> transp(Q) * Q = I - * - * m: matrix to factorize. changes in place - * + lower triangle -> L - * + upper triangle -> all reflection vectors stored in rows - * w: working buffer: len = ncols! - */ -error -linalg·lq(math·Matrix m, math·Vector w) -{ - int i, j, len; - double *row, mag; - enum { - err·nil, - err·baddims, - }; - - if (m.dim[0] > m.dim[1]) { - return err·baddims; - } - - for (i = 0; i < m.dim[0]; i++, m.data += m.dim[1]) { - row = m.data + i; - len = m.dim[0] - i; - - // TODO: Don't want to compute norm twice!! - w.data[0] = math·sgn(row[0]) * blas·normd(len, row, 1); - blas·axpyd(len, 1.0, row, 1, w.data, 1); - mag = blas·normd(len, w.data, 1); - blas·scaled(len, 1/mag, w.data, 1); - - blas·copyd(len - m.dim[0], w.data, 1, m.data + i, 1); - } - - return err·nil; -} diff --git a/sys/libmath/loop.h b/sys/libmath/loop.h deleted file mode 100644 index a877d84..0000000 --- a/sys/libmath/loop.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -/* increment operator */ -#define INC2(x) INC_##x -#define INC1(x) INC2(x) -#define INC(x) INC1(x) - -#define INC_0 1 -#define INC_1 2 -#define INC_2 3 -#define INC_3 4 -#define INC_4 5 -#define INC_5 6 -#define INC_6 7 -#define INC_7 8 -#define INC_8 9 -#define INC_9 10 -#define INC_10 11 -#define INC_11 12 -#define INC_12 13 -#define INC_13 14 -#define INC_14 15 -#define INC_15 16 - -#define ROUNDBY(x, n) ((x) & ~((n)-1)) - -/* subtraction tables */ -#define SUB2(x, y) SUB_##x##_##y -#define SUB1(x, y) SUB2(x, y) -#define SUB(x, y) SUB1(x, y) -#define SUB_8_0 8 -#define SUB_8_1 7 -#define SUB_8_2 6 -#define SUB_8_3 5 -#define SUB_8_4 4 -#define SUB_8_5 3 -#define SUB_8_6 2 -#define SUB_8_7 1 -#define SUB_8_8 0 -#define SUB_7_0 7 -#define SUB_7_1 6 -#define SUB_7_2 5 -#define SUB_7_3 4 -#define SUB_7_4 3 -#define SUB_7_5 2 -#define SUB_7_6 1 -#define SUB_7_7 0 -#define SUB_6_0 6 -#define SUB_6_1 5 -#define SUB_6_2 4 -#define SUB_6_3 3 -#define SUB_6_4 2 -#define SUB_6_5 1 -#define SUB_6_6 0 -#define SUB_5_0 5 -#define SUB_5_1 4 -#define SUB_5_2 3 -#define SUB_5_3 2 -#define SUB_5_4 1 -#define SUB_5_5 0 -#define SUB_4_0 4 -#define SUB_4_1 3 -#define SUB_4_2 2 -#define SUB_4_3 1 -#define SUB_4_4 0 -#define SUB_3_0 3 -#define SUB_3_1 2 -#define SUB_3_2 1 -#define SUB_3_3 0 -#define SUB_2_0 2 -#define SUB_2_1 1 -#define SUB_2_2 0 -#define SUB_1_0 1 -#define SUB_1_1 0 - -/* rounding operator */ -#define ROUNDBY(x, n) ((x) & ~((n)-1)) - -/* loop unrolling (vertical) */ -#define LOOP1(I,STMT,...) STMT(I,__VA_ARGS__) -#define LOOP2(I,STMT,...) STMT(I,__VA_ARGS__) LOOP1(INC(I),STMT,__VA_ARGS__) -#define LOOP3(I,STMT,...) STMT(I,__VA_ARGS__) LOOP2(INC(I),STMT,__VA_ARGS__) -#define LOOP4(I,STMT,...) STMT(I,__VA_ARGS__) LOOP3(INC(I),STMT,__VA_ARGS__) -#define LOOP5(I,STMT,...) STMT(I,__VA_ARGS__) LOOP4(INC(I),STMT,__VA_ARGS__) -#define LOOP6(I,STMT,...) STMT(I,__VA_ARGS__) LOOP5(INC(I),STMT,__VA_ARGS__) -#define LOOP7(I,STMT,...) STMT(I,__VA_ARGS__) LOOP6(INC(I),STMT,__VA_ARGS__) -#define LOOP8(I,STMT,...) STMT(I,__VA_ARGS__) LOOP7(INC(I),STMT,__VA_ARGS__) -#define LOOP9(I,STMT,...) STMT(I,__VA_ARGS__) LOOP8(INC(I),STMT,__VA_ARGS__) -#define LOOP10(I,STMT,...) STMT(I,__VA_ARGS__) LOOP9(INC(I),STMT,__VA_ARGS__) -#define LOOP11(I,STMT,...) STMT(I,__VA_ARGS__) LOOP10(INC(I),STMT,__VA_ARGS__) -#define LOOP12(I,STMT,...) STMT(I,__VA_ARGS__) LOOP11(INC(I),STMT,__VA_ARGS__) -#define LOOP13(I,STMT,...) STMT(I,__VA_ARGS__) LOOP12(INC(I),STMT,__VA_ARGS__) -#define LOOP14(I,STMT,...) STMT(I,__VA_ARGS__) LOOP13(INC(I),STMT,__VA_ARGS__) -#define LOOP15(I,STMT,...) STMT(I,__VA_ARGS__) LOOP14(INC(I),STMT,__VA_ARGS__) -#define LOOP16(I,STMT,...) STMT(I,__VA_ARGS__) LOOP15(INC(I),STMT,__VA_ARGS__) - -#define _LOOP_(n,I,STMT,...) LOOP##n(I,STMT,__VA_ARGS__) -#define LOOP(n,I,STMT,...) _LOOP_(n,I,STMT,__VA_ARGS__) - -/* loop expansion (horizontal) */ -#define EXPAND0(I,TERM,OP,...) -#define EXPAND1(I,TERM,OP,...) TERM(I,__VA_ARGS__) -#define EXPAND2(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND1(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND3(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND2(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND4(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND3(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND5(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND4(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND6(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND5(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND7(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND6(INC(I),TERM,OP,__VA_ARGS__) -#define EXPAND8(I,TERM,OP,...) TERM(I,__VA_ARGS__) OP EXPAND7(INC(I),TERM,OP,__VA_ARGS__) - -#define _EXPAND_(n,I,TERM,OP,...) EXPAND##n(I,TERM,OP,__VA_ARGS__) -#define EXPAND(n,I,TERM,OP,...) _EXPAND_(n,I,TERM,OP,__VA_ARGS__) -#define EXPAND_TRI1(n,I,TERM,OP,...) EXPAND(n,I,TERM,OP,__VA_ARGS__) -#define EXPAND_TRI(n,I,TERM,OP,...) EXPAND_TRI1(SUB(n,I),I,TERM,OP,__VA_ARGS__) diff --git a/sys/libmath/matrix.c b/sys/libmath/matrix.c deleted file mode 100644 index e8bca0b..0000000 --- a/sys/libmath/matrix.c +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include -#include - -/* TODO: replace (incrementally) with native C version! */ -#include -#include - -// ----------------------------------------------------------------------- -// level 1 - -error -la·vecslice(math·Vector *x, int min, int max, int inc) -{ - if (max > x->len || min < 0) { - errorf("out of bounds: attempted to access vector past length"); - return 1; - } - x->len = (max - min) / inc; - x->d += x->inc * min; - x->inc *= inc; - - return 0; -} - -/* simple blas wrappers */ -void -la·veccopy(math·Vector *dst, math·Vector *src) -{ - return cblas_dcopy(src->len, src->d, src->inc, dst->d, dst->inc); -} - -double -la·vecnorm(math·Vector *x) -{ - return cblas_dnrm2(x->len, x->d, x->inc); -} - -void -la·vecscale(math·Vector *x, double a) -{ - return cblas_dscal(x->len, a, x->d, x->inc); -} - -double -la·vecdot(math·Vector *x, math·Vector *y) -{ - return cblas_ddot(x->len, x->d, x->inc, y->d, y->inc); -} - -// ----------------------------------------------------------------------- -// level 2 - -error -la·vecmat(math·Vector *x, math·Matrix *M) -{ - if (M->dim[1] != x->len) { - errorf("incompatible matrix dimensions"); - return 1; - } - if (M->state & ~mat·trans) - cblas_dgemv(CblasRowMajor,CblasNoTrans,M->dim[0],M->dim[1],1.,M->d,M->inc,x->d,x->inc,0.,x->d,x->inc); - else - cblas_dgemv(CblasRowMajor,CblasTrans,M->dim[0],M->dim[1],1.,M->d,M->inc,x->d,x->inc,0.,x->d,x->inc); - - return 0; -} - -// ----------------------------------------------------------------------- -// level 3 - -void -la·transpose(math·Matrix *X) -{ - int tmp; - X->state ^= mat·trans; - tmp = X->dim[0], X->dim[0] = X->dim[1], X->dim[1] = tmp; -} - -error -la·matrow(math·Matrix *X, int r, math·Vector *row) -{ - if (r < 0 || r >= X->dim[0]) { - errorf("out of bounds"); - return 1; - } - - row->len = X->dim[1]; - row->inc = 1; - row->d = X->d + X->dim[1] * r; - - return 0; -} - -error -la·matcol(math·Matrix *X, int c, math·Vector *col) -{ - if (c < 0 || c >= X->dim[1]) { - errorf("out of bounds"); - return 1; - } - - col->len = X->dim[0]; - col->inc = X->dim[1]; - col->d = X->d + c; - - return 0; -} - -error -la·matslice(math·Matrix *X, int r[3], int c[3]) -{ - /* TODO */ - return 0; -} - -error -la·eig(math·Matrix *X) -{ - -} - -/* X = A*B */ -error -la·matmul(math·Matrix *X, math·Matrix *A, math·Matrix *B) -{ - if (A->dim[1] != B->dim[0]) { - errorf("number of interior dimensions of A '%d' not equal to that of B '%d'", A->dim[1], B->dim[0]); - return 1; - } - if (X->dim[0] != A->dim[0]) { - errorf("number of exterior dimensions of X '%d' not equal to that of A '%d'", X->dim[0], A->dim[0]); - return 1; - } - if (X->dim[1] != B->dim[1]) { - errorf("number of exterior dimensions of X '%d' not equal to that of B '%d'", X->dim[1], B->dim[1]); - return 1; - } - - if (X->state & ~mat·trans) - if (A->state & ~mat·trans) - cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasNoTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); - else - cblas_dgemm(CblasRowMajor,CblasNoTrans,CblasTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); - else - if (A->state & ~mat·trans) - cblas_dgemm(CblasRowMajor,CblasTrans,CblasNoTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); - else - cblas_dgemm(CblasRowMajor,CblasTrans,CblasTrans,A->dim[0],B->dim[1],A->dim[1],1.,A->d,A->inc,B->d,B->inc,0.,X->d,X->inc); - - return 0; -} - -/* - * solves A*X=B - * pass in B via X - */ -error -la·solve(math·Matrix *X, math·Matrix *A) -{ - error err; - int n, *ipv; - static int buf[512]; - if (n = A->dim[0], n < arrlen(buf)) { - ipv = buf; - n = 0; - } else - ipv = malloc(n*sizeof(*ipv)); - - /* TODO: utilize more specific regimes if applicable */ - err = LAPACKE_dgesv(LAPACK_ROW_MAJOR,A->dim[0],X->dim[1],A->d,A->inc,ipv,X->d,X->inc); - - if (n) - free(ipv); - return err; -} diff --git a/sys/libmath/rules.mk b/sys/libmath/rules.mk deleted file mode 100644 index 83945d7..0000000 --- a/sys/libmath/rules.mk +++ /dev/null @@ -1,24 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -# Local sources -SRCS_$(d) := \ - $(d)/basic.c \ - $(d)/blas1.c \ - $(d)/blas2.c \ - $(d)/blas3.c -LIBS_$(d) := $(d)/libmath.a -TSTS_$(d) := - -include share/paths.mk - -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -$(UNTS_$(d)): TCFLAGS := -D_GNU_SOURCE -$(UNTS_$(d)): TCLIBS := -lpthread -lm $(LIB_DIR)/libblas.a $(OBJ_DIR)/sys/libn/libn.a $(LIBS_$(d)) -$(UNTS_$(d)): $(TOBJS_$(d)) $(LIBS_$(d)) $(OBJ_DIR)/sys/libn/libn.a - $(LINK) - -include share/pop.mk diff --git a/sys/libmath/test.c b/sys/libmath/test.c deleted file mode 100644 index 66700f8..0000000 --- a/sys/libmath/test.c +++ /dev/null @@ -1,471 +0,0 @@ -#include -#include -/* #include */ - -#include - -#include - -// ----------------------------------------------------------------------- -// Vectors - -/* - * NOTE: I'm not sure I like stashing the header in _all_ vectors - * The only way to fix is to have a library based allocator... - */ -typedef struct math·Vec -{ - struct { - void *h; - mem·Allocator heap; - }; - int len; - double *d; -} math·Vec; - -math·Vec -math·makevec(int len, mem·Allocator heap, void *h) -{ - math·Vec v; - v.len = len; - v.heap = heap; - v.h = h; - v.d = heap.alloc(h, 1, len*sizeof(double)); - - // memset(v.d, 0, len*sizeof(double)); - - return v; -} - -error -math·freevec(math·Vec *v) -{ - if (v->h == nil && v->heap.alloc == nil && v->heap.free == nil) { - errorf("attempting to free a vector that doesn't own its data"); - return 1; - } - v->heap.free(v->h, v->d); - v->d = nil; - v->len = 0; - - return 0; -} - -math·Vec -math·copyvec(math·Vec v) -{ - math·Vec cpy; - cpy.heap = v.heap; - cpy.h = v.h; - cpy.len = v.len; - cpy.d = cpy.heap.alloc(cpy.h, 1, v.len); - - memcpy(cpy.d, v.d, sizeof(double)*v.len); - return cpy; -} - -/* - * Scale vector - */ - -static -void -scale_kernel8_avx2(int n, double *x, double a) -{ - __m128d a128; - __m256d a256; - register int i; - - a128 = _mm_load_sd(&a); - a256 = _mm256_broadcastsd_pd(a128); - for (i = 0; i < n; i += 8) { - _mm256_storeu_pd(x+i+0, a256 * _mm256_loadu_pd(x+i+0)); - _mm256_storeu_pd(x+i+4, a256 * _mm256_loadu_pd(x+i+4)); - } -} - -static -void -scale_kernel8(int n, double *x, double a) -{ - register int i; - for (i = 0; i < n; i += 8) { - x[i+0] *= a; - x[i+1] *= a; - x[i+2] *= a; - x[i+3] *= a; - x[i+4] *= a; - x[i+5] *= a; - x[i+6] *= a; - x[i+7] *= a; - } -} - -void -math·scalevec(math·Vec u, double a) -{ - int n; - - n = u.len & ~7; - scale_kernel8_avx2(n, u.d, a); - - for (; n < u.len; n++) { - u.d[n] *= a; - } -} - -/* - * Add scaled vector - */ - -static -void -daxpy_kernel8_avx2(int n, double *x, double *y, double a) -{ - __m128d a128; - __m256d a256; - register int i; - - a128 = _mm_load_sd(&a); - a256 = _mm256_broadcastsd_pd(a128); - for (i = 0; i < n; i += 8) { - _mm256_storeu_pd(x+i+0, _mm256_loadu_pd(x+i+0) + a256 * _mm256_loadu_pd(y+i+0)); - _mm256_storeu_pd(x+i+4, _mm256_loadu_pd(x+i+4) + a256 * _mm256_loadu_pd(y+i+4)); - } -} - -static -void -daxpy_kernel8(int n, double *x, double *y, double a) -{ - register int i; - for (i = 0; i < n; i += 8) { - x[i+0] += a*y[i+0]; - x[i+1] += a*y[i+1]; - x[i+2] += a*y[i+2]; - x[i+3] += a*y[i+3]; - x[i+4] += a*y[i+4]; - x[i+5] += a*y[i+5]; - x[i+6] += a*y[i+6]; - x[i+7] += a*y[i+7]; - } -} - -/* performs u = u + a*v */ -void -math·addvec(math·Vec u, math·Vec v, double a) -{ - int n; - - n = u.len & ~7; - daxpy_kernel8_avx2(n, u.d, v.d, a); - - for (; n < u.len; n++) { - u.d[n] += a*v.d[n]; - } -} - -/* - * Dot product - */ - -static -double -dot_kernel8_avx2(int len, double *x, double *y) -{ - register int i; - __m256d sum[4]; - __m128d res; - - for (i = 0; i < arrlen(sum); i++) { - sum[i] = _mm256_setzero_pd(); - } - - for (i = 0; i < len; i += 16) { - sum[0] += _mm256_loadu_pd(x+i+0) * _mm256_loadu_pd(y+i+0); - sum[1] += _mm256_loadu_pd(x+i+4) * _mm256_loadu_pd(y+i+4); - sum[2] += _mm256_loadu_pd(x+i+8) * _mm256_loadu_pd(y+i+8); - sum[3] += _mm256_loadu_pd(x+i+12) * _mm256_loadu_pd(y+i+12); - } - - sum[0] += sum[1] + sum[2] + sum[3]; - - res = _mm_add_pd(_mm256_extractf128_pd(sum[0], 0), _mm256_extractf128_pd(sum[0], 1)); - res = _mm_hadd_pd(res, res); - - return res[0]; -} - -static -double -dot_kernel8_fma3(int len, double *x, double *y) -{ - register int i; - __m256d sum[4]; - __m128d res; - - for (i = 0; i < arrlen(sum); i++) { - sum[i] = _mm256_setzero_pd(); - } - - for (i = 0; i < len; i += 16) { - sum[0] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+0), _mm256_loadu_pd(y+i+0), sum[0]); - sum[1] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+4), _mm256_loadu_pd(y+i+4), sum[1]); - sum[2] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+8), _mm256_loadu_pd(y+i+8), sum[2]); - sum[3] = _mm256_fmadd_pd(_mm256_loadu_pd(x+i+12), _mm256_loadu_pd(y+i+12), sum[3]); - } - - sum[0] += sum[1] + sum[2] + sum[3]; - - res = _mm_add_pd(_mm256_extractf128_pd(sum[0], 0), _mm256_extractf128_pd(sum[0], 1)); - res = _mm_hadd_pd(res, res); - - return res[0]; -} - -static -double -dot_kernel8(int len, double *x, double *y) -{ - double res; - register int i; - - for (i = 0; i < len; i += 8) { - res += x[i] * y[i] + - x[i+1] * y[i+1] + - x[i+2] * y[i+2] + - x[i+3] * y[i+3] + - x[i+4] * y[i+4] + - x[i+5] * y[i+5] + - x[i+6] * y[i+6] + - x[i+7] * y[i+7]; - } - - return res; -} - -double -math·dot(math·Vec u, math·Vec v) -{ - int i, len; - double res; - - len = u.len & ~15; // neat trick - res = dot_kernel8_fma3(len, u.d, v.d); - - for (i = len; i < u.len; i++) { - res += u.d[i] * v.d[i]; - } - - return res; -} - -// ----------------------------------------------------------------------- -// Matrix - -typedef struct math·Mtx -{ - struct { - void *h; - mem·Allocator heap; - }; - int dim[2]; - double *d; -} math·Mtx; - -math·Mtx -math·makemtx(int n, int m, mem·Allocator heap, void *h) -{ - math·Mtx a; - a.dim[0] = n; - a.dim[1] = m; - a.heap = heap; - a.h = h; - a.d = heap.alloc(h, 1, n*m*sizeof(double)); - - // memset(a.d, 0, n*m*sizeof(double)); - - return a; -} - -error -math·freemtx(math·Vec *m) -{ - if (m->h == nil && m->heap.alloc == nil && m->heap.free == nil) { - errorf("attempting to free a matrix that doesn't own its data"); - return 1; - } - m->heap.free(m->h, m->d); - m->d = nil; - m->len = 0; - - return 0; -} - -/************************************************ - * multiply matrix to vector - ***********************************************/ - -/* - * Notation: (number of rows) x (number of columns) _ unroll factor - * N => variable we sum over - */ -static -void -mtxvec_kernel4xN_4_avx2(int ncol, double **row, double *x, double *y) -{ - int c; - __m128d hr; - __m256d x256, r256[4]; - - for (c = 0; c < 4; c++) { - r256[c] = _mm256_setzero_pd(); - } - - for (c = 0; c < ncol; c += 4) { - x256 = _mm256_loadu_pd(x+c); - r256[0] += x256 * _mm256_loadu_pd(row[0] + c); - r256[1] += x256 * _mm256_loadu_pd(row[1] + c); - r256[2] += x256 * _mm256_loadu_pd(row[2] + c); - r256[3] += x256 * _mm256_loadu_pd(row[3] + c); - } - - for (c = 0; c < 4; c++) { - hr = _mm_add_pd(_mm256_extractf128_pd(r256[c], 0), _mm256_extractf128_pd(r256[c], 1)); - hr = _mm_hadd_pd(hr, hr); - y[c] = hr[0]; - } -} - -static -void -mtxvec_kernel4xN_4(int ncol, double **row, double *x, double *y) -{ - int c; - double res[4]; - - res[0] = 0.; - res[1] = 0.; - res[2] = 0.; - res[3] = 0.; - - for (c = 0; c < ncol; c += 4) { - res[0] += row[0][c+0]*x[c+0] + row[0][c+1]*x[c+1] + row[0][c+2]*x[c+2] + row[0][c+3]*x[c+3]; - res[1] += row[1][c+0]*x[c+0] + row[1][c+1]*x[c+1] + row[1][c+2]*x[c+2] + row[1][c+3]*x[c+3]; - res[2] += row[2][c+0]*x[c+0] + row[2][c+1]*x[c+1] + row[2][c+2]*x[c+2] + row[2][c+3]*x[c+3]; - res[3] += row[3][c+0]*x[c+0] + row[3][c+1]*x[c+1] + row[3][c+2]*x[c+2] + row[3][c+3]*x[c+3]; - } - - y[0] = res[0]; - y[1] = res[1]; - y[2] = res[2]; - y[3] = res[3]; -} - -static -void -mtxvec_kernel1xN_4(int ncol, double *row, double *x, double *y) -{ - int c; - double res; - - res = 0.; - for (c = 0; c < ncol; c += 4) { - res += row[c+0]*x[c+0] + row[c+1]*x[c+1] + row[c+2]*x[c+2] + row[c+3]*x[c+3]; - } - - y[0] = res; -} - -// y = a*mx + b*y -error -math·mtxvec(math·Mtx m, double a, math·Vec x, double b, math·Vec y) -{ - int c, r, nrow, ncol; - double *row[4], res[4]; - - nrow = m.dim[0] & ~3; - ncol = m.dim[1] & ~3; - for (r = 0; r < nrow; r += 4) { - row[0] = m.d + (r * (m.dim[1]+0)); - row[1] = m.d + (r * (m.dim[1]+1)); - row[2] = m.d + (r * (m.dim[1]+2)); - row[3] = m.d + (r * (m.dim[1]+3)); - - mtxvec_kernel4xN_4_avx2(ncol, row, x.d + r, res); - - for (c = ncol; c < m.dim[1]; c++) { - res[0] += row[0][c]; - res[1] += row[1][c]; - res[2] += row[2][c]; - res[3] += row[3][c]; - } - - y.d[r+0] = res[0] + b*y.d[r+0]; - y.d[r+1] = res[1] + b*y.d[r+1]; - y.d[r+2] = res[2] + b*y.d[r+2]; - y.d[r+3] = res[3] + b*y.d[r+3]; - } - - for (; r < m.dim[0]; r++) { - mtxvec_kernel1xN_4(m.dim[0], m.d + (r * m.dim[1]), x.d + r, res); - y.d[r] = res[0] + b*y.d[r]; - } - - return 0; -} - -/************************************************ - * add matrix to vector outerproduct - ***********************************************/ - -#define NITER 50 - -#if 0 -error -main() -{ - int i; - clock_t t; - double res; - - math·Mtx m; - math·Vec x, y; - - openblas_set_num_threads(1); - - x = math·makevec(1000, mem·sys, nil); - y = math·makevec(1000, mem·sys, nil); - m = math·makemtx(1000, 1000, mem·sys, nil); - - for (i = 0; i < x.len; i++) { - y.d[i] = i; - } - - t = clock(); - for (i = 0; i < NITER; i++) { - cblas_dgemv(CblasRowMajor, CblasNoTrans, m.dim[0], m.dim[1], 1.5, m.d, m.dim[1], x.d, 1, 2.5, y.d, 1); - } - t = clock() - t; - res = math·dot(y, y); - printf("the result is %f\n", res); - printf("time elapsed (blas): %fms\n", 1000.*t/CLOCKS_PER_SEC); - - for (i = 0; i < x.len; i++) { - y.d[i] = i; - } - - t = clock(); - for (i = 0; i < NITER; i++) { - math·mtxvec(m, 1.5, x, 2.5, y); - } - t = clock() - t; - res = math·dot(y, y); - - printf("the dot product is %f\n", res); - printf("time elapsed (naive): %fms\n", 1000.*t/CLOCKS_PER_SEC); - - - return 0; -} -#endif diff --git a/sys/libsre/lex.c b/sys/libsre/lex.c deleted file mode 100644 index f4c6ac2..0000000 --- a/sys/libsre/lex.c +++ /dev/null @@ -1,246 +0,0 @@ -#include "sre.h" - -static -State * -state(Machine *m, int t) -{ - if (m->state >= m->statestk + arrlen(m->statestk)) - panicf("regexp vm: out of state space"); - - m->state->type = t; - m->state->l = nil; - m->state->r = nil; - - return m->state++; -} - -static -int -poptor(Parser *p) -{ - if (p->optor <= p->optorstk) - panicf("regexp parser: opand stack underflow"); - - return *--p->optor; -} - -static -void -pushtor(Parser *p, int t) -{ - if (p->optor >= arrend(p->optorstk)) - panicf("regexp parser: opand stack overflow"); - - *p->optor++ = t; -} - -static -void -pushand(Parser *p, State *beg, State *end) -{ - if (p->node >= arrend(p->nodestk)) - panicf("regexp parser: opand stack overflow"); - - p->node->beg = beg; - p->node->end = end; - - p->node++; -} - -static -Node * -popand(Parser *p) -{ - if (p->node <= p->nodestk) - panicf("regexp parser: opand stack underflow"); - - return --p->node; -} - -static -void -operateuntil(Parser *p, int prec) -{ - Node *o1, *o2, *t; - State *s1, *s2; - - while (prec == Trparen || p->optor[-1] >= prec) { - switch (poptor(p)) { - case Tor: - o1 = popand(p); - o2 = popand(p); - - s1 = state(p->mach, Tor); - s2 = state(p->mach, Tnop); - s1->l = o1->beg; - s1->r = o2->beg; - - o1->end->out = s2; - o2->end->out = s2; - - pushand(p, s1, s2); - break; - - case Tcat: - o1 = popand(p); - o2 = popand(p); - - o1->end->out = o2->beg; - pushand(p, o1->beg, o2->end); - break; - - case Tstar: - o1 = popand(p); - s1 = state(p->mach, Tor); - o1->end->out = s1; - s1->l = o1->beg; - pushand(p, s1, s1); - break; - - case Tplus: - o1 = popand(p); - s1 = state(p->mach, Tor); - o1->end->out = s1; - s1->l = o1->beg; - pushand(p, o1->beg, s1); - break; - - case Tqmark: - o1 = popand(p); - s1 = state(p->mach, Tor); - s2 = state(p->mach, Tnop); - s1->l = o1->beg; - s1->r = s2; - o1->end->out = s2; - pushand(p, s1, s2); - break; - - default: - panicf("unsupported regexp operator"); - } - } -} - -static -void -operator(Parser *p, int t) -{ - operateuntil(p, t); - pushtor(p, t); - p->wasopand = (t != Tstar && t != Tqmark && t != Tplus && t != Trparen); -} - -static -void -operand(Parser *p, int t) -{ - State *new; - if (p->wasopand) - operator(p, Tcat); - - new = state(p->mach, t); - pushand(p, new, new); - p->wasopand = true; -} - -#define cinc 20 -int -lex(Parser *p) -{ - int c, t; - byte *class; - long n, cap; - - c = *p->re++; - switch (c) { - case '\\': - if (*p->re) - if ((c = *p->re++) == '\n') - c = '\n'; - break; - case '\0': - c = Tend, - --p->re; - break; - case '*': - c = Tstar; - break; - case '?': - c = Tqmark; - break; - case '+': - c = Tplus; - break; - case '|': - c = Tor; - break; - case '.': - c = Tany; - break; - case '(': - c = Tlparen; - break; - case ')': - c = Trparen; - break; - case '^': - c = Tbol; - break; - case '$': - c = Teol; - break; - case '[': - goto charclass; - default: - ; - } - return c; - -charclass: - panicf("to implement"); -} - -#undef cinc - -static -State* -optimize(State *entry) -{ - State *curr, *next; - for (curr=entry; curr->type != Tend; curr++) { - next = curr->out; - while (next->type == Tnop) - next = next->out; - curr->out = next; - } - - return entry; -} - -void -sre·compile(Machine *mach, byte *regexp) -{ - int tok; - Parser p; - Node *prog; - - p = (Parser) { - .re = regexp, - .mach = mach, - .node = p.nodestk, - }; - - pushtor(&p, Tstart - 1); - while ((tok = lex(&p)) != Tend) { - if ((tok & isoptor) == Toperator) - operator(&p, tok); - else - operand(&p, tok); - } - operateuntil(&p, Tstart); - operand(&p, Tend); - operateuntil(&p, Tstart); - - prog = popand(&p); - mach->entry = optimize(prog->beg); -} diff --git a/sys/libsre/sre.h b/sys/libsre/sre.h deleted file mode 100644 index a7ace1a..0000000 --- a/sys/libsre/sre.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include -#include - -enum -{ - Toperator = RuneMask + 1, - Tstart = Toperator, - Trparen, - Tlparen, - Tor, - Tcat, - Tstar, - Tplus, - Tqmark, - - Tany = Toperator << 1, - Tnop, - Tbol, - Teol, - Tcclass, - Tnclass, - Tend, - - isoptor = Toperator, - isopand = Toperator << 1, -}; - -typedef struct Class Class; -typedef struct State State; -typedef struct Patch Patch; -typedef struct Node Node; - -typedef struct Parser Parser; -typedef struct Machine Machine; - -struct Class -{ - rune *end; - rune span[64]; -}; - -struct State -{ - int type; - union { - State *l; - }; - union { - State *r; - State *out; - }; -}; - -struct Patch -{ - State *s; - Patch *link; -}; - -struct Node -{ - State *beg; - State *end; -}; - -struct Parser -{ - Machine *mach; - byte *re; - int wasopand : 1; - int *optor, optorstk[1000]; - Node *node, nodestk[1000]; -}; - -struct Machine -{ - /* memory buffers */ - struct { - void *heap; - mem·Reallocator; - }; - State *state, statestk[1000]; - - struct { - int cap; - int len; - Class *c; - } class; - - State *entry; -}; diff --git a/sys/libterm/term.c b/sys/libterm/term.c deleted file mode 100644 index 11591fc..0000000 --- a/sys/libterm/term.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "term.h" - -#include -#include - -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", -}; - -static Term *sigwinchhead; - -// ----------------------------------------------------------------------- -// 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; -} - -/* formats escape strings and writes to output */ -static void tfmt(Term *t, char *esc, int n, ...); -static void tclear(Term *t); - -// ----------------------------------------------------------------------- -// 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 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; - - t->cap.colors = unibi_get_num(t->info, unibi_max_colors); - t->cap.bce = unibi_get_bool(t->info, unibi_back_color_erase); - - /* initialize root window (get current size)*/ - struct winsize ws = { 0 }; - if (ioctl(t->fd, TIOCGWINSZ, &ws) == 1) - goto bad; - - 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; - - /* 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"); - - return t; - -bad: - panicf("failed to initialize terminal instance"); - free(t); - return nil; -} - -void -tfree(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); - 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 -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); - -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; - } -} - -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: 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 { - 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 */ -static -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 */ -static -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); -} - -static -void -tclear(Term *t) -{ - tfmt(t, t->esc.ed2, 0); -} - -void -tblit(Term *t, Window *win) -{ - 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 - -int -main() -{ - 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 deleted file mode 100644 index 6bd2f6b..0000000 --- a/sys/libterm/term.h +++ /dev/null @@ -1,270 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#define iota(x) 1 << (x) - -typedef struct RGB8 RGB8; -typedef struct Pen Pen; - -typedef struct Dot Dot; -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), - /* ... */ - PenRGB = 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 txt; - Pen pen; -}; - -struct Row -{ - Cell *cells; - uint dirty : 1; -}; - -struct Dot -{ - int row, col; -}; - -/* - * scroll.top & scroll.bot are pointers into the viewport. - * - * scroll back buffer - * - * scroll.buf->+----------------+-----+ - * | | | ^ \ - * | before | | | | - * current terminal content | viewport | | | | - * | | | | - * +----------------+-----+\ | | | s > scroll.above - * ^ | | i | \ | | i | c | - * | | | n | \ | | n | r | - * | | v | \ | | v | o | - * | | 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 - * <- maxw -> | scroll back | | - * <- w -> | buffer | | | - * | | | | - * | | | v - * scroll.buf + scroll.size->+----------------+-----+ - * <- maxw -> - * <- w -> - */ - -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; - Row *top; - Row *bot; - int size; - int index; - int above; - int below; - } scroll; - 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 */ -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; -}; - -struct Input -{ - int fd; - int flags; - 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]; - } rbuf; - struct { - uchar *s, bytes[256]; - } ebuf; - - /* key data */ - Node *keys; - struct KeyInfo c0[32]; -}; - - -struct Term -{ - /* meta data */ - char *name; - unibi_term *info; - struct { - uchar altscreen : 1; - uchar cursorvis : 1; - uchar mouse : 1; - } mode; - struct { - uchar bce : 1; - int colors; - } cap; - - /* input capture */ - Input input; - - /* output display */ - Window *root; - Pen pen; - - /* raw text to pty */ - 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; - - Term *link; -}; - -/* functions */ -void tresize(Term *t); - -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 deleted file mode 100644 index 5d36c8b..0000000 --- a/sys/libterm/window.c +++ /dev/null @@ -1,408 +0,0 @@ -#include "term.h" - -// ----------------------------------------------------------------------- -// buffers - -static -void -zero(Row *row, int start, int len) -{ - 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; -} - -static -void -roll(Row *start, Row *end, int count) -{ - int n = end - start; - - /* enforce circularity */ - count %= n; - if (count < 0) - count += n; - - 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) -{ - 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; - } - } -} - -static -void -bfini(Buffer *b) -{ - int i; - - 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; - } - } - - 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/libutf/canfit.c b/sys/libutf/canfit.c deleted file mode 100644 index 4579ab3..0000000 --- a/sys/libutf/canfit.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "internal.h" - -/* returns 1 if string of length n is long enough to be decoded */ -int -utf8·canfit(byte* s, int n) -{ - int i; - rune c; - - if(n <= 0) - return 0; - - c = *(ubyte*)s; - if(c < TByte1) - return 1; - - if(c < TByte3) - return n >= 2; - if(c < TByte4) - return n >= 3; - - return n >= UTFmax; -} diff --git a/sys/libutf/decode.c b/sys/libutf/decode.c deleted file mode 100644 index 01797f1..0000000 --- a/sys/libutf/decode.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "internal.h" - -#define ACCEPT 0 -#define REJECT 12 - -static uint8 decode[] = { - /* - * the first part of the table maps bytes to character classes that - * to reduce the size of the transition table and create bitmasks - */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - - /* - * the second part is a transition table that maps a combination - * of a state of the automaton and a character class to a state - */ - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, -}; - -int -utf8·decode(char *s, rune *r) -{ - int n; - rune v; - uint8 b, t, x=ACCEPT; - - b = ((uint8 *)s)[0]; - t = decode[b]; - v = (0xFF >> t) & b; - x = decode[256+x+t]; - - for(n=1; x > REJECT && n < UTFmax; n++){ - b = ((uint8 *)s)[n]; - t = decode[b]; - v = (v << 6) | (b & TMask); - x = decode[256+x+t]; - } - - if(x != ACCEPT){ - *r = RuneErr; - return 1; - } - - *r = v; - return n; -} - -#if 0 -int -utf8·decode(byte *s, rune *r) -{ - int c[UTFmax], i; - rune l; - - c[0] = *(ubyte*)(s); - if(c[0] < Tx){ - *r = c[0]; - return 1; - } - - l = c[0]; - for(i = 1; i < UTFmax; i++){ - c[i] = *(ubyte*)(s+i); - c[i] ^= Tx; - if(c[i] & Testx) goto bad; - - l = (l << Bitx) | c[i]; - if(c[0] < Tbyte(i + 2)){ - l &= RuneX(i + 1); - if(i == 1){ - if(c[0] < Tbyte(2) || l <= Rune1) - goto bad; - }else if(l <= RuneX(i) || l > RuneMax) - goto bad; - - if(i == 2 && SurrogateMin <= l && l <= SurrogateMax) - goto bad; - - *r = l; - return i + 1; - } - } -bad: - *r = RuneErr; - return 1; -} -#endif diff --git a/sys/libutf/decodeprev.c b/sys/libutf/decodeprev.c deleted file mode 100644 index 27dced6..0000000 --- a/sys/libutf/decodeprev.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "internal.h" - -#define ACCEPT 0 -#define REJECT 12 - -static uint8 decode[] = { - /* - * the first part of the table maps bytes to character classes that - * to reduce the size of the transition table and create bitmasks. - */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,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,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - /* - * The second part is a transition table that maps a combination - * of a state of the automaton and a character class to a state. - */ - // 0 1 2 3 4 5 6 7 8 9 10 11 - 0,24,12,12,12,12,12,24,12,24,12,12, - 0,24,12,12,12,12,12,24,12,24,12,12, - 12,36, 0,12,12,12,12,48,12,36,12,12, - 12,60,12, 0, 0,12,12,72,12,72,12,12, - 12,60,12, 0,12,12,12,72,12,72, 0,12, - 12,12,12,12,12, 0, 0,12,12,12,12,12, - 12,12,12,12,12,12,12,12,12,12,12, 0 -}; - -int -utf8·decodeprev(byte *s, rune *r) -{ - int n; - rune v; - uint8 b, t, d, x=ACCEPT; - - v=0, n=0, d=0; -nextbyte: - b = ((uint8 *)s)[-n++]; - t = decode[b]; - x = decode[256+x+t]; - - if(x > REJECT && n < UTFmax){ - v = v | ((b & TMask) << d); - d += 6; - goto nextbyte; - } - - if(x != ACCEPT) - *r = RuneErr; - else{ - v |= (((0xFFu >> t) & b) << d); - *r = v; - } - - return n; -} diff --git a/sys/libutf/encode.c b/sys/libutf/encode.c deleted file mode 100644 index fa7c93e..0000000 --- a/sys/libutf/encode.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "internal.h" - -int -utf8·encode(rune *r, byte *s) -{ - rune c; - - c = *r; - if(c < Rune1Byte){ // 7 bits - s[0] = (uint8)c; - return 1; - } - - if(c < Rune2Byte){ // 11 bits - s[0] = TByte1 | (c >> 6); - s[1] = Tx | (c & TMask); - return 2; - } - - if(c < Rune3Byte){ // 16 bits - s[0] = TByte2 | ((c >> 12)); - s[1] = Tx | ((c >> 6) & TMask); - s[2] = Tx | ((c) & TMask); - return 3; - } - - // 22 bits - if(c > RuneMax || (RuneSurrogateMin <= c && c <= RuneSurrogateMax)) - c = RuneErr; - - s[0] = TByte3 | ((c >> 18)); - s[1] = Tx | ((c >> 12) & TMask); - s[2] = Tx | ((c >> 6) & TMask); - s[3] = Tx | ((c) & TMask); - - return 4; -} - -#if 0 -int -utf8·encode(rune* r, byte* s) -{ - int i, j; - rune c; - - c = *r; - if(c <= Rune1) { - s[0] = c; - return 1; - } - - for(i = 2; i < UTFmax + 1; i++){ - if(i == 3){ - if(c > RuneMax) - c = RuneErr; - if(SurrogateMin <= c && c <= SurrogateMax) - c = RuneErr; - } - if(c <= RuneX(i) || i == UTFmax) { - s[0] = Tbyte(i) | (c >> (i - 1)*Bitx); - for(j = 1; j < i; j++) - s[j] = Tx | ((c >> (i - j - 1)*Bitx) & Maskx); - return i; - } - } - - return UTFmax; -} -#endif diff --git a/sys/libutf/find.c b/sys/libutf/find.c deleted file mode 100644 index d75feb8..0000000 --- a/sys/libutf/find.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "internal.h" - -byte* -utf8·find(byte* s, rune c) -{ - long c1; - rune r; - int n; - - if(c < Tx) - return strchr(s, c); - - for(;;){ - c1 = *(ubyte*)s; - if(c1 < Tx){ - if(c1 == 0) return nil; - if(c1 == c) return s; - s++; - continue; - } - - n = utf8·decode(s, &r); - - if(r == c) - return s; - - s += n; - } - - return nil; -} diff --git a/sys/libutf/findlast.c b/sys/libutf/findlast.c deleted file mode 100644 index ab25ab2..0000000 --- a/sys/libutf/findlast.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "internal.h" - -byte* -utf8·findlast(byte* s, rune c) -{ - long c1; - rune r; - byte *l; - - if(c < Tx) - return strrchr(s, c); - - l = nil; - for(;;){ - c1 = *(ubyte*)s; - if(c1 < Tx){ - if(c1 == 0) return l; - if(c1 == c) l = s; - s++; - continue; - } - - c1 = utf8·decode(s, &r); - - if(r == c) - l = s; - - s += c1; - } - - return nil; -} diff --git a/sys/libutf/internal.h b/sys/libutf/internal.h deleted file mode 100644 index 9719977..0000000 --- a/sys/libutf/internal.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -/* - * NOTE: we use the preprocessor to ensure we have unsigned constants. - * UTF-8 code: - * 1 byte: - * 0xxxxxxx - * 2 byte: - * 110xxxxx 10xxxxxx - * 3 byte: - * 1110xxxx 10xxxxxx 10xxxxxx - * 4 byte: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - */ - -#define Tx 0x80u // 0b10000000 transfer header -#define TMask 0x3Fu // 0b00111111 transfer mask - -#define TByte1 0xC0u // 0b11000000 -#define TByte2 0xE0u // 0b11100000 -#define TByte3 0xF0u // 0b11110000 -#define TByte4 0xF8u // 0b11111000 - -#define RuneMask 0x1FFFFFu - -#define Rune1Byte 0x000080u // 1 << 8 (1 byte) -#define Rune2Byte 0x001000u // 1 << 12 (2 bytes) -#define Rune3Byte 0x020000u // 1 << 17 (3 bytes) -#define Rune4Byte 0x400000u // 1 << 22 (4 bytes) - - -/* UTF-16 nonsense */ -#define RuneSurrogateMin 0x0D8000 -#define RuneSurrogateMax 0x0D8FFF diff --git a/sys/libutf/len.c b/sys/libutf/len.c deleted file mode 100644 index 8fbd679..0000000 --- a/sys/libutf/len.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "internal.h" - -int -utf8·len(char *s) -{ - int c; - long n; - rune r; - - n = 0; - for(;;){ - c = *(uchar*)s; - if(c < Tx){ - if(c == 0) - return n; - s++; - }else - s += utf8·decode(s, &r); - n++; - } -} diff --git a/sys/libutf/rules.mk b/sys/libutf/rules.mk deleted file mode 100644 index 53ff8cf..0000000 --- a/sys/libutf/rules.mk +++ /dev/null @@ -1,76 +0,0 @@ -include share/push.mk - -UNICODE = 14.0.0 - -SRCS_$(d) := \ - $(d)/encode.c \ - $(d)/decode.c \ - $(d)/decodeprev.c \ - $(d)/find.c \ - $(d)/findlast.c \ - $(d)/canfit.c \ - $(d)/runelen.c \ - $(d)/len.c \ - $(d)/runetype-$(UNICODE).c \ - $(d)/runewidth-$(UNICODE).c - -LIBS_$(d) := $(d)/libutf.a - -include share/paths.mk - -# ======================================================================== -# table generation - -$(d)/vendor/common.o: $(d)/vendor/common.c - $(COMPILE) - -# rune categories -$(d)/vendor/UnicodeData-$(UNICODE).txt: - @echo "GET UnicodeData.txt";\ - curl https://www.unicode.org/Public/$(UNICODE)/ucd/UnicodeData.txt > $@ - -$(d)/vendor/mkrunetype: $(d)/vendor/mkrunetype.c $(d)/vendor/common.o $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -GENS += $(d)/vendor/mkrunetype - -$(d)/runetype-$(UNICODE).c: $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/mkrunetype - @$(dir $@)vendor/mkrunetype $< > $@ - -# rune widths -$(d)/vendor/EastAsianWidth-$(UNICODE).txt: - @echo "GET EastAsianWidth.txt";\ - curl https://www.unicode.org/Public/$(UNICODE)/ucd/EastAsianWidth.txt > $@ - -$(d)/vendor/EmojiData-$(UNICODE).txt: - @echo "GET EmojiData.txt";\ - curl https://www.unicode.org/Public/$(UNICODE)/ucd/emoji/emoji-data.txt > $@ - -$(d)/vendor/mkrunewidth: $(d)/vendor/mkrunewidth.c $(d)/vendor/common.o $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -GENS += $(d)/vendor/mkrunewidth - -$(d)/runewidth-$(UNICODE).c: $(d)/vendor/mkrunewidth $(d)/vendor/UnicodeData-$(UNICODE).txt $(d)/vendor/EastAsianWidth-$(UNICODE).txt $(d)/vendor/EmojiData-$(UNICODE).txt - @$(dir $@)vendor/mkrunewidth $(filter-out $<, $^) > $@ - -# grapheme boundaries -$(d)/vendor/GraphemeBreakProperty-$(UNICODE).txt: - @echo "GET GraphemeBreakProperty.txt";\ - curl https://www.unicode.org/Public/$(UNICODE)/ucd/auxiliary/GraphemeBreakProperty.txt > $@ - -$(d)/vendor/mkgraphemedata: $(d)/vendor/mkgraphemedata.c $(d)/vendor/common.o $(OBJ_DIR)/sys/base/base.a - $(COMPLINK) - -$(d)/graphemedata-$(UNICODE).c: $(d)/vendor/mkgraphemedata $(d)/vendor/GraphemeBreakProperty-$(UNICODE).txt - $^ > $@ - -GENS += $(d)/vendor/mkgraphemedata - -# ======================================================================== -# normal operations - -$(LIBS_$(d)): $(OBJS_$(d)) - $(ARCHIVE) - -include share/pop.mk diff --git a/sys/libutf/runelen.c b/sys/libutf/runelen.c deleted file mode 100644 index dac7f15..0000000 --- a/sys/libutf/runelen.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "internal.h" - -int -utf8·runelen(rune r) -{ - byte s[10]; - return utf8·encode(&r, s); -} diff --git a/sys/libutf/vendor/common.c b/sys/libutf/vendor/common.c deleted file mode 100644 index 5a03a50..0000000 --- a/sys/libutf/vendor/common.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "common.h" - -// ----------------------------------------------------------------------- -// input functions - -int -parse(io·Stream *io, int nfield, char **field, int len, char *line) -{ - int n; - if((n=io·readln(io, len, line)) <= 0) - return ParseEOF; - - if(n == len) - panicf("line too long"); - - if(line[n-1] != '\n') - panicf("invalid line: expected '\n', found '%c'", line[n]); - - line[n-1] = 0; - - if(line[0] == '#' || line[0] == 0) - return ParseSkip; - - /* tokenize line into fields */ - n = 0; - field[n] = line; - while(*line){ - if(*line == ';'){ - *line = 0; - field[++n] = line+1; - } - line++; - } - - if(n != nfield-1) - panicf("expected %d number of fields, got %d: %s", nfield, n, line); - - return ParseOK; -} - -int -codepoint(char *s) -{ - int c, b; - - c = 0; - while((b=*s++)){ - c <<= 4; - if(b >= '0' && b <= '9') - c += b - '0'; - else if(b >= 'A' && b <= 'F') - c += b - 'A' + 10; - else - panicf("bad codepoint char '%c'", b); - } - - return c; -} - -void -codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop) -{ - int e, c; - char *other[NumFields], line[1024]; - - // XXX: the stop variable passes in the previous stopping character - e = *stop; - c = codepoint(field[Fcode]); - - if(c >= NumRunes) - panicf("unexpected large codepoint %x", c); - if(c <= e) - panicf("bad code sequence: %x then %x", e, c); - e = c; - - if(strstr(field[Fname], ", First>") != nil){ - if(!parse(utf8, arrlen(other), other, arrlen(line), line)) - panicf("range start at end of file"); - if(strstr(other[Fname], ", Last>") == nil) - panicf("range start not followed by range end"); - - e = codepoint(other[Fcode]); - - if(e <= c) - panicf("bad code sequence: %x then %x", c, e); - if(strcmp(field[Fcategory], other[Fcategory]) != 0) - panicf("range with mismatched category"); - } - - *start = c; - *stop = e; -} - -// ----------------------------------------------------------------------- -// output functions - -void -putsearch(void) -{ - puts( - "#include \n" - "#include \n" - "\n" - "static\n" - "rune*\n" - "rangesearch(rune c, rune *t, int n, int ne)\n" - "{\n" - " rune *p;\n" - " int m;\n" - " while(n > 1) {\n" - " m = n >> 1;\n" - " p = t + m*ne;\n" - " if(c >= p[0]){\n" - " t = p;\n" - " n = n-m;\n" - " }else\n" - " n = m;\n" - " }\n" - " if(n && c >= t[0])\n" - " return t;\n" - " return 0;\n" - "}\n" - ); - -} - -int -putrange(char *ident, char *prop, int force) -{ - int l, r, start; - - start = 0; - for(l = 0; l < NumRunes;) { - if(!prop[l]){ - l++; - continue; - } - - for(r = l+1; r < NumRunes; r++){ - if(!prop[r]) - break; - prop[r] = 0; - } - - if(force || r > l + 1){ - if(!start){ - printf("static rune %s[] = {\n", ident); - start = 1; - } - prop[l] = 0; - printf("\t0x%.4x, 0x%.4x,\n", l, r-1); - } - - l = r; - } - - if(start) - printf("};\n\n"); - - return start; -} - -int -putpair(char *ident, char *prop) -{ - int l, r, start; - - start = 0; - for(l=0; l+2 < NumRunes; ){ - if(!prop[l]){ - l++; - continue; - } - - for(r = l + 2; r < NumRunes; r += 2){ - if(!prop[r]) - break; - prop[r] = 0; - } - - if(r != l + 2){ - if(!start){ - printf("static rune %s[] = {\n", ident); - start = 1; - } - prop[l] = 0; - printf("\t0x%.4x, 0x%.4x,\n", l, r - 2); - } - - l = r; - } - - if(start) - printf("};\n\n"); - return start; -} - -int -putsingle(char *ident, char *prop) -{ - int i, start; - - start = 0; - for(i = 0; i < NumRunes; i++) { - if(!prop[i]) - continue; - - if(!start){ - printf("static rune %s[] = {\n", ident); - start = 1; - } - prop[i] = 0; - printf("\t0x%.4x,\n", i); - } - - if(start) - printf("};\n\n"); - - return start; -} diff --git a/sys/libutf/vendor/common.h b/sys/libutf/vendor/common.h deleted file mode 100644 index 62f6c5b..0000000 --- a/sys/libutf/vendor/common.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include - -enum -{ - // Fields inside UnicodeData.txt - Fcode, - Fname, - Fcategory, - Fcombine, - Fbidir, - Fdecomp, - Fdecimal, - Fdigit, - Fnumeric, - Fmirror, - Foldname, - Fcomment, - Fupper, - Flower, - Ftitle, - - NumFields, - NumRunes = 1 << 21, -}; - -/* input functions */ -enum -{ - ParseEOF, - ParseOK, - ParseSkip, -}; - -int parse(io·Stream *io, int nfield, char **field, int len, char *line); -int codepoint(char *s); -void codepointrange(io·Stream *utf8, char *field[NumFields], int *start, int *stop); - -/* output functions */ -void putsearch(void); -int putrange(char *ident, char *prop, int force); -int putpair(char *ident, char *prop); -int putsingle(char *ident, char *prop); diff --git a/sys/libutf/vendor/mkgraphemedata.c b/sys/libutf/vendor/mkgraphemedata.c deleted file mode 100644 index ce5a952..0000000 --- a/sys/libutf/vendor/mkgraphemedata.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include - -// ----------------------------------------------------------------------- -// main point of entry - -static -void -usage(void) -{ - fprintf(stderr, "usage: mkgraphemedata \n"); - exit(1); -} - -int -main(int argc, char *argv[]) -{ - io·Stream *utf8; - char line[1024]; - - ARGBEGIN{ - }ARGEND; -} diff --git a/sys/libutf/vendor/mkrunetype.c b/sys/libutf/vendor/mkrunetype.c deleted file mode 100644 index 9f939f4..0000000 --- a/sys/libutf/vendor/mkrunetype.c +++ /dev/null @@ -1,388 +0,0 @@ -#include "common.h" - -// ----------------------------------------------------------------------- -// globals - -#define OFFSET (1 << 20) -#define DELTA(mapx, x) ((1 << 20) + (mapx) - (x)) - -// TODO: use bitarrays. will reduce executable size 8x -struct Table -{ - /* properties */ - char isspace[NumRunes]; - char isalpha[NumRunes]; - char ismark[NumRunes]; - char isdigit[NumRunes]; - char isupper[NumRunes]; - char islower[NumRunes]; - char istitle[NumRunes]; - char ispunct[NumRunes]; - char issymbl[NumRunes]; - char iscntrl[NumRunes]; - - char combine[NumRunes]; - - /* transformations */ - int toupper[NumRunes]; - int tolower[NumRunes]; - int totitle[NumRunes]; -}; - -static struct Table table; - -// ----------------------------------------------------------------------- -// internal functions - -static -int -isrange(char *label, char *prop, int force) -{ - char ident[128]; - if(snprintf(ident, arrlen(ident), "is%s_range", label) == arrlen(ident)) - panicf("out of identifier space\n"); - - return putrange(ident, prop, force); -} - -static -int -ispair(char *label, char *prop) -{ - char ident[128]; - if(snprintf(ident, arrlen(ident), "is%s_pair", label) == arrlen(ident)) - panicf("out of identifier space\n"); - - return putpair(ident, prop); -} - -static -int -issingle(char *label, char *prop) -{ - char ident[128]; - if(snprintf(ident, arrlen(ident), "is%s_single", label) == arrlen(ident)) - panicf("out of identifier space\n"); - - return putsingle(ident, prop); -} - -static -void -makeis(char *label, char *table, int pairs, int onlyranges) -{ - int hasr, hasp=0, hass=0; - - hasr = isrange(label, table, onlyranges); - if(!onlyranges && pairs) - hasp = ispair(label, table); - if(!onlyranges) - hass = issingle(label, table); - - printf( - "int\n" - "utf8·is%s(rune c)\n" - "{\n" - " rune *p;\n" - "\n", - label); - - if(hasr){ - printf( - " p = rangesearch(c, is%s_range, arrlen(is%s_range)/2, 2);\n" - " if(p && c >= p[0] && c <= p[1])\n" - " return 1;\n", - label, label); - } - - if(hasp){ - printf( - " p = rangesearch(c, is%s_pair, arrlen(is%s_pair)/2, 2);\n" - " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" - " return 1;\n", - label, label); - } - - if(hass) - printf( - " p = rangesearch(c, is%s_single, arrlen(is%s_single), 1);\n" - " if(p && c == p[0])\n" - " return 1;\n", - label, label); - - printf( - " return 0;\n" - "}\n" - "\n"); -} - -static -int -torange(char *label, int *index, int force) -{ - int l, r, d, start = 0; - - for(l = 0; l < NumRunes; ){ - if(index[l] == l){ - l++; - continue; - } - - d = DELTA(index[l], l); - if(d != (rune)d) - panicf("bad map delta %d", d); - - for(r = l+1; r < NumRunes; r++){ - if(DELTA(index[r], r) != d) - break; - index[r] = r; - } - - if(force || r != l + 1){ - if(!start){ - printf("static rune to%s_range[] = {\n", label); - start = 1; - } - index[l] = l; - printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-1, d); - } - l = r; - } - if(start) - printf("};\n\n"); - - return start; -} - -static -int -topair(char *label, int *index) -{ - int l, r, d, start = 0; - - for(l = 0; l + 2 < NumRunes; ){ - if(index[l] == l){ - l++; - continue; - } - - d = DELTA(index[l], l); - if(d != (rune)d) - panicf("bad delta %d", d); - - for(r = l+2; r < NumRunes; r += 2){ - if(DELTA(index[r], r) != d) - break; - index[r] = r; - } - - if(r > l+2){ - if(!start){ - printf("static rune to%s_pair[] = {\n", label); - start = 1; - } - index[l] = l; - printf("\t0x%.4x, 0x%.4x, %d,\n", l, r-2, d); - } - - l = r; - } - if(start) - printf("};\n\n"); - - return start; -} - -static -int -tosingle(char *label, int *index) -{ - int i, d, start = 0; - - for(i=0; i < NumRunes; i++) { - if(index[i] == i) - continue; - - d = DELTA(index[i], i); - if(d != (rune)d) - panicf("bad map delta %d", d); - - if(!start){ - printf("static rune to%s_single[] = {\n", label); - start = 1; - } - index[i] = i; - printf("\t0x%.4x, %d,\n", i, d); - } - if(start) - printf("};\n\n"); - - return start; -} - -static -void -mkto(char *label, int *index, int pairs, int onlyrange) -{ - int hasr, hasp=0, hass=0; - - hasr = torange(label, index, !onlyrange); - if(!onlyrange && pairs) - hasp = topair(label, index); - if(!onlyrange) - hass = tosingle(label, index); - - printf( - "rune\n" - "utf8·to%s(rune c)\n" - "{\n" - " rune *p;\n" - "\n", - label); - - if(hasr) - printf( - " p = rangesearch(c, to%s_range, arrlen(to%s_range)/3, 3);\n" - " if(p && c >= p[0] && c <= p[1])\n" - " return c + p[2] - %d;\n", - label, label, OFFSET); - - if(hasp) - printf( - " p = rangesearch(c, to%s_pair, arrlen(to%s_pair)/3, 3);\n" - " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" - " return c + p[2] - %d;\n", - label, label, OFFSET); - - if(hass) - printf( - " p = rangesearch(c, to%s_single, arrlen(to%s_single)/2, 2);\n" - " if(p && c == p[0])\n" - " return c + p[1] - %d;\n", - label, label, OFFSET); - - - printf( - " return c;\n" - "}\n" - "\n" - ); -} - -// ----------------------------------------------------------------------- -// main point of entry - -static -void -usage(void) -{ - fprintf(stderr, "usage: mkrunetype \n"); - exit(1); -} - -int -main(int argc, char *argv[]) -{ - int i, sc, c, ec; - io·Stream *utf8; - char *prop, *field[NumFields], line[1024]; - - ARGBEGIN{ - }ARGEND; - - if(argc != 1) - usage(); - - if(!(utf8 = io·open(argv[0], "r"))) - panicf("can't open %s\n", argv[0]); - - /* by default each character maps to itself */ - for(i = 0; i < NumRunes; i++) { - table.toupper[i] = i; - table.tolower[i] = i; - table.totitle[i] = i; - } - - /* ensure all C local white space characters pass */ - table.isspace['\t'] = 1; - table.isspace['\n'] = 1; - table.isspace['\r'] = 1; - table.isspace['\f'] = 1; - table.isspace['\v'] = 1; - table.isspace[0x85] = 1; - - ec = -1; - // NOTE: we don't check for comments here: assume UnicodeData.txt doesn't have any - while(parse(utf8, arrlen(field), field, arrlen(line), line)){ - /* parse unicode range */ - codepointrange(utf8, field, &sc, &ec); - prop = field[Fcategory]; - - for(c = sc; c <= ec; c++){ - /* grab properties */ - switch(prop[0]){ - case 'L': - table.isalpha[c] = 1; - switch(prop[1]){ - case 'u': table.isupper[c] = 1; break; - case 'l': table.islower[c] = 1; break; - case 't': table.istitle[c] = 1; break; - case 'm': break; // modifier letters - case 'o': break; // ideograph letters - default: - goto badproperty; - } - break; - - case 'Z': - table.isspace[c] = 1; - break; - - case 'M': - table.ismark[c] = 1; - break; - - case 'N': - table.isdigit[c] = 1; - break; - - case 'P': - table.ispunct[c] = 1; - break; - - case 'S': - table.issymbl[c] = 1; - break; - - case 'C': - table.iscntrl[c] = 1; - break; - - default: badproperty: - panicf("unrecognized category '%s'", prop); - } - /* grab transformations */ - if(*field[Fupper]) - table.toupper[c] = codepoint(field[Fupper]); - if(*field[Flower]) - table.tolower[c] = codepoint(field[Flower]); - if(*field[Ftitle]) - table.totitle[c] = codepoint(field[Ftitle]); - } - } - io·close(utf8); - - putsearch(); - - makeis("space", table.isspace, 0, 1); - makeis("digit", table.isdigit, 0, 1); - makeis("alpha", table.isalpha, 0, 0); - makeis("upper", table.isupper, 1, 0); - makeis("lower", table.islower, 1, 0); - makeis("title", table.istitle, 1, 0); - makeis("punct", table.ispunct, 1, 0); - - mkto("upper", table.toupper, 1, 0); - mkto("lower", table.tolower, 1, 0); - mkto("title", table.totitle, 1, 0); -} diff --git a/sys/libutf/vendor/mkrunewidth.c b/sys/libutf/vendor/mkrunewidth.c deleted file mode 100644 index 14e6973..0000000 --- a/sys/libutf/vendor/mkrunewidth.c +++ /dev/null @@ -1,325 +0,0 @@ -#include "common.h" - -/* - * inspired by design choices in utf8proc/charwidths.jl - * all widths default to 1 unless they fall within the categories: - * 1. Mn 2. Mc 3. Me 4. Zl - * 5. Zp 6. Cc 7. Cf 8. Cs - * these default to zero width - */ -enum -{ - /* width ? */ - WidthNeutral, /* (N) practially treated like narrow but unclear ... */ - WidthAmbiguous, /* (A) sometimes wide and sometimes not... */ - /* width 1 */ - WidthHalf, /* (H) = to narrow (compatability equivalent) */ - WidthNarrow, /* (Na) ASCII width */ - /* width 2 */ - WidthWide, /* (W) 2x width */ - WidthFull, /* (F) = to wide (compatability equivalent) */ -}; - -struct Table -{ - char width[3][NumRunes]; -}; - -static struct Table table; - -// ----------------------------------------------------------------------- -// internal functions - -static -void -parse_category(char *path) -{ - int sc, c, ec, w; - io·Stream *utf8; - char *prop, *field[NumFields], line[1024]; - - if(!(utf8 = io·open(path, "r"))) - panicf("can't open %s\n", path); - - // NOTE: we don't check for comments here - ec = -1; - while(parse(utf8, arrlen(field), field, arrlen(line), line)){ - codepointrange(utf8, field, &sc, &ec); - - prop = field[Fcategory]; - - switch(prop[0]){ - case 'M': - switch(prop[1]){ - case 'n': case 'c': case 'e': - w = 0; - break; - default: - w = 1; - break; - } - break; - case 'Z': - switch(prop[1]){ - case 'l': case 'p': - w = 0; - break; - default: - w = 1; - break; - } - break; - case 'C': - switch(prop[1]){ - case 'c': case 'f': case 's': - w = 0; - break; - default: - w = 1; - break; - } - default: - w = 1; - } - - for(c = sc; c <= ec; c++) - table.width[w][c] = 1; - } - - io·close(utf8); -} - -static -void -coderange(char *field, int *l, int *r) -{ - char *s; - - if(!(s = strstr(field, ".."))) - *l=*r=codepoint(field); - else{ - *s++ = 0, *s++ = 0; - *l=codepoint(field); - *r=codepoint(s); - } -} - -static -void -parse_eawidths(char *path) -{ - int at, w; - int l, c, r; - io·Stream *utf8; - char *field[2], line[1024]; - - utf8 = io·open(path, "r"); - while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ - if(at == ParseSkip) - continue; - - switch(field[1][0]){ - case 'A': continue; - case 'N': - if(field[1][1] != 'a') - continue; - /* fallthrough */ - case 'H': w = 1; break; - - case 'W': /* fallthrough */ - case 'F': w = 2; break; - - default: - panicf("malformed east asian width class: %s\n", field[1]); - } - - coderange(field[0], &l, &r); - - for(c=l; c <= r; c++){ - /* ensure it only exists in one table */ - table.width[w][c] = 1; - table.width[(w+1)%3][c] = 0; - table.width[(w+2)%3][c] = 0; - } - } - io·close(utf8); -} - -static -void -parse_emoji(char *path) -{ - int at, w; - int l, c, r; - io·Stream *utf8; - char *s, *field[2], line[1024]; - - utf8 = io·open(path, "r"); - while((at=parse(utf8, arrlen(field), field, arrlen(line), line)) != ParseEOF){ - if(at == ParseSkip) - continue; - - /* only override emoji presentation */ - if(!strstr(field[1], "Emoji_Presentation")) - continue; - - /* trim trailing space */ - for(s=field[0]; *s; s++){ - if(*s == ' ') - *s = 0; - } - - coderange(field[0], &l, &r); - - for(c=l; c <= r; c++){ - table.width[0][c] = 0; - table.width[1][c] = 0; - table.width[2][c] = 1; - } - } - - io·close(utf8); -} - -/* output functions */ -static -void -maketable(char *label, char *table, int pairs, int onlyranges) -{ - int r, p=0, s=0; - char ident[3][128]; - - enum - { - Irange, - Ipair, - Isingle, - }; - - /* ranges */ - if(snprintf(ident[Irange], arrlen(ident[Irange]), "%s_range", label) == arrlen(ident[Irange])) - panicf("out of identifier space\n"); - r = putrange(ident[Irange], table, onlyranges); - - if(!onlyranges && pairs){ - if(snprintf(ident[Ipair], arrlen(ident[Ipair]), "%s_pair", label) == arrlen(ident[Ipair])) - panicf("out of identifier space\n"); - p = putpair(ident[Ipair], table); - } - if(!onlyranges){ - if(snprintf(ident[Isingle], arrlen(ident[Isingle]), "%s_single", label) == arrlen(ident[Isingle])) - panicf("out of identifier space\n"); - - s = putsingle(ident[Isingle], table); - } - - printf( - "static int\n" - "is%s(rune c)\n" - "{\n" - " rune *p;\n" - "\n", - label); - - if(r){ - printf( - " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" - " if(p && c >= p[0] && c <= p[1])\n" - " return 1;\n", - ident[Irange], ident[Irange]); - } - - if(p){ - printf( - " p = rangesearch(c, %s, arrlen(%s)/2, 2);\n" - " if(p && c >= p[0] && c <= p[1] && !((c - p[0]) & 1))\n" - " return 1;\n", - ident[Ipair], ident[Ipair]); - } - - if(s) - printf( - " p = rangesearch(c, %s, arrlen(%s), 1);\n" - " if(p && c == p[0])\n" - " return 1;\n", - ident[Isingle], ident[Isingle]); - - printf( - " return 0;\n" - "}\n" - "\n"); -} - -// ----------------------------------------------------------------------- -// main point of entry - -static -void -usage(void) -{ - fprintf(stderr, "usage: mkrunewidth \n"); - exit(1); -} - -#define SETW0(c) \ - table.width[0][(c)] = 1, \ - table.width[1][(c)] = 0, \ - table.width[2][(c)] = 0; - -#define SETW1(c) \ - table.width[0][(c)] = 0, \ - table.width[1][(c)] = 1, \ - table.width[2][(c)] = 0; - -#define SETW2(c) \ - table.width[0][(c)] = 0, \ - table.width[1][(c)] = 0, \ - table.width[2][(c)] = 1; - - -int -main(int argc, char *argv[]) -{ - int c; - - ARGBEGIN{ - }ARGEND; - - if(argc != 3) - usage(); - - parse_category(*argv++); - parse_eawidths(*argv++); - parse_emoji(*argv); - - /* overrides */ - SETW0(0x2028); - SETW0(0x2029); - - SETW1(0x00AD); - - /* simple checking */ - for(c=0; c 1) - panicf("improper table state"); - } - - putsearch(); - - maketable("width0", table.width[0], 1, 0); - maketable("width1", table.width[1], 1, 0); - maketable("width2", table.width[2], 1, 0); - - puts( - "\n" - "int\n" - "utf8·runewidth(rune c)\n" - "{\n" - " if(iswidth1(c))\n" - " return 1;\n" - " if(iswidth2(c))\n" - " return 2;\n" - " return 0;\n" - "}" - ); -} diff --git a/sys/nixos/rules.mk b/sys/nixos/rules.mk deleted file mode 100644 index e69de29..0000000 diff --git a/sys/rules.mk b/sys/rules.mk deleted file mode 100644 index 6d1dfa5..0000000 --- a/sys/rules.mk +++ /dev/null @@ -1,41 +0,0 @@ -include share/push.mk - -# Iterate through subdirectory tree - -DIR := $(d)/cmd -include $(DIR)/rules.mk - -DIR := $(d)/base -include $(DIR)/rules.mk - -DIR := $(d)/libutf -include $(DIR)/rules.mk - -DIR := $(d)/libfmt -include $(DIR)/rules.mk - -DIR := $(d)/libmath -include $(DIR)/rules.mk - -DIR := $(d)/libbio -include $(DIR)/rules.mk - -# DIR := $(d)/libc -# include $(DIR)/rules.mk - -# DIR := $(d)/libdraw -# include $(DIR)/rules.mk - -# DIR := $(d)/libimage -# include $(DIR)/rules.mk - -# DIR := $(d)/libfont -# include $(DIR)/rules.mk - -# DIR := $(d)/libterm -# include $(DIR)/rules.mk - -# DIR := $(d)/libsre -# include $(DIR)/rules.mk - -include share/pop.mk -- cgit v1.2.1