scripts: purge use of test '-a' and '-o' ops and clean up
[girocco.git] / bin / format-readme
blobfeae72101c8ac5b0ce10ca243627c2d5ae0143a3
1 #!/bin/sh
3 # format-readme -- find and format a repository's readme blob
4 # Copyright (C) 2015,2016,2017 Kyle J. McKay.
5 # All rights reserved.
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # Version 1.2.7
22 # Usage: format-readme [-r <prefix> | -p <prefix> [ -i <imgprefix ] ]
23 # <path-to-repo.git> [<treeish>]
25 # With -r <prefix>, prefix all non-absolute URLs with <prefix>
27 # With -p <prefix>, prefix all non-aboslute URLs with <prefix>/<symlink-path>
28 # where <symlink-path> is the dirname portion of symlink if the selected readme
29 # is a symlink that needs to be followed. If it's not a symlink or it's in the
30 # same directory, the "/<symlink-path>" is not added but <prefix> still is.
32 # With -i <imgprefix> use <imgprefix> instead of <prefix> when link is to
33 # an image. If -p was used it will include the path, if -r was used it won't.
35 set -e
37 nl='
39 symlinks=
41 addprefix=
42 addpath=
43 urlprefix=
44 imgprefix=
45 if [ "$1" = "-r" ] || [ "$1" = "-p" ]; then
46 addprefix=1
47 [ "$1" != "-p" ] || addpath=1
48 shift
49 [ -n "$1" ] || exit 2
50 urlprefix="${1%/}/"
51 shift
52 if [ "$1" = "-i" ]; then
53 shift
54 [ -n "$1" ] || exit 2
55 imgprefix="${1%/}/"
56 shift
59 projdir="$1"
60 [ -n "$projdir" ] && [ -d "$projdir" ] || exit 2
61 cd "$projdir" || exit 2
62 unset GIT_DIR
63 gd="$(git rev-parse --git-dir 2>&1)" || exit 2
64 cd "$gd" || exit 2
65 treeish="${2:-HEAD}"
66 tree="$(git rev-parse --quiet --verify "$treeish"^{tree} 2>/dev/null)" || exit 2
68 # We prefer the first file or symlink we find with
69 # a supported extension and then we will follow it
70 # if it's a relative symlink with no '.' or '..' components.
71 # If we don't find a supported extension, we use just plain README
72 # which we assume to be plain text (and we will follow a symlink).
73 # We prefer a markdown extension over others and any extension
74 # other than plain text next followed by plain text and then no extension.
76 haspod=
77 ! perl -MPod::Html -e 1 >/dev/null 2>&1 || haspod=1
79 readme=
80 readmenm=
81 readmelnk=
82 readmefb=
83 readmefbnm=
84 readmefblnk=
85 readmeext=
86 readmeextnm=
87 readmeextlnk=
88 readmeextfmt=
89 while read -r mode type hash size name; do
90 [ "$mode" = "100644" ] || [ "$mode" = "100755" ] || [ "$mode" = "120000" ] || continue
91 [ "$size" != "0" ] && [ "$size" != "-" ] || continue
92 [ "$type" = "blob" ] || continue
93 [ "$mode" != "120000" ] || [ "$size" -lt 1024 ] || continue
94 [ "$mode" != "120000" ] || symlinks="$symlinks$hash $name$nl"
95 case "$name" in
97 # Markdown extensions must match this pattern:
98 # /md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee/i
99 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Dd]|\
100 [Rr][Ee][Aa][Dd][Mm][Ee].[Rr][Mm][Dd]|\
101 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Kk][Dd]|\
102 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Kk][Dd][Nn]|\
103 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Dd][Ww][Nn]|\
104 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Dd][Oo][Ww][Nn]|\
105 [Rr][Ee][Aa][Dd][Mm][Ee].[Mm][Aa][Rr][Kk][Dd][Oo][Ww][Nn]|\
106 [Rr][Ee][Aa][Dd][Mm][Ee].[Ll][Ii][Tt][Cc][Oo][Ff][Ff][Ee][Ee])
107 if [ -n "$readmeext" ]; then
108 [ "$readmeextfmt" != "md" ] || [ "$mode" != "120000" ] || continue
110 readmeext="$hash"
111 readmeextnm="$name"
112 readmeextlnk=
113 [ "$mode" != "120000" ] || readmeextlnk=1
114 readmeextfmt=md
117 [Rr][Ee][Aa][Dd][Mm][Ee].[Pp][Oo][Dd])
118 [ -n "$haspod" ] || continue
119 if [ -n "$readmeext" ]; then
120 [ "$readmeextfmt" != "md" ] || continue
121 [ "$readmeextfmt" = "txt" ] || [ "$mode" != "120000" ] || continue
123 readmeext="$hash"
124 readmeextnm="$name"
125 readmeextlnk=
126 [ "$mode" != "120000" ] || readmeextlnk=1
127 readmeextfmt=pod
130 [Rr][Ee][Aa][Dd][Mm][Ee].[Tt][Xx][Tt]|\
131 [Rr][Ee][Aa][Dd][Mm][Ee].[Tt][Ee][Xx][Tt])
132 if [ -n "$readmeext" ]; then
133 [ "$readmeextfmt" = "txt" ] && [ "$mode" != "120000" ] || continue
135 readmeext="$hash"
136 readmeextnm="$name"
137 readmeextlnk=
138 [ "$mode" != "120000" ] || readmeextlnk=1
139 readmeextfmt=txt
142 [Rr][Ee][Aa][Dd][Mm][Ee])
143 [ -z "$readme" ] || [ "$mode" != "120000" ] || continue
144 readme="$hash"
145 readmenm="$name"
146 readmelnk=
147 [ "$mode" != "120000" ] || readmelnk=1
150 [Rr][Ee][Aa][Dd][Mm][Ee].?*)
151 [ -z "$readmefb" ] || [ "$mode" != "120000" ] || continue
152 [ "${name%.*}" = "${name%%.*}" ] || continue
153 [ "${name#*.}" = "${name##*[!A-Za-z0-9+_]}" ] || continue
154 [ "${name%[$ws]*}" = "$name" ] || continue
155 readmefb="$hash"
156 readmefbnm="$name"
157 readmefblnk=
158 [ "$mode" != "120000" ] || readmefblnk=1
161 esac
162 done <<EOT
163 $(git ls-tree -l $tree)
165 if [ -n "$readmefb" ] && [ -z "$readme" ]; then
166 readme="$readmefb"
167 readmenm="$readmefbnm"
168 readmelnk="$readmefblnk"
170 if [ -n "$readme" ] && [ -z "$readmeext" ]; then
171 readmeext="$readme"
172 readmeextnm="$readmenm"
173 readmeextlnk="$readmelnk"
174 readmeextfmt=txt
176 [ -n "$readmeext" ] || exit 1
177 if [ -n "$readmeextlnk" ]; then
178 rel="$(git cat-file blob $readmeext 2>/dev/null)" || exit 1
179 case "$rel" in /*) exit 1; esac
180 case "/$rel/" in */../*|*/./*) exit 1; esac
181 case "$rel" in */*) :;; ?*)
182 while read -r hash name; do
183 if [ -n "$hash" ] && [ "$name" = "$rel" ]; then
184 rel2="$(git cat-file blob $hash 2>/dev/null)" || exit 1
185 case "$rel2" in /*) exit 1; esac
186 case "/$rel2/" in */../*|*/./*) exit 1; esac
187 rel="$rel2"
188 break
190 done <<-EOT
191 ${symlinks%?}
193 esac
194 case "$rel" in *?/?*)
195 suffix="${rel#*/}"
196 prefix="${rel%/$suffix}"
197 while read -r hash name; do
198 if [ -n "$hash" ] && [ "$name" = "$prefix" ]; then
199 rel2="$(git cat-file blob $hash 2>/dev/null)" || exit 1
200 case "$rel2" in /*) exit 1; esac
201 case "/$rel2/" in */../*|*/./*) exit 1; esac
202 rel="$rel2/$suffix"
203 break
205 done <<-EOT
206 ${symlinks%?}
208 esac
209 if [ -n "$addpath" ]; then
210 dir="$(dirname "$rel")"
211 if [ "$dir" != "." ]; then
212 urlprefix="${urlprefix%/}/$dir"
213 [ -z "$imgprefix" ] || imgprefix="${imgprefix%/}/$dir"
216 read -r mode type hash size name <<EOT
217 $(git ls-tree -l $tree -- "$rel")
219 [ "$mode" = "100644" ] || [ "$mode" = "100755" ] || exit 1
220 [ "$type" = "blob" ] || exit 1
221 [ "$size" != "0" ] && [ "$size" != "-" ] || exit 1
222 # Fail if > 32K
223 [ "$size" -le 32768 ] || exit 1
224 readmeext="$hash"
227 # Allow up to 32K, but fail if it doesn't look like it's text.
229 contents="$(git cat-file blob $readmeext | perl -e '
230 use 5.008;
231 use strict;
232 use warnings;
233 binmode STDIN, ":perlio" or exit 1
234 unless grep /^perlio$/, PerlIO::get_layers(STDIN);
235 exit 1 unless -T STDIN;
236 undef $/;
237 my $contents = <STDIN>;
238 exit 1 unless defined($contents) && length($contents) > 0 && length($contents) <= 32768;
239 print $contents;
240 exit 0;
241 ' 2>/dev/null)" || exit 1
243 # Format the result
244 case "$readmeextfmt" in
247 # Run Markdown.pl on it
248 cmd='Markdown.pl'
249 if [ -n "$addprefix" ]; then
250 cmd="$cmd -r \"$urlprefix\""
251 [ -z "$imgprefix" ] || cmd="$cmd -i \"$imgprefix\""
253 printf '<!-- README NAME: %s -->\n' "$readmeextnm"
254 printf '%s' "$contents" | eval "$cmd 2>/dev/null"
255 exit $?
258 pod)
259 # Run pod2html and extract the contents
260 arg=
261 if [ -n "$addprefix" ] && [ -n "${urlprefix%/}" ]; then
262 arg=", \"--htmlroot=${urlprefix%/}\""
264 printf '<!-- README NAME: %s -->\n' "$readmeextnm"
265 printf '%s' "$contents" | \
266 perl -MPod::Html -e "pod2html \"--quiet\", \"--no-index\"$arg" 2>/dev/null | \
267 perl -e '
268 use strict;
269 use warnings;
270 undef $/;
271 my $contents = <STDIN>;
272 $contents =~ s,^.*<body[^>]*>\s*,,is;
273 $contents =~ s,\s*</body[^>]*>.*$,,is;
274 $contents =~ s,^.*<!--\s*INDEX\s+END\s*-->\s*,,is;
275 $contents =~ s,^\s*(?:<p>\s*</p>\s*)+,,is;
276 print $contents;
278 exit $?
281 txt)
282 # It's a <pre> block but we need some escaping
283 printf '<!-- README NAME: %s -->\n' "$readmeextnm"
284 printf '%s' '<pre class="plaintext">'
285 printf '%s' "$contents" | LC_ALL=C sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g'
286 printf '%s\n' '</pre>'
287 exit 0
290 esac
291 exit 1