This file collects per-utility POSIX conformance audits for the terminal-screen
utilities crate. Each audit follows the playbook in audits.md.
Crate: screen/ — stty, tabs, tput (three POSIX utilities) plus the
shared osdata.rs termios data tables used by stty.
Date: 2026-06-18
Method: static spec-vs-code audit against the sliced POSIX.1-2024 tree
(~/tmp/posix.2024/sliced/xcu-shell-and-utilities/3-utilities/{stty,tabs,tput}.md),
with every Critical/Major claim behaviorally confirmed by running the release
binaries (a PTY harness via pty.fork() was used for the TTY-only stty
set-mode paths).
osdata.rs(262 lines) isstty-private (mod osdata;instty.rs:13);tabs/tputdo not use it. It holds the speed table (load_speeds), the^c→control-char translation table (load_cchar_xlat), and the master termios parameter table (load_params). One data defect lives here: the speed map keys"54"toB50(osdata.rs:32) — see stty #6.- i18n is split. All three call
setlocale(LC_ALL, "")+textdomain+bind_textdomain_codesetinmain.tabsandtputwrap their runtime diagnostics ingettext();sttydoes not — everyError::other(...)/format!diagnostic instty.rsis hardcoded English (stty #8), soLC_MESSAGESis inert forstty. - terminfo dependency.
tabsandtputare thin front-ends over theterminfocrate (Database::from_env/from_name+ capability expansion).sttyis a directtermios(3)client (termioscrate). All three honor theASYNCHRONOUS EVENTS = Defaultrequirement (no signal handling needed; none present — correct). - Diagnostic prefix.
sttypropagates errors throughmain() -> Result<…, Box<dyn Error>>, so the runtime printsError: <msg>(Rust's default) rather than the conventionalstty: <msg>.tabsprefixestabs:;tputprefixes neither util name (stty #8, tput #T6 — Minor).
Implementation: screen/stty.rs (814 lines) + screen/osdata.rs (262 lines)
Tests: screen/tests/stty/mod.rs (26 lines — no executable tests; the file
is a comment explaining that stty needs a TTY and is verified only by manual
testing)
Spec: POSIX.1-2024 (IEEE Std 1003.1-2024), Vol. 3 §3, pp. 3453–3462
Reference slice: ~/tmp/posix.2024/sliced/xcu-shell-and-utilities/3-utilities/stty.md
Date: 2026-06-18
The display paths (-a long form, -g compact save, default short form) and
the termios flag/cchar tables are broad and largely conforming, and the utility
correctly uses standard input for both get and set (the spec's security
RATIONALE). But the set path — stty's primary job — is severely broken on the
golden path: stty <single-operand> (e.g. stty sane, stty raw, stty cs8)
panics (assert!(args.operands.len() > 1)), and every negation operand
(-echo, -icanon, …) is rejected by clap as an unknown option because the
positional has no allow_hyphen_values. On top of those two crashes/rejections:
single-character control-character assignment (stty erase x) is unsupported,
the Issue-8 rows/cols/size window-size operands are entirely absent, and
the speed table mis-keys B50 as "54". Diagnostics are hardcoded English.
Resolved 2026-06-18 (branch
screen-audit). All stty findings fixed across Phases 1–3: #1/#2 unbreak the set path (single-operand panic removed,allow_hyphen_valuesfor negation), #3 single-char cchar assignment, #4rows/cols/size, #5 hardened save-blob parse, #6"50"speed key (+ cfg-gated higher bauds), #7cchar_to_strdisplay, #8gettext+stty:prefix, #9 ek/sane reviewed-conforming, #10 reject operands with-a/-g. stty gained a full test suite (8 pure-function unit tests + 9 PTY regressions via theportable-ptyharness) where it previously had none.
- #1 —
stty <single mode operand>panics (assertion failure, exit 101). ✓ fixed (Phase 1): assertion changed to!args.operands.is_empty(); regressiontest_stty_single_operand_no_panic.stty.rs:642(assert!(args.operands.len() > 1)) at the top ofstty_set_long.stty_set(stty.rs:778-784) routes any non-pfmt1operand list tostty_set_long, including a list of length 1. Sostty sane,stty raw,stty cs8,stty parenb,stty -8etc. all abort. Behaviorally confirmed under a PTY:stty parenb→panicked at screen/stty.rs:642 … assertion failed: args.operands.len() > 1, exit 101. This is the most common stty usage form. Fix: change the assertion to>= 1(or delete it; thewhile idx < lenloop already handles any length). - #2 — Negation operands (
-echo,-icanon,-parenb, …) are rejected by clap before stty ever sees them. ✓ fixed (Phase 1):#[arg(allow_hyphen_values = true)]onoperands;-a/-gstill parse as options; regressiontest_stty_negation_operand_applied.stty.rs:34-59declaresoperands: Vec<String>with noallow_hyphen_values/trailing_var_arg. clap treats a leading--token as an option cluster. Behaviorally confirmed:stty -echo→error: unexpected argument '-e' found, exit 2. The internal set logic does implement negation (stty.rs:657-665strip_prefix("-")), so the entire negation surface is dead code. Fix: add#[arg(allow_hyphen_values = true)](ortrailing_var_arg) to theoperandsfield so-flagoperands reachstty_set_long.
- #3 — Single-character control-character assignment is unsupported. ✓ fixed (Phase 2): a 1-char
op_argnow sets the control char to that byte (rejecting code points > 0xff); regressiontest_stty_single_char_cchar_assignment.stty.rs:418-441(set_ti_cchar_oparg) accepts only^-/undef/^c; any other argument that is not a 2-char^-sequence returns"Invalid cchar specification". POSIX 116246: "If string is a single character, the control character shall be set to that character." Behaviorally confirmed:stty erase x→Invalid cchar specification, exit 1. Fix: whenop_arg.chars().count() == 1, set*cc = that_char as cc_t. - #4 — Issue-8 window-size operands
rows,cols, and thesizeinformational query are entirely missing. ✓ fixed (Phase 2):rows/cols/sizehandled instty_set_longviaget_winsize/set_winsize(libc::ioctlTIOCGWINSZ/TIOCSWINSZon stdin);sizeprints"%d %d\n"; regressiontest_stty_rows_cols_size. Added by Austin Group Defects 1053/1532/1687 (CHANGE HISTORY, 116429). POSIX 116290-116301 mandatesrows number,cols number(viatcsetwinsize()), andsize(write"%1d %1d\n", <rows>, <cols>to stdout).grep -nE '"rows"|"cols"|"size"|winsize|TIOCGWINSZ' stty.rs osdata.rs→ 0 matches.sizeis the only Informational Query; without itstty sizeis "Unknown operand". Fix: addrows/colsoperands usingTIOCSWINSZ, and asizequery usingTIOCGWINSZ. - #5 —
saved settingscombination mode only round-trips this implementation's ownpfmt1:blob. ✓ hardened (Phase 3): theassert_eq!(parts[0], HDR_SAVE)is now a returned diagnostic ("invalid saved-settings format"); the round-trip contract is otherwise retained.stty.rs:778-784recognizes a saved-settings operand only when itstarts_with(HDR_SAVE)("pfmt1",stty.rs:32). That is the format-gemits (stty_show_compact,stty.rs:292-319), sostty "$(stty -g)"round-trips — CONFORMS in spirit. Flagged Major only because the-gform is not the spec's "one line of printable portable-character-set tokens excluding whitespace" guarantee tested for portability, and a malformed blob path has anassert_eq!(parts[0], HDR_SAVE)(stty.rs:488) guarded only by thestarts_withcheck — safe today, but brittle. No fix required if the round-trip contract is the only goal; documented for the next auditor. - #6 — Speed table mis-keys
B50as"54";stty 50andispeed/ospeed 50fail. ✓ fixed (Phase 3): key corrected to"50"; cfg-gated460800/921600added for non-Apple; regressionstest_speed_table_50_not_54(unit) +test_stty_speed_50.osdata.rs:32(("54", libc::B50)). The decimal baud string forB50is"50", not"54". Consequences:stty 50/stty ispeed 50→set_ti_speedlookup miss →Error::other("Invalid speed")(stty.rs:451); andstty 54wrongly programsB50. Fix: change the key to"50". (Also Minor: the table omits 460800/921600 and the non-decimalexta/extbaliases.)
- #7 — Non-printable / printable-but-unmapped control chars are displayed as a raw decimal value. ✓ fixed (Phase 3): a shared
cchar_to_strhelper renders<undef>/^c/literal-char/\NNN-octal, used by bothshow_ccharsandstty_show_short; regressionstest_cchar_to_str_*(unit) +test_stty_printable_cchar_rendering.stty.rs:251-253(show_cchars) andstty.rs:146-150(stty_show_short): when ac_ccvalue is not in the^creverse map and not\0, the code printsformat!("{}", ti.c_cc[idx])(a decimal integer). POSIX 116347-116351: the value shall be "either the character, or some visual representation of the character if it is non-printable, or<undef>". A printable assignment (e.g. erase set to@) prints64instead of@or^?-style. Fix: print the literal char when printable, else a^X/\NNN-style visual representation. - #8 — All
sttydiagnostics are hardcoded English and lack thestty:prefix. ✓ fixed (Phase 3): diagnostics routed throughgettext();mainnow catches the error and printsstty: <msg>to stderr, returningExitCode::FAILURE. EveryError::other("…")/format!("Unknown operand {}", …)instty.rs(:451, 504, 508, 687, 703, 712, 752, etc.) is plain English, notgettext()-wrapped; and because they bubble throughmain() -> Result<…, Box<dyn Error>>, the user seesError: <msg>rather thanstty: <msg>. POSIX 116317-116319 (LC_MESSAGES). Fix: wrap strings ingettext()and emit via an expliciteprintln!("stty: {}", …)path (matchingtabs). -
#9 —ekandsaneuse hardcoded control-char defaults rather than the device's "system defaults".stty.rs:610-635. POSIX 116287 (ek: "system defaults") / 116288 (sane: "reasonable, unspecified, values"). The hardcoded0x7f/0x15/0x03/… are a reasonable set andsane's values are explicitly "unspecified", so this CONFORMS. Reviewed (Phase 3): no behavior change — there is no portable "system default" termios to consult; the chosen values match common practice (and coreutilssane). Documented as conforming. - #10 —
-a/-gsilently ignore any accompanying operands. ✓ fixed (Phase 3):runnow rejects operands combined with-a/-g("operands cannot be combined with -a or -g") and exits non-zero; regressiontest_stty_operands_with_all_rejected.stty.rs:797-811: whenargs.all/args.saveis set, operands are never consulted. The SYNOPSIS separatesstty [-a|-g]fromstty operand...;stty -a saneshould arguably be a usage error. clap'sgroup = "mode"correctly makes-aand-gmutually exclusive, but neither conflicts with operands. Fix: reject operands when-a/-gis present.
-
-aand-gmutually exclusive —stty.rs:40,48share clapgroup = "mode". - Negation operands reach the set path (#2 ✓) —
operandsnow hasallow_hyphen_values(stty.rs:57). - Bundled/standalone positive operands accepted —
stty cs8 cs7reaches the set path (confirmed; failed only attcsetattron the test PTY, i.e. parsing/dispatch worked). -
--end-of-options handled (clap default).
| Opt | Status | Notes (file:line) |
|---|---|---|
-a |
CONFORMS | stty.rs:797-799 → stty_show_long; emits speed … baud;, flag groups, cchars. |
-g |
PARTIAL | stty.rs:801-802 → stty_show_compact emits a pfmt1:-prefixed colon-joined blob; round-trips with this stty (#5) but is a private form. |
- Control Modes —
parenb/parodd/cs5-8/hupcl/hup/cstopb/cread/clocalpresent (osdata.rs:152-164); baudnumber/ispeed/ospeedpresent (speed table fixed, #6 ✓). - Input Modes — all present (
osdata.rs:178-192); reachable now that the set path is fixed (#1/#2 ✓). - Output Modes —
opost(Base) + XSIonlcr/ocrnl/onocr/onlret/ofill/ofdel/cr0-3/nl0-1/tab0-3/tabs/bs0-1/ff0-1/vt0-1present (osdata.rs:196-222). - Local Modes —
isig/icanon/iexten/echo/echoe/echok/echonl/noflsh/tostoppresent (osdata.rs:226-237). - Special Control Character Assignments —
eof/eol/erase/intr/kill/quit/susp/start/stopmapped (osdata.rs:247-255);^ctable complete (osdata.rs:63-124);^-/undef→0 handled; single-char form now supported (#3 ✓). -
min/time—osdata.rs:259-260+stty.rs:742-756parse numericu8args. - Combination Modes —
evenp/parity/oddp/-parity/-evenp/-oddp/raw/cooked/nl/-nl/ek/sane/tabs/-tabshandled (stty.rs:533-638);rawmatches the POSIX literal recipe (cs8, disable erase/kill/intr/quit/eof/eol,-opost,-inpck);saved settingspfmt1round-trip hardened (#5 ✓). - Terminal Window Size (
rows/cols) andsizequery now supported (#4 ✓).
- Terminal state is read from and written to standard input —
stty.rs:794Termios::from_fd(STDIN_FILENO),stty.rs:523,771tcsetattr(STDIN_FILENO, …). Matches POSIX RATIONALE 116401-116406 (security: stdin, not stdout). - No data is read from stdin — only
tcgetattr/tcsetattrioctls. - INPUT FILES "None" — no file operands consumed.
| Var | Status | Notes |
|---|---|---|
LANG/LC_ALL/LC_CTYPE |
PARTIAL | setlocale(LC_ALL, "") called (stty.rs:787); affects clap help, but byte/char interpretation of operands is plain Rust str. |
LC_MESSAGES |
MISSING (effect) | (#8) diagnostics hardcoded English, never gettext()-wrapped. |
NLSPATH (XSI) |
MISSING | No message-catalog wiring. |
- Default — no handlers required or present (
grep -nE 'SIGCONT|SIGWINCH|signal' stty.rs→ 0).
- With set-operands and no Informational Query: no stdout — set path writes nothing to stdout (
stty.rs:778-784). -
-aspeed line:"speed %d baud;"when ispeed==ospeed, else"ispeed … ospeed …"—stty.rs:74-78(ti_baud_str). CONFORMS. -
-acontrol chars:"%s = %s;"with<undef>for disabled —stty.rs:256, 247-254. CONFORMS (except value rendering, #7). -
sizequery output"%d %d\n"now emitted (#4 ✓). - Diagnostics → stderr — via
Error:/eprintln(but English & misprefixed, #8).
- 0 on success; >0 on error —
mainreturnsOk/Err(stty.rs:786-814). - No panic on
stty <single operand>(#1 ✓) — the abort is removed; single operands exit 0. - First bad operand aborts the operand list —
stty_set_longreturnsErron the first failure (stty.rs:687-714). CONSEQUENCES OF ERRORS = Default, so abort is permitted.
screen/tests/stty/mod.rs contains no #[test] — only a comment that stty
needs a TTY. The unit-test-able pure functions are untested.
Not covered (all map to findings):
-
stty <single operand>does not panic (#1) —test_stty_single_operand_no_panic(PTY harness). - Negation operand
-echoreaches the set path (#2) —test_stty_negation_operand_applied. -
stty erase xsingle-char assignment (#3) —test_stty_single_char_cchar_assignment. -
rows/cols/size(#4) —test_stty_rows_cols_size. -
set_ti_flag/speed_to_str/build_flagstr/cchar_to_strpure-function unit tests (no TTY needed) —stty.rsmod tests. -
stty 50selectsB50(#6) —test_speed_table_50_not_54+test_stty_speed_50.
Implementation: screen/tabs.rs (426 lines, incl. 11 unit tests at :358-426)
Tests: screen/tests/tabs/mod.rs (95 lines, 5 integration #[test]s) + the in-file unit tests
Spec: POSIX.1-2024 (IEEE Std 1003.1-2024), Vol. 3 §3, pp. 3463–3466 (XSI / interactive-utility option)
Reference slice: ~/tmp/posix.2024/sliced/xcu-shell-and-utilities/3-utilities/tabs.md
Date: 2026-06-18
tabs is in good shape. It supports the full XSI option set (-a -a2 -c -c2 -c3 -f -p -s -u), the repetitive -0..-9 form (mapping -0→clear, default→-8),
the n[[sep[+]n]...] operand with comma/blank separators and +N increments
with strictly-ascending validation, -T type/TERM, and uses the terminfo
hardware-tab capabilities (tbc/hts) as the spec intends. Diagnostics are
gettext()-wrapped. The notable gaps are spec-Minor: a missing-TERM/-T
condition errors out instead of falling back to an "unspecified default terminal
type", MAX_COLUMN is hardcoded at 160, and a leading +N first operand is
tolerated.
Resolved 2026-06-18 (branch
screen-audit, Phase 4). All tabs findings fixed (strict scope): #T1 default-terminal fallback, #T2/#T4 width-derived cap + beyond-width rejection, #T3 leading-+rejection, #T5 non-terminal stdout guard. Added preset/-0/default/leading-+/beyond-width unit tests plus PTY set-path and non-terminal-stdout integration tests.
- #T1 — Unset
TERMwith no-Terrors instead of using a default terminal type. ✓ fixed (Phase 4): the no--Tpath now falls backfrom_env → ansi → dumb → vt100before erroring.tabs.rs:326-337:Database::from_env()failing (TERM unset/null) prints "cannot determine terminal type" and exits 1. POSIX 116447-116449 / 116507-116508: when-Tis absent andTERMis unset/null, "an unspecified default terminal type shall be used." Erroring is a divergence from the mandated fallback. Fix: fall back to a built-in default (e.g.dumb/ansi) instead of hard-failing. (Borderline Minor — the spec leaves the default "unspecified", but mandates that one be used.)
- #T2 —
MAX_COLUMNhardcoded at 160. ✓ fixed (Phase 4):max_column()derives the cap fromTIOCGWINSZws_col(fallbackDEFAULT_MAX_COLUMN = 160);generate_repetitive_tabs(interval, max); regressiontest_generate_repetitive_tabs_respects_max.tabs.rs:19, used bygenerate_repetitive_tabs(tabs.rs:156-169). POSIX 116446: "The maximum number of tab stops allowed is terminal-dependent." A fixed cap is acceptable but should ideally derive from the terminal width (terminfocols). Fix: query terminfocolsor document the cap. - #T3 — A leading
+Nfirst operand is silently accepted. ✓ fixed (Phase 4): a leading+is now rejected ("first tab stop cannot be an increment"); regressiontest_parse_tabstops_leading_increment_rejected.tabs.rs:117-146(parse_tabstops): the first token+5is treated as0+5. POSIX 116485-116486: the+-increment applies to any value "except the first one". Fix: reject a leading+. - #T4 — Custom-operand path does not enforce
n≤ a single column max / first stop ≥ 1 vs preset semantics. ✓ fixed (Phase 4):parse_cmd_linerejects any explicit stop beyondmax_column()("tab stop larger than terminal width"); regressiontest_tabstop_beyond_width_rejected.parse_tabstopsrejects a literal0(tabs.rs:127-129) and non-ascending order (:140-142). - #T5 — Output written even when stdout is not a terminal. ✓ fixed (Phase 4, strict):
set_hw_tabsnow guards onlibc::isatty(STDOUT_FILENO)and errors ("standard output is not a terminal") rather than dumping escapes into a pipe; regressiontest_tabs_non_terminal_stdout_errors.set_hw_tabs(tabs.rs:249-285). POSIX 116512-116514: "If standard output is not a terminal, undefined results occur."
| Opt | Status | Notes (file:line) |
|---|---|---|
-n (-0..-9) |
CONFORMS | tabs.rs:42-71, 174-204; -0→clear, default (no opt)→-8 (tabs.rs:244-245). |
-a 1,10,16,36,72 |
CONFORMS | tabs.rs:207-209. |
-a2 1,10,16,40,72 |
CONFORMS | multi-char option via preprocess_args -a2→--a2 (tabs.rs:23-33, 76). |
-c 1,8,12,16,20,55 |
CONFORMS | tabs.rs:213-215. |
-c2 1,6,10,14,49 |
CONFORMS | tabs.rs:217-218. |
-c3 1,6,10,…,67 |
CONFORMS | tabs.rs:219-223. |
-f 1,7,11,15,19,23 |
CONFORMS | tabs.rs:224-226. |
-p 1,5,9,…,61 |
CONFORMS | tabs.rs:227-231. |
-s 1,10,55 |
CONFORMS | tabs.rs:232-234. |
-u 1,12,20,44 |
CONFORMS | tabs.rs:235-237. |
-T type |
CONFORMS | tabs.rs:313-325; TERM fallback at :326-337 (but see #T1). |
-
n[[sep[+]n]...]with comma or blank separators —tabs.rs:111splits on,/whitespace. -
+Nincrement relative to previous value —tabs.rs:117-137. - Strictly-ascending positive integers enforced —
tabs.rs:127-142. - Leading
+Nfirst operand rejected (#T3 ✓). - STDIN "Not used" — no stdin reads.
- INPUT FILES "None".
| Var | Status | Notes |
|---|---|---|
LANG/LC_ALL/LC_CTYPE |
CONFORMS | setlocale(LC_ALL, "") at tabs.rs:288. |
LC_MESSAGES |
CONFORMS (plumbed) | diagnostics wrapped in gettext() (tabs.rs:125,141,149,254,262,275,319,332,344). |
NLSPATH (XSI) |
MISSING | no catalog wiring (tree-wide gap). |
TERM |
CONFORMS | read via Database::from_env; falls back to a default terminal when unset (#T1 ✓). |
- Default — no signal handling needed (none present).
- Clear+set sequence to stdout in unspecified format —
set_hw_tabs(tabs.rs:249-285). - Unsupported terminal → diagnostic to stderr + exit >0 —
tabs.rs:253-256("terminal does not support setting tab stops"). - Diagnostics → stderr only — all via
eprintln!withtabs:prefix. - 0 success / >0 error —
tabs.rs:355/ variousExitCode::from(1).
Good for a TTY-dependent utility: 11 unit tests cover parse_tabstops
(comma/blank/mixed separators, increments, non-ascending, invalid, zero) and
generate_repetitive_tabs; 5 integration tests cover -T unknown, --help,
--version, and error paths.
Not covered:
- Each XSI preset (
-a/-c3/…) yields its exact documented list —test_xsi_presets. -
-0produces an empty stop list; default equals-8—test_rep0_and_default. - Leading
+Nrejection (#T3) —test_parse_tabstops_leading_increment_rejected.
Implementation: screen/tput.rs (173 lines)
Tests: screen/tests/tput/mod.rs (60 lines, 3 integration #[test]s)
Spec: POSIX.1-2024 (IEEE Std 1003.1-2024), Vol. 3 §3, pp. 3504–3506
Reference slice: ~/tmp/posix.2024/sliced/xcu-shell-and-utilities/3-utilities/tput.md
Date: 2026-06-18
tput is the cleanest of the three. It supports exactly the three POSIX-locale
operands (clear/init/reset), maps the full POSIX exit-status ladder
(0/2/3/4/>4), continues past unavailable capabilities without error, processes
multiple operands, and honors -T/TERM. The gaps are Minor: init/reset
emit the filename of an if/rf (init/reset-file) capability instead of its
contents, an invalid operand aborts the whole list before any valid operand
runs, and there is no init→fallback for a missing reset string.
Resolved 2026-06-18 (branch
screen-audit, Phase 5). All tput findings fixed (strict scope): #T1if/rffile contents emitted +iprogrun, #T2 reset→init fallback, #T3 valid operands run before an invalid-operand exit 4. Added clear / valid-then-invalid / multi-valid integration tests.
- #T1 —
init/resetemit the init/reset file capability literally instead of catting its contents. ✓ fixed (Phase 5):emit_cap_filereads theif/rffilename (viacap.as_ref()) and writes its contents;run_cap_progruns theiprog(cap::InitProg) program.tput.rs:42-45(InitFile) and:60-63(ResetFile) expand and write the capability value, which forif/rfis a pathname; the spec intent is to send the file's contents. The code comments acknowledge this ("for now just output the capability"). Few terminfo entries useif/rf, so impact is low. Fix: read the named file and write its bytes. Alsoiprog(init program) is not executed. - #T2 —
resetdoes not fall back to init strings when reset strings are absent. ✓ fixed (Phase 5, strict):tput_resetnow tracks whether anyrs1/rs2/rf/rs3was emitted and falls back totput_initwhen none are present (historical parity).tput.rs:53-69. - #T3 — An invalid operand aborts all earlier valid operands. ✓ fixed (Phase 5, strict): the early-abort pre-validation is replaced — when at least one operand is valid, terminfo loads and operands run in order, so
tput clear bogusclears then exits 4; an all-invalid list still short-circuits to exit 4 (outranking no-terminfo exit 3) without needing a terminal. Regressionstest_tput_valid_then_invalid_operand,test_tput_multiple_valid_operands.tput.rs:132-137.
-
-T type—tput.rs:28-29, 151-157(Database::from_name). -
TERMfallback —tput.rs:141-150(Database::from_env); unset/unknown → exit 3 with diagnostic. -
clear—tput.rs:71-77emitsClearScreen(clear). -
init—tput.rs:35-51emitsis1/is2+if-contents +iprog+is3(#T1 ✓). -
reset—tput.rs:53-69emitsrs1/rs2+rf-contents +rs3, falling back to init when none present (#T1/#T2 ✓). - Unsupported operation is not an error —
tput.rs:36-49,54-67,72-75skip absent capabilities viaif let Some(...);process_operandreturnsOk(POSIX 117940-117941). CONFORMS.
- STDIN "Not used"; INPUT FILES "None".
-
LANG/LC_ALL/LC_CTYPE/LC_MESSAGES—setlocale(LC_ALL, "")(tput.rs:112); diagnosticsgettext()-wrapped (tput.rs:104,134,146,154). -
NLSPATH(XSI) MISSING — no catalog wiring (tree-wide gap). -
TERM— read viaDatabase::from_env(tput.rs:141).
- Default — no handlers needed (none present).
- Sequences written to stdout —
*.expand().to(io::stdout())throughout. - Diagnostics → stderr only — all via
eprintln!.
- 0 success —
tput.rs:18, 163-172. - 2 usage error —
tput.rs:20, 120-127(clap parse failure; missing required operand → exit 2, confirmed bytest_tput_no_operand). - 3 no terminfo —
tput.rs:21, 143-157(confirmed bytest_tput_invalid_terminal_type). - 4 invalid operand —
tput.rs:22, 104-106, 132-137(confirmed bytest_tput_invalid_operand). - >4 (5) other error —
tput.rs:23, 89-101. - Continue past unavailable capability —
tput.rs:165-170keeps the worst exit code and continues. CONFORMS to CONSEQUENCES OF ERRORS. - Valid operands run before an invalid-operand exit 4 (#T3 ✓).
- Reserved exit 1 ("Boolean operand not set") is correctly not used (commented out at
tput.rs:19); POSIX RATIONALE 118012-118014 keeps 1 reserved for Boolean operands this implementation does not support. CONFORMS.
3 integration tests cover the three error exit codes (4 invalid operand, 3 bad
-T, 2 missing operand). clear/init/reset output is verified only by
manual testing (terminfo-dependent).
Not covered:
-
tput clearemits the terminal'sclearsequence —test_tput_clear_xterm. - Multi-operand continue / valid-before-invalid behavior (#T3) —
test_tput_valid_then_invalid_operand,test_tput_multiple_valid_operands. -
init/resetfile-capability (if/rf/iprog) handling (#T1) — exercised manually (few terminfo entries carry these caps; no portable fixture).
- PR A — "stty: fix the set-mode golden path" (Critical): stty #1 (assertion →
>= 1), stty #2 (allow_hyphen_valueson operands). Smallest unit that makesstty sane/stty -echowork at all. Add the PTY-harness regression tests from this audit. - PR B — "stty: control-char + window-size operands": stty #3 (single-char assignment), stty #4 (
rows/cols/sizeviaTIOC[GS]WINSZ). - PR C — "stty: data + display fixes": stty #6 (
"50"speed key), stty #7 (printable cchar rendering), stty #8 (gettext +stty:prefix), stty #10 (-a/-g+ operands). - PR D — "stty: pure-function tests": unit tests for
parse_tabstops-equivalent helpers,set_ti_flag,merge_map,speed_to_str(no TTY needed); closes the empty-test-file gap. - PR E — "tabs: terminal-default + operand polish": #T1 (default terminal on unset TERM), #T3 (leading
+N), #T2 (cols-derived max). - PR F — "tput: init/reset file handling": #T1 (cat
if/rfcontents, runiprog), #T2 (init fallback), optionally #T3 (operand ordering).