bm-migrate (3220 bytes)
1 #!/bin/sh 2 3 # bm-migrate: convert a bookmark file from the old sbm format 4 # 5 # URL description | tag tag 6 # 7 # to the tab separated one bm uses now 8 # 9 # URL<tab>description<tab>tag tag 10 # 11 # The URL is the first word, the tags are whatever follows the last " | ", and 12 # a line without " | " has no tags. Comments, blank lines and lines that already 13 # contain a tab are passed through, so converting twice changes nothing. 14 15 usage () { 16 cat <<'EOF' 17 usage: bm-migrate [-n] [file] 18 file the bookmark file; defaults to $BOOKMARKS, then to 19 $XDG_DATA_HOME/sbm/bookmarks. "-" converts stdin to stdout. 20 -n print the converted file instead of replacing it 21 The original is kept as <file>.bak (or .bak.1, .bak.2, ... if that exists). 22 EOF 23 } 24 25 die () { 26 printf 'bm-migrate: %s\n' "$*" >&2 27 exit 1 28 } 29 30 convert () { 31 awk ' 32 /\t/ || /^#/ || NF == 0 { print; next } 33 { 34 url = $1 35 desc = $0 36 sub(/^[ ]*[^ ]+[ ]*/, "", desc) 37 tags = "" 38 if (match(desc, / \|[^|]*$/)) { 39 tags = substr(desc, RSTART + 2) 40 desc = substr(desc, 1, RSTART - 1) 41 } 42 gsub(/^ +| +$/, "", desc) 43 gsub(/^ +| +$/, "", tags) 44 printf "%s\t%s\t%s\n", url, desc, tags 45 }' "$@" 46 } 47 48 # oldlines <file>: how many lines are still in the old format. 49 oldlines () { 50 awk '!/\t/ && !/^#/ && NF { n++ } END { print n + 0 }' "$1" 51 } 52 53 dryrun= 54 while [ $# -gt 0 ]; do 55 case $1 in 56 -h|--help) usage; exit 0 ;; 57 -n|--dry-run) dryrun=1 ;; 58 -) break ;; 59 -*) 60 printf 'bm-migrate: unknown option: %s\n' "$1" >&2 61 usage >&2 62 exit 2 63 ;; 64 *) break ;; 65 esac 66 shift 67 done 68 if [ $# -gt 1 ]; then 69 printf 'bm-migrate: one file at a time\n' >&2 70 exit 2 71 fi 72 73 file=${1:-${BOOKMARKS:-${XDG_DATA_HOME:-$HOME/.local/share}/sbm/bookmarks}} 74 75 if [ "$file" = - ]; then 76 convert 77 exit 78 fi 79 80 [ -f "$file" ] || die "no such file: $file" 81 [ -r "$file" ] || die "cannot read $file" 82 83 if [ -n "$dryrun" ]; then 84 convert "$file" 85 exit 86 fi 87 88 count=$(oldlines "$file") 89 if [ "$count" -eq 0 ]; then 90 printf 'nothing to migrate in %s\n' "$file" 91 exit 0 92 fi 93 94 # Never overwrite an earlier backup: it may be the only copy of the original. 95 backup=$file.bak 96 n=0 97 while [ -e "$backup" ]; do 98 n=$((n + 1)) 99 backup=$file.bak.$n 100 done 101 cp -p "$file" "$backup" || die "could not back up to $backup" 102 103 # Through cat rather than mv, so that a symlinked bookmark file stays a symlink 104 # and keeps its permissions. 105 # Beside the file if that directory can be written to, else in $TMPDIR; 106 # noclobber (O_EXCL) keeps a planted file or symlink from being followed. 107 tmp= 108 for candidate in "$file.tmp.$$" "${TMPDIR:-/tmp}/sbm.$$.${file##*/}"; do 109 if (umask 077; set -C; : > "$candidate") 2>/dev/null; then 110 tmp=$candidate 111 break 112 fi 113 done 114 [ -n "$tmp" ] || die 'cannot create a temporary file' 115 trap 'rm -f "$tmp"' EXIT INT TERM 116 if ! convert "$backup" > "$tmp" || ! cat "$tmp" > "$file"; then 117 die "could not rewrite $file; the original is in $backup" 118 fi 119 120 printf 'migrated %d lines in %s (original kept as %s)\n' "$count" "$file" "$backup"