#!/bin/bash # Matt's improved version of Fedora's lesspipe.sh # # To use this filter with less, define LESSOPEN: # export LESSOPEN="|lesspipe.sh %s" # Updated for "less" that checks our exit code ~ Matt 2010-12-28 # This script is entirely driven by the file content, with type detected by # file(1). For reproducibility and to avoid problems with strange names and # even symlinks, we do not use the original name of the file for anything. set -e set -o pipefail #set -x # Attach the file to an FD. We will reopen /dev/fd/$f to get as many fresh FDs as # we need. exec {f}<"$1" shift function run_stream_stdin { if [ "$decompressor" == cat ]; then <&$f "$@" else <&$f "$decompressor" | "$@" fi } function stage_to_file_stdin { if [ "$decompressor" != cat ]; then # There is an unavoidable race here, so I am not trying to avoid # it. If we rewrite the script in Perl, we can use open(undef). # Integration with in2tempfile looks too hard. tmpfile="$(mktemp --tmpdir lesspipe.XXXXXXXXXX)" exec {tf}>"$tmpfile" rm -f "$tmpfile" <&$f "$decompressor" >&$tf # Synchronous # Get a new FD for reading on the tempfile. This might not be # necessary if the command is just going to reopen it. exec {f}>&- &- else exec <&$f- fi } # Note: the bash file tests (except -h) follow symlinks. if [ -f /dev/fd/$f ]; then # Specify the input as -. This avoids irrelevant "set[ug]id" output for # executables and also follows the symlink without us having to pass -L. # We have to reopen stdin to not consume it. content_type="$(/proc/$PPID/fd/1 #if [ -t $pt ]; then # # XXX Does this really use stderr rather than the controlling terminal? # export COLUMNS="$(bash -i -c 'echo $COLUMNS' 2>&$pt)" #fi #exec {pt}>&- # viapty is a more comprehensive solution than the above. ~ 2011-09-05 # Hm, by the time we hack in PAGER=cat, maybe it's not any better... ~ 2011-09-06 run_stream_stdin viapty man -l -;; (*'tar archive'*) run_stream_stdin tar -tvv;; (*'cpio archive'*) run_stream_stdin cpio --quiet -itv;; (RPM*) run_stream_stdin rpm -qivl --changelog -p -;; (*image*) # FIXME: will catch disk "images" as well as graphical images if type identify &>/dev/null; then identify_cmd=(identify) elif type gm &>/dev/null; then identify_cmd=(gm identify) else ### Give them the binary data. #echo "No identify available" #echo "Install ImageMagick or GraphicsMagick to browse images" identify_cmd=(false) fi if [ "${identify_cmd[0]}" != false ]; then # Make a file so identify won't stage it again with a # nonreproducible name. (Note that "identify -" will # stage again even if /dev/stdin is a file!) stage_to_file_stdin "${identify_cmd[@]}" /dev/stdin fi;; ('OpenDocument Text'|_,'OpenDocument Text Template') stage_to_file_stdin ### CUSTOM SCRIPT -- This part cannot be upstreamed unless odt2text is. odt2text /dev/stdin # based on unzip, needs a file ;; # This has to come after the special case for odt2text. ('Zip archive data'*|_,'OpenDocument'*|_,'OpenOffice 1.x'*) stage_to_file_stdin zipinfo /dev/stdin # needs a file ;; ('PDF document'*) run_stream_stdin pdftotext - -;; ('PostScript document'*) # If you wanted to see the PostScript source code, sorry... run_stream_stdin ps2ascii;; (ELF*'dynamically linked'*) stage_to_file_stdin # read twice objdump -xRTdgl /dev/stdin # The above does not seem to show data sections. Run a separate command # to show them. (Of course I would prefer a single command.) objdump -s -j .rodata -j .data /dev/stdin ;; (ELF*) stage_to_file_stdin # read twice objdump -xdgl /dev/stdin objdump -s -j .rodata -j .data /dev/stdin ;; ('SQLite 3.x database'*) # TODO: Reimplement copy to a temporary directory to # handle databases that use write-ahead mode, etc. I had # implemented this in main VM and I forgot to copy it to # main-template before shutting down. :( ~ Noted 2015-12-28 stage_to_file_stdin # I believe that this will honor a write lock held on an # uncompressed database by another instance of SQLite. I # further believe that is what we want. sqlite3 /dev/stdin .dump # needs a file ;; ('compiled Java class data'*) # Agghhh... why couldn't javap just take a filename? It's clear # that all it is doing is translating our argument to a file and # opening that, without checking the correctness of the package # or class name. # XXX: Proper cleanup on failure dir="$(mktemp -d --tmpdir lesspipe-javap.XXXXXXXXXX)" ln -s /dev/stdin "$dir/Stdin.class" run_stream_stdin javap -classpath "$dir" -private -s -c -verbose Stdin rm -rf "$dir" ;; # Disabled for now. #(*.grl) # stage_to_file_stdin # head -n 1 /dev/stdin | fold -w 28 # tail -n +2;; (*) # Unknown content type. if [ "$decompressor" != cat ]; then # Just decompress the file. <&$f "$decompressor" else # We cannot add value in this case. # Let "less" show the original file. exit 77 fi;; esac elif [ -d /dev/fd/$f ]; then # nip blank line at the end # XXX: how to make sure "less" has -R enabled? #ls -H -al --color=always -- /dev/fd/$f/ | head --bytes=-3 ### Between coreutils-7.6-9.matt1.fc12 and coreutils-8.5-7.fc14, the ### output has changed to put the \n at the end, so the special hack is ### no longer necessary (less ignores a final \n) and in fact corrupts ### the colors. ~ Matt 2011-06-19 ls -H -al --color=always -- /dev/fd/$f/ else # Device/special file. Don't print anything; let less complain about it. # (For now, we are not interested in content sniffing on pipes. If less # gained support for calling LESSOPEN for stdin, we might reconsider.) exit 77 fi