about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Makefile38
-rw-r--r--README.md4
-rw-r--r--doc/build.md6
-rw-r--r--doc/cha-api.5828
-rw-r--r--doc/cha-config.51514
-rw-r--r--doc/cha-localcgi.5336
-rw-r--r--doc/cha-mailcap.5160
-rw-r--r--doc/cha-mime.types.572
-rw-r--r--doc/cha-protocols.5283
-rw-r--r--doc/cha-urimethodmap.5163
10 files changed, 3371 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index fd8ac6c2..86d742fe 100644
--- a/Makefile
+++ b/Makefile
@@ -208,33 +208,24 @@ $(OBJDIR)/man/cha-%.md: doc/%.md md2manpreproc
 	@mkdir -p "$(OBJDIR)/man"
 	./md2manpreproc $< > $@
 
-$(OBJDIR)/man/cha-%.5: $(OBJDIR)/man/cha-%.md
+doc/cha-%.5: $(OBJDIR)/man/cha-%.md
 	pandoc --standalone --to man $< -o $@
 
-$(OBJDIR)/man/cha.1: doc/cha.1
-	@mkdir -p "$(OBJDIR)/man"
-	cp doc/cha.1 "$(OBJDIR)/man/cha.1"
-
-$(OBJDIR)/man/mancha.1: doc/mancha.1
-	@mkdir -p "$(OBJDIR)/man"
-	cp doc/mancha.1 "$(OBJDIR)/man/mancha.1"
-
 .PHONY: clean
 clean:
 	rm -rf "$(OBJDIR)/$(TARGET)"
 	rm -rf "$(QJSOBJ)"
 	rm -f lib/libquickjs.a
 
-MANPAGES = $(OBJDIR)/man/cha.1 $(OBJDIR)/man/mancha.1 \
-	$(OBJDIR)/man/cha-config.5 $(OBJDIR)/man/cha-mailcap.5 \
-	$(OBJDIR)/man/cha-mime.types.5 $(OBJDIR)/man/cha-localcgi.5 \
-	$(OBJDIR)/man/cha-urimethodmap.5 $(OBJDIR)/man/cha-protocols.5 \
-	$(OBJDIR)/man/cha-api.5
+MANPAGES1 = doc/cha.1 doc/mancha.1
+MANPAGES5 = doc/cha-config.5 doc/cha-mailcap.5 doc/cha-mime.types.5 \
+	doc/cha-localcgi.5 doc/cha-urimethodmap.5 doc/cha-protocols.5 \
+	doc/cha-api.5
+
+MANPAGES = $(MANPAGES1) $(MANPAGES5)
 
 .PHONY: manpage
 manpage: $(MANPAGES)
-	mkdir -p "$(OUTDIR_MAN)"
-	for f in $(MANPAGES); do cp "$$f" "$(OUTDIR_MAN)"; done
 
 .PHONY: install
 install:
@@ -259,19 +250,8 @@ install:
 	install -m755 "$(OUTDIR_CGI_BIN)/spartan" $(LIBEXECDIR_CHAWAN)/cgi-bin
 	install -m755 "$(OUTDIR_LIBEXEC)/urldec" $(LIBEXECDIR_CHAWAN)/urldec
 	install -m755 "$(OUTDIR_LIBEXEC)/urlenc" $(LIBEXECDIR_CHAWAN)/urlenc
-	if test -d "$(OUTDIR_MAN)"; then \
-	mkdir -p "$(DESTDIR)$(MANPREFIX5)"; \
-	mkdir -p "$(DESTDIR)$(MANPREFIX1)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-config.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-mailcap.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-mime.types.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-localcgi.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-urimethodmap.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-protocols.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha-api.5" "$(DESTDIR)$(MANPREFIX5)"; \
-	install -m644 "$(OUTDIR_MAN)/cha.1" "$(DESTDIR)$(MANPREFIX1)"; \
-	install -m644 "$(OUTDIR_MAN)/mancha.1" "$(DESTDIR)$(MANPREFIX1)"; \
-	fi
+	for f in $(MANPAGES1); do install -m644 "$$f" "$(DESTDIR)$(MANPREFIX1)"; done
+	for f in $(MANPAGES5); do install -m644 "$$f" "$(DESTDIR)$(MANPREFIX5)"; done
 
 .PHONY: uninstall
 uninstall:
diff --git a/README.md b/README.md
index 7a4b9ac5..c859d872 100644
--- a/README.md
+++ b/README.md
@@ -36,9 +36,7 @@ supported yet.)
 4. Download parts of Chawan found in other repositories: `make submodule`
 5. Run `make`. (By default, this will build the whole project in release mode;
    for details, see [doc/build.md](doc/build.md).)
