perlhacktut - 透過簡單的 C 代碼修補進程
本文將帶您通過一個簡單的補丁示例。
如果您還沒有閱讀過 perlhack,請先閱讀!您可能也想通讀 perlsource。
在這裡完成後,請查看 perlhacktips。
讓我們從頭到尾進行一個簡單的補丁。
這是 Larry 建議的一些內容:如果 U
是 pack
中的第一個活動格式(例如 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
,因為字符串的第一個字符是已經被吞入 datumtype!
的 U
哎呀,我們忘了一件事:如果模式開頭有空格怎麼辦?像 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++;
我們可以寫得更合理(請參考Test::More了解is()和其他測試函數的完整說明)。
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郵件列表維護。