#!/bin/rc nl=' '; result=0; fn error { echo $* >[1=2]; result=-1; } fn readlinks { awk ' function isurl(s) { return s ~ /^\.\/|^\.\.\/|^http:\/\/|^https:\/\/|^gemini:\/\// } # Image: [xpx] () Source: /^(Image|Banner|Icon): [^ ]+ \[[0-9]+x[0-9]+px\]/ && isurl((url = $2)) { print FNR, url; next; } # Video: [xpx] () /^Video: [^ ]+ \[[0-9]+x[0-9]+px\] \(Preview: [^ )]+\)/ && isurl((url = $2)) && isurl((previewurl = substr($5, 1, length($5) - 1))) { print FNR, url; print FNR, previewurl; next; } # Audio: /^Audio: [^ ]+$/ && isurl((url = $2)) { print FNR, url; next; } # : (: ) match($0, /: [^ ]+ \(/) && (altend = RSTART) && (xaltstart = RSTART + RLENGTH) && isurl((url = substr($0, RSTART + 2, RLENGTH - 4))) && match($0, /: [^ ]+\)$/) && (xaltend = RSTART) && isurl((xurl = substr($0, RSTART + 2, RLENGTH - 3))) { print FNR, url; print FNR, xurl; next; } # : () match($0, /: [^ ]+ \(/) && isurl((url = substr($0, RSTART + 2, RLENGTH - 4))) && /\)$/ { print FNR, url; next; } # (: ) match($0, /^[^ ]+ \(/) && (xaltstart = RLENGTH + 1) && match($0, /: [^ ]+\)$/) && (xaltend = RSTART) && isurl((url = $1)) && isurl((xurl = substr($NF, 1, length($NF) - 1))) { print FNR, url; print FNR, xurl; next; } # () — /^[^ ]+ \(/ && match($0, /\) — /) && isurl((url = $1)) { print FNR, url; next; } # () /^[^ ]+ \(/ && /\)$/ && isurl((url = $1)) { print FNR, url; next; } # isurl((url = $0)) { print FNR, url; next; } # See also: # Further reading: # For more details, see: /(See also|Further reading|For more details, see): [^ ]+$/ && isurl((url = $NF)) { print FNR, url; next; } # - : # .* forces last occurrence. match($0, /- .*: /) && isurl((url = substr($0, RLENGTH + 1))) { print FNR, url; next; } # - /^- / && isurl((url = substr($0, 3))) { print FNR, url; next; } # : # .* forces last occurrence. match($0, /.*: /) && isurl((url = substr($0, RLENGTH + 1))) { print FNR, url; next; } # — , /^— / && /, [^ ]+$/ && isurl((url = $NF)) { print FNR, url; next; } ' $1 } fn checksections { awk ' function error(s) { printf "%s:%s: %s\n", FILENAME, FNR, s; result = 1; } /^[0-9]+\. / { s = substr($1, 1, length($1) - 1) + 0; if (s != section + 1) error("misnumbered section"); section = s; subsection = 0; next; } /^[0-9]+\.[0-9]+\. / { i = index($1, "."); s = substr($1, 1, i - 1) + 0; ss = substr($1, i + 1, length($1) - i - 1) + 0; if (s != section || ss != subsection + 1) error("misnumbered subsection"); subsection = ss; next; } END { exit result; } ' $1 >[1=2] } fn read { s=$*($#*); while (! ~ $#* 2) { $1=`{ echo $s | awk '{ print $1 }' }; s=`{ echo $s | awk 'match($0, / *[^ ]+ */) { print substr($0, RLENGTH) }' }; shift; } $1=$s; } fn relpath { cleanname `$nl{ basename -d $2 }^/`$nl{ echo $1 } } fn abspath { echo `{ cleanname `{ pwd }^/$1 } } fn checkifbacklinks { from=$1; to=$2; absfrom=`{ abspath $1 }; absto=`{ abspath $2 }; if (! { { ~ $absto /lib/garden/journal/* } || { ~ $absto /lib/garden/index.txt } || { ~ $absfrom /lib/garden/bootable-media/index.txt && ~ $absto /lib/garden/lily/nixos/index.txt } || { ~ $absfrom /lib/garden/plan-9/wacom/index.txt && ~ $absto /lib/garden/intuos/9/index.txt } }) { found=no; for (link in `$nl{ readlinks $from }) { read loc url $link; if (~ $url ./* ../* && ~ `{ relpath $url $from } $to) found=yes; } if (~ $found no) error $from: no backlink to $to; } } for (file in `{ walk -f | grep '(^|/)index.txt$' }) { for (link in `$nl{ readlinks $file }) { read loc url $link; if (~ $url ./* ../*) { otherfile=`$nl{ relpath `$nl{ echo $url } $file }; if (test -e `$nl{ echo $otherfile }) { if (~ $url */index.txt) { checkifbacklinks $otherfile $file; # Check if otherfile backlinks to file. } } if not error $file:$loc: $otherfile does not exist; } } if (checksections $file); if not result=1; } exit $result;