██╗ ██╗████████╗ ██████╗ ██████╗ ██████╗ ███████╗
██║ ██║╚══██╔══╝██╔═══██╗██╔══██╗██╔══██╗██╔════╝
███████║ ██║ ██║ ██║██████╔╝██████╔╝███████╗
██╔══██║ ██║ ██║ ██║██╔═══╝ ██╔══██╗╚════██║
██║ ██║ ██║ ╚██████╔╝██║ ██║ ██║███████║
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
[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) vCycle 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 pure-logic layers are ported — string/math utilities, the container
sort/search algorithms, the prime table, and the human-readable unit
formatter (detailed below). Partial ports also cover the faithfully-portable
subset of eight more files: Process.c (cmdline/comm string parsing, process
state char), LineEditor.c (text-buffer editing and cursor motion),
OptionItem.c (CheckItem/NumberItem accessors and editing),
RichString.c (RichString_findChar), ListItem.c (ListItem_compare),
Affinity.c (Affinity_add), History.c (the command-history ring), and
Row.c (Row_printPercentage). Functions that need still-unported substrate
(ncurses/RichString drawing, CRT colors, Panel, Object vtables,
syscalls) remain honest todo!() stubs; the rest of the C source is likewise
scaffolded with stubs so the full surface is laid out. Further parallel passes
have filled in the pure-logic functions of more files — meter sum/mode math,
FunctionBar widths, Object_isA, and Settings/Action/Scheduling/
Machine/GPUMeter helpers among them — each verified against the port gate
and unit tests. Overall and per-file coverage — real ports vs stubs — lives in
docs/port_report.html (derived from source at run time — nothing hardcoded).
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 the next phases.
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.