#!/usr/bin/perl # nudecode -- New uudecode # Brian Katzung 20 August 1994 # # Copyright 1994 by Brian Katzung # # This software is provided on an as-is basis. Use it at your own risk. # This software may be distributed for free. It may not be sold, either # separately, or as part of a package, without written permission from # the author. # # usage: nudecode [-c#] [-owhat] [-rold new] [-denwsv] [--] [file...] # -c# Set number of contiguous lines to recognize continuation # -owhat Only extract some files (by name or number) # -rold new Rename to new name on extraction # -d Debug mode # -e Allow extended character set (default auto) # -n Disallow extended character set (default auto) # -w Allow overwrite of existing files # -s Allow slashes (directory components) in file names # -v Verbose $NO = 0; $YES = 1; $MAYBE = 2; ###################################################################### # Decode one file sub decode { local($file) = $_[0]; local($myExtended); local($inData); local($valid); local($line); local($mLength); local(*FILE); local(@full); local(@partial); unless ($debug == $YES || open(FILE, ">$file")) { print STDERR "nudecode (error): $!: $file\n"; return ''; } $myExtended = $extended; $mLength = 60; $inData = $YES; while (<>) { last if /^end|^begin [0-7]{3,4} /; # Determine how much of the line contains valid data. $valid = $_; if ($myExtended == $NO) { $valid =~ s/[^ -`].*//; } else { $valid =~ s/[^ -~].*//; } if (($myExtended == $NO? /^[^ -M`]/: /^[^ -M`-~]/) || (length($valid) - 1) * 6 < (((ord() - 32) & 077) * 8)) { # This line has an invalid byte count or is # too short for the specified byte count. if ($debug == $YES) { foreach $line (@full, @partial) { # Lines we considered print "? $line"; } # The invalid line print "- $_"; } @full = (); @partial = (); $inData = $NO; $myExtended = $NO if $myExtended == $MAYBE; next; } if (($inData != $YES)? /^M.{60,$mLength}() *$/: /^M.{60,$mLength}(.*[^ \n])? *$/) { # This is a full length line. # Adapt to extraneous slop. $mLength += length($1); if ($#partial >= 0) { if ($debug == $YES) { # Lines we considered print "? $line" while $line = pop(@full); } @full = (); } push(@full, $_); if ($debug == $YES) { # Lines we considered print "? $line" while $line = pop(@partial); } @partial = (); $inData = $YES if $#full >= $context; unless ($inData == $NO) { $myExtended = $YES if $myExtended == $MAYBE && /[a-~]/; if ($debug == $YES) { # The data we would actually use print "* $_" while $_ = shift(@full); } else { print FILE unpack('u', $_) while $_ = shift(@full); } } } else { # This is either one of the last (short) # lines or junk. if ($#partial == 1) { if ($debug == $YES) { # Lines we considered print "? $line" while $line = pop(@full); print "? $partial[0]"; } @full = (); $inData = $NO; $myExtended = $NO if $myExtended == $MAYBE; shift(@partial); } push(@partial, $_); } } $line = $_; if (eof() || /^end/) { foreach (@full, @partial) { $myExtended = $YES if $myExtended == $MAYBE && /[a-z]/; if ($debug == $YES) { # Data we would actually use print "* $_"; } else { substr($_, 0, 1) =~ tr/a-z/!-:/; print FILE unpack('u', $_); } } } elsif ($debug == $YES) { foreach (@full, @partial) { # Lines we considered print "? $_"; } } close(FILE); print "nudecode (warning): Decoded extended character set: $file\n" if $extended == $MAYBE && $myExtended == $YES; print "nudecode (warning): Lines have extraneous characters (", $mLength - 60, "): $file\n" unless $mLength == 60; if ($line =~ /^end/) { print "@ end\n" if $debug == $YES; ''; } else { print "nudecode (error): No end line: $file\n"; $line; } } ###################################################################### # Main program $context = 3; # Good context -1 required for confidence $debug = $NO; $extended = $MAYBE; # Use extended character set %only = (); # Select some from several in stream $overwrite = $NO; # Overwrite existing files %rename = (); # Rename on extraction $slash = $NO; # Allow slashes in names $verbose = $NO; while ($ARGV[0] =~ /^-/) { $context = $1 - 1 if $ARGV[0] =~ /-c(\d+)/; $debug = $YES if $ARGV[0] eq '-d'; $extended = $YES if $ARGV[0] eq '-e'; $extended = $NO if $ARGV[0] eq '-n'; $only{$1} = 1 if $ARGV[0] =~ /-o(.+)/; $overwrite = $YES if $ARGV[0] eq '-w'; if ($ARGV[0] =~ /^-r(.*)/) { $rename{$1} = $ARGV[1]; splice(@ARGV, 0, 2); next; } $slash = $YES if $ARGV[0] eq '-s'; $verbose = $YES if $ARGV[0] eq '-v'; last if shift(@ARGV) eq '--'; } $item = 0; while (<>) { # Ignore everything up to a "begin" line unless (/^begin ([0-7]{3,4}) ([^\r\n]*)/) { # Lines skipped during "begin" scan print "/ $_" if $debug == $YES; next; } # A begin line print "@ $_" if $debug == $YES; # Skip this file if we're only supposed to extract some files # from the input stream and neither the item number nor any # flavor of the file name appears in the "only" list. # Renaming also occurs here. ++$item; ($f1 = $2) =~ s/[\000-\037\200-\377]/_/g; # No invisibles $f2 = $f1; $f2 =~ s/\//_/g unless $slash == $YES; # No slashes $file = $rename{$f2} || $f2; next if scalar(%only) && !($only{$item} || $only{$f1} || $only{$f2} || $only{$file}); if ($overwrite == $NO && -e $file) { print "nudecode (note): Skipping existing file: $file\n"; next unless $debug == $YES; } if ($verbose == $YES || $debug == $YES) { print "nudecode (status): Decoding file: "; print "$f1 -> " if $f1 ne $file; print "$file\n"; } $_ = &decode($file); chmod(oct($1), $file) unless $debug == $YES; redo if $_ ne ''; last if eof(); } 0;