]> Sergey Matveev's repositories - zk.zsh.git/blob - zk.zsh
Built-in mkdir
[zk.zsh.git] / zk.zsh
1 #!/usr/bin/env zsh
2 # zk.zsh -- zettelkästen/wiki/static website helper/generator
3 # Copyright (C) 2022 Sergey Matveev <stargrave@stargrave.org>
4
5 set -e
6
7 usage() {
8     cat >&2 <<EOF
9 Usage:
10   \$ $0:t links PAGE
11     Print PAGE's links
12   \$ $0:t backs PAGE
13     Print PAGE's backlinks
14   \$ $0:t htmls DIR
15     Generate HTMLs in DIR
16 EOF
17     exit 1
18 }
19
20 [[ $# -eq 2 ]] || usage
21
22 setopt GLOB_STAR_SHORT
23 zmodload -F zsh/stat b:zstat
24 typeset -A pages
25 for p (**(.)) {
26     zstat -A reply -F "%F %T" +mtime $p
27     pages[$p]=${reply[1]}
28 }
29 typeset -a cats
30 for p (**(/)) cats=($p $cats)
31
32 zmodload zsh/mapfile
33 zmodload -F zsh/files b:zf_mkdir
34 typeset -A links
35 typeset -A backs
36 typeset -aU ws
37 for p (${(k)pages}) {
38     ws=()
39     for w (${=mapfile[$p]}) {
40         [[ $w =~ "\[([^] ]+)\]" ]] || continue
41         w=${match[1]}
42         [[ ${pages[$w]} ]] || {
43             [[ $ZK_SHOW_MISSING ]] && print "missing $w"
44             continue
45         }
46         ws=($ws $w)
47     }
48     [[ $ws ]] && links[$p]=${(j: :)ws}
49 }
50 unset ws
51 for p ws (${(kv)links}) {
52     for w (${=ws}) backs[$w]="$p ${backs[$w]}"
53 }
54 for p ws (${(kv)backs}) backs[$p]=${(j: :)${(u)=ws}}
55
56 getrel() {
57     # nearly the copy-paste of Functions/Misc/relative
58     local dst=$2:a
59     local src=$1:h:a
60     local -a cur abs
61     cur=(${(s:/:)src})
62     abs=(${(s:/:)dst:h} $dst:t)
63     integer i=1
64     while [[ i -le $#abs && $abs[i] == $cur[i] ]] ; do
65         ((++i > $#cur)) && {
66             REPLY=${(j:/:)abs[i,-1]}
67             return
68         }
69     done
70     src=${(j:/:)cur[i,-1]/*/..}
71     dst=${(j:/:)abs[i,-1]}
72     REPLY=$src${dst:+/$dst}
73 }
74
75 genHTML() {
76     local page=$1
77     local data p
78     [[ $# -eq 1 ]] && data=${mapfile[$page]} || data=$2
79     data=${data//&/&amp;}
80     data=${data//</&lt;}
81     data=${data//>/&gt;}
82     local _links=(${(oi)=links[$page]})
83     for p ($_links) {
84         getrel $page $p
85         data="${data//\[${p}\]/<a href=\"${REPLY}.html\">[$p]</a>}"
86     }
87     print -r "<\!DOCTYPE html>
88 <html><head><title>$page (${pages[$page]})</title></head><body><pre>
89 $data
90 </pre>"
91     if [[ $_links ]]; then
92         print "<hr/>Links:<ul>"
93         for p ($_links) {
94             getrel $page $p
95             print "<li><a href=\"${REPLY}.html\">$p</a> <sup>${pages[$p]}</sup></li>"
96         }
97         print "</ul>"
98     fi
99     local bs=(${(oi)=${backs[$page]}})
100     if [[ $bs ]]; then
101         print "<hr/>Backlinks:<ul>"
102         for p ($bs) {
103             getrel $page $p
104             print "<li><a href=\"${REPLY}.html\">$p</a> <sup>${pages[$p]}</sup></li>"
105         }
106         print "</ul>"
107     fi
108     print "</body></html>"
109 }
110
111 zmodload -F zsh/datetime b:strftime
112 strftime -s now "%F %T"
113
114 genIndex() {
115     local p
116     local entries=()
117     local _links=()
118     typeset -aU cats=()
119     local curdepth=${#${(s:/:)1}}
120     (( curdepth = curdepth + 1 ))
121     for p (${(oi)${(k)pages[(I)$1*]}}) {
122         [[ $p =~ "/Index$" ]] && continue
123         case ${#${(As:/:)p}} in
124         ($curdepth) _links=($p $_links) ;;
125         ( $(( $curdepth + 1 )) ) cats=(${1}${${p#$1}%%/*} $cats) ;;
126         (*) continue ;;
127         esac
128     }
129     for p (${(oi)_links}) entries=($entries "[$p] (${pages[$p]})")
130     if [[ $cats ]]; then
131         entries=($entries "\nSubdirectories:\n")
132         for p (${(oi)cats}) {
133             entries=($entries "[$p/Index]")
134             _links=($p/Index $_links)
135         }
136     fi
137     links[${1}Index]=${(j: :)_links}
138     genHTML ${1}Index ${(F)entries}
139 }
140
141 case $1 in
142 (links) for w (${(oi)=${links[$2]}}) print $w ;;
143 (backs) for w (${(oi)=${backs[$2]}}) print $w ;;
144 (html) genHTML $2 ;;
145 (html-index) genIndex $2 ;;
146 (htmls)
147     for p (${(k)pages}) {
148         zf_mkdir -p $2/$p:h
149         genHTML $p > $2/$p.html
150         touch -r $p $2/$p.html
151     }
152     for p ($cats) pages[${p}/Index]=$now
153     pages[Index]=$now
154     for p ($cats) genIndex $p/ > $2/$p/Index.html
155     genIndex "" > $2/Index.html
156     for p ("" $cats) touch -d ${now/ /T} $2/$p/Index.html
157     ;;
158 (*) usage ;;
159 esac