update
[shellsnippets/shellsnippets.git] / mksh / wtf
1 #!/bin/mksh
2 myver='$MirOS: src/usr.bin/wtf/wtf,v 1.36 2017/06/15 21:00:45 tg Exp $'
3 # $NetBSD: wtf,v 1.7 2000/11/21 00:18:52 soren Exp $
4 #-
5 # Copyright © 2002, 2003, 2004, 2006, 2007, 2008, 2010, 2011,
6 #             2012, 2014, 2015, 2017
7 #       mirabilos <m@mirbsd.org>
8 #
9 # Provided that these terms and disclaimer and all copyright notices
10 # are retained or reproduced in an accompanying document, permission
11 # is granted to deal in this work without restriction, including un‐
12 # limited rights to use, publicly perform, distribute, sell, modify,
13 # merge, give away, or sublicence.
14 #
15 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
16 # the utmost extent permitted by applicable law, neither express nor
17 # implied; without malicious intent or gross negligence. In no event
18 # may a licensor, author or contributor be held liable for indirect,
19 # direct, other damage, loss, or other issues arising in any way out
20 # of dealing in the work, even if advised of the possibility of such
21 # damage or existence of a defect, except proven that it results out
22 # of said person’s immediate fault when using the work as intended.
23
24 acronyms=${ACRONYMDB:-/usr/share/misc/acronyms}
25
26 usage() {
27         print -u2 "usage: ${0##*/} [±adPpw] [-f dbfile] [is[t]] <acronym> [...]"
28         exit 1
29 }
30
31 use_acronyms=-1
32 use_dict=-1
33 use_ports=-1
34 use_whatis=-1
35 hasopt=0
36 show_ver=0
37 while getopts "adf:hPpt:Vw" ch; do
38         case $ch {
39         (+a)    hasopt=1 use_acronyms=0 ;;
40         (a)     hasopt=1 use_acronyms=1 ;;
41         (+d)    hasopt=1 use_dict=0 ;;
42         (d)     hasopt=1 use_dict=1 ;;
43         (f)     acronyms=$OPTARG ;;
44         (+P)    hasopt=1 use_ports=0 ;;
45         (P)     hasopt=1 use_ports=2 ;;
46         (+p)    hasopt=1 use_ports=0 ;;
47         (p)     hasopt=1 use_ports=1 ;;
48         (t)     ;;
49         (V)     show_ver=1 ;;
50         (+w)    hasopt=1 use_whatis=0 ;;
51         (w)     hasopt=1 use_whatis=1 ;;
52         (*)     usage ;;
53         }
54 done
55 shift $((OPTIND - 1))
56
57 if (( hasopt )); then
58         (( use_acronyms = (use_acronyms == -1) ? 0 : use_acronyms ))
59         (( use_dict = (use_dict == -1) ? 0 : use_dict ))
60         (( use_ports = (use_ports == -1) ? 0 : use_ports ))
61         (( use_whatis = (use_whatis == -1) ? 0 : use_whatis ))
62 else
63         use_acronyms=1
64         use_dict=0
65         use_ports=0
66         use_whatis=0
67 fi
68
69 if (( show_ver )); then
70         print -ru2 -- "$myver"
71         if (( use_acronyms )); then
72                 exec <"$acronyms"
73                 if ! IFS= read -r line || [[ $line != '  '* ]] || \
74                     ! IFS= read -r line || [[ $line != ' @(#)'* ]]; then
75                         print -ru2 "E: acronyms database ${acronyms@Q} too old"
76                         exit 1
77                 fi
78                 print -ru2 -- "${line# ????}"
79                 print -nu2 'Counting, please be patient…'
80                 last= nacr=0 nexp=0 lots=${EPOCHREALTIME%?????}
81                 while IFS= read -r line; do
82                         [[ $line = *'   '* ]] || continue
83                         let ++nexp
84                         line=${line%%   *}
85                         [[ $line = "$last" ]] || let ++nacr
86                         last=$line
87                         [[ $lots = ${EPOCHREALTIME%?????} ]] && continue
88                         print -nu2 \\rwtf knows at least $nacr acronyms with $nexp expansions
89                         lots=${EPOCHREALTIME%?????}
90                 done
91                 print -u2 \\rwtf currently knows about $nacr acronyms with $nexp expansions
92         fi
93         exit 0
94 fi
95
96 (( $# > 1 )) && [[ $1 = is || $1 = ist ]] && shift
97 (( $# < 1 )) && usage
98
99 if (( use_ports )); then
100         if [[ -s /usr/ports/INDEX ]]; then
101                 # MirPorts Framework, OpenBSD ports tree
102                 binpkgs=ports
103                 function ports_acquire_filtered {
104                         local a b c d e
105                         local -l x y=$1
106
107                         while IFS='|' read a b c d e; do
108                                 x=$a
109                                 [[ $x = *"$y"* ]] && \
110                                     print -r -- "$a|${d%% \(uses*}"
111                         done </usr/ports/INDEX
112                 }
113                 function ports_acquire_unfiltered {
114                         local a b c d e
115                         local -l x y=$1
116
117                         while IFS='|' read a b c d e; do
118                                 x=$a$d
119                                 [[ $x = *"$y"* ]] && \
120                                     print -r -- "$a|${d%% \(uses*}"
121                         done </usr/ports/INDEX
122                 }
123         elif [[ -x $(whence -p yum) ]]; then
124                 # Red Hat Yellowdog Updater Modified
125                 binpkgs=RPMs
126                 function ports_acquire_filtered {
127                         local -l x y=$1
128
129                         yum search -q -- "$1" | \
130                             tr '\n' '\ 1' | sed 's/\ 1 *: / /g' | tr '\ 1' '\n' | \
131                             while read a b c; do
132                                 x=$a
133                                 [[ $x = *"$y"* ]] && print -r -- "$a|$c"
134                         done
135                 }
136                 function ports_acquire_unfiltered {
137                         yum search -q -- "$1" | \
138                             tr '\n' '\ 1' | sed 's/\ 1 *: / /g' | tr '\ 1' '\n' | \
139                             while read a b c; do
140                                 print -r -- "$a|$c"
141                         done
142                 }
143         elif [[ -x $(whence -p apt-cache) ]]; then
144                 # Debian Advanced Packaging Tool
145                 binpkgs=packages
146                 function ports_acquire_filtered {
147                         local -l x y=$1
148
149                         apt-cache search -- "$1" | while read a b c; do
150                                 x=$a
151                                 [[ $x = *"$y"* ]] && print -r -- "$a|$c"
152                         done
153                 }
154                 function ports_acquire_unfiltered {
155                         apt-cache search -- "$1" | while read a b c; do
156                                 print -r -- "$a|$c"
157                         done
158                 }
159         else
160                 use_ports=0
161         fi
162         case $use_ports {
163         (1)     alias ports_acquire=ports_acquire_filtered ;;
164         (2)     alias ports_acquire=ports_acquire_unfiltered ;;
165         (*)     use_ports=0 ;;
166         }
167 fi
168
169 if (( use_acronyms )); then
170         # read input file only once
171         exec <"$acronyms"
172
173         # read case-folding code
174         if ! IFS= read -r line || [[ $line != '  '* ]]; then
175                 print -ru2 "E: acronyms database ${acronyms@Q} too old"
176                 exit 1
177         fi
178         set -A ucsrch -- $line
179
180         # create sorted input array, uppercased/folded
181         s='set -sA stsrch --'
182         i=0
183         # now: "$@"=("$0" foo bar baz)
184         for target in "$@"; do
185                 typeset -u tgsrch=$target
186                 [[ $tgsrch = *[A-Z].* ]] && tgsrch=${tgsrch//.}
187                 for p in "${ucsrch[@]}"; do
188                         eval 'tgsrch=${tgsrch//'"$p}"
189                 done
190                 s+=" ${tgsrch@Q}=$((++i))"
191         done
192         eval "$s"
193         # now: stsrch=(BAR=2 BAZ=3 FOO=1)
194
195         # create output mapping, remove mapping number from stsrch
196         set -A omsrch
197         tgsrch=
198         i=0 n=-1
199         for s in "${stsrch[@]}"; do
200                 p=${s%=*}
201                 if [[ $p = $tgsrch ]]; then
202                         # this is a repeat
203                         unset stsrch[i++]
204                 else
205                         stsrch[i++]=$p
206                         tgsrch=$p
207                         let ++n
208                 fi
209                 (( omsrch[${s##*=}] = n ))
210         done
211         set -A stsrch -- "${stsrch[@]}"
212         # now: stsrch=(BAR BAZ FOO) omsrch[1]=2 omsrch[2]=0 omsrch[3]=1
213
214         # look up acronyms
215         set -A acrout
216         i=-1
217         for s in "${stsrch[@]}"; do
218                 let ++i
219                 while :; do
220                         if [[ $line = "$s       "* ]]; then
221                                 acrout[i]+=$'\n'${line#*        }
222                         elif [[ $line > "$s     " ]]; then
223                                 continue 2
224                         fi
225                         IFS= read -r line || break 2
226                 done
227         done
228
229         exec <&-
230         i=0
231 fi
232
233 rv=0
234 for target in "$@"; do
235         if (( use_ports )); then
236                 p=$(ports_acquire "$target")
237                 if [[ -n $p ]]; then
238                         print -r "  - $binpkgs matching “$target”:"
239                         print -r -- "$p" | sort -u | column -ts'|'
240                 fi
241         fi
242
243         if (( use_acronyms )); then
244                 n=${omsrch[++i]}
245                 s=${acrout[n]}
246                 tgsrch=${stsrch[n]}
247                 if [[ -n $s ]]; then
248                         print -r -- "   $tgsrch:$s"
249                 else
250                         print -ru2 Gee… I don’t know what “"$tgsrch"” means…
251                         (( rv |= 1 ))
252                 fi
253         fi
254
255         (( use_dict || use_whatis )) && print "  - other information sources"
256
257         (( use_dict )) && if whence -p dict >/dev/null; then
258                 dict "$target" || (( rv |= 2 ))
259         else
260                 (( rv |= 4 ))
261         fi
262
263         if (( use_whatis )); then
264                 w=$(whatis "$target") || \
265                     w="Sorry, no entry for a “$target” manual page"
266                 print -r -- "$w"
267         fi
268 done
269 exit $rv