#!/usr/bin/env bash
readonly XP_SHELL="/usr/bin/env bash"

# @Author Yamada, Yasuhiro
# @Filename xpanes

set -u
readonly XP_VERSION="4.2.0"

## trap might be updated in 'xpns_pre_execution' function
trap 'rm -f "${XP_CACHE_HOME}"/__xpns_*$$; xpns_clean_session' EXIT

## --------------------------------
# Error constants
## --------------------------------
# Invalid option/argument
readonly XP_EINVAL=4

# Could not open tty.
readonly XP_ETTY=5

# Invalid layout.
readonly XP_ELAYOUT=6

# Impossible layout: Small pane
readonly XP_ESMLPANE=7

# Log related exit status is 2x.
## Could not create a directory.
readonly XP_ELOGDIR=20

## Could not directory to store logs is not writable.
readonly XP_ELOGWRITE=21

# User's intentional exit is 3x
## User exit the process intentionally by following warning message.
readonly XP_EINTENT=30

## All the panes are closed before processing due to user's options/command.
readonly XP_ENOPANE=31

# Necessary commands are not found
readonly XP_ENOCMD=127

# ===============

# XP_THIS_FILE_NAME is supposed to be "xpanes".
readonly XP_THIS_FILE_NAME="${0##*/}"
# shellcheck disable=SC2155
readonly XP_THIS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
readonly XP_ABS_THIS_FILE_NAME="${XP_THIS_DIR}/${XP_THIS_FILE_NAME}"

# Prevent cache directory being created under root / directory in any case.
# This is quite rare case (but it can be happened).
readonly XP_USER_HOME="${HOME:-/tmp}"

# Basically xpanes follows XDG Base Direcotry Specification.
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.6.html
XDG_CACHE_HOME="${XDG_CACHE_HOME:-${XP_USER_HOME}/.cache}"
readonly XP_CACHE_HOME="${XDG_CACHE_HOME}/xpanes"

# This is supposed to be xpanes-12345(PID)
readonly XP_SESSION_NAME="${XP_THIS_FILE_NAME}-$$"
# Temporary window name is tmp-12345(PID)
readonly XP_TMP_WIN_NAME="tmp-$$"
readonly XP_EMPTY_STR="EMPTY"

readonly XP_SUPPORT_TMUX_VERSION_LOWER="1.8"

# Check dependencies just in case.
# Even POSIX compliant commands are only used in this program.
# `xargs`, `sleep`, `mkfifo` are omitted because minimum functions can work without them.
readonly XP_DEPENDENCIES="${XP_DEPENDENCIES:-tmux grep sed tr od echo touch printf cat sort pwd cd mkfifo}"

## --------------------------------
# User customizable shell variables
## --------------------------------
TMUX_XPANES_EXEC=${TMUX_XPANES_EXEC:-tmux}
TMUX_XPANES_PANE_BORDER_FORMAT="${TMUX_XPANES_PANE_BORDER_FORMAT:-#[bg=green,fg=black] #T #[default]}"
TMUX_XPANES_PANE_BORDER_STATUS="${TMUX_XPANES_PANE_BORDER_STATUS:-bottom}"
TMUX_XPANES_PANE_DEAD_MESSAGE=${TMUX_XPANES_PANE_DEAD_MESSAGE:-'\033[41m\033[4m\033[30m Pane is dead: Press [Enter] to exit... \033[0m\033[39m\033[49m'}
XP_DEFAULT_TMUX_XPANES_LOG_FORMAT="[:ARG:].log.%Y-%m-%d_%H-%M-%S"
TMUX_XPANES_LOG_FORMAT="${TMUX_XPANES_LOG_FORMAT:-${XP_DEFAULT_TMUX_XPANES_LOG_FORMAT}}"
XP_DEFAULT_TMUX_XPANES_LOG_DIRECTORY="${XP_CACHE_HOME}/logs"
TMUX_XPANES_LOG_DIRECTORY="${TMUX_XPANES_LOG_DIRECTORY:-${XP_DEFAULT_TMUX_XPANES_LOG_DIRECTORY}}"

## --------------------------------
# Initialize Options
## --------------------------------
# options which work individually.
# readonly XP_FLAG_OPTIONS="[hVdetxs]"
# options which need arguments.
readonly XP_ARG_OPTIONS="[ISclnCRB]"
readonly XP_DEFAULT_LAYOUT="tiled"
readonly XP_DEFAULT_REPSTR="{}"
readonly XP_DEFAULT_CMD_UTILITY="echo {} "
readonly XP_SSH_CMD_UTILITY="ssh -o StrictHostKeyChecking=no {} "
readonly XP_OFS="${XP_OFS:- }"
XP_OPTIONS=()
XP_ARGS=()
XP_STDIN=()
XP_BEGIN_ARGS=()
XP_IS_PIPE_MODE=0
XP_OPT_IS_SYNC=1
XP_OPT_DRY_RUN=0
XP_OPT_ATTACH=1
XP_OPT_LOG_STORE=0
XP_REPSTR=""
XP_DEFAULT_SOCKET_PATH_BASE="${XP_CACHE_HOME}/socket"
XP_DEFAULT_SOCKET_PATH="${XP_DEFAULT_SOCKET_PATH_BASE}.$$"
XP_SOCKET_PATH="${XP_SOCKET_PATH:-${XP_DEFAULT_SOCKET_PATH}}"
XP_NO_OPT=0
XP_OPT_CMD_UTILITY=0
XP_CMD_UTILITY=""
XP_LAYOUT="${XP_DEFAULT_LAYOUT}"
XP_MAX_PANE_ARGS=""
XP_OPT_SET_TITLE=0
XP_OPT_CHANGE_BORDER=0
XP_OPT_EXTRA=0
XP_OPT_REUSE=0
XP_OPT_SPEEDY=0
XP_OPT_SPEEDY_AWAIT=0
XP_OPT_USE_PRESET_LAYOUT=0
XP_OPT_CUSTOM_SIZE_COLS=
XP_OPT_CUSTOM_SIZE_ROWS=
XP_OPT_BULK_COLS=
XP_WINDOW_WIDTH=
XP_WINDOW_HEIGHT=
XP_COLS=
XP_COLS_OFFSETS=
XP_OPT_INTERVAL=0
XP_OPT_DEBUG=0
XP_OPT_IGNORE_SIZE_LIMIT=0

xpns_log() {
  local _loglevel="info"
  local debugoutput=""
  if [[ "$#" -gt 1 ]]; then
    # we ignore the return value of this command, hence disabling shell check
    # shellcheck disable=SC2155
    local _specifiedloglevel=$(echo "$1" | tr '[:upper:]' '[:lower:]')
    case ${_specifiedloglevel} in
      info | warning | error)
        loglevel=${_specifiedloglevel}
        ;;
      debug)
        if [[ $XP_OPT_DEBUG -eq 1 ]]; then
          debugoutput=$(date "+:%F_%T"):${FUNCNAME[1]}
          loglevel=${_specifiedloglevel}
        else
          return
        fi
        ;;
      *)
        printf "[%s] %s %s\n" "${XP_THIS_FILE_NAME}:internal error" "invalid log type, if you get this error." "Please file an issue on github: https://github.com/greymd/tmux-xpanes/issues" >&2
        return
        ;;
    esac
    shift
  fi
  # in this case we want empty arguments, as debugoutput is not populated at all times.
  # shellcheck disable=SC2183
  printf "[%s] %s %s %s\n" "${XP_THIS_FILE_NAME}"":${loglevel}""${debugoutput}" "$*" >&2
}

xpns_usage_warn() {
  xpns_usage_short >&2
  echo "Try '${XP_THIS_FILE_NAME} --help' for more information." >&2
}

xpns_usage_short() {
  cat << _EOS_
Usage: ${XP_THIS_FILE_NAME} [OPTIONS] [argument ...]
Usage(Pipe mode): command ... | ${XP_THIS_FILE_NAME} [OPTIONS] [<command> ...]
_EOS_
}

