Recently Written · git

sbm

dmenu bookmarks. Instantly fuzzy search thousands of bookmarks and plumb them. LOOKING FOR SUCKLESS SOFTWARE EDITION? GO TO sbm-suckless INSTEAD

git clone https://github.com/equwal/sbm

Log | Files | Refs


bm-html (3948 bytes)

1 #!/bin/sh
2 
3 # bm-html: print the bookmarks as one self-contained web page.
4 #
5 #     bm-html > ~/public_html/bookmarks.html
6 #     bm --tag code --list | bm-html -t 'Code' - > code.html
7 #
8 # Without a file the default bookmark file is read; "-" is stdin.
9 #
10 # The page lists the bookmarks under each of their tags, with links to the
11 # tags at the top. A few lines of script add a filter box; without script the
12 # page is still the complete list. page.html?q=words starts out filtered, so
13 # the page can serve as a search engine keyword in a browser, or in bm's own
14 # engines file:    b | https://example.org/bookmarks.html?q=%s
15 
16 usage () {
17     printf 'usage: bm-html [-t title] [file | -] ...\n'
18 }
19 
20 title=Bookmarks
21 while [ $# -gt 0 ]; do
22     case $1 in
23         -h|--help) usage; exit 0 ;;
24         -t)
25             [ $# -ge 2 ] || { usage >&2; exit 2; }
26             title=$2
27             shift
28             ;;
29         --) shift; break ;;
30         -) break ;;
31         -?*) usage >&2; exit 2 ;;
32         *) break ;;
33     esac
34     shift
35 done
36 [ $# -gt 0 ] || set -- "${BOOKMARKS:-${XDG_DATA_HOME:-$HOME/.local/share}/sbm/bookmarks}"
37 
38 cat -- "$@" | LC_ALL=C sort -f -t'	' -k2,2 -k1,1 | SBM_TITLE=$title awk -F'\t' '
39 function esc(s) {
40     gsub(/&/, "\\&amp;", s); gsub(/</, "\\&lt;", s); gsub(/>/, "\\&gt;", s)
41     gsub(/"/, "\\&quot;", s)
42     return s
43 }
44 function host(u) {
45     sub(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//, "", u)
46     sub(/[\/?#].*/, "", u)
47     return u
48 }
49 NF == 0 || /^#/ { next }
50 # Never publish a link that would run script.
51 tolower($1) ~ /^[ \t]*(javascript|data|vbscript):/ { next }
52 {
53     total++
54     item = sprintf("<li><a href=\"%s\">%s</a> <small>%s</small>", esc($1), \
55                    esc($2 == "" ? $1 : $2), esc(host($1)))
56     n = split($3, tag, " ")
57     for (i = 1; i <= n; i++)
58         item = item sprintf(" <a class=t href=\"#%s\">%s</a>", esc(tag[i]), esc(tag[i]))
59     if (n == 0) { n = 1; tag[1] = "" }
60     for (i = 1; i <= n; i++) {
61         if (!(tag[i] in items)) names[++count] = tag[i]
62         items[tag[i]] = items[tag[i]] item "\n"
63     }
64 }
65 END {
66     # Insertion sort: tags by name, the untagged last.
67     for (i = 2; i <= count; i++) {
68         name = names[i]
69         for (j = i - 1; j > 0 && (names[j] == "" || (name != "" && names[j] > name)); j--)
70             names[j + 1] = names[j]
71         names[j + 1] = name
72     }
73     t = esc(ENVIRON["SBM_TITLE"])
74     print "<!doctype html>"
75     print "<meta charset=utf-8>"
76     print "<meta name=viewport content=\"width=device-width, initial-scale=1\">"
77     print "<title>" t "</title>"
78     print "<style>"
79     print "body{max-width:50em;margin:1em auto;padding:0 1em;font:1em/1.5 sans-serif}"
80     print "#q{width:100%;box-sizing:border-box;font:inherit;padding:.3em}"
81     print "ul{padding-left:1.2em}small,.t{color:#777;font-size:.85em}.t{margin-left:.3em}"
82     print "</style>"
83     print "<h1>" t "</h1>"
84     print "<input id=q type=search placeholder=\"filter " total " bookmarks\" hidden>"
85     printf "<p>"
86     for (i = 1; i <= count; i++)
87         if (names[i] != "")
88             printf "<a href=\"#%s\">%s</a> ", esc(names[i]), esc(names[i])
89     print "</p>"
90     for (i = 1; i <= count; i++) {
91         name = names[i] == "" ? "untagged" : names[i]
92         printf "<section><h2 id=\"%s\">%s</h2>\n<ul>\n%s</ul></section>\n", \
93                esc(name), esc(name), items[names[i]]
94     }
95     print "<script>"
96     print "var q=document.getElementById(\"q\");q.hidden=false;"
97     print "function f(){var w=q.value.toLowerCase().split(/\\s+/);"
98     print "document.querySelectorAll(\"section\").forEach(function(s){var n=0;"
99     print "s.querySelectorAll(\"li\").forEach(function(l){"
100     print "var t=(l.textContent+\" \"+l.firstChild.href).toLowerCase();"
101     print "var ok=w.every(function(x){return t.indexOf(x)>=0});l.hidden=!ok;if(ok)n++});"
102     print "s.hidden=!n})}"
103     print "q.oninput=f;q.value=new URLSearchParams(location.search).get(\"q\")||\"\";f();"
104     print "</script>"
105 }'