Here's a JAPH and explanation from the Perl-Win32-Users mailing list -
this has some quirks. On win7, I got back "steal Porch Junk" on some
versions of perl you get a newline, on some you get a syntax error but the
explanation is great!:
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[..$..$_[10].$_[2].q.x..q.($1)]./gee/print
> That's one convoluted JAPH!
Thanks.
> I like to pick through admirable JAPH's like this in an
> attempt to learn more Perl idiosyncrasies [...]
> Anyone care to explain what's going on?
How about I tell you how I created it, which I think makes it easier to
see
what's going on.
Every JAPH starts out with a small idea on some transormation of the
phrase
"Just Another Perl Hacker". I noticed there are repeated letters in that
phrase, so I decided to collapse the phrase down to just the unique
letters
and then rebuild the phrase by indexing them.
# Reduce to unique characters
$japh = 'Just another Perl hacker';
$letter{$_}++ foreach split//, $japh;
print join "", sort keys %letter;
So now I'm working with the string " JPacehklnorstu". But that string
doesn't have to be in any particular order, so it's off to the Internet
Anagram Server (
http://www.wordsmith.org/anagram/) to make things
interesting. There are lots of funny results, and I ended up making a few
different versions of the JAPH with them. The one you saw was "steal Porch
Junk".
So now, I just create a list of indices to extract the proper phrase back
out. I could simply use the following:
@indices = (12,13,0,1,5,3,14,7,1,10,2,8,5,6,2,8,4,5,10,3,9,15,2,8);
But that would be boring. And take up quite a bit of space. I noticed that
the range is 0..15 or one nybble (half a byte). I could pack all 24
indices
in just 12 bytes! I decided to represent the bytes as hex characters:
# pack 'em into bytes.
while (@indices) {
$byte = (shift(@indices)<<4)|shift(@indices);
printf('%x',$byte);
}
So now I'm working with the string "cd0153e71a28562845a39f28". To extract
the indices, I just have to use hex() on each character in that string.
So now, let's put together a first cut at the JAPH, using the techniques
above:
$_="steal Porch Junk"; @_=split //; # put the letters in @_
$_="cd0153e71a28562845a39f28"; # put the indices in $_
s/(.)/$_[hex($1)]/ge; # replace each index with its
letter
print; # print it out
Now the fun part... let's obfuscate it a bit. Adding another "e" to the
regex is low-hanging fruit. We just assemble the '$_[hex($1)]' part via
concatenated strings:
s/(.)/'$_[' . 'hex' . '($1)]'/gee;
I don't like the quotes. Too obvious. Lets change them to q() but use '.'
instead of the parens (anyone who gets confused by this needs to read
perldoc perlop):
s/(.)/q.$_[. . q.hex. . q.($1)]./gee;
Looking better, but that 'hex' stands out like a sore thumb. Noticing that
the "h" and "e" are in our letter list (@_), I decide to assemble the
'hex',
which is just a string at this point, from those letters ("h" = $_[10],
"e"
= $_[2]), leaving the 'x':
s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee
Better. Now let's get rid of the quotes from the rest of the JAPH too,
using
q.. and q==
$_=q;steal Porch Junk;;split //;
$_=q=cd0153e71a28562845a39f28=;
s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
print;
Now let's break up that hex string in the second line. I see it has three
"28"'s, so we can do a substitution. What should we replace it with? The
string "s/" is about as diabolical as any. ";q" would have been a good one
too. I chose the former. In our y// (or y;; to be tricky) lets also
replace
the "a"'s with ";". We'll also insert a newline, which we take back out
with
a s/\n//;
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;
y;\;s/;a28;;
s;\n;;;
s/(.)/q.$_[. . $_[10] . $_[2] . q.x. . q.($1)]./gee;
print;
Now let's smash it up a bit:
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28;;s;\n;;;
s/(.)/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;
Looking kinda JAPHy now, isn't it? There's a few more things we can do.
The
y operator can take extra chars at the end without consequence. We'll add
another decoy 's/'. In the first substitution, let's replace '\n' with
'$/'.
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;
s((.))/q.$_[..$_[10].$_[2].q.x..q.($1)]./gee;print;
Now, let's work on the substitution again. We can split it between lines,
and change s/// to s()//. Also we can change the ';' before print to a
'/',
which is interpreted as a divide, but will still evaluate the 'print' with
a
warning under -w which we don't care about.
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;s;$/;;;s((.))/
q.$_[..$_[10].$_[2].q.x..q.($1)]./gee/print;
More stuff: Let's insert some ';' where they won't make a difference, and
remove one from the end. Then I want to pad the last line by 2 chars
because
I like to make the lines equal length. We can add $. (which contains
nothing
outside a loop) to the substitution string.
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[.$..$_[10].$_[2].q.x..q.($1)]./gee/print;
More can always be done, but at this point it looks good enough for me.
Hope you found this interesting.
--
Mark Thomas
Thoma...@bls.gov
Internet Systems Architect DigitalNet, Inc.
$_=q;steal Porch Junk;;split//;$_=q=cd0153e71;
s/56s/45;39fs/=;y;\;s/;a28s/;;;s;$/;;;s((.))/;
q.$_[..$..$_[10].$_[2].q.x..q.($1)]./gee/print
_______________________________________________
----------------------
Andy Bach
Systems Mangler
Internet:
andy...@wiwb.uscourts.gov
Voice:
(608) 261-5738, Cell:
(608) 658-1890
We are what we pretend to be, so we must be careful about what we
pretend to be.
Kurt Vonnegut