dottum.sh 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #!/bin/bash
  2. shellfu import pretty
  3. _dottum__slstatus() {
  4. #
  5. # Print status of slpath $1
  6. #
  7. local slpath=$1
  8. local tlink=0
  9. local texis=0
  10. test -L "$slpath"; tlink=$?
  11. test -e "$slpath"; texis=$?
  12. case $tlink:$texis in
  13. 0:0) echo symlink ;;
  14. 0:1) echo broken ;;
  15. 1:0) echo nonlink ;;
  16. 1:1) echo nothing ;;
  17. *) warn "strange resiult: tlink=$tlink texis=$texis" ;;
  18. esac
  19. }
  20. dottum__lssv() {
  21. #
  22. # List sub-vaults
  23. #
  24. # Sub-vault is a sub-path of vault directory that contains items
  25. # that should be linked separately, to a similar $HOME subpath,
  26. # rather than linking the whole sub-tree. This is typical for
  27. # .config directory specified by XDG standard.
  28. #
  29. # For example, imagine vault like this:
  30. #
  31. # dotfiles/
  32. # ├── config
  33. # │   ├── dunst
  34. # │   │   └── dunstrc
  35. # │   └── uzbl
  36. # │      ├── config
  37. # │      └── style.css
  38. # ├── i3
  39. # │   └── config
  40. # ├── i3status.conf
  41. # └── vimrc
  42. #
  43. # Without sub-vault, the "config" would be linked as "$HOME/.config":
  44. #
  45. # $HOME/.config -> dotfiles/.config
  46. # $HOME/.i3 -> dotfiles/i3
  47. # $HOME/.i3status.conf -> dotfiles/i3status.conf
  48. # $HOME/.vimrc -> dotfiles/vimrc
  49. #
  50. # This is impractical in most cases, as normally you want to select
  51. # applications to link--some dotfiles could be specific to some
  52. # hosts or even private.
  53. #
  54. # Setting 'config' as sub-vault will tell mklinks that you don't
  55. # want to link this item directly but rather each of its items:
  56. #
  57. # $HOME/.config/dunst -> dotfiles/config/dunst
  58. # $HOME/.config/uzbl -> dotfiles/config/uzbl
  59. # $HOME/.i3 -> dotfiles/i3
  60. # $HOME/.i3status.conf -> dotfiles/i3status.conf
  61. # $HOME/.vimrc -> dotfiles/vimrc
  62. #
  63. # This way, you can have other items under "$HOME/.config" that
  64. # are either purely local or linked to another vault.
  65. #
  66. # Note that the subvault can be deeper than one level, i.e.
  67. # subvault "local/share" also works:
  68. #
  69. # $HOME/.local/share/klavaro -> dotfiles/local/share/klavaro
  70. #
  71. local sv
  72. test -n "${SubVaults[0]}" && {
  73. think "using sub-vaults from command line"
  74. for sv in "${SubVaults[@]}";
  75. do
  76. echo "$sv"
  77. done
  78. return
  79. }
  80. test -f "$Vault/dotvault/subvaults" && {
  81. think "reading subvaults from file: $Vault/dotvault/subvaults"
  82. grep . "$Vault/dotvault/subvaults" \
  83. | grep -v "^#.*"
  84. return
  85. }
  86. think "using hard-coded list of subvaults"
  87. echo config
  88. }
  89. dottum__lsvault() {
  90. #
  91. # List all vaults and subvaults, depth-first
  92. #
  93. {
  94. echo "$Vault"
  95. dottum__lssv \
  96. | while IFS= read -r sv;
  97. do
  98. test -e "$Vault/$sv" || {
  99. think "ignoring non-existent sub-vault: $sv"
  100. continue
  101. }
  102. test -d "$Vault/$sv" || {
  103. warn "ignoring non-directory sub-vault: $sv"
  104. continue
  105. }
  106. echo "$Vault/$sv"
  107. done
  108. } \
  109. | LC_ALL=C sort \
  110. | tac
  111. }
  112. dottum__istarget() {
  113. #
  114. # True if item $1 is possible target
  115. #
  116. # Item is obviously not a possible target if it's
  117. # a (sub-)vault.
  118. #
  119. # A less obvious case is when item is an intermediate directory
  120. # on a path leading to a sub-vault. Such item cannot be a target!
  121. #
  122. # For example: there are sub-vaults:
  123. #
  124. # foo
  125. # foo/bar/baz
  126. #
  127. # and a tree:
  128. #
  129. # foo
  130. # ├── A
  131. # └── bar
  132. # └── baz
  133. # └── B
  134. #
  135. # Items A and B could both be targets, but baz could not be!
  136. #
  137. #TODO: write an explanation
  138. #
  139. local item=$1
  140. debug -v item
  141. Verbose=false dottum__lsvault | grep -qxF "$item" && return 1
  142. Verbose=false dottum__lsvault | grep -qx "$item/.*" && return 1
  143. return 0
  144. }
  145. dottum__pfxpath() {
  146. #
  147. # Prefix paths with $1
  148. #
  149. # Just like `sed "s/^/$1/" but works with any characters
  150. # in $1 excepr newline, which is not allowed in paths..
  151. #
  152. local pfx="$1"
  153. local path
  154. while IFS= read -r path;
  155. do
  156. echo "$pfx$path"
  157. done
  158. }
  159. dottum__lstarget() {
  160. #
  161. # List potential items in vault $1
  162. #
  163. local vlt=$1
  164. debug -v vlt
  165. local item
  166. find "$vlt" -mindepth 1 -maxdepth 1 -printf "%P\n" \
  167. | dottum__pfxpath "$vlt/" \
  168. | grep -v "$Vault/dotvault" \
  169. | while IFS= read -r item;
  170. do
  171. dottum__istarget "$item" && echo "$item"
  172. done
  173. }
  174. dottum__make_links() {
  175. #
  176. # Create all links
  177. #
  178. local slpath
  179. local target
  180. local vlt
  181. local slstatus
  182. dottum__lsvault \
  183. | while IFS= read -r vlt;
  184. do
  185. dottum__lstarget "$vlt" \
  186. | while IFS= read -r target;
  187. do
  188. slpath=$(dottum__slpath "$target")
  189. slstatus=$(_dottum__slstatus "$slpath")
  190. debug -v slpath slstatus
  191. case $slstatus in
  192. nonlink)
  193. warn "skipping existing non-link: $slpath"
  194. continue
  195. ;;
  196. broken)
  197. if $ClobberLinks || $ClobberBroken; then
  198. think "removing existing broken link: $slpath"
  199. dottum__maybe rm "$slpath"
  200. else
  201. warn "skipping existing broken link: $slpath"
  202. continue
  203. fi
  204. ;;
  205. symlink)
  206. if $ClobberLinks; then
  207. think "removing existing link: $slpath"
  208. dottum__maybe rm "$slpath"
  209. else
  210. warn "skipping existing slpath: $slpath"
  211. continue
  212. fi
  213. ;;
  214. esac
  215. dottum__maybe ln -sr "$target" "$slpath"
  216. done
  217. done
  218. }
  219. dottum__explore() {
  220. #
  221. # Show what would be done
  222. #
  223. local vlt
  224. local tgt
  225. local tgts
  226. dottum__lsvault \
  227. | sort \
  228. | while IFS= read -r vlt;
  229. do
  230. tgts=$(dottum__lstarget "$vlt")
  231. test -n "$tgts" || continue
  232. if test "$vlt" == "$Vault";
  233. then
  234. echo "VAULT:$vlt:"
  235. else
  236. echo
  237. echo "SUBVAULT:$vlt"
  238. fi
  239. sort <<<"$tgts" \
  240. | while IFS= read -r tgt;
  241. do
  242. echo "${tgt}:$(dottum__slpath "$tgt")"
  243. done \
  244. | sed -e "s|:$HOME|:=> ~|" \
  245. | column -ts: \
  246. | sed -e "s/^/ /"
  247. done
  248. }
  249. dottum__maybe() {
  250. #
  251. # Maybe do things $@, maybe not (if in debug mode)
  252. #
  253. $PRETTY_DEBUG && { debug "MAYBE:$*"; return; }
  254. "$@"
  255. }
  256. dottum__slpath() {
  257. #
  258. # Print path where to create link for target $1
  259. #
  260. local item=$1
  261. echo "$HOME/.${item#$Vault/}"
  262. }