From fb89173a4257e0cfe3bfc772bc0cdbccd61e3be0 Mon Sep 17 00:00:00 2001
From: Roman Perepelitsa <roman.perepelitsa@gmail.com>
Date: Wed, 25 Nov 2020 11:14:38 +0100
Subject: Squashed 'gitstatus/' changes from 313fa4f1..1dcba393

1dcba393 bash bindings: provide better diagnostic for the common errors in gitstatus_start
603fb2ce move the rosetta check to `install`
3829edac provide better diagnostic for the common errors in gitstatus_start
e8c9d9cd propagate error messages from install to gitstatus_start; add a special diagnostic for rosetta2

git-subtree-dir: gitstatus
git-subtree-split: 1dcba3930654c54976f112b96acf2ce23006d9e5
---
 gitstatus.plugin.sh  |  37 ++++++++++----
 gitstatus.plugin.zsh |  26 ++++++----
 install              | 133 ++++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 160 insertions(+), 36 deletions(-)

diff --git a/gitstatus.plugin.sh b/gitstatus.plugin.sh
index e0cd206d..61b81e6a 100644
--- a/gitstatus.plugin.sh
+++ b/gitstatus.plugin.sh
@@ -36,6 +36,17 @@
 #   -D        Unless this option is specified, report zero staged, unstaged and conflicted
 #             changes for repositories with bash.showDirtyState = false.
 function gitstatus_start() {
+  if [[ "$BASH_VERSION" < 4 ]]; then
+    >&2 printf 'gitstatus_start: need bash version >= 4.0, found %s\n' "$BASH_VERSION"
+    >&2 printf '\n'
+    >&2 printf 'To see the version of the current shell, type:\n'
+    >&2 printf '\n'
+    >&2 printf '    \033[32mecho\033[0m \033[33m"$BASH_VERSION"\033[0m\n'
+    >&2 printf '\n'
+    >&2 printf 'The output of `\033[32mbash\033[0m --version` may be different and is not relevant.\n'
+    return 1
+  fi
+
   unset OPTIND
   local opt timeout=5 max_dirty=-1 extra_flags
   local max_num_staged=1 max_num_unstaged=1 max_num_conflicted=1 max_num_untracked=1
@@ -69,7 +80,7 @@ function gitstatus_start() {
     local gitstatus_plugin_dir="$PWD"
   fi
 
-  local tmpdir req_fifo resp_fifo
+  local tmpdir req_fifo resp_fifo culprit
 
   function gitstatus_start_impl() {
     local log_level="${GITSTATUS_LOG_LEVEL:-}"
@@ -137,7 +148,7 @@ function gitstatus_start() {
           }
 
           set -- -d "$gitstatus_plugin_dir" -s "$uname_s" -m "$uname_m" \
-            -p "printf '.\036' >&$fd_out" -- _gitstatus_set_daemon
+            -p "printf '.\036' >&$fd_out" -e "$fd_out" -- _gitstatus_set_daemon
           [[ "${GITSTATUS_AUTO_INSTALL:-1}" -ne 0 ]]  || set -- -n "$@"
           source "$gitstatus_plugin_dir"/install      || return
           [[ -n "$_gitstatus_bash_daemon" ]]          || return
@@ -166,7 +177,7 @@ function gitstatus_start() {
             trap - ${sig[@]}
             case "$ret" in
               0|129|130|131|137|141|143|159)
-                echo -nE $'bye\x1f0\x1e' >&"$fd_out"
+                echo -nE $'}bye\x1f0\x1e' >&"$fd_out"
                 exit "$ret"
               ;;
             esac
@@ -192,7 +203,7 @@ function gitstatus_start() {
           trap "trap - ${sig[*]}; kill $pid &>/dev/null" ${sig[@]}
           wait "$pid"
           trap - ${sig[@]}
-          echo -nE $'bye\x1f0\x1e' >&"$fd_out"
+          echo -nE $'}bye\x1f0\x1e' >&"$fd_out"
         ) & disown
       ) & disown
     } 0</dev/null &>"$GITSTATUS_DAEMON_LOG"
@@ -205,12 +216,15 @@ function gitstatus_start() {
     [[ "$GITSTATUS_DAEMON_PID" == [1-9]* ]] || return
 
     local reply
-    echo -nE $'hello\x1f\x1e' >&$_GITSTATUS_REQ_FD                     || return
+    echo -nE $'}hello\x1f\x1e' >&$_GITSTATUS_REQ_FD || return
     local dl=
     while true; do
-      IFS='' read -rd $'\x1e' -u $_GITSTATUS_RESP_FD -t "$timeout" reply || return
-      [[ "$reply" == $'hello\x1f0' ]] && break
-      [[ "$reply" == . ]] || return
+      reply=
+      if ! IFS='' read -rd $'\x1e' -u $_GITSTATUS_RESP_FD -t "$timeout" reply; then
+        culprit="$reply"
+        return 1
+      fi
+      [[ "$reply" == $'}hello\x1f0' ]] && break
       if [[ -z "$dl" ]]; then
         dl=1
         if [[ -t 2 ]]; then
@@ -238,8 +252,11 @@ function gitstatus_start() {
   }
 
   if ! gitstatus_start_impl; then
-    echo "" >&2
-    echo "gitstatus_start: failed to start gitstatusd" >&2
+    >&2 printf '\n'
+    >&2 printf '[\033[31mERROR\033[0m]: gitstatus failed to initialize.\n'
+    if [[ -n "${culprit-}" ]]; then
+      >&2 printf '\n%s\n' "$culprit"
+    fi
     [[ -z "${req_fifo:-}"  ]] || command rm -f "$req_fifo"
     [[ -z "${resp_fifo:-}" ]] || command rm -f "$resp_fifo"
     unset -f gitstatus_start_impl
diff --git a/gitstatus.plugin.zsh b/gitstatus.plugin.zsh
index 603e04d0..ca0fb311 100644
--- a/gitstatus.plugin.zsh
+++ b/gitstatus.plugin.zsh
@@ -404,8 +404,8 @@ function _gitstatus_daemon"${1:-}"() {
 
       local gitstatus_plugin_dir_var=_gitstatus_plugin_dir$fsuf
       local gitstatus_plugin_dir=${(P)gitstatus_plugin_dir_var}
-      builtin set -- -d $gitstatus_plugin_dir -s $uname_s -m $uname_m -p "printf . >&$pipe_fd" -- \
-        _gitstatus_set_daemon$fsuf
+      builtin set -- -d $gitstatus_plugin_dir -s $uname_s -m $uname_m \
+        -p "printf '\\001' >&$pipe_fd" -e $pipe_fd -- _gitstatus_set_daemon$fsuf
       [[ ${GITSTATUS_AUTO_INSTALL:-1} == (|-|+)<1-> ]] || builtin set -- -n "$@"
       builtin source $gitstatus_plugin_dir/install     || return
       [[ -n $_gitstatus_zsh_daemon ]]                  || return
@@ -542,7 +542,7 @@ function gitstatus_start"${1:-}"() {
   fi
 
   local -i lock_fd resp_fd stderr_fd
-  local file_prefix xtrace=/dev/null daemon_log=/dev/null
+  local file_prefix xtrace=/dev/null daemon_log=/dev/null culprit
 
   {
     if (( _GITSTATUS_STATE_$name )); then
@@ -637,8 +637,8 @@ function gitstatus_start"${1:-}"() {
         [[ $req_fd == <1-> ]]                                || return
         typeset -gi _GITSTATUS_REQ_FD_$name=req_fd
 
-        print -nru $req_fd -- $'hello\x1f\x1e' || return
-        local expected=$'hello\x1f0\x1e' actual
+        print -nru $req_fd -- $'}hello\x1f\x1e' || return
+        local expected=$'}hello\x1f0\x1e' actual
         if (( $+functions[p10k] )) && [[ ! -t 1 && ! -t 0 ]]; then
           local -F deadline='EPOCHREALTIME + 4'
         else
@@ -647,8 +647,15 @@ function gitstatus_start"${1:-}"() {
         while true; do
           [[ -t $resp_fd ]]
           sysread -s 1 -t $timeout -i $resp_fd actual || return
-          [[ $actual == h ]] && break
-          [[ $actual == . ]] || return
+          [[ $expected == $actual* ]] && break
+          if [[ $actual != $'\1' ]]; then
+            [[ -t $resp_fd ]]
+            while sysread -t $timeout -i $resp_fd 'actual[$#actual+1]'; do
+              [[ -t $resp_fd ]]
+            done
+            culprit=$actual
+            return 1
+          fi
           (( EPOCHREALTIME < deadline )) && continue
           if (( deadline > 0 )); then
             deadline=0
@@ -747,7 +754,10 @@ function gitstatus_start"${1:-}"() {
     print -ru2  -- ''
     print -Pru2 -- '[%F{red}ERROR%f]: gitstatus failed to initialize.'
     print -ru2  -- ''
-    print -ru2  -- '  Your Git prompt may disappear or become slow.'
+    if [[ -n $culprit ]]; then
+      print -ru2 -- $culprit
+      return err
+    fi
     if [[ -s $xtrace ]]; then
       print -ru2  -- ''
       print -Pru2 -- "  Zsh log (%U${xtrace//\%/%%}%u):"
diff --git a/install b/install
index 91a47205..0c103b8a 100755
--- a/install
+++ b/install
@@ -2,6 +2,45 @@
 #
 # This script does not have a stable API.
 
+_gitstatus_install_daemon_found() {
+  local installed="$1"
+  shift
+
+  case "$daemon" in
+    *-darwin-x86_64);;
+    *)
+      [ $# = 0 ] || "$@" "$daemon" "$version" "$installed"
+      return
+    ;;
+  esac
+
+  local cpu
+  if [ "$uname_sm" != 'darwin arm64' ]                                       ||
+     [ -e /Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist ] ||
+     [ ! -x /usr/sbin/softwareupdate ]                                       ||
+     ! cpu="$(/usr/sbin/sysctl -n machdep.cpu.brand_string)"; then
+    [ $# = 0 ] || "$@" "$daemon" "$version" "$installed"
+    return
+  fi
+
+  case "$cpu" in
+    *Intel*);;
+    *)
+      [ $# = 0 ] || "$@" "$daemon" "$version" "$installed"
+      return
+    ;;
+  esac
+
+  >&"$e" printf 'Please run the following command to install Rosetta:\n'
+  >&"$e" printf '\n'
+  >&"$e" printf '    \033[32m/usr/sbin/softwareupdate\033[0m --install-rosetta\n'
+  >&"$e" printf '\n'
+  >&"$e" printf 'See for details: \033[4mhttps://support.apple.com/en-us/HT211861\033[0m\n'
+  >&"$e" printf '\n'
+  >&"$e" printf 'Once Rosetta is installed, restart your shell.\n'
+  return 1
+}
+
 _gitstatus_install_main() {
   if [ -n "${ZSH_VERSION:-}" ]; then
     emulate -L sh -o no_unset
@@ -12,14 +51,14 @@ _gitstatus_install_main() {
   local argv1="$1"
   shift
 
-  local no_check= no_install= uname_s= uname_m= gitstatus_dir= dl_status=
+  local no_check= no_install= uname_s= uname_m= gitstatus_dir= dl_status= e=
   local opt= OPTARG= OPTIND=1
 
-  while getopts ':s:m:d:p:fnh' opt "$@"; do
+  while getopts ':s:m:d:p:e:fnh' opt "$@"; do
     case "$opt" in
       h)
         command cat <<\END
-Usage: install [-s KERNEL] [-m ARCH] [-d DIR] [-p CMD] [-f|-n] [-- CMD [ARG]...]
+Usage: install [-s KERNEL] [-m ARCH] [-d DIR] [-p CMD] [-e ERRFD] [-f|-n] [-- CMD [ARG]...]
 
 If positional arguments are specified, call this on success:
 
@@ -36,6 +75,7 @@ Options:
   -m ARCH    use this instead of lowercase `uname -m`
   -d DIR     use this instead of `dirname "$0"`
   -p CMD     eval this every second while downloading gitstatusd
+  -e ERRFD   write error messages to this file descriptor
   -f         download gitstatusd even if there is one locally
   -n         do not download gitstatusd (fail instead)
 END
@@ -77,6 +117,17 @@ END
         fi
         dl_status="$OPTARG"
       ;;
+      e)
+        if [ -n "$e" ]; then
+          >&2 echo "[gitstatus] error: duplicate option: -$opt"
+          return 1
+        fi
+        if [ -z "$OPTARG" ]; then
+          >&2 echo "[error] incorrect value of -$opt: $OPTARG"
+          return 1
+        fi
+        e="$OPTARG"
+      ;;
       m)
         if [ -n "$uname_m" ]; then
           >&2 echo "[gitstatus] error: duplicate option: -$opt"
@@ -107,6 +158,7 @@ END
 
   shift "$((OPTIND - 1))"
 
+  : "${e:=2}"
   : "${gitstatus_dir:=$argv1}"
 
   if [ -n "$no_check" -a -n "$no_install" ]; then
@@ -144,7 +196,8 @@ END
         >&2 echo "[gitstatus] internal error: empty gitstatus_version in build.info"
         return 1
       fi
-      [ $# = 0 ] || "$@" "$daemon" "$gitstatus_version" 0
+      local version="$gitstatus_version"
+      _gitstatus_install_daemon_found 0 "$@"
       return
     fi
   fi
@@ -184,7 +237,7 @@ END
         [ -e "$daemon" ] || daemon=
       fi
       if [ -n "$daemon" ]; then
-        [ $# = 0 ] || "$@" "$daemon" "$version" 0
+        _gitstatus_install_daemon_found 0 "$@"
         return
       fi
     fi
@@ -202,17 +255,59 @@ END
       >&2 echo "[gitstatus] error: GITSTATUS_CACHE_DIR is not absolute: $cache_dir"
       return 1
     fi
-    [ -d "$cache_dir" ] || mkdir -p -- "$cache_dir" || return
+    if [ ! -d "$cache_dir" ] && ! mkdir -p -- "$cache_dir" || [ ! -w "$cache_dir" ]; then
+      local dir="$cache_dir"
+      while true; do
+        if [ -e "$dir" ]; then
+          if [ ! -d "$dir" ]; then
+            >&"$e" printf 'Not a directory: \033[4;31m%s\033[0m\n' "$dir"
+            >&"$e" printf '\n'
+            >&"$e" printf 'Delete it, then restart your shell.\n'
+          elif [ ! -w "$dir" ]; then
+            >&"$e" printf 'Directory is not writable: \033[4;31m%s\033[0m\n' "$dir"
+            >&"$e" printf '\n'
+            >&"$e" printf 'Make it writable, then restart your shell.\n'
+          fi
+          break
+        fi
+        if [ "$dir" = / ] || [ "$dir" = . ]; then
+          break
+        fi
+        dir="$(dirname -- "$dir")"
+      done
+      return 1
+    fi
 
     local tmpdir
     if ! command -v mktemp >/dev/null 2>&1 ||
-        ! tmpdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus-install.XXXXXXXXXX)"; then
+       ! tmpdir="$(command mktemp -d "${TMPDIR:-/tmp}"/gitstatus-install.XXXXXXXXXX)"; then
       tmpdir="${TMPDIR:-/tmp}/gitstatus-install.tmp.$$"
-      mkdir -p -- "$tmpdir" || return
+      if ! mkdir -p -- "$tmpdir"; then
+        local dir="${TMPDIR:-/tmp}"
+        if [ -z "${TMPDIR:-}" ]; then
+          local label='directory'
+        else
+          local label='directory (\033[1mTMPDIR\033[m)'
+        fi
+        if [ ! -e "$dir" ]; then
+          >&"$e" printf 'Temporary '"$label"' does not exist: \033[4;31m%s\033[0m\n' "$dir"
+          >&"$e" printf '\n'
+          >&"$e" printf 'Create it, then restart your shell.\n'
+        elif [ ! -d "$dir" ]; then
+          >&"$e" printf 'Not a '"$label"': \033[4;31m%s\033[0m\n' "$dir"
+          >&"$e" printf '\n'
+          >&"$e" printf 'Make it a directory, then restart your shell.\n'
+        elif [ ! -w "$dir" ]; then
+          >&"$e" printf 'Temporary '"$label"' is not writable: \033[4;31m%s\033[0m\n' "$dir"
+          >&"$e" printf '\n'
+          >&"$e" printf 'Make it writable, then restart your shell.\n'
+        fi
+        return 1
+      fi
     fi
 
     if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
-      >&2 echo "[gitstatus] error: please install curl or wget"
+      >&"$e" printf 'Please install \033[32mcurl\033[0m or \033[32mwget\033[0m, then restart your shell.\n'
       return 1
     fi
 
@@ -363,10 +458,12 @@ END
       trap - $sig
 
       if [ -z "$n" ]; then
-        >&2 echo "[gitstatus] error: failed to download gitstatusd from any mirror"
-        >&2 echo ""
-        >&2 echo "  1. $url1"
-        >&2 echo "  2. $url2"
+        >&"$e" printf 'Failed to download \033[32m%s\033[0m from any mirror:\n' "$file"
+        >&"$e" printf '\n'
+        >&"$e" printf '  1. \033[4m%s\033[0m\n' "$url1"
+        >&"$e" printf '  2. \033[4m%s\033[0m\n' "$url2"
+        >&"$e" printf '\n'
+        >&"$e" printf 'Check your internet connection, then restart your shell.\n'
         exit 1
       fi
 
@@ -378,7 +475,7 @@ END
         tmpfile="$cache_dir"/gitstatusd.tmp.$$
       fi
 
-      command mv -f -- "$tmpdir"/gitstatusd-* "$tmpfile" || exit
+      command mv -f -- "$tmpdir"/"$file" "$tmpfile" || exit
       command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
       command rm -f -- "$cache_dir"/"$file"
       command mv -f -- "$tmpfile" "$cache_dir"/"$file" && exit
@@ -390,13 +487,13 @@ END
     command rm -rf -- "$tmpdir"
     [ "$ret" = 0 ] || return
 
-    [ $# = 0 ] || "$@" "$daemon" "$version" 1
+    _gitstatus_install_daemon_found 1 "$@"
     return
   done <"$gitstatus_dir"/install.info
 
-  >&2 echo "[gitstatus] error: no gitstatusd found for $uname_s $uname_m"
-  >&2 echo ""
-  >&2 echo "See: https://github.com/romkatv/gitstatus/blob/master/README.md#compiling"
+  >&"$e" printf 'There is no prebuilt \033[32mgitstatusd\033[0m for \033[1m%s\033[0m.\n' "$uname_s $uname_m"
+  >&"$e" printf '\n'
+  >&"$e" printf 'See: \033[4mhttps://github.com/romkatv/gitstatus#compiling\033[0m\n'
   return 1
 }
 
-- 
cgit v1.2.3