mulle-bootstrap-functions.sh
5de2fbd6
 #! /bin/sh
 #
 #   Copyright (c) 2015 Nat! - Mulle kybernetiK
 #   All rights reserved.
 #
 #   Redistribution and use in source and binary forms, with or without
 #   modification, are permitted provided that the following conditions are met:
 #
 #   Redistributions of source code must retain the above copyright notice, this
 #   list of conditions and the following disclaimer.
 #
 #   Redistributions in binary form must reproduce the above copyright notice,
 #   this list of conditions and the following disclaimer in the documentation
 #   and/or other materials provided with the distribution.
 #
 #   Neither the name of Mulle kybernetiK nor the names of its contributors
 #   may be used to endorse or promote products derived from this software
 #   without specific prior written permission.
 #
 #   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 #   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 #   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 #   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 #   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 #   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 #   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 #   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 #   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #   POSSIBILITY OF SUCH DAMAGE.
 
8e817cc1
 # Escape sequence and resets
 if [ "${MULLE_BOOTSTRAP_NO_COLOR}" != "YES" ]
 then
    C_RESET="\033[0m"
 
    # Foreground colours
    C_BLACK="\033[0;30m"   C_RED="\033[0;31m"    C_GREEN="\033[0;32m"
    C_YELLOW="\033[0;33m"  C_BLUE="\033[0;34m"   C_MAGENTA="\033[0;35m"
    C_CYAN="\033[0;36m"    C_WHITE="\033[0;37m"  C_BR_BLACK="\033[0;90m"
3be635de
 
d7f96955
    C_BR_RED="\033[0;91m"
 
    trap 'printf "${C_RESET}"' TERM EXIT
8e817cc1
 fi
 
 
c6e7156e
 C_ERROR="${C_RED}"
 log_error()
 {
    echo "${C_ERROR}$*${C_RESET}" >&2
 }
 
 
 C_WARNING="${C_YELLOW}"
 log_warning()
 {
    if [ "$MULLE_BOOTSTRAP_TERSE" != "YES" ]
    then
       echo "${C_WARNING}$*${C_RESET}" >&2
    fi
 }
 
 
 C_INFO="${C_CYAN}"
 log_info()
 {
    if [ "$MULLE_BOOTSTRAP_TERSE" != "YES" ]
    then
       echo "${C_INFO}$*${C_RESET}" >&2
    fi
 }
 
 
 C_FLUFF="${C_GREEN}"
 log_fluff()
 {
    if [ "$MULLE_BOOTSTRAP_VERBOSE" = "YES"  ]
    then
       echo "${C_FLUFF}$*${C_RESET}" >&2
    fi
 }
 
 
 C_TRACE="${C_FLUFF}"
 log_trace()
 {
   echo "${C_TRACE}$*${C_RESET}" >&2
 }
 
 
 C_TRACE2="${C_WHITE}"
 log_trace2()
 {
   echo "${C_TRACE2}$*${C_RESET}" >&2
 }
 
 
5de2fbd6
 #
 # some common functions
 #
 fail()
 {
c6e7156e
    log_error "$@"
5de2fbd6
    exit 1
 }
 
 
 internal_fail()
 {
d7f96955
    fail "${C_BR_RED}*** internal error: ${C_RED}$*"
5de2fbd6
 }
 
 
8e817cc1
 
 eval_exekutor()
 {
    if [ "$MULLE_BOOTSTRAP_DRY_RUN" = "YES" -o "$MULLE_BOOTSTRAP_TRACE" = "YES" ]
    then
       echo "$@" >&2
    fi
 
    if [ "$MULLE_BOOTSTRAP_DRY_RUN" != "YES" ]
    then
       eval "$@"
    fi
 }
 
3c8ec4d2
 
