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++;
文字列の最初の文字は datumtype
に飲み込まれたU
なので、 これは patcopy+1
でなければならないことを覚えておいてください!
おっと、ひとつ忘れていました: パターンの先頭にスペースがあったら? pack(" U*", @stuff)
は、パターンの先頭ではないにもかかわらず、 最初のアクティブな文字として U
を持ちます。 この場合、スペースがあるときは、pat
とともに patcopy
を 進める必要があります:
if (isSPACE(datumtype))
continue;
これは次のようにする必要があります:
if (isSPACE(datumtype)) {
patcopy++;
continue;
}
OK。 これで 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
が not である場合、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 を参照してください。
This document was originally written by Nathan Torkington, and is maintained by the perl5-porters mailing list.