summaryrefslogtreecommitdiff
path: root/Master/tlpkg/tlperl/lib/pods/perlhacktut.pod
diff options
context:
space:
mode:
Diffstat (limited to 'Master/tlpkg/tlperl/lib/pods/perlhacktut.pod')
-rw-r--r--Master/tlpkg/tlperl/lib/pods/perlhacktut.pod188
1 files changed, 188 insertions, 0 deletions
diff --git a/Master/tlpkg/tlperl/lib/pods/perlhacktut.pod b/Master/tlpkg/tlperl/lib/pods/perlhacktut.pod
new file mode 100644
index 00000000000..33a9ef23e8d
--- /dev/null
+++ b/Master/tlpkg/tlperl/lib/pods/perlhacktut.pod
@@ -0,0 +1,188 @@
+=encoding utf8
+
+=for comment
+Consistent formatting of this file is achieved with:
+ perl ./Porting/podtidy pod/perlhacktut.pod
+
+=head1 NAME
+
+perlhacktut - Walk through the creation of a simple C code patch
+
+=head1 DESCRIPTION
+
+This document takes you through a simple patch example.
+
+If you haven't read L<perlhack> yet, go do that first! You might also
+want to read through L<perlsource> too.
+
+Once you're done here, check out L<perlhacktips> next.
+
+=head1 EXAMPLE OF A SIMPLE PATCH
+
+Let's take a simple patch from start to finish.
+
+Here's something Larry suggested: if a C<U> is the first active format
+during a C<pack>, (for example, C<pack "U3C8", @stuff>) then the
+resulting string should be treated as UTF-8 encoded.
+
+If you are working with a git clone of the Perl repository, you will
+want to create a branch for your changes. This will make creating a
+proper patch much simpler. See the L<perlgit> for details on how to do
+this.
+
+=head2 Writing the patch
+
+How do we prepare to fix this up? First we locate the code in question
+- the C<pack> happens at runtime, so it's going to be in one of the
+F<pp> files. Sure enough, C<pp_pack> is in F<pp.c>. Since we're going
+to be altering this file, let's copy it to F<pp.c~>.
+
+[Well, it was in F<pp.c> when this tutorial was written. It has now
+been split off with C<pp_unpack> to its own file, F<pp_pack.c>]
+
+Now let's look over C<pp_pack>: we take a pattern into C<pat>, and then
+loop over the pattern, taking each format character in turn into
+C<datum_type>. Then for each possible format character, we swallow up
+the other arguments in the pattern (a field width, an asterisk, and so
+on) and convert the next chunk input into the specified format, adding
+it onto the output SV C<cat>.
+
+How do we know if the C<U> is the first format in the C<pat>? Well, if
+we have a pointer to the start of C<pat> then, if we see a C<U> we can
+test whether we're still at the start of the string. So, here's where
+C<pat> is set up:
+
+ STRLEN fromlen;
+ register char *pat = SvPVx(*++MARK, fromlen);
+ register char *patend = pat + fromlen;
+ register I32 len;
+ I32 datumtype;
+ SV *fromstr;
+
+We'll have another string pointer in there:
+
+ STRLEN fromlen;
+ register char *pat = SvPVx(*++MARK, fromlen);
+ register char *patend = pat + fromlen;
+ + char *patcopy;
+ register I32 len;
+ I32 datumtype;
+ SV *fromstr;
+
+And just before we start the loop, we'll set C<patcopy> to be the start
+of C<pat>:
+
+ items = SP - MARK;
+ MARK++;
+ sv_setpvn(cat, "", 0);
+ + patcopy = pat;
+ while (pat < patend) {
+
+Now if we see a C<U> which was at the start of the string, we turn on
+the C<UTF8> flag for the output SV, C<cat>:
+
+ + if (datumtype == 'U' && pat==patcopy+1)
+ + SvUTF8_on(cat);
+ if (datumtype == '#') {
+ while (pat < patend && *pat != '\n')
+ pat++;
+
+Remember that it has to be C<patcopy+1> because the first character of
+the string is the C<U> which has been swallowed into C<datumtype!>
+
+Oops, we forgot one thing: what if there are spaces at the start of the
+pattern? C<pack(" U*", @stuff)> will have C<U> as the first active
+character, even though it's not the first thing in the pattern. In this
+case, we have to advance C<patcopy> along with C<pat> when we see
+spaces:
+
+ if (isSPACE(datumtype))
+ continue;
+
+needs to become
+
+ if (isSPACE(datumtype)) {
+ patcopy++;
+ continue;
+ }
+
+OK. That's the C part done. Now we must do two additional things before
+this patch is ready to go: we've changed the behaviour of Perl, and so
+we must document that change. We must also provide some more regression
+tests to make sure our patch works and doesn't create a bug somewhere
+else along the line.
+
+=head2 Testing the patch
+
+The regression tests for each operator live in F<t/op/>, and so we make
+a copy of F<t/op/pack.t> to F<t/op/pack.t~>. Now we can add our tests
+to the end. First, we'll test that the C<U> does indeed create Unicode
+strings.
+
+t/op/pack.t has a sensible ok() function, but if it didn't we could use
+the one from t/test.pl.
+
+ require './test.pl';
+ plan( tests => 159 );
+
+so instead of this:
+
+ print 'not ' unless "1.20.300.4000" eq sprintf "%vd",
+ pack("U*",1,20,300,4000);
+ print "ok $test\n"; $test++;
+
+we can write the more sensible (see L<Test::More> for a full
+explanation of is() and other testing functions).
+
+ is( "1.20.300.4000", sprintf "%vd", pack("U*",1,20,300,4000),
+ "U* produces Unicode" );
+
+Now we'll test that we got that space-at-the-beginning business right:
+
+ is( "1.20.300.4000", sprintf "%vd", pack(" U*",1,20,300,4000),
+ " with spaces at the beginning" );
+
+And finally we'll test that we don't make Unicode strings if C<U> is
+B<not> the first active format:
+
+ isnt( v1.20.300.4000, sprintf "%vd", pack("C0U*",1,20,300,4000),
+ "U* not first isn't Unicode" );
+
+Mustn't forget to change the number of tests which appears at the top,
+or else the automated tester will get confused. This will either look
+like this:
+
+ print "1..156\n";
+
+or this:
+
+ plan( tests => 156 );
+
+We now compile up Perl, and run it through the test suite. Our new
+tests pass, hooray!
+
+=head2 Documenting the patch
+
+Finally, the documentation. The job is never done until the paperwork
+is over, so let's describe the change we've just made. The relevant
+place is F<pod/perlfunc.pod>; again, we make a copy, and then we'll
+insert this text in the description of C<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.
+
+=head2 Submit
+
+See L<perlhack> for details on how to submit this patch.
+
+=head1 AUTHOR
+
+This document was originally written by Nathan Torkington, and is
+maintained by the perl5-porters mailing list.