如何在c源代码中自动将类型转换添加到printf样式函数?

时间:2016-12-02 16:12:24

标签: c porting

我正在将一个大型的c项目从Windows移植到Unix,并且源代码包含数千次调用的logprint函数,声明如下:

VOID logprint(DWORD level, LPCSTR format, ...);

现在这是我的两个问题:

1。)使用的格式类型说明符不可移植

代码使用%lu表示ULONG个变量。在Windows上,这很好,因为ULONGunsigned long的typedef。但是,在移植代码时,我无法重现此typedef,因为根据[MS-DTYP] ULONG必须始终完全是32位(注意:使用Microsoft的c编译器unsigned long始终为32位)。 所以我创建了一个Windows类型头文件wtypes.h,它在stdint.hlimits.h的帮助下定义了基本的Windows数据类型。 当然,如果系统%lu是64位而我的unsigned long是32位,那么由于ULONG说明符,这会导致无效读取。所以我还必须向所有(unsigned long)个logprint参数添加ULONG强制转换。 ULONG只是当然的一个例子......

2。)使用的格式类型说明符无效

此外,该代码使用了许多无效的格式说明符。例如。 %d表示DWORD参数。

当然很容易解决:

  1. 识别所有日志打印电话
  2. 识别每个参数的类型
  3. 验证是否使用了正确的格式说明符
  4. 将正确的类型转换添加到参数
  5. 示例:

    替换:

    ULONG ulMin, ulMax;
    ...
    logprint(FATAL, "specified interval is invalid %ld..%u out of range",
                     ulMin, ulMax);
    

    使用:

    logprint(FATAL, "specified interval is invalid %lu..%lu",
                     (unsigned long) ulMin, (unsigned long) ulMax);
    

    但这需要至少两个星期,之后我的大脑会出现乱码。

    所以我的实际问题是:

    是否有任何自动化工具可以进行这些更改?

    作为最低要求,工具必须能够识别参数的类型并使用类型转换为它们添加前缀。一旦有了类型转换,我就可以轻松编写一个修复格式说明符的python脚本。

1 个答案:

答案 0 :(得分:1)

logprint的来源是否可访问?如果是,最好的方法似乎是直接改变它。它必须包含va_arg的类型转换代码,例如:

ul = va_arg(argp, ULONG);

然后根据需要更改ULONG

如果不是,只需让自己的包装函数(如logprint64执行类似的任务,但根据需要为参数转换类型)。我想,用logprint64代替logprint将花费不到一个小时。

或者,您可以重写logprint。根据您的帖子和回复,logprint似乎采用以下形式:

#include <stdio.h>
#include <stdarg.h>

enum ErrCode { FATAL, MILD };
typedef unsigned short ULONG;

#define MAX 100
char Buf[MAX];

void logprint(enum ErrCode code, char *fmt, ...)
{
    va_list aptr;  
    va_start(aptr, fmt);
    vsprintf(Buf, fmt, aptr);
    va_end(aptr);
}

int main()
{
    ULONG ulMin = 97, ulMax = 99;

    logprint(FATAL,"interval is invalid %c..%c", ulMin, ulMax);
    printf("%s\n", Buf);

    return(0);
}

您可以使用以下模拟vsprintf

的定义替换它
void logprint(enum ErrCode code, const char *fmt, ...)
{   // add your types as needed
    ULONG         h;
    unsigned long u;
    long          d;
    int           i;

    const char   *p;
    char       *buf;
    va_list    argp;
    va_start(argp, fmt);
    for (p = fmt, buf = Buf; *p != '\0'; p++) {
        if (*p != '%') {
            buf += sprintf(buf, "%c", *p); continue;
        }
        switch (*++p) { // change the type casting as needed
            case 'l':
                switch (*++p) {
                    case 'u':
                        u = (unsigned long) va_arg(argp, ULONG);
                        buf += sprintf(buf, "%lu", u); continue;
                    case 'd':
                        d = va_arg(argp, long);
                        buf += sprintf(buf, "%ld", d); continue;
                }
            case 'c':
                u = va_arg(argp, unsigned long);
                buf += sprintf(buf, "%lu", u); continue;
            case 'd':
                i = va_arg(argp, int);
                buf += sprintf(buf, "%d", i); continue;
        }
    }
    va_end(argp);
}

希望这有帮助。