xpns_usage() {
  cat << USAGE
Usage:
  ${XP_THIS_FILE_NAME} [OPTIONS] [argument ...]

Usage(Pipe mode):
  command ... | ${XP_THIS_FILE_NAME} [OPTIONS] [<command> ...]

OPTIONS:
  -h,--help                    Display this help and exit.
  -V,--version                 Output version information and exit.
  -B <begin-command>           Run <begin-command> before processing <command> in each pane. Multiple options are allowed.
  -c <command>                 Set <command> to be executed in each pane. Default is \`echo {}\`.
  -d,--desync                  Make synchronize-panes option off in new window.
  -e                           Execute given arguments as is. Same as \`-c '{}'\`
  -I <repstr>                  Replacing one or more occurrences of <repstr> in command provided by -c or -B. Default is \`{}\`.
  -C NUM,--cols=NUM            Number of columns of window layout.
  -R NUM,--rows=NUM            Number of rows of window layout.
  -l <layout>                  Set the preset of window layout. Recognized layout arguments are:
                               t    tiled
                               eh   even-horizontal
                               ev   even-vertical
                               mh   main-horizontal
                               mv   main-vertical
  -n <number>                  Set the maximum number of <argument> taken for each pane.
  -r                           Reuse the existing panes, or create then in the current active window.
  -s                           Speedy mode: Run command without opening an interactive shell.
  -ss                          Speedy mode AND close a pane automatically at the same time as process exiting.
  -S <socket-path>             Set a full alternative path to the server socket.
  -t                           Display each argument on the each pane's border as their title.
  -x                           Create extra panes in the current active window.
  --log[=<directory>]          Enable logging and store log files to ~/.cache/xpanes/logs or <directory>.
  --log-format=<FORMAT>        Make name of log files follow <FORMAT>. Default is \`${XP_DEFAULT_TMUX_XPANES_LOG_FORMAT}\`.
  --ssh                        Same as \`-t -s -c 'ssh -o StrictHostKeyChecking=no {}'\`.
  --stay                       Do not switch to new window.
  --bulk-cols=NUM1[,NUM2 ...]  Set number of columns on multiple rows (i.e, "2,2,2" represents 2 cols x 3 rows).
  --interval=<seconds>         Set interval between each pane creation and each command execution.
                               sleep(1) is used for the interval.
  --debug                      Print debug message.

Copyright (c) 2023 Yamada, Yasuhiro
Released under the MIT License.
https://github.com/greymd/tmux-xpanes
USAGE
}

# Show version number
xpns_version() {
  echo "${XP_THIS_FILE_NAME} ${XP_VERSION}"
}

# Get version number for tmux
xpns_get_tmux_version() {
  local _tmux_version=""
  if ! ${TMUX_XPANES_EXEC} -V &> /dev/null; then
    # From tmux 0.9 to 1.3, there is no -V option.
    _tmux_version="tmux 0.9-1.3"
  else
    _tmux_version="$( ${TMUX_XPANES_EXEC} -V)"
  fi
  read -r _ _ver <<< "${_tmux_version}"
  # Strip the leading "next-" part that is present in tmux versions that are
  # in development. Eg: next-3.3
  echo "${_ver//next-/}"
}

# Check whether the prefered tmux version is greater than host's tmux version.
# $1 ... Prefered version.
# $2 ... Host tmux version(optional).
# In case of tmux version is 1.7, the result will be like this.
# 0 is true, 1 is false.
##  arg  -> result
#   func 1.5  1.7 -> 0
#   func 1.6  1.7 -> 0
#   func 1.7  1.7 -> 0
#   func 1.8  1.7 -> 1
#   func 1.9  1.7 -> 1
#   func 1.9a 1.7 -> 1
#   func 2.0  1.7 -> 1
xpns_tmux_is_greater_equals() {
  local _check_version="$1"
  local _tmux_version="${2:-$(xpns_get_tmux_version)}"
  # Simple numerical comparison does not work because there is the version like "1.9a".
  if [[ "$( printf "%s\\n%s" "${_tmux_version}" "${_check_version}" | sort -n | head -n 1)" != "${_check_version}" ]]; then
    return 1
  else
    return 0
  fi
}

xpns_get_local_tmux_conf() {
  local _conf_name="$1"
  local _session="${2-}"
  {
    if [[ -z "${_session-}" ]]; then
      ${TMUX_XPANES_EXEC} show-window-options
    else
      ${TMUX_XPANES_EXEC} -S "${_session}" show-window-options
    fi
  } | grep "^${_conf_name}" |
    {
      read -r _ _v
      printf "%s\\n" "${_v}"
    }
}

xpns_get_global_tmux_conf() {
  local _conf_name="$1"
  local _session="${2-}"
  {
    if [[ -z "${_session-}" ]]; then
      ${TMUX_XPANES_EXEC} show-window-options -g
    else
      ${TMUX_XPANES_EXEC} -S "${_session}" show-window-options -g
    fi
  } | grep "^${_conf_name}" |
    {
      read -r _ _v
      printf "%s\\n" "${_v}"
    }
}

# Disable allow-rename because
# window separation does not work correctly
# if "allow-rename" option is on
xpns_suppress_allow_rename()  {
  local _default_allow_rename="$1"
  local _session="${2-}"
  if [[ "${_default_allow_rename-}" == "on"  ]]; then
    ## Temporary, disable "allow-rename"
    xpns_log "debug" "'allow-rename' option is 'off' temporarily."
    if [[ -z "${_session-}" ]]; then
      ${TMUX_XPANES_EXEC} set-window-option -g allow-rename off
    else
      ${TMUX_XPANES_EXEC} -S "${_session}" set-window-option -g allow-rename off
    fi
  fi
}

# Restore default "allow-rename"
# Do not write like 'xpns_restore_allow_rename "some value" "some value" > /dev/null'
# In tmux 1.6, 'tmux set-window-option' might be stopped in case of redirection.
xpns_restore_allow_rename()  {
  local _default_allow_rename="$1"
  local _session="${2-}"
  if [[ "${_default_allow_rename-}" == "on"  ]]; then
    xpns_log "debug" "Restore original value of 'allow-rename' option."
    if [[ -z "${_session-}" ]]; then
      ${TMUX_XPANES_EXEC} set-window-option -g allow-rename on
    else
      ${TMUX_XPANES_EXEC} -S "${_session}" set-window-option -g allow-rename on
    fi
  fi
}

xpns_interval() {
  local _interval="$1"
  if [[ "${_interval}" != "0" ]]; then
    sleep "${_interval}"
  fi
}

# func "11" "2"
#  => 6
# 11 / 2 = 5.5 => ceiling => 6
xpns_ceiling()  {
  local _divide="$1"
  shift
  local _by="$1"
  printf "%s\\n" $(((_divide + _by - 1) / _by))
}

# func "10" "3"
#  => 4 3 3
# Divide 10 into 3 parts as equally as possible.
xpns_divide_equally()  {
  local _number="$1"
  shift
  local _count="$1"
  local _upper _lower _upper_count _lower_count
  _upper="$(xpns_ceiling "$_number" "$_count")"
  _lower=$((_upper - 1))
  _lower_count=$((_upper * _count - _number))
  _upper_count=$((_count - _lower_count))
  eval "printf '${_upper} %.0s' {1..$_upper_count}"
  ((_lower_count > 0))   && eval "printf '${_lower} %.0s' {1..$_lower_count}"
}

# echo 3 3 3 3 | func
# => 3 6 9 12
xpns_nums_accumulate_sum()  {
  local s=0
  while read -r n; do
    ((s = s + n))
    printf "%s " "$s"
  done < <( cat | tr ' ' '\n')
}

# func 3 2 2 2
# => 4 4 1
#
# For example, "3 2 2 2" represents following cell positions
#   1  2  3
# 1 [] [] [] => 3 rows
# 2 [] []    => 2 rows
# 3 [] []    => 2 rows
# 4 [] []    => 2 rows
#
# After the transposition, it must be "4 4 1" which represents below
#   1  2  3  4
# 1 [] [] [] [] => 4 rows
# 2 [] [] [] [] => 4 rows
# 3 []          => 1 rows
xpns_nums_transpose()  {
  local _colnum="$1"
  local _spaces=
  local _result=
  xpns_log "debug" "column num = $_colnum, input = $*"
  _spaces="$(for i in "$@"; do
    printf "%${i}s\\n"
  done)"

  # 'for' statement does not work somehow
  _result="$(while read -r i; do
    ## This part is depending on the following 'cut' behavior
    ## $ echo 1234 | cut -c 5
    ## => result is supposed to be empty
    printf "%s\\n" "$_spaces" | cut -c "$i" | grep -c ' '
  done < <(xpns_seq 1 "${_colnum}") | xpns_newline2space)"
  xpns_log "debug" "result = $_result"
  printf "%s\\n" "$_result"
}

# Adjust size of columns and rows in accordance with given N
# func <col> <row> <N>
# i.e:
#     func "" "" 20
#       => returns 4 5
#     func "6" 0 20
#       => returns 6 4
xpns_adjust_col_row() {
  local col="${1:-0}"
  shift
  local row="${1:-0}"
  shift
  local N="$1"
  shift
  local fix_col_flg
  local fix_row_flg
  ((col != 0))   && fix_col_flg=1 || fix_col_flg=0
  ((row != 0))   && fix_row_flg=1 || fix_row_flg=0

  # This is just a author (@greymd)'s preference.
  if ((fix_col_flg == 0))   && ((fix_row_flg == 0))   && ((N == 2)); then
    col=2
    row=1
    printf "%d %d\\n" "${col}" "${row}"
    return
  fi

  # If both values are provided, col is used.
  if ((fix_col_flg == 1))   && ((fix_row_flg == 1)); then
    row=0
    fix_row_flg=0
  fi
  # This algorhythm is almost same as tmux default
  #   https://github.com/tmux/tmux/blob/2.8/layout-set.c#L436
  while ((col * row < N)); do
    ((fix_row_flg != 1))   && ((row = row + 1))
    if ((col * row < N)); then
      ((fix_col_flg != 1))   &&  ((col = col + 1))
    fi
  done
  printf "%d %d\\n" "${col}" "${row}"
}

# Make each line unique by adding index number
# echo aaa bbb ccc aaa ccc ccc | xargs -n 1 | xpns_unique_line
#  aaa-1
#  bbb-1
#  ccc-1
#  aaa-2
#  ccc-2
#  ccc-3
#
# Eval is used because associative array is not supported before bash 4.2
xpns_unique_line()  {
  local _val_name
  while read -r line; do
    _val_name="__xpns_hash_$(printf "%s" "${line}" | xpns_value2key)"
    # initialize variable
    eval "${_val_name}=\${${_val_name}:-0}"
    # increment variable
    eval "${_val_name}=\$(( ++${_val_name} ))"
    printf "%s\\n" "${line}-$(eval printf "%s" "\$${_val_name}")"
  done
}

#
# Generate log file names from given arguments.
# Usage:
#        echo <arg1> <arg2> ... | xpns_log_filenames <FORMAT>
# Return:
#        File names.
# Example:
#        $ echo aaa bbb ccc aaa ccc ccc | xargs -n 1 | xpns_log_filenames '[:ARG:]_[:PID:]_%Y%m%d.log'
#        aaa-1_1234_20160101.log
#        bbb-1_1234_20160101.log
#        ccc-1_1234_20160101.log
#        aaa-2_1234_20160101.log
#        ccc-2_1234_20160101.log
#        ccc-3_1234_20160101.log
#
xpns_log_filenames()  {
  local _arg_fmt="$1"
  local _full_fmt=
  _full_fmt="$(date "+${_arg_fmt}")"
  cat |
    # 1st argument + '-' + unique number (avoid same argument has same name)
    xpns_unique_line |
    while   read -r _arg; do
      cat <<< "${_full_fmt}" | sed -e "s/\\[:ARG:\\]/${_arg}/g" -e "s/\\[:PID:\\]/$$/g"
    done
}

## --------------------------------
# Normalize directory by making following conversion.
#  * Tilde expansion.
#  * Remove the slash '/' at the end of the dirname.
# Usage:
#        xpns_normalize_directory <direname>
# Return:
#        Normalized <dirname>
## --------------------------------

xpns_normalize_directory() {
  # Remove end of slash '/' for the first arguement
  local _dir="${1%/}"
  # tilde expansion
  printf "%s\\n" "${_dir/#~/$HOME}"
}
## --------------------------------
# Ensure existence of given directory
# Usage:
#        xpns_is_valid_directory <dirname>
# Return:
#        Absolute path of the <dirname>
## --------------------------------
xpns_is_valid_directory() {
  local _dir="$1"
  local _checkfile="${XP_THIS_FILE_NAME}.$$"
  # Check directory.
  if ! [[ -d "${_dir}" ]]; then
    # Create directory
    if ! mkdir "${_dir}"; then
      xpns_log "error" "Failed to create ${_dir}"
      exit ${XP_ELOGDIR}
    fi
    xpns_log "info" "${_dir} is created."
  fi
  # Try to create file.
  #   Not only checking directory permission,
  #   but also i-node and other misc situations.
  if ! touch "${_dir}/${_checkfile}"; then
    xpns_log "error" "${_dir} is not writable."
    rm -f "${_dir}/${_checkfile}"
    exit ${XP_ELOGWRITE}
  fi
  rm -f "${_dir}/${_checkfile}"
}

# Convert array to string which is can be used as command line argument.
# Usage:
#       xpns_arr2args <array object>
# Example:
#       array=(aaa bbb "ccc ddd" eee "f'f")
#       xpns_arr2args "${array[@]}"
#       @returns "'aaa' 'bbb' 'ccc ddd' 'eee' 'f\'f'"
# Result:
xpns_arr2args() {
  # If there is no argument, usage will be shown.
  if [[ $# -lt 1 ]]; then
    return 0
  fi
  for _arg in "$@"; do
    # Use 'cat <<<"input"' command instead of 'echo',
    # because such the command recognizes option like '-e'.
    cat <<< "${_arg}" |
      # Escaping single quotes and
      # Surround argument with single quotations.
      sed "s/'/'\"'\"'/g; s/^/'/; s/$/' /" |
      # Remove new lines
      tr -d '\n'
  done
}

# Extract first field to generate window name.
# ex, $2     =  'aaa bbb ccc'
#   return   =  aaa-12345(PID)
xpns_generate_window_name() {
  local _unprintable_str="${1-}"
  shift
  # Leave first 200 characters to prevent
  # the name exceed the maximum length of tmux window name (2000 byte).
  printf "%s\\n" "${1:-${_unprintable_str}}" |
    (   read -r _name _ && printf "%s\\n" "${_name:0:200}-$$" )
}

# Convert any string (including multi-byte chars) to another string
# which can be handled as tmux window name.
xpns_value2key() {
  od -v -tx1 -An  | tr -dc 'a-zA-Z0-9' | tr -d '\n'
}

# Restore string encoded by xpns_value2key function.
xpns_key2value() {
  read -r _key
  # shellcheck disable=SC2059
  printf "$(printf "%s" "$_key" | sed 's/../\\x&/g')"
}

# Remove empty lines
# This function behaves like `awk NF`
xpns_rm_empty_line() {
  {
    cat
    printf "\\n"
  } | while IFS= read -r line; do
    # shellcheck disable=SC2086
    set -- ${line-}
    if [[ $# != 0 ]]; then
      printf "%s\\n" "${line}"
    fi
  done
}

# Enable logging feature to the all the panes in the target window.
xpns_enable_logging() {
  local _window_name="$1"
  shift
  local _index_offset="$1"
  shift
  local _log_dir="$1"
  shift
  local _log_format="$1"
  shift
  local _unprintable_str="$1"
  shift
  local _args=("$@")
  local _args_num=$(($# - 1))
  # Generate log files from arguments.
  local _idx=0
  while read -r _logfile; do
    # Start logging
    xpns_log "debug" "Start logging pipe-pane(cat >> '${_log_dir}/${_logfile}')"
    ${TMUX_XPANES_EXEC} \
      pipe-pane -t "${_window_name}.$((_idx + _index_offset))" \
      "cat >> '${_log_dir}/${_logfile}'" # Tilde expansion does not work here.
    _idx=$((_idx + 1))
  done < <(
    for i in $(xpns_seq 0 "${_args_num}"); do
      # Replace empty string.
      printf "%s\\n" "${_args[i]:-${_unprintable_str}}"
    done | xpns_log_filenames "${_log_format}"
  )
}

## Print "1" on the particular named pipe
xpns_notify() {
  local _wait_id="$1"
  shift
  local _fifo=
  _fifo="${XP_CACHE_HOME}/__xpns_${_wait_id}"
  xpns_log "debug" "Notify to $_fifo"
  printf "%s\\n" 1 > "$_fifo" &
}

xpns_notify_logging() {
  local _window_name="$1"
  shift
  local _args_num=$(($# - 1))
  for i in $(xpns_seq 0 "${_args_num}"); do
    xpns_notify "log_${_window_name}-${i}-$$"
  done
}

xpns_notify_sync() {
  local _window_name="$1"
  shift
  local _interval="$1"
  shift
  local _args_num=$(($# - 1))
  for i in $(xpns_seq 0 "${_args_num}"); do
    xpns_interval "${_interval}"
    xpns_notify "sync_${_window_name}-${i}-$$"
  done
}

xpns_is_window_alive() {
  local _window_name="$1"
  shift
  local _speedy_await_flag="$1"
  shift
  local _def_allow_rename="$1"
  shift
  local _window_name_on_tmux _exit_status
  # Check not only exit status but also the if the window id is empty,
  # because the exit status of display-message is
  # no longer available from tmux version 3.2 (GitHub Issue #175)
  _window_name_on_tmux=$(${TMUX_XPANES_EXEC} display-message -t "$_window_name" -p '#{window_id}')
  _exit_status=$?
  if [[ $_exit_status -ne 0 ]] || [[ -z "$_window_name_on_tmux" ]]; then
    xpns_log "info" "All the panes are closed before displaying the result."
    if [[ "${_speedy_await_flag}" -eq 0 ]]; then
      xpns_log "info" "Use '-s' option instead of '-ss' option to avoid this behavior."
    fi
    xpns_restore_allow_rename "${_def_allow_rename-}"
    exit ${XP_ENOPANE}
  fi
}

xpns_inject_title() {
  local _target_pane="$1"
  shift
  local _message="$1"
  shift
  local _pane_tty=
  _pane_tty="$( ${TMUX_XPANES_EXEC} display-message -t "${_target_pane}" -p "#{pane_tty}")"
  printf "\\033]2;%s\\033\\\\" "${_message}" > "${_pane_tty}"
  xpns_log "debug" "target_pane=${_target_pane} pane_title=${_message} pane_tty=${_pane_tty}"
}

xpns_is_pane_title_required() {
  local _title_flag="$1"
  shift
  local _extra_flag="$1"
  shift
  local _pane_border_status=
  _pane_border_status=$(xpns_get_local_tmux_conf "pane-border-status")
  if [[ $_title_flag -eq 1 ]]; then
    return 0
  elif [[ ${_extra_flag} -eq 1 ]] &&
    [[ "${_pane_border_status}" != "off"    ]] &&
    [[ -n "${_pane_border_status}"    ]]; then
    ## For -x option
    # Even the -t option is not specified, it is required to inject pane title here.
    # Because user expects the title is displayed on the pane if the original window is
    # generated from tmux-xpanes with -t option.
    return 0
  fi
  return 1
}

# Set pane titles for each pane for -t option
xpns_set_titles() {
  local _window_name="$1"
  shift
  local _index_offset="$1"
  shift
  local _index=0
  local _pane_index=
  for arg in "$@"; do
    _pane_index=$((_index + _index_offset))
    xpns_inject_title "${_window_name}.${_pane_index}" "${arg}"
    _index=$((_index + 1))
  done
}

# Send command to the all the panes in the target window.
xpns_send_commands() {
  local _window_name="$1"
  shift
  local _index_offset="$1"
  shift
  local _interval="$1"
  shift
  local _repstr="$1"
  shift
  local _cmd="$1"
  shift
  local _index=0
  for arg in "$@"; do
    xpns_interval "${_interval}"
    _pane_index=$((_index + _index_offset))
    ${TMUX_XPANES_EXEC} send-keys -t "${_window_name}.${_pane_index}" "${_cmd//${_repstr}/${arg}}" C-m
    ((_index++))
  done
}

# Separate window vertically, when the number of panes is 1 or 2.
xpns_organize_panes() {
  local _window_name="$1"
  shift
  local _args_num="$1"

  # Default behavior
  local layout_command=" select-layout -t ${_window_name} even-horizontal"
  if ((_args_num > 1)); then
    layout_command=" select-layout -t ${_window_name} tiled"
  fi
  # Update layout
  if [[ "${XP_LAYOUT}" != "${XP_DEFAULT_LAYOUT}" ]]; then
    layout_command=" select-layout -t ${_window_name} ${XP_LAYOUT}"
  fi
  # shellcheck disable=SC2086
  ${TMUX_XPANES_EXEC} $layout_command
}

#
# Generate sequential number descending order.
# seq is not used because old version of
# seq does not generate descending order.
# $ xpns_seq 3 0
# 3
# 2
# 1
# 0
#
xpns_seq()  {
  local _num1="$1"
  local _num2="$2"
  eval "printf \"%d\\n\" {$_num1..$_num2}"
}

xpns_wait_func() {
  local _wait_id="$1"
  local _fifo="${XP_CACHE_HOME}/__xpns_${_wait_id}"
  local _arr=("$_fifo")
  local _fifo_arg=
  _fifo_arg=$(xpns_arr2args "${_arr[@]}")
  xpns_log "debug" "mkfifo $_fifo"
  mkfifo "${_fifo}"
  xpns_log "debug" "grep -q 1 ${_fifo_arg}"
  printf "%s\\n" "grep -q 1 ${_fifo_arg}"
}

# Split a new window into multiple panes.
#
xpns_split_window() {
  local _window_name="$1"
  shift
  local _log_flag="$1"
  shift
  local _title_flag="$1"
  shift
  local _speedy_flag="$1"
  shift
  local _await_flag="$1"
  shift
  local _interval="$1"
  shift
  local _pane_base_index="$1"
  shift
  local _repstr="$1"
  shift
  local _cmd_template="$1"
  shift
  local _exec_cmd=
  local _sep_count=0
  local args=("$@")
  _last_idx=$((${#args[@]} - 1))

  for i in $(xpns_seq $_last_idx 0); do
    xpns_log "debug" "Index:${i} Argument:${args[i]}"
    _sep_count=$((_sep_count + 1))
    _exec_cmd="${_cmd_template//${_repstr}/${args[i]}}"
    xpns_interval "${_interval}"
    ## Speedy mode
    if [[ $_speedy_flag -eq 1 ]]; then
      _exec_cmd=$(xpns_inject_wait_command "${_log_flag}" "${_title_flag}" "${_speedy_flag}" "${_await_flag}" "$i" "${_exec_cmd}")
      # Execute command as a child process of default-shell.
      ${TMUX_XPANES_EXEC} split-window -t "${_window_name}" -h -d "${_exec_cmd}"
    else
      # Open login shell and execute command on the interactive screen.
      ${TMUX_XPANES_EXEC} split-window -t "${_window_name}" -h -d
    fi
    # Restraining that size of pane's width becomes
    # less than the minimum size which is defined by tmux.
    if [[ ${_sep_count} -gt 2 ]]; then
      ${TMUX_XPANES_EXEC} select-layout -t "${_window_name}" tiled
    fi
  done
}

#
# Create new panes on existing window.
# Usage:
#    func <window name> <offset of index> <number of pane>
#
xpns_prepare_extra_panes() {
  local _window_name="$1"
  shift
  local _pane_base_index="$1"
  shift
  local _log_flag="$1"
  shift
  local _title_flag="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _await_flg="$1"
  shift
  local _interval="$1"
  shift
  # specify a pane which has the biggest index number.
  #   Because pane_id may not be immutable.
  #   If the small number of index is specified here, correspondance between pane_title and command can be slip off.
  ${TMUX_XPANES_EXEC} select-pane -t "${_window_name}.${_pane_base_index}"

  # split window into multiple panes
  xpns_split_window \
    "${_window_name}" \
    "${_log_flag}" \
    "${_title_flag}" \
    "${_speedy_flg}" \
    "${_await_flg}" \
    "${_interval}" \
    "${_pane_base_index}" \
    "$@"
}

xpns_get_joined_begin_commands()  {
  local _commands="$1"
  if [[ "${#XP_BEGIN_ARGS[*]}" -lt 1 ]]; then
    printf "%s" "${_commands}"
    return
  fi
  printf "%s\\n" "${XP_BEGIN_ARGS[@]}" "${_commands}"
}

xpns_inject_wait_command()  {
  local _log_flag="$1"
  shift
  local _title_flag="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _await_flg="$1"
  shift
  local _idx="$1"
  shift
  local _exec_cmd="$1"
  shift

  ## Speedy mode + logging
  if [[ "${_log_flag}" -eq 1 ]] && [[ "${_speedy_flg}" -eq 1 ]]; then
    # Wait for start of logging
    # Without this part, logging thread may start after new process is finished.
    # Execute function to wait for logging start.
    _exec_cmd="$(xpns_wait_func "log_${_window_name}-${_idx}-$$")"$'\n'"${_exec_cmd}"
  fi

  ## Speedy mode (Do not allow to close panes before the separation is finished).
  if [[ "${_speedy_flg}" -eq 1 ]]; then
    _exec_cmd="$(xpns_wait_func "sync_${_window_name}-${_idx}-$$")"$'\n'${_exec_cmd}
  fi

  ## -s: Speedy mode (Not -ss: Speedy mode + nowait)
  if [[ "${_await_flg}" -eq 1 ]]; then
    local _msg
    _msg="$(xpns_arr2args "${TMUX_XPANES_PANE_DEAD_MESSAGE}" | sed 's/"/\\"/g')"
    _exec_cmd="${_exec_cmd}"$'\n'"${XP_SHELL} -c \"printf -- ${_msg} >&2 && read\""
  fi
  printf "%s" "${_exec_cmd}"
}

xpns_new_window()  {
  local _window_name="$1"
  shift
  local _attach_flg="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _exec_cmd="$1"
  shift
  local _window_id=

  # Create new window.
  if [[ "${_attach_flg}" -eq 1 ]]; then
    if [[ "${_speedy_flg}" -eq 1 ]]; then
      _window_id=$(${TMUX_XPANES_EXEC} new-window -n "${_window_name}" -F '#{window_id}' -P "${_exec_cmd}")
    else
      _window_id=$(${TMUX_XPANES_EXEC} new-window -n "${_window_name}" -F '#{window_id}' -P)
    fi
  else
    # Keep background
    if [[ "${_speedy_flg}" -eq 1 ]]; then
      _window_id=$(${TMUX_XPANES_EXEC} new-window -n "${_window_name}" -F '#{window_id}' -P -d "${_exec_cmd}")
    else
      _window_id=$(${TMUX_XPANES_EXEC} new-window -n "${_window_name}" -F '#{window_id}' -P -d)
    fi
  fi
  printf "%s" "${_window_id}"
}

xpns_new_pane_vertical()  {
  local _window_id="$1"
  shift
  local _cell_height="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _exec_cmd="$1"
  shift
  local _pane_id=
  if [[ "${_speedy_flg}" -eq 1 ]]; then
    _pane_id="$(${TMUX_XPANES_EXEC} split-window -t "$_window_id" -v -d -l "${_cell_height}" -F '#{pane_id}' -P "${_exec_cmd}")"
  else
    _pane_id="$(${TMUX_XPANES_EXEC} split-window -t "$_window_id" -v -d -l "${_cell_height}" -F '#{pane_id}' -P)"
  fi
  printf "%s\\n" "${_pane_id}"
}

xpns_split_pane_horizontal()  {
  local _target_pane_id="$1"
  shift
  local _cell_width="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _exec_cmd="$1"
  shift
  if [[ "${_speedy_flg}" -eq 1 ]]; then
    ${TMUX_XPANES_EXEC} split-window -t "$_target_pane_id" -h -d -l "$_cell_width" "${_exec_cmd}"
  else
    ${TMUX_XPANES_EXEC} split-window -t "$_target_pane_id" -h -d -l "$_cell_width"
  fi
}

xpns_prepare_window()  {
  local _window_name="$1"
  shift
  local _log_flag="$1"
  shift
  local _title_flag="$1"
  shift
  local _attach_flg="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _await_flg="$1"
  shift
  local _interval="$1"
  shift
  local _repstr="$1"
  shift
  local _cmd_template="$1"
  shift
  local _args=("$@")
  local _window_height="$XP_WINDOW_HEIGHT"
  local _window_width="$XP_WINDOW_WIDTH"
  local _col="$XP_OPT_CUSTOM_SIZE_COLS"
  local _row="$XP_OPT_CUSTOM_SIZE_ROWS"
  local _cols=("${XP_COLS[@]}")
  local _cols_offset=("${XP_COLS_OFFSETS[@]}")
  local _exec_cmd=
  local _pane_id=
  local _first_pane_id=
  local _window_id=
  local _cell_height=
  local _cell_width=
  local _top_pane_height=
  local _current_pane_width=
  local i=
  local j=
  local _rest_col=
  local _rest_row=
  local _offset=

  _cell_height=$(((_window_height - _row + 1) / _row))
  ## Insert first element
  _exec_cmd="${_cmd_template//${_repstr}/${_args[0]}}"
  _exec_cmd="$(xpns_inject_wait_command "${_log_flag}" "${_title_flag}" "${_speedy_flg}" "${_await_flg}" 0 "${_exec_cmd}")"
  _window_id=$(xpns_new_window "${_window_name}" "${_attach_flg}" "${_speedy_flg}" "${_exec_cmd}")
  _first_pane_id=$(${TMUX_XPANES_EXEC} display-message -t "$_window_id" -p -F '#{pane_id}' | head -n 1)

  ## Start from last row
  for ((i = _row - 1; i > 0; i--)); do
    _col="${_cols[i]}"
    _cell_width=$(((_window_width - _col + 1) / _col))
    xpns_log "debug" "_col=$_col"
    ((_offset = _cols_offset[i]))
    for ((j = 0; j < _col; j++)); do
      xpns_interval "${_interval}"
      if ((j == 0)); then
        ((idx = _offset - _col))
        # Create new row
        # Insert first element of the row first
        _exec_cmd="${_cmd_template//${_repstr}/${_args[idx]}}"
        _exec_cmd="$(xpns_inject_wait_command "${_log_flag}" "${_title_flag}" "${_speedy_flg}" "${_await_flg}" "${idx}" "${_exec_cmd}")"
        _pane_id=$(xpns_new_pane_vertical "${_window_name}" "${_cell_height}" "${_speedy_flg}" "${_exec_cmd}")
      fi
      # Separate row into columns
      if ((j != 0)); then
        ((idx = _offset - j))
        _exec_cmd="${_cmd_template//${_repstr}/${_args[idx]}}"
        _exec_cmd="$(xpns_inject_wait_command "${_log_flag}" "${_title_flag}" "${_speedy_flg}" "${_await_flg}" "${idx}" "${_exec_cmd}")"
        ## Separate row into columns
        _current_pane_width=$(${TMUX_XPANES_EXEC} display-message -t "$_pane_id" -p '#{pane_width}' | head -n 1)
        _rest_col=$((_col - j + 1))
        _cell_width=$(((_current_pane_width - _rest_col + 1) / _rest_col))
        xpns_split_pane_horizontal "$_pane_id" "$_cell_width" "${_speedy_flg}" "${_exec_cmd}"
      fi
    done

    # Adjust height
    _top_pane_height=$(${TMUX_XPANES_EXEC} display-message -t "$_window_id" -p '#{pane_height}' | head -n 1)
    _rest_row=$((i))
    xpns_log "debug" "_top_pane_height=$_top_pane_height _rest_row=$_rest_row"
    _cell_height=$(((_top_pane_height - _rest_row + 1) / _rest_row))
  done

  # Split first row into columns
  _col="${_cols[0]}"
  _cell_width=$(((_window_width - _col + 1) / _col))
  for ((j = 1; j < _col; j++)); do
    xpns_interval "${_interval}"
    idx=$((_cols_offset[0] - j))
    # Adjust width
    _current_pane_width=$(${TMUX_XPANES_EXEC} display-message -t "$_first_pane_id" -p '#{pane_width}' | head -n 1)
    _rest_col=$((_col - j + 1))
    _cell_width=$(((_current_pane_width - _rest_col + 1) / _rest_col))
    ## Split top row into columns
    _exec_cmd="${_cmd_template//${_repstr}/${_args[idx]}}"
    _exec_cmd="$(xpns_inject_wait_command "${_log_flag}" "${_title_flag}" "${_speedy_flg}" "${_await_flg}" "${idx}" "${_exec_cmd}")"
    xpns_split_pane_horizontal "${_first_pane_id}" "${_cell_width}" "${_speedy_flg}" "${_exec_cmd}"
  done
}

xpns_is_session_running() {
  local _socket="$1"
  ${TMUX_XPANES_EXEC} -S "${_socket}" list-session > /dev/null 2>&1
}

# Remove unnecessary session files as much as possible
# to make xpanes avoid loading old .tmux.conf.
xpns_clean_session() {
  if [[ "${XP_SOCKET_PATH}" != "${XP_DEFAULT_SOCKET_PATH}" ]]; then
    return
  fi
  # Delete old socket file (xpanes v3.1.0 or before).
  if [[ -e "${XP_DEFAULT_SOCKET_PATH_BASE}" ]]; then
    if ! xpns_is_session_running "${XP_DEFAULT_SOCKET_PATH_BASE}"; then
      xpns_log "debug" "socket(${XP_DEFAULT_SOCKET_PATH_BASE}) is not running. Remove it"
      rm -f "${XP_DEFAULT_SOCKET_PATH_BASE}"
    fi
  fi
  for _socket in "${XP_CACHE_HOME}"/socket.*; do
    xpns_log "debug" "file = ${_socket}"
    if ! xpns_is_session_running "${_socket}"; then
      xpns_log "debug" "socket(${_socket}) is not running. Remove it"
      rm -f "${_socket}"
    else
      xpns_log "debug" "socket(${_socket}) is running. Keep ${_socket}"
    fi
  done
}

#
# Split a new window into multiple panes.
# Usage:
#    xpns_prepare_preset_layout_window <window name> <offset of index> <number of pane> <attach or not>
#
xpns_prepare_preset_layout_window() {
  local _window_name="$1"
  shift
  local _pane_base_index="$1"
  shift
  local _log_flag="$1"
  shift
  local _title_flag="$1"
  shift
  local _attach_flg="$1"
  shift
  local _speedy_flg="$1"
  shift
  local _await_flg="$1"
  shift
  local _interval="$1"
  shift
  # Create new window.
  if [[ "${_attach_flg}" -eq 1 ]]; then
    ${TMUX_XPANES_EXEC} new-window -n "${_window_name}"
  else
    # Keep background
    ${TMUX_XPANES_EXEC} new-window -n "${_window_name}" -d
  fi

  # specify a pane which has the youngest number of index.
  ${TMUX_XPANES_EXEC} select-pane -t "${_window_name}.${_pane_base_index}"

  # split window into multiple panes
  xpns_split_window \
    "${_window_name}" \
    "${_log_flag}" \
    "${_title_flag}" \
    "${_speedy_flg}" \
    "${_await_flg}" \
    "${_interval}" \
    "${_pane_base_index}" \
    "$@"

  ### If the first pane is still remaining,
  ### panes cannot be organized well.
  # Delete the first pane
  ${TMUX_XPANES_EXEC} kill-pane -t "${_window_name}.${_pane_base_index}"

  # Select second pane here.
  #   If the command gets error, it would most likely be caused by user (XP_ENOPANE).
  #   Suppress error message here and announce it in xpns_execution.
  ${TMUX_XPANES_EXEC} select-pane -t "${_window_name}.${_pane_base_index}" > /dev/null 2>&1
}

# Check whether given command is in the PATH or not.
xpns_check_env() {
  local _cmds="$1"
  while read -r cmd; do
    if ! type "${cmd}" > /dev/null 2>&1; then
      if [[ "${cmd}" == "tmux" ]] && [[ "${TMUX_XPANES_EXEC}" == "tmux" ]]; then
        xpns_log "error" "${cmd} is required. Install ${cmd} or set TMUX_XPANES_EXEC variable."
        exit ${XP_ENOCMD}
      elif [[ "${cmd}" != "tmux" ]]; then
        xpns_log "error" "${cmd} is required."
        exit ${XP_ENOCMD}
      fi
    fi
  done < <(echo "${_cmds}" | tr ' ' '\n')

  if ! mkdir -p "${XP_CACHE_HOME}"; then
    xpns_log "warning" "failed to create cache directory '${XP_CACHE_HOME}'."
  fi

  # Do not omit this part, this is used by testing.
  TMUX_XPANES_TMUX_VERSION="${TMUX_XPANES_TMUX_VERSION:-$(xpns_get_tmux_version)}"
  if ( xpns_tmux_is_greater_equals \
    "${XP_SUPPORT_TMUX_VERSION_LOWER}" \
    "${TMUX_XPANES_TMUX_VERSION}" ); then
    : "Supported tmux version"
  else
    xpns_log "warning" \
      "'${XP_THIS_FILE_NAME}' may not work correctly! Please check followings.
* tmux is installed correctly.
* Supported tmux version is installed.
  Version ${XP_SUPPORT_TMUX_VERSION_LOWER} and over is officially supported."
  fi

  return 0
}

xpns_pipe_filter() {
  local _number="${1-}"
  if [[ -z "${_number-}" ]]; then
    cat
  else
    xargs -n "${_number}"
  fi
}

# Merge array's element by combining with space.
#   i.e
#   array=(1 2 3 4 5)
#   func 3 "array"
#   => array is going to be ("1 2 3" "4 5")
xpns_merge_array_elements() {
  local _line=""
  local _pane_num="$1"
  shift
  local _arr_name="$1"
  shift
  local _filtered_args=()
  eval 'set -- "${'"$_arr_name"'[@]}"'
  local _num="$#"
  for ((i = 1; i <= _num; i++)); do
    if [[ -z "$_line" ]]; then
      _line="$1"
    else
      _line="${_line}${XP_OFS}$1"
    fi
    shift
    if ((i % _pane_num == 0)); then
      _filtered_args+=("${_line}")
      _line=""
    fi
  done
  if [[ -n "$_line" ]]; then
    _filtered_args+=("${_line}")
  fi
  eval "$_arr_name"'=("${_filtered_args[@]}")'
}

xpns_newline2space() {
  local _result=""
  while read -r _line; do
    if [[ -z "$_result" ]]; then
      _result="$_line"
    else
      _result="${_result}${XP_OFS}${_line}"
    fi
  done
  printf "%s\\n" "${_result}"
}

xpns_get_window_height_width() {
  local _height=
  local _width=
  local _result=
  local _dev=
  local _pattern='^([0-9]+)[ \t]+([0-9]+)$'

  if ! type stty > /dev/null 2>&1; then
    xpns_log "debug" "'stty' does not exist: Failed to get window height and size. Skip checking"
    return 1
  fi

  ## This condition is used for unit testing
  if [[ -z "${XP_IS_PIPE_MODE-}" ]]; then
    if [[ ! -t 0 ]]; then
      XP_IS_PIPE_MODE=1
    fi
  fi
  if [[ $XP_IS_PIPE_MODE -eq 0 ]]; then
    if _result=$(stty size 2> /dev/null) && [[ "$_result" =~ $_pattern ]]; then
      _height="${BASH_REMATCH[1]}"
      _width="${BASH_REMATCH[2]}"
      xpns_log "debug" "window height: $_height, width: $_width"
      printf "%s\\n" "$_height $_width"
      return 0
    fi
  else
    if ! type ps > /dev/null 2>&1; then
      xpns_log "debug" "'ps' does not exist: Failed to get window height and size. Skip checking"
      return 1
    fi
    {
      read -r       # Remove first line
      read -r _dev
    } < <(ps -o tty -p $$ 2> /dev/null)
    ## If it's Linux, -F option is used
    if _result=$(stty -F "/dev/${_dev}" size 2> /dev/null) && [[ "$_result" =~ $_pattern ]]; then
      _height="${BASH_REMATCH[1]}"
      _width="${BASH_REMATCH[2]}"
      xpns_log "debug" "window height: $_height, width: $_width"
      printf "%s\\n" "$_height $_width"
      return 0
    fi
    ## If it's BSD, macOS, -F option is used
    if _result=$(stty -f "/dev/${_dev}" size 2> /dev/null) && [[ "$_result" =~ $_pattern ]]; then
      _height="${BASH_REMATCH[1]}"
      _width="${BASH_REMATCH[2]}"
      xpns_log "debug" "window height: $_height, width: $_width"
      printf "%s\\n" "$_height $_width"
      return 0
    fi
    return 1
  fi
  return 1
}

xpns_check_cell_size_bulk() {
  local _cell_num="$1"
  shift
  local _bulk_cols="$1"
  shift
  local _win_height="$1"
  shift
  local _win_width="$1"
  shift
  local _ignore_flag="$1"
  shift
  local _all_cols=()
  # shellcheck disable=SC2178
  local _cols=0
  local _rows=0
  local _sum_cell=0
  IFS="," read -r -a _all_cols <<< "${_bulk_cols}"
  _rows="${#_all_cols[@]}"
  for i in "${_all_cols[@]}"; do
    ((i >= _cols))   && ((_cols = i))
    ((_sum_cell = _sum_cell + i))
  done
  if ((_sum_cell != _cell_num)); then
    xpns_log "error" "Number of cols does not equals to the number of arguments."
    xpns_log "error" "Expected (# of args) : $_cell_num, Actual (--bulk-cols) : $_sum_cell)."
    return ${XP_ELAYOUT:-6}
  fi
  local cell_height=$(((_win_height - _rows + 1) / _rows))
  local cell_width=$(((_win_width - _cols + 1) / _cols))

  ## Display basic information
  xpns_log "debug" "Window: { Height: $_win_height, Width: $_win_width }"
  xpns_log "debug" "Cell: { Height: $cell_height, Width: $cell_width }"
  xpns_log "debug" "# Of Panes: ${_cell_num}"
  xpns_log "debug" "         | Row[0] --...--> Row[MAX]"
  xpns_log "debug" "    -----+------------------------..."
  xpns_log "debug" "    Col[]| ${_all_cols[*]}"
  xpns_log "debug" "    -----+------------------------..."

  if [[ "$_ignore_flag" -ne 1 ]] && ( ((cell_height < 2))   || ((cell_width < 2))   ); then
    xpns_log "error" "Expected pane size is too small (height: $cell_height lines, width: $cell_width chars)"
    return ${XP_ESMLPANE:-7}
  fi
  printf "%s\\n" "${_cols} ${_rows} ${_all_cols[*]}"
}

xpns_check_cell_size() {
  local _cell_num="$1"
  shift
  local _cols="$1"
  shift
  local _rows="$1"
  shift
  local _win_height="$1"
  shift
  local _win_width="$1"
  shift
  local _ignore_flag="$1"
  shift
  local _all_cols_num=
  local _all_rows=()

  if [[ -n "${_cols-}" ]] && [[ -n "${_rows-}" ]]; then
    xpns_log "warning" "Both col size and row size are provided. Col size is preferentially going to be applied."
  fi
  ## if col is only defined
  if [[ -n "${_cols-}" ]]; then
    read -r _cols _rows < <(xpns_adjust_col_row "${_cols-}" 0 "${_cell_num}")
    IFS=" " read -r -a _all_rows <<< "$(xpns_divide_equally "${_cell_num}" "${_cols}")"
    _all_cols_num="$(xpns_nums_transpose "${_all_rows[@]}")"

  ## if row is only defined
  elif [[ -n "${_rows-}" ]]; then
    read -r _cols _rows < <(xpns_adjust_col_row 0 "${_rows-}" "${_cell_num}")
    _all_cols_num="$(xpns_divide_equally "${_cell_num}" "${_rows}")"

  ## if both are undefined
  else
    read -r _cols _rows < <(xpns_adjust_col_row 0 0 "${_cell_num}")
    _all_cols_num="$(xpns_divide_equally "${_cell_num}" "${_rows}")"
  fi

  local cell_height=$(((_win_height - _rows + 1) / _rows))
  local cell_width=$(((_win_width - _cols + 1) / _cols))

  ## Display basic information
  xpns_log "debug" "Window: { Height: $_win_height, Width: $_win_width }"
  xpns_log "debug" "Cell: { Height: $cell_height, Width: $cell_width }"
  xpns_log "debug" "# Of Panes: ${_cell_num}"
  xpns_log "debug" "         | Row[0] --...--> Row[MAX]"
  xpns_log "debug" "    -----+------------------------..."
  xpns_log "debug" "    Col[]| ${_all_cols_num}"
  xpns_log "debug" "    -----+------------------------..."

  if [[ "$_ignore_flag" -ne 1 ]] && ( ((cell_height < 2))   || ((cell_width < 2))   ); then
    xpns_log "error" "Expected pane size is too small (height: $cell_height lines, width: $cell_width chars)"
    return "${XP_ESMLPANE:-7}"
  fi
  printf "%s\\n" "${_cols} ${_rows} ${_all_cols_num}"
}

# Execute from Normal mode1
xpns_pre_execution() {
  local _opts4args=""
  local _args4args=""

  if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
    xpns_log "error" "'-x' must be used on the running tmux session."
    exit ${XP_EINVAL}
  fi
  if [[ ${XP_OPT_REUSE} -eq 1 ]]; then
    xpns_log "error" "'-r' must be used on the running tmux session."
    exit ${XP_EINVAL}
  fi

  # Run as best effort.
  # Because after the tmux session is created, cols and rows would be provided by tmux.
  IFS=" " read -r XP_WINDOW_HEIGHT XP_WINDOW_WIDTH < <(xpns_get_window_height_width) && {
    local _arg_num="${#XP_ARGS[@]}"
    local _cell_num _tmp_col_row_cols _tmp_cols
    if [[ -n "$XP_MAX_PANE_ARGS" ]] && ((XP_MAX_PANE_ARGS > 1)); then
      _cell_num=$((_arg_num / XP_MAX_PANE_ARGS))
    else
      _cell_num="${_arg_num}"
    fi
    if [[ -n "${XP_OPT_BULK_COLS}" ]]; then
      _tmp_col_row_cols="$(xpns_check_cell_size_bulk \
        "${_cell_num}" \
        "${XP_OPT_BULK_COLS}" \
        "${XP_WINDOW_HEIGHT}" \
        "${XP_WINDOW_WIDTH}" \
        "${XP_OPT_IGNORE_SIZE_LIMIT:-0}")"
      local _exit_status="$?"
      [[ $_exit_status -eq ${XP_ELAYOUT} ]] && exit ${XP_ELAYOUT}
      [[ $_exit_status -eq ${XP_ESMLPANE} ]] && exit ${XP_ESMLPANE}
    else
      _tmp_col_row_cols="$(xpns_check_cell_size \
        "${_cell_num}" \
        "${XP_OPT_CUSTOM_SIZE_COLS-}" \
        "${XP_OPT_CUSTOM_SIZE_ROWS-}" \
        "${XP_WINDOW_HEIGHT}" \
        "${XP_WINDOW_WIDTH}" \
        "${XP_OPT_IGNORE_SIZE_LIMIT:-0}")"
      [[ $? -eq ${XP_ESMLPANE} ]] && exit ${XP_ESMLPANE}
    fi

    IFS=" " read -r XP_OPT_CUSTOM_SIZE_COLS XP_OPT_CUSTOM_SIZE_ROWS _tmp_cols <<< "$_tmp_col_row_cols"
    IFS=" " read -r -a XP_COLS <<< "${_tmp_cols}"
    IFS=" " read -r -a XP_COLS_OFFSETS <<< "$(printf "%s\\n" "${XP_COLS[*]}" | xpns_nums_accumulate_sum)"
    xpns_log "debug" "Options: $(xpns_arr2args "${XP_OPTIONS[@]}")"
    xpns_log "debug" "Arguments: $(xpns_arr2args "${XP_ARGS[@]}")"
  }

  # Append -- flag.
  # Because any arguments may have `-`
  if [[ ${XP_NO_OPT} -eq 1 ]]; then
    XP_ARGS=("--" "${XP_ARGS[@]}")
  fi

  # If there is any options, escape them.
  if [[ -n "${XP_OPTIONS[*]-}" ]]; then
    _opts4args=$(xpns_arr2args "${XP_OPTIONS[@]}")
  fi
  _args4args=$(xpns_arr2args "${XP_ARGS[@]}")

  # Run as best effort
  xpns_clean_session || true

  # Create new session.
  ${TMUX_XPANES_EXEC} -S "${XP_SOCKET_PATH}" new-session \
    -s "${XP_SESSION_NAME}" \
    -n "${XP_TMP_WIN_NAME}" \
    -d "${XP_ABS_THIS_FILE_NAME} ${_opts4args} ${_args4args}"

  # Avoid attaching (for unit testing).
  if [[ ${XP_OPT_ATTACH} -eq 1 ]]; then
    if ! ${TMUX_XPANES_EXEC} -S "${XP_SOCKET_PATH}" attach-session -t "${XP_SESSION_NAME}" &&
      [[ ${XP_IS_PIPE_MODE} -eq 1    ]]; then
      ## In recovery case, overwrite trap to keep socket file
      trap 'rm -f "${XP_CACHE_HOME}"/__xpns_*$$;' EXIT

      xpns_log "info" "Recovery" \
        "Execute below command line to re-attach the new session.

${TMUX_XPANES_EXEC} -S ${XP_SOCKET_PATH} attach-session -t ${XP_SESSION_NAME}

"
      exit ${XP_ETTY}
    fi
  fi
}

# Execute from inside of tmux session
xpns_execution() {
  local _pane_base_index=
  local _window_name=
  local _last_args_idx=
  local _def_allow_rename=
  local _pane_count=0

  if [[ ${XP_IS_PIPE_MODE} -eq 0 ]] && [[ -n "${XP_MAX_PANE_ARGS-}" ]]; then
    xpns_merge_array_elements "${XP_MAX_PANE_ARGS}" 'XP_ARGS'
  fi

  ## Fix window size and define pane size
  {
    local  _tmp_col_row_cols _tmp_cols
    IFS=" " read -r XP_WINDOW_HEIGHT XP_WINDOW_WIDTH < <(
      ${TMUX_XPANES_EXEC} display-message -p '#{window_height} #{window_width}'
    )
    if [[ -n "${XP_OPT_BULK_COLS}" ]]; then
      _tmp_col_row_cols="$(xpns_check_cell_size_bulk \
        "${#XP_ARGS[@]}" \
        "${XP_OPT_BULK_COLS}" \
        "${XP_WINDOW_HEIGHT}" \
        "${XP_WINDOW_WIDTH}" \
        "${XP_OPT_IGNORE_SIZE_LIMIT:-0}")"
      local _exit_status="$?"
      [[ $_exit_status -eq ${XP_ELAYOUT} ]] && exit ${XP_ELAYOUT}
      [[ $_exit_status -eq ${XP_ESMLPANE} ]] && exit ${XP_ESMLPANE}
    else
      _tmp_col_row_cols="$(xpns_check_cell_size \
        "${#XP_ARGS[@]}" \
        "${XP_OPT_CUSTOM_SIZE_COLS-}" \
        "${XP_OPT_CUSTOM_SIZE_ROWS-}" \
        "${XP_WINDOW_HEIGHT}" \
        "${XP_WINDOW_WIDTH}" \
        "${XP_OPT_IGNORE_SIZE_LIMIT:-0}")"
      [[ $? -eq ${XP_ESMLPANE} ]] && exit ${XP_ESMLPANE}
    fi
    IFS=" " read -r XP_OPT_CUSTOM_SIZE_COLS XP_OPT_CUSTOM_SIZE_ROWS _tmp_cols <<< "$_tmp_col_row_cols"
    IFS=" " read -r -a XP_COLS <<< "${_tmp_cols}"
    IFS=" " read -r -a XP_COLS_OFFSETS <<< "$(printf "%s\\n" "${XP_COLS[*]}" | xpns_nums_accumulate_sum)"
    xpns_log "debug" "Options: $(xpns_arr2args "${XP_OPTIONS[@]}")"
    xpns_log "debug" "Arguments: $(xpns_arr2args "${XP_ARGS[@]}")"
  }

  _pane_base_index=$(xpns_get_global_tmux_conf 'pane-base-index')
  _last_args_idx=$((${#XP_ARGS[@]} - 1))
  _def_allow_rename="$(xpns_get_global_tmux_conf 'allow-rename')"

  xpns_suppress_allow_rename "${_def_allow_rename-}"
  XP_CMD_UTILITY="$(xpns_get_joined_begin_commands "${XP_CMD_UTILITY}")"

  if [[ ${XP_OPT_REUSE} -eq 1 ]]; then
    _window_name="$( ${TMUX_XPANES_EXEC} display -p -F "#{window_id}")"
    _pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
    _pane_base_index=$(($(${TMUX_XPANES_EXEC} display -p -F "#{pane_index}") + 1))
    _pane_active_pane_id=$(${TMUX_XPANES_EXEC} display -p -F "#{pane_id}")

    # Reusing the panes: if there's only 1 pane, behave like -x otherwise
    # make sure there are enough panes of higher index than the current one.
    # This allows to run -r to create the panes and then repeatedly to reuse
    # those panes.
    if [[ $((_pane_count - _pane_base_index)) -lt ${#XP_ARGS[@]}  ]]; then
      xpns_log "error" "Not enough panes ($((_pane_count - 1))) after this one to run the ${#XP_ARGS[@]} commands."
      exit ${XP_EINVAL}
    fi
  fi
  if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
    # Reuse existing window name
    # tmux 1.6 does not support -F option
    _window_name="$( ${TMUX_XPANES_EXEC} display -p -F "#{window_id}")"
    _pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
    _pane_base_index=$((_pane_base_index + _pane_count - 1))
    _pane_active_pane_id=$(${TMUX_XPANES_EXEC} display -p -F "#{pane_id}")
  elif [[ ${XP_OPT_REUSE} -eq 0 ]]; then
    _window_name=$(
      xpns_generate_window_name \
        "${XP_EMPTY_STR}" \
        "${XP_ARGS[0]}" |
        xpns_value2key
    )
  fi

  ## --------------------
  # Prepare window and panes
  ## --------------------
  if [[ ${XP_OPT_REUSE} -eq 0 ]]; then
    if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
      xpns_prepare_extra_panes \
        "${_window_name}" \
        "${_pane_base_index}" \
        "${XP_OPT_LOG_STORE}" \
        "${XP_OPT_SET_TITLE}" \
        "${XP_OPT_SPEEDY}" \
        "${XP_OPT_SPEEDY_AWAIT}" \
        "${XP_OPT_INTERVAL}" \
        "${XP_REPSTR}" \
        "${XP_CMD_UTILITY}" \
        "${XP_ARGS[@]}"
    elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 1 ]]; then
      xpns_prepare_preset_layout_window \
        "${_window_name}" \
        "${_pane_base_index}" \
        "${XP_OPT_LOG_STORE}" \
        "${XP_OPT_SET_TITLE}" \
        "${XP_OPT_ATTACH}" \
        "${XP_OPT_SPEEDY}" \
        "${XP_OPT_SPEEDY_AWAIT}" \
        "${XP_OPT_INTERVAL}" \
        "${XP_REPSTR}" \
        "${XP_CMD_UTILITY}" \
        "${XP_ARGS[@]}"
    elif [[ ${XP_OPT_USE_PRESET_LAYOUT} -eq 0 ]]; then
      xpns_prepare_window \
        "${_window_name}" \
        "${XP_OPT_LOG_STORE}" \
        "${XP_OPT_SET_TITLE}" \
        "${XP_OPT_ATTACH}" \
        "${XP_OPT_SPEEDY}" \
        "${XP_OPT_SPEEDY_AWAIT}" \
        "${XP_OPT_INTERVAL}" \
        "${XP_REPSTR}" \
        "${XP_CMD_UTILITY}" \
        "${XP_ARGS[@]}"
    fi
  fi

  ## With -ss option, it is possible to close all the panes as of here.
  ## Check status of the window. If no window exists, there is nothing to do any more and just exit.
  xpns_log "debug" "xpns_is_window_alive:1: After window separation"
  xpns_is_window_alive "${_window_name}" "${XP_OPT_SPEEDY_AWAIT}" "${_def_allow_rename-}"

  if [[ ${XP_OPT_EXTRA} -eq 1 ]]; then
    # Set offset to avoid sending command to the original pane.
    _pane_base_index=$((_pane_base_index + 1))
    # Avoid to make layout even-horizontal even if there are many panes.
    # in xpns_organize_panes
    _last_args_idx=$((_last_args_idx + _pane_count))
    # Re-select the windown that was active before.
    ${TMUX_XPANES_EXEC} select-pane -t "${_window_name}.${_pane_active_pane_id}"
  fi

  if [[ ${XP_OPT_LOG_STORE} -eq 1 ]]; then
    xpns_enable_logging \
      "${_window_name}" \
      "${_pane_base_index}" \
      "${TMUX_XPANES_LOG_DIRECTORY}" \
      "${TMUX_XPANES_LOG_FORMAT}" \
      "${XP_EMPTY_STR}" \
      "${XP_ARGS[@]}"

    if [[ $XP_OPT_SPEEDY -eq 1 ]]; then
      xpns_notify_logging \
        "${_window_name}" \
        "${XP_ARGS[@]}"
    fi
  fi

  xpns_log "debug" "xpns_is_window_alive:2: After logging"
  xpns_is_window_alive "${_window_name}" "${XP_OPT_SPEEDY_AWAIT}" "${_def_allow_rename-}"

  # Set pane titles for each pane.
  if xpns_is_pane_title_required "${XP_OPT_SET_TITLE}" "${XP_OPT_EXTRA}"; then
    xpns_set_titles \
      "${_window_name}" \
      "${_pane_base_index}" \
      "${XP_ARGS[@]}"
  fi

  if [[ $XP_OPT_SPEEDY -eq 1 ]]; then
    xpns_notify_sync \
      "${_window_name}" \
      "${XP_OPT_INTERVAL}" \
      "${XP_ARGS[@]}"
  fi

  xpns_log "debug" "xpns_is_window_alive:3: After setting title"
  xpns_is_window_alive "${_window_name}" "${XP_OPT_SPEEDY_AWAIT}" "${_def_allow_rename-}"

  # Sending operations for each pane.
  # With -s option, command is already sent.
  if [[ $XP_OPT_SPEEDY -eq 0 ]]; then
    xpns_send_commands \
      "${_window_name}" \
      "${_pane_base_index}" \
      "${XP_OPT_INTERVAL}" \
      "${XP_REPSTR}" \
      "${XP_CMD_UTILITY}" \
      "${XP_ARGS[@]}"
  fi

  xpns_log "debug" "xpns_is_window_alive:4: After sending commands"
  xpns_is_window_alive "${_window_name}" "${XP_OPT_SPEEDY_AWAIT}" "${_def_allow_rename-}"

  ## With -l <layout>, panes are organized.
  ## As well as -x, they are re-organized.
  if [[ (${XP_OPT_USE_PRESET_LAYOUT} -eq 1 || ${XP_OPT_EXTRA} -eq 1) && ${XP_OPT_REUSE} -eq 0 ]]; then
    xpns_organize_panes \
      "${_window_name}" \
      "${_last_args_idx}"
  fi

  # Enable broadcasting
  if [[ ${XP_OPT_IS_SYNC} -eq 1 ]] && [[ ${XP_OPT_EXTRA} -eq 0 ]]; then
    ${TMUX_XPANES_EXEC} \
      set-window-option -t "${_window_name}" \
      synchronize-panes on
  fi

  ## In case of -t option
  if [[ ${XP_OPT_SET_TITLE} -eq 1 ]] && [[ ${XP_OPT_CHANGE_BORDER} -eq 1 ]]; then
    # Set border format
    ${TMUX_XPANES_EXEC} \
      set-window-option -t "${_window_name}" \
      pane-border-format "${TMUX_XPANES_PANE_BORDER_FORMAT}"
    # Show border status
    ${TMUX_XPANES_EXEC} \
      set-window-option -t "${_window_name}" \
      pane-border-status "${TMUX_XPANES_PANE_BORDER_STATUS}"
  fi

  # In case of -x or -r, this statement is skipped to keep the original window name
  if [[ ${XP_OPT_EXTRA} -eq 0 ]] && [[ ${XP_OPT_REUSE} -eq 0 ]]; then
    # Restore original window name.
    ${TMUX_XPANES_EXEC} \
      rename-window -t "${_window_name}" \
      -- "$(printf "%s\\n" "${_window_name}" | xpns_key2value)"
  fi

  xpns_restore_allow_rename "${_def_allow_rename-}"
}

## ----------------
# Arrange options for pipe mode
#  * argument -> command
#  * stdin -> argument
## ----------------
xpns_switch_pipe_mode() {
  local _pane_num4new_term=""
  if [[ -n "${XP_ARGS[*]-}" ]] && [[ -n "${XP_CMD_UTILITY-}" ]]; then
    xpns_log "error" "Both arguments and other options (like '-c', '-e') which updates <command> are given."
    exit ${XP_EINVAL}
  fi

  if [[ -z "${TMUX-}" ]]; then
    xpns_log "warning" "Attached session is required for 'Pipe mode'."
    # This condition is used when the following situations.
    #   * Enter from outside of tmux session(Normal mode1)
    #   * Pipe mode.
    #   * -n option.
    #
    # For example:
    #     (Normal mode1)$ echo {a..g} | ./xpanes -n 2
    # => This will once create the new window like this.
    #     (inside of tmux session)$ ./xpanes '-n' '2' 'a' 'b' 'c' 'd' 'e' 'f' 'g'
    #     => After the window is closed, following panes would be left.
    #     (pane 1)$ echo a b
    #     (pane 2)$ echo c d
    #     (pane 3)$ echo e f
    #     (pane 4)$ echo g
    # In order to create such the query,
    # separate all the argument into minimum tokens
    # with xargs -n 1
    if [[ -n "${XP_MAX_PANE_ARGS-}" ]]; then
      _pane_num4new_term=1
    fi
  fi

  while read -r line; do
    XP_STDIN+=("${line}")
  done < <(cat | xpns_rm_empty_line |
    xpns_pipe_filter "${_pane_num4new_term:-${XP_MAX_PANE_ARGS}}")

  # Merge them into command.
  if [[ -n "${XP_ARGS[*]-}" ]]; then
    # Attention: It might be wrong result if IFS is changed.
    XP_CMD_UTILITY="${XP_ARGS[*]}"
  fi

  # If there is empty -I option or user does not assign the <repstr>,
  # Append the space and <repstr> at the end of the <command>
  # This is same as the xargs command of GNU.
  # i.e,
  #   $ echo 10 | xargs seq
  #     => seq 10
  # Whith is same as
  #   $ echo 10 | xargs -I@ seq @
  #     => seq 10
  if [[ -z "${XP_REPSTR}" ]]; then
    XP_REPSTR="${XP_DEFAULT_REPSTR}"
    if [[ -n "${XP_ARGS[*]-}" ]]; then
      XP_CMD_UTILITY="${XP_ARGS[*]-} ${XP_REPSTR}"
    fi
  fi

  # Deal with stdin as arguments.
  XP_ARGS=("${XP_STDIN[@]-}")
}

xpns_layout_short2long() {
  sed \
    -e 's/^t$/tiled/' \
    -e 's/^eh$/even-horizontal/' \
    -e 's/^ev$/even-vertical/' \
    -e 's/^mh$/main-horizontal/' \
    -e 's/^mv$/main-vertical/' \
    -e ';'
}

xpns_is_valid_layout() {
  local _layout="${1-}"
  local _pat='^(tiled|even-horizontal|even-vertical|main-horizontal|main-vertical)$'
  if ! [[ $_layout =~ $_pat ]]; then
    xpns_log "error" "Invalid layout '${_layout}'."
    exit ${XP_ELAYOUT}
  fi
}

xpns_warning_before_extra() {
  local _ans=
  local _synchronized=
  _synchronized="$(xpns_get_local_tmux_conf "synchronize-panes")"
  if [[ "on" == "${_synchronized}" ]]; then
    xpns_log "warning" "Panes are now synchronized.
'-x' option may cause unexpected behavior on the synchronized panes."
    printf "Are you really sure? [y/n]: "
    read -r _ans
    if ! [[ "${_ans-}" =~ ^[yY]  ]]; then
      return 1
    fi
  fi
}
xpns_opt_checker() {
  local _option="$1"
  local _arg="$2"
  local _check_value="${3:-default}"
  # if we do not define a positional parameter in options, we expect an integer
  local _pattern="^[0-9]+$"
  if [[ "$_check_value" == "float" ]]; then
    _pattern="^[0-9]*\.?[0-9]*$"
  elif [[ "$_check_value" == "csv" ]]; then
    _pattern="^[0-9]+(,[0-9]+)*$"
  elif [[ "$_check_value" == "string" ]]; then
    if [[ -n "$_arg" ]]; then
      return 0
    fi
  fi
  if [[ -n "$_arg" ]] && [[ "$_arg" =~ $_pattern ]]; then
    return 0
  fi
  xpns_log "error" "Invalid argument '$_arg' for $_option option"
  exit ${XP_EINVAL}
}

xpns_parse_options() {
  while (($# > 0)); do
    opt="$1"
    shift
    if [[ ${XP_NO_OPT} -eq 1 ]]; then
      XP_ARGS+=("$opt")
      continue
    fi

    ## Skip regularization if the arg is empty or --log= option
    if [[ -n "$opt" ]] && [[ -n "${opt##--log=*}" ]]; then
      # shellcheck disable=SC2295
      ## -ovalue → -o value
      if [[ -z "${opt##-${XP_ARG_OPTIONS}?*}" ]]; then
        set -- "${opt#??}" ${1+"$@"}
        opt="${opt%$1}"
      ## -abc → -a -bc
      elif [[ -z "${opt##-[!-]?*}" ]]; then
        set -- "-${opt#??}" ${1+"$@"}
        opt="${opt%${1#-}}"
      ## --option=value → --option value
      elif [[ -z "${opt##--*=*}" ]]; then
        set -- "${opt#--*=}" ${1+"$@"}
        opt="${opt%%=*}"
      fi
    fi

    case "$opt" in
      --)
        # Disable any more options
        XP_NO_OPT=1
        ;;
      ## ----------------
      # Long options
      ## ----------------
      --help)
        xpns_usage
        exit 0
        ;;
      --version)
        xpns_version
        exit 0
        ;;
      --desync)
        XP_OPTIONS+=("$opt")
        XP_OPT_IS_SYNC=0
        ;;
      --log-format)
        xpns_opt_checker "$opt" "$1" "string"
        XP_OPTIONS+=("$opt")
        XP_OPT_LOG_STORE=1
        TMUX_XPANES_LOG_FORMAT="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      --log=*)
        XP_OPT_LOG_STORE=1
        XP_OPTIONS+=("$opt")
        TMUX_XPANES_LOG_DIRECTORY="${opt#--log=}"
        xpns_opt_checker "${opt%=}" "$TMUX_XPANES_LOG_DIRECTORY" "string"
        ;;
      --log)
        XP_OPT_LOG_STORE=1
        XP_OPTIONS+=("$opt")
        ;;
      --ssh)
        XP_OPTIONS+=("$opt")
        XP_CMD_UTILITY="${XP_SSH_CMD_UTILITY}"
        # Enable -t option
        XP_OPT_SET_TITLE=1
        XP_OPT_CHANGE_BORDER=1
        # Enable -s option
        XP_OPT_SPEEDY=1
        XP_OPT_SPEEDY_AWAIT=1
        ;;
      --stay)
        XP_OPTIONS+=("$opt")
        XP_OPT_ATTACH=0
        ;;
      --cols)
        xpns_opt_checker "$opt" "$1"
        XP_OPTIONS+=("$opt")
        XP_OPT_CUSTOM_SIZE_COLS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      --rows)
        xpns_opt_checker "$opt" "$1"
        XP_OPTIONS+=("$opt")
        XP_OPT_CUSTOM_SIZE_ROWS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      --bulk-cols)
        xpns_opt_checker "$opt" "$1" "csv"
        XP_OPTIONS+=("$opt")
        XP_OPT_BULK_COLS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      --interval)
        xpns_opt_checker "$opt" "$1" "float"
        XP_OPTIONS+=("$opt")
        XP_OPT_INTERVAL="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      --debug)
        XP_OPTIONS+=("$opt")
        XP_OPT_DEBUG=1
        ;;
      --dry-run)
        XP_OPTIONS+=("$opt")
        XP_OPT_DRY_RUN=1
        ;;
      --ignore-size-limit)   # Hidden for testing
        XP_OPTIONS+=("$opt")
        XP_OPT_IGNORE_SIZE_LIMIT=1
        ;;
      ## ----------------
      # Short options without argument
      ## ----------------
      -h)
        xpns_usage
        exit 0
        ;;
      -V)
        xpns_version
        exit 0
        ;;
      -x)
        # If you modify those, make similar changes to -r
        XP_OPTIONS+=("$opt")
        XP_OPT_EXTRA=1
        XP_OPT_USE_PRESET_LAYOUT=1 ## Layout presets must be used with -x
        if ! xpns_warning_before_extra; then
          exit ${XP_EINTENT}
        fi
        ;;
      -d)
        XP_OPTIONS+=("$opt")
        XP_OPT_IS_SYNC=0
        ;;
      -e)
        XP_OPTIONS+=("$opt")
        XP_REPSTR="{}"
        XP_CMD_UTILITY="{}"
        ;;
      -t)
        XP_OPTIONS+=("$opt")
        if ( xpns_tmux_is_greater_equals 2.3 ); then
          XP_OPT_SET_TITLE=1
          XP_OPT_CHANGE_BORDER=1
        else
          xpns_log "warning" "-t option cannot be used by tmux version less than 2.3. Disabled."
          sleep 1
        fi
        ;;
      -r)
        XP_OPTIONS+=("$opt")
        # Reusing the panes: if there's only 1 pane, behave like -x
        # This allows to run -r to create the panes and then repeatedly to reuse
        # those panes.
        _pane_count="$( ${TMUX_XPANES_EXEC} list-panes | grep -c .)"
        if [[ ${_pane_count} -eq 1 ]]; then
          XP_OPT_EXTRA=1
          XP_OPT_USE_PRESET_LAYOUT=1 ## Layout presets must be used with -x
          if ! xpns_warning_before_extra; then
            exit ${XP_EINTENT}
          fi
        else
          XP_OPT_REUSE=1
          XP_OPT_EXTRA=0
          XP_OPT_IS_SYNC=0
        fi
        ;;
      -s)
        XP_OPTIONS+=("$opt")
        XP_OPT_SPEEDY=1
        XP_OPT_SPEEDY_AWAIT=1
        if [[ -z "${1#-s}" ]]; then
          XP_OPT_SPEEDY_AWAIT=0
          XP_OPTIONS+=("$1")
          shift
        fi
        ;;
      ## ----------------
      # Short options with argument
      ## ----------------
      -I)
        xpns_opt_checker "$opt" "$1" "string"
        XP_OPTIONS+=("$opt")
        XP_REPSTR="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -l)
        xpns_opt_checker "$opt" "$1" "string"
        XP_OPTIONS+=("$opt")
        XP_OPT_USE_PRESET_LAYOUT=1
        XP_LAYOUT="$(printf '%s\n' "$1" | xpns_layout_short2long)"
        xpns_is_valid_layout "${XP_LAYOUT}"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -c)
        # -c allows empty
        XP_OPTIONS+=("$opt")
        XP_CMD_UTILITY="$1"
        XP_OPT_CMD_UTILITY=1
        XP_OPTIONS+=("$1")
        shift
        ;;
      -n)
        xpns_opt_checker "$opt" "$1"
        XP_OPTIONS+=("$opt")
        XP_MAX_PANE_ARGS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -S)
        xpns_opt_checker "$opt" "$1" "string"
        XP_OPTIONS+=("$opt")
        XP_SOCKET_PATH="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -C)
        xpns_opt_checker "$opt" "$1"
        XP_OPTIONS+=("$opt")
        XP_OPT_CUSTOM_SIZE_COLS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -R)
        xpns_opt_checker "$opt" "$1"
        XP_OPTIONS+=("$opt")
        XP_OPT_CUSTOM_SIZE_ROWS="$1"
        XP_OPTIONS+=("$1")
        shift
        ;;
      -B)
        # -B allows empty
        XP_OPTIONS+=("$opt")
        XP_BEGIN_ARGS+=("$1")
        XP_OPTIONS+=("$1")
        shift
        ;;
      -*)
        xpns_log "error" "Invalid option -- '${opt}'"
        xpns_usage_warn
        exit ${XP_EINVAL}
        ;;
      ## ----------------
      # Other arguments
      ## ----------------
      *)
        XP_ARGS+=("$opt")
        XP_NO_OPT=1
        ;;
    esac
  done
  # If there is any standard input from pipe,
  # 1 line handled as 1 argument.
  if [[ ! -t 0 ]]; then
    XP_IS_PIPE_MODE=1
    xpns_switch_pipe_mode
  fi

  # When no argument is given, exit.
  if [[ -z "${XP_ARGS[*]-}" ]]; then
    xpns_log "error" "No arguments."
    xpns_usage_warn
    exit ${XP_EINVAL}
  fi

  if [[ -n "${XP_OPT_CUSTOM_SIZE_COLS-}" ]] || [[ -n "${XP_OPT_CUSTOM_SIZE_ROWS-}" ]]; then
    if [[ "$XP_OPT_EXTRA" -eq 1 ]]; then
      xpns_log "warning" "The columns/rows options (-C, --cols, -R, --rows) cannot be used with -x option. Ignored."
    elif [[ "$XP_OPT_EXTRA" -eq 0 ]] && [[ "${XP_OPT_USE_PRESET_LAYOUT}" -eq 1 ]]; then
      # This part is required to keep backward compatibility.
      ## Users can simulate xpanes v3.x to set : alias xpanes="xpanes -lt"
      xpns_log "info" "Columns/rows option (-C, --cols, -R, --rows) and -l option are provided. Disable -l. "
      XP_OPT_USE_PRESET_LAYOUT=0
    fi
  fi

  # Set default value in case of empty.
  XP_CMD_UTILITY="${XP_CMD_UTILITY:-${XP_DEFAULT_CMD_UTILITY}}"
  XP_REPSTR="${XP_REPSTR:-${XP_DEFAULT_REPSTR}}"

  # To set command on pre_execution, set -c option manually.
  if [[ ${XP_OPT_CMD_UTILITY} -eq 0 ]]; then
    XP_OPTIONS+=("-c" "${XP_CMD_UTILITY}")
  fi
}

## --------------------------------
# Main function
## --------------------------------
xpns_main() {
  xpns_parse_options ${1+"$@"}
  xpns_check_env "${XP_DEPENDENCIES}"
  ## --------------------------------
  # Parameter validation
  ## --------------------------------
  # When do dry-run flag is enabled, skip running (this is used to execute unit test of itself).
  if [[ ${XP_OPT_DRY_RUN} -eq 1 ]]; then
    return 0
  fi
  # Validate log directory.
  if [[ ${XP_OPT_LOG_STORE} -eq 1 ]]; then
    TMUX_XPANES_LOG_DIRECTORY=$(xpns_normalize_directory "${TMUX_XPANES_LOG_DIRECTORY}")
    xpns_is_valid_directory "${TMUX_XPANES_LOG_DIRECTORY}" &&
      TMUX_XPANES_LOG_DIRECTORY=$(cd "${TMUX_XPANES_LOG_DIRECTORY}" && pwd)
  fi
  ## --------------------------------
  # If current shell is outside of tmux session.
  ## --------------------------------
  if [[ -z "${TMUX-}" ]]; then
    xpns_pre_execution
  ## --------------------------------
  # If current shell is already inside of tmux session.
  ## --------------------------------
  else
    xpns_execution
  fi
  exit 0
}

## --------------------------------
# Entry Point
## --------------------------------
xpns_main ${1+"$@"}
