README (16043 bytes)
1 Manage bookmarks with dmenu and a basic file. 2 3 The file is Tab Separated Values, one bookmark per line: 4 5 URL<tab>description<tab>tag tag tag 6 7 For example: 8 9 https://equwal.com Spenser Truex's website. users 10 11 Lines starting with # are ignored. bm is one way to work with the file; 12 cut, grep, awk and your editor are others. 13 14 == Design == 15 bm owns the bookmark file and talks to the menu. Everything else is a small 16 filter that reads or writes bookmark lines, so the pieces combine like any 17 other Unix tools, and you install only the ones you use: 18 19 bm pick, open, copy, add, edit, delete, list, merge 20 bm-import browser bookmarks -> bookmark lines 21 bm-check bookmark lines -> the dead links among them 22 bm-html bookmark lines -> a searchable web page 23 bm-migrate the old sbm file format -> bookmark lines 24 bm-title a URL -> the page's title 25 bm-page a URL -> the page as text 26 bm-commit commit the bookmark file to git 27 bm-watch browser bookmarks -> bm, now and after each change 28 bm-sync the bookmark file <-> your other devices 29 30 bm-import bookmarks.html | bm --merge 31 bm --tag code --list | bm-check - 32 bm --list | grep -i wiki | bm-html -t Wikis - > wikis.html 33 34 Everything is POSIX sh and POSIX utilities: no bash, no mktemp, no GNU 35 options. make check runs shellcheck -s sh over all of it. Where the usual 36 tool is not in POSIX, the script says what stands in for it: noclobber for 37 mktemp, a FIFO of tokens for xargs -P, dd for head -c, awk's srand() for 38 date +%s. 39 40 == Requirements == 41 dmenu (or fzf), one of xclip, xsel or wl-clipboard, sh, awk, make. 42 curl for bm-title, bm-page, bm-check and bm-sync, jq for bm-import of 43 Chromium files, git for bm-commit. 44 45 == Install == 46 Edit TOOLS in config.mk to choose what gets installed, then 47 48 make install # to /usr/local/bin 49 make PREFIX="$HOME/.local" install 50 51 Each release on GitHub also has an installer for Windows and a portable app 52 for macOS. See Windows and macOS below. 53 54 == Setup == 55 Nothing is required. Bookmarks are kept in ~/.local/share/sbm/bookmarks 56 ($XDG_DATA_HOME is honoured) and the file is created on first use. 57 58 Tag choices are read from ~/.local/share/sbm/usertags: 59 60 <tag><space>|<space><description><newline> 61 62 Start from the included example, or from nothing: a tag typed into the tag 63 menu that is not in the file yet is added to it. 64 65 mkdir -p ~/.local/share/sbm 66 cp usertags engines ~/.local/share/sbm/ 67 68 == Usage == 69 bm browse (see below) 70 bm -o, --open open a bookmark, an address, or a web search 71 (-p, --plumb is the same) 72 bm -c, --copy copy a bookmark's URL to the clipboard 73 bm --print print a bookmark's URL 74 bm -a, --add [url] add a bookmark 75 bm -e, --edit open the file in $VISUAL/$EDITOR at a bookmark 76 bm -d, --delete delete a bookmark 77 bm -l, --list print the bookmarks 78 bm -m, --merge add the bookmark lines on stdin, skipping known ones 79 -t, --tag TAG restrict any of the above to one tag 80 -S, --sort ORDER used (default), recent, url, desc or tag 81 82 Bind "bm" and "bm -a" to keys in your window manager. 83 84 == Browsing == 85 Plain "bm" lists the bookmarks. Pick one and a second menu shows it in full 86 (dmenu cannot preview while you scroll, so this is its preview) and asks what 87 to do: 88 89 open 90 copy 91 edit 92 delete 93 https://suckless.org/ 94 software that sucks less 95 tags: code org 96 used: 12 97 98 Closing the second menu goes back to the list, closing the list leaves. 99 [add] at the top of the list adds a bookmark. 100 101 With fzf (in a terminal, with no display, or SBM_MENU=fzf) it is one screen: 102 the bookmark under the cursor is previewed live, and keys do the rest. With 103 bm-page installed, the preview also shows the text of the page: its title, 104 then its main part. bm-page fetches the page with curl (5 second limit, at 105 most 512k; SBM_FETCH=0 turns it off). make install also adds "sbm" to the 106 app menu of a Linux desktop: it opens bm with fzf in a terminal. 107 108 ENTER open ctrl-a add ctrl-s next sort order 109 ctrl-y copy ctrl-d delete (asks) 110 ESC leave ctrl-e edit 111 112 To see the bookmarks of a tag, type the tag. fzf searches the tags too. 113 114 == Web search == 115 In the list, and in "bm -o", text that is not a bookmark is not wasted: 116 117 suckless.org/dwm one word with a dot or "://": opened as an address 118 posix sh printf anything else: searched for on the web 119 w dynamic menu first word is a keyword: that engine is used 120 121 So one key gives you bookmarks first and the web behind them. The default 122 engine is DuckDuckGo; set another with SBM_SEARCH='https://.../?q=%s'. 123 Keywords are read from ~/.local/share/sbm/engines: 124 125 <keyword><space>|<space><url with %s><newline> 126 127 See the included engines file. 128 129 == Adding == 130 Without a url, bm offers the X11 primary selection, or the clipboard, when 131 it holds a URL. With bm-title installed the description defaults to the 132 page's title (curl, 3 second limit; SBM_FETCH=0 turns it off). In dmenu the 133 default is the one entry in the menu: Return accepts it, Shift-Return sends 134 exactly what you typed. 135 136 Tags are chosen one at a time until "done" is picked or the menu is closed 137 (fzf: TAB marks, ENTER confirms). Typing a tag that does not exist creates it. 138 139 A URL that is already bookmarked is refused (exit status 3). URLs are 140 compared without their scheme, a leading www. and trailing slashes. 141 142 == Sorting == 143 used most opened or copied first, then by url (the default) 144 recent most recently added first 145 url, desc, tag 146 147 Set a default with SBM_SORT. Use counts live in "$BOOKMARKS.usage", so the 148 bookmark file itself stays a plain list. 149 150 == A web page of your bookmarks == 151 bm-html > ~/public_html/bookmarks.html 152 153 One self-contained file: no server side, nothing loaded from elsewhere. 154 Bookmarks are listed under each of their tags, with the tags linked at the 155 top. A dozen lines of inline script add a filter box; without script the page 156 is still the complete list, and Ctrl-F works. page.html?q=words starts out 157 filtered, so the page can be a search keyword in your browser, or in bm: 158 159 b | https://example.org/bookmarks.html?q=%s 160 161 Links that would run script (javascript:, data:) are never written. 162 163 == Importing == 164 bm-import bookmarks.html | bm --merge # the HTML export of any browser 165 bm-import vivaldi | bm --merge # or chrome, brave, edge, chromium 166 bm-import path/to/Bookmarks | bm --merge # a Chromium JSON file (needs jq) 167 168 A browser name imports the bookmarks of all profiles of that browser. The 169 profiles are in ~/.config, in ~/Library/Application Support on macOS, or in 170 %LOCALAPPDATA% on Windows. Folder names become tags ("Dev Tools" becomes 171 dev-tools). bm --merge skips what you already have, so importing twice adds 172 nothing. To sync new browser bookmarks into bm, run the same command again. 173 Look before you merge: bm-import prints plain bookmark lines. 174 175 bm-watch does this import at once, and again each time that the bookmark 176 file of a profile changes. It only adds bookmarks, and it never writes to 177 the browser: 178 179 bm-watch brave & 180 181 == Dead links == 182 bm-check 183 184 prints status<tab>url for links that fail, and status<tab>url<tab>new-url for 185 ones that moved for good. It exits 1 if it printed anything. 186 187 == History and sync with git == 188 With bm-commit installed, every change bm makes is committed, provided the 189 directory holding the bookmark file is a git repository of its own. It never 190 pushes or pulls. 191 192 cd ~/.local/share/sbm && git init 193 printf '%s\n' '*.usage' '*.bak*' > .gitignore 194 195 A file that merely sits inside a bigger repository (your dotfiles) is left 196 alone unless SBM_GIT=1. SBM_GIT=0 turns commits off. 197 198 == Sync == 199 bm-sync keeps the bookmark file the same on all your computers, the sbm app 200 for Android and the sbm add-on for Firefox and Chrome. 201 202 bm-sync login # once on each computer: email and password 203 bm-sync # sync now 204 205 When bm-sync is installed and signed in, bm syncs after each change, in the 206 background. Bookmarks that you add or remove on one device are added or 207 removed on the others. The file stays a plain file, and bm works without a 208 network. The sign-in token is kept in ~/.config/sbm/sync. 209 210 An empty or missing bookmark file removes nothing on the other devices: 211 bm-sync gets all the bookmarks back from the server. 212 213 bm and bm-sync lock the file for each change, with the directory 214 $BOOKMARKS.lock, so that neither writes over a change of the other. bm -e 215 holds the lock until the editor ends; bm-sync waits for it, and bm syncs 216 the edit after. A lock left by a process that is gone is taken over. 217 218 The server is sbm-sync (https://github.com/equwal/sbm-sync). bm-sync uses 219 https://sbmsync.com unless you give another server: create an account 220 there. The server is free software, so you can run your own: 221 222 bm-sync login https://sbm.example.org 223 224 The sbm app for Android is at https://github.com/equwal/sbm-android. A demo of 225 sync between bm and the browser add-on (80 seconds): 226 https://github.com/equwal/sbm-sync#sbm-sync 227 228 == Migrating from the old format == 229 Earlier versions wrote "URL description | tag tag". 230 231 bm-migrate # $BOOKMARKS, or the default file 232 bm-migrate -n file # only show what it would become 233 bm-migrate - < old > new # as a filter 234 235 The original is kept as .bak, and an existing .bak is never overwritten. 236 237 == Configuration == 238 All through the environment, all optional. 239 240 BOOKMARKS bookmark file 241 USERTAGS tag choices file 242 DMENULINES lines shown by dmenu (default 15) 243 SBM_MENU dmenu, fzf, or a custom command 244 SBM_FZF_OPTS extra options for every fzf call 245 SBM_SORT default sort order 246 SBM_SEARCH default search engine, with %s for the query 247 SBM_ENGINES search keywords file 248 SBM_COPY command that reads the new clipboard contents from stdin 249 SBM_PASTE command that writes the clipboard contents to stdout 250 SBM_OPEN command that opens the URL given as its argument 251 SBM_LOCK_WAIT seconds that bm and bm-sync wait for the lock (default 10) 252 253 With a display bm uses dmenu, unless bm runs in a terminal: there it uses 254 fzf, whose preview shows the page. fzf is also used when dmenu is missing or 255 there is no X11 or Wayland display. SBM_MENU=dmenu keeps dmenu in a 256 terminal. A custom SBM_MENU is called with the 257 choices on stdin as one of: 258 259 <command> pick <prompt> print the chosen line, or the typed text 260 <command> multi <prompt> print every chosen line 261 <command> ask <prompt> print free text; stdin holds a default, if any 262 263 Printing nothing means the user cancelled. test/fakemenu is a small example. 264 265 == Windows == 266 bm runs under Cygwin. Get sbm-VERSION-setup.exe from a release on GitHub and 267 start it. The installer puts bm and the bm-* tools in /usr/local/bin. It 268 makes a Start menu entry and a desktop shortcut that open bm in a mintty 269 window, and Esc closes the window. The key of the desktop shortcut is 270 Ctrl+Alt+Shift+F24. Almost no keyboard has an F24 key, so keyboard firmware 271 can use this key combination without conflicts. 272 273 The installer imports the bookmarks of all profiles of the Chromium browsers 274 (Brave, Chrome, Chromium, Edge and Vivaldi). A Startup entry, sbm-watch, runs 275 bm-watch at each login, so new browser bookmarks come into bm a few seconds 276 after you add them. The import needs the jq package of Cygwin. 277 278 The installer also puts fzf.exe, the Windows build of fzf, in 279 /usr/local/libexec/sbm. Do not use the fzf package of Cygwin. That package is 280 version 0.11 from 2016, and it has no --preview and no --expect. The wrapper 281 /usr/local/bin/fzf lets fzf.exe run in mintty. /etc/profile.d/sbm.sh sets 282 SBM_OPEN, SBM_COPY, SBM_PASTE and SBM_FZF_OPTS for Cygwin. Settings in 283 ~/.bash_profile win. 284 285 For bm-title, bm-page, bm-check and bm-import, install the curl and jq 286 packages of Cygwin. The Windows builds of curl and jq do not understand 287 Cygwin paths, such as /dev/null. To remove sbm, use Apps in the Windows settings. The 288 uninstaller keeps your bookmarks. contrib/windows/build-installer.sh makes 289 the installer with NSIS. 290 291 == macOS == 292 Get sbm-VERSION.dmg from a release on GitHub, and drag sbm.app to 293 Applications. The app contains bm, the bm-* tools and fzf, so it needs no 294 other software. sbm.app opens bm in a Terminal window. The first time, macOS 295 asks if sbm can control Terminal. 296 297 The app has no signature from a registered developer, so macOS stops it the 298 first time. To open it, use Open Anyway in System Settings, Privacy & 299 Security. Or remove the quarantine attribute: 300 301 xattr -dr com.apple.quarantine /Applications/sbm.app 302 303 To open sbm with a key, make a shortcut with the action Open App in the 304 Shortcuts app, and give it a keyboard shortcut. macOS has function keys up 305 to F20. To use the tools in a shell, add these lines to ~/.zprofile: 306 307 PATH=/Applications/sbm.app/Contents/Resources/bin:$PATH 308 export SBM_OPEN=open SBM_COPY=pbcopy SBM_PASTE=pbpaste 309 310 contrib/macos/build-dmg.sh makes the image on a Mac. 311 312 == Tests == 313 make check 314 315 runs shellcheck, when installed, and test/run.sh, under sh. Run the tests 316 under another shell with SBM_SH=dash sh test/run.sh. They script dmenu, fzf 317 and curl, so they need no display and no network. 318 319 == Releases == 320 Set VERSION in config.mk. Tag the release as vVERSION. make dist makes the 321 tarball. contrib/windows/build-installer.sh makes the Windows installer. On 322 a Mac, contrib/macos/build-dmg.sh makes the image. Attach the three files to 323 the GitHub release of the tag. 324 325 == Credits == 326 Some ideas taken from Karl Voit: 327 https://karl-voit.at/2022/01/29/How-to-Use-Tags/ 328 329 == More projects == 330 - SubRead (https://subread.space/): read along with an audiobook, in the browser. 331 Also for Android (https://github.com/equwal/subread-android/releases/latest), 332 for YouTube (https://github.com/equwal/subread-extension/releases/latest) 333 and for KOReader (https://github.com/equwal/subread.koplugin). 334 - SubRead Overlay (https://github.com/equwal/subread-overlay/releases/latest): subtitle lines over any Android media player. 335 - SubRead Dictionary (https://github.com/equwal/subread-dictionary/releases/latest): a pop-up dictionary for Android that reads Yomitan dictionaries. 336 - SubRead Anki (https://github.com/equwal/subread-anki): one tap makes an Anki card from any Android app. 337 - Subrep (https://github.com/equwal/subrep-android/releases/latest): live captions of the sound of your phone. 338 - Book Simulator (https://booksimulator.com/): a reading room for Aozora Bunko and Project Gutenberg books. 339 - honjimaku.com (https://honjimaku.com/): subtitles for Japanese audiobooks. 340 - sbm Sync (https://sbmsync.com/): your bookmarks, the same on every device, 341 with sbm (https://github.com/equwal/sbm) for dmenu, 342 sbm for Android (https://github.com/equwal/sbm-android/releases/latest) 343 and the sbm add-on (https://github.com/equwal/sbm-extension/releases/latest) for Firefox and Chrome. 344 - Rebind (https://github.com/equwal/rebind/releases): remap the hardware buttons of e-ink readers and Android, 345 with Ink Recents (https://github.com/equwal/ink-recents/releases/latest), 346 Ink Dim (https://github.com/equwal/ink-dim/releases/latest) 347 and Ink Update (https://github.com/equwal/ink-update/releases/latest). 348 - dickt.store (https://dickt.store/): language-learning tools, flashcards and web toys. 349 - hentaibun.online (https://hentaibun.online/): learn kanbun and kobun. 350 - Recently Written (https://recentlywritten.com/): the blog, and a list of all projects (https://recentlywritten.com/projects.html). 351 352 == License == 353 AGPL-3.0: see LICENSE. If you run a changed version for other people over a 354 network, give them its source code. 355 356 If sbm is useful to you, you can support it on Ko-fi: 357 https://ko-fi.com/truex