HTML form basics
Processing the form input
Handling form input is one of the most common uses of CGI scripts today. This is in large part due to the numerous uses for forms. A form is just a group of HTML tags that generate such elements as input fields, list boxes, check boxes, radio buttons, and push buttons. Forms allow the user viewing your Web page to interact with you or your Web site by supplying information or making a selectionIn this chapter, you will set up a feedback form that allows the user viewing your pages to send you messages. This will be a simple example of a form and the CGI script needed to handle the input from the form. Because you create forms with HTML and process them with CGI scripts, this chapter briefly reviews the HTML code for creating forms. Again, if you are not already familiar with the HTML of a form or with the concepts of input fields, list boxes, check boxes, radio buttons, and push buttons, you should consult a book on HTML such as HTML3 Manual of Style by Larry Aronson (Ziff-Davis Press, 1995).
HTML form basics
Forms are defined in the HTML specification and not in CGI. CGI simply provides the means for handling the input received from a form. To create a form, you just use the <FORM> tag and the associated <INPUT>, <SELECT>, and <TEXTAREA> tags. A basic form can consist of as little as a single button. However, most forms contain many more elements. This section briefly presents the HTML form tags <FORM>, <INPUT>, <SELECT>, and <TEXTAREA>, and the relevant attributes for the tags.
The <FORM> Tag
All HTML forms begin with the <FORM> tag and end with the </FORM> tag. The <FORM> tag commonly takes two attributes: METHOD and ACTION. A third defined attribute, ENCTYPE, is not commonly used and is not necessary for the examples in this book.The METHOD attribute tells the Web server how to send the data to the CGI script. Values for the METHOD attribute are GET and POST. Most commonly, the POST value is assigned to METHOD. Recall from Chapter 2 that the location of the user-supplied data varies depending on this value. For the GET method, the user data is URL encoded and placed in the QUERY_STRING environment variable. The POST method also URL encodes the data, but sends it via standard input.
The value of the ACTION attribute tells the Web server what action to perform when the form is submitted. A form is submitted when the user viewing the Web page either clicks on the submit push button (more on this in "The <INPUT> Tag" below) or presses the Enter key when there is only a single input field. Once the form is submitted, the data is URL encoded and sent to the Web server by either the GET or POST method. The Web server receives the data and checks the value of the ACTION attribute.
The ACTION attribute can have any valid URL for its value. For our purposes, this value will always be the location of our CGI script. For example, let's call the CGI script for the feedback form feedback.pl. If you placed this form in the cgi-bin directory of your Web server, the ACTION attribute would be set to /cgi-bin/feedback.pl. Between the <FORM> and </FORM> tags are all other elements of the form, such as the <INPUT>, <SELECT>, and <TEXTAREA> tags. For the example feedback form, the beginning form tag would be <FORM METHOD=POST ACTION= "/cgi-bin/feedback.pl">.
The <INPUT> Tag
<INPUT> tags reside between the <FORM> and </FORM> tags and are the most versatile element tags of the form. With the <INPUT> tag, you can create check boxes, radio buttons, single-line input fields, and submit and reset push buttons. The <INPUT> tag does not have a closing tag and has six attributes, which are described in Table 4.1.Like many visual elements, these HTML tags are better shown than explained. Listing 4.1 displays the HTML code that creates an example of each type of input element. Figure 4.1 shows how these input elements appear in the Netscape browser.
Table 4.1: <INPUT> Tag Attributes Attribute Values TYPE This attribute determines what form the input field will take. The possible values are text: Creates a single-line text input field. This is the default value for the <INPUT> tag. password:Similar to text, except all entered values are represented on screen as asterisks. hidden: Creates a field that is invisible to the user. This is used to pass information that the user does not need to see or modify on to the form handler CGI script. checkbox: Creates a single check box, which can be either on (checked) or off (unchecked) radio: Similar to checkbox in that it creates an option that can be either on or off. However, when several radio buttons have the same value of the NAME attribute, only one radio button can be on (checked) at a time. submit: Creates a push button that, when clicked, causes the form to be submitted (performs the action). reset: Generates a push button that, when clicked, resets the elements of the form to their default values. NAME The value for this attribute is sent to the server as the name for the name/value pairs. For example, if the user typed robertm@ deltanet.com in a single-line text input field with "email" as the name attribute, the Web server would receive email=robertm@ deltanet.com. When you create a group of radio buttons, all the buttons must have the same value for the NAME attribute. VALUE For text and password elements, this attribute holds an initial value for the field to contain. For checkbox and radio elements, this attribute holds the value that is sent to the Web server when the element is selected. If the VALUE attribute is not specified for checkbox and radio elements, the Web server receives "on" as the value for the selected elements. For submit and reset elements, this attribute holds the value used for the label of the button. For example, creating a submit button with the VALUE attribute equal to "Send" yields a push button labeled "Send." CHECKED This attribute does not take a value and is only used with checkbox and radio elements. It makes the initial state of the element checked. SIZE This attribute is only for use with the text and password elements; it defines the physical size, in characters, of the input element. The default value is 20, which creates an input field 20 characters wide. MAXLENGTH This attribute is only for text and password elements. It defines the maximum number of characters the input element will receive.
Listing 4.1: Examples of Input Elements <FORM METHOD=POST ACTION="/cgi-bin/example.pl"> Single Line Entry Field: <INPUT TYPE=text NAME=text VALUE="Default value" SIZE=30> <P>Password Entry Field: <INPUT TYPE=password NAME=password SIZE=10 MAXLENGTH=8> <P>Check Box: <INPUT TYPE=checkbox NAME=checkbox CHECKED> <P>Radio Buttons - Yes <INPUT TYPE=radio NAME=radio VALUE=yes> No <INPUT TYPE=radio NAME=radio VALUE=no> <P>Submit Pushbutton: <INPUT TYPE=submit VALUE=Send> <P>Reset Pushbutton: <INPUT TYPE=reset VALUE=Reset> </FORM>
The feedback form you will create in this chapter uses several input fields, but all of them are of the text type. Some of the examples in Chapter 5 contain other <INPUT> tag elements. The feedback form in this chapter only needs an input field for the user's name, e-mail address, and the submit push button. Here are these three input fields:
<B>Name-Address</B><BR><INPUT NAME="name" SIZE=42> <P><B>E-mail Address</B><BR><INPUT NAME="email" SIZE=42> <P><INPUT TYPE="submit" VALUE="Send">
The first two lines create 42-character single-line text entry fields for the name and e-mail address. The final line creates the submit push button and gives it the value "Send" for the label. Because the <INPUT> tag elements do not include labels (that is, some text describing what you want the user to enter in that field), this code includes text labels for the name and e-mail fields.
![]()
Figure 4.1: The <INPUT> tag elements
The <SELECT> Tag
The <SELECT> tag allows you to create scrollable lists or drop-down lists. A scrollable list is a list of items that you can scroll through and from which you can select single or multiple items. A drop-down list is similar to a scrollable list, but it only displays one item unless you drop down the list by pressing on it. You can only select one item from a drop-down list.The <SELECT> tag has three attributes: NAME, SIZE and MULTIPLE, as described in Table 4.2.
Table 4.2: <SELECT> Tag Attributes Attribute Values NAME This attribute is similar to the NAME attribute of the <INPUT> tag. The value for this attribute is sent to the server as the name for the name/value pairs. For example, in a scrollable list with the NAME attribute set to "flavor" with two options selected, vanilla and chocolate, the Web server would receive "flavor=vanilla" and "flavor=chocolate". SIZE This attribute determines how many options are displayed for the scrollable list. If SIZE is set to 1, which is the default if the SIZE attribute is not present, the list will be a drop-down list. Otherwise, it will be a scrollable list displaying the number of options defined in SIZE. If the MULTIPLE attribute is present, the list will always be displayed as a scrollable list, regardless of whether the SIZE attribute is set to 1. MULTIPLE This attribute does not take any values. If present, it allows multiple options to be selected.
The <SELECT> tag has both beginning and ending tags, <SELECT> and </SELECT>. Within these tags, you specify the list options by using the <OPTION> tag. The <OPTION> tag does not have a closing tag and has only one attribute, SELECTED, which makes the associated option selected in the default form. For example, Listing 4.2 shows the HTML for a drop-down list of numbers from one to three. Listing 4.3 shows the HTML for a scrollable list with ice cream flavors. Notice that more than one ice cream flavor can be chosen, and that vanilla is always initially selected. Figure 4.2 shows how the Netscape browser will display these HTML examples.
Listing 4.2: Example <SELECT> Drop-down List <FORM METHOD=POST ACTION="/cgi-bin/example.pl"> <SELECT NAME="number" SIZE=1> <OPTION>one <OPTION>two <OPTION>three </SELECT> </FORM>
Listing 4.3: Example <SELECT> Scrollable List <FORM METHOD=POST ACTION="/cgi-bin/example.pl"> <SELECT NAME="flavor" SIZE=3 MULTIPLE> <OPTION>Chocolate <OPTION SELECTED>Vanilla <OPTION>Strawberry <OPTION>Chocolate Chip <OPTION>French Vanilla <OPTION>Peach </SELECT> </FORM>
![]()
Figure 4.2: <SELECT> examples The feedback form will not use any <SELECT> tags.
The <TEXTAREA> Tag
You use the <TEXTAREA> tag to place a multiline text input area in the form. It has both beginning and ending tags, <TEXTAREA> and </TEXTAREA>. Table 4.3 describes the four attributes the <TEXTAREA> tag can take.
Table 4.3: <TEXTAREA> Tag Attributes Attribute Values NAME As with the NAME attribute of the <INPUT> and <SELECT> tags, the value for this attribute is sent to the server as the name for the name/value pairs. For example, in a text area with the NAME attribute set to "comments" containing the string "I think it is a great product!" the Web server would receive "comments=I think it is a great product!". ROWS The value for this attribute defines the number of rows for the text area entry field. This only defines the physical height, in characters, of the box, not the number of rows the user can enter into the box. COLS The value for this attribute defines the number of columns for the text area entry field. Again, this is the physical width, in characters, of the box, not the number of characters the user can enter. WRAP This attribute is a Netscape extension to the <TEXTAREA> tag. A Netscape extension is a tag or attribute that works under certain versions of the Netscape Navigator browser, but does not work under all browsers. It is not a part of the current HTML specification, but it may be added in later versions of HTML. The WRAP attribute can take one of three values: Off: This default setting makes the TEXTAREA field work exactly as it would without the WRAP attribute (no wrapping). Virtual: This value causes the data being entered into the TEXTAREA field to wrap when it reaches the width of the box. However, when the data is sent to the Web server, wrapped lines are sent as a single line. Physical: This value also causes the data being entered into the TEXTAREA field to wrap when it reaches the width of the box. When the data is sent to the Web server, however, new line characters are sent at all of the wrap points.
Any text between the <TEXTAREA> and </TEXTAREA> tags is used as the default text for the text area box. What follows is the HTML for a text area box with some default text. Figure 4.3 shows how the Netscape browser would display that HTML.
<FORM METHOD=POST ACTION="/cgi-bin/example.pl"> <TEXTAREA NAME="comments" ROWS=4 COLS=40 WRAP=PHYSICAL> Place your comments here. </TEXTAREA> </FORM>
The feedback form in this chapter uses one <TEXTAREA> field. You do not need default contents for the text area box, but you will include a label. Here is the <TEXTAREA> HTML for the feedback form.
<P><B>Comments</B><BR><TEXTAREA NAME="comments" ROWS=10 COLS=38></TEXTAREA>
![]()
Figure 4.3: The <TEXTAREA> example
Putting Together the Feedback Form
Now you know all of the HTML you need for your feedback form. The next step is to combine the various parts of the form and place them in an HTML file. Let's name the file feedback.html (or feedback.htm if you cannot use a four-digit file name extension). Listing 4.4 contains the contents of the feedback.html file, and Figure 4.4 illustrates what the form looks like when displayed by the Netscape Web browser.Processing the form input
Now that the HTML for the feedback form is completed, you must create the feedback.pl CGI script that will receive the data sent by the user through the form in the Web browser. Once the feedback.pl script has received the data from the Web server, it must decode the information. Then it takes the data and places it in an e-mail message. Finally, the script returns the user to the home page of your Web site.
Listing 4.4: HTML for Feedback Form <HTML> <HEAD> <TITLE>Feedback Form</TITLE> </HEAD> <BODY> <H1>Feedback</H1> <FORM METHOD=POST ACTION="/cgi-bin/feedback.pl"> <B>Name</B><BR><INPUT NAME="name" SIZE=42> <P><B>E-mail Address</B><BR><INPUT NAME="email" SIZE=42> <P><B>Comments</B><BR><TEXTAREA NAME="comments" ROWS=10 COLS=38></TEXTAREA> <P><INPUT TYPE="submit" VALUE="Send"></FORM> </BODY> </HTML>
![]()
Figure 4.4: The feedback form
Decoding the Input
Remember, when user-supplied data is sent to a CGI script it is URL encoded. Therefore, the first thing the script must do is to decode this data. Chapter 2 included the lines of code for doing so. Let's take the code from Listing 2.3 and place it in a subroutine called User_Data. A subroutine in Perl is just a function that you can call by using the ampersand followed by the function name, as in &User_Data;. In Perl, the definitions of subroutines usually sit at the end of the file. So, your feedback.pl file will contain a call of the User_Data subroutine and the User_Data subroutine definition. Listing 4.5 shows what you have so far for the feedback.pl.
Listing 4.5: First Part of feedback.pl #!/usr/local/bin/perl # Decode the user data and place it in the # data_received associative array. %data_received = &User_Data(); sub User_Data { local (%user_data, $user_string, $name_value_pair, @name_value_pairs, $name, $value); # If the data was sent via POST, then it is available # from standard input. Otherwise, the data is in the # QUERY_STRING environment variable. if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN,$user_string,$ENV{'CONTENT_LENGTH'}); } else { $user_string = $ENV{'QUERY_STRING'}; } # This line changes the + signs to spaces. $user_string =~ s/\+/ /g; # This line places each name/value pair as a separate # element in the name_value_pairs array. @name_value_pairs = split(/&/, $user_string); # This code loops over each element in the name_value_pairs # array, splits it on the = sign, and places the value # into the user_data associative array with the name as the # key. foreach $name_value_pair (@name_value_pairs) { ($name, $value) = split(/=/, $name_value_pair); # These two lines decode the values from any URL # hexadecimal encoding. The first section searches for a # hexadecimal number and the second part converts the # hex number to decimal and returns the character # equivalent. $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/ge; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/ge; # If the name/value pair has already been given a value, # as in the case of multiple items being selected, then # separate the items with a " : ". if (defined($user_data{$name})) { $user_data{$name} .= " : " . $value; } else { $user_data{$name} = $value; } } return %user_data; }
E-Mailing the Input
After the user's input is decoded, the data is ready to be worked with. For the purposes of this example, let's have the CGI script send the information via e-mail. This requires an SMTP mail server to send the e-mail message. (SMTP stands for Simple Mail Transfer Protocol.)The way you interface with the SMTP mail server varies depending on whether you're using Windows or UNIX. So, let's create two subroutines, one for e-mailing on a Windows machine and one for e-mailing on a UNIX machine. Each takes a single argument, a string that contains the information that you want e-mailed. Because you will be using the same string regardless of which subroutine you call, let's write the code to put the string together first.
From where you left off in the previous section, your feedback.pl script has the user data in the associative array %data_received. If you just output this array to your e-mail message, the data would be one long string containing all of the information. To make that data easier to read, you can break it up into separate lines by using the following code.
foreach $key (sort keys(%data_received)) { $mail .= "$key:\n"; foreach (split(" : ", $data_received{$key})) { $mail .= "$_\n\n"; } }
This code contains two loops. The outer one loops over each array element. Recall that an associative array is indexed by elements known as keys. So, to loop over the entire array, you have to loop over all of the keys. You do this by using the Perl keys() function, which returns all of the keys for the associative array argument between the parentheses. Each key is then assigned to the $key variable. Once inside the outer loop, the key value gets added to the $mail string along with the \n character, which is the new line character. The inner loop then checks the value of each array element for the " : " (space colon space) characters. Recall from the User_Data subroutine that these characters are used to separate multiple values assigned to a single name-as with the flavors example in Listing 4.3. By splitting up the array on these characters, you can place each value on its own line.The line $mail .= "$_\n\n"; puts the values of the name/value pairs into the string that will be sent via e-mail. The .= is an assignment operator that means the same as $mail = $mail + (something else). In other words, it appends the information on the right side to the current contents of the $mail variable. You have to use this operator because you are placing all of the name/value pairs in the same variable. On the right side of the assignment operator, you place the $_ variable and two "\n" characters. The $_ variable is a special Perl variable that in this context is holding the value information of the current name/value pair. For example, the string Chocolate : Vanilla has two values. During the inner loop, the $_ variable would first be assigned Chocolate, which would get appended to the $mail string, and then assigned Vanilla, which would also be appended to the $mail string.
If you don't know Perl, this example should help to clarify what is happening. The feedback form has three input areas: the name input field, the e-mail input field, and the comments text area. For this example, you can set the contents of the name field to Robert McDaniel, the e-mail field to robertm@ deltanet.com, and the comments text area to Hi there. So, after calling the &User_Data(); subroutine, the %data_received associative array would have the following three elements:
$data_received{'name'}=Robert McDaniel $data_received{'email'}=robertm@deltanet.com $data_received{'comments'}=Hi there
The outer loop starts the formatting by looping over all of the sorted key values. So, the first value of the $key variable would be comments, followed by e-mail and then name. The $mail variable would first be set to comments:\n, and then the inner loop would start. Because the value of comments does not contain any " : "substrings, the loop only executes once with the $_ variable set to Hi there. This string and two \n's are then appended to the $mail string. At the end of the first iteration of the loop, the $mail variable is the following:
$mail=comments:\nHi there\n\n
After the next iteration, in which $key is set to email, the $mail variable is the following:
$mail=comments:\nHi there\n\nemail:\nrobertm@deltanet.com\n\n
Finally, after the final iteration of the outer loop, the variable $mail is the following:
$mail=comments:\nHi there\n\nemail:\nrobertm@deltanet.com\n\nname:\nRobert McDaniel\n\n
When the string is finally printed into an e-mail message, the contents of the message will be
comments: Hi there email: robertm@deltanet.com name: Robert McDaniel
E-mailing on a UNIX system
Now that you have the message formatted for e-mail, you can send it. If you're running UNIX, you will use the program sendmail as your SMTP server. Sendmail allows you to send the user's data in an output stream, so all you have to do is open the output stream, print the e-mail header necessary for sendmail, print the message string, and close the output stream. Listing 4.6 contains the Perl code for accomplishing these tasks. Because there is also a Windows version, I placed the code in a subroutine called Unix_Email.The Perl code to call this subroutine would be
&Unix_Email($mail);
Listing 4.6: Unix_Email Subroutine sub Unix_Email { local ($message) = @_; open(MAIL, "|/usr/sbin/sendmail -t") || die "Content-type: text/text\n\nCan't open /usr/sbin/sendmail!\n"; print MAIL "To: robertm\@robertm.com\n"; print MAIL "From: httpd\@robertm.com\n"; print MAIL "Subject: From you Feedback Form\n"; print MAIL "$message\n\n"; return close(MAIL); }
This line calls the subroutine with a parameter, namely the $mail string you just finished formatting. The Unix_Email subroutine received this parameter from the special Perl array variable @_. So, the line
local ($message) = @_;
just declares a local variable $message and assigns to it the value of the $mail variable used as the parameter. The line containing the open statement opens a new stream, which in this case is an output stream to the sendmail program. The path /usr/sbin/sendmail is the path to the sendmail program on my computer; your path may be different. The rest of the line causes the CGI script to exit if a stream to sendmail cannot be opened. The four print statements all send their output to the MAIL stream, which you just opened. The first three print lines print the header that sendmail needs to send the e-mail message. The final print statement outputs the $message variable, which contains the string that you formatted earlier. This string will be the body of the e-mail message.
E-mailing on a Windows system
If you are running Windows, you can use a program called WinSMTP for your SMTP server. WinSMTP has a command-line utility called WRMail that allows you to send a message from your CGI script. Unlike sendmail, WinSMTP cannot stream the output to WRMail. Instead you have to write the contents of the e-mail message to a file and then e-mail the file with WRMail. This involves a couple of extra steps, such as creating a unique file name, creating the file, and deleting the file after you are done. You will place this code in a subroutine called Windows_Email, the Perl code for which is shown in Listing 4.7.
Listing 4.7: The Windows_Email Subroutine sub Windows_Email { local ($message) = @_; # Create the file name $filename = substr(time, 3); srand(time||$$); $filename .= "." . int(rand(999)); # Create the file open(MAILFILE, ">$filename") || die "Content-type: text/text\n\nCan't open the output file $filename!\n"; print MAILFILE $message; close(MAILFILE); # E-mail the file system("c:\\winsmtp\\wrmail -r -t\"From Your Feedback Form\" - shttpd\@robertm.com -f$filename robertm\@robertm.com"); # Delete the file system("del $filename"); }
Most of this code should be fairly straightforward. The first section creates the file name by using the last several digits of the results of the time function (which returns the number of nonleap seconds since January 1, 1970) with a randomly generated extension between 0 and 998. Because this program could be executed several times at almost the exact same moment, you must take these precautions to guarantee a unique file name. The second section outputs the contents of the $message string into the file. As you see, to create a new file, you just open an output stream to the file. Finally, the code sends the e-mail by calling the WRMail program with the necessary parameters, as specified in the program's documentation, and deletes the temporary file.
Returning a Reply
After the information has been received and processed, the CGI script should send something to the user's browser to indicate that the action has completed. For the feedback.pl example, send your home page back to the user's browser. Because this HTML page already exists, you can just send the location of the file rather than sending all of the HTML tags and text. Here is the line of Perl code that returns the home page to the user's browser.
print "Location: http://www.robertm.com\n\n";
Now all the pieces of the feedback form are completed. Listing 4.4 contains the complete HTML for the feedback form. The previous few sections presented all of the code for the feedback.pl CGI script. Listing 4.8 supplies the entire feedback.pl script so you can see it all in one place.
Listing 4.8: The feedback.pl Script #!/usr/local/bin/perl # Decode the user data an place it in the # data_received associative array. %data_received = &User_Data(); foreach $key (sort keys(%data_received)) { $mail .= "$key:\n"; foreach (split(" : ", $data_received{$key})) { $mail .= "$_\n\n"; } } # If you want to run this program on a Windows # machine, comment out the &Unix_Email($mail); line (add a # at # the beginning) and uncomment the &Windows_Email($mail); # line. Also remember to remove the first line! &Unix_Email($mail); #&Windows_Email($mail); sub Unix_Email { local ($message) = @_; open(MAIL, "|/usr/sbin/sendmail -t") || die "Content-type: text/text\n\nCan't open /usr/sbin/sendmail!\n"; print MAIL "To: robertm\@robertm.com\n"; print MAIL "From: httpd\@robertm.com\n"; print MAIL "Subject: From you Feedback Form\n"; print MAIL "$message\n\n"; return close(MAIL); } sub Windows_Email { local ($message) = @_; # Create the file name $filename = substr(time, 3); srand(time||$$); $filename .= "." . int(rand(999)); # Create the file open(MAILFILE, ">$filename") || die "Content-type: text/text\n\nCan't open the output file $filename!\n"; print MAILFILE $message; close(MAILFILE); # E-mail the file system("c:\\winsmtp\\wrmail -r -t\"From Your Feedback Form\" - shttpd\@robertm.com -f$filename robertm\@robertm.com"); # Delete the file system("del $filename"); } sub User_Data { local (%user_data, $user_string, $name_value_pair, @name_value_pairs, $name, $value); # If the data was sent via POST, then it is available # from standard input. Otherwise, the data is in the # QUERY_STRING environment variable. if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN,$user_string,$ENV{'CONTENT_LENGTH'}); } else { $user_string = $ENV{'QUERY_STRING'}; } # This line changes the + signs to spaces. $user_string =~ s/\+/ /g; # This line places each name/value pair as a separate # element in the name_value_pairs array. @name_value_pairs = split(/&/, $user_string); # This code loops over each element in the name_value_pairs # array, splits it on the = sign, and places the value # into the user_data associative array with the name as the # key. foreach $name_value_pair (@name_value_pairs) { ($name, $value) = split(/=/, $name_value_pair); # These two lines decode the values from any UR # hexadecimal encoding. The first section searches for a # hexadecimal number and the second part converts the # hex number to decimal and returns the character # equivalent. $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/ge; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/ge; # If the name/value pair has already been given a value, # as in the case of multiple items being selected, then # separate the items with a " : ". if (defined($user_data{$name})) { $user_data{$name} .= " : " . $value; } else { $user_data{$name} = $value; } } return %user_data; }