-6. If you want manpages, run `make manpage`. (This requires
-   [pandoc](https://pandoc.org).)
-7. Finally, install using `make install` (e.g. `sudo make install`).
+6. Finally, install using `make install` (e.g. `sudo make install`).
 
 Then, try:
 
diff --git a/doc/build.md b/doc/build.md
index 3d337d82..2fe463b8 100644
--- a/doc/build.md
+++ b/doc/build.md
@@ -35,6 +35,8 @@ also override them by setting an environment variable with the same name.
   `/tmp/usr/local/bin/cha`.
 * `MANPREFIX`, `MANPREFIX1`, `MANPREFIX5`: prefixes for the installation of
   man pages. The default setting expands to `/usr/local/share/man/man1`, etc.
+  (Normally you shouldn't have to set `MANPREFIX1` or `MANPREFIX5` at all,
+  as these are derived from `MANPREFIX`.)
 * `CURLLIBNAME`: Change the name of the libcurl shared object file.
 * `LIBEXECDIR`: Path to your libexec directory; by default, it is relative
   to wherever the binary is placed when it is executed. (i.e. after installation
@@ -52,7 +54,9 @@ also override them by setting an environment variable with the same name.
 
 * `all`: build all required executables
 * `clean`: remove OBJDIR, OUTDIR, and the QuickJS library
-* `manpage`: build man pages; note that this is not part of `all`
+* `manpage`: rebuild man pages; note that this is not part of `all`.
+  Manual pages are included in the repository, so this only needs to be called
+  when you modify the documentation.
 * `install`: install the `cha` binary, and if man pages were generated,
   those as well
 * `uninstall`: remove the `cha` binary and Chawan man pages
diff --git a/doc/cha-api.5 b/doc/cha-api.5
new file mode 100644
index 00000000..4d0c228b
--- /dev/null
+++ b/doc/cha-api.5
@@ -0,0 +1,828 @@
+'\" t
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-api" "5" "" "" "Chawan\[cq]s command API"
+.hy
+.SH Chawan\[cq]s command API
+.PP
+As described in \f[B]cha-config\f[R](5), keypress combinations can be
+bound to actions.
+.PP
+An action can be either a JavaScript expression, or a command defined in
+the \f[V][cmd]\f[R] section of config.toml.
+For example, the following works:
+.IP
+.nf
+\f[C]
+gpn = \[aq]n => pager.alert(n)\[aq] # e.g. 2gpn prints \[ga]2\[aq] to the status line
+\f[R]
+.fi
+.PP
+Note however, that JavaScript functions must be called with an
+appropriate \f[V]this\f[R] value.
+Unfortunately, this also means that the following does not work:
+.IP
+.nf
+\f[C]
+gpn = \[aq]pager.alert\[aq] # broken!!!
+\f[R]
+.fi
+.PP
+To work around this limitation, actions have to wrap the target function
+in a closure, as above.
+However, this has very poor reusability; for more complex actions, you
+would have to copy and paste the entire function every time you re-bind
+it or call it from a different function.
+.PP
+To fix this, it is possible to define a command in the \f[V][cmd]\f[R]
+section:
+.IP
+.nf
+\f[C]
+[cmd.my.namespace]
+showNumber = \[aq]n => pager.alert(n)\[aq]
+\f[R]
+.fi
+.PP
+\f[V]my.namespace\f[R] can be anything you want; it is to avoid
+collisions when including multiple configs.
+Avoid setting it to \f[V]pager\f[R] or \f[V]line\f[R], because these are
+used by the default config.
+.PP
+Now you can call \f[V]cmd.my.namespace.showNumber()\f[R] from any other
+function, or just include it in an action:
+.IP
+.nf
+\f[C]
+\[aq]gpn\[aq] = \[aq]cmd.my.namespace.showNumber\[aq]
+\f[R]
+.fi
+.SS Interfaces
+.SS Client
+.PP
+The global object (\f[V]globalThis\f[R]) implements the \f[V]Client\f[R]
+interface.
+Documented functions of this are:
+.PP
+Following properties (functions/getters) are defined by \f[V]Pager\f[R]:
+.PP
+.TS
+tab(@);
+lw(28.0n) lw(38.5n) lw(3.5n).
+T{
+Property
+T}@T{
+Description
+T}@T{
+T}
+_
+T{
+\f[V]quit()\f[R]
+T}@T{
+Exit the browser.
+T}@T{
+T}
+T{
+\f[V]suspend()\f[R]
+T}@T{
+Temporarily suspend the browser, by delivering the client process a
+SIGTSTP signal.
+Note: this suspends the entire process group.
+T}@T{
+T}
+.TE
+.PP
+\f[V]Client\f[R] also implements various web standards normally
+available on the \f[V]Window\f[R] object on websites, e.g.\ fetch().
+Note however that it does \f[I]not\f[R] give access to JS objects in
+buffers, so e.g.\ \f[V]globalThis.document\f[R] is not available.
+.SS Pager
+.PP
+\f[V]Pager\f[R] is a separate interface from \f[V]Client\f[R] that gives
+access to the pager (i.e.\ browser chrome).
+It is accessible as \f[V]globalThis.pager\f[R], or simply
+\f[V]pager\f[R].
+.PP
+Following properties (functions/getters) are defined by \f[V]Pager\f[R]:
+.PP
+.TS
+tab(@);
+lw(28.0n) lw(38.5n) lw(3.5n).
+T{
+Property
+T}@T{
+Description
+T}@T{
+T}
+_
+T{
+\f[V]load(url)\f[R]
+T}@T{
+Put the specified address into the URL bar, and optionally load it.
+Note that this performs auto-expansion of URLs, so Chawan will expand
+any matching omni-rules (e.g.\ search), try to open schemeless URLs with
+the default scheme/local files, etc.
+Opens a prompt with the current URL when no parameters are specified;
+otherwise, the string passed is displayed in the prompt.
+If this string ends with a newline
+(e.g.\ \f[V]pager.load(\[dq]about:chawann\[dq])\f[R]), the URL is loaded
+directly.
+T}@T{
+T}
+T{
+\f[V]loadSubmit(url)\f[R]
+T}@T{
+Act as if \f[V]url\f[R] had been input into the address bar.
+Same as \f[V]pager.load(url + \[dq]n\[dq])\f[R].
+T}@T{
+T}
+T{
+\f[V]gotoURL(url)\f[R]
+T}@T{
+Go to the specified URL immediately (without a prompt).
+This differs from \f[V]load\f[R] and \f[V]loadSubmit\f[R] in that it
+\f[I]does not\f[R] try to correct the URL.
+Use this for loading automatically retrieved (i.e.\ non-user-provided)
+URLs.
+T}@T{
+T}
+T{
+\f[V]dupeBuffer()\f[R]
+T}@T{
+Duplicate the current buffer by loading its source to a new buffer.
+T}@T{
+T}
+T{
+\f[V]discardBuffer()\f[R]
+T}@T{
+Discard the current buffer, and move back to its previous sibling
+buffer, or if that doesn\[cq]t exist, to its parent.
+If the current buffer is a root buffer (i.e.\ it has no parent), move to
+the next sibling buffer instead.
+T}@T{
+T}
+T{
+\f[V]discardTree()\f[R]
+T}@T{
+Discard all child buffers of the current buffer.
+T}@T{
+T}
+T{
+\f[V]reload()\f[R]
+T}@T{
+Open a new buffer with the current buffer\[cq]s URL, replacing the
+current buffer.
+T}@T{
+T}
+T{
+\f[V]reshape()\f[R]
+T}@T{
+Reshape the current buffer (=render the current page anew.)
+T}@T{
+T}
+T{
+\f[V]redraw()\f[R]
+T}@T{
+Redraw screen contents.
+Useful if something messed up the display.
+T}@T{
+T}
+T{
+\f[V]toggleSource()\f[R]
+T}@T{
+If viewing an HTML buffer, open a new buffer with its source.
+Otherwise, open the current buffer\[cq]s contents as HTML.
+T}@T{
+T}
+T{
+\f[V]lineInfo()\f[R]
+T}@T{
+Display information about the current line.
+T}@T{
+T}
+T{
+\f[V]searchForward()\f[R]
+T}@T{
+Search for a string in the current buffer.
+T}@T{
+T}
+T{
+\f[V]searchBackward()\f[R]
+T}@T{
+Search for a string, backwards.
+T}@T{
+T}
+T{
+\f[V]isearchForward()\f[R]
+T}@T{
+Incremental-search for a string, highlighting the first result.
+T}@T{
+T}
+T{
+\f[V]isearchBackward()\f[R]
+T}@T{
+Incremental-search and highlight the first result, backwards.
+T}@T{
+T}
+T{
+\f[V]gotoLine(n?)\f[R]
+T}@T{
+Go to the line passed as the first argument.
+If no arguments were specified, an input window for entering a line is
+shown.
+T}@T{
+T}
+T{
+\f[V]searchNext(n = 1)\f[R]
+T}@T{
+Jump to the nth next search result.
+T}@T{
+T}
+T{
+\f[V]searchPrev(n = 1)\f[R]
+T}@T{
+Jump to the nth previous search result.
+T}@T{
+T}
+T{
+\f[V]peek()\f[R]
+T}@T{
+Display an alert message of the current URL.
+T}@T{
+T}
+T{
+\f[V]peekCursor()\f[R]
+T}@T{
+Display an alert message of the URL or title under the cursor.
+Multiple calls allow cycling through the two.
+(i.e.\ by default, press u once -> title, press again -> URL)
+T}@T{
+T}
+T{
+\f[V]ask(prompt)\f[R]
+T}@T{
+Ask the user for confirmation.
+Returns a promise which resolves to a boolean value indicating whether
+the user responded with yes.
+Can be used to implement an exit prompt like this:
+\f[V]q = \[aq]pager.ask(\[dq]Do you want to exit Chawan?\[dq]).then(x => x ? pager.quit() : void(0))\[aq]\f[R]
+T}@T{
+T}
+T{
+\f[V]askChar(prompt)\f[R]
+T}@T{
+Ask the user for any character.
+Like \f[V]pager.ask\f[R], but the return value is a character.
+T}@T{
+T}
+T{
+\f[V]saveLink()\f[R]
+T}@T{
+Save URL pointed to by the cursor.
+T}@T{
+T}
+T{
+\f[V]saveSource()\f[R]
+T}@T{
+Save the source of the current buffer.
+T}@T{
+T}
+T{
+\f[V]extern(cmd, options = {setenv: true, suspend: true, wait: false})\f[R]
+T}@T{
+Run an external command \f[V]cmd\f[R].
+The \f[V]$CHA_URL\f[R] and \f[V]$CHA_CHARSET\f[R] variables are set when
+\f[V]options.setenv\f[R] is true.
+\f[V]options.suspend\f[R] suspends the pager while the command is being
+executed, and \f[V]options.wait\f[R] makes it so the user must press a
+key before the pager is resumed.
+Returns true if the command exit successfully, false otherwise.
+Warning: this has a bug where the output is written to stdout even if
+suspend is true.
+Redirect to /dev/null in the command if this is not desired.
+(This will be fixed in the future.)
+T}@T{
+T}
+T{
+\f[V]externCapture(cmd)\f[R]
+T}@T{
+Like extern(), but redirect the command\[cq]s stdout string into the
+result.
+null is returned if the command wasn\[cq]t executed successfully, or if
+the command returned a non-zero exit value.
+T}@T{
+T}
+T{
+\f[V]externInto(cmd, ins)\f[R]
+T}@T{
+Like extern(), but redirect \f[V]ins\f[R] into the command\[cq]s
+standard input stream.
+\f[V]true\f[R] is returned if the command exits successfully, otherwise
+the return value is \f[V]false\f[R].
+T}@T{
+T}
+T{
+\f[V]externFilterSource(cmd, buffer = null, contentType = null)\f[R]
+T}@T{
+Redirects the specified (or if \f[V]buffer\f[R] is null, the current)
+buffer\[cq]s source into \f[V]cmd\f[R].
+Then, it pipes the output into a new buffer, with the content type
+\f[V]contentType\f[R] (or, if \f[V]contentType\f[R] is null, the
+original buffer\[cq]s content type).
+Returns \f[V]undefined\f[R].
+(It should return a promise; TODO.)
+T}@T{
+T}
+T{
+\f[V]buffer\f[R]
+T}@T{
+Getter for the currently displayed buffer.
+Returns a \f[V]Buffer\f[R] object; see below.
+T}@T{
+T}
+.TE
+.SS Buffer
+.PP
+Each buffer is exposed as an object that implements the \f[V]Buffer\f[R]
+interface.
+To get a reference to the currently displayed buffer, use
+\f[V]pager.buffer\f[R].
+.PP
+Important: there exists a quirk of questionable value on pager, where
+accessing properties that do not exist on the pager will dispatch those
+to the current buffer (\f[V]pager.buffer\f[R]).
+So if you see e.g.\ \f[V]pager.url\f[R], that is actually equivalent to
+\f[V]pager.buffer.url\f[R], because \f[V]Pager\f[R] has no \f[V]url\f[R]
+getter.
+.PP
+Following properties (functions/getters) are defined by
+\f[V]Buffer\f[R]:
+.PP
+.TS
+tab(@);
+lw(28.0n) lw(38.5n) lw(3.5n).
+T{
+Property
+T}@T{
+Description
+T}@T{
+T}
+_
+T{
+\f[V]cursorUp(n = 1)\f[R]
+T}@T{
+Move the cursor upwards by n lines, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cursorDown(n = 1)\f[R]
+T}@T{
+Move the cursor downwards by n lines, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cursorLeft(n = 1)\f[R]
+T}@T{
+Move the cursor to the left by n cells, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cursorRight(n = 1)\f[R]
+T}@T{
+Move the cursor to the right by n cells, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cursorLineBegin()\f[R]
+T}@T{
+Move the cursor to the first cell of the line.
+T}@T{
+T}
+T{
+\f[V]cursorLineTextStart()\f[R]
+T}@T{
+Move the cursor to the first non-blank character of the line.
+T}@T{
+T}
+T{
+\f[V]cursorLineEnd()\f[R]
+T}@T{
+Move the cursor to the last cell of the line.
+T}@T{
+T}
+T{
+\f[V]cursorNextWord()\f[R], \f[V]cursorNextViWord()\f[R],
+\f[V]cursorNextBigWord()\f[R]
+T}@T{
+Move the cursor to the beginning of the next word.
+T}@T{
+T}
+T{
+\f[V]cursorPrevWord()\f[R], \f[V]cursorPrevViWord()\f[R],
+\f[V]cursorPrevBigWord()\f[R]
+T}@T{
+Move the cursor to the end of the previous word.
+T}@T{
+T}
+T{
+\f[V]cursorWordEnd()\f[R], \f[V]cursorViWordEnd()\f[R],
+\f[V]cursorBigWordEnd()\f[R]
+T}@T{
+Move the cursor to the end of the current word, or if already there, to
+the end of the next word.
+T}@T{
+T}
+T{
+\f[V]cursorWordBegin()\f[R], \f[V]cursorViWordBegin()\f[R],
+\f[V]cursorBigWordBegin()\f[R]
+T}@T{
+Move the cursor to the beginning of the current word, or if already
+there, to the end of the previous word.
+T}@T{
+T}
+T{
+\f[V]cursorNextLink()\f[R], \f[V]cursorPrevLink()\f[R]
+T}@T{
+Move the cursor to the beginning of the next/previous clickable element.
+T}@T{
+T}
+T{
+\f[V]cursorPrevParagraph(n = 1)\f[R]
+T}@T{
+Move the cursor to the beginning of the nth next paragraph.
+T}@T{
+T}
+T{
+\f[V]cursorNextParagraph(n = 1)\f[R]
+T}@T{
+Move the cursor to the end of the nth previous paragraph.
+T}@T{
+T}
+T{
+\f[V]cursorNthLink(n = 1)\f[R]
+T}@T{
+Move the cursor to the nth link of the document.
+T}@T{
+T}
+T{
+\f[V]cursorRevNthLink(n = 1)\f[R]
+T}@T{
+Move the cursor to the nth link of the document, counting backwards from
+the document\[cq]s last line.
+T}@T{
+T}
+T{
+\f[V]pageUp(n = 1)\f[R], \f[V]pageDown(n = 1)\f[R],
+\f[V]pageLeft(n = 1)\f[R], \f[V]pageRight(n = 1)\f[R]
+T}@T{
+Scroll up/down/left/right by n pages.
+T}@T{
+T}
+T{
+\f[V]halfPageDown(n = 1)\f[R]
+T}@T{
+Scroll forwards by n half pages.
+T}@T{
+T}
+T{
+\f[V]halfPageUp(n = 1)\f[R]
+T}@T{
+Scroll backwards by n half pages.
+T}@T{
+T}
+T{
+\f[V]halfPageLeft(n = 1)\f[R]
+T}@T{
+Scroll to the left by n half pages.
+T}@T{
+T}
+T{
+\f[V]halfPageUp(n = 1)\f[R]
+T}@T{
+Scroll to the right by n half pages.
+T}@T{
+T}
+T{
+\f[V]scrollUp(n = 1)\f[R], \f[V]scrollDown(n = 1)\f[R],
+\f[V]scrollLeft(n = 1)\f[R], \f[V]scrollRight(n = 1)\f[R]
+T}@T{
+Scroll up/down/left/right by n lines.
+T}@T{
+T}
+T{
+\f[V]click()\f[R]
+T}@T{
+Click the HTML element currently under the cursor.
+T}@T{
+T}
+T{
+\f[V]cursorFirstLine()\f[R], \f[V]cursorLastLine()\f[R]
+T}@T{
+Move to the first/last line in the buffer.
+T}@T{
+T}
+T{
+\f[V]cursorTop()\f[R]
+T}@T{
+Move to the first line on the screen.
+(Equivalent to H in vi.)
+T}@T{
+T}
+T{
+\f[V]cursorMiddle()\f[R]
+T}@T{
+Move to the line in the middle of the screen.
+(Equivalent to M in vi.)
+T}@T{
+T}
+T{
+\f[V]cursorBottom()\f[R]
+T}@T{
+Move to the last line on the screen.
+(Equivalent to L in vi.)
+T}@T{
+T}
+T{
+\f[V]lowerPage(n = this.cursory)\f[R]
+T}@T{
+Move cursor to line n, then scroll up so that the cursor is on the top
+line on the screen.
+(\f[V]zt\f[R] in vim.)
+T}@T{
+T}
+T{
+\f[V]lowerPageBegin(n = this.cursory)\f[R]
+T}@T{
+Move cursor to the first non-blank character of line n, then scroll up
+so that the cursor is on the top line on the screen.
+(\f[V]z<CR>\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]centerLine(n = this.cursory)\f[R]
+T}@T{
+Center screen around line n.\ (\f[V]zz\f[R] in vim.)
+T}@T{
+T}
+T{
+\f[V]centerLineBegin(n = this.cursory)\f[R]
+T}@T{
+Center screen around line n, and move the cursor to the line\[cq]s first
+non-blank character.
+(\f[V]z.\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]raisePage(n = this.cursory)\f[R]
+T}@T{
+Move cursor to line n, then scroll down so that the cursor is on the top
+line on the screen.
+(zb in vim.)
+T}@T{
+T}
+T{
+\f[V]lowerPageBegin(n = this.cursory)\f[R]
+T}@T{
+Move cursor to the first non-blank character of line n, then scroll up
+so that the cursor is on the last line on the screen.
+(\f[V]z\[ha]\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]nextPageBegin(n = this.cursory)\f[R]
+T}@T{
+If n was given, move to the screen before the nth line and raise the
+page.
+Otherwise, go to the previous screen\[cq]s last line and raise the page.
+(\f[V]z+\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]cursorLeftEdge()\f[R], \f[V]cursorMiddleColumn()\f[R],
+\f[V]cursorRightEdge()\f[R]
+T}@T{
+Move to the first/middle/last column on the screen.
+T}@T{
+T}
+T{
+\f[V]centerColumn()\f[R]
+T}@T{
+Center screen around the current column.
+T}@T{
+T}
+T{
+\f[V]findNextMark(x = this.cursorx, y = this.cursory)\f[R]
+T}@T{
+Find the next mark after \f[V]x\f[R], \f[V]y\f[R], if any; and return
+its id (or null if none were found.)
+T}@T{
+T}
+T{
+\f[V]findPrevMark(x = this.cursorx, y = this.cursory)\f[R]
+T}@T{
+Find the previous mark before \f[V]x\f[R], \f[V]y\f[R], if any; and
+return its id (or null if none were found.)
+T}@T{
+T}
+T{
+\f[V]setMark(id, x = this.cursorx, y = this.cursory)\f[R]
+T}@T{
+Set a mark at (x, y) using the name \f[V]id\f[R].
+Returns true if no other mark exists with \f[V]id\f[R].
+If one already exists, it will be overridden and the function returns
+false.
+T}@T{
+T}
+T{
+\f[V]clearMark(id)\f[R]
+T}@T{
+Clear the mark with the name \f[V]id\f[R].
+Returns true if the mark existed, false otherwise.
+T}@T{
+T}
+T{
+\f[V]gotoMark(id)\f[R]
+T}@T{
+If the mark \f[V]id\f[R] exists, jump to its position and return true.
+Otherwise, do nothing and return false.
+T}@T{
+T}
+T{
+\f[V]gotoMarkY(id)\f[R]
+T}@T{
+If the mark \f[V]id\f[R] exists, jump to the beginning of the line at
+its Y position and return true.
+Otherwise, do nothing and return false.
+T}@T{
+T}
+T{
+\f[V]getMarkPos(id)\f[R]
+T}@T{
+If the mark \f[V]id\f[R] exists, return its position as an array where
+the first element is the X position and the second element is the Y
+position.
+If the mark does not exist, return null.
+T}@T{
+T}
+T{
+\f[V]markURL()\f[R]
+T}@T{
+Convert URL-like strings to anchors on the current page.
+T}@T{
+T}
+T{
+\f[V]url\f[R]
+T}@T{
+Getter for the buffer\[cq]s URL.
+Note: this returns a \f[V]URL\f[R] object, not a string.
+T}@T{
+T}
+T{
+\f[V]hoverTitle\f[R], \f[V]hoverLink\f[R], \f[V]hoverImage\f[R]
+T}@T{
+Getter for the string representation of the element title/link/image
+currently under the cursor.
+Returns the empty string if no title is found.
+T}@T{
+T}
+.TE
+.SS LineEdit
+.PP
+The line editor at the bottom of the screen is exposed to the JavaScript
+context as \f[V]globalThis.line\f[R], or simply \f[V]line\f[R], and
+implements the \f[V]LineEdit\f[R] interface.
+.PP
+Note that there is no single \f[V]LineEdit\f[R] object; a new one is
+created every time the line editor is opened, and when the line editor
+is closed, \f[V]globalThis.line\f[R] simply returns \f[V]null\f[R].
+.PP
+Following properties (functions/getters) are defined by
+\f[V]LineEdit\f[R]:
+.PP
+.TS
+tab(@);
+l l l.
+T{
+Property
+T}@T{
+Description
+T}@T{
+T}
+_
+T{
+\f[V]submit()\f[R]
+T}@T{
+Submit line
+T}@T{
+T}
+T{
+\f[V]cancel()\f[R]
+T}@T{
+Cancel operation
+T}@T{
+T}
+T{
+\f[V]backspace()\f[R]
+T}@T{
+Delete character before cursor
+T}@T{
+T}
+T{
+\f[V]delete()\f[R]
+T}@T{
+Delete character after cursor
+T}@T{
+T}
+T{
+\f[V]clear()\f[R]
+T}@T{
+Clear text before cursor
+T}@T{
+T}
+T{
+\f[V]kill()\f[R]
+T}@T{
+Clear text after cursor
+T}@T{
+T}
+T{
+\f[V]clearWord()\f[R]
+T}@T{
+Delete word before cursor
+T}@T{
+T}
+T{
+\f[V]killWord()\f[R]
+T}@T{
+Delete word after cursor
+T}@T{
+T}
+T{
+\f[V]backward()\f[R]
+T}@T{
+Move cursor back by one character
+T}@T{
+T}
+T{
+\f[V]forward()\f[R]
+T}@T{
+Move cursor forward by one character
+T}@T{
+T}
+T{
+\f[V]prevWord()\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]nextWord()\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]begin()\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]end()\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]escape()\f[R]
+T}@T{
+Ignore keybindings for next character
+T}@T{
+T}
+T{
+\f[V]prevHist()\f[R]
+T}@T{
+Jump to the previous history entry
+T}@T{
+T}
+T{
+\f[V]nextHist()\f[R]
+T}@T{
+Jump to the next history entry
+T}@T{
+T}
+.TE
diff --git a/doc/cha-config.5 b/doc/cha-config.5
new file mode 100644
index 00000000..c5b71eba
--- /dev/null
+++ b/doc/cha-config.5
@@ -0,0 +1,1514 @@
+'\" t
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-config" "5" "" "" "Configuration of Chawan"
+.hy
+.SH Configuration of Chawan
+.PP
+Chawan supports configuration of various options like keybindings, user
+stylesheets, site preferences, etc.
+The configuration format is very similar to toml, with the following
+exceptions:
+.IP \[bu] 2
+Inline tables may span across multiple lines.
+.IP \[bu] 2
+Table arrays can be cleared by setting a variable by the same to the
+empty array.
+This allows users to disable default table array rules.
+.PP
+Example:
+.IP
+.nf
+\f[C]
+omnirule = [] # note: this must be placed at the beginning of the file.
+
+[[omnirule]] # this is legal. all default omni-rules are now disabled.
+\f[R]
+.fi
+.PP
+Chawan will look for a config file in the $XDG_CONFIG_HOME/chawan/
+directory called \f[V]config.toml\f[R].
+(Chawan defaults to \[ti]/.config if the XDG_CONFIG_HOME environment
+variable is not set.)
+See the default configuration file in the res/ folder, and bonus
+configuration files in the bonus/ folder for further examples.
+.SS Start
+.PP
+Start-up options are to be placed in the \f[V][start]\f[R] section.
+.PP
+Following is a list of start-up options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+visual-home
+T}@T{
+url
+T}@T{
+Page opened when Chawan is called with the -V option (and no other pages
+are passed as arguments.)
+T}@T{
+T}
+T{
+startup-script
+T}@T{
+JavaScript code
+T}@T{
+Script Chawan runs on start-up.
+Pages will not be loaded until this function exits.
+(Note however that asynchronous functions like setTimeout do not block
+loading.)
+T}@T{
+T}
+T{
+headless
+T}@T{
+boolean
+T}@T{
+Whether Chawan should always start in headless mode.
+Automatically enabled when Chawan is called with -r.
+T}@T{
+T}
+T{
+console-buffer
+T}@T{
+boolean
+T}@T{
+Whether Chawan should open a console buffer in non-headless mode.
+Defaults to true.
+Warning: this is only useful for debugging.
+Disabling this option without manually redirecting standard error will
+result in error messages randomly appearing on your screen.
+T}@T{
+T}
+.TE
+.SS Search
+.PP
+Search options are to be placed in the \f[V][search]\f[R] section.
+.PP
+Following is a list of search options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+wrap
+T}@T{
+boolean
+T}@T{
+When set to true, searchNext/searchPrev wraps around the document.
+T}@T{
+T}
+T{
+ignore-case
+T}@T{
+boolean
+T}@T{
+When set to true, document-wide searches are case-insensitive by
+default.
+Note: this can also be overridden inline in the search bar (vim-style),
+with the escape sequences \f[V]c\f[R] (ignore case) and \f[V]C\f[R]
+(strict case).
+See search mode for details.)
+T}@T{
+T}
+.TE
+.SS Encoding
+.PP
+Encoding options are to be placed in the \f[V][encoding]\f[R] section.
+.PP
+Following is a list of encoding options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+document-charset
+T}@T{
+array of charset label strings
+T}@T{
+List of character sets for loading documents.
+All listed character sets are enumerated until the document has been
+decoded without errors.
+In HTML, meta tags and the BOM may override this with a different
+charset, so long as the specified charset can decode the document
+correctly.
+T}@T{
+T}
+T{
+display-charset
+T}@T{
+string
+T}@T{
+Character set for keyboard input and displaying documents.
+Used in dump mode as well.
+(This means that e.g.\ \f[V]cha -I EUC-JP -O UTF-8 a > b\f[R] is
+equivalent to \f[V]iconv -f EUC-JP -t UTF-8\f[R].)
+T}@T{
+T}
+.TE
+.SS External
+.PP
+External options are to be placed in the \f[V][external]\f[R] section.
+.PP
+Following is a list of external options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+tmpdir
+T}@T{
+path
+T}@T{
+Directory used to save temporary files.
+T}@T{
+T}
+T{
+editor
+T}@T{
+shell command
+T}@T{
+External editor command.
+%s is substituted for the file name, %d for the line number.
+T}@T{
+T}
+T{
+mailcap
+T}@T{
+array of paths
+T}@T{
+Search path for mailcap files.
+(See \f[B]cha-mailcap\f[R](5) for details.)
+T}@T{
+T}
+T{
+mime-types
+T}@T{
+array of paths
+T}@T{
+Search path for mime.types files.
+(See \f[B]cha-mime.types\f[R](5) for details.)
+T}@T{
+T}
+T{
+cgi-dir
+T}@T{
+array of paths
+T}@T{
+Search path for local CGI scripts.
+(See \f[B]cha-localcgi\f[R](5) for details.)
+T}@T{
+T}
+T{
+urimethodmap
+T}@T{
+array of paths
+T}@T{
+Search path for urimethodmap files.
+(See \f[B]cha-urimethodmap\f[R](5) for details.)
+T}@T{
+T}
+T{
+w3m-cgi-compat
+T}@T{
+boolean
+T}@T{
+Enable local CGI compatibility with w3m.
+In short, it redirects \f[V]file:///cgi-bin/*\f[R] and
+\f[V]file:///$LIB/cgi-bin/*\f[R] to \f[V]cgi-bin:*\f[R].
+For further details, see \f[B]cha-localcgi\f[R](5).
+T}@T{
+T}
+T{
+download-dir
+T}@T{
+string
+T}@T{
+Path to pre-fill for \[lq]Save to:\[rq] prompts.
+This is not validated, you can set it to whatever you find useful.
+T}@T{
+T}
+.TE
+.SS Input
+.PP
+Input options are to be placed in the \f[V][input]\f[R] section.
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+vi-numeric-prefix
+T}@T{
+boolean
+T}@T{
+Whether vi-style numeric prefixes to commands should be accepted.
+When set to true, commands that return a function will be called with
+the numeric prefix as their first argument.
+Note: this only applies for keybindings defined in [page].
+T}@T{
+T}
+T{
+use-mouse
+T}@T{
+boolean
+T}@T{
+Whether Chawan is allowed to use the mouse.
+Currently, the default behavior imitates that of w3m.
+T}@T{
+T}
+.TE
+.PP
+Examples:
+.IP
+.nf
+\f[C]
+[input]
+vi-numeric-prefix = true
+
+[page]
+# Here, the arrow function will be called with the vi numbered prefix if
+# one was input, and with no argument otherwise.
+# The numeric prefix can never be zero, so it is safe to test for undefined
+# using the ternary operator.
+G = \[aq]n => n ? pager.gotoLine(n) : pager.cursorLastLine()\[aq]
+\f[R]
+.fi
+.SS Network
+.PP
+Network options are to be placed in the \f[V][network]\f[R] section.
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+max-redirect
+T}@T{
+number
+T}@T{
+Maximum number of redirections to follow.
+T}@T{
+T}
+T{
+prepend-scheme
+T}@T{
+string
+T}@T{
+Prepend this to URLs passed to Chawan without a scheme.
+Note that local files (\f[V]file:\f[R] scheme) will always be checked
+first; only if this fails, Chawan will retry the request with
+\f[V]prepend-scheme\f[R] set as the scheme.
+By default, this is set to \[lq]https://\[rq].
+Note that the \[lq]://\[rq] part is mandatory.
+T}@T{
+T}
+T{
+prepend-https
+T}@T{
+boolean
+T}@T{
+Deprecated: use prepend-scheme instead.
+When set to false, Chawan will act as if prepend-scheme were set to
+\[lq]\[lq].
+T}@T{
+T}
+T{
+proxy
+T}@T{
+URL
+T}@T{
+Specify a proxy for all network requests Chawan makes.
+All proxies supported by cURL may be used.
+Can be overridden by siteconf.
+T}@T{
+T}
+T{
+default-headers
+T}@T{
+Table
+T}@T{
+Specify a list of default headers for all HTTP(S) network requests.
+Can be overridden by siteconf.
+T}@T{
+T}
+.TE
+.SS Display
+.PP
+Display options are to be placed in the \f[V][display]\f[R] section.
+.PP
+Following is a list of display options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+color-mode
+T}@T{
+\[lq]monochrome\[rq] / \[lq]ansi\[rq] / \[lq]eight-bit\[rq] /
+\[lq]true-color\[rq] / \[lq]auto\[rq]
+T}@T{
+Set the color mode.
+\[lq]auto\[rq] for automatic detection, \[lq]monochrome\[rq] for black
+on white, \[lq]ansi\[rq] for ansi colors, \[lq]eight-bit\[rq] for
+256-color mode, and \[lq]true-color\[rq] for true colors.
+\[lq]8bit\[rq] is accepted as a legacy alias of \[lq]eight-bit\[rq].
+\[lq]24bit\[rq] is accepted as a legacy alias of \[lq]true-color\[rq].
+T}@T{
+T}
+T{
+format-mode
+T}@T{
+\[lq]auto\[rq] / [\[lq]bold\[rq], \[lq]italic\[rq], \[lq]underline\[rq],
+\[lq]reverse\[rq], \[lq]strike\[rq], \[lq]overline\[rq],
+\[lq]blink\[rq]]
+T}@T{
+Specifies output formatting modes.
+Accepts the string \[lq]auto\[rq] or an array of specific attributes.
+An empty array (\f[V][]\f[R]) disables formatting completely.
+T}@T{
+T}
+T{
+no-format-mode
+T}@T{
+[\[lq]bold\[rq], \[lq]italic\[rq], \[lq]underline\[rq],
+\[lq]reverse\[rq], \[lq]strike\[rq], \[lq]overline\[rq],
+\[lq]blink\[rq]]
+T}@T{
+Disable specified formatting modes.
+T}@T{
+T}
+T{
+emulate-overline
+T}@T{
+boolean
+T}@T{
+When set to true and the overline formatting attribute is not enabled,
+overlines are substituted by underlines on the previous line.
+T}@T{
+T}
+T{
+alt-screen
+T}@T{
+\[lq]auto\[rq] / boolean
+T}@T{
+Enable/disable the alternative screen.
+T}@T{
+T}
+T{
+highlight-color
+T}@T{
+color
+T}@T{
+Set the highlight color.
+Both hex values and CSS color names are accepted.
+T}@T{
+T}
+T{
+highlight-marks
+T}@T{
+boolean
+T}@T{
+Enable/disable highlighting of marks.
+T}@T{
+T}
+T{
+double-width-ambiguous
+T}@T{
+boolean
+T}@T{
+Assume the terminal displays characters in the East Asian Ambiguous
+category as double-width characters.
+Useful when e.g.\ \[ci] occupies two cells.
+T}@T{
+T}
+T{
+minimum-contrast
+T}@T{
+number
+T}@T{
+Specify the minimum difference between the luminance (Y) of the
+background and the foreground.
+-1 disables this function (i.e.\ allows black letters on black
+background, etc).
+T}@T{
+T}
+T{
+force-clear
+T}@T{
+boolean
+T}@T{
+Force the screen to be completely cleared every time it is redrawn.
+T}@T{
+T}
+T{
+set-title
+T}@T{
+boolean
+T}@T{
+Set the terminal emulator\[cq]s window title to that of the current
+page.
+T}@T{
+T}
+T{
+default-background-color
+T}@T{
+\[lq]auto\[rq] / color
+T}@T{
+Overrides the assumed background color of the terminal.
+\[lq]auto\[rq] leaves background color detection to Chawan.
+T}@T{
+T}
+T{
+default-foreground-color
+T}@T{
+\[lq]auto\[rq] / color
+T}@T{
+Sets the assumed foreground color of the terminal.
+\[lq]auto\[rq] leaves foreground color detection to Chawan.
+T}@T{
+T}
+T{
+query-da1
+T}@T{
+bool
+T}@T{
+Enable/disable querying Primary Device Attributes, and with it, all
+\[lq]dynamic\[rq] terminal querying.
+It is highly recommended not to alter the default value (which is true),
+or the output will most likely look horrible.
+(Except, obviously, if your terminal does not support Primary Device
+Attributes.)
+T}@T{
+T}
+T{
+columns, lines, pixels-per-column, pixels-per-line
+T}@T{
+number
+T}@T{
+Fallback values for the number of columns, lines, pixels per column, and
+pixels per line for the cases where it cannot be determined
+automatically.
+(For example, these values are used in dump mode.)
+T}@T{
+T}
+T{
+force-columns, force-lines, force-pixels-per-column,
+force-pixels-per-line
+T}@T{
+boolean
+T}@T{
+Force-set columns, lines, pixels per column, or pixels per line to the
+fallback values provided above.
+T}@T{
+T}
+.TE
+.SS Omnirule
+.PP
+The omni-bar (by default opened with C-l) can be used to perform
+searches using omni-rules.
+These are to be placed in the table array \f[V][[omnirule]]\f[R].
+.PP
+Examples:
+.IP
+.nf
+\f[C]
+# Search using DuckDuckGo Lite. (Bound to C-k by default.)
+[[omnirule]]
+match = \[aq]\[ha]ddg:\[aq]
+substitute-url = \[aq](x) => \[dq]https://lite.duckduckgo.com/lite/?kp=-1&kd=-1&q=\[dq] + encodeURIComponent(x.split(\[dq]:\[dq]).slice(1).join(\[dq]:\[dq]))\[aq]
+
+# Search using Wikipedia, Firefox-style.
+[[omnirule]]
+match = \[aq]\[ha]\[at]wikipedia\[aq]
+substitute-url = \[aq](x) => \[dq]https://en.wikipedia.org/wiki/Special:Search?search=\[dq] + encodeURIComponent(x.replace(/\[at]wikipedia/, \[dq]\[dq]))\[aq]
+\f[R]
+.fi
+.PP
+Omnirule options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+match
+T}@T{
+regex
+T}@T{
+Regular expression used to match the input string.
+Note that websites passed as arguments are matched as well.
+Note: regexes are handled according to the match mode regex handling
+rules.
+T}@T{
+T}
+T{
+substitute-url
+T}@T{
+JavaScript function
+T}@T{
+A JavaScript function Chawan will pass the input string to.
+If a new string is returned, it will be parsed instead of the old one.
+T}@T{
+T}
+.TE
+.SS Siteconf
+.PP
+Configuration options can be specified for individual sites.
+Entries are to be placed in the table array \f[V][[siteconf]]\f[R].
+.PP
+Examples:
+.IP
+.nf
+\f[C]
+# Enable cookies on the orange website for log-in.
+[[siteconf]]
+url = \[aq]https://news.ycombinator.com/.*\[aq]
+cookie = true
+
+# Redirect npr.org to text.npr.org.
+[[siteconf]]
+host = \[aq](www.)?npr.org\[aq]
+rewrite-url = \[aq]\[aq]\[aq]
+(x) => {
+x.host = \[dq]text.npr.org\[dq];
+const s = x.pathname.split(\[aq]/\[aq]);
+x.pathname = s.at(s.length > 2 ? -2 : 1);
+/* No need to return; URL objects are passed by reference. */
+}
+\[aq]\[aq]\[aq]
+
+# Allow cookie sharing on *sr.ht domains.
+[[siteconf]]
+host = \[aq](.*.)?sr.ht\[aq] # either \[aq]something.sr.ht\[aq] or \[aq]sr.ht\[aq]
+cookie = true # enable cookies
+share-cookie-jar = \[aq]sr.ht\[aq] # use the cookie jar of \[aq]sr.ht\[aq] for all matched hosts
+third-party-cookie = \[aq].*.sr.ht\[aq] # allow cookies from subdomains
+\f[R]
+.fi
+.PP
+Siteconf options:
+.PP
+.TS
+tab(@);
+lw(15.6n) lw(19.4n) lw(31.1n) lw(3.9n).
+T{
+Name
+T}@T{
+Value
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+url
+T}@T{
+regex
+T}@T{
+Regular expression used to match the URL.
+Either this or the \f[V]host\f[R] option must be specified.
+Note: regexes are handled according to the match mode regex handling
+rules.
+T}@T{
+T}
+T{
+host
+T}@T{
+regex
+T}@T{
+Regular expression used to match the host part of the URL (i.e.\ domain
+name/ip address.)
+Either this or the \f[V]url\f[R] option must be specified.
+Note: regexes are handled according to the match mode regex handling
+rules.
+T}@T{
+T}
+T{
+rewrite-url
+T}@T{
+JavaScript function
+T}@T{
+A JavaScript function Chawan will pass the URL to.
+If a new URL is returned, it will replace the old one.
+T}@T{
+T}
+T{
+cookie
+T}@T{
+boolean
+T}@T{
+Whether loading cookies should be allowed for this URL.
+By default, this is false for all websites.
+T}@T{
+T}
+T{
+third-party-cookie
+T}@T{
+array of regexes
+T}@T{
+Domains for which third-party cookies are allowed on this domain.
+Note: this only works for buffers which share the same cookie jar.
+Note: regexes are handled according to the match mode regex handling
+rules.
+T}@T{
+T}
+T{
+share-cookie-jar
+T}@T{
+host
+T}@T{
+Cookie jar to use for this domain.
+Useful for e.g.\ sharing cookies with subdomains.
+T}@T{
+T}
+T{
+referer-from
+T}@T{
+boolean
+T}@T{
+Whether or not we should send a Referer header when opening requests
+originating from this domain.
+Simplified example: if you click a link on a.com that refers to b.com,
+and referer-from is true, b.com is sent \[lq]a.com\[rq] as the Referer
+header.
+Defaults to false.
+T}@T{
+T}
+T{
+scripting
+T}@T{
+boolean
+T}@T{
+Enable/disable JavaScript execution on this site.
+T}@T{
+T}
+T{
+document-charset
+T}@T{
+boolean
+T}@T{
+Specify the default encoding for this site.
+Overrides document-charset in encoding.
+T}@T{
+T}
+T{
+stylesheet
+T}@T{
+CSS stylesheet
+T}@T{
+Specify an additional user-stylesheet for this site.
+Note: other user-stylesheets (specified under [css] or additional
+matching siteconfs) are not overridden.
+(In other words, they will be concatenated with this stylesheet to get
+the final user stylesheet.)
+T}@T{
+T}
+T{
+proxy
+T}@T{
+URL
+T}@T{
+Specify a proxy for network requests fetching contents of this buffer.
+T}@T{
+T}
+T{
+default-headers
+T}@T{
+Table
+T}@T{
+Specify a list of default headers for HTTP(S) network requests to this
+buffer.
+T}@T{
+T}
+.TE
+.SS Stylesheets
+.PP
+User stylesheets are to be placed in the \f[V][css]\f[R] section.
+.PP
+There are two ways to import user stylesheets:
+.IP "1." 3
+Include a user stylesheet using the format
+\f[V]include = \[aq]path-to-user.css\[aq]\f[R].
+To include multiple stylesheets, use
+\f[V]include = [\[aq]first-stylesheet.css, second-stylesheet.css\[aq]]\f[R].
+Relative paths are interpreted relative to the config directory.
+.IP "2." 3
+Place your stylesheet directly in your configuration file using
+\f[V]inline = \[dq]\[dq]\[dq]your-style\[dq]\[dq]\[dq]\f[R].
+.SS Keybindings
+.PP
+Keybindings are to be placed in these sections:
+.IP \[bu] 2
+for pager interaction: \f[V][page]\f[R]
+.IP \[bu] 2
+for line editing: \f[V][line]\f[R]
+.PP
+Keybindings are configured using the syntax
+.PP
+`' = `'
+.PP
+Where \f[V]<keybinding>\f[R] is a combination of unicode characters with
+or without modifiers.
+Modifiers are the prefixes \f[V]C-\f[R] and \f[V]M-\f[R], which add
+control or escape to the keybinding respectively (essentially making
+\f[V]M-\f[R] the same as \f[V]C-[\f[R]).
+Modifiers can be escaped with the \[ga]\[ga] sign.
+.PP
+\f[V]<action>\f[R] is either a command defined in the \f[V][cmd]\f[R]
+section, or a JavaScript expression.
+Here we only describe the pre-defined actions in the default config; for
+a description of the API, please see:
+.PP
+The API documentation at \f[B]cha-api\f[R](5).
+.PP
+Examples:
+.IP
+.nf
+\f[C]
+# show change URL when Control, Escape and j are pressed
+\[aq]C-M-j\[aq] = \[aq]cmd.pager.load\[aq]
+# go to the first line of the page when g is pressed twice without a preceding
+# number, or to the line when a preceding number is given.
+\[aq]gg\[aq] = \[aq]cmd.pager.gotoLineOrStart\[aq]
+\f[R]
+.fi
+.SS Browser actions
+.PP
+.TS
+tab(@);
+lw(21.5n) lw(43.1n) lw(5.4n).
+T{
+Name
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+\f[V]cmd.pager.quit\f[R]
+T}@T{
+Exit the browser.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.suspend\f[R]
+T}@T{
+Temporarily suspend the browser Note: this also suspends e.g.\ buffer
+processes or CGI scripts.
+So if you are downloading something, that will be delayed until you
+restart the process.
+T}@T{
+T}
+.TE
+.SS Pager actions
+.PP
+Note: \f[V]n\f[R] in the following text refers to a number preceding the
+action.
+e.g.
+in \f[V]10gg\f[R], n = 10.
+If no preceding number is input, then it is left unspecified.
+.PP
+.TS
+tab(@);
+lw(21.5n) lw(43.1n) lw(5.4n).
+T{
+Name
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+\f[V]cmd.pager.cursorUp\f[R]
+T}@T{
+Move the cursor upwards by n lines, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorDown\f[R]
+T}@T{
+Move the cursor downwards by n lines, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorLeft\f[R]
+T}@T{
+Move the cursor to the left by n cells, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorRight\f[R]
+T}@T{
+Move the cursor to the right by n cells, or if n is unspecified, by 1.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorLineBegin\f[R]
+T}@T{
+Move the cursor to the first cell of the line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorLineTextStart\f[R]
+T}@T{
+Move the cursor to the first non-blank character of the line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorLineEnd\f[R]
+T}@T{
+Move the cursor to the last cell of the line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorNextWord\f[R],
+\f[V]cmd.pager.cursorNextViWord\f[R],
+\f[V]cmd.pager.cursorNextBigWord\f[R]
+T}@T{
+Move the cursor to the beginning of the next word.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorPrevWord\f[R],
+\f[V]cmd.pager.cursorPrevViWord\f[R],
+\f[V]cmd.pager.cursorPrevBigWord\f[R]
+T}@T{
+Move the cursor to the end of the previous word.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorWordEnd\f[R], \f[V]cmd.pager.cursorViWordEnd\f[R],
+\f[V]cmd.pager.cursorBigWordEnd\f[R]
+T}@T{
+Move the cursor to the end of the current word, or if already there, to
+the end of the next word.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorWordBegin\f[R],
+\f[V]cmd.pager.cursorViWordBegin\f[R],
+\f[V]cmd.pager.cursorBigWordBegin\f[R]
+T}@T{
+Move the cursor to the beginning of the current word, or if already
+there, to the end of the previous word.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorPrevLink\f[R]
+T}@T{
+Move the cursor to the beginning of the previous clickable element.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorNextLink\f[R]
+T}@T{
+Move the cursor to the beginning of the next clickable element.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorPrevParagraph\f[R]
+T}@T{
+Move the cursor to the beginning of the nth next paragraph.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorNextParagraph\f[R]
+T}@T{
+Move the cursor to the end of the nth previous paragraph.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorRevNthLink\f[R]
+T}@T{
+Move the cursor to the nth link of the document, counting backwards from
+the document\[cq]s last line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorNthLink\f[R]
+T}@T{
+Move the cursor to the nth link of the document.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.pageUp\f[R], \f[V]cmd.pager.pageDown\f[R],
+\f[V]cmd.pager.pageLeft\f[R], \f[V]cmd.pager.pageRight\f[R]
+T}@T{
+Scroll up/down/left/right by n pages, or if n is unspecified, by one
+page.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.halfPageUp\f[R], \f[V]cmd.pager.halfPageDown\f[R],
+\f[V]cmd.pager.halfPageLeft\f[R], \f[V]pager.halfPageUp\f[R]
+T}@T{
+Scroll up/down/left/right by n half pages, or if n is unspecified, by
+one page.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.scrollUp\f[R], \f[V]cmd.pager.scrollDown\f[R],
+\f[V]cmd.pager.scrollLeft\f[R], \f[V]cmd.pager.scrollRight\f[R]
+T}@T{
+Scroll up/down/left/right by n lines, or if n is unspecified, by one
+line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.click\f[R]
+T}@T{
+Click the HTML element currently under the cursor.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.load\f[R]
+T}@T{
+Open the current address in the URL bar.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.webSearch\f[R]
+T}@T{
+Open the URL bar with an arbitrary search engine.
+At the moment, this is DuckDuckGo Lite.
+(Note: Chawan developers aren\[cq]t affiliated with DuckDuckGo the
+company or their product in any way.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.dupeBuffer\f[R]
+T}@T{
+Duplicate the current buffer by loading its source to a new buffer.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.discardBuffer\f[R]
+T}@T{
+Discard the current buffer, and move back to its previous sibling
+buffer, or if that doesn\[cq]t exist, to its parent.
+If the current buffer is a root buffer (i.e.\ it has no parent), move to
+the next sibling buffer instead.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.discardTree\f[R]
+T}@T{
+Discard all child buffers of the current buffer.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.reload\f[R]
+T}@T{
+Open a new buffer with the current buffer\[cq]s URL, replacing the
+current buffer.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.reshape\f[R]
+T}@T{
+Reshape the current buffer (=render the current page anew.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.redraw\f[R]
+T}@T{
+Redraw screen contents.
+Useful if something messed up the display.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.toggleSource\f[R]
+T}@T{
+If viewing an HTML buffer, open a new buffer with its source.
+Otherwise, open the current buffer\[cq]s contents as HTML.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorFirstLine\f[R], \f[V]cmd.pager.cursorLastLine\f[R]
+T}@T{
+Move to the beginning/end in the buffer.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorTop\f[R]
+T}@T{
+Move to the first line on the screen.
+(Equivalent to H in vi.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorMiddle\f[R]
+T}@T{
+Move to the line in the middle of the screen.
+(Equivalent to M in vi.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorBottom\f[R]
+T}@T{
+Move to the last line on the screen.
+(Equivalent to L in vi.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.raisePage\f[R], \f[V]cmd.pager.raisePageBegin\f[R],
+\f[V]cmd.pager.centerLine\f[R], \f[V]cmd.pager.centerLineBegin\f[R],
+\f[V]cmd.pager.lowerPage\f[R], \f[V]cmd.pager.lowerPageBegin\f[R]
+T}@T{
+If n is specified, move cursor to line n.\ Then, * \f[V]raisePage\f[R]
+scrolls down so that the cursor is on the top line of the screen.
+(vi \f[V]z<CR>\f[R], vim \f[V]zt\f[R].)
+* \f[V]centerLine\f[R] shifts the screen so that the cursor is in the
+middle of the screen.
+(vi \f[V]z.\f[R], vim \f[V]zz\f[R].)
+* \f[V]lowerPage\f[R] scrolls up so that the cursor is on the bottom
+line of the screen.
+(vi \f[V]z-\f[R], vim \f[V]zb\f[R].)
+The -\f[V]Begin\f[R] variants also move the cursor to the line\[cq]s
+first non-blank character, as the variants originating from vi do.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.nextPageBegin\f[R]
+T}@T{
+If n is specified, move to the screen before the nth line and raise the
+page.
+Otherwise, go to the previous screen\[cq]s last line and raise the page.
+(\f[V]z+\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.previousPageBegin\f[R]
+T}@T{
+If n is specified, move to the screen before the nth line and raise the
+page.
+Otherwise, go to the previous screen\[cq]s last line and raise the page.
+(\f[V]z+\f[R] in vi.)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.cursorLeftEdge\f[R],
+\f[V]cmd.pager.cursorMiddleColumn\f[R],
+\f[V]cmd.pager.cursorRightEdge\f[R]
+T}@T{
+Move to the first/middle/last column on the screen.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.centerColumn\f[R]
+T}@T{
+Center screen around the current column.
+(w3m \f[V]Z\f[R].)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.lineInfo\f[R]
+T}@T{
+Display information about the current line on the status line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.searchForward\f[R], \f[V]cmd.pager.searchBackward\f[R]
+T}@T{
+Search for a string in the current buffer, forwards or backwards.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.isearchForward\f[R], \f[V]cmd.pager.searchBackward\f[R]
+T}@T{
+Incremental-search for a string, highlighting the first result, forwards
+or backwards.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.gotoLineOrStart\f[R], \f[V]cmd.pager.gotoLineOrEnd\f[R]
+T}@T{
+If n is specified, jump to line n.\ Otherwise, jump to the start/end of
+the page.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.searchNext\f[R], \f[V]cmd.pager.searchPrev\f[R]
+T}@T{
+Jump to the nth (or if unspecified, first) next/previous search result.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.peek\f[R]
+T}@T{
+Display a message of the current buffer\[cq]s URL on the status line.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.peekCursor\f[R]
+T}@T{
+Display a message of the URL or title under the cursor on the status
+line.
+Multiple calls allow cycling through the two.
+(i.e.\ by default, press u once -> title, press again -> URL)
+T}@T{
+T}
+T{
+\f[V]cmd.pager.setMark\f[R]
+T}@T{
+Wait for a character \f[V]x\f[R] and then set a mark with the ID
+\f[V]x\f[R].
+T}@T{
+T}
+T{
+\f[V]cmd.pager.gotoMark\f[R], \f[V]cmd.pager.gotoMarkY\f[R]
+T}@T{
+Wait for a character \f[V]x\f[R] and then jump to the mark with the ID
+\f[V]x\f[R] (if it exists on the page).
+\f[V]gotoMark\f[R] sets both the X and Y positions; gotoMarkY only sets
+the Y position.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.markURL\f[R]
+T}@T{
+Convert URL-like strings to anchors on the current page.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.saveLink\f[R]
+T}@T{
+Save resource from the URL pointed to by the cursor to the disk.
+T}@T{
+T}
+T{
+\f[V]cmd.pager.saveSource\f[R]
+T}@T{
+Save the source of the current buffer to the disk.
+T}@T{
+T}
+.TE
+.SS Line-editing actions
+.PP
+.TS
+tab(@);
+lw(21.5n) lw(43.1n) lw(5.4n).
+T{
+Name
+T}@T{
+Function
+T}@T{
+T}
+_
+T{
+\f[V]cmd.line.submit\f[R]
+T}@T{
+Submit line
+T}@T{
+T}
+T{
+\f[V]cmd.line.cancel\f[R]
+T}@T{
+Cancel operation
+T}@T{
+T}
+T{
+\f[V]cmd.line.backspace\f[R]
+T}@T{
+Delete character before cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.delete\f[R]
+T}@T{
+Delete character after cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.clear\f[R]
+T}@T{
+Clear text before cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.kill\f[R]
+T}@T{
+Clear text after cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.clearWord\f[R]
+T}@T{
+Delete word before cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.killWord\f[R]
+T}@T{
+Delete word after cursor
+T}@T{
+T}
+T{
+\f[V]cmd.line.backward\f[R]
+T}@T{
+Move cursor back by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.forward\f[R]
+T}@T{
+Move cursor forward by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.prevWord\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.nextWord\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.begin\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.end\f[R]
+T}@T{
+Move cursor to the previous word by one character
+T}@T{
+T}
+T{
+\f[V]cmd.line.escape\f[R]
+T}@T{
+Ignore keybindings for next character
+T}@T{
+T}
+T{
+\f[V]cmd.line.prevHist\f[R]
+T}@T{
+Jump to the previous history entry
+T}@T{
+T}
+T{
+\f[V]cmd.line.nextHist\f[R]
+T}@T{
+Jump to the next history entry
+T}@T{
+T}
+.TE
+.PP
+Note: to facilitate URL editing, the line editor has a different
+definition of what a word is than the pager.
+For the line editor, a word is either a sequence of alphanumeric
+characters, or any single non-alphanumeric character.
+(This means that e.g.\ \f[V]https://\f[R] consists of four words:
+\f[V]https\f[R], \f[V]:\f[R], \f[V]/\f[R] and \f[V]/\f[R].)
+.IP
+.nf
+\f[C]
+# Control+A moves the cursor to the beginning of the line.
+\[aq]C-a\[aq] = \[aq]cmd.line.begin\[aq]
+
+# Escape+D deletes everything after the cursor until it reaches a word-breaking
+# character.
+\[aq]M-d\[aq] = \[aq]cmd.line.killWord\[aq]
+\f[R]
+.fi
+.SS Appendix
+.SS Regex handling
+.PP
+Regular expressions are currently handled using libregexp which is
+included in QuickJS.
+This means that all regular expressions work as in JavaScript.
+.PP
+There are two different modes of regex preprocessing in Chawan:
+\[lq]search\[rq] mode, and \[lq]match\[rq] mode.
+\[lq]match\[rq] mode is used for configurations (meaning in all values
+in this document described as \[lq]regex\[rq]).
+\[lq]search\[rq] mode is used for the on-page search function (using
+searchForward/isearchForward etc.)
+.SS Match mode
+.PP
+Regular expressions are assumed to be exact matches, except when they
+start with a caret (\[ha]) sign or end with an unescaped dollar ($)
+sign.
+.PP
+In other words, the following transformations occur:
+.IP
+.nf
+\f[C]
+\[ha]abcd -> \[ha]abcd (no change, only beginning is matched)
+efgh$ -> efgh$ (no change, only end is matched)
+\[ha]ijkl$ -> \[ha]ijkl$ (no change, the entire line is matched)
+mnop -> \[ha]mnop$ (changed to exact match, the entire line is matched)
+\f[R]
+.fi
+.PP
+Match mode has no way to toggle JavaScript regex flags like \f[V]i\f[R].
+.SS Search mode
+.PP
+For on-page search, the above transformations do not apply; the search
+\f[V]/abcd\f[R] searches for the string \f[V]abcd\f[R] inside all lines.
+.PP
+\[lq]Search\[rq] mode also has some other convenience transformations:
+.IP \[bu] 2
+The string \f[V]c\f[R] (backslash + lower-case c) inside a search-mode
+regex enables case-insensitive matching.
+.IP \[bu] 2
+Conversely, \f[V]C\f[R] (backslash + capital C) disables
+case-insensitive matching.
+(Useful if you have the \[lq]i\[rq] flag inside default-flags.)
+.IP \[bu] 2
+\f[V]<\f[R] and \f[V]>\f[R] is converted to \f[V]b\f[R] (as in vi, grep,
+etc.)
+.PP
+Note that none of these work in \[lq]match\[rq] mode.
+.SS Path handling
+.PP
+Rules for path handling are similar to how strings in the shell are
+handled.
+.IP \[bu] 2
+Tilde-expansion is used to determine the user\[cq]s home directory.
+So e.g.\ \f[V]\[ti]/whatever\f[R] works.
+.IP \[bu] 2
+Environment variables can be used like \f[V]$ENV_VAR\f[R].
+.IP \[bu] 2
+Relative paths are relative to the Chawan configuration directory.
+.PP
+Some non-external variables are also defined by Chawan.
+These can be accessed using the syntax \f[V]${%VARIABLE}\f[R]:
+.IP \[bu] 2
+\f[V]${%CHA_BIN_DIR}\f[R]: the directory which the \f[V]cha\f[R] binary
+resides in.
+Note that symbolic links are automatically resolved to determine this
+path.
+.IP \[bu] 2
+\f[V]${%CHA_LIBEXEC_DIR}\f[R]: the directory for all executables Chawan
+uses for operation.
+By default, this is \f[V]${%CHA_BIN_DIR}/../libexec/chawan\f[R].
+.SS Word types
+.PP
+Word-based pager commands can operate with different definitions of
+words.
+Currently, these are:
+.IP \[bu] 2
+w3m words
+.IP \[bu] 2
+vi words
+.IP \[bu] 2
+Big words
+.SS w3m word
+.PP
+A w3m word is a sequence of alphanumeric characters.
+Symbols are treated in the same way as whitespace.
+.SS vi word
+.PP
+A vi word is a sequence of alphanumeric characters, OR a sequence of
+symbols.
+.PP
+vi words may be separated by whitespace; however, symbolic and
+alphanumeric vi words do not have to be whitespace-separated.
+e.g.\ following character sequence contains two words:
+.IP
+.nf
+\f[C]
+hello[]+{}\[at]\[ga]!
+\f[R]
+.fi
+.SS Big word
+.PP
+A big word is a sequence of non-whitespace characters.
+.PP
+It is essentially the same as a w3m word, but with symbols being defined
+as non-whitespace.
+.SS See also
+.PP
+\f[B]cha\f[R](1)
diff --git a/doc/cha-localcgi.5 b/doc/cha-localcgi.5
new file mode 100644
index 00000000..c9a8a54e
--- /dev/null
+++ b/doc/cha-localcgi.5
@@ -0,0 +1,336 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-localcgi" "5" "" "" "Local CGI support in Chawan"
+.hy
+.SH Local CGI support in Chawan
+.PP
+Chawan supports the invocation of CGI scripts locally.
+This feature can be used in the following way:
+.IP \[bu] 2
+All local CGI scripts must be placed in a directory specified in
+\f[V]external.cgi-dir\f[R].
+Multiple directories can be specified in an array too, and directories
+specified first have higher precedence.
+.IP \[bu] 2
+Then, a CGI script in one of these directories can be executed by
+visiting the URL \f[V]cgi-bin:script-name\f[R].
+$PATH_INFO and $QUERY_STRING are set as normal,
+i.e.\ \f[V]cgi-bin:script-name/abcd?defgh=ijkl\f[R] will set $PATH_INFO
+to \f[V]/abcd\f[R], and $QUERY_STRING to \f[V]defgh=ijkl\f[R].
+.PP
+Further notes on processing CGI paths:
+.IP \[bu] 2
+The URL must be opaque, so you must not add a double slash after the
+scheme.
+e.g.\ \f[V]cgi-bin://script-name\f[R] will NOT work, only
+\f[V]cgi-bin:script-name\f[R].
+.IP \[bu] 2
+Paths beginning with \f[V]/cgi-bin/\f[R] or \f[V]/$LIB/\f[R] are
+stripped of this segment automatically.
+So e.g.\ \f[V]cgi-bin:/cgi-bin/script-name\f[R] becomes
+\f[V]cgi-bin:script-name\f[R].
+.IP \[bu] 2
+If \f[V]external.w3m-cgi-compat\f[R] is true, file: URLs are converted
+to cgi-bin: URLs if the path name starts with \f[V]/cgi-bin/\f[R],
+\f[V]/$LIB/\f[R], or the path of a local CGI script.
+Note: this is unsafe, please do not use it unless you must.
+.IP \[bu] 2
+Absolute paths are accepted as
+e.g.\ \f[V]cgi-bin:/path/to/cgi/dir/script-name\f[R].
+Note however, that this only works if \f[V]/path/to/cgi/dir\f[R] has
+already been specified as a CGI directory in \f[V]external.cgi-dir\f[R].
+.PP
+Note that this is different from w3m\[cq]s cgi-bin functionality, in
+that we use a custom scheme for local CGI instead of interpreting all
+requests to a designated path as a CGI request.
+(This incompatibility is bridged over when
+\f[V]external.w3m-cgi-compat\f[R] is true.)
+.SS Headers
+.PP
+Local CGI scripts may send some headers that Chawan will interpret
+specially (and thus will not pass forward to e.g.\ the fetch API, etc):
+.IP \[bu] 2
+\f[V]Status\f[R]: interpreted as the HTTP status code.
+.IP \[bu] 2
+\f[V]Cha-Control\f[R]: special header, see below.
+.PP
+Note that these headers MUST be sent before any regular headers.
+Headers received after a regular header or a
+\f[V]Cha-Control: ControlDone\f[R] header will be treated as regular
+headers.
+.PP
+The \f[V]Cha-Control\f[R] header\[cq]s value is parsed as follows:
+.IP
+.nf
+\f[C]
+Cha-Control-Value = Command *Parameter
+Command = ALPHA *ALPHA
+Parameter = SPACE *CHAR
+\f[R]
+.fi
+.PP
+In other words, it is \f[V]Command [Param1] [Param2] ...\f[R].
+.PP
+Currently available commands are:
+.IP \[bu] 2
+\f[V]Connected\f[R]: Takes no parameters.
+Must be the first reported header; it means that connection to the
+server has been successfully established, but no data has been received
+yet.
+When any other header is sent first, Chawan will act as if a
+\f[V]Cha-Control: Connected\f[R] header had been implicitly sent before
+that.
+.IP \[bu] 2
+\f[V]ConnectionError\f[R]: Must be the first reported header.
+Parameter 1 is the error code, see below.
+If any following parameters are given, they are concatenated to form a
+custom error message.
+Note: short but descriptive error messages are preferred, messages that
+do not fit on the screen are currently truncated.
+(TODO fix this somehow :P)
+.IP \[bu] 2
+\f[V]ControlDone\f[R]: Signals that no more special headers will be
+sent; this means that \f[V]Cha-Control\f[R] and \f[V]Status\f[R] headers
+sent after this must be interpreted as regular headers (and thus
+e.g.\ will be available for JS code calling the script using the fetch
+API).
+WARNING: this header must be sent before any non-hardcoded headers that
+take external input.
+For example, an HTTP client would have to send
+\f[V]Cha-Control: ControlDone\f[R] before returning the retrieved
+headers.
+.PP
+List of public error codes:
+.IP \[bu] 2
+\f[V]1 internal error\f[R]: An internal error prevented the script from
+retrieving the requested resource.
+CGI scripts can also use this to signal that they have no information on
+what went wrong.
+.IP \[bu] 2
+\f[V]2 invalid method\f[R]: The client requested data using a method not
+supported by this protocol.
+.IP \[bu] 2
+\f[V]3 invalid URL\f[R]: The request URL could not be interpreted as a
+valid URL for this format.
+.IP \[bu] 2
+\f[V]4 file not found\f[R]: No file was found at the requested address,
+and thus the request is meaningless.
+Note: this should only be used by protocols that do not rely on a
+client-server architecture, e.g.\ local file access, local databases, or
+peer-to-peer file retrieval mechanisms.
+A server responding with \[lq]no file found\[rq] is NOT a connection
+error, and is better represented as a response with a 404 status code.
+.IP \[bu] 2
+\f[V]5 failed to resolve host\f[R]: The hostname could not be resolved.
+.IP \[bu] 2
+\f[V]6 failed to resolve proxy\f[R]: The proxy could not be resolved.
+.IP \[bu] 2
+\f[V]7 connection refused\f[R]: The server refused to establish a
+connection.
+.IP \[bu] 2
+\f[V]8 proxy refused to connect\f[R]: The proxy refused to establish a
+connection.
+.SS Environment variables
+.PP
+Chawan sets the following environment variables:
+.IP \[bu] 2
+\f[V]SERVER_SOFTWARE=\[dq]Chawan\[dq]\f[R]
+.IP \[bu] 2
+\f[V]SERVER_PROTOCOL=\[dq]HTTP/1.0\[dq]\f[R]
+.IP \[bu] 2
+\f[V]SERVER_NAME=\[dq]localhost\[dq]\f[R]
+.IP \[bu] 2
+\f[V]SERVER_PORT=\[dq]80\[dq]\f[R]
+.IP \[bu] 2
+\f[V]REMOTE_HOST=\[dq]localhost\[dq]\f[R]
+.IP \[bu] 2
+\f[V]REMOTE_ADDR=\[dq]127.0.0.1\[dq]\f[R]
+.IP \[bu] 2
+\f[V]GATEWAY_INTERFACE=\[dq]CGI/1.1\[dq]\f[R]
+.IP \[bu] 2
+\f[V]SCRIPT_NAME=\[dq]/cgi-bin/script-name\[dq]\f[R] if called with a
+relative path, and \f[V]\[dq]/path/to/script/script-name\[dq]\f[R] if
+called with an absolute path.
+.IP \[bu] 2
+\f[V]SCRIPT_FILENAME=\[dq]/path/to/script/script-name\[dq]\f[R]
+.IP \[bu] 2
+\f[V]QUERY_STRING=\f[R] the query string (i.e.\ \f[V]URL.search\f[R]).
+Note that this variable is percent-encoded.
+.IP \[bu] 2
+\f[V]PATH_INFO=\f[R] everything after the script\[cq]s path name,
+e.g.\ for \f[V]cgi-bin:script-name/abcd/efgh\f[R]
+\f[V]\[dq]/abcd/efgh\[dq]\f[R].
+Note that this variable is NOT percent-encoded.
+.IP \[bu] 2
+\f[V]REQUEST_URI=\[dq]$SCRIPT_NAME/$PATH_INFO?$QUERY_STRING\f[R]
+.IP \[bu] 2
+\f[V]REQUEST_METHOD=\f[R] HTTP method used for making the request,
+e.g.\ GET or POST
+.IP \[bu] 2
+\f[V]REQUEST_HEADERS=\f[R] A newline-separated list of all headers for
+this request.
+.IP \[bu] 2
+\f[V]CHA_LIBEXEC_DIR=\f[R] The libexec directory Chawan was configured
+to use at compile time.
+See the tools section below for details of why this is useful.
+.IP \[bu] 2
+\f[V]CONTENT_TYPE=\f[R] for POST requests, the Content-Type header.
+Not set for other request types (e.g.\ GET).
+.IP \[bu] 2
+\f[V]CONTENT_LENGTH=\f[R] the content length, if $CONTENT_TYPE has been
+set.
+.IP \[bu] 2
+\f[V]ALL_PROXY=\f[R] if a proxy has been set, the proxy URL.
+WARNING: for security reasons, this MUST be respected when making
+external connections.
+If a CGI script does not support proxies, it must never make any
+external connections when the \f[V]ALL_PROXY\f[R] variable is set, even
+if this results in it returning an error.
+.IP \[bu] 2
+\f[V]HTTP_COOKIE=\f[R] if set, the Cookie header.
+.IP \[bu] 2
+\f[V]HTTP_REFERER=\f[R] if set, the Referer header.
+.PP
+For requests originating from a urimethodmap rewrite, Chawan will also
+set the parsed URL\[cq]s parts as environment variables.
+Use of these is highly encouraged, to avoid exploits originating from
+double-parsing of URLs.
+.PP
+e.g.\ if
+example://username:password\[at]example.org:1234/path/name.html?example
+is the original URL, then:
+.IP \[bu] 2
+\f[V]MAPPED_URI_SCHEME=\f[R] the scheme of the original URL, in this
+case \f[V]example\f[R].
+.IP \[bu] 2
+\f[V]MAPPED_URI_USERNAME=\f[R] the username part, in this case
+\f[V]username\f[R].
+If no username was specified, the variable is set to the empty string.
+.IP \[bu] 2
+\f[V]MAPPED_URI_PASSWORD=\f[R] the password part, in this case
+\f[V]password\f[R].
+If no password was specified, the variable is set to the empty string.
+.IP \[bu] 2
+\f[V]MAPPED_URI_HOST=\f[R] the host part, in this case
+\f[V]host.org\f[R] If no host was specified, the variable is set to the
+empty string.
+(An example of a URL with no host: \f[V]about:blank\f[R], here
+\f[V]blank\f[R] is the path name.)
+.IP \[bu] 2
+\f[V]MAPPED_URI_PORT=\f[R] the port, in this case \f[V]1234\f[R].
+If no port was specified, the variable is set to the empty string.
+(In this case, the CGI script is expected to use the default port for
+the scheme, if any.)
+.IP \[bu] 2
+\f[V]MAPPED_URI_PATH=\f[R] the path name, in this case
+\f[V]/path/name.html?example\f[R].
+If no path was specified, the variable is set to the empty string.
+Note: the path name is percent-encoded.
+.IP \[bu] 2
+\f[V]MAPPED_URI_QUERY=\f[R] the query string, in this case
+\f[V]example\f[R].
+Note that, unlike in JavaScript, no question mark is prepended to the
+string.
+The query string is percent-encoded as well.
+.PP
+Note: the fragment part is omitted intentionally.
+.SS Request body
+.PP
+If the request body is not empty, it is streamed into the program
+through the standard input.
+.PP
+Note that this may be both an application/x-www-form-urlencoded or a
+multipart/form-data request; \f[V]CONTENT_TYPE\f[R] stores information
+about the request type, and in case of a multipart request, the boundary
+as well.
+.SS Tools
+.PP
+Chawan provides certain helper binaries that may be useful for CGI
+scripts.
+These can be portably accessed by executing
+\f[V]\[dq]$CHA_LIBEXEC_DIR\[dq]/[program name]\f[R].
+.PP
+Currently, the following tools are available:
+.IP \[bu] 2
+\f[V]urldec\f[R]: percent-decode strings passed on standard input.
+.IP \[bu] 2
+\f[V]urlenc\f[R]: percent-encode strings passed on standard input,
+taking a percent-encode set as the first parameter.
+.SS Troubleshooting
+.PP
+Note that standard error is redirected to the browser console (by
+default, M-cM-c).
+This makes it easy to debug a misbehaving CGI script, but may also slow
+down the browser in case of excessive logging.
+If this is not the desired behavior, we recommend wrapping your script
+into a shell script that redirects stderr to /dev/null.
+.SS My script is returning a \[lq]no local-CGI directory configured\[rq] error message.
+.PP
+Currently, the default setting includes a cgi-bin directory at
+\f[V]$(which cha)/../libexec/chawan/cgi-bin\f[R], which usually looks
+something like \f[V]/usr/local/libexec/chawan/cgi-bin\f[R].
+You only get the above message if you intentionally set the cgi-dir
+setting to an empty array.
+(This will likely break everything else too, so do not.)
+.PP
+To change the default local-CGI directory, use the
+\f[V]external.cgi-dir\f[R] option.
+.PP
+e.g.\ you could add this to your config.toml:
+.IP
+.nf
+\f[C]
+[external]
+cgi-dir = [\[dq]\[ti]/cgi-bin\[dq], \[dq]${%CHA_LIBEXEC_DIR}/cgi-bin\[dq]]
+\f[R]
+.fi
+.PP
+and then put your script in \f[V]$HOME/cgi-bin\f[R].
+Note the second element in the array; if you don\[cq]t add it, the
+default CGI scripts (including http, https, etc\&...)
+will not work.
+.SS My script is returning a \[lq]Failed to execute script\[rq] error message.
+.PP
+This means the \f[V]execl\f[R] call to the script failed.
+Make sure that your CGI script\[cq]s executable bit is set, i.e.\ run
+\f[V]chmod +x /path/to/cgi/script\f[R].
+.SS My script is returning an \[lq]invalid CGI path\[rq] error message.
+.PP
+Make sure that you did not include leading slashes.
+Reminder: \f[V]cgi-bin://script-name\f[R] does not work, use
+\f[V]cgi-bin:script-name\f[R].
+.SS My script is returning a \[lq]CGI file not found\[rq] error message.
+.PP
+Double check that your CGI script is in the correct location.
+Also, make sure that you are not accidentally calling the script with an
+absolute path via \f[V]cgi-bin:/script-name\f[R] (instead of the correct
+\f[V]cgi-bin:script-name\f[R]).
+.PP
+It is also possible that \f[V]external.cgi-dir\f[R] is not really set to
+the directory your script is in.
+Note that by default, this depends on the binary\[cq]s path, so e.g.\ if
+your binary is in \f[V]\[ti]/src/chawan/target/release/bin/cha\f[R], but
+you put your CGI script to \f[V]/usr/local/libexec/chawan/cgi-bin\f[R],
+then it will not work.
+.SS My script is returning a \[lq]failed to set up CGI script\[rq] error message.
+.PP
+This means that either \f[V]pipe\f[R] or \f[V]fork\f[R] failed.
+Something strange is going on with your system; we recommend exorcism.
+(Maybe you are running out of memory?)
+.SS See also
+.PP
+\f[B]cha\f[R](1)
diff --git a/doc/cha-mailcap.5 b/doc/cha-mailcap.5
new file mode 100644
index 00000000..9972b63a
--- /dev/null
+++ b/doc/cha-mailcap.5
@@ -0,0 +1,160 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-mailcap" "5" "" "" "Mailcap support in Chawan"
+.hy
+.SH Mailcap
+.PP
+Chawan\[cq]s buffers can only handle HTML and plain text.
+To make Chawan recognize other file formats, the mailcap file format can
+be used.
+.PP
+Note that Chawan\[cq]s default mime.types file only recognizes a few
+file extensions, which may result in your entries not being executed.
+Please consult the \f[B]cha-mime.types\f[R](5) documentation for
+details.
+.PP
+For an exact description of the mailcap format, see RFC
+1524 (https://www.rfc-editor.org/rfc/rfc1524).
+.SS Search path
+.PP
+The search path for mailcap files can be overridden using the
+configuration variable \f[V]external.mailcap\f[R].
+.PP
+By default, the only file checked by Chawan is \f[V]$HOME/.mailcap\f[R].
+.PP
+In the past, the full path from the specification was used.
+This was changed because mailcap files shipped with various systems are
+usually incompatible with the assumptions Chawan makes about mailcap
+file contents.
+You can restore the old/standard-recommended behavior by adding this to
+your config.toml:
+.IP
+.nf
+\f[C]
+mailcap = [
+\[dq]\[ti]/.mailcap\[dq],
+\[dq]/etc/mailcap\[dq],
+\[dq]/usr/etc/mailcap\[dq],
+\[dq]/usr/local/etc/mailcap\[dq]
+]
+\f[R]
+.fi
+.SS Format
+.PP
+Chawan tries to adhere to the format described in RFC 1524, with a few
+extensions.
+.SS Templating
+.PP
+%s, %t works as described in the standard.
+However, named content type fields (%{\&...}) only work with %{charset}
+as of now.
+(TODO: fix this.)
+.PP
+If no quoting is applied, Chawan will quote the templates automatically.
+(This works with $(command substitutions) as well.)
+.PP
+DEPRECATED:
+.PP
+Also, the non-standard template %u may be specified to get the original
+URL of the resource.
+Note that this is no longer recommended; instead, use the $MAILCAP_URL
+environment variable which is set to the same value before the execution
+of every mailcap command.
+.SS Fields
+.PP
+The \f[V]test\f[R], \f[V]nametemplate\f[R], \f[V]needsterminal\f[R] and
+\f[V]copiousoutput\f[R] fields are recognized.
+Additionally, the non-standard \f[V]x-htmloutput\f[R] and
+\f[V]x-ansioutput\f[R] extension fields are recognized too.
+.IP \[bu] 2
+When the \f[V]test\f[R] named field is specified, the mailcap entry is
+only used if the test command returns 0.
+Warning: as of now, \f[V]%s\f[R] does not work with \f[V]test\f[R];
+\f[V]test\f[R] named fields with a \f[V]%s\f[R] template are skipped.
+Additionally, no data is piped into \f[V]test\f[R] either.
+.IP \[bu] 2
+\f[V]copiousoutput\f[R] makes Chawan redirect the output of the external
+command into a new buffer.
+If either x-htmloutput or x-ansioutput is defined too, then it is
+ignored.
+.IP \[bu] 2
+The \f[V]x-htmloutput\f[R] extension field behaves the same as
+\f[V]copiousoutput\f[R], but makes Chawan interpret the command\[cq]s
+output as HTML.
+.IP \[bu] 2
+\f[V]x-ansioutput\f[R] makes Chawan pipe the output through the default
+\[lq]text/x-ansi\[rq] content type handler.
+This means that you get colors, formatting, etc.
+displayed with ANSI escape sequences.
+.IP \[bu] 2
+\f[V]needsterminal\f[R] hands over control of the terminal to the
+command while it is running.
+Note: as of now, \f[V]needsterminal\f[R] does nothing if either
+\f[V]copiousoutput\f[R] or \f[V]x-htmloutput\f[R] is specified.
+.IP \[bu] 2
+For a description of \f[V]nametemplate\f[R], see the RFC.
+Note however, that it does not work with test (since %s is not supported
+there).
+.SS Environment variables
+.PP
+As noted above, the $MAILCAP_URL variable is set to the URL of the
+target resource before the execution of the mailcap command.
+Backwards compatibility with mailcap agents that do not support this
+variable can be achieved through shell substitution,
+e.g.\ ${MAILCAP_URL:-string for when it is unsupported}.
+.PP
+Note that it is not recommended to set %s as the fallback, because it
+will force Chawan to download the entire file before displaying it even
+if it could have been piped into the command.
+.SS Note
+.PP
+Entries with a content type of text/html or text/plain are ignored.
+.SS Examples
+.IP
+.nf
+\f[C]
+# Note: these examples require an entry in mime.types that sets e.g. md as
+# the markdown content type.
+
+# Handle markdown files using pandoc.
+text/markdown; pandoc - -f markdown -t html -o -; x-htmloutput
+
+# Show syntax highlighting for JavaScript source files using bat.
+text/javascript; bat -f -l es6 --file-name ${MAILCAP_URL:-STDIN} -; x-ansioutput
+
+# Play music using mpv, and hand over control of the terminal until mpv exits.
+audio/*; mpv -; needsterminal
+
+# Play videos using mpv in the background, redirecting its standard output
+# and standard error to /dev/null.
+video/*; mpv -
+
+# Open docx files using LibreOffice Writer.
+application/vnd.openxmlformats-officedocument.wordprocessingml.document;lowriter %s
+# (Wow that was ugly.)
+
+# Display manpages using pandoc. (Make sure the mime type matches the one
+# set in your mime.types file for extensions .1, .2, .3, ...)
+application/x-troff-man;pandoc - -f man -t html -o -; x-htmloutput
+
+# Following entry will be ignored, as text/html is supported natively by Chawan.
+text/html; cha -dT text/html -I %{charset}; copiousoutput
+\f[R]
+.fi
+.SS See also
+.PP
+\f[B]cha\f[R](1)
diff --git a/doc/cha-mime.types.5 b/doc/cha-mime.types.5
new file mode 100644
index 00000000..c0f9aa14
--- /dev/null
+++ b/doc/cha-mime.types.5
@@ -0,0 +1,72 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-config" "5" "" "" "MIME type detection in Chawan"
+.hy
+.SH mime.types
+.PP
+Chawan uses the mime.types file to recognize certain file extensions for
+matching mailcap entries.
+See the \f[B]cha-mailcap\f[R](5) documentation for a description of
+mailcap.
+.SS Search path
+.PP
+Chawan parses all mime.types files defined in
+\f[V]external.mime-types\f[R].
+If no mime.types file was found, the built-in mime type associations are
+used.
+.PP
+The default search path for mime.types files is:
+.IP
+.nf
+\f[C]
+$HOME/.mime.types:/etc/mime.types:/usr/etc/mime.types:/usr/local/etc/mime.types
+\f[R]
+.fi
+.SS Format
+.PP
+The mime.types file is a list of whitespace-separated columns.
+The first column represents the mime type, all following columns are
+file extensions.
+.PP
+Lines starting with a hash character (#) are recognized as comments, and
+are ignored.
+.PP
+Example:
+.IP
+.nf
+\f[C]
+# comment
+application/x-example   exmpl   ex
+\f[R]
+.fi
+.PP
+This mime.types file would register the file extensions \[lq]exmpl\[rq]
+and \[lq]ex\[rq] to be recognized as the mime type
+\f[V]application/x-example\f[R].
+.SS Note
+.PP
+Chawan only uses mime.types files for finding mailcap entries; buffers
+use an internal mime.types file for content type detection instead.
+.PP
+The default mime.types file only includes file formats that buffers can
+handle, which is rather limited (at the time of writing, 7 file
+formats).
+Therefore it is highly recommended to configure at least one external
+mime.types file if you use mailcap.
+.SS See also
+.PP
+\f[B]cha\f[R](1)
diff --git a/doc/cha-protocols.5 b/doc/cha-protocols.5
new file mode 100644
index 00000000..202fd991
--- /dev/null
+++ b/doc/cha-protocols.5
@@ -0,0 +1,283 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-protocols" "5" "" "" "Protocol support in Chawan"
+.hy
+.SH Protocols
+.PP
+Chawan supports downloading resources from various protocols: HTTP, FTP,
+Gopher, Gemini, and Finger.
+Details on these protocols, and information on how users can add support
+to their preferred protocols is outlined in this document.
+.SS HTTP
+.PP
+HTTP/s support is based on libcurl; supported features largely depend on
+your libcurl version.
+The adapter is found at \f[V]adapter/protocol/http.nim\f[R].
+.PP
+The libcurl HTTP adapter can take arbitrary headers and POST data, is
+able to use passed userinfo data
+(\f[V]https://username:password\[at]example.org\f[R]), and returns all
+headers and response body it receives from libcurl without exception.
+.PP
+It is possible to build these adapters using
+curl-impersonate (https://github.com/lwthiker/curl-impersonate) by
+setting the compile-time variable CURLLIBNAME to
+\f[V]libcurl-impersonate.so\f[R].
+Note that for curl-impersonate to work, you must set
+\f[V]network.default-headers = {}\f[R] in the Chawan config.
+(Otherwise, the libcurl adapter will happily override curl-impersonate
+headers, which is probably not what you want.)
+.PP
+The \f[V]bonus/libfetch\f[R] directory contains an alternative HTTP
+client, which is based on FreeBSD libfetch.
+It is mostly a proof of concept, as FreeBSD libfetch HTTP support is
+very limited; in particular, it does not support HTTP headers (beyond
+some basic request headers), so e.g.\ cookies will not work.
+.SS FTP
+.PP
+Chawan supports FTP through the \f[V]adapter/protocol/ftp.nim\f[R]
+libcurl adapter.
+For directory listings, it assumes UNIX output style, and will probably
+break horribly on receiving anything else.
+Otherwise, the directory listing view is identical to the file://
+directory listing.
+.PP
+SFTP \[lq]works\[rq] too, but YMMV.
+Note that if an IdentityFile declaration is found in your ssh config,
+then it will prompt for the identity file password, but there is no way
+to tell whether it is really asking for that.
+Also, settings covered by the Match field are ignored.
+.PP
+In theory, FTPS should work too, but it is completely untested.
+.SS Gopher
+.PP
+Gopher is supported through the \f[V]adapter/protocol/gopher.nim\f[R]
+libcurl adapter.
+Gopher directories are passed as the \f[V]text/gopher\f[R] type, and
+\f[V]adapter/format/gopher.nim\f[R] takes care of converting this to
+HTML.
+.PP
+Gopher selector types are converted to MIME types when possible; note
+however, that this is very limited, as most of them (like \f[V]s\f[R]
+sound, or \f[V]I\f[R] image) cannot be unambiguously converted without
+some other sniffing method.
+Chawan will fall back to extension-based detection in these cases, and
+in the worst case may end up with \f[V]application/octet-stream\f[R].
+.SS Gemini
+.PP
+Chawan\[cq]s gemini adapter (in \f[V]adapter/protocol/gmifetch.c\f[R])
+is a C program.
+It requires OpenSSL to work.
+.PP
+Currently, it still has some limitations:
+.IP \[bu] 2
+It does not support proxies yet.
+.IP \[bu] 2
+It does not support sites that require private key authentication.
+.PP
+\f[V]adapter/format/gmi2html.nim\f[R] is its companion program to
+convert the \f[V]text/gemini\f[R] file format to HTML.
+Note that the gemtext specification insists on line breaks being
+visually significant, and forbids their collapsing onto a single line;
+gmi2html respects this.
+However, inline whitespace is still collapsed outside of preformatted
+blocks.
+.SS Finger
+.PP
+Finger is supported through the \f[V]adapter/protocol/cha-finger\f[R]
+shell script.
+It is implemented as a shell script because of the protocol\[cq]s
+simplicity.
+cha-finger uses the \f[V]curl\f[R] program\[cq]s telnet:// protocol to
+make requests.
+As such, it will not work if \f[V]curl\f[R] is not installed.
+.PP
+Aspiring protocol adapter writers are encouraged to study cha-finger for
+a simple example of how a custom protocol handler could be written.
+.SS Spartan
+.PP
+Spartan is a protocol similar to Gemini, but without TLS.
+It is supported through the \f[V]adapter/protocol/spartan\f[R] shell
+script, which uses \f[V]nc\f[R] to make requests.
+.PP
+Spartan has the very strange property of extending gemtext with a
+protocol-specific line type.
+This is sort of supported through a sed filter for gemtext outputs in
+the CGI script (in other words, no modification to gmi2html was done to
+support this).
+.SS Local schemes: file:, about:, man:, data:
+.PP
+While these are not necessarily \f[I]protocols\f[R], they are
+implemented similarly to the protocols listed above (and thus can also
+be replaced, if the user wishes; see below).
+.PP
+\f[V]file:\f[R] loads a file from the local filesystem.
+In case of directories, it shows the directory listing like the FTP
+protocol does.
+.PP
+\f[V]about:\f[R] contains informational pages about the browser.
+At the time of writing, the following pages are available:
+\f[V]about:chawan\f[R], \f[V]about:blank\f[R] and
+\f[V]about:license\f[R].
+.PP
+\f[V]man:\f[R], \f[V]man-k:\f[R] and \f[V]man-l:\f[R] are wrappers
+around the commands \f[V]man\f[R], \f[V]man -k\f[R] and
+\f[V]man -l\f[R].
+These look up man pages using \f[V]/usr/bin/man\f[R] and turn on-page
+references into links.
+A wrapper command \f[V]mancha\f[R] also exists; this has an interface
+similar to \f[V]man\f[R].
+Note: this used to be based on w3mman2html.cgi, but it has been
+rewritten in Nim (and therefore no longer depends on Perl either).
+.PP
+\f[V]data:\f[R] decodes a data URL as defined in RFC 2397.
+.SS Internal schemes: cgi-bin:, stream:, cache:
+.PP
+Three internal protocols exist: \f[V]cgi-bin:\f[R], \f[V]stream:\f[R]
+and \f[V]cache:\f[R].
+These are the basic building blocks for the implementation of every
+protocol mentioned above; for this reason, these can \f[I]not\f[R] be
+replaced, and are implemented in the main browser binary.
+.PP
+\f[V]cgi-bin:\f[R] executes a local CGI script.
+This scheme is used for the actual implementation of the non-internal
+protocols mentioned above.
+Local CGI scripts can also be used to implement wrappers of other
+programs inside Chawan (e.g.\ dictionaries).
+.PP
+\f[V]stream:\f[R] is used for reading in streams returned by external
+programs or passed to Chawan via standard input.
+It differs from \f[V]cgi-bin:\f[R] in that it does not cooperate with
+the external process, and that the loader does not keep track of where
+the stream originally comes from.
+Therefore it is suitable for reading in the output of mailcap entries,
+or for turning stdin into a URL.
+.PP
+Since Chawan does not keep track of the origin of \f[V]stream:\f[R]
+URLs, it is not possible to reload them.
+(For that matter, reloading stdin does not make much sense anyway.)
+To support rewinding and \[lq]view source\[rq], the output of
+\f[V]stream:\f[R]\[cq]s is stored in a temporary file until the buffer
+is discarded.
+.PP
+\f[V]cache:\f[R] is not something an end user would normally see;
+it\[cq]s used for rewinding or re-interpreting streams already
+downloaded.
+Note that this is not a real cache; files are deterministically loaded
+from the \[lq]cache\[rq] upon certain actions, and from the network upon
+others, but neither is used as a fallback to the other.
+.SS Custom protocols
+.PP
+Chawan is protocol-agnostic.
+This means that the \f[V]cha\f[R] binary itself does not know much about
+the protocols listed above; instead, it loads these through a
+combination of local CGI, urimethodmap, and if conversion to HTML or
+plain text is necessary, mailcap (using x-htmloutput, x-ansioutput and
+copiousoutput).
+.PP
+urimethodmap can also be used to override default handlers for the
+protocols listed above.
+This is similar to how w3m allows you to override the default directory
+listing display, but much more powerful; this way, any library or
+program that can retrieve and output text through a certain protocol can
+be combined with Chawan.
+.PP
+For example, consider the urimethodmap definition of cha-finger:
+.IP
+.nf
+\f[C]
+finger:     cgi-bin:cha-finger
+\f[R]
+.fi
+.PP
+This commands Chawan to load the cha-finger CGI script, setting the
+\f[V]$MAPPED_URI_*\f[R] variables to the target URL\[cq]s parts in the
+process.
+.PP
+Then, cha-finger uses these passed parts to construct an appropriate
+curl command that will retrieve the specified \f[V]finger:\f[R] URL; it
+prints the header `Content-Type: text/plain' to the output, then an
+empty line, then the body of the retrieved resource.
+If an error is encountered, it prints a \f[V]Cha-Control\f[R] header
+with an error code and a specific error message instead.
+.SS Adding a new protocol
+.PP
+Here we will add a protocol called \[lq]cowsay\[rq], so that the URL
+cowsay:text prints the output of \f[V]cowsay text\f[R] after a second of
+waiting.
+.PP
+First, make sure you have a local CGI path \f[V]\[ti]/cgi-bin\f[R] set
+up in your \f[V]\[ti]/.config/chawan/config.toml\f[R]:
+.IP
+.nf
+\f[C]
+cgi-dir = [\[dq]\[ti]/cgi-bin\[dq], \[dq]${%CHA_LIBEXEC_DIR}/cgi-bin\[dq]]
+\f[R]
+.fi
+.PP
+It is also possible to just put your CGI scripts to
+\f[V]/usr/local/libexec/chawan/cgi-bin\f[R]; this is enabled by default,
+so you need no edits in your config.
+But it seems more convenient to use a dedicated cgi-bin in your home
+directory.
+.PP
+\f[V]mkdir \[ti]/cgi-bin\f[R], and create a CGI script in
+\f[V]\[ti]/cgi-bin\f[R] called \f[V]cowsay.cgi\f[R]:
+.IP
+.nf
+\f[C]
+#!/bin/sh
+# We are going to wait a second from now, but want Chawan to show
+# \[dq]Downloading...\[dq] instead of \[dq]Connecting...\[dq]. So signal to the browser that the
+# connection has succeeded.
+printf \[aq]Cha-Control: Connectedn\[aq]
+sleep 1 # sleep
+# Status is a special header that signals the equivalent HTTP status code.
+printf \[aq]Status: 200\[aq] # HTTP OK
+# Tell the browser that no more control headers are to be expected.
+# This is useful when you want to send remotely received headers; then, it would
+# be an attack vector to simply send the headers without ControlDone, as nothing
+# stops the website from sending a Cha-Control header. With ControlDone sent,
+# even Cha-Control headers will be interpreted as regular headers.
+printf \[aq]Cha-Control: ControlDonen\[aq]
+# As in HTTP, you must send an empty line before the body.
+printf \[aq]n\[aq]
+# Now, print the body. We take the path passed to the URL; urimethodmap
+# sets this as MAPPED_URI_PATH. This is URI-encoded, so we also run the urldec
+# utility on it.
+cowsay \[dq]$(printf \[aq]%sn\[aq] \[dq]$MAPPED_URI_PATH\[dq] | \[dq]$CHA_LIBEXEC_DIR\[dq]/urldec)\[dq]
+\f[R]
+.fi
+.PP
+Now, create a \[lq].urimethodmap\[rq] file in your \f[V]$HOME\f[R]
+directory.
+.PP
+Then, enter into it the following:
+.IP
+.nf
+\f[C]
+cowsay:     /cgi-bin/cowsay.cgi
+\f[R]
+.fi
+.PP
+Now try \f[V]cha cowsay:Hello,%20world.\f[R].
+If you did everything correctly, it should wait one second, then print a
+cow saying \[lq]Hello, world.\[rq].
+.SS See also
+.PP
+\f[B]cha\f[R](1), \f[B]cha-localcgi\f[R](5),
+\f[B]cha-urimethodmap\f[R](5), \f[B]cha-mailcap\f[R](5)
diff --git a/doc/cha-urimethodmap.5 b/doc/cha-urimethodmap.5
new file mode 100644
index 00000000..98dd6dbe
--- /dev/null
+++ b/doc/cha-urimethodmap.5
@@ -0,0 +1,163 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "cha-urimethodmap" "5" "" "" "URI method map support in Chawan"
+.hy
+.SH URI method map support in Chawan
+.PP
+Chawan can be used to map unrecognized protocols to known protocols
+using the \f[V]urimethodmap\f[R] format.
+.PP
+The main use case for this is implementing handlers to protocols unknown
+to Chawan through a protocol that the browser \f[I]does\f[R] understand.
+.SS Search path
+.PP
+The search path for urimethodmap files can be overridden using the
+configuration variable \f[V]external.urimethodmap\f[R].
+.PP
+The default search path for urimethodmap files is:
+.IP
+.nf
+\f[C]
+$HOME/.urimethodmap:$HOME/.w3m/urimethodmap:/etc/urimethodmap:/usr/local/etc/urimethodmap
+\f[R]
+.fi
+.SS Format
+.PP
+The urimethodmap format is taken 1:1 from w3m, with only some
+modifications to the interpretation of templates.
+.PP
+A rough attempt at the formal description of this:
+.IP
+.nf
+\f[C]
+URIMethodMap-File = *URIMethodMap-line
+
+URIMethodMap-Line = Comment / URIMethodMap-Entry
+
+URIMethodMap-Entry = Protocol *WHITESPACE Template *WHITESPACE CR
+
+Protocol = 1*CHAR COLON
+
+Template = [see below]
+
+Comment = *WHITESPACE CR / \[dq]#\[dq] *CHAR CR
+\f[R]
+.fi
+.PP
+Note that an ASCII colon sign (:) must be present after the protocol
+name.
+However, the whitespace may be omitted.
+.PP
+Examples:
+.IP
+.nf
+\f[C]
+# This is ok:
+protocol:   /cgi-bin/interpret-protocol?%s
+# This is ok too:
+protocol:/cgi-bin/interpret-protocol?%s
+# Spaces and tabs are both allowed, so this is also ok:
+protocol:   /cgi-bin/interpret-protocol?%s
+# However, this is incorrect, because the colon sign is missing:
+protocol    /cgi-bin/interpret-protocol?%s
+\f[R]
+.fi
+.PP
+The redirection template is the target URL.
+If the string \f[V]%s\f[R] is contained in the template, it will be
+replaced by the target URL.
+.PP
+Note: Chawan used to URL-encode the substituted URL in the past, but
+this is no longer the case.
+.PP
+For compatibility with w3m, templates starting with \f[V]/cgi-bin/\f[R]
+and \f[V]file:/cgi-bin/\f[R] are special-cased and the starting string
+is replaced with \f[V]cgi-bin:\f[R].
+So for example, the template \f[V]/cgi-bin/w3mdict.cgi\f[R] is the same
+as \f[V]cgi-bin:w3mdict.cgi\f[R] (and so is
+\f[V]file:/cgi-bin/w3mdict.cgi\f[R]).
+.PP
+Example:
+.IP
+.nf
+\f[C]
+# The following are the same in Chawan
+protocol:   /cgi-bin/interpret-protocol?%s
+protocol:   file:/cgi-bin/interpret-protocol?%s
+# Note: this last entry does not work in w3m.
+protocol:   cgi-bin:interpret-protocol?%s
+\f[R]
+.fi
+.PP
+Note however that absolute paths to cgi scripts are NOT special cased,
+so e.g.\ \f[V]file:///usr/local/libexec/w3m/cgi-bin/w3mdict.cgi\f[R]
+will simply open w3mdict.cgi in the file viewer.
+(Unlike in w3m, where it could run \f[V]w3mdict.cgi\f[R] depending on
+the user\[cq]s configuration.)
+.SS Examples
+.SS In config.toml
+.IP
+.nf
+\f[C]
+# Following sets the urimethodmap search path to the path relative to the
+# configuration file. So if your configuration file is in
+# \[ti]/.config/chawan/config.toml, Chawan will use \[ti]/.config/chawan/urimethodmap.
+# in the same directory.
+[external]
+urimethodmap = \[dq]urimethodmap\[dq]
+\f[R]
+.fi
+.SS In urimethodmap
+.SS magnet.cgi
+.IP
+.nf
+\f[C]
+# Use the \[ga]magnet.cgi\[ga] CGI shell script to pass magnet links to Transmission.
+magnet:     /cgi-bin/magnet.cgi?%s
+\f[R]
+.fi
+.PP
+\f[V]magnet.cgi\f[R] can be found in the \f[V]bonus/\f[R] directory.
+You can also write a local CGI wrapper to pass the links to your
+BitTorrent client of choice.
+.SS dict
+.PP
+In w3m, urimethodmap is commonly (ab)used to define shorthands for CGI
+scripts.
+.PP
+This works in Chawan too; for an example, you could define a
+\f[V]tl:\f[R] shorthand like this:
+.IP
+.nf
+\f[C]
+# (trans.cgi is a script you can find and study in the bonus/ directory.)
+tl:     /cgi-bin/trans.cgi?%s
+\f[R]
+.fi
+.PP
+Then, you could open the translation of any word using
+\f[V]tl:word\f[R].
+.PP
+Note however that Chawan has a more powerful facility for substitution
+shorthands like this in the form of omni-rules.
+So if you want to redirect to an on-line dictionary site with tl:word
+instead of providing a local CGI interface, it is probably easier to
+just use omni-rules instead of urimethodmap + local CGI redirection.
+.PP
+Rule of thumb: if you find yourself writing local CGI scripts that just
+send a \f[V]Location:\f[R] header, maybe consider just using an
+omni-rule.