# This is a library of useful perl cgi scripting routines
# that use perl5 objects.
# Version 1.2, 3/9/95, L. Stein
# ABSTRACT:
# This perl library uses perl5 objects to make it easy to create
# Web fill-out forms and parse their contents. This package
# defines CGI objects, entities that contain the values of the
# current query string and other state variables.
# Using a CGI object's methods, you can examine keywords and parameters
# passed to your script, and create forms whose initial values
# are taken from the current query (thereby preserving state
# information).
# INSTALLATION:
# To use this package, install it in your perl library path and
# add the following to your perl CGI script:
# Use CGI;
# CREATING A NEW QUERY OBJECT:
#
# $query = new CGI
#
# This will parse the input (from both POST and GET methods) and store
# it into a perl5 object called $query.
# FETCHING A LIST OF KEYWORDS FROM THE QUERY:
#
# @keywords = $query->keywords
#
# If the script was invoked as the result of an search, the
# parsed keywords can be obtained as an array using the keywords() method.
# FETCHING THE NAMES OF ALL THE PARAMETERS PASSED TO YOUR SCRIPT:
#
# @names = $query->param
#
# If the script was invoked with a parameter list
# (e.g. "name1=value1&name2=value2&name3=value3"), the param()
# method will return the parameter names as a list. If the
# script was invoked as an script, there will be a
# single parameter named 'keywords'.
# FETCHING THE VALUE(S) OF A SINGLE NAMED PARAMETER:
#
# @values = $query->param('foo');
# -or-
# $value = $query->param('foo');
#
# Pass the param() method a single argument to fetch the value of the
# named parameter. If the parameter is multivalued (e.g. from multiple
# selections in a scrolling list), you can ask to receive an array. Otherwise
# the method will return a single value.
# SETTING THE VALUE(S) OF A NAMED PARAMETER:
#
# $query->param('foo','an','array','of','values');
#
# This sets the value for the named parameter 'foo' to an array of
# values. Do this if you want to change the value of a field AFTER
# the script has been invoked before.
# PRINTING THE HTTP HEADER:
#
# header() returns the Content-type: header.
# you can provide your own MIME type if you choose,
# otherwise it defaults to text/html
# CREATING FORMS:
#
# General note. The various form-creating methods all return strings
# to the caller, containing the tag or tags that will create the requested
# form element. You are responsible for actually printing out these strings.
# It's set up this way so that you can place formatting tags
# around the form elements.
# Another note: The default values that you specify for the forms are only
# used the _first_ time the script is invoked. If there are already values
# present in the query string, they are used, even if blank. If you want
# to change the value of a field from its previous value, call the param()
# method to set it.
# CREATING AN ISINDEX TAG
#
# print $query->isindex($action);
#
# Prints out an tag. Not very exciting. The optional
# parameter specifies an ACTION="" attribute.
# STARTING AND ENDING A FORM
#
# print $query->startform($method,$action);
# <... various form stuff ...>
# print $query->endform;
#
# startform() will return a tag.
# CREATING A TEXT FIELD
#
# print $query->textfield('foo','starting value',50);
#
# textfield() will return a text input field.
# - The first parameter is the required name for the field. The
# second parameter is an optional starting value for the field contents.
# - The optional third parameter is the size of the field in
# characters.
# As with all these methods, the field will be initialized with its
# previous contents from earlier invocations of the script.
# When the form is processed, the value of the text field can be
# retrieved with:
# $value = $query->param('foo');
# CREATING A BIG TEXT FIELD
#
# print $query->textarea('foo','starting value',10,50);
#
# textarea() is just like textfield, but it allows you to specify
# rows and columns for a multiline text entry box. You can provide
# a starting value for the field, which can be long and contain
# multiple lines.
# CREATING A PASSWORD FIELD
#
# print $query->password('secret','starting value',50);
#
# password() is identical to textfield(), except that its contents
# will be starred out on the web page.
# CREATING A POPUP MENU
#
# print $query->popup_menu('menu_name',['eenie','meenie','minie'],'meenie');
#
#
# popup_menu() creates a menu.
# - The required first argument is the menu's name.
# - The required second argument is an array _reference_ containing the list
# of menu items in the menu. You can pass the method an anonymous
# array, as shown in the example, or a reference to a named array,
# such as "\@foo".
# - The optional third parameter is the name of the default menu choice.
# If not specified, the first item will be the default. The values of
# the previous choice will be maintained across queries.
# When the form is processed, the selected value of the popup menu can
# be retrieved using:
# $popup_menu_value = $query->param('menu_name');
# CREATING A SCROLLING LIST
#
# print $query->scrolling_list('list_name',
# ['eenie','meenie','minie','moe'],
# ['eenie','moe'],5,'true');
#
# scrolling_list() creates a scrolling list.
# - The first and second arguments are the list name and values,
# respectively. As in the popup menu, the second argument should
# be an array reference.
# - The optional third argument can be either a reference to a list
# containing the values to be selected by default, or can be a
# single value to select. If this argument is missing or undefined,
# then nothing is selected when the list first appears.
# - The optional fourth argument is the size of the list.
# - The optional fifth argument should be set to true to allow multiple
# simultaneous selections.
# When this form is procesed, all selected list items will be returned as
# a list under the parameter name 'list_name'. The values of the
# selected items can be retrieved with:
# @selected = $query->param('list_name');
# CREATING A GROUP OF RELATED CHECKBOXES
#
# print $query->checkbox_group('group_name',
# ['eenie','meenie','minie','moe'],
# ['eenie','moe'],'true');
#
# checkbox_group() creates a list of checkboxes that are related
# by the same name.
# - The first and second arguments are the checkbox name and values,
# respectively. As in the popup menu, the second argument should
# be an array reference. These values are used for the user-readable
# labels printed next to the checkboxes as well as for the values
# passed to your script in the query string.
# - The optional third argument can be either a reference to a list
# containing the values to be checked by default, or can be a
# single value to checked. If this argument is missing or undefined,
# then nothing is selected when the list first appears.
# - The optional fourth argument can be set to true to place line breaks
# between the checkboxes so that they appear as a vertical list.
# Otherwise, they will be strung together on a horizontal line.
# When the form is procesed, all checked boxes will be returned as
# a list under the parameter name 'group_name'. The values of the
# "on" checkboxes can be retrieved with:
# @turned_on = $query->param('group_name');
# CREATING A STANDALONE CHECKBOX
#
# print $query->checkbox('checkbox_name','TURNED ON');
#
# checkbox() is used to create an isolated checkbox that isn't logically
# related to any others.
# - The first parameter is the required name for the checkbox. It
# will also be used for the user-readable label printed next to
# the checkbox.
# - The optional second parameter specifies the value of the checkbox
# when it is checked. If not provided, the word "on" is assumed.
# The value of the checkbox can be retrieved using:
# $turned_on = $query->param('checkbox_name');
# CREATING A RADIO BUTTON GROUP
#
# print $query->radio_group('group_name',['eenie','meenie','minie'],
# 'meenie','true');
#
#
# radio_group() creates a set of logically-related radio buttons
# (turning one member of the group on turns the others off)
# - The first argument is the name of the group and is required.
# - The second argument is the list of values for the radio buttons.
# The values and the labels that appear on the page are identical.
# Pass an array _reference_ in the second argument, either using
# an anonymous array, as shown, or by referencing a named array as
# in "\@foo".
# - The optional third parameter is the name of the default button to
# turn on. If not specified, the first item will be the default.
# - The optional fourth parameter can be set to 'true' to put
# line breaks between the buttons, creating a vertical list.
# When the form is processed, the selected radio button can
# be retrieved using:
# $which_radio_button = $query->param('group_name');
# CREATING A SUBMIT BUTTON
#
# print $query->submit('button_name','value');
#
# submit() will create the query submission button. Every form
# should have one of these.
# - The first argument is optional. You can give the button a
# name if you have several submission buttons in your form and
# you want to distinguish between them. The name will also be
# used as the user-visible label.
# - The second argument is also optional. This gives the button
# a value that will be passed to your script in the query string.
# You can figure out which button was pressed by using different
# values for each one:
# $which_one = $query->param('button_name');
# CREATING A RESET BUTTON
#
# print $query->reset
#
# reset() creates the "reset" button. Note that it restores the
# form to its value from the last time the script was called,
# NOT necessarily to the defaults.
# CREATING A DEFAULT BUTTON
#
# print $query->defaults('button_label')
#
# defaults() creates a button that, when invoked, will cause the
# form to be completely reset to its defaults, wiping out all the
# changes the user ever made.
# CREATING A HIDDEN FIELD
#
# print $query->hidden('hidden_name','hidden_value');
#
# hidden() produces a text field that can't be seen by the user. It
# is useful for passing state variable information from one invocation
# of the script to the next.
# - The first argument is required and specifies the name of this
# field.
# - The second argument is also required and specifies its value.
# Fetch the value of a hidden field this way:
# $hidden_value = $query->param('hidden_name');
# DEBUGGING:
# If you are running the script
# from the command line or in the perl debugger, you can pass the script
# a list of keywords or parameter=value pairs on the command line or
# from standard input (you don't have to worry about tricking your
# script into reading from environment variables).
# You can pass keywords like this:
# your_script.pl keyword1 keyword2 keyword3
#
# or this:
# your_script.pl keyword1+keyword2+keyword3
#
# or this:
# your_script.pl name1=value1 name2=value2
#
# or this:
# your_script.pl name1=value1&name2=value2
#
# or even as newline-delimited parameters on standard input
# DUMPING OUT ALL THE NAME/VALUE PAIRS
# The dump() method produces a string consisting of all the query's
# name/value pairs formatted nicely as a nested list. This is useful
# for debugging purposes:
#
# print $query->dump
#
# Produces something that looks like:
#
#
name1
#
#
value1
#
value2
#
#
name2
#
#
value1
#
#
# FETCHING ENVIRONMENT VARIABLES
#
# Some of the more useful environment variables can be fetched
# through this interface. The methods are as follows:
#
# accept() Return a list of MIME types that the remote browser
# accepts. If you give this method a single argument
# corresponding to a MIME type, as in
# $query->accept('text/html'), it will return a
# floating point value corresponding to the browser's
# preference for this type from 0.0 (don't want) to 1.0.
# Glob types (e.g. text/*) in the browser's accept list
# are handled correctly.
#
# path_info()
# Returns additional path information from the script URL.
# E.G. fetching /cgi-bin/your_script/additional/stuff will
# result in $query->path_info() returning
# "additional/stuff".
#
# path_translated()
# As per path_info() but returns the additional
# path information translated into a physical path, e.g.
# "/usr/local/etc/httpd/htdocs/additional/stuff".
#
# remote_host()
# Returns either the remote host name or IP address.
# if the former is unavailable.
#
# script_name()
# Return the script name as a partial URL, for self-refering
# scripts.
#
# referer()
# Return the URL of the page the browser was viewing
# prior to fetching your script. Not available for all
# browsers.
######################################################################
# AUTHOR INFORMATION
#
# This code is copyright 1995 by Lincoln Stein and the Whitehead
# Institute for Biomedical Research. It may be used and modified
# freely. I request, but do not require, that this credit appear
# in the code.
#
# Address bug reports and comments to:
# lstein@genome.wi.mit.edu
######################################################################
######################################################################
# CREDITS
# Thanks very much to Matt Heffron (heffron@falstaff.css.beckman.com)
# and James Taylor (james.taylor@srs.gov) for useful bug patches.
#######################################################################
#######################################################################
# A COMPLETE EXAMPLE OF A SIMPLE FORM-BASED SCRIPT
#
# #!/usr/local/bin/perl
# # CGI Script: object-form.pl
#
# BEGIN {
# unshift(@INC,'/usr/local/etc/httpd/cgi-bin');
# }
# use CGI;
#
# &print_header;
# &print_head;
# $query = new CGI;
# &print_prompt($query);
# &do_work($query);
# &print_tail;
# sub print_header { print "Content-type: text/html\n\n"; }
#
# sub print_head {
# print <
# Example CGI.pm Form
#
#
Example CGI.pm Form
# END
# }
#
# sub print_prompt {
# local($query) = @_;
#
# print $query->startform;
# print "What's your name? ";
# print $query->textfield('name');
# print $query->checkbox('Not my real name');
#
# print "
Where can you find English Sparrows? ";
# print $BLANK;
# print $query->checkbox_group('Sparrow locations',
# [England,France,Spain,Asia,Hoboken],
# [England,Asia]);
#
# print "
How far can they fly? ",
# $query->radio_group('how far',
# ['10 ft','1 mile','10 miles','real far'],
# '1 mile');
#
# print "
What's your favorite color? ";
# print $query->popup_menu('Color',['black','brown','red','yellow'],'red');
#
# print $query->hidden('Reference','Monty Python and the Holy Grail');
#
# print "
What have you got there? ";
# print $query->scrolling_list('possessions',
# ['A Coconut','A Grail','An Icon',
# 'A Sword','A Ticket'],
# undef,
# 10,
# 'true');
#
# print "
";
#
# foreach $key ($query->param) {
# print "$key -> ";
# @values = $query->param($key);
# print join(", ",@values)," \n";
# }
#
# }
#
# sub print_tail {
# print <
# Lincoln D. Stein
# Home Page
# END
# }
#######################################################################
# ------------------ START OF THE LIBRARY ------------
package CGI;
$VERSION=1.2;
#### Method: new
# The new routine. This will check the current environment
# for an existing query string, and initialize itself, if so.
####
sub new {
my $self = {};
bless $self;
$self->initialize;
return $self;
}
#### Method: param
# Returns the value(s)of a named parameter.
# If invoked in a list context, returns the
# entire list. Otherwise returns the first
# member of the list.
# If name is not provided, return a list of all
# the known parameters names available.
# If more than one argument is provided, the
# second and subsequent arguments are used to
# set the value of the parameter.
####
sub param {
local($self,$name,@values) = @_;
return grep ($_ !~ /^\./,keys %{$self}) unless $name;
# If values is provided, then we set it.
$self->{$name}=[@values] if @values;
return () unless $self->{$name};
return wantarray ? @{$self->{$name}} : $self->{$name}->[0];
}
#### Method: keywords
# Keywords acts a bit differently. Calling it in a list context
# returns the list of keywords.
# Calling it in a scalar context gives you the size of the list.
####
sub keywords {
my($self,@values) = @_;
# If values is provided, then we set it.
$self->{'keywords'}=[@values] if @values;
local(@result) = @{$self->{'keywords'}};
@result;
}
#### Method: version
# Return the current version
####
sub version {
return $VERSION;
}
#### Method: dump
# Returns a string in which all the known parameter/value
# pairs are represented as nested lists, mainly for the purposes
# of debugging.
####
sub dump {
my($self) = @_;
my($param,$value,@result);
push(@result,"
");
return join("\n",@result);
}
#### Method: header
# Return a Content-type: style header
#
####
sub header {
local($self,$type) = @_;
$type = $type || 'text/html';
return "Content-type: $type\n\n";
}
################################
# METHODS USED IN BUILDING FORMS
################################
#### Method: isindex
# Just prints out the isindex tag.
# Parameters:
# $action -> optional URL of script to run
# Returns:
# A string containing a tag
sub isindex {
local($self,$action) = @_;
$action = qq/ACTION="$action"/ if $action;
return "";
}
#### Method: startform
# Start a form
sub startform {
local($self,$method,$action) = @_;
$method = $method || 'POST';
$action = qq/ACTION="$action"/ if $action;
return qq/\n";
}
#### Method: textfield
# Parameters:
# $name -> Name of the text field
# $default -> Optional default value of the field if not
# already defined.
# $size -> Optional size of field.
# Returns:
# A string containing a field
#
sub textfield {
local($self,$name,$default,$size) = @_;
local($current)=$self->inited ? $self->param($name) : $default;
local($s) = qq/SIZE=$size/ if $size;
return qq//; #[BII] default this right
}
#### Method: password
# Create a "secret password" entry field
# Parameters:
# $name -> Name of the field
# $default -> Optional default value of the field if not
# already defined.
# $size -> Optional size of field.
# Returns:
# A string containing a field
#
sub password_field {
local($self,$name,$default,$size)=@_;
local($current) = $self->inited ? $self->param($name) : $default;
local($s) = qq/SIZE=$size/ if $size;
return qq//;
}
#### Method: textarea
# Parameters:
# $name -> Name of the text field
# $default -> Optional default value of the field if not
# already defined.
# $rows -> Optional number of rows in text area
# $columns -> Optional number of columns in text area
# Returns:
# A string containing a tag
#
sub textarea {
local($self,$name,$default,$rows,$cols)=@_;
local($current)= $self->inited ? $self->param($name) : $default;
local($r) = "ROWS=$rows" if $rows;
local($c) = "COLS=$cols" if $cols;
return <$current
END
}
#### Method: submit
# Create a "submit query" button.
# Parameters:
# $label -> (optional) Name for the button.
# $value -> (optional) Value of the button when selected.
# Returns:
# A string containing a tag
####
sub submit {
local($self,$label,$value) = @_;
local($name) = qq/NAME="$label"/ if $label;
$value = $value || $label;
local($val) = qq/VALUE="$value"/ if $value;
return qq/\n/;
}
#### Method: reset
# Create a "reset" button.
# Parameters:
# $label -> (optional) Name for the button.
# Returns:
# A string containing a tag
####
sub reset {
local($self,$label) = @_;
local($value) = qq/VALUE="$name"/ if $name;
return qq/\n/;
}
#### Method: defaults
# Create a "defaults" button.
# Parameters:
# $label -> (optional) Name for the button.
# Returns:
# A string containing a tag
#
# Note: this button has a special meaning to the initialization script,
# and tells it to ERASE the current query string so that your defaults
# are used again!
####
sub defaults {
local($self,$label) = @_;
$label = $label || "Defaults";
local($value) = qq/VALUE="$label"/;
return qq/\n/;
}
#### Method: checkbox
# Create a checkbox that is not logically linked to any others.
# The field value is "on" when the button is checked.
# Parameters:
# $name -> Name of the checkbox
# $checked -> (optional) turned on by default if true
# $value -> (optional) value of the checkbox, 'on' by default
# Returns:
# A string containing a field
####
sub checkbox {
local($self,$name,$checked,$value)=@_;
if ($self->inited) {
$checked = $self->param($name) ? 'CHECKED' : undef;
$value = $self->param($name) || $value || 'on';
} else {
$checked = 'CHECKED' if $checked;
$value = $value || 'on';
}
return <$name
END
}
#### Method: checkbox_group
# Create a list of logically-linked checkboxes.
# Parameters:
# $name -> Common name for all the check boxes
# $values -> A pointer to a regular array containing the
# values for each checkbox in the group.
# $settings -> (optional)
# 1. If a pointer to a regular array of checkbox values,
# then this will be used to decide which
# checkboxes to turn on by default.
# 2. If a scalar, will be assumed to hold the
# value of a single checkbox in the group to turn on.
# $linebreak -> (optional) Set to true to place linebreaks
# between the buttons.
# Returns:
# A string containing a series of fields
####
sub checkbox_group {
local($self,$name,$values,$defaults,$linebreak)=@_;
local(%checked,$break,$result);
if ($self->inited) {
grep($checked{$_}++,$self->param($name));
} elsif (ref($defaults) eq 'ARRAY') {
grep($checked{$_}++,@{$defaults});
} else {
$checked{$defaults}++;
}
$result = $break = " " if $linebreak;
foreach (@{$values}) {
$checked = $checked{$_} ? 'CHECKED' : undef;
$result .=
qq/$_$break\n/;
}
return $result;
}
#### Method: radio_group
# Create a list of logically-linked radio buttons.
# Parameters:
# $name -> Common name for all the buttons.
# $values -> A pointer to a regular array containing the
# values for each button in the group.
# $default -> (optional) Value of the button to turn on by default.
# $linebreak -> (optional) Set to true to place linebreaks
# between the buttons.
# Returns:
# A string containing a series of fields
####
sub radio_group {
local($self,$name,$values,$default,$linebreak)=@_;
local($result,$checked);
if ($self->inited) {
$checked = $self->param($name);
} else {
$checked = $default;
}
# If no check array is specified, check the first by default
$checked = $values->[0] unless $checked;
$result = " " if $linebreak;
foreach (@{$values}) {
my($checkit) = $checked eq $_ ? 'CHECKED' : undef;
my($break) = $linebreak ? ' ' : undef;
$result .= qq/$_$break\n/;
}
return $result;
}
#### Method: popup_menu
# Create a popup menu.
# Parameters:
# $name -> Name for all the menu
# $values -> A pointer to a regular array containing the
# text of each menu item.
# $default -> (optional) Default item to display
# Returns:
# A string containing the definition of a popup menu.
####
sub popup_menu {
local($self,$name,$values,$default)=@_;
local($result,$selected);
if ($self->inited) {
$selected = $self->param($name);
} else {
$selected = $default;
}
$result = qq/\n";
return $result;
}
#### Method: scrolling_list
# Create a scrolling list.
# Parameters:
# $name -> name for the list
# $values -> A pointer to a regular array containing the
# values for each option line in the list.
# $defaults -> (optional)
# 1. If a pointer to a regular array of options,
# then this will be used to decide which
# lines to turn on by default.
# 2. Otherwise holds the value of the single line to turn on.
# $size -> (optional) Size of the list.
# $multiple -> (optional) If set, allow multiple selections.
# Returns:
# A string containing the definition of a scrolling list.
####
sub scrolling_list {
local($self,$name,$values,$default,$size,$multiple)=@_;
local($result,%selected);
if ($self->inited) {
grep($selected{$_}++,$self->param($name));
} elsif (ref($default) eq 'ARRAY') {
grep($selected{$_}++,@{$default});
} else {
$selected{$default}++;
}
my($is_multiple) = $multiple ? 'MULTIPLE' : undef;
my($has_size) = $size ? "SIZE=$size" : undef;
$result = qq/\n";
return $result;
}
#### Method: hidden
# Parameters:
# $name -> Name of the hidden field
# $default -> (optional) Initial value of field
# Returns:
# A string containing a
####
sub hidden {
local($self,$name,$default)=@_;
local($v);
if ($self->inited) {
my($value) = $self->param($name);
$v = qq/VALUE="$value"/;
} else {
$v = qq/VALUE="$default"/;
}
return qq//;
}
###############################################
# OTHER INFORMATION PROVIDED BY THE ENVIRONMENT
###############################################
#### Method: path_info
# Return the extra virtual path information provided
# after the URL (if any)
####
sub path_info {
return $ENV{'PATH_INFO'};
}
#### Method: path_translated
# Return the physical path information provided
# by the URL (if any)
####
sub path_translated {
return $ENV{'PATH_TRANSLATED'};
}
#### Method: accept
# Without parameters, returns an array of the
# MIME types the browser accepts.
# With a single parameter equal to a MIME
# type, will return undef if the browser won't
# accept it, 1 if the browser accepts it but
# doesn't give a preference, or a floating point
# value between 0.0 and 1.0 if the browser
# declares a quantitative score for it.
# This handles MIME type globs correctly.
####
sub accept {
local($self,$search) = @_;
local(%prefs);
local(@accept) = split(',',$ENV{'HTTP_ACCEPT'});
foreach (@accept) {
($pref) = /q=(\d\.\d+|\d+)/;
($type) = m#(\S+/[^;]+)#;
next unless $type;
$prefs{$type}=$pref || 1;
}
return keys %prefs unless $search;
# if a search type is provided, we may need to
# perform a pattern matching operation.
# The MIME types use a glob mechanism, which
# is easily translated into a perl pattern match
# First return the preference for directly supported
# types:
return $prefs{$search} if $prefs{$search};
# Didn't get it, so try pattern matching.
foreach (keys %prefs) {
next unless /\*/; # not a pattern match
$pat=~s/([^\w*])/\\$1/g; # escape meta characters
($pat = $_) =~ s/\*/.*/g; # turn it into a pattern
return $prefs{$_} if $search=~/$pat/;
}
}
#### Method: remote_host
# Return the name of the remote host, or its IP
# address if unavailable
####
sub remote_host {
return $ENV{'REMOTE_HOST'} || $ENV{'REMOTE_ADDR'};
}
#### Method: remote_addr
# Return the IP addr of the remote host.
####
sub remote_addr {
return $ENV{'REMOTE_ADDR'};
}
#### Method: script_name
# Return the partial URL to this script for
# self-referencing scripts.
####
sub script_name {
return $ENV{'SCRIPT_NAME'};
}
#### Method: referer
# Return the HTTP_REFERER: useful for generating
# a GO BACK button.
####
sub referer {
return $ENV{'HTTP_REFERER'};
}
########################################
# THESE METHODS ARE MORE OR LESS PRIVATE
########################################
# Initialize the query object from the environment.
# If a parameter list is found, this object will be set
# to an associative array in which parameter names are keys
# and the values are stored as lists
# If a keyword list is found, this method creates a bogus
# parameter list with the single parameter 'keywords'.
sub initialize {
local($self) = @_;
local($query_string);
local(@lines);
local($method)=$ENV{'REQUEST_METHOD'};
# See whether
# If method is GET or HEAD, fetch the query from
# the environment.
if ($method=~/^(GET|HEAD)$/) {
$query_string = $ENV{'QUERY_STRING'};
# If the method is POST, fetch the query from standard
# input.
} elsif ($method eq 'POST') {
read(STDIN,$query_string,$ENV{'CONTENT_LENGTH'});
# If neither is set, assume we're being debugged offline.
# Check the command line and then the standard input for data.
} elsif (@ARGV) {
$query_string = "@ARGV";
# massage it back into standard format
$query_string=~tr/ /&/ if $query_string=~/=/;
} else { # fetch from standard input
warn "(waiting for standard input)\n";
chop(@lines = <>); # remove newlines
# massage back into standard format
if ("@lines" =~ /=/) {
$query_string=join("&",@lines);
} else {
$query_string=join("+",@lines);
}
}
# No data. Leave us empty.
return unless $query_string;
# We now have the query string in hand. We do slightly
# different things for keyword lists and parameter lists.
$self->{'.init'}++; # flag that we've been inited
if ($query_string =~ /=/) {
$self->parse_params($query_string);
} else {
$self->{'keywords'} = [$self->parse_keywordlist($query_string)];
}
# Special case. Erase everything if there is a field named
# .defaults.
if ($self->param('.defaults')) {
undef %{$self};
}
}
# Return true if we've been initialized with a query
# string.
sub inited {
local($self) = shift;
return $self->{'.init'};
}
sub unescape {
local($todecode) = @_;
$todecode =~ tr/+/ /; # pluses become spaces
$todecode =~ s/%(..)/pack("c",hex($1))/ge;
return $todecode;
}
#[BII] Nobody calls this *yet*, but it ought to work right anyway...
sub escape {
local($toencode) = @_;
$toencode=~s/([^a-zA-Z0-9_])/sprintf("%%%x",ord($1))/eg;
return $toencode;
}
# -------------- really private subroutines -----------------
sub parse_keywordlist {
local($self,$tosplit) = @_;
$tosplit = &unescape($tosplit); # unescape the keywords
$tosplit=~tr/+/ /; # pluses to spaces
local(@keywords) = split(/\s+/,$tosplit);
return @keywords;
}
sub parse_params {
local($self,$tosplit) = @_;
local(@pairs) = split('&',$tosplit);
local($param,$value);
foreach (@pairs) {
($param,$value) = split('=');
$param = &unescape($param);
$value = &unescape($value);
push (@{$self->{$param}},$value);
}
}
1; # so that require() returns true