目錄

名稱

perlhacktut - 透過簡單的 C 代碼修補進程

描述

本文將帶您通過一個簡單的補丁示例。

如果您還沒有閱讀過 perlhack,請先閱讀!您可能也想通讀 perlsource

在這裡完成後,請查看 perlhacktips

簡單補丁的示例

讓我們從頭到尾進行一個簡單的補丁。

這是 Larry 建議的一些內容:如果 Upack 中的第一個活動格式(例如 pack "U3C8", @stuff),則結果字符串應該被視為 UTF-8 編碼。

如果您正在使用 Perl 存儲庫的 git 克隆,您將希望為您的更改創建一個分支。這將使創建正確的補丁變得更加簡單。有關如何執行此操作的詳細信息,請參見 perlgit

撰寫補丁

我們如何準備修復這個問題?首先,我們找到問題代碼 - pack 是在運行時發生的,所以它會在其中一個 pp 檔案中。果然,pp_packpp.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 成為第一個活動字符,即使它不是模式中的第一個。在這種情況下,我們必須在看到空格時,將 patcopypat 一起前進

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郵件列表維護。