diff --git a/.gitignore b/.gitignore index a27db0bd1f7a64..0953f3e10ecbea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,42 @@ -### Vim ### +# Vim [._]*.s[a-w][a-z] [._]s[a-w][a-z] *.un~ Session.vim .netrwhist *~ -/debs/ -### Vagrant ### -scripts/*.log + +# Vagrant scripts/.vagrant/ + +# Logs +scripts/*.log +/*.log + +# Misc archives +/*\.deb +/*\.zip +/*\.tar +/*\.tar\.* + +# Source files. +/sources/ + +# GitHub Actions build artifacts +debs* +checksum* + +# Built *.deb files. +/debs/ +/output/ + +# Preinstalled build tools. +/build-tools/ + +# Predownloaded packages sources. +/packages/*/cache +/root-packages/*/cache +/x11-packages/*/cache + +# Temp .git directories for packages built from local sources "file://" +/*.git diff --git a/build-package.sh b/build-package.sh index 16a4464bf1f479..89a4807eab1cd4 100755 --- a/build-package.sh +++ b/build-package.sh @@ -175,6 +175,14 @@ source "$TERMUX_SCRIPTDIR/scripts/build/termux_create_subpackages.sh" # shellcheck source=scripts/build/termux_step_massage.sh source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_massage.sh" +# Function to run strip symbols during termux_step_massage +# shellcheck source=scripts/build/termux_step_strip_elf_symbols.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_strip_elf_symbols.sh" + +# Function to run termux-elf-cleaner during termux_step_massage +# shellcheck source=scripts/build/termux_step_elf_cleaner.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_elf_cleaner.sh" + # Hook for packages after massage step termux_step_post_massage() { return @@ -184,10 +192,9 @@ termux_step_post_massage() { # shellcheck source=scripts/build/termux_step_create_datatar.sh source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_create_datatar.sh" -# Hook function to create {pre,post}install, {pre,post}rm-scripts and similar -termux_step_create_debscripts() { - return -} +# Function to create {pre,post}install, {pre,post}rm-scripts and similar +# shellcheck source=scripts/build/termux_step_create_debscripts.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_create_debscripts.sh" # Create the build deb file. Not to be overridden by package scripts. # shellcheck source=scripts/build/termux_step_create_debfile.sh diff --git a/clean.sh b/clean.sh index 5df2b78d011681..e0bf82f11332e4 100755 --- a/clean.sh +++ b/clean.sh @@ -1,7 +1,11 @@ -#!/bin/sh +#!/bin/bash # clean.sh - clean everything. set -e -u +TERMUX_SCRIPTDIR=$(cd "$(realpath "$(dirname "$0")")"; pwd) + +. "$TERMUX_SCRIPTDIR/scripts/properties.sh" + # Checking if script is running on Android with 2 different methods. # Needed for safety to prevent execution of potentially dangerous # operations such as 'rm -rf /data/*' on Android device. @@ -38,10 +42,47 @@ fi chmod +w -R "$TERMUX_TOPDIR" fi - if $TERMUX_ON_DEVICE_BUILD; then - # For on-device build cleanup /data shouldn't be erased. - rm -Rf "$TERMUX_TOPDIR" - else - rm -Rf /data/* "$TERMUX_TOPDIR" + # For on-device build cleanup Termux app data directory shouldn't be erased. + if [[ "$TERMUX_ON_DEVICE_BUILD" == "false" ]]; then + for variable_name in TERMUX__PREFIX TERMUX_APP__DATA_DIR; do + variable_value="${!variable_name:-}" + if [[ ! "$variable_value" =~ ^(/[^/]+)+$ ]]; then + echo "The $variable_name '$variable_value' is not an absolute path under rootfs '/' while running 'clean.sh'." 1>&2 + exit 1 + fi + done + + # If `TERMUX__PREFIX` is under `TERMUX_APP__DATA_DIR`, then + # just delete the entire `TERMUX_APP__DATA_DIR`. Otherwise, + # only delete `TERMUX__PREFIX` since its parent directories could + # be a critical directory in `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. + # This should not be an issue as package files are only packed + # from `TERMUX_PREFIX` via `termux_step_extract_into_massagedir()`. + if [[ "$TERMUX__PREFIX" == "$TERMUX_APP__DATA_DIR" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX_APP__DATA_DIR/"* ]]; then + deletion_dir="$TERMUX_APP__DATA_DIR" + else + deletion_dir="$TERMUX__PREFIX" + fi + + if [[ -e "$deletion_dir" ]]; then + if [[ ! -d "$deletion_dir" ]]; then + echo "A non-directory file exists at deletion directory '$deletion_dir' for TERMUX__PREFIX while running 'clean.sh'." 1>&2 + exit 1 + fi + + # If deletion directory is under rootfs `/` or not accessible + # by current user, like the `builder` user in Termux docker + # cannot access root owned directories. + if [[ ! -r "$deletion_dir" ]] || [[ ! -w "$deletion_dir" ]] || [[ ! -x "$deletion_dir" ]]; then + echo "The deletion directory '$deletion_dir' for TERMUX__PREFIX is not readable, writable or searchable while running 'clean.sh'." 1>&2 + echo "Try running 'clean.sh' with 'sudo'." 1>&2 + exit 1 + fi + + find "$deletion_dir" -mindepth 1 -delete 2>/dev/null || true + fi fi + + rm -Rf "$TERMUX_TOPDIR" } 5< "$TERMUX_BUILD_LOCK_FILE" diff --git a/packages/sudo/build.sh b/packages/sudo/build.sh new file mode 100644 index 00000000000000..b5906016ab6825 --- /dev/null +++ b/packages/sudo/build.sh @@ -0,0 +1,27 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/agnostic-apollo/sudo +TERMUX_PKG_DESCRIPTION="A wrapper script to drop to the supported shells or execute shell script files or their text passed as an argument as the root (superuser) user in the Termux app" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@agnostic-apollo" +TERMUX_PKG_VERSION=1.0.0 +TERMUX_PKG_SRCURL=https://github.com/agnostic-apollo/sudo/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=3ebbc7c034cb184da46d253d8c9f90935de05161d2292aed5417d5a662a46ea9 +TERMUX_PKG_DEPENDS="bash" +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_CONFLICTS="tsu" +TERMUX_PKG_REPLACES="tsu" +TERMUX_PKG_EXTRA_MAKE_ARGS="SUDO_PKG__VERSION=${TERMUX_PKG_VERSION} SUDO_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} diff --git a/packages/termux-am-socket/build.sh b/packages/termux-am-socket/build.sh index c59677eec74ccd..0e26cbf019843a 100644 --- a/packages/termux-am-socket/build.sh +++ b/packages/termux-am-socket/build.sh @@ -2,6 +2,18 @@ TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-am-socket TERMUX_PKG_DESCRIPTION="A faster version of am with less features that only works while Termux is running" TERMUX_PKG_LICENSE="GPL-3.0" TERMUX_PKG_MAINTAINER="@termux" -TERMUX_PKG_VERSION=1.3.0 +TERMUX_PKG_VERSION=1.5.0 TERMUX_PKG_SRCURL=https://github.com/termux/termux-am-socket/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz -TERMUX_PKG_SHA256=a0ac6f1733b0545b0a71de60615b6d4f781fb9e018d1841160308ed992c77997 +TERMUX_PKG_SHA256=5175023c7fd675492451a72d06b75c772f257685b69fe117227bae5a5e6f5494 +TERMUX_PKG_DEPENDS="libc++" + +termux_step_post_get_source() { + + for file in "${TERMUX_PKG_SRCDIR}/"*; do + sed -i'' -E -e "s|^(TERMUX_AM_SOCKET_VERSION=).*|\1$TERMUX_PKG_FULLVERSION|" \ + -e "s|\@TERMUX_APP_PACKAGE\@|${TERMUX_APP_PACKAGE}|g" \ + -e "s|\@TERMUX_APPS_DIR\@|${TERMUX_APPS_DIR}|g" \ + "$file" + done + +} diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name new file mode 100644 index 00000000000000..150f47ff10cc6a --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name @@ -0,0 +1,195 @@ +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (START) ##### + +## +# Get/Unset variable values of the app version name environment +# variables `*_APP__APP_VERSION_NAME` for Termux app `TERMUX_APP__`, +# its plugin apps `TERMUX_*_APP__` and external apps `*_APP__` app +# scoped that exist in the `termux-apps-info.env` file, with support +# for validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-app-version-name.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-app-version-name.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_apps_info_app_version_name` `get-value` [``] \ +# `` `` +# `termux_core__bash__termux_apps_info_app_version_name` `unset-value` \ +# `` +## +termux_core__bash__termux_apps_info_app_version_name() { + + local return_value + + local command_type="${1:-}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-value" ]; then + local opt; local opt_arg; local OPTARG=""; local OPTIND=1; + + local extended_validator='r+=^(()|((googleplay\.)?[0-9].*))$' + local skip_sourcing_option="--skip-sourcing-if-cur-app-var" + + if [ $# -eq 0 ]; then + echo "No arguments passed for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + return 64 # EX__USAGE + fi + + while getopts ":-:" opt; do + opt_arg="${OPTARG:-}" + case "${opt}" in + -) + case "${OPTARG}" in *?=*) opt_arg="${OPTARG#*=}";; *) opt_arg="";; esac + case "${OPTARG}" in + extended-validator=?*) + extended_validator="$opt_arg" + ;; + extended-validator | extended-validator=) + echo "No argument set for arg option '--${OPTARG%=*}'." 1>&2 + return 64 # EX__USAGE + ;; + skip-sourcing) + skip_sourcing_option="--skip-sourcing" + ;; + skip-sourcing=*) + echo "Arguments not allowed for flag option '--${OPTARG%=*}': \`--${OPTARG:-}\`." 1>&2 + return 64 # EX__USAGE + ;; + '') + break # End of options `--`. + ;; + *) + echo "Unknown option: '--${OPTARG:-}'." 1>&2 + return 64 # EX__USAGE + ;; + esac + ;; + :) + echo "No argument passed for arg option '-$OPTARG'." 1>&2 + return 64 # EX__USAGE + ;; + \?) + echo "Unknown option${OPTARG:+": '-${OPTARG:-}'"}." 1>&2 + return 64 # EX__USAGE + ;; + esac + done + shift $((OPTIND - 1)) # Remove already processed arguments from arguments array. + + if [ $# -lt 2 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_apps_info_app_version_name' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "get-value" ]; then + local __app_version_name="" + + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # A valid environment variable name (like `TERMUX__VAR`) or a bash array variable (like `TERMUX__ARRAY[0]`). + local valid_bash_variable_name_regex='^[a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?$' # ' single quote for linter error false positive against `IFS=$'\n'` + + # If `output_mode` is not a valid environment variable name. + if [[ ! "$output_mode" =~ $valid_bash_variable_name_regex ]]; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__bash__termux_apps_info_app_version_name' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + + + return_value=0 + termux_core__bash__termux_apps_info_env_variable get-value \ + "$skip_sourcing_option" __app_version_name \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" "$extended_validator" || return_value=$? + + + # If getting version name of the Termux app but failed to get it, + # likely due to Termux app scoped `APP_VERSION_NAME` environment + # variable not being exported if running in Termux app version + # `<= 0.119.0` (as `TERMUX_ENV__S_ROOT` environment variable is + # not exported), then fallback to reading the old/deprecated + # `TERMUX_VERSION` environment variable. + # This may give outdated/wrong values if running in a plugin + # app like Termux:Float app and Termux app got updated in the + # background, as `TERMUX_VERSION` would be set to the version + # at the time the Termux:Float shell was started, and not the + # updated version. + if [ $return_value -eq 0 ] && [ -z "$__app_version_name" ] && \ + { [ "$scoped_var_scope_mode" = cn="termux-app" ] || [ "$scoped_var_scope_mode" = ss="APP__" ]; } && \ + [ -z "${TERMUX_ENV__S_ROOT:-}" ]; then + return_value=0 + termux_core__bash__termux_scoped_env_variable get-value \ + __app_version_name "" "" "$extended_validator" "${TERMUX_VERSION:-}" || return_value=$? + fi + + + # If either above commands failed. + if [ $return_value -ne 0 ]; then + # If a valid value not found. + if [ $return_value -eq 81 ]; then # C_EX__NOT_FOUND + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use + # that wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `var_to_get_name` generated in + # `termux_core__bash__termux_scoped_env_variable()` is + # equal to output variable in `output_mode` passed to this + # function, then `var_to_get_name` would get unset before + # its read. + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "" || return $? + fi + fi + return $return_value + fi + + + # If a valid value found. + if [ "$output_mode" = ">" ]; then + printf "%s" "$__app_version_name" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$__app_version_name" + return $? + else + return 0 + fi + elif [ "$command_type" = "unset-value" ]; then + termux_core__bash__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" || return $? + + if [ "$scoped_var_scope_mode" = cn="termux-app" ] || \ + [ "$scoped_var_scope_mode" = ss="APP__" ]; then + unset TERMUX_VERSION + fi + + return 0 + fi + +} + +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable new file mode 100644 index 00000000000000..631882e50c80a4 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable @@ -0,0 +1,158 @@ +##### @TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_GTAIEVV_BASH__TERMUX_APPS_INFO_ENV_VARIABLE_VALUE@ replaced at build time. (START) ##### + +## +# Source the `termux-apps-info.env` file into the current environment +# or get variable values of Termux app `TERMUX_APP__`, its plugin apps +# `TERMUX_*_APP__` and external apps `*_APP__` app scoped environment +# variables that exist in the `termux-apps-info.env` file, with +# support for fallback values and validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_apps_info_env_variable` `source-env` +# `termux_core__bash__termux_apps_info_env_variable` `get-value` [``] \ +# `` \ +# `` `` \ +# `` [``] +## +termux_core__bash__termux_apps_info_env_variable() { + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "source-env" ]; then + if [ $# -ne 0 ]; then + echo "Invalid argument count $# for the 'source-env' command. \ +The 'termux_core__bash__termux_apps_info_env_variable' function expects 0 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local skip_sourcing=0 + local skip_sourcing_if_cur_app_var=0 + local ensure_sourcing=0 + + if [ "${1:-}" = "--skip-sourcing" ]; then + skip_sourcing=1 + shift 1 + elif [ "${1:-}" = "--skip-sourcing-if-cur-app-var" ]; then + skip_sourcing_if_cur_app_var=1 + shift 1 + elif [ "${1:-}" = "--ensure-sourcing" ]; then + ensure_sourcing=1 + shift 1 + fi + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + case "$scoped_var_scope_mode" in + 's='[A-Z]*'_APP__'|'ss=APP__'|'ss='[A-Z]*'_APP__'|'cn='[a-z]*'-app') :;; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__bash__termux_apps_info_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\` and ending with '-app', \ +or an environment variable scope starting with \`s=\` or \`ss=\` and ending with '_APP__'." 1>&2 + return 64 # EX__USAGE + ;; + esac + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_apps_info_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "source-env" ]; then + local termux_core__apps_info_env_file="" + + # Source the `termux-apps-info.env` file. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__bash__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" r+='^(()|((/[^/]+)+))$' || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + source "$termux_core__apps_info_env_file" || return $? + return $? + else + return 69 # EX__UNAVAILABLE + fi + elif [ "$command_type" = "get-value" ]; then + # Prefix with `app_` to prevent conflict with `termux_core__bash__termux_scoped_env_variable` argument. + local app_scoped_var_scope_name="" + local termux_core__apps_info_env_file="" + + # If `skip_sourcing` is enabled, then directly get the value from + # the current environment. + if [ "$skip_sourcing" = "1" ]; then + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + # If `skip_sourcing_if_cur_app_var` is enabled and app scope of + # the variable to get is the same as the app scope of the current + # app, then directly get the value from the current + # environment. + elif [ "$skip_sourcing_if_cur_app_var" = "1" ] && [ -n "${TERMUX_ENV__S_APP:-}" ]; then + # Get app scope of the variable to get. If caller passed + # `scoped_var_scope_mode` as `s=*`, then it will be returned + # as is, otherwise if `ss=*` or `cn=*` is passed, then full + # variable scope including Termux root scope prefix will + # be returned. The `scoped_var_sub_name` arg is passed as + # an empty string as we only need the app scope name. + termux_core__bash__termux_scoped_env_variable get-name \ + app_scoped_var_scope_name "$scoped_var_scope_mode" "" || return $? + + if [ "$TERMUX_ENV__S_APP" = "$app_scoped_var_scope_name" ]; then + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + fi + fi + + + # Unset variable to get before sourcing, otherwise if the + # `termux-apps-info.env` file does not explicitly unset it if + # variable should not be set, then any value including empty + # (for `validator_mode` `*`) that exists in the current environment + # may get used. + termux_core__bash__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "$scoped_var_sub_name" || return $? + + + # First source the `termux-apps-info.env` file to load the latest + # value in the current environment before getting it. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__bash__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" r+='^(()|((/[^/]+)+))$' || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + source "$termux_core__apps_info_env_file" || return $? + elif [ "$ensure_sourcing" = "1" ]; then + return 69 # EX__UNAVAILABLE + fi + + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + fi + +} + +##### @TERMUX_GTAIEVV_BASH__TERMUX_APPS_INFO_ENV_VARIABLE_VALUE@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable new file mode 100644 index 00000000000000..22a3529bb6091e --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable @@ -0,0 +1,559 @@ +##### TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (START) ##### + +## +# Get/Set/Unset variable names and values for `TERMUX*__` and other +# scoped environment variables exported by different Termux runtime +# components, with support for fallback values and validation of +# values. +# +# The `extended_validator` logic is based on `libalteran-sh` +# `bash___shell__validate_variable_with_extended_validator()` function. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_scoped_env_variable` `get-name` \ +# `` \ +# `` `` +# `termux_core__bash__termux_scoped_env_variable` `get-value` \ +# `` \ +# `` `` \ +# `` [``] +# `termux_core__bash__termux_scoped_env_variable` `set-value` \ +# `` `` \ +# `` +# `termux_core__bash__termux_scoped_env_variable` `unset-value` \ +# `` `` +## +termux_core__bash__termux_scoped_env_variable() { + + local return_value + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-name" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'get-name' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + local extended_validator="${4:-}" + + local validator_arg + local validator_mode + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + # Remove args before `default_values` to be used in `var_value_cur` for loop below. + shift 4 + + case "$extended_validator" in + '?'|'*') + validator_arg="" + validator_mode="$extended_validator" + ;; + 'r+='?*|'r-='?*|'c+='?*|'c-='?*) + validator_arg="${extended_validator:3}" # 3:end + validator_mode="${extended_validator:0:3}" # 0:3 + ;; + *) + echo "The extended_validator '$extended_validator' \ +argument passed to 'termux_core__bash__termux_scoped_env_variable' is not valid. \ +It must either be equal to \`?\` or \`*\`, or a regex that starts with \`r+=\` or \`r-=\`, \ +or a executable or function that starts with \`c+=\` or \`c-=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + elif [ "$command_type" = "set-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + local value_to_set="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'set-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + + if [ $# -ne 2 ]; then + echo "Invalid argument count $# for the 'unset-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_scoped_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + local i + local is_value_valid + local scoped_var_root_scope_name="" + local scoped_var_scope_name="" + local scoped_var_sub_scope_name="" + local scoped_var_name="" + local scoped_var_set="" + local scoped_var_value_invalid_error_suffix="" + local var_value_cur + + # A valid environment variable name (like `TERMUX__VAR`) or a bash array variable (like `TERMUX__ARRAY[0]`). + local valid_bash_variable_name_regex='^[a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + + if [ "$command_action" = "get" ]; then + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if [[ ! "$output_mode" =~ $valid_bash_variable_name_regex ]]; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + + case "$scoped_var_scope_mode" in + s=?*) scoped_var_scope_name="${scoped_var_scope_mode:2}";; # 2:end + ss=?*) scoped_var_sub_scope_name="${scoped_var_scope_mode:3}";; # 3:end + '') ;; + cn=termux) scoped_var_sub_scope_name="_";; + cn=termux-app) scoped_var_sub_scope_name="APP__";; + cn=termux-api-app) scoped_var_sub_scope_name="API_APP__";; + cn=termux-float-app) scoped_var_sub_scope_name="FLOAT_APP__";; + cn=termux-gui-app) scoped_var_sub_scope_name="GUI_APP__";; + cn=termux-tasker-app) scoped_var_sub_scope_name="TASKER_APP__";; + cn=termux-widget-app) scoped_var_sub_scope_name="WIDGET_APP__";; + cn=termux-x11-app) scoped_var_sub_scope_name="X11_APP__";; + cn=termux-core) scoped_var_sub_scope_name="CORE__";; + cn=termux-exec) scoped_var_sub_scope_name="EXEC__";; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__bash__termux_scoped_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\`, \ +or an environment variable scope starting with \`s=\` or \`ss=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + + + if [ -n "$scoped_var_scope_mode" ]; then + if [ -n "$scoped_var_scope_name" ]; then + # Generate the full name for the variable under the provided root and sub scope. + scoped_var_name="${scoped_var_scope_name}${scoped_var_sub_name}" + else + # Generate the full name for the variable under the Termux root scope and provided sub scope. + # If `TERMUX_ENV__S_ROOT` environment variable set. + # shellcheck disable=SC2050 + if [ -n "${TERMUX_ENV__S_ROOT:-}" ]; then + scoped_var_root_scope_name="$TERMUX_ENV__S_ROOT" + if [[ ! "$scoped_var_root_scope_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The TERMUX_ENV__S_ROOT environment variable value '$scoped_var_root_scope_name' \ +while running 'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + # If `TERMUX_ENV__S_ROOT` placeholder got replaced during build time. + elif [ "@TERMUX_ENV__S_ROOT@" != @"TERMUX_ENV__S_ROOT"@ ]; then + scoped_var_root_scope_name="@TERMUX_ENV__S_ROOT@" + if [[ ! "$scoped_var_root_scope_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The TERMUX_ENV__S_ROOT build value '$scoped_var_root_scope_name' \ +while running 'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + else + scoped_var_root_scope_name="TERMUX_" + fi + + scoped_var_name="${scoped_var_root_scope_name}${scoped_var_sub_scope_name}${scoped_var_sub_name}" + fi + + if [[ ! "$scoped_var_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The name of the variable to $command_action '$scoped_var_name' generated in \ +'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 64 # EX__USAGE + fi + + + # If command type equals `get-name`, then return the variable name and exit. + if [ "$command_type" = "get-name" ]; then + #echo "scoped_var_name=$scoped_var_name" + if [ "$output_mode" = ">" ]; then + printf "%s" "$scoped_var_name" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$scoped_var_name" + #eval "echo $output_mode=\"\${${output_mode}}\"" # Surround `${output_mode}` with `${}` in case its a base array variable name + return $? + else + return 0 + fi + elif [ "$command_type" = "set-value" ]; then + printf -v "$scoped_var_name" "%s" "$value_to_set" + return $? + elif [ "$command_type" = "unset-value" ]; then + unset "$scoped_var_name" + return $? + fi + else + if [ "$command_type" = "get-name" ] || \ + [ "$command_type" = "set-value" ] || [ "$command_type" = "unset-value" ]; then + echo "The scoped_var_scope_mode argument for the variable \ +to $command_action passed for the '$command_type' command to \ +'termux_core__bash__termux_scoped_env_variable' is not set." 1>&2 + return 64 # EX__USAGE + fi + fi + + + # If command type equals `get-value`, then find the first valid in variable name/values passed. + if [ "$command_type" = "get-value" ]; then + if [ "$validator_mode" = "c+=" ] || [ "$validator_mode" = "c-=" ]; then + local command_arg_first_arg + local -a validator_command_args=() + + # Convert command string to array. + local -a validator_command_args=() + termux_core__bash__set_shell_command_args_array validator_command_args \ + "extended_validator command" "$validator_arg" || return $? + + command_arg_first_arg="${validator_command_args[0]}" + + # Check if a command exists, like an executable in `$PATH`, + # a shell function or a path to an executable. + # - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html + if ! command -v -- "$command_arg_first_arg" >/dev/null 2>&1; then + # Check if absolute or relative path as `command -v` will only check executables under `$PATH`. + if [[ "$command_arg_first_arg" == */* ]] && \ + [ -f "$command_arg_first_arg" ] && [ -x "$command_arg_first_arg" ]; then + : + else + echo "The validator command '$command_arg_first_arg' \ +not found while running 'termux_core__bash__termux_scoped_env_variable' \ +that is set in the extended_validator '$extended_validator' argument." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + # Loop on [`` ``] to + # find the first valid value. + i=0 + for var_value_cur in "$scoped_var_name" "$@"; do + var_value_cur="${var_value_cur:-}" + + # If first loop, then expand the `scoped_var_name` to its value. + if [ "$i" = 0 ] && [ -n "$scoped_var_name" ]; then + # If mode is `*`, expand variable if it is set, or defined + # but empty. Else if its not defined, then ignore it. + if [ "$validator_mode" = "*" ]; then + eval '[ -n "${'"$scoped_var_name"'+x}" ] && scoped_var_set=1 || scoped_var_set=0' + if [ "$scoped_var_set" = "1" ]; then + var_value_cur="${!scoped_var_name:-}" + else + i=$((i + 1)); continue; + fi + else + var_value_cur="${!scoped_var_name:-}" + fi + fi + + is_value_valid=0 + if [ "$validator_mode" = "r+=" ]; then + if [[ "$var_value_cur" =~ $validator_arg ]]; then + is_value_valid=1 + fi + elif [ "$validator_mode" = "r-=" ]; then + if [[ ! "$var_value_cur" =~ $validator_arg ]]; then + is_value_valid=1 + fi + elif [ "$validator_mode" = "c+=" ]; then + # Do not use `if command; then` to preserve `set -e` failures in called function. + return_value=0 + "${validator_command_args[@]}" "$var_value_cur" || return_value=$? + if [ $return_value -eq 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + elif [ "$validator_mode" = "c-=" ]; then + # Do not use `if ! command; then` to preserve `set -e` failures in called function. + return_value=0 + "${validator_command_args[@]}" "$var_value_cur" || return_value=$? + if [ $return_value -ne 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + else + # If mode is `?` or `*`, and value is set, + # or mode is `*` and value is not set. + if [ -n "$var_value_cur" ] || [ "$validator_mode" = "*" ]; then + is_value_valid=1 + # Else if mode is `?`, and value is not set. + else + is_value_valid=0 + fi + fi + + if [ "$is_value_valid" = "1" ]; then + #echo "var_value_cur=$var_value_cur" + if [ "$output_mode" = ">" ]; then + printf "%s" "$var_value_cur" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$var_value_cur" + #eval "echo $output_mode=\"\${${output_mode}}\"" # Surround `${output_mode}` with `${}` in case its a base array variable name + return $? + else + return 0 + fi + fi + + i=$((i + 1)) + done + + + # If a valid value not found. + + if [ -n "$scoped_var_name" ]; then + scoped_var_value_invalid_error_suffix=" that is read from the '\$$scoped_var_name' variable" + fi + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use that + # wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `scoped_var_name` is equal to output variable in + # `output_mode`, then `scoped_var_name` would get unset before + # its read. + printf -v "$output_mode" "%s" "" || return $? + + echo "Failed to find a valid value to set to the '\$$output_mode' \ +variable${scoped_var_value_invalid_error_suffix}." 1>&2 + else + echo "Failed to find a valid value${scoped_var_value_invalid_error_suffix}." 1>&2 + fi + return 81 # C_EX__NOT_FOUND + fi + +} + +## +# Set a bash indexed array (`declared -a`) to shell command arguments +# defined as a string by splitting the string as normally done by the +# shell. +# +# This function is based on `libalteran-sh` +# `bash___shell__set_shell_command_args_array()` +# function, check it for more info and comments. +# +# +# `termux_core__bash__set_shell_command_args_array` \ +# `` \ +# `` `` +## +termux_core__bash__set_shell_command_args_array() { + + local return_value + + if [ $# -ne 3 ]; then + echo "Invalid argument count to 'termux_core__bash__set_shell_command_args_array'" + return 1 + fi + + local command_args_array_variable_name="$1" + local command_args_label="$2" + local command_args_string="$3" + + local command_arg + local command_args_string_contains_shell_special_chars="false" + local command_args_string_contains_space="false" + local newline=$'\n' + local space_containing_string_regex='[[:space:]]+' + local shell_special_chars_containing_string_regex='[]["'\''*?!\]+' + local xargs_command_output + local xargs_path + + local -n command_args_array="$command_args_array_variable_name" || return $? + command_args_array=() + + + if [[ -z "$command_args_string" ]]; then + # Unset `nameref` to `command_args_array_variable_name`. + unset -n command_args_array || return $? + return 0 + fi + + if [[ "$command_args_string" == *$'\n'* ]]; then + # shellcheck disable=SC2028 + echo "The $command_args_label value to be \ +converted to a command array cannot contain a newline '\n' character:" 1>&2 + echo "\`\`\`" 1>&2 + echo "$command_args_string" 1>&2 + echo "\`\`\`" 1>&2 + return 64 # EX__USAGE + fi + + + if [[ "$command_args_string" =~ $shell_special_chars_containing_string_regex ]]; then + command_args_string_contains_shell_special_chars="true" + fi + if [[ "$command_args_string" =~ $space_containing_string_regex ]]; then + command_args_string_contains_space="true" + fi + + if [[ "$command_args_string_contains_shell_special_chars" == "true" ]] || \ + [[ "$command_args_string_contains_space" == "true" ]]; then + + # Use `xargs` if command args string contains whitespaces or + # shell special characters. + if [[ "$command_args_string_contains_shell_special_chars" == "true" ]]; then + return_value=0 + xargs_path="$(command -v "xargs")" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to find 'xargs' for converting $command_args_label value to a command array." 1>&2 + return $return_value + fi + + # Android `toybox` provided `xargs` does not parse arguments + # properly and single and double quotes around arguments + # remain, nor do backslash escapes work. + # Termux `findutils` package supplied `xargs` does not + # have such issues, so we ignore the check for it only if + # `TERMUX__PREFIX` placeholder got replaced during build time. + # `/system/bin/xargs` may get called if current shell has + # `/system/bin` in `$PATH` instead of or before Termux bin path. + # shellcheck disable=SC2050 + if [[ "$xargs_path" != "@TERMUX__PREFIX@/xargs" ]] || [[ "@TERMUX__PREFIX@" == @"TERMUX__PREFIX"@ ]]; then + # The logic to check if `xargs` parsing is valid is based + # on `libalteran-sh` `bash___shell__set_is_xargs_command_args_parsing_valid()` + # function, check it for explanation. + return_value=0 + xargs_command_output="$(termux_core__bash__trim_env_for_xargs && \ + printf "%s" "'' 'arg' '' 'arg with space' 'arg surround by single quote' \"arg surround by double quote\" 'arg with '\'' single quote' 'arg with \" double quote' 'arg with \" double quote surround by single quote' \"arg with ' single quote surround by double quote\" arg\ with\ escaped\ space 'arg with backslash \\ and forward slash / surround by single quote' \"arg with backslash \\ and forward slash / surround by double quote\" \\/ \\\\ ''" | \ + xargs -r -n1 -- printf "%s\n" && echo -n x)" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to check if xargs command args parsing is valid." 1>&2 + echo "xargs_command_output=\`$xargs_command_output\`" 1>&2 + return $return_value + fi + + if [[ "${xargs_command_output%x}" != "${newline}arg${newline}${newline}arg with space${newline}arg surround by single quote${newline}arg surround by double quote${newline}arg with ' single quote${newline}arg with \" double quote${newline}arg with \" double quote surround by single quote${newline}arg with ' single quote surround by double quote${newline}arg with escaped space${newline}arg with backslash \ and forward slash / surround by single quote${newline}arg with backslash \\ and forward slash / surround by double quote${newline}/${newline}\\${newline}" ]]; then + echo "The $command_args_label value cannot be \ +converted to a command array in current execution environment as it contains \ +shell special characters and 'xargs' command args parsing is not valid:" 1>&2 + echo "\`\`\`" 1>&2 + echo "$command_args_string" 1>&2 + echo "\`\`\`" 1>&2 + echo "xargs path: \`$(command -v "xargs" || true)\`" 1>&2 + echo "xargs version: \`$({ xargs --version 2>/dev/null || true; } | head -n 1)\`" 1>&2 + return 1 + fi + fi + + command_arg="READ_ERROR" + return_value=0 + while IFS= read -r command_arg; return_value=$?; [[ $return_value -eq 0 ]]; do + command_args_array+=("$command_arg") + done < <({ termux_core__bash__trim_env_for_xargs && \ + printf "%s\n" "$command_args_string" | \ + xargs -r -n1 -- printf "%s\n"; } || echo -n "CMD_ERROR") + if [[ -n "${command_arg:-}" ]] || [ $return_value -ne 1 ]; then + echo "Failed to create command array for \ +command arguments string \`$command_args_string\`." 1>&2 + [[ -n "${command_arg:-}" ]] && echo "Error type: '$command_arg'" 1>&2 + if [[ -n ${!:-} ]]; then + wait "$!" || true + fi + return 1 + fi + else + # Avoid creating subshell and external call to xargs if + # command args string contains whitespaces but does not + # contain shell special characters and rely on whitespace + # splitting of arguments. + local old_ifs="$IFS" + IFS=$' \t\n' + # shellcheck disable=SC2206 + command_args_array=( $command_args_string ) + IFS="$old_ifs" + fi + else + # Avoid creating subshell and external call to xargs if + # command args string does not contain whitespaces or shell + # special characters. + command_args_array=( "$command_args_string" ) + fi + + # Unset `nameref` to `command_args_array_variable_name`. + unset -n command_args_array || return $? + + return 0 + +} + +## +# Trim exported environment variables before running `xargs` to +# prevent `environment is too large for exec` error on Android `< 6`. +# +# On Android `5`, `getconf ARG_MAX` equals `4096` and `xargs` always +# fails to run. +# +# Only `PATH`, `$LD_LIBRARY_PATH` and `$LD_PRELOAD` variables are kept. +# +# - https://www.gnu.org/software/findutils/manual/html_node/find_html/Error-Messages-From-xargs.html +## +termux_core__bash__trim_env_for_xargs() { + + # Find android sdk/os version. + # Termux app exports ANDROID__BUILD_VERSION_SDK for `>= v0.119.0`. + if [[ ! "${ANDROID__BUILD_VERSION_SDK:-}" =~ ^[0-9]+$ ]]; then + local return_value + ANDROID__BUILD_VERSION_SDK="$(unset LD_LIBRARY_PATH; unset LD_PRELOAD; getprop "ro.build.version.sdk")" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$ANDROID__BUILD_VERSION_SDK" =~ ^[0-9]+$ ]]; then + echo "Failure while finding 'ro.build.version.sdk' property" 1>&2 + echo "ANDROID__BUILD_VERSION_SDK='$ANDROID__BUILD_VERSION_SDK'" 1>&2 + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + fi + + # If on Android `< 6` + if [ "$ANDROID__BUILD_VERSION_SDK" -lt 25 ]; then + local env_variables + env_variables="$(compgen -e | grep -v -E "^PATH|LD_LIBRARY_PATH|LD_PRELOAD$")" || return $? + if [ -n "$env_variables" ]; then + # shellcheck disable=SC2086 + unset $env_variables + fi + fi + +} + +##### TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name new file mode 100644 index 00000000000000..e35e2af29f4a9e --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name @@ -0,0 +1,192 @@ +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (START) ##### + +## +# Get/Unset variable values of the app version name environment +# variables `*_APP__APP_VERSION_NAME` for Termux app `TERMUX_APP__`, +# its plugin apps `TERMUX_*_APP__` and external apps `*_APP__` app +# scoped that exist in the `termux-apps-info.env` file, with support +# for validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-app-version-name.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-app-version-name.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_apps_info_app_version_name` `get-value` [``] \ +# `` `` +# `termux_core__sh__termux_apps_info_app_version_name` `unset-value` \ +# `` +## +termux_core__sh__termux_apps_info_app_version_name() { + + local return_value + + local command_type="${1:-}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-value" ]; then + local opt; local opt_arg; local OPTARG=""; local OPTIND=1; + + local posix_validator="p+=''|[0-9]*|googleplay.[0-9]*" + local skip_sourcing_option="--skip-sourcing-if-cur-app-var" + + if [ $# -eq 0 ]; then + echo "No arguments passed for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + return 64 # EX__USAGE + fi + + while getopts ":-:" opt; do + opt_arg="${OPTARG:-}" + case "${opt}" in + -) + case "${OPTARG}" in *?=*) opt_arg="${OPTARG#*=}";; *) opt_arg="";; esac + case "${OPTARG}" in + posix-validator=?*) + posix_validator="$opt_arg" + ;; + posix-validator | posix-validator=) + echo "No argument set for arg option '--${OPTARG%=*}'." 1>&2 + return 64 # EX__USAGE + ;; + skip-sourcing) + skip_sourcing_option="--skip-sourcing" + ;; + skip-sourcing=*) + echo "Arguments not allowed for flag option '--${OPTARG%=*}': \`--${OPTARG:-}\`." 1>&2 + return 64 # EX__USAGE + ;; + '') + break # End of options `--`. + ;; + *) + echo "Unknown option: '--${OPTARG:-}'." 1>&2 + return 64 # EX__USAGE + ;; + esac + ;; + :) + echo "No argument passed for arg option '-$OPTARG'." 1>&2 + return 64 # EX__USAGE + ;; + \?) + echo "Unknown option${OPTARG:+": '-${OPTARG:-}'"}." 1>&2 + return 64 # EX__USAGE + ;; + esac + done + shift $((OPTIND - 1)) # Remove already processed arguments from arguments array. + + if [ $# -lt 2 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_apps_info_app_version_name' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "get-value" ]; then + local __app_version_name="" + + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if ! termux_core__sh__is_valid_shell_variable_name "$output_mode"; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__sh__termux_apps_info_app_version_name' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + + + return_value=0 + termux_core__sh__termux_apps_info_env_variable get-value \ + "$skip_sourcing_option" __app_version_name \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" "$posix_validator" || return_value=$? + + + # If getting version name of the Termux app but failed to get it, + # likely due to Termux app scoped `APP_VERSION_NAME` environment + # variable not being exported if running in Termux app version + # `<= 0.119.0` (as `TERMUX_ENV__S_ROOT` environment variable is + # not exported), then fallback to reading the old/deprecated + # `TERMUX_VERSION` environment variable. + # This may give outdated/wrong values if running in a plugin + # app like Termux:Float app and Termux app got updated in the + # background, as `TERMUX_VERSION` would be set to the version + # at the time the Termux:Float shell was started, and not the + # updated version. + if [ $return_value -eq 0 ] && [ -z "$__app_version_name" ] && \ + { [ "$scoped_var_scope_mode" = cn="termux-app" ] || [ "$scoped_var_scope_mode" = ss="APP__" ]; } && \ + [ -z "${TERMUX_ENV__S_ROOT:-}" ]; then + return_value=0 + termux_core__sh__termux_scoped_env_variable get-value \ + __app_version_name "" "" "$posix_validator" "${TERMUX_VERSION:-}" || return_value=$? + fi + + + # If either above commands failed. + if [ $return_value -ne 0 ]; then + # If a valid value not found. + if [ $return_value -eq 81 ]; then # C_EX__NOT_FOUND + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use + # that wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `var_to_get_name` generated in + # `termux_core__sh__termux_scoped_env_variable()` is + # equal to output variable in `output_mode` passed to this + # function, then `var_to_get_name` would get unset before + # its read. + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\" || return $? + fi + fi + return $return_value + fi + + + # If a valid value found. + if [ "$output_mode" = ">" ]; then + printf "%s" "$__app_version_name" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$__app_version_name\" + return $? + else + return 0 + fi + elif [ "$command_type" = "unset-value" ]; then + termux_core__sh__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" || return $? + + if [ "$scoped_var_scope_mode" = cn="termux-app" ] || \ + [ "$scoped_var_scope_mode" = ss="APP__" ]; then + unset TERMUX_VERSION + fi + + return 0 + fi + +} + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable new file mode 100644 index 00000000000000..8441cdac329613 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable @@ -0,0 +1,158 @@ +##### @TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ replaced at build time. (START) ##### + +## +# Source the `termux-apps-info.env` file into the current environment +# or get variable values of Termux app `TERMUX_APP__`, its plugin apps +# `TERMUX_*_APP__` and external apps `*_APP__` app scoped environment +# variables that exist in the `termux-apps-info.env` file, with +# support for fallback values and validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_apps_info_env_variable` `source-env` +# `termux_core__sh__termux_apps_info_env_variable` `get-value` [``] \ +# `` \ +# `` `` \ +# `` [``] +## +termux_core__sh__termux_apps_info_env_variable() { + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "source-env" ]; then + if [ $# -ne 0 ]; then + echo "Invalid argument count $# for the 'source-env' command. \ +The 'termux_core__sh__termux_apps_info_env_variable' function expects 0 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local skip_sourcing=0 + local skip_sourcing_if_cur_app_var=0 + local ensure_sourcing=0 + + if [ "${1:-}" = "--skip-sourcing" ]; then + skip_sourcing=1 + shift 1 + elif [ "${1:-}" = "--skip-sourcing-if-cur-app-var" ]; then + skip_sourcing_if_cur_app_var=1 + shift 1 + elif [ "${1:-}" = "--ensure-sourcing" ]; then + ensure_sourcing=1 + shift 1 + fi + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + case "$scoped_var_scope_mode" in + 's='[A-Z]*'_APP__'|'ss=APP__'|'ss='[A-Z]*'_APP__'|'cn='[a-z]*'-app') :;; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__sh__termux_apps_info_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\` and ending with '-app', \ +or an environment variable scope starting with \`s=\` or \`ss=\` and ending with '_APP__'." 1>&2 + return 64 # EX__USAGE + ;; + esac + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_apps_info_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "source-env" ]; then + local termux_core__apps_info_env_file="" + + # Source the `termux-apps-info.env` file. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__sh__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" p+="''|/*[!/]" || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + . "$termux_core__apps_info_env_file" + return $? + else + return 69 # EX__UNAVAILABLE + fi + elif [ "$command_type" = "get-value" ]; then + # Prefix with `app_` to prevent conflict with `termux_core__sh__termux_scoped_env_variable` argument. + local app_scoped_var_scope_name="" + local termux_core__apps_info_env_file="" + + # If `skip_sourcing` is enabled, then directly get the value from + # the current environment. + if [ "$skip_sourcing" = "1" ]; then + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + # If `skip_sourcing_if_cur_app_var` is enabled and app scope of + # the variable to get is the same as the app scope of the current + # app, then directly get the value from the current + # environment. + elif [ "$skip_sourcing_if_cur_app_var" = "1" ] && [ -n "${TERMUX_ENV__S_APP:-}" ]; then + # Get app scope of the variable to get. If caller passed + # `scoped_var_scope_mode` as `s=*`, then it will be returned + # as is, otherwise if `ss=*` or `cn=*` is passed, then full + # variable scope including Termux root scope prefix will + # be returned. The `scoped_var_sub_name` arg is passed as + # an empty string as we only need the app scope name. + termux_core__sh__termux_scoped_env_variable get-name \ + app_scoped_var_scope_name "$scoped_var_scope_mode" "" || return $? + + if [ "$TERMUX_ENV__S_APP" = "$app_scoped_var_scope_name" ]; then + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + fi + fi + + + # Unset variable to get before sourcing, otherwise if the + # `termux-apps-info.env` file does not explicitly unset it if + # variable should not be set, then any value including empty + # (for `validator_mode` `*`) that exists in the current environment + # may get used. + termux_core__sh__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "$scoped_var_sub_name" || return $? + + + # First source the `termux-apps-info.env` file to load the latest + # value in the current environment before getting it. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__sh__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" p+="''|/*[!/]" || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + . "$termux_core__apps_info_env_file" || return $? + elif [ "$ensure_sourcing" = "1" ]; then + return 69 # EX__UNAVAILABLE + fi + + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + fi + +} + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable new file mode 100644 index 00000000000000..e14e991b3c90ff --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable @@ -0,0 +1,392 @@ +##### TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (START) ##### + +## +# Get/Set/Unset variable names and values for `TERMUX*__` and other +# scoped environment variables exported by different Termux runtime +# components, with support for fallback values and validation of +# values. +# +# The `posix_validator` logic is based on `libalteran-sh` +# `shell__validate_variable_with_posix_validator()` function. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_scoped_env_variable` `get-name` \ +# `` \ +# `` `` +# `termux_core__sh__termux_scoped_env_variable` `get-value` \ +# `` \ +# `` `` \ +# `` [``] +# `termux_core__sh__termux_scoped_env_variable` `set-value` \ +# `` `` \ +# `` +# `termux_core__sh__termux_scoped_env_variable` `unset-value` \ +# `` `` +## +termux_core__sh__termux_scoped_env_variable() { + + local return_value + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-name" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'get-name' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + local posix_validator="${4:-}" + + local validator_arg + local validator_mode + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + # Remove args before `default_values` to be used in `var_value_cur` for loop below. + shift 4 + + case "$posix_validator" in + '?'|'*') + validator_arg="" + validator_mode="$posix_validator" + ;; + 'p+='?*|'p-='?*|'c+='?*|'c-='?*) + validator_arg="${posix_validator#???}" # 3:end + validator_mode="${posix_validator%"$validator_arg"}" # 0:3 + ;; + *) + echo "The posix_validator '$posix_validator' \ +argument passed to 'termux_core__sh__termux_scoped_env_variable' is not valid. \ +It must either be equal to \`?\` or \`*\`, or a pattern that starts with \`p+=\` or \`p-=\`, \ +or a executable or function that starts with \`c+=\` or \`c-=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + elif [ "$command_type" = "set-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + # shellcheck disable=SC2034 + local value_to_set="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'set-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + + if [ $# -ne 2 ]; then + echo "Invalid argument count $# for the 'unset-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_scoped_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + local i + local is_value_valid + local scoped_var_root_scope_name="" + local scoped_var_scope_name="" + local scoped_var_sub_scope_name="" + local scoped_var_name="" + local scoped_var_value_invalid_error_suffix="" + local var_value_cur + + + if [ "$command_action" = "get" ]; then + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if ! termux_core__sh__is_valid_shell_variable_name "$output_mode"; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + + case "$scoped_var_scope_mode" in + s=?*) scoped_var_scope_name="${scoped_var_scope_mode#??}";; # 2:end + ss=?*) scoped_var_sub_scope_name="${scoped_var_scope_mode#???}";; # 3:end + '') ;; + cn=termux) scoped_var_sub_scope_name="_";; + cn=termux-app) scoped_var_sub_scope_name="APP__";; + cn=termux-api-app) scoped_var_sub_scope_name="API_APP__";; + cn=termux-float-app) scoped_var_sub_scope_name="FLOAT_APP__";; + cn=termux-gui-app) scoped_var_sub_scope_name="GUI_APP__";; + cn=termux-tasker-app) scoped_var_sub_scope_name="TASKER_APP__";; + cn=termux-widget-app) scoped_var_sub_scope_name="WIDGET_APP__";; + cn=termux-x11-app) scoped_var_sub_scope_name="X11_APP__";; + cn=termux-core) scoped_var_sub_scope_name="CORE__";; + cn=termux-exec) scoped_var_sub_scope_name="EXEC__";; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__sh__termux_scoped_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\`, \ +or an environment variable scope starting with \`s=\` or \`ss=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + + + if [ -n "$scoped_var_scope_mode" ]; then + if [ -n "$scoped_var_scope_name" ]; then + # Generate the full name for the variable under the provided root and sub scope. + scoped_var_name="${scoped_var_scope_name}${scoped_var_sub_name}" + else + # Generate the full name for the variable under the Termux root scope and provided sub scope. + # If `TERMUX_ENV__S_ROOT` environment variable set. + # shellcheck disable=SC2050 + if [ -n "${TERMUX_ENV__S_ROOT:-}" ]; then + scoped_var_root_scope_name="$TERMUX_ENV__S_ROOT" + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_root_scope_name"; then + echo "The TERMUX_ENV__S_ROOT environment variable value '$scoped_var_root_scope_name' \ +while running 'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + # If `TERMUX_ENV__S_ROOT` placeholder got replaced during build time. + elif [ "@TERMUX_ENV__S_ROOT@" != @"TERMUX_ENV__S_ROOT"@ ]; then + scoped_var_root_scope_name="@TERMUX_ENV__S_ROOT@" + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_root_scope_name"; then + echo "The TERMUX_ENV__S_ROOT build value '$scoped_var_root_scope_name' \ +while running 'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + else + scoped_var_root_scope_name="TERMUX_" + fi + + scoped_var_name="${scoped_var_root_scope_name}${scoped_var_sub_scope_name}${scoped_var_sub_name}" + fi + + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_name"; then + echo "The name of the variable to $command_action '$scoped_var_name' generated in \ +'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 64 # EX__USAGE + fi + + + # If command type equals `get-name`, then return the variable name and exit. + if [ "$command_type" = "get-name" ]; then + #echo "scoped_var_name=$scoped_var_name". + if [ "$output_mode" = ">" ]; then + printf "%s" "$scoped_var_name" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$scoped_var_name\" + #eval "echo $output_mode=\"\${${output_mode}}\"" + return $? + else + return 0 + fi + elif [ "$command_type" = "set-value" ]; then + eval "$scoped_var_name"=\"\$value_to_set\" + return $? + elif [ "$command_type" = "unset-value" ]; then + unset "$scoped_var_name" + return $? + fi + else + if [ "$command_type" = "get-name" ] || \ + [ "$command_type" = "set-value" ] || [ "$command_type" = "unset-value" ]; then + echo "The scoped_var_scope_mode argument for the variable \ +to $command_action passed for the '$command_type' command to \ +'termux_core__sh__termux_scoped_env_variable' is not set." 1>&2 + return 64 # EX__USAGE + fi + fi + + + # If command type equals `get-value`, then find the first valid in variable name/values passed. + if [ "$command_type" = "get-value" ]; then + if [ "$validator_mode" = "c+=" ] || [ "$validator_mode" = "c-=" ]; then + local command_arg_first_arg="${validator_arg%% *}" + + # Check if a command exists, like an executable in `$PATH`, + # a shell function or a path to an executable. + # - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html + if ! command -v -- "$command_arg_first_arg" >/dev/null 2>&1; then + # Check if absolute or relative path as `command -v` will only check executables under `$PATH`. + local validator_arg_is_path=0 + case "$command_arg_first_arg" in + *[/]*) validator_arg_is_path=1;; + esac + + if [ "$validator_arg_is_path" = "1" ] && [ -f "$command_arg_first_arg" ] && [ -x "$command_arg_first_arg" ]; then + : + else + echo "The validator command '$command_arg_first_arg' \ +not found while running 'termux_core__sh__termux_scoped_env_variable' \ +that is set in the posix_validator '$posix_validator' argument." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + # Loop on [`` ``] to + # find the first valid value. + i=0 + for var_value_cur in "$scoped_var_name" "$@"; do + var_value_cur="${var_value_cur:-}" + + # If first loop, then expand the `scoped_var_name` to its value. + if [ "$i" = 0 ] && [ -n "$scoped_var_name" ]; then + # If mode is `*`, expand variable if it is set, or defined + # but empty. Else if its not defined, then ignore it. + if [ "$validator_mode" = "*" ]; then + eval '[ -n "${'"$scoped_var_name"'+x}" ] && var_value_cur="$'"$scoped_var_name"'" || { i=$((i + 1)); continue; }' + else + eval 'var_value_cur="${'"$scoped_var_name"':-}"' + fi + fi + + is_value_valid=0 + if [ "$validator_mode" = "p+=" ]; then + return_value=0 + eval 'case "$var_value_cur" in + '"$validator_arg"') is_value_valid=1;; + esac' || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failure while using a positive shell \ +'case' statement to validate the variable value with the pattern '$validator_arg' \ +passed to 'termux_core__sh__termux_scoped_env_variable'." 1>&2 + return 64 # EX__USAGE + fi + elif [ "$validator_mode" = "p-=" ]; then + return_value=0 + eval 'case "$var_value_cur" in + '"$validator_arg"') :;; + *) is_value_valid=1;; + esac' || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failure while using a negative shell \ +'case' statement to validate the variable value with the pattern '$validator_arg' \ +passed to 'termux_core__sh__termux_scoped_env_variable'." 1>&2 + return 64 # EX__USAGE + fi + elif [ "$validator_mode" = "c+=" ]; then + # Do not use `if command; then` to preserve `set -e` failures in called function. + return_value=0 + $validator_arg "$var_value_cur" || return_value=$? + if [ $return_value -eq 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + elif [ "$validator_mode" = "c-=" ]; then + # Do not use `if ! command; then` to preserve `set -e` failures in called function. + return_value=0 + $validator_arg "$var_value_cur" || return_value=$? + if [ $return_value -ne 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + else + # If mode is `?` or `*`, and value is set, + # or mode is `*` and value is not set. + if [ -n "$var_value_cur" ] || [ "$validator_mode" = "*" ]; then + is_value_valid=1 + # Else if mode is `?`, and value is not set. + else + is_value_valid=0 + fi + fi + + if [ "$is_value_valid" = "1" ]; then + #echo "var_value_cur=$var_value_cur" + if [ "$output_mode" = ">" ]; then + printf "%s" "$var_value_cur" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$var_value_cur\" + #eval "echo $output_mode=\"\${${output_mode}}\"" + return $? + else + return 0 + fi + fi + + i=$((i + 1)) + done + + + # If a valid value not found. + + if [ -n "$scoped_var_name" ]; then + scoped_var_value_invalid_error_suffix=" that is read from the '\$$scoped_var_name' variable" + fi + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use that + # wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `scoped_var_name` is equal to output variable in + # `output_mode`, then `scoped_var_name` would get unset before + # its read. + eval "$output_mode"=\"\" || return $? + + echo "Failed to find a valid value to set to the '\$$output_mode' \ +variable${scoped_var_value_invalid_error_suffix}." 1>&2 + else + echo "Failed to find a valid value${scoped_var_value_invalid_error_suffix}." 1>&2 + fi + return 81 # C_EX__NOT_FOUND + fi + +} + +## +# Check if a string is a valid shell variable name. +# (like `TERMUX__VAR`). +# - https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-name +# +# +# `termux_core__sh__is_valid_shell_variable_name` `variable_name` +## +termux_core__sh__is_valid_shell_variable_name() { + + local variable_name="${1:-}" + local variable_name_rest="${variable_name#?}" # 1:end + local variable_name_first_char="${variable_name%"$variable_name_rest"}" # 0:1 + + case "$variable_name_first_char" in + [a-zA-Z_]) + case "$variable_name_rest" in + *[!a-zA-Z0-9_]*) return 1;; + *) return 0;; + esac;; + *) return 1;; + esac + +} + +##### TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (END) ##### diff --git a/packages/termux-core/basename.patch b/packages/termux-core/basename.patch new file mode 100644 index 00000000000000..02520ab6b4cced --- /dev/null +++ b/packages/termux-core/basename.patch @@ -0,0 +1,14 @@ +Fix `implicit declaration of function 'basename' is invalid in C99` +--- + +diff -uNr a/lib/termux-core_nos_c_tre/src/unix/file/UnixFileUtils.c b/lib/termux-core_nos_c_tre/src/unix/file/UnixFileUtils.c +--- a/lib/termux-core_nos_c_tre/src/unix/file/UnixFileUtils.c ++++ b/lib/termux-core_nos_c_tre/src/unix/file/UnixFileUtils.c +@@ -1,6 +1,7 @@ + #define _GNU_SOURCE + #include + #include ++#include /* for basename(3) */ + #include + #include + #include diff --git a/packages/termux-core/build.sh b/packages/termux-core/build.sh new file mode 100644 index 00000000000000..96d434d3275769 --- /dev/null +++ b/packages/termux-core/build.sh @@ -0,0 +1,43 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-core-package +TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux core" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=0.1.0 +TERMUX_PKG_SRCURL=https://github.com/termux/termux-core-package/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=9eb8586be7c1a34b66cd4c77f1145c5e968da943d3dfc4f0d12f534223b165b7 +TERMUX_PKG_ESSENTIAL=true +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TERMUX_CORE_PKG__VERSION=${TERMUX_PKG_VERSION} TERMUX_CORE_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX__REPOS_HOST_ORG_NAME=${TERMUX__REPOS_HOST_ORG_NAME} TERMUX__REPOS_HOST_ORG_URL=${TERMUX__REPOS_HOST_ORG_URL} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX__PREFIX__TMP_DIR=${TERMUX__PREFIX__TMP_DIR} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP} \ +TERMUX_ENV__SS_TERMUX_API_APP=${TERMUX_ENV__SS_TERMUX_API_APP} TERMUX_ENV__S_TERMUX_API_APP=${TERMUX_ENV__S_TERMUX_API_APP} \ +TERMUX_ENV__SS_TERMUX_ROOTFS=${TERMUX_ENV__SS_TERMUX_ROOTFS} TERMUX_ENV__S_TERMUX_ROOTFS=${TERMUX_ENV__S_TERMUX_ROOTFS} \ +TERMUX_ENV__SS_TERMUX_CORE=${TERMUX_ENV__SS_TERMUX_CORE} TERMUX_ENV__S_TERMUX_CORE=${TERMUX_ENV__S_TERMUX_CORE} \ +TERMUX_ENV__SS_TERMUX_CORE__TESTS=${TERMUX_ENV__SS_TERMUX_CORE__TESTS} TERMUX_ENV__S_TERMUX_CORE__TESTS=${TERMUX_ENV__S_TERMUX_CORE__TESTS} \ +TERMUX_ENV__SS_TERMUX_EXEC__TESTS=${TERMUX_ENV__SS_TERMUX_EXEC__TESTS} TERMUX_ENV__S_TERMUX_EXEC__TESTS=${TERMUX_ENV__S_TERMUX_EXEC__TESTS} \ +TERMUX_APP__NAMESPACE=${TERMUX_APP__NAMESPACE} \ +TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME=${TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME} TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME=${TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME} \ +TERMUX_PKGS__REPO_NAME=${TERMUX_PKGS__REPO_NAME} TERMUX_PKGS__REPO_URL=${TERMUX_PKGS__REPO_URL} \ +TERMUX_PKGS__BUILD__REPO_ROOT_DIR=${TERMUX_PKGS__BUILD__REPO_ROOT_DIR} \ +TERMUX_CORE_PKG__REPO_NAME=${TERMUX_CORE_PKG__REPO_NAME} TERMUX_CORE_PKG__REPO_URL=${TERMUX_CORE_PKG__REPO_URL}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} + +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \ + \( \ + \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -a \ + \( ! -path "./libexec/installed-tests/termux-core/*" \) \ + \) +} diff --git a/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts b/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts new file mode 100755 index 00000000000000..b109eb39169007 --- /dev/null +++ b/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts @@ -0,0 +1,258 @@ +#!/bin/bash +# shellcheck shell=bash + +if [ -z "${BASH_VERSION:-}" ]; then + echo "The 'termux-replace-termux-core-src-scripts' script must be run from a 'bash' shell."; return 64 2>/dev/null|| exit 64 # EX__USAGE +fi + + + +REGEX__ABSOLUTE_PATH='^(/[^/]+)+$' + +function termux_rtcss__log_errors() { echo "$@" 1>&2; } + +termux_rtcss__show_help() { + + cat <<'HELP_EOF' +termux-replace-termux-core-src-scripts replaces inplace the `@@` +formatted build time placeholders for the termux-core package source +scripts with their content in the `input_files` supplied. + + +Usage: + termux-replace-termux-core-src-scripts + + +The scripts whose placeholders are replaced exist in the +`packages/termux-core/app/main/scripts` directory of the `termux-package` +repo. The `` in the `@@` placeholders must be equal to +uppercased script file name whose content to replace. + + +The following required variables must be exported when executed this +script, which are defined in the `scripts/properties.sh` file of the +`termux-package` repo. + +- TERMUX_ENV__S_ROOT +- TERMUX__PREFIX +- TERMUX_PKGS__REPO_URL +- TERMUX_CORE_PKG__REPO_URL +HELP_EOF + +} + +termux_rtcss__main() { + + local return_value + + if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + termux_rtcss__show_help || return $? + return 0 + else + termux_rtcss__replace_termux_core_src_scripts "$@" + return_value=$? + if [ $return_value -eq 64 ]; then # EX__USAGE + echo "" + termux_rtcss__show_help + fi + return $return_value + fi + +} + +termux_rtcss__replace_termux_core_src_scripts() { + + local return_value + + if [ $# -lt 1 ]; then + termux_rtcss__log_errors "input_files not passed." + return 64 + fi + + local exported_variable + local input_file + local src_script_file + local src_script_file_basename + local src_script_file_placeholder + local src_script_content + local src_script_escaped_content + local src_script_content_var_name + local termux_core__src_srcipts_dir + + + termux_core__src_srcipts_dir=$(realpath "$(dirname "$TERMUX_RTCSS__ARG_0")/../../app/main/scripts") + if [[ ! "$termux_core__src_srcipts_dir" =~ $REGEX__ABSOLUTE_PATH ]]; then + termux_rtcss__log_errors "The termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' with length ${#termux_core__src_srcipts_dir} is invalid." + termux_rtcss__log_errors "The termux_core__src_srcipts_dir must be an absolute path for a sub path under rootfs '/'." + return 1 + fi + + if [ ! -d "$termux_core__src_srcipts_dir" ]; then + termux_rtcss__log_errors "The termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory does not exist." + return 1 + fi + + # The order matters here. The scripts that have `@*@` placeholders + # for other scripts must come before their placeholder scripts, + # so that first the caller scripts get replaced in the input + # files, and then the placeholders for other scripts get replaced + # in the input files. + # For example, the `termux_core__bash__termux_apps_info_env_variable` script + # has the `@TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE@` + # placeholder for the `termux_core__bash__termux_scoped_env_variable` + # script and so the `termux_core__bash__termux_apps_info_env_variable` script + # comes first. + local -a required_src_scripts=( + "termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name" + "termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable" + "termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable" + "termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name" + "termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable" + "termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable" + ) + + for src_script_file in "${required_src_scripts[@]}"; do + if [ ! -f "$termux_core__src_srcipts_dir/$src_script_file" ]; then + termux_rtcss__log_errors "The '$src_script_file' source script file \ +not found under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory." + return 1 + fi + done + + + local -a required_exported_variables=( + "TERMUX_ENV__S_ROOT" + "TERMUX__PREFIX" + "TERMUX_PKGS__REPO_URL" + "TERMUX_CORE_PKG__REPO_URL" + ) + + for exported_variable in "${required_exported_variables[@]}"; do + if [ -z "${!exported_variable}" ]; then + termux_rtcss__log_errors "The $exported_variable variable is not set." + return 1 + fi + done + + + for input_file in "$@"; do + if [ ! -f "$input_file" ]; then + termux_rtcss__log_errors "The input file '$input_file' passed does not exist at path" + return 1 + fi + done + + + + # For all the required_src_scripts, read their content into local variables. + for src_script_file in "${required_src_scripts[@]}"; do + # Read the src_script_file and ensure its not empty. + src_script_content="$(cat "$termux_core__src_srcipts_dir/$src_script_file")" + return_value=$? + if [ $return_value -ne 0 ]; then + termux_rtcss__log_errors "Failed to read the '$src_script_file' source script file \ +under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory." + return $return_value + elif [ -z "$src_script_content" ]; then + termux_rtcss__log_errors "The '$src_script_file' source script file \ +under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory is empty." + return 1 + fi + + # Replace placeholder for variables (not scripts) in the src_script_content. + src_script_content="$(printf "%s" "$src_script_content" | sed \ + -e "s%[@]TERMUX_ENV__S_ROOT[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_ENV__S_ROOT")%g" \ + -e "s%[@]TERMUX__PREFIX[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX__PREFIX")%g" \ + -e "s%[@]TERMUX_PKGS__REPO_URL[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_PKGS__REPO_URL")%g" \ + -e "s%[@]TERMUX_CORE_PKG__REPO_URL[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_CORE_PKG__REPO_URL")%g" + )" || return $? + + # Create a local variable with the same name as its script file name + # with `/` replaced with `__` and `__content` appended. + src_script_content_var_name="${src_script_file//\//__}__content" + local "$src_script_content_var_name" + printf -v "$src_script_content_var_name" "%s" "$src_script_content" + + #echo "$src_script_content_var_name:"$'\n```'"${!src_script_content_var_name}"$'\n```' + done + + + + # For all the required_src_scripts. + for src_script_file in "${required_src_scripts[@]}"; do + src_script_content_var_name="${src_script_file//\//__}__content" + src_script_file_basename="${src_script_file##*/}" + + # Escape the content to be used as a sed replacement string. + src_script_escaped_content="$(termux_rtcss__get_sed_replacement_escaped_string \ + "${!src_script_content_var_name}")" || return $? + + # For all the input_files, replace the first line found that + # contains the `@src_script_file@` placeholder with the script content. + for input_file in "$@"; do + sed -i'' -e "s/.*[@]${src_script_file_basename^^}[@].*/$src_script_escaped_content/" "$input_file" + return_value=$? + if [ $return_value -ne 0 ]; then + termux_rtcss__log_errors "Failed to replace the '$src_script_file' source script \ +placeholder '@${src_script_file_basename^^}@' in the input file '$input_file' with script content." + return $return_value + fi + done + done + + return 0 + +} + +## +# Escape the following characters with a backslash `\` in a string +# that is to be used as the `replacement` string in a `sed` +# `s/regexp/replacement/` expression so that the replacement string +# is literally replaced, even if the string is multiline: +# - `&` used to reference entire string matched by the regex, +# - `\` used to reference for capturing groups, like `\1`, `\2`, etc. +# - `/` used as the delimiter in sed `s/regexp/replacement/` expression. +# - `\n` characters must be escaped. +# +# The escaped replacement string will be echoed to `stdout`. +# +# **See Also:** +# - https://stackoverflow.com/a/29613573/14686958 +# +# `termux_rtcss__get_sed_replacement_escaped_string` `` +## +termux_rtcss__get_sed_replacement_escaped_string() { + + local sed_replacement_escaped_string + + # read is used to preserve trailing newlines that would be lost if a subshell is used, but isn't posix shell compliant. + #IFS= read -d '' -r sed_replacement_escaped_string < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$1") + + # Instead of using the `sed -z` option, we manually add everything + # to pattern space. + # `$!` checks if we are before last line, so if we are not on it, + # then we add next line to pattern space. + # Then we escape the required characters. + # The x is added in subshell to preserve trailing newlines and then removed. + sed_replacement_escaped_string="$(printf '%s' "$1" | sed -e '$!{:a;N;$!ba;}; s/[&/\]/\\&/g; s/\n/\\&/g'; printf "%s" x)" + sed_replacement_escaped_string="${sed_replacement_escaped_string%x}" + + printf %s "${sed_replacement_escaped_string%$'\n'}" + +} + + + +TERMUX_RTCSS__ARG_0="$0" + +# If script is sourced, return with error, otherwise call main function. +# - https://stackoverflow.com/a/28776166/14686958 +# - https://stackoverflow.com/a/29835459/14686958 +if (return 0 2>/dev/null); then + echo "${0##*/} cannot be sourced as '\$0' is required by internal functions, \ +which will be invalid if functions are sourced and called directly." 1>&2 + return 64 # EX__USAGE +else + termux_rtcss__main "$@" + exit $? +fi diff --git a/packages/termux-exec/build.sh b/packages/termux-exec/build.sh index baca19ea05280a..77635c3fc0dc92 100644 --- a/packages/termux-exec/build.sh +++ b/packages/termux-exec/build.sh @@ -1,9 +1,40 @@ -TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-exec -TERMUX_PKG_DESCRIPTION="An execve() wrapper to make /bin and /usr/bin shebangs work" +TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-exec-package +TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux exec including a LD_PRELOAD shared library for proper functioning of the Termux execution environment" TERMUX_PKG_LICENSE="Apache-2.0" -TERMUX_PKG_VERSION=0.4 -TERMUX_PKG_REVISION=2 -TERMUX_PKG_SRCURL=https://github.com/termux/termux-exec/archive/v$TERMUX_PKG_VERSION.tar.gz -TERMUX_PKG_SHA256=9a8d42d211a7d461d61dcd4e3ef984014c2c2c696cfd6394bae389af13572627 +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=1:2.0.0 +TERMUX_PKG_SRCURL=https://github.com/termux/termux-exec-package/archive/refs/tags/v${TERMUX_PKG_VERSION:2}.tar.gz +TERMUX_PKG_SHA256=480452bd0b9cf7b6eb6549825a8faebb2a4afa8c88b9e1d621fb05528ba7ee6e +TERMUX_PKG_BUILD_DEPENDS="termux-core" TERMUX_PKG_ESSENTIAL=true TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TERMUX_EXEC_PKG__VERSION=${TERMUX_PKG_VERSION} TERMUX_EXEC_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP} \ +TERMUX_ENV__SS_TERMUX_ROOTFS=${TERMUX_ENV__SS_TERMUX_ROOTFS} TERMUX_ENV__S_TERMUX_ROOTFS=${TERMUX_ENV__S_TERMUX_ROOTFS} \ +TERMUX_ENV__SS_TERMUX_EXEC=${TERMUX_ENV__SS_TERMUX_EXEC} TERMUX_ENV__S_TERMUX_EXEC=${TERMUX_ENV__S_TERMUX_EXEC} \ +TERMUX_ENV__SS_TERMUX_EXEC__TESTS=${TERMUX_ENV__SS_TERMUX_EXEC__TESTS} TERMUX_ENV__S_TERMUX_EXEC__TESTS=${TERMUX_ENV__S_TERMUX_EXEC__TESTS} \ +LIBTERMUX_EXEC__NOS__C__EXECVE_CALL__CHECK_ARGV0_BUFFER_OVERFLOW=1" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} + +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \ + \( \ + \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -a \ + \( ! -path "./libexec/installed-tests/termux-exec/*" \) \ + \) +} + +termux_step_create_debscripts() { + termux_step_create_debscripts__copy_from_dir "$TERMUX_PKG_SRCDIR/build/output/packaging/debian" . +} diff --git a/packages/termux-exec/memcpy.patch b/packages/termux-exec/memcpy.patch new file mode 100644 index 00000000000000..7e1921c75933f1 --- /dev/null +++ b/packages/termux-exec/memcpy.patch @@ -0,0 +1,23 @@ +Fix `implicit declaration of function 'mempcpy' is invalid in C99` +--- + +diff -uNr a/lib/termux-exec_nos_c_tre/src/termux/api/termux_exec/ld_preload/direct/exec/ExecVariantsIntercept.c b/lib/termux-exec_nos_c_tre/src/termux/api/termux_exec/ld_preload/direct/exec/ExecVariantsIntercept.c +--- a/lib/termux-exec_nos_c_tre/src/termux/api/termux_exec/ld_preload/direct/exec/ExecVariantsIntercept.c ++++ b/lib/termux-exec_nos_c_tre/src/termux/api/termux_exec/ld_preload/direct/exec/ExecVariantsIntercept.c +@@ -44,6 +44,16 @@ + + static const char* LOG_TAG = "exec"; + ++#ifdef __ANDROID__ ++# ifndef ANDROID_MEMPCPY ++# define ANDROID_MEMPCPY 1 ++static __inline__ void* mempcpy(void* dest, void const* src, size_t n) ++{ ++ return memcpy(dest, src, n) + n; ++} ++# endif ++#endif ++ + int execlIntercept(bool wasIntercepted, int variant, const char *name, const char *argv0, va_list ap) { + if (wasIntercepted) { + logErrorDebug(LOG_TAG, "<----- %s() intercepted ----->", EXEC_VARIANTS_STR[variant]); diff --git a/packages/termux-tools/build.sh b/packages/termux-tools/build.sh index c3e3ad8dd6b5bf..5b8aeffd28f023 100644 --- a/packages/termux-tools/build.sh +++ b/packages/termux-tools/build.sh @@ -1,7 +1,7 @@ TERMUX_PKG_HOMEPAGE=https://termux.dev/ TERMUX_PKG_DESCRIPTION="Basic system tools for Termux" TERMUX_PKG_LICENSE="GPL-3.0" -TERMUX_PKG_VERSION=0.77 +TERMUX_PKG_VERSION=0.78 TERMUX_PKG_SKIP_SRC_EXTRACT=true TERMUX_PKG_PLATFORM_INDEPENDENT=true TERMUX_PKG_ESSENTIAL=true @@ -12,7 +12,7 @@ TERMUX_PKG_CONFFILES="etc/motd" # Some of these packages are not dependencies and used only to ensure # that core packages are installed after upgrading (we removed busybox # from essentials). -TERMUX_PKG_DEPENDS="bzip2, coreutils, curl, dash, diffutils, findutils, gawk, grep, gzip, less, procps, psmisc, sed, tar, termux-am, termux-exec, xz-utils" +TERMUX_PKG_DEPENDS="bzip2, coreutils, curl, dash, diffutils, findutils, gawk, grep, gzip, less, procps, psmisc, sed, tar, termux-am, termux-core, termux-exec, xz-utils" # Optional packages that are distributed as part of bootstrap archives. TERMUX_PKG_RECOMMENDS="ed, dos2unix, inetutils, net-tools, patch, unzip, util-linux" @@ -37,6 +37,7 @@ termux_step_make_install() { termux-upgrade-repo termux-wake-lock termux-wake-unlock; do install -Dm700 $TERMUX_PKG_BUILDER_DIR/$script $TERMUX_PREFIX/bin/$script perl -p -i -e "s%\@TERMUX_PREFIX\@%${TERMUX_PREFIX}%g" $TERMUX_PREFIX/bin/$script + perl -p -i -e "s%\@TERMUX_APP_PACKAGE\@%${TERMUX_APP_PACKAGE}%g" $TERMUX_PREFIX/bin/$script done install -Dm600 $TERMUX_PKG_BUILDER_DIR/motd $TERMUX_PREFIX/etc/motd diff --git a/packages/termux-tools/login b/packages/termux-tools/login index 0da3b44b79fac7..3635ac165bf1db 100755 --- a/packages/termux-tools/login +++ b/packages/termux-tools/login @@ -18,8 +18,17 @@ else done fi -if [ -f @TERMUX_PREFIX@/lib/libtermux-exec.so ]; then - export LD_PRELOAD=@TERMUX_PREFIX@/lib/libtermux-exec.so +# Export `libtermux-exec-ld-preload.so` for `termux-exec` +# package version `>= 2.0.0`. +# Some devices may not support setting `$LD_PRELOAD`. +# - https://github.com/termux/termux-packages/issues/2066 +# - https://github.com/termux/termux-packages/commit/1ec6c042 +# - https://github.com/termux/termux-packages/commit/6fb2bb2f +if [ -f "@TERMUX_PREFIX@/lib/libtermux-exec-ld-preload.so" ]; then + export LD_PRELOAD="@TERMUX_PREFIX@/lib/libtermux-exec-ld-preload.so" + $SHELL -c "busybox true" > /dev/null 2>&1 || unset LD_PRELOAD +elif [ -f "@TERMUX_PREFIX@/lib/libtermux-exec.so" ]; then + export LD_PRELOAD="@TERMUX_PREFIX@/lib/libtermux-exec.so" $SHELL -c "busybox true" > /dev/null 2>&1 || unset LD_PRELOAD fi diff --git a/packages/termux-tools/termux-info b/packages/termux-tools/termux-info index aec1b87a9117c1..f1e266fb58355b 100755 --- a/packages/termux-tools/termux-info +++ b/packages/termux-tools/termux-info @@ -1,9 +1,24 @@ #!/bin/bash -if [ "$#" != "0" ]; then - echo 'usage: termux-info' +NO_SET_CLIPBOARD=0 + +show_usage () { + echo 'usage: termux-info [--no-set-clipboard]' echo 'Provides information about Termux, and the current system. Helpful for debugging.' - exit + exit 0 + +} + +while [ $# -ge 1 ]; do + case "$1" in + --no-set-clipboard) NO_SET_CLIPBOARD="1"; shift;; + -h | --help) show_usage;; + *) break;; + esac +done + +if [ "$#" != "0" ]; then + show_usage fi updates() { @@ -48,7 +63,23 @@ repo_subscriptions() { done < <(find "@TERMUX_PREFIX@/etc/apt/sources.list.d" -maxdepth 1 ! -type d) } -output="Packages CPU architecture: +case "${TERMUX__USER_ID:-}" in ''|*[!0-9]*|0[0-9]*) TERMUX__USER_ID=0;; esac +export TERMUX__USER_ID + +output="" + +if [ -n "${TERMUX_VERSION:-}" ]; then +# Application version is exported in Termux v0.107 or higher only. +output+="Termux Variables: +$(compgen -e TERMUX_ | while read v; do echo "${v}=${!v}"; done) +" +else +output+="Termux Variables: +unsupported +" +fi + +output+="Packages CPU architecture: $(dpkg --print-architecture) Subscribed repositories: $(repo_subscriptions) @@ -61,10 +92,23 @@ $(uname -a) Device manufacturer: $(getprop ro.product.manufacturer) Device model: -$(getprop ro.product.model)" +$(getprop ro.product.model) +Supported ABIs: +SUPPORTED_ABIS: $(getprop ro.product.cpu.abilist) +SUPPORTED_32_BIT_ABIS: $(getprop ro.product.cpu.abilist32) +SUPPORTED_64_BIT_ABIS: $(getprop ro.product.cpu.abilist64) +LD Variables: +LD_LIBRARY_PATH=$LD_LIBRARY_PATH +LD_PRELOAD=$LD_PRELOAD" +# Escape '\$[](){}|^.?+*' with backslashes for regex +escaped_package_name="$(echo -n "@TERMUX_APP_PACKAGE@" | sed -zE -e 's/[][\.|$(){}?+*^]/\\&/g')" +TERMUX_PLUGINS="$(pm list packages --user "$TERMUX__USER_ID" 2>&1 /dev/null diff --git a/packages/tudo/build.sh b/packages/tudo/build.sh new file mode 100644 index 00000000000000..fe9a962af667ec --- /dev/null +++ b/packages/tudo/build.sh @@ -0,0 +1,25 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/agnostic-apollo/tudo +TERMUX_PKG_DESCRIPTION="A wrapper script to drop to the supported shells or execute shell script files or their text passed as an argument as the Termux app (u_a) user in the Termux app" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@agnostic-apollo" +TERMUX_PKG_VERSION=1.0.0 +TERMUX_PKG_SRCURL=https://github.com/agnostic-apollo/tudo/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=5f99e5c1cd13ff5ec57cfd4842f50acac382c7c8c693816c763c10589a539b66 +TERMUX_PKG_DEPENDS="bash" +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TUDO_PKG__VERSION=${TERMUX_PKG_VERSION} TUDO_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} diff --git a/scripts/Dockerfile b/scripts/Dockerfile index be25b3723b3af1..762ab3c05c4990 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -10,7 +10,10 @@ FROM ubuntu:19.04 ENV LANG C.UTF-8 # Needed for setup: -COPY ./setup-ubuntu.sh ./setup-android-sdk.sh ./properties.sh /tmp/ +RUN rm -rf /tmp/termux-packages +RUN mkdir -p /tmp/termux-packages +RUN mkdir -p /tmp/termux-packages/scripts +COPY ./properties.sh ./setup-android-sdk.sh ./setup-ubuntu.sh /tmp/termux-packages/scripts/ # Setup needed packages and the Android SDK and NDK: RUN apt-get update && \ @@ -19,8 +22,9 @@ RUN apt-get update && \ adduser --disabled-password --shell /bin/bash --gecos "" builder && \ echo "builder ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/builder && \ chmod 0440 /etc/sudoers.d/builder && \ - su - builder -c /tmp/setup-ubuntu.sh && \ - su - builder -c /tmp/setup-android-sdk.sh && \ + chmod a+rx /tmp/termux-packages/scripts/*.sh && \ + su - builder -c "/tmp/termux-packages/scripts/setup-ubuntu.sh" && \ + su - builder -c "/tmp/termux-packages/scripts/setup-android-sdk.sh" && \ # Removed unused parts to make a smaller Docker image: apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ diff --git a/scripts/build/termux_download.sh b/scripts/build/termux_download.sh index 4f729d506ce9c0..b0f83da4e6b6f4 100755 --- a/scripts/build/termux_download.sh +++ b/scripts/build/termux_download.sh @@ -1,45 +1,85 @@ +#!/usr/bin/bash + termux_download() { - if [ $# != 3 ]; then - termux_error_exit "termux_download(): Invalid arguments - expected \$URL \$DESTINATION \$CHECKSUM" + if [[ $# != 2 ]] && [[ $# != 3 ]]; then + echo "termux_download(): Invalid arguments - expected []" 1>&2 + return 1 fi local URL="$1" local DESTINATION="$2" - local CHECKSUM="$3" + local CHECKSUM="${3:-SKIP_CHECKSUM}" + + if [[ "$URL" =~ ^file://(/[^/]+)+$ ]]; then + local source="${URL:7}" # Remove `file://` prefix + + if [ -d "$source" ]; then + # Create tar file from local directory + echo "Downloading local source directory at '$source'" + rm -f "$DESTINATION" + (cd "$(dirname "$source")" && tar -cf "$DESTINATION" --exclude=".git" "$(basename "$source")") + return 0 + elif [ ! -f "$source" ]; then + echo "No local source file found at path of URL '$URL'" + return 1 + fi + fi if [ -f "$DESTINATION" ] && [ "$CHECKSUM" != "SKIP_CHECKSUM" ]; then # Keep existing file if checksum matches. local EXISTING_CHECKSUM - EXISTING_CHECKSUM=$(sha256sum "$DESTINATION" | cut -f 1 -d ' ') - if [ "$EXISTING_CHECKSUM" = "$CHECKSUM" ]; then return; fi + EXISTING_CHECKSUM=$(sha256sum "$DESTINATION" | cut -d' ' -f1) + [[ "$EXISTING_CHECKSUM" == "$CHECKSUM" ]] && return fi local TMPFILE - TMPFILE=$(mktemp "$TERMUX_PKG_TMPDIR/download.$TERMUX_PKG_NAME.XXXXXXXXX") + local -a CURL_OPTIONS=( + --fail + --retry 5 + --retry-connrefused + --retry-delay 5 + --speed-limit 1000 + --speed-time 60 + --location + ) + TMPFILE=$(mktemp "$TERMUX_PKG_TMPDIR/download.${TERMUX_PKG_NAME-unnamed}.XXXXXXXXX") + if [[ "${TERMUX_QUIET_BUILD-}" == "true" ]]; then + CURL_OPTIONS+=( --no-progress-meter) + fi + echo "Downloading ${URL}" - local TRYMAX=6 - for try in $(seq 1 $TRYMAX); do - if curl -L --fail --retry 2 -o "$TMPFILE" "$URL"; then - local ACTUAL_CHECKSUM - ACTUAL_CHECKSUM=$(sha256sum "$TMPFILE" | cut -f 1 -d ' ') - if [ "$CHECKSUM" != "SKIP_CHECKSUM" ]; then - if [ "$CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then - >&2 printf "Wrong checksum for %s:\nExpected: %s\nActual: %s\n" \ - "$URL" "$CHECKSUM" "$ACTUAL_CHECKSUM" - exit 1 - fi - else - printf "WARNING: No checksum check for %s:\nActual: %s\n" \ - "$URL" "$ACTUAL_CHECKSUM" + if ! curl "${CURL_OPTIONS[@]}" --output "$TMPFILE" "$URL"; then + local error=1 + local retry=2 + local delay=60 + local try + for (( try=1; try <= retry; try++ )); do + echo "Retrying #${try} download ${URL} in ${delay}" + sleep "${delay}" + if curl "${CURL_OPTIONS[@]}" --output "$TMPFILE" "$URL"; then + error=0 + break fi - mv "$TMPFILE" "$DESTINATION" - return - else - echo "Download of $URL failed (attempt $try/$TRYMAX)" 1>&2 - sleep 45 + done + if [[ "${error}" != 0 ]]; then + echo "Failed to download $URL" 1>&2 + return 1 fi - done + fi - termux_error_exit "Failed to download $URL" + local ACTUAL_CHECKSUM + ACTUAL_CHECKSUM=$(sha256sum "$TMPFILE" | cut -d' ' -f1) + if [[ -z "$CHECKSUM" ]]; then + printf "WARNING: No checksum check for %s:\nActual: %s\n" \ + "$URL" "$ACTUAL_CHECKSUM" + elif [[ "$CHECKSUM" == "SKIP_CHECKSUM" ]]; then + : + elif [[ "$CHECKSUM" != "$ACTUAL_CHECKSUM" ]]; then + printf "Wrong checksum for %s\nExpected: %s\nActual: %s\n" \ + "$URL" "$CHECKSUM" "$ACTUAL_CHECKSUM" 1>&2 + return 1 + fi + mv "$TMPFILE" "$DESTINATION" + return 0 } # Make script standalone executable as well as sourceable diff --git a/scripts/build/termux_step_create_debscripts.sh b/scripts/build/termux_step_create_debscripts.sh new file mode 100644 index 00000000000000..c9e6080dbb34b7 --- /dev/null +++ b/scripts/build/termux_step_create_debscripts.sh @@ -0,0 +1,32 @@ +termux_step_create_debscripts() { + return 0 +} + +termux_step_create_debscripts__copy_from_dir() { + + local return_value + + local src_dir="${1:-}" + local dest_dir="${2:-}" + + if [[ ! -d "$src_dir" ]]; then + echo "Failed to find source directory '$src_dir' to copy debscripts from" 1>&2 + return 1 + fi + + return_value=0 + mkdir -p "$dest_dir" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to create destination directory '$dest_dir' to copy debscripts to" 1>&2 + return 1 + fi + + ( + find "$src_dir" -mindepth 1 -maxdepth 1 -type f \ + -regextype posix-extended -regex "^.*/(postinst|postrm|preinst|prerm|config|conffiles|templates|triggers|clilibs|fortran_mod|runit|shlibs|starlibs|symbols)$" \ + -print0 | xargs -0 -n1 sh -c \ + 'cp -a "$0" '"'${dest_dir//\'/\'\\\'\'}/'" + + ) + +} diff --git a/scripts/build/termux_step_elf_cleaner.sh b/scripts/build/termux_step_elf_cleaner.sh new file mode 100644 index 00000000000000..d220201dd60756 --- /dev/null +++ b/scripts/build/termux_step_elf_cleaner.sh @@ -0,0 +1,11 @@ +termux_step_elf_cleaner() { + termux_step_elf_cleaner__from_paths . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" -o -path "./opt/*" \) +} + +termux_step_elf_cleaner__from_paths() { + # Remove entries unsupported by Android's linker: + find "$@" -type f -print0 | xargs -r -0 \ + "$TERMUX_ELF_CLEANER" + # TODO: Uncomment when termux-elf-cleaner is updated. + #"$TERMUX_ELF_CLEANER" --api-level "$TERMUX_PKG_API_LEVEL" +} diff --git a/scripts/build/termux_step_extract_package.sh b/scripts/build/termux_step_extract_package.sh index a8d3a4c613197a..aea0340b05eb2f 100644 --- a/scripts/build/termux_step_extract_package.sh +++ b/scripts/build/termux_step_extract_package.sh @@ -19,7 +19,7 @@ termux_step_extract_package() { local file="$TERMUX_PKG_CACHEDIR/$filename" # Allow TERMUX_PKG_SHA256 to be empty: set +u - termux_download "${PKG_SRCURL[$i]}" "$file" "${PKG_SHA256[$i]}" + termux_download "${PKG_SRCURL[$i]}" "$file" "${PKG_SHA256[$i]:-}" set -u local folder diff --git a/scripts/build/termux_step_massage.sh b/scripts/build/termux_step_massage.sh index 5036ad2cef6c97..fec6875edc33fd 100644 --- a/scripts/build/termux_step_massage.sh +++ b/scripts/build/termux_step_massage.sh @@ -20,15 +20,21 @@ termux_step_massage() { if [ "$TERMUX_DEBUG" = "false" ]; then # Strip binaries. file(1) may fail for certain unusual files, so disable pipefail. - set +e +o pipefail - find . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -type f | \ - xargs -r file | grep -E "ELF .+ (executable|shared object)" | cut -f 1 -d : | \ - xargs -r "$STRIP" --strip-unneeded --preserve-dates - set -e -o pipefail + #set +e +o pipefail + #find . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -type f | \ + # xargs -r file | grep -E "ELF .+ (executable|shared object)" | cut -f 1 -d : | \ + # xargs -r "$STRIP" --strip-unneeded --preserve-dates + #set -e -o pipefail + : fi - # Remove entries unsupported by Android's linker: - find . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -type f -print0 | xargs -r -0 "$TERMUX_ELF_CLEANER" + if [ "$TERMUX_PKG_NO_STRIP" != "true" ] && [ "$TERMUX_DEBUG" = "false" ]; then + termux_step_strip_elf_symbols + fi + + if [ "$TERMUX_PKG_NO_ELF_CLEANER" != "true" ]; then + termux_step_elf_cleaner + fi # Fix shebang paths: while IFS= read -r -d '' file diff --git a/scripts/build/termux_step_patch_package.sh b/scripts/build/termux_step_patch_package.sh index ddba5a53b786c4..809b98edd5e853 100644 --- a/scripts/build/termux_step_patch_package.sh +++ b/scripts/build/termux_step_patch_package.sh @@ -9,9 +9,18 @@ termux_step_patch_package() { # Suffix patch with ".patch32" or ".patch64" to only apply for these bitnesses: shopt -s nullglob for patch in $TERMUX_PKG_BUILDER_DIR/*.patch{$TERMUX_ARCH_BITS,} $DEBUG_PATCHES; do - test -f "$patch" && sed "s%\@TERMUX_PREFIX\@%${TERMUX_PREFIX}%g" "$patch" | \ - sed "s%\@TERMUX_HOME\@%${TERMUX_ANDROID_HOME}%g" | \ - patch --silent -p1 + test -f "$patch" && sed \ + -e "s%\@TERMUX_APP_PACKAGE\@%${TERMUX_APP_PACKAGE}%g" \ + -e "s%\@TERMUX_BASE_DIR\@%${TERMUX_BASE_DIR}%g" \ + -e "s%\@TERMUX_CACHE_DIR\@%${TERMUX_CACHE_DIR}%g" \ + -e "s%\@TERMUX_HOME\@%${TERMUX_ANDROID_HOME}%g" \ + -e "s%\@TERMUX_PREFIX\@%${TERMUX_PREFIX}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX\@%${TERMUX_ENV__S_TERMUX}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_APP\@%${TERMUX_ENV__S_TERMUX_APP}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_API_APP\@%${TERMUX_ENV__S_TERMUX_API_APP}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_ROOTFS\@%${TERMUX_ENV__S_TERMUX_ROOTFS}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_EXEC\@%${TERMUX_ENV__S_TERMUX_EXEC}%g" \ + "$patch" | patch --silent -p1 done shopt -u nullglob } diff --git a/scripts/build/termux_step_setup_variables.sh b/scripts/build/termux_step_setup_variables.sh index 1df41b5b2de0d8..b1e136d06cf9ea 100644 --- a/scripts/build/termux_step_setup_variables.sh +++ b/scripts/build/termux_step_setup_variables.sh @@ -2,8 +2,6 @@ termux_step_setup_variables() { : "${TERMUX_MAKE_PROCESSES:="$(nproc)"}" : "${TERMUX_TOPDIR:="$HOME/.termux-build"}" : "${TERMUX_ARCH:="aarch64"}" # arm, aarch64, i686 or x86_64. - : "${TERMUX_PREFIX:="/data/data/com.termux/files/usr"}" - : "${TERMUX_ANDROID_HOME:="/data/data/com.termux/files/home"}" : "${TERMUX_DEBUG:="false"}" : "${TERMUX_PKG_API_LEVEL:="21"}" : "${TERMUX_NO_CLEAN:="false"}" @@ -21,8 +19,6 @@ termux_step_setup_variables() { # These variables should not be configurable for on-device builds. # TERMUX_ARCH already set in build-package.sh - TERMUX_PREFIX="/data/data/com.termux/files/usr" - TERMUX_ANDROID_HOME="/data/data/com.termux/files/home" TERMUX_NO_CLEAN="true" # On device builds are considered as unofficial. @@ -131,6 +127,8 @@ termux_step_setup_variables() { TERMUX_CMAKE_BUILD=Ninja # Which cmake generator to use TERMUX_PKG_HAS_DEBUG=true # set to false if debug build doesn't exist or doesn't work, for example for python based packages TERMUX_PKG_METAPACKAGE=false + TERMUX_PKG_NO_ELF_CLEANER=false # set this to true to disable running of termux-elf-cleaner on built binaries + TERMUX_PKG_NO_STRIP=false # set this to true to disable stripping binaries unset CFLAGS CPPFLAGS LDFLAGS CXXFLAGS } diff --git a/scripts/build/termux_step_strip_elf_symbols.sh b/scripts/build/termux_step_strip_elf_symbols.sh new file mode 100644 index 00000000000000..2ffe7e0f71be82 --- /dev/null +++ b/scripts/build/termux_step_strip_elf_symbols.sh @@ -0,0 +1,13 @@ +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) +} + +termux_step_strip_elf_symbols__from_paths() { + # Strip binaries. file(1) may fail for certain unusual files, so disable pipefail. + ( + set +e +o pipefail && \ + find "$@" -type f -print0 | xargs -r -0 \ + file | grep -E "ELF .+ (executable|shared object)" | cut -f 1 -d : | + xargs -r "$STRIP" --strip-unneeded --preserve-dates + ) +} diff --git a/scripts/generate-bootstraps.sh b/scripts/generate-bootstraps.sh index da601a6350239a..1d93be1d9cb318 100755 --- a/scripts/generate-bootstraps.sh +++ b/scripts/generate-bootstraps.sh @@ -315,6 +315,7 @@ for package_arch in "${TERMUX_ARCHITECTURES[@]}"; do pull_package sed pull_package tar pull_package termux-am-socket + pull_package termux-core pull_package termux-exec pull_package termux-keyring pull_package termux-licenses diff --git a/scripts/properties.sh b/scripts/properties.sh index a37617b014b59b..2f485715ea360a 100644 --- a/scripts/properties.sh +++ b/scripts/properties.sh @@ -1,9 +1,2265 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 + +# XXX: This file is sourced by repology-updater script +# So avoid doing things like executing commands except of those available in +# coreutils and are clearly not a default part of most Linux installations, +# or sourcing any other script in our build directories. + +if [ -z "${BASH_VERSION:-}" ]; then + echo "The 'properties.sh' script must be run from a 'bash' shell."; return 64 2>/dev/null|| exit 64 # EX__USAGE +fi + + + +### +# Variables for validation of Termux properties variables. +# Validation is done to ensure packages are not compiled for invalid +# values that are not supported, and values are as per Termux file +# path limits. +# +# Additionally, the Termux packages build system is an unsafe mess of +# unquoted variables in shell scripts, and so validation is necessary +# for important variables, especially specific path variables against +# `TERMUX_REGEX__SAFE_*_PATH` regexes to reduce any potential damage. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +### + +## +# The map of variable names to their space separated list of validator +# actions to perform. +# +# Following are the supported validator actions. +# - `allow_unset_value`: Allow variable to be defined but unset, and +# skip other validations. +# - `app_package_name`: Variable must match `TERMUX_REGEX__APP_PACKAGE_NAME`. +# - `invalid_termux_rootfs_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`. +# - `invalid_termux_home_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS`. +# - `invalid_termux_prefix_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. +# - `path_equal_to_or_under_termux_rootfs`: Path variable must be equal +# to or be under `TERMUX__ROOTFS`. +# - `path_under_termux_rootfs`:Path variable must be under `TERMUX__ROOTFS`. +# - `safe_absolute_path`: Path variable must match +# `TERMUX_REGEX__SAFE_ABSOLUTE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `safe_relative_path`: Path variable must match +# `TERMUX_REGEX__SAFE_RELATIVE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `safe_rootfs_or_absolute_path`: Path variable must match +# `TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `apps_api_socket__server_parent_dir`: Path variable must have max +# length `<= TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN` +# including the null `\0` terminator. +# - `unix_path_max`: Path variable must have max length `<= TERMUX__UNIX_PATH_MAX` +# including the null `\0` terminator. +# - `unsigned_int`: Variable must match `TERMUX_REGEX__UNSIGNED_INT`. +## +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP; declare -A __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP=() + +## +# The list of variable names added to `__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP` +# that maintains insertion order. +## +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES; declare -a __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES=() + +## +# Whether to validate max lengths of Termux paths. Set to `false` to skip validation. +## +__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN="true" + +## +# Whether to validate `usr` merge format for `TERMUX__PREFIX`. Set to `false` to skip validation. +# Check `TERMUX__PREFIX` variable docs for more info. +## +__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT="true" + + + +## +# `__termux_build_props__add_variables_validator_actions` `` `` +## +__termux_build_props__add_variables_validator_actions() { + + if [ $# -ne 2 ]; then + echo "Invalid argument count '$#' to '__termux_build_props__add_variables_validator_actions'." 1>&2 + return 1 + fi + + local variable_name="$1" + local validator_actions="$2" + + if [[ ! "$variable_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + echo "The variable_name '$variable_name' passed to '__termux_build_props__add_variables_validator_actions' is not a valid shell variable name." 1>&2 + return 1 + fi + + if [[ " ${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES[*]} " != *" $variable_name "* ]]; then + __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES+=("$variable_name") + fi + + __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP["$variable_name"]+="$validator_actions" + +} + + + + + +### +# Variables for the Termux build tools. +### + +## +# The path to the `termux-packages` repo root directory. +## +TERMUX_PKGS__BUILD__REPO_ROOT_DIR="${TERMUX_PKGS__BUILD__REPO_ROOT_DIR:-}" + +__termux_build_props__set_termux_builder__repo_root_dir() { + + local relative_path="${1:-}" + local return_value=0 + if [[ -z "${TERMUX_PKGS__BUILD__REPO_ROOT_DIR:-}" ]]; then + if [[ "$(readlink --help 2>&1 || true)" =~ [\ ]-f[,\ ] ]]; then + TERMUX_PKGS__BUILD__REPO_ROOT_DIR="$(file="$(readlink -f -- "${BASH_SOURCE[0]}")" && \ + parent="$(dirname -- "$file")" && \ + readlink -f -- "${parent}${relative_path}")" || return_value=$? + else + TERMUX_PKGS__BUILD__REPO_ROOT_DIR="$(pwd)" || return_value=$? # macOS `< 12.3` compatibility. + fi + fi + if [ $return_value -ne 0 ] || [[ ! "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR" =~ ^(/[a-zA-Z0-9+,.=_-]+)+$ ]] || \ + [[ ! -f "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/scripts/properties.sh" ]]; then + echo "The TERMUX_PKGS__BUILD__REPO_ROOT_DIR '$TERMUX_PKGS__BUILD__REPO_ROOT_DIR' not found or is not valid." 1>&2 + return 1; + fi + +} +__termux_build_props__set_termux_builder__repo_root_dir "/.." || exit $? +unset __termux_build_props__set_termux_builder__repo_root_dir +TERMUX_SCRIPTDIR="${TERMUX_SCRIPTDIR:-TERMUX_PKGS__BUILD__REPO_ROOT_DIR}" # Deprecated alternative variable for `TERMUX_PKGS__BUILD__REPO_ROOT_DIR` + + + TERMUX_ANDROID_BUILD_TOOLS_VERSION=29.0.2 TERMUX_NDK_VERSION_NUM=20 TERMUX_NDK_REVISION="" -TERMUX_NDK_VERSION=$TERMUX_NDK_VERSION_NUM$TERMUX_NDK_REVISION - -test -f "$HOME/.termuxrc" && . "$HOME/.termuxrc" +TERMUX_NDK_VERSION="${TERMUX_NDK_VERSION_NUM}${TERMUX_NDK_REVISION}" : "${ANDROID_HOME:="${HOME}/lib/android-sdk"}" : "${NDK:="${HOME}/lib/android-ndk"}" + + + +### +# Variables for the Termux apps and packages for which to compile packages. +# +# Variables defined in this file need to be in sync with `termux-app` +# (`TermuxConstants` and `TermuxCoreConstants`), termux site and `termux-exec`. +# - https://github.com/termux/termux-app/blob/master/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java +# - https://github.com/termux/termux-app/blob/master/termux-shared/src/main/java/com/termux/shared/termux/core/TermuxCoreConstants.java +# +# Following is a list of `TERMUX_` variables that are safe to modify when forking. +# **DO NOT MODIFY ANY OTHER VARIABLE UNLESS YOU KNOW WHAT YOU ARE DOING.** +# +# - `TERMUX__NAME`, `TERMUX__LNAME` and `TERMUX__UNAME`. +# - `TERMUX__REPOS_HOST_ORG_NAME` and `TERMUX__REPOS_HOST_ORG_URL`. +# - `TERMUX_*__REPO_NAME` and `TERMUX_*__REPO_URL`. +# - `TERMUX_APP__PACKAGE_NAME`. +# - `TERMUX_APP__DATA_DIR`. +# - `TERMUX__PROJECT_SUBDIR`. +# - `TERMUX__ROOTFS_SUBDIR`. +# - `TERMUX__ROOTFS` and alternates. +# - `TERMUX__PREFIX` and alternates. +# - `TERMUX_ANDROID_HOME` and alternates. +# - `TERMUX_APP__NAME` and `TERMUX_APP__LNAME`. +# - `TERMUX_APP__IDENTIFIER`. +# - `TERMUX_APP__NAMESPACE`. +# - `TERMUX_APP__SHELL_ACTIVITY__*`. +# - `TERMUX_APP__SHELL_SERVICE__*`. +# - `TERMUX_APP__RUN_COMMAND_SERVICE__*`. +# - `TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__*`. +# - `TERMUX_API_APP__PACKAGE_NAME`. +# - `TERMUX_API_APP__NAME`. +# - `TERMUX_API_APP__IDENTIFIER`. +# - `TERMUX_API_APP__NAMESPACE`. +# - `TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__*`. +# - `TERMUX_AM_APP__NAMESPACE`. +### + +## +# Termux project name. +# +# Default value: `Termux` +## +TERMUX__NAME="Termux" + +## +# The lower case value for `TERMUX__NAME`. +# +# Default value: `termux` +## +TERMUX__LNAME="${TERMUX__NAME,,}" + +## +# The upper case value for `TERMUX__NAME`. +# +# Default value: `TERMUX` +## +TERMUX__UNAME="${TERMUX__NAME^^}" + + + +## +# Termux internal project name. +# +# This is used internally for paths, filenames, and other internal use +# cases and must match the `TERMUX__INTERNAL_NAME_REGEX` regex and +# have max length `TERMUX__INTERNAL_NAME___MAX_LEN`. +# +# **This must not be changed unless doing a full fork of Termux where +# all Termux references are changed instead of just changing the +# `TERMUX__NAME`, `TERMUX_APP__PACKAGE_NAME` and urls.** +# +# Default value: `termux` +## +TERMUX__INTERNAL_NAME="termux" + +## +# The regex to validate `TERMUX__INTERNAL_NAME`. +# +# The internal name must start with characters in the range +# `[a-z0-9]`, followed by at least one character in the range +# `[a-z0-9_-]`, and end with characters in the range `[a-z0-9]`. The +# min length is `3`. The max length `7` as per +# `TERMUX__INTERNAL_NAME___MAX_LEN` is not checked by this regex and +# must be checked separately. +# +# Constant value: `^[a-z0-9][a-z0-9_-]+[a-z0-9]$` +## +TERMUX__INTERNAL_NAME_REGEX="^[a-z0-9][a-z0-9_-]+[a-z0-9]$" + +## +# The max length for the `TERMUX__INTERNAL_NAME`. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `7` is chosen. +# +# Constant value: `7` +## +TERMUX__INTERNAL_NAME___MAX_LEN=7 + + + +## +# Termux repositories host organization name. +# +# Default value: `termux` +## +TERMUX__REPOS_HOST_ORG_NAME="termux" + +## +# Termux repositories host organization url. +# +# Default value: `https://github.com/termux` +## +TERMUX__REPOS_HOST_ORG_URL="https://github.com/$TERMUX__REPOS_HOST_ORG_NAME" + + + +## +# Termux app package name used for `TERMUX_APP__DATA_DIR` and +# `TERMUX_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*` variables. +# +# Ideally package name should be `<= 21` characters and max `33` +# characters. If package name has not yet been chosen, then it would +# be best to keep it to `<= 10` characters. Check +# https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why. +# +# See also `TERMUX_APP__NAMESPACE`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +# +# Default value: `com.termux` +## +TERMUX_APP__PACKAGE_NAME="com.termux" +TERMUX_APP_PACKAGE="$TERMUX_APP__PACKAGE_NAME" # Deprecated alternative variable for `TERMUX_APP__PACKAGE_NAME` + +__termux_build_props__add_variables_validator_actions "TERMUX_APP__PACKAGE_NAME" "app_package_name" + +## +# Termux app data directory path that is expected to be assigned by +# Android to the Termux app with `TERMUX_APP__PACKAGE_NAME` for all +# its app data, that will contain the Termux project directory +# (`TERMUX__PROJECT_DIR`), and optionally the Termux rootfs directory +# (`TERMUX__ROOTFS`). +# +# The path must match `TERMUX_REGEX__APP_DATA_DIR_PATH`. +# +# The directory set will be deleted by `clean.sh` if `TERMUX__PREFIX` +# is under `TERMUX_APP__DATA_DIR` and not running on-device, so make +# sure a safe path is set if running `clean.sh` in Termux docker or +# host OS build environment. +# +# Default value: `/data/data/com.termux` +## +TERMUX_APP__DATA_DIR="/data/data/$TERMUX_APP__PACKAGE_NAME" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__DATA_DIR" "safe_absolute_path" + +## +# The max length for the `TERMUX_APP__DATA_DIR` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `69` is chosen. +# +# Constant value: `69` +## +TERMUX_APP__DATA_DIR___MAX_LEN=69 + + + + + +## +# Termux subdirectory path for `TERMUX__PROJECT_DIR`. +# +# Default value: `termux` +## +TERMUX__PROJECT_SUBDIR="$TERMUX__INTERNAL_NAME" +__termux_build_props__add_variables_validator_actions "TERMUX__PROJECT_SUBDIR" "safe_relative_path" + +## +# Termux project directory path under `TERMUX_APP__DATA_DIR`. +# +# This is an exclusive directory for all Termux files that includes +# Termux core directory (`TERMUX__CORE_DIR`), Termux apps directory +# (`TERMUX__APPS_DIR`), and optionally the Termux rootfs directory +# (`TERMUX__ROOTFS`). +# +# Currently, the default Termux rootfs directory is not under it and +# is at the `/files` subdirectory but there are plans to move it to +# `termux/rootfs/II` in future where `II` refers to rootfs id starting +# at `0` for multi-rootfs support. +# +# An exclusive directory is required so that all Termux files exist +# under a single directory, especially for when Termux is provided as +# a library, so that Termux files do not interfere with other files +# of Termux app forks or apps that may use the Termux library. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-project-directory +# +# Default value: `/data/data/com.termux/termux` +## +TERMUX__PROJECT_DIR="$TERMUX_APP__DATA_DIR/$TERMUX__PROJECT_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__PROJECT_DIR" "safe_absolute_path" + + + + + +## +# Termux subdirectory path for `TERMUX__CORE_DIR`. +# +# Constant value: `core` +## +TERMUX__CORE_SUBDIR="core" + +## +# Termux core directory path under `TERMUX__PROJECT_DIR`. +# +# This contains Termux core files for the Termux app, like user settings and configs for the app, +# which and are independent of any specific rootfs. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-core-directory +# +# Default value: `/data/data/com.termux/termux/core` +## +TERMUX__CORE_DIR="$TERMUX__PROJECT_DIR/$TERMUX__CORE_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__CORE_DIR" "safe_absolute_path" + + + + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR`. +# +# Constant value: `apps` +## +TERMUX__APPS_SUBDIR="apps" + +## +# Termux apps directory path under `TERMUX__PROJECT_DIR`. +# +# This contains app specific files for the Termux app, its plugin +# apps, and third party apps, like used for app APIs and +# filesystem/pathname socket files of servers created by the apps. +# - https://man7.org/linux/man-pages/man7/unix.7.html +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-apps-directory +# +# Default value: `/data/data/com.termux/termux/apps` +## +TERMUX__APPS_DIR="$TERMUX__PROJECT_DIR/$TERMUX__APPS_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__APPS_DIR" "safe_absolute_path" + +## +# The max length for the `TERMUX__APPS_DIR` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `84` is chosen. +# +# Constant value: `84` +## +TERMUX__APPS_DIR___MAX_LEN=84 + +## +# The max length for the Termux apps api socket server parent directory +# including the null '\0' terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `98` is chosen. +# +# Constant value: `98` +## +TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN=98 + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Constant value: `i` +## +TERMUX__APPS_DIR_BY_IDENTIFIER_SUBDIR="i" + +## +# Termux apps directory path by app identifier under `TERMUX__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i` +## +TERMUX__APPS_DIR_BY_IDENTIFIER="$TERMUX__APPS_DIR/$TERMUX__APPS_DIR_BY_IDENTIFIER_SUBDIR" + +## +# The regex to validate a subdirectory name under the +# `TERMUX__APPS_DIR_BY_IDENTIFIER` excluding the null '\0' terminator +# that represents an app identifier. +# +# The app identifier must only contain characters in the range +# `[a-zA-Z0-9]` as segments, with `[._-]` as separators between +# segments, and with the first segment containing at least 3 +# characters. The max length `10` as per +# `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` is not checked by this regex +# and must be checked separately. +# +# Constant value: `^[a-zA-Z0-9]{3,}([._-][a-zA-Z0-9]+)*$` +## +TERMUX__APPS_APP_IDENTIFIER_REGEX="^[a-zA-Z0-9]{3,}([._-][a-zA-Z0-9]+)*$" + +## +# The max length for a subdirectory name under the +# `TERMUX__APPS_DIR_BY_IDENTIFIER` excluding the null '\0' terminator +# that represents an app identifier. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `10` is chosen. +# +# Constant value: `10` +## +TERMUX__APPS_APP_IDENTIFIER___MAX_LEN=10 + + + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR_BY_UID`. +# +# Constant value: `u` +## +TERMUX__APPS_DIR_BY_UID_SUBDIR="u" + +## +# Termux apps directory path by app uid (user_id + app_id) under +# `TERMUX__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/u` +## +TERMUX__APPS_DIR_BY_UID="$TERMUX__APPS_DIR/$TERMUX__APPS_DIR_BY_UID_SUBDIR" + +## +# The regex to validate a subdirectory name under the +# `TERMUX__APPS_DIR_BY_UID` excluding the null '\0' terminator that +# represents an app uid. +# +# The app uid must only contains `5` to `9` characters that are +# numbers and must not start with a `0`. +# +# Constant value: `^[1-9][0-9]{4,8}$` +## +TERMUX__APPS_APP_UID_REGEX="^[1-9][0-9]{4,8}$" + +## +# The max length for a subdirectory name under the +# `TERMUX__APPS_DIR_BY_UID` excluding the null '\0' terminator that +# represents an app uid. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `9` is chosen. +# +# Constant value: `9` +## +TERMUX__APPS_APP_UID___MAX_LEN=9 + + + + + +## +# Termux apps info environment subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-apps-info.env` +## +TERMUX_CORE__APPS_INFO_ENV_SUBFILE="$TERMUX__INTERNAL_NAME-apps-info.env" + +## +# Termux apps info json subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-apps-info.json` +## +TERMUX_CORE__APPS_INFO_JSON_SUBFILE="$TERMUX__INTERNAL_NAME-apps-info.json" + + + +## +# `termux-am-socket` server subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-am` +## +TERMUX_AM_SOCKET__SERVER_SOCKET_SUBFILE="$TERMUX__INTERNAL_NAME-am" + + + + + +## +# Termux `TERMUX__ROOTFS` id. +# +# Default value: `0` +## +TERMUX__ROOTFS_ID="0" +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS_ID" "unsigned_int" + +## +# Termux subdirectory path for `TERMUX__ROOTFS`. +# +# Default value: `files` +## +TERMUX__ROOTFS_SUBDIR="files" +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS_SUBDIR" "allow_unset_value safe_relative_path" + +########### +# Uncomment if to place `TERMUX__ROOTFS` under `TERMUX__PROJECT_DIR` +# instead of at `files`. This may be used for future multi-rootfs +# design. Make sure to update `TERMUX__CACHE_SUBDIR` above as well. + +## +# Termux subdirectory path for `TERMUX__ROOTFS`. +# +# Default value: `termux/rootfs/0` +## +#TERMUX__ROOTFS_SUBDIR="$TERMUX__PROJECT_SUBDIR/rootfs/$TERMUX__ROOTFS_ID" +########### + + +## +# Termux rootfs directory path under `TERMUX_APP__DATA_DIR` that +# contains the Linux environment rootfs provided by Termux. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-rootfs-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03.html +# +# The Termux rootfs must not be set to path in +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`. It can exist outside +# the `TERMUX_APP__DATA_DIR` if compiling packages for the Android +# system or `adb` `shell` user. +# +# Default value: `/data/data/com.termux/files` +## +TERMUX__ROOTFS="$TERMUX_APP__DATA_DIR/$TERMUX__ROOTFS_SUBDIR" +TERMUX_BASE_DIR="$TERMUX__ROOTFS" # Deprecated alternative variable for `TERMUX__ROOTFS` + +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS" "safe_rootfs_or_absolute_path invalid_termux_rootfs_paths" + +# FIXME: Remove after updating Termux app and `termux-am-socket` +# package sources and use `TERMUX__APPS_DIR`. +TERMUX_APPS_DIR="$TERMUX__ROOTFS/apps" + +## +# The max length for the `TERMUX__ROOTFS` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `86` is chosen. +# +# Constant value: `86` +## +TERMUX__ROOTFS_DIR___MAX_LEN=86 + + + + + +#### +# Variables for the Termux home. +#### + +## +# Termux subdirectory path for `TERMUX__HOME`. +# +# Default value: `home` +## +TERMUX__HOME_SUBDIR="home" +__termux_build_props__add_variables_validator_actions "TERMUX__HOME_SUBDIR" "safe_relative_path" + +## +# Termux home directory path under `TERMUX__ROOTFS` used for `$HOME`. +# +# It serves the same purpose as the `/home` directory on Linux distros. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-home-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s08.html +# +# Check `TERMUX__PREFIX` variable docs for rules that apply depending +# on if `TERMUX__ROOTFS` is equal to Android/Linux rootfs `/` or not. +# The Termux home must not be set to Android/Linux rootfs `/` or any +# other path in `TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS`. +# +# Default value: `/data/data/com.termux/files/home` +## +[[ "$TERMUX__ROOTFS" != "/" ]] && TERMUX__HOME="$TERMUX__ROOTFS/$TERMUX__HOME_SUBDIR" || \ + TERMUX__HOME="/$TERMUX__HOME_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__HOME" "safe_absolute_path invalid_termux_home_paths path_under_termux_rootfs" + +TERMUX_ANDROID_HOME="$TERMUX__HOME" # Deprecated alternative variable for `TERMUX__HOME` + +## +# Termux data directory path under `TERMUX__HOME`. +# +# Default value: `/data/data/com.termux/files/home/.termux` +## +TERMUX__DATA_HOME="$TERMUX__HOME/.termux" + + + + + +#### +# Variables for the Termux prefix. +#### + +## +# Termux subdirectory path for `TERMUX__PREFIX`. +# +# Default value: `usr` +## +TERMUX__PREFIX_SUBDIR="usr" +__termux_build_props__add_variables_validator_actions "TERMUX__PREFIX_SUBDIR" "allow_unset_value safe_relative_path" + +## +# Termux prefix directory path under or equal to `TERMUX__ROOTFS` +# where all Termux packages data is installed. +# +# It serves the same purpose as the `/usr` directory on Linux distros +# and contains the `bin`, `etc`, `include`, `lib`, `libexec`, `opt`, +# `share`, `tmp` and `var` sub directories. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-prefix-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04.html +# +# If `TERMUX__ROOTFS` is not equal to `/`, then by default Termux +# uses `usr` merge format, like used by `debian`, as per +# `__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT` +# being enabled by default. In the `usr` merge format, all packages +# are installed under the `usr` subdirectory under rootfs, like under +# `$TERMUX__ROOTFS/usr/bin` and `$TERMUX__ROOTFS/usr/lib`, +# instead of under `$TERMUX__ROOTFS/bin` and `$TERMUX__ROOTFS/lib`. +# So if `usr` merge format is enabled, then DO NOT change the default +# value of `TERMUX__PREFIX_SUBDIR` from `usr`. +# The `$TERMUX__ROOTFS/usr-staging` directory is also used as a +# temporary directory for extracting bootstrap zip by the Termux app, +# before its renamed to `$TERMUX__ROOTFS/usr`. +# Additionally, `TERMUX__PREFIX` must not be equal to `TERMUX__HOME` +# and they must not be under each other, as Termux app requires that +# prefix and home are separate directories as prefix gets wiped during +# bootstrap installation or if `termux-reset` is run, and backup +# scripts require the same. Package data also needs to be kept +# separate from `home`, so it does not make sense for them to be +# equal to or be under each other. +# However, if a Termux app fork is using a modified bootstrap +# installation that does not use the `usr` merge format, then +# `__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT` can +# be set to `false` and `TERMUX__PREFIX_SUBDIR` could optionally be +# set to an empty string if `TERMUX__ROOTFS` should be equal to +# `TERMUX__PREFIX`, or a custom directory other than `usr`. In this +# case `TERMUX__HOME` can optionally be under `TERMUX__PREFIX`, but +# not be equal to it. +# +# - https://wiki.debian.org/UsrMerge +# - https://lists.debian.org/debian-devel-announce/2019/03/msg00001.html +# - https://dep-team.pages.debian.net/deps/dep17/ +# +# If `TERMUX__ROOTFS` is equal to Android/Linux rootfs `/`, then +# `TERMUX__PREFIX_SUBDIR` must not be set to an empty string as +# `TERMUX__PREFIX` must be a subdirectory under rootfs `/`, and must +# not be set to `usr` either or or any other path in +# `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. Check the +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS` variable docs for why +# some paths like `/usr`, etc are now allowed. +# +# Basically, the following rules apply for `TERMUX__PREFIX`. +# - If `TERMUX__ROOTFS` is not equal to `/`: +# - If `usr` merge format is enabled: +# - `TERMUX__PREFIX` must be equal to `$TERMUX__ROOTFS/usr`. +# - `TERMUX__PREFIX` must not be equal to `TERMUX__HOME` and +# they must not be under each other. +# - If `usr` merge format is disabled: +# - `TERMUX__PREFIX` must be equal to or be under `$TERMUX__ROOTFS`. +# - `TERMUX__PREFIX` must not be equal to or be under `TERMUX__HOME`. +# - If `TERMUX__ROOTFS` is equal to `/`: +# - If `usr` merge format is enabled or disabled: +# - `TERMUX__PREFIX` must be under `$TERMUX__ROOTFS` and not +# equal to `/usr` or other paths in `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. +# - `TERMUX__PREFIX` must not be equal to or be under `TERMUX__HOME`. +# +# The directory set will be deleted by `clean.sh` if not running +# on-device, so make sure a safe path is set if running `clean.sh` in +# Termux docker or host OS build environment. +# +# Default value: `/data/data/com.termux/files/usr` +## +[[ "$TERMUX__ROOTFS" != "/" ]] && TERMUX__PREFIX="$TERMUX__ROOTFS${TERMUX__PREFIX_SUBDIR:+"/$TERMUX__PREFIX_SUBDIR"}" || \ + TERMUX__PREFIX="/$TERMUX__PREFIX_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" "safe_absolute_path invalid_termux_prefix_paths" + +if [[ "$TERMUX__ROOTFS" != "/" ]] && [[ "$__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT" != "true" ]]; then + __termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" " path_equal_to_or_under_termux_rootfs" +else + __termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" " path_under_termux_rootfs" +fi + +TERMUX_PREFIX="$TERMUX__PREFIX" # Deprecated alternative variable for `TERMUX__PREFIX` + +## +# The max length for the `TERMUX__PREFIX` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `90` is chosen. +# +# Constant value: `90` +## +TERMUX__PREFIX_DIR___MAX_LEN="$((TERMUX__ROOTFS_DIR___MAX_LEN + 1 + 3))" # "/usr" (90) + + + +## +# Termux subdirectory path for `TERMUX__PREFIX__BIN_DIR`. +# +# Constant value: `bin` +## +TERMUX__PREFIX__BIN_SUBDIR="bin" + +## +# Termux bin directory path under `TERMUX__PREFIX`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-bin-directory +# +# Default value: `/data/data/com.termux/files/usr/bin` +## +TERMUX__PREFIX__BIN_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__BIN_SUBDIR" + + +## +# The max length for the `TERMUX__BIN_DIR` including the null '\0' terminator. +# +# Constant value: `94` +## +TERMUX__PREFIX__BIN_DIR___MAX_LEN="$((TERMUX__PREFIX_DIR___MAX_LEN + 1 + 3))" # "/bin" (94) + +## +# The max safe length for a sub file path under the `TERMUX__BIN_DIR` +# including the null '\0' terminator. +# +# This allows for a filename with max length `33` so that the path +# length is under `128` (`BINPRM_BUF_SIZE`) for Linux kernel `< 5.1`, +# and ensures `argv[0]` length is `< 128` on Android `< 6`, otherwise +# commands will fail with exit code 1 without any error on `stderr`, +# but with the `library name "" too long` error in +# `logcat` if linker debugging is enabled. +# +# **See Also:** +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# - https://github.com/termux/termux-core-package/blob/master/lib/termux-core_nos_c_tre/include/termux/termux_core__nos__c/v1/termux/file/TermuxFile.h +# - https://github.com/termux/termux-exec-package/blob/master/lib/termux-exec_nos_c_tre/include/termux/termux_exec__nos__c/v1/termux/api/termux_exec/ld_preload/direct/exec/ExecIntercept.h +# +# Constant value: `127` +## +TERMUX__PREFIX__BIN_FILE___SAFE_MAX_LEN="$((TERMUX__PREFIX__BIN_DIR___MAX_LEN + 1 + 33))" # "/" (127) + +## +# The max length for entire shebang line for `termux-exec`. +# +# **See Also:** +# - https://github.com/termux/termux-exec-package/blob/master/lib/termux-exec_nos_c_tre/include/termux/termux_exec__nos__c/v1/termux/api/termux_exec/exec/ExecIntercept.h +# +# Default value: `340` +## +TERMUX__FILE_HEADER__BUFFER_SIZE="340" + + + +## +# Termux subdirectory path for `TERMUX__PREFIX__ETC_DIR`. +# +# Constant value: `etc` +## +TERMUX__PREFIX__ETC_SUBDIR="etc" + +## +# Termux etc directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/etc` +## +TERMUX__PREFIX__ETC_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__ETC_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__INCLUDE_DIR`. +# +# Constant value: `include` +## +TERMUX__PREFIX__INCLUDE_SUBDIR="include" + +## +# Termux include directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/include` +## +TERMUX__PREFIX__INCLUDE_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__INCLUDE_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__LIB_DIR`. +# +# Constant value: `lib` +## +TERMUX__PREFIX__LIB_SUBDIR="lib" + +## +# Termux lib directory path under `TERMUX__PREFIX`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-lib-directory +# +# Default value: `/data/data/com.termux/files/usr/lib` +## +TERMUX__PREFIX__LIB_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__LIB_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__LIBEXEC_DIR`. +# +# Constant value: `libexec` +## +TERMUX__PREFIX__LIBEXEC_SUBDIR="libexec" + +## +# Termux libexec directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/libexec` +## +TERMUX__PREFIX__LIBEXEC_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__LIBEXEC_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__OPT_DIR`. +# +# Constant value: `opt` +## +TERMUX__PREFIX__OPT_SUBDIR="opt" + +## +# Termux opt directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/opt` +## +TERMUX__PREFIX__OPT_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__OPT_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__SHARE_DIR`. +# +# Constant value: `share` +## +TERMUX__PREFIX__SHARE_SUBDIR="share" + +## +# Termux share directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/share` +## +TERMUX__PREFIX__SHARE_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__SHARE_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__TMP_DIR`. +# +# Constant value: `tmp` +## +TERMUX__PREFIX__TMP_SUBDIR="tmp" + +## +# Termux tmp directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/tmp` +## +TERMUX__PREFIX__TMP_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__TMP_SUBDIR" + +## +# The max length for the `TERMUX__PREFIX__TMP_DIR` including the null +# '\0' terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `94` is chosen. +# +# Constant value: `94` +## +TERMUX__PREFIX__TMP_DIR___MAX_LEN=94 + + +## +# Termux subdirectory path for `TERMUX__PREFIX__VAR_DIR`. +# +# Constant value: `var` +## +TERMUX__PREFIX__VAR_SUBDIR="var" + +## +# Termux var directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/var` +## +TERMUX__PREFIX__VAR_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__VAR_SUBDIR" + + + +## +# Termux `profile.d` directory path under `TERMUX__PREFIX__ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/profile.d` +## +TERMUX__PREFIX__PROFILE_D_DIR="$TERMUX__PREFIX__ETC_DIR/profile.d" + + +## +# Termux data directory path under `TERMUX__PREFIX__ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/termux` +## +TERMUX__PREFIX__TERMUX_DATA_ETC_DIR="$TERMUX__PREFIX__ETC_DIR/termux" + + + + + +#### +# Variables for the Termux cache. +#### + +## +# Termux subdirectory path for `TERMUX__CACHE_DIR`. +# +# Constant value: `cache` +## +TERMUX__CACHE_SUBDIR="cache" + +########### +# Uncomment if to place `TERMUX__ROOTFS` under `TERMUX__PROJECT_DIR` +# instead of at `files`. This may be used for future multi-rootfs +# design. This will also ensure `termux` files are not mixed with +# other cached files of an app, especially if Termux is forked or +# used as a library in other apps. Make sure to update +# `TERMUX__ROOTFS_SUBDIR` above as well. + +## +# Termux subdirectory path for `TERMUX__CACHE_DIR`. +# +# Default value: `cache/termux/rootfs/0` +## +#TERMUX__CACHE_SUBDIR="cache/termux/rootfs/$TERMUX__ROOTFS_ID" +########### + +## +# Termux app cache directory path under `TERMUX_APP__DATA_DIR` +# contains cache files that are safe to be deleted by Android or +# Termux if required. +# +# The `cache` subdirectory is hardcoded in Android and must not be +# changed. +# +# Currently this is primarily used for packages cache files of package +# managers (`apt`/`pacman`). +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-app-cache-directory +# +# Default value: `/data/data/com.termux/cache` +## +TERMUX__CACHE_DIR="$TERMUX_APP__DATA_DIR/$TERMUX__CACHE_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__CACHE_DIR" "safe_absolute_path" + +TERMUX_CACHE_DIR="$TERMUX__CACHE_DIR" # Deprecated alternative variable for `TERMUX__CACHE_DIR` + + + + + +#### +# Variables for the Termux bootstraps. +#### + +## +# Termux bootstrap config directory path under `TERMUX__PREFIX__TERMUX_DATA_ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/termux/bootstrap` +## +TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR="$TERMUX__PREFIX__TERMUX_DATA_ETC_DIR/bootstrap" + + + + + +## +# Max size in bytes for a path component or file name without the +# terminating `null` byte `\0`. +# +# On unix systems, any path component length of a path cannot be +# greater than what is supported by the filesystem under which the +# path is mounted. +# +# The common filesystems like `ext4`/`f2fs`/`btrfs`/`erofs`/`fat32`/`exfat`/`ntfs` +# all support max path component length of `255`. Check +# [filesystems limits wiki page] for more info on limits. +# +# The [POSIX standard requires `NAME_MAX` to be defined in `limits.h`] (not `c` standard). +# +# [`NAME_MAX`]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/limits.h;l=27 +# [`readdir`]: https://www.man7.org/linux/man-pages/man3/readdir.3.html +# [`readdir_r`]: https://www.man7.org/linux/man-pages/man3/readdir_r.3.html +# [filesystems limits wiki page]: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits +# [POSIX standard requires `NAME_MAX` to be defined in `limits.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html +# [path component length greater than `NAME_MAX` should be considered an error]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#4.13 +# [`NAME_MAX` value should normally be set to `255`]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/limits.h;l=27 +# [the `NAME_MAX` value may not always be enforced, like by the GNU C library]: https://www.gnu.org/software/libc/manual/html_node/Limits-for-Files.html +# [`_PC_NAME_MAX` defined by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html +# [`glibc` `_PC_NAME_MAX` source]: https://github.com/bminor/glibc/blob/569cfcc6/sysdeps/posix/fpathconf.c#L65 +# [`bionic` `_PC_NAME_MAX` source]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/bionic/pathconf.cpp;l=91 +# [`pathconf`]: https://man7.org/linux/man-pages/man3/pathconf.3.html +# [`statvfs.f_namemax`]: https://man7.org/linux/man-pages/man3/statvfs.3.html +# [`realpath(1)`]: https://man7.org/linux/man-pages/man1/realpath.1.html +# [`realpath(3)`]: https://man7.org/linux/man-pages/man3/realpath.3.html +# [To what extent does Linux support file names longer than 255 bytes?]: https://unix.stackexchange.com/questions/619625/to-what-extent-does-linux-support-file-names-longer-than-255-bytes +# [Extending ext4 File system's filename size limit to 1012 characters]: https://stackoverflow.com/questions/34980895/extending-ext4-file-systems-filename-size-limit-to-1012-characters +# [Limit on file name length in bash]: https://stackoverflow.com/questions/6571435/limit-on-file-name-length-in-bash +# +# Constant value: `255` +## +TERMUX__NAME_MAX=255 + +## +# The max length for a filesystem socket file path (pathanme UNIX domain socket) +# for the `sockaddr_un.sun_path` field including the null `\0` +# terminator as per `UNIX_PATH_MAX`. +# +# All filesystem socket path lengths created by Termux apps and packages must be `< 108`. +# +# - https://man7.org/linux/man-pages/man7/unix.7.html +# - https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/un.h;l=22 +# +# Constant value: `108` +## +TERMUX__UNIX_PATH_MAX=108 + + + + + +## +# Termux environment variables root scope. +# +# The name of this variable `TERMUX_ENV__S_ROOT` is considered a +# constant for Termux execution environment that's exported by Termux +# app containing the root scope and **must not be changed even for +# forks**. It can be used to check if running under Termux or any of +# its forks, and should be used to generate all Termux variable names +# that may need to be read, since Termux app forks may not export +# variables under the `TERMUX_` root scope and may do it under a +# different root scope like `FOO_`, so the `TERMUX__PREFIX` variable +# would be `FOO__PREFIX` instead. +# +# The `TERMUX_ENV__S_APP` environment variable will be exported at +# runtime for the scope of the current Termux app running the shell. +# +# Termux packages and external programs can use the +# `termux-scoped-env-variable` util from the `termux-core` +# package to get variable names and values for Termux. It uses the +# root scope from the `$TERMUX_ENV__S_ROOT` environment variable +# exported by the Termux app to dynamically generate the Termux +# variable names and/or get their values, with support for fallback +# to the build values defined here if `$TERMUX_ENV__S_ROOT` variable +# is not exported.** +# - https://github.com/termux/termux-core-package/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# +# The value of this variable `TERMUX_ENV__S_ROOT` may be modified, +# although not advisable since external programs would be using +# hardcoded `TERMUX_` value for reading Termux environment variables, +# and so changing variable names to say `FOO_*` would result in +# `TERMUX_*` ones being unset during execution, which would change +# external programs behaviour and may break them. +# **If the value is changed here, it must also be set to the same +# value in Termux app that is exported.** +# +# Moreover, currently, only `termux-exec` supports modifying this, all +# other termux (internal) packages, like `termux-tools`, etc do not. +# So forks should not modify it at least until all termux packages +# support modifying it. +# +# Default value: `TERMUX_` +## +TERMUX_ENV__S_ROOT="TERMUX_" + + + +## +# Termux environment variables Termux sub scope for primary variables +# or variables for currently running Termux config. +# +# **Do not modify this!** This is considered a constant Termux sub +# scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX` and variable names under it.** +# +# Default value: `_` +## +TERMUX_ENV__SS_TERMUX="_" + +## +# Termux environment variables Termux scope for primary variables or +# variables for currently running Termux config. +# +# **Do not modify this!** +# +# Default value: `TERMUX__` +## +TERMUX_ENV__S_TERMUX="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX}" + + + +## +# Termux environment variables Termux app sub scope. +# +# **Do not modify this!** This is considered a constant Termux app sub +# scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_APP` and variable names under it.** +# +# Default value: `APP__` +## +TERMUX_ENV__SS_TERMUX_APP="APP__" + +## +# Termux environment variables Termux app scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_APP__` +## +TERMUX_ENV__S_TERMUX_APP="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_APP}" + + + +## +# Termux environment variables Termux:API sub scope. +# +# **Do not modify this!** This is considered a constant Termux:API +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_API` and variable names under it.** +# +# Default value: `API__` +## +TERMUX_ENV__SS_TERMUX_API="API__" + +## +# Termux environment variables Termux:API scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_API__` +## +TERMUX_ENV__S_TERMUX_API="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_API}" + + + +## +# Termux environment variables Termux:API app sub scope. +# +# This may be allowed to be modified, in case APIs are provided under +# a different app name or under the main Termux app itself by a fork. +# Consequences for changing this haven't been fully looked at yet. +# +# Default value: `API_APP__` +## +TERMUX_ENV__SS_TERMUX_API_APP="API_APP__" + +## +# Termux environment variables Termux:API app scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_API_APP__` +## +TERMUX_ENV__S_TERMUX_API_APP="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_API_APP}" + + + +## +# Termux environment variables Termux rootfs sub scope. +# +# **Do not modify this!** This is considered a constant Termux rootfs +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_ROOTFS` and variable names under it.** +# +# Default value: `ROOTFS__` +## +TERMUX_ENV__SS_TERMUX_ROOTFS="ROOTFS__" + +## +# Termux environment variables Termux rootfs scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_ROOTFS__` +## +TERMUX_ENV__S_TERMUX_ROOTFS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_ROOTFS}" + + + +## +# Termux environment variables `termux-core` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-core` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_CORE` and variable names under it.** +# +# Default value: `CORE__` +## +TERMUX_ENV__SS_TERMUX_CORE="CORE__" + +## +# Termux environment variables `termux-core` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_CORE__` +## +TERMUX_ENV__S_TERMUX_CORE="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_CORE}" + + +## +# Termux environment variables `termux-core-tests` sub scope. +# +# **Do not modify this!** This is considered a constant +# `termux-core-tests` sub scope for Termux execution environment +# that's used by `termux-core` package to generate the value for +# `$TERMUX_ENV__S_TERMUX_CORE__TESTS` and variable names under it.** +# +# Default value: `TERMUX_CORE__TESTS__` +## +TERMUX_ENV__SS_TERMUX_CORE__TESTS="CORE__TESTS__" + +## +# Termux environment variables `termux-core-tests` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_CORE__TESTS__` +## +TERMUX_ENV__S_TERMUX_CORE__TESTS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_CORE__TESTS}" + + + +## +# Termux environment variables `termux-exec` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-exec` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_EXEC` and variable names under it.** +# +# Default value: `EXEC__` +## +TERMUX_ENV__SS_TERMUX_EXEC="EXEC__" + +## +# Termux environment variables `termux-exec` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_EXEC__` +## +TERMUX_ENV__S_TERMUX_EXEC="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_EXEC}" + + +## +# Termux environment variables `termux-exec-tests` sub scope. +# +# **Do not modify this!** This is considered a constant +# `termux-exec-tests` sub scope for Termux execution environment +# that's used by `termux-exec` package to generate the value for +# `$TERMUX_ENV__S_TERMUX_EXEC__TESTS` and variable names under it.** +# +# Default value: `TERMUX_EXEC__TESTS__` +## +TERMUX_ENV__SS_TERMUX_EXEC__TESTS="EXEC__TESTS__" + +## +# Termux environment variables `termux-exec-tests` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_EXEC__TESTS__` +## +TERMUX_ENV__S_TERMUX_EXEC__TESTS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_EXEC__TESTS}" + + + +## +# Termux environment variables `termux-am-socket` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-am-socket` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_AM_SOCKET` and variable names under it.** +# +# Default value: `AM_SOCKET__` +## +TERMUX_ENV__SS_TERMUX_AM_SOCKET="AM_SOCKET__" + +## +# Termux environment variables `termux-am-socket` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_AM_SOCKET__` +## +TERMUX_ENV__S_TERMUX_AM_SOCKET="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_AM_SOCKET}" + + + + + +#### +# Variables for the Termux packages. +# +# - https://github.com/termux/termux-packages +#### + +## +# Termux packages repo name. +# +# Default value: `termux-packages` +## +TERMUX_PKGS__REPO_NAME="termux-packages" + +## +# Termux packages repo url. +# +# Default value: `https://github.com/termux/termux-packages` +## +TERMUX_PKGS__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_PKGS__REPO_NAME" + + + + + +#### +# Variables for the Termux app that hosts the packages. +# +# - https://github.com/termux/termux-app +#### + +## +# Termux app name. +# +# Default value: `Termux` +## +TERMUX_APP__NAME="$TERMUX__NAME" + + +## +# The lower case value for `TERMUX_APP__NAME`. +# +# Default value: `termux` +## +TERMUX_APP__LNAME="${TERMUX_APP__NAME,,}" + +## +# Termux app identifier for `TERMUX__APPS_DIR_BY_IDENTIFIER` subdirectory. +# +# Default value: `termux` +# Validation regex: `TERMUX__APPS_APP_IDENTIFIER_REGEX` +# Max length: `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` +## +TERMUX_APP__IDENTIFIER="termux" + + + +## +# Termux app repo name. +# +# Default value: `termux-app` +## +TERMUX_APP__REPO_NAME="termux-app" + +## +# Termux app repo url. +# +# Default value: `https://github.com/termux/termux-app` +## +TERMUX_APP__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_APP__REPO_NAME" + + + +## +# Termux app namespace, i.e the Java package name under which Termux +# classes exists used for `TERMUX_APP__*_CLASS__*` and +# `TERMUX_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*`variables. +# +# - https://github.com/termux/termux-app/tree/master/app/src/main/java/com/termux +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# See also `TERMUX_APP__PACKAGE_NAME`. +# +# Default value: `com.termux` +## +TERMUX_APP__NAMESPACE="com.termux" + +__termux_build_props__add_variables_validator_actions "TERMUX_APP__NAMESPACE" "app_package_name" + + + +## +# Termux app apps directory path under `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux` +## +TERMUX_APP__APPS_DIR="$TERMUX__APPS_DIR_BY_IDENTIFIER/$TERMUX_APP__IDENTIFIER" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__APPS_DIR" "safe_absolute_path" + + + +## +# Termux app shell `Activity` class name that hosts the shell/terminal views. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxActivity.java +# +# Default value: `com.termux.app.TermuxActivity` +## +TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxActivity" + +## +# Termux app shell `Activity` component name for `TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxActivity` +## +TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME" + + + +## +# Termux app shell `Service` class name that manages the shells. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxService.java +# +# Default value: `com.termux.app.TermuxService` +## +TERMUX_APP__SHELL_SERVICE__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxService" + +## +# Termux app shell `Service` component name for `TERMUX_APP__SHELL_SERVICE__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxService` +## +TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__SHELL_SERVICE__CLASS_NAME" + + + +## +# Termux app RUN_COMMAND `Service` class name that receives commands via intents. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java +# - https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent +# +# Default value: `com.termux.app.RunCommandService` +## +TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.RunCommandService" + +## +# Termux app shell `Service` component name for `TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.RunCommandService` +## +TERMUX_APP__RUN_COMMAND_SERVICE__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME" + + + +## +# Termux app data sender `BroadcastReceiver` class name that receives +# data view broadcasts and sends the data with `ACTION_SEND` and +# `ACTION_VIEW` intents to other apps, like by `termux-open`. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxOpenReceiver.java +# - https://github.com/termux/termux-tools/blob/master/scripts/termux-open.in +# +# Default value: `com.termux.app.TermuxOpenReceiver` +## +TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxOpenReceiver" + +## +# Termux app data sender `BroadcastReceiver` component name for `TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxOpenReceiver` +## +TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME" + + + + + +## +# Termux apps info environment file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-apps-info.env` +## +TERMUX_APP__CORE__APPS_INFO_ENV_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_CORE__APPS_INFO_ENV_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__CORE__APPS_INFO_ENV_FILE" "safe_absolute_path" + +## +# Termux apps info json file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-apps-info.json` +## +TERMUX_APP__CORE__APPS_INFO_JSON_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_CORE__APPS_INFO_JSON_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__CORE__APPS_INFO_JSON_FILE" "safe_absolute_path" + +## +# `termux-am-socket` server file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-am` +## +TERMUX_APP__AM_SOCKET__SERVER_SOCKET_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_AM_SOCKET__SERVER_SOCKET_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__AM_SOCKET__SERVER_SOCKET_FILE" "safe_absolute_path unix_path_max" + + + + + +#### +# Variables for the Termux:API app that hosts the packages. +# +# - https://github.com/termux/termux-api +#### + +## +# Termux:API app package name used for +# `TERMUX_API_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*` variables. +# +# See also `TERMUX_API_APP__NAMESPACE`. +# +# Default value: `com.termux.api` +## +TERMUX_API_APP__PACKAGE_NAME="com.termux.api" + +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__PACKAGE_NAME" "app_package_name" + + + +## +# Termux:API app name. +# +# Default value: `Termux:API` +## +TERMUX_API_APP__NAME="$TERMUX__NAME:API" + +## +# Termux:API app identifier for `TERMUX__APPS_DIR_BY_IDENTIFIER` subdirectory. +# +# Default value: `termuxapi` +# Validation regex: `TERMUX__APPS_APP_IDENTIFIER_REGEX` +# Max length: `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` +## +TERMUX_API_APP__IDENTIFIER="termuxapi" + + + +## +# Termux:API app repo name. +# +# Default value: `termux-api` +## +TERMUX_API_APP__REPO_NAME="termux-api" + +## +# Termux:API app repo url. +# +# Default value: `https://github.com/termux/termux-api` +## +TERMUX_API_APP__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_API_APP__REPO_NAME" + + + +## +# Termux:API app namespace, i.e the Java package name under which +# Termux:API classes exists used for `TERMUX_API_APP__*_CLASS__*` and +# `TERMUX_API_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*`variables. +# +# - https://github.com/termux/termux-api/tree/master/app/src/main/java/com/termux/api +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# See also `TERMUX_API_APP__PACKAGE_NAME`. +# +# Default value: `com.termux.api` +## +TERMUX_API_APP__NAMESPACE="com.termux.api" + +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__NAMESPACE" "app_package_name" + + + +## +# Termux:API app apps directory path under `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termuxapi` +## +TERMUX_API_APP__APPS_DIR="$TERMUX__APPS_DIR_BY_IDENTIFIER/$TERMUX_API_APP__IDENTIFIER" +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__APPS_DIR" "safe_absolute_path" + + + +## +# Termux:API app API `BroadcastReceiver` class name that receives +# and processes API requests from command line via `termux-api` native +# library. +# +# - https://github.com/termux/termux-api/blob/master/app/src/main/java/com/termux/api/TermuxApiReceiver.java +# - https://github.com/termux/termux-api-package/blob/master/termux-api.c +# +# Default value: `com.termux.api.TermuxApiReceiver` +## +TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME="$TERMUX_API_APP__NAMESPACE.TermuxApiReceiver" + +## +# Termux:API app API `BroadcastReceiver` component name for `TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME`. +# +# Default value: `com.termux.api/com.termux.api.TermuxApiReceiver` +## +TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__COMPONENT_NAME="$TERMUX_API_APP__PACKAGE_NAME/$TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME" + + + + + +#### +# Variables for the `termux-api` package. +# +# - https://github.com/termux/termux-api-package +#### + +## +# The `termux-api` package repo name. +# +# Default value: `termux-api-package` +## +TERMUX_API_PKG__REPO_NAME="termux-api-package" + +## +# The `termux-api` package repo url. +# +# Default value: `https://github.com/termux/termux-api-package` +## +TERMUX_API_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_API_PKG__REPO_NAME" + + + + + +#### +# Variables for the `termux-core` package. +# +# - https://github.com/termux/termux-core-package +#### + +## +# The `termux-core` package repo name. +# +# Default value: `termux-core-package` +## +TERMUX_CORE_PKG__REPO_NAME="termux-core-package" + +## +# The `termux-core` package repo url. +# +# Default value: `https://github.com/termux/termux-core-package` +## +TERMUX_CORE_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_CORE_PKG__REPO_NAME" + + + + + +#### +# Variables for the `termux-am` package. +# +# - https://github.com/termux/TermuxAm +#### + +## +# The `termux-am` package repo name. +# +# Default value: `TermuxAm` +## +TERMUX_AM_PKG__REPO_NAME="TermuxAm" + +## +# The `termux-am` package repo url. +# +# Default value: `https://github.com/termux/TermuxAm` +## +TERMUX_AM_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_AM_PKG__REPO_NAME" + + + +## +# TermuxAm namespace, i.e the Java package name under which Termux +# classes exists used for `TERMUX_AM__*_CLASS__*` variables. +# +# This must not be changed unless the classes in the `TermuxAm` repo +# are moved to a different Java package name (in forks). +# +# - https://github.com/termux/TermuxAm/tree/master/app/src/main/java/com/termux/termuxam +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# Constant value: `com.termux.termuxam` +## +TERMUX_AM_APP__NAMESPACE="com.termux.termuxam" + +__termux_build_props__add_variables_validator_actions "TERMUX_AM_APP__NAMESPACE" "app_package_name" + + + +## +# TermuxAm main class that is passed as `start-class-name` to +# `/system/bin/app_process` when running `am.apk` set in `$CLASSPATH`. +# +# - https://github.com/termux/TermuxAm/blob/master/app/src/main/java/com/termux/termuxam/Am.java +# - https://github.com/termux/TermuxAm/blob/v0.8.0/am-libexec-packaged#L30 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/cmds/app_process/app_main.cpp;l=31 +# +# Default value: `com.termux.termuxam.Am` +## +TERMUX_AM_APP__AM_CLASS__CLASS_NAME="$TERMUX_AM_APP__NAMESPACE.Am" + + + + + +#### +# Variables for validating Termux variables. +#### + +## +# Regex that matches an absolute path that starts with a `/` with at +# least one characters under rootfs `/`. Duplicate or trailing path +# separators `/` are not allowed. +## +TERMUX_REGEX__ABSOLUTE_PATH='^(/[^/]+)+$' + +## +# Regex that matches a relative path that does not start with a `/`. +# Duplicate or trailing path separators `/` are not allowed. +## +TERMUX_REGEX__RELATIVE_PATH='^[^/]+(/[^/]+)*$' + +## +# Regex that matches (rootfs `/`) or (an absolute path that starts +# with a `/`). Duplicate or trailing path separators `/` are not +# allowed. +## +TERMUX_REGEX__ROOTFS_OR_ABSOLUTE_PATH='^((/)|((/[^/]+)+))$' + + +## +# Regex that matches a safe absolute path that starts with a `/` with +# at least one characters under rootfs `/`. Duplicate or trailing path +# separators `/` are not allowed. The path component characters must +# be in the range `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_ABSOLUTE_PATH='^(/[a-zA-Z0-9+,.=_-]+)+$' + +## +# Regex that matches a safe relative path that does not start with a +# `/`. Duplicate or trailing path separators `/` are not allowed. The +# path component characters must be in the range `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_RELATIVE_PATH='^[a-zA-Z0-9+,.=_-]+(/[a-zA-Z0-9+,.=_-]+)*$' + +## +# Regex that matches (rootfs `/`) or (a safe absolute path that starts +# with a `/`). Duplicate or trailing path separators `/` are not +# allowed. The path component characters must be in the range +# `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH='^((/)|((/[a-zA-Z0-9+,.=_-]+)+))$' + + +## +# Regex that matches a path containing single `/./` or double `/../` dot components. +## +TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH='((^\./)|(^\.\./)|(/\.$)|(/\.\.$)|(/\./)|(/\.\./))' + + +## +# Regex that matches invalid Termux rootfs paths. +# +# The Termux rootfs or prefix paths must not be equal to or be under +# specific Filesystem Hierarchy Standard paths or paths used by Termux +# docker image/host OS for its own files, as Termux packages files +# must be kept separate from the build host. The Termux app data/prefix +# directories are also wiped by `clean.sh` when not running on-device, +# which wouldn't be possible if Termux and host directories are shared. +# +# The invalid paths list does not include the `/data` and `/mnt/expand` +# paths under which private app data directories are assigned to +# Android apps, or the `/data/local/tmp` directory assigned to `adb` +# `shell` user, or the `/system` directory for the Android system. +# +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html +# - https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +## +TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS='^((/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/home)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr)|(/usr/local)|(((/usr/)|(/usr/local/))((bin)|(games)|(include)|(lib)|(libexec)|(lib[^/]+)|(sbin)|(share)|(src)|(X11R6))(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + +## +# Regex that matches invalid Termux home paths. +# +# Same reasoning as `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`, +# and invalid paths are the same as well except that `/home` is +# allowed, and `/` and all paths under `/usr` are not allowed. +# +# `/home` is allowed as package data files are not packaged from there. +## +TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS='^((/)|(/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + +## +# Regex that matches invalid Termux prefix paths. +# +# Same reasoning as `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`, +# and invalid paths are the same as well except that `/` is not +# allowed. +## +TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS='^((/)|(/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/home)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr)|(/usr/local)|(((/usr/)|(/usr/local/))((bin)|(games)|(include)|(lib)|(libexec)|(lib[^/]+)|(sbin)|(share)|(src)|(X11R6))(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + + +## +# Regex that matches an unsigned integer `>= 0`. +## +TERMUX_REGEX__UNSIGNED_INT='^[0-9]+$' + + +## +# Regex to match an android app package name. +# +# The package name must have at least two segments separated by a dot +# `.`, where each segment must start with at least one character in +# the range `[a-zA-Z]`, followed by zero or more characters in the +# range `[a-zA-Z0-9_]`. The package name length must also be +# `<= 255` (`NAME_MAX` for ext4 partitions). The length is not checked +# by this regex and it must be checked with `TERMUX__NAME_MAX`, as +# `bash` `=~` regex conditional does not support lookaround. +# +# Unlike Android, the Termux app package name max length is not `255` +# as its limited by `TERMUX__APPS_DIR___MAX_LEN` and `TERMUX__ROOTFS_DIR___MAX_LEN`. +# +# - https://developer.android.com/build/configure-app-module#set-application-id +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java;l=669-677 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java;l=63-103 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/os/FileUtils.java;l=954-994 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/PackageManager.java;l=2147-2155 +## +TERMUX_REGEX__APP_PACKAGE_NAME="^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$" + +## +# Regex to match an android app data path. +# +# The supported formats are: +# - `/data/data/` (for primary user `0`) if app is to be +# installed on internal sd. +# - `/data/user//` (for all users) if app is to +# be installed on internal sd. +# `/mnt/expand//user//` if app is +# to be installed on a removable/portable volume/sd card being used as +# adoptable storage. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +## +TERMUX_REGEX__APP_DATA_DIR_PATH='^(((/data/data)|(/data/user/[0-9]+)|(/mnt/expand/[^/]+/user/[0-9]+))/[^/]+)$' + + + + + +### +# Misc +### + +# Allow to override setup. +for f in "${HOME}/.config/termux/termuxrc.sh" "${HOME}/.termux/termuxrc.sh" "${HOME}/.termuxrc"; do + if [ -f "$f" ]; then + echo "Using builder configuration from '$f'..." + # shellcheck source=/dev/null + . "$f" + break + fi +done +unset f + + + + + +### +# Run Termux properties variable values validation. +### + +# Uncomment to print `TERMUX_` variables set +#compgen -v TERMUX_ | while read v; do echo "${v}=${!v}"; done + +## +# `__termux_build_props__validate_variables` +## +__termux_build_props__validate_variables() { + + local is_value_defined + local validator_action + local validator_actions + local variable_name + local variable_value + + if [[ ! "$TERMUX__INTERNAL_NAME" =~ ${TERMUX__INTERNAL_NAME_REGEX:?} ]]; then + echo "The TERMUX__INTERNAL_NAME '$TERMUX__INTERNAL_NAME' with length ${#TERMUX__INTERNAL_NAME} is invalid." 1>&2 + echo "Check 'TERMUX__INTERNAL_NAME_REGEX' variable docs for info on what is a valid internal name." 1>&2 + return 1 + fi + + if [ "${#TERMUX__INTERNAL_NAME}" -gt ${TERMUX__INTERNAL_NAME___MAX_LEN:?} ]; then + echo "The TERMUX__INTERNAL_NAME '$TERMUX__INTERNAL_NAME' with length ${#TERMUX__INTERNAL_NAME} is invalid." 1>&2 + echo "The TERMUX__INTERNAL_NAME must have max length \`<= TERMUX__INTERNAL_NAME___MAX_LEN ($TERMUX__INTERNAL_NAME___MAX_LEN)\`." 1>&2 + return 1 + fi + + if [[ ! "$TERMUX_APP__DATA_DIR" =~ ${TERMUX_REGEX__APP_DATA_DIR_PATH:?} ]]; then + echo "The TERMUX_APP__DATA_DIR '$TERMUX_APP__DATA_DIR' with length ${#TERMUX_APP__DATA_DIR} is invalid." 1>&2 + echo "The TERMUX_APP__DATA_DIR must match \`/data/data/\`, \`/data/user//\` \ +or \`/mnt/expand//user//\` formats." 1>&2 + return 1 + fi + + + for variable_name in "${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES[@]}"; do + if [[ ! "$variable_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + echo "The variable_name '$variable_name' in Termux properties variables validator actions is not a valid shell variable name." 1>&2 + return 1 + fi + + variable_value="${!variable_name:-}" + validator_actions="${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP["$variable_name"]}" + [[ -z "$validator_actions" ]] && continue + + if [[ -n "$variable_value" ]]; then + : + else + is_value_defined=0 + eval '[ -n "${'"$variable_name"'+x}" ] && is_value_defined=1' + + # If not defined. + if [[ "$is_value_defined" = "0" ]]; then + echo "The variable_name '$variable_name' in Termux properties variables validator actions is not defined." 1>&2 + return 1 + fi + + # If defined but unset. + [[ " ${validator_actions[*]} " == *" allow_unset_value "* ]] && continue + + echo "The Termux properties variable value for variable name '$variable_name' is not set." 1>&2 + return 1 + fi + + for validator_action in $validator_actions; do + case "$validator_action" in + allow_unset_value) + : + ;; + app_package_name) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__APP_PACKAGE_NAME:?} ]] || \ + [ "${#variable_value}" -gt "${TERMUX__NAME_MAX:?}" ]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must be a valid android app package name with \ +max length \`<= TERMUX__NAME_MAX ($TERMUX__NAME_MAX)\`." 1>&2 + echo "- https://developer.android.com/build/configure-app-module#set-application-id" 1>&2 + return 1 + fi + ;; + invalid_termux_rootfs_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS\`." 1>&2 + return 1 + fi + ;; + invalid_termux_home_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS\`." 1>&2 + return 1 + fi + ;; + invalid_termux_prefix_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS\`." 1>&2 + return 1 + fi + ;; + path_equal_to_or_under_termux_rootfs) + if [[ "$variable_value" != "${TERMUX__ROOTFS:?}" ]] && \ + { + { [[ "${TERMUX__ROOTFS:?}" != "/" ]] && [[ "$variable_value" != "${TERMUX__ROOTFS}/"* ]]; } || \ + { [[ "${TERMUX__ROOTFS:?}" == "/" ]] && [[ "$variable_value" != "/"* ]]; }; + }; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be equal to or be under TERMUX__ROOTFS \`$TERMUX__ROOTFS\`." 1>&2 + return 1 + fi + ;; + path_under_termux_rootfs) + if { [[ "${TERMUX__ROOTFS:?}" != "/" ]] && [[ "$variable_value" != "${TERMUX__ROOTFS}/"* ]]; } || \ + { [[ "${TERMUX__ROOTFS:?}" == "/" ]] && [[ "$variable_value" != "/"* ]]; }; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be under TERMUX__ROOTFS \`$TERMUX__ROOTFS\`." 1>&2 + return 1 + fi + ;; + safe_absolute_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_ABSOLUTE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match a safe absolute path that starts with a \`/\` with at least one \ +characters under rootfs \`/\`. Duplicate or trailing path separators \`/\` are not allowed. \ +The path component characters must be in the range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or \ +double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + safe_relative_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_RELATIVE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match a safe relative path that does not start with a \`/\`. \ +Duplicate or trailing path separators \`/\` are not allowed. The path component characters must be in the \ +range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + safe_rootfs_or_absolute_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match (rootfs \`/\`) or (a safe absolute path that starts with a \`/\`). \ +Duplicate or trailing path separators \`/\` are not allowed. The path component characters must be in the \ +range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + apps_api_socket__server_parent_dir) + if [[ "${#variable_value}" -ge "${TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN:?}" ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must have max length \`<= TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN \ +($TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN)\` including the null \`\0\` terminator." 1>&2 + return 1 + fi + ;; + unix_path_max) + if [[ "${#variable_value}" -ge "${TERMUX__UNIX_PATH_MAX:?}" ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must have max length \`<= TERMUX__UNIX_PATH_MAX ($TERMUX__UNIX_PATH_MAX)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + ;; + unsigned_int) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__UNSIGNED_INT:?} ]]; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be an unsigned integer \`>= 0\`." 1>&2 + return 1 + fi + ;; + *) + echo "The Termux properties variables validator action '$validator_action' for \ +variable name '$variable_name' is invalid." 1>&2 + return 1 + ;; + esac + done + done + + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX_APP__DATA_DIR}" -ge ${TERMUX_APP__DATA_DIR___MAX_LEN:?} ]; then + echo "The TERMUX_APP__DATA_DIR '$TERMUX_APP__DATA_DIR' with length ${#TERMUX_APP__DATA_DIR} is invalid." 1>&2 + echo "The TERMUX_APP__DATA_DIR must have max length \`<= TERMUX_APP__DATA_DIR___MAX_LEN ($TERMUX_APP__DATA_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__APPS_DIR}" -ge ${TERMUX__APPS_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__APPS_DIR '$TERMUX__APPS_DIR' with length ${#TERMUX__APPS_DIR} is invalid." 1>&2 + echo "The TERMUX__APPS_DIR must have max length \`<= TERMUX__APPS_DIR___MAX_LEN ($TERMUX__APPS_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + + if [[ ! "$TERMUX_APP__IDENTIFIER" =~ ${TERMUX__APPS_APP_IDENTIFIER_REGEX:?} ]]; then + echo "The TERMUX_APP__IDENTIFIER '$TERMUX_APP__IDENTIFIER' with length ${#TERMUX_APP__IDENTIFIER} is invalid." 1>&2 + echo "Check 'TERMUX__APPS_APP_IDENTIFIER_REGEX' variable docs for info on what is a valid app identifier." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX_APP__IDENTIFIER}" -gt ${TERMUX__APPS_APP_IDENTIFIER___MAX_LEN:?} ]; then + echo "The TERMUX_APP__IDENTIFIER '$TERMUX_APP__IDENTIFIER' with length ${#TERMUX_APP__IDENTIFIER} is invalid." 1>&2 + echo "The TERMUX_APP__IDENTIFIER must have max length \ +\`<= TERMUX__APPS_APP_IDENTIFIER___MAX_LEN ($TERMUX__APPS_APP_IDENTIFIER___MAX_LEN)\`." 1>&2 + return 1 + fi + + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__ROOTFS}" -ge ${TERMUX__ROOTFS_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__ROOTFS '$TERMUX__ROOTFS' with length ${#TERMUX__ROOTFS} is invalid." 1>&2 + echo "The TERMUX__ROOTFS must have max length \`<= TERMUX__ROOTFS_DIR___MAX_LEN ($TERMUX__ROOTFS_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__PREFIX}" -ge ${TERMUX__PREFIX_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' with length ${#TERMUX__PREFIX} is invalid." 1>&2 + echo "The TERMUX__PREFIX must have max length \`<= TERMUX__PREFIX_DIR___MAX_LEN ($TERMUX__PREFIX_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__PREFIX__TMP_DIR}" -ge ${TERMUX__PREFIX__TMP_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__PREFIX__TMP_DIR '$TERMUX__PREFIX__TMP_DIR' with length ${#TERMUX__PREFIX__TMP_DIR} is invalid." 1>&2 + echo "The TERMUX__PREFIX__TMP_DIR must have max length \`<= TERMUX__PREFIX__TMP_DIR___MAX_LEN ($TERMUX__PREFIX__TMP_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + + if [[ "$TERMUX__ROOTFS" != "/" ]] && \ + [[ "$__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT" == "true" ]]; then + if [[ "$TERMUX__PREFIX" != "$TERMUX__ROOTFS/usr" ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' is invalid." 1>&2 + echo "The TERMUX__PREFIX must be equal to '\$TERMUX__ROOTFS/usr' ($TERMUX__ROOTFS/usr) as per 'usr' merge format." 1>&2 + return 1 + fi + + if [[ "${TERMUX__PREFIX:?}" == "${TERMUX__HOME:?}" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX__HOME/"* ]] || \ + [[ "$TERMUX__HOME" == "$TERMUX__PREFIX/"* ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or TERMUX__HOME '$TERMUX__HOME' is invalid." 1>&2 + echo "The TERMUX__PREFIX must not be equal to TERMUX__HOME and they must not be under each other as per 'usr' merge format." 1>&2 + return 1 + fi + else + if [[ "${TERMUX__PREFIX:?}" == "${TERMUX__HOME:?}" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX__HOME/"* ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or TERMUX__HOME '$TERMUX__HOME' is invalid." 1>&2 + echo "The TERMUX__PREFIX must not be equal to or be under TERMUX__HOME." 1>&2 + return 1 + fi + fi + +} + +__termux_build_props__validate_variables || exit $? + +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES +unset __TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN +unset __TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT +unset __termux_build_props__add_variables_validator_actions +unset __termux_build_props__validate_variables diff --git a/scripts/setup-android-sdk.sh b/scripts/setup-android-sdk.sh index ac29dfa59bae4a..7613ebe66cef5b 100755 --- a/scripts/setup-android-sdk.sh +++ b/scripts/setup-android-sdk.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -e -u # Install desired parts of the Android SDK: diff --git a/scripts/setup-archlinux.sh b/scripts/setup-archlinux.sh index 21522732499683..5be6e68006badc 100755 --- a/scripts/setup-archlinux.sh +++ b/scripts/setup-archlinux.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e -u PACKAGES="" @@ -43,9 +43,22 @@ PACKAGES+=" lua" # Needed to build luarocks package. PACKAGES+=" python-recommonmark" # Needed for LLVM-8 documentation. PACKAGES+=" jre8-openjdk-headless" -sudo pacman -Syq --noconfirm $PACKAGES +# Do not require sudo if already running as root. +if [ "$(id -u)" = "0" ]; then + SUDO="" +else + SUDO="sudo" +fi -sudo mkdir -p /data/data/com.termux/files/usr -sudo chown -R $(whoami) /data +$SUDO pacman -Syq --noconfirm $PACKAGES + +. $(dirname "$(realpath "$0")")/properties.sh + +# Ownership of `TERMUX__PREFIX` must be fixed before `TERMUX_APP__DATA_DIR` +# if its under it, otherwise `TERMUX__ROOTFS` will not have its ownership fixed. +$SUDO mkdir -p "$TERMUX__PREFIX" +$SUDO chown -R "$(whoami)" "$TERMUX__PREFIX" +$SUDO mkdir -p "$TERMUX_APP__DATA_DIR" +$SUDO chown -R "$(whoami)" "${TERMUX_APP__DATA_DIR%"${TERMUX_APP__DATA_DIR#/*/}"}" # Get `/path/` from `/path/to/app__data_dir`. echo "Please also install ncurses5-compat-libs and makedepend packages from the AUR before continuing" diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh index 6cf97350e3c6db..adf918220a467e 100755 --- a/scripts/setup-ubuntu.sh +++ b/scripts/setup-ubuntu.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e -u PACKAGES="" @@ -57,11 +57,18 @@ PACKAGES+=" zip" # For smalltalk. PACKAGES+=" libssl-dev:i386" # Needed by swi-prolog 32-bit PACKAGES+=" zlib1g-dev:i386" +# Do not require sudo if already running as root. +if [ "$(id -u)" = "0" ]; then + SUDO="" +else + SUDO="sudo" +fi + # Allow 32-bit packages. -sudo dpkg --add-architecture i386 -sudo apt-get -yq update +$SUDO dpkg --add-architecture i386 +$SUDO apt-get -yq update -sudo DEBIAN_FRONTEND=noninteractive \ +$SUDO DEBIAN_FRONTEND=noninteractive \ apt-get install -yq --no-install-recommends $PACKAGES # Find and assign UBUNTU_VERSION @@ -76,8 +83,14 @@ curl -O http://security.ubuntu.com/ubuntu/pool/universe/o/openjdk-8/openjdk-8-jd curl -O http://security.ubuntu.com/ubuntu/pool/universe/o/openjdk-8/openjdk-8-jdk_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb curl -O http://security.ubuntu.com/ubuntu/pool/universe/o/openjdk-8/openjdk-8-jre_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb curl -O http://security.ubuntu.com/ubuntu/pool/universe/o/openjdk-8/openjdk-8-jre-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb -sudo dpkg -i openjdk-8-jre-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jre_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb || sudo apt install -f -y +$SUDO dpkg -i openjdk-8-jre-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jre_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb || $SUDO apt install -f -y rm openjdk-8-jre-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jre_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb openjdk-8-jdk-headless_8u212-b03-0ubuntu1."$UBUNTU_VERSION"_amd64.deb -sudo mkdir -p /data/data/com.termux/files/usr -sudo chown -R $(whoami) /data +. $(dirname "$(realpath "$0")")/properties.sh + +# Ownership of `TERMUX__PREFIX` must be fixed before `TERMUX_APP__DATA_DIR` +# if its under it, otherwise `TERMUX__ROOTFS` will not have its ownership fixed. +$SUDO mkdir -p "$TERMUX__PREFIX" +$SUDO chown -R "$(whoami)" "$TERMUX__PREFIX" +$SUDO mkdir -p "$TERMUX_APP__DATA_DIR" +$SUDO chown -R "$(whoami)" "${TERMUX_APP__DATA_DIR%"${TERMUX_APP__DATA_DIR#/*/}"}" # Get `/path/` from `/path/to/app__data_dir`.