perlhacktut - 简单的 C 代码补丁创建过程
本文档将带您逐步了解一个简单的补丁示例。
如果您还没有阅读 perlhack,请先阅读!您可能还想阅读 perlsource。
完成本文档后,请继续阅读 perlhacktips。
让我们从头到尾完成一个简单的补丁。
Larry 建议:如果在 pack
操作中,U
是第一个活动的格式(例如,pack "U3C8", @stuff
),那么生成的字符串应被视为 UTF-8 编码。
如果您正在使用 Perl 代码库的 git 克隆,您需要为您的更改创建一个分支。这将使创建正确的补丁变得更加简单。有关如何执行此操作的详细信息,请参阅 perlgit。
我们如何准备修复这个问题?首先,我们找到相关的代码 - pack
在运行时发生,因此它将位于某个 pp 文件中。果然,pp_pack
位于 pp.c 中。由于我们将要修改此文件,因此让我们将其复制到 pp.c~ 中。
[嗯,在编写本教程时,它位于 pp.c 中。现在它已经与 pp_unpack
分开到自己的文件 pp_pack.c 中了]
现在让我们看一下 pp_pack
:我们将一个模式放入 pat
中,然后循环遍历该模式,依次将每个格式字符放入 datum_type
中。然后,对于每个可能的格式字符,我们都会吞掉模式中的其他参数(字段宽度、星号等),并将下一个输入块转换为指定的格式,并将其添加到输出 SV cat
中。
我们如何知道 U
是否是 pat
中的第一个格式?嗯,如果我们有一个指向 pat
开头的指针,那么如果我们看到一个 U
,我们可以测试我们是否仍然位于字符串的开头。因此,以下是 pat
的设置位置
STRLEN fromlen;
char *pat = SvPVx(*++MARK, fromlen);
char *patend = pat + fromlen;
I32 len;
I32 datumtype;
SV *fromstr;
我们将在这里添加另一个字符串指针
STRLEN fromlen;
char *pat = SvPVx(*++MARK, fromlen);
char *patend = pat + fromlen;
+ char *patcopy;
I32 len;
I32 datumtype;
SV *fromstr;
在开始循环之前,我们将 patcopy
设置为 pat
的开头
items = SP - MARK;
MARK++;
SvPVCLEAR(cat);
+ patcopy = pat;
while (pat < patend) {
现在,如果我们看到一个位于字符串开头的 U
,我们将为输出 SV cat
打开 UTF8
标志
+ if (datumtype == 'U' && pat==patcopy+1)
+ SvUTF8_on(cat);
if (datumtype == '#') {
while (pat < patend && *pat != '\n')
pat++;
请记住,它必须是 patcopy+1
,因为字符串的第一个字符是 U
,它已经被吞入 datumtype!
中了
糟糕,我们忘记了一件事:如果模式开头有空格怎么办?pack(" U*", @stuff)
将以 U
作为第一个活动字符,即使它不是模式中的第一个字符。在这种情况下,当我们看到空格时,我们必须将 patcopy
与 pat
一起前进
if (isSPACE(datumtype))
continue;
需要变为
if (isSPACE(datumtype)) {
patcopy++;
continue;
}
好了。C 部分完成了。现在,在我们准备好发布此补丁之前,我们必须再做两件事:我们已经改变了 Perl 的行为,因此我们必须记录该更改。我们还必须提供更多回归测试,以确保我们的补丁有效,并且不会在其他地方产生错误。
每个操作符的回归测试都位于t/op/中,因此我们将t/op/pack.t复制到t/op/pack.t~。现在,我们可以将测试添加到末尾。首先,我们将测试U
是否确实创建了 Unicode 字符串。
t/op/pack.t 有一个合理的 ok() 函数,但如果它没有,我们可以使用 t/test.pl 中的函数。
require './test.pl';
plan( tests => 159 );
所以,与其这样
print 'not ' unless "1.20.300.4000" eq sprintf "%vd",
pack("U*",1,20,300,4000);
print "ok $test\n"; $test++;
我们可以写出更合理的代码(有关 is() 和其他测试函数的完整说明,请参阅 Test::More)。
is( "1.20.300.4000", sprintf "%vd", pack("U*",1,20,300,4000),
"U* produces Unicode" );
现在,我们将测试我们是否正确地处理了开头空格的问题
is( "1.20.300.4000", sprintf "%vd", pack(" U*",1,20,300,4000),
" with spaces at the beginning" );
最后,我们将测试如果U
不是第一个活动格式,我们不会创建 Unicode 字符串
isnt( v1.20.300.4000, sprintf "%vd", pack("C0U*",1,20,300,4000),
"U* not first isn't Unicode" );
不要忘记更改顶部出现的测试数量,否则自动测试器会感到困惑。它看起来像这样
print "1..156\n";
或者这样
plan( tests => 156 );
现在,我们编译 Perl,并通过测试套件运行它。我们的新测试通过了,万岁!
最后,是文档。在完成文书工作之前,工作永远不会完成,所以让我们描述一下我们刚刚做出的更改。相关的地方是pod/perlfunc.pod;同样,我们做一个副本,然后在pack
的描述中插入这段文字
=item *
If the pattern begins with a C<U>, the resulting string will be treated
as UTF-8-encoded Unicode. You can force UTF-8 encoding on in a string
with an initial C<U0>, and the bytes that follow will be interpreted as
Unicode characters. If you don't want this to happen, you can begin
your pattern with C<C0> (or anything else) to force Perl not to UTF-8
encode your string, and then follow this with a C<U*> somewhere in your
pattern.
有关如何提交此补丁的详细信息,请参阅 perlhack。
本文档最初由 Nathan Torkington 编写,并由 perl5-porters 邮件列表维护。