3 # format-readme -- find and format a repository's readme blob
4 # Copyright (C) 2015,2016,2017 Kyle J. McKay.
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/>.
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.
45 if [ "$1" = "-r" ] ||
[ "$1" = "-p" ]; then
47 [ "$1" != "-p" ] || addpath
=1
52 if [ "$1" = "-i" ]; then
60 [ -n "$projdir" ] && [ -d "$projdir" ] ||
exit 2
61 cd "$projdir" ||
exit 2
63 gd
="$(git rev-parse --git-dir 2>&1)" ||
exit 2
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.
77 ! perl
-MPod::Html
-e 1 >/dev
/null
2>&1 || haspod
=1
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"
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
113 [ "$mode" != "120000" ] || readmeextlnk
=1
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
126 [ "$mode" != "120000" ] || readmeextlnk
=1
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
138 [ "$mode" != "120000" ] || readmeextlnk
=1
142 [Rr
][Ee
][Aa
][Dd
][Mm
][Ee
])
143 [ -z "$readme" ] ||
[ "$mode" != "120000" ] ||
continue
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
158 [ "$mode" != "120000" ] || readmefblnk
=1
163 $(git ls-tree -l $tree)
165 if [ -n "$readmefb" ] && [ -z "$readme" ]; then
167 readmenm
="$readmefbnm"
168 readmelnk
="$readmefblnk"
170 if [ -n "$readme" ] && [ -z "$readmeext" ]; then
172 readmeextnm
="$readmenm"
173 readmeextlnk
="$readmelnk"
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
194 case "$rel" in *?
/?
*)
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
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
223 [ "$size" -le 32768 ] ||
exit 1
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 '
233 binmode STDIN, ":perlio
" or exit 1
234 unless grep /^perlio$/, PerlIO::get_layers(STDIN);
235 exit 1 unless -T STDIN;
237 my $contents = <STDIN>;
238 exit 1 unless defined($contents) && length($contents) > 0 && length($contents) <= 32768;
241 ' 2>/dev/null)" ||
exit 1
244 case "$readmeextfmt" in
247 # Run Markdown.pl on it
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"
259 # Run pod2html and extract the contents
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 | \
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;
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/&/\&/g' -e 's/</\</g'
286 printf '%s\n' '</pre>'