8e817cc1
 exekutor()
 {
    if [ "$MULLE_BOOTSTRAP_DRY_RUN" = "YES" -o "$MULLE_BOOTSTRAP_TRACE" = "YES" ]
    then
       echo "$@" >&2
    fi
 
    if [ "$MULLE_BOOTSTRAP_DRY_RUN" != "YES" ]
    then
       "$@"
    fi
 }
 
 
b06853c1
 is_yes()
 {
    local s
 
e3e80f18
    s=`echo "${1}" | tr '[:lower:]' '[:upper:]'`
b06853c1
    case "${s}" in
       YES|Y|1)
          return 0
       ;;
       NO|N|0|"")
          return 1
       ;;
 
       *)
          fail "$2 should contain YES or NO (or be empty)"
       ;;
    esac
 }
 
 
 concat()
 {
    local i
    local s
 
    for i in "$@"
    do
       if [ "${i}" != "" ]
       then
          if [ "${s}" != "" ]
          then
             s="${s} ${i}"
          else
             s="${i}"
          fi
       fi
    done
 
    echo "${s}"
 }
 
 
5de2fbd6
 path_depth()
 {
    local name
    local depth
 
    name="$1"
    depth=0
 
    if [ "${name}" != "" ]
    then
       while [ "$name" != "." ]
       do
          name=`dirname "$name"`
          depth=`expr $depth + 1`
       done
    fi
e3e80f18
    echo "$depth"
5de2fbd6
 }
 
 
8d41d7c7
 #
 # stolen from:
 # http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
 # ----
 #
 _prepend_path_if_relative()
 {
    case "$2" in
       /* )
          echo "$2"
          ;;
       * )
          echo "$1/$2"
          ;;
    esac
 }
 
 
 resolve_symlinks()
 {
    local dir_context path
 
    path="`readlink "$1"`"
    if [ $? -eq 0 ]
    then
       dir_context=`dirname "$1"`
       resolve_symlinks "`_prepend_path_if_relative "$dir_context" "$path"`"
    else
       echo "$1"
    fi
 }
 
 
 _canonicalize_dir_path()
 {
     (cd "$1" 2>/dev/null && pwd -P)
 }
 
 
 _canonicalize_file_path()
 {
     local dir file
 
     dir="` dirname "$1"`"
     file="`basename "$1"`"
     (cd "${dir}" 2>/dev/null && echo "`pwd -P`/${file}")
 }
 
 
 canonicalize_path()
 {
    if [ -d "$1" ]
    then
       _canonicalize_dir_path "$1"
    else
       _canonicalize_file_path "$1"
    fi
 }
 
 
 realpath()
 {
    canonicalize_path "`resolve_symlinks "$1"`"
 }
 
 # ----
 
c6e7156e
 relative_path_between()
 {
8d41d7c7
    python -c "import os.path; print os.path.relpath( '$1', '$2')"
c6e7156e
 }
 
 
5de2fbd6
 compute_relative()
 {
    local depth
    local relative
    local name
 
    name="$1"
 
    depth=`path_depth "${name}"`
    if [ "${depth}" -gt 0 ]
    then
       relative=".."
       while [ "$depth" -gt 1 ]
       do
          relative="${relative}/.."
          depth=`expr $depth - 1`
       done
    fi
    echo "${relative}"
 }
 
 
e3e80f18
 remove_absolute_path_prefix_up_to()
 {
    local s
    local prefix
 
    s="$1"
    prefix="$2"
 
    if [ "`basename "${s}"`" = "${prefix}" ]
    then
       return 0
    fi
 
    echo "${s}" | sed "s|^.*/${prefix}/\(.*\)*|\1|g"
 }
 
 
