[QUIZ] Whiteout (#34)

R

Ruby Quiz

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Perl programmers have all the fun. They have an entire section of the CPAN
devoted to their playing around. The ACME modules are all fun little toys that
have interesting if rarely very useful effects.

This week's Ruby Quiz is to port ACME::Bleach to Ruby. I won't make you go
hunting through the CPAN to figure it out though. Here's how our version will
work:

1. Make a Ruby file that is both an executable and a library. We'll call
it "whiteout".
2. When "whiteout" is executed, it should take ARGV to be a list of Ruby
source code files to alter in-place. (You may save backup copies if you
like, but the original file should be changed.) Here are the changes:
a. A Shebang line, if present is to be passed through the filter
without any changes.
b. The script should then add the line: require "whiteout"
c. The entire rest of the file should be made invisible. You might do
this by converting the rest of the file to whitespace, as
ACME::Bleach does.
3. When "whiteout" is required, the original code must be executed with no
change in functionality.

Let's show those Perl guys that we know how to have a good time too!
 
D

Dave Burt

3. When "whiteout" is required, the original code must be executed with
no
change in functionality.

That means that a file that is just "require 'whiteout'" and then a bunch of
whitespace should run like the original program before it was whited out.

Perl has something called "source filters" (see perl doco "perlfilter"),
which apply a transformation to following source code, kind of like a
preprocessor. This would be an ideal way of dealing with this situation, and
avoid doing File.read($0).

Is there a way to do that in Ruby? Can a C extension concievably alter
following code before it is parsed? I think I'm expecting a simple "no".

Cheers,
Dave
 
F

Florian Groß

Dave said:
Is there a way to do that in Ruby? Can a C extension concievably alter
following code before it is parsed? I think I'm expecting a simple "no".

If you are allowed to alter the shebang then you can append -ryourlib
and steal STDIN from Ruby and use eval() to execute the filtered code.
 
J

James Edward Gray II

That means that a file that is just "require 'whiteout'" and then a
bunch of
whitespace should run like the original program before it was
whited out.
Correct.

Perl has something called "source filters" (see perl doco
"perlfilter"),
which apply a transformation to following source code, kind of like a
preprocessor. This would be an ideal way of dealing with this
situation, and
avoid doing File.read($0).

Yeah, this is an interesting if probably evil feature. I wasn't sure
how feasible something like this was, or wasn't in Ruby. I would
like to see a source filter library for Ruby, even if it is bad style.

James Edward Gray II
 
J

James Edward Gray II

Dave Burt wrote:



If you are allowed to alter the shebang then you can append -=20
ryourlib and steal STDIN from Ruby and use eval() to execute the =20
filtered code.

Hmm, I hadn't considered something like this. Since:

#!/usr/local/bin/ruby -rwhiteout

is basically:

#!/usr/local/bin/ruby

require "whiteout"

I'm okay with it if you want to try something like this.

James Edward Gray II=
 
E

Eric Hodel

Yeah, this is an interesting if probably evil feature. I wasn't
sure how feasible something like this was, or wasn't in Ruby. I
would like to see a source filter library for Ruby, even if it is
bad style.

I believe ParseTree can provide some of that for you.
 
L

Logan Capaldo

=20
That means that a file that is just "require 'whiteout'" and then a bunch= of
whitespace should run like the original program before it was whited out.
=20
Perl has something called "source filters" (see perl doco "perlfilter"),
which apply a transformation to following source code, kind of like a
preprocessor. This would be an ideal way of dealing with this situation, = and
avoid doing File.read($0).
=20
Is there a way to do that in Ruby? Can a C extension concievably alter
following code before it is parsed? I think I'm expecting a simple "no".
=20
Cheers,
Dave
=20
=20
=20
=20

The problem with doing source filters in ruby is that 'use' doesn't
really work the same way as require. Things that are 'use'd get looked
at BEFORE perl reads the rest of the file. This means that this will
let you do things like have invalid syntax before the source filter
gets to it. In contrast, require in ruby (and in perl btw) occurs more
on the runtime side of the compile --- runtime scale. Same thing with
ruby's BEGIN blocks. Which is why perl will let you do this:

$ cat test.pl
#!/usr/bin/env perl
BEGIN { exit 0; }
dasdadadaddada =3D 33423q3412 -,_1+e=3D3

$ perl test.pl
$

Even more amusing:
$ perl -c test.pl
test.pl syntax OK

In ruby however...
$ cat test.rb
#!/usr/bin/env ruby
BEGIN { exit 0 }
fsdnddfqw,+-};

$ ruby test.rb
test.rb:2: syntax error
fsdnddfqw,+-};
^

You can possibly do it with soemthing like this:
$ cat runaway.rb
exit 0

$ ruby -rrunaway test.rb
$

But to use this to create a source filter requires smarter people than me.
 
B

Brian Schröder

Hello Group,

first let me thank you for this quiz. It is a hillarious idea. Never
would have come up with this by myself and it is really cool to see
the sourcecode vanish and reappear as if by magic.

I first thought to modify the whitout library such, that it includes a
loaded program, but then came to the solution to encode the file in
the whitespace. The simplest encoding / decoding routines are:

# Encode a string into whitespace
def encode(string)
string.unpack('B*')[0].tr('01', " \n")
end

# Decode a whitespace encoded string
def decode(string)
[string.tr(" \n", '01')].pack('B*')
end

But these make the files quite big. So I added two thing:
- Encoding into a eight-sign code with eight different whitespace
symbols that ruby doesn't choke on. This may show as non-whitespace in
something like an editor or "less", but using "cat" its only
whitespace.
- On the fly zipping of the files before encoding.

The encoding of a encoded file is detected by a four byte whitespace
header, so everything works out automatically.

I also added a nice user interface using optparse. Though there is
some optparse bashing going on at the moment I think it is really a
great library.

You can find my solution at:
http://ruby.brian-schroeder.de/quiz/whiteout/

Have fun with the solution and please don't come after me if you have
converted all your ruby code to whitespace and can't get it back.

--=20
http://ruby.brian-schroeder.de/

Guitar chords in different tunings: http://chordlist.brian-schroeder.de/gui=
tar/
 
M

Matthew D Moss

I thought of adding more bits by using other whitespace characters, but
in the end stuck with space and tab, since everything will render them
as whitespace (whereas almost any other whitespace I tried showed up as
garbage in one editor/reader or another). Also, I didn't use linefeed
nor carriage return, since non-binary transmission from one system to
another could potentially break the file (ie, eol conversion).

Anyway, here's my solution. I tried to keep it short and simple but
should still be easily understood.


#!/usr/bin/env ruby

Bits = '01'
Blank = " \t"

def shebang(f)
f.pos = 0 unless f.gets =~ /^#!/
end

def confuse(fname)
File.open(fname, File::RDWR) do |f|
shebang f
f.pos, data = f.pos, f.read
f.puts "require '#{File.basename($0, '.*')}'"
f.write data.unpack('b*').join.tr(Bits, Blank)
f.truncate f.pos
end
end

def clarify(fname)
File.open(fname, File::RDONLY) do |f|
shebang f
f.gets # skip require 'whiteout'
eval [f.read.tr(Blank, Bits)].pack('b*')
end
end

if __FILE__ == $0
ARGV.each { |fname| confuse fname }
else
clarify($0)
end
 
M

Matthew D Moss

I just realized I can shave this down by one expression by removing
"f.truncate f.pos". I put that in there to esure that, if the
"confused" version were shorter, the file would have the proper length.
However, since the file is always expanded by this algorithm, there's
no need for truncate.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top