whisp has asked for the wisdom of the Perl Monks concerning the following question:

I am calling a module (parse.pm in library PDF-111 from CPAN) that dies when it encounters a PDF with an invalid internal structure so:
$_=PDF::Core::PDFGetline ($fd,\$offset); die "Can't read cross-reference section, according to trailer\n" if +! /xref\r?\n?/ ;
This then cause my program to die. I want to recover from the error and go on and process the next file. I am new to Perl and the tutorial materials I've found seem to feel that error handling is too advanced to cover. Does anyone have any ideas? Thanks!

Replies are listed 'Best First'.
Re: Life after DIE
by derby (Abbot) on Jan 11, 2002 at 23:26 UTC
    whisp,

    There are a couple of different ways to handle this. Are these

    $_=PDF::Core::PDFGetline ($fd,\$offset); die "Can't read cross-reference section, according to trailer\n" if + ! /xref\r?\n?/ ;

    your code or internal to the PDF module? If they're yours, don't die, just use warn and then break out of what ever control loop you have. Or, wrap the call in an 'eval'

    eval { PDF::whatever_method(); }; if( $@ ) { print "Ooops an error occurred processing that pdf file; }

    Basically you need to give us more info on the processing loop. I'm assuming it's something like this:

    while( @FILES ) { $somevar = new PDF( $_ ); process( $somevar ); }

    if that's the case, wrap the `"per file" code in an eval:

    while( @FILES ) { eval { $somevar = new PDF( $_ ); process( $somevar ); }; if( $@ ) { print "Ooops on $_\n"; } }

    That should help.

    Updated: Forgot the semi-colon on one of the eval blocks - that always bites me.

    -derby

Re: Life after DIE
by lhoward (Vicar) on Jan 11, 2002 at 23:46 UTC
    Another option would be to replace the DIE handler for the execcution of the step that throws the error:
    $SIG{__DIE__}=sub( my $d=shift; if($d eq 'error to really die on'){ print $d; exit; }elsif($d eq 'some other die to handle specially'){ # do some special code to recover from this die } #just return to code on other dies ); $_=PDF::Core::PDFGetline ($fd,\$offset); die "Can't read cross-reference section, according to trailer\n" if ! +/xref\r?\n?/ ; #resturn to default DIE handler $sig{__DIE__}='DEFAULT';
Re: Life after DIE
by jsegal (Friar) on Jan 12, 2002 at 00:05 UTC
    Hi. The standard way of dealing with "die" is to place an "eval" around the code you call which might call die, and check the $@ special variable to see if die was called. for example, if the function "read_pdf" has a die somewhere inside it, and you want to safely call it, instead of

    my $pdf = read_pdf("foo.pdf");
    try
    my $pdf; eval { $pdf = read_pdf("foo.pdf"); }; if ($@) { #read_pdf called die, handle error gracefully.. } ...

    This is the perl-way of doing "throw" and "catch" -- think of die as "throw" and eval as "catch".
    NB: That semicolon after the eval block is important, and easy to miss. (It separates the "eval" from the "if" -- otherwise the parser will think the if is a postfix if, and you'll get a parse error).

    Hope this helps...

    -Jonathan
Re: Life after DIE
by cLive ;-) (Prior) on Jan 11, 2002 at 23:32 UTC
    Error handling's not too hard - you need to eval the statement and then check if it was OK. Eg:
    eval '$_=PDF::Core::PDFGetline ($fd,\$offset);'; if ($@) { # deal with error } # ok, so carry on

    cLive ;-)

Re: Life after DIE
by whisp (Novice) on Jan 12, 2002 at 01:06 UTC
    Thanks to everyone. I think the EVAL is the piece I was missing. To clearify anyone else, the previous code snipet was in the called module. My relevant code is:
    while (defined($file = readdir(DIR))) { # Read directory entries print "Processing $file"; if ($file=~/.pdf$/i) { # If PDF, &Handle_Acrobat_File } # Go Extract Info sub Handle_Acrobat_File { my $PDF= PDF->new ( $file ); # Extract summary data if ($PDF->IsaPDF) { # Is this really a PDF? $bkTitle = $PDF->GetInfo("Title"); # Assign title $bkGrp = $PDF->GetInfo("Subject"); # Assign group $bkAuth = $PDF->GetInfo("Author"); # Assign Author print LNKS "$bkGrp|$bkTitle|$bkAuth|$dirname\\$file\n"; }
    I think I can change the my $PDF= PDF->new ( $file ); to use an EVAL and be fine. Again, thanks to all who responded.
Re: Life after DIE
by xylus (Pilgrim) on Jan 11, 2002 at 23:16 UTC
    If this is being referenced in a loop you could try something like this:
    $_=PDF::Core::PDFGetline ($fd,\$offset); next if !/xref\r?\n?/;
    instead of die. This will go on to process the next file.
Re: Life after DIE (boo)
by boo_radley (Parson) on Jan 11, 2002 at 23:43 UTC
    Hopefully this will give you some ideas.
    print "in\n"; eval ("die 'boing'"); print "out\n"; print $@;
    see perldoc -f eval for more info

    Since so many people have come up with the same answer, I'd like to tack on a question : Is dying the appropriate way for (it seems, I'm not familiar with this package) a file processing package package to behave?