Stash

Stash is a library for efficiently storing maps of keys to values when one doesn't care what the keys are but wants blazing fast O(1) insertions, deletions, and lookups.

Use cases include file descriptor tables, session tables, or MIO context tables.

See the project page for more.

Disabling Firefox Addon Signature Verification

As of Firefox 48, it's impossible to disable mandatory addon signature verification without monkey patching Firefox or recompiling with MOZ_REQUIRE_SIGNING unset. Personally, I find this unacceptable as the user should always be in charge. It's also completely useless as any party powerful enough to disable the signature verification in about:config could just as easily sideload a powerful (but signed) extension like greasemonkey and then install a malicious greasemonkey script.

Rants aside, the correct solution (for the user) is to either recompile Firefox with mandatory signature verification disabled or use the Firefox Developer build. Unfortunately, Firefox is a monster and recompiling it just isn't a viable option for me (or anyone with a laptop). Also unfortunately, prepackaged Firefox binaries are missing some useful security features like PIE and dynamic libraries. Finally, the Firefox Developer build is a bit too bleeding edge for my taste (I would like my primary browser to be relatively bug free).

So, I've written a (disgusting) script that monkey patches Firefox's omni.ja to make signature verification optional again. I've only tested it on Arch Linux but it should work on all unix-like systems. However, if your omni.ja file is not in /usr/lib/firefox/, you'll have to tell the script where to find it (i.e., ./nosign.sh /path/to/omni.ja).


NOTE: This script does not disable addon signature verification, only makes it optional. To turn it off, you still need to set xpinstall.signatures.required to false in about:config.

WARNING: This script updates the omni.ja file IN PLACE (using sudo).

WARNING: Use at your own risk.

Localtime

Localtime is a small, light-weight go daemon for keeping the timezone up-to-date. It uses geoclue2 and systemd-timedated to do all the heavy lifting so it's able to run with minimal privileges. See the project page for more information.

Pipe to Emacs

While there are many ways to pipe to emacs, they all involve either shuttling text by repeatedly calling emacsclient or writing to a temporary file. However, neither are necessary.

Basically, while emacs can't (yet) read from a named pipe (FIFO), it can read standard output from a process so, one gratuitous use of cat later...

(defun pager-read-pipe (fname)
  (let ((buf (generate-new-buffer "*pager*"))
        (pname (concat "pager-" fname)))
    (with-current-buffer buf (read-only-mode))
    (switch-to-buffer buf)

    (let ((proc (start-process pname buf "/usr/bin/cat" fname)))
      (set-process-sentinel proc (lambda (proc e) ()))
      (set-process-filter proc (lambda (proc string)
                                 (when (buffer-live-p (process-buffer proc))
                                   (with-current-buffer (process-buffer proc)
                                     (save-excursion
                                       ;; Insert the text, advancing the process marker.
                                       (let ((inhibit-read-only t))
                                         (goto-char (process-mark proc))
                                         (insert string)
                                         (set-auto-mode)
                                         (set-marker (process-mark proc) (point))))))))
      proc)))

...and you can read a from named pipe. As an added bonus, this function will try to autodetect the correct mode.

To actually use this, I recommend the following shell script:

#!/bin/bash
set -e

cleanup() {
    trap - TERM INT EXIT
    if [[ -O "$FIFO" ]]; then
        rm -f "$FIFO" || :
    fi
    if [[ -O "$DIR" ]]; then
        rmdir "$DIR" || :
    fi
}
trap "cleanup" TERM INT EXIT

SOCKET="${XDG_RUNTIME_DIR:-/run/user/$UID}/emacs/server"

# Create a named pipe in /dev/shm
DIR=$(mktemp -d "/dev/shm/epipe-$$.XXXXXXXXXX")
FIFO="$DIR/fifo"
mkfifo -m 0600 "$DIR/fifo"

# Ask emacs to read from the names socket.
emacsclient -s "$SOCKET" -n --eval "(pager-read-pipe \"$FIFO\")" >/dev/null <&-

exec 1>"$FIFO"
cleanup # Cleanup early. Nobody needs the paths now...
cat

You will probably need to set the SOCKET variable to your emacs socket filename.

Usage:

$ dmesg --follow | epipe

Map Range (Rust) Macro

Below is a (rust) macro for applying another macro to a range of values 0..N where N is specified as a sequence of big endian octal digits. This is primarily useful for implementing traits for fixed-sized arrays.

This macro may eventually end up in some crate but I haven't found a good place to put it (and it doesn't really deserve its own crate).

The Macro

macro_rules! map_range {
    /* The actual macro */
    (%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, (0 $($rest:tt)*)) => {
        map_range!(%# apply $tmpl, $shift, $offset, $by*2, ($($rest)*));
        map_range!(%# apply $tmpl, $shift, $offset + $by, $by*2, ($($rest)*));
    };
    (%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, (1 $($rest:tt)*)) => {
        map_range!(%# apply $tmpl, $shift-$by, $offset, $by, (0 $($rest)*));
        $tmpl!((($offset) - ($shift)));
    };
    (%# apply $tmpl:ident, $shift:expr, $offset:expr, $by:expr, ()) => { };

    /* Convert from little endien octal to big endian binary */
    (%# convert $tmpl:ident, (0 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (0 0 0 $($binary)*));
    };
    (%# convert $tmpl:ident, (1 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (1 0 0 $($binary)*));
    };
    (%# convert $tmpl:ident, (2 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (0 1 0 $($binary)*));
    };
    (%# convert $tmpl:ident, (3 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (1 1 0 $($binary)*));
    };
    (%# convert $tmpl:ident, (4 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (0 0 1 $($binary)*));
    };
    (%# convert $tmpl:ident, (5 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (1 0 1 $($binary)*));
    };
    (%# convert $tmpl:ident, (6 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (0 1 1 $($binary)*));
    };
    (%# convert $tmpl:ident, (7 $($octal:tt)*), ($($binary:tt)*)) => {
        map_range!(%# convert $tmpl, ($($octal)*), (1 1 1 $($binary)*));
    };
    (%# convert $tmpl:ident, (), $binary:tt) => {
        map_range!(%# apply $tmpl, 0, 0, 1, $binary);
    };

    /* Public API */
    ($tmpl:ident @ $($num:tt)*) => {
        map_range!(%# convert $tmpl, ($($num)*), ());
    };
}

Example

use std::mem;

trait AsArray<T> {
    fn as_array(&self) -> &T;
}

macro_rules! array_impl {
    ($value:expr) => {
        impl<T> AsArray<[T; $value]> for [T] {
            fn as_array(&self) -> &[T; $value] {
                const LEN: usize = $value;

                if self.len() == LEN {
                    unsafe { mem::transmute(self.as_ptr()) }
                } else {
                    panic!();
                }
            }
        }
    };
}

// Call array_impl!(i) for i in 0..256. "2 0 0" means 0o200 (octal 200).
map_range!(array_impl @ 2 0 0);

Gazetta

Gazetta is a static site generator written in rust (currently powering this website). You can find more details on the project page.

Platter

Announcing platter, a file server for direct file transfers. See the project page for details.

Screenshot

Overkill Initial Release

I have been working on a project I call overkill for the past few months or so. It's is a publish-subscribe framework (or a functional-reactive programming framework if you want to use the latest buzz word) for collecting and distributing information on a local machine. Personally, I use it to generate my status bar (hence the name, overkill). See the project page for more information.