12 Home
diviocity edited this page 2026-06-03 18:44:12 +00:00

overview

divifetch is a build-time fetch script compiler.

instead of fetching system information on each run and dynamically assembling the output like the other fetch scripts, divifetch generates a C source file from a config, which you can compile into a standalone binary.

this approach allows for the resulting code to be mostly hard-coded at generation time. this also allows for the resulting binary to not have any runtime dependencies, to not waste time reading a config file at startup and to be as small as it should be.


how it works

config.conf -> generator -> fetch.c and Makefile -> make -> divifetch
  1. you create a config file
  2. generator source reads the config and produces fetch.c
  3. generator make reads the make config and produces the Makefile
  4. you compile fetch.c with make

usage

./generator [OPTIONS] SUBCOMMAND

subcommands

subcommand description
source read config.conf and write the fetch source to fetch.c
config generate an example config file (auto-detects OS, CPU, GPUs, mounts)
make read make.conf and generate the Makefile

multiple commands can be specified (default: source, make)

options

flag long form argument description
-h --help print help and exit
-c --config <file> config file to read for source (default: config.conf)
-cm --make-config <file> config file to read for make (default: make.conf)
-os --out-source <file> where to write the generated source (default: fetch.c)
-oc --out-config <file> where to write the example config (default: config.conf.example)
-om --out-make <file> where to write the Makefile (default: Makefile)

-c can be specified multiple times for random runtime selection between them

typical workflow

# clone the repo:
git clone https://git.divio.city/me/divifetch.git
cd divifetch

# compile the generator:
g++ -std=c++17 -o generator generator.cpp 

# generate, copy and edit the example config file:
./generator config -oc config.conf
vi config.conf

# generate the source code and Makefile:
./generator source make

# build the fetch script:
make

config file format

configs use a simple s-expression syntax.

  • a form is (name arg1 arg2 ...)

  • comments start with ; and run to end of line

  • string values are quoted with "

  • - and _ can be interchangeably used in field names


configuring the fetch

global config

there is one config form per file. it sets global defaults that apply to all entries.

(config
    (art-width 45)
    (art-file "ascii/cat.txt")
    (art-prefix "\033[1m")
    (art-suffix  "\033[0m")
    (key-prefix  "\033[1m\033[34m")
    (key-suffix  "\033[0m: ")
    (value-prefix "")
    (value-suffix ""))
field type description
art-width integer column width reserved for the ascii art (including padding); prefixes and suffixes are not counted
art-file string path to a text file containing the ascii art (if missing or empty the art column is left blank)
art-prefix string escape sequence inserted before each art line
art-suffix string escape sequence inserted after each art line
key-prefix string default escape sequence before each entry's key
key-suffix string default text/sequence after each entry's key (e.g. : )
value-prefix string default escape sequence before each entry's value
value-suffix string default text/sequence after each entry's value

entry types

each entry occupies one line of output.

lines are paired with art lines from top to bottom.

if there are more entries than art lines the art column is padded with spaces.

static entries

values are known at generation time and baked into the binary.

entry type description
static the standard key/value entry
plain like static but with all prefixes and suffixes stripped. useful for decorative lines or headers
break an empty line (no key, no value, no prefixes)
colors_normal prints the 8 standard terminal palette colors as colored blocks
colors_bright prints the 8 bright terminal palette colors as colored blocks

dynamic entries

values are resolved at runtime by calling a module function. the entry type must match a subdirectory name inside modules/.

if a module takes an argument (like mount), pass it as the second positional value or via the value field.

(mount "Mount (/)" "/")
;        ^          ^
;        key        argument

per-entry fields

every entry (static or dynamic) accepts optional field overrides in named or positional form.

positional form

(static "OS" "Artix GNU/Linux")
;        ^    ^
;        key  value

values are assigned in the following order:

key, value, key-prefix, key-suffix, value-prefix, value-suffix

named form

use (field-name value) pairs inside the entry form

