bootstrap-utils.sh 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/bin/sh
  2. #
  3. # bootstrap-utils.sh
  4. # Copyright (C) 2022 Kovid Goyal <kovid at kovidgoyal.net>
  5. #
  6. # Distributed under terms of the MIT license.
  7. #
  8. mv_files_and_dirs() {
  9. cwd="$PWD"
  10. cd "$1"
  11. command find . -type d -exec mkdir -p "$2/{}" ";"
  12. command find . -type l -exec sh -c "tgt=\$(command readlink -n \"{}\"); command ln -snf \"\$tgt\" \"$2/{}\"; command rm -f \"{}\"" ";"
  13. command find . -type f -exec mv "{}" "$2/{}" ";"
  14. cd "$cwd"
  15. }
  16. compile_terminfo() {
  17. tname=".terminfo"
  18. # Ensure the 78 dir is present
  19. if [ ! -f "$1/$tname/78/xterm-kitty" ]; then
  20. command mkdir -p "$1/$tname/78"
  21. command ln -sf "../x/xterm-kitty" "$1/$tname/78/xterm-kitty"
  22. fi
  23. if [ -e "/usr/share/misc/terminfo.cdb" ]; then
  24. # NetBSD requires this file, see https://github.com/kovidgoyal/kitty/issues/4622
  25. # Also compile terminfo using tic installed via pkgsrc,
  26. # so that programs that depend on the new version of ncurses automatically fall back to this one.
  27. if [ -x "/usr/pkg/bin/tic" ]; then
  28. /usr/pkg/bin/tic -x -o "$1/$tname" "$1/.terminfo/kitty.terminfo" 2>/dev/null
  29. fi
  30. if [ ! -e "$1/$tname/x/xterm-kitty" ]; then
  31. command ln -sf "../../.terminfo.cdb" "$1/$tname/x/xterm-kitty"
  32. fi
  33. tname=".terminfo.cdb"
  34. fi
  35. # export TERMINFO
  36. export TERMINFO="$HOME/$tname"
  37. # compile terminfo for this system
  38. if [ -x "$(command -v tic)" ]; then
  39. tic_out=$(command tic -x -o "$1/$tname" "$1/.terminfo/kitty.terminfo" 2>&1)
  40. [ $? = 0 ] || die "Failed to compile terminfo with err: $tic_out"
  41. fi
  42. }
  43. parse_passwd_record() {
  44. printf "%s" "$(command grep -o '[^:]*$')"
  45. }
  46. login_shell_is_ok() {
  47. [ -n "$1" ] && login_shell=$(echo $1 | parse_passwd_record)
  48. [ -n "$login_shell" -a -x "$login_shell" ] && return 0
  49. return 1
  50. }
  51. using_getent() {
  52. cmd=$(command -v getent) && [ -n "$cmd" ] && output=$(command "$cmd" passwd "$USER" 2>/dev/null) \
  53. && login_shell_is_ok "$output"
  54. }
  55. using_id() {
  56. cmd=$(command -v id) && [ -n "$cmd" ] && output=$(command "$cmd" -P "$USER" 2>/dev/null) \
  57. && login_shell_is_ok "$output"
  58. }
  59. using_python() {
  60. detect_python && output=$(command "$python" -c "import pwd, os; print(pwd.getpwuid(os.geteuid()).pw_shell)" 2>/dev/null) \
  61. && login_shell="$output" && login_shell_is_ok
  62. }
  63. using_perl() {
  64. detect_perl && output=$(command "$perl" -e 'my $shell = (getpwuid($<))[8]; print $shell' 2>/dev/null) \
  65. && login_shell="$output" && login_shell_is_ok
  66. }
  67. using_passwd() {
  68. [ -f "/etc/passwd" -a -r "/etc/passwd" ] && output=$(command grep "^$USER:" /etc/passwd 2>/dev/null) \
  69. && login_shell_is_ok "$output"
  70. }
  71. using_shell_env() {
  72. [ -n "$SHELL" ] && login_shell="$SHELL" && login_shell_is_ok
  73. }
  74. execute_with_python() {
  75. if detect_python; then
  76. exec "$python" "-c" "import os; os.execlp('$login_shell', '-' '$shell_name')"
  77. fi
  78. return 1
  79. }
  80. execute_with_perl() {
  81. if detect_perl; then
  82. exec "$perl" "-e" "exec {'$login_shell'} '-$shell_name'"
  83. fi
  84. return 1
  85. }
  86. exec_zsh_with_integration() {
  87. zdotdir="$ZDOTDIR"
  88. if [ -z "$zdotdir" ]; then
  89. zdotdir=~
  90. else
  91. export KITTY_ORIG_ZDOTDIR="$zdotdir"
  92. fi
  93. # dont prevent zsh-newuser-install from running
  94. if [ -f "$zdotdir/.zshrc" -o -f "$zdotdir/.zshenv" -o -f "$zdotdir/.zprofile" -o -f "$zdotdir/.zlogin" ]; then
  95. export ZDOTDIR="$shell_integration_dir/zsh"
  96. exec "$login_shell" "-l"
  97. fi
  98. # ensure this is not propagated
  99. unset KITTY_ORIG_ZDOTDIR
  100. }
  101. exec_fish_with_integration() {
  102. if [ -z "$XDG_DATA_DIRS" ]; then
  103. export XDG_DATA_DIRS="$shell_integration_dir"
  104. else
  105. export XDG_DATA_DIRS="$shell_integration_dir:$XDG_DATA_DIRS"
  106. fi
  107. export KITTY_FISH_XDG_DATA_DIR="$shell_integration_dir"
  108. exec "$login_shell" "-l"
  109. }
  110. exec_bash_with_integration() {
  111. export ENV="$shell_integration_dir/bash/kitty.bash"
  112. export KITTY_BASH_INJECT="1"
  113. if [ -z "$HISTFILE" ]; then
  114. export HISTFILE="$HOME/.bash_history"
  115. export KITTY_BASH_UNEXPORT_HISTFILE="1"
  116. fi
  117. exec "$login_shell" "--login" "--posix"
  118. }
  119. exec_with_shell_integration() {
  120. [ -z "$shell_integration_dir" ] && return
  121. case "$shell_name" in
  122. "zsh")
  123. exec_zsh_with_integration
  124. ;;
  125. "fish")
  126. exec_fish_with_integration
  127. ;;
  128. "bash")
  129. exec_bash_with_integration
  130. ;;
  131. esac
  132. }
  133. execute_sh_with_posix_env() {
  134. # only for sh as that is likely to be POSIX compliant
  135. [ "$shell_name" = "sh" ] || return
  136. # sh supports -l so use that
  137. command "$login_shell" -l -c ":" > /dev/null 2> /dev/null && return
  138. [ -z "$shell_integration_dir" ] && die "Could not read data over tty ssh kitten cannot function"
  139. sh_dir="$shell_integration_dir/sh"
  140. command mkdir -p "$sh_dir" || die "Creating directory $sh_dir failed"
  141. sh_script="$sh_dir/login_shell_env.sh"
  142. # Source /etc/profile, ~/.profile, and then check and source ENV
  143. printf "%s" '
  144. if [ -n "$KITTY_SH_INJECT" ]; then
  145. unset ENV; unset KITTY_SH_INJECT
  146. _ksi_safe_source() { [ -f "$1" -a -r "$1" ] || return 1; . "$1"; return 0; }
  147. [ -n "$KITTY_SH_POSIX_ENV" ] && export ENV="$KITTY_SH_POSIX_ENV"
  148. unset KITTY_SH_POSIX_ENV
  149. _ksi_safe_source "/etc/profile"; _ksi_safe_source "${HOME-}/.profile"
  150. [ -n "$ENV" ] && _ksi_safe_source "$ENV"
  151. unset -f _ksi_safe_source
  152. fi' > "$sh_script"
  153. export KITTY_SH_INJECT="1"
  154. [ -n "$ENV" ] && export KITTY_SH_POSIX_ENV="$ENV"
  155. export ENV="$sh_script"
  156. exec "$login_shell"
  157. }
  158. install_kitty_bootstrap() {
  159. kitty_exists="n"
  160. command -v kitty 2> /dev/null > /dev/null && kitty_exists="y"
  161. if [ "$kitty_remote" = "yes" -o "$kitty_remote-$kitty_exists" = "if-needed-n" ]; then
  162. kitty_dir="$data_dir/kitty/bin"
  163. if [ "$kitty_exists" = "y" ]; then
  164. export PATH="$kitty_dir:$PATH"
  165. else
  166. export PATH="$PATH:$kitty_dir"
  167. fi
  168. fi
  169. }
  170. prepare_for_exec() {
  171. if [ -n "$leading_data" ]; then
  172. # clear current line as it might have things echoed on it from leading_data
  173. # because we only turn off echo in this script whereas the leading bytes could
  174. # have been sent before the script had a chance to run
  175. printf "\r\033[K" > /dev/tty
  176. fi
  177. [ -f "$HOME/.terminfo/kitty.terminfo" ] || die "Incomplete extraction of ssh data"
  178. install_kitty_bootstrap
  179. [ -n "$login_shell" ] || using_getent || using_id || using_python || using_perl || using_passwd || using_shell_env || login_shell="sh"
  180. case "$login_shell" in
  181. /*) ;;
  182. *)
  183. if ! command -v "$login_shell" > /dev/null 2> /dev/null; then
  184. for i in /opt/homebrew/bin /opt/homebrew/sbin /opt/local/bin /opt/local/sbin /usr/local/bin /usr/bin /bin /usr/sbin /sbin
  185. do
  186. if [ -x "$i/$login_shell" ]; then
  187. login_shell="$i/$login_shell"
  188. break
  189. fi
  190. done
  191. fi
  192. ;;
  193. esac
  194. shell_name=$(command basename $login_shell)
  195. [ -n "$login_cwd" ] && cd "$login_cwd"
  196. }
  197. exec_login_shell() {
  198. case "$KITTY_SHELL_INTEGRATION" in
  199. ("")
  200. # only blanks or unset
  201. unset KITTY_SHELL_INTEGRATION
  202. ;;
  203. (*)
  204. # not blank
  205. printf "%s" "$KITTY_SHELL_INTEGRATION" | command grep -q '\bno-rc\b' || exec_with_shell_integration
  206. # either no-rc or exec failed
  207. unset KITTY_SHELL_INTEGRATION
  208. ;;
  209. esac
  210. # We need to pass the first argument to the executed program with a leading -
  211. # to make sure the shell executes as a login shell. Note that not all shells
  212. # support exec -a so we use the below to try to detect such shells
  213. [ "$(exec -a echo echo OK 2> /dev/null)" = "OK" ] && exec -a "-$shell_name" "$login_shell"
  214. execute_with_python
  215. execute_with_perl
  216. execute_sh_with_posix_env
  217. exec "$login_shell" "-l"
  218. if [ -e /dev/stderr ]; then
  219. printf "%s\n" "Could not execute the shell $login_shell as a login shell" > /dev/stderr
  220. elif [ -e /dev/fd/2 ]; then
  221. printf "%s\n" "Could not execute the shell $login_shell as a login shell" > /dev/fd/2
  222. else
  223. printf "%s\n" "Could not execute the shell $login_shell as a login shell"
  224. fi
  225. exec "$login_shell"
  226. }