diff options
-rw-r--r-- | Makefile | 38 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | doc/build.md | 6 | ||||
-rw-r--r-- | doc/cha-api.5 | 828 | ||||
-rw-r--r-- | doc/cha-config.5 | 1514 | ||||
-rw-r--r-- | doc/cha-localcgi.5 | 336 | ||||
-rw-r--r-- | doc/cha-mailcap.5 | 160 | ||||
-rw-r--r-- | doc/cha-mime.types.5 | 72 | ||||
-rw-r--r-- | doc/cha-protocols.5 | 283 | ||||
-rw-r--r-- | doc/cha-urimethodmap.5 | 163 |
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. |