terminfo参数化字符串%d编码行为

时间:2017-11-19 02:06:16

标签: c++ c ncurses curses terminfo

我试图理解terminfo的参数化字符串解析器中%d编码的行为。相关的手册页是here并声明 -

%[[:]flags][width[.precision]][doxXs]
        as  in  printf, flags are [-+#] and space.  Use a ":" to allow the
        next character to be a "-" flag, avoiding interpreting "%-" as  an
        operator.

但没有说明要打印值的位置以及如何处理边缘情况。它们是从堆栈还是从传递给参数化字符串的参数?当传递额外参数(参数化字符串中不等于%d)或存在额外%d时,会发生什么(参数化字符串参数不正确?)?那个未定义的行为或实现定义或定义在某个地方?

我尝试通过手动编写一些有效和无效的字符串并验证输出来检查某些情况,但到目前为止所有内容都有点不一致,所以我在这里看不到模式 -

#include <iostream>
#include <curses.h>
#include <term.h>

using namespace std;

int main() {
    // single %d prints single argument
    // => 2
    auto res = tparm("[%d]", 2);
    cout << res << endl;

    // single %d prints single argument and ignores additional
    // => 2
    res = tparm("[%d]", 2, 3, 4);
    cout << res << endl;

    // multiple %d prints 0 for absent additional arguments
    // => 2-0-0-0
    res = tparm("[%d-%d-%d-%d]", 2);
    cout << res << endl;

    // multiple %d prints with equal number of arguments prints
    // first two correctly and rest 0
    // => 2-3-0-0-0
    res = tparm("[%d-%d-%d-%d-%d]", 2,3,4,5,6);
    cout << res << endl;

    // single value pushed to stack prints from stack
    // => 2
    res = tparm("[%p1%d]", 2);
    cout << res << endl;

    // single value pushed to stack prints from stack and ignores extra arguments
    // => 2
    res = tparm("[%p1%d]", 2,3,4);
    cout << res << endl;

    // single value pushed to stack prints from stack and additional prints are 0
    // if no arguments are provided
    // => 2-0-0
    res = tparm("[%p1%d-%d-%d]", 2);
    cout << res << endl;

    // single value pushed to stack prints from stack and additional prints 0
    // even if equal arguments are provided
    // => 2-0-0
    res = tparm("[%p1%d-%d-%d]", 2,3,4);
    cout << res << endl;

    // single value pushed to stack prints from stack after pop()?
    // => 100-<garbage>
    res = tparm("[%p1%d-%c]", 100);
    cout << res << endl;

    // pushed to stack via {} and equal arguments provided, prints all
    // => 2-1-100-200
    res = tparm("[%{1}%{2}%d-%d-%d-%d]", 100, 200);
    cout << res << endl;

    // pushed to stack via {} and %p1 equal arguments provided
    // prints only stack and rest 0
    // => 100-2-1-0
    res = tparm("[%{1}%{2}%p1%d-%d-%d-%d]", 100, 200);
    cout << res << endl;
}

1 个答案:

答案 0 :(得分:1)

您的示例中提到的一个问题是它们会执行未定义的行为。 terminfo的定义的行为使用显式参数标记(例如%p1)将推送参数传递到堆栈,其中可以由%d等运算符弹出。缺乏这一点,你依赖于ncurses对termcap(没有参数令牌)的解决方法,并且随便,这会产生类似

的表达式
    res = tparm("[%d-%d-%d-%d]", 2);

尝试从参数列表中读取多个参数。您的示例提供了一个,因此您处于 C语言未定义行为的领域(即,可变长度参数列表中的参数数量错误)。如果您的通话传递了额外的参数,那么可能就可以了(请参阅例如Visual C accepting wrong number of arguments?),但是如果更少,则结果可能是访问外部的内存参数列表。

回应评论: ncurses允许使用%d而不使用%p1的termcap样式。但它会计算参数的数量,在进行实际替换之前列出这些参数。由于它将这些处理为可变长度参数列表,因此无法确定您的应用程序是否为给定字符串传递了错误数量的参数。

进一步阅读:

    /*
     * Analyze the string to see how many parameters we need from the varargs list,
     * and what their types are.  We will only accept string parameters if they
     * appear as a %l or %s format following an explicit parameter reference (e.g.,
     * %p2%s).  All other parameters are numbers.
     *
     * 'number' counts coarsely the number of pop's we see in the string, and
     * 'popcount' shows the highest parameter number in the string.  We would like
     * to simply use the latter count, but if we are reading termcap strings, there
     * may be cases that we cannot see the explicit parameter numbers.
     */

以及tc_BUMP等功能,可以容纳termcap缺少参数令牌。