我可以使用perror()
或strerror()
打印属于errno
的“人类可读”错误消息,但是如果我 * 还 * 想打印errno
的符号名称(例如,“EAGAIN
“),该怎么办?
有没有方便的函数或宏来做这件事?
编辑未来:你可能最好使用公认答案中引用的库: 附上一些我用过一段时间的hacky代码,基于下面答案的想法和它的评论:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
int get_errno_name(char *buf, int buf_size) {
// Using the linux-only gawk instead of awk, because of the convenient
// match() functionality. For Posix portability, use a different recipe...
char cmd[] = "e= && " // errno to be inserted here (max digits = 6)
"echo '#include <errno.h>' | "
"gcc -dM -E - | " // optionally, use $CC inead of gcc
"gawk \"match(\\$0, /^#[[:space:]]*define[[:space:]]+"
"(E[[:alnum:]]+)[[:space:]]+$e($|[^[:alnum:]])/, m) "
"{ print m[1] }\"";
{
// Insert the errno as the "e" shell variable in the command above.
int errno_digit_c = snprintf(cmd + 2, 6, "%d", errno);
if (errno_digit_c < 1) {
fprintf(stderr, "Failed to stringify an errno "
"in get_errno_name().\n");
return -1;
}
// Replace the inserted terminating '\0' with whitespace
cmd[errno_digit_c + 2] = ' ';
}
FILE *f = popen(cmd, "r");
if (f == NULL) {
perror("Failed to popen() in get_errno_name()");
return -1;
}
int read_size = 0, c;
while ((c = getc(f)) != EOF) {
if (isalnum(c)) {
buf[read_size++] = c;
if (read_size + 1 > buf_size) {
fprintf(stderr, "Read errno name is larger than the character "
"buffer supplied to get_errno_name().\n");
return -1;
}
}
}
buf[read_size++] = '\0';
if (pclose(f) == -1) {
perror("Failed to pclose() in get_errno_name()");
return -1;
}
return read_size;
}
7条答案
按热度按时间gdx19jrr1#
我最近写了一个the
errnoname
library,它有一个errnoname
函数,可以让你这样做。不幸的是,* 仍然 * 没有标准的API来做这件事。
我的目标是让你可以在任何系统上使用,同时覆盖该系统上所有的
errno
名称。到目前为止,包括Linux,达尔文(macOS,iOS等),FreeBSD,NetBSD,OpenBSD,DragonflyBSD,几个闭源Unix,Windows和其他一些系统。正如其他答案所示,唯一“困难”的部分是获取
errno
名称和值的列表以执行查找,了解目标系统上定义了哪些errno
,并彻底处理多个errno
名称可能Map到同一个编号的所有情况。因此,在
errnoname
库中,您不必处理任何这些内容。我将许多操作系统的errno
名称收集到一个列表中,作为一个单独的步骤,然后从中生成实际发布的C代码。C代码使用#ifdef
表示每个名称,因此它可以在任何系统上编译,而不管系统具有哪些errno
名称和值。t必须调用任何程序或在库标题中搜索errno
。无论如何,因为它是在“零条款BSD许可证”(0 BSD)下发布的,这是一个许可证,或者更准确地说,是一个公共领域等效许可证,所以您可以对它做任何您想做的事情。
下面是从我的库中复制粘贴的函数,所以这个答案是有用的,即使我的repo链接死了。
1.这里的副本只涵盖了我能找到的所有
errno
名称 * 截至2019年8月初 * -从那以后我找到了其他名称。1.如果您为它提供一个它不知道名称的
errno
值,它将返回一个NULL
指针。1.我会不断更新库以包含
errno
名称,因为我发现它们,系统添加它们,添加其他改进,并添加重复保护,因为可能的重复被发现。但我不会(和最后我检查,不能,由于大小限制的答案)保持这个答案的副本更新。1zmg4dgp2#
要做到这一点并不简单。
您可以创建一个程序--我已经创建了一个,它可以被重新打包为一个库函数--将数字转换为名称。但是生成表比较困难。我使用Perl脚本运行编译器(GCC或同等),带选项(
-H
)以列出通过包含/usr/include/errno.h
而包含的标题,然后扫描这些文件,查找名称(#define
加上E
,后面跟一个大写字母或数字)、数字和注解。这可以在Linux、Mac OS X、Solaris、HP-UX和AIX上工作。它不是特别琐碎。注意,MacOSX上的
errno.h
包含一个名称ELAST
(为实现保留的名称),它是最大数字的副本(但是Map随版本而变化;在山狮是105,我相信,但在小牛是106)。7eumitmz3#
glibc为此提供了一个函数(GNU扩展):
strerrorname_np()
.它在
<string.h>
中声明。对于有效值,它返回
errno
名称;对于无效值,它返回NULL
名称。man strerrorname_np
它是在glibc 2.32中添加的。
inb24sb24#
据我所知,你不能。一些整型错误常量被Map到一个以上的符号名,例如EAGAIN和EWOULDBLOCK。显然,你可以在设置errno的命令的手册页上找到它们。
pxyaymoc5#
如果您知道您预期会出现什么错误,您可以针对
errno
编写大的switch/case
或if/else
块,样式如下:这样做的一个明显问题是,如果您想要特定的errno 'name',则需要针对每个可能的选项进行编写,而选项非常多。
nnvyjq4y6#
我不知道有哪个函数能做到这一点,但是如果您熟悉程序中返回的错误,编写一个这样的函数并不难。
你可以编写包含strror()输出的字符串,并使用for循环和if语句与strcmp()(它根据成功或失败返回一个值)来比较你编写的字符串,如果它们相同,你可以输出符号名 *,如果你把它设置为另一个字符串。
t2a7ltrp7#
由于符号名是以枚举形式存储的,
C
将其视为Ints
,因此您必须创建一个类似于此SO问题How to convert enum names to string in c的函数。您可以很容易地将其缩短为一个宏,但必须为每个错误创建一个用例。