Add an optional RAW argument to efinger--start so efinger-finger streams the server reply verbatim, without the title header.
203 lines
9 KiB
Markdown
203 lines
9 KiB
Markdown
# Efinger for Emacs
|
|
|
|
A client for the [Finger protocol](https://www.rfc-editor.org/rfc/rfc1288.html) (RFC 1288) with an [Elfeed](https://github.com/skeeto/elfeed)-inspired reader for Emacs.
|
|
|
|
Requires Emacs 28.1 or later.
|
|
|
|
Finger, born at Stanford in 1971, is arguably the first social network: your whole profile lives in a plain-text `.plan` file that anyone can read with `finger you@your-host`. John Carmack famously kept his DOOM/Quake development diary this way. Efinger lets you keep a list of accounts and browse their `.plan` files from a two-pane interface, the same way you follow feeds in Elfeed. For the story behind the protocol, see [Finger: la red social de 1971](https://andros.dev/blog/79c99190/finger-la-red-social-de-1971-que-nunca-llego-a-morir/).
|
|
|
|
## How it works
|
|
|
|
```
|
|
Emacs Finger servers (TCP 79)
|
|
+---------------------+
|
|
| *efinger* | n / p +----------------------+
|
|
| ------------------ |------ move -->| happynetbox.com |
|
|
| >random@happynetbox | | thebackupbox.net |
|
|
| Carmack diary | RET / l | your own fingerd |
|
|
| ring@thebackupbox |<-- enter/back | |
|
|
+----------+----------+ +----------+-----------+
|
|
| |
|
|
v preview | async, :nowait
|
|
+---------------------+ |
|
|
| *efinger .plan* |<-------------------------+
|
|
| the fingered text |
|
|
+---------------------+
|
|
```
|
|
|
|
1. You keep a list of finger accounts in `efinger-accounts`.
|
|
2. `M-x efinger` opens the account list next to a content pane.
|
|
3. Moving with `n` / `p` fingers the account under point and previews its `.plan` in the other pane, asynchronously so Emacs never blocks.
|
|
4. `RET` jumps into the content, `l` goes back to the list, `q` closes both panes.
|
|
|
|
## Buffers
|
|
|
|
### Account list + content (`M-x efinger`)
|
|
|
|
```
|
|
Efinger — n/p move · RET open · g refresh · q quit
|
|
┌────────────────────────┬───────────────────────────────────────┐
|
|
│ random@happynetbox.com │ johnc@idsoftware.com │
|
|
│ Carmack diary │ ───────────────────────────────────── │
|
|
│ ring@thebackupbox.net │ │
|
|
│ me@happynetbox.com │ -------------------------------------- │
|
|
│ │ John Carmack's .plan for Aug 18, 1997 │
|
|
│ │ -------------------------------------- │
|
|
│ │ │
|
|
│ │ I get asked about the DOOM source code │
|
|
│ │ every once in a while, so here is a │
|
|
│ │ full status update: │
|
|
│ │ ... │
|
|
└────────────────────────┴───────────────────────────────────────┘
|
|
```
|
|
|
|
### Single account (`M-x efinger-finger`)
|
|
|
|
```
|
|
.plan — l back · n/p next/prev · g refresh · q quit
|
|
Emily Carter
|
|
Marine biologist. Coffee, kayaks and old maps.
|
|
|
|
--- 2026-08-14 ---
|
|
Back from sampling on the coast. Three jars of plankton to review.
|
|
```
|
|
|
|
## Features
|
|
|
|
- **Account list**: keep your finger accounts in `efinger-accounts` and browse them in a dedicated buffer, the way Elfeed lists feeds.
|
|
- **Live preview**: move with `n` / `p` and the `.plan` under point is fingered and shown in the content pane automatically.
|
|
- **Two-pane reader**: `RET` enters the content pane to read and scroll, `l` returns to the list, `q` closes both.
|
|
- **Asynchronous connections**: connections use `:nowait`, so Emacs never freezes even when a host resolves but never answers on port 79 (the common case today).
|
|
- **Connection timeout**: a stalled connection is abandoned after `efinger-connection-timeout` seconds with a clear message, instead of hanging.
|
|
- **Graceful errors**: refused or timed-out connections are reported in the content pane, never as a backtrace.
|
|
- **One-off finger**: `M-x efinger-finger` fingers a single `user@host` in its own buffer, showing the server reply raw, with no title header.
|
|
- **Configurable layout**: list on the left and content on the right (`horizontal`), or list on top and content below (`vertical`).
|
|
- **Ports and listing**: targets accept an optional `:port`; omitting the user (`host` or `@host`) asks the server to list every user.
|
|
- **UTF-8**: replies are decoded as UTF-8, so accents and box drawing render correctly.
|
|
|
|
## Keymap
|
|
|
|
### Account list (`efinger-list-mode`)
|
|
|
|
| Key | Description |
|
|
|-------|------------------------------------------|
|
|
| `n` | Move to next account and preview it |
|
|
| `p` | Move to previous account and preview it |
|
|
| `RET` | Enter the content pane for this account |
|
|
| `g` | Re-finger the account at point |
|
|
| `G` | Rebuild the list from `efinger-accounts`|
|
|
| `q` | Close both panes |
|
|
|
|
### Content pane (`efinger-plan-mode`)
|
|
|
|
| Key | Description |
|
|
|-------|------------------------------------------|
|
|
| `l` | Back to the account list |
|
|
| `n` | Preview next account (stay in content) |
|
|
| `p` | Preview previous account (stay in content)|
|
|
| `g` | Re-finger the current account |
|
|
| `q` | Close both panes |
|
|
| `SPC` / `DEL` | Scroll the content up / down |
|
|
|
|
## Installation
|
|
|
|
### MELPA
|
|
|
|
```
|
|
M-x package-install RET efinger RET
|
|
```
|
|
|
|
### use-package with :vc (Emacs 29+)
|
|
|
|
```elisp
|
|
(use-package efinger
|
|
:vc (:url "https://git.andros.dev/andros/efinger.el"
|
|
:rev :newest)
|
|
:config
|
|
(setq efinger-accounts
|
|
'("random@happynetbox.com"
|
|
("Backup Box ring" "ring@thebackupbox.net"))))
|
|
```
|
|
|
|
### use-package with :load-path
|
|
|
|
For manual installation or Emacs < 29:
|
|
|
|
```elisp
|
|
(use-package efinger
|
|
:load-path "/path/to/efinger.el"
|
|
:config
|
|
(setq efinger-accounts
|
|
'("random@happynetbox.com"
|
|
("Backup Box ring" "ring@thebackupbox.net"))))
|
|
```
|
|
|
|
### Manual
|
|
|
|
Clone the repository and place the file in a directory on your `load-path`:
|
|
|
|
```sh
|
|
git clone https://git.andros.dev/andros/efinger.el.git
|
|
```
|
|
|
|
Then add to your init file:
|
|
|
|
```elisp
|
|
(add-to-list 'load-path "/path/to/efinger.el")
|
|
(require 'efinger)
|
|
(setq efinger-accounts
|
|
'("random@happynetbox.com"
|
|
("Backup Box ring" "ring@thebackupbox.net")))
|
|
```
|
|
|
|
## Usage
|
|
|
|
1. Set `efinger-accounts` in your init file. Each entry is either a
|
|
`"[user@]host[:port]"` string or a `(LABEL "[user@]host[:port]")` list.
|
|
2. Run `M-x efinger` to open the reader.
|
|
3. Move with `n` / `p` to preview each account, `RET` to read one in full,
|
|
`l` to return to the list and `q` to quit.
|
|
4. Or run `M-x efinger-finger` to finger a single account on demand.
|
|
|
|
Public servers you can try today: `random@happynetbox.com`,
|
|
`benbrown@happynetbox.com` and `ring@thebackupbox.net`.
|
|
|
|
## Customization
|
|
|
|
Run `M-x customize-group RET efinger RET` to list all available options.
|
|
|
|
Key options:
|
|
|
|
- `efinger-accounts`: the accounts shown in the reader.
|
|
- `efinger-port` (default `79`): default finger TCP port.
|
|
- `efinger-connection-timeout` (default `10`): seconds before a stalled
|
|
connection is abandoned; `nil` waits indefinitely.
|
|
- `efinger-forward-host` (default `nil`): send `user@host` instead of just
|
|
`user` (finger forwarding, for old daemons).
|
|
- `efinger-layout` (default `horizontal`): `horizontal` (list left, content
|
|
right) or `vertical` (list top, content bottom).
|
|
- `efinger-list-width` (default `40`): list width in columns (horizontal).
|
|
- `efinger-list-height` (default `12`): list height in lines (vertical).
|
|
|
|
## The Finger protocol
|
|
|
|
Finger is minimal: the client opens a TCP connection to port 79 of the server,
|
|
sends a single line (the user name, or an empty line to list everyone)
|
|
terminated by CRLF, and the server replies with plain text and closes the
|
|
connection. No encryption, no headers, no sessions. You can speak it by hand:
|
|
|
|
```sh
|
|
echo "random" | nc happynetbox.com 79
|
|
```
|
|
|
|
## Contributing
|
|
|
|
Contributions are welcome! Please see the [contribution guidelines](https://git.andros.dev/andros/contribute) for instructions on how to submit issues or pull requests.
|
|
|
|
## License
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along with this program. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
|