██╗ ██╗████████╗ ██████╗ ██████╗ ██████╗ ███████╗
██║ ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗██╔════╝
███████║ ██║ ██║ ██║██████╔╝██████╔╝███████╗
██╔══██║ ██║ ██║ ██║██╔═══╝ ██╔══██╗╚════██║
██║ ██║ ██║ ╚██████╔╝██║ ██║ ██║███████║
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
[THE FAITHFUL RUST PORT OF HTOP]
"The htop C source is the spec — ported function-for-function, not reimagined."
htoprs is a faithful Rust port of htop, the
interactive process viewer. Every function under src/ported/ ports a specific htop
C function, cited by <File>.c:<line> in its doc comment. Created by MenkeTechnologies.
Read the Docs · Engineering Report · Port Report
Porting methodology
The C source is the specification. Ports are faithful — the original C is
translated function-for-function, never reimplemented from scratch. This is
enforced mechanically, following the same precedent as zshrs.
-
Spec: htop 3.5.1, 131
.cfiles — vendored in-repo as thevendor/htopsubmodule (pinned to the 3.5.1 tag) and mirrored at the developer checkout~/forkedRepos/htop.docs/port_report.htmlis generated againstvendor/htop; the snapshot/report tools take$HTOP_C_SOURCE(default~/forkedRepos/htop) to point at either. -
Port tree:
src/ported/<file>.rs— one Rust module per C file. Eachfncarries a/// Port ofcitation naming its<File>.c:<line>origin. -
Extensions tree:
src/extensions/<name>.rs— htoprs-original code that is not a translation of htop C and is therefore exempt from the port-purity gate.extensions::themeholds the named color-scheme system (31 built-in 6-color palettes plus custom-theme plumbing), andextensions::overlaythe themed keyboard-help overlay, theme chooser, and theme editor (rendering into aratatui::Buffer) — both ported from iftoprs.extensions::colorsmakes a chosen theme recolor the live htop UI in 256-color via a base16-style ANSI palette remap consulted at the singleNcurses::to_colorchoke point, andextensions::prefspersists the selection to~/.config/htoprs/prefs.json. The overlay is wired intoScreenManager_run:copens the theme chooser,Cthe editor,h/?/F1 the themed help overlay (Esccloses it),gtoggles the header,Btoggles the border (B, since lowercasebis the bar fill-style cycler below andxis htop's file-locks screen). Toggles and the bar-style change surface a transient status toast (overlay::draw_status, ported from iftoprs).extensions::bridgematerializes the live portedProcessrows as theProcmodel (viaObject::as_process), andextensions::panelsis the running-TUI wiring for the htoprs-original monitoring capabilities — the monitoring analog of the theme overlay. A thread-local state is fed the real table each sample tick (advancing the per-PID history rings, the debounced threshold alerts, and the CPU history graph) and gets first refusal on keys, with hotkeys chosen from those htop leaves unbound:Key Capability fFuzzy process finder (Enter jumps the cursor to the match) rRegex / substring filter over comm/cmdline/user, with a saved named store ( ~/.config/htoprs/filters.json)dSnapshot: first press captures a baseline, next press diffs the live table against it ( +started-exited~changed);wwrites the snapshot JSONoExport the current table to JSON + CSV under ~/.config/htoprs/AThreshold alerts — the rule set and every currently-firing PID GBraille CPU history graph (system total plus the selected PID) yAggregate/pivot: live CPU+memory totals grouped by user / command / parent ( Tabcycles the key):Command palette — fuzzy-search every action by name and run it (reuses the fmatcher); reaches both extension and htop actionsvCycle the per-PID CPU sparkline: off → narrow right-edge column → CPU-scaled inline braille graph Two of these reach the rows themselves rather than a modal, injected at the per-row draw site in
Panel_draw(the same extension-hook pattern the theme border uses, so no new portedfn): a firing-alert PID's row is recolored, and thevsparkline is drawn on the rows.vcycles three states — off, a narrow braille sparkline overdrawn at each row's right edge, and an inline graph mode where each process grows a full-width braille CPU graph beneath its info line. The graph is rendered by the same braille canvas as theGhistory graph, and its height scales with the process's CPU: idle processes stay a single line, busy ones grow up toSPARK_GRAPH_Hgraph lines (more CPU = more rows). This makes the process panel variable-height:Panel_draw/Panel_onKeyproject every screen-Y, page step, and scroll clamp through per-row heights (item_height=1 + graph_lines(cpu)for a process,1otherwise), so the cursor, paging, and scrolling all track whole processes. Non-process panels keeprowHeight = 1and are byte-identical to the ported behavior. -
Bar fill-glyph cycler (
extensions::barstyle, ported from storageshower):bcycles the character every bar meter (CPU, Memory, Swap, …) fills with, through five styles — Classic (|, htop's default), Gradient (position-shaded█▓▒░with a▸tip), Solid (█), Thin (▬/▸), and Ascii (#/>) — keeping each segment's semantic color. Each press shows the iftoprs-style status toast (overlay::draw_status, e.g.Bar style: solid) centered near the bottom for 3s. The selection persists to~/.config/htoprs/prefs.jsonand is restored on launch. It is consulted by the portedBarMeterMode_drawfill loop (barstyle::fill_glyph,None⇒ htop's native glyph) and wired into the keybinding table (Action_setBindingsbinds thekeys['b']slot htop leaves free unlessHAVE_BACKTRACE_SCREENis set) as anHtop_Action. -
Port-purity gate (
build.rs): on everycargo build/cargo test/cargo checkthat touchessrc/ported/, every freefnname is checked against the htop C-function snapshot attests/data/htop_c_fn_names.txt. Afnwhose name has no C counterpart fails the build. Rust-original helpers are rejected; genuine architectural helpers must be justified intests/data/fake_fn_allowlist.txt. The gate cannot be bypassed bycargo test --test X. -
C-name snapshot: regenerate with
tests/data/extract_c_fn_names.shafter pulling upstream htop (HTOP_C_SOURCE=~/forkedRepos/htop). -
Stub scaffold (
scripts/gen_port_stubs.py): lays out the full port surface — one module per C file, onetodo!()stubpub fnper C function (named to satisfy the gate). Stubs are placeholders, not ports; replace each with a faithful body and a/// Port ofcite. Already-ported modules are never overwritten. -
Port report:
scripts/gen_port_report.pywalks the C source and the Rust port and writesdocs/port_report.htmlwith per-file and overall coverage, derived from source at run time (nothing hardcoded). Atodo!()/unimplemented!()body is counted as stubbed, never ported, so scaffolding cannot inflate the coverage number.
Current state
The port covers 891 of 1093 C functions (81.5%) across 121 of the 131 C
files, with 31 stubs remaining — the TUI runs as a daily driver on macOS. The
core is ported end-to-end: the process model and table build (Process,
ProcessTable, Table, Row, Machine), the container/util layer (Vector,
Hashtable, RichString, XUtils), the full meter set (CPU, Memory, Swap,
Load, Battery, Network, DiskIO, GPU, ZFS, and the dynamic meters), the UI panels
and main loop (Panel, ScreenManager, MainPanel, FunctionBar, Header,
and the setup/columns/colors/display-options screens), key dispatch (Action,
IncSet, LineEditor), the CRT terminal layer, and the per-OS machine /
process-table backends (darwin / linux / freebsd / netbsd / openbsd /
dragonflybsd / solaris). Functions that need genuinely unportable C substrate
(the xMalloc family, raw Object**/bucket-table internals, varargs
formatters) stay intentionally unported; a shrinking set of honest todo!()
stubs marks work still in flight. Overall and per-file coverage — real ports vs
stubs — lives in docs/port_report.html (derived from source at run time —
nothing hardcoded).
On top of the port sits an src/extensions/ layer (18 modules, exempt from the
port-purity gate) — the named color-theme system, the help/theme overlay, and
the live monitoring suite (per-PID history, alerts, braille CPU graphs, finder,
diffs, exporters) described above.
Terminal backend & substrate
htoprs must render byte-for-byte identical to htop (enforced by the parity
suite). The terminal layer is crossterm —
pure-Rust, vendorable, cross-arch, no C dependency — giving full control over
every glyph/color/attribute so the output matches htop while the draw code is a
behavioral (not line-for-line) port. The substrate the UI renders through is
ported: Object.c (htop's vtable OOP → a Rust Object trait with a class-chain
Object_isA), RichString.c (the full styled-character buffer), and CRT.c's
color model — the ColorElements enum and every CRT_colorSchemes entry
transcribed verbatim so colors match htop exactly. The terminal-control fns
(CRT_init/readKey, Panel/ScreenManager draw) and the platform
data-collection layer (Platform_*, process scan) are now ported and drive the
live TUI; the remaining gaps are the un-started files tracked in the port report.
XUtils.c — string / math utilities:
| C function | notes |
|---|---|
String_cat |
concatenation |
String_trim |
trims leading/trailing space, tab, newline (only those three) |
String_split |
splits on a separator; interior empties kept, trailing empty dropped |
String_splitFirst |
splits on first separator only |
String_contains_i |
case-insensitive substring; |-multi-needle mode |
compareRealNumbers |
NaN-aware ordering (NaN sorts first) |
sumPositiveValues |
sum of strictly-positive values, NaN skipped |
countDigits |
digit count in a given base, with overflow guard |
countTrailingZeros |
mod-37 lowest-set-bit table |
Vector.c — container sort / search core, ported as generics over a slice
with a C-int-returning comparator (the Object** pointer array becomes a
slice; signed isize indices preserve the C's below-left arithmetic):
| C function | notes |
|---|---|
swap |
exchange two slots |
partition |
Lomuto partition, pivot moved to right |
quickSort |
recursive quicksort, pivot left + (right - left) / 2 |
insertionSort |
in-place insertion sort over [left, right] |
Vector_indexOf |
linear search, C -1 sentinel preserved |
Hashtable.c — prime-table math:
| C function | notes |
|---|---|
nextPrime |
smallest OEIS prime >= n; aborts (panics) if none fits |
Meter.c — value formatting:
| C function | notes |
|---|---|
Meter_humanUnit |
kibibytes → human-readable string (K…Q, inf cap) |
The C allocation wrappers, null-terminated-string helpers, varargs formatters,
and String_freeArray (XUtils.c); the Object** dynamic-array memory
machinery — Vector_new / _insert / _add / _resizeIfNecessary and the
rest (Vector.c); and the open-addressing bucket table — Hashtable_new /
_put / _get / _foreach and the rest (Hashtable.c) — have no faithful
safe-Rust analog (Rust's Vec / HashMap own allocation, bounds, probing,
and lifetimes) and are intentionally not ported. combSort is commented-out
dead code in Vector.c and is not ported.
Build & test
The parity suite (tests/parity/) runs the same inputs through the
reference htop (the C original htoprs is ported from) and htoprs, then
compares output byte-for-byte, modulo the deliberate rebrand
(htoprs→htop, version banner). It skips gracefully when no matching htop
3.5.x is installed, so CI stays green; a dev box with htop runs the real
comparison. Point it at a specific reference with HTOP_REF=/path/to/htop.
See docs/PARITY.md.