[SPOILER] Re: [Israel.pm] What does this code do? (No. 3)

Shlomi Fish shlomif at iglu.org.il
Sun Jul 11 06:45:33 PDT 2004


On Sunday 11 July 2004 14:50, Yosef Meller wrote:
> Shlomi Fish wrote:
>  > Here is the next installment in the "What does this code do?" series of
>  > quizzes. You can post solutions to the list, but mark them with a
>
> [SPOILER]
>
>  > in the title. Other discussions are also welcome.
>  >
>  > The script in question is:
>  >
>  > <<<
>  > #!/usr/bin/perl -w
>  >
>  > use strict;
>  >
>  > my %lines_to_extract;
>  >
>  > open DECLS, "bash analyze-globals.sh |";
>  > while (<DECLS>)
>  > {
>  >     chomp($_);
>  >     my ($file,$line_num,$text) = /^(\w+\.c):(\d+):(.*)$/;
>  >     push @{$lines_to_extract{$file}}, +{ 'l' => $line_num, 't' =>
>
> $text, };
>
>  > }
>  > close(DECLS);
>  >
>  > my @lines_to_put = ();
>  > foreach my $file (keys(%lines_to_extract))
>  > {
>  >     my $lines = $lines_to_extract{$file};
>  >     my $line_num = 1;
>  >     my $extracted_line_idx = 0;
>  >     open I, "<$file";
>  >     open O, ">$file.new";
>  >     while (<I>)
>  >     {
>  >         if (($extracted_line_idx < @$lines) &&
>  >             ($line_num == $lines->[$extracted_line_idx]->{l})
>  >            )
>  >         {
>  >             push @lines_to_put, $_;
>  >             $extracted_line_idx++;
>  >         }
>  >         else
>  >         {
>  >             print O $_;
>  >         }
>  >     }
>  >     continue
>  >     {
>  >         $line_num++;
>  >     }
>  >     close(I);
>  >     close(O);
>  >     rename("$file.new", "$file");
>  > }
>  > open GLOBALS, ">globals.c";
>  > print GLOBALS @lines_to_put;
>  > close(GLOBALS);
>  >
>  >
>  > It makes use of the bash script analyze-globals.sh which is:
>  >
>  > <<<
>  > #!/bin/bash
>  > grep -nP '^\S.*;' *.c | grep -v static | grep -vP '^\w+\.c:\d+:}'
>  >
>  >
>  > Regards,
>  >
>  > 	Shlomi Fish
>
> This time you chose revealing variable names, so it was easy.

Hmmm... yes. I wrote my script this way originally, and as I prepared to send 
it to this list, I decided that I did not have the nerve to change the 
variable names, and just sent it as is.

> The script removes global variables from a bunch of files and puts them
> in globals.c, for good order I guess (haven't used C for a while).

Correct. More accurately, it removes all the non-"static" C variables, that 
occupy names in the linker namespace. 

As for good order, this was part of the reason, but not all of it. The context 
of this script was that I wanted to revamp a rather poorly-written code 
(gimp's gimpressionist plug-in), which had used a bootload of global 
variables like that and accessed them from different places. I wanted to put 
them in one file, so I can later try to eliminate as many of them as I can.

Generally, accessing global variables from a different module is considered 
bad style. It is usually a good idea to make these variables static and 
create non-static functions that manipulate them. This is what I have done 
for most of these variables, eventually.

>
> The program assumes that the line numberes are sorted, which should be
> true, considering the method of generation.

Right again.

>
> The perl version I suggest:
>
>
> #!/path/to/perl -w
> open DECLS, "bash analyze-globals.sh |";
> while (<DECLS>) {
> 	/^(\w+\.c):(\d+):(.*)\n$/ and push @{$lines_to_extract{$1}},
> 	+{ 'l' => $2, 't' => $3 };
> }
>
> foreach my $file (keys(%lines_to_extract)) {
>      my @lines = @{$lines_to_extract{$file}};
>      my $line_num = 1;
>      open I, "<$file";
>      open O, ">$file.new";
>      while (($_ = <I>) && @$lines)
>      {
>          if ($line_num++ == $lines->[0]->{l}) {
> 		# We'll use STDOUT for better flexibility, and one less
> 		# filehandle to worry aboyut
>              print;
>              shift @$lines;
>          } else {
>              print O $_;
>          }
>      }
>      close(I);
>      close(O);
>      rename("$file.new", "$file");
> }
> <<<<<<
>
> Invoke with:
> $ ./thescript.pl > myglobals.c

Nice. Albeit, it does not seem substantially different than the original. 
Somewhat more brief, though, I suppose.

>
> I'm sure there must be a way to use the -i switch, but I can't think of
> it right now.

Well, you can open GLOBALS in the BEGIN { } and use the $ARGV variable to 
determine the file, and do all kinds of trickery like that. But it would be 
more trouble than it is worth, if you ask me. (although I would be glad to be 
shown otherwise).

Regards,

	Shlomi Fish

-- 

---------------------------------------------------------------------
Shlomi Fish      shlomif at iglu.org.il
Homepage:        http://shlomif.il.eu.org/

Knuth is not God! It took him two days to build the Roman Empire.



More information about the Perl mailing list