目前,glibc source of perror中的逻辑是这样的:
如果面向stderr
,请按原样使用,否则使用dup()
并使用perror()
'dup()
fd
上的stderr
。
如果size_t len = strlen (fmt) + 1;
wchar_t wfmt[len];
for (size_t i = 0; i < len; ++i)
{
assert (isascii (fmt[i]));
wfmt[i] = fmt[i];
}
res = __vfwprintf (fp, wfmt, ap);
面向广泛,则使用stdio-common/fxprintf.c中的以下逻辑:
wfmt[i] = fmt[i];
格式字符串通过以下代码转换为宽字符形式,我不明白:
isascii
此外,它使用assert (isascii(fmt[i]));
断言:
#include <stdio.h>
#include <errno.h>
#include <wchar.h>
#include <locale.h>
int main(void)
{
setlocale(LC_CTYPE, "en_US.UTF-8");
fwide(stderr, 1);
errno = EINVAL;
perror("привет мир"); /* note, that the string is multibyte */
return 0;
}
但格式字符串在宽字符程序中并不总是ascii,因为我们可能使用UTF-8格式字符串,它可以包含非7位值。 为什么在运行以下代码时没有断言警告(假设UTF-8语言环境和UTF-8编译器编码)?
$ ./a.out
привет мир: Invalid argument
dup()
我们可以在面向广角的stderr
上使用perror()
来使其不是面向广播的吗?在这种情况下,可以在不使用这种神秘转换的情况下重写代码,同时考虑到#include <stdio.h>
#include <wchar.h>
#include <unistd.h>
int main(void)
{
fwide(stdout,1);
FILE *fp;
int fd = -1;
if ((fd = fileno (stdout)) == -1) return 1;
if ((fd = dup (fd)) == -1) return 1;
if ((fp = fdopen (fd, "w+")) == NULL) return 1;
wprintf(L"stdout: %d, dup: %d\n", fwide(stdout, 0), fwide(fp, 0));
return 0;
}
只接受多字节字符串(const char * s)和语言环境消息都是多字节的事实。
原来我们可以。以下代码演示了这一点:
$ ./a.out
stdout: 1, dup: 0
dup()
BTW,是否值得向glibc开发人员提出有关此改进的问题?
注意
使用perror()
在缓冲方面受到限制。我想知道在glibc中#include <wchar.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
wint_t wc = L'b';
fwprintf(stdout, L"%lc", wc);
/* --- */
FILE *fp;
int fd = -1;
if ((fd = fileno (stdout)) == -1) return 1;
if ((fd = dup (fd)) == -1) return 1;
if ((fp = fdopen (fd, "w+")) == NULL) return 1;
char c = 'h';
fprintf(fp, "%c\n", c);
return 0;
}
的实现是否考虑过它。以下示例演示了此问题。
输出不按写入流的顺序进行,而是按照写入缓冲区中数据的顺序进行。
注意,输出中的值的顺序与程序中的顺序不同,因为fprintf的输出首先被注销(因为“\ n”),并且当程序退出时fwprintf的输出被注销。
$ ./a.out
h
b
\n
但如果我们在fwprintf中使用$ ./a.out
b
h
,则输出与程序中的输出相同:
perror()
stderr
设法逃脱了,因为在GNU中,libc stderr
是无缓冲的。但它会在diff -urN glibc-2.24.orig/stdio-common/perror.c glibc-2.24/stdio-common/perror.c
--- glibc-2.24.orig/stdio-common/perror.c 2016-08-02 09:01:36.000000000 +0700
+++ glibc-2.24/stdio-common/perror.c 2016-10-10 16:46:03.814756394 +0700
@@ -36,7 +36,7 @@
errstring = __strerror_r (errnum, buf, sizeof buf);
- (void) __fxprintf (fp, "%s%s%s\n", s, colon, errstring);
+ (void) _IO_fprintf (fp, "%s%s%s\n", s, colon, errstring);
}
@@ -55,7 +55,7 @@
of the stream. What is supposed to happen when the stream isn't
oriented yet? In this case we'll create a new stream which is
using the same underlying file descriptor. */
- if (__builtin_expect (_IO_fwide (stderr, 0) != 0, 1)
+ if (__builtin_expect (_IO_fwide (stderr, 0) < 0, 1)
|| (fd = __fileno (stderr)) == -1
|| (fd = __dup (fd)) == -1
|| (fp = fdopen (fd, "w+")) == NULL)
被手动设置为缓冲模式的程序中安全地工作吗?
这是我向glibc开发人员提出的补丁:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
id presentedViewController = [window.rootViewController presentedViewController];
NSString *className = presentedViewController ? NSStringFromClass([presentedViewController class]) : nil;
if (window && ([className isEqualToString:@"MPInlineVideoFullscreenViewController"] || [className isEqualToString:@"AVFullScreenViewController"])) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
答案 0 :(得分:1)
它使用isascii断言。
这没关系。你不应该调用这个函数。这是一个glibc内部。请注意名称前面的两个下划线。从perror调用时,有问题的参数是"%s%s%s\n"
,它完全是ASCII。
但格式字符串在宽字符程序中并不总是ascii,因为我们可能使用UTF-8
首先,UTF-8与宽字符无关。其次,格式字符串始终是ASCII,因为该函数仅由知道它们正在做什么的其他glibc函数调用。
perror(“приветмир”);
这不是格式字符串,这是与实际格式字符串中%s
之一对应的参数之一。
我们可以在面向广角的stderr
上使用dup()
您无法在dup
上使用FILE*
,它可以在POSIX上运行
没有方向的文件描述符。
这是我向glibc开发人员提出的补丁:
为什么呢?什么不起作用?
答案 1 :(得分:1)
注意:在这篇文章中找到具体问题并不容易;总的来说,这个帖子似乎是试图参与讨论glibc的实现细节,在我看来,它会更好地针对专门面向该库开发的论坛,如libc-alpha mailing list。 (或者参见https://www.gnu.org/software/libc/development.html了解其他选项。)这种讨论与StackOverflow,恕我直言并不是很好的匹配。尽管如此,我试图回答我能找到的问题。
wfmt[i] = fmt[i];
如何从多字节转换为宽字符?实际上,代码是:
assert(isascii(fmt[i]));
wfmt[i] = fmt[i];
基于ascii字符的数值与wchar_t
相同的事实。严格来说,情况并非如此。 C标准规定:
如果实现未定义
__STDC_MB_MIGHT_NEQ_WC__
,则在用作整数字符常量中的单个字符时,基本字符集的每个成员的代码值应等于其值。 (§7.19/ 2)
(gcc没有定义该符号。)
但是,这仅适用于基本集中的字符,而不适用于isascii
识别的所有字符。基本字符集包含91个可打印的ascii字符以及空格,换行符,水平制表符,垂直制表符和换页符。因此理论上可能无法正确转换其中一个剩余控制字符。但是,对__fxprintf
的调用中使用的实际格式字符串仅包含基本字符集中的字符,因此在实践中,这种迂腐的细节并不重要。
perror("привет мир");
时没有断言警告?因为只转换格式字符串,格式字符串("%s%s%s\n"
)仅包含ascii字符。由于格式字符串包含%s
(而不是%ls
),因此在窄字符和宽字符方向中,参数应为char*
(而不是wchar_t*
)
dup()
来使它不是面向广的吗?这不是一个好主意。首先,如果流具有方向,则它也可能具有非空的内部缓冲区。由于该缓冲区是stdio库的一部分而不是底层Posix fd的一部分,因此它不会与重复的fd共享。因此,perror打印的消息可能会在某些现有输出的中间进行插值。另外,多字节编码可能具有移位状态,并且输出流当前不处于初始移位状态。在这种情况下,输出ascii序列可能会导致输出乱码。
在实际实现中,只在没有方向的流上执行dup;这些流从来没有任何针对它们的输出,因此它们肯定仍然处于初始移位状态,并且具有空缓冲区(如果流被缓冲)。
这取决于你,但不要在这里做。这样做的正常方法是提交bug。没有理由相信glibc开发人员阅读SO问题,即使他们这样做,也有人必须将问题复制到bug中,并复制任何提议的补丁。