scanf 注册新的转换说明符
scanf register new conversion specifier
我这周写了 printf
系列函数的扩展,以接受 %b
打印二进制文件。为此,我使用了函数 register_printf_specifier()
.
现在我想知道我是否可以在 scanf
函数族中做同样的事情来接受二进制输入并将其写入变量。
是否有任何扩展允许我这样做?
TL;DR: 没有。 使用时至少没有 glibc
.
我下载了最新的 glibc
版本:
% wget https://ftp.gnu.org/gnu/glibc/glibc-2.29.tar.gz
% tar -xzf glibc-2.29.tar.gz
和 grep
'ed find
,搜索我想到的随机 scanf
族函数 - 在这种情况下,它是 vfscanf
:
% find | grep "vfscanf"
根据我的经验,我知道真正的实现在 -internal
中的某处,但我查看了输出:
./stdio-common/iovfscanf.c
./stdio-common/isoc99_vfscanf.c
./stdio-common/vfscanf-internal.c
./stdio-common/vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-isoc99_vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
并决定检查 ./stdio-common/vfscanf.c
,它实际上包含内部函数的存根:
% cat ./stdio-common/vfscanf.c
int
___vfscanf (FILE *s, const char *format, va_list argptr)
{
return __vfscanf_internal (s, format, argptr, 0);
}
接下来,我查看了文件,并找到了格式解析器:
% cat ./stdio-common/vfscanf-internal.c | head -n 1390 | tail -n 20
}
break;
case L_('x'): /* Hexadecimal integer. */
case L_('X'): /* Ditto. */
base = 16;
goto number;
case L_('o'): /* Octal integer. */
base = 8;
goto number;
case L_('u'): /* Unsigned decimal integer. */
base = 10;
goto number;
case L_('d'): /* Signed decimal integer. */
base = 10;
flags |= NUMBER_SIGNED;
goto number;
我查看了文件末尾,找到了一些整理案例标签:
% cat ./stdio-common/vfscanf-internal.c | tail -n 60
++done;
}
}
break;
case L_('p'): /* Generic pointer. */
base = 16;
/* A PTR must be the same size as a `long int'. */
flags &= ~(SHORT|LONGDBL);
if (need_long)
flags |= LONG;
flags |= READ_POINTER;
goto number;
default:
/* If this is an unknown format character punt. */
conv_error ();
}
}
/* The last thing we saw int the format string was a white space.
Consume the last white spaces. */
if (skip_space)
{
do
c = inchar ();
while (ISSPACE (c));
ungetc (c, s);
}
errout:
/* Unlock stream. */
UNLOCK_STREAM (s);
scratch_buffer_free (&charbuf.scratch);
if (__glibc_unlikely (done == EOF))
{
if (__glibc_unlikely (ptrs_to_free != NULL))
{
struct ptrs_to_free *p = ptrs_to_free;
while (p != NULL)
{
for (size_t cnt = 0; cnt < p->count; ++cnt)
{
free (*p->ptrs[cnt]);
*p->ptrs[cnt] = NULL;
}
p = p->next;
ptrs_to_free = p;
}
}
}
else if (__glibc_unlikely (strptr != NULL))
{
free (*strptr);
*strptr = NULL;
}
return done;
}
以及完成功能的代码。这意味着,对于 scanf
系列函数之一,所有格式说明符都是常量,这意味着您不能注册新的处理程序而不会弄乱 glibc 源代码中的大型 clusterf..k(当然不会便携)。
我这周写了 printf
系列函数的扩展,以接受 %b
打印二进制文件。为此,我使用了函数 register_printf_specifier()
.
现在我想知道我是否可以在 scanf
函数族中做同样的事情来接受二进制输入并将其写入变量。
是否有任何扩展允许我这样做?
TL;DR: 没有。 使用时至少没有 glibc
.
我下载了最新的 glibc
版本:
% wget https://ftp.gnu.org/gnu/glibc/glibc-2.29.tar.gz
% tar -xzf glibc-2.29.tar.gz
和 grep
'ed find
,搜索我想到的随机 scanf
族函数 - 在这种情况下,它是 vfscanf
:
% find | grep "vfscanf"
根据我的经验,我知道真正的实现在 -internal
中的某处,但我查看了输出:
./stdio-common/iovfscanf.c
./stdio-common/isoc99_vfscanf.c
./stdio-common/vfscanf-internal.c
./stdio-common/vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-isoc99_vfscanf.c
./sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
并决定检查 ./stdio-common/vfscanf.c
,它实际上包含内部函数的存根:
% cat ./stdio-common/vfscanf.c
int
___vfscanf (FILE *s, const char *format, va_list argptr)
{
return __vfscanf_internal (s, format, argptr, 0);
}
接下来,我查看了文件,并找到了格式解析器:
% cat ./stdio-common/vfscanf-internal.c | head -n 1390 | tail -n 20
}
break;
case L_('x'): /* Hexadecimal integer. */
case L_('X'): /* Ditto. */
base = 16;
goto number;
case L_('o'): /* Octal integer. */
base = 8;
goto number;
case L_('u'): /* Unsigned decimal integer. */
base = 10;
goto number;
case L_('d'): /* Signed decimal integer. */
base = 10;
flags |= NUMBER_SIGNED;
goto number;
我查看了文件末尾,找到了一些整理案例标签:
% cat ./stdio-common/vfscanf-internal.c | tail -n 60
++done;
}
}
break;
case L_('p'): /* Generic pointer. */
base = 16;
/* A PTR must be the same size as a `long int'. */
flags &= ~(SHORT|LONGDBL);
if (need_long)
flags |= LONG;
flags |= READ_POINTER;
goto number;
default:
/* If this is an unknown format character punt. */
conv_error ();
}
}
/* The last thing we saw int the format string was a white space.
Consume the last white spaces. */
if (skip_space)
{
do
c = inchar ();
while (ISSPACE (c));
ungetc (c, s);
}
errout:
/* Unlock stream. */
UNLOCK_STREAM (s);
scratch_buffer_free (&charbuf.scratch);
if (__glibc_unlikely (done == EOF))
{
if (__glibc_unlikely (ptrs_to_free != NULL))
{
struct ptrs_to_free *p = ptrs_to_free;
while (p != NULL)
{
for (size_t cnt = 0; cnt < p->count; ++cnt)
{
free (*p->ptrs[cnt]);
*p->ptrs[cnt] = NULL;
}
p = p->next;
ptrs_to_free = p;
}
}
}
else if (__glibc_unlikely (strptr != NULL))
{
free (*strptr);
*strptr = NULL;
}
return done;
}
以及完成功能的代码。这意味着,对于 scanf
系列函数之一,所有格式说明符都是常量,这意味着您不能注册新的处理程序而不会弄乱 glibc 源代码中的大型 clusterf..k(当然不会便携)。