Skip to content

Commit bc13afe

Browse files
achadwickSmartFinn
authored andcommitted
Add a script to find missing mimetypes icons.
1 parent d71a525 commit bc13afe

File tree

1 file changed

+313
-0
lines changed

1 file changed

+313
-0
lines changed

tools/missing_mimetypes.sh

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
#!/usr/bin/env bash
2+
#: Use this computer's shared-mime-info database to find missing MIME type
3+
#: icons in Papirus. This script can also be used to discover which icon
4+
#: would be used for a given MIME type, and why.
5+
#:
6+
#: usage: __SCRIPT__ [OPTIONS]
7+
8+
set -euo pipefail
9+
10+
SRCDIR=$(realpath "../Papirus")
11+
AVAILABLE_ICONS_CACHE="x-srcdir-icons"
12+
MIME_DIRS_CACHE="x-mimedirs"
13+
MIME_TYPES_CACHE="types"
14+
MIME_ICONS_CACHE="icons"
15+
MIME_GENERIC_ICONS_CACHE="generic-icons"
16+
17+
# Command line option vars and default behaviour
18+
VERBOSITY=1
19+
SHOW_HEADER=1
20+
SCAN_PERSONAL_MIMEDIR=0
21+
22+
23+
show_usage () {
24+
script=$(basename "$0")
25+
sed -Ee 's/^\s*#:\s?//p;d' "$0" | sed -Ee 's/__SCRIPT__/'"$script"'/' >&2
26+
}
27+
28+
29+
# Get all MIME dirs on the system.
30+
31+
get_mime_dirs () {
32+
local data_dirs=${XDG_DATA_DIRS:-/usr/local/share/:/usr/share/}
33+
if [[ $SCAN_PERSONAL_MIMEDIR -gt 0 ]]; then
34+
data_dirs="${XDG_DATA_HOME:-$HOME/.local/share}:${data_dirs}"
35+
fi
36+
local IFS=:
37+
for d in $data_dirs; do
38+
local m="$d/mime"
39+
[ -d "$m" ] || continue
40+
printf "%s\n" "$m"
41+
done
42+
}
43+
44+
45+
# Gathers a list of all available icon names in $SRCDIR.
46+
# This is not limited to just icons in the mimetypes subfolders,
47+
# because explicit <icon/> and <generic-icon/> dirctives
48+
# may name icons in any context.
49+
50+
get_available_icons () {
51+
find "$SRCDIR/"*'x'* \( -type f -o -type l \) -name '*.svg' \
52+
| sed -e 's!.*/!!;s![.]svg$!!' \
53+
| sort | uniq
54+
}
55+
56+
57+
58+
# Collects a line-oriented cache file from all shared-mime-info MIME dirs.
59+
# Expects to be run inside the working tempdir.
60+
61+
get_mimedirs_cache () {
62+
local cache="$1"
63+
while read -r d; do
64+
local c="$d/$cache"
65+
[ -f "$c" ] || continue
66+
cat "$c"
67+
done < "$MIME_DIRS_CACHE" | sort | uniq
68+
}
69+
70+
71+
72+
# Display a header for the lines check_mimetype outputs.
73+
74+
show_header () {
75+
((SHOW_HEADER)) || return 0
76+
cat <<"__END_HEADER__"
77+
┌─────── E=Exact icon found, as named in the mimetype’s <icon/> directive.
78+
│┌────── e=Exact icon found using the default “replace / with -” strategy.
79+
││┌───── G=Generic icon found, as named in the mimetype’s <generic-icon/>.
80+
│││┌──── g=Generic icon found using the default “<type>/x-generic” strategy.
81+
││││ ┌── Match quality (filtered by --output-level).
82+
││││ │ ┌ MIME type + any named matches from E and G.
83+
││││ │ │
84+
__END_HEADER__
85+
}
86+
87+
88+
# Check one MIME type has a valid icon in the theme.
89+
# Expects to be run inside the working tempdir.
90+
91+
check_mimetype () {
92+
local mimetype="$1"
93+
local mimetype_re
94+
mimetype_re=$(sed -Ee 's!(\W)!\\\1!g' <<< "$mimetype")
95+
96+
local icon
97+
local match_quality
98+
local named_matches=""
99+
local flag_char
100+
local flags_str=""
101+
local color
102+
103+
# First possibility is that it has an explicit icon override
104+
# defined in MIMEDIRS/icons. This falls through to the tests below
105+
# if there's no icon, or if there's no override defined.
106+
flag_char="-"
107+
icon=$(sed -Ee "s#^${mimetype_re}:##g; tL; d; :L q" "$MIME_ICONS_CACHE")
108+
if [ -n "$icon" ]; then
109+
if grep -q -x -F "$icon" -- "$AVAILABLE_ICONS_CACHE"; then
110+
match_quality=${match_quality:-4} # best
111+
[[ VERBOSITY -lt match_quality ]] && return 0
112+
flag_char="E"
113+
color=${color:-2} # green
114+
named_matches="${named_matches}, ${icon}"
115+
fi
116+
fi
117+
flags_str="${flags_str}${flag_char}"
118+
119+
# Second possibility is that we can form an icon name by
120+
# substituting "-" for "/" in the mimetype.
121+
flag_char="-"
122+
icon=$(sed -Ee 's#/#-#g' <<< "$mimetype")
123+
if grep -q -x -F "$icon" -- "$AVAILABLE_ICONS_CACHE"; then
124+
match_quality=${match_quality:-3} # good
125+
[[ VERBOSITY -lt match_quality ]] && return 0
126+
flag_char="e"
127+
color=${color:-2} # green
128+
fi
129+
flags_str="${flags_str}${flag_char}"
130+
131+
# Third chance for the mimetype is that there's a generic icon defined as
132+
# a fallback in MIMEDIRS/generic-icons. The syntax of these files is the
133+
# same as MIMEDIRS/icons
134+
flag_char="-"
135+
icon=$(sed -Ee "s#^${mimetype_re}:##g; tL; d; :L q" \
136+
"$MIME_GENERIC_ICONS_CACHE")
137+
if [ -n "$icon" ]; then
138+
if grep -q -x -F "$icon" -- "$AVAILABLE_ICONS_CACHE"; then
139+
match_quality=${match_quality:-2} # reasonable
140+
[[ VERBOSITY -lt match_quality ]] && return 0
141+
flag_char="G"
142+
color=${color:-3} # yellow
143+
named_matches="${named_matches}, ${icon}"
144+
fi
145+
fi
146+
flags_str="${flags_str}${flag_char}"
147+
148+
# Final chance!
149+
# Try to form an icon name as if the subtype was "x-generic" instead.
150+
flag_char="-"
151+
icon=$(sed -Ee 's#/.*#-x-generic#g' <<< "$mimetype")
152+
if grep -q -x -F "$icon" -- "$AVAILABLE_ICONS_CACHE"; then
153+
match_quality=${match_quality:-1} # poor, but sometimes acceptable
154+
[[ VERBOSITY -lt match_quality ]] && return 0
155+
flag_char="g"
156+
color=${color:-1} # red
157+
fi
158+
flags_str="${flags_str}${flag_char}"
159+
160+
# Format the summary line.
161+
color=${color:-1} # red
162+
match_quality=${match_quality:-0} # worst
163+
if [[ VERBOSITY -ge match_quality ]]; then
164+
local style_on=""
165+
local style_off=""
166+
if [[ -t 1 ]]; then
167+
printf -v style_on '\033[3%dm' "$color"
168+
printf -v style_off '\033[0m'
169+
fi
170+
printf '%s%s %d %s' \
171+
"$style_on" "$flags_str" "$match_quality" "$mimetype"
172+
if [[ -n $named_matches ]]; then
173+
printf ' → {%s}' \
174+
"$(sed -Ee 's!^,\s*!!' <<< "$named_matches")"
175+
fi
176+
printf '%s\n' "$style_off"
177+
fi
178+
}
179+
180+
181+
# Show a summary of all known MIME types that don't have an icon in SRCDIR.
182+
# Expects to be run inside the working tempdir.
183+
184+
find_missing_mimetype_icons () {
185+
# Start by caching lots of info
186+
get_mime_dirs > "$MIME_DIRS_CACHE"
187+
get_available_icons > "$AVAILABLE_ICONS_CACHE"
188+
for cache in \
189+
"$MIME_ICONS_CACHE" "$MIME_TYPES_CACHE" \
190+
"$MIME_GENERIC_ICONS_CACHE"
191+
do
192+
get_mimedirs_cache "$cache" > "./$cache"
193+
done
194+
195+
show_header
196+
while read -r type; do
197+
check_mimetype "$type" </dev/null
198+
done < "$MIME_TYPES_CACHE"
199+
}
200+
201+
202+
# Convert a single Boolean argument to 0 or 1.
203+
204+
boolify () {
205+
local arg
206+
arg=$(tr '[:lower:]' '[:upper:]' <<< "$1")
207+
case "$arg" in
208+
1|TRUE|Y|YES)
209+
printf "%s\n" 1
210+
return 0
211+
;;
212+
0|FALSE|N|NO)
213+
printf "%s\n" 0
214+
return 0
215+
;;
216+
esac
217+
printf >&2 "ERROR: %s\n" \
218+
"boolean value must be 1/true/yes or 0/false/no"
219+
exit 2
220+
}
221+
222+
223+
# Parse args and dispatch commands
224+
225+
parse_args () {
226+
#:
227+
#: Options:
228+
#:
229+
local flag value
230+
while [ $# -gt 0 ]; do
231+
case "$1" in
232+
# The "--" symbol indicates no further options.
233+
--)
234+
shift
235+
break
236+
;;
237+
#: --help, -h
238+
#: Show this help message, then exit.
239+
--help|-h)
240+
shift
241+
show_usage
242+
exit 0
243+
;;
244+
#: --output-level INT, -l INT (default: 1)
245+
#: How much output to show. Smaller values show less info,
246+
#: larger values show more info about successful lookups.
247+
--output-level|-l)
248+
shift
249+
value="${1:?No value for --output-level}"
250+
shift
251+
VERBOSITY=$((value + 0))
252+
;;
253+
#: --verbose, -v
254+
#: Increase output verbosity.
255+
--verbose|-v)
256+
shift
257+
VERBOSITY=$((VERBOSITY + 1))
258+
;;
259+
#: --quiet, -q
260+
#: Decrease output verbosity.
261+
--quiet|-q)
262+
shift
263+
VERBOSITY=$((VERBOSITY - 1))
264+
;;
265+
#: --personal-mimedir BOOL (default: 0)
266+
#: Load mimetypes etc. from ~/.local/share/mime too.
267+
--personal-mimedir)
268+
shift
269+
flag="${1:?No value for --personal-mimedir}"
270+
shift
271+
SCAN_PERSONAL_MIMEDIR=$(boolify "$flag")
272+
;;
273+
-*)
274+
printf 'Unrecognised option: "%s"\n' "$1"
275+
shift
276+
return 1
277+
;;
278+
*)
279+
break
280+
;;
281+
esac
282+
done
283+
284+
#:
285+
#: By default, at output level 1, this script shows which MIMEtypes
286+
#: are falling back to "<type>-x-generic.svg". That's usually the best
287+
#: place to look for mimetypes which need something more specific.
288+
#:
289+
290+
if [ $# -ne 0 ]; then
291+
printf "%s\n" "This script takes no positional parameters"
292+
printf "\n"
293+
return 1
294+
fi
295+
296+
return 0
297+
}
298+
299+
300+
# Main script flow.
301+
302+
main () {
303+
if ! parse_args "$@"; then
304+
printf >&2 "ERROR: failed to parse command line\n"
305+
show_usage
306+
exit 1
307+
fi
308+
local tempdir
309+
tempdir=$(mktemp --directory)
310+
( cd "$tempdir" && find_missing_mimetype_icons ) || true
311+
rm -fr "$tempdir"
312+
}
313+
main "$@"

0 commit comments

Comments
 (0)