Skip to content


Repository files navigation

EMUS: Eric's Multi-platform UNIX Stack

ASCII art mural of two emus facing inward flanking a cat using a computer to the left of a confused cow

This repository consists of configuration files, various programs and patches used to tailor UNIX and UNIX-like operating systems to my liking. The core components -- various command line and text interface applications -- support Linux, macOS, FreeBSD and OpenBSD, but only builds on Linux and macOS are regularly exercised, so builds on other systems may not work. Although the applications that act as the cornerstones of my desktop environment support FreeBSD and OpenBSD, I have only used and configured them for Linux because it is the only operating system I use with X11. All patches are licensed under their respective project's licenses unless stated otherwise.


The Makefile at the root of this repository is used for (mostly) automatic installation based on whether the current host is a "pet" (make pet) or "cattle" (make cattle):

  • pet:
    • Install system-wide configuration files.
    • Automatically install build dependencies and compile all binaries available on the host's platform. For all operating system, this includes the tools in the "core/" folder. On Linux, the applications in the "desktop-environment/" folder are also built. There may also be platform-specific binaries compiled in "platform/bin/".
    • Install user-specific configuration files.
  • cattle:
    • Download and install precompiled binaries for the current platform.
    • Install user-specific configuration files.

On Linux, macOS and FreeBSD, sudo(8) is used to elevate privileges whereas doas(1) is used on OpenBSD.

This script is used to bootstrap and execute a full "pet" installation on a freshly imaged machine. It currently only supports Debian Linux.


Configuration files ("dot files") for various applications and libraries.


  • user: Install user-specific configuration files for various applications. This is the default target.
  • host: Install system-wide configuration files. This target requires elevated privileges to execute i.e. sudo make host.
    • Linux:
      • Configure "/etc/fstab" to mount "/tmp/" using tmpfs.
      • Restrict access to "/proc" so most users can only see information about processes associated with their account.
      • Install a custom kernel module configuration at "/etc/modprobe.d/local.conf".
      • Create a systemd service to lock the screen when the host is suspended.
  • prepare: Make preparations to setup user-specific configuration files. This process entails checking to see if there any files existing in locations where symlinks would be created. Existing files are simply deleted if they are symlinks or renamed if they are real files. A typical use case for this target would be make prepare && make user.
  • uninstall: Delete symlinks that point to the configuration directory.


A build system for some of my most frequently used command line and text interface applications with various customizations. The compiled binaries are statically linked by default on all platforms except macOS where statically linking binaries is not well supported. On Linux, musl libc is used instead of GNU libc (glibc) because many of glibc's functions support Name Service Switch making it impossible to achieve full static linking without making changes to the code that depends on those functions or by hybrid linking against both musl libc and glibc. Although the build system supports GNU and BSD Make, GNU Make must be installed to compile musl.

The wcwidth(3) function in musl has been reimplemented with utf8proc. The replacement code can also be compiled as a shared library that can be used with "LD_PRELOAD" so other, dynamically linked applications "agree" on the width of characters.


Dependency Graph:

          ,-> coreutils
(GMP) ---|
  |       `-> (MPFR) -----------,+-> GAWK
   `-------->------------------/ ^
             ,-> (libreadline) -+--> Bash
            |,-> Less
(ncurses) --|
            |`-> Vim
             `-> tmux
(libevent) -------

              ,-> grep
