Commit | Line | Data |
---|---|---|
273c3903 MM |
1 | #!/bin/bash |
2 | # Matt's improved version of Fedora's lesspipe.sh | |
3 | # | |
4 | # To use this filter with less, define LESSOPEN: | |
5 | # export LESSOPEN="|lesspipe.sh %s" | |
6 | ||
7 | # Updated for "less" that checks our exit code ~ Matt 2010-12-28 | |
8 | ||
9 | # This script is entirely driven by the file content, with type detected by | |
10 | # file(1). For reproducibility and to avoid problems with strange names and | |
11 | # even symlinks, we do not use the original name of the file for anything. | |
12 | ||
13 | set -e | |
14 | set -o pipefail | |
15 | #set -x | |
16 | ||
17 | # Attach the file to an FD. We will reopen /dev/fd/$f to get as many fresh FDs as | |
18 | # we need. | |
19 | exec {f}<"$1" | |
20 | shift | |
21 | ||
22 | function run_stream_stdin { | |
23 | if [ "$decompressor" == cat ]; then | |
24 | <&$f "$@" | |
25 | else | |
26 | <&$f "$decompressor" | "$@" | |
27 | fi | |
28 | } | |
29 | ||
30 | function stage_to_file_stdin { | |
31 | if [ "$decompressor" != cat ]; then | |
32 | # There is an unavoidable race here, so I am not trying to avoid | |
33 | # it. If we rewrite the script in Perl, we can use open(undef). | |
34 | # Integration with in2tempfile looks too hard. | |
35 | tmpfile="$(mktemp --tmpdir lesspipe.XXXXXXXXXX)" | |
36 | exec {tf}>"$tmpfile" | |
37 | rm -f "$tmpfile" | |
38 | <&$f "$decompressor" >&$tf # Synchronous | |
39 | # Get a new FD for reading on the tempfile. This might not be | |
40 | # necessary if the command is just going to reopen it. | |
41 | exec {f}>&- </dev/fd/$tf {tf}>&- | |
42 | else | |
43 | exec <&$f- | |
44 | fi | |
45 | } | |
46 | ||
47 | # Note: the bash file tests (except -h) follow symlinks. | |
48 | if [ -f /dev/fd/$f ]; then | |
49 | # Specify the input as -. This avoids irrelevant "set[ug]id" output for | |
50 | # executables and also follows the symlink without us having to pass -L. | |
51 | # We have to reopen stdin to not consume it. | |
52 | content_type="$(</dev/fd/$f file -b -)" | |
53 | ||
54 | case "$content_type" in | |
55 | ('gzip compressed data'*) | |
56 | decompressor=gunzip;; | |
57 | ('bzip2 compressed data'*) | |
58 | decompressor=bunzip2;; | |
59 | ('XZ compressed data'*) | |
60 | decompressor=unxz;; | |
61 | (*) | |
62 | decompressor=cat;; | |
63 | esac | |
64 | ||
65 | if [ "$decompressor" != cat ]; then | |
66 | # Get the real content type. Whatever work the decompressor | |
67 | # does before it is killed with SIGPIPE, we will duplicate later | |
68 | # when we read the file, but there is no good alternative: | |
69 | # - If we do a "tee" with one pipe held for later use, "tee" | |
70 | # will block once it fills up that pipe. So if "file" needs | |
71 | # more data than the pipe buffer to make its decision | |
72 | # (probably unlikely), we could hang. | |
73 | # - Going ahead and dumping the decompressed data to a temporary | |
74 | # file is a waste of work if the content type turns out to be | |
75 | # one for which we have a streaming viewer, but of course we | |
76 | # don't know that yet. | |
77 | content_type="$(</dev/fd/$f "$decompressor" | file -b -)" || [ $? == 141 ] # SIGPIPE | |
78 | fi | |
79 | ||
80 | case "$content_type" in | |
81 | ('troff or preprocessor input'*) | |
82 | ## Presuming our parent is "less", set COLUMNS based on the width | |
83 | ## of the terminal. | |
84 | #exec {pt}>/proc/$PPID/fd/1 | |
85 | #if [ -t $pt ]; then | |
86 | # # XXX Does this really use stderr rather than the controlling terminal? | |
87 | # export COLUMNS="$(bash -i -c 'echo $COLUMNS' 2>&$pt)" | |
88 | #fi | |
89 | #exec {pt}>&- | |
90 | # viapty is a more comprehensive solution than the above. ~ 2011-09-05 | |
91 | # Hm, by the time we hack in PAGER=cat, maybe it's not any better... ~ 2011-09-06 | |
92 | run_stream_stdin viapty man -l -;; | |
93 | (*'tar archive'*) | |
94 | run_stream_stdin tar -tvv;; | |
95 | (*'cpio archive'*) | |
96 | run_stream_stdin cpio --quiet -itv;; | |
97 | (RPM*) | |
98 | run_stream_stdin rpm -qivl --changelog -p -;; | |
99 | (*image*) # FIXME: will catch disk "images" as well as graphical images | |
100 | if type identify &>/dev/null; then | |
101 | identify_cmd=(identify) | |
102 | elif type gm &>/dev/null; then | |
103 | identify_cmd=(gm identify) | |
104 | else | |
105 | ### Give them the binary data. | |
106 | #echo "No identify available" | |
107 | #echo "Install ImageMagick or GraphicsMagick to browse images" | |
108 | identify_cmd=(false) | |
109 | fi | |
110 | if [ "${identify_cmd[0]}" != false ]; then | |
111 | # Make a file so identify won't stage it again with a | |
112 | # nonreproducible name. (Note that "identify -" will | |
113 | # stage again even if /dev/stdin is a file!) | |
114 | stage_to_file_stdin | |
115 | "${identify_cmd[@]}" /dev/stdin | |
116 | fi;; | |
117 | ('OpenDocument Text'|_,'OpenDocument Text Template') | |
118 | stage_to_file_stdin | |
119 | ### CUSTOM SCRIPT -- This part cannot be upstreamed unless odt2text is. | |
120 | odt2text /dev/stdin # based on unzip, needs a file | |
121 | ;; | |
122 | # This has to come after the special case for odt2text. | |
123 | ('Zip archive data'*|_,'OpenDocument'*|_,'OpenOffice 1.x'*) | |
124 | stage_to_file_stdin | |
125 | zipinfo /dev/stdin # needs a file | |
126 | ;; | |
127 | ('PDF document'*) | |
128 | run_stream_stdin pdftotext - -;; | |
129 | ('PostScript document'*) | |
130 | # If you wanted to see the PostScript source code, sorry... | |
131 | run_stream_stdin ps2ascii;; | |
132 | (ELF*'dynamically linked'*) | |
133 | stage_to_file_stdin # read twice | |
134 | objdump -xRTdgl /dev/stdin | |
135 | # The above does not seem to show data sections. Run a separate command | |
136 | # to show them. (Of course I would prefer a single command.) | |
137 | objdump -s -j .rodata -j .data /dev/stdin | |
138 | ;; | |
139 | (ELF*) | |
140 | stage_to_file_stdin # read twice | |
141 | objdump -xdgl /dev/stdin | |
142 | objdump -s -j .rodata -j .data /dev/stdin | |
143 | ;; | |
144 | ('SQLite 3.x database'*) | |
145 | # TODO: Reimplement copy to a temporary directory to | |
146 | # handle databases that use write-ahead mode, etc. I had | |
147 | # implemented this in main VM and I forgot to copy it to | |
148 | # main-template before shutting down. :( ~ Noted 2015-12-28 | |
149 | stage_to_file_stdin | |
150 | # I believe that this will honor a write lock held on an | |
151 | # uncompressed database by another instance of SQLite. I | |
152 | # further believe that is what we want. | |
153 | sqlite3 /dev/stdin .dump # needs a file | |
154 | ;; | |
155 | ('compiled Java class data'*) | |
156 | # Agghhh... why couldn't javap just take a filename? It's clear | |
157 | # that all it is doing is translating our argument to a file and | |
158 | # opening that, without checking the correctness of the package | |
159 | # or class name. | |
160 | # XXX: Proper cleanup on failure | |
161 | dir="$(mktemp -d --tmpdir lesspipe-javap.XXXXXXXXXX)" | |
162 | ln -s /dev/stdin "$dir/Stdin.class" | |
163 | run_stream_stdin javap -classpath "$dir" -private -s -c -verbose Stdin | |
164 | rm -rf "$dir" | |
165 | ;; | |
166 | # Disabled for now. | |
167 | #(*.grl) | |
168 | # stage_to_file_stdin | |
169 | # head -n 1 /dev/stdin | fold -w 28 | |
170 | # tail -n +2;; | |
171 | (*) # Unknown content type. | |
172 | if [ "$decompressor" != cat ]; then | |
173 | # Just decompress the file. | |
174 | <&$f "$decompressor" | |
175 | else | |
176 | # We cannot add value in this case. | |
177 | # Let "less" show the original file. | |
178 | exit 77 | |
179 | fi;; | |
180 | esac | |
181 | ||
182 | elif [ -d /dev/fd/$f ]; then | |
183 | # nip blank line at the end | |
184 | # XXX: how to make sure "less" has -R enabled? | |
185 | #ls -H -al --color=always -- /dev/fd/$f/ | head --bytes=-3 | |
186 | ### Between coreutils-7.6-9.matt1.fc12 and coreutils-8.5-7.fc14, the | |
187 | ### output has changed to put the \n at the end, so the special hack is | |
188 | ### no longer necessary (less ignores a final \n) and in fact corrupts | |
189 | ### the colors. ~ Matt 2011-06-19 | |
190 | ls -H -al --color=always -- /dev/fd/$f/ | |
191 | else | |
192 | # Device/special file. Don't print anything; let less complain about it. | |
193 | # (For now, we are not interested in content sniffing on pipes. If less | |
194 | # gained support for calling LESSOPEN for stdin, we might reconsider.) | |
195 | exit 77 | |
196 | fi |