Skip to content

Instantly share code, notes, and snippets.

@mulle-nat
Last active November 11, 2021 22:07
Show Gist options
  • Save mulle-nat/4859038254afbd24b03fb99959ce520d to your computer and use it in GitHub Desktop.
Save mulle-nat/4859038254afbd24b03fb99959ce520d to your computer and use it in GitHub Desktop.
πŸ‘ Clone all Repositories relevant for hacking on mulle-objc
#! /bin/sh
[ "${TRACE}" = 'YES' -o "${MULLE_OBJC_CLONE_ALL_TRACE}" = 'YES' ] && set -x && : "$0" "$@"
REPOS_M="mulle-bashfunctions"
REPOS_S="mulle-craft
mulle-dispense
mulle-domain
mulle-env
mulle-fetch
mulle-make
mulle-match
mulle-monitor
mulle-platform
mulle-project
mulle-sde
mulle-sde-developer
mulle-semver
mulle-sourcetree
mulle-template
mulle-test"
REPOS_C="mulle-c11
mulle-allocator
mulle-data
mulle-buffer
mulle-container
mulle-http
mulle-identifier
mulle-regex
mulle-unicode
mulle-url
mulle-utf
mulle-vararg
mulle-c-developer"
REPOS_CONCURRENT="mulle-thread
mulle-fifo
mulle-aba
mulle-concurrent"
REPOS_CORE="mulle-atexit
mulle-atinit
mulle-dlfcn
mulle-fprintf
mulle-mmap
mulle-sprintf
mulle-stacktrace
mulle-testallocator
mulle-time"
REPOS_OBJC="mulle-objc-runtime
mulle-objc-debug
mulle-objc-list
mulle-objc-musl
mulle-objc-compat
MulleObjC
MulleObjC-startup
mulle-objc-runtime-startup
mulle-objc-developer"
REPOS_FOUNDATION="MulleObject
MulleObjCArchiverFoundation
MulleObjCCalendarFoundation
MulleObjCContainerFoundation
MulleObjCDecimalFoundation
MulleObjCExpatFoundation
MulleObjCInetOSFoundation
MulleObjCKVCFoundation
MulleObjCLockFoundation
MulleObjCMathFoundation
MulleObjCOSFoundation
MulleObjCPlistFoundation
MulleObjCStandardFoundation
MulleObjCUnicodeFoundation
MulleObjCValueFoundation
Foundation
MulleFoundation
objc-compat
mulle-bunchobjects
mulle-testgen
MulleObjCStandardFoundation-startup
Foundation-startup
MulleFoundation-startup
mulle-foundation-developer
foundation-developer"
REPOS_WEB="MulleObjCHTTPFoundation
MulleObjCInetFoundation
MulleObjCJSMNFoundation
MulleBase64
MulleCivetWeb
MulleCurl
MulleJS
MulleZlib
MulleHoedown
MulleWebClient
MulleWebServer
#MulleWeb
MulleScion
MulleScionHTMLPreprocessor
mulle-scion"
REPOS_UI=
REPOS_EOF=
####
#
# Prelude to be placed at top of each script. Rerun this script either in
# bash or zsh, if not already running in either (which can happen!)
# Allows script to run on systems that either have bash (linux) or
# zsh (macOS) only by default.
if [ "$1" != --no-auto-shell ]
then
if [ -z "${BASH_VERSION}" -a -z "${ZSH_VERSION}" ]
then
exe_shell="`command -v "bash" `"
exe_shell="${exe_shell:-`command -v "zsh" `}"
script="$0"
#
# Quote incoming arguments for shell expansion
#
args=""
for arg in "$@"
do
# True bourne sh doesn't know ${a//b/c} and <<<
case "${arg}" in
*\'*)
# Use cat instead of echo to avoid possible echo -n
# problems. Escape single quotes in string.
arg="`cat <<EOF | sed -e s/\'/\'\\\"\'\\\"\'/g
${arg}
EOF
`"
;;
esac
if [ -z "${args}" ]
then
args="'${arg}'"
else
args="${args} '${arg}'"
fi
done
#
# bash/zsh will use arg after -c <arg> as $0, convenient!
#
exec "${exe_shell:-bash}" -c ". ${script} --no-auto-shell ${args}" "${script}"
fi
if [ ! -z "${BASH_VERSION}" ]
then
set +o posix
fi
else
shift # get rid of --no-auto-shell
fi
#
# Main script to follow, runs now either in zsh or bash
#
####
MULLE_EXECUTABLE_VERSION="0.0.0"
print_hidden_flags()
{
echo " --github : clone from github (default)"
echo " --host <h> : specify custom repository host"
echo " --mulle : clone from Mulle kybernetiK"
echo " --nat : use a custom directory layout"
echo " --style <s> : specify URL style gitolite or github"
echo " --user <u> : specify custom repository user"
echo " -b <b> : specify custom branch to clone from"
}
print_flags()
{
echo " -lx : trace execution of external commands"
echo " -l : don't stop if a git clone doesn't succeed (default)"
echo " --prefix <p> : prefix where to clone to e.g. /tmp (.)"
##
## ADD YOUR FLAGS DESCRIPTIONS HERE
##
options_technical_flags_usage \
" : "
}
usage()
{
[ $# -ne 0 ] && log_error "$*"
cat <<EOF >&2
Usage:
mulle-objc-clone-all [flags]
Clone all repositiories for mulle-objc development from github. This is
useful for actually changing code in any of the downloaded repositories.
To develop Objective-C programs, you can just use mulle-sde and don't
need this script.
This script will not install the mulle-clang compiler or the mulle-gdb
debugger. These two projects have to be installed separately.
But it will fetch:
* mulle-bashfunctions
* mulle-sde
* mulle-c
* mulle-concurrent
* mulle-core
* mulle-objc
* MulleFoundation
* MulleWeb
Flags:
EOF
(
print_flags
if [ "${MULLE_FLAG_LOG_VERBOSE}" = 'YES' ]
then
print_hidden_flags
fi
) | LC_ALL=C sort >&2
exit 1
}
download_repo()
{
log_entry "download_repo" "$@"
local repo="$1"
local org="$2"
local dstparentdir="$3"
local host="$4"
local user="$5"
local style="$6"
local protocol="$7"
local branch="$8"
local url
case "${style}" in
gitolite)
url="${host}:${repo}.git"
;;
github)
case "${protocol}" in
https)
url="https://${host}/${org}/${repo}.git"
;;
git)
url="${user:-git}@${host}:${org}/${repo}.git"
;;
*)
fail "unsupported protocol ${protocol}"
;;
esac
;;
*)
fail "unsupported URL style ${style}"
;;
esac
r_filepath_concat "${dstparentdir}" "${repo}"
if [ -e "${RVAL}" ]
then
log_verbose "${RVAL#${MULLE_USER_PWD}/} already present"
return
fi
mkdir_if_missing "${dstparentdir}" || exit 1
local rval
local options
if [ -z "${branch}" -o "${branch}" = "master" ]
then
(
exekutor cd "${dstparentdir}" &&
exekutor git ${GITFLAGS} clone "${url}"
)
rval=$?
else
(
exekutor cd "${dstparentdir}" &&
exekutor git ${GITFLAGS} clone -b "${branch}" "${url}" &&
exekutor cd "${repo}" &&
exekutor git ${GITFLAGS} checkout -b master
)
rval=$?
fi
if [ $rval -ne 0 -a "${OPTION_LENIENT}" != 'YES' ]
then
exit 1
fi
}
download_repos()
{
log_entry "download_repos" "$@"
local repos="$1"
shift 1
local repo
shell_disable_glob
IFS=$'\n'
for repo in ${repos}
do
shell_enable_glob ; IFS="${DEFAULT_IFS}"
case "${repo}" in
\#*|"")
continue
;;
esac
download_repo "${repo}" "$@"
done
shell_enable_glob ; IFS="${DEFAULT_IFS}"
}
download_all()
{
log_entry "download_all" "$@"
ORG_M=mulle-nat
DST_M="${SRC_M}"
download_repos "${REPOS_M}" "${ORG_M}" "${DST_M}" "$@"
ORG_SDE=mulle-sde
DST_SDE="${SRC_S}"
download_repos "${REPOS_S}" "${ORG_SDE}" "${DST_SDE}" "$@"
ORG_C=mulle-c
DST_C="${SRC_O}/${ORG_C}"
download_repos "${REPOS_C}" "${ORG_C}" "${DST_C}" "$@"
ORG_CONCURRENT=mulle-concurrent
DST_CONCURRENT="${SRC_O}/${ORG_CONCURRENT}"
download_repos "${REPOS_CONCURRENT}" "${ORG_CONCURRENT}" "${DST_CONCURRENT}" "$@"
ORG_CORE=mulle-core
DST_CORE="${SRC_O}/${ORG_CORE}"
download_repos "${REPOS_CORE}" "${ORG_CORE}" "${DST_CORE}" "$@"
ORG_OBJC=mulle-objc
DST_OBJC="${SRC_O}/${ORG_OBJC}"
download_repos "${REPOS_OBJC}" "${ORG_OBJC}" "${DST_OBJC}" "$@"
ORG_FOUNDATION=MulleFoundation
DST_FOUNDATION="${SRC_O}/${ORG_FOUNDATION}"
download_repos "${REPOS_FOUNDATION}" "${ORG_FOUNDATION}" "${DST_FOUNDATION}" "$@"
ORG_WEB=MulleWeb
DST_WEB="${SRC_O}/${ORG_WEB}"
download_repos "${REPOS_WEB}" "${ORG_WEB}" "${DST_WEB}" "$@"
ORG_UI=MulleUI
DST_UI="${SRC_O}/${ORG_UI}"
download_repos "${REPOS_UI}" "${ORG_UI}" "${DST_UI}" "$@"
ORG_EOF=MulleEOF
DST_EOF="${SRC_M}/${ORG_EOF}"
download_repos "${REPOS_EOF}" "${ORG_EOF}" "${DST_EOF}" "$@"
}
main()
{
local MULLE_FLAG_MAGNUM_FORCE='NO'
# technical flags
local MULLE_TRACE
local MULLE_FLAG_EXEKUTOR_DRY_RUN='NO'
local MULLE_FLAG_LOG_LOCAL='NO'
local MULLE_FLAG_LOG_DEBUG='NO'
local MULLE_FLAG_LOG_EXEKUTOR='NO'
local MULLE_FLAG_LOG_FLUFF='NO'
local MULLE_FLAG_LOG_SCRIPTS='NO'
local MULLE_FLAG_LOG_SETTINGS='NO'
local MULLE_FLAG_LOG_VERBOSE='NO'
local MULLE_FLAG_LOG_MERGE='NO'
local MULLE_TRACE_POSTPONE='NO'
#
# simple option/flag handling
#
local OPTION_REPOUSER=""
local OPTION_REPOHOST="github.com"
local OPTION_PROTOCOL="https"
local OPTION_URLSTYLE="github"
local OPTION_BRANCH="release"
local OPTION_PREFIX=""
local OPTION_LENIENT="YES"
while [ $# -ne 0 ]
do
if options_technical_flags "$1"
then
shift
continue
fi
case "$1" in
-f|--force)
MULLE_FLAG_MAGNUM_FORCE='YES'
;;
-h*|--help|help)
usage
;;
-l|--lenient)
OPTION_LENIENT='YES'
;;
--strict)
OPTION_LENIENT='NO'
;;
--prefix)
[ $# -eq 1 ] && usage "missing argument to $1"
shift
OPTION_PREFIX="$1"
;;
--host)
[ $# -eq 1 ] && usage "missing argument to $1"
shift
OPTION_REPOHOST="$1"
;;
--user)
[ $# -eq 1 ] && usage "missing argument to $1"
shift
OPTION_REPOUSER="$1"
;;
--style)
[ $# -eq 1 ] && usage "missing argument to $1"
shift
OPTION_URLSTYLE="$1"
;;
-b|--branch)
[ $# -eq 1 ] && usage "missing argument to $1"
shift
OPTION_BRANCH="$1"
;;
--github)
OPTION_REPOHOST="github.com"
OPTION_URLSTYLE="github"
OPTION_PROTOCOL="https"
OPTION_BRANCH="release"
;;
--mulle)
OPTION_REPOHOST="gitolite.mulle-kybernetik.com"
OPTION_URLSTYLE="gitolite"
OPTION_PROTOCOL="git"
OPTION_BRANCH="master"
;;
--nat)
r_filepath_concat "${OPTION_PREFIX}" "srcM"
SRC_M="${RVAL}"
r_filepath_concat "${OPTION_PREFIX}" "srcO"
SRC_O="${RVAL}"
r_filepath_concat "${OPTION_PREFIX}" "srcS"
SRC_S="${RVAL}"
if [ -z "${OPTION_PREFIX}" ]
then
case "${MULLE_UNAME}" in
darwin)
SRC_M=/Volumes/Source/srcM
SRC_O=/Volumes/Source/srcO
SRC_S=/Volumes/Source/srcS
;;
linux|freebsd)
SRC_M=/home/src/srcM
SRC_O=/home/src/srcO
SRC_S=/home/src/srcS
;;
esac
fi
;;
--version)
printf "%s\n" "${MULLE_EXECUTABLE_VERSION}"
exit 0
;;
##
## ADD YOUR FLAGS HERE
##
-*)
usage "Unknown flag \"$1\""
;;
*)
break
;;
esac
shift
done
options_setup_trace "${MULLE_TRACE}" && set -x
if [ -z "${SRC_M}" ]
then
r_filepath_concat "${OPTION_PREFIX}" "sde"
SRC_M="${RVAL}"
fi
if [ -z "${SRC_S}" ]
then
r_filepath_concat "${OPTION_PREFIX}" "sde"
SRC_S="${RVAL}"
fi
if [ -z "${SRC_O}" ]
then
r_filepath_concat "${OPTION_PREFIX}" "objc"
SRC_O="${RVAL}"
fi
##
## ADD YOUR CODE HERE
##
download_all "${OPTION_REPOHOST}" \
"${OPTION_REPOUSER}" \
"${OPTION_URLSTYLE}" \
"${OPTION_PROTOCOL}" \
"${OPTION_BRANCH}"
}
########
###
### INIT - You usually won't edit code below, except if you want
### to embed mulle-bashfunctions instead of a dynamic load.
### To Embed, just copy/paste "mulle-bashfunctions.sh"
### before _init
_init()
{
#
# minimal setup exit
#
if [ "$1" = "version" ]
then
printf "%s\n" "${MULLE_EXECUTABLE_VERSION}"
exit 0
fi
#
# leading backslash ? looks like we're getting called from
# mingw via a .BAT or so
#
case "$PATH" in
'\\'*)
PATH="${PATH//\\/\/}"
;;
esac
if [ -z "${MULLE_BASHGLOBAL_SH}" ]
then
if [ -z "${MULLE_BASHFUNCTIONS_LIBEXEC_DIR}" ]
then
MULLE_BASHFUNCTIONS_LIBEXEC_DIR="`mulle-bashfunctions-env libexec-dir 2> /dev/null`"
if [ -z "${MULLE_BASHFUNCTIONS_LIBEXEC_DIR}" ]
then
if [ -z "`command -v "${executablepath}"`" ]
then
echo "Fatal Error: Could not find mulle-bashfunctions-env in PATH ($PATH) - mulle-bashfunctions not installed ?" >&2
else
echo "Fatal Error: Could not find libexec of mulle-bashfunctions-env ($PWD)" >&2
fi
exit 1
fi
fi
. "${MULLE_BASHFUNCTIONS_LIBEXEC_DIR}/mulle-bashfunctions.sh" ||
fail "failed to load bashfunctions from ${MULLE_BASHFUNCTIONS_LIBEXEC_DIR}"
fi
# set -e # more pain then gain in the end
# set -u # doesn't work with my style
shell_enable_pipefail
shell_enable_extglob
call_main "${MULLE_OBJC_CLONE_ALL_FLAGS}" "$@"
}
#
# set MULLE_EXECUTABLE here unless set already
#
MULLE_EXECUTABLE="${MULLE_EXECUTABLE:-${BASH_SOURCE[0]:-${(%):-%x}}}"
### >> START OF mulle-bashfunctions-embed.sh >>
#
# Copyright (c) 2015-2021 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
#
if [ -z "${MULLE_BASHGLOBAL_SH}" ]
then
MULLE_BASHGLOBAL_SH="included"
DEFAULT_IFS="${IFS}" # as early as possible
if [ ! -z "${ZSH_VERSION}" ]
then
setopt sh_word_split
setopt POSIX_ARGZERO
fi
if [ -z "${MULLE_EXECUTABLE}" ]
then
MULLE_EXECUTABLE="${BASH_SOURCE[0]:-${(%):-%x}}"
case "${MULLE_EXECUTABLE##*/}" in
mulle-bash*.sh)
MULLE_EXECUTABLE="$0"
;;
esac
fi
case "${MULLE_EXECUTABLE##*/}" in
mulle-bash*.sh)
echo "MULLE_EXECUTABLE fail" >&2
exit 1
;;
esac
if [ -z "${MULLE_EXECUTABLE_NAME}" ]
then
MULLE_EXECUTABLE_NAME="${MULLE_EXECUTABLE##*/}"
fi
if [ -z "${MULLE_USER_PWD}" ]
then
MULLE_USER_PWD="${PWD}"
export MULLE_USER_PWD
fi
MULLE_USAGE_NAME="${MULLE_USAGE_NAME:-${MULLE_EXECUTABLE_NAME}}"
MULLE_EXECUTABLE_PWD="${PWD}"
MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_EXECUTABLE_NAME}"
MULLE_EXECUTABLE_PID="$$"
if [ -z "${MULLE_UNAME}" ]
then
case "${BASH_VERSION}" in
[0123]*)
MULLE_UNAME="`uname | tr '[:upper:]' '[:lower:]'`"
;;
*)
MULLE_UNAME="`uname`"
if [ ! -z "${ZSH_VERSION}" ]
then
MULLE_UNAME="${MULLE_UNAME:l}"
else
MULLE_UNAME="${MULLE_UNAME,,}"
fi
;;
esac
MULLE_UNAME="${MULLE_UNAME%%_*}"
MULLE_UNAME="${MULLE_UNAME%[36][24]}" # remove 32 64 (hax)
if [ "${MULLE_UNAME}" = "linux" ]
then
read -r MULLE_UNAME < /proc/sys/kernel/osrelease
case "${MULLE_UNAME}" in
*-Microsoft)
MULLE_UNAME="windows"
MULLE_EXE_EXTENSION=".exe"
;;
esac
fi
fi
if [ -z "${MULLE_HOSTNAME}" ]
then
case "${MULLE_UNAME}" in
'mingw'*)
MULLE_HOSTNAME="`hostname`"
;;
*)
MULLE_HOSTNAME="`hostname -s`"
;;
esac
case "${MULLE_HOSTNAME}" in
\.*)
MULLE_HOSTNAME="_${MULLE_HOSTNAME}"
;;
esac
fi
if [ -z "${MULLE_USERNAME}" ]
then
MULLE_USERNAME="${MULLE_USERNAME:-${USERNAME}}" # mingw
MULLE_USERNAME="${MULLE_USERNAME:-${USER}}"
MULLE_USERNAME="${MULLE_USERNAME:-${LOGNAME}}"
MULLE_USERNAME="${MULLE_USERNAME:-`id -nu 2> /dev/null`}"
MULLE_USERNAME="${MULLE_USERNAME:-cptnemo}"
fi
fi
[ ! -z "${MULLE_COMPATIBILITY_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-compatibility.sh" >&2
MULLE_COMPATIBILITY_SH="included"
shell_enable_pipefail()
{
set -o pipefail
}
shell_disable_pipefail()
{
set +o pipefail
}
shell_is_pipefail_enabled()
{
case "$-" in
*f*)
return 1
;;
esac
return 0
}
shell_enable_extglob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
setopt kshglob
setopt bareglobqual
else
shopt -s extglob
fi
}
shell_disable_extglob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
unsetopt bareglobqual
unsetopt kshglob
else
shopt -u extglob
fi
}
shell_is_extglob_enabled()
{
if [ ! -z "${ZSH_VERSION}" ]
then
[[ -o kshglob ]]
return $?
fi
shopt -q extglob
}
shell_enable_nullglob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
setopt nullglob
else
shopt -s nullglob
fi
}
shell_disable_nullglob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
unsetopt nullglob
else
shopt -u nullglob
fi
}
shell_is_nullglob_enabled()
{
if [ ! -z "${ZSH_VERSION}" ]
then
[[ -o nullglob ]]
return $?
fi
shopt -q nullglob
}
shell_enable_glob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
unsetopt noglob
else
set +f
fi
}
shell_disable_glob()
{
if [ ! -z "${ZSH_VERSION}" ]
then
setopt noglob
else
set -f
fi
}
shell_is_glob_enabled()
{
if [ ! -z "${ZSH_VERSION}" ]
then
if [[ -o noglob ]]
then
return 1
fi
return 0
fi
case "$-" in
*f*)
return 1
;;
esac
return 0
}
shell_is_function()
{
if [ ! -z "${ZSH_VERSION}" ]
then
case "`type "$1" `" in
*function*)
return 0
;;
esac
return 1
fi
[ "`type -t "$1"`" = "function" ]
return $?
}
shell_enable_extglob
[ ! -z "${MULLE_LOGGING_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-logging.sh" >&2
MULLE_LOGGING_SH="included"
log_printf()
{
local format="$1" ; shift
if [ -z "${MULLE_EXEKUTOR_LOG_DEVICE}" ]
then
printf "${format}" "$@" >&2
else
printf "${format}" "$@" > "${MULLE_EXEKUTOR_LOG_DEVICE}"
fi
}
MULLE_LOG_ERROR_PREFIX=" error: "
log_error()
{
log_printf "${C_ERROR}${MULLE_EXECUTABLE_FAIL_PREFIX}${MULLE_LOG_ERROR_PREFIX}${C_ERROR_TEXT}%b${C_RESET}\n" "$*"
}
MULLE_LOG_FAIL_ERROR_PREFIX=" fatal error: "
log_fail()
{
log_printf "${C_ERROR}${MULLE_EXECUTABLE_FAIL_PREFIX}${MULLE_LOG_FAIL_ERROR_PREFIX}${C_ERROR_TEXT}%b${C_RESET}\n" "$*"
}
log_warning()
{
if [ "${MULLE_FLAG_LOG_TERSE}" != 'YES' ]
then
log_printf "${C_WARNING}%b${C_RESET}\n" "$*"
fi
}
log_info()
{
if [ "${MULLE_FLAG_LOG_TERSE}" != 'YES' ]
then
log_printf "${C_INFO}%b${C_RESET}\n" "$*"
fi
}
log_verbose()
{
if [ "${MULLE_FLAG_LOG_VERBOSE}" = 'YES' ]
then
log_printf "${C_VERBOSE}%b${C_RESET}\n" "$*"
fi
}
log_fluff()
{
if [ "${MULLE_FLAG_LOG_FLUFF}" = 'YES' ]
then
log_printf "${C_FLUFF}%b${C_RESET}\n" "$*"
else
log_debug "$@"
fi
}
log_setting()
{
if [ "${MULLE_FLAG_LOG_FLUFF}" = 'YES' ]
then
log_printf "${C_SETTING}%b${C_RESET}\n" "$*"
fi
}
log_debug()
{
if [ "${MULLE_FLAG_LOG_DEBUG}" != 'YES' ]
then
return
fi
case "${MULLE_UNAME}" in
linux)
log_printf "${C_DEBUG}$(date "+%s.%N") %b${C_RESET}\n" "$*"
;;
*)
log_printf "${C_DEBUG}$(date "+%s") %b${C_RESET}\n" "$*"
;;
esac
}
log_entry()
{
if [ "${MULLE_FLAG_LOG_DEBUG}" != 'YES' ]
then
return
fi
local functionname="$1" ; shift
local args
if [ $# -ne 0 ]
then
args="'$1'"
shift
fi
while [ $# -ne 0 ]
do
args="${args}, '$1'"
shift
done
log_debug "${functionname} ${args}"
}
log_trace()
{
case "${MULLE_UNAME}" in
linux)
log_printf "${C_TRACE}$(date "+%s.%N") %b${C_RESET}\n" "$*"
;;
*)
log_printf "${C_TRACE}$(date "+%s") %b${C_RESET}\n" "$*"
;;
esac
}
log_trace2()
{
case "${MULLE_UNAME}" in
linux)
log_printf "${C_TRACE2}$(date "+%s.%N") %b${C_RESET}\n" "$*"
;;
*)
log_printf "${C_TRACE2}$(date "+%s") %b${C_RESET}\n" "$*"
;;
esac
}
if [ -z "${BASH_VERSION}" ]
then
function caller()
{
local i="${1:-1}"
i=$((i+1))
local file=${funcfiletrace[$((i))]%:*}
local line line=${funcfiletrace[$((i))]##*:}
local func=${funcstack[$((i + 1))]}
if [ -z "${func## }" ]
then
return 1
fi
printf "%s %s %s\n" "$line" "$func" "${file##*/}"
return 0
}
fi
stacktrace()
{
local i=1
local line
local max
case "$-" in
*x*)
return
;;
esac
max=100
while line="`caller $i`"
do
log_printf "${C_CYAN}%b${C_RESET}\n" "$i: #${line}"
i=$((i + 1))
[ $i -gt $max ] && break
done
}
fail()
{
if [ ! -z "$*" ]
then
log_fail "$*"
fi
if [ "${MULLE_FLAG_LOG_DEBUG}" = 'YES' ]
then
stacktrace
fi
exit 1
}
MULLE_INTERNAL_ERROR_PREFIX=" *** internal error ***:"
internal_fail()
{
log_printf "${C_ERROR}${MULLE_EXECUTABLE_FAIL_PREFIX}${MULLE_INTERNAL_ERROR_PREFIX}${C_ERROR_TEXT}%b${C_RESET}\n" "$*"
stacktrace
exit 1
}
logging_reset()
{
printf "${C_RESET}" >&2
}
logging_trap_install()
{
trap 'logging_reset ; exit 1' TERM INT
}
logging_initialize_color()
{
case "${TERM}" in
dumb)
MULLE_NO_COLOR=YES
;;
esac
if [ -z "${NO_COLOR}" -a "${MULLE_NO_COLOR}" != 'YES' ] && [ ! -f /dev/stderr ]
then
C_RESET="\033[0m"
C_RED="\033[0;31m" C_GREEN="\033[0;32m"
C_BLUE="\033[0;34m" C_MAGENTA="\033[0;35m"
C_CYAN="\033[0;36m"
C_BR_RED="\033[0;91m"
C_BOLD="\033[1m"
C_FAINT="\033[2m"
C_SPECIAL_BLUE="\033[38;5;39;40m"
if [ "${MULLE_LOGGING_TRAP}" != 'NO' ]
then
logging_trap_install
fi
fi
C_RESET_BOLD="${C_RESET}${C_BOLD}"
C_ERROR="${C_BR_RED}${C_BOLD}"
C_WARNING="${C_RED}${C_BOLD}"
C_INFO="${C_CYAN}${C_BOLD}"
C_VERBOSE="${C_GREEN}${C_BOLD}"
C_FLUFF="${C_GREEN}${C_BOLD}"
C_SETTING="${C_GREEN}${C_FAINT}"
C_TRACE="${C_FLUFF}${C_FAINT}"
C_TRACE2="${C_RESET}${C_FAINT}"
C_DEBUG="${C_SPECIAL_BLUE}"
C_ERROR_TEXT="${C_RESET}${C_BR_RED}${C_BOLD}"
}
_r_lowercase()
{
case "${BASH_VERSION}" in
[4-9]*|[1-9][0-9]*)
RVAL="${1,,}"
return
;;
esac
if [ ! -z "${ZSH_VERSION}" ]
then
RVAL="${1:l}"
return
fi
RVAL="`printf "$1" | tr '[:upper:]' '[:lower:]'`"
}
logging_initialize()
{
logging_initialize_color
}
logging_initialize "$@"
:
[ ! -z "${MULLE_EXEKUTOR_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-exekutor.sh" >&2
[ -z "${MULLE_LOGGING_SH}" ] && \
echo "mulle-logging.sh must be included before mulle-executor.sh" 2>&1 && exit 1
MULLE_EXEKUTOR_SH="included"
exekutor_print_arrow()
{
local arrow
[ -z "${MULLE_EXECUTABLE_PID}" ] && internal_fail "MULLE_EXECUTABLE_PID not set"
if [ -z "${MULLE_EXEKUTOR_LOG_DEVICE}" \
-a ! -z "${BASHPID}" \
-a "${MULLE_EXECUTABLE_PID}" != "${BASHPID}" ]
then
arrow="=[${BASHPID:-0}]=>"
else
arrow="==>"
fi
printf "%s" "${arrow}"
}
exekutor_print()
{
exekutor_print_arrow
local escaped
while [ $# -ne 0 ]
do
case "$1" in
*[^a-zA-Z0-9._-]*|"")
escaped="${1//\'/\'\"\'\"\'}"
printf "%.240s" " '${escaped}'"
;;
*)
printf "%.240s" " $1"
;;
esac
shift
done
printf '\n'
}
eval_exekutor_print()
{
exekutor_print_arrow
local escaped
while [ $# -ne 0 ]
do
printf "%s" " `echo \"$1\"`" # what was the point of that ?
shift
done
printf '\n'
}
exekutor_trace()
{
local printer="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" = 'YES' ]
then
if [ -z "${MULLE_EXEKUTOR_LOG_DEVICE}" ]
then
${printer} "$@" >&2
else
${printer} "$@" > "${MULLE_EXEKUTOR_LOG_DEVICE}"
fi
fi
}
exekutor_trace_output()
{
local printer="$1"; shift
local redirect="$1"; shift
local output="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" = 'YES' ]
then
if [ -z "${MULLE_EXEKUTOR_LOG_DEVICE}" ]
then
${printer} "$@" "${redirect}" "${output}" >&2
else
${printer} "$@" "${redirect}" "${output}" > "${MULLE_EXEKUTOR_LOG_DEVICE}"
fi
fi
}
exekutor()
{
exekutor_trace "exekutor_print" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
"$@"
MULLE_EXEKUTOR_RVAL=$?
return ${MULLE_EXEKUTOR_RVAL}
}
rexekutor()
{
exekutor_trace "exekutor_print" "$@"
"$@"
MULLE_EXEKUTOR_RVAL=$?
return ${MULLE_EXEKUTOR_RVAL}
}
eval_exekutor()
{
exekutor_trace "eval_exekutor_print" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
eval "$@"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
eval_rexekutor()
{
exekutor_trace "eval_exekutor_print" "$@"
eval "$@"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
_eval_exekutor()
{
exekutor_trace "eval_exekutor_print" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
eval "$@"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
redirect_exekutor()
{
local output="$1"; shift
exekutor_trace_output "exekutor_print" '>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( "$@" ) > "${output}"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
redirect_eval_exekutor()
{
local output="$1"; shift
exekutor_trace_output "eval_exekutor_print" '>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( eval "$@" ) > "${output}"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
redirect_append_exekutor()
{
local output="$1"; shift
exekutor_trace_output "exekutor_print" '>>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( "$@" ) >> "${output}"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
_redirect_append_eval_exekutor()
{
local output="$1"; shift
exekutor_trace_output "eval_exekutor_print" '>>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( eval "$@" ) >> "${output}"
MULLE_EXEKUTOR_RVAL=$?
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
_append_tee_exekutor()
{
local output="$1"; shift
local teeoutput="$1"; shift
exekutor_trace_output "eval_exekutor_print" '>>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( "$@" ) 2>&1 | tee -a "${teeoutput}" "${output}"
MULLE_EXEKUTOR_RVAL=${PIPESTATUS[0]}
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
_append_tee_eval_exekutor()
{
local output="$1"; shift
local teeoutput="$1"; shift
exekutor_trace_output "eval_exekutor_print" '>>' "${output}" "$@"
if [ "${MULLE_FLAG_EXEKUTOR_DRY_RUN}" = 'YES' ]
then
return
fi
( eval "$@" ) 2>&1 | tee -a "${teeoutput}" "${output}"
MULLE_EXEKUTOR_RVAL=${PIPESTATUS[0]}
[ "${MULLE_EXEKUTOR_RVAL}" = "${MULLE_EXEKUTOR_STRACKTRACE_RVAL:-18}" ] && stacktrace
return ${MULLE_EXEKUTOR_RVAL}
}
logging_tee_exekutor()
{
local output="$1"; shift
local teeoutput="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" != 'YES' ]
then
exekutor_print "$@" >> "${teeoutput}"
fi
exekutor_print "$@" >> "${output}"
_append_tee_exekutor "${output}" "${teeoutput}" "$@"
}
logging_tee_eval_exekutor()
{
local output="$1"; shift
local teeoutput="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" != 'YES' ]
then
eval_exekutor_print "$@" >> "${teeoutput}"
fi
eval_exekutor_print "$@" >> "${output}"
_append_tee_eval_exekutor "${output}" "${teeoutput}" "$@"
}
logging_redirekt_exekutor()
{
local output="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" != 'YES' ]
then
exekutor_print "$@" >> "${output}"
fi
redirect_append_exekutor "${output}" "$@"
}
logging_redirect_eval_exekutor()
{
local output="$1"; shift
if [ "${MULLE_FLAG_LOG_EXEKUTOR}" != 'YES' ]
then
eval_exekutor_print "$@" >> "${output}"
fi
_redirect_append_eval_exekutor "${output}" "$@"
}
rexecute_column_table_or_cat()
{
local separator="$1"; shift
local cmd
local column_cmds="mulle-column column cat"
if [ -z "${COLUMN}" ]
then
for cmd in ${column_cmds}
do
if COLUMN="`command -v "${cmd}" `"
then
break
fi
done
fi
if [ -z "${COLUMN}" ]
then
fail "No matching executable for any of ${column_cmds// /,} found"
fi
case "${COLUMN}" in
*column)
rexekutor "${COLUMN}" '-t' '-s' "${separator:-;}" "$@"
;;
*)
rexekutor "${COLUMN}" "$@"
;;
esac
}
:
[ ! -z "${MULLE_STRING_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-string.sh" >&2
MULLE_STRING_SH="included"
[ -z "${MULLE_BASHGLOBAL_SH}" ] && echo "mulle-bashglobal.sh must be included before mulle-file.sh" 2>&1 && exit 1
[ -z "${MULLE_COMPATIBILITY_SH}" ] && echo "mulle-compatibility.sh must be included before mulle-string.sh" 2>&1 && exit 1
r_append()
{
RVAL="${1}${2}"
}
r_concat()
{
local separator="${3:- }"
if [ -z "${1}" ]
then
RVAL="${2}"
else
if [ -z "${2}" ]
then
RVAL="${1}"
else
RVAL="${1}${separator}${2}"
fi
fi
}
concat()
{
r_concat "$@"
printf "%s\n" "$RVAL"
}
r_trim_whitespace()
{
RVAL="$*"
RVAL="${RVAL#"${RVAL%%[![:space:]]*}"}"
RVAL="${RVAL%"${RVAL##*[![:space:]]}"}"
}
r_remove_ugly()
{
local s="$1"
local separator="${2:- }"
local escaped
local dualescaped
local replacement
printf -v escaped '%q' "${separator}"
dualescaped="${escaped//\//\/\/}"
replacement="${separator}"
case "${separator}" in
*/*)
replacement="${escaped}"
;;
esac
local old
RVAL="${s}"
old=''
while [ "${RVAL}" != "${old}" ]
do
old="${RVAL}"
RVAL="${RVAL##[${separator}]}"
RVAL="${RVAL%%[${separator}]}"
RVAL="${RVAL//${dualescaped}${dualescaped}/${replacement}}"
done
}
r_colon_concat()
{
r_concat "$1" "$2" ":"
r_remove_ugly "${RVAL}" ":"
}
r_comma_concat()
{
r_concat "$1" "$2" ","
r_remove_ugly "${RVAL}" ","
}
r_semicolon_concat()
{
r_concat "$1" "$2" ";"
}
r_slash_concat()
{
r_concat "$1" "$2" "/"
r_remove_duplicate "${RVAL}" "/"
}
r_list_remove()
{
local sep="${3:- }"
RVAL="${sep}$1${sep}//${sep}$2${sep}/}"
RVAL="${RVAL##${sep}}"
RVAL="${RVAL%%${sep}}"
}
r_colon_remove()
{
r_list_remove "$1" "$2" ":"
}
r_comma_remove()
{
r_list_remove "$1" "$2" ","
}
r_space_concat()
{
concat_no_double_separator "$1" "$2" | string_remove_ugly_separators
}
r_add_line()
{
local lines="$1"
local line="$2"
if [ ! -z "${lines:0:1}" ]
then
if [ ! -z "${line:0:1}" ]
then
RVAL="${lines}"$'\n'"${line}"
else
RVAL="${lines}"
fi
else
RVAL="${line}"
fi
}
r_remove_line()
{
local lines="$1"
local search="$2"
local line
local delim
RVAL=
shell_disable_glob; IFS=$'\n'
for line in ${lines}
do
if [ "${line}" != "${search}" ]
then
RVAL="${RVAL}${delim}${line}"
delim=$'\n'
fi
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
}
r_remove_line_once()
{
local lines="$1"
local search="$2"
local line
local delim
RVAL=
shell_disable_glob; IFS=$'\n'
for line in ${lines}
do
if [ -z "${search}" -o "${line}" != "${search}" ]
then
RVAL="${RVAL}${delim}${line}"
delim=$'\n'
else
search=""
fi
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
}
r_get_last_line()
{
RVAL="$(sed -n '$p' <<< "$1")" # get last line
}
r_remove_last_line()
{
RVAL="$(sed '$d' <<< "$1")" # remove last line
}
find_item()
{
local line="$1"
local search="$2"
local delim="${3:-,}"
shell_is_extglob_enabled || internal_fail "need extglob enabled"
if [ ! -z "${ZSH_VERSION}" ]
then
case "${delim}${line}${delim}" in
*"${delim}${~search}${delim}"*)
return 0
;;
esac
else
case "${delim}${line}${delim}" in
*"${delim}${search}${delim}"*)
return 0
;;
esac
fi
return 1
}
find_empty_line_zsh()
{
local lines="$1"
case "${lines}" in
*$'\n'$'\n'*)
return 0
;;
esac
return 1
}
find_line_zsh()
{
local lines="$1"
local search="$2"
if [ -z "${search:0:1}" ]
then
if [ -z "${lines:0:1}" ]
then
return 0
fi
find_empty_line_zsh "${lines}"
return $?
fi
local rval
local line
rval=1
IFS=$'\n'
for line in ${lines}
do
if [ "${line}" = "${search}" ]
then
rval=0
break
fi
done
IFS="${DEFAULT_IFS}"
return $rval
}
find_line()
{
if [ ! -z "${ZSH_VERSION}" ]
then
find_line_zsh "$@"
return $?
fi
local lines="$1"
local search="$2"
local escaped_lines
local pattern
printf -v escaped_lines "%q" "
${lines}
"
printf -v pattern "%q" "${search}
"
pattern="${pattern:2}"
pattern="${pattern%???}"
local rval
rval=1
shell_is_extglob_enabled || internal_fail "extglob must be enabled"
if [ ! -z "${ZSH_VERSION}" ]
then
case "${escaped_lines}" in
*"\\n${~pattern}\\n"*)
rval=0
;;
esac
else
case "${escaped_lines}" in
*"\\n${pattern}\\n"*)
rval=0
;;
esac
fi
return $rval
}
r_count_lines()
{
local array="$1"
RVAL=0
local line
shell_disable_glob; IFS=$'\n'
for line in ${array}
do
RVAL=$((RVAL + 1))
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
}
r_add_unique_line()
{
local lines="$1"
local line="$2"
if [ -z "${line:0:1}" -o -z "${lines:0:1}" ]
then
RVAL="${lines}${line}"
return
fi
if find_line "${lines}" "${line}"
then
RVAL="${lines}"
return
fi
RVAL="${lines}
${line}"
}
r_remove_duplicate_lines()
{
RVAL="`awk '!x[$0]++' <<< "$@"`"
}
remove_duplicate_lines()
{
awk '!x[$0]++' <<< "$@"
}
remove_duplicate_lines_stdin()
{
awk '!x[$0]++'
}
r_reverse_lines()
{
local lines="$1"
local line
local delim
RVAL=
IFS=$'\n'
while read -r line
do
RVAL="${line}${delim}${RVAL}"
delim=$'\n'
done <<< "${lines}"
IFS="${DEFAULT_IFS}"
}
r_filepath_cleaned()
{
RVAL="$1"
[ -z "${RVAL}" ] && return
local old
old=''
while [ "${RVAL}" != "${old}" ]
do
old="${RVAL}"
RVAL="${RVAL//\/.\///}"
RVAL="${RVAL//\/\///}"
done
if [ -z "${RVAL}" ]
then
RVAL="${1:0:1}"
fi
}
r_filepath_concat()
{
local i
local s
local sep
local fallback
fallback=
shell_disable_glob
for i in "$@"
do
sep="/"
r_filepath_cleaned "${i}"
i="${RVAL}"
case "$i" in
"")
continue
;;
"."|"./")
if [ -z "${fallback}" ]
then
fallback="./"
fi
continue
;;
esac
case "$i" in
"/"|"/.")
if [ -z "${fallback}" ]
then
fallback="/"
fi
continue
;;
esac
if [ -z "${s}" ]
then
s="${fallback}$i"
else
case "${i}" in
/*)
s="${s}${i}"
;;
*)
s="${s}/${i}"
;;
esac
fi
done
shell_enable_glob
if [ ! -z "${s}" ]
then
r_filepath_cleaned "${s}"
else
RVAL="${fallback:0:1}" # / make ./ . again
fi
}
filepath_concat()
{
r_filepath_concat "$@"
[ ! -z "${RVAL}" ] && printf "%s\n" "${RVAL}"
}
r_upper_firstchar()
{
case "${BASH_VERSION}" in
[0123]*)
RVAL="`printf "${1:0:1}" | tr '[:lower:]' '[:upper:]'`"
RVAL="${RVAL}${1:1}"
;;
*)
if [ ! -z "${ZSH_VERSION}" ]
then
RVAL="${1:0:1}"
RVAL="${RVAL:u}${1:1}"
else
RVAL="${1^}"
fi
;;
esac
}
r_capitalize()
{
r_lowercase "$@"
r_upper_firstchar "${RVAL}"
}
r_uppercase()
{
case "${BASH_VERSION}" in
[0123]*)
RVAL="`printf "$1" | tr '[:lower:]' '[:upper:]'`"
;;
*)
if [ ! -z "${ZSH_VERSION}" ]
then
RVAL="${1:u}"
else
RVAL="${1^^}"
fi
;;
esac
}
r_lowercase()
{
case "${BASH_VERSION}" in
[0123]*)
RVAL="`printf "$1" | tr '[:upper:]' '[:lower:]'`"
;;
*)
if [ ! -z "${ZSH_VERSION}" ]
then
RVAL="${1:l}"
else
RVAL="${1,,}"
fi
;;
esac
}
r_identifier()
{
RVAL="${1//-/_}" # __
RVAL="${RVAL//[^a-zA-Z0-9]/_}"
case "${RVAL}" in
[0-9]*)
RVAL="_${RVAL}"
;;
esac
}
is_yes()
{
local s
case "$1" in
[yY][eE][sS]|Y|1)
return 0
;;
[nN][oO]|[nN]|0|"")
return 1
;;
*)
return 255
;;
esac
}
r_escaped_grep_pattern()
{
local s="$1"
s="${s//\\/\\\\}"
s="${s//\[/\\[}"
s="${s//\]/\\]}"
s="${s//\//\\/}"
s="${s//\$/\\$}"
s="${s//\*/\\*}"
s="${s//\./\\.}"
s="${s//\^/\\^}"
s="${s//\|/\\|}"
RVAL="$s"
}
r_escaped_sed_pattern()
{
local s="$1"
s="${s//\\/\\\\}"
s="${s//\[/\\[}"
s="${s//\]/\\]}"
s="${s//\//\\/}"
s="${s//\$/\\$}"
s="${s//\*/\\*}"
s="${s//\./\\.}"
s="${s//\^/\\^}"
RVAL="$s"
}
r_escaped_sed_replacement()
{
local s="$1"
s="${s//\\/\\\\}"
s="${s//\//\\/}"
s="${s//&/\\&}"
RVAL="$s"
}
r_escaped_spaces()
{
RVAL="${1// /\\ }"
}
r_escaped_backslashes()
{
RVAL="${1//\\/\\\\}"
}
r_escaped_singlequotes()
{
local quote
quote="'"
RVAL="${1//${quote}/${quote}\"${quote}\"${quote}}"
}
r_escaped_doublequotes()
{
RVAL="${*//\\/\\\\}"
RVAL="${RVAL//\"/\\\"}"
}
r_unescaped_doublequotes()
{
RVAL="${*//\\\"/\"}"
RVAL="${RVAL//\\\\/\\}"
}
r_escaped_shell_string()
{
printf -v RVAL '%q' "$*"
}
string_has_prefix()
{
[ "${1#$2}" != "$1" ]
}
string_has_suffix()
{
[ "${1%$2}" != "$1" ]
}
r_basename()
{
local filename="$1"
while :
do
case "${filename}" in
/)
RVAL="/"
return
;;
*/)
filename="${filename%?}"
;;
*)
RVAL="${filename##*/}"
return
;;
esac
done
}
r_dirname()
{
local filename="$1"
local last
while :
do
case "${filename}" in
/)
RVAL="${filename}"
return
;;
*/)
filename="${filename%?}"
continue
;;
esac
break
done
printf -v last '%q' "${filename##*/}"
RVAL="${filename%${last}}"
while :
do
case "${RVAL}" in
/)
return
;;
*/)
RVAL="${RVAL%?}"
;;
*)
RVAL="${RVAL:-.}"
return
;;
esac
done
}
_r_prefix_with_unquoted_string()
{
local s="$1"
local c="$2"
local prefix
local e_prefix
local head
while :
do
prefix="${s%%${c}*}" # a${
if [ "${prefix}" = "${_s}" ]
then
RVAL=
return 1
fi
e_prefix="${_s%%\\${c}*}" # a\\${ or whole string if no match
if [ "${e_prefix}" = "${_s}" ]
then
RVAL="${head}${prefix}"
return 0
fi
if [ "${#e_prefix}" -gt "${#prefix}" ]
then
RVAL="${head}${prefix}"
return 0
fi
e_prefix="${e_prefix}\\${c}"
head="${head}${e_prefix}"
s="${s:${#e_prefix}}"
done
}
_r_expand_string()
{
local prefix_opener
local prefix_closer
local identifier
local identifier_1
local identifier_2
local anything
local value
local default_value
local head
local found
while [ ${#_s} -ne 0 ]
do
_r_prefix_with_unquoted_string "${_s}" '${'
found=$?
prefix_opener="${RVAL}" # can be empty
if ! _r_prefix_with_unquoted_string "${_s}" '}'
then
if [ ${found} -eq 0 ]
then
log_error "missing '}'"
RVAL=
return 1
fi
else
prefix_closer="${RVAL}"
if [ ${found} -ne 0 -o ${#prefix_closer} -lt ${#prefix_opener} ]
then
_s="${_s:${#prefix_closer}}"
_s="${_s#\}}"
RVAL="${head}${prefix_closer}"
return 0
fi
fi
if [ ${found} -ne 0 ]
then
RVAL="${head}${_s}"
return 0
fi
head="${head}${prefix_opener}" # copy verbatim and continue
_s="${_s:${#prefix_opener}}"
_s="${_s#\$\{}"
anything=
identifier_1="${_s%%\}*}" # this can't fail
identifier_2="${_s%%:-*}"
if [ "${identifier_2}" = "${_s}" ]
then
identifier_2=""
fi
default_value=
if [ -z "${identifier_2}" -o ${#identifier_1} -lt ${#identifier_2} ]
then
identifier="${identifier_1}"
_s="${_s:${#identifier}}"
_s="${_s#\}}"
anything=
else
identifier="${identifier_2}"
_s="${_s:${#identifier}}"
_s="${_s#:-}"
anything="${_s}"
if [ ! -z "${anything}" ]
then
if ! _r_expand_string
then
return 1
fi
default_value="${RVAL}"
fi
fi
r_identifier "${identifier}"
identifier="${RVAL}"
if [ "${_expand}" = 'YES' ]
then
if [ ! -z "${ZSH_VERSION}" ]
then
value="${(P)identifier:-${default_value}}"
else
value="${!identifier:-${default_value}}"
fi
else
value="${default_value}"
fi
head="${head}${value}"
done
RVAL="${head}"
return 0
}
r_expanded_string()
{
local string="$1"
local expand="${2:-YES}"
local _s="${string}"
local _expand="${expand}"
local rval
_r_expand_string
rval=$?
return $rval
}
:
[ ! -z "${MULLE_INIT_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-init.sh" >&2
[ -z "${MULLE_STRING_SH}" ] && echo "mulle-string.sh must be included before mulle-init.sh" 2>&1 && exit 1
MULLE_INIT_SH="included"
r_dirname()
{
RVAL="$1"
while :
do
case "${RVAL}" in
/)
return
;;
*/)
RVAL="${RVAL%?}"
continue
;;
esac
break
done
local last
last="${RVAL##*/}"
RVAL="${RVAL%${last}}"
while :
do
case "${RVAL}" in
/)
return
;;
*/)
RVAL="${RVAL%?}"
;;
*)
RVAL="${RVAL:-.}"
return
;;
esac
done
}
r_prepend_path_if_relative()
{
case "$2" in
/*)
RVAL="$2"
;;
*)
RVAL="$1/$2"
;;
esac
}
r_resolve_symlinks()
{
local filepath
RVAL="$1"
filepath="`readlink "${RVAL}"`"
if [ $? -eq 0 ]
then
r_dirname "${RVAL}"
r_prepend_path_if_relative "${RVAL}" "${filepath}"
r_resolve_symlinks "${RVAL}"
fi
}
r_get_libexec_dir()
{
local executablepath="$1"
local subdir="$2"
local matchfile="$3"
local exedirpath
local prefix
case "${executablepath}" in
\.*|/*|~*)
;;
*)
executablepath="`command -v "${executablepath}"`"
;;
esac
r_resolve_symlinks "${executablepath}"
executablepath="${RVAL}"
r_dirname "${executablepath}"
exedirpath="${RVAL}"
r_dirname "${exedirpath}"
prefix="${RVAL}"
RVAL="${prefix}/libexec/${subdir}"
if [ ! -f "${RVAL}/${matchfile}" ]
then
RVAL="${exedirpath}/src"
fi
case "$RVAL" in
/*|~*)
;;
.)
RVAL="$PWD"
;;
*)
RVAL="$PWD/${RVAL}"
;;
esac
if [ ! -f "${RVAL}/${matchfile}" ]
then
unset RVAL
printf "%s\n" "$0 fatal error: Could not find \"${subdir}\" libexec (${PWD#${MULLE_USER_PWD}/})" >&2
exit 1
fi
}
call_main()
{
local flags="$1"; [ $# -ne 0 ] && shift
if [ -z "${flags}" ]
then
main "$@"
return $?
fi
local arg
local args
local sep
args=""
for arg in "$@"
do
printf -v args "%s%s%q" "${args}" "${sep}" "${arg}"
sep=" "
done
unset arg
eval main "${flags}" "${args}"
}
[ ! -z "${MULLE_OPTIONS_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-options.sh" >&2
[ -z "${MULLE_LOGGING_SH}" ] && echo "mulle-logging.sh must be included before mulle-options.sh" 2>&1 && exit 1
MULLE_OPTIONS_SH="included"
options_dump_env()
{
log_trace "ARGS:${C_TRACE2} ${MULLE_ARGUMENTS}"
log_trace "PWD :${C_TRACE2} `pwd -P 2> /dev/null`"
log_trace "ENV :${C_TRACE2} `env | sort`"
log_trace "LS :${C_TRACE2} `ls -a1F`"
}
options_setup_trace()
{
if [ "${MULLE_FLAG_LOG_ENVIRONMENT}" = YES ]
then
options_dump_env
fi
case "${1}" in
VERBOSE)
MULLE_FLAG_LOG_VERBOSE='YES'
;;
FLUFF)
MULLE_FLAG_LOG_FLUFF='YES'
MULLE_FLAG_LOG_VERBOSE='YES'
;;
TRACE)
MULLE_FLAG_LOG_EXEKUTOR='YES'
MULLE_FLAG_LOG_FLUFF='YES'
MULLE_FLAG_LOG_VERBOSE='YES'
;;
1848)
MULLE_FLAG_LOG_SETTINGS='YES'
MULLE_FLAG_LOG_FLUFF='YES'
MULLE_FLAG_LOG_VERBOSE='YES'
if [ "${MULLE_TRACE_POSTPONE}" != 'YES' ]
then
PS4="+ ${ps4string} + "
fi
return 0
;;
esac
return 2
}
_options_technical_flags_usage()
{
local DELIMITER="${1:- : }"
local align="${2:-YES}"
local S
if [ "${align}" = 'YES' ]
then
S=" "
fi
cat <<EOF
-n${S}${S}${S}${DELIMITER}dry run
-s${S}${S}${S}${DELIMITER}be silent
-v${S}${S}${S}${DELIMITER}be verbose (increase with -vv, -vvv)
EOF
if [ ! -z "${MULLE_TRACE}" ]
then
cat <<EOF
-ld${S}${S}${DELIMITER}additional debug output
-le${S}${S}${DELIMITER}additional environment debug output
-lx${S}${S}${DELIMITER}external command execution log output
EOF
fi
}
options_technical_flags_usage()
{
_options_technical_flags_usage "$@" | sort
}
before_trace_fail()
{
[ "${MULLE_TRACE}" = '1848' ] || \
fail "option \"$1\" must be specified after -t"
}
after_trace_warning()
{
[ "${MULLE_TRACE}" = '1848' ] && \
log_warning "warning: ${MULLE_EXECUTABLE_FAIL_PREFIX}: $1 after -t invalidates -t"
}
options_technical_flags()
{
local flag="$1"
case "${flag}" in
-n|--dry-run)
MULLE_FLAG_EXEKUTOR_DRY_RUN='YES'
;;
-ld|--log-debug)
MULLE_FLAG_LOG_DEBUG='YES'
;;
-lD)
MULLE_FLAG_LOG_DEBUG='YES'
return # don't propagate
;;
-lD*D)
MULLE_FLAG_LOG_DEBUG='YES'
flag="${flag%D}"
;;
-le|--log-environment)
MULLE_FLAG_LOG_ENVIRONMENT='YES'
;;
-lE)
MULLE_FLAG_LOG_ENVIRONMENT='YES'
return # don't propagate
;;
-lE*E)
MULLE_FLAG_LOG_ENVIRONMENT='YES'
flag="${flag%E}"
;;
-ls|--log-settings)
MULLE_FLAG_LOG_SETTINGS='YES'
;;
-lS)
MULLE_FLAG_LOG_SETTINGS='YES'
return # don't propagate
;;
-lS*S)
MULLE_FLAG_LOG_SETTINGS='YES'
flag="${flag%S}"
;;
-lx|--log-exekutor|--log-execution)
MULLE_FLAG_LOG_EXEKUTOR='YES'
;;
-lX)
MULLE_FLAG_LOG_EXEKUTOR='YES'
return # don't propagate
;;
-lX*X)
MULLE_FLAG_LOG_EXEKUTOR='YES'
flag="${flag%X}"
;;
-l-)
MULLE_FLAG_LOG_DEBUG=
MULLE_FLAG_LOG_ENVIRONMENT=
MULLE_FLAG_LOG_EXEKUTOR=
MULLE_FLAG_LOG_SETTINGS=
;;
-t|--trace)
MULLE_TRACE='1848'
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='%1x:%I' # TODO: fix for zsh
else
ps4string='${BASH_SOURCE[0]##*/}:${LINENO}'
fi
;;
-tfpwd|--trace-full-pwd)
before_trace_fail "${flag}"
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='%1x:%I \"\w\"'
else
ps4string='${BASH_SOURCE[0]##*/}:${LINENO} \"\w\"'
fi
;;
-tp|--trace-profile)
before_trace_fail "${flag}"
case "${MULLE_UNAME}" in
'')
internal_fail 'MULLE_UNAME must be set by now'
;;
linux)
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='$(date "+%s.%N (%1x:%I)")'
else
ps4string='$(date "+%s.%N (${BASH_SOURCE[0]##*/}:${LINENO})")'
fi
;;
*)
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='$(date "+%s (%1x:%I)")'
else
ps4string='$(date "+%s (${BASH_SOURCE[0]##*/}:${LINENO})")'
fi
;;
esac
return # don't propagate
;;
-tpo|--trace-postpone)
before_trace_fail "${flag}"
MULLE_TRACE_POSTPONE='YES'
;;
-tpwd|--trace-pwd)
before_trace_fail "${flag}"
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='%1x:%I \".../\W\"'
else
ps4string='${BASH_SOURCE[0]##*/}:${LINENO} \".../\W\"'
fi
;;
-tx|--trace-immediately)
set -x
;;
-t-)
MULLE_TRACE=
set +x
;;
-T)
MULLE_TRACE='1848'
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='%1x:%I'
else
ps4string='${BASH_SOURCE[0]##*/}:${LINENO}'
fi
return # don't propagate
;;
-T*T)
MULLE_TRACE='1848'
if [ ! -z "${ZSH_VERSION}" ]
then
ps4string='%1x:%I'
else
ps4string='${BASH_SOURCE[0]##*/}:${LINENO}'
fi
flag="${flag%T}"
;;
-s|--silent)
MULLE_FLAG_LOG_TERSE='YES'
;;
-v-|--no-verbose)
MULLE_FLAG_LOG_TERSE=
;;
-v|--verbose)
after_trace_warning "${flag}"
MULLE_TRACE='VERBOSE'
;;
-vv|--very-verbose)
after_trace_warning "${flag}"
MULLE_TRACE='FLUFF'
;;
-vvv|--very-very-verbose)
after_trace_warning "${flag}"
MULLE_TRACE='TRACE'
;;
-V)
after_trace_warning "${flag}"
MULLE_TRACE='VERBOSE'
return # don't propagate
;;
-VV)
after_trace_warning "${flag}"
MULLE_TRACE='FLUFF'
return # don't propagate
;;
-VVV)
after_trace_warning "${flag}"
MULLE_TRACE='TRACE'
return # don't propagate
;;
--clear-flags)
MULLE_TECHNICAL_FLAGS=''
return 0
;;
--list-technical-flags)
echo "\
--clear-flags
--dry-run
--log-debug
--log-environment
--log-settings
--log-exekutor
--trace
--trace-full-pwd
--trace-profile
--trace-postpone
--trace-pwd
--trace-immediately
--silent
--verbose
--very-verbose
--very-very-verbose"
return 0
;;
*)
return 1
;;
esac
if [ -z "${MULLE_TECHNICAL_FLAGS}" ]
then
MULLE_TECHNICAL_FLAGS="${flag}"
else
MULLE_TECHNICAL_FLAGS="${MULLE_TECHNICAL_FLAGS} ${flag}"
fi
return 0
}
options_unpostpone_trace()
{
if [ ! -z "${MULLE_TRACE_POSTPONE}" -a "${MULLE_TRACE}" = '1848' ]
then
set -x
PS4="+ ${ps4string} + "
fi
}
_options_mini_main()
{
while [ $# -ne 0 ]
do
if options_technical_flags "$1"
then
shift
continue
fi
break
done
options_setup_trace "${MULLE_TRACE}"
}
:
[ ! -z "${MULLE_PATH_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-path.sh" >&2
[ -z "${MULLE_STRING_SH}" ] && echo "mulle-string.sh must be included before mulle-path.sh" 2>&1 && exit 1
MULLE_PATH_SH="included"
r_path_depth()
{
local name="$1"
local depth
depth=0
if [ ! -z "${name}" ]
then
depth=1
while [ "$name" != "." -a "${name}" != '/' ]
do
r_dirname "${name}"
name="${RVAL}"
depth=$((depth + 1))
done
fi
RVAL="${depth}"
}
r_extensionless_basename()
{
r_basename "$@"
RVAL="${RVAL%.*}"
}
r_path_extension()
{
r_basename "$@"
case "${RVAL}" in
*.*)
RVAL="${RVAL##*.}"
return
;;
esac
RVAL=""
}
_r_canonicalize_dir_path()
{
RVAL="`
(
cd "$1" 2>/dev/null &&
pwd -P
)`"
}
_r_canonicalize_file_path()
{
local component
local directory
r_basename "$1"
component="${RVAL}"
r_dirname "$1"
directory="${RVAL}"
if ! _r_canonicalize_dir_path "${directory}"
then
return 1
fi
RVAL="${RVAL}/${component}"
return 0
}
r_canonicalize_path()
{
[ -z "$1" ] && internal_fail "empty path"
if [ -d "$1" ]
then
_r_canonicalize_dir_path "$1"
else
_r_canonicalize_file_path "$1"
fi
}
__r_relative_path_between()
{
RVAL=''
[ $# -ge 1 ] && [ $# -le 2 ] || return 1
current="${2:+"$1"}"
target="${2:-"$1"}"
[ "$target" != . ] || target=/
target="/${target##/}"
[ "$current" != . ] || current=/
current="${current:="/"}"
current="/${current##/}"
appendix="${target##/}"
relative=''
while appendix="${target#"$current"/}"
[ "$current" != '/' ] && [ "$appendix" = "$target" ]; do
if [ "$current" = "$appendix" ]; then
relative="${relative:-.}"
RVAL="${relative#/}"
return 0
fi
current="${current%/*}"
relative="$relative${relative:+/}.."
done
RVAL="$relative${relative:+${appendix:+/}}${appendix#/}"
}
_r_relative_path_between()
{
local a
local b
if [ "${MULLE_TRACE_PATHS_FLIP_X}" = 'YES' ]
then
set +x
fi
r_simplified_path "$1"
a="${RVAL}"
r_simplified_path "$2"
b="${RVAL}"
[ -z "${a}" ] && internal_fail "Empty path (\$1)"
[ -z "${b}" ] && internal_fail "Empty path (\$2)"
__r_relative_path_between "${b}" "${a}" # flip args (historic)
if [ "${MULLE_TRACE_PATHS_FLIP_X}" = 'YES' ]
then
set -x
fi
}
r_relative_path_between()
{
local a="$1"
local b="$2"
case "${a}" in
"")
internal_fail "First path is empty"
;;
../*|*/..|*/../*|..)
internal_fail "Path \"${a}\" mustn't contain .."
;;
./*|*/.|*/./*|.)
internal_fail "Filename \"${a}\" mustn't contain component \".\""
;;
/*)
case "${b}" in
"")
internal_fail "Second path is empty"
;;
../*|*/..|*/../*|..)
internal_fail "Filename \"${b}\" mustn't contain \"..\""
;;
./*|*/.|*/./*|.)
internal_fail "Filename \"${b}\" mustn't contain \".\""
;;
/*)
;;
*)
internal_fail "Mixing absolute filename \"${a}\" and relative filename \"${b}\""
;;
esac
;;
*)
case "${b}" in
"")
internal_fail "Second path is empty"
;;
../*|*/..|*/../*|..)
internal_fail "Filename \"${b}\" mustn't contain component \"..\"/"
;;
./*|*/.|*/./*|.)
internal_fail "Filename \"${b}\" mustn't contain component \".\""
;;
/*)
internal_fail "Mixing relative filename \"${a}\" and absolute filename \"${b}\""
;;
*)
;;
esac
;;
esac
_r_relative_path_between "${a}" "${b}"
}
r_compute_relative()
{
local name="$1"
local depth
local relative
r_path_depth "${name}"
depth="${RVAL}"
if [ "${depth}" -gt 1 ]
then
relative=".."
while [ "$depth" -gt 2 ]
do
relative="${relative}/.."
depth=$(($depth - 1))
done
fi
RVAL="${relative}"
}
r_physicalpath()
{
if [ -d "$1" ]
then
RVAL="`( cd "$1" && pwd -P ) 2>/dev/null `"
return $?
fi
local dir
local file
r_dirname "$1"
dir="${RVAL}"
r_basename "$1"
file="${RVAL}"
if ! r_physicalpath "${dir}"
then
RVAL=
return 1
fi
r_filepath_concat "${RVAL}" "${file}"
}
physicalpath()
{
if ! r_physicalpath "$@"
then
return 1
fi
printf "%s\n" "${RVAL}"
}
is_absolutepath()
{
case "${1}" in
/*|~*)
return 0
;;
*)
return 1
;;
esac
}
is_relativepath()
{
case "${1}" in
""|/*|~*)
return 1
;;
*)
return 0
;;
esac
}
r_absolutepath()
{
local directory="$1"
local working="${2:-${PWD}}"
case "${directory}" in
"")
RVAL=''
;;
/*|~*)
RVAL="${directory}"
;;
*)
RVAL="${working}/${directory}"
;;
esac
}
r_simplified_absolutepath()
{
local directory="$1"
local working="${2:-${PWD}}"
case "${1}" in
"")
RVAL=''
;;
/*|~*)
r_simplified_path "${directory}"
;;
*)
r_simplified_path "${working}/${directory}"
;;
esac
}
r_symlink_relpath()
{
local a
local b
r_absolutepath "$1"
a="$RVAL"
r_absolutepath "$2"
b="$RVAL"
_r_relative_path_between "${a}" "${b}"
}
_r_simplified_path()
{
local filepath="$1"
[ -z "${filepath}" ] && fail "empty path given"
local i
local last
local result
local remove_empty
remove_empty='NO' # remove trailing slashes
IFS="/"
shell_disable_glob
for i in ${filepath}
do
shell_enable_glob
case "$i" in
\.)
remove_empty='YES'
continue
;;
\.\.)
remove_empty='YES'
if [ "${last}" = "|" ]
then
continue
fi
if [ ! -z "${last}" -a "${last}" != ".." ]
then
r_remove_last_line "${result}"
result="${RVAL}"
r_get_last_line "${result}"
last="${RVAL}"
continue
fi
;;
~*)
fail "Can't deal with ~ filepaths"
;;
"")
if [ "${remove_empty}" = 'NO' ]
then
last='|'
result='|'
fi
continue
;;
esac
remove_empty='YES'
last="${i}"
r_add_line "${result}" "${i}"
result="${RVAL}"
done
IFS="${DEFAULT_IFS}"
shell_enable_glob
if [ -z "${result}" ]
then
RVAL="."
return
fi
if [ "${result}" = '|' ]
then
RVAL="/"
return
fi
RVAL="`tr -d '|' <<< "${result}" | tr '\012' '/'`"
RVAL="${RVAL%/}"
}
r_simplified_path()
{
case "${1}" in
""|".")
RVAL="."
;;
*/|*\.\.*|*\./*|*/\.)
if [ "${MULLE_TRACE_PATHS_FLIP_X}" = 'YES' ]
then
set +x
fi
_r_simplified_path "$@"
if [ "${MULLE_TRACE_PATHS_FLIP_X}" = 'YES' ]
then
set -x
fi
;;
*)
RVAL="$1"
;;
esac
}
assert_sane_subdir_path()
{
r_simplified_path "$1"
case "${RVAL}" in
"")
fail "refuse empty subdirectory \"$1\""
exit 1
;;
\$*|~|..|.|/*)
fail "refuse unsafe subdirectory path \"$1\""
;;
esac
}
r_assert_sane_path()
{
r_simplified_path "$1"
case "${RVAL}" in
\$*|~|${HOME}|..|.)
log_error "refuse unsafe path \"$1\""
exit 1
;;
/tmp/*)
;;
""|/*)
local filepath
filepath="${RVAL}"
r_path_depth "${filepath}"
if [ "${RVAL}" -le 2 ]
then
fail "Refuse suspicious path \"$1\""
fi
RVAL="${filepath}"
;;
esac
}
:
[ ! -z "${MULLE_FILE_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-file.sh" >&2
[ -z "${MULLE_BASHGLOBAL_SH}" ] && echo "mulle-bashglobal.sh must be included before mulle-file.sh" 2>&1 && exit 1
[ -z "${MULLE_PATH_SH}" ] && echo "mulle-path.sh must be included before mulle-file.sh" 2>&1 && exit 1
[ -z "${MULLE_EXEKUTOR_SH}" ] && echo "mulle-exekutor.sh must be included before mulle-file.sh" 2>&1 && exit 1
MULLE_FILE_SH="included"
mkdir_if_missing()
{
[ -z "$1" ] && internal_fail "empty path"
if [ -d "$1" ]
then
return 0
fi
log_fluff "Creating directory \"$1\" (${PWD#${MULLE_USER_PWD}/})"
local rval
exekutor mkdir -p "$1"
rval="$?"
if [ "${rval}" -eq 0 ]
then
return 0
fi
if [ -L "$1" ]
then
r_resolve_symlinks "$1"
if [ ! -d "${RVAL}" ]
then
fail "failed to create directory \"$1\" as a symlink is there"
fi
return 0
fi
if [ -f "$1" ]
then
fail "failed to create directory \"$1\" because a file is there"
fi
fail "failed to create directory \"$1\" from $PWD ($rval)"
}
r_mkdir_parent_if_missing()
{
local dstdir="$1"
r_dirname "${dstdir}"
case "${RVAL}" in
""|\.)
;;
*)
mkdir_if_missing "${RVAL}"
return $?
;;
esac
return 1
}
mkdir_parent_if_missing()
{
r_mkdir_parent_if_missing "$@"
}
dir_is_empty()
{
[ -z "$1" ] && internal_fail "empty path"
if [ ! -d "$1" ]
then
return 2
fi
local empty
empty="`ls -A "$1" 2> /dev/null`"
[ -z "$empty" ]
}
rmdir_safer()
{
[ -z "$1" ] && internal_fail "empty path"
if [ -d "$1" ]
then
r_assert_sane_path "$1"
exekutor chmod -R ugo+wX "${RVAL}" >&2 || fail "Failed to make \"${RVAL}\" writable"
exekutor rm -rf "${RVAL}" >&2 || fail "failed to remove \"${RVAL}\""
fi
}
rmdir_if_empty()
{
[ -z "$1" ] && internal_fail "empty path"
if dir_is_empty "$1"
then
exekutor rmdir "$1" >&2 || fail "failed to remove $1"
fi
}
_create_file_if_missing()
{
local filepath="$1" ; shift
[ -z "${filepath}" ] && internal_fail "empty path"
if [ -f "${filepath}" ]
then
return
fi
local directory
r_dirname "${filepath}"
directory="${RVAL}"
if [ ! -z "${directory}" ]
then
mkdir_if_missing "${directory}"
fi
log_fluff "Creating \"${filepath}\""
if [ ! -z "$*" ]
then
redirect_exekutor "${filepath}" printf "%s\n" "$*" || fail "failed to create \"{filepath}\""
else
exekutor touch "${filepath}" || fail "failed to create \"${filepath}\""
fi
}
merge_line_into_file()
{
local line="$1"
local filepath="$2"
if fgrep -s -q -x "${line}" "${filepath}" 2> /dev/null
then
return
fi
redirect_append_exekutor "${filepath}" printf "%s\n" "${line}"
}
create_file_if_missing()
{
_create_file_if_missing "$1" "# intentionally blank file"
}
_remove_file_if_present()
{
[ -z "$1" ] && internal_fail "empty path"
if ! exekutor rm -f "$1" 2> /dev/null
then
exekutor chmod u+w "$1" || fail "Failed to make $1 writable"
exekutor rm -f "$1" || fail "failed to remove \"$1\""
fi
return 0
}
remove_file_if_present()
{
if [ -e "$1" -o -L "$1" ] && _remove_file_if_present "$1"
then
log_fluff "Removed \"${1#${PWD}/}\" (${PWD#${MULLE_USER_PWD}/})"
return 0
fi
return 1
}
_make_tmp_in_dir_mktemp()
{
local tmpdir="$1"
local name="$2"
local filetype="$3"
case "${filetype}" in
*d*)
TMPDIR="${tmpdir}" exekutor mktemp -d "${name}-XXXXXXXX"
;;
*)
TMPDIR="${tmpdir}" exekutor mktemp "${name}-XXXXXXXX"
;;
esac
}
_r_make_tmp_in_dir_uuidgen()
{
local UUIDGEN="$1"; shift
local tmpdir="$1"
local name="$2"
local filetype="$3"
local uuid
local fluke
local MKDIR
local TOUCH
MKDIR="$(command -v mkdir)"
TOUCH="$(command -v touch)"
[ -z "${MKDIR}" ] && fail "No \"mkdir\" found in PATH ($PATH)"
[ -z "${TOUCH}" ] && fail "No \"touch\" found in PATH ($PATH)"
fluke=0
RVAL=''
while :
do
uuid="`"${UUIDGEN}"`" || internal_fail "uuidgen failed"
RVAL="${tmpdir}/${name}-${uuid:0:8}"
case "${filetype}" in
*d*)
exekutor "${MKDIR}" "${RVAL}" 2> /dev/null && return 0
;;
*)
exekutor "${TOUCH}" "${RVAL}" 2> /dev/null && return 0
;;
esac
if [ ! -e "${RVAL}" ]
then
fluke=$((fluke + 1 ))
if [ "${fluke}" -lt 3 ]
then
fail "Could not (even repeatedly) create \"${RVAL}\" (${filetype:-f})"
fi
fi
done
}
_r_make_tmp_in_dir()
{
local tmpdir="$1"
local name="$2"
local filetype="$3"
mkdir_if_missing "${tmpdir}"
[ ! -w "${tmpdir}" ] && fail "${tmpdir} does not exist or is not writable"
name="${name:-${MULLE_EXECUTABLE_NAME}}"
name="${name:-mulle}"
local UUIDGEN
UUIDGEN="`command -v "uuidgen"`"
if [ ! -z "${UUIDGEN}" ]
then
_r_make_tmp_in_dir_uuidgen "${UUIDGEN}" "${tmpdir}" "${name}" "${filetype}"
return $?
fi
RVAL="`_make_tmp_in_dir_mktemp "${tmpdir}" "${name}" "${filetype}"`"
return $?
}
r_make_tmp()
{
local name="$1"
local filetype="$2"
local tmpdir
tmpdir=
case "${MULLE_UNAME}" in
darwin)
;;
*)
r_filepath_cleaned "${TMPDIR}"
tmpdir="${RVAL}"
;;
esac
tmpdir="${tmpdir:-/tmp}"
_r_make_tmp_in_dir "${tmpdir}" "${name}" "${filetype}"
}
r_make_tmp_file()
{
r_make_tmp "$1" "f"
}
r_make_tmp_directory()
{
r_make_tmp "$1" "d"
}
r_resolve_all_path_symlinks()
{
local filepath="$1"
local resolved
r_resolve_symlinks "${filepath}"
resolved="${RVAL}"
local filename
local directory
local resolved
r_dirname "${resolved}"
directory="${RVAL}"
case "${directory}" in
''|'/')
RVAL="${resolved}"
;;
*)
r_basename "${resolved}"
filename="${RVAL}"
r_resolve_all_path_symlinks "${directory}"
r_filepath_concat "${RVAL}" "${filename}"
;;
esac
}
r_realpath()
{
[ -e "$1" ] || fail "only use r_realpath on existing files ($1)"
r_resolve_symlinks "$1"
r_canonicalize_path "${RVAL}"
}
create_symlink()
{
local url="$1" # URL of the clone
local stashdir="$2" # stashdir of this clone (absolute or relative to $PWD)
local absolute="$3"
[ -e "${url}" ] || fail "${C_RESET}${C_BOLD}${url}${C_ERROR} does not exist (${PWD#${MULLE_USER_PWD}/})"
[ ! -z "${absolute}" ] || fail "absolute must be YES or NO"
r_absolutepath "${url}"
r_realpath "${RVAL}"
url="${RVAL}" # resolve symlinks
local directory
r_dirname "${stashdir}"
directory="${RVAL}"
mkdir_if_missing "${directory}"
r_realpath "${directory}"
directory="${RVAL}" # resolve symlinks
if [ "${absolute}" = 'NO' ]
then
r_symlink_relpath "${url}" "${directory}"
url="${RVAL}"
fi
local oldlink
if [ -L "${oldlink}" ]
then
oldlink="`readlink "${stashdir}"`"
fi
if [ -z "${oldlink}" -o "${oldlink}" != "${url}" ]
then
exekutor ln -s -f "${url}" "${stashdir}" >&2 || \
fail "failed to setup symlink \"${stashdir}\" (to \"${url}\")"
fi
}
modification_timestamp()
{
case "${MULLE_UNAME}" in
linux|mingw)
stat --printf "%Y\n" "$1"
;;
* )
stat -f "%m" "$1"
;;
esac
}
lso()
{
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}'
}
file_is_binary()
{
local result
result="`file -b --mime-encoding "$1"`"
[ "${result}" = "binary" ]
}
dir_has_files()
{
local dirpath="$1"; shift
local flags
case "$1" in
f)
flags="-type f"
shift
;;
d)
flags="-type d"
shift
;;
esac
local empty
empty="`rexekutor find "${dirpath}" -xdev \
-mindepth 1 \
-maxdepth 1 \
-name "[a-zA-Z0-9_-]*" \
${flags} \
"$@" \
-print 2> /dev/null`"
[ ! -z "$empty" ]
}
dirs_contain_same_files()
{
log_entry "dirs_contain_same_files" "$@"
local etcdir="$1"
local sharedir="$2"
if [ ! -d "${etcdir}" -o ! -e "${etcdir}" ]
then
internal_fail "Both directories \"${etcdir}\" and \"${sharedir}\" need to exist"
fi
etcdir="${etcdir%%/}"
sharedir="${sharedir%%/}"
local etcfile
local sharefile
local filename
IFS=$'\n'; shell_disable_glob
for sharefile in `find ${sharedir} \! -type d -print`
do
IFS="${DEFAULT_IFS}"; shell_enable_glob
filename="${sharefile#${sharedir}/}"
etcfile="${etcdir}/${filename}"
if ! diff -q -b "${etcfile}" "${sharefile}" > /dev/null
then
return 2
fi
done
IFS=$'\n'; shell_disable_glob
for etcfile in `find ${etcdir} \! -type d -print`
do
IFS="${DEFAULT_IFS}"; shell_enable_glob
filename="${etcfile#${etcdir}/}"
sharefile="${sharedir}/${filename}"
if [ ! -e "${sharefile}" ]
then
return 2
fi
done
IFS="${DEFAULT_IFS}"; shell_enable_glob
return 0
}
inplace_sed()
{
local tmpfile
local args
local filename
local rval
case "${MULLE_UNAME}" in
darwin|freebsd)
while [ $# -ne 1 ]
do
r_escaped_shell_string "$1"
r_concat "${args}" "${RVAL}"
args="${RVAL}"
shift
done
filename="$1"
if [ ! -w "${filename}" ]
then
if [ ! -e "${filename}" ]
then
fail "\"${filename}\" does not exist"
fi
fail "\"${filename}\" is not writable"
fi
r_make_tmp
tmpfile="${RVAL}"
redirect_eval_exekutor "${tmpfile}" 'sed' "${args}" "'${filename}'"
rval=$?
if [ $rval -eq 0 ]
then
exekutor cp "${tmpfile}" "${filename}"
fi
_remove_file_if_present "${tmpfile}" # don't fluff log :)
;;
*)
exekutor sed -i'' "$@"
rval=$?
;;
esac
return ${rval}
}
:
[ "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' -a ! -z "${MULLE_ARRAY_SH}" ] && \
echo "double inclusion of mulle-array.sh" >&2
[ -z "${MULLE_LOGGING_SH}" ] && echo "mulle-logging.sh must be included before mulle-array.sh" 2>&1 && exit 1
MULLE_ARRAY_SH="included"
array_value_check()
{
local value="$1"
case "${value}" in
*$'\n'*)
internal_fail "\"${value}\" has unescaped linefeeds"
;;
esac
}
r_get_line_at_index()
{
local array="$1"
local i="${2:-0}"
shell_disable_glob; IFS=$'\n'
for RVAL in ${array}
do
if [ $i -eq 0 ]
then
IFS="${DEFAULT_IFS}" ; shell_enable_glob
return 0
fi
i=$((i - 1))
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
return 1
}
r_insert_line_at_index()
{
local array="$1"
local i="$2"
local value="$3"
array_value_check "${value}"
local line
local added='NO'
local rval
RVAL=
rval=1
shell_disable_glob; IFS=$'\n'
for line in ${array}
do
if [ $i -eq 0 ]
then
r_add_line "${RVAL}" "${value}"
rval=0
fi
r_add_line "${RVAL}" "${line}"
i=$((i - 1))
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
if [ $i -eq 0 ]
then
r_add_line "${RVAL}" "${value}"
rval=0
fi
return $rval
}
r_lines_in_range()
{
local array="$1"
local i="$2"
local n="$3"
declare -a bash_array
declare -a res_array
IFS=$'\n' read -r -d '' -a bash_array <<< "${array}"
local j
local sentinel
sentinel=$((i + n))
j=0
while [ $i -lt ${sentinel} ]
do
res_array[${j}]="${bash_array[${i}]}"
i=$((i + 1))
j=$((j + 1))
done
RVAL="${res_array[*]}"
}
assoc_array_key_check()
{
local key="$1"
[ -z "${key}" ] && internal_fail "key is empty"
local identifier
r_identifier "${key}"
identifier="${RVAL}"
[ "${identifier}" != "${key}" -a "${identifier}" != "_${key}" ] && internal_fail "\"${key}\" has non-identifier characters"
}
assoc_array_value_check()
{
array_value_check "$@"
}
_r_assoc_array_add()
{
local array="$1"
local key="$2"
local value="$3"
assoc_array_key_check "${key}"
assoc_array_value_check "${value}"
r_add_line "${array}" "${key}=${value}"
}
_r_assoc_array_remove()
{
local array="$1"
local key="$2"
local line
local delim
RVAL=
shell_disable_glob; IFS=$'\n'
for line in ${array}
do
case "${line}" in
"${key}="*)
;;
*)
RVAL="${line}${delim}${RVAL}"
delim=$'\n'
;;
esac
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
}
r_assoc_array_get()
{
local array="$1"
local key="$2"
local line
local rval
RVAL=
rval=1
shell_disable_glob; IFS=$'\n'
for line in ${array}
do
case "${line}" in
"${key}="*)
RVAL="${line#*=}"
rval=0
break
;;
esac
done
IFS="${DEFAULT_IFS}" ; shell_enable_glob
return $rval
}
assoc_array_all_keys()
{
local array="$1"
sed -n 's/^\([^=]*\)=.*$/\1/p' <<< "${array}"
}
assoc_array_all_values()
{
local array="$1"
sed -n 's/^[^=]*=\(.*\)$/\1/p' <<< "${array}"
}
r_assoc_array_set()
{
local array="$1"
local key="$2"
local value="$3"
if [ -z "${value}" ]
then
_r_assoc_array_remove "${array}" "${key}"
return
fi
local old_value
r_assoc_array_get "${array}" "${key}"
old_value="${RVAL}"
if [ ! -z "${old_value}" ]
then
_r_assoc_array_remove "${array}" "${key}"
array="${RVAL}"
fi
_r_assoc_array_add "${array}" "${key}" "${value}"
}
assoc_array_merge_with_array()
{
local array1="$1"
local array2="$2"
printf "%s%s\n" "${array2}" "${array1}" | sort -u -t'=' -k1,1
}
assoc_array_augment_with_array()
{
local array1="$1"
local array2="$2"
printf "%s%s\n" "${array1}" "${array2}" | sort -u -t'=' -k1,1
}
:
[ ! -z "${MULLE_CASE_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-case.sh" >&2
MULLE_CASE_SH="included"
_r_tweaked_de_camel_case()
{
local s="$1"
local output
local state
local collect
local c
local d
s="${s//ObjC/Objc}"
state='start'
while [ ! -z "${s}" ]
do
d="${c}"
c="${s:0:1}"
s="${s:1}"
case "${state}" in
'start')
case "${c}" in
[A-Z])
state="upper";
collect="${collect}${c}"
continue
;;
*)
state="lower"
;;
esac
;;
'upper')
case "${c}" in
[A-Z])
collect="${collect}${c}"
continue
;;
*)
if [ ! -z "${output}" -a ! -z "${collect}" ]
then
if [ ! -z "${collect:1}" ]
then
output="${output}_${collect%?}_${collect#${collect%?}}"
else
output="${output}_${collect}"
fi
else
output="${output}${collect}"
fi
collect=""
state="lower"
;;
esac
;;
'lower')
case "${c}" in
[A-Z])
output="${output}${collect}"
collect="${c}"
state="upper"
continue
;;
esac
;;
esac
output="${output}${c}"
done
if [ ! -z "${output}" -a ! -z "${collect}" ]
then
output="${output}_${collect}"
else
output="${output}${collect}"
fi
RVAL="${output}"
}
r_tweaked_de_camel_case()
{
LC_ALL=C _r_tweaked_de_camel_case "$@"
}
r_de_camel_case_upcase_identifier()
{
r_tweaked_de_camel_case "$1"
r_identifier "${RVAL}"
r_uppercase "${RVAL}"
case "${RVAL}" in
[A-Za-z_]*)
;;
*)
RVAL="_${RVAL}"
;;
esac
}
MULLE_PARALLEL_SH="included"
[ -z "${MULLE_FILE_SH}" ] && echo "mulle-file.sh must be included before mulle-parallel.sh" 2>&1 && exit 1
very_short_sleep()
{
case "${MULLE_UNAME}" in
darwin)
sleep 0.001 #s # 1000Hz
;;
*)
sleep 0.001s #s # 1000Hz
;;
esac
}
r_get_core_count()
{
if [ -z "${MULLE_CORES}" ]
then
MULLE_CORES="`/usr/bin/nproc 2> /dev/null`"
if [ -z "${MULLE_CORES}" ]
then
MULLE_CORES="`/usr/sbin/sysctl -n hw.ncpu 2> /dev/null`"
fi
if [ -z "${MULLE_CORES}" ]
then
MULLE_CORES=4
log_verbose "Unknown core count, setting it to 4 as default"
fi
fi
RVAL=${MULLE_CORES}
}
r_convenient_max_load_average()
{
local cores="$1"
if [ -z "${cores}" ]
then
r_get_core_count
cores="${RVAL}"
fi
case "${MULLE_UNAME}" in
linux)
RVAL=$((cores * 6))
;;
*)
RVAL=$((cores * 2))
;;
esac
}
wait_for_available_job()
{
log_entry "wait_for_available_job" "$@"
local maxjobs="${1:-8}"
local running
local count
while :
do
running=($(jobs -pr)) # http://mywiki.wooledge.org/BashFAQ/004
count=${#running[@]}
if [ ${count} -le ${maxjobs} ]
then
log_debug "Currently only ${count} jobs run, spawn another"
break
fi
log_debug "Waiting on jobs to finish (${#running[@]})"
very_short_sleep
done
}
get_current_load_average()
{
case "${MULLE_UNAME}" in
freebsd|darwin)
sysctl -n vm.loadavg | sed -n -e 's/.*{[ ]*\([0-9]*\).*/\1/p'
;;
mingw)
echo "7" # no way to know
;;
*)
uptime | sed -n -e 's/.*average[s]*:[ ]*\([0-9]*\).*/\1/p'
;;
esac
}
r_available_core_count()
{
log_entry "r_available_core_count" "$@"
local maxaverage="$1"
local cores
r_get_core_count
cores="${RVAL}"
if [ -z "${maxaverage}" ]
then
r_convenient_max_load_average "${cores}"
maxaverage="${RVAL}"
fi
local loadavg
local available
loadavg="`get_current_load_average`"
available="$(( cores - loadavg ))"
if [ ${available} -lt 1 ]
then
available="1"
fi
RVAL="${available}"
}
wait_for_load_average()
{
log_entry "wait_for_load_average" "$@"
local maxaverage="${1:-8}"
local loadavg
while :
do
loadavg="`get_current_load_average`"
if [ "${loadavg:-0}" -le ${maxaverage} ]
then
break
fi
log_debug "Waiting on load average to come down"
very_short_sleep
done
}
_parallel_begin()
{
log_entry "_parallel_begin" "$@"
_parallel_maxjobs="$1"
_parallel_jobs=0
_parallel_fails=0
r_make_tmp "mulle-parallel" || exit 1
_parallel_statusfile="${RVAL}"
if [ -z "${_parallel_maxjobs}" ]
then
_parallel_maxjobs="${MULLE_PARALLEL_MAX_JOBS}"
if [ -z "${_parallel_maxjobs}" ]
then
r_get_core_count
_parallel_maxjobs="${RVAL}"
fi
fi
}
_parallel_end()
{
log_entry "_parallel_end" "$@"
wait
_parallel_fails="`rexekutor wc -l "${_parallel_statusfile}" | awk '{ printf $1 }'`"
if [ "${MULLE_FLAG_LOG_SETTINGS}" = 'YES' ]
then
log_trace2 "_parallel_jobs : ${_parallel_jobs}"
log_trace2 "_parallel_fails: ${_parallel_fails}"
log_trace2 "${_parallel_statusfile} : `cat "${_parallel_statusfile}"`"
fi
exekutor rm "${_parallel_statusfile}"
[ "${_parallel_fails}" -eq 0 ]
}
_parallel_status()
{
log_entry "_parallel_status" "$@"
local rval="$1"; shift
[ -z "${_parallel_statusfile}" ] && internal_fail "_parallel_statusfile must be defined"
if [ $rval -ne 0 ]
then
log_warning "warning: $* failed with $rval"
redirect_append_exekutor "${_parallel_statusfile}" printf "%s\n" "${rval};$*"
fi
}
_parallel_execute()
{
log_entry "_parallel_execute" "$@"
wait_for_available_job "${_parallel_maxjobs}"
_parallel_jobs=$(($_parallel_jobs + 1))
log_debug "Running job #${_parallel_jobs}: $*"
(
local rval
( exekutor "$@" ) # run in subshell to capture exit code
_parallel_status $? "$@"
) &
}
parallel_execute()
{
log_entry "parallel_execute" "$@"
local arguments="$1"; shift
local _parallel_statusfile
local _parallel_maxjobs
local _parallel_jobs
local _parallel_fails
[ $# -eq 0 ] && internal_fail "missing commandline"
_parallel_begin
local argument
shell_disable_glob; IFS=$'\n'
for argument in ${arguments}
do
shell_enable_glob; IFS="${DEFAULT_IFS}"
_parallel_execute "$@" "${argument}"
done
shell_enable_glob; IFS="${DEFAULT_IFS}"
_parallel_end
}
[ ! -z "${MULLE_VERSION_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-version.sh" >&2
MULLE_VERSION_SH="included"
r_get_version_major()
{
RVAL="${1%%\.*}"
}
r_get_version_minor()
{
RVAL="${1#*\.}"
if [ "${RVAL}" = "$1" ]
then
RVAL=0
else
RVAL="${RVAL%%\.*}"
fi
}
r_get_version_patch()
{
local prev
prev="${1#*\.}"
RVAL="${prev#*\.}"
if [ "${RVAL}" = "${prev}" ]
then
RVAL=0
else
RVAL="${RVAL%%\.*}"
fi
}
check_version()
{
local version="$1"
local min_major="$2"
local min_minor="$3"
if [ -z "${version}" ]
then
return 1
fi
local major
local minor
r_get_version_major "${version}"
major="${RVAL}"
if [ "${major}" -lt "${min_major}" ]
then
return 0
fi
if [ "${major}" -ne "${min_major}" ]
then
return 1
fi
r_get_version_minor "${version}"
minor="${RVAL}"
[ "${minor}" -le "${min_minor}" ]
}
_r_version_value()
{
RVAL="$((${1:-0} * 1048576 + ${2:-0} * 256 + ${3:-0}))"
}
r_version_value()
{
local major
local minor
local patch
r_get_version_major "$1"
major="${RVAL}"
r_get_version_minor "$1"
minor="${RVAL}"
r_get_version_patch "$1"
patch="${RVAL}"
_r_version_value "${major}" "${minor}" "${patch}"
}
r_version_value_distance()
{
RVAL="$(($2 - $1))"
}
r_version_distance()
{
local value1
local value2
r_version_value "$1"
value1="${RVAL}"
r_version_value "$2"
value2="${RVAL}"
r_version_value_distance "${value1}" "${value2}"
}
is_compatible_version_value_distance()
{
if [ "$1" -ge 1048576 -o "$1" -le -1048575 ]
then
return 1
fi
if [ "$1" -gt 4096 ]
then
return 1
fi
[ "$1" -le 0 ]
}
is_compatible_version()
{
r_version_distance "$1" "$2"
is_compatible_version_value_distance "${RVAL}"
}
:
[ ! -z "${MULLE_ETC_SH}" -a "${MULLE_WARN_DOUBLE_INCLUSION}" = 'YES' ] && \
echo "double inclusion of mulle-etc.sh" >&2
MULLE_ETC_SH="included"
etc_prepare_for_write_of_file()
{
log_entry "etc_prepare_for_write_of_file" "$@"
local filename="$1"
if [ -L "${filename}" ]
then
exekutor rm "${filename}"
fi
}
etc_make_file_from_symlinked_file()
{
log_entry "etc_make_file_from_symlinked_file" "$@"
local dstfile="$1"
if [ ! -L "${dstfile}" ]
then
return 1
fi
local flags
if [ "${MULLE_FLAG_LOG_FLUFF}" = 'YES' ]
then
flags="-v"
fi
local targetfile
targetfile="`readlink "${dstfile}"`"
exekutor rm "${dstfile}"
local directory
local filename
r_dirname "${dstfile}"
directory="${RVAL}"
r_basename "${dstfile}"
filename="${RVAL}"
(
cd "${directory}" || exit 1
if [ ! -f "${targetfile}" ]
then
log_fluff "Stale link encountered"
return 0
fi
exekutor cp ${flags} "${targetfile}" "${filename}" || exit 1
exekutor chmod ug+w "${filename}"
) || fail "Could not copy \"${targetfile}\" to \"${dstfile}\""
}
etc_symlink_or_copy_file()
{
log_entry "etc_symlink_or_copy_file" "$@"
local srcfile="$1"
local dstdir="$2"
local filename="$3"
local symlink="$4"
[ -f "${srcfile}" ] || internal_fail "\"${srcfile}\" does not exist or is not a file"
[ -d "${dstdir}" ] || internal_fail "\"${dstdir}\" does not exist or is not a directory"
local dstfile
if [ -z "${filename}" ]
then
r_basename "${srcfile}"
filename="${RVAL}"
fi
r_filepath_concat "${dstdir}" "${filename}"
dstfile="${RVAL}"
if [ -e "${dstfile}" ]
then
fail "\"${dstfile}\" already exists"
fi
r_mkdir_parent_if_missing "${dstfile}"
local flags
if [ "${MULLE_FLAG_LOG_FLUFF}" = 'YES' ]
then
flags="-v"
fi
if [ -z "${symlink}" ]
then
case "${MULLE_UNAME}" in
mingw)
symlink="NO"
;;
*)
symlink="YES"
;;
esac
fi
if [ "${symlink}" = 'YES' ]
then
local linkrel
r_relative_path_between "${srcfile}" "${dstdir}"
linkrel="${RVAL}"
exekutor ln -s ${flags} "${linkrel}" "${dstfile}"
return $?
fi
exekutor cp ${flags} "${srcfile}" "${dstfile}" &&
exekutor chmod ug+w "${dstfile}"
}
etc_setup_from_share_if_needed()
{
log_entry "etc_setup_from_share_if_needed" "$@"
local etc="$1"
local share="$2"
local symlink="$3"
if [ -d "${etc}" ]
then
log_fluff "etc folder already setup"
return
fi
mkdir_if_missing "${etc}"
local flags
if [ "${MULLE_FLAG_LOG_FLUFF}" = 'YES' ]
then
flags="-v"
fi
local filename
local base
if [ -d "${share}" ] # sometimes it's not there, but find complains
then
IFS=$'\n'; shell_disable_glob
for filename in `find "${share}" ! -type d -print`
do
IFS="${DEFAULT_IFS}"; shell_enable_glob
r_basename "${filename}"
etc_symlink_or_copy_file "${filename}" \
"${etc}" \
"${RVAL}" \
"${symlink}"
done
IFS="${DEFAULT_IFS}"; shell_enable_glob
fi
}
etc_remove_if_possible()
{
log_entry "etc_remove_if_possible" "$@"
local etc="$1"
local share="$2"
if [ ! -d "${etc}" ]
then
return
fi
if dirs_contain_same_files "${etc}" "${share}"
then
rmdir_safer "${etc}"
fi
}
etc_repair_files()
{
log_entry "etc_repair_files" "$@"
local srcdir="$1" # share
local dstdir="$2" # etc
local glob="$3"
local add="$4"
local symlink="$5"
if [ ! -d "${dstdir}" ]
then
log_verbose "Nothing to repair, as \"${dstdir}\" does not exist yet"
return
fi
local filename
local dstfile
local srcfile
local can_remove_etc
can_remove_etc='YES'
dstdir="${dstdir%%/}"
srcdir="${srcdir%%/}"
IFS=$'\n'; shell_disable_glob
for dstfile in `find "${dstdir}" ! -type d -print` # dstdir is etc
do
IFS="${DEFAULT_IFS}"; shell_enable_glob
filename="${dstfile#${dstdir}/}"
srcfile="${srcdir}/${filename}"
if [ -L "${dstfile}" ]
then
if ! ( cd "${dstdir}" && [ -f "`readlink "${filename}"`" ] )
then
globtest="${glob}${filename#${glob}}"
if [ ! -z "${glob}" ] && [ -f "${srcdir}"/${globtest} ]
then
log_verbose "\"${filename}\" moved to ${globtest}: relink"
remove_file_if_present "${dstfile}"
etc_symlink_or_copy_file "${srcdir}/"${globtest} \
"${dstdir}" \
"" \
"${symlink}"
else
log_verbose "\"${filename}\" no longer exists: remove"
remove_file_if_present "${dstfile}"
fi
else
log_fluff "\"${filename}\" is a healthy symlink: keep"
fi
else
if [ -f "${srcfile}" ]
then
if diff -q -b "${dstfile}" "${srcfile}" > /dev/null
then
log_verbose "\"${filename}\" has no user edits: replace with symlink"
remove_file_if_present "${dstfile}"
etc_symlink_or_copy_file "${srcfile}" \
"${dstdir}" \
"${filename}" \
"${symlink}"
else
log_fluff "\"${filename}\" contains edits: keep"
can_remove_etc='NO'
fi
else
log_fluff "\"${filename}\" is an addition: keep"
can_remove_etc='NO'
fi
fi
done
IFS=$'\n'; shell_disable_glob
for srcfile in `find "${srcdir}" ! -type d -print` # dstdir is etc
do
IFS="${DEFAULT_IFS}"; shell_enable_glob
filename="${srcfile#${srcdir}/}"
dstfile="${dstdir}/${filename}"
if [ ! -e "${dstfile}" ]
then
if [ "${add}" = 'YES' ]
then
log_verbose "\"${filename}\" is missing: recreate"
etc_symlink_or_copy_file "${srcfile}" \
"${dstdir}" \
"${filename}" \
"${symlink}"
else
log_info "\"${filename}\" is new but not used. Use \`repair --add\` to add it."
can_remove_etc='NO'
fi
fi
done
IFS="${DEFAULT_IFS}"; shell_enable_glob
if [ "${can_remove_etc}" = 'YES' ]
then
log_info "\"${dstdir#${MULLE_USER_PWD}/}\" contains no user changes so use \"share\" again"
rmdir_safer "${dstdir}"
rmdir_if_empty "${srcdir}"
fi
}
### << END OF mulle-bashfunctions-embed.sh <<
_init "$@" # will call main indirectly via call_main
###
### INIT
###
########
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment