CGI Programming with Perl, 2nd Edition by Scott Guelich, Shishir Gundavaram & Gunther Birznieks Unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. This page was updated June 22, 2004. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification UNCONFIRMED errors and suggestions from readers: [20] URI Encoding (line 32 from the top): My question is about the sentence below: The following characters: - _ . ! ~ * ' ( ) In the book, tilde "~" is said to be an exception for URL encoding, but according to RFC1738 (Uniform Resource Locators) section 2.2, tilde should be encoded. {21} both code fragments: The s///; command in both of the code fragments on this page should include a /g option. {29} Table 2-2: The header named "Authentication" in the table should actually read "Authorization". This is according to both the text (see the bottom of page 29) and RFC 2616. (32) paragraph 2: "A example" should be "An example"; "protocol are" should be "protocol is". (35) paragraph 1: "differs for the Web than" should be "are different for the Web than". (However the phrase "different than" is almost never correct. "Different from" should be used.) (50) 1st paragraph; It says, "The first foreach loop iterates over %ENV to add any additional environment variables defined by the current web server. Then the foreach loop iterates through the combined list and displays the name, description, and value of each environment variable." This is confusing... the 2nd sentence should specify that it is the _second_ foreach loop. (60) Example 3-4: (last line on this page) spurious ";" between"`binmode" and "STDOUT;" (63) Example 3-5: spurious ";" between "binmode" and "STDOUT;" (64) Second paragraph, I guess - I'm reading from an html book; When the author says: "If you are especially paranoid (and do not mind the extra traffic it causes), then you could also output a Pragma: no-cache header for the real images too." He is considering the example script 3-5 wasn't printing the mentioned pragma for the real images, but, actually, the script does output the pragma for the real images too - this happens because the "display_image()" subroutine prints that pragma for any image file it prints out. So, this paragraph doesn't make sense. (95) ... to change CGI.pm's behavior ro allow access to all parameters via param... I think "ro" should become "to." (95) 2nd paragraph; Missing period/full stop at end of paragraph {96} after the 6th paragraph: The book indicates that "If you wish to print the value of someone's input, you can use an intermediate variable:" The code that follows is incorrect: my $name = $q->param( 'user' ); print "Hi, $user!"; The proper code should read: my $name = $q->param( 'user' ); print "Hi, $name!"; The code as written gives you this error: Global symbol "$user" requires explicit package name [98] next-to-last line: my $fh = $q->upload( $file ); On Win32 systems (I'm not sure about Unix) it appears that the "upload" method should be passed the name of the "upload field" from the HTML form (which in this case is "file") rather than the path and name of the file itself ($file). From the CGI documentation: -------------------------------------------------------------------- When the form is processed, you can retrieve the entered filename by calling param(): $filename = $query->param('uploaded_file'); ... (some stuff snipped out) ... To be safe, use the *upload()* function (new in version 2.47). When called with the name of an upload field, *upload()* returns a filehandle, or undef if the parameter is not a valid filehandle. $fh = $query->upload('uploaded_file'); while (<$fh>) { print; } {98} (minor, because it still kind of works as is) middle of page 98, HTML snippet that begins...
", you should include: which will create a submit button for this form. A submit button does not show up when the HTML snippet is used as is (but by pressing return on the keyboard, one can get the form submitted). [98,99] Last line on 98; next to last line on 99; On both 98 and 99, my $fh = $q->upload( $file ); should be my $fh = $q->upload( "file" ); In the first paragraph on 99: "Be sure that you pass upload the name of the file according to param, and not a different name..." would be more clear as: "Be sure the name that you pass upload is the same name you would pass to param, and not a different name..." As it is, it makes it sound like you are passing the name returned from param, which is wrong (but it matches the code samples, which makes it even more confusing. {99} Example 5-2 4th line of text; the line use constant UPLOAD_DIR => "/usr/local/apache/data/uploads"; needs a trailing slash for it to work. At least on Mac OS X, the example given does not let the process get through to a directory. [98-99] Bottom; Comfirmation that on Unix, Mac OS X and Mac OS that the argument to upload should be the name of the param not the name of the file. {99} bottom of page Perl example 5-2: upload.cgi the 2nd to last line reads: my $fh = $q->upload( $file ); When typed in as written, the example did not work on my computer. So I looked through the CGI.pm file, and came across a section which seemed to suggest that the line should in fact read: my $fh = $q->upload( "file" ); {100}middle of page Perl example 5-2: upload.cgi The section that begins... # Open output file, making sure the name is unique until ( sysopen... { .. .. } I'm not a Perl expert, so I don't know why that section didn't work, but I had to comment that out and replace it with a simpler snippet: #### OPEN OUTPUT FILE open (OUTPUT, ">" . UPLOAD_DIR . "/" . $filename) or print "Hey...can't open file UPLOAD_DIR/$filename: $! \n"; Those two changes finally enabled the perl script to run without errors (on my computer at least). [100] example 5.2, upload.cgi: The regular expression to open a unique output file only works if the user-supplied filename contains an extension separated by a period. If this is not the case, the regex fails, a large amount of garbage is printed to the error log, and the connection eventually times out. The solution is to fix the regex on line 31 of the program so that it validates the filename correctly, and adds an extension if one is necessary. For example: if ( $filename =~ /^(\w[\w-]*)(.[\w.-]+)?/ ) { $filename = $1 . ($2 || '.err'); } Text description: From the start of the line, ensure that there is a string that starts with a word character and is continued by word characters or hyphens. This may, but need not be, followed by a period, followed by one or more characters that can be either word characters, periods, or hyphens (to accommodate things like foo.tar.gz). The filename is the part before the extension concatenated either with the part after the extension (if present) or the string '.err' (if not present). << The user errata at the book web site indicate that others have had problems with this, but the proposed solution is to delete the code and replace it with a much simpler file check that does not have nearly as much error handling. The above solution preserves this error handling and should be universally applicable (it even works if the filename ends in a period). (101) near end of code example 5-2; 'procesed' should be 'processed' 'occured' should be 'occurred' These spelling mistakes are also present in the downloadable source code. [105] start_html tag: This would seem to preclude the use of frames with CGI.pm start_html because FRAMESET is allergic to BODY tags. It must be possible since at the bottom of p 104 the -target argument is discussed. Is it possible for CGI.pm to redraw just one frame? My current cgi redraws all the frames even though only one changes. [110] line 15; -name => "curtain" should be: -name => "look_behind" because lines 21 - 23 have: NAME="look_behind" (119) 1st paragraph; "name of module" should be "name of the module" {134} Table 6-4; The last attribute in the table is listed as: NAME="/file/path" It should be: NAME="/path/file" (140) 3rd paragraph; "the model ... often end up" should be "the model ... often ends up" (141) paragraph 1: "this works similar" should be "this works similarly". {142} embpcgi.pl: you can also use the embpcgi.pl CGI script that is distributed with Embperl. "embpcgi.pl" is no longer included in the current Embperl distributions (1.3.x and later) on CPAN. I believe the last version that it can be found in is 1.2.1. [150-161] all of the embperl news page examples: In your example of EMBPerl, there appears to be an issue with the edit function. Although it will work fine if run form the embpcgi.pl, editing a story will not work at all while running in mod_perl, as it is currently written. When the story is read, a shared lock is placed on the file, as mod_perl does not terminate, like the cgi script, this lock is maintained and will not allow you to write to the file. This results in no editability unless the cgi is used...Obviously, a poor choice if mod_perl is an option. The approach I would take is to add a cleanup sub to unlock the file for editing, run every time the page is compleatly loaded...or at least from talking to people this would seam to be the solution (mod_perl EMBPerl newsgroup). Oh, just in case I am wrong...The reason that the lock came to mind was I reset apache (and subsiquently mod_perl) and then saved after going to the edit_article.epl?, and then it worked. {158} code above HTML header: The code for the if-then statement should be swapped: if ( $fdat{story} ) { ( $fdat{headline}, $fdat{article} ) = News::get_story( $fdat{story} ); } elsif ( $fdat{save} ) { News::save_story( $fdat{story}, $fdat{headline}, $fdat{article} ); $http_headers_out{Location} = "edit_news.epl"; exit; } should be: if ( $fdat{save} ) { News::save_story( $fdat{story}, $fdat{headline}, $fdat{article} ); $http_headers_out{Location} = "edit_news.epl"; exit; } elsif ( $fdat{story} ) { ( $fdat{headline}, $fdat{article} ) = News::get_story( $fdat{story} ); } The way it is now, "$fdat{story}" will always match, even after you've pressed the "submit" button. The check for "$fdat{save}" should come first because that will only match if you click the "submit" button. As it is, you never get redirected back to the "edit_news.epl" page. (172) Function requireValues: The requireValues function names its two arguments "form" and "requiredValues" in the definition. However, the second line of the function reads: element = requiredText[i]; It should be: element = requiredValues[i]; Either that or the "requiredValues" argument should have been named "requiredText." (172) paragraph 4: "may be supported" should be "may not be supported". <175> 3rd paragraph, line 9; It says, "As we mentioned earlier, JavaScript may be supported by the user's browser or it may be turned off." It should say, "As we mentioned earlier, JavaScript may not be supported by the user's browser..." (176) 2nd line; It says, "...because the it doesn't know..." "the" should be removed (176) 3rd line from bottom: "makes" should be "makers". (184) 4th paragraph, after code; It says, "...the value of the concert field is added it to the concertList select list." "it" should be removed. (185) 2nd paragraph, line 3; period missing at end of sentence: "...value of the filename field If no song..." (188) paragraph 3: "convert to this to" should be "convert this to". (189) last paragraph, 2nd to last line; "...which users you are willing to loose." "loose" should be "lose" [190] 4th from last line; $q->textarea(........... -value => $comment ), should be: $q->textarea(........... -default => $comment ), because textarea has no value property (per pg. 79). (201) 2nd paragraph in "Trusting the Browser", 2nd-3rd line; it says, "...each page calls the same CGI script to processes the transaction." "processes" should be "process" {201} second line; a dollar sign is missing: FIGLET is a bareword, should be the scalar variable $FIGLET (202) last paragraph, line 3; "Hypertext Transport Protocol" the chapter was changed to "Hypertext Transfer Protocol" (204) 1st paragraph under "Encryption", line 4; It says, "A secure https connections using SSL..." "connections" should be "connection" (207) 1st paragraph after code, line 3; It says, "Because our examples this chapter intentionally showed..." It should say, "Because our examples in this chapter..." (207) 2nd paragraph, 1st sentence; It says, "The purpose of taint mode is to not allow any data from outside your application from affecting anything else external to your application." It should say, "The purpose of taint mode is to not allow any data from outside your application to affect anything else external to your application." **OR** "The purpose of taint mode is to keep any data from outside your application from affecting anything else external to your application." (216) paragraph 1: "None of us like spam" should be "None of us likes spam". (222) paragraph 1: "sender to be that" doesn't make sense. {223} Example 9.1: The instruction that starts $q->p( "The email address ou entered ....."); should read $q->p( "The email address ou entered ....."), so the next instruction can be printed ($q->end_html). (225) paragraph 3: "... the web server runs as has ..." doesn't make sense. [226] two lines of Perl code in "mailx and mail" section; The code appears exactly as printed in the next 2 lines: open MAIL "|-" or exec( "/bin/mail", $email ) or die "Cannot exec mail $!"; First, there should be a common between MAIL and "|-" More importantly, although the text states that this code uses the "fork and exec" trick, there is no explicit call to fork (although the OPEN does implicitly call fork) The exec will only take place if the open fails! {227} 3rd paragraph (code); close $mailer should be: $mailer->close; {240} example 10-3. line 17; if ( exists $dbm_hash{$username} ) { $email = $q->a( { href => "mailto:$dbm_hash{$username}" }, $dbm_hash{$user_name} ); "$user_name" should be "$username" {243} code: $mary{email} = 'mary..'; #... should read $mary->{email} = 'mary..'; #... {248} connect statement at bottom of page; According to the accompanying text, the name of the driver in the connect() call should be DBD:CSV, not DBI:CSV [256] The getQueryResults subroutine at the end of page 256 to page 257; The query's don't actually return anything. I am unable to figure this out as I am using the book to teach myself CGI programming. If you do a query without filling in the fields of the query screen, it returns every entry in the database. If you put anything in the fields, including exact matches, it returns nothing. I typed the entire script from the book and when this problem occurred I downloaded the script from your website (Under Chapter 10 cgi2_examples.tar.gz) and it behaves the same way. I compared the downloaded version to your book and it is the same. (257) half way down page in else of $where_clause: You are using the "like" predicate in the where clause, but you should be using the "clike" to do case-insensitive matching. like does case-sensitive matching (at least in the latest SQL::Statement perl module). [259-260] map functions; The use of map(... " = \"" ... "\"" on pg 259, and twice on pg 260 do not work for SQL code. You should be using single quotes around where logic literals, as it was done on pg 258 in the doAdd subroutine, using "push(@value_array, "'" . $value . "'"); (264) 1st paragraph, 1st line; "Chapter 2, The Hypertext Transport Protocol, ..." "Transport" was changed to "Transfer" in chapter name. {294} Example 12-1, 15th line; my $DOCUMENT_ROOT = $ENV{DOCUMENT_ROOT}; should be changed to: my( $DOCUMENT_ROOT ) = $ENV{DOCUMENT_ROOT} =~ /^([\w:/\\-]+)$/ or die "Unsafe document root!"; There is a syntax error in the reg expression. It should be something like: m|^([\w:/\\-]+)$| Otherwise the / in the middle closes the reg exp. {298} near the top and in middle of page: In this script, you mentioned that the declaration of the file in your errata should go right before the foreach() loop. Unfortunately, that doesn't work when using script. To fix this, just add: my $file; my $full_path; right after declaring the variables for DOCUMENT_ROOT and VIRTUAL_PATH. {298} bottom of the page, same script as above: Every time I check the syntax of the problem, I get this read back to me: Can't use global @_ in "my" at grep_search2.cgi at line 83. This is referring to the declaration: my( $text ) = @_; {303} 11 lines down in the code: The errata says to change the current regex in the book from: s/<.+?>//gs; to s/<(?:[^>'"]*|(['"]).*?\1)*>//gs; But with regex, the following error occurs at runtime: /<(?:[^>'"]*|(['"]).*?\1)*>/: regexp *+ operand could be empty at indexer.pl line 91. [309] 5th line of code - Specifically the sort code; The sort is coded to sort the number of matches from lowest to highest. Search engines usually show highest number of matches first. To allow this, the sort should be coded as follows (swapping $a->[0] and $b->[0]): sort { $matches{$b->[0]} <=> $matches{$a->[0]} || $a->[1] <=> $b->[1] } {313} first paragraph after bulleted list: The uri http://www.cdrom.com/pub/png/pngintro.html is outdated and lists http://www.libpng.org/pub/png/pngintro.html as the correct location. I, however, have not been able to verify that this uri works, as www.libpng.org appears to be down right now. {408} The URL that reads: http://www.activestate.com/PPM should be changed to: to http://www.activestate.com/ASPN/Downloads/ActivePerl/PPM.