build 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. #!/usr/bin/env sh
  2. # SPDX-License-Identifier: GPL-3.0-or-later
  3. # SPDX-FileCopyrightText: 2017,2021-2023 Leah Rowe <leah@libreboot.org>
  4. # SPDX-FileCopyrightText: 2017 Alyssa Rosenzweig <alyssa@rosenzweig.io>
  5. # SPDX-FileCopyrightText: 2017 Michael Reed <michael@michaelreed.io>
  6. [ "${DEBUG+set}" = "set" ] && set -v
  7. set -u -e
  8. id -u 1>/dev/null 2>/dev/null || exit 1
  9. if [ "$(id -u)" = "0" ]; then
  10. printf "%s: Running as root not allowed, for security.\n" "$0" 1>&2
  11. exit 1
  12. fi
  13. export LC_COLLATE=C
  14. export LC_ALL=C
  15. . "include/err.sh"
  16. . "include/news.sh"
  17. . "include/option.sh"
  18. tmpdir=""
  19. tmpdir_was_set="y"
  20. [ -z "${TMPDIR+x}" ] && tmpdir_was_set="n"
  21. if [ "${tmpdir_was_set}" = "n" ]; then
  22. export TMPDIR="/tmp"
  23. tmpdir="$(mktemp -d -t untitledssg_XXXXXXXX)"
  24. export TMPDIR="${tmpdir}"
  25. else
  26. export TMPDIR="${TMPDIR}"
  27. tmpdir="${TMPDIR}"
  28. fi
  29. UVARS="TITLE CSS DOMAIN BLOGTITLE LAZY BLOGDESCRIPTION DEFAULTLANG BLOGDIR"
  30. UVARS="$UVARS SITEMAP"
  31. eval "$(setvars "" sitechanged $UVARS tmproll)"
  32. linkpath="${0}"
  33. linkname="${linkpath##*/}"
  34. main()
  35. {
  36. check_path d www || fail "invalid www/ directory"
  37. eval "$(setvars "" _cmd _args)"
  38. mode="sites"
  39. if [ "${linkname}" != "clean" ]; then
  40. [ $# -gt 0 ] && \
  41. mode="${1}" && shift 1
  42. if [ "${mode}" = "pages" ] && [ $# -lt 1 ]; then
  43. mode="sites"
  44. elif [ "${mode}" != "pages" ] && [ "${mode}" != "sites" ]; then
  45. printf "%s\n" "${mode}" > "${tmpdir}/args"
  46. _args="${mode}"
  47. mode="sites"
  48. fi
  49. fi
  50. if [ $# -lt 1 ] && [ -z "${_args}" ]; then
  51. for x in www/*; do
  52. printf "%s\n" "${x}" >> "${tmpdir}/args"
  53. done
  54. else
  55. for x in "$@"; do
  56. printf "%s\n" "${x}" >> "${tmpdir}/args"
  57. done
  58. fi
  59. [ "${linkname}" = "clean" ] && _cmd="cleansite"
  60. if [ "${linkname}" != "clean" ]; then
  61. [ "${mode}" = "sites" ] && _cmd="buildsite"
  62. [ "${mode}" = "pages" ] && _cmd="buildpage"
  63. fi
  64. [ -z "${_cmd}" ] && fail "unrecognised command"
  65. while read -r x; do
  66. [ -z "${x}" ] && continue
  67. eval "$(setvars "" $UVARS)"
  68. startdate=$(date +%s%N | cut -b1-13)
  69. $_cmd "$x"
  70. enddate=$(date +%s%N | cut -b1-13)
  71. endtime=$(( enddate - startdate ))
  72. printf "time: %d milliseconds\n" "${endtime}"
  73. done < "${tmpdir}/args"
  74. untitled_exit 0
  75. }
  76. buildsite()
  77. {
  78. eval "$(setvars "n" sitechanged tmproll)"
  79. _sitedir="${1#www/}"
  80. _sitedir="www/${_sitedir%%/*}"
  81. printf "\nProcessing files in site: %s\n" "${_sitedir##*/}"
  82. check_path d "${_sitedir}" || return 0
  83. check_symlinks "${_sitedir}" "sitedir" || return 0
  84. check_site_changed "${_sitedir}"
  85. check_site_config "${_sitedir}" || return 0
  86. find -L "${_sitedir}/site/" -name "*.md" > "${tmpdir}/pages"
  87. while read -r y; do
  88. mkhtml "${y}" "${_sitedir##*/}"
  89. done < "${tmpdir}/pages"
  90. mksitemap "${_sitedir}"
  91. mknews "$_sitedir"
  92. }
  93. buildpage()
  94. {
  95. [ $# -lt 1 ] && return 0
  96. _file=${1%.md}
  97. _file="${1}"
  98. _file="${_file#/}"
  99. _file="${_file#./}"
  100. _filelocal="${_file##*/}"
  101. [ "${_file%.md}" = "${_file}" ] && return 0
  102. # path translation
  103. # e.g. libreboot/index.md becomes www/libreboot/site/index.md
  104. # e.g. libreboot/site/index.md becomes www/libreboot/site/index.md
  105. # e.g. www/libreboot/index.md becomes www/libreboot/site/index.md
  106. # in all cases, the correct path is: www/libreboot/site/index.md
  107. _file="${_file#www/}"
  108. _realfile="${_file#*/}"
  109. [ "${_realfile#site/}" = "${_realfile}" ] && \
  110. _realfile="site/${_realfile}"
  111. _file="www/${_file%%/*}/${_realfile}"
  112. _sitedir="${_file#www/}"
  113. [ "${_sitedir}" = "${_file}" ] && return 0
  114. _sitename="${_sitedir%%/*}"
  115. [ "${_sitename}" = "${_sitedir}" ] && return 0
  116. _sitedir="www/${_sitename}"
  117. check_path d "${_sitedir}" || return 0
  118. check_symlinks "${_sitedir}" "sitedir" || return 0
  119. printf "\nPage: %s\n" "${_file}"
  120. printf "Processing page in site directory: %s/site/\n" "${_sitedir}"
  121. check_site_config "${_sitedir}" || return 0
  122. mkhtml "${_file}" "${_sitedir##*/}"
  123. mksitemap "${_sitedir}"
  124. mknews "$_sitedir"
  125. }
  126. cleansite() {
  127. SITENAME="${1##*/}"
  128. if [ ! -d "www/${SITENAME}/site" ]; then
  129. printf "Site '%s' has no site directory; skipping clean\n" \
  130. "${SITENAME}"
  131. return 0
  132. fi
  133. if [ -L "www/${SITENAME}" ] || [ -L "www/${SITENAME}/site" ]; then
  134. printf "Site '%s' is a symlink directory. skipping clean\n" \
  135. "${SITENAME}"
  136. return 0
  137. fi
  138. printf "Cleaning generated files from site: '%s'\n" "${SITENAME}"
  139. find -L "www/${SITENAME}/site/" -type f -name "*.html" \
  140. > "${tmpdir}/list"
  141. while read -r f; do
  142. if [ ! -f "${f}" ]; then continue; fi
  143. if [ -L "${f}" ]; then continue; fi
  144. if [ -f "${f%.html}.md" ] && [ ! -L "${f%.html}.md" ]; then
  145. rm -f "${f}"
  146. fi
  147. done < "${tmpdir}/list"
  148. find -L "www/${SITENAME}/site" -type f -name "*.date" \
  149. > "${tmpdir}/list"
  150. while read -r f; do
  151. if [ ! -f "${f}" ]; then continue; fi
  152. if [ -L "${f}" ]; then continue; fi
  153. if [ "${f##*/}" = "footer.include.date" ] \
  154. || [ "${f##*/}" = "nav.include.date" ] \
  155. || [ "${f##*/}" = "template.include.date" ]; then
  156. continue
  157. fi
  158. rm -f "${f}"
  159. done < "${tmpdir}/list"
  160. rm -f www/"${SITENAME}"/site/feed.xml
  161. find -L "www/${SITENAME}/site/" -type f -name "MANIFEST" \
  162. > "${tmpdir}/list"
  163. while read -r f; do
  164. if [ ! -f "${f}" ]; then continue; fi
  165. if [ -L "${f}" ]; then continue; fi
  166. for ff in "${f%MANIFEST}"{index.md,index.html,feed.xml}; do
  167. if [ ! -f "${ff}" ]; then continue; fi
  168. if [ -L "${ff}" ]; then continue; fi
  169. rm -f "${ff}"
  170. done
  171. done < "${tmpdir}/list"
  172. }
  173. # make human readable html sitemap, and a google-friendly xml sitemap
  174. mksitemap()
  175. {
  176. [ "${SITEMAP}" = "n" ] && return 0
  177. _sitedir="${1}"
  178. _tmpfile=$(mktemp -t untitled_www.XXXXXXXXXX)
  179. _tmpxml="$(mktemp -t untitled_www.XXXXXXXXXX)"
  180. if [ -f "${_sitedir}/site/sitemap.include" ]; then
  181. cat "${_sitedir}/site/sitemap.include" > "${_tmpfile}"
  182. else
  183. printf "# Site map\n\nList of pages:\n" \
  184. > "${_tmpfile}"
  185. fi
  186. printf "\n\n" >> "${_tmpfile}"
  187. find -L "${_sitedir}/site/" -type f -name "*.md" > "${tmpdir}/toc"
  188. sort "${tmpdir}/toc" > "${tmpdir}/toc_sorted"
  189. mv "${tmpdir}/toc_sorted" "${tmpdir}/toc"
  190. printf "<?xml version='1.0' encoding='UTF-8'?>\n" >> "${_tmpxml}"
  191. printf "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" \
  192. >> "${_tmpxml}"
  193. printf "<div class='sitemap'>\n" >> "${_tmpfile}"
  194. while read -r y; do
  195. check_path f "${y}" || continue
  196. [ "${y}" = "${_sitedir}/site/sitemap.md" ] && continue
  197. _uri="${y##"${_sitedir}/site"}"
  198. _uri2="${_uri}"
  199. _uri="${_uri%index.md}"
  200. _urihtml="${_uri%.md}.html"
  201. [ "$_urihtml" != "${_urihtml%/.html}" ] && \
  202. _urihtml="${_urihtml%.html}"
  203. # for the markdown sitemap, to be compiled into html
  204. printf "* %s: [%s](%s)\n" "${_uri}" "$(mktitle "${y}")" \
  205. "${_uri}" >> "${_tmpfile}"
  206. # xml sitemap, for google and so on
  207. printf " <url>\n" >> "${_tmpxml}"
  208. printf " <loc>%s/%s</loc>\n" "${DOMAIN%/}" "${_urihtml#/}" \
  209. >> "${_tmpxml}"
  210. printf " <lastmod>%s</lastmod>\n" \
  211. "`date -r "$y.date" +"%Y-%m-%d"`" >> "${_tmpxml}"
  212. printf " </url>\n" >> "${_tmpxml}"
  213. done < "${tmpdir}/toc"
  214. printf "</div>\n\n" >> "${_tmpfile}"
  215. printf "</urlset>\n" >> "${_tmpxml}"
  216. cp "${_tmpfile}" "${_sitedir}/site/sitemap.md"
  217. cp "${_tmpxml}" "${_sitedir}/site/sitemap.xml"
  218. chmod 644 "${_sitedir}/site/sitemap.xml" "${_sitedir}/site/sitemap.md"
  219. rm -f "${_tmpfile}" "${_tmpxml}"
  220. check_path f "${_sitedir}/site/sitemap.md" || return 0
  221. mkhtml "${_sitedir}/site/sitemap.md" "${_sitedir##*/}"
  222. }
  223. # convert pandoc-markdown file into html, using a template file
  224. mkhtml()
  225. {
  226. check_path f "${1}" || return 0
  227. eval "$(setvars "" _css _footer _nav _template)"
  228. _final="${tmpdir}/final"
  229. _file="${1%.md}"
  230. _sitedir="www/${2}"
  231. # will be set to y if page changes are detected
  232. _needchange="n"
  233. # e.g. file.md is default(english) and file.ru.md is russian
  234. _realpage="${_file##*/}"
  235. _realpagelang="${_realpage##*.}"
  236. if [ "${_realpagelang}" = "${_realpage}" ]; then
  237. _realpagelang="${DEFAULTLANG}"
  238. else
  239. _file="${_file%".${_realpagelang}"}"
  240. fi
  241. _strconf="lang/${_realpagelang}/strings.cfg"
  242. for p in "lang/${DEFAULTLANG}/strings.cfg" "lang/en/strings.cfg"; do
  243. [ -f "$_strconf" ] || _strconf="$p"
  244. done
  245. # This is the uri but without any extension, *and without language
  246. # extension*. This will be used extensively, especially for backlinks
  247. _uri="${_file##"${_sitedir}/site"}" # without file extension e.g. .html
  248. # Backlink text for each page. This helps with site navigation
  249. _backlink=""
  250. if [ "${_uri}" != "/index" ]; then # the homepage doesn't need one
  251. _backlink="$(getConfigValue "${_strconf}" "BACKLINK_PREVDIR")"
  252. [ "${_uri##*/}" = "index" ] || \
  253. _backlink="$(getConfigValue "${_strconf}" \
  254. "BACKLINK_CURRENTDIR")"
  255. fi
  256. # Allow a given page to override the footer/nav/template/css file
  257. for p in footer nav template css; do
  258. eval "$(setvars "" _${p})"
  259. [ -f "${_file}.${p}" ] && \
  260. eval "$(setvars "${_file}.${p}" _${p})"
  261. [ "${_realpagelang}" != "${DEFAULTLANG}" ] && \
  262. [ -f "${_file}.${_realpagelang}.${p}" ] && \
  263. eval "$(setvars "${_file}.${_realpagelang}.$p" _$p)"
  264. done
  265. [ -z "${_css}" ] || _css="${_css##*/}"
  266. if [ "${_realpagelang}" != "${DEFAULTLANG}" ]; then
  267. _file="${_file}.${_realpagelang}"
  268. _uri="${_uri}.${_realpagelang}"
  269. fi
  270. # allow overriding the global footer/nav/template file
  271. for p in footer nav template; do
  272. eval "[ -z \"\${_${p}}\" ] || continue"
  273. [ -f "${_sitedir}/site/${p}.include" ] && \
  274. eval "$(setvars "${_sitedir}/site/${p}.include" _${p})"
  275. [ "${_realpagelang}" != "${DEFAULTLANG}" ] && \
  276. [ -f "$_sitedir/site/${p}.${_realpagelang}.include" ] && \
  277. eval "_$p=\"$_sitedir/site/$p.$_realpagelang.include\""
  278. done
  279. for y in "${_file}.md" "$_footer" "$_nav" "$_template"; do
  280. [ -z "${y}" ] && continue
  281. filehasnotchanged "${y}" || _needchange="y"
  282. done
  283. if [ "${linkname}" = "roll" ] || [ "${tmproll}" = "y" ]; then
  284. _needchange="y"
  285. fi
  286. [ "${_needchange}" = "n" ] && check_path f "${_file}.html" && return 0
  287. # The news and sitemap function need to know this
  288. # because they will only be called if this variable is set to y
  289. sitechanged="y"
  290. _tmpfile=$(mktemp -t untitled_www.XXXXXXXXXX)
  291. eval "$(setvars "" _date _author)"
  292. # This code sucks. TODO: do it better
  293. _firstchar=$(head -c 1 "${1}")
  294. _firstthreechars=$(head -c 3 "${1}")
  295. if [ "${_firstchar}" = "#" ]; then
  296. filetitle="$(mktitle "${1}")"
  297. _tmp2=$(mktemp -t untitled_www.XXXXXXXXXX)
  298. head -n1 "title.example" >"$_tmp2"
  299. printf "title: %s\n" "${filetitle}" >> "${_tmp2}"
  300. tail -n-2 "title.example" >> "$_tmp2"
  301. _tmptitle="$(cat "${_tmp2}")"
  302. rm -f "${_tmp2}"
  303. _pagetext="$(tail -n+2 "${1}")"
  304. elif [ "${_firstchar}" = "%" ]; then
  305. _tmp2=$(mktemp -t untitled_www.XXXXXXXXXX)
  306. _tmptitle="$(head -n3 "${1}")"
  307. printf "%s\n" "${_tmptitle}" > "$_tmp2"
  308. chunk="$(head -n1 "$_tmp2")"
  309. chunk="$(tail -n+2 "$_tmp2")"
  310. printf "%s\n" "${chunk}" > "$_tmp2"
  311. _author="$(head -n1 "$_tmp2")"
  312. chunk="$(tail -n+2 "$_tmp2")"
  313. printf "%s\n" "${chunk}" > "$_tmp2"
  314. _date="$(head -n1 "$_tmp2")"
  315. rm -f "$_tmp2"
  316. _pagetext="$(tail -n+4 "${1}")"
  317. elif [ "${_firstthreechars}" = "---" ]; then
  318. _tmptitle="$(head -n4 "${1}")"
  319. eval "$(setvars "" _author _date)"
  320. _pagetext="$(tail -n+5 "${1}")"
  321. else
  322. printf "WARNING: no title. defaulting to first line.\n" 1>&2
  323. filetitle="$(mktitle "${1}")"
  324. _tmp2=$(mktemp -t untitled_www.XXXXXXXXXX)
  325. head -n1 "title.example" > "$_tmp2"
  326. printf "title: %s\n" "${filetitle}" >> "${_tmp2}"
  327. tail -n-2 "title.example" >> "$_tmp2"
  328. _tmptitle="$(cat "${_tmp2}")"
  329. rm -f "${_tmp2}"
  330. _pagetext="$(tail -n+2 "${1}")"
  331. fi
  332. printf "%s\n" "${_tmptitle}" > "$_tmpfile"
  333. if [ -n "${_nav}" ]; then
  334. printf "\n<div class='nav'>\n%s\n</div>\n" \
  335. "$(cat "${_nav}")" >> "${_tmpfile}"
  336. fi
  337. # insert the language select menu
  338. _langmenu=""
  339. _defaultpage="${_file##*/}"
  340. _defaultpage="${_defaultpage%.*}"
  341. if [ -n "${_defaultpage}" ]; then
  342. for y in "${_file%/*}/${_defaultpage}".*.md; do
  343. [ ! -f "${y}" ] && continue
  344. _langname=$(getlangname "${y}" "${DEFAULTLANG}")
  345. _langmenu="${_langmenu} | [${_langname}](${y##*/})"
  346. done
  347. fi
  348. if [ "${_langmenu}" != "" ]; then
  349. _langmenu="${_langmenu# | }"
  350. if [ -f "${_file%/*}/${_defaultpage}.md" ]; then
  351. _langname=$(getlangname \
  352. "${_file%/*}/${_defaultpage}.md" "${DEFAULTLANG}")
  353. if [ "${_defaultpage}" = "index" ]; then
  354. _langmenu="[${_langname}](./) | ${_langmenu}"
  355. else
  356. _langmenu="[$_langname]($_defaultpage.md) | $_langmenu"
  357. fi
  358. fi
  359. fi
  360. # TODO: display "select language" text
  361. [ -n "${_langmenu}" ] && \
  362. printf "\n%s\n" "${_langmenu}" >> "${_tmpfile}"
  363. [ -n "${_backlink}" ] && \
  364. printf "\n%s\n" "${_backlink}" >> "${_tmpfile}"
  365. # the directory was already checked. no need to
  366. # check the validity of it, because it's part
  367. # of untitled, not part of a given site, and
  368. # what goes in untitled is reviewed thoroughly
  369. if [ -n "${_author}" ] && [ -n "${_date}" ]; then
  370. _pubname="$(getConfigValue "${_strconf}" "PUBLISHED_BY")"
  371. printf "\n%s %s\n" "$_pubname" "${_author#% }" >> "$_tmpfile"
  372. _pubdate="$(getConfigValue "${_strconf}" "PUBLICATION_DATE")"
  373. printf "\n%s %s\n" "${_pubdate}" "${_date#% }" >> "${_tmpfile}"
  374. fi
  375. printf "\n%s\n" "${_pagetext}" >> "$_tmpfile"
  376. if [ -n "${_footer}" ]; then
  377. printf "\n<div id='footer'>\n%s\n</div>\n" \
  378. "$(cat "${_footer}")" >> "${_tmpfile}"
  379. fi
  380. eval "$(getConfigValues "${_strconf}" MARKDOWN_LINK RSS_LINK \
  381. SITEMAP_LINK SHAMELESS_PLUG PDIR)"
  382. printf "\n%s <%s%s.md>\n" "${MARKDOWN_LINK}" "${DOMAIN}" \
  383. "${_uri##/}" >> "${_tmpfile}"
  384. find -L "$_sitedir/site/" -type f -name "MANIFEST" | grep -q MANIFEST \
  385. && printf "\n%s\n" "${RSS_LINK}" >> "${_tmpfile}"
  386. check_path f "${_sitedir}/site/sitemap.md" && \
  387. printf "\n%s\n" "${SITEMAP_LINK}" >> "${_tmpfile}"
  388. # TODO: make this configurable to enable/disable in site.cfg
  389. printf "\n%s\n" "${SHAMELESS_PLUG}" >> "${_tmpfile}"
  390. # change out .md -> .html
  391. # but not for external links
  392. sed -e '/\/\//!{s/\.md\(#[a-zA-Z0-9_-]*\)\?\([])]*\)/.html\1\2/g}' \
  393. "${_tmpfile}" > "${_final}.md"
  394. # toc is always enabled on news pages
  395. _toc=$(grep -q "^x-toc-enable: true$" "$_tmpfile" && \
  396. printf '%s\n' "--toc --toc-depth=4") || _toc=""
  397. # TODO: make toc depth configurable in site.cfg
  398. [ -f "${_file%/*}/MANIFEST" ] && \
  399. _toc="--toc --toc-depth=4"
  400. printf "Generating '%s.html'\n" "${_file}"
  401. _html_link="${MARKDOWN_LINK%.md}"
  402. _html_link="${DOMAIN}${_uri##/}"
  403. if [ "${_html_link##*/}" = "index" ]; then
  404. _html_link="${_html_link%/*}/"
  405. else
  406. _html_link="${_html_link}.html"
  407. fi
  408. canonurl="${_html_link%/*}/${_defaultpage}.html"
  409. if [ "$canonurl" != "${canonurl%/index.html}" ]; then
  410. canonurl="${canonurl%index.html}"
  411. fi
  412. # parse page description
  413. pagedesc="${_file}.md.description"
  414. metadesc=""
  415. if [ ! -L "$pagedesc" ] && [ -f "$pagedesc" ]; then
  416. while read -r descline; do
  417. metadesc="$metadesc $descline"
  418. done < "$pagedesc"
  419. metadesc="${metadesc# }"
  420. fi
  421. pandoc -V lang="$_realpagelang" -V dir="$PDIR" $_toc -f markdown+smart \
  422. -t html "${_final}.md" --preserve-tabs --tab-stop 8 -T "$TITLE" \
  423. -V antisocialurl="$_html_link" -s --css "$CSS" --css "$_css" \
  424. -V antisocialcanon="$canonurl" -V metadesc="$metadesc" \
  425. --template "$_template" --metadata return="" > "${_final}.html"
  426. # generate section title anchors as [link]
  427. sed -e 's_^<h\([123]\) id="\(.*\)">\(.*\)</h\1>_<div class="h"><h\1 id="\2">\3</h\1><a aria-hidden="true" href="#\2">[link]</a></div>_' \
  428. "${_final}.html" > "${_final}.md"
  429. mv "${_final}.md" "${_final}.html"
  430. # LAZY images, if enabled
  431. if [ "${LAZY}" = "y" ]; then
  432. for y in img iframe; do
  433. sed -e "s/<${y}/<${y}\ loading=\"lazy\"/g" \
  434. "${_final}.html" > "${_final}.md"
  435. mv "${_final}.md" "${_final}.html"
  436. done
  437. fi
  438. mv "${_final}.html" "${_file}.html"
  439. rm -f "$_tmpfile"
  440. }
  441. main "$@"