(static
    (key "OS")
    (value "Artix GNU/Linux"))

both forms can be mixed

positional arguments come first, named pairs after

(static "OS"
    (value "Artix GNU/Linux"))
field description
key the label shown before the value; defaults to the entry type with a capitalised first letter
value for static entries: the displayed value. for dynamic entries: optional argument passed to the module function
key-prefix overrides the global key-prefix for this entry
key-suffix overrides the global key-suffix for this entry
value-prefix overrides the global value-prefix for this entry
value-suffix overrides the global value-suffix for this entry

ansi escape codes

strings in the config support ANSI escape sequences written as \033[...m.

for a reference on color and formatting codes see: https://jakob-bagterp.github.io/colorist-for-python/ansi-escape-codes/

common examples:

code effect
\033[0m reset all formatting
\033[1m bold
\033[30m-\033[37m foreground colors (black → white)
\033[90m-\033[97m bright foreground colors
\033[38;2;R;G;Bm RGB foreground color

module system

a dynamic module lives in a subdirectory of modules/ and consists of three files:

modules/
\--- kernel/
    |--- module.h    # header for the module
    |--- module.c    # source defining the module function
    \--- module.conf # configuration for the make template

the generator reads only the modules that are actually referenced in your config, so unused ones produce no code in the output binary.

module function signature

every module must export exactly one function following this convention:

// module without arguments
const char* kernel_module_preset(char* buf, size_t buf_size);

// module with an argument
const char* mount_module_preset(const char* arg, char* buf, size_t buf_size);

the function writes its result into buf (256 bytes) and returns a pointer to it.

writing a new module

  1. create a directory modules/$NAME/
  2. add a module.h header file
  3. add a module.c file implementing $NAME_module_preset
  4. add a module.conf file specifying which make template to use
  5. use $NAME in your config

reference hello-world modules are provided for C, C++ and Rust.

entry type names with hyphens are converted to underscores when generating the function name

compilation

for each dynamic module referenced by the config

  • the generator checks whether the module contains module.conf
  • the module language is determined (c by default)
  • the matching template is loaded from make.conf
  • build rules are generated automatically
  • only modules used in the config are compiled

unused modules generate no build rules and are not linked into the final binary.


configuring make templates

make.conf

make.conf contains one or more template forms.

each template defines how modules written in a specific language are compiled into object files and optionally archived into static libraries.

(template c
	(source-file "module.c")
	(command "gcc -O2 -static -s -c $< -o $@"))

(template cpp
	(source-file "module.cpp")
	(command "g++ -std=c++17 -O2 -c $< -o $@")
	(linker-flags "-lstdc++"))
field description
source-file source file inside the module directory (required)
command Makefile command used to compile the module (required)
object-extension extension of the generated object file
archive if yes, the object file is first packed into a static library before linking
linker-flags additional flags appended to the final link command

module.conf

a module may contain a module.conf file, where the language and per-module template overrides are specified.

(language c)

(template
	(command "gcc -O3 -march=native -flto -pipe -funswitch-loops -static -s -c $< -o $@"))

if no language is specified, the generator defaults to using the c template.

generated targets

the generated Makefile provides the following targets:

target description
all build the final binary
clean remove the build directory
install install the binary into $(DESTDIR)
uninstall remove the installed binary
run build and execute the binary

default variables

DESTDIR = /usr/local/bin
TARGET  = divifetch

directory structure

divifetch/
|--- generator.cpp          # the generator source 
|--- config.conf            # your config file
|--- config.conf.example    # generated example config
|--- fetch.c                # generated fetch source (do not edit manually)
|--- Makefile               # generated Makefile
|--- ascii/
|   \--- *.txt              # ascii art files
\--- modules/
    |--- kernel/
    |   |--- module.h
    |   \--- module.c
    |   |--- module.conf
    |--- shell/
    |   |--- module.h
    |   \--- module.c
    |   |--- module.conf
    \--- ...                # one directory per dynamic module