Perl Cookbook, 2nd Edition by Tom Christiansen, Nathan Torkington The 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. 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 This page was updated August 16, 2007. UNCONFIRMED errors and comments from readers: (? (online version)) Section 8.8.2 (in Recipe 8.8. Reading a Particular Line in a File); do { $LINE = } until $. = = $DESIRED_LINE_NUMBER || eof; should be do { $LINE = } until $. == $DESIRED_LINE_NUMBER || eof; [3] Mid-page paragraph beginning "The q// and q// quoting ..."; The sentence "The q// and qq// quoting operators allow arbitrary delimiters on interpolated and uninterpolated literals, respectively, ..." should have the words 'interpolated' and 'uninterpolated' swapped. [6] both print statments; The first statement is : printf "char %s is code %d, %#04x\n", $char, $code, $code; the supposed output (given previous assignments) is char [capital delta] is code 916, 0x394 Using 5.8.4, I get the character ['I' with an arch over it] The second statement is: print "\xC4 and \x{0394} look different\n"; The supposed output is: char ['A' with an umlaut over it] and [capital delta] look different I get: Wide character in print at ... ['A' with '~' over it] and ['I' with an arch over it] look different {23} Mid-page example; Where both Unicode code points "Combining caron" and "Combining cedilla" are show together, they are reversed from the order given in the numeric values. Either swap the code lines $string = v99.807.780; and $string = v99.780.807; or swap the comment lines # COMBINING CARON # COMBINING CEDILLA and # COMBINING CEDILLA # COMBINING CARON Guide is that COMBINING CARON = U+030C = 780 COMBINING CEDILLA = U+0327 = 807 [45] sub parse_csv1 code; This subroutine treats a comma that is between double-quotes in the first field as if it were a field delimiter. This is out-of-spec for the subroutine. If the contents of $record is: "Howell, III","Thurston","1 Desert Island Way","Somewhere","South Pacific","00000"," ","OTHER","01","2005","","" Then: @fields = parse_csv1($record); $fields[0] is: Howell $fields[1] is: III ... (45) 3rd to last line from bottom of page; "Text:CSV" should be "Text::CSV" {82} first line of code in Discussion; "#or Math::Complex->new(3,5);" is redundant, perhaps it was intended to be "new Math::Complex (3,5);" (123) middle of page, sub EOL function defn.; Page# is from the 1st edition, but the sample program is the same in the current examples file. I tried to wrap this program in as a column-print function in my own program. It works perfectly the first time through, but all subsequent calls to the function the "EOL" function never resolves true resulting in some odd formatting. After a couple of hours of debugging I tried replacing the function call with a simple "if (($item+1..." call -- effectively replacing what was in the function definition with the same code in-line. This worked perfectly. I think the function as presented in the book is fine if it's the program itself, however for people like me who want some nice columnar output as a function, the use of the global variables and no paramater/reference passing causes it to break in unpredictable ways. {131} 4th block of code; In order to get the output "Time Flies Like An Arrow", the first splice statement should not include the original '@initiates' array. The current statement produces the unexpected output: Time Flies Like An Arrow An Arrow The splice statement should instead read splice(@members, 2, 0, "Like"); (191) 7th line from bottom of page; The value of $hdr_line is missing the initial "^" that is present on the 3rd line from the bottom of the page. It now reads: $hdr_line = '(?m:[^:]*:.*)'; It should read: $hdr_line = '(?m:^[^:]*:.*)'; {205} First paragraph; "A better solution is the qr// operator, which first appeared in v5.6" The qr// operator first appeared in Perl version 5.005. {220} Last solution on page; On pg 220, I was unable to make your sample solution work: /(?=^(?:(?!BAD).)*$)GOOD/s The above solution appears after the following sentence: "True if pattern BAD does not match, but pattern GOOD does:" However, I *was* able to make it work by adding a .* before GOOD: /(?=^(?:(?!BAD).)*$).*GOOD/s On a minor note, in the previous solutions on this page, the user-specified pattern references (/ALPHA/, /BETA/, /PAT/) are wrapped with '/' slashes in the descriptive sentences, except for this one, where you refer to them as GOOD and BAD instead of /GOOD/ and /BAD/. NOTE: I am still using Perl 5.6.1, so its possible that the regex engine has changed by v5.8. If so, a comment should be made to that effect [220] second solution; The "may overlap" regular expression: /^(?=.*ALPHA)BETA/s is incorrect. The "^" should not be present. [220] second solution; "True if both /ALPHA/ and /BETA/ match, but may overlap ..." The RE listed requires that BETA be at the beginning of the string (and even without "^", it has to be before ALPHA) A correct RE is: /(?:(?=.*ALPHA)BETA)|(?:(?=.*BETA)ALPHA)/s {237} Third item from top; Search for 'HREF' too aggressive and needs to require a separator before 'HREF'. Perhaps should include '\b' like so: ... ]+?\bHREF . {253} Chapter 7.14, Solution: 3rd example, last "chunk" of example; Example for non-blocking I/O using sysread() reads: $rv = sysread(HANDLE, $buffer, $BUFSIZ); or die "sysread: $!"; if (!defined($rv) && $! == EAGAIN) { # would block } else { # successfully read $rv bytes from HANDLE } The block of code needs reworking, as sysread returns zero on EOF (and there's a syntax error on the first line -- the line ends with a ";"): $rv = sysread(HANDLE, $buffer, $BUFSIZ); if (!defined($rv)) { if ($! == EAGAIN) { # would block } else { die "sysread: $!"; } else { # successfully read $rv bytes from HANDLE } {265} First code under title "Solution"; Code line use File::Temp qw/ tempdir /; should be use File::Temp qw/ tempfile /; Each routine used from module File::Temp must be specifically exported to be used without fully-qualifying the name. As written error message "Undefined subroutine &main::tempfile called at PC2ed02.pl line 12." is obtained at run-time. Similarly the next section of code should have line use File::Temp qw/ tempdir /; changed to use File::Temp qw/ tempdir tempfile /; to export both routines. (304) Last code line above section title "Advanced Operations"; In binmode(FH, "<:raw:crlf"); the 'layer' string contains an extra '<' character, perhaps from being copied from open() examples. While it does not cause any errors at compile-time, at execution time one gets the error: Invalid separator character '<' in PerlIO layer specification <:raw:crlf at PC2ed01.pl line 10. The line should read binmode(FH, ":raw:crlf"); (304) last sentence in I/O Layers section; "Recipes 8.18, 8.19, and 8.20" should read "Recipes 8.19, 8.21, and 8.21" instead. (306) second line from page top; The phrase "and stores 5 characters into $block," is at best confusing. Read literally it is plain wrong. Perhaps the entire last part of the sentence could be something like The sysread calls reads 256 characters from INFILE and stores them into $block after skipping past the first 5 characters of $block. [315] Solution of 8.7; @lines = shuffle(@lines); NOWREADS @reordered = shuffle(@lines); {317} second code line from page bottom; I believe the line should be changed from if (@lines > $line_number) { to if (@lines < $line_number) { The same logic line in Example 8-3 on the next page may also need to be changed similarly [Chaper 9 example 7] The lst cookbook example (Chap 9 example 7) is really useful but it has a typo in it on line 10. ..... or die << DEATH; should not have spaces around the << i.e. ...... or die< $saved_size; $saved_size = -s _; should be: return unless -f && -s $_ > $saved_size; $saved_size = -s $_; [361] 1st full example; $age = (stat(_))[9]; should be: $age = (stat($_))[9]; [367] discussion; The last sentence in the first paragraph of the Discussion, "For more fine-grained control, Stat::lsMode offers format_mode ..." describes how file_mode is stated to work in the previous sentence. The next paragraph confuses the issue further. "The format_perms function ... " is not in the following example. The example shows file_mode working as it did in the first Discussion paragraph, but format_mode returns a 9-character string, which is what format_perms was supposed to do. [367] entire page; addition to previous submission: The last sentence in the first Discussion paragraph states that format_mode takes a numeric permissions value. That now seems to be OK, but the Solution is supposed to convert numeric permissions, yet file_mode is called with $pathname as an argument. The whole Solution and Discussion need to be rewritten. (394) Code under title "Using local() for temporary values for globals"; More specifically the line having the comment "pass filehandle by glob reference." I think the intent of the author was to show the form "\*FH". As it is this line is identical to the line above. I believe the second line reading $para = get_paragraph(*FH); should read $para = get_paragraph(\*FH); to agree with the comment. [401] Solution example; The example includes: case (@array) ... case (%hash) ... The lines ought to be case [@array] ... case [%hash] ... (410) Near page bottom, line of code beginning "return ..."; Quite unusual usage - undef is most often seen without parentheses. Perhaps at some previous point the line read "return $errcount ? () : %record;" which is a different idiom. (430) Fourth(?) paragraph, beginning "Options available as of ..."; Option named 'quoteHighBit' is repeated twice, possibly because it is repeated twice in the documentation for module Dumpvalue. Also, wondering why module Dumpvalue is not mentioned under "See Also" for this section. [438] first paragraph (code for sub insert_value); I believe that insert_value is incorrect since it weakens all references to the newly created node, exposing it to garbage collection as soon as &insert_value returns. Clearly, at least one reference to $node must stay strong. Since $node is not returned from $insert_value, its caller cannot maintain the strong reference. I believe it would be better to have for example all PREV references weak and all NEXT references strong with the exception of the last one, i.e., $ring->{PREV}- >{NEXT} which should be weak (to break the circle). This state of affairs can be maintained by not calling weaken on $node->{NEXT} nor on $ring->{NEXT} in &insert_value, by not calling weaken on $node->{PREV}->{NEXT} in &delete_node and by continuing to call weaken on both $node->{NEXT} and $node->{PREV} in &node. [495] sub modname; line 68 is (page 495 - sub modname): if (index($_, $Start_Dir . "/") = = 0) { But should be: if (index($_, $Start_Dir . "/") == 0) { Also, I thought that the line 73 adds some extra spaces. line 73 is (page 495 - sub modname): s { \.p(m|od)$ } { }x; But was replaced by: s { \.p(m|od)$ } {}x; [497] end of pmdesc program; lines 139-141 (page 497 - end of program): format = ^<<<<<<<<<<<<<<<<<~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $Module, $_ . But write command in page 496 (sub wanted, inside while) is commented, so I commented lines 139-141: #format = ^<<<<<<<<<<<<<<<<<~~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #$Module, $_ #. After this correction, the following error is eliminated: syntax error at ./pmdesc.pl line 139, near "format =" Execution of ./pmdesc.pl aborted due to compilation errors. (533) Second code section from top; On this page and next, confusion as to whether the routine is called 'insert' or 'insert_value'. The definition is sub insert_value { However the call on page 533 is for ($i = 0; $i < $COUNT; $i++) { $r->insert($i) } and the comment on page 534 is # $ring->insert( $value ) : insert $value into the ring structure Since 'delete_value' is defined and used with that name I believe that the two occurrences of 'insert' should be changed to 'insert_value'. {535} penultimate paragraph; The paragraph incorrectly states that the functions will be called as method invocation. They'll simply be called as functions that are passed the object as its first argument. This is critical when you consider what happens if you later subclass TimeNumber and redefine any of the functions in the subclass. If the functions had been named rather than passed in by reference: use overload '=' => "my_plus", '-' => "my_minus", etc Then the methods in the subclass would be called. As the example stands however the functions in the origianl class are called. {638} Solution Listing; First the close(WRITEME) must done _before_ the output could be captured. Second output may be multiline, so output should be an array. $pid = open2(*README, *WRITEME, $program); print WRITEME "here's your input\n"; close(WRITEME); # <--- CHANGE @output = ; # <--- CHANGE close(README); waitpid($pid, 0); [648] the code for example #10 in recipe 16.11 shows an error: now: $SIG{ALRM} = sub { close(FIFO) }; # move on to the next queued process should be: $SIG{ALRM} = sub { close($fifo) }; # move on to the next queued process to be consistent with the rest of the program. The global filehandle FIFO isn't used anywhere else. ?612? Example code for section 15.17.; The string "$^0" appears to be an error. However, I am told that the correct string is "$^O". It's an oh, not a zero. This seems impossible to determine given the font, and might conceivably be worth a clarifying note, especially in light of the expression being used in a way that suggests numbered, passed-in environmental data. [664 (3rd Edition)] Solution 16.21; Background SunOS, (Perl 5.8.0) and Win XP (ActiveState 5.8.8): Timing out the operation as per the code of the example fails. $@ is always '' at the the line 'die if $a && $@ !~ /.../ #reraise' I've tried two "patches" --- In the first I added a variable and test it after the eval. --- In the second I re-raised the "die $@ if ($@" as in [code] eval { local $SIG{ALRM}=sub { die "Timing out!"; }; alarm 1; eval { die "Dying, dying, dying!"; }; alarm 0; # added die $@ if ($@); # added }; alarm 0; print "after eval \$\@='$@'\n"; die $@ if ($@ && $@ !~ /Timing out!/); [/code] (679) last paragraph; Change: which is nicely wrapped by the standard IO::Socket class to: which is nicely wrapped by the standard IO::Select class (748) XML-RPC Client Solution code; "$result = $server->call('ClassName.handler', @ARGS);" should be "$call = $server->call('ClassName.handler', @ARGS);" (750) SOAP Client Solution code; "$result = $server->call('ClassName.handler', @ARGS);" should be "$call = $server->call('ClassName.handler', @ARGS);" {753} 4th line of code; The line: if ($stat[2] & 177) { should read: if ($stat[2] & 0177) { {760} Example 19-1; The hiweb cgi script will not run as is. It does not ask for user input, it generates an error "Use of uninitialized value in string eq at (eval 6) line 3." and it prints nothing out after the "You typed:" prompt. I believe for the problem may be that there should be an HTML page that describes the simple FORM that invokes this cgi script. It would be nice if complete examples were given, not merely the PERL aspect of an example. I am invoking the hiweb script directly from a browers ala: http:/mywebserver.com/cgi-bin/hiweb.cgi {794} table at bottom of page; The third entry under "Guess" is file:/etc/passwd - it ought to be file:///etc/passwd {796} first sentence of Solution; The first sentence ends "use the GET method on an LWP::UserAgent object:", but the code uses LWP::Simple and calls get() as a procedure. [801] code in Discussion; The call to format_string() has no argument that holds the string to be converted to ASCII. {808} example 20-7; At the bottom of the page, after "END_OF_SELECT", there should be a line like that in example 20-8: $sth-execute() or die "couldn't execute SQL";