3be635de
 mkdir_if_missing()
 {
    if [ ! -d "${1}" ]
    then
28ed6b30
       exekutor mkdir -p "$1" || fail "failed to create directory \"$1\""
3be635de
    fi
 }
 
 
 #
 # consider . .. ~ or absolute paths as unsafe
 # anything starting with a $ is probably also bad
 # this just catches some obvious problems, not all
 #
 assert_sane_subdir_path()
 {
    case "$1"  in
       \$*|~/.|..|./|../|/*)
          log_error "refuse unsafe path ${C_WHITE}$1"
          exit 1
       ;;
    esac
 }
 
 
 assert_sane_path()
 {
    case "$1"  in
       \$*|~/*|..|.|/|./|../*)
          log_error "refuse unsafe path ${C_WHITE}$1"
          exit 1
       ;;
    esac
 }
 
 
 rmdir_safer()
 {
    if [ -d "$1" ]
    then
       assert_sane_path "$1"
       exekutor rm -rf "$1" || fail "failed to remove ${1}"
    fi
 }
 
 
5de2fbd6
 user_say_yes()
 {
   local  x
 
   x="nix"
   while [ "$x" != "y" -a "$x" != "n" -a "$x" != "" ]
   do
8e817cc1
      echo "${C_YELLOW}$* (${C_WHITE}y${C_YELLOW}/${C_GREEN}N${C_YELLOW})${C_RESET}" >&2
5de2fbd6
      read x
   done
 
   [ "$x" = "y" ]
   return $?
 }
 
 
28ed6b30
 dir_can_be_rmdir()
5de2fbd6
 {
    local empty
 
28ed6b30
    if [ ! -d "$1" ]
    then
       return 2
    fi
 
    empty="`ls -A "$1" 2> /dev/null`"
5de2fbd6
    [ "$empty" = "" ]
 }
 
 
28ed6b30
 # this does not check for hidden files
5de2fbd6
 dir_has_files()
 {
    local empty
3be635de
    local result
5de2fbd6
 
    empty=`ls "$1"/* 2> /dev/null` 2> /dev/null
    [ "$empty" != "" ]
3be635de
    result=$?
 
    if [ "$result" -eq 1 ]
    then
28ed6b30
       log_fluff "Directory \"$1\" has no files"
3be635de
    else
28ed6b30
       log_fluff "Directory \"$1\" has files"
3be635de
    fi
    return "$result"
5de2fbd6
 }
 
 
 #
 # first find a project with matching name, otherwise find
 # first nearest project
 #
 find_xcodeproj()
 {
    local found
    local expect
    local depth
 
    found=""
    expect="$1"
    depth=1000
    #     IFS='\0'
 
    for i in `find . -name "*.xcodeproj" -print`
    do
       match=`basename "${i}" .xcodeproj`
       if [ "$match" = "$expect" ]
       then
          echo "$i"
          return 0
       fi
 
       new_depth=`path_depth "$i"`
       if [ "$new_depth" -lt "$depth" ]
       then
          found="${i}"
          depth="$new_depth"
       fi
    done
 
    if [ "$found" != "" ]
    then
       echo "${found}"
       return 0
    fi
 
    return 1
 }
 
 
cbead75b
 canonical_clone_name()
 {
    local url
 
    url="${1}"
 
    basename "${url}" .git
 }
 
 
8e817cc1
 # http://askubuntu.com/questions/152001/how-can-i-get-octal-file-permissions-from-command-line
 lso()
5de2fbd6
 {
8e817cc1
    ls -aldG "$@" | \
    awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(" %0o ",k);print }' | \
    awk '{print $1}'
5de2fbd6
 }
40fe269d
 
 
 run_script()
 {
    local script
 
    script="$1"
    shift
 
d7f96955
    [ ! -z "$script" ] || internal_fail "script is empty"
40fe269d
 
    if [ -x "${script}" ]
    then
       log_info "Executing script ${script}"
       exekutor "${script}" "$@" || fail "script \"${script}\" did not run successfully"
    else
       if [ ! -e "${script}" ]
       then
d7f96955
          fail "script \"${script}\" not found ($PWD)"
40fe269d
       else
          fail "script \"${script}\" not executable"
       fi
    fi
 }