Terminfo 的消亡:当 40 年前的技术假设失效时
Terminfo 的消亡:当 40 年前的技术假设失效时
在软件维护的世界里,存在着某些“定律”——这些技术假设根深蒂固,以至于变得不可见。对于许多 Unix 老兵来说,其中一条定律就是 terminfo 和 curses 库的必要性。四十年来,创建文本用户界面 (TUI) 的标准做法是避免对用户的硬件做出任何假设,而是依赖一个复杂的终端能力数据库,以确保程序可以在从 VT100 到 Wyse 50 的各种设备上运行。
最近,Eric S. Raymond (ESR) 在将一个名为 "greed" 的旧 Unix 游戏从 C 移植到 Rust 时,分享了他关于这种做法过时的个人感悟。他的经历凸显了计算领域的一个更广泛的转变:从碎片化的硬件格局向同质化的软件模拟环境的过渡。
Curses 和 Terminfo 的遗产
要理解为什么这种转变具有重要意义,必须理解 curses 的作用。在 Unix 的早期阶段,用户通过各种各样的物理字符单元终端连接到系统。每个制造商都有不同的转义序列,用于移动光标、清除屏幕或更改颜色。
为了解决这个问题,开发了 curses 库。它的工作原理是通过检查 TERM 环境变量并查询名为 terminfo 的系统级数据库。该数据库包含了“魔术字符串”——即操作特定终端型号所需的特定控制代码。通过抽象化硬件,开发者可以编写一段可以在数百种不同设备上运行的代码。
催化剂:移植到 Rust
在维护 "greed" 时,Raymond 决定将代码库迁移到 Rust,以利用该语言的安全保证。在使用 AI 助手处理移植工作时,他注意到一个反复出现的问题:生成的 Rust 代码中充满了 unsafe 块。
经过调查,他发现 AI 只是在封装 C 的 curses 库。因为 curses 是一个 C 库,所以从 Rust 中与之交互需要使用 unsafe 块,这违背了迁移的主要目的。
当 Raymond 要求 AI 使用像 crossterm 这样的纯 Rust 替代方案时,AI 遇到了一个障碍:Raymond 并没有安装 cargo (Rust 的包管理器),因为他依靠 AI 来处理移植过程中的“代码磨炼”工作,而不是亲自动手编写 Rust。
“偶然”的创新
面对缺乏包管理器的局面,AI 采取了一条意想不到的路径。它没有依赖外部库或传统的 C curses 系统,而是直接在源代码中编写了一个自定义的、轻量级的屏幕绘制后端。
至关重要的是,这个新的后端做了一个大胆的假设:它假设用户正在使用支持颜色的 ANSI 终端。
对于一名经验丰富的 Unix 开发者,这违反了基本规则。然而,在审查代码后,Raymond 意识到结果非常简洁、优雅,且完全没有 unsafe 块。由于 AI 缺乏 20 世纪 Unix 硬件的“制度记忆”,它只是忽略了一个不再具有实际用途的需求。
终端的新现实
今天,几乎所有使用的终端都是 ANSI 兼容的模拟器。曾经需要 terminfo 的那种多样化的物理硬件已经消失,取而代之的是遵循共同标准的软件。
Raymond 指出,这种感悟就像是“宇宙向侧面猛然倾斜”。那种认为一个人 必须 支持数百种过时的终端类型这一假设一直持续了很久,直到需求消失之后。
他这样说:
"I hadn't had to directly confront before the fact that the entire set of assumptions that made TERM and terminfo a thing are as obsolete as dial-up acoustic modems."
反对观点与考量
虽然向仅限 ANSI 的假设转变简化了开发,但一些开发者仍然会遇到传统系统的摩擦。Hacker News 上的一位评论者指出,他们与 terminfo 唯一的剩余交互仅在于环境变量被错误配置时(例如,$TERM 被设置为 screen-256color 而不是 screen),导致 vim 或 less 等工具失效。在这种情况下,他们认为,假设一个标准终端可能实际上比依赖一个可能损坏的环境变量更可靠。
结论:代码即粘土
"greed" 从 C 移植到 Rust 的转变,说明了我们看待遗留代码的方式的转变。在 AI 辅助开发的时代,改变一个基本架构假设的成本已大幅降低。如果用户中真的出现了需要非 ANSI 支持的需求,修复方法不再是长达数月的重写,一个简单的 AI 提示词即可完成。
通过摆脱 40 年前的技术假设的重担,开发者可以编写更精凑、更安全、更易于维护的软件,并承认某些编程的“古老定律”已不再适用于现代世界。