my xfce4 dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
15 KiB

3 years ago
  1. #!/bin/sh
  2. #
  3. # This script does not have a stable API.
  4. _gitstatus_install_daemon_found() {
  5. local installed="$1"
  6. shift
  7. [ $# = 0 ] || "$@" "$daemon" "$version" "$installed"
  8. }
  9. _gitstatus_install_main() {
  10. if [ -n "${ZSH_VERSION:-}" ]; then
  11. emulate -L sh -o no_unset
  12. else
  13. set -u
  14. fi
  15. local argv1="$1"
  16. shift
  17. local no_check= no_install= uname_s= uname_m= gitstatus_dir= dl_status= e=
  18. local opt= OPTARG= OPTIND=1
  19. while getopts ':s:m:d:p:e:fnh' opt "$@"; do
  20. case "$opt" in
  21. h)
  22. command cat <<\END
  23. Usage: install [-s KERNEL] [-m ARCH] [-d DIR] [-p CMD] [-e ERRFD] [-f|-n] [-- CMD [ARG]...]
  24. If positional arguments are specified, call this on success:
  25. CMD [ARG]... DAEMON VERSION INSTALLED
  26. DAEMON is path to gitstatusd. VERSION is a glob pattern for the
  27. version this daemon should support; it's supposed to be passed as
  28. -G to gitstatusd. INSTALLED is 1 if gitstatusd has just been
  29. downloaded and 0 otherwise.
  30. Options:
  31. -s KERNEL use this instead of lowercase `uname -s`
  32. -m ARCH use this instead of lowercase `uname -m`
  33. -d DIR use this instead of `dirname "$0"`
  34. -p CMD eval this every second while downloading gitstatusd
  35. -e ERRFD write error messages to this file descriptor
  36. -f download gitstatusd even if there is one locally
  37. -n do not download gitstatusd (fail instead)
  38. END
  39. return
  40. ;;
  41. n)
  42. if [ -n "$no_install" ]; then
  43. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  44. return 1
  45. fi
  46. no_install=1
  47. ;;
  48. f)
  49. if [ -n "$no_check" ]; then
  50. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  51. return 1
  52. fi
  53. no_check=1
  54. ;;
  55. d)
  56. if [ -n "$gitstatus_dir" ]; then
  57. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  58. return 1
  59. fi
  60. if [ -z "$OPTARG" ]; then
  61. >&2 echo "[error] incorrect value of -$opt: $OPTARG"
  62. return 1
  63. fi
  64. gitstatus_dir="$OPTARG"
  65. ;;
  66. p)
  67. if [ -n "$dl_status" ]; then
  68. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  69. return 1
  70. fi
  71. if [ -z "$OPTARG" ]; then
  72. >&2 echo "[error] incorrect value of -$opt: $OPTARG"
  73. return 1
  74. fi
  75. dl_status="$OPTARG"
  76. ;;
  77. e)
  78. if [ -n "$e" ]; then
  79. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  80. return 1
  81. fi
  82. if [ -z "$OPTARG" ]; then
  83. >&2 echo "[error] incorrect value of -$opt: $OPTARG"
  84. return 1
  85. fi
  86. e="$OPTARG"
  87. ;;
  88. m)
  89. if [ -n "$uname_m" ]; then
  90. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  91. return 1
  92. fi
  93. if [ -z "$OPTARG" ]; then
  94. >&2 echo "[error] incorrect value of -$opt: $OPTARG"
  95. return 1
  96. fi
  97. uname_m="$OPTARG"
  98. ;;
  99. s)
  100. if [ -n "$uname_s" ]; then
  101. >&2 echo "[gitstatus] error: duplicate option: -$opt"
  102. return 1
  103. fi
  104. if [ -z "$OPTARG" ]; then
  105. >&2 echo "[error] incorrect value of -$opt: $OPTARG"
  106. return 1
  107. fi
  108. uname_s="$OPTARG"
  109. ;;
  110. \?) >&2 echo "[gitstatus] error: invalid option: -$OPTARG" ; return 1;;
  111. :) >&2 echo "[gitstatus] error: missing required argument: -$OPTARG"; return 1;;
  112. *) >&2 echo "[gitstatus] internal error: unhandled option: -$opt" ; return 1;;
  113. esac
  114. done
  115. shift "$((OPTIND - 1))"
  116. : "${e:=2}"
  117. : "${gitstatus_dir:=$argv1}"
  118. if [ -n "$no_check" -a -n "$no_install" ]; then
  119. >&2 echo "[gitstatus] error: incompatible options: -f, -n"
  120. return 1
  121. fi
  122. if [ -z "$uname_s" ]; then
  123. uname_s="$(command uname -s)" || return
  124. uname_s="$(printf '%s' "$uname_s" | command tr '[A-Z]' '[a-z]')" || return
  125. fi
  126. if [ -z "$uname_m" ]; then
  127. uname_m="$(command uname -m)" || return
  128. uname_m="$(printf '%s' "$uname_m" | command tr '[A-Z]' '[a-z]')" || return
  129. fi
  130. local daemon="${GITSTATUS_DAEMON:-}"
  131. local cache_dir="${GITSTATUS_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/gitstatus}"
  132. if [ -z "$no_check" ]; then
  133. if [ -n "${daemon##/*}" ]; then
  134. >&2 echo "[gitstatus] error: GITSTATUS_DAEMON is not absolute path: $daemon"
  135. return 1
  136. fi
  137. if [ -z "$daemon" -a -e "$gitstatus_dir"/usrbin/gitstatusd ]; then
  138. daemon="$gitstatus_dir"/usrbin/gitstatusd
  139. fi
  140. if [ -n "$daemon" ]; then
  141. local gitstatus_version= libgit2_version=
  142. if ! . "$gitstatus_dir"/build.info; then
  143. >&2 echo "[gitstatus] internal error: failed to source build.info"
  144. return 1
  145. fi
  146. if [ -z "$gitstatus_version" ]; then
  147. >&2 echo "[gitstatus] internal error: empty gitstatus_version in build.info"
  148. return 1
  149. fi
  150. local version="$gitstatus_version"
  151. _gitstatus_install_daemon_found 0 "$@"
  152. return
  153. fi
  154. fi
  155. while IFS= read -r line; do
  156. line="${line###*}"
  157. [ -n "$line" ] || continue
  158. local uname_s_glob= uname_m_glob= file= version= sha256=
  159. eval "$line" || return
  160. if [ -z "$uname_s_glob" -o \
  161. -z "$uname_m_glob" -o \
  162. -z "$file" -o \
  163. -z "$version" -o \
  164. -z "$sha256" ]; then
  165. >&2 echo "[gitstatus] internal error: invalid install.info line: $line"
  166. return 1
  167. fi
  168. case "$uname_s" in
  169. $uname_s_glob) ;;
  170. *) continue;;
  171. esac
  172. case "$uname_m" in
  173. $uname_m_glob) ;;
  174. *) continue;;
  175. esac
  176. # Found a match. The while loop will terminate during this iteration.
  177. if [ -z "$no_check" ]; then
  178. # Check if a suitable gitstatusd already exists.
  179. local daemon="$gitstatus_dir"/usrbin/"$file"
  180. if [ ! -e "$daemon" ]; then
  181. daemon="$cache_dir"/"$file"
  182. [ -e "$daemon" ] || daemon=
  183. fi
  184. if [ -n "$daemon" ]; then
  185. _gitstatus_install_daemon_found 0 "$@"
  186. return
  187. fi
  188. fi
  189. # No suitable gitstatusd exists. Need to download.
  190. if [ -n "$no_install" ]; then
  191. >&2 echo "[gitstatus] error: no gitstatusd found and installation is disabled"
  192. return 1
  193. fi
  194. local daemon="$cache_dir"/"$file"
  195. if [ -n "${cache_dir##/*}" ]; then
  196. >&2 echo "[gitstatus] error: GITSTATUS_CACHE_DIR is not absolute: $cache_dir"
  197. return 1
  198. fi
  199. if [ ! -d "$cache_dir" ] && ! mkdir -p -- "$cache_dir" || [ ! -w "$cache_dir" ]; then
  200. local dir="$cache_dir"
  201. while true; do
  202. if [ -e "$dir" ]; then
  203. if [ ! -d "$dir" ]; then
  204. >&"$e" printf 'Not a directory: \033[4;31m%s\033[0m\n' "$dir"
  205. >&"$e" printf '\n'
  206. >&"$e" printf 'Delete it, then restart your shell.\n'
  207. elif [ ! -w "$dir" ]; then
  208. >&"$e" printf 'Directory is not writable: \033[4;31m%s\033[0m\n' "$dir"
  209. >&"$e" printf '\n'
  210. >&"$e" printf 'Make it writable, then restart your shell.\n'
  211. fi
  212. break
  213. fi
  214. if [ "$dir" = / ] || [ "$dir" = . ]; then
  215. break
  216. fi
  217. dir="$(dirname -- "$dir")"
  218. done
  219. return 1
  220. fi
  221. local tmpdir
  222. if ! command -v mktemp >/dev/null 2>&1 ||
  223. ! tmpdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus-install.XXXXXXXXXX)"; then
  224. tmpdir="${TMPDIR:-/tmp}/gitstatus-install.tmp.$$"
  225. if ! mkdir -p -- "$tmpdir"; then
  226. local dir="${TMPDIR:-/tmp}"
  227. if [ -z "${TMPDIR:-}" ]; then
  228. local label='directory'
  229. else
  230. local label='directory (\033[1mTMPDIR\033[m)'
  231. fi
  232. if [ ! -e "$dir" ]; then
  233. >&"$e" printf 'Temporary '"$label"' does not exist: \033[4;31m%s\033[0m\n' "$dir"
  234. >&"$e" printf '\n'
  235. >&"$e" printf 'Create it, then restart your shell.\n'
  236. elif [ ! -d "$dir" ]; then
  237. >&"$e" printf 'Not a '"$label"': \033[4;31m%s\033[0m\n' "$dir"
  238. >&"$e" printf '\n'
  239. >&"$e" printf 'Make it a directory, then restart your shell.\n'
  240. elif [ ! -w "$dir" ]; then
  241. >&"$e" printf 'Temporary '"$label"' is not writable: \033[4;31m%s\033[0m\n' "$dir"
  242. >&"$e" printf '\n'
  243. >&"$e" printf 'Make it writable, then restart your shell.\n'
  244. fi
  245. return 1
  246. fi
  247. fi
  248. if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
  249. >&"$e" printf 'Please install \033[32mcurl\033[0m or \033[32mwget\033[0m, then restart your shell.\n'
  250. return 1
  251. fi
  252. (
  253. run_cmd() {
  254. command -v "$1" >/dev/null 2>/dev/null || return 127
  255. local trapped= pid die ret
  256. trap 'trapped=1' $sig
  257. # The only reason for suppressing stderr is that `curl -f` cannot be silenced:
  258. # `-s` doesn't work despite what the docs say.
  259. command "$@" 2>/dev/null &
  260. ret="$?"
  261. if [ "$ret" = 0 ]; then
  262. pid="$!"
  263. die="trap - $sig; kill -- $pid 2>/dev/null; wait -- $pid 2>/dev/null; exit 1"
  264. trap "$die" $sig
  265. [ -z "$trapped" ] || eval "$die"
  266. wait -- "$pid" 2>/dev/null
  267. ret="$?"
  268. fi
  269. trap - $sig
  270. [ -z "$trapped" ] || exit
  271. return "$ret"
  272. }
  273. check_sha256() {
  274. local data_file="$tmpdir"/"$1".tar.gz
  275. local hash_file="$tmpdir"/"$1".tar.gz.sha256
  276. local hash=
  277. {
  278. command -v shasum >/dev/null 2>/dev/null &&
  279. run_cmd shasum -b -a 256 -- "$data_file" >"$hash_file" </dev/null &&
  280. IFS= read -r hash <"$hash_file" &&
  281. hash="${hash%% *}" &&
  282. [ ${#hash} -eq 64 ]
  283. } || {
  284. command -v sha256sum >/dev/null 2>/dev/null &&
  285. run_cmd sha256sum -b -- "$data_file" >"$hash_file" </dev/null &&
  286. IFS= read -r hash <"$hash_file" &&
  287. hash="${hash%% *}" &&
  288. [ ${#hash} -eq 64 ]
  289. } || {
  290. # Note: sha256 can be from hashalot. It's incompatible.
  291. # Thankfully, it produces shorter output.
  292. command -v sha256 >/dev/null 2>/dev/null &&
  293. run_cmd sha256 -- "$data_file" >"$hash_file" </dev/null &&
  294. IFS= read -r hash <"$hash_file" &&
  295. hash="${hash##* }" &&
  296. [ ${#hash} -eq 64 ]
  297. } || {
  298. hash=
  299. }
  300. [ "$1" = 1 -a -z "$hash" -o "$hash" = "$sha256" ]
  301. }
  302. local url1="https://github.com/romkatv/gitstatus/releases/download/$version/$file.tar.gz"
  303. local url2="https://gitee.com/romkatv/gitstatus/raw/release-$version/release/$file.tar.gz"
  304. local sig='INT QUIT TERM ILL PIPE'
  305. fetch() {
  306. if [ "$1" != 1 ] && command -v sleep >/dev/null 2>/dev/null; then
  307. if ! run_cmd sleep "$1"; then
  308. echo -n >"$tmpdir"/"$1".status
  309. return 1
  310. fi
  311. fi
  312. local cmd part url ret
  313. for cmd in 'curl -kfsSL' 'wget -qO-' 'curl -q -kfsSL' 'wget --no-config -qO-'; do
  314. part=0
  315. while true; do
  316. if [ "$part" = 2 ]; then
  317. ret=1
  318. break
  319. elif [ "$part" = 0 ]; then
  320. url="$2"
  321. else
  322. url="$2"."$part"
  323. fi
  324. run_cmd $cmd -- "$url" >>"$tmpdir"/"$1".tar.gz
  325. ret="$?"
  326. [ "$ret" = 0 ] || break
  327. check_sha256 "$1" && break
  328. part=$((part+1))
  329. done
  330. [ "$ret" = 0 ] && break
  331. run_cmd rm -f -- "$tmpdir"/"$1".tar.gz && continue
  332. ret="$?"
  333. break
  334. done
  335. echo -n >"$tmpdir"/"$1".status
  336. return "$ret"
  337. }
  338. local trapped=
  339. trap 'trapped=1' $sig
  340. fetch 1 "$url1" &
  341. local pid1="$!"
  342. fetch 2 "$url2" &
  343. local pid2="$!"
  344. local die="trap - $sig; kill -- $pid1 $pid2 2>/dev/null; wait -- $pid1 $pid2 2>/dev/null; exit 1"
  345. trap "$die" $sig
  346. [ -z "$trapped" ] || eval "$die"
  347. local n=
  348. while true; do
  349. [ -z "$dl_status" ] || eval "$dl_status" || eval "$die"
  350. if command -v sleep >/dev/null 2>/dev/null; then
  351. command sleep 1
  352. elif command -v true >/dev/null 2>/dev/null; then
  353. command true
  354. fi
  355. if [ -n "$pid1" -a -e "$tmpdir"/1.status ]; then
  356. wait -- "$pid1" 2>/dev/null
  357. local ret="$?"
  358. pid1=
  359. if [ "$ret" = 0 ]; then
  360. if [ -n "$pid2" ]; then
  361. kill -- "$pid2" 2>/dev/null
  362. wait -- "$pid2" 2>/dev/null
  363. fi
  364. n=1
  365. break
  366. elif [ -z "$pid2" ]; then
  367. break
  368. else
  369. die="trap - $sig; kill -- $pid2 2>/dev/null; wait -- $pid2 2>/dev/null; exit 1"
  370. trap "$die" $sig
  371. fi
  372. elif [ -n "$pid2" -a -e "$tmpdir"/2.status ]; then
  373. wait -- "$pid2" 2>/dev/null
  374. local ret="$?"
  375. pid2=
  376. if [ "$ret" = 0 ]; then
  377. if [ -n "$pid1" ]; then
  378. kill -- "$pid1" 2>/dev/null
  379. wait -- "$pid1" 2>/dev/null
  380. fi
  381. n=2
  382. break
  383. elif [ -z "$pid1" ]; then
  384. break
  385. else
  386. die="trap - $sig; kill -- $pid1 2>/dev/null; wait -- $pid1 2>/dev/null; exit 1"
  387. trap "$die" $sig
  388. fi
  389. fi
  390. done
  391. trap - $sig
  392. if [ -z "$n" ]; then
  393. >&"$e" printf 'Failed to download \033[32m%s\033[0m from any mirror:\n' "$file"
  394. >&"$e" printf '\n'
  395. >&"$e" printf ' 1. \033[4m%s\033[0m\n' "$url1"
  396. >&"$e" printf ' 2. \033[4m%s\033[0m\n' "$url2"
  397. >&"$e" printf '\n'
  398. >&"$e" printf 'Check your internet connection, then restart your shell.\n'
  399. exit 1
  400. fi
  401. command tar -C "$tmpdir" -xzf "$tmpdir"/"$n".tar.gz || exit
  402. local tmpfile
  403. if ! command -v mktemp >/dev/null 2>&1 ||
  404. ! tmpfile="$(command mktemp "$cache_dir"/gitstatusd.XXXXXXXXXX)"; then
  405. tmpfile="$cache_dir"/gitstatusd.tmp.$$
  406. fi
  407. command mv -f -- "$tmpdir"/"$file" "$tmpfile" || exit
  408. command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
  409. command rm -f -- "$cache_dir"/"$file"
  410. command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
  411. command rm -f -- "$tmpfile"
  412. exit 1
  413. )
  414. local ret="$?"
  415. command rm -rf -- "$tmpdir"
  416. [ "$ret" = 0 ] || return
  417. _gitstatus_install_daemon_found 1 "$@"
  418. return
  419. done <"$gitstatus_dir"/install.info
  420. >&"$e" printf 'There is no prebuilt \033[32mgitstatusd\033[0m for \033[1m%s\033[0m.\n' "$uname_s $uname_m"
  421. >&"$e" printf '\n'
  422. >&"$e" printf 'See: \033[4mhttps://github.com/romkatv/gitstatus#compiling\033[0m\n'
  423. return 1
  424. }
  425. if [ -z "${0##*/*}" ]; then
  426. _gitstatus_install_main "${0%/*}" "$@"
  427. else
  428. _gitstatus_install_main . "$@"
  429. fi