Tracking line numbers for warnings can be tricky. Sometimes it's neccessary to put little progress report warnings in the uncertain area, as in
warn "before if";
if ($_[$i] =~ /^(.+?)\.(zip|t?gz|tar|bz2?|tbz)$/i) {
warn "in if"
...
elsif (...) {
warn "in elsif 1";
...
else {
warn "in else";
With these in place, you can tell where the "undefined" warning comes in the sequence of execution.
It's messy, but you only have to look at it once, for a quick reality check. The debugger is another way to do this, which could be easier or harder, depending on your inclination to switch over to the debugger mindset.