我正在尝试使用 ncurses 对话框库(随处可用,源镜像在这里),我试图在代码中动态决定是使用对话框还是仅打印到 stderr 并根据 stdin/out/err 是否连接到 char 块设备、ttys 以及它们拥有的 termcaps 进行提示。我从 less 中窃取了一些代码,这些代码在检测它是否可以正确渲染方面似乎相当可靠,并且它会查找 termcaps ce
、cd
、cl
和cm
、 或ho
、ll
和al
或sr
,否则它会切换到哑模式。但 less 的要求可能比dialog低(很多?)(至少我只需要 yesno 框)。我有点惊讶对话框或 ncurses 中没有检查大写字母,也没有办法只询问终端是否可以处理事情,或者如果没有大写字母则 initscr 失败? emacs shell 上的对话框(不是在 ansi-term 上,它在 obvs 上工作得很好)只是吐出无意义的文本,因为它是一个哑终端(与export TERM=dumb
普通的 xterm 相同),所以这里显然没有进行功能检查。
我大部分时间都不是 UNIX 程序员,那么有没有一些可靠的标准方法可以做到这一点?让我感到奇怪的是,这不是一件容易检查的事情。
显然,我可以通过对话框 yesno 代码并尝试记录它调用的所有 ncurses 内容,但这很痛苦,所以我希望我只是未能在某处找到清晰的终端功能要求列表。
更新:因此,我决定尝试制作自己的自定义 terminfo 条目,以测试我可以在多大程度上仍然获得“yesno”对话框,而且它非常小,因此测试越少越好。很难弄清楚如何做到这一点,因此以下是来自 THE FUTURE 的其他人的简短总结:
- 以下是 termcap 定义你需要把事情弄清楚;您可以直接使用 terminfo 定义并跳过几个步骤,但是像 less 这样的旧软件正在使用 termcap 版本,因此在该空间中工作会更容易。
- 假设您有一个在当前终端(或
dialog --title Hi --yesno There 0 0
)上运行的 ncurses 应用程序,请找出您的 TERM envvar 是什么。我正在 screen 和 emacs ansi-term 中工作,所以它们screen
在eterm-color
我的测试中,当然dumb
- 使用 转储您想要处理的版本(您正在运行测试,因为您希望新版本成为工作版本的子集)
infocmp eterm > eterm2.info
,这将为您提供要使用的信息版本。 - 将其转换为上限
infotocap eterm2.info > eterm2.cap
- 看看里面,然后把它剪下来,就像这是我的最小上限文件,在没有警告的情况下几乎不会运行更少:
# hacked eterm for testing dialog
eterm2|gnu emacs term.el terminal emulation:\
:am:mi:xn:\
:co#80:li#24:\
:ce=\E[K:cd=\E[J:cl=\E[H\E[J:\
:ho=\E[H:\
:cm=\E[%i%d;%dH:\
:al=\E[L:
- 请注意,我也将开头的名称更改为 eterm2。
- 将其转换回信息
captoinfo eterm2.cap > eterm2.info
mkdir infodb
创建要编译此测试信息的本地目录。export TERMINFO=$(pwd)/infodb
告诉 termcap/terminfo 在哪里可以找到它tic -D
应该首先输出本地目录tic -s eterm2.info
将其编译到该目录export TERM=eterm2
设置你的新的黑客终端- 运行你的 ncurses 应用程序,或者
dialog --title Hi --yesno There 0 0
它应该以黑白简单的方式显示出来。 - 您可以尝试重新添加其他命令以查看发生的变化,但完成后
rm -rf infodb
,export TERMINFO=
请export TERM=<oldterm>
确保一切正常运行。
更新2: 这是我最终得到的代码,它似乎在我的所有测试中都有效:
if(TermMode == TERM_UNINITIALIZED) {
struct stat s;
if((isatty(STDOUT_FILENO) && (fstat(STDOUT_FILENO,&s) == 0) && S_ISCHR(s.st_mode)) &&
(isatty( STDIN_FILENO) && (fstat( STDIN_FILENO,&s) == 0) && S_ISCHR(s.st_mode)))
{
// both stdout and stdin are char block device ttys, now check termcaps
// mostly stolen from the gnu less source code, since it seems robust
// https://github.com/gwsw/less/blob/22e4af5cccbfab633000c7d610f868a868ad6e1a/screen.c#L1280
char termbuf[2048];
char const* term = getenv("TERM");
int const TGETENT_OK = 1;
if(tgetent(termbuf,term) == TGETENT_OK) {
char sbuf[2048];
char* sp = sbuf;
char* sr = 0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
// ce ce cl are required
if((sr = tgetstr("ce",&sp)) && *sr && // eol clear
(sr = tgetstr("cd",&sp)) && *sr && // eos clear
(sr = tgetstr("cl",&sp)) && *sr) // screen clear
{
// cm or (ho and ll)
if(((sr = tgetstr("cm",&sp)) && *sr) || // cursor move
(((sr = tgetstr("ho",&sp)) && *sr) && // home
((sr = tgetstr("ll",&sp)) && *sr))) // lower left
{
// al or sr
if(((sr = tgetstr("al",&sp)) && *sr) || // add line
((sr = tgetstr("sr",&sp)) && *sr)) // scroll reverse
{
// we've got what we need for ncurses dialog!
TermMode = TERM_DIALOG;
goto term_detected;
}
}
}
#pragma GCC diagnostic pop
}
// we're at least connected to a tty
TermMode = TERM_STDIO;
} else {
// no interaction, so gotta just abort
TermMode = TERM_ABORT;
}
term_detected:
;
}
不管怎样,知道最小的 termcap 是什么还是非常好的,但至少上面列出的那些可以减少工作量,所以我将使用它们。
谢谢,克里斯