Path: utzoo!attcan!uunet!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!uwm.edu!rutgers!mcnc!uvaarpa!mmdf From: martin%easby.durham.ac.uk@CORNELLC.cit.cornell.edu (Martin Ward) Newsgroups: comp.lang.perl Subject: (none) Message-ID: <1990Jul4.185533.7282@uvaarpa.Virginia.EDU> Date: 4 Jul 90 18:55:33 GMT Sender: mmdf@uvaarpa.Virginia.EDU (Uvaarpa Mail System) Reply-To: martin%easby.durham.ac.uk@CORNELLC.cit.cornell.edu Organization: The Internet Lines: 203 Here's a script I cooked up to make a general "file viewer". It reads the first 1k or so of the file (from standard input or given as an argument) and decided what set of filters are required to turn it into something readable which is then viewed with the "less" pager. For example uuencoded compressed tar files are passed through "uudecodef | uncompress | tar tf -" to give a list of the files in rthe tar archive. If the output is to a terminal then the "less" pager is used and an informative header added to show what filters were used. If the output is to a program then no pager or header is used. uudecodef and btoaf are hacked versions of uudecode and btoa which: 1) Work as simple filters 2) Ignore checksums and "short file" errors Using a pipeline of filters, and only looking at the first part of a file, means that the first page of output should appear quickly even for extremely large files. I am not entirely happy with the "filterbuf" subroutine (which sends the contents of the $buf string through the given filter using a temp file) - can anyone think of a way to do this _without_ using a temp file? I tried: $buf = `echo '$buf' | $filter`; but the shell clears bit 7 of all characters on the command line - which corrupts compressed files, so if $buf is the first part of a compressed file and $filter is "uncompress" this will not work. Suggestions anyone? Martin. My ARPANET address is: martin%EASBY.DUR.AC.UK@CUNYVM.CUNY.EDU OR: martin%uk.ac.dur.easby@nfsnet-relay.ac.uk UUCP:...!mcvax!ukc!easby!martin JANET: martin@uk.ac.dur.easby BITNET: IN%"MARTIN@EASBY.DURHAM.AC.UK" sh-prompt$ echo 'xbtoa Begin +.\L&b+)&`#)%tg+O%KDUVOp,/660"E3b-#&f<0Q$q\:r8rj`3BFr!.@L<\]<`";l@:D_7,:HqR.A6 `pJhh5b-ia5I xbtoa End N 70 46 E de S 1abc R fd3ff229' | viewer | cat #! /user/csm/gnu/bin/perl # viewer [file ...] OR viewer < file # Generic file viewer - convert compressed/qed/uuencoded etc files to ascii # and view using less. Use pipes to get the first page of a large file # to appear quickly. $temp = "/tmp/v.tmp.$$"; # temp file for buffer $header = ""; # Header for filtered files $dash = "=" x 20; # 20 dashes $nulls = "\000" x 80; # 80 nulls $ls = "ls -alFL"; # The command to use for viewing a directory. $* = 1; # Allow multi-line matches in strings $SIG'QUIT' = 'cleanup'; # catch quit and interrupt signals $SIG'INT' = 'cleanup'; # and delete the $temp file. $ARGV[0] = "-" if $#ARGV < 0; # Set first arg to - (stdin) if no args unshift(@ARGV, '-') if $#ARGV < $[; # Set first arg to - if no files while ($ARGV = shift) $file = $ARGV; ($shortfile = $file) =^ s|.*/||; $header = ""; $lessargs = ""; if (($file ne "-") && (-d $file)) # directory - treat as special case if (-t STDOUT) # output is to a tty system "(echo '$file:' ; $ls $file) | less"; else # output is to a program system "$ls $file"; next; # do next file in ARGV. open(file, $file) || die "Can't open $file\n"; read(file, $buf, 1024); # Get first 1024 (or so) chars. $save = $buf; # save in case reading from stdin. $#filters = -1; while (1 == 1) # look for file type: ($c1,$c2,$c3,$c4) = split(//,$buf); # get first 4 chars of $buf $magic = ord($c1) * 256 + ord($c2); # Get magic number. $long = $magic * 65536 + ord($c3) * 256 + ord($c4); # get 4 byte long. if ($buf =^ /xbtoa Begin/) # btoa'd file do addfilter("atobf"); do filterbuf("atobf"); # Pass the buffer through the filter next; if ($buf =^ /begin /) # uuencoded file do addfilter("uudecodef"); do filterbuf("uudecodef"); next; if ($magic == 0x1f9d) # Compressed file do addfilter("uncompress"); do filterbuf("uncompress"); next; if ($long == 0x7F000400) # QED file do addfilter("qtoa"); do filterbuf("qtoa"); next; if (($buf =^ /.so /) || ($buf =^ /.TH /)) # nroff man file $lessargs .= " -s"; do addfilter("nroff -man"); last; # Result of "nroff -man" filter is always ascii. # tar files seem to have chars 16-96 all null and $magic != 0 if (substr($buf,16,80) eq $nulls && $magic != 0) do addfilter("tar tf -"); last; # Result of "tar tf -" filter is always ascii. if (($magic == 0x8002) || ($magic == 2) # mc68020 executable || ($magic == 0x080456) # mc68020 core file || ($magic == 0x0103) # RT executable || ($magic == 023000)) # RT core dump do addfilter("od -s4", # search for strings of length >= 4 chars ($magic == 0x8002) ? "mc68020 dynamically linked executable" : ($magic == 2) ? "mc68020 executable" : ($magic == 0x080456) ? "mc68020 core file" : ($magic == 0x0103) ? "RT executable" : ($magic == 023000) ? "RT core dump" : die); last; # Result of "od -s4" is always ascii last; # All tests failed - @filters is complete. if ($file eq "-") # Check if reading fronm stdin. if ($#filters == -1) # Check if any filters. if (-t STDOUT) open(OUT, "| less"); # no filters and stdin. else open(OUT, ">-"); # send output to stdout. else if (-t STDOUT) # Don't write header if output not a terminal open(OUT, "| (echo '$header' ; " . join(" | ",@filters) . ") 2>/dev/null | less $lessargs"); else # output is a pipe - no header or "|less" open(OUT, "| " . join(" | ",@filters)); print OUT $save; # print saved buffer if (length($save) == 1024) # If stuff left on pipe while (read(file, $buf, 1024)) # then print in 1k chunks print OUT $buf; close OUT; else # We have a real file if ($#filters == -1) # check if any filters needed system "less $file"; # no filters and a real file else $file = "< $file"; # add "<" to filename if ($#filters == 0) # one filter if (-t STDOUT) # Don't write header if output not a terminal system "(echo '$header' ; $filters[0] $file)" . " 2>/dev/null | less $lessargs"; else system "$filters[0] $file"; else # Lots of filters if (-t STDOUT) # Don't write header if output not a terminal system ("(echo '$header' ; $filters[0] $file | " . join(" | ",@filters[1..$#filters]) . ") 2>/dev/null | less $lessargs"); else system ("$filters[0] $file | " . join(" | ",@filters[1..$#filters])); sub addfilter # Add filter to list and header local ($filter,$title) = @_; # If no $title, use $FILTER push(@filters, $filter); ($title = $filter) =^ tr/a-z/A-Z/ if ($title eq ""); if ($file eq "-") $header .= "$dash $title $dash\n"; else $header .= "$dash $shortfile: $title $dash\n"; sub filterbuf # Pass $buf through the given filter. local ($filter) = @_; open(OUTPUT, "| $filter >$temp 2>/dev/null"); # pipe to filter: ignore errors print OUTPUT $buf; close OUTPUT; # wait for $filter to finish open(INPUT, "$temp"); read(INPUT, $buf, 1024); # read up to 1024 filtered chars close INPUT; unlink $temp; sub cleanup # 1st argument is signal name local($sig) = @_; print "Caught a SIG$sig--shutting down\n"; unlink $temp; exit(0);