(libpcre) ---|
              `-> pcre2grep

Note: Tree, cmark, flock, GNU Find Utilities, GNU sed, jq, rsync and oathtool do not appear because they do not have dependencies on any libraries built from this repository.


This folder includes a configuration script that will generate a Makfile targeting the current platform. A typical build consists of ./configure && make. The following targets are supported:

  • all: Build every binary, then execute the "sanity" target. Intermediate build failures are temporarily ignored and reported later. This is the default target.
  • sanity: Perform basic sanity checks to verify that compiled binaries can be executed.
  • deps: Attempt to automatically install build dependencies. This target should typically be run as root, e.g. sudo make deps or doas make deps.
  • install: Install any applications that have been compiled and fail if there is nothing to install. The variable "BIN" must be set to a folder where the binaries, "vimruntime" and "awklib" folders should be installed, e.g. make install BIN=$HOME/bin. Program manuals can optionally be installed by setting the "MAN" variable to destination folder that would typically be added to man(1)'s search path, e.g. make install BIN=$HOME/bin MAN=$HOME/.local/share/man. Note that "MAN" must always accompany "BIN" because a manual is only installed when its associated tool is. Assume that anything with the same name as one of the installed files will be overwritten.
  • what: Display a list of binaries that can be compiled and their status. A "+" indicates that a binary has been compiled, and a "~" indicates that the source code has been downloaded and unpacked.
  • dist: Create a distributable archive named "dist.tar.xz" that includes all compiled programs, documentation and a Makefile for quick installation. The variables "DIST_BIN" and "DIST_MAN" control the default values of "BIN" and "MAN" used by the distributable's Makefile. The default value of "DIST_BIN" is "~/.local/bin", and the default value of "DIST_MAN" is "~/.local/share/man".
  • clean: Iterate over source code and build folders in this directory and, if a folder is a Git repository, restore it to a pristine state while other folders are deleted in their entirety. To clean a specific project's folder, use clean-$PROJECT_NAME e.g. "clean-bash," "clean-less," etc.
  • pristine: Like clean but deletes everything except musl.



Define a stub to replace fmtcheck(3) on non-Apple systems.


Make use of the deprecated xlocale.h header dependent on whether Bash is being compiled on an Apple OS.


When running commands interactively, modify the terminal attributes so the suspend character is interpreted as literal sequence at the prompt but produces a SIGTSTP signal during command execution. This makes it possible to bind ^Z in readline. For example, adding "\C-z": "\C-afg \C-m" to "~/.inputrc" would make it possible to suspend a program using ^Z then pressing ^Z again to bring the program back to the foreground.


This patch fixes a memory leak associated with using $(< ...) constructs for reading files. Taken from


Allow the user to define a "no_such_file_handle" function which is analogous to "command_not_found_handle" but also runs when a command contains a "/".


Ensure the prompt will always be displayed on a clean line even if the output of the last program did not end with a newline.


~$ printf abc


~$ printf abc


If a user's information cannot be queried from the password database, use the environment variables "LOGNAME", "HOME" and "SHELL" if they are set.


Disable strtoimax(3) compatibility implementation when using C99 and up since it is part of the C99 standard.


Add support for a new cat(1) option, "--bold-escapes", that makes escape sequences generated by "-E", "-v" and "-T" bold making it easy to determine which strings are literal sequences in the input and which strings represent escaped characters. The "--bold-escapes" option is ignored if "-v" is not specified since it may conflict with escape sequences present in the input.


Add support for a new option, "--almost-all-if-not-home", that implicitly shows dotfiles for every directory except $HOME.


Add support for a new cat(1) option, "--bold-escapes", that makes escape sequences generated by "-E", "-v" and "-T" bold making it easy to determine which strings are literal sequences in the input and which strings represent escaped characters. The "--bold-escapes" option is ignored if "-v" is not specified since it may conflict with escape sequences present in the input.


If the current user's information cannot be queried from the password database, use the environment variables "LOGNAME" and "USER" as fallbacks for pwent->pw_name.


Adds support for having sleep(1) report the amount of time remaining via the terminal title. This feature is activated by setting the environment variable "REPORT_REMAINING_SLEEP_TIME_VARIABLE" to a non-empty string.


Explicitly cast arithmetic result to fix "error: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘__suseconds_t {aka long int}’ [-Werror=format=]" when compiling with musl.


Change the handling of various implementation compatibility options. Most notably, the "--lint" option will no longer warns about non-POSIX features and GNU extensions unless POSIX or traditional mode has been explicitly enabled, and some non-standard features that were accepted when using one of those modes will now produce fatal errors. No changes to code only affecting "--lint-old" were made.


Remove the lint heuristics for "regular expression on right of assignment" and "assignment used in conditional context."


Remove the lint heuristics for "subscript of array ... is null string."


Set the default value of "AWKPATH" and "AWKLIBPATH" to .:$EXEDIR/awklib where $EXEDIR is the directory in which the GAWK binary resides.


Refine the heuristics for "substr: start index ... is past end of string" so the warning is only shown if the third argument is set or if the start index is more than 1 character beyond the end of a string.


Normally hitting Ctrl+C when less than one screen of text has been shown effectively results in "--quit-if-one-screen" being ignored. This patch resolves this by making Less act as though "--quit-on-intr" ("-K") is set when less than one screen of text has been shown, and both "--no-init" ("-X") and "--quit-if-one-screen" ("-F") are set.


Adjust some preprocessor guards to resolve build failures on OpenBSD.


Preserve "-static" flag in libtool to ensure generated binaries are statically linked. See also: PCRE2Project/pcre2#341.


Use US standards by default where imperial or metric alternatives exist, add some units commonly used for cooking in the USA, and do not subdivide miles into yards since that is rarely done in the USA.


Disable error 173, "... more files to edit" which is generated when "the last item in the argument list has not been edited."


Disable Vim's default/built-in digraphs.


Set the default value of "VIMRUNTIME" to $EXEDIR/vimruntime where $EXEDIR is the directory in which the Vim binary resides.


Do not include the build time in the binary to improve reproducibility.


The core components of my desktop and graphical environment come from dwm, a tiling window manager, st, a terminal emulator, dmenu, graphical menu and a screen locker based on slock.



Sets the root window color and pixmap on all screens to a solid black rectangle. Unlike xsetroot, this utility also updates the _XROOTPMAP_ID and ESETROOT_PMAP_ID atoms so it works with compositors like xcompmgr and compton.


Desktop Entry Launch (DEL) searches for Freedesktop Desktop Entries, generates a list of graphical commands and uses dmenu as a front-end so the user can select a command to execute.


This sets the X11 root window name to lines read from standard input until the end of the file has been reached or an error occurs.


This program generates a status line. It supports displaying clocks for multiple time zones, battery status and user-defined indicators that are read from a file.


Print the amount of time in milliseconds the display has been idle.



This patch implements fuzzy matching that, unlike the original implementation, is disabled by default.


Adds a new command line option ("-g") that turns dmenu into a password prompt.


This patch adds a new command line option ("-a") that will make dmenu print the current selection and exit if the user input only matches one item.


With this patch, the prompt foreground can be specified with the "-pf" command line option, and the background can be specified with "-pb". A new entry must be added to the color definition array in the configuration header after applying this patch:

static const char *colors[SchemeLast][2] = {
    // ...
    [SchemePrompt] = { "#000000", "#ffffff" },


Disables borders on tiled windows when only one, non-floating window is visible.


Modifies focus behavior so window focus is only changed when a mouse button is clicked. The focusonclick patch renders MODKEY mouse actions inoperable, but this patch does not suffer from the same problem.

This patch has been modified from its original version so that using the mouse scroll wheel on top of a window will not shift focus.


Copies most macros defined in dwm's config.def.h to dwm.c and adds some additional helpers. The user must still explicitly map "MODKEY" to their preferred window manager key e.g. #define MODKEY HyperKey.


Let's users to define more complex constraints than what the "rules" array allows; the user defines a function as void ruleshook(Client *c) in the configuration header that is executed at the end of "applyrules" for every window dwm manages. Two new properties, "class" and "instance", are added to the Client struct, and a global variable named "scanning" is also added which can be used to differentiate between windows that already exist when dwm is started from those that were created afterward. The value of "scanning" is 1 while dwm is initializing and 0 thereafter. This patch also gives untitled windows more descriptive fallback names based on the window class and/or instance.

In the following example, windows that are created on unselected tags after dwm is started are marked urgent:

void ruleshook(Client *c)
    if (!ISVISIBLE(c) && !scanning) {


This patch prevents dwm from drawing tags with no clients (i.e. vacant) on the bar. It also makes sure that clicking a tag on the bar behaves accordingly by excluding vacant tags from the list of displayed tags. Empty rectangles would normally be drawn next to vacant tags, but this patch removes them since they are no longer necessary.


The number of windows shown in the monocle mode icon will not include floating windows with this patch applied.


This patch adds support for interprocess communication to dwm via a named pipe. The path of the pipe is defined with static const char fifopath[] in the configuration header. The user must also define void fifohook(char *data), a function that accepts the null-terminated data sent over the pipe as its only argument. Three new macros are defined and can be seen in the following example hook:

void fifohook(char *data)
    Arg arg;
    Client *c;
    Monitor *m;
    char title[sizeof(((Client *)0)->name)];

    PARSING_LOOP (data) {
        // Close window with specified title
        if (argmatch("close %s", title)) {
            for (m = mons; m; m = m->next) {
                for (c = m->clients; c; c = c->next) {
                    if (!strcmp(c->name, title)) {
                        arg.v = (void *) c;
        // Shut down dwm
        } else if (wordmatch("quit")) {
            running = 0;
        // Parsing failure
        } else {

The PARSING_LOOP macro defines the looping condition for parsing and instantiates variables used by the other macros. The argmatch macro is used to handle commands that expect arguments. It accepts a string literal that is a scanf(3) format specifier followed by pointers to locations used to store the parsed values. The "wordmatch" macro is for commands that accept no arguments. Both the "argmatch" and "wordmatch" macros return non-zero values if they were able to match the specified pattern or string.

Note: The "fifopath" variable supports using ~/ as a prefix to represent the user's home directory.


Enables the use of regular expressions for window rules' "class", "title" and "instance" attributes.


Remove borders from floating windows that fill the display.


Replace the deprecated function "XKeycodeToKeysym" with "XkbKeycodeToKeysym" to silence a compiler warning.


Adds function that is used to select the most recently, previously focused window that is currently visible. To compile dwm with this patch, the command line option "-lrt" must be set and _POSIX_C_SOURCE must be greater than or equal to 199309 (cc ... -D_POSIX_C_SOURCE=199309L ...).


Makes clicks on the window title area register only if they happen in the top half. This patch was created to reduce the amount of accidental window closures caused by attempts to middle-mouse-click browser tabs.


Implements a system tray for dwm. The following variables must be defined in the configuration header:

// -1: systray follows selected monitor
// X: pin systray to monitor X
static const unsigned int systraypinning = 0;

// Icon paddding
static const unsigned int systrayspacing = 2;

// 1: if pinning fails, display systray on the first monitor
// 0: display systray on the last monitor
static const int systraypinningfailfirst = 1;

// 0 means no systray
static const int showsystray = 1;

This patch has been modified from its original version to make the monitor numbers used for "systraypinning" consistent with the monitor numbers dwm uses internally; in the original version, "systraypinning" is indexed from 1 instead of 0.


Adds a new set of rules that can be used to temporarily override the layout, nmaster and mfact values when certain tags are active. The new rule structs have six properties:

  • triggertags: This is a bitmask representing the tags that must be active for the rule to be triggered.
  • exact: When when this is non-zero, only the tags listed in triggertags can be active for this rule to fire. When this is zero, the rule will trigger if the active tags are superset of the trigger tags.
  • mnum: The monitor the rule applies to. When this value is less than zero, the rule will apply to tags on any monitor.
  • minclients: Minimum number of tiled windows required for a rule to fire.
  • maxclients: Maximum number of tiled windows allowed for a rule to fire. When there is maximum, this should be set to 0.
  • layoutnum: Index of the layout in the "layouts" array to use when the rule is triggered. When the layout is changed, the layout symbol is not updated to reflect the ephemeral layout used by a tag rule fires. If this number is less than zero, the layout will not be changed.
  • mfact: Value of mfact to be used when the rule is active. This can be set to -1 to leave mfact unchanged.

Here is an example definition of the new rules array:

static const TagRule tagrules[] = {
    // Tag              Exact   Monitor     Mn,Mx   nmaster     Layout No.  mfact
    // If tags 9 and 2 are active, switch to layout number 2.
    { TAG(9) | TAG(2),  1,      -1,          0, 0,  -1,         2,          -1 },
    // If tag 1 is active and there are at least two windows shown, switch
    // to layout 1 and set mfact to 0.5.
    { TAG(1),           0,      -1,          2, 0   -1,         1,          0.5 },

Rules are evaluated from 0...N, and no further rules are evaluated once a match is encountered.

Note: Although it would a little more efficient to integrate the code of "applyrules" into "arrangemon", this patch makes changes to "arrange" instead to reduce the likelihood of conflicting with other patches.


Allows customization of colors for tags containing urgent windows. A new color scheme should be defined in the "colors" array indexed with SchemeUrg:

static const char *colors[][3] = {
    // Scheme           Foreground  Background  Border
    // ...
    [SchemeUrg]     = { white,      red,        red    },


Removes urgency hint from any window that is visible. This will not reset most applications' internal urgency states which means things like tray icons may remain active even though a window's urgency hint has been removed.


Adds an additional property named "priority" to window rule definitions. When a new window is managed by dwm, its position in the window list is determined by its priority: the higher the priority, the earlier in the list it is added. A default priority must be specified in configuration header like so:

static const int defaultpriority = 50;

When using the "zoom" function, a window cannot be zoomed above a window with a higher priority.


This patch sets the environment variable "DWM_SELECTED_WINDOW_TITLE" to the title of the currently selected window when spawning a subprocess.


For whatever reason, some windows that should be centered are instead spawned in the top left corner at position (0, 0) relative to the dwm bar; this change centers these windows when they appear.

This patch must be applied after "dwm-00-window-attachment-priority.diff".


This change adds the option to restrict the mouse to the focused monitor. When the mouse reaches the edge of the screen while restriction is enabled, the cursor will not be permitted to move to another monitor. This feature is controlled by the variable "restrictmouse" which should be defined in the configuration header i.e. static int restrictmouse = 1. When mouse restriction is enabled, the behavior of "focusmon" changes so that it warps the mouse to the newly focused monitor.


Show the status bar on all monitors -- focused or not, and always use "SchemeSel" (the selected-item color scheme) on the focused monitor's status bar for the window title area even when no windows are visible.

This patch depends on "dwm-00-systray.diff".


If text is selected in another window and st loses ownership of the X11 "PRIMARY" buffer, clear any existing highlighted / selected lines.


When a character is emboldened, its color may not be the same as the non-bold version, so swapping the background color and foreground color will result in text's color becoming something other than the background color. This patch disables preservation of the bold attribute of the character under the cursor to resolve this.


Modifies st to support user-defined fallback fonts specified in a NULL terminated array defined as const char *fonts[]. This change also resolves an issue where fallback fonts were used in place of default fonts in an inconsistent manner which caused identical sets of text to sometimes use different fonts. In the following example, DejaVu Sans Mono is the primary font with two others specified as fallbacks:

const char *fonts[] = {
    "DejaVu Sans Mono",
    "VL Gothic",
    "WenQuanYi Micro Hei",


With certain font configurations, underscores are drawn outside the character cell clipping area in st, a problem that also plagues other terminal emulators that use Xft:

To work around this problem, this patch adds a new variable, static float underscoreascentscale, a scaling factor for font->ascent when a glyph is an underscore.

This patch depends on "st-00-font-array-support.diff".


EMUS: Eric's Multi-platform UNIX Stack






No releases published


No packages published