X-Git-Url: https://mattmccutchen.net/utils/utils.git/blobdiff_plain/40ea9b7868f2b7746e7cbabfba6aba982096392a..273c390351c42303171c25215304d1cfd6ca02d4:/lesspipe.sh diff --git a/lesspipe.sh b/lesspipe.sh new file mode 100755 index 0000000..372abb5 --- /dev/null +++ b/lesspipe.sh @@ -0,0 +1,196 @@ +#!/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