Skip to main content.
home | support | download

Back to List Archive

Re: Getting older .cgi scripts to work with

From: Bill Moseley <moseley(at)not-real.hank.org>
Date: Mon Jun 03 2002 - 16:15:00 GMT
At 08:12 AM 06/03/02 -0700, Andrew Lord wrote:
>>     ./swish-e -k c
>>
>> and split on white space.
>
>I have tried the above (and index_words_only) at the command-line level and 
>have had no problems, at that level.  However I've had no luck with changing 
>makewordlist() in dictionary.cgi.  

Ok, here's the original code:

sub makewordlist{
	$swish = '/usr/local/bin/swish-e';

	open SWISH, '-|' or exec $swish, '-D',"$swishdir/$index" 
		or exec 'echo',"$!";
	while(<SWISH>) {
		next if /^\d/;
		next unless /^$pattern/;
		chop;
		($word,$numbers) = split(/:/,$_);
		if(length $numbers > 0) {
			push @words,$word;
		}
	}
}

The entire CGI script could use a make over.  The use of global vars makes
things messy.  The function should stand on its own, really.  It's using
variables that it doesn't own and works by side-effects (by pushing works
onto @words which is defined elsewhere.

I'd rewrite it something like this.  This is a complete program, which is a
good way to test individual functions.

#!/usr/local/bin/perl -w
use strict;

# Use letter on command line or "a" by default.
my @stuff = makewordlist( shift || 'a', './swish-e' );

print scalar @stuff, " words:\n", join "\n", @stuff,'';

use Symbol;
sub makewordlist {
    my ( $letter, $swish ) = @_;

    # pick which letters are ok
    unless ( $letter =~ /^[a-zA-Z]$/ ) {
        warn "invalid letter '$letter'";
        return;
    }

    my $fh = gensym;

    open( $fh, "$swish -k $letter|" ) or
        die "Failed to run '$swish -k $letter': $!";

    # -k
    my @words;

    # format is index-file: space separated list of words
    
    while ( <$fh> ) {
        next if /^#/;  # skip comments
        if ( my $words = ( split /\s*:\s*/ )[1] ) {
            push @words, split /\s+/, $words;
        }
    }
        

    close $fh
       or warn "Failed to close '$swish -k $letter': "
       . ($! || "Exit status: $?");

    return @words;
    # or return \@words to return a "reference" instead
    # of the entire list if you want to avoid the copy
    
}

You could use that directly in that CGI script and then change where
makewordlist() is called to pass in the right variables, and set the @words
array. 

I don't bother forking and exec'ing for this because I've validated the
user input data.  It might be slower, but if I were writing this I'd also
might just cache the words in a file instead of asking swish-e for the list
every time.  It will be much faster than the original code since -D had
much more output to parse.



>PS.    > You might want to adjust the script to start like:
>>
>>   #!/usr/local/bin/perl -wT
>>   use strict;
>>
>> That will catch potential errors in the script.
>
>I tried the above in dictionary.cgi and, even while attempting to use the 
>then-modified dictionary.cgi with swish-e-2.0.5 (which otherwise works 
>perfectly with dictionary.cgi), the error_log showed that it fell over on 
>more lines than I know how to count.   So I'm unsure of the value of 'use 
>strict;' with this script.  I need to learn about debugging.

First thing to learn about debugging CGI script is to debug from the
command line.  The web server will just complicate things when first
debugging.

For perl, you first make sure it compiles:

   $ perl -c dictionary.cgi

All those errors with "-wT" and "use strict" are potential problems.

-w and "use strict" will catch programmer errors, and variables that are
used in an unwise way.  -w will notify of things such as using a variable
that hasn't been initialized.  use strict will force you to declare your
variables -- which tends to avoid side-effects.  You don't want to change
$foo one place and have it change another $foo somewhere far away.

Localize your variables and place everything in small subroutines so you
can easily localize variables with "my" (see above code) and isolate code into
small functional blocks.

Once compile errors are fixed you can run it from the command line:

   $ ./dictionary.cgi

and look at the output.  You can easily emulate the CGI environment.  Just
set environment variables

   REQUEST_METHOD=GET     # emulate a GET request
   QUERY_STRING='query=hello&color=red' # emulate the parameters

How you do that depends on your shell.  Or just edit the script and force
parameters.

The script you are using decodes the CGI environment manually.  The vast
majority of perl programmers will tell you to use the standard (comes with
every perl installation) CGI.pm module.  Type "perldoc CGI" to learn about
using it.

Good luck.


-- 
Bill Moseley
mailto:moseley@hank.org
Received on Mon Jun 3 16:18:30 2002