我正在为参数化字符串(用于为终端指定一些终端功能)在C++中实现解析器。然后我在terminfo的手册页上看到了这个% encoding
:
%l push strlen(pop)
所以,我的问题是,每当我们将任何东西推送到堆栈上时,就是遇到以下任何% encodings
的时候:
%p[1-9] push ith parm
%’c’ push char constant c
%{nn} push decimal constant nn
%l push strlen(pop)
%+ %− %* %/ %m (arithmetic): push(pop integer2 op pop integer1)
%& %| %^ (bit operations): push(pop integer2 op pop integer1)
%= %> %< (logical operations): push(pop integer2 op pop integer1)
%A %O (logical operations): and, or
%! %~ (unary operations): push(op pop)
每当遇到这些并计算它们的结果时,当结果即将被推送到堆栈上时,一个整数(包括 0 或 1 表示布尔结果)或一个字符将被推送到堆栈上,那么%l encoding
意味着以下任何或没有
从堆栈中弹出单个值,然后
if a character push 1 onto stack
并if an integer push #digits_in_that_integer onto the stack
.(因为
%l
是使用strlen
在手册页中编写的)从堆栈中弹出一个字符串(弹出字符串:继续弹出直到堆栈为空),然后将弹出字符串的长度推回堆栈。
所以,我的问题是%l push strlen(pop)
是什么意思,它在谈论哪个长度?
奖励问题:在术语信息的参数化字符串的情况下弹出字符串的方法是否正确(在上面提到的第二个项目符号中)?
编辑:正如Thomas Dickey所指出的,现在我指的是terminfo的这个手册页。
尽管页面标题为"Linux Manpages Online">,但所引用的手册页是 Solaris (SVr4),它已被 X/Open curses 淘汰。 两者都没有提供必要的细节;NCURSES的解释填补了细节:
- SVr4(和 X/Open,它反刍该信息而不增加清晰度)表示
tparm
参数是"长"的。 但是一些参数必须是字符串(即char*
),以支持标签功能。 - 在
tparm
首次被记录下来的时候,long
似乎足够大,可以容纳一个指针(即char*
,一个字符串),<stdarg.h>
并不常见。 关于"足够大"的假设不一定是正确的(参见20年前的64位编程模型:为什么是LP64?)中的讨论),但这是为tparm
做出的假设。 - 对于您最感兴趣的平台,假设您有 LP64(或 LP32)。
- 调用
tparm
时,ncurses 会分析功能字符串以确定特定参数是否要解释为字符串(无论它与%l
匹配还是%s
匹配),并且每当使用该参数时,它都会提供字符串。 - ncurses 使用堆栈进行一系列操作(请参阅
terminfo
手册页中的参数化字符串)。
实际上,ncurses 在功能字符串上使用两次传递:
- 在第一次传递中(参见源代码中的
_nc_tparm_analyze
),它遍历字符串以查看哪个参数将被推送到堆栈上,当它看到%l
或%s
时,将该位置标记为字符串p_is_s[]
数组中的位置。 - 然后在第二遍中,ncurses 使用
_nc_tparm_internal
(分别由 varargs 和固定长度的参数列表函数共享tiparm
和tparm
)。 使用该数组,它知道是将零参数处理为数字零还是空字符串。 参考源代码,如果要求弹出一个给定数字的字符串(或者如果堆栈上没有任何东西),ncurses 会传回一个空字符串。
所有这些都依赖于对tparm
的正确调用,因为没有可移植的方法来确定传递给函数的参数数量,也没有确定它们的实际类型。与printf
不同,编译器没有帮助。 但是,如果参数列表与功能字符串匹配,ncurses 将(可能...)匹配它。 SVr4 curses 不会这样做(例如参见 illumos-gate 上的tparm.c
)。
在给定的示例中,%p1%l
- ncurses 期望将字符串推送到堆栈上,例如,使用
%p1
(引用功能字符串后的第一个参数tparm
),以及 - ncurses 从堆栈中弹出字符串值,
- 调用
strlen
以获取其长度和 - 将该长度(作为数字)推到堆栈上。
堆栈上的该数字可用于计算,例如,
%p1%l%{1}%+
将 1 添加到它(将结果推送到堆栈上),或者通过使用%d
格式化数字来使用它(堆栈上没有任何内容),等等。
要输出字符串及其长度,再次假设字符串是第一个参数,那么您可以在功能字符串中多次引用它,如下所示
%p1%l%d:%p1%s
要输出字符串的长度,请使用冒号 (:
) 分隔符和字符串本身。tparm
的"输出"当然是另一个字符串,旨在使用putp
或tputs
打印,因为它可能具有嵌入的填充信息(请参阅 terminfo 函数手册页中的输出函数)。
为 terminfo 定义的操作来自 SVr4,该 SVr4 于 1988 年正式宣布,尽管在实践中它花了几年时间才成为现实。 没有为字符串串联或子字符串定义任何操作;应用程序必须自己做这种事情。 terminfo所做的是参数化数字,并且(不是事后的想法)提供在适当位置插入字符串。